Skip to content

Commit 4a7498a

Browse files
authored
Readme and language review (#17)
I rewrote the readme to give a bit of a cleaner look, and then did a big (LLM-assisted this time) review of the language to get rid of some remaining typos and inconsistencies. I think I'd be happy to register after this :)
1 parent f87561f commit 4a7498a

13 files changed

Lines changed: 118 additions & 129 deletions

Changelog.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ All notable Changes to the Julia package `AlgorithmsInterface.jl` are documented
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [0.1.0] 2026/05/01
8+
## [0.1.0] 2026-05-01
99

10-
Initial release.
10+
Initial release.

Readme.md

Lines changed: 18 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
11
# 🧮 AlgorithmsInterface.jl
22

3-
`AlgorithmsInterface.jl` is a Julia package to provide a common interface to run iterative tasks.
4-
**Algorithm** here refers to an iterative sequence of commands, that are run until a certain stopping criterion is met.
3+
A small, composable interface for iterative algorithms in Julia.
54

65
[![docs][docs-dev-img]][docs-dev-url] [![CI][ci-img]][ci-url] [![runic][runic-img]][runic-url] [![codecov][codecov-img]][codecov-url] [![aqua][aqua-img]][aqua-url]
76

7+
8+
## Design
9+
10+
Iterative methods tend to share the same moving parts, which can lead to quite a bit of boilerplate and friction when trying to compose them.
11+
This package aims to provide abstractions such as the main loop, stopping criteria, and a logging system shared by these methods.
12+
It does not ship any concrete algorithms; the goal is to provide the tools to build on.
13+
It does however ship with a useful set of stopping-criterion and logging primitives out of the box.
14+
15+
The surface is intentionally small.
16+
The main design goal of the interface is to cleanly separate the implementation of the algorithm itself from the generic tools that surround it.
17+
Those generic tools, such as stopping, logging and debugging, are written once and then work across every algorithm that adopts the interface.
18+
19+
See the [documentation][docs-dev-url] for the design walk-through, the API reference, and a worked example.
20+
For background and discussion, see the [initial discussion](https://github.com/JuliaManifolds/AlgorithmsInterface.jl/discussions/1).
21+
22+
Note that this package is still in its design phase, and while SemVer is respected, (breaking) changes might still occur as the design takes shape.
23+
824
[docs-dev-img]: https://img.shields.io/badge/docs-dev-blue.svg
925
[docs-dev-url]: https://JuliaManifolds.github.io/AlgorithmsInterface.jl/dev/
1026

@@ -19,31 +35,3 @@
1935

2036
[aqua-img]: https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg
2137
[aqua-url]: https://github.com/JuliaTesting/Aqua.jl
22-
23-
# Statement of need
24-
25-
A first approach to algorithms is a simple for-loop for a maximum number of iterations.
26-
Using an interface instead allows to both specify different criteria to stop easily, even in their combination.
27-
Furthermore a generic interface allows to both “hook into” an algorithm easily as well as combining them.
28-
29-
A common interface for algorithms allows to reuse common code – especially stopping criteria, but especially also logging, debug, recording, and caching capabilities.
30-
Finally, a common interface also allows to easily combine existing algorithms, hence enhancing interoperability, for example using one algorithm as a sub routine of another one.
31-
32-
# Main features
33-
34-
See the [initial discussion](https://github.com/JuliaManifolds/AlgorithmsInterface.jl/discussions/1)
35-
as well as the [overview on existing things](https://github.com/JuliaManifolds/AlgorithmsInterface.jl/discussions/2)
36-
37-
## Further ideas
38-
39-
* generic stopping criteria `<:AbstractStoppingCriterion`
40-
* `StopAfterIteration(i)` for example
41-
* a factory that turns certain keywords like `maxiter=` into stopping criteria
42-
* still support the `stopping_criterion=` ideas from `Manopt.jl`
43-
* by default `stop()` from above would check such a stopping criterion
44-
* generic debug and record functionality – together with hooks even
45-
46-
## Possible extensions
47-
48-
* to `LineSearches.jl`
49-
*

docs/src/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# AlgorithmsInterface.jl
22

3-
Welcome to the Documentation of `AlgorithmsInterface.jl`.
3+
Welcome to the documentation of `AlgorithmsInterface.jl`.
44

55
```@meta
66
CurrentModule = AlgorithmsInterface

docs/src/interface.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ The interface in this package formalizes those roles with three abstract types:
2727
* [`Problem`](@ref): immutable, algorithm‑agnostic input data.
2828
* [`Algorithm`](@ref): immutable configuration and parameters deciding how to iterate.
2929
* [`State`](@ref): mutable data that evolves (current iterate, caches, counters, diagnostics).
30+
3031
It provides a framework for decomposing iterative methods into small, composable parts:
3132
concrete `Problem`/`Algorithm`/`State` types have to implement a minimal set of core functionality,
3233
and this package helps to stitch everything together and provide additional helper functionality such as stopping criteria and logging functionality.
@@ -86,7 +87,7 @@ function AlgorithmsInterface.initialize_state!(problem::SqrtProblem, algorithm::
8687
# reset the state for the algorithm
8788
state.iterate = rand()
8889
state.iteration = 0
89-
90+
9091
# reset the state for the stopping criterion
9192
state = AlgorithmsInterface.initialize_state!(
9293
problem, algorithm, algorithm.stopping_criterion, state.stopping_criterion_state

docs/src/logging.md

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ The logging system aims to achieve these goals by separating the logging logic i
2424
These parts can be roughly described as *events* and *actions*, where the logging system is responsible for mapping between them.
2525
Concretely, we have:
2626

27-
* **When do we log?**an [`with_algorithmlogger`](@ref) to control how to map events to actions.
28-
* **What happens when we log?** → a [`LoggingAction`](@ref) to determine what to do when an event happens.
27+
* **When do we log?**[`with_algorithmlogger`](@ref) controls how events are mapped to actions.
28+
* **What happens when we log?** → a [`LoggingAction`](@ref) determines what to do when an event happens.
2929

3030
This separation allows users to compose rich behaviors (printing, collecting statistics, plotting) without modifying algorithm code, and lets algorithm authors emit domain‑specific events.
3131

@@ -316,18 +316,18 @@ Inside the `solve!` function, logging events are emitted at key points:
316316
function solve!(problem::Problem, algorithm::Algorithm, state::State; kwargs...)
317317
initialize_state!(problem, algorithm, state; kwargs...)
318318
emit_message(problem, algorithm, state, :Start)
319-
319+
320320
while !is_finished!(problem, algorithm, state)
321321
emit_message(problem, algorithm, state, :PreStep)
322-
322+
323323
increment!(state)
324324
step!(problem, algorithm, state)
325-
325+
326326
emit_message(problem, algorithm, state, :PostStep)
327327
end
328-
328+
329329
emit_message(problem, algorithm, state, :Stop)
330-
330+
331331
return finalize_state!(problem, algorithm, state)
332332
end
333333
```
@@ -405,9 +405,9 @@ function AlgorithmsInterface.step!(problem::SqrtProblem, algorithm::HeronAlgorit
405405
# Suppose we check for numerical issues
406406
if !isfinite(state.iterate) || mod(state.iteration, 10) == 0
407407
emit_message(problem, algorithm, state, :Restart)
408-
state.iterate = rand() # Reset the iterate an try again
408+
state.iterate = rand() # Reset the iterate and try again
409409
end
410-
410+
411411
# Normal step
412412
S = problem.S
413413
x = state.iterate
@@ -438,7 +438,7 @@ nothing # hide
438438

439439
### Performance considerations
440440

441-
* Logging actions may be fast or slow, since the overhead is only incurred when actually using them.
441+
* Logging actions can be as fast or slow as needed; the overhead is only incurred when they are actually used.
442442
* Algorithms should be mindful of emitting events in hot loops. These events incur an overhead similar to accessing a `ScopedValue` (~10-100 ns), even when no logging action is registered.
443443
* For expensive operations (plotting, I/O), it is often better to collect data during iteration and process afterward.
444444
* Use `set_global_logging_state!(false)` for production benchmarks.
@@ -466,18 +466,18 @@ When designing custom logging contexts for your algorithms:
466466
Implementing logging involves three main components:
467467

468468
1. **LoggingAction**: Define what happens when a logging event occurs.
469-
- Use `CallbackAction` for quick inline functions.
470-
- Implement custom subtypes for reusable, stateful logging.
471-
- Implement `handle_message!(action, problem, algorithm, state; kwargs...)`.
469+
* Use `CallbackAction` for quick inline functions.
470+
* Implement custom subtypes for reusable, stateful logging.
471+
* Implement `handle_message!(action, problem, algorithm, state; kwargs...)`.
472472

473473
2. **AlgorithmLogger**: Map contexts (`:Start`, `:PostStep`, etc.) to actions.
474-
- Construct with `with_algorithmlogger(:Context => action, ...)`.
475-
- Use `ActionGroup` to compose multiple actions at one context.
474+
* Construct with `with_algorithmlogger(:Context => action, ...)`.
475+
* Use `ActionGroup` to compose multiple actions at one context.
476476

477477
3. **Custom contexts**: Emit domain-specific events from algorithms.
478-
- Call `emit_message(problem, algorithm, state, :YourContext)`.
479-
- Document custom contexts in your algorithm's documentation.
480-
- Use descriptive symbol names.
478+
* Call `emit_message(problem, algorithm, state, :YourContext)`.
479+
* Document custom contexts in your algorithm's documentation.
480+
* Use descriptive symbol names.
481481

482482
The logging system is designed for composability and zero-overhead when disabled, letting you instrument algorithms without compromising performance or code clarity.
483483

docs/src/stopping_criterion.md

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ The package ships several concrete [`StoppingCriterion`](@ref)s:
2727

2828
Each criterion has an associated [`StoppingCriterionState`](@ref) storing dynamic data (iteration when met, elapsed time, etc.).
2929

30-
Recall our [example implementation](@ref sec_heron) for Heron's method, where we we added a `stopping_criterion` to the `Algorithm`, as well as a `stopping_criterion_state` to the `State`.
30+
Recall our [example implementation](@ref sec_heron) for Heron's method, where we added a `stopping_criterion` to the `Algorithm`, as well as a `stopping_criterion_state` to the `State`.
3131

3232
```@example Heron
3333
using AlgorithmsInterface
@@ -134,7 +134,7 @@ criterion = StopAfterIteration(25) | StopAfter(Millisecond(50)) # logical OR
134134
heron_sqrt(2; stopping_criterion = criterion)
135135
```
136136

137-
Conversely, to demand both a minimum iteration quality condition **and** a cap, use `&` (logical AND).
137+
Conversely, to demand both a minimum iteration count **and** a time cap, use `&` (logical AND).
138138

139139
```@example Heron
140140
criterion = StopAfterIteration(25) & StopAfter(Millisecond(50)) # logical AND
@@ -194,30 +194,30 @@ Here, the mutating version alters the `stopping_criterion_state`, and should the
194194
function AlgorithmsInterface.is_finished!(
195195
::Problem, ::Algorithm, state::State, c::StopWhenStable, st::StopWhenStableState
196196
)
197-
k = state.iteration
198-
if k == 0
199-
st.previous_iterate = state.iterate
200-
st.at_iteration = -1
201-
return false
202-
end
203-
204-
st.delta = abs(state.iterate - st.previous_iterate)
205-
st.previous_iterate = state.iterate
206-
if st.delta < c.tol
207-
st.at_iteration = k
208-
return true
209-
end
210-
return false
197+
k = state.iteration
198+
if k == 0
199+
st.previous_iterate = state.iterate
200+
st.at_iteration = -1
201+
return false
202+
end
203+
204+
st.delta = abs(state.iterate - st.previous_iterate)
205+
st.previous_iterate = state.iterate
206+
if st.delta < c.tol
207+
st.at_iteration = k
208+
return true
209+
end
210+
return false
211211
end
212212
213213
function AlgorithmsInterface.is_finished(
214214
::Problem, ::Algorithm, state::State, c::StopWhenStable, st::StopWhenStableState
215215
)
216-
k = state.iteration
217-
k == 0 && return false
216+
k = state.iteration
217+
k == 0 && return false
218218
219-
Δ = abs(state.iterate - st.previous_iterate)
220-
return Δ < c.tol
219+
Δ = abs(state.iterate - st.previous_iterate)
220+
return Δ < c.tol
221221
end
222222
```
223223

@@ -244,7 +244,7 @@ criterion = StopWhenStable(1e-8)
244244
heron_sqrt(16.0; stopping_criterion = criterion)
245245
```
246246

247-
Note that our work payd off, as we can still compose this stopping criterion with other criteria as well:
247+
Note that our work paid off, as we can still compose this stopping criterion with other criteria as well:
248248

249249
```@example Heron
250250
criterion = StopWhenStable(1e-8) | StopAfterIteration(5)

src/interface/algorithm.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
An abstract type to represent an algorithm.
55
6-
A concrete algorithm contains all static parameters that characterise the algorithms.
6+
A concrete algorithm contains all static parameters that characterise the algorithm.
77
Together with a [`Problem`](@ref) an `Algorithm` subtype should be able to initialize
88
or reset a [`State`](@ref).
99

src/interface/interface.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ function initialize_state! end
1717
@doc "$(_doc_init_state)"
1818
initialize_state!(::Problem, ::Algorithm, ::State; kwargs...)
1919

20-
2120
"""
2221
output = finalize_state!(problem::Problem, algorithm::Algorithm, state::State)
2322

src/interface/problem.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ For a [gradient descent](https://en.wikipedia.org/wiki/Gradient_descent) algorit
1111
* a `cost` function ``f: C → ℝ``
1212
* a gradient function ``$(raw"\operatorname{grad}")f``
1313
14-
The problem then could that these are given in four different forms
14+
The problem could specify these in four different forms:
1515
1616
* a function `c = cost(x)` and a gradient `d = gradient(x)`
1717
* a function `c = cost(x)` and an in-place gradient `gradient!(d,x)`

src/interface/state.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ and keeps all information needed from one step to the next.
1111
In order to interact with the stopping criteria, the state should contain the following properties,
1212
and provide corresponding `getproperty` and `setproperty!` methods.
1313
14-
* `iteration` – the current iteration step ``k`` that is is currently performed or was last performed
14+
* `iteration` – the current iteration step ``k`` that is currently being performed or was last performed.
1515
* `stopping_criterion_state` – a [`StoppingCriterionState`](@ref) that indicates whether an [`Algorithm`](@ref)
1616
will stop after this iteration or has stopped.
17-
* `iterate` the current iterate ``x^{(k)}``.
17+
* `iterate` the current iterate ``x^{(k)}``.
1818
1919
## Methods
2020
@@ -27,7 +27,7 @@ abstract type State end
2727
"""
2828
increment!(state::State)
2929
30-
Increment the current iteration a [`State`](@ref) either is currently performing or was last performed
30+
Increment the current iteration that a [`State`](@ref) is currently performing or was last performing.
3131
3232
The default assumes that the current iteration is stored in `state.iteration`.
3333
"""

0 commit comments

Comments
 (0)