NVIDIA、Python科学コミュニティ向け数学ライブラリ「nvmath-python」1.0 を一般公開
本文の状態
日本語全文を表示中
詳細モードで約18分の本文を読めます。
同じ出来事の情報源
この情報源を基点に整理
NVIDIA Developer Blog
NVIDIA は Python 科学コミュニティ向けに CUDA-X 数学ライブラリを抽象化する「nvmath-python」v1.0 を一般提供し、CPU から分散マルチノード環境までスケーラブルな高性能計算を可能にする。
AI深層分析を開く2026年7月31日 22:39
AI深層分析
キーポイント
CUDA-X ライブラリの Python 化と抽象化
nvmath-python は cuFFT や cuBLASLt などの CUDA-X 数学ライブラリに対する Pythonic な抽象化レイヤーとして機能し、既存のワークフローを崩さずに GPU 性能を利用可能にする。
ユニバーサルスパーステンソル(UST)の実装
独自のドメイン固有言語を通じてユーザーがアプリケーション最適化されたスパース形式を定義できる「ユニバーサルスパーステンソル」を採用し、従来の実装手法にない柔軟なアプローチを提供する。
多様な環境への迅速かつ柔軟なインストール
pip や conda などのパッケージマネージャーを選べ、依存関係の自動解決や最小限のインストール、CPU バックエンドや NumPy/CuPy/PyTorch との連携を自由に選択できる。
スケーラブルな計算実行環境
単一 CPU や GPU から始まり、分散マルチ GPU、マルチノードシステムまで対応し、大規模な数学演算のスケールアウトを可能にする設計となっている。
既存の配列ライブラリとの補完関係
nvmath-python はインデックスやスライスなどの機能を提供するのではなく、CUDA-X マスライブラリの機能を Python で利用可能にするための補完ツールとして設計されている。
重要な引用
nvmath-python is a library designed to bridge the gap between the Python scientific community and NVIDIA CUDA-X math libraries.
A novel approach to sparsity, the universal sparse tensor (UST), enables the user to create their own unique application-optimal sparse format through a domain-specific language without having to implement it in code.
nvmath-python focuses on exposing the full functionality and power of CUDA-X math libraries in Python, making it easier for existing array libraries and frameworks to use highly optimized GPU-accelerated routines without relying on low-level C/C++ interfaces.
This support simplifies code migration between CPU and GPU and enables hybrid and distributed workflows that combine CPU and GPU execution.
編集コメントを表示
編集コメント
Python の生態系と NVIDIA のハードウェア性能を統合するこのアプローチは、大規模モデルの学習や推論における開発効率を高める重要な一歩となる。特にスパース計算の柔軟な定義が可能になる点は、特定のワークロードに特化した最適化を求める研究者にとって大きな価値を持つ。
Source Article
元記事を日本語で読む
本文に関係しない購読案内、埋め込み通知、サイト内プロモーションは除いています。
NVIDIA の「nvmath-python」は、Python 科学コミュニティと NVIDIA CUDA-X 数学ライブラリをつなぐために設計されたライブラリです。既存のワークフローを崩すことなく、一般的な数値演算に CUDA-X の高性能アクセスを提供します。
API に応じて、このライブラリは CPU や CUDA 対応 GPU、あるいは分散型マルチ GPU・マルチノードシステム上で動作します。
nvmath-python v1.0 リリース
nvmath-python v1.0 が一般提供されたことに伴い、本稿では同ライブラリの設計思想と、CPU や単一 GPU からマルチ GPU・マルチノード規模まで数値演算を加速する独自の機能について解説します。
nvmath-python は、cuFFT、cuBLASLt、cuDSS、cuSPARSE、cuTENSOR、cuBLASMp などの CUDA および NVPL 数学ライブラリの上に構築された Python 風の抽象化レイヤーです。特に注目すべきは、スパース性に対する新しいアプローチである「ユニバーサル・スパーステンソル(UST)」です。これにより、ユーザーはコードを実装することなく、ドメイン固有言語(DSL)を通じてアプリケーションに最適な独自のスパース形式を定義できます。
高速かつ柔軟なインストール
複雑なネイティブ依存関係を持つ Python パッケージのインストールは、時間がかかり、イライラする経験になりがちです。しかし nvmath-python は迅速にインストールでき、さまざまな環境に合わせてカスタマイズ可能です。
パッケージマネージャーとして、pip、conda、uv、または pixi のいずれかを選択します。
パッケージマネージャーの依存関係解決機能を通じてすべての必要な依存関係をインストールするか、CI/CD や CPU のみの環境など特定のシナリオで有用な最小限のインストールを行うかのオプションがあります。
CPU バックエンドやデバイス API のサポート、分散 API などを必要に応じて選択できます。
NumPy、CuPy、PyTorch(またはこれらすべて)など、連携する配列ライブラリを選択してください。利用可能な選択肢の詳細はインストールガイドをご覧ください。
既存の配列ライブラリに有用な補完ツール
NumPy など他の数学ライブラリと同様に、nvmath-python は多くのエンジニアリングや科学計算アプリケーションで重要な*コア数値演算*を実装しています。ただし、これは汎用配列ライブラリの代替となるものではなく、インデックス付け、スライス、集約といった従来の機能を提供するものではありません。
代わりに nvmath-python の焦点は、Python から CUDA-X 数学ライブラリの全機能と威力を直接利用できるようにすることにあります。これにより、既存の配列ライブラリやフレームワークが、低レベルな C/C++ インターフェースに依存することなく、高度に最適化された GPU アクセラレーションルーチンを利用しやすくなります。
以下の例では、nvmath-python が NumPy 配列を受け取り、結果もまた NumPy 配列として返されます。
import numpy as np
import nvmath
m, n, k = 10, 40, 100
a = np.random.randn(m, k)
b = np.random.randn(k, n)
c = nvmath.linalg.advanced.matmul(a, b)
メモリと実行空間の選択
配列ライブラリの選択の柔軟性は、GPU 向けの CuPy や CPU 向けの NumPy など、あらゆるライブラリに適用可能です。これは nvmath-python が以下の要素によって支えられているためです。
- cuBLAS や cuFFT に代表される GPU ライブラリ
- NVIDIA Grace または ARM v8 CPU 向け NVPL、x86 ホスト向け Intel MKL など、CPU ライブラリ
- cuBLASMp、cuSOLVERMp、cuFFTMp などの分散処理対応ライブラリ
このサポートにより、CPU と GPU の間でのコード移行が容易になり、CPU と GPU の実行を組み合わせたハイブリッド型や分散型のワークフローが可能になります。
以下のコードは、nvmath-python が複数のメモリ空間と実行空間をサポートする方法を示しています。
import cupy as cp
import numpy as np
import nvmath
N = 2048
a_gpu = cp.random.randn(N) + 1j * cp.random.randn(N)
a_cpu = np.random.randn(N) + 1j * np.random.randn(N)
c_gpu = nvmath.fft.fft(a_gpu)
c_cpu = nvmath.fft.fft(a_cpu)
各呼び出しの FFT 実行空間は、入力テンソル(a_gpu または a_cpu)から自動的に推定されます。ただし、異なる実行空間を指定することも可能です。ライブラリの ロギング機能 を使用すれば、各操作がどこで実行されたかを確認できます。
汎用 API と特化型 API
nvmath-python の API は、大きく分けて 2 つのクラスに分類されます。1 つは柔軟なマルチツールとして機能する汎用 API(幅広いが浅い)、もう 1 つは精密で専用設計の特殊化 API(狭く深い)です。
汎用 API は、さまざまな実行環境やメモリ空間、オペランドタイプにわたって一貫したユーザー体験を提供することに注力しています。ただし、その設定機能は、幅広い範囲で共通する基本的な機能に限定されています。一方、特殊化 API は、特定の狭い運用範囲のために設計された包括的な機能と設定オプションを備えており、特定のハードウェアに限定されることもあります。
具体例として、高度な行列乗算(matrix multiplication)を見てみましょう。これは GPU 上の密行列(dense operands)に対して複合演算を実装し、最大限のハードウェア効率を引き出すために必要なすべての構成を提供します。対照的に、汎用行列乗算 API は CPU と GPU の両方の実行空間で密行列および構造化されたオペランドに対応しますが、その広範な範囲に共通するオプションの一部のみを提供します。
最適な選択肢は、具体的なユースケースに完全に依存します。特定の演算がハードウェア固有の最適化や独自機能へのアクセスを必要とする計算ボトルネックとなる場合、専用 API が理想的です。一方、パフォーマンスがクリティカルでないタスクや、専用のカスタマイズが不要な場合には、汎用 API の方が適しています。
すべての専用 API は、汎用 API と明確に区別するために、高度なサブモジュール内に配置されています。
nvmath-python によるログ出力
このライブラリは、Python 標準ライブラリの logging モジュール と統合されており、デバッグ、情報、警告、エラーの各レベルで計算の詳細をキャプチャできます。
以下の例では、メモリ空間と実行空間間のデータフロー(高度な行列積 matmul を使用)を示しています。
import numpy as np
import nvmath
import logging
logging.basicConfig(level=logging.INFO,
format="%(asctime)s %(levelname)-8s %(message)s", force=True)
logging.disable(logging.NOTSET)
m, n, k = 8000, 2000, 4000
a_cpu = np.random.randn(m, k).astype(np.float32)
b_cpu = np.random.randn(k, n).astype(np.float32)
d_cpu = nvmath.linalg.advanced.matmul(a_cpu, b_cpu)
生成される出力は以下のようになります。
2025-09-18 14:53:32,166 INFO = SPECIFICATION PHASE =
2025-09-18 14:53:32,167 INFO The data type of operand A is 'float32', and that of operand B is 'float32'.
2025-09-18 14:53:32,168 INFO The input operands' memory space is cpu, and the execution space is on device 0.
...
入力オペランドのメモリ空間と、そこで実行される場所を記録しておくことが重要です。これは、メモリ間や実行環境間でデータ転送が発生している可能性を示す指標となります。次に、一般的な API(fft など)を用いた実験を実行し、メモリと実行空間間のデータフローを確認してみましょう。
import numpy as np
import nvmath
import logging
logging.basicConfig(level=logging.INFO,
format="%(asctime)s %(levelname)-8s %(message)s", force=True)
logging.disable(logging.NOTSET)
N = 10000
e_cpu = (np.random.randn(N) + 1j * np.random.randn(N)).astype(np.complex64)
r_cpu = nvmath.fft.fft(e_cpu)
ログ出力は以下のようになります。
2025-09-18 15:46:22,295 INFO The FFT type is C2C.
2025-09-18 15:46:22,295 INFO The input data type is complex64, and the result data type is complex64.
2025-09-18 15:46:22,296 INFO The specified FFT axes are (0,).
2025-09-18 15:46:22,297 INFO The input tensor's memory space is cpu, and the execution space is cpu, with device cpu.
2025-09-18 15:46:22,298 INFO The specified stream for the FFT ctor is None.
...
ここで注目すべきは、実行空間が入力データのメモリ空間と同じである点です。可能な限り nvmath-python はデータ転送のオーバーヘッドを最小化するために、自動的に最適な実行空間を選択します。ただし、ユーザーが必要に応じて execution キーワード引数を API に渡すことで、実行空間を手動で指定することも可能です。
合成演算が重要な理由
NumPy に似た API を持つ単純な演算は、多くのユースケースで十分に機能します。しかし、基盤となるプリミティブ演算の「演算強度(arithmetic intensity)」が低い場合、それらを連続する呼び出しとしてチェーン化するのは非効率的です。
特に注目すべき例が、「背の高い細長い行列(tall-and-skinny matrix)」に対して GEMM(一般行列積)を計算する場合です。以下のコードは、CuPy と nvmath-python を用いた、背の高い細長い行列に対する GEMM の実装例を示しています。
import cupy as cp
import nvmath
m, n, k = 10_000_000, 40, 10
a = cp.random.randn(m, k, dtype=cp.float32)
b = cp.random.randn(k, n, dtype=cp.float32)
c = cp.random.randn(m, n, dtype=cp.float32)
alpha, beta = 1.5, 0.5
d1 = alpha * cp.matmul(a, b) + beta * c
d2 = nvmath.linalg.advanced.matmul(a, b, c=c, alpha=alpha, beta=beta)
Figure 1 shows that a fused composite operation brings measurable benefits compared to NumPy-like APIs.
image*Figure 1. Effect of kernel fusion on low-arithmetic-intensity composite operations*
nvmath-python は、基盤となる cuBLASLt ライブラリを活用しているため、はるかに高いパフォーマンスを発揮します。このライブラリは、必要に応じてカーネルを結合する「即時コンパイル(just-in-time kernel fusion)」を実現可能です。これは演算強度を高めるための効果的な手法の一つです。
状態保持型 API を活用して準備コストを分散する
これまでの例では、nvmath-python の「関数形式(stateless)」API を活用してきました。これは便利な単一呼び出し API ですが、その背後には「計画フェーズ」と呼ばれる時間のかかる準備ロジックが含まれています。さらに、この準備コストには「自動調整(autotuning)」の費用も含まれる場合があります。
パフォーマンスに関する注意
NVIDIA CUDA-X 数値ライブラリはヒューリスティックを用いて、最適なパフォーマンスを発揮する実装を特定します。問題サイズやデータレイアウト、データタイプごとに最適化された複数の特殊なカーネルが存在する可能性があります。どのハードウェア、ワークロード、その他の要因の組み合わせにおいて特定のカーネルが最も高速に動作するかは、必ずしも明瞭ではありません。自動調整(autotuning)は、デフォルトのカーネル選択を上書きするために、利用可能なカーネルオプションを反復的に試し、そのパフォーマンスを測定して最良のものを選択するプロセスです。このため、自動調整フェーズには非常に時間がかかる場合があります。
深層学習などのワークロードでは、同じ演算が異なる入力データで繰り返し実行されます。このような場合、計画の作成と再利用を行うことで、計画にかかるコストを実行全体に分散(アモタイズ)できます。nvmath-python のクラスベース(ステートフル)な API は、こうしたワークフローをサポートしています。
パフォーマンスに関する注意
NVIDIA CUDA-X 数値ライブラリはヒューリスティックを用いて、最適なパフォーマンスを発揮する実装を特定します。問題サイズやデータレイアウト、データタイプごとに最適化された複数の特殊なカーネルが存在する可能性があります。どのハードウェア、ワークロード、その他の要因の組み合わせにおいて特定のカーネルが最も高速に動作するかは、必ずしも明瞭ではありません。自動調整(autotuning)は、デフォルトのカーネル選択を上書きするために、利用可能なカーネルオプションを反復的に試し、そのパフォーマンスを測定して最良のものを選択するプロセスです。このため、自動調整フェーズには非常に時間がかかる場合があります。
深層学習などのワークロードでは、同じ演算が異なる入力データで繰り返し実行されます。このような場合、計画の作成と再利用を行うことで、計画にかかるコストを実行全体に分散(アモタイズ)できます。nvmath-python のクラスベース(ステートフル)な API は、こうしたワークフローをサポートしています。
以下の例は、nvmath-python のクラス形式 API を使用して、batch_size 個の行列 a と b、およびバイアス bias に対して matmul(RELU_BIAS エピローグ付き)を実行する方法を示しています。前の行列乗算の結果が次の行列乗算の入力となり、feed_count 回の操作が行われます。
このプロセスでは *計画* フェーズに加え、*自動調整* フェーズも実行されます。
以下のコードは、計画・自動調整・実行を明確に分離したステートフル API の使用例です。
import nvmath
from nvmath.linalg.advanced import MatmulEpilog
import cupy as cp
feed_count = 10
batch_size = 1024
m, n, k = 1024, 1024, 1024
a = cp.random.rand(batch_size, m, k, dtype=cp.float32)
b = cp.random.rand(batch_size, k, n, dtype=cp.float32)
bias = cp.random.rand(batch_size, m, 1, dtype=cp.float32)
with nvmath.linalg.advanced.Matmul(a, b) as mm:
mm.plan(epilog=MatmulEpilog(MatmulEpilog.RELU_BIAS),
epilog_inputs={"bias": bias})
mm.autotune(iterations=5)
for i in range(feed_count):
d = mm.execute()
mm.reset_operands_unchecked(a=d)
image図 2. ステートフル API を用いることで、複数回の実行を通じて計画と自動調整のコストを迅速に回収できます。
図 2 は、実行回数が増えるにつれて計算コストがどのように変化するかを示しています。細い破線は、nvmath-python のステートレス API を使用した場合のコストを表します。太い破線は、ステートフル API に切り替えることで得られるコスト削減効果を、点線はオートチューニングによる追加のパフォーマンス向上を示しています。
ステートフル API は仕様定義や準備にかかるコストを一度で済ませるのに対し、ステートレス API では毎回そのコストが発生します。一方、オートチューニングの恩恵はセッションを超えて享受できます。一度最適化された計画をディスクに保存しておけば、新しいセッションでもそれをロードして再利用できるからです。
図 3 は、組み込みのヒューリスティック(経験則)を使えば、多くの場合でオートチューニングなしでも高性能なカーネルが選択できると示しています。ただし、問題サイズやデータ型、演算子のレイアウト、ハードウェアの種類など、特定の組み合わせにおいてはオートチューニングによってさらに性能を引き出せるケースもあります。
今回のテスト構成では、NVIDIA RTX A6000 で最大の速度向上が見られましたが、NVIDIA B200 ではオートチューニングなしでもピークパフォーマンスに達していました。
image*図 3. 実行フェーズでのオートチューニングは常に劇的な速度向上をもたらすわけではありませんが、この特定の課題では RTX A6000 で 256% の性能向上を達成しています*
nvmath-python と融合したカスタムカーネル
nvmath-python は numba-cuda などの Python コンパイラと連携しており、高性能なカスタム Python コードを JIT(Just-In-Time)コンパイルして、nvmath-python の操作と併用することが可能です。
カスタム FFT コールバック
FFT のためのコールバック関数
FFT 用のコールバック関数は、事前に定義されたシグネチャを持つ Python 関数として記述され、JIT コンパイルによって中間表現に変換されます。この中間表現は、後で nvmath-python の順方向または逆方向 FFT に対するカスタムプロローグ(事前処理)やエピローグ(事後処理)として利用されます。
image*図 4: 左側はガウスフィルタを適用する前の犬のグレースケール画像。右側は、nvmath-python の FFT とカスタム JIT コンパイルされたコールバック関数を使用して実装した結果の画像*
ガウスフィルタの例
具体例として、元の画像にぼかし効果を適用するガウスフィルタの実装を紹介します。以下のコードスニペットでは、画像読み込みに PIL ライブラリを使用し、これをグレースケール [0, 1] の CuPy ndarray に変換しています。画像のフィルタリング処理は、「img → R2C FFT(実数→複素数)→ ガウスフィルタ → C2R iFFT(複素数→実数)→ filtered_img」という一連の流れで行われます。ガウスフィルタは空間領域ではガウス関数ですが、周波数領域でも同様にガウス分布となります。
以下に、nvmath-python の FFT とカスタムコールバック関数を使用して画像のガウスフィルタを適用するコードを示します:
from PIL import Image
import nvmath
import cupy as cp
img = cp.asarray(Image.open("your_lovely_dog.jpg").convert("L")) / 255.0
wh = img.shape[0] * image.shape[1]
sigma_value = 20.0
def gaussian_filter(shape, sigma):
fy = cp.fft.fftfreq(shape[0])[:,None]
fx = cp.fft.rfftfreq(shape[1])[None,:]
return = cp.exp(-2.0 * cp.pi * cp.pi * sigma * sigma * (fx * fx + fy * fy))
def epilog_impl(data_out, offset, data, filter_data, unused):
data_out[offset] = data * filter_data[offset] / wh
epilog = nvmath.fft.compile_epilog(epilog_impl, "complex64", "complex64")
h_filter = gaussian_filter(img.shape, sigma)
img_fft = nvmath.fft.rfft(image, epilog={"ltoir": epilog, "data": h_filter.data.ptr})
filtered_img = nvmath.fft.irfft(img_fft)Custom numba-cuda kernels with nvmath-python calls
2 つ目の一般的なユースケースは、numba-cuda で記述した GPU カーネル内部から nvmath-python のデバイス API を呼び出すことです。nvmath-python は、FFT、GEMM、密行列直接ソルバー(LU、Cholesky、QR)、および乱数生成器(RNG)に対応するデバイス API をサポートしています。
以下の例は、モンテカルロ株価シミュレーションのための幾何ブラウン運動(GBM: Geometric Brownian Motion)の実装を示しています。これは、ガウス分布用の nvmath-python の乱数生成器と、正規分布から GBM モンテカルロパスへ変換するカスタム numba-cuda コードを組み合わせて使用しています。
from numba import cuda
from nvmath.device import random
import cupy as cp
import math
compiled_rng = random.Compile(cc=None)
rng_seed = 7777
n_time_steps, n_paths = 252, 8192
mu, sigma, s0 = 0.003, 0.027, 100.0
threads_per_block = 32
blocks = n_paths // threads_per_block + bool(n_paths % threads_per_block)
nthreads = threads_per_block * blocks
@cuda.jit(link=compiled_rng.files, extensions=compiled_rng.extension)
def init_rng(states, seed):
idx = cuda.grid(1)random.init(seed, idx, 0, states[idx])
@cuda.jit(link=compiled_rng.files, extensions=compiled_rng.extension)
def generate_gbm_paths(states, paths, nsteps, mu, sigma, s0):
idx = cuda.grid(1)
if idx >= paths.shape[0]:
return
paths[idx, 0] = s0
for i in range(1, nsteps, 4):
v = random.normal4(states[idx])
vals = v.x, v.y, v.z, v.w
for j in range(i, min(i + 4, nsteps)):
paths[idx, j] = paths[idx, j - 1] * math.exp(mu + sigma * vals[j - i])
states = random.StatesPhilox4_32_10(nthreads)
init_rngblocks, threads_per_block
paths = cp.empty((n_paths, n_time_steps), dtype=cp.float32, order='F')
generate_gbm_pathsblocks, threads_per_block
generate_gbm_paths 内のすべての演算は演算強度が低いため、ホスト API ベースの実装では非効率になります。これらの演算を numba-cuda と nvmath-python のデバイス API で融合させることが極めて重要です。
nvmath-python を始める
パフォーマンスを犠牲にすることなく生産性を重視して設計された nvmath-python は、現代の数学ライブラリのあり方を再定義しています。以下の 1 つのコマンドで簡単に始められます:
pip install nvmath-python[cu13]
さらに役立つリソースとして以下があります。
- 詳細なセットアップ手順を記載したインストールドキュメント
- コード例や深入りしたチュートリアルノートブックを含む nvmath-python の GitHub リポジトリ
- NVIDIA Accelerated Computing Hub で提供される Python トレーニング教材
前回の投稿「nvmath-python のユニバーサルスパーステンソルでスパース深層学習を簡素化する」および、最新ニュースや発表を確認できる nvmath-python プロダクトページをご覧ください。
謝辞
本ライブラリは、NVIDIA 社内外の多くの人々の協力によって実現されました。主な貢献者には以下の方が含まれます:
Harun Bayraktar, Becca Zandstein, Lukasz Ligowski, Aart Bik, Yevhenii Havrylko, Juan Galvez, Daniel Ching, Mark Olah, Yang Gao, Szymon Karpinski, Kamil Tokarski, Francesco Rizzi, Jakub Lisowski, Marcin Rogowski, Robbie Jensen, Artem Amogolonov, Sushma Kini, Rachna Pandey, Graham Markall, Michael Yh Wang, Bradley Dice, Liam Zhang, Jack Cui, Chang Liu, Qi Xia, Feng Cheng, Ruilin Tian, Zan Xu, Almog Segal, Kirill Voronin, Evarist Fomenko、その他多数。
原文を表示
NVIDIA nvmath-python is a library designed to bridge the gap between the Python scientific community and NVIDIA CUDA-X math libraries. It gives Python users access to CUDA-X performance for common math operations without disrupting existing workflows. Depending on the API, operations can run on a CPU, CUDA-enabled GPU, or distributed multi-GPU, multi-node systems.
nvmath-python v1.0 release
With the general availability of nvmath-python v1.0, this post explores the library’s design and unique capabilities for accelerating math operations—from a CPU or single GPU up to multi-GPU, multi-node scale. nvmath-python is a Pythonic abstraction layer over the CUDA and NVPL math libraries such as cuFFT, cuBLASLt, cuDSS, cuSPARSE, cuTENSOR, cuBLASMp, and more. A novel approach to sparsity, the universal sparse tensor (UST), enables the user to create their own unique application-optimal sparse format through a domain-specific language without having to implement it in code.
Fast and flexible installation
Installing a Python package with complex native dependencies can be a time-consuming and frustrating experience. nvmath-python installs quickly and can be customized for different environments.
- Choose a package manager, such as pip, conda, uv, or pixi.
- There is an option to install all required dependencies through the package manager’s dependency resolution system or perform a bare-minimum installation, useful in scenarios such as CI/CD or CPU-only environments.
- Pick and choose the CPU backend, device APIs support, or distributed APIs.
- Choose a companion array library to work with, such as NumPy, CuPy, or PyTorch (or all of them). See the detailed installation guide for available options.
A useful complement to existing array libraries
Like other math libraries such as NumPy, nvmath-python implements *core numerical operations *useful in many engineering and scientific computing applications. However, it’s not intended to replace general-purpose array libraries or provide traditional features like indexing, slicing, or reduction.
Instead, nvmath-python focuses on exposing the full functionality and power of CUDA-X math libraries in Python, making it easier for existing array libraries and frameworks to use highly optimized GPU-accelerated routines without relying on low-level C/C++ interfaces.
In the following example, nvmath-python consumes NumPy arrays and the result is also a NumPy array.
import numpy as np
import nvmath
m, n, k = 10, 40, 100
a = np.random.randn(m, k)
b = np.random.randn(k, n)
c = nvmath.linalg.advanced.matmul(a, b)
Choice of memory and execution spaces
The flexibility of choosing an array library applies to both GPU libraries, such as CuPy, and CPU libraries, such as NumPy. This is possible because nvmath-python is backed by the following:
- GPU libraries such as cuBLAS and cuFFT.
- CPU libraries such as NVPL for NVIDIA Grace or any ARM v8 CPUs and Intel MKL for x86 hosts.
- Distributed libraries such as cuBLASMp, cuSOLVERMp, or cuFFTMp.
This support simplifies code migration between CPU and GPU and enables hybrid and distributed workflows that combine CPU and GPU execution.
The following code illustrates how nvmath-python supports multiple memory and execution spaces.
import cupy as cp
import numpy as np
import nvmath
N = 2048
a_gpu = cp.random.randn(N) + 1j * cp.random.randn(N)
a_cpu = np.random.randn(N) + 1j * np.random.randn(N)
c_gpu = nvmath.fft.fft(a_gpu)
c_cpu = nvmath.fft.fft(a_cpu)
The fft execution space for each call is inferred from its input tensor, either a_gpu or a_cpu, although a different execution space can be specified. The library’s logging facility shows where each operation ran.
Generic and specialized APIs
The APIs within nvmath-python are broadly divided into two classes: generic APIs that act as flexible multitools (wide but shallow), and specialized APIs designed as precise, dedicated instruments (narrow and deep).
Generic APIs focus on providing a uniform user experience across various execution and memory spaces as well as operand types, however they restrict configurability to the baseline, common features shared across their broad scope. Meanwhile, specialized APIs provide a comprehensive set of features and configurations designed specifically for a narrow operational range and may be restricted to particular hardware.
To illustrate, the advanced matrix multiplication implements the composite operation specifically for dense operands on the GPU and provides every configuration necessary to squeeze out the highest possible hardware efficiency. Conversely, the generic matrix multiplication API accommodates dense and structured operands across CPU and GPU execution spaces, but offers only the common subset of options applicable to its wider scope.
The optimal choice depends entirely on the specific use-case: specialized APIs are ideal when an operation becomes a computational bottleneck that demands hardware-specific optimizations or access to distinct features. Meanwhile, generic APIs are better suited for tasks that are not performance-critical or when specialized customization is unnecessary. All specialized APIs live within the advanced submodules to keep them distinct from generic APIs.
Logging with nvmath-python
The library provides integration with the Python standard library logger from the logging module for capturing computational details at various levels (debug, information, warning, and error).
The following example illustrates the data flow between memory and execution spaces (using the advanced matmul).
import numpy as np
import nvmath
import logging
logging.basicConfig(level=logging.INFO,
format="%(asctime)s %(levelname)-8s %(message)s", force=True)
logging.disable(logging.NOTSET)
m, n, k = 8000, 2000, 4000
a_cpu = np.random.randn(m, k).astype(np.float32)
b_cpu = np.random.randn(k, n).astype(np.float32)
d_cpu = nvmath.linalg.advanced.matmul(a_cpu, b_cpu)
The produced output will look like:
2025-09-18 14:53:32,166 INFO = SPECIFICATION PHASE =
2025-09-18 14:53:32,167 INFO The data type of operand A is 'float32', and that of operand B is 'float32'.
2025-09-18 14:53:32,168 INFO The input operands' memory space is cpu, and the execution space is on device 0.
...
Take note of the record showing where operands come from and where they are consumed. This is an indication of potentially expensive data transfer between memory and execution spaces. Now run a similar experiment with a generic API like fft to illustrate data flow between memory and execution spaces.
import numpy as np
import nvmath
import logging
logging.basicConfig(level=logging.INFO,
format="%(asctime)s %(levelname)-8s %(message)s", force=True)
logging.disable(logging.NOTSET)
N = 10000
e_cpu = (np.random.randn(N) + 1j * np.random.randn(N)).astype(np.complex64)
r_cpu = nvmath.fft.fft(e_cpu)
The logging output looks like:
2025-09-18 15:46:22,295 INFO The FFT type is C2C.
2025-09-18 15:46:22,295 INFO The input data type is complex64, and the result data type is complex64.
2025-09-18 15:46:22,296 INFO The specified FFT axes are (0,).
2025-09-18 15:46:22,297 INFO The input tensor's memory space is cpu, and the execution space is cpu, with device cpu.
2025-09-18 15:46:22,298 INFO The specified stream for the FFT ctor is None.
...
Note that execution space is the same as inputs’ memory space. Whenever possible, nvmath-python selects the execution space to minimize the data transfer overheads. The user is free to select the desired execution space by providing the execution keyword argument to an API.
Why composite operations matter
An operation like with a pure NumPy-like API will work decently in many use cases. However, when underlying primitive operations have low *arithmetic intensity*, chaining them as a series of calls is inefficient. A notable example is computing GEMM with being a tall-and-skinny matrix:
The following code illustrates GEMM on tall-and-skinny matrices with CuPy and nvmath-python.
import cupy as cp
import nvmath
m, n, k = 10_000_000, 40, 10
a = cp.random.randn(m, k, dtype=cp.float32)
b = cp.random.randn(k, n, dtype=cp.float32)
c = cp.random.randn(m, n, dtype=cp.float32)
alpha, beta = 1.5, 0.5
d1 = alpha * cp.matmul(a, b) + beta * c
d2 = nvmath.linalg.advanced.matmul(a, b, c=c, alpha=alpha, beta=beta)
Figure 1 shows that a fused composite operation brings measurable benefits compared to NumPy-like APIs.

