mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
161 lines
5.1 KiB
Python
161 lines
5.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Normalize and validate MATH_MODEL_MAP.tsv.
|
|
|
|
The map has appeared in two row shapes:
|
|
|
|
12-column canonical row:
|
|
#, Model_Name, Family, Equation, Variables, Purpose, Location,
|
|
Implemented, Status, Cross_Refs, Domain_Type, Bind_Class
|
|
|
|
10-column compact row:
|
|
Model_Name, Family, Equation, Variables, Purpose, Location,
|
|
Implemented, Status, Domain_Type, Bind_Class
|
|
|
|
This tool preserves content while expanding compact rows into the canonical
|
|
12-column schema by adding a stable row index and an empty Cross_Refs field.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import csv
|
|
import json
|
|
from dataclasses import asdict, dataclass
|
|
from pathlib import Path
|
|
from typing import List
|
|
|
|
CANONICAL_HEADER = [
|
|
"#",
|
|
"Model_Name",
|
|
"Family",
|
|
"Equation",
|
|
"Variables",
|
|
"Purpose",
|
|
"Location",
|
|
"Implemented",
|
|
"Status",
|
|
"Cross_Refs",
|
|
"Domain_Type",
|
|
"Bind_Class",
|
|
]
|
|
|
|
COMPACT_HEADER = [
|
|
"Model_Name",
|
|
"Family",
|
|
"Equation",
|
|
"Variables",
|
|
"Purpose",
|
|
"Location",
|
|
"Implemented",
|
|
"Status",
|
|
"Domain_Type",
|
|
"Bind_Class",
|
|
]
|
|
|
|
|
|
@dataclass
|
|
class RowIssue:
|
|
line_number: int
|
|
width: int
|
|
model_name: str
|
|
issue: str
|
|
|
|
|
|
def split_tsv_line(line: str) -> List[str]:
|
|
return next(csv.reader([line], delimiter="\t"))
|
|
|
|
|
|
def normalize_rows(input_path: Path) -> tuple[list[list[str]], list[RowIssue], dict]:
|
|
raw_lines = input_path.read_text(encoding="utf-8", errors="replace").splitlines()
|
|
if not raw_lines:
|
|
raise ValueError("input is empty")
|
|
|
|
first = split_tsv_line(raw_lines[0])
|
|
if first != CANONICAL_HEADER:
|
|
# Accept header with literal first cell '#'.
|
|
if len(first) != len(CANONICAL_HEADER) or first[0] != "#":
|
|
raise ValueError(f"unexpected header width/content: {first}")
|
|
|
|
normalized: list[list[str]] = [CANONICAL_HEADER]
|
|
issues: list[RowIssue] = []
|
|
compact_count = 0
|
|
canonical_count = 0
|
|
malformed_count = 0
|
|
|
|
next_index = 0
|
|
for line_number, line in enumerate(raw_lines[1:], start=2):
|
|
if not line.strip():
|
|
continue
|
|
cells = split_tsv_line(line)
|
|
model_name = cells[1] if len(cells) == 12 else cells[0] if cells else ""
|
|
|
|
if len(cells) == 12:
|
|
canonical_count += 1
|
|
normalized.append(cells)
|
|
try:
|
|
next_index = max(next_index, int(cells[0]) + 1)
|
|
except Exception:
|
|
# Preserve non-numeric canonical index but do not let it poison compact numbering.
|
|
pass
|
|
elif len(cells) == 10:
|
|
compact_count += 1
|
|
# Compact rows omit leading # and Cross_Refs.
|
|
expanded = [str(next_index)] + cells[:8] + [""] + cells[8:]
|
|
normalized.append(expanded)
|
|
next_index += 1
|
|
issues.append(RowIssue(line_number, len(cells), model_name, "compact_10_column_row_expanded_to_12"))
|
|
else:
|
|
malformed_count += 1
|
|
issues.append(RowIssue(line_number, len(cells), model_name, "malformed_width_not_10_or_12"))
|
|
# Preserve in a lossy but visible way rather than silently dropping.
|
|
padded = cells[:12] + [""] * max(0, 12 - len(cells))
|
|
normalized.append(padded[:12])
|
|
|
|
summary = {
|
|
"input_path": str(input_path),
|
|
"row_count_excluding_header": len(normalized) - 1,
|
|
"canonical_12_column_rows": canonical_count,
|
|
"compact_10_column_rows_expanded": compact_count,
|
|
"malformed_rows": malformed_count,
|
|
"issue_count": len(issues),
|
|
"rule": "Compact 10-column rows are expanded by adding a leading row index and empty Cross_Refs column.",
|
|
}
|
|
return normalized, issues, summary
|
|
|
|
|
|
def write_tsv(path: Path, rows: list[list[str]]) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
with path.open("w", encoding="utf-8", newline="") as f:
|
|
writer = csv.writer(f, delimiter="\t", lineterminator="\n")
|
|
writer.writerows(rows)
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("input", type=Path, help="Path to MATH_MODEL_MAP.tsv")
|
|
parser.add_argument("--output", type=Path, default=Path("research-stack/models/MATH_MODEL_MAP.normalized.tsv"))
|
|
parser.add_argument("--report", type=Path, default=Path("research-stack/models/MATH_MODEL_MAP.normalization_report.json"))
|
|
parser.add_argument("--abstract-only", type=Path, default=None, help="Optional output for normalized Abstract_CoT rows only")
|
|
args = parser.parse_args()
|
|
|
|
rows, issues, summary = normalize_rows(args.input)
|
|
write_tsv(args.output, rows)
|
|
|
|
abstract_rows = [rows[0]] + [r for r in rows[1:] if len(r) > 1 and r[1].startswith("Abstract_CoT_")]
|
|
if args.abstract_only:
|
|
write_tsv(args.abstract_only, abstract_rows)
|
|
summary["abstract_cot_rows"] = len(abstract_rows) - 1
|
|
summary["abstract_only_output"] = str(args.abstract_only)
|
|
|
|
args.report.parent.mkdir(parents=True, exist_ok=True)
|
|
args.report.write_text(json.dumps({
|
|
"summary": summary,
|
|
"issues": [asdict(i) for i in issues],
|
|
}, indent=2), encoding="utf-8")
|
|
|
|
print(json.dumps(summary, indent=2))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|