math-openenv / src /math_env /grader.py
emrekuruu's picture
Upload folder using huggingface_hub
ba270e9 verified
Raw History Blame Contribute Delete
2.38 kB
"""MATH final-answer normalization and equality grading."""
from __future__ import annotations
import re
class InfraError(RuntimeError):
"""The server could not grade a submission because its own data is invalid."""
_STRIP_RE = re.compile(r"[\\,;\s]")
def normalize_answer(value: object) -> str:
"""Canonicalize a MATH final-answer string for comparison.
Lowercases, converts LaTeX fractions/sqrt to plain forms, collapses whitespace/commas/
backslashes, and trims stray braces/dollars so equivalent LaTeX and plain forms of the
same value agree. This is deliberately tolerant; it is not a full LaTeX semantic
normalizer.
"""
text = str(value).strip()
# Strip only dollar signs (and any outer paired braces like ${...}$); internal braces matter.
text = text.strip("$")
while text.startswith("{") and text.endswith("}") and "\\" not in text[:1]:
text = text[1:-1].strip()
# Convert \frac{a}{b}, \dfrac, \tfrac -> a/b (recursively, innermost first).
for _ in range(8):
new = re.sub(
r"\\dfrac|\\tfrac|\\frac",
r"\\frac",
text,
)
new = re.sub(
r"\\frac\s*\{([^{}]*)\}\s*\{([^{}]*)\}",
lambda m: f"{m.group(1).strip()}/{m.group(2).strip()}",
new,
)
# \sqrt{x} -> sqrt(x)
new = re.sub(
r"\\sqrt(?:\[[^\]]*\])?\s*\{([^{}]*)\}",
r"sqrt(\1)",
new,
)
# \left / \right -> empty
new = re.sub(r"\\left|\\right", "", new)
if new == text:
break
text = new
text = _STRIP_RE.sub("", text)
text = text.strip("$")
text = re.sub(r"\s+", "", text)
return text.lower()
def compare_answers(predicted: object, expected: str) -> bool:
return normalize_answer(predicted) == normalize_answer(expected)
def verification_report(predicted: object, expected: str) -> str:
"""Render a short, non-revealing grader report without leaking the reference value."""
if compare_answers(predicted, expected):
return "## Answer Verification\nNormalized match: PASS\n"
return (
"## Answer Verification\nNormalized match: FAIL\n"
"Predicted was accepted as a valid attempt; the normalized form did not equal the "
"reference answer. No reference value is exposed.\n"
)