from __future__ import annotations import math from numbers import Integral import torch from torch import nn GEN_IMAGE_ROW_MARKERS = 'gen_image_row_markers' GEN_IMAGE_FIXED_PREFIX_TOKENS = 'gen_image_fixed_prefix_tokens' GENERATION_SKIP_DOWNSAMPLE = 'generation_skip_down_sample' ROW_MARKER_OFFSET = 0 IMAGE_MASK_OFFSET = 1 IMAGE_END_OFFSET = 2 FIXED_PREFIX_OFFSET = 3 RESERVED_GENERATION_ROWS = 256 MARKERS_PER_RAW_ROW = 2 UNCOMPRESSED_MARKERS_PER_RAW_ROW = 1 _SUPPORTED_MARKERS_PER_ROW = (UNCOMPRESSED_MARKERS_PER_RAW_ROW, MARKERS_PER_RAW_ROW) def _validate_markers_per_row(value) -> int: if isinstance(value, bool) or not isinstance(value, Integral): raise TypeError('markers_per_row must be an integer') value = int(value) if value not in _SUPPORTED_MARKERS_PER_ROW: raise ValueError('markers_per_row must be 1 for an uncompressed image grid or 2 for a grid that will undergo 2x spatial compression') return value def _validate_fixed_prefix_count(value) -> int: if value is None: return 0 if isinstance(value, bool) or not isinstance(value, Integral): raise TypeError(f'config.{GEN_IMAGE_FIXED_PREFIX_TOKENS} must be an integer') value = int(value) max_tokens = RESERVED_GENERATION_ROWS - FIXED_PREFIX_OFFSET if not 0 <= value <= max_tokens: raise ValueError(f'config.{GEN_IMAGE_FIXED_PREFIX_TOKENS} must be between 0 and {max_tokens}') return value def image_row_markers_enabled(config) -> bool: value = getattr(config, GEN_IMAGE_ROW_MARKERS, False) if value is None: return False if not isinstance(value, bool): raise TypeError(f'config.{GEN_IMAGE_ROW_MARKERS} must be boolean') return value def resolve_generation_skip_down_sample(config, *, runtime_value=None) -> bool: configured = getattr(config, GENERATION_SKIP_DOWNSAMPLE, None) if configured is not None and (not isinstance(configured, bool)): raise TypeError(f'config.{GENERATION_SKIP_DOWNSAMPLE} must be boolean or null') runtime_configured = runtime_value is not None runtime_enabled = bool(runtime_value) if configured is None: return runtime_enabled if runtime_configured and runtime_enabled != configured: raise ValueError(f'SKIP_DOWN_SAMPLE conflicts with checkpoint-backed config.{GENERATION_SKIP_DOWNSAMPLE}: runtime={runtime_enabled}, checkpoint={configured}') return configured def image_generation_special_ids(codebook_size: int) -> tuple[int, int, int]: if isinstance(codebook_size, bool) or not isinstance(codebook_size, Integral): raise TypeError('codebook_size must be a positive integer') codebook_size = int(codebook_size) if codebook_size < 1: raise ValueError('codebook_size must be a positive integer') return (codebook_size + ROW_MARKER_OFFSET, codebook_size + IMAGE_MASK_OFFSET, codebook_size + IMAGE_END_OFFSET) def image_fixed_prefix_token_count(config) -> int: return _validate_fixed_prefix_count(getattr(config, GEN_IMAGE_FIXED_PREFIX_TOKENS, 0)) def image_generation_fixed_prefix_ids(codebook_size: int, count: int) -> tuple[int, ...]: image_generation_special_ids(codebook_size) count = _validate_fixed_prefix_count(count) start = int(codebook_size) + FIXED_PREFIX_OFFSET return tuple(range(start, start + count)) def split_generation_fixed_prefix(generation_placeholder_mask: torch.Tensor, *, codebook_size: int, count: int) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: if not torch.is_tensor(generation_placeholder_mask): raise TypeError('generation_placeholder_mask must be a tensor') if generation_placeholder_mask.ndim != 2: raise ValueError('generation_placeholder_mask must have shape [batch, sequence]') placeholder_mask = generation_placeholder_mask.to(dtype=torch.bool) fixed_ids = image_generation_fixed_prefix_ids(codebook_size, count) if not fixed_ids: return (torch.zeros_like(placeholder_mask), placeholder_mask, torch.empty(0, dtype=torch.long, device=placeholder_mask.device)) previous_placeholder = torch.cat((torch.zeros_like(placeholder_mask[:, :1]), placeholder_mask[:, :-1]), dim=1) run_starts = placeholder_mask & ~previous_placeholder positions = torch.arange(placeholder_mask.shape[1], device=placeholder_mask.device, dtype=torch.long).unsqueeze(0).expand_as(placeholder_mask) most_recent_start = torch.where(run_starts, positions, positions.new_full((), -1)).cummax(dim=1).values run_offsets = positions - most_recent_start fixed_mask = placeholder_mask & run_offsets.lt(len(fixed_ids)) image_mask = placeholder_mask & ~fixed_mask next_placeholder = torch.cat((placeholder_mask[:, 1:], torch.zeros_like(placeholder_mask[:, :1])), dim=1) run_ends = placeholder_mask & ~next_placeholder run_lengths = run_offsets[run_ends] + 1 if bool(run_lengths.le(len(fixed_ids)).any()): shortest = int(run_lengths.min().item()) raise ValueError(f'each generated-image placeholder run must contain the fixed prefix plus at least one image slot; shortest run={shortest}, fixed prefix={len(fixed_ids)}') dense_ids = int(codebook_size) + FIXED_PREFIX_OFFSET + run_offsets return (fixed_mask, image_mask, dense_ids[fixed_mask]) def generation_embedding_capacity(generation_model) -> int: embedding = getattr(generation_model, 'gen_embedding', None) if isinstance(embedding, nn.Embedding): return int(embedding.num_embeddings) if isinstance(embedding, nn.Module): for module in embedding.modules(): if isinstance(module, nn.Embedding): return int(module.num_embeddings) raise TypeError('generation model does not expose an nn.Embedding lookup') def image_row_marker_count(generation_model, *, skip_down_sample=False) -> int: compression_available = getattr(generation_model, 'packed_image_codec', None) is not None or getattr(generation_model, 'downsample_gen', None) is not None if compression_available and (not skip_down_sample): return MARKERS_PER_RAW_ROW return UNCOMPRESSED_MARKERS_PER_RAW_ROW def validate_image_row_marker_support(generation_model, *, skip_down_sample=False): config = generation_model.config if 'emu3' not in 'emu3'.lower(): raise NotImplementedError("generation image row markers currently require Emu3's single-codebook tokenizer") image_row_marker_count(generation_model, skip_down_sample=skip_down_sample) codebook_size = int(generation_model.vqvae.config.num_vq_embeddings) (row_id, mask_id, end_id) = image_generation_special_ids(codebook_size) capacity = generation_embedding_capacity(generation_model) if end_id >= capacity: raise ValueError(f'generation embedding has no spare rows for row/end markers: required ID {end_id}, capacity {capacity}') return (row_id, mask_id, end_id) def validate_image_fixed_prefix_support(generation_model) -> tuple[int, ...]: config = generation_model.config count = image_fixed_prefix_token_count(config) if count == 0: return () if getattr(config, 'generation_embedding_scale', None) is None: raise ValueError('generation fixed-prefix tokens require factorized generation scaling so uncompressed tokens receive the common input scale') if 'emu3' not in 'emu3'.lower(): raise NotImplementedError("generation fixed-prefix tokens currently require Emu3's single-codebook tokenizer") codebook_size = int(generation_model.vqvae.config.num_vq_embeddings) fixed_ids = image_generation_fixed_prefix_ids(codebook_size, count) capacity = generation_embedding_capacity(generation_model) if fixed_ids[-1] >= capacity: raise ValueError(f'generation embedding has no spare rows for the requested fixed prefix: required ID {fixed_ids[-1]}, capacity {capacity}') special_ids = set(image_generation_special_ids(codebook_size)) if special_ids.intersection(fixed_ids): raise RuntimeError('generation fixed-prefix IDs overlap image mask/row/end markers') return fixed_ids def augmented_image_shape(gen_shape, *, markers_per_row: int=MARKERS_PER_RAW_ROW) -> tuple[int, int]: if gen_shape is None or len(gen_shape) != 2: raise ValueError('gen_shape must contain image height and width') markers_per_row = _validate_markers_per_row(markers_per_row) (height, width) = (int(value) for value in gen_shape) if height < 1 or width < 1: raise ValueError('image row markers require a positive image grid') if markers_per_row == MARKERS_PER_RAW_ROW and (height % 2 or width % 2): raise ValueError('two-column image row markers require an even-height, even-width grid for 2x spatial compression') return (height, width + markers_per_row) def append_image_row_markers(token_ids: torch.Tensor, gen_shape, *, row_marker_id: int, image_end_id: int, markers_per_row: int=MARKERS_PER_RAW_ROW) -> tuple[torch.Tensor, tuple[int, int], torch.Tensor]: if not torch.is_tensor(token_ids) or token_ids.ndim != 2: raise ValueError('token_ids must have shape [batch, raw_image_tokens]') markers_per_row = _validate_markers_per_row(markers_per_row) (height, width) = (int(value) for value in gen_shape) augmented_shape = augmented_image_shape((height, width), markers_per_row=markers_per_row) if token_ids.shape[-1] != height * width: raise ValueError(f'raw image token count does not match gen_shape: {token_ids.shape[-1]} != {height * width}') grid = token_ids.reshape(token_ids.shape[0], height, width) markers = torch.full((token_ids.shape[0], height, markers_per_row), int(row_marker_id), dtype=token_ids.dtype, device=token_ids.device) markers[:, -1, :] = int(image_end_id) augmented = torch.cat((grid, markers), dim=-1).flatten(1) structural_mask = torch.zeros_like(augmented, dtype=torch.bool) structural_mask.reshape(token_ids.shape[0], *augmented_shape)[:, :, -markers_per_row:] = True return (augmented, augmented_shape, structural_mask) def strip_image_row_markers(token_ids: torch.Tensor, original_gen_shape, *, markers_per_row: int=MARKERS_PER_RAW_ROW) -> torch.Tensor: if not torch.is_tensor(token_ids) or token_ids.ndim != 2: raise ValueError('token_ids must have shape [batch, augmented_tokens]') markers_per_row = _validate_markers_per_row(markers_per_row) (height, width) = (int(value) for value in original_gen_shape) augmented_shape = augmented_image_shape((height, width), markers_per_row=markers_per_row) if token_ids.shape[-1] != math.prod(augmented_shape): raise ValueError(f'augmented image token count does not match marker grid: {token_ids.shape[-1]} != {math.prod(augmented_shape)}') return token_ids.reshape(token_ids.shape[0], *augmented_shape)[:, :, :width].flatten(1)