@@ -118,6 +118,7 @@ def __call__(
118118 use_cfg_cache : bool = False ,
119119 use_sen_cache : bool = False ,
120120 use_kv_cache : bool = False ,
121+ output_type : str = "pil" ,
121122 ):
122123 config = getattr (self , "config" , None )
123124 if max_sequence_length is None :
@@ -203,6 +204,9 @@ def __call__(
203204 latents .block_until_ready ()
204205 trace ["denoise_total" ] = time .perf_counter () - t_denoise_start
205206
207+ if output_type == "latent" :
208+ return latents , trace
209+
206210 t_decode_start = time .perf_counter ()
207211 video = self ._decode_latents_to_video (latents , trace = trace )
208212 if hasattr (video , "block_until_ready" ):
@@ -279,6 +283,8 @@ def run_inference_2_2(
279283 high_transformer = nnx .merge (high_noise_graphdef , high_noise_state , high_noise_rest )
280284 kv_cache_high , encoder_attention_mask_high = high_transformer .compute_kv_cache (prompt_embeds_combined )
281285
286+ timesteps = jnp .array (scheduler_state .timesteps , dtype = jnp .int32 )
287+
282288 # ── SenCache path (arXiv:2602.24208) ──
283289 if use_sen_cache and do_classifier_free_guidance :
284290 timesteps_np = np .array (scheduler_state .timesteps , dtype = np .int32 )
@@ -303,16 +309,18 @@ def run_inference_2_2(
303309 num_train_timesteps = float (scheduler .config .num_train_timesteps )
304310
305311 # SenCache state
306- ref_noise_pred = None # y^r: cached denoiser output
307- ref_latent = None # x^r: latent at last cache refresh
308- ref_timestep = 0.0 # t^r: timestep (normalized to [0,1]) at last cache refresh
309- accum_dx = 0.0 # accumulated ||Δx|| since last refresh
310- accum_dt = 0.0 # accumulated |Δt| since last refresh
311- reuse_count = 0 # consecutive cache reuses
312- cache_count = 0
312+ ref_noise_pred = jnp .zeros (
313+ (bsz * 2 , latents .shape [1 ], latents .shape [2 ], latents .shape [3 ], latents .shape [4 ]), dtype = latents .dtype
314+ )
315+ ref_latent = jnp .zeros_like (latents )
316+ ref_timestep = jnp .array (0.0 , dtype = jnp .float32 )
317+ accum_dx = jnp .array (0.0 , dtype = jnp .float32 )
318+ accum_dt = jnp .array (0.0 , dtype = jnp .float32 )
319+ reuse_count = jnp .array (0 , dtype = jnp .int32 )
320+ cache_count = jnp .array (0 , dtype = jnp .int32 )
313321
314322 for step in range (num_inference_steps ):
315- t = jnp . array ( scheduler_state . timesteps , dtype = jnp . int32 ) [step ]
323+ t = timesteps [step ]
316324 t_float = float (timesteps_np [step ]) / num_train_timesteps # normalize to [0, 1]
317325
318326 # Select transformer and guidance scale
@@ -358,29 +366,30 @@ def run_inference_2_2(
358366 )
359367 ref_noise_pred = noise_pred
360368 ref_latent = latents
361- ref_timestep = t_float
362- accum_dx = 0.0
363- accum_dt = 0.0
364- reuse_count = 0
369+ ref_timestep = jnp . array ( t_float , dtype = jnp . float32 )
370+ accum_dx = jnp . array ( 0.0 , dtype = jnp . float32 )
371+ accum_dt = jnp . array ( 0.0 , dtype = jnp . float32 )
372+ reuse_count = jnp . array ( 0 , dtype = jnp . int32 )
365373 latents , scheduler_state = scheduler .step (scheduler_state , noise_pred , t , latents ).to_tuple ()
366374 continue
367375
368376 # Accumulate deltas since last full compute
369- dx_norm = float ( jnp .sqrt (jnp .mean ((latents - ref_latent ) ** 2 ) ))
370- dt = abs (t_float - ref_timestep )
377+ dx_norm = jnp .sqrt (jnp .mean ((latents - ref_latent ) ** 2 ))
378+ dt = jnp . abs (t_float - ref_timestep )
371379 accum_dx += dx_norm
372380 accum_dt += dt
373381
374382 # Sensitivity score (Eq. 9)
375383 score = alpha_x * accum_dx + alpha_t * accum_dt
376384
377- if score <= sen_epsilon and reuse_count < max_reuse :
378- # Cache hit: reuse previous output
379- noise_pred = ref_noise_pred
380- reuse_count += 1
381- cache_count += 1
382- else :
383- # Cache miss: full CFG forward pass
385+ cond = jnp .logical_and (score <= sen_epsilon , reuse_count < max_reuse )
386+
387+ def hit_fn (args ):
388+ ref_noise_pred , ref_latent , ref_timestep , accum_dx , accum_dt , reuse_count , cache_count = args
389+ return ref_noise_pred , ref_latent , ref_timestep , accum_dx , accum_dt , reuse_count + 1 , cache_count + 1
390+
391+ def miss_fn (args ):
392+ _ , _ , _ , _ , _ , _ , cache_count = args
384393 latents_doubled = jnp .concatenate ([latents ] * 2 )
385394 timestep = jnp .broadcast_to (t , bsz * 2 )
386395 noise_pred , _ , _ = transformer_forward_pass_full_cfg (
@@ -395,12 +404,19 @@ def run_inference_2_2(
395404 rotary_emb = rotary_emb ,
396405 encoder_attention_mask = encoder_attention_mask ,
397406 )
398- ref_noise_pred = noise_pred
399- ref_latent = latents
400- ref_timestep = t_float
401- accum_dx = 0.0
402- accum_dt = 0.0
403- reuse_count = 0
407+ return (
408+ noise_pred ,
409+ latents ,
410+ jnp .array (t_float , dtype = jnp .float32 ),
411+ jnp .array (0.0 , dtype = jnp .float32 ),
412+ jnp .array (0.0 , dtype = jnp .float32 ),
413+ jnp .array (0 , dtype = jnp .int32 ),
414+ cache_count ,
415+ )
416+
417+ noise_pred , ref_latent , ref_timestep , accum_dx , accum_dt , reuse_count , cache_count = jax .lax .cond (
418+ cond , hit_fn , miss_fn , (ref_noise_pred , ref_latent , ref_timestep , accum_dx , accum_dt , reuse_count , cache_count )
419+ )
404420
405421 latents , scheduler_state = scheduler .step (scheduler_state , noise_pred , t , latents ).to_tuple ()
406422
@@ -470,7 +486,7 @@ def run_inference_2_2(
470486 cached_noise_uncond = None
471487
472488 for step in range (num_inference_steps ):
473- t = jnp . array ( scheduler_state . timesteps , dtype = jnp . int32 ) [step ]
489+ t = timesteps [step ]
474490 is_cache_step = step_is_cache [step ]
475491
476492 # Select transformer and guidance scale based on precomputed schedule
@@ -607,8 +623,6 @@ def low_noise_branch(operands):
607623 )
608624
609625 if scan_diffusion_loop :
610- timesteps = jnp .array (scheduler_state .timesteps , dtype = jnp .int32 )
611-
612626 scheduler_state = scheduler_state .replace (last_sample = jnp .zeros_like (latents ), step_index = jnp .array (0 , dtype = jnp .int32 ))
613627
614628 def scan_body (carry , t ):
@@ -657,7 +671,7 @@ def scan_body(carry, t):
657671 profiler = max_utils .Profiler (config )
658672 profiler .start ()
659673
660- t = jnp . array ( scheduler_state . timesteps , dtype = jnp . int32 ) [step ]
674+ t = timesteps [step ]
661675
662676 if step_uses_high [step ]:
663677 graphdef , state , rest = (
0 commit comments