# Grouped GEMM harness:
# Section A = 2Dx3D (variable M)
# Section B = 2Dx2D (variable contraction dim K)
#
# --- Section A: 2Dx3D variant: variable M, weights 3D [G, K, N] -------------
#
# Format: the sizes field is either explicit sizes or a named profile:
#   --grouped=<dim_idx>:<num_groups>:<s0+s1+...>[:HINT]        MxK:GxKxN
#   --grouped=<dim_idx>:<num_groups>:<PROFILE[.PARAM]>[:HINT]  MxK:GxKxN
# Where:
#   dim_idx      - dimension index to group (0=M, 1=K)
#   num_groups   - number of groups/experts (must match G)
#   s0+s1+...    - explicit '+'-separated per-group sizes (sum must equal the variable dim)
#   PROFILE      - balanced[.seed] | hot[.pct] | decode
#   HINT         - max-group dispatch hint (DNNL_ARG_HINT_MAX_GROUP_SIZE).
#                  Explicit form: omit for the no-hint path, or ':hint' to set it
#                  Profile form: defaults to max(sizes), or 'PROFILE:HINT' to set
#   M, G         - total variable-dim size and group count
#
# Configurations below cover:
# - Data types: f32, bf16, f16, u8/s8 with s4/u4 weights
# - Bias: --bia-dt=f32/bf16/f16, --bia_mask=2 (per-expert bias [num_experts, N])
# - Empty groups (M_g = 0): balanced at small M (E=4, M=8 -> [0,3,4,1])
# - Row-wise src scales: --attr-scales=src:1
# - Grouped src scales: 3:f16:1x32 (grouped along K, mask = (1<<0) | (1<<1))
# - Column-wise wei scales: 5 (scales vary by expert and N, mask = (1<<0) | (1<<2))
# - Grouped wei scales: 7:f32:32x1 (grouped along K, mask = (1<<0) | (1<<1) | (1<<2))
# - Column-wise weight zero points: --attr-zero-points=wei:5:s8 (mask = (1<<0) | (1<<2))
# - Grouped weight zero points: --attr-zero-points=wei:7:s8:32x1 (grouped along K)
# - 2D-grouped wei scales/zero points: 7:e8m0:32x32 (grouped along both K and N,
#           so one scale covers a 32x32 block instead of a 32-element K strip)
# - WOQ (weight-only quantization) with f16 activations and s4/u4/8-bit weights
# - Transposed weights: --wtag=acb (weights layout [num_experts, N, K] vs abc [num_experts, K, N])
# - FP8 row-wise: f8_e5m2/f8_e4m3 dtypes with row-wise src + column-wise wei f32 scales
# - MXFP8: f8_e4m3 dtypes, e8m0 block scales, block size 32
# - MXFP4: f4_e2m1 dtypes, e8m0 block scales, block size 32
# - NVFP4: f4_e2m1 dtypes, f8_e4m3 block scales (block size 16) + per-group global
#           f32 scale via binary mul post-op (--attr-post-ops=mul:f32:0)
# - Post-ops: any eltwise algorithm (relu, tanh, gelu_tanh/erf, ...),
#             binary_mul (per-row, dense, grouped encoding), chained
#             eltwise+binary_mul(grouped) (SwiGLU/GEGLU gate), post-ops with bias/scales

--reset

# Basic correctness
--dt=f32:f32:f32,f16:f16:f16,bf16:bf16:bf16,bf16:bf16:f32
--wtag=any,abc,acb
--batch=shapes_grouped

# Transposed weights with bias - f32
--reset
--dt=f32:f32:f32
--wtag=acb
--bia-dt=f32 --bia_mask=2
--batch=shapes_grouped

# Blocked wei scales with different block sizes - u8/s8
--reset
--dt=u8:s4:bf16,s8:s4:bf16,u8:u8:bf16
--wtag=any,abc
--grouped=0:4:balanced
--attr-scales=wei:7:bf16:128x1,wei:7:bf16:32x1
32x512:4x512x256

# Combined row-wise src + blocked weight scales - u8/s8
--reset
--dt=u8:s4:bf16,s8:s4:bf16,u8:s4:bf16
--wtag=any,abc,acb
--grouped=0:4:balanced
--attr-scales=src:1+wei:7:bf16:128x1,src:1+wei:7:bf16:32x1
32x512:4x512x256

# Combine row-wise src scales and zp u8/s8/s32
--reset
--dt=s8:s4:f32,s8:s4:bf16,u8:s4:bf16
--wtag=abc,acb
--grouped=0:4:balanced
--bia-dt=undef,bf16 --bia_mask=2
--attr-scales=src:1+wei:7:f32:128x1
--attr-zero-points=,src:1:u8,src:1:s8,src:1:s32
32x512:4x512x256

# Combine blocked src scales and zp u8/s8/s32
--reset
--dt=s8:s4:bf16,u8:s4:bf16
--wtag=abc,acb
--grouped=0:4:balanced
--bia-dt=undef,bf16 --bia_mask=2
--attr-scales=src:3:f32:1x128
--attr-zero-points=,src:3:u8:1x128,src:3:s8:1x128,src:3:s32:1x128
32x512:4x512x256

