Hetman-2b / model.py
YSamchuk's picture
Release Hetman-2-NCM 2.0B model definitions, configuration and SLH-Core v2
2dd7b9f verified
Raw History Blame Contribute Delete
29 kB
"""
Hetman-2-NCM 2.0B: Master PyTorch Architecture Implementation
File: src/model.py
License: Apache License 2.0
Implements:
- Hetman2Config & Hetman2Config.create_mini()
- BitNet b1.58 Ternary Quantization & BitLinear
- Fast Walsh-Hadamard Transform (FWHT INT8 Engine)
- Gated DeltaNet Recurrent Block (O(1) Constant FP32 State)
- Phase 1: Causal AR Blueprint Drafter
- Phase 2: Global Bidirectional NCM Refiner & Anti-Hallucination Deduplicator (3 passes)
- Phase 3: Block-Causal NCM Decoder (8 passes via DynamicBlueprintEvolver)
- Hetman2ForCausalLM (Full End-to-End Hierarchical Generation Pipeline)
"""
import math
from dataclasses import dataclass
from typing import Dict, List, Optional, Tuple, Any
import torch
import torch.nn as nn
import torch.nn.functional as F
import sys
from pathlib import Path
_ROOT = str(Path(__file__).resolve().parent.parent)
if _ROOT not in sys.path:
sys.path.insert(0, _ROOT)
from src.dynamic_blueprint_evolver import (
DynamicBlueprintEvolver,
SphericalNorm,
AdaLNZeroModulator,
)
@dataclass
class Hetman2Config:
"""
Configuration specification for Hetman-2-NCM 2.0B.
"""
vocab_size: int = 65536
hidden_size: int = 1536
intermediate_size: int = 6144
num_layers: int = 34
num_deltanet_layers: int = 28
num_global_layers: int = 6
deltanet_heads: int = 16
deltanet_head_dim: int = 64
global_heads_q: int = 12
global_heads_kv: int = 2
global_head_dim: int = 128
hopfield_groups: int = 16
hopfield_codebook_size: int = 1024
hopfield_val_dim: int = 32
tokens_per_block: int = 400
max_blocks: int = 8
latents_per_block: int = 16
phase2_passes: int = 3
phase3_passes: int = 8
fwht_block_size: int = 512
dedup_tau: float = 0.85
seam_window: int = 4
alpha_residual_anchor: float = 0.25
@classmethod
def create_mini(
cls,
vocab_size: int = 256,
hidden_size: int = 128,
intermediate_size: int = 256,
num_layers: int = 4,
num_deltanet_layers: int = 3,
num_global_layers: int = 1,
deltanet_heads: int = 4,
deltanet_head_dim: int = 32,
tokens_per_block: int = 16,
max_blocks: int = 2,
latents_per_block: int = 4,
phase2_passes: int = 3,
phase3_passes: int = 8,
fwht_block_size: int = 64,
**kwargs: Any
) -> "Hetman2Config":
"""
Creates a compact mini-instance configuration for fast unit & end-to-end testing
while preserving all architectural invariants and mathematical structures.
"""
return cls(
vocab_size=vocab_size,
hidden_size=hidden_size,
intermediate_size=intermediate_size,
num_layers=num_layers,
num_deltanet_layers=num_deltanet_layers,
num_global_layers=num_global_layers,
deltanet_heads=deltanet_heads,
deltanet_head_dim=deltanet_head_dim,
global_heads_q=max(2, deltanet_heads),
global_heads_kv=max(1, deltanet_heads // 2),
global_head_dim=deltanet_head_dim,
hopfield_groups=4,
hopfield_codebook_size=64,
hopfield_val_dim=16,
tokens_per_block=tokens_per_block,
max_blocks=max_blocks,
latents_per_block=latents_per_block,
phase2_passes=phase2_passes,
phase3_passes=phase3_passes,
fwht_block_size=min(fwht_block_size, hidden_size),
**kwargs
)
# ==============================================================================
# 1. BitNet b1.58 Ternary Quantization
# ==============================================================================
class TernaryQuantizeSTE(torch.autograd.Function):
"""
Straight-Through Estimator for BitNet b1.58 Absmean ternary quantization {-1, 0, +1}.
"""
@staticmethod
def forward(ctx, w: torch.Tensor, eps: float = 1e-6) -> Tuple[torch.Tensor, torch.Tensor]:
if torch.isnan(w).any() or torch.isinf(w).any():
w = torch.nan_to_num(w, nan=0.0, posinf=1.0, neginf=-1.0)
gamma = torch.mean(torch.abs(w)) + eps
scaled_w = w / gamma
w_quant = torch.clamp(torch.round(scaled_w), -1.0, 1.0)
ctx.save_for_backward(scaled_w)
return w_quant, gamma
@staticmethod
def backward(ctx, grad_w_quant: torch.Tensor, grad_gamma: Optional[torch.Tensor]) -> Tuple[torch.Tensor, None]:
scaled_w, = ctx.saved_tensors
mask = (torch.abs(scaled_w) <= 1.0).to(grad_w_quant.dtype)
grad_w = grad_w_quant * mask
return grad_w, None
class BitLinear(nn.Module):
"""
BitNet b1.58 Linear layer with ternary weights {-1, 0, +1} and per-tensor scale gamma.
"""
def __init__(self, in_features: int, out_features: int, bias: bool = False):
super().__init__()
self.in_features = in_features
self.out_features = out_features
self.weight = nn.Parameter(torch.randn(out_features, in_features) * (1.0 / math.sqrt(in_features)))
if bias:
self.bias = nn.Parameter(torch.zeros(out_features))
else:
self.register_parameter("bias", None)
def forward(self, x: torch.Tensor) -> torch.Tensor:
w_quant, gamma = TernaryQuantizeSTE.apply(self.weight)
out = F.linear(x, w_quant, None) * gamma
if self.bias is not None:
out = out + self.bias
return out
# ==============================================================================
# 2. Fast Walsh-Hadamard Transform (FWHT B=512)
# ==============================================================================
class FastWalshHadamardTransform(nn.Module):
"""
Online Blockwise FWHT for outlier attenuation and INT8 stabilization.
"""
def __init__(self, block_size: int = 512, eta_min: float = 0.05, eta_max: float = 40.0):
super().__init__()
self.block_size = block_size
self.eta_min = eta_min
self.eta_max = eta_max
def _fwht_butterfly(self, x: torch.Tensor) -> torch.Tensor:
orig_shape = x.shape
dim = orig_shape[-1]
x_flat = x.reshape(-1, dim)
h = 1
while h < dim:
x_flat = x_flat.view(-1, dim // (2 * h), 2, h)
x0 = x_flat[:, :, 0, :].clone()
x1 = x_flat[:, :, 1, :].clone()
x_flat = torch.cat([x0 + x1, x0 - x1], dim=2)
h *= 2
return x_flat.view(orig_shape) / math.sqrt(dim)
def forward(self, x: torch.Tensor) -> Tuple[torch.Tensor, float]:
if torch.isnan(x).any() or torch.isinf(x).any():
x = torch.nan_to_num(x, nan=0.0, posinf=self.eta_max, neginf=-self.eta_max)
orig_shape = x.shape
d = orig_shape[-1]
block = min(self.block_size, d)
if d % block != 0:
return x, 1.0
x_blocked = x.view(-1, block)
x_rot = self._fwht_butterfly(x_blocked)
eta = torch.amax(torch.abs(x_rot), dim=-1, keepdim=True) + 1e-5
eta_clamped = torch.clamp(eta, self.eta_min, self.eta_max)
x_quant = torch.clamp(torch.round(x_rot * (127.0 / eta_clamped)), -128.0, 127.0)
x_dequant = x_quant * (eta_clamped / 127.0)
x_inv = self._fwht_butterfly(x_dequant)
return x_inv.view(orig_shape), eta_clamped.mean().item()
# ==============================================================================
# 3. Gated DeltaNet Recurrent Block (O(1) State Dynamics in FP32)
# ==============================================================================
class GatedDeltaNetLayer(nn.Module):
"""
Linear recurrent Gated DeltaNet layer with constant O(1) state per head.
Maintains recurrent state S_t in FP32 across all generation steps.
"""
def __init__(self, d_model: int, num_heads: int, head_dim: int):
super().__init__()
self.d_model = d_model
self.num_heads = num_heads
self.head_dim = head_dim
self.total_dim = num_heads * head_dim
self.q_proj = BitLinear(d_model, self.total_dim, bias=False)
self.k_proj = BitLinear(d_model, self.total_dim, bias=False)
self.v_proj = BitLinear(d_model, self.total_dim, bias=False)
self.alpha_proj = nn.Linear(d_model, num_heads, bias=True)
self.beta_proj = nn.Linear(d_model, num_heads, bias=True)
self.out_proj = BitLinear(self.total_dim, d_model, bias=False)
self.norm = nn.LayerNorm(d_model)
# SwiGLU FFN
intermediate = d_model * 4
self.ffn_gate = BitLinear(d_model, intermediate, bias=False)
self.ffn_up = BitLinear(d_model, intermediate, bias=False)
self.ffn_down = BitLinear(intermediate, d_model, bias=False)
self.ffn_norm = nn.LayerNorm(d_model)
def forward_step(
self,
x_t: torch.Tensor,
state_prev: Optional[torch.Tensor] = None
) -> Tuple[torch.Tensor, torch.Tensor]:
"""
Single recurrent step for token vector x_t: [Batch, d_model].
state_prev: [Batch, num_heads, head_dim, head_dim] in FP32.
"""
B = x_t.size(0)
if state_prev is None:
state_prev = torch.zeros(
B, self.num_heads, self.head_dim, self.head_dim,
dtype=torch.float32, device=x_t.device
)
elif torch.isnan(state_prev).any() or torch.isinf(state_prev).any():
state_prev = torch.nan_to_num(state_prev, nan=0.0, posinf=50.0, neginf=-50.0)
if torch.isnan(x_t).any() or torch.isinf(x_t).any():
x_t = torch.nan_to_num(x_t, nan=0.0, posinf=50.0, neginf=-50.0)
norm_x = self.norm(x_t)
q = self.q_proj(norm_x).view(B, self.num_heads, self.head_dim)
k = self.k_proj(norm_x).view(B, self.num_heads, self.head_dim)
v = self.v_proj(norm_x).view(B, self.num_heads, self.head_dim)
# Normalize key vector for stable recurrence
k = F.normalize(k, p=2, dim=-1)
alpha = torch.sigmoid(self.alpha_proj(norm_x)).view(B, self.num_heads, 1, 1).to(torch.float32)
beta = torch.sigmoid(self.beta_proj(norm_x)).view(B, self.num_heads, 1).to(torch.float32)
# Recurrent DeltaNet update:
# delta_v = v - (S_{t-1} @ k)
k_col = k.unsqueeze(-1).to(torch.float32) # [B, H, D, 1]
v_col = v.unsqueeze(-1).to(torch.float32) # [B, H, D, 1]
q_row = q.unsqueeze(-2).to(torch.float32) # [B, H, 1, D]
retrieved_v = torch.matmul(state_prev, k_col) # [B, H, D, 1]
delta_v = v_col - retrieved_v
update_term = torch.matmul(delta_v, k_col.transpose(-2, -1)) # [B, H, D, D]
state_new = alpha * state_prev + beta.unsqueeze(-1) * update_term
# Readout: out = S_new @ q
out_v = torch.matmul(state_new, q_row.transpose(-2, -1)).squeeze(-1) # [B, H, D]
attn_out = self.out_proj(out_v.view(B, -1).to(x_t.dtype))
x_post = x_t + attn_out
# SwiGLU FFN step
ffn_in = self.ffn_norm(x_post)
ffn_out = self.ffn_down(F.silu(self.ffn_gate(ffn_in)) * self.ffn_up(ffn_in))
x_final = x_post + ffn_out
return x_final, state_new
def forward_sequence(
self,
x_seq: torch.Tensor,
state_init: Optional[torch.Tensor] = None
) -> Tuple[torch.Tensor, torch.Tensor]:
"""
Processes full token sequence [Batch, SeqLen, d_model] through recurrent steps.
"""
B, S, D = x_seq.shape
state = state_init
outputs = []
for t in range(S):
x_t = x_seq[:, t, :]
out_t, state = self.forward_step(x_t, state)
outputs.append(out_t.unsqueeze(1))
return torch.cat(outputs, dim=1), state
# ==============================================================================
# 4. Phase 1: Causal AR Blueprint Drafter
# ==============================================================================
class Phase1BlueprintDrafter(nn.Module):
"""
Phase 1: Generates coarse conceptual blueprints B_k for K blocks based on prompt context.
"""
def __init__(self, config: Hetman2Config):
super().__init__()
self.config = config
self.d_model = config.hidden_size
self.max_blocks = config.max_blocks
self.latents_per_block = config.latents_per_block
self.block_query_embed = nn.Parameter(
torch.randn(config.max_blocks, self.d_model) * 0.02
)
self.drafter_layers = nn.ModuleList([
GatedDeltaNetLayer(
d_model=self.d_model,
num_heads=config.deltanet_heads,
head_dim=config.deltanet_head_dim
)
for _ in range(min(4, config.num_layers))
])
self.spher_norm = SphericalNorm()
def forward(
self,
prompt_repr: torch.Tensor,
num_blocks: Optional[int] = None
) -> torch.Tensor:
"""
prompt_repr: [Batch, PromptLen, d_model]
Returns:
blueprints: [Batch, num_blocks, d_model]
"""
B = prompt_repr.size(0)
K = num_blocks or self.max_blocks
K = min(K, self.max_blocks)
# Context summary via mean pooling with empty and NaN guards
if prompt_repr.size(1) == 0:
ctx = torch.zeros(B, 1, self.d_model, device=prompt_repr.device, dtype=prompt_repr.dtype)
else:
if torch.isnan(prompt_repr).any() or torch.isinf(prompt_repr).any():
prompt_repr = torch.nan_to_num(prompt_repr, nan=0.0, posinf=10.0, neginf=-10.0)
ctx = prompt_repr.mean(dim=1, keepdim=True) # [B, 1, d]
queries = self.block_query_embed[:K].unsqueeze(0).expand(B, -1, -1) # [B, K, d]
x = queries + ctx
# Run through causal drafter layers
state = None
for layer in self.drafter_layers:
x, state = layer.forward_sequence(x, state)
blueprints = self.spher_norm(x)
return blueprints
# ==============================================================================
# 5. Phase 2: Global Bidirectional NCM Refiner & Anti-Hallucination Deduplicator
# ==============================================================================
class SemanticDeduplicationFilter(nn.Module):
"""
Anti-Hallucination Deduplicator. Applies inhibitory orthogonal repulsion
when inter-chunk cosine similarity exceeds tau = 0.85.
"""
def __init__(self, d_model: int = 1536, tau: float = 0.85, eps: float = 1e-8):
super().__init__()
self.d_model = d_model
self.tau = tau
self.eps = eps
def forward(self, H: torch.Tensor) -> Tuple[torch.Tensor, int]:
# H: [Batch, NumChunks, d_model]
B, K, d = H.shape
if K <= 1:
return H, 0
c_norm = torch.norm(H, p=2, dim=-1, keepdim=True) + self.eps
c_hat = H / c_norm
M = torch.bmm(c_hat, c_hat.transpose(1, 2))
H_out = H.clone()
repulsions = 0
for b in range(B):
for i in range(K):
for j in range(i + 1, K):
sim = M[b, i, j].item()
if math.isnan(sim):
sim = 0.0
if sim > self.tau:
repulsions += 1
c_i = H[b, i]
c_j = H[b, j]
c_i_sq = torch.dot(c_i, c_i) + self.eps
proj = (torch.dot(c_j, c_i) / c_i_sq) * c_i
alpha_rep = min(1.0, (sim - self.tau) / (1.0 - self.tau + self.eps))
H_out[b, j] = H_out[b, j] - alpha_rep * proj
return H_out, repulsions
class BoundarySeamSmoother(nn.Module):
"""
Smooths semantic transitions across chunk boundaries.
"""
def __init__(self, d_model: int = 1536):
super().__init__()
self.d_model = d_model
self.gate = nn.Sequential(
nn.Linear(d_model * 2, d_model),
nn.Sigmoid()
)
def forward(self, H: torch.Tensor) -> torch.Tensor:
B, K, d = H.shape
if K <= 1:
return H
H_out = H.clone()
for k in range(K - 1):
h_curr = H[:, k, :]
h_next = H[:, k + 1, :]
bridge = 0.5 * (h_curr + h_next)
g = self.gate(torch.cat([h_curr, h_next], dim=-1))
H_out[:, k + 1, :] = H_out[:, k + 1, :] + 0.15 * g * (bridge - h_next)
H_out[:, k, :] = H_out[:, k, :] + 0.15 * (1.0 - g) * (bridge - h_curr)
return H_out
class DirichletEnergyWatchdog(nn.Module):
"""
Tracks graph Dirichlet energy E(H) to safeguard against oversmoothing.
"""
def __init__(self, threshold_ratio: float = 0.5, boost_gain: float = 0.25):
super().__init__()
self.threshold_ratio = threshold_ratio
self.boost_gain = boost_gain
def compute_energy(self, H: torch.Tensor) -> float:
B, K, d = H.shape
if K <= 1:
return 1.0
diff = H.unsqueeze(2) - H.unsqueeze(1)
energy = torch.sum(diff ** 2, dim=-1).mean() / (2.0 * K)
return float(energy.item())
def forward(self, H: torch.Tensor, initial_energy: float) -> Tuple[torch.Tensor, float, bool]:
current_energy = self.compute_energy(H)
triggered = False
H_out = H
if math.isnan(initial_energy) or math.isnan(current_energy):
triggered = True
H_out = torch.nan_to_num(H, nan=0.0)
current_energy = 1.0
elif initial_energy < 1e-6:
triggered = True
barycenter = H.mean(dim=1, keepdim=True)
H_out = H + self.boost_gain * (H - barycenter) + torch.randn_like(H) * 0.02
elif current_energy < self.threshold_ratio * initial_energy:
triggered = True
barycenter = H.mean(dim=1, keepdim=True)
H_out = H + self.boost_gain * (H - barycenter)
return H_out, current_energy, triggered
class Phase2NCMRefiner(nn.Module):
"""
Phase 2: Global Bidirectional NCM Refiner.
Iteratively refines raw draft blueprints across 3 passes (num_iterations=3).
"""
def __init__(self, config: Hetman2Config):
super().__init__()
self.config = config
self.d_model = config.hidden_size
self.num_iterations = config.phase2_passes
self.mode_embedding_refine = nn.Parameter(
torch.randn(1, 1, self.d_model) * 0.02
)
# Shared bidirectional arbiter attention
self.arbiter_q = nn.Linear(self.d_model, self.d_model, bias=False)
self.arbiter_k = nn.Linear(self.d_model, self.d_model, bias=False)
self.arbiter_v = nn.Linear(self.d_model, self.d_model, bias=False)
self.arbiter_out = nn.Linear(self.d_model, self.d_model, bias=False)
self.arbiter_norm = nn.LayerNorm(self.d_model)
self.deduplicator = SemanticDeduplicationFilter(d_model=self.d_model, tau=config.dedup_tau)
self.seam_smoother = BoundarySeamSmoother(d_model=self.d_model)
self.watchdog = DirichletEnergyWatchdog()
self.fwht = FastWalshHadamardTransform(block_size=config.fwht_block_size)
self.spher_norm = SphericalNorm()
def forward(self, H_raw: torch.Tensor) -> Tuple[torch.Tensor, Dict[str, Any]]:
B, K, d = H_raw.shape
H_0 = H_raw.clone()
H_curr = H_raw.clone()
initial_energy = self.watchdog.compute_energy(H_0)
telemetry: Dict[str, Any] = {
"initial_dirichlet_energy": initial_energy,
"pass_telemetry": []
}
for step in range(1, self.num_iterations + 1):
H_cond = H_curr + self.mode_embedding_refine
norm_h = self.arbiter_norm(H_cond)
q = self.arbiter_q(norm_h)
k = self.arbiter_k(norm_h)
v = self.arbiter_v(norm_h)
scores = torch.bmm(q, k.transpose(1, 2)) / math.sqrt(d)
attn = F.softmax(scores, dim=-1)
H_arb = H_cond + self.arbiter_out(torch.bmm(attn, v))
# Residual anchor (25% initial anchor)
H_anchored = (
self.config.alpha_residual_anchor * H_0 +
(1.0 - self.config.alpha_residual_anchor) * H_arb
)
# Semantic deduplication
H_dedup, repulsions = self.deduplicator(H_anchored)
# Seam smoother
H_seam = self.seam_smoother(H_dedup)
# Dirichlet watchdog
H_watch, current_energy, triggered = self.watchdog(H_seam, initial_energy)
# FWHT INT8 normalization
H_quant, mean_eta = self.fwht(H_watch)
H_curr = self.spher_norm(H_quant)
telemetry["pass_telemetry"].append({
"pass": step,
"dirichlet_energy": current_energy,
"repulsions": repulsions,
"watchdog_triggered": triggered,
"mean_fwht_eta": mean_eta
})
return H_curr, telemetry
# ==============================================================================
# 6. Phase 3: Block-Causal NCM Decoder
# ==============================================================================
class Phase3NCMBlockDecoder(nn.Module):
"""
Phase 3: Synthesizes final discrete tokens for each block using 8 dynamic passes
via DynamicBlueprintEvolver while carrying forward Gated DeltaNet state.
"""
def __init__(self, config: Hetman2Config):
super().__init__()
self.config = config
self.d_model = config.hidden_size
self.tokens_per_block = config.tokens_per_block
self.num_passes = config.phase3_passes
self.evolver = DynamicBlueprintEvolver(
d_model=self.d_model,
total_steps=self.num_passes,
gamma_min=0.10,
gamma_max=0.80,
schedule_type="cosine"
)
self.adaln = AdaLNZeroModulator(d_model=self.d_model)
# DeltaNet core for token synthesis & persistent context
self.deltanet_core = GatedDeltaNetLayer(
d_model=self.d_model,
num_heads=config.deltanet_heads,
head_dim=config.deltanet_head_dim
)
self.token_pos_embed = nn.Parameter(
torch.randn(config.tokens_per_block, self.d_model) * 0.02
)
self.lm_head = BitLinear(self.d_model, config.vocab_size, bias=False)
def decode_single_block(
self,
blueprint: torch.Tensor,
state_prev: Optional[torch.Tensor] = None
) -> Tuple[torch.Tensor, torch.Tensor, List[Dict[str, float]]]:
"""
blueprint: [Batch, d_model] - Single refined block blueprint
state_prev: [Batch, H, D, D] in FP32 - DeltaNet recurrent state from block k-1
Returns:
token_ids: [Batch, tokens_per_block]
state_next: [Batch, H, D, D] in FP32
pass_metrics: list of metrics from 8 evolver passes
"""
B = blueprint.size(0)
L = self.tokens_per_block
B_curr = blueprint
B_init = blueprint
# Initialize token representation candidate
pos = self.token_pos_embed.unsqueeze(0).expand(B, -1, -1) # [B, L, d]
H_tokens = pos + blueprint.unsqueeze(1)
pass_metrics: List[Dict[str, float]] = []
# 1. Base coarse-to-fine RVQ ladder passes (Scale 1 -> Scale 2 -> Scale 3)
base_passes = self.num_passes
for pass_idx in range(1, base_passes + 1):
g1, b1, a1, g2, b2, a2 = self.adaln(B_curr)
g1 = g1.unsqueeze(1)
b1 = b1.unsqueeze(1)
a1 = a1.unsqueeze(1)
# Modulated token representations
H_norm = F.layer_norm(H_tokens, (self.d_model,))
H_mod = (1.0 + g1) * H_norm + b1
H_tokens = H_tokens + a1 * H_mod
# Intent subtraction via evolver
B_curr, metrics = self.evolver.step(
B_curr=B_curr,
B_init=B_init,
H_gen=H_tokens,
step_idx=pass_idx
)
metrics["pass_type"] = "base"
pass_metrics.append(metrics)
# 2. Emergency correction passes (up to 3 refinement passes on uncertain positions)
emergency_passes = 3
for em_idx in range(1, emergency_passes + 1):
g1, b1, a1, g2, b2, a2 = self.adaln(B_curr)
g1 = g1.unsqueeze(1)
b1 = b1.unsqueeze(1)
a1 = a1.unsqueeze(1)
H_norm = F.layer_norm(H_tokens, (self.d_model,))
H_mod = (1.0 + g1) * H_norm + b1
H_tokens = H_tokens + 0.5 * a1 * H_mod
# Emergency refinement step
B_curr, metrics = self.evolver.step(
B_curr=B_curr,
B_init=B_init,
H_gen=H_tokens,
step_idx=base_passes # clamped to terminal step
)
metrics["pass_type"] = f"emergency_{em_idx}"
pass_metrics.append(metrics)
# 3. Advance DeltaNet recurrent state across all tokens of this block in FP32
H_tokens_out, state_next = self.deltanet_core.forward_sequence(H_tokens, state_prev)
# Emit discrete token IDs via LM Head
logits = self.lm_head(H_tokens_out) # [B, L, vocab_size]
token_ids = torch.argmax(logits, dim=-1) # [B, L]
block_telemetry = {
"base_passes_executed": base_passes,
"emergency_passes_executed": emergency_passes,
"total_passes_executed": base_passes + emergency_passes,
"pass_metrics": pass_metrics,
"delta_state_norm": float(torch.norm(state_next).item())
}
return token_ids, state_next, block_telemetry
# ==============================================================================
# 7. Master Model: Hetman2ForCausalLM
# ==============================================================================
class Hetman2ForCausalLM(nn.Module):
"""
Hetman-2-NCM Master Hierarchical Generative Model.
Unifies Phase 1 (Draft), Phase 2 (Refine), Phase 3 (Decode) with Gated DeltaNet state.
"""
def __init__(self, config: Optional[Hetman2Config] = None):
super().__init__()
self.config = config or Hetman2Config()
self.embed_tokens = nn.Embedding(self.config.vocab_size, self.config.hidden_size)
self.phase1_drafter = Phase1BlueprintDrafter(self.config)
self.phase2_refiner = Phase2NCMRefiner(self.config)
self.phase3_decoder = Phase3NCMBlockDecoder(self.config)
# Tied weights: LM Head shares weights with embedding table
self.phase3_decoder.lm_head.weight = self.embed_tokens.weight
def forward(
self,
input_ids: torch.Tensor,
num_blocks: Optional[int] = None
) -> Dict[str, Any]:
"""
Executes full generation forward pass (Phase 1 -> Phase 2 -> Phase 3).
"""
B = input_ids.size(0)
K = num_blocks or self.config.max_blocks
# Embed prompt tokens with OOV clamping and empty-sequence protection
if input_ids.numel() == 0 or input_ids.size(1) == 0:
prompt_repr = torch.zeros(B, 0, self.config.hidden_size, device=input_ids.device)
else:
clamped_ids = torch.clamp(input_ids, 0, self.config.vocab_size - 1)
prompt_repr = self.embed_tokens(clamped_ids)
# Phase 1: Draft Blueprints
draft_bps = self.phase1_drafter(prompt_repr, num_blocks=K)
# Phase 2: Refine Blueprints (3 passes)
refined_bps, phase2_telemetry = self.phase2_refiner(draft_bps)
# Phase 3: Decode Blocks (8 passes per block + DeltaNet state propagation)
state: Optional[torch.Tensor] = None
all_block_tokens: List[torch.Tensor] = []
phase3_telemetry: List[List[Dict[str, float]]] = []
for k in range(K):
bp_k = refined_bps[:, k, :]
tokens_k, state, metrics_k = self.phase3_decoder.decode_single_block(bp_k, state)
all_block_tokens.append(tokens_k)
phase3_telemetry.append(metrics_k)
generated_tokens = torch.cat(all_block_tokens, dim=1) # [B, K * tokens_per_block]
return {
"generated_tokens": generated_tokens,
"draft_blueprints": draft_bps,
"refined_blueprints": refined_bps,
"final_deltanet_state": state,
"phase2_telemetry": phase2_telemetry,
"phase3_telemetry": phase3_telemetry
}
def generate(
self,
prompt_token_ids: torch.Tensor,
num_blocks: Optional[int] = None
) -> Dict[str, Any]:
"""
Public inference API matching full generation cycle.
"""
self.eval()
with torch.no_grad():
return self.forward(prompt_token_ids, num_blocks=num_blocks)