Skip to main content

Crate oximo_solver

Crate oximo_solver 

Source
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.4"
oximo-core   = "0.4"

§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.

FieldTypeDescription
terminationTerminationStatusWhy the solve stopped
primal_statusPrimalStatusWhether a usable primal point is available
solutionsVec<SolutionPoint>Primal points, best first (empty if no solution)
dualFxHashMap<ConstraintId, f64>Constraint duals at the best point
reduced_costsFxHashMap<VarId, f64>Variable reduced costs at the best point
best_boundOption<f64>Dual/relaxation bound (branch-and-bound backends)
gapOption<f64>Relative optimality gap, when reported
solve_timeDurationWall time around the solve call
iterationsu64Simplex iteration count (if reported)
raw_logOption<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.

§Accessors

result.objective()    // Option<f64>, best solution's objective
result.value_of(expr) // Option<f64>, primal value for a Var expr (best solution)
result.value(var_id)  // Option<f64>, primal value by VarId (best solution)
result.dual_of(c_id)  // Option<f64>, dual for a constraint
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) // Display: model-aware summary (status, vars, duals)

// Indexed variables
result.value_of_idx(&flow, "nyc")                  // Option<f64>, value at a specific index
result.values_of(&flow)                            // Iterator<(&IndexKey, f64)>, all entries with a primal value
result.values_of(&flow).filter(|(_, v)| *v != 0.0) // nonzero only (sparse solutions)

§TerminationStatus

Why the solve stopped (complementary to whether a point was returned).

VariantMeaning
OptimalProven globally optimal
LocallyOptimalA local optimum
InfeasibleNo feasible solution exists
UnboundedObjective is unbounded
InfeasibleOrUnboundedInfeasible or unbounded; solver could not tell which
IterationLimitStopped at an iteration limit
TimeLimitStopped at a time limit
NodeLimitStopped at a branch-and-bound node limit
InterruptedStopped early (user limit/interrupt, sub-optimal stop)
NumericErrorSolver reported numerical difficulties
NotSolvedDefault, solve not yet called
Other(String)Backend-specific status not covered above

§PrimalStatus

VariantMeaning
NoSolutionNo primal point is available
FeasiblePointA feasible point is available, not proven optimal
OptimalPointA proven-optimal point is available

§SolverError

VariantCause
UnsupportedKind(ModelKind)Backend does not support this model kind
NoObjectiveModel has no objective set
NonlinearBackend 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);
MethodFieldType
.time_limit(Duration)time_limitOption<Duration>
.threads(u32)threadsOption<u32>
.verbose(bool)verboseOption<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):

  1. lib.rs: public struct + impl Solver. supports() declares which ModelKinds are handled. solve() delegates to translate::solve.
  2. options.rs: converts MyOptions into the backend’s native option calls.
  3. 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 options::HasUniversal;
pub use options::UniversalOptions;
pub use options::UniversalOptionsExt;
pub use persistent::PersistentSolver;
pub use result::ModelReport;
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
options
persistent
result
solver
status