From aa10649bf4bdf954d407c30e0f69c78076ce6884 Mon Sep 17 00:00:00 2001 From: "Mark J. Olah" Date: Thu, 13 Mar 2025 09:04:49 -0600 Subject: [PATCH] nvmath-python-0.3.0 --- .gitignore | 2 +- .markdownlint.yaml | 4 + .pre-commit-config.yaml | 4 +- .talismanrc | 69 +- MANIFEST.in | 5 + builder/__init__.py | 2 +- builder/pep517.py | 4 +- builder/utils.py | 2 +- docs/Makefile | 2 +- docs/sphinx/_static/switcher.json | 4 + docs/sphinx/bindings/index.rst | 4 +- docs/sphinx/conf.py | 55 +- docs/sphinx/device-apis/cufft.rst | 2 +- docs/sphinx/device-apis/index.rst | 1 + docs/sphinx/{ => host-apis}/fft/index.rst | 0 docs/sphinx/host-apis/index.rst | 20 + docs/sphinx/{ => host-apis}/linalg/index.rst | 22 + .../{host-utils.rst => host-apis/utils.rst} | 23 +- docs/sphinx/index.rst | 18 +- docs/sphinx/installation.rst | 74 +- docs/sphinx/overview.rst | 78 +- docs/sphinx/quickstart.rst | 9 +- docs/sphinx/release-notes.rst | 41 +- docs/sphinx/tutorials/linalg.rst | 11 + .../notebooks/matmul/01_introduction.nblink | 3 + .../notebooks/matmul/02_epilogs.nblink | 3 + .../matmul/03_backpropagation.nblink | 3 + .../tutorials/notebooks/matmul/04_fp8.nblink | 3 + examples/device/common.py | 2 +- examples/device/common_cupy.py | 2 +- examples/device/common_numba.py | 2 +- examples/device/cublasdx_batched_gemm_fp64.py | 2 +- .../device/cublasdx_blockdim_gemm_fp16.py | 2 +- .../device/cublasdx_fused_gemm_performance.py | 2 +- examples/device/cublasdx_gemm_fft.py | 2 +- examples/device/cublasdx_gemm_fft_fp16.py | 2 +- .../device/cublasdx_gemm_fft_performance.py | 2 +- examples/device/cublasdx_gemm_fusion.py | 2 +- examples/device/cublasdx_simple_gemm_cfp16.py | 2 +- examples/device/cublasdx_simple_gemm_fp32.py | 2 +- ...cublasdx_simple_gemm_leading_dimensions.py | 2 +- .../cublasdx_single_gemm_performance.py | 2 +- examples/device/cufftdx_autotuning.py | 2 +- examples/device/cufftdx_block_fft.py | 2 +- .../device/cufftdx_block_fft_performance.py | 2 +- .../cufftdx_block_fft_performance_many.py | 2 +- examples/device/cufftdx_convolution.py | 2 +- .../device/cufftdx_convolution_performance.py | 2 +- .../device/cufftdx_convolution_r2c_c2r.py | 2 +- ...nvolution_r2c_c2r_packed_fold_optimized.py | 2 +- examples/device/cufftdx_convolution_signal.py | 2 +- examples/device/cufftdx_fft_2d.py | 2 +- examples/device/cufftdx_fft_2d_r2c_c2r.py | 2 +- .../device/cufftdx_fft_2d_single_kernel.py | 2 +- .../device/cufftdx_fft_3d_box_single_block.py | 2 +- .../cufftdx_fft_3d_cube_single_block.py | 2 +- examples/device/cufftdx_helloworld.py | 2 +- examples/device/cufftdx_simple_fft_block.py | 2 +- .../device/cufftdx_simple_fft_block_c2r.py | 2 +- .../cufftdx_simple_fft_block_c2r_fp16.py | 2 +- .../device/cufftdx_simple_fft_block_half2.py | 2 +- .../device/cufftdx_simple_fft_block_r2c.py | 2 +- .../cufftdx_simple_fft_block_r2c_fp16.py | 2 +- .../device/cufftdx_simple_fft_block_shared.py | 2 +- examples/device/cufftdx_simple_fft_thread.py | 2 +- .../device/cufftdx_simple_fft_thread_fp16.py | 2 +- examples/device/curand_cufftdx_block_fft.py | 5 +- examples/device/curand_philox_uniform4.py | 3 +- examples/device/curand_scrambled_sobol64.py | 2 +- examples/device/curand_xorwow_uniform.py | 3 +- examples/fft/caching.py | 4 +- examples/fft/example01_cupy.py | 2 +- examples/fft/example01_cupy_layouts.py | 2 +- examples/fft/example01_numpy.py | 2 +- examples/fft/example01_numpy_cpu_execution.py | 2 +- examples/fft/example01_torch_complex32.py | 2 +- examples/fft/example02_stateful_cupy.py | 2 +- examples/fft/example02_stateful_torch.py | 2 +- examples/fft/example02_stateful_torch_cpu.py | 2 +- .../example02_stateful_torch_cpu_execution.py | 2 +- examples/fft/example03_options.py | 2 +- .../fft/example03_options_cpu_execution.py | 2 +- examples/fft/example04_logging_global.py | 2 +- examples/fft/example04_logging_user.py | 2 +- examples/fft/example05_stateful_inplace.py | 2 +- examples/fft/example05_stateful_reset.py | 2 +- examples/fft/example06_r2c.py | 2 +- examples/fft/example07_c2r.py | 2 +- examples/fft/example07_c2r_odd.py | 2 +- examples/fft/example08_cupy_inplace.py | 2 +- examples/fft/example08_numpy_inplace.py | 2 +- .../example08_numpy_inplace_cpu_execution.py | 2 +- examples/fft/example09_streams.py | 2 +- examples/fft/example10_memory_allocator.py | 2 +- examples/fft/example11_resource_mgmt.py | 2 +- ...example12_stateful_unsupported_fallback.py | 2 +- .../fft/example12_unsupported_fallback.py | 2 +- examples/fft/example13_cupy_mt_mgpu.py | 2 +- examples/fft/example13_numpy_mp_mgpu.py | 2 +- examples/fft/example14_caching.py | 2 +- .../fft/example15_cupy_nd_fft_benchmark.py | 2 +- .../fft/example16_cupy_nd_fft_benchmark.py | 2 +- examples/fft/example17_trunc.py | 2 +- examples/fft/example18_5D_trunc.py | 2 +- .../example19_convolution_epilog_callback.py | 2 +- ...le19_convolution_memory_layout_callback.py | 2 +- .../example19_convolution_prolog_callback.py | 2 +- examples/fft/fftn1.py | 2 +- examples/fft/fftn2.py | 2 +- examples/fft/truncation.py | 2 +- .../linalg/advanced/matmul/example01_cupy.py | 2 +- .../matmul/example01_cupy_complex64.py | 2 +- .../linalg/advanced/matmul/example01_numpy.py | 2 +- .../linalg/advanced/matmul/example01_torch.py | 4 +- .../advanced/matmul/example02_options.py | 2 +- .../matmul/example03_logging_global.py | 2 +- .../advanced/matmul/example03_logging_user.py | 2 +- .../matmul/example04_stateful_cupy.py | 2 +- .../matmul/example04_stateful_torch.py | 2 +- .../matmul/example04_stateful_torch_cpu.py | 2 +- .../matmul/example05_stateful_inplace.py | 12 +- .../matmul/example05_stateful_reset.py | 12 +- .../linalg/advanced/matmul/example06_gemm.py | 2 +- .../advanced/matmul/example07_batched_a.py | 2 +- .../advanced/matmul/example07_batched_a_b.py | 2 +- .../matmul/example08_batched_a_bcast_c.py | 4 +- .../advanced/matmul/example08_batched_a_c.py | 2 +- .../advanced/matmul/example09_epilog_bias.py | 2 +- .../matmul/example09_epilog_gelu_bias.py | 2 +- .../advanced/matmul/example10_epilog_dgelu.py | 2 +- .../advanced/matmul/example10_epilog_drelu.py | 2 +- .../matmul/example10_epilog_relu_aux.py | 2 +- .../matmul/example11_epilog_drelu_bgrad.py | 2 +- .../matmul/example12_epilog_bgrada.py | 2 +- .../matmul/example12_epilog_bgradb.py | 2 +- .../matmul/example13_epilog_stateful_reset.py | 12 +- .../advanced/matmul/example14_autotune.py | 2 +- .../matmul/example15_manual_tuning.py | 2 +- .../matmul/example16_reuse_algorithms.py | 2 +- .../linalg/advanced/matmul/example17_fp8.py | 41 + .../advanced/matmul/example18_fp8_types.py | 69 ++ .../advanced/matmul/example19_fp8_reset.py | 46 + .../example20_fp8_inplace_scale_change.py | 52 + .../advanced/matmul/example21_fp8_amax.py | 54 + .../matmul/example22_fp8_delayed_scaling.py | 64 + .../advanced/matmul/example23_fp8_epilog.py | 45 + .../matmul/example24_fp8_epilog_aux.py | 69 ++ .../linalg/advanced/matmul/example25_mxfp8.py | 61 + .../advanced/matmul/example26_mxfp8_d_out.py | 78 ++ .../matmul/example27_mxfp8_chaining.py | 61 + .../advanced/matmul/example28_mxfp8_epilog.py | 37 + .../advanced/matmul/example29_mxfp8_layout.py | 41 + notebooks/matmul/01_introduction.ipynb | 8 +- notebooks/matmul/02_epilogs.ipynb | 6 +- notebooks/matmul/03_backpropagation.ipynb | 6 +- notebooks/matmul/04_fp8.ipynb | 1077 +++++++++++++++++ nvmath/__init__.py | 19 +- nvmath/_internal/enum_utils.py | 4 +- nvmath/_internal/formatters.py | 2 +- nvmath/_internal/layout.py | 2 +- nvmath/_internal/mem_limit.py | 2 +- nvmath/_internal/package_ifc.py | 2 +- nvmath/_internal/package_ifc_cupy.py | 2 +- nvmath/_internal/package_ifc_torch.py | 2 +- nvmath/_internal/package_wrapper.py | 4 +- nvmath/_internal/tensor_ifc.py | 7 +- nvmath/_internal/tensor_ifc_cupy.py | 6 +- nvmath/_internal/tensor_ifc_numpy.py | 6 +- nvmath/_internal/tensor_ifc_torch.py | 6 +- nvmath/_internal/tensor_wrapper.py | 2 +- nvmath/_internal/typemaps.py | 26 +- nvmath/_internal/utils.py | 4 +- nvmath/_utils.py | 2 +- nvmath/bindings/__init__.py | 16 +- nvmath/bindings/_internal/cublas.pxd | 4 +- nvmath/bindings/_internal/cublasLt.pxd | 4 +- nvmath/bindings/_internal/cublasLt_linux.pyx | 4 +- .../bindings/_internal/cublasLt_windows.pyx | 4 +- nvmath/bindings/_internal/cublas_linux.pyx | 4 +- nvmath/bindings/_internal/cublas_windows.pyx | 4 +- nvmath/bindings/_internal/cufft.pxd | 4 +- nvmath/bindings/_internal/cufft_linux.pyx | 4 +- nvmath/bindings/_internal/cufft_windows.pyx | 4 +- nvmath/bindings/_internal/curand.pxd | 4 +- nvmath/bindings/_internal/curand_linux.pyx | 4 +- nvmath/bindings/_internal/curand_windows.pyx | 4 +- nvmath/bindings/_internal/cusolver.pxd | 2 +- nvmath/bindings/_internal/cusolverDn.pxd | 4 +- .../bindings/_internal/cusolverDn_linux.pyx | 4 +- .../bindings/_internal/cusolverDn_windows.pyx | 4 +- nvmath/bindings/_internal/cusolver_linux.pyx | 2 +- .../bindings/_internal/cusolver_windows.pyx | 2 +- nvmath/bindings/_internal/cusparse.pxd | 4 +- nvmath/bindings/_internal/cusparse_linux.pyx | 4 +- .../bindings/_internal/cusparse_windows.pyx | 4 +- nvmath/bindings/_internal/utils.pxd | 4 + nvmath/bindings/_internal/utils.pyx | 4 + nvmath/bindings/cublas.pxd | 4 +- nvmath/bindings/cublas.pyx | 4 +- nvmath/bindings/cublasLt.pxd | 5 +- nvmath/bindings/cublasLt.pyx | 35 +- nvmath/bindings/cufft.pxd | 16 +- nvmath/bindings/cufft.pyx | 4 +- nvmath/bindings/curand.pxd | 4 +- nvmath/bindings/curand.pyx | 14 +- nvmath/bindings/cusolver.pxd | 2 +- nvmath/bindings/cusolver.pyx | 2 +- nvmath/bindings/cusolverDn.pxd | 4 +- nvmath/bindings/cusolverDn.pyx | 4 +- nvmath/bindings/cusparse.pxd | 4 +- nvmath/bindings/cusparse.pyx | 4 +- nvmath/bindings/cycublas.pxd | 4 +- nvmath/bindings/cycublas.pyx | 4 +- nvmath/bindings/cycublasLt.pxd | 31 +- nvmath/bindings/cycublasLt.pyx | 4 +- nvmath/bindings/cycufft.pxd | 74 +- nvmath/bindings/cycufft.pyx | 4 +- nvmath/bindings/cycurand.pxd | 4 +- nvmath/bindings/cycurand.pyx | 4 +- nvmath/bindings/cycusolver.pxd | 2 +- nvmath/bindings/cycusolver.pyx | 2 +- nvmath/bindings/cycusolverDn.pxd | 4 +- nvmath/bindings/cycusolverDn.pyx | 4 +- nvmath/bindings/cycusparse.pxd | 4 +- nvmath/bindings/cycusparse.pyx | 4 +- nvmath/bindings/nvpl/__init__.py | 6 +- nvmath/bindings/nvpl/_internal/fft.pxd | 2 +- nvmath/bindings/nvpl/_internal/fft_linux.pyx | 2 +- nvmath/bindings/nvpl/cyfft.pxd | 2 +- nvmath/bindings/nvpl/cyfft.pyx | 2 +- nvmath/bindings/nvpl/fft.pxd | 2 +- nvmath/bindings/nvpl/fft.pyx | 2 +- nvmath/device/__init__.py | 13 +- nvmath/device/caching.py | 2 +- nvmath/device/common.py | 4 +- nvmath/device/common_cpp.py | 2 +- nvmath/device/common_cuda.py | 11 +- nvmath/device/common_mathdx.py | 2 +- nvmath/device/common_numba.py | 2 +- nvmath/device/cublasdx.py | 7 +- nvmath/device/cublasdx_backend.py | 2 +- nvmath/device/cublasdx_numba.py | 2 +- nvmath/device/cufftdx.py | 21 +- nvmath/device/cufftdx_backend.py | 2 +- nvmath/device/cufftdx_db.py | 2 +- nvmath/device/cufftdx_numba.py | 2 +- nvmath/device/cufftdx_workspace.py | 2 +- nvmath/device/curand_kernel.py | 2 +- nvmath/device/nvrtc.py | 2 +- nvmath/device/patch.py | 5 +- nvmath/device/random.py | 2 +- nvmath/device/random_helpers.py | 2 +- nvmath/device/random_states.py | 10 +- nvmath/device/types.py | 2 +- nvmath/device/vector_types_numba.py | 2 +- nvmath/fft/__init__.py | 8 +- nvmath/fft/_configuration.py | 19 +- nvmath/fft/_exec_utils.py | 4 +- nvmath/fft/_helpers.py | 4 +- nvmath/fft/fft.py | 20 +- nvmath/linalg/__init__.py | 8 +- nvmath/linalg/_internal/__init__.py | 4 +- nvmath/linalg/_internal/algo_cap_ifc.py | 2 +- nvmath/linalg/_internal/algo_config_ifc.py | 2 +- nvmath/linalg/_internal/enum_to_tuples.py | 2 +- nvmath/linalg/_internal/epilog_protocol.py | 36 +- nvmath/linalg/_internal/matmul_desc_ifc.py | 9 +- nvmath/linalg/_internal/matmul_pref_ifc.py | 10 +- nvmath/linalg/_internal/matrix_layout_ifc.py | 6 +- nvmath/linalg/_internal/typemaps.py | 69 +- nvmath/linalg/_internal/utils.py | 2 +- nvmath/linalg/advanced/__init__.py | 9 +- nvmath/linalg/advanced/_algorithmmod.py | 3 +- nvmath/linalg/advanced/_configuration.py | 113 +- .../advanced/helpers/__init__.py} | 2 +- nvmath/linalg/advanced/helpers/matmul.py | 173 +++ nvmath/linalg/advanced/matmulmod.py | 595 +++++++-- nvmath/memory.py | 2 +- pyproject.toml | 13 +- requirements/README.md | 1 + requirements/pip/docs.txt | 4 + requirements/pip/torch-cu12-nightly.txt | 5 + setup.py | 8 +- tests/conftest.py | 3 +- tests/docstring_tests/test_docstrings.py | 4 + .../device_tests/test_device_samples.py | 4 +- .../fft_tests/test_fft_samples.py | 4 +- .../test_advanced_matmul_samples.py | 38 +- tests/example_tests/test_utils.py | 2 +- tests/nvmath_tests/device/cpp_conv.py | 2 +- tests/nvmath_tests/device/cpp_gemm_batched.py | 2 +- tests/nvmath_tests/device/cpp_gemm_loop.py | 2 +- .../device/curand/compiled_apis.py | 2 +- .../device/curand/distributions.py | 144 +-- .../nvmath_tests/device/curand/generators.py | 3 +- .../nvmath_tests/device/curand/test_random.py | 43 +- tests/nvmath_tests/device/curand/utils.py | 14 +- tests/nvmath_tests/device/helpers.py | 6 +- tests/nvmath_tests/device/helpers_cpp.py | 13 +- tests/nvmath_tests/device/helpers_numba.py | 2 +- tests/nvmath_tests/device/numba_conv.py | 2 +- .../nvmath_tests/device/numba_gemm_batched.py | 3 +- tests/nvmath_tests/device/numba_gemm_loop.py | 2 +- .../device/test_cublasdx_generic.py | 2 +- .../device/test_cublasdx_numba.py | 3 +- .../device/test_cublasdx_numba_perf.py | 2 +- .../device/test_cufftdx_generic.py | 2 +- .../nvmath_tests/device/test_cufftdx_numba.py | 2 +- .../device/test_cufftdx_numba_perf.py | 2 +- .../device/test_vector_types_numba.py | 2 +- .../nvmath_tests/fft/test_default_backend.py | 2 +- .../fft/test_fft_with_hypothesis.py | 6 +- tests/nvmath_tests/fft/test_lto_callbacks.py | 68 +- tests/nvmath_tests/fft/test_perf.py | 2 +- tests/nvmath_tests/fft/test_perf_2d.py | 2 +- tests/nvmath_tests/fft/test_perf_4-5d.py | 5 +- tests/nvmath_tests/fft/test_stateful.py | 4 +- tests/nvmath_tests/fft/test_stateless_1d.py | 30 +- tests/nvmath_tests/fft/test_stateless_nd.py | 2 +- tests/nvmath_tests/fft/utils/axes_utils.py | 2 +- tests/nvmath_tests/fft/utils/check_helpers.py | 2 +- tests/nvmath_tests/fft/utils/common_axes.py | 2 +- .../nvmath_tests/fft/utils/input_fixtures.py | 12 +- .../nvmath_tests/fft/utils/support_matrix.py | 2 +- tests/nvmath_tests/helpers.py | 2 +- .../linalg/advanced/matmul/fp8_utils.py | 264 ++++ .../linalg/advanced/matmul/test_epilog.py | 90 +- .../linalg/advanced/matmul/test_fp8.py | 647 ++++++++++ .../advanced/matmul/test_fp8_epilogs.py | 310 +++++ .../linalg/advanced/matmul/test_fp8_utils.py | 88 ++ .../linalg/advanced/matmul/test_ifc.py | 37 +- .../linalg/advanced/matmul/test_input.py | 39 +- .../matmul/test_matmul_with_hypothesis.py | 128 +- .../linalg/advanced/matmul/test_mxfp8.py | 788 ++++++++++++ .../linalg/advanced/matmul/test_options.py | 14 +- .../linalg/advanced/matmul/test_perf.py | 2 +- .../linalg/advanced/matmul/test_planning.py | 86 +- .../linalg/advanced/matmul/test_reset.py | 5 +- .../linalg/advanced/matmul/utils.py | 55 +- 339 files changed, 6294 insertions(+), 967 deletions(-) create mode 100644 MANIFEST.in rename docs/sphinx/{ => host-apis}/fft/index.rst (100%) create mode 100644 docs/sphinx/host-apis/index.rst rename docs/sphinx/{ => host-apis}/linalg/index.rst (63%) rename docs/sphinx/{host-utils.rst => host-apis/utils.rst} (64%) create mode 100644 docs/sphinx/tutorials/linalg.rst create mode 100644 docs/sphinx/tutorials/notebooks/matmul/01_introduction.nblink create mode 100644 docs/sphinx/tutorials/notebooks/matmul/02_epilogs.nblink create mode 100644 docs/sphinx/tutorials/notebooks/matmul/03_backpropagation.nblink create mode 100644 docs/sphinx/tutorials/notebooks/matmul/04_fp8.nblink create mode 100644 examples/linalg/advanced/matmul/example17_fp8.py create mode 100644 examples/linalg/advanced/matmul/example18_fp8_types.py create mode 100644 examples/linalg/advanced/matmul/example19_fp8_reset.py create mode 100644 examples/linalg/advanced/matmul/example20_fp8_inplace_scale_change.py create mode 100644 examples/linalg/advanced/matmul/example21_fp8_amax.py create mode 100644 examples/linalg/advanced/matmul/example22_fp8_delayed_scaling.py create mode 100644 examples/linalg/advanced/matmul/example23_fp8_epilog.py create mode 100644 examples/linalg/advanced/matmul/example24_fp8_epilog_aux.py create mode 100644 examples/linalg/advanced/matmul/example25_mxfp8.py create mode 100644 examples/linalg/advanced/matmul/example26_mxfp8_d_out.py create mode 100644 examples/linalg/advanced/matmul/example27_mxfp8_chaining.py create mode 100644 examples/linalg/advanced/matmul/example28_mxfp8_epilog.py create mode 100644 examples/linalg/advanced/matmul/example29_mxfp8_layout.py create mode 100644 notebooks/matmul/04_fp8.ipynb rename nvmath/{_version.py => linalg/advanced/helpers/__init__.py} (72%) create mode 100644 nvmath/linalg/advanced/helpers/matmul.py create mode 100644 requirements/pip/torch-cu12-nightly.txt create mode 100644 tests/nvmath_tests/linalg/advanced/matmul/fp8_utils.py create mode 100644 tests/nvmath_tests/linalg/advanced/matmul/test_fp8.py create mode 100644 tests/nvmath_tests/linalg/advanced/matmul/test_fp8_epilogs.py create mode 100644 tests/nvmath_tests/linalg/advanced/matmul/test_fp8_utils.py create mode 100644 tests/nvmath_tests/linalg/advanced/matmul/test_mxfp8.py diff --git a/.gitignore b/.gitignore index 419f809..a58b87a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ __pycache__ *.so *.pyd docs/_build -docs/sphinx/*/generated +docs/sphinx/**/generated docs/sphinx/generated dist build diff --git a/.markdownlint.yaml b/.markdownlint.yaml index 859154b..56025fb 100644 --- a/.markdownlint.yaml +++ b/.markdownlint.yaml @@ -1,3 +1,7 @@ +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + MD013: line_length: 92 code_block_line_length: 88 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 740be91..30a16dd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,6 @@ -# Copyright (c) 2024, NVIDIA CORPORATION. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 repos: - repo: https://github.com/pre-commit/pre-commit-hooks diff --git a/.talismanrc b/.talismanrc index 4b8d727..960fa89 100644 --- a/.talismanrc +++ b/.talismanrc @@ -18,5 +18,72 @@ fileignoreconfig: checksum: 01022d56aafb7c98d5af05a3e9e87ce4d267781def6f1844470fd4cd59d6b26b - filename: nvmath/device/random.py checksum: c534d9a475521cfcbfa6b048904f8495ff70e2a9ccdf3f2710e050cf75fafa35 + +- filename: .ci/pipenv/manylinux_2_28_x86_64-py311-test-sysctk12-Pipfile.lock + checksum: ac3e74b0d9d8e36c9400aaccda328a23eae6abb09b39813d3767fcca4f7314c9 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py311-test-sysctk11-Pipfile.lock + checksum: 7d7fe899d77a9b3cddd67b7ad6cedd3b0fd508e403dc750ad8d4b186f3e0e470 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py310-test-cu12-cpu-Pipfile.lock + checksum: dc0f70918f75d9a336d748eade983ff4b46ad03149565e0740efe8a4aadfdc10 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py310-test-cu12-dx-Pipfile.lock + checksum: 3bc632416be184605b6dcb3c1ec28af1e26e68df6f3232ad40a02a4091153a0a +- filename: .ci/pipenv/manylinux_2_28_x86_64-py312-test-cpu-Pipfile.lock + checksum: f11933df76dcc98ae3b25e5f356cc9060afdd9a838cf705a4ceca6bdd7161d01 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py311-test-sysctk12-dx-Pipfile.lock + checksum: 357c92fecf447f5640da599d25def0ac04490b03adc859798b2eba937a546f09 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py312-build-Pipfile.lock + checksum: 224516e0451196831d93512a6ff9e26d8dee14b83064270ce6cbe9567dcb5753 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py312-test-cu12-Pipfile.lock + checksum: 61d43b09f08b7f6e965194ade06c41896827cd5868c3d64e95b4524e3a1d98b9 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py310-test-cpu-Pipfile.lock + checksum: 73cfd9f66cfa1c7252cc16904b90d79faf7e06cd27b0fe653fd290e1c20a819c +- filename: .ci/pipenv/manylinux_2_28_x86_64-py312-test-cu12-cpu-Pipfile.lock + checksum: 88c76a2fd790a1f20f0bd68fd47c30cd99df07603ff8e82353855e18ffc16e75 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py312-test-cu12-dx-Pipfile.lock + checksum: 8a0d52aff956ac7f241e09b79af3ca5bddcf0aa040476a861f7a499a5681c410 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py310-test-cu11-Pipfile.lock + checksum: 7333ea9adfb7d931a5a4f4056e26deab90ded9caa054ff7baadc03722577b2d6 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py310-build-Pipfile.lock + checksum: 7d9895b83fe7051b9a0fb146a8f47cb25f87a8deccf4862400941f7c61196ed4 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py312-test-cu12-dx-torch-Pipfile.lock + checksum: 1e3fce2ab1065d2feef0e714a06dfe172033c10cade32de030749ee466b45423 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py312-test-cu11-Pipfile.lock + checksum: 555f9e4ba8b76f3ea912e1df46fee0ed5a0a809b01cceb9e79abe5138ad6e1c0 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py311-test-docstrings-Pipfile.lock + checksum: 2dc4d248779a72d1aad7b7119f0eae9cf5430f58b60cd84a1eb5b28dc6602bd9 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py311-test-cpu-Pipfile.lock + checksum: d5a24df09c39349a868443a7da14840eb0d503132402cc11d916c0928eb41286 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py310-test-cu11-torch-Pipfile.lock + checksum: 14172b0e1f856fbaeb3075613bbdc8d08fed7db84c8017e914656d0f41d1c2bf +- filename: .ci/pipenv/manylinux_2_28_x86_64-py312-test-sysctk12-dx-Pipfile.lock + checksum: f4ee267a48ed091c1daf92ab57a6ee65a62182e531ac3f6d351ff379a94e03ef +- filename: .ci/pipenv/manylinux_2_28_x86_64-py310-test-cu12-dx-torch-Pipfile.lock + checksum: fe0b0af438bc668bb1ecfeee126cb5d5e1aaa3ab4997bdb88191c65e8e70fcb2 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py311-test-cu12-cpu-Pipfile.lock + checksum: 9a26295b25c524d38ee32dc15225a60a1f78a42c0fbe831881603c4528c3b79d +- filename: .ci/pipenv/manylinux_2_28_x86_64-py310-test-sysctk11-Pipfile.lock + checksum: 15710d3872d0ff5ad83750e0c15f5d05128a9b2e131f3e91a645f78e6651f450 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py312-test-sysctk11-Pipfile.lock + checksum: 27b5fed2e2beac1530ca3c7a2e14ce058ba0fbbc58585775c12c9f1f7b479dbc +- filename: .ci/pipenv/manylinux_2_28_x86_64-py311-test-cu12-Pipfile.lock + checksum: eeeb2c572ffe4cf2f5c84f9e3ff6a66f6dfbe54ce0523fd13a309fb8151e061e +- filename: .ci/pipenv/manylinux_2_28_x86_64-py310-test-sysctk12-Pipfile.lock + checksum: a341c44c5bcfabbf8faab106dfe4d331eef55e9b4895b34d6d26b4ea975579b2 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py310-test-sysctk12-dx-Pipfile.lock + checksum: e0fb537be5bde7c0a52c551198fe21085ffac9bed1b30700afd5e05659506fb3 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py310-test-cu12-Pipfile.lock + checksum: a42cbc8a03f5a82494b44226903cee90dfc4208320385b6eccfbc72cc8e508f7 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py311-test-cu11-torch-Pipfile.lock + checksum: 64ca8c7987f08a2de952a99833010f14a7180948ba23b46c17fb04c35cfc9ba1 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py311-test-cu11-Pipfile.lock + checksum: d88bb28ca54a8f79d3ce4ae0575658de62c4fbd49048f8844a740372b2483e82 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py311-build-Pipfile.lock + checksum: 68a465110297077c4e07616dc055b73a5e37ad412d9c5ca4bb5800024d3fa273 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py311-test-cu12-dx-Pipfile.lock + checksum: 669ce9cf12b07e98b1955008ea90fbeb333a087269ed05b5c5319f4ea9c5988b +- filename: .ci/pipenv/manylinux_2_28_x86_64-py311-test-cu12-dx-torch-Pipfile.lock + checksum: b89939ae0ac554d89f5a333f37da8d9e7fb37c0ad832fb3e6f9b73b00b21bf99 +- filename: .ci/pipenv/manylinux_2_28_x86_64-py312-test-sysctk12-Pipfile.lock + checksum: 62280af6c8aa520138f636a09d0330f3cda9efa2ac4e9337d7e995c93bd10c06 - filename: .ci/pipenv/manylinux_2_28_x86_64-py311-docs-Pipfile.lock - checksum: 50417e87baee9d7aa17765525d21c48ea99f9d3c0b2a2b25d401102b5c5bb32a + checksum: acfaab5ffb3098a96645323d7879d8d1df69a549b40a09a590bf8fe1315dc839 diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..39aca1a --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,5 @@ +graft nvmath +global-include *.pyd +global-include *.pyi +global-exclude *.cpp +global-exclude *.pyx diff --git a/builder/__init__.py b/builder/__init__.py index 4f1a2f2..6ff03ac 100644 --- a/builder/__init__.py +++ b/builder/__init__.py @@ -1,3 +1,3 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/builder/pep517.py b/builder/pep517.py index c7a21c1..4e9b7a8 100644 --- a/builder/pep517.py +++ b/builder/pep517.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -15,8 +15,6 @@ from setuptools import build_meta as _build_meta -import utils # this is builder.utils (the build system has sys.path set up) - prepare_metadata_for_build_wheel = _build_meta.prepare_metadata_for_build_wheel build_wheel = _build_meta.build_wheel diff --git a/builder/utils.py b/builder/utils.py index 7c123f5..9c517b8 100644 --- a/builder/utils.py +++ b/builder/utils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/docs/Makefile b/docs/Makefile index de6a9ce..0a3e2e4 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -2,7 +2,7 @@ SHELL=/bin/bash # You can set these variables from the command line or environment -SPHINX_NVMATH_PYTHON_VER ?= $(shell [[ $$(< ../nvmath/_version.py) =~ __version__[^0-9.]*([0-9.]*) ]] && echo $${BASH_REMATCH[1]}) +SPHINX_NVMATH_PYTHON_VER ?= $(shell [[ $$(< ../pyproject.toml) =~ [^a-zA-Z_]version\ =\ [^0-9.]*([0-9.]*) ]] && echo $${BASH_REMATCH[1]}) SPHINXOPTS ?= -W SPHINXBUILD ?= sphinx-build SOURCEDIR = sphinx diff --git a/docs/sphinx/_static/switcher.json b/docs/sphinx/_static/switcher.json index aabddac..55122ce 100644 --- a/docs/sphinx/_static/switcher.json +++ b/docs/sphinx/_static/switcher.json @@ -3,6 +3,10 @@ "version": "latest", "url": "https://docs.nvidia.com/cuda/nvmath-python/latest" }, + { + "version": "0.3.0", + "url": "https://docs.nvidia.com/cuda/nvmath-python/0.3.0" + }, { "version": "0.2.1", "url": "https://docs.nvidia.com/cuda/nvmath-python/0.2.1" diff --git a/docs/sphinx/bindings/index.rst b/docs/sphinx/bindings/index.rst index d87532f..799601d 100644 --- a/docs/sphinx/bindings/index.rst +++ b/docs/sphinx/bindings/index.rst @@ -157,13 +157,13 @@ require a sequence or a nested sequence, the following operations are equivalent my_func(..., buf, ...) # the underlying data type is determined by the C API which is particularly useful when users need to pass multiple sequences or nested sequences -to C (ex: :func:`nvmath.bindings.cufft.plan_many`). +to C (For example, :func:`nvmath.bindings.cufft.plan_many`). .. note:: Some functions require their arguments to be in the device memory. You need to pass device memory (for example, :class:`cupy.ndarray`) to such arguments. nvmath-python - does not validate the memory pointers passed and does not implicitly transfer the data. + neither validates the memory pointers nor implicitly transfers the data. Passing host memory where device memory is expected (and vice versa) results in undefined behavior. diff --git a/docs/sphinx/conf.py b/docs/sphinx/conf.py index ad66638..0bde383 100644 --- a/docs/sphinx/conf.py +++ b/docs/sphinx/conf.py @@ -17,11 +17,12 @@ import os import re import sys +import tomllib +import tempfile +import json sys.path.insert(0, os.path.abspath(".")) -import pkg_resources import warnings -import json from sphinx.writers.html import HTMLTranslator from docutils.transforms import Transform @@ -56,10 +57,8 @@ # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. -with open("../../nvmath/_version.py") as f: - exec(f.read()) - nvmath_py_ver = __version__ # noqa: F821 - del __version__ # noqa: F821 +with open("../../pyproject.toml", "rb") as f: + nvmath_py_ver = tomllib.load(f)["project"]["version"] # The short X.Y version. version = nvmath_py_ver @@ -90,6 +89,8 @@ #'sphinxcontrib.autoprogram', "sphinxcontrib.programoutput", "sphinx_favicon", + "nbsphinx", + "nbsphinx_link", ] imgmath_latex_preamble = r"\usepackage{braket}" @@ -101,6 +102,9 @@ # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] +# Silence a warning about unpicklable value +nbsphinx_custom_formats = {} + # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # @@ -181,7 +185,7 @@ def autodoc_process_docstring(app, what, name, obj, options, lines): struct = snake_to_camel([mod] + struct.split("_")[:-1]) line = f"NumPy dtype object that represents the `{struct}` struct.\n" else: - # handle dtype in high-level pythonic APIs + # handle dtype in high-level Pythonic APIs struct = " ".join(struct.split("_")[:-1]) line = f"NumPy dtype object that encapsulates the {struct} in {mod}.\n" lines.clear() @@ -250,9 +254,42 @@ def default_departure(self, node): default_priority = 800 +class NotebookHandler: + def __init__(self): + self.tmpdir = tempfile.mkdtemp() + + def __del__(self): + os.unlink(self.tmpdir) + + def remove_notebook_copyright(self, app, docname, content): + if os.path.exists(os.path.join("sphinx", docname + ".nblink")): + link = json.loads(content[0]) + notebook_path = os.path.join("sphinx", os.path.dirname(docname), link["path"]) + + with open(notebook_path) as original_notebook_file: + notebook_content = json.load(original_notebook_file) + copyright_regex = ( + r"\s*Copyright \(c\) [0-9-]+, NVIDIA CORPORATION & AFFILIATES\s*SPDX-License-Identifier: BSD-3-Clause\s*" + ) + if re.match(copyright_regex, "".join(notebook_content["cells"][0]["source"])): + # Remove first cell if it's a copyright notice + notebook_content["cells"] = notebook_content["cells"][1:] + + new_notebook_path = os.path.join(self.tmpdir, docname.replace(".nblink", ".ipynb").replace("/", "__")) + with open(new_notebook_path, "w") as new_notebook_file: + json.dump(notebook_content, new_notebook_file) + + link["path"] = os.path.relpath(new_notebook_path, os.path.join("sphinx", os.path.dirname(docname))) + content[0] = json.dumps(link) + + +notebook_handler = NotebookHandler() + + def setup(app): app.add_css_file("nvmath_override.css") app.connect("autodoc-process-docstring", autodoc_process_docstring) + app.connect("source-read", lambda *args, **kwargs: notebook_handler.remove_notebook_copyright(*args, **kwargs)) app.set_translator("html", DotBreakHtmlTranslator) app.add_autodocumenter(PatchedEnumDocumenter, override=True) app.add_post_transform(UnqualifiedTitlesTransform) @@ -283,6 +320,10 @@ def setup(app): # sweetspot value determined by trial & error to suppress all warnings autosectionlabel_maxdepth = 2 +show_warning_types = True +suppress_warnings = [ + "config.cache", # nbsphinx_link makes nbsphinx_custom_formats unpicklable +] doctest_global_setup = """ import numpy as np diff --git a/docs/sphinx/device-apis/cufft.rst b/docs/sphinx/device-apis/cufft.rst index 065ff0a..81078fc 100644 --- a/docs/sphinx/device-apis/cufft.rst +++ b/docs/sphinx/device-apis/cufft.rst @@ -9,7 +9,7 @@ Overview ======== These APIs offer integration with the NVIDIA cuFFTDx library. -Detailed documentation of cuBLASDx can be found in the +Detailed documentation of cuFFTDx can be found in the `cuFFTDx documentation `_. .. note:: diff --git a/docs/sphinx/device-apis/index.rst b/docs/sphinx/device-apis/index.rst index d25a257..bdddcc7 100644 --- a/docs/sphinx/device-apis/index.rst +++ b/docs/sphinx/device-apis/index.rst @@ -13,6 +13,7 @@ Detailed documentation for these libraries can be found at `cuFFTDx `_, `cuBLASDx `_, and `cuRAND device APIs `_ respectively. +Device APIs can only be called from CUDA device or kernel code, and execute on the GPU. Users may take advantage of the device module via the two approaches below: diff --git a/docs/sphinx/fft/index.rst b/docs/sphinx/host-apis/fft/index.rst similarity index 100% rename from docs/sphinx/fft/index.rst rename to docs/sphinx/host-apis/fft/index.rst diff --git a/docs/sphinx/host-apis/index.rst b/docs/sphinx/host-apis/index.rst new file mode 100644 index 0000000..7f1f7f2 --- /dev/null +++ b/docs/sphinx/host-apis/index.rst @@ -0,0 +1,20 @@ +********* +Host APIs +********* + +The following of modules of nvmath-python offer integration with NVIDIA's +high-performance computing libraries through host APIs for cuBLAS and cuFFT. +Host APIs are called from host code but can execute in any supported execution +space (CPU or GPU). + +======== +Contents +======== + +.. toctree:: + :caption: API Reference + :maxdepth: 2 + + Linear Algebra + Fast Fourier Transform + Host API Utilities diff --git a/docs/sphinx/linalg/index.rst b/docs/sphinx/host-apis/linalg/index.rst similarity index 63% rename from docs/sphinx/linalg/index.rst rename to docs/sphinx/host-apis/linalg/index.rst index d005942..8da23c2 100644 --- a/docs/sphinx/linalg/index.rst +++ b/docs/sphinx/host-apis/linalg/index.rst @@ -43,5 +43,27 @@ Specialized Linear Algebra APIs (:mod:`nvmath.linalg.advanced`) :template: dataclass.rst + MatmulEpilogPreferences MatmulOptions MatmulPlanPreferences + MatmulQuantizationScales + +Helpers +^^^^^^^ + +The Specialized Linear Algebra helpers module :mod:`nvmath.linalg.advanced.helpers` +provides helper functions to facilitate working with some of the complex features of +:mod:`nvmath.linalg.advanced` module. + +Matmul helpers (:mod:`nvmath.linalg.advanced.helpers.matmul`) +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" + +.. module:: nvmath.linalg.advanced.helpers.matmul + +.. autosummary:: + :toctree: generated/ + + create_mxfp8_scale + invert_mxfp8_scale + apply_mxfp8_scale + get_mxfp8_scale_offset diff --git a/docs/sphinx/host-utils.rst b/docs/sphinx/host-apis/utils.rst similarity index 64% rename from docs/sphinx/host-utils.rst rename to docs/sphinx/host-apis/utils.rst index 1654166..2d72f43 100644 --- a/docs/sphinx/host-utils.rst +++ b/docs/sphinx/host-apis/utils.rst @@ -2,13 +2,6 @@ Host API Utilities ********************** -.. _host-api-util-overview: - -Overview -======== - -nvmath-python provides host-side APIs for managing device-side memory. - .. _host-api-util-reference: API Reference @@ -16,8 +9,24 @@ API Reference .. module:: nvmath +Memory utilities +---------------- + +nvmath-python provides host-side APIs for managing device-side memory. + .. autosummary:: :toctree: generated/ BaseCUDAMemoryManager MemoryPointer + +Data types +---------- + +nvmath-python provides the following data types. + + +.. autosummary:: + :toctree: generated/ + + CudaDataType diff --git a/docs/sphinx/index.rst b/docs/sphinx/index.rst index 0245cc7..295d189 100644 --- a/docs/sphinx/index.rst +++ b/docs/sphinx/index.rst @@ -10,9 +10,10 @@ Welcome to the nvmath-python documentation! interoperability within the Python computational ecosystem through NVIDIA's high-performance math libraries. -To quickly get started with *nvmath-python*, take a look at our +To quickly get started with nvmath-python, take a look at our :doc:`Getting Started ` manual. -Refer to our :doc:`Installation Guide ` for detailed instructions on the various installation choices available. +Refer to our :doc:`Installation Guide ` for detailed instructions on the +various installation choices available. ======== Contents @@ -32,12 +33,17 @@ Contents Installation .. toctree:: - :caption: API Reference + :caption: Examples and tutorials :maxdepth: 2 - Host API Utilities - Linear Algebra - Fast Fourier Transform + Linear Algebra Host APIs Tutorial + Examples on GitHub + +.. toctree:: + :caption: API Reference + :maxdepth: 3 + + Host APIs Device APIs Bindings diff --git a/docs/sphinx/installation.rst b/docs/sphinx/installation.rst index c9062d7..03631aa 100644 --- a/docs/sphinx/installation.rst +++ b/docs/sphinx/installation.rst @@ -5,13 +5,13 @@ Install nvmath-python ===================== nvmath-python, like most modern Python packages, provides pre-built binaries (wheels and -later conda packages) to the end users. The full source code is hosted in the +conda packages) to the end users. The full source code is hosted in the `NVIDIA/nvmath-python `_ repository. In terms of CUDA Toolkit (CTK) choices, nvmath-python is designed and implemented to allow building and running against 1. ``pip``-wheel, 2. ``conda``, or 3. system installation of -CTK. Having a full CTK installation at either build- or run- time is not necessary; just a -small fraction as explained below is enough. +CTK. Having a full CTK installation at either build- or run-time is not necessary; only a +small subset, as explained below, is enough. Host & device APIs (see :ref:`nvmath overview`) have different run-time dependencies and requirements. Even among host APIs the needed underlying libraries are different (for @@ -20,9 +20,9 @@ loaded when only needed. Therefore, nvmath-python is designed to have most of it dependencies *optional*, but provides convenient installation commands for users to quickly spin up a working Python environment. -The :ref:`cheatsheet ` below captures nvmath-python's required/optional, -build-/run- time dependencies. Using the installation commands from the sections below -should support most of your needs. +The :ref:`cheatsheet ` below captures nvmath-python's required and optional +build-time and run-time dependencies. Using the installation commands from the sections +below should support most of your needs. .. _install from pypi: @@ -60,12 +60,12 @@ needed; the dependencies are pulled via extras). **Note**: - 1. NVPL is for ARM architecture only. MKL or another FFTW3 [9]_ compatible - library may be substituted for x86 architecture. + 1. NVPL supports only ARM architecture, while MKL or another FFTW3 [9]_ + compatible library may be substituted for x86 architecture. 2. The environment variable ``NVMATH_FFT_CPU_LIBRARY`` may be used to provide the path to an alternate shared object which implements the - FFTW3 (non-guru) API. ``LD_LIBRARY_PATH`` should be set properly to - include this library if it is not already in the PATH. + FFTW3 (non-guru) API. Ensure ``LD_LIBRARY_PATH`` includes this + library if it is not already in the PATH. The options below are for adventurous users who want to manage most of the dependencies themselves. The following assumes that **system CTK is installed**. @@ -80,15 +80,13 @@ themselves. The following assumes that **system CTK is installed**. - Install nvmath-python along with CuPy for CUDA 11 to support nvmath host APIs. - **Note**: ``LD_LIBRARY_PATH`` should be set - properly to include CUDA libraries. + **Note**: Set ``LD_LIBRARY_PATH`` to include the CUDA libraries. * - ``pip install nvmath-python[sysctk12]`` - Install nvmath-python along with CuPy for CUDA 12 to support nvmath host APIs. - **Note**: ``LD_LIBRARY_PATH`` should be set - properly to include CUDA libraries. + **Note**: Set ``LD_LIBRARY_PATH`` to include the CUDA libraries. * - ``pip install nvmath-python[sysctk12-dx]`` - Install nvmath-python along with CuPy for CUDA 12 to support @@ -96,9 +94,9 @@ themselves. The following assumes that **system CTK is installed**. **Note**: - 1. ``LD_LIBRARY_PATH`` should be set properly to include CUDA libraries. - 2. For using :mod:`nvmath.device` APIs, ``CUDA_HOME`` (or ``CUDA_PATH``) should be - set to point to the system CTK. + 1. Set ``LD_LIBRARY_PATH`` to include the CUDA libraries. + 2. To use :mod:`nvmath.device` APIs, set ``CUDA_HOME`` (or ``CUDA_PATH``) + to point to the system CTK. For system admins or expert users, ``pip install nvmath-python`` would be a bare minimal installation (very lightweight). This allows fully explicit control of all dependencies. @@ -132,10 +130,10 @@ Conda packages can be installed from the `conda-forge ` **Note**: - 1. ``nvmath-python-dx`` is a meta-package for ease of installing + 1. ``nvmath-python-dx`` is a metapackage for ease of installing ``nvmath-python`` and other dependencies. - 2. ``pynvjitlink`` currently only lives on the rapidsai channel, - not the conda-forge channel. + 2. Currently, ``pynvjitlink`` is only available on the rapidsai channel, + and not on conda-forge. * - ``conda install -c conda-forge nvmath-python-cpu`` - Install nvmath-python along with all CPU optional dependencies (NVPL or other) to support optimized CPU FFT APIs. [1]_ @@ -183,9 +181,9 @@ source. There are several ways to build it since we need some CUDA headers at bu **Note**: in this case we get CUDA headers by installing pip wheels to the isolated build environment. * - ``CUDA_PATH=/path/to/your/cuda/installation pip install --no-build-isolation -v .`` - - Skip creating a build isolation (it'd use CUDA headers from ``$CUDA_PATH/include`` - instead), build the project, and install it to the current - user environment together with the run-time dependencies. One can use: + - Skip creating a build isolation (it would use CUDA headers from + ``$CUDA_PATH/include`` instead), build the project, and install it to the current + user environment together with the run-time dependencies. Use: - conda: After installing CUDA 12 conda packages, set the environment variable ``CUDA_PATH`` @@ -198,9 +196,9 @@ source. There are several ways to build it since we need some CUDA headers at bu **Notes**: -- If you add the "extras" notation after the dot ``.`` (for example ``.[cu11]``, ``.[cu12,dx]``, - ...), it has the same meaning as explained in the :ref:`previous section `. +- If you add the "extras" notation after the dot ``.`` (for example ``.[cu11]``, + ``.[cu12,dx]``, ...), it has the same meaning as explained in the :ref:`previous section + `. - If you don't want the run-time dependencies to be automatically handled, add ``--no-deps`` after the ``pip install`` command above; in this case, however, it's your responsibility to make sure that all the run-time requirements are met. @@ -299,7 +297,7 @@ dependency is *required* unless stated otherwise. * - | PyTorch | (see `PyTorch installation guide `_) - - - >=1.10 (optional) + - >=1.10 (optional) [10]_ - - >=1.10 (optional) * - MathDx (cuBLASDx, cuFFTDx, ...) @@ -319,7 +317,7 @@ dependency is *required* unless stated otherwise. - * - Math Kernel Library (MKL) - - - 2024.4 (optional) + - >=2024 (optional) - - * - NVIDIA Performance Libraries (NVPL) @@ -343,9 +341,9 @@ nvmath-python is tested in the following environments: * - CUDA - 11.x (latest), 12.x (latest) * - Driver - - R450, R520, R525, R560 + - R520, R525, R570 * - GPU model - - A100, H100, RTX 4090, CG1 (Grace-Hopper) + - H100, B100, RTX 4090, CG1 (Grace-Hopper) * - Python - 3.10, 3.11, 3.12 * - CPU architecture @@ -486,18 +484,18 @@ libraries, there are user-visible caveats. 3. CuPy installed from ``pip`` currently (as of v13.3.0) only supports conda and system CTK, and not ``pip``-installed CUDA wheels. nvmath-python can help CuPy use the CUDA libraries installed to ``site-packages`` (where wheels are installed to) if ``nvmath`` is imported. - From beta 2 (v0.2.0) onwards the libraries are "soft-loaded" (no error is raised if a library is - not installed) when ``import nvmath`` happens. This behavior may change in a future - release. + From beta 2 (v0.2.0) onwards the libraries are "soft-loaded" (no error is raised if a + library is not installed) when ``import nvmath`` happens. This behavior may change in a + future release. 4. Numba installed from ``pip`` currently (as of v0.60.0) only supports conda and system CTK, and not ``pip``-installed CUDA wheels. nvmath-python can also help Numba use the CUDA compilers installed to ``site-packages`` if ``nvmath`` is imported. Same as above, this behavior may change in a future release. In general, mixing-and-matching CTK packages from ``pip``, ``conda``, and the system is -possible but can be very fragile, so it's important to understand what you're doing. The nvmath-python -internals are designed to work with everything installed either via ``pip``, ``conda``, or -local system (system CTK, including `tarball extractions +possible but can be very fragile, so it's important to understand what you're doing. The +nvmath-python internals are designed to work with everything installed either via ``pip``, +``conda``, or local system (system CTK, including `tarball extractions `_, are the fallback solution in the detection logic), but mix-n-match makes the detection logic impossible to get right. @@ -563,7 +561,7 @@ For more information with regard to the new CUDA 12+ package layout on conda-for .. [2] nvmath-python relies on `CUDA minor version compatibility `_. -.. [4] As of beta 2.1 (v0.2.1), CuPy is a required run-time dependency except for CPU-only +.. [4] As of beta 3.0 (v0.3.0), CuPy is a required run-time dependency except for CPU-only execution. In a future release it will be turned into an optional run-time dependency. .. [5] For example, Hopper GPUs are supported starting CUDA 11.8, so they would not work with libraries from CUDA 11.7 or below. @@ -576,3 +574,5 @@ For more information with regard to the new CUDA 12+ package layout on conda-for already takes care of this. .. [9] The library must ship FFTW3 symbols for single and double precision transforms in a single ``so`` file. +.. [10] To use ``matmul`` with FP8 or MXFP8 you need PyTorch version built with CUDA 12.8 + (``>=2.7.0`` or nightly version) diff --git a/docs/sphinx/overview.rst b/docs/sphinx/overview.rst index fe4dd0d..8d3b550 100644 --- a/docs/sphinx/overview.rst +++ b/docs/sphinx/overview.rst @@ -4,7 +4,7 @@ Overview ******** The primary goal of nvmath-python is to bring the power of the NVIDIA math libraries to the -Python ecosystem. The package aims to provide intuitive pythonic APIs that provide users +Python ecosystem. The package aims to provide intuitive Pythonic APIs that provide users full access to all the features offered by our libraries in a variety of execution spaces. We hope to empower a wide range of Python users by providing easy access to high-performance @@ -26,7 +26,7 @@ The APIs provided by nvmath-python can be categorized into: nvmath-python is dedicated to delivering the following key features and commitments: -1. **Logical Feature Parity**: While the pythonic API surface (the number of APIs and the +1. **Logical Feature Parity**: While the Pythonic API surface (the number of APIs and the complexity of each) is more concise compared to that of the C libraries, it provides access to their complete functionality. 2. **Consistent Design Patterns**: Uniform design across all modules to simplify user @@ -67,12 +67,12 @@ flexibility allows: Additionally, we offer :doc:`Python bindings ` that provide a 1:1 mapping with the C APIs. These bindings, which serve as wrappers with API signatures similar to their C counterparts, are ideal for library developers looking to integrate the capabilities -of the NVIDIA Math Libraries in a customized manner, in the event that the pythonic APIs -don't meet their specific requirements. Conversely, our high-level pythonic APIs deliver a +of the NVIDIA Math Libraries in a customized manner, in the event that the Pythonic APIs +don't meet their specific requirements. Conversely, our high-level Pythonic APIs deliver a fully integrated solution suitable for native Python users as well as library developers, -encompassing both host and device APIs. In the future, select host APIs will accept -**callback functions written in Python** and compiled into supported formats such as LTO-IR, -using compilers like `Numba`_. +encompassing both host and device APIs. Select host APIs accept **callback functions +written in Python**, which are compiled into supported formats such as LTO-IR, using +compilers like `Numba`_. .. _host api section: @@ -86,9 +86,9 @@ nvmath-python provides a collection of APIs that can be directly invoked from th categories: - Fast Fourier Transform in :mod:`nvmath.fft`. Refer to :doc:`Fast Fourier Transform - ` for details. + ` for details. - Linear Algebra in :mod:`nvmath.linalg`. Refer to :doc:`Linear Algebra - ` for details. + ` for details. .. _host api interop: @@ -108,7 +108,7 @@ frameworks. One example for the interoperability is shown below: # Create a numpy.ndarray as input a = np.random.random(128) + 1.j * np.random.random(128) - # Call nvmath-python pythonic APIs + # Call nvmath-python Pythonic APIs b = nvmath.fft.fft(a) # Verify that output is also a numpy.ndarray @@ -134,24 +134,24 @@ potentially enhancing performance significantly. The design pattern for all stateful APIs in nvmath-python consists of several key phases: - - Problem Specification: This initial phase involves defining the operation and setting - options that affect its execution. It's designed to be as lightweight as possible, - ensuring the problem is well-defined and supported by the current implementation. - - Preparation: Using FFT as an example, this phase includes a planning step to select - the optimal algorithm for the defined FFT operation. An optional autotuning operation, - when available, also falls within the preparation phase. The preparation phase is - generally the most resource-intensive and may incorporate user-specified planning and - autotuning options. - - Execution: This phase allows for repeated execution, where the operand can be either - modified in-place or explicitly reset using the ``reset_operand``/``reset_operands`` - method. The costs associated with the first two phases are therefore amortized over - these multiple executions. - - Resource Release: Users are advised to use stateful objects from within a context - using the `with statement - `_, which - automatically handles the release of internal resources upon exit. If the object is - not used as a context manager using ``with``, it is necessary to explicitly call the - ``free`` method to ensure all resources are properly released. +- Problem Specification: This initial phase involves defining the operation and setting + options that affect its execution. It's designed to be as lightweight as possible, + ensuring the problem is well-defined and supported by the current implementation. +- Preparation: Using FFT as an example, this phase includes a planning step to select + the optimal algorithm for the defined FFT operation. An optional autotuning operation, + when available, also falls within the preparation phase. The preparation phase is + generally the most resource-intensive and may incorporate user-specified planning and + autotuning options. +- Execution: This phase allows for repeated execution, where the operand can be either + modified in-place or explicitly reset using the ``reset_operand``/``reset_operands`` + method. The costs associated with the first two phases are therefore amortized over + these multiple executions. +- Resource Release: Users are advised to use stateful objects from within a context + using the `with statement + `_, which + automatically handles the release of internal resources upon exit. If the object is + not used as a context manager using ``with``, it is necessary to explicitly call the + ``free`` method to ensure all resources are properly released. .. note:: @@ -166,7 +166,7 @@ The design pattern for all stateful APIs in nvmath-python consists of several ke .. note:: The decision to require explicit ``free`` calls for resource release is driven by the - fact that Python's garbage collector can delay freeing object resources when the object + fact that Python's garbage collector may delay freeing object resources when the object goes out of scope or its reference count drops to zero. For details, refer to the `__del__ method Python documentation `_. @@ -206,8 +206,8 @@ Full Logging Support nvmath-python provides integration with the Python standard library logger from the `logging module `_ to offer full logging of the -computational details at various levels, for example debug, information, warning and error. An -example illustrating the use of the global Python logger is shown below: +computational details at various levels, for example debug, information, warning and error. +An example illustrating the use of the global Python logger is shown below: .. code-block:: python @@ -220,7 +220,7 @@ example illustrating the use of the global Python logger is shown below: datefmt='%m-%d %H:%M:%S' ) - # Call nvmath-python pythonic APIs + # Call nvmath-python Pythonic APIs out = nvmath.linalg.advanced.matmul(...) Alternatively, for APIs that contain the ``options`` argument, users can set a custom logger @@ -236,7 +236,7 @@ object, for example :attr:`nvmath.fft.FFTOptions.logger` for :func:`nvmath.fft.f logger = logging.getLogger('userlogger') ... - # Call nvmath-python pythonic APIs + # Call nvmath-python Pythonic APIs out = nvmath.fft.fft(..., options={'logger': logger}) For the complete examples, refer to `global logging example @@ -260,7 +260,7 @@ example04_logging_user.py>`_. Call Blocking Behavior ---------------------- -By default, calls to all pythonic host APIs that require GPU execution are *not* blocking if +By default, calls to all Pythonic host APIs that require GPU execution are *not* blocking if the input operands reside on the device. This means that functions like :func:`nvmath.linalg.advanced.matmul`, :meth:`nvmath.fft.FFT.execute`, and :meth:`nvmath.linalg.advanced.Matmul.execute` will return immediately after the operation is @@ -269,7 +269,7 @@ properly synchronizing the stream when needed. The default behavior can be modif setting the ``blocking`` attribute (default ``'auto'``) of the relevant ``Options`` object to ``True``. For example, users may set :attr:`nvmath.fft.FFTOptions.blocking` to ``True`` and pass this options object to the corresponding FFT API calls. If the input operands are -on the host, the pythonic API calls will always block since the computation yields an output +on the host, the Pythonic API calls will always block since the computation yields an output operand that will also reside on the host. Meanwhile, APIs that execute on the host (such as :meth:`nvmath.fft.FFT.create_key`) always block. @@ -295,7 +295,7 @@ provided for two reasons: For non-blocking behavior, it is the user's responsibility to ensure correct stream ordering between the execution API calls. -In any case, the execution APIs are launched on the provided stream. +The execution APIs are always launched on the provided stream. For examples on stream ordering, refer to `FFT with multiple streams `_. @@ -307,8 +307,8 @@ Memory Management By default, the host APIs use the memory pool from the package that their operands belong to. This ensures that there is no contention for memory or spurious out-of-memory errors. -However the user also has the ability to provide their own memory allocator if they choose -to do so. In our pythonic APIs, we support an `EMM`_-like interface as proposed and +However, the user also has the ability to provide their own memory allocator if they choose +to do so. In our Pythonic APIs, we support an `EMM`_-like interface as proposed and supported by Numba for users to set their Python mempool. Taking FFT as an example, users can set the option :attr:`nvmath.fft.FFTOptions.allocator` to a Python object complying with the :class:`nvmath.BaseCUDAMemoryManager` protocol, and pass the options to the high-level @@ -397,7 +397,7 @@ considerations, we strive to meet the following commitments: Note that all bindings are currently *experimental*. -2. For the high-level pythonic APIs, we maintain backward compatibility to the greatest +2. For the high-level Pythonic APIs, we maintain backward compatibility to the greatest extent feasible. When a breaking change is necessary, we issue a runtime warning to alert users of the upcoming changes in the next major release. This practice ensures that breaking changes are clearly communicated and reserved for major version updates, diff --git a/docs/sphinx/quickstart.rst b/docs/sphinx/quickstart.rst index ee70b8b..6a6dd43 100644 --- a/docs/sphinx/quickstart.rst +++ b/docs/sphinx/quickstart.rst @@ -2,7 +2,7 @@ Getting Started *************** nvmath-python brings the power of the NVIDIA math libraries to the Python ecosystem. -The package aims to provide intuitive pythonic APIs that provide users full access +The package aims to provide intuitive Pythonic APIs that provide users full access to all the features offered by NVIDIA's libraries in a variety of execution spaces. nvmath-python works seamlessly with existing Python array/tensor frameworks and focuses on providing functionality that is missing from those frameworks. @@ -32,7 +32,8 @@ Matrix multiplication Using the nvmath-python API allows access to all parameters of the underlying NVIDIA cuBLASLt library. -Some of these parameters are unavailable in other wrappings of NVIDIA's C-API libraries. +Some of these parameters are unavailable in other packages that wrap NVIDIA's C-API +libraries. .. doctest:: @@ -76,7 +77,7 @@ prolog to the IFFT operation. >>> B, N = 256, 1024 >>> a = cp.random.rand(B, N, dtype=cp.float64) + 1j * cp.random.rand(B, N, dtype=cp.float64) >>> - >>> # Create the data to use as filter. + >>> # Create the data to use as a filter. >>> filter_data = cp.sin(a) >>> >>> # Define the prolog function for the inverse FFT. @@ -84,7 +85,7 @@ prolog to the IFFT operation. >>> def convolve(data_in, offset, filter_data, unused): ... # Note we are accessing `data_out` and `filter_data` with a single `offset` integer, ... # even though the input and `filter_data` are 2D tensors (batches of samples). - ... # Care must be taken to assure that both arrays accessed here have the same memory + ... # Care must be taken to ensure that both arrays accessed here have the same memory ... # layout. ... return data_in[offset] * filter_data[offset] / N >>> diff --git a/docs/sphinx/release-notes.rst b/docs/sphinx/release-notes.rst index 94852b0..6c7d45c 100644 --- a/docs/sphinx/release-notes.rst +++ b/docs/sphinx/release-notes.rst @@ -1,14 +1,37 @@ nvmath-python Release Notes *************************** +nvmath-python v0.3.0 +==================== + +Beta3 release. + +* FP8 and MXFP8 support for the advanced matrix multiplication API. +* Notebook to illustrate use of FP8 and MXFP8 in the advanced matrix multiplication API. +* Added bindings for new APIs introduced in CTK version 12.8. + +Bugs Fixed +---------- + +* The advanced matrix multiplication API may return an incorrect result when a bias vector + is used along with 1-D A and C operands. + +API Changes +----------- + +* The ``last_axis_size`` option in :class:`nvmath.fft.FFTOptions` is removed in favor of + ``last_axis_parity`` to better reflect its semantics. + nvmath-python v0.2.1 ==================== Beta2 update 1 with improved diagnostics, testing enhancements, and bug fixes. -* New tests for batched epilogs and autotuning with epilogs for the advanced matrix multiplication APIs. +* New tests for batched epilogs and autotuning with epilogs for the advanced matrix + multiplication APIs. * Added more hypothesis-based tests for host APIs. -* Improved algorithm for detecting overlapping memory operands for certain sliced tensors, thereby supporting such layouts for FFTs. +* Improved algorithm for detecting overlapping memory operands for certain sliced tensors, + thereby supporting such layouts for FFTs. * Added bindings for new APIs introduced in CTK versions 12.5 and 12.6. * Further coding style fixes toward meeting PEP8 recommendations. * Clarified batched semantics for matrix multiplication epilogs in the documentation. @@ -18,13 +41,16 @@ Bugs Fixed ---------- * C2R FFT may fail with "illegal memory access" on sliced tensors. -* Improved diagnostics to detect incompatible combinations of scale and compute types for matrix multiplication, that previously may have resulted in incorrect results. -* Matrix multiplication provided incorrect results when operand A is a vector (number of dimensions=1). +* Improved diagnostics to detect incompatible combinations of scale and compute types for + matrix multiplication, that previously may have resulted in incorrect results. +* Matrix multiplication provided incorrect results when operand A is a vector (number of + dimensions=1). API Changes ----------- -* The ``last_axis_size`` option in :class:`nvmath.fft.FFTOptions` is now deprecated in favor of `last_axis_parity` to better reflect its semantics. +* The ``last_axis_size`` option in :class:`nvmath.fft.FFTOptions` is now deprecated in favor + of ``last_axis_parity`` to better reflect its semantics. .. note:: @@ -35,7 +61,8 @@ nvmath-python v0.2.0 Beta2 release. -* CPU execution space support for FFT libraries that conform to FFTW3 API (for example MKL, NVPL). +* CPU execution space support for FFT libraries that conform to FFTW3 API (for example MKL, + NVPL). * Support for prolog and epilog callback for FFT, written in Python. * New device APIs for random number generation. * Notebooks to illustrate use of advanced matrix multiplication APIs. @@ -78,7 +105,7 @@ The required and optional dependencies are summarized in the :ref:`cheatsheet `_ diff --git a/docs/sphinx/tutorials/linalg.rst b/docs/sphinx/tutorials/linalg.rst new file mode 100644 index 0000000..8e1f6f9 --- /dev/null +++ b/docs/sphinx/tutorials/linalg.rst @@ -0,0 +1,11 @@ +********************************* +Linear Algebra Host APIs Tutorial +********************************* + +.. toctree:: + :maxdepth: 1 + + Introduction to GEMM with nvmath-python + Fused Epilogs + Implementing a simple neural network + Narrow-precision operations \ No newline at end of file diff --git a/docs/sphinx/tutorials/notebooks/matmul/01_introduction.nblink b/docs/sphinx/tutorials/notebooks/matmul/01_introduction.nblink new file mode 100644 index 0000000..a9d07c6 --- /dev/null +++ b/docs/sphinx/tutorials/notebooks/matmul/01_introduction.nblink @@ -0,0 +1,3 @@ +{ + "path": "../../../../../notebooks/matmul/01_introduction.ipynb" +} diff --git a/docs/sphinx/tutorials/notebooks/matmul/02_epilogs.nblink b/docs/sphinx/tutorials/notebooks/matmul/02_epilogs.nblink new file mode 100644 index 0000000..c50805b --- /dev/null +++ b/docs/sphinx/tutorials/notebooks/matmul/02_epilogs.nblink @@ -0,0 +1,3 @@ +{ + "path": "../../../../../notebooks/matmul/02_epilogs.ipynb" +} diff --git a/docs/sphinx/tutorials/notebooks/matmul/03_backpropagation.nblink b/docs/sphinx/tutorials/notebooks/matmul/03_backpropagation.nblink new file mode 100644 index 0000000..d135b43 --- /dev/null +++ b/docs/sphinx/tutorials/notebooks/matmul/03_backpropagation.nblink @@ -0,0 +1,3 @@ +{ + "path": "../../../../../notebooks/matmul/03_backpropagation.ipynb" +} diff --git a/docs/sphinx/tutorials/notebooks/matmul/04_fp8.nblink b/docs/sphinx/tutorials/notebooks/matmul/04_fp8.nblink new file mode 100644 index 0000000..9f755da --- /dev/null +++ b/docs/sphinx/tutorials/notebooks/matmul/04_fp8.nblink @@ -0,0 +1,3 @@ +{ + "path": "../../../../../notebooks/matmul/04_fp8.ipynb" +} diff --git a/examples/device/common.py b/examples/device/common.py index c633945..e173bab 100644 --- a/examples/device/common.py +++ b/examples/device/common.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/common_cupy.py b/examples/device/common_cupy.py index 43eed9b..96f7248 100644 --- a/examples/device/common_cupy.py +++ b/examples/device/common_cupy.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/common_numba.py b/examples/device/common_numba.py index 4f323bd..1d81edb 100644 --- a/examples/device/common_numba.py +++ b/examples/device/common_numba.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cublasdx_batched_gemm_fp64.py b/examples/device/cublasdx_batched_gemm_fp64.py index 973f18b..fb3df22 100644 --- a/examples/device/cublasdx_batched_gemm_fp64.py +++ b/examples/device/cublasdx_batched_gemm_fp64.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cublasdx_blockdim_gemm_fp16.py b/examples/device/cublasdx_blockdim_gemm_fp16.py index b620a99..98abe48 100644 --- a/examples/device/cublasdx_blockdim_gemm_fp16.py +++ b/examples/device/cublasdx_blockdim_gemm_fp16.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cublasdx_fused_gemm_performance.py b/examples/device/cublasdx_fused_gemm_performance.py index 2f93af1..1db8c9d 100644 --- a/examples/device/cublasdx_fused_gemm_performance.py +++ b/examples/device/cublasdx_fused_gemm_performance.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cublasdx_gemm_fft.py b/examples/device/cublasdx_gemm_fft.py index fe4f819..38a8749 100644 --- a/examples/device/cublasdx_gemm_fft.py +++ b/examples/device/cublasdx_gemm_fft.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cublasdx_gemm_fft_fp16.py b/examples/device/cublasdx_gemm_fft_fp16.py index b183549..e7447cf 100644 --- a/examples/device/cublasdx_gemm_fft_fp16.py +++ b/examples/device/cublasdx_gemm_fft_fp16.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cublasdx_gemm_fft_performance.py b/examples/device/cublasdx_gemm_fft_performance.py index 9ae4de7..095bd8b 100644 --- a/examples/device/cublasdx_gemm_fft_performance.py +++ b/examples/device/cublasdx_gemm_fft_performance.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cublasdx_gemm_fusion.py b/examples/device/cublasdx_gemm_fusion.py index 7b7b0a4..8b849b1 100644 --- a/examples/device/cublasdx_gemm_fusion.py +++ b/examples/device/cublasdx_gemm_fusion.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cublasdx_simple_gemm_cfp16.py b/examples/device/cublasdx_simple_gemm_cfp16.py index bcf9520..6dfecef 100644 --- a/examples/device/cublasdx_simple_gemm_cfp16.py +++ b/examples/device/cublasdx_simple_gemm_cfp16.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cublasdx_simple_gemm_fp32.py b/examples/device/cublasdx_simple_gemm_fp32.py index 18ee520..94f3bd7 100644 --- a/examples/device/cublasdx_simple_gemm_fp32.py +++ b/examples/device/cublasdx_simple_gemm_fp32.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cublasdx_simple_gemm_leading_dimensions.py b/examples/device/cublasdx_simple_gemm_leading_dimensions.py index ec20560..75b5c1c 100644 --- a/examples/device/cublasdx_simple_gemm_leading_dimensions.py +++ b/examples/device/cublasdx_simple_gemm_leading_dimensions.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cublasdx_single_gemm_performance.py b/examples/device/cublasdx_single_gemm_performance.py index 738efea..a11d50b 100644 --- a/examples/device/cublasdx_single_gemm_performance.py +++ b/examples/device/cublasdx_single_gemm_performance.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_autotuning.py b/examples/device/cufftdx_autotuning.py index f7aba72..dfb581e 100644 --- a/examples/device/cufftdx_autotuning.py +++ b/examples/device/cufftdx_autotuning.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_block_fft.py b/examples/device/cufftdx_block_fft.py index ea587bc..5aac1bb 100644 --- a/examples/device/cufftdx_block_fft.py +++ b/examples/device/cufftdx_block_fft.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_block_fft_performance.py b/examples/device/cufftdx_block_fft_performance.py index 894ca5b..bd97482 100644 --- a/examples/device/cufftdx_block_fft_performance.py +++ b/examples/device/cufftdx_block_fft_performance.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_block_fft_performance_many.py b/examples/device/cufftdx_block_fft_performance_many.py index acc6e6b..f3b6ea0 100644 --- a/examples/device/cufftdx_block_fft_performance_many.py +++ b/examples/device/cufftdx_block_fft_performance_many.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_convolution.py b/examples/device/cufftdx_convolution.py index 28411ad..52684b2 100644 --- a/examples/device/cufftdx_convolution.py +++ b/examples/device/cufftdx_convolution.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_convolution_performance.py b/examples/device/cufftdx_convolution_performance.py index 5cf743e..1045d90 100644 --- a/examples/device/cufftdx_convolution_performance.py +++ b/examples/device/cufftdx_convolution_performance.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_convolution_r2c_c2r.py b/examples/device/cufftdx_convolution_r2c_c2r.py index 08c1008..83e2545 100644 --- a/examples/device/cufftdx_convolution_r2c_c2r.py +++ b/examples/device/cufftdx_convolution_r2c_c2r.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_convolution_r2c_c2r_packed_fold_optimized.py b/examples/device/cufftdx_convolution_r2c_c2r_packed_fold_optimized.py index 6b5c329..e2edff2 100644 --- a/examples/device/cufftdx_convolution_r2c_c2r_packed_fold_optimized.py +++ b/examples/device/cufftdx_convolution_r2c_c2r_packed_fold_optimized.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_convolution_signal.py b/examples/device/cufftdx_convolution_signal.py index c5e9d33..4d9c6dd 100644 --- a/examples/device/cufftdx_convolution_signal.py +++ b/examples/device/cufftdx_convolution_signal.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_fft_2d.py b/examples/device/cufftdx_fft_2d.py index 2811b4a..840badb 100644 --- a/examples/device/cufftdx_fft_2d.py +++ b/examples/device/cufftdx_fft_2d.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_fft_2d_r2c_c2r.py b/examples/device/cufftdx_fft_2d_r2c_c2r.py index 716a7b2..49d41bc 100644 --- a/examples/device/cufftdx_fft_2d_r2c_c2r.py +++ b/examples/device/cufftdx_fft_2d_r2c_c2r.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_fft_2d_single_kernel.py b/examples/device/cufftdx_fft_2d_single_kernel.py index a50c167..fcfd3fc 100644 --- a/examples/device/cufftdx_fft_2d_single_kernel.py +++ b/examples/device/cufftdx_fft_2d_single_kernel.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_fft_3d_box_single_block.py b/examples/device/cufftdx_fft_3d_box_single_block.py index 2396742..5df13b8 100644 --- a/examples/device/cufftdx_fft_3d_box_single_block.py +++ b/examples/device/cufftdx_fft_3d_box_single_block.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_fft_3d_cube_single_block.py b/examples/device/cufftdx_fft_3d_cube_single_block.py index 8b51f5a..c6d02c8 100644 --- a/examples/device/cufftdx_fft_3d_cube_single_block.py +++ b/examples/device/cufftdx_fft_3d_cube_single_block.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_helloworld.py b/examples/device/cufftdx_helloworld.py index ab5cc60..e873e48 100644 --- a/examples/device/cufftdx_helloworld.py +++ b/examples/device/cufftdx_helloworld.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_simple_fft_block.py b/examples/device/cufftdx_simple_fft_block.py index b29c86f..957eeba 100644 --- a/examples/device/cufftdx_simple_fft_block.py +++ b/examples/device/cufftdx_simple_fft_block.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_simple_fft_block_c2r.py b/examples/device/cufftdx_simple_fft_block_c2r.py index 39aefaa..b92444f 100644 --- a/examples/device/cufftdx_simple_fft_block_c2r.py +++ b/examples/device/cufftdx_simple_fft_block_c2r.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_simple_fft_block_c2r_fp16.py b/examples/device/cufftdx_simple_fft_block_c2r_fp16.py index cc72edb..9e7d6e7 100644 --- a/examples/device/cufftdx_simple_fft_block_c2r_fp16.py +++ b/examples/device/cufftdx_simple_fft_block_c2r_fp16.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_simple_fft_block_half2.py b/examples/device/cufftdx_simple_fft_block_half2.py index 38ec629..f2a4d3b 100644 --- a/examples/device/cufftdx_simple_fft_block_half2.py +++ b/examples/device/cufftdx_simple_fft_block_half2.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_simple_fft_block_r2c.py b/examples/device/cufftdx_simple_fft_block_r2c.py index f95c740..38232d8 100644 --- a/examples/device/cufftdx_simple_fft_block_r2c.py +++ b/examples/device/cufftdx_simple_fft_block_r2c.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_simple_fft_block_r2c_fp16.py b/examples/device/cufftdx_simple_fft_block_r2c_fp16.py index 4835417..78c15f9 100644 --- a/examples/device/cufftdx_simple_fft_block_r2c_fp16.py +++ b/examples/device/cufftdx_simple_fft_block_r2c_fp16.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_simple_fft_block_shared.py b/examples/device/cufftdx_simple_fft_block_shared.py index 553e0e7..a5432a6 100644 --- a/examples/device/cufftdx_simple_fft_block_shared.py +++ b/examples/device/cufftdx_simple_fft_block_shared.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_simple_fft_thread.py b/examples/device/cufftdx_simple_fft_thread.py index 9b9ae71..2a848c3 100644 --- a/examples/device/cufftdx_simple_fft_thread.py +++ b/examples/device/cufftdx_simple_fft_thread.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/cufftdx_simple_fft_thread_fp16.py b/examples/device/cufftdx_simple_fft_thread_fp16.py index 0bffc89..51be9cc 100644 --- a/examples/device/cufftdx_simple_fft_thread_fp16.py +++ b/examples/device/cufftdx_simple_fft_thread_fp16.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/curand_cufftdx_block_fft.py b/examples/device/curand_cufftdx_block_fft.py index 2a5086f..d2b0774 100644 --- a/examples/device/curand_cufftdx_block_fft.py +++ b/examples/device/curand_cufftdx_block_fft.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -10,8 +10,7 @@ import numpy as np from numba import cuda -from nvmath.device import fft, random -from nvmath.device.vector_types_numba import float32x2 +from nvmath.device import fft, random, float32x2 # Compile the random device APIs for the current device. compiled_random_apis = random.Compile(cc=None) diff --git a/examples/device/curand_philox_uniform4.py b/examples/device/curand_philox_uniform4.py index 4aa4edb..2dd883f 100644 --- a/examples/device/curand_philox_uniform4.py +++ b/examples/device/curand_philox_uniform4.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -12,7 +12,6 @@ """ import numpy as np -from numpy.testing import assert_allclose from numba import cuda diff --git a/examples/device/curand_scrambled_sobol64.py b/examples/device/curand_scrambled_sobol64.py index 3d87023..2b54b25 100644 --- a/examples/device/curand_scrambled_sobol64.py +++ b/examples/device/curand_scrambled_sobol64.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/device/curand_xorwow_uniform.py b/examples/device/curand_xorwow_uniform.py index 85e856f..0ec4845 100644 --- a/examples/device/curand_xorwow_uniform.py +++ b/examples/device/curand_xorwow_uniform.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -11,7 +11,6 @@ """ import numpy as np -from numpy.testing import assert_allclose from numba import cuda diff --git a/examples/fft/caching.py b/examples/fft/caching.py index 8a18e8a..fab93bc 100644 --- a/examples/fft/caching.py +++ b/examples/fft/caching.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -36,7 +36,7 @@ def fft( cache: dict | None = None, ): """ - A cached version of FFT, taking a cache argument in addition the the regular arguments + A cached version of FFT, taking a cache argument in addition to the regular arguments for fft(). The stateful objects are cached in the provided cache, and reused. Args: diff --git a/examples/fft/example01_cupy.py b/examples/fft/example01_cupy.py index cb53175..6b74382 100644 --- a/examples/fft/example01_cupy.py +++ b/examples/fft/example01_cupy.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example01_cupy_layouts.py b/examples/fft/example01_cupy_layouts.py index bc7c3b4..c47cd09 100644 --- a/examples/fft/example01_cupy_layouts.py +++ b/examples/fft/example01_cupy_layouts.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example01_numpy.py b/examples/fft/example01_numpy.py index ec86624..b9a6e49 100644 --- a/examples/fft/example01_numpy.py +++ b/examples/fft/example01_numpy.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example01_numpy_cpu_execution.py b/examples/fft/example01_numpy_cpu_execution.py index 7556537..5946652 100644 --- a/examples/fft/example01_numpy_cpu_execution.py +++ b/examples/fft/example01_numpy_cpu_execution.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example01_torch_complex32.py b/examples/fft/example01_torch_complex32.py index ab19051..8488d6c 100644 --- a/examples/fft/example01_torch_complex32.py +++ b/examples/fft/example01_torch_complex32.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example02_stateful_cupy.py b/examples/fft/example02_stateful_cupy.py index f9a1d2c..fb5727f 100644 --- a/examples/fft/example02_stateful_cupy.py +++ b/examples/fft/example02_stateful_cupy.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example02_stateful_torch.py b/examples/fft/example02_stateful_torch.py index 7e1f61c..2724238 100644 --- a/examples/fft/example02_stateful_torch.py +++ b/examples/fft/example02_stateful_torch.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example02_stateful_torch_cpu.py b/examples/fft/example02_stateful_torch_cpu.py index f3da766..c5bd048 100644 --- a/examples/fft/example02_stateful_torch_cpu.py +++ b/examples/fft/example02_stateful_torch_cpu.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example02_stateful_torch_cpu_execution.py b/examples/fft/example02_stateful_torch_cpu_execution.py index d2a40cf..9c9812b 100644 --- a/examples/fft/example02_stateful_torch_cpu_execution.py +++ b/examples/fft/example02_stateful_torch_cpu_execution.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example03_options.py b/examples/fft/example03_options.py index 70e3496..3514fe0 100644 --- a/examples/fft/example03_options.py +++ b/examples/fft/example03_options.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example03_options_cpu_execution.py b/examples/fft/example03_options_cpu_execution.py index 0bffe74..7d1f82d 100644 --- a/examples/fft/example03_options_cpu_execution.py +++ b/examples/fft/example03_options_cpu_execution.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example04_logging_global.py b/examples/fft/example04_logging_global.py index 763a894..d77b8ba 100644 --- a/examples/fft/example04_logging_global.py +++ b/examples/fft/example04_logging_global.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example04_logging_user.py b/examples/fft/example04_logging_user.py index 02b1da6..bf969e2 100644 --- a/examples/fft/example04_logging_user.py +++ b/examples/fft/example04_logging_user.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example05_stateful_inplace.py b/examples/fft/example05_stateful_inplace.py index 633efe7..398ed0b 100644 --- a/examples/fft/example05_stateful_inplace.py +++ b/examples/fft/example05_stateful_inplace.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example05_stateful_reset.py b/examples/fft/example05_stateful_reset.py index 2a8a310..e81ec88 100644 --- a/examples/fft/example05_stateful_reset.py +++ b/examples/fft/example05_stateful_reset.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example06_r2c.py b/examples/fft/example06_r2c.py index bcaf983..b9c3d52 100644 --- a/examples/fft/example06_r2c.py +++ b/examples/fft/example06_r2c.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example07_c2r.py b/examples/fft/example07_c2r.py index ad286aa..c15108a 100644 --- a/examples/fft/example07_c2r.py +++ b/examples/fft/example07_c2r.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example07_c2r_odd.py b/examples/fft/example07_c2r_odd.py index 78a3984..2eec232 100644 --- a/examples/fft/example07_c2r_odd.py +++ b/examples/fft/example07_c2r_odd.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example08_cupy_inplace.py b/examples/fft/example08_cupy_inplace.py index 689619f..e16b5b1 100644 --- a/examples/fft/example08_cupy_inplace.py +++ b/examples/fft/example08_cupy_inplace.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example08_numpy_inplace.py b/examples/fft/example08_numpy_inplace.py index 9b48608..0d930be 100644 --- a/examples/fft/example08_numpy_inplace.py +++ b/examples/fft/example08_numpy_inplace.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example08_numpy_inplace_cpu_execution.py b/examples/fft/example08_numpy_inplace_cpu_execution.py index 4bbd491..d5d9db3 100644 --- a/examples/fft/example08_numpy_inplace_cpu_execution.py +++ b/examples/fft/example08_numpy_inplace_cpu_execution.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example09_streams.py b/examples/fft/example09_streams.py index a13369e..6eea6fb 100644 --- a/examples/fft/example09_streams.py +++ b/examples/fft/example09_streams.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example10_memory_allocator.py b/examples/fft/example10_memory_allocator.py index 1c4b271..5d45930 100644 --- a/examples/fft/example10_memory_allocator.py +++ b/examples/fft/example10_memory_allocator.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example11_resource_mgmt.py b/examples/fft/example11_resource_mgmt.py index 40b90d9..2335b3f 100644 --- a/examples/fft/example11_resource_mgmt.py +++ b/examples/fft/example11_resource_mgmt.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example12_stateful_unsupported_fallback.py b/examples/fft/example12_stateful_unsupported_fallback.py index 632bbfc..d7e468c 100644 --- a/examples/fft/example12_stateful_unsupported_fallback.py +++ b/examples/fft/example12_stateful_unsupported_fallback.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example12_unsupported_fallback.py b/examples/fft/example12_unsupported_fallback.py index 9fd7522..d48eca2 100644 --- a/examples/fft/example12_unsupported_fallback.py +++ b/examples/fft/example12_unsupported_fallback.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example13_cupy_mt_mgpu.py b/examples/fft/example13_cupy_mt_mgpu.py index d6bc1e3..6c31908 100644 --- a/examples/fft/example13_cupy_mt_mgpu.py +++ b/examples/fft/example13_cupy_mt_mgpu.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example13_numpy_mp_mgpu.py b/examples/fft/example13_numpy_mp_mgpu.py index c3cfefe..d523bc4 100644 --- a/examples/fft/example13_numpy_mp_mgpu.py +++ b/examples/fft/example13_numpy_mp_mgpu.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example14_caching.py b/examples/fft/example14_caching.py index cb33745..8f1623a 100644 --- a/examples/fft/example14_caching.py +++ b/examples/fft/example14_caching.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example15_cupy_nd_fft_benchmark.py b/examples/fft/example15_cupy_nd_fft_benchmark.py index adcb257..9201229 100644 --- a/examples/fft/example15_cupy_nd_fft_benchmark.py +++ b/examples/fft/example15_cupy_nd_fft_benchmark.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example16_cupy_nd_fft_benchmark.py b/examples/fft/example16_cupy_nd_fft_benchmark.py index 104eccc..787cfa2 100644 --- a/examples/fft/example16_cupy_nd_fft_benchmark.py +++ b/examples/fft/example16_cupy_nd_fft_benchmark.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example17_trunc.py b/examples/fft/example17_trunc.py index 45ed02a..dc57084 100644 --- a/examples/fft/example17_trunc.py +++ b/examples/fft/example17_trunc.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example18_5D_trunc.py b/examples/fft/example18_5D_trunc.py index 0989465..f9f1f3f 100644 --- a/examples/fft/example18_5D_trunc.py +++ b/examples/fft/example18_5D_trunc.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example19_convolution_epilog_callback.py b/examples/fft/example19_convolution_epilog_callback.py index d5a95d2..c068092 100644 --- a/examples/fft/example19_convolution_epilog_callback.py +++ b/examples/fft/example19_convolution_epilog_callback.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example19_convolution_memory_layout_callback.py b/examples/fft/example19_convolution_memory_layout_callback.py index fa251b9..3b6b48d 100644 --- a/examples/fft/example19_convolution_memory_layout_callback.py +++ b/examples/fft/example19_convolution_memory_layout_callback.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/example19_convolution_prolog_callback.py b/examples/fft/example19_convolution_prolog_callback.py index 8decd42..81a9052 100644 --- a/examples/fft/example19_convolution_prolog_callback.py +++ b/examples/fft/example19_convolution_prolog_callback.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/fftn1.py b/examples/fft/fftn1.py index 8a034c2..8401aff 100644 --- a/examples/fft/fftn1.py +++ b/examples/fft/fftn1.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/fftn2.py b/examples/fft/fftn2.py index 298943a..f6f7af3 100644 --- a/examples/fft/fftn2.py +++ b/examples/fft/fftn2.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/fft/truncation.py b/examples/fft/truncation.py index 8aeb93d..4c39ef2 100644 --- a/examples/fft/truncation.py +++ b/examples/fft/truncation.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example01_cupy.py b/examples/linalg/advanced/matmul/example01_cupy.py index 25a4fa0..b692c1c 100644 --- a/examples/linalg/advanced/matmul/example01_cupy.py +++ b/examples/linalg/advanced/matmul/example01_cupy.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example01_cupy_complex64.py b/examples/linalg/advanced/matmul/example01_cupy_complex64.py index c52ff53..687115d 100644 --- a/examples/linalg/advanced/matmul/example01_cupy_complex64.py +++ b/examples/linalg/advanced/matmul/example01_cupy_complex64.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example01_numpy.py b/examples/linalg/advanced/matmul/example01_numpy.py index 258b744..7d03653 100644 --- a/examples/linalg/advanced/matmul/example01_numpy.py +++ b/examples/linalg/advanced/matmul/example01_numpy.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example01_torch.py b/examples/linalg/advanced/matmul/example01_torch.py index d6151cc..34b542a 100644 --- a/examples/linalg/advanced/matmul/example01_torch.py +++ b/examples/linalg/advanced/matmul/example01_torch.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -25,7 +25,7 @@ # No synchronization is needed for CPU tensors, since the execution always blocks. -# Check if the result is numpy array as well. +# Check if the result is torch tensor as well. print(f"Inputs were of types {type(a)} and {type(b)} and the result is of type {type(result)}.") print(f"Inputs were located on devices {a.device} and {b.device} and the result is on {result.device}") diff --git a/examples/linalg/advanced/matmul/example02_options.py b/examples/linalg/advanced/matmul/example02_options.py index 395a595..f3ebdf7 100644 --- a/examples/linalg/advanced/matmul/example02_options.py +++ b/examples/linalg/advanced/matmul/example02_options.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example03_logging_global.py b/examples/linalg/advanced/matmul/example03_logging_global.py index c7d995d..bf4dd43 100644 --- a/examples/linalg/advanced/matmul/example03_logging_global.py +++ b/examples/linalg/advanced/matmul/example03_logging_global.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example03_logging_user.py b/examples/linalg/advanced/matmul/example03_logging_user.py index 7a30b3b..333d3d1 100644 --- a/examples/linalg/advanced/matmul/example03_logging_user.py +++ b/examples/linalg/advanced/matmul/example03_logging_user.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example04_stateful_cupy.py b/examples/linalg/advanced/matmul/example04_stateful_cupy.py index a39ec0b..b84c011 100644 --- a/examples/linalg/advanced/matmul/example04_stateful_cupy.py +++ b/examples/linalg/advanced/matmul/example04_stateful_cupy.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example04_stateful_torch.py b/examples/linalg/advanced/matmul/example04_stateful_torch.py index 87e6917..ae37fc9 100644 --- a/examples/linalg/advanced/matmul/example04_stateful_torch.py +++ b/examples/linalg/advanced/matmul/example04_stateful_torch.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example04_stateful_torch_cpu.py b/examples/linalg/advanced/matmul/example04_stateful_torch_cpu.py index 3ad4aa0..5540c28 100644 --- a/examples/linalg/advanced/matmul/example04_stateful_torch_cpu.py +++ b/examples/linalg/advanced/matmul/example04_stateful_torch_cpu.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example05_stateful_inplace.py b/examples/linalg/advanced/matmul/example05_stateful_inplace.py index 5d66faa..2721f7f 100644 --- a/examples/linalg/advanced/matmul/example05_stateful_inplace.py +++ b/examples/linalg/advanced/matmul/example05_stateful_inplace.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -13,20 +13,20 @@ execution also happens on the GPU. """ +import logging + import cupy as cp import nvmath +# Turn on logging to see what's happening. +logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)-8s %(message)s", datefmt="%m-%d %H:%M:%S") + # Prepare sample input data m, n, k = 123, 456, 789 a = cp.random.rand(m, k) b = cp.random.rand(k, n) -# Turn on logging to see what's happening. -import logging - -logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)-8s %(message)s", datefmt="%m-%d %H:%M:%S") - # Use the stateful object as a context manager to automatically release resources. with nvmath.linalg.advanced.Matmul(a, b) as mm: # Plan the matrix multiplication. Planning returns a sequence of algorithms that can be diff --git a/examples/linalg/advanced/matmul/example05_stateful_reset.py b/examples/linalg/advanced/matmul/example05_stateful_reset.py index 3f838ee..462b8b6 100644 --- a/examples/linalg/advanced/matmul/example05_stateful_reset.py +++ b/examples/linalg/advanced/matmul/example05_stateful_reset.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -11,21 +11,21 @@ The inputs as well as the result are NumPy ndarrays. """ +import logging + import numpy as np import nvmath +# Turn on logging to see what's happening. +logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)-8s %(message)s", datefmt="%m-%d %H:%M:%S") + # Prepare sample input data m, n, k = 123, 456, 789 m, n, k = 2, 3, 4 a = np.random.rand(m, k) b = np.random.rand(k, n) -# Turn on logging to see what's happening. -import logging - -logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)-8s %(message)s", datefmt="%m-%d %H:%M:%S") - # Use the stateful object as a context manager to automatically release resources. with nvmath.linalg.advanced.Matmul(a, b) as mm: # Plan the matrix multiplication. Planning returns a sequence of algorithms that can be diff --git a/examples/linalg/advanced/matmul/example06_gemm.py b/examples/linalg/advanced/matmul/example06_gemm.py index de60b9a..358512f 100644 --- a/examples/linalg/advanced/matmul/example06_gemm.py +++ b/examples/linalg/advanced/matmul/example06_gemm.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example07_batched_a.py b/examples/linalg/advanced/matmul/example07_batched_a.py index 6404851..fff7a19 100644 --- a/examples/linalg/advanced/matmul/example07_batched_a.py +++ b/examples/linalg/advanced/matmul/example07_batched_a.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example07_batched_a_b.py b/examples/linalg/advanced/matmul/example07_batched_a_b.py index 176cd02..90235a3 100644 --- a/examples/linalg/advanced/matmul/example07_batched_a_b.py +++ b/examples/linalg/advanced/matmul/example07_batched_a_b.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example08_batched_a_bcast_c.py b/examples/linalg/advanced/matmul/example08_batched_a_bcast_c.py index ff63ca5..bfc8017 100644 --- a/examples/linalg/advanced/matmul/example08_batched_a_bcast_c.py +++ b/examples/linalg/advanced/matmul/example08_batched_a_bcast_c.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -23,7 +23,7 @@ m = n = k = 2000 a_batch = cp.random.rand(batch_size, m, k) b = cp.random.rand(k, n) -c = cp.random.rand(m) +c = cp.random.rand(m, 1) beta = 1.2 print(f"a shape is: {a_batch.shape}, b shape is: {b.shape} and c shape is: {c.shape}") diff --git a/examples/linalg/advanced/matmul/example08_batched_a_c.py b/examples/linalg/advanced/matmul/example08_batched_a_c.py index 9307946..a9a3234 100644 --- a/examples/linalg/advanced/matmul/example08_batched_a_c.py +++ b/examples/linalg/advanced/matmul/example08_batched_a_c.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example09_epilog_bias.py b/examples/linalg/advanced/matmul/example09_epilog_bias.py index e3cab2a..7bfc7f6 100644 --- a/examples/linalg/advanced/matmul/example09_epilog_bias.py +++ b/examples/linalg/advanced/matmul/example09_epilog_bias.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example09_epilog_gelu_bias.py b/examples/linalg/advanced/matmul/example09_epilog_gelu_bias.py index fd356cf..ae2731c 100644 --- a/examples/linalg/advanced/matmul/example09_epilog_gelu_bias.py +++ b/examples/linalg/advanced/matmul/example09_epilog_gelu_bias.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example10_epilog_dgelu.py b/examples/linalg/advanced/matmul/example10_epilog_dgelu.py index 5d2a2f0..9b48981 100644 --- a/examples/linalg/advanced/matmul/example10_epilog_dgelu.py +++ b/examples/linalg/advanced/matmul/example10_epilog_dgelu.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example10_epilog_drelu.py b/examples/linalg/advanced/matmul/example10_epilog_drelu.py index c86f8c0..80e8639 100644 --- a/examples/linalg/advanced/matmul/example10_epilog_drelu.py +++ b/examples/linalg/advanced/matmul/example10_epilog_drelu.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example10_epilog_relu_aux.py b/examples/linalg/advanced/matmul/example10_epilog_relu_aux.py index 3e8879f..fc68b99 100644 --- a/examples/linalg/advanced/matmul/example10_epilog_relu_aux.py +++ b/examples/linalg/advanced/matmul/example10_epilog_relu_aux.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example11_epilog_drelu_bgrad.py b/examples/linalg/advanced/matmul/example11_epilog_drelu_bgrad.py index d002697..25af423 100644 --- a/examples/linalg/advanced/matmul/example11_epilog_drelu_bgrad.py +++ b/examples/linalg/advanced/matmul/example11_epilog_drelu_bgrad.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example12_epilog_bgrada.py b/examples/linalg/advanced/matmul/example12_epilog_bgrada.py index c13ed63..5b610d8 100644 --- a/examples/linalg/advanced/matmul/example12_epilog_bgrada.py +++ b/examples/linalg/advanced/matmul/example12_epilog_bgrada.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example12_epilog_bgradb.py b/examples/linalg/advanced/matmul/example12_epilog_bgradb.py index 51e1b41..a8a9cd6 100644 --- a/examples/linalg/advanced/matmul/example12_epilog_bgradb.py +++ b/examples/linalg/advanced/matmul/example12_epilog_bgradb.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example13_epilog_stateful_reset.py b/examples/linalg/advanced/matmul/example13_epilog_stateful_reset.py index 87cf5bd..638dac1 100644 --- a/examples/linalg/advanced/matmul/example13_epilog_stateful_reset.py +++ b/examples/linalg/advanced/matmul/example13_epilog_stateful_reset.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -11,21 +11,21 @@ The inputs as well as the result are NumPy ndarrays. """ +import logging + import numpy as np import nvmath +# Turn on logging to see what's happening. +logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)-8s %(message)s", datefmt="%m-%d %H:%M:%S") + # Prepare sample input data. m, n, k = 123, 456, 789 a = np.random.rand(m, k) b = np.random.rand(k, n) bias = np.random.rand(m, 1) -# Turn on logging to see what's happening. -import logging - -logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)-8s %(message)s", datefmt="%m-%d %H:%M:%S") - # Use the stateful object as a context manager to automatically release resources. with nvmath.linalg.advanced.Matmul(a, b) as mm: # Plan the matrix multiplication for the BIAS epilog. diff --git a/examples/linalg/advanced/matmul/example14_autotune.py b/examples/linalg/advanced/matmul/example14_autotune.py index b027f98..5aaf1a5 100644 --- a/examples/linalg/advanced/matmul/example14_autotune.py +++ b/examples/linalg/advanced/matmul/example14_autotune.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example15_manual_tuning.py b/examples/linalg/advanced/matmul/example15_manual_tuning.py index 8593864..047804e 100644 --- a/examples/linalg/advanced/matmul/example15_manual_tuning.py +++ b/examples/linalg/advanced/matmul/example15_manual_tuning.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example16_reuse_algorithms.py b/examples/linalg/advanced/matmul/example16_reuse_algorithms.py index 6009d9e..1f32868 100644 --- a/examples/linalg/advanced/matmul/example16_reuse_algorithms.py +++ b/examples/linalg/advanced/matmul/example16_reuse_algorithms.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/examples/linalg/advanced/matmul/example17_fp8.py b/examples/linalg/advanced/matmul/example17_fp8.py new file mode 100644 index 0000000..417a38e --- /dev/null +++ b/examples/linalg/advanced/matmul/example17_fp8.py @@ -0,0 +1,41 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +This example demonstrates basic matrix multiplication of FP8 tensors. + +In narrow-precision operations, quantization scales must be provided for each tensor. These +scales are used to dequantize input operands and quantize the result. Without proper +scaling, the results of FP8 operations will likely exceed the type's range. + +FP8 is only supported with cuBLAS 12.8 or newer and on devices with compute +capability 8.9 or higher. +""" + +import torch + +import nvmath + +# Prepare sample input data. Note that N, M and K must be divisible by 16 for FP8. +# cuBLAS requires B to be column-major, so we first create a row-major tensor and then +# transpose it. +m, n, k = 64, 32, 48 +a = (torch.rand(m, k, device="cuda") * 10).type(torch.float8_e4m3fn) +b = (torch.rand(n, k, device="cuda") * 10).type(torch.float8_e4m3fn).T + +# Prepare quantization scales. The scales must allow the result to fit within the dynamic +# range of the data type used. Scales can be provided either as a dictionary or as a +# MatmulQuantizationScales object. Note that scales are only allowed for FP8 operands. +scales = {"a": 1, "b": 1, "d": 0.1} + +# Perform the multiplication. The result of the multiplication will be: +# (scales.a * A) @ (scales.b * B) * scales.d +result = nvmath.linalg.advanced.matmul(a, b, quantization_scales=scales) + +# Check how scaling helped to fit into the dynamic range of float8_e4m3fn type. +result_without_scaling = nvmath.linalg.advanced.matmul(a, b, quantization_scales={"a": 1, "b": 1, "d": 1}) +print("Without scaling, most of the elements were clamped to the maximum value of float8_e4m3fn type (448):") +print(result_without_scaling) +print(f"\nWith D scale set to {scales['d']}, they were scaled down to fit into the dynamic range of float8_e4m3fn:") +print(result) diff --git a/examples/linalg/advanced/matmul/example18_fp8_types.py b/examples/linalg/advanced/matmul/example18_fp8_types.py new file mode 100644 index 0000000..be657f9 --- /dev/null +++ b/examples/linalg/advanced/matmul/example18_fp8_types.py @@ -0,0 +1,69 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +This example demonstrates how different data types can be used in FP8 multiplication. + +Two kinds of FP8 are supported: float8_e4m3fn and float8_e5m2, which have 4 and 5 bits for +the exponent respectively. We support e4m3*e4m3, e4m3*e5m2, and e5m2*e4m3 operations. + +In FP8 operations, the MatmulOptions.result_type option can be used to specify the desired +output type. For the full list of supported type combinations, please visit the cuBLAS +documentation at https://docs.nvidia.com/cuda/cublas/#cublasltmatmul. + +FP8 is only supported with cuBLAS 12.8 or newer and on devices with compute +capability 8.9 or higher. +""" + +import torch + +import nvmath + +# Prepare sample input data. Note that A and B are FP8 numbers of different types. +m, n, k = 64, 32, 48 +a = torch.rand(m, k, device="cuda").type(torch.float8_e5m2) +b = torch.rand(n, k, device="cuda").type(torch.float8_e4m3fn).T + +# Perform the multiplication, requesting FP32 output. Note that a scale for the result (D +# is not specified because it is not FP8. +result_fp32 = nvmath.linalg.advanced.matmul( + a, b, quantization_scales={"a": 1, "b": 1}, options={"result_type": nvmath.CudaDataType.CUDA_R_32F} +) + +# Perform the multiplication, requesting FP16 output. +result_fp16 = nvmath.linalg.advanced.matmul( + a, b, quantization_scales={"a": 1, "b": 1}, options={"result_type": nvmath.CudaDataType.CUDA_R_16F} +) + +# Now, request FP8 (e4m3fn) output. We set the scale for D to 1 for simplicity - with small +# values in A and B, we won't exceed the range of the type anyway. +result_fp8_e4m3fn = nvmath.linalg.advanced.matmul( + a, b, quantization_scales={"a": 1, "b": 1, "d": 1}, options={"result_type": nvmath.CudaDataType.CUDA_R_8F_E4M3} +) + +# Finally, request FP8 (e5m2) output. +result_fp8_e5m2fn = nvmath.linalg.advanced.matmul( + a, b, quantization_scales={"a": 1, "b": 1, "d": 1}, options={"result_type": nvmath.CudaDataType.CUDA_R_8F_E5M2} +) + + +# Print mean relative error for each of the types +def mean_relative_error_vs_fp32(x): + reference = result_fp32.cpu() + actual = x.cpu().type(torch.float32) + return ((reference - actual).abs() / reference.abs()).mean() + + +print(f"{result_fp32.dtype=}.") +print( + f"{result_fp16.dtype=}. The mean relative error to the FP32 reference is {mean_relative_error_vs_fp32(result_fp16):.07f}." +) +print( + f"{result_fp8_e4m3fn.dtype=}. The mean relative error to the FP32 reference is " + f"{mean_relative_error_vs_fp32(result_fp8_e4m3fn):.07f}." +) +print( + f"{result_fp8_e5m2fn.dtype=}. The mean relative error to the FP32 reference is " + f"{mean_relative_error_vs_fp32(result_fp8_e5m2fn):.07f}." +) diff --git a/examples/linalg/advanced/matmul/example19_fp8_reset.py b/examples/linalg/advanced/matmul/example19_fp8_reset.py new file mode 100644 index 0000000..18822db --- /dev/null +++ b/examples/linalg/advanced/matmul/example19_fp8_reset.py @@ -0,0 +1,46 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +This example demonstrates how the reset_operands method of a Matmul object can be used to +change both the operands and their quantization scales. + +FP8 is only supported with cuBLAS 12.8 or newer and on devices with compute +capability 8.9 or higher. +""" + +import torch + +import nvmath + +# Prepare sample input data +m, n, k = 128, 256, 16 +a = torch.ones(m, k, device="cuda", dtype=torch.float8_e5m2) +b = torch.ones(n, k, device="cuda", dtype=torch.float8_e4m3fn).T +print(f"A = \n{a}") +print(f"\nB = \n{b}") + +scales = {"a": 3, "b": 2, "d": 1} + +with nvmath.linalg.advanced.Matmul( + a, b, quantization_scales=scales, options={"result_type": nvmath.CudaDataType.CUDA_R_8F_E5M2} +) as mm: + # Plan the multiplication + mm.plan() + + # Execute the multiplication and print the result + result = mm.execute() + print(f"\nA (A scale: {scales['a']}) @ B (B scale: {scales['b']}) = (D scale: {scales['d']}) \n{result}") + + # Replace A with a matrix filled with 128 and adjust A and D scales. + # Note that since no new scale for B is specified, it will remain unchanged. + new_a = torch.full((m, k), 128, device="cuda").type(torch.float8_e5m2) + print(f"\nnew A = \n{new_a}") + new_a_scale = 1 + new_d_scale = 0.01 + mm.reset_operands(a=new_a, quantization_scales={"a": new_a_scale, "d": new_d_scale}) + + # Execute the multiplication again and print the new result + result2 = mm.execute() + print(f"\nA (A scale: {new_a_scale}) @ B (B scale: {scales['b']}) = (D scale: {new_d_scale}) \n{result2}") diff --git a/examples/linalg/advanced/matmul/example20_fp8_inplace_scale_change.py b/examples/linalg/advanced/matmul/example20_fp8_inplace_scale_change.py new file mode 100644 index 0000000..7f9e915 --- /dev/null +++ b/examples/linalg/advanced/matmul/example20_fp8_inplace_scale_change.py @@ -0,0 +1,52 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +This example demonstrates how quantization scales passed as GPU tensors can be modified +in-place without needing to call reset_operands(). + +FP8 is only supported with cuBLAS 12.8 or newer and on devices with compute +capability 8.9 or higher. +""" + +import torch + +import nvmath + +# Prepare sample input data +m, n, k = 128, 256, 16 +a = torch.ones(m, k, device="cuda", dtype=torch.float8_e4m3fn) +b = torch.ones(n, k, device="cuda", dtype=torch.float8_e5m2).T +print(f"A = \n{a}") +print(f"\nB = \n{b}") + +# Create 1D single-element float32 GPU tensors to hold the quantization scales. +# These will be modified in-place later. +scales = { + "a": torch.full((1,), 3, dtype=torch.float32, device="cuda"), + "b": torch.full((1,), 2, dtype=torch.float32, device="cuda"), + "d": torch.full((1,), 1, dtype=torch.float32, device="cuda"), +} + +with nvmath.linalg.advanced.Matmul( + a, b, quantization_scales=scales, options={"result_type": nvmath.CudaDataType.CUDA_R_8F_E5M2} +) as mm: + # Plan the multiplication + mm.plan() + + # Execute the multiplication and print the result + result = mm.execute() + print( + f"\nA (A scale: {scales['a'].item()}) @ B (B scale: {scales['b'].item()}) = (D scale: {scales['d'].item()}) \n{result}" + ) + + # Modify the quantization scales for A and D in-place + scales["a"][:] = 2 + scales["d"][:] = 0.25 + + # Execute the multiplication again with the new quantization scales and print the result + result2 = mm.execute() + print( + f"\nA (A scale: {scales['a'].item()}) @ B (B scale: {scales['b'].item()}) = (D scale: {scales['d'].item()}) \n{result2}" + ) diff --git a/examples/linalg/advanced/matmul/example21_fp8_amax.py b/examples/linalg/advanced/matmul/example21_fp8_amax.py new file mode 100644 index 0000000..0176a4a --- /dev/null +++ b/examples/linalg/advanced/matmul/example21_fp8_amax.py @@ -0,0 +1,54 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +This example demonstrates how to obtain the maximum absolute value (amax) in the result, +computed before quantization. + +In previous examples, quantization scales were set manually to appropriate values. Amax can +be used to automatically set proper scales in FP8 operations, as it indicates how much the +result needs to be scaled to fit into the dynamic range of the result type. In this example, +we first compute the result without scaling, then use amax to compute the correct scale, and +repeat the multiplication. While this approach is inefficient, it demonstrates the concept. +For a more practical example, see the `fp8_delayed_scaling` example, which uses amax from +previous iterations to choose scales for subsequent multiplications. + +FP8 is only supported with cuBLAS 12.8 or newer and on devices with compute +capability 8.9 or higher. +""" + +import torch + +import nvmath + +# Fill the input tensors with random numbers from (0, 30). +m, n, k = 128, 128, 16 +a = (torch.rand(m, k, device="cuda") * 30).type(torch.float8_e4m3fn) +b = (torch.rand(n, k, device="cuda") * 30).type(torch.float8_e4m3fn).T + +# To request amax, set `result_amax` option to True. +options = {"result_amax": True} + +# When result_amax is set, a tuple containing the actual result and the auxiliary outputs +# will be returned instead of just the result. +result, aux = nvmath.linalg.advanced.matmul(a, b, quantization_scales={"a": 1, "b": 1, "d": 1}, options=options) + +# With all quantization scales set to 1, most of the elements are clamped to the maximum +# value: +print("Result is:") +print(result) + +# Amax will be present in the auxiliary outputs dictionary as "result_amax". +print(f"Matmul returned the result and the auxiliary outputs of type {type(aux)}: {aux}") + +# Compute the proper scale by dividing the maximum representable value by amax. +max_representable_value = 448 +amax = aux["result_amax"].item() +d_scale = max_representable_value / amax +print(f"d_scale = max_representable_value / amax = {max_representable_value} / {amax:.5f} = {d_scale:.5f}") + +# Repeat the computation, this time using the proper scale for D. +result2 = nvmath.linalg.advanced.matmul(a, b, quantization_scales={"a": 1, "b": 1, "d": d_scale}) +print(f"Result (with D scale set to {d_scale:.5f}) is:") +print(result2) diff --git a/examples/linalg/advanced/matmul/example22_fp8_delayed_scaling.py b/examples/linalg/advanced/matmul/example22_fp8_delayed_scaling.py new file mode 100644 index 0000000..78629b4 --- /dev/null +++ b/examples/linalg/advanced/matmul/example22_fp8_delayed_scaling.py @@ -0,0 +1,64 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +This example demonstrates how to implement a simple delayed scaling algorithm. We use the +amax value from the previous iteration to set the scale for the next iteration. In a more +advanced setup, an average amax from N previous iterations could be used as well. In each +iteration, we multiply two normally-distributed matrices A and B and add matrix C to the +result. + +FP8 is only supported with cuBLAS 12.8 or newer and on devices with compute +capability 8.9 or higher. +""" + +import torch + +import nvmath + +m, n, k = 256, 256, 256 +a = torch.zeros(m, k, device="cuda", dtype=torch.float8_e4m3fn) +b = torch.zeros(n, k, device="cuda", dtype=torch.float8_e4m3fn).T +c = torch.zeros(m, n, device="cuda", dtype=torch.float16) + + +def regenerate_inputs(): + a[:] = torch.randn(a.shape, device="cuda") * 10 + b[:] = torch.randn(b.shape, device="cuda") * 10 + c[:] = torch.randn(c.shape, device="cuda") * 10 + return a, b, c + + +# Keep D scale in a GPU tensor instead of a Python float to allow in-place changes +dscale = torch.ones((1,), dtype=torch.float32, device="cuda") +scales = {"a": 1, "b": 1, "d": dscale} + +# Request FP8 output and AMAX calculation +options = {"result_type": nvmath.CudaDataType.CUDA_R_8F_E4M3, "result_amax": True} + +with nvmath.linalg.advanced.Matmul(a, b, c=c, beta=1, quantization_scales=scales, options=options) as mm: + mm.plan() + + for iteration in range(10): + # Populate a, b, and c with fresh random data + regenerate_inputs() + + # Execute the matrix multiplication + result, aux = mm.execute() + amax = aux["result_amax"] + + # Calculate the percentage of clamped values + max_representable_value = 448 + clamped_percent = ( + 100 * ((result == max_representable_value) | (result == -max_representable_value)).sum().item() / result.nelement() + ) + + # Print a report. Note that the percentage of clamped values will rapidly decrease + print( + f"Iteration {iteration} with dscale={dscale.item():05f}: " + f"amax={amax.item():.2f}, {clamped_percent:.02f}% of values were clamped to the max value." + ) + + # Update D scale for the next iteration + dscale[:] = max_representable_value / amax diff --git a/examples/linalg/advanced/matmul/example23_fp8_epilog.py b/examples/linalg/advanced/matmul/example23_fp8_epilog.py new file mode 100644 index 0000000..2f94144 --- /dev/null +++ b/examples/linalg/advanced/matmul/example23_fp8_epilog.py @@ -0,0 +1,45 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +This example demonstrates using RELU epilog with FP8 matrix multiplication. + +In FP8 operations, quantization scales must be provided for each tensor. These scales are +used to dequantize input operands and quantize the result. The RELU epilog is applied +after scaling but before final quantization. + +FP8 is only supported with cuBLAS 12.8 or newer and on devices with compute +capability 8.9 or higher. +""" + +import torch + +import nvmath + +# Prepare sample input data with some negative values +m, n, k = 64, 32, 48 +a = (torch.rand(m, k, device="cuda") * 20 - 10).type(torch.float8_e4m3fn) +b = (torch.rand(n, k, device="cuda") * 20 - 10).type(torch.float8_e4m3fn).T + +# Set quantization scales to keep values in range +scales = {"a": 1, "b": 1, "d": 0.1} + +# First perform multiplication without RELU +result_no_relu = nvmath.linalg.advanced.matmul(a, b, quantization_scales=scales) + +# Now perform multiplication with RELU epilog +result_with_relu = nvmath.linalg.advanced.matmul( + a, + b, + epilog=nvmath.linalg.advanced.MatmulEpilog.RELU, + quantization_scales=scales, +) + +print("Result without RELU (notice negative values):") +print(result_no_relu) +print("\nResult with RELU (all values >= 0):") +print(result_with_relu) + +# Verify that all values in the RELU result are non-negative +assert torch.all(result_with_relu.type(torch.float32) >= 0), "RELU result contains negative values!" diff --git a/examples/linalg/advanced/matmul/example24_fp8_epilog_aux.py b/examples/linalg/advanced/matmul/example24_fp8_epilog_aux.py new file mode 100644 index 0000000..2df34be --- /dev/null +++ b/examples/linalg/advanced/matmul/example24_fp8_epilog_aux.py @@ -0,0 +1,69 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +This example demonstrates using GELU_AUX epilog with FP8 outputs. + +For GELU_AUX epilog, when A and B are e4m3fn, you can request the auxiliary output to +be returned as FP8. To request FP8 auxiliary output, set epilog.aux_type to an FP8 type +in MatmulPlanPreferences. + +You can specify the scale for this auxiliary output by passing the scale +as "epilog_aux_scale" input in `epilog_inputs`. Additionally, you can request amax to be +computed for this output by setting `epilog.aux_amax=True` in MatmulPlanPreferences. + +Note that FP8 auxiliary outputs are supported only for particular epilogs and type +combinations. For more details on the supported configurations, please see the cuBLAS +documentation. + +FP8 is only supported with cuBLAS 12.8 or newer and on devices with compute +capability 8.9 or higher. +""" + +import torch + +import nvmath + +m, n, k = 64, 64, 64 +a = (torch.randn(m, k, device="cuda") - 0.5).type(torch.float8_e4m3fn) +b = (torch.randn(n, k, device="cuda") - 0.5).type(torch.float8_e4m3fn).T + +scales = {"a": 1, "b": 1, "d": 1} + +# Specify quantization scale to use for auxiliary epilog output +epilog_inputs = {"aux_quantization_scale": 0.1} + +# Instead of a Dict, you may instantiate MatmulPlanPreferences object. +preferences = { + "epilog": { + "aux_type": nvmath.CudaDataType.CUDA_R_8F_E4M3, + "aux_amax": True, + } +} + +# Execute the operation. Note that we pass `preferences` argument. +result, aux = nvmath.linalg.advanced.matmul( + a, + b, + epilog=nvmath.linalg.advanced.MatmulEpilog.GELU_AUX, + epilog_inputs=epilog_inputs, + preferences=preferences, + quantization_scales=scales, +) + +# Print the result. +print("Result:") +print(result) +print() + +# Print the auxiliary values returned. There should be "gelu_aux" (scaled by 0.1) and +# "gelu_aux_amax" containing the maximum absolute value before scaling (amax). +assert set(aux.keys()) == {"gelu_aux", "gelu_aux_amax"} +print(f"Auxiliary outputs are {set(aux.keys())}:") +print(aux) +print() + +print(f"Note that gelu_aux is an FP8 tensor: {aux['gelu_aux'].dtype=}") +print(f"Also, amax has been returned: {aux['gelu_aux_amax']=}") +print(f"Also, amax has been returned: {aux['gelu_aux_amax']=}") diff --git a/examples/linalg/advanced/matmul/example25_mxfp8.py b/examples/linalg/advanced/matmul/example25_mxfp8.py new file mode 100644 index 0000000..09ce8be --- /dev/null +++ b/examples/linalg/advanced/matmul/example25_mxfp8.py @@ -0,0 +1,61 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +This example demonstrates basic matrix multiplication of FP8 tensors using MXFP8 +(microscaled FP8) quantization scales. + +Key differences from FP8: +- MXFP8 scales are applied to each 32-element block of the tensors, rather than using a + single tensor-wide scaling factor. This allows more fine-grained control over scaling + and improves the accuracy of MXFP8 operations. +- MXFP8 scales are uint8 numbers in exponent-only format, representing values of the form + 2^n, where n is an integer between -127 and 128. +- In MXFP8 mode, if D is FP8, it is scaled automatically during the matmul operation and + the quantization scales used are returned as "d_out_scale". This is covered in the next + example. + +To use MXFP8, set the `block_scaling` option to True. + +The layout of the quantization scales is relatively complex. To facilitate working with +MXFP8, we provide helper functions in `nvmath.linalg.advanced.helpers.matmul`. + +FP8 is only supported with cuBLAS 12.8 or newer and on devices with compute +capability 10.0 or higher. +""" + +import torch + +import nvmath + +# Prepare sample input data. Note that N, M and K must be divisible by 128 for MXFP8. +# cuBLAS requires B to be column-major, so we first create a row-major tensor and then +# transpose it. +a = torch.eye(256, device="cuda", dtype=torch.float8_e4m3fn) # A is an identity matrix +b = torch.ones((256, 256), device="cuda", dtype=torch.float8_e4m3fn).T # B is filled with ones + +# Prepare quantization scales for A and B using the `create_mxfp8_scale` helper. +# While MXFP8 allows different scales for different blocks in A and B, +# this helper creates uniform scaling across all blocks. +# For more advanced scale configurations, see the cuBLAS documentation and +# the `get_mxfp8_scale_offset` helper. +scales = { + "a": nvmath.linalg.advanced.helpers.matmul.create_mxfp8_scale(a, -1), # 2^-1 = 0.5 + "b": nvmath.linalg.advanced.helpers.matmul.create_mxfp8_scale(b, 3), # 2^3 = 8 +} + +# Enable block scaling by setting the `block_scaling` option to True. For simplicity, we +# request FP16 output. For FP8 output scaling, see the mxfp8_d_out_scale example. +options = {"block_scaling": True, "result_type": nvmath.CudaDataType.CUDA_R_16F} + +# Perform the multiplication. The result is a tuple (result, aux), where aux +# contains the "d_out_scale" key with the scale used for the result. +result = nvmath.linalg.advanced.matmul(a, b, quantization_scales=scales, options=options) + +# Compute reference result without scaling +reference = a.type(torch.float16) @ b.type(torch.float16) +print(f"Reference result (without scaling):\n{reference}") + +# Print the result with scaling applied +print(f"Result with scaling (A scaled by 0.5, B scaled by 8):\n{result}") diff --git a/examples/linalg/advanced/matmul/example26_mxfp8_d_out.py b/examples/linalg/advanced/matmul/example26_mxfp8_d_out.py new file mode 100644 index 0000000..92d3c1f --- /dev/null +++ b/examples/linalg/advanced/matmul/example26_mxfp8_d_out.py @@ -0,0 +1,78 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +This example demonstrates automatic output scaling in MXFP8 mode. +When using MXFP8, D is automatically scaled during the matmul operation and the scale used +is returned as "d_out_scale". This scale can be used as input for subsequent matrix +multiplications (see mxfp8_chaining example) or applied to the result using a helper +function. + +To use MXFP8, set the `block_scaling` option to True. + +The layout of MXFP8 scales is complex. To simplify working with them, we provide helper +functions in `nvmath.linalg.advanced.helpers.matmul`. For more advanced operations on +MXFP8 scales, please refer to the cuBLAS documentation. + +FP8 is only supported with cuBLAS 12.8 or newer and on devices with compute +capability 10.0 or higher. +""" + +import torch + +import nvmath + +# Prepare sample input data. Note that N, M and K must be divisible by 128 for MXFP8. +m, n, k = 256, 256, 512 + +# Create matrix A with values increasing by row to demonstrate scaling with different +# magnitudes +a = torch.zeros(m, k, device="cuda", dtype=torch.float8_e4m3fn) +a[:] = torch.arange(m)[:, None] # Each row will have progressively larger values +print("Matrix A:") +print(a) +print() + +# cuBLAS requires B to be column-major, so we first create a row-major tensor and then +# transpose it. +b = torch.rand(m, k, device="cuda").type(torch.float8_e4m3fn).T +print("Matrix B:") +print(b) +print() + +# Prepare quantization scales for A and B using the create_mxfp8_scale helper. +# Note: We don't set a scale for D since MXFP8 automatically scales the result to fit +# within the output type's dynamic range. +scales = { + "a": nvmath.linalg.advanced.helpers.matmul.create_mxfp8_scale(a, -6), # 2^-6 = 0.015625 + "b": nvmath.linalg.advanced.helpers.matmul.create_mxfp8_scale(b, 0), # 2^0 = 1 +} + +# Enable block scaling +options = { + "block_scaling": True, +} + +# Perform the multiplication. The result is a tuple containing (result, aux). +# The aux dictionary contains "d_out_scale" - the scale used for the result. +result, aux = nvmath.linalg.advanced.matmul(a, b, quantization_scales=scales, options=options) + +# Display results +print("Result (each block scaled to fit within float8_e4m3fn range):") +print(result) +print() + +# Examine the D_OUT quantization scales +print(f"Auxiliary output contains these keys: {list(aux.keys())}") +print( + f"D scale tensor shape: {aux['d_out_scale'].shape}, type: {aux['d_out_scale'].dtype}. " + f"Contains {len(aux['d_out_scale'].unique())} unique scale factors." +) + +# Apply the scale to get the actual result. Note: This helper function is for demonstration +# purposes and may use significant memory. For production use, set result_type to a +# non-FP8 type instead. +actual_result = nvmath.linalg.advanced.helpers.matmul.apply_mxfp8_scale(result, aux["d_out_scale"]) +print("Final result (with quantization scales applied):") +print(actual_result) diff --git a/examples/linalg/advanced/matmul/example27_mxfp8_chaining.py b/examples/linalg/advanced/matmul/example27_mxfp8_chaining.py new file mode 100644 index 0000000..0b535df --- /dev/null +++ b/examples/linalg/advanced/matmul/example27_mxfp8_chaining.py @@ -0,0 +1,61 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +This example demonstrates how D_OUT quantization scale can be reused as input scale for +subsequent matrix multiplications. In this example, we compute matrix exponentiation by +chaining multiple matrix multiplications, while feeding D_OUT scale as A scale. + +FP8 is only supported with cuBLAS 12.8 or newer and on devices with compute +capability 10.0 or higher. +""" + +import torch + +import nvmath + +size = 256 + +p = 4 + +# We will compute B^p = A*B*B*...*B +a = torch.eye(size, device="cuda", dtype=torch.float8_e4m3fn) # Identity matrix +print("Initial value of A (identity matrix):") +print(a) +print() + +b = ( + (torch.eye(size, device="cuda") * (1 + torch.arange(size, device="cuda"))).type(torch.float8_e4m3fn).T +) # Diagonal matrix with ascending values +print("Initial value of B (diagonal matrix):") +print(b) +print() + +b_scale = nvmath.linalg.advanced.helpers.matmul.create_mxfp8_scale(b, 0) # 2^0 = 1 + +init_scales = { + "a": nvmath.linalg.advanced.helpers.matmul.create_mxfp8_scale(a, 0), # 2^0 = 1 + "b": b_scale, +} + +options = { + "block_scaling": True, +} + +torch.set_printoptions(sci_mode=False) + +with nvmath.linalg.advanced.Matmul(a, b, quantization_scales=init_scales, options=options) as mm: + mm.plan() + for i in range(1, p + 1): + d, aux = mm.execute() + + # Replace A with A*B and use the D_OUT scale as input scale for the new A + d_out_scale = aux["d_out_scale"] + print(f"{d_out_scale=}") + mm.reset_operands(a=d, quantization_scales={"a": d_out_scale}) + + # Print the result with quantization scales applied + print(f"Result of B^{i} (with quantization scales applied):") + print(nvmath.linalg.advanced.helpers.matmul.apply_mxfp8_scale(d, d_out_scale)) + print() diff --git a/examples/linalg/advanced/matmul/example28_mxfp8_epilog.py b/examples/linalg/advanced/matmul/example28_mxfp8_epilog.py new file mode 100644 index 0000000..a0185b3 --- /dev/null +++ b/examples/linalg/advanced/matmul/example28_mxfp8_epilog.py @@ -0,0 +1,37 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +In this example, we perform MXFP8 matrix multiplication with ReLU activation. + +FP8 is only supported with cuBLAS 12.8 or newer and on devices with compute +capability 10.0 or higher. +""" + +import torch + +import nvmath + +# Prepare sample input data with dimensions m=256, n=256, k=512 +m, n, k = 256, 256, 512 +a = torch.randn(m, k, device="cuda").type(torch.float8_e4m3fn) +b = torch.randn(n, k, device="cuda").type(torch.float8_e4m3fn).T + +scales = { + "a": nvmath.linalg.advanced.helpers.matmul.create_mxfp8_scale(a, 0), # Scale factor 2^0 = 1 + "b": nvmath.linalg.advanced.helpers.matmul.create_mxfp8_scale(b, 0), # Scale factor 2^0 = 1 +} + +options = { + "block_scaling": True, +} + +result, aux = nvmath.linalg.advanced.matmul( + a, b, quantization_scales=scales, options=options, epilog=nvmath.linalg.advanced.MatmulEpilog.RELU +) + +# Display the results +print("Result after applying D_OUT scales:") +print(nvmath.linalg.advanced.helpers.matmul.apply_mxfp8_scale(result, aux["d_out_scale"])) +print("All values are non-negative due to the ReLU activation.") diff --git a/examples/linalg/advanced/matmul/example29_mxfp8_layout.py b/examples/linalg/advanced/matmul/example29_mxfp8_layout.py new file mode 100644 index 0000000..ee9b6a1 --- /dev/null +++ b/examples/linalg/advanced/matmul/example29_mxfp8_layout.py @@ -0,0 +1,41 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + +""" +This example demonstrates how to use the get_mxfp8_scale_offset helper function to modify +individual scaling factors in MXFP8 matrix multiplication. + +FP8 is only supported with cuBLAS 12.8 or newer and on devices with compute +capability 10.0 or higher. +""" + +import torch + +import nvmath + +# Prepare sample input data +size = 256 +a = torch.eye(size, device="cuda", dtype=torch.float8_e4m3fn) +b = torch.ones(size, size, device="cuda", dtype=torch.float8_e4m3fn).T + +a_scale = nvmath.linalg.advanced.helpers.matmul.create_mxfp8_scale(a, 0) +b_scale = nvmath.linalg.advanced.helpers.matmul.create_mxfp8_scale(a, 0) + +options = {"block_scaling": True, "result_type": nvmath.CudaDataType.CUDA_R_32F} + +# Compute initial result with all scale factors set to 1 +result = nvmath.linalg.advanced.matmul(a, b, quantization_scales={"a": a_scale, "b": b_scale}, options=options) +print("Initial result with all scale factors set to 1:") +print(result) + +# Use get_mxfp8_scale_offset helper to modify the scale factor for the block containing +# position (2, 1) +offset = nvmath.linalg.advanced.helpers.matmul.get_mxfp8_scale_offset(b, (2, 1)) +b_scale[offset] += 4 # Increase the exponent by 4 + +# Compute result with modified scale factor +result2 = nvmath.linalg.advanced.matmul(a, b, quantization_scales={"a": a_scale, "b": b_scale}, options=options) +print("\nResult after modifying one scale factor:") +print(result2) +print(f"\nThe scale factor modification affected {(result2 != 1).sum().item()} elements in the block.") diff --git a/notebooks/matmul/01_introduction.ipynb b/notebooks/matmul/01_introduction.ipynb index ba6a3f0..719c990 100644 --- a/notebooks/matmul/01_introduction.ipynb +++ b/notebooks/matmul/01_introduction.ipynb @@ -5,7 +5,7 @@ "id": "88073684-ba4e-42eb-9d9e-f7541473ce4f", "metadata": {}, "source": [ - "Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES\n", + "Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES\n", "\n", "SPDX-License-Identifier: BSD-3-Clause" ] @@ -23,7 +23,7 @@ "id": "292c2024-bd73-48de-8320-62c0ec0df645", "metadata": {}, "source": [ - "In this notebook we will demonstrate how to perform GEMM (General Matrix Multiply) with nvmath-python library.\n", + "In this tutorial we will demonstrate how to perform GEMM (General Matrix Multiply) with nvmath-python library.\n", "\n", "We will demonstrate two APIs to execute matrix multiplication with nvmath-python:\n", "- `matmul` function (*stateless API*), which performs a single GEMM on its arguments and returns the result.\n", @@ -402,7 +402,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "id": "2444b8d9-473c-40cf-ba0c-10002598d57e", "metadata": {}, "outputs": [ @@ -423,7 +423,7 @@ "with Matmul(a, b, c=c, alpha=2, beta=0.7) as mm:\n", " mm.plan()\n", " result = mm.execute()\n", - " # mm.free() is no longer needed, the resourced are freed here.\n", + " # mm.free() is no longer needed, the resources are freed by the context manager.\n", "print(result)" ] }, diff --git a/notebooks/matmul/02_epilogs.ipynb b/notebooks/matmul/02_epilogs.ipynb index 3c4d100..fc98742 100644 --- a/notebooks/matmul/02_epilogs.ipynb +++ b/notebooks/matmul/02_epilogs.ipynb @@ -5,7 +5,7 @@ "id": "fb0742d2-785b-4def-9939-9309b0f5c3e7", "metadata": {}, "source": [ - "Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES\n", + "Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES\n", "\n", "SPDX-License-Identifier: BSD-3-Clause" ] @@ -23,7 +23,7 @@ "id": "292c2024-bd73-48de-8320-62c0ec0df645", "metadata": {}, "source": [ - "In this notebook, we will demonstrate the use of cuBLAS *epilogs*. An epilog is a simple computation executed after performing the matrix multiplication. Epilogs are usually much faster than the same computation executed manually on the result." + "In this tutorial, we will demonstrate the use of cuBLAS *epilogs*. An epilog is a simple computation executed after performing the matrix multiplication. Epilogs are usually much faster than the same computation executed manually on the result." ] }, { @@ -315,7 +315,7 @@ "source": [ "## Learning more\n", "\n", - "We will show a practical use case for the epilogs in the next notebook, in which we will implement a simple digit recognition neural network using nvmath-python `matmul` and its epilogs.\n", + "We will show a practical use case for the epilogs in the next tutorial, in which we will implement a simple digit recognition neural network using nvmath-python `matmul` and its epilogs.\n", "\n", "To learn more about the available epilogs, you can visit [cuBLAS documentation on epilogs](https://docs.nvidia.com/cuda/cublas/#cublasltepilogue-t)." ] diff --git a/notebooks/matmul/03_backpropagation.ipynb b/notebooks/matmul/03_backpropagation.ipynb index 2801b56..37ddb0c 100644 --- a/notebooks/matmul/03_backpropagation.ipynb +++ b/notebooks/matmul/03_backpropagation.ipynb @@ -5,7 +5,7 @@ "id": "7853d721-ee63-4177-a01a-0c07f835814d", "metadata": {}, "source": [ - "Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES\n", + "Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES\n", "\n", "SPDX-License-Identifier: BSD-3-Clause" ] @@ -23,7 +23,7 @@ "id": "292c2024-bd73-48de-8320-62c0ec0df645", "metadata": {}, "source": [ - "In this notebook we will demonstrate how you can use nvmath-python matrix multiplication capabilities to implement a simple neural network recognizing digits from MNIST dataset. We will show how matmul epilogs can be used to simplify implementation and improve performance of both forward and backward pass. To learn more about how to use nvmath-python, please refer to the previous notebooks.\n", + "In this tutorial we will demonstrate how you can use nvmath-python matrix multiplication capabilities to implement a simple neural network recognizing digits from MNIST dataset. We will show how matmul epilogs can be used to simplify implementation and improve performance of both forward and backward pass. To learn more about how to use nvmath-python, please refer to the previous tutorials.\n", "\n", "We will use PyTorch to conveniently load and preprocess the data, but the model itself will be implemented in CuPy and nvmath-python." ] @@ -164,7 +164,7 @@ "source": [ "## Implementation baseclass\n", "\n", - "`MnistNetBase` will serve as the base class for our implementation of the model. It handles weight and bias initialization, but doesn't implement forward and backward pass. We will provide implementations for them later in the notebook." + "`MnistNetBase` will serve as the base class for our implementation of the model. It handles weight and bias initialization, but doesn't implement forward and backward pass. We will provide implementations for them later in the tutorial." ] }, { diff --git a/notebooks/matmul/04_fp8.ipynb b/notebooks/matmul/04_fp8.ipynb new file mode 100644 index 0000000..c4b2984 --- /dev/null +++ b/notebooks/matmul/04_fp8.ipynb @@ -0,0 +1,1077 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7f359b74-cfdf-4f89-9ab4-26316904cafb", + "metadata": {}, + "source": [ + "Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES\n", + "\n", + "SPDX-License-Identifier: BSD-3-Clause" + ] + }, + { + "cell_type": "markdown", + "id": "8fecddc0-de29-4943-b0c9-f9be659d5ee4", + "metadata": {}, + "source": [ + "# FP8 computations with nvmath-python\n", + "\n", + "In this tutorial we will introduce the narrow-precision FP8 formats and demonstrate how to use nvmath-python to perform computations in FP8 and MXFP8 formats.\n", + "\n", + "## Table of Contents\n", + "1. Classic floating-point formats\n", + "2. Narrow-precision floating-point formats (FP8)\n", + "3. FP8 operations with nvmath-python\n", + "4. MXFP8 (microscaling FP8) operations with nvmath-python\n", + "\n", + "## Classic floating-point formats\n", + "[Floating point numbers](https://en.wikipedia.org/wiki/Floating-point_arithmetic) consist of a *significand* (also known as *mantissa*) multiplied by an integral power of 2 (*exponent*). Except for certain special cases (such as infinity, NaN, or [subnormal numbers](https://en.wikipedia.org/wiki/Subnormal_number)), the value of the number is:\n", + "$$\n", + "\\pm\\;\\text{significand} \\cdot 2 ^ {\\;\\text{exponent}}\n", + "$$\n", + "\n", + "In a typical floating point number, a fixed number of bits store the exponent and the significand. For example, in the [IEEE 754 32-bit float](https://en.wikipedia.org/wiki/Single-precision_floating-point_format) (also known as single-precision float), 23 bits store the significand, 8 bits store the exponent, and one last bit indicates the sign of the number. \n", + "\n", + "The two most important characteristics of a floating point number are:\n", + "- *Precision*, which is the number of bits in the significand. Precision determines the number of representable values on a certain interval.\n", + "- *Dynamic range*, which is the range of representable values. The dynamic range is determined by the number of bits for the exponent.\n", + "\n", + "### High-precision formats\n", + "\n", + "In scientific computations, where precision is extremely important, single-precision formats are sometimes not enough. For this reason, some formats aim to increase precision and range by using more bits for both the significand and exponent. This includes the [double-precision 64-bit floats](https://en.wikipedia.org/wiki/Double-precision_floating-point_format), which use 11 bits for the exponent and 52 bits for the significand, but also even wider formats, such as [quadruple-precision 128-bit floats](https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format) or [octuple-precision 256-bit floats](https://en.wikipedia.org/wiki/Octuple-precision_floating-point_format).\n", + "\n", + "### FP16 formats\n", + "\n", + "In certain use cases, most notably in the training of deep learning networks, the exact computations are not as important, and lower-precision formats can offer a better trade-off between performance and precision. Using 16-bit floating point numbers, which occupy half the memory of a classic float, can significantly speed up memory-bound computations, while maintaining an acceptable precision. The two most common 16-bit floating point types are:\n", + "\n", + "- [Half-precision](https://en.wikipedia.org/wiki/Half-precision_floating-point_format), with 5 bits for the exponent and 10 bits for the significand.\n", + "- [Bfloat16](https://en.wikipedia.org/wiki/Bfloat16_floating-point_format) (*brain* float), with 8 bits for the exponent and 7 bits for the significand. The dynamic range of `bfloat16` matches that of a single-precision float since both have 8 bits for the exponent. However, this comes at the cost of significantly reduced precision.\n", + "\n", + "An interesting alternative to 16-bit data types is the [TF32](https://en.wikipedia.org/wiki/TensorFloat-32) format, which is an internal computation format available on NVIDIA Tensor Cores.\n", + "\n", + "## Narrow-precision (FP8 and lower) formats\n", + "\n", + "Even smaller floating point formats have recently gained popularity. While narrow-precision formats are not practical in many areas, they still perform well in Deep Learning applications, especially in the inference. In nvmath-python, we support 8-bit floating point types. However, even smaller types are used, such as 6-bit FP6 type or even 4-bit FP4.\n", + "\n", + "The two most commonly used FP8 formats are `float8_e4m3fn` and `float8_e5m2`.\n", + "\n", + "The `float8_e4m3fn` format has 4 bits for the exponent (`e4`) and 3 bits for the significand (`m3`). The `fn` suffix indicates that the number is finite, meaning there's no special value indicating infinity. With just 4 bits for the exponent, the dynamic range of `float8_e4m3fn` is very small - its maximum value is just 448.\n", + "\n", + "The second format, `float8_e5m2`, has one more bit reserved for the exponent, allowing it to store values up to 57344. However, this comes at the cost of reduced precision due to having one fewer bit for the significand.\n", + "\n", + "To illustrate the precision of these types, let's plot their representable values between 0 and 20 and between 0 and 2000. Note how `float8_e5m2` has half the density of `float8_e4m3fn` due to having one less bit for the significand, but has much wider dynamic range. Also notice that 9 is not representable in `float8_e5m2`, and 17 is not representable in `float8_e4m3fn`." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "6395b056-59d9-4be8-8956-9db2e73d4809", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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gEREREQywwKQiIiISGZYABIRERHJDAtAIiIiIplhAUhEREQkMywAiYiIiGSGBSARERGRzLAAJCIiIpIZFoBEREREMsMCkIiIiEhmWAASERERyQwLQCIiIiKZYQFIREREJDMsAImIiIhkhgUgERERkcywACQiIiKSGRaARERERDLDApCIiIhIZlgAEhEREckMC0AiIiIimWEBSERERCQzLACJiIiIZIYFIBEREZHMsAAkIiIikhkWgEREREQywwKQiIiISGZYABIRERHJDAtAIiIiIplhAUhEREQkMywAiYiIiGSGBSARERGRzLAAJCIiIpIZFoBEREREMsMCkIiIiEhmWAASERERyQwLQCIiIiKZYQFIREREJDMsAImIiIhkhgUgERERkcwYaTuAym4VlWDIkj3IKVZvN1IAliYGMDI0QBsLEzgpzeBgZQqFAhBC4GZRCe6XqeBuaw5fNxvYW5ki724JbC1McOvOfeTdK4UQQBsLE9hZmlRZpoACQR3s0ae9HY5dvI3cwmI4WJoCCuBm0X04WZshwLNNvZY1tG+glx0MDRQAgDKVwJGsW612WW24nmbX09acjcH59Hs+bczJ+fRba88P0P0cFUIIUVengoIC2NjYID8/H0qlstmC6fPOHtwoKmm28eujvKCsfpmBAlDVc1lD+rramGH+MB8AwILvziA7v7hVLgvt5lr9BgGw63Q219PgetqaszE4n37Pp405OZ9+a+35AdrLsSH1ms4UgLpQ/GmLAkBNL0JrWQYAq0f3qvaNv+t0Nl75/HiV9ble49bT1pyNwfn0ez5tzMn5NP8atqTWnh+g3RwbUq/pxDmAt4pKZFv8ATUXTq1t2YLvzqCs0m7RMpXAgu/OVLs+12v4etqaszE4n37Pp405OZ/mX8OW1NrzA/QrR50oAEeuO6TtEKiZCQDZ+cU4knVLrf1I1i21XeRcr2nraWvOxuB8+j2fNubkfJp/DVtSa88P0K8cdaIAzC2U794/ucktLK71Oddr2nramrMxOJ9+z6eNOTmfZudraa09P0C/ctSJAtDJ2kTbIVALcbI2q/U512vaetqaszE4n37Pp405OZ9m52tprT0/QL9y1IkCcMvEvtoOgZqZAuVXQAV62am1B3rZwdXGTDo5lus1bT1tzdkYnE+/59PGnJxP869hS2rt+QH6laNOFIB2ViZwtJLv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fzpfBNpaGEv346tQuU6pT5/GrFNdHxcbM6we3Qtzhvhg9ehecLExq3Z5aDdXhHZz1Zk+9YmXiIiImo9O3AbmYfwlEP4SCBERETWc3t0HkIiIiIiapiH1ms4cAiYiIiKilsECkIiIiEhmWAASERERyQwLQCIiIiKZYQFIREREJDMsAImIiIhkhgUgERERkcywACQiIiKSGRaARERERDLDApCIiIhIZlgAEhEREckMC0AiIiIimWEBSERERCQzLACJiIiIZIYFIBEREZHMsAAkIiIikhkWgEREREQywwKQiIiISGZYABIRERHJDAtAIiIiIplhAUhEREQkM0b16SSEAAAUFBQ0azBERERE1DgVdVpF3VabehWAhYWFAAAPD48mhEVEREREza2wsBA2Nja19lGIepSJKpUK165dg7W1NRQKhcYCrElBQQE8PDxw+fJlKJXKZp+vJTE3/cTc9BNz00+tOTegdefH3LRLCIHCwkK4ubnBwKD2s/zqtQfQwMAA7u7uGgmuIZRKpc5u5KZibvqJuekn5qafWnNuQOvOj7lpT117/irwIhAiIiIimWEBSERERCQzOlkAmpqaYv78+TA1NdV2KBrH3PQTc9NPzE0/tebcgNadH3PTH/W6CISIiIiIWg+d3ANIRERERM2HBSARERGRzLAAJCIiIpIZFoBEREREMsMCkIiIiEhmdLIAXLVqFdq3bw8zMzM8+uijOHLkiLZDqtV7772HPn36wNraGk5OTnjqqadw7tw5tT7FxcWYMmUK7O3tYWVlhWeeeQbXr19X63Pp0iWEhYXBwsICTk5OmDlzJh48eNCSqdTp/fffh0KhQExMjNSmz7ldvXoVo0ePhr29PczNzdG9e3ccPXpUWi6EwL///W+4urrC3NwcgwYNQkZGhtoYt27dwqhRo6BUKmFra4vx48ejqKiopVNRU1ZWhtjYWHh5ecHc3BwdOnTA22+/rfYD4fqS24EDBzBs2DC4ublBoVBg27Ztass1lcfJkyfRr18/mJmZwcPDA//5z3+aO7VacystLcWsWbPQvXt3WFpaws3NDVFRUbh27Zre51bZpEmToFAosHz5crV2fc7t7NmzGD58OGxsbGBpaYk+ffrg0qVL0nJd/tysK7+ioiJER0fD3d0d5ubm8PHxwZo1a9T66GJ+LfldvX//fvTq1Qumpqbo2LEj4uLi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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import torch\n", + "from matplotlib import pyplot as plt\n", + "\n", + "\n", + "fig, axs = plt.subplots(2, 1, figsize=(8,5))\n", + "for ax, limit in zip(axs, (20, 2000), strict=True):\n", + " x = torch.linspace(0, limit, 10**4)\n", + " for i, dtype in enumerate((torch.float8_e4m3fn, torch.float8_e5m2)):\n", + " representable_values = x.type(dtype).type(torch.float32).unique() # Cast to float8 dtype and back\n", + " ax.scatter(representable_values, torch.full_like(representable_values, i), label=repr(dtype))\n", + " ax.set_ylim(-0.5, 2.5)\n", + " ax.set_yticks([])\n", + " ax.set_xticks(torch.linspace(0, limit, 11, dtype=torch.int32))\n", + " ax.legend()\n", + "fig.suptitle(\"Representable values of float8 types\");" + ] + }, + { + "cell_type": "markdown", + "id": "e5f255ea-c58e-43a0-8bbc-04653372fe5e", + "metadata": {}, + "source": [ + "### FP8 scaling\n", + "\n", + "Due to the very limited dynamic range of FP8 types, the results of FP8 GEMM rarely fit into the dynamic range of FP8. To address this issue, we use *scaling factors* for each operand to ensure they fit into the dynamic range of FP8 type. The internal computations of GEMM are performed in higher precision.\n", + "\n", + "We first scale the input FP8 operands to expand them from the FP8 type into the desired range (*dequantize*). Then, we scale the result down to make it fit into the dynamic range (*quantize*) again. With scales, matrix multiplication becomes:\n", + "$$\n", + "D = \\text{scale}_D\\cdot\\left( (\\text{scale}_A \\cdot A)\\;@\\;(\\text{scale}_B \\cdot B) \\right) \n", + "$$\n", + "\n", + "We will discuss methods for properly choosing the scales later." + ] + }, + { + "cell_type": "markdown", + "id": "ecdd8446-1dff-48eb-905f-d221caedb140", + "metadata": {}, + "source": [ + "## FP8 operations with nvmath-python" + ] + }, + { + "cell_type": "markdown", + "id": "a756e092-904b-48f2-a862-ac6f119b6a6b", + "metadata": {}, + "source": [ + "\n", + "
\n", + " FP8 matrix multiplication in nvmath-python requires CUDA Toolkit 12.8 or newer and a device with compute capability 8.9 or higher. Devices with compute capability 8.9 or higher include devices based on Ada, Hopper or Blackwell architectures, such as L40, H100, H200, B100, B200. Please refer to the table at https://developer.nvidia.com/cuda-gpus to check the compute capability of your device. Note that you also need PyTorch version built with CUDA 12.8.\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "bddce74d-eac5-4c3c-9fc8-070336a0a3d4", + "metadata": {}, + "source": [ + "Let's begin by computing a simple FP8 GEMM with nvmath-python. The only API difference between this simple FP8 operation and a classic GEMM with nvmath-python is the presence of the `quantization_scales` argument. It can be either a `MatmulQuantizationScales` object or a dictionary. Scales are allowed (and required) only for FP8 operands.\n", + "\n", + "There are certain limitations regarding the shapes and layouts of the operands. All dimensions must be divisible by 16, and `b` needs to be in column-major layout while `a` needs to be in row-major layout. Since PyTorch creates row-major tensors by default, we will transpose `b` after creation.\n", + "\n", + "Let's create `a`, `b`, and `c` with values sampled from $\\mathcal{N}(0, 10)$." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "127c1137-7c7c-44f8-84b5-b3592588d09b", + "metadata": {}, + "outputs": [], + "source": [ + "import nvmath\n", + "\n", + "m, n, k = 256, 240, 128\n", + "a = torch.normal(mean=torch.zeros(m, k), std=5).cuda().type(torch.float8_e4m3fn)\n", + "b = torch.normal(mean=torch.zeros(n, k), std=5).cuda().type(torch.float8_e4m3fn).T" + ] + }, + { + "cell_type": "markdown", + "id": "2208fd8a-7e67-4141-be6e-3d9989d9fc42", + "metadata": {}, + "source": [ + "To demonstrate why scaling is so important for FP8 operations, let's first compute the result with all scales set to 1. Note that we're using stateless `matmul` function for simplicty, but stateful `Matmul` class should be used for repeated computations." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "b76dddc7-6bdb-4f61-8e1c-a0be9f7357e1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([[-320., -448., 2., ..., 104., 8., -36.],\n", + " [-288., -120., -144., ..., 448., 448., 288.],\n", + " [-448., 176., -60., ..., -176., 96., -448.],\n", + " ...,\n", + " [ 192., 384., 104., ..., 320., 20., -144.],\n", + " [-192., -448., 288., ..., 176., 112., 384.],\n", + " [-160., -320., 192., ..., 448., -120., 96.]], device='cuda:0',\n", + " dtype=torch.float8_e4m3fn)" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result_unscaled = nvmath.linalg.advanced.matmul(a, b, quantization_scales={\"a\": 1, \"b\": 1, \"d\": 1})\n", + "result_unscaled" + ] + }, + { + "cell_type": "markdown", + "id": "2499c680-4bde-4315-b33c-21651127ccfd", + "metadata": {}, + "source": [ + "Note that many values reached the maximum/minimum value of `float8_e4m3fn` ($\\pm 448$). To avoid this, let's lower the scale for the result (D):" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "e13fa8ac-f5a2-4a40-8dbf-581ef36bbd01", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([[-160., -288., 1., ..., 52., 4., -18.],\n", + " [-144., -60., -72., ..., 224., 224., 144.],\n", + " [-288., 88., -30., ..., -88., 48., -240.],\n", + " ...,\n", + " [ 96., 192., 52., ..., 160., 10., -72.],\n", + " [ -96., -256., 144., ..., 88., 56., 192.],\n", + " [ -80., -160., 96., ..., 240., -60., 48.]], device='cuda:0',\n", + " dtype=torch.float8_e4m3fn)" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "result_scaled = nvmath.linalg.advanced.matmul(a, b, quantization_scales={\"a\": 1, \"b\": 1, \"d\": 0.5})\n", + "result_scaled" + ] + }, + { + "cell_type": "markdown", + "id": "bc45d256-6e46-4b75-97f8-40f45ad929c6", + "metadata": {}, + "source": [ + "Now all values have been scaled down by a factor of $0.5$. As a result, most of them fit into the dynamic range of `float8_e4m3fn`. Let's illustrate this with a histogram:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "ff104d7e-e38e-4028-b5b3-04fc28c6ff41", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiAAAAGdCAYAAAArNcgqAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAAMEJJREFUeJzt3Xl0VGW+7vEnA6kkQpEghARImikKyJTIICoOhxwD4rXlclQQh9jI1OEASttAa4stS5OFrrPOAm1au2+DV2ljg91OIDleFBU7gCBTCNAgYSbBFiEgkJDU7/6hlJQQTLDyZvp+1qoltfdbe7/1s9j1sPf77goxMxMAAIBDobXdAQAA0PgQQAAAgHMEEAAA4BwBBAAAOEcAAQAAzhFAAACAcwQQAADgHAEEAAA4F+5qRz6fTwcPHlSzZs0UEhLiarcAAOAnMDMdP35cbdq0UWho8M5bOAsgBw8eVGJioqvdAQCAINq3b5/atWsXtO05CyDNmjWT9O0b8Hq9rnZbJ/hOntSOgTdIkpI/+Vih0dG13CMAQFVw/JZKSkqUmJjo/x4PFmcB5OxlF6/X2/gCSHi4moaFSfr2/TfGDzAA1Eccv78X7OETDEIFAADOEUAAAIBzBBAAAOCcszEgAID6raKiQmfOnKntbjjlKy2VLyFBknS6tDSo01DrirCwMIWHhzu/RQYBBADwo06cOKH9+/fLzGq7K06Zz6eKxx+TJO0+eFAhDTCASFJ0dLQSEhIUERHhbJ8EEADARVVUVGj//v2Kjo5Wq1at6sXNJLcXlQRlOyFmSmpVIUnytG+vkO9mxDQUZqaysjJ9+eWXKiwsVHJysrOzPAQQAMBFnTlzRmamVq1aKSoqqra7UyUh4aeDsp1QM3m++0KOjIxscAFEkqKiotSkSRPt2bNHZWVlioyMdLLfhnkuCQAQdPXhzAcuTW2MbSGAAAAA57gE41jXJ5apNNwTlG3tzh4alO0AAOAaAQQAcEnaT1/idH8N5R9dK1as0M0336yvv/5aMTExtd2dWsMlGABAg/Tbh3+pXomx6pUYq6s7tNLNKVdo3D3D9PecV+Xz+Wq7ezVq0qRJuvrqq+XxeNS7d+/a7s4FEUAAAA3WdTcN0vJ127T0Hxv1+/+7SH0HXK/ZT87Qf2bcrfLy8truXo36xS9+obvvvru2u1EpAggAoMGKiPCoZVxrtU5oo649eumh/5yq//4/C7Xyw/+ntxf9pdLXfZa3UvfcNkj9rmynhGuv1b/dd5/27NnjX//OO++ob9++ioyMVMuWLTVs2DD/uldeeUV9+vRRs2bNFB8fr3vuuUeHDx++aD9XrlypgQMHKioqSomJiZo0aZK++eabS37fc+bMUWZmpjp27HjJ26hpBBAAQKPS/7obdGW37lr+3jsXXF9eXq6HHxqlPtdcqzdyP9GHr76qX/zHf/inIS9ZskTDhg3TrbfeqvXr12v58uXq16+f//VnzpzRrFmztHHjRr355pvavXu3MjIyKu3PF198ocGDB2v48OHatGmTXn/9da1cuVITJ070txk/fryaNm160Ud9wyBUAECj075TsnZsK7jgum+OH9fxkhLdMGiwEtt3UKfYCHXp2FGRSUmSpKefflojRozQ7373O/9revXq5f/zL37xC/+fO3bsqDlz5qhv3746ceLEBYNCVlaWRo0apSlTpkiSkpOTNWfOHN14442aN2+eIiMj9dRTT+lXv/pVMN56nUEAARqhmpi90FBmKKBxMJNUyY3VmsfG6vY779GE+4ZrwPU36bY+vfW/09PVoWtXSdKGDRs0ZsyYSre9bt06Pfnkk9q4caO+/vpr/4DXvXv3qlu3bue137hxozZt2qSFCxee0z+Tz+dTYWGhunbtqri4OMXFxf2Ed1z3cAkGANDoFO7crraJSZWun/VfL+j/vvk/6n11Py1etky9brtNq1atkqSL3o7+m2++UXp6urxerxYuXKjPPvtMf//73yVJZWVlF3zNiRMnNG7cOG3YsMH/2Lhxo3bs2KFOnTpJ4hIMAAD13upPP9aObQW696FfXrRd1+49ddVVPZR13126adQo/SUnRwOuu049e/bU8uXL9eCDD573mm3btumrr75Sdna2EhMTJUlr16696H5SU1NVUFCgzp07V9qGSzAAANQjZWWl+tfhYlVUVOjIv77Upyv+n/7PC/+tG9LS9b/+Y8QFX7N/7x698ZcFuunfh6h1XLwKN6/SF3v36oEuXSRJM2fO1KBBg9SpUyeNGDFC5eXlWrp0qaZNm6akpCRFRERo7ty5Gj9+vPLz8zVr1qyL9nHatGm65pprNHHiRD300EO67LLLVFBQoPfff1/PP/+8JFX7EszOnTt14sQJFRUV6dSpU9qwYYMkqVu3boqIiKjydmoSAQQAcEnqw7ifT1cs16Cruyg8PFzNmsfoym7dNe132br9zpGV/gBbVFSUCnfu0DuLcnT06BEltGypsSNGaNzYsZKkm266SYsWLdKsWbOUnZ0tr9erG264QZLUqlUrLViwQL/5zW80Z84cpaam6rnnntPtt99eaR979uypjz76SI899pgGDhwoM1OnTp1+0j08HnroIX300Uf+5ykpKZKkwsJCtW/f/pK3G0whZmYudlRSUqLmzZvr2LFj8nq9LnZZZ/hOntT21KslSXfc9jS/BYNa5/oW2peCz3fdcfr0aRUWFqpDhw7Ofqr9p9q0/2hQthNqpk7HDkiSIrt2VUhYWFC2W9dc7P9xTX1/MwgVAAA4RwABAADOMQYEQJ3EvUqAho0zIAAAwDkCCACgShzNWUAtqI3/t1yCAdBoBPuyTmO5pBP23cyPsrKyi94FFPXXyZMnJUlNmjRxtk8CCADgosLDwxUdHa0vv/xSTZo0qfT+GXWJlV/4tufV5TNT6Xe/5aLTpxvcNFwz08mTJ3X48GHFxMT4w6YLBBAgyOrDPTaA6ggJCVFCQoIKCwu1Z8+e2u5OlRz++lRQthMik04dlSSFh4UppB6Er0sRExOj+Ph4p/skgAAAflRERISSk5Mr/UG1uuahv60IynYiysv0/Ir/liS1/9sbCm2Al6CaNGni9MzHWQQQAECVhIaG1ps7oR44XhGU7XjKyxV66JAkKdLjUWg9ef/1QcM8lwQAAOo0AggAAHCOAAIAAJwjgAAAAOcIIAAAwDkCCAAAcI4AAgAAnCOAAAAA5wggAADAOQIIAABwjgACAACcI4AAAADnCCAAAMA5AggAAHCOAAIAAJwjgAAAAOcIIAAAwLnw2u4AUNvaT19S210AgEaHAAIAl6gmwuvu7KFB3yZQF3EJBgAAOEcAAQAAzhFAAACAcwQQAADgHAEEAAA4RwABAADOEUAAAIBzBBAAAOAcAQQAADhHAAEAAM5xK3bUK/xuCwA0DJwBAQAAzhFAAACAcwQQAADgHGNAAKAOCfY4p93ZQ4O6PSBYOAMCAACc4wwIahSzVoDaVRN/BzmrgmDgDAgAAHCOAAIAAJwjgAAAAOcIIAAAwDkCCAAAcI4AAgAAnCOAAAAA5wggAADAOQIIAABwjgACAACcI4AAAADnCCAAAMA5AggAAHCOAAIAAJwjgAAAAOcIIAAAwDkCCAAAcI4AAgAAnCOAAAAA58JruwO4dO2nLwnq9nZnDw3q9gCgKoJ9LEP9wBkQAADgHAEEAAA4RwABAADOEUAAAIBzBBAAAOAcAQQAADhHAAEAAM4RQAAAgHMEEAAA4BwBBAAAOEcAAQAAzhFAAACAcwQQAADgHAEEAAA4RwABAADOEUAAAIBzBBAAAOAcAQQAADhHAAEAAM4RQAAAgHMEEAAA4BwBBAAAOEcAAQAAzhFAAACAcwQQAADgHAEEAAA4RwABAADOEUAAAIBzBBAAAOAcAQQAADgXXtsdQN3RfvqS2u4CgHqAYwWCgTMgAADAOQIIAABwjgACAACcI4AAAADnCCAAAMA5AggAAHCOAAIAAJwjgAAAAOcIIAAAwDkCCAAAcI4AAgAAnCOAAAAA5wggAADAOQIIAABwjgACAACcI4AAAADnCCAAAMA5AggAAHCOAAIAAJwjgAAAAOcIIAAAwDkCCAAAcI4AAgAAnCOAAAAA5wggAADAOQIIAABwjgACAACcI4AAAADnCCAAAMA5AggAAHCOAAIAAJwjgAAAAOcIIAAAwDkCCAAAcI4AAgAAnCOAAAAA5wggAADAOQIIAABwjgACAACcI4AAAADnCCAAAMA5AggAAHCOAAIAAJwjgAAAAOcIIAAAwLlwVzsyM0lSSUmJq13WGb6TJ3WiokKSVFF6Ur7v/gwAqNsqykv9x++SkhKFlpfXco/cO/u9ffZ7PFhCLNhbrMT+/fuVmJjoYlcAACDI9u3bp3bt2gVte84CiM/n08GDB2VmSkpK0r59++T1el3sukEqKSlRYmIidfyJqGNwUMfgoI7BQR2D42wd9+7dq5CQELVp00ahocEbueHsEkxoaKjatWvnP5Xj9Xr5YAQBdQwO6hgc1DE4qGNwUMfgaN68eY3UkUGoAADAOQIIAABwznkA8Xg8mjlzpjwej+tdNyjUMTioY3BQx+CgjsFBHYOjpuvobBAqAADAWVyCAQAAzhFAAACAcwQQAADgHAEEAAA4V6MBZMmSJerfv7+ioqIUGxurO+64I2D93r17NXToUEVHRysuLk6PPvqoyn9wn/0VK1YoNTVVHo9HnTt31oIFC2qyy3VaaWmpevfurZCQEG3YsCFg3aZNmzRw4EBFRkYqMTFRs2fPPu/1ixYtUpcuXRQZGakePXpo6dKljnpeu3bv3q3Ro0erQ4cOioqKUqdOnTRz5kyVlZUFtKOGl+aFF15Q+/btFRkZqf79+2vNmjW13aU6JSsrS3379lWzZs0UFxenO+64Q9u3bw9oc/r0aWVmZuryyy9X06ZNNXz4cBUXFwe0qcrxsrHIzs5WSEiIpkyZ4l9GDavuwIEDuvfee3X55ZcrKipKPXr00Nq1a/3rzUxPPPGEEhISFBUVpbS0NO3YsSNgG0eOHNGoUaPk9XoVExOj0aNH68SJE9XriNWQxYsXW2xsrM2bN8+2b99uW7Zssddff92/vry83Lp3725paWm2fv16W7p0qbVs2dJmzJjhb7Nr1y6Ljo62Rx55xAoKCmzu3LkWFhZmy5Ytq6lu12mTJk2yIUOGmCRbv369f/mxY8esdevWNmrUKMvPz7fXXnvNoqKi7MUXX/S3+fTTTy0sLMxmz55tBQUF9vjjj1uTJk1s8+bNtfBO3HrvvfcsIyPDcnNz7YsvvrC33nrL4uLibOrUqf421PDS5OTkWEREhP35z3+2LVu22JgxYywmJsaKi4tru2t1Rnp6us2fP9/y8/Ntw4YNduutt1pSUpKdOHHC32b8+PGWmJhoy5cvt7Vr19o111xj1157rX99VY6XjcWaNWusffv21rNnT5s8ebJ/OTWsmiNHjtjPfvYzy8jIsNWrV9uuXbssNzfXdu7c6W+TnZ1tzZs3tzfffNM2btxot99+u3Xo0MFOnTrlbzN48GDr1auXrVq1yj755BPr3LmzjRw5slp9qZEAcubMGWvbtq396U9/qrTN0qVLLTQ01IqKivzL5s2bZ16v10pLS83M7Ne//rVdddVVAa+7++67LT09vSa6XactXbrUunTpYlu2bDkvgPz+97+32NhYf93MzKZNm2ZXXnml//ldd91lQ4cODdhm//79bdy4cTXe97po9uzZ1qFDB/9zanhp+vXrZ5mZmf7nFRUV1qZNG8vKyqrFXtVthw8fNkn20UcfmZnZ0aNHrUmTJrZo0SJ/m61bt5oky8vLM7OqHS8bg+PHj1tycrK9//77duONN/oDCDWsumnTptn1119f6Xqfz2fx8fH27LPP+pcdPXrUPB6Pvfbaa2ZmVlBQYJLss88+87d57733LCQkxA4cOFDlvtTIJZjPP/9cBw4cUGhoqFJSUpSQkKAhQ4YoPz/f3yYvL089evRQ69at/cvS09NVUlKiLVu2+NukpaUFbDs9PV15eXk10e06q7i4WGPGjNErr7yi6Ojo89bn5eXphhtuUEREhH9Zenq6tm/frq+//trfhlp+79ixY2rRooX/OTWsvrKyMq1bty6gJqGhoUpLS2u0NamKY8eOSZL/87du3TqdOXMmoI5dunRRUlKSv45VOV42BpmZmRo6dOh5fw+pYdW9/fbb6tOnj+68807FxcUpJSVFf/zjH/3rCwsLVVRUFFDL5s2bq3///gG1jImJUZ8+ffxt0tLSFBoaqtWrV1e5LzUSQHbt2iVJevLJJ/X444/r3XffVWxsrG666SYdOXJEklRUVBTwQZDkf15UVHTRNiUlJTp16lRNdL3OMTNlZGRo/PjxAf+zz/VTanl2fWOyc+dOzZ07V+PGjfMvo4bV969//UsVFRXUpBp8Pp+mTJmi6667Tt27d5f07ecqIiJCMTExAW3PrWNVPp8NXU5Ojj7//HNlZWWdt44aVt2uXbs0b948JScnKzc3VxMmTNCkSZP08ssvS/q+Fhf7e11UVKS4uLiA9eHh4WrRokW1almtADJ9+nSFhIRc9LFt2zb5fD5J0mOPPabhw4fr6quv1vz58xUSEqJFixZVZ5cNVlVrOXfuXB0/flwzZsyo7S7XOVWt4bkOHDigwYMH684779SYMWNqqedorDIzM5Wfn6+cnJza7kq9sm/fPk2ePFkLFy5UZGRkbXenXvP5fEpNTdUzzzyjlJQUjR07VmPGjNEf/vAH530Jr07jqVOnKiMj46JtOnbsqEOHDkmSunXr5l/u8XjUsWNH7d27V5IUHx9/3mj5syOW4+Pj/f/94Sjm4uJieb1eRUVFVafrdU5Va/nBBx8oLy/vvHvx9+nTR6NGjdLLL79caZ2kH6/l2fX1UVVreNbBgwd1880369prr9VLL70U0K6x1vCnaNmypcLCwqhJFU2cOFHvvvuuPv74Y7Vr186/PD4+XmVlZTp69GjAv+DPrWNVjpcN2bp163T48GGlpqb6l1VUVOjjjz/W888/r9zcXGpYRQkJCQHfzZLUtWtXvfHGG5K+r0VxcbESEhL8bYqLi9W7d29/m8OHDwdso7y8XEeOHKleLas/hOXHHTt2zDweT8Ag1LKyMouLi/PPKjg7IOjc0fIvvviieb1eO336tJl9Owi1e/fuAdseOXJkoxqEumfPHtu8ebP/kZuba5Js8eLFtm/fPjP7fgBlWVmZ/3UzZsw4bwDlbbfdFrDtAQMGNJoBlPv377fk5GQbMWKElZeXn7eeGl6afv362cSJE/3PKyoqrG3btgxCPYfP57PMzExr06aN/fOf/zxv/dkBlIsXL/Yv27Zt2wUHUF7seNmQlZSUBBwHN2/ebH369LF7773XNm/eTA2rYeTIkecNQp0yZYoNGDDAzL4fhPrcc8/515/9Tv/hINS1a9f62+Tm5lZ7EGqNTcOdPHmytW3b1nJzc23btm02evRoi4uLsyNHjpjZ91OibrnlFtuwYYMtW7bMWrVqdcFpuI8++qht3brVXnjhhUY9DdfMrLCw8LxZMEePHrXWrVvbfffdZ/n5+ZaTk2PR0dHnTSENDw+35557zrZu3WozZ85sNFNI9+/fb507d7ZBgwbZ/v377dChQ/7HWdTw0uTk5JjH47EFCxZYQUGBjR071mJiYgJmGjR2EyZMsObNm9uKFSsCPnsnT570txk/frwlJSXZBx98YGvXrrUBAwb4vxDMqna8bGzOnQVjRg2ras2aNRYeHm5PP/207dixwxYuXGjR0dH26quv+ttkZ2dbTEyMvfXWW7Zp0yb7+c9/fsFpuCkpKbZ69WpbuXKlJScn141puGbfnvGYOnWqxcXFWbNmzSwtLc3y8/MD2uzevduGDBliUVFR1rJlS5s6daqdOXMmoM2HH35ovXv3toiICOvYsaPNnz+/prpcL1wogJiZbdy40a6//nrzeDzWtm1by87OPu+1f/3rX+2KK66wiIgIu+qqq2zJkiWOel275s+fb5Iu+DgXNbw0c+fOtaSkJIuIiLB+/frZqlWrartLdUpln71zj2WnTp2yX/7ylxYbG2vR0dE2bNiwgIBsVrXjZWPywwBCDavunXfese7du5vH47EuXbrYSy+9FLDe5/PZb3/7W2vdurV5PB4bNGiQbd++PaDNV199ZSNHjrSmTZua1+u1Bx980I4fP16tfoSYmVX9gg0AAMBPx2/BAAAA5wggAADAOQIIAABwjgACAACcI4AAAADnCCAAAMA5AggAAHCOAAIAAJwjgAAAAOcIIAAAwLlwVzvy+Xw6ePCgmjVrppCQEFe7BQAAP4GZ6fjx42rTpo1CQ4N33sJZADl48KASExNd7Q4AAATRvn371K5du6Btz1kAadasmaRv34DX63W12zrBd/Kkdgy8QZKU/MnHCo2OruUeAQCqguO3VFJSosTERP/3eLA4CyBnL7t4vd7GF0DCw9U0LEzSt++/MX6AAaA+4vj9vWAPn2AQKgAAcI4AAgAAnCOAAAAA55yNAQEA1B0+n09lZWW13Y06z1daKl9CgiTpdGlpUKeh1hVNmjRR2HfjXFwigABAI1NWVqbCwkL5fL7a7kqdZz6fKh5/TJK0++BBhTTAACJJMTExio+Pd3qfLgII0Ai1n76ktrvwo3ZnD63tLjRIZqZDhw4pLCxMiYmJDfJf9MFkFRUqraiQJHnat1dILZwpqElmppMnT+rw4cOSpITvzva4QAABgEakvLxcJ0+eVJs2bRTdiKeUVpVVVEjfhbTIyMgGF0AkKSoqSpJ0+PBhxcXFObscQ/QFgEak4rt/zUdERNRyT1CXnA2jZ86ccbZPAggANEL8JhfOVRufBwIIAABwjgACAMAlWrFihUJCQnT06NHa7kq9wyBUAIDzmVHVneWUkZGhl19+WZIUHh6uFi1aqGfPnho5cqQyMjIa9GyevXv3asKECfrwww/VtGlTPfDAA8rKylJ4eOVf4e3bt9eePXsClmVlZWn69Ok13d0qI4AAAOqFwYMHa/78+aqoqFBxcbGWLVumyZMna/HixXr77bcv+oVcX1VUVGjo0KGKj4/XP/7xDx06dEj333+/mjRpomeeeeair33qqac0ZswY//Ng/5rtT9VwIyMAoEHxeDyKj49X27ZtlZqaqt/85jd666239N5772nBggWVvm7FihXq16+fLrvsMsXExOi6664LODvwzjvvqG/fvoqMjFTLli01bNgw/7pXXn1V1919t+L691dC27a65557/PfMqMzKlSs1cOBARUVFKTExUZMmTdI333xzSe/5f/7nf1RQUKBXX31VvXv31pAhQzRr1iy98MILP3on22bNmik+Pt7/uOyyyy6pDzWFAAIAqLf+7d/+Tb169dLf/va3C64vLy/XHXfcoRtvvFGbNm1SXl6exo4d65/1sWTJEg0bNky33nqr1q9fr+XLl6tfv37+1585c0ZPTJyo1YsX6+9vvKHdu3crIyOj0v588cUXGjx4sIYPH65Nmzbp9ddf18qVKzVx4kR/m/Hjx6tp06YXfZyVl5enHj16qHXr1v5l6enpKikp0ZYtWy5am+zsbF1++eVKSUnRs88+q/Ly8ou2d63hna8CADQqXbp00aZNmy64rqSkRMeOHdNtt92mTp06SZK6du3qX//0009rxIgR+t3vfudf1qtXL/+ff/Hggzq9daskKbJrV82ZM0d9+/bViRMnAoLCWVlZWRo1apSmTJkiSUpOTtacOXN04403at68eYqMjNRTTz2lX/3qV1V6b0VFRQHhQ5L/eVFRUaWvmzRpklJTU9WiRQv94x//0IwZM3To0CH913/9V5X26wIBBABQr5lZpfexaNGihTIyMpSenq5///d/V1pamu666y7/Lcc3bNgQME7ih9atW6cnfv1rbd6+XUdPnPD/fs7evXvVrVu389pv3LhRmzZt0sKFCwP65/P5VFhYqK5duyouLk5xcXE/5S3/qEceecT/5549eyoiIkLjxo1TVlaWPB5Pje67qrgEAwCo17Zu3aoOHTpUun7+/PnKy8vTtddeq9dff11XXHGFVq1aJen725BfyDfffKPBt94q72WXaX52ttasWqW///3vklTp+IsTJ05o3Lhx2rBhg/+xceNG7dixw38GpjqXYOLj41VcXBywj7PP4+Pjq1Cdb/Xv31/l5eXavXt3lV9T0zgDAgCotz744ANt3rxZDz/88EXbpaSkKCUlRTNmzNCAAQP0l7/8Rddcc4169uyp5cuX68EHHzzvNdu2bdNXX32lWQ8/rHbx8Yrs0kXr1q+/6H5SU1NVUFCgzp07V9qmOpdgBgwYoKefftr/Oy2S9P7778vr9V7wDExlNmzYoNDQ0Bo/81IdBBAAQL1QWlqqoqKigGm4WVlZuu2223T//fdf8DWFhYV66aWXdPvtt6tNmzbavn27duzY4W8/c+ZMDRo0SJ06ddKIESNUXl6upUuXatq0aUpKSlJERITm/eUveujOO7Vj507NmjXron2cNm2arrnmGk2cOFEPPfSQLrvsMhUUFOj999/X888/L0nVugRzyy23qFu3brrvvvs0e/ZsFRUV6fHHH1dmZqb/UsqaNWt0//33a/ny5Wrbtq3y8vK0evVq3XzzzWrWrJny8vL08MMP695771VsbGxVy13jCCAAgHph2bJlSkhIUHh4uGJjY9WrVy/NmTNHDzzwQKU3IouOjta2bdv08ssv66uvvlJCQoIyMzM1btw4SdJNN92kRYsWadasWcrOzpbX69UNN9wgSWrVqpXm//nP+s20afr9woVKTU3Vc889p9tvv73SPvbs2VMfffSRHnvsMQ0cOFBmpk6dOunuu+++pPccFhamd999VxMmTNCAAQN02WWX6YEHHtBTTz3lb3Py5Elt377d/0NyHo9HOTk5evLJJ1VaWqoOHTro4YcfDhgXUheEmJm52FFJSYmaN2+uY8eOyev1uthlneE7eVLbU6+WJF35+TqF8hPYqGWu73p5Kap7p0xUzenTp1VYWKgOHTooMjKytrtT51lFRcAsmBBHP1Xv2sU+FzX1/c0gVAAA4ByXYADUSTVxloazKkDdQQAB6oH6cMkEAKqDSzAAAMA5AggAAHCOAAIAjZCjCZCoJ87eYt4lxoAAQCPSpEkThYSE6Msvv1SrVq0q/Q0VfMsqKlR69sv59OkGNw3XzFRWVqYvv/xSoaGhioiIcLZvAggANCJhYWFq166d9u/fX6d+F6SuMp9P5V9+KUkKDwtTSCU3PKvvoqOjlZSUVOkN3WoCAQQAGpmmTZsqOTnZf+dMVM536pQKJ/6nJKn9395Q6EV+vK6+CgsLU3h4uPOzYQQQAGiEwsLCFNbALifUBJ/Pp9BDhyRJkR6PQrl7bNA0zHNJAACgTiOAAAAA5wggAADAOQIIAABwjgACAACcI4AAAADnCCAAAMA5AggAAHCOAAIAAJwjgAAAAOcIIAAAwDkCCAAAcI4AAgAAnCOAAAAA5wggAADAOQIIAABwjgACAACcI4AAAADnCCAAAMA5AggAAHAuvLY7ADQ07acvqe0uAECdxxkQAADgHAEEAAA4xyUYAI1GsC+P7c4eGtTtAY0JZ0AAAIBzBBAAAOAcAQQAADhHAAEAAM4RQAAAgHMEEAAA4BwBBAAAOEcAAQAAzhFAAACAcwQQAADgHAEEAAA4RwABAADOEUAAAIBzBBAAAOAcAQQAADhHAAEAAM4RQAAAgHMEEAAA4BwBBAAAOEcAAQAAzhFAAACAcwQQAADgHAEEAAA4RwABAADOEUAAAIBzBBAAAOAcAQQAADhHAAEAAM4RQAAAgHMEEAAA4BwBBAAAOEcAAQAAzhFAAACAcwQQAADgHAEEAAA4RwABAADOEUAAAIBzBBAAAOAcAQQAADgXXtsdAGpb++lLarsLqKdq4rOzO3to0LcJ1EWcAQEAAM4RQAAAgHMEEAAA4BwBBAAAOEcAAQAAzhFAAACAcwQQAADgHAEEAAA4RwABAADOEUAAAIBzBBAAAOAcAQQAADhHAAEAAM4RQAAAgHMEEAAA4BwBBAAAOEcAAQAAzhFAAACAcwQQAADgHAEEAAA4RwABAADOEUAAAIBzBBAAAOAcAQQAADhHAAEAAM6F13YHAADfaz99SVC3tzt7aFC3BwQLAQT1SrAPzgCA2sElGAAA4BwBBAAAOEcAAQAAzhFAAACAcwQQAADgHAEEAAA4RwABAADOEUAAAIBzBBAAAOAcAQQAADhHAAEAAM4RQAAAgHMEEAAA4BwBBAAAOBde2x0AANSc9tOXBH2bu7OHBn2baHwIIKhRNXHwAwDUf1yCAQAAzhFAAACAcwQQAADgHAEEAAA4RwABAADOEUAAAIBzBBAAAOAcAQQAADhHAAEAAM4RQAAAgHMEEAAA4BwBBAAAOEcAAQAAzhFAAACAcwQQAADgHAEEAAA4RwABAADOEUAAAIBz4bXdAQBA/dJ++pKgbm939tCgbg/1AwEEfsE+qABAVdTEsYdQU/cRQOoxAgMAoL5iDAgAAHCOAAIAAJzjEoxjXZ9YptJwT213AwCAWkUAAQA0OMEaI+cpL9WbQdkSfshZADEzSVJJSYmrXdYZvpMndaKiQpJUUXpSvu/+DACo2yrKS/3H75KSEoWWl9dyj9w7+7199ns8WEIs2FusxP79+5WYmOhiVwAAIMj27dundu3aBW17zgKIz+fTwYMHZWZKSkrSvn375PV6Xey6QSopKVFiYiJ1/ImoY3BQx+CgjsFBHYPjbB337t2rkJAQtWnTRqGhwZu74uwSTGhoqNq1a+c/leP1evlgBAF1DA7qGBzUMTioY3BQx+Bo3rx5jdSRabgAAMA5AggAAHDOeQDxeDyaOXOmPB7uhfFTUMfgoI7BQR2DgzoGB3UMjpquo7NBqAAAAGdxCQYAADhHAAEAAM4RQAAAgHMEEAAA4FyNBpAlS5aof//+ioqKUmxsrO64446A9Xv37tXQoUMVHR2tuLg4Pfrooyr/wX32V6xYodTUVHk8HnXu3FkLFiyoyS7XaaWlperdu7dCQkK0YcOGgHWbNm3SwIEDFRkZqcTERM2ePfu81y9atEhdunRRZGSkevTooaVLlzrqee3avXu3Ro8erQ4dOigqKkqdOnXSzJkzVVZWFtCOGl6aF154Qe3bt1dkZKT69++vNWvW1HaX6pSsrCz17dtXzZo1U1xcnO644w5t3749oM3p06eVmZmpyy+/XE2bNtXw4cNVXFwc0KYqx8vGIjs7WyEhIZoyZYp/GTWsugMHDujee+/V5ZdfrqioKPXo0UNr1671rzczPfHEE0pISFBUVJTS0tK0Y8eOgG0cOXJEo0aNktfrVUxMjEaPHq0TJ05UryNWQxYvXmyxsbE2b9482759u23ZssVef/11//ry8nLr3r27paWl2fr1623p0qXWsmVLmzFjhr/Nrl27LDo62h555BErKCiwuXPnWlhYmC1btqymul2nTZo0yYYMGWKSbP369f7lx44ds9atW9uoUaMsPz/fXnvtNYuKirIXX3zR3+bTTz+1sLAwmz17thUUFNjjjz9uTZo0sc2bN9fCO3Hrvffes4yMDMvNzbUvvvjC3nrrLYuLi7OpU6f621DDS5OTk2MRERH25z//2bZs2WJjxoyxmJgYKy4uru2u1Rnp6ek2f/58y8/Ptw0bNtitt95qSUlJduLECX+b8ePHW2Jioi1fvtzWrl1r11xzjV177bX+9VU5XjYWa9assfbt21vPnj1t8uTJ/uXUsGqOHDliP/vZzywjI8NWr15tu3btstzcXNu5c6e/TXZ2tjVv3tzefPNN27hxo91+++3WoUMHO3XqlL/N4MGDrVevXrZq1Sr75JNPrHPnzjZy5Mhq9aVGAsiZM2esbdu29qc//anSNkuXLrXQ0FArKiryL5s3b555vV4rLS01M7Nf//rXdtVVVwW87u6777b09PSa6HadtnTpUuvSpYtt2bLlvADy+9//3mJjY/11MzObNm2aXXnllf7nd911lw0dOjRgm/3797dx48bVeN/rotmzZ1uHDh38z6nhpenXr59lZmb6n1dUVFibNm0sKyurFntVtx0+fNgk2UcffWRmZkePHrUmTZrYokWL/G22bt1qkiwvL8/Mqna8bAyOHz9uycnJ9v7779uNN97oDyDUsOqmTZtm119/faXrfT6fxcfH27PPPutfdvToUfN4PPbaa6+ZmVlBQYFJss8++8zf5r333rOQkBA7cOBAlftSI5dgPv/8cx04cEChoaFKSUlRQkKChgwZovz8fH+bvLw89ejRQ61bt/YvS09PV0lJibZs2eJvk5aWFrDt9PR05eXl1US366zi4mKNGTNGr7zyiqKjo89bn5eXpxtuuEERERH+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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, axs = plt.subplots(2, 1)\n", + "bins = range(-550, 551, 50)\n", + "axs[0].hist(result_unscaled.flatten().cpu().type(torch.float32), density=True, bins=bins,label=\"D scale=1\")\n", + "axs[1].hist(result_scaled.flatten().cpu().type(torch.float32), density=True, bins=bins, label=\"D scale=0.5\")\n", + "for ax in axs:\n", + " ax.set_yticks([])\n", + " ax.axvline(448, color=\"tab:red\")\n", + " ax.axvline(-448, color=\"tab:red\")\n", + " ax.legend()" + ] + }, + { + "cell_type": "markdown", + "id": "1e30578d-74fe-412f-a695-37ebc54ca688", + "metadata": {}, + "source": [ + "Notice how values are clamped to ±448 when the scale is set to 1." + ] + }, + { + "cell_type": "markdown", + "id": "8884013b-a995-4e71-b6d9-2113e1aec12f", + "metadata": {}, + "source": [ + "### Mixing different data types for A, B, C and D\n", + "\n", + "Unlike higher precision operations, for FP8 operations the types of all operands don't have to be equal. Specifically:\n", + "- A and B can both be `float8_e4m3fn`, or one of them can be `float8_e5m2`.\n", + "- C can be `float16`, `bfloat16` or `float32` (but not `float8`).\n", + "- If C is a 16-bit float, D can have the type of A, B or C.\n", + "\n", + "For more details on the supported types, please refer to the [cublasLtMatmul documentation in cuBLASLt docs](https://docs.nvidia.com/cuda/cublas/#cublasltmatmul).\n", + "\n", + "In nvmath-python, the default result type for D is the type of C:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "98f3789e-7089-4625-aa94-39174da46acb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a.dtype=torch.float8_e5m2 b.dtype=torch.float8_e4m3fn c.dtype=torch.bfloat16 => d.dtype=torch.bfloat16\n" + ] + } + ], + "source": [ + "a = torch.rand(m, k).type(torch.float8_e5m2)\n", + "b = torch.rand(n, k).type(torch.float8_e4m3fn).T\n", + "c = torch.rand(m, n).type(torch.bfloat16)\n", + "d = nvmath.linalg.advanced.matmul(a, b, c=c, beta=1, quantization_scales={\"a\": 1, \"b\": 1})\n", + "print(f\"{a.dtype=} {b.dtype=} {c.dtype=} => {d.dtype=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "184af9ab-0b19-4636-bb1d-c04b76f98e8f", + "metadata": {}, + "source": [ + "If C is not specified, the default type for D is the type of A:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "17cba1da-efa7-464a-a3c6-ed5d5ece4018", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a.dtype=torch.float8_e5m2 b.dtype=torch.float8_e4m3fn => d.dtype=torch.float8_e5m2\n" + ] + } + ], + "source": [ + "a = torch.rand(m, k).type(torch.float8_e5m2)\n", + "b = torch.rand(n, k).type(torch.float8_e4m3fn).T\n", + "d = nvmath.linalg.advanced.matmul(a, b, quantization_scales={\"a\": 1, \"b\": 1, \"d\": 1})\n", + "print(f\"{a.dtype=} {b.dtype=} => {d.dtype=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "cea06ad0-ef17-438d-94cd-d4e0ccfd6ff5", + "metadata": {}, + "source": [ + "However, you can also request a specific type for D by setting the `result_type` option:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "20d19166-70ec-40ae-864a-28e440364426", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "a.dtype=torch.float8_e5m2 b.dtype=torch.float8_e4m3fn options={'result_type': } => d.dtype=torch.bfloat16\n" + ] + } + ], + "source": [ + "a = torch.rand(m, k).type(torch.float8_e5m2)\n", + "b = torch.rand(n, k).type(torch.float8_e4m3fn).T\n", + "options = {\n", + " \"result_type\": nvmath.CudaDataType.CUDA_R_16BF # bfloat16\n", + "}\n", + "d = nvmath.linalg.advanced.matmul(a, b, quantization_scales={\"a\": 1, \"b\": 1}, options=options)\n", + "print(f\"{a.dtype=} {b.dtype=} {options=} => {d.dtype=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "2ae843db-bfd1-49f2-bd3b-2e47ced9308b", + "metadata": {}, + "source": [ + "Note that we provide a D scale only when D is FP8." + ] + }, + { + "cell_type": "markdown", + "id": "c95c17c5-806e-49df-b7e2-05804fcefe25", + "metadata": {}, + "source": [ + "### Scale modification in stateful Matmul\n", + "\n", + "Until now, we were using stateless `matmul` function to perform the multiplication. However, for repeated computations, stateful `Matmul` class should be used. For more details on the stateful and stateless APIs, visit the [nvmath-python documentation](https://docs.nvidia.com/cuda/nvmath-python/latest/overview.html#stateless-and-stateful-apis)\n", + ".\n", + "When performing multiple multiplications with a stateful `Matmul` object, you'll likely need to change the scales along with the operands. You can do this by specifying new scales as the `quantization_scales` argument of `reset_operands`:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "0d2b1749-888c-4fb1-9cef-e94d4c44868a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Mean value of result with A scale set to 2: 126.25104522705078\n", + "Mean value of result with A scale set to 0.0001: 0.006436571013182402\n" + ] + } + ], + "source": [ + "a = torch.rand(m, k, device=\"cuda\").type(torch.float8_e5m2)\n", + "b = torch.rand(n, k, device=\"cuda\").type(torch.float8_e4m3fn).T\n", + "with nvmath.linalg.advanced.Matmul(a, b, quantization_scales={\"a\": 2, \"b\": 2, \"d\": 1}) as mm:\n", + " mm.plan()\n", + " result1 = mm.execute()\n", + " print(f\"Mean value of result with A scale set to 2: {result1.type(torch.float32).mean().item()}\")\n", + " new_a = torch.rand(m, k, device=\"cuda\").type(torch.float8_e5m2)\n", + " mm.reset_operands(a=new_a, quantization_scales={\"a\": 0.0001}) # Not that not all scales need to be reset\n", + " result2 = mm.execute()\n", + " print(f\"Mean value of result with A scale set to 0.0001: {result2.type(torch.float32).mean().item()}\")" + ] + }, + { + "cell_type": "markdown", + "id": "e4581f18-bdec-46d3-b79b-dd2ea8f4c542", + "metadata": {}, + "source": [ + "Alternatively, you can provide the quantization scales as single-element GPU tensors (instead of Python scalars). This allows you to change them in-place:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "081c14f3-3df9-48f7-bd3a-2a994e299cf4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Mean value of result with A scale set to 2: 127.38880920410156\n", + "Mean value of result with A scale set to 0.0001: 0.006453291978687048\n" + ] + } + ], + "source": [ + "a = torch.rand(m, k, device=\"cuda\").type(torch.float8_e5m2)\n", + "b = torch.rand(n, k, device=\"cuda\").type(torch.float8_e4m3fn).T\n", + "a_scale = torch.full((1,), 2., device=\"cuda\", dtype=torch.float32) # We'll change it in-place\n", + "with nvmath.linalg.advanced.Matmul(a, b, quantization_scales={\"a\": a_scale, \"b\": 2, \"d\": 1}) as mm:\n", + " mm.plan()\n", + " result1 = mm.execute()\n", + " print(f\"Mean value of result with A scale set to 2: {result1.type(torch.float32).mean().item()}\")\n", + " a_scale[:] = 0.0001 # Note: no reset_operands\n", + " result2 = mm.execute()\n", + " print(f\"Mean value of result with A scale set to 0.0001: {result2.type(torch.float32).mean().item()}\")" + ] + }, + { + "cell_type": "markdown", + "id": "7bd981a0-75e4-4c54-8f56-318350295264", + "metadata": {}, + "source": [ + "### Delayed scaling with amax\n", + "\n", + "In real-world applications, choosing the scales might be non-trivial. However, in some cases, such as during the training of deep learning models, the distribution of inputs in subsequent iterations tends to be similar, which allows estimating the scales needed based on the results from previous iterations.\n", + "\n", + "To facilitate choosing scales automatically, nvmath-python can output the maximum absolute value in the result, abbreviated as *amax*. To request the amax, set the `result_amax` option to `True`. When amax is requested, nvmath-python will return a tuple containing the actual result and an auxiliary output dictionary containing the amax as a single-element tensor (possibly along with other outputs)." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "ad0262b4-e0b6-4c07-8b49-8f437ed92145", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "type(ret)=\n", + "aux={'result_amax': tensor([42.3923], device='cuda:0')}\n" + ] + } + ], + "source": [ + "a = torch.rand(m, k, device=\"cuda\").type(torch.float8_e4m3fn)\n", + "b = torch.rand(n, k, device=\"cuda\").type(torch.float8_e4m3fn).T\n", + "options = {\n", + " \"result_amax\": True\n", + "}\n", + "ret = nvmath.linalg.advanced.matmul(a, b, quantization_scales={\"a\": 1, \"b\": 1, \"d\": 1}, options=options)\n", + "print(f\"{type(ret)=}\")\n", + "result, aux = ret\n", + "print(f\"{aux=}\")" + ] + }, + { + "cell_type": "markdown", + "id": "22a6e556-9e39-4e19-86a3-f831d8a18198", + "metadata": {}, + "source": [ + "Let's now implement a simple delayed scaling approach. With delayed scaling, the amax values from previous iterations are used to compute the correct D scales for subsequent iterations. In each iteration, we will multiply two tensors with values sampled from $\\mathcal{N}(0, 1)$, with each tensor scaled by a random factor sampled uniformly from $(0, 10)$.\n", + "\n", + "We'll start with all scales set to 1. In each iteration, we will adjust the scale for D to:\n", + "$$\n", + "\\text{scale}_D = \\frac{\\max(\\mathtt{float8\\_e4m3fn})}{\\text{amax}} = \\frac{448}{\\text{amax}}\n", + "$$" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "f1cbd081-6c55-44d1-84e9-e65b34839e19", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Iteration 1: amax=tensor([2117.1724], device='cuda:0'), 28.85% values are 448. Setting D scale to 0.21160298585891724\n", + "Iteration 2: amax=tensor([1987.5466], device='cuda:0'), 0.00% values are 448. Setting D scale to 0.2254035323858261\n", + "Iteration 3: amax=tensor([2071.7327], device='cuda:0'), 0.00% values are 448. Setting D scale to 0.21624411642551422\n", + "Iteration 4: amax=tensor([2219.4202], device='cuda:0'), 0.00% values are 448. Setting D scale to 0.20185452699661255\n", + "Iteration 5: amax=tensor([1961.7855], device='cuda:0'), 0.00% values are 448. Setting D scale to 0.22836339473724365\n", + "Iteration 6: amax=tensor([2017.6495], device='cuda:0'), 0.00% values are 448. Setting D scale to 0.2220405638217926\n", + "Iteration 7: amax=tensor([2089.1074], device='cuda:0'), 0.00% values are 448. Setting D scale to 0.21444565057754517\n", + "Iteration 8: amax=tensor([2168.7864], device='cuda:0'), 0.00% values are 448. Setting D scale to 0.20656713843345642\n", + "Iteration 9: amax=tensor([2081.2173], device='cuda:0'), 0.00% values are 448. Setting D scale to 0.2152586430311203\n", + "Iteration 10: amax=tensor([2324.1553], device='cuda:0'), 0.00% values are 448. Setting D scale to 0.1927582025527954\n" + ] + } + ], + "source": [ + "m, n, k = 1024, 1024, 2048\n", + "a = torch.zeros(m, k, device=\"cuda\").type(torch.float8_e4m3fn)\n", + "b = torch.zeros(n, k, device=\"cuda\").type(torch.float8_e4m3fn).T\n", + "\n", + "def regenerate_inputs():\n", + " global a, b\n", + " a[:] = 3 * torch.randn(a.shape, device=\"cuda\")\n", + " b[:] = 3 * torch.randn(b.shape, device=\"cuda\")\n", + "\n", + "scales = {\"a\": 1, \"b\": 1, \"d\": torch.ones((1,), device=\"cuda\", dtype=torch.float32)} # D scale is a GPU tensor to allow for in-place change\n", + "options = {\"result_amax\": True}\n", + "num_iters = 10\n", + "with nvmath.linalg.advanced.Matmul(a, b, quantization_scales=scales, options=options) as mm:\n", + " mm.plan()\n", + " for iteration in range(1, num_iters+1):\n", + " regenerate_inputs()\n", + " d, aux = mm.execute()\n", + " amax = aux[\"result_amax\"]\n", + " overflow_frac = (d.cpu().abs() == 448).sum() / d.nelement()\n", + " scales[\"d\"][:] = 448 / amax\n", + " print(f\"Iteration {iteration}: amax={amax}, {overflow_frac*100:.02f}% values are 448. Setting D scale to {scales['d'].item()}\")" + ] + }, + { + "cell_type": "markdown", + "id": "0ab4fa2b-a778-4fef-81b8-1d9897c0499c", + "metadata": {}, + "source": [ + "Depending on the use case, it might be beneficial to compute the new scale based not on a single amax value, but on the history of amax values." + ] + }, + { + "cell_type": "markdown", + "id": "3d5bd83a-085b-47b0-b334-a884199938ff", + "metadata": {}, + "source": [ + "## MXFP8 (microscaling FP8) operations with nvmath-python\n", + "\n", + "
\n", + "MXFP8 matrix multiplication in nvmath-python requires CUDA Toolkit 12.8 or newer and a device with compute capability 10.0 or higher. Devices with compute capability 10.0 or higher include devices based on Blackwell architecture, such as B100 and B200. Please refer to the table at https://developer.nvidia.com/cuda-gpus to check the compute capability of your device. Note that you also need PyTorch version built with CUDA 12.8.\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "ca86db73-f598-4856-a68c-a72bfaac4bec", + "metadata": {}, + "source": [ + "### The limits of tensor-wide scaling\n", + "\n", + "Sometimes, even with scaling, getting results clamped is unavoidable. The D scale factor needs to be small enough to squeeze all (or almost all) values into the dynamic range of the FP8 type used. However, if it's too small, numbers very close to 0 turn into 0 when scaled down. To illustrate this, let's consider the following (artificial) example.\n", + "\n", + "Let A and B be matrices sampled from $\\mathcal{N}(0, 1)$. However, to expand the range of values, let's scale each row by a random power of 2, sampled from $2^{\\;\\mathcal{U}(-8, 8)}$. This example is exaggerated for learning purposes, but illustrates the problems with FP8 tensor-wide scaling well." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "da80b7c2-9b6d-4911-b8b6-a761b3797d9d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Absolute values in the result range from 1.998816878767684e-05 to 12908.6708984375.\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA78AAAEOCAYAAABB6aBjAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzsvXe8ZVV99/9ea/dT77l97r3TKzB0GLCDAoIo6BPEGmNiTFETkxjzmDxJLClPijHGQn5P1EgiKE8eRbEL2MBGlTIzMIW5028vp+661vr9sYeJI8jMCMkonvfrdV53zj5rr712+8x3rfVd368wxhi6dOnSpUuXLl26dOnSpUuXpzHyRDegS5cuXbp06dKlS5cuXbp0+a+m2/nt0qVLly5dunTp0qVLly5Pe7qd3y5dunTp0qVLly5dunTp8rSn2/nt0qVLly5dunTp0qVLly5Pe7qd3y5dunTp0qVLly5dunTp8rSn2/nt0qVLly5dunTp0qVLly5Pe7qd3y5dunTp0qVLly5dunTp8rSn2/nt0qVLly5dunTp0qVLly5Pe7qd3y5dunTp0qVLly5dunTp8rSn2/n9KdFas3HjRv7qr/7q8LZrr70WIQS7d+8+cQ17CpmamuKqq66ir68PIQTvf//7n3SdQgje8pa3PPnGAa985Su5+uqrn5K6unTp8tTR1cefjq4+dunS5Xj5j//4D3p7e2m1Woe3rVixgte//vUnrlFPMZ/4xCfYsGEDjuPQ09PzpOt717vehRCC2dnZJ13X1q1bsW2bzZs3P+m6uvz38Avd+b3mmmsQQnDeeecd976f+tSn2Ldv31NmqBwr+/bt493vfjebNm2iVqvR39/PBRdcwK233vqUH+v3f//3+drXvsYf//Ef84lPfIJLL730KT/Gk+F//s//yWc+8xnuv//+E92ULl2eFjzaQf3Rz+DgIBdeeCFf+cpXjrmerj6eeLr62KXLkTwZm+/xyLKMTqfzU++/YsWKI7TW933Wrl3L29/+dubn54+pDqUU73znO/md3/kdSqXST92Wn4Ybb7yRV7ziFaxatYpCocD69et529vexuLi4lN6nIcffpjXv/71rF69mo985CP8y7/8y1Na/5Pl5JNP5vLLL+fP//zPT3RTuhwj9oluwInk+uuvZ8WKFdx5553s3LmTNWvWHPO+f//3f88rX/lKqtXqf2ELH8tNN93E3/7t3/LSl76UX/mVXyHLMv793/+diy++mH/913/lV3/1V5+yY33jG9/gyiuv5A//8A+fsjqfSs4880zOOecc/uEf/oF///d/P9HN6dLlacN73vMeVq5ciTGGqakprr32Wl70ohfxhS98gRe/+MVH3b+rjyeerj526XIkT8bme5QDBw7wvve9j5tuuoldu3ZhjKFWq3HxxRfz27/921xwwQXHVd8ZZ5zB2972NgCiKOKee+7h/e9/P9/+9re58847j7r/F77wBbZt28Zv/MZvHPe5PFl+4zd+g5GREV772teybNkyHnzwQT70oQ/x5S9/mXvvvZcgCJ6S43zrW99Ca80//dM//VT37L+D3/qt3+JFL3oRjzzyCKtXrz7RzelyNMwvKLt27TKAufHGG83AwIB517vedcz73nvvvQYwt9566xHbP/7xjxvAjI+PP8Wt/U82b95sZmZmjtgWRZHZsGGDGRsbe0qPJYQwb37zm5/SOoGntM73vve9plgsmmaz+ZTV2aXLLyqPathdd911xPb5+XnjOI559atffdQ6uvr409PVxy5d/mt4Mjbfo3z84x83QRCYNWvWmD/90z81n/70p80XvvAF8+EPf9hcdtllRkppfvVXf9UkSXJM9S1fvtxcfvnlj9n+h3/4hwYw27dvP2odV1xxhXn2s5/9uHX/yq/8yjG146flm9/85mO2/du//ZsBzEc+8pGn7Djvfve7DfAYbX8yvPOd73xK60ySxNRqNfNnf/ZnT0l9Xf5r+YV1e77++uup1WpcfvnlXHXVVVx//fXHvO/nPvc5XNfluc997jGVv+aaazjllFPwPI+RkRHe/OY3P65byIc//GFWrVpFEARs2rSJ22+/nQsuuOCIkcRTTjmF/v7+I/bzPI8XvehF7N+/n2azedT27Nq1i5e//OX09vZSKBQ4//zz+dKXvnT490ddH40xfPjDHz7skvNEvPe97+WZz3wmfX19BEHA2Wefzac//emfWP76669n/fr1+L7P2WefzW233XbE781mk9/7vd9jxYoVeJ7H4OAgF198Mffee+8R5S6++GLa7Ta33HLLUc+7S5cuPx09PT0EQYBtH91ZqKuPj6Wrj126nFiejM0H8NGPfpQ3vOENvOtd7+Lhhx/mL/7iL/ilX/olXvziF/OmN72JL3/5y3z3u9/lG9/4Bq973eueVFuHh4cBjqq3URTx1a9+lYsuuuiY6j2atj3Knj17uOKKKygWiwwODh5e4iGE4Fvf+tbhco83y/2yl70MgIceeuiY2nQ0/V+xYgXvfOc7ARgYGEAIwbve9a6fWN8DDzzA61//elatWoXv+wwPD/Nrv/ZrzM3NPW752dlZrr76aiqVCn19fbz1rW8liqIjytxyyy08+9nPpqenh1KpxPr16/mTP/mTI8o4jsMFF1zATTfddEzn3eUEc6J73yeKDRs2mDe84Q3GGGNuu+02A5g777zzmPa96KKLzFlnnfWY7Y83s/Ho6NJFF11kPvjBD5q3vOUtxrIsc+655x4xOnjNNdcYwDznOc8xH/jAB8wf/MEfmN7eXrN69WrzvOc976htevWrX20KhYLJsuwJy01OTpqhoSFTLpfN//pf/8u8733vM6effrqRUpobb7zRGGPMI488Yj7xiU8YwFx88cXmE5/4hPnEJz7xhPWOjY2ZN73pTeZDH/qQed/73mc2bdpkAPPFL37xiHKA2bhxo+nv7zfvec97zN/+7d+a5cuXmyAIzIMPPnjE+biua/7gD/7AfPSjHzV/+7d/a17ykpeY66677oj60jQ1QRCYt73tbUe9Rl26dHliHtWwW2+91czMzJjp6WmzefNm85u/+ZtGSmluvvnmo9bR1cfH0tXHLl1OLE/G5tuxY4fxPM98/OMfP2J7s9k0SiljjDGLi4smiiKzZ88e09/fb2644Yaj1rt8+XJzySWXmJmZGTMzM2P27dtnPv/5z5uRkRHz3Oc+96j7f+c73zGA+fznP/+4df/ozO+xaJsxxrRaLbNq1SoTBIF5xzveYd7//vebTZs2mdNPP90Ajzvb+6Ns377dAOav//qvj9r+Y9H/z372s+ZlL3uZAcw///M/m0984hPm/vvv/4l1vve97zXPec5zzHve8x7zL//yL+atb32rCYLAbNq0yWitH3PsU0891bzkJS8xH/rQh8xrX/taA5hf/uVfPlxu8+bNxnVdc84555h/+qd/Mv/f//f/mT/8wz983Pvzl3/5l0ZKaer1+lHPvcuJ5Rey83v33XcbwNxyyy3GGGO01mZsbMy89a1vPab9x8bGzC/90i89ZvuPG3fT09PGdV1zySWXHBZIY4z50Ic+ZADzr//6r8YYY+I4Nn19febcc881aZoeLnft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/vIRgaWeiAMHDhzWqSRJuP/++/k//+f/0N/ffzj40k/C930uueQSbr31Vt7znvc8Ydlj1bbf/M3f5EMf+hCvetWreOtb38qSJUu4/vrr8X0fONIt+NJLL2XXrl380R/9Ed/5znf4zne+c/i3oaGhJ9SdY9X/46FSqfDc5z6Xv/u7vyNNU0ZHR7n55pufcGZ8fHycK664gksvvZTvf//7XHfddbz61a8+PPDwnve8h9tuu43LL7+c5cuXMz09zTXXXMPY2NgR9zhNU7797W/zpje96bjb3eUEcEJiTJ8gXvKSlxjf90273f6JZV7/+tcbx3GOGmb+tNNOO5wz7lEeL4+lMXno9g0bNhjHcczQ0JD57d/+bbOwsPCYOj/wgQ+Y5cuXG8/zzKZNm8x3v/tdc/bZZ5tLL730cJlHw7P/pM/RwtAbk+epvOqqq0xPT4/xfd9s2rTpMfkmjTm+VB4f+9jHzNq1a43neWbDhg3m4x//+OG2Pl6d11133eHyZ5555hHtjuPYvP3tbzenn366KZfLplgsmtNPP91cc801jznueeedZ1772tceUxu7dOnyxDxeqiPf980ZZ5xh/vmf//mIPIlPRFcfj6Srj126/PfzVNl83/zmN43rukeksGk0Gub2228/nNLn/vvvN4888shxte/HUx1JKc3g4KB51ateZXbu3HlMddx4441GCGH27t37mLp/NNWRMceubbt27TKXX365CYLADAwMmLe97W3mM5/5jAHMD37wg8PlnkhrjyX/ujHHpv/Hk+po//795mUve5np6ekx1WrVvPzlLzcHDx40gHnnO9/5mDq3bt1qrrrqKlMul02tVjNvectbTBiGh8t9/etfN1deeaUZGRkxruuakZER86pXveoxqZy+8pWvGMDs2LHjmM67y4lFGPNTRtz4BecTn/gEb37zm9m7d+9PHdTgaGitGRgY4H/8j//xuC4nv+jcd999nHXWWdx7772H3VB+kYmi6Jgi8UI+m/boSG6XLk81XX088XT18Ui6+tjlyfDmN7+ZT3/603z2s5/9icsIbr/9dlavXn1MkeWfKpRSnHzyyVx99dX8xV/8xX/Zcd7//vfz+7//++zfv5/R0dH/suP8vPLSl74UIQSf/exnT3RTfiqORx/haaCRJ7r3/fOKUsqccsop5i//8i+fkvrCMHzMrMqjMyXXXXfd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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "m, n, k = 256, 256, 256\n", + "\n", + "a_fp32 = torch.randn(m, k) * (2 ** (torch.rand(m) * 16 - 8)[:,None])\n", + "b_fp32 = torch.randn(n, k).T\n", + "d_fp32 = a_fp32 @ b_fp32\n", + "\n", + "fig, axs = plt.subplots(1, 3, figsize=(12, 3))\n", + "plt.colorbar(axs[0].imshow(a_fp32.abs().log2()), ax=axs[0])\n", + "axs[0].set_title(\"A (log2 of abs)\")\n", + "axs[0].axis(\"off\")\n", + "plt.colorbar(axs[1].imshow(b_fp32.abs().log2()), ax=axs[1])\n", + "axs[1].set_title(\"B (log2 of abs)\")\n", + "axs[1].axis(\"off\")\n", + "plt.colorbar(axs[2].imshow(d_fp32.abs().log2()), ax=axs[2])\n", + "# ax=axs[2])\n", + "axs[2].set_title(\"A @ B (log2 of abs)\")\n", + "axs[2].axis(\"off\")\n", + "\n", + "print(f\"Absolute values in the result range from {d_fp32.abs().min()} to {d_fp32.abs().max()}.\")" + ] + }, + { + "cell_type": "markdown", + "id": "e311db38-d240-4c2a-8986-844073093046", + "metadata": {}, + "source": [ + "With such a huge range of values, regardless of the scale we choose, either some very small values will be scaled down to 0, or some big values will be clamped to 448. Let's see on a plot how the choice of the quantization scale for D affects the number of errors of each kind." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "e871cb38-474d-4e43-ae5e-c92a25aaaddd", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "a = a_fp32.cuda().type(torch.float8_e4m3fn)\n", + "b = b_fp32.cuda().type(torch.float8_e4m3fn)\n", + "\n", + "def count_errors(dscale):\n", + " scales = {\"a\": 1, \"b\": 1, \"d\": dscale}\n", + " d = nvmath.linalg.advanced.matmul(a, b, quantization_scales=scales)\n", + " return (d == 0).sum().item() / d.nelement(), (d.cpu().abs() == 448).sum().item() / d.nelement()\n", + "\n", + "dscales = 2**torch.linspace(-5, 0, 100)\n", + "errors = torch.as_tensor([count_errors(dscale) for dscale in dscales])\n", + "underflows = errors[:,0] * 100\n", + "overflows = errors[:,1] * 100\n", + "total_fp8_errors = underflows + overflows\n", + "\n", + "fig, ax = plt.subplots(1, 1, figsize=(6,4))\n", + "ax.plot(dscales, underflows, label=\"Underflow errors\")\n", + "ax.plot(dscales, overflows, label=\"Overflow errors\")\n", + "ax.plot(dscales, total_fp8_errors, color=\"black\", linestyle=\":\", label=\"Total errors\")\n", + "ax.set_xscale(\"log\", base=2)\n", + "ax.set_xlabel(\"Applied D scale\")\n", + "ax.set_ylabel(\"Errors [%]\")\n", + "ax.legend();\n", + "plt.ylim(ymin=0);" + ] + }, + { + "cell_type": "markdown", + "id": "9c7e2a81-5eb1-4eab-b2e8-4589fb78679f", + "metadata": {}, + "source": [ + "### Basics of MXFP8\n", + "\n", + "To address this issue, MXFP8, which stands for microscaling FP8, uses *block scaling* instead of *tensor-wide scaling*: instead of applying a single scale factor to the whole matrix, separate scales are applied to blocks of 32 elements. This way, even when there are both very big and very small numbers in the matrix, it is possible keep both of them - unless, of course, both big and small values happen to be placed in the same scaling block.\n", + "\n", + "To enable MXFP8, set `block_scaling` option to `True`.\n", + "\n", + "Also, in MXFP8 mode, result scaling works differently. Instead of expecting the user to provide the scale for D, cuBLAS will compute appropriate scale for each block, apply the scales, and return the scaled output together with the scales used. The scales will be returned as `\"d_out\"` in the auxilary outputs dictionary.\n", + "\n", + "As in case of FP8, you have to provide scales for A and B. Because the layout of the block scaling factor is quite complex, nvmath provides `create_mxfp8_scale` helper, which allows you to create a block scaling factors for a given tensor, filled with a constant value. The second argument of `create_mxfp8_scale` is a base 2 exponent of the scale to be used. In this case, by passing $0$ we request the scale to be $2^0=1$." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "6e4151e8-393e-4df3-b9e5-3c24ad8ebbd8", + "metadata": {}, + "outputs": [], + "source": [ + "scales = {\n", + " \"a\": nvmath.linalg.advanced.helpers.matmul.create_mxfp8_scale(a, 0),\n", + " \"b\": nvmath.linalg.advanced.helpers.matmul.create_mxfp8_scale(b, 0),\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "c628c8c2-78df-4df5-991e-058bc38ebfd2", + "metadata": {}, + "source": [ + "Let's try that:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "9da4bff5-433c-4d4e-838b-da67311eee4e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(tensor([[-384., 160., 24., ..., 416., 208., 224.],\n", + " [-120., -120., -104., ..., -22., 144., 160.],\n", + " [-192., -288., -120., ..., 224., 96., 15.],\n", + " ...,\n", + " [-416., 416., 384., ..., 18., 32., -72.],\n", + " [ 112., 320., -60., ..., 112., 13., 64.],\n", + " [ 192., 128., -192., ..., 160., 176., -56.]], device='cuda:0',\n", + " dtype=torch.float8_e4m3fn),\n", + " {'d_out_scale': tensor([128, 128, 129, ..., 117, 117, 117], device='cuda:0',\n", + " dtype=torch.uint8)})" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "options = {\"block_scaling\": True}\n", + "result, aux = nvmath.linalg.advanced.matmul(a, b, options=options, quantization_scales=scales) # Note: a tuple is returned\n", + "result, aux" + ] + }, + { + "cell_type": "markdown", + "id": "46252c4a-e3f1-4838-bf0e-d8bc5d025877", + "metadata": {}, + "source": [ + "We will discuss the meaning of the block scales tensor later. For now, let's just use the `apply_mxfp8_scales` to apply the returned scales to the returned D:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "e80b4308-c1c5-4ef9-a6a3-f66117a3ad68", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([[-7.6800e+02, 3.2000e+02, 4.8000e+01, ..., 8.3200e+02,\n", + " 4.1600e+02, 4.4800e+02],\n", + " [-3.7500e+00, -3.7500e+00, -3.2500e+00, ..., -6.8750e-01,\n", + " 4.5000e+00, 5.0000e+00],\n", + " [-1.8750e-01, -2.8125e-01, -1.1719e-01, ..., 4.3750e-01,\n", + " 1.8750e-01, 2.9297e-02],\n", + " ...,\n", + " [-1.3000e+01, 1.3000e+01, 1.2000e+01, ..., 1.1250e+00,\n", + " 2.0000e+00, -4.5000e+00],\n", + " [ 2.1875e-01, 6.2500e-01, -1.1719e-01, ..., 4.3750e-01,\n", + " 5.0781e-02, 2.5000e-01],\n", + " [ 1.8750e-01, 1.2500e-01, -1.8750e-01, ..., 1.5625e-01,\n", + " 1.7188e-01, -5.4688e-02]], device='cuda:0')" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nvmath.linalg.advanced.helpers.matmul.apply_mxfp8_scale(result, aux[\"d_out_scale\"])" + ] + }, + { + "cell_type": "markdown", + "id": "e98a2fc1-5d6f-470d-ac0f-23ef78083d0e", + "metadata": {}, + "source": [ + "Let's see how many underflow/overflow errors happened in the MXFP8 computation:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "7fafc3e4-5882-455e-820c-a8efe80f0c51", + "metadata": {}, + "outputs": [], + "source": [ + "mxfp8_underflows = 100 * (result == 0).sum() / result.nelement()\n", + "mxfp8_overflows = 100 * (result.cpu().abs() == 448).sum() / result.nelement()\n", + "mxfp8_errors = mxfp8_underflows + mxfp8_overflows" + ] + }, + { + "cell_type": "markdown", + "id": "94e8c513-31f5-4089-b7a6-64ce4ac53863", + "metadata": {}, + "source": [ + "Let's see how it compares to FP8:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "278362e0-373b-477f-983b-d2c8a0b7fa27", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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2scJmER65P3JmtbW1rC2ts6x3btPUSUi0hWcM0FERERqYTFBREREamExQURERGphMUFERERqYTFBREREamExQURERGphMUFERERqYTFBREREamExQURERGphMUFERERqYTFBREREamExQURERGphMUFERERqYTFBREREamExQURERGphMUFERERqYTFBREREamExQURERGphMUFERERqYTFBREREamExQURERGphMUFERERqYTFBREREamExQURERGphMUFERERqYTFBREREamExQURERGphMUFERERqYTFBREREamExQURERGoxkjpAUclUpCFTYSh1DCIiIr2RqUjL13YlppiYcdwVppZSpyAiItIfGfmrJTjMQUREROqRCYIgSB1Cm+RyOWxtbfHsRSxsbGykjkNERKQ35HI5HB1ckZyc/MHvUL0Y5li6dCnmzp2LuLg41KhRA4sXL0bdunUL9B4mRpYwMeI4BxERUX6ZGGXnazudH+bYsmULRo0ahcmTJ+PSpUuoUaMGgoKCkJCQIHU0IiIigh4Mc9SrVw916tTBkiVLAABKpRJubm4YOnQoxo8fn2P7jIwMZGRkiMvJyclwd3fH48ePOcxBRERUAHK5HG5ubkhKSoKtrW2e2+n0MEdmZiYuXryI4OBgsc3AwACBgYE4c+ZMrvuEhIRg6tSpOdrd3Ny0lpOIiKg4S0lJ0d9i4vnz58jOzoazs7NKu7OzM27evJnrPsHBwRg1apS4rFQqkZiYCAcHB8hkMo3kelup8WyH5rBPNYv9qXnsU81if2qeNvpUEASkpKTA1dX1g9vpdDFRGKampjA1NVVps7Oz08qxbGxs+B+BhrFPNYv9qXnsU81if2qepvv0Q2ck3tLpCZilS5eGoaEh4uPjVdrj4+Ph4uIiUSoiIiJ6l04XEyYmJqhVqxaOHDkitimVShw5cgQNGjSQMBkRERG9pfPDHKNGjUKvXr1Qu3Zt1K1bFwsXLkRaWhr69OkjWSZTU1NMnjw5x3AKFR77VLPYn5rHPtUs9qfmSdmnOn9pKAAsWbJEvGmVv78/fv31V9SrV0/qWERERAQ9KSaIiIhId+n0nAkiIiLSfSwmiIiISC0sJoiIiEgtLCaIiIhILSwmtMDT0xN+fn7w9/dHs2bNpI5TLLx69QoeHh4YPXq01FH0XlJSEmrXrg1/f39Uq1YN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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "fig, ax = plt.subplots(1, 1, figsize=(6,4))\n", + "ax.plot(dscales, total_fp8_errors, label=\"FP8 errors\", color=\"black\", linestyle=\":\")\n", + "ax.axhline(mxfp8_errors.item(), label=\"MXFP8 errors\", color=\"#76b900\")\n", + "ax.set_xscale(\"log\", base=2)\n", + "ax.set_xlabel(\"Applied D scale for FP8\")\n", + "ax.set_ylabel(\"Errors [%]\")\n", + "plt.ylim(ymin=0)\n", + "ax.legend();" + ] + }, + { + "cell_type": "markdown", + "id": "43625c5f-f9a5-4027-97eb-79b93d132a53", + "metadata": {}, + "source": [ + "In this particular case, MXFP8 performed so well, because by scaling each row by a single factor we ensured that the numbers in each MXFP8 block are of similar magnitude. If we scaled each element individually by a random power of 2, the gains from MXFP8 would be much less visible. However, in the Deep Learning workloads, MXFP8 has been demonstrated to improve the accuracy." + ] + }, + { + "cell_type": "markdown", + "id": "1d69ac06-2a69-4659-b089-fc810997f626", + "metadata": {}, + "source": [ + "### MXFP8 block scales value encoding\n", + "\n", + "#### Value encoding\n", + "As you may have noticed, the scales used in MXFP8 are small integers:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "19f89955-7ee7-499d-912f-43f09d03564d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([128, 128, 129, ..., 117, 117, 117], device='cuda:0',\n", + " dtype=torch.uint8)" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aux[\"d_out_scale\"]" + ] + }, + { + "cell_type": "markdown", + "id": "a9b0659a-d48a-4ce0-82f5-17820d93fa33", + "metadata": {}, + "source": [ + "Each scale is encoded in the UE8M0 format, which means an unsigned 8-bit exponent, without any exp}licit significand bits. We interpret the value $x$ as:\n", + "$\n", + "2^{x - 127}\n", + "$. For example, value `132` means that scale used was $2^{132-127}=2^5=32$.\n", + "\n", + "### [Advanced] MXFP8 block scales layout\n", + "\n", + "The layout of the scales is relatively complex. In typical use cases, you shouldn't have to care about it. However, in advanced use cases you may need to tweak particular scaling factors. This section is optional and for curious users. In normal use-cases, the details of MXPF8 layout should be opaque to the user and are subject to change in future releases of nvmath-python.\n", + "\n", + "We provide `get_mxfp8_scale_offset`, which given a tuple of indices in the matrix, returns the index in the scales vector containing the scale for this item. To illustrate its usage, let's create two simple matrices, and prepare unit scales for both:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "9d6289a6-5909-4439-beea-eb3a4511f19c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([[1., 1., 1., ..., 1., 1., 1.],\n", + " [1., 1., 1., ..., 1., 1., 1.],\n", + " [1., 1., 1., ..., 1., 1., 1.],\n", + " ...,\n", + " [1., 1., 1., ..., 1., 1., 1.],\n", + " [1., 1., 1., ..., 1., 1., 1.],\n", + " [1., 1., 1., ..., 1., 1., 1.]])" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = torch.ones(128, 128).type(torch.float8_e4m3fn)\n", + "b = torch.eye(128).type(torch.float8_e4m3fn).T # identity matrix\n", + "scales = {\n", + " \"a\": nvmath.linalg.advanced.helpers.matmul.create_mxfp8_scale(a, 0),\n", + " \"b\": nvmath.linalg.advanced.helpers.matmul.create_mxfp8_scale(b, 0),\n", + "}\n", + "result, aux = nvmath.linalg.advanced.matmul(a, b, quantization_scales=scales, options={\"block_scaling\": True})\n", + "actual_result = nvmath.linalg.advanced.helpers.matmul.apply_mxfp8_scale(result, aux[\"d_out_scale\"])\n", + "actual_result" + ] + }, + { + "cell_type": "markdown", + "id": "304b3d9b-5a72-40b2-86af-afe2ad0ea4a6", + "metadata": {}, + "source": [ + "Now, let's use `get_mxfp8_scale_offset` to get the scale for element at $(40, 70)$ in `a` and set the scale to 16x bigger value. Let's repeat the matmul and display the results. Note how changing one scale affected the whole block of 32 elements." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "b57a80c2-40d4-443c-8e8f-aebe8cc65dca", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Offset is 134\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "offset = nvmath.linalg.advanced.helpers.matmul.get_mxfp8_scale_offset(a, (40, 70))\n", + "print(\"Offset is\", offset)\n", + "scales[\"a\"][offset] += 4 # Increasing the exponent by 4 means scaling 16x.\n", + "\n", + "result2, aux = nvmath.linalg.advanced.matmul(a, b, quantization_scales=scales, options={\"block_scaling\": True})\n", + "actual_result2 = nvmath.linalg.advanced.helpers.matmul.apply_mxfp8_scale(result, aux[\"d_out_scale\"])\n", + "\n", + "fig, axs = plt.subplots(1, 2, figsize=(6, 4))\n", + "axs[0].imshow(actual_result)\n", + "axs[1].imshow(actual_result2)\n", + "axs[0].set_title(\"Before scale change\")\n", + "axs[1].set_title(\"After scale change\");" + ] + }, + { + "cell_type": "markdown", + "id": "7bd2ee42-bb37-4dd0-b3b9-a10ad992413d", + "metadata": {}, + "source": [ + "To visualize the layout of MXFP8 scales, let's use `get_mxfp8_scale_offset` again, but this time computing the offset for each element. Note how the blocks for A (row-major layout) are laid out horizontally, while blocks of B (column-major layout) are laid out vertically. This is because the MXFP8 scales are always applied to blocks of consecutive elements in the memory." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "bd4c3000-7f2a-46f8-b5a8-07514dc02344", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAqsAAAFbCAYAAADyYcd1AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjEsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvc2/+5QAAAAlwSFlzAAAPYQAAD2EBqD+naQAAbx5JREFUeJzt3Xl4FFX6Pvy7O13ZuxPSQEJkV5BFBYWRAUVRUQYFRVG/IKOoCC4gIKiIP1dGB4UZRVFxB1QcZ1xwGwVZRRQRQYUECAmEnSRk7X0/7x++1dNdqYbELFUd7s919QWpruWpqpNTT06dOmUQQggQEREREemQUesAiIiIiIhiYbJKRERERLrFZJWIiIiIdIvJKhERERHpFpNVIiIiItItJqtEREREpFtMVomIiIhIt5isEhEREZFuMVklIiIiIt1isqpzBoMBTzzxRJOse/369TAYDPjoo4+aZP1LliyBwWDA/v37m2T9DTFv3jz06NEDoVBI61A098QTT8BgMDTqOv1+Pzp06IBXXnmlUddLpybWg02jqevBIUOGYMiQIU2y7pbk1ltvRefOnRt1nRUVFUhLS8NXX33VqOvVCpPVZiZXXJGftm3b4pJLLsHXX3+tdXhxo7q6GsnJyTAYDNi1a1e9lrXZbHj22Wcxa9YsGI38FWgKkiRhxowZePrpp+HxeLQOh3SG9WDDKI9dWloaevXqhaeeegoul6tO62A92LJZrVbccccdePTRR7UOpVGwhGpkzpw5ePfdd/HOO+/gwQcfxPHjx3HllVfiyy+/1Dq0RnPzzTfD7XajU6dOjb7uDz/8EAaDATk5OVi2bFm9ln377bcRCAQwduzYRo8rHj3yyCNwu92Nvt7bbrsN5eXleP/99xt93dQysB784y6//HK8++67ePfdd/HPf/4T5557Lh599FGMHz++TsuzHtSPN954AwUFBY2+3rvuugvbtm3D2rVrG33dzc2kdQCnquHDh6N///7hnydMmIDs7Gz861//wogRIzSMrPEkJCQgISGhSdb93nvv4corr0SnTp3w/vvv46mnnqrzsosXL8bVV1+N5OTkE84XCAQQCoWQmJjY0HB1zWQywWRqnKpACAGPx4OUlBRkZmbiiiuuwJIlS3D77bc3yvqpZWE9+Md1794df/3rX8M/33XXXfD5fPjkk0/g8XhOWr/VtR6kpidJUqOtKxQKwefzITk5GT179sRZZ52FJUuW4NJLL220bWiBLas6kZmZiZSUlDolDb/88guGDx8Oi8WC9PR0XHbZZfjxxx9rzVddXY377rsPnTt3RlJSEtq3b49bbrkF5eXlMdft9XoxYsQIZGRk4IcffgAA2O12TJ8+Pbyetm3b4vLLL8e2bdtOGKdaX63OnTtjxIgR2LhxI84//3wkJyeja9eueOedd06637KDBw/iu+++w5gxYzBmzBgUFxeHYz2Z4uJibN++HUOHDo2avn//fhgMBvzjH//AggULcPrppyMpKQk7d+4EAKxduxaDBw9GWloaMjMzcc0110R1P9i+fTsMBgM+//zz8LStW7fCYDDgvPPOi9rW8OHDMWDAgJPGajAYMGXKFHz44Yfo1asXUlJSMHDgQOzYsQMA8Nprr+GMM85AcnIyhgwZUqtP3HfffYcbbrgBHTt2RFJSEjp06ID77ruvViuqWp/VQCCAv/3tb+Hj0LlzZzz88MPwer1R88nnc+XKlejfvz9SUlLw2muvhb+//PLLsXHjRlRWVp50f4lYD9a9HlSTk5MDg8Fw0uMXqx4Efk92XnjhBZx99tlITk5GmzZt8Je//AU///xzeJ661g9Ksfrvyv2G169fH542ZMgQnHXWWdi+fTsuvvhipKam4owzzgj3Lf72228xYMAApKSk4Mwzz8Tq1auj1inXa0VFRbj11luRmZmJjIwM3HbbbXXqKhF5TXj55ZfRtWtXpKam4oorrsChQ4cghMDf/vY3tG/fHikpKbjmmmtq1XOfffYZrrrqKuTm5iIpKQmnn346/va3vyEYDEbNp9Zn1el0YubMmejQoQOSkpJw5pln4h//+AeEEFHzydeJZcuWoXfv3khKSsKKFSvC319++eX44osvai0Xb9iyqpGamhqUl5dDCIGysjIsXLgQDocj6i9lNfn5+Rg8eDAsFgsefPBBSJKE1157DUOGDAn/8gKAw+HA4MGDsWvXLtx+++0477zzUF5ejs8//xyHDx9G69ata63b7Xbjmmuuwc8//4zVq1fjT3/6E4Df/2L/6KOPMGXKFPTq1QsVFRXYuHEjdu3aVSsRq4uioiJcf/31mDBhAsaPH4+3334bt956K/r164fevXufdPl//etfSEtLw4gRI5CSkoLTTz8dy5Ytw6BBg066rHzhiRX34sWL4fF4MGnSJCQlJSErKwurV6/G8OHD0bVrVzzxxBNwu91YuHAhLrjgAmzbtg2dO3fGWWedhczMTGzYsAFXX301gN+TRaPRiN9++w02mw0WiwWhUAg//PADJk2aVKdj9d133+Hzzz/H5MmTAQBz587FiBEj8OCDD+KVV17BPffcg6qqKsybNw+333571O2eDz/8EC6XC3fffTesVit++uknLFy4EIcPH8aHH354wu3ecccdWLp0Ka6//nrMnDkTmzdvxty5c7Fr1y4sX748at6CggKMHTsWd955JyZOnIgzzzwz/F2/fv0ghMAPP/zQYlrKqPGwHvzj9aDH4wkn3E6nE99//z2WLl2Km2666aTJ6onqwQkTJmDJkiUYPnw47rjjDgQCAXz33Xf48ccfw63g9akfGqKqqgojRozAmDFjcMMNN2DRokUYM2YMli1bhunTp+Ouu+7CTTfdhPnz5+P666/HoUOHYDabo9Zx4403okuXLpg7dy62bduGN998E23btsWzzz5bpxiWLVsGn8+He++9F5WVlZg3bx5uvPFGXHrppVi/fj1mzZqFoqIiLFy4EPfffz/efvvt8LJLlixBeno6ZsyYgfT0dKxduxaPPfYYbDYb5s+fH3ObQghcffXVWLduHSZMmIC+ffti5cqVeOCBB3DkyBE8//zzUfOvXbsW//nPfzBlyhS0bt06KvHt168fnn/+eeTn5+Oss86q0z7rkqBmtXjxYgGg1icpKUksWbKk1vwAxOOPPx7+edSoUSIxMVHs3bs3PO3o0aPCbDaLiy66KDztscceEwDEJ598UmudoVBICCHEunXrBADx4YcfCrvdLi6++GLRunVr8csvv0TNn5GRISZPnvyH97W4uDg8rVOnTgKA2LBhQ3haWVmZSEpKEjNnzqzTes8++2wxbty48M8PP/ywaN26tfD7/Sdd9pFHHhEAhN1uj5peXFwsAAiLxSLKysqivuvbt69o27atqKioCE/77bffhNFoFLfcckt42lVXXSXOP//88M/XXXeduO6660RCQoL4+uuvhRBCbNu2TQAQn3322UljlctF5PF77bXXBACRk5MjbDZbePrs2bNrHWuXy1VrnXPnzhUGg0EcOHAgPO3xxx8XkVXBr7/+KgCIO+64I2rZ+++/XwAQa9euDU+Tz+eKFStU9+Ho0aMCgHj22WdPur906mA92LB6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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "all_indices = torch.meshgrid(torch.arange(128), torch.arange(128), indexing=\"ij\")\n", + "offsets_a = nvmath.linalg.advanced.helpers.matmul.get_mxfp8_scale_offset(a, all_indices)\n", + "offsets_b = nvmath.linalg.advanced.helpers.matmul.get_mxfp8_scale_offset(b, all_indices)\n", + "\n", + "fig, ax = plt.subplots(1, 2, figsize=(8, 4))\n", + "ax[0].imshow(offsets_a * 10 % 93, cmap=\"gray\")\n", + "ax[1].imshow(offsets_b * 10 % 93, cmap=\"gray\")\n", + "ax[0].set_title(\"Blocks in A (row-major)\")\n", + "ax[1].set_title(\"Blocks in B (column-major)\");" + ] + }, + { + "cell_type": "markdown", + "id": "53917462-7d60-4370-b6c0-4d90a2c27d45", + "metadata": {}, + "source": [ + "For more details about the MXFP8 scales layout, please visit the cuBLASLt documentation." + ] + }, + { + "cell_type": "markdown", + "id": "6686d5f8-701e-4cd4-a6ab-170b761e62c4", + "metadata": {}, + "source": [ + "## Learning more\n", + "- For more examples of FP8 and MXFP8 usage, see the [examples on our GitHub](https://github.com/NVIDIA/nvmath-python/tree/main/examples/linalg/advanced/matmul).\n", + "- For more details on narrow-precision types, visit [cuBLASLt documentation](https://docs.nvidia.com/cuda/cublas/#narrow-precision-data-types-usage)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/nvmath/__init__.py b/nvmath/__init__.py index 5755be5..c508d69 100644 --- a/nvmath/__init__.py +++ b/nvmath/__init__.py @@ -1,14 +1,16 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 +import importlib.metadata + from nvmath import bindings from nvmath._utils import ComputeType from nvmath._utils import CudaDataType from nvmath._utils import LibraryPropertyType from nvmath import fft, linalg -from nvmath.memory import * +from nvmath.memory import BaseCUDAMemoryManager, MemoryPointer # Attempt to preload libraries. Fail silently if preload fails. @@ -19,3 +21,16 @@ def _force_lib_load(): _force_lib_load() + +__all__ = [ + "BaseCUDAMemoryManager", + "bindings", + "ComputeType", + "CudaDataType", + "fft", + "LibraryPropertyType", + "linalg", + "MemoryPointer", +] + +__version__ = importlib.metadata.version("nvmath-python") diff --git a/nvmath/_internal/enum_utils.py b/nvmath/_internal/enum_utils.py index 03a9eee..54f4335 100644 --- a/nvmath/_internal/enum_utils.py +++ b/nvmath/_internal/enum_utils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -10,7 +10,7 @@ import dataclasses from enum import IntEnum import re -from typing import Any, ClassVar, Optional +from typing import Any, ClassVar from collections.abc import Callable import numpy diff --git a/nvmath/_internal/formatters.py b/nvmath/_internal/formatters.py index f1b785a..d1bafde 100644 --- a/nvmath/_internal/formatters.py +++ b/nvmath/_internal/formatters.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/_internal/layout.py b/nvmath/_internal/layout.py index fa497ef..2e5d6c1 100644 --- a/nvmath/_internal/layout.py +++ b/nvmath/_internal/layout.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/_internal/mem_limit.py b/nvmath/_internal/mem_limit.py index 69d6b5b..609a874 100644 --- a/nvmath/_internal/mem_limit.py +++ b/nvmath/_internal/mem_limit.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/_internal/package_ifc.py b/nvmath/_internal/package_ifc.py index 855af2b..ed9d76a 100644 --- a/nvmath/_internal/package_ifc.py +++ b/nvmath/_internal/package_ifc.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/_internal/package_ifc_cupy.py b/nvmath/_internal/package_ifc_cupy.py index 328d602..92773c6 100644 --- a/nvmath/_internal/package_ifc_cupy.py +++ b/nvmath/_internal/package_ifc_cupy.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/_internal/package_ifc_torch.py b/nvmath/_internal/package_ifc_torch.py index 0b704ec..251f281 100644 --- a/nvmath/_internal/package_ifc_torch.py +++ b/nvmath/_internal/package_ifc_torch.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/_internal/package_wrapper.py b/nvmath/_internal/package_wrapper.py index af22414..986b7f5 100644 --- a/nvmath/_internal/package_wrapper.py +++ b/nvmath/_internal/package_wrapper.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -13,7 +13,7 @@ PACKAGE: dict[str, type[Package]] = {"cupy": CupyPackage} try: - import torch + import torch # noqa: F401 from .package_ifc_torch import TorchPackage PACKAGE["torch"] = TorchPackage diff --git a/nvmath/_internal/tensor_ifc.py b/nvmath/_internal/tensor_ifc.py index 767e7a4..d797ff8 100644 --- a/nvmath/_internal/tensor_ifc.py +++ b/nvmath/_internal/tensor_ifc.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -57,6 +57,11 @@ def numpy(self, stream_holder): def shape(self) -> Sequence[int]: raise NotImplementedError + @property + @abstractmethod + def size(self) -> int: + raise NotImplementedError + @property @abstractmethod def strides(self) -> Sequence[int]: diff --git a/nvmath/_internal/tensor_ifc_cupy.py b/nvmath/_internal/tensor_ifc_cupy.py index c8a8f98..84fc212 100644 --- a/nvmath/_internal/tensor_ifc_cupy.py +++ b/nvmath/_internal/tensor_ifc_cupy.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -53,6 +53,10 @@ def dtype(self): def shape(self): return tuple(self.tensor.shape) + @property + def size(self): + return self.tensor.size + @property def strides(self): return tuple(stride_in_bytes // self.tensor.itemsize for stride_in_bytes in self.tensor.strides) diff --git a/nvmath/_internal/tensor_ifc_numpy.py b/nvmath/_internal/tensor_ifc_numpy.py index c945234..6808204 100644 --- a/nvmath/_internal/tensor_ifc_numpy.py +++ b/nvmath/_internal/tensor_ifc_numpy.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -52,6 +52,10 @@ def dtype(self): def shape(self): return tuple(self.tensor.shape) + @property + def size(self): + return self.tensor.size + @property def strides(self): return tuple(stride_in_bytes // self.tensor.itemsize for stride_in_bytes in self.tensor.strides) diff --git a/nvmath/_internal/tensor_ifc_torch.py b/nvmath/_internal/tensor_ifc_torch.py index 23f8119..894a7ad 100644 --- a/nvmath/_internal/tensor_ifc_torch.py +++ b/nvmath/_internal/tensor_ifc_torch.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -51,6 +51,10 @@ def dtype(self): def shape(self): return tuple(self.tensor.shape) + @property + def size(self): + return self.tensor.nelement() + @property def strides(self): return self.tensor.stride() diff --git a/nvmath/_internal/tensor_wrapper.py b/nvmath/_internal/tensor_wrapper.py index 58367ab..e5e5945 100644 --- a/nvmath/_internal/tensor_wrapper.py +++ b/nvmath/_internal/tensor_wrapper.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/_internal/typemaps.py b/nvmath/_internal/typemaps.py index 268b591..1ffb45a 100644 --- a/nvmath/_internal/typemaps.py +++ b/nvmath/_internal/typemaps.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -6,7 +6,7 @@ Functions to link type names with CUDA data and compute types. """ -__all__ = ["COMPUTE_TYPE_TO_NAME", "DATA_TYPE_TO_NAME", "NAME_TO_DATA_TYPE", "NAME_TO_COMPUTE_TYPE"] +__all__ = ["COMPUTE_TYPE_TO_NAME", "DATA_TYPE_TO_NAME", "NAME_TO_DATA_TYPE", "NAME_TO_COMPUTE_TYPE", "NAME_TO_DATA_WIDTH"] from enum import IntEnum import re @@ -58,19 +58,25 @@ class cudaDataType(IntEnum): CUDA_C_64I = 25 CUDA_R_64U = 26 CUDA_C_64U = 27 + CUDA_R_8F_E4M3 = 28 + CUDA_R_8F_E5M2 = 29 def create_cuda_data_type_map(cuda_data_type_enum_class): """ Map the data type name to the corresponding CUDA data type. """ - cuda_data_type_pattern = re.compile(r"CUDA_(?PC|R)_(?P\d+)(?PF|I|U|BF)") + cuda_data_type_pattern = re.compile(r"CUDA_(?PC|R)_(?P\d+)(?PF|I|U|BF)_?(?P(E\dM\d)?)") type_code_map = {"i": "int", "u": "uint", "f": "float", "bf": "bfloat"} + # A map from (width, exponent kind) to qualifiers (finite, unsigned zero, ...) for data + # types. + type_qualifier_map = {(8, "e4m3"): "fn"} complex_types = {"float": "complex", "bfloat": "bcomplex"} cuda_data_type_map = dict() + data_type_width_map = dict() for d in cuda_data_type_enum_class: m = cuda_data_type_pattern.match(d.name) @@ -87,9 +93,19 @@ def create_cuda_data_type_map(cuda_data_type_enum_class): type_code = complex_types[type_code] name = type_code + str(width) + + # Handle narrow type kinds. + if width <= 8: + kind = m.group("kind").lower() + # Handle type qualifiers for narrow types. + kind += type_qualifier_map.get((width, kind), "") + if kind: + name += "_" + kind + cuda_data_type_map[name] = d + data_type_width_map[name] = width - return cuda_data_type_map + return cuda_data_type_map, data_type_width_map def create_cuda_compute_type_map(cuda_compute_type_enum_class): @@ -125,7 +141,7 @@ def create_cuda_compute_type_map(cuda_compute_type_enum_class): return cuda_compute_type_map -NAME_TO_DATA_TYPE = create_cuda_data_type_map(cudaDataType) +NAME_TO_DATA_TYPE, NAME_TO_DATA_WIDTH = create_cuda_data_type_map(cudaDataType) DATA_TYPE_TO_NAME = {v: k for k, v in NAME_TO_DATA_TYPE.items()} NAME_TO_COMPUTE_TYPE = create_cuda_compute_type_map(ComputeType) COMPUTE_TYPE_TO_NAME = {v: k for k, v in NAME_TO_COMPUTE_TYPE.items()} diff --git a/nvmath/_internal/utils.py b/nvmath/_internal/utils.py index 59ee5d0..560ac98 100644 --- a/nvmath/_internal/utils.py +++ b/nvmath/_internal/utils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -22,7 +22,6 @@ from . import formatters from . import mem_limit from . import package_wrapper -from . import tensor_wrapper from .package_ifc import StreamHolder from .tensor_ifc import Tensor from .layout import is_contiguous_and_dense @@ -236,7 +235,6 @@ def get_memory_limit(memory_limit, device): """ Parse user provided memory limit and return the memory limit in bytes. """ - import re _, total_memory = device.mem_info if isinstance(memory_limit, int): diff --git a/nvmath/_utils.py b/nvmath/_utils.py index 8c0531b..db56760 100644 --- a/nvmath/_utils.py +++ b/nvmath/_utils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/bindings/__init__.py b/nvmath/bindings/__init__.py index defbe92..6988162 100644 --- a/nvmath/bindings/__init__.py +++ b/nvmath/bindings/__init__.py @@ -1,17 +1,27 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # type: ignore from nvmath.bindings import cublas +from nvmath.bindings import cufft +from nvmath.bindings import curand from nvmath.bindings import cusolver from nvmath.bindings import cusolverDn -from nvmath.bindings import cufft from nvmath.bindings import cusparse -from nvmath.bindings import curand try: from nvmath.bindings import nvpl except ImportError: nvpl = None + +__all__ = [ + "cublas", + "cufft", + "curand", + "cusolver", + "cusolverDn", + "cusparse", + "nvpl", +] diff --git a/nvmath/bindings/_internal/cublas.pxd b/nvmath/bindings/_internal/cublas.pxd index 00d1cf0..f589f58 100644 --- a/nvmath/bindings/_internal/cublas.pxd +++ b/nvmath/bindings/_internal/cublas.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from ..cycublas cimport * diff --git a/nvmath/bindings/_internal/cublasLt.pxd b/nvmath/bindings/_internal/cublasLt.pxd index 6a2fcb1..3e4859f 100644 --- a/nvmath/bindings/_internal/cublasLt.pxd +++ b/nvmath/bindings/_internal/cublasLt.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from ..cycublasLt cimport * diff --git a/nvmath/bindings/_internal/cublasLt_linux.pyx b/nvmath/bindings/_internal/cublasLt_linux.pyx index 19cc443..8e69249 100644 --- a/nvmath/bindings/_internal/cublasLt_linux.pyx +++ b/nvmath/bindings/_internal/cublasLt_linux.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libc.stdint cimport intptr_t diff --git a/nvmath/bindings/_internal/cublasLt_windows.pyx b/nvmath/bindings/_internal/cublasLt_windows.pyx index 95f2182..3db7c89 100644 --- a/nvmath/bindings/_internal/cublasLt_windows.pyx +++ b/nvmath/bindings/_internal/cublasLt_windows.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libc.stdint cimport intptr_t diff --git a/nvmath/bindings/_internal/cublas_linux.pyx b/nvmath/bindings/_internal/cublas_linux.pyx index 1bf35f4..d4101ea 100644 --- a/nvmath/bindings/_internal/cublas_linux.pyx +++ b/nvmath/bindings/_internal/cublas_linux.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libc.stdint cimport intptr_t diff --git a/nvmath/bindings/_internal/cublas_windows.pyx b/nvmath/bindings/_internal/cublas_windows.pyx index 46e2a23..722d2ea 100644 --- a/nvmath/bindings/_internal/cublas_windows.pyx +++ b/nvmath/bindings/_internal/cublas_windows.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libc.stdint cimport intptr_t diff --git a/nvmath/bindings/_internal/cufft.pxd b/nvmath/bindings/_internal/cufft.pxd index c3f319d..8d23c3d 100644 --- a/nvmath/bindings/_internal/cufft.pxd +++ b/nvmath/bindings/_internal/cufft.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from ..cycufft cimport * diff --git a/nvmath/bindings/_internal/cufft_linux.pyx b/nvmath/bindings/_internal/cufft_linux.pyx index 7d358a9..8fe8719 100644 --- a/nvmath/bindings/_internal/cufft_linux.pyx +++ b/nvmath/bindings/_internal/cufft_linux.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libc.stdint cimport intptr_t diff --git a/nvmath/bindings/_internal/cufft_windows.pyx b/nvmath/bindings/_internal/cufft_windows.pyx index d732860..2485629 100644 --- a/nvmath/bindings/_internal/cufft_windows.pyx +++ b/nvmath/bindings/_internal/cufft_windows.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libc.stdint cimport intptr_t diff --git a/nvmath/bindings/_internal/curand.pxd b/nvmath/bindings/_internal/curand.pxd index 6a085d0..2542cb2 100644 --- a/nvmath/bindings/_internal/curand.pxd +++ b/nvmath/bindings/_internal/curand.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from ..cycurand cimport * diff --git a/nvmath/bindings/_internal/curand_linux.pyx b/nvmath/bindings/_internal/curand_linux.pyx index d3d215c..d94b22c 100644 --- a/nvmath/bindings/_internal/curand_linux.pyx +++ b/nvmath/bindings/_internal/curand_linux.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libc.stdint cimport intptr_t diff --git a/nvmath/bindings/_internal/curand_windows.pyx b/nvmath/bindings/_internal/curand_windows.pyx index ba73a8b..daeea75 100644 --- a/nvmath/bindings/_internal/curand_windows.pyx +++ b/nvmath/bindings/_internal/curand_windows.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libc.stdint cimport intptr_t diff --git a/nvmath/bindings/_internal/cusolver.pxd b/nvmath/bindings/_internal/cusolver.pxd index 9ee36b3..3aee07a 100644 --- a/nvmath/bindings/_internal/cusolver.pxd +++ b/nvmath/bindings/_internal/cusolver.pxd @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/nvmath/bindings/_internal/cusolverDn.pxd b/nvmath/bindings/_internal/cusolverDn.pxd index 49fe8ca..f05b99a 100644 --- a/nvmath/bindings/_internal/cusolverDn.pxd +++ b/nvmath/bindings/_internal/cusolverDn.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from ..cycusolverDn cimport * diff --git a/nvmath/bindings/_internal/cusolverDn_linux.pyx b/nvmath/bindings/_internal/cusolverDn_linux.pyx index c99ca26..4fa178c 100644 --- a/nvmath/bindings/_internal/cusolverDn_linux.pyx +++ b/nvmath/bindings/_internal/cusolverDn_linux.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libc.stdint cimport intptr_t diff --git a/nvmath/bindings/_internal/cusolverDn_windows.pyx b/nvmath/bindings/_internal/cusolverDn_windows.pyx index f705fa6..5ece197 100644 --- a/nvmath/bindings/_internal/cusolverDn_windows.pyx +++ b/nvmath/bindings/_internal/cusolverDn_windows.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libc.stdint cimport intptr_t diff --git a/nvmath/bindings/_internal/cusolver_linux.pyx b/nvmath/bindings/_internal/cusolver_linux.pyx index 87ef093..ea07066 100644 --- a/nvmath/bindings/_internal/cusolver_linux.pyx +++ b/nvmath/bindings/_internal/cusolver_linux.pyx @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/nvmath/bindings/_internal/cusolver_windows.pyx b/nvmath/bindings/_internal/cusolver_windows.pyx index 07c5cfd..580d6c8 100644 --- a/nvmath/bindings/_internal/cusolver_windows.pyx +++ b/nvmath/bindings/_internal/cusolver_windows.pyx @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/nvmath/bindings/_internal/cusparse.pxd b/nvmath/bindings/_internal/cusparse.pxd index 8f8025a..7669424 100644 --- a/nvmath/bindings/_internal/cusparse.pxd +++ b/nvmath/bindings/_internal/cusparse.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from ..cycusparse cimport * diff --git a/nvmath/bindings/_internal/cusparse_linux.pyx b/nvmath/bindings/_internal/cusparse_linux.pyx index 41e25c8..831a2ef 100644 --- a/nvmath/bindings/_internal/cusparse_linux.pyx +++ b/nvmath/bindings/_internal/cusparse_linux.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libc.stdint cimport intptr_t diff --git a/nvmath/bindings/_internal/cusparse_windows.pyx b/nvmath/bindings/_internal/cusparse_windows.pyx index 2601c03..44a4ab6 100644 --- a/nvmath/bindings/_internal/cusparse_windows.pyx +++ b/nvmath/bindings/_internal/cusparse_windows.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libc.stdint cimport intptr_t diff --git a/nvmath/bindings/_internal/utils.pxd b/nvmath/bindings/_internal/utils.pxd index e3b82e9..546f268 100644 --- a/nvmath/bindings/_internal/utils.pxd +++ b/nvmath/bindings/_internal/utils.pxd @@ -1,3 +1,7 @@ +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + from libc.stdint cimport int32_t, int64_t, intptr_t from libcpp.vector cimport vector from libcpp cimport bool as cppbool diff --git a/nvmath/bindings/_internal/utils.pyx b/nvmath/bindings/_internal/utils.pyx index b69e2e6..c5b4761 100644 --- a/nvmath/bindings/_internal/utils.pyx +++ b/nvmath/bindings/_internal/utils.pyx @@ -1,3 +1,7 @@ +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + cimport cpython from libc.stdint cimport intptr_t from libcpp.utility cimport move diff --git a/nvmath/bindings/cublas.pxd b/nvmath/bindings/cublas.pxd index 906294d..f96952a 100644 --- a/nvmath/bindings/cublas.pxd +++ b/nvmath/bindings/cublas.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libc.stdint cimport intptr_t diff --git a/nvmath/bindings/cublas.pyx b/nvmath/bindings/cublas.pyx index 726a2eb..2654e38 100644 --- a/nvmath/bindings/cublas.pyx +++ b/nvmath/bindings/cublas.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. cimport cython # NOQA from libcpp.vector cimport vector diff --git a/nvmath/bindings/cublasLt.pxd b/nvmath/bindings/cublasLt.pxd index f1e47e2..00c79f6 100644 --- a/nvmath/bindings/cublasLt.pxd +++ b/nvmath/bindings/cublasLt.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libc.stdint cimport intptr_t @@ -45,6 +45,7 @@ ctypedef cublasLtMatmulAlgoCapAttributes_t _MatmulAlgoCapAttribute ctypedef cublasLtMatmulAlgoConfigAttributes_t _MatmulAlgoConfigAttribute ctypedef cublasLtClusterShape_t _ClusterShape ctypedef cublasLtMatmulInnerShape_t _MatmulInnerShape +ctypedef cublasLtMatmulMatrixScale_t _MatmulMatrixScale ############################################################################### diff --git a/nvmath/bindings/cublasLt.pyx b/nvmath/bindings/cublasLt.pyx index 2f5f392..98f6c98 100644 --- a/nvmath/bindings/cublasLt.pyx +++ b/nvmath/bindings/cublasLt.pyx @@ -1,13 +1,13 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. cimport cython # NOQA from libcpp.vector cimport vector from cpython cimport buffer as _buffer -from cpython cimport memoryview as _memoryview +from cpython.memoryview cimport PyMemoryView_FromMemory from enum import IntEnum as _IntEnum @@ -128,7 +128,7 @@ cdef class MatmulAlgo: raise ValueError("ptr must not be null (0)") cdef MatmulAlgo obj = MatmulAlgo.__new__(MatmulAlgo) cdef flag = _buffer.PyBUF_READ if readonly else _buffer.PyBUF_WRITE - cdef object buf = _memoryview.PyMemoryView_FromMemory( + cdef object buf = PyMemoryView_FromMemory( ptr, sizeof(cublasLtMatmulAlgo_t) * size, flag) data = _numpy.ndarray((size,), buffer=buf, dtype=matmul_algo_dtype) @@ -283,7 +283,7 @@ cdef class MatmulHeuristicResult: raise ValueError("ptr must not be null (0)") cdef MatmulHeuristicResult obj = MatmulHeuristicResult.__new__(MatmulHeuristicResult) cdef flag = _buffer.PyBUF_READ if readonly else _buffer.PyBUF_WRITE - cdef object buf = _memoryview.PyMemoryView_FromMemory( + cdef object buf = PyMemoryView_FromMemory( ptr, sizeof(cublasLtMatmulHeuristicResult_t) * size, flag) data = _numpy.ndarray((size,), buffer=buf, dtype=matmul_heuristic_result_dtype) @@ -929,6 +929,10 @@ class MatmulTile(_IntEnum): TILE_768x56 = CUBLASLT_MATMUL_TILE_768x56 TILE_768x72 = CUBLASLT_MATMUL_TILE_768x72 TILE_768x80 = CUBLASLT_MATMUL_TILE_768x80 + TILE_256x512 = CUBLASLT_MATMUL_TILE_256x512 + TILE_256x1024 = CUBLASLT_MATMUL_TILE_256x1024 + TILE_512x512 = CUBLASLT_MATMUL_TILE_512x512 + TILE_512x1024 = CUBLASLT_MATMUL_TILE_512x1024 class MatmulStages(_IntEnum): """See `cublasLtMatmulStages_t`.""" @@ -967,6 +971,7 @@ class MatmulStages(_IntEnum): STAGES_32xAUTO = CUBLASLT_MATMUL_STAGES_32xAUTO STAGES_64xAUTO = CUBLASLT_MATMUL_STAGES_64xAUTO STAGES_128xAUTO = CUBLASLT_MATMUL_STAGES_128xAUTO + STAGES_256xAUTO = CUBLASLT_MATMUL_STAGES_256xAUTO STAGES_16x80 = CUBLASLT_MATMUL_STAGES_16x80 STAGES_64x80 = CUBLASLT_MATMUL_STAGES_64x80 @@ -1037,6 +1042,13 @@ class MatmulDescAttribute(_IntEnum): ATOMIC_SYNC_NUM_CHUNKS_D_COLS = CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_NUM_CHUNKS_D_COLS ATOMIC_SYNC_IN_COUNTERS_POINTER = CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_IN_COUNTERS_POINTER ATOMIC_SYNC_OUT_COUNTERS_POINTER = CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_OUT_COUNTERS_POINTER + A_SCALE_MODE = CUBLASLT_MATMUL_DESC_A_SCALE_MODE + B_SCALE_MODE = CUBLASLT_MATMUL_DESC_B_SCALE_MODE + C_SCALE_MODE = CUBLASLT_MATMUL_DESC_C_SCALE_MODE + D_SCALE_MODE = CUBLASLT_MATMUL_DESC_D_SCALE_MODE + EPILOGUE_AUX_SCALE_MODE = CUBLASLT_MATMUL_DESC_EPILOGUE_AUX_SCALE_MODE + D_OUT_SCALE_POINTER = CUBLASLT_MATMUL_DESC_D_OUT_SCALE_POINTER + D_OUT_SCALE_MODE = CUBLASLT_MATMUL_DESC_D_OUT_SCALE_MODE class MatrixTransformDescAttribute(_IntEnum): """See `cublasLtMatrixTransformDescAttributes_t`.""" @@ -1200,6 +1212,12 @@ class MatmulInnerShape(_IntEnum): MMA1688 = CUBLASLT_MATMUL_INNER_SHAPE_MMA1688 MMA16816 = CUBLASLT_MATMUL_INNER_SHAPE_MMA16816 +class MatmulMatrixScale(_IntEnum): + """See `cublasLtMatmulMatrixScale_t`.""" + SCALAR_32F = CUBLASLT_MATMUL_MATRIX_SCALE_SCALAR_32F + VEC16_UE4M3 = CUBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE4M3 + VEC32_UE8M0 = CUBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE8M0 + ############################################################################### # Error handling @@ -1393,6 +1411,13 @@ cdef dict matmul_desc_attribute_sizes = { CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_NUM_CHUNKS_D_COLS: _numpy.int32, CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_IN_COUNTERS_POINTER: _numpy.int32, CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_OUT_COUNTERS_POINTER: _numpy.int32, + CUBLASLT_MATMUL_DESC_A_SCALE_MODE: _numpy.int32, + CUBLASLT_MATMUL_DESC_B_SCALE_MODE: _numpy.int32, + CUBLASLT_MATMUL_DESC_C_SCALE_MODE: _numpy.int32, + CUBLASLT_MATMUL_DESC_D_SCALE_MODE: _numpy.int32, + CUBLASLT_MATMUL_DESC_EPILOGUE_AUX_SCALE_MODE: _numpy.int32, + CUBLASLT_MATMUL_DESC_D_OUT_SCALE_POINTER: _numpy.intp, + CUBLASLT_MATMUL_DESC_D_OUT_SCALE_MODE: _numpy.int32, } cpdef get_matmul_desc_attribute_dtype(int attr): diff --git a/nvmath/bindings/cufft.pxd b/nvmath/bindings/cufft.pxd index e24af44..fbe9281 100644 --- a/nvmath/bindings/cufft.pxd +++ b/nvmath/bindings/cufft.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libc.stdint cimport intptr_t @@ -18,18 +18,18 @@ ctypedef cufftCallbackLoadC CallbackLoadC ctypedef cufftCallbackLoadZ CallbackLoadZ ctypedef cufftCallbackLoadR CallbackLoadR ctypedef cufftCallbackLoadD CallbackLoadD -ctypedef cufftCallbackStoreC CallbackStoreC -ctypedef cufftCallbackStoreZ CallbackStoreZ -ctypedef cufftCallbackStoreR CallbackStoreR -ctypedef cufftCallbackStoreD CallbackStoreD ctypedef cufftJITCallbackLoadC JITCallbackLoadC ctypedef cufftJITCallbackLoadZ JITCallbackLoadZ ctypedef cufftJITCallbackLoadR JITCallbackLoadR ctypedef cufftJITCallbackLoadD JITCallbackLoadD -ctypedef cufftJITCallbackStoreC JITCallbackStoreC -ctypedef cufftJITCallbackStoreZ JITCallbackStoreZ +ctypedef cufftCallbackStoreR CallbackStoreR ctypedef cufftJITCallbackStoreR JITCallbackStoreR +ctypedef cufftCallbackStoreD CallbackStoreD ctypedef cufftJITCallbackStoreD JITCallbackStoreD +ctypedef cufftCallbackStoreC CallbackStoreC +ctypedef cufftJITCallbackStoreC JITCallbackStoreC +ctypedef cufftCallbackStoreZ CallbackStoreZ +ctypedef cufftJITCallbackStoreZ JITCallbackStoreZ ctypedef cudaStream_t Stream ctypedef cudaDataType DataType diff --git a/nvmath/bindings/cufft.pyx b/nvmath/bindings/cufft.pyx index 884bc21..16d9f38 100644 --- a/nvmath/bindings/cufft.pyx +++ b/nvmath/bindings/cufft.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. cimport cython # NOQA from libc.stdint cimport int64_t diff --git a/nvmath/bindings/curand.pxd b/nvmath/bindings/curand.pxd index a22451c..673162d 100644 --- a/nvmath/bindings/curand.pxd +++ b/nvmath/bindings/curand.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. cimport cython diff --git a/nvmath/bindings/curand.pyx b/nvmath/bindings/curand.pyx index 5d22a02..caf9520 100644 --- a/nvmath/bindings/curand.pyx +++ b/nvmath/bindings/curand.pyx @@ -1,12 +1,12 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. cimport cython # NOQA cimport cpython -from cpython cimport memoryview as _memoryview +from cpython.memoryview cimport PyMemoryView_FromMemory from enum import IntEnum as _IntEnum @@ -479,7 +479,7 @@ cpdef get_scramble_constants32(size_t size): with nogil: status = curandGetScrambleConstants32(&constants) check_status(status) - cdef object buf = _memoryview.PyMemoryView_FromMemory( + cdef object buf = PyMemoryView_FromMemory( constants, size * sizeof(unsigned int), cpython.PyBUF_READ) return _numpy.ndarray((size,), buffer=buf, dtype=_numpy.uint32) @@ -501,7 +501,7 @@ cpdef get_scramble_constants64(size_t size): with nogil: status = curandGetScrambleConstants64(&constants) check_status(status) - cdef object buf = _memoryview.PyMemoryView_FromMemory( + cdef object buf = PyMemoryView_FromMemory( constants, size * sizeof(unsigned long long), cpython.PyBUF_READ) return _numpy.ndarray((size,), buffer=buf, dtype=_numpy.uint64) @@ -524,7 +524,7 @@ cpdef get_direction_vectors32(int set_, size_t size): with nogil: status = curandGetDirectionVectors32(&vec, <_DirectionVectorSet>set_) check_status(status) - cdef object buf = _memoryview.PyMemoryView_FromMemory( + cdef object buf = PyMemoryView_FromMemory( vec, size * sizeof(curandDirectionVectors32_t), cpython.PyBUF_READ) return _numpy.ndarray((size, 32,), buffer=buf, dtype=_numpy.uint32) @@ -547,6 +547,6 @@ cpdef get_direction_vectors64(int set_, size_t size): with nogil: status = curandGetDirectionVectors64(&vec, <_DirectionVectorSet>set_) check_status(status) - cdef object buf = _memoryview.PyMemoryView_FromMemory( + cdef object buf = PyMemoryView_FromMemory( vec, size * sizeof(curandDirectionVectors64_t), cpython.PyBUF_READ) return _numpy.ndarray((size, 64,), buffer=buf, dtype=_numpy.uint64) diff --git a/nvmath/bindings/cusolver.pxd b/nvmath/bindings/cusolver.pxd index 51940da..7a89543 100644 --- a/nvmath/bindings/cusolver.pxd +++ b/nvmath/bindings/cusolver.pxd @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/nvmath/bindings/cusolver.pyx b/nvmath/bindings/cusolver.pyx index 30f5e3e..cd42b2f 100644 --- a/nvmath/bindings/cusolver.pyx +++ b/nvmath/bindings/cusolver.pyx @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/nvmath/bindings/cusolverDn.pxd b/nvmath/bindings/cusolverDn.pxd index fc68205..a17ba82 100644 --- a/nvmath/bindings/cusolverDn.pxd +++ b/nvmath/bindings/cusolverDn.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libc.stdint cimport intptr_t diff --git a/nvmath/bindings/cusolverDn.pyx b/nvmath/bindings/cusolverDn.pyx index 9c1b5f6..4b7c363 100644 --- a/nvmath/bindings/cusolverDn.pyx +++ b/nvmath/bindings/cusolverDn.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from libcpp.vector cimport vector diff --git a/nvmath/bindings/cusparse.pxd b/nvmath/bindings/cusparse.pxd index 3df3661..bb433aa 100644 --- a/nvmath/bindings/cusparse.pxd +++ b/nvmath/bindings/cusparse.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. cimport cython diff --git a/nvmath/bindings/cusparse.pyx b/nvmath/bindings/cusparse.pyx index c66bfbb..f25e9cd 100644 --- a/nvmath/bindings/cusparse.pyx +++ b/nvmath/bindings/cusparse.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. cimport cython # NOQA diff --git a/nvmath/bindings/cycublas.pxd b/nvmath/bindings/cycublas.pxd index cc984a1..bf81dec 100644 --- a/nvmath/bindings/cycublas.pxd +++ b/nvmath/bindings/cycublas.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. # This layer exposes the C header to Cython as-is. from libc.stdint cimport int64_t diff --git a/nvmath/bindings/cycublas.pyx b/nvmath/bindings/cycublas.pyx index 242dae3..19065c4 100644 --- a/nvmath/bindings/cycublas.pyx +++ b/nvmath/bindings/cycublas.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from ._internal cimport cublas as _cublas diff --git a/nvmath/bindings/cycublasLt.pxd b/nvmath/bindings/cycublasLt.pxd index c3879b9..6574006 100644 --- a/nvmath/bindings/cycublasLt.pxd +++ b/nvmath/bindings/cycublasLt.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. # This layer exposes the C header to Cython as-is. from libc.stdint cimport int64_t, uint64_t @@ -648,6 +648,10 @@ ctypedef enum cublasLtMatmulTile_t "cublasLtMatmulTile_t": CUBLASLT_MATMUL_TILE_768x56 "CUBLASLT_MATMUL_TILE_768x56" = 628 CUBLASLT_MATMUL_TILE_768x72 "CUBLASLT_MATMUL_TILE_768x72" = 629 CUBLASLT_MATMUL_TILE_768x80 "CUBLASLT_MATMUL_TILE_768x80" = 630 + CUBLASLT_MATMUL_TILE_256x512 "CUBLASLT_MATMUL_TILE_256x512" = 631 + CUBLASLT_MATMUL_TILE_256x1024 "CUBLASLT_MATMUL_TILE_256x1024" = 632 + CUBLASLT_MATMUL_TILE_512x512 "CUBLASLT_MATMUL_TILE_512x512" = 633 + CUBLASLT_MATMUL_TILE_512x1024 "CUBLASLT_MATMUL_TILE_512x1024" = 634 ctypedef enum cublasLtMatmulStages_t "cublasLtMatmulStages_t": CUBLASLT_MATMUL_STAGES_UNDEFINED "CUBLASLT_MATMUL_STAGES_UNDEFINED" = 0 @@ -685,6 +689,7 @@ ctypedef enum cublasLtMatmulStages_t "cublasLtMatmulStages_t": CUBLASLT_MATMUL_STAGES_32xAUTO "CUBLASLT_MATMUL_STAGES_32xAUTO" = 34 CUBLASLT_MATMUL_STAGES_64xAUTO "CUBLASLT_MATMUL_STAGES_64xAUTO" = 35 CUBLASLT_MATMUL_STAGES_128xAUTO "CUBLASLT_MATMUL_STAGES_128xAUTO" = 36 + CUBLASLT_MATMUL_STAGES_256xAUTO "CUBLASLT_MATMUL_STAGES_256xAUTO" = 37 CUBLASLT_MATMUL_STAGES_16x80 "CUBLASLT_MATMUL_STAGES_16x80" = 29 CUBLASLT_MATMUL_STAGES_64x80 "CUBLASLT_MATMUL_STAGES_64x80" = 30 @@ -750,6 +755,13 @@ ctypedef enum cublasLtMatmulDescAttributes_t "cublasLtMatmulDescAttributes_t": CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_NUM_CHUNKS_D_COLS "CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_NUM_CHUNKS_D_COLS" = 28 CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_IN_COUNTERS_POINTER "CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_IN_COUNTERS_POINTER" = 29 CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_OUT_COUNTERS_POINTER "CUBLASLT_MATMUL_DESC_ATOMIC_SYNC_OUT_COUNTERS_POINTER" = 30 + CUBLASLT_MATMUL_DESC_A_SCALE_MODE "CUBLASLT_MATMUL_DESC_A_SCALE_MODE" = 31 + CUBLASLT_MATMUL_DESC_B_SCALE_MODE "CUBLASLT_MATMUL_DESC_B_SCALE_MODE" = 32 + CUBLASLT_MATMUL_DESC_C_SCALE_MODE "CUBLASLT_MATMUL_DESC_C_SCALE_MODE" = 33 + CUBLASLT_MATMUL_DESC_D_SCALE_MODE "CUBLASLT_MATMUL_DESC_D_SCALE_MODE" = 34 + CUBLASLT_MATMUL_DESC_EPILOGUE_AUX_SCALE_MODE "CUBLASLT_MATMUL_DESC_EPILOGUE_AUX_SCALE_MODE" = 35 + CUBLASLT_MATMUL_DESC_D_OUT_SCALE_POINTER "CUBLASLT_MATMUL_DESC_D_OUT_SCALE_POINTER" = 36 + CUBLASLT_MATMUL_DESC_D_OUT_SCALE_MODE "CUBLASLT_MATMUL_DESC_D_OUT_SCALE_MODE" = 37 ctypedef enum cublasLtMatrixTransformDescAttributes_t "cublasLtMatrixTransformDescAttributes_t": CUBLASLT_MATRIX_TRANSFORM_DESC_SCALE_TYPE "CUBLASLT_MATRIX_TRANSFORM_DESC_SCALE_TYPE" @@ -904,6 +916,11 @@ ctypedef enum cublasLtMatmulInnerShape_t "cublasLtMatmulInnerShape_t": CUBLASLT_MATMUL_INNER_SHAPE_MMA1688 "CUBLASLT_MATMUL_INNER_SHAPE_MMA1688" = 3 CUBLASLT_MATMUL_INNER_SHAPE_MMA16816 "CUBLASLT_MATMUL_INNER_SHAPE_MMA16816" = 4 +ctypedef enum cublasLtMatmulMatrixScale_t "cublasLtMatmulMatrixScale_t": + CUBLASLT_MATMUL_MATRIX_SCALE_SCALAR_32F "CUBLASLT_MATMUL_MATRIX_SCALE_SCALAR_32F" = 0 + CUBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE4M3 "CUBLASLT_MATMUL_MATRIX_SCALE_VEC16_UE4M3" = 1 + CUBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE8M0 "CUBLASLT_MATMUL_MATRIX_SCALE_VEC32_UE8M0" = 2 + # types cdef extern from *: @@ -932,17 +949,17 @@ ctypedef void* cublasLtMatrixTransformDesc_t 'cublasLtMatrixTransformDesc_t' ctypedef void* cublasLtMatmulPreference_t 'cublasLtMatmulPreference_t' ctypedef struct cublasLtMatmulAlgo_t 'cublasLtMatmulAlgo_t': uint64_t data[8] +ctypedef void (*cublasLtLoggerCallback_t 'cublasLtLoggerCallback_t')( + int logLevel, + const char* functionName, + const char* message +) ctypedef struct cublasLtMatmulHeuristicResult_t 'cublasLtMatmulHeuristicResult_t': cublasLtMatmulAlgo_t algo size_t workspaceSize cublasStatus_t state float wavesCount int reserved[4] -ctypedef void (*cublasLtLoggerCallback_t 'cublasLtLoggerCallback_t')( - int logLevel, - const char* functionName, - const char* message -) ############################################################################### diff --git a/nvmath/bindings/cycublasLt.pyx b/nvmath/bindings/cycublasLt.pyx index 966e6b3..e625f49 100644 --- a/nvmath/bindings/cycublasLt.pyx +++ b/nvmath/bindings/cycublasLt.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from ._internal cimport cublasLt as _cublasLt diff --git a/nvmath/bindings/cycufft.pxd b/nvmath/bindings/cycufft.pxd index 94b53c4..b523fea 100644 --- a/nvmath/bindings/cycufft.pxd +++ b/nvmath/bindings/cycufft.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. # This layer exposes the C header to Cython as-is. ############################################################################### @@ -158,83 +158,83 @@ ctypedef cufftDoubleReal (*cufftCallbackLoadD 'cufftCallbackLoadD')( void* callerInfo, void* sharedPointer ) -ctypedef void (*cufftCallbackStoreC 'cufftCallbackStoreC')( - void* dataOut, - size_t offset, - cufftComplex element, +ctypedef cufftComplex (*cufftJITCallbackLoadC 'cufftJITCallbackLoadC')( + void* dataIn, + unsigned long long offset, void* callerInfo, void* sharedPointer ) -ctypedef void (*cufftCallbackStoreZ 'cufftCallbackStoreZ')( - void* dataOut, - size_t offset, - cufftDoubleComplex element, +ctypedef cufftDoubleComplex (*cufftJITCallbackLoadZ 'cufftJITCallbackLoadZ')( + void* dataIn, + unsigned long long offset, void* callerInfo, void* sharedPointer ) -ctypedef void (*cufftCallbackStoreR 'cufftCallbackStoreR')( - void* dataOut, - size_t offset, - cufftReal element, +ctypedef cufftReal (*cufftJITCallbackLoadR 'cufftJITCallbackLoadR')( + void* dataIn, + unsigned long long offset, void* callerInfo, void* sharedPointer ) -ctypedef void (*cufftCallbackStoreD 'cufftCallbackStoreD')( - void* dataOut, - size_t offset, - cufftDoubleReal element, +ctypedef cufftDoubleReal (*cufftJITCallbackLoadD 'cufftJITCallbackLoadD')( + void* dataIn, + unsigned long long offset, void* callerInfo, void* sharedPointer ) -ctypedef cufftComplex (*cufftJITCallbackLoadC 'cufftJITCallbackLoadC')( - void* dataIn, - unsigned long long offset, +ctypedef void (*cufftCallbackStoreR 'cufftCallbackStoreR')( + void* dataOut, + size_t offset, + cufftReal element, void* callerInfo, void* sharedPointer ) -ctypedef cufftDoubleComplex (*cufftJITCallbackLoadZ 'cufftJITCallbackLoadZ')( - void* dataIn, +ctypedef void (*cufftJITCallbackStoreR 'cufftJITCallbackStoreR')( + void* dataOut, unsigned long long offset, + cufftReal element, void* callerInfo, void* sharedPointer ) -ctypedef cufftReal (*cufftJITCallbackLoadR 'cufftJITCallbackLoadR')( - void* dataIn, - unsigned long long offset, +ctypedef void (*cufftCallbackStoreD 'cufftCallbackStoreD')( + void* dataOut, + size_t offset, + cufftDoubleReal element, void* callerInfo, void* sharedPointer ) -ctypedef cufftDoubleReal (*cufftJITCallbackLoadD 'cufftJITCallbackLoadD')( - void* dataIn, +ctypedef void (*cufftJITCallbackStoreD 'cufftJITCallbackStoreD')( + void* dataOut, unsigned long long offset, + cufftDoubleReal element, void* callerInfo, void* sharedPointer ) -ctypedef void (*cufftJITCallbackStoreC 'cufftJITCallbackStoreC')( +ctypedef void (*cufftCallbackStoreC 'cufftCallbackStoreC')( void* dataOut, - unsigned long long offset, + size_t offset, cufftComplex element, void* callerInfo, void* sharedPointer ) -ctypedef void (*cufftJITCallbackStoreZ 'cufftJITCallbackStoreZ')( +ctypedef void (*cufftJITCallbackStoreC 'cufftJITCallbackStoreC')( void* dataOut, unsigned long long offset, - cufftDoubleComplex element, + cufftComplex element, void* callerInfo, void* sharedPointer ) -ctypedef void (*cufftJITCallbackStoreR 'cufftJITCallbackStoreR')( +ctypedef void (*cufftCallbackStoreZ 'cufftCallbackStoreZ')( void* dataOut, - unsigned long long offset, - cufftReal element, + size_t offset, + cufftDoubleComplex element, void* callerInfo, void* sharedPointer ) -ctypedef void (*cufftJITCallbackStoreD 'cufftJITCallbackStoreD')( +ctypedef void (*cufftJITCallbackStoreZ 'cufftJITCallbackStoreZ')( void* dataOut, unsigned long long offset, - cufftDoubleReal element, + cufftDoubleComplex element, void* callerInfo, void* sharedPointer ) diff --git a/nvmath/bindings/cycufft.pyx b/nvmath/bindings/cycufft.pyx index 561a30a..5e249f9 100644 --- a/nvmath/bindings/cycufft.pyx +++ b/nvmath/bindings/cycufft.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from ._internal cimport cufft as _cufft diff --git a/nvmath/bindings/cycurand.pxd b/nvmath/bindings/cycurand.pxd index e191e39..c377a61 100644 --- a/nvmath/bindings/cycurand.pxd +++ b/nvmath/bindings/cycurand.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. # This layer exposes the C header to Cython as-is. from libc.stdint cimport int64_t diff --git a/nvmath/bindings/cycurand.pyx b/nvmath/bindings/cycurand.pyx index d2a5ec8..e9d44f2 100644 --- a/nvmath/bindings/cycurand.pyx +++ b/nvmath/bindings/cycurand.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from ._internal cimport curand as _curand diff --git a/nvmath/bindings/cycusolver.pxd b/nvmath/bindings/cycusolver.pxd index 85b2af7..85f3d80 100644 --- a/nvmath/bindings/cycusolver.pxd +++ b/nvmath/bindings/cycusolver.pxd @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/nvmath/bindings/cycusolver.pyx b/nvmath/bindings/cycusolver.pyx index 10d66ab..69f772d 100644 --- a/nvmath/bindings/cycusolver.pyx +++ b/nvmath/bindings/cycusolver.pyx @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/nvmath/bindings/cycusolverDn.pxd b/nvmath/bindings/cycusolverDn.pxd index 2c94637..377de4b 100644 --- a/nvmath/bindings/cycusolverDn.pxd +++ b/nvmath/bindings/cycusolverDn.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. # This layer exposes the C header to Cython as-is. from libc.stdint cimport int64_t diff --git a/nvmath/bindings/cycusolverDn.pyx b/nvmath/bindings/cycusolverDn.pyx index 3d03f3c..be4f61c 100644 --- a/nvmath/bindings/cycusolverDn.pyx +++ b/nvmath/bindings/cycusolverDn.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from ._internal cimport cusolverDn as _cusolverDn diff --git a/nvmath/bindings/cycusparse.pxd b/nvmath/bindings/cycusparse.pxd index de2f01b..e9279fd 100644 --- a/nvmath/bindings/cycusparse.pxd +++ b/nvmath/bindings/cycusparse.pxd @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. # This layer exposes the C header to Cython as-is. from libc.stdint cimport int64_t diff --git a/nvmath/bindings/cycusparse.pyx b/nvmath/bindings/cycusparse.pyx index b59feb7..0907680 100644 --- a/nvmath/bindings/cycusparse.pyx +++ b/nvmath/bindings/cycusparse.pyx @@ -1,8 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # -# This code was automatically generated across versions from 11.0.3 to 12.6.2. Do not modify it directly. +# This code was automatically generated across versions from 11.0.3 to 12.8.0. Do not modify it directly. from ._internal cimport cusparse as _cusparse diff --git a/nvmath/bindings/nvpl/__init__.py b/nvmath/bindings/nvpl/__init__.py index dfcdae0..6172ecb 100644 --- a/nvmath/bindings/nvpl/__init__.py +++ b/nvmath/bindings/nvpl/__init__.py @@ -1,7 +1,11 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # type: ignore from . import fft + +__all__ = [ + "fft", +] diff --git a/nvmath/bindings/nvpl/_internal/fft.pxd b/nvmath/bindings/nvpl/_internal/fft.pxd index a422030..c5ba5eb 100644 --- a/nvmath/bindings/nvpl/_internal/fft.pxd +++ b/nvmath/bindings/nvpl/_internal/fft.pxd @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/nvmath/bindings/nvpl/_internal/fft_linux.pyx b/nvmath/bindings/nvpl/_internal/fft_linux.pyx index 7421bc0..041d312 100644 --- a/nvmath/bindings/nvpl/_internal/fft_linux.pyx +++ b/nvmath/bindings/nvpl/_internal/fft_linux.pyx @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/nvmath/bindings/nvpl/cyfft.pxd b/nvmath/bindings/nvpl/cyfft.pxd index c227697..d2530a5 100644 --- a/nvmath/bindings/nvpl/cyfft.pxd +++ b/nvmath/bindings/nvpl/cyfft.pxd @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/nvmath/bindings/nvpl/cyfft.pyx b/nvmath/bindings/nvpl/cyfft.pyx index 052d7e8..dfb122e 100644 --- a/nvmath/bindings/nvpl/cyfft.pyx +++ b/nvmath/bindings/nvpl/cyfft.pyx @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/nvmath/bindings/nvpl/fft.pxd b/nvmath/bindings/nvpl/fft.pxd index 449da11..3380a13 100644 --- a/nvmath/bindings/nvpl/fft.pxd +++ b/nvmath/bindings/nvpl/fft.pxd @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/nvmath/bindings/nvpl/fft.pyx b/nvmath/bindings/nvpl/fft.pyx index 01d84ac..db58f06 100644 --- a/nvmath/bindings/nvpl/fft.pyx +++ b/nvmath/bindings/nvpl/fft.pyx @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/nvmath/device/__init__.py b/nvmath/device/__init__.py index 6b167aa..d62c03f 100644 --- a/nvmath/device/__init__.py +++ b/nvmath/device/__init__.py @@ -1,14 +1,15 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 from .patch import patch_codegen patch_codegen() -from .common_cuda import * -from .cufftdx import * -from .cublasdx import * -from .cublasdx_backend import * -from .vector_types_numba import * + +from .common_cuda import * # noqa: E402, F403 +from .cufftdx import * # noqa: E402, F403 +from .cublasdx import * # noqa: E402, F403 +from .cublasdx_backend import * # noqa: E402, F403 +from .vector_types_numba import * # noqa: E402, F403 del patch_codegen diff --git a/nvmath/device/caching.py b/nvmath/device/caching.py index 1f31fe4..1c4d5aa 100644 --- a/nvmath/device/caching.py +++ b/nvmath/device/caching.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/device/common.py b/nvmath/device/common.py index 53dd12a..42a241a 100644 --- a/nvmath/device/common.py +++ b/nvmath/device/common.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -9,7 +9,7 @@ SHARED_DEVICE_DOCSTRINGS = { - "compiler": "A string to specify the compiler for the device code, currently supports ``None`` (default) and ``'Numba'``", + "compiler": "A string to specify the compiler for the device code, currently supports ``None`` (default) and ``'numba'``", # "precision": """\ The computation precision specified as a numpy float dtype, currently supports ``numpy.float16``, ``numpy.float32`` and diff --git a/nvmath/device/common_cpp.py b/nvmath/device/common_cpp.py index 62e7990..a7c3781 100644 --- a/nvmath/device/common_cpp.py +++ b/nvmath/device/common_cpp.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/device/common_cuda.py b/nvmath/device/common_cuda.py index adf9b25..ad87d71 100644 --- a/nvmath/device/common_cuda.py +++ b/nvmath/device/common_cuda.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -113,8 +113,15 @@ def get_current_device_cc(): CHECK_CUDART(err) err, prop = cudart.cudaGetDeviceProperties(device) CHECK_CUDART(err) + major, minor = prop.major, prop.minor + # TODO: dx does not support platforms > arch90 for now + if (major, minor) > (9, 0): + logging.info( + f"The current device supports compute capability {prop.major}.{prop.minor}, but the generated LTO version is capped at 9.0." + ) + major, minor = 9, 0 logging.info(f"Using device {device} for default compute capability, found cc = {prop.major}.{prop.minor}") - return ComputeCapability(prop.major, prop.minor) + return ComputeCapability(major, minor) def get_default_code_type(): diff --git a/nvmath/device/common_mathdx.py b/nvmath/device/common_mathdx.py index 12c95d0..aed233e 100644 --- a/nvmath/device/common_mathdx.py +++ b/nvmath/device/common_mathdx.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/device/common_numba.py b/nvmath/device/common_numba.py index b3f1287..f542f02 100644 --- a/nvmath/device/common_numba.py +++ b/nvmath/device/common_numba.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/device/cublasdx.py b/nvmath/device/cublasdx.py index cc9e68c..65e8688 100644 --- a/nvmath/device/cublasdx.py +++ b/nvmath/device/cublasdx.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -117,6 +117,11 @@ def __init__( raise RuntimeError( "Minimal compute capability 7.0 is required by cuBLASDx, got " f"{code_type.cc.major}.{code_type.cc.minor}" ) + # TODO: cublasdx does not support platforms > arch90 for now + if (code_type.cc.major, code_type.cc.minor) > (9, 0): + raise RuntimeError( + f"The maximum compute capability currently supported by device APIs is 9.0, got {code_type.cc.major}.{code_type.cc.minor}" + ) if len(transpose_mode) != 2: raise ValueError( diff --git a/nvmath/device/cublasdx_backend.py b/nvmath/device/cublasdx_backend.py index 1cc27dd..6016485 100644 --- a/nvmath/device/cublasdx_backend.py +++ b/nvmath/device/cublasdx_backend.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/device/cublasdx_numba.py b/nvmath/device/cublasdx_numba.py index 66b925c..73f374a 100644 --- a/nvmath/device/cublasdx_numba.py +++ b/nvmath/device/cublasdx_numba.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/device/cufftdx.py b/nvmath/device/cufftdx.py index 264ff03..3df3e80 100644 --- a/nvmath/device/cufftdx.py +++ b/nvmath/device/cufftdx.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -87,8 +87,8 @@ class FFTOptions: - ``'real_mode'``, currently supports ``'normal'`` and ``'folded``. Note: - The class is not meant to used directly with its constructor. Users are instead - advised to use :func:`fft` create the object. + The class is not meant to be used directly with its constructor. Users are instead + advised to use :func:`fft` to create the object. See Also: The attributes of this class provide a 1:1 mapping with the CUDA C++ cuFFTDx APIs. @@ -116,6 +116,11 @@ def __init__( raise RuntimeError( f"Minimal compute capability 7.0 is required by cuFFTDx, got {code_type.cc.major}.{code_type.cc.minor}" ) + # TODO: cufftdx does not support platforms > arch90 for now + if (code_type.cc.major, code_type.cc.minor) > (9, 0): + raise RuntimeError( + f"The maximum compute capability currently supported by device APIs is 9.0, got {code_type.cc.major}.{code_type.cc.minor}" + ) # # Check that the knobs are, individually, valid @@ -246,14 +251,6 @@ def create(self, **kwargs): # Private implementations # - def _valid(self, knob): - if knob == "elements_per_thread": - return [self._suggested_elements_per_thread] - elif knob == "ffts_per_block": - return [1, self._suggested_ffts_per_block] - else: - raise ValueError("Unsupported knob") - def _suggested(self, what): # Generate full PTX cpp = generate_block( @@ -449,7 +446,7 @@ def fft(*, compiler=None, **kwargs): compiler (str): {compiler} - code_type (CodeType): {code_type}. Optional if compiler is specified as ``'Numba'``. + code_type (CodeType): {code_type}. Optional if compiler is specified as ``'numba'``. execution (str): {execution} diff --git a/nvmath/device/cufftdx_backend.py b/nvmath/device/cufftdx_backend.py index f4e04e6..c24ddbc 100644 --- a/nvmath/device/cufftdx_backend.py +++ b/nvmath/device/cufftdx_backend.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/device/cufftdx_db.py b/nvmath/device/cufftdx_db.py index 8857a59..ec079b2 100644 --- a/nvmath/device/cufftdx_db.py +++ b/nvmath/device/cufftdx_db.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/device/cufftdx_numba.py b/nvmath/device/cufftdx_numba.py index 9c85741..e69b33b 100644 --- a/nvmath/device/cufftdx_numba.py +++ b/nvmath/device/cufftdx_numba.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/device/cufftdx_workspace.py b/nvmath/device/cufftdx_workspace.py index 92a29fb..9ca0bd8 100644 --- a/nvmath/device/cufftdx_workspace.py +++ b/nvmath/device/cufftdx_workspace.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/device/curand_kernel.py b/nvmath/device/curand_kernel.py index c811eeb..d446ed8 100644 --- a/nvmath/device/curand_kernel.py +++ b/nvmath/device/curand_kernel.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/device/nvrtc.py b/nvmath/device/nvrtc.py index 3c48389..94609bc 100644 --- a/nvmath/device/nvrtc.py +++ b/nvmath/device/nvrtc.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/device/patch.py b/nvmath/device/patch.py index 733fcbd..adf9682 100644 --- a/nvmath/device/patch.py +++ b/nvmath/device/patch.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -7,12 +7,9 @@ # support LTO code generation and linking # find libnvvm located in PYPI wheels # - -import os import functools import numba -from numba.cuda.cudadrv import libs import numba.cuda.cudadrv.nvrtc as nvrtc import numba.cuda.cudadrv.nvvm as nvvm import pynvjitlink.patch # type: ignore diff --git a/nvmath/device/random.py b/nvmath/device/random.py index 7892faa..4efc763 100644 --- a/nvmath/device/random.py +++ b/nvmath/device/random.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/device/random_helpers.py b/nvmath/device/random_helpers.py index d063e27..3508c4a 100644 --- a/nvmath/device/random_helpers.py +++ b/nvmath/device/random_helpers.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/device/random_states.py b/nvmath/device/random_states.py index a544149..edcad17 100644 --- a/nvmath/device/random_states.py +++ b/nvmath/device/random_states.py @@ -1,12 +1,11 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 import operator -import re from nvmath.device import curand_kernel -from nvmath.device.common_mathdx import CURAND_HOME +from nvmath.device.common_mathdx import CURAND_HOME # noqa: F401 from numba import cuda, types from numba.extending import models, register_model, typeof_impl @@ -17,11 +16,6 @@ from llvmlite import ir -import os -import logging - -from numba import config - xorwow_dtype = np.dtype( [ ("d", np.uint32), diff --git a/nvmath/device/types.py b/nvmath/device/types.py index 89fdcb2..c653b94 100644 --- a/nvmath/device/types.py +++ b/nvmath/device/types.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/device/vector_types_numba.py b/nvmath/device/vector_types_numba.py index 1426ced..50e3ca0 100644 --- a/nvmath/device/vector_types_numba.py +++ b/nvmath/device/vector_types_numba.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/fft/__init__.py b/nvmath/fft/__init__.py index 874dce5..f9ec48d 100644 --- a/nvmath/fft/__init__.py +++ b/nvmath/fft/__init__.py @@ -1,7 +1,7 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 -from ._configuration import * -from ._helpers import * -from .fft import * +from ._configuration import * # noqa: F403 +from ._helpers import * # noqa: F403 +from .fft import * # noqa: F403 diff --git a/nvmath/fft/_configuration.py b/nvmath/fft/_configuration.py index 7c583a6..6abddaa 100644 --- a/nvmath/fft/_configuration.py +++ b/nvmath/fft/_configuration.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -8,9 +8,7 @@ from enum import IntEnum from logging import Logger from typing import Literal -import warnings -from nvmath._internal.mem_limit import MEM_LIMIT_RE_PCT, MEM_LIMIT_RE_VAL, MEM_LIMIT_DOC from nvmath.memory import BaseCUDAMemoryManager @@ -74,12 +72,6 @@ class FFTOptions: * (m - 1) + 1`. The specified value should be either ``'even'`` or ``'odd'``, with the default being ``'even'``. - last_axis_size: See :attr:`last_axis_parity`. - - .. deprecated:: 0.2.1 - :attr:`last_axis_size` will be removed in 0.3.0. Use - :attr:`last_axis_parity` instead. - result_layout: The layout to use for the result, either ``'natural'`` or ``'optimized'``. For the ``'natural'`` option, the result layout is the same as that of the operand. The default is ``'optimized'``, which generally provides @@ -123,7 +115,6 @@ class FFTOptions: fft_type: Literal["C2C", "C2R", "R2C"] | None = None inplace: bool = False last_axis_parity: Literal["even", "odd"] | None = "even" - last_axis_size: None = None result_layout: Literal["natural", "optimized"] | None = "optimized" device_id: int | None = None logger: Logger | None = None @@ -138,14 +129,6 @@ def __post_init__(self): if not isinstance(self.inplace, bool): raise ValueError("The value specified for 'inplace' must be of type bool (True or False).") - # TODO: Remove in version 0.3.0 - if self.last_axis_size is not None: - warnings.warn( - "FFTOptions.last_axis_size is deprecated and will be removed in version 0.3.0. " - "Use FFTOptions.last_axis_parity instead.", - DeprecationWarning, - ) - self.last_axis_parity = self.last_axis_size valid_last_axis_parity = ["even", "odd"] if self.last_axis_parity not in valid_last_axis_parity: raise ValueError(f"The value specified for 'last_axis_parity' must be one of {valid_last_axis_parity}.") diff --git a/nvmath/fft/_exec_utils.py b/nvmath/fft/_exec_utils.py index dfcc05c..2076f34 100644 --- a/nvmath/fft/_exec_utils.py +++ b/nvmath/fft/_exec_utils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -31,7 +31,7 @@ def _check_init_cufft(): ) from e try: - import cupy + import cupy # noqa: F401 except ImportError as e: raise RuntimeError("Currently, the FFT CUDA execution requires cupy. Please make sure cupy is installed.") from e diff --git a/nvmath/fft/_helpers.py b/nvmath/fft/_helpers.py index 9a0c4c9..393bdbc 100644 --- a/nvmath/fft/_helpers.py +++ b/nvmath/fft/_helpers.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -24,7 +24,7 @@ def _check_numba_available(): try: - import numba + import numba # noqa: F401 except ModuleNotFoundError as e: raise RuntimeError("Numba is required to compile FFT prolog and epilog functions.") from e diff --git a/nvmath/fft/fft.py b/nvmath/fft/fft.py index 8af5838..6d6ca15 100644 --- a/nvmath/fft/fft.py +++ b/nvmath/fft/fft.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -464,7 +464,7 @@ def get_fft_plan_traits( if last_axis_size == 0: raise ValueError( f"The size of the last FFT axis in the result for FFT type '{fft_abstract_type}' is 0 for operand shape = " - f"{operand_shape} and axes = {axes}. To fix this, provide 'last_axis_size' = 'odd' to the FFT options." + f"{operand_shape} and axes = {axes}. To fix this, provide 'last_axis_parity' = 'odd' to the FFT options." ) ordered_fft_out_shape = list(ordered_fft_in_shape) index = ordered_axes.index(last_axis_id) @@ -839,7 +839,12 @@ def create_fft_key(operand, *, axes=None, options=None, execution=None, inplace= # Prolog and epilog, if used. if prolog is not None or epilog is not None: - get_data = lambda device_callable: None if device_callable is None else (device_callable.ltoir, device_callable.data) + prolog = utils.check_or_create_options(_configuration.DeviceCallable, prolog, "prolog", keep_none=True) + epilog = utils.check_or_create_options(_configuration.DeviceCallable, epilog, "epilog", keep_none=True) + + def get_data(device_callable): + return None if device_callable is None else (device_callable.ltoir, device_callable.data) + callable_data = get_data(prolog), get_data(epilog) else: callable_data = None @@ -1071,7 +1076,7 @@ class FFT: - The input must be Hermitian-symmetric when :attr:`FFTOptions.fft_type` is ``'C2R'``, otherwise the result is undefined. As a specific example, if the input for a C2R FFT was generated using an R2C FFT with an odd last axis size, then - :attr:`FFTOptions.last_axis_size` must be set to `odd` to recover the original + :attr:`FFTOptions.last_axis_parity` must be set to `odd` to recover the original signal. """ @@ -1823,6 +1828,7 @@ def _release_workspace_memory_perhaps(self, exception: Exception | None = None) "the value of 'workspace_allocated_here'." ) self._free_workspace_memory_perhaps(release_workspace) + self._workspace_allocated_here = False return True @utils.precondition(_check_valid_fft) @@ -2199,9 +2205,9 @@ def irfft(x, *, axes=None, options=None, execution=None, prolog=None, epilog=Non Returns: A real tensor that remains on the same device and belongs to the same package as the input operand. The extent of the last transformed axis in the result will be - ``(operand.shape[axes[-1]] - 1) * 2`` if :attr:`FFTOptions.last_axis_size` is + ``(operand.shape[axes[-1]] - 1) * 2`` if :attr:`FFTOptions.last_axis_parity` is ``even``, or ``operand.shape[axes[-1]] * 2 - 1`` if - :attr:`FFTOptions.last_axis_size` is ``odd``. + :attr:`FFTOptions.last_axis_parity` is ``odd``. See Also: :func:`fft`, :func:`ifft`, :class:`FFT`. @@ -2239,7 +2245,7 @@ def irfft(x, *, axes=None, options=None, execution=None, prolog=None, epilog=Non example, 1-D transforms require the first element (and the last element, if the extent is even) of the input to be purely real-valued. In addition, if the input to `irfft` was generated using an R2C FFT with an odd last axis size, - :attr:`FFTOptions.last_axis_size` must be set to ``odd`` to recover the original + :attr:`FFTOptions.last_axis_parity` must be set to ``odd`` to recover the original signal. - For more details, please refer to `C2R example `_ diff --git a/nvmath/linalg/__init__.py b/nvmath/linalg/__init__.py index 3422703..ebe6de8 100644 --- a/nvmath/linalg/__init__.py +++ b/nvmath/linalg/__init__.py @@ -1,5 +1,11 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 from . import advanced +from nvmath.bindings.cublas import ComputeType # type: ignore + +__all__ = [ + "advanced", + "ComputeType", +] diff --git a/nvmath/linalg/_internal/__init__.py b/nvmath/linalg/_internal/__init__.py index 281f836..6ff03ac 100644 --- a/nvmath/linalg/_internal/__init__.py +++ b/nvmath/linalg/_internal/__init__.py @@ -1,5 +1,3 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 - -from .enum_to_tuples import * diff --git a/nvmath/linalg/_internal/algo_cap_ifc.py b/nvmath/linalg/_internal/algo_cap_ifc.py index 90bb56f..59a7b4c 100644 --- a/nvmath/linalg/_internal/algo_cap_ifc.py +++ b/nvmath/linalg/_internal/algo_cap_ifc.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/linalg/_internal/algo_config_ifc.py b/nvmath/linalg/_internal/algo_config_ifc.py index ccc7837..442d8e4 100644 --- a/nvmath/linalg/_internal/algo_config_ifc.py +++ b/nvmath/linalg/_internal/algo_config_ifc.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/linalg/_internal/enum_to_tuples.py b/nvmath/linalg/_internal/enum_to_tuples.py index 7ec8360..42fba08 100644 --- a/nvmath/linalg/_internal/enum_to_tuples.py +++ b/nvmath/linalg/_internal/enum_to_tuples.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/linalg/_internal/epilog_protocol.py b/nvmath/linalg/_internal/epilog_protocol.py index 3e284f6..a44ec04 100644 --- a/nvmath/linalg/_internal/epilog_protocol.py +++ b/nvmath/linalg/_internal/epilog_protocol.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -135,7 +135,7 @@ def update_ptr(self, mm_desc_ifc, ptr): class BiasHandler(EpilogInputHandler): - def __init__(self, logger, mm_traits, enumerator, d_dtype_name): + def __init__(self, logger, mm_traits, enumerator, c_dtype_name, d_dtype_name, aux_dtype_name): self.logger = logger self.mm_traits = mm_traits @@ -264,7 +264,7 @@ def gelu_aux_mm_shape(m, n): class ReluAuxHandler(EpilogOutputHandler): - def __init__(self, logger, mm_traits, enumerator, d_dtype_name): + def __init__(self, logger, mm_traits, enumerator, c_dtype_name, d_dtype_name, aux_dtype_name): self.logger = logger self.mm_traits = mm_traits @@ -279,6 +279,8 @@ def __init__(self, logger, mm_traits, enumerator, d_dtype_name): self.aux_shape = mm_traits.batch_shape + [m, n] aux_axis_order = [batch_len, batch_len + 1] + list(mm_traits.batch_axis_order) # Column order for the bitmask. self.aux_strides = calculate_strides(self.aux_shape, aux_axis_order) + if aux_dtype_name is not None: + raise ValueError("Custom type for auxiliary outputs is not supported for RELU epilogs.") self.aux_dtype_name = "uint8" # We store bitmask using int8 dtype but the values below are in number of elements. @@ -309,7 +311,7 @@ def update_ptr(self, mm_desc_ifc, ptr): class GeluAuxHandler(EpilogOutputHandler): - def __init__(self, logger, mm_traits, enumerator, d_dtype_name): + def __init__(self, logger, mm_traits, enumerator, c_dtype_name, d_dtype_name, aux_dtype_name): self.logger = logger self.mm_traits = mm_traits @@ -326,7 +328,15 @@ def __init__(self, logger, mm_traits, enumerator, d_dtype_name): self.aux_shape = mm_traits.batch_shape + [m, n] aux_axis_order = [batch_len, batch_len + 1] + list(mm_traits.batch_axis_order) # Column order for the GELU inputs. self.aux_strides = calculate_strides(self.aux_shape, aux_axis_order) - self.aux_dtype_name = d_dtype_name + + self.version = cublaslt.get_version() + + if aux_dtype_name: + if self.version < 120800: + raise ValueError("Specifying custom AUX data type is not supported for cuBLAS < 12.8.") + self.aux_dtype_name = aux_dtype_name + else: + self.aux_dtype_name = c_dtype_name if "float8" in d_dtype_name else d_dtype_name self.aux_ld = m # should be consistent with order (currently COL). self.aux_batch_offset = m * n @@ -347,9 +357,10 @@ def update(self, mm_desc_ifc): mm_desc_ifc.epilogue_aux_ld = self.aux_ld # Set the aux batch offset. mm_desc_ifc.epilogue_aux_batch_stride = self.aux_batch_offset - # The aux data type is by default the data type of the result for all the cases we - # support. - assert self.aux_dtype_name == self.d_dtype_name, "Internal error." + # Set the pointer to 0x1 to bypass the cuBLAS check. + mm_desc_ifc.epilogue_aux_pointer = 0x1 + if self.aux_dtype_name is not None and self.version >= 120800: + mm_desc_ifc.epilogue_aux_data_type = typemaps.NAME_TO_DATA_TYPE[self.aux_dtype_name] def update_ptr(self, mm_desc_ifc, ptr): # Set the aux pointer. @@ -357,7 +368,7 @@ def update_ptr(self, mm_desc_ifc, ptr): class BgradHandler(EpilogOutputHandler): - def __init__(self, logger, mm_traits, enumerator, d_dtype_name): + def __init__(self, logger, mm_traits, enumerator, c_dtype_name, d_dtype_name, aux_dtype_name): self.logger = logger self.mm_traits = mm_traits @@ -372,6 +383,9 @@ def __init__(self, logger, mm_traits, enumerator, d_dtype_name): self._name = enumerator.name.lower() + if aux_dtype_name is not None: + raise ValueError("Custom type for auxiliary outputs is not supported for RELU epilogs.") + m = mm_traits.N if enumerator == Epilog.BGRADB else mm_traits.M batch_len = len(mm_traits.batch_axis_order) @@ -418,7 +432,7 @@ def update_ptr(self, mm_desc_ifc, ptr): class DReluAuxHandler(EpilogInputHandler): - def __init__(self, logger, mm_traits, enumerator, d_dtype_name): + def __init__(self, logger, mm_traits, enumerator, c_dtype_name, d_dtype_name, aux_dtype_name): self.logger = logger self.mm_traits = mm_traits @@ -520,7 +534,7 @@ def update(self, mm_desc_ifc, relu_aux_tensor): class DGeluAuxHandler(EpilogInputHandler): - def __init__(self, logger, mm_traits, enumerator, d_dtype_name): + def __init__(self, logger, mm_traits, enumerator, c_dtype_name, d_dtype_name, aux_dtype_name): self.logger = logger self.mm_traits = mm_traits diff --git a/nvmath/linalg/_internal/matmul_desc_ifc.py b/nvmath/linalg/_internal/matmul_desc_ifc.py index 5a3bb39..a70860c 100644 --- a/nvmath/linalg/_internal/matmul_desc_ifc.py +++ b/nvmath/linalg/_internal/matmul_desc_ifc.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -8,19 +8,14 @@ __all__ = ["MatmulDescInterface"] -from collections.abc import Sequence -import itertools -import numbers -import operator import ctypes import logging -logger = logging.getLogger() - import numpy as np from nvmath.bindings import cublasLt as cublaslt +logger = logging.getLogger() DescEnum = cublaslt.MatmulDescAttribute diff --git a/nvmath/linalg/_internal/matmul_pref_ifc.py b/nvmath/linalg/_internal/matmul_pref_ifc.py index fbea322..5b06d72 100644 --- a/nvmath/linalg/_internal/matmul_pref_ifc.py +++ b/nvmath/linalg/_internal/matmul_pref_ifc.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -9,19 +9,15 @@ __all__ = ["MatmulPreferenceInterface"] -from collections.abc import Sequence -import itertools -import numbers -import operator import logging -logger = logging.getLogger() - import numpy as np from nvmath.bindings import cublasLt as cublaslt +logger = logging.getLogger() + PreferenceEnum = cublaslt.MatmulPreferenceAttribute diff --git a/nvmath/linalg/_internal/matrix_layout_ifc.py b/nvmath/linalg/_internal/matrix_layout_ifc.py index 2a2bf22..b615117 100644 --- a/nvmath/linalg/_internal/matrix_layout_ifc.py +++ b/nvmath/linalg/_internal/matrix_layout_ifc.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -8,10 +8,6 @@ __all__ = ["MatrixLayoutInterface"] -from collections.abc import Sequence -import itertools -import numbers -import operator import numpy as np diff --git a/nvmath/linalg/_internal/typemaps.py b/nvmath/linalg/_internal/typemaps.py index ea17b11..736c57a 100644 --- a/nvmath/linalg/_internal/typemaps.py +++ b/nvmath/linalg/_internal/typemaps.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -6,11 +6,16 @@ Functions to link type names with CUBLAS compute types. """ -__all__ = ["NAME_TO_DEFAULT_SCALE_TYPE", "NAME_TO_DEFAULT_COMPUTE_TYPE"] +__all__ = [ + "NAMES_TO_DEFAULT_SCALE_TYPE", + "NAMES_TO_DEFAULT_COMPUTE_TYPE", + "SUPPORTED_TYPES", + "COMPUTE_TYPE_TO_DEFAULT_SCALE_TYPE", + "SCALE_TYPE_TO_DEFAULT_COMPUTE_TYPE", +] from nvmath.bindings import cublas # type: ignore from nvmath._internal.typemaps import cudaDataType -import re def create_default_scale_type_map(): @@ -22,22 +27,23 @@ def create_default_scale_type_map(): dt = cudaDataType scale_type_map = dict() - # scale_type_map['float8'] = dt.CUDA_R_32F # both CUDA_R_8F_E4M3 and CUDA_R_8F_E5M2 -> - # CUDA_R_32F - scale_type_map["bfloat16"] = dt.CUDA_R_32F - scale_type_map["float16"] = dt.CUDA_R_32F - scale_type_map["float32"] = dt.CUDA_R_32F - scale_type_map["float64"] = dt.CUDA_R_64F - scale_type_map["complex32"] = dt.CUDA_C_32F - scale_type_map["complex64"] = dt.CUDA_C_32F - scale_type_map["complex128"] = dt.CUDA_C_64F + scale_type_map["float8_e4m3fn", "float8_e5m2"] = dt.CUDA_R_32F + scale_type_map["float8_e5m2", "float8_e4m3fn"] = dt.CUDA_R_32F + scale_type_map["float8_e4m3fn", "float8_e4m3fn"] = dt.CUDA_R_32F + scale_type_map["bfloat16", "bfloat16"] = dt.CUDA_R_32F + scale_type_map["float16", "float16"] = dt.CUDA_R_32F + scale_type_map["float32", "float32"] = dt.CUDA_R_32F + scale_type_map["float64", "float64"] = dt.CUDA_R_64F + scale_type_map["complex32", "complex32"] = dt.CUDA_C_32F + scale_type_map["complex64", "complex64"] = dt.CUDA_C_32F + scale_type_map["complex128", "complex128"] = dt.CUDA_C_64F return scale_type_map def create_compute_type_to_scale_type_map(is_complex): """ - Map the compute type to the corresponding CUDA data type that's appropriate for + Map the compute type to the corresponding CUDA data type that's appropriate for default scale. """ @@ -82,22 +88,23 @@ def create_scale_type_to_compute_type_map(): def create_compute_type_map(): """ - Map the data type name to the corresponding CUDA data type that's appropriate for + Map the data type name to the corresponding CUDA data type that's appropriate for default scale. """ ct = cublas.ComputeType compute_type_map = dict() - # compute_type_map['float8'] = ct.COMPUTE_32F - # both CUDA_R_8F_E4M3 and CUDA_R_8F_E5M2 -> CUBLAS_COMPUTE_32F - compute_type_map["bfloat16"] = ct.COMPUTE_32F - compute_type_map["float16"] = ct.COMPUTE_32F - compute_type_map["float32"] = ct.COMPUTE_32F - compute_type_map["float64"] = ct.COMPUTE_64F - compute_type_map["complex32"] = ct.COMPUTE_32F - compute_type_map["complex64"] = ct.COMPUTE_32F - compute_type_map["complex128"] = ct.COMPUTE_64F + compute_type_map["float8_e4m3fn", "float8_e5m2"] = ct.COMPUTE_32F + compute_type_map["float8_e5m2", "float8_e4m3fn"] = ct.COMPUTE_32F + compute_type_map["float8_e4m3fn", "float8_e4m3fn"] = ct.COMPUTE_32F + compute_type_map["bfloat16", "bfloat16"] = ct.COMPUTE_32F + compute_type_map["float16", "float16"] = ct.COMPUTE_32F + compute_type_map["float32", "float32"] = ct.COMPUTE_32F + compute_type_map["float64", "float64"] = ct.COMPUTE_64F + compute_type_map["complex32", "complex32"] = ct.COMPUTE_32F + compute_type_map["complex64", "complex64"] = ct.COMPUTE_32F + compute_type_map["complex128", "complex128"] = ct.COMPUTE_64F return compute_type_map @@ -107,10 +114,22 @@ def create_compute_type_map(): cublas.ComputeType.COMPUTE_64F: ("float64", "complex128"), } -NAME_TO_DEFAULT_SCALE_TYPE = create_default_scale_type_map() -NAME_TO_DEFAULT_COMPUTE_TYPE = create_compute_type_map() +NAMES_TO_DEFAULT_SCALE_TYPE = create_default_scale_type_map() +NAMES_TO_DEFAULT_COMPUTE_TYPE = create_compute_type_map() COMPUTE_TYPE_TO_DEFAULT_SCALE_TYPE = { "real": create_compute_type_to_scale_type_map(is_complex=False), "complex": create_compute_type_to_scale_type_map(is_complex=True), } SCALE_TYPE_TO_DEFAULT_COMPUTE_TYPE = create_scale_type_to_compute_type_map() + +SUPPORTED_TYPES = [ + "float8_e4m3fn", + "float8_e5m2", + "bfloat16", + "float16", + "float32", + "float64", + "complex32", + "complex64", + "complex128", +] diff --git a/nvmath/linalg/_internal/utils.py b/nvmath/linalg/_internal/utils.py index fe47e29..bf0bce8 100644 --- a/nvmath/linalg/_internal/utils.py +++ b/nvmath/linalg/_internal/utils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/nvmath/linalg/advanced/__init__.py b/nvmath/linalg/advanced/__init__.py index 7ba8dbe..5f9cb25 100644 --- a/nvmath/linalg/advanced/__init__.py +++ b/nvmath/linalg/advanced/__init__.py @@ -1,7 +1,8 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 -from ._algorithmmod import * -from ._configuration import * -from .matmulmod import * +from ._algorithmmod import * # noqa: F403 +from ._configuration import * # noqa: F403 +from .matmulmod import * # noqa: F403 +from . import helpers as helpers # noqa: F403 diff --git a/nvmath/linalg/advanced/_algorithmmod.py b/nvmath/linalg/advanced/_algorithmmod.py index c7db5db..2019d02 100644 --- a/nvmath/linalg/advanced/_algorithmmod.py +++ b/nvmath/linalg/advanced/_algorithmmod.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -10,7 +10,6 @@ import dataclasses -import numpy as np from nvmath.linalg.advanced._configuration import AlgorithmCapabilities from nvmath.linalg._internal.algo_cap_ifc import AlgoCapInterface diff --git a/nvmath/linalg/advanced/_configuration.py b/nvmath/linalg/advanced/_configuration.py index cd5298d..ef7bdc3 100644 --- a/nvmath/linalg/advanced/_configuration.py +++ b/nvmath/linalg/advanced/_configuration.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -7,7 +7,9 @@ "MatmulInnerShape", "MatmulNumericalImplFlags", "MatmulOptions", + "MatmulEpilogPreferences", "MatmulPlanPreferences", + "MatmulQuantizationScales", "MatmulReductionScheme", "matrix_qualifiers_dtype", ] @@ -22,8 +24,8 @@ from nvmath.bindings import cublas # type: ignore from nvmath.bindings import cublasLt as cublaslt # type: ignore from nvmath._internal import enum_utils +from nvmath._internal.utils import check_or_create_options from nvmath._internal.mem_limit import check_memory_str -from nvmath._internal.mem_limit import MEM_LIMIT_RE_PCT, MEM_LIMIT_RE_VAL, MEM_LIMIT_DOC from nvmath.memory import BaseCUDAMemoryManager from nvmath._utils import CudaDataType @@ -39,11 +41,28 @@ class MatmulOptions: Attributes: compute_type (nvmath.linalg.ComputeType): CUDA compute type. A suitable compute type - will be selected if not specified. + will be selected if not specified. scale_type (nvmath.CudaDataType): CUDA data type. A suitable data type consistent with the compute type will be selected if not specified. + result_type (nvmath.CudaDataType): CUDA data type. A requested datatype of the + result. If not specified, this type will be determined based on the input types. + Non-default result types are only supported for narrow-precision (FP8 and lower) + operations. + + result_amax (bool): If set, the absolute maximum (amax) of the result will be + returned in the auxiliary output tensor. Only supported for narrow-precision + (FP8 and lower) operations. + + block_scaling (bool): If set, block scaling (MXFP8) will be used instead of + tensor-wide scaling for FP8 operations. If the result is a narrow-precision + (FP8 and lower) data type, scales used for result quantization will be returned + in the auxiliary output tensor as ``"d_out_scale"`` in UE8M0 format. For more + information on UE8M0 format, see the documentation of + :class:`~linalg.advanced.MatmulQuantizationScales`. + This option is only supported for narrow-precision (FP8 and lower) operations. + sm_count_target (int) : The number of SMs to use for execution. The default is 0, corresponding to all available SMs. @@ -82,6 +101,9 @@ class MatmulOptions: compute_type: int | None = None scale_type: int | None = None + result_type: int | None = None + result_amax: bool = False + block_scaling: bool = False sm_count_target: int | None = 0 fast_accumulation: bool | None = False device_id: int | None = None @@ -157,29 +179,61 @@ class MatmulNumericalImplFlags(IntEnum): ALL = (1 << 64) - 1 +@dataclasses.dataclass +class MatmulEpilogPreferences: + """A data class for providing epilog options as part of ``preferences`` to the + :meth:`Matmul.plan` method and the wrapper function :func:`matmul`. + + Attributes: + aux_type (nvmath.CudaDataType): The requested datatype of the + epilog auxiliary output. If not specified, this type will be determined based on + the input types. Non-default auxiliary output types are only supported for + narrow-precision operations and certain epilogs. For more details on the + supported combinations, see ``CUBLASLT_MATMUL_DESC_EPILOGUE_AUX_DATA_TYPE`` in + cuBLAS documentation. If this option is set to a narrow-precision data type, + an additional epilog input ``"aux_quantization_scale"`` needs to be specified. + + aux_amax (bool): If set, the absolute maximum (amax) of the epilog + auxiliary output will be returned in the auxiliary output tensor. + Only supported when ``aux_type`` option is set to a narrow-precision + data type. + + See Also: + :meth:`Matmul.plan`, :func:`matmul`, :class:`MatmulPlanPreferences` + """ + + aux_type: int | None = None + aux_amax: bool = False + + @dataclasses.dataclass class MatmulPlanPreferences: """A data class for providing options to the :meth:`Matmul.plan` method and the wrapper function :func:`matmul`. Attributes: - reduction_scheme_mask (object of type - :class:`linalg.advanced.MatmulReductionScheme`) : Enumerators from - :class:`linalg.advanced.MatmulReductionScheme` combined with bitwise operator - ``|``. The default is all reduction schemes. + reduction_scheme_mask (:class:`nvmath.linalg.advanced.MatmulReductionScheme`): + Enumerators from + :class:`nvmath.linalg.advanced.MatmulReductionScheme` combined with + bitwise operator ``|``. The default is all reduction schemes. max_waves_count (float) : The maximum wave count. Selecting a value greater than 0. will exclude algorithms with device utilization greater than specified. The default is 0. - numerical_impl_mask (object of type - :class:`linalg.advanced.MatmulNumericalImplFlags`) : Enumerators from - :class:`linalg.advanced.MatmulNumericalImplFlags` combined with bitwise operator - ``|``. The default is all numerical implementation flag choices. + numerical_impl_mask (:class:`nvmath.linalg.advanced.MatmulNumericalImplFlags`): + Enumerators from + :class:`nvmath.nvmath.linalg.advanced.MatmulNumericalImplFlags` combined with + bitwise operator ``|``. The default is all numerical implementation flag + choices. limit (int) : The number of algorithms to consider. If not specified, a suitable default will be chosen. + epilog (:class:`nvmath.linalg.advanced.MatmulEpilogPreferences`): + Epilog preferences (as an object of class + :class:`~nvmath.linalg.advanced.MatmulEpilogPreferences` or a `dict`). + See Also: :meth:`Matmul.plan`, :func:`matmul` """ @@ -188,6 +242,7 @@ class MatmulPlanPreferences: max_waves_count: float | None = 0.0 numerical_impl_mask: MatmulNumericalImplFlags | None = MatmulNumericalImplFlags.ALL limit: int = 8 + epilog: MatmulEpilogPreferences | None = None def __post_init__(self): if self.reduction_scheme_mask is None: @@ -204,6 +259,42 @@ def __post_init__(self): if self.limit is None: self.limit = MatmulPlanPreferences.limit + self.epilog = check_or_create_options(MatmulEpilogPreferences, self.epilog, "epilog preferences") + + +@dataclasses.dataclass +class MatmulQuantizationScales: + """A data class for providing quantization_scales to :class:`Matmul` constructor and the + wrapper function :func:`matmul`. + + Scales can only be set for narrow-precision (FP8 and lower) matrices. + + When ``MatmulOptions.block_scaling=False``, each scale can either be a scalar (integer + or float) or a single-element tensor of shape ``()`` or ``(1,)``. + + When ``MatmulOptions.block_scaling=True``, each scale should be a 1D ``uint8`` tensor + with layout matching the requirements of cuBLAS MXFP8 scaling tensor. Values in the + tensor will be interpreted as UE8M0 values. This means that a value :math:`x` in the + scaling tensor will cause cuBLAS to multiply the respective block by :math:`2^{x-127}`. + + Attributes: + a (float or Tensor) : Scale for matrix A. + + b (float or Tensor) : Scale for matrix B. + + c (float or Tensor) : Scale for matrix C. + + d (float or Tensor) : Scale for matrix D. + + See Also: + :class:`Matmul`, :func:`matmul` + """ + + a : float | None = None + b : float | None = None + c : float | None = None + d : float | None = None + _create_options = enum_utils.create_options_class_from_enum _algo_cap_enum = cublaslt.MatmulAlgoCapAttribute diff --git a/nvmath/_version.py b/nvmath/linalg/advanced/helpers/__init__.py similarity index 72% rename from nvmath/_version.py rename to nvmath/linalg/advanced/helpers/__init__.py index 506a64f..bc833c9 100644 --- a/nvmath/_version.py +++ b/nvmath/linalg/advanced/helpers/__init__.py @@ -2,4 +2,4 @@ # # SPDX-License-Identifier: Apache-2.0 -__version__ = "0.2.1" +from . import matmul as matmul # noqa: F403 diff --git a/nvmath/linalg/advanced/helpers/matmul.py b/nvmath/linalg/advanced/helpers/matmul.py new file mode 100644 index 0000000..fe47c05 --- /dev/null +++ b/nvmath/linalg/advanced/helpers/matmul.py @@ -0,0 +1,173 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + +from nvmath._internal.tensor_wrapper import wrap_operand +from nvmath._internal.utils import create_empty_tensor, infer_object_package, get_or_create_stream + +__all__ = ["create_mxfp8_scale", "invert_mxfp8_scale", "get_mxfp8_scale_offset", "apply_mxfp8_scale"] + + +def _validate_tensor(x, where, tensor_name="tensor", dtype=None): + """ + Validate the tensor package and dtype. + Args: + x: wrapped tensor object + where: name of the function that is performing the validation + tensor_name: name of the tensor to use in the error messages + dtype: if not None, check that the object dtype matches the specified dtype + """ + package = infer_object_package(x) + if package != "torch": + raise ValueError( + f"Only torch.Tensor is currently supported by function '{where}'; the " + f"specified {tensor_name} belongs to '{package}' package." + ) + x = wrap_operand(x) + if dtype is not None and x.dtype != dtype: + raise ValueError( + f"The function '{where}' requires the specified {tensor_name} to have dtype " + f"'{dtype}', whereas it has dtype '{x.dtype}'." + ) + + +def _validate_mxfp8_scale(scale, where, x=None): + if x is not None: + _validate_tensor(x, where) + x = wrap_operand(x) + _validate_tensor(scale, where, tensor_name="scale tensor", dtype="uint8") + scale = wrap_operand(scale) + + if x is not None and scale.shape != (x.size // 32,): + raise ValueError( + f"The shape of scale {scale.shape} is not compatible with a tensor of shape {x.shape}. " + f"The expected scale shape is {(x.size // 32,)}." + ) + return scale + + +def create_mxfp8_scale(x, exponent, stream=None): + """ + Create MXFP8 block scale with the same value for the whole tensor ``x``. + + Args: + x: The tensor to create the block scale for + + exponent: An integer from [-127, 128] range. Effective scale will be ``2^exponent``. + + stream: Optional stream to create the block scale on. + Defaults to the stream of ``x``. + + Returns: + An MXFP8 block scale factors tensor to be used with MXFP8 computations. + """ + _validate_tensor(x, "create_mxfp8_scale") + x = wrap_operand(x) + + if not -127 <= exponent <= 128: + raise ValueError("The exponent should be an integer from [-127, 128] range.") + + stream_holder = get_or_create_stream(x.device_id, stream, x.name) + scale = create_empty_tensor( + x.__class__, (x.size // 32,), "uint8", device_id=x.device, stream_holder=stream_holder, verify_strides=False + ) + scale.tensor[:] = exponent + 127 + return scale.tensor + + +def invert_mxfp8_scale(scale): + """ + Compute a reciprocal of MXFP8 block scale. + + Args: + scale: MXFP8 block scale tensor. + + Returns: + An MXFP8 block scale factors tensor with reciprocals of the values in ``scale``. + """ + _validate_mxfp8_scale(scale, "invert_mxfp8_scale") + + scale[scale == 255] = 254 # Prevent the overflow + return (127 + 127) - scale + + +def get_mxfp8_scale_offset(x, index): + """ + Computes the offset of MXFP8 scale used for element ``x[index]``. + + Args: + x: The tensor to which ``index`` referes. + + index: A tuple of tensor indices. This function supports broadcasting, + so the `index` can be a tuple of integers or a tuple of tensors. + + Returns: + A single integer indicating an offset to the MXFP8 block scale factor which + is applied to ``x[index]`` during scaling. + + Note: + In typical use-cases, there should be no need to manually modify MXFP8 scales. + The scales returned as ``"d_out_scale"`` by one multiplication can be directly + reused as input scales for another multiplication. + """ + + _validate_tensor(x, where="get_mxfp8_scale_offset") + x = wrap_operand(x) + ndim = len(x.shape) + if len(index) != ndim: + raise ValueError("Index length should match the number of dimensions of x.") + + if ndim == 2: + batch_offset = 0 + elif ndim > 2: + # Compute batch offset + batch_strides = x.strides[:-2] + batch_index = index[:-2] + batch_offset = sum(i * stride for i, stride in zip(batch_index, batch_strides, strict=True)) // min(batch_strides) + else: + raise ValueError(f"Got {ndim}-D tensor in `get_mxfp8_scale_offset`, but expected at least 2-D.") + major_d, minor_d = (-2, -1) if x.strides[-2] > x.strides[-1] else (-1, -2) + major, minor, minor_length = index[major_d], index[minor_d], x.shape[minor_d] + + # Compute tile offset + tile_minor = minor // 128 + tile_major = major // 128 + tile_offset = (minor_length // 128) * tile_major + tile_minor + minor = minor % 128 + major = major % 128 + + # Compute offset in the tile + minor = minor // 32 + offset = (major % 32) * 16 + (major // 32) * 4 + minor + + # Add the offsets together + tile_size = 128 * 128 // 32 + matrix_size = x.shape[-1] * x.shape[-2] // 32 + return batch_offset * matrix_size + tile_offset * tile_size + offset + + +def apply_mxfp8_scale(x, scale): + """ + Apply MXFP8 block scale factors to tensor ``x``. + + Args: + x: The tensor to which the scaling should be applied. + + scale: The block scale factors to apply. + + Returns: + A ``float32`` tensor with values of ``x`` with scales applied. + + Note: + This function is not intended for production usage due to its relatively low + performance and high memory consumption. Instead of applying the scales + manually using this function, use + :attr:`~nvmath.linalg.advanced.MatmulOptions.result_type` to request non-FP8 output. + """ + scale = _validate_mxfp8_scale(scale, "apply_mxfp8_scale", x=x) + import torch + + idx = get_mxfp8_scale_offset(x, torch.meshgrid(*(torch.arange(d) for d in x.shape), indexing="ij")).to(x.device) + + actual_scale = 2 ** (scale.tensor.type(torch.float32)[idx] - 127) + return x.type(torch.float32) * actual_scale diff --git a/nvmath/linalg/advanced/matmulmod.py b/nvmath/linalg/advanced/matmulmod.py index 05324bf..2e13c4b 100644 --- a/nvmath/linalg/advanced/matmulmod.py +++ b/nvmath/linalg/advanced/matmulmod.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -9,7 +9,6 @@ from dataclasses import dataclass import functools import logging -import math import operator from collections.abc import Sequence @@ -33,17 +32,16 @@ from nvmath.linalg._internal import matmul_desc_ifc, matmul_pref_ifc, matrix_layout_ifc from nvmath.linalg._internal.typemaps import ( - NAME_TO_DEFAULT_SCALE_TYPE, - NAME_TO_DEFAULT_COMPUTE_TYPE, + NAMES_TO_DEFAULT_SCALE_TYPE, + NAMES_TO_DEFAULT_COMPUTE_TYPE, COMPUTE_TYPE_TO_DEFAULT_SCALE_TYPE, SCALE_TYPE_TO_DEFAULT_COMPUTE_TYPE, + SUPPORTED_TYPES, ) from nvmath.linalg._internal.utils import ( axis_order_in_memory, calculate_strides, check_batch_tileable, - create_handle, - destroy_handle, get_handle, pointer_aligned_to, ) @@ -268,7 +266,7 @@ def get_mm_traits(a_layout, b_layout, c_layout, logger): ) raise ValueError(message) logger.debug( - f"The batch layout for A corresponding to shape = {a_batch_shape} and strides = {a_batch_strides} IS " "tileable." + f"The batch layout for A corresponding to shape = {a_batch_shape} and strides = {a_batch_strides} IS tileable." ) batch_shape = a_batch_shape batch_axis_order = a_batch_axis_order = axis_order_in_memory(a_batch_strides) @@ -281,7 +279,7 @@ def get_mm_traits(a_layout, b_layout, c_layout, logger): ) raise ValueError(message) logger.debug( - f"The batch layout for B corresponding to shape = {b_batch_shape} and strides = {b_batch_strides} IS " "tileable." + f"The batch layout for B corresponding to shape = {b_batch_shape} and strides = {b_batch_strides} IS tileable." ) batch_shape = b_batch_shape batch_axis_order = b_batch_axis_order = axis_order_in_memory(b_batch_strides) @@ -462,17 +460,10 @@ def get_result_traits(mm_traits, epilog_ordering, logger): supported types are :class:`numpy.ndarray`, :class:`cupy.ndarray`, and :class:`torch.Tensor`.""".replace("\n", " "), # "c_admonitions": """ - .. note:: - The broadcasting behavior of a 1-D (vector) `c` deviates from the - equivalent NumPy expression. With nvmath-python, `c` is internally - promoted to shape (M, 1) in order to broadcast with ``a @ b``; this matches the - behavior of cuBLASLt. With NumPy, a 1-D `c` behaves as if it has shape - (1, N) in the expression ``a @ b + c``. - - .. deprecated:: 0.2.1 - In order to avoid broadcasting behavior ambiguity, nvmath-python will no longer - accept a 1-D (vector) `c` starting in version 0.3.0. Use a singleton - dimension to convert your input array to 2-D. + .. versionchanged:: 0.3.0 + In order to avoid broadcasting behavior ambiguity, nvmath-python no longer + accepts a 1-D (vector) `c`. Use a singleton dimension to convert your input + array to 2-D. """, # "alpha": """\ @@ -482,6 +473,13 @@ def get_result_traits(mm_traits, epilog_ordering, logger): "beta": """\ The scale factor for the matrix addition term as a real or complex number. A value for `beta` must be provided if operand `c` is specified.""".replace("\n", " "), + # + "quantization_scales": """\ +Specify scale factors for the matrix multiplication as a :class:`~nvmath.linalg.advanced.MatmulQuantizationScales` +object. Alternatively, a `dict` containing the parameters for the +:class:`~nvmath.linalg.advanced.MatmulQuantizationScales` +constructor can also be provided. +Allowed and required only for narrow-precision (FP8 and lower) operations.""".replace("\n", " "), # "algorithms": """\ A sequence of :class:`Algorithm` objects that can be directly provided to bypass planning. The algorithm objects must be @@ -520,9 +518,45 @@ def get_result_traits(mm_traits, epilog_ordering, logger): "result": """\ The result of the specified matrix multiplication (epilog applied), which remains on the same device and belong to the same package as the input operands. If an epilog (like :attr:`nvmath.linalg.advanced.MatmulEpilog.RELU_AUX`) that -results in extra output is used, a tuple is returned with the first element being the matrix multiplication result -(epilog applied) and the second element being the auxiliary output provided by the selected epilog as a -`dict`.""".replace("\n", " "), +results in extra output is used, or an extra output is requested (for example by setting +:attr:`~nvmath.linalg.advanced.MatmulOptions.result_amax` option in ``options`` argument), +a tuple is returned with the first element being the matrix multiplication result (epilog applied) and the second element +being the auxiliary output provided as a `dict`. """.replace("\n", " "), + # + "narrow_precision": """\ + Matrix multiplication with narrow-precision operands is supported, in both FP8 and MXFP8 formats. + + .. note:: + + Narrow-precision matrix multiplication in nvmath-python requires **CUDA Toolkit 12.8 or newer**. + **FP8 requires a device with compute capability 8.9 or higher** (Ada, Hopper, Blackwell or newer architecture). + **MXFP8 requires a device with compute capability 10.0 or higher** (Blackwell or newer architecture). + Please refer to the `compute capability table `_ + to check the compute capability of your device. + + For FP8 operations: + + * For each operand a scaling factor needs to be specified via ``quantization_scales`` argument. + * Maximum absolute value of the result (amax) can be requested via + :attr:`~nvmath.linalg.advanced.MatmulOptions.result_amax` option in ``options`` argument. + * Custom result type (both FP8 and non-FP8) can be requested via + :attr:`~nvmath.linalg.advanced.MatmulOptions.result_type` option in ``options`` argument. + + For MXFP8 operations: + + * To enable MXFP8 operations, :attr:`~nvmath.linalg.advanced.MatmulOptions.block_scaling` option + must be set to ``True``. + * Block scaling factors need to be specified via ``quantization_scales`` argument. + * Utilities in :mod:`nvmath.linalg.advanced.helpers.matmul` can be used to create and modify + block scaling factors. + * When MXFP8 is used and the result type is a narrow-precision data type, the auxiliary output + ``"d_out_scale"`` will be returned in the auxiliary output tensor. It will contain the scales + that were used for the result quantization. + + Please refer to the examples and narrow-precision operations tutorial for more details. + For more details on the FP8 and MXFP8 formats in cuBLAS, + see the `cublasLtMatmul documentation `_. +""".strip(), # "semantics": """\ The semantics of the matrix multiplication follows :func:`numpy.matmul` semantics, with some restrictions on @@ -535,10 +569,10 @@ def get_result_traits(mm_traits, epilog_ordering, logger): multiplication, the appended ``1`` is removed from the result's dimensions. * If `a` or `b` is N-D (N > 2), then the operand is treated as a batch of matrices. If both `a` and `b` are N-D, their batch dimensions must match. If exactly one of `a` or `b` is N-D, the other operand is broadcast. - * The operand for the matrix addition `c` may be a vector of length M, a matrix of shape (M, 1) or (M, N), or - batched versions of the latter (..., M, 1) or (..., M, N). Here M and N are the dimensions of the result of - the matrix multiplication. If a vector is provided or N = 1, the columns of `c` are broadcast for the - addition. If batch dimensions are not present, `c` is broadcast across batches as needed. + * The operand for the matrix addition `c` may be a matrix of shape (M, 1) or (M, N), or the batched versions + (..., M, 1) or (..., M, N). Here M and N are the dimensions of the result of the matrix multiplication. If N = 1, the + columns of `c` are broadcast for the addition; the rows of `c` are never broadcast. If batch dimensions are not + present, `c` is broadcast across batches as needed. * Similarly, when operating on a batch, auxiliary outputs are 3-D for all epilogs. Therefore, epilogs that return 1-D vectors of length N in non-batched mode return 3-D matrices of size (batch, N, 1) in batched mode. """.strip(), @@ -612,9 +646,14 @@ class Matmul: stream: {stream} + quantization_scales: {quantization_scales} + Semantics: {semantics} + Narrow-precision support: + {narrow_precision} + See Also: :meth:`autotune`, :meth:`plan`, :meth:`reset_operands`, :meth:`execute` @@ -697,16 +736,35 @@ class Matmul: directory. """ - def __init__(self, a, b, /, c=None, *, alpha=None, beta=None, qualifiers=None, options=None, stream=None): + def __init__( + self, + a, + b, + /, + c=None, + *, + alpha=None, + beta=None, + qualifiers=None, + quantization_scales=None, + options=None, + stream=None, + ): options = utils.check_or_create_options(_configuration.MatmulOptions, options, "Matrix multiplication options") self.options = options self.logger = options.logger if options.logger is not None else logging.getLogger() + def check_dtype(dtype, operand_name): + if dtype not in SUPPORTED_TYPES: + raise ValueError(f"The dtype of operand {operand_name} ({dtype}) is not supported.") + # The matrix multiplication has two required operands 'a' and 'b', and one optional # operand 'c'. a = tensor_wrapper.wrap_operand(a) b = tensor_wrapper.wrap_operand(b) + check_dtype(a.dtype, "A") + check_dtype(b.dtype, "B") self.logger.info("= SPECIFICATION PHASE =") self.logger.info(f"The data type of operand A is '{a.dtype}', and that of operand B is '{b.dtype}'.") @@ -714,14 +772,23 @@ def __init__(self, a, b, /, c=None, *, alpha=None, beta=None, qualifiers=None, o if c is not None: self.num_operands = 3 c = tensor_wrapper.wrap_operand(c) + if len(c.shape) < 2: + raise ValueError( + "In order to avoid broadcasting behavior ambiguity, `c` must be at least 2-D. " + "Use a singleton dimension to convert your input array to 2-D." + ) + check_dtype(c.dtype, "C") self.logger.info(f"The data type of operand C is {c.dtype}.") if c is not None and beta is None: raise ValueError("A value for beta must be provided if operand C is provided.") - if a.dtype != b.dtype: - raise ValueError(f"The dtype of operands A {a.dtype} and B {b.dtype} must be the same.") - self.ab_dtype_name = a.dtype + if (a.dtype, b.dtype) not in NAMES_TO_DEFAULT_SCALE_TYPE: + raise ValueError(f"Unsupported combination of dtypes for operands A {a.dtype} and B {b.dtype}.") + + # Currently, a.dtype != b.dtype is only supported for FP8 (different FP8 kinds are + # allowed), so we assume that A and B have equal width. + self.input_type_width = typemaps.NAME_TO_DATA_WIDTH[a.dtype] assert self.num_operands == 2 or self.num_operands == 3, "Internal Error." @@ -745,8 +812,7 @@ def __init__(self, a, b, /, c=None, *, alpha=None, beta=None, qualifiers=None, o self.memory_space = "cpu" self.device_id = options.device_id self.logger.info( - f"The input operands' memory space is {self.memory_space}, and the execution space is on device " - f"{self.device_id}." + f"The input operands' memory space is {self.memory_space}, and the execution space is on device {self.device_id}." ) # Allocate device memory (in stream context) if needed. @@ -808,40 +874,52 @@ def __init__(self, a, b, /, c=None, *, alpha=None, beta=None, qualifiers=None, o # Determine the data types for a and b. self.a_dtype = typemaps.NAME_TO_DATA_TYPE[a.dtype] self.b_dtype = typemaps.NAME_TO_DATA_TYPE[b.dtype] + self.a_dtype_name = a.dtype + self.b_dtype_name = b.dtype + + self.is_complex = "complex" in self.a_dtype_name or "complex" in self.b_dtype_name + + # Determine the data types for c and d. + self.d_dtype = options.result_type + if self.num_operands == 3: + self.c_dtype = typemaps.NAME_TO_DATA_TYPE[c.dtype] + if self.d_dtype is None: + self.d_dtype = self.c_dtype + elif self.num_operands == 2: + if self.d_dtype is None: + self.d_dtype = self.a_dtype + if self.d_dtype in (CudaDataType.CUDA_R_8F_E5M2, CudaDataType.CUDA_R_8F_E4M3): + self.c_dtype = CudaDataType.CUDA_R_16F + else: + self.c_dtype = self.d_dtype + self.c_dtype_name = typemaps.DATA_TYPE_TO_NAME[self.c_dtype] + self.d_dtype_name = typemaps.DATA_TYPE_TO_NAME[self.d_dtype] + self.c_dtype_width = typemaps.NAME_TO_DATA_WIDTH[self.c_dtype_name] + self.d_dtype_width = typemaps.NAME_TO_DATA_WIDTH[self.d_dtype_name] - # Determine the data type for c (if not provided) and d. The two must match. - if self.num_operands == 2: - self.d_dtype_name, self.d_dtype = self.ab_dtype_name, typemaps.NAME_TO_DATA_TYPE[self.ab_dtype_name] - self.c_dtype = self.d_dtype - self.c_dtype_name = self.d_dtype_name - else: - self.c_dtype_name = c.dtype - self.c_dtype = typemaps.NAME_TO_DATA_TYPE[self.c_dtype_name] - self.d_dtype = self.c_dtype - self.d_dtype_name = typemaps.DATA_TYPE_TO_NAME[self.d_dtype] self.logger.info(f"The data type for the result D is '{self.d_dtype_name}'.") def assert_valid_compute_type(compute_type): - if compute_type not in cublas.ComputeType: - message = f"Unsupported compute type. The compute type '{compute_type}' is currently not supported." + if compute_type not in COMPUTE_TYPE_TO_DEFAULT_SCALE_TYPE["real"]: + message = f"Unsupported compute type. The compute type '{repr(compute_type)}' is currently not supported." raise ValueError(message) # Determine the scale type. if options.scale_type is None: if options.compute_type is not None: assert_valid_compute_type(options.compute_type) - if "complex" in self.ab_dtype_name: + if self.is_complex: scale_type_map = COMPUTE_TYPE_TO_DEFAULT_SCALE_TYPE["complex"] else: scale_type_map = COMPUTE_TYPE_TO_DEFAULT_SCALE_TYPE["real"] self.scale_type = scale_type_map[options.compute_type] else: - self.scale_type = NAME_TO_DEFAULT_SCALE_TYPE[self.ab_dtype_name] + self.scale_type = NAMES_TO_DEFAULT_SCALE_TYPE[(self.a_dtype_name, self.b_dtype_name)] self.scale_type_name = typemaps.DATA_TYPE_TO_NAME[self.scale_type] else: self.scale_type = options.scale_type - if self.scale_type not in typemaps.DATA_TYPE_TO_NAME: - message = f"Unsupported scale type. The data type '{self.scale_type}' is currently not supported." + if self.scale_type not in SCALE_TYPE_TO_DEFAULT_COMPUTE_TYPE: + message = f"Unsupported scale type. The data type '{repr(self.scale_type)}' is currently not supported." raise ValueError(message) self.scale_type_name = typemaps.DATA_TYPE_TO_NAME[self.scale_type] self.logger.info(f"The scale type is '{self.scale_type_name}'.") @@ -851,38 +929,52 @@ def assert_valid_compute_type(compute_type): if options.scale_type is not None: self.compute_type = SCALE_TYPE_TO_DEFAULT_COMPUTE_TYPE[options.scale_type] else: - self.compute_type = NAME_TO_DEFAULT_COMPUTE_TYPE[self.ab_dtype_name] + self.compute_type = NAMES_TO_DEFAULT_COMPUTE_TYPE[(self.a_dtype_name, self.b_dtype_name)] else: self.compute_type = options.compute_type assert_valid_compute_type(self.compute_type) self.logger.info(f"The compute type is {self.compute_type.name}.") - def is_supported(dtype, compute_type, scale_type): + def is_supported(atype, btype, compute_type, scale_type): ct = cublas.ComputeType st = CudaDataType + abtype = atype if atype == btype else (atype, btype) if compute_type in (ct.COMPUTE_16F, ct.COMPUTE_16F_PEDANTIC): - return scale_type == st.CUDA_R_16F and dtype == "float16" - elif compute_type in (ct.COMPUTE_32F, ct.COMPUTE_32F_PEDANTIC): + return scale_type == st.CUDA_R_16F and abtype == "float16" + elif compute_type == ct.COMPUTE_32F_PEDANTIC: + if scale_type == st.CUDA_R_32F: + return abtype in ("float32", "bfloat16", "float16", "float8_e4m3fn", "float8_e5m2") + elif scale_type == st.CUDA_C_32F: + return abtype == "complex64" + elif compute_type == ct.COMPUTE_32F: if scale_type == st.CUDA_R_32F: - return dtype in ("float32", "bfloat16", "float16") + return abtype in ( + "float32", + "bfloat16", + "float16", + "float8_e4m3fn", + "float8_e5m2", + ("float8_e4m3fn", "float8_e5m2"), + ("float8_e5m2", "float8_e4m3fn"), + ) elif scale_type == st.CUDA_C_32F: - return dtype == "complex64" + return abtype == "complex64" elif compute_type in (ct.COMPUTE_32F_FAST_16F, ct.COMPUTE_32F_FAST_16BF, ct.COMPUTE_32F_FAST_TF32): if scale_type == st.CUDA_R_32F: - return dtype == "float32" + return abtype == "float32" if scale_type == st.CUDA_C_32F: - return dtype == "complex64" + return abtype == "complex64" elif compute_type in (ct.COMPUTE_64F, ct.COMPUTE_64F_PEDANTIC): if scale_type == st.CUDA_R_64F: - return dtype == "float64" + return abtype == "float64" if scale_type == st.CUDA_C_64F: - return dtype == "complex128" + return abtype == "complex128" return False - if not is_supported(self.ab_dtype_name, self.compute_type, self.scale_type): + if not is_supported(self.a_dtype_name, self.b_dtype_name, self.compute_type, self.scale_type): raise ValueError( f"Selected scale_type={repr(self.scale_type)} compute_type={repr(self.compute_type)} " - + f"are not supported for data type {self.ab_dtype_name}" + + f"are not supported for data types {self.a_dtype_name} (A) and {self.b_dtype_name} (B)." ) # Set alpha and beta. @@ -900,6 +992,27 @@ def is_supported(dtype, compute_type, scale_type): except (ValueError, TypeError) as e: raise ValueError(f"The value provided for beta {beta} is not convertible to dtype '{self.beta.dtype}'.") from e + # Set narrow-precision (FP8 and lower) quantization_scales. + if self.input_type_width <= 8: + self.quantization_scales = self._validate_operand_scales(quantization_scales, all_required=True) + elif quantization_scales is not None: + self.logger.warning( + "Matmul: The provided scales are ignored, since they are only applicable to narrow-precision (FP8 and lower) " + "operations." + ) + + if self.options.result_amax and self.d_dtype_width > 8: + raise ValueError("result_amax=True is allowed only for narrow-precision (FP8 and lower) results") + + # Check operands alignment if needed + if self.input_type_width <= 8: + for operand, operand_name in zip(self.operands, "ABC", strict=False): + if operand.data_ptr % 16 != 0: + raise ValueError( + f"For narrow-precision (FP8 and lower) multiplication, operand {operand_name} should be aligned to 16 " + "bytes." + ) + # Capture operand extents and strides for consistency check when resetting operands. self.operand_extents = tuple(o.shape for o in self.operands) self.operand_strides = tuple(o.strides for o in self.operands) @@ -909,6 +1022,12 @@ def is_supported(dtype, compute_type, scale_type): b_layout = MatrixLayout(self.operands[1].shape, self.operands[1].strides, self.qualifiers[1]["is_conjugate"]) c_layout = MatrixLayout(self.operands[2].shape, self.operands[2].strides) if self.num_operands == 3 else None + # Enforce equal batch shape for A and B if block_scaling=True. + if self.options.block_scaling and a_layout.shape[:-2] != b_layout.shape[:-2]: + raise ValueError( + "When block_scaling=True, the batch dimensions of A and B must match (broadcasting is not supported)." + ) + # Get the operation traits. self.mm_traits = get_mm_traits(a_layout, b_layout, c_layout, self.logger) self.result_traits = None # Wait till planning to determine this based on the epilog. @@ -942,6 +1061,14 @@ def is_supported(dtype, compute_type, scale_type): self.mm_desc_ifc.fast_accum = options.fast_accumulation self.logger.info(f"The flag for fast accumulation mode is {options.fast_accumulation}.") + if self.input_type_width == 8 and version < 120800: + raise ValueError( + f"FP8 is not supported for cuBLASLt version {version}. cuBLASLt version 12.8 or higher is required." + ) + + # Planning preferences + self.preferences = None + # Epilog attributes. self.epilog = None @@ -960,6 +1087,9 @@ def is_supported(dtype, compute_type, scale_type): # Keep track of epilog output handlers to allocate output in execute(). self.epilog_output_handlers = [] + # Non-epilog aux outputs. Currently, only used for quantization outputs (amax etc.) + self.aux_outputs = None + # Plan attributes. self.preference_ptr = None self.a_layout_ptr, self.b_layout_ptr, self.c_layout_ptr, self.d_layout_ptr = None, None, None, None @@ -979,6 +1109,9 @@ def is_supported(dtype, compute_type, scale_type): self.workspace_stream = None self.last_compute_event = None + # Device-side array with the quantization_scales + self.quantization_scales_device = {} + self.valid_state = True self.logger.info("The Matmul operation has been created.") @@ -1018,7 +1151,7 @@ def _free_plan_resources(self, exception: Exception | None = None) -> bool: if self.b_layout_ptr is not None: cublaslt.matrix_layout_destroy(self.b_layout_ptr) self.b_layout_ptr = None - if self.num_operands == 3 and self.c_layout_ptr is not None: # Note that c layout aliases with that of d. + if self.c_layout_ptr != self.d_layout_ptr and self.c_layout_ptr is not None: cublaslt.matrix_layout_destroy(self.c_layout_ptr) self.c_layout_ptr = None if self.d_layout_ptr is not None: @@ -1079,6 +1212,7 @@ def _release_workspace_memory_perhaps_wrapper(self, exception: Exception | None This is used in @atomic. """ self._release_workspace_memory_perhaps(release_workspace=self.workspace_allocated_here) + self._reset_workspace_allocation_tracking() return True @utils.precondition(_check_valid_matmul) @@ -1149,6 +1283,126 @@ def applicable_algorithm_ids(self, limit=8): ) return algo_ids + def _validate_operand_scales(self, quantization_scales, all_required): + """ + Validates the user-provided quantization scales and wraps them converts them to + MatmulQuantizationScales if needed. + """ + if quantization_scales is None: + raise ValueError( + "Scales are required for narrow-precision (FP8 and lower) operations. Please set `quantization_scales` " + "argument." + ) + quantization_scales = utils.check_or_create_options( + _configuration.MatmulQuantizationScales, quantization_scales, "Scale factors" + ) + expected_scales = "AB" + if self.d_dtype_width <= 8 and not self.options.block_scaling: + expected_scales += "D" + elif quantization_scales.d is not None: + if self.options.block_scaling: + raise ValueError("Quantization scaling is not supported for D when `block_scaling` option is enabled.") + if self.d_dtype_width > 8: + raise ValueError( + "Quantization scaling is not supported for D when it is not a narrow-precision (FP8 and lower) type." + ) + if self.num_operands == 3 and self.c_dtype_width <= 8: + expected_scales += "C" + elif quantization_scales.c is not None: + raise ValueError( + "Quantization scaling is not supported for C when it is not a narrow-precision (FP8 and lower) type." + ) + if all_required: + for operand in expected_scales: + if getattr(quantization_scales, operand.lower()) is None: + raise ValueError(f"Scale for {operand.upper()} is not specified") + return quantization_scales + + def _validate_epilog_aux_scale(self, aux_quantization_scale, *, required): + is_fp8_aux = ( + self.preferences.epilog.aux_type is not None + and typemaps.NAME_TO_DATA_WIDTH[typemaps.DATA_TYPE_TO_NAME[self.preferences.epilog.aux_type]] <= 8 + ) + if aux_quantization_scale is not None and not is_fp8_aux: + raise ValueError( + "Scales for epilog auxiliary output are not supported when `preferences.epilog.aux_type` is not set to a " + "narrow-precision type." + ) + elif aux_quantization_scale is None and is_fp8_aux and required: + raise ValueError( + '"aux_quantization_scale" epilog input is required when `preferences.epilog.aux_type` is not set to a ' + "narrow-precision type." + ) + + def _prepare_quantization_scale(self, scale, operand, cublas_operand, operand_size=None): + if scale is None: + return + elif isinstance(scale, int | float): + if self.options.block_scaling: + raise ValueError("A scalar tensor-wide scale factor is not allowed when block_scaling=True.") + # If it's a scalar, copy to GPU. Float32 is the only type allowed by + # cublasLtMatmulScale_t for tensor-wide scaling. + self.logger.debug(f"Scale for {operand.upper()} will be copied to device {self.device_id}.") + self.quantization_scales_device[operand] = tensor_wrapper.wrap_operand(cp.asarray([scale], dtype="float32")) + else: + if utils.infer_object_package(scale) != self.package: + raise TypeError("The quantization scaling tensors must belong to the same package as the operands.") + self.quantization_scales_device[operand] = tensor_wrapper.wrap_operand(scale) + device_id = self.quantization_scales_device[operand].device_id + if device_id is not None and self.device_id != device_id: + raise ValueError(f"The scales must be on the same device ({device_id}) as the operands ({self.device_id}).") + if self.quantization_scales_device[operand].device in (None, "cpu"): + # If it's on CPU, copy to GPU + self.logger.debug(f"Scale for {operand.upper()} will be copied to device {self.device_id}.") + self.quantization_scales_device[operand] = tensor_wrapper.wrap_operand( + self.quantization_scales_device[operand].to(self.device_id) + ) + if not self.options.block_scaling: + if self.quantization_scales_device[operand].shape not in ((1,), ()): + raise ValueError( + f"The provided {operand.upper()} scale tensor has to be of shape (1,) or (). " + f"Got {self.quantization_scales_device[operand].shape} instead." + ) + if self.quantization_scales_device[operand].dtype != "float32": + raise ValueError( + f"The provided {operand.upper()} scale tensor has to be float32 type. " + f"Got {self.quantization_scales_device[operand].dtype} instead." + ) + elif self.input_type_width == 8: + if operand_size is None: + raise ValueError(f"Block scaling is not supported for {operand.upper()} scale.") + expected_shape = (operand_size // 32,) + if self.quantization_scales_device[operand].shape != expected_shape: + raise ValueError( + f"Scales for {operand.upper()} should have shape {expected_shape}. " + f"Got {self.quantization_scales_device[operand].shape}." + ) + if self.quantization_scales_device[operand].dtype != "uint8": + raise ValueError(f"Block scales for {operand.upper()} should be uint8 tensor.") + else: + raise ValueError("block_scaling == True is not supported for non-FP8 types.") + setattr(self.mm_desc_ifc, f"{cublas_operand}_scale_pointer", self.quantization_scales_device[operand].data_ptr) + if self.options.block_scaling: + self.logger.debug(f"Using VEC32_UE8M0 scale mode for operand {operand.upper()}.") + setattr(self.mm_desc_ifc, f"{cublas_operand}_scale_mode", cublaslt.MatmulMatrixScale.VEC32_UE8M0) + else: + self.logger.debug(f"Using SCALAR_32F scale mode for operand {operand.upper()}.") + setattr(self.mm_desc_ifc, f"{cublas_operand}_scale_mode", cublaslt.MatmulMatrixScale.SCALAR_32F) + + def _prepare_operand_quantization_scales(self, scales): + """ + Copies the scales to the GPU and updates the pointers in mm_desc_ifc. + """ + for operand in "abcd": + scale = getattr(scales, operand) + if self.options.block_scaling and operand == "a": + operand_size = self.operands[0].size + elif self.options.block_scaling and operand == "b": + operand_size = self.operands[1].size + else: + operand_size = None + self._prepare_quantization_scale(scale, operand, cublas_operand=operand, operand_size=operand_size) + @utils.precondition(_check_valid_matmul) @utils.atomic(_free_plan_resources, method=True) def plan( @@ -1246,6 +1500,11 @@ def plan( self.epilog_input_name_to_handler = dict() # Clear input name to handler map as well, self.epilog_inputs_traits = dict() # ... and the input traits as well. + preferences = utils.check_or_create_options( + _configuration.MatmulPlanPreferences, preferences, "Matrix multiplication plan preferences" + ) + self.preferences = preferences + mm_traits = self.mm_traits stream_holder = utils.get_or_create_stream(self.device_id, stream, self.package) @@ -1257,7 +1516,7 @@ def plan( if epilog is None and epilog_inputs is not None: self.logger.warning( - f"Matmul: The provided epilog inputs {epilog_inputs.keys()} are ignored since an epilog is not " "specified." + f"Matmul: The provided epilog inputs {epilog_inputs.keys()} are ignored since an epilog is not specified." ) self.epilog = epilog @@ -1294,11 +1553,47 @@ def plan( ): msg = f"The epilog {epilog.name} requires input matrix 'c' to be F-contiguous (column-major)." raise ValueError(msg) + if ( + version < 120804 + # A has one row + and self.mm_traits.M == 1 + # C is broadcast + and self.mm_traits.c_layout_traits is not None + and self.mm_traits.c_layout_traits.ld == 0 + # Using both an bias epilog and C + and self.epilog & _configuration.MatmulEpilog.BIAS > 0 + ): + message = ( + "When matrix 'a' has one row, " + "simultaneously broadcasting matrix 'c' and using a BIAS epilog requires cublaslt >= 120804; " + f"You have version {version}. Update to CUDA Toolkit >= 12.8.1." + ) + raise ValueError(message) + + # Take a copy of the user-provided inputs. + if epilog_inputs is not None: + epilog_inputs = epilog_inputs.copy() + else: + epilog_inputs = {} + + # Get the dtype of auxiliary buffer + aux_dtype_name = ( + typemaps.DATA_TYPE_TO_NAME[self.preferences.epilog.aux_type] + if self.preferences.epilog.aux_type is not None + else None + ) + + # Extract aux quantization scale from the inputs. + aux_quantization_scale = ( + epilog_inputs.pop("aux_quantization_scale") if "aux_quantization_scale" in epilog_inputs else None + ) + self._validate_epilog_aux_scale(aux_quantization_scale, required=True) + self._prepare_quantization_scale(aux_quantization_scale, "epilog_aux", cublas_operand="epilogue_aux") epilog_input_handler_types = EPILOG_INPUT_HANDLERS_MAP[epilog] if epilog_input_handler_types: epilog_input_handlers = [ - handler_type(self.logger, mm_traits, epilog, self.d_dtype_name) + handler_type(self.logger, mm_traits, epilog, self.c_dtype_name, self.d_dtype_name, aux_dtype_name) for handler_type in epilog_input_handler_types ] @@ -1311,19 +1606,13 @@ def plan( required_epilog_input_names = {h.name for h in epilog_input_handlers} self.logger.info(f"The epilog requires the following additional inputs: {required_epilog_input_names}.") - if required_epilog_input_names and epilog_inputs is None: - raise ValueError( - f"The epilog {epilog.name} requires the following input tensors: {required_epilog_input_names}." - ) - if required_epilog_input_names != set(epilog_inputs.keys()): raise ValueError( f"The epilog {epilog.name} requires the following input tensors: " f"{required_epilog_input_names}. The provided tensor names are: {epilog_inputs.keys()}" ) - # Wrap epilog inputs. Take a copy of the user-provided dict. - epilog_inputs = epilog_inputs.copy() + # Wrap epilog inputs. for name in epilog_inputs: epilog_inputs[name] = tensor_wrapper.wrap_operand(epilog_inputs[name]) @@ -1340,7 +1629,7 @@ def plan( device_id = utils.get_operands_device_id(list(epilog_inputs.values())) if device_id is not None and self.device_id != device_id: raise ValueError( - f"The epilog inputs must be on the same device ({device_id}) as the operands " f"({self.device_id})." + f"The epilog inputs must be on the same device ({device_id}) as the operands ({self.device_id})." ) # Move epilog inputs to the GPU, if needed. @@ -1377,10 +1666,9 @@ def plan( epilog_output_handler_types = EPILOG_OUTPUT_HANDLERS_MAP[epilog] if epilog_output_handler_types: self.epilog_output_handlers = epilog_output_handlers = [ - handler_type(self.logger, mm_traits, epilog, self.d_dtype_name) + handler_type(self.logger, mm_traits, epilog, self.c_dtype_name, self.d_dtype_name, aux_dtype_name) for handler_type in epilog_output_handler_types ] - # Check if the epilog requires a specific result layout, and if the # requirement is consistent for all the handlers. epilog_output_handlers_ordering = {h.order for h in epilog_output_handlers} @@ -1407,19 +1695,23 @@ def plan( f"{self.result_traits.d_layout_traits.batch_offset}." ) - preferences = utils.check_or_create_options( - _configuration.MatmulPlanPreferences, preferences, "Matrix multiplication plan preferences" - ) - # Internally transpose operand A if required (conjugate flag) and create layout. transpose = False - if mm_traits.a_layout_traits.is_conjugate and "complex" in self.ab_dtype_name: + if mm_traits.a_layout_traits.is_conjugate and self.is_complex: self.mm_desc_ifc.transa = cublas.Operation.C transpose = True self.logger.debug( "To conjugate A, the operand A will be internally transposed and the matrix multiplication will be " "performed with OP_C for operand A." ) + if self.input_type_width <= 8: + # narrow-precision (FP8 and lower) data types are only supported for transa=OP_T + self.mm_desc_ifc.transa = cublas.Operation.T + transpose = True + self.logger.debug( + "For narrow-precision (FP8 and lower) multiplication, the operand A will be internally transposed and the " + "matrix multiplication will be performed with OP_T for operand A." + ) m, n, ld, a_order = mm_traits.a_layout_traits.get_mm_layout(transpose=transpose) self.a_layout_ptr = cublaslt.matrix_layout_create(self.a_dtype, rows=m, cols=n, ld=ld) self.logger.debug(f"Layout for A: rows = {m}, cols = {n}, ld = {ld}.") @@ -1427,7 +1719,7 @@ def plan( # Internally transpose operand B if required (conjugate flag, or epilog is BGRADB) # and create layout. transpose = False - if mm_traits.b_layout_traits.is_conjugate and "complex" in self.ab_dtype_name: + if mm_traits.b_layout_traits.is_conjugate and self.is_complex: self.mm_desc_ifc.transb = cublas.Operation.C transpose = True self.logger.debug( @@ -1465,7 +1757,18 @@ def plan( layout_d_ifc.strided_batch_offset = result_traits.d_layout_traits.batch_offset if self.num_operands == 2: - self.c_layout_ptr = self.d_layout_ptr + if self.c_dtype == self.d_dtype: + # If C and D have equal types, reuse the layout. + self.c_layout_ptr = self.d_layout_ptr + else: + # Otherwise, create a D-like layout, but with different type. + self.c_layout_ptr = cublaslt.matrix_layout_create( + self.c_dtype, rows=mm_traits.M, cols=mm_traits.N, ld=result_traits.d_layout_traits.ld + ) + layout_c_ifc = matrix_layout_ifc.MatrixLayoutInterface(self.c_layout_ptr) + layout_c_ifc.order = result_traits.d_layout_traits.order + layout_c_ifc.batch_count = mm_traits.batch_count + layout_c_ifc.strided_batch_offset = result_traits.d_layout_traits.batch_offset else: self.c_layout_ptr = cublaslt.matrix_layout_create( self.c_dtype, rows=mm_traits.M, cols=mm_traits.N, ld=mm_traits.c_layout_traits.ld @@ -1475,6 +1778,35 @@ def plan( layout_c_ifc.batch_count = mm_traits.batch_count layout_c_ifc.strided_batch_offset = mm_traits.c_layout_traits.batch_offset + if ( + self.input_type_width == 8 + and self.options.block_scaling + and (mm_traits.M % 128 != 0 or mm_traits.N % 128 != 0 or mm_traits.K % 128 != 0) + ): + raise ValueError( + f"M={mm_traits.M} N={mm_traits.N} K={mm_traits.K} must be divisible by 128 when block_scaling=True." + ) + + if self.input_type_width == 8 and (mm_traits.M % 16 != 0 or mm_traits.N % 16 != 0 or mm_traits.K % 16 != 0): + raise ValueError(f"M={mm_traits.M} N={mm_traits.N} K={mm_traits.K} must be divisible by 16 for FP8 operations") + + if self.options.block_scaling and self.d_dtype_width == 8: + self.mm_desc_ifc.alpha_vector_batch_stride = 1 # Workaround for library caching issue + + # cublasLtMatmulAlgoGetHeuristic requires the scale pointer to be set. + self.aux_outputs = { + "d_out_scale": utils.create_empty_tensor( + self.result_class, + ((mm_traits.M * mm_traits.N) // 32 * self.mm_traits.batch_count), + "uint8", + self.device_id, + stream_holder, + verify_strides=False, + ) + } + self.mm_desc_ifc.d_out_scale_pointer = self.aux_outputs["d_out_scale"].data_ptr + self.mm_desc_ifc.d_out_scale_mode = cublaslt.MatmulMatrixScale.VEC32_UE8M0 + limit = preferences.limit if algorithms is None: num_algorithms = np.empty((1,), dtype=np.int32) @@ -1493,6 +1825,9 @@ def plan( cublaslt.matmul_preference_destroy(self.preference_ptr) self.preference_ptr = cublaslt.matmul_preference_create() + if self.input_type_width <= 8: + self._prepare_operand_quantization_scales(self.quantization_scales) + if algorithms is None: # Set preferences. preference_ifc = matmul_pref_ifc.MatmulPreferenceInterface(self.preference_ptr) @@ -1667,7 +2002,18 @@ def _check_and_set_operand( return @utils.precondition(_check_valid_matmul) - def reset_operands(self, a=None, b=None, c=None, *, alpha=None, beta=None, epilog_inputs=None, stream=None): + def reset_operands( + self, + a=None, + b=None, + c=None, + *, + alpha=None, + beta=None, + quantization_scales=None, + epilog_inputs=None, + stream=None, + ): """ Reset the operands held by this :class:`Matmul` instance. @@ -1705,6 +2051,8 @@ def reset_operands(self, a=None, b=None, c=None, *, alpha=None, beta=None, epilo stream: {stream} + quantization_scales: {quantization_scales} + Examples: >>> import cupy as cp @@ -1810,6 +2158,25 @@ def reset_operands(self, a=None, b=None, c=None, *, alpha=None, beta=None, epilo f"The value provided for beta {beta} is not convertible to dtype '{self.beta.dtype}'." ) from e + # Update quantization_scales. + if quantization_scales is not None: + quantization_scales = self._validate_operand_scales(quantization_scales, all_required=False) + if quantization_scales.a is not None: + self.quantization_scales.a = quantization_scales.a + if quantization_scales.b is not None: + self.quantization_scales.b = quantization_scales.b + if quantization_scales.c is not None: + self.quantization_scales.c = quantization_scales.c + if quantization_scales.d is not None: + self.quantization_scales.d = quantization_scales.d + self._prepare_operand_quantization_scales(self.quantization_scales) + + if epilog_inputs is not None and "aux_quantization_scale" in epilog_inputs: + epilog_inputs = epilog_inputs.copy() + aux_quantization_scale = epilog_inputs.pop("aux_quantization_scale") + self._validate_epilog_aux_scale(aux_quantization_scale, required=False) + self._prepare_quantization_scale(aux_quantization_scale, "epilog_aux", cublas_operand="epilogue_aux") + stream_holder = utils.get_or_create_stream(self.device_id, stream, self.package) # Reset the provided operands. @@ -1822,7 +2189,7 @@ def reset_operands(self, a=None, b=None, c=None, *, alpha=None, beta=None, epilo self.mm_desc_ifc, stream_holder, operand_index=index, - dtype=self.ab_dtype_name, + dtype=self.a_dtype_name, extents=self.operand_extents[index], strides=self.operand_strides[index], ) @@ -1836,7 +2203,7 @@ def reset_operands(self, a=None, b=None, c=None, *, alpha=None, beta=None, epilo self.mm_desc_ifc, stream_holder, operand_index=index, - dtype=self.ab_dtype_name, + dtype=self.b_dtype_name, extents=self.operand_extents[index], strides=self.operand_strides[index], ) @@ -2022,7 +2389,7 @@ def execute_matmul(algorithm_ptr): self.logger.info(f"The best performance remains at {formatters.FLOPSStr(orig_flop_rate, 'FLOP/s')}.") end = timer() - self.logger.info(f"The autotuning took {(end - start) * 1000.:.3f} ms to complete.") + self.logger.info(f"The autotuning took {(end - start) * 1000.0:.3f} ms to complete.") @utils.precondition(_check_valid_matmul) @utils.precondition(_check_planned, "Execution") @@ -2074,6 +2441,18 @@ def execute(self, *, algorithm=None, release_workspace=False, stream=None): ) if log_debug: self.logger.debug(f"The auxiliary output tensor '{name}' has been created.") + if self.preferences.epilog.aux_amax: + if "float8" not in dtype_name: + raise ValueError("epilog.aux_amax=True is not supported when epilog output type is not FP8.") + self.epilog_outputs[f"{name}_amax"] = utils.create_empty_tensor( + self.result_class, + (1,), + "float32", # This is the only type allowed by cuBLAS for AMAX. + self.device_id, + stream_holder, + verify_strides=False, + ) + self.mm_desc_ifc.epilogue_aux_amax_pointer = self.epilog_outputs[f"{name}_amax"].data_ptr # Update the data pointer in the MM descriptor. handler.update_ptr(self.mm_desc_ifc, aux.data_ptr) @@ -2097,6 +2476,30 @@ def execute(self, *, algorithm=None, release_workspace=False, stream=None): if log_debug: self.logger.debug("The output (empty) tensor has been created.") + self.aux_outputs = dict() + + if self.options.result_amax: + self.aux_outputs["result_amax"] = utils.create_empty_tensor( + self.result_class, + (1,), + "float32", # This is the only type allowed by cuBLAS for AMAX. + self.device_id, + stream_holder, + verify_strides=False, + ) + self.mm_desc_ifc.amax_d_pointer = self.aux_outputs["result_amax"].data_ptr + + if self.options.block_scaling and self.d_dtype_width == 8: + self.aux_outputs["d_out_scale"] = utils.create_empty_tensor( + self.result_class, + (self.mm_traits.batch_count * self.result_traits.result_shape[-1] * self.result_traits.result_shape[-2] // 32), + "uint8", + self.device_id, + stream_holder, + verify_strides=False, + ) + self.mm_desc_ifc.d_out_scale_pointer = self.aux_outputs["d_out_scale"].data_ptr + # Select the first (best) algorithm if one is not provided. if algorithm is None: algorithm_struct = self.algorithms_buffer[0]["algo"] @@ -2150,17 +2553,19 @@ def execute(self, *, algorithm=None, release_workspace=False, stream=None): self._release_workspace_memory_perhaps(True) # Return the result and auxiliary outputs, if present. + all_outputs = self.epilog_outputs | self.aux_outputs if self.memory_space == "cpu": out = self.result.to("cpu", stream_holder=stream_holder) # Copy auxiliary output to CPU. - aux = {name: self.epilog_outputs[name].to("cpu", stream_holder=stream_holder) for name in self.epilog_outputs} + aux = {name: all_outputs[name].to("cpu", stream_holder=stream_holder) for name in all_outputs} else: out = self.result.tensor # Return the unwrapped epilog output tensor(s). - aux = {name: self.epilog_outputs[name].tensor for name in self.epilog_outputs} + aux = {name: all_outputs[name].tensor for name in all_outputs} # Release internal reference to the result to permit recycling of memory. self.result = None + self.aux_outputs = dict() self.epilog_outputs = dict() self._reset_workspace_allocation_tracking() @@ -2215,6 +2620,7 @@ def matmul( epilog=None, epilog_inputs=None, qualifiers=None, + quantization_scales=None, options=None, preferences=None, algorithm=None, @@ -2271,12 +2677,17 @@ def matmul( stream: {stream} + quantization_scales: {quantization_scales} + Returns: {result} Semantics: {semantics} + Narrow-precision support: + {narrow_precision} + See Also: :class:`Matmul`, :class:`MatmulOptions`, :class:`MatmulEpilog`, :class:`MatmulPlanPreferences` @@ -2359,7 +2770,17 @@ def matmul( else: algorithms = [algorithm] # The type of algorithm should be algorithm.Algorithm and will be checked in plan() - with Matmul(a, b, c=c, alpha=alpha, beta=beta, qualifiers=qualifiers, options=options, stream=stream) as mm: + with Matmul( + a, + b, + c=c, + alpha=alpha, + beta=beta, + qualifiers=qualifiers, + options=options, + stream=stream, + quantization_scales=quantization_scales, + ) as mm: mm.plan(preferences=preferences, epilog=epilog, epilog_inputs=epilog_inputs, stream=stream, algorithms=algorithms) r = mm.execute(stream=stream) diff --git a/nvmath/memory.py b/nvmath/memory.py index 2b4f576..7ce947b 100644 --- a/nvmath/memory.py +++ b/nvmath/memory.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/pyproject.toml b/pyproject.toml index a2c64e1..84c385e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -15,8 +15,8 @@ backend-path = ["builder"] [project] name = "nvmath-python" +version = "0.3.0" dynamic = [ - "version", "readme", "dependencies", "optional-dependencies" @@ -51,7 +51,6 @@ classifiers = [ ] [tool.setuptools.dynamic] -version = { attr = "nvmath._version.__version__" } readme = { file = ["DESCRIPTION.rst"], content-type = "text/x-rst" } dependencies = {file = ["requirements/pip/nvmath-python.txt"] } @@ -106,8 +105,6 @@ ignore = [ "I001", "SIM105", "SIM108", - # Ignore module-import-not-at-top-of-file - "E402", # Ignore multiple-statements-on-one-line-colon "E701", # Ignore multiple-statements-on-one-line-semicolon @@ -118,12 +115,6 @@ ignore = [ "E731", # Ignore ambiguous-variable-name "E741", - # Ignore unused-import - "F401", - # Ignore undefined-local-with-import-star - "F403", - # Ignore undefined-local-with-import-star-usage - "F405", ] fixable = ["ALL"] diff --git a/requirements/README.md b/requirements/README.md index ff4ef5b..0851556 100644 --- a/requirements/README.md +++ b/requirements/README.md @@ -70,3 +70,4 @@ requirements are included by the top-level requirements sets. | requirements/pip/tests.txt | Test dependencies | | requirements/pip/torch-cu11.txt | Enable torch use in tests and examples via wheels for CUDA-11.8 | | requirements/pip/torch-cu12.txt | Enable torch use in tests and examples via wheels for CUDA-12.1 | +| requirements/pip/torch-cu12-nightly.txt | Enable torch nightly + CTK-12.8 wheels | diff --git a/requirements/pip/docs.txt b/requirements/pip/docs.txt index d26766f..1ec3a4a 100644 --- a/requirements/pip/docs.txt +++ b/requirements/pip/docs.txt @@ -1,8 +1,12 @@ breathe enum-tools grip +jupyter myst-parser +nbsphinx +nbsphinx-link nvidia-sphinx-theme +pandoc sphinx sphinx-favicon sphinx-toolbox diff --git a/requirements/pip/torch-cu12-nightly.txt b/requirements/pip/torch-cu12-nightly.txt new file mode 100644 index 0000000..227fee8 --- /dev/null +++ b/requirements/pip/torch-cu12-nightly.txt @@ -0,0 +1,5 @@ +# pytorch >=2.3 to ensure numpy 1/2 compatibility +# torch wheels depend on nvidia wheels; do not add if testing system ctk +--pre +--index https://download.pytorch.org/whl/nightly/cu128 +torch>=2.6; platform_system!="Windows" diff --git a/setup.py b/setup.py index c38e6d9..22a8b7b 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -21,7 +21,7 @@ # flow, so we need to hack sys.path ourselves... source_root = os.path.abspath(os.path.dirname(__file__)) sys.path.append(os.path.join(source_root, "builder")) -import utils # this is builder.utils +import utils # type: ignore # this is builder.utils # noqa: E402 # List the main modules, and infer the auxiliary modules automatically @@ -129,10 +129,6 @@ def calculate_modules(module): setup( ext_modules=cythonize(ext_modules, verbose=True, language_level=3, compiler_directives=compiler_directives), packages=find_packages(include=["nvmath", "nvmath.*"]), - package_data=dict.fromkeys( - find_packages(include=["nvmath.*"]), - ["*.pxd", "*.pyx", "*.py"], - ), zip_safe=False, cmdclass=cmdclass, ) diff --git a/tests/conftest.py b/tests/conftest.py index 35d4348..4898dc5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -8,7 +8,6 @@ from collections.abc import Iterable import datetime -import os import hypothesis diff --git a/tests/docstring_tests/test_docstrings.py b/tests/docstring_tests/test_docstrings.py index 19ccb2e..725b837 100644 --- a/tests/docstring_tests/test_docstrings.py +++ b/tests/docstring_tests/test_docstrings.py @@ -1,3 +1,7 @@ +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + import contextlib import os import sphinx.cmd.build diff --git a/tests/example_tests/device_tests/test_device_samples.py b/tests/example_tests/device_tests/test_device_samples.py index 5d45955..8c4b078 100644 --- a/tests/example_tests/device_tests/test_device_samples.py +++ b/tests/example_tests/device_tests/test_device_samples.py @@ -1,14 +1,12 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 import glob import os -import re import pytest -from nvmath import bindings from ..test_utils import run_sample diff --git a/tests/example_tests/fft_tests/test_fft_samples.py b/tests/example_tests/fft_tests/test_fft_samples.py index ba36d1a..c56d475 100644 --- a/tests/example_tests/fft_tests/test_fft_samples.py +++ b/tests/example_tests/fft_tests/test_fft_samples.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -36,7 +36,7 @@ def _has_numba(): try: - import numba + import numba # noqa: F401 return True except ModuleNotFoundError: diff --git a/tests/example_tests/matmul_tests/test_advanced_matmul_samples.py b/tests/example_tests/matmul_tests/test_advanced_matmul_samples.py index 1f29823..cf0f1f2 100644 --- a/tests/example_tests/matmul_tests/test_advanced_matmul_samples.py +++ b/tests/example_tests/matmul_tests/test_advanced_matmul_samples.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -9,7 +9,7 @@ import pytest try: - import cupy + import cupy # noqa: F401 except ModuleNotFoundError: pytest.skip("cupy required for matmul tests", allow_module_level=True) @@ -36,9 +36,40 @@ "example13_epilog_stateful_reset.py": 11501, "example14_autotune.py": 11501, "example16_reuse_algorithms.py": 11501, + "example17_fp8.py": 120800, + "example18_fp8_types.py": 120800, + "example19_fp8_reset.py": 120800, + "example20_fp8_inplace_scale_change.py": 120800, + "example21_fp8_amax.py": 120800, + "example22_fp8_delayed_scaling.py": 120800, + "example23_fp8_epilog.py": 120800, + "example24_fp8_epilog_aux.py": 120800, + "example25_mxfp8.py": 120800, + "example26_mxfp8_d_out.py": 120800, + "example27_mxfp8_chaining.py": 120800, + "example28_mxfp8_epilog.py": 120800, + "example29_mxfp8_layout.py": 120800, +} + +min_cc = { + "example17_fp8.py": (8, 9), + "example18_fp8_types.py": (8, 9), + "example19_fp8_reset.py": (8, 9), + "example20_fp8_inplace_scale_change.py": (8, 9), + "example21_fp8_amax.py": (8, 9), + "example22_fp8_delayed_scaling.py": (8, 9), + "example23_fp8_epilog.py": (8, 9), + "example24_fp8_epilog_aux.py": (8, 9), + "example25_mxfp8.py": (10, 0), + "example26_mxfp8_d_out.py": (10, 0), + "example27_mxfp8_chaining.py": (10, 0), + "example28_mxfp8_epilog.py": (10, 0), + "example29_mxfp8_layout.py": (10, 0), } cublas_version = bindings.cublasLt.get_version() +device_properties = cupy.cuda.runtime.getDeviceProperties(cupy.cuda.runtime.getDevice()) +cc = (device_properties["major"], device_properties["minor"]) @pytest.mark.parametrize("sample", sample_files) @@ -48,4 +79,7 @@ def test_sample(self, sample): required_cublas_version = min_cublas_version.get(filename, 0) if cublas_version < required_cublas_version: pytest.skip(f"cublas version {cublas_version} lower than required ({required_cublas_version})") + required_cc = min_cc.get(filename, (0, 0)) + if cc < required_cc: + pytest.skip(f"compute capability {cc} lower than required {required_cc}") run_sample(samples_path, sample) diff --git a/tests/example_tests/test_utils.py b/tests/example_tests/test_utils.py index 136772b..48e9bf0 100644 --- a/tests/example_tests/test_utils.py +++ b/tests/example_tests/test_utils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/device/cpp_conv.py b/tests/nvmath_tests/device/cpp_conv.py index 5104865..150f909 100644 --- a/tests/nvmath_tests/device/cpp_conv.py +++ b/tests/nvmath_tests/device/cpp_conv.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/device/cpp_gemm_batched.py b/tests/nvmath_tests/device/cpp_gemm_batched.py index 0c81600..1eeb015 100644 --- a/tests/nvmath_tests/device/cpp_gemm_batched.py +++ b/tests/nvmath_tests/device/cpp_gemm_batched.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/device/cpp_gemm_loop.py b/tests/nvmath_tests/device/cpp_gemm_loop.py index e5bb6c4..3f19de8 100644 --- a/tests/nvmath_tests/device/cpp_gemm_loop.py +++ b/tests/nvmath_tests/device/cpp_gemm_loop.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/device/curand/compiled_apis.py b/tests/nvmath_tests/device/curand/compiled_apis.py index 17397e4..a46840c 100644 --- a/tests/nvmath_tests/device/curand/compiled_apis.py +++ b/tests/nvmath_tests/device/curand/compiled_apis.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/device/curand/distributions.py b/tests/nvmath_tests/device/curand/distributions.py index 3e919ea..d5558f6 100644 --- a/tests/nvmath_tests/device/curand/distributions.py +++ b/tests/nvmath_tests/device/curand/distributions.py @@ -1,12 +1,14 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 +from collections.abc import Callable + import numpy as np -import scipy.stats as stats import nvmath.device.random as R -from collections.abc import Callable -from .generators import * +import scipy.stats as stats + +from . import generators class Distribution: @@ -22,7 +24,7 @@ def ppf(self): """ raise NotImplementedError - def curand_variants(self) -> dict[tuple[str, int, Generator], Callable]: + def curand_variants(self) -> dict[tuple[str, int, generators.Generator], Callable]: """ A dictionary mapping (dtype, group size, generator) to curand distribution function. """ @@ -72,22 +74,22 @@ def cdf(self, x): def curand_variants(self): return { - ("float", 1, XorwowGenerator()): R.uniform, - ("float", 1, MrgGenerator()): R.uniform, - ("float", 1, PhiloxGenerator()): R.uniform, - ("float", 1, Sobol32Generator()): R.uniform, - ("float", 1, ScrambledSobol32Generator()): R.uniform, - ("float", 1, Sobol64Generator()): R.uniform, - ("float", 1, ScrambledSobol64Generator()): R.uniform, - ("double", 1, XorwowGenerator()): R.uniform_double, - ("double", 1, MrgGenerator()): R.uniform_double, - ("double", 1, PhiloxGenerator()): R.uniform_double, - ("double", 1, Sobol32Generator()): R.uniform_double, - ("double", 1, ScrambledSobol32Generator()): R.uniform_double, - ("double", 1, Sobol64Generator()): R.uniform_double, - ("double", 1, ScrambledSobol64Generator()): R.uniform_double, - ("double", 2, PhiloxGenerator()): R.uniform2_double, - ("float", 4, PhiloxGenerator()): R.uniform4, + ("float", 1, generators.XorwowGenerator()): R.uniform, + ("float", 1, generators.MrgGenerator()): R.uniform, + ("float", 1, generators.PhiloxGenerator()): R.uniform, + ("float", 1, generators.Sobol32Generator()): R.uniform, + ("float", 1, generators.ScrambledSobol32Generator()): R.uniform, + ("float", 1, generators.Sobol64Generator()): R.uniform, + ("float", 1, generators.ScrambledSobol64Generator()): R.uniform, + ("double", 1, generators.XorwowGenerator()): R.uniform_double, + ("double", 1, generators.MrgGenerator()): R.uniform_double, + ("double", 1, generators.PhiloxGenerator()): R.uniform_double, + ("double", 1, generators.Sobol32Generator()): R.uniform_double, + ("double", 1, generators.ScrambledSobol32Generator()): R.uniform_double, + ("double", 1, generators.Sobol64Generator()): R.uniform_double, + ("double", 1, generators.ScrambledSobol64Generator()): R.uniform_double, + ("double", 2, generators.PhiloxGenerator()): R.uniform2_double, + ("float", 4, generators.PhiloxGenerator()): R.uniform4, } def curand(self, dtype_name, group_size): @@ -107,27 +109,27 @@ def cdf(self, x): def curand_variants(self): return { - ("float", 1, XorwowGenerator()): R.normal, - ("float", 1, MrgGenerator()): R.normal, - ("float", 1, PhiloxGenerator()): R.normal, - ("float", 1, Sobol32Generator()): R.normal, - ("float", 1, ScrambledSobol32Generator()): R.normal, - ("float", 1, Sobol64Generator()): R.normal, - ("float", 1, ScrambledSobol64Generator()): R.normal, - ("double", 1, XorwowGenerator()): R.normal_double, - ("double", 1, MrgGenerator()): R.normal_double, - ("double", 1, PhiloxGenerator()): R.normal_double, - ("double", 1, Sobol32Generator()): R.normal_double, - ("double", 1, ScrambledSobol32Generator()): R.normal_double, - ("double", 1, Sobol64Generator()): R.normal_double, - ("double", 1, ScrambledSobol64Generator()): R.normal_double, - ("float", 2, XorwowGenerator()): R.normal2, - ("float", 2, MrgGenerator()): R.normal2, - ("float", 2, PhiloxGenerator()): R.normal2, - ("double", 2, XorwowGenerator()): R.normal2_double, - ("double", 2, MrgGenerator()): R.normal2_double, - ("double", 2, PhiloxGenerator()): R.normal2_double, - ("float", 4, PhiloxGenerator()): R.normal4, + ("float", 1, generators.XorwowGenerator()): R.normal, + ("float", 1, generators.MrgGenerator()): R.normal, + ("float", 1, generators.PhiloxGenerator()): R.normal, + ("float", 1, generators.Sobol32Generator()): R.normal, + ("float", 1, generators.ScrambledSobol32Generator()): R.normal, + ("float", 1, generators.Sobol64Generator()): R.normal, + ("float", 1, generators.ScrambledSobol64Generator()): R.normal, + ("double", 1, generators.XorwowGenerator()): R.normal_double, + ("double", 1, generators.MrgGenerator()): R.normal_double, + ("double", 1, generators.PhiloxGenerator()): R.normal_double, + ("double", 1, generators.Sobol32Generator()): R.normal_double, + ("double", 1, generators.ScrambledSobol32Generator()): R.normal_double, + ("double", 1, generators.Sobol64Generator()): R.normal_double, + ("double", 1, generators.ScrambledSobol64Generator()): R.normal_double, + ("float", 2, generators.XorwowGenerator()): R.normal2, + ("float", 2, generators.MrgGenerator()): R.normal2, + ("float", 2, generators.PhiloxGenerator()): R.normal2, + ("double", 2, generators.XorwowGenerator()): R.normal2_double, + ("double", 2, generators.MrgGenerator()): R.normal2_double, + ("double", 2, generators.PhiloxGenerator()): R.normal2_double, + ("float", 4, generators.PhiloxGenerator()): R.normal4, } def curand(self, dtype_name, group_size): @@ -150,27 +152,27 @@ def cdf(self, x): def curand_variants(self): return { - ("float", 1, XorwowGenerator()): R.log_normal, - ("float", 1, MrgGenerator()): R.log_normal, - ("float", 1, PhiloxGenerator()): R.log_normal, - ("float", 1, Sobol32Generator()): R.log_normal, - ("float", 1, ScrambledSobol32Generator()): R.log_normal, - ("float", 1, Sobol64Generator()): R.log_normal, - ("float", 1, ScrambledSobol64Generator()): R.log_normal, - ("double", 1, XorwowGenerator()): R.log_normal_double, - ("double", 1, MrgGenerator()): R.log_normal_double, - ("double", 1, PhiloxGenerator()): R.log_normal_double, - ("double", 1, Sobol32Generator()): R.log_normal_double, - ("double", 1, ScrambledSobol32Generator()): R.log_normal_double, - ("double", 1, Sobol64Generator()): R.log_normal_double, - ("double", 1, ScrambledSobol64Generator()): R.log_normal_double, - ("float", 2, XorwowGenerator()): R.log_normal2, - ("float", 2, MrgGenerator()): R.log_normal2, - ("float", 2, PhiloxGenerator()): R.log_normal2, - ("double", 2, XorwowGenerator()): R.log_normal2_double, - ("double", 2, MrgGenerator()): R.log_normal2_double, - ("double", 2, PhiloxGenerator()): R.log_normal2_double, - ("float", 4, PhiloxGenerator()): R.log_normal4, + ("float", 1, generators.XorwowGenerator()): R.log_normal, + ("float", 1, generators.MrgGenerator()): R.log_normal, + ("float", 1, generators.PhiloxGenerator()): R.log_normal, + ("float", 1, generators.Sobol32Generator()): R.log_normal, + ("float", 1, generators.ScrambledSobol32Generator()): R.log_normal, + ("float", 1, generators.Sobol64Generator()): R.log_normal, + ("float", 1, generators.ScrambledSobol64Generator()): R.log_normal, + ("double", 1, generators.XorwowGenerator()): R.log_normal_double, + ("double", 1, generators.MrgGenerator()): R.log_normal_double, + ("double", 1, generators.PhiloxGenerator()): R.log_normal_double, + ("double", 1, generators.Sobol32Generator()): R.log_normal_double, + ("double", 1, generators.ScrambledSobol32Generator()): R.log_normal_double, + ("double", 1, generators.Sobol64Generator()): R.log_normal_double, + ("double", 1, generators.ScrambledSobol64Generator()): R.log_normal_double, + ("float", 2, generators.XorwowGenerator()): R.log_normal2, + ("float", 2, generators.MrgGenerator()): R.log_normal2, + ("float", 2, generators.PhiloxGenerator()): R.log_normal2, + ("double", 2, generators.XorwowGenerator()): R.log_normal2_double, + ("double", 2, generators.MrgGenerator()): R.log_normal2_double, + ("double", 2, generators.PhiloxGenerator()): R.log_normal2_double, + ("float", 4, generators.PhiloxGenerator()): R.log_normal4, } def curand(self, dtype_name, group_size): @@ -199,14 +201,14 @@ def ppf(self, x): def curand_variants(self): return { - ("uint32", 1, XorwowGenerator()): R.poisson, - ("uint32", 1, MrgGenerator()): R.poisson, - ("uint32", 1, PhiloxGenerator()): R.poisson, - ("uint32", 1, Sobol32Generator()): R.poisson, - ("uint32", 1, ScrambledSobol32Generator()): R.poisson, - ("uint32", 1, Sobol64Generator()): R.poisson, - ("uint32", 1, ScrambledSobol64Generator()): R.poisson, - ("uint32", 4, PhiloxGenerator()): R.poisson4, + ("uint32", 1, generators.XorwowGenerator()): R.poisson, + ("uint32", 1, generators.MrgGenerator()): R.poisson, + ("uint32", 1, generators.PhiloxGenerator()): R.poisson, + ("uint32", 1, generators.Sobol32Generator()): R.poisson, + ("uint32", 1, generators.ScrambledSobol32Generator()): R.poisson, + ("uint32", 1, generators.Sobol64Generator()): R.poisson, + ("uint32", 1, generators.ScrambledSobol64Generator()): R.poisson, + ("uint32", 4, generators.PhiloxGenerator()): R.poisson4, } def curand(self, dtype_name, group_size): diff --git a/tests/nvmath_tests/device/curand/generators.py b/tests/nvmath_tests/device/curand/generators.py index 5957345..27eb86c 100644 --- a/tests/nvmath_tests/device/curand/generators.py +++ b/tests/nvmath_tests/device/curand/generators.py @@ -1,10 +1,9 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 from numba import cuda, uint32, uint64 import nvmath.device.random as R -from functools import cache from .compiled_apis import compiled_apis import cffi diff --git a/tests/nvmath_tests/device/curand/test_random.py b/tests/nvmath_tests/device/curand/test_random.py index 52fa09b..dce8f8f 100644 --- a/tests/nvmath_tests/device/curand/test_random.py +++ b/tests/nvmath_tests/device/curand/test_random.py @@ -1,14 +1,21 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 import numpy as np import scipy.stats as stats import pytest -import nvmath.device.random as R -from .generators import * -from .distributions import * -from .utils import * + +from . import distributions +from . import generators +from .utils import ( + all_supported_configs, + generate_random_numbers, + per_thread_skipahead, + per_thread_skipahead_sequence, + prepare_states, + prepare_states_and_generate, +) """ This set of tests checks random device APIs. @@ -25,13 +32,13 @@ @pytest.mark.parametrize( "distribution,dtype_name,group_size,generator", all_supported_configs( - Poisson(1), - Poisson(10), - Poisson(15), - LogNormal(), - LogNormal(-2, 0.5), - Uniform(), - Normal(), + distributions.Poisson(1), + distributions.Poisson(10), + distributions.Poisson(15), + distributions.LogNormal(), + distributions.LogNormal(-2, 0.5), + distributions.Uniform(), + distributions.Normal(), ), ) def test_distribution(distribution, dtype_name, generator, nsamples, threads, blocks, group_size): @@ -81,7 +88,9 @@ def test(x): @pytest.mark.parametrize("threads,blocks", ((38, 2), (1, 1))) @pytest.mark.parametrize( "distribution,dtype_name,group_size,generator", - all_supported_configs(Poisson(12), LogNormal(2, 1.1), Uniform(), Normal()), + all_supported_configs( + distributions.Poisson(12), distributions.LogNormal(2, 1.1), distributions.Uniform(), distributions.Normal() + ), ) def test_seeds(distribution, dtype_name, generator, nsamples, threads, blocks, group_size): """ @@ -122,7 +131,7 @@ def generate_with_seed(seed): ) @pytest.mark.parametrize( "generator", - [pytest.param(g(), id=g().name()) for g in GENERATORS if g().supports_skipahead()], + [pytest.param(g(), id=g().name()) for g in generators.GENERATORS if g().supports_skipahead()], ) def test_skipahead(generator, threads, blocks): """ @@ -139,7 +148,7 @@ def test_skipahead(generator, threads, blocks): def gen(states, n): return generate_random_numbers( states=states, - distribution=Uniform(), + distribution=distributions.Uniform(), dtype_name="float", nsamples=n, threads=threads, @@ -195,7 +204,7 @@ def gen_all(): ) @pytest.mark.parametrize( "generator", - [pytest.param(g(), id=g().name()) for g in GENERATORS if g().supports_skipahead_subsequence()], + [pytest.param(g(), id=g().name()) for g in generators.GENERATORS if g().supports_skipahead_subsequence()], ) def test_skipahead_sequence(generator, threads, blocks): """ @@ -211,7 +220,7 @@ def test_skipahead_sequence(generator, threads, blocks): def gen(states): return generate_random_numbers( states=states, - distribution=Uniform(), + distribution=distributions.Uniform(), dtype_name="double", nsamples=1, threads=threads, diff --git a/tests/nvmath_tests/device/curand/utils.py b/tests/nvmath_tests/device/curand/utils.py index a0e2549..696a585 100644 --- a/tests/nvmath_tests/device/curand/utils.py +++ b/tests/nvmath_tests/device/curand/utils.py @@ -1,12 +1,14 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 +from functools import cache +from numba import cuda import numpy as np -import pytest import nvmath.device.random as R -from functools import cache -from .generators import * +import pytest + +from . import generators from .compiled_apis import compiled_apis NP_DTYPES = { @@ -48,7 +50,7 @@ def random_kernel(states, nsamples, result, curand_distribution_args): def prepare_states( *, - generator: Generator, + generator: generators.Generator, seed, threads, blocks, @@ -140,7 +142,7 @@ def per_thread_skipahead_sequence( def prepare_states_and_generate( *, - generator: Generator, + generator: generators.Generator, seed, threads, blocks, diff --git a/tests/nvmath_tests/device/helpers.py b/tests/nvmath_tests/device/helpers.py index dabcbe3..87defdc 100644 --- a/tests/nvmath_tests/device/helpers.py +++ b/tests/nvmath_tests/device/helpers.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -36,6 +36,10 @@ def set_device(): CHECK_CUDART(err) err, prop = cudart.cudaGetDeviceProperties(0) CHECK_CUDART(err) + # TODO: dx does not support platforms > arch90 for now and version is capped + # at 9.0 + if (prop.major, prop.minor) > (9, 0): + return (9, 0) return (prop.major, prop.minor) diff --git a/tests/nvmath_tests/device/helpers_cpp.py b/tests/nvmath_tests/device/helpers_cpp.py index a8ec85b..c09af46 100644 --- a/tests/nvmath_tests/device/helpers_cpp.py +++ b/tests/nvmath_tests/device/helpers_cpp.py @@ -1,14 +1,13 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 -from cuda import cuda -from cuda import nvrtc +from cuda import cudart, nvrtc, cuda from nvmath.device.common_mathdx import CUDA_HOME as _CUDA_HOME from nvmath.device.common_mathdx import MATHDX_HOME as _MATHDX_HOME -from .helpers import CHECK_CUDA, CHECK_NVRTC, make_args, get_unsigned +from .helpers import CHECK_CUDA, CHECK_CUDART, CHECK_NVRTC, make_args, get_unsigned def run_and_time(kernel, grid_dim, block_dim, shared_memory_size, ncycles, *args): @@ -62,6 +61,12 @@ def run_and_time(kernel, grid_dim, block_dim, shared_memory_size, ncycles, *args def compile_cpp_kernel(cpp, sm, mangled): print(f"compile_cpp_kernel CUDA_HOME = {_CUDA_HOME}, _MATHDX_HOME = {_MATHDX_HOME}") + # TODO: dx does not support platforms > arch90 for now and version is capped + # at 9.0, but we want to compile program against actual architecture. + err, prop = cudart.cudaGetDeviceProperties(0) + CHECK_CUDART(err) + sm = (prop.major, prop.minor) + opts = ( [b"--std=c++17", b"--device-as-default-execution-space", b"-DCUFFTDX_DETAIL_USE_CUDA_STL=1"] + [bytes(f"--include-path={h}/include", encoding="ascii") for h in _CUDA_HOME] diff --git a/tests/nvmath_tests/device/helpers_numba.py b/tests/nvmath_tests/device/helpers_numba.py index 4f6f017..bf5b744 100644 --- a/tests/nvmath_tests/device/helpers_numba.py +++ b/tests/nvmath_tests/device/helpers_numba.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/device/numba_conv.py b/tests/nvmath_tests/device/numba_conv.py index 9e8ec73..c6d264f 100644 --- a/tests/nvmath_tests/device/numba_conv.py +++ b/tests/nvmath_tests/device/numba_conv.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/device/numba_gemm_batched.py b/tests/nvmath_tests/device/numba_gemm_batched.py index fbff205..6696da5 100644 --- a/tests/nvmath_tests/device/numba_gemm_batched.py +++ b/tests/nvmath_tests/device/numba_gemm_batched.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -8,7 +8,6 @@ import cupy from numba import cuda from .helpers_numba import run_and_time, shared_load_3d, shared_store_3d -import time class NumbaGemmBatched: diff --git a/tests/nvmath_tests/device/numba_gemm_loop.py b/tests/nvmath_tests/device/numba_gemm_loop.py index 24ad7ed..17f35fd 100644 --- a/tests/nvmath_tests/device/numba_gemm_loop.py +++ b/tests/nvmath_tests/device/numba_gemm_loop.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/device/test_cublasdx_generic.py b/tests/nvmath_tests/device/test_cublasdx_generic.py index 0b3124c..437092a 100644 --- a/tests/nvmath_tests/device/test_cublasdx_generic.py +++ b/tests/nvmath_tests/device/test_cublasdx_generic.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/device/test_cublasdx_numba.py b/tests/nvmath_tests/device/test_cublasdx_numba.py index 44eb543..62a2fdd 100644 --- a/tests/nvmath_tests/device/test_cublasdx_numba.py +++ b/tests/nvmath_tests/device/test_cublasdx_numba.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -239,6 +239,7 @@ def test_matmul(shape, block_size, block_dim, data_type, trans, precision, np_ty assert MM.block_dim == Dim3(*block_dim) assert MM.max_threads_per_block <= 1024 assert MM.code_type.kind == "lto" + assert MM.code_type.cc.major == SM[0] assert MM.code_type.cc.minor == SM[1] diff --git a/tests/nvmath_tests/device/test_cublasdx_numba_perf.py b/tests/nvmath_tests/device/test_cublasdx_numba_perf.py index b163783..0f84e0f 100644 --- a/tests/nvmath_tests/device/test_cublasdx_numba_perf.py +++ b/tests/nvmath_tests/device/test_cublasdx_numba_perf.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/device/test_cufftdx_generic.py b/tests/nvmath_tests/device/test_cufftdx_generic.py index 4f32eb9..5330980 100644 --- a/tests/nvmath_tests/device/test_cufftdx_generic.py +++ b/tests/nvmath_tests/device/test_cufftdx_generic.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/device/test_cufftdx_numba.py b/tests/nvmath_tests/device/test_cufftdx_numba.py index 5941e40..38c6e49 100644 --- a/tests/nvmath_tests/device/test_cufftdx_numba.py +++ b/tests/nvmath_tests/device/test_cufftdx_numba.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/device/test_cufftdx_numba_perf.py b/tests/nvmath_tests/device/test_cufftdx_numba_perf.py index b059e7f..56137a7 100644 --- a/tests/nvmath_tests/device/test_cufftdx_numba_perf.py +++ b/tests/nvmath_tests/device/test_cufftdx_numba_perf.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/device/test_vector_types_numba.py b/tests/nvmath_tests/device/test_vector_types_numba.py index 22d88db..2365c16 100644 --- a/tests/nvmath_tests/device/test_vector_types_numba.py +++ b/tests/nvmath_tests/device/test_vector_types_numba.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/fft/test_default_backend.py b/tests/nvmath_tests/fft/test_default_backend.py index 6db05e6..6da51fe 100644 --- a/tests/nvmath_tests/fft/test_default_backend.py +++ b/tests/nvmath_tests/fft/test_default_backend.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/fft/test_fft_with_hypothesis.py b/tests/nvmath_tests/fft/test_fft_with_hypothesis.py index 5dc0e4f..85837c8 100644 --- a/tests/nvmath_tests/fft/test_fft_with_hypothesis.py +++ b/tests/nvmath_tests/fft/test_fft_with_hypothesis.py @@ -1,10 +1,14 @@ +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + import itertools import cupy as cp import numpy as np import scipy.fft -from hypothesis import given, reproduce_failure, strategies as st +from hypothesis import given, strategies as st from hypothesis.extra.numpy import arrays, array_shapes import nvmath diff --git a/tests/nvmath_tests/fft/test_lto_callbacks.py b/tests/nvmath_tests/fft/test_lto_callbacks.py index 93877e9..f58a92d 100644 --- a/tests/nvmath_tests/fft/test_lto_callbacks.py +++ b/tests/nvmath_tests/fft/test_lto_callbacks.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -52,7 +52,8 @@ get_custom_stream, get_primes_up_to, init_assert_exec_backend_specified, - fx_last_operand_layout, + # pytest fixture is used but not detected by linter because of strange syntax + fx_last_operand_layout, # noqa: F401 ) from .utils.check_helpers import ( add_in_place, @@ -150,7 +151,7 @@ def allow_to_fail_compund_shape(e, shape, axes): def _has_numba(): try: - import numba + import numba # noqa: F401 return True except ModuleNotFoundError: @@ -399,6 +400,28 @@ def epilog_cb(data_out, offset, value, filter_data, unused): epilog_ltoir = nvmath.fft.compile_epilog(epilog_cb, epilog_dtype.name, epilog_dtype.name) cb_kwargs["epilog"] = {"ltoir": epilog_ltoir} scaling *= 5 + # Test create_key() function + try: + key1 = nvmath.fft.FFT.create_key( + signal, + axes=axes, + prolog=cb_kwargs["prolog"] if fft_callbacks.has_prolog() else None, + epilog=cb_kwargs["epilog"] if fft_callbacks.has_epilog() else None, + ) + assert key1 is not None + key2 = nvmath.fft.FFT.create_key( + signal, + axes=axes, + prolog=nvmath.fft.DeviceCallable(**cb_kwargs["prolog"]) if fft_callbacks.has_prolog() else None, + epilog=nvmath.fft.DeviceCallable(**cb_kwargs["epilog"]) if fft_callbacks.has_epilog() else None, + ) + assert key1 == key2 + except RuntimeError as e: + if "The FFT CPU execution is not available" in str(e) and mem_backend == MemBackend.cpu: + # Skip this check since create_key() function needs CPU FFT lib availability + pass + else: + raise ref = get_fft_ref(get_scaled(signal, scaling), axes=axes) @@ -1423,8 +1446,10 @@ def test_operand_and_filter_shapes_fft_ifft( "dtype_1", "shape_0", "axes_0", + "shape_kind_0", "shape_1", "axes_1", + "shape_kind_1", "callbacks_0", "callbacks_1", ), @@ -1437,16 +1462,39 @@ def test_operand_and_filter_shapes_fft_ifft( rng.choice(lto_callback_supperted_types), repr(shape_0), repr(axes_0), + shape_kind_0, repr(shape_1), repr(axes_1), + shape_kind_1, callbacks_0, rng.choice(list(LtoCallback)), ) for dtype_0 in lto_callback_supperted_types - for shape_0, axes_0, shape_1, axes_1 in [ - ((4200, 13), (0,), (7, 4199), (1,)), # 2*2*2*3*5*5*7, 13*17*19 - ((420, 512, 3), (0, 1), (5, 4, 4307), (1, 2)), # 4307=59*73 - ((2, 16, 16, 5), (0, 1, 2), (3, 9, 49, 25), (1, 2, 3)), + for shape_0, axes_0, shape_kind_0, shape_1, axes_1, shape_kind_1, in [ + ( + (4200, 13), + (0,), + ShapeKind.pow2357, + (7, 4199), + (1,), + ShapeKind.random, + ), # 2*2*2*3*5*5*7, 13*17*19 + ( + (420, 512, 3), + (0, 1), + ShapeKind.pow2357, + (5, 4, 4307), + (1, 2), + ShapeKind.random, + ), # 4307=59*73 + ( + (2, 16, 16, 5), + (0, 1, 2), + ShapeKind.pow2, + (3, 9, 49, 25), + (1, 2, 3), + ShapeKind.pow2357, + ), ] for framework in Framework.enabled() if ExecBackend.cufft in supported_backends.exec @@ -1463,8 +1511,10 @@ def test_two_plans_different_cbs( dtype_1, shape_0, axes_0, + shape_kind_0, shape_1, axes_1, + shape_kind_1, callbacks_0, callbacks_1, ): @@ -1579,8 +1629,8 @@ def epilog_cb_1(data_out, offset, value, filter_data, unused): assert_array_type(fft_0_out, framework, mem_backend, get_fft_dtype(dtype_0)) assert_array_type(fft_1_out, framework, mem_backend, get_fft_dtype(dtype_1)) - assert_norm_close(fft_0_out, ref_0, **get_tolerance(signal_0)) - assert_norm_close(fft_1_out, ref_1, **get_tolerance(signal_1)) + assert_norm_close(fft_0_out, ref_0, **get_tolerance(signal_0, shape_kind=shape_kind_0)) + assert_norm_close(fft_1_out, ref_1, **get_tolerance(signal_1, shape_kind=shape_kind_1)) @skip_if_lto_unssuported diff --git a/tests/nvmath_tests/fft/test_perf.py b/tests/nvmath_tests/fft/test_perf.py index 8a45ef7..cf97661 100644 --- a/tests/nvmath_tests/fft/test_perf.py +++ b/tests/nvmath_tests/fft/test_perf.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/fft/test_perf_2d.py b/tests/nvmath_tests/fft/test_perf_2d.py index 9af56a2..cd5fb5c 100644 --- a/tests/nvmath_tests/fft/test_perf_2d.py +++ b/tests/nvmath_tests/fft/test_perf_2d.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/fft/test_perf_4-5d.py b/tests/nvmath_tests/fft/test_perf_4-5d.py index d06524f..5ad6f4f 100644 --- a/tests/nvmath_tests/fft/test_perf_4-5d.py +++ b/tests/nvmath_tests/fft/test_perf_4-5d.py @@ -1,8 +1,7 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 -import nvmath import numpy as np import itertools import functools @@ -14,7 +13,7 @@ if ExecBackend.cufft in supported_backends.exec: import cupy - from nvmath_tests.helpers import time_cupy, random_complex, print_aligned_table, fft_perf_GFlops + from nvmath_tests.helpers import time_cupy, print_aligned_table, fft_perf_GFlops def test_fft(): try: diff --git a/tests/nvmath_tests/fft/test_stateful.py b/tests/nvmath_tests/fft/test_stateful.py index 9f57e88..19a5f35 100644 --- a/tests/nvmath_tests/fft/test_stateful.py +++ b/tests/nvmath_tests/fft/test_stateful.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -1383,7 +1383,7 @@ def decreasingly_aligned_signals(): "fft_type": fft_type.value, "inplace": inplace, "result_layout": layout.value, - "last_axis_size": "odd", + "last_axis_parity": "odd", }, axes=axes, ) as fft: diff --git a/tests/nvmath_tests/fft/test_stateless_1d.py b/tests/nvmath_tests/fft/test_stateless_1d.py index bdbca3c..cffd81f 100644 --- a/tests/nvmath_tests/fft/test_stateless_1d.py +++ b/tests/nvmath_tests/fft/test_stateless_1d.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -54,6 +54,7 @@ get_random_1d_shape, get_random_input_data, get_custom_stream, + get_stream_pointer, init_assert_exec_backend_specified, ) from .utils.check_helpers import ( @@ -648,9 +649,9 @@ def test_fft_array_device_id(monkeypatch, framework, exec_backend, mem_backend, @pytest.mark.parametrize( - ("framework", "exec_backend", "mem_backend", "dtype"), + ("framework", "exec_backend", "mem_backend", "dtype", "use_stream_ptr"), [ - (framework, exec_backend, mem_backend, dtype) + (framework, exec_backend, mem_backend, dtype, use_stream_ptr) for framework in Framework.enabled() for exec_backend in [ExecBackend.cufft] if exec_backend in supported_backends.exec @@ -658,9 +659,10 @@ def test_fft_array_device_id(monkeypatch, framework, exec_backend, mem_backend, if mem_backend in supported_backends.framework_mem[framework] for dtype in framework_exec_type_support[framework][exec_backend] if not is_half(dtype) # for the fft size, the halfs lack precision + for use_stream_ptr in (True, False) ], ) -def test_fft_custom_stream(framework, exec_backend, mem_backend, dtype): +def test_fft_custom_stream(framework, exec_backend, mem_backend, dtype, use_stream_ptr): stream = get_custom_stream(framework) shape = 1024 * 1024 @@ -670,14 +672,18 @@ def test_fft_custom_stream(framework, exec_backend, mem_backend, dtype): fft_ref = fft_ref * 42 fft_fn = nvmath.fft.fft if is_complex(dtype) else nvmath.fft.rfft - fft = fft_fn( - signal, - execution=exec_backend.nvname, - options={ - "blocking": "auto", - }, - stream=stream, - ) + try: + fft = fft_fn( + signal, + execution=exec_backend.nvname, + options={ + "blocking": "auto", + }, + stream=get_stream_pointer(stream) if use_stream_ptr else stream, + ) + except TypeError as e: + assert "A stream object must be provided for PyTorch operands" in str(e) and framework == Framework.torch + return with use_stream(stream): # The stateless API synchronizes on plan creation, diff --git a/tests/nvmath_tests/fft/test_stateless_nd.py b/tests/nvmath_tests/fft/test_stateless_nd.py index dc76440..0f8c34c 100644 --- a/tests/nvmath_tests/fft/test_stateless_nd.py +++ b/tests/nvmath_tests/fft/test_stateless_nd.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/fft/utils/axes_utils.py b/tests/nvmath_tests/fft/utils/axes_utils.py index 1630e80..f874df3 100644 --- a/tests/nvmath_tests/fft/utils/axes_utils.py +++ b/tests/nvmath_tests/fft/utils/axes_utils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/fft/utils/check_helpers.py b/tests/nvmath_tests/fft/utils/check_helpers.py index f6e7021..c9afe69 100644 --- a/tests/nvmath_tests/fft/utils/check_helpers.py +++ b/tests/nvmath_tests/fft/utils/check_helpers.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/fft/utils/common_axes.py b/tests/nvmath_tests/fft/utils/common_axes.py index 883b860..9b54a09 100644 --- a/tests/nvmath_tests/fft/utils/common_axes.py +++ b/tests/nvmath_tests/fft/utils/common_axes.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/fft/utils/input_fixtures.py b/tests/nvmath_tests/fft/utils/input_fixtures.py index bbb90d2..4a88e5d 100644 --- a/tests/nvmath_tests/fft/utils/input_fixtures.py +++ b/tests/nvmath_tests/fft/utils/input_fixtures.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -137,6 +137,16 @@ def get_custom_stream(framework: Framework, device_id=None): raise ValueError(f"Unknown GPU framework {framework}") +def get_stream_pointer(stream) -> int: + package = stream.__class__.__module__.split(".")[0] + if package == "cupy": + return stream.ptr + elif package == "torch": + return stream.cuda_stream + else: + raise ValueError(f"Unknown GPU framework {package}") + + def init_assert_exec_backend_specified(): import pytest import nvmath diff --git a/tests/nvmath_tests/fft/utils/support_matrix.py b/tests/nvmath_tests/fft/utils/support_matrix.py index 73d356b..1c18e76 100644 --- a/tests/nvmath_tests/fft/utils/support_matrix.py +++ b/tests/nvmath_tests/fft/utils/support_matrix.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/helpers.py b/tests/nvmath_tests/helpers.py index 3ab23d1..86f46c0 100644 --- a/tests/nvmath_tests/helpers.py +++ b/tests/nvmath_tests/helpers.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/linalg/advanced/matmul/fp8_utils.py b/tests/nvmath_tests/linalg/advanced/matmul/fp8_utils.py new file mode 100644 index 0000000..55915ad --- /dev/null +++ b/tests/nvmath_tests/linalg/advanced/matmul/fp8_utils.py @@ -0,0 +1,264 @@ +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + +try: + import torch +except ImportError: + torch = None +import pytest +import numpy as np +from .utils import sample_matrix, assert_tensors_equal, to_numpy +from nvmath._internal.utils import check_or_create_options +from nvmath.linalg.advanced import matmul +from nvmath.linalg.advanced import _configuration + +if torch is None: + pytest.skip("Torch is required for FP8 tests", allow_module_level=True) + + +class Fp8Helper: + """ + A helper class to compare quantized results. + """ + + def __init__(self, exponent_bits, significand_bits): + self.exponent_bits, self.significand_bits = exponent_bits, significand_bits + + def is_normal(e, m): + if (exponent_bits, significand_bits) == (4, 3): + return e > 0 and (e, m) != (2**exponent_bits - 1, 2**significand_bits - 1) + elif (exponent_bits, significand_bits) == (5, 2): + return e > 0 and e != 2**exponent_bits - 1 + else: + raise RuntimeError(f"is_normal not implemented for E{exponent_bits}M{significand_bits}") + + # Compute all values of FP8 number. + exponent_bias = 2 ** (exponent_bits - 1) - 1 + normal_values = [ + sign * 2 ** (e - exponent_bias) * (1 + m / 2**significand_bits) + for e in range(2**exponent_bits) + for m in range(2**significand_bits) + for sign in (-1, 1) + if is_normal(e, m) + ] + subnormal_values = [ + sign * 2 ** (1 - exponent_bias) * (0 + m / 2**significand_bits) + for m in range(1, 2**significand_bits) + for sign in (-1, 1) + ] + values = subnormal_values + normal_values + [0.0] + assert len(set(values)) == len(values) + self.values = np.asarray(sorted(values)) + + # For each value, calculate the range it covers. + middles = (self.values[1:] + self.values[:-1]) / 2 + self.lranges = np.append(np.asarray([-np.inf]), middles) + self.rranges = np.append(middles, np.asarray([np.inf])) + + def range(self, value): + """ + Finds a representable value closest to `value` and returns its range. + """ + + i = np.abs(self.values - value).argmin() + return self.lranges[i], self.rranges[i] + + def absdiff(self, quantized, expected): + """ + Returns absolute difference between the ranges of quantized numbers and the expected + values. + """ + l, r = np.vectorize(self.range)(quantized) + diff = np.minimum(abs(l - expected), abs(r - expected)) + diff[(l <= expected) & (r >= expected)] = 0.0 + return diff + + def allclose(self, quantized, expected, atol=1e-2, rtol=1e-2, return_info=False): + """ + Checks if quantized values are close enough to the expected ones. + """ + quantized = to_numpy(quantized.type(torch.float64)) + expected = to_numpy(expected.type(torch.float64)) + ok = np.all(self.absdiff(quantized, expected) <= atol + rtol * np.abs(expected)) + if not return_info: + return ok + else: + aerr = self.absdiff(quantized, expected) + rerr = aerr / (np.abs(expected) + 0.000001) + return ok, { + "aerr": np.max(aerr), + "atol": atol, + "rerr": np.max(rerr), + "rtol": rtol, + } + + +fp8helpers = { + "float8_e4m3fn": Fp8Helper(4, 3), + "float8_e5m2": Fp8Helper(5, 2), +} + + +def choose_scales( + a, b, c, atype, btype, ctype, dtype, alpha=1.0, beta=1.0, allowed_in_range=(0.5, 2), allowed_out_range=(1, 100) +): + """ + Chooses reasonable scales for each of the operands. Tries to fit (absolute values of) + a, b and c into `allowed_in_range`, and (absolute values of) d into `allowed_out_range`. + However, some noise is added at the end, so this is not a hard guarantee. + """ + a = a.type(torch.float32) + amax = a.abs().max().item() + ascale = np.random.uniform(*allowed_in_range) / amax if amax > 0 else 1 + a *= ascale + + b = b.type(torch.float32) + bmax = b.abs().max().item() + bscale = np.random.uniform(*allowed_in_range) / bmax if bmax > 0 else 1 + b *= bscale + + if c is not None: + c = c.type(torch.float32) + cmax = c.abs().max().item() + cscale = np.random.uniform(*allowed_in_range) / cmax if cmax > 0 else 1 + c *= cscale + else: + cscale = None + + if c is not None: + d = alpha * a @ b + beta * c + else: + d = alpha * a @ b + + dmax = d.abs().max().item() + dscale = np.random.uniform(*allowed_out_range) / dmax if dmax > 0 else 1 + + # Add some noise + ascale *= np.random.uniform(0.95, 1.05) + bscale *= np.random.uniform(0.95, 1.05) + if cscale is not None: + cscale *= np.random.uniform(0.95, 1.05) + dscale *= np.random.uniform(0.95, 1.05) + + # Flip the signs randomly + ascale *= np.random.choice((-1, 1)) + bscale *= np.random.choice((-1, 1)) + if cscale is not None: + cscale *= np.random.choice((-1, 1)) + dscale *= np.random.choice((-1, 1)) + + result_type = dtype or ctype or atype + if "float8" not in result_type: + dscale = None # Not supported, would raise an error + + if ctype and "float8" not in ctype: + cscale = None # Not supported, would raise an error + + return {"a": ascale, "b": bscale, "c": cscale, "d": dscale} + + +def simple_scales(atype, btype, ctype, dtype): + ascale = 1.2 + bscale = 3.4 + cscale = 0.56 + dscale = 0.78 + result_type = dtype or ctype or atype + if "float8" not in result_type: + dscale = None # Not supported, would raise an error + if not ctype or "float8" not in ctype: + cscale = None # Not supported, would raise an error + return {"a": ascale, "b": bscale, "c": cscale, "d": dscale} + + +def fp8_matmul_reference( + a, b, c=None, *args, epilog_inputs=None, quantization_scales=None, options=None, preferences=None, **kwargs +): + """ + Computes FP8-like matmul, but with higher precision. + """ + quantization_scales = check_or_create_options( + _configuration.MatmulQuantizationScales, quantization_scales, "Matmul quantization_scales" + ) + options = check_or_create_options(_configuration.MatmulOptions, options, "Matmul options") + preferences = check_or_create_options(_configuration.MatmulPlanPreferences, preferences, "Matmul preferences") + options.result_type = None + options.result_amax = False + preferences.epilog.aux_type = None + epilog_aux_amax = preferences.epilog.aux_amax + preferences.epilog.aux_amax = False + + if epilog_inputs is None: + epilog_inputs = {} + epilog_inputs = epilog_inputs.copy() + aux_scale = epilog_inputs.pop("aux_quantization_scale", None) + + for key in ("bias", "gelu_aux"): + if epilog_inputs and key in epilog_inputs: + epilog_inputs[key] = epilog_inputs[key].type(torch.float32) + + a_scale = quantization_scales.a if quantization_scales.a is not None else 1 + b_scale = quantization_scales.b if quantization_scales.b is not None else 1 + c_scale = quantization_scales.c if quantization_scales.c is not None else 1 + d_scale = quantization_scales.d if quantization_scales.d is not None else 1 + + ascaled = a.type(torch.float32) * a_scale + bscaled = b.type(torch.float32) * b_scale + cscaled = None if c is None else c_scale * c.type(torch.float32) + result = matmul(ascaled, bscaled, cscaled, *args, options=options, epilog_inputs=epilog_inputs, **kwargs) + if isinstance(result, tuple): + d, aux = result + d *= d_scale + assert len(aux) == 1 + key = list(aux.keys())[0] + if epilog_aux_amax: + aux[f"{key}_amax"] = max(key) + if aux_scale is not None: + aux[key] *= aux_scale + else: + result *= d_scale + + return result + + +def assert_fp8_equal(result, reference, atol=1e-2, rtol=1e-2): + """ + Checks if the result is close enough to the reference. For quantized results, uses + Fp8Helper. + """ + result_type = str(result.dtype).split(".")[-1] + if "float8" in result_type: + ok, info = fp8helpers[result_type].allclose(result, reference, atol=1e-1, rtol=5e-2, return_info=True) + if not ok: + print(f"Absolute error: {info['aerr']} (tolerance {info['atol']})") + print(f"Relative error: {info['rerr']} (tolerance {info['rtol']})") + print("Result:") + print(result) + print("Reference:") + print(reference) + assert ok + else: + assert_tensors_equal(result, reference, atol=atol, rtol=rtol) + + +def generate_inputs(m, n, k, atype, btype, ctype, *, c_transposed=False, min=0, max=5, use_cuda): + """ + Generates matmul inputs of given shapes and types. + """ + + a = sample_matrix("torch", atype, (m, k), use_cuda=use_cuda, min=min, max=max) + b = sample_matrix("torch", btype, (n, k), use_cuda=use_cuda, min=min, max=max).T + + if ctype is not None: + if not c_transposed: + c = sample_matrix("torch", ctype, (m, n), use_cuda=use_cuda, min=min, max=max) + else: + c = sample_matrix("torch", ctype, (n, m), use_cuda=use_cuda, min=min, max=max).T + beta = np.random.uniform(-2, 2) + else: + c = None + beta = None + + alpha = np.random.uniform(-2, 2) + + return a, b, c, alpha, beta diff --git a/tests/nvmath_tests/linalg/advanced/matmul/test_epilog.py b/tests/nvmath_tests/linalg/advanced/matmul/test_epilog.py index ba9fe94..f1fceda 100644 --- a/tests/nvmath_tests/linalg/advanced/matmul/test_epilog.py +++ b/tests/nvmath_tests/linalg/advanced/matmul/test_epilog.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -6,15 +6,25 @@ This set of tests verifies the correctness of the epilog handling. """ +import numpy as np +import pytest + from nvmath.linalg.advanced import matmul, Matmul, MatmulEpilog as Epilog from nvmath.bindings import cublasLt as cublaslt -import pytest -import random -from .utils import * -import numpy as np + +from .utils import ( + compare_tensors, + get_absolute_tolerance, + get_framework, + sample_float_tensor, + sample_matrix, + assert_tensors_equal, + skip_if_cublas_before, + to_numpy, +) try: - from cupy import tanh, sqrt, pi, cosh + import cupy except ModuleNotFoundError: pytest.skip("cupy required for matmul tests", allow_module_level=True) @@ -28,7 +38,7 @@ def relu(x): def gelu(x): - return 0.5 * x * (1 + tanh(sqrt(2 / pi) * (x + 0.0447115 * x**3))) + return 0.5 * x * (1 + cupy.tanh(cupy.sqrt(2 / cupy.pi) * (x + 0.0447115 * x**3))) simple_epilogs = ( @@ -55,7 +65,7 @@ def verify_relu_bitmask(x, bitmask): n, m = x.shape for i in range(n): for j in range(m): - if abs(x[i][j]) <= get_tolerance(x): + if abs(x[i][j]) <= get_absolute_tolerance(x): # This value is dangerously close to 0 and the bitmask might # be incorrect due to precision issues. continue @@ -136,9 +146,9 @@ def dgelu(x): """ Derivative of (tanh-approximated) gelu from the values returned by GELU_AUX """ - sech = lambda x: 1 / cosh(x) + sech = lambda x: 1 / cupy.cosh(x) return ( - 0.5 * tanh(0.0356774 * x**3 + 0.797885 * x) + 0.5 * cupy.tanh(0.0356774 * x**3 + 0.797885 * x) + (0.0535161 * x**3 + 0.398942 * x) * (sech(0.0356774 * x**3 + 0.797885 * x)) ** 2 + 0.5 ) @@ -483,3 +493,63 @@ def test_renamed_epilog_inputs(): epilog_inputs={"not_a_bias": cupy.full((4, 1), np.float32(0.8))}, ), ) + +@pytest.mark.parametrize("epilog", epilogs_with_bias) +@pytest.mark.parametrize("test_case", [ + { + "name": "mismatch_batch_size", + "a_shape": (2, 4, 5), + "b_shape": (2, 5, 8), + "bias_shape": (3, 4, 1), + "error_pattern": "batch dimensions of the bias.*must match", + "make_bias": lambda shape: cupy.full(shape, np.float32(0.8)), + "min_cublas_version": 11703 + }, + { + "name": "mismatched_batch_axis_order", + "a_shape": (2, 3, 4, 5), + "b_shape": (2, 3, 5, 8), + "bias_shape": (2, 3, 4, 1), + "error_pattern": "batch axis order of the bias.*must match", + "make_bias": lambda shape: cupy.lib.stride_tricks.as_strided( + cupy.full((2, 3, 4, 1), np.float32(0.8)), + shape=shape, + strides=(4, 12, 1, 4) + ), + "min_cublas_version": 11703 + }, + { + "name": "non_tileable_batch", + "a_shape": (2, 3, 4, 5), + "b_shape": (2, 3, 5, 8), + "bias_shape": (2, 3, 4, 1), + "error_pattern": "not supported because it is not tileable", + "make_bias": lambda shape: cupy.lib.stride_tricks.as_strided( + cupy.full(shape, np.float32(0.8)), + shape=shape, + strides=(16, 4, 1, 4) + ), + "min_cublas_version": 11703 + }, + { + "name": "invalid_stride", + "a_shape": (4, 5), + "b_shape": (5, 8), + "bias_shape": (4, 2), + "error_pattern": "stride of the bias.*must be 1", + "make_bias": lambda shape: cupy.lib.stride_tricks.as_strided( + cupy.full(shape, np.float32(0.8)), + shape=(4, 1), + strides=(2, 1) + ), + "min_cublas_version": 11501 + } +]) +def test_invalid_bias(epilog, test_case): + skip_if_cublas_before(test_case["min_cublas_version"]) + + a = sample_float_tensor(test_case["a_shape"]) + b = sample_float_tensor(test_case["b_shape"]) + bias = test_case["make_bias"](test_case["bias_shape"]) + with pytest.raises(ValueError, match=test_case["error_pattern"]): + matmul(a, b, epilog=epilog, epilog_inputs={"bias": bias}) \ No newline at end of file diff --git a/tests/nvmath_tests/linalg/advanced/matmul/test_fp8.py b/tests/nvmath_tests/linalg/advanced/matmul/test_fp8.py new file mode 100644 index 0000000..47e22a9 --- /dev/null +++ b/tests/nvmath_tests/linalg/advanced/matmul/test_fp8.py @@ -0,0 +1,647 @@ +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + + +try: + import torch +except ImportError: + torch = None +import pytest +from .utils import sample_matrix, assert_tensors_equal, matmul_with_random_autotune +from .fp8_utils import choose_scales, generate_inputs, assert_fp8_equal, fp8_matmul_reference +from nvmath.linalg.advanced import Matmul, matmul, MatmulQuantizationScales +from nvmath._internal.typemaps import NAME_TO_DATA_TYPE +from nvmath.bindings import cublasLt as cublaslt + +if torch is None: + pytest.skip("Torch is required for FP8 tests", allow_module_level=True) + +if (torch.cuda.get_device_properties(0).major, torch.cuda.get_device_properties(0).minor) < (8, 9): + pytest.skip("CC>=8.9 is required for FP8 tests", allow_module_level=True) + +if cublaslt.get_version() < 120800: + pytest.skip("cuBLAS 120800 is required for FP8 tests", allow_module_level=True) + +SUPPORTED_TYPE_COMBINATIONS = [ + # No type specification for D. + ("float8_e4m3fn", "float8_e4m3fn", "float16", None), + ("float8_e4m3fn", "float8_e4m3fn", "bfloat16", None), + ("float8_e4m3fn", "float8_e4m3fn", "float32", None), + ("float8_e4m3fn", "float8_e5m2", "float32", None), + ("float8_e4m3fn", "float8_e5m2", "float16", None), + ("float8_e4m3fn", "float8_e5m2", "bfloat16", None), + # No type specification for D. No C. + ("float8_e4m3fn", "float8_e4m3fn", None, None), + ("float8_e4m3fn", "float8_e5m2", None, None), + ("float8_e5m2", "float8_e4m3fn", None, None), + # Explicit type specification for A, B, C, D. + ("float8_e4m3fn", "float8_e4m3fn", "float16", "float16"), + ("float8_e4m3fn", "float8_e4m3fn", "float16", "float8_e4m3fn"), + ("float8_e4m3fn", "float8_e4m3fn", "bfloat16", "bfloat16"), + ("float8_e4m3fn", "float8_e4m3fn", "bfloat16", "float8_e4m3fn"), + ("float8_e4m3fn", "float8_e4m3fn", "float32", "float32"), + ("float8_e4m3fn", "float8_e5m2", "bfloat16", "bfloat16"), + ("float8_e4m3fn", "float8_e5m2", "bfloat16", "float8_e5m2"), + ("float8_e4m3fn", "float8_e5m2", "bfloat16", "float8_e4m3fn"), + ("float8_e4m3fn", "float8_e5m2", "float16", "float16"), + ("float8_e4m3fn", "float8_e5m2", "float16", "float8_e5m2"), + ("float8_e4m3fn", "float8_e5m2", "float16", "float8_e4m3fn"), + ("float8_e5m2", "float8_e4m3fn", "bfloat16", "bfloat16"), + ("float8_e5m2", "float8_e4m3fn", "bfloat16", "float8_e5m2"), + ("float8_e5m2", "float8_e4m3fn", "bfloat16", "float8_e4m3fn"), + ("float8_e5m2", "float8_e4m3fn", "float16", "float16"), + ("float8_e5m2", "float8_e4m3fn", "float16", "float8_e5m2"), + ("float8_e5m2", "float8_e4m3fn", "float16", "float8_e4m3fn"), + # Explicit type specification for A, B, D. No C. + ("float8_e4m3fn", "float8_e4m3fn", None, "float16"), + ("float8_e4m3fn", "float8_e4m3fn", None, "bfloat16"), + ("float8_e4m3fn", "float8_e4m3fn", None, "float32"), + ("float8_e4m3fn", "float8_e5m2", None, "bfloat16"), + ("float8_e4m3fn", "float8_e5m2", None, "float8_e5m2"), + ("float8_e4m3fn", "float8_e5m2", None, "float16"), + ("float8_e5m2", "float8_e4m3fn", None, "bfloat16"), + ("float8_e5m2", "float8_e4m3fn", None, "float8_e5m2"), + ("float8_e5m2", "float8_e4m3fn", None, "float16"), + ("float8_e5m2", "float8_e4m3fn", None, "float8_e4m3fn"), +] + +if cublaslt.get_version() < 120600: + SUPPORTED_TYPE_COMBINATIONS = [(a, b, c, d) for (a, b, c, d) in SUPPORTED_TYPE_COMBINATIONS if a == b] + + +def expected_result_type(atype, btype, ctype, dtype): + return dtype or ctype or atype + + +SUPPORTED_TYPE_COMBINATIONS_WITH_FP8_D = [ + (a, b, c, d) for (a, b, c, d) in SUPPORTED_TYPE_COMBINATIONS if "float8" in expected_result_type(a, b, c, d) +] + +SUPPORTED_TYPE_COMBINATIONS_WITH_NON_FP8_D = [ + t for t in SUPPORTED_TYPE_COMBINATIONS if t not in SUPPORTED_TYPE_COMBINATIONS_WITH_FP8_D +] + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize( + "m,n,k", + ( + (16, 16, 16), + (32, 16, 16), + (16, 32, 16), + (16, 16, 32), + (64, 32, 16), + (16, 96, 32), + (64, 16, 32), + (64, 96, 16), + ), +) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +def test_stateful(m, n, k, atype, btype, ctype, dtype, use_cuda): + """ + General test of FP8 multiplication. + """ + a, b, c, alpha, beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + quantization_scales = choose_scales(a, b, c, atype, btype, ctype, dtype, alpha=alpha, beta=beta) + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None} + + mm = Matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + mm.plan() + result = mm.execute() + + assert str(result.dtype).split(".")[-1] == expected_result_type(atype, btype, ctype, dtype) + + reference = fp8_matmul_reference(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + assert_fp8_equal(result, reference) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize( + "m,n,k", + ((16, 16, 16),), +) +@pytest.mark.parametrize("amax", (True, False)) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +def test_autotuning(m, n, k, atype, btype, ctype, dtype, amax, use_cuda): + """ + Tests if autotuning works with FP8 multiplication. + """ + a, b, c, alpha, beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + quantization_scales = choose_scales(a, b, c, atype, btype, ctype, dtype, alpha=alpha, beta=beta) + result_type = expected_result_type(atype, btype, ctype, dtype) + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None, "result_amax": amax and "float8" in result_type} + mm = Matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + mm.plan() + mm.autotune() + result = mm.execute() + if isinstance(result, tuple): + result = result[0] + reference = fp8_matmul_reference(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + assert_fp8_equal(result, reference) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize( + "m,n,k", + ((96, 128, 16),), +) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +def test_stateless(m, n, k, atype, btype, ctype, dtype, use_cuda): + """ + Tests if stateless `matmul` supports quantization_scales and options. + """ + a, b, c, alpha, beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + quantization_scales = choose_scales(a, b, c, atype, btype, ctype, dtype, alpha=alpha, beta=beta) + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None} + result = matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + assert str(result.dtype).split(".")[-1] == expected_result_type(atype, btype, ctype, dtype) + + reference = fp8_matmul_reference(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + + assert_fp8_equal(result, reference) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize( + "a_batch,b_batch,c_batch,d_batch", + ( + ((), (), (), ()), + ((3,), (3,), (3,), (3,)), + ((8,), (8,), (8,), (8,)), + ((5,), (), (5,), (5,)), + ((), (2,), (2,), (2,)), + ((2, 3), (2, 3), (2, 3), (2, 3)), + ), +) +@pytest.mark.parametrize( + "m,n,k", + ((16, 16, 16),), +) +@pytest.mark.parametrize(("use_cuda"), (True,)) +def test_batching(m, n, k, atype, btype, ctype, dtype, a_batch, b_batch, c_batch, d_batch, use_cuda): + """ + Tests if batching works with FP8. + """ + + def sample_batch(batch_shape, matrix_shape, type, transposed=False): + shape = (*batch_shape, *matrix_shape) + if transposed: + shape = (*shape[:-2], shape[-1], shape[-2]) + x = sample_matrix("torch", type, shape, use_cuda=use_cuda, min=0, max=2) + return x.swapaxes(-1, -2) if transposed else x + + a = sample_batch(a_batch, (m, k), atype, transposed=False) + b = sample_batch(b_batch, (k, n), btype, transposed=True) + + if ctype is not None: + c = sample_batch(c_batch, (m, n), ctype, transposed=False) + beta = 0.12 + else: + c = None + beta = None + + alpha = 0.32 + + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None} + quantization_scales = choose_scales(a, b, c, atype, btype, ctype, dtype, alpha=alpha, beta=beta) + result = matmul_with_random_autotune( + a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options + ) + + expected_result_shape = (*d_batch, m, n) + assert result.shape == expected_result_shape + + reference = fp8_matmul_reference(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + assert_fp8_equal(result, reference) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize( + "m,n,k", + ((64, 32, 16),), +) +@pytest.mark.parametrize("a_scale_kind", ("scalar", "gpu", "cpu")) +@pytest.mark.parametrize("b_scale_kind", ("scalar", "gpu", "cpu")) +@pytest.mark.parametrize("c_scale_kind", ("scalar", "gpu", "cpu")) +@pytest.mark.parametrize("d_scale_kind", ("scalar", "gpu", "cpu")) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +def test_tensor_scales(m, n, k, atype, btype, ctype, dtype, a_scale_kind, b_scale_kind, c_scale_kind, d_scale_kind, use_cuda): + """ + Test scales provided as scalars/GPU tensors/CPU tensors + """ + a, b, c, alpha, beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + scalar_scales = choose_scales(a, b, c, atype, btype, ctype, dtype, alpha=alpha, beta=beta) + + def prepare_scales(scales): + """ + Change some of the scales into tensors + """ + + def wrap_scale(x, kind): + if kind == "scalar" or x is None: + return x + tensor = torch.as_tensor(x, dtype=torch.float32) + return tensor.cuda() if kind == "gpu" else tensor + + return { + "a": wrap_scale(scales["a"], a_scale_kind), + "b": wrap_scale(scales["b"], b_scale_kind), + "c": wrap_scale(scales["c"], c_scale_kind), + "d": wrap_scale(scales["d"], d_scale_kind), + } + + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None} + + scales = prepare_scales(scalar_scales) + mm = Matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=scales, options=options) + mm.plan() + result = mm.execute() + reference = fp8_matmul_reference(a, b, c, alpha=alpha, beta=beta, quantization_scales=scalar_scales, options=options) + assert_fp8_equal(result, reference) + + # In-place modification of GPU scales + if a_scale_kind == "gpu": + scalar_scales["a"] *= 0.5 + scales["a"].copy_(scalar_scales["a"]) + if b_scale_kind == "gpu": + scalar_scales["b"] *= -1 + scales["b"].copy_(scalar_scales["b"]) + if c_scale_kind == "gpu" and scalar_scales["c"] is not None: + scalar_scales["c"] *= -1 + scales["c"].copy_(scalar_scales["c"]) + if d_scale_kind == "gpu" and scalar_scales["d"] is not None: + scalar_scales["d"] *= 0.5 + scales["d"].copy_(scalar_scales["d"]) + result = mm.execute() + reference = fp8_matmul_reference(a, b, c, alpha=alpha, beta=beta, quantization_scales=scalar_scales, options=options) + assert_fp8_equal(result, reference) + + # Reset of the scales + new_a, new_b, new_c, new_alpha, new_beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + new_scalar_scales = choose_scales(new_a, new_b, new_c, atype, btype, ctype, dtype, alpha=new_alpha, beta=new_beta) + new_scales = prepare_scales(new_scalar_scales) + mm.reset_operands(a=new_a, b=new_b, c=new_c, quantization_scales=new_scales, alpha=new_alpha, beta=new_beta) + result = mm.execute() + reference = fp8_matmul_reference( + new_a, new_b, new_c, alpha=new_alpha, beta=new_beta, quantization_scales=new_scalar_scales, options=options + ) + assert_fp8_equal(result, reference) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS_WITH_FP8_D) +@pytest.mark.parametrize( + "m,n,k", + ( + (16, 16, 16), + (96, 64, 16), + ), +) +@pytest.mark.parametrize(("stateless"), (True, False)) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +def test_amax(m, n, k, atype, btype, ctype, dtype, stateless, use_cuda): + """ + Test if amax is computed properly. + """ + a, b, c, alpha, beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + scales = choose_scales(a, b, c, atype, btype, ctype, dtype, alpha=alpha, beta=beta) + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None, "result_amax": True} + if not stateless: + with Matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=scales, options=options) as mm: + mm.plan() + result, aux = mm.execute() + else: + result, aux = matmul_with_random_autotune(a, b, c, alpha=alpha, beta=beta, quantization_scales=scales, options=options) + assert len(aux) == 1 + amax = aux["result_amax"] + reference = fp8_matmul_reference(a, b, c, alpha=alpha, beta=beta, options=options, quantization_scales=scales) + assert_fp8_equal(result, reference) + not_scaled_reference = fp8_matmul_reference( + a, + b, + c, + alpha=alpha, + beta=beta, + options=options, + quantization_scales={k: v if k != "d" else 1.0 for k, v in scales.items()}, + ) + assert_tensors_equal(amax, (not_scaled_reference.abs().max()), atol=0.01, rtol=1e-3) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize( + "reset_a,reset_b,reset_c", + ((a, b, c) for a in (True, False) for b in (True, False) for c in (True, False) if (a, b, c) != (False, False, False)), +) +@pytest.mark.parametrize("reset_alpha", (True, False)) +@pytest.mark.parametrize("reset_beta", (True, False)) +@pytest.mark.parametrize("m,n,k", ((16, 16, 16),)) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +def test_reset_operands(m, n, k, atype, btype, ctype, dtype, reset_a, reset_b, reset_c, reset_alpha, reset_beta, use_cuda): + """ + Tests if reset_operands works with FP8 matmuls without resetting the scales. + """ + if reset_c and ctype is None: + pytest.skip("Can't reset C because C is not specified") + a, b, c, alpha, beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + quantization_scales = choose_scales(a, b, c, atype, btype, ctype, dtype, alpha=alpha, beta=beta) + + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None} + mm = Matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + mm.plan() + result1 = mm.execute() + reference1 = fp8_matmul_reference(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + assert_fp8_equal(result1, reference1) + + new_a, new_b, new_c, new_alpha, new_beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + + reset_kwargs = {} + if reset_a: + reset_kwargs["a"] = a = new_a + if reset_b: + reset_kwargs["b"] = b = new_b + if reset_c: + reset_kwargs["c"] = c = new_c + if reset_alpha: + reset_kwargs["alpha"] = alpha = new_alpha + if reset_beta: + reset_kwargs["beta"] = beta = new_beta + + mm.reset_operands(**reset_kwargs) + result2 = mm.execute() + reference2 = fp8_matmul_reference(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + assert_fp8_equal(result2, reference2) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize("reset_a_scale", (True, False)) +@pytest.mark.parametrize("reset_b_scale", (True, False)) +@pytest.mark.parametrize("reset_c_scale", (True, False)) +@pytest.mark.parametrize("reset_d_scale", (True, False)) +@pytest.mark.parametrize("m,n,k", ((16, 16, 16),)) +@pytest.mark.parametrize("use_cuda", (True,)) +def test_reset_quantization_scales( + m, n, k, atype, btype, ctype, dtype, reset_a_scale, reset_b_scale, reset_c_scale, reset_d_scale, use_cuda +): + """ + Tests if reset_operands allows resetting (some or all) quantization_scales. + """ + a, b, c, alpha, beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + quantization_scales = choose_scales(a, b, c, atype, btype, ctype, dtype, alpha=alpha, beta=beta) + + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None} + mm = Matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + mm.plan() + result1 = mm.execute() + reference1 = fp8_matmul_reference(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + assert_fp8_equal(result1, reference1) + + new_a, new_b, new_c, new_alpha, new_beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + new_quantization_scales = choose_scales(new_a, new_b, new_c, atype, btype, ctype, dtype, alpha=new_alpha, beta=new_beta) + + reset_kwargs = {"a": new_a} + + reset_quantization_scales = {} + if reset_a_scale: + reset_quantization_scales["a"] = quantization_scales["a"] = new_quantization_scales["a"] + if reset_b_scale: + reset_quantization_scales["b"] = quantization_scales["b"] = new_quantization_scales["b"] + if reset_c_scale: + reset_quantization_scales["c"] = quantization_scales["c"] = new_quantization_scales["c"] + if reset_d_scale: + reset_quantization_scales["d"] = quantization_scales["d"] = new_quantization_scales["d"] + if reset_quantization_scales: + reset_kwargs["quantization_scales"] = reset_quantization_scales + + mm.reset_operands(**reset_kwargs) + result2 = mm.execute() + reference2 = fp8_matmul_reference( + new_a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options + ) + assert_fp8_equal(result2, reference2) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS[0:1]) +@pytest.mark.parametrize("m,n,k", ((16, 16, 16),)) +@pytest.mark.parametrize("use_cuda", (True,)) +def test_quantization_scales_as_object(m, n, k, atype, btype, ctype, dtype, use_cuda): + """ + Tests if passing the scales as an instance of MatmulQuantizationScales works. + """ + a, b, c, alpha, beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + dict_scales = choose_scales(a, b, c, atype, btype, ctype, dtype, alpha=alpha, beta=beta) + quantization_scales = MatmulQuantizationScales( + a=dict_scales["a"], b=dict_scales["b"], c=dict_scales["c"], d=dict_scales["d"] + ) + result = matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales) + assert str(result.dtype).split(".")[-1] == expected_result_type(atype, btype, ctype, dtype) + + reference = fp8_matmul_reference(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales) + + assert_fp8_equal(result, reference) + + +############### +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize("a_scale", (True, False)) +@pytest.mark.parametrize("b_scale", (True, False)) +@pytest.mark.parametrize("c_scale", (True, False)) +@pytest.mark.parametrize("d_scale", (True, False)) +@pytest.mark.parametrize("use_cuda", (True,)) +def test_validation_required_quantization_scales(atype, btype, ctype, dtype, a_scale, b_scale, c_scale, d_scale, use_cuda): + """ + Tests if unspecified quantization_scales trigger an error. + """ + a, b, c, alpha, beta = generate_inputs(16, 16, 16, atype, btype, ctype, use_cuda=use_cuda) + all_quantization_scales = choose_scales(a, b, c, atype, btype, ctype, dtype, alpha=alpha, beta=beta) + + quantization_scales = {} + if a_scale: + quantization_scales["a"] = all_quantization_scales["a"] + if b_scale: + quantization_scales["b"] = all_quantization_scales["b"] + if c_scale: + quantization_scales["c"] = all_quantization_scales["c"] + if d_scale: + quantization_scales["d"] = all_quantization_scales["d"] + + quantization_scales_ok = all(x in quantization_scales or all_quantization_scales[x] is None for x in "abcd") + + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None} + + with pytest.raises(ValueError, match=r"Scales are required for narrow-precision \(FP8 and lower\) operations"): + matmul(a, b, c, alpha=alpha, beta=beta, options=options) + + if quantization_scales_ok: + result = matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + reference = fp8_matmul_reference( + a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options + ) + assert_fp8_equal(result, reference) + else: + with pytest.raises(ValueError): + matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS[1:2]) +@pytest.mark.parametrize("m,n,k", ((16, 16, 16),)) +@pytest.mark.parametrize("use_cuda", (True,)) +def test_validation_invalid_quantization_scales_type(m, n, k, atype, btype, ctype, dtype, use_cuda): + """ + Tests what happens when an invalid type is provided for `quantization_scales`. + """ + a, b, c, alpha, beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + with pytest.raises( + TypeError, match="Scale factors must be provided as an object of type MatmulQuantizationScales or as a dict" + ): + matmul(a, b, quantization_scales="oh no!") + + +@pytest.mark.parametrize( + "atype,btype, ctype", + ( + ("float8_e4m3fn", "float8_e5m2", "float16"), + ("float8_e5m2", "float8_e4m3fn", None), + ("float8_e5m2", "float8_e4m3fn", "float32"), + ), +) +@pytest.mark.parametrize("m,n,k", ((16, 16, 16),)) +def test_validation_unsupported_different_ab_types(m, n, k, atype, btype, ctype): + version = cublaslt.get_version() + if version >= 120600: + pytest.skip(f"Different A and B types are supported for cuBLAS {version}") + a, b, c, alpha, beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=True) + with pytest.raises( + ValueError, match=f"FP8 multiplication of {atype} and {btype} is not supported in cuBLASLt version {version}" + ): + matmul(a, b, quantization_scales={"a": 1, "b": 1, "c": 1, "d": 1}) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize( + "m,n,k", + ( + (16, 16, 8), + (32, 8, 16), + (8, 16, 16), + (32, 32, 12), + (32, 36, 16), + (4, 48, 32), + (32, 96, 17), + (80, 11, 64), + (19, 96, 128), + (33, 44, 55), + ), +) +def test_validation_invalid_sizes(m, n, k, atype, btype, ctype, dtype): + """ + Tests if invalid size raises an error. + """ + a, b, c, alpha, beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=True) + quantization_scales = choose_scales(a, b, c, atype, btype, ctype, dtype, alpha=alpha, beta=beta) + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None} + + with pytest.raises(ValueError, match="must be divisible by 16"): + matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize( + "m,n,k", + ((64, 64, 64),), +) +@pytest.mark.parametrize("misaligned", ("a", "b")) +@pytest.mark.parametrize("offset", (1, 2, 4, 8, 12)) +def test_validation_misaligned(m, n, k, atype, btype, ctype, dtype, misaligned, offset): + """ + Tests if invalid alignment raises an error or returns a correct result. + """ + a, b, c, alpha, beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=True) + + def gen_misaligned(shape, type): + assert len(shape) == 2 + aligned = torch.rand(shape[0] * shape[1] + offset).type(getattr(torch, type)).cuda() + return aligned[offset:].reshape(shape) + + if misaligned == "a": + a = gen_misaligned((m, k), atype) + if misaligned == "b": + b = gen_misaligned((n, k), btype).T + + quantization_scales = choose_scales(a, b, c, atype, btype, ctype, dtype, alpha=alpha, beta=beta) + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None} + + with pytest.raises(ValueError, match="should be aligned to 16 bytes"): + matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + + +@pytest.mark.parametrize( + "atype,btype,ctype,dtype", + [ + (a, b, c, d) + for (a, b, c, d) in SUPPORTED_TYPE_COMBINATIONS + if "float8" not in expected_result_type(a, b, c, d) or (c and "float8" not in c) + ], +) +@pytest.mark.parametrize( + "m,n,k", + ((16, 16, 16),), +) +def test_validation_non_fp8_scale(m, n, k, atype, btype, ctype, dtype): + """ + Tests if scales are prohibited for non-FP8 tensors + """ + a, b, c, alpha, beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=True) + quantization_scales = {"a": 1, "b": 1, "c": 1, "d": 1} + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None} + with pytest.raises( + ValueError, + match=r"Quantization scaling is not supported for . when it is not a narrow-precision \(FP8 and lower\) type", + ): + matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + + +@pytest.mark.parametrize( + "atype,btype,ctype,dtype", + [ + (a, b, c, d) + for (a, b, c, d) in SUPPORTED_TYPE_COMBINATIONS + if "float8" not in expected_result_type(a, b, c, d) or (c and "float8" not in c) + ], +) +@pytest.mark.parametrize( + "m,n,k", + ((16, 16, 16),), +) +def test_validation_non_fp8_scale_reset(m, n, k, atype, btype, ctype, dtype): + """ + Tests if attempt to reset the scale for non-FP8 tensor raises an error. + """ + a, b, c, alpha, beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=True) + quantization_scales = choose_scales(a, b, c, atype, btype, ctype, dtype, alpha=alpha, beta=beta) + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None} + with Matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) as mm: + mm.plan() + with pytest.raises( + ValueError, + match=r"Quantization scaling is not supported for . when it is not a narrow-precision \(FP8 and lower\) type", + ): + mm.reset_operands(a=a, quantization_scales={"a": 1, "b": 1, "c": 1, "d": 1}) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS_WITH_NON_FP8_D) +@pytest.mark.parametrize( + "m,n,k", + ((16, 16, 16),), +) +@pytest.mark.parametrize(("stateless"), (True, False)) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +def test_validation_non_fp8_amax(m, n, k, atype, btype, ctype, dtype, stateless, use_cuda): + """ + Test if amax is not supported for non-FP8 D. + """ + a, b, c, alpha, beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + scales = choose_scales(a, b, c, atype, btype, ctype, dtype, alpha=alpha, beta=beta) + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None, "result_amax": True} + with pytest.raises(ValueError, match=r"result_amax=True is allowed only for narrow-precision \(FP8 and lower\) results"): + matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=scales, options=options) diff --git a/tests/nvmath_tests/linalg/advanced/matmul/test_fp8_epilogs.py b/tests/nvmath_tests/linalg/advanced/matmul/test_fp8_epilogs.py new file mode 100644 index 0000000..5b7b177 --- /dev/null +++ b/tests/nvmath_tests/linalg/advanced/matmul/test_fp8_epilogs.py @@ -0,0 +1,310 @@ +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + + +try: + import torch +except ImportError: + torch = None +import pytest +from .utils import sample_matrix, allow_cublas_unsupported, matmul_with_random_autotune +from .fp8_utils import assert_fp8_equal, fp8_matmul_reference, simple_scales, generate_inputs, choose_scales +from nvmath.linalg.advanced import Matmul, MatmulEpilog as Epilog +from nvmath._internal.typemaps import NAME_TO_DATA_TYPE +from nvmath.bindings import cublasLt as cublaslt +from .test_fp8 import SUPPORTED_TYPE_COMBINATIONS, expected_result_type +from contextlib import nullcontext + +if torch is None: + pytest.skip("Torch is required for FP8 tests", allow_module_level=True) + +COMPUTE_CAPABILITY = (torch.cuda.get_device_properties(0).major, torch.cuda.get_device_properties(0).minor) + +if COMPUTE_CAPABILITY < (8, 9): + pytest.skip("CC>=8.9 is required for FP8 tests", allow_module_level=True) + +if cublaslt.get_version() < 120800: + pytest.skip("cuBLAS 120800 is required for FP8 tests", allow_module_level=True) + + +def unpack_bitmask(bitmask, shape): + if len(bitmask.shape) > 2: + return torch.stack([unpack_bitmask(bitmask[i], shape) for i in range(bitmask.shape[0])]) + result = torch.zeros(shape) + n, m = shape + for i in range(n): + for j in range(m): + result[i][j] = bool(int(bitmask[i // 8][j].item()) & (1 << i % 8)) + return result + + +@pytest.mark.parametrize( + "m,n,k", + ( + (16, 16, 16), + (80, 96, 16), + ), +) +@pytest.mark.parametrize( + "a_batch,b_batch,c_batch,d_batch", + ( + ((), (), (), ()), + ((2, 3), (2, 3), (2, 3), (2, 3)), + ), +) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize( + "epilog_name,order,epilog_aux_type,epilog_aux_amax", + ( + ("BIAS", "col", None, False), + ("RELU", "col", None, False), + ("RELU", "row", None, False), + ("RELU_AUX", "col", None, False), + ("RELU_BIAS", "col", None, False), + ("RELU_AUX_BIAS", "col", None, False), + ("GELU", "col", None, False), + ("GELU_AUX", "col", None, False), + ("GELU_BIAS", "col", None, False), + ("GELU_AUX_BIAS", "col", None, False), + ("BGRADA", "col", None, False), + ("BGRADB", "col", None, False), + ("DRELU", "col", None, False), + ("DRELU_BGRAD", "col", None, False), + ("DGELU", "col", None, False), + ("DGELU_BGRAD", "col", None, False), + ("GELU_AUX", "col", "float8_e4m3fn", True), + ("GELU_AUX", "col", "float8_e4m3fn", False), + ), +) +def test_epilogs( + m, + n, + k, + atype, + btype, + ctype, + dtype, + epilog_name, + order, + a_batch, + b_batch, + c_batch, + d_batch, + epilog_aux_type, + epilog_aux_amax, + use_cuda, +): + epilog = getattr(Epilog, epilog_name) + + result_type = expected_result_type(atype, btype, ctype, dtype) + inferred_ctype = ctype or ("float16" if "float8" in result_type else result_type) + + # Handle gaps in cuBLAS support + allow_not_supported = False + allow_not_supported |= (inferred_ctype, result_type) == ("float32", "float32") and "GELU" in epilog_name + allow_not_supported |= epilog_name in ("DGELU", "GELU_AUX") and inferred_ctype == "float16" and result_type == "float16" + allow_not_supported |= epilog_name in ("DRELU", "DGELU") and "float16" in inferred_ctype and "float8" in result_type + allow_not_supported |= "BGRAD" in epilog_name + allow_not_supported |= epilog_name.startswith("RELU_AUX") and atype != btype + allow_not_supported |= epilog_name == "RELU_AUX_BIAS" and inferred_ctype == "float32" + allow_not_supported |= epilog_name in ("GELU", "GELU_BIAS") and "float8" in result_type + allow_not_supported |= epilog_aux_type is not None and not ( + atype == "float8_e4m3fn" and btype == "float8_e4m3fn" and ctype == "float16" and epilog_name == "GELU_AUX" + ) + if COMPUTE_CAPABILITY <= (8, 9): + allow_not_supported |= "AUX" in epilog_name and "float8" in result_type and d_batch != () + allow_not_supported |= epilog_name.startswith("GELU_AUX") and atype != btype + + def sample_batch(batch_shape, matrix_shape, type, transposed=False): + shape = (*batch_shape, *matrix_shape) + if transposed: + shape = (*shape[:-2], shape[-1], shape[-2]) + x = sample_matrix("torch", type, shape, use_cuda=use_cuda, min=-0.2, max=1) + return x.swapaxes(-1, -2) if transposed else x + + a = sample_batch(a_batch, (m, k), atype, transposed=False) + b = sample_batch(b_batch, (k, n), btype, transposed=True) + alpha, beta = 0.8, None + if ctype is not None: + c = sample_batch(c_batch, (m, n), ctype, transposed=(order == "col")) + beta = 0.12 + else: + c = None + beta = None + + quantization_scales = simple_scales(atype, btype, ctype, dtype) + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None} + preferences = { + "epilog": { + "aux_type": NAME_TO_DATA_TYPE[epilog_aux_type] if epilog_aux_type else None, + "aux_amax": epilog_aux_amax, + } + } + + # Prepare epilog inputs if needed + inputs = {} + if epilog_aux_type and "float8" in epilog_aux_type: + inputs["aux_quantization_scale"] = 0.45 + if "BIAS" in epilog_name: + bias_type = "float16" if inferred_ctype == "float16" else "bfloat16" + bias = sample_matrix("torch", bias_type, (m,), use_cuda=use_cuda, min=0, max=1) + inputs["bias"] = bias + if "DRELU" in epilog_name: + round_16 = lambda x: (x + 15) // 16 * 16 + inputs["relu_aux"] = torch.randint(low=0, high=256, size=(n, round_16(m // 16))).type(torch.uint8).T + if "DGELU" in epilog_name: + if order == "col": + inputs["gelu_aux"] = sample_matrix("torch", result_type, (n, m), use_cuda=use_cuda, min=-5, max=5).T + else: + inputs["gelu_aux"] = sample_matrix("torch", result_type, (m, n), use_cuda=use_cuda, min=-5, max=5) + + # Run matmul. Allow cuBLAS NOT_SUPPORTED error for certain configurations (see above) + def unpack_matmul(result): + return result if isinstance(result, tuple) else (result, {}) + + with ( + nullcontext() + if not allow_not_supported + else allow_cublas_unsupported( + message=f"FP8 epilog not supported by cuBLAS: {epilog_name} for A:{atype} B:{btype} C:{ctype} D:{dtype}", + allow_invalid_value=True, + ) + ): + result, aux = unpack_matmul( + matmul_with_random_autotune( + a, + b, + c, + alpha=alpha, + beta=beta, + epilog=epilog, + quantization_scales=quantization_scales, + options=options, + preferences=preferences, + epilog_inputs=inputs, + ) + ) + + assert result.shape == (*d_batch, m, n) + + # Compute the reference and compare + reference, reference_aux = unpack_matmul( + fp8_matmul_reference( + a, + b, + c, + alpha=alpha, + beta=beta, + epilog=epilog, + quantization_scales=quantization_scales, + options=options, + preferences=preferences, + epilog_inputs=inputs, + ) + ) + if "GELU" in epilog_name and result_type not in ("float16", "float32"): + assert_fp8_equal(result, reference, atol=1e-1, rtol=1e-1) + else: + assert_fp8_equal(result, reference) + + # Compare auxiliary outputs + assert set(aux.keys()) == set(reference_aux.keys()) + for key in aux: + if key == "relu_aux": + x = unpack_bitmask(aux[key], (m, n)) + y = unpack_bitmask(reference_aux[key], (m, n)) + assert torch.mean((x == y).type(torch.float32)) > 0.99 + elif key == "gelu_aux": + assert_fp8_equal(aux[key], reference_aux[key]) + if epilog_aux_type is not None: + assert str(aux[key].dtype).split(".")[-1] == epilog_aux_type + elif key == "gelu_aux_amax": + assert torch.allclose( + aux["gelu_aux_amax"], + (aux["gelu_aux"].type(torch.float32) / inputs.get("aux_quantization_scale", 1)).abs().max(), + atol=1e-1, + rtol=1e-1, + ) + elif key == "drelu_bgrad" or key == "dgelu_bgrad": + assert_fp8_equal(aux[key], reference.sum(axis=-1, keepdims=(d_batch != ())), atol=1e-1, rtol=1e-1) + if epilog_aux_type is not None: + assert str(aux[key].dtype).split(".")[-1] == epilog_aux_type + else: + raise RuntimeError(f"Test for {key} not implemented") + + +@pytest.mark.parametrize( + "m,n,k", + ((80, 96, 16),), +) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +@pytest.mark.parametrize("atype,btype,ctype,dtype", (("float8_e4m3fn", "float8_e4m3fn", None, None),)) +@pytest.mark.parametrize( + "epilog_name,epilog_aux_type", + (("GELU_AUX", "float8_e4m3fn"),), +) +def test_epilog_aux_scale_reset( + m, + n, + k, + atype, + btype, + ctype, + dtype, + epilog_name, + epilog_aux_type, + use_cuda, +): + """ + Test if reset_operands can reset epilog aux scales. + """ + epilog = getattr(Epilog, epilog_name) + + a, b, c, alpha, beta = generate_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + quantization_scales = choose_scales(a, b, c, atype, btype, ctype, dtype, alpha=alpha, beta=beta) + options = { + "result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None, + } + preferences = { + "epilog": { + "aux_type": NAME_TO_DATA_TYPE[epilog_aux_type] if epilog_aux_type else None, + } + } + inputs = {"aux_quantization_scale": 10} + mm = Matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=quantization_scales, options=options) + mm.plan(epilog=epilog, epilog_inputs=inputs, preferences=preferences) + result, aux = mm.execute() + reference, reference_aux = fp8_matmul_reference( + a, + b, + c, + alpha=alpha, + beta=beta, + quantization_scales=quantization_scales, + options=options, + preferences=preferences, + epilog_inputs=inputs, + epilog=epilog, + ) + assert_fp8_equal(result, reference) + assert_fp8_equal(aux["gelu_aux"], reference_aux["gelu_aux"]) + + inputs2 = {"aux_quantization_scale": -0.1} + mm.reset_operands(a=a, epilog_inputs=inputs2) + result2, aux2 = mm.execute() + reference2, reference_aux2 = fp8_matmul_reference( + a, + b, + c, + alpha=alpha, + beta=beta, + quantization_scales=quantization_scales, + options=options, + preferences=preferences, + epilog_inputs=inputs2, + epilog=epilog, + ) + assert_fp8_equal(result2, reference2) + assert_fp8_equal(aux2["gelu_aux"], reference_aux2["gelu_aux"]) diff --git a/tests/nvmath_tests/linalg/advanced/matmul/test_fp8_utils.py b/tests/nvmath_tests/linalg/advanced/matmul/test_fp8_utils.py new file mode 100644 index 0000000..44a1c4a --- /dev/null +++ b/tests/nvmath_tests/linalg/advanced/matmul/test_fp8_utils.py @@ -0,0 +1,88 @@ +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + + +try: + import torch +except ImportError: + torch = None +import pytest +from .fp8_utils import fp8helpers +import numpy as np + +if torch is None: + pytest.skip("Torch is required for FP8 tests", allow_module_level=True) + + +def fp8_values(dtype, finite=False): + values = list(torch.frombuffer(bytes(range(256)), dtype=getattr(torch, dtype))) + values = [x.item() for x in values if not finite or torch.isfinite(x.type(torch.float32))] + return values + + +@pytest.mark.parametrize( + "format,left,value,right", + ( + ("float8_e4m3fn", 0.00390625, 0.005859375, 0.0078125), + ("float8_e4m3fn", 0.0, 0.001953125, 0.00390625), + ("float8_e4m3fn", -0.00390625, -0.001953125, 0.0), + ("float8_e4m3fn", -288.0, -256.0, -240.0), + ("float8_e4m3fn", 1.25, 1.375, 1.5), + ("float8_e4m3fn", 0.6875, 0.75, 0.8125), + ("float8_e4m3fn", -448.0, -416.0, -384.0), + ("float8_e4m3fn", -np.inf, -448.0, -416.0), + ("float8_e4m3fn", 416.0, 448.0, np.inf), + ("float8_e4m3fn", -0.001953125, 0.0, 0.001953125), + ("float8_e5m2", 3072.0, 3584.0, 4096.0), + ("float8_e5m2", -2048.0, -1792.0, -1536.0), + ("float8_e5m2", 0.875, 1.0, 1.25), + ("float8_e5m2", -0.625, -0.5, -0.4375), + ("float8_e5m2", -57344.0, -49152.0, -40960.0), + ("float8_e5m2", 40960.0, 49152.0, 57344.0), + ("float8_e5m2", -np.inf, -57344.0, -49152.0), + ("float8_e5m2", 49152.0, 57344.0, np.inf), + ("float8_e5m2", 1.52587890625e-05, 3.0517578125e-05, 4.57763671875e-05), + ("float8_e5m2", -4.57763671875e-05, -3.0517578125e-05, -1.52587890625e-05), + ("float8_e5m2", -1.52587890625e-05, 0.0, 1.52587890625e-05), + ), +) +def test_fp8_helper(format, left, value, right): + helper = fp8helpers[format] + isclose = lambda x, y: np.allclose(x, y, atol=0, rtol=1e-5) + + # Check ranges + expected_range_left, expected_range_right = (value + left) / 2, (value + right) / 2 + range_left, range_right = helper.range(value) + assert isclose(expected_range_left, range_left) + assert isclose(expected_range_right, range_right) + + # Check if ranges work for non-exact match + if np.isfinite(left): + range_left, range_right = helper.range(value * 0.8 + left * 0.2) + assert isclose(expected_range_left, range_left) + assert isclose(expected_range_right, range_right) + if np.isfinite(right): + range_left, range_right = helper.range(value * 0.7 + right * 0.3) + assert isclose(expected_range_left, range_left) + assert isclose(expected_range_right, range_right) + + # Check if absdiff works + scalar_absdiff = lambda x, y: helper.absdiff(np.asarray([x]), np.asarray([y])) + assert isclose(scalar_absdiff(value, value), 0) + + if np.isfinite(right): + assert isclose(scalar_absdiff(value, right), abs(right - range_right)) + assert isclose(scalar_absdiff(value, right + 1), abs(right - range_right) + 1) + assert isclose(scalar_absdiff(value, value * 0.9 + right * 0.1), 0) + assert isclose(scalar_absdiff(value, value * 0.5 + right * 0.5), 0) + if np.isfinite(left): + assert isclose(scalar_absdiff(value, value * 0.9 + left * 0.1), 0) + assert isclose(scalar_absdiff(value, value * 0.5 + left * 0.5), 0) + assert isclose(scalar_absdiff(value, left - 1), abs(left - range_left) + 1) + assert isclose(scalar_absdiff(value, left), abs(left - range_left)) + + if not np.isfinite(right): + assert isclose(scalar_absdiff(value, value + 1234567), 0) + if not np.isfinite(left): + assert isclose(scalar_absdiff(value, value - 7654321), 0) diff --git a/tests/nvmath_tests/linalg/advanced/matmul/test_ifc.py b/tests/nvmath_tests/linalg/advanced/matmul/test_ifc.py index 16e66b3..5f9d02a 100644 --- a/tests/nvmath_tests/linalg/advanced/matmul/test_ifc.py +++ b/tests/nvmath_tests/linalg/advanced/matmul/test_ifc.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -10,11 +10,15 @@ from nvmath.linalg.advanced import Matmul from nvmath.linalg._internal.matmul_desc_ifc import MatmulDescInterface +from nvmath.linalg._internal.matrix_layout_ifc import MatrixLayoutInterface +from nvmath.linalg._internal.matmul_pref_ifc import MatmulPreferenceInterface + +from nvmath._internal import typemaps import pytest try: - import cupy + import cupy # noqa: F401 except ModuleNotFoundError: pytest.skip("cupy required for matmul tests", allow_module_level=True) @@ -23,7 +27,28 @@ def test_matmul_desc_ifc(): """ Test MatmulDescInterface.__getattr__ (not used anywhere yet) """ - mm = Matmul(np.zeros((1, 1)), np.zeros((1, 1))) - desc = MatmulDescInterface(mm.mm_desc) - desc.epilog = 123 - assert desc.epilog == 123 + a = np.zeros((1, 1)) + with Matmul(a, a) as mm: + desc = MatmulDescInterface(mm.mm_desc) + desc.epilogue = 123 + assert desc.epilogue == 123 + +def test_matrix_layout_ifc(): + ''' + Test MatrixLayoutInterface.__getattr__ + ''' + a = np.zeros((1, 1), dtype=np.float32) + with Matmul(a, a) as mm: + algorithms = mm.plan() + layout_a_ifc = MatrixLayoutInterface(mm.a_layout_ptr) + assert typemaps.DATA_TYPE_TO_NAME[layout_a_ifc.type] == "float32" + +def test_matmul_pref_ifc(): + ''' + Test MatmulPreferenceInterface.__getattr__ + ''' + a = np.zeros((1, 1)) + with Matmul(a, a) as mm: + algorithms = mm.plan() + pref_ifc = MatmulPreferenceInterface(mm.preference_ptr) + assert pref_ifc.max_workspace_bytes == mm.memory_limit \ No newline at end of file diff --git a/tests/nvmath_tests/linalg/advanced/matmul/test_input.py b/tests/nvmath_tests/linalg/advanced/matmul/test_input.py index ac58736..083b13f 100644 --- a/tests/nvmath_tests/linalg/advanced/matmul/test_input.py +++ b/tests/nvmath_tests/linalg/advanced/matmul/test_input.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -7,8 +7,11 @@ """ from nvmath.linalg.advanced import matmul, matrix_qualifiers_dtype +import numpy as np import pytest -from .utils import * +from nvmath.bindings import cublasLt as cublaslt + +from .utils import compare_tensors, random_torch_complex, sample_matrix, assert_tensors_equal, to_numpy, get_framework @pytest.mark.parametrize("framework", ("torch", "numpy/cupy")) @@ -140,7 +143,7 @@ def sample_batch(batch_shape): assert_tensors_equal(result, a @ b + c) -@pytest.mark.parametrize("c_desc", (None, "M", "M1", "MN")) +@pytest.mark.parametrize("c_desc", (None, "M1", "MN")) @pytest.mark.parametrize("b_desc", ("K", "KN")) @pytest.mark.parametrize("a_desc", ("K", "MK")) @pytest.mark.parametrize("a_t", (True, False)) @@ -384,7 +387,7 @@ def test_dtype_mismatch(framework, a_dtype, b_dtype, c_dtype): c = sample_matrix(framework, c_dtype, (2, 2), True) except NotImplementedError: pytest.skip("Unable to generate matrix of this dtype") - with pytest.raises(ValueError, match=r"The dtype of operands .* must be the same"): + with pytest.raises(ValueError, match=r"Unsupported combination of dtypes"): matmul(a, b, c, beta=1) @@ -421,6 +424,30 @@ def test_unsupported_type(): """ Tests if a proper error is reported for an unsupported data type. """ - a = b = c = np.asarray(["hello"]) - with pytest.raises(ValueError, match=r"Unsupported dtype."): + a = b = c = np.zeros((2, 2), dtype=np.int64) + with pytest.raises(ValueError, match=r"^The dtype of operand.*not supported"): matmul(a, b, c, beta=1) + + +def test_unsupported_float8(): + """ + Tests if proper error is reported when FP8 is not supported. + """ + try: + import torch + except: + pytest.skip("Torch is required for FP8 support test.") + + if not hasattr(torch, "float8_e4m3fn"): + # Old torch versions don't support float8_e4m3fn at all. + pytest.skip("torch.float8_e4m3fn is required for FP8 support test.") + + a = torch.zeros((16, 16)).type(torch.float8_e4m3fn).cuda() + b = torch.zeros((16, 16)).type(torch.float8_e4m3fn).cuda() + + if cublaslt.get_version() < 120800: + with pytest.raises(ValueError, match=r"FP8 is not supported.*cuBLASLt version 12\.8 or higher is required"): + matmul(a, b, quantization_scales={"a": 1, "b": 1, "d": 1}) + elif (torch.cuda.get_device_properties(0).major, torch.cuda.get_device_properties(0).minor) < (8, 9): + with pytest.raises(cublaslt.cuBLASLtError): + matmul(a, b, quantization_scales={"a": 1, "b": 1, "d": 1}) diff --git a/tests/nvmath_tests/linalg/advanced/matmul/test_matmul_with_hypothesis.py b/tests/nvmath_tests/linalg/advanced/matmul/test_matmul_with_hypothesis.py index 63adf01..0052d5d 100644 --- a/tests/nvmath_tests/linalg/advanced/matmul/test_matmul_with_hypothesis.py +++ b/tests/nvmath_tests/linalg/advanced/matmul/test_matmul_with_hypothesis.py @@ -1,9 +1,13 @@ +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + import collections import logging import re import typing -from hypothesis import given, settings, reproduce_failure, assume +from hypothesis import given, assume from hypothesis.extra.numpy import arrays, from_dtype from hypothesis.strategies import ( one_of, @@ -11,7 +15,6 @@ none, floats, integers, - complex_numbers, sampled_from, fixed_dictionaries, composite, @@ -24,15 +27,21 @@ pytest.skip("cupy is required for matmul tests", allow_module_level=True) import numpy as np -from nvmath._utils import CudaDataType +import nvmath.linalg +from nvmath import CudaDataType from nvmath.bindings.cublasLt import cuBLASLtError, ReductionScheme -from nvmath.linalg._internal.typemaps import NAME_TO_DEFAULT_COMPUTE_TYPE, CUBLAS_COMPUTE_TYPE_TO_NAME +from nvmath.linalg._internal.typemaps import ( + NAMES_TO_DEFAULT_COMPUTE_TYPE, + CUBLAS_COMPUTE_TYPE_TO_NAME, + SCALE_TYPE_TO_DEFAULT_COMPUTE_TYPE, + COMPUTE_TYPE_TO_DEFAULT_SCALE_TYPE, +) from nvmath.linalg.advanced import MatmulEpilog, MatmulNumericalImplFlags, MatmulPlanPreferences, matmul from nvmath.linalg.advanced.matmulmod import EPILOG_INPUT_HANDLERS_MAP, EPILOG_MINIMUM_VERSIONS_MAP from nvmath.memory import _RawCUDAMemoryManager, BaseCUDAMemoryManager, _CupyCUDAMemoryManager from nvmath_tests.helpers import nvmath_seed -from nvmath_tests.linalg.advanced.matmul.utils import get_tolerance +from .utils import get_absolute_tolerance MatmulEpilog_BIAS_list = [ MatmulEpilog.BIAS, @@ -97,7 +106,7 @@ def compare_result(ref, res): ref, equal_nan=True, rtol=(1e-02 if res.dtype == np.float16 else 2e-05), - atol=2 * get_tolerance(ref), + atol=2 * get_absolute_tolerance(ref), ) @@ -122,7 +131,7 @@ def drelu(x, bitmask): def verify_result(a, b, c, result_c, alpha, beta, epilog, epilog_inputs): - possible_dtype = CUBLAS_COMPUTE_TYPE_TO_NAME[NAME_TO_DEFAULT_COMPUTE_TYPE[str(a.dtype)]] + possible_dtype = CUBLAS_COMPUTE_TYPE_TO_NAME[NAMES_TO_DEFAULT_COMPUTE_TYPE[(str(a.dtype), str(b.dtype))]] compute_dtype = possible_dtype[1] if np.iscomplexobj(a) else possible_dtype[0] added_singleton_dimensions: list[int] = [] @@ -219,6 +228,10 @@ def verify_result(a, b, c, result_c, alpha, beta, epilog, epilog_inputs): ) +def notNone(x): + return x is not None + + @composite def matrix_multiply_arrays(draw): m = draw(one_of(none(), problem_size_mnk)) @@ -247,11 +260,14 @@ def matrix_multiply_arrays(draw): none(), arrays( dtype=ab_type, - shape=(m_for_c,) - if n is None - else ( - m_for_c, - draw(sampled_from([1, n])), + shape=tuple( + filter( + notNone, + ( + m_for_c, + draw(sampled_from([1, n])), + ), + ) ), elements=element_properties, ), @@ -276,6 +292,13 @@ def matrix_multiply_arrays(draw): return MatmulInputs(a=a, b=b, c=c, m=m, n=n, k=k, ab_type=ab_type, bias=bias, beta=beta, alpha=alpha, epilogs=epilogs) +@composite +def preference_object_strategy(draw): + limit = draw(integers(min_value=1, max_value=8)) + reduction_scheme_mask = draw(one_of(sampled_from(ReductionScheme))) + return MatmulPlanPreferences(reduction_scheme_mask=reduction_scheme_mask, limit=limit) + + @nvmath_seed() @given( input_arrays=matrix_multiply_arrays(), @@ -286,7 +309,8 @@ def matrix_multiply_arrays(draw): { "blocking": sampled_from(options_blocking_values), "allocator": sampled_from(options_allocator_values), - "scale_type": one_of(none()), + "scale_type": one_of(none(), sampled_from(CudaDataType)), + # "compute_type": one_of(none(), sampled_from(nvmath.linalg.ComputeType)), } ), ), @@ -298,27 +322,28 @@ def matrix_multiply_arrays(draw): "max_waves_count": one_of(floats(min_value=0, max_value=100, width=32)), } ), + preference_object_strategy(), ), ) def test_matmul(input_arrays, order, options, preferences): """Call nvmath.linalg.advanced.matmul() with valid inputs.""" - try: - a, b, c, m, n, k, ab_type, bias, beta, alpha, epilogs = input_arrays - epilog, epilog1 = epilogs - - d_a = cp.asarray(a, order=order) - d_b = cp.asarray(b, order=order) - c_order = "F" if epilog is not None and epilog in [MatmulEpilog.BGRADB, MatmulEpilog.BGRADA] else order - d_c = ( - # FIXME: c must be F ordered when using BGRAD[A,B] - None if c is None else cp.asarray(c, order=c_order) - ) + a, b, c, m, n, k, ab_type, bias, beta, alpha, epilogs = input_arrays + epilog, epilog1 = epilogs + + d_a = cp.asarray(a, order=order) + d_b = cp.asarray(b, order=order) + c_order = "F" if epilog is not None and epilog in [MatmulEpilog.BGRADB, MatmulEpilog.BGRADA] else order + d_c = ( + # FIXME: c must be F ordered when using BGRAD[A,B] + None if c is None else cp.asarray(c, order=c_order) + ) - epilog_inputs = None if epilog is None else {} + epilog_inputs = None if epilog is None else {} - if epilog is not None and epilog in MatmulEpilog_BIAS_list: - epilog_inputs["bias"] = cp.asarray(bias, order=order) + if epilog is not None and epilog in MatmulEpilog_BIAS_list: + epilog_inputs["bias"] = cp.asarray(bias, order=order) + try: result_c = matmul( d_a, d_b, @@ -359,7 +384,17 @@ def test_matmul(input_arrays, order, options, preferences): raise e except ValueError as e: # FIXME: Check for CUDA toolkit version 11 - if re.search("K=1 is not supported for (BGRAD(A|B)|D(R|G)ELU) epilog", str(e)) or "requires cublaslt >=" in str(e): + if ( + re.search("K=1 is not supported for (BGRAD(A|B)|D(R|G)ELU) epilog", str(e)) + or "requires cublaslt >=" in str(e) + or ("`c` must be at least 2-D." in str(e) and c is not None and len(c.shape) < 2) + or ("Unsupported scale type." in str(e) and options["scale_type"] not in SCALE_TYPE_TO_DEFAULT_COMPUTE_TYPE) + or ( + "Unsupported compute type." in str(e) + and options["compute_type"] not in COMPUTE_TYPE_TO_DEFAULT_SCALE_TYPE["real"] + ) + or re.search("Selected scale_type=(.*) compute_type=(.*) are not supported for data types", str(e)) + ): pass else: raise e @@ -431,17 +466,17 @@ def generate_alpha_beta(value_type, value): def test_matmul_negative(a, b, c, alpha_value, beta_value, epilog, epilog_inputs, options, preferences): """Call nvmath.linalg.advanced.matmul() with invalid inputs; catch expected exceptions.""" - try: - if c is not None and ((a.dtype != c.dtype) or (a.shape[0] != c.shape[0]) or (c.shape[1] != b.shape[1])): - return + if c is not None and ((a.dtype != c.dtype) or (a.shape[0] != c.shape[0]) or (c.shape[1] != b.shape[1])): + return - d_a = cp.asarray(a, order="F") - d_b = cp.asarray(b, order="F") - d_c = cp.asarray(c, order="F") if c is not None else None + d_a = cp.asarray(a, order="F") + d_b = cp.asarray(b, order="F") + d_c = cp.asarray(c, order="F") if c is not None else None - alpha = generate_alpha_beta(a.dtype, alpha_value) - beta = generate_alpha_beta(a.dtype, beta_value) + alpha = generate_alpha_beta(a.dtype, alpha_value) + beta = generate_alpha_beta(a.dtype, beta_value) + try: result_c = matmul( d_a, d_b, @@ -466,7 +501,7 @@ def test_matmul_negative(a, b, c, alpha_value, beta_value, epilog, epilog_inputs except ValueError as e: if "A value for beta must be provided if operand C is provided." in str(e): assert (beta is None) and (c is not None) - elif f"The dtype of operands A {a.dtype} and B {b.dtype} must be the same." in str(e): + elif f"Unsupported combination of dtypes for operands A {a.dtype} and B {b.dtype}" in str(e): assert a.dtype != b.dtype elif ( f"The 'K' extent must match for the operands: K={a.shape[1]} in operand A is not equal to K={b.shape[0]} " @@ -479,10 +514,7 @@ def test_matmul_negative(a, b, c, alpha_value, beta_value, epilog, epilog_inputs assert c.shape[0] != a.shape[0] or c.shape[1] != b.shape[1] elif re.search(re.compile(r"The epilog \w+ requires the following input tensors: \{\'\w+\'\}\."), str(e)): assert epilog is not None - if "The provided tensor names are:" in str(e): - assert epilog_inputs is not None - else: - assert epilog_inputs is None + assert epilog_inputs is None or epilog_inputs == {} elif "The value specified for blocking must be either True or 'auto'." in str(e): assert options["blocking"] not in (True, "auto") elif "is not a valid CudaDataType" in str(e): @@ -496,7 +528,19 @@ def test_matmul_negative(a, b, c, alpha_value, beta_value, epilog, epilog_inputs elif "requires cublaslt >=" in str(e): from nvmath.bindings import cublasLt - assert cublasLt.get_version() < EPILOG_MINIMUM_VERSIONS_MAP[epilog]["cublaslt"] + assert cublasLt.get_version() < EPILOG_MINIMUM_VERSIONS_MAP[epilog]["cublaslt"] or ( + a.shape[-2] == 1 and c is not None and c.shape[-1] == 1 and epilog & MatmulEpilog.BIAS > 0 + ) + elif re.search("K=1 is not supported for (BGRAD(A|B)|D(R|G)ELU) epilog", str(e)): + assert a.shape[1] == 1 and b.shape[0] == 1 + assert epilog in [ + MatmulEpilog.BGRADA, + MatmulEpilog.BGRADB, + MatmulEpilog.DGELU_BGRAD, + MatmulEpilog.DGELU, + MatmulEpilog.DRELU_BGRAD, + MatmulEpilog.DRELU, + ] else: raise e except TypeError as e: diff --git a/tests/nvmath_tests/linalg/advanced/matmul/test_mxfp8.py b/tests/nvmath_tests/linalg/advanced/matmul/test_mxfp8.py new file mode 100644 index 0000000..85bdac9 --- /dev/null +++ b/tests/nvmath_tests/linalg/advanced/matmul/test_mxfp8.py @@ -0,0 +1,788 @@ +# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# +# SPDX-License-Identifier: Apache-2.0 + + +try: + import torch +except ImportError: + torch = None +import pytest +from .utils import sample_matrix +from .fp8_utils import assert_fp8_equal +from nvmath.linalg.advanced import Matmul, matmul, MatmulEpilog as Epilog +from nvmath.linalg.advanced.helpers import matmul as matmul_helpers +from nvmath._internal.typemaps import NAME_TO_DATA_TYPE +from nvmath.bindings import cublasLt as cublaslt +from nvmath._internal.utils import check_or_create_options +from nvmath.linalg.advanced import _configuration +from contextlib import nullcontext +from .utils import allow_cublas_unsupported + +if torch is None: + pytest.skip("Torch is required for MXFP8 tests", allow_module_level=True) + +if torch.cuda.get_device_properties(0).major < 10: + pytest.skip("CC>=10.0 is required for MXFP8 tests", allow_module_level=True) + +if cublaslt.get_version() < 120800: + pytest.skip("cuBLAS 12.8 is required for MXFP8 tests", allow_module_level=True) + + +def unpack_matmul(result): + """ + Helper function which unpacks the result of `matmul` into D, d_out (or None), epilog aux + """ + if isinstance(result, tuple): + d, aux = result + d_out = aux.pop("d_out_scale", None) + return d, d_out, aux + else: + return result, None, {} + + +def expected_result_type(atype, btype, ctype, dtype): + """ + Result type of FP8 matmul. ctype=None means no C. dtype=None means no explicit + specification of the result type. + """ + return dtype or ctype or atype + + +def generate_mxfp8_scales(x, scale_range, *, use_cuda, validate_shape=True): + """ + Generates UE8M0 scales for x, randomly chosen from [2^scale_range[0], 2^scale_range[1]] + """ + low = scale_range[0] + 127 + high = scale_range[1] + 127 + assert low >= 0 + assert high <= 255 + if validate_shape: + assert all(s % 128 == 0 for s in x.shape[-2:]) + num_scales = x.nelement() // 32 + s = torch.randint(low=low, high=high + 1, size=(num_scales,)).to(torch.uint8) + if use_cuda: + s = s.cuda() + return s + + +def expand_mxfp8_scales(x, scales): + """ + Expands block UE8M0 scales tensor for `x` into a float32 tensor with actual scale + factors. + """ + idx = matmul_helpers.get_mxfp8_scale_offset(x, torch.meshgrid(*(torch.arange(d) for d in x.shape), indexing="ij")) + if scales.is_cuda: + idx = idx.cuda() + return 2 ** (scales.type(torch.float32)[idx] - 127) + + +def generate_simple_inputs(m, n, k, atype, btype, ctype, *, c_transposed=False, use_cuda): + """ + Generates matmul inputs of given shapes and types. + """ + + def random_choice(choices, shape=()): + choices = torch.as_tensor(choices) + idx = torch.randint(low=0, high=len(choices), size=shape) + return choices[idx] + + def random_sign(): + return random_choice([-1, 1]).item() + + # Use non-symmetric distributions to reduce the risk of catastrophic cancellations + a = random_choice([-1, 0, 0.5, 1, 1.5], shape=(m, k)).type(getattr(torch, atype)) + b = random_choice([-1, 0, 0.5, 1, 1.5], shape=(n, k)).type(getattr(torch, btype)).T + + alpha = random_sign() * 2 ** torch.randint(low=-10, high=-8, size=()).item() + + if ctype is not None: + if c_transposed: + c = random_choice([-0.25, 0, 0.5, 1], shape=(n, m)).type(getattr(torch, ctype)).T + else: + c = random_choice([-0.25, 0, 0.5, 1], shape=(m, n)).type(getattr(torch, ctype)) + beta = random_sign() * alpha * torch.rand(size=()).item() + else: + c = None + beta = None + if use_cuda: + a = a.cuda() + b = b.cuda() + if c is not None: + c = c.cuda() + return a, b, c, alpha, beta + + +def mxfp8_matmul_reference( + a, b, c=None, *args, d_out=None, quantization_scales=None, epilog_inputs=None, options=None, **kwargs +): + """ + Computes MXFP8-like matmul, but with higher precision. + """ + scales = check_or_create_options(_configuration.MatmulQuantizationScales, quantization_scales, "Matmul scales") + options = check_or_create_options(_configuration.MatmulOptions, options, "Matmul options") + options.result_type = None + + a_scale = expand_mxfp8_scales(a, scales.a) + b_scale = expand_mxfp8_scales(b, scales.b) + ascaled = a.type(torch.float32) * a_scale + bscaled = b.type(torch.float32) * b_scale + + for key in ("bias", "gelu_aux"): + if epilog_inputs and key in epilog_inputs: + epilog_inputs[key] = epilog_inputs[key].type(torch.float32) + + d = matmul( + ascaled, + bscaled, + c.type(torch.float32) if c is not None else None, + *args, + quantization_scales=None, + epilog_inputs=epilog_inputs, + options=options, + **kwargs, + ) + if d_out is not None: + d_scale = expand_mxfp8_scales(d, d_out) + d /= d_scale + return d + + +SUPPORTED_TYPE_COMBINATIONS = ( + ("float8_e4m3fn", "float8_e4m3fn", None, None), + ("float8_e4m3fn", "float8_e5m2", None, None), + ("float8_e5m2", "float8_e4m3fn", None, None), + ("float8_e4m3fn", "float8_e4m3fn", "float32", "float32"), + ("float8_e4m3fn", "float8_e5m2", "float32", "float32"), + ("float8_e5m2", "float8_e4m3fn", "float32", "float32"), + ("float8_e4m3fn", "float8_e4m3fn", "float32", None), + ("float8_e4m3fn", "float8_e5m2", "float32", None), + ("float8_e5m2", "float8_e4m3fn", "float32", None), + ("float8_e4m3fn", "float8_e4m3fn", "float16", "float16"), + ("float8_e4m3fn", "float8_e5m2", "float16", "float16"), + ("float8_e5m2", "float8_e4m3fn", "float16", "float16"), + ("float8_e4m3fn", "float8_e4m3fn", "float16", None), + ("float8_e4m3fn", "float8_e5m2", "float16", None), + ("float8_e5m2", "float8_e4m3fn", "float16", None), + ("float8_e4m3fn", "float8_e4m3fn", "bfloat16", "bfloat16"), + ("float8_e4m3fn", "float8_e5m2", "bfloat16", "bfloat16"), + ("float8_e5m2", "float8_e4m3fn", "bfloat16", "bfloat16"), + ("float8_e4m3fn", "float8_e4m3fn", "bfloat16", None), + ("float8_e4m3fn", "float8_e5m2", "bfloat16", None), + ("float8_e5m2", "float8_e4m3fn", "bfloat16", None), + ("float8_e4m3fn", "float8_e4m3fn", "float16", "float8_e4m3fn"), + ("float8_e4m3fn", "float8_e5m2", "float16", "float8_e4m3fn"), + ("float8_e5m2", "float8_e4m3fn", "float16", "float8_e4m3fn"), + ("float8_e4m3fn", "float8_e4m3fn", "bfloat16", "float8_e4m3fn"), + ("float8_e4m3fn", "float8_e5m2", "bfloat16", "float8_e4m3fn"), + ("float8_e5m2", "float8_e4m3fn", "bfloat16", "float8_e4m3fn"), +) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize( + "m,n,k", + ( + (128, 128, 128), + (2 * 128, 4 * 128, 2 * 128), + (7 * 128, 5 * 128, 3 * 128), + ), +) +@pytest.mark.parametrize("a_scale_range", ((-5, 5),)) +@pytest.mark.parametrize("b_scale_range", ((-5, 5),)) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +def test_mxfp8(m, n, k, atype, btype, ctype, dtype, a_scale_range, b_scale_range, use_cuda): + """ + Basic MXFP8 test. + """ + a, b, c, alpha, beta = generate_simple_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + + result_type = expected_result_type(atype, btype, ctype, dtype) + ascales = generate_mxfp8_scales(a, a_scale_range, use_cuda=use_cuda) + bscales = generate_mxfp8_scales(b, b_scale_range, use_cuda=use_cuda) + + scales = {"a": ascales, "b": bscales} + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None, "block_scaling": True} + result, d_out, _ = unpack_matmul(matmul(a, b, c=c, alpha=alpha, beta=beta, quantization_scales=scales, options=options)) + + reference = mxfp8_matmul_reference( + a, b, d_out=d_out, c=c, alpha=alpha, beta=beta, quantization_scales=scales, options=options + ) + assert_fp8_equal(result, reference) + assert str(result.dtype).split(".")[-1] == result_type + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize("a_scale_range", ((-5, 5),)) +@pytest.mark.parametrize("b_scale_range", ((-5, 5),)) +@pytest.mark.parametrize( + "a_batch,b_batch,c_batch,d_batch", + ( + ((), (), (), ()), + ((3,), (3,), (3,), (3,)), + ((8,), (8,), (8,), (8,)), + ((2, 3), (2, 3), (2, 3), (2, 3)), + ), +) +@pytest.mark.parametrize( + "m,n,k", + ((128, 128, 128),), +) +@pytest.mark.parametrize(("use_cuda"), (True,)) +def test_batching( + m, n, k, atype, btype, ctype, dtype, a_scale_range, b_scale_range, a_batch, b_batch, c_batch, d_batch, use_cuda +): + """ + Tests if batching works with MXFP8. + """ + + def sample_batch(batch_shape, matrix_shape, type, transposed=False): + shape = (*batch_shape, *matrix_shape) + if transposed: + shape = (*shape[:-2], shape[-1], shape[-2]) + x = sample_matrix("torch", type, shape, use_cuda=use_cuda, min=0, max=2) + return x.swapaxes(-1, -2) if transposed else x + + a = sample_batch(a_batch, (m, k), atype, transposed=False) + b = sample_batch(b_batch, (k, n), btype, transposed=True) + + ascales = generate_mxfp8_scales(a, a_scale_range, use_cuda=use_cuda) + bscales = generate_mxfp8_scales(b, b_scale_range, use_cuda=use_cuda) + + if ctype is not None: + c = sample_batch(c_batch, (m, n), ctype, transposed=False) + beta = 0.12 + else: + c = None + beta = None + + alpha = 0.32 + + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None, "block_scaling": True} + scales = {"a": ascales, "b": bscales} + result, d_out, _ = unpack_matmul(matmul(a, b, c, alpha=alpha, beta=beta, quantization_scales=scales, options=options)) + reference = mxfp8_matmul_reference( + a, b, c=c, alpha=alpha, d_out=d_out, beta=beta, quantization_scales=scales, options=options + ) + expected_result_shape = (*d_batch, m, n) + assert result.shape == expected_result_shape + + reference = mxfp8_matmul_reference( + a, b, c, d_out=d_out, alpha=alpha, beta=beta, quantization_scales=scales, options=options + ) + assert_fp8_equal(result, reference) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize( + "m,n,k", + ((2 * 128, 4 * 128, 3 * 128),), +) +@pytest.mark.parametrize("a_scale_range", ((-3, 3),)) +@pytest.mark.parametrize("b_scale_range", ((-3, 3),)) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +def test_reset(m, n, k, atype, btype, ctype, dtype, a_scale_range, b_scale_range, use_cuda): + """ + Tests if in-place change of A/B scales and reset_operands works. + """ + a, b, c, alpha, beta = generate_simple_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + + ascales = generate_mxfp8_scales(a, a_scale_range, use_cuda=use_cuda) + bscales = generate_mxfp8_scales(b, b_scale_range, use_cuda=use_cuda) + + scales = {"a": ascales, "b": bscales} + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None, "block_scaling": True} + + with Matmul(a, b, c=c, alpha=alpha, beta=beta, quantization_scales=scales, options=options) as mm: + mm.plan() + + # Check initial result + result, d_out, _ = unpack_matmul(mm.execute()) + reference = mxfp8_matmul_reference( + a, b, c=c, alpha=alpha, d_out=d_out, beta=beta, quantization_scales=scales, options=options + ) + assert_fp8_equal(result, reference) + + # Change A and B scales in place + if use_cuda: + ascales[: len(ascales) // 2] *= -1 + bscales[len(bscales) // 2 :] = bscales[0] + result = mm.execute() + result, d_out, _ = unpack_matmul(mm.execute()) + reference = mxfp8_matmul_reference( + a, b, c=c, alpha=alpha, d_out=d_out, beta=beta, quantization_scales=scales, options=options + ) + assert_fp8_equal(result, reference) + + # Reset A scale, keep B scale + ascales2 = generate_mxfp8_scales(a, a_scale_range, use_cuda=use_cuda) + scales2 = scales.copy() + scales2["a"] = ascales2 + mm.reset_operands(a=a, b=b, quantization_scales={"a": ascales2}) + result, d_out, _ = unpack_matmul(mm.execute()) + reference = mxfp8_matmul_reference( + a, b, c=c, alpha=alpha, d_out=d_out, beta=beta, quantization_scales=scales2, options=options + ) + assert_fp8_equal(result, reference) + + # Reset A scale and B scale + ascales3 = generate_mxfp8_scales(a, a_scale_range, use_cuda=use_cuda) + bscales3 = generate_mxfp8_scales(b, b_scale_range, use_cuda=use_cuda) + scales3 = scales2.copy() + scales3["a"] = ascales3 + scales3["b"] = bscales3 + mm.reset_operands(a=a, b=b, quantization_scales={"a": ascales3, "b": bscales3}) + result, d_out, _ = unpack_matmul(mm.execute()) + reference = mxfp8_matmul_reference( + a, b, c=c, alpha=alpha, d_out=d_out, beta=beta, quantization_scales=scales3, options=options + ) + assert_fp8_equal(result, reference) + + +def unpack_bitmask(bitmask, shape): + """ + Utility function unpacking ReLU aux bitmask. + """ + if len(bitmask.shape) > 2: + return torch.stack([unpack_bitmask(bitmask[i], shape) for i in range(bitmask.shape[0])]) + result = torch.zeros(shape) + n, m = shape + for i in range(n): + for j in range(m): + result[i][j] = bool(int(bitmask[i // 8][j].item()) & (1 << i % 8)) + return result + + +@pytest.mark.parametrize( + "m,n,k", + ( + (128, 128, 128), + (4 * 128, 3 * 128, 2 * 128), + ), +) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize( + "epilog_name,order", + ( + ("RELU", "col"), + ("RELU", "row"), + ("GELU", "col"), + ("BGRADA", "col"), + ("BGRADB", "col"), + ("DRELU", "col"), + ("DRELU_BGRAD", "col"), + ("DGELU", "col"), + ("DGELU_BGRAD", "col"), + ("RELU_BIAS", "col"), + ("BIAS", "col"), + ("GELU_BIAS", "col"), + ("RELU_AUX", "col"), + ("RELU_AUX_BIAS", "col"), + ("GELU_AUX", "col"), + ("GELU_AUX_BIAS", "col"), + ), +) +@pytest.mark.parametrize( + "a_batch,b_batch,c_batch,d_batch", + ( + ((), (), (), ()), + ((2,), (2,), (2,), (2,)), + ((2, 3), (2, 3), (2, 3), (2, 3)), + ), +) +@pytest.mark.parametrize("a_scale_range", ((-2, 2),)) +@pytest.mark.parametrize("b_scale_range", ((-3, 3),)) +def test_epilogs( + m, + n, + k, + atype, + btype, + ctype, + dtype, + a_scale_range, + b_scale_range, + epilog_name, + a_batch, + b_batch, + c_batch, + d_batch, + order, + use_cuda, +): + """ + Tests epilogs with MXFP8. + """ + epilog = getattr(Epilog, epilog_name) + + result_type = expected_result_type(atype, btype, ctype, dtype) + inferred_ctype = ctype or ("float16" if "float8" in result_type else result_type) + + # Currently, those are not supported by cuBLAS, so we allow them to fail with + # "NOT_SUPPORTED". + allow_not_supported = False + allow_not_supported |= "BGRAD" in epilog_name + allow_not_supported |= "DRELU" in epilog_name + allow_not_supported |= "DGELU" in epilog_name + allow_not_supported |= "AUX" in epilog_name + + def sample_batch(batch_shape, matrix_shape, type, transposed=False): + shape = (*batch_shape, *matrix_shape) + if transposed: + shape = (*shape[:-2], shape[-1], shape[-2]) + x = sample_matrix("torch", type, shape, use_cuda=use_cuda, min=-0.2, max=1) + return x.swapaxes(-1, -2) if transposed else x + + a = sample_batch(a_batch, (m, k), atype, transposed=False) + b = sample_batch(b_batch, (k, n), btype, transposed=True) + alpha, beta = 0.12, None + if ctype is not None: + c = sample_batch(c_batch, (m, n), ctype, transposed=(order == "col")) + beta = 0.34 + else: + c = None + beta = None + + ascales = generate_mxfp8_scales(a, a_scale_range, use_cuda=use_cuda) + bscales = generate_mxfp8_scales(b, b_scale_range, use_cuda=use_cuda) + + inputs = {} + if "BIAS" in epilog_name: + bias_type = "float16" if inferred_ctype == "float16" else "bfloat16" + bias = sample_matrix("torch", bias_type, (m,), use_cuda=use_cuda, min=0, max=1) + inputs["bias"] = bias + if "DRELU" in epilog_name: + round_16 = lambda x: (x + 15) // 16 * 16 + inputs["relu_aux"] = torch.randint(low=0, high=256, size=(n, round_16(m // 8))).type(torch.uint8).T + if "DGELU" in epilog_name: + if order == "col": + inputs["gelu_aux"] = sample_matrix("torch", result_type, (n, m), use_cuda=use_cuda, min=-5, max=5).T + else: + inputs["gelu_aux"] = sample_matrix("torch", result_type, (m, n), use_cuda=use_cuda, min=-5, max=5) + + scales = {"a": ascales, "b": bscales} + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None, "block_scaling": True} + + # Run matmul. Allow cuBLAS NOT_SUPPORTED error for certain configurations (see above) + with ( + nullcontext() + if not allow_not_supported + else allow_cublas_unsupported( + message=f"MXFP8 epilog not supported by cuBLAS: {epilog_name} for A:{atype} B:{btype} C:{ctype} D:{dtype}", + allow_invalid_value=True, + ) + ): + result, d_out, aux = unpack_matmul( + matmul( + a, + b, + c, + alpha=alpha, + beta=beta, + epilog=epilog, + quantization_scales=scales, + options=options, + epilog_inputs=inputs, + ) + ) + + # Compute the reference and compare + reference = mxfp8_matmul_reference( + a, + b, + c, + d_out=d_out, + alpha=alpha, + beta=beta, + epilog=epilog, + quantization_scales=scales, + options=options, + epilog_inputs=inputs, + ) + + if isinstance(reference, tuple): + reference, reference_aux = reference + else: + reference_aux = {} + + if "GELU" in epilog_name and result_type not in ("float16", "float32"): + assert_fp8_equal(result, reference, atol=1e-1, rtol=1e-1) + else: + assert_fp8_equal(result, reference) + + # Compare auxiliary outputs + assert aux.keys() == reference_aux.keys() + for key in aux: + if key == "relu_aux": + x = unpack_bitmask(aux[key], (m, n)) + y = unpack_bitmask(reference_aux[key], (m, n)) + assert torch.mean((x == y).type(torch.float32)) > 0.99 + elif key == "gelu_aux": + assert_fp8_equal(aux[key], reference_aux[key]) + elif key == "drelu_bgrad" or key == "dgelu_bgrad": + assert_fp8_equal(aux[key], reference.sum(axis=1), atol=1e-1, rtol=1e-1) + else: + raise RuntimeError(f"Test for {key} not implemented") + + +def test_helpers(): + """ + Tests MXFP8 helpers. + """ + x = torch.ones((1024, 3 * 1024), dtype=torch.float8_e4m3fn) + scales = matmul_helpers.create_mxfp8_scale(x, 3) + y = matmul_helpers.apply_mxfp8_scale(x, scales) + assert_fp8_equal(y, x.type(torch.float32) * 8) + z = matmul_helpers.apply_mxfp8_scale(y, matmul_helpers.invert_mxfp8_scale(scales)) + assert_fp8_equal(z, x.type(torch.float32)) + + +@pytest.mark.parametrize("M,N", ((1024, 3 * 1024), (128, 128), (5 * 1024, 256))) +@pytest.mark.parametrize("nsamples", (1, 7, 100)) +@pytest.mark.parametrize("input_format", ("vectors", "ints")) +def test_indexing_helpers(M, N, nsamples, input_format): + """ + Tests indexing helpers. + """ + tensor = torch.zeros((M, N), dtype=torch.float8_e4m3fn) + xs, ys = (torch.randint(size=(nsamples,), low=0, high=d, dtype=torch.int32) for d in (M, N)) + + full = matmul_helpers.get_mxfp8_scale_offset(tensor, torch.meshgrid(*(torch.arange(d) for d in (M, N)), indexing="ij")) + reference = full[xs, ys] + + if input_format == "vectors": + result = matmul_helpers.get_mxfp8_scale_offset(tensor, (xs, ys)) + assert torch.all(result == reference) + elif input_format == "ints": + result = [matmul_helpers.get_mxfp8_scale_offset(tensor, (x, y)) for x, y in zip(xs, ys, strict=True)] + assert all(res == ref for res, ref in zip(result, reference, strict=True)) + else: + raise RuntimeError + + +@pytest.mark.parametrize("order", ("t", "b", "tb", "bt", "tbt", "btb")) +def test_mxfp8_and_fp8(order): + """ + Test if MXFP8 and FP8 work together + """ + m, n, k = 256, 256, 256 + + a = torch.zeros(m, k, device="cuda", dtype=torch.float8_e4m3fn) + b = torch.zeros(n, k, device="cuda", dtype=torch.float8_e4m3fn).T + + for kind in order: + if kind == "t": + # Tensor-wide scaling + matmul(a, b, quantization_scales={"a": 1, "b": 1, "d": 1}) + else: + # Block scaling + matmul( + a, + b, + options={"block_scaling": True}, + quantization_scales={ + "a": matmul_helpers.create_mxfp8_scale(a, 0), + "b": matmul_helpers.create_mxfp8_scale(b, 0), + }, + ) + + +@pytest.mark.parametrize( + "m,n,k", + ((128, 128, 128),), +) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +def test_validation_d_scale(m, n, k, atype, btype, ctype, dtype, use_cuda): + """ + Test if an error is raised if D scale is provided. + """ + a, b, c, alpha, beta = generate_simple_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + + ascales = generate_mxfp8_scales(a, (-10, 10), use_cuda=use_cuda) + bscales = generate_mxfp8_scales(b, (-10, 10), use_cuda=use_cuda) + scales = {"a": ascales, "b": bscales, "d": torch.zeros(m, n).type(torch.uint8).cuda()} + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None, "block_scaling": True} + with pytest.raises(ValueError, match="Quantization scaling is not supported for D when `block_scaling` option is enabled."): + matmul(a, b, c=c, alpha=alpha, beta=beta, quantization_scales=scales, options=options) + + +@pytest.mark.parametrize( + "m,n,k", + ((128, 128, 128),), +) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize("scales_provided", ("", "a", "b")) +def test_validation_all_scales_required(m, n, k, atype, btype, ctype, dtype, scales_provided, use_cuda): + """ + Test if an error is raised if not all scales are provided. + """ + a, b, c, alpha, beta = generate_simple_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + + result_type = expected_result_type(atype, btype, ctype, dtype) + ascales = generate_mxfp8_scales(a, (-10, 10), use_cuda=use_cuda) + bscales = generate_mxfp8_scales(b, (-10, 10), use_cuda=use_cuda) + scales = {} + if "a" in scales_provided: + scales["a"] = ascales + if "b" in scales_provided: + scales["b"] = bscales + + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None, "block_scaling": True} + + ok = "a" in scales_provided and "b" in scales_provided and ("d" in scales_provided or "float8" not in result_type) + + if not ok: + with pytest.raises(ValueError, match=r"Scale for . is not specified"): + matmul(a, b, c=c, alpha=alpha, beta=beta, quantization_scales=scales, options=options) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize("a_scale_range", ((-5, 5),)) +@pytest.mark.parametrize("b_scale_range", ((-5, 5),)) +@pytest.mark.parametrize( + "a_batch,b_batch", + ( + ((3,), ()), + ((), (3,)), + ((2,), (3,)), + ((2, 2), (1, 4)), + ), +) +@pytest.mark.parametrize( + "m,n,k", + ((128, 128, 128),), +) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +def test_validation_ab_batches_different( + m, n, k, atype, btype, ctype, dtype, a_scale_range, b_scale_range, a_batch, b_batch, use_cuda +): + """ + Tests if MXFP8 raises an error when batch sizes are different. + """ + + def sample_batch(batch_shape, matrix_shape, type, transposed=False): + shape = (*batch_shape, *matrix_shape) + if transposed: + shape = (*shape[:-2], shape[-1], shape[-2]) + x = sample_matrix("torch", type, shape, use_cuda=use_cuda, min=0, max=2) + return x.swapaxes(-1, -2) if transposed else x + + a = sample_batch(a_batch, (m, k), atype, transposed=False) + b = sample_batch(b_batch, (k, n), btype, transposed=True) + + ascales = generate_mxfp8_scales(a, a_scale_range, use_cuda=use_cuda) + bscales = generate_mxfp8_scales(b, b_scale_range, use_cuda=use_cuda) + + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None, "block_scaling": True} + scales = {"a": ascales, "b": bscales} + with pytest.raises( + ValueError, + match=r"When block_scaling=True, the batch dimensions of A and B must match \(broadcasting is not supported\).", + ): + matmul(a, b, quantization_scales=scales, options=options) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize( + "m,n,k", + ( + (128 + 1, 128, 128), + (128, 128 + 1, 128), + (128, 128, 128 + 1), + (128 + 64, 128, 128), + (128, 128 + 64, 128), + (128, 128, 128 + 64), + ), +) +@pytest.mark.parametrize("a_scale_range", ((-5, 5),)) +@pytest.mark.parametrize("b_scale_range", ((-5, 5),)) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +def test_validation_shapes(m, n, k, atype, btype, ctype, dtype, a_scale_range, b_scale_range, use_cuda): + """ + Tests if an error is raised when M, N, K are not divisible by 128. + """ + a, b, c, alpha, beta = generate_simple_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + + ascales = generate_mxfp8_scales(a, a_scale_range, use_cuda=use_cuda, validate_shape=False) + bscales = generate_mxfp8_scales(b, b_scale_range, use_cuda=use_cuda, validate_shape=False) + + scales = {"a": ascales, "b": bscales} + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None, "block_scaling": True} + with pytest.raises(ValueError, match=f"M={m} N={n} K={k} must be divisible by 128 when block_scaling=True"): + matmul(a, b, c=c, alpha=alpha, beta=beta, quantization_scales=scales, options=options) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS) +@pytest.mark.parametrize( + "m,n,k", + ( + (128, 128, 128), + (128, 256, 512), + (512, 512, 128), + ), +) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +@pytest.mark.parametrize( + "a_err,b_err", + ( + ( + (1, 0), + (0, 1), + (1, 1), + (32, 32), + (-1, -1), + (-1, 0), + (0, -1), + ) + ), +) +def test_validation_scales_shapes(m, n, k, atype, btype, ctype, dtype, a_err, b_err, use_cuda): + """ + Tests if an error is scale shapes don't match input shapes. + """ + a, b, c, alpha, beta = generate_simple_inputs(m, n, k, atype, btype, ctype, use_cuda=use_cuda) + + ascales = torch.zeros(size=(a.nelement() // 32 + a_err,), dtype=torch.uint8) + bscales = torch.zeros(size=(a.nelement() // 32 + b_err,), dtype=torch.uint8) + scales = {"a": ascales, "b": bscales} + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None, "block_scaling": True} + + with pytest.raises(ValueError, match=r"Scales for (A|B) should have shape .* Got .*"): + matmul(a, b, c=c, alpha=alpha, beta=beta, quantization_scales=scales, options=options) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS[:3]) +@pytest.mark.parametrize(("use_cuda"), (True, False)) +def test_validation_scalar_scales(atype, btype, ctype, dtype, use_cuda): + """ + Tests if scalar scales are disallowed when block_scaling=True. + """ + a, b, c, alpha, beta = generate_simple_inputs(128, 128, 128, atype, btype, ctype, use_cuda=use_cuda) + scales = {"a": 1, "b": 1} + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None, "block_scaling": True} + with pytest.raises(ValueError, match="A scalar tensor-wide scale factor is not allowed when block_scaling=True."): + matmul(a, b, c=c, alpha=alpha, beta=beta, quantization_scales=scales, options=options) + + +@pytest.mark.parametrize("atype,btype,ctype,dtype", SUPPORTED_TYPE_COMBINATIONS[:3]) +@pytest.mark.parametrize( + "m,n,k", + ((128, 128, 128),), +) +@pytest.mark.parametrize(("scale_dtype"), (torch.int8, torch.float32)) +def test_validation_scales_dtype(m, n, k, atype, btype, ctype, dtype, scale_dtype): + """ + Tests if scales of invalid type are rejected. + """ + a, b, c, alpha, beta = generate_simple_inputs(m, n, k, atype, btype, ctype, use_cuda=False) + + ascales = torch.zeros(size=(a.nelement() // 32,), dtype=scale_dtype) + bscales = torch.zeros(size=(a.nelement() // 32,), dtype=scale_dtype) + scales = {"a": ascales, "b": bscales} + options = {"result_type": NAME_TO_DATA_TYPE[dtype] if dtype else None, "block_scaling": True} + + with pytest.raises(ValueError, match="Block scales for (A|B) should be uint8 tensor"): + matmul(a, b, c=c, alpha=alpha, beta=beta, quantization_scales=scales, options=options) diff --git a/tests/nvmath_tests/linalg/advanced/matmul/test_options.py b/tests/nvmath_tests/linalg/advanced/matmul/test_options.py index 1411949..ef44e32 100644 --- a/tests/nvmath_tests/linalg/advanced/matmul/test_options.py +++ b/tests/nvmath_tests/linalg/advanced/matmul/test_options.py @@ -1,13 +1,14 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 -import nvmath from nvmath.bindings import cublas from nvmath.linalg.advanced import matmul, Matmul, MatmulOptions -from .utils import * -import pytest import logging +import nvmath +import pytest + +from .utils import assert_tensors_equal, sample_matrix, is_torch_available try: import cupy_backends.cuda @@ -25,12 +26,12 @@ """ -def check_matmul_with_options(size, options, use_cuda=False, dtype="float32", atol=None): +def check_matmul_with_options(size, options, use_cuda=False, dtype="float32", atol=None, rtol=None): a = b = sample_matrix("numpy/cupy" if dtype != "bfloat16" else "torch", dtype, (size, size), use_cuda) is_complex = "_C_" in str(options.scale_type) or (options.compute_type is None and "complex" in dtype) alpha = 0.42 + 0.24j if is_complex else 0.42 result = matmul(a, b, alpha=alpha, options=options) - assert_tensors_equal(result, alpha * (a @ b), atol=atol) + assert_tensors_equal(result, alpha * (a @ b), atol=atol, rtol=rtol) return result @@ -102,6 +103,7 @@ def test_compute_scale_type(dtype, compute_type, scale_type): dtype=dtype, use_cuda=True, atol=0.1, + rtol=None, ) diff --git a/tests/nvmath_tests/linalg/advanced/matmul/test_perf.py b/tests/nvmath_tests/linalg/advanced/matmul/test_perf.py index 50518b1..b2a763c 100644 --- a/tests/nvmath_tests/linalg/advanced/matmul/test_perf.py +++ b/tests/nvmath_tests/linalg/advanced/matmul/test_perf.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 diff --git a/tests/nvmath_tests/linalg/advanced/matmul/test_planning.py b/tests/nvmath_tests/linalg/advanced/matmul/test_planning.py index e0c07a3..9c06d59 100644 --- a/tests/nvmath_tests/linalg/advanced/matmul/test_planning.py +++ b/tests/nvmath_tests/linalg/advanced/matmul/test_planning.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -6,12 +6,17 @@ This set of tests checks basic properties of separated planning. """ -import re -import nvmath -from nvmath.linalg.advanced import Matmul, MatmulPlanPreferences from nvmath.bindings import cublasLt as cublaslt +from nvmath.linalg.advanced import Matmul, MatmulPlanPreferences +import numpy as np import pytest -from .utils import * + +from .utils import sample_matrix, allow_cublas_unsupported, assert_tensors_equal + +try: + import cupy +except ModuleNotFoundError: + pytest.skip("cupy required for matmul tests", allow_module_level=True) @pytest.mark.parametrize("framework", ("numpy/cupy", "torch")) @@ -176,3 +181,74 @@ def test_algorithm_not_planned(framework, use_cuda): match=r"Algorithm passed to execute\(\) has to be included in the plan\(\) algorithms", ): mm2.execute(algorithm=algos[0]) + +def test_algorithm_ids(): + a = cupy.zeros((10, 10)) + b = cupy.zeros((10, 10)) + with Matmul(a, b) as mm: + assert len(mm.applicable_algorithm_ids(limit=4)) <= 4 + +def test_algo_attributes(): + ''' + Test Algorithm class setter/property + ''' + m, n, k = 24, 24, 24 + a = cupy.random.rand(m, k) + b = cupy.random.rand(k, n) + + with Matmul(a, b) as mm: + algos = mm.plan() + best = algos[0] + + # An attribute may not be supported in all cuBLASLt versions (INVALID_VALUE). + + message = "The attribute '{attr}' is not supported in this version." + with allow_cublas_unsupported( + allow_invalid_value=True, + message=message.format(attr='stages') + ): + if best.capabilities.stages_ids: + best.stages = best.capabilities.stages_ids[-1] + assert best.stages == best.capabilities.stages_ids[-1] + + with allow_cublas_unsupported( + allow_invalid_value=True, + message=message.format(attr='split_k') + ): + best.split_k = 4 + assert best.split_k == 4 + + with allow_cublas_unsupported( + allow_invalid_value=True, + message=message.format(attr='reduction_scheme') + ): + best.reduction_scheme = best.capabilities.reduction_scheme_mask + assert best.reduction_scheme == best.capabilities.reduction_scheme_mask + + with allow_cublas_unsupported( + allow_invalid_value=True, + message=message.format(attr='cta_swizzling') + ): + best.cta_swizzling = True + assert best.cta_swizzling == True + + with allow_cublas_unsupported( + allow_invalid_value=True, + message=message.format(attr='custom_option') + ): + best.custom_option = 1 + assert best.custom_option == 1 + + with allow_cublas_unsupported( + allow_invalid_value=True, + message=message.format(attr='inner_shape') + ): + best.inner_shape = cublaslt.MatmulInnerShape.MMA884 + assert best.inner_shape == cublaslt.MatmulInnerShape.MMA884 + + with allow_cublas_unsupported( + allow_invalid_value=True, + message=message.format(attr='cluster_shape') + ): + best.cluster_shape = (1,1,1) + assert best.cluster_shape == (1,1,1) diff --git a/tests/nvmath_tests/linalg/advanced/matmul/test_reset.py b/tests/nvmath_tests/linalg/advanced/matmul/test_reset.py index edd479a..c1645a7 100644 --- a/tests/nvmath_tests/linalg/advanced/matmul/test_reset.py +++ b/tests/nvmath_tests/linalg/advanced/matmul/test_reset.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -8,7 +8,8 @@ import nvmath import pytest -from .utils import * + +from .utils import assert_tensors_equal, random_torch_complex, sample_matrix, skip_if_cublas_before @pytest.mark.parametrize("framework", ("numpy/cupy", "torch")) diff --git a/tests/nvmath_tests/linalg/advanced/matmul/utils.py b/tests/nvmath_tests/linalg/advanced/matmul/utils.py index ca7db5e..1ee0374 100644 --- a/tests/nvmath_tests/linalg/advanced/matmul/utils.py +++ b/tests/nvmath_tests/linalg/advanced/matmul/utils.py @@ -1,4 +1,4 @@ -# Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. +# Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED. # # SPDX-License-Identifier: Apache-2.0 @@ -17,10 +17,11 @@ import numpy as np import nvmath +import datetime import re -def sample_matrix(framework, dtype, shape, use_cuda): +def sample_matrix(framework, dtype, shape, use_cuda, min=-5, max=5): """ Generates a sample matrix with random contents. """ @@ -32,7 +33,7 @@ def sample_matrix(framework, dtype, shape, use_cuda): if torch is None: pytest.skip("pytorch not present") dtype = getattr(torch, dtype) - r = (10 * torch.rand(shape) - 5).type(dtype) + r = ((max - min) * torch.rand(shape) + min).type(dtype) return r.cuda() if use_cuda else r elif framework == "cupy": if not use_cuda: @@ -83,26 +84,38 @@ def get_framework(tensor): raise AssertionError() -def get_tolerance(value): +def get_machine_eps(value): eps = np.finfo(to_numpy(value).dtype).eps if torch is not None and value.dtype == torch.bfloat16: eps = 2**-6 - return eps**0.5 + return eps -def compare_tensors(result, reference, atol=None): +def get_absolute_tolerance(value): + return get_machine_eps(value) ** 0.5 + + +def get_relative_tolerance(value): + return max(1e-5, get_machine_eps(value) ** 0.5) + + +def compare_tensors(result, reference, atol=None, rtol=None): if atol is None: - atol = get_tolerance(result) - return np.allclose(to_numpy(result), to_numpy(reference), atol=atol) + atol = get_absolute_tolerance(result) + if rtol is None: + rtol = get_relative_tolerance(result) + return np.allclose(to_numpy(result), to_numpy(reference), atol=atol, rtol=rtol) -def assert_tensors_equal(result, reference, atol=None): +def assert_tensors_equal(result, reference, atol=None, rtol=None): """ Checks if result is close to the provided numpy reference. """ assert result is not reference, "same object passed as `result` and `reference`!" - ok = compare_tensors(result, reference, atol=atol) + ok = compare_tensors(result, reference, atol=atol, rtol=rtol) if not ok: + print(f"Absdiff: {np.max(np.abs(to_numpy(result) - to_numpy(reference)))}") + print(f"Reldiff: {np.max(np.abs(to_numpy(result) - to_numpy(reference)) / (np.abs(to_numpy(reference)) + 0.000001) )}") print("Result:\n", result) print("Reference:\n", reference) assert ok @@ -128,12 +141,30 @@ def skip_if_cublas_before(version, message="Unsupported cublas version."): return False +# Setting the seed once per day allows randomness, but helps with reproducibility. +matmul_with_random_autotune_rng = np.random.default_rng(seed=abs(hash(datetime.date.today()))) + + +def matmul_with_random_autotune(*args, p=0.25, **kwargs): + """ + Executes matmul, using autotuning with probability p. + """ + constructor_kwargs = ("c", "alpha", "beta", "qualifiers", "options", "stream", "quantization_scales") + plan_kwargs = ("preferences", "epilog", "epilog_inputs", "algorithms", "stream") + execute_kwargs = ("stream",) + mm = nvmath.linalg.advanced.Matmul(*args, **{k: kwargs[k] for k in constructor_kwargs if k in kwargs}) + mm.plan(**{k: kwargs[k] for k in plan_kwargs if k in kwargs}) + if matmul_with_random_autotune_rng.random() < p: + mm.autotune() + return mm.execute(**{k: kwargs[k] for k in execute_kwargs if k in kwargs}) + + class allow_cublas_unsupported: def __init__(self, *, allow_invalid_value=True, unsupported_before=None, message="Unsupported cublas version."): if allow_invalid_value: - self.regex = r"\(CUBLAS_STATUS_(NOT_SUPPORTED|INVALID_VALUE)\)" + self.regex = r"\(?(CUBLAS_STATUS_)?(NOT_SUPPORTED|INVALID_VALUE)\)?" else: - self.regex = r"\(CUBLAS_STATUS_NOT_SUPPORTED\)" + self.regex = r"\(?(CUBLAS_STATUS_)?NOT_SUPPORTED\)?" self.unsupported_before = unsupported_before self.message = message