cuquantum-python-jax-cu13 0.0.6__tar.gz → 0.0.7__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 (50) hide show
  1. {cuquantum_python_jax_cu13-0.0.6/cuquantum_python_jax_cu13.egg-info → cuquantum_python_jax_cu13-0.0.7}/PKG-INFO +11 -8
  2. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/README.md +9 -6
  3. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/densitymat/jax/__init__.py +2 -9
  4. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/densitymat/jax/cppsrc/cudensitymat.h +166 -26
  5. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/densitymat/jax/operator_action.py +61 -30
  6. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/densitymat/jax/pysrc/__init__.py +1 -0
  7. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/densitymat/jax/pysrc/base.py +0 -8
  8. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/densitymat/jax/pysrc/context.py +17 -2
  9. cuquantum_python_jax_cu13-0.0.7/cuquantum/densitymat/jax/pysrc/elementary_operator.py +474 -0
  10. cuquantum_python_jax_cu13-0.0.7/cuquantum/densitymat/jax/pysrc/matrix_operator.py +387 -0
  11. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/densitymat/jax/pysrc/operator.py +156 -66
  12. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/densitymat/jax/pysrc/operator_action_prim.py +134 -46
  13. cuquantum_python_jax_cu13-0.0.7/cuquantum/densitymat/jax/pysrc/operator_term.py +1138 -0
  14. cuquantum_python_jax_cu13-0.0.7/cuquantum/densitymat/jax/pysrc/simplifier_config.py +112 -0
  15. cuquantum_python_jax_cu13-0.0.7/cuquantum/densitymat/jax/utils.py +858 -0
  16. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/stabilizer/jax/__init__.py +0 -10
  17. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/stabilizer/jax/cppsrc/CMakeLists.txt +0 -1
  18. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/stabilizer/jax/cppsrc/custabilizer.h +229 -8
  19. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/stabilizer/jax/pysrc/_ffi.py +30 -4
  20. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7/cuquantum_python_jax_cu13.egg-info}/PKG-INFO +11 -8
  21. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum_python_jax_cu13.egg-info/SOURCES.txt +1 -0
  22. cuquantum_python_jax_cu13-0.0.7/cuquantum_python_jax_cu13.egg-info/requires.txt +2 -0
  23. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/pyproject.toml +2 -2
  24. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/pyproject.toml.template +2 -2
  25. cuquantum_python_jax_cu13-0.0.6/cuquantum/densitymat/jax/pysrc/elementary_operator.py +0 -259
  26. cuquantum_python_jax_cu13-0.0.6/cuquantum/densitymat/jax/pysrc/matrix_operator.py +0 -200
  27. cuquantum_python_jax_cu13-0.0.6/cuquantum/densitymat/jax/pysrc/operator_term.py +0 -487
  28. cuquantum_python_jax_cu13-0.0.6/cuquantum/densitymat/jax/utils.py +0 -399
  29. cuquantum_python_jax_cu13-0.0.6/cuquantum_python_jax_cu13.egg-info/requires.txt +0 -2
  30. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/LICENSE +0 -0
  31. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/MANIFEST.in +0 -0
  32. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/NV.LICENSE +0 -0
  33. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/configure.sh +0 -0
  34. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/densitymat/jax/_build_info.py +0 -0
  35. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/densitymat/jax/cppsrc/CMakeLists.txt +0 -0
  36. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/densitymat/jax/cppsrc/cudensitymat_jax.cpp +0 -0
  37. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/densitymat/jax/cppsrc/cudensitymat_jax.h +0 -0
  38. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/densitymat/jax/cppsrc/pybind.cpp +0 -0
  39. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/densitymat/jax/cppsrc/utils.h +0 -0
  40. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/stabilizer/jax/_build_info.py +0 -0
  41. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/stabilizer/jax/cppsrc/custabilizer_jax.cpp +0 -0
  42. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/stabilizer/jax/cppsrc/custabilizer_jax.h +0 -0
  43. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/stabilizer/jax/cppsrc/pybind.cpp +0 -0
  44. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/stabilizer/jax/pysrc/__init__.py +0 -0
  45. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum/stabilizer/jax/pysrc/_backend.py +0 -0
  46. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum_python_jax_cu13.egg-info/dependency_links.txt +0 -0
  47. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum_python_jax_cu13.egg-info/not-zip-safe +0 -0
  48. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/cuquantum_python_jax_cu13.egg-info/top_level.txt +0 -0
  49. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/setup.cfg +0 -0
  50. {cuquantum_python_jax_cu13-0.0.6 → cuquantum_python_jax_cu13-0.0.7}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cuquantum-python-jax-cu13
3
- Version: 0.0.6
3
+ Version: 0.0.7
4
4
  Summary: NVIDIA cuQuantum Python JAX
5
5
  Author-email: NVIDIA Corporation <cuquantum-python@nvidia.com>
6
6
  License-Expression: BSD-3-Clause
@@ -21,13 +21,13 @@ Requires-Python: >=3.11.0
21
21
  Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
23
  License-File: NV.LICENSE
24
- Requires-Dist: cuquantum-python-cu13~=26.6.0
24
+ Requires-Dist: cuquantum-python-cu13~=26.9.0
25
25
  Requires-Dist: jax[cuda13-local]>=0.8
26
26
  Dynamic: license-file
27
27
 
28
28
  # cuQuantum Python JAX
29
29
 
30
- cuQuantum Python JAX provides a JAX extension for cuQuantum Python. It exposes selected functionality of cuQuantum SDK in a JAX-compatible way that enables JAX frameworks to directly interface with the exposed cuQuantum API. In the current release, cuQuantum JAX exposes a JAX interface to the Operator Action API from the cuDensityMat library and the GF(2) sparse-dense matrix multiply from the cuStabilizer library.
30
+ cuQuantum Python JAX provides a JAX extension for cuQuantum Python. It exposes selected functionalities of cuQuantum SDK in a JAX-compatible way that enables JAX frameworks to directly interface with the exposed cuQuantum API. In the current release, cuQuantum JAX exposes JAX interfaces to the Operator Action API from the cuDensityMat library and the GF(2) sparse-dense matrix multiplication from the cuStabilizer library.
31
31
 
32
32
  ## Documentation
33
33
 
@@ -39,15 +39,17 @@ Please visit the [NVIDIA cuQuantum Python documentation](https://docs.nvidia.com
39
39
 
40
40
  The build-time dependencies of the cuQuantum Python JAX package include:
41
41
 
42
+ * Python 3.11+
42
43
  * CUDA Toolkit 12.x or 13.x
43
- * jax[cuda12-local]>=0.5,<0.7 for CUDA 12 or jax[cuda13-local]>=0.8,<0.9 for CUDA 13
44
+ * cmake>=3.22
45
+ * jax[cuda12-local]>=0.8 for CUDA 12 or jax[cuda13-local]>=0.8 for CUDA 13
44
46
  * pybind11
45
47
  * wheel
46
48
  * setuptools>=77.0.3
47
49
 
48
50
  Note:
49
- - cuQuantum Python JAX is only supported with CUDA 12 and CUDA 13.
50
- - cuQuantum Python JAX wheels are CUDA-versioned: `cuquantum-python-jax-cu12` for CUDA 12 and `cuquantum-python-jax-cu13` for CUDA 13.
51
+ - cuQuantum Python JAX is only supported with CUDA 12 and CUDA 13 and the distributions are CUDA-versioned: `cuquantum-python-jax-cu12` for CUDA 12 and `cuquantum-python-jax-cu13` for CUDA 13.
52
+ - **Build isolation note:** When installing with build isolation (`pip`'s default mode), the JAX version used at build time is determined by the build dependency specification in `pyproject.toml` and may differ from the JAX version pre-installed in the environment. Mismatched build-time and runtime JAX versions can lead to errors when importing cuQuantum's JAX extension. We recommend passing `--no-build-isolation` to `pip install` and pre-installing all build dependencies manually.
51
53
 
52
54
  #### Installation using `jax[cudaXX-local]`
53
55
 
@@ -101,10 +103,11 @@ The CUDA version is detected automatically from `$CUDA_PATH` and the wheel will
101
103
 
102
104
  Runtime dependencies of the cuQuantum Python JAX package include:
103
105
 
106
+ * Python 3.11+
104
107
  * An NVIDIA GPU with compute capability 7.5+
105
108
  * CUDA Toolkit 12.x or 13.x
106
- * cuquantum-python-cu12~=26.3.0 for CUDA 12 or cuquantum-python-cu13~=26.3.0 for CUDA 13
107
- * jax[cuda12-local]>=0.5,<0.7 for CUDA 12 or jax[cuda13-local]>=0.8,<0.9 for CUDA 13
109
+ * cuquantum-python-cu12~=26.9.0 for CUDA 12 or cuquantum-python-cu13~=26.9.0 for CUDA 13
110
+ * jax[cuda12-local]>=0.8 for CUDA 12 or jax[cuda13-local]>=0.8 for CUDA 13
108
111
 
109
112
  ## Developer Notes
110
113
 
@@ -1,6 +1,6 @@
1
1
  # cuQuantum Python JAX
2
2
 
3
- cuQuantum Python JAX provides a JAX extension for cuQuantum Python. It exposes selected functionality of cuQuantum SDK in a JAX-compatible way that enables JAX frameworks to directly interface with the exposed cuQuantum API. In the current release, cuQuantum JAX exposes a JAX interface to the Operator Action API from the cuDensityMat library and the GF(2) sparse-dense matrix multiply from the cuStabilizer library.
3
+ cuQuantum Python JAX provides a JAX extension for cuQuantum Python. It exposes selected functionalities of cuQuantum SDK in a JAX-compatible way that enables JAX frameworks to directly interface with the exposed cuQuantum API. In the current release, cuQuantum JAX exposes JAX interfaces to the Operator Action API from the cuDensityMat library and the GF(2) sparse-dense matrix multiplication from the cuStabilizer library.
4
4
 
5
5
  ## Documentation
6
6
 
@@ -12,15 +12,17 @@ Please visit the [NVIDIA cuQuantum Python documentation](https://docs.nvidia.com
12
12
 
13
13
  The build-time dependencies of the cuQuantum Python JAX package include:
14
14
 
15
+ * Python 3.11+
15
16
  * CUDA Toolkit 12.x or 13.x
16
- * jax[cuda12-local]>=0.5,<0.7 for CUDA 12 or jax[cuda13-local]>=0.8,<0.9 for CUDA 13
17
+ * cmake>=3.22
18
+ * jax[cuda12-local]>=0.8 for CUDA 12 or jax[cuda13-local]>=0.8 for CUDA 13
17
19
  * pybind11
18
20
  * wheel
19
21
  * setuptools>=77.0.3
20
22
 
21
23
  Note:
22
- - cuQuantum Python JAX is only supported with CUDA 12 and CUDA 13.
23
- - cuQuantum Python JAX wheels are CUDA-versioned: `cuquantum-python-jax-cu12` for CUDA 12 and `cuquantum-python-jax-cu13` for CUDA 13.
24
+ - cuQuantum Python JAX is only supported with CUDA 12 and CUDA 13 and the distributions are CUDA-versioned: `cuquantum-python-jax-cu12` for CUDA 12 and `cuquantum-python-jax-cu13` for CUDA 13.
25
+ - **Build isolation note:** When installing with build isolation (`pip`'s default mode), the JAX version used at build time is determined by the build dependency specification in `pyproject.toml` and may differ from the JAX version pre-installed in the environment. Mismatched build-time and runtime JAX versions can lead to errors when importing cuQuantum's JAX extension. We recommend passing `--no-build-isolation` to `pip install` and pre-installing all build dependencies manually.
24
26
 
25
27
  #### Installation using `jax[cudaXX-local]`
26
28
 
@@ -74,10 +76,11 @@ The CUDA version is detected automatically from `$CUDA_PATH` and the wheel will
74
76
 
75
77
  Runtime dependencies of the cuQuantum Python JAX package include:
76
78
 
79
+ * Python 3.11+
77
80
  * An NVIDIA GPU with compute capability 7.5+
78
81
  * CUDA Toolkit 12.x or 13.x
79
- * cuquantum-python-cu12~=26.3.0 for CUDA 12 or cuquantum-python-cu13~=26.3.0 for CUDA 13
80
- * jax[cuda12-local]>=0.5,<0.7 for CUDA 12 or jax[cuda13-local]>=0.8,<0.9 for CUDA 13
82
+ * cuquantum-python-cu12~=26.9.0 for CUDA 12 or cuquantum-python-cu13~=26.9.0 for CUDA 13
83
+ * jax[cuda12-local]>=0.8 for CUDA 12 or jax[cuda13-local]>=0.8 for CUDA 13
81
84
 
82
85
  ## Developer Notes
83
86
 
@@ -2,11 +2,6 @@
2
2
  #
3
3
  # SPDX-License-Identifier: BSD-3-Clause
4
4
 
5
- from cuquantum.bindings._internal import cudensitymat as _cudm
6
- _cudm._inspect_function_pointers() # for loading libcudensitymat.so
7
-
8
- import jax
9
-
10
5
  try:
11
6
  from ._build_info import check_jax_abi as _check_jax_abi
12
7
  except ImportError:
@@ -15,13 +10,11 @@ else:
15
10
  _check_jax_abi()
16
11
  del _check_jax_abi
17
12
 
18
- if not jax.config.jax_enable_x64:
19
- raise RuntimeError("jax_enable_x64 must be set to True to use cuQuantum Python JAX")
20
-
21
13
  from .operator_action import operator_action
22
14
  from .pysrc import (
23
15
  ElementaryOperator,
24
16
  MatrixOperator,
25
17
  OperatorTerm,
26
- Operator
18
+ Operator,
19
+ SimplifierConfig
27
20
  )
@@ -63,7 +63,7 @@
63
63
  // LIBRARY VERSION
64
64
 
65
65
  #define CUDENSITYMAT_MAJOR 0 //!< cuDensityMat major version.
66
- #define CUDENSITYMAT_MINOR 6 //!< cuDensityMat minor version.
66
+ #define CUDENSITYMAT_MINOR 7 //!< cuDensityMat minor version.
67
67
  #define CUDENSITYMAT_PATCH 0 //!< cuDensityMat patch version.
68
68
  #define CUDENSITYMAT_VERSION (CUDENSITYMAT_MAJOR * 10000 + CUDENSITYMAT_MINOR * 100 + CUDENSITYMAT_PATCH)
69
69
 
@@ -282,7 +282,8 @@ typedef enum
282
282
  */
283
283
  typedef enum
284
284
  {
285
- CUDENSITYMAT_EIGEN_SCOPE_SPLIT_DMRG = 0, ///< DMRG-based split decomposition (default)
285
+ CUDENSITYMAT_EIGEN_SCOPE_SPLIT_DMRG = 0, ///< DMRG-based split decomposition (default)
286
+ CUDENSITYMAT_EIGEN_SCOPE_SPLIT_SHIFT_INVERT_DMRG = 1, ///< Shift-invert DMRG split decomposition (eigenpair nearest a target energy)
286
287
  } cudensitymatEigenDecompositionScopeSplitKind_t;
287
288
 
288
289
  /**
@@ -298,8 +299,9 @@ typedef enum
298
299
  */
299
300
  typedef enum
300
301
  {
301
- CUDENSITYMAT_EIGEN_APPROACH_KRYLOV = 0, ///< Krylov subspace method
302
- //CUDENSITYMAT_EIGEN_APPROACH_DAVIDSON = 1, ///< Davidson method
302
+ CUDENSITYMAT_EIGEN_APPROACH_KRYLOV = 0, ///< Krylov subspace (block-Lanczos) eigensolver
303
+ CUDENSITYMAT_EIGEN_APPROACH_LINEAR = 1, ///< Iterative linear solver (currently MINRES)
304
+ //CUDENSITYMAT_EIGEN_APPROACH_DAVIDSON = 2, ///< Davidson method
303
305
  } cudensitymatEigenDecompositionApproachKind_t;
304
306
 
305
307
  /**
@@ -322,8 +324,9 @@ typedef enum
322
324
  CUDENSITYMAT_EIGEN_SPLIT_SCOPE_KIND = 0, ///< int32_t (cudensitymatEigenDecompositionScopeSplitKind_t): Split kind
323
325
  //CUDENSITYMAT_EIGEN_FULL_SCOPE_KIND = 1, ///< int32_t (cudensitymatEigenDecompositionScopeFullKind_t): Full-scope kind
324
326
  //CUDENSITYMAT_EIGEN_FULL_SCOPE_EXACT_CONFIG = 2, ///< cudensitymatEigenDecompositionScopeFullExactConfig_t: Full exact-scope configuration
325
- CUDENSITYMAT_EIGEN_SPLIT_SCOPE_DMRG_CONFIG = 3, ///< cudensitymatEigenDecompositionScopeSplitDMRGConfig_t: DMRG split configuration
327
+ CUDENSITYMAT_EIGEN_SPLIT_SCOPE_DMRG_CONFIG = 3, ///< cudensitymatEigenDecompositionScopeSplitDMRGConfig_t: DMRG split configuration (shared by the DMRG and shift-invert DMRG split kinds)
326
328
  CUDENSITYMAT_EIGEN_APPROACH_KRYLOV_CONFIG = 10, ///< cudensitymatEigenDecompositionApproachKrylovConfig_t: Krylov approach configuration
329
+ CUDENSITYMAT_EIGEN_APPROACH_LINEAR_CONFIG = 11, ///< cudensitymatEigenDecompositionApproachLinearConfig_t: Linear-solver approach configuration
327
330
  } cudensitymatEigenDecompositionAttribute_t;
328
331
 
329
332
  // /**
@@ -378,12 +381,21 @@ typedef enum
378
381
  */
379
382
  typedef enum
380
383
  {
381
- CUDENSITYMAT_EIGEN_SPLIT_SCOPE_DMRG_NUM_SITES = 0, ///< int32_t: Number of sites in the DMRG chain (default: 1)
382
- CUDENSITYMAT_EIGEN_SPLIT_SCOPE_DMRG_SVD_CONFIG = 1, ///< cudensitymatSVDConfig_t: SVD truncation configuration
383
- CUDENSITYMAT_EIGEN_SPLIT_SCOPE_DMRG_MAX_SWEEPS = 2, ///< int32_t: Maximum number of full L-R-L sweeps (default: 20)
384
- CUDENSITYMAT_EIGEN_SPLIT_SCOPE_DMRG_ENERGY_TOLERANCE = 3, ///< double: Convergence threshold on the change in variational energy between consecutive sweeps; the sweep loop terminates when |E_k - E_{k-1}| falls below this value (default: 1e-10)
384
+ CUDENSITYMAT_EIGEN_SPLIT_SCOPE_DMRG_NUM_SITES = 0, ///< int32_t: Number of neighboring MPS sites updated in each local solve (1 or 2; default: 1). SetAttribute returns CUDENSITYMAT_STATUS_INVALID_VALUE for values <= 0 and CUDENSITYMAT_STATUS_NOT_SUPPORTED for values > 2.
385
+ CUDENSITYMAT_EIGEN_SPLIT_SCOPE_DMRG_SVD_CONFIG = 1, ///< cudensitymatSVDConfig_t: SVD truncation configuration for 2-site updates
386
+ CUDENSITYMAT_EIGEN_SPLIT_SCOPE_DMRG_MAX_SWEEPS = 2, ///< int32_t: Maximum number of full L-R-L sweeps (default: 20); for shift-invert, the fitting-sweep cap for each inverse application
387
+ CUDENSITYMAT_EIGEN_SPLIT_SCOPE_DMRG_ENERGY_TOLERANCE = 3, ///< double: Convergence threshold on the change in energy between consecutive sweeps for ground-state DMRG or outer power iterations for shift-invert DMRG (default: 1e-10)
388
+ CUDENSITYMAT_EIGEN_SPLIT_SCOPE_DMRG_MAX_POWER_ITERATIONS = 4, ///< int32_t: Maximum number of outer power iterations on (H - sigma)^{-1} for the shift-invert DMRG split kind; ignored by the ground-state DMRG split kind (default: 10)
385
389
  } cudensitymatEigenDecompositionScopeSplitDMRGConfigAttribute_t;
386
390
 
391
+ /**
392
+ * \brief Configuration attributes for the iterative linear solver (currently MINRES).
393
+ */
394
+ typedef enum
395
+ {
396
+ CUDENSITYMAT_EIGEN_APPROACH_LINEAR_MAX_ITERATIONS = 0, ///< int32_t: Maximum number of iterations for the local linear solve (default: 200). The per-site solve convergence tolerance is supplied at Compute time via the in/out tolerances[] argument, not as a config attribute.
397
+ } cudensitymatEigenDecompositionApproachLinearConfigAttribute_t;
398
+
387
399
  /**
388
400
  * \brief State-fitting scope (full vs split fitting).
389
401
  */
@@ -500,7 +512,7 @@ typedef enum
500
512
  typedef enum
501
513
  {
502
514
  CUDENSITYMAT_EIGEN_SPECTRUM_LARGEST = 0, ///< Compute the largest by magnitude eigen-values of the operator
503
- CUDENSITYMAT_EIGEN_SPECTRUM_SMALLEST = 1, ///< Compute the smallest by magnitude eigen-values of the operator
515
+ CUDENSITYMAT_EIGEN_SPECTRUM_SMALLEST = 1, ///< Compute the smallest by magnitude eigenvalues; for shift-invert DMRG, select the smallest |E - sigma| using the Compute target
504
516
  CUDENSITYMAT_EIGEN_SPECTRUM_LARGEST_REAL = 2, ///< Compute the largest by the real part eigen-values of the operator
505
517
  CUDENSITYMAT_EIGEN_SPECTRUM_SMALLEST_REAL = 3, ///< Compute the smallest by the real part eigen-values of the operator
506
518
  } cudensitymatEigenDecompositionSpectrumKind_t;
@@ -685,6 +697,14 @@ typedef void * cudensitymatTimePropagationScopeSplitTDVPConfig_t;
685
697
  */
686
698
  typedef void * cudensitymatEigenDecompositionScopeSplitDMRGConfig_t;
687
699
 
700
+ /**
701
+ * \brief Opaque data structure holding the iterative linear-solver configuration.
702
+ *
703
+ * \details This configuration object stores the iteration cap for the iterative
704
+ * linear solver; the solve tolerance is supplied to Compute.
705
+ */
706
+ typedef void * cudensitymatEigenDecompositionApproachLinearConfig_t;
707
+
688
708
  /**
689
709
  * \brief Opaque data structure holding variational ALS configuration
690
710
  * for split-scope state fitting on a factorized (e.g., MPS) quantum state.
@@ -1631,7 +1651,8 @@ cudensitymatStatus_t cudensitymatStateComputeInnerProduct(
1631
1651
  * The full non-zero diagonals are stored in a concatenated form,
1632
1652
  * following the order how they appear in the `diagonalOffsets` argument.
1633
1653
  * The length of each stored diagonal is equal to the full matrix dimension,
1634
- * padded with trailing zeros for non-main diagonals.
1654
+ * padded with trailing zeros for non-main diagonals. Each non-zero diagonal
1655
+ * is stored exactly once, hence all diagonal offsets must be unique.
1635
1656
  *
1636
1657
  * \note Currently the multi-diagonal storage format is only supported
1637
1658
  * by 1-body elementary tensor operators (restriction subject to lifting in future).
@@ -1648,7 +1669,9 @@ cudensitymatStatus_t cudensitymatStateComputeInnerProduct(
1648
1669
  * \param[in] diagonalOffsets For multi-diagonal tensor operator matrices, these are
1649
1670
  * the offsets of the non-zero diagonals (for example, the main diagonal has offset 0,
1650
1671
  * the diagonal right above the main diagonal has offset +1, the diagonal right below
1651
- * the main diagonal has offset -1, and so on).
1672
+ * the main diagonal has offset -1, and so on). The diagonal offsets must be unique
1673
+ * (each non-zero diagonal is specified and stored exactly once); repeated offsets
1674
+ * are rejected.
1652
1675
  * \param[in] dataType Tensor operator data type.
1653
1676
  * \param[in] tensorData GPU-accessible pointer to the tensor operator elements storage.
1654
1677
  * \param[in] tensorCallback Optional user-defined tensor callback function
@@ -1703,7 +1726,8 @@ cudensitymatStatus_t cudensitymatCreateElementaryOperator(
1703
1726
  * The full non-zero diagonals are stored in a concatenated form,
1704
1727
  * following the order how they appear in the `diagonalOffsets` argument.
1705
1728
  * The length of each stored diagonal is equal to the full matrix dimension,
1706
- * padded with trailing zeros for non-main diagonals.
1729
+ * padded with trailing zeros for non-main diagonals. Each non-zero diagonal
1730
+ * is stored exactly once, hence all diagonal offsets must be unique.
1707
1731
  *
1708
1732
  * \note Currently the multi-diagonal storage format is only supported
1709
1733
  * by 1-body elementary tensor operators (restriction subject to lifting in future).
@@ -1724,6 +1748,8 @@ cudensitymatStatus_t cudensitymatCreateElementaryOperator(
1724
1748
  * \param[in] diagonalOffsets Offsets of the non-zero diagonals (for example,
1725
1749
  * the main diagonal has offset 0, the diagonal right above the main diagonal
1726
1750
  * has offset +1, the diagonal right below the main diagonal has offset -1, and so on).
1751
+ * The diagonal offsets must be unique (each non-zero diagonal is specified and
1752
+ * stored exactly once); repeated offsets are rejected.
1727
1753
  * \param[in] dataType Tensor operator data type.
1728
1754
  * \param[in] tensorData GPU-accessible pointer to the tensor operator elements storage,
1729
1755
  * where all elementary tensor operators within the batch are stored contiguously in memory.
@@ -3011,9 +3037,13 @@ cudensitymatStatus_t cudensitymatOperatorSpectrumPrepare(
3011
3037
  * of shape [numEigenStates, batchSize]) in GPU-accessible RAM (same data type
3012
3038
  * as used by the quantum state and operator).
3013
3039
  * \param[inout] tolerances Pointer to an F-order array of shape [numEigenStates, batchSize]
3014
- * in CPU-accessible RAM. The initial values represent the desirable convergence tolerances
3015
- * for all eigen-states. The returned values represent the actually achieved residual norms
3016
- * for all eigen-states.
3040
+ * in CPU-accessible RAM. On input, the desired solver convergence tolerances for all
3041
+ * eigen-states; on output, the actually achieved solver convergence residual norms (which
3042
+ * may differ from the requested values in either direction). These characterize solver
3043
+ * convergence, not the representation error of the returned state. For the full-state solver
3044
+ * they coincide with the eigenpair residual ||H x - E x||; for split (DMRG / shift-invert)
3045
+ * solvers the returned value is the local per-site solver residual, NOT the global
3046
+ * ||H|psi> - E|psi>||. The representation error is a separate quantity (dedicated query, when available).
3017
3047
  * \param[in] workspace Allocated workspace descriptor.
3018
3048
  * \param[in] stream CUDA stream.
3019
3049
  * \return cudensitymatStatus_t
@@ -3572,6 +3602,71 @@ cudensitymatStatus_t cudensitymatEigenDecompositionApproachKrylovConfigGetAttrib
3572
3602
  void * attributeValue,
3573
3603
  size_t attributeSize);
3574
3604
 
3605
+ // ============================================================================
3606
+ // Linear-Solver Configuration API (eigensolver)
3607
+ // ============================================================================
3608
+
3609
+ /**
3610
+ * \brief Creates an eigen-decomposition iterative linear-solver approach
3611
+ * configuration object with default settings.
3612
+ *
3613
+ * \param[in] handle Library handle.
3614
+ * \param[out] config Linear-solver configuration object.
3615
+ * \return cudensitymatStatus_t
3616
+ */
3617
+ cudensitymatStatus_t cudensitymatCreateEigenDecompositionApproachLinearConfig(
3618
+ const cudensitymatHandle_t handle,
3619
+ cudensitymatEigenDecompositionApproachLinearConfig_t * config);
3620
+
3621
+ /**
3622
+ * \brief Destroys an eigen-decomposition iterative linear-solver approach
3623
+ * configuration object.
3624
+ *
3625
+ * \param[in] config Linear-solver configuration object.
3626
+ * \return cudensitymatStatus_t
3627
+ */
3628
+ cudensitymatStatus_t cudensitymatDestroyEigenDecompositionApproachLinearConfig(cudensitymatEigenDecompositionApproachLinearConfig_t config);
3629
+
3630
+ /**
3631
+ * \brief Sets an attribute of the eigen-decomposition iterative linear-solver
3632
+ * approach configuration.
3633
+ *
3634
+ * \param[in] handle Library handle.
3635
+ * \param[in] config Linear-solver configuration object.
3636
+ * \param[in] attribute Attribute to set.
3637
+ * \param[in] attributeValue Pointer to the attribute value.
3638
+ * \param[in] attributeSize Size of the attribute value in bytes.
3639
+ * \return cudensitymatStatus_t
3640
+ *
3641
+ * \note The library captures the attribute by value at this call; the caller
3642
+ * retains ownership of `attributeValue` and may destroy the source as soon as
3643
+ * the call returns.
3644
+ */
3645
+ cudensitymatStatus_t cudensitymatEigenDecompositionApproachLinearConfigSetAttribute(
3646
+ const cudensitymatHandle_t handle,
3647
+ cudensitymatEigenDecompositionApproachLinearConfig_t config,
3648
+ cudensitymatEigenDecompositionApproachLinearConfigAttribute_t attribute,
3649
+ const void * attributeValue,
3650
+ size_t attributeSize);
3651
+
3652
+ /**
3653
+ * \brief Gets an attribute of the eigen-decomposition iterative linear-solver
3654
+ * approach configuration.
3655
+ *
3656
+ * \param[in] handle Library handle.
3657
+ * \param[in] config Linear-solver configuration object.
3658
+ * \param[in] attribute Attribute to get.
3659
+ * \param[out] attributeValue Pointer to store the attribute value.
3660
+ * \param[in] attributeSize Size of the buffer in bytes.
3661
+ * \return cudensitymatStatus_t
3662
+ */
3663
+ cudensitymatStatus_t cudensitymatEigenDecompositionApproachLinearConfigGetAttribute(
3664
+ const cudensitymatHandle_t handle,
3665
+ const cudensitymatEigenDecompositionApproachLinearConfig_t config,
3666
+ cudensitymatEigenDecompositionApproachLinearConfigAttribute_t attribute,
3667
+ void * attributeValue,
3668
+ size_t attributeSize);
3669
+
3575
3670
  // ============================================================================
3576
3671
  // Eigen Decomposition API
3577
3672
  // ============================================================================
@@ -3584,7 +3679,8 @@ cudensitymatStatus_t cudensitymatEigenDecompositionApproachKrylovConfigGetAttrib
3584
3679
  * \param[in] isHermitian Specifies whether the operator is Hermitian (!=0) or not (0).
3585
3680
  * \param[in] spectrumKind Requested kind of the eigen-spectrum computation.
3586
3681
  * \param[in] scopeKind Requested decomposition scope (full vs split).
3587
- * \param[in] approachKind Requested decomposition approach (e.g., Krylov).
3682
+ * \param[in] approachKind Requested decomposition approach (e.g., Krylov or
3683
+ * iterative linear solve).
3588
3684
  * \param[out] eigenDecomposition Eigen-decomposition computation object.
3589
3685
  * \return cudensitymatStatus_t
3590
3686
  *
@@ -3595,6 +3691,10 @@ cudensitymatStatus_t cudensitymatEigenDecompositionApproachKrylovConfigGetAttrib
3595
3691
  * `CUDENSITYMAT_STATUS_NOT_SUPPORTED`; only Hermitian operators are
3596
3692
  * supported in this release. The library trusts the caller's flag and does
3597
3693
  * not verify Hermiticity of the operator.
3694
+ *
3695
+ * \note In this release, an operator containing more than one MPO product,
3696
+ * or an MPO product containing more than one MPO, is rejected at Create with
3697
+ * `CUDENSITYMAT_STATUS_NOT_SUPPORTED`.
3598
3698
  */
3599
3699
  cudensitymatStatus_t cudensitymatCreateEigenDecomposition(
3600
3700
  const cudensitymatHandle_t handle,
@@ -3655,17 +3755,30 @@ cudensitymatStatus_t cudensitymatEigenDecompositionConfigure(
3655
3755
  * \param[in] stream CUDA stream.
3656
3756
  * \return cudensitymatStatus_t
3657
3757
  *
3758
+ * \note Prepare is transactional with respect to the cached eigen plan and
3759
+ * workspace requirements. If Prepare fails without an intervening successful
3760
+ * Configure, the last successfully prepared plan and workspace requirements
3761
+ * remain valid provided the generator, state geometry, and Context distribution
3762
+ * are unchanged. A successful Configure invalidates the prepared lifecycle, so
3763
+ * a subsequent failed Prepare leaves the object unprepared.
3764
+ *
3658
3765
  * \note In this release, Prepare returns `CUDENSITYMAT_STATUS_NOT_SUPPORTED`
3659
3766
  * when any of the following conditions is met:
3660
3767
  * (a) the `scopeKind` provided at Create is `CUDENSITYMAT_EIGEN_SCOPE_FULL`;
3661
3768
  * (b) the bound DMRG configuration has `CUDENSITYMAT_EIGEN_SPLIT_SCOPE_DMRG_NUM_SITES`
3662
3769
  * not in `{1, 2}` (only 1-site and 2-site DMRG are supported in this release);
3663
3770
  * (c) `maxEigenStates != 1` (only a single eigen-pair is supported in this release);
3664
- * (d) the `spectrumKind` provided at Create is not
3665
- * `CUDENSITYMAT_EIGEN_SPECTRUM_SMALLEST_REAL` (only the smallest-real
3666
- * eigenvalue is supported by the DMRG engine in this release);
3771
+ * (d) the selected split/approach/spectrum combination is neither
3772
+ * `SPLIT_DMRG + KRYLOV + SMALLEST_REAL` nor
3773
+ * `SPLIT_SHIFT_INVERT_DMRG + LINEAR + SMALLEST`;
3667
3774
  * (e) the representative `state` is a single-site (1-mode) MPS (an MPS
3668
- * with at least two sites is required by the DMRG engine).
3775
+ * with at least two sites is required by the DMRG engine);
3776
+ * (f) the state data type is not `CUDA_R_64F` or `CUDA_C_64F`, or
3777
+ * `computeType` is not `CUDENSITYMAT_COMPUTE_64F`; or
3778
+ * (g) shift-invert DMRG is configured with `NUM_SITES == 2` for an MPS
3779
+ * containing exactly two tensors. This last restriction is a two-site
3780
+ * ProjectionMPS dependency limitation: `NUM_SITES == 1` remains supported
3781
+ * for a two-tensor MPS.
3669
3782
  */
3670
3783
  cudensitymatStatus_t cudensitymatEigenDecompositionPrepare(
3671
3784
  const cudensitymatHandle_t handle,
@@ -3697,13 +3810,25 @@ cudensitymatStatus_t cudensitymatEigenDecompositionPrepare(
3697
3810
  * \param[inout] eigenstates Quantum eigenstates.
3698
3811
  * The initial values of the provided quantum states will be used as the
3699
3812
  * initial guesses for the iterative solver.
3700
- * \param[out] eigenvalues Pointer to the eigenvalues storage (F-order array
3813
+ * \param[inout] eigenvalues Pointer to the eigenvalues storage (F-order array
3701
3814
  * of shape [numEigenStates, batchSize]) in GPU-accessible RAM (same data type
3702
- * as used by the quantum state and operator).
3815
+ * as used by the quantum state and operator). For the shift-invert DMRG split kind,
3816
+ * the input value is the target energy sigma (the returned eigenpair is the one whose
3817
+ * eigenvalue is nearest sigma); it is ignored on input by other split kinds/approaches.
3818
+ * On output, holds the computed eigenvalue(s).
3703
3819
  * \param[inout] tolerances Pointer to an F-order array of shape [numEigenStates, batchSize]
3704
- * in CPU-accessible RAM. The initial values represent the desirable convergence tolerances
3705
- * for all eigen-states. The returned values represent the actually achieved residual norms
3706
- * for all eigen-states.
3820
+ * in CPU-accessible RAM. On input, the desired solver convergence tolerances for all
3821
+ * eigen-states; on output, the actually achieved solver convergence residual norms (which
3822
+ * may differ from the requested values in either direction). These characterize solver
3823
+ * convergence, not the representation error of the returned state. For the full-state solver
3824
+ * they coincide with the eigenpair residual ||H x - E x||; for split (DMRG / shift-invert)
3825
+ * solvers the returned value is the local per-site solver residual, NOT the global
3826
+ * ||H|psi> - E|psi>||. The representation error is a separate quantity (dedicated query, when available).
3827
+ * For shift-invert DMRG specifically, the input value is the requested per-site
3828
+ * relative normal-equation tolerance. On output it is the maximum achieved absolute
3829
+ * post-insertion (and, for 2-site updates, post-truncation) local normal-equation
3830
+ * residual over every region in the final complete fitting sweep of the final outer
3831
+ * iteration. It is not the achieved outer energy-difference metric.
3707
3832
  * \param[in] workspace Allocated workspace descriptor.
3708
3833
  * \param[in] stream CUDA stream.
3709
3834
  * \return cudensitymatStatus_t
@@ -3714,10 +3839,25 @@ cudensitymatStatus_t cudensitymatEigenDecompositionPrepare(
3714
3839
  * \note `numEigenStates != 1` returns `CUDENSITYMAT_STATUS_NOT_SUPPORTED`
3715
3840
  * in this release; only a single eigen-pair is supported.
3716
3841
  *
3842
+ * \note `batchSize < 1` returns `CUDENSITYMAT_STATUS_INVALID_VALUE`;
3843
+ * `batchSize > 1` returns `CUDENSITYMAT_STATUS_NOT_SUPPORTED`.
3844
+ *
3845
+ * \note `handle` and its distributed configuration must match the last
3846
+ * successful Prepare. After changing either, call Prepare again before Compute.
3847
+ *
3717
3848
  * \note The eigenvalue data type matches the state's data type (i.e., the
3718
3849
  * complex type used for the input `eigenstates`). For Hermitian operators
3719
3850
  * the real eigenvalue is written into the complex slot with `imag = 0`.
3720
3851
  *
3852
+ * \note Shift-invert convergence requires finite real sigma, d1 > 0, and
3853
+ * d1 < d2, where d1 and d2 are the nearest distinct-eigenvalue distances.
3854
+ * A degenerate nearest eigenspace is allowed; exact and equidistant targets
3855
+ * are not detected or detuned.
3856
+ *
3857
+ * \note Cap exhaustion returns `CUDENSITYMAT_STATUS_SUCCESS` with the final
3858
+ * normalized approximation, Rayleigh quotient, and final-sweep local residual;
3859
+ * these outputs do not certify outer or global convergence.
3860
+ *
3721
3861
  * \note The input `eigenstates` are used as initial guesses for the iterative
3722
3862
  * solver. The library does not canonicalize them; the caller is responsible
3723
3863
  * for providing a linearly-independent (and ideally well-conditioned) set.
@@ -8,8 +8,6 @@ from collections.abc import Sequence
8
8
 
9
9
  import jax
10
10
 
11
- from cuquantum.lib.cudensitymat_jax import InputType
12
-
13
11
  from .pysrc.context import CudensitymatContext
14
12
  from .pysrc.operator import Operator
15
13
  from .pysrc.operator_action_prim import (
@@ -24,14 +22,33 @@ from .utils import (
24
22
  check_and_return_final_batch_size,
25
23
  check_and_return_device,
26
24
  get_original_shape,
27
- get_vmap_depth,
28
- is_grad_inside_vmap,
25
+ is_vmap_traced,
29
26
  )
30
27
 
31
28
 
32
29
  logger = logging.getLogger("cudensitymat-jax.operator_action")
33
30
 
34
31
 
32
+ _ffi_registered = False
33
+ _InputType = None
34
+
35
+
36
+ def _register_ffi_targets():
37
+ global _ffi_registered, _InputType
38
+ if _ffi_registered:
39
+ return
40
+
41
+ from cuquantum.bindings._internal import cudensitymat as _cudm
42
+ _cudm._inspect_function_pointers() # for loading libcudensitymat.so
43
+
44
+ from cuquantum.lib import cudensitymat_jax
45
+ _InputType = cudensitymat_jax.InputType
46
+
47
+ for _name, _value in cudensitymat_jax.registrations().items():
48
+ jax.ffi.register_ffi_target(_name, _value, platform="CUDA")
49
+ _ffi_registered = True
50
+
51
+
35
52
  def operator_action(op: Operator,
36
53
  state_in_bufs: jax.Array | Sequence[jax.Array],
37
54
  device: jax.Device | None = None,
@@ -49,20 +66,17 @@ def operator_action(op: Operator,
49
66
  """
50
67
  logger.info("Calling operator_action")
51
68
 
69
+ _register_ffi_targets()
70
+
71
+ if not jax.config.jax_enable_x64:
72
+ raise RuntimeError("jax_enable_x64 must be set to True to use cuQuantum Python JAX")
73
+
52
74
  # Process input arguments.
53
75
  if isinstance(state_in_bufs, jax.Array):
54
76
  state_in_bufs = (state_in_bufs,)
55
77
  else:
56
78
  state_in_bufs = tuple(state_in_bufs)
57
79
 
58
- # Guard against nested vmap transformations, which are not supported.
59
- if get_vmap_depth(state_in_bufs[0]) > 1:
60
- raise NotImplementedError("operator_action does not support nested vmap transformations.")
61
-
62
- # Guard against grad applied inside vmap, which is not supported.
63
- if is_grad_inside_vmap(state_in_bufs[0]):
64
- raise NotImplementedError("operator_action does not support grad transformations inside vmap.")
65
-
66
80
  # Check and set device from op and state.
67
81
  device = check_and_return_device(op, state_in_bufs)
68
82
 
@@ -74,12 +88,23 @@ def operator_action(op: Operator,
74
88
  device = devices[0]
75
89
  logger.info("No device specified, using the first GPU device.")
76
90
 
77
- # Check state shape and maybe expand to a leading batch dimension.
78
- state_batch_size, purity = get_state_batch_size_and_purity(state_in_bufs, len(op.dims))
79
- batch_size = check_and_return_final_batch_size(state_in_bufs, state_batch_size, op.batch_size)
91
+ if op._ptr is None:
92
+ op._simplify()
93
+ op._update_metadata() # update batch sizes
80
94
 
81
- state_in_bufs, did_expand = maybe_expand_dim(state_in_bufs, len(op.dims))
82
- state_shape = get_original_shape(state_in_bufs[0])
95
+ # Check state shape and maybe expand to a leading batch dimension.
96
+ state_batch_size, purity, has_explicit_batch = get_state_batch_size_and_purity(
97
+ state_in_bufs, op.dims)
98
+ batch_size = check_and_return_final_batch_size(state_batch_size, op._batch_size)
99
+
100
+ state_in_bufs, did_expand = maybe_expand_dim(state_in_bufs, has_explicit_batch)
101
+ # For nested vmap, get_original_shape would return a multi-dim batch prefix
102
+ # (outer_B, inner_B, *physical), which breaks maybe_create_state_context's
103
+ # single-leading-batch assumption. Fuse all batch levels into one leading dim.
104
+ if is_vmap_traced(state_in_bufs[0]):
105
+ state_shape = (batch_size, *state_in_bufs[0].shape)
106
+ else:
107
+ state_shape = tuple(state_in_bufs[0].shape) # already has batch from maybe_expand_dim
83
108
 
84
109
  # Prepare library context for forward operator action.
85
110
  # NOTE: Assuming a single state component.
@@ -117,19 +142,19 @@ def operator_action(op: Operator,
117
142
  is_op_term_coeff_batched = op._op_term_batch_sizes[i] != 1
118
143
  if is_op_term_coeff_batched:
119
144
  dynamic_ptr = op_term_total_coeffs_ptr
120
- dynamic_type = InputType.OPERATOR_TERM_BATCHED_COEFFS.value
145
+ dynamic_type = _InputType.OPERATOR_TERM_BATCHED_COEFFS.value
121
146
  if dynamic_ptr == 0:
122
147
  raise RuntimeError("Missing total coefficient pointer for batched operator term coefficient.")
123
148
  else:
124
149
  dynamic_ptr = op_term_coeff_ptr
125
- dynamic_type = InputType.NON_BATCHED_COEFFS.value
150
+ dynamic_type = _InputType.NON_BATCHED_COEFFS.value
126
151
 
127
152
  if dynamic_ptr not in op_term_coeff_metadata.ptrs:
128
153
  op_term_coeff_metadata.indices.append(i)
129
154
  op_term_coeff_metadata.types.append(dynamic_type)
130
155
  op_term_coeff_metadata.ptrs.append(dynamic_ptr)
131
156
 
132
- if op_term_coeff_grad_ptr != 0:
157
+ if op_term_coeff_grad_ptr != 0 and op_term_coeff_grad_ptr not in op_term_coeff_grad_metadata.ptrs:
133
158
  op_term_coeff_grad_metadata.indices.append(i)
134
159
  op_term_coeff_grad_metadata.ptrs.append(op_term_coeff_grad_ptr)
135
160
  # The gradient buffer is always sized (batch_size,) regardless of whether the
@@ -155,19 +180,19 @@ def operator_action(op: Operator,
155
180
  is_op_prod_coeff_batched = op_term._op_prod_batch_sizes[j] != 1
156
181
  if is_op_prod_coeff_batched:
157
182
  dynamic_ptr = op_prod_total_coeffs_ptr
158
- dynamic_type = InputType.OPERATOR_PRODUCT_BATCHED_COEFFS.value
183
+ dynamic_type = _InputType.OPERATOR_PRODUCT_BATCHED_COEFFS.value
159
184
  if dynamic_ptr == 0:
160
185
  raise RuntimeError("Missing total coefficient pointer for batched operator product coefficient.")
161
186
  else:
162
187
  dynamic_ptr = op_prod_coeff_ptr
163
- dynamic_type = InputType.NON_BATCHED_COEFFS.value
188
+ dynamic_type = _InputType.NON_BATCHED_COEFFS.value
164
189
 
165
190
  if dynamic_ptr not in op_prod_coeff_metadata.ptrs:
166
191
  op_prod_coeff_metadata.indices.append((i, j))
167
192
  op_prod_coeff_metadata.types.append(dynamic_type)
168
193
  op_prod_coeff_metadata.ptrs.append(dynamic_ptr)
169
194
 
170
- if op_prod_coeff_grad_ptr != 0:
195
+ if op_prod_coeff_grad_ptr != 0 and op_prod_coeff_grad_ptr not in op_prod_coeff_grad_metadata.ptrs:
171
196
  op_prod_coeff_grad_metadata.indices.append((i, j))
172
197
  op_prod_coeff_grad_metadata.ptrs.append(op_prod_coeff_grad_ptr)
173
198
  # The gradient buffer is always sized (batch_size,) regardless of whether the
@@ -182,8 +207,8 @@ def operator_action(op: Operator,
182
207
  if base_op._ptr is not None and base_op._ptr not in base_op_metadata.ptrs:
183
208
  base_op_metadata.indices.append((i, j, k))
184
209
  base_op_metadata.types.append(
185
- InputType.ELEMENTARY_OPERATOR.value if base_op._is_elementary
186
- else InputType.MATRIX_OPERATOR.value
210
+ _InputType.ELEMENTARY_OPERATOR.value if base_op._is_elementary
211
+ else _InputType.MATRIX_OPERATOR.value
187
212
  )
188
213
  base_op_metadata.ptrs.append(base_op._ptr)
189
214
 
@@ -191,12 +216,18 @@ def operator_action(op: Operator,
191
216
  base_op_grad_metadata.indices.append((i, j, k))
192
217
  base_op_grad_metadata.ptrs.append(base_op._grad_ptr)
193
218
  data_shape = get_original_shape(base_op.data)
194
- if base_op.batch_size == 1:
195
- # Non-batched base op: cudensitymat writes batch_size gradient tensors
219
+ if is_vmap_traced(base_op.data) or base_op._batch_size > 1:
220
+ # data_shape already includes the batch dim: either fused in by
221
+ # get_original_shape for a vmap trace (at any extent, including a
222
+ # size-1 vmap axis), or baked into the concrete array's own shape
223
+ # for materialized (non-vmap) batching, where _batch_size > 1 is a
224
+ # rank-verified presence signal (elementary_operator.py's
225
+ # `ndim % 2 == 1` check), not an extent guess.
226
+ grad_shape = data_shape
227
+ else:
228
+ # Genuinely unbatched: cudensitymat writes batch_size gradient tensors
196
229
  # with batch as the last dimension, so buffer has (batch_size, *data_shape).
197
230
  grad_shape = (batch_size, *data_shape)
198
- else:
199
- grad_shape = data_shape # already includes batch dim
200
231
  shape_dtype = jax.ShapeDtypeStruct(grad_shape, base_op.data.dtype)
201
232
  base_op_grad_metadata.shape_dtypes.append(shape_dtype)
202
233
 
@@ -228,7 +259,7 @@ def operator_action(op: Operator,
228
259
  )
229
260
 
230
261
  # Undo the leading batch dim when it was added by maybe_expand_dim (single-state, non-vmap).
231
- state_out_bufs = maybe_squeeze_dim(state_out_bufs, len(op.dims), did_expand)
262
+ state_out_bufs = maybe_squeeze_dim(state_out_bufs, did_expand)
232
263
 
233
264
  # Process output argument.
234
265
  if len(state_out_bufs) == 1: