oximo-core
Core modeling types for oximo: Model, Variable, Set, Constraint, Objective, Parameter, IndexedVar, Domain, and ModelKind.
Re-exports oximo-expr types (Expr, ExprArena, ExprId, ExprNode, ParamId, VarId) so downstream code does not need a separate oximo-expr import. End users typically depend on the umbrella oximo crate rather than this one directly.
Usage
[]
= "0.4"
Or via the umbrella crate (recommended for end users):
[]
= "0.4"
Quick example
use *;
let m = new;
// Scalar variables
variable!;
variable!;
// Constraints (incl. a two-sided range, kept as one constraint)
constraint!;
constraint!;
constraint!;
// Objective (or `objective!(m, Feasibility)` for a pure feasibility problem)
objective!;
println!; // LP
Modeling API
The modeling surface is a set of macros: variable!, constraint!, objective!,
sum!, set!, and param!. Each expands to the underlying typed model operations,
so there is no runtime cost and full compile-time type/borrow checking is preserved.
Model uses interior mutability (RefCell), so a macro can take &m, register
variables/constraints, and the variable!-introduced bindings (x, y, ...) are
locals you can use immediately.
let m = new;
variable!; // binds a local `x: Expr<'_>`
constraint!; // uses x while holding &m
Names are unique per registry. Registering a duplicate variable or constraint name panics.
Accessors
m.num_variables // usize
m.num_constraints // usize
m.variables // Ref<'_, Vec<Variable>>
m.constraints // Ref<'_, Vec<Constraint>>
m.arena // Ref<'_, ExprArena>
m.kind // ModelKind, cached, invalidated on change
m.try_objective // Result<Objective, Error>
m.variable_id // Option<VarId>
m.constraint_id // Option<ConstraintId>
Fixing and unfixing variables
m.fix_var; // lb = ub = 3.0
m.unfix_var; // restore bounds
Variables
Scalar variables
variable!; // free (-inf, +inf)
variable!; // lower bound only
variable!; // both bounds
variable!; // binary {0, 1} (also Binary)
variable!; // general integer (also Integer)
variable!; // semicontinuous: 0 or in [2, 10]
variable!; // semi-integer: 0 or integer in [1, 5]
// Keyword args:
variable!; // same as `0.0 <= u <= 10.0`
variable!; // keyword domain (or a positional `Int`)
variable!; // warm start (scalar only)
variable!; // fixed to 5.0 (scalar only)
Indexed variables
Creates one scalar variable per key in a Set (or range), named base[key],
and binds an IndexedVar.
let i = range;
variable!; // uniform bounds
variable!; // integer family
variable!; // multi-index (Cartesian product)
// Access by key (panics on missing key):
let expr = x; // single key (usize / "name" / (a, b))
let e2 = z; // inside the macros: multi-index sugar == z[(&a, &b)]
// Bounds may reference the index -> lowered to per-key bounds:
variable!;
// Filtered family: keep only matching keys (no trivial elements built).
variable!;
Domain
| Variant | Description |
|---|---|
Domain::Real |
Any real number (default) |
Domain::Integer |
Any integer |
Domain::Binary |
0 or 1 |
Domain::SemiContinuous { threshold } |
0 or any value >= threshold |
Domain::SemiInteger { threshold } |
0 or any integer >= threshold |
Sets
Set is an ordered finite index set. Three variants:
let i = range; // Range: i64 keys 0..5
let j = strings; // Strings
let k = product; // Tuples: (0,"a"), (0,"b"), ...
let k = &i * &j; // Same via Mul operator
// From sparse ints:
let s = from_ints;
// Filter:
let evens = i.filter;
Constraints
==, <=, and >= are written directly, the macro intercepts the tokens, so
these are real constraint operators.
constraint!; // named, also >= and ==
constraint!; // anonymous (auto-named _c0, _c1, ...)
constraint!; // two-sided range -> one constraint (expr bounds -> band_lo/band_hi)
constraint!; // computed run-time name
Indexed family over a set
// One constraint per key, auto-named supply[seattle], ...
constraint!;
// Multi-index family (multi-index access sugar: x[i, j]).
constraint!;
// Filtered family: only keys passing the guard.
constraint!;
Second-order cone constraints
soc_constraint! registers ||terms||_2 <= bound. Every term and the bound
must be affine. The model classifies as SOCP/MISOCP.
soc_constraint!; // named -> SocConstraintId
soc_constraint!; // anonymous (auto-named _soc0, ...)
soc_constraint!; // computed run-time name
soc_constraint!; // family: risk[key] per key
The method form m.add_soc_constraint("cone", [x, y], t) is equivalent.
Summation
sum!(body for k in domain) reads as sum_{k in domain} body. Nest with extra
clauses and filter with a trailing if:
constraint!;
objective!;
let evens = sum!; // filtered
Objectives
objective!;
objective!;
Parameters
param!; // binds a re-bindable `rate: Expr<'_>`
rate.set_param_value; // change between solves without rebuilding
Indexed parameters
Mirror indexed variables: one re-bindable scalar parameter per key, bound as an
IndexedParam. The right-hand side is evaluated per key and may reference the
index.
let items = range;
param!; // one parameter per key
param!; // multi-index
param!; // string-keyed (sparse)
let unit = cost; // index for a param `Expr`
cost.set_param_value; // re-bind one entry via its handle
m.set_param_idx; // ...or by key on the model
m.param_value_idx; // -> Some(9.0)
Model kind
Inferred automatically from variables and expressions, cached and invalidated
on change. The decision ladder runs top-down. Any integer/binary variable
picks the MI* variant of the row that matches:
| Kind (continuous/integer) | Conditions |
|---|---|
NLP/MINLP |
Any nonlinear expression (degree > 2, transcendentals, division) |
QCP/MIQCP |
Any quadratic constraint not recognized as a second-order cone |
SOCP/MISOCP |
Second-order cones present (explicit or detected) |
QP/MIQP |
Quadratic objective, linear constraints |
LP/MILP |
Everything linear |
License
MIT OR Apache-2.0