Skip to content

Commit 4ba87e1

Browse files
committed
refactor; add domain
1 parent 6e6ad88 commit 4ba87e1

File tree

1 file changed

+60
-34
lines changed

1 file changed

+60
-34
lines changed

pydens/model_torch.py

Lines changed: 60 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,34 @@
1616

1717
class TorchModel(ABC, nn.Module):
1818
""" Pytorch model for solving differential equations with neural networks. """
19-
def __init__(self, initial_condition=None, boundary_condition=None, ndims=1, nparams=0, **kwargs):
19+
def __init__(self, ndims, initial_condition=None, boundary_condition=None, domain=(0, 1), nparams=0, **kwargs):
2020
_ = kwargs
2121
super().__init__()
2222

2323
# Store the number of variables and parameters - dimensionality of the problem.
2424
self.ndims = ndims
25+
self.ndims_spatial = ndims if initial_condition is None else ndims - 1
2526
self.nparams = nparams
2627
self.total = ndims + nparams
2728
self.variables = {}
2829

29-
# Parse and store initial and boundary condition.
30+
# Parse and store initial condition, boundary condition and domain of the problem.
3031
if initial_condition is None:
3132
self.initial_condition = None
3233
else:
3334
self.initial_condition = (initial_condition if callable(initial_condition)
3435
else lambda *args: torch.tensor(initial_condition, dtype=torch.float32))
3536
self.boundary_condition = boundary_condition
37+
if isinstance(domain, (tuple, list)):
38+
if isinstance(domain[0], (float, int)):
39+
domain = [domain] * ndims
40+
elif isinstance(domain[0], (tuple, list)):
41+
pass
42+
else:
43+
raise ValueError(f'Should be either 1d or 2d-sequence of float/ints.')
44+
else:
45+
raise ValueError(f'Should be either 1d or 2d-sequence of float/ints.')
46+
self.domain = domain
3647

3748
# Initialize trainable variables for anzatc-trasform to bind initial
3849
# and boundary conditions.
@@ -96,18 +107,23 @@ def unfreeze_trainable(self, layers=None, variables=None):
96107
def anzatc(self, u, xs):
97108
""" Anzatc-transformation of the model-output needed for binding initial and boundary conditions. """
98109
# Get tensor of spatial variables and time-tensor.
99-
xs_spatial = xs[:, :self.ndims] if self.initial_condition is None else xs[:, :self.ndims - 1]
110+
xs_spatial = xs[:, :self.ndims_spatial]
100111
t = xs[:, self.ndims - 1:self.ndims]
112+
lower, upper = [lims[0] for lims in self.domain], [lims[1] for lims in self.domain]
113+
lower_spatial, upper_spatial = [torch.Tensor(lst[:self.ndims_spatial]).reshape(1, -1).float()
114+
for lst in (lower, upper)]
115+
t0 = lower[-1]
101116

102117
# Apply transformation to bind the boundary condition.
103118
if self.boundary_condition is not None:
104-
u = u * (torch.prod(xs_spatial, dim=1, keepdim=True) *
105-
torch.prod((1 - xs_spatial), dim=1, keepdim=True)) + self.boundary_condition
119+
u = (u * (torch.prod((xs_spatial - lower_spatial) / (upper_spatial - lower_spatial), dim=1, keepdim=True) *
120+
torch.prod((upper_spatial - xs_spatial) / (upper_spatial - lower_spatial), dim=1, keepdim=True))
121+
+ self.boundary_condition)
106122

