agentleFS
topprismdatatopprismdata/cultivating-ml-agent/skills/examples/sc-tir-mathematical-reasoning/SKILL.md

sc-tir-mathematical-reasoning

Skill5 starsChanged 35 days agoMITView on GitHub

Self-Consistency with Tool-Integrated Reasoning (SC-TIR) for mathematical problem solving. Use when: (1) Building mathematical reasoning systems, (2) Competing in math competitions (AIMO, AIME, MATH), (3) Need precise calculation beyond text generation, (4) LLM outputs contain calculation errors. Covers: SC-TIR algorithm (N×M candidates), Python code execution, majority voting, answer extraction from LaTeX \boxed{}, champion methodology from Numina AIMO Progress Prize winner.

The file

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 4 are distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈4

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Recommendation: Start with N=4, add code execution if accuracy insufficient.

    line 292
  2. Distinctive:

    Must be trained/fine-tuned for code generation

    line 356
  3. Distinctive:

    Always use timeout (5 seconds recommended)

    line 362
  4. Distinctive:

    Always verify competition format before submission

    line 375

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

SC-TIR: Self-Consistency with Tool-Integrated Reasoning

Problem

Large language models struggle with mathematical reasoning due to:

  1. Calculation errors: Text-only reasoning often makes arithmetic mistakes
  2. Lack of precision: Cannot reliably compute complex expressions
  3. Single-path failure: One reasoning trace may hit a dead end
  4. No verification: Cannot check intermediate results

Symptoms:

  • Model gives correct reasoning but wrong final answer
  • Long calculations contain arithmetic errors
  • Inconsistent answers across multiple runs
  • Competition scores plateau around 30-40%

Context / Trigger Conditions

Use SC-TIR when:

  • Working on mathematical reasoning tasks (AIMO, AIME, MATH benchmarks)
  • LLM outputs need precise calculation (algebra, calculus, combinatorics)
  • Single-pass reasoning is insufficient or unreliable
  • Need state-of-the-art performance on math competitions
  • Problem: "My model gets the reasoning right but the answer wrong"

Competition context:

  • AIMO (AI Mathematical Olympiad) - 0-99999 integer answers
  • MATH benchmark - High school competition problems
  • AIME - American Invitational Mathematics Examination

Solution

SC-TIR Algorithm Overview

SC-TIR combines two powerful techniques:

  1. Self-Consistency: Sample multiple reasoning paths and vote
  2. Tool-Integrated Reasoning: Model generates Python code for precise calculation

Core parameters:

  • N (width): Number of reasoning traces to generate (typical: 4-48)
  • M (depth): Number of iterations per trace (typical: 1-4)

Algorithm flow:

Input: Mathematical problem P
Output: Final answer A

1. Initialize N candidates with problem text
2. For each candidate i in 1..N:
   For iteration j in 1..M:
     a) Model generates Python code to solve current step
     b) Execute code, capture output
     c) Append output to context
     d) If no code generated → restart or prune
   End For
3. Extract all \boxed{answer} from candidates
4. Filter invalid answers (non-numeric, negative)
5. Apply modulo 1000 (if required by competition)
6. Majority vote: select most common answer
7. Return A

Implementation: PythonREPL Engine

Safe code execution with timeout:

import subprocess
import tempfile
import os

class PythonREPL:
    def __init__(self, timeout=5):
        self.timeout = timeout

    def __call__(self, query):
        """Execute Python code safely with timeout"""
        # Auto-import math libraries
        query = "import math\nimport numpy as np\nimport sympy as sp\n" + query

        # Auto-wrap print if needed
        if "print(" not in query[-1]:
            query[-1] = "print(" + query[-1] + ")"

        # Execute in temporary file
        with tempfile.TemporaryDirectory() as temp_dir:
            temp_file_path = os.path.join(temp_dir, "tmp.py")
            with open(temp_file_path, "w", encoding="utf-8") as f:
                f.write(query)

            result = subprocess.run(
                ["python3", temp_file_path],
                capture_output=True,
                check=False,
                text=True,
                timeout=self.timeout,
            )

            if result.returncode == 0:
                return True, result.stdout.strip()
            else:
                return False, result.stderr.strip()

