Skip to content
This repository was archived by the owner on Sep 28, 2024. It is now read-only.

Commit 1ae1d89

Browse files
authored
Merge pull request #23 from foldfelis/dp
add double pendulum example and the docs
2 parents 7c29024 + e040ade commit 1ae1d89

File tree

14 files changed

+421
-24
lines changed

14 files changed

+421
-24
lines changed

README.md

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,15 @@
1212
[codecov badge]: https://codecov.io/gh/foldfelis/NeuralOperators.jl/branch/master/graph/badge.svg?token=JQH3MP1Y9R
1313
[codecov link]: https://codecov.io/gh/foldfelis/NeuralOperators.jl
1414

15+
| **Ground Truth** | **Inferenced** |
16+
|:----------------:|:--------------:|
17+
| ![](example/FlowOverCircle/gallery/ans.gif) | ![](example/FlowOverCircle/gallery/inferenced.gif) |
18+
19+
The demonstration showing above is Navier-Stokes equation learned by the `MarkovNeuralOperator` with only one time step information.
20+
Example can be found in [`example/FlowOverCircle`](example/FlowOverCircle).
21+
22+
## Abstract
23+
1524
Neural operator is a novel deep learning architecture.
1625
It learns a operator, which is a mapping between infinite-dimensional function spaces.
1726
It can be used to resolve [partial differential equations (PDE)](https://en.wikipedia.org/wiki/Partial_differential_equation).
@@ -70,34 +79,17 @@ Flux.@epochs 50 Flux.train!(loss, params(model), data, opt)
7079

7180
PDE training examples are provided in `example` folder.
7281

73-
### One-dimensional Burgers' equation
74-
75-
[Burgers' equation](https://en.wikipedia.org/wiki/Burgers%27_equation) example can be found in `example/Burgers`.
76-
Use following commend to train model:
82+
### One-dimensional Fourier Neural Operator
7783

78-
```julia
79-
$ julia --proj
84+
[Burgers' equation](example/Burgers)
8085

81-
julia> using Burgers; Burgers.train()
82-
```
86+
### Two-dimensional Fourier Neural Operator
8387

84-
### Two-dimensional with time Navier-Stokes equation
88+
[Double Pendulum](example/DoublePendulum)
8589

86-
The Navier-Stokes equation is learned by the `MarkovNeuralOperator` with only one time step information.
87-
Example can be found in `example/FlowOverCircle`.
88-
The result is also provided [here](https://foldfelis.github.io/NeuralOperators.jl/dev/assets/notebook/mno.jl.html)
90+
### Markov Neural Operator
8991

90-
| **Ground Truth** | **Inferenced** |
91-
|:----------------:|:--------------:|
92-
| ![](example/FlowOverCircle/gallery/ans.gif) | ![](example/FlowOverCircle/gallery/inferenced.gif) |
93-
94-
Use following commend to train model:
95-
96-
```julia
97-
$ julia --proj
98-
99-
julia> using FlowOverCircle; FlowOverCircle.train()
100-
```
92+
[Time dependent Navier-Stokes equation](example/FlowOverCircle)
10193

10294
## Roadmap
10395

docs/src/assets/notebook/double_pendulum.jl.html

Lines changed: 75 additions & 0 deletions
Large diffs are not rendered by default.

docs/src/index.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ Documentation for [NeuralOperators](https://github.com/foldfelis/NeuralOperators
1212

1313
The demonstration showing above is Navier-Stokes equation learned by the `MarkovNeuralOperator` with only one time step information.
1414
Example can be found in [`example/FlowOverCircle`](https://github.com/foldfelis/NeuralOperators.jl/tree/master/example/FlowOverCircle).
15-
The result is also provided [here](assets/notebook/mno.jl.html)
1615

1716
## Abstract
1817

example/Burgers/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Burgers' equation
2+
3+
In this example, a [Burgers' equation](https://en.wikipedia.org/wiki/Burgers%27_equation)
4+
is learned by a one-dimensional Fourier neural operator network.
5+
Change directory to `example/Burgers` and use following commend to train model:
6+
7+
```julia
8+
$ julia --proj
9+
10+
julia> using Burgers; Burgers.train()
11+
```
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
name = "DoublePendulum"
2+
uuid = "0c23c1c1-5f41-4617-a685-ac46aae913c3"
3+
4+
[deps]
5+
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
6+
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
7+
DataDeps = "124859b0-ceae-595e-8997-d05f6a7a8dfe"
8+
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
9+
Flux = "587475ba-b771-5e3f-ad9e-33799f191a9c"
10+
JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819"
11+
NeuralOperators = "ea5c82af-86e5-48da-8ee1-382d6ad7af4b"
12+
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
13+
Pluto = "c3e4b0f8-55cb-11ea-2926-15256bba5781"
14+
15+
[extras]
16+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
17+
18+
[targets]
19+
test = ["Test"]

example/DoublePendulum/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Double pendulum
2+
3+
A [double pendulum](https://www.wikiwand.com/en/Double_pendulum) is a pendulum with another pendulum attached to its end.
4+
In is example, instead of learning from a well-described equation of motion,
5+
we train the model with the famous dataset provided by IBM.
6+
The equation of motion to the real experiments of double pendulum is learned by a two-dimensional Fourier neural operator.
7+
It learns to inference the next 30 steps with the given first 30 steps.
8+
The result of this example can be found [here](https://foldfelis.github.io/NeuralOperators.jl/dev/assets/notebook/double_pendulum.jl.html).
9+
10+
![](gallery/result.gif)
11+
12+
By inference the result recurrently, we can generate up to 150 steps with the first 30 initial steps.
13+
14+
Change directory to `example/DoublePendulum` and use following commend to train model:
15+
16+
```julia
17+
$ julia --proj
18+
19+
julia> using DoublePendulum; DoublePendulum.train()
20+
```
205 KB
Loading

example/DoublePendulum/model/.gitkeep

Whitespace-only changes.
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
### A Pluto.jl notebook ###
2+
# v0.16.0
3+
4+
using Markdown
5+
using InteractiveUtils
6+
7+
# ╔═╡ 194baef2-0417-11ec-05ab-4527ef614024
8+
using Pkg; Pkg.develop(path=".."); Pkg.activate("..")
9+
10+
# ╔═╡ 38c9ced5-dcf8-4e03-ac07-7c435687861b
11+
begin
12+
using DoublePendulum
13+
using Plots
14+
end
15+
16+
# ╔═╡ 396b5d7a-a7a4-4f22-a87e-39b405e8d62a
17+
md"
18+
# Double Pendulum
19+
20+
JingYu Ning
21+
"
22+
23+
# ╔═╡ 2a606ecf-acf0-41ad-9290-7569dbb22b5a
24+
md"
25+
The data is provided by [IBM](https://developer.ibm.com/exchanges/data/all/double-pendulum-chaotic/)
26+
27+
> In this dataset, videos of the double pendulum were taken using a high-speed Phantom Miro EX2 camera. To make the extraction of the arm positions easier, a matte black background was used, and the three datums were marked with red, green and blue fiducial markers. The camera was placed at 2 meters from the pendulum, with the axis of the objective aligned with the first pendulum datum. The pendulum was launched by hand, and the camera was motion triggered. The dataset was generated on the basis of 21 individual runs of the pendulum. Each of the recorded sequences lasted around 40s and consisted of around 17500 frames.
28+
"
29+
30+
# ╔═╡ 5268feee-bda2-4612-9d4c-a1db424a11c7
31+
data_x, data_y, _, _ = DoublePendulum.preprocess(
32+
DoublePendulum.get_data(i=20),
33+
ratio=1
34+
);
35+
36+
# ╔═╡ 4d0b08a4-8a54-41fd-997f-ad54d4c984cd
37+
m = DoublePendulum.get_model();
38+
39+
# ╔═╡ ad6302b2-3d62-4a3f-b8bf-f69bab80c7a4
40+
ground_truth_data = cat(
41+
[data_x[:, :, :, i]
42+
for i in 1:size(data_x, 3):size(data_x)[end]]..., dims=3
43+
)[1, :, :];
44+
45+
# ╔═╡ 794374ce-6674-481d-8a3b-04db0f32d233
46+
begin
47+
n = 5
48+
inferenced_data = data_x[:, :, :, 1:1]
49+
for i in 1:n
50+
inferenced_data = cat(
51+
inferenced_data,
52+
m(inferenced_data[:, :, :, i:i]),
53+
dims=4
54+
)
55+
end
56+
57+
inferenced_data = cat(
58+
[inferenced_data[:, :, :, i] for i in 1:n]..., dims=3
59+
)[1, :, :]
60+
end;
61+
62+
# ╔═╡ 9c8b3f8a-1b85-4c32-a416-ead51b244b94
63+
begin
64+
c = [
65+
RGB([239, 71, 111]/255...),
66+
RGB([6, 214, 160]/255...),
67+
RGB([17, 138, 178]/255...)
68+
]
69+
xi, yi = [2, 4, 6], [1, 3, 5]
70+
71+
anim = @animate for i in 1:size(inferenced_data)[end]
72+
i_data = [0, 0, inferenced_data[:, i]...]
73+
g_data = [0, 0, ground_truth_data[:, i]...]
74+
75+
scatter(
76+
legend=false, ticks=false,
77+
xlim=(-1500, 1500), ylim=(-1500, 1500), size=(400, 350)
78+
)
79+
plot!(i_data[xi], i_data[yi], color=:black)
80+
scatter!(i_data[xi], i_data[yi], color=c, markersize=8)
81+
plot!(g_data[xi], g_data[yi], color=:gray)
82+
scatter!(g_data[xi], g_data[yi], color=c, markersize=4)
83+
84+
if i 30
85+
annotate!(-1400, -1400, text("t=$i", :left, color=:black))
86+
else
87+
annotate!(-1400, -1400, text("t=$i", :left, color=:red))
88+
end
89+
end
90+
91+
gif(anim)
92+
end
93+
94+
# ╔═╡ Cell order:
95+
# ╟─396b5d7a-a7a4-4f22-a87e-39b405e8d62a
96+
# ╟─2a606ecf-acf0-41ad-9290-7569dbb22b5a
97+
# ╟─194baef2-0417-11ec-05ab-4527ef614024
98+
# ╠═38c9ced5-dcf8-4e03-ac07-7c435687861b
99+
# ╠═5268feee-bda2-4612-9d4c-a1db424a11c7
100+
# ╠═4d0b08a4-8a54-41fd-997f-ad54d4c984cd
101+
# ╠═ad6302b2-3d62-4a3f-b8bf-f69bab80c7a4
102+
# ╠═794374ce-6674-481d-8a3b-04db0f32d233
103+
# ╟─9c8b3f8a-1b85-4c32-a416-ead51b244b94
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
module DoublePendulum
2+
3+
using NeuralOperators
4+
using Flux
5+
using CUDA
6+
using JLD2
7+
8+
include("data.jl")
9+
10+
__init__() = register_double_pendulum_chaotic()
11+
12+
function update_model!(model_file_path, model)
13+
model = cpu(model)
14+
jldsave(model_file_path; model)
15+
@warn "model updated!"
16+
end
17+
18+
function train(; Δt=1)
19+
if has_cuda()
20+
@info "CUDA is on"
21+
device = gpu
22+
CUDA.allowscalar(false)
23+
else
24+
device = cpu
25+
end
26+
27+
m = Chain(
28+
Dense(2, 64),
29+
FourierOperator(64=>64, (4, 16), gelu),
30+
FourierOperator(64=>64, (4, 16), gelu),
31+
FourierOperator(64=>64, (4, 16), gelu),
32+
FourierOperator(64=>64, (4, 16)),
33+
Dense(64, 128, gelu),
34+
Dense(128, 2),
35+
) |> device
36+
37+
loss(𝐱, 𝐲) = sum(abs2, 𝐲 .- m(𝐱)) / size(𝐱)[end]
38+
39+
opt = Flux.Optimiser(WeightDecay(1f-4), Flux.ADAM(1f-3))
40+
41+
loader_train, loader_test = get_dataloader(Δt=Δt)
42+
43+
losses = Float32[]
44+
function validate()
45+
validation_loss = sum(loss(device(𝐱), device(𝐲)) for (𝐱, 𝐲) in loader_test)/length(loader_test)
46+
@info "loss: $validation_loss"
47+
48+
push!(losses, validation_loss)
49+
(losses[end] == minimum(losses)) && update_model!(joinpath(@__DIR__, "../model/model.jld2"), m)
50+
end
51+
call_back = Flux.throttle(validate, 10, leading=false, trailing=true)
52+
53+
data = [(𝐱, 𝐲) for (𝐱, 𝐲) in loader_train] |> device
54+
for e in 1:20
55+
@info "Epoch $e\n η: $(opt.os[2].eta)"
56+
@time Flux.train!(loss, params(m), data, opt, cb=call_back)
57+
(e%3 == 0) && (opt.os[2].eta /= 2)
58+
end
59+
end
60+
61+
function get_model()
62+
f = jldopen(joinpath(@__DIR__, "../model/model.jld2"))
63+
model = f["model"]
64+
close(f)
65+
66+
return model
67+
end
68+
69+
end

0 commit comments

Comments
 (0)