Skip to content

Commit c0ad8e3

Browse files
lkdvoskellertuer
andauthored
Logging interface (#7)
* add logging file * add logging statements * some progress * some actual progress * small fixes * fix Printf compat * minor optimizations * one more optimization attempt * various improvements and rework interface.md * small docstring typos * stopping criterion docs and small fixes * WIP * collapse docstrings by default * write logging docs * clean up and finish logging docs * fix test * add iteration test * add error test * Add test for `IfAction` * Add GroupAction test * add global toggle test * `GroupAction` -> `ActionGroup` * Update interface.md Co-authored-by: Ronny Bergmann <git@ronnybergmann.net> --------- Co-authored-by: Ronny Bergmann <git@ronnybergmann.net>
1 parent 4a60118 commit c0ad8e3

13 files changed

Lines changed: 1342 additions & 44 deletions

Project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ version = "0.1.0"
55

66
[deps]
77
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
8+
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
9+
ScopedValues = "7e506255-f358-4e82-b7e4-beb19740aa63"
810

911
[compat]
1012
Aqua = "0.8"
1113
Dates = "1.10"
14+
Printf = "1.10"
1215
SafeTestsets = "0.1"
16+
ScopedValues = "1.5.0"
1317
Test = "1.10"
1418
julia = "1.10"
1519

docs/make.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,11 @@ makedocs(;
4646
"Home" => "index.md",
4747
"Interface" => "interface.md",
4848
"Stopping criteria" => "stopping_criterion.md",
49+
"Logging" => "logging.md",
4950
"Notation" => "notation.md",
5051
"References" => "references.md",
5152
],
53+
expandfirst = ["interface.md", "stopping_criterion.md"],
5254
plugins = [bib, links],
5355
)
5456
deploydocs(; repo = "github.com/JuliaManifolds/AlgorithmsInterface.jl", push_preview = true)

docs/src/interface.md

Lines changed: 143 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,145 @@
1-
# The algorithm interface
1+
```@meta
2+
CollapsedDocStrings = true
3+
```
4+
5+
# [The algorithm interface](@id sec_interface)
6+
7+
This section starts a single, cohesive story that will weave through all documentation pages.
8+
We will incrementally build an iterative algorithm, enrich it with stopping criteria, and
9+
finally refine how it records (logs) its progress. Instead of presenting the API in the
10+
abstract, we anchor every concept in one concrete, tiny example you can copy & adapt.
11+
12+
Why an “interface” for algorithms? Iterative numerical methods nearly always share the
13+
same moving pieces:
14+
15+
* immutable input (the mathematical problem you are solving),
16+
* immutable configuration (parameters and knobs of the chosen algorithm), and
17+
* mutable working data (current iterate, caches, diagnostics) that evolves as you step.
218

3-
## General design ideas
19+
Bundling these together loosely without forcing one giant monolithic type makes it easier to:
420

