as.nonmem() and as.monolix() write the NONMEM control stream
or Monolix project of an nlmixr2 model without running NONMEM or
Monolix, with the file names of nonmemControl(modelName=) and
monolixControl(modelName=). Without data=, a one-subject
template dataset is made from the model; timeVarying= (none by
default) names the time-varying covariates, and dose=,
doseType=, ss=, cens= and limit= describe the doses and
observations.est="pknca" unit conversion scales the whole DV expression, so an
additive term (like a baseline) is converted too (#236)
NONMEM: names that differ only by case get one NONMEM name each for
the whole control stream, so the data columns, $INPUT, $MODEL,
$PK, $DES and $ERROR agree. Covariates ignore case (like
rxode2), so wt and WT are one data column; a variable or
compartment that clashes is renamed RXC001, ... (#237)
Monolix: a modeled rate() with f() infuses over f*amt/rate, the
rxode2 (and NONMEM) duration, instead of amt/rate (#239)
as.nlmixr2() of a nonmem2rx import solves with
rxControl(nonmem = TRUE, addlKeepsCov = FALSE), the way nonmem2rx
validates the import, when rxode2 supports it (nlmixr2/rxode2#1429)
and nlmixr2est keeps it through its fits (nlmixr2/nlmixr2est#1167).
Statements that do not depend on a state (NONMEM's $PK) then read
TIME as the time of the record ending each interval, like NONMEM,
instead of the continuous time; for TIME in $PK the IPRED
difference to NONMEM goes from 21.8% to 0.004% (#252). Models with
delay() keep the continuous time.
An empty Monolix administration table builds its type factor with
factor(levels=) instead of structure(.Label=), which R-devel's
R CMD check flags as deprecated.
Monolix imports (as.nlmixr2() of a monolix2rx model) no longer fail
when the project's steady-state dose count (nbdoses) is below 6:
minSS/maxSS come from monolix2rx's .getSsLimits(), raised to
rxode2's floor of 5 and 7.
simplifyUnit() and modelUnitConversion() work without the
suggested packages testthat and units installed: simplifyUnit()
checked its arguments with checkmate's expect_*() (testthat
expectations) instead of assert_*(), and both now ask for units
with rxode2::rxReq(). Their examples run only when units is
installed, and the PKNCA and saemix tests skip when those packages
are not installed (CRAN's noSuggests check).
Monolix (fourth stress kit run): EQUATION: only has assignments
and conditions, so PK macros there were rejected. The model lines
before the ODEs (with the compartment properties the macros use) are
now written in the PK: block before the macros, and the ODEs stay
in EQUATION:. Compartment properties set after the ODEs are moved
before them, and a property set in an if is set in every branch
(rxode2's default in the others) instead of from a default assigned
before the if, which Monolix cannot reassign. A missing LIMIT
is written as missing instead of -Inf.
Monolix projects Monolix could not load (third stress kit run, with Monolix's reasons):
if (SEX == 1) cl <- cl * 1.2, or
the pruned else if) gets a new name (cl_rx1); an if/else
that changes a defined variable is pruned first.f(), alag(), dur(), rate()) are defined in EQUATION:, so
the PK macros using them are now written in EQUATION: after them
(not in a PK: block before). A modeled rate uses
Tk0=rx_tk0_<state> with rx_tk0_<state> = amtDose/rate (macro
arguments cannot be calculations).monolixControl(stiff=TRUE) wrote odeType = stiff where Monolix
expects an input definition; it is now in EQUATION:.LIMIT on uncensored observations (M2) is refused: Monolix only
uses LIMIT with censored values.Monolix fits use the number of observations Monolix used for the
objective function, like NONMEM fits; it was off for data with
evid=2 records or time not starting at zero.
Monolix: a mu-referenced parameter that stays inside exp() in the
model (like cl <- exp(tcl + eta.cl) * (CRCL/100)^cl.crcl, or
f(depot) <- exp(lfdepot)) was given a log-normal distribution while
the model still took exp() of it, so exp() was applied twice.
It is now a normal parameter on nlmixr2's scale (its initial value,
estimate and covariance too).
When lixoftConnectors cannot load or run a Monolix project, the error
now includes Monolix's reason (its [ERROR] lines) instead of
"see Monolix's [ERROR] above".
Fixes found by running the NONMEM/Monolix stress kit with NONMEM 7.4:
nonmemControl(est="imp") and est="its" fits were always
reported as unsuccessful; NONMEM ends them with "OPTIMIZATION WAS
NOT TESTED FOR CONVERGENCE", which is now read as finished.nonmemControl(est="posthoc") fits could not be read (no #TERM:
block and no parameter history).ELSE IF
structures); the transformations are now written without nested
IFs. They are still transform-both-sides (DV in CCONTR, IPRED in
$ERROR). Predictions at or below sqrt(DBL_EPSILON) are floored
like rxode2 instead of being set to -1000000000 (which gave
lnorm() fits a huge objective function), and Box-Cox no longer
divides by zero when lambda is 0.IPRED/PRED comparison with NONMEM now undoes the residual
transformation first (NONMEM's predictions are on the transformed
scale).CENS, CENS + LIMIT, LIMIT only) models were
rejected by NM-TRAN ("random variable is defined in a nested IF
structure").$PK parameter changed later in the model (like
if (SEX == 1) cl <- cl * 1.2) was changed in $DES, which NONMEM
does not allow, and used undefined in $ERROR; it is now defined
as RXPK_<name> in $PK.METHOD=COND is invalid).Monolix fixes found by running the NONMEM/Monolix stress kit:
RATE) or duration (TINF) were written
to the data but not declared in [CONTENT], so Monolix gave them
as bolus doses.concentration.in.plasma) kept
the dots in the Monolix project (OUTPUT, observation names,
predictions), which Monolix cannot load.tfdepot <- fix(0.8))
failed with "subscript out of bounds"; Monolix's covariance has
only the estimated parameters.monolixControl(runCommand=) command that finished without
writing Monolix's output now stops with an error instead of
waiting for it forever.nonmemControl(cov="") now skips the covariance step as documented;
before it gave an error because match.arg() cannot match "".
The NONMEM/Monolix stress test (inst/stress) is now a kit to run on
a machine with NONMEM and/or Monolix: install-kit.R installs the
versions to test, run-stress.R --check finds NONMEM/Monolix, and
run-stress.R --kit runs every case end to end, checks that rxode2
reproduces the NONMEM/Monolix predictions and that a second fit reads
the saved output, and zips the results to send back. New cases cover
missing observations, evid=2, character IDs, time not starting at
zero, extra data columns, a single subject, oral plus iv doses, ODE
infusions (rate, steady state, modeled duration), two endpoints,
bounded thetas, several covariates, models without random effects
and the NONMEM/Monolix estimation options.
NONMEM control streams now use $ABBR PROTECT so NM-TRAN replaces
LOG, EXP, SQRT, division and powers with NONMEM's protected
functions (NONMEM 7.4 or later). Turn it off with
nonmemControl(protect=FALSE) or options(babelmixr2.nmProtect=FALSE).
Since NM-TRAN writes B**E as PEXP(E*PLOG(B)), integer powers are
written as products (or powers of x*x, which is never negative) and
powers of a positive number with DEXP(). Functions of numbers
(like exp(0), log(2*pi) or expit(0)) and divisions of numbers
are written as numbers, and model
variables named like a protected function (like plog) are renamed.
babelmixr2's own zero protection (protectZeros) is now only used
with protect=FALSE, like for NONMEM before 7.4 (#62).
NONMEM control streams now explain the zero-protection code
babelmixr2 adds: each RXDZ### IF block is preceded by a comment
saying what it keeps the variable away from and why, as is the
IF (W1 .EQ. 0.0) residual variance protection (#91).
The protection for lfactorial()/lgamma1p() now keeps its argument
above -1 (it previously clamped it just below -1, making x+1
negative), log(x) and 1/x no longer share one protected
variable, so 1/x keeps the sign of a negative x, and a variable
reassigned in the model is protected again instead of reusing the
protection of its old value. Zero protection needed by f(),
alag(), rate() or dur() (written in $PK) is no longer shared
with other lines, which could use it before (or without) it being
calculated.
est="pknca" now works with covariates in the data and with a mix of
intravascular and extravascular doses (#102). Doses into a
compartment that the observations are calculated from are
intravascular. With both routes, ka is estimated from the
extravascular doses and vc and cl from the intravascular doses.
Intravascular bolus doses have the concentration at the time of
dosing back-extrapolated (replacing a predose concentration at the
first dose), and other doses have it imputed (as the predose
concentration, or zero for the first dose). When no doses are only
extravascular, ka is not updated. Multiple-dose data no longer need
a concentration at each dose time; each dose until the next (with at
least 2 concentrations) is used, with vc from the first dose of each
route and cl from dosing intervals mostly covered by concentrations.
Doses at the same time are combined.
When a NONMEM run fails, est="nonmem" now says why and where to look
instead of failing with an unclear error: a run command that was not
found or wrote no output, a NONMEM license problem, an NM-TRAN error in
the control stream or the data, NONMEM not starting (for example a
compiler problem), NONMEM crashing or stopping during estimation, and
output babelmixr2 cannot read (#46).
est="pknca" now updates the initial estimates of models that are not
mu-referenced, like ka <- tka * exp(eta.ka), instead of failing with
"Must have names" (#101). When there is no vc, a central volume
named v, V, Vc, VC, v1 or V1 (only one of them) now gets
the NCA central volume estimate. Parameters defined as expit() of a
theta are now transformed back correctly. A message lists the
parameters that could not be updated.
NONMEM models can now use nested if/else if/else statements
(and ifelse()): their branches are pruned with rxode2's branch
pruning before the model is translated to NONMEM (#11). With the
default nonmemControl(prune="auto") a model whose if blocks are
simple is still written with NONMEM IF blocks and only a model that
needs it is pruned; prune=TRUE always prunes and prune=FALSE never
prunes (the error for unsupported if/else statements suggests
prune=TRUE).
monolixControl(prune=) has the same option for Monolix. Monolix
writes if/elseif/else (and nested if) statements directly, so
prune="auto" (the default) only prunes a model that uses ifelse(),
which Monolix cannot write; prune=TRUE always prunes and
prune=FALSE never prunes. A logical expression used as a number in
a Monolix model is written as a 0/1 indicator variable (#11).
A logical expression used as a number in a NONMEM model (like
cl <- tcl * (WT > 70)) is now written as a 0/1 indicator variable,
since NONMEM cannot use a logical expression as a number. A numeric
if () condition is written as not equal to zero.
est="nonmem" and est="monolix" fits with
table=tableControl(cwres=TRUE) no longer fail with "objective
function 'FOCEi' already present". The fit keeps both the NONMEM (or
Monolix) objective, which stays in use, and nlmixr2's FOCEi objective
(#94). as.nlmixr2() of a nonmem2rx or monolix2rx model now also
keeps the imported objective in use with cwres=TRUE, and no longer
fails after an earlier import with cwres=TRUE (it started from that
fit's etas).
est="monolix" now translates compartment properties (f(),
alag(), rate() and dur()) that are expressions, like
f(depot) <- exp(lfdepot), instead of erroring with "the complex F is
not supported by babelmixr2" (#115). The expression is calculated in
a new variable (like rx_f_depot) that the PK: macro uses. A
property set only inside an if now keeps rxode2's default otherwise
(1 for f(), 0 for alag()) instead of being applied unconditionally.
The ACoP 2024 babelmixr2/PopED abstract
(doi:10.70534/XUMG6226) is now
in citation("babelmixr2"), the package description and the PopED
article (#155).
as.nlmixr2() of a nonmem2rx model now stops with an informative
error when the model still contains untranslated NONMEM residual
variables (eps# or err#) instead of failing inside the estimation
routine (#95).
popedControl(sigdig=) now sets the ODE solver tolerances itself
(atol = rtol = 0.5*10^(-sigdig-2), the same values as before) instead
of using rxode2::rxControl(sigdig=). rxode2 5.1.5 loosened the
tolerances that rxControl(sigdig=) gives, which made the
finite-difference FIM, and so the design OFV and RSEs, less accurate
(#223).
est="nonmem" now fits censored data the way nlmixr2 does (#92). M3
(CENS), M4 (CENS with a finite LIMIT) and M2 (CENS=0 with a
finite LIMIT, including data with a LIMIT but no CENS column)
use F_FLAG likelihoods in $ERROR with LAPLACIAN estimation. A
missing LIMIT is now written as NONMEM's infinity instead of 0,
CENS/LIMIT columns that do not censor anything are dropped, the
objective function is adjusted so the log-likelihood includes the
censored observations correctly, and censoring with a transformed
endpoint (like lnorm()) is refused instead of giving the wrong
likelihood. The censored observations are left out of the NONMEM
PRED comparison since NONMEM's PRED is their likelihood.
est="nonmem" and est="monolix" now fit linCmt() models. A pure
linCmt() model uses NONMEM's closed-form solutions (ADVAN1-ADVAN4,
ADVAN11 or ADVAN12 with TRANS1 micro-constants) or Monolix's
pkmodel(); a model the closed form cannot represent (other ODEs,
parameters that change with time, and for Monolix modeled rates or
durations, doses into more than one compartment or amounts used in
the model) is translated to ODEs with rxode2::linToOde().
nonmemControl(linCmt="ode") and monolixControl(linCmt="ode")
always use ODEs. The closed-form solutions need an rxode2 with
linCmtMicro(); with an older rxode2, linCmt() models use ODEs.
est="poped" now designs linCmt() models (translated to ODEs).
est="nlmer", est="fmeMcmc" and est="pseudoOptim" are now tested
with linCmt() models.
New NONMEM/Monolix stress test (inst/stress/), shared by the package
tests and a command line runner (run-stress.R). The runner can also
fit every case with NONMEM and/or Monolix end to end on a machine that
has them, and optionally translate the nlmixr2lib models; see
inst/stress/README.md.
The NONMEM data dropped the RATE, SS and II items: which items
were written was decided from settings made only after the data were
converted, so infusions, steady state doses and modeled rates or
durations were written as plain bolus doses. The items are now kept
when the data use them.
A steady state dose with a lag time (which rxode2 splits in two) is now
written as one SS dose for NONMEM and Monolix, not as two bolus
doses.
NONMEM now writes a modeled duration as Dn (it was DURn, which
NONMEM does not know) and a modeled rate as Rn (it was dropped).
NONMEM now uses the NONMEM name of a mu-referenced covariate in $PK
(like NLMIXRMUDERCOV1), matching $INPUT.
probitInv() (which rxode2 writes with erf()) now translates to
NONMEM (PHI()) and Monolix (normcdf()).
est="monolix" now refuses models without between-subject
variability, residual errors other than add(), prop() and
add() + prop(), and transformations other than lnorm() and
logitNorm() up front with a clear error. Before, a boxCox(),
yeoJohnson() or logitNorm() residual stopped with "argument must be
a character string".
Monolix now separates the arguments of the empty() macro and the
logitNormal distribution with commas.
rxode2's normalized powers (Rx_pow_di(), Rx_pow()) now translate to
NONMEM (**) and Monolix (^).
NONMEM and Monolix data without doses (like a $PRED model with the
dose as a covariate) no longer stop with "undefined columns selected".
est="nonmem" now refuses residual transformations it cannot write
(like probitNorm()) up front; before, writing the files stopped with
"can only write character objects".
Monolix now applies a bioavailability or lag time to the doses of that
compartment only; a property of a compartment without doses no longer
stops with "values must be length 1". A parameter written with
brackets (like v <- (tv + eta.v)) is now a normal parameter, and an
unsupported parameter transformation (like log10()) gives a clear
error.
est="monolix" now spells a mu-referenced parameter with a . in its
name (like the tainted rx__cl.wt, or ka.x <- exp(tka + eta.ka)) the
same way in every section of the Monolix model (#220). The EQUATION:
block already wrote it as rx__cl__wt, but input=, [INDIVIDUAL],
<PARAMETER> and the output readers used rx__cl.wt, so Monolix saw an
undeclared variable.
est="saemix" now fits linCmt() models. The prediction was looked
up in a column named after the endpoint (rxLinCmt), which the solved
model does not output, so saemix stopped with non-numeric argument to function (#212).
est="saemix" now fits models where a structural theta has no
between-subject variability (e.g. v <- exp(tv)). Collecting the
individual etas after the fit failed with invalid subscript type 'list' (#212).
est="saemix" now refuses a model it cannot fit, instead of fitting it
with a different residual error. saemix fits one endpoint with an
add(), prop(), add() + prop() (combined2, the only combination
saemix has) or lnorm() residual error, or an ll() likelihood. A
model with more than one endpoint (previously fit against predictions of
zero), a combined1 add() + prop() (including
saemixControl(addProp="combined1")), pow(), boxCox(),
yeoJohnson(), a logit/probit transformation, lnorm() + prop() or a
non-normal residual distribution now stops with an error, as does a
fixed residual error or between-subject variability, which saemix
would otherwise estimate anyway. The checks use the new rxode2
assertions assertRxUiTransform(), assertRxUiErrType(),
assertRxUiAddProp(), assertRxUiNoFixedResiduals() and
assertRxUiNoFixedOmega(), so this requires rxode2 5.1.8 (#212).
est="saemix" now fits lnorm() residual errors with saemix's
exponential error model; they were previously fit as an additive error
with a missing starting value (#212).
est="monolix" now accepts normal priors from ini({}) and writes them
as Monolix MAP estimation (#207). A parameter with a prior is estimated
with method=MAP, and its prior is written to a [POPULATION] section
of <MODEL>. Monolix's prior on a typical value has the same
distribution as the parameter itself, with its sd in the Gaussian
space, so the prior mean is back-transformed like the estimate
(exp(), expit(), probitInv()) while the prior sd is written as is:
prior(tka) ~ dnorm(log(1.5), 0.5) becomes
ka_pop = {distribution=logNormal, typical=1.5, sd=0.5}, the same
distribution with no approximation. Covariate effects get a normal
prior. Checked with Monolix 2024R1: tight priors pin ka_pop,
cl_pop, a covariate effect and a logit-normal parameter at the prior
mean, and a vague prior leaves the estimate at the MLE. Priors Monolix
cannot honor are errors rather than being dropped: priors on omega
elements or omega blocks, multivariate normal priors, non-normal
priors, priors on a probitInv() parameter with bounds other than
(0, 1), and priors on residual error parameters -- Monolix accepts a
MAP prior on add__sd but ignores it (every estimate identical to the
run without it).
The "PRED absolute difference compared to Monolix PRED" line of a Monolix
fit's message is now an absolute difference (it printed a relative one).
The covariance of a fit from Monolix 2020 or later now carries nlmixr2's
parameter names (tka, cl.wt) instead of Monolix's (ka_pop,
beta_cl_lWT), like fits from older Monolix versions already did.
Monolix projects with mu-referenced covariates (cl <- exp(tcl + eta.cl + cl.wt * lWT)) now load in Monolix. The covariate was missing from the
[INDIVIDUAL] inputs (Monolix: Undefined variable 'lWT'), and when
every covariate was mu-referenced the structural model got a regressor
line with no name (= {use=regressor}, a syntax error). Reading the
results of such a fit back failed with subscript out of bounds: the
covariance looked up the covariate effect as NA_pop instead of
beta_cl_lWT. The tests now replay Monolix 2024R1 runs, with and
without MAP priors. When
lixoftConnectors cannot load or run the project, nlmixr2() now stops
with an error instead of waiting forever for output Monolix never
writes.
est="nonmem" now runs models with ini({}) priors, translating them
to NONMEM's $PRIOR NWPRI (#205). Normal priors on population
parameters (dnorm(), stdNormal(), the tcl + tv ~ c(...) joint
normal) become $THETAP/$THETAPV, and invWishart(nu) degrees of
freedom on an omega block become $OMEGAP/$OMEGAPD, with the block's
own initial estimate as the prior scale. NWPRI gives its priors to the
first THETAs and the first omega blocks, so the parameters with a prior
have to come first in ini({}); otherwise, and for priors NWPRI cannot
express (dcauchy(), a normal prior directly on an omega element, which
is TNPRI), the model is refused before any file is written instead of
fitting a different prior. When the output is read back, the prior
values NM-TRAN adds as extra THETAs and OMEGAs are dropped, and the
objective function type says nwpri because NONMEM's objective
includes the prior.
$OMEGA BLOCK() records of 3 or more etas are now written in the order
NONMEM reads them (row by row down the lower triangle). They used to be
written column by column, so NONMEM started from the wrong initial
omega values.
The $PROBLEM record of a generated NONMEM control stream now carries
the model name ($PROBLEM one.cmt translated from babelmixr2). It read
a misspelled getter and was always blank (#209). Because the control
stream changes, an existing NONMEM export that has a .md5 hash file
will not match and is re-run once in a new numbered directory.
Moving past a second stale export (-001-nonmem also not matching) no
longer hangs: the export directory kept its cached number and the
hash check looped forever.
est="fmeMcmc" now uses priors declared in the model's ini({}) block
(for example prior(tka) ~ dnorm(0, 10)) instead of refusing the model.
They become the prior function FME::modMCMC() samples with,
evaluated with rxode2's shared prior kernel on the natural parameter
scale, even when scaleType makes FME sample a rescaled space.
Supplying fmeMcmcControl(prior=) as well is an error rather than
silently preferring one of them (#208).
nonmemControl(est="its") now writes $ESTIMATION METHOD=ITS INTERACTION (iterative two stage). It wrote METHOD=IMP, so NONMEM
ran importance sampling while the returned fit was labeled with the
nonmem its objective function type (#211).
A PopED design dataset that gives cmt as a compartment number
(et(amt=180, cmt=1)) now doses the right compartment. et() keeps
cmt as a character column, so rxode2::etTrans() read "1" as a
compartment name, found no match and quietly moved the dose to an
extra compartment; the design built without a warning but every
prediction was zero and the FIM was degenerate (#201). This also works
when the column mixes names and numbers, which is what a multiple
endpoint design looks like when it names the endpoint on its
observation records. A dosing record that still cannot be matched to a
model compartment is now an error instead of a silently empty design.
A multiple endpoint PopED design can now name its endpoints with cmt
(cmt="cp", cmt="eff") instead of dvid. The cmt fallback was
already written but unreachable: a dataset without a dvid column
stopped with attempt to select less than one element in get1index
before it was tried. This applies to the usual design space; a design
that gives per-ID sampling through popedControl(a=) still needs
dvid.
The PopED model translation no longer drops the if () condition that
guards an adaptive dosing call (evid_(), bolus(), infuse(),
infuseDur(), reset(), ...). The branch pruner used to flatten the
model unconditionally, so a model like if (t <= 0) infuseDur(DOSE, TINF, cmt=1) pushed a dose at every design point instead of once
(#131). The pruner's capture protocol is now used and the guarded call
is restored after the branches are flattened.
Added two PopED examples showing how to make the dosing regimen itself optimizable (#131):
inst/poped/ex.10.PKPD.HCV.dose-and-tinf.babelmixr2.R keeps the dose
record and makes the amount and infusion duration design (a)
variables via f(depot) <- DOSE (with amt=1) and
dur(depot) <- TINF (with rate=-2).
inst/poped/ex.10.PKPD.HCV.adaptive-dosing.babelmixr2.R drops the
dose records entirely and pushes the regimen from inside the model
with infuseDur(), which makes the dosing interval a design
variable as well. This one needs rxode2 > 5.1.7 (rxode2#1214).
Both are optimized with poped_optim(..., opt_a=TRUE) and agree on the
reference design (OFV 88.27).
The NONMEM/Monolix fit cache is now written with saveRDS() as
<model>.rds / nlmixr.rds instead of qs2, so qs2 moved from
Imports to Suggests. Existing run directories keep working: a
.qs2 cache is read once (when qs2 is installed) and rewritten as
the .rds, and if it cannot be read the fit is rebuilt from the run
output as it would be for any missing cache.
Each estimation method now carries type and description attributes so it
appears in the category-grouped method list nlmixr2est prints for an
unsupported est= (or a bare nlmixr2() call): nonmem, monolix, pknca,
fmeMcmc and pseudoOptim under "External", saemix under "Stochastic EM",
nlmer under "Integral approximation", and poped under "Optimal Design".
The mu-referenced covariate algorithm (muRefCovAlg) is now applied
through the nlmixr2est preprocessing/post-final-object hooks instead
of explicit nlmixr2est::.uiApplyMu2()/.uiFinalizeMu2() calls in the
saemix, nonmem, monolix, and nlmer estimation methods (#184).
The nonmem and monolix methods gained the mu method attribute so
the hooks fire for them.
The nlmer estimation method now prints its iterations during the
lme4::nlmer optimization and records a parameter history, both driven
by the shared nlmixr2est nlm machinery (not lme4). Each recorded
nlmerSolveGrad() evaluation logs the population parameter estimate
(per-subject mean of the phi columns) into the resident nlm scale;
the accumulated history is recovered via nlmixr2est::nlmGetParHist()
and stored on the fit as parHistData. No objective column is shown
(lme4 owns the deviance). Iteration printing defaults on
(nlmerControl(print = 1L)). Requires nlmixr2est (>= 6.2.0).
The pseudoOptimControl() and fmeMcmcControl() functions now
accept either the legacy scalar print / printNcol / useColor
arguments or a pre-built nlmixr2est::iterPrintControl() object via
print. Internally the control list stores a single
iterPrintControl sub-list (matching the upstream nlmixr2est
unification in nlmixr2est PR #651), so iteration output from these
estimators uses the same shared C++ formatter as every other
nlmixr2est estimator. Requires nlmixr2est (>= 6.0.1).
The iterPrintControl unification now also covers nlmerControl()
and saemixControl(). nlmerControl() gains the standard print /
printNcol / useColor arguments (or a pre-built
nlmixr2est::iterPrintControl() object) and feeds the resulting
iterPrintControl sub-list to the nlm C solving engine instead of a
hard-coded print = 0L. saemixControl() absorbs its legacy
print (logical), printNcol and useColor arguments into the same
iterPrintControl sub-list; a nonzero every enables the saemix
progress output.
Fix NONMEM export silently dropping the absorption lag (#190). A
lag(depot)/alag(depot) assignment computed the lag parameter in $PK
but never emitted the corresponding ALAG<n>= statement, so NONMEM fit the
model without any lag. The lag value is now assigned to ALAG<n> in $PK.
NONMEM export now announces when a model variable is renamed because it
collides with a NONMEM reserved name (e.g. a variable named alag becomes
RXR1). The rename was previously silent (#190).
Added nlmer estimation method: fits nlmixr2 models via lme4::nlmer using
analytical gradients from rxode2 sensitivity equations. Supports
mu-referenced and non-mu-referenced random-effects models. Access via
nlmixr(model, data, est = "nlmer"). The underlying lme4 fit is stored as
fit$nlmer.
Fix integer type safety in C++ source: loop variables and size variables now
use R_xlen_t (signed) or size_t (unsigned) instead of int/unsigned int where appropriate, preventing potential integer overflow and segfaults
for vectors with more than 2^31 elements. The specific crash: in
getDvid(), int j = cmtDvid.size() when cmtDvid.size() ≥ 2^31 wraps to
INT_MIN, the subsequent decrement jumps to INT_MAX, and
cmtDvid[INT_MAX] accesses memory far out of bounds.
Add bounds check in popedSolveIdME() and popedSolveIdME2() to verify
that modelSwitch values are within the allocated matrix column dimensions
(nend), in addition to the existing check against the number of unique IDs
in the global time indexer.
Remove qs since it will be archived and replace with qs2.
Added saemix estimation method
units (#179)Added estimation method fmeMcmc which runs FME::modMCMC(). It
is also compatible with the coda package; you can convert with
as.mcmc(fit) and then run coda tools like
coda::raftery.diag(coda::as.mcmc(fit2)).
Added estimation method pseudoOptim which runs
FME::pseudoOptim(). This estimation method requires all parameters
to be bound.
Added bug fix for rstudio completion
Use new nlmixr2est covariate selection enforcement for babelmixr2
Fix a bug where the NONMEM export isn't working well (#839)
Check loaded rxode2 information and compare to what the loaded
model information should be. This allows better checking of which
model is loaded and even more robust stability. It requires
rxode2 > 3.0.2.
Fix bug where PopED could error with certain dvid values
Fix bug where if/else clauses in the model could cause the model to not predict the values correctly.
Fix bug so that shrinkage() calculation works
Fix bug so that you can mix 2 different PopED data bases in an
analysis without crashing R. While this didn't occur with every
database clash, it more frequently occurred when you interleaved
PopED code between two different PopED databases, like in issue
#131.
Added a new function babelBpopIdx(poped.db, "par") which will get
the poped index for a model generated from babelmixr2, which is
useful when calculating the power (as in example 11).
Added experimental PopED integration
Removed dependence on rxode2parse
Imported monolix2rx from the monolix2rx package
Also allow conversion of a model imported from monolix to a
nlmixr2 fit.
Changed default NONMEM rounding protection to FALSE
Added a run option to the monolixControl() and nonemControl()
in case you only want to export the modeling files and not run the
models.
Handle algebraic mu expressions
PKNCA controller now contains rxControl since it is used for some
translation options
This revision will load the pruned ui model to query the compartment properties (i.e. bioavailability, lag time, etc) when writing out the NONMEM model. It should fix issues where the PK block does not define some of the variables and will have a larger calculated variable that can be used in the model instead.
When nonmem2rx has a different lst file, as long as
nonmem2rx::nminfo(file) works, then a successful conversion to a
nlmixr2 fit object will occur.
Fix to save parameter history into $parHistData to accommodate
changes in focei's output ($parHist is now derived).
Changed the solving options to match the new steady state options in
rxode2 and how NONMEM implements them. Also changed the iwres
model to account for the rxerr. instead of the err. which was
updated in rxode2 as well.
Add new method as.nlmixr2 to convert nonmem2rx methods to nlmixr fits
Dropped pmxTools in favor of nonmem2rx to conserve some of the
methods
Babelmixr has support for "monolix", "nonmem", and "pknca" methods on release.
Added a NEWS.md file to track changes to the package.