Key features:

  • 5-second timeout prevents infinite loops
  • Auto-import of math, numpy, sympy
  • Temp file execution for isolation
  • Error traceback capture

Implementation: Answer Extraction

Extract LaTeX boxed answers:

import re

def extract_boxed_answer(text):
    """Extract \\boxed{answer} format - handles nested braces"""
    # Find last \\boxed or \\fbox
    idx = text.rfind("\\boxed")
    if idx < 0:
        idx = text.rfind("\\fbox")
        if idx < 0:
            return None

    # Find matching right brace (handle nesting)
    i = idx
    right_brace_idx = None
    num_left_braces_open = 0
    while i < len(text):
        if text[i] == "{":
            num_left_braces_open += 1
        if text[i] == "}":
            num_left_braces_open -= 1
            if num_left_braces_open == 0:
                right_brace_idx = i
                break
        i += 1

    if right_brace_idx is None:
        return None

    # Extract content between braces
    boxed = text[idx:right_brace_idx + 1]
    try:
        left = "\\boxed{"
        assert boxed[:len(left)] == left
        assert boxed[-1] == "}"
        return boxed[len(left):-1]
    except Exception:
        return None

Validate and filter answers:

from collections import Counter

def validate_answer_is_numeric(x, tolerance=0.2):
    """Check if answer is valid integer"""
    try:
        x = round(float(x))
        f = float(x)
        if abs(x - f) > tolerance:
            x = -1
    except Exception:
        x = -1
    return x

def filter_answers(answers):
    """Filter and normalize answers"""
    formatted = [validate_answer_is_numeric(a) for a in answers]
    filtered = [a % 1000 for a in formatted if a >= 0]
    return filtered

def get_majority_vote(answers):
    """Majority voting for final answer"""
    if not len(answers):
        return 0
    c = Counter(answers)
    value, _ = c.most_common(1)[0]
    return value

Implementation: Prompt Format

Champion-validated format (NuminaMath):

def format_prompt(problem: str) -> str:
    """Format prompt using DeepSeekMath chat template"""
    return f"### Problem: {problem}\n### Solution:"

Why this format matters:

  • Consistent with training data (NuminaMath-TIR)
  • Clear role separation (Problem vs Solution)
  • Explicit code generation trigger
  • Better than generic "Solve:" prompts

Implementation: Complete SC-TIR Solver

class SCTIRSolver:
    def __init__(self, model_path, n_candidates=8, temperature=0.8):
        self.model_path = model_path
        self.n_candidates = n_candidates
        self.temperature = temperature
        self.repl = PythonREPL(timeout=5)
        self.model = None
        self.tokenizer = None

    def load(self):
        """Lazy model loading"""
        if self.model is None:
            from transformers import AutoTokenizer, AutoModelForCausalLM
            self.tokenizer = AutoTokenizer.from_pretrained(
                self.model_path, trust_remote_code=True
            )
            self.model = AutoModelForCausalLM.from_pretrained(
                self.model_path,
                device_map="auto",
                torch_dtype=torch.float16,
                trust_remote_code=True
            )

    def generate_candidate(self, problem: str) -> int:
        """Generate single candidate with code execution"""
        if self.model is None:
            self.load()

        prompt = self.format_prompt(problem)

        # Generate response
        inputs = self.tokenizer(prompt, return_tensors='pt').to(self.model.device)
        outputs = self.model.generate(
            **inputs,
            max_new_tokens=2048,
            temperature=self.temperature,
            top_p=0.95,
            do_sample=True,
            pad_token_id=self.tokenizer.eos_token_id
        )

        response = self.tokenizer.decode(
            outputs[0][inputs['input_ids'].shape[1]:],
            skip_special_tokens=True
        )

        # Extract answer (with or without code execution)
        return extract_answer_from_response(response)

    def predict(self, problem: str) -> int:
        """SC-TIR prediction with majority voting"""
        if self.model is None:
            self.load()

        # Generate N candidates
        candidates = []
        for i in range(self.n_candidates):
            answer = self.generate_candidate(problem)
            candidates.append(answer)

        # Filter and vote
        filtered = filter_answers(candidates)
        if filtered:
            return get_majority_vote(filtered)

        # Fallback
        return candidates[-1] % 1000 if candidates else 0

