Skip to content

pgw#993: one flattening rule — carried_by resolves through the same expansion dynamic_shapes mirrors - #516

Merged
PaulFidika merged 1 commit into
masterfrom
993-dim-flattening
Aug 7, 2026
Merged

pgw#993: one flattening rule — carried_by resolves through the same expansion dynamic_shapes mirrors#516
PaulFidika merged 1 commit into
masterfrom
993-dim-flattening

Conversation

@PaulFidika

Copy link
Copy Markdown
Contributor

P0. Blocks pgw#868 A2 entirely — z-image is the only family in the fleet using the list-container vocabulary and, before this, it could never mint an AOT cell.

The defect

gen-worker 0.93.2, pod 7evvazd2xplzml (A100-SXM4-80GB), $0.4655: the mint passed every earlier gate, entered the AOT recipe, and refused in trace_graph — exit=2, deterministic, four identical attempts.

entry 'transformer/adapter=true,cfg=true': declared-range gate: declared dynamic dim
names input 'x', which is not a user input of the exported program
(inputs: ['cap_feats_0', 'cap_feats_1', 'lora_a', 'lora_b', 't', 'x_0', 'x_1'])

Two SDK features that each work and could not compose:

  • Input.repeat containers (pgw#853) are flattened by torch.export into one positional user input per element, suffixed _0, _1, … for every arity — a one-element container is x_0, never x (re-measured here on a real export, both arms of the CFG fork).
  • Dim.carried_by names its input by the declared name, and the declared-range gate resolved that name against the exported program.

So a Dim carried by a repeated container was unsatisfiable by construction. No declaration edit fixes it: naming x_0 breaks the cfg=false arm, dropping the dims forfeits dynamic-collapse — the reason z-image is 2 entries and not 20.

The fix — the invariant, not the patch

aot_mint.exported_input_names is now the ONE expansion rule. dynamic_shapes_spec mirrors the container structure with it; declared_range_gaps and lifted_input_gaps resolve declared names against exported ones with it. All three take the same per-arm arity map (aot_declaration.container_arities) the example feed was built from, computed once at the mint call site.

A gate that resolves declared names against exported names must share the name-mangling with the code that produced them; two independent spellings of one mapping is the defect class — which is why lifted_input_gaps is in this change rather than left for the next rented pod (pgw#993 acceptance item 4).

Every element is gated, not merely found: a per-element specialization is still refused, under the element's own name. Non-container declarations resolve exactly as before (pgw#846).

RED, off-GPU

tests/test_dim_flattening_pgw993.py drives the pod's program as a double (user inputs verbatim, refusal string verbatim), then exports for real on CPU for both arms. Reproduced on v0.93.2 and origin/master, green here:

N=1 user_inputs=['x_0', 't']         gate(declared names) -> the refusal above
N=2 user_inputs=['x_0', 'x_1', 't']  gate(declared names) -> the refusal above
N=1 / N=2 with the arity map         -> []

11 rows; mypy clean, ruff clean, lint_http_timeouts / lint_unreached_surface / lint_config_reads green; both suites run locally.

Also lands on 0.93.3

Per the tracker's release-vehicle rules, this lane cuts no branch and no tag: the mint-speed lane cherry-picks this commit onto release/0.93.3 (based on the v0.93.2 tag, pre-wire-v1) alongside pgw#992/pgw#989.

Not in scope, filed separately

The serve-side ingress contract has the same shape of gap one gate later: aot_package.input_contract records position as the index among exported (flattened) inputs while aot_serve.bind_call_inputs matches that position against the caller's pre-flattening args, so a container input binds the whole list to element 0 and shifts every later input. It is unreachable until a container family actually mints — i.e. now. Reported to the tracker rather than folded in here, because it needs its own design (element identity in the contract) and its own RED.

… expansion `dynamic_shapes` mirrors

MEASURED (gen-worker 0.93.2, pod `7evvazd2xplzml`, A100-SXM4-80GB, $0.4655):
the z-image AOT mint passed every earlier gate, entered the AOT recipe, and
refused in `trace_graph` — exit=2, `deterministic`, four identical attempts:

    entry 'transformer/adapter=true,cfg=true': declared-range gate: declared
    dynamic dim names input 'x', which is not a user input of the exported
    program (inputs: ['cap_feats_0', 'cap_feats_1', 'lora_a', 'lora_b', 't',
    'x_0', 'x_1'])

Two SDK features that each work and could not compose. `Input.repeat`
containers (pgw#853) are FLATTENED by torch.export into one positional user
input per element, suffixed `_0`, `_1`, … for EVERY arity — a one-element
container is `x_0`, never `x` (re-measured here on a real export, both arms).
`Dim.carried_by` names its input by the DECLARED name, and the declared-range
gate resolved that name against the exported program. That makes a `Dim`
carried by a repeated container unsatisfiable BY CONSTRUCTION: naming `x_0`
breaks the `cfg=false` arm, and dropping the dims forfeits `dynamic-collapse`,
which is the reason z-image is 2 entries and not 20.

The fix is the invariant, not the patch. `aot_mint.exported_input_names` is now
the ONE expansion rule: `dynamic_shapes_spec` mirrors the container structure
with it, and `declared_range_gaps` + `lifted_input_gaps` resolve declared names
against exported ones with it — all three from the same per-arm arity map
(`aot_declaration.container_arities`) the example feed was built from, computed
once at the mint call site. A gate that resolves declared names against
exported names must share the name-mangling with the code that produced them;
two independent spellings of one mapping is the defect class, which is why the
lifted-input gate is in this change rather than left for the next rented pod.

Every element is gated, not merely found: a per-element specialization is still
refused, under the element's own name (`x_1[2] exported as the STATIC value
64 …`). Non-container declarations resolve exactly as before (pgw#846).

RED, off-GPU, on the real path (declaration -> export -> gate), reproduced on
BOTH `v0.93.2` and `origin/master` and green here:

    N=1 user_inputs=['x_0', 't']         gate(declared names) -> refusal
    N=2 user_inputs=['x_0', 'x_1', 't']  gate(declared names) -> refusal
    N=1/N=2 with the arity map           -> []

tests/test_dim_flattening_pgw993.py drives the pod's program as a double
(user inputs verbatim, refusal string verbatim) and then exports for real on
CPU for both arms of the fork.
@PaulFidika
PaulFidika merged commit 6f5db9e into master Aug 7, 2026
2 checks passed
@PaulFidika
PaulFidika deleted the 993-dim-flattening branch August 7, 2026 02:18
PaulFidika added a commit that referenced this pull request Aug 7, 2026
… expansion `dynamic_shapes` mirrors (#516)

MEASURED (gen-worker 0.93.2, pod `7evvazd2xplzml`, A100-SXM4-80GB, $0.4655):
the z-image AOT mint passed every earlier gate, entered the AOT recipe, and
refused in `trace_graph` — exit=2, `deterministic`, four identical attempts:

    entry 'transformer/adapter=true,cfg=true': declared-range gate: declared
    dynamic dim names input 'x', which is not a user input of the exported
    program (inputs: ['cap_feats_0', 'cap_feats_1', 'lora_a', 'lora_b', 't',
    'x_0', 'x_1'])

Two SDK features that each work and could not compose. `Input.repeat`
containers (pgw#853) are FLATTENED by torch.export into one positional user
input per element, suffixed `_0`, `_1`, … for EVERY arity — a one-element
container is `x_0`, never `x` (re-measured here on a real export, both arms).
`Dim.carried_by` names its input by the DECLARED name, and the declared-range
gate resolved that name against the exported program. That makes a `Dim`
carried by a repeated container unsatisfiable BY CONSTRUCTION: naming `x_0`
breaks the `cfg=false` arm, and dropping the dims forfeits `dynamic-collapse`,
which is the reason z-image is 2 entries and not 20.

The fix is the invariant, not the patch. `aot_mint.exported_input_names` is now
the ONE expansion rule: `dynamic_shapes_spec` mirrors the container structure
with it, and `declared_range_gaps` + `lifted_input_gaps` resolve declared names
against exported ones with it — all three from the same per-arm arity map
(`aot_declaration.container_arities`) the example feed was built from, computed
once at the mint call site. A gate that resolves declared names against
exported names must share the name-mangling with the code that produced them;
two independent spellings of one mapping is the defect class, which is why the
lifted-input gate is in this change rather than left for the next rented pod.

Every element is gated, not merely found: a per-element specialization is still
refused, under the element's own name (`x_1[2] exported as the STATIC value
64 …`). Non-container declarations resolve exactly as before (pgw#846).

RED, off-GPU, on the real path (declaration -> export -> gate), reproduced on
BOTH `v0.93.2` and `origin/master` and green here:

    N=1 user_inputs=['x_0', 't']         gate(declared names) -> refusal
    N=2 user_inputs=['x_0', 'x_1', 't']  gate(declared names) -> refusal
    N=1/N=2 with the arity map           -> []

tests/test_dim_flattening_pgw993.py drives the pod's program as a double
(user inputs verbatim, refusal string verbatim) and then exports for real on
CPU for both arms of the fork.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant