The linear-algebra layer of the Tensor4all stack: the SVD, QR, Cholesky, LU and eigensolves that
tensor algorithms reduce to once a tensor has been unfolded into a batch of matrices. The name is t
for tensor plus linalg for linear algebra — read "tee-lin-alg" — and the t is the same one
that prefixes tcontract.
tlinalg is the numerical layer that tenferro's CPU linear algebra is being extracted into. It owns
the kernels and the batch/scheduling behaviour; the host owns tensors, allocation, dtype dispatch,
placement, execution context and error wrapping.
Tensor-free describes the interface, not the subject. No tensor type appears in it: every entry
point takes borrowed strided matrix batches — one call per batch over a rank-2+B descriptor — plus a
small vocabulary of its own (tcontract_exec::Exec, Scalar, Error, Op). That is what keeps the two
providers siblings with no shared trait crate, lets a host adapt them to the interface the host
defines, and lets the kernels be tested and measured on plain buffers. It is not a claim that this
library is about matrices instead of tensors.
| Crate | Role |
|---|---|
tlinalg |
The faer-backed provider, with the batch-direction lane fan-out forked over the caller's Exec token. |
tlinalg-blas |
The LAPACK/BLAS provider: vendor calls and their argument marshalling, a serial batch loop, vendor-owned threading. |
tlinalg-testkit |
Dev-only, unpublished: provider-neutral test helpers (scalar test trait, generators, reference arithmetic, batched layouts, counting allocator). |
tlinalg-parity |
Dev-only, unpublished: the same batched cases through both providers, compared gauge-aware. |
tlinalg-bench |
Unpublished: Criterion benchmarks of each provider's kernels, faer vs LAPACK. |
The two providers are siblings: neither depends on the other, and each owns its own vocabulary.
The dev-only crates depend on the providers, never the other way round (tlinalg-testkit is only
ever a [dev-dependencies] entry of a provider).
tlinalg is the faer-backed provider. It owns batched kernels (SVD, packed LU, Cholesky,
triangular solve, LU and solve, full-pivot LU, QR and column-pivoted QR, Hermitian and general
eigendecompositions, compact Householder QR), the batch loop and lane fan-out over them, and the
vocabulary its entry points take: borrowed strided I/O, one execution token (tcontract_exec::Exec)
and typed errors (Error). The batch route belongs to the library; no tensor types.
tlinalg-blas has the same batched shape without a parallelism token: LAPACK and BLAS own their
threading, the batch loop is serial, and scratch comes from a host Workspace, queried and acquired
once per call and reused for every item. It was developed as tensor4all/tlinalg-blas-rs and merged
here with its history.
The interface a host requires of its linear-algebra providers is defined by the host: tenferro owns it and adapts each provider to it.
Every entry point is batched, torch-style: one call per batch over a rank-2 + B strided
descriptor, with the library owning the batch loop and the lane fan-out. The contract is
docs/design/batched-api.md.
The crate documentation is the specification for the rest:
- numerical conventions and failure behaviour — crate root of
tlinalg; - the execution and budget contract —
tcontract_exec::Exec; - library-owned batch lanes on the shared work model and the accepted faer count-hint limitation —
docs/design/library-owned-lanes.md; - output assembly and lane scratch —
docs/design/batched-api.md; - the error vocabulary —
tlinalg::Error.
Extraction in progress. Nothing is published; publish = false is set deliberately until the
interface and the package names settle.
cargo fmt --all -- --check
cargo clippy -j 16 --workspace --all-targets --features tlinalg-blas/link-openblas,tlinalg-parity/link-openblas,tlinalg-bench/link-openblas -- -D warnings
cargo test -j 16 --workspace --features tlinalg-blas/link-openblas,tlinalg-parity/link-openblas
cargo test -j 16 --workspace --features tlinalg-blas/link-openblas,tlinalg-blas/provider-inject,tlinalg-parity/link-openblaslink-openblas exists only so tlinalg-blas has an executable check of its own; it builds OpenBLAS
from source through openblas-src. Tenferro selects the vendor and the injected-symbol path
itself, so the feature is not part of the implementation contract. Without it the workspace still
builds and lints; the LAPACK tests are skipped. tlinalg-parity/link-openblas turns on the
cross-provider parity suite, which needs both providers to run.
tlinalg-parity runs every family (SVD thin/full/values, Cholesky, triangular solve in all flag
combinations, LU, solve, full-pivot LU and its solve, QR, column-pivoted QR, eigh/eigvalsh,
eig/eigvals, packed LU factor/prepared/fused solve, and the compact Householder pair) through
both providers for f32, f64, Complex32 and Complex64, over batch shapes [], [3] and
[2, 3] (gapped, so no axis merges, and with transposed batch strides), plus a stride-0 broadcast
coefficient for the binary families. What is unique is compared directly (singular values,
eigenvalues as a multiset, Cholesky factors, solutions, permutation-convention LU factors, QR factors
after fixing the R diagonal phase); the rest is checked by reconstruction against each
provider's documented convention.
tlinalg-bench measures the ordinary single-token batched entry points directly: compact inputs,
output vectors reused across iterations, and a recycling
LAPACK workspace, so steady-state allocation is not what is timed.
# Everything (faer rows only without the feature):
OPENBLAS_NUM_THREADS=1 cargo bench -p tlinalg-bench --features link-openblas
# One family or case (Criterion filter on "family/dtype/row/case"):
OPENBLAS_NUM_THREADS=1 cargo bench -p tlinalg-bench --features link-openblas -- '^eigh/f64/.*/n4xb1024$'Groups are family/dtype (f64 everywhere, c64 for a representative subset); cases are
n{n}xb{batch} (n x n matrices, batch of them: n = 2, 4, 8 with batch = 1, 3, 4, 8, 1024,
and n = 32, 128, 512 single matrices) and t64x24 (a tall matrix, SVD and QR). Each case has up
to three rows:
faer-1lane— one sequential lane: per-item cost plus the batch loop, no threading.faer-{N}t— an explicitN-member team and requested budget (TLINALG_BENCH_THREADS, default the available parallelism); tlinalg'sAutoroute forks the budget over outer lanes and hands each lane's share to faer as its width.TLINALG_BENCH_STRATEGY=auto|sequential|outer|inner|split:Nforces a route. UseTLINALG_BENCH_THREADS=1for overhead baselines. The faer width is not a hard active-thread limit; the token bounds the lanes it starts.lapack—tlinalg-blas(with--features link-openblas): a serial batch loop, vendor-owned threading.
Read faer-1lane against lapack with OPENBLAS_NUM_THREADS=1 for a like-for-like kernel
comparison; with OpenBLAS's default thread count, small LAPACK calls pay its threading overhead,
which says more about the vendor configuration than the kernel. faer-{N}t against faer-1lane
shows what lane fan-out buys for a batch and what intra-item parallelism costs or buys for one
matrix. These are kernel numbers only: tensor construction, dtype dispatch, session entry and pool
checkout belong to tenferro, and integrated route-level performance lives in tenferro-benchmark.
MIT OR Apache-2.0.