Simulai dense

simulai.regression#

Dense#

Linear#

Bases: NetworkTemplate

Source code in simulai/regression/_pytorch/_dense.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
class Linear(NetworkTemplate):
    name = "linear"
    engine = "torch"

    def __init__(
        self,
        input_size: int = None,
        output_size: int = None,
        bias: bool = True,
        name: str = None,
    ) -> None:
        """Linear operator F(u) = Au + b

        Args:
            input_size (int, optional): Dimension of the input. (Default value = None)
            output_size (int, optional): Dimension of the output. (Default value = None)
            bias (bool, optional): Using bias tensor or not. (Default value = True)
            name (str, optional): A name for identifying the model. (Default value = None)

        """

        super(Linear, self).__init__(name=name)

        self.input_size = input_size
        self.output_size = output_size

        self.activations_str = None

        self.layers = [torch.nn.Linear(input_size, output_size, bias=bias)]

        self.add_module(self.name + "_" + "linear_op", self.layers[0])

        self.weights = [item.weight for item in self.layers]

        self.bias = [item.bias for item in self.layers]

        self.name = name

    @as_tensor
    def forward(
        self, input_data: Union[torch.Tensor, np.ndarray] = None
    ) -> torch.Tensor:
        """Applying the operator Linear.

        Args:
            input_data (Union[torch.Tensor, np.ndarray], optional): Data to be processed using Linear. (Default value = None)

        """

        return self.layers[0](input_data)

    def to_numpy(self):
        """It converts the tensors in Linear to numpy.ndarray."""

        return LinearNumpy(layer=self.layers[0], name=self.name)

__init__(input_size=None, output_size=None, bias=True, name=None) #

Linear operator F(u) = Au + b

Parameters:

Name Type Description Default
input_size int

Dimension of the input. (Default value = None)

None
output_size int

Dimension of the output. (Default value = None)

None
bias bool

Using bias tensor or not. (Default value = True)

True
name str

A name for identifying the model. (Default value = None)

None
Source code in simulai/regression/_pytorch/_dense.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
def __init__(
    self,
    input_size: int = None,
    output_size: int = None,
    bias: bool = True,
    name: str = None,
) -> None:
    """Linear operator F(u) = Au + b

    Args:
        input_size (int, optional): Dimension of the input. (Default value = None)
        output_size (int, optional): Dimension of the output. (Default value = None)
        bias (bool, optional): Using bias tensor or not. (Default value = True)
        name (str, optional): A name for identifying the model. (Default value = None)

    """

    super(Linear, self).__init__(name=name)

    self.input_size = input_size
    self.output_size = output_size

    self.activations_str = None

    self.layers = [torch.nn.Linear(input_size, output_size, bias=bias)]

    self.add_module(self.name + "_" + "linear_op", self.layers[0])

    self.weights = [item.weight for item in self.layers]

    self.bias = [item.bias for item in self.layers]

    self.name = name

forward(input_data=None) #

Applying the operator Linear.

Parameters:

Name Type Description Default
input_data Union[Tensor, ndarray]

Data to be processed using Linear. (Default value = None)

None
Source code in simulai/regression/_pytorch/_dense.py
65
66
67
68
69
70
71
72
73
74
75
76
@as_tensor
def forward(
    self, input_data: Union[torch.Tensor, np.ndarray] = None
) -> torch.Tensor:
    """Applying the operator Linear.

    Args:
        input_data (Union[torch.Tensor, np.ndarray], optional): Data to be processed using Linear. (Default value = None)

    """

    return self.layers[0](input_data)

to_numpy() #

It converts the tensors in Linear to numpy.ndarray.

Source code in simulai/regression/_pytorch/_dense.py
78
79
80
81
def to_numpy(self):
    """It converts the tensors in Linear to numpy.ndarray."""

    return LinearNumpy(layer=self.layers[0], name=self.name)

SLFNN#

Bases: Linear

Source code in simulai/regression/_pytorch/_dense.py
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
class SLFNN(Linear):
    def __init__(
        self,
        input_size: int = None,
        output_size: int = None,
        bias: bool = True,
        name: str = None,
        activation: str = "tanh",
    ) -> None:
        """Single layer fully-connected (dense) neural network

        Args:
            input_size (int, optional): Dimension of the input. (Default value = None)
            output_size (int, optional): Dimension of the output. (Default value = None)
            bias (bool, optional): Using bias tensor or not. (Default value = True)
            name (str, optional): A name for identifying the model. (Default value = None)
            activation (str, optional): Activation function. (Default value = "tanh")

        """

        super(SLFNN, self).__init__(
            input_size=input_size, output_size=output_size, bias=bias, name=name
        )

        self.activation = self._get_operation(operation=activation)

    def forward(
        self, input_data: Union[torch.Tensor, np.ndarray] = None
    ) -> torch.Tensor:
        """Applying the operator Linear.

        Args:
            input_data (Union[torch.Tensor, np.ndarray], optional): Data to be processed using SLFNN. (Default value = None)

        """

        return self.activation(super().forward(input_data=input_data))

__init__(input_size=None, output_size=None, bias=True, name=None, activation='tanh') #

Single layer fully-connected (dense) neural network

Parameters:

Name Type Description Default
input_size int

Dimension of the input. (Default value = None)

None
output_size int

Dimension of the output. (Default value = None)

None
bias bool

Using bias tensor or not. (Default value = True)

True
name str

A name for identifying the model. (Default value = None)

None
activation str

Activation function. (Default value = "tanh")

'tanh'
Source code in simulai/regression/_pytorch/_dense.py
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
def __init__(
    self,
    input_size: int = None,
    output_size: int = None,
    bias: bool = True,
    name: str = None,
    activation: str = "tanh",
) -> None:
    """Single layer fully-connected (dense) neural network

    Args:
        input_size (int, optional): Dimension of the input. (Default value = None)
        output_size (int, optional): Dimension of the output. (Default value = None)
        bias (bool, optional): Using bias tensor or not. (Default value = True)
        name (str, optional): A name for identifying the model. (Default value = None)
        activation (str, optional): Activation function. (Default value = "tanh")

    """

    super(SLFNN, self).__init__(
        input_size=input_size, output_size=output_size, bias=bias, name=name
    )

    self.activation = self._get_operation(operation=activation)

forward(input_data=None) #

Applying the operator Linear.

Parameters:

Name Type Description Default
input_data Union[Tensor, ndarray]

Data to be processed using SLFNN. (Default value = None)

None
Source code in simulai/regression/_pytorch/_dense.py
110
111
112
113
114
115
116
117
118
119
120
def forward(
    self, input_data: Union[torch.Tensor, np.ndarray] = None
) -> torch.Tensor:
    """Applying the operator Linear.

    Args:
        input_data (Union[torch.Tensor, np.ndarray], optional): Data to be processed using SLFNN. (Default value = None)

    """

    return self.activation(super().forward(input_data=input_data))

ShallowNetwork#

Bases: SLFNN

Source code in simulai/regression/_pytorch/_dense.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
class ShallowNetwork(SLFNN):
    def __init__(
        self,
        input_size: int = None,
        hidden_size: int = None,
        output_size: int = None,
        bias: bool = True,
        name: str = None,
        activation: str = "tanh",
    ) -> None:
        """ELM-like (Extreme Learning Machine) shallow network

        Args:
            input_size (int, optional): Dimension of the input. (Default value = None)
            hidden_size (int, optional): Dimension of the hidden (intermediary) state. (Default value = None)
            output_size (int, optional): Dimension of the output. (Default value = None)
            bias (bool, optional): Using bias or not for the last layer. (Default value = True)
            name (str, optional): A name for identifying the model. (Default value = None)
            activation (str, optional): Activation function. (Default value = "tanh")

        """

        super(ShallowNetwork, self).__init__(
            input_size=input_size, output_size=hidden_size, bias=bias, name=name
        )

        self.output_layer = Linear(
            input_size=hidden_size, output_size=output_size, bias=False, name="output"
        )

        self.output_size = output_size

    def forward(
        self, input_data: Union[torch.Tensor, np.ndarray] = None
    ) -> torch.Tensor:
        """

        Args:
            input_data (Union[torch.Tensor, np.ndarray], optional):  (Default value = None)

        """

        hidden_state = self.activation(super().forward(input_data=input_data))

        return self.output_layer.forward(input_data=hidden_state)

__init__(input_size=None, hidden_size=None, output_size=None, bias=True, name=None, activation='tanh') #

ELM-like (Extreme Learning Machine) shallow network

Parameters:

Name Type Description Default
input_size int

Dimension of the input. (Default value = None)

None
hidden_size int

Dimension of the hidden (intermediary) state. (Default value = None)

None
output_size int

Dimension of the output. (Default value = None)

None
bias bool

Using bias or not for the last layer. (Default value = True)

True
name str

A name for identifying the model. (Default value = None)

None
activation str

Activation function. (Default value = "tanh")

