""" PRISM Custom DiT (Diffusion Transformer) Architecture Definition. Implements a modular Diffusion Transformer with AdaLN-Zero conditioning, 2D spatial patchification, and cross-attention text guidance. """ import math from typing import Optional, Tuple import torch import torch.nn as nn import torch.nn.functional as F def modulate(x: torch.Tensor, shift: torch.Tensor, scale: torch.Tensor) -> torch.Tensor: """Modulates input tensor using affine scale and shift parameters.""" return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1) class TimestepEmbedder(nn.Module): """ Embeds scalar timesteps into vector representations via sinusoidal embeddings followed by a two-layer MLP. """ def __init__(self, hidden_size: int, frequency_embedding_size: int = 256): super().__init__() self.mlp = nn.Sequential( nn.Linear(frequency_embedding_size, hidden_size, bias=True), nn.SiLU(), nn.Linear(hidden_size, hidden_size, bias=True), ) self.frequency_embedding_size = frequency_embedding_size @staticmethod def sinusoidal_embedding(t: torch.Tensor, dim: int, max_period: int = 10000) -> torch.Tensor: half = dim // 2 freqs = torch.exp( -math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32, device=t.device) / half ) args = t[:, None].float() * freqs[None] embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1) if dim % 2 == 1: embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1) return embedding def forward(self, t: torch.Tensor) -> torch.Tensor: t_freq = self.sinusoidal_embedding(t, self.frequency_embedding_size) return self.mlp(t_freq) class PatchEmbed(nn.Module): """2D Image/Latent to Patch Embedding.""" def __init__(self, patch_size: int = 2, in_channels: int = 16, embed_dim: int = 1152): super().__init__() self.patch_size = patch_size self.proj = nn.Conv2d( in_channels, embed_dim, kernel_size=patch_size, stride=patch_size, bias=True ) def forward(self, x: torch.Tensor) -> torch.Tensor: # x: (B, C, H, W) -> proj -> (B, D, H/p, W/p) -> flatten -> (B, N, D) x = self.proj(x) x = x.flatten(2).transpose(1, 2) return x class PrismDiTBlock(nn.Module): """ DiT Block with AdaLN-Zero conditioning and Cross-Attention for text conditioning. """ def __init__(self, hidden_size: int, num_heads: int, context_dim: int = 1024, mlp_ratio: float = 4.0): super().__init__() self.hidden_size = hidden_size self.num_heads = num_heads self.head_dim = hidden_size // num_heads # Normalization layers self.norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6) self.norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6) self.norm_cross = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6) # Self-Attention self.self_attn = nn.MultiheadAttention(hidden_size, num_heads, batch_first=True) # Cross-Attention for text embeddings self.cross_attn = nn.MultiheadAttention( embed_dim=hidden_size, kdim=context_dim, vdim=context_dim, num_heads=num_heads, batch_first=True, ) # Feed-Forward Network mlp_hidden_dim = int(hidden_size * mlp_ratio) self.mlp = nn.Sequential( nn.Linear(hidden_size, mlp_hidden_dim, bias=True), nn.GELU(approximate="tanh"), nn.Linear(mlp_hidden_dim, hidden_size, bias=True), ) # AdaLN modulation table: 6 modulation scales (shift, scale, gate for self-attn and mlp) self.adaLN_modulation = nn.Sequential( nn.SiLU(), nn.Linear(hidden_size, 6 * hidden_size, bias=True), ) def forward( self, x: torch.Tensor, c: torch.Tensor, context: Optional[torch.Tensor] = None, context_mask: Optional[torch.Tensor] = None, ) -> torch.Tensor: # Modulation vectors from timestep / conditioning vector c shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = ( self.adaLN_modulation(c).chunk(6, dim=1) ) # 1. Self Attention with AdaLN modulation normed_x = modulate(self.norm1(x), shift_msa, scale_msa) attn_out, _ = self.self_attn(normed_x, normed_x, normed_x) x = x + gate_msa.unsqueeze(1) * attn_out # 2. Cross Attention with text context (if provided) if context is not None: normed_cross = self.norm_cross(x) cross_out, _ = self.cross_attn( query=normed_cross, key=context, value=context, key_padding_mask=context_mask, ) x = x + cross_out # 3. Feed Forward with AdaLN modulation normed_mlp = modulate(self.norm2(x), shift_mlp, scale_mlp) mlp_out = self.mlp(normed_mlp) x = x + gate_mlp.unsqueeze(1) * mlp_out return x class FinalLayer(nn.Module): """Final AdaLN layer and linear unpatchify projection.""" def __init__(self, hidden_size: int, patch_size: int, out_channels: int): super().__init__() self.norm_final = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6) self.linear = nn.Linear(hidden_size, patch_size * patch_size * out_channels, bias=True) self.adaLN_modulation = nn.Sequential( nn.SiLU(), nn.Linear(hidden_size, 2 * hidden_size, bias=True), ) def forward(self, x: torch.Tensor, c: torch.Tensor) -> torch.Tensor: shift, scale = self.adaLN_modulation(c).chunk(2, dim=1) x = modulate(self.norm_final(x), shift, scale) return self.linear(x) class PrismDiT(nn.Module): """ PRISM Diffusion Transformer Backbone. Supports: - Flow-matching or v-prediction / epsilon diffusion - Flexible latent channels (default: 16 channels for high-fidelity VAE) - Full multimodal context cross-attention """ def __init__( self, input_size: int = 64, patch_size: int = 2, in_channels: int = 16, hidden_size: int = 1152, depth: int = 28, num_heads: int = 16, mlp_ratio: float = 4.0, context_dim: int = 1024, ): super().__init__() self.in_channels = in_channels self.out_channels = in_channels self.patch_size = patch_size self.num_heads = num_heads self.hidden_size = hidden_size self.x_embedder = PatchEmbed(patch_size, in_channels, hidden_size) self.t_embedder = TimestepEmbedder(hidden_size) # 2D Positional embeddings (learnable or fixed sinusoidal) num_patches = (input_size // patch_size) ** 2 self.pos_embed = nn.Parameter(torch.zeros(1, num_patches, hidden_size)) # Transformer blocks self.blocks = nn.ModuleList([ PrismDiTBlock(hidden_size, num_heads, context_dim=context_dim, mlp_ratio=mlp_ratio) for _ in range(depth) ]) # Final projection layer self.final_layer = FinalLayer(hidden_size, patch_size, self.out_channels) self.initialize_weights() def initialize_weights(self): # Initialize patch embedding like linear projection w = self.x_embedder.proj.weight.data nn.init.xavier_uniform_(w.view([w.shape[0], -1])) nn.init.constant_(self.x_embedder.proj.bias, 0) # Initialize pos_embed nn.init.normal_(self.pos_embed, std=0.02) # Zero-out AdaLN modulation layers for identity start for block in self.blocks: nn.init.constant_(block.adaLN_modulation[-1].weight, 0) nn.init.constant_(block.adaLN_modulation[-1].bias, 0) nn.init.constant_(self.final_layer.adaLN_modulation[-1].weight, 0) nn.init.constant_(self.final_layer.adaLN_modulation[-1].bias, 0) nn.init.constant_(self.final_layer.linear.weight, 0) nn.init.constant_(self.final_layer.linear.bias, 0) def unpatchify(self, x: torch.Tensor, h: int, w: int) -> torch.Tensor: """ x: (B, N, patch_size**2 * C) Returns: (B, C, H, W) """ c = self.out_channels p = self.patch_size ph, pw = h // p, w // p assert ph * pw == x.shape[1] x = x.reshape(shape=(x.shape[0], ph, pw, p, p, c)) x = torch.einsum("nhwpqc->nchpwq", x) imgs = x.reshape(shape=(x.shape[0], c, ph * p, pw * p)) return imgs def forward( self, x: torch.Tensor, t: torch.Tensor, context: Optional[torch.Tensor] = None, context_mask: Optional[torch.Tensor] = None, ) -> torch.Tensor: """ Forward pass for PRISM DiT. x: (B, C, H, W) Latent tensor t: (B,) Timestep tensor context: (B, L, context_dim) Text/multimodal conditioning sequence """ b, c, h, w = x.shape x = self.x_embedder(x) # Add positional embedding (interpolating if resolution differs) if x.shape[1] == self.pos_embed.shape[1]: x = x + self.pos_embed else: # Dynamic positional interpolation for arbitrary aspect ratios pos_embed = F.interpolate( self.pos_embed.transpose(1, 2).reshape(1, self.hidden_size, int(math.sqrt(self.pos_embed.shape[1])), -1), size=(h // self.patch_size, w // self.patch_size), mode="bicubic", align_corners=False, ).flatten(2).transpose(1, 2) x = x + pos_embed c_emb = self.t_embedder(t) for block in self.blocks: x = block(x, c_emb, context=context, context_mask=context_mask) x = self.final_layer(x, c_emb) x = self.unpatchify(x, h, w) return x