JuliaSmoothOptimizers / JuliaSmoothOptimizers/JSOSolvers.jl

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

Open
#366 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Julia
Stars
78
Forks
18
Avg merge
1h 30m
Merged PRs (30d)
1

Description

**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 👇**

```julia
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 ⚠️**

```julia
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()`

```julia
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)`

```julia
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
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:

```julia
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.
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.