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.
- cuquantum/__init__.py +10 -0
- cuquantum/__main__.py +128 -0
- cuquantum/_internal/__init__.py +3 -0
- cuquantum/_internal/package_ifc.py +91 -0
- cuquantum/_internal/tensor_ifc.py +88 -0
- cuquantum/_internal/tensor_ifc_cupy.py +126 -0
- cuquantum/_internal/tensor_ifc_numpy.py +105 -0
- cuquantum/_internal/tensor_ifc_torch.py +104 -0
- cuquantum/_internal/tensor_wrapper.py +112 -0
- cuquantum/_internal/utils.py +202 -0
- cuquantum/_version.py +8 -0
- cuquantum/bindings/__init__.pxd +3 -0
- cuquantum/bindings/__init__.py +13 -0
- cuquantum/bindings/_internal/cudensitymat.cpython-312-aarch64-linux-gnu.so +0 -0
- cuquantum/bindings/_internal/custatevec.cpython-312-aarch64-linux-gnu.so +0 -0
- cuquantum/bindings/_internal/cutensornet.cpython-312-aarch64-linux-gnu.so +0 -0
- cuquantum/bindings/_utils.cpython-312-aarch64-linux-gnu.so +0 -0
- cuquantum/bindings/_utils.pxd +187 -0
- cuquantum/bindings/_utils.pyx +271 -0
- cuquantum/bindings/cudensitymat.cpython-312-aarch64-linux-gnu.so +0 -0
- cuquantum/bindings/cudensitymat.pxd +148 -0
- cuquantum/bindings/cudensitymat.pyx +1890 -0
- cuquantum/bindings/custatevec.cpython-312-aarch64-linux-gnu.so +0 -0
- cuquantum/bindings/custatevec.pxd +122 -0
- cuquantum/bindings/custatevec.pyx +2489 -0
- cuquantum/bindings/cutensornet.cpython-312-aarch64-linux-gnu.so +0 -0
- cuquantum/bindings/cutensornet.pxd +243 -0
- cuquantum/bindings/cutensornet.pyx +4157 -0
- cuquantum/bindings/cycudensitymat.cpython-312-aarch64-linux-gnu.so +0 -0
- cuquantum/bindings/cycudensitymat.pxd +288 -0
- cuquantum/bindings/cycudensitymat.pyx +243 -0
- cuquantum/bindings/cycustatevec.cpython-312-aarch64-linux-gnu.so +0 -0
- cuquantum/bindings/cycustatevec.pxd +236 -0
- cuquantum/bindings/cycustatevec.pyx +311 -0
- cuquantum/bindings/cycutensornet.cpython-312-aarch64-linux-gnu.so +0 -0
- cuquantum/bindings/cycutensornet.pxd +492 -0
- cuquantum/bindings/cycutensornet.pyx +579 -0
- cuquantum/densitymat/__init__.py +11 -0
- cuquantum/densitymat/_internal/__init__.py +3 -0
- cuquantum/densitymat/_internal/callbacks.py +405 -0
- cuquantum/densitymat/_internal/library_handle.py +152 -0
- cuquantum/densitymat/_internal/typemaps.py +8 -0
- cuquantum/densitymat/_internal/utils.py +201 -0
- cuquantum/densitymat/callbacks.py +341 -0
- cuquantum/densitymat/elementary_operator.py +892 -0
- cuquantum/densitymat/matrix_operator.py +228 -0
- cuquantum/densitymat/memory.py +171 -0
- cuquantum/densitymat/operators.py +1714 -0
- cuquantum/densitymat/spectrum.py +405 -0
- cuquantum/densitymat/state.py +614 -0
- cuquantum/densitymat/work_stream.py +362 -0
- cuquantum/memory.py +47 -0
- cuquantum/tensornet/__init__.py +11 -0
- cuquantum/tensornet/_internal/__init__.py +3 -0
- cuquantum/tensornet/_internal/circuit_converter_utils.py +263 -0
- cuquantum/tensornet/_internal/circuit_parser_utils_cirq.py +93 -0
- cuquantum/tensornet/_internal/circuit_parser_utils_qiskit.py +215 -0
- cuquantum/tensornet/_internal/decomposition_utils.py +493 -0
- cuquantum/tensornet/_internal/einsum_parser.py +384 -0
- cuquantum/tensornet/_internal/grad_torch.py +84 -0
- cuquantum/tensornet/_internal/helpers.py +146 -0
- cuquantum/tensornet/_internal/optimizer_ifc.py +244 -0
- cuquantum/tensornet/circuit_converter.py +417 -0
- cuquantum/tensornet/configuration.py +215 -0
- cuquantum/tensornet/experimental/__init__.py +8 -0
- cuquantum/tensornet/experimental/_internal/__init__.py +3 -0
- cuquantum/tensornet/experimental/_internal/network_state_utils.py +228 -0
- cuquantum/tensornet/experimental/_internal/utils.py +53 -0
- cuquantum/tensornet/experimental/configuration.py +207 -0
- cuquantum/tensornet/experimental/network_operator.py +237 -0
- cuquantum/tensornet/experimental/network_state.py +1312 -0
- cuquantum/tensornet/experimental/tensor_network.py +408 -0
- cuquantum/tensornet/tensor.py +535 -0
- cuquantum/tensornet/tensor_network.py +1675 -0
- cuquantum_python_cu13-25.9.0.dist-info/METADATA +158 -0
- cuquantum_python_cu13-25.9.0.dist-info/RECORD +79 -0
- cuquantum_python_cu13-25.9.0.dist-info/WHEEL +5 -0
- cuquantum_python_cu13-25.9.0.dist-info/licenses/LICENSE +28 -0
- 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,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)
|