Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Migrating from SymPy

This page is a reference for developers who know SymPy and want to find the equivalent operations in symplex. It is organized by task, with SymPy on the left and symplex on the right.

Key Differences

Before the translation table, a few structural differences to be aware of:

ConceptSymPysymplex
StateGlobal implicit state; symbols are free-standing objectsEvery expression belongs to a Context; no global state
TypesEverything is an Expr at runtimeEx (numeric), BoolEx (boolean), SetEx (set-valued) — distinct at compile time
ArithmeticPython operators on SymPy objectsRust operators on &Ex references (or use expr! macro)
Evaluationsimplify() is the catch-alleval() for exact reduction, simplify() for multi-strategy simplification to a fixpoint, simplify_traced() to see what fired
FailureReturns unevaluated or raises exceptionReturns unevaluated Ex (Pattern 1) or Result (Patterns 2–6); never raises
RealnessSymbols are complex unless real=True; re(z) stays symbolicSame in 0.2: z.re() is re(z) unless z is declared Real
FloatsFloat type exists alongside exactNo float type in expressions; floats only via eval_f64()
Printingpprint(), latex(), str()println!("{expr}"), expr.to_latex()

Setup

SymPysymplex
from sympy import *use symplex::prelude::*;
x, y, z = symbols('x y z')symplex::syms!(ctx; x, y, z);
x = Symbol('x')let x = ctx.symbol("x");
x = Symbol('x', positive=True)let x = sym!(ctx; x, Positive);
x = Symbol('x', integer=True)let x = sym!(ctx; x, Integer);

Building Expressions

SymPysymplex
x**2 + 2*x + 1&x.powi(2) + &x * 2 + 1
x**2 + 2*x + 1expr!(ctx, x^2 + 2*x + 1)
Rational(1, 3)ctx.rational(1, 3)
r.p, r.q (numerator / denominator of a Rational)e.as_ratio_i128()Option<(i128, i128)>, e.as_ratio_parts()Option<(BigInt, BigInt)>; the full Ratio<BigInt> via e.as_rational() (types re-exported as symplex::num_rational / symplex::num_bigint)
Integer(42)ctx.int(42)
pictx.pi()
Ectx.e()
Ictx.i_unit()
ooctx.infinity()
zooctx.complex_infinity()
EulerGamma, Catalan, GoldenRatioctx.euler_gamma(), ctx.catalan(), ctx.golden_ratio()
Float(0.3)no float type — ctx.from_f64_approx(0.3, 1_000_000)3/10, ctx.from_f64(0.3) → exact dyadic
Rational("22/7"), S("0.125")ctx.rational_str("22/7"), ctx.decimal_str("0.125")
sin(x)x.sin()
cos(x)x.cos()
exp(x)x.exp()
log(x)x.ln()
sqrt(x)x.sqrt()
Abs(x)x.abs()
re(z), im(z), conjugate(z), arg(z)z.re(), z.im(), z.conjugate(), z.arg()
z.as_real_imag()z.as_real_imag()
expand_complex(z)z.expand_complex()
x**yx.pow(&y)
x**5x.powi(5)
factorial(n)n.factorial()
binomial(n, k)n.binomial(&k)
gamma(x)x.gamma()
erf(x)x.erf()
Piecewise((a, cond1), (b, cond2))Ex::piecewise(&[(&a, &cond1), (&b, &cond2)])
Matrix([[1, 2], [3, 4]])matrix![ctx, [1, 2], [3, 4]]

Calculus