'tanh'
Source code in simulai/regression/_pytorch/_dense.py
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
def __init__(
    self,
    input_size: int = None,
    hidden_size: int = None,
    output_size: int = None,
    bias: bool = True,
    name: str = None,
    activation: str = "tanh",
) -> None:
    """ELM-like (Extreme Learning Machine) shallow network

    Args:
        input_size (int, optional): Dimension of the input. (Default value = None)
        hidden_size (int, optional): Dimension of the hidden (intermediary) state. (Default value = None)
        output_size (int, optional): Dimension of the output. (Default value = None)
        bias (bool, optional): Using bias or not for the last layer. (Default value = True)
        name (str, optional): A name for identifying the model. (Default value = None)
        activation (str, optional): Activation function. (Default value = "tanh")

    """

    super(ShallowNetwork, self).__init__(
        input_size=input_size, output_size=hidden_size, bias=bias, name=name
    )

    self.output_layer = Linear(
        input_size=hidden_size, output_size=output_size, bias=False, name="output"
    )

    self.output_size = output_size

forward(input_data=None) #

Parameters:

Name Type Description Default
input_data Union[Tensor, ndarray]

(Default value = None)

None
Source code in simulai/regression/_pytorch/_dense.py
155
156
157
158
159
160
161
162
163
164
165
166
167
def forward(
    self, input_data: Union[torch.Tensor, np.ndarray] = None
) -> torch.Tensor:
    """

    Args:
        input_data (Union[torch.Tensor, np.ndarray], optional):  (Default value = None)

    """

    hidden_state = self.activation(super().forward(input_data=input_data))

    return self.output_layer.forward(input_data=hidden_state)

DenseNetwork#

Bases: NetworkTemplate

Source code in simulai/regression/_pytorch/_dense.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
class DenseNetwork(NetworkTemplate):
    name = "dense"
    engine = "torch"

    def __init__(
        self,
        layers_units: list = None,
        activations: Union[list, str] = None,
        input_size: int = None,
        output_size: int = None,
        normalization: str = "bypass",
        name: str = "",
        last_bias: bool = True,
        last_activation: str = "identity",
        **kwargs,
    ) -> None:
        """Dense (fully-connected) neural network written in PyTorch

        Args:
            layers_units (list, optional): List with the number of neurons for each layer. (Default value = None)
            activations (Union[list, str], optional): List of activations for each layer or a single string
            informing the activation used for all of them. (Default value = None)
            input_size (int, optional): Dimension of the input. (Default value = None)
            output_size (int, optional): Dimension of the output. (Default value = None)
            normalization (str, optional): Kind of normalization used between two layers. (Default value = "bypass")
            name (str, optional): A name for identifying the model. (Default value = "")
            last_bias (bool, optional): Using bias in the last layer or not. (Default value = True)
            last_activation (str, optional): Activation for the last layer (default is 'identity').
            **kwargs

        """

        super(DenseNetwork, self).__init__(**kwargs)

        assert layers_units, "Please, set a list of units for each layer"

        assert activations, (
            "Please, set a list of activation functions" "or a string for all of them."
        )

        # These activations support gain evaluation for the initial state
        self.gain_supported_activations = ["sigmoid", "tanh", "relu", "leaky_relu"]

        # Default attributes
        self.layers_units = layers_units
        self.input_size = input_size
        self.output_size = output_size
        self.normalization = normalization
        self.name = name
        self.last_bias = last_bias

        # For extra and not ever required parameters
        for k, v in kwargs.items():
            setattr(self, k, v)

        # Getting up parameters from host
        self._get_from_guest(activation=activations)

        self.weights = list()

        # The total number of layers includes the output layer
        self.n_layers = len(self.layers_units) + 1

        self.default_last_activation = last_activation

        self.activations, self.activations_str = self._setup_activations(
            activation=activations
        )

        self.initializations = [
            self._determine_initialization(activation)
            for activation in self.activations_str
        ]

        self.layers = self._setup_hidden_layers(last_bias=last_bias)

        array_layers = self._numpy_layers()
        n_layers = len(self.layers)

        self.shapes = [item.shape for item in list(sum(array_layers, []))]
        self.shapes_layers = [[item[0].shape, item[1].shape] for item in array_layers]
        self.stitch_idx = self._make_stitch_idx()

        self.layers_map = [[ll, ll + 1] for ll in range(0, 2 * n_layers, 2)]

    def _calculate_gain(self, activation: str = "Tanh") -> float:
        """It evaluates a multiplier coefficient, named as `gain`,
        which is used to enhance the funcionality of each kind of activation
        function.

        Args:
            activation (str, optional):  (Default value = "Tanh")

        """

        if type(activation) is not str:
            assert hasattr(
                activation, "name"
            ), f"Activation object {type(activation)} must have attribute ´name´."
            name = getattr(activation, "name")
        else:
            name = activation

        if name.lower() in self.gain_supported_activations:
            return torch.nn.init.calculate_gain(name.lower())
        else:
            return 1

    @staticmethod
    def _determine_initialization(activation: str = "Tanh") -> str:
        """It determines the most proper initialization method for each
        activation function.

        Args:
            activation (str, optional): Activation function. (Default value = "Tanh")

        """

        if type(activation) is not str:
            assert hasattr(
                activation, "name"
            ), f"Activation object {type(activation)} must have attribute ´name´."
            name = getattr(activation, "name")
        else:
            name = activation

        if name in ["ReLU"]:
            return "kaiming"
        elif name == "Siren":
            return "siren"
        else:
            return "xavier"

    def _setup_layer(
        self,
        input_size: int = 0,
        output_size: int = 0,
        initialization: str = None,
        bias: bool = True,
        first_layer: bool = False,
    ) -> torch.nn.Linear:
        """

        Args:
            input_size (int, optional): Dimension of the input. (Default value = 0)
            output_size (int, optional): Dimension of the output. (Default value = 0)
            initialization (str, optional): Initialization method. (Default value = None)
            bias (bool, optional): Using bias tensor or not. (Default value = True)
            first_layer (bool, optional): Is this layer the first layer or not. (Default value = False)

        """

        # It instantiates a linear operation
        # f: y^l = f(x^(l-1)) = (W^l).dot(x^(l-1)) + b^l
        layer = torch.nn.Linear(input_size, output_size, bias=bias)

        if initialization == "xavier":
            torch.nn.init.xavier_normal_(
                layer.weight, gain=self._calculate_gain(self.activations_str[0])
            )
            return layer

        # The Siren initialization requires some special consideration
        elif initialization == "siren":
            assert (
                self.c is not None
            ), "When using siren, the parameter c must be defined."
            assert (
                self.omega_0 is not None
            ), "When using siren, the parameter omega_0 must be defined."

            if first_layer == True:
                m = 1 / input_size
            else:
                m = np.sqrt(self.c / input_size) / self.omega_0

            torch.nn.init.trunc_normal_(layer.weight, a=-m, b=m)
            b = np.sqrt(1 / input_size)
            torch.nn.init.trunc_normal_(layer.bias, a=-b, b=b)
            return layer

        elif initialization == "kaiming":
            return layer  # Kaiming is the default initialization in PyTorch

        else:
            print(
                "Initialization method still not implemented.\
                  Using Kaiming instead"
            )

            return layer

    # The forward step of the network
    @as_tensor
    def forward(
        self, input_data: Union[torch.Tensor, np.ndarray] = None
    ) -> torch.Tensor:
        """It executes the forward step for the DenseNetwork.

        Args:
            input_data (Union[torch.Tensor, np.ndarray], optional): The input tensor to be processed by DenseNetwork. (Default value = None)

        """

        input_tensor_ = input_data

        # TODO It can be done using the PyTorch Sequential object
        for layer_id in range(len(self.layers)):
            output_tensor_ = self.layers[layer_id](input_tensor_)
            _output_tensor_ = self.activations[layer_id](output_tensor_)
            input_tensor_ = _output_tensor_

        output_tensor = input_tensor_

        return output_tensor

__init__(layers_units=None, activations=None, input_size=None, output_size=None, normalization='bypass', name='', last_bias=True, last_activation='identity', **kwargs) #

Dense (fully-connected) neural network written in PyTorch

Parameters:

Name Type Description Default
layers_units list

List with the number of neurons for each layer. (Default value = None)

None
activations Union[list, str]

List of activations for each layer or a single string

None
input_size int

Dimension of the input. (Default value = None)

None
output_size int

Dimension of the output. (Default value = None)

None
normalization str

Kind of normalization used between two layers. (Default value = "bypass")

'bypass'
name str

A name for identifying the model. (Default value = "")

''
last_bias bool

Using bias in the last layer or not. (Default value = True)

True
last_activation str

Activation for the last layer (default is 'identity').

'identity'
Source code in simulai/regression/_pytorch/_dense.py
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
def __init__(
    self,
    layers_units: list = None,
    activations: Union[list, str] = None,
    input_size: int = None,
    output_size: int = None,
    normalization: str = "bypass",
    name: str = "",
    last_bias: bool = True,
    last_activation: str = "identity",
    **kwargs,
) -> None:
    """Dense (fully-connected) neural network written in PyTorch

    Args:
        layers_units (list, optional): List with the number of neurons for each layer. (Default value = None)
        activations (Union[list, str], optional): List of activations for each layer or a single string
        informing the activation used for all of them. (Default value = None)
        input_size (int, optional): Dimension of the input. (Default value = None)
        output_size (int, optional): Dimension of the output. (Default value = None)
        normalization (str, optional): Kind of normalization used between two layers. (Default value = "bypass")
        name (str, optional): A name for identifying the model. (Default value = "")
        last_bias (bool, optional): Using bias in the last layer or not. (Default value = True)
        last_activation (str, optional): Activation for the last layer (default is 'identity').
        **kwargs

    """

    super(DenseNetwork, self).__init__(**kwargs)

    assert layers_units, "Please, set a list of units for each layer"

    assert activations, (
        "Please, set a list of activation functions" "or a string for all of them."
    )

    # These activations support gain evaluation for the initial state
    self.gain_supported_activations = ["sigmoid", "tanh", "relu", "leaky_relu"]

    # Default attributes
    self.layers_units = layers_units
    self.input_size = input_size
    self.output_size = output_size
    self.normalization = normalization
    self.name = name
    self.last_bias = last_bias

    # For extra and not ever required parameters
    for k, v in kwargs.items():
        setattr(self, k, v)

    # Getting up parameters from host
    self._get_from_guest(activation=activations)

    self.weights = list()

    # The total number of layers includes the output layer
    self.n_layers = len(self.layers_units) + 1

    self.default_last_activation = last_activation

    self.activations, self.activations_str = self._setup_activations(
        activation=activations
    )

    self.initializations = [
        self._determine_initialization(activation)
        for activation in self.activations_str
    ]

    self.layers = self._setup_hidden_layers(last_bias=last_bias)

    array_layers = self._numpy_layers()
    n_layers = len(self.layers)

    self.shapes = [item.shape for item in list(sum(array_layers, []))]
    self.shapes_layers = [[item[0].shape, item[1].shape] for item in array_layers]
    self.stitch_idx = self._make_stitch_idx()

    self.layers_map = [[ll, ll + 1] for ll in range(0, 2 * n_layers, 2)]