nvmath-python performs much better due to the underlying cuBLASLt library, capable of just-in-time *kernel fusion*. It is among the effective techniques for increasing arithmetic intensity.
Amortizing preparation costs by using stateful APIs
All previous examples exploit the *functional-form*, or *stateless*, API of the nvmath-python. It’s a convenient single-call API, which involves a time-consuming preparation logic, called the *planning *phase. Additionally the preparation cost may also include the cost of *autotuning*. It is distinct from the *execution *phase that performs the requested math operation after the planning/autotuning.
Performance note
NVIDIA CUDA-X math libraries employ heuristics to determine a specific implementation that yields the best performance. There can be multiple choices of specialized kernels optimized for certain problem sizes, layouts or data types. It is not always obvious which kernel will run best on a specific combination of hardware, workload and other factors. Autotuning aims at overriding the default kernel selection by iterating through kernel options, measuring their performance, and choosing the best one. As a result, the autotuning phase may be very time consuming.
In workloads such as deep learning, the same operation may run repeatedly with different inputs. Creating and reusing a plan across executions amortizes its planning cost. nvmath-python’s class-based, or stateful, APIs support this workflow.
The following example illustrates the use of class-form API for matmul (with RELU_BIAS epilog) on a batch of the batch_size size of matrices a and b, and biases bias. The result of the prior matrix multiplication is an operand in the next matrix multiplication, and there are feed_count operations. Besides *planning* it also performs an *autotuning* phase.
The code below illustrates using stateful APIs with planning, autotuning, and execution as distinct phases.
import nvmath
from nvmath.linalg.advanced import MatmulEpilog
import cupy as cp
feed_count = 10
batch_size = 1024
m, n, k = 1024, 1024, 1024
a = cp.random.rand(batch_size, m, k, dtype=cp.float32)
b = cp.random.rand(batch_size, k, n, dtype=cp.float32)
bias = cp.random.rand(batch_size, m, 1, dtype=cp.float32)
with nvmath.linalg.advanced.Matmul(a, b) as mm:
mm.plan(epilog=MatmulEpilog(MatmulEpilog.RELU_BIAS),
epilog_inputs={"bias": bias})
mm.autotune(iterations=5)
for i in range(feed_count):
d = mm.execute()
mm.reset_operands_unchecked(a=d)

