Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,42 +1,57 @@
name = "CTBase"
uuid = "54762871-cc72-4466-b8e8-f6c8b58076cd"
version = "0.23.0-beta"
version = "0.24.0-beta"
authors = ["Olivier Cots <olivier.cots@irit.fr>", "Jean-Baptiste Caillau <caillau@univ-cotedazur.fr>"]

[deps]
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"

[weakdeps]
Coverage = "a2441757-f6aa-5fb2-8edb-039e3f45d037"
DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
MarkdownAST = "d0879d2d-cac2-40c8-9cee-1863dc0c7391"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[extensions]
CTBaseDifferentiationInterface = ["DifferentiationInterface"]
CoveragePostprocessing = ["Coverage"]
DocumenterReference = ["Documenter", "Markdown", "MarkdownAST"]
TestRunner = ["Test"]

[extras]
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Coverage = "a2441757-f6aa-5fb2-8edb-039e3f45d037"
DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
MarkdownAST = "d0879d2d-cac2-40c8-9cee-1863dc0c7391"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = [
"Aqua",
"Coverage",
"DifferentiationInterface",
"Documenter",
"ForwardDiff",
"Markdown",
"MarkdownAST",
"Test"
]

[compat]
ADTypes = "1"
Aqua = "0.8"
Coverage = "1"
DifferentiationInterface = "0.7"
DocStringExtensions = "0.9"
Documenter = "1"
ForwardDiff = "0.10, 1"
Markdown = "1"
MarkdownAST = "0.1"
Test = "1"
Expand Down
7 changes: 7 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
[deps]
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"
CTBase = "54762871-cc72-4466-b8e8-f6c8b58076cd"
Coverage = "a2441757-f6aa-5fb2-8edb-039e3f45d037"
DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63"
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
DocumenterVitepress = "4710194d-e776-4893-9690-8d956a29c365"
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
LiveServer = "16fef848-5104-11e9-1b77-fb7a48bbb589"
MarkdownAST = "d0879d2d-cac2-40c8-9cee-1863dc0c7391"

[compat]
ADTypes = "1"
Coverage = "1"
DifferentiationInterface = "0.7"
Documenter = "1"
DocumenterVitepress = "0.3"
ForwardDiff = "0.10, 1"
LiveServer = "1"
MarkdownAST = "0.1"
julia = "1.10"
9 changes: 9 additions & 0 deletions docs/api_reference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ function generate_api_reference(src_dir::String)
joinpath("Data", "abstract_hamiltonian_vector_field.jl"),
joinpath("Data", "hamiltonian_vector_field.jl"),
)),
(mod=CTBase.Differentiation, title="Differentiation", filename="differentiation", files=src(
joinpath("Differentiation", "Differentiation.jl"), joinpath("Differentiation", "default.jl"),
joinpath("Differentiation", "abstract_ad_backend.jl"),
joinpath("Differentiation", "differentiation_interface.jl"),
joinpath("Differentiation", "building.jl"),
)),
(mod=CTBase.Descriptions, title="Descriptions", filename="descriptions", files=src(
joinpath("Descriptions", "Descriptions.jl"), joinpath("Descriptions", "types.jl"),
joinpath("Descriptions", "similarity.jl"), joinpath("Descriptions", "display.jl"),
Expand Down Expand Up @@ -106,6 +112,9 @@ function generate_api_reference(src_dir::String)
internals_modules = Any[cfg.mod => cfg.files for cfg in modules_config]

for (sym, files) in [
(:CTBaseDifferentiationInterface, ext(
joinpath("CTBaseDifferentiationInterface.jl"),
)),
(:DocumenterReference, ext(
joinpath("DocumenterReference", "DocumenterReference.jl"),
joinpath("DocumenterReference", "config_helpers.jl"),
Expand Down
1 change: 1 addition & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ with_api_reference(src_dir) do api_pages
"Implementing a Strategy" => joinpath("guide", "implementing-a-strategy.md"),
"Strategy Parameters" => joinpath("guide", "strategy-parameters.md"),
"Orchestration & Routing" => joinpath("guide", "orchestration-and-routing.md"),
"Differentiation" => joinpath("guide", "differentiation.md"),
],
"Developer Tools" => [
"Test Runner" => joinpath("guide", "test-runner.md"),
Expand Down
9 changes: 8 additions & 1 deletion docs/src/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ Three things to keep in mind:
CTBase.Exceptions.NotImplemented
```
2. **Submodule-first API.** The public API lives in named submodules
(`Core`, `Descriptions`, `Exceptions`, `Traits`, `Data`, `DevTools`, `Unicode`, …).
(`Core`, `Exceptions`, `Traits`, `Data`, `Descriptions`, `Options`, `Strategies`,
`Orchestration`, `Differentiation`, `Interpolation`, `DevTools`, `Unicode`, …).
You can bring a submodule's exports into scope explicitly:
```julia
using CTBase.Exceptions # brings IncorrectArgument, NotImplemented, … into scope
Expand All @@ -40,6 +41,8 @@ Three things to keep in mind:
3. **Extension-backed features.** `run_tests`, `postprocess_coverage`, and
`automatic_reference_documentation` require loading the matching weak dependency
(`Test`, `Coverage`, `Documenter` respectively) before they become active.
Likewise, the differentiation primitives of `CTBase.Differentiation` become active
only once `DifferentiationInterface` and an AD package (e.g. `ForwardDiff`) are loaded.

## 5-Minute Walkthrough

Expand Down Expand Up @@ -134,6 +137,10 @@ For more, see the **[Data guide](guide/data.md)**.
| Compile-time traits and dispatch | [Traits](guide/traits.md) |
| Trait-carrying vector fields and Hamiltonians | [Data](guide/data.md) |
| Descriptions catalogue and completion | [Descriptions](guide/descriptions.md) |
| Option schema, validation, and aliases | [Options System](guide/options-system.md) |
| Strategy contract and registration | [Implementing a Strategy](guide/implementing-a-strategy.md) |
| Routing options to strategies | [Orchestration & Routing](guide/orchestration-and-routing.md) |
| AD backends and differentiation primitives | [Differentiation](guide/differentiation.md) |
| Modular test runner setup | [Test Runner](guide/test-runner.md) |
| Coverage report generation | [Coverage](guide/coverage.md) |
| Auto-generated API reference | [API Documentation](guide/api-documentation.md) |
Expand Down
190 changes: 190 additions & 0 deletions docs/src/guide/differentiation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# Differentiation: AD backend strategies

```@meta
CurrentModule = CTBase
```

The [`CTBase.Differentiation`](@ref CTBase.Differentiation) submodule provides an
**automatic differentiation (AD) backend** abstraction built on the
[Strategies](implementing-a-strategy.md) contract. It exposes a small set of
differentiation primitives — gradients, derivatives, partial derivatives and
Jacobian–vector products — behind a single strategy type, so that consumers
(Hamiltonian systems, flows, differential geometry) never depend on a concrete
AD package directly.

!!! tip "Prerequisites"
Read the [Implementing a Strategy](@ref) and [Options System](@ref) guides
first: an AD backend is a strategy with a single `:ad_backend` option.

```@setup diff
using CTBase
using CTBase.Differentiation
using CTBase.Data
using CTBase.Strategies
# Loading DifferentiationInterface (+ an AD package) activates the extension
# that provides the actual differentiation methods.
using DifferentiationInterface
using ForwardDiff
import ADTypes
```

## Overview

The module is organised around a two-level strategy contract:

| Level | Symbol | Role |
|---|---|---|
| Family | [`Differentiation.AbstractADBackend`](@ref CTBase.Differentiation.AbstractADBackend) | abstract strategy; defines the AD contract |
| Concrete | [`Differentiation.DifferentiationInterface`](@ref CTBase.Differentiation.DifferentiationInterface) | wraps a [DifferentiationInterface.jl](https://github.com/JuliaDiff/DifferentiationInterface.jl) backend |

The concrete strategy stores a single option, `:ad_backend`, holding an
[ADTypes.jl](https://github.com/SciML/ADTypes.jl) backend (default
`AutoForwardDiff()`). `ADTypes` is a hard dependency of CTBase, so the default is
always available.

The contract has seven methods.
[`Differentiation.ad_backend`](@ref CTBase.Differentiation.ad_backend) — the accessor
returning the wrapped ADTypes backend — is resolved in core and always available; the
six differentiation primitives below live in an extension.

!!! note "The differentiation methods live in an extension"
The contract methods (`gradient`, `derivative`, `differentiate`,
`pushforward`, `hamiltonian_gradient`, `variable_gradient`) are implemented in
the `CTBaseDifferentiationInterface` **package extension**. They become
available only once `DifferentiationInterface` *and* a concrete AD package
(e.g. `ForwardDiff`) are loaded. Without the extension, every contract method
throws a [`CTBase.Exceptions.NotImplemented`](@ref) with a helpful message.

## Building a backend

Use [`Differentiation.build_ad_backend`](@ref CTBase.Differentiation.build_ad_backend)
to construct a backend with the default AD type:

```@example diff
backend = Differentiation.build_ad_backend()
```

Pass an explicit ADTypes backend through the `ad_backend` option (the aliases
`backend` and `ad` resolve to the same option):

```@example diff
Differentiation.build_ad_backend(; ad_backend = ADTypes.AutoForwardDiff())
```

The wrapped AD type is recovered with
[`Differentiation.ad_backend`](@ref CTBase.Differentiation.ad_backend):

```@example diff
Differentiation.ad_backend(backend)
```

Because it is a regular strategy, its metadata is introspectable at the type level:

```@example diff
Strategies.metadata(Differentiation.DifferentiationInterface)
```

## Differentiation primitives

### Gradient and derivative

[`Differentiation.gradient`](@ref CTBase.Differentiation.gradient) differentiates a
scalar function of a vector, and
[`Differentiation.derivative`](@ref CTBase.Differentiation.derivative) a scalar
function of a scalar:

```@example diff
f(x) = sum(x .^ 2) # ∇f = 2x
Differentiation.gradient(backend, f, [1.0, 2.0])
```

```@example diff
g(t) = t^3 # g'(t) = 3t²
Differentiation.derivative(backend, g, 2.0)
```

### Partial derivative at a slot

[`Differentiation.differentiate`](@ref CTBase.Differentiation.differentiate)
computes the partial derivative of `f` with respect to the argument at a
**compile-time slot** `Val(Slot)`, holding the other arguments fixed. The active
argument comes first, the frozen ones follow in slot order:

```@example diff
# H(x, p) = ½‖p‖² + ‖x‖² → ∂H/∂x = 2x, ∂H/∂p = p
H(x, p) = 0.5 * sum(p .^ 2) + sum(x .^ 2)
x = [1.0, 2.0]; p = [3.0, 4.0]

∂x = Differentiation.differentiate(backend, H, Val(1), x, p) # active = x (slot 1), const = p
∂p = Differentiation.differentiate(backend, H, Val(2), p, x) # active = p (slot 2), const = x
(∂x, ∂p)
```

A scalar slot yields a scalar derivative:

```@example diff
# f(t, x) = t * x[1] → ∂f/∂t = x[1]
ft(t, x) = t * x[1]
Differentiation.differentiate(backend, ft, Val(1), 2.0, [3.0, 4.0])
```

### Pushforward (Jacobian–vector product)

[`Differentiation.pushforward`](@ref CTBase.Differentiation.pushforward) computes the
directional derivative of `f` at `x` along `dx`, freezing the other arguments:

```@example diff
A = [0.0 1.0; -1.0 0.0]
X(x) = A * x # J_X = A → pushforward = A·dx
Differentiation.pushforward(backend, X, Val(1), [1.0, 2.0], [5.0, 6.0])
```

## Hamiltonian gradients

The two domain-specific methods operate directly on a
[`Data.Hamiltonian`](@ref CTBase.Data.Hamiltonian) (see the [Data](data.md) guide).
[`Differentiation.hamiltonian_gradient`](@ref CTBase.Differentiation.hamiltonian_gradient)
returns `(∂H/∂x, ∂H/∂p)`, **non-negated** — the caller applies the signs of
Hamilton's equations (`ẋ = ∂H/∂p`, `ṗ = -∂H/∂x`):

```@example diff
# H(x, p) = ½(‖x‖² + ‖p‖²) → ∂H/∂x = x, ∂H/∂p = p
h = Data.Hamiltonian((x, p) -> 0.5 * (sum(abs2, x) + sum(abs2, p)))
∂x, ∂p = Differentiation.hamiltonian_gradient(backend, h, 0.0, [1.0, 2.0], [3.0, 4.0], nothing)
(∂x, ∂p)
```

For a non-fixed Hamiltonian,
[`Differentiation.variable_gradient`](@ref CTBase.Differentiation.variable_gradient)
returns `∂H/∂v`:

```@example diff
# H(x, p, v) = ½(‖x‖² + ‖p‖² + ‖v‖²) → ∂H/∂v = v
hv = Data.Hamiltonian((x, p, v) -> 0.5 * (sum(abs2, x) + sum(abs2, p) + sum(abs2, v)); is_variable=true)
Differentiation.variable_gradient(backend, hv, 0.0, [1.0, 2.0], [3.0, 4.0], [5.0, 6.0])
```

## The contract without the extension

Every contract method has a fallback on `AbstractADBackend` that throws
[`CTBase.Exceptions.NotImplemented`](@ref) (see the [Exceptions](exceptions.md)
guide). This is what users hit when the AD package is not loaded, and what custom
backends must override:

```@repl diff
struct MyBackend <: Differentiation.AbstractADBackend
options::Strategies.StrategyOptions
end
try # hide
Differentiation.gradient(MyBackend(Strategies.StrategyOptions()), x -> sum(x), [1.0])
catch e # hide
showerror(IOContext(stdout, :color => false), e) # hide
end # hide
```

## See also

- [`Differentiation.AbstractADBackend`](@ref CTBase.Differentiation.AbstractADBackend), [`Differentiation.DifferentiationInterface`](@ref CTBase.Differentiation.DifferentiationInterface) — the strategy types.
- [Implementing a Strategy](@ref), [Options System](@ref) — the contract these build on.
- [Data](data.md) — `Hamiltonian` wrappers consumed by the gradient methods.
- [Exceptions](exceptions.md) — `NotImplemented`, raised by the contract fallbacks.
10 changes: 10 additions & 0 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ It provides the **base layer** shared by all packages: common types, structured
| [`CTBase.Traits`](@ref) | Compile-time trait types for time dependence, variable dependence, mutability, and dynamics dispatch |
| [`CTBase.Data`](@ref) | Trait-carrying function wrappers: `VectorField`, `Hamiltonian`, `HamiltonianVectorField` |
| [`CTBase.Descriptions`](@ref) | Symbolic description tuples: catalogue management, pattern completion, similarity search |
| [`CTBase.Options`](@ref) | Generic option handling: provenance tracking, schema definition, validation, and aliases |
| [`CTBase.Strategies`](@ref) | Strategy contract, registry, building/validation, and metadata for pluggable algorithmic components |
| [`CTBase.Orchestration`](@ref) | Option routing and disambiguation between problem-level actions and strategies |
| [`CTBase.Differentiation`](@ref) | AD-backend strategies for gradients, derivatives, partial derivatives, and Jacobian–vector products |
| [`CTBase.Interpolation`](@ref) | Linear and piecewise-constant interpolation with flat extrapolation |
| [`CTBase.DevTools`](@ref) | Developer tools with tag-based dispatch for `run_tests`, `postprocess_coverage`, and `automatic_reference_documentation` |
| [`CTBase.Unicode`](@ref) | Unicode subscript/superscript helpers for display |

Expand All @@ -39,6 +44,11 @@ It provides the **base layer** shared by all packages: common types, structured
- **[Traits](guide/traits.md)** — compile-time trait types, the opt-in contract, and predicate functions.
- **[Data](guide/data.md)** — trait-carrying wrappers for vector fields and Hamiltonians.
- **[Descriptions](guide/descriptions.md)** — catalogue API, pattern matching, error handling.
- **[Options System](guide/options-system.md)** — option schema, validation, aliases, and provenance.
- **[Implementing a Strategy](guide/implementing-a-strategy.md)** — the strategy contract and how to add one.
- **[Strategy Parameters](guide/strategy-parameters.md)** — declaring and resolving strategy options.
- **[Orchestration & Routing](guide/orchestration-and-routing.md)** — routing options to strategies with disambiguation.
- **[Differentiation](guide/differentiation.md)** — AD-backend strategies and differentiation primitives (extension-backed).
- **[Test Runner](guide/test-runner.md)** — modular test infrastructure with `CTBase.DevTools.run_tests`.
- **[Coverage](guide/coverage.md)** — post-processing coverage artifacts with `CTBase.postprocess_coverage`.
- **[API Documentation](guide/api-documentation.md)** — auto-generating per-module API pages.
Expand Down
Loading