SymPysymplex
diff(f, x)f.diff(&x)
diff(f, x, 3)f.diff_n(&x, 3)
diff(f, x, y)f.diff(&x).diff(&y)
integrate(f, x)f.integrate(&x)
integrate(f, (x, 0, 1))f.integrate_definite(&x, &ctx.int(0), &ctx.int(1)) (unevaluated node if undecided) or f.try_integrate_definite(…)? (Err(Divergent) when proven divergent)
integrate(f, (x, 0, oo))f.integrate_definite(&x, &ctx.int(0), &ctx.infinity())
Integral(f, (x, 0, 1)).evalf()f.integrate_numeric(&x, &ctx.int(0), &ctx.int(1))? (Gauss–Kronrod)
limit(f, x, 0)f.limit(&x, &ctx.int(0))
limit(f, x, 0, '+'), limit(f, x, 0, '-')f.limit_right(&x, &ctx.int(0)), f.limit_left(&x, &ctx.int(0))
limit(f, x, oo)f.limit(&x, &ctx.infinity())
series(f, x, 0, 5)f.series(&x, &ctx.int(0), 5)
f.series(x, 0, 5).removeO()f.maclaurin(&x, 5)
series(f, x, oo, 4)f.series_at_infinity(&x, 4)
fps(f, x)f.fps_maclaurin(&x)FormalPowerSeries (coefficient(k), general_term(&k), truncate(n))
residue(f, z, a)f.residue(&z, &a)
summation(f, (k, 0, n))f.summation(&k, &ctx.int(0), &n) (or try_summation)
product(f, (k, 1, n))f.product_over(&k, &ctx.int(1), &n)
Sum(f, (k, 1, oo)).is_convergent()f.is_convergent(&k)Option<bool>
finite_diff_weights(1, [x-h, x, x+h], x)finite_diff::finite_diff_weights(1, &[…], &x)
differentiate_finite(f, x, points=[…])f.differentiate_finite(&x, &points, 1)

Simplification

SymPysymplex
simplify(expr)expr.simplify() (simplify_with(&SimplifyOpts::single_pass()) for one pass)
expand(expr)expr.expand()
factor(expr)expr.factor(&x); multivariate expr.factor_all()
factor_list(expr)expr.factor_list(&x)(content, Vec<(factor, mult)>)
sqf_list(expr)expr.sqf_list(&x)
resultant(f, g, x), discriminant(f, x)f.resultant(&g, &x), f.discriminant(&x)Option<Ex>
div(f, g, x), gcdex(f, g, x)(s, t, h)f.poly_div(&g, &x), f.poly_gcdex(&g, &x)ExtendedGcd { x: s, y: t, gcd: h }
decompose(f, x), interpolate(points, x)f.decompose(&x), Ex::poly_interpolate(&points, &x)
Poly(f).nroots(), real_roots(f), count_roots(f)f.nroots(&x, digits)Vec<Complex64>, f.real_roots_isolate(&x), f.count_real_roots(&x)
Poly(f).count_roots(inf, sup)f.count_real_roots_in(&x, &lo, &hi) / p.count_real_roots_in(&lo, &hi) on a Poly (endpoints rational or ±∞)
cancel(expr) (all variables) / ratsimp(expr)expr.ratsimp() — rational normal form, opaque non-rational subexpressions treated as indeterminates
cancel(expr, x)expr.cancel(&x)
expr.as_numer_denom(), fraction(expr)expr.as_numer_denom() — same semantics: 3/31(3, 31), x/2 + 1/3(3*x + 2, 6), sums combined at every depth, nothing cancelled
fraction(together(expr)), fraction(cancel(expr))expr.as_numer_denom() (already deep), expr.ratsimp().as_numer_denom()
apart(expr, x)expr.partial_fractions(&x)
together(expr)expr.together()
collect(expr, x)expr.collect(&x)
trigsimp(expr)expr.simplify_trig()
expand_trig(expr)expr.expand_trig()
logcombine(expr)expr.log_combine()
expand_log(expr)expr.expand_log()
powsimp(expr)expr.simplify_powers()
combsimp(expr)expr.simplify_combinatorial()
radsimp(expr)expr.rationalize_denom()
sqrtdenest(expr)expr.sqrtdenest()
signsimp(expr)expr.signsimp()
powdenest(expr, force=True)expr.powdenest(true)
expand(expr, deep=False)expr.expand_with(&ExpandOpts { deep: false, ..Default::default() })
expand_power_base(expr, force=True)expr.expand_power_base(true)
nsimplify(expr)expr.nsimplify(tol); nsimplify(expr, [pi])expr.nsimplify_with_constants(&[&ctx.pi()], tol)
rcollect(expr, x, y)expr.rcollect(&[&x, &y])
separatevars(expr)expr.separate_vars(&[&x, &y]), separate_vars_dict
expr.subs(x**2, u) (algebraic)expr.subs_algebraic(&x.powi(2), &u)
expr.replace(pattern, repl) with WildRule::new(name, &lhs_with_a_, &rhs) + expr.rewrite(&RuleSet::from_rules(vec![rule])) — see The Rule Engine

