Passing Fixed and Variable parameters to Optimx - r

This is a syntax question and probably has a simple solution but I can't find it covered anywhere on SO for the optimx package.
Minimal working example & Question
I have a function like:
ToOptimise = function(a,b,d,e){
(a-1)^2 + (b-2)^2 + (d-3)^2 +(e-4)^2
}
I can optimise select parameters in mle2 quite easily:
library(bbmle)
Calib2 = mle2(ToOptimise,
start = list(a = 1, d = 10),
fixed = list(b = 2, e = 2))
This is not really the right tool though as it is not a maximum likelihood problem.
I want to find a way to pass fixed and optimisable parameters to Optimx as easily
I plan to do several calibrations holding different parameters fixed at any time and so do not want to have to redo the function hardcoding in some parameters.
My attempt that don't work
library(optimx)
ToOptimiseVector = function(Theta){
a = Theta[1]
b = Theta[2]
d = Theta[3]
e = Theta[4]
(a-1)^2 + (b-2)^2 + (d-3)^2 +(e-4)^2
}
Calib1 = optimx( par = c(1,2,1,2),
fn = ToOptimiseVector,
Theta[2] = 2, Theta[4] = 2)
Another related Question on Stack Overflow
Another optimx syntax question that does not look at fixed and optimisable parameters is:
R- Optimx for exponential function with 2 parameters - cannot evaluate function at initial parameter values

Added a opt parameter to the fn. Just pass this opt vector into optimx(). Any non NA values will become fixed.
ToOptimiseVector <- function(Theta, opt = rep(NA, 4)){
# Check if any of opt is not NA
if (any(!sapply(opt, is.na))) {
i = !sapply(opt, is.na)
# Fix non-NA values
Theta[i] <- opt[i]
}
a <- Theta[1]
b <- Theta[2]
d <- Theta[3]
e <- Theta[4]
return((a-1)^2 + (b-2)^2 + (d-3)^2 +(e-4)^2)
}
Seems to work.
Calib1 = optimx( par = c(1,2,1,2), fn = ToOptimiseVector, opt = c(NA, 2, NA, 2))
Calib2 = optimx( par = c(1,2,1,2), fn = ToOptimiseVector)
> Calib1
p1 p2 p3 p4 value fevals gevals niter convcode kkt1 kkt2 xtimes
Nelder-Mead 0.9998974 5.517528 3.00022 10.83214 4 103 NA NA 0 TRUE FALSE 0.02
BFGS 1.0000000 4.000000 3.00000 8.00000 4 6 3 NA 0 TRUE FALSE 0.00
> Calib2
p1 p2 p3 p4 value fevals gevals niter convcode kkt1 kkt2 xtimes
Nelder-Mead 1.000936 1.999793 3.0006 4.000256 1.344336e-06 227 NA NA 0 FALSE TRUE 0.01
BFGS 1.000000 2.000000 3.0000 4.000000 3.566556e-23 16 3 NA 0 TRUE TRUE 0.00

The trick is that the starting params and the arguments to the function to be optimized have to be aligned. Please see if the following helps you at all.
library(optimx)
ToOptimiseVector <- function(Theta){
a <- Theta[1]
b <- Theta[2]
d <- Theta[3]
e <- Theta[4]
(a-1)^2 + (b-2)^2 + (d-3)^2 +(e-4)^2
}
start <- c(1,0,1,1)
start <- c(1,0,0,0)
start <- c(1,2,1,2)
Calib1 <- optimx( par=start,
fn = ToOptimiseVector)
This gives warning messages, but you do get a result.
> Calib1
p1 p2 p3 p4 value fevals gevals niter convcode kkt1 kkt2
Nelder-Mead 1 2 3 4 0 4 4 NA 0 TRUE TRUE
BFGS 1 2 3 4 0 4 4 NA 0 TRUE TRUE
xtimes
Nelder-Mead 0
BFGS 0
Hope you can start with this and proceed.

Related

Finding the root of an equation of 1.75th order

