Module neuralops.attentions.aegis_hybrid

Aegis-Hybrid Attention — Production-Hardened (v2).

Combines -----= 1. Local sliding-window attention (chunked). 2. Optional global linear attention via FAVOR+ style kernel features. 3. Trainable LoRA adapters on top of frozen low-rank base. 4. Learnable fusion gate between local and global branches.

Fixes (v2): AH-FIX-01 FactorizedLinear.is_custom_attention = False. AH-FIX-02 use_global is configurable; global path restored with proper learnable kernel feature map. AH-FIX-03 Fast weight property (no full matmul on each access). AH-FIX-04 GQA-aware SVD initialization with repeat_interleave pooling. AH-FIX-05 Robust GPT-2 Conv1D handling (checks layout at runtime). AH-FIX-06 Comprehensive mask normalization (2D/3D/4D). AH-FIX-07 Correct past_len derived from past_key_value shape, not from N_kv - N (which is wrong for cross-attention). AH-FIX-08 Full inject_aegis_hybrid() with recursion + idempotency guard. AH-FIX-09 RoPE supported via position_embeddings or self.rotary_emb. AH-FIX-10 dtype-safe attention matmul. AH-FIX-11 local_window default consistent (256). AH-FIX-12 Storage accounting. AH-FIX-13 output_hidden_states / return_dict / *args supported. AH-FIX-14 FactorizedLinear uses down/up names for consistency with the rest of the framework, with backward-compat aliases lin1/lin2. AH-FIX-15 Optional activation whitening via whitening dict.

Functions

