Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor get_reason #395

Merged
merged 25 commits into from
Jun 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable Changes to the Julia package `Manopt.jl` will be documented in this
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.4.65] June 13, 2024

### Changed

* refactor stopping criteria to not store a `sc.reason` internally, but instead only
generate the reason (and hence allocate a string) when actually asked for a reason.

## [0.4.64] June 4, 2024

### Added
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Manopt"
uuid = "0fc0a36d-df90-57f3-8f93-d78a9fc72bb5"
authors = ["Ronny Bergmann <manopt@ronnybergmann.net>"]
version = "0.4.64"
version = "0.4.65"

[deps]
ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
Expand Down
3 changes: 2 additions & 1 deletion docs/src/plans/stopping_criteria.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ The stopping criteria `s` might have certain internal values/fields it uses to v
This is done when calling them as a function `s(amp::AbstractManoptProblem, ams::AbstractManoptSolverState)`,
where the [`AbstractManoptProblem`](@ref) and the [`AbstractManoptSolverState`](@ref) together represent
the current state of the solver. The functor returns either `false` when the stopping criterion is not fulfilled or `true` otherwise.
One field all criteria should have is the `s.reason`, a string giving the reason to stop, see [`get_reason`](@ref).
One field all criteria should have is the `s.at_iteration`, to indicate at which iteration
the stopping criterion (last) indicated to stop. `0` refers to an indication _before_ starting the algorithm, while any negative number meant the stopping criterion is not (yet) fulfilled. To can access a string giving the reason of stopping see [`get_reason`](@ref).

## Generic stopping criteria

Expand Down
59 changes: 43 additions & 16 deletions src/plans/bundle_plan.jl
Original file line number Diff line number Diff line change
Expand Up @@ -92,40 +92,67 @@ For the [`proximal_bundle_method`](@ref), the equation reads ``-ν = μ \lvert d

# Constructors

StopWhenLagrangeMultiplierLess(tolerance=1e-6; mode::Symbol=:estimate)
StopWhenLagrangeMultiplierLess(tolerance=1e-6; mode::Symbol=:estimate, names=nothing)

Create the stopping criterion for one of the `mode`s mentioned.
Note that tolerance can be a single number for the `:estimate` case,
but a vector of two values is required for the `:both` mode.
Here the first entry specifies the tolerance for ``ε`` (``c``),
the second the tolerance for ``\lvert g \rvert`` (``\lvert d \rvert``), respectively.
"""
mutable struct StopWhenLagrangeMultiplierLess{T<:Real,A<:AbstractVector{<:T}} <:
StoppingCriterion
tolerance::A
mutable struct StopWhenLagrangeMultiplierLess{
T<:Real,A<:AbstractVector{<:T},B<:Union{Nothing,<:AbstractVector{<:String}}
} <: StoppingCriterion
tolerances::A
values::A
names::B
mode::Symbol
reason::String
at_iteration::Int
function StopWhenLagrangeMultiplierLess(tol::T; mode::Symbol=:estimate) where {T<:Real}
return new{T,Vector{T}}([tol], mode, "", 0)
function StopWhenLagrangeMultiplierLess(
tol::T; mode::Symbol=:estimate, names::B=nothing
) where {T<:Real,B<:Union{Nothing,<:AbstractVector{<:String}}}
return new{T,Vector{T},B}([tol], zero([tol]), names, mode, -1)
end
function StopWhenLagrangeMultiplierLess(
tols::A; mode::Symbol=:estimate
) where {T<:Real,A<:AbstractVector{<:T}}
return new{T,A}(tols, mode, "", 0)
tols::A; mode::Symbol=:estimate, names::B=nothing
) where {T<:Real,A<:AbstractVector{<:T},B<:Union{Nothing,<:AbstractVector{<:String}}}
return new{T,A,B}(tols, zero(tols), names, mode, -1)
end
end
function get_reason(sc::StopWhenLagrangeMultiplierLess)
if (sc.at_iteration >= 0)
if isnothing(sc.names)
tol_str = join(
["$ai < $bi" for (ai, bi) in zip(sc.values, sc.tolerances)], ", "
)
else
tol_str = join(
[
"$si = $ai < $bi" for
(si, ai, bi) in zip(sc.names, sc.values, sc.tolerances)
],
", ",
)
end
return "After $(sc.at_iteration) iterations the algorithm reached an approximate critical point with tolerances $tol_str.\n"
end
return ""
end

function status_summary(sc::StopWhenLagrangeMultiplierLess)
s = length(sc.reason) > 0 ? "reached" : "not reached"
msg = ""
(sc.mode === :both) && (msg = " ε ≤ $(sc.tolerance[1]) and |g| ≤ $(sc.tolerance[2])")
(sc.mode === :estimate) && (msg = " -ξ ≤ $(sc.tolerance[1])")
return "Stopping parameter: $(msg) :\t$(s)"
s = (sc.at_iteration >= 0) ? "reached" : "not reached"
msg = "Lagrange multipliers"
isnothing(sc.names) && (msg *= " with tolerances $(sc.tolerances)")
if !isnothing(sc.names)
msg *= join(["$si < $bi" for (si, bi) in zip(sc.names, sc.tolerances)], ", ")
end
return "$(msg) :\t$(s)"
end
function show(io::IO, sc::StopWhenLagrangeMultiplierLess)
n = isnothing(sc.names) ? "" : ", $(names)"
return print(
io,
"StopWhenLagrangeMultiplierLess($(sc.tolerance); mode=:$(sc.mode))\n $(status_summary(sc))",
"StopWhenLagrangeMultiplierLess($(sc.tolerances); mode=:$(sc.mode)$n)\n $(status_summary(sc))",
)
end

Expand Down
Loading
Loading