Files
Raptor/invariants/GRAPH_COMPUTE_BATCH_INVARIANT.md
T
ilgeco 61e3ea9996
Validate Operations / validate-operations (push) Waiting to run
Unexpected invariant now it's clear (batched in the first tensor rank)
2026-07-13 12:05:59 +02:00

363 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Graph Compute Batch Physical-Fragment Invariant
## Status
This document is **normative** for Raptor's Spatial graph IR.
Every developer or coding agent modifying Spatial graph construction, graph
verification, Blueprint handling, or `MergeComputeNodes` must read this file
after `README.md` and `AGENTS.md`.
`AGENTS.md` must contain this instruction:
```text
* Always read the full invariants/GRAPH_COMPUTE_BATCH_INVARIANT.md before modifying Spatial graph IR, Blueprint handling, or MergeComputeNodes.
```
## Scope
This invariant applies to:
- `spat.graph_compute_batch`;
- graph-level values produced by it;
- `tensor.parallel_insert_slice` operations that publish its lane results;
- `spat.blueprint` operations that describe logical reconstruction;
- graph analyses and transformations that consume those values;
- the graph-to-scheduled transition in `MergeComputeNodes`.
It does **not** impose the same representation on:
- `spat.scheduled_compute`;
- `spat.scheduled_compute_batch`;
- `pim.core` or `pim.core_batch`;
- values whose cross-core movement is already represented by explicit
`spat.channel_send` and `spat.channel_receive` operations.
Scheduled IR represents execution on assigned cores. Communication and value
availability there are defined by local SSA forwarding and explicit
send/receive operations, not by the graph physical-fragment invariant.
## Core invariant
For every result of a `spat.graph_compute_batch` with `N` graph lanes:
1. Every graph lane produces exactly one fragment for that result.
2. All lanes produce fragments with the same exact ranked tensor type `F`.
3. The graph result is a physical collection of those fragments with type:
```text
tensor<N x shape(F) x element-type(F)>
```
Conceptually, the result is `N × F`: one leading physical fragment-slot
dimension followed by the complete per-lane fragment shape.
4. Physical slot `i` identifies a fragment publication. It does not, by itself,
identify a row, column, channel, tile, or any other logical tensor position.
5. The result type carries no logical reconstruction order.
The leading dimension is therefore a **physical fragment-slot dimension**, not
a logical tensor dimension.
## Per-lane computation is unrestricted
The invariant constrains the published result representation, not what a lane
may compute.
A graph lane may:
- read several input slices;
- perform reductions;
- add or combine multiple columns;
- execute matrix/vector operations;
- produce a fragment that corresponds to any logical region;
- participate in a multi-stage or logarithmic reduction tree implemented by
following `spat.graph_compute` or `spat.graph_compute_batch` operations.
Arithmetic combination is graph computation. `spat.blueprint` is not an
arithmetic reduction operation.
### Example: `16×4 -> 16×2`
Two graph lanes may compute:
```text
lane 0: input[:, 0] + input[:, 1] -> tensor<16x1>
lane 1: input[:, 2] + input[:, 3] -> tensor<16x1>
```
The physical graph result is:
```text
tensor<2x16x1>
```
A Blueprint then maps:
```text
physical slot 0 -> logical output[:, 0:1]
physical slot 1 -> logical output[:, 1:2]
```
and describes the logical result `tensor<16x2>`.
For a larger reduction, following graph compute batches may reduce fragments in
`ceil(log2(N))` stages. Every intermediate batch still publishes a physical
`batch × fragment` collection.
## Physical publication inside `spat.graph_compute_batch`
The batch body must publish each lane's fragment into the physical result.
For one result with fragment type `F`, the corresponding
`tensor.parallel_insert_slice` must insert the fragment into one slot of the
physical `N × F` destination:
```text
physical offsets = [slot, 0, 0, ...]
physical sizes = [1, shape(F)...]
physical strides = [1, 1, 1, ...]
```
The slot may be the graph lane directly or a statically analyzable permutation
of it. The insertion describes physical slot placement only. It must not use a
logical output dimension as the physical batch dimension.
For each graph result, the body must contain exactly one physical publication
per graph lane. Since the body executes once per lane, this normally means one
`tensor.parallel_insert_slice` operation targeting that result.
## Logical reconstruction
Logical reconstruction is separate from physical publication.
The reconstruction descriptor defines, for every physical fragment slot:
- which physical batch operand owns the fragment;
- which physical slot contains it;
- its destination offsets in the logical tensor;
- its destination sizes;
- its destination strides;
- coverage and conflict policy where relevant.
The persistent owner of this information is `spat.blueprint` or an equivalent
explicit graph-level reconstruction operation.
A logical consumer must not infer reconstruction from the physical tensor type
or assume that physical slot order equals logical order.
The logical mapping may be arbitrary. For example:
```text
physical slot 0 -> logical row 13
physical slot 1 -> logical row 4
physical slot 2 -> logical row 10
```
The physical result remains a regular `batch × fragment` tensor.
## Relationship between `parallel_insert_slice` and Blueprint
During graph construction, an algorithm may naturally describe logical
placement with `tensor.parallel_insert_slice` geometry. Before the graph is in
its canonical form:
1. that geometry must be separated from physical fragment publication;
2. the graph batch result must be normalized to `N × F`;
3. the logical insertion geometry must be transferred to a persistent
`spat.blueprint` reconstruction descriptor.
After normalization:
- `parallel_insert_slice` inside `spat.graph_compute_batch` publishes into
physical fragment slots;
- `spat.blueprint` describes reconstruction into the logical tensor.
The original graph operation may be erased only after all reconstruction
information needed by later stages has a persistent owner.
## Blueprint semantics
Blueprint is placement/reconstruction metadata. It may:
- concatenate fragments;
- reorder fragments;
- insert fragments into arbitrary disjoint logical regions;
- describe complete or partial logical coverage;
- expose a logical tensor view when materialization is required.
Blueprint must not silently perform arithmetic such as addition, multiplication,
maximum, or reduction. Such transformations must be represented by following
`spat.graph_compute` or `spat.graph_compute_batch` operations.
A Blueprint consuming a physical fragment batch must explicitly identify the
physical source slot for every logical fragment. It must not derive that slot
from operand order unless that convention is explicitly represented and
verified.
## Multiple results
A `spat.graph_compute_batch` may have several results.
For each result `r` independently:
- every lane produces one fragment of type `F_r`;
- the graph result type is `N × F_r`;
- its physical publication and logical reconstruction descriptor are verified
independently.
Different results may use different fragment shapes.
## Graph consumers
A graph consumer of a batch result may:
1. consume fragments directly as physical fragments;
2. select one or more physical slots in a `spat.deferred_communication` body;
3. use a Blueprint to obtain or describe a logical reconstruction;
4. feed fragments to following graph computes or graph compute batches.
A consumer must not treat the leading physical slot dimension as a logical
model dimension unless an explicit graph operation intentionally performs such
an interpretation.
All constant selection, slicing, reshaping, concatenation, and other
compile-time shaping needed for a scheduled consumer must be encoded inside the
corresponding `spat.deferred_communication` body. Phase 2 must not recover
missing graph semantics by inspecting consumers after the deferred operation.
## Graph lane, scheduled lane, and physical core are different identities
These concepts must never be conflated:
- **graph lane**: the lane of the original `spat.graph_compute_batch`;
- **physical fragment slot**: the slot in the graph batch result;
- **scheduled lane**: one lane of a `spat.scheduled_compute_batch` equivalence
class;
- **physical core**: the core selected by PEFT.
The graph batch body or its Blueprint defines graph-lane-to-fragment-slot and
fragment-slot-to-logical-region mappings.
PEFT defines graph-instance-to-core placement.
Scheduled communication defines how values move between cores.
## Scheduled IR exclusion
Do not add a verifier requiring `spat.scheduled_compute_batch` results to have
`laneCount` as their first dimension.
Do not rewrite scheduled values merely to resemble graph physical fragment
collections.
When lowering graph IR into scheduled IR:
- resolve graph fragments and reconstruction metadata before erasing their
graph owners;
- create local forwarding or `spat.channel_send`/`spat.channel_receive` for
cross-core dependencies;
- allow scheduled result representation to follow the scheduled IR contract;
- preserve numerical and deadlock correctness.
The graph invariant is an input contract for scheduling, not a scheduled-value
layout contract.
## Required verifier properties
`spat.graph_compute_batch` verification must establish, for every result:
1. the result is a static or otherwise supported ranked tensor;
2. result rank is exactly `fragment rank + 1`;
3. result dimension 0 equals `laneCount`;
4. every lane publication source has the same exact fragment type;
5. the physical insertion targets the corresponding result block argument;
6. physical insertion offsets have the fragment slot in dimension 0;
7. all remaining physical offsets are zero;
8. physical sizes are `[1] + fragment shape`;
9. physical strides are unit;
10. exactly one publication is defined for each graph result in the per-lane
body.
These checks apply only to `spat.graph_compute_batch`, not to
`spat.scheduled_compute_batch`.
Blueprint verification must establish that every logical reconstruction entry:
- references an existing physical batch operand;
- references a valid physical fragment slot;
- maps a fragment compatible with the declared logical slice;
- stays within logical bounds;
- follows the declared conflict and coverage policies.
## Invalid representations
The following are invariant violations.
### Logical aggregate returned directly by graph batch
```text
laneCount = 16
result = tensor<1x4x16x16>
```
with each lane inserting into logical dimension 2.
This is a logical assembly masquerading as a graph batch result. The graph
result must instead be `16 × per-row-fragment`, and a Blueprint must describe
placement into `tensor<1x4x16x16>`.
### Physical storage derived from logical destination shape
Code equivalent to:
```cpp
shape = logicalDestinationType.getShape();
shape[logicalInsertionDimension] = laneCount;
```
is invalid.
Physical graph storage must be derived from the per-lane fragment type:
```cpp
physicalShape = [laneCount] + fragmentType.getShape();
```
### Reconstruction inferred from result type
It is invalid to assume that physical slot `i` belongs at logical offset `i`.
The Blueprint or another explicit reconstruction descriptor must state the
mapping.
### Blueprint used for arithmetic
It is invalid to encode `fragment0 + fragment1` as Blueprint reconstruction.
Create a following graph compute or graph compute batch for the addition.
## Ownership
- ONNX-to-Spatial lowering owns creation of valid graph fragment batches.
- Graph canonicalization owns normalization of temporary logical-assembly forms
into physical graph batches plus Blueprints.
- `spat.graph_compute_batch` verifier rejects invalid physical publications.
- `spat.blueprint` owns persistent logical reconstruction metadata.
- Deferred communication Phase 1 owns complete consumer-side constant shaping.
- Merge scheduling consumes this graph contract and introduces explicit
communication.
- Scheduled IR verifiers validate scheduled execution and communication, not
the graph fragment representation.
## No repair downstream
If graph IR violates this invariant, fix the graph producer or graph
canonicalization.
Do not repair an invalid graph batch by:
- guessing a lane dimension in `MergeComputeNodes`;
- deriving physical storage from a logical destination tensor;
- inspecting deferred-result users;
- reconstructing omitted Blueprint data after graph erasure;
- weakening graph verifiers;
- imposing the graph representation on scheduled operations.