vector-quantize-pytorch 1.23.4__tar.gz → 1.24.0__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 (32) hide show
  1. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/PKG-INFO +13 -1
  2. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/README.md +12 -0
  3. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/examples/autoencoder.py +3 -2
  4. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/pyproject.toml +1 -1
  5. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/tests/test_readme.py +22 -0
  6. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/vector_quantize_pytorch/vector_quantize_pytorch.py +43 -8
  7. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/.github/workflows/build.yml +0 -0
  8. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/.github/workflows/python-publish.yml +0 -0
  9. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/.github/workflows/test.yml +0 -0
  10. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/.gitignore +0 -0
  11. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/LICENSE +0 -0
  12. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/examples/autoencoder_fsq.py +0 -0
  13. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/examples/autoencoder_lfq.py +0 -0
  14. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/examples/autoencoder_sim_vq.py +0 -0
  15. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/images/fsq.png +0 -0
  16. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/images/lfq.png +0 -0
  17. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/images/simvq.png +0 -0
  18. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/images/vq.png +0 -0
  19. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/ruff.toml +0 -0
  20. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/tests/test_latent_quantization.py +0 -0
  21. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/tests/test_lfq.py +0 -0
  22. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/vector_quantize_pytorch/__init__.py +0 -0
  23. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/vector_quantize_pytorch/finite_scalar_quantization.py +0 -0
  24. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/vector_quantize_pytorch/latent_quantization.py +0 -0
  25. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/vector_quantize_pytorch/lookup_free_quantization.py +0 -0
  26. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/vector_quantize_pytorch/random_projection_quantizer.py +0 -0
  27. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/vector_quantize_pytorch/residual_fsq.py +0 -0
  28. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/vector_quantize_pytorch/residual_lfq.py +0 -0
  29. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/vector_quantize_pytorch/residual_sim_vq.py +0 -0
  30. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/vector_quantize_pytorch/residual_vq.py +0 -0
  31. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/vector_quantize_pytorch/sim_vq.py +0 -0
  32. {vector_quantize_pytorch-1.23.4 → vector_quantize_pytorch-1.24.0}/vector_quantize_pytorch/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vector-quantize-pytorch
3
- Version: 1.23.4
3
+ Version: 1.24.0
4
4
  Summary: Vector Quantization - Pytorch
5
5
  Project-URL: Homepage, https://pypi.org/project/vector-quantize-pytorch/
6
6
  Project-URL: Repository, https://github.com/lucidrains/vector-quantizer-pytorch
@@ -823,3 +823,15 @@ assert loss.item() >= 0
823
823
  url = {https://api.semanticscholar.org/CorpusID:274423406}
824
824
  }
