Modern C++20 symbolic mathematics library. Clean-room port of SymPy algorithms with SymPy itself wired in as the validation oracle.
1817 tests / 7702 assertions all passing
764 cases (3915 assertions) oracle-validated against SymPy
15 of 15 phases shipped (v1.1)
On textbook-shaped inputs (calculus, algebra, transforms, solvers, sets) SymPP
and SymPy are now effectively interchangeable, and a 100-case Tier 1 + Tier 2
acceptance suite (just parity) is at SymPy parity, all oracle-validated. The
v1.1 push completed code generation (GLSL / Graphviz-dot / srepr printers, a
codegen AST with CSE, and autowrap compile-to-native — Phase 13 ✅) and
deepened Tier 2: multivariate Wang up to 4 variables and a cyclotomic
factor(x^N−1) fast path, a broad Risch integral table, hyperexpand through
squared-argument and polylog ₃F₂ forms, gamma-ratio / competing-power / tower
Gruntz limits, group theory (conjugacy/center/derived, is_solvable/
is_nilpotent/is_simple/abelian_invariants, Pólya, Sylow), and single +
multi-mode bosonic/fermionic second quantization with normal ordering. The
remaining gaps are the deepest research-grade algorithms — the full Risch tower
recursion + non-elementarity proofs (which SymPy does not do either),
arbitrary-arity Wang with full Hensel lifting, Slater-complete hyperexpand
(elliptic-K-valued pFq) — plus the low-priority large modules (F4/F5 Gröbner,
holonomic functions, Galois tools). See
docs/04-roadmap.md and
docs/10-parity-roadmap.md for the breakdown.
| Phase | Title | Status |
|---|---|---|
| 0 | Foundation & oracle harness | ✅ |
| 1 | Core expression tree | ✅ |
| 2 | Assumptions | ✅ full sign/number ontology (incl. prime/irrational/algebraic/transcendental/commutative) with closure + Add/Mul/Pow propagation, consumed by simplify/integrate/refine; scoped assuming context; boolean/SAT-style ask over arbitrary predicate combinations. At SymPy parity on the predicates (see Assumptions below) |
| 3 | Elementary & special functions | ✅ |
| 4 | Polynomials | ✅ |
| 5 | Simplification | ✅ |
| 6 | Calculus | ✅ |
| 7 | Integration | ✅ |
| 8 | Transforms | ✅ |
| 9 | Linear algebra | ✅ |
| 10 | Equation solvers | ✅ |
| 11 | ODE / PDE | ✅ |
| 12 | Units | ✅ |
| 13 | Code generation | ✅ C/C++/Fortran/LaTeX/Octave/Rust/Julia/MathML/GLSL + Graphviz-dot + srepr printers, codegen AST (CSE), lambdify/autowrap |
| 15 | Parser & MATLAB facade | ✅ |
| 16 | Hardening & v1.0 | ✅ find_package/install/export, vcpkg + Conan packaging, benchmark harness, Doxygen target, CI matrix — v1.0 |
Phase 14 (Plotting bridge) was dropped from scope. Plot via the code-gen pipeline or
evalf/vpaintegralnumeric output piped to the consumer's renderer of choice.
See docs/04-roadmap.md for the per-phase shipped/deferred breakdown and the path to SymPy parity.
#include <sympp/sympp.hpp>
#include <iostream>
int main() {
using namespace sympp;
auto x = symbol("x");
auto y = symbol("y");
// Calculus
auto f = pow(x, integer(3)) + integer(2) * x * y;
std::cout << "df/dx = " << diff(f, x)->str() << "\n";
std::cout << "∫f dx = " << integrate(f, x)->str() << "\n";
// Solve
auto roots = solve(pow(x, integer(2)) - integer(5) * x + integer(6), x);
for (auto& r : roots) std::cout << "root: " << r->str() << "\n";
// Linear algebra
Matrix A = {{integer(1), integer(2)}, {integer(3), integer(4)}};
std::cout << "det(A) = " << A.det()->str() << "\n";
// Code gen
std::cout << printing::ccode(diff(sin(x) * exp(x), x)) << "\n";
}Output:
df/dx = 2*y + 3*x**2
∫f dx = 1/4*x**4 + x**2*y
root: 3
root: 2
det(A) = -2
exp(x)*sin(x) + exp(x)*cos(x)
More worked examples: docs/08-tutorial.md.
Every call below returns an Expr; the comment shows its ->str(). All
outputs are produced by the current build and cross-checked against SymPy.
Calculus, algebra, solving
using namespace sympp;
auto x = symbol("x");
diff(pow(x, integer(3)), x); // → 3*x**2
integrate(log(x), x); // → -x + x*log(x)
integrate(pow(integer(1) + x * x, integer(-1)), x); // → atan(x)
limit(pow(integer(1) + integer(1) / x, x),
x, S::Infinity()); // → E
factor(pow(x, integer(4)) - integer(1), x); // → (x + 1)*(x - 1)*(x**2 + 1)
solve(pow(x, integer(2)) - integer(2), x); // → [2**(1/2), -2**(1/2)]Sums & simplification — including closed forms recently brought to parity:
auto n = symbol("n");
auto oo = S::Infinity();
summation(pow(n, integer(-2)), n, integer(1), oo); // → 1/6*pi**2 (Basel)
summation(pow(n * n + integer(1), integer(-1)),
n, integer(1), oo); // → 1/2*(pi*coth(pi) - 1)
product(n, n, integer(1), symbol("m")); // → factorial(m)
product(pow(integer(2), n), n, integer(1),
symbol("m")); // → 2**(m*(m+1)/2)
simplify(pow(sin(x), integer(2))
+ pow(cos(x), integer(2))); // → 1
simplify(sin(integer(2) * x)
* pow(sin(x), integer(-1))); // → 2*cos(x)Transforms & ODEs
auto s = symbol("s"), t = symbol("t");
laplace_transform(sin(t), t, s); // → (s**2 + 1)**(-1)
inverse_laplace_transform(
pow(pow(s + integer(1), integer(2)) + integer(1),
integer(-1)), s, t); // → exp(-t)*sin(t)
inverse_laplace_transform(
pow(pow(s, integer(2)) + integer(1), integer(-2)),
s, t); // → 1/2*(-t*cos(t) + sin(t))
FunctionSymbol y("y");
auto ode = diff(y(x), x, 2) + y(x); // y'' + y = 0
dsolve(ode, y(x), x); // → __C0*cos(x) + __C1*sin(x)Assumptions & linear algebra
auto p = symbol("p", AssumptionMask{}.set_positive(true));
is_positive(pow(p, integer(2)) + integer(1)); // → std::optional<bool>{true}
Matrix M = {{integer(2), integer(0)}, {integer(0), integer(3)}};
M.eigenvals(); // → [3, 2]Symbols carry an assumptions ontology — the same idea as SymPy's
Symbol('x', positive=True). You attach predicates at construction, the system
closes them under their logical consequences, propagates them through
Add/Mul/Pow, and feeds the result back into simplify, integrate, and
refine. Queries are three-valued (std::optional<bool>: true / false /
unknown (std::nullopt)) — an honest "I can't prove it" is never confused with
"false".
using namespace sympp;
// Declare predicates with the AssumptionMask builder.
auto p = symbol("p", AssumptionMask{}.set_positive(true)); // p > 0
auto n = symbol("n", AssumptionMask{}.set_integer(true)); // n ∈ ℤ
auto x = symbol("x"); // generic (complex)
// Closure: positive ⇒ real, finite, nonzero; integer ⇒ rational ⇒ real.
is_real(p); // → true
is_nonzero(p); // → true
// Propagation through Add / Mul / Pow (incl. sign-by-parity).
is_positive(pow(p, integer(2)) + integer(1)); // → true (p² ≥ 0, +1 > 0)
is_negative(mul(S::NegativeOne(), p)); // → true (−p < 0)
is_integer(integer(2) * n + integer(1)); // → true (2n+1 ∈ ℤ)
is_positive(mul(p, n)); // → unknown (n's sign is open)
// Abs is always real and nonnegative — even for a generic complex x.
is_nonnegative(abs(x)); // → true (SymPy returns None here)
// The full ontology: number theory, the complex tower, extended sign and more.
is_prime(integer(7)); // → true
is_irrational(sqrt(integer(2))); // → true
is_imaginary(sqrt(mul(S::NegativeOne(), p))); // → true (√(−p) = i·√p)
is_extended_positive(S::Infinity()); // → true (+∞ on the extended real line)
is_hermitian(symbol("r", AssumptionMask{}.set_real(true))); // → true
is_commutative(x); // → true (false only for non-commutative symbols)
// simplify / integrate consume the assumptions.
simplify(sqrt(pow(p, integer(2)))); // → p (nonneg base)
simplify(sqrt(pow(x, integer(2)))); // → sqrt(x**2) (generic — Abs, not x)Scoped assuming context. Beyond construction-time facts, a thread-local
assuming(...) RAII scope asserts a relational fact about an existing symbol for
a region of code — SymPy's with assuming(Q.positive(x)): …. The central ask
consults the active scopes, so the fact propagates through Add/Mul/Pow and
unlocks refine; it retracts at scope exit (and adds no overhead when no scope is
active).
auto x = symbol("x"); // no built-in assumptions
{
assuming pos(x, AssumptionMask{}.set_positive(true));
is_positive(mul(x, x)); // → true (propagates through Mul)
refine(sqrt(pow(x, integer(2)))); // → x (√(x²) = |x| = x for x > 0)
refine(abs(x)); // → x
} // fact retracted hereDeclarable predicates (via AssumptionMask::set_*): real, positive,
negative, zero, nonzero, nonnegative, nonpositive, finite, integer,
rational, even, odd, complex, imaginary, prime, composite,
irrational, algebraic, transcendental, extended_real, infinite,
extended_positive, extended_negative, extended_nonnegative,
extended_nonpositive, hermitian, antihermitian, commutative — the full
SymPy-style ontology. They are closed under the obvious implications (e.g.
zero ⇒ integer ∧ nonnegative ∧ nonpositive, real ⇒ finite,
nonnegative ⇒ real ∧ ¬negative, prime ⇒ integer ∧ positive,
positive ⇒ extended_positive, real ⇒ hermitian) before propagation. The
extended-signed predicates carry the sign of ±∞ on the extended real line
(extended_positive ∧ finite ⇒ positive); commutative defaults to true for
every number and symbol (as in SymPy) and is false only for an expression built
from a symbol explicitly declared commutative=false.
Boolean / SAT-style ask. Beyond single predicates, ask accepts arbitrary
boolean combinations of predicate literals — Q(x, Positive) || Q(x, Negative),
Q(k, Even) && !Q(k, Odd), etc. (see <sympp/core/satask.hpp>). It evaluates
them three-valued and, for a single expression, refutes by closure: a
proposition is true when asserting its negation yields an inconsistent
assumption mask. So a real, nonzero x answers Q(x, Positive) || Q(x, Negative)
as true and a known integer answers Q(k, Even) || Q(k, Odd) as true, exactly
matching SymPy's satask.
auto x = symbol("x", AssumptionMask{}.set_real(true).set_nonzero(true));
ask(Q(x, Positive) || Q(x, Negative)); // → true (neither disjunct alone is)
ask(Q(x, Positive) && Q(x, Negative)); // → false (contradiction)Parity with SymPy. On a battery of sign/number/parity/domain queries the
subsystem matches SymPy's ask on every predicate; the only divergences are cases
where SymPP is strictly more decisive — e.g. Abs(x) is reported
real/nonnegative for a generic x, ask(Q(k, Even) || Q(k, Odd)) is true
for a known integer k, and tan(x) for real x is taken as real (assuming
the argument avoids the poles) — all cases where SymPy returns None. The first
two are exact; the trig one is a deliberate not-at-a-pole convention. Two intentional
differences remain, both deliberate (and exercised by the engine): SymPP's
nonzero predicate means "≠ 0" rather than SymPy's "real ∧ ≠ 0" (so I and
±∞ are nonzero), and SymPP reports ±∞ as strictly positive/negative
(required by the limit engine) where SymPy ties strict sign to finiteness
(oo.is_positive is False, only oo.is_extended_positive is True).
Non-commutative algebra is also supported: mul preserves the order of
non-commuting symbols (A·B ≠ B·A, with commutative factors folded into a
leading coefficient and adjacent equal bases merged, A·A → A²), alongside the
commutative predicate.
This parity is guarded by a test: tests/core/parity_probe.cpp checks
SymPP's full predicate table over a battery of expressions against a committed
SymPy golden (tools/parity_expected.tsv, regenerated by tools/parity_sympy.py)
and fails on any new divergence outside the whitelisted intentional set.
- Symbolic algebra — Add/Mul/Pow with full canonical form, hash-cons cache, structural equality.
- Number tower —
Integer/Rational(GMP) /Float(MPFR arbitrary-precision) /ImaginaryUnit/Pi/E/oo/-oo/zoo/nan(full infinity arithmetic). - Assumptions — the full SymPy predicate ontology (28 predicates:
real, the sign tier incl.nonzero/nonnegative/nonpositive,finite/infinite,integer/rational, parity,complex/imaginary,prime/composite,irrational/algebraic/transcendental,extended_real+ the four extended-signed predicates,hermitian/antihermitian,commutative) with logical closure and Add/Mul/Pow propagation; three-valuedis_positive/is_real/ … queries consumed bysimplify/integrate/refine; a scopedassumingcontext; and a boolean/SAT-styleaskover arbitrary predicate combinations. At SymPy parity on the predicates, guarded by an oracle parity test — see Assumptions. - 30+ named functions — sin/cos/tan/exp/log/sqrt/abs/floor/
factorial/gamma/erf/heaviside/dirac_delta plus
lowergamma/uppergamma(incomplete gamma, with positive-integer and half-integer erf/erfc closed forms),HyperandMeijerG(proper Function classes with auto-eval) and the rest of the elementary + special + combinatorial canon. - Calculus —
diff(closed-form parameter derivatives for the gamma family —∂ₐΒ(a,b)=Β(a,b)(ψ(a)−ψ(a+b)),H′(x)=ψ⁽¹⁾(x+1)— with an unevaluatedDerivativenode for the genuinely non-elementary directions (∂ₛγ(s,x),∂ₙψ⁽ⁿ⁾) and for undefined/untabulated functions — never a silent0),integrate(table + trig + reciprocal-trig/hyperbolic + parts + arctan/asin/asinh + rational incl. improper/linear-denominator + heurisch + Weierstrass half-angle + the exponential Risch differential equation for rational·exp (∫ x·eˣ/(x+1)² = eˣ/(x+1)) + incomplete-gamma forms∫xˢ⁻¹e⁻ˣ = γ(s,x),∫₀^∞ xˢ⁻¹e⁻ᶜˣ = Γ(s)/cˢ; definite integrals evaluate boundaries as limits, so∫₀^∞ xⁿe^(-x) = n!),series,limitwith a Gruntz-style leading-term engine (L'Hôpital + composable asymptotic stages: power-as-exp, dominant-term∞−∞, power continuity, log-exp reduction for nested transcendentals, numerically-verified small-angle/Maclaurin-head for0·∞analytic forms, harmonic-number and log-Stirling (log(n!)/(n·log n)→1) asymptotics, a gamma-ratio asymptotic with Legendre/Gauss multiplication, exponential rates, unbalanced gamma powers and combined-denominator flattening, the full Stirling prefactor for cancelling products (n!/(nⁿe⁻ⁿ)→∞), constant-base-exponential rationals ((2ˣ+3ˣ)/3ˣ→1), hyperbolic→exp rewriting ((sinh x+cosh x)/eˣ→1), inverse-hyperbolic two-term asymptotics (asinh x/log x→1), an MRV rewrite for exponential differences — including a product carrying one,M·(eᵖ−eᵍ)withp−q→0(Gruntz's flagshipeˣ·(e^{1/x−e⁻ˣ}−e^{1/x})→−1) — special-function asymptotic series forerfc/erf(Gaussian tail),Ei, Riemannζ, digamma / higher polygamma, log-Stirlingloggamma, the Tricomi–Erdélyi gamma-ratio subleading term, and the arctangent (x·(atan x−π/2)→−1, with subleading), dominant-summand extraction from alogof an exponential- or logarithmic-rate sum (log(x+eˣ)/log(x+e²ˣ)→1/2,log(log x+log log x)−log log x→0), the log-exp reduction for tower products (xˣ/(x+1)^{x+1}→0), and a leading-term-by-series stage for1^∞corrections at±∞and finite points (x·((1+1/x)ˣ−e)→−e/2,(xˣ−1)/(x·log x)→1), generalized to a series core times a non-polynomial multiplier (eˣ·((1+1/x)ˣ−e)→−∞) — resolvesΓ(2n)/nⁿ,eˣ·(sin(1/x+e⁻ˣ)−sin(1/x))→1; size-bounded so it never hangs),summation(p-series → ζ, exponential/geometric series, telescoping, geometric/arithmetic-geometric, irreducible-quadraticΣ1/(n²+a²) → (π√a²·coth(π√a²)−1)/(2a²)),product(∏ closed forms:∏k = n!,∏2k = 2ⁿ·n!,∏2^k = 2^{n(n+1)/2}, polynomial-in-k via Gamma ratios∏(k²−1), telescoping∏(k+1)/(k+2) → 2/(n+2)), Padé, Euler-Lagrange, asymptotes. - Polynomials — div/gcd/sqf, factor over ℤ (univariate + homogeneous
bivariate:
x²−y² → (x−y)(x+y), cubes, perfect-square trinomials; plus higher-degree bivariate by monomial-root deflation:x³−y³ → (x−y)(x²+xy+y²),x³+y·x²−x−y → (x−1)(x+1)(x+y)), polynomial-time Berlekamp–Zassenhaus univariate factorization (factor_zassenhaus, with multifactor Hensel lifting), Cardano cubic, Ferrari quartic,RootOf, partial fractions, Gröbner basis, and DomainMatrix (fraction-free ℤ/ℚ matrices). - Simplification —
simplifyorchestrator (anti-bloat-guarded so it never returns a form larger than its input) chaining trigsimp (Pythagorean + double-angle / power-reduction(1∓cos2x)/2 → sin²/cos²/2sin·cos → sin2xcollapses + double-angle ratio reductionsin(2x)/sin(x) → 2cos(x)),expand_trig(circular and hyperbolic angle-addition + multiple-anglesin(nx)/sinh(nx)),fu, powsimp, exp folds ((eˣ)ⁿ → e^(nx),eˣ+e⁻ˣ → 2cosh x), hyperbolic double-angle (2sinh·cosh → sinh2x,cosh²+sinh² → cosh2x,sinh2x/sinh x → 2cosh x), imaginary-argument folds (cos(I·x) → cosh x,|exp(I·x)| → 1),Abs/signidentities (sign(x)·|x| → x,|x|·|y| → |x·y|),log(√x) → ½log x, radsimp, sqrtdenest, combsimp, gammasimp, cse, nsimplify,togetherover the LCM of denominators (incl. nested compound fractions1/(1+1/x) → x/(x+1)),hyperexpand(₀F₀→exp, ₁F₀, ₁F₁/₂F₁ closed forms incl. radical-argument cosh/sinh/erf/atanh/asin forms and squared-argument elementary forms (₀F₁(;3/2;−z²/4)→sin z/z, ₀F₁(;1/2;−z²/4)→cos z, ₂F₁(1/2,1/2;3/2;z²)→asin z/z), parameter cancellation, and Meijer-G via Slater's theorem — generic case). - Meijer-G engine (
sympp::integrals/meijerint) — generic-case Slater reduction, Mellin transform (Gamma-ratio master formula),∫₀^∞via the transform at s=1, function→Meijer-G recognition with the η·xᶜ substitution, the confluent G^{2,0}_{0,2} → modified Bessel K closed form, and the two-G∫₀^∞ G₁·G₂Mellin–Parseval convolution — wired intointegrate(·,0,∞): Gaussian∫₀^∞ e⁻ˣ² = √π/2, Dirichlet∫₀^∞ sin x/x = π/2, Fresnel∫₀^∞ cos x² = √(2π)/4,∫₀^∞ xᵃe⁻ˣ = Γ(a+1). - Linear algebra — det, inverse, eigendecomposition,
singular_values+ fullsvd, LU/QR/Cholesky, rref / rank / nullspace, jacobian/hessian/wronskian,Matrix::jordan_form()(general chains),Matrix::exp(t)matrix exponential via Jordan,MatrixSymbolexpression tree. - Geometry —
Point2D/Line2Dover exact coordinates: distance, midpoint, collinearity, triangle/polygon area, slope, intersection, parallel/perpendicular, point-to-line distance, perimeter. - Statistics (
sympp::stats) — Normal/Uniform/Exponential and Bernoulli/Binomial/Poisson distributions withmean/variance/pdf/cdf. - Vector calculus & differential geometry (
sympp::vector) — grad/div/curl/ laplacian over a coordinate list; Christoffel symbols, Ricci tensor and Ricci scalar from a metric. - Tensor algebra (
sympp::tensor) — dense N-dim tensors: tensor product, index contraction, metric raising/lowering. - Cryptography (
sympp::crypto) — RSA, Diffie–Hellman, ElGamal, and elliptic-curve primitives over 𝔽ₚ (point group law, scalar multiplication, ECDH, ECDSA sign/verify), plus modular exponentiation/inverse. - Combinatorics & group theory (
sympp::combinatorics) — permutations (composition, inverse, sign, order, cyclic form), permutation groups with a Schreier–Sims BSGS (fast order & membership for large groups — S₁₂ in milliseconds — plus abelian test), the standard symmetric / cyclic / dihedral / alternating groups, group orbits and Pólya/Burnside coloring counts (colorings_count), the Pólya cycle-index polynomial (cycle_index) and the necklace inventory (necklaces), Sylow p-subgroups, conjugacy classes / center / derived series (withis_solvable/is_nilpotent), simplicity & abelianization (is_simple,normal_closure,abelian_invariants), and integer partitions (partition_count). - Bessel functions —
besselj/bessely/besseli/besselk(special values, ±1/2 elementary closed forms, derivative recurrences). - Special integral functions —
Ei,Si,Ci,Shi,Chi, the Fresnel integralsfresnels/fresnelc, and the generalized exponential integralexpint(n, z). - Physics (
sympp::physics) — quantum (commutators, Pauli matrices, ladder/number operators, arbitrary-spin angular-momentum operators Jx/Jy/Jz/J±/J², Wigner 3-j / 6-j / 9-j, Racah W, Gaunt and Clebsch–Gordan coupling coefficients, Dirac γ-matrices), atomic (hydrogen energies & radial wavefunctions, 1D harmonic-oscillator energies & wavefunctions), geometric optics (ABCD ray matrices + Gaussian-beamqpropagation), quantum computing (qubit gates H/S/T/CNOT, Bell states, bra-ket), second quantization (Jordan-Wigner fermionic operators, single-mode bosonic Fock states with annihilation/creation/number operators, normal ordering of bosonic/fermionic operator words), and classical mechanics (conjugate momentum, Hamiltonian, multi-coordinate Lagrangian EOM). - LaTeX parser —
parse_latexturns LaTeX math back into anExpr(inverse ofprinting::latex). - Discrete transforms (
sympp::discrete) —fft/ifft(exact DFT),ntt/intt, linearconvolution, and the subset (Möbius) transform. - Equation solvers —
solve(distinct roots; expands factored polynomials; radical equations√x = 2),solvesetwith_invertchain (peels log/exp/sin/cos/tan/sinh/cosh/tanh/abs and integer powers from the LHS; emits ImageSet over ℤ for periodic trig, including scaled argumentscos(2x)=1),nsolve/vpasolve(Newton in MPFR), inequality solver (exact endpoints, real±∞),rsolve,nonlinsolvevia resultants and via Gröbner. Set algebra computes interval∪/∩/ complement ([1,3]∩[2,4] → [2,3],ℝ\[1,3] → (−∞,1)∪(3,∞)). - ODE / PDE —
dsolvefor separable, linear, Bernoulli, exact, Riccati, homogeneous, autonomous, constant-coefficient, Cauchy-Euler, hypergeometric. Variation of parameters for nonhomogeneous 2nd-order linear (constant-coef and Cauchy-Euler). Linear systems via Jordan-form matrix exponential (handles defective A). PDE for first-order linear, heat, wave. IVP application +checkodesol. - Transforms — Laplace (table + s-shift theorem:
sinh/cosh, damped oscillationse^(at)sin/cos,tⁿe^(at), and the multiplication-by-tⁿruleL{t·cos t} = (s²−1)/(s²+1)²) and inverse Laplace (incl. completed-square quadratics1/((s+1)²+1) → e^(-t)sin t, linear numerators, and repeated irreducible quadratics1/(s²+1)² → (sin t − t·cos t)/2), Fourier, Mellin, Z, sine/cosine, and the Hankel transform (order-ν, Bessel-enabled) — forward and inverse, table-driven with linearity. - Units — SI / CGS / US customary, prefixes, 12 physical constants with exact post-2019-redef values, affine temperature conversion.
- Code generation — C / C++ / Fortran / LaTeX / Octave / Rust / Julia /
MathML / GLSL code printers, a Graphviz-
dotexpression-tree printer and a SymPy-compatiblesreprconstructor printer, function emission (c_function/…), a structured codegen AST (Assignment/CodeBlock/FunctionDefinitionwith CSE-basedemit_c/emit_cxx), a 2D ASCIIpretty()printer (stacked fractions, superscript powers, radicals),lambdify— compile an expression to a nativestd::function<double(...)>(portable closure backend, plus an optional LLVM ORC-JIT backendlambdify_jit) — andautowrap, which compiles generated C through the system toolchain into adlopen'd native callable (SymPy's autowrap backend). - Logic & boolean algebra —
bool_and/bool_or/bool_not(+ xor/implies/ equivalent) connectives withsatisfiable,to_cnf/to_dnf, andsimplify_logic(Quine–McCluskey minimization). - Number theory —
factorint(Pollard rho),divisors,igcdex,jacobi_symbol/legendre_symbol,continued_fraction,n_order,primitive_root,sqrt_mod, quadratic residues (quadratic_residues,is_quadratic_residue), Carmichael λ (reduced_totient),nextprime/prevprime/primorial/multiplicity, the Chinese Remainder Theorem (crt, non-coprime moduli), discrete logarithm (discrete_log, baby-step/giant-step), linear Diophantine (diop_linear), Pell (diop_pell), and sums of two/three/four squares, plus classical orthogonal polynomials (Legendre, Chebyshev, Hermite, Laguerre),bell/tribonaccinumbers, andrewrite(target)(trig/hyperbolic ↔ exp). - MATLAB facade —
sympp::matlab::*namespace withsyms,sym(string)/str2sym(parser-routed),assume/assumeAlso/refresh,dsolve/linsolve/nsolve/vpasolve/pdsolveoverloads under MATLAB-friendly names. - SymPy oracle — every numeric and structural assertion
cross-checked against SymPy (1.13+) via a long-lived Python
subprocess; 764 oracle-validated test cases. The
hyperexpandandsreprops cross-validate SymPP's hypergeometric rewrites and constructor printing against SymPy's reference implementation.
- docs/README.md — documentation index
- docs/01-vision-and-scope.md
- docs/02-architecture.md
- docs/03-feature-mapping.md — MATLAB → SymPy reference table
- docs/04-roadmap.md — phase-by-phase status + forward plan
- docs/05-validation-strategy.md
- docs/06-build-and-tooling.md
- docs/07-coding-standards.md
- docs/08-tutorial.md — worked examples
- docs/09-known-issues.md — SymPy-parity bug log
- docs/10-parity-roadmap.md — forward plan for the remaining SymPy-parity work
- openspec/ — spec-driven change specs (see
openspec/changes/andopenspec/specs/;openspec list)
- CMake ≥ 3.25
- C++20 compiler (gcc 11+, clang 14+, AppleClang 14+)
- GMP + GMPXX (
brew install gmp/apt install libgmp-dev libgmpxx4ldbl) - MPFR (
brew install mpfr/apt install libmpfr-dev) - Python 3.10+ with SymPy 1.13+ for the test oracle (
pip install sympy)
CMake fetches Catch2 v3.5.4 and nlohmann/json v3.11.3 automatically.
git clone https://github.com/leonardoaraujosantos/SymPP.git
cd SymPP
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j
./build/tests/sympp_testsThe test runner spawns a Python subprocess on demand for the oracle —
SymPy must be importable from your python3. Tests with the [oracle]
Catch2 tag exercise that path; everything else runs without Python.
A justfile wraps the common workflows (just ≥ 1.0):
just # list all recipes
just build # configure + compile (library, tests, examples)
just test # full suite (unit + integration/oracle)
just test-unit # pure-C++ unit tests (no SymPy oracle)
just test-integration # SymPy-oracle-validated integration tests
just test-filter "[polys]" # any Catch2 tag/name filter
just examples # build & run every example
just example calculus_03_integration # run one example
just parity # Tier 1 + Tier 2 acceptance probe (expects REMAINING=0)
just bench # build & run the benchmark harness
just docs # Doxygen API docs (requires `doxygen`)
just ci # configure + build + full suite (the CI pipeline)
just clean # remove the build directoryRecipes accept inline overrides, e.g. just build_type=Debug test.
The [oracle] tag splits the suite: test-unit runs the ~[oracle]
(pure-C++) tests, test-integration runs the [oracle] (SymPy-validated)
tests.
cmake --install build --prefix /your/prefixfind_package(SymPP REQUIRED)
target_link_libraries(your_target PRIVATE SymPP::sympp)SymPPConfig.cmake and the per-target export are installed by the
build's install rules. A worked consumer build (with demo.cpp +
CMakeLists.txt) lives in the CI workflow's find_package consumer build step. Package manifests are provided for both
vcpkg.json (manifest-mode vcpkg) and
conanfile.py (Conan 2).
cmake -S . -B build -DSYMPP_BUILD_DOCS=ON
cmake --build build --target sympp_docs # requires Doxygen; output in build/docs/htmlA dependency-free wall-clock harness times representative operations (build/diff/expand/factor/simplify/integrate/limit/solve/det):
cmake -S . -B build -DSYMPP_BUILD_BENCHMARKS=ON -DCMAKE_BUILD_TYPE=Release
cmake --build build --target sympp_bench -j
./build/benchmarks/sympp_benchBSD 3-Clause. See LICENSE. Matches SymPy upstream.
Latest: v1.1.0
— source tarball (sympp-1.1.0.tar.gz + .sha256) attached, or clone main.
See CHANGELOG.md for the full history.