Polynomials (Poly)

See Polynomials as Data. Generators are explicit; anything else becomes a (possibly symbolic) coefficient.

SymPysymplex
Poly(e, x, y)e.as_poly(&[&x, &y])Option<Poly> (also Poly::new(&e, &[&x, &y]))
Poly(e, x, y).as_dict() / .terms()e.as_poly(&[&x, &y]).unwrap().terms()Vec<(Vec<u32>, Ex)>, lex-descending
p.monoms(), p.coeffs()p.monoms(), p.coeffs()
p.coeff_monomial(x*y)p.coeff_monomial(&[1, 1])?
p.LC(), p.LM(), p.LT()p.leading_coeff(), p.leading_monomial(), p.leading_term()
p.degree(x), p.total_degree(), p.degree_list()p.degree_in(&x), p.total_degree(), p.degree_list()
p.all_coeffs() (univariate, highest first)p.all_coeffs()Option<Vec<Ex>>
degree(e, x), Poly(e, x).coeffs(), e.coeff(x, 2), LC(e, x)e.degree(&x), e.coeffs(&x) (ascending), e.coeff(&x, 2), e.leading_coeff(&x) — symbolic coefficients allowed since 0.3
p.eval({x: 1, y: 2}), p.eval(x, 2)p.eval(&[&one, &two])?, p.eval_gen(&x, &two)?
p.as_expr()p.to_ex()
p + q, p * q, p ** 3, p.diff(x)p.add(&q)?, p.mul(&q)?, p.pow(3)?, p.derivative(&x)?
p.primitive(), p.monic()p.content_and_primitive(), p.monic()
Poly(e, x).nroots()e.as_poly(&[&x]).unwrap().nroots(digits)? (or e.nroots(&x, digits)?) → Vec<Complex64>
Poly(e, x).count_roots(a, b), real_rootsp.count_real_roots_in(&a, &b), p.count_real_roots(), p.real_roots_isolate()
Poly(e, x).shift(a)p.shift(&x, &a)?p(x + a); all coefficients ≥ 0 after shifting by a certifies p ≥ 0 on [a, ∞)
(no equivalent)p.is_nonnegative_on(&lo, &hi), p.is_positive_on(&lo, &hi)Option<bool> (exact, Sturm)
Interval(lo, hi).is_subset(solve_univariate_inequality(e >= 0, x))e.poly_is_nonnegative_on(&x, &lo, &hi) (and poly_is_positive_on) → Option<bool>, exact Sturm-based decision
groebner([f, g], x, y)groebner::groebner_basis(&[f.to_multipoly()?, g.to_multipoly()?]), back with Poly::from_multipoly
Matrix of coefficients by handPoly::monomial_basis(&polys)?, Poly::coefficient_matrix(&polys, &basis)?

Solving

