reasoning-gym/reasoning_gym/algebra/simple_equations.py
2025-02-08 20:59:59 +00:00

125 lines
4.6 KiB
Python

"""
Simple equations exercise that generates and solves linear equations with one variable.
"""
from typing import Dict, Any
from sympy import Symbol, solve, parse_expr, Eq
class SimpleEquationsExercise:
"""Exercise generator for simple equations with one variable."""
def __init__(self):
self.curriculum = None
def generate(self, curriculum: Any) -> Dict[str, Any]:
"""
Generate a simple equation problem using the curriculum.
Returns:
Dict containing:
- question: str (e.g. "Find the value of x in the equation: 3*x + 2 = 4*x - 1")
- answer: str (the solution value, e.g. "3")
- metadata: dict with details (equation, variable, etc.)
"""
self.curriculum = curriculum
template = curriculum.get_template(curriculum.rng)
return template.eval(self, curriculum.rng)
def _parse_expression(self, metadata: Dict[str, Any]) -> Dict[str, Any]:
"""
Parse the template metadata into structured data.
The metadata structure is expected to be:
{
"lhs": {
"term_0": {
"sign": str, # "" or "-"
"coeff": str, # coefficient value with "*" if needed
"variable": str # variable name or empty
},
"term_1": {...}, # Same structure as term_0
...,
"op_0": str, # "+" or "-" between terms
"op_1": str, # More operators if needed
...
},
"rhs": { # Same structure as lhs
...
},
"variable": {
"var": str # The variable name used in the equation
}
}
Args:
metadata: Raw metadata from template evaluation
Returns:
Dictionary containing:
- lhs_terms: List[str] of formatted term strings for left side
- rhs_terms: List[str] of formatted term strings for right side
- lhs_operators: List[str] of operators between left terms
- rhs_operators: List[str] of operators between right terms
- variable: str, the variable name used
"""
def parse_side(side_parts: Dict[str, Any]) -> tuple[list, list]:
"""Helper to parse one side of the equation."""
terms = []
operators = []
i = 0
while f"term_{i}" in side_parts:
term_dict = side_parts[f"term_{i}"]
terms.append("".join(term_dict[k] for k in ("sign", "coeff", "variable")))
if f"op_{i}" in side_parts:
operators.append(side_parts[f"op_{i}"])
i += 1
return terms, operators
# Parse both sides of the equation
lhs_terms, lhs_operators = parse_side(metadata["lhs"])
rhs_terms, rhs_operators = parse_side(metadata["rhs"])
return {
"lhs_terms": lhs_terms,
"rhs_terms": rhs_terms,
"lhs_operators": lhs_operators,
"rhs_operators": rhs_operators,
"variable": metadata["variable"]["var"]
}
def _evaluate_expression(self, parsed: Dict[str, Any]) -> str:
"""
Evaluate the equation and find its solution.
Args:
parsed: Dictionary containing parsed expression data
Returns:
String representation of the solution
"""
# Create sympy symbol from parsed variable
var = Symbol(parsed["variable"])
# Build left and right expressions
def build_expr(terms: list, operators: list) -> str:
"""Helper to build expression string from terms and operators."""
expr = terms[0]
for i, op in enumerate(operators):
expr = f"{expr} {op} {terms[i + 1]}"
return expr
lhs_expr = build_expr(parsed["lhs_terms"], parsed["lhs_operators"])
rhs_expr = build_expr(parsed["rhs_terms"], parsed["rhs_operators"])
try:
# Parse both sides into sympy expressions
lhs = parse_expr(lhs_expr, local_dict={parsed["variable"]: var})
rhs = parse_expr(rhs_expr, local_dict={parsed["variable"]: var})
# Solve the equation
solution = solve(Eq(lhs, rhs), var)
# Convert to float and return as string
if solution:
return str(float(solution[0]))
return ""
except Exception as e:
return f"Error solving equation: {lhs_expr} = {rhs_expr}\nError: {str(e)}"