Skip to content

Commit d085672

Browse files
Guard against gbarrier_norm == 0 in computation of initial mu (fixes #1233) (#1257)
* Guard against gbarrier_norm == 0 in computation of initial mu Fixes #1233 If both `gnorm == 0` and `gbarrier_norm == 0`, then `mu = 0/0` which evaluates to `NaN`. I.e., this error is produced if all parameters are unbounded. Bug was introduced by removing safety catch in commit 9b664c2 "Re-instate old behavior for Fminbox..." leading up to v2.0.1. I added back the old safety check and a dedicated test for good measure. * Update test/multivariate/solvers/constrained/fminbox.jl Extend tests of `initial_mu` and `Fminbox` for additional cases, such as half-bounded parameters. Co-authored-by: Patrick Kofod Mogensen <patrick.mogensen@gmail.com> --------- Co-authored-by: Patrick Kofod Mogensen <patrick.mogensen@gmail.com>
1 parent a14f67b commit d085672

2 files changed

Lines changed: 34 additions & 1 deletion

File tree

src/multivariate/solvers/constrained/fminbox.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,13 @@ function initial_mu(box::BoxBarrier, x::AbstractArray, g_x::AbstractArray, F::Fm
267267

268268
gnorm, gbarrier_norm, mufactor, mu0 = promote(_gnorm, _gbarrier_norm, F.mufactor, F.mu0)
269269
mu = if isnan(mu0)
270-
mufactor * gnorm / gbarrier_norm
270+
# Guard against gbarrier_norm == 0 case leading to mu = 0/0, which is NaN
271+
if gbarrier_norm > 0
272+
mufactor * gnorm / gbarrier_norm
273+
else
274+
# Presumably, there is no barrier function
275+
zero(gnorm)
276+
end
271277
else
272278
mu0
273279
end

test/multivariate/solvers/constrained/fminbox.jl

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,33 @@
181181
Optim.Options(),
182182
)
183183
end
184+
@testset "Guard against gbarrier_norm == 0 in computation of initial mu #1233" begin
185+
l_inf, u_inf = fill(-Inf, 2), fill(Inf, 2)
186+
187+
# gnorm > 0, gbarrier_norm == 0: previously mu = gnorm/0 = Inf
188+
res = optimize(exponential, l_inf, u_inf, initial_x, Fminbox(), Optim.Options())
189+
@test Optim.converged(res)
190+
@test Optim.minimizer(res) [2.0, 3.0] atol = 1e-3
191+
192+
# gnorm == 0 and gbarrier_norm == 0: previously mu = 0/0 = NaN
193+
# ([2.0, 3.0] is the stationary point of `exponential`)
194+
res = optimize(exponential, l_inf, u_inf, [2.0, 3.0], Fminbox(), Optim.Options())
195+
@test Optim.converged(res)
196+
@test Optim.minimizer(res) [2.0, 3.0]
197+
198+
# initial_mu unit tests
199+
box_inf = Optim.BoxBarrier(l_inf, u_inf)
200+
@test Optim.initial_mu(box_inf, [0.0, 0.0], [1.0, 1.0], Fminbox()) == 0 # Inf case
201+
@test Optim.initial_mu(box_inf, [0.0, 0.0], [0.0, 0.0], Fminbox()) == 0 # NaN case
202+
203+
# half-bounded box: a single finite bound must still give mu > 0
204+
box_half = Optim.BoxBarrier([-Inf, 0.0], [Inf, Inf])
205+
@test Optim.initial_mu(box_half, [0.0, 1.0], [1.0, 1.0], Fminbox()) > 0
206+
207+
# explicitly non-finite user-supplied mu0 should still throw
208+
@test_throws ArgumentError Optim.initial_mu(box_inf, [0.0, 0.0], [1.0, 1.0],
209+
Fminbox(LBFGS(); mu0 = Inf))
210+
end
184211
end
185212
end
186213

0 commit comments

Comments
 (0)