Skip to content

Commit 8d7e228

Browse files
committed
update stopping interface
1 parent c0ad8e3 commit 8d7e228

4 files changed

Lines changed: 237 additions & 175 deletions

File tree

src/AlgorithmsInterface.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ using ScopedValues
1515
include("interface/algorithm.jl")
1616
include("interface/problem.jl")
1717
include("interface/state.jl")
18+
include("interface/stopping.jl")
1819
include("interface/interface.jl")
1920

2021
include("stopping_criterion.jl")

src/interface/interface.jl

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,34 @@
11
_doc_init_state = """
2-
state = initialize_state(problem::Problem, algorithm::Algorithm; kwargs...)
3-
state = initialize_state!(problem::Problem, algorithm::Algorithm, state::State; kwargs...)
2+
state = initialize_state(
3+
problem::Problem, algorithm::Algorithm,
4+
[stopping_criterion_state::StoppingCriterionState];
5+
kwargs...
6+
)
7+
state = initialize_state!(
8+
problem::Problem, algorithm::Algorithm, state::State;
9+
kwargs...
10+
)
411
512
Initialize a [`State`](@ref) based on a [`Problem`](@ref) and an [`Algorithm`](@ref).
613
The `kwargs...` should allow to initialize for example the initial point.
714
This can be done in-place for `state`, then only values that did change have to be provided.
15+
16+
Note that since the returned state should also hold `state.stopping_criterion_state`,
17+
which will be used to keep the internal state of the stopping criterion, the out-of-place
18+
version receives this as one of its arguments. By default, that will be initialized separately
19+
through a call to [`initialize_stopping_state`](@ref) and provided as an argument.
20+
21+
On the other hand, the in-place version is not responsible for initializing the `stopping_criterion_state`,
22+
as that will be handled separately by [`initialize_stopping_state!`](@ref).
23+
24+
Users that which to handle the stopping criterion initialization in `initialize_state` manually
25+
should overload the 2-argument version, while by default the 3-argument version should be implemented.
826
"""
927

10-
function initialize_state end
28+
function initialize_state(problem::Problem, algorithm::Algorithm; kwargs...)
29+
stopping_criterion_state = initialize_stopping_state(problem, algorithm; kwargs...)
30+
return initialize_state(problem, algorithm, stopping_criterion_state; kwargs...)
31+
end
1132

