KitsuVp commited on
Commit
3714c3f
·
verified ·
1 Parent(s): 38f7c5e

Update modeling_neollm.py

Browse files
Files changed (1) hide show
  1. modeling_neollm.py +589 -95
modeling_neollm.py CHANGED
@@ -81,6 +81,7 @@ from typing import Optional, Union, Tuple
81
  import torch
82
  import torch.nn.functional as F
83
  from torch import nn
 
84
 
85
  try:
86
  from cut_cross_entropy import linear_cross_entropy # type: ignore[import-not-found]
@@ -95,6 +96,35 @@ try:
95
  except ImportError:
96
  meap_mask_inputs = None
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  _LEV_KERNEL_AVAILABLE = False
99
  _leviathan_embedding_compiler_safe = None
100
  _leviathan_supports = None
@@ -497,7 +527,13 @@ class LeviathanGenerator(nn.Module):
497
  # in the compiled graph while Inductor excludes only the unsafe Triton
498
  # launch from CUDA-graph capture. A CUDA launch error is allowed to
499
  # propagate instead of falling back to reference work on the same stream.
500
- def forward(self, token_ids: torch.Tensor) -> torch.Tensor:
 
 
 
 
 
 
501
  """
502
  Generate embeddings from discrete token indices.
503
 
@@ -508,6 +544,24 @@ class LeviathanGenerator(nn.Module):
508
  Returns:
509
  embeddings [*token_ids.shape, hidden_size].
510
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
511
  if (
512
  self.use_leviathan_triton
513
  and _LEV_KERNEL_AVAILABLE
@@ -541,6 +595,8 @@ class LeviathanGenerator(nn.Module):
541
  params,
542
  self.config,
543
  self.knot_grid,
 
 
544
  )
545
  except (TypeError, ValueError, AttributeError):
546
  # Only validation/configuration failures use the reference
@@ -592,7 +648,14 @@ class LeviathanGenerator(nn.Module):
592
 
593
  e = e + modes.to(self.head_out_weight.dtype) @ self.head_out_weight[m]
594
 
595
- return e.reshape(*orig_shape, self.hidden_size)
 
 
 
 
 
 
 
596
 
597
 
598
 
@@ -870,6 +933,18 @@ class NeoLLMRotaryEmbedding(nn.Module):
870
  self.register_buffer("inv_freq", inv_freq, persistent=False)
871
  self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
872
 
 
 
 
 
 
 
 
 
 
 
 
 
873
  @staticmethod
874
  def compute_default_rope_parameters(
875
  config: NeoLLMConfig = None,
@@ -1697,10 +1772,16 @@ class REPOModule(nn.Module):
1697
  where ``h_i ∈ R^d`` is the hidden state of token ``i`` entering the
1698
  decoder layer (pre-FANLayer), and ``d_p = hidden_size // 8`` by default.
1699
 
1700
- The resulting positions ``z [B, H, S]`` are real-valued and
1701
- unconstrained. They are used to compute per-head ``cos/sin`` embeddings
1702
- inline from ``inv_freq``, replacing the standard integer-based
1703
- ``position_embeddings`` for this layer.
 
 
 
 
 
 
1704
 
1705
  Design notes:
1706
  - ``W_g`` and ``W_c`` are shared across heads (parameter efficiency).
@@ -1870,20 +1951,64 @@ def _apply_repo_rope(
1870
 
1871
  class RepoGrapePositioning(nn.Module):
1872
  r"""
1873
- Minimal REPO-GRAPE-M positional operator.
 
 
 
 
 
 
 
 
1874
 
1875
- This module intentionally keeps only the specific multiplicative path:
 
 
 
 
 
1876
 
1877
- 1. REPO predicts contextual coordinates z_i^(h)=f_phi^(h)(h_i).
1878
- 2. REPO-GRAPE uses u_i^(h)=z_i^(h), with no extra position mode.
1879
- 3. GRAPE-M applies canonical commuting SO(d) rotary planes to Q/K.
1880
- 4. Each query head learns its own angular spectrum
 
 
1881
 
1882
- theta_{h,r}=inv_freq_r*exp(s_{h,r}),
 
1883
 
1884
- and therefore its own phase
 
1885
 
1886
- phi_{b,h,i,r}=u_{b,h,i}*theta_{h,r}.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1887
 
1888
  With one key per query head, the multiplicative action obeys the exact
1889
  relative law
@@ -1894,32 +2019,23 @@ class RepoGrapePositioning(nn.Module):
1894
  GQA introduces a separate geometric restriction: several query heads
1895
  share one key, although their phases phi_{b,h,i,r} can differ. A shared
1896
  key cannot realize every head-specific rotation simultaneously. For a
1897
- KV group H_m and rotary plane r, this implementation therefore projects
1898
- the requested rotations onto the single optimal shared SO(2) rotation:
1899
 
1900
  Z_{b,m,i,r} = (1/g) * sum_{h in H_m} exp(i*phi_{b,h,i,r})
1901
  = C_{b,m,i,r} + i*S_{b,m,i,r},
1902
 
1903
- psi*_{b,m,i,r} = arg Z_{b,m,i,r},
1904
 
1905
- or, equivalently when |Z| > 0,
1906
 
1907
  cos(psi*) = C/|Z|,
1908
  sin(psi*) = S/|Z|.
1909
 
1910
- This is the global minimizer of
1911
-
1912
- sum_{h in H_m} ||R(phi_{b,h,i,r})k - R(psi)k||_2^2,
1913
-
1914
- so it replaces the previous arithmetic approximation
1915
-
1916
- mean(u_h) * mean(theta_h),
1917
-
1918
- which neither averages the effective phase u_h*theta_h nor respects its
1919
- 2*pi periodicity. If the circular resultant is numerically zero, the
1920
- objective does not identify a preferred shared direction; the code uses
1921
- the identity rotation as a deterministic finite convention. Under IHA,
1922
- the projection is computed independently inside every pseudo-slot.
1923
  """
1924
 
1925
  def __init__(
@@ -1938,8 +2054,21 @@ class RepoGrapePositioning(nn.Module):
1938
  self.head_dim = int(head_dim)
1939
  self.max_rot_half = self.head_dim // 2
1940
 
1941
- # The single REPO-GRAPE parameter: a learned log-scale over the
1942
- # base RoPE/GRAPE frequencies, per query head and rotary plane.
 
 
 
 
 
 
 
 
 
 
 
 
 
1943
  self.freq_log_scale = nn.Parameter(
1944
  torch.zeros(
1945
  self.num_attention_heads, self.max_rot_half, dtype=torch.float32
@@ -1947,21 +2076,108 @@ class RepoGrapePositioning(nn.Module):
1947
  requires_grad=True,
1948
  )
1949
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1950
  def _apply(self, fn):
1951
- # Keep the small spectral parameters in fp32 under model.to(dtype).
 
 
1952
  super()._apply(fn)
1953
- p = self.freq_log_scale
1954
- p.data = p.data.float()
1955
- if p.grad is not None:
1956
- p.grad.data = p.grad.data.float()
1957
  return self
1958
 
1959
  def transform_positions(
1960
  self,
1961
  z: torch.Tensor,
 
1962
  ) -> torch.Tensor:
1963
- """Use the REPO coordinate directly: u_i^(h)=z_i^(h)."""
1964
- return z
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1965
 
1966
  def _query_freq(
1967
  self,
@@ -1989,16 +2205,18 @@ class RepoGrapePositioning(nn.Module):
1989
  self,
1990
  q: torch.Tensor,
1991
  k: torch.Tensor,
1992
- z: torch.Tensor,
1993
  inv_freq: torch.Tensor,
1994
  attention_scaling: float,
1995
  ) -> Tuple[torch.Tensor, torch.Tensor]:
1996
  r"""
1997
- Apply REPO-GRAPE-M rotation to Q/K using contextual REPO coordinates.
1998
 
 
 
1999
  Query heads keep their individual phases
2000
 
2001
- phi_{b,h,i,r} = z_{b,h,i} * theta_{h,r}.
2002
 
2003
  For GQA, each KV head m serves a group H_m of query heads. Its shared
2004
  rotation is the circular projection
@@ -2018,7 +2236,7 @@ class RepoGrapePositioning(nn.Module):
2018
  Args:
2019
  q: [B, H_q_eff, S, head_dim]
2020
  k: [B, H_k_eff, S, head_dim]
2021
- z: [B, H_q_base, S]
2022
  inv_freq: [rotary_dim/2]