I have found the yield of a crop (Y) as a function of its nitrogen offtake (U) i.e., Y(U).
The rest of the values for this particular crop are:
Y_crit
U_crit
Q
p
U_max
Y
12327.9
123.2790
57.14286
0.75
198.38
14170
I want to solve for U.
I tried solving this using a binary search algorithm, using the uniroot() and polyroot(), all to no avail :(
I tried defining it as
fn <- function(U)
{
Y - Y_crit - Q * (U-U_Crit) + ((Q/(p+1)) * ((U - U_crit)/(U_max - U_crit))^(p+1) * (U_max - U_crit)
}
U <- polyroot(fn)
print(U)
but it says: "Error in polyroot(fn) : unimplemented type 'closure' in 'polyroot'"
I had first presented the value of Y as 14170 (=Y_max) but then confusing it with data for another crop, changed it to 11000. I have now changed it back.
I have rewritten the function in order to make it more clear what it is computing, there is an error in the question's version (wrong parenthesis).
Pass the arguments values as one object and in the function coerce it to list. This will make argument passing easier and less error prone;
repeating terms are pre-computed and reused.
I have plotted the function with values starting with U = 123.79, the value in the data.frame, until a visual inspection found an interval where the root is.
fn <- function(U, args) {
with(as.list(args), {
term1 <- U - U_crit
term2 <- U_max - U_crit
lhs <- Y_crit + Q*term1 - Q/(p+1) * (term1/term2)^(p+1) * term2
rhs <- Y
return(lhs - rhs)
})
}
U <- uniroot(fn, c(123.279, 350), args = args)
U
#> $root
#> [1] 308.6662
#>
#> $f.root
#> [1] 0.0004746999
#>
#> $iter
#> [1] 7
#>
#> $init.it
#> [1] NA
#>
#> $estim.prec
#> [1] 6.103516e-05
curve(fn(x, args), 123.3, 350, lwd = 2)
abline(h = 0)
points(U$root, U$f.root, col = "red", pch = 19)
Created on 2022-12-22 with reprex v2.0.2
Edit
According to its documentation, package optimx
Provides a replacement and extension of the optim()
function to call to several function minimization codes in R in a single
statement.
But it only minimises the objective function, so write a wrapper around it, gn below.
``` r
library(optimx)
gn <- function(x0, args) {
with(as.list(x0), {
args$Y <- Y
-fn(U, args)
})
}
x0 <- c(U = 124, Y = 10000)
optimx(par = x0, gn,
method = c("Nelder-Mead", "BFGS", "CG", "L-BFGS-B"),
args = args)
#> U Y value fevals gevals niter
#> Nelder-Mead 1.887090e+19 -7.002469e+34 -6.310914e+34 501 NA NA
#> BFGS 1.917764e+02 8.128266e+03 -6.026305e+03 100 100 NA
#> CG 1.983800e+02 9.853717e+03 -4.315391e+03 201 101 NA
#> L-BFGS-B NA NA 8.988466e+307 NA NA NA
#> convcode kkt1 kkt2 xtime
#> Nelder-Mead 1 TRUE FALSE 0.00
#> BFGS 1 TRUE FALSE 0.06
#> CG 1 TRUE FALSE 0.02
#> L-BFGS-B 9999 NA NA 0.01
optimx(par = x0, gn, method = c("BFGS", "CG"), args = args)
#> U Y value fevals gevals niter convcode kkt1 kkt2 xtime
#> BFGS 191.7764 8128.266 -6026.305 100 100 NA 1 TRUE FALSE 0.04
#> CG 198.3800 9853.717 -4315.391 201 101 NA 1 TRUE FALSE 0.02
Created on 2022-12-23 with reprex v2.0.2
The first run with 4 methods gives similar results for methods BFGS and CG. The second run keeps only these two methods.
The function's values are the symmetric of the values in column value.
Data
Here is the posted arguments data set as copy&paste able code.
args <- "Y_crit U_crit Q p U_max Y
12327.9 123.2790 57.14286 0.75 198.38 11000"
args <- read.table(textConnection(args), header = TRUE)
Created on 2022-12-22 with reprex v2.0.2

Solving dispersion equation

I am trying to solve the dispersion equation:
w^2 = k*g * tanh(kh)
I have a vector (25x1 element) input for w and want a vector output for k.
I've tried below, but is highly dependent on the tolerance values for uniroot.all:
g = 9.81 #m/s^2
h = 8 #m
w = c(0.1,0.2,0.3) # 3 element vector for ease
dispersion <- function(k) { 0 == k*g*tanh(k*h)^0.5-w }
k1 <- uniroot.all(function(k) dispersion(1e4), c(-10,10), tol = 1e-100, maxiter = 1000)
You can use an apply function, like so:
# Parameters
g <- 9.81
h <- 8
w <- c(0.1, 0.2, 0.3)
# Function to find root of
disp_root <- function(k, w) {k * g * tanh(k * h) - w^2 }
# Apply for each w
res <- sapply(w, function(x)rootSolve::uniroot.all(disp_root, c(-1,1), w = x))
# Repackage results
df_res <- data.frame(w, t(res))
# Fix names
names(df_res)[2:3] <- c("first_root", "second_root")
# Examine results
df_res
#> w first_root second_root
#> 1 0.1 -0.01126658 0.01126658
#> 2 0.2 -0.02272161 0.02272161
#> 3 0.3 -0.03419646 0.03419646
Created on 2020-01-31 by the reprex package (v0.3.0)

R optimx different values with different implementations

I am using optimx package in R to solve an optimization problem. I have a very large data set stored in hadoop and I am trying to use hive streaming with R to solve the problem.
Method 1 :- Using hive streaming with R code.
Method 2 :- Running the code from R console on the same server using
R CMD BATCH
For example, below is a sample of the dataset that is used for both the cases.
ITM_NBR FAM_ID OLD_PRICE MIN_COMP MAX_COMP ELAST Units PTC_FAM_ID PTC_ITM_NBR
1 64430 101006 59.98 50.983 68.977 1 230 AB_101006 AB64430
2 16961 101006 59.98 50.983 68.977 1 151 AB_101006 AB16961
Below is the R code I am using to read data from Hive table and CSV files to calculate optimal value. The only difference between two methods is the way that I read the data.
##library required for the program
library(optimx)
##Variables required for this program
lb = numeric(5)
ub = numeric(5)
exprvec <- expression()
o <- el <- u <- numeric(2)
#**Method 1**
f <- file("stdin")
open(f)
e = read.table(f)
#**Method 2**
e <- read.csv('Dataset1.csv')
names(e) <- c("ITM_NBR","FAM_ID","OLD_PRICE","MIN_COMP","MAX_COMP","ELAST","Units","PTC_FAM_ID","PTC_ITM_NBR","MIN_E","MAX_E")
lb <- max(e$MIN_COMP,e$MIN_E)
ub <- min(e$MAX_COMP,e$MAX_E)
x0 <- mean(c(lb,ub),rm.na = TRUE)
for(j in 1:nrow(e)){
o[j] = e$OLD_PRICE[j]
el[j] = e$ELAST[j]
u[j] = e$Units[j]
exprvec[[j]] <- substitute(x*(1-(x-o)*el/o)*u,list(o=o[j],el=el[j],u=u[j]))
}
eval_obj_f <- function(x){
sum(sapply(exprvec, eval, envir=list(x=x)))
}
## Required options for the optimx package and algorithms
res <- optimx(par = y,fn=eval_obj_f,gr=NULL,lower= lb,upper= ub,method="L- BFGS-B",control=list(maximize = TRUE))
print(res)
#**Method 1 Invocation**
hive> from(select ITM_NBR,FAM_ID,OLD_PRICE,MIN_COMP,MAX_COMP,ELAST,UNITS,PTC_FAM_ID,PTC_ITM_NBR,MIN_E,MAX_E from rtable distribute by PTC_FAM_ID)t1 insert overwrite directory '/user/rapp/t1' reduce ITM_NBR,FAM_ID,OLD_PRICE,MIN_COMP,MAX_COMP,ELAST,UNITS,PTC_FAM_ID,PTC_ITM_NBR,MIN_E,MAX_E using 'rscript.r';
#**Method 2 Invocation**
[m1.hdp22] R CMD BATCH optim.r
#**Method 1 results**
lb= 50.983
ub= 62.979
x0= 56.981
p1 value fevals gevals niter convcode kkt1 kkt2 xtimes
L-BFGS-B 59.98 2.285238e+04 4 4 NA 0 TRUE TRUE 0.001
#**Method 2 results**
lb= 50.983
ub= 62.979
x0= 56.981
p1 value fevals gevals niter convcode kkt1 kkt2 xtimes
L-BFGS-B 53.05923 23247.9 4 4 NA 0 TRUE TRUE 0.001

Nonlinear Least Squares in R - Levenberg Marquardt to Fit Heligman Pollard Model Parameters

I am attempting to reproduce the solutions of paper by Kostakis. In this paper an abridged mortality table is expanded to a complete life table using de Heligman-Pollard model. The model has 8 parameters which have to be fitted. The author used a modified Gauss-Newton algorithm; this algorithm (E04FDF) is part of the NAG library of computer programs. Should not Levenberg Marquardt yield the same set of parameters? What is wrong with my code or application of the LM algorithm?
library(minpack.lm)
## Heligman-Pollard is used to expand an abridged table.
## nonlinear least squares algorithm is used to fit the parameters on nqx observed over 5 year intervals (5qx)
AGE <- c(0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70)
MORTALITY <- c(0.010384069, 0.001469140, 0.001309318, 0.003814265, 0.005378395, 0.005985625, 0.006741766, 0.009325056, 0.014149626, 0.021601755, 0.034271934, 0.053836246, 0.085287751, 0.136549522, 0.215953304)
## The start parameters for de Heligman-Pollard Formula (Converged set a=0.0005893,b=0.0043836,c=0.0828424,d=0.000706,e=9.927863,f=22.197312,g=0.00004948,h=1.10003)
## I modified a random parameter "a" in order to have a start values. The converged set is listed above.
parStart <- list(a=0.0008893,b=0.0043836,c=0.0828424,d=0.000706,e=9.927863,f=22.197312,g=0.00004948,h=1.10003)
## The Heligman-Pollard Formula (HP8) = qx/px = ...8 parameter equation
HP8 <-function(parS,x)
ifelse(x==0, parS$a^((x+parS$b)^parS$c) + parS$g*parS$h^x,
parS$a^((x+parS$b)^parS$c) + parS$d*exp(-parS$e*(log(x/parS$f))^2) +
parS$g*parS$h^x)
## Define qx = HP8/(1+HP8)
qxPred <- function(parS,x) HP8(parS,x)/(1+HP8(parS,x))
## Calculate nqx predicted by HP8 model (nqxPred(parStart,x))
nqxPred <- function(parS,x)
(1 -(1-qxPred(parS,x)) * (1-qxPred(parS,x+1)) *
(1-qxPred(parS,x+2)) * (1-qxPred(parS,x+3)) *
(1-qxPred(parS,x+4)))
##Define Residual Function, the relative squared distance is minimized
ResidFun <- function(parS, Observed,x) (nqxPred(parS,x)/Observed-1)^2
## Applying the nls.lm algo.
nls.out <- nls.lm(par=parStart, fn = ResidFun, Observed = MORTALITY, x = AGE,
control = nls.lm.control(nprint=1,
ftol = .Machine$double.eps,
ptol = .Machine$double.eps,
maxfev=10000, maxiter = 500))
summary(nls.out)
## The author used a modified Gauss-Newton algorithm, this alogorithm (E04FDF) is part of the NAG library of computer programs
## Should not Levenberg Marquardt yield the same set of parameters
The bottom line here is that #Roland is absolutely right, this is a very ill-posed problem, and you shouldn't necessarily expect to get reliable answers. Below I've
cleaned up the code in a few small ways (this is just aesthetic)
changed the ResidFun to return residuals, not squared residuals. (The former is correct, but this doesn't make very much difference.)
explored results from several different optimizers. It actually looks like the answer you're getting is better than the "converged parameters" you list above, which I'm assuming are the parameters from the original study (can you please provide a reference?).
Load package:
library(minpack.lm)
Data, as a data frame:
d <- data.frame(
AGE = seq(0,70,by=5),
MORTALITY=c(0.010384069, 0.001469140, 0.001309318, 0.003814265,
0.005378395, 0.005985625, 0.006741766, 0.009325056,
0.014149626, 0.021601755, 0.034271934, 0.053836246,
0.085287751, 0.136549522, 0.215953304))
First view of the data:
library(ggplot2)
(g1 <- ggplot(d,aes(AGE,MORTALITY))+geom_point())
g1+geom_smooth() ## with loess fit
Parameter choices:
Presumably these are the parameters from the original paper ...
parConv <- c(a=0.0005893,b=0.0043836,c=0.0828424,
d=0.000706,e=9.927863,f=22.197312,g=0.00004948,h=1.10003)
Perturbed parameters:
parStart <- parConv
parStart["a"] <- parStart["a"]+3e-4
The formulae:
HP8 <-function(parS,x)
with(as.list(parS),
ifelse(x==0, a^((x+b)^c) + g*h^x,
a^((x+b)^c) + d*exp(-e*(log(x/f))^2) + g*h^x))
## Define qx = HP8/(1+HP8)
qxPred <- function(parS,x) {
h <- HP8(parS,x)
h/(1+h)
}
## Calculate nqx predicted by HP8 model (nqxPred(parStart,x))
nqxPred <- function(parS,x)
(1 -(1-qxPred(parS,x)) * (1-qxPred(parS,x+1)) *
(1-qxPred(parS,x+2)) * (1-qxPred(parS,x+3)) *
(1-qxPred(parS,x+4)))
##Define Residual Function, the relative squared distance is minimized
ResidFun <- function(parS, Observed,x) (nqxPred(parS,x)/Observed-1)
n.b. this is changed slightly from the OP's version; nls.lm wants residuals, not squared residuals.
A sum-of-squares function for use with other optimizers:
ssqfun <- function(parS, Observed, x) {
sum(ResidFun(parS, Observed, x)^2)
}
Applying nls.lm. (Not sure why ftol and ptol were lowered
from sqrt(.Machine$double.eps) to .Machine$double.eps -- the
former is generally a practical limit to precision ...
nls.out <- nls.lm(par=parStart, fn = ResidFun,
Observed = d$MORTALITY, x = d$AGE,
control = nls.lm.control(nprint=0,
ftol = .Machine$double.eps,
ptol = .Machine$double.eps,
maxfev=10000, maxiter = 1000))
parNLS <- coef(nls.out)
pred0 <- nqxPred(as.list(parConv),d$AGE)
pred1 <- nqxPred(as.list(parNLS),d$AGE)
dPred <- with(d,rbind(data.frame(AGE,MORTALITY=pred0,w="conv"),
data.frame(AGE,MORTALITY=pred1,w="nls")))
g1 + geom_line(data=dPred,aes(colour=w))
The lines are indistinguishable, but the parameters have some big
differences:
round(cbind(parNLS,parConv),5)
## parNLS parConv
## a 1.00000 0.00059
## b 50.46708 0.00438
## c 3.56799 0.08284
## d 0.00072 0.00071
## e 6.05200 9.92786
## f 21.82347 22.19731
## g 0.00005 0.00005
## h 1.10026 1.10003
d,f,g,h are close, but a,b,c are orders of magnitude different and e is 50% different.
Looking at the original equations, what's happening here is that a^((x+b)^c) is getting set to a constant, because a is approaching 1: once a is approximately 1, b and c are essentially irrelevant.
Let's check the correlation (we need a generalized inverse because
the matrix is so strongly correlated):
obj <- nls.out
vcov <- with(obj,deviance/(length(fvec) - length(par)) *
MASS::ginv(hessian))
cmat <- round(cov2cor(vcov),1)
dimnames(cmat) <- list(letters[1:8],letters[1:8])
## a b c d e f g h
## a 1.0 0.0 0.0 0.0 0.0 0.0 -0.1 0.0
## b 0.0 1.0 -1.0 1.0 -1.0 -1.0 -0.4 -1.0
## c 0.0 -1.0 1.0 -1.0 1.0 1.0 0.4 1.0
## d 0.0 1.0 -1.0 1.0 -1.0 -1.0 -0.4 -1.0
## e 0.0 -1.0 1.0 -1.0 1.0 1.0 0.4 1.0
## f 0.0 -1.0 1.0 -1.0 1.0 1.0 0.4 1.0
## g -0.1 -0.4 0.4 -0.4 0.4 0.4 1.0 0.4
## h 0.0 -1.0 1.0 -1.0 1.0 1.0 0.4 1.0
This is not actually so useful -- it really just confirms that lots
of the variables are strongly correlated ...
library(optimx)
mvec <- c('Nelder-Mead','BFGS','CG','L-BFGS-B',
'nlm','nlminb','spg','ucminf')
opt1 <- optimx(par=parStart, fn = ssqfun,
Observed = d$MORTALITY, x = d$AGE,
itnmax=5000,
method=mvec,control=list(kkt=TRUE))
## control=list(all.methods=TRUE,kkt=TRUE)) ## Boom!
## fvalues method fns grs itns conv KKT1 KKT2 xtimes
## 2 8.988466e+307 BFGS NA NULL NULL 9999 NA NA 0
## 3 8.988466e+307 CG NA NULL NULL 9999 NA NA 0
## 4 8.988466e+307 L-BFGS-B NA NULL NULL 9999 NA NA 0
## 5 8.988466e+307 nlm NA NA NA 9999 NA NA 0
## 7 0.3400858 spg 1 NA 1 3 NA NA 0.064
## 8 0.3400858 ucminf 1 1 NULL 0 NA NA 0.032
## 1 0.06099295 Nelder-Mead 501 NA NULL 1 NA NA 0.252
## 6 0.009275733 nlminb 200 1204 145 1 NA NA 0.708
This warns about bad scaling, and also finds a variety of different
answers: only ucminf claims to have converged, but nlminb gets a
better answer -- and the itnmax parameter seems to be ignored ...
opt2 <- nlminb(start=parStart, objective = ssqfun,
Observed = d$MORTALITY, x = d$AGE,
control= list(eval.max=5000,iter.max=5000))
parNLM <- opt2$par
Finishes, but with a false convergence warning ...
round(cbind(parNLS,parConv,parNLM),5)
## parNLS parConv parNLM
## a 1.00000 0.00059 1.00000
## b 50.46708 0.00438 55.37270
## c 3.56799 0.08284 3.89162
## d 0.00072 0.00071 0.00072
## e 6.05200 9.92786 6.04416
## f 21.82347 22.19731 21.82292
## g 0.00005 0.00005 0.00005
## h 1.10026 1.10003 1.10026
sapply(list(parNLS,parConv,parNLM),
ssqfun,Observed=d$MORTALITY,x=d$AGE)
## [1] 0.006346250 0.049972367 0.006315034
It looks like nlminb and minpack.lm are getting similar answers, and are actually doing better than the originally stated parameters (by quite a bit):
pred2 <- nqxPred(as.list(parNLM),d$AGE)
dPred <- with(d,rbind(dPred,
data.frame(AGE,MORTALITY=pred2,w="nlminb")))
g1 + geom_line(data=dPred,aes(colour=w))
ggsave("cmpplot.png")
ggplot(data=dPred,aes(x=AGE,y=MORTALITY-d$MORTALITY,colour=w))+
geom_line()+geom_point(aes(shape=w),alpha=0.3)
ggsave("residplot.png")
Other things one could try would be:
appropriate scaling -- although a quick test of this doesn't seem to help that much
provide analytical gradients
use AD Model Builder
use the slice function from bbmle to explore whether the old and new parameters seem to represent distinct minima, or whether the old parameters are just a false convergence ...
get the KKT (Karsh-Kuhn-Tucker) criterion calculators from optimx or related packages working for similar checks
PS: the largest deviations (by far) are for the oldest age classes, which probably also have small samples. From a statistical point of view it would probably be worth doing a fit that weighted by the precision of the individual points ...
#BenBolker, fitting the parameters with the entire dataset (underlying qx) values. Still not able to reproduce parameters
library(minpack.lm)
library(ggplot2)
library(optimx)
getwd()
d <- data.frame(AGE = seq(0,74), MORTALITY=c(869,58,40,37,36,35,32,28,29,23,24,22,24,28,
33,52,57,77,93,103,103,109,105,114,108,112,119,
125,117,127,125,134,134,131,152,179,173,182,199,
203,232,245,296,315,335,356,405,438,445,535,594,
623,693,749,816,915,994,1128,1172,1294,1473,
1544,1721,1967,2129,2331,2559,2901,3203,3470,
3782,4348,4714,5245,5646))
d$MORTALITY <- d$MORTALITY/100000
ggplot(d,aes(AGE,MORTALITY))+geom_point()
##Not allowed to post Images
g1 <- ggplot(d,aes(AGE,MORTALITY))+geom_point()
g1+geom_smooth()## with loess fit
Reported Parameters:
parConv <- c(a=0.0005893,b=0.0043836,c=0.0828424,d=0.000706,e=9.927863,f=22.197312,
g=0.00004948,h=1.10003)
parStart <- parConv
parStart["a"] <- parStart["a"]+3e-4
## Define qx = HP8/(1+HP8)
HP8 <-function(parS,x)
with(as.list(parS),
ifelse(x==0, a^((x+b)^c) + g*h^x, a^((x+b)^c) + d*exp(-e*(log(x/f))^2) + g*h^x))
qxPred <- function(parS,x) {
h <- HP8(parS,x)
h/(1+h)
}
##Define Residual Function, the relative squared distance is minimized,
ResidFun <- function(parS, Observed,x) (qxPred(parS,x)/Observed-1)
ssqfun <- function(parS, Observed, x) {
sum(ResidFun(parS, Observed, x)^2)
}
nls.out <- nls.lm(par=parStart, fn = ResidFun, Observed = d$MORTALITY, x = d$AGE,
control = nls.lm.control(nprint=1, ftol = sqrt(.Machine$double.eps),
ptol = sqrt(.Machine$double.eps), maxfev=1000, maxiter=1000))
parNLS <- coef(nls.out)
pred0 <- qxPred(as.list(parConv),d$AGE)
pred1 <- qxPred(as.list(parNLS),d$AGE)
#Binds Row wise the dataframes from pred0 and pred1
dPred <- with(d,rbind(data.frame(AGE,MORTALITY=pred0,w="conv"),
data.frame(AGE,MORTALITY=pred1,w="nls")))
g1 + geom_line(data=dPred,aes(colour=w))
round(cbind(parNLS,parConv),7)
mvec <- c('Nelder-Mead','BFGS','CG','L-BFGS-B','nlm','nlminb','spg','ucminf')
opt1 <- optimx(par=parStart, fn = ssqfun,
Observed = d$MORTALITY, x = d$AGE,
itnmax=5000,
method=mvec, control=list(all.methods=TRUE,kkt=TRUE,)
## control=list(all.methods=TRUE,kkt=TRUE)) ## Boom
get.result(opt1, attribute= c("fvalues","method", "grs", "itns",
"conv", "KKT1", "KKT2", "xtimes"))
## method fvalues grs itns conv KKT1 KKT2 xtimes
##5 nlm 8.988466e+307 NA NA 9999 NA NA 0
##4 L-BFGS-B 8.988466e+307 NULL NULL 9999 NA NA 0
##2 CG 8.988466e+307 NULL NULL 9999 NA NA 0.02
##1 BFGS 8.988466e+307 NULL NULL 9999 NA NA 0
##3 Nelder-Mead 0.5673864 NA NULL 0 NA NA 0.42
##6 nlminb 0.4127198 546 62 0 NA NA 0.17
opt2 <- nlminb(start=parStart, objective = ssqfun,
Observed = d$MORTALITY, x = d$AGE,
control= list(eval.max=5000,iter.max=5000))
parNLM <- opt2$par
Check on parameters:
round(cbind(parNLS,parConv,parNLM),5)
## parNLS parConv parNLM
##a 0.00058 0.00059 0.00058
##b 0.00369 0.00438 0.00369
##c 0.08065 0.08284 0.08065
##d 0.00070 0.00071 0.00070
##e 9.30948 9.92786 9.30970
##f 22.30769 22.19731 22.30769
##g 0.00005 0.00005 0.00005
##h 1.10084 1.10003 1.10084
SSE Review:
sapply(list(parNLS,parConv,parNLM),
ssqfun,Observed=d$MORTALITY,x=d$AGE)
##[1] 0.4127198 0.4169513 0.4127198
Not able to upload graphs but the code is here. Still appears that the parameters found in the article are not the best fit when the complete mortality data (not abridged or subset) is used
##pred2 <- qxPred(as.list(parNLM),d$AGE)
##dPred <- with(d,rbind(dPred,
data.frame(AGE,MORTALITY=pred2,w="nlminb")))
##g1 + geom_line(data=dPred,aes(colour=w))
ggplot(data=dPred,aes(x=AGE,y=MORTALITY-d$MORTALITY,colour=w))
+ geom_line()+geom_point(aes(shape=w),alpha=0.3)

How to solve ODEs with an internal threshold?

I have the following function containing some odes:
myfunction <- function(t, state, parameters) {
with(as.list(c(state, parameters)),{
if (X>20) { # this is an internal threshold!
Y <- 35000
dY <- 0
}else{
dY <- b * (Y-Z)
}
dX <- a*X^6 + Y*Z
dZ <- -X*Y + c*Y - Z
# return the rate of change
list(c(dX, dY, dZ),Y,dY)
})
}
Here are some results:
library(deSolve)
parameters <- c(a = -8/3, b = -10, c = 28)
state <- c(X = 1, Y = 1, Z = 1)
times <- seq(0, 10, by = 0.1)
out <- ode(y = state, times = times, func = myfunction, parms = parameters)
out
time X Y Z Y dY
1 0.0 1.000000 1.000000 1.000000 1.000000 0.00000
2 0.1 1.104670 2.132728 4.470145 2.132728 23.37417
3 0.2 1.783117 6.598806 14.086158 6.598806 74.87351
4 0.3 2.620428 20.325966 42.957134 20.325966 226.31169
5 0.4 3.775597 60.969424 126.920014 60.969424 659.50590
6 0.5 5.358823 176.094907 358.726482 176.094907 1826.31575
7 0.6 7.460841 482.506706 953.270570 482.506706 4707.63864
8 0.7 10.122371 1230.831764 2330.599161 1230.831764 10997.67398
9 0.8 13.279052 2859.284114 5113.458479 2859.284114 22541.74365
10 0.9 16.711405 5912.675147 9823.406760 5912.675147 39107.31613
11 1.0 24.452867 10590.600567 16288.435139 35000.000000 0.00000
12 1.1 25.988924 10590.600567 23476.343542 35000.000000 0.00000
13 1.2 26.572411 10590.600567 26821.703961 35000.000000 0.00000
14 1.3 26.844240 10590.600567 28510.668725 35000.000000 0.00000
15 1.4 26.980647 10590.600567 29391.032472 35000.000000 0.00000
...
States Y are different, can anybody explain me why please?
I believe I haven't set my threshold correctly.
Is there a way to that?
Thanks!
Think the simplest method to solve ODEs, i.e. Euler method:
state = state+myfunction(t,state,parameters)*h
f(t+h)=f(t) + f'(t) *h
h is a small time step, myfunction is the f'(t) derivative of f(t) and only evaluates the derivative, it does not have access to the actual state nor Y. Both are set internally in ode using a method which in principle is similar to Euler's: given the numerical values of f(t),f'(t),h it just updates state f(t+h).
So the threshold adjusts dY but cannot access state["Y"]. The process just manipulates a local variable which is evaluated as 35000 in dX <- a*X^6 + Y*Z and dZ <- -X*Y + c*Y - Z but the actual state["Y"] is overwritten after the myfuction has returned inside the ode function.
I am afraid that I cannot think of a simple way to bypass this design. I would just use out[5].

Resources