Skip to content

Commit c8021e6

Browse files
Detect unbounded and infeasible problems
1 parent 29e72d3 commit c8021e6

4 files changed

Lines changed: 94 additions & 31 deletions

File tree

src/AL_alg.jl

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,12 @@ If adopted, the Hessian is accessed as an abstract operator and need not be the
102102
103103
- `x::AbstractVector`: a primal initial guess (default: `reg_nlp.model.meta.x0`)
104104
- `y::AbstractVector`: a dual initial guess (default: `reg_nlp.model.meta.y0`)
105-
- `atol::T = √eps(T)`: absolute optimality tolerance;
105+
- `atol::T = eps(T)^(1/3)`: absolute tolerance
106+
- `diverging_iterates_tol::T = eps(T)^(-1)`: diverging tolerance for the norm of the iterates (the norm should be lower than the tolerance);
107+
- `diverging_obj_tol::T = -eps(T)^(-1)`: diverging tolerance for the objective function (the objective function should be higher than the tolerance);
108+
- `cviol_tol::T = eps(T)^(-1)`: tolerance to determine whether the constraints are infeasible
109+
- `diverging_max_iter::Int = 5`: maximum number of iteration at which `diverging_obj_tol` or `diverging_iterates_tol` is violated;
110+
- `cviol_max_iter::Int = 5`: maximum number of iteration at which the regularisation parameter is increasing and the constraints are still violated;
106111
- `ctol::T = atol`: absolute feasibility tolerance;
107112
- `verbose::Int = 0`: if > 0, display iteration details every `verbose` iteration;
108113
- `max_iter::Int = 10000`: maximum number of iterations;
@@ -209,7 +214,12 @@ function SolverCore.solve!(
209214
callback = (args...) -> nothing,
210215
x::V = reg_nlp.model.meta.x0,
211216
y::V = reg_nlp.model.meta.y0,
212-
atol::T = eps(T),
217+
atol::T = eps(T)^(1/3),
218+
diverging_iterates_tol::T = eps(T)^(-1),
219+
diverging_obj_tol::T = -eps(T)^(-1),
220+
cviol_tol::T = eps(T)^(-1),
221+
diverging_max_iter::Int = 5,
222+
cviol_max_iter::Int = 5,
213223
verbose::Int = 0,
214224
max_iter::Int = 10000,
215225
max_time::Float64 = 30.0,
@@ -225,6 +235,9 @@ function SolverCore.solve!(
225235
) where {T, V}
226236
reset!(stats)
227237

238+
local diverging_iter::Int = zero(Int)
239+
local cviol_iter::Int = zero(Int)
240+
228241
# Retrieve workspace
229242
nlp = reg_nlp.model
230243
h = reg_nlp.h
@@ -315,6 +328,8 @@ function SolverCore.solve!(
315328
# objective
316329
fx = obj(nlp, solver.x)
317330
hx = @views h(solver.x[selected])
331+
improper = (hx == -Inf)
332+
318333
objx = fx + hx
319334
set_objective!(stats, objx)
320335
set_solver_specific!(stats, :smooth_obj, fx)
@@ -345,19 +360,19 @@ function SolverCore.solve!(
345360
set_time!(stats, time() - start_time)
346361
set_status!(
347362
stats,
348-
SolverCore.get_status(
349-
nlp,
363+
get_status(
364+
reg_nlp;
350365
elapsed_time = stats.elapsed_time,
351366
iter = stats.iter,
352367
optimal = optimal,
353-
infeasible = false,
354-
parameter_too_large = false,
355-
unbounded = false,
356-
stalled = false,
357-
exception = false,
368+
improper = improper,
369+
diverging_iter = diverging_iter,
370+
cviol_iter = cviol_iter,
358371
max_eval = max_eval,
359372
max_time = max_time,
360373
max_iter = max_iter,
374+
diverging_max_iter = diverging_max_iter,
375+
cviol_max_iter = cviol_max_iter,
361376
),
362377
)
363378

@@ -372,6 +387,13 @@ function SolverCore.solve!(
372387
if !done
373388
if cviol > max(ctol, factor_primal_linear_improvement * cviol_old)
374389
mu *= factor_penalty_up
390+
if cviol > cviol_tol
391+
cviol_iter += 1
392+
end
393+
end
394+
if (fx + hx < diverging_obj_tol) || (norm(solver.x) > diverging_iterates_tol)
395+
mu *= factor_penalty_up
396+
diverging_iter = diverging_iter + 1
375397
end
376398
update_μ!(solver.sub_problem.model, mu)
377399
cviol_old = cviol

src/R2N.jl

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,11 @@ For advanced usage, first define a solver "R2NSolver" to preallocate the memory
137137
138138
# Keyword arguments
139139
- `x::V = nlp.meta.x0`: the initial guess;
140-
- `atol::T = √eps(T)`: absolute tolerance;
141-
- `rtol::T = √eps(T)`: relative tolerance;
140+
- `atol::T = eps(T)^(1/3)`: absolute tolerance
141+
- `diverging_iterates_tol::T = eps(T)^(-1)`: diverging tolerance for the norm of the iterates (the norm should be lower than the tolerance);
142+
- `diverging_obj_tol::T = -eps(T)^(-1)`: diverging tolerance for the objective function (the objective function should be higher than the tolerance);
143+
- `diverging_max_iter::Int = 5`: maximum number of iteration at which `diverging_obj_tol` or `diverging_iterates_tol` is violated;
144+
- `rtol::T = eps(T)^(1/3)`: relative tolerance;
142145
- `neg_tol::T = eps(T)^(1 / 4)`: negative tolerance;
143146
- `max_eval::Int = -1`: maximum number of evaluation of the objective function (negative number means unlimited);
144147
- `max_time::Float64 = 30.0`: maximum time limit in seconds;
@@ -216,8 +219,11 @@ function SolverCore.solve!(
216219
qn_update_y!::Function = _qn_grad_update_y!,
217220
qn_copy!::Function = _qn_grad_copy!,
218221
x::V = reg_nlp.model.meta.x0,
219-
atol::T = eps(T),
220-
rtol::T = eps(T),
222+
atol::T = eps(T)^(1/3),
223+
diverging_iterates_tol::T = eps(T)^(-1),
224+
diverging_obj_tol::T = -eps(T)^(-1),
225+
diverging_max_iter::Int = 5,
226+
rtol::T = eps(T)^(1/3),
221227
neg_tol::T = eps(T)^(1 / 4),
222228
verbose::Int = 0,
223229
max_iter::Int = 10000,
@@ -299,6 +305,7 @@ function SolverCore.solve!(
299305
local ξ1::T
300306
local ρk::T = zero(T)
301307
local prox_evals::Int = 0
308+
local diverging_iter::Int = zero(Int)
302309

303310
fk = compute_obj ? obj(nlp, xk) : stats.solver_specific[:smooth_obj]
304311
compute_grad && grad!(nlp, xk, ∇fk)
@@ -368,9 +375,11 @@ function SolverCore.solve!(
368375
iter = stats.iter,
369376
optimal = solved,
370377
improper = improper,
378+
diverging_iter = diverging_iter,
371379
max_eval = max_eval,
372380
max_time = max_time,
373381
max_iter = max_iter,
382+
diverging_max_iter = diverging_max_iter,
374383
),
375384
)
376385

@@ -475,13 +484,18 @@ function SolverCore.solve!(
475484
set_step_status!(stats, :accepted)
476485
end
477486

478-
if η2 ρk < Inf
479-
σk = max(σk/γ, σmin)
480-
end
481-
482-
if ρk < η1 || ρk == Inf
487+
if (fk + hk < diverging_obj_tol) || (norm(xk) > diverging_iterates_tol)
483488
σk = σk * γ
484-
set_step_status!(stats, :rejected)
489+
diverging_iter = diverging_iter + 1
490+
else
491+
diverging_iter = 0
492+
if η2 ρk < Inf
493+
σk = max(σk / γ, σmin)
494+
end
495+
if ρk < η1 || ρk == Inf
496+
σk = σk * γ
497+
set_step_status!(stats, :rejected)
498+
end
485499
end
486500

487501
ν₁ = θ / (λmax + σk)
@@ -515,9 +529,11 @@ function SolverCore.solve!(
515529
iter = stats.iter,
516530
optimal = solved,
517531
improper = improper,
532+
diverging_iter = diverging_iter,
518533
max_eval = max_eval,
519534
max_time = max_time,
520535
max_iter = max_iter,
536+
diverging_max_iter = diverging_max_iter,
521537
),
522538
)
523539

src/R2_alg.jl

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,12 @@ For advanced usage, first define a solver "R2Solver" to preallocate the memory u
141141
142142
# Keyword arguments
143143
- `x::V = nlp.meta.x0`: the initial guess;
144-
- `atol::T = √eps(T)`: absolute tolerance;
145-
- `rtol::T = √eps(T)`: relative tolerance;
146-
- `neg_tol::T = eps(T)^(1 / 4)`: negative tolerance
144+
- `atol::T = eps(T)^(1/3)`: absolute tolerance
145+
- `diverging_iterates_tol::T = eps(T)^(-1)`: diverging tolerance for the norm of the iterates (the norm should be lower than the tolerance);
146+
- `diverging_obj_tol::T = -eps(T)^(-1)`: diverging tolerance for the objective function (the objective function should be higher than the tolerance);
147+
- `diverging_max_iter::Int = 5`: maximum number of iteration at which `diverging_obj_tol` or `diverging_iterates_tol` is violated;
148+
- `rtol::T = eps(T)^(1/3)`: relative tolerance;
149+
- `neg_tol::T = eps(T)^(1 / 4)`: negative tolerance;
147150
- `max_eval::Int = -1`: maximum number of evaluation of the objective function (negative number means unlimited);
148151
- `max_time::Float64 = 30.0`: maximum time limit in seconds;
149152
- `max_iter::Int = 10000`: maximum number of iterations;
@@ -313,8 +316,11 @@ function SolverCore.solve!(
313316
stats::GenericExecutionStats{T, V};
314317
callback = (args...) -> nothing,
315318
x::V = reg_nlp.model.meta.x0,
316-
atol::T = eps(T),
317-
rtol::T = eps(T),
319+
atol::T = eps(T)^(1/3),
320+
diverging_iterates_tol::T = eps(T)^(-1),
321+
diverging_obj_tol::T = -eps(T)^(-1),
322+
diverging_max_iter::Int = 5,
323+
rtol::T = eps(T)^(1/3),
318324
neg_tol::T = eps(T)^(1 / 4),
319325
verbose::Int = 0,
320326
max_iter::Int = 10000,
@@ -386,6 +392,7 @@ function SolverCore.solve!(
386392

387393
local ξ::T
388394
local ρk::T = zero(T)
395+
local diverging_iter::Int = zero(Int)
389396
σk = max(1 / ν, σmin)
390397
ν = 1 / σk
391398
sqrt_ξ_νInv = one(T)
@@ -420,14 +427,16 @@ function SolverCore.solve!(
420427
set_status!(
421428
stats,
422429
get_status(
423-
reg_nlp,
430+
reg_nlp;
424431
elapsed_time = stats.elapsed_time,
425432
iter = stats.iter,
426433
optimal = solved,
427434
improper = improper,
435+
diverging_iter = diverging_iter,
428436
max_eval = max_eval,
429437
max_time = max_time,
430438
max_iter = max_iter,
439+
diverging_max_iter = diverging_max_iter,
431440
),
432441
)
433442

@@ -476,12 +485,18 @@ function SolverCore.solve!(
476485
set_step_status!(stats, :accepted)
477486
end
478487

479-
if η2 ρk < Inf
480-
σk = max(σk / γ, σmin)
481-
end
482-
if ρk < η1 || ρk == Inf
488+
if (fk + hk < diverging_obj_tol) || (norm(xk) > diverging_iterates_tol)
483489
σk = σk * γ
484-
set_step_status!(stats, :rejected)
490+
diverging_iter = diverging_iter + 1
491+
else
492+
diverging_iter = 0
493+
if η2 ρk < Inf
494+
σk = max(σk / γ, σmin)
495+
end
496+
if ρk < η1 || ρk == Inf
497+
σk = σk * γ
498+
set_step_status!(stats, :rejected)
499+
end
485500
end
486501

487502
ν = 1 / σk
@@ -506,14 +521,16 @@ function SolverCore.solve!(
506521
set_status!(
507522
stats,
508523
get_status(
509-
reg_nlp,
524+
reg_nlp;
510525
elapsed_time = stats.elapsed_time,
511526
iter = stats.iter,
512527
optimal = solved,
513528
improper = improper,
529+
diverging_iter = diverging_iter,
514530
max_eval = max_eval,
515531
max_time = max_time,
516532
max_iter = max_iter,
533+
diverging_max_iter = diverging_max_iter,
517534
),
518535
)
519536

src/utils.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,13 @@ function get_status(
142142
iter = 0,
143143
optimal = false,
144144
improper = false,
145+
diverging_iter = 0,
146+
cviol_iter = 0,
145147
max_eval = Inf,
146148
max_time = Inf,
147149
max_iter = Inf,
150+
diverging_max_iter = Inf,
151+
cviol_max_iter = Inf,
148152
) where {M <: AbstractRegularizedNLPModel}
149153
if optimal
150154
:first_order
@@ -156,6 +160,10 @@ function get_status(
156160
:max_time
157161
elseif neval_obj(reg_nlp.model) >= max_eval && max_eval >= 0
158162
:max_eval
163+
elseif diverging_max_iter < diverging_iter
164+
:unbounded
165+
elseif cviol_max_iter < cviol_iter
166+
:infeasible
159167
else
160168
:unknown
161169
end

0 commit comments

Comments
 (0)