evolutionary-policy-optimization 0.3.5__tar.gz → 0.4.1__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.
@@ -174,3 +174,15 @@ cython_debug/
174
174
 
175
175
  # PyPI configuration file
176
176
  .pypirc
177
+
178
+ # microduck runs / artifacts
179
+ checkpoints*/
180
+ experiments/
181
+ videos*/
182
+ recordings/
183
+ wandb/
184
+ scratch/
185
+ data/
186
+ *.pt
187
+ *.mp4
188
+ *.log
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: evolutionary-policy-optimization
3
- Version: 0.3.5
3
+ Version: 0.4.1
4
4
  Summary: EPO - Pytorch
5
5
  Project-URL: Homepage, https://pypi.org/project/evolutionary-policy-optimization/
6
6
  Project-URL: Repository, https://github.com/lucidrains/evolutionary-policy-optimization
@@ -40,7 +40,7 @@ Requires-Dist: einops>=0.8.1
40
40
  Requires-Dist: einx>=0.3.0
41
41
  Requires-Dist: ema-pytorch>=0.7.7
42
42
  Requires-Dist: hl-gauss-pytorch>=0.2.8
43
- Requires-Dist: mean-conc-beta>=0.0.7
43
+ Requires-Dist: mean-conc-beta>=0.2.7
44
44
  Requires-Dist: torch>=2.2
45
45
  Requires-Dist: tqdm
46
46
  Requires-Dist: x-mlps-pytorch>=0.3.0
@@ -367,6 +367,16 @@ That's it
367
367
  }