1233
@doc "$(_doc_init_state)"
1334
initialize_state(::Problem, ::Algorithm; kwargs...)
@@ -23,9 +44,10 @@ initialize_state!(::Problem, ::Algorithm, ::State; kwargs...)
2344
solve(problem::Problem, algorithm::Algorithm; kwargs...)
2445
2546
Solve the [`Problem`](@ref) using an [`Algorithm`](@ref).
26-
The keyword arguments `kwargs...` have to provide enough details such that
27-
the corresponding state initialisation [`initialize_state`](@ref)`(problem, algorithm)`
28-
returns a state.
47+
48+
The keyword arguments `kwargs...` have to provide enough details such that the corresponding
49+
state and stopping state initialisation [`initialize_state`](@ref)` and [`initialize_stopping_state`](@ref)
50+
can be used to return valid states and stopping states.
2951
3052
By default this method continues to call [`solve!`](@ref).
3153
"""
@@ -40,16 +62,18 @@ end
4062
Solve the [`Problem`](@ref) using an [`Algorithm`](@ref), starting from a given [`State`](@ref).
4163
The state is modified in-place.
4264
43-
All keyword arguments are passed to the [`initialize_state!`](@ref)`(problem, algorithm, state)` function.
65+
All keyword arguments are passed to the [`initialize_state!`](@ref) and
66+
[`initialize_stopping_state!`](@ref) functions.
4467
"""
4568
function solve!(problem::Problem, algorithm::Algorithm, state::State; kwargs...)
4669
# obtain logger once to minimize overhead from accessing ScopedValue
4770
# additionally handle logging initialization to enable stateful LoggingAction
4871
logger = algorithm_logger()
49-
# initialize_logger(logger, problem, algorithm, state)
5072

5173
# initialize the state and emit message
74+
initialize_stopping_state!(problem, algorithm, state; kwargs...)
5275
initialize_state!(problem, algorithm, state; kwargs...)
76+
5377
emit_message(logger, problem, algorithm, state, :Start)
5478

5579
# main body of the algorithm
@@ -72,6 +96,7 @@ function solve!(problem::Problem, algorithm::Algorithm, state::State; kwargs...)
7296
end
7397

7498
function step! end
99+
75100
@doc """
76101
step!(problem::Problem, algorithm::Algorithm, state::State)
77102

src/interface/stopping.jl

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
@doc """
2+
StoppingCriterion
3+
4+
An abstract type to represent a stopping criterion of an [`Algorithm`](@ref).
5+
6+
A concrete [`StoppingCriterion`](@ref) should also implement a
7+
[`initialize_state(problem::Problem, algorithm::Algorithm, stopping_criterion::StoppingCriterion; kwargs...)`](@ref) function to create its accompanying
8+
[`StoppingCriterionState`](@ref).
9+
as well as the corresponding mutating variant to reset such a [`StoppingCriterionState`](@ref).
10+
11+
It should usually implement
12+
13+
* [`indicates_convergence`](@ref)`(stopping_criterion)`
14+
* [`indicates_convergence`](@ref)`(stopping_criterion, stopping_criterion_state)`
15+
* [`is_finished!`](@ref)`(problem, algorithm, state, stopping_criterion, stopping_criterion_state)`
16+
* [`is_finished`](@ref)`(problem, algorithm, state, stopping_criterion, stopping_criterion_state)`
17+
"""
18+
abstract type StoppingCriterion end
19+
20+
@doc """
21+
StoppingCriterionState
22+
23+
An abstract type to represent a stopping criterion state within a [`State`](@ref).
24+
It represents the concrete state a [`StoppingCriterion`](@ref) is in.
25+
26+
It should usually implement
27+
28+
* [`get_reason`](@ref)`(stopping_criterion, stopping_criterion_state)`
29+
* [`indicates_convergence`](@ref)`(stopping_criterion, stopping_criterion_state)`
30+
* [`is_finished!`](@ref)`(problem, algorithm, state, stopping_criterion, stopping_criterion_state)`
31+
* [`is_finished`](@ref)`(problem, algorithm, state, stopping_criterion, stopping_criterion_state)`
32+
"""
33+
abstract type StoppingCriterionState end
34+
35+
# Initialization
36+
# --------------
37+
_doc_init_stopping_state = """
38+
stopping_criterion_state = initialize_stopping_state(
39+
problem::Problem, algorithm::Algorithm
40+
stopping_criterion::StoppingCriterion = algorithm.stopping_criterion;
41+
kwargs...
42+
)
43+
stopping_criterion_state = initialize_stopping_state!(
44+
problem::Problem, algorithm::Algorithm, state::State,
45+
stopping_criterion::StoppingCriterion = algorithm.stopping_criterion,
46+
stopping_criterion_state::StoppingCriterionState = state.stopping_criterion_state;
47+
kwargs...
48+
)
49+
50+
Initialize a [`StoppingCriterionState`](@ref) based on a [`Problem`](@ref), [`Algorithm`](@ref),
51+
[`State`](@ref) triplet for a given [`StoppingCriterion`](@ref).
52+
By default, the `stopping_criterion` is retrieved from the `Algorithm` via `algorithm.stopping_criterion`.
53+
54+
The first signature is used for setting up a completely new stopping criterion state, while the second
55+
simply resets a given state in-place.
56+
"""
57+
58+
initialize_stopping_state(problem::Problem, algorithm::Algorithm; kwargs...) =
59+
initialize_stopping_state(problem, algorithm, algorithm.stopping_criterion; kwargs...)
60+
61+
@doc "$(_doc_init_stopping_state)"
62+
initialize_stopping_state(::Problem, ::Algorithm, ::StoppingCriterion; kwargs...)
63+
64+
function initialize_stopping_state!(problem::Problem, algorithm::Algorithm, state::State; kwargs...)
65+
return initialize_stopping_state!(
66+
problem, algorithm, state, algorithm.stopping_criterion, state.stopping_criterion_state; kwargs...
67+
)
68+
end
69+
70+
@doc "$(_doc_init_stopping_state)"
71+
initialize_stopping_state!(::Problem, ::Algorithm, ::State, ::StoppingCriterion, ::StoppingCriterionState; kwargs...)
72+
73+
74+
# Convergence characterization
75+
# ----------------------------
76+
function get_reason end
77+
78+
@doc """
79+
get_reason(stopping_criterion::StoppingCriterion, stopping_criterion_state::StoppingCriterionState)
80+
81+
Provide a reason in human readable text as to why a [`StoppingCriterion`](@ref) with [`StoppingCriterionState`](@ref) indicated to stop.
82+
If it does not indicate to stop, this should return `nothing`.
83+
84+
Providing the iteration at which this indicated to stop in the reason would be preferable.
85+
"""
86+
get_reason(::StoppingCriterion, ::StoppingCriterionState)
87+
88+
function indicates_convergence end
89+
@doc """
90+
indicates_convergence(stopping_criterion::StoppingCriterion)
91+
92+
Return whether or not a [`StoppingCriterion`](@ref) indicates convergence.
93+
"""
94+
indicates_convergence(stopping_criterion::StoppingCriterion)
95+
96+
@doc """
97+
indicates_convergence(stopping_criterion::StoppingCriterion, ::StoppingCriterionState)
98+
99+
Return whether or not a [`StoppingCriterion`](@ref) indicates convergence when it is in [`StoppingCriterionState`](@ref).
100+
101+
By default this checks whether the [`StoppingCriterion`](@ref) has actually stopped.
102+
If so it returns whether `stopping_criterion` itself indicates convergence, otherwise it returns `false`,
103+
since the algorithm has then not yet stopped.
104+
"""
105+
function indicates_convergence(
106+
stopping_criterion::StoppingCriterion,
107+
stopping_criterion_state::StoppingCriterionState,
108+
)
109+
return isnothing(get_reason(stopping_criterion, stopping_criterion_state)) &&
110+
indicates_convergence(stopping_criterion)
111+
end
112+
113+
# Convergence indication
114+
# ----------------------
115+
_doc_is_finished = """
116+
is_finished(problem::Problem, algorithm::Algorithm, state::State)
117+
is_finished(problem::Problem, algorithm::Algorithm, state::State, stopping_criterion::StoppingCriterion, stopping_criterion_state::StoppingCriterionState)
118+
is_finished!(problem::Problem, algorithm::Algorithm, state::State)
119+
is_finished!(problem::Problem, algorithm::Algorithm, state::State, stopping_criterion::StoppingCriterion, stopping_criterion_state::StoppingCriterionState)
120+
121+
Indicate whether an [`Algorithm`](@ref) solving [`Problem`](@ref) is finished having reached
122+
a certain [`State`](@ref). The variant with three arguments by default extracts the
123+
[`StoppingCriterion`](@ref) and its [`StoppingCriterionState`](@ref) and their actual
124+
checks are performed in the implementation with five arguments.
125+
126+
The mutating variant does alter the `stopping_criterion_state` and and should only be called
127+
once per iteration, the other one merely inspects the current status without mutation.
128+
"""
129+
130+
@doc "$(_doc_is_finished)"
131+
function is_finished(problem::Problem, algorithm::Algorithm, state::State)
132+
return is_finished(
133+
problem, algorithm, state,
134+
algorithm.stopping_criterion,
135+
state.stopping_criterion_state,
136+
)
137+
end
138+
139+
@doc "$(_doc_is_finished)"
140+
is_finished(::Problem, ::Algorithm, ::State, ::StoppingCriterion, ::StoppingCriterionState)
141+
142+
@doc "$(_doc_is_finished)"
143+
function is_finished!(problem::Problem, algorithm::Algorithm, state::State)
144+
return is_finished!(
145+
problem, algorithm, state,
146+
algorithm.stopping_criterion,
147+
state.stopping_criterion_state,
148+
)
149+
end
150+
151+
@doc "$(_doc_is_finished)"
152+
is_finished!(::Problem, ::Algorithm, ::State, ::StoppingCriterion, ::StoppingCriterionState)
153+
154+
@doc """
155+
summary(io::IO, stopping_criterion::StoppingCriterion, stopping_criterion_state::StoppingCriterionState)
156+
157+
Provide a summary of the status of a stopping criterion – its parameters and whether
158+
it currently indicates to stop. It should not be longer than one line
159+
160+
# Example
161+
162+
For the [`StopAfterIteration`](@ref) criterion, the summary looks like
163+
164+
```
165+
Max Iterations (15): not reached
166+
```
167+
"""
168+
Base.summary(io::IO, ::StoppingCriterion, ::StoppingCriterionState)

0 commit comments

Comments
 (0)