From 1653e29f5986ad4d536986ff73ca4844f5032ca0 Mon Sep 17 00:00:00 2001 From: Maxence Gollier Date: Mon, 8 Sep 2025 10:49:18 -0400 Subject: [PATCH 01/12] remove cumulative subiters --- src/AL_alg.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AL_alg.jl b/src/AL_alg.jl index 3745d685..9bbab380 100644 --- a/src/AL_alg.jl +++ b/src/AL_alg.jl @@ -299,7 +299,7 @@ function SolverCore.solve!( ) solver.x .= subout.solution solver.cx .= solver.sub_model.cx - subiters += subout.iter + subiters = subout.iter # objective fx = obj(nlp, solver.x) From dfa059f4e72c21c6aaccfbd03d95e72992e5924e Mon Sep 17 00:00:00 2001 From: Maxence Gollier Date: Mon, 8 Sep 2025 11:09:12 -0400 Subject: [PATCH 02/12] allocation-free solver --- src/AL_alg.jl | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/AL_alg.jl b/src/AL_alg.jl index 9bbab380..b802f5ba 100644 --- a/src/AL_alg.jl +++ b/src/AL_alg.jl @@ -138,14 +138,14 @@ Notably, you can access, and modify, the following: - `stats.solver_specific[:smooth_obj]`: current value of the smooth part of the objective function; - `stats.solver_specific[:nonsmooth_obj]`: current value of the nonsmooth part of the objective function. """ -mutable struct ALSolver{T, V, M, ST} <: AbstractOptimizationSolver +mutable struct ALSolver{T, V, M, Pb, ST} <: AbstractOptimizationSolver x::V cx::V y::V has_bnds::Bool - sub_model::AugLagModel{M, T, V} + sub_problem::Pb sub_solver::ST - sub_stats::GenericExecutionStats{T, V, V, Any} + sub_stats::GenericExecutionStats{T, V, V, T} end function ALSolver(reg_nlp::AbstractRegularizedNLPModel{T, V}; kwargs...) where {T, V} @@ -156,11 +156,12 @@ function ALSolver(reg_nlp::AbstractRegularizedNLPModel{T, V}; kwargs...) where { y = V(undef, ncon) has_bnds = has_bounds(nlp) sub_model = AugLagModel(nlp, V(undef, ncon), T(0), x, T(0), V(undef, ncon)) + sub_problem = RegularizedNLPModel(sub_model, reg_nlp.h,reg_nlp.selected) sub_solver = R2Solver(reg_nlp; kwargs...) - sub_stats = GenericExecutionStats(sub_model) + sub_stats = RegularizedExecutionStats(sub_problem) M = typeof(nlp) ST = typeof(sub_solver) - return ALSolver{T, V, M, ST}(x, cx, y, has_bnds, sub_model, sub_solver, sub_stats) + return ALSolver{T, V, M, typeof(sub_problem), ST}(x, cx, y, has_bnds, sub_problem, sub_solver, sub_stats) end @doc (@doc ALSolver) function AL(::Val{:equ}, reg_nlp::AbstractRegularizedNLPModel; kwargs...) @@ -182,7 +183,7 @@ function SolverCore.solve!( model::AbstractRegularizedNLPModel; kwargs..., ) - stats = GenericExecutionStats(model.model) + stats = RegularizedExecutionStats(model) solve!(solver, model, stats; kwargs...) end @@ -209,6 +210,7 @@ function SolverCore.solve!( factor_decrease_subtol::T = T(1 // 4), dual_safeguard = project_y!, ) where {T, V} + reset!(stats) # Retrieve workspace nlp = reg_nlp.model @@ -254,8 +256,8 @@ function SolverCore.solve!( set_solver_specific!(stats, :nonsmooth_obj, hx) mu = init_penalty - solver.sub_model.y .= solver.y - update_μ!(solver.sub_model, mu) + solver.sub_problem.model.y .= solver.y + update_μ!(solver.sub_problem.model, mu) cviol = norm(solver.cx, Inf) cviol_old = Inf @@ -279,15 +281,13 @@ function SolverCore.solve!( iter += 1 # dual safeguard - dual_safeguard(solver.sub_model) + dual_safeguard(solver.sub_problem.model) - # AL subproblem - sub_reg_nlp = RegularizedNLPModel(solver.sub_model, h, selected) subtol = max(subtol, atol) reset!(subout) solve!( solver.sub_solver, - sub_reg_nlp, + solver.sub_problem, subout, x = solver.x, atol = subtol, @@ -298,7 +298,7 @@ function SolverCore.solve!( verbose = subsolver_verbose, ) solver.x .= subout.solution - solver.cx .= solver.sub_model.cx + solver.cx .= solver.sub_problem.model.cx subiters = subout.iter # objective @@ -310,8 +310,8 @@ function SolverCore.solve!( set_solver_specific!(stats, :nonsmooth_obj, hx) # dual estimate - update_y!(solver.sub_model) - solver.y .= solver.sub_model.y + update_y!(solver.sub_problem.model) + solver.y .= solver.sub_problem.model.y set_constraint_multipliers!(stats, solver.y) # stationarity measure @@ -362,7 +362,7 @@ function SolverCore.solve!( if cviol > max(ctol, factor_primal_linear_improvement * cviol_old) mu *= factor_penalty_up end - update_μ!(solver.sub_model, mu) + update_μ!(solver.sub_problem.model, mu) cviol_old = cviol subtol *= factor_decrease_subtol rem_eval = max_eval < 0 ? max_eval : max_eval - neval_obj(nlp) From e7559cf34542c2ab30b001c703990372364b0235 Mon Sep 17 00:00:00 2001 From: Maxence Gollier Date: Mon, 8 Sep 2025 12:08:08 -0400 Subject: [PATCH 03/12] add tests for augmented lagrangian --- Project.toml | 4 +++- test/runtests.jl | 4 +++- test/test_allocs.jl | 7 +++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index baf0d479..929ea101 100644 --- a/Project.toml +++ b/Project.toml @@ -20,6 +20,7 @@ SolverCore = "ff4d7338-4cf1-434d-91df-b86cb86fb843" [compat] Arpack = "0.5" +CUTEst = "1.3.4" LinearOperators = "2.10.0" ManualNLPModels = "0.2.0" NLPModels = "0.19, 0.20, 0.21" @@ -32,10 +33,11 @@ SolverCore = "0.3.0" julia = "^1.6.0" [extras] +CUTEst = "1b53aba6-35b6-5f92-a507-53c67d53f819" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" RegularizedProblems = "ea076b23-609f-44d2-bb12-a4ae45328278" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" TestSetExtensions = "98d24dd4-01ad-11ea-1b02-c9a08f80db04" [targets] -test = ["Random", "RegularizedProblems", "Test", "TestSetExtensions"] +test = ["CUTEst", "Random", "RegularizedProblems", "Test", "TestSetExtensions"] diff --git a/test/runtests.jl b/test/runtests.jl index 1eddcad1..fc2d4c00 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,7 +1,7 @@ using LinearAlgebra: length using LinearAlgebra, Random, Test using ProximalOperators -using NLPModels, NLPModelsModifiers, RegularizedProblems, RegularizedOptimization, SolverCore +using CUTEst, NLPModels, NLPModelsModifiers, RegularizedProblems, RegularizedOptimization, SolverCore const global compound = 1 const global nz = 10 * compound @@ -10,6 +10,8 @@ const global bpdn, bpdn_nls, sol = bpdn_model(compound) const global bpdn2, bpdn_nls2, sol2 = bpdn_model(compound, bounds = true) const global λ = norm(grad(bpdn, zeros(bpdn.meta.nvar)), Inf) / 10 +include("test_AL.jl") + for (mod, mod_name) ∈ ((x -> x, "exact"), (LSR1Model, "lsr1"), (LBFGSModel, "lbfgs")) for (h, h_name) ∈ ((NormL0(λ), "l0"), (NormL1(λ), "l1"), (IndBallL0(10 * compound), "B0")) for solver_sym ∈ (:R2, :TR) diff --git a/test/test_allocs.jl b/test/test_allocs.jl index dbd641a5..69ba8c27 100644 --- a/test/test_allocs.jl +++ b/test/test_allocs.jl @@ -65,5 +65,12 @@ end @test stats.status == :first_order end end + @testset "Augmented Lagrangian" begin + reg_nlp = RegularizedNLPModel(CUTEstModel("HS8"), h) + solver = ALSolver(reg_nlp) + stats = RegularizedExecutionStats(reg_nlp) + @test @wrappedallocs(solve!(solver, reg_nlp, stats, atol = 1e-3)) == 0 + @test stats.status == :first_order + end end end From 97ccbd55b3e75e79dee916970d3653f20ee48556 Mon Sep 17 00:00:00 2001 From: Maxence Gollier Date: Mon, 8 Sep 2025 16:15:15 -0400 Subject: [PATCH 04/12] add augmented lagrangian tests --- test/test_AL.jl | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 test/test_AL.jl diff --git a/test/test_AL.jl b/test/test_AL.jl new file mode 100644 index 00000000..7fcf04ca --- /dev/null +++ b/test/test_AL.jl @@ -0,0 +1,16 @@ +problem_list = ["HS8",] + +@testset "Augmented Lagrangian" begin + for problem_name in problem_list + nlp = CUTEstModel(problem_name) + for h in (NormL1(1.0), NormL2(1.0)) + stats = AL(nlp, h, atol = 1e-3, verbose = 1) + @test stats.status == :first_order + @test stats.primal_feas <= 1e-2 + @test stats.dual_feas <= 1e-2 + @test length(stats.solution) == nlp.meta.nvar + @test typeof(stats.solution) == typeof(nlp.meta.x0) + end + finalize(nlp) + end +end From 26637c7c179d3b7034ecbe1315dd250ae1e6817e Mon Sep 17 00:00:00 2001 From: Maxence Gollier Date: Sun, 14 Sep 2025 17:47:13 -0400 Subject: [PATCH 05/12] test with adnlpproblmems --- Project.toml | 10 +++++++--- test/runtests.jl | 3 ++- test/test_AL.jl | 5 ++--- test/test_allocs.jl | 3 ++- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Project.toml b/Project.toml index 929ea101..e446b814 100644 --- a/Project.toml +++ b/Project.toml @@ -4,6 +4,7 @@ author = ["Robert Baraldi and Dominique Orban x, "exact"), (LSR1Model, "lsr1"), (LBFGSModel, "lbfgs")) diff --git a/test/test_AL.jl b/test/test_AL.jl index 7fcf04ca..5c458de3 100644 --- a/test/test_AL.jl +++ b/test/test_AL.jl @@ -1,8 +1,7 @@ -problem_list = ["HS8",] @testset "Augmented Lagrangian" begin - for problem_name in problem_list - nlp = CUTEstModel(problem_name) + for problem in problem_list + nlp = eval(problem)(backend = :optimized) for h in (NormL1(1.0), NormL2(1.0)) stats = AL(nlp, h, atol = 1e-3, verbose = 1) @test stats.status == :first_order diff --git a/test/test_allocs.jl b/test/test_allocs.jl index 69ba8c27..cf348baf 100644 --- a/test/test_allocs.jl +++ b/test/test_allocs.jl @@ -66,7 +66,8 @@ end end end @testset "Augmented Lagrangian" begin - reg_nlp = RegularizedNLPModel(CUTEstModel("HS8"), h) + continue # FIXME : fails due to type instabilities in ADNLPModels... + reg_nlp = RegularizedNLPModel(hs8(backend = :generic), h) solver = ALSolver(reg_nlp) stats = RegularizedExecutionStats(reg_nlp) @test @wrappedallocs(solve!(solver, reg_nlp, stats, atol = 1e-3)) == 0 From 0989468531dab5cc7876df5b31013b6cba4778ed Mon Sep 17 00:00:00 2001 From: Maxence Gollier Date: Sun, 14 Sep 2025 17:50:52 -0400 Subject: [PATCH 06/12] fix documenter compat --- docs/Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Project.toml b/docs/Project.toml index ed025f5a..531318e4 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -2,4 +2,4 @@ Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" [compat] -Documenter = "~0.25" +Documenter = "0.25" From e53e97384af038358a9be8c0dcc5a0b860d5e025 Mon Sep 17 00:00:00 2001 From: Maxence Gollier Date: Sun, 14 Sep 2025 17:57:31 -0400 Subject: [PATCH 07/12] move adnlpmodels to extras --- Project.toml | 6 +++--- docs/Project.toml | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/Project.toml b/Project.toml index e446b814..b8f8b8ed 100644 --- a/Project.toml +++ b/Project.toml @@ -4,7 +4,6 @@ author = ["Robert Baraldi and Dominique Orban Date: Sun, 14 Sep 2025 17:57:49 -0400 Subject: [PATCH 08/12] revert changes to docs --- docs/Project.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/Project.toml b/docs/Project.toml index dfa65cd1..ed025f5a 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -1,2 +1,5 @@ [deps] Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" + +[compat] +Documenter = "~0.25" From 634bdb539b6350e952b4755cb512d8d0a81eb663 Mon Sep 17 00:00:00 2001 From: Maxence Gollier Date: Sun, 14 Sep 2025 17:59:29 -0400 Subject: [PATCH 09/12] fix tests --- Project.toml | 2 -- test/test_AL.jl | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index b8f8b8ed..e6e5aff0 100644 --- a/Project.toml +++ b/Project.toml @@ -15,7 +15,6 @@ Percival = "01435c0c-c90d-11e9-3788-63660f8fbccc" Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7" ProximalOperators = "a725b495-10eb-56fe-b38b-717eba820537" RegularizedProblems = "ea076b23-609f-44d2-bb12-a4ae45328278" -ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267" ShiftedProximalOperators = "d4fd37fa-580c-4e43-9b30-361c21aae263" SolverCore = "ff4d7338-4cf1-434d-91df-b86cb86fb843" @@ -30,7 +29,6 @@ OptimizationProblems = "0.9.2" Percival = "0.7.2" ProximalOperators = "0.15" RegularizedProblems = "0.1.1" -ReverseDiff = "1.16.1" ShiftedProximalOperators = "0.2" SolverCore = "0.3.0" julia = "^1.6.0" diff --git a/test/test_AL.jl b/test/test_AL.jl index 5c458de3..855ad15b 100644 --- a/test/test_AL.jl +++ b/test/test_AL.jl @@ -1,4 +1,6 @@ +problem_list = [:hs8] + @testset "Augmented Lagrangian" begin for problem in problem_list nlp = eval(problem)(backend = :optimized) From 229e314e97691b0ec46437bd37299614eae647df Mon Sep 17 00:00:00 2001 From: Maxence Gollier Date: Sun, 14 Sep 2025 18:11:07 -0400 Subject: [PATCH 10/12] move alloc tests to the end --- test/runtests.jl | 1 - 1 file changed, 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index bc0ce7b2..fb32ce5e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -10,7 +10,6 @@ const global bpdn, bpdn_nls, sol = bpdn_model(compound) const global bpdn2, bpdn_nls2, sol2 = bpdn_model(compound, bounds = true) const global λ = norm(grad(bpdn, zeros(bpdn.meta.nvar)), Inf) / 10 -include("test_allocs.jl") include("test_AL.jl") for (mod, mod_name) ∈ ((x -> x, "exact"), (LSR1Model, "lsr1"), (LBFGSModel, "lbfgs")) From c3c3d08dea8f0f88fc9e43bbbafdec1a0ea29dc8 Mon Sep 17 00:00:00 2001 From: Maxence Gollier Date: Sun, 14 Sep 2025 18:26:23 -0400 Subject: [PATCH 11/12] remove ell_2 norm test --- test/test_AL.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_AL.jl b/test/test_AL.jl index 855ad15b..bd328c3b 100644 --- a/test/test_AL.jl +++ b/test/test_AL.jl @@ -4,7 +4,7 @@ problem_list = [:hs8] @testset "Augmented Lagrangian" begin for problem in problem_list nlp = eval(problem)(backend = :optimized) - for h in (NormL1(1.0), NormL2(1.0)) + for h in (NormL1(1.0)) stats = AL(nlp, h, atol = 1e-3, verbose = 1) @test stats.status == :first_order @test stats.primal_feas <= 1e-2 From 056cc8652363ed1fedfc00452e3fd098757a2ff4 Mon Sep 17 00:00:00 2001 From: Maxence Gollier Date: Sun, 14 Sep 2025 18:34:53 -0400 Subject: [PATCH 12/12] fix test --- test/test_AL.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_AL.jl b/test/test_AL.jl index bd328c3b..afe3ba41 100644 --- a/test/test_AL.jl +++ b/test/test_AL.jl @@ -4,7 +4,7 @@ problem_list = [:hs8] @testset "Augmented Lagrangian" begin for problem in problem_list nlp = eval(problem)(backend = :optimized) - for h in (NormL1(1.0)) + for h in (NormL1(1.0),) stats = AL(nlp, h, atol = 1e-3, verbose = 1) @test stats.status == :first_order @test stats.primal_feas <= 1e-2