368
368
  ```
369
369
 
370
+ ```bibtex
371
+ @inproceedings{Sharma2020DynamicsAwareUD,
372
+ title = {Dynamics-Aware Unsupervised Discovery of Skills},
373
+ author = {Archit Sharma and Shixiang Gu and Sergey Levine and Vikash Kumar and Karol Hausman},
374
+ booktitle = {International Conference on Learning Representations},
375
+ year = {2020},
376
+ url = {https://arxiv.org/abs/1907.01657}
377
+ }
378
+ ```
379
+
370
380
  ```bibtex
371
381
  @article{Zhu2025Hybrid,
372
382
  author = {Zhu, Zimo and Yu, Chuanqiang and Wang, Junti},
@@ -305,6 +305,16 @@ That's it
305
305
  }
306
306
  ```
307
307
 
308
+ ```bibtex
309
+ @inproceedings{Sharma2020DynamicsAwareUD,
310
+ title = {Dynamics-Aware Unsupervised Discovery of Skills},
311
+ author = {Archit Sharma and Shixiang Gu and Sergey Levine and Vikash Kumar and Karol Hausman},
312
+ booktitle = {International Conference on Learning Representations},
313
+ year = {2020},
314
+ url = {https://arxiv.org/abs/1907.01657}
315
+ }
316
+ ```
317
+
308
318
  ```bibtex
309
319
  @article{Zhu2025Hybrid,
310
320
  author = {Zhu, Zimo and Yu, Chuanqiang and Wang, Junti},
@@ -1,3 +1,3 @@
1
1
  from evolutionary_policy_optimization.env_wrappers import GymnasiumEnvWrapper, rescale_from_to
2
- from evolutionary_policy_optimization.epo import EPO, MLP, Actor, Agent, Beta, CategoricalActionDistr, Critic, LatentGenePool, create_agent
2
+ from evolutionary_policy_optimization.epo import EPO, MLP, Actor, Agent, Beta, CategoricalActionDistr, Critic, DiversityDiscr, LatentGenePool, create_agent
3
3
  from evolutionary_policy_optimization.mock_env import Env, VectorEnv
@@ -52,6 +52,20 @@ def divisible_by(num, den):
52
52
  def to_device(inp, device):
53
53
  return tree_map(lambda t: t.to(device) if is_tensor(t) else t, inp)
54
54
 
55
+ class ShuffledBatches:
56
+ # fresh permutation on every pass
57
+
58
+ def __init__(self, fields, batch_size):
59
+ self.fields = fields
60
+ self.batch_size = batch_size
61
+
62
+ def __iter__(self):
63
+ perm = torch.randperm(self.fields[0].shape[0])
64
+
65
+ for i in range(0, len(perm), self.batch_size):
66
+ b = perm[i:i + self.batch_size]
67
+ yield [field[b] for field in self.fields]
68
+
55
69
  def maybe(fn):
56
70
 
57
71
  @wraps(fn)
@@ -513,7 +527,7 @@ class MLP(Module):
513
527
 
514
528
  return self.final_lime(x, prev_layer_inputs)
515
529
 
516
- # discriminator for predicting latent code from states and actions (DIAYN - Eysenbach et al. 2018)
530
+ # discriminator for predicting latent code from states or state transitions (DIAYN / DADS)
517
531
 
518
532
  class DiversityDiscr(Module):
519
533
  def __init__(
@@ -521,15 +535,19 @@ class DiversityDiscr(Module):
521
535
  dim_state,
522
536
  num_latents,
523
537
  dim = 64,
524
- depth = 2
538
+ depth = 2,
539
+ time_gap = 1
525
540
  ):
526
541
  super().__init__()
542
+ self.time_gap = time_gap
543
+ self.state_transition = time_gap > 0
544
+
527
545
  self.state_proj = nn.Linear(dim_state, dim)
528
546
 
529
547
  self.net = AttnResidualNormedMLP(
530
548
  dim = dim,
531
549
  depth = depth,
532
- dim_in = dim * 2,
550
+ dim_in = dim * 2 if self.state_transition else dim,
533
551
  dim_out = num_latents
534
552
  )
535
553
 
@@ -538,8 +556,12 @@ class DiversityDiscr(Module):
538
556
  if isinstance(module, (nn.Linear, nn.LayerNorm, nn.RMSNorm)):
539
557
  module.reset_parameters()
540
558
 
541
- def forward(self, state, next_state):
559
+ def forward(self, state, next_state = None):
542
560
  state_embed = self.state_proj(state)
561
+
562
+ if not self.state_transition or not exists(next_state):
563
+ return self.net(state_embed)
564
+
543
565
  next_state_embed = self.state_proj(next_state)
544
566
  return self.net((state_embed, next_state_embed))
545
567
 
@@ -1215,6 +1237,7 @@ class Agent(Module):
1215
1237
  critic_optim_kwargs: dict = dict(),
1216
1238
  latent_optim_kwargs: dict = dict(),
1217
1239
  use_diversity_discr = False,
1240
+ diversity_time_gap = 1, # 0 for DIAYN (state only), >= 1 for DADS (state transition with given time gap)
1218
1241
  diversity_discr_kwargs: dict = dict(dim = 64, depth = 2),
1219
1242
  diversity_discr_lr = 3e-4,
1220
1243
  diversity_discr_optim_kwargs: dict = dict(),
@@ -1225,6 +1248,7 @@ class Agent(Module):
1225
1248
  hidden_spr_ema_update = 0.99,
1226
1249
  get_fitness_scores: Callable[..., Tensor] = get_fitness_scores,
1227
1250
  wrap_with_accelerate: bool = True,
1251
+ use_dataloader = False,
1228
1252
  accelerate_kwargs: dict = dict(),
1229
1253
  accelerator = None,
1230
1254
  quiet: bool = False,
@@ -1236,6 +1260,7 @@ class Agent(Module):
1236
1260
  # hf accelerate
1237
1261
 
1238
1262
  self.wrap_with_accelerate = wrap_with_accelerate
1263
+ self.use_dataloader = use_dataloader
1239
1264
 
1240
1265
  if wrap_with_accelerate:
1241
1266
  self.accelerate = accelerator if exists(accelerator) else Accelerator(**accelerate_kwargs)
@@ -1305,14 +1330,16 @@ class Agent(Module):
1305
1330
  # diversity discriminator
1306
1331
 
1307
1332
  self.use_diversity_discr = use_diversity_discr
1333
+ self.diversity_time_gap = diversity_time_gap
1308
1334
 
1309
1335
  if use_diversity_discr:
1310
- assert exists(latent_gene_pool), 'latent_gene_pool must be present to use DIAYN'
1336
+ assert exists(latent_gene_pool), 'latent_gene_pool must be present to use DIAYN / DADS'
1311
1337
  dim_state = actor.init_layer[0].in_features
1312
1338
 
1313
1339
  self.diversity_discr = DiversityDiscr(
1314
1340
  dim_state = dim_state,
1315
1341
  num_latents = latent_gene_pool.num_latents,
1342
+ time_gap = diversity_time_gap,
1316
1343
  **diversity_discr_kwargs
1317
1344
  )
1318
1345
 
@@ -1443,6 +1470,7 @@ class Agent(Module):
1443
1470
  critic_ema = self.critic_ema.state_dict() if self.use_critic_ema else None,
1444
1471
  latents = unwrap(self.latent_gene_pool).state_dict() if self.has_latent_genes else None,
1445
1472
  diversity_discr = unwrap(self.diversity_discr).state_dict() if self.use_diversity_discr else None,
1473
+ diversity_time_gap = self.diversity_time_gap,
1446
1474
  has_diversity_discr_warmed_up = self.has_diversity_discr_warmed_up.item(),
1447
1475
  actor_optim = unwrap_optim(self.actor_optim).state_dict(),
1448
1476
  critic_optim = unwrap_optim(self.critic_optim).state_dict(),
@@ -1475,6 +1503,9 @@ class Agent(Module):
1475
1503
  if self.use_diversity_discr and 'diversity_discr' in pkg and exists(pkg['diversity_discr']):
1476
1504
  unwrap(self.diversity_discr).load_state_dict(pkg['diversity_discr'])
1477
1505
 
1506
+ if 'diversity_time_gap' in pkg:
1507
+ self.diversity_time_gap = pkg['diversity_time_gap']
1508
+
1478
1509
  if 'has_diversity_discr_warmed_up' in pkg:
1479
1510
  self.has_diversity_discr_warmed_up.copy_(tensor(pkg['has_diversity_discr_warmed_up']))
1480
1511
 
@@ -1577,13 +1608,12 @@ class Agent(Module):
1577
1608
  epochs = 2
1578
1609
 
1579
1610
  ):
1580
- memories_and_cumulative_rewards = to_device(memories_and_cumulative_rewards, self.device)
1581
-
1582
1611
  memories_list, rewards_per_latent_episode = memories_and_cumulative_rewards
1583
1612
 
1584
- # stack memories
1613
+ # stack, then move once per field
1585
1614
 
1586
- memories = map(stack, zip(*memories_list))
1615
+ memories = [t.to(self.device) for t in map(stack, zip(*memories_list))]
1616
+ rewards_per_latent_episode = rewards_per_latent_episode.to(self.device)
1587
1617
 
1588
1618
  memories_list.clear()
1589
1619
 
@@ -1625,12 +1655,15 @@ class Agent(Module):
1625
1655
 
1626
1656
  valid_episode = episode_ids >= 0
1627
1657
 
1628
- dataset = TensorDataset(*[t[valid_episode] for t in (advantages, states, next_states, latent_gene_ids, actions, log_probs, values, dones)])
1658
+ fields = tuple(t[valid_episode] for t in (advantages, states, next_states, latent_gene_ids, actions, log_probs, values, dones))
1629
1659
 
1630
- dataloader = DataLoader(dataset, batch_size = self.batch_size, shuffle = True)
1660
+ if self.wrap_with_accelerate or self.use_dataloader:
1661
+ dataloader = DataLoader(TensorDataset(*fields), batch_size = self.batch_size, shuffle = True)
1631
1662
 
1632
- if self.wrap_with_accelerate:
1633
- dataloader = self.accelerate.prepare(dataloader)
1663
+ if self.wrap_with_accelerate:
1664
+ dataloader = self.accelerate.prepare(dataloader)
1665
+ else:
1666
+ dataloader = ShuffledBatches(fields, self.batch_size)
1634
1667
 
1635
1668
  # updating actor and critic
1636
1669
 
@@ -1722,7 +1755,8 @@ class Agent(Module):
1722
1755
  diversity_discr_loss = self.zero
1723
1756
 
1724
1757
  if self.use_diversity_discr:
1725
- diversity_discr_logits = self.diversity_discr(states, next_states)
1758
+ discr_inp = (states,) if self.diversity_time_gap == 0 else (states, next_states)
1759
+ diversity_discr_logits = self.diversity_discr(*discr_inp)
1726
1760
  diversity_discr_loss = F.cross_entropy(diversity_discr_logits, latent_gene_ids)
1727
1761
 
1728
1762
  diversity_discr_loss.backward()
@@ -1878,6 +1912,7 @@ def create_agent(
1878
1912
  use_critic_ema = True,
1879
1913
  use_state_norm = False,
1880
1914
  action_is_continuous = False, # continuous control - beta policy
1915
+ diversity_time_gap = 1,
1881
1916
  latent_gene_pool_kwargs: dict = dict(),
1882
1917
  actor_kwargs: dict = dict(),
1883
1918
  critic_kwargs: dict = dict(),
@@ -1923,6 +1958,7 @@ def create_agent(
1923
1958
  state_norm = state_norm,
1924
1959
  latent_gene_pool = latent_gene_pool,
1925
1960
  use_critic_ema = use_critic_ema,
1961
+ diversity_time_gap = diversity_time_gap,
1926
1962
  **kwargs
1927
1963
  )
1928
1964
 
@@ -2142,6 +2178,7 @@ class EPO(Module):
2142
2178
 
2143
2179
  next_obs, rewards, terminated, truncated = env.step_batch(actions.cpu().numpy(), worker_ids = active)[:4]
2144
2180
 
2181
+ next_states = from_numpy(np.ascontiguousarray(next_obs)).float().to(self.device)
2145
2182
  rewards = from_numpy(np.asarray(rewards)).float().to(self.device)
2146
2183
  terminated = from_numpy(np.asarray(terminated)).to(self.device)
2147
2184
  truncated = from_numpy(np.asarray(truncated)).to(self.device)
@@ -2151,7 +2188,7 @@ class EPO(Module):
2151
2188
 
2152
2189
  latent_id, episode_id, latent, state = slot.latent_id, slot.episode_id, slot.latent, slot.state
2153
2190
 
2154
- next_state = from_numpy(np.array(next_obs[k])).float().to(self.device)
2191
+ next_state = next_states[k]
2155
2192
 
2156
2193
  reward, terminated_at, truncated_at = rewards[k], terminated[k], truncated[k]
2157
2194
 
@@ -2159,7 +2196,16 @@ class EPO(Module):
2159
2196
 
2160
2197
  if self.agent.use_diversity_discr and self.agent.has_diversity_discr_warmed_up.item():
2161
2198
  with torch.no_grad():
2162
- logits = self.agent.unwrap_model(self.agent.diversity_discr)(rearrange(state, '... -> 1 ...'), rearrange(next_state, '... -> 1 ...'))
2199
+ discr = self.agent.unwrap_model(self.agent.diversity_discr)
2200
+ time_gap = self.agent.diversity_time_gap
2201
+
2202
+ if time_gap == 0:
2203
+ logits = discr(rearrange(state, '... -> 1 ...'))
2204
+ elif time_gap == 1 or len(slot.memories) < (time_gap - 1):
2205
+ logits = discr(rearrange(state, '... -> 1 ...'), rearrange(next_state, '... -> 1 ...'))
2206
+ else:
2207
+ past_state = slot.memories[-(time_gap - 1)].state
2208
+ logits = discr(rearrange(past_state, '... -> 1 ...'), rearrange(next_state, '... -> 1 ...'))
2163
2209
 
2164
2210
  latent_log_probs = logits.log_softmax(dim = -1)
2165
2211
  diversity_reward = latent_log_probs[0, latent_id] + math.log(self.agent.num_latents)
@@ -2184,6 +2230,11 @@ class EPO(Module):
2184
2230
 
2185
2231
  slot.memories.append(memory)
2186
2232
 
2233
+ if self.agent.use_diversity_discr and self.agent.diversity_time_gap > 1:
2234
+ time_gap = self.agent.diversity_time_gap
2235
+ if len(slot.memories) >= time_gap:
2236
+ slot.memories[-time_gap] = slot.memories[-time_gap]._replace(next_state = next_state)
2237
+
2187
2238
  rewards_per_latent_episode[latent_id, episode_id] += reward
2188
2239
 
2189
2240
  # episode is done - bootstrap value if truncated, then refill
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "evolutionary-policy-optimization"
3
- version = "0.3.5"
3
+ version = "0.4.1"
4
4
  description = "EPO - Pytorch"
5
5
  authors = [
6
6
  { name = "Phil Wang", email = "lucidrains@gmail.com" }
@@ -30,7 +30,7 @@ dependencies = [
30
30
  'einops>=0.8.1',
31
31
  'ema-pytorch>=0.7.7',
32
32
  'hl-gauss-pytorch>=0.2.8',
33
- 'mean-conc-beta>=0.0.7',
33
+ 'mean-conc-beta>=0.2.7',
34
34
  'torch>=2.2',
35
35
  'tqdm',
36
36
  "x-mlps-pytorch>=0.3.0",