nvmath-python 0.5.0__cp313-cp313-win_amd64.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 (174) hide show
  1. nvmath/__init__.pxd +0 -0
  2. nvmath/__init__.py +37 -0
  3. nvmath/_internal/__init__.py +0 -0
  4. nvmath/_internal/layout.py +58 -0
  5. nvmath/_utils.py +363 -0
  6. nvmath/bindings/__init__.pxd +0 -0
  7. nvmath/bindings/__init__.py +44 -0
  8. nvmath/bindings/_internal/__init__.pxd +0 -0
  9. nvmath/bindings/_internal/__init__.py +0 -0
  10. nvmath/bindings/_internal/cublas.cp313-win_amd64.pyd +0 -0
  11. nvmath/bindings/_internal/cublas.pxd +518 -0
  12. nvmath/bindings/_internal/cublasLt.cp313-win_amd64.pyd +0 -0
  13. nvmath/bindings/_internal/cublasLt.pxd +54 -0
  14. nvmath/bindings/_internal/cudss.cp313-win_amd64.pyd +0 -0
  15. nvmath/bindings/_internal/cudss.pxd +44 -0
  16. nvmath/bindings/_internal/cufft.cp313-win_amd64.pyd +0 -0
  17. nvmath/bindings/_internal/cufft.pxd +69 -0
  18. nvmath/bindings/_internal/cufftMp.pxd +77 -0
  19. nvmath/bindings/_internal/curand.cp313-win_amd64.pyd +0 -0
  20. nvmath/bindings/_internal/curand.pxd +42 -0
  21. nvmath/bindings/_internal/cusolver.cp313-win_amd64.pyd +0 -0
  22. nvmath/bindings/_internal/cusolver.pxd +15 -0
  23. nvmath/bindings/_internal/cusolverDn.cp313-win_amd64.pyd +0 -0
  24. nvmath/bindings/_internal/cusolverDn.pxd +386 -0
  25. nvmath/bindings/_internal/cusparse.cp313-win_amd64.pyd +0 -0
  26. nvmath/bindings/_internal/cusparse.pxd +270 -0
  27. nvmath/bindings/_internal/mathdx.cp313-win_amd64.pyd +0 -0
  28. nvmath/bindings/_internal/mathdx.pxd +79 -0
  29. nvmath/bindings/_internal/nvshmem.pxd +29 -0
  30. nvmath/bindings/_internal/utils.cp313-win_amd64.pyd +0 -0
  31. nvmath/bindings/_internal/utils.pxd +186 -0
  32. nvmath/bindings/cublas.cp313-win_amd64.pyd +0 -0
  33. nvmath/bindings/cublas.pxd +545 -0
  34. nvmath/bindings/cublas.pyi +18 -0
  35. nvmath/bindings/cublasLt.cp313-win_amd64.pyd +0 -0
  36. nvmath/bindings/cublasLt.pxd +99 -0
  37. nvmath/bindings/cudss.cp313-win_amd64.pyd +0 -0
  38. nvmath/bindings/cudss.pxd +83 -0
  39. nvmath/bindings/cudss.pyi +249 -0
  40. nvmath/bindings/cufft.cp313-win_amd64.pyd +0 -0
  41. nvmath/bindings/cufft.pxd +114 -0
  42. nvmath/bindings/cufftMp.pxd +124 -0
  43. nvmath/bindings/curand.cp313-win_amd64.pyd +0 -0
  44. nvmath/bindings/curand.pxd +71 -0
  45. nvmath/bindings/cusolver.cp313-win_amd64.pyd +0 -0
  46. nvmath/bindings/cusolver.pxd +60 -0
  47. nvmath/bindings/cusolverDn.cp313-win_amd64.pyd +0 -0
  48. nvmath/bindings/cusolverDn.pxd +410 -0
  49. nvmath/bindings/cusparse.cp313-win_amd64.pyd +0 -0
  50. nvmath/bindings/cusparse.pxd +329 -0
  51. nvmath/bindings/cycublas.cp313-win_amd64.pyd +0 -0
  52. nvmath/bindings/cycublas.pxd +657 -0
  53. nvmath/bindings/cycublasLt.cp313-win_amd64.pyd +0 -0
  54. nvmath/bindings/cycublasLt.pxd +1010 -0
  55. nvmath/bindings/cycudss.cp313-win_amd64.pyd +0 -0
  56. nvmath/bindings/cycudss.pxd +191 -0
  57. nvmath/bindings/cycufft.cp313-win_amd64.pyd +0 -0
  58. nvmath/bindings/cycufft.pxd +320 -0
  59. nvmath/bindings/cycufftMp.pxd +333 -0
  60. nvmath/bindings/cycurand.cp313-win_amd64.pyd +0 -0
  61. nvmath/bindings/cycurand.pxd +146 -0
  62. nvmath/bindings/cycusolver.cp313-win_amd64.pyd +0 -0
  63. nvmath/bindings/cycusolver.pxd +143 -0
  64. nvmath/bindings/cycusolverDn.cp313-win_amd64.pyd +0 -0
  65. nvmath/bindings/cycusolverDn.pxd +414 -0
  66. nvmath/bindings/cycusparse.cp313-win_amd64.pyd +0 -0
  67. nvmath/bindings/cycusparse.pxd +476 -0
  68. nvmath/bindings/cymathdx.cp313-win_amd64.pyd +0 -0
  69. nvmath/bindings/cymathdx.pxd +363 -0
  70. nvmath/bindings/cynvshmem.pxd +126 -0
  71. nvmath/bindings/mathdx.cp313-win_amd64.pyd +0 -0
  72. nvmath/bindings/mathdx.pxd +131 -0
  73. nvmath/bindings/mathdx.pyi +686 -0
  74. nvmath/bindings/nvpl/__init__.pxd +0 -0
  75. nvmath/bindings/nvpl/__init__.py +11 -0
  76. nvmath/bindings/nvpl/_internal/__init__.pxd +0 -0
  77. nvmath/bindings/nvpl/_internal/__init__.py +0 -0
  78. nvmath/bindings/nvpl/_internal/fft.pxd +36 -0
  79. nvmath/bindings/nvpl/cyfft.pxd +87 -0
  80. nvmath/bindings/nvpl/fft.pxd +100 -0
  81. nvmath/bindings/nvshmem.pxd +54 -0
  82. nvmath/device/__init__.py +23 -0
  83. nvmath/device/_deprecated.py +33 -0
  84. nvmath/device/caching.py +51 -0
  85. nvmath/device/common.py +131 -0
  86. nvmath/device/common_backend.py +125 -0
  87. nvmath/device/common_cuda.py +156 -0
  88. nvmath/device/common_mathdx.py +97 -0
  89. nvmath/device/common_numba.py +159 -0
  90. nvmath/device/common_opaque_tensor.py +128 -0
  91. nvmath/device/cublasdx.py +1085 -0
  92. nvmath/device/cublasdx_backend.py +552 -0
  93. nvmath/device/cublasdx_numba.py +528 -0
  94. nvmath/device/cufftdx.py +551 -0
  95. nvmath/device/cufftdx_backend.py +194 -0
  96. nvmath/device/cufftdx_numba.py +123 -0
  97. nvmath/device/curand_kernel.py +9147 -0
  98. nvmath/device/nvrtc.py +95 -0
  99. nvmath/device/patch.py +35 -0
  100. nvmath/device/random.py +417 -0
  101. nvmath/device/random_helpers.py +23 -0
  102. nvmath/device/random_states.py +189 -0
  103. nvmath/device/types.py +11 -0
  104. nvmath/device/vector_types_numba.py +203 -0
  105. nvmath/distributed/__init__.py +117 -0
  106. nvmath/distributed/_internal/__init__.py +0 -0
  107. nvmath/distributed/_internal/nvshmem.py +243 -0
  108. nvmath/distributed/_internal/tensor_ifc_cupy.py +111 -0
  109. nvmath/distributed/_internal/tensor_ifc_numpy.py +53 -0
  110. nvmath/distributed/_internal/tensor_ifc_torch.py +108 -0
  111. nvmath/distributed/_internal/tensor_wrapper.py +59 -0
  112. nvmath/distributed/_utils.py +152 -0
  113. nvmath/distributed/fft/__init__.py +6 -0
  114. nvmath/distributed/fft/_configuration.py +86 -0
  115. nvmath/distributed/fft/fft.py +1737 -0
  116. nvmath/distributed/reshape/__init__.py +6 -0
  117. nvmath/distributed/reshape/_configuration.py +39 -0
  118. nvmath/distributed/reshape/reshape.py +1249 -0
  119. nvmath/fft/__init__.py +7 -0
  120. nvmath/fft/_configuration.py +208 -0
  121. nvmath/fft/_exec_utils.py +116 -0
  122. nvmath/fft/_helpers.py +260 -0
  123. nvmath/fft/fft.py +2346 -0
  124. nvmath/internal/__init__.py +5 -0
  125. nvmath/internal/enum_utils.py +142 -0
  126. nvmath/internal/formatters.py +87 -0
  127. nvmath/internal/mem_limit.py +51 -0
  128. nvmath/internal/package_ifc.py +119 -0
  129. nvmath/internal/package_ifc_cuda.py +43 -0
  130. nvmath/internal/package_ifc_cupy.py +49 -0
  131. nvmath/internal/package_ifc_torch.py +31 -0
  132. nvmath/internal/package_wrapper.py +14 -0
  133. nvmath/internal/tensor_ifc.py +143 -0
  134. nvmath/internal/tensor_ifc_cupy.py +185 -0
  135. nvmath/internal/tensor_ifc_numpy.py +157 -0
  136. nvmath/internal/tensor_ifc_torch.py +115 -0
  137. nvmath/internal/tensor_wrapper.py +160 -0
  138. nvmath/internal/typemaps.py +154 -0
  139. nvmath/internal/utils.py +634 -0
  140. nvmath/linalg/__init__.py +11 -0
  141. nvmath/linalg/_internal/__init__.py +3 -0
  142. nvmath/linalg/_internal/algo_cap_ifc.py +86 -0
  143. nvmath/linalg/_internal/algo_config_ifc.py +87 -0
  144. nvmath/linalg/_internal/enum_to_tuples.py +64 -0
  145. nvmath/linalg/_internal/epilog_protocol.py +738 -0
  146. nvmath/linalg/_internal/matmul_desc_ifc.py +72 -0
  147. nvmath/linalg/_internal/matmul_pref_ifc.py +65 -0
  148. nvmath/linalg/_internal/matrix_layout_ifc.py +59 -0
  149. nvmath/linalg/_internal/typemaps.py +135 -0
  150. nvmath/linalg/_internal/utils.py +96 -0
  151. nvmath/linalg/advanced/__init__.py +8 -0
  152. nvmath/linalg/advanced/_algorithmmod.py +142 -0
  153. nvmath/linalg/advanced/_configuration.py +320 -0
  154. nvmath/linalg/advanced/helpers/__init__.py +5 -0
  155. nvmath/linalg/advanced/helpers/matmul.py +173 -0
  156. nvmath/linalg/advanced/matmulmod.py +2855 -0
  157. nvmath/memory.py +266 -0
  158. nvmath/sparse/__init__.py +18 -0
  159. nvmath/sparse/_internal/__init__.py +21 -0
  160. nvmath/sparse/_internal/common_utils.py +81 -0
  161. nvmath/sparse/_internal/cudss_config_ifc.py +584 -0
  162. nvmath/sparse/_internal/cudss_data_ifc.py +291 -0
  163. nvmath/sparse/_internal/cudss_utils.py +398 -0
  164. nvmath/sparse/_internal/sparse_csr_ifc.py +154 -0
  165. nvmath/sparse/_internal/sparse_format_helpers.py +88 -0
  166. nvmath/sparse/_internal/sparse_tensor_ifc.py +80 -0
  167. nvmath/sparse/advanced/__init__.py +7 -0
  168. nvmath/sparse/advanced/_configuration.py +150 -0
  169. nvmath/sparse/advanced/direct_solver.py +1705 -0
  170. nvmath_python-0.5.0.dist-info/METADATA +122 -0
  171. nvmath_python-0.5.0.dist-info/RECORD +174 -0
  172. nvmath_python-0.5.0.dist-info/WHEEL +5 -0
  173. nvmath_python-0.5.0.dist-info/licenses/LICENSE +177 -0
  174. nvmath_python-0.5.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,5 @@
