diff --git a/Project.toml b/Project.toml index f92439e5..1b3671a8 100644 --- a/Project.toml +++ b/Project.toml @@ -11,6 +11,7 @@ NLPModels = "a4795742-8479-5a88-8948-cc11e1c8c1a6" NLPModelsModifiers = "e01155f1-5c6f-4375-a9d8-616dd036575f" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" SolverCore = "ff4d7338-4cf1-434d-91df-b86cb86fb843" +SolverParameters = "d076d87d-d1f9-4ea3-a44b-64b4cdd1e470" SolverTools = "b5612192-2639-5dc1-abfe-fbedd65fab29" [compat] @@ -19,6 +20,7 @@ LinearOperators = "2.0" NLPModels = "0.21" NLPModelsModifiers = "0.7" SolverCore = "0.3" +SolverParameters = "0.1" SolverTools = "0.9" julia = "1.6" diff --git a/src/JSOSolvers.jl b/src/JSOSolvers.jl index 9cc4703c..07329a86 100644 --- a/src/JSOSolvers.jl +++ b/src/JSOSolvers.jl @@ -4,7 +4,7 @@ module JSOSolvers using LinearAlgebra, Logging, Printf # JSO packages -using Krylov, LinearOperators, NLPModels, NLPModelsModifiers, SolverCore, SolverTools +using Krylov, LinearOperators, NLPModels, NLPModelsModifiers, SolverCore, SolverParameters, SolverTools import SolverCore.solve! export solve! diff --git a/src/lbfgs.jl b/src/lbfgs.jl index c5211d9a..4d41e4c2 100644 --- a/src/lbfgs.jl +++ b/src/lbfgs.jl @@ -1,5 +1,37 @@ export lbfgs, LBFGSSolver +""" + LbfgsParameterSet{T} <: AbstractParameterSet + +This structure designed for `lbfgs` regroups the following parameters: + - mem::Parameter{Int, IntegerRange{Int}}: memory parameter of the `lbfgs` algorithm + - τ₁::Parameter{T, RealInterval{T}}: slope factor in the Wolfe condition when performing the line search + - bk_max::Parameter{Int, IntegerRange{Int}}: maximum number of backtracks when performing the line search. + + Default values are: + - `mem::Int = 5` + - `τ₁::T = T(0.9999)` + - `bk_max:: Int = 25` +""" +struct LbfgsParameterSet{T} <: AbstractParameterSet + mem::Parameter{Int, IntegerRange{Int}} + τ₁::Parameter{T, RealInterval{T}} + bk_max::Parameter{Int, IntegerRange{Int}} +end + +# add a default constructor +function LbfgsParameterSet{T}(; mem::Int = 5, τ₁::T = T(0.9999), bk_max::Int = 25) where {T} + LbfgsParameterSet( + Parameter(mem, IntegerRange(Int(5), Int(20))), + Parameter( + τ₁, + RealInterval(T(0), T(1), lower_open = true), + ), + Parameter(bk_max, IntegerRange(Int(1), Int(100))), + ) +end +LbfgsParameterSet(mem::Int, τ₁::T, bk_max::Int) where {T} = LbfgsParameterSet{T}(mem = mem, τ₁ = τ₁, bk_max = bk_max) + """ lbfgs(nlp; kwargs...) @@ -62,10 +94,15 @@ mutable struct LBFGSSolver{T, V, Op <: AbstractLinearOperator{T}, M <: AbstractN d::V H::Op h::LineModel{T, V, M} + params::LbfgsParameterSet{T} end -function LBFGSSolver(nlp::M; mem::Int = 5) where {T, V, M <: AbstractNLPModel{T, V}} +function LBFGSSolver(nlp::M; kwargs...) where {T, V, M <: AbstractNLPModel{T, V}} nvar = nlp.meta.nvar + + params = LbfgsParameterSet{T}(; kwargs...) + mem = value(params.mem) + x = V(undef, nvar) d = V(undef, nvar) xt = V(undef, nvar) @@ -74,7 +111,7 @@ function LBFGSSolver(nlp::M; mem::Int = 5) where {T, V, M <: AbstractNLPModel{T, H = InverseLBFGSOperator(T, nvar, mem = mem, scaling = true) h = LineModel(nlp, x, d) Op = typeof(H) - return LBFGSSolver{T, V, Op, M}(x, xt, gx, gt, d, H, h) + return LBFGSSolver{T, V, Op, M}(x, xt, gx, gt, d, H, h, params) end function SolverCore.reset!(solver::LBFGSSolver) @@ -88,12 +125,14 @@ function SolverCore.reset!(solver::LBFGSSolver, nlp::AbstractNLPModel) end @doc (@doc LBFGSSolver) function lbfgs( - nlp::AbstractNLPModel; + nlp::AbstractNLPModel{T, V}; x::V = nlp.meta.x0, mem::Int = 5, + τ₁::T = T(0.9999), + bk_max::Int = 25, kwargs..., -) where {V} - solver = LBFGSSolver(nlp; mem = mem) +) where {T, V} + solver = LBFGSSolver(nlp; mem = mem, τ₁ = τ₁, bk_max = bk_max) return solve!(solver, nlp; x = x, kwargs...) end @@ -108,8 +147,6 @@ function SolverCore.solve!( max_eval::Int = -1, max_iter::Int = typemax(Int), max_time::Float64 = 30.0, - τ₁::T = T(0.9999), - bk_max::Int = 25, verbose::Int = 0, verbose_subsolver::Int = 0, ) where {T, V} @@ -124,6 +161,10 @@ function SolverCore.solve!( start_time = time() set_time!(stats, 0.0) + # parameters + τ₁ = value(solver.params.τ₁) + bk_max = value(solver.params.bk_max) + n = nlp.meta.nvar solver.x .= x