Skip to content

Commit

Permalink
Update udf info
Browse files Browse the repository at this point in the history
  • Loading branch information
mattfidler committed Nov 25, 2023
1 parent 7d36939 commit 68e160c
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 20 deletions.
3 changes: 1 addition & 2 deletions R/rxValidate.R
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

#' Validate rxode2
#' This allows easy validation/qualification of nlmixr by running the
#' testing suite on your system.
Expand Down Expand Up @@ -53,5 +54,3 @@ rxValidate <- function(type = NULL, skipOnCran=TRUE) {
#' @rdname rxValidate
#' @export
rxTest <- rxValidate


Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ When defining models you may have wished to write a small R function or make a f

## R based user functions

A R-based user function is the most convenient to include in the ODE, but is slower than what you could have done if it was written in `C` , `C++` or some other compiled language. This was requested [in github](https://github.com/nlmixrdevelopment/RxODE/issues/162#issue-568886732) with an appropriate example; However, I will use a very simple example here to simply illustrate the concepts.
A R-based user function is the most convenient to include in the ODE,
but is slower than what you could have done if it was written in `C` ,
`C++` or some other compiled language. This was requested [in
github](https://github.com/nlmixrdevelopment/RxODE/issues/162#issue-568886732)
with an appropriate example; However, I will use a very simple example
here to simply illustrate the concepts.

```{r fExample}
newAbs <- function(x) {
Expand All @@ -33,14 +38,19 @@ newAbs <- function(x) {
x
}
}
f <- rxode2({
a <- newAbs(time)
})
e <- et(-10, 10, length.out=40)
```

Now that the ODE has been compiled the R functions will be called while solving the ODE. Since this is calling R, this forces the parallization to be turned off since R is single-threaded. It also takes more time to solve since it is shuttling back and forth between R and C. Lets see how this very simple function performs:
Now that the ODE has been compiled the R functions will be called
while solving the ODE. Since this is calling R, this forces the
parallization to be turned off since R is single-threaded. It also
takes more time to solve since it is shuttling back and forth between
R and C. Lets see how this very simple function performs:

```{r benmchmark1}
mb1 <- microbenchmark::microbenchmark(withoutC=suppressWarnings(rxSolve(f,e)))
Expand All @@ -58,7 +68,7 @@ You can make it a better by converting the functions to C:
rxFun(newAbs)
# Recompile to use the C functions
# Note it would recompile anyway if you didn't do this step,
# it just makes sure that it doesn't recompile every step in
# it just makes sure that it doesn't recompile every step in
# the benchmark
f <- rxode2({
a <- newAbs(time)
Expand All @@ -71,7 +81,12 @@ autoplot(mb) + rxTheme() + xgxr::xgx_scale_y_log10()
print(mb)
```

The C version is almost twice as fast as the R version. You may have noticed the conversion also created C versions of the first derivative. This is done automatically and gives not just C versions of function, but C versions of the derivatives and registers them with `rxode2`. This allows the C versions to work with not only `rxode2` but `nlmixr2` models.
The C version is almost twice as fast as the R version. You may have
noticed the conversion also created C versions of the first
derivative. This is done automatically and gives not just C versions
of function, but C versions of the derivatives and registers them with
`rxode2`. This allows the C versions to work with not only `rxode2`
but `nlmixr2` models.

This function was setup in advance to allow this type of conversion. In general the derivatives will be calculated if there is not a `return()` statement in the user defined function. This means simply let R return the last value instead of explictly calling out the `return()`. Many people prefer this method of coding.

Expand All @@ -90,41 +105,60 @@ f_R <- function(actRad, k_0, a_k) {
rxFun(f_R)
```

While this is still helpful because some functions have early returns, the `nlmixr2` models requiring derivatives would be calculated be non-optimized finite differences when this occurs. While this gets into the internals of `rxode2` and `nlmixr2` you can see this more easily when calculating the derivatives:
While this is still helpful because some functions have early returns,
the `nlmixr2` models requiring derivatives would be calculated be
non-optimized finite differences when this occurs. While this gets
into the internals of `rxode2` and `nlmixr2` you can see this more
easily when calculating the derivatives:

```{r derExample}
rxFromSE("Derivative(f_R(actRad, k_0, a_k),k_0)")
```

Whereas the originally defined function `newAbs()` would use the new derivatives calculated as well:
Whereas the originally defined function `newAbs()` would use the new
derivatives calculated as well:

```{r derFromC}
rxFromSE("Derivative(newAbs(x),x)")
```

In some circumstances, the conversion to C is not possible, though you can still use the R function.
In some circumstances, the conversion to C is not possible, though you
can still use the R function.

There are some requirements for R functions to be integrated into the rxode2 system:
There are some requirements for R functions to be integrated into the
rxode2 system:

- The function must have a set number of arguments, variable arguments like `f(…)` are currently not allowed.
- The function must have a set number of arguments, variable
arguments like `f(…)` are currently not allowed.

- The function is given each argument as a single number, and the function should return a single number
- The function is given each argument as a single number, and the
function should return a single number

If these requirements are met you can use the R function in rxode2. Additional requirements for conversion to C include:
If these requirements are met you can use the R function in
rxode2. Additional requirements for conversion to C include:

- Any functions that you use within the R function must be understood and available to `rxode2`.
- Any functions that you use within the R function must be
understood and available to `rxode2`.

- Practically speaking if you have `fun2()` which refers to `fun1()`, `fun1()` must be changed to C code and available to `rxode2` before changing the function `fun2()` to C.
- Practically speaking if you have `fun2()` which refers to
`fun1()`, `fun1()` must be changed to C code and available to
`rxode2` before changing the function `fun2()` to C.

- The functions can include `if`/`else` assignments or simple return statements (either by returning a value or having that value on a line by itself). Special R control structures and functions (like `for` and `lapply`) cannot be present.
- The functions can include `if`/`else` assignments or simple return
statements (either by returning a value or having that value on a
line by itself). Special R control structures and functions (like
`for` and `lapply`) cannot be present.

- The function cannot refer to any package functions

- As mentioned, if the `return()` statement is present, the derivative C functions and `rxode2`'s derivative table is not updated.
- As mentioned, if the `return()` statement is present, the
derivative C functions and `rxode2`'s derivative table is not
updated.

## C based functions

You can add your own C functions directly into rxode2 as well using `rxFun()`:
You can add your own C functions directly into rxode2 as well using
`rxFun()`:

```{r rxFun}
fun <- "
Expand All @@ -133,7 +167,7 @@ fun <- "
}
" ## C-code for function
rxFun("fun", c("a", "b", "c"), fun)
rxFun("fun", c("a", "b", "c"), fun)
```

If you wanted you could also use C functions or expressions for the derivatives by using the `rxD()` function:
Expand All @@ -152,7 +186,8 @@ rxD("fun", list(
))
```

Removing the function with `rxRmFun()` will also remove the derivative table:
Removing the function with `rxRmFun()` will also remove the derivative
table:

```{r rxDrm}
rxRmFun("fun")
Expand Down

0 comments on commit 68e160c

Please sign in to comment.