#![allow(clippy::cast_precision_loss)]
#[cfg(any(feature = "gams", feature = "highs"))]
use oximo::prelude::*;
#[cfg(feature = "gams")]
use oximo::gams::{GamsCplexOptions, GamsSolverConfig};
#[cfg(feature = "gams")]
use oximo::solvers::Gams;
#[cfg(all(feature = "highs", not(feature = "gams")))]
use oximo::solvers::Highs;
#[cfg(any(feature = "gams", feature = "highs"))]
fn main() -> Result<(), Box<dyn std::error::Error>> {
const CHEMICALS: [&str; 34] = [
"y01", "y02", "y03", "y04", "y05", "y06", "y07", "y08", "y09", "y10", "y11", "y12", "y13",
"y14", "y15", "y16", "y17", "y18", "y19", "y20", "y21", "y22", "y23", "y24", "y25", "y26",
"y27", "y28", "y29", "y30", "y31", "y32", "y33", "y34",
];
let logicc: &[(&str, usize, &[usize])] = &[
("rxn01", 3, &[0, 1, 2]), ("rxn02", 5, &[3, 4]), ("rxn03", 6, &[3, 4]), ("rxn04", 2, &[3, 4]), ("rxn05", 10, &[7, 8, 9]), ("rxn06", 5, &[10, 11, 12]), ("rxn07", 14, &[13, 8, 9, 4]), ("rxn08", 5, &[14, 15, 16]), ("rxn09", 5, &[17, 18, 11]), ("rxn10", 19, &[17, 18, 11]), ("rxn11", 8, &[20, 21]), ("rxn12", 23, &[8, 22]), ("rxn13", 17, &[23, 16]), ("rxn14", 20, &[24, 25]), ("rxn15", 26, &[24, 25]), ("rxn16", 13, &[2, 27, 28]), ("rxn17", 31, &[29, 30, 11]), ("rxn18", 7, &[29, 30, 11]), ("rxn19", 29, &[24, 32]), ("rxn20", 12, &[24, 32]), ("rxn21", 0, &[33, 2]), ("rxn22", 33, &[13, 27]), ];
let available: &[usize] = &[1, 2, 4, 9, 11, 12, 16, 21, 24, 25, 27, 30, 32];
let unavailable: &[usize] = &[15, 18];
let m = Model::new("reaction_path");
let chemicals = Set::strings(CHEMICALS);
let y = m
.indexed_var("y", &chemicals)
.binary()
.lb_by(
|name: String| {
if available.iter().any(|&i| CHEMICALS[i] == name) { 1.0 } else { 0.0 }
},
)
.ub_by(
|name: String| {
if unavailable.iter().any(|&i| CHEMICALS[i] == name) { 0.0 } else { 1.0 }
},
)
.build();
for &(rx, prod, reactants) in logicc {
let n = reactants.len() as f64;
let reactant_sum = sum_over(reactants, |vv: usize| y[CHEMICALS[vv]]);
m.constraint(format!("leq_{rx}"), (y[CHEMICALS[prod]] - reactant_sum).ge(1.0 - n));
}
m.minimize(y["y06"]);
#[cfg(feature = "gams")]
let result = {
let opts = GamsOptions::default()
.time_limit(std::time::Duration::from_secs(60))
.solver(GamsSolverConfig::Cplex(GamsCplexOptions::default()))
.verbose(true);
let mut solver = Gams::new();
solver.solve(&m, &opts)?
};
#[cfg(all(feature = "highs", not(feature = "gams")))]
let result = Highs.solve(&m, &HighsOptions::default().verbose(true))?;
println!("Status : {:?}", result.status);
if let Some(obj) = result.objective {
println!(
"Acetone (y06, ch3coch3): {}",
if (obj - 1.0).abs() < 1e-6 { "Synthesizable" } else { "Not synthesizable" }
);
}
let synthesizable: Vec<&str> = CHEMICALS
.iter()
.copied()
.filter(|name| (result.value_of(y[*name]).unwrap_or(0.0) - 1.0).abs() < 1e-6)
.collect();
if !synthesizable.is_empty() {
println!("Synthesizable chemicals: {}", synthesizable.join(", "));
}
Ok(())
}
#[cfg(not(any(feature = "gams", feature = "highs")))]
fn main() {
println!("Enable at least one solver feature:");
println!(" cargo run --example reaction_path # HiGHS (default)");
println!(" cargo run --example reaction_path --features gams # GAMS/CPLEX");
}