SubstanceSHIFT/wan-2-2-first-last-frame
0
1"""2"""3 4from typing import Any5from typing import Callable6from typing import ParamSpec7 8import spaces9import torch10from torch.utils._pytree import tree_map_only11from torchao.quantization import quantize_12from torchao.quantization import Float8DynamicActivationFloat8WeightConfig13from torchao.quantization import Int8WeightOnlyConfig14 15from optimization_utils import capture_component_call16from optimization_utils import aoti_compile17from optimization_utils import drain_module_parameters18 19 20P = ParamSpec('P')21 22# --- CORRECTED DYNAMIC SHAPING ---23 24# VAE temporal scale factor is 1, latent_frames = num_frames. Range is [8, 81].25LATENT_FRAMES_DIM = torch.export.Dim('num_latent_frames', min=8, max=81)26 27# The transformer has a patch_size of (1, 2, 2), which means the input latent height and width28# are effectively divided by 2. This creates constraints that fail if the symbolic tracer29# assumes odd numbers are possible.30#31# To solve this, we define the dynamic dimension for the *patched* (i.e., post-division) size,32# and then express the input shape as 2 * this dimension. This mathematically guarantees33# to the compiler that the input latent dimensions are always even, satisfying the constraints.34 35# App range for pixel dimensions: [480, 832]. VAE scale factor is 8.36# Latent dimension range: [480/8, 832/8] = [60, 104].37# Patched latent dimension range: [60/2, 104/2] = [30, 52].38LATENT_PATCHED_HEIGHT_DIM = torch.export.Dim('latent_patched_height', min=30, max=52)39LATENT_PATCHED_WIDTH_DIM = torch.export.Dim('latent_patched_width', min=30, max=52)40 41# Now, we define the dynamic shapes for the transformer's `hidden_states` input,42# which has the shape (batch_size, channels, num_frames, height, width).43TRANSFORMER_DYNAMIC_SHAPES = {44 'hidden_states': {45 2: LATENT_FRAMES_DIM,46 3: 2 * LATENT_PATCHED_HEIGHT_DIM, # Guarantees even height47 4: 2 * LATENT_PATCHED_WIDTH_DIM, # Guarantees even width48 },49}50 51# --- END OF CORRECTION ---52 53 54INDUCTOR_CONFIGS = {55 'conv_1x1_as_mm': True,56 'epilogue_fusion': False,57 'coordinate_descent_tuning': True,58 'coordinate_descent_check_all_directions': True,59 'max_autotune': True,60 'triton.cudagraphs': True,61}62 63 64def optimize_pipeline_(pipeline: Callable[P, Any], *args: P.args, **kwargs: P.kwargs):65 66 @spaces.GPU(duration=1500)67 def compile_transformer():68 69 # This LoRA fusion part remains the same70 pipeline.load_lora_weights(71 "Kijai/WanVideo_comfy", 72 weight_name="Lightx2v/lightx2v_I2V_14B_480p_cfg_step_distill_rank128_bf16.safetensors", 73 adapter_name="lightx2v"74 )75 kwargs_lora = {}76 kwargs_lora["load_into_transformer_2"] = True77 pipeline.load_lora_weights(78 "Kijai/WanVideo_comfy", 79 weight_name="Lightx2v/lightx2v_I2V_14B_480p_cfg_step_distill_rank128_bf16.safetensors", 80 adapter_name="lightx2v_2", **kwargs_lora81 )82 pipeline.set_adapters(["lightx2v", "lightx2v_2"], adapter_weights=[1., 1.])83 pipeline.fuse_lora(adapter_names=["lightx2v"], lora_scale=3., components=["transformer"])84 pipeline.fuse_lora(adapter_names=["lightx2v_2"], lora_scale=1., components=["transformer_2"])85 pipeline.unload_lora_weights()86 87 # Capture a single call to get the args/kwargs structure88 with capture_component_call(pipeline, 'transformer') as call:89 pipeline(*args, **kwargs)90 91 dynamic_shapes = tree_map_only((torch.Tensor, bool), lambda t: None, call.kwargs)92 dynamic_shapes |= TRANSFORMER_DYNAMIC_SHAPES93 94 # Quantization remains the same95 quantize_(pipeline.transformer, Float8DynamicActivationFloat8WeightConfig())96 quantize_(pipeline.transformer_2, Float8DynamicActivationFloat8WeightConfig())97 98 # --- SIMPLIFIED COMPILATION ---99 100 exported_1 = torch.export.export(101 mod=pipeline.transformer,102 args=call.args,103 kwargs=call.kwargs,104 dynamic_shapes=dynamic_shapes,105 )106 107 exported_2 = torch.export.export(108 mod=pipeline.transformer_2,109 args=call.args,110 kwargs=call.kwargs,111 dynamic_shapes=dynamic_shapes,112 )113 114 compiled_1 = aoti_compile(exported_1, INDUCTOR_CONFIGS)115 compiled_2 = aoti_compile(exported_2, INDUCTOR_CONFIGS)116 117 # Return the two compiled models118 return compiled_1, compiled_2119 120 121 # Quantize text encoder (same as before)122 quantize_(pipeline.text_encoder, Int8WeightOnlyConfig())123 124 # Get the two dynamically-shaped compiled models125 compiled_transformer_1, compiled_transformer_2 = compile_transformer()126 127 # --- SIMPLIFIED ASSIGNMENT ---128 129 pipeline.transformer.forward = compiled_transformer_1130 drain_module_parameters(pipeline.transformer)131 132 pipeline.transformer_2.forward = compiled_transformer_2133 drain_module_parameters(pipeline.transformer_2)