numax: A lot of new features, closing the numpy + scipy APIs parity and improvements landed in v0.2.0, notably:
0. Every routine takes an owned Tensor or a borrowed View of one, so factoring a sub-block costs no copy.
from numax.core.tensorlike import View
from numax.linalg import cholesky, det, solve
var block = View(big.view().tile[2, 2](0, 0), big.context())
print(cholesky(block)) # factors the quadrant in place, no copy
print(det(block))
1. Full eigendecomposition, SVD, and Schur form now run on Tensor, device-resident, through MAX’s GEMM.
No more copying into a small Array to factor a matrix; it stays on the GPU the whole way.
from numax.linalg import eigh
from numax.prelude import *
def main() raises:
var A = eye[f64, 4]() + ones[f64, 4, 4]()
var result = eigh(A) # blocked, device-resident
print(result.values) # ascending eigenvalues
print(result.vectors) # orthonormal columns
2. The same call differentiates, with no adjoint rule written anywhere.
eigh, cholesky, solve, qr are written once against a numeric trait; instantiate with Dual instead of Plain and the derivative falls out of the algorithm itself.
from numax.prelude import *
def ground_state[T: FloatLike](w: T) -> T:
var H = build_hamiltonian(w) # any FloatLike kernel
return eigh[T, 24](H)[0][0] # lowest eigenvalue
print(ground_state(Plain[f64].constant(1.0))) # the value
print(ground_state(Dual[Plain[f64]].seed(1.0)).deriv) # dE/dw, exactly
3. Batch thousands of small linear-algebra problems, one per GPU thread, from a single launch.
map_blocks hands each SIMD lane or GPU thread a whole factorization instead of one scalar.
from numax.core.tensor import map_blocks
from numax.linalg.array import cholesky_solve
def solve_step(problem: Array[Plain[f32], k_in]) -> Array[Plain[f32], k_out]:
return cholesky_solve(problem) # runs inside one lane
map_blocks[step=solve_step, gpu=True](device_a.view(), device_x.view(), gpu)
# 4096 independent 4x4 SPD solves, one launch