pkident decides local structural identifiability: whether the unknown parameters of an ODE model are determined, in a neighbourhood of their values, by continuous noise-free observation of the outputs after the known doses. Two independent methods are used and must agree:
A one-compartment model with first-order absorption and unknown
bioavailability F:
m <- pkpd_model(
"d/dt(depot) = -ka*depot
d/dt(central) = ka*depot - CL/V*central
cp = central/V",
outputs = "cp",
doses = bolus_dose("depot", "F*DOSE"),
known = "DOSE"
)
m
#> <pkident_model>
#> States: depot, central
#> Outputs: cp
#> Parameters: CL, F, V, ka
#> Known: DOSE
#> d/dt(depot) = -ka*depot; depot(0) = F*DOSE
#> d/dt(central) = ka*depot - CL*central/V; central(0) = 0
#> cp = central/Vr <- structural_identifiability(m)
r
#> <pkident structural identifiability>
#> Decision is LOCAL (Lie derivatives at the known initial state up to order 8, checked by output sensitivities; 5 random points).
#> Methods agree at 5 of 5 points.
#> CL combination_only
#> F combination_only
#> V combination_only
#> ka identifiable
#> Identifiable combinations:
#> CL/F
#> CL/VCL, F and V are not
identifiable alone; the reported combinations span the identifiable
ones. The familiar V/F is among them:
as_table(r, "combinations")
#> # A tibble: 2 × 2
#> combination parameters
#> <chr> <chr>
#> 1 CL/F CL,F
#> 2 CL/V CL,V
as_table(r, "agreement")
#> # A tibble: 5 × 5
#> point lie_rank sensitivity_rank null_space_distance agree
#> <int> <int> <int> <dbl> <lgl>
#> 1 1 3 3 0 TRUE
#> 2 2 3 3 0 TRUE
#> 3 3 3 3 0.0000000211 TRUE
#> 4 4 3 3 0 TRUE
#> 5 5 3 3 0 TRUEThe package ships ten reference models whose expected results were fixed from primary literature (Janzén et al. 2016, Front Physiol 7:590) or derived analytically before any code was written, plus a variant of A10 with total receptors known.
reference_models()
#> id
#> 1 A1
#> 2 A2
#> 3 A3
#> 4 A4
#> 5 A5
#> 6 A6
#> 7 A7
#> 8 A8
#> 9 A9
#> 10 A10
#> 11 A10_Rtot_known
#> title
#> 1 One-compartment IV bolus
#> 2 One-compartment oral, bioavailability unknown
#> 3 One-compartment oral, bioavailability known
#> 4 Two-compartment IV bolus, central compartment observed
#> 5 One-compartment Michaelis-Menten elimination, IV bolus
#> 6 One-compartment parallel linear and Michaelis-Menten elimination, IV bolus
#> 7 Direct effect, steady-state receptor binding, linear transduction (known Cp)
#> 8 Indirect stimulation of production, steady-state receptor binding (known Cp)
#> 9 Effect compartment, steady-state receptor binding, linear transduction (known Cp)
#> 10 Effect compartment, dynamic receptor binding, linear transduction (known Cp)
#> 11 As A10 with total receptors known (the paper's worked example)
#> source
#> 1 Analytical derivation
#> 2 Janzen et al. 2016 (citing Cheung et al. 2013); analytical derivation
#> 3 Analytical derivation
#> 4 Analytical derivation
#> 5 Analytical derivation
#> 6 Analytical derivation
#> 7 Janzen et al. 2016, Model 1
#> 8 Janzen et al. 2016, Model 3 (baseline kin/kout)
#> 9 Janzen et al. 2016, Model 9
#> 10 Janzen et al. 2016, Model 13
#> 11 Janzen et al. 2016, Model 13, equations 6-8decide <- function(id) {
m <- reference_model(id)
r <- structural_identifiability(m)
p <- r$parameters
data.frame(
model = id,
decision = r$decision,
methods_agree = sum(r$agreement$agree),
identifiable = paste(p$parameter[p$status == "identifiable"], collapse = ", "),
not_alone = paste(p$parameter[p$status != "identifiable"], collapse = ", "),
combinations = paste(r$combinations$combination, collapse = ", "),
matches_literature = setequal(p$parameter[p$status == "identifiable"],
attr(m, "expected")$identifiable)
)
}
results <- do.call(rbind, lapply(reference_models()$id, decide))
knitr::kable(results)| model | decision | methods_agree | identifiable | not_alone | combinations | matches_literature |
|---|---|---|---|---|---|---|
| A1 | decided | 5 | CL, V | TRUE | ||
| A2 | decided | 5 | ka | CL, F, V | CL/F, CL/V | TRUE |
| A3 | decided | 5 | CL, V, ka | TRUE | ||
| A4 | decided | 5 | CL, Q, V1, V2 | TRUE | ||
| A5 | decided | 5 | KM, V, VMAX | TRUE | ||
| A6 | decided | 5 | CL, KM, V, VMAX | TRUE | ||
| A7 | decided | 5 | Kd | Rtot, ke | Rtot*ke | TRUE |
| A8 | decided | 5 | Kd, kin, kout | Rtot, ke | Rtot*ke | TRUE |
| A9 | decided | 5 | Kd, ke0 | Rtot, ke | Rtot*ke | TRUE |
| A10 | decided | 5 | ke0, koff, kon | Rtot, ke | Rtot*ke | TRUE |
| A10_Rtot_known | decided | 5 | ke, ke0, koff, kon | TRUE |
Every decision matches the expected result, and the two methods agree at all five random points for every model.
identifiable: locally identifiable. It may still have a
finite number of alternative values (A3, the oral model with
F known, has the flip-flop solution of absorption and
elimination).combination_only: not identifiable alone, but
identifiable within a reported combination.non_identifiable: not identifiable, and in no reported
combination.unresolved: the computation could not decide (limits
reached, poor conditioning, or the two methods disagree). pkident never
turns an undecided case into a decision.See validation/reference-models.md in the source
repository for the derivations and the full validation record.