Module neuralops.attentions.aegis_v2

Aegis-Attention V2 (Corrected).

Hybrid low-rank factorized attention with: * SVD + LoRA projections (FactorisedLinear) * Local sliding-window attention over all heads * Global ELU+1 linear kernel attention with adaptive soft-pruning gate * Data-dependent multi-head fusion gate

Functions

def apply_rotary_pos_emb(q: torch.Tensor, k: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) ‑> tuple[torch.Tensor, torch.Tensor]
Expand source code
def apply_rotary_pos_emb(
    q: torch.Tensor,
    k: torch.Tensor,
    cos: torch.Tensor,
    sin: torch.Tensor,
) -> tuple[torch.Tensor, torch.Tensor]:
    """Apply RoPE. cos/sin must already be sliced to the current window."""
    if cos.dim() == 2:
        cos = cos.unsqueeze(0).unsqueeze(0)
        sin = sin.unsqueeze(0).unsqueeze(0)
    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)

    if cos.shape[-2] != q.shape[-2]:
        raise ValueError(
            f"apply_rotary_pos_emb: cos/sin seq len {cos.shape[-2]} "
            f"does not match q seq len {q.shape[-2]}."
        )

    rotary_dim = cos.shape[-1]
    head_dim = q.shape[-1]

    if rotary_dim <= head_dim:
        q_rot, q_pass = q[..., :rotary_dim], q[..., rotary_dim:]
        k_rot, k_pass = k[..., :rotary_dim], k[..., rotary_dim:]
        q_rot_embed = (q_rot * cos.to(q_rot.dtype)) + (_rotate_half(q_rot) * sin.to(q_rot.dtype))
        k_rot_embed = (k_rot * cos.to(k_rot.dtype)) + (_rotate_half(k_rot) * sin.to(k_rot.dtype))
        q = torch.cat([q_rot_embed, q_pass], dim=-1) if q_pass.numel() > 0 else q_rot_embed
        k = torch.cat([k_rot_embed, k_pass], dim=-1) if k_pass.numel() > 0 else k_rot_embed
    else:
        cos_cut, sin_cut = cos[..., :head_dim], sin[..., :head_dim]
        q = (q * cos_cut.to(q.dtype)) + (_rotate_half(q) * sin_cut.to(q.dtype))
        k = (k * cos_cut.to(k.dtype)) + (_rotate_half(k) * sin_cut.to(k.dtype))

    return q, k

Apply RoPE. cos/sin must already be sliced to the current window.

