Repository navigation
Goal: feature parity with sympy #717
Description
Activity
A first measured data point for this goal, since "parity with sympy" has never had a number attached and the raw list of things we cannot do overstates the gap.
sympy 1.14.0 against the three problems
casbenchreports AngouriMath cannot solve, onmasterata45a7256:problem AngouriMath sympy 1.14 ∫ x^2 / (x^4 + 1) dxunevaluated solved — sqrt(2)*log(x^2 - sqrt(2)x + 1)/8 - …∫ sqrt(tan(x)) dxunevaluated also unevaluated — returns Integral(sqrt(tan(x)), x)ln(x) + ln(y) - ln(x*y)not reduced to 0 also not reduced — simplifyleaves it as writtenOnly one of the three is a parity gap. The other two sympy declines as well under its default
integrate/simplify— the logarithm one because combining logs needs assumptions sympy will not make either, andsqrt(tan(x))because it is genuinely hard.That matters for how this goal is scoped: a list of "things AngouriMath cannot do" is not a list of "ways AngouriMath is behind sympy", and the difference here is two thirds. Anyone measuring progress against this issue should compare against sympy on the same input rather than against an absolute standard, or the target will look much further away than it is.
The one real gap — partial fractioning over an irreducible quartic — is already tracked as an unticked box on #233.
For the integration side more broadly, the honest figure is on #718: 536 of 1774 (30.2%) of Rubi's fair problems, against 116 of 119 (97.5%) on the corpus we wrote ourselves. I have not run sympy over Rubi's suite; that would be the natural next measurement for this issue and it is a day's work rather than a design question.
You can research sympy documentation and code to identify all the features and techniques we are behind
You can research sympy documentation and code to identify all the features and techniques we are behind
Done as a measurement rather than as a reading of two feature lists: 80 probes across 23 areas, run on both sides, SymPy 1.14 in Python and AngouriMath in C#, neither half seeing the other's answer. On
masterat4ee698da.28 answer 9 decline — the operation exists and hands the expression back 5 implemented but not exposed 35 absent 1 throws 2 unclear The most useful finding: five capabilities exist and have no entry point
The gap is a public method, not an algorithm.
inside resultant PolynomialResultantsquare-free decomposition SquareFreeDecomposition,SquareFreePart,FactorSquareFree,IsSquareFreeGröbner basis Buchberger,Fglm,GroebnerSystemSolverasymptotic expansion AsymptoticSeriesrow echelon form rrefFour of the five are the polynomial layer that #746 item 43 calls the highest-leverage work on its list — and a good part of it is already written and unreachable. Exposing them is small, and it is the cheapest way to close distance with SymPy in this survey.
I would not have found this by searching the public surface, which is what I did first: the probe now searches non-public types too and reports the difference. It also has to match at a name boundary — searching for
DsolvefoundTryGCDSolveandRsolvefoundCommonDenominatorSolver, either of which would have had the report claim an ODE solver we do not have.Where we decline
The operation exists and does nothing on the input, which is honest but is the real feature gap:
SymPy us factor(x**4 - 1)(x - 1)*(x + 1)*(x**2 + 1)x ^ 4 - 1gcd(x**2*y + x*y**2, x*y)x*yunevaluated gcd(…)summation(k, (k, 1, n))n**2/2 + n/2unevaluated sum(…)summation(1/k**2, (k, 1, oo))pi**2/6unevaluated product(k, (k, 1, n))factorial(n)unevaluated sqrtdenest(sqrt(5 + 2*sqrt(6)))sqrt(2) + sqrt(3)unchanged logcombine(log(x) + log(y))log(x*y)unchanged integrate(exp(-x**2), x)sqrt(pi)*erf(x)/2unevaluated — no erfto answer withsqrt(p**2)withp > 0punchanged — no assumptions factoris the one I would put first: it works onx ^ 2 - 1and not onx ^ 4 - 1,x ^ 3 - xorx ^ 2 + 2x + 1, so what exists is pattern-shaped rather than an algorithm. Closed-form summation is the second — three of the nine rows above aresumandproduct, and #248 already tracks the node.Absent, by area
- polys — Gröbner basis as an API, minimal polynomial, partial fractions, resultant as an API, square-free as an API
- solvers — ODE, PDE, recurrence relations
- matrices — eigenvalues, characteristic polynomial, Jordan form
- functions —
zeta,erf, Bessel, Lambert W, polylogarithm - series — residue
- integrals — Laplace transform, Fourier transform, multiple integrals over a region
- logic — satisfiability, CNF
- ntheory — Möbius, Chinese remainder, continued fractions
- printing — C code generation, MathML
- and whole modules with no counterpart:
stats,geometry,physics.units,vector,tensor,diffgeom,combinatorics,holonomic,discrete(FFT, convolution)
The last group is where I would not chase parity. SymPy is a general scientific stack; #746 is a symbolic kernel with layers above it.
stats,geometryandphysics.unitsare v9.0 knowledge packages in that plan, not kernel gaps.Where we are ahead, or simply different
Worth recording so the survey is not only a list of deficits.
solve(cos(x) - x)returns 14 roots — the real one and thirteen complex ones, each correct to 1e-15 (checked againstmpmath.findroot). SymPy'snsolvegives one, andsolvesetdeclines.- Complex evaluation is exact-decimal and matches
mpmathat 20 digits on every trig probe. - Every result carries a
Providedcondition where one is needed; SymPy'scancel((x**2-1)/(x-1))givesx + 1with no mention ofx ≠ 1, ours says so. Compileproduces a real delegate;lambdifyproduces Python.
Two defects fell out of it
- A symbolic determinant is a quotient by its pivots, so it is NaN for ordinary matrices #992 — a symbolic determinant is a quotient by its pivots, so
det [[a,b,c],[d,e,f],[g,h,i]]isNaNfor two of four ordinary integer matrices, including[[0,1,2],[3,4,5],[6,7,8]]. - ToSympyCode drops a lambda's body and throws on a set builder #985 —
ToSympyCodedrops a lambda's body and throws on a set builder.
The harness is checked in at
work/sympyparitywith the generated table, so this is re-runnable rather than a snapshot: every verdict in it is computed, including "declines", which is measured against the probe's own input rather than judged.Reacted by Hadrian TangGood measurement. We need to do this periodically, and also consider all test cases there.
Re-measuring the survey rather than reading it, because one of its rows has gone stale and
it is the row the survey singles out as the one to do first.Measured at
c639d489— master1f3b02aeplus the pull request in flight for #233, which
touches only the integrator and so cannot reachFactorize.factoris no longer pattern-shapedfactoris the one I would put first: it works onx ^ 2 - 1and not onx ^ 4 - 1,
x ^ 3 - xorx ^ 2 + 2x + 1, so what exists is pattern-shaped rather than an algorithm.All three of the named failures now factor, and they factor the way an algorithm does rather
than the way a pattern does:input survey says now x ^ 4 - 1x ^ 4 - 1(x + 1) * (x - 1) * (x ^ 2 + 1)x ^ 3 - xdeclines x * (x + 1) * (x - 1)x ^ 2 + 2x + 1declines (x + 1) ^ 2x ^ 2 - 1(x - 1) * (x + 1)unchanged So the SymPy row
factor(x**4 - 1)→(x - 1)*(x + 1)*(x**2 + 1)is now matched exactly.
What changed is the polynomial layer the survey itself points at: Zassenhaus factoring over
Qwith a Yun square-free decomposition in front of it, reached throughFactorize. The
diagnosis in the survey was right — the old behaviour was pattern-shaped — and the fix was
to stop asking the rewrite rules.x ^ 4 + 1still comes back whole, and that is the correct answer rather than a remaining
gap: it is irreducible over the rationals, andMathS.Polynomials.Factor's own documentation
says so.The "solvers — ODE" row is also stale
solvers — ODE, PDE, recurrence relations
An analytical solver for first-order linear ordinary differential equations landed in #1137,
throughMathS.SolveOde. PDE and recurrence relations are still absent, so the row wants
narrowing rather than deleting.Rows I re-measured that still stand
Unchanged, so the survey is right about these:
input still sqrt(5 + 2*sqrt(6))unchanged — no denesting ln(x) + ln(y)unchanged — no logcombineln(x) + ln(y) - ln(x*y)not reduced to 0integrate(exp(-x**2), x)unevaluated — still no erfsum(k, k, 1, n)unevaluated product(k, k, 1, n)unevaluated Which leaves closed-form summation as the survey's own second pick and now its first, with
three of the nine rows in the table.I did not re-measure
gcd(x**2*y + x*y**2, x*y)orsqrt(p**2)underp > 0, so those two
rows are still as filed.On the integration rows
Worth noting next to this survey rather than in it: of the five integrals
#233 lists as unsolvable, all five
now answer,sqrt(tan(x))included — which is the one this issue records as failing in SymPy
too. That does not move a row here, since the survey does not list those, but it is the same
area and the corpus gate went from 39 solved of 40 to 40 of 40 with it.Re-measured on
masteratc19393ee. Three of the survey's nine rows have moved, and they are
the three that weresumandproduct.row survey now summation(k, (k, 1, n))unevaluated sum(…)piecewise((n + n^2)/2 provided n >= 0, 0)product(k, (k, 1, n))unevaluated piecewise(n! provided n >= 1, 1)factor(x**4 - 1)x ^ 4 - 1(x + 1) * (x - 1) * (x ^ 2 + 1)— reported above on 2026-09-02PRs #1152 and
#1153.sum(k^2, k, 1, n)and
product(k^2, k, 1, n)come with them, as does any polynomial summand, any monomial product body,
and a symbolic lower bound.Neither answer is the bare closed form SymPy prints, and that is deliberate. This library
answers an empty range with the operator's identity —sum(k, k, 5, 1)is0and
product(k, k, 5, 1)is1, both with existing tests — so(n + n^2)/2is wrong atn = -2,
where the sum is0and the polynomial is1. SymPy prints the bare form because it reads a
reversed range as the negated sum over the flipped one, under which no condition is needed. The
condition is what the different convention costs; where the bounds are concrete it is decidable
and the piecewise collapses to a number.Rows I re-measured that still stand
input still summation(1/k**2, (k, 1, oo))unevaluated — needs zeta, which the table records as absent, and an infinite bound is refused outright by both closed formssqrtdenest(sqrt(5 + 2*sqrt(6)))unchanged logcombine(log(x) + log(y))unchanged log(x) + log(y) - log(x*y)not reduced to 0integrate(exp(-x**2), x)unevaluated — still no erfgcd(x**2*y + x*y**2, x*y)unevaluated gcd(…)sqrt(p**2)withp > 0unchanged So of the survey's own two picks,
factoris done and closed-form summation is done; what is
left in that area is the two that need a special function this library does not have —zetafor
the infinite series anderffor the Gaussian integral — and those are absent-by-name rather than
gaps in an algorithm.The "solvers — ODE" row under Absent, by area is still narrower than it reads: an analytical
solver for first-order linear equations landed in
#1137. PDE and recurrence relations are
absent as recorded.- added a commit that references this issue
on Sep 3, 2026
Edit this issue to make a plan that researches sympy bugs, features, tests, benchmarks, and follow its activity. Keep this issue open to track which sympy commit we have feature parity with. ToSympy should also work for all nodes that do have a sympy equivalent; ideally we have even more than them.