1
+ """APIs in this module are for NVIDIA-use only.
2
+
3
+ APIs in internal are not intended for third-party developers, but are shared between NVIDIA
4
+ packages.
5
+ """
@@ -0,0 +1,142 @@
1
+ # Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ Factories for create options dataclasses, as well as utilities to add docstring to enum
7
+ classes.
8
+ """
9
+
10
+ import dataclasses
11
+ from enum import IntEnum
12
+ import re
13
+ from typing import Any, ClassVar
14
+ from collections.abc import Callable
15
+
16
+ import numpy
17
+
18
+
19
+ def create_options_class_from_enum(
20
+ options_class_name: str,
21
+ enum_class: IntEnum,
22
+ get_attr_dtype: Callable,
23
+ description: str,
24
+ filter_re: str = r"(?P<option_name>.*)",
25
+ ):
26
+ """
27
+ Create an options dataclass from a Python enum class. Names can be filtered if desired.
28
+
29
+ Args:
30
+ options_class_name: Name of the dataclass that will be created.
31
+
32
+ enum_class: The IntEnum class that contains the options for the dataclass.
33
+
34
+ get_attr_dtype: A callable that takes in an enum value as the argument and returns
35
+ the size in bytes of the cuTensorNet.
36
+
37
+ filter_re: A re definition that defines the match named 'option_name'.
38
+ """
39
+ if r"(?P<option_name>" not in filter_re:
40
+ message = """Incorrect re.
41
+ The re for the filter must contain the named group 'option_name'."""
42
+ raise ValueError(message)
43
+
44
+ # Helper vars for creating attribute docstring.
45
+ doc = f"""A data class for capturing the {description} options.
46
+
47
+ Attributes:
48
+ """
49
+ indent = " " * 8
50
+ prefix = determine_enum_prefix(enum_class, "_ATTRIBUTE")
51
+
52
+ filter_pattern = re.compile(filter_re)
53
+ option_to_enum: dict[str, IntEnum] = {}
54
+ option_to_dtype: dict[str, Any] = {}
55
+ # mypy claims that IntEnum is not iterable
56
+ for e in enum_class: # type: ignore
57
+ m = filter_pattern.match(e.name)
58
+ if not m:
59
+ continue
60
+ option_name = m.group("option_name").lower()
61
+ option_to_enum[option_name] = e
62
+ option_to_dtype[option_name] = get_attr_dtype(e)
63
+
64
+ # Add docstring for this attribute.
65
+ doc += indent + option_name + ":" + f" See `{prefix + '_' + m.group(0)}`.\n"
66
+
67
+ field: tuple[str, Any, None | dict]
68
+ fields: list[tuple[str, Any, None | dict]] = []
69
+ for option_name, dtype in option_to_dtype.items():
70
+ if numpy.issubdtype(dtype, numpy.integer):
71
+ field = option_name, int | None, dataclasses.field(default=None)
72
+ else:
73
+ field = option_name, Any | None, dataclasses.field(default=None)
74
+ fields.append(field)
75
+
76
+ # Add class attributes.
77
+
78
+ field = "option_to_enum", ClassVar[dict], dataclasses.field(default=option_to_enum)
79
+ fields.append(field)
80
+
81
+ field = "option_to_dtype", ClassVar[dict], dataclasses.field(default=option_to_dtype)
82
+ fields.append(field)
83
+
84
+ # Create the options class.
85
+ options_class = dataclasses.make_dataclass(options_class_name, fields)
86
+ options_class.__doc__ = doc
87
+
88
+ return options_class
89
+
90
+
91
+ def snake_to_camel(names):
92
+ name = ""
93
+ for i, sub_name in enumerate(names):
94
+ if i == 0:
95
+ name += sub_name.lower()
96
+ else:
97
+ name += sub_name[0].upper() + sub_name[1:]
98
+ name += "_t"
99
+ return name
100
+
101
+
102
+ def camel_to_snake(name, upper=True):
103
+ """
104
+ Convert string from camel case to snake style.
105
+ """
106
+
107
+ def transformer(m):
108
+ prefix = iter(("", "_", ""))
109
+ for i in 1, 3, 5:
110
+ first, second = i, i + 1
111
+ s = next(prefix)
112
+ if m.group(second):
113
+ if m.group(first): # If the second group doesn't exist, the first won't either by the design of the RE.
114
+ s += m.group(first).lower() + "_"
115
+ s += m.group(second).lower()
116
+ break
117
+ return s
118
+
119
+ name = re.sub(r"^([A-Z]*)([A-Z])|(?<!_)([A-Z]*)([A-Z])|([A-Z]*)([A-Z])", transformer, name)
120
+ if upper:
121
+ name = name.upper()
122
+ return name
123
+
124
+
125
+ def determine_enum_prefix(enum_class, chomp):
126
+ """
127
+ This function assumes that the convention used to translate C enumerators to Python enum
128
+ names holds.
129
+ """
130
+
131
+ prefix = enum_class.__module__.split(".")[-1].upper()
132
+ prefix += "_" + camel_to_snake(enum_class.__name__)
133
+ prefix = re.sub(chomp, "", prefix)
134
+ return prefix
135
+
136
+
137
+ def add_enum_class_doc(enum_class, chomp):
138
+ """
139
+ Add docstring to enum classes.
140
+ """
141
+ for e in enum_class:
142
+ e.__doc__ = f"See `{determine_enum_prefix(enum_class, chomp) + '_' + e.name.upper()}`."
@@ -0,0 +1,87 @@
1
+ # Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ Formatters for printing data.
7
+ """
8
+
9
+ import numpy as np
10
+
11
+
12
+ class MemoryStr:
13
+ """
14
+ A simple type to pretty-print memory-like values.
15
+ """
16
+
17
+ def __init__(self, memory, base_unit="B"):
18
+ self.memory = memory
19
+ self.base_unit = base_unit
20
+ self.base = 1024
21
+
22
+ def __str__(self):
23
+ """
24
+ Convert large values to powers of 1024 for readability.
25
+ """
26
+
27
+ base, base_unit, memory = self.base, self.base_unit, self.memory
28
+
29
+ if memory < base:
30
+ value, unit = memory, base_unit
31
+ elif memory < base**2:
32
+ value, unit = memory / base, f"Ki{base_unit}"
33
+ elif memory < base**3:
34
+ value, unit = memory / base**2, f"Mi{base_unit}"
35
+ else:
36
+ value, unit = memory / base**3, f"Gi{base_unit}"
37
+
38
+ return f"{value:0.2f} {unit}"
39
+
40
+
41
+ class FLOPSStr:
42
+ """
43
+ A simple type to pretty-print FLOP count and FLOP/s-like values.
44
+ """
45
+
46
+ def __init__(self, flops, base_unit):
47
+ self.flops = flops
48
+ self.base_unit = base_unit
49
+ self.base = 1000
50
+
51
+ def __str__(self):
52
+ """
53
+ Convert large values to powers of 1000 for readability.
54
+ """
55
+
56
+ base, base_unit, flops = self.base, self.base_unit, self.flops
57
+
58
+ if flops < base:
59
+ value, unit = flops, base_unit
60
+ elif flops < base**2:
61
+ value, unit = flops / base, f"K{base_unit}"
62
+ elif flops < base**3:
63
+ value, unit = flops / base**2, f"M{base_unit}"
64
+ elif flops < base**4:
65
+ value, unit = flops / base**3, f"G{base_unit}"
66
+ elif flops < base**5:
67
+ value, unit = flops / base**4, f"T{base_unit}"
68
+ else:
69
+ value, unit = flops / base**5, f"P{base_unit}"
70
+
71
+ return f"{value:0.3f} {unit}"
72
+
73
+
74
+ def array2string(array_like):
75
+ """
76
+ String representation of an array-like object with possible truncation of "interior"
77
+ values to limit string size.
78
+
79
+ The NumPy function "set_printoptions" can be used to control the display of the array.
80
+ """
81
+
82
+ return np.array2string(
83
+ np.asanyarray(array_like, dtype=object),
84
+ separator=", ",
85
+ # NumPy hates empty strings so we print 'None' instead.
86
+ formatter={"object": lambda s: s if s != "" else "None"},
87
+ )
@@ -0,0 +1,51 @@
1
+ # Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ Memory specification regular expression.
7
+ """
8
+
9
+ __all__ = ["MEM_LIMIT_RE_PCT", "MEM_LIMIT_RE_VAL", "MEM_LIMIT_DOC", "check_memory_str"]
10
+
11
+ import re
12
+
13
+ MEM_LIMIT_RE_PCT = re.compile(r"(?P<value>[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)\s*%\s*$")
14
+ MEM_LIMIT_RE_VAL = re.compile(
15
+ r"(?P<value>[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)\s*(?P<units>[kmg])?(?P<binary>(?<=[kmg])i)?b\s*$",
16
+ re.IGNORECASE,
17
+ )
18
+ MEM_LIMIT_DOC = """The {kind} must be specified in one of the following forms:
19
+ (1) A number (int or float). If the number is a float between 0 and 1 inclusive, the
20
+ {kind} is interpreted as a fraction of the total device memory; otherwise it is
21
+ interpreted as the number of bytes of memory, with float value being cast to int.
22
+ Examples: 0.75, 50E6, 50000000, ...
23
+ (2) A string containing a positive value followed by B, kB, MB, or GB for powers of 1000.
24
+ Examples: "0.05 GB", "50 MB", "50000000 B" ...
25
+ (3) A string containing a positive value followed by kiB, MiB, or GiB for powers of 1024.
26
+ Examples: "0.05 GiB", "51.2 MiB", "53687091 B" ...
27
+ (4) A string with value in the range [0, 100] followed by a % symbol.
28
+ Examples: "26%","82%", ...
29
+
30
+ Whitespace between values and units is optional.
31
+
32
+ The provided {kind} is "{value}".
33
+ """
34
+
35
+
36
+ def check_memory_str(value, kind):
37
+ """
38
+ Check if the memory specification string is valid.
39
+
40
+ value = the memory specification string.
41
+ kind = a string denoting the type of memory being checked, used in error messages.
42
+ """
43
+ if not isinstance(value, int | float):
44
+ m1 = MEM_LIMIT_RE_PCT.match(value)
45
+ if m1:
46
+ factor = float(m1.group("value"))
47
+ if factor < 0 or factor > 100:
48
+ raise ValueError(f"The {kind} percentage must be in the range [0, 100].")
49
+ m2 = MEM_LIMIT_RE_VAL.match(value)
50
+ if not (m1 or m2):
51
+ raise ValueError(MEM_LIMIT_DOC.format(kind=kind, value=value))
@@ -0,0 +1,119 @@
1
+ # Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ An abstract interface to certain package-provided operations.
7
+
8
+ Even though cuda.core does not have the concept of a "current stream" or a "stream context",
9
+ we promise to honor third-party CUDA stream contexts if they match the input operand's
10
+ package. The interface class defined in this module wraps around those stream concepts to
11
+ make them uniform.
12
+
13
+ The strategy is to use cuda.core everywhere internally except for context managers which
14
+ will need to wrap back around to the external implementation.
15
+ """
16
+
17
+ __all__ = ["Package", "StreamHolder"]
18
+
19
+ from abc import ABC, abstractmethod
20
+ from collections.abc import Hashable
21
+ from contextlib import AbstractContextManager
22
+ from dataclasses import dataclass
23
+ from typing import Generic, Protocol, TypeVar
24
+
25
+ import cuda.core.experimental as ccx
26
+
27
+
28
+ class AnyStream(Hashable, Protocol):
29
+ """Any supported Stream object such as a Stream from cuda.core, CuPy, or PyTorch."""
30
+
31
+ # This empty protocol class is just a placeholder to make type checking more helpful.
32
+ # Since the class is empty, it doesn't assume anything is true about the implementation
33
+ # classes.
34
+ pass
35
+
36
+
37
+ """
38
+ A generic type for the third-party Stream which a given Package implementation wraps
39
+ around.
40
+ """
41
+ Stream = TypeVar("Stream")
42
+
43
+
44
+ class Package(ABC, Generic[Stream]):
45
+ @staticmethod
46
+ @abstractmethod
47
+ def get_current_stream(device_id: int) -> Stream:
48
+ """
49
+ Obtain the current stream on the device.
50
+
51
+ Args:
52
+ device_id: The id (ordinal) of the device.
53
+ """
54
+ raise NotImplementedError
55
+
56
+ @staticmethod
57
+ @abstractmethod
58
+ def to_stream_pointer(stream: Stream) -> int:
59
+ """
60
+ Obtain the stream pointer.
61
+
62
+ Args:
63
+ stream: The stream object.
64
+ """
65
+ raise NotImplementedError
66
+
67
+ @staticmethod
68
+ @abstractmethod
69
+ def to_stream_context(stream: Stream) -> AbstractContextManager[Stream]:
70
+ """
71
+ Create a context manager from the stream.
72
+
73
+ Args:
74
+ stream: The stream object.
75
+ """
76
+ raise NotImplementedError
77
+
78
+ @staticmethod
79
+ @abstractmethod
80
+ def create_external_stream(device_id: int, stream_ptr: int) -> Stream:
81
+ """
82
+ Wrap a stream pointer into an external stream object.
83
+
84
+ Args:
85
+ device_id: The id (ordinal) of the device.
86
+ stream: The stream pointer (int) to be wrapped.
87
+ """
88
+ raise NotImplementedError
89
+
90
+ @classmethod
91
+ def create_stream(cls, external: Stream) -> ccx.Stream:
92
+ """
93
+ Wrap an external Stream object into a cuda.core.Stream.
94
+
95
+ Args:
96
+ external: The external Stream object.
97
+ """
98
+ return ccx.Stream.from_handle(cls.to_stream_pointer(external))
99
+
100
+
101
+ @dataclass
102
+ class StreamHolder(Generic[Stream]):
103
+ """A data class for easing CUDA stream manipulation.
104
+
105
+ Attributes:
106
+ ctx: A context manager for using the specified stream.
107
+ device_id (int): The device ID where the encapsulated stream locates.
108
+ external: A foreign object that holds the stream alive.
109
+ obj: The cuda.core Stream object wrapping external.
110
+ package (str): The name of the package to which the external stream belongs.
111
+ ptr (int): The address of the underlying ``cudaStream_t`` object.
112
+ """
113
+
114
+ ctx: AbstractContextManager[Stream]
115
+ device_id: int
116
+ external: Stream
117
+ obj: ccx.Stream
118
+ package: str
119
+ ptr: int
@@ -0,0 +1,43 @@
1
+ # Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ Interface to cuda.core operations.
7
+ """
8
+
9
+ __all__ = ["CUDAPackage"]
10
+
11
+ import contextlib
12
+ import typing
13
+
14
+ import cuda.core.experimental as ccx
15
+
16
+ from .package_ifc import Package
17
+
18
+
19
+ class CUDAPackage(Package[ccx.Stream]):
20
+ @staticmethod
21
+ def get_current_stream(device_id: int):
22
+ message = "cuda.core has no concept of a current stream or a stream context."
23
+ raise NotImplementedError(message)
24
+
25
+ @staticmethod
26
+ def to_stream_pointer(stream: ccx.Stream) -> int: # type: ignore[override]
27
+ return int(stream.handle)
28
+
29
+ @staticmethod
30
+ def to_stream_context(stream: ccx.Stream): # type: ignore[override]
31
+ @contextlib.contextmanager
32
+ def stream_context() -> typing.Iterator[ccx.Stream]:
33
+ yield stream
34
+
35
+ return stream_context
36
+
37
+ @staticmethod
38
+ def create_external_stream(device_id: int, stream_ptr: int) -> ccx.Stream:
39
+ return ccx.Stream.from_handle(stream_ptr)
40
+
41
+ @classmethod
42
+ def create_stream(cls, external: ccx.Stream) -> ccx.Stream: # type: ignore[override]
43
+ return external
@@ -0,0 +1,49 @@
1
+ # Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ Interface to CuPy operations.
7
+ """
8
+
9
+ __all__ = ["CupyPackage"]
10
+
11
+ import cupy as cp
12
+
13
+ from . import utils
14
+ from .package_ifc import Package
15
+
16
+ # Using the functional API is faster than setting a device context
17
+ if int(cp.__version__.split(".")[0]) >= 13:
18
+ _get_current_stream = cp.cuda.get_current_stream
19
+ else:
20
+
21
+ def _get_current_stream(device_id: int):
22
+ with utils.device_ctx(device_id):
23
+ stream = cp.cuda.get_current_stream()
24
+ return stream
25
+
26
+
27
+ # Monkey patch older versions of CuPy, so that Streams are hashable
28
+ # NOTE: We choose not to patch the BaseStream/_BaseStream class because of name change
29
+ if int(cp.__version__.split(".")[0]) < 11:
30
+ cp.cuda.Stream.__hash__ = lambda self: hash(self.ptr)
31
+ cp.cuda.ExternalStream.__hash__ = lambda self: hash(self.ptr)
32
+
33
+
34
+ class CupyPackage(Package[cp.cuda.Stream]):
35
+ @staticmethod
36
+ def get_current_stream(device_id: int):
37
+ return _get_current_stream(device_id)
38
+
39
+ @staticmethod
40
+ def to_stream_pointer(stream: cp.cuda.Stream) -> int:
41
+ return stream.ptr
42
+
43
+ @staticmethod
44
+ def to_stream_context(stream: cp.cuda.Stream):
45
+ return stream
46
+
47
+ @staticmethod
48
+ def create_external_stream(device_id: int, stream_ptr: int) -> cp.cuda.ExternalStream:
49
+ return cp.cuda.ExternalStream(stream_ptr)
@@ -0,0 +1,31 @@
1
+ # Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ Interface to Torch operations.
7
+ """
8
+
9
+ __all__ = ["TorchPackage"]
10
+
11
+ import torch
12
+
13
+ from .package_ifc import Package
14
+
15
+
16
+ class TorchPackage(Package[torch.cuda.Stream]):
17
+ @staticmethod
18
+ def get_current_stream(device_id: int) -> torch.cuda.Stream:
19
+ return torch.cuda.current_stream(device=device_id)
20
+
21
+ @staticmethod
22
+ def to_stream_pointer(stream: torch.cuda.Stream) -> int: # type: ignore[override]
23
+ return stream.cuda_stream
24
+
25
+ @staticmethod
26
+ def to_stream_context(stream: torch.cuda.Stream) -> torch.cuda.StreamContext: # type: ignore[override]
27
+ return torch.cuda.stream(stream)
28
+
29
+ @staticmethod
30
+ def create_external_stream(device_id: int, stream_ptr: int):
31
+ return torch.cuda.ExternalStream(stream_ptr, device=device_id)
@@ -0,0 +1,14 @@
1
+ # Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ Create map from package names to package interface objects.
7
+ """
8
+
9
+ __all__ = ["PACKAGE", "AnyStream", "StreamHolder"]
10
+
11
+ from .package_ifc import Package, AnyStream, StreamHolder
12
+ from .package_ifc_cuda import CUDAPackage
13
+
14
+ PACKAGE: dict[str, type[Package]] = {"cuda": CUDAPackage}
@@ -0,0 +1,143 @@
1
+ # Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ Interface to seamlessly use tensors (or ndarray-like objects) from different libraries.
7
+ """
8
+
9
+ from __future__ import annotations # allows typehint of class methods to return the self class
10
+
11
+ from abc import ABC, abstractmethod
12
+ from collections.abc import Callable, Sequence
13
+ from typing import Any, Literal, Generic, Protocol, TypeVar
14
+ from types import ModuleType
15
+
16
+ from . import typemaps
17
+ from .package_ifc import StreamHolder
18
+
19
+
20
+ class AnyTensor(Protocol):
21
+ """Any supported external tensor object such as NumPy, CuPy, and PyTorch arrays."""
22
+
23
+ # This empty protocol class is just a placeholder to make type checking more helpful.
24
+ # Since the class is empty, it doesn't assume anything is true about the implementation
25
+ # classes. Used to type hint any object that TensorHolder knows how to wrap.
26
+ pass
27
+
28
+
29
+ """
30
+ A generic type for the third-party Tensor which a given TensorHolder implementation wraps
31
+ around.
32
+ """
33
+ Tensor = TypeVar("Tensor")
34
+
35
+
36
+ class TensorHolder(ABC, Generic[Tensor]):
37
+ """
38
+ A simple wrapper type for tensors to make the API package-agnostic.
39
+
40
+ Methods of a TensorHolder should always return a TensorHolder instead of a Tensor (one
41
+ of the wrapped classes) in order to prevent implementation details from the various
42
+ Tensor implementations from leaking into nvmath-python.
43
+
44
+ Tensors from the user should be immediately wrapped with a TensorHolder and should
45
+ remain wrapped until just before returning to the user.
46
+ """
47
+
48
+ name: str
49
+ module: ModuleType
50
+ name_to_dtype: dict[str, Any]
51
+
52
+ def __init__(self, tensor: Tensor):
53
+ self.tensor: Tensor = tensor
54
+
55
+ @property
56
+ @abstractmethod
57
+ def data_ptr(self) -> int:
58
+ raise NotImplementedError
59
+
60
+ @property
61
+ @abstractmethod
62
+ def device(self) -> Literal["cuda"] | Literal["cpu"]:
63
+ """The type of the device which stores the tensor."""
64
+ raise NotImplementedError
65
+
66
+ @property
67
+ @abstractmethod
68
+ def device_id(self) -> int | Literal["cpu"]:
69
+ """The device ordinal of the device storing the tensor."""
70
+ raise NotImplementedError
71
+
72
+ @property
73
+ @abstractmethod
74
+ def dtype(self) -> str:
75
+ """Name of the data type"""
76
+ raise NotImplementedError
77
+
78
+ @classmethod
79
+ @abstractmethod
80
+ def empty(cls, shape: Sequence[int], device_id: int | Literal["cpu"], **context: Any) -> TensorHolder[Tensor]:
81
+ """Create an empty TensorHolder of the specified shape and data type."""
82
+ raise NotImplementedError
83
+
84
+ @property
85
+ @abstractmethod
86
+ def shape(self) -> Sequence[int]:
87
+ raise NotImplementedError
88
+
89
+ @property
90
+ @abstractmethod
91
+ def size(self) -> int:
92
+ raise NotImplementedError
93
+
94
+ @property
95
+ @abstractmethod
96
+ def strides(self) -> Sequence[int]:
97
+ raise NotImplementedError
98
+
99
+ @abstractmethod
100
+ def to(self, device_id: int | Literal["cpu"], stream_holder: StreamHolder | None) -> TensorHolder:
101
+ """Copy the TensorHolder to a different device.
102
+
103
+ No copy is performed if the TensorHolder is already on the requested device.
104
+ """
105
+ raise NotImplementedError
106
+
107
+ @abstractmethod
108
+ def copy_(self, src: TensorHolder, stream_holder: StreamHolder | None) -> None:
109
+ """Overwrite self.tensor (in-place) with a copy of src."""
110
+ raise NotImplementedError
111
+
112
+ @staticmethod
113
+ def create_name_dtype_map(conversion_function: Callable[[str], Any], exception_type: type[Exception]) -> dict[str, Any]:
114
+ """
115
+ Create a map between CUDA data type names and the corresponding package dtypes for
116
+ supported data types.
117
+ """
118
+ names = typemaps.NAME_TO_DATA_TYPE.keys()
119
+ name_to_dtype = {}
120
+ for name in names:
121
+ try:
122
+ name_to_dtype[name] = conversion_function(name)
123
+ except exception_type:
124
+ pass
125
+ return name_to_dtype
126
+
127
+ @abstractmethod
128
+ def istensor(self) -> bool:
129
+ """Return whether self.tensor is the expected type."""
130
+ raise NotImplementedError
131
+
132
+ @abstractmethod
133
+ def reshape(self, shape: Sequence[int], *, copy: bool | None = None) -> TensorHolder[Tensor]:
134
+ """Reshapes tensor without changing its data.
135
+
136
+ Args:
137
+ shape: a new shape compatible with the original shape.
138
+
139
+ copy (Optional[bool]): whether or not to copy the input tensor. If True, the
140
+ function must always copy. If False, the function must never copy. If None,
141
+ the function must avoid copying, if possible, and may copy otherwise.
142
+ """
143
+ raise NotImplementedError