Skip to content

Remove nerdamer: SymPy is the only symbolic engine - #130

Merged
vl3c merged 10 commits into
mainfrom
claude/remove-nerdamer-hsp1n0
Oct 17, 2026
Merged

vl3c merged 10 commits into
mainfrom
claude/remove-nerdamer-hsp1n0

Conversation

@vl3c

@vl3c vl3c commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

Requested by V · project thread

Before: derive, expand, factor and systems of equations ran on nerdamer in the browser (unmaintained, with a time guard against its runaway limit()), and the other CAS tools fell back to it when the server had no SymPy. Drawing a function asked nerdamer for denominator roots.

After: SymPy on the server answers every symbolic tool, and every result is still checked numerically in the browser with math.js. nerdamer and nerdamer_guard.js are deleted. Without SymPy, the symbolic tools say it is needed, while definite integrals, limits, solve, systems (linear ones by elimination) and numeric_integrate still answer numerically and say so. Drawing needs no CAS.

What users and the model see change:

  • factor(360) is 2^3*3^2*5; expand/factor/derive give SymPy's forms (x^2+2*x+1).
  • Systems: linear (linsolve, with "No solution" and "Infinitely many ... for any y"), polynomial systems via a Groebner shape basis (every solution, a count of complex ones; a quintic system in 0.3 s), dependent curves, parameters kept (x + y = a). y = 2^x, y = x^2 lists all three real solutions by substitution. Systems read |x| and x(x+1) as solve does; glued products beside their unknown (y = mx + b) and equations holding no unknown (b = 2) are refused with the form to write.
  • solve with no closed form (cos(x) = x) finds the real roots numerically from the inequality sign chart and says so: on [-2π, 2π] only when x occurs only inside trig calls or the equation keeps crossing (x*sin(x) = 1, with a note); x + sin(x) = 10 and sin(x) = x/10 on the whole line.
  • Formulas solve for a variable: R = V/I for I, T = 2*pi*sqrt(L/g) for g, a^x = b, |x| = a, N = N0*e^(-k*t) for t.
  • arcsin, arccos, arctan (and arcsec, arccsc, arccot) are aliases everywhere, including evaluate.
  • ∫|x| dx is x^2*sign(x)/2 instead of SymPy's Piecewise (which was refused).
  • math.js now reads ln and sgn, so every numeric check reads them.

How:

  • static/sympy_cas.py: expand, factor, solve_system, roots with parameters (solveset, else sp.solve checked at sample parameter values), Piecewise-free antiderivatives.
  • static/client/utils/polynomials.py: coefficients from math.js's parse tree in exact fractions; utils/numeric_equations.py: roots from the sign chart; utils/linear_systems.py: elimination without SymPy.
  • math_utils.py: tools ask SymPy only; zeros of non-polynomial denominators and abs arguments come from a scan of [-1000, 1000] (Illinois false position, zeros nearest 0 kept, a finer rescan where zeros are dense), probed through one JavaScript function.
  • Deliberate deviation from the brief: the test helper WithoutSympy stays, to pin the no-SymPy path (design review M2).

Reviewed in three independent rounds (reports in the project folder); the minor findings are listed as follow-ups under Roadmap A1 CAS reliability 10.

Tests: server 4250 passed; client 4059/4059 locally; CI green.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QyBvusqEMjyZ7mMd45ji2S

expand and factor (whole numbers into primes), solve_system (linsolve for
linear systems, a Groebner shape basis for polynomial systems, whole curves
for dependent ones), roots with parameters, and indefinite integrals of abs
and sign written with sign() instead of Piecewise.

Claude-Session: https://claude.ai/code/session_01QyBvusqEMjyZ7mMd45ji2S
utils/polynomials.py reads a polynomial's coefficients from math.js's parse
tree with exact fractions; utils/numeric_equations.py finds the real roots of
an equation from the inequality sign chart when SymPy has no closed form.

