Skip to content

Commit ef7919a

Browse files
Merge pull request #721 from SciML/doctests
Enable doctesting
2 parents d367dc6 + b3bd9ff commit ef7919a

File tree

10 files changed

+184
-194
lines changed

10 files changed

+184
-194
lines changed

docs/Project.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
[deps]
2+
DiffEqFlux = "aae7a2af-3d4f-5e19-a356-7da93b79d9d0"
3+
DiffEqSensitivity = "41bf760c-e81c-5289-8e54-58b1f1f8abe2"
4+
DifferentialEquations = "0c46a032-eb83-5123-abaf-570d42b7fbaa"
5+
Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
6+
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
27
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
8+
Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c"
9+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
10+
Lux = "b2108857-7c20-44ae-9111-449ecde12c47"
11+
Optimization = "7f7a1694-90dd-40f0-9382-eb1efda571ba"
12+
OptimizationFlux = "253f991c-a7b2-45f8-8852-8b9a9df78a86"
13+
OptimizationOptimJL = "36348300-93cb-4f02-beb5-3c3902f8871e"
14+
OptimizationPolyalgorithms = "500b13db-7e66-49ce-bda4-eed966be6282"
15+
OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed"
16+
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
17+
ReverseDiff = "37e2e3b7-166d-5795-8a7a-e32c996b4267"
18+
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
319

420
[compat]
521
Documenter = "0.27"

docs/make.jl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
using Documenter, DiffEqFlux
22

3+
ENV["GKSwstype"] = "100"
4+
using Plots
5+
36
include("pages.jl")
47

