from __future__ import annotations import json import math from dataclasses import asdict, dataclass from pathlib import Path import torch from torch import nn from torch.nn import functional as F _compiled_block = None def _rms(x: torch.Tensor, weight: torch.Tensor) -> torch.Tensor: y = x.float() y = y * torch.rsqrt(y.square().mean(-1, keepdim=True) + 1e-6) return y.to(x.dtype) * weight def _block_forward( x: torch.Tensor, n1: torch.Tensor, qkv: torch.Tensor, qn: torch.Tensor, kn: torch.Tensor, out: torch.Tensor, n2: torch.Tensor, gate: torch.Tensor, up: torch.Tensor, down: torch.Tensor, rope_cos: torch.Tensor, rope_sin: torch.Tensor, heads: int, ) -> torch.Tensor: batch, length, dim = x.shape head_dim = dim // heads a = _rms(x, n1) q, k, v = F.linear(a, qkv).chunk(3, dim=-1) q = _rms(q.view(batch, length, heads, head_dim), qn).transpose(1, 2) k = _rms(k.view(batch, length, heads, head_dim), kn).transpose(1, 2) v = v.view(batch, length, heads, head_dim).transpose(1, 2) cos = rope_cos[:, :, :length].to(x.dtype) sin = rope_sin[:, :, :length].to(x.dtype) qa, qb = q[..., 0::2], q[..., 1::2] ka, kb = k[..., 0::2], k[..., 1::2] q = torch.stack((qa * cos - qb * sin, qa * sin + qb * cos), dim=-1).flatten(-2) k = torch.stack((ka * cos - kb * sin, ka * sin + kb * cos), dim=-1).flatten(-2) a = F.scaled_dot_product_attention(q, k, v, is_causal=True) x = x + F.linear(a.transpose(1, 2).reshape(batch, length, dim), out) a = _rms(x, n2) return x + F.linear(F.silu(F.linear(a, gate)) * F.linear(a, up), down) @dataclass class CascadeConfig: vocab_size: int = 256 sequence_length: int = 1024 local_dimension: int = 384 local_layers: int = 4 local_heads: int = 6 local_intermediate_size: int = 1536 dimension: int = 1024 heads: int = 16 intermediate_size: int = 12032 route_paths: int = 3 active_layers: int = 8 max_patches: int = 192 patch_rate: float = 0.125 entropy_threshold: float = 5.0 rope_theta: float = 10000.0 patch_aux_weight: float = 0.25 patch_mode: str = "byte_transition" tokenizer_name: str | None = None model_type: str = "cascade_byte_lm" architectures: tuple[str, ...] = ("CascadeForCausalLM",) def save(self, path: str | Path) -> None: Path(path).write_text(json.dumps(asdict(self), indent=2), encoding="utf-8") @classmethod def load(cls, path: str | Path) -> "CascadeConfig": values = json.loads(Path(path).read_text(encoding="utf-8")) values.pop("model_type", None) values.pop("architectures", None) return cls(**values) class RMSNorm(nn.Module): def __init__(self, dim: int) -> None: super().__init__() self.weight = nn.Parameter(torch.ones(dim)) def forward(self, x: torch.Tensor) -> torch.Tensor: y = x.float() y = y * torch.rsqrt(y.square().mean(-1, keepdim=True) + 1e-6) return y.to(x.dtype) * self.weight class Attention(nn.Module): def __init__(self, dim: int, heads: int, max_length: int, theta: float) -> None: super().__init__() self.heads = heads self.head_dim = dim // heads self.qkv = nn.Linear(dim, 3 * dim, bias=False) self.q_norm = RMSNorm(self.head_dim) self.k_norm = RMSNorm(self.head_dim) self.out = nn.Linear(dim, dim, bias=False) pos = torch.arange(max_length, dtype=torch.float32) inv = torch.exp(-math.log(theta) * torch.arange(0, self.head_dim, 2) / self.head_dim) angle = pos[:, None] * inv[None, :] self.register_buffer("rope_cos", angle.cos()[None, None], persistent=False) self.register_buffer("rope_sin", angle.sin()[None, None], persistent=False) def rotate(self, x: torch.Tensor) -> torch.Tensor: length = x.shape[-2] cos = self.rope_cos[:, :, :length].to(x.dtype) sin = self.rope_sin[:, :, :length].to(x.dtype) a, b = x[..., 0::2], x[..., 1::2] return torch.stack((a * cos - b * sin, a * sin + b * cos), dim=-1).flatten(-2) def forward(self, x: torch.Tensor) -> torch.Tensor: batch, length, _ = x.shape q, k, v = self.qkv(x).chunk(3, dim=-1) q = self.q_norm(q.view(batch, length, self.heads, self.head_dim)).transpose(1, 2) k = self.k_norm(k.view(batch, length, self.heads, self.head_dim)).transpose(1, 2) v = v.view(batch, length, self.heads, self.head_dim).transpose(1, 2) q, k = self.rotate(q), self.rotate(k) y = F.scaled_dot_product_attention(q, k, v, is_causal=True) return self.out(y.transpose(1, 2).reshape(batch, length, -1)) class FeedForward(nn.Module): def __init__(self, dim: int, hidden: int) -> None: super().__init__() self.gate = nn.Linear(dim, hidden, bias=False) self.up = nn.Linear(dim, hidden, bias=False) self.down = nn.Linear(hidden, dim, bias=False) def forward(self, x: torch.Tensor) -> torch.Tensor: return self.down(F.silu(self.gate(x)) * self.up(x)) class Block(nn.Module): def __init__(self, dim: int, heads: int, hidden: int, max_length: int, theta: float) -> None: super().__init__() self.n1 = RMSNorm(dim) self.attn = Attention(dim, heads, max_length, theta) self.n2 = RMSNorm(dim) self.ffn = FeedForward(dim, hidden) self.compiled = False def forward(self, x: torch.Tensor) -> torch.Tensor: if self.compiled: return _compiled_block( x, self.n1.weight, self.attn.qkv.weight, self.attn.q_norm.weight, self.attn.k_norm.weight, self.attn.out.weight, self.n2.weight, self.ffn.gate.weight, self.ffn.up.weight, self.ffn.down.weight, self.attn.rope_cos, self.attn.rope_sin, self.attn.heads, ) x = x + self.attn(self.n1(x)) return x + self.ffn(self.n2(x)) class CascadeForCausalLM(nn.Module): def __init__(self, config: CascadeConfig, surprise: torch.Tensor | None = None) -> None: super().__init__() self.config = config if surprise is None: shape = (config.vocab_size,) if config.patch_mode == "token_surprise" else (config.vocab_size, config.vocab_size) surprise = torch.full(shape, config.entropy_threshold) self.register_buffer("surprise", surprise.float()) self.register_buffer("entropy_threshold", torch.tensor(config.entropy_threshold)) self.embed = nn.Embedding(config.vocab_size, config.local_dimension) self.local_blocks = nn.ModuleList( Block( config.local_dimension, config.local_heads, config.local_intermediate_size, config.sequence_length, config.rope_theta, ) for _ in range(config.local_layers) ) self.to_core = nn.Linear(config.local_dimension, config.dimension, bias=False) self.paths = nn.ModuleList( nn.ModuleList( Block( config.dimension, config.heads, config.intermediate_size, config.max_patches, config.rope_theta, ) for _ in range(config.active_layers) ) for _ in range(config.route_paths) ) self.core_norm = RMSNorm(config.dimension) self.patch_head = nn.Linear(config.dimension, config.vocab_size, bias=False) self.from_core = nn.Linear(config.dimension, config.local_dimension, bias=False) self.mix = nn.Linear(2 * config.local_dimension, config.local_dimension, bias=False) self.norm = RMSNorm(config.local_dimension) self.head = nn.Linear(config.local_dimension, config.vocab_size, bias=False) self.head.weight = self.embed.weight self.apply(self._initialize) residual_std = 0.02 / math.sqrt(2 * (config.local_layers + config.active_layers)) for module in self.modules(): if isinstance(module, Attention): nn.init.normal_(module.out.weight, std=residual_std) elif isinstance(module, FeedForward): nn.init.normal_(module.down.weight, std=residual_std) @staticmethod def _initialize(module: nn.Module) -> None: if isinstance(module, (nn.Linear, nn.Embedding)): nn.init.normal_(module.weight, mean=0.0, std=0.02) def set_entropy(self, surprise: torch.Tensor, threshold: float) -> None: self.surprise.copy_(surprise) self.entropy_threshold.fill_(threshold) self.config.entropy_threshold = threshold def compile_blocks(self) -> None: global _compiled_block if _compiled_block is None: _compiled_block = torch.compile(_block_forward, mode="max-autotune-no-cudagraphs", fullgraph=True) for block in self.local_blocks: block.compiled = True for path in self.paths: for block in path: block.compiled = True def patch(self, x: torch.Tensor, h: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]: if self.config.patch_mode == "token_surprise": score = self.surprise[x[:, 1:]] else: score = self.surprise[x[:, :-1], x[:, 1:]] boundary = torch.cat((torch.ones_like(x[:, :1], dtype=torch.bool), score >= self.entropy_threshold), dim=1) raw_ids = boundary.long().cumsum(1).sub(1) totals = raw_ids[:, -1:].add(1) compacted = raw_ids.mul(self.config.max_patches).div(totals, rounding_mode="floor") ids = torch.where(totals.gt(self.config.max_patches), compacted, raw_ids) merged_boundary = torch.cat((torch.ones_like(ids[:, :1], dtype=torch.bool), ids[:, 1:].ne(ids[:, :-1])), dim=1) end = torch.cat((ids[:, :-1].ne(ids[:, 1:]), torch.ones_like(ids[:, -1:], dtype=torch.bool)), dim=1) patches = torch.zeros(x.shape[0], self.config.max_patches, h.shape[-1], device=h.device, dtype=h.dtype) patches.scatter_add_(1, ids.unsqueeze(-1).expand_as(h), h * end.unsqueeze(-1)) counts = torch.zeros(x.shape[0], self.config.max_patches, device=x.device, dtype=torch.int32) counts.scatter_add_(1, ids, torch.ones_like(ids, dtype=torch.int32)) first = torch.zeros(x.shape[0], self.config.max_patches, device=x.device, dtype=torch.long) first.scatter_add_(1, ids, x * merged_boundary) overflow = totals[:, 0].gt(self.config.max_patches) return patches, ids, counts, first, overflow def forward(self, x: torch.Tensor, route_id: int) -> tuple[torch.Tensor, torch.Tensor, dict[str, torch.Tensor]]: h = self.embed(x) for block in self.local_blocks: h = block(h) patches, ids, counts, first, overflow = self.patch(x, h) z = self.to_core(patches) for block in self.paths[route_id]: z = block(z) z = self.core_norm(z) previous = ids.sub(1).clamp_min(0) context = z.gather(1, previous.unsqueeze(-1).expand(-1, -1, z.shape[-1])) context = self.from_core(context) * ids.gt(0).unsqueeze(-1) gate = torch.sigmoid(self.mix(torch.cat((h, context), dim=-1))) logits = self.head(self.norm(h + gate * context)) valid = counts[:, 1:].gt(0) auxiliary = F.cross_entropy(self.patch_head(z[:, :-1])[valid], first[:, 1:][valid]) stats = { "patches": counts.gt(0).sum(), "overflow": overflow.sum(), "gate": gate.mean(), } return logits, auxiliary, stats def parameter_counts(self) -> dict[str, int]: paths = sum(p.numel() for p in self.paths.parameters()) active_path = paths // self.config.route_paths total = sum(p.numel() for p in self.parameters()) return {"total": total, "active": total - paths + active_path, "shared": total - paths, "active_path": active_path}