Update modeling_neollm.py
Browse files- modeling_neollm.py +768 -19
modeling_neollm.py
CHANGED
|
@@ -12,6 +12,12 @@ factorised attention log-priors (Litman & Guo, 2026), optional StackMemory
|
|
| 12 |
(Zhang et al., NeurIPS 2025), and optional HOLA-derived episodic memory for
|
| 13 |
the FlashAttention-2 IHA seq-expand path (Cui, 2026).
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
The FlashAttention training paths deliberately use dense right-padded
|
| 16 |
attention with a shared dtype and compilation contract across IHA and non-IHA.
|
| 17 |
See ``NeoLLMModel.forward`` for its padding/packing constraints.
|
|
@@ -56,6 +62,8 @@ References:
|
|
| 56 |
Towards Flexible and Stable Transformer Attention." arXiv:2602.23057.
|
| 57 |
Leviathan Generator: Batley & Saha (2026). "A Separable Architecture for
|
| 58 |
Continuous Token Representation in Language Models." arXiv:2601.22040.
|
|
|
|
|
|
|
| 59 |
KHRONOS: Batley & Saha (2025). "KHRONOS: a Kernel-Based Neural Architecture
|
| 60 |
for Rapid, Resource-Efficient Scientific Computation." arXiv:2505.13315.
|
| 61 |
Spelling Bee Embeddings: Rabe, Clymo & Dong (2026). "Spelling Bee
|
|
@@ -76,6 +84,7 @@ References:
|
|
| 76 |
|
| 77 |
import inspect
|
| 78 |
import math
|
|
|
|
| 79 |
from typing import Optional, Union, Tuple
|
| 80 |
|
| 81 |
import torch
|
|
@@ -127,6 +136,7 @@ except (ImportError, ModuleNotFoundError, AttributeError):
|
|
| 127 |
|
| 128 |
_LEV_KERNEL_AVAILABLE = False
|
| 129 |
_leviathan_embedding_compiler_safe = None
|
|
|
|
| 130 |
_leviathan_supports = None
|
| 131 |
try:
|
| 132 |
from cut_cross_entropy.leviathan import ( # type: ignore[import-not-found]
|
|
@@ -136,12 +146,40 @@ try:
|
|
| 136 |
)
|
| 137 |
|
| 138 |
_LEV_KERNEL_AVAILABLE = bool(_LEV_HAS_KERNEL_FORWARD)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
except (ImportError, ModuleNotFoundError, AttributeError):
|
| 140 |
# The model must remain usable with a plain CCE installation. In that
|
| 141 |
# environment the original reference LeviathanGenerator.forward below is
|
| 142 |
# the complete embedding path.
|
| 143 |
_LEV_HAS_KERNEL_FORWARD = False
|
| 144 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 145 |
|
| 146 |
def _leviathan_config_supported(config) -> bool:
|
| 147 |
"""Check LEV shapes without depending on NeoLLM's optional dtype field."""
|
|
@@ -185,6 +223,23 @@ from .configuration_neollm import NeoLLMConfig
|
|
| 185 |
|
| 186 |
from transformers import AutoConfig, AutoModel, AutoModelForCausalLM
|
| 187 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
logger = logging.get_logger(__name__)
|
| 189 |
_NEOLLM_FA_USE_TOP_LEFT_MASK = flash_attn_supports_top_left_mask()
|
| 190 |
|
|
@@ -443,6 +498,26 @@ class LeviathanGenerator(nn.Module):
|
|
| 443 |
torch.empty(self.num_modes, self.krank, self.hidden_size)
|
| 444 |
)
|
| 445 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 446 |
def _base_k_decompose(self, token_ids: torch.Tensor) -> torch.Tensor:
|
| 447 |
"""
|
| 448 |
Deterministic base-b decomposition: i -> (i_0, ..., i_{k-1}).
|
|
@@ -463,6 +538,48 @@ class LeviathanGenerator(nn.Module):
|
|
| 463 |
ids = ids // self.b
|
| 464 |
return coords
|
| 465 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 466 |
@staticmethod
|
| 467 |
def _normalize_bspline_basis(B: torch.Tensor) -> torch.Tensor:
|
| 468 |
"""
|
|
@@ -533,7 +650,8 @@ class LeviathanGenerator(nn.Module):
|
|
| 533 |
*,
|
| 534 |
meap_mask_embedding: Optional[torch.Tensor] = None,
|
| 535 |
meap_mask_token_id: Optional[int] = None,
|
| 536 |
-
|
|
|
|
| 537 |
"""
|
| 538 |
Generate embeddings from discrete token indices.
|
| 539 |
|
|
@@ -542,7 +660,10 @@ class LeviathanGenerator(nn.Module):
|
|
| 542 |
Args:
|
| 543 |
token_ids: (batch, seq_len) or (seq_len,).
|
| 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
|
|
@@ -562,7 +683,7 @@ class LeviathanGenerator(nn.Module):
|
|
| 562 |
f"({self.hidden_size},)"
|
| 563 |
)
|
| 564 |
|
| 565 |
-
|
| 566 |
self.use_leviathan_triton
|
| 567 |
and _LEV_KERNEL_AVAILABLE
|
| 568 |
and _leviathan_embedding_compiler_safe is not None
|
|
@@ -580,6 +701,37 @@ class LeviathanGenerator(nn.Module):
|
|
| 580 |
self.head_out_weight,
|
| 581 |
)
|
| 582 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 583 |
):
|
| 584 |
params = {
|
| 585 |
"codebooks": self.codebooks,
|
|
@@ -590,6 +742,23 @@ class LeviathanGenerator(nn.Module):
|
|
| 590 |
"head_out_weight": self.head_out_weight,
|
| 591 |
}
|
| 592 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 593 |
return _leviathan_embedding_compiler_safe(
|
| 594 |
token_ids,
|
| 595 |
params,
|
|
@@ -603,6 +772,8 @@ class LeviathanGenerator(nn.Module):
|
|
| 603 |
# implementation. A CUDA launch error must propagate: doing
|
| 604 |
# reference work after a failed launch can poison the next
|
| 605 |
# CUDA-graph/FlashAttention partition.
|
|
|
|
|
|
|
| 606 |
pass
|
| 607 |
|
| 608 |
target_dtype = self.codebooks.dtype
|
|
@@ -610,11 +781,7 @@ class LeviathanGenerator(nn.Module):
|
|
| 610 |
N = token_ids.numel()
|
| 611 |
|
| 612 |
# -- Stage 1: compositional codebook indexing -------------------------
|
| 613 |
-
|
| 614 |
-
coords_flat = coords.reshape(N, self.k)
|
| 615 |
-
z = torch.zeros(N, self.d_seed, device=token_ids.device, dtype=target_dtype)
|
| 616 |
-
for r in range(self.k):
|
| 617 |
-
z = z + self.codebooks[r][coords_flat[:, r]]
|
| 618 |
|
| 619 |
# -- Stages 2-4: per-head projection, spline expansion, sum ----------
|
| 620 |
e = torch.zeros(
|
|
@@ -655,11 +822,393 @@ class LeviathanGenerator(nn.Module):
|
|
| 655 |
meap_mask_embedding.to(device=e.device, dtype=e.dtype),
|
| 656 |
e,
|
| 657 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 658 |
return e
|
| 659 |
|
| 660 |
|
| 661 |
|
| 662 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 663 |
# ==================== ORIGINAL COMPONENTS ====================
|
| 664 |
|
| 665 |
|
|
@@ -4676,6 +5225,23 @@ class NeoLLMDecoderLayer(GradientCheckpointingLayer):
|
|
| 4676 |
)
|
| 4677 |
self.self_attn = NeoLLMAttention(config, layer_idx)
|
| 4678 |
self.mlp = NeoLLMMLP(config)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4679 |
if self.use_siamesenorm:
|
| 4680 |
self.input_layernorm = None
|
| 4681 |
self.post_attention_layernorm = None
|
|
@@ -4971,6 +5537,9 @@ class NeoLLMDecoderLayer(GradientCheckpointingLayer):
|
|
| 4971 |
output_attentions: Optional[bool] = False,
|
| 4972 |
repo_rope_args: Optional[Tuple[torch.Tensor, float]] = None,
|
| 4973 |
position_ids: Optional[torch.LongTensor] = None,
|
|
|
|
|
|
|
|
|
|
| 4974 |
**kwargs: Unpack[FlashAttentionKwargs],
|
| 4975 |
) -> Tuple:
|
| 4976 |
# SiameseNorm keeps two coupled streams with shared Attention/MLP
|
|
@@ -4990,6 +5559,7 @@ class NeoLLMDecoderLayer(GradientCheckpointingLayer):
|
|
| 4990 |
h_attn, stack_memory, stack_memory_mask = self.apply_stack_memory(
|
| 4991 |
h_attn, stack_memory, stack_memory_mask
|
| 4992 |
)
|
|
|
|
| 4993 |
|
| 4994 |
h_lns = self.lns_attn(h_attn) if self.use_lns else h_attn
|
| 4995 |
attn_out, attn_weights, self.current_layer_fan = self.self_attn(
|
|
@@ -5020,7 +5590,21 @@ class NeoLLMDecoderLayer(GradientCheckpointingLayer):
|
|
| 5020 |
h_lns2 = self.lns_mlp(h_mlp) if self.use_lns else h_mlp
|
| 5021 |
delta_m = self.mlp(h_lns2)
|
| 5022 |
|
| 5023 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5024 |
|
| 5025 |
x_next = x_after_attn + stream_scale * shared_update
|
| 5026 |
y_next = y_after_attn + shared_update
|
|
@@ -5031,6 +5615,8 @@ class NeoLLMDecoderLayer(GradientCheckpointingLayer):
|
|
| 5031 |
outputs = (x_next, y_next)
|
| 5032 |
if self.use_stack_memory:
|
| 5033 |
outputs += (stack_memory, stack_memory_mask)
|
|
|
|
|
|
|
| 5034 |
if output_attentions:
|
| 5035 |
outputs += (attn_weights,)
|
| 5036 |
return outputs
|
|
@@ -5046,6 +5632,9 @@ class NeoLLMDecoderLayer(GradientCheckpointingLayer):
|
|
| 5046 |
output_attentions: Optional[bool] = False,
|
| 5047 |
repo_rope_args: Optional[Tuple[torch.Tensor, float]] = None,
|
| 5048 |
position_ids: Optional[torch.LongTensor] = None,
|
|
|
|
|
|
|
|
|
|
| 5049 |
**kwargs: Unpack[FlashAttentionKwargs],
|
| 5050 |
) -> Tuple:
|
| 5051 |
# ── Snapshot input ────────────────────────────────────────────────
|
|
@@ -5083,6 +5672,9 @@ class NeoLLMDecoderLayer(GradientCheckpointingLayer):
|
|
| 5083 |
h_normed = h_attn
|
| 5084 |
else:
|
| 5085 |
h_normed = _apply_norm(self.input_layernorm, h_attn)
|
|
|
|
|
|
|
|
|
|
| 5086 |
|
| 5087 |
h_lns = self.lns_attn(h_normed) if self.use_lns else h_normed
|
| 5088 |
hidden_states, attn_weights, self.current_layer_fan = self.self_attn(
|
|
@@ -5131,6 +5723,20 @@ class NeoLLMDecoderLayer(GradientCheckpointingLayer):
|
|
| 5131 |
h_lns2 = self.lns_mlp(h_normed2) if self.use_lns else h_normed2
|
| 5132 |
delta_m = self.mlp(h_lns2)
|
| 5133 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5134 |
# ── Residual junction (MLP sublayer) ────��────────────────────────
|
| 5135 |
# LAuReL augments the base MLP residual (residual_mlp + delta_m).
|
| 5136 |
if self.use_laurel:
|
|
@@ -5148,6 +5754,8 @@ class NeoLLMDecoderLayer(GradientCheckpointingLayer):
|
|
| 5148 |
hidden_states = self.gpas_mlp(mlp_aug) if self.use_gpas else mlp_aug
|
| 5149 |
|
| 5150 |
outputs = (hidden_states,)
|
|
|
|
|
|
|
| 5151 |
if output_attentions:
|
| 5152 |
outputs += (attn_weights,)
|
| 5153 |
return outputs
|
|
@@ -5658,6 +6266,37 @@ class NeoLLMPreTrainedModel(PreTrainedModel):
|
|
| 5658 |
# outputs starts with the same variance as a single projection.
|
| 5659 |
out_std = self.config.initializer_range / math.sqrt(module.num_modes)
|
| 5660 |
nn.init.normal_(module.head_out_weight, mean=0.0, std=out_std)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5661 |
|
| 5662 |
elif isinstance(module, StackMemory):
|
| 5663 |
nn.init.normal_(
|
|
@@ -5892,6 +6531,15 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
|
|
| 5892 |
if (input_ids is None) ^ (inputs_embeds is not None):
|
| 5893 |
raise ValueError("Specify exactly one of input_ids or inputs_embeds")
|
| 5894 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5895 |
# ── Embedding stage ────────────────────────────────────────────────
|
| 5896 |
meap_mask_embedding = None
|
| 5897 |
meap_mask_token_id = None
|
|
@@ -5914,13 +6562,33 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
|
|
| 5914 |
# selection so byte features cannot alter masked positions.
|
| 5915 |
fuse_meap_in_leviathan = meap_active and self.spelling_bee is None
|
| 5916 |
if fuse_meap_in_leviathan:
|
| 5917 |
-
|
| 5918 |
-
|
| 5919 |
-
|
| 5920 |
-
|
| 5921 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5922 |
else:
|
| 5923 |
-
inputs_embeds =
|
| 5924 |
meap_consumed = fuse_meap_in_leviathan
|
| 5925 |
else:
|
| 5926 |
inputs_embeds = self.embed_tokens(input_ids)
|
|
@@ -5954,6 +6622,26 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
|
|
| 5954 |
inputs_embeds,
|
| 5955 |
)
|
| 5956 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5957 |
if position_ids is None:
|
| 5958 |
if attention_mask is not None:
|
| 5959 |
position_ids = attention_mask.to(dtype=torch.long).cumsum(-1) - 1
|
|
@@ -6018,6 +6706,10 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
|
|
| 6018 |
all_hidden_states = () if output_hidden_states else None
|
| 6019 |
all_attentions = () if output_attentions else None
|
| 6020 |
tweo_activations = () if output_tweo_activations else None
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6021 |
|
| 6022 |
# ── StackMemory state ─────────────────────────────────────────────
|
| 6023 |
# Same source-level idea as StackTrans:
|
|
@@ -6130,6 +6822,9 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
|
|
| 6130 |
output_attentions=output_attentions,
|
| 6131 |
repo_rope_args=repo_rope_args,
|
| 6132 |
position_ids=position_ids,
|
|
|
|
|
|
|
|
|
|
| 6133 |
**kwargs,
|
| 6134 |
)
|
| 6135 |
hidden_states = layer_outputs[0]
|
|
@@ -6151,6 +6846,9 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
|
|
| 6151 |
output_attentions=output_attentions,
|
| 6152 |
repo_rope_args=repo_rope_args,
|
| 6153 |
position_ids=position_ids,
|
|
|
|
|
|
|
|
|
|
| 6154 |
**kwargs,
|
| 6155 |
)
|
| 6156 |
hidden_states = layer_outputs[0]
|
|
@@ -6160,6 +6858,16 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
|
|
| 6160 |
if use_attn_res:
|
| 6161 |
attn_res_partial = hidden_states
|
| 6162 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6163 |
if output_attentions:
|
| 6164 |
all_attentions = all_attentions + (layer_outputs[extras_start],)
|
| 6165 |
extras_start += 1
|
|
@@ -6191,15 +6899,18 @@ class NeoLLMModel(NeoLLMPreTrainedModel):
|
|
| 6191 |
for v in [hidden_states, None, all_hidden_states, all_attentions]
|
| 6192 |
if v is not None
|
| 6193 |
)
|
|
|
|
|
|
|
| 6194 |
if output_tweo_activations:
|
| 6195 |
return base_outputs + (tweo_activations,)
|
| 6196 |
return base_outputs
|
| 6197 |
|
| 6198 |
-
outputs =
|
| 6199 |
last_hidden_state=hidden_states,
|
| 6200 |
past_key_values=None,
|
| 6201 |
hidden_states=all_hidden_states,
|
| 6202 |
attentions=all_attentions,
|
|
|
|
| 6203 |
)
|
| 6204 |
if output_tweo_activations:
|
| 6205 |
return outputs, tweo_activations
|
|
@@ -8031,6 +8742,7 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
|
|
| 8031 |
self._last_nextlat_loss = None
|
| 8032 |
self._last_nextlat_mse_loss = None
|
| 8033 |
self._last_nextlat_kl_loss = None
|
|
|
|
| 8034 |
self._last_total_loss = None
|
| 8035 |
|
| 8036 |
if config.use_token_generator:
|
|
@@ -8211,17 +8923,35 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
|
|
| 8211 |
)
|
| 8212 |
|
| 8213 |
# Unpack: model returns BaseModelOutputWithPast, or a tuple when
|
| 8214 |
-
# return_dict=False (with
|
|
|
|
| 8215 |
tweo_activations = None
|
| 8216 |
hidden_states_tuple = None
|
|
|
|
| 8217 |
if isinstance(model_out, tuple):
|
| 8218 |
outputs = model_out[0]
|
| 8219 |
if tweo_enabled and len(model_out) >= 2:
|
| 8220 |
tweo_activations = model_out[-1]
|
| 8221 |
if isinstance(outputs, torch.Tensor):
|
| 8222 |
hidden_states = outputs
|
| 8223 |
-
if
|
| 8224 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8225 |
elif isinstance(outputs, tuple):
|
| 8226 |
hidden_states = outputs[0]
|
| 8227 |
if len(outputs) > 2:
|
|
@@ -8229,10 +8959,12 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
|
|
| 8229 |
else:
|
| 8230 |
hidden_states = outputs.last_hidden_state
|
| 8231 |
hidden_states_tuple = outputs.hidden_states
|
|
|
|
| 8232 |
else:
|
| 8233 |
outputs = model_out
|
| 8234 |
hidden_states = outputs.last_hidden_state
|
| 8235 |
hidden_states_tuple = outputs.hidden_states
|
|
|
|
| 8236 |
|
| 8237 |
loss = None
|
| 8238 |
ntp_loss = None
|
|
@@ -8255,6 +8987,7 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
|
|
| 8255 |
nextlat_loss = None
|
| 8256 |
nextlat_mse_loss = None
|
| 8257 |
nextlat_kl_loss = None
|
|
|
|
| 8258 |
self._last_ntp_loss = None
|
| 8259 |
self._last_ntp_ce_unweighted = None
|
| 8260 |
self._last_mile_reweighting_delta = None
|
|
@@ -8280,6 +9013,7 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
|
|
| 8280 |
self._last_nextlat_loss = None
|
| 8281 |
self._last_nextlat_mse_loss = None
|
| 8282 |
self._last_nextlat_kl_loss = None
|
|
|
|
| 8283 |
self._last_total_loss = None
|
| 8284 |
if labels is not None:
|
| 8285 |
if self.ntp_loss_backend == "liger":
|
|
@@ -8513,6 +9247,15 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
|
|
| 8513 |
)
|
| 8514 |
loss = loss + nextlat_loss
|
| 8515 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8516 |
self._last_ntp_loss = ntp_loss.detach() if ntp_loss is not None else None
|
| 8517 |
self._last_tweo_loss = tweo_loss.detach() if tweo_loss is not None else None
|
| 8518 |
self._last_nitp_loss = nitp_loss.detach() if nitp_loss is not None else None
|
|
@@ -8608,6 +9351,9 @@ class NeoLLMForCausalLM(NeoLLMPreTrainedModel, GenerationMixin):
|
|
| 8608 |
self._last_nextlat_kl_loss = (
|
| 8609 |
nextlat_kl_loss.detach() if nextlat_kl_loss is not None else None
|
| 8610 |
)
|
|
|
|
|
|
|
|
|
|
| 8611 |
self._last_total_loss = loss.detach() if loss is not None else None
|
| 8612 |
logits = None
|
| 8613 |
else:
|
|
@@ -8635,6 +9381,9 @@ __all__ = [
|
|
| 8635 |
"NeoLLMPreTrainedModel",
|
| 8636 |
"NeoLLMConfig",
|
| 8637 |
"LeviathanGenerator",
|
|
|
|
|
|
|
|
|
|
| 8638 |
"SpellingBeeEmbedding",
|
| 8639 |
"StackMemory",
|
| 8640 |
"FANLayer",
|
|
|
|
| 12 |
(Zhang et al., NeurIPS 2025), and optional HOLA-derived episodic memory for
|
| 13 |
the FlashAttention-2 IHA seq-expand path (Cui, 2026).
|
| 14 |
|
| 15 |
+
When enabled, Leviathan-JTok/JTok-M uses reference PyTorch operations by
|
| 16 |
+
default. The runtime-only ``config.jtok_kernel_backend = "triton"`` opt-in
|
| 17 |
+
dispatches the same module parameters through the compatible CCE JTok/JTok-M
|
| 18 |
+
adapter. The default ``"torch"`` path is unchanged and the backend choice
|
| 19 |
+
does not add parameters or buffers to the checkpoint.
|
| 20 |
+
|
| 21 |
The FlashAttention training paths deliberately use dense right-padded
|
| 22 |
attention with a shared dtype and compilation contract across IHA and non-IHA.
|
| 23 |
See ``NeoLLMModel.forward`` for its padding/packing constraints.
|
|
|
|
| 62 |
Towards Flexible and Stable Transformer Attention." arXiv:2602.23057.
|
| 63 |
Leviathan Generator: Batley & Saha (2026). "A Separable Architecture for
|
| 64 |
Continuous Token Representation in Language Models." arXiv:2601.22040.
|
| 65 |
+
JTok: Yang et al. (2026). "JTok: On Token Embedding as another Axis of
|
| 66 |
+
Scaling Law via Joint Token Self-modulation." arXiv:2602.00800.
|
| 67 |
KHRONOS: Batley & Saha (2025). "KHRONOS: a Kernel-Based Neural Architecture
|
| 68 |
for Rapid, Resource-Efficient Scientific Computation." arXiv:2505.13315.
|
| 69 |
Spelling Bee Embeddings: Rabe, Clymo & Dong (2026). "Spelling Bee
|
|
|
|
| 84 |
|
| 85 |
import inspect
|
| 86 |
import math
|
| 87 |
+
from dataclasses import dataclass
|
| 88 |
from typing import Optional, Union, Tuple
|
| 89 |
|
| 90 |
import torch
|
|
|
|
| 136 |
|
| 137 |
_LEV_KERNEL_AVAILABLE = False
|
| 138 |
_leviathan_embedding_compiler_safe = None
|
| 139 |
+
_leviathan_embedding_with_seed_compiler_safe = None
|
| 140 |
_leviathan_supports = None
|
| 141 |
try:
|
| 142 |
from cut_cross_entropy.leviathan import ( # type: ignore[import-not-found]
|
|
|
|
| 146 |
)
|
| 147 |
|
| 148 |
_LEV_KERNEL_AVAILABLE = bool(_LEV_HAS_KERNEL_FORWARD)
|
| 149 |
+
try:
|
| 150 |
+
from cut_cross_entropy.leviathan import ( # type: ignore[import-not-found]
|
| 151 |
+
leviathan_embedding_with_seed_compiler_safe as _leviathan_embedding_with_seed_compiler_safe,
|
| 152 |
+
)
|
| 153 |
+
except (ImportError, ModuleNotFoundError, AttributeError):
|
| 154 |
+
# An older CCE package may provide the legacy Leviathan kernel without
|
| 155 |
+
# the opt-in JTok geometry bridge. Keep the legacy path available.
|
| 156 |
+
pass
|
| 157 |
except (ImportError, ModuleNotFoundError, AttributeError):
|
| 158 |
# The model must remain usable with a plain CCE installation. In that
|
| 159 |
# environment the original reference LeviathanGenerator.forward below is
|
| 160 |
# the complete embedding path.
|
| 161 |
_LEV_HAS_KERNEL_FORWARD = False
|
| 162 |
|
| 163 |
+
_LEV_GEOMETRY_KERNEL_AVAILABLE = (
|
| 164 |
+
_LEV_KERNEL_AVAILABLE
|
| 165 |
+
and _leviathan_embedding_with_seed_compiler_safe is not None
|
| 166 |
+
)
|
| 167 |
+
|
| 168 |
+
# JTok/JTok-M is a separate, strict opt-in path. Keep this import separate
|
| 169 |
+
# from the legacy Leviathan import above: an older CCE installation may expose
|
| 170 |
+
# the original Leviathan kernel but not the newer JTok adapter, and that must
|
| 171 |
+
# not silently disable the legacy path.
|
| 172 |
+
_JTOK_KERNEL_AVAILABLE = False
|
| 173 |
+
_apply_neollm_jtok = None
|
| 174 |
+
try:
|
| 175 |
+
from cut_cross_entropy.leviathan import ( # type: ignore[import-not-found]
|
| 176 |
+
apply_neollm_jtok as _apply_neollm_jtok,
|
| 177 |
+
)
|
| 178 |
+
|
| 179 |
+
_JTOK_KERNEL_AVAILABLE = True
|
| 180 |
+
except (ImportError, ModuleNotFoundError, AttributeError):
|
| 181 |
+
pass
|
| 182 |
+
|
| 183 |
|
| 184 |
def _leviathan_config_supported(config) -> bool:
|
| 185 |
"""Check LEV shapes without depending on NeoLLM's optional dtype field."""
|
|
|
|
| 223 |
|
| 224 |
from transformers import AutoConfig, AutoModel, AutoModelForCausalLM
|
| 225 |
|
| 226 |
+
|
| 227 |
+
@dataclass
|
| 228 |
+
class NeoLLMBaseModelOutputWithPast(BaseModelOutputWithPast):
|
| 229 |
+
"""Backbone output with compact, differentiable JTok-M statistics.
|
| 230 |
+
|
| 231 |
+
When JTok-M is active during training, ``jtokm_aux_stats`` contains one
|
| 232 |
+
``(P, n, T)`` tuple per decoder layer. ``P`` is the differentiable sum of
|
| 233 |
+
the router probabilities, ``n`` is the detached hard Top-K count, and
|
| 234 |
+
``T`` is the number of valid routed tokens. Keeping only these reductions
|
| 235 |
+
in the model output avoids retaining per-token router tensors until the
|
| 236 |
+
language-model loss is assembled.
|
| 237 |
+
"""
|
| 238 |
+
|
| 239 |
+
jtokm_aux_stats: Optional[
|
| 240 |
+
Tuple[Tuple[torch.Tensor, torch.Tensor, torch.Tensor], ...]
|
| 241 |
+
] = None
|
| 242 |
+
|
| 243 |
logger = logging.get_logger(__name__)
|
| 244 |
_NEOLLM_FA_USE_TOP_LEFT_MASK = flash_attn_supports_top_left_mask()
|
| 245 |
|
|
|
|
| 498 |
torch.empty(self.num_modes, self.krank, self.hidden_size)
|
| 499 |
)
|
| 500 |
|
| 501 |
+
# Shared continuous coordinate for Leviathan-JTok/JTok-M. The
|
| 502 |
+
# original JTok paper stores a separate V×D table per layer; NeoLLM
|
| 503 |
+
# replaces that table with a compact surface over one Leviathan seed.
|
| 504 |
+
# These bridge parameters are present only when JTok is requested, so
|
| 505 |
+
# the default Leviathan checkpoint/state-dict contract is unchanged.
|
| 506 |
+
if bool(getattr(config, "use_jtok", False)):
|
| 507 |
+
self.jtok_seed_proj_weight = nn.Parameter(
|
| 508 |
+
torch.empty(self.d_seed, self.d_seed)
|
| 509 |
+
)
|
| 510 |
+
self.jtok_seed_proj_bias = nn.Parameter(torch.zeros(self.d_seed))
|
| 511 |
+
self.jtok_seed_norm_weight = nn.Parameter(torch.ones(self.d_seed))
|
| 512 |
+
self.jtok_seed_norm_bias = nn.Parameter(torch.zeros(self.d_seed))
|
| 513 |
+
self.jtok_seed_norm_eps = 1e-5
|
| 514 |
+
else:
|
| 515 |
+
self.register_parameter("jtok_seed_proj_weight", None)
|
| 516 |
+
self.register_parameter("jtok_seed_proj_bias", None)
|
| 517 |
+
self.register_parameter("jtok_seed_norm_weight", None)
|
| 518 |
+
self.register_parameter("jtok_seed_norm_bias", None)
|
| 519 |
+
self.jtok_seed_norm_eps = 1e-5
|
| 520 |
+
|
| 521 |
def _base_k_decompose(self, token_ids: torch.Tensor) -> torch.Tensor:
|
| 522 |
"""
|
| 523 |
Deterministic base-b decomposition: i -> (i_0, ..., i_{k-1}).
|
|
|
|
| 538 |
ids = ids // self.b
|
| 539 |
return coords
|
| 540 |
|
| 541 |
+
def _codebook_seed(self, token_ids: torch.Tensor) -> torch.Tensor:
|
| 542 |
+
"""Return the differentiable compositional Leviathan seed ``z``."""
|
| 543 |
+
orig_shape = token_ids.shape
|
| 544 |
+
N = token_ids.numel()
|
| 545 |
+
coords = self._base_k_decompose(token_ids).reshape(N, self.k)
|
| 546 |
+
z = torch.zeros(
|
| 547 |
+
N,
|
| 548 |
+
self.d_seed,
|
| 549 |
+
device=token_ids.device,
|
| 550 |
+
dtype=self.codebooks.dtype,
|
| 551 |
+
)
|
| 552 |
+
for r in range(self.k):
|
| 553 |
+
z = z + self.codebooks[r][coords[:, r]]
|
| 554 |
+
return z.reshape(*orig_shape, self.d_seed)
|
| 555 |
+
|
| 556 |
+
def _jtok_coordinate_from_seed(self, z: torch.Tensor) -> torch.Tensor:
|
| 557 |
+
r"""Map a raw Leviathan seed to the shared bounded JTok coordinate.
|
| 558 |
+
|
| 559 |
+
The bridge is a single learned ``Linear → LayerNorm → sigmoid`` path
|
| 560 |
+
shared by all decoder layers. It preserves the smooth compositional
|
| 561 |
+
geometry of Leviathan while giving every JTok surface the same
|
| 562 |
+
coordinate system.
|
| 563 |
+
"""
|
| 564 |
+
if self.jtok_seed_proj_weight is None:
|
| 565 |
+
raise RuntimeError(
|
| 566 |
+
"JTok geometry requested but `use_jtok` was not enabled at "
|
| 567 |
+
"model construction time."
|
| 568 |
+
)
|
| 569 |
+
orig_shape = z.shape[:-1]
|
| 570 |
+
flat = z.reshape(-1, self.d_seed)
|
| 571 |
+
y = F.linear(
|
| 572 |
+
flat.to(self.jtok_seed_proj_weight.dtype),
|
| 573 |
+
self.jtok_seed_proj_weight,
|
| 574 |
+
self.jtok_seed_proj_bias,
|
| 575 |
+
).float()
|
| 576 |
+
mean = y.mean(dim=-1, keepdim=True)
|
| 577 |
+
var = y.var(dim=-1, keepdim=True, unbiased=False)
|
| 578 |
+
y = (y - mean) * torch.rsqrt(var + self.jtok_seed_norm_eps)
|
| 579 |
+
y = y * self.jtok_seed_norm_weight.float() + self.jtok_seed_norm_bias.float()
|
| 580 |
+
y = torch.sigmoid(0.5 * y).clamp(0.0, 1.0)
|
| 581 |
+
return y.to(z.dtype).reshape(*orig_shape, self.d_seed)
|
| 582 |
+
|
| 583 |
@staticmethod
|
| 584 |
def _normalize_bspline_basis(B: torch.Tensor) -> torch.Tensor:
|
| 585 |
"""
|
|
|
|
| 650 |
*,
|
| 651 |
meap_mask_embedding: Optional[torch.Tensor] = None,
|
| 652 |
meap_mask_token_id: Optional[int] = None,
|
| 653 |
+
return_jtok_geometry: bool = False,
|
| 654 |
+
) -> Union[torch.Tensor, Tuple[torch.Tensor, torch.Tensor]]:
|
| 655 |
"""
|
| 656 |
Generate embeddings from discrete token indices.
|
| 657 |
|
|
|
|
| 660 |
Args:
|
| 661 |
token_ids: (batch, seq_len) or (seq_len,).
|
| 662 |
Returns:
|
| 663 |
+
embeddings [*token_ids.shape, hidden_size]. When
|
| 664 |
+
``return_jtok_geometry=True``, returns ``(embeddings, z_tilde)``
|
| 665 |
+
where ``z_tilde`` is the shared differentiable Leviathan/JTok
|
| 666 |
+
coordinate with shape ``[*token_ids.shape, d_seed]``.
|
| 667 |
"""
|
| 668 |
has_meap = (
|
| 669 |
meap_mask_embedding is not None or meap_mask_token_id is not None
|
|
|
|
| 683 |
f"({self.hidden_size},)"
|
| 684 |
)
|
| 685 |
|
| 686 |
+
leviathan_kernel_ready = (
|
| 687 |
self.use_leviathan_triton
|
| 688 |
and _LEV_KERNEL_AVAILABLE
|
| 689 |
and _leviathan_embedding_compiler_safe is not None
|
|
|
|
| 701 |
self.head_out_weight,
|
| 702 |
)
|
| 703 |
)
|
| 704 |
+
)
|
| 705 |
+
require_leviathan_triton = bool(
|
| 706 |
+
getattr(self.config, "_require_leviathan_triton", False)
|
| 707 |
+
)
|
| 708 |
+
if require_leviathan_triton and not leviathan_kernel_ready:
|
| 709 |
+
raise RuntimeError(
|
| 710 |
+
"This run requires the Triton Leviathan kernel, but the "
|
| 711 |
+
"installed CCE package or the current geometry is not "
|
| 712 |
+
"supported. Refusing a reference fallback."
|
| 713 |
+
)
|
| 714 |
+
if (
|
| 715 |
+
return_jtok_geometry
|
| 716 |
+
and str(getattr(self.config, "jtok_kernel_backend", "torch"))
|
| 717 |
+
.strip()
|
| 718 |
+
.lower()
|
| 719 |
+
== "triton"
|
| 720 |
+
and not (leviathan_kernel_ready and _LEV_GEOMETRY_KERNEL_AVAILABLE)
|
| 721 |
+
):
|
| 722 |
+
raise RuntimeError(
|
| 723 |
+
"JTok Triton requires a supported CUDA Leviathan kernel and "
|
| 724 |
+
"the CCE geometry adapter; refusing a silent reference fallback."
|
| 725 |
+
)
|
| 726 |
+
|
| 727 |
+
# Ordinary Leviathan calls keep their existing embedding-only custom
|
| 728 |
+
# op. JTok additionally needs the raw compositional seed. The CCE
|
| 729 |
+
# adapter keeps the legacy kernel for the embedding and exposes only a
|
| 730 |
+
# small differentiable base-b/codebook bridge for the geometry; this
|
| 731 |
+
# avoids both a full reference-Leviathan detour and a silent cut in the
|
| 732 |
+
# JTok -> codebook gradient.
|
| 733 |
+
if (
|
| 734 |
+
leviathan_kernel_ready
|
| 735 |
):
|
| 736 |
params = {
|
| 737 |
"codebooks": self.codebooks,
|
|
|
|
| 742 |
"head_out_weight": self.head_out_weight,
|
| 743 |
}
|
| 744 |
try:
|
| 745 |
+
if return_jtok_geometry:
|
| 746 |
+
if not _LEV_GEOMETRY_KERNEL_AVAILABLE:
|
| 747 |
+
raise RuntimeError(
|
| 748 |
+
"JTok geometry requested with the Leviathan kernel, "
|
| 749 |
+
"but the installed CCE package lacks the geometry "
|
| 750 |
+
"adapter."
|
| 751 |
+
)
|
| 752 |
+
embedding, seed = _leviathan_embedding_with_seed_compiler_safe(
|
| 753 |
+
token_ids,
|
| 754 |
+
params,
|
| 755 |
+
self.config,
|
| 756 |
+
self.knot_grid,
|
| 757 |
+
mask_embedding=meap_mask_embedding,
|
| 758 |
+
mask_token_id=meap_mask_token_id,
|
| 759 |
+
)
|
| 760 |
+
embedding = embedding.reshape(*token_ids.shape, self.hidden_size)
|
| 761 |
+
return embedding, self._jtok_coordinate_from_seed(seed)
|
| 762 |
return _leviathan_embedding_compiler_safe(
|
| 763 |
token_ids,
|
| 764 |
params,
|
|
|
|
| 772 |
# implementation. A CUDA launch error must propagate: doing
|
| 773 |
# reference work after a failed launch can poison the next
|
| 774 |
# CUDA-graph/FlashAttention partition.
|
| 775 |
+
if require_leviathan_triton:
|
| 776 |
+
raise
|
| 777 |
pass
|
| 778 |
|
| 779 |
target_dtype = self.codebooks.dtype
|
|
|
|
| 781 |
N = token_ids.numel()
|
| 782 |
|
| 783 |
# -- Stage 1: compositional codebook indexing -------------------------
|
| 784 |
+
z = self._codebook_seed(token_ids).reshape(N, self.d_seed)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 785 |
|
| 786 |
# -- Stages 2-4: per-head projection, spline expansion, sum ----------
|
| 787 |
e = torch.zeros(
|
|
|
|
| 822 |
meap_mask_embedding.to(device=e.device, dtype=e.dtype),
|
| 823 |
e,
|
| 824 |
)
|
| 825 |
+
if return_jtok_geometry:
|
| 826 |
+
z_jtok = self._jtok_coordinate_from_seed(
|
| 827 |
+
z.reshape(*orig_shape, self.d_seed)
|
| 828 |
+
)
|
| 829 |
+
return e, z_jtok
|
| 830 |
return e
|
| 831 |
|
| 832 |
|
| 833 |
|
| 834 |
|
| 835 |
+
# ==================== LEVIATHAN-JTOK / JTOK-M MODULATION ====================
|
| 836 |
+
|
| 837 |
+
|
| 838 |
+
class LeviathanJTok(nn.Module):
|
| 839 |
+
r"""Pure-PyTorch Leviathan-coupled implementation of JTok and JTok-M.
|
| 840 |
+
|
| 841 |
+
``use_jtok=True, use_jtokm=False`` activates JTok. Enabling
|
| 842 |
+
``use_jtokm=True`` upgrades the same module to JTok-M; the configuration
|
| 843 |
+
validates that JTok and the Leviathan generator are active first.
|
| 844 |
+
|
| 845 |
+
The original JTok paper stores a token table per decoder layer. NeoLLM
|
| 846 |
+
keeps the JTok modulation/routing equations but replaces every table row
|
| 847 |
+
with a continuous CP-separable B-spline surface over the shared
|
| 848 |
+
Leviathan-derived coordinate ``z_tilde``:
|
| 849 |
+
|
| 850 |
+
.. math::
|
| 851 |
+
\hat{\Delta m}_x^l = \Delta m_x^l \odot
|
| 852 |
+
(1+s^l\odot\operatorname{Norm}(S_l(\tilde z_x)))
|
| 853 |
+
|
| 854 |
+
JTok-M evaluates the selected Top-K surfaces and injects their normalized
|
| 855 |
+
mixture as ``Delta_m + Delta_r`` with the paper's
|
| 856 |
+
``1/sqrt(2L)`` depth factor. The default implementation is reference
|
| 857 |
+
PyTorch. Setting the runtime-only ``config.jtok_kernel_backend`` to
|
| 858 |
+
``"triton"`` dispatches through the optional CCE JTok/JTok-M adapter while
|
| 859 |
+
preserving this module's parameters, state-dict layout, and decoder return
|
| 860 |
+
contract. The existing optional Leviathan embedding kernel remains a
|
| 861 |
+
separate path for ordinary embedding calls. The Triton choice is strict:
|
| 862 |
+
if the adapter is unavailable, forward raises instead of silently
|
| 863 |
+
changing the training or benchmark path back to Torch.
|
| 864 |
+
|
| 865 |
+
The temporary ``surface_chunk_tokens`` value bounds inference/evaluation
|
| 866 |
+
work tiles only. It is not a context-length, batch-size, or VRAM limit.
|
| 867 |
+
"""
|
| 868 |
+
|
| 869 |
+
def __init__(self, config: NeoLLMConfig, layer_idx: int):
|
| 870 |
+
super().__init__()
|
| 871 |
+
self.layer_idx = int(layer_idx)
|
| 872 |
+
self.use_mixture = bool(getattr(config, "use_jtokm", False))
|
| 873 |
+
self.jtok_kernel_backend = str(
|
| 874 |
+
getattr(config, "jtok_kernel_backend", "torch")
|
| 875 |
+
).strip().lower()
|
| 876 |
+
if self.jtok_kernel_backend not in {"torch", "triton"}:
|
| 877 |
+
raise ValueError(
|
| 878 |
+
"`jtok_kernel_backend` must be either `torch` or `triton`."
|
| 879 |
+
)
|
| 880 |
+
if self.jtok_kernel_backend == "triton" and not _JTOK_KERNEL_AVAILABLE:
|
| 881 |
+
raise RuntimeError(
|
| 882 |
+
"`jtok_kernel_backend='triton'` was requested, but the "
|
| 883 |
+
"installed CCE package does not expose `apply_neollm_jtok`. "
|
| 884 |
+
"Install the compatible JTok/JTok-M CCE implementation or "
|
| 885 |
+
"set `jtok_kernel_backend='torch'`."
|
| 886 |
+
)
|
| 887 |
+
self.hidden_size = int(config.hidden_size)
|
| 888 |
+
self.d_seed = int(config.generator_d_seed)
|
| 889 |
+
self.num_knots = int(config.generator_num_knots)
|
| 890 |
+
self.num_modes = int(getattr(config, "jtok_num_modes", 4))
|
| 891 |
+
self.num_experts = (
|
| 892 |
+
int(getattr(config, "jtok_num_experts", 5))
|
| 893 |
+
if self.use_mixture
|
| 894 |
+
else 1
|
| 895 |
+
)
|
| 896 |
+
self.top_k = (
|
| 897 |
+
int(getattr(config, "jtok_top_k", 2))
|
| 898 |
+
if self.use_mixture
|
| 899 |
+
else 1
|
| 900 |
+
)
|
| 901 |
+
self.norm_eps = float(getattr(config, "jtok_norm_eps", 1e-6))
|
| 902 |
+
# Work-tile size only. The optional attribute is intentionally read
|
| 903 |
+
# with getattr so older config.json files remain loadable.
|
| 904 |
+
self.surface_chunk_tokens = max(
|
| 905 |
+
int(getattr(config, "jtok_surface_chunk_tokens", 2048)), 1
|
| 906 |
+
)
|
| 907 |
+
self.jtokm_residual_scale = 1.0 / math.sqrt(
|
| 908 |
+
2.0 * float(config.num_hidden_layers)
|
| 909 |
+
)
|
| 910 |
+
|
| 911 |
+
self.register_buffer(
|
| 912 |
+
"knot_grid",
|
| 913 |
+
torch.linspace(0.0, 1.0, self.num_knots),
|
| 914 |
+
persistent=True,
|
| 915 |
+
)
|
| 916 |
+
|
| 917 |
+
# Continuous replacement for the paper's per-token tables. These
|
| 918 |
+
# remain ordinary Parameters so both their surface and the shared
|
| 919 |
+
# Leviathan codebooks receive gradients during pretraining.
|
| 920 |
+
self.spline_coeff = nn.Parameter(
|
| 921 |
+
torch.empty(
|
| 922 |
+
self.num_experts,
|
| 923 |
+
self.num_modes,
|
| 924 |
+
self.d_seed,
|
| 925 |
+
self.num_knots,
|
| 926 |
+
)
|
| 927 |
+
)
|
| 928 |
+
self.W_out = nn.Parameter(
|
| 929 |
+
torch.empty(self.num_experts, self.num_modes, self.hidden_size)
|
| 930 |
+
)
|
| 931 |
+
self.W_res = nn.Parameter(
|
| 932 |
+
torch.empty(self.num_experts, self.d_seed, self.hidden_size)
|
| 933 |
+
)
|
| 934 |
+
self.scaler = nn.Parameter(torch.ones(self.hidden_size))
|
| 935 |
+
self.router = (
|
| 936 |
+
nn.Linear(self.hidden_size, self.num_experts, bias=False)
|
| 937 |
+
if self.use_mixture
|
| 938 |
+
else None
|
| 939 |
+
)
|
| 940 |
+
|
| 941 |
+
def _bspline_basis(self, z_flat: torch.Tensor) -> torch.Tensor:
|
| 942 |
+
"""Evaluate the normalized quadratic B-spline basis in FP32."""
|
| 943 |
+
scale = float(self.num_knots - 1)
|
| 944 |
+
z32 = z_flat.float().unsqueeze(-1)
|
| 945 |
+
grid = self.knot_grid.float().view(1, 1, -1)
|
| 946 |
+
d = (z32 - grid).abs() * scale
|
| 947 |
+
B = torch.where(
|
| 948 |
+
d < 0.5,
|
| 949 |
+
0.75 - d.square(),
|
| 950 |
+
torch.where(
|
| 951 |
+
d < 1.5,
|
| 952 |
+
0.5 * (1.5 - d).square(),
|
| 953 |
+
torch.zeros_like(d),
|
| 954 |
+
),
|
| 955 |
+
)
|
| 956 |
+
return B / B.sum(dim=-1, keepdim=True).clamp_min(1e-12)
|
| 957 |
+
|
| 958 |
+
def _eval_surfaces(
|
| 959 |
+
self,
|
| 960 |
+
z_flat: torch.Tensor,
|
| 961 |
+
target_dtype: torch.dtype,
|
| 962 |
+
) -> torch.Tensor:
|
| 963 |
+
"""Evaluate all continuous surfaces, returning ``[N, E, D]``."""
|
| 964 |
+
B = self._bspline_basis(z_flat)
|
| 965 |
+
phi = torch.einsum("emrg,nrg->nemr", self.spline_coeff.float(), B)
|
| 966 |
+
log_mag = torch.log(phi.abs() + 1e-9).sum(dim=-1)
|
| 967 |
+
num_neg = (phi < 0).to(torch.int32).sum(dim=-1)
|
| 968 |
+
prod_sign = 1.0 - 2.0 * (num_neg % 2).float()
|
| 969 |
+
modes = (prod_sign * torch.exp(log_mag)).to(target_dtype)
|
| 970 |
+
out_modes = torch.einsum(
|
| 971 |
+
"nem,emd->ned", modes, self.W_out.to(target_dtype)
|
| 972 |
+
)
|
| 973 |
+
out_res = torch.einsum(
|
| 974 |
+
"nr,erd->ned",
|
| 975 |
+
z_flat.to(target_dtype),
|
| 976 |
+
self.W_res.to(target_dtype),
|
| 977 |
+
)
|
| 978 |
+
return out_modes + out_res
|
| 979 |
+
|
| 980 |
+
def _eval_selected_surfaces(
|
| 981 |
+
self,
|
| 982 |
+
z_flat: torch.Tensor,
|
| 983 |
+
expert_idx: torch.Tensor,
|
| 984 |
+
target_dtype: torch.dtype,
|
| 985 |
+
) -> torch.Tensor:
|
| 986 |
+
"""Evaluate only the selected expert surfaces for each token."""
|
| 987 |
+
if expert_idx.ndim != 2:
|
| 988 |
+
raise ValueError("expert_idx must have shape [tokens, selected_experts]")
|
| 989 |
+
N, selected_k = expert_idx.shape
|
| 990 |
+
basis_all = self._bspline_basis(z_flat)
|
| 991 |
+
selected_flat = torch.zeros(
|
| 992 |
+
N * selected_k,
|
| 993 |
+
self.hidden_size,
|
| 994 |
+
dtype=target_dtype,
|
| 995 |
+
device=z_flat.device,
|
| 996 |
+
)
|
| 997 |
+
# Top-K indices are unique per token, so each flattened destination is
|
| 998 |
+
# written once. This avoids materializing [N,K,M,D] or [N,K,R,D].
|
| 999 |
+
for expert_idx_value in range(self.num_experts):
|
| 1000 |
+
token_idx, slot_idx = torch.where(expert_idx == expert_idx_value)
|
| 1001 |
+
flat_idx = token_idx * selected_k + slot_idx
|
| 1002 |
+
z_selected = z_flat[token_idx]
|
| 1003 |
+
B = basis_all[token_idx]
|
| 1004 |
+
coeff = self.spline_coeff[expert_idx_value].float()
|
| 1005 |
+
phi = torch.einsum("sdg,mdg->smd", B, coeff)
|
| 1006 |
+
log_mag = torch.log(phi.abs() + 1e-9).sum(dim=-1)
|
| 1007 |
+
num_neg = (phi < 0).to(torch.int32).sum(dim=-1)
|
| 1008 |
+
prod_sign = 1.0 - 2.0 * (num_neg % 2).float()
|
| 1009 |
+
modes = prod_sign * torch.exp(log_mag)
|
| 1010 |
+
values = modes.to(target_dtype) @ self.W_out[
|
| 1011 |
+
expert_idx_value
|
| 1012 |
+
].to(target_dtype)
|
| 1013 |
+
values = values + z_selected.to(target_dtype) @ self.W_res[
|
| 1014 |
+
expert_idx_value
|
| 1015 |
+
].to(target_dtype)
|
| 1016 |
+
selected_flat = selected_flat.index_copy(0, flat_idx, values)
|
| 1017 |
+
return selected_flat.reshape(N, selected_k, self.hidden_size)
|
| 1018 |
+
|
| 1019 |
+
def _eval_selected_surfaces_tiled(
|
| 1020 |
+
self,
|
| 1021 |
+
z_flat: torch.Tensor,
|
| 1022 |
+
expert_idx: torch.Tensor,
|
| 1023 |
+
target_dtype: torch.dtype,
|
| 1024 |
+
) -> torch.Tensor:
|
| 1025 |
+
"""Route-first surface evaluation with bounded temporary token tiles."""
|
| 1026 |
+
if z_flat.shape[0] != expert_idx.shape[0]:
|
| 1027 |
+
raise ValueError("z_flat and expert_idx must contain the same token count")
|
| 1028 |
+
chunks = []
|
| 1029 |
+
for start in range(0, z_flat.shape[0], self.surface_chunk_tokens):
|
| 1030 |
+
stop = min(start + self.surface_chunk_tokens, z_flat.shape[0])
|
| 1031 |
+
chunks.append(
|
| 1032 |
+
self._eval_selected_surfaces(
|
| 1033 |
+
z_flat[start:stop], expert_idx[start:stop], target_dtype
|
| 1034 |
+
)
|
| 1035 |
+
)
|
| 1036 |
+
if not chunks:
|
| 1037 |
+
return z_flat.new_empty((0, expert_idx.shape[1], self.hidden_size))
|
| 1038 |
+
return torch.cat(chunks, dim=0)
|
| 1039 |
+
|
| 1040 |
+
def _eval_selected_surfaces_dense_tiled(
|
| 1041 |
+
self,
|
| 1042 |
+
z_flat: torch.Tensor,
|
| 1043 |
+
expert_idx: torch.Tensor,
|
| 1044 |
+
target_dtype: torch.dtype,
|
| 1045 |
+
) -> torch.Tensor:
|
| 1046 |
+
"""Compile-friendly selected evaluation over bounded dense tiles."""
|
| 1047 |
+
if z_flat.shape[0] != expert_idx.shape[0]:
|
| 1048 |
+
raise ValueError("z_flat and expert_idx must contain the same token count")
|
| 1049 |
+
chunks = []
|
| 1050 |
+
for start in range(0, z_flat.shape[0], self.surface_chunk_tokens):
|
| 1051 |
+
stop = min(start + self.surface_chunk_tokens, z_flat.shape[0])
|
| 1052 |
+
surfaces = self._eval_surfaces(z_flat[start:stop], target_dtype)
|
| 1053 |
+
gather_idx = expert_idx[start:stop].unsqueeze(-1).expand(
|
| 1054 |
+
stop - start, expert_idx.shape[1], self.hidden_size
|
| 1055 |
+
)
|
| 1056 |
+
chunks.append(surfaces.gather(1, gather_idx))
|
| 1057 |
+
if not chunks:
|
| 1058 |
+
return z_flat.new_empty((0, expert_idx.shape[1], self.hidden_size))
|
| 1059 |
+
return torch.cat(chunks, dim=0)
|
| 1060 |
+
|
| 1061 |
+
def _l2_normalize(self, x: torch.Tensor) -> torch.Tensor:
|
| 1062 |
+
return x / (x.norm(dim=-1, keepdim=True) + self.norm_eps)
|
| 1063 |
+
|
| 1064 |
+
def _valid_mask(
|
| 1065 |
+
self,
|
| 1066 |
+
valid_mask: Optional[torch.Tensor],
|
| 1067 |
+
N: int,
|
| 1068 |
+
device: torch.device,
|
| 1069 |
+
) -> torch.Tensor:
|
| 1070 |
+
if valid_mask is None:
|
| 1071 |
+
return torch.ones(N, device=device, dtype=torch.bool)
|
| 1072 |
+
return valid_mask.reshape(N).to(device=device, dtype=torch.bool)
|
| 1073 |
+
|
| 1074 |
+
def _jtokm_aux_stats(
|
| 1075 |
+
self,
|
| 1076 |
+
logits: torch.Tensor,
|
| 1077 |
+
topk_idx: torch.Tensor,
|
| 1078 |
+
mask: torch.Tensor,
|
| 1079 |
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 1080 |
+
"""Return differentiable probability and hard Top-K count reductions."""
|
| 1081 |
+
aux_sigmoid = torch.sigmoid(logits.float())
|
| 1082 |
+
# Normalize only the auxiliary probability branch. The forward
|
| 1083 |
+
# mixture remains selected-only Sigmoid+TopK, as in the source design.
|
| 1084 |
+
prob_all = aux_sigmoid / aux_sigmoid.sum(
|
| 1085 |
+
dim=-1, keepdim=True
|
| 1086 |
+
).clamp_min(self.norm_eps)
|
| 1087 |
+
valid32 = mask.to(prob_all.dtype).unsqueeze(-1)
|
| 1088 |
+
p_sum = (prob_all * valid32).sum(dim=0)
|
| 1089 |
+
onehot = torch.zeros_like(prob_all).scatter(1, topk_idx, 1.0)
|
| 1090 |
+
f_sum = (onehot * valid32).sum(dim=0).detach()
|
| 1091 |
+
T = mask.sum().to(dtype=torch.float32).detach()
|
| 1092 |
+
return p_sum, f_sum, T
|
| 1093 |
+
|
| 1094 |
+
def forward(
|
| 1095 |
+
self,
|
| 1096 |
+
delta_m: torch.Tensor,
|
| 1097 |
+
z_tilde: torch.Tensor,
|
| 1098 |
+
*,
|
| 1099 |
+
router_state: Optional[torch.Tensor] = None,
|
| 1100 |
+
valid_mask: Optional[torch.Tensor] = None,
|
| 1101 |
+
compute_aux: bool = False,
|
| 1102 |
+
) -> Tuple[
|
| 1103 |
+
torch.Tensor,
|
| 1104 |
+
Optional[Tuple[torch.Tensor, torch.Tensor, torch.Tensor]],
|
| 1105 |
+
]:
|
| 1106 |
+
"""Apply JTok/JTok-M and optionally return training-only router stats."""
|
| 1107 |
+
if self.jtok_kernel_backend == "triton":
|
| 1108 |
+
# Dispatch only: this branch does not change compiler mode,
|
| 1109 |
+
# CUDA-graph policy, training state, optimizer behavior, or any
|
| 1110 |
+
# model-global state. The adapter returns the same
|
| 1111 |
+
# ``(updated_delta, compact_router_stats)`` contract as the Torch
|
| 1112 |
+
# implementation, so the surrounding pretraining graph remains
|
| 1113 |
+
# unchanged when the backend is switched.
|
| 1114 |
+
if _apply_neollm_jtok is None:
|
| 1115 |
+
raise RuntimeError(
|
| 1116 |
+
"JTok Triton adapter became unavailable after module "
|
| 1117 |
+
"construction; refusing an implicit Torch fallback."
|
| 1118 |
+
)
|
| 1119 |
+
return _apply_neollm_jtok(
|
| 1120 |
+
self,
|
| 1121 |
+
delta_m,
|
| 1122 |
+
z_tilde,
|
| 1123 |
+
router_state=router_state,
|
| 1124 |
+
valid_mask=valid_mask,
|
| 1125 |
+
compute_aux=bool(compute_aux and self.use_mixture),
|
| 1126 |
+
backend="triton",
|
| 1127 |
+
)
|
| 1128 |
+
|
| 1129 |
+
orig_shape = delta_m.shape
|
| 1130 |
+
N = delta_m.numel() // self.hidden_size
|
| 1131 |
+
dm = delta_m.reshape(N, self.hidden_size)
|
| 1132 |
+
z = z_tilde.reshape(N, self.d_seed)
|
| 1133 |
+
mask = self._valid_mask(valid_mask, N, dm.device)
|
| 1134 |
+
mask_f = mask.to(dm.dtype).unsqueeze(-1)
|
| 1135 |
+
|
| 1136 |
+
if not self.use_mixture:
|
| 1137 |
+
surfaces = self._eval_surfaces(z, dm.dtype)
|
| 1138 |
+
token_direction = self._l2_normalize(surfaces[:, 0, :])
|
| 1139 |
+
gate = 1.0 + self.scaler.to(dm.dtype) * token_direction
|
| 1140 |
+
gate = torch.where(mask.unsqueeze(-1), gate, torch.ones_like(gate))
|
| 1141 |
+
return (dm * gate).reshape(orig_shape), None
|
| 1142 |
+
|
| 1143 |
+
if router_state is None:
|
| 1144 |
+
raise ValueError("JTok-M requires a pre-attention `router_state`.")
|
| 1145 |
+
h = router_state.reshape(N, self.hidden_size)
|
| 1146 |
+
h = h * torch.rsqrt(h.square().mean(dim=-1, keepdim=True) + self.norm_eps)
|
| 1147 |
+
logits = self.router(h)
|
| 1148 |
+
topk_vals, topk_idx = torch.topk(logits, self.top_k, dim=-1)
|
| 1149 |
+
selected_prob = torch.sigmoid(topk_vals)
|
| 1150 |
+
weights = selected_prob / selected_prob.sum(
|
| 1151 |
+
dim=-1, keepdim=True
|
| 1152 |
+
).clamp_min(self.norm_eps)
|
| 1153 |
+
|
| 1154 |
+
# Match the Torch reference semantics: keep the small dense path for
|
| 1155 |
+
# gradient-enabled training and use route-first tiles for large eager
|
| 1156 |
+
# evaluation. Both routes implement the same surface equations.
|
| 1157 |
+
if N <= self.surface_chunk_tokens or torch.is_grad_enabled():
|
| 1158 |
+
surfaces = self._eval_surfaces(z, dm.dtype)
|
| 1159 |
+
gather_idx = topk_idx.unsqueeze(-1).expand(
|
| 1160 |
+
N, self.top_k, self.hidden_size
|
| 1161 |
+
)
|
| 1162 |
+
selected = surfaces.gather(1, gather_idx)
|
| 1163 |
+
elif getattr(torch.compiler, "is_compiling", lambda: False)():
|
| 1164 |
+
selected = self._eval_selected_surfaces_dense_tiled(
|
| 1165 |
+
z, topk_idx, dm.dtype
|
| 1166 |
+
)
|
| 1167 |
+
else:
|
| 1168 |
+
selected = self._eval_selected_surfaces_tiled(
|
| 1169 |
+
z, topk_idx, dm.dtype
|
| 1170 |
+
)
|
| 1171 |
+
|
| 1172 |
+
mixed = (weights.unsqueeze(-1) * selected).sum(dim=1)
|
| 1173 |
+
delta_r = (
|
| 1174 |
+
self.jtokm_residual_scale
|
| 1175 |
+
* self.scaler.to(dm.dtype)
|
| 1176 |
+
* self._l2_normalize(mixed)
|
| 1177 |
+
)
|
| 1178 |
+
delta_r = delta_r * mask_f
|
| 1179 |
+
shared_update = dm + delta_r
|
| 1180 |
+
aux_stats = self._jtokm_aux_stats(logits, topk_idx, mask) if compute_aux else None
|
| 1181 |
+
return shared_update.reshape(orig_shape), aux_stats
|
| 1182 |
+
|
| 1183 |
+
|
| 1184 |
+
def compute_jtokm_aux_loss(
|
| 1185 |
+
aux_stats_list: Tuple[Tuple[torch.Tensor, torch.Tensor, torch.Tensor], ...],
|
| 1186 |
+
*,
|
| 1187 |
+
n_e: int,
|
| 1188 |
+
top_k: int,
|
| 1189 |
+
weight: float,
|
| 1190 |
+
) -> torch.Tensor:
|
| 1191 |
+
r"""Compute the JTok-M load-balancing objective from compact reductions.
|
| 1192 |
+
|
| 1193 |
+
For each layer, ``p_i=P_i/T`` and ``f_i=n_i/(T*K)``; the returned value is
|
| 1194 |
+
the layer-average of ``weight * n_e * sum_i(p_i*f_i)``. ``P_i`` remains
|
| 1195 |
+
attached to the router graph while hard Top-K counts are detached.
|
| 1196 |
+
"""
|
| 1197 |
+
if not aux_stats_list:
|
| 1198 |
+
raise ValueError("JTok-M auxiliary loss requires non-empty router stats.")
|
| 1199 |
+
layer_losses = []
|
| 1200 |
+
for p_sum, f_sum, T in aux_stats_list:
|
| 1201 |
+
denom_t = T.to(device=p_sum.device, dtype=torch.float32).clamp_min(1.0)
|
| 1202 |
+
p_i = p_sum.float() / denom_t
|
| 1203 |
+
f_i = f_sum.to(device=p_sum.device, dtype=torch.float32) / (
|
| 1204 |
+
denom_t * float(top_k)
|
| 1205 |
+
)
|
| 1206 |
+
layer_losses.append(float(n_e) * (p_i * f_i).sum())
|
| 1207 |
+
mean_loss = torch.stack(layer_losses).mean()
|
| 1208 |
+
lambda_t = torch.as_tensor(weight, device=mean_loss.device, dtype=torch.float32)
|
| 1209 |
+
return lambda_t * mean_loss
|
| 1210 |
+
|
| 1211 |
+
|
| 1212 |
# ==================== ORIGINAL COMPONENTS ====================
|
| 1213 |
|
| 1214 |
|
|
|
|
| 5225 |
)
|
| 5226 |
self.self_attn = NeoLLMAttention(config, layer_idx)
|
| 5227 |
self.mlp = NeoLLMMLP(config)
|
| 5228 |
+
# JTok/JTok-M operates on the shared MLP update. It is instantiated
|
| 5229 |
+
# only when the corresponding config flag is active; the default
|
| 5230 |
+
# training graph therefore keeps the original residual topology.
|
| 5231 |
+
self.use_jtok = bool(getattr(config, "use_jtok", False))
|
| 5232 |
+
self.use_jtokm = bool(getattr(config, "use_jtokm", False))
|
| 5233 |
+
if self.use_jtokm and not self.use_jtok:
|
| 5234 |
+
# ``NeoLLMConfig`` validates this relationship, but decoder layers
|
| 5235 |
+
# are also constructed directly by a few checkpoint conversion and
|
| 5236 |
+
# test paths. Fail explicitly there instead of silently dropping
|
| 5237 |
+
# JTok-M while leaving the rest of the model apparently valid.
|
| 5238 |
+
raise ValueError(
|
| 5239 |
+
"`use_jtokm=True` requires `use_jtok=True`; enable the base "
|
| 5240 |
+
"JTok path before enabling JTok-M."
|
| 5241 |
+
)
|
| 5242 |
+
self.jtok = (
|
| 5243 |
+
LeviathanJTok(config, layer_idx) if self.use_jtok else None
|
| 5244 |
+
)
|
| 5245 |
if self.use_siamesenorm:
|
| 5246 |
self.input_layernorm = None
|
| 5247 |
self.post_attention_layernorm = None
|
|
|
|
| 5537 |
output_attentions: Optional[bool] = False,
|
| 5538 |
repo_rope_args: Optional[Tuple[torch.Tensor, float]] = None,
|
| 5539 |
position_ids: Optional[torch.LongTensor] = None,
|
| 5540 |
+
jtok_z_tilde: Optional[torch.Tensor] = None,
|
| 5541 |
+
jtok_valid_mask: Optional[torch.Tensor] = None,
|
| 5542 |
+
jtok_compute_aux: bool = False,
|
| 5543 |
**kwargs: Unpack[FlashAttentionKwargs],
|
| 5544 |
) -> Tuple:
|
| 5545 |
# SiameseNorm keeps two coupled streams with shared Attention/MLP
|
|
|
|
| 5559 |
h_attn, stack_memory, stack_memory_mask = self.apply_stack_memory(
|
| 5560 |
h_attn, stack_memory, stack_memory_mask
|
| 5561 |
)
|
| 5562 |
+
jtok_router_state = h_attn
|
| 5563 |
|
| 5564 |
h_lns = self.lns_attn(h_attn) if self.use_lns else h_attn
|
| 5565 |
attn_out, attn_weights, self.current_layer_fan = self.self_attn(
|
|
|
|
| 5590 |
h_lns2 = self.lns_mlp(h_mlp) if self.use_lns else h_mlp
|
| 5591 |
delta_m = self.mlp(h_lns2)
|
| 5592 |
|
| 5593 |
+
jtok_aux_stats = None
|
| 5594 |
+
if self.jtok is not None:
|
| 5595 |
+
if jtok_z_tilde is None:
|
| 5596 |
+
raise ValueError(
|
| 5597 |
+
"Active JTok/JTok-M requires Leviathan `jtok_z_tilde`."
|
| 5598 |
+
)
|
| 5599 |
+
shared_update, jtok_aux_stats = self.jtok(
|
| 5600 |
+
delta_m,
|
| 5601 |
+
jtok_z_tilde,
|
| 5602 |
+
router_state=jtok_router_state if self.use_jtokm else None,
|
| 5603 |
+
valid_mask=jtok_valid_mask,
|
| 5604 |
+
compute_aux=bool(jtok_compute_aux and self.use_jtokm),
|
| 5605 |
+
)
|
| 5606 |
+
else:
|
| 5607 |
+
shared_update = delta_m
|
| 5608 |
|
| 5609 |
x_next = x_after_attn + stream_scale * shared_update
|
| 5610 |
y_next = y_after_attn + shared_update
|
|
|
|
| 5615 |
outputs = (x_next, y_next)
|
| 5616 |
if self.use_stack_memory:
|
| 5617 |
outputs += (stack_memory, stack_memory_mask)
|
| 5618 |
+
if jtok_aux_stats is not None:
|
| 5619 |
+
outputs += jtok_aux_stats
|
| 5620 |
if output_attentions:
|
| 5621 |
outputs += (attn_weights,)
|
| 5622 |
return outputs
|
|
|
|
| 5632 |
output_attentions: Optional[bool] = False,
|
| 5633 |
repo_rope_args: Optional[Tuple[torch.Tensor, float]] = None,
|
| 5634 |
position_ids: Optional[torch.LongTensor] = None,
|
| 5635 |
+
jtok_z_tilde: Optional[torch.Tensor] = None,
|
| 5636 |
+
jtok_valid_mask: Optional[torch.Tensor] = None,
|
| 5637 |
+
jtok_compute_aux: bool = False,
|
| 5638 |
**kwargs: Unpack[FlashAttentionKwargs],
|
| 5639 |
) -> Tuple:
|
| 5640 |
# ── Snapshot input ────────────────────────────────────────────────
|
|
|
|
| 5672 |
h_normed = h_attn
|
| 5673 |
else:
|
| 5674 |
h_normed = _apply_norm(self.input_layernorm, h_attn)
|
| 5675 |
+
# JTok-M follows the source design and routes from the fused
|
| 5676 |
+
# pre-attention contextual state, before the attention output is added.
|
| 5677 |
+
jtok_router_state = h_normed
|
| 5678 |
|
| 5679 |
h_lns = self.lns_attn(h_normed) if self.use_lns else h_normed
|
| 5680 |
hidden_states, attn_weights, self.current_layer_fan = self.self_attn(
|
|
|
|
| 5723 |
h_lns2 = self.lns_mlp(h_normed2) if self.use_lns else h_normed2
|
| 5724 |
delta_m = self.mlp(h_lns2)
|
| 5725 |
|
| 5726 |
+
jtok_aux_stats = None
|
| 5727 |
+
if self.jtok is not None:
|
| 5728 |
+
if jtok_z_tilde is None:
|
| 5729 |
+
raise ValueError(
|
| 5730 |
+
"Active JTok/JTok-M requires Leviathan `jtok_z_tilde`."
|
| 5731 |
+
)
|
| 5732 |
+
delta_m, jtok_aux_stats = self.jtok(
|
| 5733 |
+
delta_m,
|
| 5734 |
+
jtok_z_tilde,
|
| 5735 |
+
router_state=jtok_router_state if self.use_jtokm else None,
|
| 5736 |
+
valid_mask=jtok_valid_mask,
|
| 5737 |
+
compute_aux=bool(jtok_compute_aux and self.use_jtokm),
|
| 5738 |
+
)
|
| 5739 |
+
|
| 5740 |
# ── Residual junction (MLP sublayer) ────��────────────────────────
|
| 5741 |
# LAuReL augments the base MLP residual (residual_mlp + delta_m).
|
| 5742 |
if self.use_laurel:
|
|
|
|
| 5754 |
hidden_states = self.gpas_mlp(mlp_aug) if self.use_gpas else mlp_aug
|
| 5755 |
|
| 5756 |
outputs = (hidden_states,)
|
| 5757 |
+
if jtok_aux_stats is not None:
|
| 5758 |
+
outputs += jtok_aux_stats
|
| 5759 |
if output_attentions:
|
| 5760 |
outputs += (attn_weights,)
|
| 5761 |
return outputs
|
|
|
|
| 6266 |
# outputs starts with the same variance as a single projection.
|
| 6267 |
out_std = self.config.initializer_range / math.sqrt(module.num_modes)
|
| 6268 |
nn.init.normal_(module.head_out_weight, mean=0.0, std=out_std)
|
| 6269 |
+
if module.jtok_seed_proj_weight is not None:
|
| 6270 |
+
nn.init.normal_(
|
| 6271 |
+
module.jtok_seed_proj_weight,
|
| 6272 |
+
mean=0.0,
|
| 6273 |
+
std=self.config.initializer_range,
|
| 6274 |
+
)
|
| 6275 |
+
nn.init.zeros_(module.jtok_seed_proj_bias)
|
| 6276 |
+
module.jtok_seed_norm_weight.data.fill_(1.0)
|
| 6277 |
+
module.jtok_seed_norm_bias.data.zero_()
|
| 6278 |
+
|
| 6279 |
+
elif isinstance(module, LeviathanJTok):
|
| 6280 |
+
# Start continuous JTok surfaces close to a neutral residual
|
| 6281 |
+
# modulation while leaving all parameters trainable immediately.
|
| 6282 |
+
nn.init.normal_(module.spline_coeff, mean=1.0, std=0.1)
|
| 6283 |
+
nn.init.normal_(
|
| 6284 |
+
module.W_out,
|
| 6285 |
+
mean=0.0,
|
| 6286 |
+
std=self.config.initializer_range,
|
| 6287 |
+
)
|
| 6288 |
+
nn.init.normal_(
|
| 6289 |
+
module.W_res,
|
| 6290 |
+
mean=0.0,
|
| 6291 |
+
std=self.config.initializer_range,
|
| 6292 |
+
)
|
| 6293 |
+
module.scaler.data.fill_(1.0)
|
| 6294 |
+
if module.router is not None:
|
| 6295 |
+
nn.init.normal_(
|
| 6296 |
+
module.router.weight,
|
| 6297 |
+
mean=0.0,
|
| 6298 |
+
std=self.config.initializer_range,
|
| 6299 |
+
)
|
| 6300 |
|
| 6301 |
elif isinstance(module, StackMemory):
|
| 6302 |
nn.init.normal_(
|
|
|
|
| 6531 |
if (input_ids is None) ^ (inputs_embeds is not None):
|
| 6532 |
raise ValueError("Specify exactly one of input_ids or inputs_embeds")
|
| 6533 |
|
| 6534 |
+
use_jtok = bool(getattr(self.config, "use_jtok", False))
|
| 6535 |
+
use_jtokm = bool(getattr(self.config, "use_jtokm", False))
|
| 6536 |
+
if use_jtok and input_ids is None:
|
| 6537 |
+
raise ValueError(
|
| 6538 |
+
"Active JTok/JTok-M requires `input_ids`; precomputed "
|
| 6539 |
+
"`inputs_embeds` do not carry the Leviathan token coordinate."
|
| 6540 |
+
)
|
| 6541 |
+
jtok_z_tilde = None
|
| 6542 |
+
|
| 6543 |
# ── Embedding stage ────────────────────────────────────────────────
|
| 6544 |
meap_mask_embedding = None
|
| 6545 |
meap_mask_token_id = None
|
|
|
|
| 6562 |
# selection so byte features cannot alter masked positions.
|
| 6563 |
fuse_meap_in_leviathan = meap_active and self.spelling_bee is None
|
| 6564 |
if fuse_meap_in_leviathan:
|
| 6565 |
+
if use_jtok:
|
| 6566 |
+
generator_result = self.token_generator(
|
| 6567 |
+
input_ids,
|
| 6568 |
+
meap_mask_embedding=meap_mask_embedding,
|
| 6569 |
+
meap_mask_token_id=meap_mask_token_id,
|
| 6570 |
+
return_jtok_geometry=True,
|
| 6571 |
+
)
|
| 6572 |
+
else:
|
| 6573 |
+
# Keep the pre-JTok call signature for custom or
|
| 6574 |
+
# checkpoint-restored token generators.
|
| 6575 |
+
generator_result = self.token_generator(
|
| 6576 |
+
input_ids,
|
| 6577 |
+
meap_mask_embedding=meap_mask_embedding,
|
| 6578 |
+
meap_mask_token_id=meap_mask_token_id,
|
| 6579 |
+
)
|
| 6580 |
+
else:
|
| 6581 |
+
if use_jtok:
|
| 6582 |
+
generator_result = self.token_generator(
|
| 6583 |
+
input_ids,
|
| 6584 |
+
return_jtok_geometry=True,
|
| 6585 |
+
)
|
| 6586 |
+
else:
|
| 6587 |
+
generator_result = self.token_generator(input_ids)
|
| 6588 |
+
if use_jtok:
|
| 6589 |
+
inputs_embeds, jtok_z_tilde = generator_result
|
| 6590 |
else:
|
| 6591 |
+
inputs_embeds = generator_result
|
| 6592 |
meap_consumed = fuse_meap_in_leviathan
|
| 6593 |
else:
|
| 6594 |
inputs_embeds = self.embed_tokens(input_ids)
|
|
|
|
| 6622 |
inputs_embeds,
|
| 6623 |
)
|
| 6624 |
|
| 6625 |
+
# The valid mask serves two separate purposes: JTok/JTok-M must not
|
| 6626 |
+
# modulate padding or MEAP-masked positions, and JTok-M must exclude
|
| 6627 |
+
# those positions from its load-balancing counts. The original
|
| 6628 |
+
# attention mask remains unchanged for positions/loss computation.
|
| 6629 |
+
if use_jtok:
|
| 6630 |
+
jtok_valid_mask = (
|
| 6631 |
+
attention_mask.to(
|
| 6632 |
+
device=input_ids.device,
|
| 6633 |
+
dtype=torch.bool,
|
| 6634 |
+
)
|
| 6635 |
+
if attention_mask is not None
|
| 6636 |
+
else torch.ones_like(input_ids, dtype=torch.bool)
|
| 6637 |
+
)
|
| 6638 |
+
if meap_active:
|
| 6639 |
+
jtok_valid_mask = jtok_valid_mask & input_ids.ne(
|
| 6640 |
+
int(meap_mask_token_id)
|
| 6641 |
+
)
|
| 6642 |
+
else:
|
| 6643 |
+
jtok_valid_mask = None
|
| 6644 |
+
|
| 6645 |
if position_ids is None:
|
| 6646 |
if attention_mask is not None:
|
| 6647 |
position_ids = attention_mask.to(dtype=torch.long).cumsum(-1) - 1
|
|
|
|
| 6706 |
all_hidden_states = () if output_hidden_states else None
|
| 6707 |
all_attentions = () if output_attentions else None
|
| 6708 |
tweo_activations = () if output_tweo_activations else None
|
| 6709 |
+
collect_jtokm_aux = bool(
|
| 6710 |
+
use_jtokm and self.training and torch.is_grad_enabled()
|
| 6711 |
+
)
|
| 6712 |
+
all_jtokm_aux_stats = () if collect_jtokm_aux else None
|
| 6713 |
|
| 6714 |
# ── StackMemory state ─────────────────────────────────────────────
|
| 6715 |
# Same source-level idea as StackTrans:
|
|
|
|
| 6822 |
output_attentions=output_attentions,
|
| 6823 |
repo_rope_args=repo_rope_args,
|
| 6824 |
position_ids=position_ids,
|
| 6825 |
+
jtok_z_tilde=jtok_z_tilde,
|
| 6826 |
+
jtok_valid_mask=jtok_valid_mask,
|
| 6827 |
+
jtok_compute_aux=collect_jtokm_aux,
|
| 6828 |
**kwargs,
|
| 6829 |
)
|
| 6830 |
hidden_states = layer_outputs[0]
|
|
|
|
| 6846 |
output_attentions=output_attentions,
|
| 6847 |
repo_rope_args=repo_rope_args,
|
| 6848 |
position_ids=position_ids,
|
| 6849 |
+
jtok_z_tilde=jtok_z_tilde,
|
| 6850 |
+
jtok_valid_mask=jtok_valid_mask,
|
| 6851 |
+
jtok_compute_aux=collect_jtokm_aux,
|
| 6852 |
**kwargs,
|
| 6853 |
)
|
| 6854 |
hidden_states = layer_outputs[0]
|
|
|
|
| 6858 |
if use_attn_res:
|
| 6859 |
attn_res_partial = hidden_states
|
| 6860 |
|
| 6861 |
+
if collect_jtokm_aux:
|
| 6862 |
+
all_jtokm_aux_stats = all_jtokm_aux_stats + (
|
| 6863 |
+
(
|
| 6864 |
+
layer_outputs[extras_start],
|
| 6865 |
+
layer_outputs[extras_start + 1],
|
| 6866 |
+
layer_outputs[extras_start + 2],
|
| 6867 |
+
),
|
| 6868 |
+
)
|
| 6869 |
+
extras_start += 3
|
| 6870 |
+
|
| 6871 |
if output_attentions:
|
| 6872 |
all_attentions = all_attentions + (layer_outputs[extras_start],)
|
| 6873 |
extras_start += 1
|
|
|
|
| 6899 |
for v in [hidden_states, None, all_hidden_states, all_attentions]
|
| 6900 |
if v is not None
|
| 6901 |
)
|
| 6902 |
+
if all_jtokm_aux_stats is not None:
|
| 6903 |
+
base_outputs = base_outputs + (all_jtokm_aux_stats,)
|
| 6904 |
if output_tweo_activations:
|
| 6905 |
return base_outputs + (tweo_activations,)
|
| 6906 |
return base_outputs
|
| 6907 |
|
| 6908 |
+
outputs = NeoLLMBaseModelOutputWithPast(
|
| 6909 |
last_hidden_state=hidden_states,
|
| 6910 |
past_key_values=None,
|
| 6911 |
hidden_states=all_hidden_states,
|
| 6912 |
attentions=all_attentions,
|
| 6913 |
+
jtokm_aux_stats=all_jtokm_aux_stats,
|
| 6914 |
)
|
| 6915 |
if output_tweo_activations:
|
| 6916 |
return outputs, tweo_activations
|
|
|
|
| 8742 |
self._last_nextlat_loss = None
|
| 8743 |
self._last_nextlat_mse_loss = None
|
| 8744 |
self._last_nextlat_kl_loss = None
|
| 8745 |
+
self._last_jtokm_aux_loss = None
|
| 8746 |
self._last_total_loss = None
|
| 8747 |
|
| 8748 |
if config.use_token_generator:
|
|
|
|
| 8923 |
)
|
| 8924 |
|
| 8925 |
# Unpack: model returns BaseModelOutputWithPast, or a tuple when
|
| 8926 |
+
# return_dict=False (with compact JTok-M stats and/or tweo activations
|
| 8927 |
+
# appended when enabled).
|
| 8928 |
tweo_activations = None
|
| 8929 |
hidden_states_tuple = None
|
| 8930 |
+
jtokm_aux_stats = None
|
| 8931 |
if isinstance(model_out, tuple):
|
| 8932 |
outputs = model_out[0]
|
| 8933 |
if tweo_enabled and len(model_out) >= 2:
|
| 8934 |
tweo_activations = model_out[-1]
|
| 8935 |
if isinstance(outputs, torch.Tensor):
|
| 8936 |
hidden_states = outputs
|
| 8937 |
+
if model_output_hidden_states:
|
| 8938 |
+
tuple_candidates = [
|
| 8939 |
+
candidate
|
| 8940 |
+
for candidate in model_out[1:]
|
| 8941 |
+
if isinstance(candidate, tuple)
|
| 8942 |
+
and (not candidate or not isinstance(candidate[0], tuple))
|
| 8943 |
+
]
|
| 8944 |
+
if tuple_candidates:
|
| 8945 |
+
hidden_states_tuple = tuple_candidates[0]
|
| 8946 |
+
for candidate in model_out[1:]:
|
| 8947 |
+
if (
|
| 8948 |
+
isinstance(candidate, tuple)
|
| 8949 |
+
and candidate
|
| 8950 |
+
and isinstance(candidate[0], tuple)
|
| 8951 |
+
and len(candidate[0]) == 3
|
| 8952 |
+
):
|
| 8953 |
+
jtokm_aux_stats = candidate
|
| 8954 |
+
break
|
| 8955 |
elif isinstance(outputs, tuple):
|
| 8956 |
hidden_states = outputs[0]
|
| 8957 |
if len(outputs) > 2:
|
|
|
|
| 8959 |
else:
|
| 8960 |
hidden_states = outputs.last_hidden_state
|
| 8961 |
hidden_states_tuple = outputs.hidden_states
|
| 8962 |
+
jtokm_aux_stats = getattr(outputs, "jtokm_aux_stats", None)
|
| 8963 |
else:
|
| 8964 |
outputs = model_out
|
| 8965 |
hidden_states = outputs.last_hidden_state
|
| 8966 |
hidden_states_tuple = outputs.hidden_states
|
| 8967 |
+
jtokm_aux_stats = getattr(outputs, "jtokm_aux_stats", None)
|
| 8968 |
|
| 8969 |
loss = None
|
| 8970 |
ntp_loss = None
|
|
|
|
| 8987 |
nextlat_loss = None
|
| 8988 |
nextlat_mse_loss = None
|
| 8989 |
nextlat_kl_loss = None
|
| 8990 |
+
jtokm_aux_loss = None
|
| 8991 |
self._last_ntp_loss = None
|
| 8992 |
self._last_ntp_ce_unweighted = None
|
| 8993 |
self._last_mile_reweighting_delta = None
|
|
|
|
| 9013 |
self._last_nextlat_loss = None
|
| 9014 |
self._last_nextlat_mse_loss = None
|
| 9015 |
self._last_nextlat_kl_loss = None
|
| 9016 |
+
self._last_jtokm_aux_loss = None
|
| 9017 |
self._last_total_loss = None
|
| 9018 |
if labels is not None:
|
| 9019 |
if self.ntp_loss_backend == "liger":
|
|
|
|
| 9247 |
)
|
| 9248 |
loss = loss + nextlat_loss
|
| 9249 |
|
| 9250 |
+
if bool(getattr(self.config, "use_jtokm", False)) and jtokm_aux_stats:
|
| 9251 |
+
jtokm_aux_loss = compute_jtokm_aux_loss(
|
| 9252 |
+
jtokm_aux_stats,
|
| 9253 |
+
n_e=int(getattr(self.config, "jtok_num_experts", 5)),
|
| 9254 |
+
top_k=int(getattr(self.config, "jtok_top_k", 2)),
|
| 9255 |
+
weight=float(getattr(self.config, "jtok_aux_loss_weight", 1e-4)),
|
| 9256 |
+
)
|
| 9257 |
+
loss = loss + jtokm_aux_loss
|
| 9258 |
+
|
| 9259 |
self._last_ntp_loss = ntp_loss.detach() if ntp_loss is not None else None
|
| 9260 |
self._last_tweo_loss = tweo_loss.detach() if tweo_loss is not None else None
|
| 9261 |
self._last_nitp_loss = nitp_loss.detach() if nitp_loss is not None else None
|
|
|
|
| 9351 |
self._last_nextlat_kl_loss = (
|
| 9352 |
nextlat_kl_loss.detach() if nextlat_kl_loss is not None else None
|
| 9353 |
)
|
| 9354 |
+
self._last_jtokm_aux_loss = (
|
| 9355 |
+
jtokm_aux_loss.detach() if jtokm_aux_loss is not None else None
|
| 9356 |
+
)
|
| 9357 |
self._last_total_loss = loss.detach() if loss is not None else None
|
| 9358 |
logits = None
|
| 9359 |
else:
|
|
|
|
| 9381 |
"NeoLLMPreTrainedModel",
|
| 9382 |
"NeoLLMConfig",
|
| 9383 |
"LeviathanGenerator",
|
| 9384 |
+
"LeviathanJTok",
|
| 9385 |
+
"NeoLLMBaseModelOutputWithPast",
|
| 9386 |
+
"compute_jtokm_aux_loss",
|
| 9387 |
"SpellingBeeEmbedding",
|
| 9388 |
"StackMemory",
|
| 9389 |
"FANLayer",
|