Skip to content

Commit e6aa0c3

Browse files
committed
nonmonotone PANOC
1 parent e3308ce commit e6aa0c3

File tree

5 files changed

+72
-7
lines changed

5 files changed

+72
-7
lines changed

src/algorithms/panoc.jl

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ See also: [`PANOC`](@ref).
3232
- `minimum_gamma=1e-7`: lower bound to `gamma` in case `adaptive == true`.
3333
- `max_backtracks=20`: maximum number of line-search backtracks.
3434
- `directions=LBFGS(5)`: strategy to use to compute line-search directions.
35+
- `monotonicity=1`: parameter controlling the averaging scheme for nonmonotone linesearch; monotonicity ∈ (0,1], monotone scheme by default.
3536
3637
# References
3738
1. Stella, Themelis, Sopasakis, Patrinos, "A simple and efficient algorithm for nonlinear model predictive control", 56th IEEE Conference on Decision and Control (2017).
@@ -49,6 +50,7 @@ Base.@kwdef struct PANOCIteration{R,Tx,Tf,TA,Tg,TLf,Tgamma,D}
4950
minimum_gamma::R = real(eltype(x0))(1e-7)
5051
max_backtracks::Int = 20
5152
directions::D = LBFGS(5)
53+
monotonicity::R = real(eltype(x0))(1)
5254
end
5355

5456
Base.IteratorSize(::Type{<:PANOCIteration}) = Base.IsInfinite()
@@ -65,6 +67,7 @@ Base.@kwdef mutable struct PANOCState{R,Tx,TAx,TH}
6567
g_z::R # value of nonsmooth term (at z)
6668
res::Tx # fixed-point residual at iterate (= x - z)
6769
H::TH # variable metric
70+
merit::R = zero(gamma)
6871
tau::R = zero(gamma)
6972
x_prev::Tx = similar(x)
7073
res_prev::Tx = similar(x)
@@ -108,6 +111,8 @@ function Base.iterate(iter::PANOCIteration{R}) where {R}
108111
res = x - z,
109112
H = initialize(iter.directions, x),
110113
)
114+
# initialize merit
115+
state.merit = f_model(iter, state) + state.g_z
111116
return state, state
112117
end
113118

@@ -138,7 +143,7 @@ reset_direction_state!(iter::PANOCIteration, state::PANOCState) =
138143
function Base.iterate(iter::PANOCIteration{R,Tx,Tf}, state::PANOCState) where {R,Tx,Tf}
139144
f_Az, a, b, c = R(Inf), R(Inf), R(Inf), R(Inf)
140145