def apply_rotary_pos_emb(q, k, cos, sin)
Expand source code
def apply_rotary_pos_emb(q, k, cos, sin):
    if cos is None or sin is None:
        return q, k
    if cos.dim() == 2:
        cos = cos.unsqueeze(0).unsqueeze(1)
        sin = sin.unsqueeze(0).unsqueeze(1)
    elif cos.dim() == 3:
        cos = cos.unsqueeze(1)
        sin = sin.unsqueeze(1)
    while cos.dim() < q.dim():
        cos = cos.unsqueeze(0)
        sin = sin.unsqueeze(0)

    head_dim = q.shape[-1]
    rotary_dim = 2 * (min(cos.shape[-1], head_dim, k.shape[-1]) // 2)
    if rotary_dim == 0:
        return q, k

    q_rot, q_pass = q[..., :rotary_dim], q[..., rotary_dim:]
    k_rot, k_pass = k[..., :rotary_dim], k[..., rotary_dim:]

    cos_q = cos[..., : q.shape[-2], :rotary_dim].to(q.device, q.dtype)
    sin_q = sin[..., : q.shape[-2], :rotary_dim].to(q.device, q.dtype)
    cos_k = cos[..., : k.shape[-2], :rotary_dim].to(k.device, k.dtype)
    sin_k = sin[..., : k.shape[-2], :rotary_dim].to(k.device, k.dtype)

    q_rot_emb = (q_rot * cos_q) + (_rotate_half(q_rot) * sin_q)
    k_rot_emb = (k_rot * cos_k) + (_rotate_half(k_rot) * sin_k)

    q_out = torch.cat([q_rot_emb, q_pass], dim=-1) if q_pass.shape[-1] > 0 else q_rot_emb
    k_out = torch.cat([k_rot_emb, k_pass], dim=-1) if k_pass.shape[-1] > 0 else k_rot_emb
    return q_out, k_out
def inject_aegis_hybrid(layer: Any = None,
model: Any = None,
model_config: Any = None,
comp_config: Any = None,
layer_idx: int = 0,
total_layers: int = 12,
old_attn: nn.Module | None = None,
**kwargs: Any)
Expand source code
@ATTENTION_REGISTRY.register("aegis_hybrid_inject")
@ATTENTION_REGISTRY.register("inject_aegis_hybrid")
def inject_aegis_hybrid(
    layer: Any = None,
    model: Any = None,
    model_config: Any = None,
    comp_config: Any = None,
    layer_idx: int = 0,
    total_layers: int = 12,
    old_attn: nn.Module | None = None,
    _depth: int = 0,
    **kwargs: Any,
):
    if _depth > 8:
        logger.warning("inject_aegis_hybrid: max recursion depth exceeded.")
        return None

    target = layer if layer is not None else model
    if target is None:
        return None

    cfg = model_config if model_config is not None else getattr(target, "config", None)

    # Whole-model traversal
    layer_list = None
    for path in (
        ("distilbert", "transformer", "layer"),
        ("roberta", "encoder", "layer"),
        ("bert", "encoder", "layer"),
        ("transformer", "h"),
        ("model", "layers"),
        ("encoder", "layer"),
        ("layers",),
        ("h",),
    ):
        obj = target
        ok = True
        for p in path:
            if hasattr(obj, p):
                obj = getattr(obj, p)
            else:
                ok = False
                break
        if ok and isinstance(obj, (nn.ModuleList, list)):
            layer_list = obj
            break

    if layer_list is not None:
        n_injected = 0
        tot = len(layer_list)
        for idx, lyr in enumerate(layer_list):
            res = inject_aegis_hybrid(
                layer=lyr,
                model_config=cfg,
                comp_config=comp_config,
                layer_idx=idx,
                total_layers=tot,
                _depth=_depth + 1,
                **kwargs,
            )
            if res is not None:
                n_injected += 1
        return target if n_injected > 0 else None

    # Single-layer
    src = old_attn
    if src is None:
        for attr in ("attention", "attn", "self_attn", "self"):
            if hasattr(target, attr):
                cand = getattr(target, attr)
                if isinstance(cand, nn.Module) and cand is not target:
                    src = getattr(cand, "self", cand)
                    break
    if src is None:
        src = target

    # Idempotency guard
    if isinstance(src, AegisHybridAttention) or "AegisHybrid" in src.__class__.__name__:
        return None

    h_size = getattr(cfg, "hidden_size", None) if cfg else None
    if h_size is None:
        for attr in ("out_lin", "dense", "c_proj", "o_proj", "out_proj"):
            m = getattr(src, attr, None)
            if m is not None and hasattr(m, "out_features"):
                h_size = m.out_features
                break
    h_size = int(h_size or kwargs.get("hidden_size", 768))

    n_heads = getattr(cfg, "num_attention_heads", None) if cfg else None
    if n_heads is None:
        n_heads = getattr(
            src,
            "num_attention_heads",
            getattr(src, "num_heads", kwargs.get("num_heads", 12)),
        )
    n_heads = int(n_heads)

    dec = _detect_decoder(cfg, kwargs.get("is_decoder"), src)

    aegis_cfg = AegisHybridConfig(
        hidden_size=h_size,
        num_attention_heads=n_heads,
        head_dim=kwargs.get("head_dim", h_size // max(1, n_heads)),
        num_key_value_heads=(getattr(cfg, "num_key_value_heads", n_heads) if cfg else n_heads),
        local_window=kwargs.get(
            "local_window",
            getattr(comp_config, "local_window", 256) if comp_config else 256,
        ),
        rank=kwargs.pop(
            "rank",
            (getattr(comp_config, "rank", min(128, h_size)) if comp_config else min(128, h_size)),
        ),
        lora_rank=kwargs.get(
            "lora_rank", getattr(comp_config, "lora_rank", 32) if comp_config else 32
        ),
        dropout=kwargs.get("dropout", getattr(comp_config, "dropout", 0.1) if comp_config else 0.1),
        is_decoder=dec,
        use_global=kwargs.get(
            "use_global",
            getattr(comp_config, "use_global", True) if comp_config else True,
        ),
    )

    try:
        new_attn = AegisHybridAttention(
            config=aegis_cfg,
            pretrained_attn=src if src is not target else None,
            whitening=kwargs.pop("whitening", None),
        )
    except Exception as e:
        logger.error("inject_aegis_hybrid: construction failed: %s", e)
        return None

    p = next(src.parameters(), None)
    if p is not None:
        new_attn.to(device=p.device, dtype=p.dtype)

    spliced = False
    if hasattr(target, "attention"):
        if hasattr(target.attention, "self"):
            new_attn.is_nested_encoder = True
            target.attention.self = new_attn
        else:
            new_attn.is_nested_encoder = False
            target.attention = new_attn
        spliced = True
    elif hasattr(target, "self_attn"):
        new_attn.is_nested_encoder = False
        target.self_attn = new_attn
        spliced = True
    elif hasattr(target, "attn"):
        new_attn.is_nested_encoder = False
        target.attn = new_attn
        spliced = True
    elif hasattr(target, "self"):
        new_attn.is_nested_encoder = True
        target.self = new_attn
        spliced = True

    if not spliced:
        logger.error(
            "inject_aegis_hybrid: no writable attention attribute on %s",
            type(target).__name__,
        )
        return None

    return new_attn

Classes

class AegisHybridAttention (config: Any | None = None,
hidden_size: int | None = None,
num_heads: int | None = None,
pretrained_attn: nn.Module | None = None,
rank: int | None = None,
lora_rank: int | None = None,
is_decoder: bool | None = None,
whitening: dict[str, torch.Tensor] | None = None,
**kwargs: Any)
Expand source code
@ATTENTION_REGISTRY.register(
    "aegis_hybrid", supports_causal=True, supports_gqa=True, supports_rope=True
)
@ATTENTION_REGISTRY.register(
    "aegis_hybrid_attn", supports_causal=True, supports_gqa=True, supports_rope=True
)
@ATTENTION_REGISTRY.register(
    "aegis_hybrid_attention",
    supports_causal=True,
    supports_gqa=True,
    supports_rope=True,
)
class AegisHybridAttention(BaseAttention):
    supports_causal: bool = True
    supports_gqa: bool = True
    supports_rope: bool = True
    is_custom_attention: bool = True

    def __init__(
        self,
        config: Any | None = None,
        hidden_size: int | None = None,
        num_heads: int | None = None,
        pretrained_attn: nn.Module | None = None,
        rank: int | None = None,
        lora_rank: int | None = None,
        is_decoder: bool | None = None,
        whitening: dict[str, torch.Tensor] | None = None,
        **kwargs: Any,
    ) -> None:
        try:
            super().__init__(config, rank if rank is not None else 128)
        except TypeError:
            super().__init__(config)

        if not isinstance(config, AegisHybridConfig):
            cfg_src = config if config is not None else kwargs
            h_size = hidden_size or getattr(cfg_src, "hidden_size", 768)
            n_heads = num_heads or getattr(
                cfg_src, "num_attention_heads", getattr(cfg_src, "n_heads", 12)
            )
            n_kv = getattr(
                cfg_src,
                "num_key_value_heads",
                getattr(cfg_src, "num_kv_heads", n_heads),
            )
            h_dim = getattr(cfg_src, "head_dim", None) or (h_size // max(1, n_heads))
            r = rank or getattr(cfg_src, "rank", min(128, h_size))
            l_r = lora_rank or getattr(cfg_src, "lora_rank", 32)

            dec = _detect_decoder(config, is_decoder, pretrained_attn)
            config = AegisHybridConfig(
                hidden_size=h_size,
                num_attention_heads=n_heads,
                head_dim=h_dim,
                num_key_value_heads=n_kv,
                local_window=kwargs.get("local_window", 256),
                rank=r,
                lora_rank=l_r,
                is_decoder=dec,
                use_global=kwargs.get("use_global", True),
                kernel_feature_dim=kwargs.get("kernel_feature_dim", 128),
                chunk_size=kwargs.get("chunk_size", 64),
            )

        self.config = config
        self.hidden_size: int = int(config.hidden_size)
        self.num_heads: int = int(config.num_attention_heads)
        self.head_dim: int = int(config.head_dim)
        self.num_kv_heads: int = int(config.num_key_value_heads or self.num_heads)
        self.kv_repeat: int = max(1, self.num_heads // max(1, self.num_kv_heads))
        self.all_head_size: int = self.num_heads * self.head_dim
        self.all_kv_head_size: int = self.num_kv_heads * self.head_dim
        self.local_window: int = int(config.local_window)
        self.rank: int = max(1, min(int(config.rank), self.hidden_size))
        self.lora_rank: int = max(1, min(int(config.lora_rank), self.hidden_size))
        self.dropout = nn.Dropout(config.dropout)

        self.is_decoder: bool = _detect_decoder(config, is_decoder, pretrained_attn)
        self.use_global: bool = bool(config.use_global)
        self.is_nested_encoder: bool = False
        self.rotary_emb = getattr(pretrained_attn, "rotary_emb", None)

        self.whitening = whitening or {}

        # ---- projections ----
        self.q_proj = FactorizedLinear(
            self.hidden_size,
            self.all_head_size,
            rank=self.rank,
            lora_rank=self.lora_rank,
            bias=True,
        )
        self.k_proj = FactorizedLinear(
            self.hidden_size,
            self.all_kv_head_size,
            rank=self.rank,
            lora_rank=self.lora_rank,
            bias=True,
        )
        self.v_proj = FactorizedLinear(
            self.hidden_size,
            self.all_kv_head_size,
            rank=self.rank,
            lora_rank=self.lora_rank,
            bias=True,
        )
        self.out_proj = FactorizedLinear(
            self.all_head_size,
            self.hidden_size,
            rank=self.rank,
            lora_rank=self.lora_rank,
            bias=True,
        )

        # ---- kernel feature map ----
        self.kernel_map = KernelFeatureMap(
            head_dim=self.head_dim,
            num_features=config.kernel_feature_dim,
            seed=config.kernel_seed,
        )

        # ---- fusion gate (per-head, init ~0.88 to local) ----
        self.fusion_beta = nn.Parameter(torch.zeros(self.num_heads))

        # AH-FIX-12: storage accounting
        self._orig_storage_bytes = 0
        self._comp_storage_bytes = 0

        if pretrained_attn is not None:
            self.copy_weights(pretrained_attn)

    # ---- aliases ----
    @property
    def query(self):
        return self.q_proj

    @property
    def key(self):
        return self.k_proj

    @property
    def value(self):
        return self.v_proj

    @property
    def dense(self):
        return self.out_proj

    @property
    def q_lin(self):
        return self.q_proj

    @property
    def k_lin(self):
        return self.k_proj

    @property
    def v_lin(self):
        return self.v_proj

    @property
    def out_lin(self):
        return self.out_proj

    @property
    def o_proj(self):
        return self.out_proj

    @property
    def c_proj(self):
        return self.out_proj

    # ------------------------------------------------------------------
    # AH-FIX-04: GQA-aware SVD init
    # ------------------------------------------------------------------
    @staticmethod
    def _svd_init(
        w: torch.Tensor,
        b: torch.Tensor | None,
        proj: FactorizedLinear,
        whitening_L: torch.Tensor | None = None,
    ) -> None:
        with torch.no_grad():
            W = w.detach().float()
            out_dim, in_dim = W.shape
            target_out, target_in = proj.out_features, proj.in_features

            # GQA: pool or repeat along output dim
            if out_dim > target_out and out_dim % target_out == 0:
                rep = out_dim // target_out
                W = W.view(target_out, rep, in_dim).mean(dim=1)
                if b is not None:
                    b = b.view(target_out, rep).mean(dim=1)
            elif out_dim < target_out and target_out % out_dim == 0:
                rep = target_out // out_dim
                W = W.repeat_interleave(rep, dim=0)
                if b is not None:
                    b = b.repeat_interleave(rep)

            if W.shape[0] != target_out or W.shape[1] != target_in:
                raise ValueError(
                    f"FactorizedLinear shape mismatch after GQA pooling: "
                    f"{tuple(W.shape)} vs ({target_out}, {target_in})"
                )

            # Optional whitening
            L = None
            if whitening_L is not None:
                Lw = whitening_L.float().to(W.device)
                if Lw.shape == (target_in, target_in):
                    eps = 1e-6 * torch.trace(Lw @ Lw.t()).item() / max(1, target_in)
                    L = Lw + eps * torch.eye(target_in, device=W.device, dtype=W.dtype)

            if L is not None:
                try:
                    W_eff = W @ L
                    U, S, Vh = torch.linalg.svd(W_eff, full_matrices=False)
                    L_inv = torch.linalg.inv(L)
                    Vh = Vh @ L_inv
                except Exception:
                    U, S, Vh = torch.linalg.svd(W, full_matrices=False)
            else:
                U, S, Vh = torch.linalg.svd(W, full_matrices=False)

            r = min(proj.rank, U.size(1), Vh.size(0))
            proj.down.weight.data.zero_()
            proj.up.weight.data.zero_()
            proj.down.weight.data[:r, :].copy_(Vh[:r, :].to(proj.down.weight.dtype))
            US = U[:, :r] @ torch.diag(S[:r])
            proj.up.weight.data[:, :r].copy_(US.to(proj.up.weight.dtype))

            if b is not None and proj.up.bias is not None:
                n = min(b.numel(), proj.up.bias.numel())
                proj.up.bias.data[:n].copy_(b.detach()[:n].to(proj.up.bias.dtype))
            elif proj.up.bias is not None:
                proj.up.bias.data.zero_()

            # Invalidate cache
            proj._cached_weight = None
            proj._cached_versions = ()

    # ------------------------------------------------------------------
    # AH-FIX-05: copy_weights with proper Conv1D handling
    # ------------------------------------------------------------------
    def copy_weights(self, old_attn: nn.Module) -> None:
        try:
            for p in old_attn.parameters():
                self._orig_storage_bytes += p.numel() * p.element_size()
        except Exception:
            pass

        from neuralops.core.adapters import UniversalAdapter

        extracted: dict[str, tuple[torch.Tensor | None, torch.Tensor | None]] = {
            "q": (None, None),
            "k": (None, None),
            "v": (None, None),
            "o": (None, None),
        }

        # UniversalAdapter first
        for p in ("q", "k", "v", "o"):
            try:
                w, b = UniversalAdapter.get_weight_bias(old_attn, p)
                if w is not None:
                    extracted[p] = (w, b)
            except Exception:
                pass

        # GPT-2 fused c_attn
        if hasattr(old_attn, "c_attn") and extracted["q"][0] is None:
            c = old_attn.c_attn
            c_w = c.weight.data.clone()
            # Conv1D stores weight as (in, out) → transpose to (out, in)
            if (
                c_w.shape[1] == 3 * self.hidden_size
                or c_w.ndim == 2
                and c_w.shape[0] < c_w.shape[1]
            ):
                c_w = c_w.t().contiguous()
            split = c_w.size(0) // 3
            c_b = getattr(c, "bias", None)
            c_b = c_b.data.clone() if c_b is not None else None
            extracted["q"] = (c_w[:split], c_b[:split] if c_b is not None else None)
            extracted["k"] = (
                c_w[split : 2 * split],
                c_b[split : 2 * split] if c_b is not None else None,
            )
            extracted["v"] = (
                c_w[2 * split :],
                c_b[2 * split :] if c_b is not None else None,
            )

        if hasattr(old_attn, "c_proj") and extracted["o"][0] is None:
            c = old_attn.c_proj
            w_o = c.weight.data.clone()
            if w_o.ndim == 2 and w_o.shape[0] < w_o.shape[1]:
                w_o = w_o.t().contiguous()
            b_o = getattr(c, "bias", None)
            extracted["o"] = (w_o, b_o.data.clone() if b_o is not None else None)

        # Nested BERT/RoBERTa
        if hasattr(old_attn, "self") and hasattr(old_attn.self, "query"):
            s = old_attn.self
            for key, sub in (("q", "query"), ("k", "key"), ("v", "value")):
                if extracted[key][0] is None:
                    m = getattr(s, sub, None)
                    if m is not None and hasattr(m, "weight"):
                        b = getattr(m, "bias", None)
                        extracted[key] = (
                            m.weight.data.clone(),
                            b.data.clone() if b is not None else None,
                        )
            if (
                extracted["o"][0] is None
                and hasattr(old_attn, "output")
                and hasattr(old_attn.output, "dense")
            ):
                d = old_attn.output.dense
                b = getattr(d, "bias", None)
                extracted["o"] = (
                    d.weight.data.clone(),
                    b.data.clone() if b is not None else None,
                )

        # Flat modules
        named_map = {
            "q": ("q_proj", "q_lin", "query"),
            "k": ("k_proj", "k_lin", "key"),
            "v": ("v_proj", "v_lin", "value"),
            "o": ("o_proj", "out_proj", "out_lin", "dense"),
        }
        for key, names in named_map.items():
            if extracted[key][0] is None:
                for n in names:
                    if hasattr(old_attn, n):
                        m = getattr(old_attn, n)
                        if m is not None and hasattr(m, "weight"):
                            b = getattr(m, "bias", None)
                            extracted[key] = (
                                m.weight.data.clone(),
                                b.data.clone() if b is not None else None,
                            )
                            break

        # Apply SVD init
        for key, target in (
            ("q", self.q_proj),
            ("k", self.k_proj),
            ("v", self.v_proj),
            ("o", self.out_proj),
        ):
            w, b = extracted[key]
            if w is not None:
                try:
                    self._svd_init(
                        w,
                        b,
                        target,
                        whitening_L=self.whitening.get(key, None),
                    )
                except Exception as e:
                    logger.warning("Aegis-Hybrid SVD init failed for '%s': %s", key, e)
            elif key == "o" and self.is_nested_encoder:
                # Identity for nested encoder output (bypassed externally)
                with torch.no_grad():
                    r = min(target.out_features, target.in_features, target.rank)
                    target.down.weight.data.zero_()
                    target.up.weight.data.zero_()
                    target.down.weight.data[:r, :r].fill_diagonal_(1.0)
                    target.up.weight.data[:r, :r].fill_diagonal_(1.0)

        # storage accounting
        try:
            for m in (self.q_proj, self.k_proj, self.v_proj, self.out_proj):
                for p in m.parameters():
                    self._comp_storage_bytes += p.numel() * p.element_size()
        except Exception:
            pass

    # ------------------------------------------------------------------
    # AH-FIX-06: comprehensive mask normalization
    # ------------------------------------------------------------------
    @staticmethod
    def _prepare_valid_mask(attention_mask, B, L, device) -> torch.Tensor | None:
        if attention_mask is None:
            return None

        def _to_bool(t):
            if t.dtype == torch.bool:
                return t
            if t.dtype.is_floating_point:
                return t > -1e4
            return t > 0

        if attention_mask.dim() == 2:
            m = _to_bool(attention_mask)
            if m.shape == (B, L):
                return m
            if m.shape[0] == B and m.shape[1] != L:
                m = m[:, :L]
                if m.shape[1] == L:
                    return m
        elif attention_mask.dim() == 3:
            m = _to_bool(attention_mask)
            if m.shape[0] == B and m.shape[-1] == L:
                return m[:, -1, :]
        elif attention_mask.dim() == 4:
            m = _to_bool(attention_mask)
            m = m.any(dim=-2)
            if m.dim() == 3:
                m = m.squeeze(1)
            if m.shape == (B, L):
                return m
        return None

    # ------------------------------------------------------------------
    # AH-FIX-07: correct past_len from cache shape
    # ------------------------------------------------------------------
    @staticmethod
    def _read_past_kv(past_key_value, layer_idx=0):
        if past_key_value is None:
            return None, None
        if hasattr(past_key_value, "key_cache") and hasattr(past_key_value, "value_cache"):
            if not past_key_value.key_cache:
                return None, None
            idx = min(layer_idx, len(past_key_value.key_cache) - 1)
            return past_key_value.key_cache[idx], past_key_value.value_cache[idx]
        if isinstance(past_key_value, (tuple, list)) and len(past_key_value) >= 2:
            return past_key_value[0], past_key_value[1]
        return None, None

    # ------------------------------------------------------------------
    # Forward
    # ------------------------------------------------------------------
    def forward(
        self,
        hidden_states: torch.Tensor,
        attention_mask: torch.Tensor | None = None,
        head_mask: torch.Tensor | None = None,
        encoder_hidden_states: torch.Tensor | None = None,
        encoder_attention_mask: torch.Tensor | None = None,
        past_key_value: Any | None = None,
        output_attentions: bool | None = False,
        output_hidden_states: bool | None = False,
        return_dict: bool | None = True,
        position_embeddings: tuple[torch.Tensor, torch.Tensor] | None = None,
        layer_idx: int = 0,
        *args: Any,
        **kwargs: Any,
    ):
        if not hidden_states.is_contiguous():
            hidden_states = hidden_states.contiguous()

        B, N, _ = hidden_states.shape
        device = hidden_states.device
        Dh = self.head_dim

        is_cross = encoder_hidden_states is not None
        kv_input = encoder_hidden_states if is_cross else hidden_states
        N_kv_new = kv_input.shape[1]

        Q = self.q_proj(hidden_states).view(B, N, self.num_heads, Dh).transpose(1, 2).contiguous()
        K_new = (
            self.k_proj(kv_input)
            .view(B, N_kv_new, self.num_kv_heads, Dh)
            .transpose(1, 2)
            .contiguous()
        )
        V_new = (
            self.v_proj(kv_input)
            .view(B, N_kv_new, self.num_kv_heads, Dh)
            .transpose(1, 2)
            .contiguous()
        )

        # ---- RoPE ----
        if position_embeddings is None:
            position_embeddings = kwargs.get("position_embeddings")
        if position_embeddings is not None and not is_cross:
            cos, sin = position_embeddings
            past_k_probe, _ = self._read_past_kv(past_key_value, layer_idx)
            past_len_rope = 0 if past_k_probe is None else past_k_probe.shape[-2]
            cos = cos[..., past_len_rope : past_len_rope + N, :]
            sin = sin[..., past_len_rope : past_len_rope + N, :]
            Q, K_new = apply_rotary_pos_emb(Q, K_new, cos, sin)

        # ---- KV cache ----
        past_k, past_v = self._read_past_kv(past_key_value, layer_idx)
        if past_k is not None and not is_cross:
            K_cat = torch.cat([past_k, K_new], dim=-2)
            V_cat = torch.cat([past_v, V_new], dim=-2)
            past_len = past_k.shape[-2]
        else:
            K_cat, V_cat = K_new, V_new
            past_len = 0

        if past_key_value is not None and hasattr(past_key_value, "update") and not is_cross:
            past_key_value.update(K_new, V_new, layer_idx)

        N_kv = K_cat.shape[2]

        K_full = repeat_kv(K_cat, self.kv_repeat) if self.kv_repeat > 1 else K_cat
        V_full = repeat_kv(V_cat, self.kv_repeat) if self.kv_repeat > 1 else V_cat

        # ---- mask ----
        eff_mask = encoder_attention_mask if is_cross else attention_mask
        token_valid = self._prepare_valid_mask(eff_mask, B, N_kv, device)

        # ---- local sliding window ----
        local_out, attn_local = _local_window_attention_chunked(
            Q,
            K_full,
            V_full,
            window=self.local_window,
            token_valid=token_valid,
            is_decoder=self.is_decoder and not is_cross,
            past_len=past_len,
            dropout_p=self.config.dropout,
            training=self.training,
        )

        # ---- global linear attention (optional) ----
        if self.use_global:
            global_out = _global_linear_attention(
                Q,
                K_full,
                V_full,
                self.kernel_map,
                token_valid,
            )
            beta = torch.sigmoid(self.fusion_beta).view(1, self.num_heads, 1, 1)
            # β → local, (1-β) → global
            fused = beta * local_out + (1.0 - beta) * global_out
        else:
            fused = local_out

        # ---- head_mask ----
        if head_mask is not None:
            hm = head_mask.to(fused.dtype)
            if hm.dim() == 1:
                hm = hm.view(1, -1, 1, 1)
            elif hm.dim() == 2:
                hm = hm.view(hm.shape[0], hm.shape[1], 1, 1)
            elif hm.dim() == 3:
                hm = hm.unsqueeze(-1)
            fused = fused * hm

        fused = fused.transpose(1, 2).contiguous().view(B, N, self.all_head_size)
        fused = torch.nan_to_num(fused, nan=0.0, posinf=1e4, neginf=-1e4)

        if self.is_nested_encoder:
            final = fused
        else:
            final = self.out_proj(fused)

        final = torch.nan_to_num(final, nan=0.0, posinf=1e4, neginf=-1e4)

        return final.to(hidden_states.dtype), (attn_local if output_attentions else None)

    # ------------------------------------------------------------------
    def storage_bytes(self) -> dict[str, int]:
        def _sz(p):
            if isinstance(p, (nn.Parameter, torch.Tensor)):
                return p.numel() * p.element_size()
            return 0

        out = {}
        for name, m in (
            ("q_proj", self.q_proj),
            ("k_proj", self.k_proj),
            ("v_proj", self.v_proj),
            ("out_proj", self.out_proj),
            ("kernel_map", self.kernel_map),
        ):
            out[name] = sum(_sz(p) for p in m.parameters())
        out["fusion_beta"] = _sz(self.fusion_beta)
        out["total"] = sum(out.values())
        return out

Every custom attention module must inherit from this.

Initialize internal Module state, shared by both nn.Module and ScriptModule.

Ancestors

Class variables

var is_custom_attention : bool

The type of the None singleton.

var supports_causal : bool

The type of the None singleton.

var supports_gqa : bool

The type of the None singleton.

var supports_rope : bool

The type of the None singleton.

Instance variables

prop c_proj
Expand source code
@property
def c_proj(self):
    return self.out_proj
prop dense
Expand source code
@property
def dense(self):
    return self.out_proj
prop k_lin
Expand source code
@property
def k_lin(self):
    return self.k_proj
prop key
Expand source code
@property
def key(self):
    return self.k_proj
prop o_proj
Expand source code
@property
def o_proj(self):
    return self.out_proj
prop out_lin
Expand source code
@property
def out_lin(self):
    return self.out_proj
prop q_lin
Expand source code
@property
def q_lin(self):
    return self.q_proj
prop query
Expand source code
@property
def query(self):
    return self.q_proj
prop v_lin
Expand source code
@property
def v_lin(self):
    return self.v_proj
prop value
Expand source code
@property
def value(self):
    return self.v_proj

Methods

def storage_bytes(self) ‑> dict[str, int]
Expand source code
def storage_bytes(self) -> dict[str, int]:
    def _sz(p):
        if isinstance(p, (nn.Parameter, torch.Tensor)):
            return p.numel() * p.element_size()
        return 0

    out = {}
    for name, m in (
        ("q_proj", self.q_proj),
        ("k_proj", self.k_proj),
        ("v_proj", self.v_proj),
        ("out_proj", self.out_proj),
        ("kernel_map", self.kernel_map),
    ):
        out[name] = sum(_sz(p) for p in m.parameters())
    out["fusion_beta"] = _sz(self.fusion_beta)
    out["total"] = sum(out.values())
    return out

Inherited members

class AegisHybridConfig (hidden_size: int = 768,
num_attention_heads: int = 12,
head_dim: int = 64,
num_key_value_heads: int | None = None,
local_window: int = 256,
rank: int = 128,
lora_rank: int = 32,
dropout: float = 0.1,
use_adaptive_pruning: bool = False,
gate_init_bias: float = 1.8,
is_decoder: bool = False,
use_global: bool = True,
kernel_feature_dim: int = 128,
kernel_seed: int = 1234,
chunk_size: int = 64)
Expand source code
@dataclass
class AegisHybridConfig:
    hidden_size: int = 768
    num_attention_heads: int = 12
    head_dim: int = 64
    num_key_value_heads: int | None = None
    local_window: int = 256
    rank: int = 128
    lora_rank: int = 32
    dropout: float = 0.1
    use_adaptive_pruning: bool = False
    gate_init_bias: float = 1.8
    is_decoder: bool = False
    use_global: bool = True
    kernel_feature_dim: int = 128
    kernel_seed: int = 1234
    chunk_size: int = 64

    def __post_init__(self) -> None:
        if self.num_key_value_heads is None:
            self.num_key_value_heads = self.num_attention_heads

AegisHybridConfig(hidden_size: 'int' = 768, num_attention_heads: 'int' = 12, head_dim: 'int' = 64, num_key_value_heads: 'int | None' = None, local_window: 'int' = 256, rank: 'int' = 128, lora_rank: 'int' = 32, dropout: 'float' = 0.1, use_adaptive_pruning: 'bool' = False, gate_init_bias: 'float' = 1.8, is_decoder: 'bool' = False, use_global: 'bool' = True, kernel_feature_dim: 'int' = 128, kernel_seed: 'int' = 1234, chunk_size: 'int' = 64)

Instance variables

var chunk_size : int

The type of the None singleton.

var dropout : float

The type of the None singleton.

var gate_init_bias : float

The type of the None singleton.

var head_dim : int

The type of the None singleton.

var hidden_size : int

The type of the None singleton.

var is_decoder : bool

The type of the None singleton.

var kernel_feature_dim : int

The type of the None singleton.

var kernel_seed : int

The type of the None singleton.

var local_window : int

The type of the None singleton.

var lora_rank : int

The type of the None singleton.

var num_attention_heads : int

The type of the None singleton.

var num_key_value_heads : int | None

The type of the None singleton.

var rank : int

The type of the None singleton.

var use_adaptive_pruning : bool

The type of the None singleton.

var use_global : bool

The type of the None singleton.

class FactorizedLinear (in_features: int,
out_features: int,
rank: int = 128,
lora_rank: int = 32,
bias: bool = True,
device: torch.device | None = None,
dtype: torch.dtype | None = None)
Expand source code
class FactorizedLinear(nn.Module):
    """
    Low-rank factorized linear + LoRA:
        y = (x @ V.T) @ U.T + LoRA(x) + b
    """

    is_custom_attention: bool = False  # AH-FIX-01

    def __init__(
        self,
        in_features: int,
        out_features: int,
        rank: int = 128,
        lora_rank: int = 32,
        bias: bool = True,
        device: torch.device | None = None,
        dtype: torch.dtype | None = None,
    ) -> None:
        super().__init__()
        self.in_features = int(in_features)
        self.out_features = int(out_features)
        self.rank = max(1, min(int(rank), self.in_features, self.out_features))
        self.svd_rank = self.rank
        self.lora_rank = (
            min(int(lora_rank), min(self.in_features, self.out_features)) if lora_rank > 0 else 0
        )

        fk = {"device": device, "dtype": dtype}
        # AH-FIX-14: consistent `down`/`up` naming
        self.down = nn.Linear(self.in_features, self.rank, bias=False, **fk)
        self.up = nn.Linear(self.rank, self.out_features, bias=bias, **fk)

        self.down.weight.requires_grad = False
        self.up.weight.requires_grad = False
        if self.up.bias is not None:
            self.up.bias.requires_grad = False

        if self.lora_rank > 0:
            self.lora_A = nn.Parameter(torch.empty(self.lora_rank, self.in_features, **fk))
            self.lora_B = nn.Parameter(torch.empty(self.out_features, self.lora_rank, **fk))
        else:
            self.register_parameter("lora_A", None)
            self.register_parameter("lora_B", None)

        # AH-FIX-03: cached weight, invalidated on parameter mutation
        self._cached_weight: torch.Tensor | None = None
        self._cached_versions: tuple[int, ...] = ()

        self.reset_parameters()

    # -- backward-compat aliases --
    @property
    def lin1(self):
        return self.down

    @property
    def lin2(self):
        return self.up

    def reset_parameters(self) -> None:
        with torch.no_grad():
            nn.init.kaiming_uniform_(self.down.weight, a=math.sqrt(5))
            nn.init.kaiming_uniform_(self.up.weight, a=math.sqrt(5))
            if self.up.bias is not None:
                nn.init.zeros_(self.up.bias)
            if self.lora_A is not None:
                nn.init.kaiming_uniform_(self.lora_A, a=math.sqrt(5))
                nn.init.zeros_(self.lora_B)

    def _current_versions(self) -> tuple[int, ...]:
        vs = [
            getattr(self.down.weight, "_version", 0),
            getattr(self.up.weight, "_version", 0),
        ]
        if self.lora_A is not None:
            vs.append(getattr(self.lora_A, "_version", 0))
            vs.append(getattr(self.lora_B, "_version", 0))
        return tuple(vs)

    @property
    def weight(self) -> torch.Tensor:
        # AH-FIX-03
        if (
            not self.training
            and self._cached_weight is not None
            and self._cached_versions == self._current_versions()
        ):
            return self._cached_weight
        w = torch.matmul(self.up.weight.float(), self.down.weight.float())
        if self.lora_A is not None:
            w = w + torch.matmul(self.lora_B.float(), self.lora_A.float())
        w = w.to(self.up.weight.dtype)
        if not self.training:
            self._cached_weight = w
            self._cached_versions = self._current_versions()
        return w

    @property
    def bias(self) -> torch.Tensor | None:
        return self.up.bias

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        orig_dtype = x.dtype
        x_down = x.to(self.down.weight.dtype)
        latent = self.down(x_down)
        latent_up = latent.to(self.up.weight.dtype)
        out = self.up(latent_up)
        if self.lora_A is not None:
            a = self.lora_A.to(orig_dtype) if self.lora_A.dtype != orig_dtype else self.lora_A
            b = self.lora_B.to(orig_dtype) if self.lora_B.dtype != orig_dtype else self.lora_B
            out = out.to(orig_dtype) + F.linear(F.linear(x, a), b)
        return out.to(orig_dtype)

Low-rank factorized linear + LoRA: y = (x @ V.T) @ U.T + LoRA(x) + b

Initialize internal Module state, shared by both nn.Module and ScriptModule.

Ancestors

  • torch.nn.modules.module.Module

Class variables

var is_custom_attention : bool

The type of the None singleton.

Instance variables

prop bias : torch.Tensor | None
Expand source code
@property
def bias(self) -> torch.Tensor | None:
    return self.up.bias
prop lin1
Expand source code
@property
def lin1(self):
    return self.down
prop lin2
Expand source code
@property
def lin2(self):
    return self.up
prop weight : torch.Tensor
Expand source code
@property
def weight(self) -> torch.Tensor:
    # AH-FIX-03
    if (
        not self.training
        and self._cached_weight is not None
        and self._cached_versions == self._current_versions()
    ):
        return self._cached_weight
    w = torch.matmul(self.up.weight.float(), self.down.weight.float())
    if self.lora_A is not None:
        w = w + torch.matmul(self.lora_B.float(), self.lora_A.float())
    w = w.to(self.up.weight.dtype)
    if not self.training:
        self._cached_weight = w
        self._cached_versions = self._current_versions()
    return w

Methods

def forward(self, x: torch.Tensor) ‑> torch.Tensor
Expand source code
def forward(self, x: torch.Tensor) -> torch.Tensor:
    orig_dtype = x.dtype
    x_down = x.to(self.down.weight.dtype)
    latent = self.down(x_down)
    latent_up = latent.to(self.up.weight.dtype)
    out = self.up(latent_up)
    if self.lora_A is not None:
        a = self.lora_A.to(orig_dtype) if self.lora_A.dtype != orig_dtype else self.lora_A
        b = self.lora_B.to(orig_dtype) if self.lora_B.dtype != orig_dtype else self.lora_B
        out = out.to(orig_dtype) + F.linear(F.linear(x, a), b)
    return out.to(orig_dtype)

Define the computation performed at every call.

Should be overridden by all subclasses.

Note

Although the recipe for forward pass needs to be defined within this function, one should call the :class:Module instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.

def reset_parameters(self) ‑> None
Expand source code
def reset_parameters(self) -> None:
    with torch.no_grad():
        nn.init.kaiming_uniform_(self.down.weight, a=math.sqrt(5))
        nn.init.kaiming_uniform_(self.up.weight, a=math.sqrt(5))
        if self.up.bias is not None:
            nn.init.zeros_(self.up.bias)
        if self.lora_A is not None:
            nn.init.kaiming_uniform_(self.lora_A, a=math.sqrt(5))
            nn.init.zeros_(self.lora_B)
class KernelFeatureMap (head_dim: int, num_features: int = 128, seed: int = 1234)
Expand source code
class KernelFeatureMap(nn.Module):
    """FAVOR+ style random Fourier features with deterministic seed."""

    is_custom_attention: bool = False

    def __init__(self, head_dim: int, num_features: int = 128, seed: int = 1234):
        super().__init__()
        self.head_dim = int(head_dim)
        self.num_features = int(num_features)
        gen = torch.Generator(device="cpu").manual_seed(int(seed))
        proj = torch.randn(self.head_dim, self.num_features, generator=gen)
        # Orthogonal init if m <= d
        if self.num_features <= self.head_dim:
            q, _ = torch.linalg.qr(torch.randn(self.head_dim, self.num_features, generator=gen))
            proj = q
        self.register_buffer("proj", proj * math.sqrt(self.head_dim))
        self.scale = 1.0 / math.sqrt(self.num_features)

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        with torch.autocast(device_type=x.device.type, enabled=False):
            x_f = x.float()
            proj = self.proj.float()
            phi = torch.matmul(x_f, proj)
            norm_sq = (x_f * x_f).sum(dim=-1, keepdim=True) / 2.0
            raw = phi - norm_sq
            max_val = raw.detach().max()
            exp_in = torch.clamp(raw - max_val, min=-20.0, max=8.0)
            return (self.scale * torch.exp(exp_in)).to(x.dtype)

FAVOR+ style random Fourier features with deterministic seed.

Initialize internal Module state, shared by both nn.Module and ScriptModule.

Ancestors

  • torch.nn.modules.module.Module

Class variables

var is_custom_attention : bool

The type of the None singleton.

Methods

def forward(self, x: torch.Tensor) ‑> torch.Tensor
Expand source code
def forward(self, x: torch.Tensor) -> torch.Tensor:
    with torch.autocast(device_type=x.device.type, enabled=False):
        x_f = x.float()
        proj = self.proj.float()
        phi = torch.matmul(x_f, proj)
        norm_sq = (x_f * x_f).sum(dim=-1, keepdim=True) / 2.0
        raw = phi - norm_sq
        max_val = raw.detach().max()
        exp_in = torch.clamp(raw - max_val, min=-20.0, max=8.0)
        return (self.scale * torch.exp(exp_in)).to(x.dtype)

Define the computation performed at every call.

Should be overridden by all subclasses.

Note

Although the recipe for forward pass needs to be defined within this function, one should call the :class:Module instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.