forward(input_data=None) #

It executes the forward step for the DenseNetwork.

Parameters:

Name Type Description Default
input_data Union[Tensor, ndarray]

The input tensor to be processed by DenseNetwork. (Default value = None)

None
Source code in simulai/regression/_pytorch/_dense.py
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
@as_tensor
def forward(
    self, input_data: Union[torch.Tensor, np.ndarray] = None
) -> torch.Tensor:
    """It executes the forward step for the DenseNetwork.

    Args:
        input_data (Union[torch.Tensor, np.ndarray], optional): The input tensor to be processed by DenseNetwork. (Default value = None)

    """

    input_tensor_ = input_data

    # TODO It can be done using the PyTorch Sequential object
    for layer_id in range(len(self.layers)):
        output_tensor_ = self.layers[layer_id](input_tensor_)
        _output_tensor_ = self.activations[layer_id](output_tensor_)
        input_tensor_ = _output_tensor_

    output_tensor = input_tensor_

    return output_tensor

ResDenseNetwork#

Bases: DenseNetwork

Source code in simulai/regression/_pytorch/_dense.py
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
class ResDenseNetwork(DenseNetwork):
    name = "residualdense"
    engine = "torch"

    def __init__(
        self,
        layers_units: list = None,
        activations: Union[list, str] = None,
        input_size: int = None,
        output_size: int = None,
        normalization: str = "bypass",
        name: str = "",
        last_bias: bool = True,
        last_activation: str = "identity",
        residual_size: int = 1,
        **kwargs,
    ) -> None:
        """Residual Dense (fully-connected) neural network written in PyTorch

        Args:
            layers_units (list, optional): List with the number of neurons for each layer. (Default value = None)
            activations (Union[list, str], optional): List of activations for each layer or a single string
            informing the activation used for all of them. (Default value = None)
            input_size (int, optional): Dimension of the input. (Default value = None)
            output_size (int, optional): Dimension of the output. (Default value = None)
            normalization (str, optional): Kind of normalization used between two layers. (Default value = "bypass")
            name (str, optional): A name for identifying the model. (Default value = "")
            last_bias (bool, optional): Using bias in the last layer or not. (Default value = True)
            last_activation (str, optional): Activation for the last layer (default is 'identity').
            residual_size (int, optional): Size of the residual block. (Default value = 1)
            **kwargs

        """

        super().__init__(
            layers_units=layers_units,
            activations=activations,
            input_size=input_size,
            output_size=output_size,
            normalization=normalization,
            name=name,
            last_bias=last_bias,
            last_activation=last_activation,
            **kwargs,
        )

        # Considering the activations layers
        self.residual_size = 2 * residual_size
        self.ratio = 0.5

        # Excluding the input and output layers
        merged_layers = self._merge(layer=self.layers, act=self.activations)

        assert len(merged_layers[2:-2]) % self.residual_size == 0, (
            "The number of layers must be divisible"
            " by the residual block size,"
            f" but received {len(merged_layers)} and {residual_size}"
        )

        self.n_residual_blocks = int(len(merged_layers[2:-2]) / self.residual_size)

        sub_layers = [
            item.tolist()
            for item in np.split(np.array(merged_layers[2:-2]), self.n_residual_blocks)
        ]

        self.input_block = torch.nn.Sequential(*merged_layers[:2])
        self.hidden_blocks = [torch.nn.Sequential(*item) for item in sub_layers]
        self.output_block = torch.nn.Sequential(*merged_layers[-2:])

    # Merging the layers into a reasonable sequence
    def _merge(self, layer: list = None, act: list = None) -> list:
        """It merges the dense layers and the activations into a single block.

        Args:
            layer (list, optional): List of dense layers. (Default value = None)
            act (list, optional): List of activation functions. (Default value = None)

        """

        merged_list = list()

        for i, j in zip(layer, act):
            merged_list.append(i)
            merged_list.append(j)

        return merged_list

    def summary(self):
        """It prints a summary of the network."""

        super().summary()

        print("Residual Blocks:\n")

        print(self.input_block)
        print(self.hidden_blocks)
        print(self.output_block)

    @as_tensor
    def forward(
        self, input_data: Union[torch.Tensor, np.ndarray] = None
    ) -> torch.Tensor:
        """

        Args:
            input_data (Union[torch.Tensor, np.ndarray], optional):  (Default value = None)

        """

        input_tensor_ = input_data

        input_tensor_ = self.input_block(input_tensor_)

        for block in self.hidden_blocks:
            output_tensor_ = self.ratio * (input_tensor_ + block(input_tensor_))

            input_tensor_ = output_tensor_

        output_tensor = self.output_block(input_tensor_)

        return output_tensor

