| { |
| "op": "com.microsoft.LinearAttentionGate", |
| "cases": [ |
| { |
| "name": "rank3_h8_vec4_gate_beta", |
| "provenance": { |
| "notes": "The (B, T, H) shape the schema names, with a head count that is a multiple of four so the vectorized path runs. Covers vec4_gate_beta. Tolerance: the measured kernel-vs-reference max relative deviation over this op's fixtures is 4.4e-7, which is the f32 floor for softplus just above the log1p series crossover; 2e-6 keeps roughly four times that as device margin." |
| }, |
| "inputs": { |
| "aT": { |
| "dtype": "float32", |
| "shape": [2, 3, 8], |
| "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.17, "scale": 2.5 } |
| }, |
| "dtBiasT": { "dtype": "float32", "shape": [8], "data": { "kind": "linspace", "start": -1.5, "end": 1.1 } }, |
| "decayScaleT": { "dtype": "float32", "shape": [8], "data": { "kind": "linspace", "start": -3.2, "end": -0.15 } }, |
| "bT": { |
| "dtype": "float32", |
| "shape": [2, 3, 8], |
| "data": { "kind": "fillFloat32", "sinStep": 0.31, "cosStep": 0.13, "scale": 3.0 } |
| } |
| }, |
| "outputs": { |
| "decayT": { "dtype": "float32", "shape": [2, 3, 8], "tolerance": 0, "relTolerance": 0.000002 }, |
| "betaT": { "dtype": "float32", "shape": [2, 3, 8], "tolerance": 0, "relTolerance": 0.000002 } |
| } |
| }, |
| { |
| "name": "rank2_h6_scalar_gate_beta", |
| "provenance": { |
| "notes": "Head count 6 is not a multiple of four, so the vectorized head-to-parameter mapping does not hold and the scalar path is the only eligible one. Covers scalar_gate_beta." |
| }, |
| "inputs": { |
| "aT": { |
| "dtype": "float32", |
| "shape": [5, 6], |
| "data": { "kind": "fillFloat32", "sinStep": 0.29, "cosStep": 0.19, "scale": 2.2 } |
| }, |
| "dtBiasT": { "dtype": "float32", "shape": [6], "data": { "kind": "linspace", "start": -2.0, "end": 0.9 } }, |
| "decayScaleT": { "dtype": "float32", "shape": [6], "data": { "kind": "linspace", "start": -2.6, "end": -0.2 } }, |
| "bT": { |
| "dtype": "float32", |
| "shape": [5, 6], |
| "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.11, "scale": 2.8 } |
| } |
| }, |
| "outputs": { |
| "decayT": { "dtype": "float32", "shape": [5, 6], "tolerance": 0, "relTolerance": 0.000002 }, |
| "betaT": { "dtype": "float32", "shape": [5, 6], "tolerance": 0, "relTolerance": 0.000002 } |
| } |
| }, |
| { |
| "name": "rank4_h4_vec4_gate_only", |
| "provenance": { |
| "notes": "Rank 4 proves the leading axes are folded rather than parsed: only the last axis is the head axis. No b input, so no beta output. Covers vec4_gate." |
| }, |
| "inputs": { |
| "aT": { |
| "dtype": "float32", |
| "shape": [2, 2, 3, 4], |
| "data": { "kind": "fillFloat32", "sinStep": 0.41, "cosStep": 0.23, "scale": 3.1 } |
| }, |
| "dtBiasT": { |
| "dtype": "float32", |
| "shape": [4], |
| "data": { "kind": "values", "values": [-1.25, 0.4, 1.75, -0.6] } |
| }, |
| "decayScaleT": { |
| "dtype": "float32", |
| "shape": [4], |
| "data": { "kind": "values", "values": [-1.5, -0.35, -2.8, -0.9] } |
| } |
| }, |
| "outputs": { "decayT": { "dtype": "float32", "shape": [2, 2, 3, 4], "tolerance": 0, "relTolerance": 0.000002 } } |
| }, |
| { |
| "name": "rank1_h5_scalar_gate_only", |
| "provenance": { |
| "notes": "Rank 1 is the single-token decode shape: the whole tensor is one head row, so every element reads a different parameter slot. Covers scalar_gate." |
| }, |
| "inputs": { |
| "aT": { |
| "dtype": "float32", |
| "shape": [5], |
| "data": { "kind": "values", "values": [-9.5, -1.25, 0.0, 2.5, 14.0] } |
| }, |
| "dtBiasT": { |
| "dtype": "float32", |
| "shape": [5], |
| "data": { "kind": "values", "values": [0.75, -0.5, 1.25, -1.75, 0.3] } |
| }, |
| "decayScaleT": { |
| "dtype": "float32", |
| "shape": [5], |
| "data": { "kind": "values", "values": [-0.4, -1.6, -3.05, -0.85, -2.2] } |
| } |
| }, |
| "outputs": { "decayT": { "dtype": "float32", "shape": [5], "tolerance": 0, "relTolerance": 0.000002 } } |
| }, |
| { |
| "name": "b_without_beta_is_ignored", |
| "provenance": { |
| "notes": "The public schema requires b only in the forward direction: requesting beta requires b. ORT CPU and WebGPU accept b when beta is omitted and do not read it; this fixture prevents the manifest from imposing the stronger, non-schema b-implies-beta rule." |
| }, |
| "inputs": { |
| "aT": { |
| "dtype": "float32", |
| "shape": [2, 4], |
| "data": { "kind": "fillFloat32", "sinStep": 0.27, "cosStep": 0.19, "scale": 1.7 } |
| }, |
| "dtBiasT": { "dtype": "float32", "shape": [4], "data": { "kind": "linspace", "start": -0.7, "end": 0.8 } }, |
| "decayScaleT": { |
| "dtype": "float32", |
| "shape": [4], |
| "data": { "kind": "values", "values": [-0.5, -1.0, -1.5, -2.0] } |
| }, |
| "bT": { |
| "dtype": "float32", |
| "shape": [2, 4], |
| "data": { "kind": "fillFloat32", "sinStep": 0.43, "cosStep": 0.11, "scale": 4.0 } |
| } |
| }, |
| "outputs": { "decayT": { "dtype": "float32", "shape": [2, 4], "tolerance": 0, "relTolerance": 0.000002 } } |
| }, |
| { |
| "name": "rank2_h4_vec4_pinned_head_parameters", |
| "provenance": { |
| "notes": "Hand-computed, anchoring the op off its own reference. Row 0 is built so a + dt_bias == 0 in every head, making softplus exactly ln(2) = 0.69314718, so each head's decay is its decay_scale times that one constant -- a wrong head-to-parameter mapping cannot reproduce the column pattern. Row 1 biases to 25 in every head, above the x > 20 softplus threshold where softplus(x) == x exactly, so those four values are exact. beta pins both sigmoid branches (x >= 0 and x < 0) at 0, +/-1, +/-2 and +/-6." |
| }, |
| "inputs": { |
| "aT": { |
| "dtype": "float32", |
| "shape": [2, 4], |
| "data": { "kind": "values", "values": [0.0, -1.0, 1.0, -0.5, 25.0, 24.0, 26.0, 24.5] } |
| }, |
| "dtBiasT": { "dtype": "float32", "shape": [4], "data": { "kind": "values", "values": [0.0, 1.0, -1.0, 0.5] } }, |
| "decayScaleT": { |
| "dtype": "float32", |
| "shape": [4], |
| "data": { "kind": "values", "values": [-1.0, -2.0, -0.5, -4.0] } |
| }, |
| "bT": { |
| "dtype": "float32", |
| "shape": [2, 4], |
| "data": { "kind": "values", "values": [0.0, 6.0, -6.0, 1.0, -1.0, 2.0, -2.0, 0.0] } |
| } |
| }, |
| "outputs": { |
| "decayT": { |
| "dtype": "float32", |
| "shape": [2, 4], |
| "data": { |
| "kind": "values", |
| "values": [-0.69314718, -1.3862944, -0.34657359, -2.7725887, -25.0, -50.0, -12.5, -100.0] |
| }, |
| "tolerance": 0, |
| "relTolerance": 0.000001 |
| }, |
| "betaT": { |
| "dtype": "float32", |
| "shape": [2, 4], |
| "data": { |
| "kind": "values", |
| "values": [0.5, 0.99752738, 0.0024726232, 0.73105858, 0.26894142, 0.88079708, 0.11920292, 0.5] |
| }, |
| "tolerance": 0, |
| "relTolerance": 0.000001 |
| } |
| } |
| }, |
| { |
| "name": "rank2_h5_scalar_pinned_saturation", |
| "provenance": { |
| "notes": "Hand-computed on the scalar path (head count 5). Both rows bias to one shared value -- 25 in row 0 (the x > 20 softplus identity) and 0 in row 1 (ln 2) -- so the five distinct decay values in each row are exactly the five decay_scale entries times one constant. That is the per-head parameter index, index % head_count, stated as arithmetic." |
| }, |
| "inputs": { |
| "aT": { |
| "dtype": "float32", |
| "shape": [2, 5], |
| "data": { "kind": "values", "values": [24.5, 25.5, 25.0, 23.0, 27.0, -0.5, 0.5, 0.0, -2.0, 2.0] } |
| }, |
| "dtBiasT": { |
| "dtype": "float32", |
| "shape": [5], |
| "data": { "kind": "values", "values": [0.5, -0.5, 0.0, 2.0, -2.0] } |
| }, |
| "decayScaleT": { |
| "dtype": "float32", |
| "shape": [5], |
| "data": { "kind": "values", "values": [-1.0, -2.0, -0.25, -4.0, -0.5] } |
| }, |
| "bT": { |
| "dtype": "float32", |
| "shape": [2, 5], |
| "data": { "kind": "values", "values": [-6.0, -2.0, 0.0, 2.0, 6.0, -1.0, -0.25, 0.25, 1.0, 3.0] } |
| } |
| }, |
| "outputs": { |
| "decayT": { |
| "dtype": "float32", |
| "shape": [2, 5], |
| "data": { |
| "kind": "values", |
| "values": [-25.0, -50.0, -6.25, -100.0, -12.5, -0.69314718, -1.3862944, -0.1732868, -2.7725887, -0.34657359] |
| }, |
| "tolerance": 0, |
| "relTolerance": 0.000001 |
| }, |
| "betaT": { |
| "dtype": "float32", |
| "shape": [2, 5], |
| "data": { |
| "kind": "values", |
| "values": [0.0024726232, 0.11920292, 0.5, 0.88079708, 0.99752738, 0.26894142, 0.4378235, 0.5621765, 0.73105858, 0.95257413] |
| }, |
| "tolerance": 0, |
| "relTolerance": 0.000001 |
| } |
| } |
| }, |
| { |
| "name": "f16_rank3_h8_vec4_gate_beta", |
| "provenance": { |
| "notes": "float16 a/b/decay/beta with float32 dt_bias and decay_scale -- the schema's T/TF split. The gate arithmetic still runs in f32 and only the store narrows." |
| }, |
| "inputs": { |
| "aT": { |
| "dtype": "float16", |
| "shape": [2, 3, 8], |
| "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.17, "scale": 2.5 } |
| }, |
| "dtBiasT": { "dtype": "float32", "shape": [8], "data": { "kind": "linspace", "start": -1.5, "end": 1.1 } }, |
| "decayScaleT": { "dtype": "float32", "shape": [8], "data": { "kind": "linspace", "start": -3.2, "end": -0.15 } }, |
| "bT": { |
| "dtype": "float16", |
| "shape": [2, 3, 8], |
| "data": { "kind": "fillFloat32", "sinStep": 0.31, "cosStep": 0.13, "scale": 3.0 } |
| } |
| }, |
| "outputs": { |
| "decayT": { "dtype": "float16", "shape": [2, 3, 8], "tolerance": 0, "relTolerance": 0.002 }, |
| "betaT": { "dtype": "float16", "shape": [2, 3, 8], "tolerance": 0, "relTolerance": 0.002 } |
| } |
| }, |
| { |
| "name": "f16_rank2_h6_scalar_gate_beta", |
| "provenance": { "notes": "float16 gate tensors on the scalar path (head count 6)." }, |
| "inputs": { |
| "aT": { |
| "dtype": "float16", |
| "shape": [5, 6], |
| "data": { "kind": "fillFloat32", "sinStep": 0.29, "cosStep": 0.19, "scale": 2.2 } |
| }, |
| "dtBiasT": { "dtype": "float32", "shape": [6], "data": { "kind": "linspace", "start": -2.0, "end": 0.9 } }, |
| "decayScaleT": { "dtype": "float32", "shape": [6], "data": { "kind": "linspace", "start": -2.6, "end": -0.2 } }, |
| "bT": { |
| "dtype": "float16", |
| "shape": [5, 6], |
| "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.11, "scale": 2.8 } |
| } |
| }, |
| "outputs": { |
| "decayT": { "dtype": "float16", "shape": [5, 6], "tolerance": 0, "relTolerance": 0.002 }, |
| "betaT": { "dtype": "float16", "shape": [5, 6], "tolerance": 0, "relTolerance": 0.002 } |
| } |
| } |
| ] |
| } |
|
|