58
makedocs(
@@ -8,7 +11,14 @@ makedocs(
811
clean = true,
912
doctest = false,
1013
modules = [DiffEqFlux],
11-
14+
strict=[
15+
:doctest,
16+
:linkcheck,
17+
:parse_error,
18+
:example_block,
19+
# Other available options are
20+
# :autodocs_block, :cross_references, :docs_block, :eval_block, :example_block, :footnote, :meta_block, :missing_docs, :setup_block
21+
],
1222
format = Documenter.HTML(analytics = "UA-90474609-3",
1323
assets = ["assets/favicon.ico"],
1424
canonical="https://diffeqflux.sciml.ai/stable/"),

docs/src/examples/augmented_neural_ode.md

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
## Copy-Pasteable Code
44

5-
```julia
5+
```@example augneuralode_cp
66
using DiffEqFlux, DifferentialEquations
77
using Statistics, LinearAlgebra, Plots
88
using Flux.Data: DataLoader
@@ -28,7 +28,7 @@ function concentric_sphere(dim, inner_radius_range, outer_radius_range,
2828
end
2929
data = cat(data..., dims=2)
3030
labels = cat(labels..., dims=2)
31-
return DataLoader((data |> gpu, labels |> gpu); batchsize=batch_size, shuffle=true,
31+
DataLoader((data |> gpu, labels |> gpu); batchsize=batch_size, shuffle=true,
3232
partial=false)
3333
end
3434
@@ -43,6 +43,7 @@ function construct_model(out_dim, input_dim, hidden_dim, augment_dim)
4343
reltol = 1e-3, abstol = 1e-3, save_start = false) |> gpu
4444
node = augment_dim == 0 ? node : AugmentedNDELayer(node, augment_dim)
4545
return Chain((x, p=node.p) -> node(x, p),
46+
Array,
4647
diffeqarray_to_array,
4748
Dense(input_dim, out_dim) |> gpu), node.p |> gpu
4849
end
@@ -67,34 +68,34 @@ println("Generating Dataset")
6768
6869
dataloader = concentric_sphere(2, (0.0, 2.0), (3.0, 4.0), 2000, 2000; batch_size = 256)
6970
71+
iter = 0
7072
cb = function()
71-
global iter += 1
73+
global iter
74+
iter += 1
7275
if iter % 10 == 0
7376
println("Iteration $iter || Loss = $(loss_node(dataloader.data[1], dataloader.data[2]))")
7477
end
7578
end
7679
7780
model, parameters = construct_model(1, 2, 64, 0)
7881
opt = ADAM(0.005)
79-
iter = 0
8082
8183
println("Training Neural ODE")
8284
8385
for _ in 1:10
84-
Flux.train!(loss_node, Flux.params([parameters, model]), dataloader, opt, cb = cb)
86+
Flux.train!(loss_node, Flux.params(parameters, model), dataloader, opt, cb = cb)
8587
end
8688
8789
plt_node = plot_contour(model)
8890
8991
model, parameters = construct_model(1, 2, 64, 1)
9092
opt = ADAM(0.005)
91-
iter = 0
9293
9394
println()
9495
println("Training Augmented Neural ODE")
9596
9697
for _ in 1:10
97-
Flux.train!(loss_node, Flux.params([parameters, model]), dataloader, opt, cb = cb)
98+
Flux.train!(loss_node, Flux.params(parameters, model), dataloader, opt, cb = cb)
9899
end
99100
100101
plt_anode = plot_contour(model)
@@ -104,7 +105,7 @@ plt_anode = plot_contour(model)
104105

105106
## Loading required packages
106107

107-
```julia
108+
```@example augneuralode
108109
using DiffEqFlux, DifferentialEquations
109110
using Statistics, LinearAlgebra, Plots
110111
using Flux.Data: DataLoader
@@ -118,7 +119,7 @@ circle, and `-1` to any point which lies between the inner and outer circle. Our
118119
`random_point_in_sphere` samples points uniformly between 2 concentric circles/spheres of radii
119120
`min_radius` and `max_radius` respectively.
120121

121-
```julia
122+
```@example augneuralode
122123
function random_point_in_sphere(dim, min_radius, max_radius)
123124
distance = (max_radius - min_radius) .* (rand(1) .^ (1.0 / dim)) .+ min_radius
124125
direction = randn(dim)
@@ -130,7 +131,7 @@ end
130131
Next, we will construct a dataset of these points and use Flux's DataLoader to automatically minibatch
131132
and shuffle the data.
132133

133-
```julia
134+
```@example augneuralode
134135
function concentric_sphere(dim, inner_radius_range, outer_radius_range,
135136
num_samples_inner, num_samples_outer; batch_size = 64)
136137
data = []
@@ -145,7 +146,7 @@ function concentric_sphere(dim, inner_radius_range, outer_radius_range,
145146
end
146147
data = cat(data..., dims=2)
147148
labels = cat(labels..., dims=2)
148-
return DataLoader(data |> gpu, labels |> gpu; batchsize=batch_size, shuffle=true,
149+
return DataLoader((data |> gpu, labels |> gpu); batchsize=batch_size, shuffle=true,
149150
partial=false)
150151
end
151152
```
@@ -162,7 +163,7 @@ and construct that layer accordingly.
162163
In order to run the models on GPU, we need to manually transfer the models to GPU. First one is the network
163164
predicting the derivatives inside the Neural ODE and the other one is the last layer in the Chain.
164165

165-
```julia
166+
```@example augneuralode
166167
diffeqarray_to_array(x) = reshape(gpu(x), size(x)[1:2])
167168
168169
function construct_model(out_dim, input_dim, hidden_dim, augment_dim)
@@ -174,6 +175,7 @@ function construct_model(out_dim, input_dim, hidden_dim, augment_dim)
174175
reltol = 1e-3, abstol = 1e-3, save_start = false) |> gpu
175176
node = augment_dim == 0 ? node : (AugmentedNDELayer(node, augment_dim) |> gpu)
176177
return Chain((x, p=node.p) -> node(x, p),
178+
Array,
177179
diffeqarray_to_array,
178180
Dense(input_dim, out_dim) |> gpu), node.p |> gpu
179181
end
@@ -183,7 +185,7 @@ end
183185

184186
Here, we define an utility to plot our model regression results as a heatmap.
185187

186-
```julia
188+
```@example augneuralode
187189
function plot_contour(model, npoints = 300)
188190
grid_points = zeros(2, npoints ^ 2)
189191
idx = 1
@@ -206,7 +208,7 @@ end
206208
We use the L2 distance between the model prediction `model(x)` and the actual prediction `y` as the
207209
optimization objective.
208210

209-
```julia
211+
```@example augneuralode
210212
loss_node(x, y) = mean((model(x) .- y) .^ 2)
211213
```
212214

@@ -215,15 +217,16 @@ loss_node(x, y) = mean((model(x) .- y) .^ 2)
215217
Next, we generate the dataset. We restrict ourselves to 2 dimensions as it is easy to visualize.
216218
We sample a total of `4000` data points.
217219

218-
```julia
220+
```@example augneuralode
219221
dataloader = concentric_sphere(2, (0.0, 2.0), (3.0, 4.0), 2000, 2000; batch_size = 256)
220222
```
221223

222224
### Callback Function
223225

224226
Additionally we define a callback function which displays the total loss at specific intervals.
225227

226-
```julia
228+
```@example augneuralode
229+
iter = 0
227230
cb = function()
228231
global iter += 1
229232
if iter % 10 == 1
@@ -236,7 +239,7 @@ end
236239

237240
We use ADAM as the optimizer with a learning rate of 0.005
238241

239-
```julia
242+
```@example augneuralode
240243
opt = ADAM(0.005)
241244
```
242245

@@ -246,11 +249,11 @@ To train our neural ode model, we need to pass the appropriate learnable paramet
246249
returned by the `construct_models` function. It is simply the `node.p` vector. We then train our model
247250
for `20` epochs.
248251

249-
```julia
252+
```@example augneuralode
250253
model, parameters = construct_model(1, 2, 64, 0)
251254
252255
for _ in 1:10
253-
Flux.train!(loss_node, Flux.params([model, parameters]), dataloader, opt, cb = cb)
256+
Flux.train!(loss_node, Flux.params(model, parameters), dataloader, opt, cb = cb)
254257
end
255258
```
256259

@@ -265,11 +268,11 @@ Our training configuration will be same as that of Neural ODE. Only in this case
265268
input with a single zero. This makes the problem 3 dimensional and as such it is possible to find
266269
a function which can be expressed by the neural ode. For more details and proofs please refer to [1].
267270

268-
```julia
271+
```@example augneuralode
269272
model, parameters = construct_model(1, 2, 64, 1)
270273
271274
for _ in 1:10
272-
Flux.train!(loss_node, Flux.params([model, parameters]), dataloader, opt, cb = cb)
275+
Flux.train!(loss_node, Flux.params(model, parameters), dataloader, opt, cb = cb)
273276
end
274277
```
275278

docs/src/examples/collocation.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ One can avoid a lot of the computational cost of the ODE solver by
44
pretraining the neural network against a smoothed collocation of the
55
data. First the example and then an explanation.
66

7-
```julia
7+
```@example collocation_cp
88
using Lux, DiffEqFlux, OrdinaryDiffEq, DiffEqSensitivity, Optimization, OptimizationFlux, Plots
99
1010
using Random
@@ -79,7 +79,7 @@ optprob = Optimization.OptimizationProblem(optf, Lux.ComponentArray(pinit))
7979
8080
numerical_neuralode = Optimization.solve(optprob,
8181
ADAM(0.05),
82-
cb = callback,
82+
callback = callback,
8383
maxiters = 300)
8484
8585
nn_sol, st = prob_neuralode(u0, numerical_neuralode.u, st)
@@ -93,8 +93,11 @@ savefig("post_trained.png")
9393
The smoothed collocation is a spline fit of the datapoints which allows
9494
us to get a an estimate of the approximate noiseless dynamics:
9595

96-
```julia
97-
using Flux, DiffEqFlux, Optimization, OptimizationFlux, DifferentialEquations, Plots
96+
```@example collocation
97+
using Lux, DiffEqFlux, Optimization, OptimizationFlux, DifferentialEquations, Plots
98+
99+
using Random
100+
rng = Random.default_rng()
98101
99102
u0 = Float32[2.0; 0.0]
100103
datasize = 300
@@ -120,7 +123,7 @@ plot!(tsteps,u',lw=5)
120123
We can then differentiate the smoothed function to get estimates of the
121124
derivative at each datapoint:
122125

123-
```julia
126+
```@example collocation
124127
plot(tsteps,du')
125128
```
126129

@@ -130,7 +133,7 @@ Because we have `(u',u)` pairs, we can write a loss function that
130133
calculates the squared difference between `f(u,p,t)` and `u'` at each
131134
point, and find the parameters which minimize this difference:
132135

133-
```julia
136+
```@example collocation
134137
dudt2 = Lux.Chain(ActivationFunction(x -> x.^3),
135138
Lux.Dense(2, 50, tanh),
136139
Lux.Dense(50, 2))
@@ -170,7 +173,7 @@ full solution all throughout the timeseries, but it does have a drift.
170173
We can continue to optimize like this, or we can use this as the
171174
initial condition to the next phase of our fitting:
172175

173-
```julia
176+
```@example collocation
174177
function predict_neuralode(p)
175178
Array(prob_neuralode(u0, p, st)[1])
176179
end
@@ -187,7 +190,7 @@ optprob = Optimization.OptimizationProblem(optf, Lux.ComponentArray(pinit))
187190
188191
numerical_neuralode = Optimization.solve(optprob,
189192
ADAM(0.05),
190-
cb = callback,
193+
callback = callback,
191194
maxiters = 300)
192195
193196
nn_sol, st = prob_neuralode(u0, numerical_neuralode.u, st)

docs/src/examples/hamiltonian_nn.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ m\ddot(x) + kx = 0
88

99
Now we make some simplifying assumptions, and assign ``m = 1`` and ``k = 1``. Analytically solving this equation, we get ``x = sin(t)``. Hence, ``q = sin(t)``, and ``p = cos(t)``. Using these solutions we generate our dataset and fit the `NeuralHamiltonianDE` to learn the dynamics of this system.
1010

11-
```julia
12-
using DiffEqFlux, DifferentialEquations, Statistics, Plots
11+
```@example hamiltonian_cp
12+
using Flux, DiffEqFlux, DifferentialEquations, Statistics, Plots, ReverseDiff
1313
1414
t = range(0.0f0, 1.0f0, length = 1024)
1515
π_32 = Float32(π)
@@ -20,7 +20,7 @@ dpdt = -2π_32 .* q_t
2020
2121
data = cat(q_t, p_t, dims = 1)
2222
target = cat(dqdt, dpdt, dims = 1)
23-
dataloader = Flux.Data.DataLoader(data, target; batchsize=256, shuffle=true)
23+
dataloader = Flux.Data.DataLoader((data, target); batchsize=256, shuffle=true)
2424
2525
hnn = HamiltonianNN(
2626
Chain(Dense(2, 64, relu), Dense(64, 1))
@@ -65,7 +65,9 @@ ylabel!("Momentum (p)")
6565

6666
The HNN predicts the gradients ``(\dot(q), \dot(p))`` given ``(q, p)``. Hence, we generate the pairs ``(q, p)`` using the equations given at the top. Additionally to supervise the training we also generate the gradients. Next we use use Flux DataLoader for automatically batching our dataset.
6767

68-
```julia
68+
```@example hamiltonian
69+
using Flux, DiffEqFlux, DifferentialEquations, Statistics, Plots, ReverseDiff
70+
6971
t = range(0.0f0, 1.0f0, length = 1024)
7072
π_32 = Float32(π)
7173
q_t = reshape(sin.(2π_32 * t), 1, :)
@@ -75,14 +77,14 @@ dpdt = -2π_32 .* q_t
7577
7678
data = cat(q_t, p_t, dims = 1)
7779
target = cat(dqdt, dpdt, dims = 1)
78-
dataloader = Flux.Data.DataLoader(data, target; batchsize=256, shuffle=true)
80+
dataloader = Flux.Data.DataLoader((data, target); batchsize=256, shuffle=true)
7981
```
8082

8183
### Training the HamiltonianNN
8284

8385
We parameterize the HamiltonianNN with a small MultiLayered Perceptron (HNN also works with the Fast* Layers provided in DiffEqFlux). HNNs are trained by optimizing the gradients of the Neural Network. Zygote currently doesn't support nesting itself, so we will be using ReverseDiff in the training loop to compute the gradients of the HNN Layer for Optimization.
8486

85-
```julia
87+
```@example hamiltonian
8688
hnn = HamiltonianNN(
8789
Chain(Dense(2, 64, relu), Dense(64, 1))
8890
)
@@ -112,7 +114,7 @@ callback()
112114

113115
In order to visualize the learned trajectories, we need to solve the ODE. We will use the `NeuralHamiltonianDE` layer which is essentially a wrapper over `HamiltonianNN` layer and solves the ODE.
114116

115-
```julia
117+
```@example hamiltonian
116118
model = NeuralHamiltonianDE(
117119
hnn, (0.0f0, 1.0f0),
118120
Tsit5(), save_everystep = false,

0 commit comments

Comments
 (0)