@@ -59,6 +59,48 @@ def cpu(self):
5959 return self
6060
6161
62+ class _StochasticScheduler :
63+ init_noise_sigma = 1.0
64+
65+ def set_timesteps (self , num_inference_steps , * , device ):
66+ self .timesteps = torch .arange (num_inference_steps - 1 , - 1 , - 1 , device = device )
67+
68+ def scale_model_input (self , sample , timestep ):
69+ return sample
70+
71+ def step (self , model_output , timestep , sample , * , generator = None , return_dict = True ):
72+ variance_noise = torch .randn (
73+ sample .shape ,
74+ generator = generator ,
75+ device = sample .device ,
76+ dtype = sample .dtype ,
77+ )
78+ return (sample + variance_noise ,)
79+
80+
81+ def _make_stochastic_layerdiff_pipeline (module ):
82+ class ZeroUnet (module .UNetFrameConditionModel ):
83+ device = torch .device ("cpu" )
84+ dtype = torch .float32
85+ config = types .SimpleNamespace (time_cond_proj_dim = None )
86+
87+ def __call__ (self , sample , * args , ** kwargs ):
88+ return (torch .zeros_like (sample .narrow (- 3 , 0 , 4 )),)
89+
90+ pipeline = module .KDiffusionStableDiffusionXLPipeline (
91+ vae = object (),
92+ text_encoder = object (),
93+ tokenizer = object (),
94+ text_encoder_2 = object (),
95+ tokenizer_2 = object (),
96+ unet = ZeroUnet (),
97+ scheduler = _StochasticScheduler (),
98+ trans_vae = None ,
99+ )
100+ pipeline .vae_scale_factor = 8
101+ return pipeline
102+
103+
62104def _package (name : str ) -> types .ModuleType :
63105 module = types .ModuleType (name )
64106 module .__path__ = []
@@ -84,11 +126,20 @@ def _load_layerdiff_vendor_module(monkeypatch):
84126 "StableDiffusionXLImg2ImgPipeline" ,
85127 "CLIPVisionModelWithProjection" ,
86128 "CLIPImageProcessor" ,
129+ "randn_tensor" ,
130+ "retrieve_timesteps" ,
87131 ]
88132 pipeline_module .torch = torch
89133 pipeline_module .StableDiffusionXLImg2ImgPipeline = _FakeDiffusionPipeline
90134 pipeline_module .CLIPVisionModelWithProjection = type ("CLIPVisionModelWithProjection" , (), {})
91135 pipeline_module .CLIPImageProcessor = type ("CLIPImageProcessor" , (), {})
136+ pipeline_module .randn_tensor = torch .randn
137+
138+ def retrieve_timesteps (scheduler , num_inference_steps , device , ** kwargs ):
139+ scheduler .set_timesteps (num_inference_steps , device = device )
140+ return scheduler .timesteps , num_inference_steps
141+
142+ pipeline_module .retrieve_timesteps = retrieve_timesteps
92143
93144 diffusers = _package ("diffusers" )
94145 for name in (
@@ -248,6 +299,54 @@ def fake_encode(prompts):
248299 assert empty_cache_calls == [True ]
249300
250301
302+ def test_layerdiff_seed_controls_scheduler_variance_noise (monkeypatch ):
303+ module = _load_layerdiff_vendor_module (monkeypatch )
304+
305+ def run_with_global_seed (global_seed ):
306+ torch .manual_seed (global_seed )
307+ pipeline = _make_stochastic_layerdiff_pipeline (module )
308+ return pipeline (
309+ num_inference_steps = 3 ,
310+ guidance_scale = 1.0 ,
311+ generator = torch .Generator (device = "cpu" ).manual_seed (42 ),
312+ prompt_embeds = torch .zeros ((1 , 1 , 1 )),
313+ pooled_prompt_embeds = torch .zeros ((1 , 1 )),
314+ c_concat = torch .zeros ((1 , 4 , 2 , 2 )),
315+ )
316+
317+ with torch .random .fork_rng ():
318+ first = run_with_global_seed (100 )
319+ second = run_with_global_seed (999 )
320+
321+ assert torch .equal (first , second )
322+
323+
324+ def test_layerdiff_denoise_func_seed_controls_scheduler_variance_noise (monkeypatch ):
325+ module = _load_layerdiff_vendor_module (monkeypatch )
326+
327+ def run_with_global_seed (global_seed ):
328+ torch .manual_seed (global_seed )
329+ generator = torch .Generator (device = "cpu" ).manual_seed (42 )
330+ pipeline = _make_stochastic_layerdiff_pipeline (module )
331+ pipeline ._guidance_scale = 1.0
332+ initial_latents = torch .randn ((1 , 1 , 4 , 2 , 2 ), generator = generator )
333+ return pipeline .denoise_func (
334+ initial_latents ,
335+ add_text_embeds = torch .zeros ((1 , 1 )),
336+ add_time_ids = torch .zeros ((1 , 1 )),
337+ prompt_embeds = torch .zeros ((1 , 1 , 1 )),
338+ c_concat = torch .zeros ((1 , 1 , 4 , 2 , 2 )),
339+ num_inference_steps = 3 ,
340+ generator = generator ,
341+ )
342+
343+ with torch .random .fork_rng ():
344+ first = run_with_global_seed (100 )
345+ second = run_with_global_seed (999 )
346+
347+ assert torch .equal (first , second )
348+
349+
251350def test_marigold_caches_empty_embedding_before_unloading_text_encoder (monkeypatch ):
252351 module = _load_marigold_vendor_module (monkeypatch )
253352 unet = module .UNetFrameConditionModel ()
0 commit comments