Claude-Session: https://claude.ai/code/session_01QyBvusqEMjyZ7mMd45ji2S
derive, expand and factor now ask the server's SymPy like the other tools, and
systems of equations go to its solve_system (substitution for a transcendental
pair with y = f(x), else the Newton solver). Without SymPy the symbolic tools
say it is needed, while definite integrals, limits, solve and systems answer
numerically and say so. The asymptote analysis reads polynomial coefficients
from math.js's parse tree and scans for other zeros, so drawing needs no CAS.
math.js now reads ln and sgn, so every numeric check reads them.

Claude-Session: https://claude.ai/code/session_01QyBvusqEMjyZ7mMd45ji2S
The library, its limit() time guard and their script tags are gone; SymPy on
the server is the only symbolic engine.

Claude-Session: https://claude.ai/code/session_01QyBvusqEMjyZ7mMd45ji2S
Roadmap item 10 lists the change and its known gaps.

Claude-Session: https://claude.ai/code/session_01QyBvusqEMjyZ7mMd45ji2S
@vl3c vl3c self-assigned this Oct 17, 2026
Sign changes are narrowed by Illinois false position (about 10 evaluations
instead of 40), denominator zeros get the quick pole verdict tan() poles
already had, and the math.js probe runs in JavaScript so values past 2^53 no
longer go through Brython's slow big-int exception. Unbounded 1/sin(x) drops
from 2 s to about 0.5 s; 1/(e^x-2) from 0.55 s to 0.14 s.

Claude-Session: https://claude.ai/code/session_01QyBvusqEMjyZ7mMd45ji2S
- solve: accept solveset's {root} \ {pole} answer and fall back to
  sp.solve, checked at sample parameter values, for formulas solved for
  a variable (R = V/I for I, T = 2*pi*sqrt(L/g) for g, a^x = b)
- arcsin, arccos, arctan, arcsec, arccsc and arccot are aliases of asin
  and the rest, read by every symbolic tool and the evaluator
- the numeric root finder and inequality chart use the [-2*pi, 2*pi]
  window only when the variable occurs only inside trig calls, or the
  equation keeps changing sign far from 0 (with a note); x + sin(x) = 10
  is solved on the whole grid
- the asymptote zero scan keeps the zeros nearest 0 and rescans the
  middle finely when zeros are closer than its samples (1/sin(10x))
- systems read input as solve does (|x|, x(x+1)), refuse an equation
  holding none of the unknowns instead of calling it a contradiction,
  and solve linear systems by elimination without SymPy
- numeric_integrate names other names in the integrand
- the expression table golden records the new aliases; its math.js
  recorder stands in for window.Function

Claude-Session: https://claude.ai/code/session_01QyBvusqEMjyZ7mMd45ji2S
- solve: unwrap solveset's ConditionSet on the parameters (|x| = a gives
  a, -a) and keep a formula root that holds at any sample parameter
  values that make it real (A = P*(1-r)^t for t, sqrt(x) = a-5)
- systems refuse glued products with an unknown (y = mx + b, y = ax^2)
  with the form to write, as solve does
- the elimination for linear systems runs only without SymPy and checks
  linearity at spread-out points, so max(x, 1) is not taken for a line
- the quick pole verdict avoids generators and casts (tan(10x) analysis
  1.7 s -> 1.0 s)
- the Roadmap lists the minor findings as follow-ups

Claude-Session: https://claude.ai/code/session_01QyBvusqEMjyZ7mMd45ji2S
xA and xB, vx and vy, xi and eta are names in a system without x, y or z
alone; y = mx + b beside y = -x + 3 is still refused. A ConditionSet on
the parameters is unwrapped only when its condition can hold.

Claude-Session: https://claude.ai/code/session_01QyBvusqEMjyZ7mMd45ji2S
@vl3c
vl3c marked this pull request as ready for review October 17, 2026 19:52
@vl3c
vl3c merged commit ba39429 into main Oct 17, 2026
6 checks passed
@vl3c
vl3c deleted the claude/remove-nerdamer-hsp1n0 branch October 17, 2026 19:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants