Skip to content

Commit 8851619

Browse files
authored
Add StopWhenSubgradientNormLess stopping criterion (#352)
1 parent 89d88ad commit 8851619

File tree

5 files changed

+62
-1
lines changed

5 files changed

+62
-1
lines changed

Changelog.md

+6
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1111

1212
* Allow the `message=` of the `DebugIfEntry` debug action to contain a format element to print the field in the message as well.
1313

14+
## [0.4.51] January 30, 2024
15+
16+
### Added
17+
18+
* A `StopWhenSubgradientNormLess` stopping criterion for subgradient-based optimization.
19+
1420
## [0.4.50] January 26, 2024
1521

1622
### Fixed

Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "Manopt"
22
uuid = "0fc0a36d-df90-57f3-8f93-d78a9fc72bb5"
33
authors = ["Ronny Bergmann <[email protected]>"]
4-
version = "0.4.50"
4+
version = "0.4.51"
55

66
[deps]
77
ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4"

src/Manopt.jl

+1
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ export StopAfter,
448448
StopWhenPopulationConcentrated,
449449
StopWhenSmallerOrEqual,
450450
StopWhenStepsizeLess,
451+
StopWhenSubgradientNormLess,
451452
StopWhenTrustRegionIsExceeded
452453
export get_active_stopping_criteria,
453454
get_stopping_criteria, get_reason, get_stopping_criterion

src/plans/stopping_criterion.jl

+51
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,57 @@ function update_stopping_criterion!(c::StopAfterIteration, ::Val{:MaxIteration},
173173
return c
174174
end
175175

176+
"""
177+
StopWhenSubgradientNormLess <: StoppingCriterion
178+
179+
A stopping criterion based on the current subgradient norm.
180+
181+
# Constructor
182+
183+
StopWhenSubgradientNormLess(ε::Float64)
184+
185+
Create a stopping criterion with threshold `ε` for the subgradient, that is, this criterion
186+
indicates to stop when [`get_subgradient`](@ref) returns a subgradient vector of norm less than `ε`.
187+
"""
188+
mutable struct StopWhenSubgradientNormLess <: StoppingCriterion
189+
threshold::Float64
190+
reason::String
191+
StopWhenSubgradientNormLess::Float64) = new(ε, "")
192+
end
193+
function (c::StopWhenSubgradientNormLess)(
194+
mp::AbstractManoptProblem, s::AbstractManoptSolverState, i::Int
195+
)
196+
M = get_manifold(mp)
197+
(i == 0) && (c.reason = "") # reset on init
198+
if (norm(M, get_iterate(s), get_subgradient(s)) < c.threshold) && (i > 0)
199+
c.reason = "The algorithm reached approximately critical point after $i iterations; the subgradient norm ($(norm(M,get_iterate(s),get_subgradient(s)))) is less than $(c.threshold).\n"
200+
return true
201+
end
202+
return false
203+
end
204+
function status_summary(c::StopWhenSubgradientNormLess)
205+
has_stopped = length(c.reason) > 0
206+
s = has_stopped ? "reached" : "not reached"
207+
return "|subgrad f| < $(c.threshold): $s"
208+
end
209+
indicates_convergence(c::StopWhenSubgradientNormLess) = true
210+
function show(io::IO, c::StopWhenSubgradientNormLess)
211+
return print(
212+
io, "StopWhenSubgradientNormLess($(c.threshold))\n $(status_summary(c))"
213+
)
214+
end
215+
"""
216+
update_stopping_criterion!(c::StopWhenSubgradientNormLess, :MinSubgradNorm, v::Float64)
217+
218+
Update the minimal subgradient norm when an algorithm shall stop
219+
"""
220+
function update_stopping_criterion!(
221+
c::StopWhenSubgradientNormLess, ::Val{:MinSubgradNorm}, v::Float64
222+
)
223+
c.threshold = v
224+
return c
225+
end
226+
176227
"""
177228
StopWhenChangeLess <: StoppingCriterion
178229

test/plans/test_stopping_criteria.jl

+3
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ end
7474
c = StopWhenGradientNormLess(1e-6)
7575
sc = "StopWhenGradientNormLess(1.0e-6)\n $(Manopt.status_summary(c))"
7676
@test repr(c) == sc
77+
c2 = StopWhenSubgradientNormLess(1e-6)
78+
sc2 = "StopWhenSubgradientNormLess(1.0e-6)\n $(Manopt.status_summary(c2))"
79+
@test repr(c2) == sc2
7780
d = StopWhenAll(a, b, c)
7881
@test typeof(d) === typeof(a & b & c)
7982
@test typeof(d) === typeof(a & (b & c))

0 commit comments

Comments
 (0)