CoolFace
Apppublic

shivamtech9395/maths_reasoning_env

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
math_environment.py252 linesDownload Raw Back to server
1"""2MathReasoningEnvironment — core server-side logic.3Implements reset(), step(), state() with 5 graded task types.4"""5 6import uuid7import random8import math9from typing import Tuple10 11from ..models import MathAction, MathObservation, MathState12 13 14# ──────────────────────────────────────────────15# Graders (one per task type — at least 3 required)16# ──────────────────────────────────────────────17 18def grade_arithmetic(problem_data: dict, answer: str) -> Tuple[bool, str]:19    """Grader for arithmetic tasks."""20    try:21        user_val = float(answer.strip().replace(",", ""))22        expected = float(problem_data["answer"])23        correct = abs(user_val - expected) < 1e-624        return correct, str(problem_data["answer"])25    except (ValueError, KeyError):26        return False, str(problem_data.get("answer", ""))27 28 29def grade_algebra(problem_data: dict, answer: str) -> Tuple[bool, str]:30    """Grader for algebra tasks (linear equations)."""31    try:32        user_val = float(answer.strip())33        expected = float(problem_data["answer"])34        correct = abs(user_val - expected) < 1e-435        return correct, str(problem_data["answer"])36    except (ValueError, KeyError):37        return False, str(problem_data.get("answer", ""))38 39 40def grade_word_problems(problem_data: dict, answer: str) -> Tuple[bool, str]:41    """Grader for word problems."""42    try:43        user_val = float(answer.strip().replace(",", ""))44        expected = float(problem_data["answer"])45        correct = abs(user_val - expected) < 1e-446        return correct, str(problem_data["answer"])47    except (ValueError, KeyError):48        return False, str(problem_data.get("answer", ""))49 50 51def grade_number_theory(problem_data: dict, answer: str) -> Tuple[bool, str]:52    """Grader for number theory tasks (GCD, LCM, prime check)."""53    try:54        user_val = int(answer.strip())55        expected = int(problem_data["answer"])56        correct = user_val == expected57        return correct, str(problem_data["answer"])58    except (ValueError, KeyError):59        return False, str(problem_data.get("answer", ""))60 61 62def grade_geometry(problem_data: dict, answer: str) -> Tuple[bool, str]:63    """Grader for geometry tasks (area, perimeter, volume)."""64    try:65        user_val = float(answer.strip())66        expected = float(problem_data["answer"])67        correct = abs(user_val - expected) < 0.0168        return correct, str(round(problem_data["answer"], 4))69    except (ValueError, KeyError):70        return False, str(problem_data.get("answer", ""))71 72 73# Map task_type → grader function74GRADERS = {75    "arithmetic": grade_arithmetic,76    "algebra": grade_algebra,77    "word_problems": grade_word_problems,78    "number_theory": grade_number_theory,79    "geometry": grade_geometry,80}81 82TASK_TYPES = list(GRADERS.keys())83 84 85# ──────────────────────────────────────────────86# Problem generators87# ──────────────────────────────────────────────88 89def generate_arithmetic():90    ops = ['+', '-', '*', '/']91    op = random.choice(ops)92    a = random.randint(1, 50)93    b = random.randint(1, 20)94    if op == '+':95        ans = a + b96    elif op == '-':97        ans = a - b98    elif op == '*':99        ans = a * b100    else:101        b = random.randint(1, 10)102        ans = round(a / b, 4)103    return {"problem": f"What is {a} {op} {b}?", "answer": ans}104 105 106def generate_algebra():107    # ax + b = c  →  x = (c - b) / a108    a = random.randint(2, 10)109    b = random.randint(1, 20)110    x = random.randint(-10, 10)111    c = a * x + b112    return {"problem": f"Solve for x: {a}x + {b} = {c}", "answer": x}113 114 115def generate_word_problem():116    templates = [117        lambda: {118            "problem": f"Alice has {random.randint(5,30)} apples. She gives {random.randint(1,4)} to each of her {random.randint(2,5)} friends. How many apples does she have left?",119            "answer": None  # computed below120        },121    ]122    n_apples = random.randint(10, 50)123    give_each = random.randint(1, 4)124    n_friends = random.randint(2, 5)125    left = n_apples - give_each * n_friends126    if left < 0:127        left = 0128    return {129        "problem": f"Alice has {n_apples} apples. She gives {give_each} to each of her {n_friends} friends. How many apples does she have left?",130        "answer": left131    }132 133 134def generate_number_theory():135    task = random.choice(["gcd", "lcm"])136    a = random.randint(2, 30)137    b = random.randint(2, 30)138    if task == "gcd":139        ans = math.gcd(a, b)140        return {"problem": f"What is the GCD of {a} and {b}?", "answer": ans}141    else:142        ans = (a * b) // math.gcd(a, b)143        return {"problem": f"What is the LCM of {a} and {b}?", "answer": ans}144 145 146def generate_geometry():147    shape = random.choice(["circle_area", "rectangle_area", "triangle_area", "rectangle_perimeter"])148    if shape == "circle_area":149        r = random.randint(1, 10)150        ans = round(math.pi * r * r, 4)151        return {"problem": f"What is the area of a circle with radius {r}? (Use π=3.14159, round to 4 decimal places)", "answer": ans}152    elif shape == "rectangle_area":153        w, h = random.randint(2, 20), random.randint(2, 20)154        return {"problem": f"What is the area of a rectangle with width {w} and height {h}?", "answer": w * h}155    elif shape == "triangle_area":156        b, h = random.randint(2, 20), random.randint(2, 20)157        ans = 0.5 * b * h158        return {"problem": f"What is the area of a triangle with base {b} and height {h}?", "answer": ans}159    else:160        w, h = random.randint(2, 20), random.randint(2, 20)161        return {"problem": f"What is the perimeter of a rectangle with width {w} and height {h}?", "answer": 2 * (w + h)}162 163 164GENERATORS = {165    "arithmetic": generate_arithmetic,166    "algebra": generate_algebra,167    "word_problems": generate_word_problem,168    "number_theory": generate_number_theory,169    "geometry": generate_geometry,170}171 172 173# ──────────────────────────────────────────────174# Environment class175# ──────────────────────────────────────────────176 177class MathReasoningEnvironment:178    """179    OpenEnv-compatible Math Reasoning Environment.180    Supports 5 graded task types. Gymnasium-style API: reset / step / state.181    """182 183    def __init__(self):184        self._state = MathState()185        self._current_problem_data: dict = {}186 187    def reset(self, task_type: str = None) -> MathObservation:188        task = task_type if task_type in TASK_TYPES else random.choice(TASK_TYPES)189        self._state = MathState(190            episode_id=str(uuid.uuid4()),191            current_task=task,192            max_steps=10,193        )194        problem_data = GENERATORS[task]()195        self._current_problem_data = problem_data196        return MathObservation(197            problem=problem_data["problem"],198            task_type=task,199            feedback=None,200            correct=None,201            correct_answer=None,202            episode_done=False,203            score=0.0,204        )205 206    def step(self, action: MathAction) -> MathObservation:207        self._state.step_count += 1208        self._state.problems_attempted += 1209 210        task = self._state.current_task211        grader = GRADERS[task]212        correct, correct_answer = grader(self._current_problem_data, action.answer)213 214        # Reward shaping215        reward = 1.0 if correct else -0.2216        # Bonus for chain-of-thought reasoning217        if action.reasoning and len(action.reasoning.strip()) > 10:218            reward += 0.1219        self._state.total_score += reward220 221        if correct:222            self._state.problems_correct += 1223            feedback = "✅ Correct! Well done."224        else:225            feedback = f"❌ Incorrect. The correct answer was {correct_answer}."226 227        done = self._state.step_count >= self._state.max_steps228 229        # Generate next problem (unless done)230        if not done:231            next_task = random.choice(TASK_TYPES)232            self._state.current_task = next_task233            next_problem = GENERATORS[next_task]()234            self._current_problem_data = next_problem235            next_problem_text = next_problem["problem"]236        else:237            next_problem_text = "Episode complete."238 239        return MathObservation(240            problem=next_problem_text,241            task_type=self._state.current_task,242            feedback=feedback,243            correct=correct,244            correct_answer=correct_answer if not correct else None,245            episode_done=done,246            score=round(self._state.total_score, 4),247        )248 249    @property250    def state(self) -> MathState:251        return self._state252