Skip to main content
DateOct 12, 2026·Versionv0.10.0-alpha.1·AI usageYes

ADR 0010 - LAPACK interface layer: call order, buffer reuse, output layout

1. Context​

Above the raw LAPACKE translation layer (ADR-0009) sits the LAPACK interface layer: the builders in rstsr-blas-traits/src/lapack_{solve,eigh,svd}/. A builder receives rstsr tensors - arbitrary layouts and ownership, plus a device - and drives an XxxDriverAPI implementation whose signature takes raw pointers, one FlagOrder, and dimensions.

This layer decides everything the raw driver cannot: which single order the whole call runs in, how each tensor becomes a pointer and a leading dimension, where outputs are allocated, and how a raw info becomes an rstsr error. It answers the same questions scipy answers with a copy-to-Fortran arena and its overwrite_* fast path, but per call and on the caller's own layouts.

The BLAS layer (src/blas3/ - gemm, trsm, syhemm) keeps a different contract: its routines take user-visible out= buffers and transposition flags, so a row-major call is served by rewriting the algebra - swapping operands, flipping trans/uplo/side flags, reversing axes - in the spirit of CBLAS but per argument, letting c-prefer and f-prefer inputs mix freely. This ADR does not govern that layer.

A LAPACK routine admits no such rewrite: factorizations take no per-argument transposition flags and overwrite their inputs. The LAPACK interface layer therefore specifies one order and works at that order - every tensor is converted to it, and the driver's layout branch (ADR-0009) supplies the row-major adaptation as transposed temporaries.

2. Decision​

2.1 1. One call order per call​

Each call runs in a single order, chosen once by, in priority order:

  1. explicit output buffers (out=), if present - the output's own order wins outright;
  2. the matrix inputs' layout preferences - a c-prefer input elects RowMajor, an f-prefer input elects ColMajor;
  3. otherwise the device default.

Ambiguous inputs are neutral in stage 2: size-0 and single-row/column matrices report both preferences at once, neither-prefer reports none, and neither elects an order. The mechanics (first-class slots, votes, the exactly-one-prefer rule) are util_layout::get_output_order's documented contract.

The driver takes one order for every matrix of the call. For the single-matrix-input routines built so far (gesvd, gesdd, ...) this reduces to: f-prefer input → ColMajor, c-prefer → RowMajor, otherwise the device default.

2.2 2. Input conversion​

  • f-prefer inputs pass zero-copy by raw pointer, with the leading dimension derived from the strides (it need not equal any dimension).
  • c-prefer and fully non-contiguous inputs are cloned into the call order. Fully non-contiguous support is an rstsr extension: LAPACKE itself admits only the two contiguous-preferred forms.
  • Routines that overwrite their input (factorizations, in-place solves) clone-or-consume regardless of contiguity. An owned buffer already in the call order is written through in place; a view - or an owned buffer in the other order - is materialized first. A zero-copy f-prefer pass is therefore available only to an owned input of an overwriting routine.
  • 1-D arguments (ipiv, tau, s, w) carry no layout; they are allocated, or made contiguous, before pointerizing.

2.3 3. Output layout​

  • Fresh outputs are allocated in the call order. For a prefer input the output strides follow the input layout. Values are unchanged; only the strides differ, and only for minority-layout inputs.
  • In-place / overwritable outputs reuse the input buffer and its order - the scipy overwrite_* analogue. This maximally utilizes an f-prefer mutable input: no bridge copy and no extra allocation, even on a row-major device.
  • Zero-size results (min(m,n) == 0) are synthesized in the builder in the call order - identity sub-blocks for the requested factors, empty vectors for the rest - and the driver is not called.

2.4 4. Error mapping​

info == 0 is success. info < 0 is an illegal argument - an internal bug, since the builder validates every argument before the call - reported with its index. info > 0 is a routine-specific failure. The tier-internal sentinels -1010 and -1011 (ADR-0009 §6) decode to MemoryError. All mapping goes through util_error::map_lapack_info.

2.5 5. 2-D only​

Builders at this layer are 2-D. N-dimensional (stacked) linalg lives at a higher tier (the faer device), not here.

3. Considered​

  • Fresh outputs in the device default order - LAPACK's own convention, and numpy/scipy's default. Rejected: the driver runs in one order, so device-default outputs would force either a post-call output bridge or a wasted input clone, forfeiting the zero-copy f-prefer pass.
  • A scipy-style single-arena copy to Fortran. Rejected: it treats every input alike and discards the f-prefer zero-copy path.
  • Never reuse the caller's buffer (copy-on-write for all outputs). Rejected: buffer reuse for owned inputs is the point of the overwrite_* interface.

4. Consequences​

  • A caller's f-prefer buffer can reach LAPACK untouched; its transposed twin is never materialized.
  • Output strides depend on the input layout, so a test must compare values, not strides.
  • The builder carries layout, ownership, and error policy only; all FFI and workspace mechanics stay in the driver tier (ADR-0009).