SymPysymplex
solve(f, x)f.solve(&x)Result<Vec<Ex>>; identities are Err(InfiniteSolutions), contradictions Err(NoSolution)
solve(f, x) (ignoring errors)f.solve_or_empty(&x)Vec<Ex>
solveset(sin(x) - 1/2, x) (with ImageSet)f.solve_general(&x)?GeneralSolution { solutions, parameters }
solveset(f > 0, x)f.solve_gt(&x)SetEx
solveset(f >= 0, x)f.solve_ge(&x)SetEx
reduce_inequalities([x > 0, x <= 5], x)reduce_inequalities(&[x.gt(&zero), x.le(&five)], &x)?SetEx
linsolve([eq1, eq2], [x, y])linsolve(&[eq1, eq2], &[x, y])?LinearSolution::{Unique, Parametric, Inconsistent}
linsolve((A, b), x1, x2)linsolve_matrix(&a, &b)? (unknowns are named x1, x2, …; singular and inconsistent systems handled)
solve(a*x**2 + b*x + c, x) (parametric)(a x² + b x + c).solve(&x)? — results are ratsimp’d since 0.3
sympy.solvers.simplex.lpmax(f, constraints) / lpminLpProblem::maximize(c).le(row, rhs)….solve()? / LpProblem::minimize — exact over ℚ, see Exact Linear Programming
sympy.solvers.simplex.linprog(c, A, b, A_eq, b_eq, bounds)linprog::linprog(&c, &a_ub, &b_ub, &a_eq, &b_eq, &bounds)?
scipy.optimize.linprog(c, A_ub, b_ub, A_eq, b_eq)linprog::linprog(…) (exact) or linprog_matrix(Objective::Minimize, &c, Some(&a_ub), Some(&b_ub), None, None)?
“is b a non-negative combination of these vectors?”linprog::feasible_nonneg(&rows, &b)?Option<Vec<Q>>
dual values / infeasibility certificateLpSolution::duals, LpSolution::farkas
solve([eq1, eq2], [x, y]) (polynomial)symplex::polysys::solve_system_ex(&[eq1, eq2], &[x, y])? (algebraic solutions)
nsolve(f, x, x0)f.solve_numeric(&x, x0, max_iter, tol)
nsolve([f1, f2], [x, y], [x0, y0])solve_numeric_system(&[f1, f2], &[x, y], &[x0, y0])?
dsolve(ode, y(x))ode.solve_ode(&y, &x)
dsolve(ode, y(x), ics={y(0): 0, y(x).diff(x).subs(x, 0): 1})ode.solve_ode_ivp(&y, &x, &[InitialCondition { order: 0, x: x0, value: v0 }, InitialCondition { order: 1, x: x0, value: v1 }])?
rsolve(a(n+2) - a(n+1) - a(n), a(n), {a(0): 0, a(1): 1})rsolve::rsolve_linear(&[c0, c1, c2], forcing, &n, &[a0, a1])?

Linear Algebra

SymPysymplex
M = Matrix([[1,2],[3,4]])let m = matrix![ctx, [1, 2], [3, 4]];
M.det()m.det().unwrap()
M.inv()m.inv().unwrap()
M.eigenvals()m.eigenvals_with_multiplicity()? (m.eigenvals()? lists with repetition)
M.eigenvects()m.eigenvects()?Vec<(value, multiplicity, Vec<Matrix>)>
M.charpoly(x)m.char_poly(&x)?
M.Tm.transpose()
M.Hm.adjoint()
M * N&m * &n or m.matmul(&n)?
2 * M, M / 22 * &m, m.clone() / 2
M[i, j], M[i, j] = vm[(i, j)], m[(i, j)] = v
M.trace()m.trace()?
M.rank()m.rank()usize
M.nullspace()m.nullspace()Vec<Matrix> (also rowspace, columnspace, left_nullspace)
integer kernel (no direct SymPy API)m.integer_nullspace()? — a ℤ-basis, see Integer Lattices
hermite_normal_form(M) (sympy.matrices.normalforms, column style H = A·V)normalforms::column_hermite_normal_form(&m)? (leading zero columns kept); row style H = U·A is m.hermite_normal_form()? / hermite_normal_form_with_transform
smith_normal_form(M)m.smith_normal_form()?, normalforms::smith_normal_form_with_transforms(&m)?SmithNormalForm { s, u, v }
abs(M.det()) == 1normalforms::is_unimodular(&m)?
M.extract(rows, cols)m.extract(&rows, &cols)?
M[rows, :], M[:, cols]m.select_rows(&rows)?, m.select_cols(&cols)?
M.row_del(i), M.col_del(j)m.delete_row(i)?, m.delete_col(j)? (returns a new matrix)
M.is_zerom.is_zero()Option<bool>
all(e.is_integer for e in M)m.is_integer_matrix()Option<bool>
M.subs({x: y, y: x}) (simultaneous)m.subs_map(&[(&x, &y), (&y, &x)])
Matrix(rows) from Rational/int/float dataMatrix::from_ratio(&ctx, &rows)?, Matrix::from_bigint, Matrix::from_f64_rows (exact dyadic)
[[e for e in row] for row in M.tolist()] as numbersm.to_rational_rows(), m.to_bigint_rows()Option<Vec<Vec<_>>>
M.diagonalize()m.diagonalize()?Diagonalization { p, d }
M.is_diagonalizable()m.is_diagonalizable()Option<bool>
M.jordan_form()m.jordan_form()?JordanForm { p, j }
M.exp()m.matrix_exp()?; (t*M).exp()m.matrix_exp_t(&t)?
M**n (symbolic n)m.matrix_pow_symbolic(&n)?
M.sqrt() / M**Rational(1,2)m.matrix_sqrt()?
M.LUdecomposition()m.lu()?Lu { l, u, perm }
M.cholesky()m.cholesky()?
M.LDLdecomposition()m.ldl()?Ldl { l, d }
M.QRdecomposition()m.qr()?Qr { q, r }
GramSchmidt(vecs, True)matrix_decomp::gram_schmidt(&vecs, true)?
M.is_symmetric(), M.is_positive_definitem.is_symmetric(), m.is_positive_definite()Option<bool>
M.norm(), M.norm(1), M.norm(oo)m.norm_frobenius(), m.norm_1(), m.norm_inf()
M.solve_least_squares(b)m.solve_least_squares(&b)?
hessian(f, [x, y]), wronskian([f, g], x)matrix_decomp::hessian(&f, &[&x, &y]), matrix_decomp::wronskian(&[&f, &g], &x)

