Distributed tensor descriptors and completion ADR¶
Date: 2 November 2026. Owner: TENS-11 (#804). Status: design contract with a bounded CPU descriptor/completion implementation and an opt-in W=1 adapter to #820 byte transfers, described in the source records below. No GPU integration, physical-consumer scientific acceptance or scaling result is delivered here. #804 stays open. This refines the architecture, semantic contract, CPU ADR and stream ADR. #820 owns MPI collective, thread and error mechanics; #803 owns GPU transport; #819 owns measurement records.
Original fake CPU source record¶
distributed.hpp implements the
next local increment using the existing Budget, Domain, View, Lease and
CompensatedPairPlan. It introduces no production dispatch, MPI import/call,
GPU operation, resource ledger or dependency. The original design below remains
the wider contract; not every proposed interface is implemented.
Implemented type |
Current capability and limit |
|---|---|
|
Stable namespace/kind/serial/revision sidecars, ordered ranges and parent coordinates; FP64/complex descriptors, at most eight axes, one parent per axis and selected maps of at most 16 coordinates. Larger pages/deeper dependencies refuse. |
|
Method-authenticated stable/native token mapping, at most 32 tokens and eight tensor definitions. Conflicting revisions, ordering, parents, source, type or shapes refuse. Binding retains its registry generation, checked View and captured native value version. No eviction/reminting or global directory. |
|
One declared complex binary row contraction, output block, value/placement epoch, deterministic input owner/executor functions and ordered reduction IDs. Fixed transpose/adjoint choice, beta zero, explicit finite scalar-product-plus-visit work limit. No planner search or cache dependency. |
|
One active contribution slot. Separate producer-ready, receive, send, consumer and release-receipt gates. Input read leases exclude writes until the slot retires. Future destruction requests failure but cannot release executor-owned active state. |
|
Fixed native fake envelope compares the full expected contribution, epoch, generation, sender, receiver and size. Payload uses canonical real/imaginary binary64 bytes and CRC-32/ISO-HDLC. Portable descriptor/header serialization is supplied by the subsequent CPU adapter record below. |
|
Single successful private-output publication; retains immutable result storage, descriptor and registry after executor/input teardown. Failed, duplicate, missing or incomplete work exposes no new result. Existing published handles are unaffected. |
The method must authenticate native source/domain owners before registration; these APIs do not manufacture a physical-source certificate. The CPU executor accepts only retained owned inputs. A synchronous borrow, including a still-live borrow, is refused for delayed use; callers must make a separately admitted owned copy before binding. FP64 descriptors can bind, but this registered executor is complex FP64 only. General ragged arithmetic is not implemented: bounded ragged metadata binding is covered, while the arithmetic fixture is rectangular.
All executor gates and registry mutation use one caller thread. Future failure
requests are atomic; that does not make executor access or destruction concurrent.
The fake executor has no background work: explicit drain_fake() or executor
destruction can settle its simulated activity synchronously. This is deliberately
not an MPI/device cancellation policy. A future cannot be used to release pending
leases; production adapters must use their actual completion and termination rules.
Retirement after failure or explicit fake drain releases workspace; successful
publication transfers result storage and releases the other slots. Fixed terminal
state/registry control remains charged while executor/future handles retain it.
Admission and fixture evidence¶
FakeExecutor::admission_bytes validates the operation and computes its complete
fixed reservation before any input read. Constructor failure unwinds all partial
reservations. For N = left_extent * right_extent, the additional census is:
sizeof(executor State) + sizeof(Published) + stream_control_bytes
+ 5 * sizeof(Allocation) + 96 * N
The five complex allocations are producer sum/correction, received values and private output sum/correction. Canonical send bytes add 16N bytes to their 80N total. This is an explicit alternative to the earlier illustrative fixture census: owned input views are retained instead of copying L/R rows, and the send buffer is canonical bytes rather than a second complex array. Existing inputs, registry, bindings and views stay charged once in their original inventory. The conservative execution-control allowance is temporarily replaced by the registered kernel’s actual View/IndexProgram reservations on the same Budget, then restored when those temporary objects die. There are no callbacks or concurrent executor entry during that synchronous exchange. This is a tracked requested-byte ceiling, not process RSS; shared-pointer allocator overhead and external runtime allocations retain the core ledger’s documented exclusions.
Focused current-header checks in the native fixture, driven by test_tensor_distributed.py, cover:
Both exact ADR matrices using an independent integer-pair oracle; simulated 1/2/3/4-rank ownership, uneven/empty layouts and shifted executor placement.
Registry conflicts, revisions, selected/dependent/empty domains, metadata caps, overflow, foreign registry/source binding, stale native values and owned-input admission. Binary64 byte order and CRC use independent known answers.
Delayed producer, send, receive, consumer and receipt; write exclusion, dropped futures, retained owners and published result lifetime. Both send-before-consume and consume/receipt-before-send orders retain the slot correctly.
Duplicate, missing/out-of-order, stale epoch/generation, corrupted length/payload, provider/nonfinite failure and cancellation; no failed result escapes.
A precomputed exact cap succeeds; one byte below refuses without input reads. Successful output ownership and failure teardown return tracked reservations to their expected retained baselines. A 10,003-contribution stream has the same tracked peak as three contributions, without an all-contribution ID set.
Eight new source cases and four existing stream cases pass. The eight new cases also pass AddressSanitizer and UndefinedBehaviorSanitizer. The harness compiles these current C++17 headers directly; it neither imports the installed QC native extension nor links MPI/GPU libraries. No chemistry, full CI or scaling run was performed. New test classification is generated from the curated T2 entry. This internal capability has no new user-facing method or release fragment.
Logical ranks in this fake fixture are placement labels within one process and one execution ledger. These checks do not establish separate-process transport, per-rank/node collective admission, empty-rank collective participation, or MPI failure recovery. Those are the next integration tests, not claimed results here. #819 retains measurement-record ownership. #804 remains open.
Inspected capabilities and boundaries¶
Original design baseline: 9588b923f3391fb12249fc7e1796e7a5c969f96e. Issue descriptions
and latest notes for #795-#800, #803, #804 and #820 were inspected. Rechecked
incoming main e8e457fe, including #820 landing 3ba7b301, during drafting.
The unrelated evaluated-state bridge changes do not alter these tensor contracts.
That historical snapshot preceded #800 landing 508b8765; the fake CPU slice
inspected that incoming implementation but needs no schedule cache. Consume
concrete capabilities without requiring whole-issue closure.
The following table records the original design baseline. Current additions are in the CPU source record and next-step section, including the newer #820 tickets.
Existing type or implementation |
Capability at original baseline |
Capability then missing |
|---|---|---|
core.hpp: |
Process-local generated identity/revision; affine and selected ordered coordinates; one parent space/coordinate; runtime-rank views |
Portable identity binding, canonical block enumeration and distributed owner map (#795/#804) |
|
Checked addresses, FP64/complex FP64, explicit conjugation, local value versions, synchronous exclusion and imported inventory |
Retained asynchronous lifetime and remote lease receipts (#797/#804); a borrow cannot be made safe merely by retaining its weak token |
|
One shared local tracked-byte ledger, bounded arrays, overflow checks |
Admission bundle and retained ticket reservations using this ledger, plus externally admitted node/device inventories; no new ledger |
|
Checked binary indexed operation and local synchronous execution |
Distributed contribution enumeration/publication; no general multi-expression DAG assumed (#796) |
ri_j.hpp: |
Replayable resident FP64 panels, exact-zero versus unavailable state, private synchronous result |
Remote complex providers and completion-safe request lifetime; not an existing network provider |
stream.hpp: |
Invocation-scoped authenticated source/metric/frame tokens, ordered visit counts, rectangular complex compensated CPU updates |
Portable source binding, remote receipts, duplicate accounting and asynchronous completion; count alone does not prove distributed coverage |
mpi.py: |
Optional activation, main/initializing-thread checks, FUNNELED minimum; #820 fixed admission and one completed float64 sum on borrowed COMM_WORLD |
Paired point-to-point transfer slots and release receipts remain missing; existing |
#798/#799 source slices supply the local arithmetic and lifetime foundation. Their broader physical acceptance remains separate. Physical source, gauge, metric, selection, screening and work limits remain method-owned. A descriptor neither authenticates chemistry from dimensions nor authorizes a new approximation.
Decision: identity is independent of placement¶
The following are proposed extension records, not a new public ABI. Integers
on the wire have specified unsigned widths, checked conversions and no wrap.
Use a versioned, length-delimited canonical encoding, not native struct bytes.
Version 1 uses network byte order, a 128-bit namespace, 64-bit object serials,
revisions, coordinates and lengths, and a 16-bit kind/version tag. Zero IDs are
invalid; counters refuse overflow. Phase IDs/epochs additionally fit #820 signed
int64 and payload counts its supported MPI count width. The bounded first
codec admits at most 8 axes, 8 parent references, a 4096-byte header and one
contiguous payload fragment per ticket. Reject larger descriptors, rather than
truncate. Payload integrity uses CRC-32/ISO-HDLC over the canonical bytes; it is
an accidental-corruption check, not authentication. Identity uses full fields.
Reject unsupported schema versions, excessive lengths, count/byte overflow and
unknown dtype before payload allocation. Complex FP64 wire values are ordered
IEEE binary64 real/imaginary pairs with declared byte order; no assumption about
std::complex or MPI datatype layout crosses the interface. Local affine strides
are reconstructed and checked through View, never transmitted pointers.
Proposed record/field |
Meaning and validation |
Existing anchor / minimal extension |
|---|---|---|
|
Opaque run-manifest namespace and monotone serial, unique per tensor, domain, source, metric, frame or operation kind; never rank, pointer or display name |
Add adapter registry binding IDs to existing |
|
Ordered parent domain IDs/revisions and global parent coordinates, plus affine |
Bind one-parent cases to |
|
Ordered axis domain references and logical representation; rectangular extents are independent; no shape-based source equivalence |
Portable sidecar for |
|
Canonical half-open ranges in each ordered global axis, including ragged parent coordinates; excludes rank, local tile ordinal, address and placement epoch |
New sidecar key; ranges map to |
|
Immutable logical value version; full source/metric/frame/domain revisions must match the descriptor |
Bridge to |
|
Exactly one authoritative owner per value key in the phase group; function or bounded directory page; read-only replicas are separately admitted |
Extension at provider/plan boundary, reusing |
|
Meaning, placement and finite expected contributions fixed before launch |
Extend |
|
One canonical reduction chunk, independent of executing rank or retry |
Bounded ordered accounting alongside |
|
Bounded envelope with exact expected size, canonical metadata and payload integrity check; sender/receiver must match admission |
New provider completion record retaining |
|
Owner-side read exclusion plus borrower-side immutable replica; one declared final-consumer receipt |
Extend local lease semantics, not raw remote pointers |
|
Owner-only private result becomes readable exactly once after all dependencies succeed |
Extend private |
The architecture’s source-epoch-qualified block lookup is the pair
GlobalBlockKey plus ValueRef, rather than embedding mutable epochs in the
structural key. Neither key alone authorizes access.
The coordinator supplies IDs through a compact manifest; each rank interns only
its bounded working set into local Space objects. Each live ID/revision has
one interned local token retained by every referencing view, plan and lease;
eviction cannot mint a second token while the first is live. Retiring an entire
binding generation invalidates plans before re-interning. Native source adapters must
validate their real owners before binding the manifest. Duplicate ID with
different semantics is an error; a hash is an integrity aid, not scientific
identity or collision-free equality. Manifest identity persists across rank-count
changes if retained; durable restart/manifest storage belongs to #826. A fresh
run has a fresh namespace unless that manifest is explicitly restored.
Axis ranges refer to ordinals in the pinned global ordered domain; the domain map resolves them to scientific coordinates, including selected/ragged indices. Canonical logical blocks are defined by the source’s structure revision, before rank placement. Changing rank count or packing does not change keys. Retiling for transport produces subrange fragments of a key; exact ordered nonoverlapping coverage is validated before the block is accepted. Changing the canonical block scheme increments structure revision and requires an explicit reconstruction map. Do not silently identify two differently tiled blocks. Rectangles need no square padding. A ragged key includes its parent coordinate and child ordered domain; parents with lengths 2, 0 and 3 have distinct keys and no padded payload. An empty domain is explicit; unavailable data and screened omission are not exact zero. Conjugating or transposing a view changes the operation, not its stored value ID.
Ownership, epochs and placement¶
The authoritative owner publishes and changes a value. Contributors may compute
private partials only for the predeclared ContributionId assigned to them; they
are not concurrent writers to the authoritative output. The executor map may
place computation elsewhere than either input or output owner. Remote input
reads acquire immutable replicas; partial outputs return to the single reduction
destination. Moving computation does not silently migrate authority.
value_epoch, domain/source revisions, placement_epoch, operation ID and #820
phase sequence/epoch have distinct purposes. Repartitioning changes placement,
not values; mutation changes the value epoch; a domain reorder changes its
revision even at identical shape. Rebinding validates all relevant fields.
Reject stale envelopes before numerical access and reject future/undeclared
versions rather than guessing. Never reuse IDs or wrapped counters.
The first implementation uses a quiescent epoch fence: stop new reads, drain launched work and collect all declared consumer release receipts, then retire replicas and publish a new value or owner map. No outstanding replica can cross that fence. Cache entries key the full value and placement reference; a cache cannot satisfy a newer epoch. Explicit concurrent multi-version storage may be added later only with separate reservations. Timeouts are not evidence that a remote reader has stopped. Untracked importer mutation remains prohibited.
Example of distinct placement: rank 1 owns the p=1 input rows, rank 2 computes that row’s outer product from a bounded read-only copy, and rank 0 reduces it. All three roles appear explicitly. The baseline fixture instead computes at the input owner to minimize required capabilities; a placement variant exercises this separation without changing keys or expected values.
Interface and lifetime sketch¶
These signatures name required semantics, not callable functions today:
// Adapter-side additions using the existing tensor objects.
BoundDescriptor bind(const TensorDesc&, const LocalAuthenticatedOwners&);
Admission prepare(const Operation&, Budget&, const ProviderBounds&);
// prepare validates finite bounds and reserves all simultaneously live objects.
LocalRead acquire_local(const ValueRef&, const View&); // retains a Lease
RemoteRead acquire_remote(const ValueRef&, const AdmittedReceiveSlot&);
Ticket produce_or_pack(const LocalRead&, const AdmittedSendSlot&);
void progress_on_mpi_owner(PhaseHandle&, Ticket&); // #820 seam, never a worker
void accept(const ContributionId&, const ValidatedPayload&, PrivateOutput&);
void consumer_complete(Ticket&, CompletionReceipt); // CPU return or GPU event
PublishedValue publish(PrivateOutput&&, const SuccessfulPhaseResult&);
void fail_and_drain(PhaseHandle&, OperationState&); // no implied MPI cancellation
BoundDescriptor retains a charged local binding/map page. Admission owns a
bundle of existing reservations until transferred to tickets/result; construction
failure releases the entire unlaunched bundle. ProviderBounds declares replay,
maximum panel/metadata/workspace/work counts and retainable ownership. These
extend ResidentFactors/Tile capability declarations. PrivateOutput retains
Allocation/sum/correction and the ordered contribution cursor. PublishedValue
transfers that allocation’s ownership without copying. CompletionReceipt is a
validated ticket/generation and terminal status, not an untrusted boolean.
PhaseHandle, slots and SuccessfulPhaseResult are #820-owned transport seams.
No additional memory counter independent of Budget is introduced.
A synchronous borrow may be consumed before its scope ends. Async use requires
an actual retained owner or an admitted copy made while the borrow is valid;
retaining View alone cannot extend BorrowScope. A transient provider must not
advance/recycle its tile until the producer’s read finishes. Send buffers live
until local send completion. Receive replicas live through the final local
consumer, then acknowledge release; the authoritative remote read lease stays
until that receipt is processed. A failed/dropped user future does not release
active state: the executor retains tickets until drain or process termination.
State |
Entry/transition condition |
Retained resources / permitted action |
|---|---|---|
Validated |
Identity, domains, arithmetic, provider and finite contribution schedule checked |
Charged bounded metadata only; no output mutation |
Admitted |
Local reservation bundle succeeds and #820 collective admission succeeds on every participant |
Private output, slots, controls and workspace; empty ranks also participate |
Producing |
Source read lease acquired; CPU kernel/pack or GPU producer launched |
Source owner and producer workspace retained |
Producer-ready |
CPU producer returns successfully or #803 producer event completes |
Payload immutable; MPI owner may post transport |
Transport-active |
Matched receive credit granted and bounded request posted |
Send/receive buffers and communicator retained |
Transport-complete |
Each local request reports completion; receiver verifies envelope and payload |
Sender may recycle send bytes; receiver still retains replica |
Consuming |
Validated payload is bound to CPU reduction or GPU consumer |
Replica/lease, output and consumer workspace retained |
Consumer-complete |
CPU operation returns or #803 final consumer event completes |
Emit release receipt; contribution becomes accepted once, in logical order |
Ready-to-publish |
Exact expected contribution coverage, consumer releases and phase statuses succeed |
Private result only; no read handle exposed yet |
Published |
Owner atomically installs readable |
Output reservation transfers to caller inventory; no partial visibility |
Failed/draining |
Any validation/provider/compute failure or cancellation |
Stop new launches, invalidate private output; retain launched resources until actual completion |
Retired |
All local requests/events and remote release receipts settled |
Recycle slots with new generation; release reservations and old map |
Producer, transport and consumer completion are separate facts, not necessarily one globally observed sequence. A send may complete before the receiver posts or finishes computation because MPI has copied the bytes internally. It proves only that the sender can reuse the send buffer. Even receive completion says nothing about a GPU kernel still reading that receive buffer. #803 must supply producer-ready and final-consumer event queries/error states; MPI request completion never substitutes for those events. Initial GPU work uses admitted host staging. Direct device buffers require later #803 qualification, including stream ordering, workspace and MPI capability; no direct-device support is assumed.
Exactly-once accounting and publication¶
The first schedule has one outstanding contribution window per output and a
fixed canonical reduction order. The owner stores next_expected plus a bounded
current slot, not a set containing every historical contribution. Producers
obtain credit for that exact ID before sending. accept requires the expected
ID, declared contributor, epochs, byte extent and integrity checks. It serially
updates a private accumulator and advances the cursor only on successful local
consumer completion. Atomic here means publication visibility, not transactional
rollback of each floating-point update. Any exception after a partial update
invalidates the whole unpublished result.
An ID below the cursor is a duplicate and fails the operation; an ID above it is missing/out-of-order and fails without applying it. No automatic retry or replay after ambiguous receipt is supported in this first slice. Thus successful publication proves exactly one application per declared ID, not exactly-once network delivery or recovery after rank loss. A later retry design would require bounded receipt retention and a proven retirement protocol; it cannot just resend. End-of-phase with a short cursor is missing coverage, never a zero contribution. A declared exact-zero contribution advances with an authenticated zero receipt; unavailable, screened and failed blocks do not. Structural absence must already be part of the declared method-owned contribution rule.
The single owner initializes private output (beta=0 does not read old output), keeps compensation if the registered kernel requires it, and finalizes only after the exact contribution count and order match. Nonzero beta would require a pinned old-value read and separate admitted private output; it is outside the first fixture. No caller buffer is incrementally mutated. Publication is one local handle/epoch transition after #820 success agreement, with dependent operations requiring that phase result. It is not a fault-tolerant distributed transaction: a transport/process failure during agreement terminates without claiming a recoverable committed epoch. Multi-block durable commit is #826 work.
Invariants:
Every readable value has one authenticated source and one authoritative owner.
Rank changes preserve logical keys and expected contribution coverage.
Writes exclude live local reads and unretired remote read leases.
No payload is sent without a matching reserved receive slot; every request, lease, replica and event has one lifetime owner and a bounded resource charge.
No unpublished or failed accumulator is exposed as a valid output epoch.
Successful publication requires exact IDs, not just a count or matching sum.
No completion or timeout is allowed to release a resource still in use.
Scientific domains, precision, screening and source receipts survive transport.
Admission, metadata and failure¶
For each rank reserve the simultaneous live total, using checked add/mul:
existing Budget baseline + imported authoritative inventory
+ local binding/map metadata + private output and compensation
+ bounded producer and consumer workspace + send/receive/pack/replica payloads
+ ticket/request/receipt controls + bounded contribution cursor
+ declared backend workspace and retained cache bytes
Count each retained allocation once within its scope, each replica at its actual
placement, and imported inventory once through existing BorrowScope credits.
Do not import a globally replicated inventory as if it were node-shared memory.
The parent method’s existing node/device/work ceilings remain authoritative;
static per-rank shares may be admitted there and enforced with local Budget.
Hierarchical collective accounting is not implemented by today’s Budget.
Reject a required scope that has no authoritative admission, rather than inventing
a second ledger or treating a local cap as a node cap.
Set finite maxima before enumeration: tensor rank, parent depth, encoded header, map-page entries, active operations, slots, fragments, retained replicas and work. Use deterministic owner functions or paged directories. At most W live tickets and bounded page cache imply metadata O(W * (rank + parent depth) + page cap), not O(all blocks) or O(all contributions) per rank. Refuse unsupported maxima before provider reads. First slice W=1, one contiguous fragment, one output and no cache; it does not require generalized ragged enumeration. Metadata needed for arbitrary ragged lookup must be paged/reserved or rejected, never allgathered.
Data credits and request/tag credits are distinct. Reserve control/receipt capacity independently so releasing a data slot never requires a free data slot. #820 owns MPI tag allocation, generations, request handles and group lifetime. A slot is reused only after local transport, consumer completion and required remote receipt, with generation advanced. Admission acquires an entire runnable bundle before launching; no task holds one peer’s scarce buffer while waiting for another unadmitted peer. No unbounded queue or eager payload fallback.
Tracked requested allocations are not process RSS. Allocator bookkeeping, Python objects outside the adapter census, MPI internal buffers, BLAS caches, driver allocations and OS pages must be reported separately under #819. Known adapter/backend workspace is reserved; unknown dynamic workspace refuses strict tracked admission. External MPI/runtime memory is explicitly outside that tracked ceiling with a separately documented external allowance/measurement, not asserted zero or bounded by this ADR. No hard process-memory or OOM guarantee follows.
Failure |
Required outcome |
|---|---|
Bad ID/domain/source, overflow, unsupported provider/workspace or insufficient budget |
Refuse before payload/provider consumption; #820 propagates local preflight refusal coherently |
Provider/packing/compute failure after admission |
Invalidate private output, stop new work, use #820 agreed error boundary and drain known launched work; single-pass source is not retried |
Duplicate/missing contribution, stale epoch, bad length/digest |
Reject without applying invalid payload; fail output and drain; never silently omit or overwrite |
User cancellation or future destruction |
Request stop; retain executor-owned active tickets until actual completion |
MPI transport failure or lost process preventing agreement |
#820 termination policy; no promise of cancellation, collective rollback or in-place recovery |
Serial execution uses the same keys, ownership (rank 0), admission and publication invariants, with immediate local transport and no MPI import/initialization. The existing synchronous kernel remains available. Unsupported distribution is an explicit refusal, not full-object allgather or implicit fallback to replication.
Synthetic rectangular complex fixture¶
This is a specification for later tests, not evidence that distributed execution
runs. Use synthetic namespace fixture-v1 (128-bit integer 1). Its manifest
assigns domain serials P=1, A=2, B=3; tensor serials L=1, R=2, T=3, H=4;
operation serials transpose=1, adjoint=2; and source/metric/frame serial 1 in
each distinct kind. P/A/B have independent extents 3/2/3. Use value epoch 7,
source/metric/frame/domain revision 1, structure revision 1 and placement epoch
The optional old published sentinel has epoch 6 and every entry
99-7i. LetL[P,A]andR[P,B]be row-major complex FP64, with exact integer lanes:
L = [[ 1+2i, 3-i ],
[-2+i, 4+3i],
[ 2-3i, -1+2i]]
R = [[2-i, -3+2i, 1+4i],
[1+3i, 2-2i, -4+i ],
[3+2i, -1-4i, 2+i ]]
T[a,b] = sum(p=0..2) L[p,a] * R[p,b] // L transpose times R
H[a,b] = sum(p=0..2) conj(L[p,a]) * R[p,b] // L adjoint times R
Use one canonical reduction chunk per p, regardless of rank count. Input block
keys are L (P:[p,p+1), A:[0,2)) and R (P:[p,p+1), B:[0,3)).
Output key is (A:[0,2), B:[0,3)); T and H have distinct tensor/operation IDs.
Each chunk contributes a 2-by-3 outer product. Derive expected lanes independently
with integer pairs: (x,y)*(u,v)=(xu-yv,xv+yu) and conjugation (x,-y).
For example T[0,0] is (4+3i)+(-5-5i)+(12-5i)=11-7i, whereas H[0,0]
is (-5i)+(1-7i)+(13i)=1+i. No tensor runtime or BLAS is the oracle.
p |
T partial, first row; second row |
H partial, first row; second row |
|---|---|---|
0 |
|
|
1 |
|
|
2 |
|
|
T = [[11-7i, -23-3i, 7-4i],
[-7+14i, 16+9i, -16+6i]]
H = [[ 1+i, 5-i, 19+12i],
[21, -16-5i, -14+24i]]
Require exact real/imaginary lane equality for these small Gaussian integers
(signed zero is normalized in comparison). Fixed p order 0,1,2 is required.
This says nothing about bitwise general floating-point equality across backends.
Use CompensatedPairPlan with one-row inputs and explicit conjugation; its
sum/correction semantics remain unchanged. Reduce partials with the same declared
ordered compensated policy, finalizing sum+correction only at publication.
Participants |
Input owners and baseline contribution executors |
Output owner / empty participation |
|---|---|---|
1 |
rank 0: p=0,1,2 |
rank 0; local serial transport |
2 |
rank 0: p=0,2; rank 1: p=1 |
rank 0; uneven work |
3 |
rank 0: p=0; rank 1: p=1; rank 2: p=2 |
rank 0 |
4 |
ranks 0,1,2: p=0,1,2 respectively; rank 3: none |
rank 0; rank 3 enters every declared phase with zero payload |
Also run three ranks with rank 0 owning p=0,2, rank 1 owning p=1 and rank 2 empty. Compare canonical key sequences across all maps. A separate three-rank placement variant executes p=1 on rank 2 while rank 1 keeps input authority. Only bounded rows/partials move; no communication reconstructs full inputs. Seed old published output with a different sentinel and verify it remains readable and unchanged until the new private epoch is published, or throughout any failure.
For a portable exact-budget fixture use a census, not guessed ABI sizes. Define
K_r as the checked sum of all retained input bytes, Budget::baseline_bytes(),
actual charged descriptor/Array/View/plan controls, maps, cursor, ticket/receipt
controls and declared workspace for rank r. Derive it from the fixed schedule
before execution, including temporary constructor capacities; do not set it from
an observed execution peak. Use separate scopes for T and H. Retained input
payload is 80 bytes per owned p row pair (32 for L and 48 for R): 240 bytes in the singleton; 160/80
bytes on two ranks; 80/80/80 on three ranks; 80/80/80/0 on four ranks.
These resident bytes are in K_r, separate from the copied producer slot.
A conservative fully reserved numeric workspace is:
Producer slot: L row 32 bytes, R row 48, partial sum 96, partial correction 96 and immutable send copy 96, totaling 368 bytes per active producer.
Output owner: receive partial 96, private sum 96 and private correction 96, totaling 288 bytes. Existing published sentinel is an additional 96 bytes retained in
K_r; final publication transfers the private sum handle.Thus
cap_r = K_r + 368*producer_r + 288*output_owner_r, where flags are zero or one. A serial owner conservatively reserves both slot sets (656 bytes). An empty rank has zero numeric slots but a nonzeroK_rfor phase controls.
All slot allocations coexist from admission through drain for this fixture;
any omitted copy requires an explicit alternative census. At cap_r admit;
at cap_r-1 on each participant in turn refuse collectively before producer
reads or mutation. Require each reservation to return to the retained baseline
on failure and to the baseline plus published result on success. This is a
tracked-byte boundary specification, not an RSS value or a measured peak.
Injection / variant |
Required observation |
|---|---|
Flip only the L conjugation flag |
Exactly T versus H above; transpose alone never conjugates; a domain permutation retains the corresponding domain IDs |
Duplicate p=1 before p=2 |
Reject duplicate ID, do not add twice, invalidate unpublished result |
Omit p=1 or finish after p=1 |
Reject unexpected p=2 or incomplete cursor; no publication |
Send epoch 6, old source/domain revision, or old placement generation |
Refuse before binding payload; equal dimensions do not rescue it |
Delay local send completion |
Send allocation and request credit remain live; cannot refill or destroy communicator |
Complete send/receive but delay consumer return/event |
Receive replica, remote read lease and slot remain live; no epoch fence/publication; release only after event and receipt |
Delay producer event |
MPI owner does not post payload until ready; no worker MPI calls |
Provider fails on p=1, or future is dropped |
Private result invalid, no new p=2 launch, known requests/events drain, no budget leak |
Corrupt payload/length, exceed fragment/header maximum |
Fail validation without an oversized allocation or arithmetic update |
Exact/below cap, including empty rank |
Agreement succeeds only when every rank admits all fixed controls and slots |
Ragged metadata parents 0/1/2 with child lengths 2/0/3 |
Repartition preserves keys, empty child has no payload, same-size reordered child is rejected; no padded materialization |
Serial environment without mpi4py |
Same outputs and refusal semantics, no MPI initialization |
Use controllable fake completion gates for lifecycle tests before real MPI/GPU coverage. Later real 1/2/3/empty-rank tests must use bounded harness timeouts and #820 failure paths. A timeout detects a test failure, not safe resource release. No chemistry calculation, GPU availability, scaling evidence or physical-factor qualification is implied by this fixture.
Literature decisions and rejected alternatives¶
Read the configured massive-parallel-vibe-qc-2026-11-02 collection’s README,
models, migration and relevant evidence alongside the primary papers:
CTF, 10.1016/j.jpdc.2014.06.002, sections 4-6, key
gpu20261027_solomonik2014_70c444: section 4 separates global indexing, cyclic blocking, overdecomposition and redistribution; sections 5.3-5.4 evaluate communication/replication and mapping before moving data. Reuse global keys and a separate owner map, but do not mandate cyclic padding, torus topology, full redistribution or smallest-tensor replication. Section 6’s CC iteration measurements on specific supercomputers are not evidence for this fixture, present hardware or complete-method speedup.TAMM, 10.1063/5.0142433, sections III-IV, key
mutlu2023tamm: tiled/dependent spaces and explicit operation dependencies separate data distribution from execution. Equations 21-24 factor the double contraction throughI[m,n,i,j] = sum(e,f) v[m,n,e,f]*t[e,f,i,j], followed byr[a,b,i,j] += 0.25*sum(m,n) I[m,n,i,j]*t[a,b,m,n]. An intermediate’s complete value must precede its consumer; each reduction needs declared coverage. Reuse that dependency principle, not TAMM’s GA/PGAS runtime or a new DAG engine. Factorization/reassociation requires the existing numerical policy; equation 25’s Cholesky approximation and reported CPU/GPU comparisons are not adopted.
Rejected: rank-based IDs; pointer serialization; shape/hash-only scientific identity; implicit adjoints; writable replicas; completion inferred from send; unbounded deduplication sets or replicated all-block directories; automatic retries after ambiguous completion; direct remote accumulation into public output; full-object allgather fallback; unknown-workspace-as-zero; a new resource ledger; mandatory CTF/TAMM/GA dependencies; changed precision/screening or approximations. Also rejected are native-worker MPI, a hidden progress thread and a requirement for MPI_THREAD_MULTIPLE. FUNNELED main-thread MPI remains the contract.
CPU byte-transfer source record¶
The subsequent #804 increment implements the synchronous CPU route in
distributed_wire.hpp,
distributed_cpu.hpp,
tensor_distributed_bindings.cpp
and tensor_mpi.py. The native core registers
_tensor_cpu; its Session binds caller-authenticated descriptors to owned row
copies and native leases. contract(session_factory, provide_row) calls the
unchanged #820 BoundedTransferPhase contract. This opt-in API does not change
scientific dispatch. It reuses the literature decisions above and adopts neither
CTF nor TAMM runtime.
Portable v1 encoding¶
Every integer is unsigned, most-significant byte first, with no padding or native struct representation. An ID is kind u16, namespace-high u64, namespace-low u64, serial u64; a token adds revision u64. Kinds are domain=1, tensor=2, source=3, metric=4, frame=5, operation=6. A domain contains token, dependent u8, optional parent token and parent-coordinate u64, selected u8, extent u64, followed by selected coordinates u64 or affine begin/step u64. A descriptor contains tensor ID, schema u16, structure revision u64, dtype u16 (FP64=1, complex FP64=2), rank u16, source/metric/frame tokens, then rank domains. Inactive array tails are not encoded. Bounded selected/dependent metadata has an encoding, but this first real CPU arithmetic route deliberately refuses those domain forms.
The header order is magic VQTC (four bytes), version u16=1, header length u16,
operation token, value epoch u64, left/right/output descriptors, ranks/active/
executor-shift/output-owner u32, placement epoch u64, work limit u64, adjoint u8,
reduction ordinal u64, slot generation u64, sender/receiver u32, payload bytes
u64, CRC u32. The header is at most 4096 bytes. V1 fixes alpha=1, beta=0, one
full rectangular output block and an ordered compensated outer-product row.
Domains plus ordinal determine global input keys; output descriptors determine
the full output key. No process-local native ID enters the wire format.
Payload is row-major complex binary64, real then imaginary, each big endian. CRC-32/ISO-HDLC covers payload only; it detects corruption, not malicious peers. Receivers have their own authenticated manifest and regenerate the entire expected header, including the payload CRC. Exact length and every byte must match before arithmetic. There is no parser-controlled allocation, hash-only identity acceptance or unbounded metadata directory. Unsupported versions, narrowing, overflowing sizes, nonfinite values and identity/epoch changes refuse.
Lifecycle and Python ownership¶
Every rank constructs the existing sum and transfer phases on the initializing
main thread under FUNNELED. A zero-element BoundedSumPhase callback admits the
native factory on all ranks. Rank zero sends its bounded manifest to each rank
through byte tickets before the contribution loop, including empty ranks. This
compares descriptor content and contribution counts, not just message sizes.
Only metadata is replicated; no full-object data allgather is used. The native
session copies the manifest, registers its tokens and pins its value/placement
epoch for the whole operation. A changed manifest requires a new session.
Boundary |
Native action |
Existing #820 action |
|---|---|---|
Admission |
Check explicit rank cap, affine complex schema, owner map, work bound and provider inventory; allocate fixed role workspace |
Zero-element sum collectively refuses a local factory error |
Produce row p |
Copy two contiguous complex128 provider rows into owned allocations, bind descriptor keys, take read leases, execute existing compensated CPU plan, encode one packet |
Source-only prepare copies the read-only packet into one transport-owned byte buffer |
Payload complete |
Keep producer input leases, packet and private output alive |
Owner |
Consumer complete |
Verify complete header/CRC and ordered contribution, add to private compensated output |
Destination |
Retire |
Release input leases and advance the exact ordered cursor on every rank |
Only after successful |
Final ready |
Require complete coverage, no pending slot and finite compensated final output |
Zero-element agreement, then normal collective transfer close |
Publish |
Only output owner returns immutable |
No new payload operation |
Recoverable refusal |
Discard private output and owned inputs after transport is safely drained |
Collective refusal, then collective close |
Fatal transport |
Retain session and its native allocations/leases in a rooted Python record |
Retain poisoned phases/requests; job termination, no cancellation or collective rollback |
Providers and consumers terminate synchronously and perform no MPI. The producer
and executor are colocated; executor shifts refuse. One native slot and one
transport ticket are live at a time. A sender’s MPI request may complete while
the remote consumer has not even entered its callback. Native input retirement
therefore follows the consumer receipt and terminal agreement, never a send
completion or progress() result. No GPU event is interpreted as CPU completion.
The binding rejects implicit dtype conversion and noncontiguous provider rows. Packet and manifest exports are read-only borrowed views with the native session as their Python base. A packet view is reused by the next contribution, so a caller must not treat it as a historical snapshot. Its storage remains charged and alive until the session and all exported views die, including after explicit safe release. Results own their output independently. The normal adapter consumes exports synchronously and does not retain an unbounded list of rows or packets.
Admission and focused evidence¶
Session.required_bytes(operation, rank, provider_bytes) is the admission census
for that rank. It includes the existing Budget baseline and bounded registry,
sizeof(Session), 376 imported #820 control bytes (144 sum plus 232 transfer),
one transport buffer of max(header, packet if this rank produces or consumes),
and the declared live provider inventory on every rank. A producer additionally
charges the existing
65536-byte CPU control allowance, four Allocation controls, owned left/right
rows, partial/correction arrays, and one native encoded packet. The output owner
adds two Allocation controls, sum/correction arrays and a Published control.
Roles add on a rank; transport controls/buffer are imported once, not recharged
for every ticket. Empty ranks still admit manifest reception and control storage.
Provider inventory must include at least both live row arrays and any larger retained provider allocation; the caller declares that actual inventory. A row view into a larger retained array does not make its parent allocation free. Normal fixture providers materialize only two rows. Test-only packet corruption copies are fault injection, not admission evidence. Bound controls and allocation payloads are tracked limits, not process RSS: Python/NumPy object overhead, allocator/shared-pointer bookkeeping, stack temporaries and MPI/external-library internals remain separate. Phase construction itself precedes collective admission; allocation or MPI setup failure there remains #820’s fatal setup seam. No node-wide aggregate admission or RSS cap is claimed.
The current-source pybind fixture compiles the actual binding translation unit and loads it with the source MPI modules, never the installed chemistry extension. The focused invocation is:
.venv/bin/python -m pytest --confcutdir=tests/fixtures tests/test_tensor_mpi.py tests/test_tensor_distributed.py -q
The explicit configuration boundary excludes chemistry-wide installed-core fixtures; it does not permit a stale native module in these tests. Nineteen new checks and eight existing fake checks pass. Real MPI launches cover 1/2/3 ranks, four ranks with an empty participant, a three-rank/two-producer layout, an empty output-owner rank and a zero-contribution reduction. Both transpose and adjoint match the independent integer-pair oracle above. Exact per-rank caps pass; one byte below source, output-owner or empty-rank caps refuses before row reads. An independent Python encoder checks all v1 bytes and big-endian payload/CRC; every one-byte header mutation and truncation refuses. Exported packet lifetime, duplicate/missing IDs, stale epoch, corruption, provider refusal, empty-rank manifest disagreement, delayed producer and delayed consumer are covered. The source keeps its pending native lease until delayed consumer completion. Tests also verify no output on refusal and serial execution without importing MPI. These are synthetic functional checks, not scaling or physical-method evidence.
Next bounded implementation and exact prerequisites¶
The current CPU byte-ticket increment is complete. #804 stays open. The next separately claimed step is a bounded remote-input CPU route with executor distinct from input owner, using the same descriptor, wire, lease and publication contract. Prerequisites: declare a fixed two-input staging inventory and ordered input transfer schedule; pin both input value/source/placement epochs; retain source leases through each #820 consumer receipt; authenticate destination staging bindings before arithmetic; account staging and transport copies exactly once on each participating rank; and add real shifted-executor, empty-rank, exact/below-cap and stale-input refusal fixtures before enabling the route. Do not silently relax the current executor-shift refusal or add an allgather fallback.
GPU integration remains a later #803 event/staging seam. General ragged arithmetic, physical consumers, node-wide admission, scientific validation and scaling are also outside this increment. #820 retains collective/thread/error ownership; #819 retains measurement records. No new runtime, resource ledger, mandatory dependency, scientific approximation or release gate is introduced.