Files
nvfp4-megamoe-kernel/dsv4/_archive/kernels/indexer/csa_indexer.py
biondizzle f3b551956d Cleanup Step 2: Archive Lineage P code, fix broken imports
- Move dead dsv4/ modules to dsv4/_archive/ (52 files)
  - model/{dsv4,mtp,layer,layer_schedule}
  - layers/{embedding,attention,ffn,norm} (kept linear,mhc,router,moe,shared_expert,grouped_linear - live)
  - cache/*, kernels/cache/*, kernels/indexer/{csa_indexer,score_topk,compute_valid_lens}
  - kernels/router/{nvfp4_fused_router,dense_router_decode_kernel,dense_router_prefill}
  - ops/{topk,topk_select,rope,router}, loader/{hf_checkpoint,layout_convert}
  - reference/{attention,compressor,csa_attention,moe_pipeline}
  - kernels/compressor/{compress_tail,csa_hca}
- Restore dsv4/ops/{router,custom_ops}.py (needed by live layers)
- Fix dsv4/kernels/{indexer,compressor,attention}/__init__.py (removed broken imports)
- Remove preload_all() from loader.py (dead, referenced nonexistent .cu file)
- Fix loader.py docstring (fused_amax_quantize_nvfp4 → quantize_nvfp4_from_buffer)
- Move broken tests to tests/e2e_archive/
  - test_fused_router, production_values_test, e2e/{one_layer,model_construction,csa_hca}
- vLLM has 0 imports of dsv4 (Step 0 confirmed)
2026-06-02 19:27:07 +00:00

90 lines
3.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""CSA indexer — sparse top-k selection from compressed KV cache.
Paper §2.3.1, eq. 1317:
c_Q = h_t · W_DQ (shared with main queries)
q^I_t = c_Q · W_IUQ (low-rank indexer queries)
w^I_t = h_t · W_w (per-head weights)
I[t,s] = Σ_h w^I_t,h · ReLU(q^I_t,h · K^IComp[s]) (MQA: shared key K)
Selected = TopK(I[t,:])
Key layout: K^IComp[s] is shared across indexer heads (MQA, NOT per-head).
The dot product is: q^I_t,h (per-head) · K^IComp[s] (shared).
This matches the production Indexer.forward() einsum 'tnd,cd->tnc'.
RoPE: Neither indexer queries nor keys have RoPE applied.
The indexer is a lightweight scoring mechanism for block selection,
not a full attention layer. If the HF reference applies RoPE to
indexer keys, the stored FP4 keys would need it baked in at
compression time. VERIFY THIS AGAINST THE REFERENCE BEFORE PRODUCTION.
The indexer only exists in CSA layers. HCA and SWA layers don't have
an indexer (they do dense attention).
"""
from __future__ import annotations
from typing import TYPE_CHECKING
import torch
if TYPE_CHECKING:
from dsv4.model.config import DSV4Config
from dsv4.cache.handle import LayerCacheHandle
class CSAIndexer:
"""Lightning indexer for CSA layers.
Composed by AttentionSubBlock when layer is CSA. Owns W_IUQ and W_w.
The shared c_Q comes from the main query path; this class does NOT
own W_DQ.
"""
def __init__(self, config: "DSV4Config"):
self.config = config
self._runner_id = None
def __call__(
self,
c_Q: torch.Tensor, # [T, d_c] BF16 — shared latent
h_t: torch.Tensor, # [T, d] BF16 — hidden states
cache: "LayerCacheHandle",
) -> torch.Tensor:
"""Return top-k compressed-block indices per query token.
Returns [T, csa_top_k] int32 indices into the compressed pool.
"""
from dsv4.kernels.indexer.score_topk import run_indexer_score_topk
# Kernel A: indexer query up-projection (c_Q -> q_I)
# For now, use a simple torch linear; will swap to Nvfp4Linear
# with FP4 output in Phase 2.
if not hasattr(self, '_q_up_weight'):
# WARNING: USING RANDOM WEIGHTS — csa_indexer.py has NO weight loading.
# The production path uses the Indexer class in single_shot_inference.py
# which loads real weights from the checkpoint via Nvfp4Linear.
# This CSAIndexer class should NOT be used for production inference.
# If you see this message, you need to wire up checkpoint weight loading
# or use the production Indexer instead.
raise RuntimeError(
"CSAIndexer has no checkpoint weight loading. "
"Use the production Indexer class (single_shot_inference.py) instead, "
"or implement weight loading for CSAIndexer.")
# Old code (random weights — removed to prevent silent incorrect behavior):
# d_c = self.config.query_compression_dim
# n_ih = self.config.indexer_num_heads
# c_i = self.config.indexer_head_dim
# self._q_up_weight = torch.randn(d_c, n_ih * c_i, ...) * 0.02
# self._w_head_weight = torch.randn(hidden_size, n_ih, ...) * 0.02
q_I = torch.nn.functional.linear(c_Q, self._q_up_weight.T) # [T, n_ih * c_i] BF16
w_h = torch.nn.functional.linear(h_t, self._w_head_weight.T).float() # [T, n_ih] FP32
view = cache.read_indexer_view()
return run_indexer_score_topk(
q_I=q_I,
w_h=w_h,
indexer_view=view,
num_heads=self.config.indexer_num_heads,
head_dim=self.config.indexer_head_dim,
top_k=self.config.csa_top_k,
entries_per_block=cache.paged.schema.entries_per_block,
)