Quickstart

Build and solve your first LP with oximo in under a minute.

This walkthrough builds a small linear program end-to-end: variables, constraints, objective, solve, read result. By the end you'll have run Highs on a real model and inspected the solution.

🔗The problem

\[ \begin{aligned} \max \quad & 3x + 4y \\ \text{s.t.} \quad & x + 2y \le 14 \\ & 3x \ge y \\ & x \le y + 2 \\ & x \ge 0 \\ & 0 \le y \le 4 \end{aligned} \]

🔗The full program

use oximo::prelude::*;
use oximo::solvers::Highs;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let m = Model::new("transport");

    variable!(m, x >= 0.0);
    variable!(m, 0.0 <= y <= 4.0);

    constraint!(m, c1, x + 2.0 * y <= 14.0);
    constraint!(m, c2, 3.0 * x >= y);
    constraint!(m, c3, x <= y + 2.0);
    objective!(m, Max, 3.0 * x + 4.0 * y);

    let result = Highs.solve(&m, &HighsOptions::default())?;
    println!("obj = {:?}", result.objective()); // Some(34.0)
    println!("x   = {:?}", result.value_of(x)); // Some(6.0)
    println!("y   = {:?}", result.value_of(y)); // Some(4.0)
    Ok(())
}

Run it with cargo run and you should see the optimum: obj = Some(34.0).

🔗Step by step

🔗1. Create a model

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

Model is the container that holds variables, constraints, and an objective. The name is used when exporting or printing it and as a label in solver logs.

🔗2. Declare variables

variable!(m, x >= 0.0);
variable!(m, 0.0 <= y <= 4.0);

The variable! macro registers a variable on m and binds a Rust binding of the same name, so you can use x directly in later expressions. Write the bounds the way you'd write them on paper: x >= 0.0, 0.0 <= y <= 4.0, or just variable!(m, z) for a free variable. See Modeling for integer, binary, and indexed variables.

🔗3. Add constraints

constraint!(m, c1, x + 2.0 * y <= 14.0);

constraint! takes the model, a name, and a relation written with <=, >=, or ==. Expressions use standard Rust operators.

🔗4. Set an objective

objective!(m, Max, 3.0 * x + 4.0 * y);

objective! takes a sense (Max or Min, also Maximize/max, Minimize/min) and the expression. A Model has one objective.

If your problem is a feasibility problem, you can use Feas/Feasibility as the sense.

🔗5. Solve

let result = Highs.solve(&m, &HighsOptions::default())?;

Each backend implements the Solver trait, so switching engines is a one-line change. Highs.solve(&model, &options) returns a SolverResult.

🔗6. Read the result

println!("obj = {:?}", result.objective());
println!("x   = {:?}", result.value_of(x));

SolverResult::objective() returns Option<f64>, None when the model has no objective or no solution was found. value_of(expr) gives a variable's value in the best solution.

For a quick human-readable dump of the whole solution, print the built-in report:

print!("{}", result.report(&m));

For status checking, duals, and reduced costs, see Results.

For more examples, see the examples directory.

🔗Next steps

  • Modeling: variables, index sets, indexed variables, summation, rule-style constraints
  • Solvers: backend options and result inspection
  • Printing & Debugging: print a model as algebra when it doesn't say what you expected
  • I/O: export models to MPS, LP, or NL