5-
The interface this package provides is based on three ingredients of running an algorithm
6-
consists of:
21+
* reason about what is allowed to change and what must remain fixed,
22+
* write generic tooling (e.g. stopping logic, logging, benchmarking) that applies across many algorithms,
23+
* test algorithms in isolation by constructing minimal `Problem`/`Algorithm` pairs, and
24+
* extend behavior (add new stopping criteria, new logging events) without rewriting core loops.
25+
26+
The interface in this package formalizes those roles with three abstract types:
27+
* [`Problem`](@ref): immutable, algorithm‑agnostic input data.
28+
* [`Algorithm`](@ref): immutable configuration and parameters deciding how to iterate.
29+
* [`State`](@ref): mutable data that evolves (current iterate, caches, counters, diagnostics).
30+
It provides a framework for decomposing iterative methods into small, composable parts:
31+
concrete `Problem`/`Algorithm`/`State` types have to implement a minimal set of core functionality,
32+
and this package helps to stitch everything together and provide additional helper functionality such as stopping criteria and logging functionality.
33+
34+
## [Concrete example: Heron's method](@id sec_heron)
35+
36+
To make everything tangible, we will work through a concrete example to illustrate the library's goals and concepts.
37+
Our running example is Heron's / Babylonian method for estimating $\sqrt{S}$.
38+
(see also the concise background on Wikipedia: [Babylonian method (Heron's method)](https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)):
39+
Starting from an initial guess $x_0$, we may converge to the solution by iterating:
40+
41+
```math
42+
x_{k+1} = \frac{1}{2}\left(x_k + \frac{S}{x_k}\right)
43+
```
744

8-
* a [`Problem`](@ref) that is to be solved and contains all information that is algorithm independent.
9-
This is _static information_ in the sense that it does not change during the runtime of the algorithm.
10-
* an [`Algorithm`](@ref) that includes all of the _settings_ and _parameters_ that an algorithm.
11-
this is also information that is _static_.
12-
* a [`State`](@ref) that contains all remaining data, especially data that might vary during the iteration,
13-
temporary caches, for example the current iteration the algorithm run is in and the current iterate, respectively.
45+
We therefore suggest the following concrete implementations of the abstract types provided by this package:
46+
They are illustrative; various performance and generality questions will be left unaddressed to keep this example simple.
1447

15-
The combination of the static information should be enough to initialize the varying data.
48+
### Algorithm types
1649

17-
This general scheme is a guiding principle of the package, splitting information into _static_
18-
or _configuration_ types or data that allows to [`initialize_state`](@ref) a corresponding _variable_ data type.
50+
```@example Heron
51+
using AlgorithmsInterface
1952
20-
The order of arguments is given by two ideas
53+
struct SqrtProblem <: Problem
54+
S::Float64 # number whose square root we seek
55+
end
2156
22-
1. for non-mutating functions the order should be from the most fixed data to the most variable one.
23-
For example the three types just mentioned would be ordered like `f(problem, algorithm, state)`
24-
2. For mutating functions the variable that is mutated comes first, for the remainder the guiding principle from 1 continues.
25-
The main case here is `f!(state, problem, algorithm)`.
57+
struct HeronAlgorithm <: Algorithm
58+
stopping_criterion # will be plugged in later (any StoppingCriterion)
59+
end
60+
61+
mutable struct HeronState <: State
62+
iterate::Float64 # current iterate
63+
iteration::Int # current iteration count
64+
stopping_criterion_state # will be plugged in later (any StoppingCriterionState)
65+
end
66+
```
67+
68+
### Initialization
69+
70+
In order to start implementing the core parts of our algorithm, we start at the very beginning.
71+
There are two main entry points provided by the interface:
72+
73+
- [`initialize_state`](@ref) constructs an entirely new state for the algorithm
74+
- [`initialize_state!`](@ref) (in-place) reset of an existing state.
75+
76+
An example implementation might look like:
77+
78+
```@example Heron
79+
function AlgorithmsInterface.initialize_state(problem::SqrtProblem, algorithm::HeronAlgorithm; kwargs...)
80+
x0 = rand() # random initial guess
81+
stopping_criterion_state = initialize_state(problem, algorithm, algorithm.stopping_criterion)
82+
return HeronState(x0, 0, stopping_criterion_state)
83+
end
84+
85+
function AlgorithmsInterface.initialize_state!(problem::SqrtProblem, algorithm::HeronAlgorithm, state::HeronState; kwargs...)
86+
# reset the state for the algorithm
87+
state.iterate = rand()
88+
state.iteration = 0
89+
90+
# reset the state for the stopping criterion
91+
state = AlgorithmsInterface.initialize_state!(
92+
problem, algorithm, algorithm.stopping_criterion, state.stopping_criterion_state
93+
)
94+
return state
95+
end
96+
```
97+
98+
### Iteration steps
99+
100+
Algorithms define a mutable step via [`step!`](@ref). For Heron's method:
101+
102+
```@example Heron
103+
function AlgorithmsInterface.step!(problem::SqrtProblem, algorithm::HeronAlgorithm, state::HeronState)
104+
S = problem.S
105+
x = state.iterate
106+
state.iterate = 0.5 * (x + S / x)
107+
return state
108+
end
109+
```
110+
111+
Note that we are only focussing on the actual algorithm, and *not* incrementing the iteration counter.
112+
These kinds of bookkeeping should be handled by the [`AlgorithmsInterface.increment!`](@ref) function, which will by default already increment the iteration counter.
113+
The following generic functionality is therefore enough for our purposes, and does *not* need to be defined.
114+
Nevertheless, if additional bookkeeping would be desired, this can be achieved by overloading that function:
115+
116+
```julia
117+
function AlgorithmsInterface.increment!(state::State)
118+
state.iteration += 1
119+
return state
120+
end
121+
```
122+
123+
### Running the algorithm
124+
125+
With these definitions in place you can already run (assuming you also choose a stopping criterion – added in the next section):
126+
127+
```@example Heron
128+
function heron_sqrt(x; maxiter = 10)
129+
prob = SqrtProblem(x)
130+
alg = HeronAlgorithm(StopAfterIteration(maxiter))
131+
state = solve(prob, alg) # allocates & runs
132+
return state.iterate
133+
end
134+
135+
println("Approximate sqrt: ", heron_sqrt(16.0))
136+
```
137+
138+
We will refine this example with better halting logic and logging shortly.
139+
140+
## Reference: Core interface types & functions
141+
142+
Below are the automatic API docs for the core interface pieces. Read them after grasping the example above – the intent should now be clearer.
26143

27144
```@autodocs
28145
Modules = [AlgorithmsInterface]
@@ -31,7 +148,7 @@ Order = [:type, :function]
31148
Private = true
32149
```
33150

34-
## Algorithm
151+
### Algorithm
35152

36153
```@autodocs
37154
Modules = [AlgorithmsInterface]
@@ -40,7 +157,7 @@ Order = [:type, :function]
40157
Private = true
41158
```
42159

43-
## Problem
160+
### Problem
44161

45162
```@autodocs
46163
Modules = [AlgorithmsInterface]
@@ -49,11 +166,15 @@ Order = [:type, :function]
49166
Private = true
50167
```
51168

52-
## State
169+
### State
53170

54171
```@autodocs
55172
Modules = [AlgorithmsInterface]
56173
Pages = ["interface/state.jl"]
57174
Order = [:type, :function]
58175
Private = true
59-
```
176+
```
177+
178+
### Next: Stopping criteria
179+
180+
Proceed to the stopping criteria section to add robust halting logic (iteration caps, time limits, tolerance on successive iterates, and combinations) to this square‑root example.

0 commit comments

Comments
 (0)