Skip to content
This repository was archived by the owner on Aug 20, 2026. It is now read-only.
This repository was archived by the owner on Aug 20, 2026. It is now read-only.

[teeny] Phase B2: posdef entry points take teeny carriers — delete the _any shared-size reconstruction #62

Description

@balbasty

Agent:claude-fastfields-to-teeny

Part of the tensor-native-boundaries umbrella (fastfields-lib#57), Phase B. One coordinated PR-set with the cpu-lib half (fastfields-cpu-lib "Phase B2" issue); this issue covers the impl repo (posdef.h). Convention: TEENY-MIGRATION.md §9 — R2 spellings at.ndim / at.size(at.ndim - 1).

Tier-1 status confirmed (no kernel work here):kernels/posdef/matrix.h is already tensor-native — matvec(Ov&& o, const Hv& h, const Xv& x) per packed layout, matvec_backward(Hv&&, const Xv&, const Yv&), solve_/solve_w_/invert_w_ all take teeny views. This issue is the impl-entry re-skin ONLY, the same shape distance_l1/euclidean went through in Phase A.

Current signatures (exact; 8 entry points)

matvec / addmatvec_ / submatvec_ (via _matvec), sym_matvec_backward, solve, solve_, sym_invert, sym_invert_:

template <type Ty, int C, typenamereduce_t, typenamescalar_t, typenameoffset_t>
voidmatvec(offset_t nbatch, offset_t nchannel, scalar_t* out,
constscalar_t* hes, constscalar_t* inp, constoffset_t* size,
constoffset_t* stride_out, constoffset_t* stride_hes, constoffset_t* stride_inp);
template <type Ty, typenamereduce_t, typenamescalar_t, typenameoffset_t>
voidsolve(offset_t nbatch, offset_t nchannel, scalar_t* out, constscalar_t* inp,
constscalar_t* hes, constscalar_t* wgt, constoffset_t* size,
constoffset_t* stride_out, constoffset_t* stride_inp,
constoffset_t* stride_hes, constoffset_t* stride_wgt); // wgt may be nullptr

Every body opens with the _any helper — the textbook R3 hazard instance §9.2 cites: ONE shared size[] for all operands, each carrier rebuilt from the shared batch dims + a per-tensor trailing extent (nchannel vs CC), because vectors trail C while the packed hessian trails C(C+1)/2:

template <typename T, typenameoffset_t>
staticinlineauto_any(T* p, constoffset_t* size, offset_t nbatch,
offset_t trailing, constoffset_t* stride)
{
std::vector<offset_t> sz(size, size + nbatch);
sz.push_back(trailing);
returntny::as_anyrank(p, sz.data(), stride, static_cast<int>(nbatch + 1), tny::copy_meta);
}

With carriers built lib-side from each DLTensor's OWN shape (from_dlpack), the reconstruction — including its per-call std::vector allocation — disappears entirely. (The lib's CHECK_SAME_BATCH/CHECK_RANK/CHECK_SAME validation already guarantees the geometry _any was re-deriving.)

Target

template <type Ty, wr W, int C, typenamereduce_t, classAO, classAH, classAI>
staticvoid_matvec(AO ao, constAH ah, constAI ai)
// offset_t = decltype(ao.size(0)); nchannel = ao.size(ao.ndim - 1) (R2)// static_assert the three carriers' element types agree (Phase A's one-line// mismatch report, instead of a template error inside the kernel)
template <type Ty, typename reduce_t, class AO, class AI, class AH>
void solve(AO ao, constAI ai, constAH ah); // no weighttemplate <type Ty, typenamereduce_t, classAO, classAI, classAH, classAW>
voidsolve(AO ao, constAI ai, constAH ah, constAW aw); // with weight
  • nbatch, nchannel, size[], and every stride_*[] argument disappear (R2/R3). CC for validation/recast still comes from _packed/_packed_s (kept — the C > 0 static recast(tny::shape<C>{}) fast path is untouched).
  • The optional weight becomes overloads, not a null carrier: _dispatch_solve already has exactly these two arities; the lib's has_wgt branch picks the overload. A "null-data carrier" sentinel would smuggle the pointer-era convention through the tensor boundary.
  • Read-only operands (ah, ai, aw, ag) are const-element carriers (R4).
  • The per-thread Cholesky workspace (std::vector<reduce_t> b + tny::wrap(b.data(), shape<-1,-1>{nc,nc})) and the peel_front_at<-1> voxel loop are unchanged — only the prologue moves out.

Scope

posdef.h only, all 8 entry points (matvec, addmatvec_, submatvec_, sym_matvec_backward, solve, solve_, sym_invert, sym_invert_). NOT kernels/posdef/* (already tier-1 tensor-native). NOT cuda-impl (its own Phase B issue — the cuda twin keeps its signature until then; separate files, nothing breaks). Old signatures deleted, not deprecated — cpu-lib is the only caller and converts in the same PR-set.

Gate (Phase A's, restated — the PR-set merges only if ALL hold)

  1. Untouched kernel-layer instantiations (kernels/posdef/matrix.h: eye/diag/estatics/sym/full::matvec, sym::matvec_backward, solve_/solve_w_/invert_w_) byte-identical at -O2 wherever they exist as standalone symbols in cpu-lib's posdef.o, both compilers.
  2. Re-skinned driver loops (parallel_for bodies): no new instructions inside the per-voxel loop; anything added is explained or eliminated before merge. Note one expected prologue-side improvement: _any's heap std::vector construction per entry disappears — that is outside the loop and is the point.
  3. Oracle: test_posdef5092 checks (clang make test baseline, 2026-08-04), identical counts clang++ AND g++, true make clean between; whole suite green, unchanged counts; asan/ubsan on touched paths.
  4. g++ nuance (Phase A finding): whole-TU inlining jitter in untouched neighboring symbols under g++ is acceptable; the g++ bar is kernels + driver loops + oracles.

Judgment call to document in the PR

Whether _matvec's if constexpr (C > 0) static-recast arms keep nchannel folded from C (as today) — they should; the dynamic arm alone reads the carrier. Confirm the recast still folds to the same code (gate 1 covers the kernels; the recast itself is driver-side, gate 2).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions