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].