__init__(layers_units=None, activations=None, input_size=None, output_size=None, normalization='bypass', name='', last_bias=True, last_activation='identity', residual_size=1, **kwargs) #

Residual Dense (fully-connected) neural network written in PyTorch

Parameters:

Name Type Description Default
layers_units list

List with the number of neurons for each layer. (Default value = None)

None
activations Union[list, str]

List of activations for each layer or a single string

None
input_size int

Dimension of the input. (Default value = None)

None
output_size int

Dimension of the output. (Default value = None)

None
normalization str

Kind of normalization used between two layers. (Default value = "bypass")

'bypass'
name str

A name for identifying the model. (Default value = "")

''
last_bias bool

Using bias in the last layer or not. (Default value = True)

True
last_activation str

Activation for the last layer (default is 'identity').

'identity'
residual_size int

Size of the residual block. (Default value = 1)

1
Source code in simulai/regression/_pytorch/_dense.py
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
def __init__(
    self,
    layers_units: list = None,
    activations: Union[list, str] = None,
    input_size: int = None,
    output_size: int = None,
    normalization: str = "bypass",
    name: str = "",
    last_bias: bool = True,
    last_activation: str = "identity",
    residual_size: int = 1,
    **kwargs,
) -> None:
    """Residual Dense (fully-connected) neural network written in PyTorch

    Args:
        layers_units (list, optional): List with the number of neurons for each layer. (Default value = None)
        activations (Union[list, str], optional): List of activations for each layer or a single string
        informing the activation used for all of them. (Default value = None)
        input_size (int, optional): Dimension of the input. (Default value = None)
        output_size (int, optional): Dimension of the output. (Default value = None)
        normalization (str, optional): Kind of normalization used between two layers. (Default value = "bypass")
        name (str, optional): A name for identifying the model. (Default value = "")
        last_bias (bool, optional): Using bias in the last layer or not. (Default value = True)
        last_activation (str, optional): Activation for the last layer (default is 'identity').
        residual_size (int, optional): Size of the residual block. (Default value = 1)
        **kwargs

    """

    super().__init__(
        layers_units=layers_units,
        activations=activations,
        input_size=input_size,
        output_size=output_size,
        normalization=normalization,
        name=name,
        last_bias=last_bias,
        last_activation=last_activation,
        **kwargs,
    )

    # Considering the activations layers
    self.residual_size = 2 * residual_size
    self.ratio = 0.5

    # Excluding the input and output layers
    merged_layers = self._merge(layer=self.layers, act=self.activations)

    assert len(merged_layers[2:-2]) % self.residual_size == 0, (
        "The number of layers must be divisible"
        " by the residual block size,"
        f" but received {len(merged_layers)} and {residual_size}"
    )

    self.n_residual_blocks = int(len(merged_layers[2:-2]) / self.residual_size)

    sub_layers = [
        item.tolist()
        for item in np.split(np.array(merged_layers[2:-2]), self.n_residual_blocks)
    ]

    self.input_block = torch.nn.Sequential(*merged_layers[:2])
    self.hidden_blocks = [torch.nn.Sequential(*item) for item in sub_layers]
    self.output_block = torch.nn.Sequential(*merged_layers[-2:])

