Skip to content

Goal: feature parity with sympy #717

Description

@Happypig375

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.

Activity

  1. Rafael-SOWNet commented on Aug 16, 2026

    @Rafael-SOWNet
    Member

    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 casbench reports AngouriMath cannot solve, on master at a45a7256:

    problem AngouriMath sympy 1.14
    ∫ x^2 / (x^4 + 1) dx unevaluated solved — sqrt(2)*log(x^2 - sqrt(2)x + 1)/8 - …
    ∫ sqrt(tan(x)) dx unevaluated also unevaluated — returns Integral(sqrt(tan(x)), x)
    ln(x) + ln(y) - ln(x*y) not reduced to 0 also not reduced — simplify leaves it as written

    Only 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, and sqrt(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.

  2. Happypig375 commented on Aug 16, 2026

    @Happypig375
    MemberAuthor

    You can research sympy documentation and code to identify all the features and techniques we are behind

  3. Rafael-SOWNet commented on Aug 20, 2026

    @Rafael-SOWNet
    Member

    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 master at 4ee698da.

    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 PolynomialResultant
    square-free decomposition SquareFreeDecomposition, SquareFreePart, FactorSquareFree, IsSquareFree
    Gröbner basis Buchberger, Fglm, GroebnerSystemSolver
    asymptotic expansion AsymptoticSeries
    row echelon form rref

    Four 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 Dsolve found TryGCDSolve and Rsolve found CommonDenominatorSolver, 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 - 1
    gcd(x**2*y + x*y**2, x*y) x*y unevaluated gcd(…)
    summation(k, (k, 1, n)) n**2/2 + n/2 unevaluated sum(…)
    summation(1/k**2, (k, 1, oo)) pi**2/6 unevaluated
    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)/2 unevaluated — no erf to answer with
    sqrt(p**2) with p > 0 p unchanged — no assumptions

    factor is the one I would put first: it works on x ^ 2 - 1 and not on x ^ 4 - 1, x ^ 3 - x or x ^ 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 are sum and product, 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, geometry and physics.units are 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 against mpmath.findroot). SymPy's nsolve gives one, and solveset declines.
    • Complex evaluation is exact-decimal and matches mpmath at 20 digits on every trig probe.
    • Every result carries a Provided condition where one is needed; SymPy's cancel((x**2-1)/(x-1)) gives x + 1 with no mention of x ≠ 1, ours says so.
    • Compile produces a real delegate; lambdify produces Python.

    Two defects fell out of it

    The harness is checked in at work/sympyparity with 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.

  4. Happypig375 commented on Aug 20, 2026

    @Happypig375
    MemberAuthor

    Good measurement. We need to do this periodically, and also consider all test cases there.

  5. Rafael-SOWNet commented on Sep 2, 2026

    @Rafael-SOWNet
    Member

    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 — master 1f3b02ae plus the pull request in flight for #233, which
    touches only the integrator and so cannot reach Factorize.

    factor is no longer pattern-shaped

    factor is the one I would put first: it works on x ^ 2 - 1 and not on x ^ 4 - 1,
    x ^ 3 - x or x ^ 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 - 1 x ^ 4 - 1 (x + 1) * (x - 1) * (x ^ 2 + 1)
    x ^ 3 - x declines x * (x + 1) * (x - 1)
    x ^ 2 + 2x + 1 declines (x + 1) ^ 2
    x ^ 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
    Q with a Yun square-free decomposition in front of it, reached through Factorize. 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 + 1 still comes back whole, and that is the correct answer rather than a remaining
    gap: it is irreducible over the rationals, and MathS.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,
    through MathS.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 logcombine
    ln(x) + ln(y) - ln(x*y) not reduced to 0
    integrate(exp(-x**2), x) unevaluated — still no erf
    sum(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) or sqrt(p**2) under p > 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.

  6. Rafael-SOWNet commented on Sep 3, 2026

    @Rafael-SOWNet
    Member

    Re-measured on master at c19393ee. Three of the survey's nine rows have moved, and they are
    the three that were sum and product.

    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-02

    PRs #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) is 0 and
    product(k, k, 5, 1) is 1, both with existing tests — so (n + n^2)/2 is wrong at n = -2,
    where the sum is 0 and the polynomial is 1. 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 forms
    sqrtdenest(sqrt(5 + 2*sqrt(6))) unchanged
    logcombine(log(x) + log(y)) unchanged
    log(x) + log(y) - log(x*y) not reduced to 0
    integrate(exp(-x**2), x) unevaluated — still no erf
    gcd(x**2*y + x*y**2, x*y) unevaluated gcd(…)
    sqrt(p**2) with p > 0 unchanged

    So of the survey's own two picks, factor is 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 — zeta for
    the infinite series and erf for 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.

  7. added a commit that references this issue on Sep 3, 2026
  8. added this to the milestone on Sep 18, 2026
  9. added theissue type on Sep 22, 2026
  10. removed this from the milestone on Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions