Text Generation
Transformers
Safetensors
PyTorch
English
neollm
causal-lm
research
bf16
attention
normalization
conversational
custom_code
Instructions to use KitsuVp/NeoLLM with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use KitsuVp/NeoLLM with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="KitsuVp/NeoLLM", trust_remote_code=True) messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# pip install -U transformers accelerate # Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("KitsuVp/NeoLLM", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use KitsuVp/NeoLLM with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "KitsuVp/NeoLLM" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "KitsuVp/NeoLLM", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/KitsuVp/NeoLLM
- SGLang
How to use KitsuVp/NeoLLM with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "KitsuVp/NeoLLM" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "KitsuVp/NeoLLM", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "KitsuVp/NeoLLM" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "KitsuVp/NeoLLM", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use KitsuVp/NeoLLM with Docker Model Runner:
docker model run hf.co/KitsuVp/NeoLLM
Update modeling_neollm.py
Browse files- modeling_neollm.py +272 -26
modeling_neollm.py
CHANGED
|
@@ -5,8 +5,9 @@ full attention augmented with optional Momentum, MEA, and LUCID operators,
|
|
| 5 |
Gated Attention (Qiu et al., 2025) combined with Affine-Scaled Attention
|
| 6 |
(Bae et al., 2026), an optional Leviathan continuous token embedding
|
| 7 |
generator, an optional Leviathan-JTok-M token-indexed modulation module,
|
| 8 |
-
optional Spelling Bee Embeddings (Rabe et al., 2026),
|
| 9 |
-
Re-Positioning (Li et al., 2026)
|
|
|
|
| 10 |
|
| 11 |
Attention stack (orthogonal, all active simultaneously when enabled):
|
| 12 |
1. Gated Attention (use_gated_attention implicit via q_proj gate chunk):
|
|
@@ -55,6 +56,8 @@ References:
|
|
| 55 |
Embeddings for Language Modeling." arXiv:2601.18030.
|
| 56 |
Context Re-Positioning: Li, Zhao, Cai & Sproat (2026). "REPO: Language
|
| 57 |
Models with Context Re-Positioning." arXiv:2512.14391.
|
|
|
|
|
|
|
| 58 |
"""
|
| 59 |
|
| 60 |
import math
|
|
@@ -243,6 +246,28 @@ class REPOAnalysis:
|
|
| 243 |
r_repr: Optional[torch.Tensor] = None # r [B, S, d_p] — shared repr
|
| 244 |
|
| 245 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 246 |
@dataclass
|
| 247 |
class AttentionAnalysis:
|
| 248 |
"""
|
|
@@ -325,6 +350,9 @@ class AttentionAnalysis:
|
|
| 325 |
# ── REPO position prediction (conditional on use_repo) ────────────
|
| 326 |
repo: Optional["REPOAnalysis"] = None # None when layer_idx < repo_start_layer
|
| 327 |
|
|
|
|
|
|
|
|
|
|
| 328 |
# ── IHA pseudo-head mixing (conditional on use_iha) ──────────────
|
| 329 |
iha: Optional["IHAAnalysis"] = None # None when use_iha=False
|
| 330 |
|
|
@@ -2272,13 +2300,201 @@ def _apply_repo_rope(
|
|
| 2272 |
return q_embed, k_embed
|
| 2273 |
|
| 2274 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2275 |
class NeoLLMAttention(nn.Module):
|
| 2276 |
"""
|
| 2277 |
Full attention with FANformer, SeeDNorm, ResFormer, Learnable Multipliers,
|
| 2278 |
optional Momentum, MEA head-level composition, optional LUCID preconditioning,
|
| 2279 |
optional Affine-Scaled Attention, optional Exclusive Self Attention,
|
| 2280 |
-
optional Directional Routing (Taylor, 2026),
|
| 2281 |
-
Re-Positioning (Li et al., 2026)
|
|
|
|
| 2282 |
|
| 2283 |
Directional Routing inserts at position C — post-XSA, pre-reshape — where
|
| 2284 |
the output is already normalized (MEAHeadSeeDNorm) and has auto-position
|
|
@@ -2286,8 +2502,8 @@ class NeoLLMAttention(nn.Module):
|
|
| 2286 |
orthogonal to the self-position already cleaned by XSA.
|
| 2287 |
|
| 2288 |
Pipeline (all active simultaneously when enabled):
|
| 2289 |
-
FANLayer → q_proj(gate) → q_norm/k_norm → REPO/RoPE
|
| 2290 |
-
→ MEA(K,V) → LUCID(V) → v_ref → Affine-Scaled SDPA
|
| 2291 |
→ MEAHeadSeeDNorm → XSA → Directional Routing → reshape
|
| 2292 |
→ o_proj · sigmoid(gate) → dropout
|
| 2293 |
|
|
@@ -2495,15 +2711,19 @@ class NeoLLMAttention(nn.Module):
|
|
| 2495 |
self.direction_vecs = None
|
| 2496 |
self.direction_router = None
|
| 2497 |
|
| 2498 |
-
# ── Context Re-Positioning
|
| 2499 |
-
#
|
| 2500 |
-
#
|
| 2501 |
-
#
|
| 2502 |
-
|
|
|
|
|
|
|
| 2503 |
self.use_repo = (
|
| 2504 |
-
|
| 2505 |
-
and layer_idx >=
|
| 2506 |
)
|
|
|
|
|
|
|
| 2507 |
if self.use_repo:
|
| 2508 |
_d_p = getattr(config, "repo_d_p", config.hidden_size // 8)
|
| 2509 |
self.repo_module = REPOModule(
|
|
@@ -2514,6 +2734,17 @@ class NeoLLMAttention(nn.Module):
|
|
| 2514 |
else:
|
| 2515 |
self.repo_module = None
|
| 2516 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2517 |
# ── Interleaved Head Attention (Duvvuri et al., 2026) ─────────────────
|
| 2518 |
# Implementa cross-head mixing antes de RoPE: cada pseudo-head h,p
|
| 2519 |
# es combinación lineal aprendida de los H input-heads originales.
|
|
@@ -3294,24 +3525,34 @@ class NeoLLMAttention(nn.Module):
|
|
| 3294 |
# no meta-tensor issue on lm_eval / to(device) paths.
|
| 3295 |
# (Li et al., 2026, §3.2 — Eq. 6–7)
|
| 3296 |
repo_a = attn_analysis.repo if attn_analysis is not None else None
|
|
|
|
| 3297 |
z = self.repo_module(hidden_states, repo_analysis=repo_a) # [B, H, S]
|
|
|
|
|
|
|
| 3298 |
inv_freq, attn_scaling = repo_rope_args
|
| 3299 |
if _iha_seq_expand:
|
| 3300 |
-
# IHA seq-expand + REPO: each real position s has P virtual slots.
|
| 3301 |
-
# Assign continuous
|
| 3302 |
-
#
|
| 3303 |
-
# REPO's relative ordering across tokens.
|
| 3304 |
-
# _apply_repo_rope handles the flat S·P sequence correctly when
|
| 3305 |
-
# H_q_eff == H_repo (P_derived=1 inside the function).
|
| 3306 |
P = self.iha_P
|
| 3307 |
p_offsets = torch.arange(P, device=z.device, dtype=z.dtype)
|
| 3308 |
# z[B,H,S] → [B,H,S,1]*P + [1,1,1,P] → [B,H,S,P] → [B,H,S·P]
|
| 3309 |
z_expanded = (z.unsqueeze(-1) * P + p_offsets.view(1, 1, 1, P)
|
| 3310 |
).reshape(z.shape[0], z.shape[1], _S_orig * P)
|
| 3311 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3312 |
else:
|
| 3313 |
# Standard REPO path (P=1 or no IHA): z covers original S positions.
|
| 3314 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3315 |
elif _iha_seq_expand:
|
| 3316 |
# IHA seq-expand + standard integer RoPE:
|
| 3317 |
# Build interleaved positions pos_iha[s·P+p] = position_ids[s]·P + p
|
|
@@ -5115,18 +5356,22 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
|
|
| 5115 |
Called once per layer per forward when analysis is active.
|
| 5116 |
"""
|
| 5117 |
cfg = self.config
|
|
|
|
|
|
|
| 5118 |
_repo_active = (
|
| 5119 |
-
|
| 5120 |
and layer_idx >= getattr(cfg, "repo_start_layer", cfg.num_hidden_layers // 3)
|
| 5121 |
)
|
|
|
|
| 5122 |
_versatile = getattr(cfg, "use_versatile_ffn", False)
|
| 5123 |
return LayerAnalysis(
|
| 5124 |
seednorm_pre_attn = SeeDNormAnalysis(),
|
| 5125 |
seednorm_post_attn = SeeDNormAnalysis(),
|
| 5126 |
attention = AttentionAnalysis(
|
| 5127 |
-
fan
|
| 5128 |
-
hadamard
|
| 5129 |
-
repo
|
|
|
|
| 5130 |
),
|
| 5131 |
mlp = MLPAnalysis(
|
| 5132 |
fan = FANAnalysis() if not _versatile else None,
|
|
@@ -5250,9 +5495,10 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
|
|
| 5250 |
getattr(self.config, "use_iha", False)
|
| 5251 |
and int(getattr(self.config, "iha_num_pseudo_heads", 1)) > 1
|
| 5252 |
)
|
|
|
|
| 5253 |
repo_rope_args = (
|
| 5254 |
(self.rotary_emb.inv_freq, self.rotary_emb.attention_scaling)
|
| 5255 |
-
if (getattr(self.config, "use_repo", False) or _iha_needs_inv_freq)
|
| 5256 |
else None
|
| 5257 |
)
|
| 5258 |
|
|
|
|
| 5 |
Gated Attention (Qiu et al., 2025) combined with Affine-Scaled Attention
|
| 6 |
(Bae et al., 2026), an optional Leviathan continuous token embedding
|
| 7 |
generator, an optional Leviathan-JTok-M token-indexed modulation module,
|
| 8 |
+
optional Spelling Bee Embeddings (Rabe et al., 2026), optional Context
|
| 9 |
+
Re-Positioning (Li et al., 2026), and optional REPO-GRAPE contextual group
|
| 10 |
+
positioning (Li et al., 2026 + Zhang et al., 2026).
|
| 11 |
|
| 12 |
Attention stack (orthogonal, all active simultaneously when enabled):
|
| 13 |
1. Gated Attention (use_gated_attention implicit via q_proj gate chunk):
|
|
|
|
| 56 |
Embeddings for Language Modeling." arXiv:2601.18030.
|
| 57 |
Context Re-Positioning: Li, Zhao, Cai & Sproat (2026). "REPO: Language
|
| 58 |
Models with Context Re-Positioning." arXiv:2512.14391.
|
| 59 |
+
GRAPE: Zhang et al. (2026). "Group Representational Position Encoding."
|
| 60 |
+
arXiv:2512.07805 / ICLR 2026.
|
| 61 |
"""
|
| 62 |
|
| 63 |
import math
|
|
|
|
| 246 |
r_repr: Optional[torch.Tensor] = None # r [B, S, d_p] — shared repr
|
| 247 |
|
| 248 |
|
| 249 |
+
|
| 250 |
+
@dataclass
|
| 251 |
+
class RepoGrapeAnalysis:
|
| 252 |
+
"""
|
| 253 |
+
Internals of the REPO-GRAPE positional operator.
|
| 254 |
+
Populated only when config.use_repo_grape=True and the current layer is
|
| 255 |
+
at or above repo_start_layer.
|
| 256 |
+
|
| 257 |
+
Minimal operator kept from the proposal:
|
| 258 |
+
|
| 259 |
+
u_i^(h) = z_i^(h)
|
| 260 |
+
theta_{h,r} = inv_freq_r * exp(s_{h,r})
|
| 261 |
+
logit_ij += q_i^T exp((u_j-u_i) Omega_h) k_j / sqrt(d)
|
| 262 |
+
|
| 263 |
+
The implementation uses canonical commuting rotary planes, learns the
|
| 264 |
+
per-head/per-plane angular spectrum, rotates Q/K once, and then calls the
|
| 265 |
+
existing attention backend.
|
| 266 |
+
"""
|
| 267 |
+
positions: Optional[torch.Tensor] = None # [B,H,S] u_i = z_i
|
| 268 |
+
freq: Optional[torch.Tensor] = None # [H,rotary_dim/2] theta_{h,r}
|
| 269 |
+
|
| 270 |
+
|
| 271 |
@dataclass
|
| 272 |
class AttentionAnalysis:
|
| 273 |
"""
|
|
|
|
| 350 |
# ── REPO position prediction (conditional on use_repo) ────────────
|
| 351 |
repo: Optional["REPOAnalysis"] = None # None when layer_idx < repo_start_layer
|
| 352 |
|
| 353 |
+
# ── REPO-GRAPE contextual group positioning (conditional on use_repo_grape)
|
| 354 |
+
repo_grape: Optional["RepoGrapeAnalysis"] = None
|
| 355 |
+
|
| 356 |
# ── IHA pseudo-head mixing (conditional on use_iha) ──────────────
|
| 357 |
iha: Optional["IHAAnalysis"] = None # None when use_iha=False
|
| 358 |
|
|
|
|
| 2300 |
return q_embed, k_embed
|
| 2301 |
|
| 2302 |
|
| 2303 |
+
|
| 2304 |
+
class RepoGrapePositioning(nn.Module):
|
| 2305 |
+
r"""
|
| 2306 |
+
Minimal REPO-GRAPE-M positional operator.
|
| 2307 |
+
|
| 2308 |
+
This module intentionally keeps only the specific operator from the
|
| 2309 |
+
proposal:
|
| 2310 |
+
|
| 2311 |
+
1. REPO predicts contextual coordinates z_i^(h)=f_phi^(h)(h_i).
|
| 2312 |
+
2. REPO-GRAPE uses u_i^(h)=z_i^(h), with no extra position mode.
|
| 2313 |
+
3. GRAPE-M applies canonical commuting SO(d) rotary planes to Q/K.
|
| 2314 |
+
4. Each query head learns its own angular spectrum:
|
| 2315 |
+
|
| 2316 |
+
theta_{h,r}=inv_freq_r*exp(s_{h,r}).
|
| 2317 |
+
|
| 2318 |
+
Therefore
|
| 2319 |
+
|
| 2320 |
+
(G_h(u_i)q_i)^T(G_h(u_j)k_j)
|
| 2321 |
+
= q_i^T G_h(u_j-u_i) k_j,
|
| 2322 |
+
|
| 2323 |
+
preserving the relative GRAPE law while staying compatible with the
|
| 2324 |
+
existing attention backend.
|
| 2325 |
+
"""
|
| 2326 |
+
|
| 2327 |
+
def __init__(
|
| 2328 |
+
self,
|
| 2329 |
+
config: NeoLLMConfig,
|
| 2330 |
+
layer_idx: int,
|
| 2331 |
+
num_attention_heads: int,
|
| 2332 |
+
num_key_value_heads: int,
|
| 2333 |
+
head_dim: int,
|
| 2334 |
+
):
|
| 2335 |
+
super().__init__()
|
| 2336 |
+
self.config = config
|
| 2337 |
+
self.layer_idx = layer_idx
|
| 2338 |
+
self.num_attention_heads = int(num_attention_heads)
|
| 2339 |
+
self.num_key_value_heads = int(num_key_value_heads)
|
| 2340 |
+
self.head_dim = int(head_dim)
|
| 2341 |
+
self.max_rot_half = self.head_dim // 2
|
| 2342 |
+
|
| 2343 |
+
# The single REPO-GRAPE parameter: a learned log-scale over the
|
| 2344 |
+
# base RoPE/GRAPE frequencies, per query head and rotary plane.
|
| 2345 |
+
self.freq_log_scale = nn.Parameter(
|
| 2346 |
+
torch.zeros(self.num_attention_heads, self.max_rot_half, dtype=torch.float32),
|
| 2347 |
+
requires_grad=True,
|
| 2348 |
+
)
|
| 2349 |
+
|
| 2350 |
+
def _apply(self, fn):
|
| 2351 |
+
# Keep the small spectral parameters in fp32 under model.to(dtype).
|
| 2352 |
+
super()._apply(fn)
|
| 2353 |
+
p = self.freq_log_scale
|
| 2354 |
+
p.data = p.data.float()
|
| 2355 |
+
if p.grad is not None:
|
| 2356 |
+
p.grad.data = p.grad.data.float()
|
| 2357 |
+
return self
|
| 2358 |
+
|
| 2359 |
+
def transform_positions(
|
| 2360 |
+
self,
|
| 2361 |
+
z: torch.Tensor,
|
| 2362 |
+
analysis: Optional[RepoGrapeAnalysis] = None,
|
| 2363 |
+
) -> torch.Tensor:
|
| 2364 |
+
"""Use the REPO coordinate directly: u_i^(h)=z_i^(h)."""
|
| 2365 |
+
if analysis is not None:
|
| 2366 |
+
analysis.positions = z.detach()
|
| 2367 |
+
return z
|
| 2368 |
+
|
| 2369 |
+
def _query_freq(
|
| 2370 |
+
self,
|
| 2371 |
+
inv_freq: torch.Tensor,
|
| 2372 |
+
H_repo: int,
|
| 2373 |
+
analysis: Optional[RepoGrapeAnalysis],
|
| 2374 |
+
) -> torch.Tensor:
|
| 2375 |
+
rot_half = int(inv_freq.shape[0])
|
| 2376 |
+
if rot_half > self.max_rot_half:
|
| 2377 |
+
raise ValueError(
|
| 2378 |
+
f"REPO-GRAPE rotary_dim/2={rot_half} exceeds head_dim/2={self.max_rot_half}."
|
| 2379 |
+
)
|
| 2380 |
+
if H_repo != self.num_attention_heads:
|
| 2381 |
+
raise ValueError(
|
| 2382 |
+
f"REPO-GRAPE expected H_repo={self.num_attention_heads}, got {H_repo}."
|
| 2383 |
+
)
|
| 2384 |
+
|
| 2385 |
+
base = inv_freq.float().view(1, rot_half)
|
| 2386 |
+
log_scale = self.freq_log_scale[:, :rot_half].float()
|
| 2387 |
+
freq = base * torch.exp(log_scale)
|
| 2388 |
+
freq = freq.to(device=inv_freq.device)
|
| 2389 |
+
|
| 2390 |
+
if analysis is not None:
|
| 2391 |
+
analysis.freq = freq.detach()
|
| 2392 |
+
return freq
|
| 2393 |
+
|
| 2394 |
+
def apply_multiplicative(
|
| 2395 |
+
self,
|
| 2396 |
+
q: torch.Tensor,
|
| 2397 |
+
k: torch.Tensor,
|
| 2398 |
+
z: torch.Tensor,
|
| 2399 |
+
inv_freq: torch.Tensor,
|
| 2400 |
+
attention_scaling: float,
|
| 2401 |
+
analysis: Optional[RepoGrapeAnalysis] = None,
|
| 2402 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 2403 |
+
"""
|
| 2404 |
+
Apply REPO-GRAPE-M rotation to Q/K using contextual REPO coordinates.
|
| 2405 |
+
|
| 2406 |
+
Args:
|
| 2407 |
+
q: [B, H_q_eff, S, head_dim]
|
| 2408 |
+
k: [B, H_k_eff, S, head_dim]
|
| 2409 |
+
z: [B, H_q_base, S]
|
| 2410 |
+
inv_freq: [rotary_dim/2]
|
| 2411 |
+
attention_scaling: scaling from NeoLLMRotaryEmbedding
|
| 2412 |
+
"""
|
| 2413 |
+
B, H_repo, S = z.shape
|
| 2414 |
+
H_q_eff = q.shape[1]
|
| 2415 |
+
H_k_eff = k.shape[1]
|
| 2416 |
+
|
| 2417 |
+
if H_q_eff % H_repo != 0:
|
| 2418 |
+
raise ValueError(
|
| 2419 |
+
f"REPO-GRAPE expected q heads divisible by z heads, "
|
| 2420 |
+
f"got H_q={H_q_eff} vs H_repo={H_repo}."
|
| 2421 |
+
)
|
| 2422 |
+
P = H_q_eff // H_repo
|
| 2423 |
+
|
| 2424 |
+
if H_k_eff % max(P, 1) != 0:
|
| 2425 |
+
raise ValueError(
|
| 2426 |
+
f"REPO-GRAPE expected k heads divisible by pseudo factor P, "
|
| 2427 |
+
f"got H_k={H_k_eff} vs P={P}."
|
| 2428 |
+
)
|
| 2429 |
+
|
| 2430 |
+
z_q_struct = z.unsqueeze(2).expand(B, H_repo, P, S) # [B,H_q_base,P,S]
|
| 2431 |
+
H_k_base = H_k_eff // max(P, 1)
|
| 2432 |
+
if H_repo % H_k_base != 0:
|
| 2433 |
+
raise ValueError(
|
| 2434 |
+
f"REPO-GRAPE expected original query heads divisible by key groups, "
|
| 2435 |
+
f"got H_repo={H_repo} vs H_k_base={H_k_base}."
|
| 2436 |
+
)
|
| 2437 |
+
|
| 2438 |
+
q_per_k_base = H_repo // H_k_base
|
| 2439 |
+
z_q = z_q_struct.reshape(B, H_q_eff, S)
|
| 2440 |
+
z_k = (
|
| 2441 |
+
z_q_struct
|
| 2442 |
+
.view(B, H_k_base, q_per_k_base, P, S)
|
| 2443 |
+
.mean(dim=2)
|
| 2444 |
+
.reshape(B, H_k_eff, S)
|
| 2445 |
+
)
|
| 2446 |
+
|
| 2447 |
+
rot_half = int(inv_freq.shape[0])
|
| 2448 |
+
rotary_dim = rot_half * 2
|
| 2449 |
+
|
| 2450 |
+
freq_repo = self._query_freq(inv_freq, H_repo, analysis=analysis)
|
| 2451 |
+
freq_q_struct = freq_repo.unsqueeze(1).expand(H_repo, P, rot_half)
|
| 2452 |
+
freq_q = freq_q_struct.reshape(H_q_eff, rot_half)
|
| 2453 |
+
freq_k = (
|
| 2454 |
+
freq_q_struct
|
| 2455 |
+
.view(H_k_base, q_per_k_base, P, rot_half)
|
| 2456 |
+
.mean(dim=1)
|
| 2457 |
+
.reshape(H_k_eff, rot_half)
|
| 2458 |
+
)
|
| 2459 |
+
|
| 2460 |
+
freqs_q = z_q.float().unsqueeze(-1) * freq_q.to(device=q.device).view(
|
| 2461 |
+
1, H_q_eff, 1, rot_half
|
| 2462 |
+
)
|
| 2463 |
+
freqs_k = z_k.float().unsqueeze(-1) * freq_k.to(device=k.device).view(
|
| 2464 |
+
1, H_k_eff, 1, rot_half
|
| 2465 |
+
)
|
| 2466 |
+
|
| 2467 |
+
emb_q = torch.cat([freqs_q, freqs_q], dim=-1)
|
| 2468 |
+
emb_k = torch.cat([freqs_k, freqs_k], dim=-1)
|
| 2469 |
+
|
| 2470 |
+
cos_q = (emb_q.cos() * attention_scaling).to(q.dtype)
|
| 2471 |
+
sin_q = (emb_q.sin() * attention_scaling).to(q.dtype)
|
| 2472 |
+
cos_k = (emb_k.cos() * attention_scaling).to(k.dtype)
|
| 2473 |
+
sin_k = (emb_k.sin() * attention_scaling).to(k.dtype)
|
| 2474 |
+
|
| 2475 |
+
q_rot, q_pass = q[..., :rotary_dim], q[..., rotary_dim:]
|
| 2476 |
+
k_rot, k_pass = k[..., :rotary_dim], k[..., rotary_dim:]
|
| 2477 |
+
|
| 2478 |
+
q_embed = torch.cat(
|
| 2479 |
+
[(q_rot * cos_q) + (rotate_half(q_rot) * sin_q), q_pass],
|
| 2480 |
+
dim=-1,
|
| 2481 |
+
)
|
| 2482 |
+
k_embed = torch.cat(
|
| 2483 |
+
[(k_rot * cos_k) + (rotate_half(k_rot) * sin_k), k_pass],
|
| 2484 |
+
dim=-1,
|
| 2485 |
+
)
|
| 2486 |
+
return q_embed, k_embed
|
| 2487 |
+
|
| 2488 |
+
|
| 2489 |
+
|
| 2490 |
class NeoLLMAttention(nn.Module):
|
| 2491 |
"""
|
| 2492 |
Full attention with FANformer, SeeDNorm, ResFormer, Learnable Multipliers,
|
| 2493 |
optional Momentum, MEA head-level composition, optional LUCID preconditioning,
|
| 2494 |
optional Affine-Scaled Attention, optional Exclusive Self Attention,
|
| 2495 |
+
optional Directional Routing (Taylor, 2026), optional Context
|
| 2496 |
+
Re-Positioning (Li et al., 2026), and optional REPO-GRAPE contextual group
|
| 2497 |
+
positioning (Li et al., 2026 + Zhang et al., 2026).
|
| 2498 |
|
| 2499 |
Directional Routing inserts at position C — post-XSA, pre-reshape — where
|
| 2500 |
the output is already normalized (MEAHeadSeeDNorm) and has auto-position
|
|
|
|
| 2502 |
orthogonal to the self-position already cleaned by XSA.
|
| 2503 |
|
| 2504 |
Pipeline (all active simultaneously when enabled):
|
| 2505 |
+
FANLayer → q_proj(gate) → q_norm/k_norm → REPO/RoPE or REPO-GRAPE-M
|
| 2506 |
+
→ Momentum → MEA(K,V) → LUCID(V) → v_ref → Affine-Scaled SDPA
|
| 2507 |
→ MEAHeadSeeDNorm → XSA → Directional Routing → reshape
|
| 2508 |
→ o_proj · sigmoid(gate) → dropout
|
| 2509 |
|
|
|
|
| 2711 |
self.direction_vecs = None
|
| 2712 |
self.direction_router = None
|
| 2713 |
|
| 2714 |
+
# ── Context Re-Positioning / REPO-GRAPE ─────────────────────────────
|
| 2715 |
+
# REPO-GRAPE reuses the REPO coordinate module f_phi and replaces only
|
| 2716 |
+
# the positional action applied to Q/K. Enabling use_repo_grape=True
|
| 2717 |
+
# therefore activates the REPO coordinate path even when use_repo=False.
|
| 2718 |
+
_repo_requested = bool(getattr(config, "use_repo", False))
|
| 2719 |
+
_repo_grape_requested = bool(getattr(config, "use_repo_grape", False))
|
| 2720 |
+
_repo_start_layer = getattr(config, "repo_start_layer", config.num_hidden_layers // 3)
|
| 2721 |
self.use_repo = (
|
| 2722 |
+
(_repo_requested or _repo_grape_requested)
|
| 2723 |
+
and layer_idx >= _repo_start_layer
|
| 2724 |
)
|
| 2725 |
+
self.use_repo_grape = _repo_grape_requested and self.use_repo
|
| 2726 |
+
|
| 2727 |
if self.use_repo:
|
| 2728 |
_d_p = getattr(config, "repo_d_p", config.hidden_size // 8)
|
| 2729 |
self.repo_module = REPOModule(
|
|
|
|
| 2734 |
else:
|
| 2735 |
self.repo_module = None
|
| 2736 |
|
| 2737 |
+
if self.use_repo_grape:
|
| 2738 |
+
self.repo_grape = RepoGrapePositioning(
|
| 2739 |
+
config=config,
|
| 2740 |
+
layer_idx=layer_idx,
|
| 2741 |
+
num_attention_heads=config.num_attention_heads,
|
| 2742 |
+
num_key_value_heads=config.num_key_value_heads,
|
| 2743 |
+
head_dim=self.head_dim,
|
| 2744 |
+
)
|
| 2745 |
+
else:
|
| 2746 |
+
self.repo_grape = None
|
| 2747 |
+
|
| 2748 |
# ── Interleaved Head Attention (Duvvuri et al., 2026) ─────────────────
|
| 2749 |
# Implementa cross-head mixing antes de RoPE: cada pseudo-head h,p
|
| 2750 |
# es combinación lineal aprendida de los H input-heads originales.
|
|
|
|
| 3525 |
# no meta-tensor issue on lm_eval / to(device) paths.
|
| 3526 |
# (Li et al., 2026, §3.2 — Eq. 6–7)
|
| 3527 |
repo_a = attn_analysis.repo if attn_analysis is not None else None
|
| 3528 |
+
repo_grape_a = attn_analysis.repo_grape if attn_analysis is not None else None
|
| 3529 |
z = self.repo_module(hidden_states, repo_analysis=repo_a) # [B, H, S]
|
| 3530 |
+
if self.use_repo_grape:
|
| 3531 |
+
z = self.repo_grape.transform_positions(z, analysis=repo_grape_a)
|
| 3532 |
inv_freq, attn_scaling = repo_rope_args
|
| 3533 |
if _iha_seq_expand:
|
| 3534 |
+
# IHA seq-expand + REPO/REPO-GRAPE: each real position s has P virtual slots.
|
| 3535 |
+
# Assign continuous position z[b,h,s]·P + p to slot (s,p), scaling z by P
|
| 3536 |
+
# to create room for P sub-positions while preserving relative ordering.
|
|
|
|
|
|
|
|
|
|
| 3537 |
P = self.iha_P
|
| 3538 |
p_offsets = torch.arange(P, device=z.device, dtype=z.dtype)
|
| 3539 |
# z[B,H,S] → [B,H,S,1]*P + [1,1,1,P] → [B,H,S,P] → [B,H,S·P]
|
| 3540 |
z_expanded = (z.unsqueeze(-1) * P + p_offsets.view(1, 1, 1, P)
|
| 3541 |
).reshape(z.shape[0], z.shape[1], _S_orig * P)
|
| 3542 |
+
if self.use_repo_grape:
|
| 3543 |
+
q, k = self.repo_grape.apply_multiplicative(
|
| 3544 |
+
q, k, z_expanded, inv_freq, attn_scaling, analysis=repo_grape_a
|
| 3545 |
+
)
|
| 3546 |
+
else:
|
| 3547 |
+
q, k = _apply_repo_rope(q, k, z_expanded, inv_freq, attn_scaling)
|
| 3548 |
else:
|
| 3549 |
# Standard REPO path (P=1 or no IHA): z covers original S positions.
|
| 3550 |
+
if self.use_repo_grape:
|
| 3551 |
+
q, k = self.repo_grape.apply_multiplicative(
|
| 3552 |
+
q, k, z, inv_freq, attn_scaling, analysis=repo_grape_a
|
| 3553 |
+
)
|
| 3554 |
+
else:
|
| 3555 |
+
q, k = _apply_repo_rope(q, k, z, inv_freq, attn_scaling)
|
| 3556 |
elif _iha_seq_expand:
|
| 3557 |
# IHA seq-expand + standard integer RoPE:
|
| 3558 |
# Build interleaved positions pos_iha[s·P+p] = position_ids[s]·P + p
|
|
|
|
| 5356 |
Called once per layer per forward when analysis is active.
|
| 5357 |
"""
|
| 5358 |
cfg = self.config
|
| 5359 |
+
_repo_requested = bool(getattr(cfg, "use_repo", False))
|
| 5360 |
+
_repo_grape_requested = bool(getattr(cfg, "use_repo_grape", False))
|
| 5361 |
_repo_active = (
|
| 5362 |
+
(_repo_requested or _repo_grape_requested)
|
| 5363 |
and layer_idx >= getattr(cfg, "repo_start_layer", cfg.num_hidden_layers // 3)
|
| 5364 |
)
|
| 5365 |
+
_repo_grape_active = _repo_grape_requested and _repo_active
|
| 5366 |
_versatile = getattr(cfg, "use_versatile_ffn", False)
|
| 5367 |
return LayerAnalysis(
|
| 5368 |
seednorm_pre_attn = SeeDNormAnalysis(),
|
| 5369 |
seednorm_post_attn = SeeDNormAnalysis(),
|
| 5370 |
attention = AttentionAnalysis(
|
| 5371 |
+
fan = FANAnalysis(),
|
| 5372 |
+
hadamard = HadamardAnalysis() if getattr(cfg, "use_hadamard_o_proj", False) else None,
|
| 5373 |
+
repo = REPOAnalysis() if _repo_active else None,
|
| 5374 |
+
repo_grape = RepoGrapeAnalysis() if _repo_grape_active else None,
|
| 5375 |
),
|
| 5376 |
mlp = MLPAnalysis(
|
| 5377 |
fan = FANAnalysis() if not _versatile else None,
|
|
|
|
| 5495 |
getattr(self.config, "use_iha", False)
|
| 5496 |
and int(getattr(self.config, "iha_num_pseudo_heads", 1)) > 1
|
| 5497 |
)
|
| 5498 |
+
_repo_grape_needs_inv_freq = bool(getattr(self.config, "use_repo_grape", False))
|
| 5499 |
repo_rope_args = (
|
| 5500 |
(self.rotary_emb.inv_freq, self.rotary_emb.attention_scaling)
|
| 5501 |
+
if (getattr(self.config, "use_repo", False) or _repo_grape_needs_inv_freq or _iha_needs_inv_freq)
|
| 5502 |
else None
|
| 5503 |
)
|
| 5504 |
|