Skip to content

Commit bb27be4

Browse files
authored
Merge pull request #64 from NVIDIA/release/v1.0.0
release: nvmath-python-1.0.0
2 parents 48f5b64 + 726ed65 commit bb27be4

738 files changed

Lines changed: 82994 additions & 18497 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ __pycache__
99
docs/_build
1010
docs/sphinx/**/generated
1111
docs/sphinx/generated
12+
internal/internal_docs/_build
13+
internal/internal_docs/sphinx/**/generated
1214
dist
1315
build
1416
wheelhouse

.pre-commit-config.yaml

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,35 @@ repos:
4141
- repo: https://github.com/pre-commit/mirrors-mypy
4242
rev: "v1.18.1"
4343
hooks:
44+
# We split the mypy run into two separate hooks to avoid a "Duplicate module named..."
45+
# error. Some files in examples/device/numba_cuda_mlir/ have exactly the same name
46+
# as files in examples/device/, and mypy treats them as identical modules if
47+
# passed together.
4448
- id: mypy
45-
# Envorce only one source of configuration.
49+
name: mypy (main and examples)
50+
# Enforce only one source of configuration.
4651
args: ["--config-file", "pyproject.toml"]
52+
exclude: ^examples/device/numba_cuda_mlir/
53+
additional_dependencies:
54+
- cuda-core
55+
- cuda-bindings>=12.9.2,<13
56+
- cupy-cuda12x
57+
- mpi4py>=4.1.0
58+
- numba
59+
- numba-cuda
60+
- numpy
61+
- pytest
62+
- scipy
63+
- torch
64+
- types-cffi
65+
- invoke
66+
- cython>=3.0.4,!=3.1.0,!=3.1.1
67+
- tomli
68+
- id: mypy
69+
name: mypy (numba-cuda-mlir examples)
70+
# Enforce only one source of configuration.
71+
args: ["--config-file", "pyproject.toml"]
72+
files: ^examples/device/numba_cuda_mlir/
4773
additional_dependencies:
4874
- cuda-core
4975
- cuda-bindings>=12.9.2,<13
@@ -106,6 +132,15 @@ repos:
106132
# Ignore old internal README that will not be rendered as docs page
107133
args: ["--fix", "--ignore", "internal/gtc2024/README.md"]
108134

135+
- repo: local
136+
hooks:
137+
- id: check-lock-urls
138+
name: Check that lock files use internal registries
139+
entry: python .ci/check_locks.py
140+
language: python
141+
files: ^\.ci/locks/.*\.toml$
142+
pass_filenames: true
143+
109144
- repo: https://github.com/sphinx-contrib/sphinx-lint
110145
rev: v1.0.0
111146
hooks:

README.md

Lines changed: 81 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ functionality that is missing from those frameworks.
1515
## Some Examples
1616

1717
Below are a few representative examples showcasing the three main categories of
18-
features nvmath-python offers: host, device, and distributed APIs.
18+
features nvmath-python offers: host, distributed host, and device APIs.
1919

2020
### Host APIs
2121

