Numerics extras
Scientific work defines VECMAT_USE_F64 before the include. -DVECMAT_USE_F64=ON does that for this build's tests and benchmarks; it does not drop name32.
GEMM
BLAS-style dense multiply C = alpha * op(A) * op(B) + beta * C. Row-major and column-major layouts.
vm_/gemm vm_/gemm_ ref vm_(optional bias and/or ReLU)gemm_ ex vm_/gemm_ batch vm_(shared-gemm_ strided_ batch Bpacks once)vm_unfolds an NCHW image into a GEMM-ready panelim2col
Packed 8×8 ukernel, TLS pack workspace MC=NC=KC=128, persistent thread pool. Cap threads with vm_ or VECMAT_GEMM_THREADS. fp16 / bf16 are not in this release. This is not a BLAS.
Contract: vm_ matches vm_ for trans / no-trans, row / col, odd sizes, beta ≠ 0, and batch shared-B, on a size band of about 32–512.
Column-major C = A * B for square panels:
vm_gemm(C, n, A, n, B, n, n, n, n, 1.0f, 0.0f, false, false, VM_LAYOUT_COL_MAJOR);
Dense linear algebra
Heap vm_ (M×N, column-major). LU with partial pivoting, Householder QR, thin one-sided Jacobi SVD, Cholesky. Scale-aware cutoffs tol = n * VECMAT_. Rank-deficient work returns false (an ok flag), not a silent NaN. Integer matNi_inverse is truncated, not modular inverse — the declaration says so.
vm_mat A = vm_mat_alloc(3, 3); vm_mat_set(&A, 0, 0, 2.0); vm_mat_set(&A, 1, 1, 2.0); vm_mat_set(&A, 2, 2, 2.0); vm_mat inv; bool ok = vm_mat_inverse(&inv, &A); vm_mat_free(&A); vm_mat_free(&inv);
3×3 factorizations
mat3_ returns false on a singular matrix (scale-aware cutoff). mat3_ is the analytical symmetric 3×3 eigensolve, eigenvalues sorted ascending, eigenvectors on request. mat3_ is the polar decomposition F = R U (Newton on C = Fᵀ F). A reflection is a failure.
Dense spectral primitives
vm_,matvec vm_— dense matvec (symmetric reads the lower triangle)symv vm_— Jacobi symmetric eigensolve, eigenvalues descending,syev Aoverwrittenvm_— unnormalized graph Laplacianlaplacian L = D − Avm_— thin wrapper for the leading k eigenpairssyev_ topk
Sparse systems
vm_— square CSR from tripletsspmat vm_,spmv vm_(SPD),cg vm_(nonsymmetric)bicgstab - Left preconditioners: Jacobi, SSOR (ω = 1), IC(0) (falls back to Jacobi on pivot breakdown)
vm_reportsksp_ info iters,rel_res,ok- Relative residual is
||r|| / max(||b||, ε)
Time integration and grid primitives
vm_,euler_ semi vm_,verlet vm_/rk2 vm_,rk4 vm_cfl_ dt vm_— symplectic Euler onrigid_ step (x, v, q, ω)- MAC operators and an assembled 5-/7-point Laplacian
Semi-implicit Euler on one particle. x, v, and a are length n.
vm_float_t x[3] = {0.0f, 1.0f, 0.0f}; vm_float_t v[3] = {1.0f, 0.0f, 0.0f}; const vm_float_t a[3] = {0.0f, -9.81f, 0.0f}; vm_euler_semi(x, v, a, 3, 1.0f / 60.0f);
Computer graphics (core)
Clip-space helpers
Build projection and view matrices for different graphics APIs and depth conventions.
Perspective projections — camera frustum matrices (radians; _deg if FOV is in degrees). The unsuffixed mat4_ / mat4_ / mat4_ helpers also take radians. Use mat4_ (and friends) for degrees:
mat4_/perspective_ clip mat4_perspective_ clip_ deg mat4_/perspective_ rh_ no mat4_perspective_ rh_ no_ deg mat4_/perspective_ rh_ zo mat4_perspective_ rh_ zo_ deg mat4_/perspective_ lh_ zo mat4_perspective_ lh_ zo_ deg mat4_/perspective_ lh_ no mat4_perspective_ lh_ no_ deg
Orthographic projections — parallel projection matrices from frustum bounds:
Look-at view matrices — world-to-view transforms from eye, target, and up:
Look-from-direction view matrices — the same basis as look-at, but the camera aims along a direction (FPS / fly camera, no target point):
mat4_/look_ from_ dir mat4_look_ from_ dir_ clip mat4_/look_ from_ dir_ rh mat4_look_ from_ dir_ lh quat_/look quat_— orientation whose local −Z (RH) or +Z (LH) aims along the directionlook_ clip quat_— shortest rotation taking one vector onto anotherfrom_ to
Infinite / reverse-Z projections — infinite far plane, optionally with reversed depth (near → 1, infinity → 0 on ZO):
mat4_stays historic OpenGLperspective_ infinite RH_NOmat4_— infinite and any clip convention (perspective_ infinite_ clip *_ZOis infinite and zero-to-one)mat4_— modern-engine preset: infinite + RH + ZO + reversed depthinfinite_ reverse_ z mat4_— same mapping for the other clip conventionsinfinite_ reverse_ z_ clip
Viewport, world ↔ window — NDC to a pixel box and back. Geometric vec3_ (onto a direction) is unchanged:
mat4_/viewport mat4_viewport_ depth vec3_/world_ to_ window vec3_window_ to_ world vec3_/world_ to_ window_ clip vec3_window_ to_ world_ clip
Affine inverse and normal matrices — skip the 4×4 adjugate when the transform is [A t; 0 1]:
mat4_— invert the 3×3 linear part and apply it to the translationinverse_ affine mat3_/normal mat4_— inverse-transpose of the 3×3 for transforming normalsnormal
Clip conventions — handedness + depth range selectors used by the *_clip helpers:
VM_— right-handed, clip z inCLIP_ RH_ NO [-1, 1](OpenGL-style)VM_— right-handed, clip z inCLIP_ RH_ ZO [0, 1](Vulkan-style)VM_— left-handed, clip z inCLIP_ LH_ ZO [0, 1](Direct3D-style)VM_— left-handed, clip z inCLIP_ LH_ NO [-1, 1]
A Vulkan-style camera is a projection times a view. Angles are radians; VM_ converts a degree literal.
const vector3 eye = vec3(0.0f, 1.6f, 4.0f); const vector3 at = vec3(0.0f, 1.0f, 0.0f); const vector3 up = vec3(0.0f, 1.0f, 0.0f); const matrix4 view = mat4_look_at_rh(eye, at, up); const matrix4 proj = mat4_perspective_rh_zo(VM_DEG(60.0), 16.0f / 9.0f, 0.1f, 200.0f); const matrix4 view_proj = mat4_mul(proj, view);
Rotation helpers
Build 4×4 rotation matrices from axis angles in radians (mat4_ / mat4_ / mat4_ / mat4_). Use *_deg or VM_ when the angle is in degrees: