1616
1717class 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