Skip to content

Bug: resolves use hess_op! of original problem, not current one #366

Description

@dpo

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
  • Output of versioninfo()
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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions