drlab 0.2.2__tar.gz → 0.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. {drlab-0.2.2/src/drlab.egg-info → drlab-0.2.3}/PKG-INFO +1 -1
  2. {drlab-0.2.2 → drlab-0.2.3}/pyproject.toml +1 -1
  3. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/__init__.py +1 -1
  4. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/experiments/off_policy_experiment.py +3 -2
  5. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/experiments/on_policy_experiment.py +1 -1
  6. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/learners/off_policy/base.py +4 -4
  7. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/learners/off_policy/dqn.py +6 -4
  8. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/learners/off_policy/sac.py +4 -4
  9. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/learners/on_policy/actor_critic.py +8 -8
  10. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/learners/on_policy/base.py +5 -5
  11. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/learners/on_policy/ppo.py +4 -4
  12. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/learners/on_policy/reinforce.py +4 -4
  13. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/replay/replay_buffer.py +18 -8
  14. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/replay/transition_batch.py +6 -3
  15. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/runners/runner.py +29 -16
  16. {drlab-0.2.2 → drlab-0.2.3/src/drlab.egg-info}/PKG-INFO +1 -1
  17. {drlab-0.2.2 → drlab-0.2.3}/tests/test_learners.py +7 -7
  18. {drlab-0.2.2 → drlab-0.2.3}/tests/test_off_policy_experiment.py +3 -2
  19. {drlab-0.2.2 → drlab-0.2.3}/tests/test_public_api.py +1 -1
  20. {drlab-0.2.2 → drlab-0.2.3}/tests/test_replay.py +21 -9
  21. {drlab-0.2.2 → drlab-0.2.3}/tests/test_runner.py +23 -0
  22. {drlab-0.2.2 → drlab-0.2.3}/LICENSE +0 -0
  23. {drlab-0.2.2 → drlab-0.2.3}/README.md +0 -0
  24. {drlab-0.2.2 → drlab-0.2.3}/setup.cfg +0 -0
  25. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/controllers/__init__.py +0 -0
  26. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/controllers/base.py +0 -0
  27. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/controllers/e_greedy.py +0 -0
  28. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/controllers/gaussian.py +0 -0
  29. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/controllers/greedy.py +0 -0
  30. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/controllers/stochastic.py +0 -0
  31. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/controllers/warmup.py +0 -0
  32. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/experiments/__init__.py +0 -0
  33. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/learners/__init__.py +0 -0
  34. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/learners/off_policy/__init__.py +0 -0
  35. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/learners/off_policy/configs.py +0 -0
  36. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/learners/on_policy/__init__.py +0 -0
  37. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/learners/on_policy/configs.py +0 -0
  38. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/replay/__init__.py +0 -0
  39. {drlab-0.2.2 → drlab-0.2.3}/src/drlab/runners/__init__.py +0 -0
  40. {drlab-0.2.2 → drlab-0.2.3}/src/drlab.egg-info/SOURCES.txt +0 -0
  41. {drlab-0.2.2 → drlab-0.2.3}/src/drlab.egg-info/dependency_links.txt +0 -0
  42. {drlab-0.2.2 → drlab-0.2.3}/src/drlab.egg-info/requires.txt +0 -0
  43. {drlab-0.2.2 → drlab-0.2.3}/src/drlab.egg-info/top_level.txt +0 -0
  44. {drlab-0.2.2 → drlab-0.2.3}/tests/test_controllers.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drlab
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Deep Reinforcement Learning kit for research.
5
5
  Author: Tomas Osarte
6
6
  Keywords: reinforcement-learning,deep-learning,pytorch,gymnasium
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "drlab"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "Deep Reinforcement Learning kit for research."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -35,7 +35,7 @@ from drlab.learners import (
35
35
  from drlab.replay import ReplayBuffer, TransitionBatch
36
36
  from drlab.runners import Runner
37
37
 
38
- __version__ = "0.2.2"
38
+ __version__ = "0.2.3"
39
39
 
40
40
  __all__ = [
41
41
  "__version__",
@@ -130,7 +130,8 @@ class OffPolicyExperiment:
130
130
  states=batch.states,
131
131
  actions=batch.actions,
132
132
  rewards=batch.rewards,
133
- dones=batch.dones,
133
+ terminated=batch.terminated,
134
+ truncated=batch.truncated,
134
135
  next_states=batch.next_states,
135
136
  returns=batch.returns,
136
137
  )
@@ -142,7 +143,7 @@ class OffPolicyExperiment:
142
143
  for _ in range(self.grad_repeats):
143
144
  mb = self._make_minibatch(batch, last_episode)
144
145
  total_loss += self.learner.train(
145
- mb.rewards, mb.dones, mb.states, mb.actions, mb.next_states
146
+ mb.rewards, mb.terminated, mb.states, mb.actions, mb.next_states
146
147
  )
147
148
 
148
149
  return total_loss / self.grad_repeats
@@ -65,7 +65,7 @@ class OnPolicyExperiment:
65
65
  # 2. Learn from batch
66
66
  loss = self.learner.train(
67
67
  batch.rewards,
68
- batch.dones,
68
+ batch.terminated,
69
69
  batch.states,
70
70
  batch.actions,
71
71
  batch.next_states,
@@ -88,7 +88,7 @@ class OffPolicyLearner(ABC):
88
88
  self,
89
89
  model: th.nn.Module,
90
90
  rewards: th.Tensor,
91
- dones: th.Tensor,
91
+ terminated: th.Tensor,
92
92
  states: th.Tensor,
93
93
  actions: th.Tensor,
94
94
  next_states: th.Tensor,
@@ -102,7 +102,7 @@ class OffPolicyLearner(ABC):
102
102
  value = regularizer(
103
103
  model,
104
104
  rewards,
105
- dones,
105
+ terminated,
106
106
  states,
107
107
  actions,
108
108
  next_states,
@@ -139,9 +139,9 @@ class OffPolicyLearner(ABC):
139
139
  def train(
140
140
  self,
141
141
  rewards: th.Tensor,
142
- dones: th.Tensor,
142
+ terminated: th.Tensor,
143
143
  states: th.Tensor,
144
144
  actions: th.Tensor,
145
145
  next_states: th.Tensor,
146
146
  ) -> float:
147
- pass
147
+ pass
@@ -58,7 +58,7 @@ class DQNLearner(OffPolicyLearner):
58
58
  def train(
59
59
  self,
60
60
  rewards: th.Tensor, # float32, [B,1]
61
- dones: th.Tensor, # bool or float(0/1), [B,1]
61
+ terminated: th.Tensor, # bool or float(0/1), [B,1]
62
62
  states: th.Tensor, # float32, [B, obs_dim] or [B,C,H,W]
63
63
  actions: th.Tensor, # int64, [B,1]
64
64
  next_states: th.Tensor, # float32, same as states
@@ -66,8 +66,10 @@ class DQNLearner(OffPolicyLearner):
66
66
  self.model.train(True)
67
67
 
68
68
  # Compute targets
69
- not_done = 1.0 - dones.float()
70
- targets = rewards + self.gamma * (not_done * self.next_values(next_states))
69
+ not_terminated = 1.0 - terminated.float()
70
+ targets = rewards + self.gamma * (
71
+ not_terminated * self.next_values(next_states)
72
+ )
71
73
  pred = self.current_values(states, actions)
72
74
 
73
75
  # Compute loss
@@ -75,7 +77,7 @@ class DQNLearner(OffPolicyLearner):
75
77
  reg_loss = self.regularization_loss(
76
78
  self.model,
77
79
  rewards=rewards,
78
- dones=dones,
80
+ terminated=terminated,
79
81
  states=states,
80
82
  actions=actions,
81
83
  next_states=next_states,
@@ -90,7 +90,7 @@ class SACLearner(OffPolicyLearner):
90
90
  def train(
91
91
  self,
92
92
  rewards: th.Tensor, # float32, [B,1]
93
- dones: th.Tensor, # bool or float(0/1), [B,1]
93
+ terminated: th.Tensor, # bool or float(0/1), [B,1]
94
94
  states: th.Tensor, # float32, [B, obs_dim] or [B,C,H,W]
95
95
  actions: th.Tensor, # float32, [B, action_dim]
96
96
  next_states: th.Tensor, # float32, same as states
@@ -110,9 +110,9 @@ class SACLearner(OffPolicyLearner):
110
110
  target_q2 = self.critic2_target(next_action_states)
111
111
  target_q = th.min(target_q1, target_q2)
112
112
 
113
- not_done = 1.0 - dones.float()
113
+ not_terminated = 1.0 - terminated.float()
114
114
  targets = rewards + self.config.gamma * (
115
- not_done * (target_q - self.alpha.detach() * next_log_probs)
115
+ not_terminated * (target_q - self.alpha.detach() * next_log_probs)
116
116
  )
117
117
 
118
118
  action_states = th.cat([states, actions], dim=-1)
@@ -149,7 +149,7 @@ class SACLearner(OffPolicyLearner):
149
149
  reg_loss = self.regularization_loss(
150
150
  self.actor,
151
151
  rewards=rewards,
152
- dones=dones,
152
+ terminated=terminated,
153
153
  states=states,
154
154
  actions=actions,
155
155
  next_states=next_states,
@@ -28,14 +28,14 @@ class ActorCriticLearner(OnPolicyLearner):
28
28
  self,
29
29
  returns: th.Tensor,
30
30
  rewards: th.Tensor,
31
- dones: th.Tensor,
31
+ terminated: th.Tensor,
32
32
  values: th.Tensor,
33
33
  next_values: th.Tensor,
34
34
  ) -> th.Tensor:
35
35
  if self.config.value_targets == ValueTargets.RETURNS:
36
36
  targets = returns
37
37
  elif self.config.value_targets == ValueTargets.TD:
38
- targets = rewards + self.config.gamma * (~dones * next_values)
38
+ targets = rewards + self.config.gamma * (~terminated * next_values)
39
39
  else:
40
40
  raise ValueError(f"Unknown value_targets: {self.config.value_targets}")
41
41
  return self.config.value_criterion(values, targets)
@@ -44,13 +44,13 @@ class ActorCriticLearner(OnPolicyLearner):
44
44
  self,
45
45
  returns: th.Tensor,
46
46
  rewards: th.Tensor,
47
- dones: th.Tensor,
47
+ terminated: th.Tensor,
48
48
  values: th.Tensor,
49
49
  next_values: th.Tensor
50
50
  ) -> th.Tensor:
51
51
  advantages = None
52
52
  if self.config.advantage_bootstrap:
53
- advantages = rewards + self.config.gamma * (~dones * next_values)
53
+ advantages = rewards + self.config.gamma * (~terminated * next_values)
54
54
  else:
55
55
  advantages = returns
56
56
  if self.config.use_bias:
@@ -74,7 +74,7 @@ class ActorCriticLearner(OnPolicyLearner):
74
74
  def train(
75
75
  self,
76
76
  rewards: th.Tensor,
77
- dones: th.Tensor,
77
+ terminated: th.Tensor,
78
78
  states: th.Tensor,
79
79
  actions: th.Tensor,
80
80
  next_states: th.Tensor,
@@ -106,7 +106,7 @@ class ActorCriticLearner(OnPolicyLearner):
106
106
  advantages = self._advantages(
107
107
  returns=returns,
108
108
  rewards=rewards,
109
- dones=dones,
109
+ terminated=terminated,
110
110
  values=values,
111
111
  next_values=next_values,
112
112
  )
@@ -119,7 +119,7 @@ class ActorCriticLearner(OnPolicyLearner):
119
119
  value_loss = self.config.value_lambda * self._value_loss(
120
120
  returns=returns,
121
121
  rewards=rewards,
122
- dones=dones,
122
+ terminated=terminated,
123
123
  values=values,
124
124
  next_values=next_values,
125
125
  )
@@ -128,7 +128,7 @@ class ActorCriticLearner(OnPolicyLearner):
128
128
 
129
129
  reg_loss = self._regularization_loss(
130
130
  rewards=rewards,
131
- dones=dones,
131
+ terminated=terminated,
132
132
  states=states,
133
133
  actions=actions,
134
134
  next_states=next_states,
@@ -50,7 +50,7 @@ class OnPolicyLearner:
50
50
  def _regularization_loss(
51
51
  self,
52
52
  rewards: th.Tensor,
53
- dones: th.Tensor,
53
+ terminated: th.Tensor,
54
54
  states: th.Tensor,
55
55
  actions: th.Tensor,
56
56
  next_states: th.Tensor,
@@ -59,7 +59,7 @@ class OnPolicyLearner:
59
59
  return 0.0
60
60
 
61
61
  return sum(
62
- lam * reg(self.actor, rewards, dones, states, actions, next_states)
62
+ lam * reg(self.actor, rewards, terminated, states, actions, next_states)
63
63
  for reg, lam in zip(self.config.regularizers, self.config.reg_lams)
64
64
  )
65
65
 
@@ -67,7 +67,7 @@ class OnPolicyLearner:
67
67
  self,
68
68
  returns: th.Tensor,
69
69
  rewards: th.Tensor,
70
- dones: th.Tensor,
70
+ terminated: th.Tensor,
71
71
  values: th.Tensor,
72
72
  next_values: th.Tensor,
73
73
  ) -> th.Tensor:
@@ -106,7 +106,7 @@ class OnPolicyLearner:
106
106
  self,
107
107
  returns: th.Tensor,
108
108
  rewards: th.Tensor,
109
- dones: th.Tensor,
109
+ terminated: th.Tensor,
110
110
  values: th.Tensor,
111
111
  next_values: th.Tensor
112
112
  ) -> th.Tensor:
@@ -115,7 +115,7 @@ class OnPolicyLearner:
115
115
  def train(
116
116
  self,
117
117
  rewards: th.Tensor, # float32, [B,1]
118
- dones: th.Tensor, # bool or float(0/1), [B,1]
118
+ terminated: th.Tensor, # bool or float(0/1), [B,1]
119
119
  states: th.Tensor, # float32, [B, obs_dim] or [B,C,H,W]
120
120
  actions: th.Tensor, # int64, [B,1]
121
121
  next_states: th.Tensor, # float32, same as states
@@ -38,7 +38,7 @@ class PPOLearner(ActorCriticLearner):
38
38
  def train(
39
39
  self,
40
40
  rewards: th.Tensor,
41
- dones: th.Tensor,
41
+ terminated: th.Tensor,
42
42
  states: th.Tensor,
43
43
  actions: th.Tensor,
44
44
  next_states: th.Tensor,
@@ -69,7 +69,7 @@ class PPOLearner(ActorCriticLearner):
69
69
  advantages = self._advantages(
70
70
  returns=returns,
71
71
  rewards=rewards,
72
- dones=dones,
72
+ terminated=terminated,
73
73
  values=old_values,
74
74
  next_values=next_values,
75
75
  )
@@ -96,7 +96,7 @@ class PPOLearner(ActorCriticLearner):
96
96
  value_loss = self.config.value_lambda * self._value_loss(
97
97
  returns=returns,
98
98
  rewards=rewards,
99
- dones=dones,
99
+ terminated=terminated,
100
100
  values=values,
101
101
  next_values=next_values,
102
102
  )
@@ -105,7 +105,7 @@ class PPOLearner(ActorCriticLearner):
105
105
 
106
106
  reg_loss = self._regularization_loss(
107
107
  rewards=rewards,
108
- dones=dones,
108
+ terminated=terminated,
109
109
  states=states,
110
110
  actions=actions,
111
111
  next_states=next_states,
@@ -18,7 +18,7 @@ class ReinforceLearner(OnPolicyLearner):
18
18
  self,
19
19
  returns: th.Tensor,
20
20
  rewards: th.Tensor,
21
- dones: th.Tensor,
21
+ terminated: th.Tensor,
22
22
  values: th.Tensor | None,
23
23
  next_values: th.Tensor | None,
24
24
  ) -> th.Tensor:
@@ -34,7 +34,7 @@ class ReinforceLearner(OnPolicyLearner):
34
34
  def train(
35
35
  self,
36
36
  rewards: th.Tensor,
37
- dones: th.Tensor,
37
+ terminated: th.Tensor,
38
38
  states: th.Tensor,
39
39
  actions: th.Tensor,
40
40
  next_states: th.Tensor,
@@ -50,7 +50,7 @@ class ReinforceLearner(OnPolicyLearner):
50
50
  advantages = self._advantages(
51
51
  returns=returns,
52
52
  rewards=rewards,
53
- dones=dones,
53
+ terminated=terminated,
54
54
  values=None,
55
55
  next_values=None,
56
56
  )
@@ -60,7 +60,7 @@ class ReinforceLearner(OnPolicyLearner):
60
60
  entropy_loss = self._entropy_loss(logits)
61
61
  reg_loss = self._regularization_loss(
62
62
  rewards=rewards,
63
- dones=dones,
63
+ terminated=terminated,
64
64
  states=states,
65
65
  actions=actions,
66
66
  next_states=next_states,
@@ -27,7 +27,8 @@ class ReplayBuffer:
27
27
  self.actions = np.zeros((capacity, *action_shape), dtype=action_dtype)
28
28
 
29
29
  self.rewards = np.zeros((capacity, 1), dtype=np.float32)
30
- self.dones = np.zeros((capacity, 1), dtype=np.bool_)
30
+ self.terminated = np.zeros((capacity, 1), dtype=np.bool_)
31
+ self.truncated = np.zeros((capacity, 1), dtype=np.bool_)
31
32
  self.returns = np.zeros((capacity, 1), dtype=np.float32)
32
33
 
33
34
  self.ptr = 0
@@ -41,7 +42,8 @@ class ReplayBuffer:
41
42
  states: np.ndarray, # [B, *obs_shape]
42
43
  actions: np.ndarray, # [B] or [B,1]
43
44
  rewards: np.ndarray, # [B] or [B,1]
44
- dones: np.ndarray, # [B] or [B,1] (bool)
45
+ terminated: np.ndarray, # [B] or [B,1] (bool)
46
+ truncated: np.ndarray, # [B] or [B,1] (bool)
45
47
  next_states: np.ndarray, # [B, *obs_shape]
46
48
  returns: np.ndarray # [B, 1]
47
49
 
@@ -56,7 +58,8 @@ class ReplayBuffer:
56
58
  else:
57
59
  actions = np.asarray(actions, dtype=np.int64).reshape(B, 1)
58
60
  rewards = np.asarray(rewards, dtype=np.float32).reshape(B, 1)
59
- dones = np.asarray(dones, dtype=np.bool_).reshape(B, 1)
61
+ terminated = np.asarray(terminated, dtype=np.bool_).reshape(B, 1)
62
+ truncated = np.asarray(truncated, dtype=np.bool_).reshape(B, 1)
60
63
  returns = np.asarray(returns, dtype=np.float32).reshape(B, 1)
61
64
 
62
65
  idx = (self.ptr + np.arange(B)) % self.capacity
@@ -65,7 +68,8 @@ class ReplayBuffer:
65
68
  self.next_states[idx] = next_states
66
69
  self.actions[idx] = actions
67
70
  self.rewards[idx] = rewards
68
- self.dones[idx] = dones
71
+ self.terminated[idx] = terminated
72
+ self.truncated[idx] = truncated
69
73
  self.returns[idx] = returns
70
74
 
71
75
  self.ptr = (self.ptr + B) % self.capacity
@@ -76,9 +80,12 @@ class ReplayBuffer:
76
80
  next_states = th.from_numpy(self.next_states[:self.size]).to(self.device)
77
81
  actions = th.from_numpy(self.actions[:self.size]).to(self.device)
78
82
  rewards = th.from_numpy(self.rewards[:self.size]).to(self.device)
79
- dones = th.from_numpy(self.dones[:self.size]).to(self.device)
83
+ terminated = th.from_numpy(self.terminated[:self.size]).to(self.device)
84
+ truncated = th.from_numpy(self.truncated[:self.size]).to(self.device)
80
85
  returns = th.from_numpy(self.returns[:self.size]).to(self.device)
81
- return TransitionBatch(states, actions, rewards, dones, next_states, returns)
86
+ return TransitionBatch(
87
+ states, actions, rewards, terminated, truncated, next_states, returns
88
+ )
82
89
 
83
90
  def sample(self, batch_size: int) -> TransitionBatch:
84
91
  if self.size == 0:
@@ -90,7 +97,10 @@ class ReplayBuffer:
90
97
  next_states = th.from_numpy(self.next_states[idx]).to(self.device)
91
98
  actions = th.from_numpy(self.actions[idx]).to(self.device) # long already
92
99
  rewards = th.from_numpy(self.rewards[idx]).to(self.device) # float32
93
- dones = th.from_numpy(self.dones[idx]).to(self.device) # bool
100
+ terminated = th.from_numpy(self.terminated[idx]).to(self.device) # bool
101
+ truncated = th.from_numpy(self.truncated[idx]).to(self.device) # bool
94
102
  returns = th.from_numpy(self.returns[idx]).to(self.device) # float32
95
103
 
96
- return TransitionBatch(states, actions, rewards, dones, next_states, returns)
104
+ return TransitionBatch(
105
+ states, actions, rewards, terminated, truncated, next_states, returns
106
+ )
@@ -8,7 +8,8 @@ class TransitionBatch:
8
8
  states: th.Tensor | np.ndarray # [B, obs_dim]
9
9
  actions: th.Tensor | np.ndarray # [B, 1] (int64/float32)
10
10
  rewards: th.Tensor | np.ndarray # [B, 1] (float)
11
- dones: th.Tensor | np.ndarray # [B, 1] (bool)
11
+ terminated: th.Tensor | np.ndarray # [B, 1] (bool), disables bootstrapping
12
+ truncated: th.Tensor | np.ndarray # [B, 1] (bool), ends an episode only
12
13
  next_states: th.Tensor | np.ndarray # [B, obs_dim]
13
14
  returns: th.Tensor | np.ndarray # [B, 1] (float)
14
15
 
@@ -24,7 +25,8 @@ class TransitionBatch:
24
25
  states=move(self.states),
25
26
  actions=move(self.actions),
26
27
  rewards=move(self.rewards),
27
- dones=move(self.dones),
28
+ terminated=move(self.terminated),
29
+ truncated=move(self.truncated),
28
30
  next_states=move(self.next_states),
29
31
  returns=move(self.returns),
30
32
  )
@@ -34,7 +36,8 @@ class TransitionBatch:
34
36
  states=th.cat([self.states, other.states], dim=0),
35
37
  actions=th.cat([self.actions, other.actions], dim=0),
36
38
  rewards=th.cat([self.rewards, other.rewards], dim=0),
37
- dones=th.cat([self.dones, other.dones], dim=0),
39
+ terminated=th.cat([self.terminated, other.terminated], dim=0),
40
+ truncated=th.cat([self.truncated, other.truncated], dim=0),
38
41
  next_states=th.cat([self.next_states, other.next_states], dim=0),
39
42
  returns=th.cat([self.returns, other.returns], dim=0),
40
43
  )
@@ -34,7 +34,8 @@ class Runner:
34
34
  self.return_last_episode = return_last_episode
35
35
  self.ep_len, self.ep_ret = 0, 0.0
36
36
  self._ep_states, self._ep_actions, self._ep_rewards = [], [], []
37
- self._ep_dones, self._ep_next_states, self._ep_returns = [], [], []
37
+ self._ep_terminated, self._ep_truncated = [], []
38
+ self._ep_next_states, self._ep_returns = [], []
38
39
 
39
40
  def _discounted_returns(self, rewards: list[float]) -> list[float]:
40
41
  out = [0.0] * len(rewards)
@@ -68,7 +69,8 @@ class Runner:
68
69
  states,
69
70
  actions,
70
71
  rewards,
71
- dones,
72
+ terminated,
73
+ truncated,
72
74
  next_states,
73
75
  returns,
74
76
  as_numpy: bool,
@@ -76,18 +78,22 @@ class Runner:
76
78
  states = np.asarray(states, dtype=np.float32)
77
79
  actions = self._actions_to_array(actions)
78
80
  rewards = np.asarray(rewards, dtype=np.float32).reshape(-1, 1)
79
- dones = np.asarray(dones, dtype=np.bool_).reshape(-1, 1)
81
+ terminated = np.asarray(terminated, dtype=np.bool_).reshape(-1, 1)
82
+ truncated = np.asarray(truncated, dtype=np.bool_).reshape(-1, 1)
80
83
  next_states = np.asarray(next_states, dtype=np.float32)
81
84
  returns = np.asarray(returns, dtype=np.float32).reshape(-1, 1)
82
85
 
83
86
  if as_numpy:
84
- return TransitionBatch(states, actions, rewards, dones, next_states, returns)
87
+ return TransitionBatch(
88
+ states, actions, rewards, terminated, truncated, next_states, returns
89
+ )
85
90
 
86
91
  return TransitionBatch(
87
92
  states=th.from_numpy(states),
88
93
  actions=th.from_numpy(actions),
89
94
  rewards=th.from_numpy(rewards),
90
- dones=th.from_numpy(dones),
95
+ terminated=th.from_numpy(terminated),
96
+ truncated=th.from_numpy(truncated),
91
97
  next_states=th.from_numpy(next_states),
92
98
  returns=th.from_numpy(returns),
93
99
  )
@@ -102,7 +108,9 @@ class Runner:
102
108
  last_episode = None
103
109
  ep_returns, ep_lengths = [], []
104
110
  ep_start = 0
105
- states, actions, rewards, dones, next_states, returns = [], [], [], [], [], []
111
+ states, actions, rewards = [], [], []
112
+ terminated_flags, truncated_flags = [], []
113
+ next_states, returns = [], []
106
114
  run_one_episode = num_steps <= 0
107
115
 
108
116
  st = 0
@@ -113,14 +121,15 @@ class Runner:
113
121
  with th.inference_mode():
114
122
  action_t = self.controller.choose(state_t)
115
123
  action = self._action_to_env(action_t)
116
- next_state, reward, terminated, truncated, _ = self.env.step(action)
117
- done = terminated or truncated
124
+ next_state, reward, terminated, truncated, _ = self.env.step(action)
125
+ episode_end = terminated or truncated
118
126
 
119
127
  # 2. Store transition
120
128
  states.append(self.state)
121
129
  actions.append(action)
122
130
  rewards.append(reward)
123
- dones.append(done)
131
+ terminated_flags.append(terminated)
132
+ truncated_flags.append(truncated)
124
133
  next_states.append(next_state)
125
134
  returns.append(0)
126
135
  self.ep_ret += reward
@@ -129,34 +138,37 @@ class Runner:
129
138
  self._ep_states.append(self.state)
130
139
  self._ep_actions.append(action)
131
140
  self._ep_rewards.append(reward)
132
- self._ep_dones.append(done)
141
+ self._ep_terminated.append(terminated)
142
+ self._ep_truncated.append(truncated)
133
143
  self._ep_next_states.append(next_state)
134
144
  self._ep_returns.append(0)
135
145
 
136
146
  # 3. Compute cumlative returns
137
- if self.calculate_returns and (done or st == num_steps - 1):
147
+ if self.calculate_returns and (episode_end or st == num_steps - 1):
138
148
  ep_rewards = rewards[ep_start: st + 1]
139
149
  discounted_returns = self._discounted_returns(ep_rewards)
140
150
 
141
151
  returns[ep_start: st + 1] = discounted_returns
142
- if self.return_last_episode and done:
152
+ if self.return_last_episode and episode_end:
143
153
  self._ep_returns = discounted_returns
144
154
 
145
155
  # 4. Update state
146
156
  self.state = next_state
147
- if done:
157
+ if episode_end:
148
158
  if self.return_last_episode:
149
159
  last_episode = self._make_batch(
150
160
  self._ep_states,
151
161
  self._ep_actions,
152
162
  self._ep_rewards,
153
- self._ep_dones,
163
+ self._ep_terminated,
164
+ self._ep_truncated,
154
165
  self._ep_next_states,
155
166
  self._ep_returns,
156
167
  as_numpy,
157
168
  )
158
169
  self._ep_states, self._ep_actions, self._ep_rewards = [], [], []
159
- self._ep_dones, self._ep_next_states, self._ep_returns = [], [], []
170
+ self._ep_terminated, self._ep_truncated = [], []
171
+ self._ep_next_states, self._ep_returns = [], []
160
172
  ep_start = st + 1
161
173
  ep_returns.append(self.ep_ret)
162
174
  ep_lengths.append(self.ep_len)
@@ -173,7 +185,8 @@ class Runner:
173
185
  states,
174
186
  actions,
175
187
  rewards,
176
- dones,
188
+ terminated_flags,
189
+ truncated_flags,
177
190
  next_states,
178
191
  returns,
179
192
  as_numpy,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drlab
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Deep Reinforcement Learning kit for research.
5
5
  Author: Tomas Osarte
6
6
  Keywords: reinforcement-learning,deep-learning,pytorch,gymnasium
@@ -47,7 +47,7 @@ class LearnerSmokeTest(unittest.TestCase):
47
47
 
48
48
  loss = learner.train(
49
49
  rewards=th.tensor([[1.0], [0.0], [1.0]]),
50
- dones=th.tensor([[False], [True], [False]]),
50
+ terminated=th.tensor([[False], [True], [False]]),
51
51
  states=th.randn(3, 4),
52
52
  actions=th.tensor([[0], [1], [0]], dtype=th.long),
53
53
  next_states=th.randn(3, 4),
@@ -90,7 +90,7 @@ class LearnerSmokeTest(unittest.TestCase):
90
90
 
91
91
  loss = learner.train(
92
92
  rewards=th.tensor([[1.0], [0.0], [1.0]]),
93
- dones=th.tensor([[False], [True], [False]]),
93
+ terminated=th.tensor([[False], [True], [False]]),
94
94
  states=th.randn(3, obs_dim),
95
95
  actions=th.randn(3, action_dim).tanh(),
96
96
  next_states=th.randn(3, obs_dim),
@@ -125,7 +125,7 @@ class LearnerSmokeTest(unittest.TestCase):
125
125
  )
126
126
  calls = []
127
127
 
128
- def actor_l2(model, rewards, dones, states, actions, next_states):
128
+ def actor_l2(model, rewards, terminated, states, actions, next_states):
129
129
  calls.append(model)
130
130
  return sum(param.square().sum() for param in model.parameters())
131
131
 
@@ -145,7 +145,7 @@ class LearnerSmokeTest(unittest.TestCase):
145
145
 
146
146
  learner.train(
147
147
  rewards=th.tensor([[1.0], [0.0], [1.0]]),
148
- dones=th.tensor([[False], [True], [False]]),
148
+ terminated=th.tensor([[False], [True], [False]]),
149
149
  states=th.randn(3, obs_dim),
150
150
  actions=th.randn(3, action_dim).tanh(),
151
151
  next_states=th.randn(3, obs_dim),
@@ -171,7 +171,7 @@ class LearnerSmokeTest(unittest.TestCase):
171
171
 
172
172
  loss = learner.train(
173
173
  rewards=th.tensor([[1.0], [0.5], [0.0]]),
174
- dones=th.tensor([[False], [False], [True]]),
174
+ terminated=th.tensor([[False], [False], [True]]),
175
175
  states=th.randn(3, 4),
176
176
  actions=th.tensor([[0], [1], [0]], dtype=th.long),
177
177
  next_states=th.randn(3, 4),
@@ -199,7 +199,7 @@ class LearnerSmokeTest(unittest.TestCase):
199
199
 
200
200
  loss = learner.train(
201
201
  rewards=th.tensor([[1.0], [0.5], [0.0]]),
202
- dones=th.tensor([[False], [False], [True]]),
202
+ terminated=th.tensor([[False], [False], [True]]),
203
203
  states=th.randn(3, 4),
204
204
  actions=th.tensor([[0], [1], [0]], dtype=th.long),
205
205
  next_states=th.randn(3, 4),
@@ -227,7 +227,7 @@ class LearnerSmokeTest(unittest.TestCase):
227
227
 
228
228
  loss = learner.train(
229
229
  rewards=th.tensor([[1.0], [0.5], [0.0]]),
230
- dones=th.tensor([[False], [False], [True]]),
230
+ terminated=th.tensor([[False], [False], [True]]),
231
231
  states=th.randn(3, 4),
232
232
  actions=th.tensor([[0], [1], [0]], dtype=th.long),
233
233
  next_states=th.randn(3, 4),
@@ -49,7 +49,7 @@ class CountingLearner(OffPolicyLearner):
49
49
  def train(
50
50
  self,
51
51
  rewards: th.Tensor,
52
- dones: th.Tensor,
52
+ terminated: th.Tensor,
53
53
  states: th.Tensor,
54
54
  actions: th.Tensor,
55
55
  next_states: th.Tensor,
@@ -63,7 +63,8 @@ def one_transition() -> TransitionBatch:
63
63
  states=np.asarray([[0.0]], dtype=np.float32),
64
64
  actions=np.asarray([[0]], dtype=np.int64),
65
65
  rewards=np.asarray([[1.0]], dtype=np.float32),
66
- dones=np.asarray([[False]], dtype=np.bool_),
66
+ terminated=np.asarray([[False]], dtype=np.bool_),
67
+ truncated=np.asarray([[False]], dtype=np.bool_),
67
68
  next_states=np.asarray([[1.0]], dtype=np.float32),
68
69
  returns=np.asarray([[1.0]], dtype=np.float32),
69
70
  )
@@ -37,7 +37,7 @@ class PublicAPITest(unittest.TestCase):
37
37
  "WarmupController",
38
38
  }
39
39
 
40
- self.assertEqual(drlab.__version__, "0.2.2")
40
+ self.assertEqual(drlab.__version__, "0.2.3")
41
41
  self.assertTrue(expected.issubset(set(drlab.__all__)))
42
42
  for name in expected:
43
43
  self.assertTrue(hasattr(drlab, name), name)
@@ -12,7 +12,8 @@ class TransitionBatchTest(unittest.TestCase):
12
12
  states=th.zeros(2, 3),
13
13
  actions=th.zeros(2, 1, dtype=th.long),
14
14
  rewards=th.ones(2, 1),
15
- dones=th.zeros(2, 1, dtype=th.bool),
15
+ terminated=th.zeros(2, 1, dtype=th.bool),
16
+ truncated=th.zeros(2, 1, dtype=th.bool),
16
17
  next_states=th.ones(2, 3),
17
18
  returns=th.full((2, 1), 2.0),
18
19
  )
@@ -28,7 +29,8 @@ class TransitionBatchTest(unittest.TestCase):
28
29
  states=np.zeros((2, 3), dtype=np.float32),
29
30
  actions=np.zeros((2, 1), dtype=np.int64),
30
31
  rewards=np.ones((2, 1), dtype=np.float32),
31
- dones=np.zeros((2, 1), dtype=np.bool_),
32
+ terminated=np.zeros((2, 1), dtype=np.bool_),
33
+ truncated=np.zeros((2, 1), dtype=np.bool_),
32
34
  next_states=np.ones((2, 3), dtype=np.float32),
33
35
  returns=np.full((2, 1), 2.0, dtype=np.float32),
34
36
  )
@@ -37,7 +39,8 @@ class TransitionBatchTest(unittest.TestCase):
37
39
 
38
40
  self.assertEqual(moved.states.dtype, th.float32)
39
41
  self.assertEqual(moved.actions.dtype, th.int64)
40
- self.assertEqual(moved.dones.dtype, th.bool)
42
+ self.assertEqual(moved.terminated.dtype, th.bool)
43
+ self.assertEqual(moved.truncated.dtype, th.bool)
41
44
  self.assertTrue(th.equal(moved.returns, th.full((2, 1), 2.0)))
42
45
 
43
46
  def test_cat_concatenates_every_field(self):
@@ -45,7 +48,8 @@ class TransitionBatchTest(unittest.TestCase):
45
48
  states=th.zeros(1, 2),
46
49
  actions=th.zeros(1, 1, dtype=th.long),
47
50
  rewards=th.ones(1, 1),
48
- dones=th.zeros(1, 1, dtype=th.bool),
51
+ terminated=th.zeros(1, 1, dtype=th.bool),
52
+ truncated=th.ones(1, 1, dtype=th.bool),
49
53
  next_states=th.ones(1, 2),
50
54
  returns=th.ones(1, 1),
51
55
  )
@@ -53,7 +57,8 @@ class TransitionBatchTest(unittest.TestCase):
53
57
  states=th.ones(2, 2),
54
58
  actions=th.ones(2, 1, dtype=th.long),
55
59
  rewards=th.full((2, 1), 2.0),
56
- dones=th.ones(2, 1, dtype=th.bool),
60
+ terminated=th.ones(2, 1, dtype=th.bool),
61
+ truncated=th.zeros(2, 1, dtype=th.bool),
57
62
  next_states=th.full((2, 2), 3.0),
58
63
  returns=th.full((2, 1), 4.0),
59
64
  )
@@ -63,6 +68,8 @@ class TransitionBatchTest(unittest.TestCase):
63
68
  self.assertEqual(combined.states.shape, (3, 2))
64
69
  self.assertEqual(combined.actions.shape, (3, 1))
65
70
  self.assertTrue(th.equal(combined.rewards.squeeze(-1), th.tensor([1.0, 2.0, 2.0])))
71
+ episode_ends = combined.terminated | combined.truncated
72
+ self.assertTrue(th.equal(episode_ends, th.ones(3, 1, dtype=th.bool)))
66
73
 
67
74
 
68
75
  class ReplayBufferTest(unittest.TestCase):
@@ -75,7 +82,8 @@ class ReplayBufferTest(unittest.TestCase):
75
82
  states=states,
76
83
  actions=np.asarray([0, 1, 0]),
77
84
  rewards=np.asarray([1.0, 2.0, 3.0]),
78
- dones=np.asarray([False, False, True]),
85
+ terminated=np.asarray([False, False, True]),
86
+ truncated=np.asarray([False, True, False]),
79
87
  next_states=next_states,
80
88
  returns=np.asarray([6.0, 5.0, 3.0]),
81
89
  )
@@ -87,7 +95,9 @@ class ReplayBufferTest(unittest.TestCase):
87
95
  self.assertIsInstance(all_data, TransitionBatch)
88
96
  self.assertEqual(all_data.states.shape, (3, 2))
89
97
  self.assertEqual(all_data.actions.dtype, th.int64)
90
- self.assertEqual(all_data.dones.dtype, th.bool)
98
+ self.assertEqual(all_data.terminated.dtype, th.bool)
99
+ self.assertEqual(all_data.truncated.dtype, th.bool)
100
+ self.assertTrue(all_data.truncated[1].item())
91
101
  self.assertIsInstance(sample, TransitionBatch)
92
102
  self.assertEqual(sample.states.shape, (2, 2))
93
103
 
@@ -98,7 +108,8 @@ class ReplayBufferTest(unittest.TestCase):
98
108
  states=np.asarray([[0], [1]], dtype=np.float32),
99
109
  actions=np.asarray([0, 1]),
100
110
  rewards=np.asarray([0.0, 1.0]),
101
- dones=np.asarray([False, False]),
111
+ terminated=np.asarray([False, False]),
112
+ truncated=np.asarray([False, False]),
102
113
  next_states=np.asarray([[1], [2]], dtype=np.float32),
103
114
  returns=np.asarray([1.0, 1.0]),
104
115
  )
@@ -106,7 +117,8 @@ class ReplayBufferTest(unittest.TestCase):
106
117
  states=np.asarray([[2], [3]], dtype=np.float32),
107
118
  actions=np.asarray([0, 1]),
108
119
  rewards=np.asarray([2.0, 3.0]),
109
- dones=np.asarray([False, True]),
120
+ terminated=np.asarray([False, True]),
121
+ truncated=np.asarray([False, False]),
110
122
  next_states=np.asarray([[3], [4]], dtype=np.float32),
111
123
  returns=np.asarray([5.0, 3.0]),
112
124
  )
@@ -48,6 +48,12 @@ class ThreeStepEnv(gym.Env):
48
48
  return obs, reward, terminated, truncated, {}
49
49
 
50
50
 
51
+ class TruncatingEnv(ThreeStepEnv):
52
+ def step(self, action):
53
+ obs, reward, _, _, info = super().step(action)
54
+ return obs, reward, False, self.t >= 3, info
55
+
56
+
51
57
  class RunnerTest(unittest.TestCase):
52
58
  def test_run_zero_collects_one_complete_episode_with_returns(self):
53
59
  env = ThreeStepEnv()
@@ -65,6 +71,23 @@ class RunnerTest(unittest.TestCase):
65
71
  self.assertEqual(ep_lengths, [3])
66
72
  self.assertTrue(th.equal(batch.returns.squeeze(-1), th.tensor([6.0, 4.0, 2.0])))
67
73
  self.assertTrue(th.equal(last_episode.returns.squeeze(-1), th.tensor([6.0, 4.0, 2.0])))
74
+ self.assertTrue(batch.terminated[-1].item())
75
+ self.assertFalse(batch.truncated.any().item())
76
+
77
+ def test_truncation_ends_episode_without_marking_transition_terminal(self):
78
+ env = TruncatingEnv()
79
+ model = FixedLogits([0.0, 1.0])
80
+ controller = GreedyController(model, num_actions=2)
81
+ runner = Runner(env, controller, calculate_returns=False)
82
+
83
+ batch, ep_returns, ep_lengths, last_episode = runner.run(0)
84
+
85
+ self.assertEqual(ep_returns, [6.0])
86
+ self.assertEqual(ep_lengths, [3])
87
+ self.assertIsNotNone(last_episode)
88
+ self.assertFalse(batch.terminated.any().item())
89
+ self.assertTrue(batch.truncated[-1].item())
90
+ self.assertTrue((batch.terminated | batch.truncated)[-1].item())
68
91
 
69
92
  def test_run_positive_steps_can_return_partial_batch(self):
70
93
  env = ThreeStepEnv()
File without changes
File without changes
File without changes
File without changes