141-
f_Az_upp = if iter.adaptive == true
146+
if iter.adaptive == true
142147
gamma_prev = state.gamma
143148
state.gamma, state.g_z, f_Az, f_Az_upp = backtrack_stepsize!(
144149
state.gamma,
@@ -160,13 +165,8 @@ function Base.iterate(iter::PANOCIteration{R,Tx,Tf}, state::PANOCState) where {R
160165
if state.gamma != gamma_prev
161166
reset_direction_state!(iter, state)
162167
end
163-
f_Az_upp
164-
else
165-
f_model(iter, state)
166168
end
167169

168-
# compute FBE
169-
FBE_x = f_Az_upp + state.g_z
170170

171171
# compute direction
172172
set_next_direction!(iter, state)
@@ -192,6 +192,8 @@ function Base.iterate(iter::PANOCIteration{R,Tx,Tf}, state::PANOCState) where {R
192192
copyto!(state.z_curr, state.z)
193193
state.f_Ax = state.f_Ax_d
194194

195+
# retrieve merit and set threshold
196+
FBE_x = state.merit
195197
sigma = iter.beta * (0.5 / state.gamma) * (1 - iter.alpha)
196198
tol = 10 * eps(R) * (1 + abs(FBE_x))
197199
threshold = FBE_x - sigma * norm(state.res)^2 + tol
@@ -250,7 +252,8 @@ function Base.iterate(iter::PANOCIteration{R,Tx,Tf}, state::PANOCState) where {R
250252
end
251253

252254
update_direction_state!(iter, state)
253-
255+
# update merit with averaging rule
256+
state.merit = (1 - iter.monotonicity) * state.merit + iter.monotonicity * FBE_x_new
254257
return state, state
255258
end
256259

test/problems/test_lasso_small.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,17 @@ using ProximalAlgorithms:
191191

192192
end
193193

194+
@testset "PANOC (fixed, nonmonotone)" begin
195+
x0 = zeros(T, n)
196+
x0_backup = copy(x0)
197+
solver = ProximalAlgorithms.PANOC(tol = TOL, monotonicity=R(0.3))
198+
x, it = @inferred solver(x0 = x0, f = f_autodiff, A = A, g = g, Lf = Lf)
199+
@test eltype(x) == T
200+
@test norm(x - x_star, Inf) <= TOL
201+
@test it < 20
202+
@test x0 == x0_backup
203+
end
204+
194205
@testset "PANOC (adaptive step)" begin
195206
x0 = zeros(T, n)
196207
x0_backup = copy(x0)
@@ -202,6 +213,17 @@ using ProximalAlgorithms:
202213
@test x0 == x0_backup
203214
end
204215

216+
@testset "PANOC (adaptive, nonmonotone)" begin
217+
x0 = zeros(T, n)
218+
x0_backup = copy(x0)
219+
solver = ProximalAlgorithms.PANOC(adaptive = true, tol = TOL, monotonicity=R(0.3))
220+
x, it = @inferred solver(x0 = x0, f = f_autodiff, A = A, g = g)
221+
@test eltype(x) == T
222+
@test norm(x - x_star, Inf) <= TOL
223+
@test it < 35
224+
@test x0 == x0_backup
225+
end
226+
205227
@testset "PANOCplus (fixed step)" begin
206228
x0 = zeros(T, n)
207229
x0_backup = copy(x0)

test/problems/test_lasso_small_strongly_convex.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,15 @@ using ProximalAlgorithms
188188
@test x0 == x0_backup
189189
end
190190

191+
@testset "PANOC (nonmonotone)" begin
192+
solver = ProximalAlgorithms.PANOC(tol=TOL, monotonicity=T(0.2))
193+
y, it = solver(x0=x0, f=fA_autodiff, g=g, Lf=Lf)
194+
@test eltype(y) == T
195+
@test norm(y - x_star, Inf) <= TOL
196+
@test it < 45
197+
@test x0 == x0_backup
198+
end
199+
191200
@testset "PANOCplus" begin
192201
solver = ProximalAlgorithms.PANOCplus(tol=TOL)
193202
y, it = solver(x0=x0, f=fA_autodiff, g=g, Lf=Lf)

test/problems/test_nonconvex_qp.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ using Test
3535
@test x0 == x0_backup
3636
end
3737

38+
@testset "PANOC (nonmonotone)" begin
39+
x0 = zeros(T, n)
40+
x0_backup = copy(x0)
41+
solver = ProximalAlgorithms.PANOC(tol = TOL, monotonicity=T(0.1))
42+
x, it = solver(x0 = x0, f = f, g = g)
43+
z = min.(upp, max.(low, x .- gamma .* (Q * x + q)))
44+
@test norm(x - z, Inf) / gamma <= TOL
45+
@test x0 == x0_backup
46+
end
47+
3848
@testset "PANOCplus" begin
3949
x0 = zeros(T, n)
4050
x0_backup = copy(x0)
@@ -112,6 +122,16 @@ end
112122
@test x0 == x0_backup
113123
end
114124

125+
@testset "PANOC (nonmonotone)" begin
126+
x0 = zeros(T, n)
127+
x0_backup = copy(x0)
128+
solver = ProximalAlgorithms.PANOC(tol = TOL, monotonicity=T(0.1))
129+
x, it = solver(x0 = x0, f = f, g = g)
130+
z = min.(upp, max.(low, x .- gamma .* (Q * x + q)))
131+
@test norm(x - z, Inf) / gamma <= TOL
132+
@test x0 == x0_backup
133+
end
134+
115135
@testset "PANOCplus" begin
116136
x0 = zeros(T, n)
117137
x0_backup = copy(x0)

test/problems/test_sparse_logistic_small.jl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,17 @@ using LinearAlgebra
120120
@test x0 == x0_backup
121121
end
122122

123+
@testset "PANOC (adaptive, nonmonotone)" begin
124+
x0 = zeros(T, n)
125+
x0_backup = copy(x0)
126+
solver = ProximalAlgorithms.PANOC(adaptive = true, tol = TOL, monotonicity=R(0.5))
127+
x, it = solver(x0 = x0, f = f_autodiff, A = A, g = g)
128+
@test eltype(x) == T
129+
@test norm(x - x_star, Inf) <= 1e-4
130+
@test it < 50
131+
@test x0 == x0_backup
132+
end
133+
123134
@testset "PANOCplus (adaptive step)" begin
124135
x0 = zeros(T, n)
125136
x0_backup = copy(x0)

0 commit comments

Comments
 (0)