Skip to main content

pmcore/bestdose/
mod.rs

1//! # BestDose: dose forecasting and optimization
2//!
3//! BestDose finds dosing regimens that hit target drug concentrations or AUC
4//! values for a given distribution over model parameters.
5//!
6//! The distribution is supplied by the caller as support points
7//! ([`Theta`](crate::estimation::nonparametric::Theta)) and probability
8//! [`Weights`](crate::estimation::nonparametric::Weights). It typically comes from a
9//! population fit, optionally
10//! updated to a patient-specific posterior with the NCNPAG or NPMAP algorithms.
11//!
12//! # Flow
13//!
14//! ```rust,no_run,ignore
15//! use pmcore::bestdose::{BestDoseProblem, BestDoseOptions, DoseRange, Target};
16//! use pmcore::prelude::*;
17//!
18//! # fn example(eq: pharmsol::prelude::ODE, pop_data: pharmsol::prelude::Data,
19//! #            prior_theta: pmcore::estimation::nonparametric::Theta,
20//! #            ems: pharmsol::prelude::AssayErrorModels,
21//! #            past_data: Option<pharmsol::prelude::Subject>,
22//! #            target: pharmsol::prelude::Subject) -> anyhow::Result<()> {
23//! // 1. Fit the population model with any algorithm.
24//! let fit = EstimationProblem::nonparametric(eq.clone(), pop_data, prior_theta, ems.clone())?
25//!     .fit_with(NpagConfig::default())?;
26//!
27//! // 2. Choose the distribution: patient-specific posterior (past data) or population.
28//! let (theta, weights) = match past_data {
29//!     Some(past) => {
30//!         let post = EstimationProblem::nonparametric(
31//!                 eq.clone(), data::Data::new(vec![past]), fit.get_theta().clone(), ems.clone())?
32//!             .fit_with(NcnpagConfig::default())?; // or NpmapConfig::default()
33//!         (post.get_theta().clone(), post.weights().clone())
34//!     }
35//!     None => (fit.get_theta().clone(), fit.weights().clone()),
36//! };
37//!
38//! // 3. Optimize doses.
39//! let problem = BestDoseProblem::new(eq, theta, weights)?;
40//! let result = problem.optimize(
41//!     target,
42//!     Target::Concentration,
43//!     DoseRange::new(0.0, 300.0),
44//!     0.5, // bias λ: 0 = personalized, 1 = population-typical
45//!     BestDoseOptions::default(),
46//! )?;
47//!
48//! let optimal_subject = result.subject();
49//! let cost = result.cost();
50//! # Ok(())
51//! # }
52//! ```
53//!
54//! # Cost function
55//!
56//! `optimize` minimizes, over the optimizable doses, a hybrid objective computed
57//! from the single distribution `(theta, weights)`:
58//!
59//! ```text
60//! Cost = (1-λ) × Variance + λ × Bias²
61//! Variance = Σᵢ wᵢ Σⱼ (targetⱼ − pred[i,j])²      (expected squared error)
62//! Bias²    = Σⱼ (targetⱼ − Σᵢ wᵢ pred[i,j])²       (error of the weighted mean)
63//! ```
64
65pub mod cost;
66mod optimization;
67pub mod predictions;
68mod types;
69
70pub use types::{Achievement, BestDoseOptions, BestDoseProblem, BestDoseResult, DoseRange, Target};