diff --git a/NAMESPACE b/NAMESPACE index f428efd33..eb9776135 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -157,7 +157,13 @@ S3method(rxSolve,rxUi) S3method(rxSolve,rxode2tos) S3method(rxTrans,character) S3method(rxTrans,default) +S3method(rxUdfUi,default) S3method(rxUdfUi,linMod) +S3method(rxUdfUi,linMod0) +S3method(rxUdfUi,linModA) +S3method(rxUdfUi,linModA0) +S3method(rxUdfUi,linModB) +S3method(rxUdfUi,linModB0) S3method(rxUiDeparse,default) S3method(rxUiDeparse,lotriFix) S3method(rxUiDeparse,rxControl) diff --git a/R/err.R b/R/err.R index 38cb69bb2..3a9210dcb 100644 --- a/R/err.R +++ b/R/err.R @@ -1134,7 +1134,7 @@ rxErrTypeCombine <- function(oldErrType, newErrType) { })) if (length(.y) != .len) { # Update the lengths of lstChr, lstErr, lstExpr - .len <- length(.cur)+ length(.env$before) + length(.env$after) + .len <- length(.env$before) + length(.env$after) .env$lstChr <- c(.env$lstChr, character(.len)) .env$lstErr <- c(.env$lstErr, vector(.len, mode="list")) .env$lstExpr <- c(.env$lstExpr, vector(.len, mode="list")) diff --git a/R/rudf.R b/R/rudf.R index f7ac6dfe9..55a9844be 100644 --- a/R/rudf.R +++ b/R/rudf.R @@ -610,9 +610,14 @@ rxUdfUi <- function(num, fun, iniDf) { UseMethod("rxUdfUi") } -.linMod <- function(num, fun, iniDf, intercept=TRUE) { +.linMod <- function(num, fun, iniDf, intercept=TRUE, type=c("replace", "before", "after")) { + type <- match.arg(type) .var <- fun[[2]] .pow <- fun[[3]] + if (!checkmate::testIntegerish(.pow, lower=ifelse(intercept, 0L, 1L), len=1L)) { + stop("linCmt(", .var, ", ", .pow, ") needs to have an integer >= ", ifelse(intercept, 0L, 1L), + call.=FALSE) + } .pre <- paste0("rx.linMod.", .var, num, base::letters[seq_len(.pow+ifelse(intercept, 1L, 0L))]) .theta <- iniDf[!is.na(iniDf$ntheta),,drop=FALSE] if (length(.theta$ntheta) > 0L) { @@ -639,34 +644,73 @@ rxUdfUi <- function(num, fun, iniDf) { } .eta <- iniDf[is.na(iniDf$neta),,drop=FALSE] .iniDf <- rbind(.theta, .eta) - list(replace=paste(vapply(seq_along(.pre), - function(i) { - if (intercept) { - if (i == 1) return(.pre[i]) - if (i == 2) return(paste0(.pre[i], "*", .var)) - paste0(.pre[i], "*", paste0(.var,"^", i-1L)) - } else { - if (i == 1) return(paste0(.pre[i], "*", .var)) - paste0(.pre[i], "*", paste0(.var,"^", i)) - } - }, character(1)), collapse="+"), - iniDf=.iniDf) + .linMod <- paste(vapply(seq_along(.pre), + function(i) { + if (intercept) { + if (i == 1) return(.pre[i]) + if (i == 2) return(paste0(.pre[i], "*", .var)) + paste0(.pre[i], "*", paste0(.var,"^", i-1L)) + } else { + if (i == 1) return(paste0(.pre[i], "*", .var)) + paste0(.pre[i], "*", paste0(.var,"^", i)) + } + }, character(1)), collapse="+") + if (type == "replace") { + list(replace=.linMod, + iniDf=.iniDf ) + } else if (type == "before") { + .replace <- paste0("rx.linMod.", .var, ".f", num) + list(before=paste0(.replace, " <- ", .linMod), + replace=.replace, + iniDf=.iniDf) + } else if (type == "after") { + .replace <- paste0("rx.linMod.", .var, ".f", num) + list(after=paste0(.replace, " <- ", .linMod), + replace="0", + iniDf=.iniDf) + } + } #' @export rxUdfUi.linMod <- function(num, fun, iniDf) { - .linMod(num, fun, iniDf, intercept=TRUE) + .linMod(num, fun, iniDf, intercept=TRUE, type="replace") } attr(rxUdfUi.linMod, "nargs") <- 2L #' @export rxUdfUi.linMod0 <- function(num, fun, iniDf) { - .linMod(num, fun, iniDf, intercept=FALSE) + .linMod(num, fun, iniDf, intercept=FALSE, type="replace") } attr(rxUdfUi.linMod, "nargs") <- 2L +#' @export +rxUdfUi.linModB <- function(num, fun, iniDf) { + .linMod(num, fun, iniDf, intercept=TRUE, type="before") +} +attr(rxUdfUi.linMod, "nargs") <- 2L + +#' @export +rxUdfUi.linModB0 <- function(num, fun, iniDf) { + .linMod(num, fun, iniDf, intercept=FALSE, type="before") +} +attr(rxUdfUi.linMod, "nargs") <- 2L + +#' @export +rxUdfUi.linModA <- function(num, fun, iniDf) { + .linMod(num, fun, iniDf, intercept=TRUE, type="after") +} +attr(rxUdfUi.linMod, "nargs") <- 2L + +#' @export +rxUdfUi.linModA0 <- function(num, fun, iniDf) { + .linMod(num, fun, iniDf, intercept=FALSE, type="after") +} +attr(rxUdfUi.linMod, "nargs") <- 2L + +#' @export rxUdfUi.default <- function(num, fun, iniDf) { - stop("rxode2 user defined function '", fun, "' not supported", call.=FALSE) + stop("rxode2 user defined function '", fun, "' not supported", call.=FALSE) # nocov } #' Get the number of arguments for user defined functions for ui diff --git a/R/rxUiBlessed.R b/R/rxUiBlessed.R index efce3d7f8..33e76cfee 100644 --- a/R/rxUiBlessed.R +++ b/R/rxUiBlessed.R @@ -5,5 +5,5 @@ "modelName", "mu2RefCovariateReplaceDataFrame", "muRefCovariateDataFrame", "muRefCovariateEmpty", "muRefCurEval", "muRefDataFrame", "muRefDropParameters", "muRefExtra", "muRefExtraEmpty", "mv0", - "mvL", "nonMuEtas", "oneTheta", "predDf", "singleTheta", + "mvL", "nonMuEtas", "oneTheta", "predDf", "redo", "singleTheta", "sticky", "thetaLhsDf") diff --git a/man/rxode2.Rd b/man/rxode2.Rd index f25f6c609..908b50cc3 100644 --- a/man/rxode2.Rd +++ b/man/rxode2.Rd @@ -232,9 +232,6 @@ mod <- rxode2(\{ d/dt(centr) <- F*KA*depot - CL*C2 - Q*C2 + Q*C3; \}) }\if{html}{\out{}} - -\if{html}{\out{
}}\preformatted{## using C compiler: ‘gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0’ -}\if{html}{\out{
}} \itemize{ \item Inside a \code{rxode2("")} string statement: }\if{html}{\out{ @@ -250,9 +247,6 @@ mod <- rxode2(\{ d/dt(centr) <- F*KA*depot - CL*C2 - Q*C2 + Q*C3; ") }\if{html}{\out{}} - -\if{html}{\out{
}}\preformatted{## using C compiler: ‘gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0’ -}\if{html}{\out{
}} \itemize{ \item In a file name to be loaded by rxode2: }\if{html}{\out{ @@ -329,30 +323,11 @@ creates a parsed \code{rxode2} ui that can be translated to the \code{rxode2} compilation model. \if{html}{\out{
}}\preformatted{mod$simulationModel -}\if{html}{\out{
}} - -\if{html}{\out{
}}\preformatted{## using C compiler: ‘gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0’ -## rxode2 3.0.0 model named rx_632db1954a006dfe249974d0974ba18b model (ready). -## x$state: depot, center -## x$stateExtra: cp -## x$params: tka, tcl, tv, add.sd, eta.ka, eta.cl, eta.v, rxerr.cp -## x$lhs: ka, cl, v, cp, ipredSim, sim -}\if{html}{\out{
}} - -\if{html}{\out{
}}\preformatted{# or +# or mod$simulationIniModel }\if{html}{\out{
}} -\if{html}{\out{
}}\preformatted{## using C compiler: ‘gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0’ - -## rxode2 3.0.0 model named rx_7e1fb9007822c2542d741c71c9c37dd4 model (ready). -## x$state: depot, center -## x$stateExtra: cp -## x$params: tka, tcl, tv, add.sd, eta.ka, eta.cl, eta.v, rxerr.cp -## x$lhs: ka, cl, v, cp, ipredSim, sim -}\if{html}{\out{
}} - This is the same type of function required for \code{nlmixr2} estimation and can be extended and modified by model piping. For this reason will be focused on in the documentation. diff --git a/tests/testthat/test-udf.R b/tests/testthat/test-udf.R index 36a350cf5..adac710c4 100644 --- a/tests/testthat/test-udf.R +++ b/tests/testthat/test-udf.R @@ -394,14 +394,13 @@ test_that("udf type 2 (that changes ui models upon parsing)", { tmp <- f() expect_equal(tmp$iniDf$name, - c("d", "rx.linMod.time1a", "rx.linMod.time1b", "rx.linMod.time1c", - "rx.linMod.time1d")) + c("d", "rx.linMod.time1a", "rx.linMod.time1b", "rx.linMod.time1c", + "rx.linMod.time1d")) expect_equal(modelExtract(tmp, a), "a <- (rx.linMod.time1a + rx.linMod.time1b * time + rx.linMod.time1c * time^2 + rx.linMod.time1d * time^3)") # Test a linear model construction without an intercept - f <- function() { ini({ d <- 4 @@ -414,7 +413,7 @@ test_that("udf type 2 (that changes ui models upon parsing)", { tmp <- f() expect_equal(tmp$iniDf$name, - c("d", "rx.linMod.time1a", "rx.linMod.time1b", "rx.linMod.time1c")) + c("d", "rx.linMod.time1a", "rx.linMod.time1b", "rx.linMod.time1c")) expect_equal(modelExtract(tmp, a), @@ -438,10 +437,82 @@ test_that("udf type 2 (that changes ui models upon parsing)", { c("d", "rx.linMod.time1a", "rx.linMod.time1b", "rx.linMod.time1c", "rx.linMod.time1d", "rx.linMod.time2a", "rx.linMod.time2b", "rx.linMod.time2c", "rx.linMod.time2d")) - expect_equal(modelExtract(tmp, a), - "a <- (rx.linMod.time1a + rx.linMod.time1b * time + rx.linMod.time1c * time^2 + rx.linMod.time1d * time^3)") + expect_equal(modelExtract(tmp, a), + "a <- (rx.linMod.time1a + rx.linMod.time1b * time + rx.linMod.time1c * time^2 + rx.linMod.time1d * time^3)") + + expect_equal(modelExtract(tmp, b), + "b <- (rx.linMod.time2a + rx.linMod.time2b * time + rx.linMod.time2c * time^2 + rx.linMod.time2d * time^3)") + + + f <- function() { + ini({ + d <- 4 + }) + model({ + a <- linModB(time, 3) + b <- d + }) + } + + tmp <- f() + + expect_equal(modelExtract(tmp, rx.linMod.time.f1), + "rx.linMod.time.f1 <- rx.linMod.time1a + rx.linMod.time1b * time + rx.linMod.time1c * time^2 + rx.linMod.time1d * time^3") + + expect_equal(modelExtract(tmp, a), + "a <- (rx.linMod.time.f1)") + + f <- function() { + ini({ + d <- 4 + }) + model({ + a <- linModB0(time, 3) + d + }) + } + + tmp <- f() + + expect_equal(modelExtract(tmp, rx.linMod.time.f1), + "rx.linMod.time.f1 <- rx.linMod.time1a * time + rx.linMod.time1b * time^2 + rx.linMod.time1c * time^3") + + expect_equal(modelExtract(tmp, a), + "a <- (rx.linMod.time.f1) + d") + + f <- function() { + ini({ + d <- 4 + }) + model({ + a <- linModA(time, 1) + d + }) + } + + tmp <- f() + + expect_equal(modelExtract(tmp, rx.linMod.time.f1), + "rx.linMod.time.f1 <- rx.linMod.time1a + rx.linMod.time1b * time") + + expect_equal(modelExtract(tmp, a), + "a <- (0) + d") + + f <- function() { + ini({ + d <- 4 + }) + model({ + a <- linModA0(time, 1) + d + }) + } + + tmp <- f() + + expect_equal(modelExtract(tmp, rx.linMod.time.f1), + "rx.linMod.time.f1 <- rx.linMod.time1a * time") + + expect_equal(modelExtract(tmp, a), + "a <- (0) + d") + - expect_equal(modelExtract(tmp, b), - "b <- (rx.linMod.time2a + rx.linMod.time2b * time + rx.linMod.time2c * time^2 + rx.linMod.time2d * time^3)") })