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

API Patterns

Every public operation in symplex follows one of a small number of patterns. Once you know which pattern a method uses, you know its return type and what a “failure” looks like.

Pattern 1 — Always returns Ex

Operations for which “unchanged” or “unevaluated” is a legitimate answer never fail:

expr.simplify()          expr.expand()           expr.eval()
expr.factor(&x)          expr.subs(&x, &v)       expr.rewrite(&rules)
expr.diff(&x)            // Derivative(f, x) if it cannot differentiate
expr.integrate(&x)       // Integral(f, x) if no closed form
expr.integrate_definite(&x, &a, &b)   // Integral node if undecided
expr.limit(&x, &a)       // Limit(f, x, a)
expr.summation(&k, &a, &b)            // Sum node
expr.laplace(&t, &s)     // LaplaceTransform node
expr.solve_ode(&y, &x)   // DSolve node

Check with has_unevaluated(). Note that RootOf and RootSum are exact algebraic answers and are not counted.

Pattern 2 — try_ twin returns Result<Ex>

Every Pattern-1 method that can produce an unevaluated form has a try_ twin that returns Err instead. The twin calls the base method and checks has_unevaluated(), so there is no behavioural drift between the two.

let anti = expr.try_integrate(&x)?;                       // Err(ComputationFailed) if unevaluated
let val  = expr.try_integrate_definite(&x, &a, &b)?;      // Err(Divergent) if proven divergent
let lim  = expr.try_limit_right(&x, &a)?;
let sum  = expr.try_summation(&k, &lo, &hi)?;

Available twins: try_diff, try_integrate, try_integrate_definite, try_limit, try_limit_left, try_limit_right, try_limit_dir, try_series, try_maclaurin, try_series_at_infinity, try_summation, try_product_over, try_laplace, try_inverse_laplace, try_residue, try_gosper_sum, try_solve_ode, try_solve_gt/ge/lt/le.

Pattern 3 — Numeric boundary → Result

Crossing from symbols to numbers can fail (free symbols, unsupported node, precision exhausted, non-convergence):

expr.eval_f64()                        // Result<f64>
expr.eval_complex64()                  // Result<Complex64>  (num_complex; in the prelude)
expr.eval_decimal(50)                  // Result<String>
expr.compile(&["x"])                   // Result<CompiledFn>
Ex::compile_many(&[&a, &b], &["x"])    // Result<CompiledFnVec>
expr.to_rust_fn("f", &["x"])           // Result<String>
expr.to_c_fn("f", &["x"])              // Result<String>
expr.integrate_numeric(&x, &a, &b)     // Result<f64>
expr.nroots(&x, 12)                    // Result<Vec<Complex64>>
expr.textplot(&x, a, b)                // Result<String>  (all plotting methods)

Pattern 4 — Queries → Option

Three-valued questions return Option<bool> (yes / no / cannot decide) and structural queries return Option<T>:

expr.is_positive()  expr.is_real()  expr.is_integer()  expr.equals(&other)
expr.is_convergent(&k)  expr.is_absolutely_convergent(&k)  expr.is_real_valued()
set.contains(&e)  set.is_subset(&t)  set.is_disjoint(&t)  set.is_empty()  set.is_open()
matrix.is_symmetric()  matrix.is_orthogonal()  matrix.is_positive_definite()  matrix.is_diagonalizable()
bool_ex.is_tautology()  bool_ex.satisfiable()
vector::is_conservative(&f, &vars)

expr.degree(&x)  expr.coeff(&x, 2)  expr.resultant(&g, &x)  expr.discriminant(&x)
expr.hypergeometric_ratio(&k)  expr.as_i64()  expr.as_rational()  set.inf()  set.measure()

None is a real answer — do not unwrap() it. A symbolic entry usually means the question cannot be decided without assumptions.

Pattern 5 — Structural preconditions → Result

Operations whose input must have a particular shape:

matrix.det()            matrix.inv()          matrix.matmul(&other)
matrix.cholesky()       matrix.lu()           matrix.minor(i, j)
matrix.eigenvals()      matrix.jordan_form()  matrix.qr()
Matrix::new(rows)       Matrix::from_i64(&ctx, rows)
Rule::try_new(...)      bool_ex.truth_table(&atoms)

Pattern 6 — Mathematical outcomes as Err or enum variants

Solvers distinguish “no method” from “the answer is: none” or “the answer is: all”:

CallOutcomeRepresentation
solveidentityErr(SymplexError::InfiniteSolutions { .. })
solvecontradiction / range violationErr(SymplexError::NoSolution { .. })
solve_system_expositive-dimensionalErr(InfiniteSolutions)
linsolvecontradictory systemOk(LinearSolution::Inconsistent)
linsolveunder-determinedOk(LinearSolution::Parametric { .. })
try_integrate_definitedivergentErr(SymplexError::Divergent { .. })
laplace_final_valueunstable poleErr(Divergent)
fourier_transform, mellin_transform, z_transformnot in table / missing sign assumptionErr(ComputationFailed) (no unevaluated node exists for these)

Ownership and references

Ex is Clone (cheap: an Arc bump and a u32) but not Copy. Operators are implemented on references and values (&x + &y, &x * 2, x.clone() / 3, 2 * &x, x += 1), and scalars of type i32, i64, u32, u64, i128, f64, BigInt, Ratio<BigInt> are accepted through the Scalar/ToEx traits. Methods take &Ex arguments. Collections use Context::sum(iter) / Context::product(iter) or Option<Ex>iter.sum::<Ex>() panics on an empty iterator because there is no context to build 0 in.

Contexts

Everything belongs to a Context. Mixing expressions from different contexts panics with a clear message (the only panic in the symbolic layer, treated as a logic error like indexing out of bounds). Context is Clone; clones share the arena. Context::compact(&roots) garbage-collects into a fresh context.

Naming conventions

Suffix / prefixMeaningExample
try_Result twin of a Pattern-1 methodtry_integrate
_withsame operation with an options structsimplify_with(&SimplifyOpts), rewrite_with(&rules, &RewriteOpts), integrate_numeric_with(…, &QuadOpts)
_tracedalso returns Vec<Step>simplify_traced, rewrite_traced
_or_emptyswallow the error into an empty Vecsolve_or_empty
_generalcomplete solution familysolve_general
_ivpwith initial conditionssolve_ode_ivp
_allall variables (multivariate)factor_all, sqrt_mod_all
is_*three-valued queryis_positive, is_symmetric
as_*cheap structural viewas_rational, as_numer_denom, as_intervals
from_*constructor on Context/typesfrom_f64, from_ratio, from_coefficients