diff --git "a/build/webgpu/test.json" "b/build/webgpu/test.json" new file mode 100644--- /dev/null +++ "b/build/webgpu/test.json" @@ -0,0 +1,2337 @@ +{ + "op": "com.microsoft.LinearAttention", + "cases": [ + { + "name": "linear_zero_state", + "attrs": { "q_num_heads": 2, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.7, "chunk_size": 17 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 3, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 3, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 3, 3], + "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 3, 6], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 2, 3], "tolerance": 0.0001 } + } + }, + { + "name": "ort_linear_rule_single_token", + "provenance": { + "source": "onnxruntime/test/contrib_ops/linear_attention_op_test.cc", + "test": "ContribOpLinearAttentionTest.LinearRule_SingleToken" + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 1, 4], + "data": { "kind": "values", "values": [1.0, 0.0, 0.5, -0.5] } + }, + "keyT": { "dtype": "float32", "shape": [1, 1, 4], "data": { "kind": "values", "values": [0.5, 0.5, 0.0, 1.0] } }, + "valueT": { + "dtype": "float32", + "shape": [1, 1, 4], + "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0] } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 1, 4], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 4, 4], "tolerance": 0.0001 } + } + }, + { + "name": "ort_linear_rule_multi_token", + "provenance": { + "source": "onnxruntime/test/contrib_ops/linear_attention_op_test.cc", + "test": "ContribOpLinearAttentionTest.LinearRule_MultiToken" + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 3, 4], + "data": { "kind": "values", "values": [1.0, 0.0, 0.5, -0.5, 0.5, 1.0, -0.5, 0.0, 0.0, -1.0, 1.0, 0.5] } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 3, 4], + "data": { "kind": "values", "values": [0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 1.0, 0.5, -0.5, 1.0, 0.5, 0.0] } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 3, 4], + "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 2.0, 1.0, 0.0, 3.0, 3.0, 0.0, 1.0, 2.0] } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 3, 4], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 4, 4], "tolerance": 0.0001 } + } + }, + { + "name": "ort_linear_rule_with_initial_state", + "provenance": { + "source": "onnxruntime/test/contrib_ops/linear_attention_op_test.cc", + "test": "ContribOpLinearAttentionTest.LinearRule_WithInitialState" + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "values", "values": [1.0, 0.0, 0.5, -0.5, 0.5, 1.0, -0.5, 0.0] } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "values", "values": [0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 1.0, 0.5] } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 2.0, 1.0, 0.0, 3.0] } + }, + "pastStateT": { "dtype": "float32", "shape": [1, 1, 4, 4], "data": { "kind": "constant", "value": 0.1 } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 4], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 4, 4], "tolerance": 0.0001 } + } + }, + { + "name": "ort_gated_rule_single_token", + "provenance": { + "source": "onnxruntime/test/contrib_ops/linear_attention_op_test.cc", + "test": "ContribOpLinearAttentionTest.GatedRule_SingleToken" + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 1, 4], + "data": { "kind": "values", "values": [1.0, 0.0, 0.5, -0.5] } + }, + "keyT": { "dtype": "float32", "shape": [1, 1, 4], "data": { "kind": "values", "values": [0.5, 0.5, 0.0, 1.0] } }, + "valueT": { + "dtype": "float32", + "shape": [1, 1, 4], + "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0] } + }, + "pastStateT": { "dtype": "float32", "shape": [1, 1, 4, 4], "data": { "kind": "constant", "value": 1.0 } }, + "decayT": { + "dtype": "float32", + "shape": [1, 1, 4], + "data": { "kind": "values", "values": [-0.1, -0.2, -0.05, -0.15] } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 1, 4], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 4, 4], "tolerance": 0.0001 } + } + }, + { + "name": "ort_delta_rule_multi_token", + "provenance": { + "source": "onnxruntime/test/contrib_ops/linear_attention_op_test.cc", + "test": "ContribOpLinearAttentionTest.DeltaRule_MultiToken" + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "delta", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 3, 4], + "data": { "kind": "values", "values": [1.0, 0.0, 0.5, -0.5, 0.5, 1.0, -0.5, 0.0, 0.0, -1.0, 1.0, 0.5] } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 3, 4], + "data": { "kind": "values", "values": [0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 1.0, 0.5, -0.5, 1.0, 0.5, 0.0] } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 3, 4], + "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 2.0, 1.0, 0.0, 3.0, 3.0, 0.0, 1.0, 2.0] } + }, + "betaT": { "dtype": "float32", "shape": [1, 3, 1], "data": { "kind": "values", "values": [0.8, 0.6, 0.9] } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 3, 4], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 4, 4], "tolerance": 0.0001 } + } + }, + { + "name": "ort_gated_rule_broadcast_decay", + "provenance": { + "source": "onnxruntime/test/contrib_ops/linear_attention_op_test.cc", + "test": "ContribOpLinearAttentionTest.GatedRule_BroadcastDecay" + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 3, 4], + "data": { "kind": "values", "values": [1.0, 0.0, 0.5, -0.5, 0.5, 1.0, -0.5, 0.0, 0.0, -1.0, 1.0, 0.5] } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 3, 4], + "data": { "kind": "values", "values": [0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 1.0, 0.5, -0.5, 1.0, 0.5, 0.0] } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 3, 4], + "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 2.0, 1.0, 0.0, 3.0, 3.0, 0.0, 1.0, 2.0] } + }, + "pastStateT": { "dtype": "float32", "shape": [1, 1, 4, 4], "data": { "kind": "constant", "value": 0.5 } }, + "decayT": { + "dtype": "float32", + "shape": [1, 3, 1], + "data": { "kind": "values", "values": [-0.1, -0.2, -0.05] } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 3, 4], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 4, 4], "tolerance": 0.0001 } + } + }, + { + "name": "linear_batch2_mqa_head_dim1", + "attrs": { "q_num_heads": 2, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [2, 2, 2], + "data": { "kind": "values", "values": [1.0, 0.5, -1.0, 2.0, 0.25, -0.75, 1.5, -0.5] } + }, + "keyT": { + "dtype": "float32", + "shape": [2, 2, 1], + "data": { "kind": "values", "values": [1.0, 2.0, -1.0, 0.5] } + }, + "valueT": { + "dtype": "float32", + "shape": [2, 2, 2], + "data": { "kind": "values", "values": [3.0, -3.0, 4.0, -4.0, 5.0, 6.0, -7.0, 8.0] } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [2, 2, 4], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [2, 1, 1, 2], "tolerance": 0.0001 } + } + }, + { + "name": "ort_linear_rule_default_scale", + "provenance": { + "source": "onnxruntime/test/contrib_ops/linear_attention_op_test.cc", + "test": "ContribOpLinearAttentionTest.LinearRule_DefaultScale" + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear" }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 1, 4], + "data": { "kind": "values", "values": [1.0, 0.0, 0.5, -0.5] } + }, + "keyT": { "dtype": "float32", "shape": [1, 1, 4], "data": { "kind": "values", "values": [0.5, 0.5, 0.0, 1.0] } }, + "valueT": { + "dtype": "float32", + "shape": [1, 1, 4], + "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0] } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 1, 4], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 4, 4], "tolerance": 0.0001 } + } + }, + { + "name": "ort_linear_rule_multi_batch_multi_head", + "provenance": { + "source": "onnxruntime/test/contrib_ops/linear_attention_op_test.cc", + "test": "ContribOpLinearAttentionTest.LinearRule_MultiBatchMultiHead" + }, + "attrs": { "q_num_heads": 2, "kv_num_heads": 2, "update_rule": "linear", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [2, 2, 8], + "data": { "kind": "fillFloat32", "scale": 0.7, "sinStep": 0.3, "cosStep": 0.0 } + }, + "keyT": { + "dtype": "float32", + "shape": [2, 2, 8], + "data": { "kind": "fillFloat32", "scale": 0.7, "sinStep": 0.0, "cosStep": 0.5 } + }, + "valueT": { + "dtype": "float32", + "shape": [2, 2, 8], + "data": { "kind": "fillFloat32", "scale": 0.7, "sinStep": 0.7, "cosStep": 0.13 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [2, 2, 8], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [2, 2, 4, 4], "tolerance": 0.0001 } + } + }, + { + "name": "gated_delta_scalar_gqa_headdim6_dv_tiled", + "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated_delta" }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [2, 5, 24], + "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.33 } + }, + "keyT": { + "dtype": "float32", + "shape": [2, 5, 12], + "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.27 } + }, + "valueT": { + "dtype": "float32", + "shape": [2, 5, 20], + "data": { "kind": "fillFloat32", "sinStep": 0.09, "cosStep": 0.43 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [2, 2, 6, 10], + "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.19, "cosStep": 0.37 } + }, + "decayT": { + "dtype": "float32", + "shape": [2, 5, 2], + "data": { "kind": "fillFloat32", "scale": 0.15, "offset": -0.2, "sinStep": 0.29, "cosStep": 0.53 } + }, + "betaT": { + "dtype": "float32", + "shape": [2, 5, 2], + "data": { "kind": "fillFloat32", "scale": 0.25, "offset": 0.5, "sinStep": 0.37, "cosStep": 0.19 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [2, 5, 40] }, + "presentStateT": { "dtype": "float32", "shape": [2, 2, 6, 10] } + } + }, + { + "name": "gated_delta_scalar_headdim6_seq128_state", + "provenance": { + "notes": "Compact sibling for the long-sequence scalar gated-delta benchmark; preserves qHeads=kvHeads=4, headDimK=6 (non-vec4), past state, per-head decay/beta, and a multi-token recurrence loop." + }, + "attrs": { "q_num_heads": 4, "kv_num_heads": 4, "update_rule": "gated_delta", "scale": 0.25 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 128, 24], + "data": { "kind": "fillFloat32", "sinStep": 0.021, "cosStep": 0.033, "scale": 0.05 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 128, 24], + "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.027, "scale": 0.05 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 128, 48], + "data": { "kind": "fillFloat32", "sinStep": 0.009, "cosStep": 0.043, "scale": 0.05 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 4, 6, 12], + "data": { "kind": "fillFloat32", "scale": 0.02, "sinStep": 0.019, "cosStep": 0.037 } + }, + "decayT": { + "dtype": "float32", + "shape": [1, 128, 4], + "data": { "kind": "fillFloat32", "scale": 0.08, "offset": -0.2, "sinStep": 0.029, "cosStep": 0.053 } + }, + "betaT": { + "dtype": "float32", + "shape": [1, 128, 4], + "data": { "kind": "fillFloat32", "scale": 0.08, "offset": 0.5, "sinStep": 0.037, "cosStep": 0.019 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 128, 48], "tolerance": 0.0005, "relTolerance": 0.0005 }, + "presentStateT": { "dtype": "float32", "shape": [1, 4, 6, 12], "tolerance": 0.0005, "relTolerance": 0.0005 } + } + }, + { + "name": "gated_delta_headdim6_seq128_offset_value_scale_lock", + "provenance": { + "notes": "Long-recurrence scale lock at headDimK=6, the non-vec4 width served by the serial-small and scalar gated-delta routes. Scaling the key to |k|^2 about 0.7 and offsetting V to 1.0 makes the 128-step recurrence materially update and converge. Missing or duplicated decay, incorrect beta, and uniform output scaling therefore produce clear output errors." + }, + "attrs": { "q_num_heads": 4, "kv_num_heads": 4, "update_rule": "gated_delta", "scale": 0.25 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 128, 24], + "data": { "kind": "fillFloat32", "sinStep": 0.021, "cosStep": 0.033, "scale": 1.0 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 128, 24], + "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.027, "scale": 0.4 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 128, 48], + "data": { "kind": "fillFloat32", "sinStep": 0.009, "cosStep": 0.043, "scale": 0.3, "offset": 1.0 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 4, 6, 12], + "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.019, "cosStep": 0.037 } + }, + "decayT": { + "dtype": "float32", + "shape": [1, 128, 4], + "data": { "kind": "fillFloat32", "scale": 0.08, "offset": -0.2, "sinStep": 0.029, "cosStep": 0.053 } + }, + "betaT": { + "dtype": "float32", + "shape": [1, 128, 4], + "data": { "kind": "fillFloat32", "scale": 0.08, "offset": 0.5, "sinStep": 0.037, "cosStep": 0.019 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 128, 48], "tolerance": 0.001, "relTolerance": 0.001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 4, 6, 12], "tolerance": 0.001, "relTolerance": 0.001 } + } + }, + { + "name": "gated_delta_scalar_elementwise_decay_shared_key_head", + "attrs": { "q_num_heads": 2, "kv_num_heads": 2, "update_rule": "gated_delta", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 4, 10], + "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.41 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 4, 5], + "data": { "kind": "fillFloat32", "sinStep": 0.31, "cosStep": 0.17 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 4, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.47 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 2, 5, 4], + "data": { "kind": "fillFloat32", "scale": 0.25, "sinStep": 0.27, "cosStep": 0.13 } + }, + "decayT": { + "dtype": "float32", + "shape": [1, 4, 10], + "data": { "kind": "fillFloat32", "scale": 0.15, "offset": -0.25, "sinStep": 0.43, "cosStep": 0.61 } + }, + "betaT": { + "dtype": "float32", + "shape": [1, 4, 1], + "data": { "kind": "fillFloat32", "scale": 0.2, "offset": 0.45, "sinStep": 0.53, "cosStep": 0.29 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 4, 8] }, + "presentStateT": { "dtype": "float32", "shape": [1, 2, 5, 4] } + } + }, + { + "name": "gated_delta_vec4_gqa_headdim12_dv_tiled", + "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated_delta" }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [2, 6, 48], + "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.29 } + }, + "keyT": { + "dtype": "float32", + "shape": [2, 6, 24], + "data": { "kind": "fillFloat32", "sinStep": 0.15, "cosStep": 0.39 } + }, + "valueT": { + "dtype": "float32", + "shape": [2, 6, 24], + "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.51 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [2, 2, 12, 12], + "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.33, "cosStep": 0.21 } + }, + "decayT": { + "dtype": "float32", + "shape": [2, 6, 2], + "data": { "kind": "fillFloat32", "scale": 0.15, "offset": -0.2, "sinStep": 0.47, "cosStep": 0.31 } + }, + "betaT": { + "dtype": "float32", + "shape": [2, 6, 2], + "data": { "kind": "fillFloat32", "scale": 0.25, "offset": 0.5, "sinStep": 0.41, "cosStep": 0.23 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [2, 6, 48] }, + "presentStateT": { "dtype": "float32", "shape": [2, 2, 12, 12] } + } + }, + { + "name": "gated_delta_vec4_elementwise_decay_beta_dim1", + "attrs": { "q_num_heads": 3, "kv_num_heads": 1, "update_rule": "gated_delta", "scale": 0.4 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 5, 24], + "data": { "kind": "fillFloat32", "sinStep": 0.25, "cosStep": 0.37 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 5, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.49 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 5, 9], + "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.57 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 1, 8, 9], + "data": { "kind": "fillFloat32", "scale": 0.25, "sinStep": 0.21, "cosStep": 0.43 } + }, + "decayT": { + "dtype": "float32", + "shape": [1, 5, 8], + "data": { "kind": "fillFloat32", "scale": 0.15, "offset": -0.25, "sinStep": 0.59, "cosStep": 0.27 } + }, + "betaT": { + "dtype": "float32", + "shape": [1, 5, 1], + "data": { "kind": "fillFloat32", "scale": 0.2, "offset": 0.5, "sinStep": 0.61, "cosStep": 0.33 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 5, 27] }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 8, 9] } + } + }, + { + "name": "linear_zero_scalar_f16_seq128", + "provenance": { + "notes": "Compact sibling for the f16 linear-rule scalar benchmark; f16 query/state force the scalar implementation while seq=128 keeps the zero-state recurrence loop model-shaped." + }, + "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "scale": 0.25 }, + "inputs": { + "queryT": { + "dtype": "float16", + "shape": [1, 128, 64], + "data": { "kind": "fillFloat32", "sinStep": 0.012, "cosStep": 0.023, "scale": 0.03 } + }, + "keyT": { + "dtype": "float16", + "shape": [1, 128, 32], + "data": { "kind": "fillFloat32", "sinStep": 0.014, "cosStep": 0.019, "scale": 0.03 } + }, + "valueT": { + "dtype": "float16", + "shape": [1, 128, 32], + "data": { "kind": "fillFloat32", "sinStep": 0.008, "cosStep": 0.017, "scale": 0.03 } + } + }, + "outputs": { + "outputT": { "dtype": "float16", "shape": [1, 128, 64], "tolerance": 0.04, "relTolerance": 0.04 }, + "presentStateT": { "dtype": "float16", "shape": [1, 2, 16, 16], "tolerance": 0.04, "relTolerance": 0.04 } + } + }, + { + "name": "linear_zero_f16_seq128_offset_value_scale_lock", + "provenance": { + "notes": "Scale lock for the zero-past-state linear recurrence over 128 tokens (the \"zero\" in linear_zero names the absent past_state, not the output). Its sibling linear_zero_scalar_f16_seq128 drives q/k/v at amplitude 0.03 about zero, so the state is a 128-term random walk of zero-mean outer products and the output peaks at 1.7e-3 against a 0.04 absolute tolerance - blind to any multiplicative error below 24x, and 13.5x on present_state. Here the key carries a small positive DC and V oscillates about 0.5, so the accumulated state peaks at 1.5 and the output at 2.0; halving either output now scores ~30x the allowed error. Same shape, args and dtypes, so it still covers linear_zero_serial_small_dk, linear_zero_scalar and linear_zero_vec4." + }, + "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "scale": 0.25 }, + "inputs": { + "queryT": { + "dtype": "float16", + "shape": [1, 128, 64], + "data": { "kind": "fillFloat32", "sinStep": 0.012, "cosStep": 0.023, "scale": 0.2 } + }, + "keyT": { + "dtype": "float16", + "shape": [1, 128, 32], + "data": { "kind": "fillFloat32", "sinStep": 0.014, "cosStep": 0.019, "scale": 0.15, "offset": 0.02 } + }, + "valueT": { + "dtype": "float16", + "shape": [1, 128, 32], + "data": { "kind": "fillFloat32", "sinStep": 0.008, "cosStep": 0.017, "scale": 0.2, "offset": 0.5 } + } + }, + "outputs": { + "outputT": { "dtype": "float16", "shape": [1, 128, 64], "tolerance": 0.01, "relTolerance": 0.01 }, + "presentStateT": { "dtype": "float16", "shape": [1, 2, 16, 16], "tolerance": 0.01, "relTolerance": 0.01 } + } + }, + { + "name": "linear_state_scalar_f16_seq128", + "provenance": { + "notes": "Compact correctness sibling for the long supplied-state f16 pathology benchmark. It selects linear_state_serial_small_dk and validates that the initial state is incorporated rather than silently treated as zero." + }, + "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "scale": 0.25 }, + "inputs": { + "queryT": { + "dtype": "float16", + "shape": [1, 128, 64], + "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.021, "scale": 0.03 } + }, + "keyT": { + "dtype": "float16", + "shape": [1, 128, 32], + "data": { "kind": "fillFloat32", "sinStep": 0.015, "cosStep": 0.018, "scale": 0.03 } + }, + "valueT": { + "dtype": "float16", + "shape": [1, 128, 32], + "data": { "kind": "fillFloat32", "sinStep": 0.009, "cosStep": 0.016, "scale": 0.03 } + }, + "pastStateT": { + "dtype": "float16", + "shape": [1, 2, 16, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.011, "scale": 0.01 } + } + }, + "outputs": { + "outputT": { "dtype": "float16", "shape": [1, 128, 64], "tolerance": 0.04, "relTolerance": 0.04 }, + "presentStateT": { "dtype": "float16", "shape": [1, 2, 16, 16], "tolerance": 0.04, "relTolerance": 0.04 } + } + }, + { + "name": "linear_state_f16_seq128_offset_value_scale_lock", + "provenance": { + "notes": "Supplied-past-state arm of the same lock. linear_state_scalar_f16_seq128 claims to validate that the initial state is incorporated rather than treated as zero, but at amplitude 0.03 its output peaks at 4.4e-3 against a 0.04 absolute tolerance (9.1x blind) and the past state itself is scaled 0.01, so zeroing past_state entirely stays well inside the tolerance. With the past state at amplitude 0.3, a keyed DC and V about 0.5, the output peaks at 1.3 and the state at 1.5: dropping past_state now scores 11x the allowed error on the output and 35x on present_state, and halving either scores ~28x." + }, + "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "scale": 0.25 }, + "inputs": { + "queryT": { + "dtype": "float16", + "shape": [1, 128, 64], + "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.021, "scale": 0.2 } + }, + "keyT": { + "dtype": "float16", + "shape": [1, 128, 32], + "data": { "kind": "fillFloat32", "sinStep": 0.015, "cosStep": 0.018, "scale": 0.15, "offset": 0.02 } + }, + "valueT": { + "dtype": "float16", + "shape": [1, 128, 32], + "data": { "kind": "fillFloat32", "sinStep": 0.009, "cosStep": 0.016, "scale": 0.2, "offset": 0.5 } + }, + "pastStateT": { + "dtype": "float16", + "shape": [1, 2, 16, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.011, "scale": 0.3 } + } + }, + "outputs": { + "outputT": { "dtype": "float16", "shape": [1, 128, 64], "tolerance": 0.01, "relTolerance": 0.01 }, + "presentStateT": { "dtype": "float16", "shape": [1, 2, 16, 16], "tolerance": 0.01, "relTolerance": 0.01 } + } + }, + { + "name": "empty_zero_dim", + "provenance": { + "notes": "Zero-length batch axis of query/key/value (empty input). head/packed dims stay normal; output=[0,seq,qHeads*vHeadDim] and present_state=[0,kvHeads,headDimK,vHeadDim] => both EMPTY. linear rule, no past state." + }, + "attrs": { "q_num_heads": 2, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.7 }, + "inputs": { + "queryT": { "dtype": "float32", "shape": [0, 3, 4], "data": { "kind": "values", "values": [] } }, + "keyT": { "dtype": "float32", "shape": [0, 3, 2], "data": { "kind": "values", "values": [] } }, + "valueT": { "dtype": "float32", "shape": [0, 3, 3], "data": { "kind": "values", "values": [] } } + }, + "outputs": { + "outputT": { + "dtype": "float32", + "shape": [0, 3, 6], + "tolerance": 0, + "data": { "kind": "values", "values": [] } + }, + "presentStateT": { + "dtype": "float32", + "shape": [0, 1, 2, 3], + "tolerance": 0, + "data": { "kind": "values", "values": [] } + } + } + }, + { + "name": "empty_zero_dim_f16", + "provenance": { + "notes": "Zero-length batch axis of query/key/value (empty input). head/packed dims stay normal; output=[0,seq,qHeads*vHeadDim] and present_state=[0,kvHeads,headDimK,vHeadDim] => both EMPTY. linear rule, no past state. f16 variant." + }, + "attrs": { "q_num_heads": 2, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.7 }, + "inputs": { + "queryT": { "dtype": "float16", "shape": [0, 3, 4], "data": { "kind": "values", "values": [] } }, + "keyT": { "dtype": "float16", "shape": [0, 3, 2], "data": { "kind": "values", "values": [] } }, + "valueT": { "dtype": "float16", "shape": [0, 3, 3], "data": { "kind": "values", "values": [] } } + }, + "outputs": { + "outputT": { + "dtype": "float16", + "shape": [0, 3, 6], + "tolerance": 0, + "data": { "kind": "values", "values": [] } + }, + "presentStateT": { + "dtype": "float16", + "shape": [0, 1, 2, 3], + "tolerance": 0, + "data": { "kind": "values", "values": [] } + } + } + }, + { + "name": "linear_state_zero_seqlen_present_equals_past", + "provenance": { + "notes": "T=0 (zero-length seq axis) with a past_state. outputT=[1,0,8] is EMPTY but presentStateT=[1,1,4,4] is NON-empty, so allOutputsEmpty does NOT short-circuit and the kernel runs with seqLength==0: the recurrence loop runs zero times, so present_state must equal past_state bit-for-bit. linear rule, headDimK=4 (%4==0). Distinct from empty_zero_dim which zeros the batch axis." + }, + "attrs": { "q_num_heads": 2, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5 }, + "inputs": { + "queryT": { "dtype": "float32", "shape": [1, 0, 8], "data": { "kind": "values", "values": [] } }, + "keyT": { "dtype": "float32", "shape": [1, 0, 4], "data": { "kind": "values", "values": [] } }, + "valueT": { "dtype": "float32", "shape": [1, 0, 4], "data": { "kind": "values", "values": [] } }, + "pastStateT": { "dtype": "float32", "shape": [1, 1, 4, 4], "data": { "kind": "constant", "value": 0.1 } } + }, + "outputs": { + "outputT": { + "dtype": "float32", + "shape": [1, 0, 8], + "tolerance": 0, + "data": { "kind": "values", "values": [] } + }, + "presentStateT": { + "dtype": "float32", + "shape": [1, 1, 4, 4], + "tolerance": 0, + "data": { "kind": "constant", "value": 0.1 } + } + } + }, + { + "name": "linear_zero_zero_seqlen_present_all_zeros", + "provenance": { + "notes": "T=0 (zero-length seq axis), no past_state. outputT=[1,0,8] EMPTY, presentStateT=[1,1,4,4] NON-empty must be all zeros (state zero-initialized, zero-trip recurrence). linear rule, headDimK=4 (%4==0). Exercises zero-init path with a live kernel dispatch." + }, + "attrs": { "q_num_heads": 2, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5 }, + "inputs": { + "queryT": { "dtype": "float32", "shape": [1, 0, 8], "data": { "kind": "values", "values": [] } }, + "keyT": { "dtype": "float32", "shape": [1, 0, 4], "data": { "kind": "values", "values": [] } }, + "valueT": { "dtype": "float32", "shape": [1, 0, 4], "data": { "kind": "values", "values": [] } } + }, + "outputs": { + "outputT": { + "dtype": "float32", + "shape": [1, 0, 8], + "tolerance": 0, + "data": { "kind": "values", "values": [] } + }, + "presentStateT": { + "dtype": "float32", + "shape": [1, 1, 4, 4], + "tolerance": 0, + "data": { "kind": "constant", "value": 0.0 } + } + } + }, + { + "name": "gated_delta_scalar_headdimk_gt_128_partial_dv_tile", + "provenance": { + "notes": "headDimK=130 (not %4 -> scalar variant; >128 so WG=256 with 130 active lanes -> tid dv_tiles=3 with a partial last tile (2/4 valid) exercising dv_start+j manifest derive.effRule defaults to gated_delta (host and reference agree). Requires decay+beta+past_state. headDimK=4 (%4==0) so vec4 path. Verifies the default-rule contract across manifest/host/reference." + }, + "attrs": { "q_num_heads": 2, "kv_num_heads": 1 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 3, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.25, "cosStep": 0.37 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 3, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.49 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 3, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.57 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 1, 4, 4], + "data": { "kind": "fillFloat32", "scale": 0.25, "sinStep": 0.21, "cosStep": 0.43 } + }, + "decayT": { + "dtype": "float32", + "shape": [1, 3, 1], + "data": { "kind": "fillFloat32", "scale": 0.15, "offset": -0.25, "sinStep": 0.59, "cosStep": 0.27 } + }, + "betaT": { + "dtype": "float32", + "shape": [1, 3, 1], + "data": { "kind": "fillFloat32", "scale": 0.2, "offset": 0.5, "sinStep": 0.61, "cosStep": 0.33 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 3, 8], "tolerance": 0.0005 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 4, 4], "tolerance": 0.0005 } + } + }, + { + "name": "gated_delta_f32_dk128_dv128_compact", + "provenance": { + "notes": "Compact correctness lock for the Bonsai-shaped dK=dV=128 gated-delta benchmark. qHeads=4, kvHeads=2 and seq=2 bound CPU reference work while preserving GQA state sharing and the f32 vec4 path." + }, + "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated_delta", "scale": 0.08838834764831845 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 512], + "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.023, "scale": 0.03 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 256], + "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.029, "scale": 0.03 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 256], + "data": { "kind": "fillFloat32", "sinStep": 0.009, "cosStep": 0.043, "scale": 0.03 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 2, 128, 128], + "data": { "kind": "fillFloat32", "sinStep": 0.019, "cosStep": 0.037, "scale": 0.01 } + }, + "decayT": { + "dtype": "float32", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.053, "scale": 0.08, "offset": -0.2 } + }, + "betaT": { + "dtype": "float32", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.037, "cosStep": 0.019, "scale": 0.08, "offset": 0.5 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 512], "tolerance": 0.0008, "relTolerance": 0.0008 }, + "presentStateT": { "dtype": "float32", "shape": [1, 2, 128, 128], "tolerance": 0.0008, "relTolerance": 0.0008 } + } + }, + { + "name": "gated_delta_f32_dk128_dv128_offset_value_scale_lock", + "provenance": { + "notes": "Scale lock for the f32 dK=dV=128 gated-delta route. Scaling the key to |k|^2 about 0.7 makes the delta correction material, while V around 1.0 and an O(1) query keep outputT well-conditioned. This makes a missing or duplicated decay, incorrect beta, or uniform output scaling observable on outputT rather than relying only on presentStateT." + }, + "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated_delta", "scale": 0.08838834764831845 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 512], + "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.023, "scale": 1.0 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 256], + "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.029, "scale": 0.1 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 256], + "data": { "kind": "fillFloat32", "sinStep": 0.009, "cosStep": 0.043, "scale": 0.3, "offset": 1.0 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 2, 128, 128], + "data": { "kind": "fillFloat32", "sinStep": 0.019, "cosStep": 0.037, "scale": 0.3 } + }, + "decayT": { + "dtype": "float32", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.053, "scale": 0.08, "offset": -0.2 } + }, + "betaT": { + "dtype": "float32", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.037, "cosStep": 0.019, "scale": 0.08, "offset": 0.5 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 512], "tolerance": 0.001, "relTolerance": 0.001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 2, 128, 128], "tolerance": 0.001, "relTolerance": 0.001 } + } + }, + { + "name": "gated_delta_f16_dk128_dv128_compact", + "provenance": { + "notes": "Float16 sibling of the dK=dV=128 gated-delta lock. It preserves the model-shaped head dimensions and scalar f16 route while remaining small enough for CPU reference validation." + }, + "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated_delta", "scale": 0.08838834764831845 }, + "inputs": { + "queryT": { + "dtype": "float16", + "shape": [1, 2, 512], + "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.023, "scale": 0.03 } + }, + "keyT": { + "dtype": "float16", + "shape": [1, 2, 256], + "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.029, "scale": 0.03 } + }, + "valueT": { + "dtype": "float16", + "shape": [1, 2, 256], + "data": { "kind": "fillFloat32", "sinStep": 0.009, "cosStep": 0.043, "scale": 0.03 } + }, + "pastStateT": { + "dtype": "float16", + "shape": [1, 2, 128, 128], + "data": { "kind": "fillFloat32", "sinStep": 0.019, "cosStep": 0.037, "scale": 0.01 } + }, + "decayT": { + "dtype": "float16", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.053, "scale": 0.08, "offset": -0.2 } + }, + "betaT": { + "dtype": "float16", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.037, "cosStep": 0.019, "scale": 0.08, "offset": 0.5 } + } + }, + "outputs": { + "outputT": { "dtype": "float16", "shape": [1, 2, 512], "tolerance": 0.08, "relTolerance": 0.08 }, + "presentStateT": { "dtype": "float16", "shape": [1, 2, 128, 128], "tolerance": 0.08, "relTolerance": 0.08 } + } + }, + { + "name": "gated_delta_f16_dk128_dv128_offset_value_scale_lock", + "provenance": { + "notes": "Float16 dK=dV=128 gated-delta scale lock covering both vec4 and scalar state routes. Scaling the key to |k|^2 about 0.7 makes the delta correction material, while V around 1.0 and an O(1) query keep outputs well-conditioned. Missing or duplicated decay, incorrect beta, and uniform output scaling therefore exceed tolerance on both output and state." + }, + "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated_delta", "scale": 0.08838834764831845 }, + "inputs": { + "queryT": { + "dtype": "float16", + "shape": [1, 2, 512], + "data": { "kind": "fillFloat32", "sinStep": 0.017, "cosStep": 0.023, "scale": 1.0 } + }, + "keyT": { + "dtype": "float16", + "shape": [1, 2, 256], + "data": { "kind": "fillFloat32", "sinStep": 0.013, "cosStep": 0.029, "scale": 0.1 } + }, + "valueT": { + "dtype": "float16", + "shape": [1, 2, 256], + "data": { "kind": "fillFloat32", "sinStep": 0.009, "cosStep": 0.043, "scale": 0.3, "offset": 1.0 } + }, + "pastStateT": { + "dtype": "float16", + "shape": [1, 2, 128, 128], + "data": { "kind": "fillFloat32", "sinStep": 0.019, "cosStep": 0.037, "scale": 0.3 } + }, + "decayT": { + "dtype": "float16", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.029, "cosStep": 0.053, "scale": 0.08, "offset": -0.2 } + }, + "betaT": { + "dtype": "float16", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.037, "cosStep": 0.019, "scale": 0.08, "offset": 0.5 } + } + }, + "outputs": { + "outputT": { "dtype": "float16", "shape": [1, 2, 512], "tolerance": 0.01, "relTolerance": 0.01 }, + "presentStateT": { "dtype": "float16", "shape": [1, 2, 128, 128], "tolerance": 0.01, "relTolerance": 0.01 } + } + }, + { + "name": "linear_zero_vec4_dk256_wg_gt_subgroup", + "provenance": { + "source": "onnxruntime/contrib_ops/webgpu/bert/linear_attention.wgsl.template", + "test": "two-level subgroup reduction (PR #28412)", + "notes": "The dk reduction must span the whole workgroup. head_dim_k/4 lanes exceed the subgroup size here, so a bare subgroupAdd only sums one subgroup's partial dot products." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear" }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 256], + "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.29 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 256], + "data": { "kind": "fillFloat32", "sinStep": 0.15, "cosStep": 0.39 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.51 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 8], "tolerance": 0.001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 256, 8], "tolerance": 0.001 } + } + }, + { + "name": "gated_delta_vec4_dk132_wg_gt_subgroup", + "provenance": { + "source": "onnxruntime/contrib_ops/webgpu/bert/linear_attention.wgsl.template", + "test": "two-level subgroup reduction (PR #28412)", + "notes": "The dk reduction must span the whole workgroup. head_dim_k/4 lanes exceed the subgroup size here, so a bare subgroupAdd only sums one subgroup's partial dot products." + }, + "attrs": { "q_num_heads": 2, "kv_num_heads": 1, "update_rule": "gated_delta" }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 3, 264], + "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.29 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 3, 132], + "data": { "kind": "fillFloat32", "sinStep": 0.15, "cosStep": 0.39 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 3, 6], + "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.51 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 1, 132, 6], + "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.33, "cosStep": 0.21 } + }, + "decayT": { + "dtype": "float32", + "shape": [1, 3, 1], + "data": { "kind": "fillFloat32", "scale": 0.15, "offset": -0.2, "sinStep": 0.47, "cosStep": 0.31 } + }, + "betaT": { + "dtype": "float32", + "shape": [1, 3, 1], + "data": { "kind": "fillFloat32", "scale": 0.3, "offset": 0.4, "sinStep": 0.23, "cosStep": 0.17 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 3, 12], "tolerance": 0.001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 132, 6], "tolerance": 0.001 } + } + }, + { + "name": "linear_zero_vec4_dk2048_wide_workgroup", + "requires": { "limits": { "maxComputeInvocationsPerWorkgroup": 512, "maxComputeWorkgroupSizeX": 512 } }, + "provenance": { + "source": "onnxruntime/contrib_ops/webgpu/bert/linear_attention.cc", + "test": "head-size-driven workgroup sizing", + "notes": "One vec4 lane per four dk rows, so head_dim_k = 2048 needs 512 lanes. The width was pinned to 256 and the head dim to 1024 to match; both are now taken from the device, which is what the second half of the old guard already did. A device that cannot run 512 invocations per workgroup still cannot serve this shape, so the case declares that limit rather than claiming to be universal. Query and key are positive cycles of length 5 and 6 rather than sinusoids: a signed 2048-term dot product cancels down to ~1e-4, where no tolerance the f32 summation order allows can see a scale error. Both cycle lengths are coprime with the four-lane vec4 stride, so every lane still sees every value." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear" }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 1, 2048], + "data": { "kind": "cycle", "values": [0.25, 0.5, 0.75, 1.0, 0.375] } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 1, 2048], + "data": { "kind": "cycle", "values": [0.5, 0.25, 1.0, 0.125, 0.75, 0.625] } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 1, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.51 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 1, 2], "tolerance": 0.001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 2048, 2], "tolerance": 0.001 } + } + }, + { + "name": "linear_zero_scalar_dk17_above_serial_cap_unaligned", + "provenance": { + "notes": "headDimK=17 is above serialHeadDimFits (dk<=16) and not %4, so neither linear_zero_serial_small_dk nor linear_zero_vec4 is eligible and the scalar zero-state kernel must run. WG=pow2ceil(17)=32 with 17 active lanes exercises the masked dk reduction; TILE_V=2 covers head_dim_v in one tile." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 17], + "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.29 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 17], + "data": { "kind": "fillFloat32", "sinStep": 0.15, "cosStep": 0.39 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.51 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 2], "tolerance": 0.001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 17, 2], "tolerance": 0.001 } + } + }, + { + "name": "linear_state_scalar_dk17_above_serial_cap_unaligned", + "provenance": { + "notes": "Supplied-state sibling of the dk=17 scalar lock: headDimK=17 defeats serialHeadDimFits (dk<=16) and the %4 vec4 guard, so the scalar kernel runs with a past state and must fold it into the recurrence instead of starting from zeros." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 17], + "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.33 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 17], + "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.27 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.09, "cosStep": 0.43 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 1, 17, 2], + "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.19, "cosStep": 0.37 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 2], "tolerance": 0.001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 17, 2], "tolerance": 0.001 } + } + }, + { + "name": "linear_state_vec4_dk20_above_serial_cap", + "provenance": { + "notes": "headDimK=20 is %4 (vec4-eligible) but above serialHeadDimFits (dk<=16), so the supplied-state vec4 kernel wins instead of linear_state_serial_small_dk. WG=pow2ceil(ceil(20/4))=8 leaves 3 idle lanes, and head_dim_v=3 exercises a partial dv tile on both output and present_state." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 20], + "data": { "kind": "fillFloat32", "sinStep": 0.25, "cosStep": 0.37 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 20], + "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.49 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 3], + "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.57 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 1, 20, 3], + "data": { "kind": "fillFloat32", "scale": 0.25, "sinStep": 0.21, "cosStep": 0.43 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 3], "tolerance": 0.001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 20, 3], "tolerance": 0.001 } + } + }, + { + "name": "linear_state_window2_pinned", + "provenance": { + "notes": "Hand-computed linear-rule window. state += k (outer) v each token, so the state after each token is exact in f32; slot j must hold the state after token (seq - W + j), slot W-1 must equal the unwindowed present_state, and slots below max(0, W - seq) must be zero. A kernel that only wrote the final state would leave the earlier slots untouched." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 1, "state_window": 2 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 3, 2], + "data": { "kind": "values", "values": [1.0, 0.0, 0.0, 1.0, 1.0, 1.0] } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 3, 2], + "data": { "kind": "values", "values": [1.0, 0.0, 0.0, 1.0, 1.0, 1.0] } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 3, 2], + "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0] } + } + }, + "outputs": { + "outputT": { + "dtype": "float32", + "shape": [1, 3, 2], + "tolerance": 0.00001, + "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 14.0, 18.0] } + }, + "presentStateT": { + "dtype": "float32", + "shape": [2, 1, 1, 2, 2], + "tolerance": 0.00001, + "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 6.0, 8.0, 8.0, 10.0] } + } + } + }, + { + "name": "linear_state_window4_longer_than_sequence", + "provenance": { + "notes": "Hand-computed linear-rule window. state += k (outer) v each token, so the state after each token is exact in f32; slot j must hold the state after token (seq - W + j), slot W-1 must equal the unwindowed present_state, and slots below max(0, W - seq) must be zero. A kernel that only wrote the final state would leave the earlier slots untouched." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 1, "state_window": 4 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 3, 2], + "data": { "kind": "values", "values": [1.0, 0.0, 0.0, 1.0, 1.0, 1.0] } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 3, 2], + "data": { "kind": "values", "values": [1.0, 0.0, 0.0, 1.0, 1.0, 1.0] } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 3, 2], + "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 5.0, 6.0] } + } + }, + "outputs": { + "outputT": { + "dtype": "float32", + "shape": [1, 3, 2], + "tolerance": 0.00001, + "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 14.0, 18.0] } + }, + "presentStateT": { + "dtype": "float32", + "shape": [4, 1, 1, 2, 2], + "tolerance": 0.00001, + "data": { + "kind": "values", + "values": [0.0, 0.0, 0.0, 0.0, 1.0, 2.0, 0.0, 0.0, 1.0, 2.0, 3.0, 4.0, 6.0, 8.0, 8.0, 10.0] + } + } + } + }, + { + "name": "linear_state_scalar_dk17_window3", + "provenance": { + "notes": "Windowed twin of linear_state_scalar_dk17_above_serial_cap_unaligned, which is the case that selects this variant. The pinned serial window pair anchors the reference; this one checks that the same variant's per-token snapshot and slot indexing agree with it." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5, "state_window": 3 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 17], + "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.33 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 17], + "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.27 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.09, "cosStep": 0.43 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [3, 1, 1, 17, 2], + "data": { "kind": "fillFloat32", "scale": 0.2, "sinStep": 0.19, "cosStep": 0.37 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 2], "tolerance": 0.001 }, + "presentStateT": { "dtype": "float32", "shape": [3, 1, 1, 17, 2], "tolerance": 0.001 } + } + }, + { + "name": "linear_state_vec4_dk20_window2", + "provenance": { + "notes": "Windowed twin of linear_state_vec4_dk20_above_serial_cap, which is the case that selects this variant. The pinned serial window pair anchors the reference; this one checks that the same variant's per-token snapshot and slot indexing agree with it." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5, "state_window": 2 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 20], + "data": { "kind": "fillFloat32", "sinStep": 0.25, "cosStep": 0.37 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 20], + "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.49 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 3], + "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.57 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [2, 1, 1, 20, 3], + "data": { "kind": "fillFloat32", "scale": 0.25, "sinStep": 0.21, "cosStep": 0.43 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 3], "tolerance": 0.001 }, + "presentStateT": { "dtype": "float32", "shape": [2, 1, 1, 20, 3], "tolerance": 0.001 } + } + }, + { + "name": "linear_state_serial_window2_past_slot", + "provenance": { + "notes": "A small-head-dimension serial recurrence with a past state and state_window=2 must gather slot stateWindow-1 rather than the unwindowed offset." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5, "state_window": 2 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "values", "values": [1.0, 0.0, 0.5, -0.5, 0.5, 1.0, -0.5, 0.0] } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "values", "values": [0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 1.0, 0.5] } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "values", "values": [1.0, 2.0, 3.0, 4.0, 2.0, 1.0, 0.0, 3.0] } + }, + "pastStateT": { "dtype": "float32", "shape": [2, 1, 1, 4, 4], "data": { "kind": "constant", "value": 0.1 } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 4], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [2, 1, 1, 4, 4], "tolerance": 0.0001 } + } + }, + { + "name": "linear_zero_vec4_window2", + "provenance": { + "notes": "Windowed zero-state coverage for the linear vec4 route, including per-token present-state slots." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5, "state_window": 2 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.17, "cosStep": 0.31 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.11, "cosStep": 0.23 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "scale": 0.5, "sinStep": 0.07, "cosStep": 0.41 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 2], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [2, 1, 1, 4, 2], "tolerance": 0.0001 } + } + }, + { + "name": "gated_zero_window2_all_routes", + "provenance": { "notes": "Windowed zero-state coverage for every eligible gated serial, scalar, and vec4 route." }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated", "scale": 0.5, "state_window": 2 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.17, "cosStep": 0.31 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.11, "cosStep": 0.23 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "scale": 0.5, "sinStep": 0.07, "cosStep": 0.41 } + }, + "decayT": { "dtype": "float32", "shape": [1, 2, 1], "data": { "kind": "values", "values": [-0.2, -0.1] } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 2], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [2, 1, 1, 4, 2], "tolerance": 0.0001 } + } + }, + { + "name": "gated_state_window2_all_routes", + "provenance": { + "notes": "Windowed carried-state coverage for every eligible gated serial, scalar, and vec4 route." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated", "scale": 0.5, "state_window": 2 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.17, "cosStep": 0.31 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.11, "cosStep": 0.23 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "scale": 0.5, "sinStep": 0.07, "cosStep": 0.41 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [2, 1, 1, 4, 2], + "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.13, "cosStep": 0.19 } + }, + "decayT": { "dtype": "float32", "shape": [1, 2, 1], "data": { "kind": "values", "values": [-0.2, -0.1] } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 2], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [2, 1, 1, 4, 2], "tolerance": 0.0001 } + } + }, + { + "name": "delta_zero_window2_all_routes", + "provenance": { "notes": "Windowed zero-state coverage for every eligible delta scalar and vec4 route." }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "delta", "scale": 0.5, "state_window": 2 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.17, "cosStep": 0.31 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.11, "cosStep": 0.23 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "scale": 0.5, "sinStep": 0.07, "cosStep": 0.41 } + }, + "betaT": { "dtype": "float32", "shape": [1, 2, 1], "data": { "kind": "values", "values": [0.7, 0.4] } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 2], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [2, 1, 1, 4, 2], "tolerance": 0.0001 } + } + }, + { + "name": "delta_state_window2_all_routes", + "provenance": { "notes": "Windowed carried-state coverage for every eligible delta scalar and vec4 route." }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "delta", "scale": 0.5, "state_window": 2 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.17, "cosStep": 0.31 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.11, "cosStep": 0.23 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "scale": 0.5, "sinStep": 0.07, "cosStep": 0.41 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [2, 1, 1, 4, 2], + "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.13, "cosStep": 0.19 } + }, + "betaT": { "dtype": "float32", "shape": [1, 2, 1], "data": { "kind": "values", "values": [0.7, 0.4] } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 2], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [2, 1, 1, 4, 2], "tolerance": 0.0001 } + } + }, + { + "name": "gated_delta_zero_window2_all_routes", + "provenance": { + "notes": "Windowed zero-state coverage for every eligible gated-delta serial, scalar, and vec4 route." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated_delta", "scale": 0.5, "state_window": 2 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.17, "cosStep": 0.31 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.11, "cosStep": 0.23 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "scale": 0.5, "sinStep": 0.07, "cosStep": 0.41 } + }, + "decayT": { "dtype": "float32", "shape": [1, 2, 1], "data": { "kind": "values", "values": [-0.2, -0.1] } }, + "betaT": { "dtype": "float32", "shape": [1, 2, 1], "data": { "kind": "values", "values": [0.7, 0.4] } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 2], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [2, 1, 1, 4, 2], "tolerance": 0.0001 } + } + }, + { + "name": "gated_delta_state_window2_all_routes", + "provenance": { + "notes": "Windowed carried-state coverage for every eligible gated-delta serial, scalar, and vec4 route." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated_delta", "scale": 0.5, "state_window": 2 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "scale": 0.4, "sinStep": 0.17, "cosStep": 0.31 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "scale": 0.3, "sinStep": 0.11, "cosStep": 0.23 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 2], + "data": { "kind": "fillFloat32", "scale": 0.5, "sinStep": 0.07, "cosStep": 0.41 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [2, 1, 1, 4, 2], + "data": { "kind": "fillFloat32", "scale": 0.1, "sinStep": 0.13, "cosStep": 0.19 } + }, + "decayT": { "dtype": "float32", "shape": [1, 2, 1], "data": { "kind": "values", "values": [-0.2, -0.1] } }, + "betaT": { "dtype": "float32", "shape": [1, 2, 1], "data": { "kind": "values", "values": [0.7, 0.4] } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 2], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [2, 1, 1, 4, 2], "tolerance": 0.0001 } + } + }, + { + "name": "inverse_gqa_linear_zero_q2_kv4", + "provenance": { + "notes": "Inverse GQA: kvNumHeads exceeds qNumHeads, so the output carries one head per KV head rather than per query head and several KV heads share a query head (KV head h reads query head floor(h * qNumHeads / kvNumHeads)). Linear rule with no carried state; the four KV heads pair onto query heads 0, 0, 1, 1." + }, + "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "scale": 0.7 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 3, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 3, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 3, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 3, 16], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 4, 4, 4], "tolerance": 0.0001 } + } + }, + { + "name": "inverse_gqa_linear_state_q2_kv4", + "provenance": { + "notes": "Inverse GQA: kvNumHeads exceeds qNumHeads, so the output carries one head per KV head rather than per query head and several KV heads share a query head (KV head h reads query head floor(h * qNumHeads / kvNumHeads)). Linear rule continuing from a carried state, so the state layout is exercised on the KV-head axis while the query axis is narrower." + }, + "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "linear", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 3, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.29 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 3, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.37 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 3, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.11 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 4, 4, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.31, "cosStep": 0.07 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 3, 16], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 4, 4, 4], "tolerance": 0.0001 } + } + }, + { + "name": "inverse_gqa_gated_delta_state_q2_kv4", + "provenance": { + "notes": "Inverse GQA: kvNumHeads exceeds qNumHeads, so the output carries one head per KV head rather than per query head and several KV heads share a query head (KV head h reads query head floor(h * qNumHeads / kvNumHeads)). The gated-delta rule, which is the regime the inverse layout exists for." + }, + "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "gated_delta", "scale": 0.6 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 3, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.13 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 3, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.09, "cosStep": 0.33 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 3, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.27 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 4, 4, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.29, "cosStep": 0.05 } + }, + "decayT": { "dtype": "float32", "shape": [1, 3, 4], "data": { "kind": "linspace", "start": -0.9, "end": -0.1 } }, + "betaT": { "dtype": "float32", "shape": [1, 3, 4], "data": { "kind": "linspace", "start": 0.2, "end": 0.8 } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 3, 16], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 4, 4, 4], "tolerance": 0.0001 } + } + }, + { + "name": "inverse_gqa_gated_delta_state_q4_kv8_dk8", + "provenance": { + "notes": "Inverse GQA: kvNumHeads exceeds qNumHeads, so the output carries one head per KV head rather than per query head and several KV heads share a query head (KV head h reads query head floor(h * qNumHeads / kvNumHeads)). A head dimension divisible by four and eight KV heads over four query heads, so the vectorized kernel runs the inverse mapping too." + }, + "attrs": { "q_num_heads": 4, "kv_num_heads": 8, "update_rule": "gated_delta", "scale": 0.4 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 4, 32], + "data": { "kind": "fillFloat32", "sinStep": 0.23, "cosStep": 0.17 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 4, 64], + "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.29 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 4, 64], + "data": { "kind": "fillFloat32", "sinStep": 0.31, "cosStep": 0.19 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 8, 8, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.43 } + }, + "decayT": { + "dtype": "float32", + "shape": [1, 4, 8], + "data": { "kind": "linspace", "start": -1.1, "end": -0.05 } + }, + "betaT": { "dtype": "float32", "shape": [1, 4, 8], "data": { "kind": "linspace", "start": 0.15, "end": 0.85 } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 4, 64], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 8, 8, 8], "tolerance": 0.0001 } + } + }, + { + "name": "inverse_gqa_gated_delta_state_dk32_tiled", + "provenance": { + "notes": "Inverse GQA with a head dimension of 32, above the serial kernel's ceiling of 16, so the tiled scalar and vectorized kernels are the ones selected rather than merely eligible." + }, + "attrs": { "q_num_heads": 2, "kv_num_heads": 4, "update_rule": "gated_delta", "scale": 0.35 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 64], + "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.37 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 128], + "data": { "kind": "fillFloat32", "sinStep": 0.29, "cosStep": 0.11 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 32], + "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.23 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 4, 32, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.31 } + }, + "decayT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "linspace", "start": -0.8, "end": -0.15 } + }, + "betaT": { "dtype": "float32", "shape": [1, 2, 4], "data": { "kind": "linspace", "start": 0.25, "end": 0.75 } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 32], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 4, 32, 8], "tolerance": 0.0001 } + } + }, + { + "name": "gated_zero_state_without_past", + "provenance": { + "notes": "The public schema defaults an omitted past_state to zeros for every update rule; this covers the gated path without a recurrent-state input." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.4 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23, "scale": 0.3 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 3], + "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41, "scale": 0.5 } + }, + "decayT": { "dtype": "float32", "shape": [1, 2, 1], "data": { "kind": "values", "values": [-0.2, -0.1] } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 3], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 4, 3], "tolerance": 0.0001 } + } + }, + { + "name": "gated_delta_zero_state_without_past", + "provenance": { + "notes": "Covers the default gated-delta recurrence with an omitted past_state, which is initialized to zeros by the public contract." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated_delta", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.4 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23, "scale": 0.3 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 3], + "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41, "scale": 0.5 } + }, + "decayT": { "dtype": "float32", "shape": [1, 2, 1], "data": { "kind": "values", "values": [-0.2, -0.1] } }, + "betaT": { "dtype": "float32", "shape": [1, 2, 1], "data": { "kind": "values", "values": [0.7, 0.4] } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 3], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 4, 3], "tolerance": 0.0001 } + } + }, + { + "name": "delta_with_initial_state", + "provenance": { + "notes": "Covers the schema-valid delta recurrence with a supplied past_state; providers consume the same optional state for all four update rules." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "delta", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.4 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23, "scale": 0.3 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 3], + "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41, "scale": 0.5 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 1, 4, 3], + "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.19, "scale": 0.1 } + }, + "betaT": { "dtype": "float32", "shape": [1, 2, 1], "data": { "kind": "values", "values": [0.7, 0.4] } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 3], "tolerance": 0.0001 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 4, 3], "tolerance": 0.0001 } + } + }, + { + "name": "linear_f32_with_f16_state", + "provenance": { + "notes": "Connects the independently typed T and S schema variables: float32 activations consume and return a float16 recurrent state." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.3 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23, "scale": 0.3 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41, "scale": 0.3 } + }, + "pastStateT": { + "dtype": "float16", + "shape": [1, 1, 4, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.19, "scale": 0.1 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 4], "tolerance": 0.0002 }, + "presentStateT": { "dtype": "float16", "shape": [1, 1, 4, 4], "tolerance": 0.002, "relTolerance": 0.002 } + } + }, + { + "name": "linear_f16_with_f32_state", + "provenance": { + "notes": "Connects the independently typed T and S schema variables in the opposite direction: float16 activations consume and return a float32 recurrent state." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.5 }, + "inputs": { + "queryT": { + "dtype": "float16", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.3 } + }, + "keyT": { + "dtype": "float16", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23, "scale": 0.3 } + }, + "valueT": { + "dtype": "float16", + "shape": [1, 2, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41, "scale": 0.3 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 1, 4, 4], + "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.19, "scale": 0.1 } + } + }, + "outputs": { + "outputT": { "dtype": "float16", "shape": [1, 2, 4], "tolerance": 0.002, "relTolerance": 0.002 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 4, 4], "tolerance": 0.0002 } + } + }, + { + "name": "gated_delta_zero_scalar_dk17_no_past", + "provenance": { + "notes": "Omitted past_state with headDimK=17 crosses the serial-kernel ceiling and is not divisible by four, selecting the scalar zero-state recurrence rather than a state-bearing or vec4 sibling." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated_delta", "scale": 0.25 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 17], + "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.3 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 17], + "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23, "scale": 0.3 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 5], + "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41, "scale": 0.3 } + }, + "decayT": { "dtype": "float32", "shape": [1, 2, 1], "data": { "kind": "values", "values": [-0.2, -0.1] } }, + "betaT": { "dtype": "float32", "shape": [1, 2, 1], "data": { "kind": "values", "values": [0.7, 0.4] } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 5], "tolerance": 0.0002 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 17, 5], "tolerance": 0.0002 } + } + }, + { + "name": "gated_delta_zero_vec4_dk20_no_past", + "provenance": { + "notes": "Omitted past_state with headDimK=20 crosses the serial-kernel ceiling while retaining vec4 alignment, selecting the vector zero-state recurrence and a partial dV tile." + }, + "attrs": { "q_num_heads": 1, "kv_num_heads": 1, "update_rule": "gated_delta", "scale": 0.25 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 2, 20], + "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.31, "scale": 0.3 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 2, 20], + "data": { "kind": "fillFloat32", "sinStep": 0.11, "cosStep": 0.23, "scale": 0.3 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 2, 6], + "data": { "kind": "fillFloat32", "sinStep": 0.07, "cosStep": 0.41, "scale": 0.3 } + }, + "decayT": { "dtype": "float32", "shape": [1, 2, 1], "data": { "kind": "values", "values": [-0.2, -0.1] } }, + "betaT": { "dtype": "float32", "shape": [1, 2, 1], "data": { "kind": "values", "values": [0.7, 0.4] } } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 2, 6], "tolerance": 0.0002 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 20, 6], "tolerance": 0.0002 } + } + }, + { + "name": "gated_zero_chunked_seq1024", + "provenance": { + "notes": "Selects the chunked prefill decomposition, which needs seqLength >= 1024. Compact head dims keep the fixture small while covering the gated rule with a zero entry state and an elementwise decay gate." + }, + "attrs": { "q_num_heads": 2, "kv_num_heads": 1, "update_rule": "gated", "scale": 0.35 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 1024, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.12, "cosStep": 0.24000000000000002, "scale": 0.4 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 1024, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.08, "cosStep": 0.32, "scale": 0.4 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 1024, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.14, "cosStep": 0.2, "scale": 0.4 } + }, + "decayT": { + "dtype": "float32", + "shape": [1, 1024, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.18000000000000002, "cosStep": 0.42, "scale": 0.1 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 1024, 16], "tolerance": 0.0005, "relTolerance": 0.0005 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 8, 8], "tolerance": 0.0005, "relTolerance": 0.0005 } + } + }, + { + "name": "gated_state_chunked_seq1024", + "provenance": { + "notes": "Selects the chunked prefill decomposition, which needs seqLength >= 1024. Compact head dims keep the fixture small while covering the gated rule with a supplied entry state and a per-head decay gate." + }, + "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated", "scale": 0.35 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 1024, 32], + "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.25, "scale": 0.4 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 1024, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.09000000000000001, "cosStep": 0.33, "scale": 0.4 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 1024, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.15, "cosStep": 0.21, "scale": 0.4 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 2, 8, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.31, "cosStep": 0.39, "scale": 0.2 } + }, + "decayT": { + "dtype": "float32", + "shape": [1, 1024, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.43, "scale": 0.1 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 1024, 32], "tolerance": 0.0005, "relTolerance": 0.0005 }, + "presentStateT": { "dtype": "float32", "shape": [1, 2, 8, 8], "tolerance": 0.0005, "relTolerance": 0.0005 } + } + }, + { + "name": "delta_zero_chunked_seq1024", + "provenance": { + "notes": "Selects the chunked prefill decomposition, which needs seqLength >= 1024. Compact head dims keep the fixture small while covering the delta rule with a zero entry state and a shared beta column." + }, + "attrs": { "q_num_heads": 2, "kv_num_heads": 1, "update_rule": "delta", "scale": 0.35 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 1024, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.14, "cosStep": 0.26, "scale": 0.4 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 1024, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.1, "cosStep": 0.33999999999999997, "scale": 0.4 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 1024, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.16, "cosStep": 0.22, "scale": 0.4 } + }, + "betaT": { + "dtype": "float32", + "shape": [1, 1024, 1], + "data": { "kind": "fillFloat32", "sinStep": 0.26, "cosStep": 0.31999999999999995, "scale": 0.3 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 1024, 16], "tolerance": 0.0005, "relTolerance": 0.0005 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 8, 8], "tolerance": 0.0005, "relTolerance": 0.0005 } + } + }, + { + "name": "delta_state_chunked_seq1024", + "provenance": { + "notes": "Selects the chunked prefill decomposition, which needs seqLength >= 1024. Compact head dims keep the fixture small while covering the delta rule with a supplied entry state and per-head beta." + }, + "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "delta", "scale": 0.35 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 1024, 32], + "data": { "kind": "fillFloat32", "sinStep": 0.15, "cosStep": 0.27, "scale": 0.4 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 1024, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.11000000000000001, "cosStep": 0.35, "scale": 0.4 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 1024, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.17, "cosStep": 0.23, "scale": 0.4 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 2, 8, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.32999999999999996, "cosStep": 0.41, "scale": 0.2 } + }, + "betaT": { + "dtype": "float32", + "shape": [1, 1024, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.27, "cosStep": 0.32999999999999996, "scale": 0.3 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 1024, 32], "tolerance": 0.0005, "relTolerance": 0.0005 }, + "presentStateT": { "dtype": "float32", "shape": [1, 2, 8, 8], "tolerance": 0.0005, "relTolerance": 0.0005 } + } + }, + { + "name": "gated_delta_zero_chunked_seq1024", + "provenance": { + "notes": "Selects the chunked prefill decomposition, which needs seqLength >= 1024. Compact head dims keep the fixture small while covering the gated-delta rule with a zero entry state, elementwise decay and a shared beta column." + }, + "attrs": { "q_num_heads": 2, "kv_num_heads": 1, "update_rule": "gated_delta", "scale": 0.35 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 1024, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.16, "cosStep": 0.28, "scale": 0.4 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 1024, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.12000000000000001, "cosStep": 0.36, "scale": 0.4 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 1024, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.18, "cosStep": 0.24, "scale": 0.4 } + }, + "decayT": { + "dtype": "float32", + "shape": [1, 1024, 8], + "data": { + "kind": "fillFloat32", + "sinStep": 0.22000000000000003, + "cosStep": 0.45999999999999996, + "scale": 0.1 + } + }, + "betaT": { + "dtype": "float32", + "shape": [1, 1024, 1], + "data": { "kind": "fillFloat32", "sinStep": 0.28, "cosStep": 0.33999999999999997, "scale": 0.3 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 1024, 16], "tolerance": 0.0005, "relTolerance": 0.0005 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 8, 8], "tolerance": 0.0005, "relTolerance": 0.0005 } + } + }, + { + "name": "linear_zero_chunked_seq1024", + "provenance": { + "notes": "Selects the chunked prefill decomposition, which needs seqLength >= 1024. Compact head dims keep the fixture small while covering the linear rule with a zero entry state." + }, + "attrs": { "q_num_heads": 2, "kv_num_heads": 1, "update_rule": "linear", "scale": 0.35 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 1024, 16], + "data": { + "kind": "fillFloat32", + "sinStep": 0.16999999999999998, + "cosStep": 0.29000000000000004, + "scale": 0.4 + } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 1024, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.37, "scale": 0.4 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 1024, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.25, "scale": 0.4 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 1024, 16], "tolerance": 0.0005, "relTolerance": 0.0005 }, + "presentStateT": { "dtype": "float32", "shape": [1, 1, 8, 8], "tolerance": 0.0005, "relTolerance": 0.0005 } + } + }, + { + "name": "linear_state_chunked_seq1024", + "provenance": { + "notes": "Selects the chunked prefill decomposition, which needs seqLength >= 1024. Compact head dims keep the fixture small while covering the linear rule with a supplied entry state and grouped query heads." + }, + "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "linear", "scale": 0.35 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 1024, 32], + "data": { "kind": "fillFloat32", "sinStep": 0.18, "cosStep": 0.30000000000000004, "scale": 0.4 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 1024, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.14, "cosStep": 0.38, "scale": 0.4 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 1024, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.2, "cosStep": 0.26, "scale": 0.4 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 2, 8, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.36, "cosStep": 0.44, "scale": 0.2 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 1024, 32], "tolerance": 0.0005, "relTolerance": 0.0005 }, + "presentStateT": { "dtype": "float32", "shape": [1, 2, 8, 8], "tolerance": 0.0005, "relTolerance": 0.0005 } + } + }, + { + "name": "gated_delta_state_chunked_seq1024", + "provenance": { + "notes": "Selects the chunked prefill decomposition, which needs seqLength >= 1024. Compact head dims keep the fixture small while covering the gated-delta rule with a supplied entry state, per-head decay and per-head beta." + }, + "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated_delta", "scale": 0.35 }, + "inputs": { + "queryT": { + "dtype": "float32", + "shape": [1, 1024, 32], + "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.31, "scale": 0.4 } + }, + "keyT": { + "dtype": "float32", + "shape": [1, 1024, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.15000000000000002, "cosStep": 0.39, "scale": 0.4 } + }, + "valueT": { + "dtype": "float32", + "shape": [1, 1024, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.21000000000000002, "cosStep": 0.27, "scale": 0.4 } + }, + "pastStateT": { + "dtype": "float32", + "shape": [1, 2, 8, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.37, "cosStep": 0.45, "scale": 0.2 } + }, + "decayT": { + "dtype": "float32", + "shape": [1, 1024, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.25, "cosStep": 0.49, "scale": 0.1 } + }, + "betaT": { + "dtype": "float32", + "shape": [1, 1024, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.31, "cosStep": 0.37, "scale": 0.3 } + } + }, + "outputs": { + "outputT": { "dtype": "float32", "shape": [1, 1024, 32], "tolerance": 0.0005, "relTolerance": 0.0005 }, + "presentStateT": { "dtype": "float32", "shape": [1, 2, 8, 8], "tolerance": 0.0005, "relTolerance": 0.0005 } + } + }, + { + "name": "gated_delta_state_chunked_f16_seq1024", + "provenance": { + "notes": "float16 activations and state through the chunked prefill decomposition, which needs seqLength >= 1024. The chunk passes accumulate in float32 and only their loads and stores are narrowed, so this is the fixture that exercises the f16 enable in every pass." + }, + "attrs": { "q_num_heads": 4, "kv_num_heads": 2, "update_rule": "gated_delta", "scale": 0.35 }, + "inputs": { + "queryT": { + "dtype": "float16", + "shape": [1, 1024, 32], + "data": { "kind": "fillFloat32", "sinStep": 0.19, "cosStep": 0.27, "scale": 0.4 } + }, + "keyT": { + "dtype": "float16", + "shape": [1, 1024, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.15, "cosStep": 0.33, "scale": 0.4 } + }, + "valueT": { + "dtype": "float16", + "shape": [1, 1024, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.21, "cosStep": 0.25, "scale": 0.4 } + }, + "pastStateT": { + "dtype": "float16", + "shape": [1, 2, 8, 8], + "data": { "kind": "fillFloat32", "sinStep": 0.31, "cosStep": 0.39, "scale": 0.2 } + }, + "decayT": { + "dtype": "float16", + "shape": [1, 1024, 16], + "data": { "kind": "fillFloat32", "sinStep": 0.13, "cosStep": 0.43, "scale": 0.1 } + }, + "betaT": { + "dtype": "float16", + "shape": [1, 1024, 2], + "data": { "kind": "fillFloat32", "sinStep": 0.27, "cosStep": 0.35, "scale": 0.3 } + } + }, + "outputs": { + "outputT": { "dtype": "float16", "shape": [1, 1024, 32], "tolerance": 0.04, "relTolerance": 0.04 }, + "presentStateT": { "dtype": "float16", "shape": [1, 2, 8, 8], "tolerance": 0.04, "relTolerance": 0.04 } + } + } + ] +}