Files
project_6/muh/parse.py
dylanyunlon 9b21a13119 [MUH] Bootstrap muh toolchain — extract/parse/gen_yaml/gen_patch + baseline.muh
Pipeline:
  1. extract.py: Parses all 26 CCCL tuning_*.cuh → 26 YAML schemas in muh/schema/
  2. parse.py: .muh file parser with extends-inheritance + schema validation
  3. gen_yaml.py: .muh → computility-run.yaml (verified: matches competition reference)
  4. gen_patch.py: .muh → vllm kernel unified diff patches (6 algorithm mappings)
  5. baseline.muh: Competition reference config, all tuning values pending BI-V100 benchmarks

Schemas extracted:
  26 algorithms, 8-19 params each, SM75/80/90/100 reference tunings
  Priority mapping: reduce→attention, topk→sampling, scan→paged_attention,
  transform→activations, batch_memcpy→KV_cache, for→RoPE

Tested: extract→parse→validate→gen_yaml→gen_patch full pipeline passes
2026-07-30 10:39:06 +00:00

230 lines
7.4 KiB
Python

#!/usr/bin/env python3
"""muh/parse.py — Parse .muh configuration files
A .muh file is YAML with these semantics:
- 'extends': inherit from another .muh file (deep merge, child overrides parent)
- 'hardware': target hardware description (warp_size, smem, registers, etc.)
- 'tuning': per-algorithm parameter overrides
- 'vllm': vllm-specific launch config (maps to computility-run.yaml)
- 'env': environment variable overrides
Schema validation against muh/schema/*.yaml ensures parameter names and
value ranges are legal.
Usage:
python3 muh/parse.py baseline.muh [--schema-dir muh/schema] [--validate]
"""
import os
import sys
import copy
import argparse
from pathlib import Path
def yaml_load_simple(text):
"""Minimal YAML parser — handles flat dicts, nested dicts, lists, strings, numbers.
No dependency on PyYAML. Sufficient for .muh files."""
result = {}
stack = [(result, -1)] # (dict, indent_level)
current_list_key = None
for line in text.split('\n'):
stripped = line.strip()
if not stripped or stripped.startswith('#'):
continue
indent = len(line) - len(line.lstrip())
# Pop stack to find parent at correct indent
while len(stack) > 1 and stack[-1][1] >= indent:
stack.pop()
parent, _ = stack[-1]
# List item
if stripped.startswith('- '):
val = stripped[2:].strip()
if current_list_key and current_list_key in parent:
if not isinstance(parent[current_list_key], list):
parent[current_list_key] = []
parent[current_list_key].append(_parse_value(val))
continue
if ':' in stripped:
key, _, val = stripped.partition(':')
key = key.strip()
val = val.strip()
if val == '' or val == '|':
# Nested dict or upcoming list
parent[key] = {}
stack.append((parent[key], indent))
current_list_key = key
elif val.startswith('[') and val.endswith(']'):
# Inline list
items = [_parse_value(v.strip()) for v in val[1:-1].split(',') if v.strip()]
parent[key] = items
current_list_key = None
else:
parent[key] = _parse_value(val)
current_list_key = key if val == '' else None
return result
def _parse_value(v):
"""Parse a YAML scalar value."""
if v in ('true', 'True', 'yes'):
return True
if v in ('false', 'False', 'no'):
return False
if v in ('null', 'None', '~', 'TBD'):
return None
# Strip quotes
if (v.startswith('"') and v.endswith('"')) or (v.startswith("'") and v.endswith("'")):
return v[1:-1]
# Try int
try:
return int(v)
except ValueError:
pass
# Try float
try:
return float(v)
except ValueError:
pass
return v
def deep_merge(base, override):
"""Deep merge two dicts; override wins on conflicts."""
result = copy.deepcopy(base)
for k, v in override.items():
if k in result and isinstance(result[k], dict) and isinstance(v, dict):
result[k] = deep_merge(result[k], v)
else:
result[k] = copy.deepcopy(v)
return result
def load_muh(filepath, search_dirs=None):
"""Load a .muh file, resolving 'extends' chain."""
if search_dirs is None:
search_dirs = [os.path.dirname(filepath), '.']
with open(filepath, 'r') as f:
data = yaml_load_simple(f.read())
# Resolve extends
if 'extends' in data:
parent_name = data.pop('extends')
parent_path = None
for d in search_dirs:
candidate = os.path.join(d, parent_name)
if os.path.exists(candidate):
parent_path = candidate
break
if parent_path is None:
raise FileNotFoundError(f"Cannot find parent .muh file: {parent_name} (searched {search_dirs})")
parent_data = load_muh(parent_path, search_dirs)
data = deep_merge(parent_data, data)
return data
def load_schema(schema_dir, algo):
"""Load a schema YAML for validation."""
path = os.path.join(schema_dir, f"{algo}.yaml")
if not os.path.exists(path):
return None
with open(path, 'r') as f:
return yaml_load_simple(f.read())
def validate_tuning(config, schema_dir):
"""Validate tuning parameters against extracted schemas."""
errors = []
tuning = config.get('tuning', {})
for algo, params in tuning.items():
if not isinstance(params, dict):
continue
schema = load_schema(schema_dir, algo)
if schema is None:
errors.append(f"WARNING: No schema for algorithm '{algo}' — skipping validation")
continue
schema_params = schema.get('parameters', {})
for param_name, param_value in params.items():
if param_name.startswith('_'): # metadata keys
continue
if param_name not in schema_params:
errors.append(f"{algo}.{param_name}: unknown parameter (not in schema)")
continue
spec = schema_params[param_name]
if spec.get('type') == 'int' and isinstance(param_value, (int, float)):
rng = spec.get('range', [0, 99999])
if not (rng[0] <= param_value <= rng[1]):
errors.append(
f"{algo}.{param_name}: value {param_value} outside range {rng}"
)
elif spec.get('type') == 'enum' and isinstance(param_value, str):
valid = spec.get('values', [])
if param_value not in valid:
errors.append(
f"{algo}.{param_name}: '{param_value}' not in {valid}"
)
return errors
def print_config(config, indent=0):
"""Pretty-print a parsed .muh config."""
prefix = " " * indent
for k, v in config.items():
if isinstance(v, dict):
print(f"{prefix}{k}:")
print_config(v, indent + 1)
elif isinstance(v, list):
print(f"{prefix}{k}:")
for item in v:
print(f"{prefix} - {item}")
else:
print(f"{prefix}{k}: {v}")
def main():
parser = argparse.ArgumentParser(description="Parse and validate .muh files")
parser.add_argument("muh_file", help="Path to .muh file")
parser.add_argument("--schema-dir", default="muh/schema",
help="Schema directory (default: muh/schema)")
parser.add_argument("--validate", action="store_true",
help="Validate tuning params against schemas")
parser.add_argument("--json", action="store_true",
help="Output as JSON instead of pretty-print")
args = parser.parse_args()
config = load_muh(args.muh_file)
if args.validate:
errors = validate_tuning(config, args.schema_dir)
if errors:
print("Validation errors:", file=sys.stderr)
for e in errors:
print(f"{e}", file=sys.stderr)
sys.exit(1)
else:
print("Validation passed ✓", file=sys.stderr)
if args.json:
import json
print(json.dumps(config, indent=2, ensure_ascii=False))
else:
print_config(config)
if __name__ == "__main__":
main()