---
title: "Simulate Models"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Simulate Models}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

```{r setup}
library(uqsa)
library(parallel)
library(errors)
```

In this package, there are two solvers available to you, solvers for
ODEs from the GNU Scientific Library (called via the `.Call`
interface), and a C implementation of the Gillespie algorithm. Here we
show how these are used on one of the included examples.

We make heavy use of closures: functions that were created in an
environment with access to implicit arguments such as simulation
experiments. The created closures have an explicit argument, such as
the Markov chain variable `parMCMC`, which in some way maps to the
parameters that the model can be simulated with `parModel`.

We assume that simulations require lots of arguments, but only one of
them will change every once in a while, the `parMCMC` variable; the
simulation experiments (initial state vector, input parameters, output
times, etc.) remain the same, and so does the model structure.

This closure approach allows you to hijack the procedure at any point
by constructing your own simulation closure that uses your solver of
convenience and/or necessity. You can write a function that accepts a
`parMCMC` vector and calls the solvers from the `deSolve`
package. Just make sure to package the simulation results in the same
way as our solvers do.

Typically a simulation result `y` has this structure: `y[[i]]` is the
simulation of `ex[[i]]` (where `ex` is the list of simulation
experiments, with data from the real experiments). Each `y[[i]]` has
the fields: `state`, `func`, `status`, `cpuSeconds`, `numSteps`. Only
`state` and `func` have scientific relevance. The `func` array
(3d-array), contains the measurable quantities for this model (the
observables): `y[[i]]$func[j,k,l]` is experiment `i`, output function
`j`, time-point `k` value for parameter set `l` if you are simulating
several parameter vectors in one call (otherwise `l` is exactly `1`).


# Load the AKAP79 Model

This model is included with the package. To load your own model (also
a collection of TSV files) call `model_from_tsv()` with a
character-vector of file names or the name of the directory that
contains the TSV files. To use an example model included with the
package, we use `uqsa_example()`:

```{r shared-library}
m <- model_from_tsv(uqsa_example("AKAP79"))
o <- as_ode(m)
c_path(o) <- write_c_code(generate_code(o))
so_path(o) <- shlib(o)
print(o)
if (!file.exists(so_path(o))) stop('creation of shared library failed')
```

By default, `as_ode` performs a conservation law analysis that can be
turned off with `as_ode(o,cla=FALSE)`.

With conservation laws, some species are calculated
algebraically. Their initial values are turned into input parameters
(using the found law):

With this hypothetical relationship ($c$ is a constant):

$$A+B = c\,,$$

we can determine that

$$c = A_0 + B_0\,.$$

And thus we can replace either of the two species:

$$A(t) = A_0 + B_0 - B(t)$$

And $A_0+B_0$ are turned into an input called `A_ConservedConst` (the
$c$ above) with the value determined from the stated, experiment
specific, initial condition.

# Load Experiments (data)

This list of data-sets also includes instructions for the simulator on
how to simulate the scenario with the model.

```{r experiments}
ex <- experiments(m,o)
```

We supply both `m` and `o` because `o` contains information about
whether or not conservation law analysis has occurred. With
conservation law analysis, we must adjust the input vector for the
simulation experiment and reduce the initial state for each simulation
to the dynamic variables rather (without the ones determined
algebraically). Currently, there is no way to influence which
variables are replaced by algebraic constraints. This can be
inconvenient if you are relying very heavily on the state component
of the result `y[[i]]$state` (where some variables are now missing,
due to conservation laws). This is why you should rely on the `func`
component of the return value. This directly corresponds to the
`m$Output` table: the observable/measurable quantities for this
model. If you need to compare substance `A` to the data (in some way),
then add an output function for it (e.g. `A_out` or `A_obs` with a
value of `A`). Then it doesn't matter whether the conservation law
analysis removes `A` from the state space; it remains part of the
`func` component.

# Simulate

This will make a function `s`, which will always simulate the
scenarios described in the `experiments` list, but for user supplied
parameters. The parameters in the tables are supplied in log10-space,
this is because the author of tat file intends to sample in
log10-space. This way the parameters are guaranteed to be positive and
we can very quickly sample several orders of magnitude for each
parameter. The simulator accepts a function in the `parMap` slot,
which can do any arbitrary transformation between the values you
supply to the simulator and the values it supplies the model with: the
model will be simulated with `parModel <- parMap(p)`, where `p` is
what we call the simulator with. If the sampling is done in log10
space, then the model accepts `10^p`. This package has a set of
functions that perform this reverse transformation from sampling space
to model-parameter space, based on which transformation the user
performed in the model files: log10ParMap belongs to a log10 sampling
space and performs `10^p` (to get back to the model-parameters):

```{r sim}
stopifnot(all(m$Parameter$scale=="log10")) # just to make sure
opt <- options(mc.cores = 2)               # required by CRAN to be 2, set this to a bigger value for yourself
s <- simulator.c(ex,o,parMap=log10ParMap)

p0 <- values(m$Parameter) # default parameters, in log10-space
rprior <- rNormalPrior(p0,rep(0.1,length(p0))) # a small neighborhood
y <- s(t(rprior(300)))
status <- unlist(lapply(y,\(E) as.logical(E$status))) # non-zero means an error occurred
print(status)
if (any(status)) stop("simulation failed.")
```

Next we plot the result:

```{r plotting, out.width="100%", res=200, fig.width=12, fig.height=10}
e.g. <- 18
plot(
	as.errors(ex[[e.g.]]$outputTimes), # exact
	ex[[e.g.]]$data,                   # uncertain
	xlab="time",
	ylab="AKAR4p",
	main=sprintf("AKAP79 model, expr. %i: %s",e.g.,names(ex)[e.g.]),
	ylim=c(90,200)
)
matplot(
	ex[[e.g.]]$outputTimes,
	y[[e.g.]]$func['AKAR4pOUT',,],
	lwd=2,                                   # fat
	col=rgb(200,0,255,12,maxColorValue=255), # low opacity
	lty=1,                                   # solid
	type="l",                                # line
	add=TRUE
)
```

There is also a specialized plot function for simulations, but we
subset the simulation results:

```{r plot-generic, out.width="100%", res=300, fig.width=12, fig.height=18}
print(class(y))
oldpar <- par()      # remember graphics values
on.exit(par(oldpar)) # restore original values
par(mfrow=c(3,2))
plot(ex[seq(6)],y[seq(6)],ylim=c(90,140))
```

This is an overview of the simulation:

```{r, label="print-y"}
print(y)
```

Reset options:

```
options(opt) # restores original values
```
