Skip to content

Commit

Permalink
Refactor get_reason (#395)
Browse files Browse the repository at this point in the history
* first part of rework
* short interims commit.
* remove all reason fields.
* 📈
* fix a stopping criterions summary and further test coverage.
* Bump version.

---------

Co-authored-by: Mateusz Baran <mateuszbaran89@gmail.com>
  • Loading branch information
kellertuer and mateuszbaran authored Jun 13, 2024
1 parent 4ec57f5 commit befce29
Show file tree
Hide file tree
Showing 19 changed files with 619 additions and 321 deletions.
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

2 comments on commit befce29

@kellertuer
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register

Release Notes:

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.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/108903

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.4.65 -m "<description of version>" befce2951e1e72099966a60eeb49429f712eb66c
git push origin v0.4.65

Please sign in to comment.