cuquantum-python-cu13 25.9.0__cp312-cp312-manylinux2014_aarch64.whl

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 (79) hide show
  1. cuquantum/__init__.py +10 -0
  2. cuquantum/__main__.py +128 -0
  3. cuquantum/_internal/__init__.py +3 -0
  4. cuquantum/_internal/package_ifc.py +91 -0
  5. cuquantum/_internal/tensor_ifc.py +88 -0
  6. cuquantum/_internal/tensor_ifc_cupy.py +126 -0
  7. cuquantum/_internal/tensor_ifc_numpy.py +105 -0
  8. cuquantum/_internal/tensor_ifc_torch.py +104 -0
  9. cuquantum/_internal/tensor_wrapper.py +112 -0
  10. cuquantum/_internal/utils.py +202 -0
  11. cuquantum/_version.py +8 -0
  12. cuquantum/bindings/__init__.pxd +3 -0
  13. cuquantum/bindings/__init__.py +13 -0
  14. cuquantum/bindings/_internal/cudensitymat.cpython-312-aarch64-linux-gnu.so +0 -0
  15. cuquantum/bindings/_internal/custatevec.cpython-312-aarch64-linux-gnu.so +0 -0
  16. cuquantum/bindings/_internal/cutensornet.cpython-312-aarch64-linux-gnu.so +0 -0
  17. cuquantum/bindings/_utils.cpython-312-aarch64-linux-gnu.so +0 -0
  18. cuquantum/bindings/_utils.pxd +187 -0
  19. cuquantum/bindings/_utils.pyx +271 -0
  20. cuquantum/bindings/cudensitymat.cpython-312-aarch64-linux-gnu.so +0 -0
  21. cuquantum/bindings/cudensitymat.pxd +148 -0
  22. cuquantum/bindings/cudensitymat.pyx +1890 -0
  23. cuquantum/bindings/custatevec.cpython-312-aarch64-linux-gnu.so +0 -0
  24. cuquantum/bindings/custatevec.pxd +122 -0
  25. cuquantum/bindings/custatevec.pyx +2489 -0
  26. cuquantum/bindings/cutensornet.cpython-312-aarch64-linux-gnu.so +0 -0
  27. cuquantum/bindings/cutensornet.pxd +243 -0
  28. cuquantum/bindings/cutensornet.pyx +4157 -0
  29. cuquantum/bindings/cycudensitymat.cpython-312-aarch64-linux-gnu.so +0 -0
  30. cuquantum/bindings/cycudensitymat.pxd +288 -0
  31. cuquantum/bindings/cycudensitymat.pyx +243 -0
  32. cuquantum/bindings/cycustatevec.cpython-312-aarch64-linux-gnu.so +0 -0
  33. cuquantum/bindings/cycustatevec.pxd +236 -0
  34. cuquantum/bindings/cycustatevec.pyx +311 -0
  35. cuquantum/bindings/cycutensornet.cpython-312-aarch64-linux-gnu.so +0 -0
  36. cuquantum/bindings/cycutensornet.pxd +492 -0
  37. cuquantum/bindings/cycutensornet.pyx +579 -0
  38. cuquantum/densitymat/__init__.py +11 -0
  39. cuquantum/densitymat/_internal/__init__.py +3 -0
  40. cuquantum/densitymat/_internal/callbacks.py +405 -0
  41. cuquantum/densitymat/_internal/library_handle.py +152 -0
  42. cuquantum/densitymat/_internal/typemaps.py +8 -0
  43. cuquantum/densitymat/_internal/utils.py +201 -0
  44. cuquantum/densitymat/callbacks.py +341 -0
  45. cuquantum/densitymat/elementary_operator.py +892 -0
  46. cuquantum/densitymat/matrix_operator.py +228 -0
  47. cuquantum/densitymat/memory.py +171 -0
  48. cuquantum/densitymat/operators.py +1714 -0
  49. cuquantum/densitymat/spectrum.py +405 -0
  50. cuquantum/densitymat/state.py +614 -0
  51. cuquantum/densitymat/work_stream.py +362 -0
  52. cuquantum/memory.py +47 -0
  53. cuquantum/tensornet/__init__.py +11 -0
  54. cuquantum/tensornet/_internal/__init__.py +3 -0
  55. cuquantum/tensornet/_internal/circuit_converter_utils.py +263 -0
  56. cuquantum/tensornet/_internal/circuit_parser_utils_cirq.py +93 -0
  57. cuquantum/tensornet/_internal/circuit_parser_utils_qiskit.py +215 -0
  58. cuquantum/tensornet/_internal/decomposition_utils.py +493 -0
  59. cuquantum/tensornet/_internal/einsum_parser.py +384 -0
  60. cuquantum/tensornet/_internal/grad_torch.py +84 -0
  61. cuquantum/tensornet/_internal/helpers.py +146 -0
  62. cuquantum/tensornet/_internal/optimizer_ifc.py +244 -0
  63. cuquantum/tensornet/circuit_converter.py +417 -0
  64. cuquantum/tensornet/configuration.py +215 -0
  65. cuquantum/tensornet/experimental/__init__.py +8 -0
  66. cuquantum/tensornet/experimental/_internal/__init__.py +3 -0
  67. cuquantum/tensornet/experimental/_internal/network_state_utils.py +228 -0
  68. cuquantum/tensornet/experimental/_internal/utils.py +53 -0
  69. cuquantum/tensornet/experimental/configuration.py +207 -0
  70. cuquantum/tensornet/experimental/network_operator.py +237 -0
  71. cuquantum/tensornet/experimental/network_state.py +1312 -0
  72. cuquantum/tensornet/experimental/tensor_network.py +408 -0
  73. cuquantum/tensornet/tensor.py +535 -0
  74. cuquantum/tensornet/tensor_network.py +1675 -0
  75. cuquantum_python_cu13-25.9.0.dist-info/METADATA +158 -0
  76. cuquantum_python_cu13-25.9.0.dist-info/RECORD +79 -0
  77. cuquantum_python_cu13-25.9.0.dist-info/WHEEL +5 -0
  78. cuquantum_python_cu13-25.9.0.dist-info/licenses/LICENSE +28 -0
  79. cuquantum_python_cu13-25.9.0.dist-info/top_level.txt +1 -0