@@ -102,6 +102,83 @@ s = cp.fft.fftn(a, axes=[-1], norm="ortho")
102102
assert cp.allclose(r, s)
103103
```
104104

105+
### Distributed Host APIs
106+
107+
Distributed Host APIs are called from host code but execute on a distributed
108+
(multi-node multi-GPU) system. The following example shows the use of the
109+
function-form distributed FFT with CuPy ndarrays:
110+
111+
```python
112+
import cupy as cp
113+
from mpi4py import MPI
114+
115+
import nvmath.distributed
116+
from nvmath.distributed.distribution import Slab
117+
118+
# Initialize nvmath.distributed.
119+
comm = MPI.COMM_WORLD
120+
rank = comm.Get_rank()
121+
nranks = comm.Get_size()
122+
device_id = rank % cp.cuda.runtime.getDeviceCount()
123+
nvmath.distributed.initialize(device_id, comm, backends=["nvshmem"])
124+
125+
# The global 3-D FFT size is (512, 256, 512).
126+
# In this example, the input data is distributed across processes according to
127+
# the cuFFTMp Slab distribution on the X axis.
128+
shape = 512 // nranks, 256, 512
129+
130+
# cuFFTMp uses the NVSHMEM PGAS model for distributed computation, which requires GPU
131+
# operands to be on the symmetric heap.
132+
a = nvmath.distributed.allocate_symmetric_memory(shape, cp, dtype=cp.complex128)
133+
# a is a cupy ndarray and can be operated on using in-place cupy operations.
134+
with cp.cuda.Device(device_id):
135+
a[:] = (
136+
cp.random.rand(*shape, dtype=cp.float64)
137+
+ 1j * cp.random.rand(*shape, dtype=cp.float64)
138+
)
139+
140+
# Forward FFT.
141+
# In this example, the forward FFT operand is distributed according
142+
# to Slab.X distribution. With redistribute=False, the FFT result will be
143+
# distributed according to Slab.Y distribution.
144+
b = nvmath.distributed.fft.fft(a, distribution=Slab.X, options={"redistribute": False})
145+
146+
# Distributed FFT performs computations in-place. The result is stored in the same
147+
# buffer as operand a. Note, however, that operand b has a different shape (due
148+
# to Slab.Y distribution).
149+
if rank == 0:
150+
print(f"Shape of a on rank {rank} is {a.shape}")
151+
print(f"Shape of b on rank {rank} is {b.shape}")
152+
153+
# Inverse FFT.
154+
# Recall from previous transform that the inverse FFT operand is distributed according
155+
# to Slab.Y. With redistribute=False, the inverse FFT result will be distributed
156+
# according to Slab.X distribution.
157+
c = nvmath.distributed.fft.ifft(b, distribution=Slab.Y, options={"redistribute": False})
158+
159+
# The shape of c is the same as a (due to Slab.X distribution). Once again, note that
160+
# a, b and c are sharing the same symmetric memory buffer (distributed FFT operations
161+
# are in-place).
162+
if rank == 0:
163+
print(f"Shape of c on rank {rank} is {c.shape}")
164+
165+
# Synchronize the default stream
166+
with cp.cuda.Device(device_id):
167+
cp.cuda.get_current_stream().synchronize()
168+
169+
if rank == 0:
170+
print(f"Input type = {type(a)}, device = {a.device}")
171+
print(f"FFT output type = {type(b)}, device = {b.device}")
172+
print(f"IFFT output type = {type(c)}, device = {c.device}")
173+
174+
# GPU operands on the symmetric heap are not garbage-collected and the user is
175+
# responsible for freeing any that they own (this deallocation is a collective
176+
# operation that must be called by all processes at the same point in the execution).
177+
# All cuFFTMp operations are inplace (a, b, and c share the same memory buffer), so
178+
# we take care to only free the buffer once.
179+
nvmath.distributed.free_symmetric_memory(a)
180+
```
181+
105182
### Device-side APIs
106183

107184
nvmath-python exposes NVIDIA's device-side (Dx) APIs. This allows developers to call NVIDIA
@@ -195,89 +272,12 @@ if __name__ == "__main__":
195272
main()
196273
```
197274

198-
### Distributed APIs
199-
200-
Distributed APIs are called from host code but execute on a distributed
201-
(multi-node multi-GPU) system. The following example shows the use of the
202-
function-form distributed FFT with CuPy ndarrays:
203-
204-
```python
205-
import cupy as cp
206-
from mpi4py import MPI
207-
208-
import nvmath.distributed
209-
from nvmath.distributed.distribution import Slab
210-
211-
# Initialize nvmath.distributed.
212-
comm = MPI.COMM_WORLD
213-
rank = comm.Get_rank()
214-
nranks = comm.Get_size()
215-
device_id = rank % cp.cuda.runtime.getDeviceCount()
216-
nvmath.distributed.initialize(device_id, comm, backends=["nvshmem"])
217-
218-
# The global 3-D FFT size is (512, 256, 512).
219-
# In this example, the input data is distributed across processes according to
220-
# the cuFFTMp Slab distribution on the X axis.
221-
shape = 512 // nranks, 256, 512
222-
223-
# cuFFTMp uses the NVSHMEM PGAS model for distributed computation, which requires GPU
224-
# operands to be on the symmetric heap.
225-
a = nvmath.distributed.allocate_symmetric_memory(shape, cp, dtype=cp.complex128)
226-
# a is a cupy ndarray and can be operated on using in-place cupy operations.
227-
with cp.cuda.Device(device_id):
228-
a[:] = (
229-
cp.random.rand(*shape, dtype=cp.float64)
230-
+ 1j * cp.random.rand(*shape, dtype=cp.float64)
231-
)
232-
233-
# Forward FFT.
234-
# In this example, the forward FFT operand is distributed according
235-
# to Slab.X distribution. With reshape=False, the FFT result will be
236-
# distributed according to Slab.Y distribution.
237-
b = nvmath.distributed.fft.fft(a, distribution=Slab.X, options={"reshape": False})
238-
239-
# Distributed FFT performs computations in-place. The result is stored in the same
240-
# buffer as operand a. Note, however, that operand b has a different shape (due
241-
# to Slab.Y distribution).
242-
if rank == 0:
243-
print(f"Shape of a on rank {rank} is {a.shape}")
244-
print(f"Shape of b on rank {rank} is {b.shape}")
245-
246-
# Inverse FFT.
247-
# Recall from previous transform that the inverse FFT operand is distributed according
248-
# to Slab.Y. With reshape=False, the inverse FFT result will be distributed according
249-
# to Slab.X distribution.
250-
c = nvmath.distributed.fft.ifft(b, distribution=Slab.Y, options={"reshape": False})
251-
252-
# The shape of c is the same as a (due to Slab.X distribution). Once again, note that
253-
# a, b and c are sharing the same symmetric memory buffer (distributed FFT operations
254-
# are in-place).
255-
if rank == 0:
256-
print(f"Shape of c on rank {rank} is {c.shape}")
257-
258-
# Synchronize the default stream
259-
with cp.cuda.Device(device_id):
260-
cp.cuda.get_current_stream().synchronize()
261-
262-
if rank == 0:
263-
print(f"Input type = {type(a)}, device = {a.device}")
264-
print(f"FFT output type = {type(b)}, device = {b.device}")
265-
print(f"IFFT output type = {type(c)}, device = {c.device}")
266-
267-
# GPU operands on the symmetric heap are not garbage-collected and the user is
268-
# responsible for freeing any that they own (this deallocation is a collective
269-
# operation that must be called by all processes at the same point in the execution).
270-
# All cuFFTMp operations are inplace (a, b, and c share the same memory buffer), so
271-
# we take care to only free the buffer once.
272-
nvmath.distributed.free_symmetric_memory(a)
273-
```
274-
275275
## License
276276

