numerics

C++23 solver suite for numerical linear algebra, ODE/PDE integration, Fourier transforms, and statistical simulation.

Direct solvers, Krylov methods, eigen/SVD routines, finite-difference operators, FFTs, and Monte Carlo kernels with custom implementations plus optional BLAS/LAPACK/LAPACKE, FFTW3, OpenMP, CUDA, and MPI backends.

Install

Link the library target from CMake. The target requires a C++23 compiler.

include(FetchContent)
FetchContent_Declare(
    numerics
    GIT_REPOSITORY https://github.com/AdityaDendukuri/numerics
    GIT_TAG        main
)
FetchContent_MakeAvailable(numerics)
target_link_libraries(my_app PRIVATE numerics)

Installed use:

find_package(numerics REQUIRED)
target_link_libraries(my_app PRIVATE numerics::numerics)

Optional system packages: BLAS/LAPACK/LAPACKE or MKL, FFTW3, OpenMP, CUDA, and MPI.

Examples

#include <numerics.hpp>

int main() {
    const double sigma = 10.0, rho = 28.0, beta = 8.0 / 3.0;

    auto lorenz = [&](double, const num::Vector& s, num::Vector& ds) {
        ds[0] = sigma * (s[1] - s[0]);
        ds[1] = s[0] * (rho - s[2]) - s[1];
        ds[2] = s[0] * s[1] - beta * s[2];
    };

    num::Series xz;
    auto observer = [&](double, const num::Vector& y) {
        xz.store(y[0], y[2]);
    };

    num::solve(
        num::ODEProblem{lorenz, {1.0, 0.0, 0.0}, 0.0, 50.0},
        num::RK45{.rtol = 1e-8, .atol = 1e-10, .max_steps = 2000000},
        observer);

    num::plt::plot(xz);
    num::plt::title("Lorenz attractor (sigma=10, rho=28, beta=8/3)");
    num::plt::xlabel("x");
    num::plt::ylabel("z");
    num::plt::savefig("lorenz.png");
}
Lorenz attractor phase portrait (x vs z)
Lorenz attractor — adaptive RK45 via num::solve + num::RK45
#include <numerics.hpp>

#include <cmath>
#include <numbers>

int main() {
    constexpr int N = 1024;
    constexpr double fs = 1000.0;
    constexpr double pi = std::numbers::pi;

    num::Vector x(N);
    for (int i = 0; i < N; ++i)
        x[i] = std::sin(2.0 * pi * 50.0 * i / fs)
             + 0.5 * std::sin(2.0 * pi * 120.0 * i / fs);

    num::CVector X(N / 2 + 1);
    num::spectral::rfft(x, X);

    num::Series signal, spectrum;
    for (int i = 0; i < 100; ++i)
        signal.store(i / fs * 1000.0, x[i]);
    for (num::idx k = 0; k < X.size(); ++k)
        spectrum.store(k * fs / N, std::abs(X[k]));

    num::plt::subplot(2, 1);
    num::plt::plot(signal);
    num::plt::title("Signal: 50 Hz + 0.5 * 120 Hz");
    num::plt::xlabel("time (ms)");  num::plt::ylabel("amplitude");

    num::plt::next();
    num::plt::plot(spectrum);
    num::plt::title("FFT power spectrum");
    num::plt::xlabel("frequency (Hz)");  num::plt::ylabel("|X[k]|");

    num::plt::savefig("fft_demo.png");
}
FFT power spectrum
FFT power spectrumnum::spectral::rfft; peaks at 50 Hz and 120 Hz
#include <numerics.hpp>

#include <algorithm>
#include <cmath>
#include <numeric>
#include <random>
#include <vector>

int main() {
    constexpr int N = 64;
    constexpr int NN = N * N;

    num::PBCLattice2D nbr(N);
    std::vector<double> spins(NN);
    std::mt19937 rng(42);

    num::Series curve;
    for (double T = 0.5; T <= 4.01; T += 0.1) {
        std::fill(spins.begin(), spins.end(), 1.0);
        double beta = 1.0 / T;

        auto accept = [&](int i) {
            double ns = spins[nbr.up[i]] + spins[nbr.dn[i]]
                      + spins[nbr.lt[i]] + spins[nbr.rt[i]];
            return num::markov::boltzmann_accept(2.0*spins[i]*ns, beta);
        };
        auto flip    = [&](int i) { spins[i] = -spins[i]; };
        auto measure = [&]() {
            return std::abs(std::accumulate(spins.begin(), spins.end(), 0.0) / NN);
        };

        double m = num::solve(
            num::MCMCProblem{accept, flip, NN},
            num::Metropolis{.equilibration = 2000, .measurements = 500},
            measure, rng);

        curve.store(T, m);
    }
    num::plt::plot(curve);
    num::plt::xlabel("T");  num::plt::ylabel("|<m>|");
    num::plt::savefig("ising_demo.png");
}
Ising magnetization vs temperature
Ising phase transitionnum::solve + num::Metropolis; |m| drops at Tc ≈ 2.27
#include <numerics.hpp>

#include <string>

