Skip to main content

oxiz_math/
lib.rs

1//! # oxiz-math
2//!
3//! Mathematical foundations for the OxiZ SMT solver.
4//!
5//! This crate provides Pure Rust implementations of mathematical algorithms
6//! required for SMT solving, including:
7//!
8//! ## Linear Arithmetic
9//! - **Simplex**: Dual simplex algorithm for linear programming (LRA theory)
10//! - **Interior Point**: Primal-dual interior point method for large-scale LP
11//! - **Matrix**: Dense and sparse matrix operations with Gaussian elimination
12//! - **Interval**: Interval arithmetic for bound propagation
13//! - **Delta Rational**: Support for strict inequalities in simplex
14//! - **BLAS**: High-performance BLAS operations for large-scale LP (1000+ variables)
15//!
16//! ## Non-Linear Arithmetic
17//! - **Polynomial**: Multivariate polynomial arithmetic with GCD and factorization
18//! - **Rational Function**: Arithmetic on quotients of polynomials (p/q operations)
19//! - **Gröbner**: Gröbner basis computation (Buchberger, F4, and F5 algorithms)
20//! - **Real Closure**: Algebraic number representation and root isolation
21//! - **Hilbert**: Hilbert basis computation for integer cones
22//!
23//! ## Decision Diagrams
24//! - **BDD**: Reduced Ordered Binary Decision Diagrams
25//! - **ZDD**: Zero-suppressed BDDs for sparse set representation
26//! - **ADD**: Algebraic Decision Diagrams for rational-valued functions
27//!
28//! ## Numerical Utilities
29//! - **Rational**: Arbitrary precision rational arithmetic utilities
30//! - **MPFR**: Arbitrary precision floating-point arithmetic (MPFR-like)
31//!
32//! # Features
33//!
34//! - **`std`** (default): enables the standard library. Without it the crate
35//!   builds as `no_std` + `alloc`. The following modules need floating-point
36//!   or OS facilities that `core` does not provide and are therefore
37//!   available only with `std`: [`blas`], [`blas_ops`], [`mpfr`],
38//!   [`simd::matrix_simd`], [`simd::simplex_simd`], and
39//!   [`rational::is_prime`] (which needs a random number generator).
40//!   Everything else -- polynomials, Gröbner bases, simplex, intervals,
41//!   BDDs, real closure -- builds in both configurations.
42//! - **`simd`**: reserved. The kernels in [`simd`] are compiled
43//!   unconditionally and rely on auto-vectorization, so enabling this
44//!   feature currently changes nothing; it exists so that an explicit
45//!   opt-in does not become a breaking change later.
46//!
47//! # Examples
48//!
49//! ## Polynomial Arithmetic
50//!
51//! ```
52//! use oxiz_math::polynomial::{Polynomial, Var};
53//!
54//! // Create polynomial for variable x (index 0)
55//! let x: Var = 0;
56//!
57//! // Create polynomial representing just x
58//! let p = Polynomial::from_var(x);
59//!
60//! // Compute x * x = x^2
61//! let p_squared = p.clone() * p.clone();
62//! ```
63//!
64//! ## BDD Operations
65//!
66//! ```
67//! use oxiz_math::bdd::BddManager;
68//!
69//! let mut mgr = BddManager::new();
70//!
71//! // Create variables (VarId is u32)
72//! let x = mgr.variable(0);
73//! let y = mgr.variable(1);
74//!
75//! // Compute x AND y
76//! let and_xy = mgr.and(x, y);
77//!
78//! // Compute x OR y
79//! let or_xy = mgr.or(x, y);
80//! ```
81//!
82//! ## BLAS Operations
83//!
84//! ```
85//! use oxiz_math::blas::{ddot, dgemv, Transpose};
86//!
87//! // Vector dot product
88//! let x = vec![1.0, 2.0, 3.0];
89//! let y = vec![4.0, 5.0, 6.0];
90//! let dot = ddot(&x, &y);
91//! assert_eq!(dot, 32.0);
92//! ```
93//!
94//! ## Arbitrary Precision Floats
95//!
96//! ```
97//! use oxiz_math::mpfr::{ArbitraryFloat, Precision, RoundingMode};
98//!
99//! let prec = Precision::new(128);
100//! let a = ArbitraryFloat::from_f64(3.14159, prec);
101//! let b = ArbitraryFloat::from_f64(2.71828, prec);
102//! let sum = a.add(&b, RoundingMode::RoundNearest);
103//! ```
104
105#![cfg_attr(not(feature = "std"), no_std)]
106#![warn(missing_docs)]
107
108#[cfg(not(feature = "std"))]
109extern crate alloc;
110
111mod prelude;
112
113pub mod algebraic;
114pub mod bdd;
115#[cfg(feature = "std")]
116pub mod blas;
117#[cfg(feature = "std")]
118pub mod blas_ops;
119pub mod delta_rational;
120pub mod fast_rational;
121pub mod grobner;
122pub mod hilbert;
123pub mod interior_point;
124pub mod interval;
125pub mod lp;
126pub mod lp_core;
127pub mod matrix;
128#[cfg(feature = "std")]
129pub mod mpfr;
130pub mod polynomial;
131pub mod rational;
132pub mod rational_function;
133pub mod realclosure;
134pub mod realclosure_advanced;
135pub mod simd;
136pub mod simplex;
137pub mod simplex_parametric;
138pub mod simplex_solver;
139
140pub use simplex_solver::{
141    Constraint as SimplexConstraint, ConstraintKind, SimplexError, SimplexSolver, SolveResult,
142    SolveStatus,
143};
144
145#[cfg(test)]
146mod integration_tests {
147    use super::*;
148    use num_bigint::BigInt;
149    use num_rational::BigRational;
150
151    fn rat(n: i64) -> BigRational {
152        BigRational::from_integer(BigInt::from(n))
153    }
154
155    #[test]
156    fn test_grobner_with_root_isolation() {
157        // Integration test: Use Gröbner basis to simplify, then isolate roots
158        // System: x^2 - 2 = 0, y - x = 0
159        // Should reduce to y^2 - 2 = 0
160
161        let x_squared_minus_2 = polynomial::Polynomial::from_coeffs_int(&[
162            (1, &[(0, 2)]), // x^2
163            (-2, &[]),      // -2
164        ]);
165
166        let y_minus_x = polynomial::Polynomial::from_coeffs_int(&[
167            (1, &[(1, 1)]),  // y
168            (-1, &[(0, 1)]), // -x
169        ]);
170
171        let gb = grobner::grobner_basis(&[x_squared_minus_2.clone(), y_minus_x]);
172
173        // The Gröbner basis should contain polynomials
174        assert!(!gb.is_empty());
175
176        // One of the polynomials should be univariate
177        let has_univariate = gb.iter().any(|p| p.is_univariate());
178        assert!(has_univariate || gb.len() == 1);
179    }
180
181    #[test]
182    fn test_nra_solver_with_algebraic_numbers() {
183        // Integration test: NRA solver with algebraic number evaluation
184        // Solve x^2 - 2 = 0
185
186        let mut solver = grobner::NraSolver::new();
187
188        let x_squared_minus_2 = polynomial::Polynomial::from_coeffs_int(&[
189            (1, &[(0, 2)]), // x^2
190            (-2, &[]),      // -2
191        ]);
192
193        solver.add_equality(x_squared_minus_2.clone());
194
195        // Should be satisfiable
196        assert_eq!(solver.check_sat(), grobner::SatResult::Sat);
197
198        // Create algebraic number for sqrt(2)
199        // AlgebraicNumber::new(poly, var, lower, upper)
200        let sqrt_2 = realclosure::AlgebraicNumber::new(
201            x_squared_minus_2,
202            0, // variable 0
203            rat(1),
204            rat(2),
205        );
206
207        // Algebraic number should be valid
208        let _ = sqrt_2;
209    }
210
211    #[test]
212    fn test_interval_with_polynomial_bounds() {
213        // Integration test: Use interval arithmetic with polynomial evaluation
214        // Evaluate x^2 over [1, 2] should give [1, 4]
215
216        let x_squared = polynomial::Polynomial::from_coeffs_int(&[(1, &[(0, 2)])]);
217
218        // Evaluate at x = 1
219        let mut assignment1 = crate::prelude::FxHashMap::default();
220        assignment1.insert(0, rat(1));
221        let val1 = x_squared.eval(&assignment1);
222        assert_eq!(val1, rat(1));
223
224        // Evaluate at x = 2
225        let mut assignment2 = crate::prelude::FxHashMap::default();
226        assignment2.insert(0, rat(2));
227        let val2 = x_squared.eval(&assignment2);
228        assert_eq!(val2, rat(4));
229
230        // Create interval [1, 4]
231        let interval = interval::Interval::closed(rat(1), rat(4));
232        assert!(interval.contains(&val1));
233        assert!(interval.contains(&val2));
234    }
235
236    #[test]
237    fn test_delta_rationals_ordering() {
238        // Integration test: Delta rationals for strict inequalities
239        let delta_zero = delta_rational::DeltaRational::from_rational(rat(0));
240        let delta_small = delta_rational::DeltaRational::new(rat(0), 1); // delta_coeff is i64
241
242        // 0 + delta > 0
243        assert!(delta_small > delta_zero);
244
245        // Delta rationals maintain ordering
246        let delta_one = delta_rational::DeltaRational::from_rational(rat(1));
247        assert!(delta_one > delta_small);
248    }
249
250    #[test]
251    fn test_matrix_operations() {
252        // Integration test: Matrix operations (used in F4 algorithm)
253        use matrix::Matrix;
254        use num_rational::Rational64;
255
256        // Create a simple 2x2 matrix
257        let m = Matrix::from_vec(
258            2,
259            2,
260            vec![
261                Rational64::new(2, 1),
262                Rational64::new(1, 1),
263                Rational64::new(1, 1),
264                Rational64::new(1, 1),
265            ],
266        );
267
268        // Check matrix values
269        assert_eq!(m.get(0, 0), Rational64::new(2, 1));
270        assert_eq!(m.get(0, 1), Rational64::new(1, 1));
271    }
272
273    #[test]
274    fn test_polynomial_factorization_with_grobner() {
275        // Integration test: Factorization helps with Gröbner basis computation
276        // x^2 - y^2 can be analyzed via Gröbner basis
277
278        let x_sq_minus_y_sq = polynomial::Polynomial::from_coeffs_int(&[
279            (1, &[(0, 2)]),  // x^2
280            (-1, &[(1, 2)]), // -y^2
281        ]);
282
283        // Compute Gröbner basis of {x^2 - y^2}
284        let gb = grobner::grobner_basis(&[x_sq_minus_y_sq]);
285
286        assert!(!gb.is_empty());
287    }
288
289    #[test]
290    fn test_real_closure_root_isolation_integration() {
291        // Integration test: Real closure and root isolation
292        // Find roots of x^3 - 2 = 0
293
294        let poly = polynomial::Polynomial::from_coeffs_int(&[
295            (1, &[(0, 3)]), // x^3
296            (-2, &[]),      // -2
297        ]);
298
299        // Isolate roots (for variable 0)
300        let roots = poly.isolate_roots(0);
301
302        // Should find at least one real root (cube root of 2)
303        assert!(!roots.is_empty());
304    }
305
306    #[test]
307    fn test_polynomial_gcd_univariate() {
308        // Integration test: GCD computation for univariate polynomials
309        // gcd(x^2 - 1, x - 1) = x - 1
310
311        let p1 = polynomial::Polynomial::from_coeffs_int(&[
312            (1, &[(0, 2)]), // x^2
313            (-1, &[]),      // -1
314        ]);
315
316        let p2 = polynomial::Polynomial::from_coeffs_int(&[
317            (1, &[(0, 1)]), // x
318            (-1, &[]),      // -1
319        ]);
320
321        let gcd = p1.gcd_univariate(&p2);
322
323        // GCD should be x - 1 (or a scalar multiple)
324        assert_eq!(gcd.total_degree(), 1);
325    }
326}