rlmodule 0.0.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,771 @@
1
+ from typing import Any, Mapping, Optional, Sequence, Tuple, Union
2
+
3
+
4
+ import gym
5
+ import gymnasium
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+
10
+ from enum import Enum
11
+
12
+ # TODO(ll): get independent of skrl
13
+ # also remove skrl from pyproject.toml
14
+ from .model import Model, Shape
15
+ from .deprecated import DeterministicMixin, GaussianMixin # noqa
16
+
17
+ # TODO(ll) consider moving this or part of it into skrl.models.torch
18
+ # TODO(ll) MultivariatGaussian, Categorical, Multicategorical
19
+
20
+
21
+
22
+ # TODO(ll) move to base ... and call from shared as well
23
+ def contains_rnn_module(module: nn.Module, module_types):
24
+ for submodule in module.modules():
25
+ if isinstance(submodule, module_types):
26
+ return True
27
+ return False
28
+
29
+
30
+ class RLModel(Model):
31
+ def __init__(self,
32
+ observation_space: Optional[Union[int, Tuple[int], gym.Space, gymnasium.Space]] = None,
33
+ action_space: Optional[Union[int, Tuple[int], gym.Space, gymnasium.Space]] = None,
34
+ input_shape: Shape = Shape.STATES,
35
+ device: Optional[Union[str, torch.device]] = None,
36
+ # TODO network, if None do pecreptron?
37
+ network: Optional[nn.Module] = None,
38
+ # TODO can be combination of various types ... mixed layer
39
+ # but now it is dict
40
+ output_layer: Optional[nn.Module] = None,
41
+ ):
42
+ #TODO(ll) description
43
+ """Gaussian model
44
+
45
+ :param observation_space: Observation/state space or shape (default: None).
46
+ If it is not None, the num_observations property will contain the size of that space
47
+ :type observation_space: int, tuple or list of integers, gym.Space, gymnasium.Space or None, optional
48
+ :param action_space: Action space or shape (default: None).
49
+ If it is not None, the num_actions property will contain the size of that space
50
+ :type action_space: int, tuple or list of integers, gym.Space, gymnasium.Space or None, optional
51
+ :param input_shape todo(ll)
52
+ :type input_shape
53
+ :param output_shape todo(ll)
54
+ :type output_shape
55
+ :param network todo(ll) description + optional?
56
+ :type network
57
+ :param device: Device on which a tensor/array is or will be allocated (default: ``None``).
58
+ If None, the device will be either ``"cuda"`` if available or ``"cpu"``
59
+ :type device: str or torch.device, optional
60
+ :param clip_actions: Flag to indicate whether the actions should be clipped (default: False)
61
+ :type clip_actions: bool, optional
62
+ :param clip_log_std: Flag to indicate whether the log standard deviations should be clipped (default: True)
63
+ :type clip_log_std: bool, optional
64
+ :param min_log_std: Minimum value of the log standard deviation (default: -20)
65
+ :type min_log_std: float, optional
66
+ :param max_log_std: Maximum value of the log standard deviation (default: 2)
67
+ :type max_log_std: float, optional
68
+ :param initial_log_std: Initial value for the log standard deviation (default: 0)
69
+ :type initial_log_std: float, optional
70
+ :param reduction: Reduction method for returning the log probability density function: (default: ``"sum"``).
71
+ Supported values are ``"mean"``, ``"sum"``, ``"prod"`` and ``"none"``. If "``none"``, the log probability density
72
+ function is returned as a tensor of shape ``(num_samples, num_actions)`` instead of ``(num_samples, 1)``
73
+ :type reduction: str, optional
74
+ """
75
+
76
+ super().__init__(observation_space, action_space, device)
77
+ # GaussianMixin.__init__(self, clip_actions, clip_log_std, min_log_std, max_log_std, reduction)
78
+
79
+ #TODO(ll) other input types ..gym, tuple?
80
+ #TODO(ll) ignore output scale for now because don't like, but may reintroduce
81
+ #TODO(ll) where is activation tanh propagated (what used to be forgotten as a bug)
82
+ # self.instantiator_output_scale = metadata["output_scale"]
83
+
84
+ self.input_shape = input_shape
85
+
86
+ self.net = network
87
+ self.output_layer = output_layer
88
+
89
+ # TODO(ll) maybe other way .. check net for lstm/rnn/gru?
90
+
91
+ self._lstm = contains_rnn_module( self, nn.LSTM)
92
+ self._rnn = contains_rnn_module( self, (nn.LSTM, nn.RNN, nn.GRU))
93
+
94
+
95
+ print("!rnn: ", self._rnn)
96
+ print("!lstm: ", self._lstm)
97
+
98
+
99
+ print("!rnn: ", self._rnn)
100
+ print("!lstm: ", self._lstm)
101
+
102
+ # TODO(ll) one liner:
103
+ # self.net = nn.Sequential(network, self._get_num_units_by_shape(self.output_shape), torch.nn.Tanh())
104
+
105
+ # TODO(ll) output_units = self._get_num_units_by_shape(self.output_shape)
106
+
107
+ # TODO(ll) 512 (last hidden layer) is hardcoded.
108
+ #layers = [nn.Linear(512, self._get_num_units_by_shape(self.output_shape)), torch.nn.Tanh()]
109
+
110
+ # TODO(ll) propagate output activation from outside. Maybe actually propagate function, so it is easily customable and can include scaling.
111
+ # -- it can default to tanh here, and to identity in deterministic
112
+ #
113
+ # if metadata[0]["output_activation"] is not None:
114
+ # layers.append(_get_activation_function(metadata[0]["output_activation"]))
115
+
116
+ #self.mean_net = nn.Sequential(*layers)
117
+
118
+ # self.log_std_parameter = nn.Parameter(
119
+ # initial_log_std * torch.ones(self._get_num_units_by_shape(self.output_shape)))
120
+
121
+ #TODO(ll) only needed for LSTM, RNN, GRU .. PPO_RNN
122
+ #TODO(ll) consider adding it to Base model.
123
+ # Recurrent Neural Network (RNN) specification for RNN, LSTM and GRU layers/cells
124
+ def get_specification(self):
125
+ if self._lstm:
126
+ # batch size (N) is the number of envs
127
+ return {"rnn": {"sequence_length": self.net.sequence_length,
128
+ "sizes": [(self.net.num_layers, self.net.num_envs, self.net.hidden_size), # hidden states (D ∗ num_layers, N, Hout)
129
+ (self.net.num_layers, self.net.num_envs, self.net.hidden_size)]}} # cell states (D ∗ num_layers, N, Hcell)
130
+ elif self._rnn:
131
+ return {"rnn": {"sequence_length": self.net.sequence_length,
132
+ "sizes": [(self.net.num_layers, self.net.num_envs, self.net.hidden_size)]}} # hidden states (D ∗ num_layers, N, Hout)
133
+ else:
134
+ return {}
135
+
136
+ # TODO(ll) RNN & GRU has this simplier specification
137
+ # def get_specification(self):
138
+
139
+ # # batch size (N) is the number of envs
140
+ # return {"rnn": {"sequence_length": self.sequence_length,
141
+ # "sizes": [(self.num_layers, self.num_envs, self.hidden_size)]}} # hidden states (D ∗ num_layers, N, Hout)
142
+
143
+ def get_entropy(self, role: str = "") -> torch.Tensor:
144
+ """Compute and return the entropy of the model
145
+
146
+ :return: Entropy of the model
147
+ :rtype: torch.Tensor
148
+ :param role: Role play by the model (default: ``""``)
149
+ :type role: str, optional
150
+
151
+ Example::
152
+
153
+ >>> entropy = model.get_entropy()
154
+ >>> print(entropy.shape)
155
+ torch.Size([4096, 8])
156
+ """
157
+ return self.output_layer.mixin.get_entropy(role)
158
+
159
+ def distribution(self, role: str = "") -> torch.distributions.Normal:
160
+ """Get the current distribution of the model
161
+
162
+ :return: Distribution of the model
163
+ :rtype: torch.distributions.Normal
164
+ :param role: Role play by the model (default: ``""``)
165
+ :type role: str, optional
166
+
167
+ Example::
168
+
169
+ >>> distribution = model.distribution()
170
+ >>> print(distribution)
171
+ Normal(loc: torch.Size([4096, 8]), scale: torch.Size([4096, 8]))
172
+ """
173
+ return self.output_layer.mixin.distribution(role)
174
+
175
+ def act(self,
176
+ inputs: Mapping[str, Union[torch.Tensor, Any]],
177
+ role: str = "") -> Tuple[torch.Tensor, Union[torch.Tensor, None], Mapping[str, Union[torch.Tensor, Any]]]:
178
+
179
+ # TODO(ll) once shared model is design, you may rethink this to do by call some function pointer or whatever.
180
+ # or consider inheritance GaussianModelRNN
181
+ # TODO(ll) if one of the models is not rnn ... and PPO_RNN is called what happens?
182
+ if self._rnn:
183
+ output, output_dict = self.net(self._get_all_inputs(inputs), inputs.get('terminated', None), inputs['rnn'])
184
+ else:
185
+
186
+ output = self.net(self._get_all_inputs(inputs))
187
+ output_dict = {}
188
+
189
+ # TODO(ll) passing by independetly as together sequential has just one input in forward
190
+ # potentially can be done that output dict would be propagated through mean net (mean net overload forward)
191
+
192
+ return self.output_layer(output, inputs.get('taken_actions', None), output_dict)
193
+
194
+ # TODO(ll)
195
+ # CNN
196
+ # 1) changing shape if it comes in linear fashion of input, check how I was doing this.
197
+ # def compute(self, inputs, role):
198
+ # # permute (samples, width * height * channels) -> (samples, channels, width, height)
199
+ # return self.net(inputs["states"].view(-1, *self.observation_space.shape).permute(0, 3, 1, 2)), self.log_std_parameter, {}
200
+ # 2) what with that weird Shapes? search for taken_actions, who called it with this input. How should CNN be applied to such things..just in states?
201
+
202
+ # TODO(ll) output scale removed .. check that tanh where is
203
+ # return output * self.instantiator_output_scale, self.log_std_parameter, {}
204
+ return output, self.log_std_parameter, output_dict
205
+
206
+ class SharedRLModel(Model):
207
+ def __init__(self,
208
+ observation_space: Optional[Union[int, Tuple[int], gym.Space, gymnasium.Space]] = None,
209
+ action_space: Optional[Union[int, Tuple[int], gym.Space, gymnasium.Space]] = None,
210
+ input_shape: Shape = Shape.STATES, # TODO get rid of this?
211
+ device: Optional[Union[str, torch.device]] = None,
212
+ # TODO network, if None do pecreptron?
213
+ network: Optional[nn.Module] = None,
214
+ # TODO can be combination of various types ... mixed layer
215
+ # but now it is dict
216
+ output_layer: Optional[nn.Module] = None,
217
+ ):
218
+ #TODO(ll) description
219
+ """Gaussian model
220
+
221
+ :param observation_space: Observation/state space or shape (default: None).
222
+ If it is not None, the num_observations property will contain the size of that space
223
+ :type observation_space: int, tuple or list of integers, gym.Space, gymnasium.Space or None, optional
224
+ :param action_space: Action space or shape (default: None).
225
+ If it is not None, the num_actions property will contain the size of that space
226
+ :type action_space: int, tuple or list of integers, gym.Space, gymnasium.Space or None, optional
227
+ :param input_shape todo(ll)
228
+ :type input_shape
229
+ :param output_shape todo(ll)
230
+ :type output_shape
231
+ :param network todo(ll) description + optional?
232
+ :type network
233
+ :param device: Device on which a tensor/array is or will be allocated (default: ``None``).
234
+ If None, the device will be either ``"cuda"`` if available or ``"cpu"``
235
+ :type device: str or torch.device, optional
236
+ :param clip_actions: Flag to indicate whether the actions should be clipped (default: False)
237
+ :type clip_actions: bool, optional
238
+ :param clip_log_std: Flag to indicate whether the log standard deviations should be clipped (default: True)
239
+ :type clip_log_std: bool, optional
240
+ :param min_log_std: Minimum value of the log standard deviation (default: -20)
241
+ :type min_log_std: float, optional
242
+ :param max_log_std: Maximum value of the log standard deviation (default: 2)
243
+ :type max_log_std: float, optional
244
+ :param initial_log_std: Initial value for the log standard deviation (default: 0)
245
+ :type initial_log_std: float, optional
246
+ :param reduction: Reduction method for returning the log probability density function: (default: ``"sum"``).
247
+ Supported values are ``"mean"``, ``"sum"``, ``"prod"`` and ``"none"``. If "``none"``, the log probability density
248
+ function is returned as a tensor of shape ``(num_samples, num_actions)`` instead of ``(num_samples, 1)``
249
+ :type reduction: str, optional
250
+ """
251
+
252
+ super().__init__(observation_space, action_space, device)
253
+ # GaussianMixin.__init__(self, clip_actions, clip_log_std, min_log_std, max_log_std, reduction)
254
+
255
+ #TODO(ll) other input types ..gym, tuple?
256
+ #TODO(ll) ignore output scale for now because don't like, but may reintroduce
257
+ #TODO(ll) where is activation tanh propagated (what used to be forgotten as a bug)
258
+ # self.instantiator_output_scale = metadata["output_scale"]
259
+
260
+ self.input_shape = input_shape
261
+
262
+ self.net = network
263
+ # self.output_layer = output_layer
264
+
265
+ self.policy_output_layer = output_layer["policy"]
266
+ self.value_output_layer = output_layer["value"]
267
+
268
+ self._lstm = contains_rnn_module( self, nn.LSTM)
269
+ self._rnn = contains_rnn_module( self, (nn.LSTM, nn.RNN, nn.GRU))
270
+
271
+ print("!rnn: ", self._rnn)
272
+ print("!lstm: ", self._lstm)
273
+
274
+ # caching for shared forward pass for policy and value roles
275
+ self._cached_states = None
276
+ self._cached_outputs = None
277
+
278
+ # TODO(ll) one liner:
279
+ # self.net = nn.Sequential(network, self._get_num_units_by_shape(self.output_shape), torch.nn.Tanh())
280
+
281
+ # TODO(ll) output_units = self._get_num_units_by_shape(self.output_shape)
282
+
283
+ # TODO(ll) 512 (last hidden layer) is hardcoded.
284
+ #layers = [nn.Linear(512, self._get_num_units_by_shape(self.output_shape)), torch.nn.Tanh()]
285
+
286
+ # TODO(ll) propagate output activation from outside. Maybe actually propagate function, so it is easily customable and can include scaling.
287
+ # -- it can default to tanh here, and to identity in deterministic
288
+ #
289
+ # if metadata[0]["output_activation"] is not None:
290
+ # layers.append(_get_activation_function(metadata[0]["output_activation"]))
291
+
292
+ #self.mean_net = nn.Sequential(*layers)
293
+
294
+ # self.log_std_parameter = nn.Parameter(
295
+ # initial_log_std * torch.ones(self._get_num_units_by_shape(self.output_shape)))
296
+
297
+ #TODO(ll) only needed for LSTM, RNN, GRU .. PPO_RNN
298
+ #TODO(ll) consider adding it to Base model.
299
+ # Recurrent Neural Network (RNN) specification for RNN, LSTM and GRU layers/cells
300
+ def get_specification(self):
301
+ if self._lstm:
302
+ # batch size (N) is the number of envs
303
+ return {"rnn": {"sequence_length": self.net.sequence_length,
304
+ "sizes": [(self.net.num_layers, self.net.num_envs, self.net.hidden_size), # hidden states (D ∗ num_layers, N, Hout)
305
+ (self.net.num_layers, self.net.num_envs, self.net.hidden_size)]}} # cell states (D ∗ num_layers, N, Hcell)
306
+ elif self._rnn:
307
+ return {"rnn": {"sequence_length": self.net.sequence_length,
308
+ "sizes": [(self.net.num_layers, self.net.num_envs, self.net.hidden_size)]}} # hidden states (D ∗ num_layers, N, Hout)
309
+ else:
310
+ return {}
311
+
312
+ def get_entropy(self, role: str = "") -> torch.Tensor:
313
+ """Compute and return the entropy of the model
314
+
315
+ :return: Entropy of the model
316
+ :rtype: torch.Tensor
317
+ :param role: Role play by the model (default: ``""``)
318
+ :type role: str, optional
319
+
320
+ Example::
321
+
322
+ >>> entropy = model.get_entropy()
323
+ >>> print(entropy.shape)
324
+ torch.Size([4096, 8])
325
+ """
326
+ return self.policy_output_layer.mixin.get_entropy(role)
327
+
328
+ def distribution(self, role: str = "") -> torch.distributions.Normal:
329
+ """Get the current distribution of the model
330
+
331
+ :return: Distribution of the model
332
+ :rtype: torch.distributions.Normal
333
+ :param role: Role play by the model (default: ``""``)
334
+ :type role: str, optional
335
+
336
+ Example::
337
+
338
+ >>> distribution = model.distribution()
339
+ >>> print(distribution)
340
+ Normal(loc: torch.Size([4096, 8]), scale: torch.Size([4096, 8]))
341
+ """
342
+ return self.policy_output_layer.mixin.distribution(role)
343
+
344
+ def _have_cached_states(self, states):
345
+ return (self._cached_states is not None
346
+ and torch.equal(self._cached_states, states)
347
+ )
348
+
349
+ def act(self,
350
+ inputs: Mapping[str, Union[torch.Tensor, Any]],
351
+ role: str = "") -> Tuple[torch.Tensor, Union[torch.Tensor, None], Mapping[str, Union[torch.Tensor, Any]]]:
352
+
353
+ states = self._get_all_inputs(inputs) # TODO(ll) rename to get_all_states
354
+
355
+ if not self._have_cached_states(states):
356
+ # TODO(ll) if one of the models is not rnn ... and PPO_RNN is called what happens?
357
+ if self._rnn:
358
+ output, output_dict = self.net(states, inputs.get('terminated', None), inputs['rnn'])
359
+ else:
360
+ output = self.net(states)
361
+ output_dict = {}
362
+
363
+ self._cached_states = states
364
+ self._cached_output = output, output_dict
365
+
366
+ output, output_dict = self._cached_output
367
+
368
+ if role == "policy":
369
+ return self.policy_output_layer(output, inputs.get('taken_actions', None), output_dict)
370
+ elif role == "value":
371
+ return self.value_output_layer(output, inputs.get('taken_actions', None), output_dict)
372
+
373
+
374
+
375
+
376
+ # TODO(ll)
377
+ # CNN
378
+ # 1) changing shape if it comes in linear fashion of input, check how I was doing this.
379
+ # def compute(self, inputs, role):
380
+ # # permute (samples, width * height * channels) -> (samples, channels, width, height)
381
+ # return self.net(inputs["states"].view(-1, *self.observation_space.shape).permute(0, 3, 1, 2)), self.log_std_parameter, {}
382
+ # 2) what with that weird Shapes? search for taken_actions, who called it with this input. How should CNN be applied to such things..just in states?
383
+
384
+ # TODO(ll) output scale removed .. check that tanh where is
385
+ # return output * self.instantiator_output_scale, self.log_std_parameter, {}
386
+ # return output, self.log_std_parameter, output_dict
387
+
388
+
389
+ class GaussianLayer(nn.Module):
390
+ def __init__(self,
391
+ input_size, # TODO(ll)
392
+ output_size, # TODO(ll) allow here the Box input type and handle or throw away shapes.
393
+ device,
394
+ output_shape: Shape = Shape.ACTIONS,
395
+ output_activation: nn.Module = nn.Tanh(),
396
+ clip_actions: bool = False,
397
+ clip_log_std: bool = True,
398
+ min_log_std: float = -20,
399
+ max_log_std: float = 2,
400
+ initial_log_std: float = 0,
401
+ reduction="sum",
402
+ ):
403
+ """Gaussian model
404
+
405
+ :param observation_space: Observation/state space or shape (default: None).
406
+ If it is not None, the num_observations property will contain the size of that space
407
+ :type observation_space: int, tuple or list of integers, gym.Space, gymnasium.Space or None, optional
408
+ :param action_space: Action space or shape (default: None).
409
+ If it is not None, the num_actions property will contain the size of that space
410
+ :type action_space: int, tuple or list of integers, gym.Space, gymnasium.Space or None, optional
411
+ :param input_shape todo(ll)
412
+ :type input_shape
413
+ :param output_shape todo(ll)
414
+ :type output_shape
415
+ :param network todo(ll) description + optional?
416
+ :type network
417
+ :param device: Device on which a tensor/array is or will be allocated (default: ``None``).
418
+ If None, the device will be either ``"cuda"`` if available or ``"cpu"``
419
+ :type device: str or torch.device, optional
420
+ :param clip_actions: Flag to indicate whether the actions should be clipped (default: False)
421
+ :type clip_actions: bool, optional
422
+ :param clip_log_std: Flag to indicate whether the log standard deviations should be clipped (default: True)
423
+ :type clip_log_std: bool, optional
424
+ :param min_log_std: Minimum value of the log standard deviation (default: -20)
425
+ :type min_log_std: float, optional
426
+ :param max_log_std: Maximum value of the log standard deviation (default: 2)
427
+ :type max_log_std: float, optional
428
+ :param initial_log_std: Initial value for the log standard deviation (default: 0)
429
+ :type initial_log_std: float, optional
430
+ :param reduction: Reduction method for returning the log probability density function: (default: ``"sum"``).
431
+ Supported values are ``"mean"``, ``"sum"``, ``"prod"`` and ``"none"``. If "``none"``, the log probability density
432
+ function is returned as a tensor of shape ``(num_samples, num_actions)`` instead of ``(num_samples, 1)``
433
+ :type reduction: str, optional
434
+ """
435
+ super().__init__()
436
+ # GaussianMixin.__init__(self, clip_actions, clip_log_std, min_log_std, max_log_std, reduction)
437
+
438
+ self.mixin = GaussianMixin(clip_actions, clip_log_std, min_log_std, max_log_std, reduction)
439
+
440
+ #TODO(ll) !!!!!!! propagate device TODO(ll) is device used somewhere, A: it is used in Mixin and not propagated yet.
441
+ self.device=device
442
+
443
+
444
+ #TODO(ll) ignore output scale for now because don't like, but may reintroduce
445
+ #TODO(ll) where is activation tanh propagated (what used to be forgotten as a bug)
446
+ # self.instantiator_output_scale = metadata["output_scale"]
447
+
448
+ self.output_shape = output_shape
449
+ # self.output_activation = output_activation
450
+
451
+ # TODO(ll) propagate output activation from outside. Maybe actually propagate function, so it is easily customable and can include scaling.
452
+ # -- it can default to tanh here, and to identity in deterministic
453
+ #
454
+ # if metadata[0]["output_activation"] is not None:
455
+ # layers.append(_get_activation_function(metadata[0]["output_activation"]))
456
+
457
+ self.net = nn.Sequential(
458
+ nn.Linear(input_size, output_size), # self._get_num_units_by_shape(self.output_shape)),
459
+ output_activation)
460
+
461
+ self.log_std_parameter = nn.Parameter(
462
+ initial_log_std * torch.ones( output_size )) #self._get_num_units_by_shape(self.output_shape)))
463
+
464
+ # TODO(ll) this already lives in Model. move to utils
465
+ def _get_num_units_by_shape(self, shape: Shape) -> int:
466
+ """Get the number of units in a layer by shape
467
+
468
+ :param model: Model to get the number of units for
469
+ :type model: Model
470
+ :param shape: Shape of the layer
471
+ :type shape: Shape or int
472
+
473
+ :return: Number of units in the layer
474
+ :rtype: int
475
+ """
476
+ num_units = {Shape.ONE: 1,
477
+ Shape.STATES: self.num_observations,
478
+ Shape.ACTIONS: self.num_actions,
479
+ Shape.STATES_ACTIONS: self.num_observations + self.num_actions}
480
+ try:
481
+ return num_units[shape]
482
+ except:
483
+ return shape
484
+
485
+
486
+ def forward(self, input, taken_actions, outputs_dict):
487
+
488
+ # TODO(ll) passing by independetly as together sequential has just one input in forward
489
+ # potentially can be done that output dict would be propagated through mean net (mean net overload forward)
490
+ mean_actions = self.net(input)
491
+
492
+ #TODO
493
+ return self.mixin.forward(mean_actions, taken_actions, self.log_std_parameter, outputs_dict)
494
+
495
+ # TODO(ll)
496
+ # CNN
497
+ # 1) changing shape if it comes in linear fashion of input, check how I was doing this.
498
+ # def compute(self, inputs, role):
499
+ # # permute (samples, width * height * channels) -> (samples, channels, width, height)
500
+ # return self.net(inputs["states"].view(-1, *self.observation_space.shape).permute(0, 3, 1, 2)), self.log_std_parameter, {}
501
+ # 2) what with that weird Shapes? search for taken_actions, who called it with this input. How should CNN be applied to such things..just in states?
502
+
503
+ # TODO(ll) output scale removed .. check that tanh where is
504
+ # return output * self.instantiator_output_scale, self.log_std_parameter, {}
505
+ return output, self.log_std_parameter, output_dict
506
+
507
+
508
+ class DeterministicLayer(torch.nn.Module, DeterministicMixin):
509
+ def __init__(self,
510
+ input_size,
511
+ output_size,
512
+ device,
513
+ output_shape: Shape = Shape.ONE,
514
+ output_activation: nn.Module = nn.Tanh(),
515
+ clip_actions: bool = False,
516
+ ):
517
+ """Deterministic model
518
+
519
+ TODO(ll) update doc string
520
+ :param observation_space: Observation/state space or shape (default: None).
521
+ If it is not None, the num_observations property will contain the size of that space
522
+ :type observation_space: int, tuple or list of integers, gym.Space, gymnasium.Space or None, optional
523
+ :param action_space: Action space or shape (default: None).
524
+ If it is not None, the num_actions property will contain the size of that space
525
+ :type action_space: int, tuple or list of integers, gym.Space, gymnasium.Space or None, optional
526
+ :param device: Device on which a tensor/array is or will be allocated (default: ``None``).
527
+ If None, the device will be either ``"cuda"`` if available or ``"cpu"``
528
+ :type device: str or torch.device, optional
529
+ :param clip_actions: Flag to indicate whether the actions should be clipped to the action space (default: False)
530
+ :type clip_actions: bool, optional
531
+ :param input_shape: Shape of the input (default: Shape.STATES)
532
+ :type input_shape: Shape, optional
533
+ :param hiddens: Number of hidden units in each hidden layer
534
+ :type hiddens: int or list of ints
535
+ :param hidden_activation: Activation function for each hidden layer (default: "relu").
536
+ :type hidden_activation: list of strings
537
+ :param output_shape: Shape of the output (default: Shape.ACTIONS)
538
+ :type output_shape: Shape, optional
539
+ :param output_activation: Activation function for the output layer (default: "tanh")
540
+ :type output_activation: str or None, optional
541
+ :param output_scale: Scale of the output layer (default: 1.0).
542
+ If None, the output layer will not be scaled
543
+ :type output_scale: float, optional
544
+
545
+ :return: Deterministic model instance
546
+ :rtype: Model
547
+ """
548
+
549
+ super().__init__()
550
+ self.mixin = DeterministicMixin(clip_actions)
551
+
552
+ #TODO(ll) !!!!!!propagate device
553
+ self.device=device
554
+
555
+ self.output_shape = output_shape
556
+
557
+ self.net = nn.Sequential(
558
+ nn.Linear(input_size, output_size), # self._get_num_units_by_shape(self.output_shape)),
559
+ output_activation
560
+ )
561
+
562
+ def forward(self, input, taken_actions, outputs_dict):
563
+
564
+ output = self.net(input)
565
+ return self.mixin.forward(output, outputs_dict)
566
+
567
+
568
+
569
+ # class GaussianModel(GaussianMixin, Model):
570
+ # def __init__(self,
571
+ # observation_space: Optional[Union[int, Tuple[int], gym.Space, gymnasium.Space]] = None,
572
+ # action_space: Optional[Union[int, Tuple[int], gym.Space, gymnasium.Space]] = None,
573
+ # # TODO network, if None do pecreptron?
574
+ # network: Optional[nn.Module] = None,
575
+ # device: Optional[Union[str, torch.device]] = None,
576
+ # clip_actions: bool = False,
577
+ # clip_log_std: bool = True,
578
+ # min_log_std: float = -20,
579
+ # max_log_std: float = 2,
580
+ # initial_log_std: float = 0,
581
+ # input_shape: Shape = Shape.STATES,
582
+ # output_shape: Shape = Shape.ACTIONS,
583
+ # reduction="sum",
584
+ # ):
585
+ # """Gaussian model
586
+
587
+ # :param observation_space: Observation/state space or shape (default: None).
588
+ # If it is not None, the num_observations property will contain the size of that space
589
+ # :type observation_space: int, tuple or list of integers, gym.Space, gymnasium.Space or None, optional
590
+ # :param action_space: Action space or shape (default: None).
591
+ # If it is not None, the num_actions property will contain the size of that space
592
+ # :type action_space: int, tuple or list of integers, gym.Space, gymnasium.Space or None, optional
593
+ # :param input_shape todo(ll)
594
+ # :type input_shape
595
+ # :param output_shape todo(ll)
596
+ # :type output_shape
597
+ # :param network todo(ll) description + optional?
598
+ # :type network
599
+ # :param device: Device on which a tensor/array is or will be allocated (default: ``None``).
600
+ # If None, the device will be either ``"cuda"`` if available or ``"cpu"``
601
+ # :type device: str or torch.device, optional
602
+ # :param clip_actions: Flag to indicate whether the actions should be clipped (default: False)
603
+ # :type clip_actions: bool, optional
604
+ # :param clip_log_std: Flag to indicate whether the log standard deviations should be clipped (default: True)
605
+ # :type clip_log_std: bool, optional
606
+ # :param min_log_std: Minimum value of the log standard deviation (default: -20)
607
+ # :type min_log_std: float, optional
608
+ # :param max_log_std: Maximum value of the log standard deviation (default: 2)
609
+ # :type max_log_std: float, optional
610
+ # :param initial_log_std: Initial value for the log standard deviation (default: 0)
611
+ # :type initial_log_std: float, optional
612
+ # :param reduction: Reduction method for returning the log probability density function: (default: ``"sum"``).
613
+ # Supported values are ``"mean"``, ``"sum"``, ``"prod"`` and ``"none"``. If "``none"``, the log probability density
614
+ # function is returned as a tensor of shape ``(num_samples, num_actions)`` instead of ``(num_samples, 1)``
615
+ # :type reduction: str, optional
616
+ # """
617
+
618
+ # Model.__init__(self, observation_space, action_space, device)
619
+ # GaussianMixin.__init__(self, clip_actions, clip_log_std, min_log_std, max_log_std, reduction)
620
+
621
+ # #TODO(ll) other input types ..gym, tuple?
622
+ # #TODO(ll) ignore output scale for now because don't like, but may reintroduce
623
+ # #TODO(ll) where is activation tanh propagated (what used to be forgotten as a bug)
624
+ # # self.instantiator_output_scale = metadata["output_scale"]
625
+
626
+ # self.input_shape = input_shape
627
+ # self.output_shape = output_shape
628
+
629
+ # self.net = network
630
+
631
+ # # TODO(ll) maybe other way .. check net for lstm/rnn/gru?
632
+ # self._rnn = hasattr(self.net, 'sequence_length')
633
+
634
+ # # TODO(ll) one liner:
635
+ # # self.net = nn.Sequential(network, self._get_num_units_by_shape(self.output_shape), torch.nn.Tanh())
636
+
637
+ # # TODO(ll) output_units = self._get_num_units_by_shape(self.output_shape)
638
+
639
+ # # TODO(ll) 512 (last hidden layer) is hardcoded.
640
+ # layers = [nn.Linear(512, self._get_num_units_by_shape(self.output_shape)), torch.nn.Tanh()]
641
+
642
+ # # TODO(ll) propagate output activation from outside. Maybe actually propagate function, so it is easily customable and can include scaling.
643
+ # # -- it can default to tanh here, and to identity in deterministic
644
+ # #
645
+ # # if metadata[0]["output_activation"] is not None:
646
+ # # layers.append(_get_activation_function(metadata[0]["output_activation"]))
647
+
648
+ # self.mean_net = nn.Sequential(*layers)
649
+
650
+ # self.log_std_parameter = nn.Parameter(
651
+ # initial_log_std * torch.ones(self._get_num_units_by_shape(self.output_shape)))
652
+
653
+ # #TODO(ll) only needed for LSTM, RNN, GRU .. PPO_RNN
654
+ # #TODO(ll) consider adding it to Base model.
655
+ # # Recurrent Neural Network (RNN) specification for RNN, LSTM and GRU layers/cells
656
+ # def get_specification(self):
657
+ # if self._rnn:
658
+ # # batch size (N) is the number of envs
659
+ # return {"rnn": {"sequence_length": self.net.sequence_length,
660
+ # "sizes": [(self.net.num_layers, self.net.num_envs, self.net.hidden_size), # hidden states (D ∗ num_layers, N, Hout)
661
+ # (self.net.num_layers, self.net.num_envs, self.net.hidden_size)]}} # cell states (D ∗ num_layers, N, Hcell)
662
+ # else:
663
+ # return {}
664
+
665
+ # def compute(self, inputs, role=""):
666
+
667
+ # # TODO(ll) once shared model is design, you may rethink this to do by call some function pointer or whatever.
668
+ # # or consider inheritance GaussianModelRNN
669
+ # # TODO(ll) if one of the models is not rnn ... and PPO_RNN is called what happens?
670
+ # if self._rnn:
671
+ # output, output_dict = self.net(self._get_all_inputs(inputs), inputs.get('terminated', None), inputs['rnn'])
672
+ # else:
673
+ # output = self.net(self._get_all_inputs(inputs))
674
+ # output_dict = {}
675
+
676
+ # # TODO(ll) passing by independetly as together sequential has just one input in forward
677
+ # # potentially can be done that output dict would be propagated through mean net (mean net overload forward)
678
+ # output = self.mean_net(output)
679
+
680
+ # # TODO(ll)
681
+ # # CNN
682
+ # # 1) changing shape if it comes in linear fashion of input, check how I was doing this.
683
+ # # def compute(self, inputs, role):
684
+ # # # permute (samples, width * height * channels) -> (samples, channels, width, height)
685
+ # # return self.net(inputs["states"].view(-1, *self.observation_space.shape).permute(0, 3, 1, 2)), self.log_std_parameter, {}
686
+ # # 2) what with that weird Shapes? search for taken_actions, who called it with this input. How should CNN be applied to such things..just in states?
687
+
688
+ # # TODO(ll) output scale removed .. check that tanh where is
689
+ # # return output * self.instantiator_output_scale, self.log_std_parameter, {}
690
+ # return output, self.log_std_parameter, output_dict
691
+
692
+
693
+ # class DeterministicModel(DeterministicMixin, Model):
694
+ # def __init__(self,
695
+ # observation_space: Optional[Union[int, Tuple[int], gym.Space, gymnasium.Space]] = None,
696
+ # action_space: Optional[Union[int, Tuple[int], gym.Space, gymnasium.Space]] = None,
697
+ # network: Optional[nn.Module] = None,
698
+ # device: Optional[Union[str, torch.device]] = None,
699
+ # clip_actions: bool = False,
700
+ # input_shape: Shape = Shape.STATES,
701
+ # output_shape: Shape = Shape.ONE,
702
+ # ):
703
+ # """Deterministic model
704
+
705
+ # TODO(ll) update doc string
706
+ # :param observation_space: Observation/state space or shape (default: None).
707
+ # If it is not None, the num_observations property will contain the size of that space
708
+ # :type observation_space: int, tuple or list of integers, gym.Space, gymnasium.Space or None, optional
709
+ # :param action_space: Action space or shape (default: None).
710
+ # If it is not None, the num_actions property will contain the size of that space
711
+ # :type action_space: int, tuple or list of integers, gym.Space, gymnasium.Space or None, optional
712
+ # :param device: Device on which a tensor/array is or will be allocated (default: ``None``).
713
+ # If None, the device will be either ``"cuda"`` if available or ``"cpu"``
714
+ # :type device: str or torch.device, optional
715
+ # :param clip_actions: Flag to indicate whether the actions should be clipped to the action space (default: False)
716
+ # :type clip_actions: bool, optional
717
+ # :param input_shape: Shape of the input (default: Shape.STATES)
718
+ # :type input_shape: Shape, optional
719
+ # :param hiddens: Number of hidden units in each hidden layer
720
+ # :type hiddens: int or list of ints
721
+ # :param hidden_activation: Activation function for each hidden layer (default: "relu").
722
+ # :type hidden_activation: list of strings
723
+ # :param output_shape: Shape of the output (default: Shape.ACTIONS)
724
+ # :type output_shape: Shape, optional
725
+ # :param output_activation: Activation function for the output layer (default: "tanh")
726
+ # :type output_activation: str or None, optional
727
+ # :param output_scale: Scale of the output layer (default: 1.0).
728
+ # If None, the output layer will not be scaled
729
+ # :type output_scale: float, optional
730
+
731
+ # :return: Deterministic model instance
732
+ # :rtype: Model
733
+ # """
734
+
735
+ # Model.__init__(self, observation_space, action_space, device)
736
+ # DeterministicMixin.__init__(self, clip_actions)
737
+
738
+ # self.input_shape = input_shape
739
+ # self.output_shape = output_shape
740
+
741
+ # self.net = network
742
+ # self._rnn = hasattr(self.net, 'sequence_length')
743
+
744
+ # # TODO(ll) change identity by custom fction.
745
+
746
+ # # TODO(ll) 512 is hardcoded.
747
+ # self.deterministic_layer = nn.Sequential(
748
+ # nn.Linear(512, self._get_num_units_by_shape(self.output_shape)), torch.nn.Identity())
749
+
750
+ # def get_specification(self):
751
+ # if self._rnn:
752
+ # # batch size (N) is the number of envs
753
+ # return {"rnn": {"sequence_length": self.net.sequence_length,
754
+ # "sizes": [(self.net.num_layers, self.net.num_envs, self.net.hidden_size), # hidden states (D ∗ num_layers, N, Hout)
755
+ # (self.net.num_layers, self.net.num_envs, self.net.hidden_size)]}} # cell states (D ∗ num_layers, N, Hcell)
756
+ # else:
757
+ # return {}
758
+
759
+ # def compute(self, inputs, role=""):
760
+ # # TODO(ll) this block can be maybe moved into base.py if it is the same for all (where get_all_inputs live)
761
+ # if self._rnn:
762
+ # output, output_dict = self.net(self._get_all_inputs(inputs), inputs.get('terminated', None), inputs['rnn'])
763
+ # else:
764
+ # output = self.net(self._get_all_inputs(inputs))
765
+ # output_dict = {}
766
+
767
+ # output = self.deterministic_layer(output)
768
+
769
+ # # TODO(ll) output scale removed
770
+ # # return output * self.instantiator_output_scale, {}
771
+ # return output, output_dict