# int src + int wei with column-wise scales and ZPs
--reset
--dt=u8:s8:f32,s8:s8:f32
--wtag=abc,acb
--attr-scales=,wei:5:f32,wei:5:f16
--attr-zero-points=,wei:5:s8,wei:5:s32
--grouped=0:4:balanced
32x64:4x64x32
--grouped=0:256:hot.100
255x64:256x64x512
--grouped=0:32:hot,0:32:balanced
512x64:32x64x512
--grouped=0:256:decode
1x64:256x64x512

# int src + int wei with blocked scales and ZPs
--reset
--dt=u8:s8:bf16,s8:s8:bf16
--wtag=abc
--grouped=0:4:balanced
--attr-scales=wei:7:bf16:128x1
--attr-zero-points=wei:7:s8:128x1,wei:7:s32:128x1
32x512:4x512x256

# WOQ: f16 activation + s4/u4 weights with blocked scales and ZPs
--reset
--dt=f16:s4:f16,f16:u4:f16
--wtag=abc,acb
--grouped=0:4:balanced
--attr-fpmath=f16:true
--attr-scales=wei:7:f16:32x1
--attr-zero-points=,wei:7:u4:32x1
32x128:4x128x64

# WOQ: f16 activation + u8 weights with per-expert per-column scales and ZPs
--reset
--dt=f16:u8:f16
--wtag=abc,acb
--attr-fpmath=f16:true
--attr-scales=wei:5:f16
--attr-zero-points=wei:5:u8
--batch=shapes_grouped

# WOQ: f16:s4 with per-expert per-column wei scales + per-expert bias
--reset
--dt=f16:s4:f16
--wtag=abc,acb
--bia-dt=f16 --bia_mask=2
--attr-fpmath=f16:true
--attr-scales=wei:5:f16
--batch=shapes_grouped

# int src + int wei with K-grouped f16 src scales and K-grouped wei scales and ZPs
--reset
--dt=u8:s8:f32,s8:s8:f32,u8:u8:f32
--wtag=abc,acb
--grouped=0:4:balanced
--attr-scales=src:3:f16:1x128+wei:7:f16:128x1
--attr-zero-points=,wei:7:s8:128x1
8x512:4x512x256

# f8_e5m2 with row-wise/block src scales + column-wise wei scales
--reset
--dt=f8_e5m2:f8_e5m2:bf16
--wtag=abc,acb
--attr-scales=,src:1+wei:5,src:3:e8m0:1x32+wei:7:e8m0:32x1
--grouped=0:4:balanced
8x128:4x128x64

# f8_e4m3 with e8m0 block scales
--reset
--dt=f8_e4m3:f8_e4m3:bf16
--wtag=abc,acb
--grouped=0:4:balanced
--attr-scales=src:3:e8m0:1x32+wei:7:e8m0:32x1,src:3:e8m0:1x32+wei:5,src:1+wei:7:e8m0:32x1
32x128:4x128x64

# f8_e4m3 row-wise src + column-wise wei scales across varied shapes
--reset
--dt=f8_e4m3:f8_e4m3:bf16
--wtag=abc,acb
--attr-scales=src:1+wei:5
--batch=shapes_grouped

# f4_e2m1 with e8m0 block scales
--reset
--dt=f4_e2m1:f4_e2m1:bf16
--wtag=abc,acb
--grouped=0:4:balanced
--attr-scales=src:3:e8m0:1x32+wei:7:e8m0:32x1
32x128:4x128x64

# 2D-grouped wei scales: blocked along N as well as K, so each expert holds
# (N / N group) x (K / K group) scales rather than N x (K / K group)
--reset
--dt=f8_e4m3:f8_e4m3:bf16,f4_e2m1:f4_e2m1:bf16
--wtag=abc,acb
--grouped=0:4:balanced
--attr-scales=wei:7:e8m0:32x32,src:3:e8m0:1x32+wei:7:e8m0:32x32
32x128:4x128x64

# 2D-grouped wei scales + zero points - WOQ
--reset
--dt=f16:s8:f16
--wtag=abc,acb
--grouped=0:4:balanced
--attr-fpmath=f16:true
--attr-scales=wei:7:f16:32x32
--attr-zero-points=,wei:7:s8:32x32
32x128:4x128x64

# NVFP4: f4_e2m1 dt with f8_e4m3 block scales + per-group global scale
# via binary mul (f32/f16/bf16)
--reset
--dt=f4_e2m1:f4_e2m1:bf16
--wtag=abc,acb
--grouped=0:4:balanced
--attr-scales=src:3:f8_e4m3:1x16+wei:7:f8_e4m3:16x1
--attr-post-ops=mul:f32:0,mul:f16:0,mul:bf16:0
32x128:4x128x64

