The Pure Rust SMT solver just got a single, ergonomic front door — one dependency, capabilities on demand.
Today we released OxiZ 0.1.1 — a same-day usability patch that introduces the unified oxiz meta-crate (add one dependency, turn features on with flags) and lands real correctness fixes in quantifier instantiation and theory propagation.
No C. No Fortran. No FFI. For roughly fifteen years, Z3 (C++) has been the de-facto SMT standard, and for good reason — but it brings a C++ toolchain and an FFI boundary along with it. OxiZ is the Pure Rust alternative: it compiles to a single static binary (or WASM), it is memory-safe by construction, and every line of the solver is auditable Rust. No linking against a foreign object you can’t read.
Why 0.1.1 matters
The day after a big debut, the most-requested thing is almost never a new theory — it’s an ergonomic way in. OxiZ 0.1.0 shipped eleven crates: a CDCL SAT core, a theory layer, a DPLL(T) solver, proof generation, and more. Powerful, but wiring up around eleven separate dependencies by hand is friction.
0.1.1 fixes exactly that. The new oxiz meta-crate means you add one dependency and switch capabilities on with Cargo feature flags. Want just a solver? That’s the default. Need nonlinear arithmetic, optimization, or a CHC engine? Flip a flag. No version-juggling across a constellation of sub-crates.
It is also more than packaging. 0.1.1 lands genuine correctness fixes — Model-Based Quantifier Instantiation comparison handlers, CDCL(T) theory propagation, and basic bitvector support — so formulas that previously stumbled now solve.
The numbers behind it (from the 0.1.1 README/CHANGELOG): roughly 90–92% Z3 feature parity, about 173,500 lines of Rust, 3,670 tests, with 84/84 solver tests passing for this fix release. It was also verified against the Legalis formal-verification framework — 467/467 tests passing.
Technical Deep Dive
The meta-crate feature map. The oxiz crate is a thin, well-documented umbrella over the workspace. The feature surface (straight from its Cargo.toml):
default = ["solver"]— pulls inoxiz-sat+oxiz-theories+oxiz-solver. The everyday SMT solving path.nlsat— nonlinear real arithmetic (NLSAT).optimization— MaxSMT / Optimization Modulo Theories (OMT).spacer— the CHC (Constrained Horn Clause) solver for program verification.proof— proof generation.standard—solver+nlsat+optimization+proof. The common bundle, minus the heavyweight CHC engine.full— everything, includingspacer.
The CDCL(T) stack it wraps. Under the umbrella, the architecture is unchanged and battle-tested:
oxiz-sat— a modern CDCL SAT engine with VSIDS / LRB / VMTF / CHB decision heuristics and DRAT proof logging.oxiz-theories— the theory solvers: EUF, LRA, LIA, BV, Arrays, Strings, FP, and Datatypes.oxiz-solver— the DPLL(T) orchestration layer, including MBQI (Model-Based Quantifier Instantiation).oxiz-proof— proof artifacts in DRAT / Alethe / LFSC, with export to Coq, Lean, and Isabelle.
The fixes. See What’s New below for the full list — the headline trio is MBQI comparison evaluation, correct incremental backtracking in the arithmetic theory, and bounded-integer bitvector comparisons.
Getting Started
Install is one line:
cargo add oxiz
Pick exactly the capabilities you want with feature flags:
[dependencies]
oxiz = "0.1.1" # default: solver
# oxiz = { version = "0.1.1", features = ["nlsat", "optimization"] }
# oxiz = { version = "0.1.1", features = ["full"] }
A minimal, copy-pasteable example using the 0.1.1 builder API — solve x + y = 10 with x > 5:
use oxiz::{Solver, TermManager, SolverResult};
use num_bigint::BigInt;
fn main() {
let mut solver = Solver::new();
let mut tm = TermManager::new();
// x + y = 10, with x > 5
let x = tm.mk_var("x", tm.sorts.int_sort);
let y = tm.mk_var("y", tm.sorts.int_sort);
let sum = tm.mk_add([x, y]);
let ten = tm.mk_int(BigInt::from(10));
let eq = tm.mk_eq(sum, ten);
let five = tm.mk_int(BigInt::from(5));
let gt = tm.mk_gt(x, five);
solver.assert(eq, &mut tm);
solver.assert(gt, &mut tm);
match solver.check(&mut tm) {
SolverResult::Sat => println!("SAT"),
SolverResult::Unsat => println!("UNSAT"),
SolverResult::Unknown => println!("Unknown"),
}
}
Prefer to feed raw SMT-LIB2? That path is still here: oxiz::solver::Context together with execute_script will run a standard SMT-LIB script for you.
What’s New in 0.1.1
Added
- NEW: the unified
oxizmeta-crate. Feature flags:solver(default),nlsat,optimization,spacer,proof,standard, andfull. One dependency, capabilities on demand. - Workspace-level lint configuration for consistent code quality across the whole project.
- Comprehensive API documentation added to the meta-crate.
Changed
- Updated README with meta-crate usage; crates.io badges now point at
oxiz. - Removed the redundant
rust-versionfield — Edition 2024 already requires Rust 1.85+.
Fixed
- MBQI (Model-Based Quantifier Instantiation): added the missing
Gt,Ge, andLecases inevaluate_under_model()(previously onlyLtwas handled), fixing quantifier-instantiation failures on those comparisons. - CDCL(T) theory propagation:
add_le()now substitutes basic variables before adding constraints (matchingadd_strict_lt());ArithSolver::push()/pop()now callsimplex.push()/pop()for correct incremental backtracking; and a newon_new_level()callback onTheoryCallbacklets the SAT solver notify theory solvers when a new decision level is opened — closing a class of stale-state bugs. - Bitvectors: basic BV support via
BvUlt/BvUle/BvSlt/BvSleas bounded integer comparisons;BitVecConstis now handled in arithmetic constraint parsing.
Compatibility
- No breaking changes — this is a backwards-compatible bug-fix release. 84/84 solver tests passing, clippy-clean under
-D warnings, and verified with Legalis (467/467).
Tips
-
Add one dependency, not eleven. Reach for
oxizand toggle features instead of juggling sub-crates by hand. -
Pick the right bundle.
features = ["full"]gives you everything (including the CHCspacerengine);features = ["standard"]is the common-but-no-spacer set (solver + nlsat + optimization + proof). -
Raw SMT-LIB2 when you want it. Use
oxiz::solver::Context::execute_scriptto feed standard SMT-LIB2 scripts directly:use oxiz::solver::Context; let mut ctx = Context::new(); ctx.execute_script("(set-logic QF_LIA)(declare-const x Int)(assert (> x 5))(check-sat)"); -
The builder API is the idiomatic in-Rust path.
Solver+TermManagerkeeps everything type-checked and free of string parsing. -
Migrating from 0.1.0? If you were depending on
oxiz-solverdirectly, move tooxiz— no code-breaking changes, just a cleaner front door. -
Quantifiers that failed before should work now. MBQI handles every comparison operator (
<,<=,>,>=) in quantified formulas in 0.1.1, so instantiation that stalled in 0.1.0 should succeed.
This is the foundation
Even at 0.1.1, OxiZ is already doing real work: it was verified with the Legalis formal-verification framework, passing all 467/467 tests — early evidence that a Pure Rust SMT solver can underpin serious verification, not just toy benchmarks. As the broader Pure Rust ecosystem grows, a fast, auditable, FFI-free SMT solver is the kind of bedrock everything else can stand on.
Repository: https://github.com/cool-japan/oxiz
Star the repo if a Pure Rust Z3 replacement is something you’ve been waiting for — it genuinely helps.
Pure Rust formal reasoning is here — fast, safe, and sovereign.
— KitaSan at COOLJAPAN OÜ January 12, 2026