evolutionary-policy-optimization 0.2.16__tar.gz → 0.2.19__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: evolutionary-policy-optimization
3
- Version: 0.2.16
3
+ Version: 0.2.19
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
@@ -43,6 +43,7 @@ Requires-Dist: ema-pytorch>=0.7.7
43
43
  Requires-Dist: hl-gauss-pytorch>=0.1.19
44
44
  Requires-Dist: torch>=2.2
45
45
  Requires-Dist: tqdm
46
+ Requires-Dist: x-mlps-pytorch>=0.3.0
46
47
  Provides-Extra: examples
47
48
  Requires-Dist: numpy; extra == 'examples'
48
49
  Requires-Dist: pufferlib>=2.0.6; extra == 'examples'
@@ -302,13 +303,13 @@ That's it
302
303
 
303
304
  ```bibtex
304
305
  @misc{xie2025simplepolicyoptimization,
305
- title = {Simple Policy Optimization},
306
+ title = {Simple Policy Optimization},
306
307
  author = {Zhengpeng Xie and Qiang Zhang and Fan Yang and Marco Hutter and Renjing Xu},
307
308
  year = {2025},
308
309
  eprint = {2401.16025},
309
310
  archivePrefix = {arXiv},
310
311
  primaryClass = {cs.LG},
311
- url = {https://arxiv.org/abs/2401.16025},
312
+ url = {https://arxiv.org/abs/2401.16025},
312
313
  }
313
314
  ```
314
315
 
@@ -322,6 +323,57 @@ That's it
322
323
  }
