Skip to content
33 changes: 15 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,21 @@ let analytical = analytical! {
params: [ke, v],
states: [central],
outputs: [cp],
routes: {
routes: [
infusion(iv) -> central,
},
],
structure: one_compartment,
out: |x, _p, _t, _cov, y| {
y[cp] = x[central] / v;
},
};

let iv = analytical.route_index("iv").unwrap();
let cp = analytical.output_index("cp").unwrap();

let subject = Subject::builder("patient_001")
.infusion(0.0, 500.0, iv, 0.5)
.missing_observation(0.5, cp)
.missing_observation(1.0, cp)
.missing_observation(2.0, cp)
.missing_observation(4.0, cp)
.infusion(0.0, 500.0, "iv", 0.5)
.missing_observation(0.5, "cp")
.missing_observation(1.0, "cp")
.missing_observation(2.0, "cp")
.missing_observation(4.0, "cp")
.build();

let predictions = analytical
Expand All @@ -64,9 +61,9 @@ let ode = ode! {
params: [ke, v],
states: [central],
outputs: [cp],
routes: {
routes: [
infusion(iv) -> central,
},
],
diffeq: |x, _p, _t, dx, _cov| {
dx[central] = -ke * x[central];
},
Expand Down Expand Up @@ -121,12 +118,12 @@ use pharmsol::prelude::*;
use pharmsol::nca::NCAOptions;

let subject = Subject::builder("patient_001")
.bolus(0.0, 100.0, 0) // 100 mg oral dose
.observation(0.5, 5.0, 0)
.observation(1.0, 10.0, 0)
.observation(2.0, 8.0, 0)
.observation(4.0, 4.0, 0)
.observation(8.0, 2.0, 0)
.bolus(0.0, 100.0, "oral") // 100 mg oral dose
.observation(0.5, 5.0, "cp")
.observation(1.0, 10.0, "cp")
.observation(2.0, 8.0, "cp")
.observation(4.0, 4.0, "cp")
.observation(8.0, 2.0, "cp")
.build();

let result = subject.nca(&NCAOptions::default()).expect("NCA failed");
Expand Down
17 changes: 7 additions & 10 deletions examples/analytical_readme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,21 @@ fn main() -> Result<(), pharmsol::PharmsolError> {
params: [ke, v],
states: [central],
outputs: [cp],
routes: {
routes: [
infusion(iv) -> central,
},
],
structure: one_compartment,
out: |x, _p, _t, _cov, y| {
y[cp] = x[central] / v;
},
};

let iv = analytical.route_index("iv").expect("iv route exists");
let cp = analytical.output_index("cp").expect("cp output exists");

let subject = Subject::builder("analytical_readme")
.infusion(0.0, 500.0, iv, 0.5)
.missing_observation(0.5, cp)
.missing_observation(1.0, cp)
.missing_observation(2.0, cp)
.missing_observation(4.0, cp)
.infusion(0.0, 500.0, "iv", 0.5)
.missing_observation(0.5, "cp")
.missing_observation(1.0, "cp")
.missing_observation(2.0, "cp")
.missing_observation(4.0, "cp")
.build();

let predictions = analytical.estimate_predictions(&subject, &[1.022, 194.0])?;
Expand Down
32 changes: 16 additions & 16 deletions examples/analytical_vs_ode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,9 @@ fn one_cmt_iv(params: &[f64]) {
params: [ke, v],
states: [central],
outputs: [cp],
routes: {
routes: [
infusion(iv) -> central,
},
],
structure: one_compartment,
out: |x, _p, _t, _cov, y| {
y[cp] = x[central] / v;
Expand All @@ -86,9 +86,9 @@ fn one_cmt_iv(params: &[f64]) {
params: [ke, v],
states: [central],
outputs: [cp],
routes: {
routes: [
infusion(iv) -> central,
},
],
diffeq: |x, _p, _t, dx, _cov| {
dx[central] = -ke * x[central];
},
Expand All @@ -114,9 +114,9 @@ fn one_cmt_oral(params: &[f64]) {
params: [ka, ke, v],
states: [gut, central],
outputs: [cp],
routes: {
routes: [
bolus(oral) -> gut,
},
],
structure: one_compartment_with_absorption,
out: |x, _p, _t, _cov, y| {
y[cp] = x[central] / v;
Expand All @@ -128,9 +128,9 @@ fn one_cmt_oral(params: &[f64]) {
params: [ka, ke, v],
states: [gut, central],
outputs: [cp],
routes: {
routes: [
bolus(oral) -> gut,
},
],
diffeq: |x, _p, _t, dx, _cov| {
dx[gut] = -ka * x[gut];
dx[central] = ka * x[gut] - ke * x[central];
Expand All @@ -157,9 +157,9 @@ fn two_cmt_iv(params: &[f64]) {
params: [ke, k12, k21, v],
states: [central, peripheral],
outputs: [cp],
routes: {
routes: [
infusion(iv) -> central,
},
],
structure: two_compartments,
out: |x, _p, _t, _cov, y| {
y[cp] = x[central] / v;
Expand All @@ -171,9 +171,9 @@ fn two_cmt_iv(params: &[f64]) {
params: [ke, k12, k21, v],
states: [central, peripheral],
outputs: [cp],
routes: {
routes: [
infusion(iv) -> central,
},
],
diffeq: |x, _p, _t, dx, _cov| {
dx[central] = -ke * x[central] - k12 * x[central] + k21 * x[peripheral];
dx[peripheral] = k12 * x[central] - k21 * x[peripheral];
Expand All @@ -200,9 +200,9 @@ fn two_cmt_oral(params: &[f64]) {
params: [ka, ke, k12, k21, v],
states: [gut, central, peripheral],
outputs: [cp],
routes: {
routes: [
bolus(oral) -> gut,
},
],
structure: two_compartments_with_absorption,
out: |x, _p, _t, _cov, y| {
y[cp] = x[central] / v;
Expand All @@ -214,9 +214,9 @@ fn two_cmt_oral(params: &[f64]) {
params: [ka, ke, k12, k21, v],
states: [gut, central, peripheral],
outputs: [cp],
routes: {
routes: [
bolus(oral) -> gut,
},
],
diffeq: |x, _p, _t, dx, _cov| {
dx[gut] = -ka * x[gut];
dx[central] = ka * x[gut] - ke * x[central] - k12 * x[central] + k21 * x[peripheral];
Expand Down
40 changes: 16 additions & 24 deletions examples/compare_solvers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ fn two_cpt(solver: OdeSolver) -> equation::ODE {
params: [ke, kcp, kpc, v],
states: [central, peripheral],
outputs: [cp],
routes: {
routes: [
bolus(load) -> central,
infusion(iv) -> central,
},
],
diffeq: |x, _p, _t, dx, _cov| {
dx[central] = -ke * x[central] - kcp * x[central] + kpc * x[peripheral];
dx[peripheral] = kcp * x[central] - kpc * x[peripheral];
Expand All @@ -48,32 +48,24 @@ fn main() {
let trbdf2 = two_cpt(OdeSolver::Sdirk(SdirkTableau::TrBdf2));
let esdirk34 = two_cpt(OdeSolver::Sdirk(SdirkTableau::Esdirk34));

// Both declarations resolve to the same shared input channel, so subject
// Both declarations resolve to the same shared input, so subject
// authoring still uses one numeric index for the loading bolus and the
// maintenance infusion.
let load = bdf.route_index("load").expect("load route exists");
let iv = bdf.route_index("iv").expect("iv route exists");
let cp = bdf.output_index("cp").expect("cp output exists");

assert_eq!(
load, iv,
"mixed IV declarations should share one numeric channel"
);

let subject = Subject::builder("id1")
.bolus(0.0, 100.0, iv)
.infusion(12.0, 200.0, iv, 2.0)
.missing_observation(0.5, cp)
.missing_observation(1.0, cp)
.missing_observation(2.0, cp)
.missing_observation(4.0, cp)
.missing_observation(8.0, cp)
.missing_observation(12.0, cp)
.missing_observation(12.5, cp)
.missing_observation(13.0, cp)
.missing_observation(14.0, cp)
.missing_observation(16.0, cp)
.missing_observation(24.0, cp)
.bolus(0.0, 100.0, "load")
.infusion(12.0, 200.0, "iv", 2.0)
.missing_observation(0.5, "cp")
.missing_observation(1.0, "cp")
.missing_observation(2.0, "cp")
.missing_observation(4.0, "cp")
.missing_observation(8.0, "cp")
.missing_observation(12.0, "cp")
.missing_observation(12.5, "cp")
.missing_observation(13.0, "cp")
.missing_observation(14.0, "cp")
.missing_observation(16.0, "cp")
.missing_observation(24.0, "cp")
.build();

let spp = vec![0.1, 0.05, 0.03, 50.0]; // ke, kcp, kpc, V
Expand Down
4 changes: 2 additions & 2 deletions examples/covariates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ fn main() {
covariates: [creatinine, age],
states: [gut, central],
outputs: [cp],
routes: {
routes: [
bolus(oral) -> gut,
},
],
diffeq: |x, _t, dx| {
let scaled_ke = ke * (creatinine / 75.0).powf(0.75) * (age / 25.0).powf(0.5);

Expand Down
24 changes: 8 additions & 16 deletions examples/dsl_runtime_jit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,16 @@ out(cp) = central / v
on_compile_event,
)?;

// 2. Resolve the route and output indices declared by the model.
let iv = model
.route_index("iv")
.ok_or_else(|| io::Error::other("missing iv route"))?;
let cp = model
.output_index("cp")
.ok_or_else(|| io::Error::other("missing cp output"))?;

// 3. Define the subject data.
let subject = Subject::builder("bimodal_ke")
.infusion(0.0, 500.0, iv, 0.5)
.missing_observation(0.5, cp)
.missing_observation(1.0, cp)
.missing_observation(2.0, cp)
.missing_observation(3.0, cp)
.missing_observation(4.0, cp)
.missing_observation(6.0, cp)
.missing_observation(8.0, cp)
.infusion(0.0, 500.0, "iv", 0.5)
.missing_observation(0.5, "cp")
.missing_observation(1.0, "cp")
.missing_observation(2.0, "cp")
.missing_observation(3.0, "cp")
.missing_observation(4.0, "cp")
.missing_observation(6.0, "cp")
.missing_observation(8.0, "cp")
.build();

// 4. Estimate predictions for one support point.
Expand Down
19 changes: 11 additions & 8 deletions examples/macro_vs_handwritten_one_cpt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ fn macro_model() -> equation::ODE {
params: [ke, v],
states: [central],
outputs: [cp],
routes: {
routes: [
infusion(iv) -> central,
},
],
diffeq: |x, _p, _t, dx, _cov| {
dx[central] = -ke * x[central];
},
Expand All @@ -26,6 +26,9 @@ fn macro_model() -> equation::ODE {

fn handwritten_model() -> equation::ODE {
equation::ODE::new(
// Handwritten closures stay on dense internal slots.
// Public labels like `iv` and `cp` live in attached metadata, not in
// the low-level `rateiv[]` / `y[]` buffers.
Comment on lines +29 to +31
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Too verbose / unclear

|x, p, _t, dx, _bolus, rateiv, _cov| {
fetch_params!(p, ke, _v);
dx[0] = rateiv[0] - ke * x[0];
Expand Down Expand Up @@ -75,12 +78,12 @@ fn main() -> Result<(), pharmsol::PharmsolError> {
assert_eq!(handwritten_ode.output_index("cp"), Some(cp));

let subject = Subject::builder("macro-vs-handwritten-one-cpt")
.infusion(0.0, 500.0, iv, 0.5)
.missing_observation(0.5, cp)
.missing_observation(1.0, cp)
.missing_observation(2.0, cp)
.missing_observation(4.0, cp)
.missing_observation(8.0, cp)
.infusion(0.0, 500.0, "iv", 0.5)
.missing_observation(0.5, "cp")
.missing_observation(1.0, "cp")
.missing_observation(2.0, "cp")
.missing_observation(4.0, "cp")
.missing_observation(8.0, "cp")
.build();

let params = [1.022, 194.0];
Expand Down
Loading
Loading