Modilify-Mk1-preview / modilify_mk1 /latent_deliberation.py
ydy9038074's picture
Publish Modilify Mk1 Preview
164d101 verified
Raw
History Blame Contribute Delete
16.5 kB
# Copyright 2026 Modilify
# SPDX-License-Identifier: LicenseRef-Modilify-Open-Model-1.0
"""Fixed-shape latent deliberation state for Modilify Mk1 decoding.
The state deliberately contains no vocabulary-sized tensors. Keeping the
per-canvas information in a small latent space prevents iterative diffusion
rollouts from retaining one logits/probability allocation per denoise pass.
"""
from __future__ import annotations
from dataclasses import dataclass
import torch
from torch import nn
@dataclass
class LatentDeliberationState:
"""Persistent, fixed-size state for one or more canvas episodes."""
token_latents: torch.Tensor
memory_slots: torch.Tensor
confidence: torch.Tensor
entropy: torch.Tensor
age: torch.Tensor
token_changed: torch.Tensor
confidence_delta: torch.Tensor
entropy_delta: torch.Tensor
ponder_steps: torch.Tensor
stagnation_steps: torch.Tensor
@classmethod
def empty(
cls,
*,
batch_size: int,
canvas_length: int,
latent_dim: int,
memory_slots: int,
device: torch.device,
dtype: torch.dtype,
) -> "LatentDeliberationState":
"""Create a zero-initialized recurrent state.
Args:
batch_size: Number of independent sequences.
canvas_length: Number of rolling canvas positions.
latent_dim: Width of each latent token and memory slot.
memory_slots: Number of persistent memory slots.
device: Allocation device.
dtype: Floating-point dtype for latent tensors.
Returns:
A zero-initialized state with integer progress clocks.
"""
return cls(
token_latents=torch.zeros(
batch_size, canvas_length, latent_dim, device=device, dtype=dtype
),
memory_slots=torch.zeros(
batch_size, memory_slots, latent_dim, device=device, dtype=dtype
),
confidence=torch.zeros(
batch_size, canvas_length, device=device, dtype=torch.float32
),
entropy=torch.zeros(
batch_size, canvas_length, device=device, dtype=torch.float32
),
age=torch.zeros(
batch_size, canvas_length, device=device, dtype=torch.int32
),
token_changed=torch.zeros(
batch_size, canvas_length, device=device, dtype=torch.float32
),
confidence_delta=torch.zeros(
batch_size, canvas_length, device=device, dtype=torch.float32
),
entropy_delta=torch.zeros(
batch_size, canvas_length, device=device, dtype=torch.float32
),
ponder_steps=torch.zeros(batch_size, device=device, dtype=torch.int32),
stagnation_steps=torch.zeros(batch_size, device=device, dtype=torch.int32),
)
def advance_trajectory_clocks(
ponder_steps: torch.Tensor,
stagnation_steps: torch.Tensor,
*,
commit_lengths: torch.LongTensor,
active_rows: torch.BoolTensor,
progress_scores: torch.Tensor,
min_progress: float,
) -> tuple[torch.IntTensor, torch.IntTensor]:
"""Advance useful-ponder and true-stagnation clocks for each row.
Args:
ponder_steps: Total waiting steps for each row.
stagnation_steps: Consecutive non-improving steps for each row.
commit_lengths: Number of committed tokens for each row.
active_rows: Rows that are still generating.
progress_scores: Signed fused-risk improvements.
min_progress: Smallest improvement that resets stagnation.
Returns:
Updated ponder and stagnation counters.
"""
if min_progress < 0:
raise ValueError("`min_progress` must be non-negative.")
if not (
ponder_steps.shape == stagnation_steps.shape == commit_lengths.shape
== active_rows.shape == progress_scores.shape
):
raise ValueError("Trajectory clock inputs must share shape [batch].")
committed = commit_lengths.gt(0)
waiting = active_rows & ~committed
improving = progress_scores.ge(min_progress)
next_ponder = torch.where(
committed, torch.zeros_like(ponder_steps), ponder_steps + waiting.to(torch.int32)
)
next_stagnation = torch.where(
committed,
torch.zeros_like(stagnation_steps),
torch.where(
waiting & improving,
torch.zeros_like(stagnation_steps),
stagnation_steps + waiting.to(torch.int32),
),
)
return next_ponder.to(torch.int32), next_stagnation.to(torch.int32)
def should_force_trajectory_jump(
ponder_steps: torch.Tensor,
stagnation_steps: torch.Tensor,
*,
max_ponder_steps: int,
stagnation_threshold: int,
) -> torch.BoolTensor:
"""Return rows that exhausted either inference progress clock.
Args:
ponder_steps: Total waiting steps for each row.
stagnation_steps: Consecutive non-improving steps for each row.
max_ponder_steps: Maximum allowed waiting steps.
stagnation_threshold: Maximum consecutive stagnation steps.
Returns:
Boolean mask selecting rows that must use a forced jump.
"""
if max_ponder_steps <= 0 or stagnation_threshold <= 0:
raise ValueError("Trajectory jump limits must be positive.")
return ponder_steps.ge(max_ponder_steps) | stagnation_steps.ge(stagnation_threshold)
class _TemporalTransformerCell(nn.Module):
"""One-step recurrent token update with fixed-slot memory attention."""
def __init__(
self, latent_dim: int, num_heads: int, dropout: float,
local_attention_window: int,
) -> None:
super().__init__()
self.state_norm = nn.LayerNorm(latent_dim)
self.observation_norm = nn.LayerNorm(latent_dim)
# A slot's learned identity is only used for attention addressing. The
# recurrent state itself remains pure memory content so commit shifts
# cannot accidentally write positional identity into persistent state.
self.memory_address_norm = nn.LayerNorm(latent_dim)
self.memory_value_norm = nn.LayerNorm(latent_dim)
self.temporal_update = nn.Linear(2 * latent_dim, 2 * latent_dim)
self.local_attention = nn.MultiheadAttention(
latent_dim, num_heads, dropout=dropout, batch_first=True
)
self.local_attention_window = local_attention_window
self.register_buffer("_local_attention_mask", torch.empty(0), persistent=False)
self.token_memory_attention = nn.MultiheadAttention(
latent_dim, num_heads, dropout=dropout, batch_first=True
)
self.memory_token_attention = nn.MultiheadAttention(
latent_dim, num_heads, dropout=dropout, batch_first=True
)
self.token_ff_norm = nn.LayerNorm(latent_dim)
self.memory_ff_norm = nn.LayerNorm(latent_dim)
self.stored_token_norm = nn.LayerNorm(latent_dim)
self.stored_memory_norm = nn.LayerNorm(latent_dim)
expansion = latent_dim * 4
self.token_ff = nn.Sequential(
nn.Linear(latent_dim, expansion),
nn.SiLU(),
nn.Linear(expansion, latent_dim),
)
self.memory_ff = nn.Sequential(
nn.Linear(latent_dim, expansion),
nn.SiLU(),
nn.Linear(expansion, latent_dim),
)
def forward(
self,
previous_tokens: torch.Tensor,
observation: torch.Tensor,
memory: torch.Tensor,
memory_slot_identity: torch.Tensor,
) -> tuple[torch.Tensor, torch.Tensor]:
gate_logits, candidate = self.temporal_update(
torch.cat(
(self.state_norm(previous_tokens), self.observation_norm(observation)),
dim=-1,
)
).chunk(2, dim=-1)
gate = torch.sigmoid(gate_logits)
tokens = gate * previous_tokens + (1.0 - gate) * torch.nn.functional.silu(candidate)
if (
self._local_attention_mask.shape != (tokens.shape[1], tokens.shape[1])
or self._local_attention_mask.device != tokens.device
or self._local_attention_mask.dtype != tokens.dtype
):
positions = torch.arange(tokens.shape[1], device=tokens.device)
allowed = (
positions[:, None] - positions[None, :]
).abs() < self.local_attention_window
self._local_attention_mask = torch.zeros(
tokens.shape[1], tokens.shape[1], device=tokens.device, dtype=tokens.dtype
).masked_fill(~allowed, torch.finfo(tokens.dtype).min)
local_update, _ = self.local_attention(
self.state_norm(tokens), self.state_norm(tokens), self.state_norm(tokens),
attn_mask=self._local_attention_mask, need_weights=False,
)
tokens = tokens + local_update
addressed_memory = self.memory_address_norm(memory + memory_slot_identity)
memory_values = self.memory_value_norm(memory)
token_memory_update, _ = self.token_memory_attention(
self.state_norm(tokens), addressed_memory, memory_values, need_weights=False
)
tokens = tokens + token_memory_update
tokens = tokens + self.token_ff(self.token_ff_norm(tokens))
memory_token_update, _ = self.memory_token_attention(
addressed_memory,
self.state_norm(tokens),
self.state_norm(tokens),
need_weights=False,
)
memory = memory + memory_token_update
memory = memory + self.memory_ff(self.memory_ff_norm(memory))
# This module is a recurrent cell, not a depth-only Transformer block.
# Persist normalized state so repeated denoise updates cannot accumulate
# an unbounded residual magnitude across time.
return self.stored_token_norm(tokens), self.stored_memory_norm(memory)
class LatentDeliberationTransformer(nn.Module):
"""Small recurrent Transformer that compresses repeated denoise context."""
def __init__(
self,
*,
hidden_size: int,
latent_dim: int = 512,
memory_slots: int = 16,
num_layers: int = 2,
num_heads: int = 8,
local_attention_window: int = 32,
dropout: float = 0.0,
) -> None:
super().__init__()
if latent_dim % num_heads:
raise ValueError("`latent_dim` must be divisible by `num_heads`.")
if local_attention_window <= 0:
raise ValueError("`local_attention_window` must be positive.")
self.hidden_size = hidden_size
self.latent_dim = latent_dim
self.memory_slots = memory_slots
self.heavy_projection = nn.Linear(hidden_size, latent_dim, bias=False)
self.embedding_projection = nn.Linear(hidden_size, latent_dim, bias=False)
self.scalar_projection = nn.Linear(11, latent_dim, bias=False)
self.blocks = nn.ModuleList(
[
_TemporalTransformerCell(
latent_dim, num_heads, dropout, local_attention_window
)
for _ in range(num_layers)
]
)
self.output_norm = nn.LayerNorm(latent_dim)
self.output_projection = nn.Linear(latent_dim, hidden_size, bias=False)
self.memory_slot_identity = nn.Parameter(torch.empty(memory_slots, latent_dim))
self.reset_memory_slot_identity()
@torch.no_grad()
def reset_memory_slot_identity(self) -> None:
"""Restore learned memory addresses after generic initialization."""
nn.init.normal_(self.memory_slot_identity, mean=0.0, std=0.02)
def project_context(self, token_latents: torch.Tensor) -> torch.Tensor:
"""Translate latent state into a self-conditioning embedding."""
normalized_tokens = self.output_norm(token_latents)
return self.output_projection(normalized_tokens)
def forward(
self,
*,
heavy_hidden: torch.Tensor,
token_embeddings: torch.Tensor,
confidence: torch.Tensor,
entropy: torch.Tensor,
state: LatentDeliberationState,
) -> tuple[torch.Tensor, LatentDeliberationState]:
"""Advance latent memory and produce decoder self-conditioning.
Args:
heavy_hidden: Hidden states from the previous decoder pass.
token_embeddings: Embeddings of current noisy canvas tokens.
confidence: Proposal confidence for each canvas position.
entropy: Proposal entropy for each canvas position.
state: Persistent latent state from the preceding pass.
Returns:
Self-conditioning embeddings and the next compact latent state.
"""
if heavy_hidden.ndim != 3:
raise ValueError("`heavy_hidden` must have shape [batch, canvas, hidden].")
if heavy_hidden.shape != token_embeddings.shape:
raise ValueError("`heavy_hidden` and `token_embeddings` must have the same shape.")
batch_size, canvas_length, hidden_size = heavy_hidden.shape
if hidden_size != self.hidden_size:
raise ValueError("Unexpected hidden size for latent deliberation.")
expected_state = (batch_size, canvas_length, self.latent_dim)
if state.token_latents.shape != expected_state:
raise ValueError("State token latents do not match the current canvas.")
if state.memory_slots.shape != (batch_size, self.memory_slots, self.latent_dim):
raise ValueError("State memory slots do not match this module.")
if state.age.dtype is not torch.int32:
raise TypeError("Latent deliberation ages must use int32.")
scalars = torch.stack(
(
confidence.to(dtype=heavy_hidden.dtype),
entropy.to(dtype=heavy_hidden.dtype).log1p(),
state.age.to(dtype=heavy_hidden.dtype).clamp_max(32767).log1p(),
torch.linspace(
-1.0, 1.0, canvas_length, device=heavy_hidden.device,
dtype=heavy_hidden.dtype,
).unsqueeze(0).expand(batch_size, -1),
state.token_changed.to(dtype=heavy_hidden.dtype),
state.confidence_delta.to(dtype=heavy_hidden.dtype),
state.entropy_delta.to(dtype=heavy_hidden.dtype).sign()
* state.entropy_delta.to(dtype=heavy_hidden.dtype).abs().log1p(),
state.ponder_steps.to(dtype=heavy_hidden.dtype).log1p()[:, None]
.expand(-1, canvas_length),
state.stagnation_steps.to(dtype=heavy_hidden.dtype).log1p()[:, None]
.expand(-1, canvas_length),
confidence.to(dtype=heavy_hidden.dtype)
* torch.exp(-entropy.to(dtype=heavy_hidden.dtype).clamp_min(0.0)),
state.confidence_delta.to(dtype=heavy_hidden.dtype).clamp_min(0.0)
+ (-state.entropy_delta.to(dtype=heavy_hidden.dtype)).clamp_min(0.0).log1p(),
),
dim=-1,
)
observation = (
self.heavy_projection(heavy_hidden)
+ self.embedding_projection(token_embeddings)
+ self.scalar_projection(scalars)
)
tokens = state.token_latents
memory = state.memory_slots
slot_identity = self.memory_slot_identity.to(device=memory.device, dtype=memory.dtype)
slot_identity = slot_identity.unsqueeze(0).expand(batch_size, -1, -1)
for block in self.blocks:
tokens, memory = block(tokens, observation, memory, slot_identity)
observation = tokens
context = self.project_context(tokens)
next_state = LatentDeliberationState(
token_latents=tokens,
memory_slots=memory,
confidence=confidence.to(dtype=torch.float32),
entropy=entropy.to(dtype=torch.float32),
age=state.age,
token_changed=state.token_changed,
confidence_delta=state.confidence_delta,
entropy_delta=state.entropy_delta,
ponder_steps=state.ponder_steps,
stagnation_steps=state.stagnation_steps,
)
return context, next_state
__all__ = [
"LatentDeliberationState", "LatentDeliberationTransformer",
"advance_trajectory_clocks", "should_force_trajectory_jump",
]