oximo is a Rust algebraic modeling library for mathematical optimization. Build LP, MILP, QP/MIQP, NLP, and MINLP models with a concise macro API, then solve them with bundled or commercial solvers.
use *;
use Highs;
let m = new;
variable!;
variable!;
constraint!;
constraint!;
constraint!;
objective!;
let result = Highs.solve?;
println!; // 34.0
println!; // 6.0
println!; // 4.0
# Ok::
Building models
Variables
let m = new;
variable!; // continuous, x >= 0
variable!; // continuous, 0 <= y <= 10
variable!; // free (unbounded by default)
variable!; // binary {0, 1} (also Binary)
variable!; // general integer (also Integer)
variable!; // semicontinuous: 0 or in [2, 10] (SemiInt too)
Bounds, domain, warm start, and fixing can also be given as keyword args after the name:
variable!; // same as `0.0 <= x <= 1.0`
variable!; // keyword domain
variable!; // mixed with positional domain token
variable!; // warm start (scalar only)
variable!; // fixed to 5.0 (scalar only)
Constraints and objectives
Expressions are built with standard Rust operators. The macros let you write the
relational operators ==, <=, >= directly. Scalar multiplication, addition,
subtraction, and nonlinear operations (see Nonlinear Expressions) all work out of the box:
constraint!;
constraint!;
constraint!;
constraint!; // two-sided range -> band_lo + band_hi
objective!;
// or
objective!; // also Minimize/min, Maximize/max
Indexed variables
variable!(m, x[k in set]) registers one scalar per key with auto-named entries
like x[seattle,nyc]. Bounds apply uniformly by default; a multi-index family
ranges over a Cartesian product.
let m = new;
variable!; // one var per route
variable!; // integer family
variable!; // multi-index (Cartesian product)
// Scalar lookup: any type that converts to IndexKey works.
let e1 = x;
let e2 = z;
// Per-key bounds may reference the index
variable!;
variable!;
// Filtered family: keep only matching keys (no trivial elements built).
variable!;
Summing over sets
sum!(body for k in set) reads as sum_{k in set} body.
// Single sum: sum_{i in items} weights[i] * x[i]
constraint!;
// Double sum, flat: sum_{(p,q) in P*M} c[p,q] * x[p,q]
let total_cost = sum!;
// Filtered sum.
let active = sum!;
Rule-style constraints
The indexed form of constraint! emits one constraint per key, auto-named like
supply[seattle]. A trailing if filters the keys, and name = expr gives a
computed run-time name.
// Scalar set: one constraint per period.
let periods = range;
constraint!;
// Tuple set + inner sum builds the LHS expression (key types inferred).
constraint!;
// Filtered family: only the keys passing the guard are built.
constraint!;
// Computed run-time name.
constraint!;
Index sets
A Set is the modeling-layer container for an ordered, finite index set over
integers, strings, or tuples. Most domains need no explicit Set: an integer
range is already a domain (x[i in 0..5], sum!(.. for i in 0..n)). Reach for
Set when keys are strings, tuples, sparse, or a subset reused across statements.
The set! macro binds a named set. A plain right side is normalized to an owned
set, a pat in domain[ if cond] comprehension builds (and optionally filters)
one.
use *;
let plants = strings;
set!; // range normalized to Set<usize>
set!; // Cartesian product
// Comprehension: product domain + by-value `if`. These two are equivalent.
set!; // single tuple pattern
set!; // multi-bind product
// The typed filter is also a Set method (the receiver pins the key type):
let diag = .filter_typed;
// Sparse / string leaf sets keep their constructors.
let sparse = from_ints;
Nonlinear expressions
Pow, Sin, Cos, Exp, Log, Abs, and bilinear products are first-class. The
model's kind (LP/MILP/QP/MIQP/NLP/MINLP) is inferred from the
expressions.
// Rosenbrock NLP
objective!;
// Quadratic constraint
constraint!;
// Transcendental utility (MINLP when any variable is integer/binary)
objective!;
Solving
All backends implement the Solver trait:
Features
| Feature | What it adds | Default |
|---|---|---|
highs |
HiGHS - LP/MILP/QP solver (bundled, no install) | yes |
io |
MPS and LP file writers | yes |
gurobi |
Gurobi - LP/MILP/QP/MIQP/NLP/MINLP solver (requires licensed install) | no |
gams |
GAMS bridge - LP/MILP/QP/MIQP/NLP/MINLP depending on solver | no |
baron |
BARON - LP/MILP/QP/MIQP/NLP/MINLP solver (requires licensed install) | no |
HiGHS (default)
No install required, HiGHS is compiled from source via the highs crate.
use *;
use Highs;
let result = Highs.solve?;
Gurobi
Requires a licensed Gurobi install and GUROBI_HOME set. See crates/oximo-gurobi/README.md.
use *;
use Gurobi;
let result = Gurobi.solve?;
GAMS
Requires GAMS on PATH. Supports solving models via GAMS solvers (CPLEX, BARON, etc.). See crates/oximo-gams/README.md.
use *;
use Gams;
let result = Gams.solve?;
BARON
Requires a licensed BARON install on PATH. Global solver for nonconvex LP/MILP/QP/MIQP/NLP/MINLP. See crates/oximo-baron/README.md.
use *;
use Baron;
let result = new.solve?;
Reading results
For a quick, model-aware summary, print result.report(&m). For programmatic access:
let result = Highs.solve?;
match result.status
// Variable values (best solution)
let x_val = result.value_of; // Option<f64>
// Constraint duals
let dual = result.dual_of; // Option<f64>
// Reduced costs, keyed by VarId
let rc = result.reduced_costs.get;
// Solution pools (e.g. Gurobi, BARON with .num_sol(n)): all points, best first
for i in 0..result.result_count
Model export
With the io feature (default), you can export models to MPS, LP and NL format for inspection or use with external solvers.
Requirements
- Gurobi feature: Gurobi,
GUROBI_HOMEset, valid license - GAMS feature: GAMS on
PATH, valid license - BARON feature: BARON on
PATH, valid license
License
MIT OR Apache-2.0