2023
  attention_scaling: scaling from NeoLLMRotaryEmbedding
2024
 
@@ -2026,13 +2244,13 @@ class RepoGrapePositioning(nn.Module):
2026
  Rotated ``(q, k)`` tensors with the same shapes and dtypes as the
2027
  inputs. Channels beyond ``rotary_dim`` pass through unchanged.
2028
  """
2029
- B, H_repo, S = z.shape
2030
  H_q_eff = q.shape[1]
2031
  H_k_eff = k.shape[1]
2032
 
2033
  if H_q_eff % H_repo != 0:
2034
  raise ValueError(
2035
- f"REPO-GRAPE expected q heads divisible by z heads, "
2036
  f"got H_q={H_q_eff} vs H_repo={H_repo}."
2037
  )
2038
  P = H_q_eff // H_repo
@@ -2043,7 +2261,7 @@ class RepoGrapePositioning(nn.Module):
2043
  f"got H_k={H_k_eff} vs P={P}."
2044
  )
2045
 
2046
- z_q_struct = z.unsqueeze(2).expand(B, H_repo, P, S) # [B,H_q_base,P,S]
2047
  H_k_base = H_k_eff // max(P, 1)
2048
  if H_repo % H_k_base != 0:
2049
  raise ValueError(
@@ -2052,7 +2270,7 @@ class RepoGrapePositioning(nn.Module):
2052
  )
2053
 
2054
  q_per_k_base = H_repo // H_k_base
2055
- z_q = z_q_struct.reshape(B, H_q_eff, S)
2056
 
2057
  rot_half = int(inv_freq.shape[0])
2058
  rotary_dim = rot_half * 2
@@ -2064,7 +2282,7 @@ class RepoGrapePositioning(nn.Module):
2064
  # Individual query phases are the fundamental quantity. They are
2065
  # calculated in fp32 before trigonometric evaluation, as in the
2066
  # original REPO-GRAPE path.
2067
- phase_q = z_q.float().unsqueeze(-1) * freq_q.to(device=q.device).view(
2068
  1, H_q_eff, 1, rot_half
2069
  )
2070
  cos_q_half = phase_q.cos()
@@ -2681,6 +2899,9 @@ class NeoLLMAttention(nn.Module):
2681
  # REPO-GRAPE reuses the REPO coordinate module f_phi and replaces only
2682
  # the positional action applied to Q/K. Enabling use_repo_grape=True
2683
  # therefore activates the REPO coordinate path even when use_repo=False.
 
 
 
2684
  _repo_requested = bool(getattr(config, "use_repo", False))
2685
  _repo_grape_requested = bool(getattr(config, "use_repo_grape", False))
2686
  _repo_start_layer = getattr(
@@ -2709,8 +2930,14 @@ class NeoLLMAttention(nn.Module):
2709
  num_key_value_heads=config.num_key_value_heads,
2710
  head_dim=self.head_dim,
2711
  )
 
 
 
 
 
2712
  else:
2713
  self.repo_grape = None
 
2714
 
2715
  # ── REPO-GOAT factorised log-prior ───────────────────────────────────
2716
  # Independent of use_repo/use_repo_grape: it can be ablated with standard
@@ -3791,6 +4018,7 @@ class NeoLLMAttention(nn.Module):
3791
 
3792
  # ── RoPE / REPO ───────────────────────────────────────────────────────
3793
  cos, sin = position_embeddings
 
3794
  if self.use_repo:
3795
  # REPO path: f_ϕ predicts continuous per-head positions from the
3796
  # residual stream, then cos/sin are built inline from those positions
@@ -3799,34 +4027,107 @@ class NeoLLMAttention(nn.Module):
3799
  # directly from rotary_emb at forward time — no buffer on this module,
3800
  # no meta-tensor issue on lm_eval / to(device) paths.
3801
  # (Li et al., 2026, §3.2 — Eq. 6–7)
3802
- z = self.repo_module(hidden_states) # [B, H, S]
3803
- if self.use_repo_grape:
3804
- z = self.repo_grape.transform_positions(z)
3805
  inv_freq, attn_scaling = repo_rope_args
3806
- if _iha_seq_expand:
3807
- # IHA seq-expand + REPO/REPO-GRAPE: each real position s has P virtual slots.
3808
- # Assign continuous position z[b,h,s]·P + p to slot (s,p), scaling z by P
3809
- # to create room for P sub-positions while preserving relative ordering.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3810
  P = self.iha_P
3811
- p_offsets = torch.arange(P, device=z.device, dtype=z.dtype)
3812
- # z[B,H,S] → [B,H,S,1]*P + [1,1,1,P] → [B,H,S,P] → [B,H,S·P]
3813
- z_expanded = (z.unsqueeze(-1) * P + p_offsets.view(1, 1, 1, P)).reshape(
3814
- z.shape[0], z.shape[1], _S_orig * P
3815
  )
 
 
 
 
 
3816
  if self.use_repo_grape:
3817
  q, k = self.repo_grape.apply_multiplicative(
3818
- q, k, z_expanded, inv_freq, attn_scaling
3819
  )
3820
  else:
3821
- q, k = _apply_repo_rope(q, k, z_expanded, inv_freq, attn_scaling)
3822
- else:
3823
- # Standard REPO path (P=1 or no IHA): z covers original S positions.
 
 
 
3824
  if self.use_repo_grape:
3825
  q, k = self.repo_grape.apply_multiplicative(
3826
- q, k, z, inv_freq, attn_scaling
3827
  )
3828
  else:
3829
- q, k = _apply_repo_rope(q, k, z, inv_freq, attn_scaling)
 
 
3830
  elif _iha_seq_expand:
3831
  # IHA seq-expand + standard integer RoPE:
3832
  # Build interleaved positions pos_iha[s·P+p] = position_ids[s]·P + p
@@ -3848,7 +4149,8 @@ class NeoLLMAttention(nn.Module):
3848
  # Standard path: integer positions pre-computed by NeoLLMModel.
3849
  q, k = apply_rotary_pos_emb(q, k, cos, sin)
3850
 
3851
- q, k = self._apply_momentum_attention(q, k)
 
3852
  # ── MEA head mixing ───────────────────────────────────────────────────
3853
  # For IHA seq-expand (P>1): K/V are [B, H_kv, S·P, d] — same number of
3854
  # heads as without IHA, just a longer sequence. Use the standard
@@ -4187,7 +4489,44 @@ class PolyNorm(nn.Module):
4187
  def forward(
4188
  self,
4189
  x: torch.Tensor,
 
4190
  ) -> torch.Tensor:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4191
  # Caché de potencias: x_sq reutilizado en x1 y x2; x_cu = x·x_sq evita pow(3)
4192
  x_sq = x.pow(2)
4193
  x_cu = x * x_sq
@@ -4213,6 +4552,9 @@ class PolyNorm(nn.Module):
4213
  self.weight[0] * x3 + self.weight[1] * x2 + self.weight[2] * x1 + self.bias
4214
  )
4215
 
 
 
 
4216
  return output
4217
 
4218
 
@@ -4259,10 +4601,11 @@ class NeoLLMMLP(nn.Module):
4259
  gate_out = self.gate_proj(x_fan)
4260
  up_out = self.up_proj(x_fan)
4261
 
4262
- act_out = self.act_fn(gate_out)
 
4263
  act_x_up = act_out * up_out
4264
 
4265
- result = self.down_proj(self.dropout(act_x_up))
4266
 
4267
  return result
4268
 
@@ -4997,6 +5340,9 @@ class SpellingBeeEmbedding(nn.Module):
4997
  self,
4998
  token_ids: torch.Tensor, # [B, S] or [N]
4999
  token_embeds: torch.Tensor, # [B, S, d] or [N, d]
 
 
 
5000
  ) -> torch.Tensor:
5001
  """
5002
  Args:
@@ -5021,7 +5367,22 @@ class SpellingBeeEmbedding(nn.Module):
5021
  ].mean(-2) # [..., d], fixed 16-position mean
5022
 
5023
  # ── Step 3: mean with token embeddings ──────────────────────────────
5024
- return (token_embeds + e_chars) * 0.5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5025
 
5026
  # ── Inference utility ─────────────────────────────────────────────────────
5027
 
@@ -5117,11 +5478,33 @@ class NeoLLMPreTrainedModel(PreTrainedModel):
5117
 
5118
  @classmethod
