NVIDIA's CUDA math libraries for .NET. NVMath.Net is a C# counterpart to
nvmath-python: idiomatic, async-friendly wrappers over cuFFT, cuBLASLt,
cuBLAS, cuSPARSE, cuDSS, cuTENSOR, cuSOLVER and cuRAND, with the same plan → execute workflow, explicit resource
ownership and no native build step (the NVIDIA libraries are loaded at run time).
📖 Documentation: https://zcsizmadia.github.io/NVMath.Net/ — guides, per-module references and the generated API reference.
| Area | Wraps | Highlights |
|---|---|---|
| FFT | cuFFT | C2C / R2C / C2R, N-D and batched plans, leading- or trailing-axis layouts, output normalization (None, Backward, Forward, Orthonormal) |
| Matrix multiplication | cuBLASLt, cuBLAS | Matmul with epilogs (bias, ReLU, GELU, aux outputs), batching, FP8 scale pointers, algorithm autotuning with a plan cache, conjugate transpose and complex matmul; GEMM, TRSM, SYMM, DGMM |
| Sparse | cuSPARSE | CSR, COO, BSR and CSC matrices (real and complex); SpMV, SpMM, SpSV, SpSM, SDDMM, SpGEMM, CSR↔CSC |
| Sparse direct solver | cuDSS | SparseDirectSolver<T>: analyse / factorise / refactorise / solve; LU, LDLᵀ, Cholesky; real and complex (Hermitian) |
| Tensors | cuTENSOR 2 | Contractions, element-wise unary / binary / trinary operations, permutation, reduction |
| Dense solvers | cuSOLVER, cuBLAS | LU, QR, least squares, Cholesky, SVD, eigenvalues; batched Cholesky / LU / Jacobi SVD; complex (double) variants including SVD |
| Random numbers | cuRAND | Pseudo- and quasi-random generators, uniform / normal / log-normal / Poisson, integers, skip-ahead |
| Infrastructure | CUDA runtime | DeviceBuffer<T>, streams, events, multi-GPU helper, pluggable device memory managers, dependency-free logging hook |
| Requirement | Version |
|---|---|
| .NET | 8.0, 9.0 or 10.0 |
| NVIDIA GPU | Compute capability 7.0+ recommended |
| CUDA Toolkit | 13.x (runtime, cuBLAS, cuBLASLt, cuFFT 12, cuSPARSE 12, cuSOLVER 12, cuRAND 10) |
| cuTENSOR (tensor module) | 2.x (cutensor.dll / libcutensor.so.2) |
cuDSS (SparseDirectSolver) |
0.x (cudss64_0.dll / libcudss.so.0) |
Libraries are loaded lazily on first use, so you only need the ones for the modules you call. On Windows the CUDA
Toolkit, cuTENSOR and cuDSS default install folders are searched automatically; otherwise put the folder containing the
libraries on PATH (Windows) or LD_LIBRARY_PATH (Linux). Details:
Installing the native libraries.
dotnet add package NVMath.Netor add a project reference to src/NVMathNet/NVMathNet.csproj.
Matrices and tensors are column-major (the cuBLAS convention); FFT shapes are row-major (the last axis is contiguous). Always dispose GPU resources (using).
using NVMathNet;
using NVMathNet.Fft;
using var input = new DeviceBuffer<System.Numerics.Complex>(512);
input.CopyFrom(hostData);
// One-shot helper: plans, executes, returns the output buffer
using var output = await Fft.FftAsync(input, new long[] { 512 });
var spectrum = output.ToArray();For repeated transforms use the plan-based Fft class, which also supports N-D shapes, batching, axes, and
FftOptions.Normalization.
using NVMathNet;
using NVMathNet.LinAlg;
// D = alpha * A * B + beta * C (M = N = K = 4)
using var matmul = new Matmul(m: 4, n: 4, k: 4, options: new MatmulOptions { Blocking = true });
await matmul.PlanAsync();
using var a = new DeviceBuffer<float>(16);
using var b = new DeviceBuffer<float>(16);
using var c = new DeviceBuffer<float>(16);
using var d = new DeviceBuffer<float>(16);
// ... fill a and b (column-major), c.Clear() ...
await matmul.ExecuteAsync<float>(a, b, c, d, alpha: 1.0, beta: 0.0);using NVMathNet;
using NVMathNet.Sparse;
// 3x3 identity in CSR
var rowOffsets = new DeviceBuffer<int>(4);
var colIndices = new DeviceBuffer<int>(3);
var values = new DeviceBuffer<float>(3);
rowOffsets.CopyFrom(new[] { 0, 1, 2, 3 });
colIndices.CopyFrom(new[] { 0, 1, 2 });
values.CopyFrom(new[] { 1f, 1f, 1f });
using var matrix = new CsrMatrix<float>(3, 3, rowOffsets, colIndices, values); // owns the buffers
using var x = new DeviceBuffer<float>(3);
using var y = new DeviceBuffer<float>(3);
x.CopyFrom(new[] { 4f, 5f, 6f });
await SparseLinearAlgebra.SpMVAsync(matrix, x, y); // y = A x
// Solve A z = x with cuDSS
using var z = new DeviceBuffer<float>(3);
using var solver = new SparseDirectSolver<float>(matrix);
solver.Solve(x, z);using NVMathNet;
using NVMathNet.Tensor;
// C[m,n] = A[m,k] * B[k,n] (mode labels: m=0, n=1, k=2)
using var contraction = new TensorContraction(
[4, 4], [0, 2], // A
[4, 4], [2, 1], // B
[4, 4], [0, 1], // C
[4, 4], [0, 1], // D
TensorDataType.Float32,
new TensorContractionOptions { Blocking = true });
await contraction.PlanAsync();
await contraction.ExecuteAsync(a.PointerAsInt, b.PointerAsInt, c.PointerAsInt, d.PointerAsInt, alpha: 1.0, beta: 0.0);
// Row sums of a 3x4 tensor: D[i] = sum_j A[i,j]
using var reduce = new TensorReduction([3, 4], [0, 1], [3], [0]);
await reduce.ExecuteAsync(a3x4, rowSums);using NVMathNet;
using NVMathNet.Solver;
await DenseSolver.SolveAsync(dA, dB, n); // LU solve; B is overwritten with X
await DenseSolver.LeastSquaresAsync(dA, dB, m, n); // min ||A x - b||
await DenseSolver.EigAsync(dA, dW, n, computeVectors: true); // symmetric eigenproblemusing NVMathNet;
using NVMathNet.Rand;
using var rng = new CudaRandom(seed: 42);
using var buf = new DeviceBuffer<float>(10_000);
await rng.FillUniformAsync(buf); // (0, 1]
await rng.FillNormalAsync(buf, mean: 0f, stddev: 1f);using NVMathNet;
using var ctx = new MultiGpuContext(); // all visible devices
await ctx.ForEachDeviceAsync(async (index, deviceId, stream) =>
{
using var buf = new DeviceBuffer<float>(1024);
// ... GPU work on device `deviceId` ...
await stream.SynchronizeAsync();
});- Website: https://zcsizmadia.github.io/NVMath.Net/
- Guides: Getting started, Core concepts, Coming from nvmath-python, Troubleshooting
- Module references (also in the repository under
docs/api): Core · FFT · Linear algebra · Sparse · Tensor · Solver · Random - Samples: 15 runnable projects in
samples/, indexed in the samples guide; they verify their own results.
To build the documentation site locally:
dotnet tool restore
dotnet docfx docs/docfx.json --servegit clone https://github.com/zcsizmadia/NVMath.Net.git
cd NVMath.Net
dotnet buildThe test suite needs a CUDA-capable GPU and the native libraries (cuTENSOR and cuDSS tests need those libraries too):
dotnet testThe hosted CI has no GPU: it builds the library, tests and samples and packs the NuGet package, but does not run the tests. Run them locally before sending a change.
The library and tests target net8.0, net9.0 and net10.0 (set by DefaultTargetFrameworks in Directory.Build.props); the samples target net10.0.
src/NVMathNet/
Interop/ Raw bindings for each native library (public *Native classes) and exception types
Fft/ cuFFT wrapper
LinAlg/ cuBLASLt Matmul, GEMM, TRSM, SYMM, DGMM
Sparse/ CSR/COO/BSR/CSC matrices, cuSPARSE operations, cuDSS direct solver
Tensor/ cuTENSOR contraction, element-wise, permutation, reduction
Solver/ cuSOLVER dense solvers (single, batched, complex)
Rand/ cuRAND generators
Memory/ Pluggable device memory managers
*.cs DeviceBuffer, PinnedBuffer, CudaStream, CudaEvent, CudaDevice, MultiGpuContext
tests/NVMathNet.Tests/ TUnit test suite (requires a GPU)
samples/ One runnable project per feature area
docs/ Documentation site (DocFX) and per-module guides
Native libraries are loaded at run time through System.Runtime.InteropServices.NativeLibrary and function pointers
instead of [DllImport]. A single code path loads the differently named binaries on each platform
(cufft64_12.dll on Windows, libcufft.so.12 on Linux) and defers loading until a module is first used.
Function signatures and enum values are checked against the NVIDIA headers.
Issues and pull requests are welcome. Public members need XML documentation (missing docs are a build error), and new
functionality needs a test, a section in the matching docs/api guide, and where appropriate a sample.