Evaluation and Substitution

SymPysymplex
expr.subs(x, 3)expr.subs(&x, &ctx.int(3))
expr.subs(x, 3) (integer shorthand)expr.subs_i64(&x, 3)
expr.subs([(x, 1), (y, 2)])expr.subs_map_i64(&[(&x, 1), (&y, 2)]) or expr.eval_at(&[(&x, &a), (&y, &b)])
expr.evalf()expr.eval_f64()Result<f64>
expr.evalf(50)expr.eval_decimal(50)Result<String>
expr.is_numberexpr.is_constant() / expr.as_rational().is_some()
expr.free_symbolsexpr.free_symbols()
expr.equals(other)expr.equals(&other)Option<bool>; expr.probably_equal(&other, samples)
expr1 < expr2 (numeric)expr1.compare_numeric(&expr2), expr1.is_less_than(&expr2)Option

Code Generation

SymPysymplex
sstr(expr) then hand-edit for Lean / Mathlibexpr.to_lean()? — Mathlib spacing (2 * j + 1, j ^ 2), (3 / 31 : ℝ), (j - 1) / (2 * j), Real.sin x, 0 < x ∧ x < 1 for a BoolEx; to_lean_with(&LeanOpts::default().with_real_type("ℚ")) for the carrier type / ascribing every integer
(wrap Lean output to ≤ 100 columns by hand)lean::wrap_lean(&text, lean::MATHLIB_LINE_WIDTH); the certificate emitters already do this
ask(Q.positive(3*u**2 + 2*u + 1)) with u declared positive — usually Nonee.is_positive()Some(true) for a rational-coefficient polynomial in one real-assumed symbol (exact Sturm decision); is_nonnegative, is_negative, is_nonpositive likewise
lambdify([x], expr)expr.compile(&["x"])Result<CompiledFn> (Clone + Send + Sync, arity(), try_call())
lambdify([x, y], [f1, f2])Ex::compile_many(&[&f1, &f2], &["x", "y"])Result<CompiledFnVec> (shared CSE)
rust_code(expr)expr.to_rust_fn("name", &["x"])Result<String> (to_rust_fn_with_options for f32, no_std, checked_domain, …)
ccode(expr) / codegen(("f", expr), "C99")expr.to_c_fn("f", &["x"])Result<String> (self-contained C99 with helpers)
cse([expr1, expr2])Ex::cse_many(&[&expr1, &expr2])(Vec<(Ex, Ex)>, Vec<Ex>); single: expr.cse()
latex(expr)expr.to_latex()String
expr.to_json()Result<String>

Number Theory

