quantax 0.2.0__tar.gz → 0.2.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.
Files changed (65) hide show
  1. {quantax-0.2.0/quantax.egg-info → quantax-0.2.1}/PKG-INFO +3 -3
  2. {quantax-0.2.0 → quantax-0.2.1}/README.md +22 -12
  3. {quantax-0.2.0 → quantax-0.2.1}/docs/source/conf.py +1 -1
  4. {quantax-0.2.0 → quantax-0.2.1}/pyproject.toml +3 -3
  5. {quantax-0.2.0 → quantax-0.2.1}/quantax/model/conv_nets.py +9 -2
  6. {quantax-0.2.0 → quantax-0.2.1}/quantax/nn/modules.py +5 -4
  7. {quantax-0.2.0 → quantax-0.2.1}/quantax/optimizer/__init__.py +1 -1
  8. {quantax-0.2.0 → quantax-0.2.1}/quantax/optimizer/driver.py +13 -10
  9. {quantax-0.2.0 → quantax-0.2.1}/quantax/optimizer/sr.py +1 -1
  10. {quantax-0.2.0 → quantax-0.2.1}/quantax/optimizer/time_evol.py +24 -38
  11. {quantax-0.2.0 → quantax-0.2.1}/quantax/sampler/metropolis.py +4 -9
  12. {quantax-0.2.0 → quantax-0.2.1}/quantax/state/variational.py +49 -23
  13. {quantax-0.2.0 → quantax-0.2.1}/quantax/utils/__init__.py +1 -1
  14. {quantax-0.2.0 → quantax-0.2.1}/quantax/utils/function.py +38 -6
  15. {quantax-0.2.0 → quantax-0.2.1/quantax.egg-info}/PKG-INFO +3 -3
  16. {quantax-0.2.0 → quantax-0.2.1}/quantax.egg-info/requires.txt +2 -2
  17. {quantax-0.2.0 → quantax-0.2.1}/LICENSE +0 -0
  18. {quantax-0.2.0 → quantax-0.2.1}/quantax/__init__.py +0 -0
  19. {quantax-0.2.0 → quantax-0.2.1}/quantax/global_defs.py +0 -0
  20. {quantax-0.2.0 → quantax-0.2.1}/quantax/model/__init__.py +0 -0
  21. {quantax-0.2.0 → quantax-0.2.1}/quantax/model/backflow.py +0 -0
  22. {quantax-0.2.0 → quantax-0.2.1}/quantax/model/fermion_mf.py +0 -0
  23. {quantax-0.2.0 → quantax-0.2.1}/quantax/model/jastrow.py +0 -0
  24. {quantax-0.2.0 → quantax-0.2.1}/quantax/model/shallow_nets.py +0 -0
  25. {quantax-0.2.0 → quantax-0.2.1}/quantax/model/transformer.py +0 -0
  26. {quantax-0.2.0 → quantax-0.2.1}/quantax/model/triangular_nets.py +0 -0
  27. {quantax-0.2.0 → quantax-0.2.1}/quantax/nn/__init__.py +0 -0
  28. {quantax-0.2.0 → quantax-0.2.1}/quantax/nn/activation.py +0 -0
  29. {quantax-0.2.0 → quantax-0.2.1}/quantax/nn/conv.py +0 -0
  30. {quantax-0.2.0 → quantax-0.2.1}/quantax/nn/fermion.py +0 -0
  31. {quantax-0.2.0 → quantax-0.2.1}/quantax/nn/initializers.py +0 -0
  32. {quantax-0.2.0 → quantax-0.2.1}/quantax/nn/sign.py +0 -0
  33. {quantax-0.2.0 → quantax-0.2.1}/quantax/operator/__init__.py +0 -0
  34. {quantax-0.2.0 → quantax-0.2.1}/quantax/operator/common_operators.py +0 -0
  35. {quantax-0.2.0 → quantax-0.2.1}/quantax/operator/operator.py +0 -0
  36. {quantax-0.2.0 → quantax-0.2.1}/quantax/operator/site_operator.py +0 -0
  37. {quantax-0.2.0 → quantax-0.2.1}/quantax/optimizer/solver.py +0 -0
  38. {quantax-0.2.0 → quantax-0.2.1}/quantax/optimizer/supervised.py +0 -0
  39. {quantax-0.2.0 → quantax-0.2.1}/quantax/sampler/__init__.py +0 -0
  40. {quantax-0.2.0 → quantax-0.2.1}/quantax/sampler/common_samplers.py +0 -0
  41. {quantax-0.2.0 → quantax-0.2.1}/quantax/sampler/sampler.py +0 -0
  42. {quantax-0.2.0 → quantax-0.2.1}/quantax/sampler/samples.py +0 -0
  43. {quantax-0.2.0 → quantax-0.2.1}/quantax/sites/__init__.py +0 -0
  44. {quantax-0.2.0 → quantax-0.2.1}/quantax/sites/cluster.py +0 -0
  45. {quantax-0.2.0 → quantax-0.2.1}/quantax/sites/common_lattices.py +0 -0
  46. {quantax-0.2.0 → quantax-0.2.1}/quantax/sites/lattice.py +0 -0
  47. {quantax-0.2.0 → quantax-0.2.1}/quantax/sites/sites.py +0 -0
  48. {quantax-0.2.0 → quantax-0.2.1}/quantax/state/__init__.py +0 -0
  49. {quantax-0.2.0 → quantax-0.2.1}/quantax/state/fermion_mf.py +0 -0
  50. {quantax-0.2.0 → quantax-0.2.1}/quantax/state/lanczos.py +0 -0
  51. {quantax-0.2.0 → quantax-0.2.1}/quantax/state/state.py +0 -0
  52. {quantax-0.2.0 → quantax-0.2.1}/quantax/symmetry/__init__.py +0 -0
  53. {quantax-0.2.0 → quantax-0.2.1}/quantax/symmetry/common_symmetries.py +0 -0
  54. {quantax-0.2.0 → quantax-0.2.1}/quantax/symmetry/symmetry.py +0 -0
  55. {quantax-0.2.0 → quantax-0.2.1}/quantax/symmetry/translation.py +0 -0
  56. {quantax-0.2.0 → quantax-0.2.1}/quantax/utils/array.py +0 -0
  57. {quantax-0.2.0 → quantax-0.2.1}/quantax/utils/basis.py +0 -0
  58. {quantax-0.2.0 → quantax-0.2.1}/quantax/utils/big_array.py +0 -0
  59. {quantax-0.2.0 → quantax-0.2.1}/quantax/utils/data.py +0 -0
  60. {quantax-0.2.0 → quantax-0.2.1}/quantax/utils/sharding.py +0 -0
  61. {quantax-0.2.0 → quantax-0.2.1}/quantax/utils/tree.py +0 -0
  62. {quantax-0.2.0 → quantax-0.2.1}/quantax.egg-info/SOURCES.txt +0 -0
  63. {quantax-0.2.0 → quantax-0.2.1}/quantax.egg-info/dependency_links.txt +0 -0
  64. {quantax-0.2.0 → quantax-0.2.1}/quantax.egg-info/top_level.txt +0 -0
  65. {quantax-0.2.0 → quantax-0.2.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quantax
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Flexible neural quantum states based on JAX
5
5
  Author: Christopher Roth
6
6
  Author-email: Ao Chen <chenao.phys@gmail.com>
@@ -8,7 +8,7 @@ Requires-Python: <=3.13,>=3.10
8
8
  License-File: LICENSE
9
9
  Requires-Dist: numpy>=1.26.0
10
10
  Requires-Dist: scipy>=1.11.1
11
- Requires-Dist: jax>=0.4.34
11
+ Requires-Dist: jax>=0.6.1
12
12
  Requires-Dist: equinox>=0.11.4
13
13
  Requires-Dist: lrux>=0.1.0
14
14
  Provides-Extra: full
@@ -16,7 +16,7 @@ Requires-Dist: numpy>=2.0.0; extra == "full"
16
16
  Requires-Dist: scipy>=1.13.1; extra == "full"
17
17
  Requires-Dist: matplotlib>=3.8.0; extra == "full"
18
18
  Requires-Dist: quspin>=1.0.0; extra == "full"
19
- Requires-Dist: jax>=0.4.34; extra == "full"
19
+ Requires-Dist: jax>=0.6.1; extra == "full"
20
20
  Requires-Dist: equinox>=0.11.4; extra == "full"
21
21
  Requires-Dist: lrux>=0.1.0; extra == "full"
22
22
  Dynamic: license-file
@@ -1,34 +1,43 @@
1
1
  <h1 align='center'>Quantax</h1>
2
2
 
3
- **Flexible neural quantum states based on [JAX](https://github.com/google/jax)**
3
+ <p align="center">
4
+ <strong>Flexible and scalable neural quantum states built on JAX</strong>
5
+ </p>
4
6
 
5
- [📖 Documentation](https://chenao-phys.github.io/quantax)
7
+ <p align="center">
8
+ <a href="https://chenao-phys.github.io/quantax">📖 Documentation</a>
9
+ </p>
6
10
 
7
- ## Not only NQS, but also
11
+ ---
8
12
 
9
- - Exact diagonalization (based on [QuSpin](https://github.com/QuSpin/QuSpin))
13
+
14
+ ## 🔍Scope
15
+
16
+ Quantax is a research-oriented Python package for quantum many-body physics, with a focus on flexible and scalable neural quantum states (NQS). Apart from NQS, Quantax also includes several useful techniques in variational Monte Carlo (VMC):
17
+
18
+ - Exact diagonalization and small-system benchmarks via [QuSpin](https://github.com/QuSpin/QuSpin)
10
19
  - Fermionic mean-field wavefunctions
11
- - Flexible neural network design (based on [Equinox](https://github.com/patrick-kidger/equinox))
20
+ - Flexible (neural) wavefunction design via [Equinox](https://github.com/patrick-kidger/equinox)
21
+ - Tensor networks via [quimb](https://github.com/jcmgray/quimb/tree/main) and [symmray](https://github.com/jcmgray/symmray) (to be implemented)
12
22
 
13
- ## Installation
23
+ ## ⚙️Installation
14
24
 
15
- Requires Python 3.10+, JAX 0.4.34+
25
+ Requires Python 3.10+, JAX 0.6.1+
16
26
 
17
- First, ensure that a correct JAX version is installed.
18
- For details, check [JAX Installation](https://docs.jax.dev/en/latest/installation.html).
27
+ First, ensure that a correct JAX version is installed. For details, check [JAX Installation](https://docs.jax.dev/en/latest/installation.html).
19
28
 
20
29
  For a direct installation of full functionality (recommended in most cases),
21
- ```
30
+ ```bash
22
31
  pip install quantax[full]
23
32
  ```
24
33
 
25
34
  For a minimal installation,
26
- ```
35
+ ```bash
27
36
  pip install quantax
28
37
  ```
29
38
 
30
39
 
31
- ## Quick Start
40
+ ## 🚀Quick Start
32
41
 
33
42
  ```python
34
43
  import quantax as qtx
@@ -65,3 +74,4 @@ for i in range(100):
65
74
  energy_data.plot(baseline=E)
66
75
  plt.show()
67
76
  ```
77
+ ![quick_start](images/quick_start.svg)
@@ -14,7 +14,7 @@ sys.path.insert(0, pathlib.Path(__file__).parents[2].resolve().as_posix())
14
14
  project = "Quantax"
15
15
  copyright = "2025, Ao Chen"
16
16
  author = "Ao Chen, Christopher Roth"
17
- release = "0.2.0"
17
+ release = "0.2.1"
18
18
 
19
19
  # -- General configuration ---------------------------------------------------
20
20
  # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "quantax"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Flexible neural quantum states based on JAX"
9
9
  authors = [
10
10
  { name = "Ao Chen", email = "chenao.phys@gmail.com" },
@@ -14,7 +14,7 @@ requires-python = ">=3.10,<=3.13"
14
14
  dependencies = [
15
15
  "numpy>=1.26.0",
16
16
  "scipy>=1.11.1",
17
- "jax>=0.4.34",
17
+ "jax>=0.6.1",
18
18
  "equinox>=0.11.4",
19
19
  "lrux>=0.1.0",
20
20
  ]
@@ -25,7 +25,7 @@ full = [
25
25
  "scipy>=1.13.1",
26
26
  "matplotlib>=3.8.0",
27
27
  "quspin>=1.0.0",
28
- "jax>=0.4.34",
28
+ "jax>=0.6.1",
29
29
  "equinox>=0.11.4",
30
30
  "lrux>=0.1.0",
31
31
  ]
@@ -93,6 +93,8 @@ class _ConvBlock(eqx.Module):
93
93
 
94
94
 
95
95
  class ResConv(Sequential):
96
+ """Deep convolutional residual network."""
97
+
96
98
  nblocks: int
97
99
  channels: int
98
100
  kernel_size: Union[int, Sequence[int]]
@@ -133,10 +135,15 @@ class ResConv(Sequential):
133
135
  The translation symmetry to be applied in the last layer, see `~quantax.nn.ConvSymmetrize`.
134
136
 
135
137
  :param dtype:
136
- The data type of the parameters.
138
+ The data type of the parameters. Must be a real dtype.
139
+
140
+ :param out_dtype:
141
+ The data type of the output wavefunction. By default, it is the same as `dtype`.
142
+ If `out_dtype` is complex, `~quantax.nn.pair_cpl` will be applied to the output
143
+ of convolutional layers to make the final output complex.
137
144
 
138
145
  .. tip::
139
- This is the recommended architecture for deep neural quantum states in spin systems.
146
+ This is the recommended architecture for deep NQS in spin systems.
140
147
  """
141
148
  if jnp.issubdtype(dtype, jnp.complexfloating):
142
149
  raise ValueError("`ResSum` doesn't support complex dtypes.")
@@ -3,6 +3,7 @@ from typing import Sequence, Tuple, Union, Callable
3
3
  from jaxtyping import PyTree
4
4
  import jax
5
5
  import equinox as eqx
6
+ from ..utils import PsiArray
6
7
 
7
8
 
8
9
  class Sequential(eqx.nn.Sequential):
@@ -35,7 +36,7 @@ class Sequential(eqx.nn.Sequential):
35
36
  super().__init__(layers)
36
37
  self.holomorphic = holomorphic
37
38
 
38
- def __call__(self, x: jax.Array, *, s: jax.Array = None) -> jax.Array:
39
+ def __call__(self, x: jax.Array, *, s: jax.Array = None) -> PsiArray:
39
40
  """**Arguments:**
40
41
 
41
42
  - `x`: passed to the first member of the sequence.
@@ -74,7 +75,7 @@ class RawInputLayer(eqx.Module):
74
75
  basis state.
75
76
  """
76
77
 
77
- def __call__(self, x: jax.Array, s: jax.Array) -> jax.Array:
78
+ def __call__(self, x: jax.Array, s: jax.Array) -> Callable:
78
79
  """
79
80
  The forward pass.
80
81
 
@@ -97,7 +98,7 @@ class RefModel(eqx.Module):
97
98
  Return initial internal values for the given configuration.
98
99
  """
99
100
 
100
- def __call__(self, x: jax.Array) -> jax.Array:
101
+ def __call__(self, x: jax.Array) -> PsiArray:
101
102
  """
102
103
  Usual forward pass without internal quantities.
103
104
  """
@@ -109,7 +110,7 @@ class RefModel(eqx.Module):
109
110
  nflips: int,
110
111
  internal: PyTree,
111
112
  return_update: bool = False,
112
- ) -> Union[jax.Array, Tuple[jax.Array, PyTree]]:
113
+ ) -> Union[PsiArray, Tuple[PsiArray, PyTree]]:
113
114
  """
114
115
  Accelerated forward pass through local updates and internal quantities.
115
116
 
@@ -15,4 +15,4 @@ from .solver import (
15
15
  minsr_pinv_eig,
16
16
  sgd_solver,
17
17
  )
18
- from .time_evol import TimeEvol, ExactTimeEvol
18
+ from .time_evol import TimeEvol
@@ -7,7 +7,7 @@ from ..sampler import Sampler
7
7
  from ..utils import DataTracer
8
8
 
9
9
 
10
- class Driver():
10
+ class Driver:
11
11
  def __init__(
12
12
  self,
13
13
  state: Variational,
@@ -26,8 +26,10 @@ class Driver():
26
26
  def step(self) -> None:
27
27
  """iteration"""
28
28
 
29
+
29
30
  class Euler(Driver):
30
31
  """First order Euler driver"""
32
+
31
33
  def step(self) -> None:
32
34
  samples = self._sampler.sweep()
33
35
  step = self._optimizer.get_step(samples)
@@ -39,6 +41,7 @@ class Euler(Driver):
39
41
 
40
42
  class AdaptiveHeunEvolution(Driver):
41
43
  """Adaptive second order Heun driver, designed for unitary time-evolution"""
44
+
42
45
  def __init__(
43
46
  self,
44
47
  state: Variational,
@@ -57,13 +60,13 @@ class AdaptiveHeunEvolution(Driver):
57
60
 
58
61
  samples = self._sampler.sweep()
59
62
  stepi = tdvp.get_step(samples)
60
- self._state.update(dt * stepi, rescale=False)
63
+ self._state.update(dt * stepi)
61
64
  dtheta0_i = 1j * tdvp.energy - jnp.dot(tdvp._Omean, stepi)
62
-
65
+
63
66
  samples = self._sampler.sweep()
64
67
  stepf = tdvp.get_step(samples)
65
68
  step1 = (stepi + stepf) / 2
66
- self._state.update(-dt / 2 * stepi, rescale=False)
69
+ self._state.update(-dt / 2 * stepi)
67
70
 
68
71
  samples = self._sampler.sweep()
69
72
  stepm = tdvp.get_step(samples)
@@ -72,18 +75,18 @@ class AdaptiveHeunEvolution(Driver):
72
75
 
73
76
  samples = self._sampler.sweep()
74
77
  stepmm = tdvp.get_step(samples)
75
- self._state.update(dt / 2 * stepmm, rescale=False)
78
+ self._state.update(dt / 2 * stepmm)
76
79
  dtheta0_mm = 1j * tdvp.energy - jnp.dot(tdvp._Omean, stepmm)
77
80
 
78
81
  samples = self._sampler.sweep()
79
82
  Smat, Fvec = tdvp.get_SF(samples)
80
83
  stepff = tdvp.solve(Smat, Fvec)
81
- self._state.update(dt / 4 * (stepff - stepmm), rescale=True)
84
+ self._state.update(dt / 4 * (stepff - stepmm))
82
85
  dtheta0_ff = 1j * tdvp.energy - jnp.dot(tdvp._Omean, stepff)
83
86
  step2 = (stepi + stepm + stepmm + stepff) / 4
84
87
 
85
- dtheta0 = dt * (dtheta0_i + dtheta0_m + dtheta0_mm + dtheta0_ff) / 4
86
- self._state.rescale(dtheta0)
88
+ # dtheta0 = dt * (dtheta0_i + dtheta0_m + dtheta0_mm + dtheta0_ff) / 4
89
+ # self._state.rescale(dtheta0)
87
90
 
88
91
  diff = step1 - step2
89
92
  new_err = jnp.einsum("k,kq,q", diff.conj(), Smat, diff).real
@@ -95,5 +98,5 @@ class AdaptiveHeunEvolution(Driver):
95
98
  self._step_length = new_step_length
96
99
 
97
100
  self.step_size.append(new_step_length, self._time)
98
- self.energy.append(self._optimizer.energy, self._time)
99
- self.VarE.append(self._optimizer.VarE, self._time)
101
+ self.energy.append(tdvp.energy, self._time)
102
+ self.VarE.append(tdvp.VarE, self._time)
@@ -491,7 +491,7 @@ class ER(QNGD):
491
491
  energy = dense @ H_psi
492
492
  Ebar = H_psi - dense * energy
493
493
  self._energy = energy.real
494
- return Ebar.psi
494
+ return jnp.asarray(Ebar.psi)
495
495
 
496
496
  def get_Obar(self, psi: jax.Array) -> jax.Array:
497
497
  r"""Compute :math:`\bar O` in the full Hilbert space."""
@@ -1,13 +1,11 @@
1
- from typing import Union, Tuple, Optional, Callable
2
- from numbers import Number
1
+ from typing import Tuple, Optional, Callable
3
2
  from functools import partial
4
- import numpy as np
5
3
  import jax
6
4
  import jax.numpy as jnp
7
5
 
8
6
  from .sr import SR
9
7
  from .solver import pinvh_solve
10
- from ..state import DenseState, Variational, VS_TYPE
8
+ from ..state import Variational, VS_TYPE
11
9
  from ..operator import Operator
12
10
  from ..sampler import Samples
13
11
  from ..utils import get_distribute_sharding, array_extend
@@ -26,18 +24,40 @@ def _AconjB(A: jax.Array, B: jax.Array) -> jax.Array:
26
24
 
27
25
 
28
26
  class TimeEvol(SR):
27
+ r"""
28
+ Time evolution optimizer, equivalent to real-time `~quantax.optimizer.SR`.
29
+ This optimizer assumes the number of samples is more than the number of parameters,
30
+ and is more memory-efficient than `~quantax.optimizer.SR` when this is the case.
31
+ """
32
+
29
33
  def __init__(
30
34
  self,
31
35
  state: Variational,
32
36
  hamiltonian: Operator,
33
37
  solver: Optional[Callable] = None,
34
38
  ):
39
+ r"""
40
+ :param state:
41
+ Variational state to be evolved.
42
+
43
+ :param hamiltonian:
44
+ Hamiltonian operator for time evolution.
45
+
46
+ :param solver:
47
+ The numerical solver for :math:`Sx = F`, default to pseudo-inverse.
48
+ """
49
+
35
50
  if solver is None:
36
51
  solver = pinvh_solve()
37
52
  super().__init__(state, hamiltonian, imag_time=False, solver=solver)
38
53
  self._max_parallel = state._backward_chunk
39
54
 
40
55
  def get_SF(self, samples: Samples) -> Tuple[jax.Array, jax.Array]:
56
+ r"""
57
+ Compute :math:`S = \bar O^\dagger \bar O` and :math:`F = \bar O^\dagger \bar \epsilon`
58
+ with the given samples. When the number of samples is large, this function will
59
+ automatically switch to a more memory-efficient implementation.
60
+ """
41
61
  if (
42
62
  self._max_parallel is None
43
63
  or samples.nsamples <= self._max_parallel * jax.device_count()
@@ -81,7 +101,6 @@ class TimeEvol(SR):
81
101
  newF = _AconjB(Omat, e)
82
102
  Smat += newS
83
103
  Fvec += newF
84
- # psum here, nsamples definition should be modified to all samples across nodes
85
104
  Smat = jnp.sum(Smat, axis=0) / nsamples
86
105
  Fvec = jnp.sum(Fvec, axis=0) / nsamples
87
106
  Omean = jnp.sum(Omean, axis=0) / nsamples
@@ -113,36 +132,3 @@ class TimeEvol(SR):
113
132
  Smat, Fvec = self.get_SF(samples)
114
133
  step = self.solve(Smat, Fvec)
115
134
  return step
116
-
117
-
118
- class ExactTimeEvol:
119
- def __init__(self, init_state: DenseState, hamiltonian: Operator) -> None:
120
- self._init_state = init_state
121
- self._symm = self._init_state.symm
122
- self._eigs, self._U = hamiltonian.diagonalize(self._symm, "full")
123
- self._eigs = jnp.asarray(self._eigs)
124
- self._U = jnp.asarray(self._U)
125
-
126
- def get_evolved_psi(self, time: Union[float, jax.Array]) -> jax.Array:
127
- is_float = isinstance(time, float)
128
- if is_float:
129
- time = jnp.array([time])
130
- exp_eigs = jnp.exp(-1j * jnp.einsum("t,d->td", time, self._eigs))
131
- psi0 = self._init_state.psi
132
- psi = jnp.einsum("ij,tj,kj,k->ti", self._U, exp_eigs, self._U.conj(), psi0)
133
- if is_float:
134
- psi = psi[0]
135
- return psi
136
-
137
- def expectation(
138
- self, operator: Operator, time: Union[float, jax.Array]
139
- ) -> Union[Number, jax.Array]:
140
- psi = self.get_evolved_psi(time)
141
- psi /= jnp.linalg.norm(psi, axis=1, keepdims=True)
142
- op = operator.get_quspin_op(self._symm)
143
-
144
- # this hasn't been tested
145
- out = jnp.einsum("ti,it->t", psi.conj(), op.dot(np.ascontiguousarray(psi.T)))
146
- if isinstance(time, float):
147
- out = out.item()
148
- return out
@@ -181,15 +181,10 @@ class Metropolis(Sampler):
181
181
 
182
182
  state = self._state
183
183
  use_ref = state.use_ref and self.nflips is not None
184
- if use_ref:
185
- has_chunk = hasattr(state, "ref_chunk") and state.ref_chunk is not None
186
- chunk_size = state.ref_chunk
187
- else:
188
- has_chunk = (
189
- hasattr(state, "forward_chunk") and state.forward_chunk is not None
190
- )
191
- chunk_size = state.forward_chunk
192
- is_chunked = has_chunk and chunk_size < self.nsamples // jax.device_count()
184
+ attr = "ref_chunk" if use_ref else "forward_chunk"
185
+ chunk_size = getattr(state, attr, None)
186
+ ns = self.nsamples // jax.device_count()
187
+ is_chunked = chunk_size is not None and chunk_size < ns
193
188
 
194
189
  if is_chunked:
195
190
  if use_ref:
@@ -1,5 +1,5 @@
1
1
  from __future__ import annotations
2
- from typing import Callable, Optional, Tuple, Union, BinaryIO
2
+ from typing import Optional, Tuple, Union, BinaryIO
3
3
  from jaxtyping import PyTree
4
4
  from pathlib import Path
5
5
 
@@ -16,6 +16,8 @@ from .state import State
16
16
  from ..symmetry import Symmetry
17
17
  from ..nn import RefModel
18
18
  from ..utils import (
19
+ chunk_map,
20
+ shard_vmap,
19
21
  chunk_shard_vmap,
20
22
  to_distribute_array,
21
23
  filter_replicate,
@@ -114,7 +116,7 @@ class Variational(State):
114
116
  model: eqx.Module,
115
117
  param_file: Optional[Union[str, Path, BinaryIO]] = None,
116
118
  symm: Optional[Symmetry] = None,
117
- max_parallel: Union[None, int, Tuple[int, int]] = None,
119
+ max_parallel: Union[None, int, Tuple[int, int], Tuple[int, int, int]] = None,
118
120
  use_ref: bool = True,
119
121
  ):
120
122
  r"""
@@ -126,23 +128,35 @@ class Variational(State):
126
128
  or `equinox.tree_serialise_leaves`, default to not loading parameters.
127
129
 
128
130
  :param symm: Symmetry of the network, default to `~quantax.symmetry.Identity`.
131
+ Denoting the network output as :math:`f(s)`
132
+ and symmetry elements as :math:`T_i` with characters :math:`\omega_i`,
133
+ the wave function is given by
134
+ :math:`\psi(s) = \sum_i \omega_i \, f(T_i s) / n_{symm}`
129
135
 
130
136
  :param max_parallel:
131
- The maximum foward pass allowed per device, default to no limit.
132
- Specifying a limited value is important for large batches to avoid memory overflow.
133
- For Heisenberg-like hamiltonian, this also helps to improve the efficiency
134
- of computing local energy by keeping constant amount of forward pass and
135
- avoiding re-jitting.
137
+ The maximum chunk size allowed per device.
138
+ Specifying a limited value is important for avoiding memory overflow.
139
+ For many hamiltonians, this also helps to improve the efficiency
140
+ by keeping a constant amount of forward pass and avoiding re-jitting.
141
+ The allowed input formats are:
142
+
143
+ - None:
144
+ No chunk size (default).
145
+
146
+ - int:
147
+ The same chunk size for all forward and backward passes.
148
+
149
+ - Tuple[int, int]:
150
+ The chunk size for forward and backward passes respectively.
151
+
152
+ - Tuple[int, int, int]:
153
+ The chunk size for forward pass, backward pass and
154
+ `~quantax.state.Variational.ref_forward_with_updates` respectively.
136
155
 
137
156
  :param use_ref:
138
157
  Whether `ref_forward` and `ref_forward_with_updates` will be used when
139
158
  the model is a `~quantax.nn.RefModel`. When the model is not a `RefModel`,
140
159
  this argument has no effect. Default to ``True``.
141
-
142
- Denoting the network output as :math:`f(s)`,
143
- and symmetry elements as :math:`T_i` with characters :math:`\omega_i`,
144
- the final wave function is given by
145
- :math:`\psi(s) = \sum_i \omega_i \, f(T_i s) / n_{symm}`
146
160
  """
147
161
  super().__init__(symm)
148
162
  if param_file is not None:
@@ -180,17 +194,20 @@ class Variational(State):
180
194
 
181
195
  @property
182
196
  def forward_chunk(self) -> int:
183
- """The maximum foward pass allowed per device."""
197
+ """The maximum chunk size of forward pass allowed per device."""
184
198
  return self._forward_chunk
185
199
 
186
200
  @property
187
201
  def backward_chunk(self) -> int:
188
- """The maximum backward pass allowed per device."""
202
+ """The maximum chunk size of backward pass allowed per device."""
189
203
  return self._backward_chunk
190
204
 
191
205
  @property
192
206
  def ref_chunk(self) -> int:
193
- """The maximum reference forward with updates allowed per device."""
207
+ """
208
+ The maximum chunk size of `~quantax.state.Variational.ref_forward_with_updates`
209
+ allowed per device.
210
+ """
194
211
  return self._ref_chunk
195
212
 
196
213
  @property
@@ -242,23 +259,28 @@ class Variational(State):
242
259
  )
243
260
 
244
261
  def _init_forward(self) -> None:
245
- def direct_forward(model: eqx.Module, s: jax.Array) -> jax.Array:
262
+ def batch_forward(model: eqx.Module, s: jax.Array) -> jax.Array:
246
263
  s_symm = self.symm.get_symm_spins(s)
247
264
  psi = jax.vmap(model)(s_symm)
248
265
  psi = self.symm.symmetrize(psi, s)
249
266
  return psi.astype(get_default_dtype())
250
267
 
251
- self._direct_forward = chunk_shard_vmap(
252
- direct_forward, in_axes=(None, 0), out_axes=0, chunk_size=self.forward_chunk
268
+ self._batch_forward = shard_vmap(batch_forward, in_axes=(None, 0), out_axes=0)
269
+ self._direct_forward = chunk_map(
270
+ self._batch_forward, in_axes=(None, 0), chunk_size=self.forward_chunk
271
+ )
272
+ self._fulljit_forward = chunk_shard_vmap(
273
+ batch_forward, in_axes=(None, 0), out_axes=0, chunk_size=self.forward_chunk
253
274
  )
254
275
 
255
276
  def init_internal(model, s):
256
277
  s_symm = self.symm.get_symm_spins(s)
257
278
  return jax.vmap(model.init_internal)(s_symm)
258
279
 
259
- self._init_internal = chunk_shard_vmap(
280
+ init_internal = chunk_shard_vmap(
260
281
  init_internal, in_axes=(None, 0), out_axes=0, chunk_size=self.ref_chunk
261
282
  )
283
+ self._init_internal = eqx.filter_jit(init_internal)
262
284
 
263
285
  def ref_forward_with_updates(model, s, s_old, nflips, internal):
264
286
  s_symm = self.symm.get_symm_spins(s)
@@ -288,11 +310,15 @@ class Variational(State):
288
310
  psi = self.symm.symmetrize(psi, s)
289
311
  return psi.astype(get_default_dtype())
290
312
 
291
- self._ref_forward = chunk_shard_vmap(
313
+ self._batch_ref_forward = shard_vmap(
292
314
  ref_forward,
293
315
  in_axes=(None, 0, None, None, 0, None),
294
- shard_axes=(None, 0, 0, None, 0, 0),
295
316
  out_axes=0,
317
+ shard_axes=(None, 0, 0, None, 0, 0),
318
+ )
319
+ self._ref_forward = chunk_map(
320
+ self._batch_ref_forward,
321
+ in_axes=(None, 0, None, None, 0, None),
296
322
  chunk_size=self.forward_chunk,
297
323
  )
298
324
 
@@ -355,7 +381,7 @@ class Variational(State):
355
381
  if self._use_ref:
356
382
  out = self._ref_forward_with_updates(self.model, s, s_old, nflips, internal)
357
383
  else:
358
- out = self(s), None
384
+ out = self._fulljit_forward(self.model, s), None
359
385
  return out
360
386
 
361
387
  def ref_forward(
@@ -392,7 +418,7 @@ class Variational(State):
392
418
  if self._use_ref:
393
419
  out = self._ref_forward(self.model, s, s_old, nflips, idx_segment, internal)
394
420
  else:
395
- out = self(s)
421
+ out = self._direct_forward(self.model, s)
396
422
  return out
397
423
 
398
424
  def _init_backward(self) -> None:
@@ -23,5 +23,5 @@ from .tree import (
23
23
  apply_updates,
24
24
  )
25
25
  from .big_array import LogArray, ScaleArray, PsiArray, where
26
- from .function import chunk_shard_vmap, chunk_map, shmap
26
+ from .function import chunk_map, shmap, shard_vmap, chunk_shard_vmap
27
27
  from .basis import ints_to_array, array_to_ints, neel, stripe, Sqz_factor, rand_states
@@ -4,7 +4,6 @@ import jax
4
4
  import jax.numpy as jnp
5
5
  from jaxtyping import PyTree
6
6
  from jax.sharding import Mesh, PartitionSpec
7
- from jax.experimental.shard_map import shard_map
8
7
  import equinox as eqx
9
8
  from .array import array_extend
10
9
  from .tree import filter_tree_map
@@ -180,19 +179,20 @@ def shmap(
180
179
  mesh = Mesh(jax.devices(), "x")
181
180
  in_specs = _axes_to_specs(in_axes)
182
181
  out_specs = _axes_to_specs(out_axes)
183
- f = shard_map(f, mesh, in_specs, out_specs, check_rep=False)
182
+ f = jax.shard_map(
183
+ f, mesh=mesh, in_specs=in_specs, out_specs=out_specs, check_vma=False
184
+ )
184
185
  return f
185
186
 
186
187
 
187
- def chunk_shard_vmap(
188
+ def shard_vmap(
188
189
  f: Callable,
189
190
  in_axes: Union[tuple, int, None],
190
191
  out_axes: Union[tuple, int, None],
191
- chunk_size: Optional[int] = None,
192
192
  shard_axes: Union[tuple, int, None] = None,
193
193
  ) -> Callable:
194
194
  """
195
- f -> jit(chunk_map(shard_map(vmap(f))))
195
+ f -> shard_map(vmap(f))
196
196
 
197
197
  :param f:
198
198
  The function to be converted. The arguments of f will be sharded.
@@ -202,12 +202,44 @@ def chunk_shard_vmap(
202
202
 
203
203
  :param out_axes:
204
204
  The mapped axes of f outputs.
205
- """
206
205
 
206
+ :param shard_axes:
207
+ The sharded axes of f input arguments. Default to in_axes.
208
+ """
207
209
  shard_axes = in_axes if shard_axes is None else shard_axes
208
210
 
209
211
  f = jax.vmap(f, in_axes, out_axes)
210
212
  f = shmap(f, shard_axes, out_axes)
213
+ f = eqx.filter_jit(f)
214
+ return f
215
+
216
+
217
+ def chunk_shard_vmap(
218
+ f: Callable,
219
+ in_axes: Union[tuple, int, None],
220
+ out_axes: Union[tuple, int, None],
221
+ shard_axes: Union[tuple, int, None] = None,
222
+ chunk_size: Optional[int] = None,
223
+ ) -> Callable:
224
+ """
225
+ f -> chunk_map(shard_map(vmap(f)), use_scan=True)
226
+
227
+ :param f:
228
+ The function to be converted. The arguments of f will be sharded.
229
+
230
+ :param in_axes:
231
+ The mapped axes of f input arguments.
232
+
233
+ :param out_axes:
234
+ The mapped axes of f outputs.
235
+
236
+ :param shard_axes:
237
+ The sharded axes of f input arguments. Default to in_axes.
238
+
239
+ :param chunk_size:
240
+ The chunk size on each machine. If None, no chunking will be applied.
241
+ """
242
+ f = shard_vmap(f, in_axes, out_axes, shard_axes)
211
243
  f = chunk_map(f, in_axes, out_axes, chunk_size, use_scan=True)
212
244
  f = eqx.filter_jit(f)
213
245
  return f
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: quantax
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Flexible neural quantum states based on JAX
5
5
  Author: Christopher Roth
6
6
  Author-email: Ao Chen <chenao.phys@gmail.com>
@@ -8,7 +8,7 @@ Requires-Python: <=3.13,>=3.10
8
8
  License-File: LICENSE
9
9
  Requires-Dist: numpy>=1.26.0
10
10
  Requires-Dist: scipy>=1.11.1
11
- Requires-Dist: jax>=0.4.34
11
+ Requires-Dist: jax>=0.6.1
12
12
  Requires-Dist: equinox>=0.11.4
13
13
  Requires-Dist: lrux>=0.1.0
14
14
  Provides-Extra: full
@@ -16,7 +16,7 @@ Requires-Dist: numpy>=2.0.0; extra == "full"
16
16
  Requires-Dist: scipy>=1.13.1; extra == "full"
17
17
  Requires-Dist: matplotlib>=3.8.0; extra == "full"
18
18
  Requires-Dist: quspin>=1.0.0; extra == "full"
19
- Requires-Dist: jax>=0.4.34; extra == "full"
19
+ Requires-Dist: jax>=0.6.1; extra == "full"
20
20
  Requires-Dist: equinox>=0.11.4; extra == "full"
21
21
  Requires-Dist: lrux>=0.1.0; extra == "full"
22
22
  Dynamic: license-file
@@ -1,6 +1,6 @@
1
1
  numpy>=1.26.0
2
2
  scipy>=1.11.1
3
- jax>=0.4.34
3
+ jax>=0.6.1
4
4
  equinox>=0.11.4
5
5
  lrux>=0.1.0
6
6
 
@@ -9,6 +9,6 @@ numpy>=2.0.0
9
9
  scipy>=1.13.1
10
10
  matplotlib>=3.8.0
11
11
  quspin>=1.0.0
12
- jax>=0.4.34
12
+ jax>=0.6.1
13
13
  equinox>=0.11.4
14
14
  lrux>=0.1.0
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes