Jeremy Goldhaber-Fiebert
2007-Jun-06 16:17 UTC
[R] Using odesolve to produce non-negative solutions
Hello,
I am using odesolve to simulate a group of people moving through time and
transmitting infections to one another.
In Matlab, there is a NonNegative option which tells the Matlab solver to keep
the vector elements of the ODE solution non-negative at all times. What is the
right way to do this in R?
Thanks,
Jeremy
P.S., Below is a simplified version of the code I use to try to do this, but I
am not sure that it is theoretically right
dynmodel <- function(t,y,p)
{
## Initialize parameter values
birth <- p$mybirth(t)
death <- p$mydeath(t)
recover <- p$myrecover
beta <- p$mybeta
vaxeff <- p$myvaxeff
vaccinated <- p$myvax(t)
vax <- vaxeff*vaccinated/100
## If the state currently has negative quantities (shouldn't have), then
reset to reasonable values for computing meaningful derivatives
for (i in 1:length(y)) {
if (y[i]<0) {
y[i] <- 0
}
}
S <- y[1]
I <- y[2]
R <- y[3]
N <- y[4]
shat <- (birth*(1-vax)) - (death*S) - (beta*S*I/N)
ihat <- (beta*S*I/N) - (death*I) - (recover*I)
rhat <- (birth*(vax)) + (recover*I) - (death*R)
## Do we overshoot into negative space, if so shrink derivative to bring state
to 0
## then rescale the components that take the derivative negative
if (shat+S<0) {
shat_old <- shat
shat <- -1*S
scaled_transmission <- (shat/shat_old)*(beta*S*I/N)
ihat <- scaled_transmission - (death*I) - (recover*I)
}
if (ihat+I<0) {
ihat_old <- ihat
ihat <- -1*I
scaled_recovery <- (ihat/ihat_old)*(recover*I)
rhat <- scaled_recovery +(birth*(vax)) - (death*R)
}
if (rhat+R<0) {
rhat <- -1*R
}
nhat <- shat + ihat + rhat
if (nhat+N<0) {
nhat <- -1*N
}
## return derivatives
list(c(shat,ihat,rhat,nhat),c(0))
}
Jeremy Goldhaber-Fiebert
2007-Jun-06 16:31 UTC
[R] Fwd: Using odesolve to produce non-negative solutions
Hello,
I am using odesolve to simulate a group of people moving through time and
transmitting infections to one another.
In Matlab, there is a NonNegative option which tells the Matlab solver to keep
the vector elements of the ODE solution non-negative at all times. What is the
right way to do this in R?
Thanks,
Jeremy
P.S., Below is a simplified version of the code I use to try to do this, but I
am not sure that it is theoretically right
dynmodel <- function(t,y,p)
{
## Initialize parameter values
birth <- p$mybirth(t)
death <- p$mydeath(t)
recover <- p$myrecover
beta <- p$mybeta
vaxeff <- p$myvaxeff
vaccinated <- p$myvax(t)
vax <- vaxeff*vaccinated/100
## If the state currently has negative quantities (shouldn't have), then
reset to reasonable values for computing meaningful derivatives
for (i in 1:length(y)) {
if (y[i]<0) {
y[i] <- 0
}
}
S <- y[1]
I <- y[2]
R <- y[3]
N <- y[4]
shat <- (birth*(1-vax)) - (death*S) - (beta*S*I/N)
ihat <- (beta*S*I/N) - (death*I) - (recover*I)
rhat <- (birth*(vax)) + (recover*I) - (death*R)
## Do we overshoot into negative space, if so shrink derivative to bring state
to 0
## then rescale the components that take the derivative negative
if (shat+S<0) {
shat_old <- shat
shat <- -1*S
scaled_transmission <- (shat/shat_old)*(beta*S*I/N)
ihat <- scaled_transmission - (death*I) - (recover*I)
}
if (ihat+I<0) {
ihat_old <- ihat
ihat <- -1*I
scaled_recovery <- (ihat/ihat_old)*(recover*I)
rhat <- scaled_recovery +(birth*(vax)) - (death*R)
}
if (rhat+R<0) {
rhat <- -1*R
}
nhat <- shat + ihat + rhat
if (nhat+N<0) {
nhat <- -1*N
}
## return derivatives
list(c(shat,ihat,rhat,nhat),c(0))
}
Thomas Petzoldt
2007-Jun-14 09:03 UTC
[R] Using odesolve to produce non-negative solutions
Dear Jeremy, a few notes about your model: The equations of your derivatives are designed in a way that can lead to negative state variables with certain parameter combinations. In order to avoid this, you are using "if constructions" which are intended to correct this. This method is however (as far as I have learned from theory and own experience ;-) a bad idea and should be strictly avoided. This trick may violate assumptions of the ODE solvers and in many cases also mass-balances (or similar). Instead of this, processes should be modeled in a way that avoids "crossing zero", e.g. exponential decay (x, k > 0): dx = - k * x (1) and not a linear decay like dx = -k (2) which by its nature can lead to negative state values. Case (1) can be managed almost perfectly by lsoda with his automatic internal time step algorithm. hmax is intended for non-autonomous models to ensure that external signals are not skipped by the automatism, which may be appropriate in your case because p seems to contain time dependent functions. As far as I can see, your equations belong to type (1) and should not need any of the if and for constructs, as long as your parameters (which are not given in your post) do have the correct sign and range (for example, vax <= 1, death >= 0 etc.). If you perform optimization, you must ensure that parameters stay in the plausible range is met using transformations of the parameters or constraints in the optimization procedure. Thomas PS: another question, what is the purpose of the state variable N? I guess it can be derived from the other states. Jeremy Goldhaber-Fiebert wrote:> Hello, > > I am using odesolve to simulate a group of people moving through time > and transmitting infections to one another. > > In Matlab, there is a NonNegative option which tells the Matlab > solver to keep the vector elements of the ODE solution non-negative > at all times. What is the right way to do this in R? > > Thanks, Jeremy[... code deleted, see original post ...]
If you didn't get this solved.
I have done parameter estimation with models
defined by ODE's where negative solutions are a problem
and one can only avoid them with great difficulty if the
standard explicit methods for solving the ODE are used. I found that
using implicit methods could be a great help.
For example in the exponential case
dN/dt = -k*N
the simple finite difference approximation is
N_{t+1}-N+t
-------------- = -k*N_t , k>=0
h
or
N_{t+1} = N_t -k*h*N_t
and if k*h gets too large N_{t+1} goes negative and you are in trouble.
Consider instead the implicit formulation where the second
N_t on the RHS is replaced by N_{t+1} and one gets
N_{t+1} = N_t/(1+k*h)
which is correct for k*h=0 and as k*h--> infinity
For a more complicated example see
http://otter-rsch.com/admodel/cc4.html
for something I called "semi-implicit".
I hope these ideas will be useful for your problem.
Cheers,
Dave
--
David A. Fournier
P.O. Box 2040,
Sidney, B.C. V8l 3S3
Canada
Phone/FAX 250-655-3364
http://otter-rsch.com