825
825
  ```
826
+
827
+ ```bibtex
828
+ @misc{vali2025diveqdifferentiablevectorquantization,
829
+ title = {DiVeQ: Differentiable Vector Quantization Using the Reparameterization Trick},
830
+ author = {Mohammad Hassan Vali and Tom Bäckström and Arno Solin},
831
+ year = {2025},
832
+ eprint = {2509.26469},
833
+ archivePrefix = {arXiv},
834
+ primaryClass = {cs.LG},
835
+ url = {https://arxiv.org/abs/2509.26469},
836
+ }
837
+ ```
@@ -779,3 +779,15 @@ assert loss.item() >= 0
779
779
  url = {https://api.semanticscholar.org/CorpusID:274423406}
780
780
  }
781
781
  ```
782
+
783
+ ```bibtex
784
+ @misc{vali2025diveqdifferentiablevectorquantization,
785
+ title = {DiVeQ: Differentiable Vector Quantization Using the Reparameterization Trick},
786
+ author = {Mohammad Hassan Vali and Tom Bäckström and Arno Solin},
787
+ year = {2025},
788
+ eprint = {2509.26469},
789
+ archivePrefix = {arXiv},
790
+ primaryClass = {cs.LG},
791
+ url = {https://arxiv.org/abs/2509.26469},
792
+ }
793
+ ```
@@ -75,8 +75,9 @@ torch.random.manual_seed(seed)
75
75
 
76
76
  model = SimpleVQAutoEncoder(
77
77
  codebook_size = num_codes,
78
- rotation_trick = True,
79
- straight_through = False
78
+ rotation_trick = False,
79
+ straight_through = False,
80
+ directional_reparam = True
80
81
  ).to(device)
81
82
 
82
83
  opt = torch.optim.AdamW(model.parameters(), lr=lr)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "vector-quantize-pytorch"
3
- version = "1.23.4"
3
+ version = "1.24.0"
4
4
  description = "Vector Quantization - Pytorch"
5
5
  authors = [
6
6
  { name = "Phil Wang", email = "lucidrains@gmail.com" }
@@ -224,6 +224,28 @@ def test_tiger():
224
224
 
225
225
  assert torch.allclose(quantized, quantized_out, atol = 1e-5)
226
226
 
227
+ def test_directional_reparam():
228
+ from vector_quantize_pytorch import VectorQuantize, ResidualVQ
229
+
230
+ vq = VectorQuantize(
231
+ dim = 256,
232
+ codebook_size = 512, # codebook size
233
+ directional_reparam = True
234
+ )
235
+
236
+ x = torch.randn(1, 1024, 256).requires_grad_()
237
+
238
+ quantized, indices, _ = vq(x)
239
+
240
+ rq = ResidualVQ(
241
+ dim = 256,
242
+ num_quantizers = 8,
243
+ codebook_size = 128,
244
+ directional_reparam = True
245
+ )
246
+
247
+ quantized, indices, _ = rq(x)
248
+
227
249
  @pytest.mark.parametrize('preserve_symmetry', (True, False))
228
250
  def test_fsq(
229
251
  preserve_symmetry
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from math import sqrt
3
4
  from functools import partial, cache
4
5
  from collections import namedtuple
5
6
 
@@ -28,6 +29,9 @@ def noop(*args, **kwargs):
28
29
  def identity(t):
29
30
  return t
30
31
 
32
+ def at_most_one_of(*bools):
33
+ return sum([*map(int, bools)]) <= 1
34
+
31
35
  def l2norm(t, dim = -1, eps = 1e-6):
32
36
  return F.normalize(t, p = 2, dim = dim, eps = eps)
33
37
 
@@ -264,6 +268,11 @@ def kmeans(
264
268
 
265
269
  return means, bins
266
270
 
271
+ # straight through
272
+
273
+ def straight_through(src, tgt):
274
+ return src + (tgt + src).detach()
275
+
267
276
  # rotation trick related
268
277
 
269
278
  def efficient_rotation_trick_transform(u, q, e):
@@ -279,7 +288,7 @@ def efficient_rotation_trick_transform(u, q, e):
279
288
  2 * (e @ rearrange(u, 'b d -> b d 1').detach() @ rearrange(q, 'b d -> b 1 d').detach())
280
289
  )
281
290
 
282
- return rearrange(out, '... 1 -> ...')
291
+ return rearrange(out, '... 1 d -> ... d')
283
292
 
284
293
  def rotate_to(src, tgt):
285
294
  # rotation trick STE (https://arxiv.org/abs/2410.06424) to get gradients through VQ layer.
@@ -299,6 +308,18 @@ def rotate_to(src, tgt):
299
308
 
300
309
  return inverse(rotated)
301
310
 
311
+ # directional reparam related
312
+ # figure 1. https://openreview.net/forum?id=KRVnpTbx7R
313
+
314
+ def directional_reparam(src, tgt, noise_variance = 5e-3):
315
+ error_dir = tgt - src
316
+ error_dir_norm = error_dir.norm(dim = -1, keepdim = True)
317
+
318
+ noised_dir = error_dir + sqrt(noise_variance) * torch.randn_like(error_dir)
319
+ unit_noised_dir = l2norm(noised_dir)
320
+
321
+ return src + unit_noised_dir * error_dir_norm
322
+
302
323
  # distributed helpers
303
324
 
304
325
  @cache
@@ -898,12 +919,14 @@ class VectorQuantize(Module):
898
919
  stochastic_sample_codes = False,
899
920
  sample_codebook_temp = 1.,
900
921
  straight_through = False,
901
- rotation_trick = None, # Propagate grads through VQ layer w/ rotation trick: https://arxiv.org/abs/2410.06424 by @cfifty
922
+ rotation_trick = None, # propagate grads through VQ layer w/ rotation trick: https://arxiv.org/abs/2410.06424 by @cfifty
923
+ directional_reparam = False, # add the difference between the nearest code and input vector, with some noise on the direction
924
+ directional_reparam_variance = 5e-3,
902
925
  sync_codebook = None,
903
926
  sync_affine_param = False,
904
- ema_update = True,
927
+ ema_update = None,
905
928
  manual_ema_update = False,
906
- learnable_codebook = False,
929
+ learnable_codebook = None,
907
930
  in_place_codebook_optimizer: Callable[..., Optimizer] = None, # Optimizer used to update the codebook embedding if using learnable_codebook
908
931
  manual_in_place_optimizer_update = False,
909
932
  affine_param = False,
@@ -913,7 +936,14 @@ class VectorQuantize(Module):
913
936
  return_zeros_for_masked_padding = True
914
937
  ):
915
938
  super().__init__()
916
- rotation_trick = default(rotation_trick, dim > 1) # only use rotation trick if feature dimension greater than 1
939
+
940
+ # defaults
941
+
942
+ ema_update = default(ema_update, not directional_reparam)
943
+ learnable_codebook = default(learnable_codebook, directional_reparam)
944
+ rotation_trick = default(rotation_trick, not directional_reparam and dim > 1) # only use rotation trick if feature dimension greater than 1
945
+
946
+ # basic variables
917
947
 
918
948
  self.dim = dim
919
949
  self.heads = heads
@@ -935,7 +965,7 @@ class VectorQuantize(Module):
935
965
 
936
966
  self.eps = eps
937
967
 
938
- self.has_commitment_loss = commitment_weight > 0.
968
+ self.has_commitment_loss = commitment_weight > 0. and not directional_reparam
939
969
  self.commitment_weight = commitment_weight
940
970
  self.commitment_use_cross_entropy_loss = commitment_use_cross_entropy_loss # whether to use cross entropy loss to codebook as commitment loss
941
971
 
@@ -953,9 +983,12 @@ class VectorQuantize(Module):
953
983
  self.codebook_diversity_temperature = codebook_diversity_temperature
954
984
  self.codebook_diversity_loss_weight = codebook_diversity_loss_weight
955
985
 
956
- assert not (straight_through and rotation_trick)
986
+ assert at_most_one_of(straight_through, rotation_trick, directional_reparam)
957
987
  self.rotation_trick = rotation_trick
958
988
 
989
+ self.directional_reparam = directional_reparam
990
+ self.directional_reparam_variance = directional_reparam_variance
991
+
959
992
  assert not (ema_update and learnable_codebook), 'learnable codebook not compatible with EMA update'
960
993
 
961
994
  assert 0 <= sync_update_v <= 1.
@@ -1213,9 +1246,11 @@ class VectorQuantize(Module):
1213
1246
  if input_requires_grad:
1214
1247
  if self.rotation_trick:
1215
1248
  quantize = rotate_to(x, quantize)
1249
+ elif self.directional_reparam:
1250
+ quantize = directional_reparam(x, quantize, self.directional_reparam_variance)
1216
1251
  else:
1217
1252
  # standard STE to get gradients through VQ layer.
1218
- quantize = x + (quantize - x).detach()
1253
+ quantize = straight_through(x, quantize)
1219
1254
 
1220
1255
  if self.sync_update_v > 0.:
1221
1256
  # (21) in https://minyoungg.github.io/vqtorch/assets/draft_050523.pdf