Skip to content

Commit 6cd444e

Browse files
committed
finish cleaning
1 parent 66f27f7 commit 6cd444e

File tree

7 files changed

+347
-99
lines changed

7 files changed

+347
-99
lines changed

docs/src/index.md

Lines changed: 262 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ CurrentModule = CTBase
66

77
The `CTBase.jl` package is part of the [control-toolbox ecosystem](https://github.com/control-toolbox).
88

9-
The root package is [OptimalControl.jl](https://github.com/control-toolbox/OptimalControl.jl) which aims to provide tools to model and solve optimal control problems with ordinary differential equations by direct and indirect methods, both on CPU and GPU.
9+
!!! note
10+
11+
The root package is [OptimalControl.jl](https://github.com/control-toolbox/OptimalControl.jl) which aims to provide tools to model and solve optimal control problems with ordinary differential equations by direct and indirect methods, both on CPU and GPU.
1012

1113
!!! warning
1214

@@ -27,15 +29,269 @@ The root package is [OptimalControl.jl](https://github.com/control-toolbox/Optim
2729
julia> CTBase.private_fun(x)
2830
```
2931

30-
If the method is re-exported by another package, there is no need to
31-
prefix it with the original module name:
32-
33-
```julia-repl
34-
julia> module OptimalControl
32+
If the method is re-exported by another package,
33+
34+
```julia
35+
module OptimalControl
3536
import CTBase: private_fun
3637
export private_fun
3738
end
39+
```
40+
41+
then there is no need to prefix it with the original module name:
42+
43+
```julia-repl
3844
julia> using OptimalControl
3945
julia> x = 1
4046
julia> private_fun(x)
4147
```
48+
49+
## Descriptions: encoding algorithms
50+
51+
One of the central ideas in CTBase is the notion of a **description**.
52+
A description is simply a tuple of `Symbol`s that encodes an algorithm or
53+
configuration in a declarative way.
54+
55+
Formally, CTBase defines:
56+
57+
```julia
58+
const DescVarArg = Vararg{Symbol}
59+
const Description = Tuple{DescVarArg}
60+
```
61+
62+
For example, the tuple
63+
64+
```julia-repl
65+
julia> using CTBase
66+
67+
julia> d = (:descent, :bfgs, :bisection)
68+
(:descent, :bfgs, :bisection)
69+
70+
julia> typeof(d) <: CTBase.Description
71+
true
72+
```
73+
74+
can be read as “a descent algorithm, with BFGS directions and a bisection
75+
line search”. Higher-level packages in the control-toolbox ecosystem use
76+
descriptions to catalogue algorithms in a uniform way.
77+
78+
### Building a library of descriptions
79+
80+
CTBase provides a few small functions to manage collections of descriptions:
81+
82+
- `CTBase.add(x, y)` adds the description `y` to the tuple of descriptions `x`,
83+
rejecting duplicates with an `IncorrectArgument` exception.
84+
- `CTBase.complete(list; descriptions=D)` picks a complete description from a
85+
set `D` based on a partial list of symbols.
86+
- `CTBase.remove(x, y)` returns the set difference of two descriptions.
87+
88+
Here is a complete example of a small “algorithm library”:
89+
90+
```julia-repl
91+
julia> algorithms = ()
92+
()
93+
94+
julia> algorithms = CTBase.add(algorithms, (:descent, :bfgs, :bisection))
95+
((:descent, :bfgs, :bisection),)
96+
97+
julia> algorithms = CTBase.add(algorithms, (:descent, :gradient, :fixedstep))
98+
((:descent, :bfgs, :bisection), (:descent, :gradient, :fixedstep))
99+
100+
julia> display(algorithms)
101+
(:descent, :bfgs, :bisection)
102+
(:descent, :gradient, :fixedstep)
103+
```
104+
105+
Given this library, we can **complete** a partial description:
106+
107+
```julia-repl
108+
julia> CTBase.complete((:descent,); descriptions=algorithms)
109+
(:descent, :bfgs, :bisection)
110+
111+
julia> CTBase.complete((:gradient, :fixedstep); descriptions=algorithms)
112+
(:descent, :gradient, :fixedstep)
113+
```
114+
115+
Internally, `CTBase.complete` scans the `descriptions` tuple from top to
116+
bottom. For each candidate description it computes:
117+
118+
- how many symbols it shares with the partial list, and
119+
- whether the partial list is a subset of the full description.
120+
121+
If no description contains all the symbols from the partial list,
122+
`AmbiguousDescription` is thrown. Otherwise, among the descriptions that do
123+
contain the partial list, CTBase selects the one with the largest
124+
intersection; if several have the same score, the **first** one in the
125+
`descriptions` tuple wins. In other words, the order of `descriptions`
126+
encodes a priority from top to bottom.
127+
128+
With this mechanism in place, we can then analyse the *remainder* of a
129+
description by removing a prefix:
130+
131+
```julia-repl
132+
julia> full = CTBase.complete((:descent,); descriptions=algorithms)
133+
(:descent, :bfgs, :bisection)
134+
135+
julia> CTBase.remove(full, (:descent, :bfgs))
136+
(:bisection,)
137+
```
138+
139+
This “description language” lets higher-level packages refer to algorithms in a
140+
structured, composable way, while CTBase takes care of the low-level
141+
operations (adding, completing, and comparing descriptions).
142+
143+
## Error handling and CTBase exceptions
144+
145+
CTBase defines a small hierarchy of domain-specific exceptions to make error
146+
handling explicit and consistent across the control-toolbox ecosystem.
147+
148+
All custom exceptions inherit from `CTBase.CTException`:
149+
150+
```julia
151+
abstract type CTBase.CTException <: Exception end
152+
```
153+
154+
You should generally catch exceptions like this:
155+
156+
```julia
157+
try
158+
# call into CTBase or a package built on top of it
159+
catch e
160+
if e isa CTBase.CTException
161+
# handle CTBase domain errors in a uniform way
162+
@warn "CTBase error" exception=(e, catch_backtrace())
163+
else
164+
# non-CTBase error: rethrow so it is not hidden
165+
rethrow()
166+
end
167+
end
168+
```
169+
170+
This pattern avoids accidentally swallowing unrelated internal errors while still
171+
giving you a single place to handle all CTBase-specific problems.
172+
173+
### `AmbiguousDescription`
174+
175+
```julia
176+
CTBase.AmbiguousDescription <: CTBase.CTException
177+
```
178+
179+
Thrown when a description (a tuple of `Symbol`s) cannot be matched to any known
180+
valid description. This typically happens in `CTBase.complete` when the user
181+
provides an incomplete or inconsistent description.
182+
183+
```julia-repl
184+
julia> using CTBase
185+
186+
julia> D = ((:a, :b), (:a, :b, :c), (:b, :c))
187+
julia> CTBase.complete(:f; descriptions=D)
188+
ERROR: AmbiguousDescription: the description (:f,) is ambiguous / incorrect
189+
```
190+
191+
Use this exception when *the high-level choice of description itself* is wrong
192+
or ambiguous and there is no sensible default.
193+
194+
### `IncorrectArgument`
195+
196+
```julia
197+
CTBase.IncorrectArgument <: CTBase.CTException
198+
```
199+
200+
Thrown when an individual argument is invalid or violates a precondition.
201+
202+
Examples from CTBase:
203+
204+
- Adding a duplicate description:
205+
206+
```julia-repl
207+
julia> algorithms = CTBase.add((), (:a, :b))
208+
julia> CTBase.add(algorithms, (:a, :b))
209+
ERROR: IncorrectArgument: the description (:a, :b) is already in ((:a, :b),)
210+
```
211+
212+
- Using invalid indices for the Unicode helpers:
213+
214+
```julia-repl
215+
julia> CTBase.ctindice(-1)
216+
ERROR: IncorrectArgument: the subscript must be between 0 and 9
217+
```
218+
219+
Use this exception whenever *one input value* is outside the allowed domain
220+
(wrong range, duplicate, empty when it must not be, etc.).
221+
222+
### `NotImplemented`
223+
224+
```julia
225+
CTBase.NotImplemented <: CTBase.CTException
226+
```
227+
228+
Used to mark interface points that must be implemented by concrete subtypes.
229+
The typical pattern is to provide a method on an abstract type that throws
230+
`NotImplemented`, and then override it in each concrete implementation:
231+
232+
```julia
233+
abstract type MyAbstractAlgorithm end
234+
235+
function run!(algo::MyAbstractAlgorithm, state)
236+
throw(CTBase.NotImplemented("run! is not implemented for $(typeof(algo))"))
237+
end
238+
```
239+
240+
Concrete algorithms then provide their own `run!` method instead of raising
241+
this exception. This makes it easy to detect missing implementations during
242+
testing.
243+
244+
Use `NotImplemented` when defining *interfaces* and you want an explicit,
245+
typed error rather than a generic `error("TODO")`.
246+
247+
### `UnauthorizedCall`
248+
249+
```julia
250+
CTBase.UnauthorizedCall <: CTBase.CTException
251+
```
252+
253+
Signals that a function call is not allowed in the **current state** of the
254+
object or system. This is different from `IncorrectArgument`: here the
255+
arguments may be valid, but the call is forbidden because of *when* or *how*
256+
it is made.
257+
258+
A common pattern is a method that is meant to be called only once:
259+
260+
```julia
261+
function finalize!(s::SomeState)
262+
if s.is_finalized
263+
throw(CTBase.UnauthorizedCall("finalize! was already called for this state"))
264+
end
265+
# ... perform finalisation and mark state as finalised ...
266+
end
267+
```
268+
269+
Use `UnauthorizedCall` when the calling context is invalid (wrong phase of a
270+
computation, method already called, state already closed, missing
271+
permissions, illegal order of calls, etc.).
272+
273+
It is also used internally by `ExtensionError` when it is called without any
274+
weak dependencies:
275+
276+
```julia-repl
277+
julia> using CTBase
278+
279+
julia> CTBase.ExtensionError()
280+
ERROR: UnauthorizedCall: Please provide at least one weak dependence for the extension.
281+
```
282+
283+
### `ParsingError`
284+
285+
```julia
286+
CTBase.ParsingError <: CTBase.CTException
287+
```
288+
289+
Intended for errors detected during parsing of input structures or DSLs
290+
(domain-specific languages).
291+
292+
```julia-repl
293+
julia> using CTBase
294+
295+
julia> throw(CTBase.ParsingError("unexpected token 'end'"))
296+
ERROR: ParsingError: unexpected token 'end'
297+
```

ext/DocumenterReference.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -647,7 +647,7 @@ function _build_api_page(document::Documenter.Document, config::_Config)
647647
public_overview = """
648648
# $(public_title)
649649
650-
This page lists the **exported** symbols of `$(current_module)`.
650+
This page lists **exported** symbols of `$(current_module)`.
651651
652652
Load all public symbols into the current scope with:
653653
```julia
@@ -694,7 +694,7 @@ function _build_api_page(document::Documenter.Document, config::_Config)
694694
695695
# $(private_title)
696696
697-
This page lists the **non-exported** (internal) symbols of `$(current_module)`.
697+
This page lists **non-exported** (internal) symbols of `$(current_module)`.
698698
699699
Access these symbols with:
700700
```julia

src/exception.jl

Lines changed: 0 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -107,73 +107,6 @@ end
107107
"""
108108
$(TYPEDEF)
109109
110-
Exception thrown when a specified method name or function symbol does not exist.
111-
112-
# Fields
113-
114-
- `var::Symbol`: The method or function symbol that was expected but not found.
115-
116-
# Example
117-
118-
```julia-repl
119-
julia> using CTBase
120-
121-
julia> throw(CTBase.IncorrectMethod(:nonexistent_func))
122-
ERROR: IncorrectMethod: nonexistent_func is not an existing method
123-
```
124-
"""
125-
struct IncorrectMethod <: CTException
126-
var::Symbol
127-
end
128-
129-
"""
130-
$(TYPEDSIGNATURES)
131-
132-
Customizes the printed message of the exception.
133-
"""
134-
function Base.showerror(io::IO, e::IncorrectMethod)
135-
printstyled(io, "IncorrectMethod"; color=:red, bold=true)
136-
return print(io, ": ", e.var, " is not an existing method")
137-
end
138-
139-
# ------------------------------------------------------------------------
140-
"""
141-
$(TYPEDEF)
142-
143-
Exception thrown when the output produced by a function is incorrect or inconsistent
144-
with expected results.
145-
146-
# Fields
147-
148-
- `var::String`: A descriptive message explaining the incorrect output.
149-
150-
# Example
151-
152-
```julia-repl
153-
julia> using CTBase
154-
155-
julia> throw(CTBase.IncorrectOutput("the function returned NaN"))
156-
ERROR: IncorrectOutput: the function returned NaN
157-
```
158-
"""
159-
struct IncorrectOutput <: CTException
160-
var::String
161-
end
162-
163-
"""
164-
$(TYPEDSIGNATURES)
165-
166-
Customizes the printed message of the exception.
167-
"""
168-
function Base.showerror(io::IO, e::IncorrectOutput)
169-
printstyled(io, "IncorrectOutput"; color=:red, bold=true)
170-
return print(io, ": ", e.var)
171-
end
172-
173-
# ------------------------------------------------------------------------
174-
"""
175-
$(TYPEDEF)
176-
177110
Exception thrown when a method or function has not been implemented yet.
178111
179112
# Fields

test/runtests.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ using CTBase
22
using Test
33
using Aqua
44
using Documenter
5+
using Markdown
6+
using MarkdownAST
57

68
const DocumenterReference = Base.get_extension(CTBase, :DocumenterReference) # to test functions from CTFlowsODE not in CTFlows
79

@@ -28,6 +30,7 @@ end
2830
:exceptions,
2931
:utils,
3032
:documenter_reference,
33+
:integration,
3134
)
3235
@testset "$(name)" begin
3336
test_name = Symbol(:test_, name)

0 commit comments

Comments
 (0)