Describe the bug 🐞
When a solver is called multiple times in a loop, it is more efficient to pre-allocate the solver object.
The latter stores the hess_op! of the model used to initialize the solver.
However, on subsequent solves with a different model (of the same dimensions), the solver object’s hess_op! is not updated and continues to refer to the problem used to initialize the solver.
Expected behavior
The hess_op! of the model passed to solve!() should be used.
Minimal Reproducible Example 👇
using ManualNLPModels, NLPModelsModifiers, JSOSolvers
f(x) = (x[1] - 1)^2 + 4 * (x[2] - x[1]^2)^2
g!(gx, x) = begin
gx[1] = 2 * (x[1] - 1) - 16 * x[1] * (x[2] - x[1]^2)
gx[2] = 8 * (x[2] - x[1]^2)
gx
end
nlp = NLPModel([-1.2; 1.0], f, grad = g!)
# nlp does not implement second derivatives.
# However, it should only be used for its dimensions when initializing the solver object.
# The following works fine.
solver = TrunkSolver(nlp)
# Now we solve a problem that _does_ implement second derivatives, but the solve fails.
solve!(solver, LBFGSModel(nlp))
ERROR: The function you called was not implemented.
Error & Stacktrace ⚠️
ERROR: The function you called was not implemented.
Stacktrace:
[1] error(s::String)
@ Base ./error.jl:44
[2] notimplemented(::Vector{Float64}, ::Vararg{Vector{Float64}}; kwargs::@Kwargs{obj_weight::Float64})
@ ManualNLPModels ~/.julia/packages/ManualNLPModels/Lf6KF/src/structure.jl:46
[3] hprod!(nlp::NLPModel{…}, x::Vector{…}, v::Vector{…}, Hv::Vector{…}; obj_weight::Float64)
@ ManualNLPModels ~/.julia/packages/ManualNLPModels/Lf6KF/src/api.jl:33
[4] (::NLPModels.var"#27#28"{Float64, NLPModel{…}, Vector{…}, Vector{…}})(res::Vector{Float64}, v::Vector{Float64}, α::Float64, β::Float64)
@ NLPModels ~/.julia/packages/NLPModels/Cfwb3/src/nlp/api.jl:1390
[5] mul!(res::Vector{…}, op::LinearOperators.LinearOperator{…}, v::Vector{…}, α::Float64, β::Float64)
@ LinearOperators ~/.julia/packages/LinearOperators/p7GRU/src/operations.jl:27
[6] mul!(res::Vector{Float64}, op::LinearOperators.LinearOperator{Float64, Vector{…}, Int64, NLPModels.var"#27#28"{…}, NLPModels.var"#27#28"{…}, NLPModels.var"#27#28"{…}}, v::Vector{Float
64})
@ LinearOperators ~/.julia/packages/LinearOperators/p7GRU/src/operations.jl:39
[7] kmul!(y::Vector{Float64}, P::LinearOperators.LinearOperator{Float64, Vector{…}, Int64, NLPModels.var"#27#28"{…}, NLPModels.var"#27#28"{…}, NLPModels.var"#27#28"{…}}, x::Vector{Float64
})
@ Krylov ~/.julia/packages/Krylov/zGitw/src/krylov_utils.jl:305
[8] cg!(workspace::Krylov.CgWorkspace{…}, A::LinearOperators.LinearOperator{…}, b::Vector{…}; M::LinearAlgebra.UniformScaling{…}, ldiv::Bool, radius::Float64, linesearch::Bool, atol::Floa
t64, rtol::Float64, itmax::Int64, timemax::Float64, verbose::Int64, history::Bool, callback::Krylov.var"#krylov_solve!##158#krylov_solve!##159", iostream::Core.CoreSTDOUT)
@ Krylov ~/.julia/packages/Krylov/zGitw/src/cg.jl:196
[9] cg!
@ ~/.julia/packages/Krylov/zGitw/src/cg.jl:120 [inlined]
[10] krylov_solve!
@ ~/.julia/packages/Krylov/zGitw/src/interface.jl:228 [inlined]
[11] solve!(solver::TrunkSolver{…}, nlp::LBFGSModel{…}, stats::SolverCore.GenericExecutionStats{…}; callback::JSOSolvers.var"#21#22", callback_quasi_newton::typeof(default_callback_quasi_n
ewton), subsolver_logger::Base.CoreLogging.NullLogger, x::Vector{…}, atol::Float64, rtol::Float64, max_eval::Int64, max_iter::Int64, max_time::Float64, verbose::Int64, subsolver_verbose::In
t64, M::LinearAlgebra.UniformScaling{…})
@ JSOSolvers ~/.julia/packages/JSOSolvers/tX6Sk/src/trunk.jl:295
[12] solve!
@ ~/.julia/packages/JSOSolvers/tX6Sk/src/trunk.jl:189 [inlined]
[13] #solve!#1
@ ~/.julia/packages/SolverCore/UI1dA/ext/SolverCoreNLPModelsExt.jl:60 [inlined]
[14] solve!(solver::TrunkSolver{…}, model::LBFGSModel{…})
@ SolverCoreNLPModelsExt ~/.julia/packages/SolverCore/UI1dA/ext/SolverCoreNLPModelsExt.jl:54
[15] top-level scope
@ REPL[7]:1
Some type information was truncated. Use `show(err)` to see complete types.
Environment (please complete the following information):
- Output of
using Pkg; Pkg.status()
Status `/private/tmp/test-jsosolvers/Project.toml`
[10dff2fc] JSOSolvers v0.14.8
[30dfa513] ManualNLPModels v0.2.0
[e01155f1] NLPModelsModifiers v0.8.0
- Output of
using Pkg; Pkg.status(; mode = PKGMODE_MANIFEST)
Status `/private/tmp/test-jsosolvers/Manifest.toml`
[e2ba6199] ExprTools v0.1.10
[9aa1b823] FastClosures v0.3.2
[10dff2fc] JSOSolvers v0.14.8
[ba0b0d4f] Krylov v0.10.6
[5c8ed15e] LinearOperators v2.13.0
[30dfa513] ManualNLPModels v0.2.0
[a4795742] NLPModels v0.21.12
[e01155f1] NLPModelsModifiers v0.8.0
[ff4d7338] SolverCore v0.3.10
[d076d87d] SolverParameters v0.1.2
[b5612192] SolverTools v0.10.1
[a759f4b9] TimerOutputs v0.5.29
[56f22d72] Artifacts v1.11.0
[8f399da3] Libdl v1.11.0
[37e2e46d] LinearAlgebra v1.12.0
[56ddb016] Logging v1.11.0
[de0858da] Printf v1.11.0
[9a3f8284] Random v1.11.0
[ea8e919c] SHA v0.7.0
[9e88b42a] Serialization v1.11.0
[2f01184e] SparseArrays v1.12.0
[4ec0a83e] Unicode v1.11.0
[e66e0078] CompilerSupportLibraries_jll v1.3.0+1
[4536629a] OpenBLAS_jll v0.3.29+0
[bea87d4a] SuiteSparse_jll v7.8.3+2
[8e850b90] libblastrampoline_jll v5.15.0+0
Julia Version 1.12.6
Commit 15346901f00 (2026-04-09 19:20 UTC)
Build Info:
Official https://julialang.org release
Platform Info:
OS: macOS (arm64-apple-darwin24.0.0)
CPU: 32 × Apple M3 Ultra
WORD_SIZE: 64
LLVM: libLLVM-18.1.7 (ORCJIT, apple-m3)
GC: Built with stock GC
Threads: 1 default, 1 interactive, 1 GC (on 24 virtual cores)
Environment:
JULIA_EDITOR = nvim
Additional context
It doesn’t matter that the nlp used to create the TrunkSolver object doesn’t implement second derivatives.
The point is that solve!() will call that problem’s hess_op!, and not that of the model passed as argument.
EDIT: Calling reset!() does not help here because the linear operators returned by the original model and the quasi-Newton model have different types:
using SolverCore
qn_nlp = LBFGSModel(nlp)
reset!(solver, qn_nlp)
ERROR: MethodError: Cannot `convert` an object of type
LinearOperators.LinearOperator{Float64,Array{Float64,1},Int64,NLPModels.var"#27#28"{Float64,LBFGSModel{Float64, Vector{Float64}, NLPModel{Float64, Vector{Float64}, typeof(f), typeof(g!),
typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented), Vector{Int64}, typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented), typeof(ManualN
LPModels.notimplemented), typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented)}, NLPModels.NLPModelMeta{Float64, Vector{Float64}}, LinearOperators.LBFGSOperator{Fl
oat64, Int64, LinearOperators.var"#56#57"{LinearOperators.var"#lbfgs_multiply#55", LinearOperators.LBFGSData{Float64, Int64}}, LinearOperators.var"#56#57"{LinearOperators.var"#lbfgs_multipl
y#55", LinearOperators.LBFGSData{Float64, Int64}}, LinearOperators.var"#56#57"{LinearOperators.var"#lbfgs_multiply#55", LinearOperators.LBFGSData{Float64, Int64}}}},Array{Float64,1},Array{F
loat64,1}},NLPModels.var"#27#28"{Float64,LBFGSModel{Float64, Vector{Float64}, NLPModel{Float64, Vector{Float64}, typeof(f), typeof(g!), typeof(ManualNLPModels.notimplemented), typeof(Manual
NLPModels.notimplemented), Vector{Int64}, typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.noti
mplemented), typeof(ManualNLPModels.notimplemented)}, NLPModels.NLPModelMeta{Float64, Vector{Float64}}, LinearOperators.LBFGSOperator{Float64, Int64, LinearOperators.var"#56#57"{LinearOpera
tors.var"#lbfgs_multiply#55", LinearOperators.LBFGSData{Float64, Int64}}, LinearOperators.var"#56#57"{LinearOperators.var"#lbfgs_multiply#55", LinearOperators.LBFGSData{Float64, Int64}}, Li
nearOperators.var"#56#57"{LinearOperators.var"#lbfgs_multiply#55", LinearOperators.LBFGSData{Float64, Int64}}}},Array{Float64,1},Array{Float64,1}},NLPModels.var"#27#28"{Float64,LBFGSModel{F
loat64, Vector{Float64}, NLPModel{Float64, Vector{Float64}, typeof(f), typeof(g!), typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented), Vector{Int64}, typeof(Manu
alNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented)},
NLPModels.NLPModelMeta{Float64, Vector{Float64}}, LinearOperators.LBFGSOperator{Float64, Int64, LinearOperators.var"#56#57"{LinearOperators.var"#lbfgs_multiply#55", LinearOperators.LBFGSDa
ta{Float64, Int64}}, LinearOperators.var"#56#57"{LinearOperators.var"#lbfgs_multiply#55", LinearOperators.LBFGSData{Float64, Int64}}, LinearOperators.var"#56#57"{LinearOperators.var"#lbfgs_
multiply#55", LinearOperators.LBFGSData{Float64, Int64}}}},Array{Float64,1},Array{Float64,1}}} to an object of type
LinearOperators.LinearOperator{Float64,Array{Float64,1},Int64,NLPModels.var"#27#28"{Float64,NLPModel{Float64, Vector{Float64}, typeof(f), typeof(g!), typeof(ManualNLPModels.notimplemented
), typeof(ManualNLPModels.notimplemented), Vector{Int64}, typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented), typeof(Manu
alNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented)},Array{Float64,1},Array{Float64,1}},NLPModels.var"#27#28"{Float64,NLPModel{Float64, Vector{Float64}, typeof(f), typeof(g!
), typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented), Vector{Int64}, typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented), typeof(Manu
alNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented)},Array{Float64,1},Array{Float64,1}},NLPModels.var"#27#28"{Float64,NLPModel{Float6
4, Vector{Float64}, typeof(f), typeof(g!), typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented), Vector{Int64}, typeof(ManualNLPModels.notimplemented), typeof(Manu
alNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented), typeof(ManualNLPModels.notimplemented)},Array{Float64,1},Array{Float64,1}}}
The function `convert` exists, but no method is defined for this combination of argument types.
Closest candidates are:
(::Type{LinearOperators.LinearOperator{T, S, I, F, Ft, Fct}} where {T, S, I<:Integer, F, Ft, Fct})(::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any, ::Any)
@ LinearOperators ~/.julia/packages/LinearOperators/p7GRU/src/abstract.jl:47
convert(::Type{T}, ::T) where T
@ Base Base_compiler.jl:133
Stacktrace:
[1] setproperty!(x::TrunkSolver{…}, f::Symbol, v::LinearOperators.LinearOperator{…})
@ Base ./Base_compiler.jl:57
[2] reset!(solver::TrunkSolver{…}, nlp::LBFGSModel{…})
@ JSOSolvers ~/.julia/packages/JSOSolvers/tX6Sk/src/trunk.jl:163
[3] top-level scope
@ REPL[16]:1
Some type information was truncated. Use `show(err)` to see complete types.
Describe the bug 🐞
When a solver is called multiple times in a loop, it is more efficient to pre-allocate the solver object.
The latter stores the
hess_op!of the model used to initialize the solver.However, on subsequent solves with a different model (of the same dimensions), the solver object’s
hess_op!is not updated and continues to refer to the problem used to initialize the solver.Expected behavior
The
hess_op!of the model passed tosolve!()should be used.Minimal Reproducible Example 👇
Error & Stacktrace⚠️
Environment (please complete the following information):
using Pkg; Pkg.status()using Pkg; Pkg.status(; mode = PKGMODE_MANIFEST)versioninfo()Additional context
It doesn’t matter that the
nlpused to create theTrunkSolverobject doesn’t implement second derivatives.The point is that
solve!()will call that problem’shess_op!, and not that of the model passed as argument.EDIT: Calling
reset!()does not help here because the linear operators returned by the original model and the quasi-Newton model have different types: