ZhengyangZhang commited on
Commit
3b540e0
·
verified ·
1 Parent(s): 661cda2

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. lib/python3.12/site-packages/ray/rllib/__pycache__/__init__.cpython-312.pyc +0 -0
  2. lib/python3.12/site-packages/ray/rllib/algorithms/__init__.py +38 -0
  3. lib/python3.12/site-packages/ray/rllib/algorithms/algorithm.py +0 -0
  4. lib/python3.12/site-packages/ray/rllib/algorithms/algorithm_config.py +0 -0
  5. lib/python3.12/site-packages/ray/rllib/algorithms/appo/__init__.py +12 -0
  6. lib/python3.12/site-packages/ray/rllib/algorithms/appo/__pycache__/__init__.cpython-312.pyc +0 -0
  7. lib/python3.12/site-packages/ray/rllib/algorithms/appo/__pycache__/appo.cpython-312.pyc +0 -0
  8. lib/python3.12/site-packages/ray/rllib/algorithms/appo/__pycache__/appo_learner.cpython-312.pyc +0 -0
  9. lib/python3.12/site-packages/ray/rllib/algorithms/appo/__pycache__/appo_rl_module.cpython-312.pyc +0 -0
  10. lib/python3.12/site-packages/ray/rllib/algorithms/appo/__pycache__/appo_tf_policy.cpython-312.pyc +0 -0
  11. lib/python3.12/site-packages/ray/rllib/algorithms/appo/__pycache__/appo_torch_policy.cpython-312.pyc +0 -0
  12. lib/python3.12/site-packages/ray/rllib/algorithms/appo/__pycache__/default_appo_rl_module.cpython-312.pyc +0 -0
  13. lib/python3.12/site-packages/ray/rllib/algorithms/appo/__pycache__/utils.cpython-312.pyc +0 -0
  14. lib/python3.12/site-packages/ray/rllib/algorithms/appo/appo.py +435 -0
  15. lib/python3.12/site-packages/ray/rllib/algorithms/appo/appo_learner.py +147 -0
  16. lib/python3.12/site-packages/ray/rllib/algorithms/appo/appo_rl_module.py +11 -0
  17. lib/python3.12/site-packages/ray/rllib/algorithms/appo/appo_tf_policy.py +393 -0
  18. lib/python3.12/site-packages/ray/rllib/algorithms/appo/appo_torch_policy.py +413 -0
  19. lib/python3.12/site-packages/ray/rllib/algorithms/appo/default_appo_rl_module.py +56 -0
  20. lib/python3.12/site-packages/ray/rllib/algorithms/appo/torch/__init__.py +0 -0
  21. lib/python3.12/site-packages/ray/rllib/algorithms/appo/torch/__pycache__/__init__.cpython-312.pyc +0 -0
  22. lib/python3.12/site-packages/ray/rllib/algorithms/appo/torch/__pycache__/appo_torch_learner.cpython-312.pyc +0 -0
  23. lib/python3.12/site-packages/ray/rllib/algorithms/appo/torch/__pycache__/appo_torch_rl_module.cpython-312.pyc +0 -0
  24. lib/python3.12/site-packages/ray/rllib/algorithms/appo/torch/__pycache__/default_appo_torch_rl_module.cpython-312.pyc +0 -0
  25. lib/python3.12/site-packages/ray/rllib/algorithms/appo/torch/appo_torch_learner.py +234 -0
  26. lib/python3.12/site-packages/ray/rllib/algorithms/appo/torch/appo_torch_rl_module.py +13 -0
  27. lib/python3.12/site-packages/ray/rllib/algorithms/appo/torch/default_appo_torch_rl_module.py +10 -0
  28. lib/python3.12/site-packages/ray/rllib/algorithms/appo/utils.py +174 -0
  29. lib/python3.12/site-packages/ray/rllib/algorithms/callbacks.py +7 -0
  30. lib/python3.12/site-packages/ray/rllib/algorithms/marwil/__init__.py +18 -0
  31. lib/python3.12/site-packages/ray/rllib/algorithms/marwil/__pycache__/__init__.cpython-312.pyc +0 -0
  32. lib/python3.12/site-packages/ray/rllib/algorithms/marwil/__pycache__/marwil.cpython-312.pyc +0 -0
  33. lib/python3.12/site-packages/ray/rllib/algorithms/marwil/__pycache__/marwil_learner.cpython-312.pyc +0 -0
  34. lib/python3.12/site-packages/ray/rllib/algorithms/marwil/__pycache__/marwil_tf_policy.cpython-312.pyc +0 -0
  35. lib/python3.12/site-packages/ray/rllib/algorithms/marwil/__pycache__/marwil_torch_policy.cpython-312.pyc +0 -0
  36. lib/python3.12/site-packages/ray/rllib/algorithms/marwil/marwil.py +536 -0
  37. lib/python3.12/site-packages/ray/rllib/algorithms/marwil/marwil_learner.py +51 -0
  38. lib/python3.12/site-packages/ray/rllib/algorithms/marwil/marwil_tf_policy.py +251 -0
  39. lib/python3.12/site-packages/ray/rllib/algorithms/marwil/marwil_torch_policy.py +132 -0
  40. lib/python3.12/site-packages/ray/rllib/algorithms/marwil/torch/__init__.py +0 -0
  41. lib/python3.12/site-packages/ray/rllib/algorithms/marwil/torch/__pycache__/__init__.cpython-312.pyc +0 -0
  42. lib/python3.12/site-packages/ray/rllib/algorithms/marwil/torch/__pycache__/marwil_torch_learner.cpython-312.pyc +0 -0
  43. lib/python3.12/site-packages/ray/rllib/algorithms/marwil/torch/marwil_torch_learner.py +138 -0
  44. lib/python3.12/site-packages/ray/rllib/algorithms/mock.py +154 -0
  45. lib/python3.12/site-packages/ray/rllib/algorithms/ppo/__init__.py +12 -0
  46. lib/python3.12/site-packages/ray/rllib/algorithms/ppo/__pycache__/__init__.cpython-312.pyc +0 -0
  47. lib/python3.12/site-packages/ray/rllib/algorithms/ppo/__pycache__/default_ppo_rl_module.cpython-312.pyc +0 -0
  48. lib/python3.12/site-packages/ray/rllib/algorithms/ppo/__pycache__/ppo.cpython-312.pyc +0 -0
  49. lib/python3.12/site-packages/ray/rllib/algorithms/ppo/__pycache__/ppo_catalog.cpython-312.pyc +0 -0
  50. lib/python3.12/site-packages/ray/rllib/algorithms/ppo/__pycache__/ppo_learner.cpython-312.pyc +0 -0
lib/python3.12/site-packages/ray/rllib/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (2.16 kB). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/__init__.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ray.rllib.algorithms.algorithm import Algorithm
2
+ from ray.rllib.algorithms.algorithm_config import AlgorithmConfig
3
+ from ray.rllib.algorithms.appo.appo import APPO, APPOConfig
4
+ from ray.rllib.algorithms.bc.bc import BC, BCConfig
5
+ from ray.rllib.algorithms.cql.cql import CQL, CQLConfig
6
+ from ray.rllib.algorithms.dqn.dqn import DQN, DQNConfig
7
+ from ray.rllib.algorithms.impala.impala import (
8
+ IMPALA,
9
+ Impala,
10
+ IMPALAConfig,
11
+ ImpalaConfig,
12
+ )
13
+ from ray.rllib.algorithms.marwil.marwil import MARWIL, MARWILConfig
14
+ from ray.rllib.algorithms.ppo.ppo import PPO, PPOConfig
15
+ from ray.rllib.algorithms.sac.sac import SAC, SACConfig
16
+
17
+ __all__ = [
18
+ "Algorithm",
19
+ "AlgorithmConfig",
20
+ "APPO",
21
+ "APPOConfig",
22
+ "BC",
23
+ "BCConfig",
24
+ "CQL",
25
+ "CQLConfig",
26
+ "DQN",
27
+ "DQNConfig",
28
+ "IMPALA",
29
+ "IMPALAConfig",
30
+ "Impala",
31
+ "ImpalaConfig",
32
+ "MARWIL",
33
+ "MARWILConfig",
34
+ "PPO",
35
+ "PPOConfig",
36
+ "SAC",
37
+ "SACConfig",
38
+ ]
lib/python3.12/site-packages/ray/rllib/algorithms/algorithm.py ADDED
The diff for this file is too large to render. See raw diff
 
lib/python3.12/site-packages/ray/rllib/algorithms/algorithm_config.py ADDED
The diff for this file is too large to render. See raw diff
 
lib/python3.12/site-packages/ray/rllib/algorithms/appo/__init__.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ray.rllib.algorithms.appo.appo import APPO, APPOConfig
2
+ from ray.rllib.algorithms.appo.appo_tf_policy import APPOTF1Policy, APPOTF2Policy
3
+ from ray.rllib.algorithms.appo.appo_torch_policy import APPOTorchPolicy
4
+
5
+ __all__ = [
6
+ "APPO",
7
+ "APPOConfig",
8
+ # @OldAPIStack
9
+ "APPOTF1Policy",
10
+ "APPOTF2Policy",
11
+ "APPOTorchPolicy",
12
+ ]
lib/python3.12/site-packages/ray/rllib/algorithms/appo/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (525 Bytes). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/appo/__pycache__/appo.cpython-312.pyc ADDED
Binary file (17.2 kB). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/appo/__pycache__/appo_learner.cpython-312.pyc ADDED
Binary file (7.36 kB). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/appo/__pycache__/appo_rl_module.cpython-312.pyc ADDED
Binary file (575 Bytes). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/appo/__pycache__/appo_tf_policy.cpython-312.pyc ADDED
Binary file (15.5 kB). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/appo/__pycache__/appo_torch_policy.cpython-312.pyc ADDED
Binary file (17.3 kB). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/appo/__pycache__/default_appo_rl_module.cpython-312.pyc ADDED
Binary file (3.35 kB). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/appo/__pycache__/utils.cpython-312.pyc ADDED
Binary file (8.12 kB). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/appo/appo.py ADDED
@@ -0,0 +1,435 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Asynchronous Proximal Policy Optimization (APPO)
2
+
3
+ The algorithm is described in [1] (under the name of "IMPACT"):
4
+
5
+ Detailed documentation:
6
+ https://docs.ray.io/en/master/rllib-algorithms.html#appo
7
+
8
+ [1] IMPACT: Importance Weighted Asynchronous Architectures with Clipped Target Networks.
9
+ Luo et al. 2020
10
+ https://arxiv.org/pdf/1912.00167
11
+ """
12
+
13
+ import logging
14
+ from typing import Optional, Type
15
+
16
+ from typing_extensions import Self
17
+
18
+ from ray._common.deprecation import DEPRECATED_VALUE, deprecation_warning
19
+ from ray.rllib.algorithms.algorithm_config import AlgorithmConfig, NotProvided
20
+ from ray.rllib.algorithms.impala.impala import IMPALA, IMPALAConfig
21
+ from ray.rllib.core.rl_module.rl_module import RLModuleSpec
22
+ from ray.rllib.policy.policy import Policy
23
+ from ray.rllib.utils.annotations import override
24
+ from ray.rllib.utils.metrics import (
25
+ LAST_TARGET_UPDATE_TS,
26
+ LEARNER_STATS_KEY,
27
+ NUM_AGENT_STEPS_SAMPLED,
28
+ NUM_ENV_STEPS_SAMPLED,
29
+ NUM_TARGET_UPDATES,
30
+ )
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+
35
+ LEARNER_RESULTS_KL_KEY = "mean_kl_loss"
36
+ LEARNER_RESULTS_CURR_KL_COEFF_KEY = "curr_kl_coeff"
37
+ OLD_ACTION_DIST_KEY = "old_action_dist"
38
+
39
+
40
+ class APPOConfig(IMPALAConfig):
41
+ """Defines a configuration class from which an APPO Algorithm can be built.
42
+
43
+ .. testcode::
44
+
45
+ from ray.rllib.algorithms.appo import APPOConfig
46
+ config = (
47
+ APPOConfig()
48
+ .training(lr=0.01, grad_clip=30.0, train_batch_size_per_learner=50)
49
+ )
50
+ config = config.learners(num_learners=1)
51
+ config = config.env_runners(num_env_runners=1)
52
+ config = config.environment("CartPole-v1")
53
+
54
+ # Build an Algorithm object from the config and run 1 training iteration.
55
+ algo = config.build()
56
+ algo.train()
57
+ del algo
58
+
59
+ .. testcode::
60
+
61
+ from ray.rllib.algorithms.appo import APPOConfig
62
+ from ray import tune
63
+
64
+ config = APPOConfig()
65
+ # Update the config object.
66
+ config = config.training(lr=tune.grid_search([0.001,]))
67
+ # Set the config object's env.
68
+ config = config.environment(env="CartPole-v1")
69
+ # Use to_dict() to get the old-style python config dict when running with tune.
70
+ tune.Tuner(
71
+ "APPO",
72
+ run_config=tune.RunConfig(
73
+ stop={"training_iteration": 1},
74
+ verbose=0,
75
+ ),
76
+ param_space=config.to_dict(),
77
+
78
+ ).fit()
79
+
80
+ .. testoutput::
81
+ :hide:
82
+
83
+ ...
84
+ """
85
+
86
+ def __init__(self, algo_class=None):
87
+ """Initializes a APPOConfig instance."""
88
+ self.exploration_config = {
89
+ # The Exploration class to use. In the simplest case, this is the name
90
+ # (str) of any class present in the `rllib.utils.exploration` package.
91
+ # You can also provide the python class directly or the full location
92
+ # of your class (e.g. "ray.rllib.utils.exploration.epsilon_greedy.
93
+ # EpsilonGreedy").
94
+ "type": "StochasticSampling",
95
+ # Add constructor kwargs here (if any).
96
+ }
97
+
98
+ super().__init__(algo_class=algo_class or APPO)
99
+
100
+ # fmt: off
101
+ # __sphinx_doc_begin__
102
+ # APPO specific settings:
103
+ self.vtrace = True
104
+ self.use_gae = True
105
+ self.lambda_ = 1.0
106
+ self.clip_param = 0.4
107
+ self.use_kl_loss = False
108
+ self.kl_coeff = 1.0
109
+ self.kl_target = 0.01
110
+ self.target_worker_clipping = 2.0
111
+
112
+ # Circular replay buffer settings.
113
+ # Used in [1] for discrete action tasks:
114
+ # `circular_buffer_num_batches=4` and `circular_buffer_iterations_per_batch=2`
115
+ # For cont. action tasks:
116
+ # `circular_buffer_num_batches=16` and `circular_buffer_iterations_per_batch=20`
117
+ self.circular_buffer_num_batches = 4
118
+ self.circular_buffer_iterations_per_batch = 2
119
+
120
+ # Override some of IMPALAConfig's default values with APPO-specific values.
121
+ self.num_env_runners = 2
122
+ self.target_network_update_freq = 2
123
+ self.broadcast_interval = 1
124
+ self.grad_clip = 40.0
125
+ # Note: Only when using enable_rl_module_and_learner=True can the clipping mode
126
+ # be configured by the user. On the old API stack, RLlib will always clip by
127
+ # global_norm, no matter the value of `grad_clip_by`.
128
+ self.grad_clip_by = "global_norm"
129
+
130
+ self.opt_type = "adam"
131
+ self.lr = 0.0005
132
+ self.decay = 0.99
133
+ self.momentum = 0.0
134
+ self.epsilon = 0.1
135
+ self.vf_loss_coeff = 0.5
136
+ self.entropy_coeff = 0.01
137
+ self.tau = 1.0
138
+ # __sphinx_doc_end__
139
+ # fmt: on
140
+
141
+ self.lr_schedule = None # @OldAPIStack
142
+ self.entropy_coeff_schedule = None # @OldAPIStack
143
+ self.num_gpus = 0 # @OldAPIStack
144
+ self.num_multi_gpu_tower_stacks = 1 # @OldAPIStack
145
+ self.minibatch_buffer_size = 1 # @OldAPIStack
146
+ self.replay_proportion = 0.0 # @OldAPIStack
147
+ self.replay_buffer_num_slots = 100 # @OldAPIStack
148
+ self.learner_queue_size = 16 # @OldAPIStack
149
+ self.learner_queue_timeout = 300 # @OldAPIStack
150
+
151
+ # Deprecated keys.
152
+ self.target_update_frequency = DEPRECATED_VALUE
153
+ self.use_critic = DEPRECATED_VALUE
154
+
155
+ @override(IMPALAConfig)
156
+ def training(
157
+ self,
158
+ *,
159
+ vtrace: Optional[bool] = NotProvided,
160
+ use_gae: Optional[bool] = NotProvided,
161
+ lambda_: Optional[float] = NotProvided,
162
+ clip_param: Optional[float] = NotProvided,
163
+ use_kl_loss: Optional[bool] = NotProvided,
164
+ kl_coeff: Optional[float] = NotProvided,
165
+ kl_target: Optional[float] = NotProvided,
166
+ target_network_update_freq: Optional[int] = NotProvided,
167
+ tau: Optional[float] = NotProvided,
168
+ target_worker_clipping: Optional[float] = NotProvided,
169
+ circular_buffer_num_batches: Optional[int] = NotProvided,
170
+ circular_buffer_iterations_per_batch: Optional[int] = NotProvided,
171
+ # Deprecated keys.
172
+ target_update_frequency=DEPRECATED_VALUE,
173
+ use_critic=DEPRECATED_VALUE,
174
+ **kwargs,
175
+ ) -> Self:
176
+ """Sets the training related configuration.
177
+
178
+ Args:
179
+ vtrace: Whether to use V-trace weighted advantages. If false, PPO GAE
180
+ advantages will be used instead.
181
+ use_gae: If true, use the Generalized Advantage Estimator (GAE)
182
+ with a value function, see https://arxiv.org/pdf/1506.02438.pdf.
183
+ Only applies if vtrace=False.
184
+ lambda_: GAE (lambda) parameter.
185
+ clip_param: PPO surrogate slipping parameter.
186
+ use_kl_loss: Whether to use the KL-term in the loss function.
187
+ kl_coeff: Coefficient for weighting the KL-loss term.
188
+ kl_target: Target term for the KL-term to reach (via adjusting the
189
+ `kl_coeff` automatically).
190
+ target_network_update_freq: NOTE: This parameter is only applicable on
191
+ the new API stack. The frequency with which to update the target
192
+ policy network from the main trained policy network. The metric
193
+ used is `NUM_ENV_STEPS_TRAINED_LIFETIME` and the unit is `n` (see [1]
194
+ 4.1.1), where: `n = [circular_buffer_num_batches (N)] *
195
+ [circular_buffer_iterations_per_batch (K)] * [train batch size]`
196
+ For example, if you set `target_network_update_freq=2`, and N=4, K=2,
197
+ and `train_batch_size_per_learner=500`, then the target net is updated
198
+ every 2*4*2*500=8000 trained env steps (every 16 batch updates on each
199
+ learner).
200
+ The authors in [1] suggests that this setting is robust to a range of
201
+ choices (try values between 0.125 and 4).
202
+ target_network_update_freq: The frequency to update the target policy and
203
+ tune the kl loss coefficients that are used during training. After
204
+ setting this parameter, the algorithm waits for at least
205
+ `target_network_update_freq` number of environment samples to be trained
206
+ on before updating the target networks and tune the kl loss
207
+ coefficients. NOTE: This parameter is only applicable when using the
208
+ Learner API (enable_rl_module_and_learner=True).
209
+ tau: The factor by which to update the target policy network towards
210
+ the current policy network. Can range between 0 and 1.
211
+ e.g. updated_param = tau * current_param + (1 - tau) * target_param
212
+ target_worker_clipping: The maximum value for the target-worker-clipping
213
+ used for computing the IS ratio, described in [1]
214
+ IS = min(π(i) / π(target), ρ) * (π / π(i))
215
+ circular_buffer_num_batches: The number of train batches that fit
216
+ into the circular buffer. Each such train batch can be sampled for
217
+ training max. `circular_buffer_iterations_per_batch` times.
218
+ circular_buffer_iterations_per_batch: The number of times any train
219
+ batch in the circular buffer can be sampled for training. A batch gets
220
+ evicted from the buffer either if it's the oldest batch in the buffer
221
+ and a new batch is added OR if the batch reaches this max. number of
222
+ being sampled.
223
+
224
+ Returns:
225
+ This updated AlgorithmConfig object.
226
+ """
227
+ if target_update_frequency != DEPRECATED_VALUE:
228
+ deprecation_warning(
229
+ old="target_update_frequency",
230
+ new="target_network_update_freq",
231
+ error=True,
232
+ )
233
+ if use_critic != DEPRECATED_VALUE:
234
+ deprecation_warning(
235
+ old="use_critic",
236
+ help="`use_critic` no longer supported! APPO always uses a value "
237
+ "function (critic).",
238
+ error=True,
239
+ )
240
+
241
+ # Pass kwargs onto super's `training()` method.
242
+ super().training(**kwargs)
243
+
244
+ if vtrace is not NotProvided:
245
+ self.vtrace = vtrace
246
+ if use_gae is not NotProvided:
247
+ self.use_gae = use_gae
248
+ if lambda_ is not NotProvided:
249
+ self.lambda_ = lambda_
250
+ if clip_param is not NotProvided:
251
+ self.clip_param = clip_param
252
+ if use_kl_loss is not NotProvided:
253
+ self.use_kl_loss = use_kl_loss
254
+ if kl_coeff is not NotProvided:
255
+ self.kl_coeff = kl_coeff
256
+ if kl_target is not NotProvided:
257
+ self.kl_target = kl_target
258
+ if target_network_update_freq is not NotProvided:
259
+ self.target_network_update_freq = target_network_update_freq
260
+ if tau is not NotProvided:
261
+ self.tau = tau
262
+ if target_worker_clipping is not NotProvided:
263
+ self.target_worker_clipping = target_worker_clipping
264
+ if circular_buffer_num_batches is not NotProvided:
265
+ self.circular_buffer_num_batches = circular_buffer_num_batches
266
+ if circular_buffer_iterations_per_batch is not NotProvided:
267
+ self.circular_buffer_iterations_per_batch = (
268
+ circular_buffer_iterations_per_batch
269
+ )
270
+
271
+ return self
272
+
273
+ @override(IMPALAConfig)
274
+ def validate(self) -> None:
275
+ super().validate()
276
+
277
+ # On new API stack, circular buffer should be used, not `minibatch_buffer_size`.
278
+ if self.enable_rl_module_and_learner:
279
+ if self.minibatch_buffer_size != 1 or self.replay_proportion != 0.0:
280
+ self._value_error(
281
+ "`minibatch_buffer_size/replay_proportion` not valid on new API "
282
+ "stack with APPO! "
283
+ "Use `circular_buffer_num_batches` for the number of train batches "
284
+ "in the circular buffer. To change the maximum number of times "
285
+ "any batch may be sampled, set "
286
+ "`circular_buffer_iterations_per_batch`."
287
+ )
288
+ if self.num_multi_gpu_tower_stacks != 1:
289
+ self._value_error(
290
+ "`num_multi_gpu_tower_stacks` not supported on new API stack with "
291
+ "APPO! In order to train on multi-GPU, use "
292
+ "`config.learners(num_learners=[number of GPUs], "
293
+ "num_gpus_per_learner=1)`. To scale the throughput of batch-to-GPU-"
294
+ "pre-loading on each of your `Learners`, set "
295
+ "`num_gpu_loader_threads` to a higher number (recommended values: "
296
+ "1-8)."
297
+ )
298
+ if self.learner_queue_size != 16:
299
+ self._value_error(
300
+ "`learner_queue_size` not supported on new API stack with "
301
+ "APPO! In order set the size of the circular buffer (which acts as "
302
+ "a 'learner queue'), use "
303
+ "`config.training(circular_buffer_num_batches=..)`. To change the "
304
+ "maximum number of times any batch may be sampled, set "
305
+ "`config.training(circular_buffer_iterations_per_batch=..)`."
306
+ )
307
+
308
+ @override(IMPALAConfig)
309
+ def get_default_learner_class(self):
310
+ if self.framework_str == "torch":
311
+ from ray.rllib.algorithms.appo.torch.appo_torch_learner import (
312
+ APPOTorchLearner,
313
+ )
314
+
315
+ return APPOTorchLearner
316
+ elif self.framework_str in ["tf2", "tf"]:
317
+ raise ValueError(
318
+ "TensorFlow is no longer supported on the new API stack! "
319
+ "Use `framework='torch'`."
320
+ )
321
+ else:
322
+ raise ValueError(
323
+ f"The framework {self.framework_str} is not supported. "
324
+ "Use `framework='torch'`."
325
+ )
326
+
327
+ @override(IMPALAConfig)
328
+ def get_default_rl_module_spec(self) -> RLModuleSpec:
329
+ if self.framework_str == "torch":
330
+ from ray.rllib.algorithms.appo.torch.appo_torch_rl_module import (
331
+ APPOTorchRLModule as RLModule,
332
+ )
333
+ else:
334
+ raise ValueError(
335
+ f"The framework {self.framework_str} is not supported. "
336
+ "Use either 'torch' or 'tf2'."
337
+ )
338
+
339
+ return RLModuleSpec(module_class=RLModule)
340
+
341
+ @property
342
+ @override(AlgorithmConfig)
343
+ def _model_config_auto_includes(self):
344
+ return super()._model_config_auto_includes | {"vf_share_layers": False}
345
+
346
+
347
+ class APPO(IMPALA):
348
+ def __init__(self, config, *args, **kwargs):
349
+ """Initializes an APPO instance."""
350
+ super().__init__(config, *args, **kwargs)
351
+
352
+ # After init: Initialize target net.
353
+
354
+ # TODO(avnishn): Does this need to happen in __init__? I think we can move it
355
+ # to setup()
356
+ if not self.config.enable_rl_module_and_learner:
357
+ self.env_runner.foreach_policy_to_train(lambda p, _: p.update_target())
358
+
359
+ @override(IMPALA)
360
+ def training_step(self) -> None:
361
+ if self.config.enable_rl_module_and_learner:
362
+ return super().training_step()
363
+
364
+ train_results = super().training_step()
365
+ # Update the target network and the KL coefficient for the APPO-loss.
366
+ # The target network update frequency is calculated automatically by the product
367
+ # of `num_epochs` setting (usually 1 for APPO) and `minibatch_buffer_size`.
368
+ last_update = self._counters[LAST_TARGET_UPDATE_TS]
369
+ cur_ts = self._counters[
370
+ (
371
+ NUM_AGENT_STEPS_SAMPLED
372
+ if self.config.count_steps_by == "agent_steps"
373
+ else NUM_ENV_STEPS_SAMPLED
374
+ )
375
+ ]
376
+ target_update_freq = self.config.num_epochs * self.config.minibatch_buffer_size
377
+ if cur_ts - last_update > target_update_freq:
378
+ self._counters[NUM_TARGET_UPDATES] += 1
379
+ self._counters[LAST_TARGET_UPDATE_TS] = cur_ts
380
+
381
+ # Update our target network.
382
+ self.env_runner.foreach_policy_to_train(lambda p, _: p.update_target())
383
+
384
+ # Also update the KL-coefficient for the APPO loss, if necessary.
385
+ if self.config.use_kl_loss:
386
+
387
+ def update(pi, pi_id):
388
+ assert LEARNER_STATS_KEY not in train_results, (
389
+ "{} should be nested under policy id key".format(
390
+ LEARNER_STATS_KEY
391
+ ),
392
+ train_results,
393
+ )
394
+ if pi_id in train_results:
395
+ kl = train_results[pi_id][LEARNER_STATS_KEY].get("kl")
396
+ assert kl is not None, (train_results, pi_id)
397
+ # Make the actual `Policy.update_kl()` call.
398
+ pi.update_kl(kl)
399
+ else:
400
+ logger.warning("No data for {}, not updating kl".format(pi_id))
401
+
402
+ # Update KL on all trainable policies within the local (trainer)
403
+ # Worker.
404
+ self.env_runner.foreach_policy_to_train(update)
405
+
406
+ return train_results
407
+
408
+ @classmethod
409
+ @override(IMPALA)
410
+ def get_default_config(cls) -> APPOConfig:
411
+ return APPOConfig()
412
+
413
+ @classmethod
414
+ @override(IMPALA)
415
+ def get_default_policy_class(
416
+ cls, config: AlgorithmConfig
417
+ ) -> Optional[Type[Policy]]:
418
+ if config["framework"] == "torch":
419
+ from ray.rllib.algorithms.appo.appo_torch_policy import APPOTorchPolicy
420
+
421
+ return APPOTorchPolicy
422
+ elif config["framework"] == "tf":
423
+ if config.enable_rl_module_and_learner:
424
+ raise ValueError(
425
+ "RLlib's RLModule and Learner API is not supported for"
426
+ " tf1. Use "
427
+ "framework='tf2' instead."
428
+ )
429
+ from ray.rllib.algorithms.appo.appo_tf_policy import APPOTF1Policy
430
+
431
+ return APPOTF1Policy
432
+ else:
433
+ from ray.rllib.algorithms.appo.appo_tf_policy import APPOTF2Policy
434
+
435
+ return APPOTF2Policy
lib/python3.12/site-packages/ray/rllib/algorithms/appo/appo_learner.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import abc
2
+ from typing import Any, Dict, Optional
3
+
4
+ from ray.rllib.algorithms.appo.appo import APPOConfig
5
+ from ray.rllib.algorithms.appo.utils import CircularBuffer
6
+ from ray.rllib.algorithms.impala.impala_learner import IMPALALearner
7
+ from ray.rllib.core.learner.learner import Learner
8
+ from ray.rllib.core.learner.utils import update_target_network
9
+ from ray.rllib.core.rl_module.apis import TargetNetworkAPI, ValueFunctionAPI
10
+ from ray.rllib.core.rl_module.multi_rl_module import MultiRLModuleSpec
11
+ from ray.rllib.core.rl_module.rl_module import RLModuleSpec
12
+ from ray.rllib.utils.annotations import override
13
+ from ray.rllib.utils.lambda_defaultdict import LambdaDefaultDict
14
+ from ray.rllib.utils.metrics import (
15
+ LAST_TARGET_UPDATE_TS,
16
+ NUM_ENV_STEPS_TRAINED_LIFETIME,
17
+ NUM_MODULE_STEPS_TRAINED,
18
+ NUM_TARGET_UPDATES,
19
+ )
20
+ from ray.rllib.utils.schedules.scheduler import Scheduler
21
+ from ray.rllib.utils.typing import ModuleID, ShouldModuleBeUpdatedFn
22
+
23
+
24
+ class APPOLearner(IMPALALearner):
25
+ """Adds KL coeff updates via `after_gradient_based_update()` to IMPALA logic.
26
+
27
+ Framework-specific subclasses must override `_update_module_kl_coeff()`.
28
+ """
29
+
30
+ @override(IMPALALearner)
31
+ def build(self):
32
+ self._learner_thread_in_queue = CircularBuffer(
33
+ num_batches=self.config.circular_buffer_num_batches,
34
+ iterations_per_batch=self.config.circular_buffer_iterations_per_batch,
35
+ )
36
+
37
+ super().build()
38
+
39
+ # Make target networks.
40
+ self.module.foreach_module(
41
+ lambda mid, mod: (
42
+ mod.make_target_networks()
43
+ if isinstance(mod, TargetNetworkAPI)
44
+ else None
45
+ )
46
+ )
47
+
48
+ # The current kl coefficients per module as (framework specific) tensor
49
+ # variables.
50
+ self.curr_kl_coeffs_per_module: LambdaDefaultDict[
51
+ ModuleID, Scheduler
52
+ ] = LambdaDefaultDict(
53
+ lambda module_id: self._get_tensor_variable(
54
+ self.config.get_config_for_module(module_id).kl_coeff
55
+ )
56
+ )
57
+
58
+ @override(Learner)
59
+ def add_module(
60
+ self,
61
+ *,
62
+ module_id: ModuleID,
63
+ module_spec: RLModuleSpec,
64
+ config_overrides: Optional[Dict] = None,
65
+ new_should_module_be_updated: Optional[ShouldModuleBeUpdatedFn] = None,
66
+ ) -> MultiRLModuleSpec:
67
+ marl_spec = super().add_module(
68
+ module_id=module_id,
69
+ module_spec=module_spec,
70
+ config_overrides=config_overrides,
71
+ new_should_module_be_updated=new_should_module_be_updated,
72
+ )
73
+ # Create target networks for added Module, if applicable.
74
+ if isinstance(self.module[module_id].unwrapped(), TargetNetworkAPI):
75
+ self.module[module_id].unwrapped().make_target_networks()
76
+ return marl_spec
77
+
78
+ @override(IMPALALearner)
79
+ def remove_module(self, module_id: str) -> MultiRLModuleSpec:
80
+ marl_spec = super().remove_module(module_id)
81
+ self.curr_kl_coeffs_per_module.pop(module_id)
82
+ return marl_spec
83
+
84
+ @override(Learner)
85
+ def after_gradient_based_update(self, *, timesteps: Dict[str, Any]) -> None:
86
+ """Updates the target Q Networks."""
87
+ super().after_gradient_based_update(timesteps=timesteps)
88
+
89
+ # TODO (sven): Maybe we should have a `after_gradient_based_update`
90
+ # method per module?
91
+ curr_timestep = timesteps.get(NUM_ENV_STEPS_TRAINED_LIFETIME, 0)
92
+ for module_id, module in self.module._rl_modules.items():
93
+ config = self.config.get_config_for_module(module_id)
94
+
95
+ last_update_ts_key = (module_id, LAST_TARGET_UPDATE_TS)
96
+ if isinstance(module.unwrapped(), TargetNetworkAPI) and (
97
+ curr_timestep - self.metrics.peek(last_update_ts_key, default=0)
98
+ >= (
99
+ config.target_network_update_freq
100
+ * config.circular_buffer_num_batches
101
+ * config.circular_buffer_iterations_per_batch
102
+ * config.train_batch_size_per_learner
103
+ )
104
+ ):
105
+ for (
106
+ main_net,
107
+ target_net,
108
+ ) in module.unwrapped().get_target_network_pairs():
109
+ update_target_network(
110
+ main_net=main_net,
111
+ target_net=target_net,
112
+ tau=config.tau,
113
+ )
114
+ # Increase lifetime target network update counter by one.
115
+ self.metrics.log_value((module_id, NUM_TARGET_UPDATES), 1, reduce="sum")
116
+ # Update the (single-value -> window=1) last updated timestep metric.
117
+ self.metrics.log_value(last_update_ts_key, curr_timestep, window=1)
118
+
119
+ if (
120
+ config.use_kl_loss
121
+ and self.metrics.peek((module_id, NUM_MODULE_STEPS_TRAINED), default=0)
122
+ > 0
123
+ ):
124
+ self._update_module_kl_coeff(module_id=module_id, config=config)
125
+
126
+ @classmethod
127
+ @override(Learner)
128
+ def rl_module_required_apis(cls) -> list[type]:
129
+ # In order for a PPOLearner to update an RLModule, it must implement the
130
+ # following APIs:
131
+ return [TargetNetworkAPI, ValueFunctionAPI]
132
+
133
+ @abc.abstractmethod
134
+ def _update_module_kl_coeff(self, module_id: ModuleID, config: APPOConfig) -> None:
135
+ """Dynamically update the KL loss coefficients of each module.
136
+
137
+ The update is completed using the mean KL divergence between the action
138
+ distributions current policy and old policy of each module. That action
139
+ distribution is computed during the most recent update/call to `compute_loss`.
140
+
141
+ Args:
142
+ module_id: The module whose KL loss coefficient to update.
143
+ config: The AlgorithmConfig specific to the given `module_id`.
144
+ """
145
+
146
+
147
+ AppoLearner = APPOLearner
lib/python3.12/site-packages/ray/rllib/algorithms/appo/appo_rl_module.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Backward compat import.
2
+ from ray.rllib.algorithms.appo.default_appo_rl_module import ( # noqa
3
+ DefaultAPPORLModule as APPORLModule,
4
+ )
5
+ from ray._common.deprecation import deprecation_warning
6
+
7
+ deprecation_warning(
8
+ old="ray.rllib.algorithms.appo.appo_rl_module.APPORLModule",
9
+ new="ray.rllib.algorithms.appo.default_appo_rl_module.DefaultAPPORLModule",
10
+ error=False,
11
+ )
lib/python3.12/site-packages/ray/rllib/algorithms/appo/appo_tf_policy.py ADDED
@@ -0,0 +1,393 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ TensorFlow policy class used for APPO.
3
+
4
+ Adapted from VTraceTFPolicy to use the PPO surrogate loss.
5
+ Keep in sync with changes to VTraceTFPolicy.
6
+ """
7
+
8
+ import logging
9
+ from typing import Dict, List, Optional, Type, Union
10
+
11
+ import gymnasium as gym
12
+ import numpy as np
13
+
14
+ from ray.rllib.algorithms.appo.utils import make_appo_models
15
+ from ray.rllib.algorithms.impala import vtrace_tf as vtrace
16
+ from ray.rllib.algorithms.impala.impala_tf_policy import (
17
+ VTraceClipGradients,
18
+ VTraceOptimizer,
19
+ _make_time_major,
20
+ )
21
+ from ray.rllib.evaluation.postprocessing import (
22
+ Postprocessing,
23
+ compute_bootstrap_value,
24
+ compute_gae_for_sample_batch,
25
+ )
26
+ from ray.rllib.models.modelv2 import ModelV2
27
+ from ray.rllib.models.tf.tf_action_dist import Categorical, TFActionDistribution
28
+ from ray.rllib.policy.dynamic_tf_policy_v2 import DynamicTFPolicyV2
29
+ from ray.rllib.policy.eager_tf_policy_v2 import EagerTFPolicyV2
30
+ from ray.rllib.policy.sample_batch import SampleBatch
31
+ from ray.rllib.policy.tf_mixins import (
32
+ EntropyCoeffSchedule,
33
+ GradStatsMixin,
34
+ KLCoeffMixin,
35
+ LearningRateSchedule,
36
+ TargetNetworkMixin,
37
+ ValueNetworkMixin,
38
+ )
39
+ from ray.rllib.utils.annotations import (
40
+ override,
41
+ )
42
+ from ray.rllib.utils.framework import try_import_tf
43
+ from ray.rllib.utils.tf_utils import explained_variance
44
+ from ray.rllib.utils.typing import TensorType
45
+
46
+ tf1, tf, tfv = try_import_tf()
47
+
48
+ logger = logging.getLogger(__name__)
49
+
50
+
51
+ # TODO (sven): Deprecate once APPO and IMPALA fully on RLModules/Learner APIs.
52
+ def get_appo_tf_policy(name: str, base: type) -> type:
53
+ """Construct an APPOTFPolicy inheriting either dynamic or eager base policies.
54
+
55
+ Args:
56
+ base: Base class for this policy. DynamicTFPolicyV2 or EagerTFPolicyV2.
57
+
58
+ Returns:
59
+ A TF Policy to be used with Impala.
60
+ """
61
+
62
+ class APPOTFPolicy(
63
+ VTraceClipGradients,
64
+ VTraceOptimizer,
65
+ LearningRateSchedule,
66
+ KLCoeffMixin,
67
+ EntropyCoeffSchedule,
68
+ ValueNetworkMixin,
69
+ TargetNetworkMixin,
70
+ GradStatsMixin,
71
+ base,
72
+ ):
73
+ def __init__(
74
+ self,
75
+ observation_space,
76
+ action_space,
77
+ config,
78
+ existing_model=None,
79
+ existing_inputs=None,
80
+ ):
81
+ # First thing first, enable eager execution if necessary.
82
+ base.enable_eager_execution_if_necessary()
83
+
84
+ # Although this is a no-op, we call __init__ here to make it clear
85
+ # that base.__init__ will use the make_model() call.
86
+ VTraceClipGradients.__init__(self)
87
+ VTraceOptimizer.__init__(self)
88
+
89
+ # Initialize base class.
90
+ base.__init__(
91
+ self,
92
+ observation_space,
93
+ action_space,
94
+ config,
95
+ existing_inputs=existing_inputs,
96
+ existing_model=existing_model,
97
+ )
98
+
99
+ # TF LearningRateSchedule depends on self.framework, so initialize
100
+ # after base.__init__() is called.
101
+ LearningRateSchedule.__init__(self, config["lr"], config["lr_schedule"])
102
+ EntropyCoeffSchedule.__init__(
103
+ self, config["entropy_coeff"], config["entropy_coeff_schedule"]
104
+ )
105
+ ValueNetworkMixin.__init__(self, config)
106
+ KLCoeffMixin.__init__(self, config)
107
+
108
+ GradStatsMixin.__init__(self)
109
+
110
+ # Note: this is a bit ugly, but loss and optimizer initialization must
111
+ # happen after all the MixIns are initialized.
112
+ self.maybe_initialize_optimizer_and_loss()
113
+
114
+ # Initiate TargetNetwork ops after loss initialization.
115
+ TargetNetworkMixin.__init__(self)
116
+
117
+ @override(base)
118
+ def make_model(self) -> ModelV2:
119
+ return make_appo_models(self)
120
+
121
+ @override(base)
122
+ def loss(
123
+ self,
124
+ model: Union[ModelV2, "tf.keras.Model"],
125
+ dist_class: Type[TFActionDistribution],
126
+ train_batch: SampleBatch,
127
+ ) -> Union[TensorType, List[TensorType]]:
128
+ model_out, _ = model(train_batch)
129
+ action_dist = dist_class(model_out, model)
130
+
131
+ if isinstance(self.action_space, gym.spaces.Discrete):
132
+ is_multidiscrete = False
133
+ output_hidden_shape = [self.action_space.n]
134
+ elif isinstance(self.action_space, gym.spaces.multi_discrete.MultiDiscrete):
135
+ is_multidiscrete = True
136
+ output_hidden_shape = self.action_space.nvec.astype(np.int32)
137
+ else:
138
+ is_multidiscrete = False
139
+ output_hidden_shape = 1
140
+
141
+ def make_time_major(*args, **kw):
142
+ return _make_time_major(
143
+ self, train_batch.get(SampleBatch.SEQ_LENS), *args, **kw
144
+ )
145
+
146
+ actions = train_batch[SampleBatch.ACTIONS]
147
+ dones = train_batch[SampleBatch.TERMINATEDS]
148
+ rewards = train_batch[SampleBatch.REWARDS]
149
+ behaviour_logits = train_batch[SampleBatch.ACTION_DIST_INPUTS]
150
+
151
+ target_model_out, _ = self.target_model(train_batch)
152
+ prev_action_dist = dist_class(behaviour_logits, self.model)
153
+ values = self.model.value_function()
154
+ values_time_major = make_time_major(values)
155
+ bootstrap_values_time_major = make_time_major(
156
+ train_batch[SampleBatch.VALUES_BOOTSTRAPPED]
157
+ )
158
+ bootstrap_value = bootstrap_values_time_major[-1]
159
+
160
+ if self.is_recurrent():
161
+ max_seq_len = tf.reduce_max(train_batch[SampleBatch.SEQ_LENS])
162
+ mask = tf.sequence_mask(train_batch[SampleBatch.SEQ_LENS], max_seq_len)
163
+ mask = tf.reshape(mask, [-1])
164
+ mask = make_time_major(mask)
165
+
166
+ def reduce_mean_valid(t):
167
+ return tf.reduce_mean(tf.boolean_mask(t, mask))
168
+
169
+ else:
170
+ reduce_mean_valid = tf.reduce_mean
171
+
172
+ if self.config["vtrace"]:
173
+ logger.debug("Using V-Trace surrogate loss (vtrace=True)")
174
+
175
+ # Prepare actions for loss.
176
+ loss_actions = (
177
+ actions if is_multidiscrete else tf.expand_dims(actions, axis=1)
178
+ )
179
+
180
+ old_policy_behaviour_logits = tf.stop_gradient(target_model_out)
181
+ old_policy_action_dist = dist_class(old_policy_behaviour_logits, model)
182
+
183
+ # Prepare KL for Loss
184
+ mean_kl = make_time_major(old_policy_action_dist.multi_kl(action_dist))
185
+
186
+ unpacked_behaviour_logits = tf.split(
187
+ behaviour_logits, output_hidden_shape, axis=1
188
+ )
189
+ unpacked_old_policy_behaviour_logits = tf.split(
190
+ old_policy_behaviour_logits, output_hidden_shape, axis=1
191
+ )
192
+
193
+ # Compute vtrace on the CPU for better perf.
194
+ with tf.device("/cpu:0"):
195
+ vtrace_returns = vtrace.multi_from_logits(
196
+ behaviour_policy_logits=make_time_major(
197
+ unpacked_behaviour_logits
198
+ ),
199
+ target_policy_logits=make_time_major(
200
+ unpacked_old_policy_behaviour_logits
201
+ ),
202
+ actions=tf.unstack(make_time_major(loss_actions), axis=2),
203
+ discounts=tf.cast(
204
+ ~make_time_major(tf.cast(dones, tf.bool)),
205
+ tf.float32,
206
+ )
207
+ * self.config["gamma"],
208
+ rewards=make_time_major(rewards),
209
+ values=values_time_major,
210
+ bootstrap_value=bootstrap_value,
211
+ dist_class=Categorical if is_multidiscrete else dist_class,
212
+ model=model,
213
+ clip_rho_threshold=tf.cast(
214
+ self.config["vtrace_clip_rho_threshold"], tf.float32
215
+ ),
216
+ clip_pg_rho_threshold=tf.cast(
217
+ self.config["vtrace_clip_pg_rho_threshold"], tf.float32
218
+ ),
219
+ )
220
+
221
+ actions_logp = make_time_major(action_dist.logp(actions))
222
+ prev_actions_logp = make_time_major(prev_action_dist.logp(actions))
223
+ old_policy_actions_logp = make_time_major(
224
+ old_policy_action_dist.logp(actions)
225
+ )
226
+
227
+ is_ratio = tf.clip_by_value(
228
+ tf.math.exp(prev_actions_logp - old_policy_actions_logp), 0.0, 2.0
229
+ )
230
+ logp_ratio = is_ratio * tf.exp(actions_logp - prev_actions_logp)
231
+ self._is_ratio = is_ratio
232
+
233
+ advantages = vtrace_returns.pg_advantages
234
+ surrogate_loss = tf.minimum(
235
+ advantages * logp_ratio,
236
+ advantages
237
+ * tf.clip_by_value(
238
+ logp_ratio,
239
+ 1 - self.config["clip_param"],
240
+ 1 + self.config["clip_param"],
241
+ ),
242
+ )
243
+
244
+ action_kl = (
245
+ tf.reduce_mean(mean_kl, axis=0) if is_multidiscrete else mean_kl
246
+ )
247
+ mean_kl_loss = reduce_mean_valid(action_kl)
248
+ mean_policy_loss = -reduce_mean_valid(surrogate_loss)
249
+
250
+ # The value function loss.
251
+ value_targets = vtrace_returns.vs
252
+ delta = values_time_major - value_targets
253
+ mean_vf_loss = 0.5 * reduce_mean_valid(tf.math.square(delta))
254
+
255
+ # The entropy loss.
256
+ actions_entropy = make_time_major(action_dist.multi_entropy())
257
+ mean_entropy = reduce_mean_valid(actions_entropy)
258
+
259
+ else:
260
+ logger.debug("Using PPO surrogate loss (vtrace=False)")
261
+
262
+ # Prepare KL for Loss
263
+ mean_kl = make_time_major(prev_action_dist.multi_kl(action_dist))
264
+
265
+ logp_ratio = tf.math.exp(
266
+ make_time_major(action_dist.logp(actions))
267
+ - make_time_major(prev_action_dist.logp(actions))
268
+ )
269
+
270
+ advantages = make_time_major(train_batch[Postprocessing.ADVANTAGES])
271
+ surrogate_loss = tf.minimum(
272
+ advantages * logp_ratio,
273
+ advantages
274
+ * tf.clip_by_value(
275
+ logp_ratio,
276
+ 1 - self.config["clip_param"],
277
+ 1 + self.config["clip_param"],
278
+ ),
279
+ )
280
+
281
+ action_kl = (
282
+ tf.reduce_mean(mean_kl, axis=0) if is_multidiscrete else mean_kl
283
+ )
284
+ mean_kl_loss = reduce_mean_valid(action_kl)
285
+ mean_policy_loss = -reduce_mean_valid(surrogate_loss)
286
+
287
+ # The value function loss.
288
+ value_targets = make_time_major(
289
+ train_batch[Postprocessing.VALUE_TARGETS]
290
+ )
291
+ delta = values_time_major - value_targets
292
+ mean_vf_loss = 0.5 * reduce_mean_valid(tf.math.square(delta))
293
+
294
+ # The entropy loss.
295
+ mean_entropy = reduce_mean_valid(
296
+ make_time_major(action_dist.multi_entropy())
297
+ )
298
+
299
+ # The summed weighted loss.
300
+ total_loss = mean_policy_loss - mean_entropy * self.entropy_coeff
301
+ # Optional KL loss.
302
+ if self.config["use_kl_loss"]:
303
+ total_loss += self.kl_coeff * mean_kl_loss
304
+ # Optional vf loss (or in a separate term due to separate
305
+ # optimizers/networks).
306
+ loss_wo_vf = total_loss
307
+ if not self.config["_separate_vf_optimizer"]:
308
+ total_loss += mean_vf_loss * self.config["vf_loss_coeff"]
309
+
310
+ # Store stats in policy for stats_fn.
311
+ self._total_loss = total_loss
312
+ self._loss_wo_vf = loss_wo_vf
313
+ self._mean_policy_loss = mean_policy_loss
314
+ # Backward compatibility: Deprecate policy._mean_kl.
315
+ self._mean_kl_loss = self._mean_kl = mean_kl_loss
316
+ self._mean_vf_loss = mean_vf_loss
317
+ self._mean_entropy = mean_entropy
318
+ self._value_targets = value_targets
319
+
320
+ # Return one total loss or two losses: vf vs rest (policy + kl).
321
+ if self.config["_separate_vf_optimizer"]:
322
+ return loss_wo_vf, mean_vf_loss
323
+ else:
324
+ return total_loss
325
+
326
+ @override(base)
327
+ def stats_fn(self, train_batch: SampleBatch) -> Dict[str, TensorType]:
328
+ values_batched = _make_time_major(
329
+ self,
330
+ train_batch.get(SampleBatch.SEQ_LENS),
331
+ self.model.value_function(),
332
+ )
333
+
334
+ stats_dict = {
335
+ "cur_lr": tf.cast(self.cur_lr, tf.float64),
336
+ "total_loss": self._total_loss,
337
+ "policy_loss": self._mean_policy_loss,
338
+ "entropy": self._mean_entropy,
339
+ "var_gnorm": tf.linalg.global_norm(self.model.trainable_variables()),
340
+ "vf_loss": self._mean_vf_loss,
341
+ "vf_explained_var": explained_variance(
342
+ tf.reshape(self._value_targets, [-1]),
343
+ tf.reshape(values_batched, [-1]),
344
+ ),
345
+ "entropy_coeff": tf.cast(self.entropy_coeff, tf.float64),
346
+ }
347
+
348
+ if self.config["vtrace"]:
349
+ is_stat_mean, is_stat_var = tf.nn.moments(self._is_ratio, [0, 1])
350
+ stats_dict["mean_IS"] = is_stat_mean
351
+ stats_dict["var_IS"] = is_stat_var
352
+
353
+ if self.config["use_kl_loss"]:
354
+ stats_dict["kl"] = self._mean_kl_loss
355
+ stats_dict["KL_Coeff"] = self.kl_coeff
356
+
357
+ return stats_dict
358
+
359
+ @override(base)
360
+ def postprocess_trajectory(
361
+ self,
362
+ sample_batch: SampleBatch,
363
+ other_agent_batches: Optional[SampleBatch] = None,
364
+ episode=None,
365
+ ):
366
+ # Call super's postprocess_trajectory first.
367
+ # sample_batch = super().postprocess_trajectory(
368
+ # sample_batch, other_agent_batches, episode
369
+ # )
370
+
371
+ if not self.config["vtrace"]:
372
+ sample_batch = compute_gae_for_sample_batch(
373
+ self, sample_batch, other_agent_batches, episode
374
+ )
375
+ else:
376
+ # Add the Columns.VALUES_BOOTSTRAPPED column, which we'll need
377
+ # inside the loss for vtrace calculations.
378
+ sample_batch = compute_bootstrap_value(sample_batch, self)
379
+
380
+ return sample_batch
381
+
382
+ @override(base)
383
+ def get_batch_divisibility_req(self) -> int:
384
+ return self.config["rollout_fragment_length"]
385
+
386
+ APPOTFPolicy.__name__ = name
387
+ APPOTFPolicy.__qualname__ = name
388
+
389
+ return APPOTFPolicy
390
+
391
+
392
+ APPOTF1Policy = get_appo_tf_policy("APPOTF1Policy", DynamicTFPolicyV2)
393
+ APPOTF2Policy = get_appo_tf_policy("APPOTF2Policy", EagerTFPolicyV2)
lib/python3.12/site-packages/ray/rllib/algorithms/appo/appo_torch_policy.py ADDED
@@ -0,0 +1,413 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ PyTorch policy class used for APPO.
3
+
4
+ Adapted from VTraceTFPolicy to use the PPO surrogate loss.
5
+ Keep in sync with changes to VTraceTFPolicy.
6
+ """
7
+
8
+ import logging
9
+ from typing import Any, Dict, List, Optional, Type, Union
10
+
11
+ import gymnasium as gym
12
+ import numpy as np
13
+
14
+ import ray
15
+ import ray.rllib.algorithms.impala.vtrace_torch as vtrace
16
+ from ray.rllib.algorithms.appo.utils import make_appo_models
17
+ from ray.rllib.algorithms.impala.impala_torch_policy import (
18
+ VTraceOptimizer,
19
+ make_time_major,
20
+ )
21
+ from ray.rllib.evaluation.postprocessing import (
22
+ Postprocessing,
23
+ compute_bootstrap_value,
24
+ compute_gae_for_sample_batch,
25
+ )
26
+ from ray.rllib.models.action_dist import ActionDistribution
27
+ from ray.rllib.models.modelv2 import ModelV2
28
+ from ray.rllib.models.torch.torch_action_dist import (
29
+ TorchCategorical,
30
+ TorchDistributionWrapper,
31
+ )
32
+ from ray.rllib.models.torch.torch_modelv2 import TorchModelV2
33
+ from ray.rllib.policy.sample_batch import SampleBatch
34
+ from ray.rllib.policy.torch_mixins import (
35
+ EntropyCoeffSchedule,
36
+ KLCoeffMixin,
37
+ LearningRateSchedule,
38
+ TargetNetworkMixin,
39
+ ValueNetworkMixin,
40
+ )
41
+ from ray.rllib.policy.torch_policy_v2 import TorchPolicyV2
42
+ from ray.rllib.utils.annotations import override
43
+ from ray.rllib.utils.framework import try_import_torch
44
+ from ray.rllib.utils.numpy import convert_to_numpy
45
+ from ray.rllib.utils.torch_utils import (
46
+ apply_grad_clipping,
47
+ explained_variance,
48
+ global_norm,
49
+ sequence_mask,
50
+ )
51
+ from ray.rllib.utils.typing import TensorType
52
+
53
+ torch, nn = try_import_torch()
54
+
55
+ logger = logging.getLogger(__name__)
56
+
57
+
58
+ # TODO (sven): Deprecate once APPO and IMPALA fully on RLModules/Learner APIs.
59
+ class APPOTorchPolicy(
60
+ VTraceOptimizer,
61
+ LearningRateSchedule,
62
+ EntropyCoeffSchedule,
63
+ KLCoeffMixin,
64
+ ValueNetworkMixin,
65
+ TargetNetworkMixin,
66
+ TorchPolicyV2,
67
+ ):
68
+ """PyTorch policy class used with APPO."""
69
+
70
+ def __init__(self, observation_space, action_space, config):
71
+ config = dict(ray.rllib.algorithms.appo.appo.APPOConfig().to_dict(), **config)
72
+ config["enable_rl_module_and_learner"] = False
73
+ config["enable_env_runner_and_connector_v2"] = False
74
+
75
+ # Although this is a no-op, we call __init__ here to make it clear
76
+ # that base.__init__ will use the make_model() call.
77
+ VTraceOptimizer.__init__(self)
78
+
79
+ lr_schedule_additional_args = []
80
+ if config.get("_separate_vf_optimizer"):
81
+ lr_schedule_additional_args = (
82
+ [config["_lr_vf"][0][1], config["_lr_vf"]]
83
+ if isinstance(config["_lr_vf"], (list, tuple))
84
+ else [config["_lr_vf"], None]
85
+ )
86
+ LearningRateSchedule.__init__(
87
+ self, config["lr"], config["lr_schedule"], *lr_schedule_additional_args
88
+ )
89
+
90
+ TorchPolicyV2.__init__(
91
+ self,
92
+ observation_space,
93
+ action_space,
94
+ config,
95
+ max_seq_len=config["model"]["max_seq_len"],
96
+ )
97
+
98
+ EntropyCoeffSchedule.__init__(
99
+ self, config["entropy_coeff"], config["entropy_coeff_schedule"]
100
+ )
101
+ ValueNetworkMixin.__init__(self, config)
102
+ KLCoeffMixin.__init__(self, config)
103
+
104
+ self._initialize_loss_from_dummy_batch()
105
+
106
+ # Initiate TargetNetwork ops after loss initialization.
107
+ TargetNetworkMixin.__init__(self)
108
+
109
+ @override(TorchPolicyV2)
110
+ def init_view_requirements(self):
111
+ self.view_requirements = self._get_default_view_requirements()
112
+
113
+ @override(TorchPolicyV2)
114
+ def make_model(self) -> ModelV2:
115
+ return make_appo_models(self)
116
+
117
+ @override(TorchPolicyV2)
118
+ def loss(
119
+ self,
120
+ model: ModelV2,
121
+ dist_class: Type[ActionDistribution],
122
+ train_batch: SampleBatch,
123
+ ) -> Union[TensorType, List[TensorType]]:
124
+ """Constructs the loss for APPO.
125
+
126
+ With IS modifications and V-trace for Advantage Estimation.
127
+
128
+ Args:
129
+ model (ModelV2): The Model to calculate the loss for.
130
+ dist_class (Type[ActionDistribution]): The action distr. class.
131
+ train_batch: The training data.
132
+
133
+ Returns:
134
+ Union[TensorType, List[TensorType]]: A single loss tensor or a list
135
+ of loss tensors.
136
+ """
137
+ target_model = self.target_models[model]
138
+
139
+ model_out, _ = model(train_batch)
140
+ action_dist = dist_class(model_out, model)
141
+
142
+ if isinstance(self.action_space, gym.spaces.Discrete):
143
+ is_multidiscrete = False
144
+ output_hidden_shape = [self.action_space.n]
145
+ elif isinstance(self.action_space, gym.spaces.multi_discrete.MultiDiscrete):
146
+ is_multidiscrete = True
147
+ output_hidden_shape = self.action_space.nvec.astype(np.int32)
148
+ else:
149
+ is_multidiscrete = False
150
+ output_hidden_shape = 1
151
+
152
+ def _make_time_major(*args, **kwargs):
153
+ return make_time_major(
154
+ self, train_batch.get(SampleBatch.SEQ_LENS), *args, **kwargs
155
+ )
156
+
157
+ actions = train_batch[SampleBatch.ACTIONS]
158
+ dones = train_batch[SampleBatch.TERMINATEDS]
159
+ rewards = train_batch[SampleBatch.REWARDS]
160
+ behaviour_logits = train_batch[SampleBatch.ACTION_DIST_INPUTS]
161
+
162
+ target_model_out, _ = target_model(train_batch)
163
+
164
+ prev_action_dist = dist_class(behaviour_logits, model)
165
+ values = model.value_function()
166
+ values_time_major = _make_time_major(values)
167
+ bootstrap_values_time_major = _make_time_major(
168
+ train_batch[SampleBatch.VALUES_BOOTSTRAPPED]
169
+ )
170
+ bootstrap_value = bootstrap_values_time_major[-1]
171
+
172
+ if self.is_recurrent():
173
+ max_seq_len = torch.max(train_batch[SampleBatch.SEQ_LENS])
174
+ mask = sequence_mask(train_batch[SampleBatch.SEQ_LENS], max_seq_len)
175
+ mask = torch.reshape(mask, [-1])
176
+ mask = _make_time_major(mask)
177
+ num_valid = torch.sum(mask)
178
+
179
+ def reduce_mean_valid(t):
180
+ return torch.sum(t[mask]) / num_valid
181
+
182
+ else:
183
+ reduce_mean_valid = torch.mean
184
+
185
+ if self.config["vtrace"]:
186
+ logger.debug("Using V-Trace surrogate loss (vtrace=True)")
187
+
188
+ old_policy_behaviour_logits = target_model_out.detach()
189
+ old_policy_action_dist = dist_class(old_policy_behaviour_logits, model)
190
+
191
+ if isinstance(output_hidden_shape, (list, tuple, np.ndarray)):
192
+ unpacked_behaviour_logits = torch.split(
193
+ behaviour_logits, list(output_hidden_shape), dim=1
194
+ )
195
+ unpacked_old_policy_behaviour_logits = torch.split(
196
+ old_policy_behaviour_logits, list(output_hidden_shape), dim=1
197
+ )
198
+ else:
199
+ unpacked_behaviour_logits = torch.chunk(
200
+ behaviour_logits, output_hidden_shape, dim=1
201
+ )
202
+ unpacked_old_policy_behaviour_logits = torch.chunk(
203
+ old_policy_behaviour_logits, output_hidden_shape, dim=1
204
+ )
205
+
206
+ # Prepare actions for loss.
207
+ loss_actions = (
208
+ actions if is_multidiscrete else torch.unsqueeze(actions, dim=1)
209
+ )
210
+
211
+ # Prepare KL for loss.
212
+ action_kl = _make_time_major(old_policy_action_dist.kl(action_dist))
213
+
214
+ # Compute vtrace on the CPU for better perf.
215
+ vtrace_returns = vtrace.multi_from_logits(
216
+ behaviour_policy_logits=_make_time_major(unpacked_behaviour_logits),
217
+ target_policy_logits=_make_time_major(
218
+ unpacked_old_policy_behaviour_logits
219
+ ),
220
+ actions=torch.unbind(_make_time_major(loss_actions), dim=2),
221
+ discounts=(1.0 - _make_time_major(dones).float())
222
+ * self.config["gamma"],
223
+ rewards=_make_time_major(rewards),
224
+ values=values_time_major,
225
+ bootstrap_value=bootstrap_value,
226
+ dist_class=TorchCategorical if is_multidiscrete else dist_class,
227
+ model=model,
228
+ clip_rho_threshold=self.config["vtrace_clip_rho_threshold"],
229
+ clip_pg_rho_threshold=self.config["vtrace_clip_pg_rho_threshold"],
230
+ )
231
+
232
+ actions_logp = _make_time_major(action_dist.logp(actions))
233
+ prev_actions_logp = _make_time_major(prev_action_dist.logp(actions))
234
+ old_policy_actions_logp = _make_time_major(
235
+ old_policy_action_dist.logp(actions)
236
+ )
237
+ is_ratio = torch.clamp(
238
+ torch.exp(prev_actions_logp - old_policy_actions_logp), 0.0, 2.0
239
+ )
240
+ logp_ratio = is_ratio * torch.exp(actions_logp - prev_actions_logp)
241
+ self._is_ratio = is_ratio
242
+
243
+ advantages = vtrace_returns.pg_advantages.to(logp_ratio.device)
244
+ surrogate_loss = torch.min(
245
+ advantages * logp_ratio,
246
+ advantages
247
+ * torch.clamp(
248
+ logp_ratio,
249
+ 1 - self.config["clip_param"],
250
+ 1 + self.config["clip_param"],
251
+ ),
252
+ )
253
+
254
+ mean_kl_loss = reduce_mean_valid(action_kl)
255
+ mean_policy_loss = -reduce_mean_valid(surrogate_loss)
256
+
257
+ # The value function loss.
258
+ value_targets = vtrace_returns.vs.to(values_time_major.device)
259
+ delta = values_time_major - value_targets
260
+ mean_vf_loss = 0.5 * reduce_mean_valid(torch.pow(delta, 2.0))
261
+
262
+ # The entropy loss.
263
+ mean_entropy = reduce_mean_valid(_make_time_major(action_dist.entropy()))
264
+
265
+ else:
266
+ logger.debug("Using PPO surrogate loss (vtrace=False)")
267
+
268
+ # Prepare KL for Loss
269
+ action_kl = _make_time_major(prev_action_dist.kl(action_dist))
270
+
271
+ actions_logp = _make_time_major(action_dist.logp(actions))
272
+ prev_actions_logp = _make_time_major(prev_action_dist.logp(actions))
273
+ logp_ratio = torch.exp(actions_logp - prev_actions_logp)
274
+
275
+ advantages = _make_time_major(train_batch[Postprocessing.ADVANTAGES])
276
+ surrogate_loss = torch.min(
277
+ advantages * logp_ratio,
278
+ advantages
279
+ * torch.clamp(
280
+ logp_ratio,
281
+ 1 - self.config["clip_param"],
282
+ 1 + self.config["clip_param"],
283
+ ),
284
+ )
285
+
286
+ mean_kl_loss = reduce_mean_valid(action_kl)
287
+ mean_policy_loss = -reduce_mean_valid(surrogate_loss)
288
+
289
+ # The value function loss.
290
+ value_targets = _make_time_major(train_batch[Postprocessing.VALUE_TARGETS])
291
+ delta = values_time_major - value_targets
292
+ mean_vf_loss = 0.5 * reduce_mean_valid(torch.pow(delta, 2.0))
293
+
294
+ # The entropy loss.
295
+ mean_entropy = reduce_mean_valid(_make_time_major(action_dist.entropy()))
296
+
297
+ # The summed weighted loss.
298
+ total_loss = mean_policy_loss - mean_entropy * self.entropy_coeff
299
+ # Optional additional KL Loss
300
+ if self.config["use_kl_loss"]:
301
+ total_loss += self.kl_coeff * mean_kl_loss
302
+
303
+ # Optional vf loss (or in a separate term due to separate
304
+ # optimizers/networks).
305
+ loss_wo_vf = total_loss
306
+ if not self.config["_separate_vf_optimizer"]:
307
+ total_loss += mean_vf_loss * self.config["vf_loss_coeff"]
308
+
309
+ # Store values for stats function in model (tower), such that for
310
+ # multi-GPU, we do not override them during the parallel loss phase.
311
+ model.tower_stats["total_loss"] = total_loss
312
+ model.tower_stats["mean_policy_loss"] = mean_policy_loss
313
+ model.tower_stats["mean_kl_loss"] = mean_kl_loss
314
+ model.tower_stats["mean_vf_loss"] = mean_vf_loss
315
+ model.tower_stats["mean_entropy"] = mean_entropy
316
+ model.tower_stats["value_targets"] = value_targets
317
+ model.tower_stats["vf_explained_var"] = explained_variance(
318
+ torch.reshape(value_targets, [-1]),
319
+ torch.reshape(values_time_major, [-1]),
320
+ )
321
+
322
+ # Return one total loss or two losses: vf vs rest (policy + kl).
323
+ if self.config["_separate_vf_optimizer"]:
324
+ return loss_wo_vf, mean_vf_loss
325
+ else:
326
+ return total_loss
327
+
328
+ @override(TorchPolicyV2)
329
+ def stats_fn(self, train_batch: SampleBatch) -> Dict[str, TensorType]:
330
+ """Stats function for APPO. Returns a dict with important loss stats.
331
+
332
+ Args:
333
+ policy: The Policy to generate stats for.
334
+ train_batch: The SampleBatch (already) used for training.
335
+
336
+ Returns:
337
+ Dict[str, TensorType]: The stats dict.
338
+ """
339
+ stats_dict = {
340
+ "cur_lr": self.cur_lr,
341
+ "total_loss": torch.mean(torch.stack(self.get_tower_stats("total_loss"))),
342
+ "policy_loss": torch.mean(
343
+ torch.stack(self.get_tower_stats("mean_policy_loss"))
344
+ ),
345
+ "entropy": torch.mean(torch.stack(self.get_tower_stats("mean_entropy"))),
346
+ "entropy_coeff": self.entropy_coeff,
347
+ "var_gnorm": global_norm(self.model.trainable_variables()),
348
+ "vf_loss": torch.mean(torch.stack(self.get_tower_stats("mean_vf_loss"))),
349
+ "vf_explained_var": torch.mean(
350
+ torch.stack(self.get_tower_stats("vf_explained_var"))
351
+ ),
352
+ }
353
+
354
+ if self.config["vtrace"]:
355
+ is_stat_mean = torch.mean(self._is_ratio, [0, 1])
356
+ is_stat_var = torch.var(self._is_ratio, [0, 1])
357
+ stats_dict["mean_IS"] = is_stat_mean
358
+ stats_dict["var_IS"] = is_stat_var
359
+
360
+ if self.config["use_kl_loss"]:
361
+ stats_dict["kl"] = torch.mean(
362
+ torch.stack(self.get_tower_stats("mean_kl_loss"))
363
+ )
364
+ stats_dict["KL_Coeff"] = self.kl_coeff
365
+
366
+ return convert_to_numpy(stats_dict)
367
+
368
+ @override(TorchPolicyV2)
369
+ def extra_action_out(
370
+ self,
371
+ input_dict: Dict[str, TensorType],
372
+ state_batches: List[TensorType],
373
+ model: TorchModelV2,
374
+ action_dist: TorchDistributionWrapper,
375
+ ) -> Dict[str, TensorType]:
376
+ return {SampleBatch.VF_PREDS: model.value_function()}
377
+
378
+ @override(TorchPolicyV2)
379
+ def postprocess_trajectory(
380
+ self,
381
+ sample_batch: SampleBatch,
382
+ other_agent_batches: Optional[Dict[Any, SampleBatch]] = None,
383
+ episode=None,
384
+ ):
385
+ # Call super's postprocess_trajectory first.
386
+ # sample_batch = super().postprocess_trajectory(
387
+ # sample_batch, other_agent_batches, episode
388
+ # )
389
+
390
+ # Do all post-processing always with no_grad().
391
+ # Not using this here will introduce a memory leak
392
+ # in torch (issue #6962).
393
+ with torch.no_grad():
394
+ if not self.config["vtrace"]:
395
+ sample_batch = compute_gae_for_sample_batch(
396
+ self, sample_batch, other_agent_batches, episode
397
+ )
398
+ else:
399
+ # Add the SampleBatch.VALUES_BOOTSTRAPPED column, which we'll need
400
+ # inside the loss for vtrace calculations.
401
+ sample_batch = compute_bootstrap_value(sample_batch, self)
402
+
403
+ return sample_batch
404
+
405
+ @override(TorchPolicyV2)
406
+ def extra_grad_process(
407
+ self, optimizer: "torch.optim.Optimizer", loss: TensorType
408
+ ) -> Dict[str, TensorType]:
409
+ return apply_grad_clipping(self, optimizer, loss)
410
+
411
+ @override(TorchPolicyV2)
412
+ def get_batch_divisibility_req(self) -> int:
413
+ return self.config["rollout_fragment_length"]
lib/python3.12/site-packages/ray/rllib/algorithms/appo/default_appo_rl_module.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import abc
2
+ from typing import Any, Dict, List, Tuple
3
+
4
+ from ray.rllib.algorithms.ppo.default_ppo_rl_module import DefaultPPORLModule
5
+ from ray.rllib.core.learner.utils import make_target_network
6
+ from ray.rllib.core.models.base import ACTOR, ENCODER_OUT
7
+ from ray.rllib.core.rl_module.apis import (
8
+ TARGET_NETWORK_ACTION_DIST_INPUTS,
9
+ TargetNetworkAPI,
10
+ )
11
+ from ray.rllib.utils.annotations import (
12
+ OverrideToImplementCustomLogic_CallToSuperRecommended,
13
+ override,
14
+ )
15
+ from ray.rllib.utils.typing import NetworkType
16
+ from ray.util.annotations import DeveloperAPI
17
+
18
+
19
+ @DeveloperAPI
20
+ class DefaultAPPORLModule(DefaultPPORLModule, TargetNetworkAPI, abc.ABC):
21
+ """Default RLModule used by APPO, if user does not specify a custom RLModule.
22
+
23
+ Users who want to train their RLModules with APPO may implement any RLModule
24
+ (or TorchRLModule) subclass as long as the custom class also implements the
25
+ `ValueFunctionAPI` (see ray.rllib.core.rl_module.apis.value_function_api.py)
26
+ and the `TargetNetworkAPI` (see
27
+ ray.rllib.core.rl_module.apis.target_network_api.py).
28
+ """
29
+
30
+ @override(TargetNetworkAPI)
31
+ def make_target_networks(self):
32
+ self._old_encoder = make_target_network(self.encoder)
33
+ self._old_pi = make_target_network(self.pi)
34
+
35
+ @override(TargetNetworkAPI)
36
+ def get_target_network_pairs(self) -> List[Tuple[NetworkType, NetworkType]]:
37
+ return [
38
+ (self.encoder, self._old_encoder),
39
+ (self.pi, self._old_pi),
40
+ ]
41
+
42
+ @override(TargetNetworkAPI)
43
+ def forward_target(self, batch: Dict[str, Any]) -> Dict[str, Any]:
44
+ old_pi_inputs_encoded = self._old_encoder(batch)[ENCODER_OUT][ACTOR]
45
+ old_action_dist_logits = self._old_pi(old_pi_inputs_encoded)
46
+ return {TARGET_NETWORK_ACTION_DIST_INPUTS: old_action_dist_logits}
47
+
48
+ @OverrideToImplementCustomLogic_CallToSuperRecommended
49
+ @override(DefaultPPORLModule)
50
+ def get_non_inference_attributes(self) -> List[str]:
51
+ # Get the NON inference-only attributes from the parent class.
52
+ ret = super().get_non_inference_attributes()
53
+ # Add the two (APPO) target networks to it (NOT needed in
54
+ # inference-only mode).
55
+ ret += ["_old_encoder", "_old_pi"]
56
+ return ret
lib/python3.12/site-packages/ray/rllib/algorithms/appo/torch/__init__.py ADDED
File without changes
lib/python3.12/site-packages/ray/rllib/algorithms/appo/torch/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (210 Bytes). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/appo/torch/__pycache__/appo_torch_learner.cpython-312.pyc ADDED
Binary file (8.16 kB). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/appo/torch/__pycache__/appo_torch_rl_module.cpython-312.pyc ADDED
Binary file (647 Bytes). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/appo/torch/__pycache__/default_appo_torch_rl_module.cpython-312.pyc ADDED
Binary file (718 Bytes). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/appo/torch/appo_torch_learner.py ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Asynchronous Proximal Policy Optimization (APPO)
2
+
3
+ The algorithm is described in [1] (under the name of "IMPACT"):
4
+
5
+ Detailed documentation:
6
+ https://docs.ray.io/en/master/rllib-algorithms.html#appo
7
+
8
+ [1] IMPACT: Importance Weighted Asynchronous Architectures with Clipped Target Networks.
9
+ Luo et al. 2020
10
+ https://arxiv.org/pdf/1912.00167
11
+ """
12
+ from typing import Dict
13
+
14
+ from ray.rllib.algorithms.appo.appo import (
15
+ LEARNER_RESULTS_CURR_KL_COEFF_KEY,
16
+ LEARNER_RESULTS_KL_KEY,
17
+ APPOConfig,
18
+ )
19
+ from ray.rllib.algorithms.appo.appo_learner import APPOLearner
20
+ from ray.rllib.algorithms.impala.torch.impala_torch_learner import IMPALATorchLearner
21
+ from ray.rllib.algorithms.impala.torch.vtrace_torch_v2 import (
22
+ make_time_major,
23
+ vtrace_torch,
24
+ )
25
+ from ray.rllib.core.columns import Columns
26
+ from ray.rllib.core.learner.learner import ENTROPY_KEY, POLICY_LOSS_KEY, VF_LOSS_KEY
27
+ from ray.rllib.core.rl_module.apis import (
28
+ TARGET_NETWORK_ACTION_DIST_INPUTS,
29
+ TargetNetworkAPI,
30
+ ValueFunctionAPI,
31
+ )
32
+ from ray.rllib.utils.annotations import override
33
+ from ray.rllib.utils.framework import try_import_torch
34
+ from ray.rllib.utils.numpy import convert_to_numpy
35
+ from ray.rllib.utils.typing import ModuleID, TensorType
36
+
37
+ torch, nn = try_import_torch()
38
+
39
+
40
+ class APPOTorchLearner(APPOLearner, IMPALATorchLearner):
41
+ """Implements APPO loss / update logic on top of IMPALATorchLearner."""
42
+
43
+ @override(IMPALATorchLearner)
44
+ def compute_loss_for_module(
45
+ self,
46
+ *,
47
+ module_id: ModuleID,
48
+ config: APPOConfig,
49
+ batch: Dict,
50
+ fwd_out: Dict[str, TensorType],
51
+ ) -> TensorType:
52
+ module = self.module[module_id].unwrapped()
53
+ assert isinstance(module, TargetNetworkAPI)
54
+ assert isinstance(module, ValueFunctionAPI)
55
+
56
+ # TODO (sven): Now that we do the +1ts trick to be less vulnerable about
57
+ # bootstrap values at the end of rollouts in the new stack, we might make
58
+ # this a more flexible, configurable parameter for users, e.g.
59
+ # `v_trace_seq_len` (independent of `rollout_fragment_length`). Separation
60
+ # of concerns (sampling vs learning).
61
+ rollout_frag_or_episode_len = config.get_rollout_fragment_length()
62
+ recurrent_seq_len = batch.get("seq_lens")
63
+
64
+ loss_mask = batch[Columns.LOSS_MASK].float()
65
+ loss_mask_time_major = make_time_major(
66
+ loss_mask,
67
+ trajectory_len=rollout_frag_or_episode_len,
68
+ recurrent_seq_len=recurrent_seq_len,
69
+ )
70
+ size_loss_mask = torch.sum(loss_mask)
71
+
72
+ values = module.compute_values(
73
+ batch, embeddings=fwd_out.get(Columns.EMBEDDINGS)
74
+ )
75
+
76
+ action_dist_cls_train = module.get_train_action_dist_cls()
77
+ target_policy_dist = action_dist_cls_train.from_logits(
78
+ fwd_out[Columns.ACTION_DIST_INPUTS]
79
+ )
80
+
81
+ old_target_policy_dist = action_dist_cls_train.from_logits(
82
+ module.forward_target(batch)[TARGET_NETWORK_ACTION_DIST_INPUTS]
83
+ )
84
+ old_target_policy_actions_logp = old_target_policy_dist.logp(
85
+ batch[Columns.ACTIONS]
86
+ )
87
+ behaviour_actions_logp = batch[Columns.ACTION_LOGP]
88
+ target_actions_logp = target_policy_dist.logp(batch[Columns.ACTIONS])
89
+
90
+ behaviour_actions_logp_time_major = make_time_major(
91
+ behaviour_actions_logp,
92
+ trajectory_len=rollout_frag_or_episode_len,
93
+ recurrent_seq_len=recurrent_seq_len,
94
+ )
95
+ target_actions_logp_time_major = make_time_major(
96
+ target_actions_logp,
97
+ trajectory_len=rollout_frag_or_episode_len,
98
+ recurrent_seq_len=recurrent_seq_len,
99
+ )
100
+ old_actions_logp_time_major = make_time_major(
101
+ old_target_policy_actions_logp,
102
+ trajectory_len=rollout_frag_or_episode_len,
103
+ recurrent_seq_len=recurrent_seq_len,
104
+ )
105
+ rewards_time_major = make_time_major(
106
+ batch[Columns.REWARDS],
107
+ trajectory_len=rollout_frag_or_episode_len,
108
+ recurrent_seq_len=recurrent_seq_len,
109
+ )
110
+ values_time_major = make_time_major(
111
+ values,
112
+ trajectory_len=rollout_frag_or_episode_len,
113
+ recurrent_seq_len=recurrent_seq_len,
114
+ )
115
+ assert Columns.VALUES_BOOTSTRAPPED not in batch
116
+ # Use as bootstrap values the vf-preds in the next "batch row", except
117
+ # for the very last row (which doesn't have a next row), for which the
118
+ # bootstrap value does not matter b/c it has a +1ts value at its end
119
+ # anyways. So we chose an arbitrary item (for simplicity of not having to
120
+ # move new data to the device).
121
+ bootstrap_values = torch.cat(
122
+ [
123
+ values_time_major[0][1:], # 0th ts values from "next row"
124
+ values_time_major[0][0:1], # <- can use any arbitrary value here
125
+ ],
126
+ dim=0,
127
+ )
128
+
129
+ # The discount factor that is used should be gamma except for timesteps where
130
+ # the episode is terminated. In that case, the discount factor should be 0.
131
+ discounts_time_major = (
132
+ 1.0
133
+ - make_time_major(
134
+ batch[Columns.TERMINATEDS],
135
+ trajectory_len=rollout_frag_or_episode_len,
136
+ recurrent_seq_len=recurrent_seq_len,
137
+ ).float()
138
+ ) * config.gamma
139
+
140
+ # Note that vtrace will compute the main loop on the CPU for better performance.
141
+ vtrace_adjusted_target_values, pg_advantages = vtrace_torch(
142
+ target_action_log_probs=old_actions_logp_time_major,
143
+ behaviour_action_log_probs=behaviour_actions_logp_time_major,
144
+ discounts=discounts_time_major,
145
+ rewards=rewards_time_major,
146
+ values=values_time_major,
147
+ bootstrap_values=bootstrap_values,
148
+ clip_pg_rho_threshold=config.vtrace_clip_pg_rho_threshold,
149
+ clip_rho_threshold=config.vtrace_clip_rho_threshold,
150
+ )
151
+ pg_advantages = pg_advantages * loss_mask_time_major
152
+
153
+ # The policy gradients loss.
154
+ is_ratio = torch.clip(
155
+ torch.exp(behaviour_actions_logp_time_major - old_actions_logp_time_major),
156
+ 0.0,
157
+ 2.0,
158
+ )
159
+ logp_ratio = is_ratio * torch.exp(
160
+ target_actions_logp_time_major - behaviour_actions_logp_time_major
161
+ )
162
+
163
+ surrogate_loss = torch.minimum(
164
+ pg_advantages * logp_ratio,
165
+ pg_advantages
166
+ * torch.clip(logp_ratio, 1 - config.clip_param, 1 + config.clip_param),
167
+ )
168
+
169
+ if config.use_kl_loss:
170
+ action_kl = old_target_policy_dist.kl(target_policy_dist) * loss_mask
171
+ mean_kl_loss = torch.sum(action_kl) / size_loss_mask
172
+ else:
173
+ mean_kl_loss = 0.0
174
+ mean_pi_loss = -(torch.sum(surrogate_loss) / size_loss_mask)
175
+
176
+ # The baseline loss.
177
+ delta = values_time_major - vtrace_adjusted_target_values
178
+ vf_loss = 0.5 * torch.sum(torch.pow(delta, 2.0) * loss_mask_time_major)
179
+ mean_vf_loss = vf_loss / size_loss_mask
180
+
181
+ # The entropy loss.
182
+ mean_entropy_loss = (
183
+ -torch.sum(target_policy_dist.entropy() * loss_mask) / size_loss_mask
184
+ )
185
+
186
+ # The summed weighted loss.
187
+ total_loss = (
188
+ mean_pi_loss
189
+ + (mean_vf_loss * config.vf_loss_coeff)
190
+ + (
191
+ mean_entropy_loss
192
+ * self.entropy_coeff_schedulers_per_module[
193
+ module_id
194
+ ].get_current_value()
195
+ )
196
+ + (mean_kl_loss * self.curr_kl_coeffs_per_module[module_id])
197
+ )
198
+
199
+ # Log important loss stats.
200
+ self.metrics.log_dict(
201
+ {
202
+ POLICY_LOSS_KEY: mean_pi_loss,
203
+ VF_LOSS_KEY: mean_vf_loss,
204
+ ENTROPY_KEY: -mean_entropy_loss,
205
+ LEARNER_RESULTS_KL_KEY: mean_kl_loss,
206
+ LEARNER_RESULTS_CURR_KL_COEFF_KEY: (
207
+ self.curr_kl_coeffs_per_module[module_id]
208
+ ),
209
+ },
210
+ key=module_id,
211
+ window=1, # <- single items (should not be mean/ema-reduced over time).
212
+ )
213
+ # Return the total loss.
214
+ return total_loss
215
+
216
+ @override(APPOLearner)
217
+ def _update_module_kl_coeff(self, module_id: ModuleID, config: APPOConfig) -> None:
218
+ # Update the current KL value based on the recently measured value.
219
+ # Increase.
220
+ kl = convert_to_numpy(self.metrics.peek((module_id, LEARNER_RESULTS_KL_KEY)))
221
+ kl_coeff_var = self.curr_kl_coeffs_per_module[module_id]
222
+
223
+ if kl > 2.0 * config.kl_target:
224
+ # TODO (Kourosh) why not *2.0?
225
+ kl_coeff_var.data *= 1.5
226
+ # Decrease.
227
+ elif kl < 0.5 * config.kl_target:
228
+ kl_coeff_var.data *= 0.5
229
+
230
+ self.metrics.log_value(
231
+ (module_id, LEARNER_RESULTS_CURR_KL_COEFF_KEY),
232
+ kl_coeff_var.item(),
233
+ window=1,
234
+ )
lib/python3.12/site-packages/ray/rllib/algorithms/appo/torch/appo_torch_rl_module.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Backward compat import.
2
+ from ray.rllib.algorithms.appo.torch.default_appo_torch_rl_module import ( # noqa
3
+ DefaultAPPOTorchRLModule as APPOTorchRLModule,
4
+ )
5
+ from ray._common.deprecation import deprecation_warning
6
+
7
+
8
+ deprecation_warning(
9
+ old="ray.rllib.algorithms.appo.torch.appo_torch_rl_module.APPOTorchRLModule",
10
+ new="ray.rllib.algorithms.appo.torch.default_appo_torch_rl_module."
11
+ "DefaultAPPOTorchRLModule",
12
+ error=False,
13
+ )
lib/python3.12/site-packages/ray/rllib/algorithms/appo/torch/default_appo_torch_rl_module.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from ray.rllib.algorithms.appo.default_appo_rl_module import DefaultAPPORLModule
2
+ from ray.rllib.algorithms.ppo.torch.default_ppo_torch_rl_module import (
3
+ DefaultPPOTorchRLModule,
4
+ )
5
+ from ray.util.annotations import DeveloperAPI
6
+
7
+
8
+ @DeveloperAPI
9
+ class DefaultAPPOTorchRLModule(DefaultPPOTorchRLModule, DefaultAPPORLModule):
10
+ pass
lib/python3.12/site-packages/ray/rllib/algorithms/appo/utils.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ [1] IMPACT: Importance Weighted Asynchronous Architectures with Clipped Target Networks.
3
+ Luo et al. 2020
4
+ https://arxiv.org/pdf/1912.00167
5
+ """
6
+ import threading
7
+ import time
8
+ from collections import deque
9
+
10
+ import numpy as np
11
+
12
+ from ray.rllib.models.catalog import ModelCatalog
13
+ from ray.rllib.models.modelv2 import ModelV2
14
+ from ray.rllib.utils.annotations import OldAPIStack
15
+ from ray.rllib.utils.metrics.ray_metrics import (
16
+ DEFAULT_HISTOGRAM_BOUNDARIES_SHORT_EVENTS,
17
+ TimerAndPrometheusLogger,
18
+ )
19
+ from ray.util.metrics import Counter, Histogram
20
+
21
+ POLICY_SCOPE = "func"
22
+ TARGET_POLICY_SCOPE = "target_func"
23
+
24
+
25
+ class CircularBuffer:
26
+ """A circular batch-wise buffer as described in [1] for APPO.
27
+
28
+ The buffer holds at most N batches, which are sampled at random (uniformly).
29
+ If full and a new batch is added, the oldest batch is discarded. Also, each batch
30
+ currently in the buffer can be sampled at most K times (after which it is also
31
+ discarded).
32
+ """
33
+
34
+ def __init__(self, num_batches: int, iterations_per_batch: int):
35
+ # N from the paper (buffer size).
36
+ self.num_batches = num_batches
37
+ # K ("replay coefficient") from the paper.
38
+ self.iterations_per_batch = iterations_per_batch
39
+
40
+ self._NxK = self.num_batches * self.iterations_per_batch
41
+ self._num_added = 0
42
+
43
+ self._buffer = deque([None for _ in range(self._NxK)], maxlen=self._NxK)
44
+ self._indices = set()
45
+ self._offset = self._NxK
46
+ self._lock = threading.Lock()
47
+
48
+ self._rng = np.random.default_rng()
49
+
50
+ # Ray metrics
51
+ self._metrics_circular_buffer_add_time = Histogram(
52
+ name="rllib_utils_circular_buffer_add_time",
53
+ description="Time spent in CircularBuffer.add()",
54
+ boundaries=DEFAULT_HISTOGRAM_BOUNDARIES_SHORT_EVENTS,
55
+ tag_keys=("rllib",),
56
+ )
57
+ self._metrics_circular_buffer_add_time.set_default_tags(
58
+ {"rllib": self.__class__.__name__}
59
+ )
60
+
61
+ self._metrics_circular_buffer_add_ts_dropped = Counter(
62
+ name="rllib_utils_circular_buffer_add_ts_dropped_counter",
63
+ description="Total number of env steps dropped by the CircularBuffer.",
64
+ tag_keys=("rllib",),
65
+ )
66
+ self._metrics_circular_buffer_add_ts_dropped.set_default_tags(
67
+ {"rllib": self.__class__.__name__}
68
+ )
69
+
70
+ self._metrics_circular_buffer_sample_time = Histogram(
71
+ name="rllib_utils_circular_buffer_sample_time",
72
+ description="Time spent in CircularBuffer.sample()",
73
+ boundaries=DEFAULT_HISTOGRAM_BOUNDARIES_SHORT_EVENTS,
74
+ tag_keys=("rllib",),
75
+ )
76
+ self._metrics_circular_buffer_sample_time.set_default_tags(
77
+ {"rllib": self.__class__.__name__}
78
+ )
79
+
80
+ def add(self, batch):
81
+ # Add buffer and k=0 information to the deque.
82
+ with TimerAndPrometheusLogger(self._metrics_circular_buffer_add_time):
83
+ with self._lock:
84
+ dropped_entry = self._buffer[0]
85
+ for _ in range(self.iterations_per_batch):
86
+ self._buffer.append(batch)
87
+ self._indices.add(self._offset)
88
+ self._indices.discard(self._offset - self._NxK)
89
+ self._offset += 1
90
+ self._num_added += 1
91
+
92
+ # A valid entry (w/ a batch whose k has not been reach K yet) was dropped.
93
+ dropped_ts = 0
94
+ if dropped_entry is not None:
95
+ dropped_ts = dropped_entry.env_steps()
96
+ if dropped_ts > 0:
97
+ self._metrics_circular_buffer_add_ts_dropped.inc(value=dropped_ts)
98
+
99
+ return dropped_ts
100
+
101
+ def sample(self):
102
+ # Only initially, the buffer may be empty -> Just wait for some time.
103
+ with TimerAndPrometheusLogger(self._metrics_circular_buffer_sample_time):
104
+ while len(self) == 0:
105
+ time.sleep(0.0001)
106
+
107
+ # Sample a random buffer index.
108
+ with self._lock:
109
+ idx = self._rng.choice(list(self._indices))
110
+ actual_buffer_idx = idx - self._offset + self._NxK
111
+ batch = self._buffer[actual_buffer_idx]
112
+ assert batch is not None, (
113
+ idx,
114
+ actual_buffer_idx,
115
+ self._offset,
116
+ self._indices,
117
+ [b is None for b in self._buffer],
118
+ )
119
+ self._buffer[actual_buffer_idx] = None
120
+ self._indices.discard(idx)
121
+
122
+ # Return the sampled batch.
123
+ return batch
124
+
125
+ @property
126
+ def filled(self):
127
+ """Whether the buffer has been filled once with at least `self.num_batches`."""
128
+ with self._lock:
129
+ return self._num_added >= self.num_batches
130
+
131
+ def __len__(self) -> int:
132
+ """Returns the number of actually valid (non-expired) batches in the buffer."""
133
+ with self._lock:
134
+ return len(self._indices)
135
+
136
+
137
+ @OldAPIStack
138
+ def make_appo_models(policy) -> ModelV2:
139
+ """Builds model and target model for APPO.
140
+
141
+ Returns:
142
+ ModelV2: The Model for the Policy to use.
143
+ Note: The target model will not be returned, just assigned to
144
+ `policy.target_model`.
145
+ """
146
+ # Get the num_outputs for the following model construction calls.
147
+ _, logit_dim = ModelCatalog.get_action_dist(
148
+ policy.action_space, policy.config["model"]
149
+ )
150
+
151
+ # Construct the (main) model.
152
+ policy.model = ModelCatalog.get_model_v2(
153
+ policy.observation_space,
154
+ policy.action_space,
155
+ logit_dim,
156
+ policy.config["model"],
157
+ name=POLICY_SCOPE,
158
+ framework=policy.framework,
159
+ )
160
+ policy.model_variables = policy.model.variables()
161
+
162
+ # Construct the target model.
163
+ policy.target_model = ModelCatalog.get_model_v2(
164
+ policy.observation_space,
165
+ policy.action_space,
166
+ logit_dim,
167
+ policy.config["model"],
168
+ name=TARGET_POLICY_SCOPE,
169
+ framework=policy.framework,
170
+ )
171
+ policy.target_model_variables = policy.target_model.variables()
172
+
173
+ # Return only the model (not the target model).
174
+ return policy.model
lib/python3.12/site-packages/ray/rllib/algorithms/callbacks.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # @OldAPIStack
2
+ from ray.rllib.callbacks.callbacks import RLlibCallback
3
+ from ray.rllib.callbacks.utils import _make_multi_callbacks
4
+
5
+ # Backward compatibility
6
+ DefaultCallbacks = RLlibCallback
7
+ make_multi_callbacks = _make_multi_callbacks
lib/python3.12/site-packages/ray/rllib/algorithms/marwil/__init__.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ray.rllib.algorithms.marwil.marwil import (
2
+ MARWIL,
3
+ MARWILConfig,
4
+ )
5
+ from ray.rllib.algorithms.marwil.marwil_tf_policy import (
6
+ MARWILTF1Policy,
7
+ MARWILTF2Policy,
8
+ )
9
+ from ray.rllib.algorithms.marwil.marwil_torch_policy import MARWILTorchPolicy
10
+
11
+ __all__ = [
12
+ "MARWIL",
13
+ "MARWILConfig",
14
+ # @OldAPIStack
15
+ "MARWILTF1Policy",
16
+ "MARWILTF2Policy",
17
+ "MARWILTorchPolicy",
18
+ ]
lib/python3.12/site-packages/ray/rllib/algorithms/marwil/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (556 Bytes). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/marwil/__pycache__/marwil.cpython-312.pyc ADDED
Binary file (19.2 kB). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/marwil/__pycache__/marwil_learner.cpython-312.pyc ADDED
Binary file (2.51 kB). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/marwil/__pycache__/marwil_tf_policy.cpython-312.pyc ADDED
Binary file (9.55 kB). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/marwil/__pycache__/marwil_torch_policy.cpython-312.pyc ADDED
Binary file (6.21 kB). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/marwil/marwil.py ADDED
@@ -0,0 +1,536 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Callable, Optional, Type, Union
2
+
3
+ from typing_extensions import Self
4
+
5
+ from ray._common.deprecation import deprecation_warning
6
+ from ray.rllib.algorithms.algorithm import Algorithm
7
+ from ray.rllib.algorithms.algorithm_config import AlgorithmConfig, NotProvided
8
+ from ray.rllib.connectors.learner import (
9
+ AddNextObservationsFromEpisodesToTrainBatch,
10
+ AddObservationsFromEpisodesToBatch,
11
+ AddOneTsToEpisodesAndTruncate,
12
+ GeneralAdvantageEstimation,
13
+ )
14
+ from ray.rllib.core.learner.learner import Learner
15
+ from ray.rllib.core.learner.training_data import TrainingData
16
+ from ray.rllib.core.rl_module.rl_module import RLModuleSpec
17
+ from ray.rllib.execution.rollout_ops import (
18
+ synchronous_parallel_sample,
19
+ )
20
+ from ray.rllib.execution.train_ops import (
21
+ multi_gpu_train_one_step,
22
+ train_one_step,
23
+ )
24
+ from ray.rllib.policy.policy import Policy
25
+ from ray.rllib.utils.annotations import OldAPIStack, override
26
+ from ray.rllib.utils.metrics import (
27
+ LEARNER_RESULTS,
28
+ LEARNER_UPDATE_TIMER,
29
+ NUM_AGENT_STEPS_SAMPLED,
30
+ NUM_ENV_STEPS_SAMPLED,
31
+ OFFLINE_SAMPLING_TIMER,
32
+ SAMPLE_TIMER,
33
+ SYNCH_WORKER_WEIGHTS_TIMER,
34
+ TIMERS,
35
+ )
36
+ from ray.rllib.utils.typing import (
37
+ EnvType,
38
+ ResultDict,
39
+ RLModuleSpecType,
40
+ )
41
+ from ray.tune.logger import Logger
42
+
43
+
44
+ class MARWILConfig(AlgorithmConfig):
45
+ """Defines a configuration class from which a MARWIL Algorithm can be built.
46
+
47
+ .. testcode::
48
+
49
+ import gymnasium as gym
50
+ import numpy as np
51
+
52
+ from pathlib import Path
53
+ from ray.rllib.algorithms.marwil import MARWILConfig
54
+
55
+ # Get the base path (to ray/rllib)
56
+ base_path = Path(__file__).parents[2]
57
+ # Get the path to the data in rllib folder.
58
+ data_path = base_path / "tests/data/cartpole/cartpole-v1_large"
59
+
60
+ config = MARWILConfig()
61
+ # Enable the new API stack.
62
+ config.api_stack(
63
+ enable_rl_module_and_learner=True,
64
+ enable_env_runner_and_connector_v2=True,
65
+ )
66
+ # Define the environment for which to learn a policy
67
+ # from offline data.
68
+ config.environment(
69
+ observation_space=gym.spaces.Box(
70
+ np.array([-4.8, -np.inf, -0.41887903, -np.inf]),
71
+ np.array([4.8, np.inf, 0.41887903, np.inf]),
72
+ shape=(4,),
73
+ dtype=np.float32,
74
+ ),
75
+ action_space=gym.spaces.Discrete(2),
76
+ )
77
+ # Set the training parameters.
78
+ config.training(
79
+ beta=1.0,
80
+ lr=1e-5,
81
+ gamma=0.99,
82
+ # We must define a train batch size for each
83
+ # learner (here 1 local learner).
84
+ train_batch_size_per_learner=2000,
85
+ )
86
+ # Define the data source for offline data.
87
+ config.offline_data(
88
+ input_=[data_path.as_posix()],
89
+ # Run exactly one update per training iteration.
90
+ dataset_num_iters_per_learner=1,
91
+ )
92
+
93
+ # Build an `Algorithm` object from the config and run 1 training
94
+ # iteration.
95
+ algo = config.build()
96
+ algo.train()
97
+
98
+ .. testcode::
99
+
100
+ import gymnasium as gym
101
+ import numpy as np
102
+
103
+ from pathlib import Path
104
+ from ray.rllib.algorithms.marwil import MARWILConfig
105
+ from ray import tune
106
+
107
+ # Get the base path (to ray/rllib)
108
+ base_path = Path(__file__).parents[2]
109
+ # Get the path to the data in rllib folder.
110
+ data_path = base_path / "tests/data/cartpole/cartpole-v1_large"
111
+
112
+ config = MARWILConfig()
113
+ # Enable the new API stack.
114
+ config.api_stack(
115
+ enable_rl_module_and_learner=True,
116
+ enable_env_runner_and_connector_v2=True,
117
+ )
118
+ # Print out some default values
119
+ print(f"beta: {config.beta}")
120
+ # Update the config object.
121
+ config.training(
122
+ lr=tune.grid_search([1e-3, 1e-4]),
123
+ beta=0.75,
124
+ # We must define a train batch size for each
125
+ # learner (here 1 local learner).
126
+ train_batch_size_per_learner=2000,
127
+ )
128
+ # Set the config's data path.
129
+ config.offline_data(
130
+ input_=[data_path.as_posix()],
131
+ # Set the number of updates to be run per learner
132
+ # per training step.
133
+ dataset_num_iters_per_learner=1,
134
+ )
135
+ # Set the config's environment for evalaution.
136
+ config.environment(
137
+ observation_space=gym.spaces.Box(
138
+ np.array([-4.8, -np.inf, -0.41887903, -np.inf]),
139
+ np.array([4.8, np.inf, 0.41887903, np.inf]),
140
+ shape=(4,),
141
+ dtype=np.float32,
142
+ ),
143
+ action_space=gym.spaces.Discrete(2),
144
+ )
145
+ # Set up a tuner to run the experiment.
146
+ tuner = tune.Tuner(
147
+ "MARWIL",
148
+ param_space=config,
149
+ run_config=tune.RunConfig(
150
+ stop={"training_iteration": 1},
151
+ ),
152
+ )
153
+ # Run the experiment.
154
+ tuner.fit()
155
+ """
156
+
157
+ def __init__(self, algo_class=None):
158
+ """Initializes a MARWILConfig instance."""
159
+ self.exploration_config = {
160
+ # The Exploration class to use. In the simplest case, this is the name
161
+ # (str) of any class present in the `rllib.utils.exploration` package.
162
+ # You can also provide the python class directly or the full location
163
+ # of your class (e.g. "ray.rllib.utils.exploration.epsilon_greedy.
164
+ # EpsilonGreedy").
165
+ "type": "StochasticSampling",
166
+ # Add constructor kwargs here (if any).
167
+ }
168
+
169
+ super().__init__(algo_class=algo_class or MARWIL)
170
+ self._is_online = False
171
+ # fmt: off
172
+ # __sphinx_doc_begin__
173
+ # MARWIL specific settings:
174
+ self.beta = 1.0
175
+ self.bc_logstd_coeff = 0.0
176
+ self.moving_average_sqd_adv_norm_update_rate = 1e-8
177
+ self.moving_average_sqd_adv_norm_start = 100.0
178
+ self.vf_coeff = 1.0
179
+ self.model["vf_share_layers"] = False
180
+ self.grad_clip = None
181
+
182
+ # Override some of AlgorithmConfig's default values with MARWIL-specific values.
183
+
184
+ # You should override input_ to point to an offline dataset
185
+ # (see algorithm.py and algorithm_config.py).
186
+ # The dataset may have an arbitrary number of timesteps
187
+ # (and even episodes) per line.
188
+ # However, each line must only contain consecutive timesteps in
189
+ # order for MARWIL to be able to calculate accumulated
190
+ # discounted returns. It is ok, though, to have multiple episodes in
191
+ # the same line.
192
+ self.input_ = "sampler"
193
+ self.postprocess_inputs = True
194
+ self.lr = 1e-4
195
+ self.lambda_ = 1.0
196
+ self.train_batch_size = 2000
197
+
198
+ # Materialize only the data in raw format, but not the mapped data b/c
199
+ # MARWIL uses a connector to calculate values and therefore the module
200
+ # needs to be updated frequently. This updating would not work if we
201
+ # map the data once at the beginning.
202
+ # TODO (simon, sven): The module is only updated when the OfflinePreLearner
203
+ # gets reinitiated, i.e. when the iterator gets reinitiated. This happens
204
+ # frequently enough with a small dataset, but with a big one this does not
205
+ # update often enough. We might need to put model weigths every couple of
206
+ # iterations into the object storage (maybe also connector states).
207
+ self.materialize_data = True
208
+ self.materialize_mapped_data = False
209
+ # __sphinx_doc_end__
210
+ # fmt: on
211
+ self._set_off_policy_estimation_methods = False
212
+
213
+ @override(AlgorithmConfig)
214
+ def training(
215
+ self,
216
+ *,
217
+ beta: Optional[float] = NotProvided,
218
+ bc_logstd_coeff: Optional[float] = NotProvided,
219
+ moving_average_sqd_adv_norm_update_rate: Optional[float] = NotProvided,
220
+ moving_average_sqd_adv_norm_start: Optional[float] = NotProvided,
221
+ vf_coeff: Optional[float] = NotProvided,
222
+ grad_clip: Optional[float] = NotProvided,
223
+ **kwargs,
224
+ ) -> Self:
225
+ """Sets the training related configuration.
226
+
227
+ Args:
228
+ beta: Scaling of advantages in exponential terms. When beta is 0.0,
229
+ MARWIL is reduced to behavior cloning (imitation learning);
230
+ see bc.py algorithm in this same directory.
231
+ bc_logstd_coeff: A coefficient to encourage higher action distribution
232
+ entropy for exploration.
233
+ moving_average_sqd_adv_norm_update_rate: The rate for updating the
234
+ squared moving average advantage norm (c^2). A higher rate leads
235
+ to faster updates of this moving avergage.
236
+ moving_average_sqd_adv_norm_start: Starting value for the
237
+ squared moving average advantage norm (c^2).
238
+ vf_coeff: Balancing value estimation loss and policy optimization loss.
239
+ grad_clip: If specified, clip the global norm of gradients by this amount.
240
+
241
+ Returns:
242
+ This updated AlgorithmConfig object.
243
+ """
244
+ # Pass kwargs onto super's `training()` method.
245
+ super().training(**kwargs)
246
+ if beta is not NotProvided:
247
+ self.beta = beta
248
+ if bc_logstd_coeff is not NotProvided:
249
+ self.bc_logstd_coeff = bc_logstd_coeff
250
+ if moving_average_sqd_adv_norm_update_rate is not NotProvided:
251
+ self.moving_average_sqd_adv_norm_update_rate = (
252
+ moving_average_sqd_adv_norm_update_rate
253
+ )
254
+ if moving_average_sqd_adv_norm_start is not NotProvided:
255
+ self.moving_average_sqd_adv_norm_start = moving_average_sqd_adv_norm_start
256
+ if vf_coeff is not NotProvided:
257
+ self.vf_coeff = vf_coeff
258
+ if grad_clip is not NotProvided:
259
+ self.grad_clip = grad_clip
260
+ return self
261
+
262
+ @override(AlgorithmConfig)
263
+ def get_default_rl_module_spec(self) -> RLModuleSpecType:
264
+ if self.framework_str == "torch":
265
+ from ray.rllib.algorithms.ppo.torch.default_ppo_torch_rl_module import (
266
+ DefaultPPOTorchRLModule,
267
+ )
268
+
269
+ return RLModuleSpec(module_class=DefaultPPOTorchRLModule)
270
+ else:
271
+ raise ValueError(
272
+ f"The framework {self.framework_str} is not supported. "
273
+ "Use 'torch' instead."
274
+ )
275
+
276
+ @override(AlgorithmConfig)
277
+ def get_default_learner_class(self) -> Union[Type["Learner"], str]:
278
+ if self.framework_str == "torch":
279
+ from ray.rllib.algorithms.marwil.torch.marwil_torch_learner import (
280
+ MARWILTorchLearner,
281
+ )
282
+
283
+ return MARWILTorchLearner
284
+ else:
285
+ raise ValueError(
286
+ f"The framework {self.framework_str} is not supported. "
287
+ "Use 'torch' instead."
288
+ )
289
+
290
+ @override(AlgorithmConfig)
291
+ def evaluation(
292
+ self,
293
+ **kwargs,
294
+ ) -> Self:
295
+ """Sets the evaluation related configuration.
296
+ Returns:
297
+ This updated AlgorithmConfig object.
298
+ """
299
+ # Pass kwargs onto super's `evaluation()` method.
300
+ super().evaluation(**kwargs)
301
+
302
+ if "off_policy_estimation_methods" in kwargs:
303
+ # User specified their OPE methods.
304
+ self._set_off_policy_estimation_methods = True
305
+
306
+ return self
307
+
308
+ @override(AlgorithmConfig)
309
+ def offline_data(self, **kwargs) -> Self:
310
+
311
+ super().offline_data(**kwargs)
312
+
313
+ # Check, if the passed in class incorporates the `OfflinePreLearner`
314
+ # interface.
315
+ if "prelearner_class" in kwargs:
316
+ from ray.rllib.offline.offline_data import OfflinePreLearner
317
+
318
+ if not issubclass(kwargs.get("prelearner_class"), OfflinePreLearner):
319
+ raise ValueError(
320
+ f"`prelearner_class` {kwargs.get('prelearner_class')} is not a "
321
+ "subclass of `OfflinePreLearner`. Any class passed to "
322
+ "`prelearner_class` needs to implement the interface given by "
323
+ "`OfflinePreLearner`."
324
+ )
325
+
326
+ return self
327
+
328
+ @override(AlgorithmConfig)
329
+ def build(
330
+ self,
331
+ env: Optional[Union[str, EnvType]] = None,
332
+ logger_creator: Optional[Callable[[], Logger]] = None,
333
+ ) -> "Algorithm":
334
+ if not self._set_off_policy_estimation_methods:
335
+ deprecation_warning(
336
+ old=r"MARWIL used to have off_policy_estimation_methods "
337
+ "is and wis by default. This has"
338
+ r"changed to off_policy_estimation_methods: \{\}."
339
+ "If you want to use an off-policy estimator, specify it in"
340
+ ".evaluation(off_policy_estimation_methods=...)",
341
+ error=False,
342
+ )
343
+ return super().build(env, logger_creator)
344
+
345
+ @override(AlgorithmConfig)
346
+ def build_learner_connector(
347
+ self,
348
+ input_observation_space,
349
+ input_action_space,
350
+ device=None,
351
+ ):
352
+ pipeline = super().build_learner_connector(
353
+ input_observation_space=input_observation_space,
354
+ input_action_space=input_action_space,
355
+ device=device,
356
+ )
357
+
358
+ # Before anything, add one ts to each episode (and record this in the loss
359
+ # mask, so that the computations at this extra ts are not used to compute
360
+ # the loss).
361
+ pipeline.prepend(AddOneTsToEpisodesAndTruncate())
362
+
363
+ # Prepend the "add-NEXT_OBS-from-episodes-to-train-batch" connector piece (right
364
+ # after the corresponding "add-OBS-..." default piece).
365
+ pipeline.insert_after(
366
+ AddObservationsFromEpisodesToBatch,
367
+ AddNextObservationsFromEpisodesToTrainBatch(),
368
+ )
369
+
370
+ # At the end of the pipeline (when the batch is already completed), add the
371
+ # GAE connector, which performs a vf forward pass, then computes the GAE
372
+ # computations, and puts the results of this (advantages, value targets)
373
+ # directly back in the batch. This is then the batch used for
374
+ # `forward_train` and `compute_losses`.
375
+ pipeline.append(
376
+ GeneralAdvantageEstimation(gamma=self.gamma, lambda_=self.lambda_)
377
+ )
378
+
379
+ return pipeline
380
+
381
+ @override(AlgorithmConfig)
382
+ def validate(self) -> None:
383
+ # Call super's validation method.
384
+ super().validate()
385
+
386
+ if self.beta < 0.0 or self.beta > 1.0:
387
+ self._value_error("`beta` must be within 0.0 and 1.0!")
388
+
389
+ if self.postprocess_inputs is False and self.beta > 0.0:
390
+ self._value_error(
391
+ "`postprocess_inputs` must be True for MARWIL (to "
392
+ "calculate accum., discounted returns)! Try setting "
393
+ "`config.offline_data(postprocess_inputs=True)`."
394
+ )
395
+
396
+ # Assert that for a local learner the number of iterations is 1. Note,
397
+ # this is needed because we have no iterators, but instead a single
398
+ # batch returned directly from the `OfflineData.sample` method.
399
+ if (
400
+ self.num_learners == 0
401
+ and not self.dataset_num_iters_per_learner
402
+ and self.enable_rl_module_and_learner
403
+ ):
404
+ self._value_error(
405
+ "When using a local Learner (`config.num_learners=0`), the number of "
406
+ "iterations per learner (`dataset_num_iters_per_learner`) has to be "
407
+ "defined! Set this hyperparameter through `config.offline_data("
408
+ "dataset_num_iters_per_learner=...)`."
409
+ )
410
+
411
+ @property
412
+ def _model_auto_keys(self):
413
+ return super()._model_auto_keys | {"beta": self.beta, "vf_share_layers": False}
414
+
415
+
416
+ class MARWIL(Algorithm):
417
+ @classmethod
418
+ @override(Algorithm)
419
+ def get_default_config(cls) -> MARWILConfig:
420
+ return MARWILConfig()
421
+
422
+ @classmethod
423
+ @override(Algorithm)
424
+ def get_default_policy_class(
425
+ cls, config: AlgorithmConfig
426
+ ) -> Optional[Type[Policy]]:
427
+ if config["framework"] == "torch":
428
+ from ray.rllib.algorithms.marwil.marwil_torch_policy import (
429
+ MARWILTorchPolicy,
430
+ )
431
+
432
+ return MARWILTorchPolicy
433
+ elif config["framework"] == "tf":
434
+ from ray.rllib.algorithms.marwil.marwil_tf_policy import (
435
+ MARWILTF1Policy,
436
+ )
437
+
438
+ return MARWILTF1Policy
439
+ else:
440
+ from ray.rllib.algorithms.marwil.marwil_tf_policy import MARWILTF2Policy
441
+
442
+ return MARWILTF2Policy
443
+
444
+ @override(Algorithm)
445
+ def training_step(self) -> None:
446
+ """Implements training logic for the new stack
447
+
448
+ Note, this includes so far training with the `OfflineData`
449
+ class (multi-/single-learner setup) and evaluation on
450
+ `EnvRunner`s. Note further, evaluation on the dataset itself
451
+ using estimators is not implemented, yet.
452
+ """
453
+ # Old API stack (Policy, RolloutWorker, Connector).
454
+ if not self.config.enable_env_runner_and_connector_v2:
455
+ return self._training_step_old_api_stack()
456
+
457
+ # TODO (simon): Take care of sampler metrics: right
458
+ # now all rewards are `nan`, which possibly confuses
459
+ # the user that sth. is not right, although it is as
460
+ # we do not step the env.
461
+ with self.metrics.log_time((TIMERS, OFFLINE_SAMPLING_TIMER)):
462
+ # If we should use an iterator in the learner(s). Note, in case of
463
+ # multiple learners we must always return a list of iterators.
464
+ return_iterator = (
465
+ self.config.num_learners > 0
466
+ or self.config.dataset_num_iters_per_learner != 1
467
+ )
468
+
469
+ # Sampling from offline data.
470
+ batch_or_iterator = self.offline_data.sample(
471
+ num_samples=self.config.train_batch_size_per_learner,
472
+ num_shards=self.config.num_learners,
473
+ # Return an iterator, if a `Learner` should update
474
+ # multiple times per RLlib iteration.
475
+ return_iterator=return_iterator,
476
+ )
477
+ if return_iterator:
478
+ training_data = TrainingData(data_iterators=batch_or_iterator)
479
+ else:
480
+ training_data = TrainingData(batch=batch_or_iterator)
481
+
482
+ with self.metrics.log_time((TIMERS, LEARNER_UPDATE_TIMER)):
483
+ # Updating the policy.
484
+ learner_results = self.learner_group.update(
485
+ training_data=training_data,
486
+ minibatch_size=self.config.train_batch_size_per_learner,
487
+ num_iters=self.config.dataset_num_iters_per_learner,
488
+ **self.offline_data.iter_batches_kwargs,
489
+ )
490
+
491
+ # Log training results.
492
+ self.metrics.aggregate(learner_results, key=LEARNER_RESULTS)
493
+
494
+ @OldAPIStack
495
+ def _training_step_old_api_stack(self) -> ResultDict:
496
+ """Implements training step for the old stack.
497
+
498
+ Note, there is no hybrid stack anymore. If you need to use `RLModule`s,
499
+ use the new api stack.
500
+ """
501
+ # Collect SampleBatches from sample workers.
502
+ with self._timers[SAMPLE_TIMER]:
503
+ train_batch = synchronous_parallel_sample(worker_set=self.env_runner_group)
504
+ train_batch = train_batch.as_multi_agent(
505
+ module_id=list(self.config.policies)[0]
506
+ )
507
+ self._counters[NUM_AGENT_STEPS_SAMPLED] += train_batch.agent_steps()
508
+ self._counters[NUM_ENV_STEPS_SAMPLED] += train_batch.env_steps()
509
+
510
+ # Train.
511
+ if self.config.simple_optimizer:
512
+ train_results = train_one_step(self, train_batch)
513
+ else:
514
+ train_results = multi_gpu_train_one_step(self, train_batch)
515
+
516
+ # TODO: Move training steps counter update outside of `train_one_step()` method.
517
+ # # Update train step counters.
518
+ # self._counters[NUM_ENV_STEPS_TRAINED] += train_batch.env_steps()
519
+ # self._counters[NUM_AGENT_STEPS_TRAINED] += train_batch.agent_steps()
520
+
521
+ global_vars = {
522
+ "timestep": self._counters[NUM_AGENT_STEPS_SAMPLED],
523
+ }
524
+
525
+ # Update weights - after learning on the local worker - on all remote
526
+ # workers (only those policies that were actually trained).
527
+ if self.env_runner_group.num_remote_env_runners() > 0:
528
+ with self._timers[SYNCH_WORKER_WEIGHTS_TIMER]:
529
+ self.env_runner_group.sync_weights(
530
+ policies=list(train_results.keys()), global_vars=global_vars
531
+ )
532
+
533
+ # Update global vars on local worker as well.
534
+ self.env_runner.set_global_vars(global_vars)
535
+
536
+ return train_results
lib/python3.12/site-packages/ray/rllib/algorithms/marwil/marwil_learner.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict, Optional
2
+
3
+ from ray.rllib.core.learner.learner import Learner
4
+ from ray.rllib.core.rl_module.apis import ValueFunctionAPI
5
+ from ray.rllib.utils.annotations import override
6
+ from ray.rllib.utils.lambda_defaultdict import LambdaDefaultDict
7
+ from ray.rllib.utils.typing import ModuleID, ShouldModuleBeUpdatedFn, TensorType
8
+
9
+ LEARNER_RESULTS_MOVING_AVG_SQD_ADV_NORM_KEY = "moving_avg_sqd_adv_norm"
10
+ LEARNER_RESULTS_VF_EXPLAINED_VAR_KEY = "vf_explained_variance"
11
+
12
+
13
+ # TODO (simon): Check, if the norm update should be done inside
14
+ # the Learner.
15
+ class MARWILLearner(Learner):
16
+ @override(Learner)
17
+ def build(self) -> None:
18
+ super().build()
19
+
20
+ # Dict mapping module IDs to the respective moving averages of squared
21
+ # advantages.
22
+ self.moving_avg_sqd_adv_norms_per_module: Dict[
23
+ ModuleID, TensorType
24
+ ] = LambdaDefaultDict(
25
+ lambda module_id: self._get_tensor_variable(
26
+ self.config.get_config_for_module(
27
+ module_id
28
+ ).moving_average_sqd_adv_norm_start
29
+ )
30
+ )
31
+
32
+ @override(Learner)
33
+ def remove_module(
34
+ self,
35
+ module_id: ModuleID,
36
+ *,
37
+ new_should_module_be_updated: Optional[ShouldModuleBeUpdatedFn] = None,
38
+ ) -> None:
39
+ super().remove_module(
40
+ module_id,
41
+ new_should_module_be_updated=new_should_module_be_updated,
42
+ )
43
+ # In case of BC (beta==0.0 and this property never being used),
44
+ self.moving_avg_sqd_adv_norms_per_module.pop(module_id, None)
45
+
46
+ @classmethod
47
+ @override(Learner)
48
+ def rl_module_required_apis(cls) -> list[type]:
49
+ # In order for a PPOLearner to update an RLModule, it must implement the
50
+ # following APIs:
51
+ return [ValueFunctionAPI]
lib/python3.12/site-packages/ray/rllib/algorithms/marwil/marwil_tf_policy.py ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ from typing import Any, Dict, List, Optional, Type, Union
3
+
4
+ from ray.rllib.evaluation.postprocessing import Postprocessing, compute_advantages
5
+ from ray.rllib.models.action_dist import ActionDistribution
6
+ from ray.rllib.models.modelv2 import ModelV2
7
+ from ray.rllib.models.tf.tf_action_dist import TFActionDistribution
8
+ from ray.rllib.policy.dynamic_tf_policy_v2 import DynamicTFPolicyV2
9
+ from ray.rllib.policy.eager_tf_policy_v2 import EagerTFPolicyV2
10
+ from ray.rllib.policy.policy import Policy
11
+ from ray.rllib.policy.sample_batch import SampleBatch
12
+ from ray.rllib.policy.tf_mixins import (
13
+ ValueNetworkMixin,
14
+ compute_gradients,
15
+ )
16
+ from ray.rllib.utils.annotations import override
17
+ from ray.rllib.utils.framework import get_variable, try_import_tf
18
+ from ray.rllib.utils.tf_utils import explained_variance
19
+ from ray.rllib.utils.typing import (
20
+ LocalOptimizer,
21
+ ModelGradients,
22
+ TensorType,
23
+ )
24
+
25
+ tf1, tf, tfv = try_import_tf()
26
+
27
+ logger = logging.getLogger(__name__)
28
+
29
+
30
+ class PostprocessAdvantages:
31
+ """Marwil's custom trajectory post-processing mixin."""
32
+
33
+ def __init__(self):
34
+ pass
35
+
36
+ def postprocess_trajectory(
37
+ self,
38
+ sample_batch: SampleBatch,
39
+ other_agent_batches: Optional[Dict[Any, SampleBatch]] = None,
40
+ episode=None,
41
+ ):
42
+ sample_batch = super().postprocess_trajectory(
43
+ sample_batch, other_agent_batches, episode
44
+ )
45
+
46
+ # Trajectory is actually complete -> last r=0.0.
47
+ if sample_batch[SampleBatch.TERMINATEDS][-1]:
48
+ last_r = 0.0
49
+ # Trajectory has been truncated -> last r=VF estimate of last obs.
50
+ else:
51
+ # Input dict is provided to us automatically via the Model's
52
+ # requirements. It's a single-timestep (last one in trajectory)
53
+ # input_dict.
54
+ # Create an input dict according to the Model's requirements.
55
+ index = "last" if SampleBatch.NEXT_OBS in sample_batch else -1
56
+ input_dict = sample_batch.get_single_step_input_dict(
57
+ self.view_requirements, index=index
58
+ )
59
+ last_r = self._value(**input_dict)
60
+
61
+ # Adds the "advantages" (which in the case of MARWIL are simply the
62
+ # discounted cumulative rewards) to the SampleBatch.
63
+ return compute_advantages(
64
+ sample_batch,
65
+ last_r,
66
+ self.config["gamma"],
67
+ # We just want the discounted cumulative rewards, so we won't need
68
+ # GAE nor critic (use_critic=True: Subtract vf-estimates from returns).
69
+ use_gae=False,
70
+ use_critic=False,
71
+ )
72
+
73
+
74
+ class MARWILLoss:
75
+ def __init__(
76
+ self,
77
+ policy: Policy,
78
+ value_estimates: TensorType,
79
+ action_dist: ActionDistribution,
80
+ train_batch: SampleBatch,
81
+ vf_loss_coeff: float,
82
+ beta: float,
83
+ ):
84
+ # L = - A * log\pi_\theta(a|s)
85
+ logprobs = action_dist.logp(train_batch[SampleBatch.ACTIONS])
86
+ if beta != 0.0:
87
+ cumulative_rewards = train_batch[Postprocessing.ADVANTAGES]
88
+ # Advantage Estimation.
89
+ adv = cumulative_rewards - value_estimates
90
+ adv_squared = tf.reduce_mean(tf.math.square(adv))
91
+ # Value function's loss term (MSE).
92
+ self.v_loss = 0.5 * adv_squared
93
+
94
+ # Perform moving averaging of advantage^2.
95
+ rate = policy.config["moving_average_sqd_adv_norm_update_rate"]
96
+
97
+ # Update averaged advantage norm.
98
+ # Eager.
99
+ if policy.config["framework"] == "tf2":
100
+ update_term = adv_squared - policy._moving_average_sqd_adv_norm
101
+ policy._moving_average_sqd_adv_norm.assign_add(rate * update_term)
102
+
103
+ # Exponentially weighted advantages.
104
+ c = tf.math.sqrt(policy._moving_average_sqd_adv_norm)
105
+ exp_advs = tf.math.exp(beta * (adv / (1e-8 + c)))
106
+ # Static graph.
107
+ else:
108
+ update_adv_norm = tf1.assign_add(
109
+ ref=policy._moving_average_sqd_adv_norm,
110
+ value=rate * (adv_squared - policy._moving_average_sqd_adv_norm),
111
+ )
112
+
113
+ # Exponentially weighted advantages.
114
+ with tf1.control_dependencies([update_adv_norm]):
115
+ exp_advs = tf.math.exp(
116
+ beta
117
+ * tf.math.divide(
118
+ adv,
119
+ 1e-8 + tf.math.sqrt(policy._moving_average_sqd_adv_norm),
120
+ )
121
+ )
122
+ exp_advs = tf.stop_gradient(exp_advs)
123
+
124
+ self.explained_variance = tf.reduce_mean(
125
+ explained_variance(cumulative_rewards, value_estimates)
126
+ )
127
+
128
+ else:
129
+ # Value function's loss term (MSE).
130
+ self.v_loss = tf.constant(0.0)
131
+ exp_advs = 1.0
132
+
133
+ # logprob loss alone tends to push action distributions to
134
+ # have very low entropy, resulting in worse performance for
135
+ # unfamiliar situations.
136
+ # A scaled logstd loss term encourages stochasticity, thus
137
+ # alleviate the problem to some extent.
138
+ logstd_coeff = policy.config["bc_logstd_coeff"]
139
+ if logstd_coeff > 0.0:
140
+ logstds = tf.reduce_sum(action_dist.log_std, axis=1)
141
+ else:
142
+ logstds = 0.0
143
+
144
+ self.p_loss = -1.0 * tf.reduce_mean(
145
+ exp_advs * (logprobs + logstd_coeff * logstds)
146
+ )
147
+
148
+ self.total_loss = self.p_loss + vf_loss_coeff * self.v_loss
149
+
150
+
151
+ # We need this builder function because we want to share the same
152
+ # custom logics between TF1 dynamic and TF2 eager policies.
153
+ def get_marwil_tf_policy(name: str, base: type) -> type:
154
+ """Construct a MARWILTFPolicy inheriting either dynamic or eager base policies.
155
+
156
+ Args:
157
+ base: Base class for this policy. DynamicTFPolicyV2 or EagerTFPolicyV2.
158
+
159
+ Returns:
160
+ A TF Policy to be used with MAML.
161
+ """
162
+
163
+ class MARWILTFPolicy(ValueNetworkMixin, PostprocessAdvantages, base):
164
+ def __init__(
165
+ self,
166
+ observation_space,
167
+ action_space,
168
+ config,
169
+ existing_model=None,
170
+ existing_inputs=None,
171
+ ):
172
+ # First thing first, enable eager execution if necessary.
173
+ base.enable_eager_execution_if_necessary()
174
+
175
+ # Initialize base class.
176
+ base.__init__(
177
+ self,
178
+ observation_space,
179
+ action_space,
180
+ config,
181
+ existing_inputs=existing_inputs,
182
+ existing_model=existing_model,
183
+ )
184
+
185
+ ValueNetworkMixin.__init__(self, config)
186
+ PostprocessAdvantages.__init__(self)
187
+
188
+ # Not needed for pure BC.
189
+ if config["beta"] != 0.0:
190
+ # Set up a tf-var for the moving avg (do this here to make it work
191
+ # with eager mode); "c^2" in the paper.
192
+ self._moving_average_sqd_adv_norm = get_variable(
193
+ config["moving_average_sqd_adv_norm_start"],
194
+ framework="tf",
195
+ tf_name="moving_average_of_advantage_norm",
196
+ trainable=False,
197
+ )
198
+
199
+ # Note: this is a bit ugly, but loss and optimizer initialization must
200
+ # happen after all the MixIns are initialized.
201
+ self.maybe_initialize_optimizer_and_loss()
202
+
203
+ @override(base)
204
+ def loss(
205
+ self,
206
+ model: Union[ModelV2, "tf.keras.Model"],
207
+ dist_class: Type[TFActionDistribution],
208
+ train_batch: SampleBatch,
209
+ ) -> Union[TensorType, List[TensorType]]:
210
+ model_out, _ = model(train_batch)
211
+ action_dist = dist_class(model_out, model)
212
+ value_estimates = model.value_function()
213
+
214
+ self._marwil_loss = MARWILLoss(
215
+ self,
216
+ value_estimates,
217
+ action_dist,
218
+ train_batch,
219
+ self.config["vf_coeff"],
220
+ self.config["beta"],
221
+ )
222
+
223
+ return self._marwil_loss.total_loss
224
+
225
+ @override(base)
226
+ def stats_fn(self, train_batch: SampleBatch) -> Dict[str, TensorType]:
227
+ stats = {
228
+ "policy_loss": self._marwil_loss.p_loss,
229
+ "total_loss": self._marwil_loss.total_loss,
230
+ }
231
+ if self.config["beta"] != 0.0:
232
+ stats["moving_average_sqd_adv_norm"] = self._moving_average_sqd_adv_norm
233
+ stats["vf_explained_var"] = self._marwil_loss.explained_variance
234
+ stats["vf_loss"] = self._marwil_loss.v_loss
235
+
236
+ return stats
237
+
238
+ @override(base)
239
+ def compute_gradients_fn(
240
+ self, optimizer: LocalOptimizer, loss: TensorType
241
+ ) -> ModelGradients:
242
+ return compute_gradients(self, optimizer, loss)
243
+
244
+ MARWILTFPolicy.__name__ = name
245
+ MARWILTFPolicy.__qualname__ = name
246
+
247
+ return MARWILTFPolicy
248
+
249
+
250
+ MARWILTF1Policy = get_marwil_tf_policy("MARWILTF1Policy", DynamicTFPolicyV2)
251
+ MARWILTF2Policy = get_marwil_tf_policy("MARWILTF2Policy", EagerTFPolicyV2)
lib/python3.12/site-packages/ray/rllib/algorithms/marwil/marwil_torch_policy.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict, List, Type, Union
2
+
3
+ from ray.rllib.algorithms.marwil.marwil_tf_policy import PostprocessAdvantages
4
+ from ray.rllib.evaluation.postprocessing import Postprocessing
5
+ from ray.rllib.models.modelv2 import ModelV2
6
+ from ray.rllib.models.torch.torch_action_dist import TorchDistributionWrapper
7
+ from ray.rllib.policy.sample_batch import SampleBatch
8
+ from ray.rllib.policy.torch_mixins import ValueNetworkMixin
9
+ from ray.rllib.policy.torch_policy_v2 import TorchPolicyV2
10
+ from ray.rllib.utils.annotations import override
11
+ from ray.rllib.utils.framework import try_import_torch
12
+ from ray.rllib.utils.numpy import convert_to_numpy
13
+ from ray.rllib.utils.torch_utils import apply_grad_clipping, explained_variance
14
+ from ray.rllib.utils.typing import TensorType
15
+
16
+ torch, _ = try_import_torch()
17
+
18
+
19
+ class MARWILTorchPolicy(ValueNetworkMixin, PostprocessAdvantages, TorchPolicyV2):
20
+ """PyTorch policy class used with Marwil."""
21
+
22
+ def __init__(self, observation_space, action_space, config):
23
+ TorchPolicyV2.__init__(
24
+ self,
25
+ observation_space,
26
+ action_space,
27
+ config,
28
+ max_seq_len=config["model"]["max_seq_len"],
29
+ )
30
+
31
+ ValueNetworkMixin.__init__(self, config)
32
+ PostprocessAdvantages.__init__(self)
33
+
34
+ # Not needed for pure BC.
35
+ if config["beta"] != 0.0:
36
+ # Set up a torch-var for the squared moving avg. advantage norm.
37
+ self._moving_average_sqd_adv_norm = torch.tensor(
38
+ [config["moving_average_sqd_adv_norm_start"]],
39
+ dtype=torch.float32,
40
+ requires_grad=False,
41
+ ).to(self.device)
42
+
43
+ # TODO: Don't require users to call this manually.
44
+ self._initialize_loss_from_dummy_batch()
45
+
46
+ @override(TorchPolicyV2)
47
+ def loss(
48
+ self,
49
+ model: ModelV2,
50
+ dist_class: Type[TorchDistributionWrapper],
51
+ train_batch: SampleBatch,
52
+ ) -> Union[TensorType, List[TensorType]]:
53
+ model_out, _ = model(train_batch)
54
+ action_dist = dist_class(model_out, model)
55
+ actions = train_batch[SampleBatch.ACTIONS]
56
+ # log\pi_\theta(a|s)
57
+ logprobs = action_dist.logp(actions)
58
+
59
+ # Advantage estimation.
60
+ if self.config["beta"] != 0.0:
61
+ cumulative_rewards = train_batch[Postprocessing.ADVANTAGES]
62
+ state_values = model.value_function()
63
+ adv = cumulative_rewards - state_values
64
+ adv_squared_mean = torch.mean(torch.pow(adv, 2.0))
65
+
66
+ explained_var = explained_variance(cumulative_rewards, state_values)
67
+ ev = torch.mean(explained_var)
68
+ model.tower_stats["explained_variance"] = ev
69
+
70
+ # Policy loss.
71
+ # Update averaged advantage norm.
72
+ rate = self.config["moving_average_sqd_adv_norm_update_rate"]
73
+ self._moving_average_sqd_adv_norm = (
74
+ rate * (adv_squared_mean.detach() - self._moving_average_sqd_adv_norm)
75
+ + self._moving_average_sqd_adv_norm
76
+ )
77
+ model.tower_stats[
78
+ "_moving_average_sqd_adv_norm"
79
+ ] = self._moving_average_sqd_adv_norm
80
+ # Exponentially weighted advantages.
81
+ exp_advs = torch.exp(
82
+ self.config["beta"]
83
+ * (adv / (1e-8 + torch.pow(self._moving_average_sqd_adv_norm, 0.5)))
84
+ ).detach()
85
+ # Value loss.
86
+ v_loss = 0.5 * adv_squared_mean
87
+ else:
88
+ # Policy loss (simple BC loss term).
89
+ exp_advs = 1.0
90
+ # Value loss.
91
+ v_loss = 0.0
92
+ model.tower_stats["v_loss"] = v_loss
93
+ # logprob loss alone tends to push action distributions to
94
+ # have very low entropy, resulting in worse performance for
95
+ # unfamiliar situations.
96
+ # A scaled logstd loss term encourages stochasticity, thus
97
+ # alleviate the problem to some extent.
98
+ logstd_coeff = self.config["bc_logstd_coeff"]
99
+ if logstd_coeff > 0.0:
100
+ logstds = torch.mean(action_dist.log_std, dim=1)
101
+ else:
102
+ logstds = 0.0
103
+
104
+ p_loss = -torch.mean(exp_advs * (logprobs + logstd_coeff * logstds))
105
+ model.tower_stats["p_loss"] = p_loss
106
+ # Combine both losses.
107
+ self.v_loss = v_loss
108
+ self.p_loss = p_loss
109
+ total_loss = p_loss + self.config["vf_coeff"] * v_loss
110
+ model.tower_stats["total_loss"] = total_loss
111
+ return total_loss
112
+
113
+ @override(TorchPolicyV2)
114
+ def stats_fn(self, train_batch: SampleBatch) -> Dict[str, TensorType]:
115
+ stats = {
116
+ "policy_loss": self.get_tower_stats("p_loss")[0].item(),
117
+ "total_loss": self.get_tower_stats("total_loss")[0].item(),
118
+ }
119
+ if self.config["beta"] != 0.0:
120
+ stats["moving_average_sqd_adv_norm"] = self.get_tower_stats(
121
+ "_moving_average_sqd_adv_norm"
122
+ )[0].item()
123
+ stats["vf_explained_var"] = self.get_tower_stats("explained_variance")[
124
+ 0
125
+ ].item()
126
+ stats["vf_loss"] = self.get_tower_stats("v_loss")[0].item()
127
+ return convert_to_numpy(stats)
128
+
129
+ def extra_grad_process(
130
+ self, optimizer: "torch.optim.Optimizer", loss: TensorType
131
+ ) -> Dict[str, TensorType]:
132
+ return apply_grad_clipping(self, optimizer, loss)
lib/python3.12/site-packages/ray/rllib/algorithms/marwil/torch/__init__.py ADDED
File without changes
lib/python3.12/site-packages/ray/rllib/algorithms/marwil/torch/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (212 Bytes). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/marwil/torch/__pycache__/marwil_torch_learner.cpython-312.pyc ADDED
Binary file (5.03 kB). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/marwil/torch/marwil_torch_learner.py ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any, Dict, Optional
2
+
3
+ from ray.rllib.algorithms.marwil.marwil import MARWILConfig
4
+ from ray.rllib.algorithms.marwil.marwil_learner import (
5
+ LEARNER_RESULTS_MOVING_AVG_SQD_ADV_NORM_KEY,
6
+ LEARNER_RESULTS_VF_EXPLAINED_VAR_KEY,
7
+ MARWILLearner,
8
+ )
9
+ from ray.rllib.core.columns import Columns
10
+ from ray.rllib.core.learner.learner import POLICY_LOSS_KEY, VF_LOSS_KEY
11
+ from ray.rllib.core.learner.torch.torch_learner import TorchLearner
12
+ from ray.rllib.evaluation.postprocessing import Postprocessing
13
+ from ray.rllib.utils.framework import try_import_torch
14
+ from ray.rllib.utils.torch_utils import explained_variance
15
+ from ray.rllib.utils.typing import TensorType
16
+
17
+ torch, nn = try_import_torch()
18
+
19
+
20
+ class MARWILTorchLearner(MARWILLearner, TorchLearner):
21
+ """Implements torch-specific MARWIL loss on top of MARWILLearner.
22
+
23
+ This class implements the MARWIL loss under `self.compute_loss_for_module()`.
24
+ """
25
+
26
+ def compute_loss_for_module(
27
+ self,
28
+ *,
29
+ module_id: str,
30
+ config: Optional[MARWILConfig] = None,
31
+ batch: Dict[str, Any],
32
+ fwd_out: Dict[str, TensorType]
33
+ ) -> TensorType:
34
+ module = self.module[module_id].unwrapped()
35
+
36
+ # Possibly apply masking to some sub loss terms and to the total loss term
37
+ # at the end. Masking could be used for RNN-based model (zero padded `batch`)
38
+ # and for PPO's batched value function (and bootstrap value) computations,
39
+ # for which we add an additional (artificial) timestep to each episode to
40
+ # simplify the actual computation.
41
+ if Columns.LOSS_MASK in batch:
42
+ num_valid = torch.sum(batch[Columns.LOSS_MASK])
43
+
44
+ def possibly_masked_mean(data_):
45
+ return torch.sum(data_[batch[Columns.LOSS_MASK]]) / num_valid
46
+
47
+ else:
48
+ possibly_masked_mean = torch.mean
49
+
50
+ action_dist_class_train = module.get_train_action_dist_cls()
51
+ curr_action_dist = action_dist_class_train.from_logits(
52
+ fwd_out[Columns.ACTION_DIST_INPUTS]
53
+ )
54
+
55
+ log_probs = curr_action_dist.logp(batch[Columns.ACTIONS])
56
+
57
+ # If beta is zero, we fall back to BC.
58
+ if config.beta == 0.0:
59
+ # Value function's loss term.
60
+ mean_vf_loss = 0.0
61
+ # Policy's loss term.
62
+ exp_weighted_advantages = 1.0
63
+ # Otherwise, compute advantages.
64
+ else:
65
+ # cumulative_rewards = batch[Columns.ADVANTAGES]
66
+ value_fn_out = module.compute_values(
67
+ batch, embeddings=fwd_out.get(Columns.EMBEDDINGS)
68
+ )
69
+ advantages = batch[Columns.VALUE_TARGETS] - value_fn_out
70
+ advantages_squared_mean = possibly_masked_mean(torch.pow(advantages, 2.0))
71
+
72
+ # Compute the value loss.
73
+ mean_vf_loss = 0.5 * advantages_squared_mean
74
+
75
+ # Compute the policy loss.
76
+ self.moving_avg_sqd_adv_norms_per_module[module_id] = (
77
+ config.moving_average_sqd_adv_norm_update_rate
78
+ * (
79
+ advantages_squared_mean.detach()
80
+ - self.moving_avg_sqd_adv_norms_per_module[module_id]
81
+ )
82
+ + self.moving_avg_sqd_adv_norms_per_module[module_id]
83
+ )
84
+ # Exponentially weighted advantages.
85
+ # TODO (simon): Check, if we need the mask here.
86
+ exp_weighted_advantages = torch.exp(
87
+ config.beta
88
+ * (
89
+ advantages
90
+ / (
91
+ 1e-8
92
+ + torch.pow(
93
+ self.moving_avg_sqd_adv_norms_per_module[module_id], 0.5
94
+ )
95
+ )
96
+ )
97
+ ).detach()
98
+
99
+ # Note, using solely a log-probability loss term tends to push the action
100
+ # distributions to have very low entropy, which results in worse performance
101
+ # specifically in unknown situations.
102
+ # Scaling the loss term with the logarithm of the action distribution's
103
+ # standard deviation encourages stochasticity in the policy.
104
+ if config.bc_logstd_coeff > 0.0:
105
+ log_stds = possibly_masked_mean(curr_action_dist.log_std, dim=1)
106
+ else:
107
+ log_stds = 0.0
108
+
109
+ # Compute the policy loss.
110
+ policy_loss = -possibly_masked_mean(
111
+ exp_weighted_advantages * (log_probs + config.bc_logstd_coeff * log_stds)
112
+ )
113
+
114
+ # Compute the total loss.
115
+ total_loss = policy_loss + config.vf_coeff * mean_vf_loss
116
+
117
+ # Log import loss stats. In case of the BC loss this is simply
118
+ # the policy loss.
119
+ if config.beta == 0.0:
120
+ self.metrics.log_value((module_id, POLICY_LOSS_KEY), policy_loss, window=1)
121
+ # Log more stats, if using the MARWIL loss.
122
+ else:
123
+ ma_sqd_adv_norms = self.moving_avg_sqd_adv_norms_per_module[module_id]
124
+ self.metrics.log_dict(
125
+ {
126
+ POLICY_LOSS_KEY: policy_loss,
127
+ VF_LOSS_KEY: mean_vf_loss,
128
+ LEARNER_RESULTS_VF_EXPLAINED_VAR_KEY: explained_variance(
129
+ batch[Postprocessing.VALUE_TARGETS], value_fn_out
130
+ ),
131
+ LEARNER_RESULTS_MOVING_AVG_SQD_ADV_NORM_KEY: ma_sqd_adv_norms,
132
+ },
133
+ key=module_id,
134
+ window=1, # <- single items (should not be mean/ema-reduced over time).
135
+ )
136
+
137
+ # Return the total loss.
138
+ return total_loss
lib/python3.12/site-packages/ray/rllib/algorithms/mock.py ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pickle
3
+ import time
4
+
5
+ import numpy as np
6
+
7
+ from ray.rllib.algorithms.algorithm import Algorithm, AlgorithmConfig
8
+ from ray.rllib.utils.annotations import override
9
+ from ray.tune import result as tune_result
10
+
11
+
12
+ class _MockTrainer(Algorithm):
13
+ """Mock Algorithm for use in tests."""
14
+
15
+ @classmethod
16
+ @override(Algorithm)
17
+ def get_default_config(cls) -> AlgorithmConfig:
18
+ return (
19
+ AlgorithmConfig()
20
+ .framework("tf")
21
+ .update_from_dict(
22
+ {
23
+ "mock_error": False,
24
+ "persistent_error": False,
25
+ "test_variable": 1,
26
+ "user_checkpoint_freq": 0,
27
+ "sleep": 0,
28
+ }
29
+ )
30
+ )
31
+
32
+ @classmethod
33
+ def default_resource_request(cls, config: AlgorithmConfig):
34
+ return None
35
+
36
+ @override(Algorithm)
37
+ def setup(self, config):
38
+ self.callbacks = self.config.callbacks_class()
39
+
40
+ # Add needed properties.
41
+ self.info = None
42
+ self.restored = False
43
+
44
+ @override(Algorithm)
45
+ def step(self):
46
+ if (
47
+ self.config.mock_error
48
+ and self.iteration == 1
49
+ and (self.config.persistent_error or not self.restored)
50
+ ):
51
+ raise Exception("mock error")
52
+ if self.config.sleep:
53
+ time.sleep(self.config.sleep)
54
+ result = dict(
55
+ episode_reward_mean=10, episode_len_mean=10, timesteps_this_iter=10, info={}
56
+ )
57
+ if self.config.user_checkpoint_freq > 0 and self.iteration > 0:
58
+ if self.iteration % self.config.user_checkpoint_freq == 0:
59
+ result.update({tune_result.SHOULD_CHECKPOINT: True})
60
+ return result
61
+
62
+ @override(Algorithm)
63
+ def save_checkpoint(self, checkpoint_dir):
64
+ path = os.path.join(checkpoint_dir, "mock_agent.pkl")
65
+ with open(path, "wb") as f:
66
+ pickle.dump(self.info, f)
67
+
68
+ @override(Algorithm)
69
+ def load_checkpoint(self, checkpoint_dir):
70
+ path = os.path.join(checkpoint_dir, "mock_agent.pkl")
71
+ with open(path, "rb") as f:
72
+ info = pickle.load(f)
73
+ self.info = info
74
+ self.restored = True
75
+
76
+ @staticmethod
77
+ @override(Algorithm)
78
+ def _get_env_id_and_creator(env_specifier, config):
79
+ # No env to register.
80
+ return None, None
81
+
82
+ def set_info(self, info):
83
+ self.info = info
84
+ return info
85
+
86
+ def get_info(self, sess=None):
87
+ return self.info
88
+
89
+
90
+ class _SigmoidFakeData(_MockTrainer):
91
+ """Algorithm that returns sigmoid learning curves.
92
+
93
+ This can be helpful for evaluating early stopping algorithms."""
94
+
95
+ @classmethod
96
+ @override(Algorithm)
97
+ def get_default_config(cls) -> AlgorithmConfig:
98
+ return AlgorithmConfig().update_from_dict(
99
+ {
100
+ "width": 100,
101
+ "height": 100,
102
+ "offset": 0,
103
+ "iter_time": 10,
104
+ "iter_timesteps": 1,
105
+ }
106
+ )
107
+
108
+ def step(self):
109
+ i = max(0, self.iteration - self.config.offset)
110
+ v = np.tanh(float(i) / self.config.width)
111
+ v *= self.config.height
112
+ return dict(
113
+ episode_reward_mean=v,
114
+ episode_len_mean=v,
115
+ timesteps_this_iter=self.config.iter_timesteps,
116
+ time_this_iter_s=self.config.iter_time,
117
+ info={},
118
+ )
119
+
120
+
121
+ class _ParameterTuningTrainer(_MockTrainer):
122
+ @classmethod
123
+ @override(Algorithm)
124
+ def get_default_config(cls) -> AlgorithmConfig:
125
+ return AlgorithmConfig().update_from_dict(
126
+ {
127
+ "reward_amt": 10,
128
+ "dummy_param": 10,
129
+ "dummy_param2": 15,
130
+ "iter_time": 10,
131
+ "iter_timesteps": 1,
132
+ }
133
+ )
134
+
135
+ def step(self):
136
+ return dict(
137
+ episode_reward_mean=self.config.reward_amt * self.iteration,
138
+ episode_len_mean=self.config.reward_amt,
139
+ timesteps_this_iter=self.config.iter_timesteps,
140
+ time_this_iter_s=self.config.iter_time,
141
+ info={},
142
+ )
143
+
144
+
145
+ def _algorithm_import_failed(trace):
146
+ """Returns dummy Algorithm class for if PyTorch etc. is not installed."""
147
+
148
+ class _AlgorithmImportFailed(Algorithm):
149
+ _name = "AlgorithmImportFailed"
150
+
151
+ def setup(self, config):
152
+ raise ImportError(trace)
153
+
154
+ return _AlgorithmImportFailed
lib/python3.12/site-packages/ray/rllib/algorithms/ppo/__init__.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from ray.rllib.algorithms.ppo.ppo import PPO, PPOConfig
2
+ from ray.rllib.algorithms.ppo.ppo_tf_policy import PPOTF1Policy, PPOTF2Policy
3
+ from ray.rllib.algorithms.ppo.ppo_torch_policy import PPOTorchPolicy
4
+
5
+ __all__ = [
6
+ "PPO",
7
+ "PPOConfig",
8
+ # @OldAPIStack
9
+ "PPOTF1Policy",
10
+ "PPOTF2Policy",
11
+ "PPOTorchPolicy",
12
+ ]
lib/python3.12/site-packages/ray/rllib/algorithms/ppo/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (513 Bytes). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/ppo/__pycache__/default_ppo_rl_module.cpython-312.pyc ADDED
Binary file (3.24 kB). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/ppo/__pycache__/ppo.cpython-312.pyc ADDED
Binary file (21.6 kB). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/ppo/__pycache__/ppo_catalog.cpython-312.pyc ADDED
Binary file (7.88 kB). View file
 
lib/python3.12/site-packages/ray/rllib/algorithms/ppo/__pycache__/ppo_learner.cpython-312.pyc ADDED
Binary file (6.29 kB). View file