5119
  def from_pretrained(cls, *args, **kwargs):
5120
- """Load legacy checkpoints without exposing an uninitialized knot grid."""
5121
- model = super().from_pretrained(*args, **kwargs)
5122
- for module in model.modules():
 
 
 
 
 
 
 
 
 
 
 
 
 
5123
  if not isinstance(module, LeviathanGenerator):
5124
  continue
 
 
 
 
 
 
 
 
 
5125
  if module.knot_grid.device.type == "meta":
5126
  module._buffers["knot_grid"] = torch.linspace(
5127
  0.0,
@@ -5132,15 +5515,45 @@ class NeoLLMPreTrainedModel(PreTrainedModel):
5132
  )
5133
  else:
5134
  with torch.no_grad():
 
 
5135
  module.knot_grid.copy_(
5136
  torch.linspace(
5137
  0.0,
5138
  1.0,
5139
  module.num_knots,
5140
  device=module.knot_grid.device,
5141
- dtype=module.knot_grid.dtype,
5142
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5143
  )
 
 
 
 
 
 
 
 
 
 
 
5144
  return model
5145
 
5146
  def _init_weights(self, module):
@@ -5346,6 +5759,26 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
5346
  else:
5347
  self.spelling_bee = None
5348
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5349
  self.layers = nn.ModuleList(
5350
  [
5351
  NeoLLMDecoderLayer(config, layer_idx)
@@ -5422,6 +5855,7 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
5422
  output_hidden_states: Optional[bool] = None,
5423
  output_attentions: Optional[bool] = None,
5424
  output_tweo_activations: Optional[bool] = None,
 
5425
  return_dict: Optional[bool] = None,
5426
  **kwargs: Unpack[TransformersKwargs],
5427
  ) -> Tuple:
@@ -5447,9 +5881,35 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
5447
  raise ValueError("Specify exactly one of input_ids or inputs_embeds")
5448
 
5449
  # ── Embedding stage ────────────────────────────────────────────────
 
 
 
 
 
 
 
 
 
 
 
 
 
5450
  if inputs_embeds is None:
5451
  if self.config.use_token_generator:
5452
- inputs_embeds = self.token_generator(input_ids)
 
 
 
 
 
 
 
 
 
 
 
 
 
5453
  else:
5454
  inputs_embeds = self.embed_tokens(input_ids)
5455
 
@@ -5458,7 +5918,29 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
5458
  # caller; in that case SBE cannot run (no token_ids available) and is
5459
  # silently skipped — consistent with the standard embedding bypass path.
5460
  if self.spelling_bee is not None and input_ids is not None:
5461
- inputs_embeds = self.spelling_bee(input_ids, inputs_embeds)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5462
 
5463
  if position_ids is None:
5464
  if attention_mask is not None:
@@ -5789,6 +6271,7 @@ def compute_cce_loss(
5789
  cce_impl="cce_kahan_full_c",
5790
  use_mile_loss=False,
5791
  mile_loss_gamma=1.0,
 
5792
  use_mu_loss=False,
5793
  mu_loss_lambda=1e-4,
5794
  return_loss_metrics=False,
@@ -5817,6 +6300,9 @@ def compute_cce_loss(
5817
  mile_enabled=True,
5818
  mile_gamma=float(mile_loss_gamma),
5819
  )
 
 
 
5820
  if use_mu_loss:
5821
  _require_extended_cce_options("mu_loss_enabled", "mu_loss_lambda")
5822
  extension_kwargs.update(
@@ -7502,19 +7988,9 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
7502
  _require_extended_cce_options("mile_enabled", "mile_gamma")
7503
  if bool(getattr(config, "use_mu_loss", False)):
7504
  _require_extended_cce_options("mu_loss_enabled", "mu_loss_lambda")
7505
- if bool(getattr(config, "use_meap", False)) and meap_mask_inputs is None:
7506
- raise ImportError(
7507
- "`use_meap=True` requires the extended cut-cross-entropy package. "
7508
- f"Install it with `pip install --force-reinstall --no-deps "
7509
- f"\"{_EXTENDED_CCE_INSTALL}\"`, or set `use_meap=False`."
7510
- )
7511
- if bool(getattr(config, "use_meap", False)) and (
7512
- "return_metrics" not in inspect.signature(meap_mask_inputs).parameters
7513
- ):
7514
- raise ImportError(
7515
- "The installed cut-cross-entropy package does not provide kernel-side "
7516
- "MEAP metrics. Reinstall the extended backend or set `use_meap=False`."
7517
- )
7518
  self._last_ntp_loss = None
7519
  self._last_ntp_ce_unweighted = None
7520
  self._last_mile_reweighting_delta = None
@@ -7609,6 +8085,7 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
7609
  and self.training
7610
  and labels is not None
7611
  )
 
7612
  if meap_enabled:
7613
  if input_ids is None or inputs_embeds is not None:
7614
  raise ValueError(
@@ -7617,8 +8094,13 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
7617
  )
7618
  if meap_mask_inputs is None:
7619
  raise ImportError(
7620
- "MEAP is enabled but is unavailable in the installed "
7621
- "cut-cross-entropy package."
 
 
 
 
 
7622
  )
7623
  mask_token_id = getattr(self.config, "meap_mask_token_id", None)
7624
  if mask_token_id is None:
@@ -7642,7 +8124,8 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
7642
  )
7643
  exclude_last = bool(getattr(self.config, "meap_exclude_last", True))
7644
  mask_ratio = float(getattr(self.config, "meap_mask_ratio", 0.15))
7645
- input_ids, meap_metrics = meap_mask_inputs(
 
7646
  input_ids,
7647
  int(mask_token_id),
7648
  enabled=True,
@@ -7650,12 +8133,16 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
7650
  eligible_mask=eligible_mask,
7651
  seed=effective_meap_seed,
7652
  exclude_last=exclude_last,
7653
- return_mask=False,
7654
  return_metrics=True,
7655
  implementation=str(
7656
  getattr(self.config, "meap_implementation", "triton")
7657
  ),
7658
  )
 
 
 
 
7659
  eligible_count = meap_metrics[0]
7660
  masked_count = meap_metrics[1]
7661
  self._last_meap_mask_fraction = (
@@ -7706,6 +8193,7 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
7706
  inputs_embeds=inputs_embeds,
7707
  output_hidden_states=model_output_hidden_states,
7708
  output_tweo_activations=tweo_enabled,
 
7709
  return_dict=return_dict,
7710
  **kwargs,
7711
  )
@@ -7810,6 +8298,12 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
7810
  mile_loss_gamma=float(
7811
  getattr(self.config, "mile_loss_gamma", 1.0)
7812
  ),
 
 
 
 
 
 
7813
  use_mu_loss=bool(getattr(self.config, "use_mu_loss", False)),
7814
  mu_loss_lambda=float(
7815
  getattr(self.config, "mu_loss_lambda", 1e-4)
 
81
  import torch
82
  import torch.nn.functional as F
83
  from torch import nn
84
+ from torch.utils.checkpoint import checkpoint as _activation_checkpoint
85
 
86
  try:
87
  from cut_cross_entropy import linear_cross_entropy # type: ignore[import-not-found]
 
96
  except ImportError:
97
  meap_mask_inputs = None
98
 
99
+ try:
100
+ from cut_cross_entropy.polynorm import ( # type: ignore[import-not-found]
101
+ polynorm as _cce_polynorm,
102
+ )
103
+ except (ImportError, ModuleNotFoundError):
104
+ _cce_polynorm = None
105
+
106
+ try:
107
+ from cut_cross_entropy.polynorm import ( # type: ignore[import-not-found]
108
+ polynorm_uses_cute as _cce_polynorm_uses_cute,
109
+ )
110
+ except (ImportError, ModuleNotFoundError):
111
+ _cce_polynorm_uses_cute = None
112
+
113
+ _REPO_GRAPE_KERNEL_AVAILABLE = False
114
+ _cce_repo_grape = None
115
+ _cce_repo_grape_supported = None
116
+ try:
117
+ from cut_cross_entropy.repo_grape import ( # type: ignore[import-not-found]
118
+ repo_grape as _cce_repo_grape,
119
+ repo_grape_supported as _cce_repo_grape_supported,
120
+ )
121
+
122
+ _REPO_GRAPE_KERNEL_AVAILABLE = True
123
+ except (ImportError, ModuleNotFoundError, AttributeError):
124
+ # REPO-GRAPE remains fully functional through RepoGrapePositioning when the
125
+ # optional Triton package (or a new-enough CCE checkout) is unavailable.
126
+ pass
127
+
128
  _LEV_KERNEL_AVAILABLE = False
129
  _leviathan_embedding_compiler_safe = None
130
  _leviathan_supports = None
 
527
  # in the compiled graph while Inductor excludes only the unsafe Triton
528
  # launch from CUDA-graph capture. A CUDA launch error is allowed to
529
  # propagate instead of falling back to reference work on the same stream.
530
+ def forward(
531
+ self,
532
+ token_ids: torch.Tensor,
533
+ *,
534
+ meap_mask_embedding: Optional[torch.Tensor] = None,
535
+ meap_mask_token_id: Optional[int] = None,
536
+ ) -> torch.Tensor:
537
  """
538
  Generate embeddings from discrete token indices.
539
 
 
544
  Returns:
545
  embeddings [*token_ids.shape, hidden_size].
546
  """
547
+ has_meap = (
548
+ meap_mask_embedding is not None or meap_mask_token_id is not None
549
+ )
550
+ if has_meap:
551
+ if meap_mask_embedding is None or meap_mask_token_id is None:
552
+ raise ValueError(
553
+ "meap_mask_embedding and meap_mask_token_id must be "
554
+ "provided together"
555
+ )
556
+ if (
557
+ meap_mask_embedding.ndim != 1
558
+ or meap_mask_embedding.numel() != self.hidden_size
559
+ ):
560
+ raise ValueError(
561
+ "meap_mask_embedding must have shape "
562
+ f"({self.hidden_size},)"
563
+ )
564
+
565
  if (
566
  self.use_leviathan_triton
567
  and _LEV_KERNEL_AVAILABLE
 
595
  params,
596
  self.config,
597
  self.knot_grid,
598
+ mask_embedding=meap_mask_embedding,
599
+ mask_token_id=meap_mask_token_id,
600
  )
601
  except (TypeError, ValueError, AttributeError):
602
  # Only validation/configuration failures use the reference
 
648
 
649
  e = e + modes.to(self.head_out_weight.dtype) @ self.head_out_weight[m]
650
 
651
+ e = e.reshape(*orig_shape, self.hidden_size)
652
+ if has_meap:
653
+ e = torch.where(
654
+ token_ids.eq(int(meap_mask_token_id)).unsqueeze(-1),
655
+ meap_mask_embedding.to(device=e.device, dtype=e.dtype),
656
+ e,
657
+ )
658
+ return e
659
 
660
 
661
 
 
933
  self.register_buffer("inv_freq", inv_freq, persistent=False)
934
  self.register_buffer("original_inv_freq", inv_freq.clone(), persistent=False)
935
 
936
+ def _apply(self, fn):
937
+ # Positional frequencies are an FP32 numerical contract for both the
938
+ # PyTorch and fused REPO-GRAPE paths. Preserve that contract across
939
+ # model.to(bfloat16)/model.to(float16) instead of allocating a cast in
940
+ # every decoder layer. The buffers remain non-persistent as before.
941
+ super()._apply(fn)
942
+ for name in ("inv_freq", "original_inv_freq"):
943
+ value = self._buffers.get(name)
944
+ if value is not None:
945
+ self._buffers[name] = value.float()
946
+ return self
947
+
948
  @staticmethod
949
  def compute_default_rope_parameters(
950
  config: NeoLLMConfig = None,
 
1772
  where ``h_i ∈ R^d`` is the hidden state of token ``i`` entering the
1773
  decoder layer (pre-FANLayer), and ``d_p = hidden_size // 8`` by default.
1774
 
1775
+ The resulting assignments ``z [B, H, S]`` are real-valued and
1776
+ unconstrained. In the original REPO-RoPE path they are used directly to
1777
+ compute per-head ``cos/sin`` embeddings. When REPO is composed with
1778
+ REPO-GRAPE, ``RepoGrapePositioning`` keeps ``z`` as the raw REPO output and
1779
+ constructs the final coordinate
1780
+
1781
+ u_i^(h) = i + alpha_h (z_i^(h) - i)
1782
+
1783
+ before the GRAPE phase is formed. This keeps the semantic distinction
1784
+ ``REPO -> z`` versus ``REPO-GRAPE -> u`` explicit in the implementation.
1785
 
1786
  Design notes:
1787
  - ``W_g`` and ``W_c`` are shared across heads (parameter efficiency).
 
1951
 
1952
  class RepoGrapePositioning(nn.Module):
1953
  r"""
1954
+ Minimal REPO-GRAPE-M positional operator with linear-blend coordinates.
1955
+
1956
+ The implementation deliberately has no positional-mode flag. Whenever
1957
+ REPO-GRAPE is active it uses exactly one coordinate map, the
1958
+ ``linear-blend-repo`` form from the accompanying derivation:
1959
+
1960
+ z_i^(h) = f_phi^(h)(h_i),
1961
+ u_i^(h) = (1 - alpha_h) i + alpha_h z_i^(h)
1962
+ = i + alpha_h (z_i^(h) - i).
1963
 
1964
+ ``i`` is the actual runtime ``position_ids`` value, not an independently
1965
+ generated global arange. This matters for padding and packed/reset
1966
+ position streams. ``alpha_h`` is one learned scalar per query head. It
1967
+ is initialized to 1, so a newly constructed model starts exactly at the
1968
+ previous REPO coordinate u=z and can learn a linear anchor without an
1969
+ abrupt initialization change.
1970
 
1971
+ The parameter is intentionally stored directly rather than through a
1972
+ sigmoid/clamp. Therefore alpha_h is initialized inside [0,1] but is not
1973
+ constrained to stay there. This avoids an additional nonlinear operation,
1974
+ preserves the exact alpha_h=1 initialization, and lets the optimizer learn
1975
+ interpolation or extrapolation. Constraining alpha would be a different
1976
+ parameterization and is not silently introduced here.
1977
 
1978
+ Backward is standard autograd. If ubar_i^(h)=dL/du_i^(h), then for this
1979
+ active linear-blend coordinate
1980
 
1981
+ dL/dz_i^(h) = alpha_h * ubar_i^(h),
1982
+ dL/dalpha_h = sum_i ubar_i^(h) * (z_i^(h) - i).
1983
 
1984
+ For comparison only, the distinct ``linear-plus-repo`` residual map
1985
+
1986
+ u_i^(h) = i + alpha_h z_i^(h)
1987
+
1988
+ would instead give dL/dalpha_h = sum_i ubar_i^(h) z_i^(h). That formula
1989
+ is documented to avoid confusing the two maps, but it is not another
1990
+ executable mode and no flag is added for it.
1991
+
1992
+ Storage/lifetime contract:
1993
+ - ``position_blend_alpha`` is an FP32 ``nn.Parameter`` of shape [H_q].
1994
+ It is optimized normally and is saved/restored in ``state_dict``.
1995
+ - ``freq_log_scale`` remains an FP32 saved parameter of shape
1996
+ [H_q, head_dim/2].
1997
+ - ``position_ids`` is runtime metadata and is never copied into a buffer or
1998
+ checkpoint.
1999
+ - ``u`` and the optional IHA-expanded ``u_{i,p}`` are transient autograd
2000
+ intermediates only. The IHA tensor replaces the previous ``P*z+p``
2001
+ intermediate, so this change does not add another expanded coordinate
2002
+ tensor.
2003
+
2004
+ IHA is applied after the coordinate map. For P pseudo-slots,
2005
+
2006
+ u_{i,p}^(h) = P u_i^(h) + p, p in {0,...,P-1},
2007
+
2008
+ and GRAPE sees the phase
2009
+
2010
+ phi_{b,h,i,p,r} = u_{b,h,i,p} * theta_{h,r},
2011
+ theta_{h,r} = inv_freq_r * exp(s_{h,r}).
2012
 
2013
  With one key per query head, the multiplicative action obeys the exact
2014
  relative law
 
2019
  GQA introduces a separate geometric restriction: several query heads
2020
  share one key, although their phases phi_{b,h,i,r} can differ. A shared
2021
  key cannot realize every head-specific rotation simultaneously. For a
2022
+ KV group H_m and rotary plane r, this implementation projects the requested
2023
+ rotations onto the single optimal shared SO(2) rotation:
2024
 
2025
  Z_{b,m,i,r} = (1/g) * sum_{h in H_m} exp(i*phi_{b,h,i,r})
2026
  = C_{b,m,i,r} + i*S_{b,m,i,r},
2027
 
2028
+ psi*_{b,m,i,r} = arg Z_{b,m,i,r}.
2029
 
2030
+ Equivalently, when |Z| > 0,
2031
 
2032
  cos(psi*) = C/|Z|,
2033
  sin(psi*) = S/|Z|.
2034
 
2035
+ If the circular resultant is numerically zero, the objective does not
2036
+ identify a preferred shared direction; the code uses the identity rotation
2037
+ as a deterministic finite convention. Under IHA, the projection is
2038
+ computed independently inside every pseudo-slot.
 
 
 
 
 
 
 
 
 
2039
  """
2040
 
2041
  def __init__(
 
2054
  self.head_dim = int(head_dim)
2055
  self.max_rot_half = self.head_dim // 2
2056
 
2057
+ # Linear-blend coefficient alpha_h, one scalar per query head:
2058
+ #
2059
+ # u_i^(h) = i + alpha_h * (z_i^(h) - i)
2060
+ #
2061
+ # Initialize at alpha_h=1 so the first forward is exactly the previous
2062
+ # REPO behavior u=z. This is a real nn.Parameter, therefore it is
2063
+ # optimized by autograd/AdamW and stored in every state_dict checkpoint.
2064
+ # It is deliberately unconstrained: no sigmoid/clamp and no new flag.
2065
+ self.position_blend_alpha = nn.Parameter(
2066
+ torch.ones(self.num_attention_heads, dtype=torch.float32),
2067
+ requires_grad=True,
2068
+ )
2069
+
2070
+ # Learned log-scale over the base RoPE/GRAPE frequencies, per query
2071
+ # head and rotary plane: theta_{h,r}=theta^0_r*exp(s_{h,r}).
2072
  self.freq_log_scale = nn.Parameter(
2073
  torch.zeros(
2074
  self.num_attention_heads, self.max_rot_half, dtype=torch.float32
 
2076
  requires_grad=True,
2077
  )
2078
 
2079
+ def _load_from_state_dict(
2080
+ self,
2081
+ state_dict,
2082
+ prefix,
2083
+ local_metadata,
2084
+ strict,
2085
+ missing_keys,
2086
+ unexpected_keys,
2087
+ error_msgs,
2088
+ ):
2089
+ # Backward-compatible checkpoint migration. Older REPO-GRAPE
2090
+ # checkpoints do not contain alpha_h because they used u=z. Injecting
2091
+ # alpha_h=1 reproduces that old forward exactly at load time, while every
2092
+ # checkpoint saved by this version stores the learned parameter normally.
2093
+ alpha_key = prefix + "position_blend_alpha"
2094
+ local_state_dict = state_dict
2095
+ if alpha_key not in state_dict:
2096
+ # Use a local shallow copy so loading does not mutate the caller's
2097
+ # checkpoint mapping. CPU FP32 also works when the module was
2098
+ # constructed on meta before Hugging Face materializes parameters.
2099
+ local_state_dict = state_dict.copy()
2100
+ local_state_dict[alpha_key] = torch.ones(
2101
+ self.num_attention_heads, dtype=torch.float32
2102
+ )
2103
+ super()._load_from_state_dict(
2104
+ local_state_dict,
2105
+ prefix,
2106
+ local_metadata,
2107
+ strict,
2108
+ missing_keys,
2109
+ unexpected_keys,
2110
+ error_msgs,
2111
+ )
2112
+
2113
  def _apply(self, fn):
2114
+ # Keep the tiny positional parameters in FP32 under model.to(dtype).
2115
+ # Both are checkpointed nn.Parameters; only runtime coordinates are
2116
+ # transient.
2117
  super()._apply(fn)
2118
+ for p in (self.position_blend_alpha, self.freq_log_scale):
2119
+ p.data = p.data.float()
2120
+ if p.grad is not None:
2121
+ p.grad.data = p.grad.data.float()
2122
  return self
2123
 
2124
  def transform_positions(
2125
  self,
2126
  z: torch.Tensor,
2127
+ position_ids: Optional[torch.Tensor],
2128
  ) -> torch.Tensor:
2129
+ r"""Build the active linear-blend REPO-GRAPE coordinate.
2130
+
2131
+ The only executable map is
2132
+
2133
+ u_i^(h) = (1-alpha_h) i + alpha_h z_i^(h)
2134
+ = i + alpha_h (z_i^(h) - i).
2135
+
2136
+ The residualized second form is used because it needs only one
2137
+ subtraction, one multiplication and one addition. ``position_ids`` is
2138
+ broadcast as [B,1,S], so it is not materialized once per head.
2139
+
2140
+ ``u`` is evaluated in FP32 because the next operation is the FP32 phase
2141
+ computation. It is not registered as a buffer and is not saved in the
2142
+ checkpoint. Standard autograd gives
2143
+
2144
+ dL/dalpha_h = sum_{b,i} ubar_{b,h,i} (z_{b,h,i} - i).
2145
+ """
2146
+ B, H, S = z.shape
2147
+ if H != self.num_attention_heads:
2148
+ raise ValueError(
2149
+ f"REPO-GRAPE expected z heads={self.num_attention_heads}, got {H}."
2150
+ )
2151
+
2152
+ if position_ids is None:
2153
+ # Defensive fallback for direct attention calls. NeoLLMModel normally
2154
+ # constructs position_ids before entering the decoder layers.
2155
+ pid = torch.arange(S, device=z.device, dtype=torch.float32).view(1, S)
2156
+ else:
2157
+ pid = position_ids
2158
+ if pid.dim() == 1:
2159
+ pid = pid.unsqueeze(0)
2160
+ elif pid.dim() > 2:
2161
+ # Match the existing IHA RoPE convention: trainers may attach
2162
+ # metadata in the final axis; column 0 is the sequential index.
2163
+ pid = pid[..., 0]
2164
+ if pid.dim() != 2 or pid.shape[-1] != S:
2165
+ raise ValueError(
2166
+ "REPO-GRAPE position_ids must have shape [S] or [B,S] "
2167
+ f"matching z[...,S={S}], got {tuple(pid.shape)}."
2168
+ )
2169
+ if pid.shape[0] == 1 and B != 1:
2170
+ pid = pid.expand(B, -1)
2171
+ elif pid.shape[0] != B:
2172
+ raise ValueError(
2173
+ f"REPO-GRAPE position_ids batch={pid.shape[0]} does not match z batch={B}."
2174
+ )
2175
+ pid = pid.to(device=z.device, dtype=torch.float32)
2176
+
2177
+ # Views/broadcasting do not allocate [B,H,S] copies of pid or alpha.
2178
+ i = pid.unsqueeze(1)
2179
+ alpha = self.position_blend_alpha.view(1, H, 1)
2180
+ return i + alpha * (z.float() - i)
2181
 
2182
  def _query_freq(
2183
  self,
 
2205
  self,
2206
  q: torch.Tensor,
2207
  k: torch.Tensor,
2208
+ u: torch.Tensor,
2209
  inv_freq: torch.Tensor,
2210
  attention_scaling: float,
2211
  ) -> Tuple[torch.Tensor, torch.Tensor]:
2212
  r"""
2213
+ Apply REPO-GRAPE-M rotation to Q/K using final coordinates ``u``.
2214
 
2215
+ ``u`` is already the linear-blend coordinate produced by
2216
+ ``transform_positions`` (and, for IHA, already expanded as P*u+p).
2217
  Query heads keep their individual phases
2218
 
2219
+ phi_{b,h,i,r} = u_{b,h,i} * theta_{h,r}.
2220
 
2221
  For GQA, each KV head m serves a group H_m of query heads. Its shared
2222
  rotation is the circular projection
 
2236
  Args:
2237
  q: [B, H_q_eff, S, head_dim]
2238
  k: [B, H_k_eff, S, head_dim]
2239
+ u: [B, H_q_base, S] final REPO-GRAPE coordinates
2240
  inv_freq: [rotary_dim/2]
2241
  attention_scaling: scaling from NeoLLMRotaryEmbedding
2242
 
 
2244
  Rotated ``(q, k)`` tensors with the same shapes and dtypes as the
2245
  inputs. Channels beyond ``rotary_dim`` pass through unchanged.
2246
  """
2247
+ B, H_repo, S = u.shape
2248
  H_q_eff = q.shape[1]
2249
  H_k_eff = k.shape[1]
2250
 
2251
  if H_q_eff % H_repo != 0:
2252
  raise ValueError(
2253
+ f"REPO-GRAPE expected q heads divisible by coordinate heads, "
2254
  f"got H_q={H_q_eff} vs H_repo={H_repo}."
2255
  )
2256
  P = H_q_eff // H_repo
 
2261
  f"got H_k={H_k_eff} vs P={P}."
2262
  )
2263
 
2264
+ u_q_struct = u.unsqueeze(2).expand(B, H_repo, P, S) # [B,H_q_base,P,S]
2265
  H_k_base = H_k_eff // max(P, 1)
2266
  if H_repo % H_k_base != 0:
2267
  raise ValueError(
 
2270
  )
2271
 
2272
  q_per_k_base = H_repo // H_k_base
2273
+ u_q = u_q_struct.reshape(B, H_q_eff, S)
2274
 
2275
  rot_half = int(inv_freq.shape[0])
2276
  rotary_dim = rot_half * 2
 
2282
  # Individual query phases are the fundamental quantity. They are
2283
  # calculated in fp32 before trigonometric evaluation, as in the
2284
  # original REPO-GRAPE path.
2285
+ phase_q = u_q.float().unsqueeze(-1) * freq_q.to(device=q.device).view(
2286
  1, H_q_eff, 1, rot_half
2287
  )
2288
  cos_q_half = phase_q.cos()
 
2899
  # REPO-GRAPE reuses the REPO coordinate module f_phi and replaces only
2900
  # the positional action applied to Q/K. Enabling use_repo_grape=True
2901
  # therefore activates the REPO coordinate path even when use_repo=False.
2902
+ # There is no extra position-mode flag: active REPO-GRAPE always maps
2903
+ # raw z to u=i+alpha_h*(z-i) with one learned FP32 alpha_h per query
2904
+ # head before GRAPE-M (and before the IHA P*u+p expansion).
2905
  _repo_requested = bool(getattr(config, "use_repo", False))
2906
  _repo_grape_requested = bool(getattr(config, "use_repo_grape", False))
2907
  _repo_start_layer = getattr(
 
2930
  num_key_value_heads=config.num_key_value_heads,
2931
  head_dim=self.head_dim,
2932
  )
2933
+ # Availability is fixed when the module is constructed, so Dynamo
2934
+ # specializes one stable route. This is deliberately not a new
2935
+ # configuration flag: use_repo_grape still owns the feature, while
2936
+ # unsupported inputs retain the exact PyTorch implementation.
2937
+ self.use_repo_grape_triton = False
2938
  else:
2939
  self.repo_grape = None
2940
+ self.use_repo_grape_triton = False
2941
 
2942
  # ── REPO-GOAT factorised log-prior ───────────────────────────────────
2943
  # Independent of use_repo/use_repo_grape: it can be ablated with standard
 
4018
 
4019
  # ── RoPE / REPO ───────────────────────────────────────────────────────
4020
  cos, sin = position_embeddings
4021
+ _repo_grape_kernel_used = False
4022
  if self.use_repo:
4023
  # REPO path: f_ϕ predicts continuous per-head positions from the
4024
  # residual stream, then cos/sin are built inline from those positions
 
4027
  # directly from rotary_emb at forward time — no buffer on this module,
4028
  # no meta-tensor issue on lm_eval / to(device) paths.
4029
  # (Li et al., 2026, §3.2 — Eq. 6–7)
4030
+ z = self.repo_module(hidden_states) # [B, H, S], raw REPO assignment
 
 
4031
  inv_freq, attn_scaling = repo_rope_args
4032
+
4033
+ # The optional kernel consumes raw REPO assignments and performs
4034
+ # the linear-blend coordinate, IHA P=2 sequence expansion, GRAPE
4035
+ # phases, GQA circular projection, and optional momentum in one
4036
+ # autograd-aware Triton boundary. Q/K are intentionally normalized
4037
+ # before this call: IHA mixes normalized heads, so moving RMSNorm
4038
+ # into the kernel after that mixing would change the model.
4039
+ _repo_grape_sequence_factor = self.iha_P if _iha_seq_expand else 1
4040
+ if (
4041
+ self.use_repo_grape
4042
+ and self.use_repo_grape_triton
4043
+ and _cce_repo_grape is not None
4044
+ and _cce_repo_grape_supported is not None
4045
+ and _repo_grape_sequence_factor in (1, 2)
4046
+ and _cce_repo_grape_supported(
4047
+ q,
4048
+ k,
4049
+ z,
4050
+ inv_freq,
4051
+ self.repo_grape.position_blend_alpha,
4052
+ self.repo_grape.freq_log_scale,
4053
+ sequence_pseudo_factor=_repo_grape_sequence_factor,
4054
+ )
4055
+ ):
4056
+ _kernel_position_ids = position_ids
4057
+ if (
4058
+ _kernel_position_ids is not None
4059
+ and _kernel_position_ids.dim() > 2
4060
+ ):
4061
+ # Match transform_positions and the IHA helpers: the first
4062
+ # metadata column is the runtime sequential position.
4063
+ _kernel_position_ids = _kernel_position_ids[..., 0]
4064
+ _kernel_gamma = (
4065
+ self.momentum_gamma if self.use_momentum_attention else 0.0
4066
+ )
4067
+ q, k = _cce_repo_grape(
4068
+ q,
4069
+ k,
4070
+ z,
4071
+ _kernel_position_ids,
4072
+ inv_freq,
4073
+ self.repo_grape.position_blend_alpha,
4074
+ self.repo_grape.freq_log_scale,
4075
+ attn_scaling,
4076
+ sequence_pseudo_factor=_repo_grape_sequence_factor,
4077
+ momentum_gamma=_kernel_gamma,
4078
+ output_dtype=q.dtype,
4079
+ )
4080
+ _repo_grape_kernel_used = True
4081
+
4082
+ # REPO-GRAPE consumes u, not raw z. There is intentionally no mode
4083
+ # flag: when REPO-GRAPE is active, transform_positions always applies
4084
+ # the learned linear blend
4085
+ #
4086
+ # u_i^(h) = i + alpha_h * (z_i^(h) - i).
4087
+ #
4088
+ # Pure REPO (without GRAPE) keeps the original paper behavior and
4089
+ # therefore continues to use z directly.
4090
+ if not _repo_grape_kernel_used:
4091
+ repo_coords = (
4092
+ self.repo_grape.transform_positions(z, position_ids)
4093
+ if self.use_repo_grape
4094
+ else z
4095
+ )
4096
+
4097
+ if not _repo_grape_kernel_used and _iha_seq_expand:
4098
+ # IHA is applied *after* the coordinate map. Thus REPO-GRAPE
4099
+ # uses u_{i,p}=P*u_i+p, while pure REPO keeps its previous
4100
+ # z_{i,p}=P*z_i+p behavior. ``repo_coords`` is transient and this
4101
+ # expanded tensor replaces, rather than accompanies, the old
4102
+ # z_expanded allocation.
4103
  P = self.iha_P
4104
+ p_offsets = torch.arange(
4105
+ P, device=repo_coords.device, dtype=repo_coords.dtype
 
 
4106
  )
4107
+ coords_expanded = (
4108
+ repo_coords.unsqueeze(-1) * P
4109
+ + p_offsets.view(1, 1, 1, P)
4110
+ ).reshape(repo_coords.shape[0], repo_coords.shape[1], _S_orig * P)
4111
+
4112
  if self.use_repo_grape:
4113
  q, k = self.repo_grape.apply_multiplicative(
4114
+ q, k, coords_expanded, inv_freq, attn_scaling
4115
  )
4116
  else:
4117
+ q, k = _apply_repo_rope(
4118
+ q, k, coords_expanded, inv_freq, attn_scaling
4119
+ )
4120
+ elif not _repo_grape_kernel_used:
4121
+ # P=1/no-IHA: ``repo_coords`` is u for REPO-GRAPE and z for
4122
+ # the original REPO-RoPE path.
4123
  if self.use_repo_grape:
4124
  q, k = self.repo_grape.apply_multiplicative(
4125
+ q, k, repo_coords, inv_freq, attn_scaling
4126
  )
4127
  else:
4128
+ q, k = _apply_repo_rope(
4129
+ q, k, repo_coords, inv_freq, attn_scaling
4130
+ )
4131
  elif _iha_seq_expand:
4132
  # IHA seq-expand + standard integer RoPE:
4133
  # Build interleaved positions pos_iha[s·P+p] = position_ids[s]·P + p
 
4149
  # Standard path: integer positions pre-computed by NeoLLMModel.
4150
  q, k = apply_rotary_pos_emb(q, k, cos, sin)
4151
 
4152
+ if not _repo_grape_kernel_used:
4153
+ q, k = self._apply_momentum_attention(q, k)
4154
  # ── MEA head mixing ───────────────────────────────────────────────────
4155
  # For IHA seq-expand (P>1): K/V are [B, H_kv, S·P, d] — same number of
4156
  # heads as without IHA, just a longer sequence. Use the standard
 
4489
  def forward(
4490
  self,
4491
  x: torch.Tensor,
4492
+ dropout_p: float = 0.0,
4493
  ) -> torch.Tensor:
4494
+ if _cce_polynorm is not None:
4495
+ if (
4496
+ self.training
4497
+ and torch.is_grad_enabled()
4498
+ and torch.compiler.is_compiling()
4499
+ and _cce_polynorm_uses_cute is not None
4500
+ and _cce_polynorm_uses_cute(
4501
+ x,
4502
+ self.weight,
4503
+ self.bias,
4504
+ eps=float(self.eps),
4505
+ exclusive_logits=getattr(self, "exclusive_logits", None),
4506
+ dropout_p=dropout_p,
4507
+ )
4508
+ ):
4509
+ return _activation_checkpoint(
4510
+ _cce_polynorm,
4511
+ x,
4512
+ self.weight,
4513
+ self.bias,
4514
+ use_reentrant=False,
4515
+ eps=float(self.eps),
4516
+ proj_eps=float(getattr(self, "proj_eps", 1e-6)),
4517
+ exclusive_logits=getattr(self, "exclusive_logits", None),
4518
+ dropout_p=dropout_p,
4519
+ )
4520
+ return _cce_polynorm(
4521
+ x,
4522
+ self.weight,
4523
+ self.bias,
4524
+ eps=float(self.eps),
4525
+ proj_eps=float(getattr(self, "proj_eps", 1e-6)),
4526
+ exclusive_logits=getattr(self, "exclusive_logits", None),
4527
+ dropout_p=dropout_p,
4528
+ )
4529
+
4530
  # Caché de potencias: x_sq reutilizado en x1 y x2; x_cu = x·x_sq evita pow(3)
4531
  x_sq = x.pow(2)
4532
  x_cu = x * x_sq
 
4552
  self.weight[0] * x3 + self.weight[1] * x2 + self.weight[2] * x1 + self.bias
4553
  )
4554
 
4555
+ if dropout_p:
4556
+ output = F.dropout(output, p=dropout_p, training=True)
4557
+
4558
  return output
4559
 
4560
 
 
4601
  gate_out = self.gate_proj(x_fan)
4602
  up_out = self.up_proj(x_fan)
4603
 
4604
+ dropout_p = float(self.dropout.p) if self.training else 0.0
4605
+ act_out = self.act_fn(gate_out, dropout_p=dropout_p)
4606
  act_x_up = act_out * up_out
4607
 
4608
+ result = self.down_proj(act_x_up)
4609
 
4610
  return result
4611
 
 
5340
  self,
5341
  token_ids: torch.Tensor, # [B, S] or [N]
5342
  token_embeds: torch.Tensor, # [B, S, d] or [N, d]
5343
+ *,
5344
+ meap_mask_embedding: Optional[torch.Tensor] = None,
5345
+ meap_mask_token_id: Optional[int] = None,
5346
  ) -> torch.Tensor:
5347
  """
5348
  Args:
 
5367
  ].mean(-2) # [..., d], fixed 16-position mean
5368
 
5369
  # ── Step 3: mean with token embeddings ──────────────────────────────
5370
+ output = (token_embeds + e_chars) * 0.5
5371
+ has_meap = (
5372
+ meap_mask_embedding is not None or meap_mask_token_id is not None
5373
+ )
5374
+ if not has_meap:
5375
+ return output
5376
+ if meap_mask_embedding is None or meap_mask_token_id is None:
5377
+ raise ValueError(
5378
+ "meap_mask_embedding and meap_mask_token_id must be provided "
5379
+ "together"
5380
+ )
5381
+ return torch.where(
5382
+ token_ids.eq(int(meap_mask_token_id)).unsqueeze(-1),
5383
+ meap_mask_embedding.to(device=output.device, dtype=output.dtype),
5384
+ output,
5385
+ )
5386
 
5387
  # ── Inference utility ─────────────────────────────────────────────────────
5388
 
 
5478
 
5479
  @classmethod
5480
  def from_pretrained(cls, *args, **kwargs):
5481
+ """Initialize only a legacy checkpoint that lacks ``knot_grid``.
5482
+
5483
+ Current checkpoints persist this buffer and must retain its exact saved
5484
+ values. The previous compatibility path regenerated the grid after
5485
+ every successful load, silently overwriting a valid checkpoint (and
5486
+ BF16 direct-linspace rounding can differ from FP32-then-BF16 rounding).
5487
+ """
5488
+ return_loading_info = bool(kwargs.pop("output_loading_info", False))
5489
+ requested_dtype = kwargs.get("dtype", kwargs.get("torch_dtype"))
5490
+ model, loading_info = super().from_pretrained(
5491
+ *args,
5492
+ output_loading_info=True,
5493
+ **kwargs,
5494
+ )
5495
+ missing_keys = set(loading_info.get("missing_keys", ()))
5496
+ for module_name, module in model.named_modules():
5497
  if not isinstance(module, LeviathanGenerator):
5498
  continue
5499
+ checkpoint_key = (
5500
+ f"{module_name}.knot_grid" if module_name else "knot_grid"
5501
+ )
5502
+ needs_legacy_initialization = (
5503
+ module.knot_grid.device.type == "meta"
5504
+ or checkpoint_key in missing_keys
5505
+ )
5506
+ if not needs_legacy_initialization:
5507
+ continue
5508
  if module.knot_grid.device.type == "meta":
5509
  module._buffers["knot_grid"] = torch.linspace(
5510
  0.0,
 
5515
  )
5516
  else:
5517
  with torch.no_grad():
5518
+ # Match training construction: create the fixed grid in
5519
+ # FP32 first, then let the explicit cast perform rounding.
5520
  module.knot_grid.copy_(
5521
  torch.linspace(
5522
  0.0,
5523
  1.0,
5524
  module.num_knots,
5525
  device=module.knot_grid.device,
5526
+ dtype=torch.float32,
5527
+ ).to(dtype=module.knot_grid.dtype)
5528
+ )
5529
+
5530
+ # These SiameseNorm tensors are constructed explicitly in FP32 to make
5531
+ # direct ``NeoLLMForCausalLM(config)`` initialization stable. Training
5532
+ # then casts them with ``model.to(BF16)``. Transformers' low-memory
5533
+ # loader honors the explicit constructor dtype instead of ``dtype=``
5534
+ # when copying a checkpoint, so without this normalization a BF16 Hub
5535
+ # checkpoint silently resumes those 12 trainable vectors in FP32.
5536
+ if (
5537
+ isinstance(requested_dtype, torch.dtype)
5538
+ and requested_dtype.is_floating_point
5539
+ ):
5540
+ for module in model.modules():
5541
+ siamese_scale = getattr(module, "siamese_attn_x_scale", None)
5542
+ if siamese_scale is not None:
5543
+ siamese_scale.data = siamese_scale.data.to(
5544
+ dtype=requested_dtype
5545
  )
5546
+ stream_scale = getattr(
5547
+ module,
5548
+ "_siamese_stream_scale_value",
5549
+ None,
5550
+ )
5551
+ if stream_scale is not None:
5552
+ module._buffers["_siamese_stream_scale_value"] = (
5553
+ stream_scale.to(dtype=requested_dtype)
5554
+ )
5555
+ if return_loading_info:
5556
+ return model, loading_info
5557
  return model
5558
 
5559
  def _init_weights(self, module):
 
5759
  else:
5760
  self.spelling_bee = None
5761
 
5762
+ # MEAP owns one independent final embedding vector. It deliberately
5763
+ # lives in the model instead of the optional CCE package so checkpoints
5764
+ # remain loadable for inference when that package is absent. The last
5765
+ # active embedding producer owns the MEAP selection: Leviathan fuses it
5766
+ # into its output GEMM, while Spelling Bee owns it when byte features
5767
+ # follow. This prevents masked positions from updating dense token rows,
5768
+ # Leviathan codebooks, or Spelling Bee bytes.
5769
+ meap_mask_token_id = getattr(config, "meap_mask_token_id", None)
5770
+ if meap_mask_token_id is None:
5771
+ self.register_parameter("meap_mask_embedding", None)
5772
+ else:
5773
+ self.meap_mask_embedding = nn.Parameter(
5774
+ torch.empty(config.hidden_size)
5775
+ )
5776
+ nn.init.normal_(
5777
+ self.meap_mask_embedding,
5778
+ mean=0.0,
5779
+ std=config.initializer_range,
5780
+ )
5781
+
5782
  self.layers = nn.ModuleList(
5783
  [
5784
  NeoLLMDecoderLayer(config, layer_idx)
 
5855
  output_hidden_states: Optional[bool] = None,
5856
  output_attentions: Optional[bool] = None,
5857
  output_tweo_activations: Optional[bool] = None,
5858
+ meap_active: bool = False,
5859
  return_dict: Optional[bool] = None,
5860
  **kwargs: Unpack[TransformersKwargs],
5861
  ) -> Tuple:
 
5881
  raise ValueError("Specify exactly one of input_ids or inputs_embeds")
5882
 
5883
  # ── Embedding stage ────────────────────────────────────────────────
5884
+ meap_mask_embedding = None
5885
+ meap_mask_token_id = None
5886
+ if meap_active:
5887
+ if input_ids is None:
5888
+ raise ValueError("Active MEAP requires `input_ids`.")
5889
+ if self.meap_mask_embedding is None:
5890
+ raise ValueError(
5891
+ "Active MEAP requires a configured `meap_mask_token_id`."
5892
+ )
5893
+ meap_mask_embedding = self.meap_mask_embedding
5894
+ meap_mask_token_id = int(self.config.meap_mask_token_id)
5895
+
5896
+ meap_consumed = False
5897
  if inputs_embeds is None:
5898
  if self.config.use_token_generator:
5899
+ # Without a later Spelling Bee epilogue, Leviathan owns the
5900
+ # final embedding write and selects the MEAP vector inside its
5901
+ # GEMM store. If SBE is active, its epilogue must own the final
5902
+ # selection so byte features cannot alter masked positions.
5903
+ fuse_meap_in_leviathan = meap_active and self.spelling_bee is None
5904
+ if fuse_meap_in_leviathan:
5905
+ inputs_embeds = self.token_generator(
5906
+ input_ids,
5907
+ meap_mask_embedding=meap_mask_embedding,
5908
+ meap_mask_token_id=meap_mask_token_id,
5909
+ )
5910
+ else:
5911
+ inputs_embeds = self.token_generator(input_ids)
5912
+ meap_consumed = fuse_meap_in_leviathan
5913
  else:
5914
  inputs_embeds = self.embed_tokens(input_ids)
5915
 
 
5918
  # caller; in that case SBE cannot run (no token_ids available) and is
5919
  # silently skipped — consistent with the standard embedding bypass path.
5920
  if self.spelling_bee is not None and input_ids is not None:
5921
+ inputs_embeds = self.spelling_bee(
5922
+ input_ids,
5923
+ inputs_embeds,
5924
+ meap_mask_embedding=(
5925
+ meap_mask_embedding if meap_active else None
5926
+ ),
5927
+ meap_mask_token_id=(meap_mask_token_id if meap_active else None),
5928
+ )
5929
+ meap_consumed = meap_active
5930
+
5931
+ # This must be the final operation in the input-embedding pipeline.
5932
+ # The clean/inference path is unchanged and does not depend on CCE.
5933
+ if meap_active and not meap_consumed:
5934
+ meap_positions = input_ids.eq(meap_mask_token_id).unsqueeze(-1)
5935
+ mask_embedding = meap_mask_embedding.to(
5936
+ device=inputs_embeds.device,
5937
+ dtype=inputs_embeds.dtype,
5938
+ ).view(1, 1, -1)
5939
+ inputs_embeds = torch.where(
5940
+ meap_positions,
5941
+ mask_embedding,
5942
+ inputs_embeds,
5943
+ )
5944
 
5945
  if position_ids is None:
5946
  if attention_mask is not None:
 
6271
  cce_impl="cce_kahan_full_c",
6272
  use_mile_loss=False,
6273
  mile_loss_gamma=1.0,
6274
+ mile_group_mask=None,
6275
  use_mu_loss=False,
6276
  mu_loss_lambda=1e-4,
6277
  return_loss_metrics=False,
 
6300
  mile_enabled=True,
6301
  mile_gamma=float(mile_loss_gamma),
6302
  )
6303
+ if mile_group_mask is not None:
6304
+ _require_extended_cce_options("mile_group_mask")
6305
+ extension_kwargs["mile_group_mask"] = mile_group_mask
6306
  if use_mu_loss:
6307
  _require_extended_cce_options("mu_loss_enabled", "mu_loss_lambda")
6308
  extension_kwargs.update(
 
7988
  _require_extended_cce_options("mile_enabled", "mile_gamma")
7989
  if bool(getattr(config, "use_mu_loss", False)):
7990
  _require_extended_cce_options("mu_loss_enabled", "mu_loss_lambda")
7991
+ # The optional MEAP backend is checked only when training actually
7992
+ # applies corruption. Clean training, evaluation, and inference remain
7993
+ # usable without cut-cross-entropy even when loading a MEAP checkpoint.
 
 
 
 
 
 
 
 
 
 
7994
  self._last_ntp_loss = None
7995
  self._last_ntp_ce_unweighted = None
7996
  self._last_mile_reweighting_delta = None
 
8085
  and self.training
8086
  and labels is not None
8087
  )
8088
+ meap_selected_mask = None
8089
  if meap_enabled:
8090
  if input_ids is None or inputs_embeds is not None:
8091
  raise ValueError(
 
8094
  )
8095
  if meap_mask_inputs is None:
8096
  raise ImportError(
8097
+ "Active MEAP training requires the extended cut-cross-entropy "
8098
+ "package. Evaluation and inference do not require it."
8099
+ )
8100
+ if "return_metrics" not in inspect.signature(meap_mask_inputs).parameters:
8101
+ raise ImportError(
8102
+ "The installed cut-cross-entropy package lacks MEAP metrics. "
8103
+ "Install the extended backend before MEAP training."
8104
  )
8105
  mask_token_id = getattr(self.config, "meap_mask_token_id", None)
8106
  if mask_token_id is None:
 
8124
  )
8125
  exclude_last = bool(getattr(self.config, "meap_exclude_last", True))
8126
  mask_ratio = float(getattr(self.config, "meap_mask_ratio", 0.15))
8127
+ return_meap_mask = bool(getattr(self.config, "use_mile_loss", False))
8128
+ meap_result = meap_mask_inputs(
8129
  input_ids,
8130
  int(mask_token_id),
8131
  enabled=True,
 
8133
  eligible_mask=eligible_mask,
8134
  seed=effective_meap_seed,
8135
  exclude_last=exclude_last,
8136
+ return_mask=return_meap_mask,
8137
  return_metrics=True,
8138
  implementation=str(
8139
  getattr(self.config, "meap_implementation", "triton")
8140
  ),
8141
  )
8142
+ if return_meap_mask:
8143
+ input_ids, meap_selected_mask, meap_metrics = meap_result
8144
+ else:
8145
+ input_ids, meap_metrics = meap_result
8146
  eligible_count = meap_metrics[0]
8147
  masked_count = meap_metrics[1]
8148
  self._last_meap_mask_fraction = (
 
8193
  inputs_embeds=inputs_embeds,
8194
  output_hidden_states=model_output_hidden_states,
8195
  output_tweo_activations=tweo_enabled,
8196
+ meap_active=meap_enabled,
8197
  return_dict=return_dict,
8198
  **kwargs,
8199
  )
 
8298
  mile_loss_gamma=float(
8299
  getattr(self.config, "mile_loss_gamma", 1.0)
8300
  ),
8301
+ mile_group_mask=(
8302
+ meap_selected_mask
8303
+ if meap_enabled
8304
+ and bool(getattr(self.config, "use_mile_loss", False))
8305
+ else None
8306
+ ),
8307
  use_mu_loss=bool(getattr(self.config, "use_mu_loss", False)),
8308
  mu_loss_lambda=float(
8309
  getattr(self.config, "mu_loss_lambda", 1e-4)