| { |
| "op": "ai.onnx.Scan", |
| "cases": [ |
| { |
| "name": "lowered_prefix_sum_state", |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [2], "data": { "kind": "values", "values": [1.0, 10.0] } }, |
| "scan_input": { |
| "dtype": "float32", |
| "shape": [3, 2], |
| "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0] } |
| } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [2], "tolerance": 0.000001 }, |
| "scan_output": { "dtype": "float32", "shape": [3, 2], "tolerance": 0.000001 } |
| } |
| }, |
| { |
| "name": "lowered_prefix_sum_reverse", |
| "attrs": { "reverse": 1 }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [1], "data": { "kind": "values", "values": [0.0] } }, |
| "scan_input": { |
| "dtype": "float32", |
| "shape": [4, 1], |
| "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0] } |
| } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [1], "tolerance": 0.000001 }, |
| "scan_output": { "dtype": "float32", "shape": [4, 1], "tolerance": 0.000001 } |
| } |
| }, |
| { |
| "name": "lowered_ort_projection_short_sequence_two_state_lanes", |
| "provenance": { |
| "source": "onnxruntime/test/providers/cpu/controlflow/scan_test.cc", |
| "test": "Scan8.ShortSequenceTwoInBatchOneLoopStateVar", |
| "notes": "Projection onto the framework's lowered prefix-sum variant using two state lanes from ORT's short sequence data." |
| }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [2], "data": { "kind": "values", "values": [0.0, 10.0] } }, |
| "scan_input": { |
| "dtype": "float32", |
| "shape": [2, 2], |
| "data": { "kind": "values", "values": [1.0, -1.0, 4.0, -4.0] } |
| } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [2], "tolerance": 0.000001 }, |
| "scan_output": { "dtype": "float32", "shape": [2, 2], "tolerance": 0.000001 } |
| } |
| }, |
| { |
| "name": "lowered_ort_projection_reverse_two_state_lanes", |
| "attrs": { "reverse": 1 }, |
| "provenance": { |
| "source": "onnxruntime/test/providers/cpu/controlflow/scan_test.cc", |
| "test": "Scan8.MixedSequenceLensReverse", |
| "notes": "Projection onto the framework's lowered reverse prefix-sum variant using reverse-direction values from ORT's Scan coverage." |
| }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [2], "data": { "kind": "values", "values": [0.0, 10.0] } }, |
| "scan_input": { |
| "dtype": "float32", |
| "shape": [2, 2], |
| "data": { "kind": "values", "values": [1.0, -1.0, 4.0, -4.0] } |
| } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [2], "tolerance": 0.000001 }, |
| "scan_output": { "dtype": "float32", "shape": [2, 2], "tolerance": 0.000001 } |
| } |
| }, |
| { |
| "name": "lowered_ort_projection_scalar_loop_state", |
| "provenance": { |
| "source": "onnxruntime/test/providers/cpu/controlflow/scan_test.cc", |
| "test": "Scan8.OnnxScalarLoopState", |
| "notes": "Projection onto the framework's lowered prefix-sum variant using a scalar carried state." |
| }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [1], "data": { "kind": "values", "values": [0.0] } }, |
| "scan_input": { "dtype": "float32", "shape": [3, 1], "data": { "kind": "values", "values": [1.0, 2.0, 3.0] } } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [1], "tolerance": 0.000001 }, |
| "scan_output": { "dtype": "float32", "shape": [3, 1], "tolerance": 0.000001 } |
| } |
| }, |
| { |
| "name": "lowered_prefix_sum_zero_steps", |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [2], "data": { "kind": "values", "values": [2.0, -3.0] } }, |
| "scan_input": { "dtype": "float32", "shape": [0, 2], "data": { "kind": "values", "values": [] } } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [2], "tolerance": 0.000001 }, |
| "scan_output": { "dtype": "float32", "shape": [0, 2], "tolerance": 0.000001 } |
| } |
| }, |
| { |
| "name": "lowered_prefix_sum_reverse_rank2_signed", |
| "attrs": { "reverse": 1 }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [2], "data": { "kind": "values", "values": [10.0, -10.0] } }, |
| "scan_input": { |
| "dtype": "float32", |
| "shape": [3, 2], |
| "data": { "kind": "values", "values": [1.0, -1.0, 2.0, -2.0, 3.0, -3.0] } |
| } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [2], "tolerance": 0.000001 }, |
| "scan_output": { "dtype": "float32", "shape": [3, 2], "tolerance": 0.000001 } |
| } |
| }, |
| { |
| "name": "lowered_prefix_sum_reverse_zero_steps", |
| "attrs": { "reverse": 1 }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [2], "data": { "kind": "values", "values": [7.0, -8.0] } }, |
| "scan_input": { "dtype": "float32", "shape": [0, 2], "data": { "kind": "values", "values": [] } } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [2], "tolerance": 0.000001 }, |
| "scan_output": { "dtype": "float32", "shape": [0, 2], "tolerance": 0.000001 } |
| } |
| }, |
| { |
| "name": "lowered_prefix_sum_zero_delta_lane", |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [3], "data": { "kind": "values", "values": [1.0, 10.0, -5.0] } }, |
| "scan_input": { |
| "dtype": "float32", |
| "shape": [3, 3], |
| "data": { "kind": "values", "values": [0.0, 2.0, -1.0, 0.0, -3.0, 4.0, 0.0, 1.0, -2.0] } |
| } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [3], "tolerance": 0.000001 }, |
| "scan_output": { "dtype": "float32", "shape": [3, 3], "tolerance": 0.000001 } |
| } |
| }, |
| { |
| "name": "coop_channel_prefix_sum_128x64", |
| "provenance": { |
| "notes": "Cross-gate case for the coop_channel_prefix_sum variant (dim>=64 and steps>=64). dim=64 -> 64 channel workgroups; steps=128 spans multiple WG-sized chunks with a cross-chunk carry. Tolerance covers the within-chunk subgroup/Hillis-Steele reassociation vs the strict serial loop." |
| }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [64] }, |
| "scan_input": { "dtype": "float32", "shape": [128, 64] } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [64], "tolerance": 0.0001 }, |
| "scan_output": { "dtype": "float32", "shape": [128, 64], "tolerance": 0.0001 } |
| } |
| }, |
| { |
| "name": "coop_channel_prefix_sum_reverse_300x96", |
| "attrs": { "reverse": 1 }, |
| "provenance": { |
| "notes": "Reverse-direction cross-gate case for coop_channel_prefix_sum. steps=300 is NOT a multiple of the 256 workgroup size, exercising the partial-chunk identity padding; dim=96 channel workgroups." |
| }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [96] }, |
| "scan_input": { "dtype": "float32", "shape": [300, 96] } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [96], "tolerance": 0.0001 }, |
| "scan_output": { "dtype": "float32", "shape": [300, 96], "tolerance": 0.0001 } |
| } |
| }, |
| { |
| "name": "coop_channel_prefix_sum_dim512_steps256_perf_compact", |
| "provenance": { |
| "notes": "Compact correctness sibling for the Scan no-MMA prefix-sum cliff: dim=512 and steps=256 select coop_channel_prefix_sum with one full workgroup-sized scan chunk." |
| }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [512] }, |
| "scan_input": { "dtype": "float32", "shape": [256, 512] } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [512], "tolerance": 0.0001 }, |
| "scan_output": { "dtype": "float32", "shape": [256, 512], "tolerance": 0.0001 } |
| } |
| }, |
| { |
| "name": "coop_channel_prefix_sum_dim1024_steps256", |
| "provenance": { |
| "notes": "Exercises a wide cooperative dispatch (1024 channel workgroups) and one full workgroup-sized scan chunk. Tolerance covers within-chunk subgroup/Hillis-Steele reassociation." |
| }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [1024] }, |
| "scan_input": { "dtype": "float32", "shape": [256, 1024] } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [1024], "tolerance": 0.0001 }, |
| "scan_output": { "dtype": "float32", "shape": [256, 1024], "tolerance": 0.0001 } |
| } |
| }, |
| { |
| "name": "coop_dim1025_above_old_fixed_gate", |
| "provenance": { |
| "notes": "Regression for the former hard-coded dim<=1024 ceiling. Device-limit-derived cooperative coverage keeps this realistic 1025-channel scan off the serial fallback." |
| }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [1025] }, |
| "scan_input": { "dtype": "float32", "shape": [256, 1025] } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [1025], "tolerance": 0.0001 }, |
| "scan_output": { "dtype": "float32", "shape": [256, 1025], "tolerance": 0.0001 } |
| } |
| }, |
| { |
| "name": "coop_channel_prefix_sum_dim64_steps64_min_gate", |
| "provenance": { |
| "notes": "Both lower coop gates at their exact inclusive thresholds: dim=64 (dim>=64) and steps=64 (steps>=64). This is the smallest shape that still selects coop_channel_prefix_sum. An off-by-one tightening of either >=64 gate (to >64) would demote this corner to the lowered fallback. Verifies coop at the minimum supported width and exactly one partial-free chunk boundary (steps==WG/4)." |
| }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [64] }, |
| "scan_input": { "dtype": "float32", "shape": [64, 64] } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [64], "tolerance": 0.0001 }, |
| "scan_output": { "dtype": "float32", "shape": [64, 64], "tolerance": 0.0001 } |
| } |
| }, |
| { |
| "name": "lowered_dim64_steps63_below_steps_gate", |
| "provenance": { |
| "notes": "Just below the coop steps gate: dim=64 passes dim>=64 but steps=63 fails steps>=64, so this MUST fall to lowered_prefix_sum. Pins the steps off-by-one: an erroneous steps>=63 (or >63 vs >=64) gate would mis-route this short sequence. Both variants are spec-correct; this checks the lowered path at the exact step below the coop threshold." |
| }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [64] }, |
| "scan_input": { "dtype": "float32", "shape": [63, 64] } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [64], "tolerance": 0.000001 }, |
| "scan_output": { "dtype": "float32", "shape": [63, 64], "tolerance": 0.000001 } |
| } |
| }, |
| { |
| "name": "lowered_prefix_sum_subnormal_residuals_gpu_gap", |
| "skipGpu": { |
| "category": "permanent", |
| "reason": "Portable WGSL floating-point semantics do not guarantee preservation of the subnormal values required by this fixture. Backend evidence: Metal flushes denormals to zero in the ALU; this lowered prefix-sum accumulates subnormal residuals that flush on GPU. Permanent FTZ limitation." |
| }, |
| "provenance": { |
| "source": "onnxruntime/test/providers/cpu/controlflow/scan_test.cc", |
| "test": "Scan8.ShortSequenceTwoInBatchOneLoopStateVar", |
| "notes": "Projection onto the lowered prefix-sum variant: finite subnormal loop-state increments are valid residuals and must not flush to zero." |
| }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [2], "data": { "kind": "values", "values": [0.0, 0.0] } }, |
| "scan_input": { |
| "dtype": "float32", |
| "shape": [4, 2], |
| "data": { "kind": "values", "values": [1e-40, -1e-40, 1e-40, -1e-40, -1e-40, 1e-40, 2e-40, -2e-40] } |
| } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [2], "tolerance": 0 }, |
| "scan_output": { "dtype": "float32", "shape": [4, 2], "tolerance": 0 } |
| } |
| }, |
| { |
| "name": "lowered_prefix_sum_reverse_subnormal_residuals_gpu_gap", |
| "skipGpu": { |
| "category": "permanent", |
| "reason": "Portable WGSL floating-point semantics do not guarantee preservation of the subnormal values required by this fixture. Backend evidence: Metal flushes denormals to zero in the ALU; this lowered prefix-sum accumulates subnormal residuals that flush on GPU. Permanent FTZ limitation." |
| }, |
| "attrs": { "reverse": 1 }, |
| "provenance": { |
| "source": "onnxruntime/test/providers/cpu/controlflow/scan_test.cc", |
| "test": "Scan8.MixedSequenceLensReverse", |
| "notes": "Reverse companion for subnormal prefix residuals; the scan-output slots are written in reverse traversal order but still contain finite float32 subnormal sums." |
| }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [1], "data": { "kind": "values", "values": [0.0] } }, |
| "scan_input": { |
| "dtype": "float32", |
| "shape": [3, 1], |
| "data": { "kind": "values", "values": [1e-40, 2e-40, 3e-40] } |
| } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [1], "tolerance": 0 }, |
| "scan_output": { "dtype": "float32", "shape": [3, 1], "tolerance": 0 } |
| } |
| }, |
| { |
| "name": "coop_channel_prefix_sum_dim64_steps257_chunk_carry", |
| "provenance": { |
| "notes": "Routes to coop_channel_prefix_sum (dim=64>=64, dim<=1024, steps=257>=64). chunks=ceil(257/256)=2: chunk 0 = steps 0..255, chunk 1 = ONLY step 256 (one valid lane, 255 identity-padded). Isolates cross-chunk carry propagation into a near-empty second chunk and the final_state = carry-after-both-chunks path. An off-by-one chunk bound, stale carry, or padded-lane double-count would corrupt scan_output[256*64+i] and final_state. Verifies BOTH outputs against the TS reference; tolerance 1e-4 covers within-chunk subgroup/Hillis-Steele reassociation." |
| }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [64] }, |
| "scan_input": { "dtype": "float32", "shape": [257, 64] } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [64], "tolerance": 0.0001 }, |
| "scan_output": { "dtype": "float32", "shape": [257, 64], "tolerance": 0.0001 } |
| } |
| }, |
| { |
| "name": "coop_channel_prefix_sum_reverse_dim64_steps257_chunk_carry", |
| "attrs": { "reverse": 1 }, |
| "provenance": { |
| "notes": "Reverse coop_channel_prefix_sum (dim=64>=64, dim<=1024, steps=257>=64, reverse=1). chunks=2: chunk 0 (j=0..255) -> original steps 256..1, chunk 1 (j=256) -> original step 0. Pins the minimal 1-step residual chunk under reverse: verifies the reversed read/write addresses at the chunk boundary and the scan-order cross-chunk carry land at the correct reversed scan_output slots and final_state. Both outputs checked vs the TS reference; tolerance 1e-4 covers within-chunk reassociation." |
| }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [64] }, |
| "scan_input": { "dtype": "float32", "shape": [257, 64] } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [64], "tolerance": 0.0001 }, |
| "scan_output": { "dtype": "float32", "shape": [257, 64], "tolerance": 0.0001 } |
| } |
| }, |
| { |
| "name": "lowered_dim1_steps2000_singlechannel_serial", |
| "provenance": { |
| "notes": "dim=1 fails coop 'dim>=64' -> lowered_prefix_sum, one active lane (i=0) running the serial 2000-step recurrence with addressing t*1+0=t. Covers the single-channel serial scan over a long sequence (existing dim=1 cases stop at steps<=4). Serial per-channel accumulation is bit-identical to the reference, so tolerance stays at 1e-6; both outputs verified vs the TS reference." |
| }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [1], "data": { "kind": "values", "values": [3.5] } }, |
| "scan_input": { |
| "dtype": "float32", |
| "shape": [2000, 1], |
| "data": { "kind": "fillFloat32", "sinStep": 0.017, "scale": 0.25, "offset": 0.01 } |
| } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [1], "tolerance": 0.000001 }, |
| "scan_output": { "dtype": "float32", "shape": [2000, 1], "tolerance": 0.000001 } |
| } |
| }, |
| { |
| "name": "multichunk_small_state_dim2_steps4096", |
| "provenance": { |
| "notes": "Compact correctness lock for a realistic long-stream recurrence with only two independent state lanes. The optimized path partitions the sequential axis into workgroup-sized chunks, scans chunk totals, then applies carries." |
| }, |
| "inputs": { |
| "initial_state": { "dtype": "float32", "shape": [2], "data": { "kind": "values", "values": [1.5, -2.0] } }, |
| "scan_input": { |
| "dtype": "float32", |
| "shape": [4096, 2], |
| "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.013, "scale": 0.01, "offset": 0.0001 } |
| } |
| }, |
| "outputs": { |
| "final_state": { "dtype": "float32", "shape": [2], "tolerance": 0.001, "relTolerance": 0.0001 }, |
| "scan_output": { "dtype": "float32", "shape": [4096, 2], "tolerance": 0.001, "relTolerance": 0.0001 } |
| } |
| } |
| ] |
| } |
|
|