Skip to main content

symplex/
lib.rs

1//! **symplex** — a fast, correct symbolic mathematics library for Rust.
2//!
3//! Expressions are exact (`Ratio<BigInt>` arithmetic, hash-consed in a
4//! [`Context`](prelude::Context) arena) and the library can differentiate,
5//! integrate (indefinite, definite, improper, numeric), sum, take limits and
6//! series, solve equations, systems, ODEs and recurrences, simplify with a
7//! public rewrite-rule engine, work with sets and boolean logic, do exact
8//! linear algebra, view expressions as polynomials with symbolic
9//! coefficients ([`Poly`](prelude::Poly)), put rational functions into
10//! normal form (`ratsimp`), solve linear programs exactly with dual values
11//! and Farkas certificates, compute Hermite and Smith normal forms of
12//! integer matrices, run numerical root finding and minimisation, apply
13//! Laplace/Fourier/Mellin/Z transforms, and generate optimized Rust or C99
14//! code. See the [README](https://github.com/cgorski/symplex) and
15//! [The Symplex Book](https://cgorski.github.io/symplex/) for a guided tour,
16//! and `CHANGELOG.md` for every release's changes (breaking changes first,
17//! under `### Breaking`).
18//!
19//! Symplex is designed around seven principles:
20//!
21//! 1. **Construction is cheap, evaluation is explicit.** Constructors only
22//!    canonicalize (flatten, sort, combine). No expansion, no function
23//!    evaluation, no identity application. Call `.eval()`, `.expand()`, or
24//!    `.simplify()` when *you* choose.
25//!
26//! 2. **Never silently wrong.** Operations return `Result::Err`, an
27//!    unevaluated node, or `None` instead of a guess: `∫₋₁¹ dx/x²` is
28//!    `Err(Divergent)`, `solve(x − x)` is `Err(InfiniteSolutions)`, `re(z)` stays
29//!    `re(z)` until `z` is known to be real. Structural substitution by default.
30//!
31//! 3. **One representation per concept.** One assumption system. One polynomial
32//!    type. One number type. One solve function.
33//!
34//! 4. **Thread-safe from day one.** Expression handles are `Send + Sync`.
35//!
36//! 5. **No recursive tree walks.** All traversals use explicit stacks.
37//!
38//! 6. **The compiler is the API contract.** `pub` = stable. `pub(crate)` = internal.
39//!    `Ex`, `BoolEx` and `SetEx` are distinct types.
40//!
41//! 7. **Extensible without inheritance.** Custom functions via registered rules
42//!    ([`Rule`](prelude::Rule), [`RuleSet`](prelude::RuleSet)).
43//!
44//! # API model
45//!
46//! * Operations for which "unevaluated" is a valid answer return
47//!   [`Ex`](prelude::Ex) and have a `try_` twin returning `Result`
48//!   (`integrate` / `try_integrate`, `integrate_definite` /
49//!   `try_integrate_definite`, `summation` / `try_summation`, …).
50//! * Numeric boundaries (`eval_f64`, `compile`, `to_rust_fn`, `to_c_fn`,
51//!   `integrate_numeric`) and structural preconditions (`Matrix::inv`,
52//!   `cholesky`) return `Result`.
53//! * Queries (`is_positive`, `equals`, `SetEx::contains`,
54//!   `Matrix::is_symmetric`) return `Option<bool>`: yes, no, or unknown.
55//!
56//! # Quick Start
57//!
58//! ```
59//! use symplex::prelude::*;
60//! use symplex::syms;
61//!
62//! let ctx = Context::new();
63//! syms!(ctx; x, y);
64//! let expr = &x * &x + &x * 2 + 1;
65//! assert_eq!(format!("{expr}"), "x^2 + 2*x + 1");
66//!
67//! // Differentiate, integrate over an infinite range, solve, compile.
68//! assert_eq!(format!("{}", expr.diff(&x)), "2*x + 2");
69//! let gauss = (-x.powi(2)).exp().integrate_definite(&x, &ctx.neg_infinity(), &ctx.infinity());
70//! assert_eq!(format!("{gauss}"), "sqrt(pi)");
71//! let roots = (&x.powi(2) - 4).solve(&x).unwrap();
72//! assert_eq!(roots.len(), 2);
73//! let f = expr.compile(&["x"]).unwrap();
74//! assert_eq!(f(&[2.0]), 9.0);
75//! ```
76//!
77//! # Module map
78//!
79//! The [`prelude`] re-exports everything most programs need. Domain modules
80//! are re-exported at the crate root: [`ntheory`], [`diophantine`],
81//! [`combinatorics`], [`mod@matrix`], [`matrix_decomp`], [`normalforms`],
82//! [`linprog`], [`optimize`], [`vector`], [`quaternion`], [`control`],
83//! [`robotics`], [`dynamics`], [`poly_ex`], [`multipoly`], [`polysys`],
84//! [`groebner`], [`factor_zassenhaus`], [`definite`], [`summation`],
85//! [`formal_series`], [`finite_diff`], [`fourier_transform`], [`mellin`],
86//! [`z_transform`], [`ode`], [`rsolve`], [`sets`], [`logic`], [`parse`],
87//! [`tree`], [`codegen`], [`lambdify`], [`units`], [`assumptions`],
88//! [`numeric`], [`errors`], [`config`], [`stats`] (random variables and
89//! statistics on data), [`certificates`] (Handelman / half-line /
90//! polyhedron / sums-of-squares non-negativity proofs), [`polytope`],
91//! [`discrete`], [`decompositions`], [`lean`], [`mathml`], [`data_export`],
92//! [`expr_view`], and the method-surface modules [`integrate_api`],
93//! [`poly_api`], [`rules`], [`series_api`], [`sets_api`], [`solvers`],
94//! [`transforms_api`]. The shared value types [`Interval`], [`Bounds`],
95//! [`Extended`], [`Budget`] and the seeded generators [`SplitMix64`] /
96//! [`XorShift64Star`] are at the root too; [`base`] exposes the foundation
97//! layer.
98//!
99//! New in 0.3: [`poly_ex`] (the [`Poly`](prelude::Poly) view of an
100//! expression), [`linprog`] (exact simplex), [`normalforms`] (Hermite /
101//! Smith normal forms, integer kernels) and [`optimize`] (Brent,
102//! Nelder–Mead, differential evolution, least-squares fitting).
103
104#![warn(missing_docs)]
105
106// ── Self-referencing extern crate so proc-macro-generated paths
107//    (`::symplex::__macro_support::…`) resolve inside the crate itself. ──
108extern crate self as symplex;
109
110// ── Directory modules (internal organisation) ──────────────────────────
111pub(crate) mod api;
112/// Foundation layer: expression nodes, arena, tree traversal, canonicalization, and core types.
113pub mod base;
114pub(crate) mod calculus;
115pub(crate) mod domains;
116pub(crate) mod output;
117pub(crate) mod plotting;
118pub(crate) mod poly;
119pub(crate) mod simplify;
120pub(crate) mod transforms;
121/// Compile-time dimensional analysis for physical quantities.
122pub mod units;
123
124// README and book code blocks as doctests (`cargo test --doc`); see the file.
125#[cfg(doctest)]
126mod doctests;
127
128// ── Public re-exports (backwards-compatible crate-root paths) ──────────
129
130// The exact-arithmetic crates whose types appear in the public API
131// (`Ratio<BigInt>` from `as_rational`, `linprog::Q`, `poly_fit_exact`,
132// `Matrix::from_ratio`, …).  Re-exported so a downstream crate can name and
133// manipulate those values without adding — and version-matching — the crates
134// itself: `symplex::num_rational::Ratio`, `symplex::num_bigint::BigInt`,
135// `symplex::num_traits::{Zero, One, Signed}`, `symplex::num_integer::Integer`,
136// `symplex::num_complex::Complex64` (from `eval_complex64`, `nroots`).
137pub use num_bigint;
138pub use num_complex;
139pub use num_integer;
140pub use num_rational;
141pub use num_traits;
142
143// base
144/// Assumption system for symbolic variables.
145pub use base::assumptions;
146/// Limits on one call — deadline, time limit, step cap — shared by the simplex and the certificate provers.
147pub use base::budget::{Budget, BudgetHit};
148/// Library-wide configuration knobs.
149pub use base::config;
150/// Error types used throughout the library.
151pub use base::errors;
152/// A value or `±∞` — the extended line; `Interval<Extended<T>>` is an unbounded interval.
153pub use base::extended::Extended;
154/// Intervals (`[a, b]`, `(a, b)`, …) and possibly-unbounded closed bounds with named endpoints — the types behind every pair of bounds in the API.
155pub use base::interval::{Bounds, Interval, IntervalKind};
156
157// base
158/// Exact `f64` ↔ rational conversions (dyadic exact, and best bounded-denominator approximations).
159pub use base::numeric;
160/// Deterministic seeded generators: SplitMix64 (general purpose) and xorshift64* (randomised algorithms).
161pub use base::rng::{SplitMix64, XorShift64Star};
162
163// poly
164/// Univariate factorization over ℤ via Berlekamp–Zassenhaus.
165pub use poly::factor_zassenhaus;
166/// Gröbner basis computation via Buchberger's algorithm with FGLM order conversion.
167pub use poly::groebner;
168/// Sparse multivariate polynomials over ℚ.
169pub use poly::multipoly;
170/// Polynomial system solving via Gröbner bases.
171pub use poly::polysys;
172
173// calculus
174/// Definite and improper integration.
175pub use calculus::definite;
176/// Finite difference methods: weights, application, and differentiation.
177pub use calculus::finite_diff;
178/// Formal power series representations and algorithms.
179pub use calculus::formal_series;
180/// Symbolic Fourier transform.
181pub use calculus::fourier_transform;
182/// Mellin transform.
183pub use calculus::mellin;
184/// Ordinary differential equation solver.
185pub use calculus::ode;
186/// Symbolic summation and products.
187pub use calculus::summation;
188/// Z-transform for discrete-time signal analysis.
189pub use calculus::z_transform;
190
191// transforms
192/// Boolean-logic simplification, normal forms, satisfiability.
193pub use transforms::logic;
194/// Recurrence-relation solver.
195pub use transforms::rsolve;
196/// Set algebra on intervals, finite sets, unions.
197pub use transforms::sets;
198
199// output
200/// Lean 4 / Mathlib rendering (`Ex::to_lean`, `LeanOpts`).
201pub use output::lean;
202/// Presentation MathML rendering.
203pub use output::mathml;
204/// Runtime expression parser — convert strings to symbolic expressions.
205pub use output::parse;
206/// Serializable expression tree for interchange (JSON, etc.).
207pub use output::tree;
208
209// plotting
210/// Data export utilities: CSV, TSV, JSON, Markdown, HTML, LaTeX table output.
211pub use plotting::data_export;
212
213// domains
214/// Exact, machine-checkable non-negativity certificates: Handelman (boxes),
215/// half-lines, parametric polyhedra and sums of squares, with Lean export.
216pub use domains::certificates;
217/// Combinatorics: Stirling numbers, multinomial coefficients, partition counting.
218pub use domains::combinatorics;
219/// Control systems: state-space models, transfer functions, stability analysis.
220pub use domains::control;
221/// Named results of matrix decompositions (`Qr`, `Lu`, `HermiteNormalForm`, …) shared by `Matrix`, `ZMatrix` and `QMatrix`.
222pub use domains::decompositions;
223/// Diophantine equations.
224pub use domains::diophantine;
225/// Discrete transforms on exact sequences (convolution, NTT, Walsh–Hadamard, Möbius).
226pub use domains::discrete;
227/// Lagrangian dynamics: equations of motion, mass matrix, Coriolis, gravity.
228pub use domains::dynamics;
229/// Exact linear programming over ℚ (two-phase simplex, duals, Farkas certificates).
230pub use domains::linprog;
231/// Symbolic matrix type and operations.
232pub use domains::matrix;
233/// Additional matrix decompositions (QR, Gram–Schmidt) and structure tests.
234pub use domains::matrix_decomp;
235/// Integer matrix normal forms: Hermite, Smith, unimodular transforms, integer kernels.
236pub use domains::normalforms;
237/// Number theory: primality, factorization, divisors, modular arithmetic.
238pub use domains::ntheory;
239/// Numerical optimisation and root bracketing (Brent, Nelder–Mead, polynomial fitting).
240pub use domains::optimize;
241/// Exact convex polyhedra in ℚⁿ from half-spaces: vertices, volume,
242/// containment, cutting.
243pub use domains::polytope;
244/// Symbolic quaternion algebra for attitude representation.
245pub use domains::quaternion;
246/// Robotics kinematics: DH parameters, forward kinematics, rotations.
247pub use domains::robotics;
248/// Symbolic probability and statistics: random variables, exact moments, probabilities, densities.
249pub use domains::stats;
250/// Vector calculus: gradient, divergence, curl, laplacian.
251pub use domains::vector;
252
253// api
254/// Expression context — arena, symbol table, configuration.
255pub use api::context;
256/// Symbolic equation type (`lhs = rhs`).
257pub use api::eq;
258/// The core expression handle and types.
259pub use api::expr;
260/// Complex-analysis methods on `Ex` (`re`, `im`, `conjugate`, `arg`, `polar`, …).
261pub use api::expr_complex;
262/// Definite / improper / numeric integration methods on `Ex`.
263pub use api::expr_integrate_ext as integrate_api;
264/// Operator overloads and scalar-conversion traits (`ToEx`, `Scalar`).
265pub use api::expr_ops;
266/// Polynomial-algebra methods on `Ex` (resultant, discriminant, division, numeric roots, …).
267pub use api::expr_poly_ext as poly_api;
268/// Public rewrite-rule engine: `Rule`, `RuleSet`, `Bindings`, `RewriteOpts`, `Step`.
269pub use api::expr_rules_ext as rules;
270/// Summation, products, series and formal-power-series methods on `Ex`.
271pub use api::expr_series_ext as series_api;
272/// Set-algebra and boolean-logic helpers (`reduce_inequalities`).
273pub use api::expr_sets_ext as sets_api;
274/// Solver entry points beyond `Ex::solve`: `linsolve`, `LinearSolution`, `GeneralSolution`, Newton systems.
275pub use api::expr_solve_ext as solvers;
276/// Integral transforms (Fourier, Mellin, Laplace helpers) and directional limits on `Ex`.
277pub use api::expr_transforms_ext as transforms_api;
278/// A non-locking, read-only view of an expression node for use in `replace()`.
279pub use api::expr_view;
280/// Convenience macros for building expressions.
281pub use api::macros;
282/// Public sparse polynomial view (`Poly`) over explicit generators.
283pub use api::poly_ex;
284
285// output
286/// Code-generation options and compiled numeric functions.
287pub use output::codegen;
288/// Compiled numeric closures (`CompiledFn`, `CompiledFnVec`).
289pub use output::lambdify;
290
291// ── Proc macro re-exports ──────────────────────────────────────────────
292pub use symplex_macros::{dim, eq, expr, matrix, rule};
293
294// ── Macro support (hidden internals used by generated code) ────────────
295#[doc(hidden)]
296pub mod __macro_support {
297    pub use crate::base::node::ExprNode;
298    pub use crate::transforms::pattern::{Pattern, Rule, WildId};
299    pub use rustc_hash::FxHashMap;
300}
301
302// bitflags types don't auto-derive Default; provide it here so
303// Assumptions::default() works.
304impl Default for base::assumptions::Props {
305    fn default() -> Self {
306        Self::empty()
307    }
308}
309
310/// The symplex prelude — one import to get started.
311///
312/// ```
313/// use symplex::prelude::*;
314/// ```
315pub mod prelude {
316    pub use crate::api::context::Context;
317    pub use crate::api::eq::Equation;
318    pub use crate::api::expr::{
319        BoolEx, Boolean, Ex, Expr, ExprType, Numeric, SetEx, SetValued, SimplifyOpts, Sort,
320    };
321    pub use crate::api::expr_ops::{Scalar, ToEx};
322    pub use crate::api::expr_rules_ext::{
323        Bindings, RewriteOpts, RewriteStrategy, Rule, RuleSet, Step,
324    };
325    pub use crate::api::expr_sets_ext::reduce_inequalities;
326    pub use crate::api::expr_solve_ext::InitialCondition;
327    pub use crate::api::expr_solve_ext::{
328        GeneralSolution, LinearSolution, NewtonOpts, ZeroForm, linsolve, linsolve_matrix,
329        solve_numeric_system, solve_numeric_system_with,
330    };
331    pub use crate::api::expr_transforms_ext::{Direction, FourierConvention, FourierSeries};
332    pub use crate::api::expr_view::ExprView;
333    pub use crate::api::poly_ex::Poly;
334    pub use crate::base::assumptions::{Assumption, Assumptions, Props};
335    pub use crate::base::config::EvalConfig;
336    pub use crate::base::errors::SymplexError;
337    pub use crate::base::extended::Extended;
338    pub use crate::base::interval::{Bounds, Interval, IntervalKind};
339    pub use crate::calculus::definite::{QuadOpts, QuadResult};
340    pub use crate::calculus::formal_series::FormalPowerSeries;
341    pub use crate::domains::control::{StateSpace, TransferFunction};
342    pub use crate::domains::decompositions::{
343        Diagonalization, HermiteNormalForm, Hessenberg, JordanForm, Ldl, LllReduction, Lu, Qr,
344        RankDecomposition, SmithNormalForm,
345    };
346    pub use crate::domains::linprog::{Feasibility, LpProblem, LpSolution, LpStatus, Q};
347    pub use crate::domains::matrix::{Matrix, QMatrix, ZMatrix};
348    pub use crate::domains::optimize::{MinimizeOpts, MinimizeResult, RootOpts};
349    pub use crate::domains::quaternion::Quaternion;
350    pub use crate::domains::robotics::EulerConvention;
351    pub use crate::domains::vector::CoordinateSystem;
352    pub use crate::output::codegen::{CodegenOptions, MathBackend, Precision};
353    pub use crate::output::lambdify::{CompiledFn, CompiledFnVec};
354    pub use crate::poly::multipoly::{MonomialOrder, MultiPoly};
355    pub use crate::transforms::expand::ExpandOpts;
356    pub use num_complex::Complex64;
357    pub use symplex_macros::{dim, eq, expr, matrix, rule};
358
359    // NOTE: `vars!`, `syms!`, and `sym!` are `#[macro_export]` macros and
360    // live at the crate root.  Use `use symplex::{vars, syms, sym};` or
361    // `use symplex::prelude::*; use symplex::vars;` to bring them in.
362}