Portfolio  ›  Projects  ›  MathGenius
Python SymPy Local LLM Streamlit

MathGenius,
Local AI Math Solver

A maths tutor that runs entirely on your own machine. SymPy computes the exact symbolic answer instantly; a local LLM writes the explanation, the alternative methods, the hints and the common mistakes around it.

◆ Local Prototype · verified running on Ollama + llama3.2
Context: Personal project, five iterations  ·  Role: Sole author
Verified: 2026-08-18, Ollama 0.32.13 + llama3.2, CPU only  ·  1 defect found and fixed
5Versions
7/7SymPy forms (was 6/7)
10Math domains
$0API cost
🐛 The Defect I Found 🧠 NeuroSlide
The Design

Two paths, deliberately independent

Every problem runs through two systems at once, and this separation is the idea worth taking from the project:

PATH A — SYMPYExact symbolic answer. Instant, deterministic, correct.
PATH B — LLMExplanation, methods, mistakes, real-world uses. ~45 s.

🎯 Why it matters

SymPy is the source of truth for the answer. The model supplies the teaching. If a small local model hallucinates a wrong root, the SymPy panel still shows the correct one beside it.

That separation is what makes a 1.9 GB model usable for mathematics at all. Asking a small model to both compute and explain gets you confident wrong arithmetic; asking it only to explain a result it was handed is a task it can actually do.

sympy_solve() is keyword-dispatched — it looks for derivative, integrate, factor, simplify, expand, or an = sign, and routes to the matching SymPy call.

Version History

Five versions

VersionLinesNotes
math_platform685Earliest build. No SymPy parsing layer
math_platform_FREE592Trimmed free edition
mathgenius_pro1,016SymPy parsing added; 6 themes
mathgenius_pro_v31,290Solver refactored into sympy_solve()
mathgenius_v41,182Current. 4 themes, quiz, ELI5, Manim hooks
The Defect

The caret operator broke its own headline example

Verification found a real bug — one that had been silently degrading the app for the whole life of the project.

🐛 x^2 - 7x + 12 = 0 returned nothing

That is the first example problem in the project's own README, and it is how most people naturally type a power. The instant-answer panel simply showed nothing — no error, no message.

The cause: sympy_solve() built its parser transformations as

TF = standard_transformations + (implicit_multiplication_application,)

SymPy's parser treats ^ as bitwise XOR, not exponentiation. So the expression raised:

TypeError: unsupported operand type(s) for ^: 'Symbol' and 'Add'

Why nobody noticed

The trap

That exception was swallowed by a bare except: pass at the end of the function. sympy_solve() returned an empty dict, and the UI rendered an empty panel — indistinguishable from "this problem type isn't supported".

The fix

Add SymPy's convert_xor transformation. ** continues to work identically. Applied to all three affected versions.

Result

6/7 → 7/7 problem forms now produce a symbolic result.

⚠️ The wider lesson

mathgenius_v4/app.py still contains 13 bare except: pass blocks. One of them hid this defect for the project's entire lifetime. Narrowing them is the obvious next cleanup and has deliberately not been done yet — it is listed as outstanding rather than quietly performed, because it is a behaviour change across code paths that have no test coverage.

Verified Run

Every result checked by hand

CheckResult
ollama_ok()Pass — True
get_models()['llama3.2:latest']
ai_solve("x^2 - 7x + 12 = 0")46.1 s, structured dict, 10 keys
short_answerx=3,4 — mathematically correct
→ classificationAlgebra · ['quadratic'] · Beginner
sympy_solve(), 7 problem forms7 / 7 after fix
Streamlit bootsHTTP 200, /_stcore/health → ok

∑ SymPy output, all verified correct by inspection

x^2 - 7x + 12 = 0                    -> solutions   ['3', '4']
Find the derivative of sin(x)*exp(x) -> derivative  exp(x)*sin(x) + exp(x)*cos(x)
integrate x**3                       -> integral    x**4/4 + C
factor x**3 - 8                      -> factored    (x - 2)*(x**2 + 2*x + 4)
simplify (x**2 - 1)/(x - 1)          -> simplified  x + 1
expand (x + 2)**3                    -> expanded    x**3 + 6*x**2 + 12*x + 8
2*x + 6 = 0                          -> solutions   ['-3']

💻 Run it

ollama serve
ollama pull llama3.2

python -m venv .venv && .venv\Scripts\activate
pip install streamlit requests sympy matplotlib plotly numpy pandas

cd mathgenius_v4
streamlit run app.py            # http://localhost:8501

manim is listed for the optional animated-video feature. It is invoked via subprocess, not imported, so the app runs fine without it — and without ffmpeg or LaTeX.

Limitations

What to trust, and what not to

Trust the answer

  • SymPy is deterministic and exact
  • Verified correct across 7 problem forms
⚠️

Read the prose as LLM output

  • Correct structure, reasonable teaching text
  • Not expert tutoring
  • Quality tracks the model used
🔍

Known gaps

  • ~45 s per AI solve on CPU
  • Keyword dispatch misses unusual phrasings
  • 13 bare excepts remain
  • No test suite
Mathematics
SymPySymbolic solvingCalculusMatrices
AI
Ollamallama3.2/api/chatJSON parsing
Interface
StreamlitPlotlyMatplotlibManim (optional)
Output

Solver output, roots marked

Plotted by the application's own graph builder, with the roots SymPy solved exactly marked on the curve. This is also the input that exposed the caret-parsing defect — before the fix it silently returned nothing.

MathGenius plot of the parabola f of x equals x squared minus 7x plus 12, with roots marked at x equals 3 and x equals 4
f(x) = x² − 7x + 12Roots marked at x = 3 and x = 4

Let the right tool do each job

A symbolic engine for the answer, a language model for the explanation. Neither is asked to do the other's work, which is why a small offline model is enough.