#![allow(clippy::many_single_char_names)]
#[cfg(any(feature = "gurobi", feature = "highs"))]
use oximo::prelude::*;
#[cfg(feature = "gurobi")]
use oximo::{GurobiOptions, solvers::Gurobi};
#[cfg(all(feature = "highs", not(feature = "gurobi")))]
use oximo::{HighsOptions, solvers::Highs};
#[cfg(any(feature = "gurobi", feature = "highs"))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
const T: usize = 12;
let demand: [f64; T] =
[120.0, 90.0, 80.0, 140.0, 160.0, 200.0, 220.0, 190.0, 150.0, 130.0, 100.0, 170.0];
let prod_cost: [f64; T] = [5.0, 5.0, 5.0, 5.5, 6.0, 6.5, 6.5, 6.0, 5.5, 5.0, 5.0, 5.5];
let setup_cost = 500.0;
let hold_cost = 2.0;
let capacity = 300.0;
let initial_inventory = 50.0;
let safety_stock = 30.0;
let m = Model::new("lot_sizing");
let periods = Set::range(0..T);
variable!(m, 0.0 <= x[t in periods] <= capacity);
variable!(m, h[t in periods] >= 0.0);
variable!(m, s[t in periods], Bin);
constraint!(m, inv_bal0, h[0] - x[0] == initial_inventory - demand[0]);
constraint!(m, inv_bal[t in 1..T], h[t] - h[t - 1] - x[t] == -demand[t]);
constraint!(m, setup[t in periods], x[t] <= capacity * s[t]);
constraint!(m, safety_stock, h[T - 1] >= safety_stock);
objective!(
m,
Min,
sum!(prod_cost[t] * x[t] + setup_cost * s[t] + hold_cost * h[t] for t in periods)
);
#[cfg(feature = "gurobi")]
let result = {
let opts = GurobiOptions::default()
.time_limit(std::time::Duration::from_secs(60))
.mip_gap(1e-4)
.verbose(true);
Gurobi.solve(&m, &opts)?
};
#[cfg(all(feature = "highs", not(feature = "gurobi")))]
let result = {
let opts = HighsOptions::default()
.time_limit(std::time::Duration::from_secs(60))
.mip_gap(1e-4)
.verbose(true);
Highs.solve(&m, &opts)?
};
println!("\nLot-Sizing Result");
println!("Status : {:?}", result.status);
if let Some(obj) = result.objective() {
println!("Total cost: {obj:.2}");
}
println!(
"\n{:<8} {:>10} {:>10} {:>8} {:>12}",
"Period", "Produce", "Inventory", "Active", "Period cost"
);
println!("{}", "-".repeat(55));
let mut total_check = 0.0;
for t in 0..T {
let xt = result.value_of(x[t]).unwrap_or(0.0);
let ht = result.value_of(h[t]).unwrap_or(0.0);
let st = result.value_of(s[t]).unwrap_or(0.0);
let period_cost = prod_cost[t] * xt + setup_cost * st + hold_cost * ht;
total_check += period_cost;
println!(
"{:<8} {:>10.1} {:>10.1} {:>8} {:>12.2}",
t + 1,
xt,
ht,
if (st - 1.0).abs() < 1e-6 { "Yes" } else { "No" },
period_cost
);
}
println!("{}", "-".repeat(55));
println!("{:<8} {:>10} {:>10} {:>8} {:>12.2}", "TOTAL", "", "", "", total_check);
Ok(())
}
#[cfg(not(any(feature = "gurobi", feature = "highs")))]
fn main() {
println!("Enable at least one solver feature:");
println!(" cargo run --example lot_sizing # HiGHS (default)");
println!(" cargo run --example lot_sizing --features gurobi # Gurobi");
}