1313"""
1414
1515from typing import Any , Dict , List , Optional , Set , Tuple , Union , Mapping
16+ import inspect
1617import os
1718from tqdm .auto import tqdm
1819from einops import rearrange
1920
21+ import numpy as np
2022import torch
2123import torch .nn as nn
2224import torch .nn .functional as F
5456# ---------------------- DiT Network -----------------------
5557
5658
59+ class FlowKarrasUniPCScheduler (UniPCMultistepScheduler ):
60+ """UniPC whose sigmas follow the official Cosmos Predict2.5 Karras ramp.
61+
62+ A Karras schedule over ``[sigma_min, sigma_max]`` mapped into flow-matching
63+ units by ``sigma / (sigma + 1)``. Built here rather than through diffusers'
64+ ``use_karras_sigmas``, which only gained that conversion in 0.37.0.
65+ """
66+
67+ # Hardcoded in diffusers' _convert_to_karras; left unconfigurable to match.
68+ _rho : float = 7.0
69+
70+ def __init__ (self , * args , sigma_min : float = 0.01 , sigma_max : float = 200.0 , ** kwargs ):
71+ # diffusers >= 0.37 declares these itself. Forward them there so they are not
72+ # recorded in the config's `_use_default_values`, which `from_config` discards;
73+ # register them ourselves on older versions that lack the parameters.
74+ base_params = inspect .signature (UniPCMultistepScheduler .__init__ ).parameters
75+ forwarded = {k : v for k , v in (("sigma_min" , sigma_min ), ("sigma_max" , sigma_max )) if k in base_params }
76+ super ().__init__ (* args , ** forwarded , ** kwargs )
77+ if not forwarded :
78+ self .register_to_config (sigma_min = sigma_min , sigma_max = sigma_max )
79+
80+ def set_timesteps (
81+ self , num_inference_steps : Optional [int ] = None , device : Union [str , torch .device ] = None , ** kwargs
82+ ):
83+ if kwargs .get ("sigmas" ) is not None :
84+ raise ValueError ("FlowKarrasUniPCScheduler builds its own ramp; an explicit `sigmas` is unsupported." )
85+ assert num_inference_steps is not None , "num_inference_steps is required"
86+ super ().set_timesteps (num_inference_steps = num_inference_steps , device = device , ** kwargs )
87+ ramp = np .linspace (0.0 , 1.0 , num_inference_steps )
88+ min_inv_rho = self .config .sigma_min ** (1 / self ._rho )
89+ max_inv_rho = self .config .sigma_max ** (1 / self ._rho )
90+ sigmas = (max_inv_rho + ramp * (min_inv_rho - max_inv_rho )) ** self ._rho
91+ sigmas = sigmas / (sigmas + 1 )
92+ # sigmas stay on CPU to avoid per-step host/device traffic, as in diffusers.
93+ self .sigmas = torch .from_numpy (np .concatenate ([sigmas , [0.0 ]]).astype (np .float32 ))
94+ self .timesteps = torch .from_numpy (sigmas * self .config .num_train_timesteps ).to (device = device , dtype = torch .int64 )
95+
96+
5797class CosmosPredict2DiT (nn .Module ):
5898 """
5999 Cosmos Predict2 DiT (Diffusion Transformer) for video generation.
@@ -1100,7 +1140,7 @@ def sample(
11001140 guidance_scale : Optional [float ] = 5.0 ,
11011141 num_steps : int = 50 ,
11021142 skip_layers : Optional [List [int ]] = None ,
1103- skip_layers_start_percent : float = 0.0 ,
1143+ skip_layers_start_fraction : float = 0.0 ,
11041144 fps : Optional [torch .Tensor ] = None ,
11051145 conditioning_latents : Optional [torch .Tensor ] = None ,
11061146 num_conditioning_frames : int = 1 ,
@@ -1131,7 +1171,9 @@ def sample(
11311171 num_steps: Number of intervals in the official Karras schedule.
11321172 The sampler evaluates ``num_steps + 1`` timesteps.
11331173 skip_layers: List of transformer layers to skip (for skip-layer guidance).
1134- skip_layers_start_percent: Percentage of steps before starting to skip layers.
1174+ skip_layers_start_fraction: Fraction in [0, 1] of the sampling steps to complete
1175+ before skip-layer guidance becomes active.
1176+ ``skip_layers_start_percent`` is accepted as a deprecated alias.
11351177 fps: Frames per second tensor for temporal conditioning.
11361178 conditioning_latents: Latent frames to condition on for video2world mode,
11371179 shape (B, C, T, H, W). If provided, enables video2world mode.
@@ -1144,17 +1186,20 @@ def sample(
11441186 Returns:
11451187 The denoised sample tensor.
11461188 """
1189+ # Backward compatibility: the old argument name was skip_layers_start_percent.
1190+ if "skip_layers_start_percent" in kwargs :
1191+ skip_layers_start_fraction = kwargs .pop ("skip_layers_start_percent" )
1192+
11471193 assert self .schedule_type == "rf" , f"{ self .schedule_type } is not supported"
11481194
1149- # Match official Cosmos Predict2.5 inference. Diffusers uses the
1150- # configured sigma bounds for its Karras conversion; the official
1151- # `num_steps` denotes intervals, hence `num_steps + 1` ramp points.
1195+ # Match official Cosmos Predict2.5 inference: a Karras ramp over [0.01, 200] in
1196+ # flow-matching units, and `num_steps` denotes intervals, hence `num_steps + 1`
1197+ # ramp points.
11521198 if self .sample_scheduler is None :
1153- self .sample_scheduler = UniPCMultistepScheduler (
1199+ self .sample_scheduler = FlowKarrasUniPCScheduler (
11541200 num_train_timesteps = 1000 ,
11551201 prediction_type = "flow_prediction" ,
11561202 use_flow_sigmas = True ,
1157- use_karras_sigmas = True ,
11581203 sigma_min = 0.01 ,
11591204 sigma_max = 200.0 ,
11601205 )
@@ -1189,7 +1234,7 @@ def sample(
11891234 conditioning_latents_full = None
11901235 condition_mask = None
11911236 condition_mask_C = None
1192- initial_noise = None
1237+ initial_latents = None
11931238
11941239 if video2world_mode :
11951240 B , C , T , H , W = latents .shape
@@ -1211,10 +1256,10 @@ def sample(
12111256 latents , v2w_condition
12121257 )
12131258
1214- # Store initial noise for velocity replacement
1215- initial_noise = latents .clone ()
1259+ # Store the initial latents for velocity replacement
1260+ initial_latents = latents .clone ()
12161261
1217- for timestep in tqdm (timesteps , total = len ( timesteps ), desc = "Sampling" ):
1262+ for step_idx , timestep in enumerate ( tqdm (timesteps , desc = "Sampling" ) ):
12181263 # Normalize timestep to [0, 1] range
12191264 t = (timestep / self .sample_scheduler .config .num_train_timesteps ).expand (latents .shape [0 ])
12201265 t = self .noise_scheduler .safe_clamp (t , min = self .noise_scheduler .min_t , max = self .noise_scheduler .max_t ).to (
@@ -1259,12 +1304,14 @@ def sample(
12591304 neg_cond_with_mask ,
12601305 fps = fps ,
12611306 conditional_frame_timestep = conditional_frame_timestep ,
1307+ skip_layers = skip_layers if step_idx >= skip_layers_start_fraction * len (timesteps ) else None ,
12621308 )
12631309 velocity_pred = velocity_uncond + guidance_scale * (velocity_pred - velocity_uncond )
12641310
1265- # Replace velocity for conditioning frames with analytical velocity: v = noise - x0
1311+ # Replace velocity for conditioning frames with the constant velocity that carries
1312+ # the initial latents onto the conditioning frames over the interval [0, t_init].
12661313 if video2world_mode and denoise_replace_gt_frames :
1267- gt_velocity = initial_noise - conditioning_latents_full
1314+ gt_velocity = ( initial_latents - conditioning_latents_full ) / t_init
12681315 velocity_pred = gt_velocity * condition_mask_C + velocity_pred * (1 - condition_mask_C )
12691316
12701317 # Keep clean frames in the DiT input while UniPC evolves raw latents.
0 commit comments