def inject_aegis_into_distilbert_v2(layer: Any = None,
model: Any = None,
model_config: Any = None,
comp_config: Any = None,
layer_idx: int = 0,
total_layers: int = 12,
old_attn: torch.nn.modules.module.Module | None = None,
**kwargs: Any)
Expand source code
@ATTENTION_REGISTRY.register("aegis_v2_inject")
@ATTENTION_REGISTRY.register("inject_aegis_v2")
@ATTENTION_REGISTRY.register("inject_aegis_v1")
@ATTENTION_REGISTRY.register("inject_aegis_v3")
@ATTENTION_REGISTRY.register("inject_aegis_v4")
def inject_aegis_v2(
    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_v2: max recursion depth, aborting.")
        return None

    target = layer if layer is not None else model
    if target is None:
        target = kwargs.get("model", kwargs.get("layer"))
    if target is None:
        return None

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

    layer_list = None
    if hasattr(target, "distilbert") and hasattr(target.distilbert, "transformer"):
        layer_list = target.distilbert.transformer.layer
    elif hasattr(target, "roberta") and hasattr(target.roberta, "encoder"):
        layer_list = target.roberta.encoder.layer
    elif hasattr(target, "transformer") and hasattr(target.transformer, "h"):
        layer_list = target.transformer.h
    elif hasattr(target, "h") and isinstance(target.h, (nn.ModuleList, list)):
        layer_list = target.h
    elif hasattr(target, "model") and hasattr(target.model, "layers"):
        layer_list = target.model.layers
    elif hasattr(target, "encoder") and hasattr(target.encoder, "layer"):
        layer_list = target.encoder.layer
    elif (
        hasattr(target, "bert")
        and hasattr(target.bert, "encoder")
        and hasattr(target.bert.encoder, "layer")
    ):
        layer_list = target.bert.encoder.layer
    elif hasattr(target, "layers") and isinstance(target.layers, (nn.ModuleList, list)):
        layer_list = target.layers

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

    layer = target
    try:
        if model_config is None:
            if any(hasattr(layer, a) for a in ("hidden_size", "d_model", "n_embd", "dim")):
                model_config = layer
            elif hasattr(layer, "attention"):
                old_attn = layer.attention

        h_size = (
            getattr(
                model_config,
                "hidden_size",
                getattr(
                    model_config,
                    "d_model",
                    getattr(model_config, "n_embd", getattr(model_config, "dim", 768)),
                ),
            )
            if model_config is not None
            else 768
        )
        n_heads = (
            getattr(
                model_config,
                "num_attention_heads",
                getattr(
                    model_config,
                    "num_heads",
                    getattr(model_config, "n_heads", getattr(model_config, "n_head", 12)),
                ),
            )
            if model_config is not None
            else 12
        )
        h_dim = (
            getattr(
                model_config,
                "head_dim",
                getattr(model_config, "d_kv", h_size // max(1, n_heads)),
            )
            if model_config is not None
            else h_size // max(1, n_heads)
        )
        n_kv_heads = (
            getattr(
                model_config,
                "num_key_value_heads",
                getattr(model_config, "num_kv_heads", n_heads),
            )
            if model_config is not None
            else n_heads
        )

        src_attn = (
            old_attn
            if old_attn is not None
            else getattr(layer, "attention", getattr(layer, "self", getattr(layer, "attn", None)))
        )

        # V2-FIX-01: use shared helper
        is_dec = _detect_decoder(model_config, None, src_attn)

        cfg = AegisConfigV2(
            hidden_size=h_size,
            num_attention_heads=n_heads,
            num_heads=n_heads,
            num_key_value_heads=n_kv_heads,
            head_dim=h_dim,
            local_window=getattr(comp_config, "block_size", 64),
            rank=getattr(comp_config, "rank", getattr(comp_config, "lora_r", 192)),
            kernel_feature_dim=getattr(comp_config, "rff_features", 128),
            dropout=getattr(comp_config, "dropout", 0.1),
            use_adaptive_pruning=getattr(comp_config, "use_gate", True),
            gate_init_bias=getattr(comp_config, "gate_init_bias", 1.8),
            is_decoder=is_dec,
        )
        new_attn = AegisAttentionV2(config=cfg)
        if src_attn is not None:
            try:
                new_attn.copy_weights(src_attn)
            except Exception as e:
                logger.error("inject_aegis_v2: copy_weights failed: %s", e)
                return None
            p = next(src_attn.parameters(), None)
            if p is not None:
                new_attn.to(device=p.device, dtype=p.dtype)

        if hasattr(layer, "attention"):
            if hasattr(layer.attention, "self"):
                layer.attention.self = new_attn
            else:
                layer.attention = new_attn
        elif hasattr(layer, "self"):
            layer.self = new_attn
        elif hasattr(layer, "attn"):
            layer.attn = new_attn
        return new_attn
    except Exception as e:
        logger.exception("Failed to inject AEGIS-V2: %s", e)
        return None
def inject_aegis_v1(layer: Any = None,
model: Any = None,
model_config: Any = None,
comp_config: Any = None,
layer_idx: int = 0,
total_layers: int = 12,
old_attn: torch.nn.modules.module.Module | None = None,
**kwargs: Any)
Expand source code
@ATTENTION_REGISTRY.register("aegis_v2_inject")
@ATTENTION_REGISTRY.register("inject_aegis_v2")
@ATTENTION_REGISTRY.register("inject_aegis_v1")
@ATTENTION_REGISTRY.register("inject_aegis_v3")
@ATTENTION_REGISTRY.register("inject_aegis_v4")
def inject_aegis_v2(
    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_v2: max recursion depth, aborting.")
        return None

    target = layer if layer is not None else model
    if target is None:
        target = kwargs.get("model", kwargs.get("layer"))
    if target is None:
        return None

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

    layer_list = None
    if hasattr(target, "distilbert") and hasattr(target.distilbert, "transformer"):
        layer_list = target.distilbert.transformer.layer
    elif hasattr(target, "roberta") and hasattr(target.roberta, "encoder"):
        layer_list = target.roberta.encoder.layer
    elif hasattr(target, "transformer") and hasattr(target.transformer, "h"):
        layer_list = target.transformer.h
    elif hasattr(target, "h") and isinstance(target.h, (nn.ModuleList, list)):
        layer_list = target.h
    elif hasattr(target, "model") and hasattr(target.model, "layers"):
        layer_list = target.model.layers
    elif hasattr(target, "encoder") and hasattr(target.encoder, "layer"):
        layer_list = target.encoder.layer
    elif (
        hasattr(target, "bert")
        and hasattr(target.bert, "encoder")
        and hasattr(target.bert.encoder, "layer")
    ):
        layer_list = target.bert.encoder.layer
    elif hasattr(target, "layers") and isinstance(target.layers, (nn.ModuleList, list)):
        layer_list = target.layers

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

    layer = target
    try:
        if model_config is None:
            if any(hasattr(layer, a) for a in ("hidden_size", "d_model", "n_embd", "dim")):
                model_config = layer
            elif hasattr(layer, "attention"):
                old_attn = layer.attention

        h_size = (
            getattr(
                model_config,
                "hidden_size",
                getattr(
                    model_config,
                    "d_model",
                    getattr(model_config, "n_embd", getattr(model_config, "dim", 768)),
                ),
            )
            if model_config is not None
            else 768
        )
        n_heads = (
            getattr(
                model_config,
                "num_attention_heads",
                getattr(
                    model_config,
                    "num_heads",
                    getattr(model_config, "n_heads", getattr(model_config, "n_head", 12)),
                ),
            )
            if model_config is not None
            else 12
        )
        h_dim = (
            getattr(
                model_config,
                "head_dim",
                getattr(model_config, "d_kv", h_size // max(1, n_heads)),
            )
            if model_config is not None
            else h_size // max(1, n_heads)
        )
        n_kv_heads = (
            getattr(
                model_config,
                "num_key_value_heads",
                getattr(model_config, "num_kv_heads", n_heads),
            )
            if model_config is not None
            else n_heads
        )

        src_attn = (
            old_attn
            if old_attn is not None
            else getattr(layer, "attention", getattr(layer, "self", getattr(layer, "attn", None)))
        )

        # V2-FIX-01: use shared helper
        is_dec = _detect_decoder(model_config, None, src_attn)

        cfg = AegisConfigV2(
            hidden_size=h_size,
            num_attention_heads=n_heads,
            num_heads=n_heads,
            num_key_value_heads=n_kv_heads,
            head_dim=h_dim,
            local_window=getattr(comp_config, "block_size", 64),
            rank=getattr(comp_config, "rank", getattr(comp_config, "lora_r", 192)),
            kernel_feature_dim=getattr(comp_config, "rff_features", 128),
            dropout=getattr(comp_config, "dropout", 0.1),
            use_adaptive_pruning=getattr(comp_config, "use_gate", True),
            gate_init_bias=getattr(comp_config, "gate_init_bias", 1.8),
            is_decoder=is_dec,
        )
        new_attn = AegisAttentionV2(config=cfg)
        if src_attn is not None:
            try:
                new_attn.copy_weights(src_attn)
            except Exception as e:
                logger.error("inject_aegis_v2: copy_weights failed: %s", e)
                return None
            p = next(src_attn.parameters(), None)
            if p is not None:
                new_attn.to(device=p.device, dtype=p.dtype)

        if hasattr(layer, "attention"):
            if hasattr(layer.attention, "self"):
                layer.attention.self = new_attn
            else:
                layer.attention = new_attn
        elif hasattr(layer, "self"):
            layer.self = new_attn
        elif hasattr(layer, "attn"):
            layer.attn = new_attn
        return new_attn
    except Exception as e:
        logger.exception("Failed to inject AEGIS-V2: %s", e)
        return None
def inject_aegis_v2(layer: Any = None,
model: Any = None,
model_config: Any = None,
comp_config: Any = None,
layer_idx: int = 0,
total_layers: int = 12,
old_attn: torch.nn.modules.module.Module | None = None,
**kwargs: Any)
Expand source code
@ATTENTION_REGISTRY.register("aegis_v2_inject")
@ATTENTION_REGISTRY.register("inject_aegis_v2")
@ATTENTION_REGISTRY.register("inject_aegis_v1")
@ATTENTION_REGISTRY.register("inject_aegis_v3")
@ATTENTION_REGISTRY.register("inject_aegis_v4")
def inject_aegis_v2(
    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_v2: max recursion depth, aborting.")
        return None

    target = layer if layer is not None else model
    if target is None:
        target = kwargs.get("model", kwargs.get("layer"))
    if target is None:
        return None

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

    layer_list = None
    if hasattr(target, "distilbert") and hasattr(target.distilbert, "transformer"):
        layer_list = target.distilbert.transformer.layer
    elif hasattr(target, "roberta") and hasattr(target.roberta, "encoder"):
        layer_list = target.roberta.encoder.layer
    elif hasattr(target, "transformer") and hasattr(target.transformer, "h"):
        layer_list = target.transformer.h
    elif hasattr(target, "h") and isinstance(target.h, (nn.ModuleList, list)):
        layer_list = target.h
    elif hasattr(target, "model") and hasattr(target.model, "layers"):
        layer_list = target.model.layers
    elif hasattr(target, "encoder") and hasattr(target.encoder, "layer"):
        layer_list = target.encoder.layer
    elif (
        hasattr(target, "bert")
        and hasattr(target.bert, "encoder")
        and hasattr(target.bert.encoder, "layer")
    ):
        layer_list = target.bert.encoder.layer
    elif hasattr(target, "layers") and isinstance(target.layers, (nn.ModuleList, list)):
        layer_list = target.layers

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

    layer = target
    try:
        if model_config is None:
            if any(hasattr(layer, a) for a in ("hidden_size", "d_model", "n_embd", "dim")):
                model_config = layer
            elif hasattr(layer, "attention"):
                old_attn = layer.attention

        h_size = (
            getattr(
                model_config,
                "hidden_size",
                getattr(
                    model_config,
                    "d_model",
                    getattr(model_config, "n_embd", getattr(model_config, "dim", 768)),
                ),
            )
            if model_config is not None
            else 768
        )
        n_heads = (
            getattr(
                model_config,
                "num_attention_heads",
                getattr(
                    model_config,
                    "num_heads",
                    getattr(model_config, "n_heads", getattr(model_config, "n_head", 12)),
                ),
            )
            if model_config is not None
            else 12
        )
        h_dim = (
            getattr(
                model_config,
                "head_dim",
                getattr(model_config, "d_kv", h_size // max(1, n_heads)),
            )
            if model_config is not None
            else h_size // max(1, n_heads)
        )
        n_kv_heads = (
            getattr(
                model_config,
                "num_key_value_heads",
                getattr(model_config, "num_kv_heads", n_heads),
            )
            if model_config is not None
            else n_heads
        )

        src_attn = (
            old_attn
            if old_attn is not None
            else getattr(layer, "attention", getattr(layer, "self", getattr(layer, "attn", None)))
        )

        # V2-FIX-01: use shared helper
        is_dec = _detect_decoder(model_config, None, src_attn)

        cfg = AegisConfigV2(
            hidden_size=h_size,
            num_attention_heads=n_heads,
            num_heads=n_heads,
            num_key_value_heads=n_kv_heads,
            head_dim=h_dim,
            local_window=getattr(comp_config, "block_size", 64),
            rank=getattr(comp_config, "rank", getattr(comp_config, "lora_r", 192)),
            kernel_feature_dim=getattr(comp_config, "rff_features", 128),
            dropout=getattr(comp_config, "dropout", 0.1),
            use_adaptive_pruning=getattr(comp_config, "use_gate", True),
            gate_init_bias=getattr(comp_config, "gate_init_bias", 1.8),
            is_decoder=is_dec,
        )
        new_attn = AegisAttentionV2(config=cfg)
        if src_attn is not None:
            try:
                new_attn.copy_weights(src_attn)
            except Exception as e:
                logger.error("inject_aegis_v2: copy_weights failed: %s", e)
                return None
            p = next(src_attn.parameters(), None)
            if p is not None:
                new_attn.to(device=p.device, dtype=p.dtype)

        if hasattr(layer, "attention"):
            if hasattr(layer.attention, "self"):
                layer.attention.self = new_attn
            else:
                layer.attention = new_attn
        elif hasattr(layer, "self"):
            layer.self = new_attn
        elif hasattr(layer, "attn"):
            layer.attn = new_attn
        return new_attn
    except Exception as e:
        logger.exception("Failed to inject AEGIS-V2: %s", e)
        return None
def inject_aegis_v3(layer: Any = None,
model: Any = None,
model_config: Any = None,
comp_config: Any = None,
layer_idx: int = 0,
total_layers: int = 12,
old_attn: torch.nn.modules.module.Module | None = None,
**kwargs: Any)
Expand source code
@ATTENTION_REGISTRY.register("aegis_v2_inject")
@ATTENTION_REGISTRY.register("inject_aegis_v2")
@ATTENTION_REGISTRY.register("inject_aegis_v1")
@ATTENTION_REGISTRY.register("inject_aegis_v3")
@ATTENTION_REGISTRY.register("inject_aegis_v4")
def inject_aegis_v2(
    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_v2: max recursion depth, aborting.")
        return None

    target = layer if layer is not None else model
    if target is None:
        target = kwargs.get("model", kwargs.get("layer"))
    if target is None:
        return None

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

    layer_list = None
    if hasattr(target, "distilbert") and hasattr(target.distilbert, "transformer"):
        layer_list = target.distilbert.transformer.layer
    elif hasattr(target, "roberta") and hasattr(target.roberta, "encoder"):
        layer_list = target.roberta.encoder.layer
    elif hasattr(target, "transformer") and hasattr(target.transformer, "h"):
        layer_list = target.transformer.h
    elif hasattr(target, "h") and isinstance(target.h, (nn.ModuleList, list)):
        layer_list = target.h
    elif hasattr(target, "model") and hasattr(target.model, "layers"):
        layer_list = target.model.layers
    elif hasattr(target, "encoder") and hasattr(target.encoder, "layer"):
        layer_list = target.encoder.layer
    elif (
        hasattr(target, "bert")
        and hasattr(target.bert, "encoder")
        and hasattr(target.bert.encoder, "layer")
    ):
        layer_list = target.bert.encoder.layer
    elif hasattr(target, "layers") and isinstance(target.layers, (nn.ModuleList, list)):
        layer_list = target.layers

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

    layer = target
    try:
        if model_config is None:
            if any(hasattr(layer, a) for a in ("hidden_size", "d_model", "n_embd", "dim")):
                model_config = layer
            elif hasattr(layer, "attention"):
                old_attn = layer.attention

        h_size = (
            getattr(
                model_config,
                "hidden_size",
                getattr(
                    model_config,
                    "d_model",
                    getattr(model_config, "n_embd", getattr(model_config, "dim", 768)),
                ),
            )
            if model_config is not None
            else 768
        )
        n_heads = (
            getattr(
                model_config,
                "num_attention_heads",
                getattr(
                    model_config,
                    "num_heads",
                    getattr(model_config, "n_heads", getattr(model_config, "n_head", 12)),
                ),
            )
            if model_config is not None
            else 12
        )
        h_dim = (
            getattr(
                model_config,
                "head_dim",
                getattr(model_config, "d_kv", h_size // max(1, n_heads)),
            )
            if model_config is not None
            else h_size // max(1, n_heads)
        )
        n_kv_heads = (
            getattr(
                model_config,
                "num_key_value_heads",
                getattr(model_config, "num_kv_heads", n_heads),
            )
            if model_config is not None
            else n_heads
        )

        src_attn = (
            old_attn
            if old_attn is not None
            else getattr(layer, "attention", getattr(layer, "self", getattr(layer, "attn", None)))
        )

        # V2-FIX-01: use shared helper
        is_dec = _detect_decoder(model_config, None, src_attn)

        cfg = AegisConfigV2(
            hidden_size=h_size,
            num_attention_heads=n_heads,
            num_heads=n_heads,
            num_key_value_heads=n_kv_heads,
            head_dim=h_dim,
            local_window=getattr(comp_config, "block_size", 64),
            rank=getattr(comp_config, "rank", getattr(comp_config, "lora_r", 192)),
            kernel_feature_dim=getattr(comp_config, "rff_features", 128),
            dropout=getattr(comp_config, "dropout", 0.1),
            use_adaptive_pruning=getattr(comp_config, "use_gate", True),
            gate_init_bias=getattr(comp_config, "gate_init_bias", 1.8),
            is_decoder=is_dec,
        )
        new_attn = AegisAttentionV2(config=cfg)
        if src_attn is not None:
            try:
                new_attn.copy_weights(src_attn)
            except Exception as e:
                logger.error("inject_aegis_v2: copy_weights failed: %s", e)
                return None
            p = next(src_attn.parameters(), None)
            if p is not None:
                new_attn.to(device=p.device, dtype=p.dtype)

        if hasattr(layer, "attention"):
            if hasattr(layer.attention, "self"):
                layer.attention.self = new_attn
            else:
                layer.attention = new_attn
        elif hasattr(layer, "self"):
            layer.self = new_attn
        elif hasattr(layer, "attn"):
            layer.attn = new_attn
        return new_attn
    except Exception as e:
        logger.exception("Failed to inject AEGIS-V2: %s", e)
        return None
def inject_aegis_v4(layer: Any = None,
model: Any = None,
model_config: Any = None,
comp_config: Any = None,
layer_idx: int = 0,
total_layers: int = 12,
old_attn: torch.nn.modules.module.Module | None = None,
**kwargs: Any)
Expand source code
@ATTENTION_REGISTRY.register("aegis_v2_inject")
@ATTENTION_REGISTRY.register("inject_aegis_v2")
@ATTENTION_REGISTRY.register("inject_aegis_v1")
@ATTENTION_REGISTRY.register("inject_aegis_v3")
@ATTENTION_REGISTRY.register("inject_aegis_v4")
def inject_aegis_v2(
    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_v2: max recursion depth, aborting.")
        return None

    target = layer if layer is not None else model
    if target is None:
        target = kwargs.get("model", kwargs.get("layer"))
    if target is None:
        return None

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

    layer_list = None
    if hasattr(target, "distilbert") and hasattr(target.distilbert, "transformer"):
        layer_list = target.distilbert.transformer.layer
    elif hasattr(target, "roberta") and hasattr(target.roberta, "encoder"):
        layer_list = target.roberta.encoder.layer
    elif hasattr(target, "transformer") and hasattr(target.transformer, "h"):
        layer_list = target.transformer.h
    elif hasattr(target, "h") and isinstance(target.h, (nn.ModuleList, list)):
        layer_list = target.h
    elif hasattr(target, "model") and hasattr(target.model, "layers"):
        layer_list = target.model.layers
    elif hasattr(target, "encoder") and hasattr(target.encoder, "layer"):
        layer_list = target.encoder.layer
    elif (
        hasattr(target, "bert")
        and hasattr(target.bert, "encoder")
        and hasattr(target.bert.encoder, "layer")
    ):
        layer_list = target.bert.encoder.layer
    elif hasattr(target, "layers") and isinstance(target.layers, (nn.ModuleList, list)):
        layer_list = target.layers

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

    layer = target
    try:
        if model_config is None:
            if any(hasattr(layer, a) for a in ("hidden_size", "d_model", "n_embd", "dim")):
                model_config = layer
            elif hasattr(layer, "attention"):
                old_attn = layer.attention

        h_size = (
            getattr(
                model_config,
                "hidden_size",
                getattr(
                    model_config,
                    "d_model",
                    getattr(model_config, "n_embd", getattr(model_config, "dim", 768)),
                ),
            )
            if model_config is not None
            else 768
        )
        n_heads = (
            getattr(
                model_config,
                "num_attention_heads",
                getattr(
                    model_config,
                    "num_heads",
                    getattr(model_config, "n_heads", getattr(model_config, "n_head", 12)),
                ),
            )
            if model_config is not None
            else 12
        )
        h_dim = (
            getattr(
                model_config,
                "head_dim",
                getattr(model_config, "d_kv", h_size // max(1, n_heads)),
            )
            if model_config is not None
            else h_size // max(1, n_heads)
        )
        n_kv_heads = (
            getattr(
                model_config,
                "num_key_value_heads",
                getattr(model_config, "num_kv_heads", n_heads),
            )
            if model_config is not None
            else n_heads
        )

        src_attn = (
            old_attn
            if old_attn is not None
            else getattr(layer, "attention", getattr(layer, "self", getattr(layer, "attn", None)))
        )

        # V2-FIX-01: use shared helper
        is_dec = _detect_decoder(model_config, None, src_attn)

        cfg = AegisConfigV2(
            hidden_size=h_size,
            num_attention_heads=n_heads,
            num_heads=n_heads,
            num_key_value_heads=n_kv_heads,
            head_dim=h_dim,
            local_window=getattr(comp_config, "block_size", 64),
            rank=getattr(comp_config, "rank", getattr(comp_config, "lora_r", 192)),
            kernel_feature_dim=getattr(comp_config, "rff_features", 128),
            dropout=getattr(comp_config, "dropout", 0.1),
            use_adaptive_pruning=getattr(comp_config, "use_gate", True),
            gate_init_bias=getattr(comp_config, "gate_init_bias", 1.8),
            is_decoder=is_dec,
        )
        new_attn = AegisAttentionV2(config=cfg)
        if src_attn is not None:
            try:
                new_attn.copy_weights(src_attn)
            except Exception as e:
                logger.error("inject_aegis_v2: copy_weights failed: %s", e)
                return None
            p = next(src_attn.parameters(), None)
            if p is not None:
                new_attn.to(device=p.device, dtype=p.dtype)

        if hasattr(layer, "attention"):
            if hasattr(layer.attention, "self"):
                layer.attention.self = new_attn
            else:
                layer.attention = new_attn
        elif hasattr(layer, "self"):
            layer.self = new_attn
        elif hasattr(layer, "attn"):
            layer.attn = new_attn
        return new_attn
    except Exception as e:
        logger.exception("Failed to inject AEGIS-V2: %s", e)
        return None

Classes

class AegisAttentionConfigV2 (hidden_size: int = 768,
num_attention_heads: int = 12,
num_heads: int = 12,
head_dim: int = 64,
num_key_value_heads: int = 12,
local_window: int = 64,
rank: int = 384,
kernel_feature_dim: int = 128,
dropout: float = 0.1,
use_adaptive_pruning: bool = True,
gate_init_bias: float = 1.8,
is_decoder: bool = False)
Expand source code
@dataclass
class AegisAttentionConfigV2:
    hidden_size: int = 768
    num_attention_heads: int = 12
    num_heads: int = 12
    head_dim: int = 64
    num_key_value_heads: int = 12
    local_window: int = 64
    rank: int = 384
    kernel_feature_dim: int = 128
    dropout: float = 0.1
    use_adaptive_pruning: bool = True
    gate_init_bias: float = 1.8
    is_decoder: bool = False

AegisAttentionConfigV2(hidden_size: int = 768, num_attention_heads: int = 12, num_heads: int = 12, head_dim: int = 64, num_key_value_heads: int = 12, local_window: int = 64, rank: int = 384, kernel_feature_dim: int = 128, dropout: float = 0.1, use_adaptive_pruning: bool = True, gate_init_bias: float = 1.8, is_decoder: bool = False)

Instance variables

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 local_window : int

The type of the None singleton.

var num_attention_heads : int

The type of the None singleton.

var num_heads : int

The type of the None singleton.

var num_key_value_heads : int

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.

class AegisConfigV2 (hidden_size: int = 768,
num_attention_heads: int = 12,
num_heads: int = 12,
head_dim: int = 64,
num_key_value_heads: int = 12,
local_window: int = 64,
rank: int = 384,
kernel_feature_dim: int = 128,
dropout: float = 0.1,
use_adaptive_pruning: bool = True,
gate_init_bias: float = 1.8,
is_decoder: bool = False)
Expand source code
@dataclass
class AegisAttentionConfigV2:
    hidden_size: int = 768
    num_attention_heads: int = 12
    num_heads: int = 12
    head_dim: int = 64
    num_key_value_heads: int = 12
    local_window: int = 64
    rank: int = 384
    kernel_feature_dim: int = 128
    dropout: float = 0.1
    use_adaptive_pruning: bool = True
    gate_init_bias: float = 1.8
    is_decoder: bool = False

AegisAttentionConfigV2(hidden_size: int = 768, num_attention_heads: int = 12, num_heads: int = 12, head_dim: int = 64, num_key_value_heads: int = 12, local_window: int = 64, rank: int = 384, kernel_feature_dim: int = 128, dropout: float = 0.1, use_adaptive_pruning: bool = True, gate_init_bias: float = 1.8, is_decoder: bool = False)

Instance variables

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 local_window : int

The type of the None singleton.

var num_attention_heads : int

The type of the None singleton.

var num_heads : int

The type of the None singleton.

var num_key_value_heads : int

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.

class AegisAttentionV2 (config: Any = None,
pretrained_attn: torch.nn.modules.module.Module | None = None,
is_decoder: bool | None = None,
**kwargs: Any)
Expand source code
@ATTENTION_REGISTRY.register("aegis_v2", supports_causal=True, supports_gqa=True)
@ATTENTION_REGISTRY.register("aegis_v1", supports_causal=True, supports_gqa=True)
@ATTENTION_REGISTRY.register("aegis_v3", supports_causal=True, supports_gqa=True)
@ATTENTION_REGISTRY.register("aegis_v4", supports_causal=True, supports_gqa=True)
@ATTENTION_REGISTRY.register("aegis_v1_module", supports_causal=True, supports_gqa=True)
class AegisAttentionV2(BaseAttention):
    supports_causal: bool = True
    supports_gqa: bool = True
    is_custom_attention: bool = True

    def __init__(
        self,
        config: Any = None,
        pretrained_attn: nn.Module | None = None,
        is_decoder: bool | None = None,
        **kwargs: Any,
    ) -> None:
        if config is None or not isinstance(config, AegisConfigV2):
            h_size = (
                kwargs.get(
                    "hidden_size",
                    getattr(
                        config,
                        "hidden_size",
                        getattr(config, "d_model", getattr(config, "dim", 768)),
                    ),
                )
                if config is not None
                else 768
            )
            n_heads = (
                kwargs.get(
                    "num_heads",
                    getattr(config, "num_attention_heads", getattr(config, "num_heads", 12)),
                )
                if config is not None
                else 12
            )
            h_dim = (
                kwargs.get(
                    "head_dim",
                    getattr(
                        config,
                        "head_dim",
                        getattr(config, "d_kv", h_size // max(1, n_heads)),
                    ),
                )
                if config is not None
                else h_size // max(1, n_heads)
            )
            n_kv_heads = (
                kwargs.get(
                    "num_key_value_heads",
                    getattr(
                        config,
                        "num_key_value_heads",
                        getattr(config, "num_kv_heads", n_heads),
                    ),
                )
                if config is not None
                else n_heads
            )

            # V2-FIX-01: robust decoder detection
            dec = _detect_decoder(config, is_decoder, pretrained_attn)

            config = AegisConfigV2(
                hidden_size=h_size,
                num_attention_heads=n_heads,
                num_heads=n_heads,
                num_key_value_heads=n_kv_heads,
                head_dim=h_dim,
                local_window=(
                    kwargs.get(
                        "local_window",
                        getattr(config, "block_size", getattr(config, "local_window", 64)),
                    )
                    if config is not None
                    else 64
                ),
                rank=(
                    kwargs.pop("rank", getattr(config, "lora_r", getattr(config, "rank", 64)))
                    if config is not None
                    else 64
                ),
                kernel_feature_dim=(
                    getattr(
                        config,
                        "rff_features",
                        getattr(config, "kernel_feature_dim", 128),
                    )
                    if config is not None
                    else 128
                ),
                dropout=(
                    kwargs.get("dropout", getattr(config, "dropout", 0.1))
                    if config is not None
                    else 0.1
                ),
                use_adaptive_pruning=(
                    getattr(config, "use_gate", True) if config is not None else True
                ),
                gate_init_bias=(
                    getattr(config, "gate_init_bias", 1.8) if config is not None else 1.8
                ),
                is_decoder=dec,
            )

        super().__init__(config)
        self.config = config
        self.hidden_size: int = config.hidden_size
        self.num_heads: int = config.num_attention_heads
        self.head_dim: int = config.head_dim
        self.num_kv_heads: int = getattr(config, "num_key_value_heads", self.num_heads)
        if self.num_heads % max(1, self.num_kv_heads) != 0:
            raise ValueError(
                f"num_heads ({self.num_heads}) must be divisible by "
                f"num_kv_heads ({self.num_kv_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 = config.local_window
        self.rank: int = config.rank
        self.kernel_dim: int = config.kernel_feature_dim
        self.dropout = nn.Dropout(config.dropout)
        self.is_decoder: bool = getattr(config, "is_decoder", False)
        self.is_nested_encoder: bool = False

        self.q_proj = FactorisedLinear(self.hidden_size, self.all_head_size, rank=self.rank)
        self.k_proj = FactorisedLinear(self.hidden_size, self.all_kv_head_size, rank=self.rank)
        self.v_proj = FactorisedLinear(self.hidden_size, self.all_kv_head_size, rank=self.rank)
        self.out_proj = FactorisedLinear(self.all_head_size, self.hidden_size, rank=self.rank)

        if config.use_adaptive_pruning:
            self.prune_gate: nn.Sequential | None = nn.Sequential(
                nn.Linear(self.hidden_size, max(1, self.hidden_size // 4)),
                nn.ReLU(),
                nn.Linear(max(1, self.hidden_size // 4), self.num_heads),
            )
        else:
            self.prune_gate = None

        self.fusion_gate = nn.Linear(self.hidden_size, self.num_heads)
        self.fusion_gate.bias.data.fill_(config.gate_init_bias)

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

    # ------------------------------------------------------------------
    # Aliases
    # ------------------------------------------------------------------
    @property
    def q_proj1(self):
        return self.q_proj.down

    @property
    def q_proj2(self):
        return self.q_proj.up

    @property
    def k_proj1(self):
        return self.k_proj.down

    @property
    def k_proj2(self):
        return self.k_proj.up

    @property
    def v_proj1(self):
        return self.v_proj.down

    @property
    def v_proj2(self):
        return self.v_proj.up

    @property
    def out_proj1(self):
        return self.out_proj.down

    @property
    def out_proj2(self):
        return self.out_proj.up

    @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 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 dense(self):
        return self.out_proj

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

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

    # ------------------------------------------------------------------
    # Weight transfer (same logic as V5)
    # ------------------------------------------------------------------
    def _copy_pretrained(self, old_attn: nn.Module) -> None:
        self.copy_weights(old_attn)

    def copy_weights(self, old_attn: Any) -> None:
        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),
        }

        for proj in ["q", "k", "v", "o"]:
            try:
                w, b = UniversalAdapter.get_weight_bias(old_attn, proj)
                if w is not None:
                    extracted[proj] = (w, b)
            except Exception as e:
                logger.debug("UniversalAdapter fallback failed: %s", e)

        # GPT-2 fused c_attn (Conv1D)
        if hasattr(old_attn, "c_attn") and any(extracted[k][0] is None for k in ("q", "k", "v")):
            c_attn_mod = old_attn.c_attn
            base_c = getattr(c_attn_mod, "base_layer", c_attn_mod)
            weight = getattr(base_c, "weight", getattr(c_attn_mod, "weight", None))
            if weight is not None:
                weight = weight.data.clone()
                is_conv1d = (
                    (c_attn_mod.__class__.__name__ == "Conv1D")
                    or (base_c.__class__.__name__ == "Conv1D")
                    or hasattr(c_attn_mod, "nf")
                    or hasattr(base_c, "nf")
                )
                if is_conv1d or (weight.dim() == 2 and weight.shape[1] == 3 * self.hidden_size):
                    weight = weight.t().contiguous()
                bias = getattr(base_c, "bias", getattr(c_attn_mod, "bias", None))
                bd = bias.data if bias is not None else None
                split = weight.shape[0] // 3
                extracted["q"] = (
                    weight[:split, :],
                    bd[:split] if bd is not None else None,
                )
                extracted["k"] = (
                    weight[split : 2 * split, :],
                    bd[split : 2 * split] if bd is not None else None,
                )
                extracted["v"] = (
                    weight[2 * split :, :],
                    bd[2 * split :] if bd is not None else None,
                )

        if hasattr(old_attn, "c_proj") and extracted["o"][0] is None:
            c_proj_mod = old_attn.c_proj
            base_p = getattr(c_proj_mod, "base_layer", c_proj_mod)
            w_o = getattr(base_p, "weight", getattr(c_proj_mod, "weight", None))
            if w_o is not None:
                w_o = w_o.data.clone()
                is_conv1d = (
                    (c_proj_mod.__class__.__name__ == "Conv1D")
                    or (base_p.__class__.__name__ == "Conv1D")
                    or hasattr(c_proj_mod, "nf")
                    or hasattr(base_p, "nf")
                )
                if is_conv1d or (
                    w_o.dim() == 2
                    and w_o.shape[0] != self.hidden_size
                    and w_o.shape[1] == self.hidden_size
                ):
                    w_o = w_o.t().contiguous()
                b_o = getattr(base_p, "bias", getattr(c_proj_mod, "bias", None))
                extracted["o"] = (w_o, b_o.data if b_o is not None else None)

        # BERT / RoBERTa nested encoder
        if hasattr(old_attn, "self") and any(extracted[k][0] is None for k in ("q", "k", "v")):
            s = old_attn.self
            if hasattr(s, "query"):
                extracted["q"] = (s.query.weight.data, getattr(s.query, "bias", None))
            if hasattr(s, "key"):
                extracted["k"] = (s.key.weight.data, getattr(s.key, "bias", None))
            if hasattr(s, "value"):
                extracted["v"] = (s.value.weight.data, getattr(s.value, "bias", None))

        if (
            hasattr(old_attn, "output")
            and hasattr(old_attn.output, "dense")
            and extracted["o"][0] is None
        ):
            dense = old_attn.output.dense
            extracted["o"] = (dense.weight.data, getattr(dense, "bias", None))

        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 proj, names in named_map.items():
            if extracted[proj][0] is None:
                for name in names:
                    if hasattr(old_attn, name):
                        layer = getattr(old_attn, name)
                        if hasattr(layer, "weight"):
                            extracted[proj] = (
                                layer.weight.data,
                                getattr(layer, "bias", None),
                            )
                            break

        cls_name = old_attn.__class__.__name__
        has_out_proj = any(
            hasattr(old_attn, a)
            for a in (
                "dense",
                "out_proj",
                "c_proj",
                "out_lin",
                "o_proj",
                "wo",
                "o",
                "proj",
            )
        )
        if (
            hasattr(old_attn, "out_lin")
            or hasattr(old_attn, "c_proj")
            or hasattr(old_attn, "o_proj")
        ):
            self.is_nested_encoder = False
        elif (
            hasattr(old_attn, "self")
            and hasattr(old_attn.self, "query")
            and hasattr(old_attn, "output")
            and hasattr(old_attn.output, "dense")
        ) or (
            cls_name
            in (
                "BertSelfAttention",
                "RobertaSelfAttention",
                "XLMRobertaSelfAttention",
                "ElectraSelfAttention",
            )
            or "RobertaSelfAttention" in str(type(old_attn))
            or "BertSelfAttention" in str(type(old_attn))
            or (cls_name.endswith("SelfAttention") and not has_out_proj)
        ):
            self.is_nested_encoder = True
        else:
            self.is_nested_encoder = False

        missing = [k for k in ("q", "k", "v") if extracted[k][0] is None]
        if not self.is_nested_encoder and extracted["o"][0] is None:
            missing.append("o")
        if missing:
            raise RuntimeError(
                f"AegisAttentionV2.copy_weights: missing {missing} from "
                f"{old_attn.__class__.__name__}. Refusing random init."
            )

        for proj_key in ("q", "k", "v"):
            w = extracted[proj_key][0]
            if w is not None:
                norm = float(torch.linalg.norm(w.float()).item())
                if norm < 1e-4:
                    logger.warning(
                        "AegisAttentionV2: weight for '%s' has near-zero norm (%.2e).",
                        proj_key,
                        norm,
                    )

        def _orient(w: torch.Tensor, in_dim: int) -> torch.Tensor:
            if w.dim() == 2 and w.shape[1] != in_dim and w.shape[0] == in_dim:
                return w.t().contiguous()
            return w

        with torch.no_grad():
            for proj_key, target in (
                ("q", self.q_proj),
                ("k", self.k_proj),
                ("v", self.v_proj),
                ("o", self.out_proj),
            ):
                w, b = extracted[proj_key]
                if w is not None:
                    in_dim = self.all_head_size if proj_key == "o" else self.hidden_size
                    target.decompose_from_weight(_orient(w, in_dim), b)
                elif proj_key == "o" and self.is_nested_encoder:
                    eye_w = torch.eye(
                        self.all_head_size,
                        self.all_head_size,
                        device=self.out_proj.up.weight.device,
                        dtype=self.out_proj.up.weight.dtype,
                    )
                    target.decompose_from_weight(eye_w, None)

        if hasattr(old_attn, "is_decoder"):
            self.is_decoder = bool(old_attn.is_decoder)
        elif hasattr(old_attn, "c_attn") or "gpt" in old_attn.__class__.__name__.lower():
            self.is_decoder = True

    # ------------------------------------------------------------------
    # V2-FIX-07: safe from_distilbert_pretrained
    # ------------------------------------------------------------------
    @classmethod
    def from_distilbert_pretrained(
        cls, old_attn: nn.Module, model_config: Any | None = None, **kwargs: Any
    ) -> "AegisAttentionV2":
        if model_config is None:
            out_mod = getattr(old_attn, "out_lin", None) or getattr(old_attn, "dense", None)
            if out_mod is None or not hasattr(out_mod, "out_features"):
                raise ValueError(
                    "from_distilbert_pretrained: could not infer hidden_size "
                    "from out_lin or dense. Pass model_config explicitly."
                )

            class DummyConfig:
                pass

            model_config = DummyConfig()
            model_config.hidden_size = out_mod.out_features
            n_heads = getattr(
                old_attn,
                "n_heads",
                getattr(
                    old_attn,
                    "num_attention_heads",
                    getattr(old_attn, "num_heads", None),
                ),
            )
            if n_heads is None:
                n_heads = max(1, model_config.hidden_size // 64)
            model_config.num_attention_heads = n_heads
            model_config.num_key_value_heads = n_heads
            model_config.head_dim = model_config.hidden_size // n_heads
        return cls(config=model_config, pretrained_attn=old_attn, **kwargs)

    # ------------------------------------------------------------------
    # V2-FIX-05: unified mask handling
    # ------------------------------------------------------------------
    @staticmethod
    def _prepare_valid_mask(
        attention_mask: torch.Tensor | None,
        B: int,
        N_kv: int,
        device: torch.device,
    ) -> torch.Tensor | None:
        """Return boolean (B, N_kv): True = key is valid. None if no mask."""
        if attention_mask is None:
            return None
        if attention_mask.dim() == 2:
            if attention_mask.dtype == torch.bool:
                return attention_mask
            if attention_mask.dtype.is_floating_point:
                return attention_mask > -1e4
            return attention_mask > 0
        if attention_mask.dim() == 4:
            if attention_mask.dtype == torch.bool:
                v = attention_mask
            elif attention_mask.dtype.is_floating_point:
                v = attention_mask > -1e4
            else:
                v = attention_mask > 0
            # (B, 1, N_q, N_kv) -> OR over query dim -> (B, N_kv)
            return v.any(dim=-2).squeeze(1)
        raise ValueError(f"Unsupported attention_mask dim: {attention_mask.dim()}")

    # ------------------------------------------------------------------
    # V2-FIX-02 + V2-FIX-09: 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,
        *args: Any,
        **kwargs: Any,
    ) -> tuple[torch.Tensor, torch.Tensor | None]:
        B, N, _ = hidden_states.shape
        H, D = self.num_heads, self.head_dim
        Hkv = self.num_kv_heads
        device = hidden_states.device

        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]

        # ---- Mask (V2-FIX-05) ----
        eff_mask = encoder_attention_mask if is_cross else attention_mask
        token_valid = self._prepare_valid_mask(eff_mask, B, N_kv_new, device)

        # ---- Projections ----
        Q = self.q_proj(hidden_states).view(B, N, H, D).transpose(1, 2).contiguous()
        K_new = self.k_proj(kv_input).view(B, N_kv_new, Hkv, D).transpose(1, 2).contiguous()
        V_new = self.v_proj(kv_input).view(B, N_kv_new, Hkv, D).transpose(1, 2).contiguous()

        # ---- RoPE with past offset ----
        if position_embeddings is not None and not is_cross:
            cos, sin = position_embeddings
            past_len_rope = 0
            if past_key_value is not None:
                if isinstance(past_key_value, (tuple, list)) and len(past_key_value) >= 1:
                    past_len_rope = past_key_value[0].shape[-2]
                elif hasattr(past_key_value, "key_cache") and past_key_value.key_cache:
                    past_len_rope = past_key_value.key_cache[0].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 concat ----
        past_k = past_v = None
        if past_key_value is not None and not is_cross:
            if isinstance(past_key_value, (tuple, list)) and len(past_key_value) >= 2:
                past_k, past_v = past_key_value[0], past_key_value[1]
            elif hasattr(past_key_value, "key_cache") and past_key_value.key_cache:
                idx = min(getattr(self, "layer_idx", 0), len(past_key_value.key_cache) - 1)
                past_k, past_v = (
                    past_key_value.key_cache[idx],
                    past_key_value.value_cache[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)
        else:
            K_cat = K_new
            V_cat = V_new

        if past_key_value is not None and hasattr(past_key_value, "update") and not is_cross:
            idx = getattr(self, "layer_idx", 0)
            past_key_value.update(K_new, V_new, idx)
        else:
            pass

        N_kv = K_cat.shape[2]
        past_len = N_kv - N if (self.is_decoder and not is_cross) else 0

        # ---- GQA expansion ----
        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

        # ---- Local attention (V2-FIX-03) ----
        local_out = _local_window_attention_v2(
            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,
        )  # (B, H, N, D)

        # ---- Global attention (V2-FIX-08) ----
        if not is_cross:
            if self.prune_gate is not None:
                keep = torch.sigmoid(self.prune_gate(hidden_states))  # (B, N, H)
                # V2-FIX-06: expand across GQA heads
                if self.kv_repeat > 1:
                    keep_kv = keep[:, :, : self.num_kv_heads]
                else:
                    keep_kv = keep
                Kg = K_cat * keep_kv.transpose(1, 2).unsqueeze(-1)
                Vg = V_cat * keep_kv.transpose(1, 2).unsqueeze(-1)
            else:
                Kg, Vg = K_cat, V_cat

            if self.kv_repeat > 1:
                Kg = repeat_kv(Kg, self.kv_repeat)
                Vg = repeat_kv(Vg, self.kv_repeat)

            global_out = _global_linear_attention_v2(
                Q,
                Kg,
                Vg,
                token_valid=token_valid,
                is_decoder=self.is_decoder,
            )  # (B, H, N, D)

            alpha = torch.sigmoid(self.fusion_gate(hidden_states)).unsqueeze(-1)  # (B, N, H, 1)
            alpha = alpha.transpose(1, 2)  # (B, H, N, 1)
            fused = alpha * local_out + (1.0 - alpha) * global_out
        else:
            # Cross-attention: local only (global over encoder is questionable)
            fused = local_out

        # ---- head_mask (V2-FIX-02) ----
        if head_mask is not None:
            if head_mask.dim() == 1:
                head_mask = head_mask.view(1, -1, 1, 1)
            elif head_mask.dim() == 2:
                head_mask = head_mask.unsqueeze(-1).unsqueeze(-1)  # (B, H, 1, 1)
            elif head_mask.dim() == 3:
                head_mask = head_mask[:, :, :1].unsqueeze(-1)  # (B, H, 1, 1)
            fused = fused * head_mask.to(fused.dtype)

        # ---- Reshape to (B, N, H*D) ----
        out = fused.transpose(1, 2).contiguous().view(B, N, self.all_head_size)
        out = torch.nan_to_num(out, nan=0.0, posinf=1e4, neginf=-1e4)

        if self.is_nested_encoder:
            output = out
        else:
            output = self.out_proj(out)
            output = self.dropout(output)
        output = torch.nan_to_num(output, nan=0.0, posinf=1e4, neginf=-1e4)
        return output.to(hidden_states.dtype), None

    def get_ortho_loss(self) -> torch.Tensor:
        return torch.zeros((), device=self.out_proj.up.weight.device)

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.

Static methods

def from_distilbert_pretrained(old_attn: torch.nn.modules.module.Module,
model_config: Any | None = None,
**kwargs: Any) ‑> AegisAttentionV2

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 k_proj1
Expand source code
@property
def k_proj1(self):
    return self.k_proj.down
prop k_proj2
Expand source code
@property
def k_proj2(self):
    return self.k_proj.up
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 out_proj1
Expand source code
@property
def out_proj1(self):
    return self.out_proj.down
prop out_proj2
Expand source code
@property
def out_proj2(self):
    return self.out_proj.up
prop q_lin
Expand source code
@property
def q_lin(self):
    return self.q_proj
prop q_proj1
Expand source code
@property
def q_proj1(self):
    return self.q_proj.down
prop q_proj2
Expand source code
@property
def q_proj2(self):
    return self.q_proj.up
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 v_proj1
Expand source code
@property
def v_proj1(self):
    return self.v_proj.down
prop v_proj2
Expand source code
@property
def v_proj2(self):
    return self.v_proj.up
prop value
Expand source code
@property
def value(self):
    return self.v_proj

Methods

def copy_weights(self, old_attn: Any) ‑> None
Expand source code
def copy_weights(self, old_attn: Any) -> None:
    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),
    }

    for proj in ["q", "k", "v", "o"]:
        try:
            w, b = UniversalAdapter.get_weight_bias(old_attn, proj)
            if w is not None:
                extracted[proj] = (w, b)
        except Exception as e:
            logger.debug("UniversalAdapter fallback failed: %s", e)

    # GPT-2 fused c_attn (Conv1D)
    if hasattr(old_attn, "c_attn") and any(extracted[k][0] is None for k in ("q", "k", "v")):
        c_attn_mod = old_attn.c_attn
        base_c = getattr(c_attn_mod, "base_layer", c_attn_mod)
        weight = getattr(base_c, "weight", getattr(c_attn_mod, "weight", None))
        if weight is not None:
            weight = weight.data.clone()
            is_conv1d = (
                (c_attn_mod.__class__.__name__ == "Conv1D")
                or (base_c.__class__.__name__ == "Conv1D")
                or hasattr(c_attn_mod, "nf")
                or hasattr(base_c, "nf")
            )
            if is_conv1d or (weight.dim() == 2 and weight.shape[1] == 3 * self.hidden_size):
                weight = weight.t().contiguous()
            bias = getattr(base_c, "bias", getattr(c_attn_mod, "bias", None))
            bd = bias.data if bias is not None else None
            split = weight.shape[0] // 3
            extracted["q"] = (
                weight[:split, :],
                bd[:split] if bd is not None else None,
            )
            extracted["k"] = (
                weight[split : 2 * split, :],
                bd[split : 2 * split] if bd is not None else None,
            )
            extracted["v"] = (
                weight[2 * split :, :],
                bd[2 * split :] if bd is not None else None,
            )

    if hasattr(old_attn, "c_proj") and extracted["o"][0] is None:
        c_proj_mod = old_attn.c_proj
        base_p = getattr(c_proj_mod, "base_layer", c_proj_mod)
        w_o = getattr(base_p, "weight", getattr(c_proj_mod, "weight", None))
        if w_o is not None:
            w_o = w_o.data.clone()
            is_conv1d = (
                (c_proj_mod.__class__.__name__ == "Conv1D")
                or (base_p.__class__.__name__ == "Conv1D")
                or hasattr(c_proj_mod, "nf")
                or hasattr(base_p, "nf")
            )
            if is_conv1d or (
                w_o.dim() == 2
                and w_o.shape[0] != self.hidden_size
                and w_o.shape[1] == self.hidden_size
            ):
                w_o = w_o.t().contiguous()
            b_o = getattr(base_p, "bias", getattr(c_proj_mod, "bias", None))
            extracted["o"] = (w_o, b_o.data if b_o is not None else None)

    # BERT / RoBERTa nested encoder
    if hasattr(old_attn, "self") and any(extracted[k][0] is None for k in ("q", "k", "v")):
        s = old_attn.self
        if hasattr(s, "query"):
            extracted["q"] = (s.query.weight.data, getattr(s.query, "bias", None))
        if hasattr(s, "key"):
            extracted["k"] = (s.key.weight.data, getattr(s.key, "bias", None))
        if hasattr(s, "value"):
            extracted["v"] = (s.value.weight.data, getattr(s.value, "bias", None))

    if (
        hasattr(old_attn, "output")
        and hasattr(old_attn.output, "dense")
        and extracted["o"][0] is None
    ):
        dense = old_attn.output.dense
        extracted["o"] = (dense.weight.data, getattr(dense, "bias", None))

    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 proj, names in named_map.items():
        if extracted[proj][0] is None:
            for name in names:
                if hasattr(old_attn, name):
                    layer = getattr(old_attn, name)
                    if hasattr(layer, "weight"):
                        extracted[proj] = (
                            layer.weight.data,
                            getattr(layer, "bias", None),
                        )
                        break

    cls_name = old_attn.__class__.__name__
    has_out_proj = any(
        hasattr(old_attn, a)
        for a in (
            "dense",
            "out_proj",
            "c_proj",
            "out_lin",
            "o_proj",
            "wo",
            "o",
            "proj",
        )
    )
    if (
        hasattr(old_attn, "out_lin")
        or hasattr(old_attn, "c_proj")
        or hasattr(old_attn, "o_proj")
    ):
        self.is_nested_encoder = False
    elif (
        hasattr(old_attn, "self")
        and hasattr(old_attn.self, "query")
        and hasattr(old_attn, "output")
        and hasattr(old_attn.output, "dense")
    ) or (
        cls_name
        in (
            "BertSelfAttention",
            "RobertaSelfAttention",
            "XLMRobertaSelfAttention",
            "ElectraSelfAttention",
        )
        or "RobertaSelfAttention" in str(type(old_attn))
        or "BertSelfAttention" in str(type(old_attn))
        or (cls_name.endswith("SelfAttention") and not has_out_proj)
    ):
        self.is_nested_encoder = True
    else:
        self.is_nested_encoder = False

    missing = [k for k in ("q", "k", "v") if extracted[k][0] is None]
    if not self.is_nested_encoder and extracted["o"][0] is None:
        missing.append("o")
    if missing:
        raise RuntimeError(
            f"AegisAttentionV2.copy_weights: missing {missing} from "
            f"{old_attn.__class__.__name__}. Refusing random init."
        )

    for proj_key in ("q", "k", "v"):
        w = extracted[proj_key][0]
        if w is not None:
            norm = float(torch.linalg.norm(w.float()).item())
            if norm < 1e-4:
                logger.warning(
                    "AegisAttentionV2: weight for '%s' has near-zero norm (%.2e).",
                    proj_key,
                    norm,
                )

    def _orient(w: torch.Tensor, in_dim: int) -> torch.Tensor:
        if w.dim() == 2 and w.shape[1] != in_dim and w.shape[0] == in_dim:
            return w.t().contiguous()
        return w

    with torch.no_grad():
        for proj_key, target in (
            ("q", self.q_proj),
            ("k", self.k_proj),
            ("v", self.v_proj),
            ("o", self.out_proj),
        ):
            w, b = extracted[proj_key]
            if w is not None:
                in_dim = self.all_head_size if proj_key == "o" else self.hidden_size
                target.decompose_from_weight(_orient(w, in_dim), b)
            elif proj_key == "o" and self.is_nested_encoder:
                eye_w = torch.eye(
                    self.all_head_size,
                    self.all_head_size,
                    device=self.out_proj.up.weight.device,
                    dtype=self.out_proj.up.weight.dtype,
                )
                target.decompose_from_weight(eye_w, None)

    if hasattr(old_attn, "is_decoder"):
        self.is_decoder = bool(old_attn.is_decoder)
    elif hasattr(old_attn, "c_attn") or "gpt" in old_attn.__class__.__name__.lower():
        self.is_decoder = True

Universal fallback: copy pretrained weights from old attention layer.

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,
*args: Any,
**kwargs: Any) ‑> tuple[torch.Tensor, torch.Tensor | None]
Expand source code
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,
    *args: Any,
    **kwargs: Any,
) -> tuple[torch.Tensor, torch.Tensor | None]:
    B, N, _ = hidden_states.shape
    H, D = self.num_heads, self.head_dim
    Hkv = self.num_kv_heads
    device = hidden_states.device

    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]

    # ---- Mask (V2-FIX-05) ----
    eff_mask = encoder_attention_mask if is_cross else attention_mask
    token_valid = self._prepare_valid_mask(eff_mask, B, N_kv_new, device)

    # ---- Projections ----
    Q = self.q_proj(hidden_states).view(B, N, H, D).transpose(1, 2).contiguous()
    K_new = self.k_proj(kv_input).view(B, N_kv_new, Hkv, D).transpose(1, 2).contiguous()
    V_new = self.v_proj(kv_input).view(B, N_kv_new, Hkv, D).transpose(1, 2).contiguous()

    # ---- RoPE with past offset ----
    if position_embeddings is not None and not is_cross:
        cos, sin = position_embeddings
        past_len_rope = 0
        if past_key_value is not None:
            if isinstance(past_key_value, (tuple, list)) and len(past_key_value) >= 1:
                past_len_rope = past_key_value[0].shape[-2]
            elif hasattr(past_key_value, "key_cache") and past_key_value.key_cache:
                past_len_rope = past_key_value.key_cache[0].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 concat ----
    past_k = past_v = None
    if past_key_value is not None and not is_cross:
        if isinstance(past_key_value, (tuple, list)) and len(past_key_value) >= 2:
            past_k, past_v = past_key_value[0], past_key_value[1]
        elif hasattr(past_key_value, "key_cache") and past_key_value.key_cache:
            idx = min(getattr(self, "layer_idx", 0), len(past_key_value.key_cache) - 1)
            past_k, past_v = (
                past_key_value.key_cache[idx],
                past_key_value.value_cache[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)
    else:
        K_cat = K_new
        V_cat = V_new

    if past_key_value is not None and hasattr(past_key_value, "update") and not is_cross:
        idx = getattr(self, "layer_idx", 0)
        past_key_value.update(K_new, V_new, idx)
    else:
        pass

    N_kv = K_cat.shape[2]
    past_len = N_kv - N if (self.is_decoder and not is_cross) else 0

    # ---- GQA expansion ----
    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

    # ---- Local attention (V2-FIX-03) ----
    local_out = _local_window_attention_v2(
        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,
    )  # (B, H, N, D)

    # ---- Global attention (V2-FIX-08) ----
    if not is_cross:
        if self.prune_gate is not None:
            keep = torch.sigmoid(self.prune_gate(hidden_states))  # (B, N, H)
            # V2-FIX-06: expand across GQA heads
            if self.kv_repeat > 1:
                keep_kv = keep[:, :, : self.num_kv_heads]
            else:
                keep_kv = keep
            Kg = K_cat * keep_kv.transpose(1, 2).unsqueeze(-1)
            Vg = V_cat * keep_kv.transpose(1, 2).unsqueeze(-1)
        else:
            Kg, Vg = K_cat, V_cat

        if self.kv_repeat > 1:
            Kg = repeat_kv(Kg, self.kv_repeat)
            Vg = repeat_kv(Vg, self.kv_repeat)

        global_out = _global_linear_attention_v2(
            Q,
            Kg,
            Vg,
            token_valid=token_valid,
            is_decoder=self.is_decoder,
        )  # (B, H, N, D)

        alpha = torch.sigmoid(self.fusion_gate(hidden_states)).unsqueeze(-1)  # (B, N, H, 1)
        alpha = alpha.transpose(1, 2)  # (B, H, N, 1)
        fused = alpha * local_out + (1.0 - alpha) * global_out
    else:
        # Cross-attention: local only (global over encoder is questionable)
        fused = local_out

    # ---- head_mask (V2-FIX-02) ----
    if head_mask is not None:
        if head_mask.dim() == 1:
            head_mask = head_mask.view(1, -1, 1, 1)
        elif head_mask.dim() == 2:
            head_mask = head_mask.unsqueeze(-1).unsqueeze(-1)  # (B, H, 1, 1)
        elif head_mask.dim() == 3:
            head_mask = head_mask[:, :, :1].unsqueeze(-1)  # (B, H, 1, 1)
        fused = fused * head_mask.to(fused.dtype)

    # ---- Reshape to (B, N, H*D) ----
    out = fused.transpose(1, 2).contiguous().view(B, N, self.all_head_size)
    out = torch.nan_to_num(out, nan=0.0, posinf=1e4, neginf=-1e4)

    if self.is_nested_encoder:
        output = out
    else:
        output = self.out_proj(out)
        output = self.dropout(output)
    output = torch.nan_to_num(output, nan=0.0, posinf=1e4, neginf=-1e4)
    return output.to(hidden_states.dtype), None

Must return (output, attention_weights_or_None).

def get_ortho_loss(self) ‑> torch.Tensor
Expand source code
def get_ortho_loss(self) -> torch.Tensor:
    return torch.zeros((), device=self.out_proj.up.weight.device)
class AegisAttentionV1 (config: Any = None,
pretrained_attn: torch.nn.modules.module.Module | None = None,
is_decoder: bool | None = None,
**kwargs: Any)
Expand source code
@ATTENTION_REGISTRY.register("aegis_v2", supports_causal=True, supports_gqa=True)
@ATTENTION_REGISTRY.register("aegis_v1", supports_causal=True, supports_gqa=True)
@ATTENTION_REGISTRY.register("aegis_v3", supports_causal=True, supports_gqa=True)
@ATTENTION_REGISTRY.register("aegis_v4", supports_causal=True, supports_gqa=True)
@ATTENTION_REGISTRY.register("aegis_v1_module", supports_causal=True, supports_gqa=True)
class AegisAttentionV2(BaseAttention):
    supports_causal: bool = True
    supports_gqa: bool = True
    is_custom_attention: bool = True

    def __init__(
        self,
        config: Any = None,
        pretrained_attn: nn.Module | None = None,
        is_decoder: bool | None = None,
        **kwargs: Any,
    ) -> None:
        if config is None or not isinstance(config, AegisConfigV2):
            h_size = (
                kwargs.get(
                    "hidden_size",
                    getattr(
                        config,
                        "hidden_size",
                        getattr(config, "d_model", getattr(config, "dim", 768)),
                    ),
                )
                if config is not None
                else 768
            )
            n_heads = (
                kwargs.get(
                    "num_heads",
                    getattr(config, "num_attention_heads", getattr(config, "num_heads", 12)),
                )
                if config is not None
                else 12
            )
            h_dim = (
                kwargs.get(
                    "head_dim",
                    getattr(
                        config,
                        "head_dim",
                        getattr(config, "d_kv", h_size // max(1, n_heads)),
                    ),
                )
                if config is not None
                else h_size // max(1, n_heads)
            )
            n_kv_heads = (
                kwargs.get(
                    "num_key_value_heads",
                    getattr(
                        config,
                        "num_key_value_heads",
                        getattr(config, "num_kv_heads", n_heads),
                    ),
                )
                if config is not None
                else n_heads
            )

            # V2-FIX-01: robust decoder detection
            dec = _detect_decoder(config, is_decoder, pretrained_attn)

            config = AegisConfigV2(
                hidden_size=h_size,
                num_attention_heads=n_heads,
                num_heads=n_heads,
                num_key_value_heads=n_kv_heads,
                head_dim=h_dim,
                local_window=(
                    kwargs.get(
                        "local_window",
                        getattr(config, "block_size", getattr(config, "local_window", 64)),
                    )
                    if config is not None
                    else 64
                ),
                rank=(
                    kwargs.pop("rank", getattr(config, "lora_r", getattr(config, "rank", 64)))
                    if config is not None
                    else 64
                ),
                kernel_feature_dim=(
                    getattr(
                        config,
                        "rff_features",
                        getattr(config, "kernel_feature_dim", 128),
                    )
                    if config is not None
                    else 128
                ),
                dropout=(
                    kwargs.get("dropout", getattr(config, "dropout", 0.1))
                    if config is not None
                    else 0.1
                ),
                use_adaptive_pruning=(
                    getattr(config, "use_gate", True) if config is not None else True
                ),
                gate_init_bias=(
                    getattr(config, "gate_init_bias", 1.8) if config is not None else 1.8
                ),
                is_decoder=dec,
            )

        super().__init__(config)
        self.config = config
        self.hidden_size: int = config.hidden_size
        self.num_heads: int = config.num_attention_heads
        self.head_dim: int = config.head_dim
        self.num_kv_heads: int = getattr(config, "num_key_value_heads", self.num_heads)
        if self.num_heads % max(1, self.num_kv_heads) != 0:
            raise ValueError(
                f"num_heads ({self.num_heads}) must be divisible by "
                f"num_kv_heads ({self.num_kv_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 = config.local_window
        self.rank: int = config.rank
        self.kernel_dim: int = config.kernel_feature_dim
        self.dropout = nn.Dropout(config.dropout)
        self.is_decoder: bool = getattr(config, "is_decoder", False)
        self.is_nested_encoder: bool = False

        self.q_proj = FactorisedLinear(self.hidden_size, self.all_head_size, rank=self.rank)
        self.k_proj = FactorisedLinear(self.hidden_size, self.all_kv_head_size, rank=self.rank)
        self.v_proj = FactorisedLinear(self.hidden_size, self.all_kv_head_size, rank=self.rank)
        self.out_proj = FactorisedLinear(self.all_head_size, self.hidden_size, rank=self.rank)

        if config.use_adaptive_pruning:
            self.prune_gate: nn.Sequential | None = nn.Sequential(
                nn.Linear(self.hidden_size, max(1, self.hidden_size // 4)),
                nn.ReLU(),
                nn.Linear(max(1, self.hidden_size // 4), self.num_heads),
            )
        else:
            self.prune_gate = None

        self.fusion_gate = nn.Linear(self.hidden_size, self.num_heads)
        self.fusion_gate.bias.data.fill_(config.gate_init_bias)

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

    # ------------------------------------------------------------------
    # Aliases
    # ------------------------------------------------------------------
    @property
    def q_proj1(self):
        return self.q_proj.down

    @property
    def q_proj2(self):
        return self.q_proj.up

    @property
    def k_proj1(self):
        return self.k_proj.down

    @property
    def k_proj2(self):
        return self.k_proj.up

    @property
    def v_proj1(self):
        return self.v_proj.down

    @property
    def v_proj2(self):
        return self.v_proj.up

    @property
    def out_proj1(self):
        return self.out_proj.down

    @property
    def out_proj2(self):
        return self.out_proj.up

    @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 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 dense(self):
        return self.out_proj

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

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

    # ------------------------------------------------------------------
    # Weight transfer (same logic as V5)
    # ------------------------------------------------------------------
    def _copy_pretrained(self, old_attn: nn.Module) -> None:
        self.copy_weights(old_attn)

    def copy_weights(self, old_attn: Any) -> None:
        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),
        }

        for proj in ["q", "k", "v", "o"]:
            try:
                w, b = UniversalAdapter.get_weight_bias(old_attn, proj)
                if w is not None:
                    extracted[proj] = (w, b)
            except Exception as e:
                logger.debug("UniversalAdapter fallback failed: %s", e)

        # GPT-2 fused c_attn (Conv1D)
        if hasattr(old_attn, "c_attn") and any(extracted[k][0] is None for k in ("q", "k", "v")):
            c_attn_mod = old_attn.c_attn
            base_c = getattr(c_attn_mod, "base_layer", c_attn_mod)
            weight = getattr(base_c, "weight", getattr(c_attn_mod, "weight", None))
            if weight is not None:
                weight = weight.data.clone()
                is_conv1d = (
                    (c_attn_mod.__class__.__name__ == "Conv1D")
                    or (base_c.__class__.__name__ == "Conv1D")
                    or hasattr(c_attn_mod, "nf")
                    or hasattr(base_c, "nf")
                )
                if is_conv1d or (weight.dim() == 2 and weight.shape[1] == 3 * self.hidden_size):
                    weight = weight.t().contiguous()
                bias = getattr(base_c, "bias", getattr(c_attn_mod, "bias", None))
                bd = bias.data if bias is not None else None
                split = weight.shape[0] // 3
                extracted["q"] = (
                    weight[:split, :],
                    bd[:split] if bd is not None else None,
                )
                extracted["k"] = (
                    weight[split : 2 * split, :],
                    bd[split : 2 * split] if bd is not None else None,
                )
                extracted["v"] = (
                    weight[2 * split :, :],
                    bd[2 * split :] if bd is not None else None,
                )

        if hasattr(old_attn, "c_proj") and extracted["o"][0] is None:
            c_proj_mod = old_attn.c_proj
            base_p = getattr(c_proj_mod, "base_layer", c_proj_mod)
            w_o = getattr(base_p, "weight", getattr(c_proj_mod, "weight", None))
            if w_o is not None:
                w_o = w_o.data.clone()
                is_conv1d = (
                    (c_proj_mod.__class__.__name__ == "Conv1D")
                    or (base_p.__class__.__name__ == "Conv1D")
                    or hasattr(c_proj_mod, "nf")
                    or hasattr(base_p, "nf")
                )
                if is_conv1d or (
                    w_o.dim() == 2
                    and w_o.shape[0] != self.hidden_size
                    and w_o.shape[1] == self.hidden_size
                ):
                    w_o = w_o.t().contiguous()
                b_o = getattr(base_p, "bias", getattr(c_proj_mod, "bias", None))
                extracted["o"] = (w_o, b_o.data if b_o is not None else None)

        # BERT / RoBERTa nested encoder
        if hasattr(old_attn, "self") and any(extracted[k][0] is None for k in ("q", "k", "v")):
            s = old_attn.self
            if hasattr(s, "query"):
                extracted["q"] = (s.query.weight.data, getattr(s.query, "bias", None))
            if hasattr(s, "key"):
                extracted["k"] = (s.key.weight.data, getattr(s.key, "bias", None))
            if hasattr(s, "value"):
                extracted["v"] = (s.value.weight.data, getattr(s.value, "bias", None))

        if (
            hasattr(old_attn, "output")
            and hasattr(old_attn.output, "dense")
            and extracted["o"][0] is None
        ):
            dense = old_attn.output.dense
            extracted["o"] = (dense.weight.data, getattr(dense, "bias", None))

        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 proj, names in named_map.items():
            if extracted[proj][0] is None:
                for name in names:
                    if hasattr(old_attn, name):
                        layer = getattr(old_attn, name)
                        if hasattr(layer, "weight"):
                            extracted[proj] = (
                                layer.weight.data,
                                getattr(layer, "bias", None),
                            )
                            break

        cls_name = old_attn.__class__.__name__
        has_out_proj = any(
            hasattr(old_attn, a)
            for a in (
                "dense",
                "out_proj",
                "c_proj",
                "out_lin",
                "o_proj",
                "wo",
                "o",
                "proj",
            )
        )
        if (
            hasattr(old_attn, "out_lin")
            or hasattr(old_attn, "c_proj")
            or hasattr(old_attn, "o_proj")
        ):
            self.is_nested_encoder = False
        elif (
            hasattr(old_attn, "self")
            and hasattr(old_attn.self, "query")
            and hasattr(old_attn, "output")
            and hasattr(old_attn.output, "dense")
        ) or (
            cls_name
            in (
                "BertSelfAttention",
                "RobertaSelfAttention",
                "XLMRobertaSelfAttention",
                "ElectraSelfAttention",
            )
            or "RobertaSelfAttention" in str(type(old_attn))
            or "BertSelfAttention" in str(type(old_attn))
            or (cls_name.endswith("SelfAttention") and not has_out_proj)
        ):
            self.is_nested_encoder = True
        else:
            self.is_nested_encoder = False

        missing = [k for k in ("q", "k", "v") if extracted[k][0] is None]
        if not self.is_nested_encoder and extracted["o"][0] is None:
            missing.append("o")
        if missing:
            raise RuntimeError(
                f"AegisAttentionV2.copy_weights: missing {missing} from "
                f"{old_attn.__class__.__name__}. Refusing random init."
            )

        for proj_key in ("q", "k", "v"):
            w = extracted[proj_key][0]
            if w is not None:
                norm = float(torch.linalg.norm(w.float()).item())
                if norm < 1e-4:
                    logger.warning(
                        "AegisAttentionV2: weight for '%s' has near-zero norm (%.2e).",
                        proj_key,
                        norm,
                    )

        def _orient(w: torch.Tensor, in_dim: int) -> torch.Tensor:
            if w.dim() == 2 and w.shape[1] != in_dim and w.shape[0] == in_dim:
                return w.t().contiguous()
            return w

        with torch.no_grad():
            for proj_key, target in (
                ("q", self.q_proj),
                ("k", self.k_proj),
                ("v", self.v_proj),
                ("o", self.out_proj),
            ):
                w, b = extracted[proj_key]
                if w is not None:
                    in_dim = self.all_head_size if proj_key == "o" else self.hidden_size
                    target.decompose_from_weight(_orient(w, in_dim), b)
                elif proj_key == "o" and self.is_nested_encoder:
                    eye_w = torch.eye(
                        self.all_head_size,
                        self.all_head_size,
                        device=self.out_proj.up.weight.device,
                        dtype=self.out_proj.up.weight.dtype,
                    )
                    target.decompose_from_weight(eye_w, None)

        if hasattr(old_attn, "is_decoder"):
            self.is_decoder = bool(old_attn.is_decoder)
        elif hasattr(old_attn, "c_attn") or "gpt" in old_attn.__class__.__name__.lower():
            self.is_decoder = True

    # ------------------------------------------------------------------
    # V2-FIX-07: safe from_distilbert_pretrained
    # ------------------------------------------------------------------
    @classmethod
    def from_distilbert_pretrained(
        cls, old_attn: nn.Module, model_config: Any | None = None, **kwargs: Any
    ) -> "AegisAttentionV2":
        if model_config is None:
            out_mod = getattr(old_attn, "out_lin", None) or getattr(old_attn, "dense", None)
            if out_mod is None or not hasattr(out_mod, "out_features"):
                raise ValueError(
                    "from_distilbert_pretrained: could not infer hidden_size "
                    "from out_lin or dense. Pass model_config explicitly."
                )

            class DummyConfig:
                pass

            model_config = DummyConfig()
            model_config.hidden_size = out_mod.out_features
            n_heads = getattr(
                old_attn,
                "n_heads",
                getattr(
                    old_attn,
                    "num_attention_heads",
                    getattr(old_attn, "num_heads", None),
                ),
            )
            if n_heads is None:
                n_heads = max(1, model_config.hidden_size // 64)
            model_config.num_attention_heads = n_heads
            model_config.num_key_value_heads = n_heads
            model_config.head_dim = model_config.hidden_size // n_heads
        return cls(config=model_config, pretrained_attn=old_attn, **kwargs)

    # ------------------------------------------------------------------
    # V2-FIX-05: unified mask handling
    # ------------------------------------------------------------------
    @staticmethod
    def _prepare_valid_mask(
        attention_mask: torch.Tensor | None,
        B: int,
        N_kv: int,
        device: torch.device,
    ) -> torch.Tensor | None:
        """Return boolean (B, N_kv): True = key is valid. None if no mask."""
        if attention_mask is None:
            return None
        if attention_mask.dim() == 2:
            if attention_mask.dtype == torch.bool:
                return attention_mask
            if attention_mask.dtype.is_floating_point:
                return attention_mask > -1e4
            return attention_mask > 0
        if attention_mask.dim() == 4:
            if attention_mask.dtype == torch.bool:
                v = attention_mask
            elif attention_mask.dtype.is_floating_point:
                v = attention_mask > -1e4
            else:
                v = attention_mask > 0
            # (B, 1, N_q, N_kv) -> OR over query dim -> (B, N_kv)
            return v.any(dim=-2).squeeze(1)
        raise ValueError(f"Unsupported attention_mask dim: {attention_mask.dim()}")

    # ------------------------------------------------------------------
    # V2-FIX-02 + V2-FIX-09: 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,
        *args: Any,
        **kwargs: Any,
    ) -> tuple[torch.Tensor, torch.Tensor | None]:
        B, N, _ = hidden_states.shape
        H, D = self.num_heads, self.head_dim
        Hkv = self.num_kv_heads
        device = hidden_states.device

        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]

        # ---- Mask (V2-FIX-05) ----
        eff_mask = encoder_attention_mask if is_cross else attention_mask
        token_valid = self._prepare_valid_mask(eff_mask, B, N_kv_new, device)

        # ---- Projections ----
        Q = self.q_proj(hidden_states).view(B, N, H, D).transpose(1, 2).contiguous()
        K_new = self.k_proj(kv_input).view(B, N_kv_new, Hkv, D).transpose(1, 2).contiguous()
        V_new = self.v_proj(kv_input).view(B, N_kv_new, Hkv, D).transpose(1, 2).contiguous()

        # ---- RoPE with past offset ----
        if position_embeddings is not None and not is_cross:
            cos, sin = position_embeddings
            past_len_rope = 0
            if past_key_value is not None:
                if isinstance(past_key_value, (tuple, list)) and len(past_key_value) >= 1:
                    past_len_rope = past_key_value[0].shape[-2]
                elif hasattr(past_key_value, "key_cache") and past_key_value.key_cache:
                    past_len_rope = past_key_value.key_cache[0].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 concat ----
        past_k = past_v = None
        if past_key_value is not None and not is_cross:
            if isinstance(past_key_value, (tuple, list)) and len(past_key_value) >= 2:
                past_k, past_v = past_key_value[0], past_key_value[1]
            elif hasattr(past_key_value, "key_cache") and past_key_value.key_cache:
                idx = min(getattr(self, "layer_idx", 0), len(past_key_value.key_cache) - 1)
                past_k, past_v = (
                    past_key_value.key_cache[idx],
                    past_key_value.value_cache[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)
        else:
            K_cat = K_new
            V_cat = V_new

        if past_key_value is not None and hasattr(past_key_value, "update") and not is_cross:
            idx = getattr(self, "layer_idx", 0)
            past_key_value.update(K_new, V_new, idx)
        else:
            pass

        N_kv = K_cat.shape[2]
        past_len = N_kv - N if (self.is_decoder and not is_cross) else 0

        # ---- GQA expansion ----
        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

        # ---- Local attention (V2-FIX-03) ----
        local_out = _local_window_attention_v2(
            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,
        )  # (B, H, N, D)

        # ---- Global attention (V2-FIX-08) ----
        if not is_cross:
            if self.prune_gate is not None:
                keep = torch.sigmoid(self.prune_gate(hidden_states))  # (B, N, H)
                # V2-FIX-06: expand across GQA heads
                if self.kv_repeat > 1:
                    keep_kv = keep[:, :, : self.num_kv_heads]
                else:
                    keep_kv = keep
                Kg = K_cat * keep_kv.transpose(1, 2).unsqueeze(-1)
                Vg = V_cat * keep_kv.transpose(1, 2).unsqueeze(-1)
            else:
                Kg, Vg = K_cat, V_cat

            if self.kv_repeat > 1:
                Kg = repeat_kv(Kg, self.kv_repeat)
                Vg = repeat_kv(Vg, self.kv_repeat)

            global_out = _global_linear_attention_v2(
                Q,
                Kg,
                Vg,
                token_valid=token_valid,
                is_decoder=self.is_decoder,
            )  # (B, H, N, D)

            alpha = torch.sigmoid(self.fusion_gate(hidden_states)).unsqueeze(-1)  # (B, N, H, 1)
            alpha = alpha.transpose(1, 2)  # (B, H, N, 1)
            fused = alpha * local_out + (1.0 - alpha) * global_out
        else:
            # Cross-attention: local only (global over encoder is questionable)
            fused = local_out

        # ---- head_mask (V2-FIX-02) ----
        if head_mask is not None:
            if head_mask.dim() == 1:
                head_mask = head_mask.view(1, -1, 1, 1)
            elif head_mask.dim() == 2:
                head_mask = head_mask.unsqueeze(-1).unsqueeze(-1)  # (B, H, 1, 1)
            elif head_mask.dim() == 3:
                head_mask = head_mask[:, :, :1].unsqueeze(-1)  # (B, H, 1, 1)
            fused = fused * head_mask.to(fused.dtype)

        # ---- Reshape to (B, N, H*D) ----
        out = fused.transpose(1, 2).contiguous().view(B, N, self.all_head_size)
        out = torch.nan_to_num(out, nan=0.0, posinf=1e4, neginf=-1e4)

        if self.is_nested_encoder:
            output = out
        else:
            output = self.out_proj(out)
            output = self.dropout(output)
        output = torch.nan_to_num(output, nan=0.0, posinf=1e4, neginf=-1e4)
        return output.to(hidden_states.dtype), None

    def get_ortho_loss(self) -> torch.Tensor:
        return torch.zeros((), device=self.out_proj.up.weight.device)

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.

Static methods

def from_distilbert_pretrained(old_attn: torch.nn.modules.module.Module,
model_config: Any | None = None,
**kwargs: Any) ‑> AegisAttentionV2

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 k_proj1
Expand source code
@property
def k_proj1(self):
    return self.k_proj.down
prop k_proj2
Expand source code
@property
def k_proj2(self):
    return self.k_proj.up
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 out_proj1
Expand source code
@property
def out_proj1(self):
    return self.out_proj.down
prop out_proj2
Expand source code
@property
def out_proj2(self):
    return self.out_proj.up
prop q_lin
Expand source code
@property
def q_lin(self):
    return self.q_proj
prop q_proj1
Expand source code
@property
def q_proj1(self):
    return self.q_proj.down
prop q_proj2
Expand source code
@property
def q_proj2(self):
    return self.q_proj.up
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 v_proj1
Expand source code
@property
def v_proj1(self):
    return self.v_proj.down
prop v_proj2
Expand source code
@property
def v_proj2(self):
    return self.v_proj.up
prop value
Expand source code
@property
def value(self):
    return self.v_proj

Methods

def copy_weights(self, old_attn: Any) ‑> None
Expand source code
def copy_weights(self, old_attn: Any) -> None:
    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),
    }

    for proj in ["q", "k", "v", "o"]:
        try:
            w, b = UniversalAdapter.get_weight_bias(old_attn, proj)
            if w is not None:
                extracted[proj] = (w, b)
        except Exception as e:
            logger.debug("UniversalAdapter fallback failed: %s", e)

    # GPT-2 fused c_attn (Conv1D)
    if hasattr(old_attn, "c_attn") and any(extracted[k][0] is None for k in ("q", "k", "v")):
        c_attn_mod = old_attn.c_attn
        base_c = getattr(c_attn_mod, "base_layer", c_attn_mod)
        weight = getattr(base_c, "weight", getattr(c_attn_mod, "weight", None))
        if weight is not None:
            weight = weight.data.clone()
            is_conv1d = (
                (c_attn_mod.__class__.__name__ == "Conv1D")
                or (base_c.__class__.__name__ == "Conv1D")
                or hasattr(c_attn_mod, "nf")
                or hasattr(base_c, "nf")
            )
            if is_conv1d or (weight.dim() == 2 and weight.shape[1] == 3 * self.hidden_size):
                weight = weight.t().contiguous()
            bias = getattr(base_c, "bias", getattr(c_attn_mod, "bias", None))
            bd = bias.data if bias is not None else None
            split = weight.shape[0] // 3
            extracted["q"] = (
                weight[:split, :],
                bd[:split] if bd is not None else None,
            )
            extracted["k"] = (
                weight[split : 2 * split, :],
                bd[split : 2 * split] if bd is not None else None,
            )
            extracted["v"] = (
                weight[2 * split :, :],
                bd[2 * split :] if bd is not None else None,
            )

    if hasattr(old_attn, "c_proj") and extracted["o"][0] is None:
        c_proj_mod = old_attn.c_proj
        base_p = getattr(c_proj_mod, "base_layer", c_proj_mod)
        w_o = getattr(base_p, "weight", getattr(c_proj_mod, "weight", None))
        if w_o is not None:
            w_o = w_o.data.clone()
            is_conv1d = (
                (c_proj_mod.__class__.__name__ == "Conv1D")
                or (base_p.__class__.__name__ == "Conv1D")
                or hasattr(c_proj_mod, "nf")
                or hasattr(base_p, "nf")
            )
            if is_conv1d or (
                w_o.dim() == 2
                and w_o.shape[0] != self.hidden_size
                and w_o.shape[1] == self.hidden_size
            ):
                w_o = w_o.t().contiguous()
            b_o = getattr(base_p, "bias", getattr(c_proj_mod, "bias", None))
            extracted["o"] = (w_o, b_o.data if b_o is not None else None)

    # BERT / RoBERTa nested encoder
    if hasattr(old_attn, "self") and any(extracted[k][0] is None for k in ("q", "k", "v")):
        s = old_attn.self
        if hasattr(s, "query"):
            extracted["q"] = (s.query.weight.data, getattr(s.query, "bias", None))
        if hasattr(s, "key"):
            extracted["k"] = (s.key.weight.data, getattr(s.key, "bias", None))
        if hasattr(s, "value"):
            extracted["v"] = (s.value.weight.data, getattr(s.value, "bias", None))

    if (
        hasattr(old_attn, "output")
        and hasattr(old_attn.output, "dense")
        and extracted["o"][0] is None
    ):
        dense = old_attn.output.dense
        extracted["o"] = (dense.weight.data, getattr(dense, "bias", None))

    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 proj, names in named_map.items():
        if extracted[proj][0] is None:
            for name in names:
                if hasattr(old_attn, name):
                    layer = getattr(old_attn, name)
                    if hasattr(layer, "weight"):
                        extracted[proj] = (
                            layer.weight.data,
                            getattr(layer, "bias", None),
                        )
                        break

    cls_name = old_attn.__class__.__name__
    has_out_proj = any(
        hasattr(old_attn, a)
        for a in (
            "dense",
            "out_proj",
            "c_proj",
            "out_lin",
            "o_proj",
            "wo",
            "o",
            "proj",
        )
    )
    if (
        hasattr(old_attn, "out_lin")
        or hasattr(old_attn, "c_proj")
        or hasattr(old_attn, "o_proj")
    ):
        self.is_nested_encoder = False
    elif (
        hasattr(old_attn, "self")
        and hasattr(old_attn.self, "query")
        and hasattr(old_attn, "output")
        and hasattr(old_attn.output, "dense")
    ) or (
        cls_name
        in (
            "BertSelfAttention",
            "RobertaSelfAttention",
            "XLMRobertaSelfAttention",
            "ElectraSelfAttention",
        )
        or "RobertaSelfAttention" in str(type(old_attn))
        or "BertSelfAttention" in str(type(old_attn))
        or (cls_name.endswith("SelfAttention") and not has_out_proj)
    ):
        self.is_nested_encoder = True
    else:
        self.is_nested_encoder = False

    missing = [k for k in ("q", "k", "v") if extracted[k][0] is None]
    if not self.is_nested_encoder and extracted["o"][0] is None:
        missing.append("o")
    if missing:
        raise RuntimeError(
            f"AegisAttentionV2.copy_weights: missing {missing} from "
            f"{old_attn.__class__.__name__}. Refusing random init."
        )

    for proj_key in ("q", "k", "v"):
        w = extracted[proj_key][0]
        if w is not None:
            norm = float(torch.linalg.norm(w.float()).item())
            if norm < 1e-4:
                logger.warning(
                    "AegisAttentionV2: weight for '%s' has near-zero norm (%.2e).",
                    proj_key,
                    norm,
                )

    def _orient(w: torch.Tensor, in_dim: int) -> torch.Tensor:
        if w.dim() == 2 and w.shape[1] != in_dim and w.shape[0] == in_dim:
            return w.t().contiguous()
        return w

    with torch.no_grad():
        for proj_key, target in (
            ("q", self.q_proj),
            ("k", self.k_proj),
            ("v", self.v_proj),
            ("o", self.out_proj),
        ):
            w, b = extracted[proj_key]
            if w is not None:
                in_dim = self.all_head_size if proj_key == "o" else self.hidden_size
                target.decompose_from_weight(_orient(w, in_dim), b)
            elif proj_key == "o" and self.is_nested_encoder:
                eye_w = torch.eye(
                    self.all_head_size,
                    self.all_head_size,
                    device=self.out_proj.up.weight.device,
                    dtype=self.out_proj.up.weight.dtype,
                )
                target.decompose_from_weight(eye_w, None)

    if hasattr(old_attn, "is_decoder"):
        self.is_decoder = bool(old_attn.is_decoder)
    elif hasattr(old_attn, "c_attn") or "gpt" in old_attn.__class__.__name__.lower():
        self.is_decoder = True

Universal fallback: copy pretrained weights from old attention layer.

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,
*args: Any,
**kwargs: Any) ‑> tuple[torch.Tensor, torch.Tensor | None]
Expand source code
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,
    *args: Any,
    **kwargs: Any,
) -> tuple[torch.Tensor, torch.Tensor | None]:
    B, N, _ = hidden_states.shape
    H, D = self.num_heads, self.head_dim
    Hkv = self.num_kv_heads
    device = hidden_states.device

    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]

    # ---- Mask (V2-FIX-05) ----
    eff_mask = encoder_attention_mask if is_cross else attention_mask
    token_valid = self._prepare_valid_mask(eff_mask, B, N_kv_new, device)

    # ---- Projections ----
    Q = self.q_proj(hidden_states).view(B, N, H, D).transpose(1, 2).contiguous()
    K_new = self.k_proj(kv_input).view(B, N_kv_new, Hkv, D).transpose(1, 2).contiguous()
    V_new = self.v_proj(kv_input).view(B, N_kv_new, Hkv, D).transpose(1, 2).contiguous()

    # ---- RoPE with past offset ----
    if position_embeddings is not None and not is_cross:
        cos, sin = position_embeddings
        past_len_rope = 0
        if past_key_value is not None:
            if isinstance(past_key_value, (tuple, list)) and len(past_key_value) >= 1:
                past_len_rope = past_key_value[0].shape[-2]
            elif hasattr(past_key_value, "key_cache") and past_key_value.key_cache:
                past_len_rope = past_key_value.key_cache[0].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 concat ----
    past_k = past_v = None
    if past_key_value is not None and not is_cross:
        if isinstance(past_key_value, (tuple, list)) and len(past_key_value) >= 2:
            past_k, past_v = past_key_value[0], past_key_value[1]
        elif hasattr(past_key_value, "key_cache") and past_key_value.key_cache:
            idx = min(getattr(self, "layer_idx", 0), len(past_key_value.key_cache) - 1)
            past_k, past_v = (
                past_key_value.key_cache[idx],
                past_key_value.value_cache[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)
    else:
        K_cat = K_new
        V_cat = V_new

    if past_key_value is not None and hasattr(past_key_value, "update") and not is_cross:
        idx = getattr(self, "layer_idx", 0)
        past_key_value.update(K_new, V_new, idx)
    else:
        pass

    N_kv = K_cat.shape[2]
    past_len = N_kv - N if (self.is_decoder and not is_cross) else 0

    # ---- GQA expansion ----
    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

    # ---- Local attention (V2-FIX-03) ----
    local_out = _local_window_attention_v2(
        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,
    )  # (B, H, N, D)

    # ---- Global attention (V2-FIX-08) ----
    if not is_cross:
        if self.prune_gate is not None:
            keep = torch.sigmoid(self.prune_gate(hidden_states))  # (B, N, H)
            # V2-FIX-06: expand across GQA heads
            if self.kv_repeat > 1:
                keep_kv = keep[:, :, : self.num_kv_heads]
            else:
                keep_kv = keep
            Kg = K_cat * keep_kv.transpose(1, 2).unsqueeze(-1)
            Vg = V_cat * keep_kv.transpose(1, 2).unsqueeze(-1)
        else:
            Kg, Vg = K_cat, V_cat

        if self.kv_repeat > 1:
            Kg = repeat_kv(Kg, self.kv_repeat)
            Vg = repeat_kv(Vg, self.kv_repeat)

        global_out = _global_linear_attention_v2(
            Q,
            Kg,
            Vg,
            token_valid=token_valid,
            is_decoder=self.is_decoder,
        )  # (B, H, N, D)

        alpha = torch.sigmoid(self.fusion_gate(hidden_states)).unsqueeze(-1)  # (B, N, H, 1)
        alpha = alpha.transpose(1, 2)  # (B, H, N, 1)
        fused = alpha * local_out + (1.0 - alpha) * global_out
    else:
        # Cross-attention: local only (global over encoder is questionable)
        fused = local_out

    # ---- head_mask (V2-FIX-02) ----
    if head_mask is not None:
        if head_mask.dim() == 1:
            head_mask = head_mask.view(1, -1, 1, 1)
        elif head_mask.dim() == 2:
            head_mask = head_mask.unsqueeze(-1).unsqueeze(-1)  # (B, H, 1, 1)
        elif head_mask.dim() == 3:
            head_mask = head_mask[:, :, :1].unsqueeze(-1)  # (B, H, 1, 1)
        fused = fused * head_mask.to(fused.dtype)

    # ---- Reshape to (B, N, H*D) ----
    out = fused.transpose(1, 2).contiguous().view(B, N, self.all_head_size)
    out = torch.nan_to_num(out, nan=0.0, posinf=1e4, neginf=-1e4)

    if self.is_nested_encoder:
        output = out
    else:
        output = self.out_proj(out)
        output = self.dropout(output)
    output = torch.nan_to_num(output, nan=0.0, posinf=1e4, neginf=-1e4)
    return output.to(hidden_states.dtype), None

Must return (output, attention_weights_or_None).

def get_ortho_loss(self) ‑> torch.Tensor
Expand source code
def get_ortho_loss(self) -> torch.Tensor:
    return torch.zeros((), device=self.out_proj.up.weight.device)
class AegisAttentionV3 (config: Any = None,
pretrained_attn: torch.nn.modules.module.Module | None = None,
is_decoder: bool | None = None,
**kwargs: Any)
Expand source code
@ATTENTION_REGISTRY.register("aegis_v2", supports_causal=True, supports_gqa=True)
@ATTENTION_REGISTRY.register("aegis_v1", supports_causal=True, supports_gqa=True)
@ATTENTION_REGISTRY.register("aegis_v3", supports_causal=True, supports_gqa=True)
@ATTENTION_REGISTRY.register("aegis_v4", supports_causal=True, supports_gqa=True)
@ATTENTION_REGISTRY.register("aegis_v1_module", supports_causal=True, supports_gqa=True)
class AegisAttentionV2(BaseAttention):
    supports_causal: bool = True
    supports_gqa: bool = True
    is_custom_attention: bool = True

    def __init__(
        self,
        config: Any = None,
        pretrained_attn: nn.Module | None = None,
        is_decoder: bool | None = None,
        **kwargs: Any,
    ) -> None:
        if config is None or not isinstance(config, AegisConfigV2):
            h_size = (
                kwargs.get(
                    "hidden_size",
                    getattr(
                        config,
                        "hidden_size",
                        getattr(config, "d_model", getattr(config, "dim", 768)),
                    ),
                )
                if config is not None
                else 768
            )
            n_heads = (
                kwargs.get(
                    "num_heads",
                    getattr(config, "num_attention_heads", getattr(config, "num_heads", 12)),
                )
                if config is not None
                else 12
            )
            h_dim = (
                kwargs.get(
                    "head_dim",
                    getattr(
                        config,
                        "head_dim",
                        getattr(config, "d_kv", h_size // max(1, n_heads)),
                    ),
                )
                if config is not None
                else h_size // max(1, n_heads)
            )
            n_kv_heads = (
                kwargs.get(
                    "num_key_value_heads",
                    getattr(
                        config,
                        "num_key_value_heads",
                        getattr(config, "num_kv_heads", n_heads),
                    ),
                )
                if config is not None
                else n_heads
            )

            # V2-FIX-01: robust decoder detection
            dec = _detect_decoder(config, is_decoder, pretrained_attn)

            config = AegisConfigV2(
                hidden_size=h_size,
                num_attention_heads=n_heads,
                num_heads=n_heads,
                num_key_value_heads=n_kv_heads,
                head_dim=h_dim,
                local_window=(
                    kwargs.get(
                        "local_window",
                        getattr(config, "block_size", getattr(config, "local_window", 64)),
                    )
                    if config is not None
                    else 64
                ),
                rank=(
                    kwargs.pop("rank", getattr(config, "lora_r", getattr(config, "rank", 64)))
                    if config is not None
                    else 64
                ),
                kernel_feature_dim=(
                    getattr(
                        config,
                        "rff_features",
                        getattr(config, "kernel_feature_dim", 128),
                    )
                    if config is not None
                    else 128
                ),
                dropout=(
                    kwargs.get("dropout", getattr(config, "dropout", 0.1))
                    if config is not None
                    else 0.1
                ),
                use_adaptive_pruning=(
                    getattr(config, "use_gate", True) if config is not None else True
                ),
                gate_init_bias=(
                    getattr(config, "gate_init_bias", 1.8) if config is not None else 1.8
                ),
                is_decoder=dec,
            )

        super().__init__(config)
        self.config = config
        self.hidden_size: int = config.hidden_size
        self.num_heads: int = config.num_attention_heads
        self.head_dim: int = config.head_dim
        self.num_kv_heads: int = getattr(config, "num_key_value_heads", self.num_heads)
        if self.num_heads % max(1, self.num_kv_heads) != 0:
            raise ValueError(
                f"num_heads ({self.num_heads}) must be divisible by "
                f"num_kv_heads ({self.num_kv_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 = config.local_window
        self.rank: int = config.rank
        self.kernel_dim: int = config.kernel_feature_dim
        self.dropout = nn.Dropout(config.dropout)
        self.is_decoder: bool = getattr(config, "is_decoder", False)
        self.is_nested_encoder: bool = False

        self.q_proj = FactorisedLinear(self.hidden_size, self.all_head_size, rank=self.rank)
        self.k_proj = FactorisedLinear(self.hidden_size, self.all_kv_head_size, rank=self.rank)
        self.v_proj = FactorisedLinear(self.hidden_size, self.all_kv_head_size, rank=self.rank)
        self.out_proj = FactorisedLinear(self.all_head_size, self.hidden_size, rank=self.rank)

        if config.use_adaptive_pruning:
            self.prune_gate: nn.Sequential | None = nn.Sequential(
                nn.Linear(self.hidden_size, max(1, self.hidden_size // 4)),
                nn.ReLU(),
                nn.Linear(max(1, self.hidden_size // 4), self.num_heads),
            )
        else:
            self.prune_gate = None

        self.fusion_gate = nn.Linear(self.hidden_size, self.num_heads)
        self.fusion_gate.bias.data.fill_(config.gate_init_bias)

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

    # ------------------------------------------------------------------
    # Aliases
    # ------------------------------------------------------------------
    @property
    def q_proj1(self):
        return self.q_proj.down

    @property
    def q_proj2(self):
        return self.q_proj.up

    @property
    def k_proj1(self):
        return self.k_proj.down

    @property
    def k_proj2(self):
        return self.k_proj.up

    @property
    def v_proj1(self):
        return self.v_proj.down

    @property
    def v_proj2(self):
        return self.v_proj.up

    @property
    def out_proj1(self):
        return self.out_proj.down

    @property
    def out_proj2(self):
        return self.out_proj.up

    @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 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 dense(self):
        return self.out_proj

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

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

    # ------------------------------------------------------------------
    # Weight transfer (same logic as V5)
    # ------------------------------------------------------------------
    def _copy_pretrained(self, old_attn: nn.Module) -> None:
        self.copy_weights(old_attn)

    def copy_weights(self, old_attn: Any) -> None:
        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),
        }

        for proj in ["q", "k", "v", "o"]:
            try:
                w, b = UniversalAdapter.get_weight_bias(old_attn, proj)
                if w is not None:
                    extracted[proj] = (w, b)
            except Exception as e:
                logger.debug("UniversalAdapter fallback failed: %s", e)

        # GPT-2 fused c_attn (Conv1D)
        if hasattr(old_attn, "c_attn") and any(extracted[k][0] is None for k in ("q", "k", "v")):
            c_attn_mod = old_attn.c_attn
            base_c = getattr(c_attn_mod, "base_layer", c_attn_mod)
            weight = getattr(base_c, "weight", getattr(c_attn_mod, "weight", None))
            if weight is not None:
                weight = weight.data.clone()
                is_conv1d = (
                    (c_attn_mod.__class__.__name__ == "Conv1D")
                    or (base_c.__class__.__name__ == "Conv1D")
                    or hasattr(c_attn_mod, "nf")
                    or hasattr(base_c, "nf")
                )
                if is_conv1d or (weight.dim() == 2 and weight.shape[1] == 3 * self.hidden_size):
                    weight = weight.t().contiguous()
                bias = getattr(base_c, "bias", getattr(c_attn_mod, "bias", None))
                bd = bias.data if bias is not None else None
                split = weight.shape[0] // 3
                extracted["q"] = (
                    weight[:split, :],
                    bd[:split] if bd is not None else None,
                )
                extracted["k"] = (
                    weight[split : 2 * split, :],
                    bd[split : 2 * split] if bd is not None else None,
                )
                extracted["v"] = (
                    weight[2 * split :, :],
                    bd[2 * split :] if bd is not None else None,
                )

        if hasattr(old_attn, "c_proj") and extracted["o"][0] is None:
            c_proj_mod = old_attn.c_proj
            base_p = getattr(c_proj_mod, "base_layer", c_proj_mod)
            w_o = getattr(base_p, "weight", getattr(c_proj_mod, "weight", None))
            if w_o is not None:
                w_o = w_o.data.clone()
                is_conv1d = (
                    (c_proj_mod.__class__.__name__ == "Conv1D")
                    or (base_p.__class__.__name__ == "Conv1D")
                    or hasattr(c_proj_mod, "nf")
                    or hasattr(base_p, "nf")
                )
                if is_conv1d or (
                    w_o.dim() == 2
                    and w_o.shape[0] != self.hidden_size
                    and w_o.shape[1] == self.hidden_size
                ):
                    w_o = w_o.t().contiguous()
                b_o = getattr(base_p, "bias", getattr(c_proj_mod, "bias", None))
                extracted["o"] = (w_o, b_o.data if b_o is not None else None)

        # BERT / RoBERTa nested encoder
        if hasattr(old_attn, "self") and any(extracted[k][0] is None for k in ("q", "k", "v")):
            s = old_attn.self
            if hasattr(s, "query"):
                extracted["q"] = (s.query.weight.data, getattr(s.query, "bias", None))
            if hasattr(s, "key"):
                extracted["k"] = (s.key.weight.data, getattr(s.key, "bias", None))
            if hasattr(s, "value"):
                extracted["v"] = (s.value.weight.data, getattr(s.value, "bias", None))

        if (
            hasattr(old_attn, "output")
            and hasattr(old_attn.output, "dense")
            and extracted["o"][0] is None
        ):
            dense = old_attn.output.dense
            extracted["o"] = (dense.weight.data, getattr(dense, "bias", None))

        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 proj, names in named_map.items():
            if extracted[proj][0] is None:
                for name in names:
                    if hasattr(old_attn, name):
                        layer = getattr(old_attn, name)
                        if hasattr(layer, "weight"):
                            extracted[proj] = (
                                layer.weight.data,
                                getattr(layer, "bias", None),
                            )
                            break

        cls_name = old_attn.__class__.__name__
        has_out_proj = any(
            hasattr(old_attn, a)
            for a in (
                "dense",
                "out_proj",
                "c_proj",
                "out_lin",
                "o_proj",
                "wo",
                "o",
                "proj",
            )
        )
        if (
            hasattr(old_attn, "out_lin")
            or hasattr(old_attn, "c_proj")
            or hasattr(old_attn, "o_proj")
        ):
            self.is_nested_encoder = False
        elif (
            hasattr(old_attn, "self")
            and hasattr(old_attn.self, "query")
            and hasattr(old_attn, "output")
            and hasattr(old_attn.output, "dense")
        ) or (
            cls_name
            in (
                "BertSelfAttention",
                "RobertaSelfAttention",
                "XLMRobertaSelfAttention",
                "ElectraSelfAttention",
            )
            or "RobertaSelfAttention" in str(type(old_attn))
            or "BertSelfAttention" in str(type(old_attn))
            or (cls_name.endswith("SelfAttention") and not has_out_proj)
        ):
            self.is_nested_encoder = True
        else:
            self.is_nested_encoder = False

        missing = [k for k in ("q", "k", "v") if extracted[k][0] is None]
        if not self.is_nested_encoder and extracted["o"][0] is None:
            missing.append("o")
        if missing:
            raise RuntimeError(
                f"AegisAttentionV2.copy_weights: missing {missing} from "
                f"{old_attn.__class__.__name__}. Refusing random init."
            )

        for proj_key in ("q", "k", "v"):
            w = extracted[proj_key][0]
            if w is not None:
                norm = float(torch.linalg.norm(w.float()).item())
                if norm < 1e-4:
                    logger.warning(
                        "AegisAttentionV2: weight for '%s' has near-zero norm (%.2e).",
                        proj_key,
                        norm,
                    )

        def _orient(w: torch.Tensor, in_dim: int) -> torch.Tensor:
            if w.dim() == 2 and w.shape[1] != in_dim and w.shape[0] == in_dim:
                return w.t().contiguous()
            return w

        with torch.no_grad():
            for proj_key, target in (
                ("q", self.q_proj),
                ("k", self.k_proj),
                ("v", self.v_proj),
                ("o", self.out_proj),
            ):
                w, b = extracted[proj_key]
                if w is not None:
                    in_dim = self.all_head_size if proj_key == "o" else self.hidden_size
                    target.decompose_from_weight(_orient(w, in_dim), b)
                elif proj_key == "o" and self.is_nested_encoder:
                    eye_w = torch.eye(
                        self.all_head_size,
                        self.all_head_size,
                        device=self.out_proj.up.weight.device,
                        dtype=self.out_proj.up.weight.dtype,
                    )
                    target.decompose_from_weight(eye_w, None)

        if hasattr(old_attn, "is_decoder"):
            self.is_decoder = bool(old_attn.is_decoder)
        elif hasattr(old_attn, "c_attn") or "gpt" in old_attn.__class__.__name__.lower():
            self.is_decoder = True

    # ------------------------------------------------------------------
    # V2-FIX-07: safe from_distilbert_pretrained
    # ------------------------------------------------------------------
    @classmethod
    def from_distilbert_pretrained(
        cls, old_attn: nn.Module, model_config: Any | None = None, **kwargs: Any
    ) -> "AegisAttentionV2":
        if model_config is None:
            out_mod = getattr(old_attn, "out_lin", None) or getattr(old_attn, "dense", None)
            if out_mod is None or not hasattr(out_mod, "out_features"):
                raise ValueError(
                    "from_distilbert_pretrained: could not infer hidden_size "
                    "from out_lin or dense. Pass model_config explicitly."
                )

            class DummyConfig:
                pass

            model_config = DummyConfig()
            model_config.hidden_size = out_mod.out_features
            n_heads = getattr(
                old_attn,
                "n_heads",
                getattr(
                    old_attn,
                    "num_attention_heads",
                    getattr(old_attn, "num_heads", None),
                ),
            )
            if n_heads is None:
                n_heads = max(1, model_config.hidden_size // 64)
            model_config.num_attention_heads = n_heads
            model_config.num_key_value_heads = n_heads
            model_config.head_dim = model_config.hidden_size // n_heads
        return cls(config=model_config, pretrained_attn=old_attn, **kwargs)

    # ------------------------------------------------------------------
    # V2-FIX-05: unified mask handling
    # ------------------------------------------------------------------
    @staticmethod
    def _prepare_valid_mask(
        attention_mask: torch.Tensor | None,
        B: int,
        N_kv: int,
        device: torch.device,
    ) -> torch.Tensor | None:
        """Return boolean (B, N_kv): True = key is valid. None if no mask."""
        if attention_mask is None:
            return None
        if attention_mask.dim() == 2:
            if attention_mask.dtype == torch.bool:
                return attention_mask
            if attention_mask.dtype.is_floating_point:
                return attention_mask > -1e4
            return attention_mask > 0
        if attention_mask.dim() == 4:
            if attention_mask.dtype == torch.bool:
                v = attention_mask
            elif attention_mask.dtype.is_floating_point:
                v = attention_mask > -1e4
            else:
                v = attention_mask > 0
            # (B, 1, N_q, N_kv) -> OR over query dim -> (B, N_kv)
            return v.any(dim=-2).squeeze(1)
        raise ValueError(f"Unsupported attention_mask dim: {attention_mask.dim()}")

    # ------------------------------------------------------------------
    # V2-FIX-02 + V2-FIX-09: 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,
        *args: Any,
        **kwargs: Any,
    ) -> tuple[torch.Tensor, torch.Tensor | None]:
        B, N, _ = hidden_states.shape
        H, D = self.num_heads, self.head_dim
        Hkv = self.num_kv_heads
        device = hidden_states.device

        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]

        # ---- Mask (V2-FIX-05) ----
        eff_mask = encoder_attention_mask if is_cross else attention_mask
        token_valid = self._prepare_valid_mask(eff_mask, B, N_kv_new, device)

        # ---- Projections ----
        Q = self.q_proj(hidden_states).view(B, N, H, D).transpose(1, 2).contiguous()
        K_new = self.k_proj(kv_input).view(B, N_kv_new, Hkv, D).transpose(1, 2).contiguous()
        V_new = self.v_proj(kv_input).view(B, N_kv_new, Hkv, D).transpose(1, 2).contiguous()

        # ---- RoPE with past offset ----
        if position_embeddings is not None and not is_cross:
            cos, sin = position_embeddings
            past_len_rope = 0
            if past_key_value is not None:
                if isinstance(past_key_value, (tuple, list)) and len(past_key_value) >= 1:
                    past_len_rope = past_key_value[0].shape[-2]
                elif hasattr(past_key_value, "key_cache") and past_key_value.key_cache:
                    past_len_rope = past_key_value.key_cache[0].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 concat ----
        past_k = past_v = None
        if past_key_value is not None and not is_cross:
            if isinstance(past_key_value, (tuple, list)) and len(past_key_value) >= 2:
                past_k, past_v = past_key_value[0], past_key_value[1]
            elif hasattr(past_key_value, "key_cache") and past_key_value.key_cache:
                idx = min(getattr(self, "layer_idx", 0), len(past_key_value.key_cache) - 1)
                past_k, past_v = (
                    past_key_value.key_cache[idx],
                    past_key_value.value_cache[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)
        else:
            K_cat = K_new
            V_cat = V_new

        if past_key_value is not None and hasattr(past_key_value, "update") and not is_cross:
            idx = getattr(self, "layer_idx", 0)
            past_key_value.update(K_new, V_new, idx)
        else:
            pass

        N_kv = K_cat.shape[2]
        past_len = N_kv - N if (self.is_decoder and not is_cross) else 0

        # ---- GQA expansion ----
        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

        # ---- Local attention (V2-FIX-03) ----
        local_out = _local_window_attention_v2(
            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,
        )  # (B, H, N, D)

        # ---- Global attention (V2-FIX-08) ----
        if not is_cross:
            if self.prune_gate is not None:
                keep = torch.sigmoid(self.prune_gate(hidden_states))  # (B, N, H)
                # V2-FIX-06: expand across GQA heads
                if self.kv_repeat > 1:
                    keep_kv = keep[:, :, : self.num_kv_heads]
                else:
                    keep_kv = keep
                Kg = K_cat * keep_kv.transpose(1, 2).unsqueeze(-1)
                Vg = V_cat * keep_kv.transpose(1, 2).unsqueeze(-1)
            else:
                Kg, Vg = K_cat, V_cat

            if self.kv_repeat > 1:
                Kg = repeat_kv(Kg, self.kv_repeat)
                Vg = repeat_kv(Vg, self.kv_repeat)

            global_out = _global_linear_attention_v2(
                Q,
                Kg,
                Vg,
                token_valid=token_valid,
                is_decoder=self.is_decoder,
            )  # (B, H, N, D)

            alpha = torch.sigmoid(self.fusion_gate(hidden_states)).unsqueeze(-1)  # (B, N, H, 1)
            alpha = alpha.transpose(1, 2)  # (B, H, N, 1)
            fused = alpha * local_out + (1.0 - alpha) * global_out
        else:
            # Cross-attention: local only (global over encoder is questionable)
            fused = local_out

        # ---- head_mask (V2-FIX-02) ----
        if head_mask is not None:
            if head_mask.dim() == 1:
                head_mask = head_mask.view(1, -1, 1, 1)
            elif head_mask.dim() == 2:
                head_mask = head_mask.unsqueeze(-1).unsqueeze(-1)  # (B, H, 1, 1)
            elif head_mask.dim() == 3:
                head_mask = head_mask[:, :, :1].unsqueeze(-1)  # (B, H, 1, 1)
            fused = fused * head_mask.to(fused.dtype)

        # ---- Reshape to (B, N, H*D) ----
        out = fused.transpose(1, 2).contiguous().view(B, N, self.all_head_size)
        out = torch.nan_to_num(out, nan=0.0, posinf=1e4, neginf=-1e4)

        if self.is_nested_encoder:
            output = out
        else:
            output = self.out_proj(out)
            output = self.dropout(output)
        output = torch.nan_to_num(output, nan=0.0, posinf=1e4, neginf=-1e4)
        return output.to(hidden_states.dtype), None

    def get_ortho_loss(self) -> torch.Tensor:
        return torch.zeros((), device=self.out_proj.up.weight.device)

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.

Static methods

def from_distilbert_pretrained(old_attn: torch.nn.modules.module.Module,
model_config: Any | None = None,
**kwargs: Any) ‑> AegisAttentionV2

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 k_proj1
Expand source code
@property
def k_proj1(self):
    return self.k_proj.down
prop k_proj2
Expand source code
@property
def k_proj2(self):
    return self.k_proj.up
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 out_proj1
Expand source code
@property
def out_proj1(self):
    return self.out_proj.down
prop out_proj2
Expand source code
@property
def out_proj2(self):
    return self.out_proj.up
prop q_lin
Expand source code
@property
def q_lin(self):
    return self.q_proj
prop q_proj1
Expand source code
@property
def q_proj1(self):
    return self.q_proj.down
prop q_proj2
Expand source code
@property
def q_proj2(self):
    return self.q_proj.up
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 v_proj1
Expand source code
@property
def v_proj1(self):
    return self.v_proj.down
prop v_proj2
Expand source code
@property
def v_proj2(self):
    return self.v_proj.up
prop value
Expand source code
@property
def value(self):
    return self.v_proj

Methods

def copy_weights(self, old_attn: Any) ‑> None
Expand source code
def copy_weights(self, old_attn: Any) -> None:
    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),
    }

    for proj in ["q", "k", "v", "o"]:
        try:
            w, b = UniversalAdapter.get_weight_bias(old_attn, proj)
            if w is not None:
                extracted[proj] = (w, b)
        except Exception as e:
            logger.debug("UniversalAdapter fallback failed: %s", e)

    # GPT-2 fused c_attn (Conv1D)
    if hasattr(old_attn, "c_attn") and any(extracted[k][0] is None for k in ("q", "k", "v")):
        c_attn_mod = old_attn.c_attn
        base_c = getattr(c_attn_mod, "base_layer", c_attn_mod)
        weight = getattr(base_c, "weight", getattr(c_attn_mod, "weight", None))
        if weight is not None:
            weight = weight.data.clone()
            is_conv1d = (
                (c_attn_mod.__class__.__name__ == "Conv1D")
                or (base_c.__class__.__name__ == "Conv1D")
                or hasattr(c_attn_mod, "nf")
                or hasattr(base_c, "nf")
            )
            if is_conv1d or (weight.dim() == 2 and weight.shape[1] == 3 * self.hidden_size):
                weight = weight.t().contiguous()
            bias = getattr(base_c, "bias", getattr(c_attn_mod, "bias", None))
            bd = bias.data if bias is not None else None
            split = weight.shape[0] // 3
            extracted["q"] = (
                weight[:split, :],
                bd[:split] if bd is not None else None,
            )
            extracted["k"] = (
                weight[split : 2 * split, :],
                bd[split : 2 * split] if bd is not None else None,
            )
            extracted["v"] = (
                weight[2 * split :, :],
                bd[2 * split :] if bd is not None else None,
            )

    if hasattr(old_attn, "c_proj") and extracted["o"][0] is None:
        c_proj_mod = old_attn.c_proj
        base_p = getattr(c_proj_mod, "base_layer", c_proj_mod)
        w_o = getattr(base_p, "weight", getattr(c_proj_mod, "weight", None))
        if w_o is not None:
            w_o = w_o.data.clone()
            is_conv1d = (
                (c_proj_mod.__class__.__name__ == "Conv1D")
                or (base_p.__class__.__name__ == "Conv1D")
                or hasattr(c_proj_mod, "nf")
                or hasattr(base_p, "nf")
            )
            if is_conv1d or (
                w_o.dim() == 2
                and w_o.shape[0] != self.hidden_size
                and w_o.shape[1] == self.hidden_size
            ):
                w_o = w_o.t().contiguous()
            b_o = getattr(base_p, "bias", getattr(c_proj_mod, "bias", None))
            extracted["o"] = (w_o, b_o.data if b_o is not None else None)

    # BERT / RoBERTa nested encoder
    if hasattr(old_attn, "self") and any(extracted[k][0] is None for k in ("q", "k", "v")):
        s = old_attn.self
        if hasattr(s, "query"):
            extracted["q"] = (s.query.weight.data, getattr(s.query, "bias", None))
        if hasattr(s, "key"):
            extracted["k"] = (s.key.weight.data, getattr(s.key, "bias", None))
        if hasattr(s, "value"):
            extracted["v"] = (s.value.weight.data, getattr(s.value, "bias", None))

    if (
        hasattr(old_attn, "output")
        and hasattr(old_attn.output, "dense")
        and extracted["o"][0] is None
    ):
        dense = old_attn.output.dense
        extracted["o"] = (dense.weight.data, getattr(dense, "bias", None))

    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 proj, names in named_map.items():
        if extracted[proj][0] is None:
            for name in names:
                if hasattr(old_attn, name):
                    layer = getattr(old_attn, name)
                    if hasattr(layer, "weight"):
                        extracted[proj] = (
                            layer.weight.data,
                            getattr(layer, "bias", None),
                        )
                        break

    cls_name = old_attn.__class__.__name__
    has_out_proj = any(
        hasattr(old_attn, a)
        for a in (
            "dense",
            "out_proj",
            "c_proj",
            "out_lin",
            "o_proj",
            "wo",
            "o",
            "proj",
        )
    )
    if (
        hasattr(old_attn, "out_lin")
        or hasattr(old_attn, "c_proj")
        or hasattr(old_attn, "o_proj")
    ):
        self.is_nested_encoder = False
    elif (
        hasattr(old_attn, "self")
        and hasattr(old_attn.self, "query")
        and hasattr(old_attn, "output")
        and hasattr(old_attn.output, "dense")
    ) or (
        cls_name
        in (
            "BertSelfAttention",
            "RobertaSelfAttention",
            "XLMRobertaSelfAttention",
            "ElectraSelfAttention",
        )
        or "RobertaSelfAttention" in str(type(old_attn))
        or "BertSelfAttention" in str(type(old_attn))
        or (cls_name.endswith("SelfAttention") and not has_out_proj)
    ):
        self.is_nested_encoder = True
    else:
        self.is_nested_encoder = False

    missing = [k for k in ("q", "k", "v") if extracted[k][0] is None]
    if not self.is_nested_encoder and extracted["o"][0] is None:
        missing.append("o")
    if missing:
        raise RuntimeError(
            f"AegisAttentionV2.copy_weights: missing {missing} from "
            f"{old_attn.__class__.__name__}. Refusing random init."
        )

    for proj_key in ("q", "k", "v"):
        w = extracted[proj_key][0]
        if w is not None:
            norm = float(torch.linalg.norm(w.float()).item())
            if norm < 1e-4:
                logger.warning(
                    "AegisAttentionV2: weight for '%s' has near-zero norm (%.2e).",
                    proj_key,
                    norm,
                )

    def _orient(w: torch.Tensor, in_dim: int) -> torch.Tensor:
        if w.dim() == 2 and w.shape[1] != in_dim and w.shape[0] == in_dim:
            return w.t().contiguous()
        return w

    with torch.no_grad():
        for proj_key, target in (
            ("q", self.q_proj),
            ("k", self.k_proj),
            ("v", self.v_proj),
            ("o", self.out_proj),
        ):
            w, b = extracted[proj_key]
            if w is not None:
                in_dim = self.all_head_size if proj_key == "o" else self.hidden_size
                target.decompose_from_weight(_orient(w, in_dim), b)
            elif proj_key == "o" and self.is_nested_encoder:
                eye_w = torch.eye(
                    self.all_head_size,
                    self.all_head_size,
                    device=self.out_proj.up.weight.device,
                    dtype=self.out_proj.up.weight.dtype,
                )
                target.decompose_from_weight(eye_w, None)

    if hasattr(old_attn, "is_decoder"):
        self.is_decoder = bool(old_attn.is_decoder)
    elif hasattr(old_attn, "c_attn") or "gpt" in old_attn.__class__.__name__.lower():
        self.is_decoder = True

Universal fallback: copy pretrained weights from old attention layer.

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,
*args: Any,
**kwargs: Any) ‑> tuple[torch.Tensor, torch.Tensor | None]
Expand source code
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,
    *args: Any,
    **kwargs: Any,
) -> tuple[torch.Tensor, torch.Tensor | None]:
    B, N, _ = hidden_states.shape
    H, D = self.num_heads, self.head_dim
    Hkv = self.num_kv_heads
    device = hidden_states.device

    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]

    # ---- Mask (V2-FIX-05) ----
    eff_mask = encoder_attention_mask if is_cross else attention_mask
    token_valid = self._prepare_valid_mask(eff_mask, B, N_kv_new, device)

    # ---- Projections ----
    Q = self.q_proj(hidden_states).view(B, N, H, D).transpose(1, 2).contiguous()
    K_new = self.k_proj(kv_input).view(B, N_kv_new, Hkv, D).transpose(1, 2).contiguous()
    V_new = self.v_proj(kv_input).view(B, N_kv_new, Hkv, D).transpose(1, 2).contiguous()

    # ---- RoPE with past offset ----
    if position_embeddings is not None and not is_cross:
        cos, sin = position_embeddings
        past_len_rope = 0
        if past_key_value is not None:
            if isinstance(past_key_value, (tuple, list)) and len(past_key_value) >= 1:
                past_len_rope = past_key_value[0].shape[-2]
            elif hasattr(past_key_value, "key_cache") and past_key_value.key_cache:
                past_len_rope = past_key_value.key_cache[0].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 concat ----
    past_k = past_v = None
    if past_key_value is not None and not is_cross:
        if isinstance(past_key_value, (tuple, list)) and len(past_key_value) >= 2:
            past_k, past_v = past_key_value[0], past_key_value[1]
        elif hasattr(past_key_value, "key_cache") and past_key_value.key_cache:
            idx = min(getattr(self, "layer_idx", 0), len(past_key_value.key_cache) - 1)
            past_k, past_v = (
                past_key_value.key_cache[idx],
                past_key_value.value_cache[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)
    else:
        K_cat = K_new
        V_cat = V_new

    if past_key_value is not None and hasattr(past_key_value, "update") and not is_cross:
        idx = getattr(self, "layer_idx", 0)
        past_key_value.update(K_new, V_new, idx)
    else:
        pass

    N_kv = K_cat.shape[2]
    past_len = N_kv - N if (self.is_decoder and not is_cross) else 0

    # ---- GQA expansion ----
    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

    # ---- Local attention (V2-FIX-03) ----
    local_out = _local_window_attention_v2(
        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,
    )  # (B, H, N, D)

    # ---- Global attention (V2-FIX-08) ----
    if not is_cross:
        if self.prune_gate is not None:
            keep = torch.sigmoid(self.prune_gate(hidden_states))  # (B, N, H)
            # V2-FIX-06: expand across GQA heads
            if self.kv_repeat > 1:
                keep_kv = keep[:, :, : self.num_kv_heads]
            else:
                keep_kv = keep
            Kg = K_cat * keep_kv.transpose(1, 2).unsqueeze(-1)
            Vg = V_cat * keep_kv.transpose(1, 2).unsqueeze(-1)
        else:
            Kg, Vg = K_cat, V_cat

        if self.kv_repeat > 1:
            Kg = repeat_kv(Kg, self.kv_repeat)
            Vg = repeat_kv(Vg, self.kv_repeat)

        global_out = _global_linear_attention_v2(
            Q,
            Kg,
            Vg,
            token_valid=token_valid,
            is_decoder=self.is_decoder,
        )  # (B, H, N, D)

        alpha = torch.sigmoid(self.fusion_gate(hidden_states)).unsqueeze(-1)  # (B, N, H, 1)
        alpha = alpha.transpose(1, 2)  # (B, H, N, 1)
        fused = alpha * local_out + (1.0 - alpha) * global_out
    else:
        # Cross-attention: local only (global over encoder is questionable)
        fused = local_out

    # ---- head_mask (V2-FIX-02) ----
    if head_mask is not None:
        if head_mask.dim() == 1:
            head_mask = head_mask.view(1, -1, 1, 1)
        elif head_mask.dim() == 2:
            head_mask = head_mask.unsqueeze(-1).unsqueeze(-1)  # (B, H, 1, 1)
        elif head_mask.dim() == 3:
            head_mask = head_mask[:, :, :1].unsqueeze(-1)  # (B, H, 1, 1)
        fused = fused * head_mask.to(fused.dtype)

    # ---- Reshape to (B, N, H*D) ----
    out = fused.transpose(1, 2).contiguous().view(B, N, self.all_head_size)
    out = torch.nan_to_num(out, nan=0.0, posinf=1e4, neginf=-1e4)

    if self.is_nested_encoder:
        output = out
    else:
        output = self.out_proj(out)
        output = self.dropout(output)
    output = torch.nan_to_num(output, nan=0.0, posinf=1e4, neginf=-1e4)
    return output.to(hidden_states.dtype), None

Must return (output, attention_weights_or_None).

def get_ortho_loss(self) ‑> torch.Tensor
Expand source code
def get_ortho_loss(self) -> torch.Tensor:
    return torch.zeros((), device=self.out_proj.up.weight.device)
class AegisAttentionV4 (config: Any = None,
pretrained_attn: torch.nn.modules.module.Module | None = None,
is_decoder: bool | None = None,
**kwargs: Any)
Expand source code
@ATTENTION_REGISTRY.register("aegis_v2", supports_causal=True, supports_gqa=True)
@ATTENTION_REGISTRY.register("aegis_v1", supports_causal=True, supports_gqa=True)
@ATTENTION_REGISTRY.register("aegis_v3", supports_causal=True, supports_gqa=True)
@ATTENTION_REGISTRY.register("aegis_v4", supports_causal=True, supports_gqa=True)
@ATTENTION_REGISTRY.register("aegis_v1_module", supports_causal=True, supports_gqa=True)
class AegisAttentionV2(BaseAttention):
    supports_causal: bool = True
    supports_gqa: bool = True
    is_custom_attention: bool = True

    def __init__(
        self,
        config: Any = None,
        pretrained_attn: nn.Module | None = None,
        is_decoder: bool | None = None,
        **kwargs: Any,
    ) -> None:
        if config is None or not isinstance(config, AegisConfigV2):
            h_size = (
                kwargs.get(
                    "hidden_size",
                    getattr(
                        config,
                        "hidden_size",
                        getattr(config, "d_model", getattr(config, "dim", 768)),
                    ),
                )
                if config is not None
                else 768
            )
            n_heads = (
                kwargs.get(
                    "num_heads",
                    getattr(config, "num_attention_heads", getattr(config, "num_heads", 12)),
                )
                if config is not None
                else 12
            )
            h_dim = (
                kwargs.get(
                    "head_dim",
                    getattr(
                        config,
                        "head_dim",
                        getattr(config, "d_kv", h_size // max(1, n_heads)),
                    ),
                )
                if config is not None
                else h_size // max(1, n_heads)
            )
            n_kv_heads = (
                kwargs.get(
                    "num_key_value_heads",
                    getattr(
                        config,
                        "num_key_value_heads",
                        getattr(config, "num_kv_heads", n_heads),
                    ),
                )
                if config is not None
                else n_heads
            )

            # V2-FIX-01: robust decoder detection
            dec = _detect_decoder(config, is_decoder, pretrained_attn)

            config = AegisConfigV2(
                hidden_size=h_size,
                num_attention_heads=n_heads,
                num_heads=n_heads,
                num_key_value_heads=n_kv_heads,
                head_dim=h_dim,
                local_window=(
                    kwargs.get(
                        "local_window",
                        getattr(config, "block_size", getattr(config, "local_window", 64)),
                    )
                    if config is not None
                    else 64
                ),
                rank=(
                    kwargs.pop("rank", getattr(config, "lora_r", getattr(config, "rank", 64)))
                    if config is not None
                    else 64
                ),
                kernel_feature_dim=(
                    getattr(
                        config,
                        "rff_features",
                        getattr(config, "kernel_feature_dim", 128),
                    )
                    if config is not None
                    else 128
                ),
                dropout=(
                    kwargs.get("dropout", getattr(config, "dropout", 0.1))
                    if config is not None
                    else 0.1
                ),
                use_adaptive_pruning=(
                    getattr(config, "use_gate", True) if config is not None else True
                ),
                gate_init_bias=(
                    getattr(config, "gate_init_bias", 1.8) if config is not None else 1.8
                ),
                is_decoder=dec,
            )

        super().__init__(config)
        self.config = config
        self.hidden_size: int = config.hidden_size
        self.num_heads: int = config.num_attention_heads
        self.head_dim: int = config.head_dim
        self.num_kv_heads: int = getattr(config, "num_key_value_heads", self.num_heads)
        if self.num_heads % max(1, self.num_kv_heads) != 0:
            raise ValueError(
                f"num_heads ({self.num_heads}) must be divisible by "
                f"num_kv_heads ({self.num_kv_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 = config.local_window
        self.rank: int = config.rank
        self.kernel_dim: int = config.kernel_feature_dim
        self.dropout = nn.Dropout(config.dropout)
        self.is_decoder: bool = getattr(config, "is_decoder", False)
        self.is_nested_encoder: bool = False

        self.q_proj = FactorisedLinear(self.hidden_size, self.all_head_size, rank=self.rank)
        self.k_proj = FactorisedLinear(self.hidden_size, self.all_kv_head_size, rank=self.rank)
        self.v_proj = FactorisedLinear(self.hidden_size, self.all_kv_head_size, rank=self.rank)
        self.out_proj = FactorisedLinear(self.all_head_size, self.hidden_size, rank=self.rank)

        if config.use_adaptive_pruning:
            self.prune_gate: nn.Sequential | None = nn.Sequential(
                nn.Linear(self.hidden_size, max(1, self.hidden_size // 4)),
                nn.ReLU(),
                nn.Linear(max(1, self.hidden_size // 4), self.num_heads),
            )
        else:
            self.prune_gate = None

        self.fusion_gate = nn.Linear(self.hidden_size, self.num_heads)
        self.fusion_gate.bias.data.fill_(config.gate_init_bias)

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

    # ------------------------------------------------------------------
    # Aliases
    # ------------------------------------------------------------------
    @property
    def q_proj1(self):
        return self.q_proj.down

    @property
    def q_proj2(self):
        return self.q_proj.up

    @property
    def k_proj1(self):
        return self.k_proj.down

    @property
    def k_proj2(self):
        return self.k_proj.up

    @property
    def v_proj1(self):
        return self.v_proj.down

    @property
    def v_proj2(self):
        return self.v_proj.up

    @property
    def out_proj1(self):
        return self.out_proj.down

    @property
    def out_proj2(self):
        return self.out_proj.up

    @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 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 dense(self):
        return self.out_proj

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

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

    # ------------------------------------------------------------------
    # Weight transfer (same logic as V5)
    # ------------------------------------------------------------------
    def _copy_pretrained(self, old_attn: nn.Module) -> None:
        self.copy_weights(old_attn)

    def copy_weights(self, old_attn: Any) -> None:
        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),
        }

        for proj in ["q", "k", "v", "o"]:
            try:
                w, b = UniversalAdapter.get_weight_bias(old_attn, proj)
                if w is not None:
                    extracted[proj] = (w, b)
            except Exception as e:
                logger.debug("UniversalAdapter fallback failed: %s", e)

        # GPT-2 fused c_attn (Conv1D)
        if hasattr(old_attn, "c_attn") and any(extracted[k][0] is None for k in ("q", "k", "v")):
            c_attn_mod = old_attn.c_attn
            base_c = getattr(c_attn_mod, "base_layer", c_attn_mod)
            weight = getattr(base_c, "weight", getattr(c_attn_mod, "weight", None))
            if weight is not None:
                weight = weight.data.clone()
                is_conv1d = (
                    (c_attn_mod.__class__.__name__ == "Conv1D")
                    or (base_c.__class__.__name__ == "Conv1D")
                    or hasattr(c_attn_mod, "nf")
                    or hasattr(base_c, "nf")
                )
                if is_conv1d or (weight.dim() == 2 and weight.shape[1] == 3 * self.hidden_size):
                    weight = weight.t().contiguous()
                bias = getattr(base_c, "bias", getattr(c_attn_mod, "bias", None))
                bd = bias.data if bias is not None else None
                split = weight.shape[0] // 3
                extracted["q"] = (
                    weight[:split, :],
                    bd[:split] if bd is not None else None,
                )
                extracted["k"] = (
                    weight[split : 2 * split, :],
                    bd[split : 2 * split] if bd is not None else None,
                )
                extracted["v"] = (
                    weight[2 * split :, :],
                    bd[2 * split :] if bd is not None else None,
                )

        if hasattr(old_attn, "c_proj") and extracted["o"][0] is None:
            c_proj_mod = old_attn.c_proj
            base_p = getattr(c_proj_mod, "base_layer", c_proj_mod)
            w_o = getattr(base_p, "weight", getattr(c_proj_mod, "weight", None))
            if w_o is not None:
                w_o = w_o.data.clone()
                is_conv1d = (
                    (c_proj_mod.__class__.__name__ == "Conv1D")
                    or (base_p.__class__.__name__ == "Conv1D")
                    or hasattr(c_proj_mod, "nf")
                    or hasattr(base_p, "nf")
                )
                if is_conv1d or (
                    w_o.dim() == 2
                    and w_o.shape[0] != self.hidden_size
                    and w_o.shape[1] == self.hidden_size
                ):
                    w_o = w_o.t().contiguous()
                b_o = getattr(base_p, "bias", getattr(c_proj_mod, "bias", None))
                extracted["o"] = (w_o, b_o.data if b_o is not None else None)

        # BERT / RoBERTa nested encoder
        if hasattr(old_attn, "self") and any(extracted[k][0] is None for k in ("q", "k", "v")):
            s = old_attn.self
            if hasattr(s, "query"):
                extracted["q"] = (s.query.weight.data, getattr(s.query, "bias", None))
            if hasattr(s, "key"):
                extracted["k"] = (s.key.weight.data, getattr(s.key, "bias", None))
            if hasattr(s, "value"):
                extracted["v"] = (s.value.weight.data, getattr(s.value, "bias", None))

        if (
            hasattr(old_attn, "output")
            and hasattr(old_attn.output, "dense")
            and extracted["o"][0] is None
        ):
            dense = old_attn.output.dense
            extracted["o"] = (dense.weight.data, getattr(dense, "bias", None))

        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 proj, names in named_map.items():
            if extracted[proj][0] is None:
                for name in names:
                    if hasattr(old_attn, name):
                        layer = getattr(old_attn, name)
                        if hasattr(layer, "weight"):
                            extracted[proj] = (
                                layer.weight.data,
                                getattr(layer, "bias", None),
                            )
                            break

        cls_name = old_attn.__class__.__name__
        has_out_proj = any(
            hasattr(old_attn, a)
            for a in (
                "dense",
                "out_proj",
                "c_proj",
                "out_lin",
                "o_proj",
                "wo",
                "o",
                "proj",
            )
        )
        if (
            hasattr(old_attn, "out_lin")
            or hasattr(old_attn, "c_proj")
            or hasattr(old_attn, "o_proj")
        ):
            self.is_nested_encoder = False
        elif (
            hasattr(old_attn, "self")
            and hasattr(old_attn.self, "query")
            and hasattr(old_attn, "output")
            and hasattr(old_attn.output, "dense")
        ) or (
            cls_name
            in (
                "BertSelfAttention",
                "RobertaSelfAttention",
                "XLMRobertaSelfAttention",
                "ElectraSelfAttention",
            )
            or "RobertaSelfAttention" in str(type(old_attn))
            or "BertSelfAttention" in str(type(old_attn))
            or (cls_name.endswith("SelfAttention") and not has_out_proj)
        ):
            self.is_nested_encoder = True
        else:
            self.is_nested_encoder = False

        missing = [k for k in ("q", "k", "v") if extracted[k][0] is None]
        if not self.is_nested_encoder and extracted["o"][0] is None:
            missing.append("o")
        if missing:
            raise RuntimeError(
                f"AegisAttentionV2.copy_weights: missing {missing} from "
                f"{old_attn.__class__.__name__}. Refusing random init."
            )

        for proj_key in ("q", "k", "v"):
            w = extracted[proj_key][0]
            if w is not None:
                norm = float(torch.linalg.norm(w.float()).item())
                if norm < 1e-4:
                    logger.warning(
                        "AegisAttentionV2: weight for '%s' has near-zero norm (%.2e).",
                        proj_key,
                        norm,
                    )

        def _orient(w: torch.Tensor, in_dim: int) -> torch.Tensor:
            if w.dim() == 2 and w.shape[1] != in_dim and w.shape[0] == in_dim:
                return w.t().contiguous()
            return w

        with torch.no_grad():
            for proj_key, target in (
                ("q", self.q_proj),
                ("k", self.k_proj),
                ("v", self.v_proj),
                ("o", self.out_proj),
            ):
                w, b = extracted[proj_key]
                if w is not None:
                    in_dim = self.all_head_size if proj_key == "o" else self.hidden_size
                    target.decompose_from_weight(_orient(w, in_dim), b)
                elif proj_key == "o" and self.is_nested_encoder:
                    eye_w = torch.eye(
                        self.all_head_size,
                        self.all_head_size,
                        device=self.out_proj.up.weight.device,
                        dtype=self.out_proj.up.weight.dtype,
                    )
                    target.decompose_from_weight(eye_w, None)

        if hasattr(old_attn, "is_decoder"):
            self.is_decoder = bool(old_attn.is_decoder)
        elif hasattr(old_attn, "c_attn") or "gpt" in old_attn.__class__.__name__.lower():
            self.is_decoder = True

    # ------------------------------------------------------------------
    # V2-FIX-07: safe from_distilbert_pretrained
    # ------------------------------------------------------------------
    @classmethod
    def from_distilbert_pretrained(
        cls, old_attn: nn.Module, model_config: Any | None = None, **kwargs: Any
    ) -> "AegisAttentionV2":
        if model_config is None:
            out_mod = getattr(old_attn, "out_lin", None) or getattr(old_attn, "dense", None)
            if out_mod is None or not hasattr(out_mod, "out_features"):
                raise ValueError(
                    "from_distilbert_pretrained: could not infer hidden_size "
                    "from out_lin or dense. Pass model_config explicitly."
                )

            class DummyConfig:
                pass

            model_config = DummyConfig()
            model_config.hidden_size = out_mod.out_features
            n_heads = getattr(
                old_attn,
                "n_heads",
                getattr(
                    old_attn,
                    "num_attention_heads",
                    getattr(old_attn, "num_heads", None),
                ),
            )
            if n_heads is None:
                n_heads = max(1, model_config.hidden_size // 64)
            model_config.num_attention_heads = n_heads
            model_config.num_key_value_heads = n_heads
            model_config.head_dim = model_config.hidden_size // n_heads
        return cls(config=model_config, pretrained_attn=old_attn, **kwargs)

    # ------------------------------------------------------------------
    # V2-FIX-05: unified mask handling
    # ------------------------------------------------------------------
    @staticmethod
    def _prepare_valid_mask(
        attention_mask: torch.Tensor | None,
        B: int,
        N_kv: int,
        device: torch.device,
    ) -> torch.Tensor | None:
        """Return boolean (B, N_kv): True = key is valid. None if no mask."""
        if attention_mask is None:
            return None
        if attention_mask.dim() == 2:
            if attention_mask.dtype == torch.bool:
                return attention_mask
            if attention_mask.dtype.is_floating_point:
                return attention_mask > -1e4
            return attention_mask > 0
        if attention_mask.dim() == 4:
            if attention_mask.dtype == torch.bool:
                v = attention_mask
            elif attention_mask.dtype.is_floating_point:
                v = attention_mask > -1e4
            else:
                v = attention_mask > 0
            # (B, 1, N_q, N_kv) -> OR over query dim -> (B, N_kv)
            return v.any(dim=-2).squeeze(1)
        raise ValueError(f"Unsupported attention_mask dim: {attention_mask.dim()}")

    # ------------------------------------------------------------------
    # V2-FIX-02 + V2-FIX-09: 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,
        *args: Any,
        **kwargs: Any,
    ) -> tuple[torch.Tensor, torch.Tensor | None]:
        B, N, _ = hidden_states.shape
        H, D = self.num_heads, self.head_dim
        Hkv = self.num_kv_heads
        device = hidden_states.device

        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]

        # ---- Mask (V2-FIX-05) ----
        eff_mask = encoder_attention_mask if is_cross else attention_mask
        token_valid = self._prepare_valid_mask(eff_mask, B, N_kv_new, device)

        # ---- Projections ----
        Q = self.q_proj(hidden_states).view(B, N, H, D).transpose(1, 2).contiguous()
        K_new = self.k_proj(kv_input).view(B, N_kv_new, Hkv, D).transpose(1, 2).contiguous()
        V_new = self.v_proj(kv_input).view(B, N_kv_new, Hkv, D).transpose(1, 2).contiguous()

        # ---- RoPE with past offset ----
        if position_embeddings is not None and not is_cross:
            cos, sin = position_embeddings
            past_len_rope = 0
            if past_key_value is not None:
                if isinstance(past_key_value, (tuple, list)) and len(past_key_value) >= 1:
                    past_len_rope = past_key_value[0].shape[-2]
                elif hasattr(past_key_value, "key_cache") and past_key_value.key_cache:
                    past_len_rope = past_key_value.key_cache[0].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 concat ----
        past_k = past_v = None
        if past_key_value is not None and not is_cross:
            if isinstance(past_key_value, (tuple, list)) and len(past_key_value) >= 2:
                past_k, past_v = past_key_value[0], past_key_value[1]
            elif hasattr(past_key_value, "key_cache") and past_key_value.key_cache:
                idx = min(getattr(self, "layer_idx", 0), len(past_key_value.key_cache) - 1)
                past_k, past_v = (
                    past_key_value.key_cache[idx],
                    past_key_value.value_cache[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)
        else:
            K_cat = K_new
            V_cat = V_new

        if past_key_value is not None and hasattr(past_key_value, "update") and not is_cross:
            idx = getattr(self, "layer_idx", 0)
            past_key_value.update(K_new, V_new, idx)
        else:
            pass

        N_kv = K_cat.shape[2]
        past_len = N_kv - N if (self.is_decoder and not is_cross) else 0

        # ---- GQA expansion ----
        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

        # ---- Local attention (V2-FIX-03) ----
        local_out = _local_window_attention_v2(
            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,
        )  # (B, H, N, D)

        # ---- Global attention (V2-FIX-08) ----
        if not is_cross:
            if self.prune_gate is not None:
                keep = torch.sigmoid(self.prune_gate(hidden_states))  # (B, N, H)
                # V2-FIX-06: expand across GQA heads
                if self.kv_repeat > 1:
                    keep_kv = keep[:, :, : self.num_kv_heads]
                else:
                    keep_kv = keep
                Kg = K_cat * keep_kv.transpose(1, 2).unsqueeze(-1)
                Vg = V_cat * keep_kv.transpose(1, 2).unsqueeze(-1)
            else:
                Kg, Vg = K_cat, V_cat

            if self.kv_repeat > 1:
                Kg = repeat_kv(Kg, self.kv_repeat)
                Vg = repeat_kv(Vg, self.kv_repeat)

            global_out = _global_linear_attention_v2(
                Q,
                Kg,
                Vg,
                token_valid=token_valid,
                is_decoder=self.is_decoder,
            )  # (B, H, N, D)

            alpha = torch.sigmoid(self.fusion_gate(hidden_states)).unsqueeze(-1)  # (B, N, H, 1)
            alpha = alpha.transpose(1, 2)  # (B, H, N, 1)
            fused = alpha * local_out + (1.0 - alpha) * global_out
        else:
            # Cross-attention: local only (global over encoder is questionable)
            fused = local_out

        # ---- head_mask (V2-FIX-02) ----
        if head_mask is not None:
            if head_mask.dim() == 1:
                head_mask = head_mask.view(1, -1, 1, 1)
            elif head_mask.dim() == 2:
                head_mask = head_mask.unsqueeze(-1).unsqueeze(-1)  # (B, H, 1, 1)
            elif head_mask.dim() == 3:
                head_mask = head_mask[:, :, :1].unsqueeze(-1)  # (B, H, 1, 1)
            fused = fused * head_mask.to(fused.dtype)

        # ---- Reshape to (B, N, H*D) ----
        out = fused.transpose(1, 2).contiguous().view(B, N, self.all_head_size)
        out = torch.nan_to_num(out, nan=0.0, posinf=1e4, neginf=-1e4)

        if self.is_nested_encoder:
            output = out
        else:
            output = self.out_proj(out)
            output = self.dropout(output)
        output = torch.nan_to_num(output, nan=0.0, posinf=1e4, neginf=-1e4)
        return output.to(hidden_states.dtype), None

    def get_ortho_loss(self) -> torch.Tensor:
        return torch.zeros((), device=self.out_proj.up.weight.device)

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.

Static methods

def from_distilbert_pretrained(old_attn: torch.nn.modules.module.Module,
model_config: Any | None = None,
**kwargs: Any) ‑> AegisAttentionV2

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 k_proj1
Expand source code
@property
def k_proj1(self):
    return self.k_proj.down
prop k_proj2
Expand source code
@property
def k_proj2(self):
    return self.k_proj.up
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 out_proj1
Expand source code
@property
def out_proj1(self):
    return self.out_proj.down
prop out_proj2
Expand source code
@property
def out_proj2(self):
    return self.out_proj.up
prop q_lin
Expand source code
@property
def q_lin(self):
    return self.q_proj
prop q_proj1
Expand source code
@property
def q_proj1(self):
    return self.q_proj.down
prop q_proj2
Expand source code
@property
def q_proj2(self):
    return self.q_proj.up
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 v_proj1
Expand source code
@property
def v_proj1(self):
    return self.v_proj.down
prop v_proj2
Expand source code
@property
def v_proj2(self):
    return self.v_proj.up
prop value
Expand source code
@property
def value(self):
    return self.v_proj

Methods

def get_ortho_loss(self) ‑> torch.Tensor
Expand source code
def get_ortho_loss(self) -> torch.Tensor:
    return torch.zeros((), device=self.out_proj.up.weight.device)

Inherited members

class FactorisedLinear (in_features: int,
out_features: int,
rank: int = 384,
bias: bool = True,
lora_rank: int = 16,
lora_alpha: float = 32.0,
energy: float = 0.95,
**kwargs: Any)
Expand source code
class FactorisedLinear(nn.Module):
    is_custom_attention: bool = True

    def __init__(
        self,
        in_features: int,
        out_features: int,
        rank: int = 384,
        bias: bool = True,
        lora_rank: int = 16,
        lora_alpha: float = 32.0,
        energy: float = 0.95,
        **kwargs: Any,
    ) -> None:
        super().__init__()
        self.in_features = in_features
        self.out_features = out_features
        self.rank = min(rank, in_features, out_features)
        # V2-FIX-10: alias so framework FLOPs detector sees the rank.
        self.svd_rank = self.rank
        self.energy = energy
        self.lora_rank = lora_rank
        self.lora_alpha = lora_alpha
        self.lora_scaling = (lora_alpha / lora_rank) if lora_rank > 0 else 1.0

        self.down = nn.Linear(in_features, self.rank, bias=False)
        self.up = nn.Linear(self.rank, out_features, bias=bias)

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

        self.reset_parameters()

    def reset_parameters(self) -> None:
        nn.init.orthogonal_(self.down.weight)
        nn.init.orthogonal_(self.up.weight)
        if self.up.bias is not None:
            nn.init.zeros_(self.up.bias)
        if self.lora_rank > 0 and self.lora_A is not None:
            nn.init.kaiming_uniform_(self.lora_A, a=math.sqrt(5))
            self.lora_A.requires_grad = True
        if self.lora_rank > 0 and self.lora_B is not None:
            nn.init.zeros_(self.lora_B)
            self.lora_B.requires_grad = True

    def decompose_from_weight(
        self,
        weight: torch.Tensor,
        bias: torch.Tensor | None = None,
        energy: float | None = None,
    ) -> None:
        with torch.no_grad():
            W = weight.float()
            if W.shape[0] < self.out_features and self.out_features % W.shape[0] == 0:
                rep = self.out_features // W.shape[0]
                W = W.repeat(rep, 1)
                if bias is not None:
                    bias = bias.repeat(rep)
            elif W.shape[0] > self.out_features and W.shape[0] % self.out_features == 0:
                rep = W.shape[0] // self.out_features
                W = W.view(self.out_features, rep, -1).mean(dim=1)
                if bias is not None:
                    bias = bias.view(self.out_features, rep).mean(dim=1)
            elif W.shape[0] != self.out_features or W.shape[1] != self.in_features:
                padded = torch.zeros(
                    self.out_features, self.in_features, device=W.device, dtype=W.dtype
                )
                r = min(self.out_features, W.shape[0])
                c = min(self.in_features, W.shape[1])
                padded[:r, :c] = W[:r, :c]
                W = padded

            U, S, Vt = torch.linalg.svd(W, full_matrices=False)
            S = torch.clamp(S, min=1e-6)
            target_energy = energy if energy is not None else self.energy
            total = (S**2).sum()
            cum = torch.cumsum(S**2, dim=0) / (total + 1e-5)
            calc_rank = int((cum < target_energy).sum().item()) + 1
            r = min(self.rank, calc_rank, U.size(1), Vt.size(0))
            r = max(1, r)

            A = U[:, :r] * S[:r].unsqueeze(0)
            B = Vt[:r, :]

            self.down.weight.data.zero_()
            self.up.weight.data.zero_()
            self.down.weight.data[:r, :] = B.to(self.down.weight.dtype)
            self.up.weight.data[:, :r] = A.to(self.up.weight.dtype)

            if bias is not None and self.up.bias is not None:
                n = min(len(self.up.bias), len(bias))
                self.up.bias.data[:n].copy_(bias[:n].to(self.up.bias.dtype))
            elif self.up.bias is not None:
                self.up.bias.data.zero_()

            self.freeze_base_weights()

    def freeze_base_weights(self) -> None:
        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 and self.lora_A is not None and self.lora_B is not None:
            self.lora_A.requires_grad = True
            self.lora_B.requires_grad = True

    @property
    def weight(self) -> torch.Tensor:
        base_w = torch.matmul(self.up.weight, self.down.weight)
        if self.lora_rank > 0 and self.lora_A is not None and self.lora_B is not None:
            return base_w + (self.lora_B @ self.lora_A) * self.lora_scaling
        return base_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_rank > 0 and self.lora_A is not None and self.lora_B 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) * self.lora_scaling
        return out.to(orig_dtype)

Base class for all neural network modules.

Your models should also subclass this class.

Modules can also contain other Modules, allowing them to be nested in a tree structure. You can assign the submodules as regular attributes::

import torch.nn as nn
import torch.nn.functional as F


class Model(nn.Module):
    def __init__(self) -> None:
        super().__init__()
        self.conv1 = nn.Conv2d(1, 20, 5)
        self.conv2 = nn.Conv2d(20, 20, 5)

    def forward(self, x):
        x = F.relu(self.conv1(x))
        return F.relu(self.conv2(x))

Submodules assigned in this way will be registered, and will also have their parameters converted when you call :meth:to, etc.

Note

As per the example above, an __init__() call to the parent class must be made before assignment on the child.

:ivar training: Boolean represents whether this module is in training or evaluation mode. :vartype training: bool

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 weight : torch.Tensor
Expand source code
@property
def weight(self) -> torch.Tensor:
    base_w = torch.matmul(self.up.weight, self.down.weight)
    if self.lora_rank > 0 and self.lora_A is not None and self.lora_B is not None:
        return base_w + (self.lora_B @ self.lora_A) * self.lora_scaling
    return base_w

Methods

def decompose_from_weight(self,
weight: torch.Tensor,
bias: torch.Tensor | None = None,
energy: float | None = None) ‑> None
Expand source code
def decompose_from_weight(
    self,
    weight: torch.Tensor,
    bias: torch.Tensor | None = None,
    energy: float | None = None,
) -> None:
    with torch.no_grad():
        W = weight.float()
        if W.shape[0] < self.out_features and self.out_features % W.shape[0] == 0:
            rep = self.out_features // W.shape[0]
            W = W.repeat(rep, 1)
            if bias is not None:
                bias = bias.repeat(rep)
        elif W.shape[0] > self.out_features and W.shape[0] % self.out_features == 0:
            rep = W.shape[0] // self.out_features
            W = W.view(self.out_features, rep, -1).mean(dim=1)
            if bias is not None:
                bias = bias.view(self.out_features, rep).mean(dim=1)
        elif W.shape[0] != self.out_features or W.shape[1] != self.in_features:
            padded = torch.zeros(
                self.out_features, self.in_features, device=W.device, dtype=W.dtype
            )
            r = min(self.out_features, W.shape[0])
            c = min(self.in_features, W.shape[1])
            padded[:r, :c] = W[:r, :c]
            W = padded

        U, S, Vt = torch.linalg.svd(W, full_matrices=False)
        S = torch.clamp(S, min=1e-6)
        target_energy = energy if energy is not None else self.energy
        total = (S**2).sum()
        cum = torch.cumsum(S**2, dim=0) / (total + 1e-5)
        calc_rank = int((cum < target_energy).sum().item()) + 1
        r = min(self.rank, calc_rank, U.size(1), Vt.size(0))
        r = max(1, r)

        A = U[:, :r] * S[:r].unsqueeze(0)
        B = Vt[:r, :]

        self.down.weight.data.zero_()
        self.up.weight.data.zero_()
        self.down.weight.data[:r, :] = B.to(self.down.weight.dtype)
        self.up.weight.data[:, :r] = A.to(self.up.weight.dtype)

        if bias is not None and self.up.bias is not None:
            n = min(len(self.up.bias), len(bias))
            self.up.bias.data[:n].copy_(bias[:n].to(self.up.bias.dtype))
        elif self.up.bias is not None:
            self.up.bias.data.zero_()

        self.freeze_base_weights()
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_rank > 0 and self.lora_A is not None and self.lora_B 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) * self.lora_scaling
    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 freeze_base_weights(self) ‑> None
Expand source code
def freeze_base_weights(self) -> None:
    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 and self.lora_A is not None and self.lora_B is not None:
        self.lora_A.requires_grad = True
        self.lora_B.requires_grad = True
def reset_parameters(self) ‑> None
Expand source code
def reset_parameters(self) -> None:
    nn.init.orthogonal_(self.down.weight)
    nn.init.orthogonal_(self.up.weight)
    if self.up.bias is not None:
        nn.init.zeros_(self.up.bias)
    if self.lora_rank > 0 and self.lora_A is not None:
        nn.init.kaiming_uniform_(self.lora_A, a=math.sqrt(5))
        self.lora_A.requires_grad = True
    if self.lora_rank > 0 and self.lora_B is not None:
        nn.init.zeros_(self.lora_B)
        self.lora_B.requires_grad = True