Figure 2 shows how computational cost changes with the number of executions. The fine dashed line represents the cost of using nvmath-python’s stateless API. The coarse dashed line shows the cost reduction from switching to the stateful API, and the dash-dot line shows the additional performance gain from autotuning. The stateful API amortizes specification and preparation costs, while the stateless API incurs them during every execution. Autotuning benefits can extend across sessions because an autotuned plan can be serialized to disk and loaded in a new session.
Figure 3 shows that built-in heuristics can often select a high-performing kernel without autotuning. However, some combinations of problem size, data type, operand layout, hardware, and other factors benefit from autotuning. In the tested configuration, the NVIDIA RTX A6000 shows the largest speedup, while the NVIDIA B200 reaches peak performance without autotuning.

Custom kernels fused with nvmath-python
nvmath-python integrates with Python compilers such as numba-cuda, enabling high-performance custom Python code to be compiled just in time (JIT) and used alongside nvmath-python operations.
Custom FFT callbacks
Callbacks for FFT are written as Python functions with a predefined signature and JIT-compiled to intermediate representation, which is later used as a custom prolog or epilog for nvmath-python’s forward or inverse FFT.

Gaussian filter example
As an illustration we implement a Gaussian filter, which applies blurring to the original image. The below code snippet uses PIL library for image loading, which is then converted to a grayscale [0, 1] image as a CuPy ndarray. For image filtration we implement a chain of img → R2C FFT → Gaussian filter → C2R iFFT → filtered_img. The Gaussian filter is , which in frequency domain is also a Gaussian .
The following code shows how to apply a Gaussian image filter with nvmath-python FFT and custom callback function:
from PIL import Image
import nvmath
import cupy as cp
img = cp.asarray(Image.open("your_lovely_dog.jpg").convert("L")) / 255.0
wh = img.shape[0] * image.shape[1]
sigma_value = 20.0
def gaussian_filter(shape, sigma):
fy = cp.fft.fftfreq(shape[0])[:,None]
fx = cp.fft.rfftfreq(shape[1])[None,:]
return = cp.exp(-2.0 * cp.pi * cp.pi * sigma * sigma * (fx * fx + fy * fy))
def epilog_impl(data_out, offset, data, filter_data, unused):
data_out[offset] = data * filter_data[offset] / wh
epilog = nvmath.fft.compile_epilog(epilog_impl, "complex64", "complex64")
h_filter = gaussian_filter(img.shape, sigma)
img_fft = nvmath.fft.rfft(image, epilog={"ltoir": epilog, "data": h_filter.data.ptr})
filtered_img = nvmath.fft.irfft(img_fft)
Custom numba-cuda kernels with nvmath-python calls
The second commonly used scenario is calling nvmath-python device APIs from within GPU kernels written in numba-cuda. nvmath-python supports device APIs for FFTs, GEMM, dense direct solvers (LU, Cholesky, QR) and RNG. The following example shows the implementation of the Geometric Brownian Motion (GBM) for Monte Carlo stock price simulations. It uses nvmath-python’s random number generator for Gaussian distribution along with the custom numba-cuda code that converts normal distribution to the GBM Monte Carlo paths:
from numba import cuda
from nvmath.device import random
import cupy as cp
import math
compiled_rng = random.Compile(cc=None)
rng_seed = 7777
n_time_steps, n_paths = 252, 8192
mu, sigma, s0 = 0.003, 0.027, 100.0
threads_per_block = 32
blocks = n_paths // threads_per_block + bool(n_paths % threads_per_block)
nthreads = threads_per_block * blocks
@cuda.jit(link=compiled_rng.files, extensions=compiled_rng.extension)
def init_rng(states, seed):
idx = cuda.grid(1)
random.init(seed, idx, 0, states[idx])
@cuda.jit(link=compiled_rng.files, extensions=compiled_rng.extension)
def generate_gbm_paths(states, paths, nsteps, mu, sigma, s0):
idx = cuda.grid(1)
if idx >= paths.shape[0]:
return
paths[idx, 0] = s0
for i in range(1, nsteps, 4):
v = random.normal4(states[idx])
vals = v.x, v.y, v.z, v.w
for j in range(i, min(i + 4, nsteps)):
paths[idx, j] = paths[idx, j - 1] * math.exp(mu + sigma * vals[j - i])
states = random.StatesPhilox4_32_10(nthreads)
init_rng[blocks, threads_per_block](states, rng_seed)
paths = cp.empty((n_paths, n_time_steps), dtype=cp.float32, order='F')
generate_gbm_paths[blocks, threads_per_block](states, paths, n_time_steps, mu, sigma, s0)
Every operation in generate_gbm_paths has low *arithmetic intensity*, which makes the host API-based implementation inefficient. It is crucial to get these operations fused with numba-cuda and nvmath-python device APIs.
Get started with nvmath-python
Designed for productivity without performance compromises, nvmath-python reimagines the design of modern math libraries. Get started with one simple command:
pip install nvmath-python[cu13]
Additional resources include:
- Installation documentation with detailed setup instructions
- The nvmath-python GitHub repository, including code examples and in-depth tutorial notebooks
- Extended Python training materials in the NVIDIA Accelerated Computing Hub
- The earlier post, Simplify Sparse Deep Learning with the Universal Sparse Tensor in nvmath-python
- The nvmath-python product page for the latest news and announcements
Acknowledgments
The library is a result of efforts of many people from across NVIDIA, including:
*Harun Bayraktar, Becca Zandstein, Lukasz Ligowski, Aart Bik, Yevhenii Havrylko, Juan Galvez, Daniel Ching, Mark Olah, Yang Gao, Szymon Karpinski , Kamil Tokarski , Francesco Rizzi, Jakub Lisowski, Marcin Rogowski, Robbie Jensen , Artem Amogolonov, Sushma Kini, Rachna Pandey, Graham Markall, Michael Yh Wang, Bradley Dice, Liam Zhang, Jack Cui, Chang Liu, Qi Xia, Feng Cheng, Ruilin Tian, Zan Xu, Almog Segal, Kirill Voronin, Evarist Fomenko, and many more.*
関連記事
今日のまとめ
AIデイリーブリーフで今日の重要ニュースをまとめ読み