forward(input_data=None) #

Parameters:

Name Type Description Default
input_data Union[Tensor, ndarray]

(Default value = None)

None
Source code in simulai/regression/_pytorch/_dense.py
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
@as_tensor
def forward(
    self, input_data: Union[torch.Tensor, np.ndarray] = None
) -> torch.Tensor:
    """

    Args:
        input_data (Union[torch.Tensor, np.ndarray], optional):  (Default value = None)

    """

    input_tensor_ = input_data

    input_tensor_ = self.input_block(input_tensor_)

    for block in self.hidden_blocks:
        output_tensor_ = self.ratio * (input_tensor_ + block(input_tensor_))

        input_tensor_ = output_tensor_

    output_tensor = self.output_block(input_tensor_)

    return output_tensor

summary() #

It prints a summary of the network.

Source code in simulai/regression/_pytorch/_dense.py
476
477
478
479
480
481
482
483
484
485
def summary(self):
    """It prints a summary of the network."""

    super().summary()

    print("Residual Blocks:\n")

    print(self.input_block)
    print(self.hidden_blocks)
    print(self.output_block)

ConvexDenseNetwork#

Bases: DenseNetwork

Source code in simulai/regression/_pytorch/_dense.py
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
class ConvexDenseNetwork(DenseNetwork):
    name = "convexdense"
    engine = "torch"

    def __init__(
        self,
        layers_units: list = None,
        activations: Union[list, str] = None,
        input_size: int = None,
        output_size: int = None,
        normalization: str = "bypass",
        name: str = "",
        last_bias: bool = True,
        last_activation: str = "identity",
        **kwargs,
    ) -> None:
        """Dense network with convex combinations in the hidden layers.
        This architecture is useful when combined to the Improved Version of DeepONets

        Args:
            layers_units (list, optional): List with the number of neurons for each layer. (Default value = None)
            activations (Union[list, str], optional): List of activations for each layer or a single string
            informing the activation used for all of them. (Default value = None)
            input_size (int, optional): Dimension of the input. (Default value = None)
            output_size (int, optional): Dimension of the output. (Default value = None)
            normalization (str, optional): Kind of normalization used between two layers. (Default value = "bypass")
            name (str, optional): A name for identifying the model. (Default value = "")
            last_bias (bool, optional): Using bias in the last layer or not. (Default value = True)
            last_activation (str, optional): Activation for the last layer (default is 'identity').
            **kwargs

        """

        self.hidden_size = None
        assert self._check_regular_net(layers_units=layers_units), (
            "All the hidden layers must be equal in" "a Convex Dense Network."
        )

        super().__init__(
            layers_units=layers_units,
            activations=activations,
            input_size=input_size,
            output_size=output_size,
            normalization=normalization,
            name=name,
            last_bias=last_bias,
            last_activation=last_activation,
            **kwargs,
        )

    def _check_regular_net(self, layers_units: list) -> bool:
        """It checks if all the layers has the same number of neurons.

        Args:
            layers_units (list):

        """

        mean = int(sum(layers_units) / len(layers_units))
        self.hidden_size = mean

        if len([True for j in layers_units if j == mean]) == len(layers_units):
            return True
        else:
            return False

    @as_tensor
    def forward(
        self,
        input_data: Union[torch.Tensor, np.ndarray] = None,
        u: Union[torch.Tensor, np.ndarray] = None,
        v: Union[torch.Tensor, np.ndarray] = None,
    ) -> torch.Tensor:
        """

        Args:
            input_data (Union[torch.Tensor, np.ndarray], optional): Input data to be processed using ConvexDenseNetwork. (Default value = None)
            u (Union[torch.Tensor, np.ndarray], optional): Input generated by the first auxiliar encoder (external model). (Default value = None)
            v (Union[torch.Tensor, np.ndarray], optional): Input generated by the second auxiliar encoder (external model). (Default value = None)

        """

        input_tensor_ = input_data

        # The first layer operation has no difference from the Vanilla one
        first_output = self.activations[0](self.layers[0](input_tensor_))

        input_tensor_ = first_output

        layers_hidden = self.layers[1:-1]
        activations_hidden = self.activations[1:-1]

        for layer_id in range(len(layers_hidden)):
            output_tensor_ = layers_hidden[layer_id](input_tensor_)
            z = activations_hidden[layer_id](output_tensor_)
            _output_tensor_ = (1 - z) * u + z * v

            input_tensor_ = _output_tensor_

        # The last layer operation too
        last_output = self.activations[-1](self.layers[-1](input_tensor_))
        output_tensor = last_output

        return output_tensor

