emax_model = @model begin
@param begin
tvEmax ∈ RealDomain(lower = 0.0, init = 100.0)
tvEC50 ∈ RealDomain(lower = 0.0, init = 20.0)
Ω ∈ PDiagDomain(2)
σ_add ∈ RealDomain(lower = 0.0, init = 3.0) # sqrt(9)
end
@random begin
η ~ MvNormal(Ω)
end
@covariates CONC
@pre begin
Emax = tvEmax * exp(η[1])
EC50 = tvEC50 * exp(η[2])
end
@derived begin
mu := @. Emax * CONC / (EC50 + CONC)
dv ~ @. Normal(mu, σ_add)
end
endChapter 3: Coding Population Nonlinear Mixed Effects Models
MI-210 Essentials of Population PKPD M&S — Pumas Edition
1 Overview
This chapter covers coding pharmacometric models in Pumas — the Julia equivalent of coding NONMEM control streams via NMTRAN. Topics include:
- Coding pharmaco-statistical models through the Pumas
@modelmacro - Interpreting modeling output
- The Pumas analytical model library (equivalent to PREDPP)
- Practice problems: enzyme kinetics, QTc, AST, and PopPK
2 Coding Pharmaco-Statistical Models in Pumas
2.1 NONMEM vs. Pumas Model Structure
In NONMEM, models are specified through control stream records. In Pumas, the @model macro provides a structured, block-based equivalent:
| NONMEM Record | Pumas Block | Purpose |
|---|---|---|
$PROBLEM |
@metadata |
Problem description |
$INPUT / $DATA |
read_pumas() |
Data specification |
$THETA |
@param |
Fixed effect parameters |
$OMEGA |
@param + @random |
Random effect variance |
$SIGMA |
@param |
Residual error parameters |
$PK |
@pre |
Individual parameter definitions |
$PRED |
@pre + @derived |
Algebraic model (no dynamics) |
$SUBROUTINES |
@dynamics |
PK model library |
$DES |
@dynamics begin ... end |
User-defined ODEs |
$ERROR |
@derived |
Observation model |
$ESTIMATION |
fit() |
Estimation method |
$COVARIANCE |
infer() |
Standard errors |
$TABLE |
inspect() |
Predictions and residuals |
2.2 PRED Model Example (No Dynamics)
The book shows an Emax model coded with $PRED. Here is the NONMEM vs. Pumas side-by-side:
$PROB EMAX MODEL
$INPUT ID DV CONC
$DATA PDDATA.CSV IGNORE=C
$PRED
EMAX=THETA(1)*EXP(ETA(1))
EC50=THETA(2)*EXP(ETA(2))
E=EMAX*CONC/(EC50+CONC)
Y=E + EPS(1)
$THETA (0, 100) (0, 20) ; EMAX and EC50
$OMEGA 0.4 0.4
$SIGMA 9
$ESTIMATION MAXEVAL=9999 PRINT=10
2.3 PREDPP Model Example (Compartmental Dynamics)
The one-compartment oral PK model describes a drug that enters a depot compartment (representing the gastrointestinal tract), undergoes first-order absorption at rate \(K_a\) into the systemic circulation (central compartment), and is eliminated by first-order processes governed by clearance (\(CL\)). The volume of distribution (\(V\)) is not a real physiological volume — it is a proportionality constant relating the total amount of drug in the body to the measured plasma concentration: \(V = X / C\). For a 70-kg human, \(V\) can far exceed total body water (~42 L) because drug partitions into tissues according to \(V = V_P + V_T \cdot f_U / f_{UT}\), where \(f_U\) and \(f_{UT}\) are the fractions unbound in plasma and tissue, respectively. Clearance (\(CL\)) is the volume of blood irreversibly cleared of drug per unit time (L/hr). It is not itself a rate; rather, it converts an observed concentration into an elimination rate via \(\text{Rate} = CL \times C\). For a linear system, \(CL = \text{Dose} / \text{AUC}\) and is independent of dose.
The book shows a 1-compartment oral PK model with ADVAN2 TRANS2. In Pumas, Depots1Central1 is the analytical equivalent:
$PROB Pop PK model
$INPUT C ID DV AMT II ADDL TIME
$DATA ../data.CSV IGNORE=C
$SUB ADVAN2 TRANS2
$PK
CL=THETA(1)*EXP(ETA(1))
V=THETA(2)*EXP(ETA(2))
KA=THETA(3)*EXP(ETA(3))
S2=V
$ERROR
Y=F*(1+ERR(1))
IPRED=F
$THETA (0, 8) (0, 50) (0, 0.45)
$OMEGA 0.04 0.04 0.04
$SIGMA 0.04
$ESTIMATION MAXEVAL=9999 METHOD=0 POSTHOC
poppk_model = @model begin
@param begin
tvCL ∈ RealDomain(lower = 0.0, init = 8.0)
tvV ∈ RealDomain(lower = 0.0, init = 50.0)
tvKa ∈ RealDomain(lower = 0.0, init = 0.45)
Ω ∈ PDiagDomain(3)
σ_prop ∈ RealDomain(lower = 0.0, init = 0.2) # sqrt(0.04)
end
@random begin
η ~ MvNormal(Ω)
end
@pre begin
CL = tvCL * exp(η[1])
Vc = tvV * exp(η[2])
Ka = tvKa * exp(η[3])
end
@dynamics Depots1Central1
@derived begin
cp := @. Central / Vc
dv ~ @. Normal(cp, abs(cp) * σ_prop)
end
end2.4 The Pumas Analytical Model Library
Pumas provides closed-form analytical solutions equivalent to NONMEM’s PREDPP ADVAN subroutines:
| NONMEM ADVAN | Pumas Dynamics | Description | Required @pre |
|---|---|---|---|
| ADVAN1 | Central1 |
1-cpt IV | CL, Vc |
| ADVAN2 | Depots1Central1 |
1-cpt oral | CL, Vc, Ka |
| ADVAN3 | Central1Periph1 |
2-cpt IV | CL, Vc, Q, Vp |
| ADVAN4 | Depots1Central1Periph1 |
2-cpt oral | CL, Vc, Q, Vp, Ka |
| ADVAN11 | Central1Periph2 |
3-cpt IV | CL, Vc, Q2, Vp2, Q3, Vp3 |
| ADVAN12 | Depots1Central1Periph2 |
3-cpt oral | CL, Vc, Q2, Vp2, Q3, Vp3, Ka |
| ADVAN6/13 | @dynamics begin ... end |
User ODEs | User-defined |
The choice of compartmental model is determined by the shape of the concentration-time curve on a semi-logarithmic plot. A one-compartment model assumes instantaneous distribution throughout the body, producing a monoexponential decline (\(C(t) = C_0 \cdot e^{-Kt}\)) that appears as a single straight line on a log-linear plot. A two-compartment model produces a biexponential decline with a rapid initial \(\alpha\) (distribution) phase followed by a slower \(\beta\) (elimination) phase — two distinct slopes on the semi-log plot. Adding an oral depot compartment (e.g., Depots1Central1, Depots1Central1Periph1) introduces a rising absorption phase governed by \(K_a\); the depot represents the GI tract where drug undergoes first-order absorption. Zero-order absorption (constant-rate input) is used for sustained-release formulations or IV infusions. Importantly, the “number of compartments” refers only to the systemic distribution compartments — the depot is not counted. Model selection should be guided by the data (graphical assessment of the log-concentration profile), not by physiological plausibility alone.
Key differences from NONMEM:
- No
S2=Vscaling needed — divide compartment by volume explicitly in@derived - Parameter names are case-sensitive and must match exactly:
CL,Vc,Ka,Q,Vp TRANS2parameterization (CL, V, KA) maps directly to Pumas analytical models
2.4.1 Four Ways to Specify Dynamics in Pumas
Beyond the analytical library, Pumas supports multiple equivalent approaches for specifying model dynamics. All produce identical results for the same model:
Fastest. Use when the model matches a library structure.
@dynamics Depots1Central1Most flexible. Required for nonlinear elimination, indirect response, or any custom dynamics.
@dynamics begin
Depot' = -Ka * Depot
Central' = Ka * Depot - (CL / Vc) * Central
endPumas automatically detects linear ODE systems and solves via matrix exponential (faster than numerical integration). Use @options begin checklinear = false end to force numerical integration for nonlinear systems.
Define the ODE symbolically outside the model, enabling structural identifiability analysis and automatic simplification.
using ModelingToolkit
@mtkmodel MTK1Cpt begin
@parameters begin
Ka
CL
Vc
end
@variables begin
Depot(t) = 0.0
Central(t) = 0.0
end
@equations begin
D(Depot) ~ -Ka * Depot
D(Central) ~ Ka * Depot - (CL / Vc) * Central
end
end
@mtkbuild sys = MTK1Cpt()
# Then in your model:
@dynamics sysDefine dynamics as chemical/pharmacological reactions — intuitive for PBPK, QSP, and TMDD models.
using Catalyst
rn = @reaction_network begin
Ka, Depot --> Central
CL/Vc, Central --> 0
end
# Then in your model:
@dynamics rn- Analytical (closed-form): Default choice for standard 1/2/3-compartment models — fastest execution
- Explicit ODEs: Nonlinear PK (Michaelis-Menten), indirect response, effect compartment, custom models
- ModelingToolkit: When you need symbolic manipulation, identifiability analysis, or model composition
- Catalyst reactions: PBPK, QSP, TMDD — large reaction networks where stoichiometry is natural
2.4.2 Absorption Model Patterns
Beyond first-order absorption (Ka with Depots1Central1), Pumas supports a range of absorption models via @dynamics and @dosecontrol:
| Absorption Type | Implementation | Key Parameters |
|---|---|---|
| First-order | Depots1Central1 or Depot' = -Ka * Depot |
Ka |
| Zero-order | @dosecontrol begin duration = (Central = dur,) end |
dur (duration) |
| Parallel ZO + FO | Dual virtual doses with bioav splitting |
Ka, dur, Frac |
| Two parallel FO | Two depot compartments with different Ka | Ka1, Ka2, Frac |
| Transit compartments | Chain: T1' = -Ktr*T1; T2' = Ktr*T1 - Ktr*T2; … |
Ktr, n |
| Erlang | @delay(Erlang(N, MAT/N), Central) |
N (integer shape), MAT |
| Gamma | @delay(Gamma(N, MAT/N), Central) |
N (continuous shape), MAT |
| Weibull | @delay(Weibull(k, λ), Central) |
k (shape), λ (scale) |
See the Absorption Models tutorial for complete working examples of each.
2.5 Interpreting Modeling Output
In Pumas, model output is accessed programmatically rather than parsed from text files:
| NONMEM Output | Pumas Equivalent |
|---|---|
| Minimum OFV | loglikelihood(fit) or -2 * loglikelihood(fit) |
| Parameter estimates | coef(fit) |
| Standard errors | infer(fit) |
| ETA-shrinkage | Printed with fit summary |
| PRED, IPRED, WRES | inspect(fit) → DataFrame |
| AIC | aic(fit) |
| BIC | bic(fit) |
Useful variance terms:
- For exponential IIV: \(\%CV \approx \sqrt{\omega^2} \times 100\)
- For additive IIV: \(SD = \sqrt{\omega^2}\)
- %RSE: \(\%RSE = \frac{SE}{PE} \times 100\)
3 Practice Problems
The Michaelis-Menten equation describes the rate of an enzymatic reaction as a function of substrate concentration:
\[V = \frac{V_{\max} \cdot C}{K_M + C}\]
\(V_{\max}\) reflects the enzyme capacity — the maximum rate achievable when all enzyme active sites are saturated. \(K_M\) is the substrate concentration at which the reaction rate is 50% of \(V_{\max}\) and serves as a surrogate for affinity: a low \(K_M\) indicates high affinity of substrate for enzyme. This equation is structurally identical to the \(E_{\max}\) model in pharmacodynamics, where \(E_{\max} \leftrightarrow V_{\max}\) and \(EC_{50} \leftrightarrow K_M\). At low concentrations (\(C \ll K_M\)), the equation simplifies to \(V \approx (V_{\max}/K_M) \cdot C\), yielding approximately linear (first-order) kinetics. At high concentrations (\(C \gg K_M\)), the rate saturates at \(V_{\max}\), producing zero-order kinetics. This saturation is the mechanistic basis for nonlinear (capacity-limited) pharmacokinetics — drugs such as phenytoin and ethanol exhibit dose-disproportional increases in exposure because their metabolizing enzymes become saturated at therapeutic concentrations.
3.1 Problem 1: Enzyme Kinetics (Michaelis-Menten)
Population analysis of in-vitro enzyme activity data in 20 liver slice samples. Concentrations: 0.1, 0.3, 1, 3, 10, 30, 100, 300, 1000 uM.
raw_enz = CSV.read(joinpath(data_dir, "enz3.csv"), DataFrame)
# Fix data entry error (concentration > 1000 → 1000, as in R script)
enz_df = @rtransform(raw_enz,
:Concentration = :Concentration > 1000 ? 1000.0 : Float64(:Concentration),
)
rename!(enz_df, :ID => :id, :Concentration => :CONC, :ObsActivity => :dv)
fig = Figure(size = (600, 400))
ax = Axis(fig[1, 1], xlabel = "Concentration (uM)", ylabel = "Enzyme Activity")
scatter!(ax, enz_df.CONC, enz_df.dv, color = :steelblue, markersize = 6)
fig# Use concentration as time axis (non-temporal PRED-style model)
enz_df.time = Float64.(enz_df.CONC)
pop_enz = read_pumas(enz_df; observations = [:dv], covariates = [:CONC], event_data = false)Population
Subjects: 20
Covariates: CONC
Observations: dv
enz_model = @model begin
@metadata begin
desc = "Michaelis-Menten Enzyme Kinetics"
end
@param begin
tvKM ∈ RealDomain(lower = 0.0, init = 20.0)
tvVMAX ∈ RealDomain(lower = 0.0, init = 120.0)
Ω ∈ PDiagDomain(2)
σ_add ∈ RealDomain(lower = 0.0, init = 3.16)
end
@random begin
η ~ MvNormal(Ω)
end
@covariates CONC
@pre begin
KM = tvKM * exp(η[1])
VMAX = tvVMAX * exp(η[2])
end
@derived begin
ACT := @. VMAX * CONC / (KM + CONC)
dv ~ @. Normal(ACT, σ_add)
end
endPumasModel
Parameters: tvKM, tvVMAX, Ω, σ_add
Random effects: η
Covariates: CONC
Dynamical system variables:
Dynamical system type: No dynamical model
Derived: dv
Observed: dv
enz_params = (tvKM = 20.0, tvVMAX = 120.0, Ω = Diagonal([0.04, 0.04]), σ_add = sqrt(10.0))
# FO estimation (METHOD=0)
enz_fo = fit(enz_model, pop_enz, enz_params, Pumas.FO())
enz_fo[ Info: Checking the initial parameter values. [ Info: The initial negative log likelihood and its gradient are finite. Check passed. Iter Function value Gradient norm 0 1.612324e+03 2.021640e+03 * time: 0.012627124786376953 1 7.765242e+02 1.665303e+02 * time: 1.1877291202545166 2 7.560971e+02 1.129869e+02 * time: 1.1880099773406982 3 7.234432e+02 4.035156e+01 * time: 1.1882009506225586 4 7.213581e+02 3.072872e+01 * time: 1.1884150505065918 5 7.203405e+02 2.585542e+01 * time: 1.18858003616333 6 7.179375e+02 2.887201e+01 * time: 1.1887269020080566 7 7.155956e+02 4.163822e+01 * time: 1.1888940334320068 8 7.140300e+02 3.219939e+01 * time: 1.1890709400177002 9 7.134381e+02 1.624644e+01 * time: 1.1892240047454834 10 7.130683e+02 1.093506e+01 * time: 1.1893811225891113 11 7.127838e+02 5.855108e+00 * time: 1.1895339488983154 12 7.126722e+02 5.831986e+00 * time: 1.1896910667419434 13 7.126297e+02 5.750130e+00 * time: 1.1898210048675537 14 7.125869e+02 5.652110e+00 * time: 1.1899559497833252 15 7.124757e+02 5.432402e+00 * time: 1.190100908279419 16 7.122104e+02 9.704714e+00 * time: 1.1902430057525635 17 7.115703e+02 1.873484e+01 * time: 1.1903901100158691 18 7.101726e+02 2.645589e+01 * time: 1.190553903579712 19 7.082110e+02 1.564613e+01 * time: 1.1907129287719727 20 7.072692e+02 6.606017e+00 * time: 1.2259149551391602 21 7.071223e+02 1.096616e+00 * time: 1.2261879444122314 22 7.071019e+02 2.603118e+00 * time: 1.226362943649292 23 7.070939e+02 1.204422e+00 * time: 1.22658109664917 24 7.070927e+02 1.089222e+00 * time: 1.2267711162567139 25 7.070915e+02 1.085128e+00 * time: 1.226943016052246 26 7.070860e+02 2.227252e+00 * time: 1.2270920276641846 27 7.070741e+02 4.471181e+00 * time: 1.2272429466247559 28 7.070422e+02 8.125073e+00 * time: 1.227381944656372 29 7.069685e+02 1.286940e+01 * time: 1.227531909942627 30 7.068075e+02 1.746722e+01 * time: 1.227682113647461 31 7.065105e+02 1.768163e+01 * time: 1.2278358936309814 32 7.061779e+02 9.495926e+00 * time: 1.2279911041259766 33 7.060588e+02 4.058715e-01 * time: 1.2281451225280762 34 7.060361e+02 2.759697e+00 * time: 1.2283120155334473 35 7.060203e+02 3.801312e+00 * time: 1.2284860610961914 36 7.060017e+02 3.009431e+00 * time: 1.2286388874053955 37 7.059896e+02 1.014903e+00 * time: 1.2287828922271729 38 7.059856e+02 3.506766e-01 * time: 1.2289791107177734 39 7.059839e+02 7.042265e-01 * time: 1.2291929721832275 40 7.059826e+02 6.669825e-01 * time: 1.2294039726257324 41 7.059818e+02 2.130024e-01 * time: 1.2296209335327148 42 7.059815e+02 2.150589e-02 * time: 1.2298469543457031 43 7.059813e+02 1.832773e-01 * time: 1.2300539016723633 44 7.059812e+02 1.405467e-01 * time: 1.2302141189575195 45 7.059812e+02 6.205356e-02 * time: 1.2303850650787354 46 7.059811e+02 1.204098e-02 * time: 1.2305710315704346 47 7.059811e+02 3.513147e-02 * time: 1.2307910919189453 48 7.059811e+02 3.008400e-02 * time: 1.2309439182281494 49 7.059811e+02 1.100063e-02 * time: 1.2310960292816162 50 7.059811e+02 3.082722e-03 * time: 1.2312428951263428 51 7.059811e+02 7.852107e-03 * time: 1.2313830852508545 52 7.059811e+02 6.154334e-03 * time: 1.2315270900726318 53 7.059811e+02 2.019669e-03 * time: 1.231679916381836 54 7.059811e+02 8.889316e-04 * time: 1.231842041015625
FittedPumasModel
Dynamical system type: No dynamical model
Number of subjects: 20
Observation records: Active Missing
dv: 180 0
Total: 180 0
Number of parameters: Constant Optimized
0 5
Likelihood approximation: FO
Likelihood optimizer: BFGS
Termination Reason: GradientNorm
Log-likelihood value: -705.98112
---------------------
Estimate
---------------------
tvKM 46.341
tvVMAX 104.94
Ω₁,₁ 2.9107e-9
Ω₂,₂ 0.0059394
σ_add 11.727
---------------------
enz_coefs = coef(enz_fo)
conc_range = range(0.1, 1000, length = 200)
pred_act = enz_coefs.tvVMAX .* conc_range ./ (enz_coefs.tvKM .+ conc_range)
fig = Figure(size = (600, 400))
ax = Axis(fig[1, 1], xlabel = "Concentration (uM)", ylabel = "Enzyme Activity",
xscale = log10)
scatter!(ax, enz_df.CONC, enz_df.dv, color = :steelblue, markersize = 5, alpha = 0.6)
lines!(ax, collect(conc_range), pred_act, color = :red, linewidth = 2, label = "Population Pred")
axislegend(ax, position = :lt)
fig3.2 Problem 2: QTc Population Models
Population analysis of QTc exposure response data from Phase 1. Three models are compared:
- Naive pool (linear) — no IIV, OMEGA BLOCK fixed at 0
- Population linear — with IIV on intercept and slope
- Population Emax — with IIV on E0, Emax, EC50
dqtc_raw = CSV.read(joinpath(data_dir, "dqtc.csv"), DataFrame)
rename!(dqtc_raw, "SID" => :id, "CONC" => :conc, "dqtc" => :dv)
# Monotonic time per subject (row index)
dqtc_raw.time = Vector{Float64}(undef, nrow(dqtc_raw))
for gdf in groupby(dqtc_raw, :id)
gdf.time .= Float64.(1:nrow(gdf))
end
pop_dqtc = read_pumas(dqtc_raw; observations = [:dv], covariates = [:conc], event_data = false)
println("Subjects: ", length(pop_dqtc))Subjects: 64
3.2.1 Model 1: Naive Pool Linear (no IIV)
Equivalent to NONMEM $OMEGA BLOCK(2) 0 / 0 0 FIXED:
qtc_naive = @model begin
@param begin
tvINT ∈ RealDomain(init = 1.0)
tvSLP ∈ RealDomain(init = 0.02)
σ_add ∈ RealDomain(lower = 0.0, init = 5.0)
end
@covariates conc
@pre begin
INT = tvINT
SLP = tvSLP
end
@derived begin
mu := @. INT + SLP * conc
dv ~ @. Normal(mu, σ_add)
end
endPumasModel
Parameters: tvINT, tvSLP, σ_add
Random effects:
Covariates: conc
Dynamical system variables:
Dynamical system type: No dynamical model
Derived: dv
Observed: dv
qtc_naive_fit = fit(qtc_naive, pop_dqtc,
(tvINT = 1.0, tvSLP = 0.02, σ_add = 5.0),
Pumas.NaivePooled())
qtc_naive_fit[ Info: Checking the initial parameter values. [ Info: The initial negative log likelihood and its gradient are finite. Check passed. Iter Function value Gradient norm 0 2.177739e+03 1.418514e+04 * time: 5.793571472167969e-5 1 2.145965e+03 6.258659e+02 * time: 0.36159205436706543 2 2.118681e+03 7.104756e+02 * time: 0.3618330955505371 3 1.915684e+03 2.055385e+03 * time: 0.362030029296875 4 1.856796e+03 2.183562e+03 * time: 0.36221909523010254 5 1.856667e+03 8.088581e+03 * time: 0.362368106842041 6 1.853771e+03 2.659683e+03 * time: 0.36250805854797363 7 1.851798e+03 1.320097e+03 * time: 0.3626539707183838 8 1.847591e+03 1.163686e+02 * time: 0.3627970218658447 9 1.814822e+03 6.855837e+03 * time: 0.3629319667816162 10 1.791657e+03 8.789379e+03 * time: 0.3630690574645996 11 1.785043e+03 3.339229e+03 * time: 0.3632030487060547 12 1.783504e+03 1.702782e+02 * time: 0.3633451461791992 13 1.783244e+03 1.914953e+02 * time: 0.3634779453277588 14 1.783242e+03 2.541537e+00 * time: 0.3636140823364258 15 1.783242e+03 1.408389e-01 * time: 0.363753080368042 16 1.783242e+03 3.340979e-03 * time: 0.36388707160949707 17 1.783242e+03 6.584688e-05 * time: 0.3640329837799072
FittedPumasModel
Dynamical system type: No dynamical model
Number of subjects: 64
Observation records: Active Missing
dv: 812 0
Total: 812 0
Number of parameters: Constant Optimized
0 3
Likelihood approximation: NaivePooled
Likelihood optimizer: BFGS
Termination Reason: GradientNorm
Log-likelihood value: -1783.2424
------------------
Estimate
------------------
tvINT -0.088942
tvSLP 0.016926
σ_add 2.1753
------------------
3.2.2 Model 2: Population Linear (diagonal IIV)
qtc_pop_lin = @model begin
@param begin
tvINT ∈ RealDomain(init = 1.0)
tvSLP ∈ RealDomain(init = 0.2)
Ω ∈ PDiagDomain(2)
σ_add ∈ RealDomain(lower = 0.0, init = 3.16)
end
@random begin
η ~ MvNormal(Ω)
end
@covariates conc
@pre begin
INT = tvINT + η[1]
SLP = tvSLP + η[2]
end
@derived begin
mu := @. INT + SLP * conc
dv ~ @. Normal(mu, σ_add)
end
endPumasModel
Parameters: tvINT, tvSLP, Ω, σ_add
Random effects: η
Covariates: conc
Dynamical system variables:
Dynamical system type: No dynamical model
Derived: dv
Observed: dv
qtc_pop_fit = fit(qtc_pop_lin, pop_dqtc,
(tvINT = 1.0, tvSLP = 0.2, Ω = Diagonal([0.1, 0.2]), σ_add = sqrt(10.0)),
Pumas.FOCE())
qtc_pop_fit[ Info: Checking the initial parameter values. [ Info: The initial negative log likelihood and its gradient are finite. Check passed. Iter Function value Gradient norm 0 1.946113e+03 5.631218e+02 * time: 1.7881393432617188e-5 1 1.725899e+03 4.212633e+02 * time: 0.4035990238189697 2 1.677271e+03 1.943342e+02 * time: 0.40445590019226074 3 1.650182e+03 6.334516e+01 * time: 0.40516090393066406 4 1.637005e+03 7.223578e+01 * time: 0.40584707260131836 5 1.632158e+03 4.568235e+01 * time: 0.40653204917907715 6 1.624934e+03 2.013463e+01 * time: 0.40718603134155273 7 1.623045e+03 2.024793e+01 * time: 0.4078540802001953 8 1.617261e+03 3.906291e+01 * time: 0.4085230827331543 9 1.606577e+03 1.060489e+02 * time: 0.4092130661010742 10 1.565896e+03 3.676768e+02 * time: 0.4099729061126709 11 1.543994e+03 2.209361e+02 * time: 0.4116358757019043 12 1.541115e+03 1.818206e+02 * time: 0.4123380184173584 13 1.536554e+03 1.049645e+02 * time: 0.41300487518310547 14 1.532072e+03 1.480411e+02 * time: 0.4136829376220703 15 1.522174e+03 5.041024e+01 * time: 0.4145689010620117 16 1.514611e+03 8.654016e+01 * time: 0.41552090644836426 17 1.512978e+03 9.238392e+01 * time: 0.41629791259765625 18 1.510279e+03 1.252041e+02 * time: 0.4170680046081543 19 1.506438e+03 1.084174e+02 * time: 0.44490694999694824 20 1.499992e+03 9.634847e+01 * time: 0.4457409381866455 21 1.488966e+03 2.485298e+02 * time: 0.4464840888977051 22 1.483359e+03 4.427660e+02 * time: 0.44748592376708984 23 1.480256e+03 3.254343e+02 * time: 0.4484560489654541 24 1.479730e+03 1.634952e+02 * time: 0.4493720531463623 25 1.479427e+03 1.382633e+02 * time: 0.45003294944763184 26 1.479392e+03 1.275796e+01 * time: 0.4507009983062744 27 1.479392e+03 7.709314e-01 * time: 0.45136499404907227 28 1.479392e+03 5.331967e-02 * time: 0.4520750045776367 29 1.479392e+03 6.878027e-03 * time: 0.4527130126953125 30 1.479392e+03 6.363288e-04 * time: 0.45334696769714355
FittedPumasModel
Dynamical system type: No dynamical model
Number of subjects: 64
Observation records: Active Missing
dv: 812 0
Total: 812 0
Number of parameters: Constant Optimized
0 5
Likelihood approximation: FOCE
Likelihood optimizer: BFGS
Termination Reason: GradientNorm
Log-likelihood value: -1479.3915
------------------
Estimate
------------------
tvINT 0.15898
tvSLP 0.013788
Ω₁,₁ 1.0842
Ω₂,₂ 2.2605e-5
σ_add 1.3379
------------------
3.2.3 Model 3: Population Emax
qtc_emax = @model begin
@param begin
tvE0 ∈ RealDomain(init = 0.1)
tvEmax ∈ RealDomain(init = 40.0)
tvEC50 ∈ RealDomain(lower = 0.0, init = 1000.0)
Ω ∈ PSDDomain(3)
σ_add ∈ RealDomain(lower = 0.0, init = 3.16)
end
@random begin
η ~ MvNormal(Ω)
end
@covariates conc
@pre begin
E0 = tvE0 + η[1]
EC50 = tvEC50 * exp(η[2])
Emax = tvEmax + η[3]
end
@derived begin
mu := @. E0 + Emax * conc / (EC50 + conc)
dv ~ @. Normal(mu, σ_add)
end
endPumasModel
Parameters: tvE0, tvEmax, tvEC50, Ω, σ_add
Random effects: η
Covariates: conc
Dynamical system variables:
Dynamical system type: No dynamical model
Derived: dv
Observed: dv
qtc_emax_fit = fit(qtc_emax, pop_dqtc,
(tvE0 = 0.1, tvEmax = 40.0, tvEC50 = 1000.0,
Ω = [0.1 0.01 0.01; 0.01 0.1 0.01; 0.01 0.01 0.1],
σ_add = sqrt(10.0)),
Pumas.FOCE())
qtc_emax_fit[ Info: Checking the initial parameter values. [ Info: The initial negative log likelihood and its gradient are finite. Check passed. Iter Function value Gradient norm 0 1.871557e+03 5.256135e+02 * time: 3.314018249511719e-5 1 1.726619e+03 6.335882e+02 * time: 0.3728041648864746 2 1.632397e+03 2.206707e+02 * time: 0.37501001358032227 3 1.601012e+03 7.437475e+01 * time: 0.37687206268310547 4 1.590285e+03 4.988657e+01 * time: 0.7839939594268799 5 1.584782e+03 5.314367e+01 * time: 0.7858569622039795 6 1.570803e+03 2.829331e+01 * time: 0.7875661849975586 7 1.569767e+03 1.343165e+01 * time: 0.7891290187835693 8 1.567879e+03 3.358515e+01 * time: 0.7907440662384033 9 1.563679e+03 7.950324e+01 * time: 0.7925851345062256 10 1.558588e+03 1.150816e+02 * time: 0.7945101261138916 11 1.554793e+03 7.332243e+01 * time: 0.7968120574951172 12 1.550747e+03 1.623739e+01 * time: 0.7988619804382324 13 1.548074e+03 3.992431e+01 * time: 0.801185131072998 14 1.545245e+03 6.347403e+01 * time: 0.8033521175384521 15 1.538866e+03 8.631144e+01 * time: 0.8057639598846436 16 1.533309e+03 6.252670e+01 * time: 0.80873703956604 17 1.525244e+03 1.922217e+01 * time: 0.8114571571350098 18 1.520555e+03 3.148577e+01 * time: 0.8141369819641113 19 1.512474e+03 5.730347e+01 * time: 0.8166530132293701 20 1.497581e+03 7.174670e+01 * time: 0.8190209865570068 21 1.478386e+03 3.842529e+01 * time: 0.8215301036834717 22 1.472186e+03 1.127034e+01 * time: 0.8509399890899658 23 1.468135e+03 3.955126e+01 * time: 0.8530151844024658 24 1.465953e+03 3.408929e+01 * time: 0.8551170825958252 25 1.465131e+03 1.046805e+01 * time: 0.8575210571289062 26 1.464138e+03 1.625806e+01 * time: 0.8594839572906494 27 1.462860e+03 3.585974e+01 * time: 0.8613979816436768 28 1.461969e+03 3.464115e+01 * time: 0.8633530139923096 29 1.461397e+03 1.734594e+01 * time: 0.8652641773223877 30 1.461141e+03 3.733233e+00 * time: 0.8672211170196533 31 1.460974e+03 5.899231e+00 * time: 0.8691320419311523 32 1.460872e+03 6.726826e+00 * time: 0.8712961673736572 33 1.460772e+03 4.665350e+00 * time: 0.8731729984283447 34 1.460560e+03 4.449482e+00 * time: 0.8822321891784668 35 1.460140e+03 7.954170e+00 * time: 0.8842029571533203 36 1.459419e+03 1.550701e+01 * time: 0.8861100673675537 37 1.458427e+03 1.652405e+01 * time: 0.8880491256713867 38 1.457459e+03 5.999460e+00 * time: 0.8899681568145752 39 1.457069e+03 2.192073e+00 * time: 0.8922080993652344 40 1.456888e+03 7.414185e+00 * time: 0.8941121101379395 41 1.456707e+03 5.304777e+00 * time: 0.895967960357666 42 1.456511e+03 2.282375e+00 * time: 0.89786696434021 43 1.456292e+03 3.791058e+00 * time: 0.8998689651489258 44 1.455902e+03 6.517744e+00 * time: 0.9083371162414551 45 1.455467e+03 7.044861e+00 * time: 0.910163164138794 46 1.455376e+03 3.528953e+00 * time: 0.9119560718536377 47 1.455327e+03 3.173182e-01 * time: 0.9137330055236816 48 1.455286e+03 2.731534e+00 * time: 0.9154629707336426 49 1.455187e+03 5.604254e+00 * time: 0.9174880981445312 50 1.454975e+03 7.157259e+00 * time: 0.9193241596221924 51 1.454244e+03 7.270402e+00 * time: 0.9212431907653809 52 1.453604e+03 3.790794e+00 * time: 0.9287431240081787 53 1.453379e+03 7.986662e-01 * time: 0.9309720993041992 54 1.453354e+03 1.990846e-01 * time: 0.9329590797424316 55 1.453353e+03 1.949871e-01 * time: 0.9348721504211426 56 1.453353e+03 9.176580e-02 * time: 0.9365851879119873 57 1.453353e+03 2.141122e-02 * time: 0.9380781650543213 58 1.453353e+03 5.188243e-03 * time: 0.9446201324462891 59 1.453353e+03 5.188243e-03 * time: 0.9479260444641113 60 1.453353e+03 5.188243e-03 * time: 0.9514420032501221
FittedPumasModel
Dynamical system type: No dynamical model
Number of subjects: 64
Observation records: Active Missing
dv: 812 0
Total: 812 0
Number of parameters: Constant Optimized
0 10
Likelihood approximation: FOCE
Likelihood optimizer: BFGS
Termination Reason: NoObjectiveChange
Log-likelihood value: -1453.3531
---------------------
Estimate
---------------------
tvE0 -0.038189
tvEmax 75.865
tvEC50 4092.9
Ω₁,₁ 1.0971
Ω₂,₁ 0.77456
Ω₃,₁ 31.495
Ω₂,₂ 0.54683
Ω₃,₂ 22.235
Ω₃,₃ 1499.0
σ_add 1.2882
---------------------
3.2.4 Model Comparison
println("Model Comparison (QTc)")
println("=" ^ 50)
println("Naive Linear AIC: ", round(aic(qtc_naive_fit), digits = 1))
println("Pop Linear AIC: ", round(aic(qtc_pop_fit), digits = 1))
println("Pop Emax AIC: ", round(aic(qtc_emax_fit), digits = 1))Model Comparison (QTc)
==================================================
Naive Linear AIC: 3572.5
Pop Linear AIC: 2968.8
Pop Emax AIC: 2926.7
3.3 Problem 3: AST Emax Population Model
Population analysis of AST exposure response data from Phase 1.
ast_raw = CSV.read(joinpath(data_dir, "ast.csv"), DataFrame)
rename!(ast_raw, "SID" => :id, "CONC" => :conc, "AST" => :dv)
ast_raw.time = Vector{Float64}(undef, nrow(ast_raw))
for gdf in groupby(ast_raw, :id)
gdf.time .= Float64.(1:nrow(gdf))
end
pop_ast = read_pumas(ast_raw; observations = [:dv], covariates = [:conc], event_data = false)Population
Subjects: 64
Covariates: conc (heterogenous)
Observations: dv (heterogenous)
ast_emax = @model begin
@metadata begin
desc = "AST Emax Population Model"
end
@param begin
tvE0 ∈ RealDomain(init = 20.0)
tvEC50 ∈ RealDomain(lower = 0.0, init = 100.0)
tvEmax ∈ RealDomain(init = 50.0)
Ω ∈ PSDDomain(3)
σ_add ∈ RealDomain(lower = 0.0, init = 3.16)
end
@random begin
η ~ MvNormal(Ω)
end
@covariates conc
@pre begin
E0 = tvE0 + η[1]
EC50 = tvEC50 * exp(η[2])
Emax = tvEmax + η[3]
end
@derived begin
mu := @. E0 + Emax * conc / (EC50 + conc)
dv ~ @. Normal(mu, σ_add)
end
endPumasModel
Parameters: tvE0, tvEC50, tvEmax, Ω, σ_add
Random effects: η
Covariates: conc
Dynamical system variables:
Dynamical system type: No dynamical model
Derived: dv
Observed: dv
ast_fit = fit(ast_emax, pop_ast,
(tvE0 = 20.0, tvEC50 = 100.0, tvEmax = 50.0,
Ω = [0.1 0.01 0.01; 0.01 0.1 0.01; 0.01 0.01 0.1],
σ_add = sqrt(10.0)),
Pumas.FOCE())
ast_fit[ Info: Checking the initial parameter values. [ Info: The initial negative log likelihood and its gradient are finite. Check passed. Iter Function value Gradient norm 0 1.694226e+03 1.577918e+03 * time: 2.193450927734375e-5 1 9.191714e+02 9.604917e+01 * time: 0.5070838928222656 2 9.067138e+02 8.195403e+01 * time: 0.5085499286651611 3 8.858533e+02 5.126383e+01 * time: 0.5097339153289795 4 8.783999e+02 6.345648e+01 * time: 0.5108730792999268 5 8.671584e+02 8.291959e+01 * time: 0.5119888782501221 6 8.538607e+02 7.804764e+01 * time: 0.5131618976593018 7 8.429917e+02 4.142100e+01 * time: 0.5144391059875488 8 8.406836e+02 9.453546e+00 * time: 0.5156209468841553 9 8.405201e+02 9.658065e+00 * time: 0.5166380405426025 10 8.399121e+02 1.046413e+01 * time: 0.5177910327911377 11 8.394418e+02 1.642671e+01 * time: 0.5189030170440674 12 8.390421e+02 1.503612e+01 * time: 0.5199248790740967 13 8.381302e+02 1.089462e+01 * time: 0.5209779739379883 14 8.358632e+02 1.002887e+01 * time: 0.5485200881958008 15 8.323047e+02 1.806133e+01 * time: 0.5498509407043457 16 8.310067e+02 1.099528e+01 * time: 0.5511000156402588 17 8.307111e+02 9.698122e+00 * time: 0.5521519184112549 18 8.301625e+02 9.316964e+00 * time: 0.5532269477844238 19 8.297585e+02 1.311702e+01 * time: 0.5542259216308594 20 8.277769e+02 2.275459e+01 * time: 0.5553889274597168 21 8.248505e+02 2.747646e+01 * time: 0.556617021560669 22 8.214115e+02 2.097432e+01 * time: 0.5578880310058594 23 8.181504e+02 8.449226e+00 * time: 0.5591731071472168 24 8.154305e+02 8.420683e+00 * time: 0.5605309009552002 25 8.153312e+02 3.275010e+00 * time: 0.5616230964660645 26 8.153082e+02 2.604366e-01 * time: 0.5627260208129883 27 8.153079e+02 2.975894e-01 * time: 0.5637369155883789 28 8.153076e+02 3.199933e-01 * time: 0.5647048950195312 29 8.153042e+02 4.871807e-01 * time: 0.5657429695129395 30 8.152983e+02 7.268925e-01 * time: 0.5668799877166748 31 8.152819e+02 1.340362e+00 * time: 0.5681259632110596 32 8.152525e+02 2.005302e+00 * time: 0.5694329738616943 33 8.152044e+02 2.425633e+00 * time: 0.5875449180603027 34 8.151088e+02 2.321137e+00 * time: 0.5887150764465332 35 8.149446e+02 1.163517e+00 * time: 0.5898840427398682 36 8.148452e+02 6.361037e-01 * time: 0.5909380912780762 37 8.148094e+02 1.206059e+00 * time: 0.5920679569244385 38 8.147652e+02 3.265117e-01 * time: 0.5932090282440186 39 8.147596e+02 2.561389e-01 * time: 0.5949239730834961 40 8.147580e+02 3.000458e-01 * time: 0.5960021018981934 41 8.147568e+02 2.611461e-01 * time: 0.5970098972320557 42 8.147558e+02 2.553203e-01 * time: 0.598085880279541 43 8.147554e+02 2.575697e-01 * time: 0.5990650653839111 44 8.147550e+02 2.591353e-01 * time: 0.6000850200653076 45 8.147544e+02 2.607006e-01 * time: 0.6010849475860596 46 8.147531e+02 2.629020e-01 * time: 0.6021809577941895 47 8.147507e+02 2.643156e-01 * time: 0.6032769680023193 48 8.147442e+02 2.665898e-01 * time: 0.6043589115142822 49 8.147253e+02 2.685605e-01 * time: 0.6055519580841064 50 8.146989e+02 6.027405e-01 * time: 0.6132829189300537 51 8.146523e+02 1.217843e+00 * time: 0.6156408786773682 52 8.144474e+02 3.016641e+00 * time: 0.6168379783630371 53 8.127958e+02 5.703653e+00 * time: 0.6182301044464111 54 8.121134e+02 8.840596e+00 * time: 0.6201930046081543 55 8.115071e+02 1.341296e+01 * time: 0.6230208873748779 56 8.113122e+02 9.704525e+00 * time: 0.6256310939788818 57 8.111868e+02 3.354920e+00 * time: 0.6269519329071045 58 8.111649e+02 3.315285e-01 * time: 0.6283490657806396 59 8.111621e+02 3.176063e-01 * time: 0.629478931427002 60 8.110946e+02 2.552949e+00 * time: 0.6308720111846924 61 8.109807e+02 5.009345e+00 * time: 0.6323039531707764 62 8.107321e+02 7.445230e+00 * time: 0.633652925491333 63 8.104088e+02 6.970537e+00 * time: 0.6401770114898682 64 8.101827e+02 3.136169e+00 * time: 0.6415081024169922 65 8.101494e+02 6.649904e-01 * time: 0.6427919864654541 66 8.101477e+02 2.623425e-01 * time: 0.6440389156341553 67 8.101468e+02 1.525673e-01 * time: 0.6451449394226074 68 8.101467e+02 1.528493e-01 * time: 0.6461400985717773 69 8.101467e+02 1.530786e-01 * time: 0.6471230983734131 70 8.101466e+02 1.534902e-01 * time: 0.6482040882110596 71 8.101463e+02 1.540950e-01 * time: 0.6493480205535889 72 8.101455e+02 1.584052e-01 * time: 0.6505169868469238 73 8.101435e+02 1.904824e-01 * time: 0.6516819000244141 74 8.101383e+02 3.452639e-01 * time: 0.6529090404510498 75 8.101244e+02 6.017616e-01 * time: 0.6577548980712891 76 8.100874e+02 1.034478e+00 * time: 0.6590180397033691 77 8.099885e+02 1.778811e+00 * time: 0.6602818965911865 78 8.098057e+02 3.325461e+00 * time: 0.6617410182952881 79 8.096503e+02 2.639750e+00 * time: 0.6632349491119385 80 8.093911e+02 1.669475e+00 * time: 0.6647319793701172 81 8.093681e+02 5.739834e-01 * time: 0.6661860942840576 82 8.093600e+02 9.433986e-01 * time: 0.6675410270690918 83 8.093472e+02 2.721046e-01 * time: 0.6689310073852539 84 8.093450e+02 2.378452e-01 * time: 0.6701970100402832 85 8.093449e+02 2.127704e-01 * time: 0.6751348972320557 86 8.093446e+02 2.220855e-01 * time: 0.6762149333953857 87 8.093438e+02 2.456031e-01 * time: 0.6773738861083984 88 8.093416e+02 3.418575e-01 * time: 0.6786160469055176 89 8.093361e+02 5.958788e-01 * time: 0.679847002029419 90 8.093216e+02 1.003145e+00 * time: 0.6810839176177979 91 8.092834e+02 1.664700e+00 * time: 0.6824159622192383 92 8.091831e+02 2.768165e+00 * time: 0.6839759349822998 93 8.089580e+02 4.820284e+00 * time: 0.6855800151824951 94 8.086509e+02 5.701941e+00 * time: 0.6871850490570068 95 8.080609e+02 3.807745e+00 * time: 0.692781925201416 96 8.075956e+02 9.191869e-01 * time: 0.694633960723877 97 8.075041e+02 8.324795e-01 * time: 0.6961989402770996 98 8.074673e+02 6.186203e-01 * time: 0.6977629661560059 99 8.074230e+02 5.781272e-01 * time: 0.6996059417724609 100 8.074084e+02 4.101525e-01 * time: 0.7029290199279785 101 8.074004e+02 1.810009e-01 * time: 0.7061610221862793 102 8.073931e+02 4.032068e-01 * time: 0.7079830169677734 103 8.073880e+02 4.295068e-01 * time: 0.7096009254455566 104 8.073503e+02 7.177194e-01 * time: 0.7158739566802979 105 8.072764e+02 1.078033e+00 * time: 0.7176098823547363 106 8.070835e+02 1.676936e+00 * time: 0.7193310260772705 107 8.067051e+02 2.970590e+00 * time: 0.7209830284118652 108 8.063589e+02 2.672784e+00 * time: 0.7222399711608887 109 8.057565e+02 3.656213e+00 * time: 0.7238130569458008 110 8.054303e+02 3.110965e+00 * time: 0.7252740859985352 111 8.054172e+02 2.375133e+00 * time: 0.7269999980926514 112 8.053551e+02 5.285517e-01 * time: 0.7284688949584961 113 8.053523e+02 1.364149e-01 * time: 0.7299840450286865 114 8.053515e+02 1.365453e-02 * time: 0.7312290668487549 115 8.053515e+02 8.137844e-03 * time: 0.7357978820800781 116 8.053515e+02 8.152911e-03 * time: 0.7368988990783691 117 8.053515e+02 8.156157e-03 * time: 0.737847089767456 118 8.053515e+02 8.156075e-03 * time: 0.7390139102935791 119 8.053515e+02 8.155839e-03 * time: 0.7402489185333252 120 8.053515e+02 8.155389e-03 * time: 0.7413949966430664 121 8.053515e+02 8.152666e-03 * time: 0.7423350811004639 122 8.053515e+02 8.152662e-03 * time: 0.7434461116790771 123 8.053515e+02 8.139791e-03 * time: 0.7445979118347168 124 8.053515e+02 1.135269e-02 * time: 0.7457559108734131 125 8.053515e+02 2.385565e-02 * time: 0.7506210803985596 126 8.053515e+02 3.979734e-02 * time: 0.7517349720001221 127 8.053514e+02 6.611758e-02 * time: 0.7528579235076904 128 8.053512e+02 1.019142e-01 * time: 0.7539749145507812 129 8.053509e+02 1.433596e-01 * time: 0.7550520896911621 130 8.053501e+02 1.684077e-01 * time: 0.7561230659484863 131 8.053490e+02 1.413035e-01 * time: 0.7571990489959717 132 8.053480e+02 5.888520e-02 * time: 0.7584519386291504 133 8.053477e+02 1.028002e-02 * time: 0.7597129344940186 134 8.053476e+02 2.437124e-02 * time: 0.7608299255371094 135 8.053476e+02 2.886175e-02 * time: 0.7618279457092285 136 8.053475e+02 1.936667e-02 * time: 0.7663478851318359 137 8.053475e+02 4.619635e-03 * time: 0.7673399448394775 138 8.053474e+02 3.401024e-03 * time: 0.7683470249176025 139 8.053474e+02 5.460919e-03 * time: 0.7692348957061768 140 8.053474e+02 4.049276e-03 * time: 0.7701120376586914 141 8.053474e+02 1.227562e-03 * time: 0.7709879875183105 142 8.053474e+02 5.661175e-04 * time: 0.7719039916992188
FittedPumasModel
Dynamical system type: No dynamical model
Number of subjects: 64
Observation records: Active Missing
dv: 235 0
Total: 235 0
Number of parameters: Constant Optimized
0 10
Likelihood approximation: FOCE
Likelihood optimizer: BFGS
Termination Reason: GradientNorm
Log-likelihood value: -805.34742
-------------------
Estimate
-------------------
tvE0 15.276
tvEC50 905.66
tvEmax 94.772
Ω₁,₁ 9.6107
Ω₂,₁ 4.3304
Ω₃,₁ 106.66
Ω₂,₂ 1.9512
Ω₃,₂ 48.059
Ω₃,₃ 1183.7
σ_add 6.6229
-------------------
ast_infer = infer(ast_fit)
ast_infer[ Info: Calculating: variance-covariance matrix. ┌ Error: Failed. └ @ Pumas ~/.julia/packages/Pumas/GZeMg/src/estimation/inference.jl:601
Asymptotic inference results
Dynamical system type: No dynamical model
Number of subjects: 64
Observation records: Active Missing
dv: 235 0
Total: 235 0
Number of parameters: Constant Optimized
0 10
Likelihood approximation: FOCE
Likelihood optimizer: BFGS
Termination Reason: GradientNorm
Log-likelihood value: -805.34742
---------------------------------------
Estimate SE 95.0% C.I.
---------------------------------------
tvE0 15.276 NaN [ NaN; NaN]
tvEC50 905.66 NaN [ NaN; NaN]
tvEmax 94.772 NaN [ NaN; NaN]
Ω₁,₁ 9.6107 NaN [ NaN; NaN]
Ω₂,₁ 4.3304 NaN [ NaN; NaN]
Ω₃,₁ 106.66 NaN [ NaN; NaN]
Ω₂,₂ 1.9512 NaN [ NaN; NaN]
Ω₃,₂ 48.059 NaN [ NaN; NaN]
Ω₃,₃ 1183.7 NaN [ NaN; NaN]
σ_add 6.6229 NaN [ NaN; NaN]
---------------------------------------
Variance-covariance matrix could not be
evaluated. The random effects may be over-
parameterized. Check the coefficients for
variance estimates near zero.
ast_insp = inspect(ast_fit)
ast_insp_df = DataFrame(ast_insp)
obs_rows = @rsubset(ast_insp_df, !ismissing(:dv))
fig = Figure(size = (800, 600))
ax1 = Axis(fig[1, 1], xlabel = "PRED", ylabel = "DV", title = "DV vs PRED")
scatter!(ax1, obs_rows.dv_pred, obs_rows.dv, color = :navy, markersize = 5)
ablines!(ax1, 0, 1, color = :black)
ax2 = Axis(fig[1, 2], xlabel = "IPRED", ylabel = "DV", title = "DV vs IPRED")
scatter!(ax2, obs_rows.dv_ipred, obs_rows.dv, color = :navy, markersize = 5)
ablines!(ax2, 0, 1, color = :black)
ax3 = Axis(fig[2, 1], xlabel = "PRED", ylabel = "WRES", title = "WRES vs PRED")
scatter!(ax3, obs_rows.dv_pred, obs_rows.dv_wres, color = :navy, markersize = 5)
hlines!(ax3, 0, color = :black)
ax4 = Axis(fig[2, 2], xlabel = "Time", ylabel = "WRES", title = "WRES vs TIME")
scatter!(ax4, obs_rows.time, obs_rows.dv_wres, color = :navy, markersize = 5)
hlines!(ax4, 0, color = :black)
fig[ Info: Calculating predictions. [ Info: Calculating weighted residuals. [ Info: Calculating empirical bayes. [ Info: Evaluating dose control parameters. [ Info: Evaluating individual parameters. [ Info: Done.
3.4 Problem 4: Population PK (1-Compartment Oral)
Population PK analysis of a typical Phase 1 dataset. The data has known errors that must be fixed (as discovered in the R script).
pk_raw = CSV.read(joinpath(data_dir, "prob_2.csv"), DataFrame)
rename!(pk_raw,
:ID => :id, :DV => :dv, :AMT => :amt,
:II => :ii, :ADDL => :addl, :TIME => :time,
)
# Data cleaning (from R script: IndPopModels.Problem2.R)
# Fix 1: ID=13, AMT=20 should be 200
@rtransform!(pk_raw, :amt = (:id == 13 && :amt == 20) ? 200.0 : Float64(:amt))
# Fix 2: ID=22, II=6 should be 8
@rtransform!(pk_raw, :ii = (:id == 22 && :ii == 6) ? 8 : :ii)
# Fix 3: ID=2, dose TIME=24 should be 0
@rtransform!(pk_raw, :time = (:id == 2 && :amt > 0 && :time == 24) ? 0.0 : Float64(:time))
# Fix 4: ID=15, dose AMT=0 should be 200
@rtransform!(pk_raw, :amt = (:id == 15 && :time == 0 && :amt == 0) ? 200.0 : Float64(:amt))
# Drop comment column, add required columns
select!(pk_raw, Not(:C))
pk_raw.evid = ifelse.(pk_raw.amt .> 0, 1, 0)
pk_raw.cmt = ifelse.(pk_raw.amt .> 0, 1, 2) # depot=1, central=2
pk_raw.dv = ifelse.(pk_raw.evid .== 1, missing, pk_raw.dv)
pop_pk = read_pumas(pk_raw)
println("Subjects: ", length(pop_pk))Subjects: 25
# 1-cpt oral model: ADVAN2 TRANS2 equivalent
poppk_1cpt = @model begin
@metadata begin
desc = "1-Compartment Oral PopPK (ADVAN2 TRANS2)"
end
@param begin
tvCL ∈ RealDomain(lower = 0.0, init = 8.0)
tvV ∈ RealDomain(lower = 0.0, init = 50.0)
tvKa ∈ RealDomain(lower = 0.0, init = 0.45)
Ω ∈ PDiagDomain(3)
σ_prop ∈ RealDomain(lower = 0.0, init = 0.2)
end
@random begin
η ~ MvNormal(Ω)
end
@pre begin
CL = tvCL * exp(η[1])
Vc = tvV * exp(η[2])
Ka = tvKa * exp(η[3])
end
@dynamics Depots1Central1
@derived begin
cp := @. Central / Vc
dv ~ @. Normal(cp, abs(cp) * σ_prop)
end
endPumasModel
Parameters: tvCL, tvV, tvKa, Ω, σ_prop
Random effects: η
Covariates:
Dynamical system variables: Depot, Central
Dynamical system type: Closed form
Derived: dv
Observed: dv
pk_params = (
tvCL = 8.0, tvV = 50.0, tvKa = 0.45,
Ω = Diagonal([0.04, 0.04, 0.04]),
σ_prop = 0.2,
)
# FO estimation first (matching NONMEM METHOD=0)
pk_fo = fit(poppk_1cpt, pop_pk, pk_params, Pumas.FO())
pk_fo[ Info: Checking the initial parameter values. [ Info: The initial negative log likelihood and its gradient are finite. Check passed. Iter Function value Gradient norm 0 8.264980e+01 2.753078e+01 * time: 2.2172927856445312e-5 1 8.141061e+01 5.944208e+01 * time: 1.3636481761932373 2 8.077844e+01 5.122005e+01 * time: 1.364319086074829 3 7.993858e+01 2.362374e+01 * time: 1.364886999130249 4 7.771907e+01 8.524163e+00 * time: 1.3654201030731201 5 7.744921e+01 5.509531e+00 * time: 1.3659591674804688 6 7.713191e+01 3.205463e+00 * time: 1.3664031028747559 7 7.703286e+01 2.240696e+00 * time: 1.366866111755371 8 7.698240e+01 3.473049e-01 * time: 1.3673031330108643 9 7.697257e+01 4.504869e-01 * time: 1.3677361011505127 10 7.695368e+01 6.067292e-01 * time: 1.3681612014770508 11 7.694136e+01 4.894391e-01 * time: 1.3686342239379883 12 7.692262e+01 4.692972e-01 * time: 1.3691060543060303 13 7.689627e+01 8.668093e-01 * time: 1.3695740699768066 14 7.686013e+01 1.080698e+00 * time: 1.3700392246246338 15 7.683395e+01 6.307220e-01 * time: 1.370530128479004 16 7.682714e+01 9.958439e-02 * time: 1.3709781169891357 17 7.682624e+01 8.441247e-02 * time: 1.371408224105835 18 7.682581e+01 1.294959e-01 * time: 1.3718361854553223 19 7.682555e+01 8.027178e-02 * time: 1.3722660541534424 20 7.682551e+01 2.023900e-02 * time: 1.372709035873413 21 7.682550e+01 3.212026e-03 * time: 1.373138189315796 22 7.682550e+01 2.372556e-04 * time: 1.373603105545044
FittedPumasModel
Dynamical system type: Closed form
Number of subjects: 25
Observation records: Active Missing
dv: 125 0
Total: 125 0
Number of parameters: Constant Optimized
0 7
Likelihood approximation: FO
Likelihood optimizer: BFGS
Termination Reason: GradientNorm
Log-likelihood value: -76.825504
--------------------
Estimate
--------------------
tvCL 8.0439
tvV 50.66
tvKa 0.49759
Ω₁,₁ 0.058496
Ω₂,₂ 0.057974
Ω₃,₃ 0.0083115
σ_prop 0.15247
--------------------
# Re-fit with FOCEI for better estimates
pk_focei = fit(poppk_1cpt, pop_pk, pk_params, Pumas.FOCEI())
pk_focei[ Info: Checking the initial parameter values. [ Info: The initial negative log likelihood and its gradient are finite. Check passed. Iter Function value Gradient norm 0 7.571519e+01 3.057811e+01 * time: 2.4080276489257812e-5 1 7.272019e+01 2.670023e+01 * time: 0.380601167678833 2 7.044261e+01 1.658803e+01 * time: 0.3841361999511719 3 6.973885e+01 1.215980e+01 * time: 0.3862471580505371 4 6.956608e+01 5.783517e+00 * time: 0.38887810707092285 5 6.914330e+01 4.683046e+00 * time: 0.39066410064697266 6 6.892282e+01 1.076909e+00 * time: 0.39240503311157227 7 6.886545e+01 7.776024e-01 * time: 0.3941681385040283 8 6.879593e+01 7.129593e-01 * time: 0.39599609375 9 6.874807e+01 7.434706e-01 * time: 0.3976931571960449 10 6.863010e+01 1.021736e+00 * time: 0.3994481563568115 11 6.851866e+01 9.436458e-01 * time: 0.4391801357269287 12 6.841345e+01 9.920031e-01 * time: 0.4410359859466553 13 6.834219e+01 7.754581e-01 * time: 0.4427511692047119 14 6.827168e+01 7.149361e-01 * time: 0.44454216957092285 15 6.821152e+01 6.196741e-01 * time: 0.44622302055358887 16 6.818100e+01 2.081450e-01 * time: 0.4478580951690674 17 6.816906e+01 1.109124e-01 * time: 0.4495580196380615 18 6.816207e+01 1.665597e-01 * time: 0.4512310028076172 19 6.815634e+01 1.542026e-01 * time: 0.45285820960998535 20 6.815261e+01 7.146703e-02 * time: 0.4544861316680908 21 6.815096e+01 3.142187e-02 * time: 0.4560542106628418 22 6.815023e+01 3.787429e-02 * time: 0.4574730396270752 23 6.814975e+01 3.439474e-02 * time: 0.45899009704589844 24 6.814946e+01 1.448103e-02 * time: 0.46054911613464355 25 6.814933e+01 4.476179e-03 * time: 0.46212220191955566 26 6.814927e+01 8.170391e-03 * time: 0.46361804008483887 27 6.814923e+01 7.013814e-03 * time: 0.4652111530303955 28 6.814921e+01 2.612858e-03 * time: 0.46680712699890137 29 6.814920e+01 9.269186e-04 * time: 0.4684431552886963
FittedPumasModel
Dynamical system type: Closed form
Number of subjects: 25
Observation records: Active Missing
dv: 125 0
Total: 125 0
Number of parameters: Constant Optimized
0 7
Likelihood approximation: FOCE
Likelihood optimizer: BFGS
Termination Reason: GradientNorm
Log-likelihood value: -68.149202
--------------------
Estimate
--------------------
tvCL 8.3055
tvV 45.422
tvKa 0.40781
Ω₁,₁ 0.057279
Ω₂,₂ 0.052748
Ω₃,₃ 5.6956e-7
σ_prop 0.15245
--------------------
pk_infer = infer(pk_focei)
pk_infer[ Info: Calculating: variance-covariance matrix. ┌ Error: Failed. └ @ Pumas ~/.julia/packages/Pumas/GZeMg/src/estimation/inference.jl:601
Asymptotic inference results
Dynamical system type: Closed form
Number of subjects: 25
Observation records: Active Missing
dv: 125 0
Total: 125 0
Number of parameters: Constant Optimized
0 7
Likelihood approximation: FOCE
Likelihood optimizer: BFGS
Termination Reason: GradientNorm
Log-likelihood value: -68.149202
----------------------------------------
Estimate SE 95.0% C.I.
----------------------------------------
tvCL 8.3055 NaN [ NaN; NaN]
tvV 45.422 NaN [ NaN; NaN]
tvKa 0.40781 NaN [ NaN; NaN]
Ω₁,₁ 0.057279 NaN [ NaN; NaN]
Ω₂,₂ 0.052748 NaN [ NaN; NaN]
Ω₃,₃ 5.6956e-7 NaN [ NaN; NaN]
σ_prop 0.15245 NaN [ NaN; NaN]
----------------------------------------
Variance-covariance matrix could not be
evaluated. The random effects may be over-
parameterized. Check the coefficients for
variance estimates near zero.
pk_insp = inspect(pk_focei)
pk_df = DataFrame(pk_insp)
pk_obs = @rsubset(pk_df, !ismissing(:dv))
fig = Figure(size = (800, 600))
ax1 = Axis(fig[1, 1], xlabel = "PRED", ylabel = "DV", title = "DV vs PRED")
scatter!(ax1, pk_obs.dv_pred, pk_obs.dv, color = :navy, markersize = 5, alpha = 0.6)
ablines!(ax1, 0, 1, color = :black)
ax2 = Axis(fig[1, 2], xlabel = "IPRED", ylabel = "DV", title = "DV vs IPRED")
scatter!(ax2, pk_obs.dv_ipred, pk_obs.dv, color = :navy, markersize = 5, alpha = 0.6)
ablines!(ax2, 0, 1, color = :black)
ax3 = Axis(fig[2, 1], xlabel = "PRED", ylabel = "WRES", title = "WRES vs PRED")
scatter!(ax3, pk_obs.dv_pred, pk_obs.dv_wres, color = :navy, markersize = 5, alpha = 0.6)
hlines!(ax3, 0, color = :black)
ax4 = Axis(fig[2, 2], xlabel = "Time (hr)", ylabel = "WRES", title = "WRES vs TIME")
scatter!(ax4, pk_obs.time, pk_obs.dv_wres, color = :navy, markersize = 5, alpha = 0.6)
hlines!(ax4, 0, color = :black)
fig[ Info: Calculating predictions. [ Info: Calculating weighted residuals. [ Info: Calculating empirical bayes. [ Info: Evaluating dose control parameters. [ Info: Evaluating individual parameters. [ Info: Done.
fig = Figure(size = (900, 700))
for (i, subj_id) in enumerate(unique(pk_obs.id)[1:9])
row, col = divrem(i - 1, 3) .+ (1, 1)
ax = Axis(fig[row, col], xlabel = "Time", ylabel = "Conc",
title = "ID $subj_id")
sdf = @rsubset(pk_obs, :id == subj_id)
scatter!(ax, sdf.time, sdf.dv, color = :blue, markersize = 6)
lines!(ax, sdf.time, sdf.dv_ipred, color = :red, linewidth = 1.5)
lines!(ax, sdf.time, sdf.dv_pred, color = :gray, linewidth = 1, linestyle = :dash)
end
figWhen validating a Pumas model against NONMEM results:
- NONMEM OFV (with constant) =
-2 * loglikelihood(fit)in Pumas — these should match exactly - NONMEM OFV (without constant) =
-2 * loglikelihood(fit) - N * log(2π)whereN= total observations - To compute the traditional OFV:
ofv = -2 * loglikelihood(fit) - nobs(fit) * log(2π)
Common reasons for mismatch:
- Data differences — column mapping, missing value handling, or filtering
- Parameterization — different IIV or error model form
- Covariate interpolation — NONMEM defaults to NOCB; Pumas defaults to LOCF (
covariates_direction = :left) - Initial estimates — compare EBEs via
empirical_bayes(fit)against NONMEM’s.phifile
Pumas provides named error distributions that simplify @derived blocks:
| Error Model | Manual Form | Convenience Form |
|---|---|---|
| Additive | dv ~ Normal(cp, σ_add) |
dv ~ Normal(cp, σ_add) |
| Proportional | dv ~ Normal(cp, abs(cp) * σ_prop) |
dv ~ ProportionalNormal(cp, σ_prop) |
| Combined | dv ~ Normal(cp, √(σ_add² + (cp*σ_prop)²)) |
dv ~ CombinedNormal(cp, σ_add, σ_prop) |
ProportionalNormal and CombinedNormal handle the abs/sqrt internally.
The choice of residual error model should be informed by the characteristics of the bioanalytical assay, not solely by statistical fit. Additive error implies constant measurement uncertainty across the concentration range, which is typical of assays operating near the lower limit of quantification (LLOQ) where absolute noise dominates. Proportional error implies uncertainty that scales with concentration, reflecting the behavior of most validated bioanalytical assays where the coefficient of variation (%CV) is approximately constant across the calibration range. Combined error captures both regimes simultaneously — additive noise dominates at low concentrations and proportional noise dominates at higher concentrations. In practice, the combined model is often the most realistic because assay precision degrades near the LLOQ (additive component) while maintaining a roughly constant %CV at mid-to-high concentrations (proportional component). Understanding the assay’s validated range and precision profile provides a principled basis for selecting the error model prior to estimation.
4 Study Guide Questions
- Explain how you might interpret the standard error of the estimates.
- List three things that you should look for in the Monitoring of Search output (optimizer convergence in Pumas).
- List three diagnostic plots that you should create for a population NLME model.
- How can you make sense of the off-diagonal elements of a covariance matrix?
- Provide an initial estimate for the first element of OMEGA, assuming a proportional inter-individual %CV equal to 30%. (Answer: 0.09, since for exponential IIV, omega = (CV/100)^2 = 0.3^2 = 0.09)
- Provide an initial estimate for SIGMA, assuming a constant residual error with standard deviation equal to 5. (Answer: In NONMEM sigma=25 (variance); in Pumas sigma_add=5 (SD))
5 Supplementary Material
| Topic | Tutorial | Key Additions |
|---|---|---|
| Lecture: 1-Cpt IV Bolus | PK W2 | The physiology behind the monoexponential model coded here |
| Lecture: Volume of Distribution | PK W4 | Why V is not a real volume — tissue partitioning, protein binding |
| Lecture: Clearance & Hepatic CL | PK W6 | CL as volume cleared per unit time, extraction ratio, intrinsic clearance |
| Lecture: Intrinsic CL & Enzyme Kinetics | PK W7a | Vmax/KM foundation of Michaelis-Menten and Emax models |
| Lecture: Oral Absorption | PK W8 | First-order absorption, bioavailability, flip-flop kinetics |
| Lecture: Error Model Specification | PKPD W7 | Choosing additive vs proportional vs combined error from assay characteristics |
| Complete Pumas fitting workflow | Fitting | Domain types, @param/@random/@pre/@dynamics/@derived deep dive, inference and GOF |
| 9 ways to specify dynamics | Dynamical Systems | Closed-form, explicit ODE, ModelingToolkit, Catalyst — with linearity detection comparison |
| NONMEM ↔︎ Pumas validation | Comparing NONMEM & Pumas | OFV matching, parameter extraction from .lst, EBE comparison, troubleshooting |
| Absorption model catalog | Absorption Models | First-order, zero-order, parallel, transit, Erlang, Gamma, Weibull — all with working code |
| VEM estimation | VEM Introduction | Variational EM as alternative to FOCE, options reference, comparison study |
| MCEM estimation | MCEM | Monte Carlo EM for complex likelihoods |