int main() {
    constexpr int N = 64;
    constexpr double kappa = 1.0, sigma = 0.06, t_end = 0.012;
    constexpr double h = 1.0 / (N + 1);
    constexpr double dt = 4.0 * h * h;

    const int nstep = static_cast<int>(t_end / dt) + 1;
    const double coeff = kappa * dt / (h * h);

    num::Grid2D grid{N, h};
    num::SparseMatrix A = num::pde::backward_euler_matrix(grid, coeff);
    num::operators::SparseOp Aop(A);
    num::LinearSolver solver = [&](const num::Vector& rhs, num::Vector& x) {
        return num::cg(Aop, rhs, x, 1e-8);
    };

    num::ScalarField2D u0(grid, [=](double x, double y) {
        return num::gaussian2d(x, y, 0.5, 0.5, sigma);
    });
    num::ScalarField2D u = u0;
    num::solve(u, num::BackwardEuler{.solver = solver, .dt = dt, .nstep = nstep});

    num::plt::subplot(1, 2);
    num::plt::heatmap(u0); num::plt::title("t = 0");
    num::plt::next();
    num::plt::heatmap(u); num::plt::title("t = " + std::to_string(t_end));
    num::plt::savefig("heat_demo.png");
}
2D heat equation — Gaussian spreading
2D heat equation — backward Euler with sparse CG operator; num::solve + num::BackwardEuler

Numerical Examples Built with numerics and Raylib

2D SPH Fluid — dam-break, hot/cold spheres, heat transfer, falling rigid body
3D SPH Fluid — dual temperature hoses, free-orbit camera
Navier-Stokes — Kelvin-Helmholtz instability, particle tracers
Electromagnetism — FEM potential solve, B-field, orbiting dipole
Schrödinger Equation — double-slit diffraction, Strang splitting
Quantum Circuits — Bell, GHZ, Grover, teleportation, QFT₃
Ising Nucleation — free-energy barrier crossing (F=0.1, T<Tc)
Ising Ordering — spontaneous symmetry breaking from random init
Galaxy Collapse — 50 random bodies under gravity; bodies merge on contact, growing in mass and radius until a dominant body with moons remains. Integrated with symplectic velocity Verlet.

Routine Families

kernel

BLAS-style vector operations, reductions, triangular solves, modified Gram-Schmidt, and Arnoldi steps.

core

Dense vectors and matrices, CSR sparse matrices, band storage, matvec, matmul, dot, axpy, scale, and norms.

fields

Uniform 2D/3D grids, scalar fields, vector fields, coordinate access, flat indexing, and trilinear sampling.

solve

Problem drivers for ODE integration and Monte Carlo sampling: RK45, BackwardEuler, Metropolis, and related tags.

factorization

LU with pivoting, QR, Thomas tridiagonal solve, and banded direct solves; LAPACK/LAPACKE paths where enabled.

solvers

CG for SPD systems, restarted GMRES for nonsymmetric systems, and stationary Jacobi/Gauss-Seidel iterations.

eigen

Power, inverse and Rayleigh iterations, Lanczos projection, Ritz values, and dense symmetric Jacobi sweeps.

operator

Operator arguments for \(y \leftarrow Ax\): dense, sparse, and matrix-free forms used by CG, GMRES, Lanczos, and expv.

svd

Full singular value decomposition and randomized truncated SVD.

spectral

Complex FFT/IFFT, real FFT, radix-2 plans, and optional FFTW3 execution.

analysis

Quadrature (trapz, Simpson, Gauss-Legendre, Romberg) and root-finding (bisection, Newton, Brent).

markov

Metropolis sweeps, precomputed Boltzmann probabilities, umbrella windows, and acceptance statistics.

stats

Welford online mean/variance, fixed-bin histograms, WHAM reweighting, and integrated autocorrelation time.

meshfree

Linked-cell neighbor search, Verlet lists with skin radius, periodic lattices, and SPH kernels.

pde

Finite-difference stencils, periodic/Dirichlet Laplacians, backward Euler matrices, ADI diffusion, and DST Poisson solves.

ode

Euler, RK4, adaptive RK45, velocity Verlet, Yoshida4, and backward Euler steps.

applications

SPH, Navier-Stokes projection, TDSE splitting, magnetostatics, Ising Monte Carlo, and N-body gravity.

Implementation Notes and Examples

cache blocking

Backend::blocked dispatch, cache-tiled matmul loop structure, implementation file, and benchmark command.

register blocking

Scalar micro-kernel shape, diagnostic entry point, and the transition from cache blocking to SIMD.

SIMD vectorization

Backend::simd dispatch, optimized dense-kernel locations, FFT SIMD/FFTW backend examples.

NS performance

Matrix-free pressure operator, CG backend selection, and projection-step code pattern.

stencil / HOF

Higher-order grid functions: 2D/3D Laplacians, fiber sweeps for ADI splitting, gradient, divergence, curl on Grid3D.

3D fields & solvers

ScalarField3D, VectorField3D, Poisson solves, gradients, divergence, curl, and \(\mathbf{B}=\nabla\times\mathbf{A}\).

SPH kernels

Cubic spline density kernel, Morris viscosity Laplacian, and spiky pressure gradient in 2D/3D.

PBC lattice

PBCLattice2D: precomputed periodic-boundary neighbor arrays for 2D square lattices; eliminates modulo in hot paths.

connected components

BFS connected-component labeling on square lattices; largest-cluster statistics for Ising nucleation.

Boltzmann table

boltzmann_accept and make_boltzmann_table: precompute min(1,exp(-βΔE)) for discrete energy sets.

FFT

Real signal spectrum, complex round trip, reusable FFT plans, explicit FFT backend calls.

PDE routines

Explicit diffusion, backward Euler sparse CG, ADI diffusion, and Poisson solve examples.

Poisson math

Dirichlet Poisson setup, manufactured sine solution, DST solve, finite-difference reference.

operator math

Storage concepts, dense/sparse/callable operators, CG, GMRES, Lanczos, and expv examples.

Detailed algorithm theory belongs in the companion scientific-computing notes; this site focuses on numerics API examples and implementation notes.

Benchmarks

Generated by cmake --build build --target report. Includes build metadata, test results, and throughput tables by routine family.

View Report →