SymPysymplex
isprime(n)symplex::ntheory::isprime(n)
factorint(n)symplex::ntheory::factorint(n)
nextprime(n)symplex::ntheory::nextprime(n)
prevprime(n)symplex::ntheory::prevprime(n)
divisors(n)symplex::ntheory::divisors(n)
totient(n)symplex::ntheory::totient(n)
mobius(n)symplex::ntheory::mobius(n)
mod_inverse(a, m)symplex::ntheory::mod_inverse(a, m)
crt([r1,r2], [m1,m2])symplex::ntheory::crt_i64(&[r1,r2], &[m1,m2])
pow(a, e, m) (3-arg pow)symplex::ntheory::mod_pow(a, e, m)
igcd(a, b, c), ilcm(a, b, c)symplex::ntheory::igcd(&[a, b, c]), ilcm(&[a, b, c]) (any integer type); gcd_many(&[BigInt]), lcm_many
ilcm(*[r.q for r in rationals])symplex::ntheory::rational_lcm_of_denominators(&rationals)
primerange(2, 50)symplex::ntheory::primerange(2, 50) / primes_up_to(50)
primepi(n), prime(n)symplex::ntheory::primepi(n), prime(n)
legendre_symbol(a, p)symplex::ntheory::legendre_symbol(a, p)
jacobi_symbol(a, n), kronecker_symbol(a, n)symplex::ntheory::jacobi_symbol(a, n)?, kronecker_symbol(a, n)
sqrt_mod(a, p), sqrt_mod(a, p, all_roots=True)symplex::ntheory::sqrt_mod(a, p), sqrt_mod_all(a, p)
discrete_log(n, a, b)symplex::ntheory::discrete_log(b, a, n) (base, target, modulus)
primitive_root(p), n_order(a, n)symplex::ntheory::primitive_root(p), n_order(a, n)
continued_fraction(x), continued_fraction_periodic(0, 1, d)symplex::ntheory::continued_fraction(&ratio), continued_fraction_periodic(d)PeriodicContinuedFraction { pre_period, period }
egyptian_fraction(r)symplex::ntheory::egyptian_fraction(&r)
diophantine(x**2 - 61*y**2 - 1)symplex::diophantine::pell(61), pell_solutions(61, k)
diophantine(3*x + 5*y - 1)symplex::diophantine::linear_diophantine(3, 5, 1)LinearDiophantine { x, y, x_step, y_step }
sum_of_squares(n, 2)symplex::diophantine::sum_of_two_squares(n)

Combinatorics

SymPysymplex
stirling(n, k, kind=2)symplex::combinatorics::stirling2(n, k)Option<BigInt>
stirling(n, k, kind=1, signed=True)symplex::combinatorics::stirling1(n, k)Option<BigInt>
npartitions(n) / partition(n)symplex::combinatorics::partition_count(n)Option<BigInt>
multinomial_coefficients(n, k)symplex::combinatorics::multinomial(n, &ks)Option<BigInt>
bell(n)symplex::combinatorics::bell(n) or ctx.int(n).bell().eval()
catalan(n)symplex::combinatorics::catalan(n) or ctx.int(n).catalan_number().eval()
subfactorial(n)symplex::combinatorics::derangements(n) or ctx.int(n).subfactorial().eval()
fibonacci(n), lucas(n)symplex::ntheory::fibonacci(n), lucas(n) or ctx.int(n).fibonacci().eval()
bernoulli(n), euler(n), harmonic(n)symplex::ntheory::bernoulli(n), euler_number(n), harmonic(n)
partitions(n) (iterator)symplex::combinatorics::partitions(n)

Special Functions

SymPysymplex
gamma(x)x.gamma()
erf(x)x.erf()
erfc(x)x.erfc()
beta(a, b)a.beta(&b)
besselj(n, x)x.bessel_j(&n)
bessely(n, x)x.bessel_y(&n)
LambertW(x)x.lambertw()
DiracDelta(x)x.dirac_delta()
Heaviside(x)x.heaviside()
digamma(x)x.digamma()
polygamma(n, x)x.polygamma(&n)
loggamma(x)x.log_gamma()
zeta(s)s.zeta()
Si(x), Ci(x), Ei(x), li(x)x.si(), x.ci(), x.ei(), x.li()
KroneckerDelta(i, j)i.kronecker_delta(&j)
besseli(n, x), besselk(n, x)x.bessel_i(&n), x.bessel_k(&n)
legendre(n, x)x.legendre(&n)
chebyshevt(n, x)x.chebyshev_t(&n)
hermite(n, x)x.hermite(&n)
laguerre(n, x)x.laguerre(&n)

Sets and Logic

SymPysymplex
Interval(0, 5), Interval.Lopen(3, 10)ctx.interval(&zero, &five, IntervalKind::Closed), ctx.interval(&three, &ten, IntervalKind::LeftOpen)
FiniteSet(1, 2, 3)ctx.finite_set(&[one, two, three])
S.Reals, S.EmptySetctx.reals(), ctx.empty_set()
A.union(B), A.intersect(B)a.union(&b).simplify(), a.intersection(&b).simplify()
A - B, A.symmetric_difference(B), A.complement(S.Reals)a.difference(&b), a.symmetric_difference(&b), a.absolute_complement()
A.contains(x), x in Aa.contains(&x)Option<bool>, x.is_in(&a)
A.is_subset(B), A.is_disjoint(B)a.is_subset(&b), a.is_disjoint(&b)Option<bool>
A.inf, A.sup, A.measure, A.boundary, A.closure, A.interiora.inf(), a.sup(), a.measure(), a.boundary(), a.closure(), a.interior()Option
A.as_relational(x)a.to_condition(&x)?
And(p, q), Or(p, q), Not(p), Implies(p, q)p.and(&q), p.or(&q), p.not(), p.implies(&q)
to_nnf, to_cnf, to_dnfb.to_nnf(), b.to_cnf(), b.to_dnf()
satisfiable(b)b.satisfiable()Option<bool>; b.is_tautology(), b.is_contradiction()
truth_table(b, [p, q])b.truth_table(&[p, q])?
piecewise_fold / Piecewise.simplify()expr.piecewise_simplify()

Transforms

SymPysymplex
fourier_transform(f, t, w) (ordinary frequency)f.fourier_transform_with(&t, &w, FourierConvention::Ordinary)?
fourier_transform with angular frequencyf.fourier_transform(&t, &w)? (non-unitary angular)
inverse_fourier_transform(F, w, t)F.inverse_fourier_transform(&w, &t)?
mellin_transform(f, x, s)(F, (a, b), cond)f.mellin_transform(&x, &s)?(F, strip: BoolEx)
inverse_mellin_transform(F, s, x, (a, b))F.inverse_mellin_transform(&s, &x)?
fourier_series(f, (x, -pi, pi))f.fourier_series_on(&x, &(-ctx.pi()), &ctx.pi(), n)?
s.truncate(n), s.an, s.bns.truncate(n), s.coefficient_a(k), s.coefficient_b(k)
Z-transform (not in SymPy core)f.z_transform(&n, &z)?, F.inverse_z_transform(&z, &n)?

Numerical Optimisation (SciPy / NumPy)

SymPy defers to SciPy and NumPy here; symplex ships equivalents in symplex::optimize (see Numerical Optimisation). All are deterministic and return Result.

SciPy / NumPysymplex
scipy.optimize.brentq(f, a, b)optimize::brent_root(f, a, b, &RootOpts::default())?; on an Ex: e.find_root_bracket(&x, a, b)?
scipy.optimize.bisect(f, a, b)optimize::bisect(f, a, b, &opts)?
scipy.optimize.newton(f, x0, fprime)optimize::newton_root(f, df, x0, &opts)? (derivative from e.diff(&x).compile(..))
scipy.optimize.minimize(f, x0, method="Nelder-Mead")optimize::nelder_mead(f, &x0, &MinimizeOpts::default())?; on an Ex: e.minimize_numeric(&[&x, &y], &x0)?MinimizeResult { x, fun, iterations, evaluations, converged }
scipy.optimize.minimize_scalar(f, bounds=(a, b), method="bounded")optimize::minimize_scalar(f, a, b, &opts)? (Brent), golden_section; on an Ex: e.minimize_scalar_numeric(&x, a, b)?ScalarMinimum { x, value }
scipy.optimize.differential_evolution(f, bounds, seed=0)optimize::differential_evolution(f, &bounds, &DeOpts { seed, .. })? with bounds: &[Interval<f64>] (closed, Interval::closed(lo, hi)); on an Ex: e.minimize_global_numeric(&vars, &bounds, &opts)?
numpy.polyfit(x, y, deg) (highest degree first)optimize::poly_fit(&xs, &ys, deg)? (ascending: [c₀, c₁, …]); eval_poly(&c, x) evaluates
numpy.polyfit with exact rationals (no NumPy equivalent)optimize::poly_fit_exact(&points, deg)?, stats::regression::polyfit(&x, &y, deg)? (both ascending), Ex::poly_fit_points(&ctx, &points, &x, deg)?Ex
scipy.stats.linregress(x, y) (slope, intercept)optimize::linear_fit(&xs, &ys)?LinearFit { slope, intercept }
numpy.trapz(y, x) / scipy.integrate.trapezoid(y, x)optimize::trapezoid(&ys, &xs)?
scipy.optimize.fsolve(F, x0)solve_numeric_system(&eqs, &vars, &x0)? (damped Newton, symbolic Jacobian)

