Estimating Parameters using SAEM

PumasEMModels can optionally be fitted using SAEM. Here is an example PumasEMModel definition:

using Pumas
using PumasUtilities
using Random

covariate_saem_cov = @emmodel begin
    @random begin
        CL ~ 1 + logwt | LogNormal
        v ~ 1 | LogNormal
    end
    @covariance 2
    @covariates wt
    @pre begin
        Vc = wt * v
    end
    @dynamics Central1
    @post begin
        cp = Central / Vc
    end
    @error begin
        dv ~ ProportionalNormal(cp)
    end
end
PumasEMModel
  Parameters with random effects:
    CL ~ (1, :logwt) | LogNormal
    v ~ (1,) | LogNormal
  Covariates: wt
  Pre-dynamical variables: Vc
  Dynamical system variables: Central
  Post-dynamical variables: cp

See the documentation on the @emmodel macro interface for an explanation of the syntax. To fit this model, we'll simulate data using simobs.

sim_params_covariate_cov = (;
    CL = (4.0, 0.75),
    v = 70.0,
    Ω = (Pumas.@SMatrix([0.1 0.05; 0.05 0.1]),),
    σ = ((0.2,),),
)

obstimes = 0.0:100.0

dose = DosageRegimen(1_000; addl = 2, ii = 24)

function choose_covariates()
    wt = (55 + 25rand()) / 70
    return (; wt, logwt = log(wt))
end

pop = [Subject(; id = i, events = dose, covariates = choose_covariates()) for i = 1:72]
sims = simobs(
    covariate_saem_cov,
    pop,
    sim_params_covariate_cov;
    obstimes,
    ensemblealg = EnsembleSerial(),
)
reread_df = DataFrame(sims);

pop_covariate = read_pumas(reread_df; observations = [:dv], covariates = [:logwt, :wt])
Population
  Subjects: 72
  Covariates: logwt, wt
  Observations: dv

When specifying inits, it is not necessary to specify any variance parameters for the random effects or error models.

init_covariate = (; CL = (2.0, 2 / 3), v = 50.0)
(CL = (2.0, 0.6666666666666666),
 v = 50.0,)

If unspecified, they will be initialized to 1.0 or the identity matrix for covariances. SAEM's stochastic exploration phase is most effective when these variance parameters are much larger than the true variances. Thus, if the true variances are believed to be around 1.0 or larger, it is recommended to specify larger initial values manually.

It is possible to pass a vector of random number generates as an argument to fit. If so, the fit will use one thread per RNG. By specifying the seeds of each RNG, SAEM() can be fully reproducible:

rngv = [MersenneTwister(1941964947i + 1) for i ∈ 1:Threads.nthreads()];

fit_covariate_cov1 = fit(
    covariate_saem_cov,
    pop_covariate,
    init_covariate,
    SAEM();
    ensemblealg = EnsembleThreads(),
    rng = rngv,
)
rngv = [MersenneTwister(1941964947i + 1) for i ∈ 1:Threads.nthreads()];

fit_covariate_cov2 = fit(
    covariate_saem_cov,
    pop_covariate,
    init_covariate,
    SAEM();
    ensemblealg = EnsembleThreads(),
    rng = rngv,
)
coef(fit_covariate_cov1) == coef(fit_covariate_cov2) # true
true

One can also specify the number of iterations for each of the three phases (rapid exploration, convergence, smoothing):

fit_covariate_cov =
    fit(covariate_saem_cov, pop_covariate, init_covariate, SAEM(; iters = (1000, 500, 500)))
FittedPumasEMModel

Dynamical system type:                 Closed form

Number of subjects:                             72

Observation records:         Active        Missing
    dv:                        7272              0
    Total:                     7272              0

Number of parameters:      Constant      Optimized
                                  0              7

Likelihood approximation:                     SAEM

Log-likelihood value:                   -11750.833

-------------------
         Estimate
-------------------
CL₁       3.9484
CL₂       0.55084
v        74.268
Ω₁,₁      0.092881
Ω₂,₁      0.05207
Ω₂,₂      0.10366
σ         0.19674
-------------------

The results of a fit can be analyzed normally:

infer_cov = infer(fit_covariate_cov)
Asymptotic inference results using sandwich estimator

Dynamical system type:                 Closed form

Number of subjects:                             72

Observation records:         Active        Missing
    dv:                        7272              0
    Total:                     7272              0

Number of parameters:      Constant      Optimized
                                  0              7

Likelihood approximation:                     SAEM

Log-likelihood value:                   -11750.833

-----------------------------------------------------------
           Estimate   SE          95.0% C.I.
-----------------------------------------------------------
CL_base     3.9484    0.14463     [  3.6649    ;  4.2318 ]
CL_logwt    0.55084   0.28112     [ -0.00014309;  1.1018 ]
v_base     74.268     2.8578      [ 68.667     ; 79.869  ]
ω_1₁,₁      0.30476   0.02196     [  0.26172   ;  0.3478 ]
ω_1₂,₁      0.17085   0.031966    [  0.1082    ;  0.2335 ]
ω_1₂,₂      0.2729    0.021178    [  0.23139   ;  0.31441]
σ_0         0.19674   0.0017965   [  0.19321   ;  0.20026]
-----------------------------------------------------------
coeftable(infer_cov)
7×7 DataFrame
Rowparameterconstantestimateserelative_seci_lowerci_upper
StringBoolFloat64Float64Float64Float64Float64
1CL_basefalse3.948360.1446290.03663013.664894.23183
2CL_logwtfalse0.5508350.2811170.510346-0.0001430931.10181
3v_basefalse74.2682.857820.038479968.666879.8693
4ω_1₁,₁false0.3047640.02195960.07205460.2617240.347804
5ω_1₂,₁false0.1708520.03196560.1870960.10820.233503
6ω_1₂,₂false0.2728990.0211780.07760380.2313910.314407
7σ_0false0.1967360.001796530.009131710.1932150.200257
inspect_cov = inspect(fit_covariate_cov)
[ Info: Calculating predictions.
[ Info: Calculating weighted residuals.
[ Info: Calculating empirical bayes.
[ Info: Evaluating dose control parameters.
[ Info: Evaluating individual parameters.
[ Info: Done.