1#[cfg(any(feature = "gams", feature = "highs"))]
37use oximo::prelude::*;
38
39#[cfg(feature = "gams")]
40use oximo::gams::{GamsCplexOptions, GamsSolverConfig};
41#[cfg(feature = "gams")]
42use oximo::solvers::Gams;
43
44#[cfg(all(feature = "highs", not(feature = "gams")))]
45use oximo::solvers::Highs;
46
47#[cfg(any(feature = "gams", feature = "highs"))]
48#[expect(clippy::cast_precision_loss)]
49fn main() -> Result<(), Box<dyn std::error::Error>> {
50 const CHEMICALS: [&str; 34] = [
52 "y01", "y02", "y03", "y04", "y05", "y06", "y07", "y08", "y09", "y10", "y11", "y12", "y13",
53 "y14", "y15", "y16", "y17", "y18", "y19", "y20", "y21", "y22", "y23", "y24", "y25", "y26",
54 "y27", "y28", "y29", "y30", "y31", "y32", "y33", "y34",
55 ];
56
57 let logicc: &[(&str, usize, &[usize])] = &[
60 ("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]), ];
83
84 let available: &[usize] = &[1, 2, 4, 9, 11, 12, 16, 21, 24, 25, 27, 30, 32];
86 let unavailable: &[usize] = &[15, 18];
88
89 let m = Model::new("reaction_path");
90 let chemicals = Set::strings(CHEMICALS);
91
92 variable!(m, y[v in chemicals], Bin);
93 for &i in available {
95 m.fix(y[CHEMICALS[i]], 1.0);
96 }
97 for &i in unavailable {
98 m.fix(y[CHEMICALS[i]], 0.0);
99 }
100
101 for &(_rx, prod, reactants) in logicc {
104 let n = reactants.len() as f64;
105 constraint!(m, y[CHEMICALS[prod]] - sum!(y[CHEMICALS[vv]] for vv in reactants) >= 1.0 - n);
106 }
107
108 objective!(m, Min, y["y06"]); #[cfg(feature = "gams")]
111 let result = {
112 let opts = GamsOptions::default()
113 .time_limit(std::time::Duration::from_secs(60))
114 .solver(GamsSolverConfig::Cplex(GamsCplexOptions::default()))
115 .verbose(true);
116 let mut solver = Gams::new();
117 solver.solve(&m, &opts)?
118 };
119
120 #[cfg(all(feature = "highs", not(feature = "gams")))]
121 let result = Highs.solve(&m, &HighsOptions::default().verbose(true))?;
122
123 println!("Status : {:?}", result.termination);
124 if let Some(obj) = result.objective() {
125 println!(
126 "Acetone (y06, ch3coch3): {}",
127 if (obj - 1.0).abs() < 1e-6 { "Synthesizable" } else { "Not synthesizable" }
128 );
129 }
130
131 let synthesizable: Vec<&str> = CHEMICALS
132 .iter()
133 .copied()
134 .filter(|name| (result.value_of(y[*name]).unwrap_or(0.0) - 1.0).abs() < 1e-6)
135 .collect();
136 if !synthesizable.is_empty() {
137 println!("Synthesizable chemicals: {}", synthesizable.join(", "));
138 }
139
140 Ok(())
141}
142
143#[cfg(not(any(feature = "gams", feature = "highs")))]
144fn main() {
145 println!("Enable at least one solver feature:");
146 println!(" cargo run --example reaction_path # HiGHS (default)");
147 println!(" cargo run --example reaction_path --features gams # GAMS/CPLEX");
148}