Problem with nloptr solver usage
RebelionTheGrey opened this issue · 6 comments
I have nloptr
package installed on my R.
When I try to use ugarchfit
with nloptr
solver, I get this error each time:
Error in if (convergence != 0) warning("\nugarchfit-->warning: solver failer to converge.") :
argument is of length zero
Code to reproduce:
library(nloptr);
library(rugarch);
x0 <- c( -1.2, 1 )
opts <- list("algorithm"="NLOPT_LD_LBFGS", "xtol_rel"=1.0e-8)
# solve Rosenbrock Banana function
res <- nloptr( x0=x0, eval_f=eval_f, eval_grad_f=eval_grad_f, opts=opts)
print(res)
data(dmbp)
spec = ugarchspec()
fit = ugarchfit(data = dmbp[,1], spec = spec, solver = "nloptr")
fit
The full output for the code above:
> library(nloptr);
> library(rugarch);
>
> x0 <- c( -1.2, 1 )
>
> opts <- list("algorithm"="NLOPT_LD_LBFGS", "xtol_rel"=1.0e-8)
>
> # solve Rosenbrock Banana function
> res <- nloptr( x0=x0, eval_f=eval_f, eval_grad_f=eval_grad_f, opts=opts)
> print(res)
Call:
nloptr(x0 = x0, eval_f = eval_f, eval_grad_f = eval_grad_f, opts = opts)
Minimization using NLopt version 2.7.1
NLopt solver status: 1 ( NLOPT_SUCCESS: Generic success return value. )
Number of Iterations....: 56
Termination conditions: xtol_rel: 1e-08
Number of inequality constraints: 0
Number of equality constraints: 0
Optimal value of objective function: 7.35727226897802e-23
Optimal value of controls: 1 1
>
> data(dmbp)
> spec = ugarchspec()
> fit = ugarchfit(data = dmbp[,1], spec = spec, solver = "nloptr")
Error in if (convergence != 0) warning("\nugarchfit-->warning: solver failer to converge.") :
argument is of length zero
>
> fit
Error: object 'fit' not found
>
First part of this code is an example from nloptr package for showing that nloptr
package works correct, the second is just a little modified example from ugarchfit
specification.
My system:
AMD Ryzen 5950X, 128 Gb DDR4, Windows 11 Pro,
R version 4.2.2 Patched (2022-12-14 r83479 ucrt) -- "Innocent and Trusting"
gcc (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders) 12.2.0
Compilation options: -m64 -O3 -w -march=native -mfpmath=both -mfma -mtune=native
nloptr package version - 2.0.3 (current).
I try to use this solver because sometimes solnp
/gosolnp
solvers freeze on some data - ugarchfit/arfimafit really freeze.
P.S. this kind of problem is just connected with only nloptr
solver. For the same data ordinary solvers work fine.
Did you make any progress on this? I'm facing the same issue. I think it was introduced after 1.4.4 since my code worked with that. I see in the change log:
2022-01-18 Alexios Galanos alexios@4dscape.com
* DESCRIPTION (Version): New version is 1.4-6.
* Removed nloptr dependency
Looking at some change logs: 0c75d22 it looks like nloptr has been removed. I don't know why, it was more stable for me than the other solvers which often crashed my R sessions. This means there's no way to make this work without forking the code and adding the commented lines back in.
The only solution seems to be to revert to 1.4-4 https://cran.r-project.org/src/contrib/Archive/rugarch/
So far I am not able to make it work on R 4.3 though.
Here is a hack I cooked up that should mimic the old package behavior. In the second example I add BFGS solver. I'm passing on all arguments so it will throw a warning that the bounds are not respected.
library(rugarch)
# define a new solver, here nloptr
.nloptrsolver = function(pars, fun, control, LB, UB, arglist){
require(nloptr)
ans = try(nloptr(x0 = pars, eval_f = fun, eval_grad_f = NULL,
eval_g_ineq = NULL, lb = LB, ub = UB, eval_jac_g_ineq = NULL,
eval_g_eq = NULL, eval_jac_g_eq = NULL, opts = control, arglist = arglist),
silent=TRUE)
if(inherits(ans, "try-error")){
sol = list()
sol$convergence = 1
sol$message = ans
sol$par = rep(NA, length(pars))
names(sol$par) = names(pars)
} else{
sol = list()
sol$convergence = 0
sol$message = ans$message
sol$par = ans$solution
}
hess = NULL
return(list(sol = sol, hess = hess))
}
# define a new solver, here BFGS
.bfgssolver = function(pars, fun, gr, parscale, control, LB, UB, arglist){
control$parscale = parscale
ans = try(optim(par = pars, fn = fun, gr = gr, arglist = arglist,
method = "BFGS", lower = LB, upper = UB, control = control,
hessian = TRUE),silent=TRUE)
if(inherits(ans, "try-error")){
sol = list()
sol$convergence = 1
sol$message = ans
sol$par = rep(NA, length(pars))
names(sol$par) = names(pars)
} else{
sol = ans
}
hess = sol$hessian
return(list(sol = sol, hess = hess))
}
# modify the garch solver
.garchsolver = function(solver, pars, fun, Ifn, ILB, IUB, gr, hessian, parscale,
control, LB, UB, ux=NULL, ci=NULL, mu=NULL, arglist)
{
gocontrol = control
control = getcontrol2(solver, control) # use new control function
retval = switch(solver,
hybrid = rugarch:::.hybridsolver(pars, fun, Ifn, ILB, IUB, gr, hessian, parscale, gocontrol, LB, UB, arglist), # use namespace and ::: otherwise it won't be found
nlminb = rugarch:::.nlminbsolver(pars, fun, gr, hessian, parscale, control, LB, UB, arglist), # use namespace and ::: otherwise it won't be found
solnp = rugarch:::.solnpsolver(pars, fun, Ifn, ILB, IUB, control, LB, UB, arglist), # use namespace and ::: otherwise it won't be found
gosolnp = rugarch:::.gosolnpsolver(pars, fun, Ifn, ILB, IUB, gocontrol, LB, UB, arglist), # use namespace and ::: otherwise it won't be found
lbfgs = rugarch:::.lbfgssolver(pars, fun, gr, parscale, control, LB, UB, arglist), # use namespace and ::: otherwise it won't be found
bfgs = .bfgssolver(pars, fun, gr, parscale, control, LB, UB, arglist), # add here
nloptr = .nloptrsolver(pars, fun, control, LB, UB, arglist)) # add here
return(retval)
}
# assign it to name space to overwrite rugarch:::.garchsolver
assignInNamespace(".garchsolver", .garchsolver, "rugarch")
getcontrol2 = function(solver, control)
{
ans = switch(solver,
nlminb = rugarch:::.nlminbctrl(control), # use namespace and ::: otherwise it won't be found
solnp = rugarch:::.solnpctrl(control), # use namespace and ::: otherwise it won't be found
gosolnp = rugarch:::.gosolnpctrl(control), # use namespace and ::: otherwise it won't be found
lbfgs = rugarch:::.lbfgsctrl(control), # use namespace and ::: otherwise it won't be found
bfgs = rugarch:::.lbfgsctrl(control), # use the same as L-BFGS-B controls, use namespace and ::: otherwise it won't be found
nloptr = .nloptrctrl(control)) # add controls, newsly specified function
return(ans)
}
.nloptrctrl = function(control){
#solver = c("NLOPT_LN_COBYLA", "NLOPT_LN_BOBYQA", "NLOPT_LN_PRAXIS", "NLOPT_LN_NELDERMEAD", "NLOPT_LN_SBPLX",
# and AUGLAG + solver
# subsolvers:
xsub = c("NLOPT_LN_COBYLA", "NLOPT_LN_BOBYQA", "NLOPT_LN_PRAXIS", "NLOPT_LN_NELDERMEAD", "NLOPT_LN_SBPLX")
ans = list()
params = unlist(control)
if(is.null(params)) {
ans$ftol_rel = 1e-8
ans$xtol_rel = 1e-6
ans$maxeval = 25000
ans$print_level = 0
mainsolver = "NLOPT_LN_SBPLX"
subsolver = NULL
} else{
npar = tolower(names(unlist(control)))
names(params) = npar
if(any(substr(npar, 1, 8) == "ftol_rel")) ans$ftol_rel = as.numeric(params["ftol_rel"]) else ans$ftol_rel = 1e-8
if(any(substr(npar, 1, 8) == "xtol_rel")) ans$xtol_rel = as.numeric(params["xtol_rel"]) else ans$xtol_rel = 1e-6
if(any(substr(npar, 1, 7) == "maxeval")) ans$maxeval = as.numeric(params["maxeval"]) else ans$maxeval = 25000
if(any(substr(npar, 1, 11) == "print_level")) ans$print_level = as.numeric(params["print_level"]) else ans$print_level = 0
if(any(substr(npar, 1, 6) == "solver")){
if(abs(as.integer(params["solver"]))>5){
mainsolver = "NLOPT_LN_AUGLAG"
subsolver = xsub[min(5,abs(as.integer(params["solver"]))-5 )]
} else{
mainsolver = xsub[min(5,abs(as.integer(params["solver"])))]
subsolver = NULL
}
} else{
mainsolver = "NLOPT_LN_SBPLX"
subsolver = NULL
}
}
if(is.null(subsolver)){
ans$algorithm = mainsolver
} else{
ans$algorithm = mainsolver
ans$local_opts$algorithm = subsolver
ans$local_opts$ftol_abs = ans$ftol_rel
ans$local_opts$xtol_rel = ans$xtol_rel
ans$local_opts$maxeval = 2000
ans$local_opts$print_level = 0
}
return(ans)
}
set.seed(1)
testdata = rnorm(100)
test1 = ugarchfit(data =testdata, spec = ugarchspec(), solver = "nloptr", solver.control=list(control=list(solver=1)))
test2 = ugarchfit(data =testdata, spec = ugarchspec(), solver = "bfgs")
test3 = ugarchfit(data =testdata, spec = ugarchspec(), solver = "hybrid")
@BPJandree, @RebelionTheGrey does this commit resolve this issue? 008125a
If so then please close the issue.
from a quick reading of the code changes in 008125a this seems to work yes.
I ran a quick test on
sionInfo()
R version 4.1.2 (2021-11-01)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 22621)
Matrix products: default
locale:
[1] LC_COLLATE=English_Netherlands.1252 LC_CTYPE=English_Netherlands.1252 LC_MONETARY=English_Netherlands.1252 LC_NUMERIC=C
[5] LC_TIME=English_Netherlands.1252
attached base packages:
[1] parallel stats graphics grDevices utils datasets methods base
other attached packages:
[1] rugarch_1.5-1
loaded via a namespace (and not attached):
[1] zoo_1.8-12 remotes_2.4.2 ks_1.14.1 purrr_1.0.1 lattice_0.20-45 vctrs_0.6.1
[7] miniUI_0.1.1.1 usethis_2.1.6 htmltools_0.5.2 SkewHyperbolic_0.4-2 rlang_1.1.0 pkgbuild_1.4.3
[13] pracma_2.4.4 urlchecker_1.0.1 later_1.3.0 nloptr_2.0.3 glue_1.6.2 sessioninfo_1.2.2
[19] lifecycle_1.0.4 stringr_1.5.0 devtools_2.4.5 htmlwidgets_1.5.4 mvtnorm_1.2-4 memoise_2.0.1
[25] callr_3.7.3 fastmap_1.1.0 httpuv_1.6.5 ps_1.7.5 curl_4.3.2 xts_0.13.1
[31] Rcpp_1.0.10 KernSmooth_2.23-20 xtable_1.8-4 promises_1.2.0.1 cachem_1.0.6 desc_1.4.3
[37] DistributionUtils_0.6-1 pkgload_1.3.2 truncnorm_1.0-9 mime_0.12 fs_1.5.2 spd_2.0-1
[43] digest_0.6.31 stringi_1.7.6 processx_3.8.3 shiny_1.7.1 numDeriv_2016.8-1.1 grid_4.1.2
[49] cli_3.6.1 tools_4.1.2 magrittr_2.0.3 Rsolnp_1.16 profvis_0.3.7 ellipsis_0.3.2
[55] GeneralizedHyperbolic_0.8-6 MASS_7.3-54 Matrix_1.3-4 R6_2.5.1 mclust_6.0.1 compiler_4.1.2
using just:
set.seed(1)
testdata = rnorm(100)
test1 = ugarchfit(data =testdata, spec = ugarchspec(), solver = "nloptr", solver.control=list(control=list(solver=1)))
test2 = ugarchfit(data =testdata, spec = ugarchspec(), solver = "nloptr", solver.control=list(control=list(solver=2)))
This both works.
I then tried:
# define a new solver, here nloptr
.nloptrsolver = function(pars, fun, control, LB, UB, arglist){
require(nloptr)
ans = try(nloptr(x0 = pars, eval_f = fun, eval_grad_f = NULL,
eval_g_ineq = NULL, lb = LB, ub = UB, eval_jac_g_ineq = NULL,
eval_g_eq = NULL, eval_jac_g_eq = NULL, opts = control, arglist = arglist),
silent=TRUE)
if(inherits(ans, "try-error")){
sol = list()
sol$convergence = 1
sol$message = ans
sol$par = rep(NA, length(pars))
names(sol$par) = names(pars)
} else{
sol = list()
sol$convergence = 0
sol$message = ans$message
sol$par = ans$solution
}
hess = NULL
return(list(sol = sol, hess = hess))
}
# define a new solver, here BFGS
.bfgssolver = function(pars, fun, gr, parscale, control, LB, UB, arglist){
control$parscale = parscale
ans = try(optim(par = pars, fn = fun, gr = gr, arglist = arglist,
method = "BFGS", lower = LB, upper = UB, control = control,
hessian = TRUE),silent=TRUE)
if(inherits(ans, "try-error")){
sol = list()
sol$convergence = 1
sol$message = ans
sol$par = rep(NA, length(pars))
names(sol$par) = names(pars)
} else{
sol = ans
}
hess = sol$hessian
return(list(sol = sol, hess = hess))
}
# modify the garch solver
.garchsolver = function(solver, pars, fun, Ifn, ILB, IUB, gr, hessian, parscale,
control, LB, UB, ux=NULL, ci=NULL, mu=NULL, arglist)
{
gocontrol = control
control = getcontrol2(solver, control) # use new control function
retval = switch(solver,
hybrid = rugarch:::.hybridsolver(pars, fun, Ifn, ILB, IUB, gr, hessian, parscale, gocontrol, LB, UB, arglist), # use namespace and ::: otherwise it won't be found
nlminb = rugarch:::.nlminbsolver(pars, fun, gr, hessian, parscale, control, LB, UB, arglist), # use namespace and ::: otherwise it won't be found
solnp = rugarch:::.solnpsolver(pars, fun, Ifn, ILB, IUB, control, LB, UB, arglist), # use namespace and ::: otherwise it won't be found
gosolnp = rugarch:::.gosolnpsolver(pars, fun, Ifn, ILB, IUB, gocontrol, LB, UB, arglist), # use namespace and ::: otherwise it won't be found
lbfgs = rugarch:::.lbfgssolver(pars, fun, gr, parscale, control, LB, UB, arglist), # use namespace and ::: otherwise it won't be found
bfgs = .bfgssolver(pars, fun, gr, parscale, control, LB, UB, arglist), # add here
nloptr = .nloptrsolver(pars, fun, control, LB, UB, arglist)) # add here
return(retval)
}
# assign it to name space to overwrite rugarch:::.garchsolver
assignInNamespace(".garchsolver", .garchsolver, "rugarch")
getcontrol2 = function(solver, control)
{
ans = switch(solver,
nlminb = rugarch:::.nlminbctrl(control), # use namespace and ::: otherwise it won't be found
solnp = rugarch:::.solnpctrl(control), # use namespace and ::: otherwise it won't be found
gosolnp = rugarch:::.gosolnpctrl(control), # use namespace and ::: otherwise it won't be found
lbfgs = rugarch:::.lbfgsctrl(control), # use namespace and ::: otherwise it won't be found
bfgs = rugarch:::.lbfgsctrl(control), # use the same as L-BFGS-B controls, use namespace and ::: otherwise it won't be found
nloptr = .nloptrctrl(control)) # add controls, newsly specified function
return(ans)
}
.nloptrctrl = function(control){
#solver = c("NLOPT_LN_COBYLA", "NLOPT_LN_BOBYQA", "NLOPT_LN_PRAXIS", "NLOPT_LN_NELDERMEAD", "NLOPT_LN_SBPLX",
# and AUGLAG + solver
# subsolvers:
xsub = c("NLOPT_LN_COBYLA", "NLOPT_LN_BOBYQA", "NLOPT_LN_PRAXIS", "NLOPT_LN_NELDERMEAD", "NLOPT_LN_SBPLX")
ans = list()
params = unlist(control)
if(is.null(params)) {
ans$ftol_rel = 1e-8
ans$xtol_rel = 1e-6
ans$maxeval = 25000
ans$print_level = 0
mainsolver = "NLOPT_LN_SBPLX"
subsolver = NULL
} else{
npar = tolower(names(unlist(control)))
names(params) = npar
if(any(substr(npar, 1, 8) == "ftol_rel")) ans$ftol_rel = as.numeric(params["ftol_rel"]) else ans$ftol_rel = 1e-8
if(any(substr(npar, 1, 8) == "xtol_rel")) ans$xtol_rel = as.numeric(params["xtol_rel"]) else ans$xtol_rel = 1e-6
if(any(substr(npar, 1, 7) == "maxeval")) ans$maxeval = as.numeric(params["maxeval"]) else ans$maxeval = 25000
if(any(substr(npar, 1, 11) == "print_level")) ans$print_level = as.numeric(params["print_level"]) else ans$print_level = 0
if(any(substr(npar, 1, 6) == "solver")){
if(abs(as.integer(params["solver"]))>5){
mainsolver = "NLOPT_LN_AUGLAG"
subsolver = xsub[min(5,abs(as.integer(params["solver"]))-5 )]
} else{
mainsolver = xsub[min(5,abs(as.integer(params["solver"])))]
subsolver = NULL
}
} else{
mainsolver = "NLOPT_LN_SBPLX"
subsolver = NULL
}
}
if(is.null(subsolver)){
ans$algorithm = mainsolver
} else{
ans$algorithm = mainsolver
ans$local_opts$algorithm = subsolver
ans$local_opts$ftol_abs = ans$ftol_rel
ans$local_opts$xtol_rel = ans$xtol_rel
ans$local_opts$maxeval = 2000
ans$local_opts$print_level = 0
}
return(ans)
}
set.seed(1)
test3 = ugarchfit(data =testdata, spec = ugarchspec(), solver = "nloptr", solver.control=list(control=list(solver=1)))
And compared outputs (truncated here):
> test1
*---------------------------------*
* GARCH Model Fit *
*---------------------------------*
Conditional Variance Dynamics
-----------------------------------
GARCH Model : sGARCH(1,1)
Mean Model : ARFIMA(1,0,1)
Distribution : norm
Optimal Parameters
------------------------------------
Estimate Std. Error t value Pr(>|t|)
mu 0.109100 0.090130 1.210475 0.22610
ar1 -0.910069 0.175661 -5.180834 0.00000
ma1 0.928281 0.154229 6.018838 0.00000
omega 0.001397 0.039392 0.035457 0.97172
alpha1 0.000000 0.050613 0.000000 1.00000
beta1 0.998556 0.007986 125.030966 0.00000
Robust Standard Errors:
Estimate Std. Error t value Pr(>|t|)
mu 0.109100 0.075885 1.43771 0.15052
ar1 -0.910069 0.072765 -12.50698 0.00000
ma1 0.928281 0.070429 13.18039 0.00000
omega 0.001397 0.037176 0.03757 0.97003
alpha1 0.000000 0.048317 0.00000 1.00000
beta1 0.998556 0.001267 788.22578 0.00000
LogLikelihood : -130.5498
> test3
*---------------------------------*
* GARCH Model Fit *
*---------------------------------*
Conditional Variance Dynamics
-----------------------------------
GARCH Model : sGARCH(1,1)
Mean Model : ARFIMA(1,0,1)
Distribution : norm
Optimal Parameters
------------------------------------
Estimate Std. Error t value Pr(>|t|)
mu 0.109100 0.090130 1.210475 0.22610
ar1 -0.910069 0.175661 -5.180834 0.00000
ma1 0.928281 0.154229 6.018838 0.00000
omega 0.001397 0.039392 0.035457 0.97172
alpha1 0.000000 0.050613 0.000000 1.00000
beta1 0.998556 0.007986 125.030966 0.00000
Robust Standard Errors:
Estimate Std. Error t value Pr(>|t|)
mu 0.109100 0.075885 1.43771 0.15052
ar1 -0.910069 0.072765 -12.50698 0.00000
ma1 0.928281 0.070429 13.18039 0.00000
omega 0.001397 0.037176 0.03757 0.97003
alpha1 0.000000 0.048317 0.00000 1.00000
beta1 0.998556 0.001267 788.22578 0.00000
LogLikelihood : -130.5498
This shows the solution of rugarch 1.5-1 nloptr is the same as the one with the "hack" I posted.
I think this issue can be closed.
Very best wishes
Sorry for the late reply to this thread. The reason I removed nloptr at the time was because it was in danger of being abandoned on CRAN (email from Brian Ripley on Jan 07 2022). In the meantime, someone stepped up to become a maintainer but I did not add it back as I'd started to focus more on the tsmodels/tsgarch package instead. I may add it back in the next release. Thanks for your understanding.
Alexios