Expand description
§oximo-solver
Solver trait, result types, status codes, and shared option building blocks for oximo.
This crate defines the contract that backend crates implement. End users interact with concrete backends (Highs, Gurobi, Gams) exposed by the umbrella oximo crate, they do not depend on this crate directly unless they are writing a new backend.
§Usage
[dependencies]
oximo-solver = "0.7.0"
oximo-core = "0.7.0"§Solver trait
pub trait Solver {
type Options;
fn name(&self) -> &str;
fn supports(&self, kind: ModelKind) -> bool;
fn solve(&mut self, model: &Model, opts: &Self::Options) -> Result<SolverResult, SolverError>;
}Each backend defines its own Options type. Users get compile-time validation and LSP autocomplete on the options that actually apply to that backend.
§SolverResult
termination records why the solve stopped, while primal_status records whether a usable
point came back.
| Field | Type | Description |
|---|---|---|
termination | TerminationStatus | Why the solve stopped |
primal_status | PrimalStatus | Whether a usable primal point is available |
solutions | Vec<SolutionPoint> | Primal points, best first (empty if no solution) |
dual | FxHashMap<ConstraintId, f64> | Constraint duals at the best point |
reduced_costs | FxHashMap<VarId, f64> | Variable reduced costs at the best point |
best_bound | Option<f64> | Dual/relaxation bound (branch-and-bound backends) |
gap | Option<f64> | Relative optimality gap, when reported |
solve_time | Duration | Wall time around the solve call |
iterations | u64 | Simplex iteration count (if reported) |
raw_log | Option<String> | Solver stdout/stderr |
Each SolutionPoint holds the primal variable values (FxHashMap<VarId, f64>) and that point’s objective (Option<f64>). Index 0 is the best/incumbent. Backends with solution pools return the extra points after it.
§Shared preparation and reconstruction
Create a fresh prepare::LoweringContext per model build, and pass it through
classification and emission. It freezes parameter values and exposes the original
model entities. PreparedExpressions can be shared with parallel writers.
Direct affine nodes borrow their coefficients. One-use streaming consumers can
explicitly bypass reuse admission. Before cache initialization, first-use compound
expressions return owned terms without locking or allocating shared storage.
Once initialized, cached entries are checked independently of reuse admission;
a small bounded history recognizes interleaved roots.
require_linear, require_linear_once, and require_quadratic take a lazy location closure, e.g.
|| format!("constraint {:?}", row.name), which runs only on failure.
Quadratic extraction returns Extracted<QuadraticTerms> (owned or shared),
which dereferences to the original coefficient representation.
Adapters own capability checks, native row/column layouts, reformulation choices,
native status decoding, and evidence that points, duals and global bounds exist.
reconstruct shares coordinate restoration and result cleanup. normalize_result
removes incomplete/nonfinite points and clears uncertified multipliers. Discarding
the incumbent also clears its native gap, which does not describe a surviving
pool point.
§Result accessors
result.objective() // Option<f64>, best solution's objective
result.value_of(expr) // Result<Option<f64>, ModelMismatchError>
result.value(var_id) // Option<f64>, primal value by VarId (best solution)
result.dual_of(handle) // Result<Option<f64>, ModelMismatchError>
result.best() // Option<&SolutionPoint>, same as .solution(0)
result.solution(i) // Option<&SolutionPoint>, i-th pooled point
result.result_count() // usize, number of returned points
result.has_solution() // true when a usable primal point is available
result.report(&model) // Result<ModelReport, ModelMismatchError>
// Indexed variables
result.value_of_idx(&flow, "nyc") // Result<Option<f64>, ModelMismatchError>
result.values_of(&flow)? // Iterator<(&IndexKey, f64)>
result.values_of(&flow)?.filter(|(_, v)| *v != 0.0) // nonzero only§TerminationStatus
Why the solve stopped (complementary to whether a point was returned).
| Variant | Meaning |
|---|---|
Optimal | Proven globally optimal |
LocallyOptimal | A local optimum |
Infeasible | No feasible solution exists |
Unbounded | Objective is unbounded |
InfeasibleOrUnbounded | Infeasible or unbounded; solver could not tell which |
IterationLimit | Stopped at an iteration limit |
TimeLimit | Stopped at a time limit |
NodeLimit | Stopped at a branch-and-bound node limit |
Interrupted | Stopped early (user limit/interrupt, sub-optimal stop) |
NumericError | Solver reported numerical difficulties |
NotSolved | Default, solve not yet called |
Other(String) | Backend-specific status not covered above |
§PrimalStatus
| Variant | Meaning |
|---|---|
NoSolution | No primal point is available |
FeasiblePoint | A feasible point is available, not proven optimal |
OptimalPoint | A proven-optimal point is available |
§SolverError
| Variant | Cause |
|---|---|
UnsupportedKind(ModelKind) | Backend does not support this model kind |
NoObjective | Model has no objective set |
Nonlinear | Backend cannot handle nonlinear expressions |
Backend(String) | Backend-reported error (e.g. license failure, bad option) |
Core(Error) | Error from oximo-core |
§Universal options
All backend options structs embed UniversalOptions and implement HasUniversal, which enables the UniversalOptionsExt blanket impl:
use oximo_solver::UniversalOptionsExt;
use std::time::Duration;
let opts = MyBackendOptions::default()
.time_limit(Duration::from_secs(120))
.threads(4)
.verbose(true);| Method | Field | Type |
|---|---|---|
.time_limit(Duration) | time_limit | Option<Duration> |
.threads(u32) | threads | Option<u32> |
.verbose(bool) | verbose | Option<bool> |
§Implementing HasUniversal for a new backend
use oximo_solver::{HasUniversal, UniversalOptions};
#[derive(Default)]
pub struct MyOptions {
universal: UniversalOptions,
// backend-specific fields ...
}
impl HasUniversal for MyOptions {
fn universal(&self) -> &UniversalOptions { &self.universal }
fn universal_mut(&mut self) -> &mut UniversalOptions { &mut self.universal }
}§Writing a new backend
Mirror the layout of an existing backend crate (oximo-highs, oximo-gurobi, oximo-gams):
lib.rs: public struct +impl Solver.supports()declares whichModelKinds are handled.solve()delegates totranslate::solve.options.rs: convertsMyOptionsinto the backend’s native option calls.translate.rs:Model-> backend conversion and result extraction.
Add an optional dep + feature in oximo/Cargo.toml and re-export the type under oximo::solvers.
§License
MIT OR Apache-2.0
Re-exports§
pub use incremental::Snapshot;pub use incremental::snapshot;pub use infeasibility::Iis;pub use infeasibility::IisReport;pub use infeasibility::InfeasibilityDiagnosis;pub use infeasibility::VarBoundKind;pub use infeasibility::is_infeasible;pub use options::HasUniversal;pub use options::UniversalOptions;pub use options::UniversalOptionsExt;pub use persistent::PersistentSolver;pub use result::ConstraintEvaluation;pub use result::DualStatus;pub use result::ModelReport;pub use result::SocEvaluation;pub use result::SolutionPoint;pub use result::SolverResult;pub use solver::Solver;pub use status::PrimalStatus;pub use status::SolverError;pub use status::TerminationStatus;
Modules§
- incremental
- infeasibility
- options
- persistent
- prepare
- Solver-independent model views.
- reconstruct
- Reversible coordinate changes and the common result contract.
- result
- solver
- status