Matrix Multiply

Scheme::matmul records a semantic GEMM/GEMV. The task graph schedules it from buffer bindings like any other node. The backend chooses an implementation on the first submit and retains that plan.

The tensor front end (goldy/tensor, on by default) derives m/n/k, transpose flags, offsets, and leading dimensions from checked TensorViews and records the same node. MatMulView remains the low-level escape hatch.

#![allow(unused)]
fn main() {
scheme
    .matmul("q_projection", MatMulDesc::gemv(dim, dim))
    .a(&weights, MatMulView::offset(wq))
    .b(&xb, MatMulView::packed())
    .out(&q, MatMulView::packed())
    .record();
}

The contract is row-major C[m, n] = alpha * op(A)[m, k] @ op(B)[k, n] + beta * C. The first slice is FP32 with alpha = 1, beta = 0 (the stdlib fallback requires those epilogue values; native libraries honor other alpha/beta).

Implementation choice

BackendDefaultOverride
CUDAcuBLAS (cublasSgemv when n = 1, otherwise cublasSgemm)GOLDY_MATMUL=fallback
MetalMetal Performance ShadersGOLDY_MATMUL=fallback
Vulkan, DX12, WebGPU, CPUGoldy stdlib kernel—

There is no public prepare(). The stdlib pipeline is compiled on first submit when the backend has no native library (or when fallback is forced). Subsequent clean submits reuse the realized command list / CUDA graph.

Custom leading dimensions are honored by native libraries. The stdlib kernel requires packed row-major storage (lda/ldb/ldc derived from m/n/k and the transpose flags).