Skip to content

Commit b41db2f

Browse files
authored
Add dev guide to docs (#317)
* Add dev guide to docs * Fix typo * Fix cleanup
1 parent 9fb32cd commit b41db2f

File tree

6 files changed

+150
-71
lines changed

6 files changed

+150
-71
lines changed

.github/workflows/DocPreviewCleanup.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,12 @@ jobs:
1414
runs-on: ubuntu-latest
1515
permissions:
1616
contents: write
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
package:
21+
- DifferentiationInterface
22+
- DifferentiationInterfaceTest
1723
steps:
1824
- name: Checkout gh-pages branch
1925
uses: actions/checkout@v4
@@ -30,4 +36,4 @@ jobs:
3036
git push --force origin gh-pages-new:gh-pages
3137
fi
3238
env:
33-
preview_dir: previews/PR${{ github.event.number }}
39+
preview_dir: ${{ matrix.package }}/previews/PR${{ github.event.number }}

DifferentiationInterface/docs/make.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ makedocs(;
3737
"Home" => "index.md",
3838
"Tutorials" => ["tutorial1.md", "tutorial2.md"],
3939
"Reference" => ["operators.md", "backends.md", "api.md"],
40-
"Advanced" => ["preparation.md", "overloads.md"],
40+
"Advanced" => ["dev_guide.md", "overloads.md"],
4141
],
4242
checkdocs=:exports,
4343
plugins=[links],

DifferentiationInterface/docs/src/backends.md

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -100,17 +100,3 @@ The wrapper [`DifferentiateWith`](@ref) allows you to switch between backends.
100100
It takes a function `f` and specifies that `f` should be differentiated with the backend of your choice, instead of whatever other backend the code is trying to use.
101101
In other words, when someone tries to differentiate `dw = DifferentiateWith(f, backend1)` with `backend2`, then `backend1` steps in and `backend2` does nothing.
102102
At the moment, `DifferentiateWith` only works when `backend2` supports [ChainRules.jl](https://github.com/JuliaDiff/ChainRules.jl).
103-
104-
## Defining your own
105-
106-
To work with DifferentiationInterface.jl, a new AD system would need to create an object subtyping [`ADTypes.AbstractADType`](@extref ADTypes).
107-
In addition, some low-level operators would need to be defined at the very least:
108-
109-
| backend subtype | pushforward necessary | pullback necessary |
110-
| :---------------------------------------- | :-------------------- | :----------------- |
111-
| [`ADTypes.ForwardMode`](@extref ADTypes) | yes | no |
112-
| [`ADTypes.ReverseMode`](@extref ADTypes) | no | yes |
113-
| [`ADTypes.SymbolicMode`](@extref ADTypes) | yes | yes |
114-
115-
Every backend we support corresponds to a package extension of DifferentiationInterface.jl (located in the `ext` subfolder).
116-
If you need to implement your own backend, take a look in there for inspiration, or reach out to us in the GitHub issues.
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Dev guide
2+
3+
This page is important reading if you want to contribute to DifferentiationInterface.jl.
4+
It is not part of the public API.
5+
6+
!!! warning
7+
The content below may become outdated, in which case you should refer to the source code as the ground truth.
8+
9+
## General principles
10+
11+
The package is structured around 8 [operators](@ref Operators):
12+
13+
- [`derivative`](@ref)
14+
- [`second_derivative`](@ref)
15+
- [`gradient`](@ref)
16+
- [`jacobian`](@ref)
17+
- [`hessian`](@ref)
18+
- [`pushforward`](@ref)
19+
- [`pullback`](@ref)
20+
- [`hvp`](@ref)
21+
22+
Most operators have 4 variants, which look like this in the first order: `operator`, `operator!`, `value_and_operator`, `value_and_operator!`.
23+
24+
### New operator
25+
26+
To implement a new operator for an existing backend, you need to write 5 methods: 1 for [preparation](@ref Preparation) and 4 corresponding to the variants of the operator (see above).
27+
In some cases, a subset of those methods will be enough, but most of the time, forgetting one will trigger errors.
28+
For first-order operators, you may also want to support [two-argument functions](@ref "Mutation and signatures"), which requires another 5 methods (defined on `f!` instead of `f`).
29+
30+
The method `prepare_operator` must output an `extras` object of the correct type.
31+
For instance, `prepare_gradient(f, backend, x)` must return a [`DifferentiationInterface.GradientExtras`](@ref).
32+
Assuming you don't need any preparation for said operator, you can use the trivial extras that are already defined, like `DifferentiationInterface.NoGradientExtras`.
33+
Otherwise, define a custom struct like `MyGradientExtras <: DifferentiationInterface.GradientExtras` and put the necessary storage in there.
34+
35+
### New backend
36+
37+
If you want to implement a new backend, for instance because you developed a new AD package called `SuperDiff`, please open a pull request to DifferentiationInterface.jl.
38+
Your AD package needs to be registered first.
39+
40+
#### Core code
41+
42+
In the main package, you should define a new struct `SuperDiffBackend` which subtypes [`ADTypes.AbstractADType`](@extref ADTypes), and endow it with the fields you need to parametrize your differentiation routines.
43+
You also have to define [`ADTypes.mode`](@extref) and [`DifferentiationInterface.twoarg_support`](@ref) on `SuperDiffBackend`.
44+
45+
!!! info
46+
In the end, this backend struct will need to be contributed to [ADTypes.jl](https://github.com/SciML/ADTypes.jl).
47+
However, putting it in the DifferentiationInterface.jl PR is a good first step for debugging.
48+
49+
In a [package extension](https://pkgdocs.julialang.org/v1/creating-packages/#Conditional-loading-of-code-in-packages-(Extensions)) named `DifferentiationInterfaceSuperDiffExt`, you need to implement at least [`pushforward`](@ref) or [`pullback`](@ref) (and their variants).
50+
The exact requirements depend on the differentiation mode you chose:
51+
52+
| backend mode | pushforward necessary | pullback necessary |
53+
| :------------------------------------------------ | :-------------------- | :----------------- |
54+
| [`ADTypes.ForwardMode`](@extref ADTypes) | yes | no |
55+
| [`ADTypes.ReverseMode`](@extref ADTypes) | no | yes |
56+
| [`ADTypes.ForwardOrReverseMode`](@extref ADTypes) | yes | yes |
57+
| [`ADTypes.SymbolicMode`](@extref ADTypes) | yes | yes |
58+
59+
Every other operator can be deduced from these two, but you can gain efficiency by implementing additional operators.
60+
61+
#### Tests and docs
62+
63+
Once that is done, you need to add your new backend to the test suite.
64+
Test files should be gathered in a folder named `SuperDiff` inside [`DifferentiationInterface/test/Single`](https://github.com/gdalle/DifferentiationInterface.jl/tree/main/DifferentiationInterface/test/Single).
65+
They should use [DifferentiationInterfaceTest.jl](https://github.com/gdalle/DifferentiationInterface.jl/tree/main/DifferentiationInterfaceTest) to check correctness against the default scenarios.
66+
Take inspiration from the tests of other backends to write your own.
67+
To activate tests in CI, modify the [test workflow](https://github.com/gdalle/DifferentiationInterface.jl/blob/main/.github/workflows/Test.yml) and add your package to the list.
68+
To run the tests locally, replace the following line in [`DifferentiationInterface/test/runtests.jl`](https://github.com/gdalle/DifferentiationInterface.jl/blob/main/DifferentiationInterface/test/runtests.jl)
69+
70+
```julia
71+
GROUP = get(ENV, "JULIA_DI_TEST_GROUP", "All")
72+
```
73+
74+
with the much cheaper version
75+
76+
```julia
77+
GROUP = get(ENV, "JULIA_DI_TEST_GROUP", "Single/SuperDiff")
78+
```
79+
80+
but don't forget to switch it back before pushing.
81+
82+
Finally, you need to add your backend to the documentation, modifying every page that involves a list of backends.
83+
That includes the README.
84+
85+
## Specific details
86+
87+
Here we give some more information on the contents of the extension for each backend.
88+
89+
### ChainRulesCore
90+
91+
For [`pullback`](@ref), same-point preparation runs the forward sweep and returns the pullback closure.
92+
93+
### Enzyme
94+
95+
In forward mode, for [`gradient`](@ref) and [`jacobian`](@ref), preparation chooses a number of chunks.
96+
97+
### FastDifferentiation
98+
99+
Preparation generates an [executable function](https://brianguenter.github.io/FastDifferentiation.jl/stable/makefunction/) from the symbolic expression of the differentiated function.
100+
101+
!!! warning
102+
Preparation can be very slow for symbolic AD.
103+
104+
### FiniteDiff
105+
106+
Whenever possible, preparation creates a cache object.
107+
108+
### ForwardDiff
109+
110+
Wherever possible, preparation creates a [config](https://juliadiff.org/ForwardDiff.jl/stable/user/api/#Preallocating/Configuring-Work-Buffers).
111+
For [`pushforward`](@ref), preparation allocates the necessary space for `Dual` number computations.
112+
113+
### ReverseDiff
114+
115+
Wherever possible, preparation records a [tape](https://juliadiff.org/ReverseDiff.jl/dev/api/#The-AbstractTape-API) of the function's execution.
116+
117+
!!! warning
118+
This tape is specific to the control flow inside the function, and cannot be reused if the control flow is value-dependent (like `if x[1] > 0`).
119+
120+
### Symbolics
121+
122+
Preparation generates an [executable function](https://docs.sciml.ai/Symbolics/stable/manual/build_function/) from the symbolic expression of the differentiated function.
123+
124+
!!! warning
125+
Preparation can be very slow for symbolic AD.
126+
127+
### Tapir
128+
129+
For [`pullback`](@ref), preparation [builds the reverse rule](https://github.com/withbayes/Tapir.jl?tab=readme-ov-file#how-it-works) of the function.
130+
131+
### Tracker
132+
133+
For [`pullback`](@ref), same-point preparation runs the forward sweep and returns the pullback closure at `x`.
134+
135+
### Zygote
136+
137+
For [`pullback`](@ref), same-point preparation runs the forward sweep and returns the pullback closure at `x`.

DifferentiationInterface/docs/src/operators.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,14 @@ Several variants of each operator are defined.
5858

5959
## Mutation and signatures
6060

61-
We support two types of functions:
61+
Some operators support two types of functions:
6262

6363
- one-argument functions `f(x) = y`
6464
- two-argument functions `f!(y, x) = nothing`
6565

66+
!!! warning
67+
Only [`pushforward`](@ref), [`pullback`](@ref), [`derivative`](@ref) and [`jacobian`](@ref) support two-argument functions at the moment.
68+
6669
The same operators are defined for both cases, but they have different signatures (they take different arguments):
6770

6871
| signature | out-of-place | in-place |
@@ -75,7 +78,7 @@ The same operators are defined for both cases, but they have different signature
7578
This convention holds regardless of the bang `!` in the operator name.
7679
In particular, for two-argument functions `f!(y, x)`, every variant of every operator will mutate `y`.
7780

78-
## [Preparation](@id Operators-Preparation)
81+
## Preparation
7982

8083
### Principle
8184

DifferentiationInterface/docs/src/preparation.md

Lines changed: 0 additions & 53 deletions
This file was deleted.

0 commit comments

Comments
 (0)