107123
# Apply transformation to bind the initial condition.
108124
if self.initial_condition is not None:
109125
_xs_spatial = [xs_spatial[:, i] for i in range(xs_spatial.shape[1])]
110-
u = ((nn.Sigmoid()(t / torch.exp(self.log_scale)) - .5) * u
126+
u = ((nn.Sigmoid()((t - t0) / torch.exp(self.log_scale)) - .5) * u
111127
+ self.initial_condition(*_xs_spatial).view(-1, 1))
112128
return u
113129

@@ -119,6 +135,8 @@ class ConvBlockModel(TorchModel):
119135
neural networks. For purposes of solving simple differential equations we suggest using
120136
fully connected networks with skip connections.
121137
138+
See also docstring of `Solver`.
139+
122140
Parameters
123141
----------
124142
layout : str
@@ -137,12 +155,12 @@ class ConvBlockModel(TorchModel):
137155
- ``layout, units, activation = 'fa fa f', [5, 10, 1], 'Sigmoid' # Fully-conn with 2 hidden``
138156
- ``layout, units, activation = 'faR fa fa+ f', [5, 10, 5, 1], 'Sigmoid # Fully-conn with 3 hidden and skip'``
139157
"""
140-
def __init__(self, layout='fafaf', units=(20, 30, 1), activation='Sigmoid', **kwargs):
141-
super().__init__(**kwargs)
158+
def __init__(self, ndims, initial_condition=None, boundary_condition=None, domain=(0, 1), nparams=0,
159+
layout='fafaf', units=(20, 30, 1), activation='Sigmoid', **kwargs):
160+
super().__init__(ndims=ndims, initial_condition=initial_condition, boundary_condition=boundary_condition,
161+
domain=domain, nparams=nparams, **kwargs)
142162

143163
# Prepare kwargs for conv-block.
144-
for key in ['initial_condition', 'ndims', 'nparams', 'boundary_condition']:
145-
_ = kwargs.pop(key, None)
146164
kwargs.update(layout=layout, units=list(units), activation=activation)
147165

148166
# Assemble conv-block.
@@ -207,6 +225,34 @@ def pde(f, x, e):
207225
So, the value of `nparams`-parameter should be set to 1, whie the value of
208226
`ndims` - to 2.
209227
228+
ndims : int
229+
The dimensionality of the problem. Equals the number of variables. For instance,
230+
when the problem includes `x`, `y` and `t`, the dimensionality is equal to 3.
231+
232+
initial_condition : callable or float
233+
Defines initial condition for a problem that includes time-variable.
234+
235+
Examples:
236+
237+
- ``lambda x: x * (1 - x)``
238+
Can define initial condition for a wave equation for a pertubed string with `x` and `t`
239+
- variables. The initial condition is basically a perturbation of a string at `t=0`.
240+
- ``lambda x, y: 10 * x * y * (1 - x) * (1 - y)``
241+
Can define initial condition for a heat equation describing temperature evolution of a
242+
2d plate.
243+
244+
boundary_condition : float
245+
Defines boundary condition for the problem.
246+
247+
domain : tuple or list
248+
Configures rectangular domain of the problem. Can be either a 2d-sequence of form
249+
`[(x_low, x_high), (y_low, y_high), ..., (t_low, t_high)]` of `length=ndims`; or
250+
a 1d-sequence `(low, high)`. In this case, limits for all dimensions are considered
251+
to be the same.
252+
253+
nparams : int
254+
The number of parameters with uncertainty in the model.
255+
210256
model : class
211257
Class inheriting `TorchModel`. The default value is `ConvBlockModel`. The class allows
212258
to implement fully connected/convolutional architectures with multiple branches
@@ -229,28 +275,6 @@ def pde(f, x, e):
229275
This set of additional constraints can be used to (i) bind `f(0.5) = -1`
230276
along with minimization of a L2-norm of `f`.
231277
232-
ndims : int
233-
The dimensionality of the problem. Equals the number of variables. For instance,
234-
when the problem includes `x`, `y` and `t`, the dimensionality is equal to 3.
235-
236-
nparams : int
237-
The number of parameters with uncertainty in the model.
238-
239-
initial_condition : callable or float
240-
Defines initial condition for a problem that includes time-variable.
241-
242-
Examples:
243-
244-
- ``lambda x: x * (1 - x)``
245-
Can define initial condition for a wave equation for a pertubed string with `x` and `t`
246-
- variables. The initial condition is basically a perturbation of a string at t=0.
247-
- ``lambda x, y: 10 * x * y * (1 - x) * (1 - y)``
248-
Can define initial condition for a heat equation describing temperature evolution of a
249-
2d plate.
250-
251-
boundary_condition : float
252-
Defines boundary condition for the problem.
253-
254278
kwargs : dict
255279
Keyword-arguments used for initialization of the model-instance. When `model`-parameter
256280
is set to its default value - `ConvBlockModel`, use these arguments to configure the
@@ -272,7 +296,8 @@ def pde(f, x, e):
272296
- ``layout, units, activation = 'fa fa f', [5, 10, 1], 'Sigmoid'``
273297
- ``layout, units, activation = 'faR fa fa+ f', [5, 10, 5, 1], 'Sigmoid'``
274298
"""
275-
def __init__(self, equation, model=ConvBlockModel, constraints=None, **kwargs):
299+
def __init__(self, equation, ndims, initial_condition=None, boundary_condition=None, domain=(0, 1),
300+
nparams=0, model=ConvBlockModel, constraints=None, **kwargs):
276301
self.equation = equation
277302
if constraints is None:
278303
self.constraints = ()
@@ -284,7 +309,8 @@ def __init__(self, equation, model=ConvBlockModel, constraints=None, **kwargs):
284309
self.optimizer = None
285310

286311
# Initialize neural network for solving the equation.
287-
self.model = model(**kwargs)
312+
self.model = model(**kwargs, ndims=ndims, initial_condition=initial_condition,
313+
boundary_condition=boundary_condition, domain=domain, nparams=nparams)
288314

289315
# Bind created model to a global context variable.
290316
current_model.set(self.model)

0 commit comments

Comments
 (0)