oximo-core 0.1.0

Core modeling types (Variable, Set, Constraint, Model) for oximo
Documentation

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

[dependencies]
oximo-core = "0.1"

Or via the umbrella crate (recommended for end users):

[dependencies]
oximo = "0.1"

Quick example

use oximo_core::prelude::*;

let m = Model::new("transport");

// Scalar variables
let x = m.var("x").lb(0.0).build();
let y = m.var("y").lb(0.0).ub(10.0).build();

// Constraints
m.constraint("c1", (x + 2.0 * y).le(14.0));
m.constraint("c2", (3.0 * x - y).ge(0.0));

// Objective
m.maximize(3.0 * x + 4.0 * y);

println!("kind = {:?}", m.kind()); // LP

Model

Model uses interior mutability (RefCell) so the builder API takes &self. This lets you hold a &Model reference, build variables and constraints, and immediately use the returned Expr handles, no &mut threading required.

let m = Model::new("my_model");
let x = m.var("x").lb(0.0).build(); // returns Expr<'_>
m.constraint("cap", x.le(5.0));     // 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("x")     // Option<VarId>
m.constraint_id("cap") // Option<ConstraintId>

Fixing and unfixing variables

m.fix_var(var_id, 3.0);         // lb = ub = 3.0
m.unfix_var(var_id, 0.0, 10.0); // restore bounds

Variables

Scalar variable builder

let x = m.var("x")
    .lb(0.0)              // lower bound (default: -inf)
    .ub(10.0)             // upper bound (default: +inf)
    .domain(Domain::Real) // explicit domain (default: Real)
    .build();             // returns Expr<'_>

// Shorthand domain setters:
let b = m.var("b").binary().build();          // Domain::Binary, bounds [0, 1]
let n = m.var("n").integer().lb(0.0).build(); // Domain::Integer

Indexed variable builder

Creates one scalar variable per key in a Set, named base[key].

let i = Set::range(0..5);
let x = m.indexed_var("x", &i)
    .lb(0.0)
    .integer()
    .build(); // IndexedVar<'_>

// Access by key (panics on missing key):
let expr = x[2]; // or x["name"], x[(a, b)]

// Per-key bounds:
let x = m.indexed_var("x", &i)
    .lb_by(|k: usize| lower_bounds[k])
    .ub_by(|k: usize| upper_bounds[k])
    .build();

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 = Set::range(0..5);              // Range: i64 keys 0..5
let j = Set::strings(["a", "b", "c"]); // Strings
let k = Set::product(&i, &j);          // Tuples: (0,"a"), (0,"b"), ...
let k = &i * &j;                       // Same via Mul operator

// From sparse ints:
let s = Set::from_ints([0, 2, 4, 8]);

// Filter:
let evens = i.filter(|k| k.as_i64().unwrap() % 2 == 0);

Constraints

Single constraint

let c_id = m.constraint("name", expr.le(rhs)); // <=
let c_id = m.constraint("name", expr.ge(rhs)); // >=
let c_id = m.constraint("name", expr.eq(rhs)); // ==

Bulk, rule over a set

m.add_constraints_over("supply", &plants, |p: String| {
    supply[&p].le(capacity[&p])
});

// Tuple sets, destructure inline:
m.add_constraints_over("flow", &(&plants * &markets), |(p, m): (String, String)| {
    x[(&p, &m)].le(capacity[&p])
});

Objectives

m.minimize(cost_expr);
m.maximize(revenue_expr);

Model kind

Inferred automatically from variables and expressions, cached and invalidated on change:

Kind Conditions
LP All continuous, all linear
MILP Any integer/binary, all linear
QP All continuous, Mul with ≥2 non-const children
MIQP Any integer/binary + quadratic
NLP All continuous, Pow/Sin/Cos/Exp/Log
MINLP Any integer/binary + nonlinear

For now, we only support linear constraints, so QP and NLP are not possible. But the API is designed to allow nonlinear constraints in the future without breaking changes.

License

MIT OR Apache-2.0