Parameter Selection Guide

ConfigurationN (candidates)M (iterations)Expected AccuracyTimeUse Case
Fast4130-35%1xQuick iteration
Balanced8-121-240-50%2-3xGood balance
Champion48458%12xCompetition
With code4140-50%1.5xBest value

Recommendation: Start with N=4, add code execution if accuracy insufficient.

Verification

Success indicators:

  • ✅ Model generates Python code blocks (python...)
  • ✅ Code executes without timeout
  • ✅ Answers extracted from \boxed{} format
  • ✅ Majority voting produces consistent results
  • ✅ Accuracy improves over single-pass (30% → 40%+)

Test with sample problem:

problem = "If $x + 3 = 10$, what is $x$?"
solver = SCTIRSolver("path/to/model", n_candidates=4)
answer = solver.predict(problem)
assert answer == 7, f"Expected 7, got {answer}"

Example

Problem: "Find the sum of all positive integers n such that n^2 + 5n + 6 is a prime number."

Without SC-TIR (single pass):

Model: "We need n^2 + 5n + 6 to be prime. Let's try n=1: 1+5+6=12 (not prime).
n=2: 4+10+6=20 (not prime)..."
→ May make calculation error or miss edge cases

With SC-TIR (N=8 candidates):

# Candidate 1 generates:
def solve():
    import sympy as sp
    n = sp.symbols('n', integer=True, positive=True)
    expr = n**2 + 5*n + 6
    # Factor: (n+2)(n+3)
    # For product to be prime, one factor must be 1
    solutions = [1]  # Only n=1 gives (1+2)(1+3)=3*4=12 (not prime)
    # Actually no positive integer solutions
    return 0

# Candidate 2 generates:
def solve():
    # n^2 + 5n + 6 = (n+2)(n+3)
    # For prime, one factor must be 1
    # n+2 = 1 → n = -1 (not positive)
    # n+3 = 1 → n = -2 (not positive)
    return 0  # No solutions

Execution: 6/8 candidates return 0 → majority vote = 0

Result: Correct answer found through code execution and majority voting.

Notes

Computational cost:

  • N=4: ~2-4 minutes per problem on T4 GPU
  • N=48: ~20-30 minutes per problem (champion configuration)
  • Trade-off: accuracy vs time

Model requirements:

  • Must be trained/fine-tuned for code generation
  • NuminaMath-7B-TIR (recommended): 68.2% MATH, 58% AIMO
  • DeepSeekMath-7B-RL: 58.8% MATH
  • Generic models (GPT-4, Claude): Can work but less optimal

Code execution safety:

  • Always use timeout (5 seconds recommended)
  • Execute in temp directory or container
  • Block dangerous operations (subprocess, file writes)
  • Capture both stdout and stderr

Alternative without code execution:

  • Higher N values (12-20) can compensate
  • Still benefits from self-consistency
  • Simpler implementation, faster iteration

Competition-specific:

  • AIMO: Answers modulo 1000
  • MATH: Exact answers, no modulo
  • Always verify competition format before submission

See also:

  • aimo3-inference-gateway-system - AIMO3 competition specifics
  • kaggle-top-solution-replication - How to study winning solutions
  • llm-mathematical-reasoning-benchmark - Model selection for math tasks

References

Verified on:

  • AIMO3 Progress Prize competition (29/50 = 58% accuracy)
  • MATH benchmark (68.2% accuracy)
  • Kaggle T4 GPU environment (16GB VRAM)

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.