Skip to content

Commit c77fc91

Browse files
committed
feat (add examples and tests for 2D monitoring)
1 parent 128d212 commit c77fc91

2 files changed

Lines changed: 168 additions & 0 deletions

File tree

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""Backend supported: tensorflow.compat.v1, tensorflow, pytorch, jax, paddle
2+
3+
Same problem as ``diffusion_1d.py``, but using
4+
``dde.callbacks.TrainingMonitor`` to watch the predicted solution over the
5+
2D (x, t) domain and the loss history update live during training, instead
6+
of only inspecting a plot after training finishes.
7+
"""
8+
import deepxde as dde
9+
import numpy as np
10+
# Backend tensorflow.compat.v1 or tensorflow
11+
from deepxde.backend import tf
12+
# Backend pytorch
13+
# import torch
14+
# Backend jax
15+
# import jax.numpy as jnp
16+
# Backend paddle
17+
# import paddle
18+
19+
20+
def pde(x, y):
21+
# Most backends
22+
dy_t = dde.grad.jacobian(y, x, j=1)
23+
dy_xx = dde.grad.hessian(y, x, j=0)
24+
# Backend jax
25+
# dy_t, _ = dde.grad.jacobian(y, x, j=1)
26+
# dy_xx, _ = dde.grad.hessian(y, x, j=0)
27+
# Backend tensorflow.compat.v1 or tensorflow
28+
return (
29+
dy_t
30+
- dy_xx
31+
+ tf.exp(-x[:, 1:])
32+
* (tf.sin(np.pi * x[:, 0:1]) - np.pi ** 2 * tf.sin(np.pi * x[:, 0:1]))
33+
)
34+
# Backend pytorch
35+
# return (
36+
# dy_t
37+
# - dy_xx
38+
# + torch.exp(-x[:, 1:])
39+
# * (torch.sin(np.pi * x[:, 0:1]) - np.pi ** 2 * torch.sin(np.pi * x[:, 0:1]))
40+
# )
41+
# Backend jax
42+
# return (
43+
# dy_t
44+
# - dy_xx
45+
# + jnp.exp(-x[:, 1:])
46+
# * (jnp.sin(np.pi * x[..., 0:1]) - np.pi ** 2 * jnp.sin(np.pi * x[..., 0:1]))
47+
# )
48+
# Backend paddle
49+
# return (
50+
# dy_t
51+
# - dy_xx
52+
# + paddle.exp(-x[:, 1:])
53+
# * (paddle.sin(np.pi * x[:, 0:1]) - np.pi ** 2 * paddle.sin(np.pi * x[:, 0:1]))
54+
# )
55+
56+
57+
def func(x):
58+
return np.sin(np.pi * x[:, 0:1]) * np.exp(-x[:, 1:])
59+
60+
61+
geom = dde.geometry.Interval(-1, 1)
62+
timedomain = dde.geometry.TimeDomain(0, 1)
63+
geomtime = dde.geometry.GeometryXTime(geom, timedomain)
64+
65+
bc = dde.icbc.DirichletBC(geomtime, func, lambda _, on_boundary: on_boundary)
66+
ic = dde.icbc.IC(geomtime, func, lambda _, on_initial: on_initial)
67+
data = dde.data.TimePDE(
68+
geomtime,
69+
pde,
70+
[bc, ic],
71+
num_domain=40,
72+
num_boundary=20,
73+
num_initial=10,
74+
solution=func,
75+
num_test=10000,
76+
)
77+
78+
layer_size = [2] + [32] * 3 + [1]
79+
activation = "tanh"
80+
initializer = "Glorot uniform"
81+
net = dde.nn.FNN(layer_size, activation, initializer)
82+
83+
model = dde.Model(data, net)
84+
85+
model.compile("adam", lr=0.001, metrics=["l2 relative error"])
86+
87+
# Points (x, t) at which the live plot evaluates and shows the predicted
88+
# solution as a color scatter, against the reference `func` for comparison.
89+
x_line = np.linspace(-1, 1, 40)
90+
t_line = np.linspace(0, 1, 40)
91+
x_grid, t_grid = np.meshgrid(x_line, t_line)
92+
x_plot = np.vstack((x_grid.ravel(), t_grid.ravel())).T
93+
94+
monitor = dde.callbacks.TrainingMonitor(
95+
period=200,
96+
x_plot=x_plot,
97+
y_reference=func,
98+
show_loss=True,
99+
)
100+
101+
losshistory, train_state = model.train(iterations=10000, callbacks=[monitor])
102+
103+
dde.saveplot(losshistory, train_state, issave=True, isplot=True)

tests/test_callbacks.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ def _make_monitor(period=3, show_loss=True):
4444
)
4545

4646

47+
def _make_2d_monitor(period=3, y_reference=True, show_loss=True):
48+
x_plot = np.random.rand(20, 2)
49+
y_reference = (lambda x: x[:, 0:1] + x[:, 1:2]) if y_reference else None
50+
return TrainingMonitor(
51+
period=period, x_plot=x_plot, y_reference=y_reference, show_loss=show_loss
52+
)
53+
54+
4755
def test_training_monitor_requires_x_plot():
4856
try:
4957
TrainingMonitor()
@@ -101,5 +109,62 @@ def spy():
101109
assert monitor.enabled is True
102110

103111

112+
def test_training_monitor_rejects_invalid_x_plot_dim():
113+
x_plot = np.random.rand(10, 3)
114+
try:
115+
TrainingMonitor(x_plot=x_plot)
116+
except ValueError:
117+
pass
118+
else:
119+
raise AssertionError("`x_plot` with 3 columns should raise ValueError")
120+
121+
122+
def test_training_monitor_2d_triggers_and_draws():
123+
# Space-time PDEs (e.g. diffusion_1d.py) evaluate the solution over
124+
# (x, t) pairs, so `x_plot` has 2 columns instead of 1. This exercises
125+
# the scatter/colorbar drawing path used in that case.
126+
period = 3
127+
monitor = _make_2d_monitor(period=period)
128+
monitor._has_display = lambda matplotlib_module: True
129+
130+
model = _FakeModel()
131+
monitor.set_model(model)
132+
monitor.on_train_begin()
133+
assert monitor.enabled is True
134+
assert monitor.dim == 2
135+
assert monitor.ax_ref is not None
136+
137+
calls = []
138+
original_update_plot = monitor._update_plot
139+
140+
def spy():
141+
calls.append(model.train_state.iteration)
142+
original_update_plot()
143+
144+
monitor._update_plot = spy
145+
146+
num_epochs = 10
147+
for _ in range(num_epochs):
148+
model.train_state.iteration += 1
149+
monitor.on_epoch_end()
150+
151+
assert calls == [period, 2 * period, 3 * period]
152+
assert monitor.enabled is True
153+
154+
155+
def test_training_monitor_2d_without_reference():
156+
monitor = _make_2d_monitor(period=1, y_reference=False)
157+
monitor._has_display = lambda matplotlib_module: True
158+
159+
model = _FakeModel()
160+
monitor.set_model(model)
161+
monitor.on_train_begin()
162+
assert monitor.ax_ref is None
163+
164+
model.train_state.iteration += 1
165+
monitor.on_epoch_end()
166+
assert monitor.enabled is True
167+
168+
104169
def test_training_monitor_accessible_from_dde_callbacks():
105170
assert dde.callbacks.TrainingMonitor is TrainingMonitor

0 commit comments

Comments
 (0)