cuquantum/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ # Copyright (c) 2021-2025, NVIDIA CORPORATION & AFFILIATES
2
+ #
3
+ # SPDX-License-Identifier: BSD-3-Clause
4
+
5
+ from cuquantum import bindings
6
+ from cuquantum import densitymat
7
+ from cuquantum import tensornet
8
+ from cuquantum.memory import *
9
+ from cuquantum.bindings._utils import ComputeType, cudaDataType, libraryPropertyType
10
+ from cuquantum._version import __version__
cuquantum/__main__.py ADDED
@@ -0,0 +1,128 @@
1
+ # Copyright (c) 2021-2025, NVIDIA CORPORATION & AFFILIATES
2
+ #
3
+ # SPDX-License-Identifier: BSD-3-Clause
4
+
5
+ import argparse
6
+ import os
7
+ import site
8
+ import sys
9
+ import logging
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ def get_lib_path(name):
14
+ """Get the loaded shared library path."""
15
+ # Ideally we should call dl_iterate_phdr or dladdr to do the job, but this
16
+ # is simpler and not bad; the former two are not strictly portable anyway
17
+ # (not part of POSIX). Obviously this only works on Linux!
18
+
19
+ # We have switched to use dlopen, force library loading via internal API
20
+ if "custatevec" in name:
21
+ from cuquantum import bindings
22
+ bindings._internal.custatevec._inspect_function_pointers()
23
+ elif "cutensor" in name: # cutensor or cutensornet
24
+ from cuquantum import bindings
25
+ bindings._internal.cutensornet._inspect_function_pointers()
26
+ elif "cudensitymat" in name:
27
+ from cuquantum import bindings
28
+ bindings._internal.cudensitymat._inspect_function_pointers()
29
+
30
+ try:
31
+ with open('/proc/self/maps') as f:
32
+ lib_map = f.read()
33
+ except FileNotFoundError as e:
34
+ raise NotImplementedError("This utility is available only on Linux.") from e
35
+ lib = set()
36
+ for line in lib_map.split('\n'):
37
+ if name in line:
38
+ fields = line.split()
39
+ lib.add(fields[-1]) # pathname is the last field, check "man proc"
40
+ if len(lib) == 0:
41
+ raise ValueError(f"library {name} is not loaded")
42
+ elif len(lib) > 1:
43
+ # This could happen when, e.g., a library exists in both the user env
44
+ # and LD_LIBRARY_PATH, and somehow both copies get loaded. This is a
45
+ # messy problem, but let's work around it by assuming the one in the
46
+ # user env is preferred.
47
+ lib2 = set()
48
+ for s in [site.getusersitepackages()] + site.getsitepackages():
49
+ for path in lib:
50
+ if path.startswith(s):
51
+ lib2.add(path)
52
+ if len(lib2) != 1:
53
+ raise RuntimeError(f"cannot find the unique copy of {name}: {lib}")
54
+ else:
55
+ lib = lib2
56
+ return lib.pop()
57
+
58
+
59
+ def _get_cuquantum_lib(lib):
60
+ try:
61
+ path = os.path.normpath(get_lib_path(f"lib{lib}.so"))
62
+ return path
63
+ except Exception as e:
64
+ logger.warning(f"library {lib} not found: {e}")
65
+ return None
66
+
67
+
68
+ def _get_cuquantum_libs():
69
+ paths = set()
70
+ for lib in ('custatevec', 'cutensornet', 'cutensor', 'cudensitymat'):
71
+ path = _get_cuquantum_lib(lib)
72
+ if path is not None:
73
+ paths.add(path)
74
+ return tuple(paths)
75
+
76
+
77
+ def _get_cuquantum_includes():
78
+ paths = set()
79
+ for path in _get_cuquantum_libs():
80
+ path = os.path.normpath(os.path.join(os.path.dirname(path), '..'))
81
+ if not os.path.isdir(os.path.join(path, 'include')):
82
+ path = os.path.normpath(os.path.join(path, '../include'))
83
+ else:
84
+ path = os.path.join(path, 'include')
85
+
86
+ if os.path.isdir(path):
87
+ paths.add(path)
88
+ return tuple(paths)
89
+
90
+
91
+ def _get_cuquantum_target(target):
92
+ path = _get_cuquantum_lib(target)
93
+ if path is None:
94
+ # Return error as the target library was explicitly asked but not found
95
+ raise RuntimeError(f"library {target} not found")
96
+ lib = os.path.basename(path)
97
+ lib = '.'.join(lib.split('.')[:3]) # keep SONAME
98
+ flag = f"-l:{lib} "
99
+ return flag
100
+
101
+
102
+ if __name__ == '__main__':
103
+ parser = argparse.ArgumentParser()
104
+ parser.add_argument('--includes', action='store_true',
105
+ help='get cuQuantum include flags')
106
+ parser.add_argument('--libs', action='store_true',
107
+ help='get cuQuantum linker flags')
108
+ parser.add_argument('--target', action='append', default=[],
109
+ choices=('custatevec', 'cutensornet', 'cudensitymat'),
110
+ help='get the linker flag for the target cuQuantum component')
111
+ args = parser.parse_args()
112
+
113
+ if not sys.argv[1:]:
114
+ parser.print_help()
115
+ sys.exit(1)
116
+ if args.includes:
117
+ out = ' '.join(f"-I{path}" for path in _get_cuquantum_includes())
118
+ print(out, end=' ')
119
+ if args.libs:
120
+ paths = set([os.path.dirname(path) for path in _get_cuquantum_libs()])
121
+ out = ' '.join(f"-L{path}" for path in paths)
122
+ print(out, end=' ')
123
+ flag = ''
124
+ for target in args.target:
125
+ flag += _get_cuquantum_target(target)
126
+ if target in ('cutensornet', 'cudensitymat') :
127
+ flag += _get_cuquantum_target('cutensor')
128
+ print(flag)
@@ -0,0 +1,3 @@
1
+ # Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES
2
+ #
3
+ # SPDX-License-Identifier: BSD-3-Clause
@@ -0,0 +1,91 @@
1
+ # Copyright (c) 2021-2023, NVIDIA CORPORATION & AFFILIATES
2
+ #
3
+ # SPDX-License-Identifier: BSD-3-Clause
4
+
5
+ """
6
+ An abstract interface to certain package-provided operations.
7
+ """
8
+
9
+ __all__ = ['Package']
10
+
11
+ from abc import ABC, abstractmethod
12
+ from dataclasses import dataclass
13
+ from contextlib import nullcontext
14
+ from typing import Any
15
+
16
+
17
+ class Package(ABC):
18
+
19
+ @staticmethod
20
+ @abstractmethod
21
+ def get_current_stream(device_id):
22
+ """
23
+ Obtain the current stream on the device.
24
+
25
+ Args:
26
+ device_id: The id (ordinal) of the device.
27
+ """
28
+ raise NotImplementedError
29
+
30
+ @staticmethod
31
+ @abstractmethod
32
+ def to_stream_pointer(stream):
33
+ """
34
+ Obtain the stream pointer.
35
+
36
+ Args:
37
+ stream: The stream object.
38
+ """
39
+ raise NotImplementedError
40
+
41
+ @staticmethod
42
+ @abstractmethod
43
+ def to_stream_context(stream):
44
+ """
45
+ Create a context manager from the stream.
46
+
47
+ Args:
48
+ stream: The stream object.
49
+ """
50
+ raise NotImplementedError
51
+
52
+ @staticmethod
53
+ @abstractmethod
54
+ def create_external_stream(device_id, stream_ptr):
55
+ """
56
+ Wrap a stream pointer into an external stream object.
57
+
58
+ Args:
59
+ device_id: The id (ordinal) of the device.
60
+ stream: The stream pointer (int) to be wrapped.
61
+ """
62
+ raise NotImplementedError
63
+
64
+ @staticmethod
65
+ @abstractmethod
66
+ def create_stream(device_id):
67
+ """
68
+ Create a new stream on the specified device.
69
+
70
+ Args:
71
+ device_id: The id (ordinal) of the device.
72
+ """
73
+ raise NotImplementedError
74
+
75
+
76
+ @dataclass
77
+ class StreamHolder:
78
+ """A data class for easing CUDA stream manipulation.
79
+
80
+ Attributes:
81
+ ctx: A context manager for using the specified stream.
82
+ device_id (int): The device ID where the encapsulated stream locates.
83
+ obj: A foreign object that holds the stream alive.
84
+ package (str):
85
+ ptr (int): The address of the underlying ``cudaStream_t`` object.
86
+ """
87
+ ctx: Any = nullcontext()
88
+ device_id: int = -2
89
+ obj: Any = None
90
+ package: str = ""
91
+ ptr: int = 0
@@ -0,0 +1,88 @@
1
+ # Copyright (c) 2021-2024, NVIDIA CORPORATION & AFFILIATES
2
+ #
3
+ # SPDX-License-Identifier: BSD-3-Clause
4
+
5
+ """
6
+ Interface to seamlessly use tensors (or ndarray-like objects) from different libraries.
7
+ """
8
+
9
+ from abc import ABC, abstractmethod
10
+
11
+ from nvmath.internal import typemaps
12
+
13
+ class Tensor(ABC):
14
+ """
15
+ A simple wrapper type for tensors to make the API package-agnostic.
16
+ """
17
+
18
+ def __init__(self, tensor):
19
+ self.tensor = tensor
20
+
21
+ @property
22
+ @abstractmethod
23
+ def data_ptr(self):
24
+ raise NotImplementedError
25
+
26
+ @property
27
+ @abstractmethod
28
+ def device(self):
29
+ raise NotImplementedError
30
+
31
+ @property
32
+ @abstractmethod
33
+ def device_id(self):
34
+ raise NotImplementedError
35
+
36
+ @property
37
+ @abstractmethod
38
+ def dtype(self):
39
+ raise NotImplementedError
40
+
41
+ @classmethod
42
+ @abstractmethod
43
+ def empty(cls, shape, **context):
44
+ raise NotImplementedError
45
+
46
+ @abstractmethod
47
+ def numpy(self, stream_holder):
48
+ raise NotImplementedError
49
+
50
+ @property
51
+ @abstractmethod
52
+ def shape(self):
53
+ raise NotImplementedError
54
+
55
+ @property
56
+ def ndim(self):
57
+ return len(self.shape)
58
+
59
+ @property
60
+ def T(self):
61
+ return self.__class__(self.tensor.T)
62
+
63
+ @property
64
+ @abstractmethod
65
+ def strides(self):
66
+ raise NotImplementedError
67
+
68
+ @abstractmethod
69
+ def to(self, device='cpu', stream_holder=None):
70
+ raise NotImplementedError
71
+
72
+ @abstractmethod
73
+ def copy_(self, src, stream_holder=None):
74
+ raise NotImplementedError
75
+
76
+ @staticmethod
77
+ def create_name_dtype_map(conversion_function, exception_type):
78
+ """
79
+ Create a map between CUDA data type names and the corresponding package dtypes for supported data types.
80
+ """
81
+ names = typemaps.NAME_TO_DATA_TYPE.keys()
82
+ name_to_dtype = dict()
83
+ for name in names:
84
+ try:
85
+ name_to_dtype[name] = conversion_function(name)
86
+ except exception_type:
87
+ pass
88
+ return name_to_dtype
@@ -0,0 +1,126 @@
1
+ # Copyright (c) 2021-2025, NVIDIA CORPORATION & AFFILIATES
2
+ #
3
+ # SPDX-License-Identifier: BSD-3-Clause
4
+
5
+ """
6
+ Interface to seamlessly use Cupy ndarray objects.
7
+ """
8
+
9
+ __all__ = ['CupyTensor']
10
+
11
+ import cupy
12
+
13
+ from nvmath.internal import utils
14
+ from .package_ifc import StreamHolder
15
+ from .tensor_ifc import Tensor
16
+
17
+
18
+ class CupyTensor(Tensor):
19
+ """
20
+ Tensor wrapper for cupy ndarrays.
21
+ """
22
+ name = 'cupy'
23
+ module = cupy
24
+ name_to_dtype = Tensor.create_name_dtype_map(conversion_function=lambda name: cupy.dtype(name), exception_type=TypeError)
25
+
26
+ def __init__(self, tensor):
27
+ super().__init__(tensor)
28
+
29
+ @property
30
+ def data_ptr(self):
31
+ return self.tensor.data.ptr
32
+
33
+ @property
34
+ def device(self):
35
+ return 'cuda'
36
+
37
+ @property
38
+ def device_id(self):
39
+ return self.tensor.device.id
40
+
41
+ @property
42
+ def dtype(self):
43
+ """Name of the data type"""
44
+ return self.tensor.dtype.name
45
+
46
+ @property
47
+ def shape(self):
48
+ return tuple(self.tensor.shape)
49
+
50
+ @property
51
+ def strides(self):
52
+ return tuple(stride_in_bytes // self.tensor.itemsize for stride_in_bytes in self.tensor.strides)
53
+
54
+ def numpy(self, stream_holder=StreamHolder()):
55
+ stream = stream_holder.obj
56
+ # cupy/cupy#7820
57
+ if stream is not None:
58
+ with stream:
59
+ out = self.tensor.get(stream=stream)
60
+ stream.synchronize()
61
+ else:
62
+ out = self.tensor.get(stream=stream)
63
+ # note that synchronization semantics are inconsistent for stream_holder
64
+ # TODO: add synchronization on current stream here (better to recompose the if/else branch then)
65
+ return out
66
+
67
+ @classmethod
68
+ def empty(cls, shape, **context):
69
+ """
70
+ Create an empty tensor of the specified shape and data type.
71
+ """
72
+ name = context.get('dtype', 'float32')
73
+ dtype = CupyTensor.name_to_dtype[name]
74
+ device = context.get('device', None)
75
+ strides = context.get('strides', None)
76
+
77
+ if isinstance(device, cupy.cuda.Device):
78
+ device_id = device.id
79
+ elif isinstance(device, int):
80
+ device_id = device
81
+ else:
82
+ raise ValueError(f"The device must be specified as an integer or cupy.cuda.Device instance, not '{device}'.")
83
+
84
+ with utils.device_ctx(device_id):
85
+ if strides:
86
+ # need an explicit allocation due to cupy/cupy#7818
87
+ size = dtype.itemsize
88
+ for s in shape:
89
+ size = size * s
90
+ ptr = cupy.cuda.alloc(size)
91
+ # when strides is not None, it should be of unit counts not bytes
92
+ strides = tuple(s * dtype.itemsize for s in strides)
93
+ tensor = cupy.ndarray(shape, dtype=dtype, strides=strides, memptr=ptr)
94
+ else:
95
+ tensor = cupy.ndarray(shape, dtype=dtype)
96
+
97
+ return tensor
98
+
99
+ def to(self, device='cpu', stream_holder=StreamHolder()):
100
+ """
101
+ Create a copy of the tensor on the specified device (integer or
102
+ 'cpu'). Copy to Numpy ndarray if CPU, otherwise return Cupy type.
103
+ """
104
+ if device == 'cpu':
105
+ return self.numpy(stream_holder=stream_holder)
106
+
107
+ if not isinstance(device, int):
108
+ raise ValueError(f"The device must be specified as an integer or 'cpu', not '{device}'.")
109
+
110
+ with utils.device_ctx(device), stream_holder.ctx:
111
+ tensor_device = cupy.asarray(self.tensor)
112
+
113
+ return tensor_device
114
+
115
+ def copy_(self, src, stream_holder=StreamHolder()):
116
+ """
117
+ Inplace copy of src (copy the data from src into self).
118
+ """
119
+ with stream_holder.ctx:
120
+ cupy.copyto(self.tensor, src)
121
+
122
+ def istensor(self):
123
+ """
124
+ Check if the object is ndarray-like.
125
+ """
126
+ return isinstance(self.tensor, cupy.ndarray)
@@ -0,0 +1,105 @@
1
+ # Copyright (c) 2021-2024, NVIDIA CORPORATION & AFFILIATES
2
+ #
3
+ # SPDX-License-Identifier: BSD-3-Clause
4
+
5
+ """
6
+ Interface to seamlessly use Numpy ndarray objects.
7
+ """
8
+
9
+ __all__ = ['NumpyTensor']
10
+
11
+ import cupy
12
+ import numpy
13
+
14
+ from nvmath.internal import utils
15
+ from .package_ifc import StreamHolder
16
+ from .tensor_ifc import Tensor
17
+
18
+
19
+ class NumpyTensor(Tensor):
20
+ """
21
+ Tensor wrapper for numpy ndarrays.
22
+ """
23
+ name = 'numpy'
24
+ module = numpy
25
+ name_to_dtype = Tensor.create_name_dtype_map(conversion_function=lambda name: numpy.dtype(name), exception_type=TypeError)
26
+
27
+ def __init__(self, tensor):
28
+ super().__init__(tensor)
29
+
30
+ @property
31
+ def data_ptr(self):
32
+ return self.tensor.ctypes.data
33
+
34
+ @property
35
+ def device(self):
36
+ return 'cpu'
37
+
38
+ @property
39
+ def device_id(self):
40
+ return None
41
+
42
+ @property
43
+ def dtype(self):
44
+ """Name of the data type"""
45
+ return self.tensor.dtype.name
46
+
47
+ @property
48
+ def shape(self):
49
+ return tuple(self.tensor.shape)
50
+
51
+ @property
52
+ def strides(self):
53
+ return tuple(stride_in_bytes // self.tensor.itemsize for stride_in_bytes in self.tensor.strides)
54
+
55
+ def numpy(self, stream_holder=StreamHolder()):
56
+ return self.tensor
57
+
58
+ @classmethod
59
+ def empty(cls, shape, **context):
60
+ """
61
+ Create an empty tensor of the specified shape and data type.
62
+ """
63
+ name = context.get('dtype', 'float32')
64
+ dtype = NumpyTensor.name_to_dtype[name]
65
+ strides = context.get('strides', None)
66
+ # when strides is not None, it should be of unit counts not bytes
67
+ return cls(numpy.ndarray(shape, dtype=dtype, strides=(tuple(s * dtype.itemsize for s in strides) if strides else None)))
68
+
69
+ def to(self, device='cpu', stream_holder=StreamHolder()):
70
+ """
71
+ Create a copy of the tensor on the specified device (integer or
72
+ 'cpu'). Copy to Cupy ndarray on the specified device if it
73
+ is not CPU. Otherwise, return self.
74
+ """
75
+ if device == 'cpu':
76
+ return self.tensor
77
+
78
+ if not isinstance(device, int):
79
+ raise ValueError(f"The device must be specified as an integer or 'cpu', not '{device}'.")
80
+
81
+ with utils.device_ctx(device), stream_holder.ctx:
82
+ tensor_device = cupy.asarray(self.tensor)
83
+
84
+ return tensor_device
85
+
86
+ def copy_(self, src, stream_holder=StreamHolder()):
87
+ package = utils.infer_object_package(src)
88
+ # Handle NumPy <=> CuPy CPU-GPU ndarray asymmetry.
89
+ if package == 'cupy':
90
+ stream = stream_holder.obj
91
+ with stream:
92
+ out = src.get(stream=stream, out=self.tensor)
93
+ # cupy/cupy#7820
94
+ if stream is not None:
95
+ stream.synchronize()
96
+
97
+ return out
98
+ else:
99
+ raise NotImplementedError
100
+
101
+ def istensor(self):
102
+ """
103
+ Check if the object is ndarray-like.
104
+ """
105
+ return isinstance(self.tensor, numpy.ndarray)
@@ -0,0 +1,104 @@
1
+ # Copyright (c) 2021-2024, NVIDIA CORPORATION & AFFILIATES
2
+ #
3
+ # SPDX-License-Identifier: BSD-3-Clause
4
+
5
+ """
6
+ Interface to seamlessly use Torch tensor objects.
7
+ """
8
+
9
+ __all__ = ['TorchTensor']
10
+
11
+ import torch
12
+
13
+ from .package_ifc import StreamHolder
14
+ from .tensor_ifc import Tensor
15
+
16
+
17
+ class TorchTensor(Tensor):
18
+ """
19
+ Tensor wrapper for Torch Tensors.
20
+ """
21
+ name = 'torch'
22
+ module = torch
23
+ name_to_dtype = Tensor.create_name_dtype_map(conversion_function=lambda name: getattr(torch, name), exception_type=AttributeError)
24
+
25
+ def __init__(self, tensor):
26
+ super().__init__(tensor)
27
+
28
+ @property
29
+ def data_ptr(self):
30
+ return self.tensor.data_ptr()
31
+
32
+ @property
33
+ def device(self):
34
+ return str(self.tensor.device).split(':')[0]
35
+
36
+ @property
37
+ def device_id(self):
38
+ return self.tensor.device.index
39
+
40
+ @property
41
+ def dtype(self):
42
+ """Name of the data type"""
43
+ return str(self.tensor.dtype).split('.')[-1]
44
+
45
+ @property
46
+ def shape(self):
47
+ return tuple(self.tensor.shape)
48
+
49
+ @property
50
+ def strides(self):
51
+ return self.tensor.stride()
52
+
53
+ @property
54
+ def T(self):
55
+ return self.__class__(self.tensor.permute(*torch.arange(self.tensor.ndim - 1, -1, -1)))
56
+
57
+ def numpy(self, stream_holder=StreamHolder()):
58
+ # We currently do not use this.
59
+ raise NotImplementedError
60
+
61
+ @classmethod
62
+ def empty(cls, shape, **context):
63
+ """
64
+ Create an empty tensor of the specified shape and data type on the specified device (None, 'cpu', or device id).
65
+ """
66
+ name = context.get('dtype', 'float32')
67
+ dtype = TorchTensor.name_to_dtype[name]
68
+ device = context.get('device', None)
69
+ strides = context.get('strides', None)
70
+ if strides:
71
+ # note: torch strides is not scaled by bytes
72
+ tensor = torch.empty_strided(shape, strides, dtype=dtype, device=device)
73
+ else:
74
+ tensor = torch.empty(shape, dtype=dtype, device=device)
75
+
76
+ return tensor
77
+
78
+ def to(self, device='cpu', stream_holder=StreamHolder()):
79
+ """
80
+ Create a copy of the tensor on the specified device (integer or
81
+ 'cpu'). Copy to Numpy ndarray if CPU, otherwise return Cupy type.
82
+ """
83
+ if not (device == 'cpu' or isinstance(device, int)):
84
+ raise ValueError(f"The device must be specified as an integer or 'cpu', not '{device}'.")
85
+
86
+ non_blocking = False if device == 'cpu' else True
87
+
88
+ with stream_holder.ctx:
89
+ tensor_device = self.tensor.to(device=device, non_blocking=non_blocking)
90
+
91
+ return tensor_device
92
+
93
+ def copy_(self, src, stream_holder=StreamHolder()):
94
+ """
95
+ Inplace copy of src (copy the data from src into self).
96
+ """
97
+ with stream_holder.ctx:
98
+ self.tensor.copy_(src)
99
+
100
+ def istensor(self):
101
+ """
102
+ Check if the object is ndarray-like.
103
+ """
104
+ return isinstance(self.tensor, torch.Tensor)