#[cfg(feature = "exa")]
fn main() {
use pharmsol::prelude::*;
use pharmsol::{build::temp_path, exa, Analytical, ODE};
use std::path::PathBuf;
let subject = Subject::builder("1")
.infusion(0.0, 500.0, 0, 0.5)
.observation(0.5, 1.645776, 0)
.observation(1.0, 1.216442, 0)
.observation(2.0, 0.4622729, 0)
.observation(3.0, 0.1697458, 0)
.observation(4.0, 0.06382178, 0)
.observation(6.0, 0.009099384, 0)
.observation(8.0, 0.001017932, 0)
.build();
let params = vec![1.2, 50.0];
let static_ode = equation::ODE::new(
|x, p, _t, dx, _bolus, rateiv, _cov| {
fetch_params!(p, ke, _v);
dx[0] = -ke * x[0] + rateiv[0];
},
|_p, _t, _cov| lag! {},
|_p, _t, _cov| fa! {},
|_p, _t, _cov, _x| {},
|x, p, _t, _cov, y| {
fetch_params!(p, _ke, v);
y[0] = x[0] / v;
},
)
.with_nstates(1)
.with_nout(1);
let test_dir = std::env::current_dir().expect("Failed to get current directory");
let ode_output_path = test_dir.join("dynamic_ode_model.pkm");
println!("Compiling ODE model...");
let ode_compiled_path = exa::build::compile::<ODE>(
r#"
equation::ODE::new(
|x, p, _t, dx, _bolus, rateiv, _cov| {
fetch_params!(p, ke, _v);
dx[0] = -ke * x[0] + rateiv[0];
},
|_p, _t, _cov| lag! {},
|_p, _t, _cov| fa! {},
|_p, _t, _cov, _x| {},
|x, p, _t, _cov, y| {
fetch_params!(p, _ke, v);
y[0] = x[0] / v;
},
)
.with_nstates(1)
.with_nout(1)
"#
.to_string(),
Some(ode_output_path),
vec!["ke".to_string(), "v".to_string()],
temp_path(),
|key, msg| println!(" [{key}] {msg}"),
)
.expect("Failed to compile ODE model");
println!("ODE model compiled to: {ode_compiled_path}");
let ode_path = PathBuf::from(&ode_compiled_path);
let (_lib_ode, (dynamic_ode, _meta)) = unsafe { exa::load::load::<ODE>(ode_path.clone()) };
let analytical_output_path = test_dir.join("dynamic_analytical_model.pkm");
println!("\nCompiling Analytical model...");
let analytical_compiled_path = exa::build::compile::<Analytical>(
r#"
equation::Analytical::new(
one_compartment,
|_p, _t, _cov| {},
|_p, _t, _cov| lag! {},
|_p, _t, _cov| fa! {},
|_p, _t, _cov, _x| {},
|x, p, _t, _cov, y| {
fetch_params!(p, _ke, v);
y[0] = x[0] / v;
},
)
.with_nstates(1)
.with_nout(1)
"#
.to_string(),
Some(analytical_output_path),
vec!["ke".to_string(), "v".to_string()],
temp_path(),
|key, msg| println!(" [{key}] {msg}"),
)
.expect("Failed to compile Analytical model");
println!("Analytical model compiled to: {analytical_compiled_path}");
let analytical_path = PathBuf::from(&analytical_compiled_path);
let (_lib_analytical, (dynamic_analytical, _meta)) =
unsafe { exa::load::load::<Analytical>(analytical_path.clone()) };
println!("\n{}", "=".repeat(60));
println!("Comparing predictions (ke={}, v={})", params[0], params[1]);
println!("{}", "=".repeat(60));
let static_ode_preds = static_ode
.estimate_predictions(&subject, ¶ms)
.expect("Static ODE prediction failed");
let dynamic_ode_preds = dynamic_ode
.estimate_predictions(&subject, ¶ms)
.expect("Dynamic ODE prediction failed");
let dynamic_analytical_preds = dynamic_analytical
.estimate_predictions(&subject, ¶ms)
.expect("Dynamic Analytical prediction failed");
let static_flat = static_ode_preds.flat_predictions();
let dynamic_ode_flat = dynamic_ode_preds.flat_predictions();
let dynamic_analytical_flat = dynamic_analytical_preds.flat_predictions();
println!(
"\n{:<12} {:>15} {:>15} {:>15}",
"Time", "Static ODE", "Dynamic ODE", "Analytical"
);
println!("{}", "-".repeat(60));
let times = [0.5, 1.0, 2.0, 3.0, 4.0, 6.0, 8.0];
for (i, &time) in times.iter().enumerate() {
println!(
"{:<12.1} {:>15.6} {:>15.6} {:>15.6}",
time, static_flat[i], dynamic_ode_flat[i], dynamic_analytical_flat[i]
);
}
println!("\n{}", "=".repeat(60));
println!("Verification:");
let ode_match = static_flat
.iter()
.zip(dynamic_ode_flat.iter())
.all(|(a, b)| (a - b).abs() < 1e-10);
println!(
" Static ODE vs Dynamic ODE: {}",
if ode_match {
"✓ MATCH"
} else {
"✗ MISMATCH"
}
);
let analytical_close = static_flat
.iter()
.zip(dynamic_analytical_flat.iter())
.all(|(a, b)| (a - b).abs() < 1e-3);
println!(
" Static ODE vs Analytical: {}",
if analytical_close {
"✓ CLOSE (within 1e-3)"
} else {
"✗ DIFFERS"
}
);
std::fs::remove_file(&ode_path).ok();
std::fs::remove_file(&analytical_path).ok();
println!("\nCleaned up temporary model files.");
}
#[cfg(not(feature = "exa"))]
fn main() {
eprintln!("This example requires the 'exa' feature.");
eprintln!("Run with: cargo run --example exa --features exa");
std::process::exit(1);
}