__init__(layers_units=None, activations=None, input_size=None, output_size=None, normalization='bypass', name='', last_bias=True, last_activation='identity', **kwargs) #

Dense network with convex combinations in the hidden layers. This architecture is useful when combined to the Improved Version of DeepONets

Parameters:

Name Type Description Default
layers_units list

List with the number of neurons for each layer. (Default value = None)

None
activations Union[list, str]

List of activations for each layer or a single string

None
input_size int

Dimension of the input. (Default value = None)

None
output_size int

Dimension of the output. (Default value = None)

None
normalization str

Kind of normalization used between two layers. (Default value = "bypass")

'bypass'
name str

A name for identifying the model. (Default value = "")

''
last_bias bool

Using bias in the last layer or not. (Default value = True)

True
last_activation str

Activation for the last layer (default is 'identity').

'identity'
Source code in simulai/regression/_pytorch/_dense.py
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
def __init__(
    self,
    layers_units: list = None,
    activations: Union[list, str] = None,
    input_size: int = None,
    output_size: int = None,
    normalization: str = "bypass",
    name: str = "",
    last_bias: bool = True,
    last_activation: str = "identity",
    **kwargs,
) -> None:
    """Dense network with convex combinations in the hidden layers.
    This architecture is useful when combined to the Improved Version of DeepONets

    Args:
        layers_units (list, optional): List with the number of neurons for each layer. (Default value = None)
        activations (Union[list, str], optional): List of activations for each layer or a single string
        informing the activation used for all of them. (Default value = None)
        input_size (int, optional): Dimension of the input. (Default value = None)
        output_size (int, optional): Dimension of the output. (Default value = None)
        normalization (str, optional): Kind of normalization used between two layers. (Default value = "bypass")
        name (str, optional): A name for identifying the model. (Default value = "")
        last_bias (bool, optional): Using bias in the last layer or not. (Default value = True)
        last_activation (str, optional): Activation for the last layer (default is 'identity').
        **kwargs

    """

    self.hidden_size = None
    assert self._check_regular_net(layers_units=layers_units), (
        "All the hidden layers must be equal in" "a Convex Dense Network."
    )

    super().__init__(
        layers_units=layers_units,
        activations=activations,
        input_size=input_size,
        output_size=output_size,
        normalization=normalization,
        name=name,
        last_bias=last_bias,
        last_activation=last_activation,
        **kwargs,
    )

forward(input_data=None, u=None, v=None) #

Parameters:

Name Type Description Default
input_data Union[Tensor, ndarray]

Input data to be processed using ConvexDenseNetwork. (Default value = None)

None
u Union[Tensor, ndarray]

Input generated by the first auxiliar encoder (external model). (Default value = None)

None
v Union[Tensor, ndarray]

Input generated by the second auxiliar encoder (external model). (Default value = None)

None
Source code in simulai/regression/_pytorch/_dense.py
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
@as_tensor
def forward(
    self,
    input_data: Union[torch.Tensor, np.ndarray] = None,
    u: Union[torch.Tensor, np.ndarray] = None,
    v: Union[torch.Tensor, np.ndarray] = None,
) -> torch.Tensor:
    """

    Args:
        input_data (Union[torch.Tensor, np.ndarray], optional): Input data to be processed using ConvexDenseNetwork. (Default value = None)
        u (Union[torch.Tensor, np.ndarray], optional): Input generated by the first auxiliar encoder (external model). (Default value = None)
        v (Union[torch.Tensor, np.ndarray], optional): Input generated by the second auxiliar encoder (external model). (Default value = None)

    """

    input_tensor_ = input_data

    # The first layer operation has no difference from the Vanilla one
    first_output = self.activations[0](self.layers[0](input_tensor_))

    input_tensor_ = first_output

    layers_hidden = self.layers[1:-1]
    activations_hidden = self.activations[1:-1]

    for layer_id in range(len(layers_hidden)):
        output_tensor_ = layers_hidden[layer_id](input_tensor_)
        z = activations_hidden[layer_id](output_tensor_)
        _output_tensor_ = (1 - z) * u + z * v

        input_tensor_ = _output_tensor_

    # The last layer operation too
    last_output = self.activations[-1](self.layers[-1](input_tensor_))
    output_tensor = last_output

    return output_tensor