1313# See the License for the specific language governing permissions and
1414# limitations under the License.
1515
16- import unittest
17-
1816import torch
1917
2018from diffusers import AutoencoderKLHunyuanVideo
2119from diffusers .models .autoencoders .autoencoder_kl_hunyuan_video import prepare_causal_attention_mask
20+ from diffusers .utils .torch_utils import randn_tensor
2221
23- from ...testing_utils import enable_full_determinism , floats_tensor , torch_device
24- from ..test_modeling_common import ModelTesterMixin
25- from .testing_utils import AutoencoderTesterMixin
22+ from ...testing_utils import enable_full_determinism , torch_device
23+ from ..testing_utils import BaseModelTesterConfig , MemoryTesterMixin , ModelTesterMixin , TrainingTesterMixin
24+ from .testing_utils import NewAutoencoderTesterMixin
2625
2726
2827enable_full_determinism ()
2928
3029
31- class AutoencoderKLHunyuanVideoTests (ModelTesterMixin , AutoencoderTesterMixin , unittest .TestCase ):
32- model_class = AutoencoderKLHunyuanVideo
33- main_input_name = "sample"
34- base_precision = 1e-2
30+ class AutoencoderKLHunyuanVideoTesterConfig (BaseModelTesterConfig ):
31+ @property
32+ def model_class (self ):
33+ return AutoencoderKLHunyuanVideo
34+
35+ @property
36+ def main_input_name (self ) -> str :
37+ return "sample"
38+
39+ @property
40+ def output_shape (self ) -> tuple :
41+ return (3 , 9 , 16 , 16 )
42+
43+ @property
44+ def generator (self ):
45+ return torch .Generator ("cpu" ).manual_seed (0 )
3546
36- def get_autoencoder_kl_hunyuan_video_config (self ):
47+ def get_init_dict (self ) -> dict :
3748 return {
3849 "in_channels" : 3 ,
3950 "out_channels" : 3 ,
@@ -60,29 +71,43 @@ def get_autoencoder_kl_hunyuan_video_config(self):
6071 "mid_block_add_attention" : True ,
6172 }
6273
63- @property
64- def dummy_input (self ):
74+ def get_dummy_inputs (self ) -> dict :
6575 batch_size = 2
6676 num_frames = 9
6777 num_channels = 3
6878 sizes = (16 , 16 )
79+ image = randn_tensor (
80+ (batch_size , num_channels , num_frames , * sizes ), generator = self .generator , device = torch_device
81+ )
82+ return {"sample" : image }
6983
70- image = floats_tensor ((batch_size , num_channels , num_frames ) + sizes ).to (torch_device )
7184
72- return {"sample" : image }
85+ class TestAutoencoderKLHunyuanVideo (AutoencoderKLHunyuanVideoTesterConfig , ModelTesterMixin ):
86+ def test_prepare_causal_attention_mask (self ):
87+ def prepare_causal_attention_mask_orig (
88+ num_frames : int , height_width : int , dtype : torch .dtype , device : torch .device , batch_size : int = None
89+ ) -> torch .Tensor :
90+ seq_len = num_frames * height_width
91+ mask = torch .full ((seq_len , seq_len ), float ("-inf" ), dtype = dtype , device = device )
92+ for i in range (seq_len ):
93+ i_frame = i // height_width
94+ mask [i , : (i_frame + 1 ) * height_width ] = 0
95+ if batch_size is not None :
96+ mask = mask .unsqueeze (0 ).expand (batch_size , - 1 , - 1 )
97+ return mask
7398
74- @property
75- def input_shape (self ):
76- return (3 , 9 , 16 , 16 )
99+ # test with some odd shapes
100+ original_mask = prepare_causal_attention_mask_orig (
101+ num_frames = 31 , height_width = 111 , dtype = torch .float32 , device = torch_device
102+ )
103+ new_mask = prepare_causal_attention_mask (
104+ num_frames = 31 , height_width = 111 , dtype = torch .float32 , device = torch_device
105+ )
106+ assert torch .allclose (original_mask , new_mask ), "Causal attention mask should be the same"
77107
78- @property
79- def output_shape (self ):
80- return (3 , 9 , 16 , 16 )
81108
82- def prepare_init_args_and_inputs_for_common (self ):
83- init_dict = self .get_autoencoder_kl_hunyuan_video_config ()
84- inputs_dict = self .dummy_input
85- return init_dict , inputs_dict
109+ class TestAutoencoderKLHunyuanVideoTraining (AutoencoderKLHunyuanVideoTesterConfig , TrainingTesterMixin ):
110+ """Training tests for AutoencoderKLHunyuanVideo."""
86111
87112 def test_gradient_checkpointing_is_applied (self ):
88113 expected_set = {
@@ -94,9 +119,18 @@ def test_gradient_checkpointing_is_applied(self):
94119 }
95120 super ().test_gradient_checkpointing_is_applied (expected_set = expected_set )
96121
97- # We need to overwrite this test because the base test does not account length of down_block_types
122+
123+ class TestAutoencoderKLHunyuanVideoMemory (AutoencoderKLHunyuanVideoTesterConfig , MemoryTesterMixin ):
124+ """Memory optimization tests for AutoencoderKLHunyuanVideo."""
125+
126+
127+ class TestAutoencoderKLHunyuanVideoSlicingTiling (AutoencoderKLHunyuanVideoTesterConfig , NewAutoencoderTesterMixin ):
128+ """Slicing and tiling tests for AutoencoderKLHunyuanVideo."""
129+
130+ # Overwritten because the base test's block_out_channels doesn't account for the length of down_block_types.
98131 def test_forward_with_norm_groups (self ):
99- init_dict , inputs_dict = self .prepare_init_args_and_inputs_for_common ()
132+ init_dict = self .get_init_dict ()
133+ inputs_dict = self .get_dummy_inputs ()
100134
101135 init_dict ["norm_num_groups" ] = 16
102136 init_dict ["block_out_channels" ] = (16 , 16 , 16 , 16 )
@@ -111,35 +145,6 @@ def test_forward_with_norm_groups(self):
111145 if isinstance (output , dict ):
112146 output = output .to_tuple ()[0 ]
113147
114- self . assertIsNotNone ( output )
148+ assert output is not None
115149 expected_shape = inputs_dict ["sample" ].shape
116- self .assertEqual (output .shape , expected_shape , "Input and output shapes do not match" )
117-
118- @unittest .skip ("Unsupported test." )
119- def test_outputs_equivalence (self ):
120- pass
121-
122- def test_prepare_causal_attention_mask (self ):
123- def prepare_causal_attention_mask_orig (
124- num_frames : int , height_width : int , dtype : torch .dtype , device : torch .device , batch_size : int = None
125- ) -> torch .Tensor :
126- seq_len = num_frames * height_width
127- mask = torch .full ((seq_len , seq_len ), float ("-inf" ), dtype = dtype , device = device )
128- for i in range (seq_len ):
129- i_frame = i // height_width
130- mask [i , : (i_frame + 1 ) * height_width ] = 0
131- if batch_size is not None :
132- mask = mask .unsqueeze (0 ).expand (batch_size , - 1 , - 1 )
133- return mask
134-
135- # test with some odd shapes
136- original_mask = prepare_causal_attention_mask_orig (
137- num_frames = 31 , height_width = 111 , dtype = torch .float32 , device = torch_device
138- )
139- new_mask = prepare_causal_attention_mask (
140- num_frames = 31 , height_width = 111 , dtype = torch .float32 , device = torch_device
141- )
142- self .assertTrue (
143- torch .allclose (original_mask , new_mask ),
144- "Causal attention mask should be the same" ,
145- )
150+ assert output .shape == expected_shape , "Input and output shapes do not match"
0 commit comments