{"record":{"id":"4ea1427019198dd7","repo":"xai-org/x-algorithm","slug":"block-sparsity-expects-sparse-block-size-q-subtil","errorCode":null,"errorMessage":"Block sparsity expects sparse_block_size_q={subtile_factor * m_block_size} for subtile_factor={subtile_factor}.","messagePattern":"Block sparsity expects sparse_block_size_q=(.+?) for subtile_factor=(.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"phoenix/xrex/cutedsl/ranker_fa4/block_sparsity.py","lineNumber":616,"sourceCode":"\n\ndef normalize_block_sparse_config_bwd(\n    tensors: BlockSparseTensorsTorch,\n    *,\n    batch_size: int,\n    num_head: int,\n    seqlen_q: int,\n    seqlen_k: int,\n    block_size: tuple[int, int],\n    subtile_factor: int,\n) -> tuple[BlockSparseTensorsTorch, Tuple[Tuple[bool, ...], ...] | None]:\n    m_block_size, n_block_size = block_size\n    if tensors.block_size is None:\n        sparse_block_size_q, sparse_block_size_kv = subtile_factor * m_block_size, n_block_size\n    else:\n        sparse_block_size_q, sparse_block_size_kv = tensors.block_size\n    if sparse_block_size_q != subtile_factor * m_block_size:\n        raise ValueError(\n            f\"Block sparsity expects sparse_block_size_q={subtile_factor * m_block_size} \"\n            f\"for subtile_factor={subtile_factor}.\"\n        )\n    if sparse_block_size_kv != n_block_size:\n        raise ValueError(\n            f\"Block sparsity expects sparse_block_size[1]={n_block_size} to match tile_n.\"\n        )\n    expected_count_shape, expected_index_shape = get_block_sparse_expected_shapes_bwd(\n        batch_size,\n        num_head,\n        seqlen_q,\n        seqlen_k,\n        m_block_size,\n        n_block_size,\n        subtile_factor,\n    )\n    normalized_tensors = normalize_block_sparse_tensors(\n        tensors,","sourceCodeStart":598,"sourceCodeEnd":634,"githubUrl":"https://github.com/xai-org/x-algorithm/blob/24c60942c5c5fdad3a6addffb4c6e6d2f228f04f/phoenix/xrex/cutedsl/ranker_fa4/block_sparsity.py#L598-L634","documentation":"In the backward normalize_block_sparse_config_bwd, the query-side sparse block size must equal subtile_factor * m_block_size — backward uses larger Q blocks due to subtiling. An explicit block_size[0] differing from that is rejected (None defaults correctly).","triggerScenarios":"Running the bwd normalization with tensors.block_size[0] set to tile_m (the forward value) instead of subtile_factor * tile_m; reusing the forward mask's block size for backward.","commonSituations":"Sharing one BlockSparseTensorsTorch between fwd and bwd with an explicit block_size; after a version change altered subtile_factor; exporting a mask with hardcoded block sizes.","solutions":["Set block_size[0] = subtile_factor * tile_m for the backward pass, or use block_size=None so the default applies","Keep separate tensor configs for forward and backward if block sizes differ","Re-derive block size from the kernel's subtile_factor rather than hardcoding"],"exampleFix":"# before\ntensors_bwd = tensors_fwd  # block_size=(64, 128), subtile_factor=2\n\n# after\ntensors_bwd = replace(tensors_fwd, block_size=(128, 128))  # subtile_factor*tile_m\n# or block_size=None on both to use per-path defaults","handlingStrategy":"validation","validationCode":"expected_q = subtile_factor * m_block_size\nassert tensors.block_size is None or tensors.block_size[0] == expected_q","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Maintain separate fwd/bwd tensor configs","Derive backward block size from subtile_factor programmatically"],"tags":["block-sparsity","backward","block-size","subtile"],"backgroundTag":"configuration-mismatch","analyzedSha":"24c60942c5c5fdad3a6addffb4c6e6d2f228f04f","analyzedAt":"2026-08-28T11:40:14.686Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}