277277
All files hosted in this repository are subject to the [Apache 2.0](./LICENSE) license.
278278

279279
## Disclaimer
280280

281-
nvmath-python is in a Beta state. Beta products may not be fully functional, may contain
282-
errors or design flaws, and may be changed at any time without notice. We appreciate your
283-
feedback to improve and iterate on our Beta products.
281+
nvmath-python contains features marked as experimental. Experimental features may not be
282+
fully functional, may contain errors or design flaws, and may be changed at any time without
283+
notice. We appreciate your feedback to improve and iterate on our experimental features.

docs/sphinx/_ext/experimental.css

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/* Experimental API styling */
2+
3+
/* Shared vars for every experimental left bar. */
4+
:root {
5+
--experimental-bar-width: 6px;
6+
--experimental-bar-pad: 10px;
7+
}
8+
9+
/* Add left border to the entire method/function/class/attribute marked experimental. */
10+
/* This covers both the signature and docstring. */
11+
/* `attribute` covers dataclass option fields, which napoleon renders
12+
/* as `.. attribute::` blocks rather than Parameters rows. */
13+
/* Uses theme's attention color variables that automatically adapt to light/dark mode. */
14+
/* The negative margin equals border + padding so the content keeps its normal */
15+
/* alignment with non-experimental siblings; only the bar falls into the left */
16+
/* gutter. It is derived from the bar constants so it stays in sync. */
17+
dl.py.method.experimental,
18+
dl.py.function.experimental,
19+
dl.py.class.experimental,
20+
dl.py.attribute.experimental {
21+
border-left: var(--experimental-bar-width) solid var(--pst-color-attention-bg);
22+
padding-left: var(--experimental-bar-pad);
23+
margin-left: calc(-1 * (var(--experimental-bar-width) + var(--experimental-bar-pad)));
24+
}
25+
26+
/* Style the experimental marker box. */
27+
/* This is a simple container created by .. experimental:: directive. */
28+
/* Using CSS variables from the theme's attention admonition style. */
29+
/* These variables automatically change with the theme switcher. */
30+
.experimental-marker {
31+
background-color: var(--pst-color-attention-bg);
32+
padding: 8px 12px;
33+
margin: 12px 0;
34+
border-radius: 4px;
35+
}
36+
37+
/* Style the paragraph inside the container. */
38+
.experimental-marker > p {
39+
color: var(--pst-color-attention-text);
40+
font-weight: 700;
41+
margin: 0;
42+
padding-left: 8px;
43+
font-size: 0.95em;
44+
}
45+
46+
/* The module banner names its module in a `code` span. Keep it monospace but */
47+
/* let it inherit the banner's attention colors instead of the theme's default */
48+
/* code background/border, which would clash with the banner fill. */
49+
.experimental-marker > p code {
50+
background-color: transparent;
51+
color: inherit;
52+
border: none;
53+
padding: 0;
54+
}
55+
56+
/* Highlight an experimental parameter row ("Parameters" list) or attribute row */
57+
/* (":ivar:"/"Variables" list). Both are set by mark_experimental_apis on the */
58+
/* row's block container -- the <li> for several entries, or the field body <dd> */
59+
/* when a lone entry collapses the list -- so the bar spans the whole row.
60+
/* (An attribute written as a ".. attribute::" object instead gets the object
61+
/* border via dl.py.attribute.experimental above.) */
62+
.experimental-param,
63+
.experimental-ivar {
64+
border-left: var(--experimental-bar-width) solid var(--pst-color-attention-bg);
65+
padding-left: var(--experimental-bar-pad);
66+
border-radius: 4px;
67+
}
68+
69+
/* Whole-module experimental: one bar spanning the entire module page. */
70+
/* Set by mark_experimental_apis on the `compound` div wrapping the page body. */
71+
/* Mirrors the per-entry treatment above (border-left + padding-left). */
72+
.experimental-module {
73+
border-left: var(--experimental-bar-width) solid var(--pst-color-attention-bg);
74+
padding-left: var(--experimental-bar-pad);
75+
}

0 commit comments

Comments
 (0)