323
324
  ```
324
325
 
326
+ ```bibtex
327
+ @article{Zhu2025Hybrid,
328
+ author = {Zhu, Zimo and Yu, Chuanqiang and Wang, Junti},
329
+ title = {A Hybrid Genetic Algorithm and Proximal Policy Optimization System for Efficient Multi-Agent Task Allocation},
330
+ journal = {Systems},
331
+ volume = {13},
332
+ year = {2025},
333
+ number = {6},
334
+ article-number = {453},
335
+ url = {https://www.mdpi.com/2079-8954/13/6/453},
336
+ issn = {2079-8954}
337
+ ```
338
+
339
+ ```bibtex
340
+ @misc{kedia2026simtoolrealobjectcentricpolicyzeroshot,
341
+ title = {SimToolReal: An Object-Centric Policy for Zero-Shot Dexterous Tool Manipulation},
342
+ @misc{kedia2026simtoolrealobjectcentricpolicyzeroshot,
343
+ title = {SimToolReal: An Object-Centric Policy for Zero-Shot Dexterous Tool Manipulation},
344
+ author = {Kushal Kedia and Tyler Ga Wei Lum and Jeannette Bohg and C. Karen Liu},
345
+ year = {2026},
346
+ eprint = {2602.16863},
347
+ archivePrefix = {arXiv},
348
+ primaryClass = {cs.RO},
349
+ url = {https://arxiv.org/abs/2602.16863},
350
+ }
351
+ ```
352
+
353
+ ```bibtex
354
+ @article{Eysenbach2018DiversityIA,
355
+ title = {Diversity is All You Need: Learning Skills without a Reward Function},
356
+ author = {Benjamin Eysenbach and Abhishek Gupta and Julian Ibarz and Sergey Levine},
357
+ journal = {ArXiv},
358
+ year = {2018},
359
+ volume = {abs/1802.06070},
360
+ url = {https://arxiv.org/abs/1802.06070}
361
+ url = {https://arxiv.org/abs/2602.16863},
362
+ ```
363
+
364
+ ```bibtex
365
+ @article{Zhu2025Hybrid,
366
+ author = {Zhu, Zimo and Yu, Chuanqiang and Wang, Junti},
367
+ title = {A Hybrid Genetic Algorithm and Proximal Policy Optimization System for Efficient Multi-Agent Task Allocation},
368
+ journal = {Systems},
369
+ volume = {13},
370
+ year = {2025},
371
+ number = {6},
372
+ article-number = {453},
373
+ url = {https://www.mdpi.com/2079-8954/13/6/453},
374
+ issn = {2079-8954}
375
+ ```
376
+
325
377
  ```bibtex
326
378
  @misc{kedia2026simtoolrealobjectcentricpolicyzeroshot,
327
379
  title = {SimToolReal: An Object-Centric Policy for Zero-Shot Dexterous Tool Manipulation},
@@ -245,13 +245,13 @@ That's it
245
245
 
246
246
  ```bibtex
247
247
  @misc{xie2025simplepolicyoptimization,
248
- title = {Simple Policy Optimization},
248
+ title = {Simple Policy Optimization},
249
249
  author = {Zhengpeng Xie and Qiang Zhang and Fan Yang and Marco Hutter and Renjing Xu},
250
250
  year = {2025},
251
251
  eprint = {2401.16025},
252
252
  archivePrefix = {arXiv},
253
253
  primaryClass = {cs.LG},
254
- url = {https://arxiv.org/abs/2401.16025},
254
+ url = {https://arxiv.org/abs/2401.16025},
255
255
  }
256
256
  ```
257
257
 
@@ -265,6 +265,57 @@ That's it
265
265
  }
266
266
  ```
267
267
 
268
+ ```bibtex
269
+ @article{Zhu2025Hybrid,
270
+ author = {Zhu, Zimo and Yu, Chuanqiang and Wang, Junti},
271
+ title = {A Hybrid Genetic Algorithm and Proximal Policy Optimization System for Efficient Multi-Agent Task Allocation},
272
+ journal = {Systems},
273
+ volume = {13},
274
+ year = {2025},
275
+ number = {6},
276
+ article-number = {453},
277
+ url = {https://www.mdpi.com/2079-8954/13/6/453},
278
+ issn = {2079-8954}
279
+ ```
280
+
281
+ ```bibtex
282
+ @misc{kedia2026simtoolrealobjectcentricpolicyzeroshot,
283
+ title = {SimToolReal: An Object-Centric Policy for Zero-Shot Dexterous Tool Manipulation},
284
+ @misc{kedia2026simtoolrealobjectcentricpolicyzeroshot,
285
+ title = {SimToolReal: An Object-Centric Policy for Zero-Shot Dexterous Tool Manipulation},
286
+ author = {Kushal Kedia and Tyler Ga Wei Lum and Jeannette Bohg and C. Karen Liu},
287
+ year = {2026},
288
+ eprint = {2602.16863},
289
+ archivePrefix = {arXiv},
290
+ primaryClass = {cs.RO},
291
+ url = {https://arxiv.org/abs/2602.16863},
292
+ }
293
+ ```
294
+
295
+ ```bibtex
296
+ @article{Eysenbach2018DiversityIA,
297
+ title = {Diversity is All You Need: Learning Skills without a Reward Function},
298
+ author = {Benjamin Eysenbach and Abhishek Gupta and Julian Ibarz and Sergey Levine},
299
+ journal = {ArXiv},
300
+ year = {2018},
301
+ volume = {abs/1802.06070},
302
+ url = {https://arxiv.org/abs/1802.06070}
303
+ url = {https://arxiv.org/abs/2602.16863},
304
+ ```
305
+
306
+ ```bibtex
307
+ @article{Zhu2025Hybrid,
308
+ author = {Zhu, Zimo and Yu, Chuanqiang and Wang, Junti},
309
+ title = {A Hybrid Genetic Algorithm and Proximal Policy Optimization System for Efficient Multi-Agent Task Allocation},
310
+ journal = {Systems},
311
+ volume = {13},
312
+ year = {2025},
313
+ number = {6},
314
+ article-number = {453},
315
+ url = {https://www.mdpi.com/2079-8954/13/6/453},
316
+ issn = {2079-8954}
317
+ ```
318
+
268
319
  ```bibtex
269
320
  @misc{kedia2026simtoolrealobjectcentricpolicyzeroshot,
270
321
  title = {SimToolReal: An Object-Centric Policy for Zero-Shot Dexterous Tool Manipulation},
@@ -22,6 +22,8 @@ import einx
22
22
  from einops import rearrange, repeat, reduce, einsum, pack
23
23
  from einops.layers.torch import Rearrange
24
24
 
25
+ from x_mlps_pytorch import AttnResidualNormedMLP
26
+
25
27
  from evolutionary_policy_optimization.distributed import (
26
28
  is_distributed,
27
29
  get_world_and_rank,
@@ -532,6 +534,38 @@ class MLP(Module):
532
534
 
533
535
  return self.final_lime(x, prev_layer_inputs)
534
536
 
537
+ # discriminator for predicting latent code from states and actions (DIAYN - Eysenbach et al. 2018)
538
+
539
+ class DiversityDiscr(Module):
540
+ def __init__(
541
+ self,
542
+ dim_state,
543
+ num_actions,
544
+ num_latents,
545
+ dim = 64,
546
+ depth = 2
547
+ ):
548
+ super().__init__()
549
+ self.action_embed = nn.Embedding(num_actions, dim)
550
+ self.state_proj = nn.Linear(dim_state, dim)
551
+
552
+ self.net = AttnResidualNormedMLP(
553
+ dim = dim,
554
+ depth = depth,
555
+ dim_in = dim * 2,
556
+ dim_out = num_latents
557
+ )
558
+
559
+ def reset_parameters(self):
560
+ for module in self.modules():
561
+ if isinstance(module, (nn.Linear, nn.LayerNorm, nn.RMSNorm, nn.Embedding)):
562
+ module.reset_parameters()
563
+
564
+ def forward(self, state, action):
565
+ state_embed = self.state_proj(state)
566
+ action_embed = self.action_embed(action)
567
+ return self.net((state_embed, action_embed))
568
+
535
569
  # actor, critic, and agent (actor + critic)
536
570
  # eventually, should just create a separate repo and aggregate all the MLP related architectures
537
571
 
@@ -862,7 +896,11 @@ class LatentGenePool(Module):
862
896
 
863
897
  if not divisible_by(self.step.item(), self.apply_genetic_algorithm_every):
864
898
  self.advance_step_()
865
- return
899
+
900
+ if inplace:
901
+ return False, None
902
+
903
+ return False, self.latents
866
904
 
867
905
  """
868
906
  i - islands
@@ -894,9 +932,9 @@ class LatentGenePool(Module):
894
932
 
895
933
  if not should_update_per_island.any():
896
934
  if inplace:
897
- return
935
+ return False, None
898
936
 
899
- return genes
937
+ return False, genes
900
938
 
901
939
  genes = rearrange(genes, '(i p) ... -> i p ...', i = islands)
902
940
 
@@ -982,8 +1020,10 @@ class LatentGenePool(Module):
982
1020
 
983
1021
  genes = rearrange(genes, 'i p ... -> (i p) ...')
984
1022
 
1023
+ ret = (True, genes)
1024
+
985
1025
  if not inplace:
986
- return genes
1026
+ return ret
987
1027
 
988
1028
  # store the genes for the next interaction with environment for new fitness values (a function of reward and other to be researched measures)
989
1029
 
@@ -991,6 +1031,9 @@ class LatentGenePool(Module):
991
1031
 
992
1032
  self.advance_step_()
993
1033
 
1034
+ if inplace:
1035
+ return ret
1036
+
994
1037
  def forward(
995
1038
  self,
996
1039
  latent_id: int | None = None,
@@ -1071,6 +1114,10 @@ class Agent(Module):
1071
1114
  actor_optim_kwargs: dict = dict(),
1072
1115
  critic_optim_kwargs: dict = dict(),
1073
1116
  latent_optim_kwargs: dict = dict(),
1117
+ use_diversity_discr = False,
1118
+ diversity_discr_kwargs: dict = dict(dim = 64, depth = 2),
1119
+ diversity_discr_lr = 3e-4,
1120
+ diversity_discr_optim_kwargs: dict = dict(),
1074
1121
  get_fitness_scores: Callable[..., Tensor] = get_fitness_scores,
1075
1122
  wrap_with_accelerate: bool = True,
1076
1123
  accelerate_kwargs: dict = dict(),
@@ -1147,6 +1194,30 @@ class Agent(Module):
1147
1194
 
1148
1195
  self.latent_optim = optim_klass(latent_gene_pool.parameters(), lr = latent_lr, **latent_optim_kwargs) if exists(latent_gene_pool) and not latent_gene_pool.frozen_latents else None
1149
1196
 
1197
+ # diversity discriminator
1198
+
1199
+ self.use_diversity_discr = use_diversity_discr
1200
+
1201
+ if use_diversity_discr:
1202
+ assert exists(latent_gene_pool), 'latent_gene_pool must be present to use DIAYN'
1203
+ dim_state = actor.init_layer[0].in_features
1204
+ num_actions = actor.to_out[1].out_features
1205
+
1206
+ self.diversity_discr = DiversityDiscr(
1207
+ dim_state = dim_state,
1208
+ num_actions = num_actions,
1209
+ num_latents = latent_gene_pool.num_latents,
1210
+ **diversity_discr_kwargs
1211
+ )
1212
+
1213
+ self.diversity_discr_optim = optim_klass(self.diversity_discr.parameters(), lr = diversity_discr_lr, **diversity_discr_optim_kwargs)
1214
+ else:
1215
+ self.diversity_discr = None
1216
+ self.diversity_discr_optim = None
1217
+
1218
+ self.register_buffer('has_diversity_discr_warmed_up', tensor(False))
1219
+ self.register_buffer('zero', tensor(0.))
1220
+
1150
1221
  # shrink and perturb every
1151
1222
 
1152
1223
  self.should_noise_weights = exists(shrink_and_perturb_every)
@@ -1174,18 +1245,22 @@ class Agent(Module):
1174
1245
  self.actor,
1175
1246
  self.critic,
1176
1247
  self.latent_gene_pool,
1248
+ self.diversity_discr,
1177
1249
  self.actor_optim,
1178
1250
  self.critic_optim,
1179
1251
  self.latent_optim,
1252
+ self.diversity_discr_optim,
1180
1253
  ) = tuple(
1181
1254
  maybe(self.accelerate.prepare)(m) for m in (
1182
1255
  self.state_norm,
1183
1256
  self.actor,
1184
1257
  self.critic,
1185
1258
  self.latent_gene_pool,
1259
+ self.diversity_discr,
1186
1260
  self.actor_optim,
1187
1261
  self.critic_optim,
1188
1262
  self.latent_optim,
1263
+ self.diversity_discr_optim,
1189
1264
  )
1190
1265
  )
1191
1266
 
@@ -1215,24 +1290,28 @@ class Agent(Module):
1215
1290
  def save(self, path, overwrite = False):
1216
1291
  path = Path(path)
1217
1292
  unwrap = self.unwrap_model
1293
+ unwrap_optim = lambda opt: opt.optimizer if hasattr(opt, 'optimizer') else opt
1218
1294
 
1219
1295
  assert not path.exists() or overwrite
1220
1296
 
1221
1297
  pkg = dict(
1222
- state_norm = unwrap(self.state_norm).state_dict() if self.state_norm else None,
1223
1298
  actor = unwrap(self.actor).state_dict(),
1224
1299
  critic = unwrap(self.critic).state_dict(),
1225
1300
  critic_ema = self.critic_ema.state_dict() if self.use_critic_ema else None,
1226
1301
  latents = unwrap(self.latent_gene_pool).state_dict() if self.has_latent_genes else None,
1227
- actor_optim = unwrap(self.actor_optim).state_dict(),
1228
- critic_optim = unwrap(self.critic_optim).state_dict(),
1229
- latent_optim = unwrap(self.latent_optim).state_dict() if exists(self.latent_optim) else None
1302
+ diversity_discr = unwrap(self.diversity_discr).state_dict() if self.use_diversity_discr else None,
1303
+ has_diversity_discr_warmed_up = self.has_diversity_discr_warmed_up.item(),
1304
+ actor_optim = unwrap_optim(self.actor_optim).state_dict(),
1305
+ critic_optim = unwrap_optim(self.critic_optim).state_dict(),
1306
+ latent_optim = unwrap_optim(self.latent_optim).state_dict() if exists(self.latent_optim) else None,
1307
+ diversity_discr_optim = unwrap_optim(self.diversity_discr_optim).state_dict() if self.use_diversity_discr else None,
1230
1308
  )
1231
1309
 
1232
1310
  torch.save(pkg, str(path))
1233
1311
 
1234
1312
  def load(self, path):
1235
1313
  unwrap = self.unwrap_model
1314
+ unwrap_optim = lambda opt: opt.optimizer if hasattr(opt, 'optimizer') else opt
1236
1315
  path = Path(path)
1237
1316
 
1238
1317
  assert path.exists()
@@ -1240,20 +1319,28 @@ class Agent(Module):
1240
1319
  pkg = torch.load(str(path), weights_only = True)
1241
1320
 
1242
1321
  unwrap(self.actor).load_state_dict(pkg['actor'])
1243
-
1244
1322
  unwrap(self.critic).load_state_dict(pkg['critic'])
1245
1323
 
1246
1324
  if self.use_critic_ema:
1247
1325
  self.critic_ema.load_state_dict(pkg['critic_ema'])
1248
1326
 
1249
- if exists(pkg.get('latents', None)):
1327
+ if 'latents' in pkg and exists(pkg['latents']):
1250
1328
  self.latent_gene_pool.load_state_dict(pkg['latents'])
1251
1329
 
1252
- unwrap(self.actor_optim).load_state_dict(pkg['actor_optim'])
1253
- unwrap(self.critic_optim).load_state_dict(pkg['critic_optim'])
1330
+ if self.use_diversity_discr and 'diversity_discr' in pkg and exists(pkg['diversity_discr']):
1331
+ unwrap(self.diversity_discr).load_state_dict(pkg['diversity_discr'])
1332
+
1333
+ if 'has_diversity_discr_warmed_up' in pkg:
1334
+ self.has_diversity_discr_warmed_up.copy_(tensor(pkg['has_diversity_discr_warmed_up']))
1335
+
1336
+ unwrap_optim(self.actor_optim).load_state_dict(pkg['actor_optim'])
1337
+ unwrap_optim(self.critic_optim).load_state_dict(pkg['critic_optim'])
1338
+
1339
+ if 'latent_optim' in pkg and exists(pkg['latent_optim']):
1340
+ unwrap_optim(self.latent_optim).load_state_dict(pkg['latent_optim'])
1254
1341
 
1255
- if exists(pkg.get('latent_optim', None)):
1256
- unwrap(self.latent_optim).load_state_dict(pkg['latent_optim'])
1342
+ if self.use_diversity_discr and 'diversity_discr_optim' in pkg and exists(pkg['diversity_discr_optim']):
1343
+ unwrap_optim(self.diversity_discr_optim).load_state_dict(pkg['diversity_discr_optim'])
1257
1344
 
1258
1345
  @move_input_tensors_to_device
1259
1346
  def get_actor_actions(
@@ -1430,11 +1517,28 @@ class Agent(Module):
1430
1517
  self.critic_optim.step()
1431
1518
  self.critic_optim.zero_grad()
1432
1519
 
1520
+ # learn diversity discriminator
1521
+
1522
+ diversity_discr_loss = self.zero
1523
+
1524
+ if self.use_diversity_discr:
1525
+ diversity_discr_logits = self.diversity_discr(states, actions)
1526
+ diversity_discr_loss = F.cross_entropy(diversity_discr_logits, latent_gene_ids)
1527
+
1528
+ diversity_discr_loss.backward()
1529
+
1530
+ if exists(self.has_grad_clip):
1531
+ self.clip_grad_norm_(self.diversity_discr.parameters(), self.max_grad_norm)
1532
+
1533
+ self.diversity_discr_optim.step()
1534
+ self.diversity_discr_optim.zero_grad()
1535
+
1433
1536
  # log actor critic loss
1434
1537
 
1435
1538
  self.log(
1436
1539
  actor_loss = actor_loss.item(),
1437
1540
  critic_loss = critic_loss.item(),
1541
+ diversity_discr_loss = diversity_discr_loss.item(),
1438
1542
  fitness_scores = fitness_scores
1439
1543
  )
1440
1544
 
@@ -1475,10 +1579,27 @@ class Agent(Module):
1475
1579
  for _, states, *_ in tqdm(dataloader, desc = 'state norm learning'):
1476
1580
  self.state_norm(states)
1477
1581
 
1582
+ # update warmed up state for discriminator
1583
+
1584
+ if self.use_diversity_discr:
1585
+ self.has_diversity_discr_warmed_up.copy_(tensor(True))
1586
+
1478
1587
  # apply evolution
1479
1588
 
1480
1589
  if self.has_latent_genes:
1481
- self.latent_gene_pool.genetic_algorithm_step(fitness_scores)
1590
+ should_update, _ = self.latent_gene_pool.genetic_algorithm_step(fitness_scores)
1591
+
1592
+ if self.use_diversity_discr and should_update:
1593
+ self.has_diversity_discr_warmed_up.copy_(tensor(False))
1594
+
1595
+ # reset discriminator
1596
+
1597
+ unwrap = self.unwrap_model
1598
+ unwrap(self.diversity_discr).reset_parameters()
1599
+
1600
+ # reset optimizer
1601
+
1602
+ self.diversity_discr_optim.state.clear()
1482
1603
 
1483
1604
  # maybe shrink and perturb
1484
1605
 
@@ -1623,11 +1744,13 @@ class EPO(Module):
1623
1744
  episodes_per_latent,
1624
1745
  max_episode_length,
1625
1746
  action_sample_temperature = 1.,
1626
- fix_environ_across_latents = True
1747
+ fix_environ_across_latents = True,
1748
+ diversity_reward_weight = 1.,
1627
1749
  ):
1628
1750
  super().__init__()
1629
1751
  self.agent = agent
1630
1752
  self.action_sample_temperature = action_sample_temperature
1753
+ self.diversity_reward_weight = diversity_reward_weight
1631
1754
 
1632
1755
  self.num_latents = agent.latent_gene_pool.num_latents if agent.has_latent_genes else 1
1633
1756
  self.episodes_per_latent = episodes_per_latent
@@ -1635,6 +1758,7 @@ class EPO(Module):
1635
1758
  self.fix_environ_across_latents = fix_environ_across_latents
1636
1759
 
1637
1760
  self.register_buffer('dummy', tensor(0, device = agent.device))
1761
+ self.register_buffer('zero', tensor(0., device = agent.device))
1638
1762
 
1639
1763
  @property
1640
1764
  def device(self):
@@ -1741,6 +1865,20 @@ class EPO(Module):
1741
1865
 
1742
1866
  state, reward, truncated, terminated, _ = interface_torch_numpy(env.step, device = self.device)(action)
1743
1867
 
1868
+ # diversity reward
1869
+
1870
+ if self.agent.use_diversity_discr and self.agent.has_diversity_discr_warmed_up.item():
1871
+ with torch.no_grad():
1872
+ diversity_discr = self.agent.unwrap_model(self.agent.diversity_discr)
1873
+ diversity_discr.eval()
1874
+
1875
+ logits = diversity_discr(rearrange(state, '... -> 1 ...'), rearrange(action, '... -> 1 ...'))
1876
+ log_probs = logits.log_softmax(dim=-1)
1877
+
1878
+ diversity_reward = log_probs[0, latent_id] + log(tensor(self.agent.num_latents))
1879
+
1880
+ reward = reward + diversity_reward * self.diversity_reward_weight
1881
+
1744
1882
  done = truncated or terminated
1745
1883
 
1746
1884
  # update cumulative rewards per latent, to be used as default fitness score
@@ -7,6 +7,7 @@ import torch.nn.functional as F
7
7
  from torch.func import vmap, functional_call
8
8
  from torch.nn import Module, ParameterList
9
9
 
10
+ import einx
10
11
  from einops import rearrange, reduce, repeat
11
12
 
12
13
  def exists(v):
@@ -31,11 +32,12 @@ def shrink_and_perturb_(
31
32
 
32
33
  def crossover_weights(
33
34
  w1, w2,
35
+ alpha = 0.5,
34
36
  shrink_perturb = False,
35
37
  shrink_factor = 0.4,
36
38
  perturb_factor = 0.1
37
39
  ):
38
- assert w2.shape == w2.shape
40
+ assert w1.shape == w2.shape
39
41
 
40
42
  no_batch = w1.ndim == 2
41
43
 
@@ -58,12 +60,17 @@ def crossover_weights(
58
60
  u1, s1, v1 = torch.svd(w1)
59
61
  u2, s2, v2 = torch.svd(w2)
60
62
 
63
+ sign = einx.dot('b j d, b j d -> b d', v1, v2).sign()
64
+
65
+ u2 = einx.multiply('b i d, b d -> b i d', u2, sign)
66
+ v2 = einx.multiply('b j d, b d -> b j d', v2, sign)
67
+
61
68
  batch_randperm = torch.randn((batch, rank), device = w1.device).argsort(dim = -1)
62
- mask = batch_randperm < (rank // 2)
69
+ mask = batch_randperm < int(rank * alpha)
63
70
 
64
- u = torch.where(mask[:, None, :], u1, u2)
65
- s = torch.where(mask, s1, s2)
66
- v = torch.where(mask[:, :, None], v1, v2)
71
+ u = einx.where('b d, b i d, b i d -> b i d', mask, u1, u2)
72
+ s = einx.where('b d, b d, b d -> b d', mask, s1, s2)
73
+ v = einx.where('b d, b j d, b j d -> b j d', mask, v1, v2)
67
74
 
68
75
  out = u @ torch.diag_embed(s) @ v.mT
69
76
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "evolutionary-policy-optimization"
3
- version = "0.2.16"
3
+ version = "0.2.19"
4
4
  description = "EPO - Pytorch"
5
5
  authors = [
6
6
  { name = "Phil Wang", email = "lucidrains@gmail.com" }
@@ -32,7 +32,8 @@ dependencies = [
32
32
  'ema-pytorch>=0.7.7',
33
33
  'hl-gauss-pytorch>=0.1.19',
34
34
  'torch>=2.2',
35
- 'tqdm'
35
+ 'tqdm',
36
+ "x-mlps-pytorch>=0.3.0",
36
37
  ]
37
38
 
38
39
  [project.urls]