Dimensional Analysis

SymPy does not have a built-in compile-time unit system. symplex provides one:

#![allow(unused)]
fn main() {
use symplex::units::*;

let ctx = Context::new();
let m = Mass::symbol(&ctx, "m");
let a = Acceleration::symbol(&ctx, "a");

// Type-safe: the compiler verifies the dimension
let force = dim!(ctx, Force: m * a);

// Typed calculus: d(Length)/d(Time) → Velocity
symplex::syms!(ctx; g, t);
let t_var = Time::symbol(&ctx, "t");
let pos = Length::from_ex(expr!(ctx, 1/2 * g * t^2));
let vel: Velocity = pos.diff_wrt(&t_var);
}

There is no SymPy equivalent for this. The closest is SymPy’s physics.units module, which performs dimensional analysis at runtime rather than compile time.

ODE Solving

SymPysymplex
dsolve(Eq(f(x).diff(x), f(x)), f(x))(see below)
classify_ode(ode)ode_expr.classify_ode(&y, &x)

ODE solving in symplex uses a different interface from SymPy. Instead of wrapping the ODE in Eq() and using Function('f'), you build the ODE as an expression involving y.formal_diff(&x):

#![allow(unused)]
fn main() {
let ctx = Context::new();
symplex::syms!(ctx; x);
let y = ctx.symbol("y");
let dy = y.formal_diff(&x);

// y' + 2y = 0
let ode = &dy + &y * 2;
let solution = ode.solve_ode(&y, &x);
println!("{solution}");   // y = C1*exp(-2*x)
}

symplex supports 16 ODE classes: simple separable, full separable, first-order linear (constant and variable coefficient), exact, integrating factor, Bernoulli, Riccati (solve_riccati with a particular solution), Euler–Cauchy, homogeneous-coefficient, second-order constant-coefficient (homogeneous and non-homogeneous), nth-order constant-coefficient, reduction of order, variation of parameters, and Clairaut. Initial-value problems use solve_ode_ivp; linear systems x' = Ax use ode::solve_ode_system_ivp.

Things That Don’t Have Direct Equivalents

In SymPy but not symplex

  • Permutation, PermutationGroup, and abstract algebra
  • geometry module (Point, Line, Circle, Polygon)
  • stats module (probability distributions)
  • tensor module (indexed tensors, Einstein summation)
  • physics.quantum module
  • pdsolve (PDE solving)
  • General diophantine() (symplex has linear_diophantine, pell, sum_of_two_squares, pythagorean_triples, not the general classifier)
  • rsolve for polynomial/rational/hypergeometric coefficients (symplex has constant-coefficient linear and first-order recurrences)
  • hyper, meijerg and the Meijer-G integration engine
  • Eq as a first-class expression in every API (symplex has Equation with solve, solve_for, arithmetic, and eq!, accepted by linsolve)
  • Pretty-printing with Unicode box drawing (pprint) — symplex has pretty() / pretty_ascii()
  • O() notation for series remainders

In symplex but not SymPy

  • Compile-time dimensional analysis (dim! macro, quantity types)
  • Compile-time expression type safety (Ex / BoolEx / SetEx)
  • Thread-safe contexts (Send + Sync, no GIL)
  • Optimized Rust and C99 code generation with CSE and an embedded special-function runtime (to_rust_fn, to_c_fn)
  • Compiled closures for fast numerical evaluation (compile, compile_many)
  • Exact RootOf eigenvalues for irreducible cubic/quartic characteristic polynomials
  • Exact linear programming over ℚ with shadow prices and Farkas certificates (symplex::linprog)
  • Exact sign of a polynomial on an interval (poly_is_nonnegative_on, Sturm-based)
  • Row-style Hermite normal form with its unimodular transform, integer kernels, lattice determinants
  • Deterministic numerical optimisation (symplex::optimize) in the same crate as the CAS
  • Err(Divergent) / Err(NoSolution) / Err(InfiniteSolutions) as first-class outcomes
  • EvalConfig for user-controllable computation limits
  • build.rs code generation pipeline for embedded targets
  • WASM compilation target (symplex-wasm)