oximo is solver-agnostic, and provides a variety of backends to solve your optimization models.
Every backend implements the same Solver trait, so swapping engines is a one-line change. The general pattern is:
let result = Backend.solve(&model, &BackendOptions::default())?;
result is a SolverResult, the same struct regardless of backend.
πChoosing a backend
To determine what backend to use, consider what each backend solves (by model kind) and what it costs and offers (license, install, diagnostics). Each backend's Cargo feature flag is named in its own section below.
πModel kind support
Each row is what Solver::supports accepts for that backend, against every ModelKind:
| Backend | LP | MILP | QP | MIQP | QCP | MIQCP | SOCP | MISOCP | SDP | MISDP | NLP | MINLP |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
Highs | β | β | β | β | β | β | β | β | β | β | β | β |
Clarabel | β | β | β | β | β | β | β | β | **β**ΒΉ | β | β | β |
Pounce | β | β | β | β | β | β | β | β | β | β | β | β |
Scip | β | β | β | β | β | β | β | β | β | β | β | β |
Gurobi | β | β | β | β | β | β | β | β | β | β | β | β |
Mosek | β | β | β | β | β | β | β | β | β | β | β | β |
Baron | β | β | β | β | β | β | β | β | β | β | β | β |
Gams | β | β | β | β | β | β | β | β | β | β | β | β |
Gams accepts every kind at the oximo layer, but the actual coverage is the GAMS sub-solver's. Pick one that handles the kind you emit.
ΒΉ Clarabel requires an SDP feature and BLAS/LAPACK provider.
πRequirements & capabilities
Deployment cost and diagnostic/solve capability per backend.
β = yes/supported; β = no.
| Backend | License | Separate install | C compiler | Direct interface | IIS | Warm start | Sol. pool | Duals | Parallel |
|---|---|---|---|---|---|---|---|---|---|
Highs | β | β | β | β | ββ | β | β | β | β |
Clarabel | β | β | β | β | β | β | β | β | β |
Pounce | β | β | β | β | β | β | β | β | β |
Scip | β | β | β | β | β | β | β | ββ‘ | β |
Gurobi | β | β | β | β | β | β | β | β | β |
Mosek | β | β | β | β | β | β | β | β | β |
Baron | β | β | β | β | β | β | β | β | β |
Gams | β | β | β | β | β | β | βΒΆ | β | β |
Column meanings:
- License: the underlying solver is commercial and needs a license at runtime. The oximo wrapper crates are all MIT OR Apache-2.0.
- Direct interface: talks to the solver in-process. C API/FFI for HiGHS, SCIP, Gurobi, and MOSEK. Pure Rust for Clarabel and Pounce. BARON and GAMS instead exchange model/result files (
.bar,.gms) with an external executable. - C compiler: whether a C/C++ compiler is required at build time.
- IIS: computes an irreducible infeasible set to explain an infeasible model.
- Warm start: persistent handle for incremental re-solves.
- Sol. pool: returns multiple solutions, best-first.
- Duals: shadow prices/reduced costs.
- Parallel: solver can solve in parallel.
Footnotes:
- β The
highscrate does not support IIS yet. - ΒΆ GAMS's pool and duals come from the underlying sub-solver's GDX output (e.g. CPLEX
solnpool). - β‘ SCIP returns LP duals and reduced costs only when it can certify a complete original-model LP solution.
Clarabel's build requirements above describe the base clarabel feature.
SDP adds native BLAS/LAPACK dependencies. Compiler and installation requirements
depend on the selected provider.
A backend rejects model kinds it can't handle, so check Model::kind() if a solve returns SolverError::UnsupportedKind.
πHiGHS
Highs is enabled with the highs Cargo feature. No external solver
install is required, but a C/C++ compiler is needed at build time. Add it with
cargo add oximo --features highs.
use oximo::prelude::*;
use oximo::solvers::Highs;
use std::time::Duration;
let result = Highs.solve(&m, &HighsOptions::default()
.time_limit(Duration::from_secs(60))
.threads(4)
.mip_gap(0.01)
.verbose(true)
.method(HighsMethod::Ipm))?;
Common HighsOptions:
| Method | Effect |
|---|---|
.time_limit(d) | Stop after d (std::time::Duration) |
.threads(n) | Cap parallelism |
.mip_gap(g) | Relative MIP optimality gap (e.g. 0.01 = 1%) |
.verbose(b) | Stream the solver log |
.method(m) | LP algorithm via HighsMethod (Simplex, Ipm, β¦) |
πClarabel
Clarabel is a conic interior-point solver. The base clarabel
feature is pure Rust, requires no external solver installation or license,
and handles continuous LP, QP (convex quadratic objectives), and SOCP models.
use oximo::prelude::*;
use oximo::solvers::Clarabel;
let result = Clarabel.solve(&m, &ClarabelOptions::default())?;πSemidefinite models
Enable clarabel-sdp with BLAS/LAPACK linkage, or select a provider feature
such as clarabel-sdp-mkl; see Installation.
This adds continuous SDP with real symmetric affine PSD blocks, linear rows,
and SOC constraints. Objectives may be affine or convex quadratic when
minimizing (concave quadratic when maximizing). General quadratic constraints,
nonlinear expressions, and integer variables are unsupported on this path.
Clarabel.persistent() updates compatible numerical data between solves.
Changes to cone structure rebuild the solver. Chordal decomposition,
presolve, or dropped structural zeros can also prevent a native update, in
which case oximo rebuilds automatically.
Primal and PSD dual matrices are available through the matrix result accessors.
πPOUNCE
Pounce is a pure-Rust IPOPT and convex-solver backend. Enable the
pounce feature:
use oximo::prelude::*;
use oximo::pounce::{Pounce, PounceOptions, PounceSolverSelection};
let result = Pounce.solve(&m, &PounceOptions::default())?;πDerivatives
- Default stable path: LP/QP/QCP models use exact analytic derivatives, including Jacobian rows and the constant Lagrangian Hessian. Nonlinear models use compiled tapes with finite-difference nonlinear derivatives and a limited-memory L-BFGS Hessian.
- pounce-enzyme feature: nightly-only exact gradients, sparse Jacobians, and sparse Lagrangian Hessians for nonlinear models. See Installation > Advanced: exact nonlinear derivatives.
Note:
pounce-enzymecurrently requires thenightly-2026-07-26toolchain. Later nightly toolchains fail. See rust-lang/rust#160470.
πAutomatic routing
PounceSolverSelection::Auto is the default. It certifies convexity before
selecting a specialized engine:
- LP and convex QP use the convex IPM.
- SOCP with a convex objective uses the conic IPM, including explicit cones and recognized quadratic SOC forms.
- QCP, indefinite QP, and general NLP use the TNLP/builder path.
- Inconclusive convexity checks fall back to NLP.
A numerical failure in an automatically selected LP or detected-SOCP route gets one NLP attempt. The specialized convex engines currently have no time-limit hook, with a time limit, Auto uses NLP.
πOptions
PounceOptions provides dedicated setters and typed builders for POUNCE's option reference:
use oximo::pounce::{MuStrategy, PounceOptions, PounceSolverSelection};
let options = PounceOptions::default()
.tol(1e-8)
.mu_strategy(MuStrategy::Adaptive)
.solver_selection(PounceSolverSelection::Auto)
.presolve(true)
.linear_solver("feral");
// Escape hatch for an option not exposed by a dedicated setter.
let options = options.set("acceptable_tol", 1e-5);
The backend manages print_level, max_cpu_time, warm_start_init_point, and
hessian_approximation. Configure those through the corresponding oximo options
or persistent handle.
πSCIP
Scip solves LP, MILP, QP, MIQP, QCP, MIQCP, SOCP, MISOCP, NLP,
and MINLP models through russcip. Enable scip to download bundled SCIP
binaries at build time:
[dependencies]
oximo = { version = "0.7", features = ["scip"] }use oximo::prelude::*;
use oximo::solvers::Scip;
use oximo::ScipOptions;
let result = Scip::new().solve(&m, &ScipOptions::default()
.mip_gap(0.01)
.node_limit(10_000))?;
For an existing SCIP installation, use scip-system instead of scip. Set
SCIPOPTDIR to a SCIP development tree containing include/scip headers and
lib/libscip*, or let the build find SCIP via CONDA_PREFIX or standard system
paths. The system build generates bindings with libclang. The scip and
scip-system features cannot be combined.
SCIP supports native SOS1/SOS2 sets, affine indicator constraints, semi-continuous and semi-integer variables, and explicit second-order cones. Nonlinear expressions support arithmetic, division, constant powers, negation, abs, sqrt, exp, log, sin, and cos. Nonlinear objectives are translated using an auxiliary variable and an epigraph or hypograph constraint.
ScipOptions includes universal time, thread, and verbosity controls, typed
SCIP limits and tolerances, and .param(name, value) for other supported
parameters. .concurrent(true) enables SCIP's concurrent solver; the thread
option alone only sets the maximum thread count.
Scip::new().persistent() reuses compatible models across solves and accepts
append-only variables and constraints. solve_detailed also returns SCIP
statistics and values for columns generated by pricers. Scip::with_plugins
registers event handlers, separators, pricers, branching rules, heuristics,
constraint handlers, and node selectors. Custom Rust plugins cannot be used
with concurrent solving.
πGurobi
Gurobi uses gurobi-rs when the
gurobi Cargo feature is enabled. It requires a licensed Gurobi installation.
Set GUROBI_HOME to the installation directory before building and make sure a
Gurobi license is active.
Note: Only Gurobi v13 and later are supported.
[dependencies]
oximo = { version = "0.7", features = ["gurobi"] }use oximo::prelude::*;
use oximo::solvers::Gurobi;
use std::time::Duration;
let result = Gurobi.solve(&m, &GurobiOptions::default()
.time_limit(Duration::from_secs(120))
.mip_focus(1)
.seed(101))?;
Common GurobiOptions:
| Method | Effect |
|---|---|
.time_limit(d) | Wall-clock limit |
.mip_focus(n) | Gurobi MIPFocus (1 = feasibility, 2 = optimality, 3 = bound) |
.mip_gap(g) | Relative MIP optimality gap |
.seed(n) | Random seed for reproducible runs |
πMOSEK
Mosek supports LP, MILP, convex QP/MIQP, convex QCP/MIQCP,
SOCP/MISOCP, and continuous SDP models. It requires the mosek Cargo feature, a licensed MOSEK
11.2 installation, and MOSEK_BINDIR_112 when MOSEK is outside its default
location. MOSEK validates the convexity of quadratic data. The backend links
through the mosek crate.
On Windows, setting MOSEK_BINDIR_112 directly to a path containing spaces can
fail because of an unquoted linker flag in the current mosek crate build
script. Create a junction with a space-free path and point the environment
variable at that junction; see mosek.rust#1.
Only MOSEK 11.2 is currently supported.
[dependencies]
oximo = { version = "0.7", features = ["mosek"] }use oximo::prelude::*;
use oximo::solvers::Mosek;
use std::time::Duration;
let result = Mosek.solve(&m, &MosekOptions::default()
.time_limit(Duration::from_secs(120))
.threads(4)
.mio_tol_rel_gap(1e-4))?;
MosekOptions provides builders for every MOSEK 11.2 parameter. Universal
options such as time_limit, threads, and verbose are applied first.
MOSEK-specific parameter builders are then applied in call order.
πSemidefinite models
The development backend supports real symmetric affine PSD blocks through the
existing mosek feature. The current SDP path requires an affine objective and
accepts linear and SOC constraints alongside PSD blocks. Quadratic objectives
with PSD blocks and mixed integer SDP are rejected; use supports_model(&m)
before selecting this backend.
Mosek.persistent() currently rebuilds SDP tasks on each solve, including
parameter-only changes. Primal and PSD dual matrices use the same
result accessors and conventions as Clarabel.
πBARON
Baron is a global solver for LP/MILP/QP/MIQP/QCP/MIQCP/SOCP/MISOCP/
NLP/MINLP models. The Oximo adapter supports all of these model kinds and
translates explicit second-order cones to BARON's quadratic constraint
representation. It requires the baron feature, a licensed BARON installation
on PATH, and exchanges model and result files with the external executable.
[dependencies]
oximo = { version = "0.7", features = ["baron"] }use oximo::prelude::*;
use oximo::solvers::Baron;
let result = Baron::new().solve(&m, &BaronOptions::default())?;πGAMS
Gams requires the gams Cargo feature plus a GAMS installation on
PATH. It exchanges model and result files with GAMS, which is useful when you
want to route a model through GAMS-managed solvers (CPLEX, BARON, IPOPT,
KNITRO, ...).
For a model containing indicators, select COPT, CPLEX, Gurobi, SCIP, or Xpress
explicitly in GamsOptions. The default solver selection is rejected because
indicator handling is sub-solver-specific.
[dependencies]
oximo = { version = "0.7", features = ["gams"] }use oximo::prelude::*;
use oximo::solvers::Gams;
let result = Gams::new().solve(&m, &GamsOptions::default())?;
See GamsOptions and the per-solver option structs in oximo::gams (GamsCplexOptions, GamsBaronOptions, GamsIpoptOptions, ...) for tuning the underlying solver.
πNonlinear operators
A solver accepting NLP or MINLP does not imply that a backend can represent every
nonlinear expression. oximo validates the expressions before solving
and returns SolverError::UnsupportedNonlinearOperator when a
translation is unavailable.
πNext steps
- Results: inspect solver status, values, duals, and solution pools
- Printing & Debugging: print a model as algebra and track down what it actually says
- I/O: write your model to MPS, LP or NL for use with external tools