# DNNL_ARG_HINT_MAX_GROUP_SIZE: hint vs no-hint dispatch
--reset
--dt=f16:f16:f16
--wtag=abc
--grouped=0:8:40+36+32+30+34+28+30+26
256x128:8x128x64
--grouped=0:8:40+36+32+30+34+28+30+26:40
256x128:8x128x64

# Avg tokens-per-expert sweep
# avg_m is in {8,16,32,64,128}, which should hit different strategies
--reset
--dt=f16:f16:f16
--wtag=abc
--grouped=0:32:balanced
256x128:32x128x64
512x128:32x128x64
1024x128:32x128x64
2048x128:32x128x64
4096x128:32x128x64

# u8 WoQ over the same avg_m sweep as above
--reset
--dt=f16:u8:f16
--wtag=abc
--attr-fpmath=f16:true
--attr-scales=wei:5:f16
--attr-zero-points=wei:5:u8
--grouped=0:32:balanced
256x128:32x128x64
512x128:32x128x64
1024x128:32x128x64
2048x128:32x128x64
4096x128:32x128x64

# Post-ops:
# swish + binary_mul (per-row/dense/grouped), all eltwise
# algorithms, chains (grouped + dense binary_mul)
--reset
--dt=f32:f32:f32,bf16:bf16:bf16,f16:f16:f16,s8:s8:f16
--wtag=abc
--grouped=0:4:balanced
--attr-post-ops=eltwise_swish:1.0,mul:f32:1,mul:f16:1,mul:bf16:1,mul:f32:3:grouped,mul:f16:3:grouped,mul:bf16:3:grouped,eltwise_relu,eltwise_relu:0.5,eltwise_tanh,eltwise_elu:1.0,eltwise_square,eltwise_abs,eltwise_sqrt,eltwise_linear:2.0:1.0,eltwise_soft_relu:1.0,eltwise_logistic,eltwise_exp,eltwise_gelu_tanh,eltwise_log,eltwise_clip:0.0:6.0,eltwise_clip_v2:0.0:6.0,eltwise_pow:1.0:3.0,eltwise_gelu_erf,eltwise_round,eltwise_mish,eltwise_hardswish:0.166667:0.5,eltwise_hardsigmoid:0.2:0.5,mul:f16:3:grouped+mul:f32:1,eltwise_swish:1.0+mul:f16:3:grouped+mul:f32:1,eltwise_swish:1.0+mul:f32:1,eltwise_swish:1.0+mul:f16:1
32x64:4x64x32

# Post-ops + bias: binary_mul per-row and grouped with bias
--reset
--dt=f32:f32:f32,bf16:bf16:bf16,u8:u8:bf16
--wtag=abc
--grouped=0:4:balanced
--bia-dt=f32 --bia_mask=2
--attr-post-ops=mul:f32:1,mul:f32:3:grouped,mul:bf16:3:grouped,mul:f16:3:grouped
32x64:4x64x32

# Post-ops + quantization: binary_mul per-row and grouped + WOQ
--reset
--dt=f16:s4:f16
--wtag=abc
--grouped=0:4:balanced
--attr-fpmath=f16:true
--attr-scales=wei:7:f16:32x1
--attr-post-ops=mul:f32:1,mul:f16:1,mul:f32:3:grouped,mul:f16:3:grouped
32x128:4x128x64

# Gate chains (swish/gelu_tanh/gelu_erf + grouped binary_mul) + WOQ
--reset
--dt=f16:s4:f16
--wtag=abc,acb
--grouped=0:4:balanced,0:4:hot.100
--attr-fpmath=f16:true
--attr-scales=wei:7:f16:32x1
--attr-post-ops=eltwise_swish:1.0+mul:f16:3:grouped,eltwise_gelu_tanh+mul:f16:3:grouped,eltwise_gelu_erf+mul:f16:3:grouped,mul:f16:3:grouped+eltwise_swish:1.0,mul:f16:3:grouped+eltwise_gelu_tanh,mul:f16:3:grouped+eltwise_gelu_erf
32x128:4x128x64

# Per-group binary_mul (mask 0) with variable group sizes (incl. empty group)
--reset
--dt=f32:f32:f32,bf16:bf16:bf16,f16:f16:f16
--wtag=abc
--grouped=0:4:balanced
--attr-post-ops=mul:f32:0,mul:f16:0,mul:bf16:0
8x64:4x64x32

# --- Section B: 2Dx2D variant: variable contraction dim K -------------------
# src[M, total_K] * wei[total_K, N] -> dst[G, M, N] (dense 3D, allows abc, acb)
--reset
--dt=f32:f32:f32,f16:f16:f16,bf16:bf16:bf16
--dtag=any,abc,acb

# Balanced
--grouped=1:4:balanced
64x32:32x32
--grouped=1:8:balanced
64x64:64x64

# One hot expert
--grouped=1:3:hot
32x16:16x24
--grouped=1:4:hot
64x100:100x32

# Single group
--grouped=1:1:balanced
16x8:8x12

# Varied mix, incl. empty groups
--grouped=1:6:hot
24x6:6x20
--grouped=1:4:balanced
32x8:8x32
