Skip to content

Commit c6556dc

Browse files
committed
Fix MethodError when solving constrained problems with LBFGS without maxiters
This commit fixes issue #958 where using Optimization.LBFGS() with constraints but without specifying maxiters would throw: 'MethodError: no method matching (::Colon)(::Int64, ::Nothing)' The issue occurred because maxiters was nothing when not specified, causing an error when creating the range 1:maxiters in the constrained optimization path. The fix adds a default value of 1000 iterations for constrained problems when maxiters is not specified, matching the behavior of unconstrained problems. Also adds a test case to prevent regression. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 779fd17 commit c6556dc

File tree

2 files changed

+44
-9
lines changed

2 files changed

+44
-9
lines changed

src/lbfgsb.jl

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ It is a quasi-Newton optimization algorithm that supports bounds.
88
99
References
1010
11-
- R. H. Byrd, P. Lu and J. Nocedal. A Limited Memory Algorithm for Bound Constrained Optimization, (1995), SIAM Journal on Scientific and Statistical Computing , 16, 5, pp. 1190-1208.
12-
- C. Zhu, R. H. Byrd and J. Nocedal. L-BFGS-B: Algorithm 778: L-BFGS-B, FORTRAN routines for large scale bound constrained optimization (1997), ACM Transactions on Mathematical Software, Vol 23, Num. 4, pp. 550 - 560.
13-
- J.L. Morales and J. Nocedal. L-BFGS-B: Remark on Algorithm 778: L-BFGS-B, FORTRAN routines for large scale bound constrained optimization (2011), to appear in ACM Transactions on Mathematical Software.
11+
- R. H. Byrd, P. Lu and J. Nocedal. A Limited Memory Algorithm for Bound Constrained Optimization, (1995), SIAM Journal on Scientific and Statistical Computing , 16, 5, pp. 1190-1208.
12+
- C. Zhu, R. H. Byrd and J. Nocedal. L-BFGS-B: Algorithm 778: L-BFGS-B, FORTRAN routines for large scale bound constrained optimization (1997), ACM Transactions on Mathematical Software, Vol 23, Num. 4, pp. 550 - 560.
13+
- J.L. Morales and J. Nocedal. L-BFGS-B: Remark on Algorithm 778: L-BFGS-B, FORTRAN routines for large scale bound constrained optimization (2011), to appear in ACM Transactions on Mathematical Software.
1414
"""
1515
@kwdef struct LBFGS
1616
m::Int = 10
@@ -92,6 +92,9 @@ function SciMLBase.__solve(cache::OptimizationCache{
9292
C
9393
}
9494
maxiters = Optimization._check_and_convert_maxiters(cache.solver_args.maxiters)
95+
if isnothing(maxiters)
96+
maxiters = 1000 # Default value for constrained problems
97+
end
9598

9699
local x
97100

@@ -124,7 +127,8 @@ function SciMLBase.__solve(cache::OptimizationCache{
124127
cache.f.cons(cons_tmp, θ)
125128
cons_tmp[eq_inds] .= cons_tmp[eq_inds] - cache.lcons[eq_inds]
126129
cons_tmp[ineq_inds] .= cons_tmp[ineq_inds] .- cache.ucons[ineq_inds]
127-
opt_state = Optimization.OptimizationState(u = θ, objective = x[1], p = cache.p, iter = iter_count[])
130+
opt_state = Optimization.OptimizationState(
131+
u = θ, objective = x[1], p = cache.p, iter = iter_count[])
128132
if cache.callback(opt_state, x...)
129133
error("Optimization halted by callback.")
130134
end
@@ -166,10 +170,12 @@ function SciMLBase.__solve(cache::OptimizationCache{
166170
n = length(cache.u0)
167171

168172
if cache.lb === nothing
169-
optimizer, bounds = LBFGSB._opt_bounds(
173+
optimizer,
174+
bounds = LBFGSB._opt_bounds(
170175
n, cache.opt.m, [-Inf for i in 1:n], [Inf for i in 1:n])
171176
else
172-
optimizer, bounds = LBFGSB._opt_bounds(
177+
optimizer,
178+
bounds = LBFGSB._opt_bounds(
173179
n, cache.opt.m, solver_kwargs.lb, solver_kwargs.ub)
174180
end
175181

@@ -212,7 +218,8 @@ function SciMLBase.__solve(cache::OptimizationCache{
212218
_loss = function (θ)
213219
x = cache.f(θ, cache.p)
214220
iter_count[] += 1
215-
opt_state = Optimization.OptimizationState(u = θ, objective = x[1], p = cache.p, iter = iter_count[])
221+
opt_state = Optimization.OptimizationState(
222+
u = θ, objective = x[1], p = cache.p, iter = iter_count[])
216223
if cache.callback(opt_state, x...)
217224
error("Optimization halted by callback.")
218225
end
@@ -222,10 +229,12 @@ function SciMLBase.__solve(cache::OptimizationCache{
222229
n = length(cache.u0)
223230

224231
if cache.lb === nothing
225-
optimizer, bounds = LBFGSB._opt_bounds(
232+
optimizer,
233+
bounds = LBFGSB._opt_bounds(
226234
n, cache.opt.m, [-Inf for i in 1:n], [Inf for i in 1:n])
227235
else
228-
optimizer, bounds = LBFGSB._opt_bounds(
236+
optimizer,
237+
bounds = LBFGSB._opt_bounds(
229238
n, cache.opt.m, solver_kwargs.lb, solver_kwargs.ub)
230239
end
231240

test/native.jl

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,29 @@ optf1 = OptimizationFunction(loss, AutoSparseForwardDiff())
6161
prob1 = OptimizationProblem(optf1, rand(5), data)
6262
sol1 = solve(prob1, OptimizationOptimisers.Adam(), maxiters = 1000, callback = callback)
6363
@test sol1.objective < l0
64+
65+
# Test for issue #958: LBFGS with constraints and no maxiters specified
66+
rosenbrock_issue958(u, p) = (p[1] - u[1])^2 + p[2] * (u[2] - u[1]^2)^2
67+
p_issue958 = [1.0, 100.0]
68+
function cons_issue958!(out, x, p)
69+
out[1] = sum(x)
70+
end
71+
72+
optf_issue958 = OptimizationFunction(
73+
rosenbrock_issue958, AutoForwardDiff(), cons = cons_issue958!)
74+
prob_issue958 = OptimizationProblem(
75+
optf_issue958, [-1, 1.0], p_issue958, lcons = [0.0], ucons = [0.0])
76+
77+
# This should not throw an error (issue #958)
78+
sol_issue958 = solve(prob_issue958, Optimization.LBFGS())
79+
# The key test is that it doesn't throw an error about maxiters being nothing
80+
# It may return MaxIters if it doesn't converge in the default 1000 iterations
81+
@test sol_issue958.retcode in [
82+
Optimization.SciMLBase.ReturnCode.Success, Optimization.SciMLBase.ReturnCode.MaxIters]
83+
84+
# If it did converge, verify the constraint is satisfied
85+
if sol_issue958.retcode == Optimization.SciMLBase.ReturnCode.Success
86+
cons_result_issue958 = zeros(1)
87+
cons_issue958!(cons_result_issue958, sol_issue958.u, p_issue958)
88+
@test abs(cons_result_issue958[1]) < 1e-6
89+
end

0 commit comments

Comments
 (0)