Orangerl commited on
Commit
d009e89
·
verified ·
1 Parent(s): 77fc290

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. third_party/diffusers/LICENSE +201 -0
  2. third_party/diffusers/README.md +231 -0
  3. third_party/diffusers/pyproject.toml +32 -0
  4. third_party/diffusers/setup.py +308 -0
  5. third_party/diffusers/src/diffusers/__init__.py +1623 -0
  6. third_party/diffusers/src/diffusers/callbacks.py +244 -0
  7. third_party/diffusers/src/diffusers/configuration_utils.py +805 -0
  8. third_party/diffusers/src/diffusers/dependency_versions_check.py +34 -0
  9. third_party/diffusers/src/diffusers/dependency_versions_table.py +56 -0
  10. third_party/diffusers/src/diffusers/hooks/__init__.py +30 -0
  11. third_party/diffusers/src/diffusers/hooks/_common.py +61 -0
  12. third_party/diffusers/src/diffusers/hooks/_helpers.py +381 -0
  13. third_party/diffusers/src/diffusers/hooks/context_parallel.py +382 -0
  14. third_party/diffusers/src/diffusers/hooks/faster_cache.py +654 -0
  15. third_party/diffusers/src/diffusers/hooks/first_block_cache.py +258 -0
  16. third_party/diffusers/src/diffusers/hooks/group_offloading.py +966 -0
  17. third_party/diffusers/src/diffusers/hooks/hooks.py +310 -0
  18. third_party/diffusers/src/diffusers/hooks/layer_skip.py +263 -0
  19. third_party/diffusers/src/diffusers/hooks/layerwise_casting.py +240 -0
  20. third_party/diffusers/src/diffusers/hooks/mag_cache.py +468 -0
  21. third_party/diffusers/src/diffusers/hooks/pyramid_attention_broadcast.py +314 -0
  22. third_party/diffusers/src/diffusers/hooks/smoothed_energy_guidance_utils.py +166 -0
  23. third_party/diffusers/src/diffusers/hooks/taylorseer_cache.py +345 -0
  24. third_party/diffusers/src/diffusers/hooks/text_kv_cache.py +173 -0
  25. third_party/diffusers/src/diffusers/hooks/utils.py +43 -0
  26. third_party/diffusers/src/diffusers/image_processor.py +1468 -0
  27. third_party/diffusers/src/diffusers/optimization.py +360 -0
  28. third_party/diffusers/src/diffusers/pipelines/README.md +171 -0
  29. third_party/diffusers/src/diffusers/pipelines/__init__.py +963 -0
  30. third_party/diffusers/src/diffusers/pipelines/auto_pipeline.py +1237 -0
  31. third_party/diffusers/src/diffusers/pipelines/chroma/__init__.py +51 -0
  32. third_party/diffusers/src/diffusers/pipelines/chroma/pipeline_chroma.py +976 -0
  33. third_party/diffusers/src/diffusers/pipelines/chroma/pipeline_chroma_img2img.py +1060 -0
  34. third_party/diffusers/src/diffusers/pipelines/chroma/pipeline_chroma_inpainting.py +1184 -0
  35. third_party/diffusers/src/diffusers/pipelines/chroma/pipeline_output.py +20 -0
  36. third_party/diffusers/src/diffusers/pipelines/cosmos/__init__.py +64 -0
  37. third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_cosmos2_5_predict.py +888 -0
  38. third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_cosmos2_text2image.py +679 -0
  39. third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_cosmos_text2world.py +670 -0
  40. third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_cosmos_video2world.py +832 -0
  41. third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_output.py +39 -0
  42. third_party/diffusers/src/diffusers/pipelines/free_init_utils.py +186 -0
  43. third_party/diffusers/src/diffusers/pipelines/free_noise_utils.py +597 -0
  44. third_party/diffusers/src/diffusers/pipelines/glm_image/__init__.py +59 -0
  45. third_party/diffusers/src/diffusers/pipelines/glm_image/pipeline_glm_image.py +1049 -0
  46. third_party/diffusers/src/diffusers/pipelines/glm_image/pipeline_output.py +20 -0
  47. third_party/diffusers/src/diffusers/pipelines/hidream_image/__init__.py +47 -0
  48. third_party/diffusers/src/diffusers/pipelines/hidream_image/pipeline_hidream_image.py +1053 -0
  49. third_party/diffusers/src/diffusers/pipelines/hidream_image/pipeline_output.py +34 -0
  50. third_party/diffusers/src/diffusers/pipelines/latte/__init__.py +48 -0
third_party/diffusers/LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, Any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
third_party/diffusers/README.md ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!---
2
+ Copyright 2022 - The HuggingFace Team. All rights reserved.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ -->
16
+
17
+ <p align="center">
18
+ <br>
19
+ <img src="https://raw.githubusercontent.com/huggingface/diffusers/main/docs/source/en/imgs/diffusers_library.jpg" width="400"/>
20
+ <br>
21
+ <p>
22
+ <p align="center">
23
+ <a href="https://github.com/huggingface/diffusers/blob/main/LICENSE"><img alt="GitHub" src="https://img.shields.io/github/license/huggingface/datasets.svg?color=blue"></a>
24
+ <a href="https://github.com/huggingface/diffusers/releases"><img alt="GitHub release" src="https://img.shields.io/github/release/huggingface/diffusers.svg"></a>
25
+ <a href="https://pepy.tech/project/diffusers"><img alt="GitHub release" src="https://static.pepy.tech/badge/diffusers/month"></a>
26
+ <a href="CODE_OF_CONDUCT.md"><img alt="Contributor Covenant" src="https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg"></a>
27
+ <a href="https://twitter.com/diffuserslib"><img alt="X account" src="https://img.shields.io/twitter/url/https/twitter.com/diffuserslib.svg?style=social&label=Follow%20%40diffuserslib"></a>
28
+ </p>
29
+
30
+ 🤗 Diffusers is the go-to library for state-of-the-art pretrained diffusion models for generating images, audio, and even 3D structures of molecules. Whether you're looking for a simple inference solution or training your own diffusion models, 🤗 Diffusers is a modular toolbox that supports both. Our library is designed with a focus on [usability over performance](https://huggingface.co/docs/diffusers/conceptual/philosophy#usability-over-performance), [simple over easy](https://huggingface.co/docs/diffusers/conceptual/philosophy#simple-over-easy), and [customizability over abstractions](https://huggingface.co/docs/diffusers/conceptual/philosophy#tweakable-contributorfriendly-over-abstraction).
31
+
32
+ 🤗 Diffusers offers three core components:
33
+
34
+ - State-of-the-art [diffusion pipelines](https://huggingface.co/docs/diffusers/api/pipelines/overview) that can be run in inference with just a few lines of code.
35
+ - Interchangeable noise [schedulers](https://huggingface.co/docs/diffusers/api/schedulers/overview) for different diffusion speeds and output quality.
36
+ - Pretrained [models](https://huggingface.co/docs/diffusers/api/models/overview) that can be used as building blocks, and combined with schedulers, for creating your own end-to-end diffusion systems.
37
+
38
+ ## Installation
39
+
40
+ We recommend installing 🤗 Diffusers in a virtual environment from PyPI or Conda. For more details about installing [PyTorch](https://pytorch.org/get-started/locally/), please refer to their official documentation.
41
+
42
+ ### PyTorch
43
+
44
+ With `pip` (official package):
45
+
46
+ ```bash
47
+ pip install --upgrade diffusers[torch]
48
+ ```
49
+
50
+ With `conda` (maintained by the community):
51
+
52
+ ```sh
53
+ conda install -c conda-forge diffusers
54
+ ```
55
+
56
+ ### Apple Silicon (M1/M2) support
57
+
58
+ Please refer to the [How to use Stable Diffusion in Apple Silicon](https://huggingface.co/docs/diffusers/optimization/mps) guide.
59
+
60
+ ## Quickstart
61
+
62
+ Generating outputs is super easy with 🤗 Diffusers. To generate an image from text, use the `from_pretrained` method to load any pretrained diffusion model (browse the [Hub](https://huggingface.co/models?library=diffusers&sort=downloads) for 30,000+ checkpoints):
63
+
64
+ ```python
65
+ from diffusers import DiffusionPipeline
66
+ import torch
67
+
68
+ pipeline = DiffusionPipeline.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5", torch_dtype=torch.float16)
69
+ pipeline.to("cuda")
70
+ pipeline("An image of a squirrel in Picasso style").images[0]
71
+ ```
72
+
73
+ You can also dig into the models and schedulers toolbox to build your own diffusion system:
74
+
75
+ ```python
76
+ from diffusers import DDPMScheduler, UNet2DModel
77
+ from PIL import Image
78
+ import torch
79
+
80
+ scheduler = DDPMScheduler.from_pretrained("google/ddpm-cat-256")
81
+ model = UNet2DModel.from_pretrained("google/ddpm-cat-256").to("cuda")
82
+ scheduler.set_timesteps(50)
83
+
84
+ sample_size = model.config.sample_size
85
+ noise = torch.randn((1, 3, sample_size, sample_size), device="cuda")
86
+ input = noise
87
+
88
+ for t in scheduler.timesteps:
89
+ with torch.no_grad():
90
+ noisy_residual = model(input, t).sample
91
+ prev_noisy_sample = scheduler.step(noisy_residual, t, input).prev_sample
92
+ input = prev_noisy_sample
93
+
94
+ image = (input / 2 + 0.5).clamp(0, 1)
95
+ image = image.cpu().permute(0, 2, 3, 1).numpy()[0]
96
+ image = Image.fromarray((image * 255).round().astype("uint8"))
97
+ image
98
+ ```
99
+
100
+ Check out the [Quickstart](https://huggingface.co/docs/diffusers/quicktour) to launch your diffusion journey today!
101
+
102
+ ## How to navigate the documentation
103
+
104
+ | **Documentation** | **What can I learn?** |
105
+ |---------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
106
+ | [Tutorial](https://huggingface.co/docs/diffusers/tutorials/tutorial_overview) | A basic crash course for learning how to use the library's most important features like using models and schedulers to build your own diffusion system, and training your own diffusion model. |
107
+ | [Loading](https://huggingface.co/docs/diffusers/using-diffusers/loading) | Guides for how to load and configure all the components (pipelines, models, and schedulers) of the library, as well as how to use different schedulers. |
108
+ | [Pipelines for inference](https://huggingface.co/docs/diffusers/using-diffusers/overview_techniques) | Guides for how to use pipelines for different inference tasks, batched generation, controlling generated outputs and randomness, and how to contribute a pipeline to the library. |
109
+ | [Optimization](https://huggingface.co/docs/diffusers/optimization/fp16) | Guides for how to optimize your diffusion model to run faster and consume less memory. |
110
+ | [Training](https://huggingface.co/docs/diffusers/training/overview) | Guides for how to train a diffusion model for different tasks with different training techniques. |
111
+ ## Contribution
112
+
113
+ We ❤️ contributions from the open-source community!
114
+ If you want to contribute to this library, please check out our [Contribution guide](https://github.com/huggingface/diffusers/blob/main/CONTRIBUTING.md).
115
+ You can look out for [issues](https://github.com/huggingface/diffusers/issues) you'd like to tackle to contribute to the library.
116
+ - See [Good first issues](https://github.com/huggingface/diffusers/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) for general opportunities to contribute
117
+ - See [New model/pipeline](https://github.com/huggingface/diffusers/issues?q=is%3Aopen+is%3Aissue+label%3A%22New+pipeline%2Fmodel%22) to contribute exciting new diffusion models / diffusion pipelines
118
+ - See [New scheduler](https://github.com/huggingface/diffusers/issues?q=is%3Aopen+is%3Aissue+label%3A%22New+scheduler%22)
119
+
120
+ Also, say 👋 in our public Discord channel <a href="https://discord.gg/G7tWnz98XR"><img alt="Join us on Discord" src="https://img.shields.io/discord/823813159592001537?color=5865F2&logo=discord&logoColor=white"></a>. We discuss the hottest trends about diffusion models, help each other with contributions, personal projects or just hang out ☕.
121
+
122
+
123
+ ## Popular Tasks & Pipelines
124
+
125
+ <table>
126
+ <tr>
127
+ <th>Task</th>
128
+ <th>Pipeline</th>
129
+ <th>🤗 Hub</th>
130
+ </tr>
131
+ <tr style="border-top: 2px solid black">
132
+ <td>Unconditional Image Generation</td>
133
+ <td><a href="https://huggingface.co/docs/diffusers/api/pipelines/ddpm"> DDPM </a></td>
134
+ <td><a href="https://huggingface.co/google/ddpm-ema-church-256"> google/ddpm-ema-church-256 </a></td>
135
+ </tr>
136
+ <tr style="border-top: 2px solid black">
137
+ <td>Text-to-Image</td>
138
+ <td><a href="https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/text2img">Stable Diffusion Text-to-Image</a></td>
139
+ <td><a href="https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5"> stable-diffusion-v1-5/stable-diffusion-v1-5 </a></td>
140
+ </tr>
141
+ <tr>
142
+ <td>Text-to-Image</td>
143
+ <td><a href="https://huggingface.co/docs/diffusers/api/pipelines/unclip">unCLIP</a></td>
144
+ <td><a href="https://huggingface.co/kakaobrain/karlo-v1-alpha"> kakaobrain/karlo-v1-alpha </a></td>
145
+ </tr>
146
+ <tr>
147
+ <td>Text-to-Image</td>
148
+ <td><a href="https://huggingface.co/docs/diffusers/api/pipelines/deepfloyd_if">DeepFloyd IF</a></td>
149
+ <td><a href="https://huggingface.co/DeepFloyd/IF-I-XL-v1.0"> DeepFloyd/IF-I-XL-v1.0 </a></td>
150
+ </tr>
151
+ <tr>
152
+ <td>Text-to-Image</td>
153
+ <td><a href="https://huggingface.co/docs/diffusers/api/pipelines/kandinsky">Kandinsky</a></td>
154
+ <td><a href="https://huggingface.co/kandinsky-community/kandinsky-2-2-decoder"> kandinsky-community/kandinsky-2-2-decoder </a></td>
155
+ </tr>
156
+ <tr style="border-top: 2px solid black">
157
+ <td>Text-guided Image-to-Image</td>
158
+ <td><a href="https://huggingface.co/docs/diffusers/api/pipelines/controlnet">ControlNet</a></td>
159
+ <td><a href="https://huggingface.co/lllyasviel/sd-controlnet-canny"> lllyasviel/sd-controlnet-canny </a></td>
160
+ </tr>
161
+ <tr>
162
+ <td>Text-guided Image-to-Image</td>
163
+ <td><a href="https://huggingface.co/docs/diffusers/api/pipelines/pix2pix">InstructPix2Pix</a></td>
164
+ <td><a href="https://huggingface.co/timbrooks/instruct-pix2pix"> timbrooks/instruct-pix2pix </a></td>
165
+ </tr>
166
+ <tr>
167
+ <td>Text-guided Image-to-Image</td>
168
+ <td><a href="https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/img2img">Stable Diffusion Image-to-Image</a></td>
169
+ <td><a href="https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5"> stable-diffusion-v1-5/stable-diffusion-v1-5 </a></td>
170
+ </tr>
171
+ <tr style="border-top: 2px solid black">
172
+ <td>Text-guided Image Inpainting</td>
173
+ <td><a href="https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/inpaint">Stable Diffusion Inpainting</a></td>
174
+ <td><a href="https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-inpainting"> stable-diffusion-v1-5/stable-diffusion-inpainting </a></td>
175
+ </tr>
176
+ <tr style="border-top: 2px solid black">
177
+ <td>Image Variation</td>
178
+ <td><a href="https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/image_variation">Stable Diffusion Image Variation</a></td>
179
+ <td><a href="https://huggingface.co/lambdalabs/sd-image-variations-diffusers"> lambdalabs/sd-image-variations-diffusers </a></td>
180
+ </tr>
181
+ <tr style="border-top: 2px solid black">
182
+ <td>Super Resolution</td>
183
+ <td><a href="https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/upscale">Stable Diffusion Upscale</a></td>
184
+ <td><a href="https://huggingface.co/stabilityai/stable-diffusion-x4-upscaler"> stabilityai/stable-diffusion-x4-upscaler </a></td>
185
+ </tr>
186
+ <tr>
187
+ <td>Super Resolution</td>
188
+ <td><a href="https://huggingface.co/docs/diffusers/api/pipelines/stable_diffusion/latent_upscale">Stable Diffusion Latent Upscale</a></td>
189
+ <td><a href="https://huggingface.co/stabilityai/sd-x2-latent-upscaler"> stabilityai/sd-x2-latent-upscaler </a></td>
190
+ </tr>
191
+ </table>
192
+
193
+ ## Popular libraries using 🧨 Diffusers
194
+
195
+ - https://github.com/microsoft/TaskMatrix
196
+ - https://github.com/invoke-ai/InvokeAI
197
+ - https://github.com/InstantID/InstantID
198
+ - https://github.com/apple/ml-stable-diffusion
199
+ - https://github.com/Sanster/lama-cleaner
200
+ - https://github.com/IDEA-Research/Grounded-Segment-Anything
201
+ - https://github.com/ashawkey/stable-dreamfusion
202
+ - https://github.com/deep-floyd/IF
203
+ - https://github.com/bentoml/BentoML
204
+ - https://github.com/bmaltais/kohya_ss
205
+ - +14,000 other amazing GitHub repositories 💪
206
+
207
+ Thank you for using us ❤️.
208
+
209
+ ## Credits
210
+
211
+ This library concretizes previous work by many different authors and would not have been possible without their great research and implementations. We'd like to thank, in particular, the following implementations which have helped us in our development and without which the API could not have been as polished today:
212
+
213
+ - @CompVis' latent diffusion models library, available [here](https://github.com/CompVis/latent-diffusion)
214
+ - @hojonathanho original DDPM implementation, available [here](https://github.com/hojonathanho/diffusion) as well as the extremely useful translation into PyTorch by @pesser, available [here](https://github.com/pesser/pytorch_diffusion)
215
+ - @ermongroup's DDIM implementation, available [here](https://github.com/ermongroup/ddim)
216
+ - @yang-song's Score-VE and Score-VP implementations, available [here](https://github.com/yang-song/score_sde_pytorch)
217
+
218
+ We also want to thank @heejkoo for the very helpful overview of papers, code and resources on diffusion models, available [here](https://github.com/heejkoo/Awesome-Diffusion-Models) as well as @crowsonkb and @rromb for useful discussions and insights.
219
+
220
+ ## Citation
221
+
222
+ ```bibtex
223
+ @misc{von-platen-etal-2022-diffusers,
224
+ author = {Patrick von Platen and Suraj Patil and Anton Lozhkov and Pedro Cuenca and Nathan Lambert and Kashif Rasul and Mishig Davaadorj and Dhruv Nair and Sayak Paul and William Berman and Yiyi Xu and Steven Liu and Thomas Wolf},
225
+ title = {Diffusers: State-of-the-art diffusion models},
226
+ year = {2022},
227
+ publisher = {GitHub},
228
+ journal = {GitHub repository},
229
+ howpublished = {\url{https://github.com/huggingface/diffusers}}
230
+ }
231
+ ```
third_party/diffusers/pyproject.toml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [tool.ruff]
2
+ line-length = 119
3
+ extend-exclude = [
4
+ "src/diffusers/pipelines/flux2/system_messages.py",
5
+ ]
6
+
7
+ [tool.ruff.lint]
8
+ # Never enforce `E501` (line length violations).
9
+ ignore = ["C901", "E501", "E721", "E741", "F402", "F823"]
10
+ select = ["C", "E", "F", "I", "W"]
11
+
12
+ # Ignore import violations in all `__init__.py` files.
13
+ [tool.ruff.lint.per-file-ignores]
14
+ "__init__.py" = ["E402", "F401", "F403", "F811"]
15
+ "src/diffusers/utils/dummy_*.py" = ["F401"]
16
+
17
+ [tool.ruff.lint.isort]
18
+ lines-after-imports = 2
19
+ known-first-party = ["diffusers"]
20
+
21
+ [tool.ruff.format]
22
+ # Like Black, use double quotes for strings.
23
+ quote-style = "double"
24
+
25
+ # Like Black, indent with spaces, rather than tabs.
26
+ indent-style = "space"
27
+
28
+ # Like Black, respect magic trailing commas.
29
+ skip-magic-trailing-comma = false
30
+
31
+ # Like Black, automatically detect the appropriate line ending.
32
+ line-ending = "auto"
third_party/diffusers/setup.py ADDED
@@ -0,0 +1,308 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ """
16
+ Simple check list from AllenNLP repo: https://github.com/allenai/allennlp/blob/main/setup.py
17
+
18
+ To create the package for PyPI.
19
+
20
+ 1. Run `make pre-release` (or `make pre-patch` for a patch release) then run `make fix-copies` to fix the index of the
21
+ documentation.
22
+
23
+ If releasing on a special branch, copy the updated README.md on the main branch for the commit you will make
24
+ for the post-release and run `make fix-copies` on the main branch as well.
25
+
26
+ 2. Unpin specific versions from setup.py that use a git install.
27
+
28
+ 3. Checkout the release branch (v<RELEASE>-release, for example v4.19-release), and commit these changes with the
29
+ message: "Release: <RELEASE>" and push.
30
+
31
+ 4. Manually trigger the "Nightly and release tests on main/release branch" workflow from the release branch. Wait for
32
+ the tests to complete. We can safely ignore the known test failures.
33
+
34
+ 5. Wait for the tests on main to be completed and be green (otherwise revert and fix bugs).
35
+
36
+ 6. Add a tag in git to mark the release: "git tag v<RELEASE> -m 'Adds tag v<RELEASE> for PyPI'"
37
+ Push the tag to git: git push --tags origin v<RELEASE>-release
38
+
39
+ 7. Build both the sources and the wheel. Do not change anything in setup.py between
40
+ creating the wheel and the source distribution (obviously).
41
+
42
+ For the wheel, run: "python setup.py bdist_wheel" in the top level directory
43
+ (This will build a wheel for the Python version you use to build it).
44
+
45
+ For the sources, run: "python setup.py sdist"
46
+ You should now have a /dist directory with both .whl and .tar.gz source versions.
47
+
48
+ Long story cut short, you need to run both before you can upload the distribution to the
49
+ test PyPI and the actual PyPI servers:
50
+
51
+ python setup.py bdist_wheel && python setup.py sdist
52
+
53
+ 8. Check that everything looks correct by uploading the package to the PyPI test server:
54
+
55
+ twine upload dist/* -r pypitest
56
+ (pypi suggests using twine as other methods upload files via plaintext.)
57
+ You may have to specify the repository url, use the following command then:
58
+ twine upload dist/* -r pypitest --repository-url=https://test.pypi.org/legacy/
59
+
60
+ Check that you can install it in a virtualenv by running:
61
+ pip install -i https://testpypi.python.org/pypi diffusers
62
+
63
+ If you are testing from a Colab Notebook, for instance, then do:
64
+ pip install diffusers && pip uninstall diffusers
65
+ pip install -i https://testpypi.python.org/pypi diffusers
66
+
67
+ Check you can run the following commands:
68
+ python -c "from diffusers import __version__; print(__version__)"
69
+ python -c "from diffusers import DiffusionPipeline; pipe = DiffusionPipeline.from_pretrained('fusing/unet-ldm-dummy-update'); pipe()"
70
+ python -c "from diffusers import DiffusionPipeline; pipe = DiffusionPipeline.from_pretrained('hf-internal-testing/tiny-stable-diffusion-pipe', safety_checker=None); pipe('ah suh du')"
71
+ python -c "from diffusers import *"
72
+
73
+ 9. Upload the final version to the actual PyPI:
74
+ twine upload dist/* -r pypi
75
+
76
+ 10. Prepare the release notes and publish them on GitHub once everything is looking hunky-dory. You can use the following
77
+ Space to fetch all the commits applicable for the release: https://huggingface.co/spacmes/sayakpaul/auto-release-notes-diffusers.
78
+ It automatically fetches the correct tag and branch but also provides the option to configure them.
79
+ `tag` should be the previous release tag (v0.26.1, for example), and `branch` should be
80
+ the latest release branch (v0.27.0-release, for example). It denotes all commits that have happened on branch
81
+ v0.27.0-release after the tag v0.26.1 was created.
82
+
83
+ 11. Run `make post-release` (or, for a patch release, `make post-patch`). If you were on a branch for the release,
84
+ you need to go back to main before executing this.
85
+ """
86
+
87
+ import os
88
+ import re
89
+ import sys
90
+
91
+ from setuptools import Command, find_packages, setup
92
+
93
+
94
+ # IMPORTANT:
95
+ # 1. all dependencies should be listed here with their version requirements if any
96
+ # 2. once modified, run: `make deps_table_update` to update src/diffusers/dependency_versions_table.py
97
+ _deps = [
98
+ "Pillow", # keep the PIL.Image.Resampling deprecation away
99
+ "accelerate>=0.31.0",
100
+ "compel==0.1.8",
101
+ "datasets",
102
+ "filelock",
103
+ "flax>=0.4.1",
104
+ "ftfy",
105
+ "hf-doc-builder>=0.3.0",
106
+ "httpx<1.0.0",
107
+ "huggingface-hub>=0.34.0,<2.0",
108
+ "requests-mock==1.10.0",
109
+ "importlib_metadata",
110
+ "invisible-watermark>=0.2.0",
111
+ "isort>=5.5.4",
112
+ "jax>=0.4.1",
113
+ "jaxlib>=0.4.1",
114
+ "Jinja2",
115
+ "torchsde",
116
+ "note_seq",
117
+ "librosa",
118
+ "numpy",
119
+ "parameterized",
120
+ "peft>=0.17.0",
121
+ "protobuf>=3.20.3,<4",
122
+ "pytest",
123
+ "pytest-timeout",
124
+ "pytest-xdist",
125
+ "python>=3.10.0",
126
+ "ruff==0.9.10",
127
+ "safetensors>=0.3.1",
128
+ "sentencepiece>=0.1.91,!=0.1.92",
129
+ "GitPython<3.1.19",
130
+ "scipy",
131
+ "onnx",
132
+ "optimum_quanto>=0.2.6",
133
+ "gguf>=0.10.0",
134
+ "torchao>=0.7.0",
135
+ "bitsandbytes>=0.43.3",
136
+ "nvidia_modelopt[hf]>=0.33.1",
137
+ "regex!=2019.12.17",
138
+ "requests",
139
+ "tensorboard",
140
+ "tiktoken>=0.7.0",
141
+ "torch>=1.4",
142
+ "torchvision",
143
+ "transformers>=4.41.2",
144
+ "urllib3<=2.0.0",
145
+ "black",
146
+ "phonemizer",
147
+ "opencv-python",
148
+ "timm",
149
+ ]
150
+
151
+ # this is a lookup table with items like:
152
+ #
153
+ # tokenizers: "huggingface-hub==0.8.0"
154
+ # packaging: "packaging"
155
+ #
156
+ # some of the values are versioned whereas others aren't.
157
+ deps = {b: a for a, b in (re.findall(r"^(([^!=<>~]+)(?:[!=<>~].*)?$)", x)[0] for x in _deps)}
158
+
159
+ # since we save this data in src/diffusers/dependency_versions_table.py it can be easily accessed from
160
+ # anywhere. If you need to quickly access the data from this table in a shell, you can do so easily with:
161
+ #
162
+ # python -c 'import sys; from diffusers.dependency_versions_table import deps; \
163
+ # print(" ".join([deps[x] for x in sys.argv[1:]]))' tokenizers datasets
164
+ #
165
+ # Just pass the desired package names to that script as it's shown with 2 packages above.
166
+ #
167
+ # If diffusers is not yet installed and the work is done from the cloned repo remember to add `PYTHONPATH=src` to the script above
168
+ #
169
+ # You can then feed this for example to `pip`:
170
+ #
171
+ # pip install -U $(python -c 'import sys; from diffusers.dependency_versions_table import deps; \
172
+ # print(" ".join([deps[x] for x in sys.argv[1:]]))' tokenizers datasets)
173
+ #
174
+
175
+
176
+ def deps_list(*pkgs):
177
+ return [deps[pkg] for pkg in pkgs]
178
+
179
+
180
+ class DepsTableUpdateCommand(Command):
181
+ """
182
+ A custom command that updates the dependency table.
183
+ usage: python setup.py deps_table_update
184
+ """
185
+
186
+ description = "build runtime dependency table"
187
+ user_options = [
188
+ # format: (long option, short option, description).
189
+ (
190
+ "dep-table-update",
191
+ None,
192
+ "updates src/diffusers/dependency_versions_table.py",
193
+ ),
194
+ ]
195
+
196
+ def initialize_options(self):
197
+ pass
198
+
199
+ def finalize_options(self):
200
+ pass
201
+
202
+ def run(self):
203
+ entries = "\n".join([f' "{k}": "{v}",' for k, v in deps.items()])
204
+ content = [
205
+ "# THIS FILE HAS BEEN AUTOGENERATED. To update:",
206
+ "# 1. modify the `_deps` dict in setup.py",
207
+ "# 2. run `make deps_table_update`",
208
+ "deps = {",
209
+ entries,
210
+ "}",
211
+ "",
212
+ ]
213
+ target = "src/diffusers/dependency_versions_table.py"
214
+ print(f"updating {target}")
215
+ with open(target, "w", encoding="utf-8", newline="\n") as f:
216
+ f.write("\n".join(content))
217
+
218
+
219
+ extras = {}
220
+ extras["quality"] = deps_list("urllib3", "isort", "ruff", "hf-doc-builder")
221
+ extras["docs"] = deps_list("hf-doc-builder")
222
+ extras["training"] = deps_list("accelerate", "datasets", "protobuf", "tensorboard", "Jinja2", "peft", "timm")
223
+ extras["test"] = deps_list(
224
+ "compel",
225
+ "ftfy",
226
+ "GitPython",
227
+ "datasets",
228
+ "Jinja2",
229
+ "invisible-watermark",
230
+ "librosa",
231
+ "parameterized",
232
+ "protobuf",
233
+ "pytest",
234
+ "pytest-timeout",
235
+ "pytest-xdist",
236
+ "requests-mock",
237
+ "safetensors",
238
+ "sentencepiece",
239
+ "scipy",
240
+ "tiktoken",
241
+ "torchsde",
242
+ "torchvision",
243
+ "transformers",
244
+ "phonemizer",
245
+ )
246
+ extras["torch"] = deps_list("torch", "accelerate")
247
+
248
+ extras["bitsandbytes"] = deps_list("bitsandbytes", "accelerate")
249
+ extras["gguf"] = deps_list("gguf", "accelerate")
250
+ extras["optimum_quanto"] = deps_list("optimum_quanto", "accelerate")
251
+ extras["torchao"] = deps_list("torchao", "accelerate")
252
+ extras["nvidia_modelopt"] = deps_list("nvidia_modelopt[hf]")
253
+
254
+ if os.name == "nt": # windows
255
+ extras["flax"] = [] # jax is not supported on windows
256
+ else:
257
+ extras["flax"] = deps_list("jax", "jaxlib", "flax")
258
+
259
+ extras["dev"] = (
260
+ extras["quality"] + extras["test"] + extras["training"] + extras["docs"] + extras["torch"] + extras["flax"]
261
+ )
262
+
263
+ install_requires = [
264
+ deps["importlib_metadata"],
265
+ deps["filelock"],
266
+ deps["httpx"],
267
+ deps["huggingface-hub"],
268
+ deps["numpy"],
269
+ deps["regex"],
270
+ deps["requests"],
271
+ deps["safetensors"],
272
+ deps["Pillow"],
273
+ ]
274
+
275
+ version_range_max = max(sys.version_info[1], 10) + 1
276
+
277
+ setup(
278
+ name="diffusers",
279
+ version="0.38.0.dev0", # expected format is one of x.y.z.dev0, or x.y.z.rc1 or x.y.z (no to dashes, yes to dots)
280
+ description="State-of-the-art diffusion in PyTorch and JAX.",
281
+ long_description=open("README.md", "r", encoding="utf-8").read(),
282
+ long_description_content_type="text/markdown",
283
+ keywords="deep learning diffusion jax pytorch stable diffusion audioldm",
284
+ license="Apache 2.0 License",
285
+ author="The Hugging Face team (past and future) with the help of all our contributors (https://github.com/huggingface/diffusers/graphs/contributors)",
286
+ author_email="diffusers@huggingface.co",
287
+ url="https://github.com/huggingface/diffusers",
288
+ package_dir={"": "src"},
289
+ packages=find_packages("src"),
290
+ package_data={"diffusers": ["py.typed"]},
291
+ include_package_data=True,
292
+ python_requires=">=3.10.0",
293
+ install_requires=list(install_requires),
294
+ extras_require=extras,
295
+ entry_points={"console_scripts": ["diffusers-cli=diffusers.commands.diffusers_cli:main"]},
296
+ classifiers=[
297
+ "Development Status :: 5 - Production/Stable",
298
+ "Intended Audience :: Developers",
299
+ "Intended Audience :: Education",
300
+ "Intended Audience :: Science/Research",
301
+ "License :: OSI Approved :: Apache Software License",
302
+ "Operating System :: OS Independent",
303
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
304
+ "Programming Language :: Python :: 3",
305
+ ]
306
+ + [f"Programming Language :: Python :: 3.{i}" for i in range(8, version_range_max)],
307
+ cmdclass={"deps_table_update": DepsTableUpdateCommand},
308
+ )
third_party/diffusers/src/diffusers/__init__.py ADDED
@@ -0,0 +1,1623 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __version__ = "0.38.0.dev0"
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from .utils import (
6
+ DIFFUSERS_SLOW_IMPORT,
7
+ OptionalDependencyNotAvailable,
8
+ _LazyModule,
9
+ is_accelerate_available,
10
+ is_bitsandbytes_available,
11
+ is_flax_available,
12
+ is_gguf_available,
13
+ is_librosa_available,
14
+ is_note_seq_available,
15
+ is_nvidia_modelopt_available,
16
+ is_onnx_available,
17
+ is_opencv_available,
18
+ is_optimum_quanto_available,
19
+ is_scipy_available,
20
+ is_sentencepiece_available,
21
+ is_torch_available,
22
+ is_torchao_available,
23
+ is_torchsde_available,
24
+ is_transformers_available,
25
+ is_transformers_version,
26
+ )
27
+
28
+
29
+ # Lazy Import based on
30
+ # https://github.com/huggingface/transformers/blob/main/src/transformers/__init__.py
31
+
32
+ # When adding a new object to this init, please add it to `_import_structure`. The `_import_structure` is a dictionary submodule to list of object names,
33
+ # and is used to defer the actual importing for when the objects are requested.
34
+ # This way `import diffusers` provides the names in the namespace without actually importing anything (and especially none of the backends).
35
+
36
+ _import_structure = {
37
+ "configuration_utils": ["ConfigMixin"],
38
+ "guiders": [],
39
+ "hooks": [],
40
+ "loaders": ["FromOriginalModelMixin"],
41
+ "models": [],
42
+ "modular_pipelines": [],
43
+ "pipelines": [],
44
+ "quantizers.pipe_quant_config": ["PipelineQuantizationConfig"],
45
+ "quantizers.quantization_config": [],
46
+ "schedulers": [],
47
+ "utils": [
48
+ "OptionalDependencyNotAvailable",
49
+ "is_flax_available",
50
+ "is_inflect_available",
51
+ "is_invisible_watermark_available",
52
+ "is_librosa_available",
53
+ "is_note_seq_available",
54
+ "is_onnx_available",
55
+ "is_scipy_available",
56
+ "is_torch_available",
57
+ "is_torchsde_available",
58
+ "is_transformers_available",
59
+ "is_transformers_version",
60
+ "is_unidecode_available",
61
+ "logging",
62
+ ],
63
+ }
64
+
65
+ try:
66
+ if not is_torch_available() and not is_accelerate_available() and not is_bitsandbytes_available():
67
+ raise OptionalDependencyNotAvailable()
68
+ except OptionalDependencyNotAvailable:
69
+ from .utils import dummy_bitsandbytes_objects
70
+
71
+ _import_structure["utils.dummy_bitsandbytes_objects"] = [
72
+ name for name in dir(dummy_bitsandbytes_objects) if not name.startswith("_")
73
+ ]
74
+ else:
75
+ _import_structure["quantizers.quantization_config"].append("BitsAndBytesConfig")
76
+
77
+ try:
78
+ if not is_torch_available() and not is_accelerate_available() and not is_gguf_available():
79
+ raise OptionalDependencyNotAvailable()
80
+ except OptionalDependencyNotAvailable:
81
+ from .utils import dummy_gguf_objects
82
+
83
+ _import_structure["utils.dummy_gguf_objects"] = [
84
+ name for name in dir(dummy_gguf_objects) if not name.startswith("_")
85
+ ]
86
+ else:
87
+ _import_structure["quantizers.quantization_config"].append("GGUFQuantizationConfig")
88
+
89
+ try:
90
+ if not is_torch_available() and not is_accelerate_available() and not is_torchao_available():
91
+ raise OptionalDependencyNotAvailable()
92
+ except OptionalDependencyNotAvailable:
93
+ from .utils import dummy_torchao_objects
94
+
95
+ _import_structure["utils.dummy_torchao_objects"] = [
96
+ name for name in dir(dummy_torchao_objects) if not name.startswith("_")
97
+ ]
98
+ else:
99
+ _import_structure["quantizers.quantization_config"].append("TorchAoConfig")
100
+
101
+ try:
102
+ if not is_torch_available() and not is_accelerate_available() and not is_optimum_quanto_available():
103
+ raise OptionalDependencyNotAvailable()
104
+ except OptionalDependencyNotAvailable:
105
+ from .utils import dummy_optimum_quanto_objects
106
+
107
+ _import_structure["utils.dummy_optimum_quanto_objects"] = [
108
+ name for name in dir(dummy_optimum_quanto_objects) if not name.startswith("_")
109
+ ]
110
+ else:
111
+ _import_structure["quantizers.quantization_config"].append("QuantoConfig")
112
+
113
+ try:
114
+ if not is_torch_available() and not is_accelerate_available() and not is_nvidia_modelopt_available():
115
+ raise OptionalDependencyNotAvailable()
116
+ except OptionalDependencyNotAvailable:
117
+ from .utils import dummy_nvidia_modelopt_objects
118
+
119
+ _import_structure["utils.dummy_nvidia_modelopt_objects"] = [
120
+ name for name in dir(dummy_nvidia_modelopt_objects) if not name.startswith("_")
121
+ ]
122
+ else:
123
+ _import_structure["quantizers.quantization_config"].append("NVIDIAModelOptConfig")
124
+
125
+ try:
126
+ if not is_onnx_available():
127
+ raise OptionalDependencyNotAvailable()
128
+ except OptionalDependencyNotAvailable:
129
+ from .utils import dummy_onnx_objects # noqa F403
130
+
131
+ _import_structure["utils.dummy_onnx_objects"] = [
132
+ name for name in dir(dummy_onnx_objects) if not name.startswith("_")
133
+ ]
134
+
135
+ else:
136
+ _import_structure["pipelines"].extend(["OnnxRuntimeModel"])
137
+
138
+ try:
139
+ if not is_torch_available():
140
+ raise OptionalDependencyNotAvailable()
141
+ except OptionalDependencyNotAvailable:
142
+ from .utils import dummy_pt_objects # noqa F403
143
+
144
+ _import_structure["utils.dummy_pt_objects"] = [name for name in dir(dummy_pt_objects) if not name.startswith("_")]
145
+
146
+ else:
147
+ _import_structure["guiders"].extend(
148
+ [
149
+ "AdaptiveProjectedGuidance",
150
+ "AdaptiveProjectedMixGuidance",
151
+ "AutoGuidance",
152
+ "BaseGuidance",
153
+ "ClassifierFreeGuidance",
154
+ "ClassifierFreeZeroStarGuidance",
155
+ "FrequencyDecoupledGuidance",
156
+ "PerturbedAttentionGuidance",
157
+ "SkipLayerGuidance",
158
+ "SmoothedEnergyGuidance",
159
+ "TangentialClassifierFreeGuidance",
160
+ ]
161
+ )
162
+ _import_structure["hooks"].extend(
163
+ [
164
+ "FasterCacheConfig",
165
+ "FirstBlockCacheConfig",
166
+ "HookRegistry",
167
+ "LayerSkipConfig",
168
+ "MagCacheConfig",
169
+ "PyramidAttentionBroadcastConfig",
170
+ "SmoothedEnergyGuidanceConfig",
171
+ "TaylorSeerCacheConfig",
172
+ "TextKVCacheConfig",
173
+ "apply_faster_cache",
174
+ "apply_first_block_cache",
175
+ "apply_layer_skip",
176
+ "apply_mag_cache",
177
+ "apply_pyramid_attention_broadcast",
178
+ "apply_taylorseer_cache",
179
+ "apply_text_kv_cache",
180
+ ]
181
+ )
182
+ _import_structure["image_processor"] = [
183
+ "InpaintProcessor",
184
+ "IPAdapterMaskProcessor",
185
+ "PixArtImageProcessor",
186
+ "VaeImageProcessor",
187
+ "VaeImageProcessorLDM3D",
188
+ ]
189
+ _import_structure["models"].extend(
190
+ [
191
+ "AllegroTransformer3DModel",
192
+ "AsymmetricAutoencoderKL",
193
+ "AttentionBackendName",
194
+ "AuraFlowTransformer2DModel",
195
+ "AutoencoderDC",
196
+ "AutoencoderKL",
197
+ "AutoencoderKLAllegro",
198
+ "AutoencoderKLCogVideoX",
199
+ "AutoencoderKLCosmos",
200
+ "AutoencoderKLFlux2",
201
+ "AutoencoderKLHunyuanImage",
202
+ "AutoencoderKLHunyuanImageRefiner",
203
+ "AutoencoderKLHunyuanVideo",
204
+ "AutoencoderKLHunyuanVideo15",
205
+ "AutoencoderKLKVAE",
206
+ "AutoencoderKLKVAEVideo",
207
+ "AutoencoderKLLTX2Audio",
208
+ "AutoencoderKLLTX2Video",
209
+ "AutoencoderKLLTXVideo",
210
+ "AutoencoderKLMagvit",
211
+ "AutoencoderKLMochi",
212
+ "AutoencoderKLQwenImage",
213
+ "AutoencoderKLTemporalDecoder",
214
+ "AutoencoderKLWan",
215
+ "AutoencoderOobleck",
216
+ "AutoencoderRAE",
217
+ "AutoencoderTiny",
218
+ "AutoencoderVidTok",
219
+ "AutoModel",
220
+ "BriaFiboTransformer2DModel",
221
+ "BriaTransformer2DModel",
222
+ "CacheMixin",
223
+ "ChromaTransformer2DModel",
224
+ "ChronoEditTransformer3DModel",
225
+ "CogVideoXTransformer3DModel",
226
+ "CogView3PlusTransformer2DModel",
227
+ "CogView4Transformer2DModel",
228
+ "ConsisIDTransformer3DModel",
229
+ "ConsistencyDecoderVAE",
230
+ "ContextParallelConfig",
231
+ "ControlNetModel",
232
+ "ControlNetUnionModel",
233
+ "ControlNetXSAdapter",
234
+ "CosmosControlNetModel",
235
+ "CosmosTransformer3DModel",
236
+ "DiTTransformer2DModel",
237
+ "EasyAnimateTransformer3DModel",
238
+ "Flux2Transformer2DModel",
239
+ "FluxControlNetModel",
240
+ "FluxMultiControlNetModel",
241
+ "FluxTransformer2DModel",
242
+ "GlmImageTransformer2DModel",
243
+ "HeliosTransformer3DModel",
244
+ "HiDreamImageTransformer2DModel",
245
+ "HunyuanDiT2DControlNetModel",
246
+ "HunyuanDiT2DModel",
247
+ "HunyuanDiT2DMultiControlNetModel",
248
+ "HunyuanImageTransformer2DModel",
249
+ "HunyuanVideo15Transformer3DModel",
250
+ "HunyuanVideoFramepackTransformer3DModel",
251
+ "HunyuanVideoTransformer3DModel",
252
+ "I2VGenXLUNet",
253
+ "Kandinsky3UNet",
254
+ "Kandinsky5Transformer3DModel",
255
+ "LatteTransformer3DModel",
256
+ "LongCatImageTransformer2DModel",
257
+ "LTX2VideoTransformer3DModel",
258
+ "LTXVideoTransformer3DModel",
259
+ "Lumina2Transformer2DModel",
260
+ "LuminaNextDiT2DModel",
261
+ "MochiTransformer3DModel",
262
+ "ModelMixin",
263
+ "MotionAdapter",
264
+ "MultiAdapter",
265
+ "MultiControlNetModel",
266
+ "NucleusMoEImageTransformer2DModel",
267
+ "OmniGenTransformer2DModel",
268
+ "OvisImageTransformer2DModel",
269
+ "ParallelConfig",
270
+ "PixArtTransformer2DModel",
271
+ "PriorTransformer",
272
+ "PRXTransformer2DModel",
273
+ "QwenImageControlNetModel",
274
+ "QwenImageMultiControlNetModel",
275
+ "QwenImageTransformer2DModel",
276
+ "SanaControlNetModel",
277
+ "SanaTransformer2DModel",
278
+ "SanaVideoTransformer3DModel",
279
+ "SD3ControlNetModel",
280
+ "SD3MultiControlNetModel",
281
+ "SD3Transformer2DModel",
282
+ "SkyReelsV2Transformer3DModel",
283
+ "SparseControlNetModel",
284
+ "StableAudioDiTModel",
285
+ "StableCascadeUNet",
286
+ "T2IAdapter",
287
+ "T5FilmDecoder",
288
+ "Transformer2DModel",
289
+ "TransformerTemporalModel",
290
+ "UNet1DModel",
291
+ "UNet2DConditionModel",
292
+ "UNet2DModel",
293
+ "UNet3DConditionModel",
294
+ "UNetControlNetXSModel",
295
+ "UNetMotionModel",
296
+ "UNetSpatioTemporalConditionModel",
297
+ "UVit2DModel",
298
+ "VQModel",
299
+ "WanAnimateTransformer3DModel",
300
+ "WanTransformer3DModel",
301
+ "WanVACETransformer3DModel",
302
+ "ZImageControlNetModel",
303
+ "ZImageTransformer2DModel",
304
+ "attention_backend",
305
+ ]
306
+ )
307
+ _import_structure["modular_pipelines"].extend(
308
+ [
309
+ "AutoPipelineBlocks",
310
+ "ComponentsManager",
311
+ "ComponentSpec",
312
+ "ConditionalPipelineBlocks",
313
+ "ConfigSpec",
314
+ "InputParam",
315
+ "LoopSequentialPipelineBlocks",
316
+ "ModularPipeline",
317
+ "ModularPipelineBlocks",
318
+ "OutputParam",
319
+ "SequentialPipelineBlocks",
320
+ ]
321
+ )
322
+ _import_structure["optimization"] = [
323
+ "get_constant_schedule",
324
+ "get_constant_schedule_with_warmup",
325
+ "get_cosine_schedule_with_warmup",
326
+ "get_cosine_with_hard_restarts_schedule_with_warmup",
327
+ "get_linear_schedule_with_warmup",
328
+ "get_polynomial_decay_schedule_with_warmup",
329
+ "get_scheduler",
330
+ ]
331
+ _import_structure["pipelines"].extend(
332
+ [
333
+ "AudioPipelineOutput",
334
+ "AutoPipelineForImage2Image",
335
+ "AutoPipelineForInpainting",
336
+ "AutoPipelineForText2Image",
337
+ "ConsistencyModelPipeline",
338
+ "DanceDiffusionPipeline",
339
+ "DDIMPipeline",
340
+ "DDPMPipeline",
341
+ "DiffusionPipeline",
342
+ "DiTPipeline",
343
+ "ImagePipelineOutput",
344
+ "KarrasVePipeline",
345
+ "LDMPipeline",
346
+ "LDMSuperResolutionPipeline",
347
+ "PNDMPipeline",
348
+ "RePaintPipeline",
349
+ "ScoreSdeVePipeline",
350
+ "StableDiffusionMixin",
351
+ ]
352
+ )
353
+ _import_structure["quantizers"] = ["DiffusersQuantizer"]
354
+ _import_structure["schedulers"].extend(
355
+ [
356
+ "AmusedScheduler",
357
+ "BlockRefinementScheduler",
358
+ "BlockRefinementSchedulerOutput",
359
+ "CMStochasticIterativeScheduler",
360
+ "CogVideoXDDIMScheduler",
361
+ "CogVideoXDPMScheduler",
362
+ "DDIMInverseScheduler",
363
+ "DDIMParallelScheduler",
364
+ "DDIMScheduler",
365
+ "DDPMParallelScheduler",
366
+ "DDPMScheduler",
367
+ "DDPMWuerstchenScheduler",
368
+ "DEISMultistepScheduler",
369
+ "DPMSolverMultistepInverseScheduler",
370
+ "DPMSolverMultistepScheduler",
371
+ "DPMSolverSinglestepScheduler",
372
+ "EDMDPMSolverMultistepScheduler",
373
+ "EDMEulerScheduler",
374
+ "EulerAncestralDiscreteScheduler",
375
+ "EulerDiscreteScheduler",
376
+ "FlowMatchEulerDiscreteScheduler",
377
+ "FlowMatchHeunDiscreteScheduler",
378
+ "FlowMatchLCMScheduler",
379
+ "HeliosDMDScheduler",
380
+ "HeliosScheduler",
381
+ "HeunDiscreteScheduler",
382
+ "IPNDMScheduler",
383
+ "KarrasVeScheduler",
384
+ "KDPM2AncestralDiscreteScheduler",
385
+ "KDPM2DiscreteScheduler",
386
+ "LCMScheduler",
387
+ "LTXEulerAncestralRFScheduler",
388
+ "PNDMScheduler",
389
+ "RePaintScheduler",
390
+ "SASolverScheduler",
391
+ "SchedulerMixin",
392
+ "SCMScheduler",
393
+ "ScoreSdeVeScheduler",
394
+ "TCDScheduler",
395
+ "UnCLIPScheduler",
396
+ "UniPCMultistepScheduler",
397
+ "VQDiffusionScheduler",
398
+ ]
399
+ )
400
+ _import_structure["training_utils"] = ["EMAModel"]
401
+ _import_structure["video_processor"] = ["VideoProcessor"]
402
+
403
+ try:
404
+ if not (is_torch_available() and is_scipy_available()):
405
+ raise OptionalDependencyNotAvailable()
406
+ except OptionalDependencyNotAvailable:
407
+ from .utils import dummy_torch_and_scipy_objects # noqa F403
408
+
409
+ _import_structure["utils.dummy_torch_and_scipy_objects"] = [
410
+ name for name in dir(dummy_torch_and_scipy_objects) if not name.startswith("_")
411
+ ]
412
+
413
+ else:
414
+ _import_structure["schedulers"].extend(["LMSDiscreteScheduler"])
415
+
416
+ try:
417
+ if not (is_torch_available() and is_torchsde_available()):
418
+ raise OptionalDependencyNotAvailable()
419
+ except OptionalDependencyNotAvailable:
420
+ from .utils import dummy_torch_and_torchsde_objects # noqa F403
421
+
422
+ _import_structure["utils.dummy_torch_and_torchsde_objects"] = [
423
+ name for name in dir(dummy_torch_and_torchsde_objects) if not name.startswith("_")
424
+ ]
425
+
426
+ else:
427
+ _import_structure["schedulers"].extend(["CosineDPMSolverMultistepScheduler", "DPMSolverSDEScheduler"])
428
+
429
+ try:
430
+ if not (is_torch_available() and is_transformers_available()):
431
+ raise OptionalDependencyNotAvailable()
432
+ except OptionalDependencyNotAvailable:
433
+ from .utils import dummy_torch_and_transformers_objects # noqa F403
434
+
435
+ _import_structure["utils.dummy_torch_and_transformers_objects"] = [
436
+ name for name in dir(dummy_torch_and_transformers_objects) if not name.startswith("_")
437
+ ]
438
+
439
+ else:
440
+ _import_structure["modular_pipelines"].extend(
441
+ [
442
+ "Flux2AutoBlocks",
443
+ "Flux2KleinAutoBlocks",
444
+ "Flux2KleinBaseAutoBlocks",
445
+ "Flux2KleinBaseModularPipeline",
446
+ "Flux2KleinModularPipeline",
447
+ "Flux2ModularPipeline",
448
+ "FluxAutoBlocks",
449
+ "FluxKontextAutoBlocks",
450
+ "FluxKontextModularPipeline",
451
+ "FluxModularPipeline",
452
+ "HeliosAutoBlocks",
453
+ "HeliosModularPipeline",
454
+ "HeliosPyramidAutoBlocks",
455
+ "HeliosPyramidDistilledAutoBlocks",
456
+ "HeliosPyramidDistilledModularPipeline",
457
+ "HeliosPyramidModularPipeline",
458
+ "QwenImageAutoBlocks",
459
+ "QwenImageEditAutoBlocks",
460
+ "QwenImageEditModularPipeline",
461
+ "QwenImageEditPlusAutoBlocks",
462
+ "QwenImageEditPlusModularPipeline",
463
+ "QwenImageLayeredAutoBlocks",
464
+ "QwenImageLayeredModularPipeline",
465
+ "QwenImageModularPipeline",
466
+ "StableDiffusionXLAutoBlocks",
467
+ "StableDiffusionXLModularPipeline",
468
+ "Wan22Blocks",
469
+ "Wan22Image2VideoBlocks",
470
+ "Wan22Image2VideoModularPipeline",
471
+ "Wan22ModularPipeline",
472
+ "WanBlocks",
473
+ "WanImage2VideoAutoBlocks",
474
+ "WanImage2VideoModularPipeline",
475
+ "WanModularPipeline",
476
+ "ZImageAutoBlocks",
477
+ "ZImageModularPipeline",
478
+ ]
479
+ )
480
+ _import_structure["pipelines"].extend(
481
+ [
482
+ "AllegroPipeline",
483
+ "AltDiffusionImg2ImgPipeline",
484
+ "AltDiffusionPipeline",
485
+ "AmusedImg2ImgPipeline",
486
+ "AmusedInpaintPipeline",
487
+ "AmusedPipeline",
488
+ "AnimateDiffControlNetPipeline",
489
+ "AnimateDiffPAGPipeline",
490
+ "AnimateDiffPipeline",
491
+ "AnimateDiffSDXLPipeline",
492
+ "AnimateDiffSparseControlNetPipeline",
493
+ "AnimateDiffVideoToVideoControlNetPipeline",
494
+ "AnimateDiffVideoToVideoPipeline",
495
+ "AudioLDM2Pipeline",
496
+ "AudioLDM2ProjectionModel",
497
+ "AudioLDM2UNet2DConditionModel",
498
+ "AudioLDMPipeline",
499
+ "AuraFlowPipeline",
500
+ "BlipDiffusionControlNetPipeline",
501
+ "BlipDiffusionPipeline",
502
+ "BriaFiboEditPipeline",
503
+ "BriaFiboPipeline",
504
+ "BriaPipeline",
505
+ "ChromaImg2ImgPipeline",
506
+ "ChromaInpaintPipeline",
507
+ "ChromaPipeline",
508
+ "ChronoEditPipeline",
509
+ "CLIPImageProjection",
510
+ "CogVideoXFunControlPipeline",
511
+ "CogVideoXImageToVideoPipeline",
512
+ "CogVideoXPipeline",
513
+ "CogVideoXVideoToVideoPipeline",
514
+ "CogView3PlusPipeline",
515
+ "CogView4ControlPipeline",
516
+ "CogView4Pipeline",
517
+ "ConsisIDPipeline",
518
+ "Cosmos2_5_PredictBasePipeline",
519
+ "Cosmos2_5_TransferPipeline",
520
+ "Cosmos2TextToImagePipeline",
521
+ "Cosmos2VideoToWorldPipeline",
522
+ "CosmosTextToWorldPipeline",
523
+ "CosmosVideoToWorldPipeline",
524
+ "CycleDiffusionPipeline",
525
+ "EasyAnimateControlPipeline",
526
+ "EasyAnimateInpaintPipeline",
527
+ "EasyAnimatePipeline",
528
+ "Flux2KleinKVPipeline",
529
+ "Flux2KleinPipeline",
530
+ "Flux2Pipeline",
531
+ "FluxControlImg2ImgPipeline",
532
+ "FluxControlInpaintPipeline",
533
+ "FluxControlNetImg2ImgPipeline",
534
+ "FluxControlNetInpaintPipeline",
535
+ "FluxControlNetPipeline",
536
+ "FluxControlPipeline",
537
+ "FluxFillPipeline",
538
+ "FluxImg2ImgPipeline",
539
+ "FluxInpaintPipeline",
540
+ "FluxKontextInpaintPipeline",
541
+ "FluxKontextPipeline",
542
+ "FluxPipeline",
543
+ "FluxPriorReduxPipeline",
544
+ "GlmImagePipeline",
545
+ "HeliosPipeline",
546
+ "HeliosPyramidPipeline",
547
+ "HiDreamImagePipeline",
548
+ "HunyuanDiTControlNetPipeline",
549
+ "HunyuanDiTPAGPipeline",
550
+ "HunyuanDiTPipeline",
551
+ "HunyuanImagePipeline",
552
+ "HunyuanImageRefinerPipeline",
553
+ "HunyuanSkyreelsImageToVideoPipeline",
554
+ "HunyuanVideo15ImageToVideoPipeline",
555
+ "HunyuanVideo15Pipeline",
556
+ "HunyuanVideoFramepackPipeline",
557
+ "HunyuanVideoImageToVideoPipeline",
558
+ "HunyuanVideoPipeline",
559
+ "I2VGenXLPipeline",
560
+ "IFImg2ImgPipeline",
561
+ "IFImg2ImgSuperResolutionPipeline",
562
+ "IFInpaintingPipeline",
563
+ "IFInpaintingSuperResolutionPipeline",
564
+ "IFPipeline",
565
+ "IFSuperResolutionPipeline",
566
+ "ImageTextPipelineOutput",
567
+ "Kandinsky3Img2ImgPipeline",
568
+ "Kandinsky3Pipeline",
569
+ "Kandinsky5I2IPipeline",
570
+ "Kandinsky5I2VPipeline",
571
+ "Kandinsky5T2IPipeline",
572
+ "Kandinsky5T2VPipeline",
573
+ "KandinskyCombinedPipeline",
574
+ "KandinskyImg2ImgCombinedPipeline",
575
+ "KandinskyImg2ImgPipeline",
576
+ "KandinskyInpaintCombinedPipeline",
577
+ "KandinskyInpaintPipeline",
578
+ "KandinskyPipeline",
579
+ "KandinskyPriorPipeline",
580
+ "KandinskyV22CombinedPipeline",
581
+ "KandinskyV22ControlnetImg2ImgPipeline",
582
+ "KandinskyV22ControlnetPipeline",
583
+ "KandinskyV22Img2ImgCombinedPipeline",
584
+ "KandinskyV22Img2ImgPipeline",
585
+ "KandinskyV22InpaintCombinedPipeline",
586
+ "KandinskyV22InpaintPipeline",
587
+ "KandinskyV22Pipeline",
588
+ "KandinskyV22PriorEmb2EmbPipeline",
589
+ "KandinskyV22PriorPipeline",
590
+ "LatentConsistencyModelImg2ImgPipeline",
591
+ "LatentConsistencyModelPipeline",
592
+ "LattePipeline",
593
+ "LDMTextToImagePipeline",
594
+ "LEditsPPPipelineStableDiffusion",
595
+ "LEditsPPPipelineStableDiffusionXL",
596
+ "LLaDA2Pipeline",
597
+ "LLaDA2PipelineOutput",
598
+ "LongCatImageEditPipeline",
599
+ "LongCatImagePipeline",
600
+ "LTX2ConditionPipeline",
601
+ "LTX2ImageToVideoPipeline",
602
+ "LTX2LatentUpsamplePipeline",
603
+ "LTX2Pipeline",
604
+ "LTXConditionPipeline",
605
+ "LTXI2VLongMultiPromptPipeline",
606
+ "LTXImageToVideoPipeline",
607
+ "LTXLatentUpsamplePipeline",
608
+ "LTXPipeline",
609
+ "LucyEditPipeline",
610
+ "Lumina2Pipeline",
611
+ "Lumina2Text2ImgPipeline",
612
+ "LuminaPipeline",
613
+ "LuminaText2ImgPipeline",
614
+ "MarigoldDepthPipeline",
615
+ "MarigoldIntrinsicsPipeline",
616
+ "MarigoldNormalsPipeline",
617
+ "MochiPipeline",
618
+ "MusicLDMPipeline",
619
+ "NucleusMoEImagePipeline",
620
+ "OmniGenPipeline",
621
+ "OvisImagePipeline",
622
+ "PaintByExamplePipeline",
623
+ "PIAPipeline",
624
+ "PixArtAlphaPipeline",
625
+ "PixArtSigmaPAGPipeline",
626
+ "PixArtSigmaPipeline",
627
+ "PRXPipeline",
628
+ "QwenImageControlNetInpaintPipeline",
629
+ "QwenImageControlNetPipeline",
630
+ "QwenImageEditInpaintPipeline",
631
+ "QwenImageEditPipeline",
632
+ "QwenImageEditPlusPipeline",
633
+ "QwenImageImg2ImgPipeline",
634
+ "QwenImageInpaintPipeline",
635
+ "QwenImageLayeredPipeline",
636
+ "QwenImagePipeline",
637
+ "ReduxImageEncoder",
638
+ "SanaControlNetPipeline",
639
+ "SanaImageToVideoPipeline",
640
+ "SanaPAGPipeline",
641
+ "SanaPipeline",
642
+ "SanaSprintImg2ImgPipeline",
643
+ "SanaSprintPipeline",
644
+ "SanaVideoPipeline",
645
+ "SanaVideoPipeline",
646
+ "SemanticStableDiffusionPipeline",
647
+ "ShapEImg2ImgPipeline",
648
+ "ShapEPipeline",
649
+ "SkyReelsV2DiffusionForcingImageToVideoPipeline",
650
+ "SkyReelsV2DiffusionForcingPipeline",
651
+ "SkyReelsV2DiffusionForcingVideoToVideoPipeline",
652
+ "SkyReelsV2ImageToVideoPipeline",
653
+ "SkyReelsV2Pipeline",
654
+ "StableAudioPipeline",
655
+ "StableAudioProjectionModel",
656
+ "StableCascadeCombinedPipeline",
657
+ "StableCascadeDecoderPipeline",
658
+ "StableCascadePriorPipeline",
659
+ "StableDiffusion3ControlNetInpaintingPipeline",
660
+ "StableDiffusion3ControlNetPipeline",
661
+ "StableDiffusion3Img2ImgPipeline",
662
+ "StableDiffusion3InpaintPipeline",
663
+ "StableDiffusion3PAGImg2ImgPipeline",
664
+ "StableDiffusion3PAGImg2ImgPipeline",
665
+ "StableDiffusion3PAGPipeline",
666
+ "StableDiffusion3Pipeline",
667
+ "StableDiffusionAdapterPipeline",
668
+ "StableDiffusionAttendAndExcitePipeline",
669
+ "StableDiffusionControlNetImg2ImgPipeline",
670
+ "StableDiffusionControlNetInpaintPipeline",
671
+ "StableDiffusionControlNetPAGInpaintPipeline",
672
+ "StableDiffusionControlNetPAGPipeline",
673
+ "StableDiffusionControlNetPipeline",
674
+ "StableDiffusionControlNetXSPipeline",
675
+ "StableDiffusionDepth2ImgPipeline",
676
+ "StableDiffusionDiffEditPipeline",
677
+ "StableDiffusionGLIGENPipeline",
678
+ "StableDiffusionGLIGENTextImagePipeline",
679
+ "StableDiffusionImageVariationPipeline",
680
+ "StableDiffusionImg2ImgPipeline",
681
+ "StableDiffusionInpaintPipeline",
682
+ "StableDiffusionInpaintPipelineLegacy",
683
+ "StableDiffusionInstructPix2PixPipeline",
684
+ "StableDiffusionLatentUpscalePipeline",
685
+ "StableDiffusionLDM3DPipeline",
686
+ "StableDiffusionModelEditingPipeline",
687
+ "StableDiffusionPAGImg2ImgPipeline",
688
+ "StableDiffusionPAGInpaintPipeline",
689
+ "StableDiffusionPAGPipeline",
690
+ "StableDiffusionPanoramaPipeline",
691
+ "StableDiffusionParadigmsPipeline",
692
+ "StableDiffusionPipeline",
693
+ "StableDiffusionPipelineSafe",
694
+ "StableDiffusionPix2PixZeroPipeline",
695
+ "StableDiffusionSAGPipeline",
696
+ "StableDiffusionUpscalePipeline",
697
+ "StableDiffusionXLAdapterPipeline",
698
+ "StableDiffusionXLControlNetImg2ImgPipeline",
699
+ "StableDiffusionXLControlNetInpaintPipeline",
700
+ "StableDiffusionXLControlNetPAGImg2ImgPipeline",
701
+ "StableDiffusionXLControlNetPAGPipeline",
702
+ "StableDiffusionXLControlNetPipeline",
703
+ "StableDiffusionXLControlNetUnionImg2ImgPipeline",
704
+ "StableDiffusionXLControlNetUnionInpaintPipeline",
705
+ "StableDiffusionXLControlNetUnionPipeline",
706
+ "StableDiffusionXLControlNetXSPipeline",
707
+ "StableDiffusionXLImg2ImgPipeline",
708
+ "StableDiffusionXLInpaintPipeline",
709
+ "StableDiffusionXLInstructPix2PixPipeline",
710
+ "StableDiffusionXLPAGImg2ImgPipeline",
711
+ "StableDiffusionXLPAGInpaintPipeline",
712
+ "StableDiffusionXLPAGPipeline",
713
+ "StableDiffusionXLPipeline",
714
+ "StableUnCLIPImg2ImgPipeline",
715
+ "StableUnCLIPPipeline",
716
+ "StableVideoDiffusionPipeline",
717
+ "TextToVideoSDPipeline",
718
+ "TextToVideoZeroPipeline",
719
+ "TextToVideoZeroSDXLPipeline",
720
+ "UnCLIPImageVariationPipeline",
721
+ "UnCLIPPipeline",
722
+ "UniDiffuserModel",
723
+ "UniDiffuserPipeline",
724
+ "UniDiffuserTextDecoder",
725
+ "VersatileDiffusionDualGuidedPipeline",
726
+ "VersatileDiffusionImageVariationPipeline",
727
+ "VersatileDiffusionPipeline",
728
+ "VersatileDiffusionTextToImagePipeline",
729
+ "VideoToVideoSDPipeline",
730
+ "VisualClozeGenerationPipeline",
731
+ "VisualClozePipeline",
732
+ "VQDiffusionPipeline",
733
+ "WanAnimatePipeline",
734
+ "WanImageToVideoPipeline",
735
+ "WanPipeline",
736
+ "WanVACEPipeline",
737
+ "WanVideoToVideoPipeline",
738
+ "WuerstchenCombinedPipeline",
739
+ "WuerstchenDecoderPipeline",
740
+ "WuerstchenPriorPipeline",
741
+ "ZImageControlNetInpaintPipeline",
742
+ "ZImageControlNetPipeline",
743
+ "ZImageImg2ImgPipeline",
744
+ "ZImageInpaintPipeline",
745
+ "ZImageOmniPipeline",
746
+ "ZImagePipeline",
747
+ ]
748
+ )
749
+
750
+
751
+ try:
752
+ if not (is_torch_available() and is_transformers_available() and is_opencv_available()):
753
+ raise OptionalDependencyNotAvailable()
754
+ except OptionalDependencyNotAvailable:
755
+ from .utils import dummy_torch_and_transformers_and_opencv_objects # noqa F403
756
+
757
+ _import_structure["utils.dummy_torch_and_transformers_and_opencv_objects"] = [
758
+ name for name in dir(dummy_torch_and_transformers_and_opencv_objects) if not name.startswith("_")
759
+ ]
760
+
761
+ else:
762
+ _import_structure["pipelines"].extend(["ConsisIDPipeline"])
763
+
764
+ try:
765
+ if not (is_torch_available() and is_transformers_available() and is_sentencepiece_available()):
766
+ raise OptionalDependencyNotAvailable()
767
+ except OptionalDependencyNotAvailable:
768
+ from .utils import dummy_torch_and_transformers_and_sentencepiece_objects # noqa F403
769
+
770
+ _import_structure["utils.dummy_torch_and_transformers_and_sentencepiece_objects"] = [
771
+ name for name in dir(dummy_torch_and_transformers_and_sentencepiece_objects) if not name.startswith("_")
772
+ ]
773
+
774
+ else:
775
+ _import_structure["pipelines"].extend(["KolorsImg2ImgPipeline", "KolorsPAGPipeline", "KolorsPipeline"])
776
+
777
+ try:
778
+ if not (is_torch_available() and is_transformers_available() and is_onnx_available()):
779
+ raise OptionalDependencyNotAvailable()
780
+ except OptionalDependencyNotAvailable:
781
+ from .utils import dummy_torch_and_transformers_and_onnx_objects # noqa F403
782
+
783
+ _import_structure["utils.dummy_torch_and_transformers_and_onnx_objects"] = [
784
+ name for name in dir(dummy_torch_and_transformers_and_onnx_objects) if not name.startswith("_")
785
+ ]
786
+
787
+ else:
788
+ _import_structure["pipelines"].extend(
789
+ [
790
+ "OnnxStableDiffusionImg2ImgPipeline",
791
+ "OnnxStableDiffusionInpaintPipeline",
792
+ "OnnxStableDiffusionInpaintPipelineLegacy",
793
+ "OnnxStableDiffusionPipeline",
794
+ "OnnxStableDiffusionUpscalePipeline",
795
+ "StableDiffusionOnnxPipeline",
796
+ ]
797
+ )
798
+
799
+ try:
800
+ if not (is_torch_available() and is_librosa_available()):
801
+ raise OptionalDependencyNotAvailable()
802
+ except OptionalDependencyNotAvailable:
803
+ from .utils import dummy_torch_and_librosa_objects # noqa F403
804
+
805
+ _import_structure["utils.dummy_torch_and_librosa_objects"] = [
806
+ name for name in dir(dummy_torch_and_librosa_objects) if not name.startswith("_")
807
+ ]
808
+
809
+ else:
810
+ _import_structure["pipelines"].extend(["AudioDiffusionPipeline", "Mel"])
811
+
812
+ try:
813
+ if not (is_transformers_available() and is_torch_available() and is_note_seq_available()):
814
+ raise OptionalDependencyNotAvailable()
815
+ except OptionalDependencyNotAvailable:
816
+ from .utils import dummy_transformers_and_torch_and_note_seq_objects # noqa F403
817
+
818
+ _import_structure["utils.dummy_transformers_and_torch_and_note_seq_objects"] = [
819
+ name for name in dir(dummy_transformers_and_torch_and_note_seq_objects) if not name.startswith("_")
820
+ ]
821
+
822
+
823
+ else:
824
+ _import_structure["pipelines"].extend(["SpectrogramDiffusionPipeline"])
825
+
826
+ try:
827
+ if not is_flax_available():
828
+ raise OptionalDependencyNotAvailable()
829
+ except OptionalDependencyNotAvailable:
830
+ from .utils import dummy_flax_objects # noqa F403
831
+
832
+ _import_structure["utils.dummy_flax_objects"] = [
833
+ name for name in dir(dummy_flax_objects) if not name.startswith("_")
834
+ ]
835
+
836
+
837
+ else:
838
+ _import_structure["models.controlnets.controlnet_flax"] = ["FlaxControlNetModel"]
839
+ _import_structure["models.modeling_flax_utils"] = ["FlaxModelMixin"]
840
+ _import_structure["models.unets.unet_2d_condition_flax"] = ["FlaxUNet2DConditionModel"]
841
+ _import_structure["models.vae_flax"] = ["FlaxAutoencoderKL"]
842
+ _import_structure["pipelines"].extend(["FlaxDiffusionPipeline"])
843
+ _import_structure["schedulers"].extend(
844
+ [
845
+ "FlaxDDIMScheduler",
846
+ "FlaxDDPMScheduler",
847
+ "FlaxDPMSolverMultistepScheduler",
848
+ "FlaxEulerDiscreteScheduler",
849
+ "FlaxKarrasVeScheduler",
850
+ "FlaxLMSDiscreteScheduler",
851
+ "FlaxPNDMScheduler",
852
+ "FlaxSchedulerMixin",
853
+ "FlaxScoreSdeVeScheduler",
854
+ ]
855
+ )
856
+
857
+
858
+ try:
859
+ if not (is_flax_available() and is_transformers_available()):
860
+ raise OptionalDependencyNotAvailable()
861
+ except OptionalDependencyNotAvailable:
862
+ from .utils import dummy_flax_and_transformers_objects # noqa F403
863
+
864
+ _import_structure["utils.dummy_flax_and_transformers_objects"] = [
865
+ name for name in dir(dummy_flax_and_transformers_objects) if not name.startswith("_")
866
+ ]
867
+
868
+
869
+ else:
870
+ _import_structure["pipelines"].extend(
871
+ [
872
+ "FlaxStableDiffusionControlNetPipeline",
873
+ "FlaxStableDiffusionImg2ImgPipeline",
874
+ "FlaxStableDiffusionInpaintPipeline",
875
+ "FlaxStableDiffusionPipeline",
876
+ "FlaxStableDiffusionXLPipeline",
877
+ ]
878
+ )
879
+
880
+ try:
881
+ if not (is_note_seq_available()):
882
+ raise OptionalDependencyNotAvailable()
883
+ except OptionalDependencyNotAvailable:
884
+ from .utils import dummy_note_seq_objects # noqa F403
885
+
886
+ _import_structure["utils.dummy_note_seq_objects"] = [
887
+ name for name in dir(dummy_note_seq_objects) if not name.startswith("_")
888
+ ]
889
+
890
+
891
+ else:
892
+ _import_structure["pipelines"].extend(["MidiProcessor"])
893
+
894
+ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
895
+ from .configuration_utils import ConfigMixin
896
+ from .quantizers import PipelineQuantizationConfig
897
+
898
+ try:
899
+ if not is_bitsandbytes_available():
900
+ raise OptionalDependencyNotAvailable()
901
+ except OptionalDependencyNotAvailable:
902
+ from .utils.dummy_bitsandbytes_objects import *
903
+ else:
904
+ from .quantizers.quantization_config import BitsAndBytesConfig
905
+
906
+ try:
907
+ if not is_gguf_available():
908
+ raise OptionalDependencyNotAvailable()
909
+ except OptionalDependencyNotAvailable:
910
+ from .utils.dummy_gguf_objects import *
911
+ else:
912
+ from .quantizers.quantization_config import GGUFQuantizationConfig
913
+
914
+ try:
915
+ if not is_torchao_available():
916
+ raise OptionalDependencyNotAvailable()
917
+ except OptionalDependencyNotAvailable:
918
+ from .utils.dummy_torchao_objects import *
919
+ else:
920
+ from .quantizers.quantization_config import TorchAoConfig
921
+
922
+ try:
923
+ if not is_optimum_quanto_available():
924
+ raise OptionalDependencyNotAvailable()
925
+ except OptionalDependencyNotAvailable:
926
+ from .utils.dummy_optimum_quanto_objects import *
927
+ else:
928
+ from .quantizers.quantization_config import QuantoConfig
929
+
930
+ try:
931
+ if not is_nvidia_modelopt_available():
932
+ raise OptionalDependencyNotAvailable()
933
+ except OptionalDependencyNotAvailable:
934
+ from .utils.dummy_nvidia_modelopt_objects import *
935
+ else:
936
+ from .quantizers.quantization_config import NVIDIAModelOptConfig
937
+
938
+ try:
939
+ if not is_onnx_available():
940
+ raise OptionalDependencyNotAvailable()
941
+ except OptionalDependencyNotAvailable:
942
+ from .utils.dummy_onnx_objects import * # noqa F403
943
+ else:
944
+ from .pipelines import OnnxRuntimeModel
945
+
946
+ try:
947
+ if not is_torch_available():
948
+ raise OptionalDependencyNotAvailable()
949
+ except OptionalDependencyNotAvailable:
950
+ from .utils.dummy_pt_objects import * # noqa F403
951
+ else:
952
+ from .guiders import (
953
+ AdaptiveProjectedGuidance,
954
+ AdaptiveProjectedMixGuidance,
955
+ AutoGuidance,
956
+ BaseGuidance,
957
+ ClassifierFreeGuidance,
958
+ ClassifierFreeZeroStarGuidance,
959
+ FrequencyDecoupledGuidance,
960
+ PerturbedAttentionGuidance,
961
+ SkipLayerGuidance,
962
+ SmoothedEnergyGuidance,
963
+ TangentialClassifierFreeGuidance,
964
+ )
965
+ from .hooks import (
966
+ FasterCacheConfig,
967
+ FirstBlockCacheConfig,
968
+ HookRegistry,
969
+ LayerSkipConfig,
970
+ MagCacheConfig,
971
+ PyramidAttentionBroadcastConfig,
972
+ SmoothedEnergyGuidanceConfig,
973
+ TaylorSeerCacheConfig,
974
+ TextKVCacheConfig,
975
+ apply_faster_cache,
976
+ apply_first_block_cache,
977
+ apply_layer_skip,
978
+ apply_mag_cache,
979
+ apply_pyramid_attention_broadcast,
980
+ apply_taylorseer_cache,
981
+ apply_text_kv_cache,
982
+ )
983
+ from .image_processor import (
984
+ InpaintProcessor,
985
+ IPAdapterMaskProcessor,
986
+ PixArtImageProcessor,
987
+ VaeImageProcessor,
988
+ VaeImageProcessorLDM3D,
989
+ )
990
+ from .models import (
991
+ AllegroTransformer3DModel,
992
+ AsymmetricAutoencoderKL,
993
+ AttentionBackendName,
994
+ AuraFlowTransformer2DModel,
995
+ AutoencoderDC,
996
+ AutoencoderKL,
997
+ AutoencoderKLAllegro,
998
+ AutoencoderKLCogVideoX,
999
+ AutoencoderKLCosmos,
1000
+ AutoencoderKLFlux2,
1001
+ AutoencoderKLHunyuanImage,
1002
+ AutoencoderKLHunyuanImageRefiner,
1003
+ AutoencoderKLHunyuanVideo,
1004
+ AutoencoderKLHunyuanVideo15,
1005
+ AutoencoderKLKVAE,
1006
+ AutoencoderKLKVAEVideo,
1007
+ AutoencoderKLLTX2Audio,
1008
+ AutoencoderKLLTX2Video,
1009
+ AutoencoderKLLTXVideo,
1010
+ AutoencoderKLMagvit,
1011
+ AutoencoderKLMochi,
1012
+ AutoencoderKLQwenImage,
1013
+ AutoencoderKLTemporalDecoder,
1014
+ AutoencoderKLWan,
1015
+ AutoencoderOobleck,
1016
+ AutoencoderRAE,
1017
+ AutoencoderTiny,
1018
+ AutoencoderVidTok,
1019
+ AutoModel,
1020
+ BriaFiboTransformer2DModel,
1021
+ BriaTransformer2DModel,
1022
+ CacheMixin,
1023
+ ChromaTransformer2DModel,
1024
+ ChronoEditTransformer3DModel,
1025
+ CogVideoXTransformer3DModel,
1026
+ CogView3PlusTransformer2DModel,
1027
+ CogView4Transformer2DModel,
1028
+ ConsisIDTransformer3DModel,
1029
+ ConsistencyDecoderVAE,
1030
+ ContextParallelConfig,
1031
+ ControlNetModel,
1032
+ ControlNetUnionModel,
1033
+ ControlNetXSAdapter,
1034
+ CosmosControlNetModel,
1035
+ CosmosTransformer3DModel,
1036
+ DiTTransformer2DModel,
1037
+ EasyAnimateTransformer3DModel,
1038
+ Flux2Transformer2DModel,
1039
+ FluxControlNetModel,
1040
+ FluxMultiControlNetModel,
1041
+ FluxTransformer2DModel,
1042
+ GlmImageTransformer2DModel,
1043
+ HeliosTransformer3DModel,
1044
+ HiDreamImageTransformer2DModel,
1045
+ HunyuanDiT2DControlNetModel,
1046
+ HunyuanDiT2DModel,
1047
+ HunyuanDiT2DMultiControlNetModel,
1048
+ HunyuanImageTransformer2DModel,
1049
+ HunyuanVideo15Transformer3DModel,
1050
+ HunyuanVideoFramepackTransformer3DModel,
1051
+ HunyuanVideoTransformer3DModel,
1052
+ I2VGenXLUNet,
1053
+ Kandinsky3UNet,
1054
+ Kandinsky5Transformer3DModel,
1055
+ LatteTransformer3DModel,
1056
+ LongCatImageTransformer2DModel,
1057
+ LTX2VideoTransformer3DModel,
1058
+ LTXVideoTransformer3DModel,
1059
+ Lumina2Transformer2DModel,
1060
+ LuminaNextDiT2DModel,
1061
+ MochiTransformer3DModel,
1062
+ ModelMixin,
1063
+ MotionAdapter,
1064
+ MultiAdapter,
1065
+ MultiControlNetModel,
1066
+ NucleusMoEImageTransformer2DModel,
1067
+ OmniGenTransformer2DModel,
1068
+ OvisImageTransformer2DModel,
1069
+ ParallelConfig,
1070
+ PixArtTransformer2DModel,
1071
+ PriorTransformer,
1072
+ PRXTransformer2DModel,
1073
+ QwenImageControlNetModel,
1074
+ QwenImageMultiControlNetModel,
1075
+ QwenImageTransformer2DModel,
1076
+ SanaControlNetModel,
1077
+ SanaTransformer2DModel,
1078
+ SanaVideoTransformer3DModel,
1079
+ SD3ControlNetModel,
1080
+ SD3MultiControlNetModel,
1081
+ SD3Transformer2DModel,
1082
+ SkyReelsV2Transformer3DModel,
1083
+ SparseControlNetModel,
1084
+ StableAudioDiTModel,
1085
+ T2IAdapter,
1086
+ T5FilmDecoder,
1087
+ Transformer2DModel,
1088
+ TransformerTemporalModel,
1089
+ UNet1DModel,
1090
+ UNet2DConditionModel,
1091
+ UNet2DModel,
1092
+ UNet3DConditionModel,
1093
+ UNetControlNetXSModel,
1094
+ UNetMotionModel,
1095
+ UNetSpatioTemporalConditionModel,
1096
+ UVit2DModel,
1097
+ VQModel,
1098
+ WanAnimateTransformer3DModel,
1099
+ WanTransformer3DModel,
1100
+ WanVACETransformer3DModel,
1101
+ ZImageControlNetModel,
1102
+ ZImageTransformer2DModel,
1103
+ attention_backend,
1104
+ )
1105
+ from .modular_pipelines import (
1106
+ AutoPipelineBlocks,
1107
+ ComponentsManager,
1108
+ ComponentSpec,
1109
+ ConditionalPipelineBlocks,
1110
+ ConfigSpec,
1111
+ InputParam,
1112
+ LoopSequentialPipelineBlocks,
1113
+ ModularPipeline,
1114
+ ModularPipelineBlocks,
1115
+ OutputParam,
1116
+ SequentialPipelineBlocks,
1117
+ )
1118
+ from .optimization import (
1119
+ get_constant_schedule,
1120
+ get_constant_schedule_with_warmup,
1121
+ get_cosine_schedule_with_warmup,
1122
+ get_cosine_with_hard_restarts_schedule_with_warmup,
1123
+ get_linear_schedule_with_warmup,
1124
+ get_polynomial_decay_schedule_with_warmup,
1125
+ get_scheduler,
1126
+ )
1127
+ from .pipelines import (
1128
+ AudioPipelineOutput,
1129
+ AutoPipelineForImage2Image,
1130
+ AutoPipelineForInpainting,
1131
+ AutoPipelineForText2Image,
1132
+ BlipDiffusionControlNetPipeline,
1133
+ BlipDiffusionPipeline,
1134
+ CLIPImageProjection,
1135
+ ConsistencyModelPipeline,
1136
+ DanceDiffusionPipeline,
1137
+ DDIMPipeline,
1138
+ DDPMPipeline,
1139
+ DiffusionPipeline,
1140
+ DiTPipeline,
1141
+ ImagePipelineOutput,
1142
+ KarrasVePipeline,
1143
+ LDMPipeline,
1144
+ LDMSuperResolutionPipeline,
1145
+ PNDMPipeline,
1146
+ RePaintPipeline,
1147
+ ScoreSdeVePipeline,
1148
+ StableDiffusionMixin,
1149
+ )
1150
+ from .quantizers import DiffusersQuantizer
1151
+ from .schedulers import (
1152
+ AmusedScheduler,
1153
+ BlockRefinementScheduler,
1154
+ BlockRefinementSchedulerOutput,
1155
+ CMStochasticIterativeScheduler,
1156
+ CogVideoXDDIMScheduler,
1157
+ CogVideoXDPMScheduler,
1158
+ DDIMInverseScheduler,
1159
+ DDIMParallelScheduler,
1160
+ DDIMScheduler,
1161
+ DDPMParallelScheduler,
1162
+ DDPMScheduler,
1163
+ DDPMWuerstchenScheduler,
1164
+ DEISMultistepScheduler,
1165
+ DPMSolverMultistepInverseScheduler,
1166
+ DPMSolverMultistepScheduler,
1167
+ DPMSolverSinglestepScheduler,
1168
+ EDMDPMSolverMultistepScheduler,
1169
+ EDMEulerScheduler,
1170
+ EulerAncestralDiscreteScheduler,
1171
+ EulerDiscreteScheduler,
1172
+ FlowMatchEulerDiscreteScheduler,
1173
+ FlowMatchHeunDiscreteScheduler,
1174
+ FlowMatchLCMScheduler,
1175
+ HeliosDMDScheduler,
1176
+ HeliosScheduler,
1177
+ HeunDiscreteScheduler,
1178
+ IPNDMScheduler,
1179
+ KarrasVeScheduler,
1180
+ KDPM2AncestralDiscreteScheduler,
1181
+ KDPM2DiscreteScheduler,
1182
+ LCMScheduler,
1183
+ LTXEulerAncestralRFScheduler,
1184
+ PNDMScheduler,
1185
+ RePaintScheduler,
1186
+ SASolverScheduler,
1187
+ SchedulerMixin,
1188
+ SCMScheduler,
1189
+ ScoreSdeVeScheduler,
1190
+ TCDScheduler,
1191
+ UnCLIPScheduler,
1192
+ UniPCMultistepScheduler,
1193
+ VQDiffusionScheduler,
1194
+ )
1195
+ from .training_utils import EMAModel
1196
+ from .video_processor import VideoProcessor
1197
+
1198
+ try:
1199
+ if not (is_torch_available() and is_scipy_available()):
1200
+ raise OptionalDependencyNotAvailable()
1201
+ except OptionalDependencyNotAvailable:
1202
+ from .utils.dummy_torch_and_scipy_objects import * # noqa F403
1203
+ else:
1204
+ from .schedulers import LMSDiscreteScheduler
1205
+
1206
+ try:
1207
+ if not (is_torch_available() and is_torchsde_available()):
1208
+ raise OptionalDependencyNotAvailable()
1209
+ except OptionalDependencyNotAvailable:
1210
+ from .utils.dummy_torch_and_torchsde_objects import * # noqa F403
1211
+ else:
1212
+ from .schedulers import CosineDPMSolverMultistepScheduler, DPMSolverSDEScheduler
1213
+
1214
+ try:
1215
+ if not (is_torch_available() and is_transformers_available()):
1216
+ raise OptionalDependencyNotAvailable()
1217
+ except OptionalDependencyNotAvailable:
1218
+ from .utils.dummy_torch_and_transformers_objects import * # noqa F403
1219
+ else:
1220
+ from .modular_pipelines import (
1221
+ Flux2AutoBlocks,
1222
+ Flux2KleinAutoBlocks,
1223
+ Flux2KleinBaseAutoBlocks,
1224
+ Flux2KleinBaseModularPipeline,
1225
+ Flux2KleinModularPipeline,
1226
+ Flux2ModularPipeline,
1227
+ FluxAutoBlocks,
1228
+ FluxKontextAutoBlocks,
1229
+ FluxKontextModularPipeline,
1230
+ FluxModularPipeline,
1231
+ HeliosAutoBlocks,
1232
+ HeliosModularPipeline,
1233
+ HeliosPyramidAutoBlocks,
1234
+ HeliosPyramidDistilledAutoBlocks,
1235
+ HeliosPyramidDistilledModularPipeline,
1236
+ HeliosPyramidModularPipeline,
1237
+ QwenImageAutoBlocks,
1238
+ QwenImageEditAutoBlocks,
1239
+ QwenImageEditModularPipeline,
1240
+ QwenImageEditPlusAutoBlocks,
1241
+ QwenImageEditPlusModularPipeline,
1242
+ QwenImageLayeredAutoBlocks,
1243
+ QwenImageLayeredModularPipeline,
1244
+ QwenImageModularPipeline,
1245
+ StableDiffusionXLAutoBlocks,
1246
+ StableDiffusionXLModularPipeline,
1247
+ Wan22Blocks,
1248
+ Wan22Image2VideoBlocks,
1249
+ Wan22Image2VideoModularPipeline,
1250
+ Wan22ModularPipeline,
1251
+ WanBlocks,
1252
+ WanImage2VideoAutoBlocks,
1253
+ WanImage2VideoModularPipeline,
1254
+ WanModularPipeline,
1255
+ ZImageAutoBlocks,
1256
+ ZImageModularPipeline,
1257
+ )
1258
+ from .pipelines import (
1259
+ AllegroPipeline,
1260
+ AltDiffusionImg2ImgPipeline,
1261
+ AltDiffusionPipeline,
1262
+ AmusedImg2ImgPipeline,
1263
+ AmusedInpaintPipeline,
1264
+ AmusedPipeline,
1265
+ AnimateDiffControlNetPipeline,
1266
+ AnimateDiffPAGPipeline,
1267
+ AnimateDiffPipeline,
1268
+ AnimateDiffSDXLPipeline,
1269
+ AnimateDiffSparseControlNetPipeline,
1270
+ AnimateDiffVideoToVideoControlNetPipeline,
1271
+ AnimateDiffVideoToVideoPipeline,
1272
+ AudioLDM2Pipeline,
1273
+ AudioLDM2ProjectionModel,
1274
+ AudioLDM2UNet2DConditionModel,
1275
+ AudioLDMPipeline,
1276
+ AuraFlowPipeline,
1277
+ BriaFiboEditPipeline,
1278
+ BriaFiboPipeline,
1279
+ BriaPipeline,
1280
+ ChromaImg2ImgPipeline,
1281
+ ChromaInpaintPipeline,
1282
+ ChromaPipeline,
1283
+ ChronoEditPipeline,
1284
+ CLIPImageProjection,
1285
+ CogVideoXFunControlPipeline,
1286
+ CogVideoXImageToVideoPipeline,
1287
+ CogVideoXPipeline,
1288
+ CogVideoXVideoToVideoPipeline,
1289
+ CogView3PlusPipeline,
1290
+ CogView4ControlPipeline,
1291
+ CogView4Pipeline,
1292
+ ConsisIDPipeline,
1293
+ Cosmos2_5_PredictBasePipeline,
1294
+ Cosmos2_5_TransferPipeline,
1295
+ Cosmos2TextToImagePipeline,
1296
+ Cosmos2VideoToWorldPipeline,
1297
+ CosmosTextToWorldPipeline,
1298
+ CosmosVideoToWorldPipeline,
1299
+ CycleDiffusionPipeline,
1300
+ EasyAnimateControlPipeline,
1301
+ EasyAnimateInpaintPipeline,
1302
+ EasyAnimatePipeline,
1303
+ Flux2KleinKVPipeline,
1304
+ Flux2KleinPipeline,
1305
+ Flux2Pipeline,
1306
+ FluxControlImg2ImgPipeline,
1307
+ FluxControlInpaintPipeline,
1308
+ FluxControlNetImg2ImgPipeline,
1309
+ FluxControlNetInpaintPipeline,
1310
+ FluxControlNetPipeline,
1311
+ FluxControlPipeline,
1312
+ FluxFillPipeline,
1313
+ FluxImg2ImgPipeline,
1314
+ FluxInpaintPipeline,
1315
+ FluxKontextInpaintPipeline,
1316
+ FluxKontextPipeline,
1317
+ FluxPipeline,
1318
+ FluxPriorReduxPipeline,
1319
+ GlmImagePipeline,
1320
+ HeliosPipeline,
1321
+ HeliosPyramidPipeline,
1322
+ HiDreamImagePipeline,
1323
+ HunyuanDiTControlNetPipeline,
1324
+ HunyuanDiTPAGPipeline,
1325
+ HunyuanDiTPipeline,
1326
+ HunyuanImagePipeline,
1327
+ HunyuanImageRefinerPipeline,
1328
+ HunyuanSkyreelsImageToVideoPipeline,
1329
+ HunyuanVideo15ImageToVideoPipeline,
1330
+ HunyuanVideo15Pipeline,
1331
+ HunyuanVideoFramepackPipeline,
1332
+ HunyuanVideoImageToVideoPipeline,
1333
+ HunyuanVideoPipeline,
1334
+ I2VGenXLPipeline,
1335
+ IFImg2ImgPipeline,
1336
+ IFImg2ImgSuperResolutionPipeline,
1337
+ IFInpaintingPipeline,
1338
+ IFInpaintingSuperResolutionPipeline,
1339
+ IFPipeline,
1340
+ IFSuperResolutionPipeline,
1341
+ ImageTextPipelineOutput,
1342
+ Kandinsky3Img2ImgPipeline,
1343
+ Kandinsky3Pipeline,
1344
+ Kandinsky5I2IPipeline,
1345
+ Kandinsky5I2VPipeline,
1346
+ Kandinsky5T2IPipeline,
1347
+ Kandinsky5T2VPipeline,
1348
+ KandinskyCombinedPipeline,
1349
+ KandinskyImg2ImgCombinedPipeline,
1350
+ KandinskyImg2ImgPipeline,
1351
+ KandinskyInpaintCombinedPipeline,
1352
+ KandinskyInpaintPipeline,
1353
+ KandinskyPipeline,
1354
+ KandinskyPriorPipeline,
1355
+ KandinskyV22CombinedPipeline,
1356
+ KandinskyV22ControlnetImg2ImgPipeline,
1357
+ KandinskyV22ControlnetPipeline,
1358
+ KandinskyV22Img2ImgCombinedPipeline,
1359
+ KandinskyV22Img2ImgPipeline,
1360
+ KandinskyV22InpaintCombinedPipeline,
1361
+ KandinskyV22InpaintPipeline,
1362
+ KandinskyV22Pipeline,
1363
+ KandinskyV22PriorEmb2EmbPipeline,
1364
+ KandinskyV22PriorPipeline,
1365
+ LatentConsistencyModelImg2ImgPipeline,
1366
+ LatentConsistencyModelPipeline,
1367
+ LattePipeline,
1368
+ LDMTextToImagePipeline,
1369
+ LEditsPPPipelineStableDiffusion,
1370
+ LEditsPPPipelineStableDiffusionXL,
1371
+ LLaDA2Pipeline,
1372
+ LLaDA2PipelineOutput,
1373
+ LongCatImageEditPipeline,
1374
+ LongCatImagePipeline,
1375
+ LTX2ConditionPipeline,
1376
+ LTX2ImageToVideoPipeline,
1377
+ LTX2LatentUpsamplePipeline,
1378
+ LTX2Pipeline,
1379
+ LTXConditionPipeline,
1380
+ LTXI2VLongMultiPromptPipeline,
1381
+ LTXImageToVideoPipeline,
1382
+ LTXLatentUpsamplePipeline,
1383
+ LTXPipeline,
1384
+ LucyEditPipeline,
1385
+ Lumina2Pipeline,
1386
+ Lumina2Text2ImgPipeline,
1387
+ LuminaPipeline,
1388
+ LuminaText2ImgPipeline,
1389
+ MarigoldDepthPipeline,
1390
+ MarigoldIntrinsicsPipeline,
1391
+ MarigoldNormalsPipeline,
1392
+ MochiPipeline,
1393
+ MusicLDMPipeline,
1394
+ NucleusMoEImagePipeline,
1395
+ OmniGenPipeline,
1396
+ OvisImagePipeline,
1397
+ PaintByExamplePipeline,
1398
+ PIAPipeline,
1399
+ PixArtAlphaPipeline,
1400
+ PixArtSigmaPAGPipeline,
1401
+ PixArtSigmaPipeline,
1402
+ PRXPipeline,
1403
+ QwenImageControlNetInpaintPipeline,
1404
+ QwenImageControlNetPipeline,
1405
+ QwenImageEditInpaintPipeline,
1406
+ QwenImageEditPipeline,
1407
+ QwenImageEditPlusPipeline,
1408
+ QwenImageImg2ImgPipeline,
1409
+ QwenImageInpaintPipeline,
1410
+ QwenImageLayeredPipeline,
1411
+ QwenImagePipeline,
1412
+ ReduxImageEncoder,
1413
+ SanaControlNetPipeline,
1414
+ SanaImageToVideoPipeline,
1415
+ SanaPAGPipeline,
1416
+ SanaPipeline,
1417
+ SanaSprintImg2ImgPipeline,
1418
+ SanaSprintPipeline,
1419
+ SanaVideoPipeline,
1420
+ SemanticStableDiffusionPipeline,
1421
+ ShapEImg2ImgPipeline,
1422
+ ShapEPipeline,
1423
+ SkyReelsV2DiffusionForcingImageToVideoPipeline,
1424
+ SkyReelsV2DiffusionForcingPipeline,
1425
+ SkyReelsV2DiffusionForcingVideoToVideoPipeline,
1426
+ SkyReelsV2ImageToVideoPipeline,
1427
+ SkyReelsV2Pipeline,
1428
+ StableAudioPipeline,
1429
+ StableAudioProjectionModel,
1430
+ StableCascadeCombinedPipeline,
1431
+ StableCascadeDecoderPipeline,
1432
+ StableCascadePriorPipeline,
1433
+ StableDiffusion3ControlNetInpaintingPipeline,
1434
+ StableDiffusion3ControlNetPipeline,
1435
+ StableDiffusion3Img2ImgPipeline,
1436
+ StableDiffusion3InpaintPipeline,
1437
+ StableDiffusion3PAGImg2ImgPipeline,
1438
+ StableDiffusion3PAGPipeline,
1439
+ StableDiffusion3Pipeline,
1440
+ StableDiffusionAdapterPipeline,
1441
+ StableDiffusionAttendAndExcitePipeline,
1442
+ StableDiffusionControlNetImg2ImgPipeline,
1443
+ StableDiffusionControlNetInpaintPipeline,
1444
+ StableDiffusionControlNetPAGInpaintPipeline,
1445
+ StableDiffusionControlNetPAGPipeline,
1446
+ StableDiffusionControlNetPipeline,
1447
+ StableDiffusionControlNetXSPipeline,
1448
+ StableDiffusionDepth2ImgPipeline,
1449
+ StableDiffusionDiffEditPipeline,
1450
+ StableDiffusionGLIGENPipeline,
1451
+ StableDiffusionGLIGENTextImagePipeline,
1452
+ StableDiffusionImageVariationPipeline,
1453
+ StableDiffusionImg2ImgPipeline,
1454
+ StableDiffusionInpaintPipeline,
1455
+ StableDiffusionInpaintPipelineLegacy,
1456
+ StableDiffusionInstructPix2PixPipeline,
1457
+ StableDiffusionLatentUpscalePipeline,
1458
+ StableDiffusionLDM3DPipeline,
1459
+ StableDiffusionModelEditingPipeline,
1460
+ StableDiffusionPAGImg2ImgPipeline,
1461
+ StableDiffusionPAGInpaintPipeline,
1462
+ StableDiffusionPAGPipeline,
1463
+ StableDiffusionPanoramaPipeline,
1464
+ StableDiffusionParadigmsPipeline,
1465
+ StableDiffusionPipeline,
1466
+ StableDiffusionPipelineSafe,
1467
+ StableDiffusionPix2PixZeroPipeline,
1468
+ StableDiffusionSAGPipeline,
1469
+ StableDiffusionUpscalePipeline,
1470
+ StableDiffusionXLAdapterPipeline,
1471
+ StableDiffusionXLControlNetImg2ImgPipeline,
1472
+ StableDiffusionXLControlNetInpaintPipeline,
1473
+ StableDiffusionXLControlNetPAGImg2ImgPipeline,
1474
+ StableDiffusionXLControlNetPAGPipeline,
1475
+ StableDiffusionXLControlNetPipeline,
1476
+ StableDiffusionXLControlNetUnionImg2ImgPipeline,
1477
+ StableDiffusionXLControlNetUnionInpaintPipeline,
1478
+ StableDiffusionXLControlNetUnionPipeline,
1479
+ StableDiffusionXLControlNetXSPipeline,
1480
+ StableDiffusionXLImg2ImgPipeline,
1481
+ StableDiffusionXLInpaintPipeline,
1482
+ StableDiffusionXLInstructPix2PixPipeline,
1483
+ StableDiffusionXLPAGImg2ImgPipeline,
1484
+ StableDiffusionXLPAGInpaintPipeline,
1485
+ StableDiffusionXLPAGPipeline,
1486
+ StableDiffusionXLPipeline,
1487
+ StableUnCLIPImg2ImgPipeline,
1488
+ StableUnCLIPPipeline,
1489
+ StableVideoDiffusionPipeline,
1490
+ TextToVideoSDPipeline,
1491
+ TextToVideoZeroPipeline,
1492
+ TextToVideoZeroSDXLPipeline,
1493
+ UnCLIPImageVariationPipeline,
1494
+ UnCLIPPipeline,
1495
+ UniDiffuserModel,
1496
+ UniDiffuserPipeline,
1497
+ UniDiffuserTextDecoder,
1498
+ VersatileDiffusionDualGuidedPipeline,
1499
+ VersatileDiffusionImageVariationPipeline,
1500
+ VersatileDiffusionPipeline,
1501
+ VersatileDiffusionTextToImagePipeline,
1502
+ VideoToVideoSDPipeline,
1503
+ VisualClozeGenerationPipeline,
1504
+ VisualClozePipeline,
1505
+ VQDiffusionPipeline,
1506
+ WanAnimatePipeline,
1507
+ WanImageToVideoPipeline,
1508
+ WanPipeline,
1509
+ WanVACEPipeline,
1510
+ WanVideoToVideoPipeline,
1511
+ WuerstchenCombinedPipeline,
1512
+ WuerstchenDecoderPipeline,
1513
+ WuerstchenPriorPipeline,
1514
+ ZImageControlNetInpaintPipeline,
1515
+ ZImageControlNetPipeline,
1516
+ ZImageImg2ImgPipeline,
1517
+ ZImageInpaintPipeline,
1518
+ ZImageOmniPipeline,
1519
+ ZImagePipeline,
1520
+ )
1521
+
1522
+ try:
1523
+ if not (is_torch_available() and is_transformers_available() and is_sentencepiece_available()):
1524
+ raise OptionalDependencyNotAvailable()
1525
+ except OptionalDependencyNotAvailable:
1526
+ from .utils.dummy_torch_and_transformers_and_sentencepiece_objects import * # noqa F403
1527
+ else:
1528
+ from .pipelines import KolorsImg2ImgPipeline, KolorsPAGPipeline, KolorsPipeline
1529
+
1530
+ try:
1531
+ if not (is_torch_available() and is_transformers_available() and is_opencv_available()):
1532
+ raise OptionalDependencyNotAvailable()
1533
+ except OptionalDependencyNotAvailable:
1534
+ from .utils.dummy_torch_and_transformers_and_opencv_objects import * # noqa F403
1535
+ else:
1536
+ from .pipelines import ConsisIDPipeline
1537
+
1538
+ try:
1539
+ if not (is_torch_available() and is_transformers_available() and is_onnx_available()):
1540
+ raise OptionalDependencyNotAvailable()
1541
+ except OptionalDependencyNotAvailable:
1542
+ from .utils.dummy_torch_and_transformers_and_onnx_objects import * # noqa F403
1543
+ else:
1544
+ from .pipelines import (
1545
+ OnnxStableDiffusionImg2ImgPipeline,
1546
+ OnnxStableDiffusionInpaintPipeline,
1547
+ OnnxStableDiffusionInpaintPipelineLegacy,
1548
+ OnnxStableDiffusionPipeline,
1549
+ OnnxStableDiffusionUpscalePipeline,
1550
+ StableDiffusionOnnxPipeline,
1551
+ )
1552
+
1553
+ try:
1554
+ if not (is_torch_available() and is_librosa_available()):
1555
+ raise OptionalDependencyNotAvailable()
1556
+ except OptionalDependencyNotAvailable:
1557
+ from .utils.dummy_torch_and_librosa_objects import * # noqa F403
1558
+ else:
1559
+ from .pipelines import AudioDiffusionPipeline, Mel
1560
+
1561
+ try:
1562
+ if not (is_transformers_available() and is_torch_available() and is_note_seq_available()):
1563
+ raise OptionalDependencyNotAvailable()
1564
+ except OptionalDependencyNotAvailable:
1565
+ from .utils.dummy_transformers_and_torch_and_note_seq_objects import * # noqa F403
1566
+ else:
1567
+ from .pipelines import SpectrogramDiffusionPipeline
1568
+
1569
+ try:
1570
+ if not is_flax_available():
1571
+ raise OptionalDependencyNotAvailable()
1572
+ except OptionalDependencyNotAvailable:
1573
+ from .utils.dummy_flax_objects import * # noqa F403
1574
+ else:
1575
+ from .models.controlnets.controlnet_flax import FlaxControlNetModel
1576
+ from .models.modeling_flax_utils import FlaxModelMixin
1577
+ from .models.unets.unet_2d_condition_flax import FlaxUNet2DConditionModel
1578
+ from .models.vae_flax import FlaxAutoencoderKL
1579
+ from .pipelines import FlaxDiffusionPipeline
1580
+ from .schedulers import (
1581
+ FlaxDDIMScheduler,
1582
+ FlaxDDPMScheduler,
1583
+ FlaxDPMSolverMultistepScheduler,
1584
+ FlaxEulerDiscreteScheduler,
1585
+ FlaxKarrasVeScheduler,
1586
+ FlaxLMSDiscreteScheduler,
1587
+ FlaxPNDMScheduler,
1588
+ FlaxSchedulerMixin,
1589
+ FlaxScoreSdeVeScheduler,
1590
+ )
1591
+
1592
+ try:
1593
+ if not (is_flax_available() and is_transformers_available()):
1594
+ raise OptionalDependencyNotAvailable()
1595
+ except OptionalDependencyNotAvailable:
1596
+ from .utils.dummy_flax_and_transformers_objects import * # noqa F403
1597
+ else:
1598
+ from .pipelines import (
1599
+ FlaxStableDiffusionControlNetPipeline,
1600
+ FlaxStableDiffusionImg2ImgPipeline,
1601
+ FlaxStableDiffusionInpaintPipeline,
1602
+ FlaxStableDiffusionPipeline,
1603
+ FlaxStableDiffusionXLPipeline,
1604
+ )
1605
+
1606
+ try:
1607
+ if not (is_note_seq_available()):
1608
+ raise OptionalDependencyNotAvailable()
1609
+ except OptionalDependencyNotAvailable:
1610
+ from .utils.dummy_note_seq_objects import * # noqa F403
1611
+ else:
1612
+ from .pipelines import MidiProcessor
1613
+
1614
+ else:
1615
+ import sys
1616
+
1617
+ sys.modules[__name__] = _LazyModule(
1618
+ __name__,
1619
+ globals()["__file__"],
1620
+ _import_structure,
1621
+ module_spec=__spec__,
1622
+ extra_objects={"__version__": __version__},
1623
+ )
third_party/diffusers/src/diffusers/callbacks.py ADDED
@@ -0,0 +1,244 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any
2
+
3
+ from .configuration_utils import ConfigMixin, register_to_config
4
+ from .utils import CONFIG_NAME
5
+
6
+
7
+ class PipelineCallback(ConfigMixin):
8
+ """
9
+ Base class for all the official callbacks used in a pipeline. This class provides a structure for implementing
10
+ custom callbacks and ensures that all callbacks have a consistent interface.
11
+
12
+ Please implement the following:
13
+ `tensor_inputs`: This should return a list of tensor inputs specific to your callback. You will only be able to
14
+ include
15
+ variables listed in the `._callback_tensor_inputs` attribute of your pipeline class.
16
+ `callback_fn`: This method defines the core functionality of your callback.
17
+ """
18
+
19
+ config_name = CONFIG_NAME
20
+
21
+ @register_to_config
22
+ def __init__(self, cutoff_step_ratio=1.0, cutoff_step_index=None):
23
+ super().__init__()
24
+
25
+ if (cutoff_step_ratio is None and cutoff_step_index is None) or (
26
+ cutoff_step_ratio is not None and cutoff_step_index is not None
27
+ ):
28
+ raise ValueError("Either cutoff_step_ratio or cutoff_step_index should be provided, not both or none.")
29
+
30
+ if cutoff_step_ratio is not None and (
31
+ not isinstance(cutoff_step_ratio, float) or not (0.0 <= cutoff_step_ratio <= 1.0)
32
+ ):
33
+ raise ValueError("cutoff_step_ratio must be a float between 0.0 and 1.0.")
34
+
35
+ @property
36
+ def tensor_inputs(self) -> list[str]:
37
+ raise NotImplementedError(f"You need to set the attribute `tensor_inputs` for {self.__class__}")
38
+
39
+ def callback_fn(self, pipeline, step_index, timesteps, callback_kwargs) -> dict[str, Any]:
40
+ raise NotImplementedError(f"You need to implement the method `callback_fn` for {self.__class__}")
41
+
42
+ def __call__(self, pipeline, step_index, timestep, callback_kwargs) -> dict[str, Any]:
43
+ return self.callback_fn(pipeline, step_index, timestep, callback_kwargs)
44
+
45
+
46
+ class MultiPipelineCallbacks:
47
+ """
48
+ This class is designed to handle multiple pipeline callbacks. It accepts a list of PipelineCallback objects and
49
+ provides a unified interface for calling all of them.
50
+ """
51
+
52
+ def __init__(self, callbacks: list[PipelineCallback]):
53
+ self.callbacks = callbacks
54
+
55
+ @property
56
+ def tensor_inputs(self) -> list[str]:
57
+ return [input for callback in self.callbacks for input in callback.tensor_inputs]
58
+
59
+ def __call__(self, pipeline, step_index, timestep, callback_kwargs) -> dict[str, Any]:
60
+ """
61
+ Calls all the callbacks in order with the given arguments and returns the final callback_kwargs.
62
+ """
63
+ for callback in self.callbacks:
64
+ callback_kwargs = callback(pipeline, step_index, timestep, callback_kwargs)
65
+
66
+ return callback_kwargs
67
+
68
+
69
+ class SDCFGCutoffCallback(PipelineCallback):
70
+ """
71
+ Callback function for Stable Diffusion Pipelines. After certain number of steps (set by `cutoff_step_ratio` or
72
+ `cutoff_step_index`), this callback will disable the CFG.
73
+
74
+ Note: This callback mutates the pipeline by changing the `_guidance_scale` attribute to 0.0 after the cutoff step.
75
+ """
76
+
77
+ tensor_inputs = ["prompt_embeds"]
78
+
79
+ def callback_fn(self, pipeline, step_index, timestep, callback_kwargs) -> dict[str, Any]:
80
+ cutoff_step_ratio = self.config.cutoff_step_ratio
81
+ cutoff_step_index = self.config.cutoff_step_index
82
+
83
+ # Use cutoff_step_index if it's not None, otherwise use cutoff_step_ratio
84
+ cutoff_step = (
85
+ cutoff_step_index if cutoff_step_index is not None else int(pipeline.num_timesteps * cutoff_step_ratio)
86
+ )
87
+
88
+ if step_index == cutoff_step:
89
+ prompt_embeds = callback_kwargs[self.tensor_inputs[0]]
90
+ prompt_embeds = prompt_embeds[-1:] # "-1" denotes the embeddings for conditional text tokens.
91
+
92
+ pipeline._guidance_scale = 0.0
93
+
94
+ callback_kwargs[self.tensor_inputs[0]] = prompt_embeds
95
+ return callback_kwargs
96
+
97
+
98
+ class SDXLCFGCutoffCallback(PipelineCallback):
99
+ """
100
+ Callback function for the base Stable Diffusion XL Pipelines. After certain number of steps (set by
101
+ `cutoff_step_ratio` or `cutoff_step_index`), this callback will disable the CFG.
102
+
103
+ Note: This callback mutates the pipeline by changing the `_guidance_scale` attribute to 0.0 after the cutoff step.
104
+ """
105
+
106
+ tensor_inputs = [
107
+ "prompt_embeds",
108
+ "add_text_embeds",
109
+ "add_time_ids",
110
+ ]
111
+
112
+ def callback_fn(self, pipeline, step_index, timestep, callback_kwargs) -> dict[str, Any]:
113
+ cutoff_step_ratio = self.config.cutoff_step_ratio
114
+ cutoff_step_index = self.config.cutoff_step_index
115
+
116
+ # Use cutoff_step_index if it's not None, otherwise use cutoff_step_ratio
117
+ cutoff_step = (
118
+ cutoff_step_index if cutoff_step_index is not None else int(pipeline.num_timesteps * cutoff_step_ratio)
119
+ )
120
+
121
+ if step_index == cutoff_step:
122
+ prompt_embeds = callback_kwargs[self.tensor_inputs[0]]
123
+ prompt_embeds = prompt_embeds[-1:] # "-1" denotes the embeddings for conditional text tokens.
124
+
125
+ add_text_embeds = callback_kwargs[self.tensor_inputs[1]]
126
+ add_text_embeds = add_text_embeds[-1:] # "-1" denotes the embeddings for conditional pooled text tokens
127
+
128
+ add_time_ids = callback_kwargs[self.tensor_inputs[2]]
129
+ add_time_ids = add_time_ids[-1:] # "-1" denotes the embeddings for conditional added time vector
130
+
131
+ pipeline._guidance_scale = 0.0
132
+
133
+ callback_kwargs[self.tensor_inputs[0]] = prompt_embeds
134
+ callback_kwargs[self.tensor_inputs[1]] = add_text_embeds
135
+ callback_kwargs[self.tensor_inputs[2]] = add_time_ids
136
+
137
+ return callback_kwargs
138
+
139
+
140
+ class SDXLControlnetCFGCutoffCallback(PipelineCallback):
141
+ """
142
+ Callback function for the Controlnet Stable Diffusion XL Pipelines. After certain number of steps (set by
143
+ `cutoff_step_ratio` or `cutoff_step_index`), this callback will disable the CFG.
144
+
145
+ Note: This callback mutates the pipeline by changing the `_guidance_scale` attribute to 0.0 after the cutoff step.
146
+ """
147
+
148
+ tensor_inputs = [
149
+ "prompt_embeds",
150
+ "add_text_embeds",
151
+ "add_time_ids",
152
+ "image",
153
+ ]
154
+
155
+ def callback_fn(self, pipeline, step_index, timestep, callback_kwargs) -> dict[str, Any]:
156
+ cutoff_step_ratio = self.config.cutoff_step_ratio
157
+ cutoff_step_index = self.config.cutoff_step_index
158
+
159
+ # Use cutoff_step_index if it's not None, otherwise use cutoff_step_ratio
160
+ cutoff_step = (
161
+ cutoff_step_index if cutoff_step_index is not None else int(pipeline.num_timesteps * cutoff_step_ratio)
162
+ )
163
+
164
+ if step_index == cutoff_step:
165
+ prompt_embeds = callback_kwargs[self.tensor_inputs[0]]
166
+ prompt_embeds = prompt_embeds[-1:] # "-1" denotes the embeddings for conditional text tokens.
167
+
168
+ add_text_embeds = callback_kwargs[self.tensor_inputs[1]]
169
+ add_text_embeds = add_text_embeds[-1:] # "-1" denotes the embeddings for conditional pooled text tokens
170
+
171
+ add_time_ids = callback_kwargs[self.tensor_inputs[2]]
172
+ add_time_ids = add_time_ids[-1:] # "-1" denotes the embeddings for conditional added time vector
173
+
174
+ # For Controlnet
175
+ image = callback_kwargs[self.tensor_inputs[3]]
176
+ image = image[-1:]
177
+
178
+ pipeline._guidance_scale = 0.0
179
+
180
+ callback_kwargs[self.tensor_inputs[0]] = prompt_embeds
181
+ callback_kwargs[self.tensor_inputs[1]] = add_text_embeds
182
+ callback_kwargs[self.tensor_inputs[2]] = add_time_ids
183
+ callback_kwargs[self.tensor_inputs[3]] = image
184
+
185
+ return callback_kwargs
186
+
187
+
188
+ class IPAdapterScaleCutoffCallback(PipelineCallback):
189
+ """
190
+ Callback function for any pipeline that inherits `IPAdapterMixin`. After certain number of steps (set by
191
+ `cutoff_step_ratio` or `cutoff_step_index`), this callback will set the IP Adapter scale to `0.0`.
192
+
193
+ Note: This callback mutates the IP Adapter attention processors by setting the scale to 0.0 after the cutoff step.
194
+ """
195
+
196
+ tensor_inputs = []
197
+
198
+ def callback_fn(self, pipeline, step_index, timestep, callback_kwargs) -> dict[str, Any]:
199
+ cutoff_step_ratio = self.config.cutoff_step_ratio
200
+ cutoff_step_index = self.config.cutoff_step_index
201
+
202
+ # Use cutoff_step_index if it's not None, otherwise use cutoff_step_ratio
203
+ cutoff_step = (
204
+ cutoff_step_index if cutoff_step_index is not None else int(pipeline.num_timesteps * cutoff_step_ratio)
205
+ )
206
+
207
+ if step_index == cutoff_step:
208
+ pipeline.set_ip_adapter_scale(0.0)
209
+ return callback_kwargs
210
+
211
+
212
+ class SD3CFGCutoffCallback(PipelineCallback):
213
+ """
214
+ Callback function for Stable Diffusion 3 Pipelines. After certain number of steps (set by `cutoff_step_ratio` or
215
+ `cutoff_step_index`), this callback will disable the CFG.
216
+
217
+ Note: This callback mutates the pipeline by changing the `_guidance_scale` attribute to 0.0 after the cutoff step.
218
+ """
219
+
220
+ tensor_inputs = ["prompt_embeds", "pooled_prompt_embeds"]
221
+
222
+ def callback_fn(self, pipeline, step_index, timestep, callback_kwargs) -> dict[str, Any]:
223
+ cutoff_step_ratio = self.config.cutoff_step_ratio
224
+ cutoff_step_index = self.config.cutoff_step_index
225
+
226
+ # Use cutoff_step_index if it's not None, otherwise use cutoff_step_ratio
227
+ cutoff_step = (
228
+ cutoff_step_index if cutoff_step_index is not None else int(pipeline.num_timesteps * cutoff_step_ratio)
229
+ )
230
+
231
+ if step_index == cutoff_step:
232
+ prompt_embeds = callback_kwargs[self.tensor_inputs[0]]
233
+ prompt_embeds = prompt_embeds[-1:] # "-1" denotes the embeddings for conditional text tokens.
234
+
235
+ pooled_prompt_embeds = callback_kwargs[self.tensor_inputs[1]]
236
+ pooled_prompt_embeds = pooled_prompt_embeds[
237
+ -1:
238
+ ] # "-1" denotes the embeddings for conditional pooled text tokens.
239
+
240
+ pipeline._guidance_scale = 0.0
241
+
242
+ callback_kwargs[self.tensor_inputs[0]] = prompt_embeds
243
+ callback_kwargs[self.tensor_inputs[1]] = pooled_prompt_embeds
244
+ return callback_kwargs
third_party/diffusers/src/diffusers/configuration_utils.py ADDED
@@ -0,0 +1,805 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ # Copyright 2025 The HuggingFace Inc. team.
3
+ # Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License");
6
+ # you may not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS,
13
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ # See the License for the specific language governing permissions and
15
+ # limitations under the License.
16
+ """ConfigMixin base class and utilities."""
17
+
18
+ import dataclasses
19
+ import functools
20
+ import importlib
21
+ import inspect
22
+ import json
23
+ import os
24
+ import re
25
+ from collections import OrderedDict
26
+ from pathlib import Path
27
+ from typing import Any
28
+
29
+ import numpy as np
30
+ from huggingface_hub import DDUFEntry, create_repo, hf_hub_download
31
+ from huggingface_hub.utils import (
32
+ EntryNotFoundError,
33
+ HfHubHTTPError,
34
+ RepositoryNotFoundError,
35
+ RevisionNotFoundError,
36
+ validate_hf_hub_args,
37
+ )
38
+ from typing_extensions import Self
39
+
40
+ from . import __version__
41
+ from .utils import (
42
+ HUGGINGFACE_CO_RESOLVE_ENDPOINT,
43
+ DummyObject,
44
+ deprecate,
45
+ extract_commit_hash,
46
+ http_user_agent,
47
+ logging,
48
+ )
49
+
50
+
51
+ logger = logging.get_logger(__name__)
52
+
53
+ _re_configuration_file = re.compile(r"config\.(.*)\.json")
54
+
55
+
56
+ class FrozenDict(OrderedDict):
57
+ def __init__(self, *args, **kwargs):
58
+ super().__init__(*args, **kwargs)
59
+
60
+ for key, value in self.items():
61
+ setattr(self, key, value)
62
+
63
+ self.__frozen = True
64
+
65
+ def __delitem__(self, *args, **kwargs):
66
+ raise Exception(f"You cannot use ``__delitem__`` on a {self.__class__.__name__} instance.")
67
+
68
+ def setdefault(self, *args, **kwargs):
69
+ raise Exception(f"You cannot use ``setdefault`` on a {self.__class__.__name__} instance.")
70
+
71
+ def pop(self, *args, **kwargs):
72
+ raise Exception(f"You cannot use ``pop`` on a {self.__class__.__name__} instance.")
73
+
74
+ def update(self, *args, **kwargs):
75
+ raise Exception(f"You cannot use ``update`` on a {self.__class__.__name__} instance.")
76
+
77
+ def __setattr__(self, name, value):
78
+ if hasattr(self, "__frozen") and self.__frozen:
79
+ raise Exception(f"You cannot use ``__setattr__`` on a {self.__class__.__name__} instance.")
80
+ super().__setattr__(name, value)
81
+
82
+ def __setitem__(self, name, value):
83
+ if hasattr(self, "__frozen") and self.__frozen:
84
+ raise Exception(f"You cannot use ``__setattr__`` on a {self.__class__.__name__} instance.")
85
+ super().__setitem__(name, value)
86
+
87
+
88
+ class ConfigMixin:
89
+ r"""
90
+ Base class for all configuration classes. All configuration parameters are stored under `self.config`. Also
91
+ provides the [`~ConfigMixin.from_config`] and [`~ConfigMixin.save_config`] methods for loading, downloading, and
92
+ saving classes that inherit from [`ConfigMixin`].
93
+
94
+ Class attributes:
95
+ - **config_name** (`str`) -- A filename under which the config should stored when calling
96
+ [`~ConfigMixin.save_config`] (should be overridden by parent class).
97
+ - **ignore_for_config** (`list[str]`) -- A list of attributes that should not be saved in the config (should be
98
+ overridden by subclass).
99
+ - **has_compatibles** (`bool`) -- Whether the class has compatible classes (should be overridden by subclass).
100
+ - **_deprecated_kwargs** (`list[str]`) -- Keyword arguments that are deprecated. Note that the `init` function
101
+ should only have a `kwargs` argument if at least one argument is deprecated (should be overridden by
102
+ subclass).
103
+ """
104
+
105
+ config_name = None
106
+ ignore_for_config = []
107
+ has_compatibles = False
108
+
109
+ _deprecated_kwargs = []
110
+ _auto_class = None
111
+
112
+ @classmethod
113
+ def register_for_auto_class(cls, auto_class="AutoModel"):
114
+ """
115
+ Register this class with the given auto class so that it can be loaded with `AutoModel.from_pretrained(...,
116
+ trust_remote_code=True)`.
117
+
118
+ When the config is saved, the resulting `config.json` will include an `auto_map` entry mapping the auto class
119
+ to this class's module and class name.
120
+
121
+ Args:
122
+ auto_class (`str` or type, *optional*, defaults to `"AutoModel"`):
123
+ The auto class to register this class with. Can be a string (e.g. `"AutoModel"`) or the class itself.
124
+ Currently only `"AutoModel"` is supported.
125
+
126
+ Example:
127
+
128
+ ```python
129
+ from diffusers import ModelMixin, ConfigMixin
130
+
131
+
132
+ class MyCustomModel(ModelMixin, ConfigMixin): ...
133
+
134
+
135
+ MyCustomModel.register_for_auto_class("AutoModel")
136
+ ```
137
+ """
138
+ if auto_class != "AutoModel":
139
+ raise ValueError(f"Only 'AutoModel' is supported, got '{auto_class}'.")
140
+
141
+ cls._auto_class = auto_class
142
+
143
+ def register_to_config(self, **kwargs):
144
+ if self.config_name is None:
145
+ raise NotImplementedError(f"Make sure that {self.__class__} has defined a class name `config_name`")
146
+ # Special case for `kwargs` used in deprecation warning added to schedulers
147
+ # TODO: remove this when we remove the deprecation warning, and the `kwargs` argument,
148
+ # or solve in a more general way.
149
+ kwargs.pop("kwargs", None)
150
+
151
+ if not hasattr(self, "_internal_dict"):
152
+ internal_dict = kwargs
153
+ else:
154
+ previous_dict = dict(self._internal_dict)
155
+ internal_dict = {**self._internal_dict, **kwargs}
156
+ logger.debug(f"Updating config from {previous_dict} to {internal_dict}")
157
+
158
+ self._internal_dict = FrozenDict(internal_dict)
159
+
160
+ def __getattr__(self, name: str) -> Any:
161
+ """The only reason we overwrite `getattr` here is to gracefully deprecate accessing
162
+ config attributes directly. See https://github.com/huggingface/diffusers/pull/3129
163
+
164
+ This function is mostly copied from PyTorch's __getattr__ overwrite:
165
+ https://pytorch.org/docs/stable/_modules/torch/nn/modules/module.html#Module
166
+ """
167
+
168
+ is_in_config = "_internal_dict" in self.__dict__ and hasattr(self.__dict__["_internal_dict"], name)
169
+ is_attribute = name in self.__dict__
170
+
171
+ if is_in_config and not is_attribute:
172
+ deprecation_message = f"Accessing config attribute `{name}` directly via '{type(self).__name__}' object attribute is deprecated. Please access '{name}' over '{type(self).__name__}'s config object instead, e.g. 'scheduler.config.{name}'."
173
+ deprecate("direct config name access", "1.0.0", deprecation_message, standard_warn=False)
174
+ return self._internal_dict[name]
175
+
176
+ raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")
177
+
178
+ def save_config(self, save_directory: str | os.PathLike, push_to_hub: bool = False, **kwargs):
179
+ """
180
+ Save a configuration object to the directory specified in `save_directory` so that it can be reloaded using the
181
+ [`~ConfigMixin.from_config`] class method.
182
+
183
+ Args:
184
+ save_directory (`str` or `os.PathLike`):
185
+ Directory where the configuration JSON file is saved (will be created if it does not exist).
186
+ push_to_hub (`bool`, *optional*, defaults to `False`):
187
+ Whether or not to push your model to the Hugging Face Hub after saving it. You can specify the
188
+ repository you want to push to with `repo_id` (will default to the name of `save_directory` in your
189
+ namespace).
190
+ kwargs (`dict[str, Any]`, *optional*):
191
+ Additional keyword arguments passed along to the [`~utils.PushToHubMixin.push_to_hub`] method.
192
+ """
193
+ if os.path.isfile(save_directory):
194
+ raise AssertionError(f"Provided path ({save_directory}) should be a directory, not a file")
195
+
196
+ os.makedirs(save_directory, exist_ok=True)
197
+
198
+ # If we save using the predefined names, we can load using `from_config`
199
+ output_config_file = os.path.join(save_directory, self.config_name)
200
+
201
+ self.to_json_file(output_config_file)
202
+ logger.info(f"Configuration saved in {output_config_file}")
203
+
204
+ if push_to_hub:
205
+ commit_message = kwargs.pop("commit_message", None)
206
+ private = kwargs.pop("private", None)
207
+ create_pr = kwargs.pop("create_pr", False)
208
+ token = kwargs.pop("token", None)
209
+ repo_id = kwargs.pop("repo_id", save_directory.split(os.path.sep)[-1])
210
+ repo_id = create_repo(repo_id, exist_ok=True, private=private, token=token).repo_id
211
+ subfolder = kwargs.pop("subfolder", None)
212
+
213
+ self._upload_folder(
214
+ save_directory,
215
+ repo_id,
216
+ token=token,
217
+ commit_message=commit_message,
218
+ create_pr=create_pr,
219
+ subfolder=subfolder,
220
+ )
221
+
222
+ @classmethod
223
+ def from_config(
224
+ cls, config: FrozenDict | dict[str, Any] = None, return_unused_kwargs=False, **kwargs
225
+ ) -> Self | tuple[Self, dict[str, Any]]:
226
+ r"""
227
+ Instantiate a Python class from a config dictionary.
228
+
229
+ Parameters:
230
+ config (`dict[str, Any]`):
231
+ A config dictionary from which the Python class is instantiated. Make sure to only load configuration
232
+ files of compatible classes.
233
+ return_unused_kwargs (`bool`, *optional*, defaults to `False`):
234
+ Whether kwargs that are not consumed by the Python class should be returned or not.
235
+ kwargs (remaining dictionary of keyword arguments, *optional*):
236
+ Can be used to update the configuration object (after it is loaded) and initiate the Python class.
237
+ `**kwargs` are passed directly to the underlying scheduler/model's `__init__` method and eventually
238
+ overwrite the same named arguments in `config`.
239
+
240
+ Returns:
241
+ [`ModelMixin`] or [`SchedulerMixin`]:
242
+ A model or scheduler object instantiated from a config dictionary.
243
+
244
+ Examples:
245
+
246
+ ```python
247
+ >>> from diffusers import DDPMScheduler, DDIMScheduler, PNDMScheduler
248
+
249
+ >>> # Download scheduler from huggingface.co and cache.
250
+ >>> scheduler = DDPMScheduler.from_pretrained("google/ddpm-cifar10-32")
251
+
252
+ >>> # Instantiate DDIM scheduler class with same config as DDPM
253
+ >>> scheduler = DDIMScheduler.from_config(scheduler.config)
254
+
255
+ >>> # Instantiate PNDM scheduler class with same config as DDPM
256
+ >>> scheduler = PNDMScheduler.from_config(scheduler.config)
257
+ ```
258
+ """
259
+ # <===== TO BE REMOVED WITH DEPRECATION
260
+ # TODO(Patrick) - make sure to remove the following lines when config=="model_path" is deprecated
261
+ if "pretrained_model_name_or_path" in kwargs:
262
+ config = kwargs.pop("pretrained_model_name_or_path")
263
+
264
+ if config is None:
265
+ raise ValueError("Please make sure to provide a config as the first positional argument.")
266
+ # ======>
267
+
268
+ if not isinstance(config, dict):
269
+ deprecation_message = "It is deprecated to pass a pretrained model name or path to `from_config`."
270
+ if "Scheduler" in cls.__name__:
271
+ deprecation_message += (
272
+ f"If you were trying to load a scheduler, please use {cls}.from_pretrained(...) instead."
273
+ " Otherwise, please make sure to pass a configuration dictionary instead. This functionality will"
274
+ " be removed in v1.0.0."
275
+ )
276
+ elif "Model" in cls.__name__:
277
+ deprecation_message += (
278
+ f"If you were trying to load a model, please use {cls}.load_config(...) followed by"
279
+ f" {cls}.from_config(...) instead. Otherwise, please make sure to pass a configuration dictionary"
280
+ " instead. This functionality will be removed in v1.0.0."
281
+ )
282
+ deprecate("config-passed-as-path", "1.0.0", deprecation_message, standard_warn=False)
283
+ config, kwargs = cls.load_config(pretrained_model_name_or_path=config, return_unused_kwargs=True, **kwargs)
284
+
285
+ init_dict, unused_kwargs, hidden_dict = cls.extract_init_dict(config, **kwargs)
286
+
287
+ # Allow dtype to be specified on initialization
288
+ if "dtype" in unused_kwargs:
289
+ init_dict["dtype"] = unused_kwargs.pop("dtype")
290
+
291
+ # add possible deprecated kwargs
292
+ for deprecated_kwarg in cls._deprecated_kwargs:
293
+ if deprecated_kwarg in unused_kwargs:
294
+ init_dict[deprecated_kwarg] = unused_kwargs.pop(deprecated_kwarg)
295
+
296
+ # Return model and optionally state and/or unused_kwargs
297
+ model = cls(**init_dict)
298
+
299
+ # make sure to also save config parameters that might be used for compatible classes
300
+ # update _class_name
301
+ if "_class_name" in hidden_dict:
302
+ hidden_dict["_class_name"] = cls.__name__
303
+
304
+ model.register_to_config(**hidden_dict)
305
+
306
+ # add hidden kwargs of compatible classes to unused_kwargs
307
+ unused_kwargs = {**unused_kwargs, **hidden_dict}
308
+
309
+ if return_unused_kwargs:
310
+ return (model, unused_kwargs)
311
+ else:
312
+ return model
313
+
314
+ @classmethod
315
+ def get_config_dict(cls, *args, **kwargs):
316
+ deprecation_message = (
317
+ f" The function get_config_dict is deprecated. Please use {cls}.load_config instead. This function will be"
318
+ " removed in version v1.0.0"
319
+ )
320
+ deprecate("get_config_dict", "1.0.0", deprecation_message, standard_warn=False)
321
+ return cls.load_config(*args, **kwargs)
322
+
323
+ @classmethod
324
+ @validate_hf_hub_args
325
+ def load_config(
326
+ cls,
327
+ pretrained_model_name_or_path: str | os.PathLike,
328
+ return_unused_kwargs=False,
329
+ return_commit_hash=False,
330
+ **kwargs,
331
+ ) -> tuple[dict[str, Any], dict[str, Any]]:
332
+ r"""
333
+ Load a model or scheduler configuration.
334
+
335
+ Parameters:
336
+ pretrained_model_name_or_path (`str` or `os.PathLike`, *optional*):
337
+ Can be either:
338
+
339
+ - A string, the *model id* (for example `google/ddpm-celebahq-256`) of a pretrained model hosted on
340
+ the Hub.
341
+ - A path to a *directory* (for example `./my_model_directory`) containing model weights saved with
342
+ [`~ConfigMixin.save_config`].
343
+
344
+ cache_dir (`str | os.PathLike`, *optional*):
345
+ Path to a directory where a downloaded pretrained model configuration is cached if the standard cache
346
+ is not used.
347
+ force_download (`bool`, *optional*, defaults to `False`):
348
+ Whether or not to force the (re-)download of the model weights and configuration files, overriding the
349
+ cached versions if they exist.
350
+ proxies (`dict[str, str]`, *optional*):
351
+ A dictionary of proxy servers to use by protocol or endpoint, for example, `{'http': 'foo.bar:3128',
352
+ 'http://hostname': 'foo.bar:4012'}`. The proxies are used on each request.
353
+ output_loading_info(`bool`, *optional*, defaults to `False`):
354
+ Whether or not to also return a dictionary containing missing keys, unexpected keys and error messages.
355
+ local_files_only (`bool`, *optional*, defaults to `False`):
356
+ Whether to only load local model weights and configuration files or not. If set to `True`, the model
357
+ won't be downloaded from the Hub.
358
+ token (`str` or *bool*, *optional*):
359
+ The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
360
+ `diffusers-cli login` (stored in `~/.huggingface`) is used.
361
+ revision (`str`, *optional*, defaults to `"main"`):
362
+ The specific model version to use. It can be a branch name, a tag name, a commit id, or any identifier
363
+ allowed by Git.
364
+ subfolder (`str`, *optional*, defaults to `""`):
365
+ The subfolder location of a model file within a larger model repository on the Hub or locally.
366
+ return_unused_kwargs (`bool`, *optional*, defaults to `False):
367
+ Whether unused keyword arguments of the config are returned.
368
+ return_commit_hash (`bool`, *optional*, defaults to `False):
369
+ Whether the `commit_hash` of the loaded configuration are returned.
370
+
371
+ Returns:
372
+ `dict`:
373
+ A dictionary of all the parameters stored in a JSON configuration file.
374
+
375
+ """
376
+ cache_dir = kwargs.pop("cache_dir", None)
377
+ local_dir = kwargs.pop("local_dir", None)
378
+ local_dir_use_symlinks = kwargs.pop("local_dir_use_symlinks", "auto")
379
+ force_download = kwargs.pop("force_download", False)
380
+ proxies = kwargs.pop("proxies", None)
381
+ token = kwargs.pop("token", None)
382
+ local_files_only = kwargs.pop("local_files_only", False)
383
+ revision = kwargs.pop("revision", None)
384
+ _ = kwargs.pop("mirror", None)
385
+ subfolder = kwargs.pop("subfolder", None)
386
+ user_agent = kwargs.pop("user_agent", {})
387
+ dduf_entries: dict[str, DDUFEntry] | None = kwargs.pop("dduf_entries", None)
388
+
389
+ user_agent = {**user_agent, "file_type": "config"}
390
+ user_agent = http_user_agent(user_agent)
391
+
392
+ pretrained_model_name_or_path = str(pretrained_model_name_or_path)
393
+
394
+ if cls.config_name is None:
395
+ raise ValueError(
396
+ "`self.config_name` is not defined. Note that one should not load a config from "
397
+ "`ConfigMixin`. Please make sure to define `config_name` in a class inheriting from `ConfigMixin`"
398
+ )
399
+ # Custom path for now
400
+ if dduf_entries:
401
+ if subfolder is not None:
402
+ raise ValueError(
403
+ "DDUF file only allow for 1 level of directory (e.g transformer/model1/model.safetentors is not allowed). "
404
+ "Please check the DDUF structure"
405
+ )
406
+ config_file = cls._get_config_file_from_dduf(pretrained_model_name_or_path, dduf_entries)
407
+ elif os.path.isfile(pretrained_model_name_or_path):
408
+ config_file = pretrained_model_name_or_path
409
+ elif os.path.isdir(pretrained_model_name_or_path):
410
+ if subfolder is not None and os.path.isfile(
411
+ os.path.join(pretrained_model_name_or_path, subfolder, cls.config_name)
412
+ ):
413
+ config_file = os.path.join(pretrained_model_name_or_path, subfolder, cls.config_name)
414
+ elif os.path.isfile(os.path.join(pretrained_model_name_or_path, cls.config_name)):
415
+ # Load from a PyTorch checkpoint
416
+ config_file = os.path.join(pretrained_model_name_or_path, cls.config_name)
417
+ else:
418
+ raise EnvironmentError(
419
+ f"Error no file named {cls.config_name} found in directory {pretrained_model_name_or_path}."
420
+ )
421
+ else:
422
+ try:
423
+ # Load from URL or cache if already cached
424
+ config_file = hf_hub_download(
425
+ pretrained_model_name_or_path,
426
+ filename=cls.config_name,
427
+ cache_dir=cache_dir,
428
+ force_download=force_download,
429
+ proxies=proxies,
430
+ local_files_only=local_files_only,
431
+ token=token,
432
+ user_agent=user_agent,
433
+ subfolder=subfolder,
434
+ revision=revision,
435
+ local_dir=local_dir,
436
+ local_dir_use_symlinks=local_dir_use_symlinks,
437
+ )
438
+ except RepositoryNotFoundError:
439
+ raise EnvironmentError(
440
+ f"{pretrained_model_name_or_path} is not a local folder and is not a valid model identifier"
441
+ " listed on 'https://huggingface.co/models'\nIf this is a private repository, make sure to pass a"
442
+ " token having permission to this repo with `token` or log in with `hf auth login`."
443
+ )
444
+ except RevisionNotFoundError:
445
+ raise EnvironmentError(
446
+ f"{revision} is not a valid git identifier (branch name, tag name or commit id) that exists for"
447
+ " this model name. Check the model page at"
448
+ f" 'https://huggingface.co/{pretrained_model_name_or_path}' for available revisions."
449
+ )
450
+ except EntryNotFoundError:
451
+ raise EnvironmentError(
452
+ f"{pretrained_model_name_or_path} does not appear to have a file named {cls.config_name}."
453
+ )
454
+ except HfHubHTTPError as err:
455
+ raise EnvironmentError(
456
+ "There was a specific connection error when trying to load"
457
+ f" {pretrained_model_name_or_path}:\n{err}"
458
+ )
459
+ except ValueError:
460
+ raise EnvironmentError(
461
+ f"We couldn't connect to '{HUGGINGFACE_CO_RESOLVE_ENDPOINT}' to load this model, couldn't find it"
462
+ f" in the cached files and it looks like {pretrained_model_name_or_path} is not the path to a"
463
+ f" directory containing a {cls.config_name} file.\nCheckout your internet connection or see how to"
464
+ " run the library in offline mode at"
465
+ " 'https://huggingface.co/docs/diffusers/installation#offline-mode'."
466
+ )
467
+ except EnvironmentError:
468
+ raise EnvironmentError(
469
+ f"Can't load config for '{pretrained_model_name_or_path}'. If you were trying to load it from "
470
+ "'https://huggingface.co/models', make sure you don't have a local directory with the same name. "
471
+ f"Otherwise, make sure '{pretrained_model_name_or_path}' is the correct path to a directory "
472
+ f"containing a {cls.config_name} file"
473
+ )
474
+ try:
475
+ config_dict = cls._dict_from_json_file(config_file, dduf_entries=dduf_entries)
476
+
477
+ commit_hash = extract_commit_hash(config_file)
478
+ except (json.JSONDecodeError, UnicodeDecodeError):
479
+ raise EnvironmentError(f"It looks like the config file at '{config_file}' is not a valid JSON file.")
480
+
481
+ if not (return_unused_kwargs or return_commit_hash):
482
+ return config_dict
483
+
484
+ outputs = (config_dict,)
485
+
486
+ if return_unused_kwargs:
487
+ outputs += (kwargs,)
488
+
489
+ if return_commit_hash:
490
+ outputs += (commit_hash,)
491
+
492
+ return outputs
493
+
494
+ @staticmethod
495
+ def _get_init_keys(input_class):
496
+ return set(dict(inspect.signature(input_class.__init__).parameters).keys())
497
+
498
+ @classmethod
499
+ def extract_init_dict(cls, config_dict, **kwargs):
500
+ # Skip keys that were not present in the original config, so default __init__ values were used
501
+ used_defaults = config_dict.get("_use_default_values", [])
502
+ config_dict = {k: v for k, v in config_dict.items() if k not in used_defaults and k != "_use_default_values"}
503
+
504
+ # 0. Copy origin config dict
505
+ original_dict = dict(config_dict.items())
506
+
507
+ # 1. Retrieve expected config attributes from __init__ signature
508
+ expected_keys = cls._get_init_keys(cls)
509
+ expected_keys.remove("self")
510
+ # remove general kwargs if present in dict
511
+ if "kwargs" in expected_keys:
512
+ expected_keys.remove("kwargs")
513
+ # remove flax internal keys
514
+ if hasattr(cls, "_flax_internal_args"):
515
+ for arg in cls._flax_internal_args:
516
+ expected_keys.remove(arg)
517
+
518
+ # 2. Remove attributes that cannot be expected from expected config attributes
519
+ # remove keys to be ignored
520
+ if len(cls.ignore_for_config) > 0:
521
+ expected_keys = expected_keys - set(cls.ignore_for_config)
522
+
523
+ # load diffusers library to import compatible and original scheduler
524
+ diffusers_library = importlib.import_module(__name__.split(".")[0])
525
+
526
+ if cls.has_compatibles:
527
+ compatible_classes = [c for c in cls._get_compatibles() if not isinstance(c, DummyObject)]
528
+ else:
529
+ compatible_classes = []
530
+
531
+ expected_keys_comp_cls = set()
532
+ for c in compatible_classes:
533
+ expected_keys_c = cls._get_init_keys(c)
534
+ expected_keys_comp_cls = expected_keys_comp_cls.union(expected_keys_c)
535
+ expected_keys_comp_cls = expected_keys_comp_cls - cls._get_init_keys(cls)
536
+ config_dict = {k: v for k, v in config_dict.items() if k not in expected_keys_comp_cls}
537
+
538
+ # remove attributes from orig class that cannot be expected
539
+ orig_cls_name = config_dict.pop("_class_name", cls.__name__)
540
+ if (
541
+ isinstance(orig_cls_name, str)
542
+ and orig_cls_name != cls.__name__
543
+ and hasattr(diffusers_library, orig_cls_name)
544
+ ):
545
+ orig_cls = getattr(diffusers_library, orig_cls_name)
546
+ unexpected_keys_from_orig = cls._get_init_keys(orig_cls) - expected_keys
547
+ config_dict = {k: v for k, v in config_dict.items() if k not in unexpected_keys_from_orig}
548
+ elif not isinstance(orig_cls_name, str) and not isinstance(orig_cls_name, (list, tuple)):
549
+ raise ValueError(
550
+ "Make sure that the `_class_name` is of type string or list of string (for custom pipelines)."
551
+ )
552
+
553
+ # remove private attributes
554
+ config_dict = {k: v for k, v in config_dict.items() if not k.startswith("_")}
555
+
556
+ # remove quantization_config
557
+ config_dict = {k: v for k, v in config_dict.items() if k != "quantization_config"}
558
+
559
+ # 3. Create keyword arguments that will be passed to __init__ from expected keyword arguments
560
+ init_dict = {}
561
+ for key in expected_keys:
562
+ # if config param is passed to kwarg and is present in config dict
563
+ # it should overwrite existing config dict key
564
+ if key in kwargs and key in config_dict:
565
+ config_dict[key] = kwargs.pop(key)
566
+
567
+ if key in kwargs:
568
+ # overwrite key
569
+ init_dict[key] = kwargs.pop(key)
570
+ elif key in config_dict:
571
+ # use value from config dict
572
+ init_dict[key] = config_dict.pop(key)
573
+
574
+ # 4. Give nice warning if unexpected values have been passed
575
+ if len(config_dict) > 0:
576
+ logger.warning(
577
+ f"The config attributes {config_dict} were passed to {cls.__name__}, "
578
+ "but are not expected and will be ignored. Please verify your "
579
+ f"{cls.config_name} configuration file."
580
+ )
581
+
582
+ # 5. Give nice info if config attributes are initialized to default because they have not been passed
583
+ passed_keys = set(init_dict.keys())
584
+ if len(expected_keys - passed_keys) > 0:
585
+ logger.info(
586
+ f"{expected_keys - passed_keys} was not found in config. Values will be initialized to default values."
587
+ )
588
+
589
+ # 6. Define unused keyword arguments
590
+ unused_kwargs = {**config_dict, **kwargs}
591
+
592
+ # 7. Define "hidden" config parameters that were saved for compatible classes
593
+ hidden_config_dict = {k: v for k, v in original_dict.items() if k not in init_dict}
594
+
595
+ return init_dict, unused_kwargs, hidden_config_dict
596
+
597
+ @classmethod
598
+ def _dict_from_json_file(cls, json_file: str | os.PathLike, dduf_entries: dict[str, DDUFEntry] | None = None):
599
+ if dduf_entries:
600
+ text = dduf_entries[json_file].read_text()
601
+ else:
602
+ with open(json_file, "r", encoding="utf-8") as reader:
603
+ text = reader.read()
604
+ return json.loads(text)
605
+
606
+ def __repr__(self):
607
+ return f"{self.__class__.__name__} {self.to_json_string()}"
608
+
609
+ @property
610
+ def config(self) -> dict[str, Any]:
611
+ """
612
+ Returns the config of the class as a frozen dictionary
613
+
614
+ Returns:
615
+ `dict[str, Any]`: Config of the class.
616
+ """
617
+ return self._internal_dict
618
+
619
+ def to_json_string(self) -> str:
620
+ """
621
+ Serializes the configuration instance to a JSON string.
622
+
623
+ Returns:
624
+ `str`:
625
+ String containing all the attributes that make up the configuration instance in JSON format.
626
+ """
627
+ config_dict = self._internal_dict if hasattr(self, "_internal_dict") else {}
628
+ config_dict["_class_name"] = self.__class__.__name__
629
+ config_dict["_diffusers_version"] = __version__
630
+
631
+ def to_json_saveable(value):
632
+ if isinstance(value, np.ndarray):
633
+ value = value.tolist()
634
+ elif isinstance(value, Path):
635
+ value = value.as_posix()
636
+ elif hasattr(value, "to_dict") and callable(value.to_dict):
637
+ value = value.to_dict()
638
+ elif isinstance(value, list):
639
+ value = [to_json_saveable(v) for v in value]
640
+ return value
641
+
642
+ if "quantization_config" in config_dict:
643
+ config_dict["quantization_config"] = (
644
+ config_dict.quantization_config.to_dict()
645
+ if not isinstance(config_dict.quantization_config, dict)
646
+ else config_dict.quantization_config
647
+ )
648
+
649
+ config_dict = {k: to_json_saveable(v) for k, v in config_dict.items()}
650
+ # Don't save "_ignore_files" or "_use_default_values"
651
+ config_dict.pop("_ignore_files", None)
652
+ config_dict.pop("_use_default_values", None)
653
+ # pop the `_pre_quantization_dtype` as torch.dtypes are not serializable.
654
+ _ = config_dict.pop("_pre_quantization_dtype", None)
655
+
656
+ if getattr(self, "_auto_class", None) is not None:
657
+ module = self.__class__.__module__.split(".")[-1]
658
+ auto_map = config_dict.get("auto_map", {})
659
+ auto_map[self._auto_class] = f"{module}.{self.__class__.__name__}"
660
+ config_dict["auto_map"] = auto_map
661
+
662
+ return json.dumps(config_dict, indent=2, sort_keys=True) + "\n"
663
+
664
+ def to_json_file(self, json_file_path: str | os.PathLike):
665
+ """
666
+ Save the configuration instance's parameters to a JSON file.
667
+
668
+ Args:
669
+ json_file_path (`str` or `os.PathLike`):
670
+ Path to the JSON file to save a configuration instance's parameters.
671
+ """
672
+ with open(json_file_path, "w", encoding="utf-8") as writer:
673
+ writer.write(self.to_json_string())
674
+
675
+ @classmethod
676
+ def _get_config_file_from_dduf(cls, pretrained_model_name_or_path: str, dduf_entries: dict[str, DDUFEntry]):
677
+ # paths inside a DDUF file must always be "/"
678
+ config_file = (
679
+ cls.config_name
680
+ if pretrained_model_name_or_path == ""
681
+ else "/".join([pretrained_model_name_or_path, cls.config_name])
682
+ )
683
+ if config_file not in dduf_entries:
684
+ raise ValueError(
685
+ f"We did not manage to find the file {config_file} in the dduf file. We only have the following files {dduf_entries.keys()}"
686
+ )
687
+ return config_file
688
+
689
+
690
+ def register_to_config(init):
691
+ r"""
692
+ Decorator to apply on the init of classes inheriting from [`ConfigMixin`] so that all the arguments are
693
+ automatically sent to `self.register_for_config`. To ignore a specific argument accepted by the init but that
694
+ shouldn't be registered in the config, use the `ignore_for_config` class variable
695
+
696
+ Warning: Once decorated, all private arguments (beginning with an underscore) are trashed and not sent to the init!
697
+ """
698
+
699
+ @functools.wraps(init)
700
+ def inner_init(self, *args, **kwargs):
701
+ # Ignore private kwargs in the init.
702
+ init_kwargs = {k: v for k, v in kwargs.items() if not k.startswith("_")}
703
+ config_init_kwargs = {k: v for k, v in kwargs.items() if k.startswith("_")}
704
+ if not isinstance(self, ConfigMixin):
705
+ raise RuntimeError(
706
+ f"`@register_for_config` was applied to {self.__class__.__name__} init method, but this class does "
707
+ "not inherit from `ConfigMixin`."
708
+ )
709
+
710
+ ignore = getattr(self, "ignore_for_config", [])
711
+ # Get positional arguments aligned with kwargs
712
+ new_kwargs = {}
713
+ signature = inspect.signature(init)
714
+ parameters = {
715
+ name: p.default for i, (name, p) in enumerate(signature.parameters.items()) if i > 0 and name not in ignore
716
+ }
717
+ for arg, name in zip(args, parameters.keys()):
718
+ new_kwargs[name] = arg
719
+
720
+ # Then add all kwargs
721
+ new_kwargs.update(
722
+ {
723
+ k: init_kwargs.get(k, default)
724
+ for k, default in parameters.items()
725
+ if k not in ignore and k not in new_kwargs
726
+ }
727
+ )
728
+
729
+ # Take note of the parameters that were not present in the loaded config
730
+ if len(set(new_kwargs.keys()) - set(init_kwargs)) > 0:
731
+ new_kwargs["_use_default_values"] = list(set(new_kwargs.keys()) - set(init_kwargs))
732
+
733
+ new_kwargs = {**config_init_kwargs, **new_kwargs}
734
+ getattr(self, "register_to_config")(**new_kwargs)
735
+ init(self, *args, **init_kwargs)
736
+
737
+ return inner_init
738
+
739
+
740
+ def flax_register_to_config(cls):
741
+ original_init = cls.__init__
742
+
743
+ @functools.wraps(original_init)
744
+ def init(self, *args, **kwargs):
745
+ if not isinstance(self, ConfigMixin):
746
+ raise RuntimeError(
747
+ f"`@register_for_config` was applied to {self.__class__.__name__} init method, but this class does "
748
+ "not inherit from `ConfigMixin`."
749
+ )
750
+
751
+ # Ignore private kwargs in the init. Retrieve all passed attributes
752
+ init_kwargs = dict(kwargs.items())
753
+
754
+ # Retrieve default values
755
+ fields = dataclasses.fields(self)
756
+ default_kwargs = {}
757
+ for field in fields:
758
+ # ignore flax specific attributes
759
+ if field.name in self._flax_internal_args:
760
+ continue
761
+ if type(field.default) == dataclasses._MISSING_TYPE:
762
+ default_kwargs[field.name] = None
763
+ else:
764
+ default_kwargs[field.name] = getattr(self, field.name)
765
+
766
+ # Make sure init_kwargs override default kwargs
767
+ new_kwargs = {**default_kwargs, **init_kwargs}
768
+ # dtype should be part of `init_kwargs`, but not `new_kwargs`
769
+ if "dtype" in new_kwargs:
770
+ new_kwargs.pop("dtype")
771
+
772
+ # Get positional arguments aligned with kwargs
773
+ for i, arg in enumerate(args):
774
+ name = fields[i].name
775
+ new_kwargs[name] = arg
776
+
777
+ # Take note of the parameters that were not present in the loaded config
778
+ if len(set(new_kwargs.keys()) - set(init_kwargs)) > 0:
779
+ new_kwargs["_use_default_values"] = list(set(new_kwargs.keys()) - set(init_kwargs))
780
+
781
+ getattr(self, "register_to_config")(**new_kwargs)
782
+ original_init(self, *args, **kwargs)
783
+
784
+ cls.__init__ = init
785
+ return cls
786
+
787
+
788
+ class LegacyConfigMixin(ConfigMixin):
789
+ r"""
790
+ A subclass of `ConfigMixin` to resolve class mapping from legacy classes (like `Transformer2DModel`) to more
791
+ pipeline-specific classes (like `DiTTransformer2DModel`).
792
+ """
793
+
794
+ @classmethod
795
+ def from_config(cls, config: FrozenDict | dict[str, Any] = None, return_unused_kwargs=False, **kwargs):
796
+ # To prevent dependency import problem.
797
+ from .models.model_loading_utils import _fetch_remapped_cls_from_config
798
+
799
+ # resolve remapping
800
+ remapped_class = _fetch_remapped_cls_from_config(config, cls)
801
+
802
+ if remapped_class is cls:
803
+ return super(LegacyConfigMixin, remapped_class).from_config(config, return_unused_kwargs, **kwargs)
804
+ else:
805
+ return remapped_class.from_config(config, return_unused_kwargs, **kwargs)
third_party/diffusers/src/diffusers/dependency_versions_check.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from .dependency_versions_table import deps
16
+ from .utils.versions import require_version, require_version_core
17
+
18
+
19
+ # define which module versions we always want to check at run time
20
+ # (usually the ones defined in `install_requires` in setup.py)
21
+ #
22
+ # order specific notes:
23
+ # - tqdm must be checked before tokenizers
24
+
25
+ pkgs_to_check_at_runtime = "python requests filelock numpy".split()
26
+ for pkg in pkgs_to_check_at_runtime:
27
+ if pkg in deps:
28
+ require_version_core(deps[pkg])
29
+ else:
30
+ raise ValueError(f"can't find {pkg} in {deps.keys()}, check dependency_versions_table.py")
31
+
32
+
33
+ def dep_version_check(pkg, hint=None):
34
+ require_version(deps[pkg], hint)
third_party/diffusers/src/diffusers/dependency_versions_table.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # THIS FILE HAS BEEN AUTOGENERATED. To update:
2
+ # 1. modify the `_deps` dict in setup.py
3
+ # 2. run `make deps_table_update`
4
+ deps = {
5
+ "Pillow": "Pillow",
6
+ "accelerate": "accelerate>=0.31.0",
7
+ "compel": "compel==0.1.8",
8
+ "datasets": "datasets",
9
+ "filelock": "filelock",
10
+ "flax": "flax>=0.4.1",
11
+ "ftfy": "ftfy",
12
+ "hf-doc-builder": "hf-doc-builder>=0.3.0",
13
+ "httpx": "httpx<1.0.0",
14
+ "huggingface-hub": "huggingface-hub>=0.34.0,<2.0",
15
+ "requests-mock": "requests-mock==1.10.0",
16
+ "importlib_metadata": "importlib_metadata",
17
+ "invisible-watermark": "invisible-watermark>=0.2.0",
18
+ "isort": "isort>=5.5.4",
19
+ "jax": "jax>=0.4.1",
20
+ "jaxlib": "jaxlib>=0.4.1",
21
+ "Jinja2": "Jinja2",
22
+ "torchsde": "torchsde",
23
+ "note_seq": "note_seq",
24
+ "librosa": "librosa",
25
+ "numpy": "numpy",
26
+ "parameterized": "parameterized",
27
+ "peft": "peft>=0.17.0",
28
+ "protobuf": "protobuf>=3.20.3,<4",
29
+ "pytest": "pytest",
30
+ "pytest-timeout": "pytest-timeout",
31
+ "pytest-xdist": "pytest-xdist",
32
+ "python": "python>=3.10.0",
33
+ "ruff": "ruff==0.9.10",
34
+ "safetensors": "safetensors>=0.3.1",
35
+ "sentencepiece": "sentencepiece>=0.1.91,!=0.1.92",
36
+ "GitPython": "GitPython<3.1.19",
37
+ "scipy": "scipy",
38
+ "onnx": "onnx",
39
+ "optimum_quanto": "optimum_quanto>=0.2.6",
40
+ "gguf": "gguf>=0.10.0",
41
+ "torchao": "torchao>=0.7.0",
42
+ "bitsandbytes": "bitsandbytes>=0.43.3",
43
+ "nvidia_modelopt[hf]": "nvidia_modelopt[hf]>=0.33.1",
44
+ "regex": "regex!=2019.12.17",
45
+ "requests": "requests",
46
+ "tensorboard": "tensorboard",
47
+ "tiktoken": "tiktoken>=0.7.0",
48
+ "torch": "torch>=1.4",
49
+ "torchvision": "torchvision",
50
+ "transformers": "transformers>=4.41.2",
51
+ "urllib3": "urllib3<=2.0.0",
52
+ "black": "black",
53
+ "phonemizer": "phonemizer",
54
+ "opencv-python": "opencv-python",
55
+ "timm": "timm",
56
+ }
third_party/diffusers/src/diffusers/hooks/__init__.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from ..utils import is_torch_available
16
+
17
+
18
+ if is_torch_available():
19
+ from .context_parallel import apply_context_parallel
20
+ from .faster_cache import FasterCacheConfig, apply_faster_cache
21
+ from .first_block_cache import FirstBlockCacheConfig, apply_first_block_cache
22
+ from .group_offloading import apply_group_offloading
23
+ from .hooks import HookRegistry, ModelHook
24
+ from .layer_skip import LayerSkipConfig, apply_layer_skip
25
+ from .layerwise_casting import apply_layerwise_casting, apply_layerwise_casting_hook
26
+ from .mag_cache import MagCacheConfig, apply_mag_cache
27
+ from .pyramid_attention_broadcast import PyramidAttentionBroadcastConfig, apply_pyramid_attention_broadcast
28
+ from .smoothed_energy_guidance_utils import SmoothedEnergyGuidanceConfig
29
+ from .taylorseer_cache import TaylorSeerCacheConfig, apply_taylorseer_cache
30
+ from .text_kv_cache import TextKVCacheConfig, apply_text_kv_cache
third_party/diffusers/src/diffusers/hooks/_common.py ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import torch
16
+
17
+ from ..models.attention import AttentionModuleMixin, FeedForward, LuminaFeedForward
18
+ from ..models.attention_processor import Attention, MochiAttention
19
+
20
+
21
+ _ATTENTION_CLASSES = (Attention, MochiAttention, AttentionModuleMixin)
22
+ _FEEDFORWARD_CLASSES = (FeedForward, LuminaFeedForward)
23
+
24
+ _SPATIAL_TRANSFORMER_BLOCK_IDENTIFIERS = (
25
+ "blocks",
26
+ "transformer_blocks",
27
+ "single_transformer_blocks",
28
+ "layers",
29
+ "visual_transformer_blocks",
30
+ )
31
+ _TEMPORAL_TRANSFORMER_BLOCK_IDENTIFIERS = ("temporal_transformer_blocks",)
32
+ _CROSS_TRANSFORMER_BLOCK_IDENTIFIERS = ("blocks", "transformer_blocks", "layers")
33
+
34
+ _ALL_TRANSFORMER_BLOCK_IDENTIFIERS = tuple(
35
+ {
36
+ *_SPATIAL_TRANSFORMER_BLOCK_IDENTIFIERS,
37
+ *_TEMPORAL_TRANSFORMER_BLOCK_IDENTIFIERS,
38
+ *_CROSS_TRANSFORMER_BLOCK_IDENTIFIERS,
39
+ }
40
+ )
41
+
42
+ # Layers supported for group offloading and layerwise casting
43
+ _GO_LC_SUPPORTED_PYTORCH_LAYERS = (
44
+ torch.nn.Conv1d,
45
+ torch.nn.Conv2d,
46
+ torch.nn.Conv3d,
47
+ torch.nn.ConvTranspose1d,
48
+ torch.nn.ConvTranspose2d,
49
+ torch.nn.ConvTranspose3d,
50
+ torch.nn.Linear,
51
+ torch.nn.Embedding,
52
+ # TODO(aryan): look into torch.nn.LayerNorm, torch.nn.GroupNorm later, seems to be causing some issues with CogVideoX
53
+ # because of double invocation of the same norm layer in CogVideoXLayerNorm
54
+ )
55
+
56
+
57
+ def _get_submodule_from_fqn(module: torch.nn.Module, fqn: str) -> torch.nn.Module | None:
58
+ for submodule_name, submodule in module.named_modules():
59
+ if submodule_name == fqn:
60
+ return submodule
61
+ return None
third_party/diffusers/src/diffusers/hooks/_helpers.py ADDED
@@ -0,0 +1,381 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ from dataclasses import dataclass
17
+ from typing import Any, Callable, Type
18
+
19
+
20
+ @dataclass
21
+ class AttentionProcessorMetadata:
22
+ skip_processor_output_fn: Callable[[Any], Any]
23
+
24
+
25
+ @dataclass
26
+ class TransformerBlockMetadata:
27
+ return_hidden_states_index: int = None
28
+ return_encoder_hidden_states_index: int = None
29
+ hidden_states_argument_name: str = "hidden_states"
30
+
31
+ _cls: Type = None
32
+ _cached_parameter_indices: dict[str, int] = None
33
+
34
+ def _get_parameter_from_args_kwargs(self, identifier: str, args=(), kwargs=None):
35
+ kwargs = kwargs or {}
36
+ if identifier in kwargs:
37
+ return kwargs[identifier]
38
+ if self._cached_parameter_indices is not None:
39
+ return args[self._cached_parameter_indices[identifier]]
40
+ if self._cls is None:
41
+ raise ValueError("Model class is not set for metadata.")
42
+ parameters = list(inspect.signature(self._cls.forward).parameters.keys())
43
+ parameters = parameters[1:] # skip `self`
44
+ self._cached_parameter_indices = {param: i for i, param in enumerate(parameters)}
45
+ if identifier not in self._cached_parameter_indices:
46
+ raise ValueError(f"Parameter '{identifier}' not found in function signature but was requested.")
47
+ index = self._cached_parameter_indices[identifier]
48
+ if index >= len(args):
49
+ raise ValueError(f"Expected {index} arguments but got {len(args)}.")
50
+ return args[index]
51
+
52
+
53
+ class AttentionProcessorRegistry:
54
+ _registry = {}
55
+ # TODO(aryan): this is only required for the time being because we need to do the registrations
56
+ # for classes. If we do it eagerly, i.e. call the functions in global scope, we will get circular
57
+ # import errors because of the models imported in this file.
58
+ _is_registered = False
59
+
60
+ @classmethod
61
+ def register(cls, model_class: Type, metadata: AttentionProcessorMetadata):
62
+ cls._register()
63
+ cls._registry[model_class] = metadata
64
+
65
+ @classmethod
66
+ def get(cls, model_class: Type) -> AttentionProcessorMetadata:
67
+ cls._register()
68
+ if model_class not in cls._registry:
69
+ raise ValueError(f"Model class {model_class} not registered.")
70
+ return cls._registry[model_class]
71
+
72
+ @classmethod
73
+ def _register(cls):
74
+ if cls._is_registered:
75
+ return
76
+ cls._is_registered = True
77
+ _register_attention_processors_metadata()
78
+
79
+
80
+ class TransformerBlockRegistry:
81
+ _registry = {}
82
+ # TODO(aryan): this is only required for the time being because we need to do the registrations
83
+ # for classes. If we do it eagerly, i.e. call the functions in global scope, we will get circular
84
+ # import errors because of the models imported in this file.
85
+ _is_registered = False
86
+
87
+ @classmethod
88
+ def register(cls, model_class: Type, metadata: TransformerBlockMetadata):
89
+ cls._register()
90
+ metadata._cls = model_class
91
+ cls._registry[model_class] = metadata
92
+
93
+ @classmethod
94
+ def get(cls, model_class: Type) -> TransformerBlockMetadata:
95
+ cls._register()
96
+ if model_class not in cls._registry:
97
+ raise ValueError(f"Model class {model_class} not registered.")
98
+ return cls._registry[model_class]
99
+
100
+ @classmethod
101
+ def _register(cls):
102
+ if cls._is_registered:
103
+ return
104
+ cls._is_registered = True
105
+ _register_transformer_blocks_metadata()
106
+
107
+
108
+ def _register_attention_processors_metadata():
109
+ from ..models.attention_processor import AttnProcessor2_0
110
+ from ..models.transformers.transformer_cogview4 import CogView4AttnProcessor
111
+ from ..models.transformers.transformer_flux import FluxAttnProcessor
112
+ from ..models.transformers.transformer_hunyuanimage import HunyuanImageAttnProcessor
113
+ from ..models.transformers.transformer_qwenimage import QwenDoubleStreamAttnProcessor2_0
114
+ from ..models.transformers.transformer_wan import WanAttnProcessor2_0
115
+ from ..models.transformers.transformer_z_image import ZSingleStreamAttnProcessor
116
+
117
+ # AttnProcessor2_0
118
+ AttentionProcessorRegistry.register(
119
+ model_class=AttnProcessor2_0,
120
+ metadata=AttentionProcessorMetadata(
121
+ skip_processor_output_fn=_skip_proc_output_fn_Attention_AttnProcessor2_0,
122
+ ),
123
+ )
124
+
125
+ # CogView4AttnProcessor
126
+ AttentionProcessorRegistry.register(
127
+ model_class=CogView4AttnProcessor,
128
+ metadata=AttentionProcessorMetadata(
129
+ skip_processor_output_fn=_skip_proc_output_fn_Attention_CogView4AttnProcessor,
130
+ ),
131
+ )
132
+
133
+ # WanAttnProcessor2_0
134
+ AttentionProcessorRegistry.register(
135
+ model_class=WanAttnProcessor2_0,
136
+ metadata=AttentionProcessorMetadata(
137
+ skip_processor_output_fn=_skip_proc_output_fn_Attention_WanAttnProcessor2_0,
138
+ ),
139
+ )
140
+
141
+ # FluxAttnProcessor
142
+ AttentionProcessorRegistry.register(
143
+ model_class=FluxAttnProcessor,
144
+ metadata=AttentionProcessorMetadata(skip_processor_output_fn=_skip_proc_output_fn_Attention_FluxAttnProcessor),
145
+ )
146
+
147
+ # QwenDoubleStreamAttnProcessor2
148
+ AttentionProcessorRegistry.register(
149
+ model_class=QwenDoubleStreamAttnProcessor2_0,
150
+ metadata=AttentionProcessorMetadata(
151
+ skip_processor_output_fn=_skip_proc_output_fn_Attention_QwenDoubleStreamAttnProcessor2_0
152
+ ),
153
+ )
154
+
155
+ # HunyuanImageAttnProcessor
156
+ AttentionProcessorRegistry.register(
157
+ model_class=HunyuanImageAttnProcessor,
158
+ metadata=AttentionProcessorMetadata(
159
+ skip_processor_output_fn=_skip_proc_output_fn_Attention_HunyuanImageAttnProcessor,
160
+ ),
161
+ )
162
+
163
+ # ZSingleStreamAttnProcessor
164
+ AttentionProcessorRegistry.register(
165
+ model_class=ZSingleStreamAttnProcessor,
166
+ metadata=AttentionProcessorMetadata(
167
+ skip_processor_output_fn=_skip_proc_output_fn_Attention_ZSingleStreamAttnProcessor,
168
+ ),
169
+ )
170
+
171
+
172
+ def _register_transformer_blocks_metadata():
173
+ from ..models.attention import BasicTransformerBlock, JointTransformerBlock
174
+ from ..models.transformers.cogvideox_transformer_3d import CogVideoXBlock
175
+ from ..models.transformers.transformer_bria import BriaTransformerBlock
176
+ from ..models.transformers.transformer_cogview4 import CogView4TransformerBlock
177
+ from ..models.transformers.transformer_flux import FluxSingleTransformerBlock, FluxTransformerBlock
178
+ from ..models.transformers.transformer_hunyuan_video import (
179
+ HunyuanVideoSingleTransformerBlock,
180
+ HunyuanVideoTokenReplaceSingleTransformerBlock,
181
+ HunyuanVideoTokenReplaceTransformerBlock,
182
+ HunyuanVideoTransformerBlock,
183
+ )
184
+ from ..models.transformers.transformer_hunyuanimage import (
185
+ HunyuanImageSingleTransformerBlock,
186
+ HunyuanImageTransformerBlock,
187
+ )
188
+ from ..models.transformers.transformer_kandinsky import Kandinsky5TransformerDecoderBlock
189
+ from ..models.transformers.transformer_ltx import LTXVideoTransformerBlock
190
+ from ..models.transformers.transformer_mochi import MochiTransformerBlock
191
+ from ..models.transformers.transformer_qwenimage import QwenImageTransformerBlock
192
+ from ..models.transformers.transformer_wan import WanTransformerBlock
193
+ from ..models.transformers.transformer_z_image import ZImageTransformerBlock
194
+
195
+ # BasicTransformerBlock
196
+ TransformerBlockRegistry.register(
197
+ model_class=BasicTransformerBlock,
198
+ metadata=TransformerBlockMetadata(
199
+ return_hidden_states_index=0,
200
+ return_encoder_hidden_states_index=None,
201
+ ),
202
+ )
203
+ TransformerBlockRegistry.register(
204
+ model_class=BriaTransformerBlock,
205
+ metadata=TransformerBlockMetadata(
206
+ return_hidden_states_index=0,
207
+ return_encoder_hidden_states_index=None,
208
+ ),
209
+ )
210
+
211
+ # CogVideoX
212
+ TransformerBlockRegistry.register(
213
+ model_class=CogVideoXBlock,
214
+ metadata=TransformerBlockMetadata(
215
+ return_hidden_states_index=0,
216
+ return_encoder_hidden_states_index=1,
217
+ ),
218
+ )
219
+
220
+ # CogView4
221
+ TransformerBlockRegistry.register(
222
+ model_class=CogView4TransformerBlock,
223
+ metadata=TransformerBlockMetadata(
224
+ return_hidden_states_index=0,
225
+ return_encoder_hidden_states_index=1,
226
+ ),
227
+ )
228
+
229
+ # Flux
230
+ TransformerBlockRegistry.register(
231
+ model_class=FluxTransformerBlock,
232
+ metadata=TransformerBlockMetadata(
233
+ return_hidden_states_index=1,
234
+ return_encoder_hidden_states_index=0,
235
+ ),
236
+ )
237
+ TransformerBlockRegistry.register(
238
+ model_class=FluxSingleTransformerBlock,
239
+ metadata=TransformerBlockMetadata(
240
+ return_hidden_states_index=1,
241
+ return_encoder_hidden_states_index=0,
242
+ ),
243
+ )
244
+
245
+ # HunyuanVideo
246
+ TransformerBlockRegistry.register(
247
+ model_class=HunyuanVideoTransformerBlock,
248
+ metadata=TransformerBlockMetadata(
249
+ return_hidden_states_index=0,
250
+ return_encoder_hidden_states_index=1,
251
+ ),
252
+ )
253
+ TransformerBlockRegistry.register(
254
+ model_class=HunyuanVideoSingleTransformerBlock,
255
+ metadata=TransformerBlockMetadata(
256
+ return_hidden_states_index=0,
257
+ return_encoder_hidden_states_index=1,
258
+ ),
259
+ )
260
+ TransformerBlockRegistry.register(
261
+ model_class=HunyuanVideoTokenReplaceTransformerBlock,
262
+ metadata=TransformerBlockMetadata(
263
+ return_hidden_states_index=0,
264
+ return_encoder_hidden_states_index=1,
265
+ ),
266
+ )
267
+ TransformerBlockRegistry.register(
268
+ model_class=HunyuanVideoTokenReplaceSingleTransformerBlock,
269
+ metadata=TransformerBlockMetadata(
270
+ return_hidden_states_index=0,
271
+ return_encoder_hidden_states_index=1,
272
+ ),
273
+ )
274
+
275
+ # LTXVideo
276
+ TransformerBlockRegistry.register(
277
+ model_class=LTXVideoTransformerBlock,
278
+ metadata=TransformerBlockMetadata(
279
+ return_hidden_states_index=0,
280
+ return_encoder_hidden_states_index=None,
281
+ ),
282
+ )
283
+
284
+ # Mochi
285
+ TransformerBlockRegistry.register(
286
+ model_class=MochiTransformerBlock,
287
+ metadata=TransformerBlockMetadata(
288
+ return_hidden_states_index=0,
289
+ return_encoder_hidden_states_index=1,
290
+ ),
291
+ )
292
+
293
+ # Wan
294
+ TransformerBlockRegistry.register(
295
+ model_class=WanTransformerBlock,
296
+ metadata=TransformerBlockMetadata(
297
+ return_hidden_states_index=0,
298
+ return_encoder_hidden_states_index=None,
299
+ ),
300
+ )
301
+
302
+ # QwenImage
303
+ TransformerBlockRegistry.register(
304
+ model_class=QwenImageTransformerBlock,
305
+ metadata=TransformerBlockMetadata(
306
+ return_hidden_states_index=1,
307
+ return_encoder_hidden_states_index=0,
308
+ ),
309
+ )
310
+
311
+ # HunyuanImage2.1
312
+ TransformerBlockRegistry.register(
313
+ model_class=HunyuanImageTransformerBlock,
314
+ metadata=TransformerBlockMetadata(
315
+ return_hidden_states_index=0,
316
+ return_encoder_hidden_states_index=1,
317
+ ),
318
+ )
319
+ TransformerBlockRegistry.register(
320
+ model_class=HunyuanImageSingleTransformerBlock,
321
+ metadata=TransformerBlockMetadata(
322
+ return_hidden_states_index=0,
323
+ return_encoder_hidden_states_index=1,
324
+ ),
325
+ )
326
+
327
+ # ZImage
328
+ TransformerBlockRegistry.register(
329
+ model_class=ZImageTransformerBlock,
330
+ metadata=TransformerBlockMetadata(
331
+ return_hidden_states_index=0,
332
+ return_encoder_hidden_states_index=None,
333
+ ),
334
+ )
335
+
336
+ TransformerBlockRegistry.register(
337
+ model_class=JointTransformerBlock,
338
+ metadata=TransformerBlockMetadata(
339
+ return_hidden_states_index=1,
340
+ return_encoder_hidden_states_index=0,
341
+ ),
342
+ )
343
+
344
+ # Kandinsky 5.0 (Kandinsky5TransformerDecoderBlock)
345
+ TransformerBlockRegistry.register(
346
+ model_class=Kandinsky5TransformerDecoderBlock,
347
+ metadata=TransformerBlockMetadata(
348
+ return_hidden_states_index=0,
349
+ return_encoder_hidden_states_index=None,
350
+ hidden_states_argument_name="visual_embed",
351
+ ),
352
+ )
353
+
354
+
355
+ # fmt: off
356
+ def _skip_attention___ret___hidden_states(self, *args, **kwargs):
357
+ hidden_states = kwargs.get("hidden_states", None)
358
+ if hidden_states is None and len(args) > 0:
359
+ hidden_states = args[0]
360
+ return hidden_states
361
+
362
+
363
+ def _skip_attention___ret___hidden_states___encoder_hidden_states(self, *args, **kwargs):
364
+ hidden_states = kwargs.get("hidden_states", None)
365
+ encoder_hidden_states = kwargs.get("encoder_hidden_states", None)
366
+ if hidden_states is None and len(args) > 0:
367
+ hidden_states = args[0]
368
+ if encoder_hidden_states is None and len(args) > 1:
369
+ encoder_hidden_states = args[1]
370
+ return hidden_states, encoder_hidden_states
371
+
372
+
373
+ _skip_proc_output_fn_Attention_AttnProcessor2_0 = _skip_attention___ret___hidden_states
374
+ _skip_proc_output_fn_Attention_CogView4AttnProcessor = _skip_attention___ret___hidden_states___encoder_hidden_states
375
+ _skip_proc_output_fn_Attention_WanAttnProcessor2_0 = _skip_attention___ret___hidden_states
376
+ # not sure what this is yet.
377
+ _skip_proc_output_fn_Attention_FluxAttnProcessor = _skip_attention___ret___hidden_states
378
+ _skip_proc_output_fn_Attention_QwenDoubleStreamAttnProcessor2_0 = _skip_attention___ret___hidden_states
379
+ _skip_proc_output_fn_Attention_HunyuanImageAttnProcessor = _skip_attention___ret___hidden_states
380
+ _skip_proc_output_fn_Attention_ZSingleStreamAttnProcessor = _skip_attention___ret___hidden_states
381
+ # fmt: on
third_party/diffusers/src/diffusers/hooks/context_parallel.py ADDED
@@ -0,0 +1,382 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import copy
15
+ import inspect
16
+ from dataclasses import dataclass
17
+ from typing import Type
18
+
19
+ import torch
20
+ import torch.distributed as dist
21
+
22
+
23
+ if torch.distributed.is_available():
24
+ import torch.distributed._functional_collectives as funcol
25
+
26
+ from ..models._modeling_parallel import (
27
+ ContextParallelConfig,
28
+ ContextParallelInput,
29
+ ContextParallelModelPlan,
30
+ ContextParallelOutput,
31
+ gather_size_by_comm,
32
+ )
33
+ from ..utils import get_logger
34
+ from ..utils.torch_utils import lru_cache_unless_export, maybe_allow_in_graph, unwrap_module
35
+ from .hooks import HookRegistry, ModelHook
36
+
37
+
38
+ logger = get_logger(__name__) # pylint: disable=invalid-name
39
+
40
+ _CONTEXT_PARALLEL_INPUT_HOOK_TEMPLATE = "cp_input---{}"
41
+ _CONTEXT_PARALLEL_OUTPUT_HOOK_TEMPLATE = "cp_output---{}"
42
+
43
+
44
+ # TODO(aryan): consolidate with ._helpers.TransformerBlockMetadata
45
+ @dataclass
46
+ class ModuleForwardMetadata:
47
+ cached_parameter_indices: dict[str, int] = None
48
+ _cls: Type = None
49
+
50
+ def _get_parameter_from_args_kwargs(self, identifier: str, args=(), kwargs=None):
51
+ kwargs = kwargs or {}
52
+
53
+ if identifier in kwargs:
54
+ return kwargs[identifier], True, None
55
+
56
+ if self.cached_parameter_indices is not None:
57
+ index = self.cached_parameter_indices.get(identifier, None)
58
+ if index is None:
59
+ raise ValueError(f"Parameter '{identifier}' not found in cached indices.")
60
+ return args[index], False, index
61
+
62
+ if self._cls is None:
63
+ raise ValueError("Model class is not set for metadata.")
64
+
65
+ parameters = list(inspect.signature(self._cls.forward).parameters.keys())
66
+ parameters = parameters[1:] # skip `self`
67
+ self.cached_parameter_indices = {param: i for i, param in enumerate(parameters)}
68
+
69
+ if identifier not in self.cached_parameter_indices:
70
+ raise ValueError(f"Parameter '{identifier}' not found in function signature but was requested.")
71
+
72
+ index = self.cached_parameter_indices[identifier]
73
+
74
+ if index >= len(args):
75
+ raise ValueError(f"Expected {index} arguments but got {len(args)}.")
76
+
77
+ return args[index], False, index
78
+
79
+
80
+ def apply_context_parallel(
81
+ module: torch.nn.Module,
82
+ parallel_config: ContextParallelConfig,
83
+ plan: dict[str, ContextParallelModelPlan],
84
+ ) -> None:
85
+ """Apply context parallel on a model."""
86
+ logger.debug(f"Applying context parallel with CP mesh: {parallel_config._mesh} and plan: {plan}")
87
+
88
+ for module_id, cp_model_plan in plan.items():
89
+ submodule = _get_submodule_by_name(module, module_id)
90
+ if not isinstance(submodule, list):
91
+ submodule = [submodule]
92
+
93
+ logger.debug(f"Applying ContextParallelHook to {module_id=} identifying a total of {len(submodule)} modules")
94
+
95
+ for m in submodule:
96
+ if isinstance(cp_model_plan, dict):
97
+ hook = ContextParallelSplitHook(cp_model_plan, parallel_config)
98
+ hook_name = _CONTEXT_PARALLEL_INPUT_HOOK_TEMPLATE.format(module_id)
99
+ elif isinstance(cp_model_plan, (ContextParallelOutput, list, tuple)):
100
+ if isinstance(cp_model_plan, ContextParallelOutput):
101
+ cp_model_plan = [cp_model_plan]
102
+ if not all(isinstance(x, ContextParallelOutput) for x in cp_model_plan):
103
+ raise ValueError(f"Expected all elements of cp_model_plan to be CPOutput, but got {cp_model_plan}")
104
+ hook = ContextParallelGatherHook(cp_model_plan, parallel_config)
105
+ hook_name = _CONTEXT_PARALLEL_OUTPUT_HOOK_TEMPLATE.format(module_id)
106
+ else:
107
+ raise ValueError(f"Unsupported context parallel model plan type: {type(cp_model_plan)}")
108
+ registry = HookRegistry.check_if_exists_or_initialize(m)
109
+ registry.register_hook(hook, hook_name)
110
+
111
+
112
+ def remove_context_parallel(module: torch.nn.Module, plan: dict[str, ContextParallelModelPlan]) -> None:
113
+ for module_id, cp_model_plan in plan.items():
114
+ submodule = _get_submodule_by_name(module, module_id)
115
+ if not isinstance(submodule, list):
116
+ submodule = [submodule]
117
+
118
+ for m in submodule:
119
+ registry = HookRegistry.check_if_exists_or_initialize(m)
120
+ if isinstance(cp_model_plan, dict):
121
+ hook_name = _CONTEXT_PARALLEL_INPUT_HOOK_TEMPLATE.format(module_id)
122
+ elif isinstance(cp_model_plan, (ContextParallelOutput, list, tuple)):
123
+ hook_name = _CONTEXT_PARALLEL_OUTPUT_HOOK_TEMPLATE.format(module_id)
124
+ else:
125
+ raise ValueError(f"Unsupported context parallel model plan type: {type(cp_model_plan)}")
126
+ registry.remove_hook(hook_name)
127
+
128
+
129
+ class ContextParallelSplitHook(ModelHook):
130
+ def __init__(self, metadata: ContextParallelModelPlan, parallel_config: ContextParallelConfig) -> None:
131
+ super().__init__()
132
+ self.metadata = metadata
133
+ self.parallel_config = parallel_config
134
+ self.module_forward_metadata = None
135
+
136
+ def initialize_hook(self, module):
137
+ cls = unwrap_module(module).__class__
138
+ self.module_forward_metadata = ModuleForwardMetadata(_cls=cls)
139
+ return module
140
+
141
+ def pre_forward(self, module, *args, **kwargs):
142
+ args_list = list(args)
143
+
144
+ for name, cpm in self.metadata.items():
145
+ if isinstance(cpm, ContextParallelInput) and cpm.split_output:
146
+ continue
147
+
148
+ # Maybe the parameter was passed as a keyword argument
149
+ input_val, is_kwarg, index = self.module_forward_metadata._get_parameter_from_args_kwargs(
150
+ name, args_list, kwargs
151
+ )
152
+
153
+ if input_val is None:
154
+ continue
155
+
156
+ # The input_val may be a tensor or list/tuple of tensors. In certain cases, user may specify to shard
157
+ # the output instead of input for a particular layer by setting split_output=True
158
+ if isinstance(input_val, torch.Tensor):
159
+ input_val = self._prepare_cp_input(input_val, cpm)
160
+ elif isinstance(input_val, (list, tuple)):
161
+ if len(input_val) != len(cpm):
162
+ raise ValueError(
163
+ f"Expected input model plan to have {len(input_val)} elements, but got {len(cpm)}."
164
+ )
165
+ sharded_input_val = []
166
+ for i, x in enumerate(input_val):
167
+ if torch.is_tensor(x) and not cpm[i].split_output:
168
+ x = self._prepare_cp_input(x, cpm[i])
169
+ sharded_input_val.append(x)
170
+ input_val = sharded_input_val
171
+ else:
172
+ raise ValueError(f"Unsupported input type: {type(input_val)}")
173
+
174
+ if is_kwarg:
175
+ kwargs[name] = input_val
176
+ elif index is not None and index < len(args_list):
177
+ args_list[index] = input_val
178
+ else:
179
+ raise ValueError(
180
+ f"An unexpected error occurred while processing the input '{name}'. Please open an "
181
+ f"issue at https://github.com/huggingface/diffusers/issues and provide a minimal reproducible "
182
+ f"example along with the full stack trace."
183
+ )
184
+
185
+ return tuple(args_list), kwargs
186
+
187
+ def post_forward(self, module, output):
188
+ is_tensor = isinstance(output, torch.Tensor)
189
+ is_tensor_list = isinstance(output, (list, tuple)) and all(isinstance(x, torch.Tensor) for x in output)
190
+
191
+ if not is_tensor and not is_tensor_list:
192
+ raise ValueError(f"Expected output to be a tensor or a list/tuple of tensors, but got {type(output)}.")
193
+
194
+ output = [output] if is_tensor else list(output)
195
+ for index, cpm in self.metadata.items():
196
+ if not isinstance(cpm, ContextParallelInput) or not cpm.split_output:
197
+ continue
198
+ if index >= len(output):
199
+ raise ValueError(f"Index {index} out of bounds for output of length {len(output)}.")
200
+ current_output = output[index]
201
+ current_output = self._prepare_cp_input(current_output, cpm)
202
+ output[index] = current_output
203
+
204
+ return output[0] if is_tensor else tuple(output)
205
+
206
+ def _prepare_cp_input(self, x: torch.Tensor, cp_input: ContextParallelInput) -> torch.Tensor:
207
+ if cp_input.expected_dims is not None and x.dim() != cp_input.expected_dims:
208
+ logger.warning_once(
209
+ f"Expected input tensor to have {cp_input.expected_dims} dimensions, but got {x.dim()} dimensions, split will not be applied."
210
+ )
211
+ return x
212
+ else:
213
+ if self.parallel_config.ulysses_anything:
214
+ return PartitionAnythingSharder.shard_anything(
215
+ x, cp_input.split_dim, self.parallel_config._flattened_mesh
216
+ )
217
+ return EquipartitionSharder.shard(x, cp_input.split_dim, self.parallel_config._flattened_mesh)
218
+
219
+
220
+ class ContextParallelGatherHook(ModelHook):
221
+ def __init__(self, metadata: ContextParallelModelPlan, parallel_config: ContextParallelConfig) -> None:
222
+ super().__init__()
223
+ self.metadata = metadata
224
+ self.parallel_config = parallel_config
225
+
226
+ def post_forward(self, module, output):
227
+ is_tensor = isinstance(output, torch.Tensor)
228
+
229
+ if is_tensor:
230
+ output = [output]
231
+ elif not (isinstance(output, (list, tuple)) and all(isinstance(x, torch.Tensor) for x in output)):
232
+ raise ValueError(f"Expected output to be a tensor or a list/tuple of tensors, but got {type(output)}.")
233
+
234
+ output = list(output)
235
+
236
+ if len(output) != len(self.metadata):
237
+ raise ValueError(f"Expected output to have {len(self.metadata)} elements, but got {len(output)}.")
238
+
239
+ for i, cpm in enumerate(self.metadata):
240
+ if cpm is None:
241
+ continue
242
+ if self.parallel_config.ulysses_anything:
243
+ output[i] = PartitionAnythingSharder.unshard_anything(
244
+ output[i], cpm.gather_dim, self.parallel_config._flattened_mesh
245
+ )
246
+ else:
247
+ output[i] = EquipartitionSharder.unshard(
248
+ output[i], cpm.gather_dim, self.parallel_config._flattened_mesh
249
+ )
250
+
251
+ return output[0] if is_tensor else tuple(output)
252
+
253
+
254
+ class AllGatherFunction(torch.autograd.Function):
255
+ @staticmethod
256
+ def forward(ctx, tensor, dim, group):
257
+ ctx.dim = dim
258
+ ctx.group = group
259
+ ctx.world_size = torch.distributed.get_world_size(group)
260
+ ctx.rank = torch.distributed.get_rank(group)
261
+ return funcol.all_gather_tensor(tensor, dim, group=group)
262
+
263
+ @staticmethod
264
+ def backward(ctx, grad_output):
265
+ grad_chunks = torch.chunk(grad_output, ctx.world_size, dim=ctx.dim)
266
+ return grad_chunks[ctx.rank], None, None
267
+
268
+
269
+ class EquipartitionSharder:
270
+ @classmethod
271
+ def shard(cls, tensor: torch.Tensor, dim: int, mesh: torch.distributed.device_mesh.DeviceMesh) -> torch.Tensor:
272
+ # NOTE: the following assertion does not have to be true in general. We simply enforce it for now
273
+ # because the alternate case has not yet been tested/required for any model.
274
+ assert tensor.size()[dim] % mesh.size() == 0, (
275
+ "Tensor size along dimension to be sharded must be divisible by mesh size"
276
+ )
277
+
278
+ # The following is not fullgraph compatible with Dynamo (fails in DeviceMesh.get_rank)
279
+ # return tensor.chunk(mesh.size(), dim=dim)[mesh.get_rank()]
280
+
281
+ return tensor.chunk(mesh.size(), dim=dim)[torch.distributed.get_rank(mesh.get_group())]
282
+
283
+ @classmethod
284
+ def unshard(cls, tensor: torch.Tensor, dim: int, mesh: torch.distributed.device_mesh.DeviceMesh) -> torch.Tensor:
285
+ tensor = tensor.contiguous()
286
+ tensor = AllGatherFunction.apply(tensor, dim, mesh.get_group())
287
+ return tensor
288
+
289
+
290
+ class AllGatherAnythingFunction(torch.autograd.Function):
291
+ @staticmethod
292
+ def forward(ctx, tensor: torch.Tensor, dim: int, group: dist.device_mesh.DeviceMesh):
293
+ ctx.dim = dim
294
+ ctx.group = group
295
+ ctx.world_size = dist.get_world_size(group)
296
+ ctx.rank = dist.get_rank(group)
297
+ gathered_tensor = _all_gather_anything(tensor, dim, group)
298
+ return gathered_tensor
299
+
300
+ @staticmethod
301
+ def backward(ctx, grad_output):
302
+ # NOTE: We use `tensor_split` instead of chunk, because the `chunk`
303
+ # function may return fewer than the specified number of chunks!
304
+ grad_splits = torch.tensor_split(grad_output, ctx.world_size, dim=ctx.dim)
305
+ return grad_splits[ctx.rank], None, None
306
+
307
+
308
+ class PartitionAnythingSharder:
309
+ @classmethod
310
+ def shard_anything(
311
+ cls, tensor: torch.Tensor, dim: int, mesh: torch.distributed.device_mesh.DeviceMesh
312
+ ) -> torch.Tensor:
313
+ assert tensor.size()[dim] >= mesh.size(), (
314
+ f"Cannot shard tensor of size {tensor.size()} along dim {dim} across mesh of size {mesh.size()}."
315
+ )
316
+ # NOTE: We use `tensor_split` instead of chunk, because the `chunk`
317
+ # function may return fewer than the specified number of chunks!
318
+ return tensor.tensor_split(mesh.size(), dim=dim)[dist.get_rank(mesh.get_group())]
319
+
320
+ @classmethod
321
+ def unshard_anything(
322
+ cls, tensor: torch.Tensor, dim: int, mesh: torch.distributed.device_mesh.DeviceMesh
323
+ ) -> torch.Tensor:
324
+ tensor = tensor.contiguous()
325
+ tensor = AllGatherAnythingFunction.apply(tensor, dim, mesh.get_group())
326
+ return tensor
327
+
328
+
329
+ @lru_cache_unless_export(maxsize=64)
330
+ def _fill_gather_shapes(shape: tuple[int], gather_dims: tuple[int], dim: int, world_size: int) -> list[list[int]]:
331
+ gather_shapes = []
332
+ for i in range(world_size):
333
+ rank_shape = list(copy.deepcopy(shape))
334
+ rank_shape[dim] = gather_dims[i]
335
+ gather_shapes.append(rank_shape)
336
+ return gather_shapes
337
+
338
+
339
+ @maybe_allow_in_graph
340
+ def _all_gather_anything(tensor: torch.Tensor, dim: int, group: dist.device_mesh.DeviceMesh) -> torch.Tensor:
341
+ world_size = dist.get_world_size(group=group)
342
+
343
+ tensor = tensor.contiguous()
344
+ shape = tensor.shape
345
+ rank_dim = shape[dim]
346
+ gather_dims = gather_size_by_comm(rank_dim, group)
347
+
348
+ gather_shapes = _fill_gather_shapes(tuple(shape), tuple(gather_dims), dim, world_size)
349
+
350
+ gathered_tensors = [torch.empty(shape, device=tensor.device, dtype=tensor.dtype) for shape in gather_shapes]
351
+
352
+ dist.all_gather(gathered_tensors, tensor, group=group)
353
+ gathered_tensor = torch.cat(gathered_tensors, dim=dim)
354
+ return gathered_tensor
355
+
356
+
357
+ def _get_submodule_by_name(model: torch.nn.Module, name: str) -> torch.nn.Module | list[torch.nn.Module]:
358
+ if name.count("*") > 1:
359
+ raise ValueError("Wildcard '*' can only be used once in the name")
360
+ return _find_submodule_by_name(model, name)
361
+
362
+
363
+ def _find_submodule_by_name(model: torch.nn.Module, name: str) -> torch.nn.Module | list[torch.nn.Module]:
364
+ if name == "":
365
+ return model
366
+ first_atom, remaining_name = name.split(".", 1) if "." in name else (name, "")
367
+ if first_atom == "*":
368
+ if not isinstance(model, torch.nn.ModuleList):
369
+ raise ValueError("Wildcard '*' can only be used with ModuleList")
370
+ submodules = []
371
+ for submodule in model:
372
+ subsubmodules = _find_submodule_by_name(submodule, remaining_name)
373
+ if not isinstance(subsubmodules, list):
374
+ subsubmodules = [subsubmodules]
375
+ submodules.extend(subsubmodules)
376
+ return submodules
377
+ else:
378
+ if hasattr(model, first_atom):
379
+ submodule = getattr(model, first_atom)
380
+ return _find_submodule_by_name(submodule, remaining_name)
381
+ else:
382
+ raise ValueError(f"'{first_atom}' is not a submodule of '{model.__class__.__name__}'")
third_party/diffusers/src/diffusers/hooks/faster_cache.py ADDED
@@ -0,0 +1,654 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import re
16
+ from dataclasses import dataclass
17
+ from typing import Any, Callable
18
+
19
+ import torch
20
+
21
+ from ..models.attention import AttentionModuleMixin
22
+ from ..models.modeling_outputs import Transformer2DModelOutput
23
+ from ..utils import logging
24
+ from ._common import _ATTENTION_CLASSES
25
+ from .hooks import HookRegistry, ModelHook
26
+
27
+
28
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
29
+
30
+
31
+ _FASTER_CACHE_DENOISER_HOOK = "faster_cache_denoiser"
32
+ _FASTER_CACHE_BLOCK_HOOK = "faster_cache_block"
33
+ _SPATIAL_ATTENTION_BLOCK_IDENTIFIERS = (
34
+ "^blocks.*attn",
35
+ "^transformer_blocks.*attn",
36
+ "^single_transformer_blocks.*attn",
37
+ )
38
+ _TEMPORAL_ATTENTION_BLOCK_IDENTIFIERS = ("^temporal_transformer_blocks.*attn",)
39
+ _TRANSFORMER_BLOCK_IDENTIFIERS = _SPATIAL_ATTENTION_BLOCK_IDENTIFIERS + _TEMPORAL_ATTENTION_BLOCK_IDENTIFIERS
40
+ _UNCOND_COND_INPUT_KWARGS_IDENTIFIERS = (
41
+ "hidden_states",
42
+ "encoder_hidden_states",
43
+ "timestep",
44
+ "attention_mask",
45
+ "encoder_attention_mask",
46
+ )
47
+
48
+
49
+ @dataclass
50
+ class FasterCacheConfig:
51
+ r"""
52
+ Configuration for [FasterCache](https://huggingface.co/papers/2410.19355).
53
+
54
+ Attributes:
55
+ spatial_attention_block_skip_range (`int`, defaults to `2`):
56
+ Calculate the attention states every `N` iterations. If this is set to `N`, the attention computation will
57
+ be skipped `N - 1` times (i.e., cached attention states will be reused) before computing the new attention
58
+ states again.
59
+ temporal_attention_block_skip_range (`int`, *optional*, defaults to `None`):
60
+ Calculate the attention states every `N` iterations. If this is set to `N`, the attention computation will
61
+ be skipped `N - 1` times (i.e., cached attention states will be reused) before computing the new attention
62
+ states again.
63
+ spatial_attention_timestep_skip_range (`tuple[float, float]`, defaults to `(-1, 681)`):
64
+ The timestep range within which the spatial attention computation can be skipped without a significant loss
65
+ in quality. This is to be determined by the user based on the underlying model. The first value in the
66
+ tuple is the lower bound and the second value is the upper bound. Typically, diffusion timesteps for
67
+ denoising are in the reversed range of 0 to 1000 (i.e. denoising starts at timestep 1000 and ends at
68
+ timestep 0). For the default values, this would mean that the spatial attention computation skipping will
69
+ be applicable only after denoising timestep 681 is reached, and continue until the end of the denoising
70
+ process.
71
+ temporal_attention_timestep_skip_range (`tuple[float, float]`, *optional*, defaults to `None`):
72
+ The timestep range within which the temporal attention computation can be skipped without a significant
73
+ loss in quality. This is to be determined by the user based on the underlying model. The first value in the
74
+ tuple is the lower bound and the second value is the upper bound. Typically, diffusion timesteps for
75
+ denoising are in the reversed range of 0 to 1000 (i.e. denoising starts at timestep 1000 and ends at
76
+ timestep 0).
77
+ low_frequency_weight_update_timestep_range (`tuple[int, int]`, defaults to `(99, 901)`):
78
+ The timestep range within which the low frequency weight scaling update is applied. The first value in the
79
+ tuple is the lower bound and the second value is the upper bound of the timestep range. The callback
80
+ function for the update is called only within this range.
81
+ high_frequency_weight_update_timestep_range (`tuple[int, int]`, defaults to `(-1, 301)`):
82
+ The timestep range within which the high frequency weight scaling update is applied. The first value in the
83
+ tuple is the lower bound and the second value is the upper bound of the timestep range. The callback
84
+ function for the update is called only within this range.
85
+ alpha_low_frequency (`float`, defaults to `1.1`):
86
+ The weight to scale the low frequency updates by. This is used to approximate the unconditional branch from
87
+ the conditional branch outputs.
88
+ alpha_high_frequency (`float`, defaults to `1.1`):
89
+ The weight to scale the high frequency updates by. This is used to approximate the unconditional branch
90
+ from the conditional branch outputs.
91
+ unconditional_batch_skip_range (`int`, defaults to `5`):
92
+ Process the unconditional branch every `N` iterations. If this is set to `N`, the unconditional branch
93
+ computation will be skipped `N - 1` times (i.e., cached unconditional branch states will be reused) before
94
+ computing the new unconditional branch states again.
95
+ unconditional_batch_timestep_skip_range (`tuple[float, float]`, defaults to `(-1, 641)`):
96
+ The timestep range within which the unconditional branch computation can be skipped without a significant
97
+ loss in quality. This is to be determined by the user based on the underlying model. The first value in the
98
+ tuple is the lower bound and the second value is the upper bound.
99
+ spatial_attention_block_identifiers (`tuple[str, ...]`, defaults to `("blocks.*attn1", "transformer_blocks.*attn1", "single_transformer_blocks.*attn1")`):
100
+ The identifiers to match the spatial attention blocks in the model. If the name of the block contains any
101
+ of these identifiers, FasterCache will be applied to that block. This can either be the full layer names,
102
+ partial layer names, or regex patterns. Matching will always be done using a regex match.
103
+ temporal_attention_block_identifiers (`tuple[str, ...]`, defaults to `("temporal_transformer_blocks.*attn1",)`):
104
+ The identifiers to match the temporal attention blocks in the model. If the name of the block contains any
105
+ of these identifiers, FasterCache will be applied to that block. This can either be the full layer names,
106
+ partial layer names, or regex patterns. Matching will always be done using a regex match.
107
+ attention_weight_callback (`Callable[[torch.nn.Module], float]`, defaults to `None`):
108
+ The callback function to determine the weight to scale the attention outputs by. This function should take
109
+ the attention module as input and return a float value. This is used to approximate the unconditional
110
+ branch from the conditional branch outputs. If not provided, the default weight is 0.5 for all timesteps.
111
+ Typically, as described in the paper, this weight should gradually increase from 0 to 1 as the inference
112
+ progresses. Users are encouraged to experiment and provide custom weight schedules that take into account
113
+ the number of inference steps and underlying model behaviour as denoising progresses.
114
+ low_frequency_weight_callback (`Callable[[torch.nn.Module], float]`, defaults to `None`):
115
+ The callback function to determine the weight to scale the low frequency updates by. If not provided, the
116
+ default weight is 1.1 for timesteps within the range specified (as described in the paper).
117
+ high_frequency_weight_callback (`Callable[[torch.nn.Module], float]`, defaults to `None`):
118
+ The callback function to determine the weight to scale the high frequency updates by. If not provided, the
119
+ default weight is 1.1 for timesteps within the range specified (as described in the paper).
120
+ tensor_format (`str`, defaults to `"BCFHW"`):
121
+ The format of the input tensors. This should be one of `"BCFHW"`, `"BFCHW"`, or `"BCHW"`. The format is
122
+ used to split individual latent frames in order for low and high frequency components to be computed.
123
+ is_guidance_distilled (`bool`, defaults to `False`):
124
+ Whether the model is guidance distilled or not. If the model is guidance distilled, FasterCache will not be
125
+ applied at the denoiser-level to skip the unconditional branch computation (as there is none).
126
+ _unconditional_conditional_input_kwargs_identifiers (`list[str]`, defaults to `("hidden_states", "encoder_hidden_states", "timestep", "attention_mask", "encoder_attention_mask")`):
127
+ The identifiers to match the input kwargs that contain the batchwise-concatenated unconditional and
128
+ conditional inputs. If the name of the input kwargs contains any of these identifiers, FasterCache will
129
+ split the inputs into unconditional and conditional branches. This must be a list of exact input kwargs
130
+ names that contain the batchwise-concatenated unconditional and conditional inputs.
131
+ """
132
+
133
+ # In the paper and codebase, they hardcode these values to 2. However, it can be made configurable
134
+ # after some testing. We default to 2 if these parameters are not provided.
135
+ spatial_attention_block_skip_range: int = 2
136
+ temporal_attention_block_skip_range: int | None = None
137
+
138
+ spatial_attention_timestep_skip_range: tuple[int, int] = (-1, 681)
139
+ temporal_attention_timestep_skip_range: tuple[int, int] = (-1, 681)
140
+
141
+ # Indicator functions for low/high frequency as mentioned in Equation 11 of the paper
142
+ low_frequency_weight_update_timestep_range: tuple[int, int] = (99, 901)
143
+ high_frequency_weight_update_timestep_range: tuple[int, int] = (-1, 301)
144
+
145
+ # ⍺1 and ⍺2 as mentioned in Equation 11 of the paper
146
+ alpha_low_frequency: float = 1.1
147
+ alpha_high_frequency: float = 1.1
148
+
149
+ # n as described in CFG-Cache explanation in the paper - dependent on the model
150
+ unconditional_batch_skip_range: int = 5
151
+ unconditional_batch_timestep_skip_range: tuple[int, int] = (-1, 641)
152
+
153
+ spatial_attention_block_identifiers: tuple[str, ...] = _SPATIAL_ATTENTION_BLOCK_IDENTIFIERS
154
+ temporal_attention_block_identifiers: tuple[str, ...] = _TEMPORAL_ATTENTION_BLOCK_IDENTIFIERS
155
+
156
+ attention_weight_callback: Callable[[torch.nn.Module], float] = None
157
+ low_frequency_weight_callback: Callable[[torch.nn.Module], float] = None
158
+ high_frequency_weight_callback: Callable[[torch.nn.Module], float] = None
159
+
160
+ tensor_format: str = "BCFHW"
161
+ is_guidance_distilled: bool = False
162
+
163
+ current_timestep_callback: Callable[[], int] = None
164
+
165
+ _unconditional_conditional_input_kwargs_identifiers: list[str] = _UNCOND_COND_INPUT_KWARGS_IDENTIFIERS
166
+
167
+ def __repr__(self) -> str:
168
+ return (
169
+ f"FasterCacheConfig(\n"
170
+ f" spatial_attention_block_skip_range={self.spatial_attention_block_skip_range},\n"
171
+ f" temporal_attention_block_skip_range={self.temporal_attention_block_skip_range},\n"
172
+ f" spatial_attention_timestep_skip_range={self.spatial_attention_timestep_skip_range},\n"
173
+ f" temporal_attention_timestep_skip_range={self.temporal_attention_timestep_skip_range},\n"
174
+ f" low_frequency_weight_update_timestep_range={self.low_frequency_weight_update_timestep_range},\n"
175
+ f" high_frequency_weight_update_timestep_range={self.high_frequency_weight_update_timestep_range},\n"
176
+ f" alpha_low_frequency={self.alpha_low_frequency},\n"
177
+ f" alpha_high_frequency={self.alpha_high_frequency},\n"
178
+ f" unconditional_batch_skip_range={self.unconditional_batch_skip_range},\n"
179
+ f" unconditional_batch_timestep_skip_range={self.unconditional_batch_timestep_skip_range},\n"
180
+ f" spatial_attention_block_identifiers={self.spatial_attention_block_identifiers},\n"
181
+ f" temporal_attention_block_identifiers={self.temporal_attention_block_identifiers},\n"
182
+ f" tensor_format={self.tensor_format},\n"
183
+ f")"
184
+ )
185
+
186
+
187
+ class FasterCacheDenoiserState:
188
+ r"""
189
+ State for [FasterCache](https://huggingface.co/papers/2410.19355) top-level denoiser module.
190
+ """
191
+
192
+ def __init__(self) -> None:
193
+ self.iteration: int = 0
194
+ self.low_frequency_delta: torch.Tensor = None
195
+ self.high_frequency_delta: torch.Tensor = None
196
+
197
+ def reset(self):
198
+ self.iteration = 0
199
+ self.low_frequency_delta = None
200
+ self.high_frequency_delta = None
201
+
202
+
203
+ class FasterCacheBlockState:
204
+ r"""
205
+ State for [FasterCache](https://huggingface.co/papers/2410.19355). Every underlying block that FasterCache is
206
+ applied to will have an instance of this state.
207
+ """
208
+
209
+ def __init__(self) -> None:
210
+ self.iteration: int = 0
211
+ self.batch_size: int = None
212
+ self.cache: tuple[torch.Tensor, torch.Tensor] = None
213
+
214
+ def reset(self):
215
+ self.iteration = 0
216
+ self.batch_size = None
217
+ self.cache = None
218
+
219
+
220
+ class FasterCacheDenoiserHook(ModelHook):
221
+ _is_stateful = True
222
+
223
+ def __init__(
224
+ self,
225
+ unconditional_batch_skip_range: int,
226
+ unconditional_batch_timestep_skip_range: tuple[int, int],
227
+ tensor_format: str,
228
+ is_guidance_distilled: bool,
229
+ uncond_cond_input_kwargs_identifiers: list[str],
230
+ current_timestep_callback: Callable[[], int],
231
+ low_frequency_weight_callback: Callable[[torch.nn.Module], torch.Tensor],
232
+ high_frequency_weight_callback: Callable[[torch.nn.Module], torch.Tensor],
233
+ ) -> None:
234
+ super().__init__()
235
+
236
+ self.unconditional_batch_skip_range = unconditional_batch_skip_range
237
+ self.unconditional_batch_timestep_skip_range = unconditional_batch_timestep_skip_range
238
+ # We can't easily detect what args are to be split in unconditional and conditional branches. We
239
+ # can only do it for kwargs, hence they are the only ones we split. The args are passed as-is.
240
+ # If a model is to be made compatible with FasterCache, the user must ensure that the inputs that
241
+ # contain batchwise-concatenated unconditional and conditional inputs are passed as kwargs.
242
+ self.uncond_cond_input_kwargs_identifiers = uncond_cond_input_kwargs_identifiers
243
+ self.tensor_format = tensor_format
244
+ self.is_guidance_distilled = is_guidance_distilled
245
+
246
+ self.current_timestep_callback = current_timestep_callback
247
+ self.low_frequency_weight_callback = low_frequency_weight_callback
248
+ self.high_frequency_weight_callback = high_frequency_weight_callback
249
+
250
+ def initialize_hook(self, module):
251
+ self.state = FasterCacheDenoiserState()
252
+ return module
253
+
254
+ @staticmethod
255
+ def _get_cond_input(input: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
256
+ # Note: this method assumes that the input tensor is batchwise-concatenated with unconditional inputs
257
+ # followed by conditional inputs.
258
+ _, cond = input.chunk(2, dim=0)
259
+ return cond
260
+
261
+ def new_forward(self, module: torch.nn.Module, *args, **kwargs) -> Any:
262
+ # Split the unconditional and conditional inputs. We only want to infer the conditional branch if the
263
+ # requirements for skipping the unconditional branch are met as described in the paper.
264
+ # We skip the unconditional branch only if the following conditions are met:
265
+ # 1. We have completed at least one iteration of the denoiser
266
+ # 2. The current timestep is within the range specified by the user. This is the optimal timestep range
267
+ # where approximating the unconditional branch from the computation of the conditional branch is possible
268
+ # without a significant loss in quality.
269
+ # 3. The current iteration is not a multiple of the unconditional batch skip range. This is done so that
270
+ # we compute the unconditional branch at least once every few iterations to ensure minimal quality loss.
271
+ is_within_timestep_range = (
272
+ self.unconditional_batch_timestep_skip_range[0]
273
+ < self.current_timestep_callback()
274
+ < self.unconditional_batch_timestep_skip_range[1]
275
+ )
276
+ should_skip_uncond = (
277
+ self.state.iteration > 0
278
+ and is_within_timestep_range
279
+ and self.state.iteration % self.unconditional_batch_skip_range != 0
280
+ and not self.is_guidance_distilled
281
+ )
282
+
283
+ if should_skip_uncond:
284
+ is_any_kwarg_uncond = any(k in self.uncond_cond_input_kwargs_identifiers for k in kwargs.keys())
285
+ if is_any_kwarg_uncond:
286
+ logger.debug("FasterCache - Skipping unconditional branch computation")
287
+ args = tuple([self._get_cond_input(arg) if torch.is_tensor(arg) else arg for arg in args])
288
+ kwargs = {
289
+ k: v if k not in self.uncond_cond_input_kwargs_identifiers else self._get_cond_input(v)
290
+ for k, v in kwargs.items()
291
+ }
292
+
293
+ output = self.fn_ref.original_forward(*args, **kwargs)
294
+
295
+ if self.is_guidance_distilled:
296
+ self.state.iteration += 1
297
+ return output
298
+
299
+ if torch.is_tensor(output):
300
+ hidden_states = output
301
+ elif isinstance(output, (tuple, Transformer2DModelOutput)):
302
+ hidden_states = output[0]
303
+
304
+ batch_size = hidden_states.size(0)
305
+
306
+ if should_skip_uncond:
307
+ self.state.low_frequency_delta = self.state.low_frequency_delta * self.low_frequency_weight_callback(
308
+ module
309
+ )
310
+ self.state.high_frequency_delta = self.state.high_frequency_delta * self.high_frequency_weight_callback(
311
+ module
312
+ )
313
+
314
+ if self.tensor_format == "BCFHW":
315
+ hidden_states = hidden_states.permute(0, 2, 1, 3, 4)
316
+ if self.tensor_format == "BCFHW" or self.tensor_format == "BFCHW":
317
+ hidden_states = hidden_states.flatten(0, 1)
318
+
319
+ low_freq_cond, high_freq_cond = _split_low_high_freq(hidden_states.float())
320
+
321
+ # Approximate/compute the unconditional branch outputs as described in Equation 9 and 10 of the paper
322
+ low_freq_uncond = self.state.low_frequency_delta + low_freq_cond
323
+ high_freq_uncond = self.state.high_frequency_delta + high_freq_cond
324
+ uncond_freq = low_freq_uncond + high_freq_uncond
325
+
326
+ uncond_states = torch.fft.ifftshift(uncond_freq)
327
+ uncond_states = torch.fft.ifft2(uncond_states).real
328
+
329
+ if self.tensor_format == "BCFHW" or self.tensor_format == "BFCHW":
330
+ uncond_states = uncond_states.unflatten(0, (batch_size, -1))
331
+ hidden_states = hidden_states.unflatten(0, (batch_size, -1))
332
+ if self.tensor_format == "BCFHW":
333
+ uncond_states = uncond_states.permute(0, 2, 1, 3, 4)
334
+ hidden_states = hidden_states.permute(0, 2, 1, 3, 4)
335
+
336
+ # Concatenate the approximated unconditional and predicted conditional branches
337
+ uncond_states = uncond_states.to(hidden_states.dtype)
338
+ hidden_states = torch.cat([uncond_states, hidden_states], dim=0)
339
+ else:
340
+ uncond_states, cond_states = hidden_states.chunk(2, dim=0)
341
+ if self.tensor_format == "BCFHW":
342
+ uncond_states = uncond_states.permute(0, 2, 1, 3, 4)
343
+ cond_states = cond_states.permute(0, 2, 1, 3, 4)
344
+ if self.tensor_format == "BCFHW" or self.tensor_format == "BFCHW":
345
+ uncond_states = uncond_states.flatten(0, 1)
346
+ cond_states = cond_states.flatten(0, 1)
347
+
348
+ low_freq_uncond, high_freq_uncond = _split_low_high_freq(uncond_states.float())
349
+ low_freq_cond, high_freq_cond = _split_low_high_freq(cond_states.float())
350
+ self.state.low_frequency_delta = low_freq_uncond - low_freq_cond
351
+ self.state.high_frequency_delta = high_freq_uncond - high_freq_cond
352
+
353
+ self.state.iteration += 1
354
+ if torch.is_tensor(output):
355
+ output = hidden_states
356
+ elif isinstance(output, tuple):
357
+ output = (hidden_states, *output[1:])
358
+ else:
359
+ output.sample = hidden_states
360
+
361
+ return output
362
+
363
+ def reset_state(self, module: torch.nn.Module) -> torch.nn.Module:
364
+ self.state.reset()
365
+ return module
366
+
367
+
368
+ class FasterCacheBlockHook(ModelHook):
369
+ _is_stateful = True
370
+
371
+ def __init__(
372
+ self,
373
+ block_skip_range: int,
374
+ timestep_skip_range: tuple[int, int],
375
+ is_guidance_distilled: bool,
376
+ weight_callback: Callable[[torch.nn.Module], float],
377
+ current_timestep_callback: Callable[[], int],
378
+ ) -> None:
379
+ super().__init__()
380
+
381
+ self.block_skip_range = block_skip_range
382
+ self.timestep_skip_range = timestep_skip_range
383
+ self.is_guidance_distilled = is_guidance_distilled
384
+
385
+ self.weight_callback = weight_callback
386
+ self.current_timestep_callback = current_timestep_callback
387
+
388
+ def initialize_hook(self, module):
389
+ self.state = FasterCacheBlockState()
390
+ return module
391
+
392
+ def _compute_approximated_attention_output(
393
+ self, t_2_output: torch.Tensor, t_output: torch.Tensor, weight: float, batch_size: int
394
+ ) -> torch.Tensor:
395
+ if t_2_output.size(0) != batch_size:
396
+ # The cache t_2_output contains both batchwise-concatenated unconditional-conditional branch outputs. Just
397
+ # take the conditional branch outputs.
398
+ assert t_2_output.size(0) == 2 * batch_size
399
+ t_2_output = t_2_output[batch_size:]
400
+ if t_output.size(0) != batch_size:
401
+ # The cache t_output contains both batchwise-concatenated unconditional-conditional branch outputs. Just
402
+ # take the conditional branch outputs.
403
+ assert t_output.size(0) == 2 * batch_size
404
+ t_output = t_output[batch_size:]
405
+ return t_output + (t_output - t_2_output) * weight
406
+
407
+ def new_forward(self, module: torch.nn.Module, *args, **kwargs) -> Any:
408
+ batch_size = [
409
+ *[arg.size(0) for arg in args if torch.is_tensor(arg)],
410
+ *[v.size(0) for v in kwargs.values() if torch.is_tensor(v)],
411
+ ][0]
412
+ if self.state.batch_size is None:
413
+ # Will be updated on first forward pass through the denoiser
414
+ self.state.batch_size = batch_size
415
+
416
+ # If we have to skip due to the skip conditions, then let's skip as expected.
417
+ # But, we can't skip if the denoiser wants to infer both unconditional and conditional branches. This
418
+ # is because the expected output shapes of attention layer will not match if we only return values from
419
+ # the cache (which only caches conditional branch outputs). So, if state.batch_size (which is the true
420
+ # unconditional-conditional batch size) is same as the current batch size, we don't perform the layer
421
+ # skip. Otherwise, we conditionally skip the layer based on what state.skip_callback returns.
422
+ is_within_timestep_range = (
423
+ self.timestep_skip_range[0] < self.current_timestep_callback() < self.timestep_skip_range[1]
424
+ )
425
+ if not is_within_timestep_range:
426
+ should_skip_attention = False
427
+ else:
428
+ should_compute_attention = self.state.iteration > 0 and self.state.iteration % self.block_skip_range == 0
429
+ should_skip_attention = not should_compute_attention
430
+ if should_skip_attention:
431
+ should_skip_attention = self.is_guidance_distilled or self.state.batch_size != batch_size
432
+
433
+ if should_skip_attention:
434
+ logger.debug("FasterCache - Skipping attention and using approximation")
435
+ if torch.is_tensor(self.state.cache[-1]):
436
+ t_2_output, t_output = self.state.cache
437
+ weight = self.weight_callback(module)
438
+ output = self._compute_approximated_attention_output(t_2_output, t_output, weight, batch_size)
439
+ else:
440
+ # The cache contains multiple tensors from past N iterations (N=2 for FasterCache). We need to handle all of them.
441
+ # Diffusers blocks can return multiple tensors - let's call them [A, B, C, ...] for simplicity.
442
+ # In our cache, we would have [[A_1, B_1, C_1, ...], [A_2, B_2, C_2, ...], ...] where each list is the output from
443
+ # a forward pass of the block. We need to compute the approximated output for each of these tensors.
444
+ # The zip(*state.cache) operation will give us [(A_1, A_2, ...), (B_1, B_2, ...), (C_1, C_2, ...), ...] which
445
+ # allows us to compute the approximated attention output for each tensor in the cache.
446
+ output = ()
447
+ for t_2_output, t_output in zip(*self.state.cache):
448
+ result = self._compute_approximated_attention_output(
449
+ t_2_output, t_output, self.weight_callback(module), batch_size
450
+ )
451
+ output += (result,)
452
+ else:
453
+ logger.debug("FasterCache - Computing attention")
454
+ output = self.fn_ref.original_forward(*args, **kwargs)
455
+
456
+ # Note that the following condition for getting hidden_states should suffice since Diffusers blocks either return
457
+ # a single hidden_states tensor, or a tuple of (hidden_states, encoder_hidden_states) tensors. We need to handle
458
+ # both cases.
459
+ if torch.is_tensor(output):
460
+ cache_output = output
461
+ if not self.is_guidance_distilled and cache_output.size(0) == self.state.batch_size:
462
+ # The output here can be both unconditional-conditional branch outputs or just conditional branch outputs.
463
+ # This is determined at the higher-level denoiser module. We only want to cache the conditional branch outputs.
464
+ cache_output = cache_output.chunk(2, dim=0)[1]
465
+ else:
466
+ # Cache all return values and perform the same operation as above
467
+ cache_output = ()
468
+ for out in output:
469
+ if not self.is_guidance_distilled and out.size(0) == self.state.batch_size:
470
+ out = out.chunk(2, dim=0)[1]
471
+ cache_output += (out,)
472
+
473
+ if self.state.cache is None:
474
+ self.state.cache = [cache_output, cache_output]
475
+ else:
476
+ self.state.cache = [self.state.cache[-1], cache_output]
477
+
478
+ self.state.iteration += 1
479
+ return output
480
+
481
+ def reset_state(self, module: torch.nn.Module) -> torch.nn.Module:
482
+ self.state.reset()
483
+ return module
484
+
485
+
486
+ def apply_faster_cache(module: torch.nn.Module, config: FasterCacheConfig) -> None:
487
+ r"""
488
+ Applies [FasterCache](https://huggingface.co/papers/2410.19355) to a given pipeline.
489
+
490
+ Args:
491
+ module (`torch.nn.Module`):
492
+ The pytorch module to apply FasterCache to. Typically, this should be a transformer architecture supported
493
+ in Diffusers, such as `CogVideoXTransformer3DModel`, but external implementations may also work.
494
+ config (`FasterCacheConfig`):
495
+ The configuration to use for FasterCache.
496
+
497
+ Example:
498
+ ```python
499
+ >>> import torch
500
+ >>> from diffusers import CogVideoXPipeline, FasterCacheConfig, apply_faster_cache
501
+
502
+ >>> pipe = CogVideoXPipeline.from_pretrained("THUDM/CogVideoX-5b", torch_dtype=torch.bfloat16)
503
+ >>> pipe.to("cuda")
504
+
505
+ >>> config = FasterCacheConfig(
506
+ ... spatial_attention_block_skip_range=2,
507
+ ... spatial_attention_timestep_skip_range=(-1, 681),
508
+ ... low_frequency_weight_update_timestep_range=(99, 641),
509
+ ... high_frequency_weight_update_timestep_range=(-1, 301),
510
+ ... spatial_attention_block_identifiers=["transformer_blocks"],
511
+ ... attention_weight_callback=lambda _: 0.3,
512
+ ... tensor_format="BFCHW",
513
+ ... )
514
+ >>> apply_faster_cache(pipe.transformer, config)
515
+ ```
516
+ """
517
+
518
+ logger.warning(
519
+ "FasterCache is a purely experimental feature and may not work as expected. Not all models support FasterCache. "
520
+ "The API is subject to change in future releases, with no guarantee of backward compatibility. Please report any issues at "
521
+ "https://github.com/huggingface/diffusers/issues."
522
+ )
523
+
524
+ if config.attention_weight_callback is None:
525
+ # If the user has not provided a weight callback, we default to 0.5 for all timesteps.
526
+ # In the paper, they recommend using a gradually increasing weight from 0 to 1 as the inference progresses, but
527
+ # this depends from model-to-model. It is required by the user to provide a weight callback if they want to
528
+ # use a different weight function. Defaulting to 0.5 works well in practice for most cases.
529
+ logger.warning(
530
+ "No `attention_weight_callback` provided when enabling FasterCache. Defaulting to using a weight of 0.5 for all timesteps."
531
+ )
532
+ config.attention_weight_callback = lambda _: 0.5
533
+
534
+ if config.low_frequency_weight_callback is None:
535
+ logger.debug(
536
+ "Low frequency weight callback not provided when enabling FasterCache. Defaulting to behaviour described in the paper."
537
+ )
538
+
539
+ def low_frequency_weight_callback(module: torch.nn.Module) -> float:
540
+ is_within_range = (
541
+ config.low_frequency_weight_update_timestep_range[0]
542
+ < config.current_timestep_callback()
543
+ < config.low_frequency_weight_update_timestep_range[1]
544
+ )
545
+ return config.alpha_low_frequency if is_within_range else 1.0
546
+
547
+ config.low_frequency_weight_callback = low_frequency_weight_callback
548
+
549
+ if config.high_frequency_weight_callback is None:
550
+ logger.debug(
551
+ "High frequency weight callback not provided when enabling FasterCache. Defaulting to behaviour described in the paper."
552
+ )
553
+
554
+ def high_frequency_weight_callback(module: torch.nn.Module) -> float:
555
+ is_within_range = (
556
+ config.high_frequency_weight_update_timestep_range[0]
557
+ < config.current_timestep_callback()
558
+ < config.high_frequency_weight_update_timestep_range[1]
559
+ )
560
+ return config.alpha_high_frequency if is_within_range else 1.0
561
+
562
+ config.high_frequency_weight_callback = high_frequency_weight_callback
563
+
564
+ supported_tensor_formats = ["BCFHW", "BFCHW", "BCHW"] # TODO(aryan): Support BSC for LTX Video
565
+ if config.tensor_format not in supported_tensor_formats:
566
+ raise ValueError(f"`tensor_format` must be one of {supported_tensor_formats}, but got {config.tensor_format}.")
567
+
568
+ _apply_faster_cache_on_denoiser(module, config)
569
+
570
+ for name, submodule in module.named_modules():
571
+ if not isinstance(submodule, _ATTENTION_CLASSES):
572
+ continue
573
+ if any(re.search(identifier, name) is not None for identifier in _TRANSFORMER_BLOCK_IDENTIFIERS):
574
+ _apply_faster_cache_on_attention_class(name, submodule, config)
575
+
576
+
577
+ def _apply_faster_cache_on_denoiser(module: torch.nn.Module, config: FasterCacheConfig) -> None:
578
+ hook = FasterCacheDenoiserHook(
579
+ config.unconditional_batch_skip_range,
580
+ config.unconditional_batch_timestep_skip_range,
581
+ config.tensor_format,
582
+ config.is_guidance_distilled,
583
+ config._unconditional_conditional_input_kwargs_identifiers,
584
+ config.current_timestep_callback,
585
+ config.low_frequency_weight_callback,
586
+ config.high_frequency_weight_callback,
587
+ )
588
+ registry = HookRegistry.check_if_exists_or_initialize(module)
589
+ registry.register_hook(hook, _FASTER_CACHE_DENOISER_HOOK)
590
+
591
+
592
+ def _apply_faster_cache_on_attention_class(name: str, module: AttentionModuleMixin, config: FasterCacheConfig) -> None:
593
+ is_spatial_self_attention = (
594
+ any(re.search(identifier, name) is not None for identifier in config.spatial_attention_block_identifiers)
595
+ and config.spatial_attention_block_skip_range is not None
596
+ and not getattr(module, "is_cross_attention", False)
597
+ )
598
+ is_temporal_self_attention = (
599
+ any(re.search(identifier, name) is not None for identifier in config.temporal_attention_block_identifiers)
600
+ and config.temporal_attention_block_skip_range is not None
601
+ and not module.is_cross_attention
602
+ )
603
+
604
+ block_skip_range, timestep_skip_range, block_type = None, None, None
605
+ if is_spatial_self_attention:
606
+ block_skip_range = config.spatial_attention_block_skip_range
607
+ timestep_skip_range = config.spatial_attention_timestep_skip_range
608
+ block_type = "spatial"
609
+ elif is_temporal_self_attention:
610
+ block_skip_range = config.temporal_attention_block_skip_range
611
+ timestep_skip_range = config.temporal_attention_timestep_skip_range
612
+ block_type = "temporal"
613
+
614
+ if block_skip_range is None or timestep_skip_range is None:
615
+ logger.debug(
616
+ f'Unable to apply FasterCache to the selected layer: "{name}" because it does '
617
+ f"not match any of the required criteria for spatial or temporal attention layers. Note, "
618
+ f"however, that this layer may still be valid for applying PAB. Please specify the correct "
619
+ f"block identifiers in the configuration or use the specialized `apply_faster_cache_on_module` "
620
+ f"function to apply FasterCache to this layer."
621
+ )
622
+ return
623
+
624
+ logger.debug(f"Enabling FasterCache ({block_type}) for layer: {name}")
625
+ hook = FasterCacheBlockHook(
626
+ block_skip_range,
627
+ timestep_skip_range,
628
+ config.is_guidance_distilled,
629
+ config.attention_weight_callback,
630
+ config.current_timestep_callback,
631
+ )
632
+ registry = HookRegistry.check_if_exists_or_initialize(module)
633
+ registry.register_hook(hook, _FASTER_CACHE_BLOCK_HOOK)
634
+
635
+
636
+ # Reference: https://github.com/Vchitect/FasterCache/blob/fab32c15014636dc854948319c0a9a8d92c7acb4/scripts/latte/faster_cache_sample_latte.py#L127C1-L143C39
637
+ @torch.no_grad()
638
+ def _split_low_high_freq(x):
639
+ fft = torch.fft.fft2(x)
640
+ fft_shifted = torch.fft.fftshift(fft)
641
+ height, width = x.shape[-2:]
642
+ radius = min(height, width) // 5
643
+
644
+ y_grid, x_grid = torch.meshgrid(torch.arange(height), torch.arange(width))
645
+ center_x, center_y = width // 2, height // 2
646
+ mask = (x_grid - center_x) ** 2 + (y_grid - center_y) ** 2 <= radius**2
647
+
648
+ low_freq_mask = mask.unsqueeze(0).unsqueeze(0).to(x.device)
649
+ high_freq_mask = ~low_freq_mask
650
+
651
+ low_freq_fft = fft_shifted * low_freq_mask
652
+ high_freq_fft = fft_shifted * high_freq_mask
653
+
654
+ return low_freq_fft, high_freq_fft
third_party/diffusers/src/diffusers/hooks/first_block_cache.py ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2024 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from dataclasses import dataclass
16
+
17
+ import torch
18
+
19
+ from ..utils import get_logger
20
+ from ..utils.torch_utils import unwrap_module
21
+ from ._common import _ALL_TRANSFORMER_BLOCK_IDENTIFIERS
22
+ from ._helpers import TransformerBlockRegistry
23
+ from .hooks import BaseState, HookRegistry, ModelHook, StateManager
24
+
25
+
26
+ logger = get_logger(__name__) # pylint: disable=invalid-name
27
+
28
+ _FBC_LEADER_BLOCK_HOOK = "fbc_leader_block_hook"
29
+ _FBC_BLOCK_HOOK = "fbc_block_hook"
30
+
31
+
32
+ @dataclass
33
+ class FirstBlockCacheConfig:
34
+ r"""
35
+ Configuration for [First Block
36
+ Cache](https://github.com/chengzeyi/ParaAttention/blob/7a266123671b55e7e5a2fe9af3121f07a36afc78/README.md#first-block-cache-our-dynamic-caching).
37
+
38
+ Args:
39
+ threshold (`float`, defaults to `0.05`):
40
+ The threshold to determine whether or not a forward pass through all layers of the model is required. A
41
+ higher threshold usually results in a forward pass through a lower number of layers and faster inference,
42
+ but might lead to poorer generation quality. A lower threshold may not result in significant generation
43
+ speedup. The threshold is compared against the absmean difference of the residuals between the current and
44
+ cached outputs from the first transformer block. If the difference is below the threshold, the forward pass
45
+ is skipped.
46
+ """
47
+
48
+ threshold: float = 0.05
49
+
50
+
51
+ class FBCSharedBlockState(BaseState):
52
+ def __init__(self) -> None:
53
+ super().__init__()
54
+
55
+ self.head_block_output: torch.Tensor | tuple[torch.Tensor, ...] = None
56
+ self.head_block_residual: torch.Tensor = None
57
+ self.tail_block_residuals: torch.Tensor | tuple[torch.Tensor, ...] = None
58
+ self.should_compute: bool = True
59
+
60
+ def reset(self):
61
+ self.tail_block_residuals = None
62
+ self.should_compute = True
63
+
64
+
65
+ class FBCHeadBlockHook(ModelHook):
66
+ _is_stateful = True
67
+
68
+ def __init__(self, state_manager: StateManager, threshold: float):
69
+ self.state_manager = state_manager
70
+ self.threshold = threshold
71
+ self._metadata = None
72
+
73
+ def initialize_hook(self, module):
74
+ unwrapped_module = unwrap_module(module)
75
+ self._metadata = TransformerBlockRegistry.get(unwrapped_module.__class__)
76
+ return module
77
+
78
+ def new_forward(self, module: torch.nn.Module, *args, **kwargs):
79
+ original_hidden_states = self._metadata._get_parameter_from_args_kwargs("hidden_states", args, kwargs)
80
+
81
+ output = self.fn_ref.original_forward(*args, **kwargs)
82
+ is_output_tuple = isinstance(output, tuple)
83
+
84
+ if is_output_tuple:
85
+ hidden_states_residual = output[self._metadata.return_hidden_states_index] - original_hidden_states
86
+ else:
87
+ hidden_states_residual = output - original_hidden_states
88
+
89
+ shared_state: FBCSharedBlockState = self.state_manager.get_state()
90
+ hidden_states = encoder_hidden_states = None
91
+ should_compute = self._should_compute_remaining_blocks(hidden_states_residual)
92
+ shared_state.should_compute = should_compute
93
+
94
+ if not should_compute:
95
+ # Apply caching
96
+ if is_output_tuple:
97
+ hidden_states = (
98
+ shared_state.tail_block_residuals[0] + output[self._metadata.return_hidden_states_index]
99
+ )
100
+ else:
101
+ hidden_states = shared_state.tail_block_residuals[0] + output
102
+
103
+ if self._metadata.return_encoder_hidden_states_index is not None:
104
+ assert is_output_tuple
105
+ encoder_hidden_states = (
106
+ shared_state.tail_block_residuals[1] + output[self._metadata.return_encoder_hidden_states_index]
107
+ )
108
+
109
+ if is_output_tuple:
110
+ return_output = [None] * len(output)
111
+ return_output[self._metadata.return_hidden_states_index] = hidden_states
112
+ return_output[self._metadata.return_encoder_hidden_states_index] = encoder_hidden_states
113
+ return_output = tuple(return_output)
114
+ else:
115
+ return_output = hidden_states
116
+ output = return_output
117
+ else:
118
+ if is_output_tuple:
119
+ head_block_output = [None] * len(output)
120
+ head_block_output[0] = output[self._metadata.return_hidden_states_index]
121
+ head_block_output[1] = output[self._metadata.return_encoder_hidden_states_index]
122
+ else:
123
+ head_block_output = output
124
+ shared_state.head_block_output = head_block_output
125
+ shared_state.head_block_residual = hidden_states_residual
126
+
127
+ return output
128
+
129
+ def reset_state(self, module):
130
+ self.state_manager.reset()
131
+ return module
132
+
133
+ @torch.compiler.disable
134
+ def _should_compute_remaining_blocks(self, hidden_states_residual: torch.Tensor) -> bool:
135
+ shared_state = self.state_manager.get_state()
136
+ if shared_state.head_block_residual is None:
137
+ return True
138
+ prev_hidden_states_residual = shared_state.head_block_residual
139
+ absmean = (hidden_states_residual - prev_hidden_states_residual).abs().mean()
140
+ prev_hidden_states_absmean = prev_hidden_states_residual.abs().mean()
141
+ diff = (absmean / prev_hidden_states_absmean).item()
142
+ return diff > self.threshold
143
+
144
+
145
+ class FBCBlockHook(ModelHook):
146
+ def __init__(self, state_manager: StateManager, is_tail: bool = False):
147
+ super().__init__()
148
+ self.state_manager = state_manager
149
+ self.is_tail = is_tail
150
+ self._metadata = None
151
+
152
+ def initialize_hook(self, module):
153
+ unwrapped_module = unwrap_module(module)
154
+ self._metadata = TransformerBlockRegistry.get(unwrapped_module.__class__)
155
+ return module
156
+
157
+ def new_forward(self, module: torch.nn.Module, *args, **kwargs):
158
+ original_hidden_states = self._metadata._get_parameter_from_args_kwargs("hidden_states", args, kwargs)
159
+ original_encoder_hidden_states = None
160
+ if self._metadata.return_encoder_hidden_states_index is not None:
161
+ original_encoder_hidden_states = self._metadata._get_parameter_from_args_kwargs(
162
+ "encoder_hidden_states", args, kwargs
163
+ )
164
+
165
+ shared_state = self.state_manager.get_state()
166
+
167
+ if shared_state.should_compute:
168
+ output = self.fn_ref.original_forward(*args, **kwargs)
169
+ if self.is_tail:
170
+ hidden_states_residual = encoder_hidden_states_residual = None
171
+ if isinstance(output, tuple):
172
+ hidden_states_residual = (
173
+ output[self._metadata.return_hidden_states_index] - shared_state.head_block_output[0]
174
+ )
175
+ encoder_hidden_states_residual = (
176
+ output[self._metadata.return_encoder_hidden_states_index] - shared_state.head_block_output[1]
177
+ )
178
+ else:
179
+ hidden_states_residual = output - shared_state.head_block_output
180
+ shared_state.tail_block_residuals = (hidden_states_residual, encoder_hidden_states_residual)
181
+ return output
182
+
183
+ if original_encoder_hidden_states is None:
184
+ return_output = original_hidden_states
185
+ else:
186
+ return_output = [None, None]
187
+ return_output[self._metadata.return_hidden_states_index] = original_hidden_states
188
+ return_output[self._metadata.return_encoder_hidden_states_index] = original_encoder_hidden_states
189
+ return_output = tuple(return_output)
190
+ return return_output
191
+
192
+
193
+ def apply_first_block_cache(module: torch.nn.Module, config: FirstBlockCacheConfig) -> None:
194
+ """
195
+ Applies [First Block
196
+ Cache](https://github.com/chengzeyi/ParaAttention/blob/4de137c5b96416489f06e43e19f2c14a772e28fd/README.md#first-block-cache-our-dynamic-caching)
197
+ to a given module.
198
+
199
+ First Block Cache builds on the ideas of [TeaCache](https://huggingface.co/papers/2411.19108). It is much simpler
200
+ to implement generically for a wide range of models and has been integrated first for experimental purposes.
201
+
202
+ Args:
203
+ module (`torch.nn.Module`):
204
+ The pytorch module to apply FBCache to. Typically, this should be a transformer architecture supported in
205
+ Diffusers, such as `CogVideoXTransformer3DModel`, but external implementations may also work.
206
+ config (`FirstBlockCacheConfig`):
207
+ The configuration to use for applying the FBCache method.
208
+
209
+ Example:
210
+ ```python
211
+ >>> import torch
212
+ >>> from diffusers import CogView4Pipeline
213
+ >>> from diffusers.hooks import apply_first_block_cache, FirstBlockCacheConfig
214
+
215
+ >>> pipe = CogView4Pipeline.from_pretrained("THUDM/CogView4-6B", torch_dtype=torch.bfloat16)
216
+ >>> pipe.to("cuda")
217
+
218
+ >>> apply_first_block_cache(pipe.transformer, FirstBlockCacheConfig(threshold=0.2))
219
+
220
+ >>> prompt = "A photo of an astronaut riding a horse on mars"
221
+ >>> image = pipe(prompt, generator=torch.Generator().manual_seed(42)).images[0]
222
+ >>> image.save("output.png")
223
+ ```
224
+ """
225
+
226
+ state_manager = StateManager(FBCSharedBlockState, (), {})
227
+ remaining_blocks = []
228
+
229
+ for name, submodule in module.named_children():
230
+ if name not in _ALL_TRANSFORMER_BLOCK_IDENTIFIERS or not isinstance(submodule, torch.nn.ModuleList):
231
+ continue
232
+ for index, block in enumerate(submodule):
233
+ remaining_blocks.append((f"{name}.{index}", block))
234
+
235
+ head_block_name, head_block = remaining_blocks.pop(0)
236
+ tail_block_name, tail_block = remaining_blocks.pop(-1)
237
+
238
+ logger.debug(f"Applying FBCHeadBlockHook to '{head_block_name}'")
239
+ _apply_fbc_head_block_hook(head_block, state_manager, config.threshold)
240
+
241
+ for name, block in remaining_blocks:
242
+ logger.debug(f"Applying FBCBlockHook to '{name}'")
243
+ _apply_fbc_block_hook(block, state_manager)
244
+
245
+ logger.debug(f"Applying FBCBlockHook to tail block '{tail_block_name}'")
246
+ _apply_fbc_block_hook(tail_block, state_manager, is_tail=True)
247
+
248
+
249
+ def _apply_fbc_head_block_hook(block: torch.nn.Module, state_manager: StateManager, threshold: float) -> None:
250
+ registry = HookRegistry.check_if_exists_or_initialize(block)
251
+ hook = FBCHeadBlockHook(state_manager, threshold)
252
+ registry.register_hook(hook, _FBC_LEADER_BLOCK_HOOK)
253
+
254
+
255
+ def _apply_fbc_block_hook(block: torch.nn.Module, state_manager: StateManager, is_tail: bool = False) -> None:
256
+ registry = HookRegistry.check_if_exists_or_initialize(block)
257
+ hook = FBCBlockHook(state_manager, is_tail)
258
+ registry.register_hook(hook, _FBC_BLOCK_HOOK)
third_party/diffusers/src/diffusers/hooks/group_offloading.py ADDED
@@ -0,0 +1,966 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import hashlib
16
+ import os
17
+ from contextlib import contextmanager, nullcontext
18
+ from dataclasses import dataclass, replace
19
+ from enum import Enum
20
+ from typing import Set
21
+
22
+ import safetensors.torch
23
+ import torch
24
+
25
+ from ..utils import get_logger, is_accelerate_available
26
+ from ._common import _GO_LC_SUPPORTED_PYTORCH_LAYERS
27
+ from .hooks import HookRegistry, ModelHook
28
+
29
+
30
+ if is_accelerate_available():
31
+ from accelerate.hooks import AlignDevicesHook, CpuOffload
32
+ from accelerate.utils import send_to_device
33
+
34
+
35
+ logger = get_logger(__name__) # pylint: disable=invalid-name
36
+
37
+
38
+ # fmt: off
39
+ _GROUP_OFFLOADING = "group_offloading"
40
+ _LAYER_EXECUTION_TRACKER = "layer_execution_tracker"
41
+ _LAZY_PREFETCH_GROUP_OFFLOADING = "lazy_prefetch_group_offloading"
42
+ _GROUP_ID_LAZY_LEAF = "lazy_leafs"
43
+ # fmt: on
44
+
45
+
46
+ class GroupOffloadingType(str, Enum):
47
+ BLOCK_LEVEL = "block_level"
48
+ LEAF_LEVEL = "leaf_level"
49
+
50
+
51
+ @dataclass
52
+ class GroupOffloadingConfig:
53
+ onload_device: torch.device
54
+ offload_device: torch.device
55
+ offload_type: GroupOffloadingType
56
+ non_blocking: bool
57
+ record_stream: bool
58
+ low_cpu_mem_usage: bool
59
+ num_blocks_per_group: int | None = None
60
+ offload_to_disk_path: str | None = None
61
+ stream: torch.cuda.Stream | torch.Stream | None = None
62
+ block_modules: list[str] | None = None
63
+ exclude_kwargs: list[str] | None = None
64
+ module_prefix: str = ""
65
+
66
+
67
+ class ModuleGroup:
68
+ def __init__(
69
+ self,
70
+ modules: list[torch.nn.Module],
71
+ offload_device: torch.device,
72
+ onload_device: torch.device,
73
+ offload_leader: torch.nn.Module,
74
+ onload_leader: torch.nn.Module | None = None,
75
+ parameters: list[torch.nn.Parameter] | None = None,
76
+ buffers: list[torch.Tensor] | None = None,
77
+ non_blocking: bool = False,
78
+ stream: torch.cuda.Stream | torch.Stream | None = None,
79
+ record_stream: bool | None = False,
80
+ low_cpu_mem_usage: bool = False,
81
+ onload_self: bool = True,
82
+ offload_to_disk_path: str | None = None,
83
+ group_id: int | str | None = None,
84
+ ) -> None:
85
+ self.modules = modules
86
+ self.offload_device = offload_device
87
+ self.onload_device = onload_device
88
+ self.offload_leader = offload_leader
89
+ self.onload_leader = onload_leader
90
+ self.parameters = parameters or []
91
+ self.buffers = buffers or []
92
+ self.non_blocking = non_blocking or stream is not None
93
+ self.stream = stream
94
+ self.record_stream = record_stream
95
+ self.onload_self = onload_self
96
+ self.low_cpu_mem_usage = low_cpu_mem_usage
97
+
98
+ self.offload_to_disk_path = offload_to_disk_path
99
+ self._is_offloaded_to_disk = False
100
+
101
+ if self.offload_to_disk_path is not None:
102
+ # Instead of `group_id or str(id(self))` we do this because `group_id` can be "" as well.
103
+ self.group_id = group_id if group_id is not None else str(id(self))
104
+ short_hash = _compute_group_hash(self.group_id)
105
+ self.safetensors_file_path = os.path.join(self.offload_to_disk_path, f"group_{short_hash}.safetensors")
106
+
107
+ all_tensors = []
108
+ for module in self.modules:
109
+ all_tensors.extend(list(module.parameters()))
110
+ all_tensors.extend(list(module.buffers()))
111
+ all_tensors.extend(self.parameters)
112
+ all_tensors.extend(self.buffers)
113
+ all_tensors = list(dict.fromkeys(all_tensors)) # Remove duplicates
114
+
115
+ self.tensor_to_key = {tensor: f"tensor_{i}" for i, tensor in enumerate(all_tensors)}
116
+ self.key_to_tensor = {v: k for k, v in self.tensor_to_key.items()}
117
+ self.cpu_param_dict = {}
118
+ else:
119
+ self.cpu_param_dict = self._init_cpu_param_dict()
120
+
121
+ self._torch_accelerator_module = (
122
+ getattr(torch, torch.accelerator.current_accelerator().type)
123
+ if hasattr(torch, "accelerator")
124
+ else torch.cuda
125
+ )
126
+
127
+ def _init_cpu_param_dict(self):
128
+ cpu_param_dict = {}
129
+ if self.stream is None:
130
+ return cpu_param_dict
131
+
132
+ for module in self.modules:
133
+ for param in module.parameters():
134
+ cpu_param_dict[param] = param.data.cpu() if self.low_cpu_mem_usage else param.data.cpu().pin_memory()
135
+ for buffer in module.buffers():
136
+ cpu_param_dict[buffer] = (
137
+ buffer.data.cpu() if self.low_cpu_mem_usage else buffer.data.cpu().pin_memory()
138
+ )
139
+
140
+ for param in self.parameters:
141
+ cpu_param_dict[param] = param.data.cpu() if self.low_cpu_mem_usage else param.data.cpu().pin_memory()
142
+
143
+ for buffer in self.buffers:
144
+ cpu_param_dict[buffer] = buffer.data.cpu() if self.low_cpu_mem_usage else buffer.data.cpu().pin_memory()
145
+
146
+ return cpu_param_dict
147
+
148
+ @contextmanager
149
+ def _pinned_memory_tensors(self):
150
+ try:
151
+ pinned_dict = {
152
+ param: tensor.pin_memory() if not tensor.is_pinned() else tensor
153
+ for param, tensor in self.cpu_param_dict.items()
154
+ }
155
+ yield pinned_dict
156
+ finally:
157
+ pinned_dict = None
158
+
159
+ def _transfer_tensor_to_device(self, tensor, source_tensor, default_stream):
160
+ tensor.data = source_tensor.to(self.onload_device, non_blocking=self.non_blocking)
161
+ if self.record_stream:
162
+ tensor.data.record_stream(default_stream)
163
+
164
+ def _process_tensors_from_modules(self, pinned_memory=None, default_stream=None):
165
+ for group_module in self.modules:
166
+ for param in group_module.parameters():
167
+ source = pinned_memory[param] if pinned_memory else param.data
168
+ self._transfer_tensor_to_device(param, source, default_stream)
169
+ for buffer in group_module.buffers():
170
+ source = pinned_memory[buffer] if pinned_memory else buffer.data
171
+ self._transfer_tensor_to_device(buffer, source, default_stream)
172
+
173
+ for param in self.parameters:
174
+ source = pinned_memory[param] if pinned_memory else param.data
175
+ self._transfer_tensor_to_device(param, source, default_stream)
176
+
177
+ for buffer in self.buffers:
178
+ source = pinned_memory[buffer] if pinned_memory else buffer.data
179
+ self._transfer_tensor_to_device(buffer, source, default_stream)
180
+
181
+ def _onload_from_disk(self):
182
+ if self.stream is not None:
183
+ # Wait for previous Host->Device transfer to complete
184
+ self.stream.synchronize()
185
+
186
+ context = nullcontext() if self.stream is None else self._torch_accelerator_module.stream(self.stream)
187
+ current_stream = self._torch_accelerator_module.current_stream() if self.record_stream else None
188
+
189
+ with context:
190
+ # Load to CPU (if using streams) or directly to target device, pin, and async copy to device
191
+ device = str(self.onload_device) if self.stream is None else "cpu"
192
+ loaded_tensors = safetensors.torch.load_file(self.safetensors_file_path, device=device)
193
+
194
+ if self.stream is not None:
195
+ for key, tensor_obj in self.key_to_tensor.items():
196
+ pinned_tensor = loaded_tensors[key].pin_memory()
197
+ tensor_obj.data = pinned_tensor.to(self.onload_device, non_blocking=self.non_blocking)
198
+ if self.record_stream:
199
+ tensor_obj.data.record_stream(current_stream)
200
+ else:
201
+ onload_device = (
202
+ self.onload_device.type if isinstance(self.onload_device, torch.device) else self.onload_device
203
+ )
204
+ loaded_tensors = safetensors.torch.load_file(self.safetensors_file_path, device=onload_device)
205
+ for key, tensor_obj in self.key_to_tensor.items():
206
+ tensor_obj.data = loaded_tensors[key]
207
+
208
+ def _onload_from_memory(self):
209
+ if self.stream is not None:
210
+ # Wait for previous Host->Device transfer to complete
211
+ self.stream.synchronize()
212
+
213
+ context = nullcontext() if self.stream is None else self._torch_accelerator_module.stream(self.stream)
214
+ default_stream = self._torch_accelerator_module.current_stream() if self.stream is not None else None
215
+
216
+ with context:
217
+ if self.stream is not None:
218
+ with self._pinned_memory_tensors() as pinned_memory:
219
+ self._process_tensors_from_modules(pinned_memory, default_stream=default_stream)
220
+ else:
221
+ self._process_tensors_from_modules(None)
222
+
223
+ def _offload_to_disk(self):
224
+ # TODO: we can potentially optimize this code path by checking if the _all_ the desired
225
+ # safetensor files exist on the disk and if so, skip this step entirely, reducing IO
226
+ # overhead. Currently, we just check if the given `safetensors_file_path` exists and if not
227
+ # we perform a write.
228
+ # Check if the file has been saved in this session or if it already exists on disk.
229
+ if not self._is_offloaded_to_disk and not os.path.exists(self.safetensors_file_path):
230
+ os.makedirs(os.path.dirname(self.safetensors_file_path), exist_ok=True)
231
+ tensors_to_save = {key: tensor.data.to(self.offload_device) for tensor, key in self.tensor_to_key.items()}
232
+ safetensors.torch.save_file(tensors_to_save, self.safetensors_file_path)
233
+
234
+ # The group is now considered offloaded to disk for the rest of the session.
235
+ self._is_offloaded_to_disk = True
236
+
237
+ # We do this to free up the RAM which is still holding the up tensor data.
238
+ for tensor_obj in self.tensor_to_key.keys():
239
+ tensor_obj.data = torch.empty_like(tensor_obj.data, device=self.offload_device)
240
+
241
+ def _offload_to_memory(self):
242
+ if self.stream is not None:
243
+ if not self.record_stream:
244
+ self._torch_accelerator_module.current_stream().synchronize()
245
+
246
+ for group_module in self.modules:
247
+ for param in group_module.parameters():
248
+ param.data = self.cpu_param_dict[param]
249
+ for param in self.parameters:
250
+ param.data = self.cpu_param_dict[param]
251
+ for buffer in self.buffers:
252
+ buffer.data = self.cpu_param_dict[buffer]
253
+ else:
254
+ for group_module in self.modules:
255
+ group_module.to(self.offload_device, non_blocking=False)
256
+ for param in self.parameters:
257
+ param.data = param.data.to(self.offload_device, non_blocking=False)
258
+ for buffer in self.buffers:
259
+ buffer.data = buffer.data.to(self.offload_device, non_blocking=False)
260
+
261
+ @torch.compiler.disable()
262
+ def onload_(self):
263
+ r"""Onloads the group of parameters to the onload_device."""
264
+ if self.offload_to_disk_path is not None:
265
+ self._onload_from_disk()
266
+ else:
267
+ self._onload_from_memory()
268
+
269
+ @torch.compiler.disable()
270
+ def offload_(self):
271
+ r"""Offloads the group of parameters to the offload_device."""
272
+ if self.offload_to_disk_path:
273
+ self._offload_to_disk()
274
+ else:
275
+ self._offload_to_memory()
276
+
277
+
278
+ class GroupOffloadingHook(ModelHook):
279
+ r"""
280
+ A hook that offloads groups of torch.nn.Module to the CPU for storage and onloads to accelerator device for
281
+ computation. Each group has one "onload leader" module that is responsible for onloading, and an "offload leader"
282
+ module that is responsible for offloading. If prefetching is enabled, the onload leader of the previous module
283
+ group is responsible for onloading the current module group.
284
+ """
285
+
286
+ _is_stateful = False
287
+
288
+ def __init__(self, group: ModuleGroup, *, config: GroupOffloadingConfig) -> None:
289
+ self.group = group
290
+ self.next_group: ModuleGroup | None = None
291
+ self.config = config
292
+
293
+ def initialize_hook(self, module: torch.nn.Module) -> torch.nn.Module:
294
+ if self.group.offload_leader == module:
295
+ self.group.offload_()
296
+ return module
297
+
298
+ def pre_forward(self, module: torch.nn.Module, *args, **kwargs):
299
+ # If there wasn't an onload_leader assigned, we assume that the submodule that first called its forward
300
+ # method is the onload_leader of the group.
301
+ if self.group.onload_leader is None:
302
+ self.group.onload_leader = module
303
+
304
+ # If the current module is the onload_leader of the group, we onload the group if it is supposed
305
+ # to onload itself. In the case of using prefetching with streams, we onload the next group if
306
+ # it is not supposed to onload itself.
307
+ if self.group.onload_leader == module:
308
+ if self.group.onload_self:
309
+ self.group.onload_()
310
+ else:
311
+ # onload_self=False means this group relies on prefetching from a previous group.
312
+ # However, for conditionally-executed modules (e.g. patch_short/patch_mid/patch_long in Helios),
313
+ # the prefetch chain may not cover them if they were absent during the first forward pass
314
+ # when the execution order was traced. In that case, their weights remain on offload_device,
315
+ # so we fall back to a synchronous onload here.
316
+ params = [p for m in self.group.modules for p in m.parameters()] + list(self.group.parameters)
317
+ if params and params[0].device == self.group.offload_device:
318
+ self.group.onload_()
319
+ if self.group.stream is not None:
320
+ self.group.stream.synchronize()
321
+
322
+ should_onload_next_group = self.next_group is not None and not self.next_group.onload_self
323
+ if should_onload_next_group:
324
+ self.next_group.onload_()
325
+
326
+ should_synchronize = (
327
+ not self.group.onload_self and self.group.stream is not None and not should_onload_next_group
328
+ )
329
+ if should_synchronize:
330
+ # If this group didn't onload itself, it means it was asynchronously onloaded by the
331
+ # previous group. We need to synchronize the side stream to ensure parameters
332
+ # are completely loaded to proceed with forward pass. Without this, uninitialized
333
+ # weights will be used in the computation, leading to incorrect results
334
+ # Also, we should only do this synchronization if we don't already do it from the sync call in
335
+ # self.next_group.onload_, hence the `not should_onload_next_group` check.
336
+ self.group.stream.synchronize()
337
+
338
+ args = send_to_device(args, self.group.onload_device, non_blocking=self.group.non_blocking)
339
+
340
+ # Some Autoencoder models use a feature cache that is passed through submodules
341
+ # and modified in place. The `send_to_device` call returns a copy of this feature cache object
342
+ # which breaks the inplace updates. Use `exclude_kwargs` to mark these cache features
343
+ exclude_kwargs = self.config.exclude_kwargs or []
344
+ if exclude_kwargs:
345
+ moved_kwargs = send_to_device(
346
+ {k: v for k, v in kwargs.items() if k not in exclude_kwargs},
347
+ self.group.onload_device,
348
+ non_blocking=self.group.non_blocking,
349
+ )
350
+ kwargs.update(moved_kwargs)
351
+ else:
352
+ kwargs = send_to_device(kwargs, self.group.onload_device, non_blocking=self.group.non_blocking)
353
+
354
+ return args, kwargs
355
+
356
+ def post_forward(self, module: torch.nn.Module, output):
357
+ if self.group.offload_leader == module:
358
+ self.group.offload_()
359
+ return output
360
+
361
+
362
+ class LazyPrefetchGroupOffloadingHook(ModelHook):
363
+ r"""
364
+ A hook, used in conjunction with GroupOffloadingHook, that applies lazy prefetching to groups of torch.nn.Module.
365
+ This hook is used to determine the order in which the layers are executed during the forward pass. Once the layer
366
+ invocation order is known, assignments of the next_group attribute for prefetching can be made, which allows
367
+ prefetching groups in the correct order.
368
+ """
369
+
370
+ _is_stateful = False
371
+
372
+ def __init__(self):
373
+ self.execution_order: list[tuple[str, torch.nn.Module]] = []
374
+ self._layer_execution_tracker_module_names = set()
375
+
376
+ def initialize_hook(self, module):
377
+ def make_execution_order_update_callback(current_name, current_submodule):
378
+ def callback():
379
+ if not torch.compiler.is_compiling():
380
+ logger.debug(f"Adding {current_name} to the execution order")
381
+ self.execution_order.append((current_name, current_submodule))
382
+
383
+ return callback
384
+
385
+ # To every submodule that contains a group offloading hook (at this point, no prefetching is enabled for any
386
+ # of the groups), we add a layer execution tracker hook that will be used to determine the order in which the
387
+ # layers are executed during the forward pass.
388
+ for name, submodule in module.named_modules():
389
+ if name == "" or not hasattr(submodule, "_diffusers_hook"):
390
+ continue
391
+
392
+ registry = HookRegistry.check_if_exists_or_initialize(submodule)
393
+ group_offloading_hook = registry.get_hook(_GROUP_OFFLOADING)
394
+
395
+ if group_offloading_hook is not None:
396
+ # For the first forward pass, we have to load in a blocking manner
397
+ group_offloading_hook.group.non_blocking = False
398
+ layer_tracker_hook = LayerExecutionTrackerHook(make_execution_order_update_callback(name, submodule))
399
+ registry.register_hook(layer_tracker_hook, _LAYER_EXECUTION_TRACKER)
400
+ self._layer_execution_tracker_module_names.add(name)
401
+
402
+ return module
403
+
404
+ def post_forward(self, module, output):
405
+ # At this point, for the current modules' submodules, we know the execution order of the layers. We can now
406
+ # remove the layer execution tracker hooks and apply prefetching by setting the next_group attribute for each
407
+ # group offloading hook.
408
+ num_executed = len(self.execution_order)
409
+ execution_order_module_names = {name for name, _ in self.execution_order}
410
+
411
+ # It may be possible that some layers were not executed during the forward pass. This can happen if the layer
412
+ # is not used in the forward pass, or if the layer is not executed due to some other reason. In such cases, we
413
+ # may not be able to apply prefetching in the correct order, which can lead to device-mismatch related errors
414
+ # if the missing layers end up being executed in the future.
415
+ if execution_order_module_names != self._layer_execution_tracker_module_names:
416
+ unexecuted_layers = list(self._layer_execution_tracker_module_names - execution_order_module_names)
417
+ if not torch.compiler.is_compiling():
418
+ logger.warning(
419
+ "It seems like some layers were not executed during the forward pass. This may lead to problems when "
420
+ "applying lazy prefetching with automatic tracing and lead to device-mismatch related errors. Please "
421
+ "make sure that all layers are executed during the forward pass. The following layers were not executed:\n"
422
+ f"{unexecuted_layers=}"
423
+ )
424
+
425
+ # Remove the layer execution tracker hooks from the submodules
426
+ base_module_registry = module._diffusers_hook
427
+ registries = [submodule._diffusers_hook for _, submodule in self.execution_order]
428
+ group_offloading_hooks = [registry.get_hook(_GROUP_OFFLOADING) for registry in registries]
429
+
430
+ for i in range(num_executed):
431
+ registries[i].remove_hook(_LAYER_EXECUTION_TRACKER, recurse=False)
432
+
433
+ # Remove the current lazy prefetch group offloading hook so that it doesn't interfere with the next forward pass
434
+ base_module_registry.remove_hook(_LAZY_PREFETCH_GROUP_OFFLOADING, recurse=False)
435
+
436
+ # LazyPrefetchGroupOffloadingHook is only used with streams, so we know that non_blocking should be True.
437
+ # We disable non_blocking for the first forward pass, but need to enable it for the subsequent passes to
438
+ # see the benefits of prefetching.
439
+ for hook in group_offloading_hooks:
440
+ hook.group.non_blocking = True
441
+
442
+ # Set required attributes for prefetching
443
+ if num_executed > 0:
444
+ base_module_group_offloading_hook = base_module_registry.get_hook(_GROUP_OFFLOADING)
445
+ base_module_group_offloading_hook.next_group = group_offloading_hooks[0].group
446
+ base_module_group_offloading_hook.next_group.onload_self = False
447
+
448
+ for i in range(num_executed - 1):
449
+ name1, _ = self.execution_order[i]
450
+ name2, _ = self.execution_order[i + 1]
451
+ if not torch.compiler.is_compiling():
452
+ logger.debug(f"Applying lazy prefetch group offloading from {name1} to {name2}")
453
+ group_offloading_hooks[i].next_group = group_offloading_hooks[i + 1].group
454
+ group_offloading_hooks[i].next_group.onload_self = False
455
+
456
+ return output
457
+
458
+
459
+ class LayerExecutionTrackerHook(ModelHook):
460
+ r"""
461
+ A hook that tracks the order in which the layers are executed during the forward pass by calling back to the
462
+ LazyPrefetchGroupOffloadingHook to update the execution order.
463
+ """
464
+
465
+ _is_stateful = False
466
+
467
+ def __init__(self, execution_order_update_callback):
468
+ self.execution_order_update_callback = execution_order_update_callback
469
+
470
+ def pre_forward(self, module, *args, **kwargs):
471
+ self.execution_order_update_callback()
472
+ return args, kwargs
473
+
474
+
475
+ def apply_group_offloading(
476
+ module: torch.nn.Module,
477
+ onload_device: str | torch.device,
478
+ offload_device: str | torch.device = torch.device("cpu"),
479
+ offload_type: str | GroupOffloadingType = "block_level",
480
+ num_blocks_per_group: int | None = None,
481
+ non_blocking: bool = False,
482
+ use_stream: bool = False,
483
+ record_stream: bool = False,
484
+ low_cpu_mem_usage: bool = False,
485
+ offload_to_disk_path: str | None = None,
486
+ block_modules: list[str] | None = None,
487
+ exclude_kwargs: list[str] | None = None,
488
+ ) -> None:
489
+ r"""
490
+ Applies group offloading to the internal layers of a torch.nn.Module. To understand what group offloading is, and
491
+ where it is beneficial, we need to first provide some context on how other supported offloading methods work.
492
+
493
+ Typically, offloading is done at two levels:
494
+ - Module-level: In Diffusers, this can be enabled using the `ModelMixin::enable_model_cpu_offload()` method. It
495
+ works by offloading each component of a pipeline to the CPU for storage, and onloading to the accelerator device
496
+ when needed for computation. This method is more memory-efficient than keeping all components on the accelerator,
497
+ but the memory requirements are still quite high. For this method to work, one needs memory equivalent to size of
498
+ the model in runtime dtype + size of largest intermediate activation tensors to be able to complete the forward
499
+ pass.
500
+ - Leaf-level: In Diffusers, this can be enabled using the `ModelMixin::enable_sequential_cpu_offload()` method. It
501
+ works by offloading the lowest leaf-level parameters of the computation graph to the CPU for storage, and
502
+ onloading only the leafs to the accelerator device for computation. This uses the lowest amount of accelerator
503
+ memory, but can be slower due to the excessive number of device synchronizations.
504
+
505
+ Group offloading is a middle ground between the two methods. It works by offloading groups of internal layers,
506
+ (either `torch.nn.ModuleList` or `torch.nn.Sequential`). This method uses lower memory than module-level
507
+ offloading. It is also faster than leaf-level/sequential offloading, as the number of device synchronizations is
508
+ reduced.
509
+
510
+ Another supported feature (for CUDA devices with support for asynchronous data transfer streams) is the ability to
511
+ overlap data transfer and computation to reduce the overall execution time compared to sequential offloading. This
512
+ is enabled using layer prefetching with streams, i.e., the layer that is to be executed next starts onloading to
513
+ the accelerator device while the current layer is being executed - this increases the memory requirements slightly.
514
+ Note that this implementation also supports leaf-level offloading but can be made much faster when using streams.
515
+
516
+ Args:
517
+ module (`torch.nn.Module`):
518
+ The module to which group offloading is applied.
519
+ onload_device (`torch.device`):
520
+ The device to which the group of modules are onloaded.
521
+ offload_device (`torch.device`, defaults to `torch.device("cpu")`):
522
+ The device to which the group of modules are offloaded. This should typically be the CPU. Default is CPU.
523
+ offload_type (`str` or `GroupOffloadingType`, defaults to "block_level"):
524
+ The type of offloading to be applied. Can be one of "block_level" or "leaf_level". Default is
525
+ "block_level".
526
+ offload_to_disk_path (`str`, *optional*, defaults to `None`):
527
+ The path to the directory where parameters will be offloaded. Setting this option can be useful in limited
528
+ RAM environment settings where a reasonable speed-memory trade-off is desired.
529
+ num_blocks_per_group (`int`, *optional*):
530
+ The number of blocks per group when using offload_type="block_level". This is required when using
531
+ offload_type="block_level".
532
+ non_blocking (`bool`, defaults to `False`):
533
+ If True, offloading and onloading is done with non-blocking data transfer.
534
+ use_stream (`bool`, defaults to `False`):
535
+ If True, offloading and onloading is done asynchronously using a CUDA stream. This can be useful for
536
+ overlapping computation and data transfer.
537
+ record_stream (`bool`, defaults to `False`): When enabled with `use_stream`, it marks the current tensor
538
+ as having been used by this stream. It is faster at the expense of slightly more memory usage. Refer to the
539
+ [PyTorch official docs](https://pytorch.org/docs/stable/generated/torch.Tensor.record_stream.html) more
540
+ details.
541
+ low_cpu_mem_usage (`bool`, defaults to `False`):
542
+ If True, the CPU memory usage is minimized by pinning tensors on-the-fly instead of pre-pinning them. This
543
+ option only matters when using streamed CPU offloading (i.e. `use_stream=True`). This can be useful when
544
+ the CPU memory is a bottleneck but may counteract the benefits of using streams.
545
+ block_modules (`list[str]`, *optional*):
546
+ List of module names that should be treated as blocks for offloading. If provided, only these modules will
547
+ be considered for block-level offloading. If not provided, the default block detection logic will be used.
548
+ exclude_kwargs (`list[str]`, *optional*):
549
+ List of kwarg keys that should not be processed by send_to_device. This is useful for mutable state like
550
+ caching lists that need to maintain their object identity across forward passes. If not provided, will be
551
+ inferred from the module's `_skip_keys` attribute if it exists.
552
+
553
+ Example:
554
+ ```python
555
+ >>> from diffusers import CogVideoXTransformer3DModel
556
+ >>> from diffusers.hooks import apply_group_offloading
557
+
558
+ >>> transformer = CogVideoXTransformer3DModel.from_pretrained(
559
+ ... "THUDM/CogVideoX-5b", subfolder="transformer", torch_dtype=torch.bfloat16
560
+ ... )
561
+
562
+ >>> apply_group_offloading(
563
+ ... transformer,
564
+ ... onload_device=torch.device("cuda"),
565
+ ... offload_device=torch.device("cpu"),
566
+ ... offload_type="block_level",
567
+ ... num_blocks_per_group=2,
568
+ ... use_stream=True,
569
+ ... )
570
+ ```
571
+ """
572
+
573
+ onload_device = torch.device(onload_device) if isinstance(onload_device, str) else onload_device
574
+ offload_device = torch.device(offload_device) if isinstance(offload_device, str) else offload_device
575
+ offload_type = GroupOffloadingType(offload_type)
576
+
577
+ stream = None
578
+ if use_stream:
579
+ if torch.cuda.is_available():
580
+ stream = torch.cuda.Stream()
581
+ elif hasattr(torch, "xpu") and torch.xpu.is_available():
582
+ stream = torch.Stream()
583
+ else:
584
+ raise ValueError("Using streams for data transfer requires a CUDA device, or an Intel XPU device.")
585
+
586
+ if not use_stream and record_stream:
587
+ raise ValueError("`record_stream` cannot be True when `use_stream=False`.")
588
+ if offload_type == GroupOffloadingType.BLOCK_LEVEL and num_blocks_per_group is None:
589
+ raise ValueError("`num_blocks_per_group` must be provided when using `offload_type='block_level'.")
590
+
591
+ _raise_error_if_accelerate_model_or_sequential_hook_present(module)
592
+
593
+ if block_modules is None:
594
+ block_modules = getattr(module, "_group_offload_block_modules", None)
595
+
596
+ if exclude_kwargs is None:
597
+ exclude_kwargs = getattr(module, "_skip_keys", None)
598
+
599
+ config = GroupOffloadingConfig(
600
+ onload_device=onload_device,
601
+ offload_device=offload_device,
602
+ offload_type=offload_type,
603
+ num_blocks_per_group=num_blocks_per_group,
604
+ non_blocking=non_blocking,
605
+ stream=stream,
606
+ record_stream=record_stream,
607
+ low_cpu_mem_usage=low_cpu_mem_usage,
608
+ offload_to_disk_path=offload_to_disk_path,
609
+ block_modules=block_modules,
610
+ exclude_kwargs=exclude_kwargs,
611
+ )
612
+ _apply_group_offloading(module, config)
613
+
614
+
615
+ def _apply_group_offloading(module: torch.nn.Module, config: GroupOffloadingConfig) -> None:
616
+ if config.offload_type == GroupOffloadingType.BLOCK_LEVEL:
617
+ _apply_group_offloading_block_level(module, config)
618
+ elif config.offload_type == GroupOffloadingType.LEAF_LEVEL:
619
+ _apply_group_offloading_leaf_level(module, config)
620
+ else:
621
+ assert False
622
+
623
+
624
+ def _apply_group_offloading_block_level(module: torch.nn.Module, config: GroupOffloadingConfig) -> None:
625
+ r"""
626
+ This function applies offloading to groups of torch.nn.ModuleList or torch.nn.Sequential blocks, and explicitly
627
+ defined block modules. In comparison to the "leaf_level" offloading, which is more fine-grained, this offloading is
628
+ done at the top-level blocks and modules specified in block_modules.
629
+
630
+ When block_modules is provided, only those modules will be treated as blocks for offloading. For each specified
631
+ module, recursively apply block offloading to it.
632
+ """
633
+ if config.stream is not None and config.num_blocks_per_group != 1:
634
+ logger.warning(
635
+ f"Using streams is only supported for num_blocks_per_group=1. Got {config.num_blocks_per_group=}. Setting it to 1."
636
+ )
637
+ config.num_blocks_per_group = 1
638
+
639
+ block_modules = set(config.block_modules) if config.block_modules is not None else set()
640
+
641
+ # Create module groups for ModuleList and Sequential blocks, and explicitly defined block modules
642
+ modules_with_group_offloading = set()
643
+ unmatched_modules = []
644
+ matched_module_groups = []
645
+
646
+ for name, submodule in module.named_children():
647
+ # Check if this is an explicitly defined block module
648
+ if name in block_modules:
649
+ # Track submodule using a prefix to avoid filename collisions during disk offload.
650
+ # Without this, submodules sharing the same model class would be assigned identical
651
+ # filenames (derived from the class name).
652
+ prefix = f"{config.module_prefix}{name}." if config.module_prefix else f"{name}."
653
+ submodule_config = replace(config, module_prefix=prefix)
654
+
655
+ _apply_group_offloading_block_level(submodule, submodule_config)
656
+ modules_with_group_offloading.add(name)
657
+
658
+ elif isinstance(submodule, (torch.nn.ModuleList, torch.nn.Sequential)):
659
+ # Handle ModuleList and Sequential blocks as before
660
+ for i in range(0, len(submodule), config.num_blocks_per_group):
661
+ current_modules = list(submodule[i : i + config.num_blocks_per_group])
662
+ if len(current_modules) == 0:
663
+ continue
664
+
665
+ group_id = f"{config.module_prefix}{name}_{i}_{i + len(current_modules) - 1}"
666
+ group = ModuleGroup(
667
+ modules=current_modules,
668
+ offload_device=config.offload_device,
669
+ onload_device=config.onload_device,
670
+ offload_to_disk_path=config.offload_to_disk_path,
671
+ offload_leader=current_modules[-1],
672
+ onload_leader=current_modules[0],
673
+ non_blocking=config.non_blocking,
674
+ stream=config.stream,
675
+ record_stream=config.record_stream,
676
+ low_cpu_mem_usage=config.low_cpu_mem_usage,
677
+ onload_self=True,
678
+ group_id=group_id,
679
+ )
680
+ matched_module_groups.append(group)
681
+ for j in range(i, i + len(current_modules)):
682
+ modules_with_group_offloading.add(f"{name}.{j}")
683
+ else:
684
+ # This is an unmatched module
685
+ unmatched_modules.append((name, submodule))
686
+
687
+ # Apply group offloading hooks to the module groups
688
+ for i, group in enumerate(matched_module_groups):
689
+ for group_module in group.modules:
690
+ _apply_group_offloading_hook(group_module, group, config=config)
691
+
692
+ # Parameters and Buffers of the top-level module need to be offloaded/onloaded separately
693
+ # when the forward pass of this module is called. This is because the top-level module is not
694
+ # part of any group (as doing so would lead to no VRAM savings).
695
+ parameters = _gather_parameters_with_no_group_offloading_parent(module, modules_with_group_offloading)
696
+ buffers = _gather_buffers_with_no_group_offloading_parent(module, modules_with_group_offloading)
697
+ parameters = [param for _, param in parameters]
698
+ buffers = [buffer for _, buffer in buffers]
699
+
700
+ # Create a group for the remaining unmatched submodules of the top-level
701
+ # module so that they are on the correct device when the forward pass is called.
702
+ unmatched_modules = [unmatched_module for _, unmatched_module in unmatched_modules]
703
+ if len(unmatched_modules) > 0 or len(parameters) > 0 or len(buffers) > 0:
704
+ unmatched_group = ModuleGroup(
705
+ modules=unmatched_modules,
706
+ offload_device=config.offload_device,
707
+ onload_device=config.onload_device,
708
+ offload_to_disk_path=config.offload_to_disk_path,
709
+ offload_leader=module,
710
+ onload_leader=module,
711
+ parameters=parameters,
712
+ buffers=buffers,
713
+ non_blocking=False,
714
+ stream=None,
715
+ record_stream=False,
716
+ onload_self=True,
717
+ group_id=f"{config.module_prefix}{module.__class__.__name__}_unmatched_group",
718
+ )
719
+ if config.stream is None:
720
+ _apply_group_offloading_hook(module, unmatched_group, config=config)
721
+ else:
722
+ _apply_lazy_group_offloading_hook(module, unmatched_group, config=config)
723
+
724
+
725
+ def _apply_group_offloading_leaf_level(module: torch.nn.Module, config: GroupOffloadingConfig) -> None:
726
+ r"""
727
+ This function applies offloading to groups of leaf modules in a torch.nn.Module. This method has minimal memory
728
+ requirements. However, it can be slower compared to other offloading methods due to the excessive number of device
729
+ synchronizations. When using devices that support streams to overlap data transfer and computation, this method can
730
+ reduce memory usage without any performance degradation.
731
+ """
732
+ # Create module groups for leaf modules and apply group offloading hooks
733
+ modules_with_group_offloading = set()
734
+ for name, submodule in module.named_modules():
735
+ if not isinstance(submodule, _GO_LC_SUPPORTED_PYTORCH_LAYERS):
736
+ continue
737
+ group = ModuleGroup(
738
+ modules=[submodule],
739
+ offload_device=config.offload_device,
740
+ onload_device=config.onload_device,
741
+ offload_to_disk_path=config.offload_to_disk_path,
742
+ offload_leader=submodule,
743
+ onload_leader=submodule,
744
+ non_blocking=config.non_blocking,
745
+ stream=config.stream,
746
+ record_stream=config.record_stream,
747
+ low_cpu_mem_usage=config.low_cpu_mem_usage,
748
+ onload_self=True,
749
+ group_id=name,
750
+ )
751
+ _apply_group_offloading_hook(submodule, group, config=config)
752
+ modules_with_group_offloading.add(name)
753
+
754
+ # Parameters and Buffers at all non-leaf levels need to be offloaded/onloaded separately when the forward pass
755
+ # of the module is called
756
+ module_dict = dict(module.named_modules())
757
+ parameters = _gather_parameters_with_no_group_offloading_parent(module, modules_with_group_offloading)
758
+ buffers = _gather_buffers_with_no_group_offloading_parent(module, modules_with_group_offloading)
759
+
760
+ # Find closest module parent for each parameter and buffer, and attach group hooks
761
+ parent_to_parameters = {}
762
+ for name, param in parameters:
763
+ parent_name = _find_parent_module_in_module_dict(name, module_dict)
764
+ if parent_name in parent_to_parameters:
765
+ parent_to_parameters[parent_name].append(param)
766
+ else:
767
+ parent_to_parameters[parent_name] = [param]
768
+
769
+ parent_to_buffers = {}
770
+ for name, buffer in buffers:
771
+ parent_name = _find_parent_module_in_module_dict(name, module_dict)
772
+ if parent_name in parent_to_buffers:
773
+ parent_to_buffers[parent_name].append(buffer)
774
+ else:
775
+ parent_to_buffers[parent_name] = [buffer]
776
+
777
+ parent_names = set(parent_to_parameters.keys()) | set(parent_to_buffers.keys())
778
+ for name in parent_names:
779
+ parameters = parent_to_parameters.get(name, [])
780
+ buffers = parent_to_buffers.get(name, [])
781
+ parent_module = module_dict[name]
782
+ group = ModuleGroup(
783
+ modules=[],
784
+ offload_device=config.offload_device,
785
+ onload_device=config.onload_device,
786
+ offload_leader=parent_module,
787
+ onload_leader=parent_module,
788
+ offload_to_disk_path=config.offload_to_disk_path,
789
+ parameters=parameters,
790
+ buffers=buffers,
791
+ non_blocking=config.non_blocking,
792
+ stream=config.stream,
793
+ record_stream=config.record_stream,
794
+ low_cpu_mem_usage=config.low_cpu_mem_usage,
795
+ onload_self=True,
796
+ group_id=name,
797
+ )
798
+ _apply_group_offloading_hook(parent_module, group, config=config)
799
+
800
+ if config.stream is not None:
801
+ # When using streams, we need to know the layer execution order for applying prefetching (to overlap data transfer
802
+ # and computation). Since we don't know the order beforehand, we apply a lazy prefetching hook that will find the
803
+ # execution order and apply prefetching in the correct order.
804
+ unmatched_group = ModuleGroup(
805
+ modules=[],
806
+ offload_device=config.offload_device,
807
+ onload_device=config.onload_device,
808
+ offload_to_disk_path=config.offload_to_disk_path,
809
+ offload_leader=module,
810
+ onload_leader=module,
811
+ parameters=None,
812
+ buffers=None,
813
+ non_blocking=False,
814
+ stream=None,
815
+ record_stream=False,
816
+ low_cpu_mem_usage=config.low_cpu_mem_usage,
817
+ onload_self=True,
818
+ group_id=_GROUP_ID_LAZY_LEAF,
819
+ )
820
+ _apply_lazy_group_offloading_hook(module, unmatched_group, config=config)
821
+
822
+
823
+ def _apply_group_offloading_hook(
824
+ module: torch.nn.Module,
825
+ group: ModuleGroup,
826
+ *,
827
+ config: GroupOffloadingConfig,
828
+ ) -> None:
829
+ registry = HookRegistry.check_if_exists_or_initialize(module)
830
+
831
+ # We may have already registered a group offloading hook if the module had a torch.nn.Parameter whose parent
832
+ # is the current module. In such cases, we don't want to overwrite the existing group offloading hook.
833
+ if registry.get_hook(_GROUP_OFFLOADING) is None:
834
+ hook = GroupOffloadingHook(group, config=config)
835
+ registry.register_hook(hook, _GROUP_OFFLOADING)
836
+
837
+
838
+ def _apply_lazy_group_offloading_hook(
839
+ module: torch.nn.Module,
840
+ group: ModuleGroup,
841
+ *,
842
+ config: GroupOffloadingConfig,
843
+ ) -> None:
844
+ registry = HookRegistry.check_if_exists_or_initialize(module)
845
+
846
+ # We may have already registered a group offloading hook if the module had a torch.nn.Parameter whose parent
847
+ # is the current module. In such cases, we don't want to overwrite the existing group offloading hook.
848
+ if registry.get_hook(_GROUP_OFFLOADING) is None:
849
+ hook = GroupOffloadingHook(group, config=config)
850
+ registry.register_hook(hook, _GROUP_OFFLOADING)
851
+
852
+ lazy_prefetch_hook = LazyPrefetchGroupOffloadingHook()
853
+ registry.register_hook(lazy_prefetch_hook, _LAZY_PREFETCH_GROUP_OFFLOADING)
854
+
855
+
856
+ def _gather_parameters_with_no_group_offloading_parent(
857
+ module: torch.nn.Module, modules_with_group_offloading: Set[str]
858
+ ) -> list[torch.nn.Parameter]:
859
+ parameters = []
860
+ for name, parameter in module.named_parameters():
861
+ has_parent_with_group_offloading = False
862
+ atoms = name.split(".")
863
+ while len(atoms) > 0:
864
+ parent_name = ".".join(atoms)
865
+ if parent_name in modules_with_group_offloading:
866
+ has_parent_with_group_offloading = True
867
+ break
868
+ atoms.pop()
869
+ if not has_parent_with_group_offloading:
870
+ parameters.append((name, parameter))
871
+ return parameters
872
+
873
+
874
+ def _gather_buffers_with_no_group_offloading_parent(
875
+ module: torch.nn.Module, modules_with_group_offloading: Set[str]
876
+ ) -> list[torch.Tensor]:
877
+ buffers = []
878
+ for name, buffer in module.named_buffers():
879
+ has_parent_with_group_offloading = False
880
+ atoms = name.split(".")
881
+ while len(atoms) > 0:
882
+ parent_name = ".".join(atoms)
883
+ if parent_name in modules_with_group_offloading:
884
+ has_parent_with_group_offloading = True
885
+ break
886
+ atoms.pop()
887
+ if not has_parent_with_group_offloading:
888
+ buffers.append((name, buffer))
889
+ return buffers
890
+
891
+
892
+ def _find_parent_module_in_module_dict(name: str, module_dict: dict[str, torch.nn.Module]) -> str:
893
+ atoms = name.split(".")
894
+ while len(atoms) > 0:
895
+ parent_name = ".".join(atoms)
896
+ if parent_name in module_dict:
897
+ return parent_name
898
+ atoms.pop()
899
+ return ""
900
+
901
+
902
+ def _raise_error_if_accelerate_model_or_sequential_hook_present(module: torch.nn.Module) -> None:
903
+ if not is_accelerate_available():
904
+ return
905
+ for name, submodule in module.named_modules():
906
+ if not hasattr(submodule, "_hf_hook"):
907
+ continue
908
+ if isinstance(submodule._hf_hook, (AlignDevicesHook, CpuOffload)):
909
+ raise ValueError(
910
+ f"Cannot apply group offloading to a module that is already applying an alternative "
911
+ f"offloading strategy from Accelerate. If you want to apply group offloading, please "
912
+ f"disable the existing offloading strategy first. Offending module: {name} ({type(submodule)})"
913
+ )
914
+
915
+
916
+ def _get_top_level_group_offload_hook(module: torch.nn.Module) -> GroupOffloadingHook | None:
917
+ for submodule in module.modules():
918
+ if hasattr(submodule, "_diffusers_hook"):
919
+ group_offloading_hook = submodule._diffusers_hook.get_hook(_GROUP_OFFLOADING)
920
+ if group_offloading_hook is not None:
921
+ return group_offloading_hook
922
+ return None
923
+
924
+
925
+ def _is_group_offload_enabled(module: torch.nn.Module) -> bool:
926
+ top_level_group_offload_hook = _get_top_level_group_offload_hook(module)
927
+ return top_level_group_offload_hook is not None
928
+
929
+
930
+ def _get_group_onload_device(module: torch.nn.Module) -> torch.device:
931
+ top_level_group_offload_hook = _get_top_level_group_offload_hook(module)
932
+ if top_level_group_offload_hook is not None:
933
+ return top_level_group_offload_hook.config.onload_device
934
+ raise ValueError("Group offloading is not enabled for the provided module.")
935
+
936
+
937
+ def _compute_group_hash(group_id):
938
+ hashed_id = hashlib.sha256(group_id.encode("utf-8")).hexdigest()
939
+ # first 16 characters for a reasonably short but unique name
940
+ return hashed_id[:16]
941
+
942
+
943
+ def _maybe_remove_and_reapply_group_offloading(module: torch.nn.Module) -> None:
944
+ r"""
945
+ Removes the group offloading hook from the module and re-applies it. This is useful when the module has been
946
+ modified in-place and the group offloading hook references-to-tensors needs to be updated. The in-place
947
+ modification can happen in a number of ways, for example, fusing QKV or unloading/loading LoRAs on-the-fly.
948
+
949
+ In this implementation, we make an assumption that group offloading has only been applied at the top-level module,
950
+ and therefore all submodules have the same onload and offload devices. If this assumption is not true, say in the
951
+ case where user has applied group offloading at multiple levels, this function will not work as expected.
952
+
953
+ There is some performance penalty associated with doing this when non-default streams are used, because we need to
954
+ retrace the execution order of the layers with `LazyPrefetchGroupOffloadingHook`.
955
+ """
956
+ top_level_group_offload_hook = _get_top_level_group_offload_hook(module)
957
+
958
+ if top_level_group_offload_hook is None:
959
+ return
960
+
961
+ registry = HookRegistry.check_if_exists_or_initialize(module)
962
+ registry.remove_hook(_GROUP_OFFLOADING, recurse=True)
963
+ registry.remove_hook(_LAYER_EXECUTION_TRACKER, recurse=True)
964
+ registry.remove_hook(_LAZY_PREFETCH_GROUP_OFFLOADING, recurse=True)
965
+
966
+ _apply_group_offloading(module, top_level_group_offload_hook.config)
third_party/diffusers/src/diffusers/hooks/hooks.py ADDED
@@ -0,0 +1,310 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import functools
16
+ from typing import Any
17
+
18
+ import torch
19
+
20
+ from ..utils.logging import get_logger
21
+ from ..utils.torch_utils import unwrap_module
22
+
23
+
24
+ logger = get_logger(__name__) # pylint: disable=invalid-name
25
+
26
+
27
+ class BaseState:
28
+ def reset(self, *args, **kwargs) -> None:
29
+ raise NotImplementedError(
30
+ "BaseState::reset is not implemented. Please implement this method in the derived class."
31
+ )
32
+
33
+
34
+ class StateManager:
35
+ def __init__(self, state_cls: BaseState, init_args=None, init_kwargs=None):
36
+ self._state_cls = state_cls
37
+ self._init_args = init_args if init_args is not None else ()
38
+ self._init_kwargs = init_kwargs if init_kwargs is not None else {}
39
+ self._state_cache = {}
40
+ self._current_context = None
41
+
42
+ def get_state(self):
43
+ if self._current_context is None:
44
+ raise ValueError("No context is set. Please set a context before retrieving the state.")
45
+ if self._current_context not in self._state_cache.keys():
46
+ self._state_cache[self._current_context] = self._state_cls(*self._init_args, **self._init_kwargs)
47
+ return self._state_cache[self._current_context]
48
+
49
+ def set_context(self, name: str) -> None:
50
+ self._current_context = name
51
+
52
+ def reset(self, *args, **kwargs) -> None:
53
+ for name, state in list(self._state_cache.items()):
54
+ state.reset(*args, **kwargs)
55
+ self._state_cache.pop(name)
56
+ self._current_context = None
57
+
58
+
59
+ class ModelHook:
60
+ r"""
61
+ A hook that contains callbacks to be executed just before and after the forward method of a model.
62
+ """
63
+
64
+ _is_stateful = False
65
+
66
+ def __init__(self):
67
+ self.fn_ref: "HookFunctionReference" = None
68
+
69
+ def initialize_hook(self, module: torch.nn.Module) -> torch.nn.Module:
70
+ r"""
71
+ Hook that is executed when a model is initialized.
72
+
73
+ Args:
74
+ module (`torch.nn.Module`):
75
+ The module attached to this hook.
76
+ """
77
+ return module
78
+
79
+ def deinitalize_hook(self, module: torch.nn.Module) -> torch.nn.Module:
80
+ r"""
81
+ Hook that is executed when a model is deinitialized.
82
+
83
+ Args:
84
+ module (`torch.nn.Module`):
85
+ The module attached to this hook.
86
+ """
87
+ return module
88
+
89
+ def pre_forward(self, module: torch.nn.Module, *args, **kwargs) -> tuple[tuple[Any], dict[str, Any]]:
90
+ r"""
91
+ Hook that is executed just before the forward method of the model.
92
+
93
+ Args:
94
+ module (`torch.nn.Module`):
95
+ The module whose forward pass will be executed just after this event.
96
+ args (`tuple[Any]`):
97
+ The positional arguments passed to the module.
98
+ kwargs (`dict[Str, Any]`):
99
+ The keyword arguments passed to the module.
100
+ Returns:
101
+ `tuple[tuple[Any], dict[Str, Any]]`:
102
+ A tuple with the treated `args` and `kwargs`.
103
+ """
104
+ return args, kwargs
105
+
106
+ def post_forward(self, module: torch.nn.Module, output: Any) -> Any:
107
+ r"""
108
+ Hook that is executed just after the forward method of the model.
109
+
110
+ Args:
111
+ module (`torch.nn.Module`):
112
+ The module whose forward pass been executed just before this event.
113
+ output (`Any`):
114
+ The output of the module.
115
+ Returns:
116
+ `Any`: The processed `output`.
117
+ """
118
+ return output
119
+
120
+ def detach_hook(self, module: torch.nn.Module) -> torch.nn.Module:
121
+ r"""
122
+ Hook that is executed when the hook is detached from a module.
123
+
124
+ Args:
125
+ module (`torch.nn.Module`):
126
+ The module detached from this hook.
127
+ """
128
+ return module
129
+
130
+ def reset_state(self, module: torch.nn.Module):
131
+ if self._is_stateful:
132
+ raise NotImplementedError("This hook is stateful and needs to implement the `reset_state` method.")
133
+ return module
134
+
135
+ def _set_context(self, module: torch.nn.Module, name: str) -> None:
136
+ # Iterate over all attributes of the hook to see if any of them have the type `StateManager`. If so, call `set_context` on them.
137
+ for attr_name in dir(self):
138
+ attr = getattr(self, attr_name)
139
+ if isinstance(attr, StateManager):
140
+ attr.set_context(name)
141
+ return module
142
+
143
+
144
+ class HookFunctionReference:
145
+ def __init__(self) -> None:
146
+ """A container class that maintains mutable references to forward pass functions in a hook chain.
147
+
148
+ Its mutable nature allows the hook system to modify the execution chain dynamically without rebuilding the
149
+ entire forward pass structure.
150
+
151
+ Attributes:
152
+ pre_forward: A callable that processes inputs before the main forward pass.
153
+ post_forward: A callable that processes outputs after the main forward pass.
154
+ forward: The current forward function in the hook chain.
155
+ original_forward: The original forward function, stored when a hook provides a custom new_forward.
156
+
157
+ The class enables hook removal by allowing updates to the forward chain through reference modification rather
158
+ than requiring reconstruction of the entire chain. When a hook is removed, only the relevant references need to
159
+ be updated, preserving the execution order of the remaining hooks.
160
+ """
161
+ self.pre_forward = None
162
+ self.post_forward = None
163
+ self.forward = None
164
+ self.original_forward = None
165
+
166
+
167
+ class HookRegistry:
168
+ def __init__(self, module_ref: torch.nn.Module) -> None:
169
+ super().__init__()
170
+
171
+ self.hooks: dict[str, ModelHook] = {}
172
+
173
+ self._module_ref = module_ref
174
+ self._hook_order = []
175
+ self._fn_refs = []
176
+
177
+ def register_hook(self, hook: ModelHook, name: str) -> None:
178
+ if name in self.hooks.keys():
179
+ raise ValueError(
180
+ f"Hook with name {name} already exists in the registry. Please use a different name or "
181
+ f"first remove the existing hook and then add a new one."
182
+ )
183
+
184
+ self._module_ref = hook.initialize_hook(self._module_ref)
185
+
186
+ def create_new_forward(function_reference: HookFunctionReference):
187
+ def new_forward(module, *args, **kwargs):
188
+ args, kwargs = function_reference.pre_forward(module, *args, **kwargs)
189
+ output = function_reference.forward(*args, **kwargs)
190
+ return function_reference.post_forward(module, output)
191
+
192
+ return new_forward
193
+
194
+ forward = self._module_ref.forward
195
+
196
+ fn_ref = HookFunctionReference()
197
+ fn_ref.pre_forward = hook.pre_forward
198
+ fn_ref.post_forward = hook.post_forward
199
+ fn_ref.forward = forward
200
+
201
+ if hasattr(hook, "new_forward"):
202
+ fn_ref.original_forward = forward
203
+ fn_ref.forward = functools.update_wrapper(
204
+ functools.partial(hook.new_forward, self._module_ref), hook.new_forward
205
+ )
206
+
207
+ rewritten_forward = create_new_forward(fn_ref)
208
+ self._module_ref.forward = functools.update_wrapper(
209
+ functools.partial(rewritten_forward, self._module_ref), rewritten_forward
210
+ )
211
+
212
+ hook.fn_ref = fn_ref
213
+ self.hooks[name] = hook
214
+ self._hook_order.append(name)
215
+ self._fn_refs.append(fn_ref)
216
+
217
+ def get_hook(self, name: str) -> ModelHook | None:
218
+ return self.hooks.get(name, None)
219
+
220
+ def remove_hook(self, name: str, recurse: bool = True) -> None:
221
+ if name in self.hooks.keys():
222
+ num_hooks = len(self._hook_order)
223
+ hook = self.hooks[name]
224
+ index = self._hook_order.index(name)
225
+ fn_ref = self._fn_refs[index]
226
+
227
+ old_forward = fn_ref.forward
228
+ if fn_ref.original_forward is not None:
229
+ old_forward = fn_ref.original_forward
230
+
231
+ if index == num_hooks - 1:
232
+ self._module_ref.forward = old_forward
233
+ else:
234
+ self._fn_refs[index + 1].forward = old_forward
235
+
236
+ self._module_ref = hook.deinitalize_hook(self._module_ref)
237
+ del self.hooks[name]
238
+ self._hook_order.pop(index)
239
+ self._fn_refs.pop(index)
240
+
241
+ if recurse:
242
+ for module_name, module in self._module_ref.named_modules():
243
+ if module_name == "":
244
+ continue
245
+ if hasattr(module, "_diffusers_hook"):
246
+ module._diffusers_hook.remove_hook(name, recurse=False)
247
+
248
+ def reset_stateful_hooks(self, recurse: bool = True) -> None:
249
+ for hook_name in reversed(self._hook_order):
250
+ hook = self.hooks[hook_name]
251
+ if hook._is_stateful:
252
+ hook.reset_state(self._module_ref)
253
+
254
+ if recurse:
255
+ for module_name, module in unwrap_module(self._module_ref).named_modules():
256
+ if module_name == "":
257
+ continue
258
+ module = unwrap_module(module)
259
+ if hasattr(module, "_diffusers_hook"):
260
+ module._diffusers_hook.reset_stateful_hooks(recurse=False)
261
+
262
+ @classmethod
263
+ def check_if_exists_or_initialize(cls, module: torch.nn.Module) -> "HookRegistry":
264
+ if not hasattr(module, "_diffusers_hook"):
265
+ module._diffusers_hook = cls(module)
266
+ return module._diffusers_hook
267
+
268
+ def _set_context(self, name: str | None = None) -> None:
269
+ for hook_name in reversed(self._hook_order):
270
+ hook = self.hooks[hook_name]
271
+ if hook._is_stateful:
272
+ hook._set_context(self._module_ref, name)
273
+
274
+ for registry in self._get_child_registries():
275
+ registry._set_context(name)
276
+
277
+ def _get_child_registries(self) -> list["HookRegistry"]:
278
+ """Return registries of child modules, using a cached list when available.
279
+
280
+ The cache is built on first call and reused for subsequent calls. This avoids the cost of walking the full
281
+ module tree via named_modules() on every _set_context call, which is significant for large models (e.g. ~2.7ms
282
+ per call on Flux2).
283
+ """
284
+ if not hasattr(self, "_child_registries_cache"):
285
+ self._child_registries_cache = None
286
+
287
+ if self._child_registries_cache is not None:
288
+ return self._child_registries_cache
289
+
290
+ registries = []
291
+ for module_name, module in unwrap_module(self._module_ref).named_modules():
292
+ if module_name == "":
293
+ continue
294
+ module = unwrap_module(module)
295
+ if hasattr(module, "_diffusers_hook"):
296
+ registries.append(module._diffusers_hook)
297
+ self._child_registries_cache = registries
298
+ return registries
299
+
300
+ def __repr__(self) -> str:
301
+ registry_repr = ""
302
+ for i, hook_name in enumerate(self._hook_order):
303
+ if self.hooks[hook_name].__class__.__repr__ is not object.__repr__:
304
+ hook_repr = self.hooks[hook_name].__repr__()
305
+ else:
306
+ hook_repr = self.hooks[hook_name].__class__.__name__
307
+ registry_repr += f" ({i}) {hook_name} - {hook_repr}"
308
+ if i < len(self._hook_order) - 1:
309
+ registry_repr += "\n"
310
+ return f"HookRegistry(\n{registry_repr}\n)"
third_party/diffusers/src/diffusers/hooks/layer_skip.py ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import math
16
+ from dataclasses import asdict, dataclass
17
+ from typing import Callable
18
+
19
+ import torch
20
+
21
+ from ..utils import get_logger
22
+ from ..utils.torch_utils import unwrap_module
23
+ from ._common import (
24
+ _ALL_TRANSFORMER_BLOCK_IDENTIFIERS,
25
+ _ATTENTION_CLASSES,
26
+ _FEEDFORWARD_CLASSES,
27
+ _get_submodule_from_fqn,
28
+ )
29
+ from ._helpers import AttentionProcessorRegistry, TransformerBlockRegistry
30
+ from .hooks import HookRegistry, ModelHook
31
+
32
+
33
+ logger = get_logger(__name__) # pylint: disable=invalid-name
34
+
35
+ _LAYER_SKIP_HOOK = "layer_skip_hook"
36
+
37
+
38
+ # Aryan/YiYi TODO: we need to make guider class a config mixin so I think this is not needed
39
+ # either remove or make it serializable
40
+ @dataclass
41
+ class LayerSkipConfig:
42
+ r"""
43
+ Configuration for skipping internal transformer blocks when executing a transformer model.
44
+
45
+ Args:
46
+ indices (`list[int]`):
47
+ The indices of the layer to skip. This is typically the first layer in the transformer block.
48
+ fqn (`str`, defaults to `"auto"`):
49
+ The fully qualified name identifying the stack of transformer blocks. Typically, this is
50
+ `transformer_blocks`, `single_transformer_blocks`, `blocks`, `layers`, or `temporal_transformer_blocks`.
51
+ For automatic detection, set this to `"auto"`. "auto" only works on DiT models. For UNet models, you must
52
+ provide the correct fqn.
53
+ skip_attention (`bool`, defaults to `True`):
54
+ Whether to skip attention blocks.
55
+ skip_ff (`bool`, defaults to `True`):
56
+ Whether to skip feed-forward blocks.
57
+ skip_attention_scores (`bool`, defaults to `False`):
58
+ Whether to skip attention score computation in the attention blocks. This is equivalent to using `value`
59
+ projections as the output of scaled dot product attention.
60
+ dropout (`float`, defaults to `1.0`):
61
+ The dropout probability for dropping the outputs of the skipped layers. By default, this is set to `1.0`,
62
+ meaning that the outputs of the skipped layers are completely ignored. If set to `0.0`, the outputs of the
63
+ skipped layers are fully retained, which is equivalent to not skipping any layers.
64
+ """
65
+
66
+ indices: list[int]
67
+ fqn: str = "auto"
68
+ skip_attention: bool = True
69
+ skip_attention_scores: bool = False
70
+ skip_ff: bool = True
71
+ dropout: float = 1.0
72
+
73
+ def __post_init__(self):
74
+ if not (0 <= self.dropout <= 1):
75
+ raise ValueError(f"Expected `dropout` to be between 0.0 and 1.0, but got {self.dropout}.")
76
+ if not math.isclose(self.dropout, 1.0) and self.skip_attention_scores:
77
+ raise ValueError(
78
+ "Cannot set `skip_attention_scores` to True when `dropout` is not 1.0. Please set `dropout` to 1.0."
79
+ )
80
+
81
+ def to_dict(self):
82
+ return asdict(self)
83
+
84
+ @staticmethod
85
+ def from_dict(data: dict) -> "LayerSkipConfig":
86
+ return LayerSkipConfig(**data)
87
+
88
+
89
+ class AttentionScoreSkipFunctionMode(torch.overrides.TorchFunctionMode):
90
+ def __torch_function__(self, func, types, args=(), kwargs=None):
91
+ if kwargs is None:
92
+ kwargs = {}
93
+ if func is torch.nn.functional.scaled_dot_product_attention:
94
+ query = kwargs.get("query", None)
95
+ key = kwargs.get("key", None)
96
+ value = kwargs.get("value", None)
97
+ query = query if query is not None else args[0]
98
+ key = key if key is not None else args[1]
99
+ value = value if value is not None else args[2]
100
+ # If the Q sequence length does not match KV sequence length, methods like
101
+ # Perturbed Attention Guidance cannot be used (because the caller expects
102
+ # the same sequence length as Q, but if we return V here, it will not match).
103
+ # When Q.shape[2] != V.shape[2], PAG will essentially not be applied and
104
+ # the overall effect would that be of normal CFG with a scale of (guidance_scale + perturbed_guidance_scale).
105
+ if query.shape[2] == value.shape[2]:
106
+ return value
107
+ return func(*args, **kwargs)
108
+
109
+
110
+ class AttentionProcessorSkipHook(ModelHook):
111
+ def __init__(self, skip_processor_output_fn: Callable, skip_attention_scores: bool = False, dropout: float = 1.0):
112
+ self.skip_processor_output_fn = skip_processor_output_fn
113
+ self.skip_attention_scores = skip_attention_scores
114
+ self.dropout = dropout
115
+
116
+ def new_forward(self, module: torch.nn.Module, *args, **kwargs):
117
+ if self.skip_attention_scores:
118
+ if not math.isclose(self.dropout, 1.0):
119
+ raise ValueError(
120
+ "Cannot set `skip_attention_scores` to True when `dropout` is not 1.0. Please set `dropout` to 1.0."
121
+ )
122
+ with AttentionScoreSkipFunctionMode():
123
+ output = self.fn_ref.original_forward(*args, **kwargs)
124
+ else:
125
+ if math.isclose(self.dropout, 1.0):
126
+ output = self.skip_processor_output_fn(module, *args, **kwargs)
127
+ else:
128
+ output = self.fn_ref.original_forward(*args, **kwargs)
129
+ output = torch.nn.functional.dropout(output, p=self.dropout)
130
+ return output
131
+
132
+
133
+ class FeedForwardSkipHook(ModelHook):
134
+ def __init__(self, dropout: float):
135
+ super().__init__()
136
+ self.dropout = dropout
137
+
138
+ def new_forward(self, module: torch.nn.Module, *args, **kwargs):
139
+ if math.isclose(self.dropout, 1.0):
140
+ output = kwargs.get("hidden_states", None)
141
+ if output is None:
142
+ output = kwargs.get("x", None)
143
+ if output is None and len(args) > 0:
144
+ output = args[0]
145
+ else:
146
+ output = self.fn_ref.original_forward(*args, **kwargs)
147
+ output = torch.nn.functional.dropout(output, p=self.dropout)
148
+ return output
149
+
150
+
151
+ class TransformerBlockSkipHook(ModelHook):
152
+ def __init__(self, dropout: float):
153
+ super().__init__()
154
+ self.dropout = dropout
155
+
156
+ def initialize_hook(self, module):
157
+ self._metadata = TransformerBlockRegistry.get(unwrap_module(module).__class__)
158
+ return module
159
+
160
+ def new_forward(self, module: torch.nn.Module, *args, **kwargs):
161
+ if math.isclose(self.dropout, 1.0):
162
+ original_hidden_states = self._metadata._get_parameter_from_args_kwargs("hidden_states", args, kwargs)
163
+ if self._metadata.return_encoder_hidden_states_index is None:
164
+ output = original_hidden_states
165
+ else:
166
+ original_encoder_hidden_states = self._metadata._get_parameter_from_args_kwargs(
167
+ "encoder_hidden_states", args, kwargs
168
+ )
169
+ output = (original_hidden_states, original_encoder_hidden_states)
170
+ else:
171
+ output = self.fn_ref.original_forward(*args, **kwargs)
172
+ output = torch.nn.functional.dropout(output, p=self.dropout)
173
+ return output
174
+
175
+
176
+ def apply_layer_skip(module: torch.nn.Module, config: LayerSkipConfig) -> None:
177
+ r"""
178
+ Apply layer skipping to internal layers of a transformer.
179
+
180
+ Args:
181
+ module (`torch.nn.Module`):
182
+ The transformer model to which the layer skip hook should be applied.
183
+ config (`LayerSkipConfig`):
184
+ The configuration for the layer skip hook.
185
+
186
+ Example:
187
+
188
+ ```python
189
+ >>> from diffusers import apply_layer_skip_hook, CogVideoXTransformer3DModel, LayerSkipConfig
190
+
191
+ >>> transformer = CogVideoXTransformer3DModel.from_pretrained("THUDM/CogVideoX-5b", torch_dtype=torch.bfloat16)
192
+ >>> config = LayerSkipConfig(layer_index=[10, 20], fqn="transformer_blocks")
193
+ >>> apply_layer_skip_hook(transformer, config)
194
+ ```
195
+ """
196
+ _apply_layer_skip_hook(module, config)
197
+
198
+
199
+ def _apply_layer_skip_hook(module: torch.nn.Module, config: LayerSkipConfig, name: str | None = None) -> None:
200
+ name = name or _LAYER_SKIP_HOOK
201
+
202
+ if config.skip_attention and config.skip_attention_scores:
203
+ raise ValueError("Cannot set both `skip_attention` and `skip_attention_scores` to True. Please choose one.")
204
+ if not math.isclose(config.dropout, 1.0) and config.skip_attention_scores:
205
+ raise ValueError(
206
+ "Cannot set `skip_attention_scores` to True when `dropout` is not 1.0. Please set `dropout` to 1.0."
207
+ )
208
+
209
+ if config.fqn == "auto":
210
+ for identifier in _ALL_TRANSFORMER_BLOCK_IDENTIFIERS:
211
+ if hasattr(module, identifier):
212
+ config.fqn = identifier
213
+ break
214
+ else:
215
+ raise ValueError(
216
+ "Could not find a suitable identifier for the transformer blocks automatically. Please provide a valid "
217
+ "`fqn` (fully qualified name) that identifies a stack of transformer blocks."
218
+ )
219
+
220
+ transformer_blocks = _get_submodule_from_fqn(module, config.fqn)
221
+ if transformer_blocks is None or not isinstance(transformer_blocks, torch.nn.ModuleList):
222
+ raise ValueError(
223
+ f"Could not find {config.fqn} in the provided module, or configured `fqn` (fully qualified name) does not identify "
224
+ f"a `torch.nn.ModuleList`. Please provide a valid `fqn` that identifies a stack of transformer blocks."
225
+ )
226
+ if len(config.indices) == 0:
227
+ raise ValueError("Layer index list is empty. Please provide a non-empty list of layer indices to skip.")
228
+
229
+ blocks_found = False
230
+ for i, block in enumerate(transformer_blocks):
231
+ if i not in config.indices:
232
+ continue
233
+
234
+ blocks_found = True
235
+
236
+ if config.skip_attention and config.skip_ff:
237
+ logger.debug(f"Applying TransformerBlockSkipHook to '{config.fqn}.{i}'")
238
+ registry = HookRegistry.check_if_exists_or_initialize(block)
239
+ hook = TransformerBlockSkipHook(config.dropout)
240
+ registry.register_hook(hook, name)
241
+
242
+ elif config.skip_attention or config.skip_attention_scores:
243
+ for submodule_name, submodule in block.named_modules():
244
+ if isinstance(submodule, _ATTENTION_CLASSES) and not submodule.is_cross_attention:
245
+ logger.debug(f"Applying AttentionProcessorSkipHook to '{config.fqn}.{i}.{submodule_name}'")
246
+ output_fn = AttentionProcessorRegistry.get(submodule.processor.__class__).skip_processor_output_fn
247
+ registry = HookRegistry.check_if_exists_or_initialize(submodule)
248
+ hook = AttentionProcessorSkipHook(output_fn, config.skip_attention_scores, config.dropout)
249
+ registry.register_hook(hook, name)
250
+
251
+ if config.skip_ff:
252
+ for submodule_name, submodule in block.named_modules():
253
+ if isinstance(submodule, _FEEDFORWARD_CLASSES):
254
+ logger.debug(f"Applying FeedForwardSkipHook to '{config.fqn}.{i}.{submodule_name}'")
255
+ registry = HookRegistry.check_if_exists_or_initialize(submodule)
256
+ hook = FeedForwardSkipHook(config.dropout)
257
+ registry.register_hook(hook, name)
258
+
259
+ if not blocks_found:
260
+ raise ValueError(
261
+ f"Could not find any transformer blocks matching the provided indices {config.indices} and "
262
+ f"fully qualified name '{config.fqn}'. Please check the indices and fqn for correctness."
263
+ )
third_party/diffusers/src/diffusers/hooks/layerwise_casting.py ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import re
16
+ from typing import Type
17
+
18
+ import torch
19
+
20
+ from ..utils import get_logger, is_peft_available, is_peft_version
21
+ from ._common import _GO_LC_SUPPORTED_PYTORCH_LAYERS
22
+ from .hooks import HookRegistry, ModelHook
23
+
24
+
25
+ logger = get_logger(__name__) # pylint: disable=invalid-name
26
+
27
+
28
+ # fmt: off
29
+ _LAYERWISE_CASTING_HOOK = "layerwise_casting"
30
+ _PEFT_AUTOCAST_DISABLE_HOOK = "peft_autocast_disable"
31
+ DEFAULT_SKIP_MODULES_PATTERN = ("pos_embed", "patch_embed", "norm", "^proj_in$", "^proj_out$")
32
+ # fmt: on
33
+
34
+ _SHOULD_DISABLE_PEFT_INPUT_AUTOCAST = is_peft_available() and is_peft_version(">", "0.14.0")
35
+ if _SHOULD_DISABLE_PEFT_INPUT_AUTOCAST:
36
+ from peft.helpers import disable_input_dtype_casting
37
+ from peft.tuners.tuners_utils import BaseTunerLayer
38
+
39
+
40
+ class LayerwiseCastingHook(ModelHook):
41
+ r"""
42
+ A hook that casts the weights of a module to a high precision dtype for computation, and to a low precision dtype
43
+ for storage. This process may lead to quality loss in the output, but can significantly reduce the memory
44
+ footprint.
45
+ """
46
+
47
+ _is_stateful = False
48
+
49
+ def __init__(self, storage_dtype: torch.dtype, compute_dtype: torch.dtype, non_blocking: bool) -> None:
50
+ self.storage_dtype = storage_dtype
51
+ self.compute_dtype = compute_dtype
52
+ self.non_blocking = non_blocking
53
+
54
+ def initialize_hook(self, module: torch.nn.Module):
55
+ module.to(dtype=self.storage_dtype, non_blocking=self.non_blocking)
56
+ return module
57
+
58
+ def deinitalize_hook(self, module: torch.nn.Module):
59
+ raise NotImplementedError(
60
+ "LayerwiseCastingHook does not support deinitialization. A model once enabled with layerwise casting will "
61
+ "have casted its weights to a lower precision dtype for storage. Casting this back to the original dtype "
62
+ "will lead to precision loss, which might have an impact on the model's generation quality. The model should "
63
+ "be re-initialized and loaded in the original dtype."
64
+ )
65
+
66
+ def pre_forward(self, module: torch.nn.Module, *args, **kwargs):
67
+ module.to(dtype=self.compute_dtype, non_blocking=self.non_blocking)
68
+ return args, kwargs
69
+
70
+ def post_forward(self, module: torch.nn.Module, output):
71
+ module.to(dtype=self.storage_dtype, non_blocking=self.non_blocking)
72
+ return output
73
+
74
+
75
+ class PeftInputAutocastDisableHook(ModelHook):
76
+ r"""
77
+ A hook that disables the casting of inputs to the module weight dtype during the forward pass. By default, PEFT
78
+ casts the inputs to the weight dtype of the module, which can lead to precision loss.
79
+
80
+ The reasons for needing this are:
81
+ - If we don't add PEFT layers' weight names to `skip_modules_pattern` when applying layerwise casting, the
82
+ inputs will be casted to the, possibly lower precision, storage dtype. Reference:
83
+ https://github.com/huggingface/peft/blob/0facdebf6208139cbd8f3586875acb378813dd97/src/peft/tuners/lora/layer.py#L706
84
+ - We can, on our end, use something like accelerate's `send_to_device` but for dtypes. This way, we can ensure
85
+ that the inputs are casted to the computation dtype correctly always. However, there are two goals we are
86
+ hoping to achieve:
87
+ 1. Making forward implementations independent of device/dtype casting operations as much as possible.
88
+ 2. Performing inference without losing information from casting to different precisions. With the current
89
+ PEFT implementation (as linked in the reference above), and assuming running layerwise casting inference
90
+ with storage_dtype=torch.float8_e4m3fn and compute_dtype=torch.bfloat16, inputs are cast to
91
+ torch.float8_e4m3fn in the lora layer. We will then upcast back to torch.bfloat16 when we continue the
92
+ forward pass in PEFT linear forward or Diffusers layer forward, with a `send_to_dtype` operation from
93
+ LayerwiseCastingHook. This will be a lossy operation and result in poorer generation quality.
94
+ """
95
+
96
+ def new_forward(self, module: torch.nn.Module, *args, **kwargs):
97
+ with disable_input_dtype_casting(module):
98
+ return self.fn_ref.original_forward(*args, **kwargs)
99
+
100
+
101
+ def apply_layerwise_casting(
102
+ module: torch.nn.Module,
103
+ storage_dtype: torch.dtype,
104
+ compute_dtype: torch.dtype,
105
+ skip_modules_pattern: str | tuple[str, ...] = "auto",
106
+ skip_modules_classes: tuple[Type[torch.nn.Module], ...] | None = None,
107
+ non_blocking: bool = False,
108
+ ) -> None:
109
+ r"""
110
+ Applies layerwise casting to a given module. The module expected here is a Diffusers ModelMixin but it can be any
111
+ nn.Module using diffusers layers or pytorch primitives.
112
+
113
+ Example:
114
+
115
+ ```python
116
+ >>> import torch
117
+ >>> from diffusers import CogVideoXTransformer3DModel
118
+
119
+ >>> transformer = CogVideoXTransformer3DModel.from_pretrained(
120
+ ... model_id, subfolder="transformer", torch_dtype=torch.bfloat16
121
+ ... )
122
+
123
+ >>> apply_layerwise_casting(
124
+ ... transformer,
125
+ ... storage_dtype=torch.float8_e4m3fn,
126
+ ... compute_dtype=torch.bfloat16,
127
+ ... skip_modules_pattern=["patch_embed", "norm", "proj_out"],
128
+ ... non_blocking=True,
129
+ ... )
130
+ ```
131
+
132
+ Args:
133
+ module (`torch.nn.Module`):
134
+ The module whose leaf modules will be cast to a high precision dtype for computation, and to a low
135
+ precision dtype for storage.
136
+ storage_dtype (`torch.dtype`):
137
+ The dtype to cast the module to before/after the forward pass for storage.
138
+ compute_dtype (`torch.dtype`):
139
+ The dtype to cast the module to during the forward pass for computation.
140
+ skip_modules_pattern (`tuple[str, ...]`, defaults to `"auto"`):
141
+ A list of patterns to match the names of the modules to skip during the layerwise casting process. If set
142
+ to `"auto"`, the default patterns are used. If set to `None`, no modules are skipped. If set to `None`
143
+ alongside `skip_modules_classes` being `None`, the layerwise casting is applied directly to the module
144
+ instead of its internal submodules.
145
+ skip_modules_classes (`tuple[Type[torch.nn.Module], ...]`, defaults to `None`):
146
+ A list of module classes to skip during the layerwise casting process.
147
+ non_blocking (`bool`, defaults to `False`):
148
+ If `True`, the weight casting operations are non-blocking.
149
+ """
150
+ if skip_modules_pattern == "auto":
151
+ skip_modules_pattern = DEFAULT_SKIP_MODULES_PATTERN
152
+
153
+ if skip_modules_classes is None and skip_modules_pattern is None:
154
+ apply_layerwise_casting_hook(module, storage_dtype, compute_dtype, non_blocking)
155
+ return
156
+
157
+ _apply_layerwise_casting(
158
+ module,
159
+ storage_dtype,
160
+ compute_dtype,
161
+ skip_modules_pattern,
162
+ skip_modules_classes,
163
+ non_blocking,
164
+ )
165
+ _disable_peft_input_autocast(module)
166
+
167
+
168
+ def _apply_layerwise_casting(
169
+ module: torch.nn.Module,
170
+ storage_dtype: torch.dtype,
171
+ compute_dtype: torch.dtype,
172
+ skip_modules_pattern: tuple[str, ...] | None = None,
173
+ skip_modules_classes: tuple[Type[torch.nn.Module], ...] | None = None,
174
+ non_blocking: bool = False,
175
+ _prefix: str = "",
176
+ ) -> None:
177
+ should_skip = (skip_modules_classes is not None and isinstance(module, skip_modules_classes)) or (
178
+ skip_modules_pattern is not None and any(re.search(pattern, _prefix) for pattern in skip_modules_pattern)
179
+ )
180
+ if should_skip:
181
+ logger.debug(f'Skipping layerwise casting for layer "{_prefix}"')
182
+ return
183
+
184
+ if isinstance(module, _GO_LC_SUPPORTED_PYTORCH_LAYERS):
185
+ logger.debug(f'Applying layerwise casting to layer "{_prefix}"')
186
+ apply_layerwise_casting_hook(module, storage_dtype, compute_dtype, non_blocking)
187
+ return
188
+
189
+ for name, submodule in module.named_children():
190
+ layer_name = f"{_prefix}.{name}" if _prefix else name
191
+ _apply_layerwise_casting(
192
+ submodule,
193
+ storage_dtype,
194
+ compute_dtype,
195
+ skip_modules_pattern,
196
+ skip_modules_classes,
197
+ non_blocking,
198
+ _prefix=layer_name,
199
+ )
200
+
201
+
202
+ def apply_layerwise_casting_hook(
203
+ module: torch.nn.Module, storage_dtype: torch.dtype, compute_dtype: torch.dtype, non_blocking: bool
204
+ ) -> None:
205
+ r"""
206
+ Applies a `LayerwiseCastingHook` to a given module.
207
+
208
+ Args:
209
+ module (`torch.nn.Module`):
210
+ The module to attach the hook to.
211
+ storage_dtype (`torch.dtype`):
212
+ The dtype to cast the module to before the forward pass.
213
+ compute_dtype (`torch.dtype`):
214
+ The dtype to cast the module to during the forward pass.
215
+ non_blocking (`bool`):
216
+ If `True`, the weight casting operations are non-blocking.
217
+ """
218
+ registry = HookRegistry.check_if_exists_or_initialize(module)
219
+ hook = LayerwiseCastingHook(storage_dtype, compute_dtype, non_blocking)
220
+ registry.register_hook(hook, _LAYERWISE_CASTING_HOOK)
221
+
222
+
223
+ def _is_layerwise_casting_active(module: torch.nn.Module) -> bool:
224
+ for submodule in module.modules():
225
+ if (
226
+ hasattr(submodule, "_diffusers_hook")
227
+ and submodule._diffusers_hook.get_hook(_LAYERWISE_CASTING_HOOK) is not None
228
+ ):
229
+ return True
230
+ return False
231
+
232
+
233
+ def _disable_peft_input_autocast(module: torch.nn.Module) -> None:
234
+ if not _SHOULD_DISABLE_PEFT_INPUT_AUTOCAST:
235
+ return
236
+ for submodule in module.modules():
237
+ if isinstance(submodule, BaseTunerLayer) and _is_layerwise_casting_active(submodule):
238
+ registry = HookRegistry.check_if_exists_or_initialize(submodule)
239
+ hook = PeftInputAutocastDisableHook()
240
+ registry.register_hook(hook, _PEFT_AUTOCAST_DISABLE_HOOK)
third_party/diffusers/src/diffusers/hooks/mag_cache.py ADDED
@@ -0,0 +1,468 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from dataclasses import dataclass
16
+ from typing import List, Optional, Tuple, Union
17
+
18
+ import torch
19
+
20
+ from ..utils import get_logger
21
+ from ..utils.torch_utils import unwrap_module
22
+ from ._common import _ALL_TRANSFORMER_BLOCK_IDENTIFIERS
23
+ from ._helpers import TransformerBlockRegistry
24
+ from .hooks import BaseState, HookRegistry, ModelHook, StateManager
25
+
26
+
27
+ logger = get_logger(__name__) # pylint: disable=invalid-name
28
+
29
+ _MAG_CACHE_LEADER_BLOCK_HOOK = "mag_cache_leader_block_hook"
30
+ _MAG_CACHE_BLOCK_HOOK = "mag_cache_block_hook"
31
+
32
+ # Default Mag Ratios for Flux models (Dev/Schnell) are provided for convenience.
33
+ # Users must explicitly pass these to the config if using Flux.
34
+ # Reference: https://github.com/Zehong-Ma/MagCache
35
+ FLUX_MAG_RATIOS = torch.tensor(
36
+ [1.0]
37
+ + [
38
+ 1.21094,
39
+ 1.11719,
40
+ 1.07812,
41
+ 1.0625,
42
+ 1.03906,
43
+ 1.03125,
44
+ 1.03906,
45
+ 1.02344,
46
+ 1.03125,
47
+ 1.02344,
48
+ 0.98047,
49
+ 1.01562,
50
+ 1.00781,
51
+ 1.0,
52
+ 1.00781,
53
+ 1.0,
54
+ 1.00781,
55
+ 1.0,
56
+ 1.0,
57
+ 0.99609,
58
+ 0.99609,
59
+ 0.98047,
60
+ 0.98828,
61
+ 0.96484,
62
+ 0.95703,
63
+ 0.93359,
64
+ 0.89062,
65
+ ]
66
+ )
67
+
68
+
69
+ def nearest_interp(src_array: torch.Tensor, target_length: int) -> torch.Tensor:
70
+ """
71
+ Interpolate the source array to the target length using nearest neighbor interpolation.
72
+ """
73
+ src_length = len(src_array)
74
+ if target_length == 1:
75
+ return src_array[-1:]
76
+
77
+ scale = (src_length - 1) / (target_length - 1)
78
+ grid = torch.arange(target_length, device=src_array.device, dtype=torch.float32)
79
+ mapped_indices = torch.round(grid * scale).long()
80
+ return src_array[mapped_indices]
81
+
82
+
83
+ @dataclass
84
+ class MagCacheConfig:
85
+ r"""
86
+ Configuration for [MagCache](https://github.com/Zehong-Ma/MagCache).
87
+
88
+ Args:
89
+ threshold (`float`, defaults to `0.06`):
90
+ The threshold for the accumulated error. If the accumulated error is below this threshold, the block
91
+ computation is skipped. A higher threshold allows for more aggressive skipping (faster) but may degrade
92
+ quality.
93
+ max_skip_steps (`int`, defaults to `3`):
94
+ The maximum number of consecutive steps that can be skipped (K in the paper).
95
+ retention_ratio (`float`, defaults to `0.2`):
96
+ The fraction of initial steps during which skipping is disabled to ensure stability. For example, if
97
+ `num_inference_steps` is 28 and `retention_ratio` is 0.2, the first 6 steps will never be skipped.
98
+ num_inference_steps (`int`, defaults to `28`):
99
+ The number of inference steps used in the pipeline. This is required to interpolate `mag_ratios` correctly.
100
+ mag_ratios (`torch.Tensor`, *optional*):
101
+ The pre-computed magnitude ratios for the model. These are checkpoint-dependent. If not provided, you must
102
+ set `calibrate=True` to calculate them for your specific model. For Flux models, you can use
103
+ `diffusers.hooks.mag_cache.FLUX_MAG_RATIOS`.
104
+ calibrate (`bool`, defaults to `False`):
105
+ If True, enables calibration mode. In this mode, no blocks are skipped. Instead, the hook calculates the
106
+ magnitude ratios for the current run and logs them at the end. Use this to obtain `mag_ratios` for new
107
+ models or schedulers.
108
+ """
109
+
110
+ threshold: float = 0.06
111
+ max_skip_steps: int = 3
112
+ retention_ratio: float = 0.2
113
+ num_inference_steps: int = 28
114
+ mag_ratios: Optional[Union[torch.Tensor, List[float]]] = None
115
+ calibrate: bool = False
116
+
117
+ def __post_init__(self):
118
+ # User MUST provide ratios OR enable calibration.
119
+ if self.mag_ratios is None and not self.calibrate:
120
+ raise ValueError(
121
+ " `mag_ratios` must be provided for MagCache inference because these ratios are model-dependent.\n"
122
+ "To get them for your model:\n"
123
+ "1. Initialize `MagCacheConfig(calibrate=True, ...)`\n"
124
+ "2. Run inference on your model once.\n"
125
+ "3. Copy the printed ratios array and pass it to `mag_ratios` in the config.\n"
126
+ "For Flux models, you can import `FLUX_MAG_RATIOS` from `diffusers.hooks.mag_cache`."
127
+ )
128
+
129
+ if not self.calibrate and self.mag_ratios is not None:
130
+ if not torch.is_tensor(self.mag_ratios):
131
+ self.mag_ratios = torch.tensor(self.mag_ratios)
132
+
133
+ if len(self.mag_ratios) != self.num_inference_steps:
134
+ logger.debug(
135
+ f"Interpolating mag_ratios from length {len(self.mag_ratios)} to {self.num_inference_steps}"
136
+ )
137
+ self.mag_ratios = nearest_interp(self.mag_ratios, self.num_inference_steps)
138
+
139
+
140
+ class MagCacheState(BaseState):
141
+ def __init__(self) -> None:
142
+ super().__init__()
143
+ # Cache for the residual (output - input) from the *previous* timestep
144
+ self.previous_residual: torch.Tensor = None
145
+
146
+ # State inputs/outputs for the current forward pass
147
+ self.head_block_input: Union[torch.Tensor, Tuple[torch.Tensor, ...]] = None
148
+ self.should_compute: bool = True
149
+
150
+ # MagCache accumulators
151
+ self.accumulated_ratio: float = 1.0
152
+ self.accumulated_err: float = 0.0
153
+ self.accumulated_steps: int = 0
154
+
155
+ # Current step counter (timestep index)
156
+ self.step_index: int = 0
157
+
158
+ # Calibration storage
159
+ self.calibration_ratios: List[float] = []
160
+
161
+ def reset(self):
162
+ self.previous_residual = None
163
+ self.should_compute = True
164
+ self.accumulated_ratio = 1.0
165
+ self.accumulated_err = 0.0
166
+ self.accumulated_steps = 0
167
+ self.step_index = 0
168
+ self.calibration_ratios = []
169
+
170
+
171
+ class MagCacheHeadHook(ModelHook):
172
+ _is_stateful = True
173
+
174
+ def __init__(self, state_manager: StateManager, config: MagCacheConfig):
175
+ self.state_manager = state_manager
176
+ self.config = config
177
+ self._metadata = None
178
+
179
+ def initialize_hook(self, module):
180
+ unwrapped_module = unwrap_module(module)
181
+ self._metadata = TransformerBlockRegistry.get(unwrapped_module.__class__)
182
+ return module
183
+
184
+ @torch.compiler.disable
185
+ def new_forward(self, module: torch.nn.Module, *args, **kwargs):
186
+ if self.state_manager._current_context is None:
187
+ self.state_manager.set_context("inference")
188
+
189
+ arg_name = self._metadata.hidden_states_argument_name
190
+ hidden_states = self._metadata._get_parameter_from_args_kwargs(arg_name, args, kwargs)
191
+
192
+ state: MagCacheState = self.state_manager.get_state()
193
+ state.head_block_input = hidden_states
194
+
195
+ should_compute = True
196
+
197
+ if self.config.calibrate:
198
+ # Never skip during calibration
199
+ should_compute = True
200
+ else:
201
+ # MagCache Logic
202
+ current_step = state.step_index
203
+ if current_step >= len(self.config.mag_ratios):
204
+ current_scale = 1.0
205
+ else:
206
+ current_scale = self.config.mag_ratios[current_step]
207
+
208
+ retention_step = int(self.config.retention_ratio * self.config.num_inference_steps + 0.5)
209
+
210
+ if current_step >= retention_step:
211
+ state.accumulated_ratio *= current_scale
212
+ state.accumulated_steps += 1
213
+ state.accumulated_err += abs(1.0 - state.accumulated_ratio)
214
+
215
+ if (
216
+ state.previous_residual is not None
217
+ and state.accumulated_err <= self.config.threshold
218
+ and state.accumulated_steps <= self.config.max_skip_steps
219
+ ):
220
+ should_compute = False
221
+ else:
222
+ state.accumulated_ratio = 1.0
223
+ state.accumulated_steps = 0
224
+ state.accumulated_err = 0.0
225
+
226
+ state.should_compute = should_compute
227
+
228
+ if not should_compute:
229
+ logger.debug(f"MagCache: Skipping step {state.step_index}")
230
+ # Apply MagCache: Output = Input + Previous Residual
231
+
232
+ output = hidden_states
233
+ res = state.previous_residual
234
+
235
+ if res.device != output.device:
236
+ res = res.to(output.device)
237
+
238
+ # Attempt to apply residual handling shape mismatches (e.g., text+image vs image only)
239
+ if res.shape == output.shape:
240
+ output = output + res
241
+ elif (
242
+ output.ndim == 3
243
+ and res.ndim == 3
244
+ and output.shape[0] == res.shape[0]
245
+ and output.shape[2] == res.shape[2]
246
+ ):
247
+ # Assuming concatenation where image part is at the end (standard in Flux/SD3)
248
+ diff = output.shape[1] - res.shape[1]
249
+ if diff > 0:
250
+ output = output.clone()
251
+ output[:, diff:, :] = output[:, diff:, :] + res
252
+ else:
253
+ logger.warning(
254
+ f"MagCache: Dimension mismatch. Input {output.shape}, Residual {res.shape}. "
255
+ "Cannot apply residual safely. Returning input without residual."
256
+ )
257
+ else:
258
+ logger.warning(
259
+ f"MagCache: Dimension mismatch. Input {output.shape}, Residual {res.shape}. "
260
+ "Cannot apply residual safely. Returning input without residual."
261
+ )
262
+
263
+ if self._metadata.return_encoder_hidden_states_index is not None:
264
+ original_encoder_hidden_states = self._metadata._get_parameter_from_args_kwargs(
265
+ "encoder_hidden_states", args, kwargs
266
+ )
267
+ max_idx = max(
268
+ self._metadata.return_hidden_states_index, self._metadata.return_encoder_hidden_states_index
269
+ )
270
+ ret_list = [None] * (max_idx + 1)
271
+ ret_list[self._metadata.return_hidden_states_index] = output
272
+ ret_list[self._metadata.return_encoder_hidden_states_index] = original_encoder_hidden_states
273
+ return tuple(ret_list)
274
+ else:
275
+ return output
276
+
277
+ else:
278
+ # Compute original forward
279
+ output = self.fn_ref.original_forward(*args, **kwargs)
280
+ return output
281
+
282
+ def reset_state(self, module):
283
+ self.state_manager.reset()
284
+ return module
285
+
286
+
287
+ class MagCacheBlockHook(ModelHook):
288
+ def __init__(self, state_manager: StateManager, is_tail: bool = False, config: MagCacheConfig = None):
289
+ super().__init__()
290
+ self.state_manager = state_manager
291
+ self.is_tail = is_tail
292
+ self.config = config
293
+ self._metadata = None
294
+
295
+ def initialize_hook(self, module):
296
+ unwrapped_module = unwrap_module(module)
297
+ self._metadata = TransformerBlockRegistry.get(unwrapped_module.__class__)
298
+ return module
299
+
300
+ @torch.compiler.disable
301
+ def new_forward(self, module: torch.nn.Module, *args, **kwargs):
302
+ if self.state_manager._current_context is None:
303
+ self.state_manager.set_context("inference")
304
+ state: MagCacheState = self.state_manager.get_state()
305
+
306
+ if not state.should_compute:
307
+ arg_name = self._metadata.hidden_states_argument_name
308
+ hidden_states = self._metadata._get_parameter_from_args_kwargs(arg_name, args, kwargs)
309
+
310
+ if self.is_tail:
311
+ # Still need to advance step index even if we skip
312
+ self._advance_step(state)
313
+
314
+ if self._metadata.return_encoder_hidden_states_index is not None:
315
+ encoder_hidden_states = self._metadata._get_parameter_from_args_kwargs(
316
+ "encoder_hidden_states", args, kwargs
317
+ )
318
+ max_idx = max(
319
+ self._metadata.return_hidden_states_index, self._metadata.return_encoder_hidden_states_index
320
+ )
321
+ ret_list = [None] * (max_idx + 1)
322
+ ret_list[self._metadata.return_hidden_states_index] = hidden_states
323
+ ret_list[self._metadata.return_encoder_hidden_states_index] = encoder_hidden_states
324
+ return tuple(ret_list)
325
+
326
+ return hidden_states
327
+
328
+ output = self.fn_ref.original_forward(*args, **kwargs)
329
+
330
+ if self.is_tail:
331
+ # Calculate residual for next steps
332
+ if isinstance(output, tuple):
333
+ out_hidden = output[self._metadata.return_hidden_states_index]
334
+ else:
335
+ out_hidden = output
336
+
337
+ in_hidden = state.head_block_input
338
+
339
+ if in_hidden is None:
340
+ return output
341
+
342
+ # Determine residual
343
+ if out_hidden.shape == in_hidden.shape:
344
+ residual = out_hidden - in_hidden
345
+ elif out_hidden.ndim == 3 and in_hidden.ndim == 3 and out_hidden.shape[2] == in_hidden.shape[2]:
346
+ diff = in_hidden.shape[1] - out_hidden.shape[1]
347
+ if diff == 0:
348
+ residual = out_hidden - in_hidden
349
+ else:
350
+ residual = out_hidden - in_hidden # Fallback to matching tail
351
+ else:
352
+ # Fallback for completely mismatched shapes
353
+ residual = out_hidden
354
+
355
+ if self.config.calibrate:
356
+ self._perform_calibration_step(state, residual)
357
+
358
+ state.previous_residual = residual
359
+ self._advance_step(state)
360
+
361
+ return output
362
+
363
+ def _perform_calibration_step(self, state: MagCacheState, current_residual: torch.Tensor):
364
+ if state.previous_residual is None:
365
+ # First step has no previous residual to compare against.
366
+ # log 1.0 as a neutral starting point.
367
+ ratio = 1.0
368
+ else:
369
+ # MagCache Calibration Formula: mean(norm(curr) / norm(prev))
370
+ # norm(dim=-1) gives magnitude of each token vector
371
+ curr_norm = torch.linalg.norm(current_residual.float(), dim=-1)
372
+ prev_norm = torch.linalg.norm(state.previous_residual.float(), dim=-1)
373
+
374
+ # Avoid division by zero
375
+ ratio = (curr_norm / (prev_norm + 1e-8)).mean().item()
376
+
377
+ state.calibration_ratios.append(ratio)
378
+
379
+ def _advance_step(self, state: MagCacheState):
380
+ state.step_index += 1
381
+ if state.step_index >= self.config.num_inference_steps:
382
+ # End of inference loop
383
+ if self.config.calibrate:
384
+ print("\n[MagCache] Calibration Complete. Copy these values to MagCacheConfig(mag_ratios=...):")
385
+ print(f"{state.calibration_ratios}\n")
386
+ logger.info(f"MagCache Calibration Results: {state.calibration_ratios}")
387
+
388
+ # Reset state
389
+ state.step_index = 0
390
+ state.accumulated_ratio = 1.0
391
+ state.accumulated_steps = 0
392
+ state.accumulated_err = 0.0
393
+ state.previous_residual = None
394
+ state.calibration_ratios = []
395
+
396
+
397
+ def apply_mag_cache(module: torch.nn.Module, config: MagCacheConfig) -> None:
398
+ """
399
+ Applies MagCache to a given module (typically a Transformer).
400
+
401
+ Args:
402
+ module (`torch.nn.Module`):
403
+ The module to apply MagCache to.
404
+ config (`MagCacheConfig`):
405
+ The configuration for MagCache.
406
+ """
407
+ # Initialize registry on the root module so the Pipeline can set context.
408
+ HookRegistry.check_if_exists_or_initialize(module)
409
+
410
+ state_manager = StateManager(MagCacheState, (), {})
411
+ remaining_blocks = []
412
+
413
+ for name, submodule in module.named_children():
414
+ if name not in _ALL_TRANSFORMER_BLOCK_IDENTIFIERS or not isinstance(submodule, torch.nn.ModuleList):
415
+ continue
416
+ for index, block in enumerate(submodule):
417
+ remaining_blocks.append((f"{name}.{index}", block))
418
+
419
+ if not remaining_blocks:
420
+ logger.warning("MagCache: No transformer blocks found to apply hooks.")
421
+ return
422
+
423
+ # Handle single-block models
424
+ if len(remaining_blocks) == 1:
425
+ name, block = remaining_blocks[0]
426
+ logger.info(f"MagCache: Applying Head+Tail Hooks to single block '{name}'")
427
+ _apply_mag_cache_block_hook(block, state_manager, config, is_tail=True)
428
+ _apply_mag_cache_head_hook(block, state_manager, config)
429
+ return
430
+
431
+ head_block_name, head_block = remaining_blocks.pop(0)
432
+ tail_block_name, tail_block = remaining_blocks.pop(-1)
433
+
434
+ logger.info(f"MagCache: Applying Head Hook to {head_block_name}")
435
+ _apply_mag_cache_head_hook(head_block, state_manager, config)
436
+
437
+ for name, block in remaining_blocks:
438
+ _apply_mag_cache_block_hook(block, state_manager, config)
439
+
440
+ logger.info(f"MagCache: Applying Tail Hook to {tail_block_name}")
441
+ _apply_mag_cache_block_hook(tail_block, state_manager, config, is_tail=True)
442
+
443
+
444
+ def _apply_mag_cache_head_hook(block: torch.nn.Module, state_manager: StateManager, config: MagCacheConfig) -> None:
445
+ registry = HookRegistry.check_if_exists_or_initialize(block)
446
+
447
+ # Automatically remove existing hook to allow re-application (e.g. switching modes)
448
+ if registry.get_hook(_MAG_CACHE_LEADER_BLOCK_HOOK) is not None:
449
+ registry.remove_hook(_MAG_CACHE_LEADER_BLOCK_HOOK)
450
+
451
+ hook = MagCacheHeadHook(state_manager, config)
452
+ registry.register_hook(hook, _MAG_CACHE_LEADER_BLOCK_HOOK)
453
+
454
+
455
+ def _apply_mag_cache_block_hook(
456
+ block: torch.nn.Module,
457
+ state_manager: StateManager,
458
+ config: MagCacheConfig,
459
+ is_tail: bool = False,
460
+ ) -> None:
461
+ registry = HookRegistry.check_if_exists_or_initialize(block)
462
+
463
+ # Automatically remove existing hook to allow re-application
464
+ if registry.get_hook(_MAG_CACHE_BLOCK_HOOK) is not None:
465
+ registry.remove_hook(_MAG_CACHE_BLOCK_HOOK)
466
+
467
+ hook = MagCacheBlockHook(state_manager, is_tail, config)
468
+ registry.register_hook(hook, _MAG_CACHE_BLOCK_HOOK)
third_party/diffusers/src/diffusers/hooks/pyramid_attention_broadcast.py ADDED
@@ -0,0 +1,314 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import re
16
+ from dataclasses import dataclass
17
+ from typing import Any, Callable
18
+
19
+ import torch
20
+
21
+ from ..models.attention import AttentionModuleMixin
22
+ from ..models.attention_processor import Attention, MochiAttention
23
+ from ..utils import logging
24
+ from ._common import (
25
+ _ATTENTION_CLASSES,
26
+ _CROSS_TRANSFORMER_BLOCK_IDENTIFIERS,
27
+ _SPATIAL_TRANSFORMER_BLOCK_IDENTIFIERS,
28
+ _TEMPORAL_TRANSFORMER_BLOCK_IDENTIFIERS,
29
+ )
30
+ from .hooks import HookRegistry, ModelHook
31
+
32
+
33
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
34
+
35
+
36
+ _PYRAMID_ATTENTION_BROADCAST_HOOK = "pyramid_attention_broadcast"
37
+
38
+
39
+ @dataclass
40
+ class PyramidAttentionBroadcastConfig:
41
+ r"""
42
+ Configuration for Pyramid Attention Broadcast.
43
+
44
+ Args:
45
+ spatial_attention_block_skip_range (`int`, *optional*, defaults to `None`):
46
+ The number of times a specific spatial attention broadcast is skipped before computing the attention states
47
+ to re-use. If this is set to the value `N`, the attention computation will be skipped `N - 1` times (i.e.,
48
+ old attention states will be reused) before computing the new attention states again.
49
+ temporal_attention_block_skip_range (`int`, *optional*, defaults to `None`):
50
+ The number of times a specific temporal attention broadcast is skipped before computing the attention
51
+ states to re-use. If this is set to the value `N`, the attention computation will be skipped `N - 1` times
52
+ (i.e., old attention states will be reused) before computing the new attention states again.
53
+ cross_attention_block_skip_range (`int`, *optional*, defaults to `None`):
54
+ The number of times a specific cross-attention broadcast is skipped before computing the attention states
55
+ to re-use. If this is set to the value `N`, the attention computation will be skipped `N - 1` times (i.e.,
56
+ old attention states will be reused) before computing the new attention states again.
57
+ spatial_attention_timestep_skip_range (`tuple[int, int]`, defaults to `(100, 800)`):
58
+ The range of timesteps to skip in the spatial attention layer. The attention computations will be
59
+ conditionally skipped if the current timestep is within the specified range.
60
+ temporal_attention_timestep_skip_range (`tuple[int, int]`, defaults to `(100, 800)`):
61
+ The range of timesteps to skip in the temporal attention layer. The attention computations will be
62
+ conditionally skipped if the current timestep is within the specified range.
63
+ cross_attention_timestep_skip_range (`tuple[int, int]`, defaults to `(100, 800)`):
64
+ The range of timesteps to skip in the cross-attention layer. The attention computations will be
65
+ conditionally skipped if the current timestep is within the specified range.
66
+ spatial_attention_block_identifiers (`tuple[str, ...]`):
67
+ The identifiers to match against the layer names to determine if the layer is a spatial attention layer.
68
+ temporal_attention_block_identifiers (`tuple[str, ...]`):
69
+ The identifiers to match against the layer names to determine if the layer is a temporal attention layer.
70
+ cross_attention_block_identifiers (`tuple[str, ...]`):
71
+ The identifiers to match against the layer names to determine if the layer is a cross-attention layer.
72
+ """
73
+
74
+ spatial_attention_block_skip_range: int | None = None
75
+ temporal_attention_block_skip_range: int | None = None
76
+ cross_attention_block_skip_range: int | None = None
77
+
78
+ spatial_attention_timestep_skip_range: tuple[int, int] = (100, 800)
79
+ temporal_attention_timestep_skip_range: tuple[int, int] = (100, 800)
80
+ cross_attention_timestep_skip_range: tuple[int, int] = (100, 800)
81
+
82
+ spatial_attention_block_identifiers: tuple[str, ...] = _SPATIAL_TRANSFORMER_BLOCK_IDENTIFIERS
83
+ temporal_attention_block_identifiers: tuple[str, ...] = _TEMPORAL_TRANSFORMER_BLOCK_IDENTIFIERS
84
+ cross_attention_block_identifiers: tuple[str, ...] = _CROSS_TRANSFORMER_BLOCK_IDENTIFIERS
85
+
86
+ current_timestep_callback: Callable[[], int] = None
87
+
88
+ # TODO(aryan): add PAB for MLP layers (very limited speedup from testing with original codebase
89
+ # so not added for now)
90
+
91
+ def __repr__(self) -> str:
92
+ return (
93
+ f"PyramidAttentionBroadcastConfig(\n"
94
+ f" spatial_attention_block_skip_range={self.spatial_attention_block_skip_range},\n"
95
+ f" temporal_attention_block_skip_range={self.temporal_attention_block_skip_range},\n"
96
+ f" cross_attention_block_skip_range={self.cross_attention_block_skip_range},\n"
97
+ f" spatial_attention_timestep_skip_range={self.spatial_attention_timestep_skip_range},\n"
98
+ f" temporal_attention_timestep_skip_range={self.temporal_attention_timestep_skip_range},\n"
99
+ f" cross_attention_timestep_skip_range={self.cross_attention_timestep_skip_range},\n"
100
+ f" spatial_attention_block_identifiers={self.spatial_attention_block_identifiers},\n"
101
+ f" temporal_attention_block_identifiers={self.temporal_attention_block_identifiers},\n"
102
+ f" cross_attention_block_identifiers={self.cross_attention_block_identifiers},\n"
103
+ f" current_timestep_callback={self.current_timestep_callback}\n"
104
+ ")"
105
+ )
106
+
107
+
108
+ class PyramidAttentionBroadcastState:
109
+ r"""
110
+ State for Pyramid Attention Broadcast.
111
+
112
+ Attributes:
113
+ iteration (`int`):
114
+ The current iteration of the Pyramid Attention Broadcast. It is necessary to ensure that `reset_state` is
115
+ called before starting a new inference forward pass for PAB to work correctly.
116
+ cache (`Any`):
117
+ The cached output from the previous forward pass. This is used to re-use the attention states when the
118
+ attention computation is skipped. It is either a tensor or a tuple of tensors, depending on the module.
119
+ """
120
+
121
+ def __init__(self) -> None:
122
+ self.iteration = 0
123
+ self.cache = None
124
+
125
+ def reset(self):
126
+ self.iteration = 0
127
+ self.cache = None
128
+
129
+ def __repr__(self):
130
+ cache_repr = ""
131
+ if self.cache is None:
132
+ cache_repr = "None"
133
+ else:
134
+ cache_repr = f"Tensor(shape={self.cache.shape}, dtype={self.cache.dtype})"
135
+ return f"PyramidAttentionBroadcastState(iteration={self.iteration}, cache={cache_repr})"
136
+
137
+
138
+ class PyramidAttentionBroadcastHook(ModelHook):
139
+ r"""A hook that applies Pyramid Attention Broadcast to a given module."""
140
+
141
+ _is_stateful = True
142
+
143
+ def __init__(
144
+ self, timestep_skip_range: tuple[int, int], block_skip_range: int, current_timestep_callback: Callable[[], int]
145
+ ) -> None:
146
+ super().__init__()
147
+
148
+ self.timestep_skip_range = timestep_skip_range
149
+ self.block_skip_range = block_skip_range
150
+ self.current_timestep_callback = current_timestep_callback
151
+
152
+ def initialize_hook(self, module):
153
+ self.state = PyramidAttentionBroadcastState()
154
+ return module
155
+
156
+ def new_forward(self, module: torch.nn.Module, *args, **kwargs) -> Any:
157
+ is_within_timestep_range = (
158
+ self.timestep_skip_range[0] < self.current_timestep_callback() < self.timestep_skip_range[1]
159
+ )
160
+ should_compute_attention = (
161
+ self.state.cache is None
162
+ or self.state.iteration == 0
163
+ or not is_within_timestep_range
164
+ or self.state.iteration % self.block_skip_range == 0
165
+ )
166
+
167
+ if should_compute_attention:
168
+ output = self.fn_ref.original_forward(*args, **kwargs)
169
+ else:
170
+ output = self.state.cache
171
+
172
+ self.state.cache = output
173
+ self.state.iteration += 1
174
+ return output
175
+
176
+ def reset_state(self, module: torch.nn.Module) -> None:
177
+ self.state.reset()
178
+ return module
179
+
180
+
181
+ def apply_pyramid_attention_broadcast(module: torch.nn.Module, config: PyramidAttentionBroadcastConfig):
182
+ r"""
183
+ Apply [Pyramid Attention Broadcast](https://huggingface.co/papers/2408.12588) to a given pipeline.
184
+
185
+ PAB is an attention approximation method that leverages the similarity in attention states between timesteps to
186
+ reduce the computational cost of attention computation. The key takeaway from the paper is that the attention
187
+ similarity in the cross-attention layers between timesteps is high, followed by less similarity in the temporal and
188
+ spatial layers. This allows for the skipping of attention computation in the cross-attention layers more frequently
189
+ than in the temporal and spatial layers. Applying PAB will, therefore, speedup the inference process.
190
+
191
+ Args:
192
+ module (`torch.nn.Module`):
193
+ The module to apply Pyramid Attention Broadcast to.
194
+ config (`PyramidAttentionBroadcastConfig | None`, `optional`, defaults to `None`):
195
+ The configuration to use for Pyramid Attention Broadcast.
196
+
197
+ Example:
198
+
199
+ ```python
200
+ >>> import torch
201
+ >>> from diffusers import CogVideoXPipeline, PyramidAttentionBroadcastConfig, apply_pyramid_attention_broadcast
202
+ >>> from diffusers.utils import export_to_video
203
+
204
+ >>> pipe = CogVideoXPipeline.from_pretrained("THUDM/CogVideoX-5b", torch_dtype=torch.bfloat16)
205
+ >>> pipe.to("cuda")
206
+
207
+ >>> config = PyramidAttentionBroadcastConfig(
208
+ ... spatial_attention_block_skip_range=2,
209
+ ... spatial_attention_timestep_skip_range=(100, 800),
210
+ ... current_timestep_callback=lambda: pipe.current_timestep,
211
+ ... )
212
+ >>> apply_pyramid_attention_broadcast(pipe.transformer, config)
213
+ ```
214
+ """
215
+ if config.current_timestep_callback is None:
216
+ raise ValueError(
217
+ "The `current_timestep_callback` function must be provided in the configuration to apply Pyramid Attention Broadcast."
218
+ )
219
+
220
+ if (
221
+ config.spatial_attention_block_skip_range is None
222
+ and config.temporal_attention_block_skip_range is None
223
+ and config.cross_attention_block_skip_range is None
224
+ ):
225
+ logger.warning(
226
+ "Pyramid Attention Broadcast requires one or more of `spatial_attention_block_skip_range`, `temporal_attention_block_skip_range` "
227
+ "or `cross_attention_block_skip_range` parameters to be set to an integer, not `None`. Defaulting to using `spatial_attention_block_skip_range=2`. "
228
+ "To avoid this warning, please set one of the above parameters."
229
+ )
230
+ config.spatial_attention_block_skip_range = 2
231
+
232
+ for name, submodule in module.named_modules():
233
+ if not isinstance(submodule, (*_ATTENTION_CLASSES, AttentionModuleMixin)):
234
+ # PAB has been implemented specific to Diffusers' Attention classes. However, this does not mean that PAB
235
+ # cannot be applied to this layer. For custom layers, users can extend this functionality and implement
236
+ # their own PAB logic similar to `_apply_pyramid_attention_broadcast_on_attention_class`.
237
+ continue
238
+ _apply_pyramid_attention_broadcast_on_attention_class(name, submodule, config)
239
+
240
+
241
+ def _apply_pyramid_attention_broadcast_on_attention_class(
242
+ name: str, module: Attention, config: PyramidAttentionBroadcastConfig
243
+ ) -> bool:
244
+ is_spatial_self_attention = (
245
+ any(re.search(identifier, name) is not None for identifier in config.spatial_attention_block_identifiers)
246
+ and config.spatial_attention_block_skip_range is not None
247
+ and not getattr(module, "is_cross_attention", False)
248
+ )
249
+ is_temporal_self_attention = (
250
+ any(re.search(identifier, name) is not None for identifier in config.temporal_attention_block_identifiers)
251
+ and config.temporal_attention_block_skip_range is not None
252
+ and not getattr(module, "is_cross_attention", False)
253
+ )
254
+ is_cross_attention = (
255
+ any(re.search(identifier, name) is not None for identifier in config.cross_attention_block_identifiers)
256
+ and config.cross_attention_block_skip_range is not None
257
+ and getattr(module, "is_cross_attention", False)
258
+ )
259
+
260
+ block_skip_range, timestep_skip_range, block_type = None, None, None
261
+ if is_spatial_self_attention:
262
+ block_skip_range = config.spatial_attention_block_skip_range
263
+ timestep_skip_range = config.spatial_attention_timestep_skip_range
264
+ block_type = "spatial"
265
+ elif is_temporal_self_attention:
266
+ block_skip_range = config.temporal_attention_block_skip_range
267
+ timestep_skip_range = config.temporal_attention_timestep_skip_range
268
+ block_type = "temporal"
269
+ elif is_cross_attention:
270
+ block_skip_range = config.cross_attention_block_skip_range
271
+ timestep_skip_range = config.cross_attention_timestep_skip_range
272
+ block_type = "cross"
273
+
274
+ if block_skip_range is None or timestep_skip_range is None:
275
+ logger.info(
276
+ f'Unable to apply Pyramid Attention Broadcast to the selected layer: "{name}" because it does '
277
+ f"not match any of the required criteria for spatial, temporal or cross attention layers. Note, "
278
+ f"however, that this layer may still be valid for applying PAB. Please specify the correct "
279
+ f"block identifiers in the configuration."
280
+ )
281
+ return False
282
+
283
+ logger.debug(f"Enabling Pyramid Attention Broadcast ({block_type}) in layer: {name}")
284
+ _apply_pyramid_attention_broadcast_hook(
285
+ module, timestep_skip_range, block_skip_range, config.current_timestep_callback
286
+ )
287
+ return True
288
+
289
+
290
+ def _apply_pyramid_attention_broadcast_hook(
291
+ module: Attention | MochiAttention,
292
+ timestep_skip_range: tuple[int, int],
293
+ block_skip_range: int,
294
+ current_timestep_callback: Callable[[], int],
295
+ ):
296
+ r"""
297
+ Apply [Pyramid Attention Broadcast](https://huggingface.co/papers/2408.12588) to a given torch.nn.Module.
298
+
299
+ Args:
300
+ module (`torch.nn.Module`):
301
+ The module to apply Pyramid Attention Broadcast to.
302
+ timestep_skip_range (`tuple[int, int]`):
303
+ The range of timesteps to skip in the attention layer. The attention computations will be conditionally
304
+ skipped if the current timestep is within the specified range.
305
+ block_skip_range (`int`):
306
+ The number of times a specific attention broadcast is skipped before computing the attention states to
307
+ re-use. If this is set to the value `N`, the attention computation will be skipped `N - 1` times (i.e., old
308
+ attention states will be reused) before computing the new attention states again.
309
+ current_timestep_callback (`Callable[[], int]`):
310
+ A callback function that returns the current inference timestep.
311
+ """
312
+ registry = HookRegistry.check_if_exists_or_initialize(module)
313
+ hook = PyramidAttentionBroadcastHook(timestep_skip_range, block_skip_range, current_timestep_callback)
314
+ registry.register_hook(hook, _PYRAMID_ATTENTION_BROADCAST_HOOK)
third_party/diffusers/src/diffusers/hooks/smoothed_energy_guidance_utils.py ADDED
@@ -0,0 +1,166 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import math
16
+ from dataclasses import asdict, dataclass
17
+
18
+ import torch
19
+ import torch.nn.functional as F
20
+
21
+ from ..utils import get_logger
22
+ from ._common import _ALL_TRANSFORMER_BLOCK_IDENTIFIERS, _ATTENTION_CLASSES, _get_submodule_from_fqn
23
+ from .hooks import HookRegistry, ModelHook
24
+
25
+
26
+ logger = get_logger(__name__) # pylint: disable=invalid-name
27
+
28
+ _SMOOTHED_ENERGY_GUIDANCE_HOOK = "smoothed_energy_guidance_hook"
29
+
30
+
31
+ @dataclass
32
+ class SmoothedEnergyGuidanceConfig:
33
+ r"""
34
+ Configuration for skipping internal transformer blocks when executing a transformer model.
35
+
36
+ Args:
37
+ indices (`list[int]`):
38
+ The indices of the layer to skip. This is typically the first layer in the transformer block.
39
+ fqn (`str`, defaults to `"auto"`):
40
+ The fully qualified name identifying the stack of transformer blocks. Typically, this is
41
+ `transformer_blocks`, `single_transformer_blocks`, `blocks`, `layers`, or `temporal_transformer_blocks`.
42
+ For automatic detection, set this to `"auto"`. "auto" only works on DiT models. For UNet models, you must
43
+ provide the correct fqn.
44
+ _query_proj_identifiers (`list[str]`, defaults to `None`):
45
+ The identifiers for the query projection layers. Typically, these are `to_q`, `query`, or `q_proj`. If
46
+ `None`, `to_q` is used by default.
47
+ """
48
+
49
+ indices: list[int]
50
+ fqn: str = "auto"
51
+ _query_proj_identifiers: list[str] = None
52
+
53
+ def to_dict(self):
54
+ return asdict(self)
55
+
56
+ @staticmethod
57
+ def from_dict(data: dict) -> "SmoothedEnergyGuidanceConfig":
58
+ return SmoothedEnergyGuidanceConfig(**data)
59
+
60
+
61
+ class SmoothedEnergyGuidanceHook(ModelHook):
62
+ def __init__(self, blur_sigma: float = 1.0, blur_threshold_inf: float = 9999.9) -> None:
63
+ super().__init__()
64
+ self.blur_sigma = blur_sigma
65
+ self.blur_threshold_inf = blur_threshold_inf
66
+
67
+ def post_forward(self, module: torch.nn.Module, output: torch.Tensor) -> torch.Tensor:
68
+ # Copied from https://github.com/SusungHong/SEG-SDXL/blob/cf8256d640d5373541cfea3b3b6caf93272cf986/pipeline_seg.py#L172C31-L172C102
69
+ kernel_size = math.ceil(6 * self.blur_sigma) + 1 - math.ceil(6 * self.blur_sigma) % 2
70
+ smoothed_output = _gaussian_blur_2d(output, kernel_size, self.blur_sigma, self.blur_threshold_inf)
71
+ return smoothed_output
72
+
73
+
74
+ def _apply_smoothed_energy_guidance_hook(
75
+ module: torch.nn.Module, config: SmoothedEnergyGuidanceConfig, blur_sigma: float, name: str | None = None
76
+ ) -> None:
77
+ name = name or _SMOOTHED_ENERGY_GUIDANCE_HOOK
78
+
79
+ if config.fqn == "auto":
80
+ for identifier in _ALL_TRANSFORMER_BLOCK_IDENTIFIERS:
81
+ if hasattr(module, identifier):
82
+ config.fqn = identifier
83
+ break
84
+ else:
85
+ raise ValueError(
86
+ "Could not find a suitable identifier for the transformer blocks automatically. Please provide a valid "
87
+ "`fqn` (fully qualified name) that identifies a stack of transformer blocks."
88
+ )
89
+
90
+ if config._query_proj_identifiers is None:
91
+ config._query_proj_identifiers = ["to_q"]
92
+
93
+ transformer_blocks = _get_submodule_from_fqn(module, config.fqn)
94
+ blocks_found = False
95
+ for i, block in enumerate(transformer_blocks):
96
+ if i not in config.indices:
97
+ continue
98
+
99
+ blocks_found = True
100
+
101
+ for submodule_name, submodule in block.named_modules():
102
+ if not isinstance(submodule, _ATTENTION_CLASSES) or submodule.is_cross_attention:
103
+ continue
104
+ for identifier in config._query_proj_identifiers:
105
+ query_proj = getattr(submodule, identifier, None)
106
+ if query_proj is None or not isinstance(query_proj, torch.nn.Linear):
107
+ continue
108
+ logger.debug(
109
+ f"Registering smoothed energy guidance hook on {config.fqn}.{i}.{submodule_name}.{identifier}"
110
+ )
111
+ registry = HookRegistry.check_if_exists_or_initialize(query_proj)
112
+ hook = SmoothedEnergyGuidanceHook(blur_sigma)
113
+ registry.register_hook(hook, name)
114
+
115
+ if not blocks_found:
116
+ raise ValueError(
117
+ f"Could not find any transformer blocks matching the provided indices {config.indices} and "
118
+ f"fully qualified name '{config.fqn}'. Please check the indices and fqn for correctness."
119
+ )
120
+
121
+
122
+ # Modified from https://github.com/SusungHong/SEG-SDXL/blob/cf8256d640d5373541cfea3b3b6caf93272cf986/pipeline_seg.py#L71
123
+ def _gaussian_blur_2d(query: torch.Tensor, kernel_size: int, sigma: float, sigma_threshold_inf: float) -> torch.Tensor:
124
+ """
125
+ This implementation assumes that the input query is for visual (image/videos) tokens to apply the 2D gaussian blur.
126
+ However, some models use joint text-visual token attention for which this may not be suitable. Additionally, this
127
+ implementation also assumes that the visual tokens come from a square image/video. In practice, despite these
128
+ assumptions, applying the 2D square gaussian blur on the query projections generates reasonable results for
129
+ Smoothed Energy Guidance.
130
+
131
+ SEG is only supported as an experimental prototype feature for now, so the implementation may be modified in the
132
+ future without warning or guarantee of reproducibility.
133
+ """
134
+ assert query.ndim == 3
135
+
136
+ is_inf = sigma > sigma_threshold_inf
137
+ batch_size, seq_len, embed_dim = query.shape
138
+
139
+ seq_len_sqrt = int(math.sqrt(seq_len))
140
+ num_square_tokens = seq_len_sqrt * seq_len_sqrt
141
+ query_slice = query[:, :num_square_tokens, :]
142
+ query_slice = query_slice.permute(0, 2, 1)
143
+ query_slice = query_slice.reshape(batch_size, embed_dim, seq_len_sqrt, seq_len_sqrt)
144
+
145
+ if is_inf:
146
+ kernel_size = min(kernel_size, seq_len_sqrt - (seq_len_sqrt % 2 - 1))
147
+ kernel_size_half = (kernel_size - 1) / 2
148
+
149
+ x = torch.linspace(-kernel_size_half, kernel_size_half, steps=kernel_size)
150
+ pdf = torch.exp(-0.5 * (x / sigma).pow(2))
151
+ kernel1d = pdf / pdf.sum()
152
+ kernel1d = kernel1d.to(query)
153
+ kernel2d = torch.matmul(kernel1d[:, None], kernel1d[None, :])
154
+ kernel2d = kernel2d.expand(embed_dim, 1, kernel2d.shape[0], kernel2d.shape[1])
155
+
156
+ padding = [kernel_size // 2, kernel_size // 2, kernel_size // 2, kernel_size // 2]
157
+ query_slice = F.pad(query_slice, padding, mode="reflect")
158
+ query_slice = F.conv2d(query_slice, kernel2d, groups=embed_dim)
159
+ else:
160
+ query_slice[:] = query_slice.mean(dim=(-2, -1), keepdim=True)
161
+
162
+ query_slice = query_slice.reshape(batch_size, embed_dim, num_square_tokens)
163
+ query_slice = query_slice.permute(0, 2, 1)
164
+ query[:, :num_square_tokens, :] = query_slice.clone()
165
+
166
+ return query
third_party/diffusers/src/diffusers/hooks/taylorseer_cache.py ADDED
@@ -0,0 +1,345 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import re
3
+ from dataclasses import dataclass
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+
8
+ from ..utils import logging
9
+ from .hooks import HookRegistry, ModelHook, StateManager
10
+
11
+
12
+ logger = logging.get_logger(__name__)
13
+ _TAYLORSEER_CACHE_HOOK = "taylorseer_cache"
14
+ _SPATIAL_ATTENTION_BLOCK_IDENTIFIERS = (
15
+ "^blocks.*attn",
16
+ "^transformer_blocks.*attn",
17
+ "^single_transformer_blocks.*attn",
18
+ )
19
+ _TEMPORAL_ATTENTION_BLOCK_IDENTIFIERS = ("^temporal_transformer_blocks.*attn",)
20
+ _TRANSFORMER_BLOCK_IDENTIFIERS = _SPATIAL_ATTENTION_BLOCK_IDENTIFIERS + _TEMPORAL_ATTENTION_BLOCK_IDENTIFIERS
21
+ _BLOCK_IDENTIFIERS = ("^[^.]*block[^.]*\\.[^.]+$",)
22
+ _PROJ_OUT_IDENTIFIERS = ("^proj_out$",)
23
+
24
+
25
+ @dataclass
26
+ class TaylorSeerCacheConfig:
27
+ """
28
+ Configuration for TaylorSeer cache. See: https://huggingface.co/papers/2503.06923
29
+
30
+ Attributes:
31
+ cache_interval (`int`, defaults to `5`):
32
+ The interval between full computation steps. After a full computation, the cached (predicted) outputs are
33
+ reused for this many subsequent denoising steps before refreshing with a new full forward pass.
34
+
35
+ disable_cache_before_step (`int`, defaults to `3`):
36
+ The denoising step index before which caching is disabled, meaning full computation is performed for the
37
+ initial steps (0 to disable_cache_before_step - 1) to gather data for Taylor series approximations. During
38
+ these steps, Taylor factors are updated, but caching/predictions are not applied. Caching begins at this
39
+ step.
40
+
41
+ disable_cache_after_step (`int`, *optional*, defaults to `None`):
42
+ The denoising step index after which caching is disabled. If set, for steps >= this value, all modules run
43
+ full computations without predictions or state updates, ensuring accuracy in later stages if needed.
44
+
45
+ max_order (`int`, defaults to `1`):
46
+ The highest order in the Taylor series expansion for approximating module outputs. Higher orders provide
47
+ better approximations but increase computation and memory usage.
48
+
49
+ taylor_factors_dtype (`torch.dtype`, defaults to `torch.bfloat16`):
50
+ Data type used for storing and computing Taylor series factors. Lower precision reduces memory but may
51
+ affect stability; higher precision improves accuracy at the cost of more memory.
52
+
53
+ skip_predict_identifiers (`list[str]`, *optional*, defaults to `None`):
54
+ Regex patterns (using `re.fullmatch`) for module names to place as "skip" in "cache" mode. In this mode,
55
+ the module computes fully during initial or refresh steps but returns a zero tensor (matching recorded
56
+ shape) during prediction steps to skip computation cheaply.
57
+
58
+ cache_identifiers (`list[str]`, *optional*, defaults to `None`):
59
+ Regex patterns (using `re.fullmatch`) for module names to place in Taylor-series caching mode, where
60
+ outputs are approximated and cached for reuse.
61
+
62
+ use_lite_mode (`bool`, *optional*, defaults to `False`):
63
+ Enables a lightweight TaylorSeer variant that minimizes memory usage by applying predefined patterns for
64
+ skipping and caching (e.g., skipping blocks and caching projections). This overrides any custom
65
+ `inactive_identifiers` or `active_identifiers`.
66
+
67
+ Notes:
68
+ - Patterns are matched using `re.fullmatch` on the module name.
69
+ - If `skip_predict_identifiers` or `cache_identifiers` are provided, only matching modules are hooked.
70
+ - If neither is provided, all attention-like modules are hooked by default.
71
+
72
+ Example of inactive and active usage:
73
+
74
+ ```py
75
+ def forward(x):
76
+ x = self.module1(x) # inactive module: returns zeros tensor based on shape recorded during full compute
77
+ x = self.module2(x) # active module: caches output here, avoiding recomputation of prior steps
78
+ return x
79
+ ```
80
+ """
81
+
82
+ cache_interval: int = 5
83
+ disable_cache_before_step: int = 3
84
+ disable_cache_after_step: int | None = None
85
+ max_order: int = 1
86
+ taylor_factors_dtype: torch.dtype | None = torch.bfloat16
87
+ skip_predict_identifiers: list[str] | None = None
88
+ cache_identifiers: list[str] | None = None
89
+ use_lite_mode: bool = False
90
+
91
+ def __repr__(self) -> str:
92
+ return (
93
+ "TaylorSeerCacheConfig("
94
+ f"cache_interval={self.cache_interval}, "
95
+ f"disable_cache_before_step={self.disable_cache_before_step}, "
96
+ f"disable_cache_after_step={self.disable_cache_after_step}, "
97
+ f"max_order={self.max_order}, "
98
+ f"taylor_factors_dtype={self.taylor_factors_dtype}, "
99
+ f"skip_predict_identifiers={self.skip_predict_identifiers}, "
100
+ f"cache_identifiers={self.cache_identifiers}, "
101
+ f"use_lite_mode={self.use_lite_mode})"
102
+ )
103
+
104
+
105
+ class TaylorSeerState:
106
+ def __init__(
107
+ self,
108
+ taylor_factors_dtype: torch.dtype | None = torch.bfloat16,
109
+ max_order: int = 1,
110
+ is_inactive: bool = False,
111
+ ):
112
+ self.taylor_factors_dtype = taylor_factors_dtype
113
+ self.max_order = max_order
114
+ self.is_inactive = is_inactive
115
+
116
+ self.module_dtypes: tuple[torch.dtype, ...] = ()
117
+ self.last_update_step: int | None = None
118
+ self.taylor_factors: dict[int, dict[int, torch.Tensor]] = {}
119
+ self.inactive_shapes: tuple[tuple[int, ...], ...] | None = None
120
+ self.device: torch.device | None = None
121
+ self.current_step: int = -1
122
+
123
+ def reset(self) -> None:
124
+ self.current_step = -1
125
+ self.last_update_step = None
126
+ self.taylor_factors = {}
127
+ self.inactive_shapes = None
128
+ self.device = None
129
+
130
+ def update(
131
+ self,
132
+ outputs: tuple[torch.Tensor, ...],
133
+ ) -> None:
134
+ self.module_dtypes = tuple(output.dtype for output in outputs)
135
+ self.device = outputs[0].device
136
+
137
+ if self.is_inactive:
138
+ self.inactive_shapes = tuple(output.shape for output in outputs)
139
+ else:
140
+ for i, features in enumerate(outputs):
141
+ new_factors: dict[int, torch.Tensor] = {0: features}
142
+ is_first_update = self.last_update_step is None
143
+ if not is_first_update:
144
+ delta_step = self.current_step - self.last_update_step
145
+ if delta_step == 0:
146
+ raise ValueError("Delta step cannot be zero for TaylorSeer update.")
147
+
148
+ # Recursive divided differences up to max_order
149
+ prev_factors = self.taylor_factors.get(i, {})
150
+ for j in range(self.max_order):
151
+ prev = prev_factors.get(j)
152
+ if prev is None:
153
+ break
154
+ new_factors[j + 1] = (new_factors[j] - prev.to(features.dtype)) / delta_step
155
+ self.taylor_factors[i] = {
156
+ order: factor.to(self.taylor_factors_dtype) for order, factor in new_factors.items()
157
+ }
158
+
159
+ self.last_update_step = self.current_step
160
+
161
+ @torch.compiler.disable
162
+ def predict(self) -> list[torch.Tensor]:
163
+ if self.last_update_step is None:
164
+ raise ValueError("Cannot predict without prior initialization/update.")
165
+
166
+ step_offset = self.current_step - self.last_update_step
167
+
168
+ outputs = []
169
+ if self.is_inactive:
170
+ if self.inactive_shapes is None:
171
+ raise ValueError("Inactive shapes not set during prediction.")
172
+ for i in range(len(self.module_dtypes)):
173
+ outputs.append(
174
+ torch.zeros(
175
+ self.inactive_shapes[i],
176
+ dtype=self.module_dtypes[i],
177
+ device=self.device,
178
+ )
179
+ )
180
+ else:
181
+ if not self.taylor_factors:
182
+ raise ValueError("Taylor factors empty during prediction.")
183
+ num_outputs = len(self.taylor_factors)
184
+ num_orders = len(self.taylor_factors[0])
185
+ for i in range(num_outputs):
186
+ output_dtype = self.module_dtypes[i]
187
+ taylor_factors = self.taylor_factors[i]
188
+ output = torch.zeros_like(taylor_factors[0], dtype=output_dtype)
189
+ for order in range(num_orders):
190
+ coeff = (step_offset**order) / math.factorial(order)
191
+ factor = taylor_factors[order]
192
+ output = output + factor.to(output_dtype) * coeff
193
+ outputs.append(output)
194
+ return outputs
195
+
196
+
197
+ class TaylorSeerCacheHook(ModelHook):
198
+ _is_stateful = True
199
+
200
+ def __init__(
201
+ self,
202
+ cache_interval: int,
203
+ disable_cache_before_step: int,
204
+ taylor_factors_dtype: torch.dtype,
205
+ state_manager: StateManager,
206
+ disable_cache_after_step: int | None = None,
207
+ ):
208
+ super().__init__()
209
+ self.cache_interval = cache_interval
210
+ self.disable_cache_before_step = disable_cache_before_step
211
+ self.disable_cache_after_step = disable_cache_after_step
212
+ self.taylor_factors_dtype = taylor_factors_dtype
213
+ self.state_manager = state_manager
214
+
215
+ def initialize_hook(self, module: torch.nn.Module):
216
+ return module
217
+
218
+ def reset_state(self, module: torch.nn.Module) -> None:
219
+ """
220
+ Reset state between sampling runs.
221
+ """
222
+ self.state_manager.reset()
223
+
224
+ @torch.compiler.disable
225
+ def _measure_should_compute(self) -> bool:
226
+ state: TaylorSeerState = self.state_manager.get_state()
227
+ state.current_step += 1
228
+ current_step = state.current_step
229
+ is_warmup_phase = current_step < self.disable_cache_before_step
230
+ is_compute_interval = (current_step - self.disable_cache_before_step - 1) % self.cache_interval == 0
231
+ is_cooldown_phase = self.disable_cache_after_step is not None and current_step >= self.disable_cache_after_step
232
+ should_compute = is_warmup_phase or is_compute_interval or is_cooldown_phase
233
+ return should_compute, state
234
+
235
+ def new_forward(self, module: torch.nn.Module, *args, **kwargs):
236
+ should_compute, state = self._measure_should_compute()
237
+ if should_compute:
238
+ outputs = self.fn_ref.original_forward(*args, **kwargs)
239
+ wrapped_outputs = (outputs,) if isinstance(outputs, torch.Tensor) else outputs
240
+ state.update(wrapped_outputs)
241
+ return outputs
242
+
243
+ outputs_list = state.predict()
244
+ return outputs_list[0] if len(outputs_list) == 1 else tuple(outputs_list)
245
+
246
+
247
+ def _resolve_patterns(config: TaylorSeerCacheConfig) -> tuple[list[str], list[str]]:
248
+ """
249
+ Resolve effective inactive and active pattern lists from config + templates.
250
+ """
251
+
252
+ inactive_patterns = config.skip_predict_identifiers if config.skip_predict_identifiers is not None else None
253
+ active_patterns = config.cache_identifiers if config.cache_identifiers is not None else None
254
+
255
+ return inactive_patterns or [], active_patterns or []
256
+
257
+
258
+ def apply_taylorseer_cache(module: torch.nn.Module, config: TaylorSeerCacheConfig):
259
+ """
260
+ Applies the TaylorSeer cache to a given pipeline (typically the transformer / UNet).
261
+
262
+ This function hooks selected modules in the model to enable caching or skipping based on the provided
263
+ configuration, reducing redundant computations in diffusion denoising loops.
264
+
265
+ Args:
266
+ module (torch.nn.Module): The model subtree to apply the hooks to.
267
+ config (TaylorSeerCacheConfig): Configuration for the cache.
268
+
269
+ Example:
270
+ ```python
271
+ >>> import torch
272
+ >>> from diffusers import FluxPipeline, TaylorSeerCacheConfig
273
+
274
+ >>> pipe = FluxPipeline.from_pretrained(
275
+ ... "black-forest-labs/FLUX.1-dev",
276
+ ... torch_dtype=torch.bfloat16,
277
+ ... )
278
+ >>> pipe.to("cuda")
279
+
280
+ >>> config = TaylorSeerCacheConfig(
281
+ ... cache_interval=5,
282
+ ... max_order=1,
283
+ ... disable_cache_before_step=3,
284
+ ... taylor_factors_dtype=torch.float32,
285
+ ... )
286
+ >>> pipe.transformer.enable_cache(config)
287
+ ```
288
+ """
289
+ inactive_patterns, active_patterns = _resolve_patterns(config)
290
+
291
+ active_patterns = active_patterns or _TRANSFORMER_BLOCK_IDENTIFIERS
292
+
293
+ if config.use_lite_mode:
294
+ logger.info("Using TaylorSeer Lite variant for cache.")
295
+ active_patterns = _PROJ_OUT_IDENTIFIERS
296
+ inactive_patterns = _BLOCK_IDENTIFIERS
297
+ if config.skip_predict_identifiers or config.cache_identifiers:
298
+ logger.warning("Lite mode overrides user patterns.")
299
+
300
+ for name, submodule in module.named_modules():
301
+ matches_inactive = any(re.fullmatch(pattern, name) for pattern in inactive_patterns)
302
+ matches_active = any(re.fullmatch(pattern, name) for pattern in active_patterns)
303
+ if not (matches_inactive or matches_active):
304
+ continue
305
+ _apply_taylorseer_cache_hook(
306
+ module=submodule,
307
+ config=config,
308
+ is_inactive=matches_inactive,
309
+ )
310
+
311
+
312
+ def _apply_taylorseer_cache_hook(
313
+ module: nn.Module,
314
+ config: TaylorSeerCacheConfig,
315
+ is_inactive: bool,
316
+ ):
317
+ """
318
+ Registers the TaylorSeer hook on the specified nn.Module.
319
+
320
+ Args:
321
+ name: Name of the module.
322
+ module: The nn.Module to be hooked.
323
+ config: Cache configuration.
324
+ is_inactive: Whether this module should operate in "inactive" mode.
325
+ """
326
+ state_manager = StateManager(
327
+ TaylorSeerState,
328
+ init_kwargs={
329
+ "taylor_factors_dtype": config.taylor_factors_dtype,
330
+ "max_order": config.max_order,
331
+ "is_inactive": is_inactive,
332
+ },
333
+ )
334
+
335
+ registry = HookRegistry.check_if_exists_or_initialize(module)
336
+
337
+ hook = TaylorSeerCacheHook(
338
+ cache_interval=config.cache_interval,
339
+ disable_cache_before_step=config.disable_cache_before_step,
340
+ taylor_factors_dtype=config.taylor_factors_dtype,
341
+ disable_cache_after_step=config.disable_cache_after_step,
342
+ state_manager=state_manager,
343
+ )
344
+
345
+ registry.register_hook(hook, _TAYLORSEER_CACHE_HOOK)
third_party/diffusers/src/diffusers/hooks/text_kv_cache.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from dataclasses import dataclass
16
+
17
+ import torch
18
+
19
+ from .hooks import BaseState, HookRegistry, ModelHook, StateManager
20
+
21
+
22
+ _TEXT_KV_CACHE_TRANSFORMER_HOOK = "text_kv_cache_transformer"
23
+ _TEXT_KV_CACHE_BLOCK_HOOK = "text_kv_cache_block"
24
+
25
+
26
+ @dataclass
27
+ class TextKVCacheConfig:
28
+ """Enable exact (lossless) text K/V caching for transformer models.
29
+
30
+ Pre-computes per-block text key and value projections once before the denoising loop and reuses them across all
31
+ steps. Positive and negative prompts are distinguished via a stable cache key captured by a transformer-level hook
32
+ before any intermediate tensor allocations.
33
+ """
34
+
35
+ pass
36
+
37
+
38
+ class TextKVCacheState(BaseState):
39
+ """Shared state between the transformer-level and block-level hooks.
40
+
41
+ The transformer hook writes the stable ``encoder_hidden_states`` ``data_ptr()`` (captured *before* ``txt_norm``) so
42
+ that block hooks can use it as a reliable cache key across denoising steps.
43
+ """
44
+
45
+ def __init__(self):
46
+ self.key: int | None = None
47
+
48
+ def reset(self):
49
+ self.key = None
50
+
51
+
52
+ class TextKVCacheBlockState(BaseState):
53
+ """Per-block state holding cached text key/value projections."""
54
+
55
+ def __init__(self):
56
+ self.kv_cache: dict[int, tuple[torch.Tensor, torch.Tensor]] = {}
57
+
58
+ def reset(self):
59
+ self.kv_cache.clear()
60
+
61
+
62
+ class TextKVCacheTransformerHook(ModelHook):
63
+ """Captures ``encoder_hidden_states.data_ptr()`` before ``txt_norm``
64
+ and writes it to shared state for the block hooks to read."""
65
+
66
+ _is_stateful = True
67
+
68
+ def __init__(self, state_manager: StateManager):
69
+ super().__init__()
70
+ self.state_manager = state_manager
71
+
72
+ def new_forward(self, module: torch.nn.Module, *args, **kwargs):
73
+ if self.state_manager._current_context is None:
74
+ self.state_manager.set_context("inference")
75
+
76
+ encoder_hidden_states = kwargs.get("encoder_hidden_states")
77
+ if encoder_hidden_states is not None:
78
+ state: TextKVCacheState = self.state_manager.get_state()
79
+ state.key = encoder_hidden_states.data_ptr()
80
+ return self.fn_ref.original_forward(*args, **kwargs)
81
+
82
+ def reset_state(self, module: torch.nn.Module):
83
+ self.state_manager.reset()
84
+ return module
85
+
86
+
87
+ class TextKVCacheBlockHook(ModelHook):
88
+ """Caches ``(txt_key, txt_value)`` per block per unique prompt using
89
+ the stable cache key from the shared state."""
90
+
91
+ _is_stateful = True
92
+
93
+ def __init__(self, state_manager: StateManager, block_state_manager: StateManager):
94
+ super().__init__()
95
+ self.state_manager = state_manager
96
+ self.block_state_manager = block_state_manager
97
+
98
+ def new_forward(self, module: torch.nn.Module, *args, **kwargs):
99
+ from ..models.transformers.transformer_nucleusmoe_image import _apply_rotary_emb_nucleus
100
+
101
+ if self.state_manager._current_context is None:
102
+ self.state_manager.set_context("inference")
103
+
104
+ if self.block_state_manager._current_context is None:
105
+ self.block_state_manager.set_context("inference")
106
+
107
+ if "encoder_hidden_states" in kwargs:
108
+ encoder_hidden_states = kwargs["encoder_hidden_states"]
109
+ else:
110
+ encoder_hidden_states = args[1]
111
+
112
+ if "image_rotary_emb" in kwargs:
113
+ image_rotary_emb = kwargs["image_rotary_emb"]
114
+ elif len(args) > 3:
115
+ image_rotary_emb = args[3]
116
+ else:
117
+ image_rotary_emb = None
118
+
119
+ state: TextKVCacheState = self.state_manager.get_state()
120
+ cache_key = state.key
121
+
122
+ block_state: TextKVCacheBlockState = self.block_state_manager.get_state()
123
+
124
+ if cache_key not in block_state.kv_cache:
125
+ context = module.encoder_proj(encoder_hidden_states)
126
+
127
+ attn = module.attn
128
+ head_dim = attn.inner_dim // attn.heads
129
+ num_kv_heads = attn.inner_kv_dim // head_dim
130
+
131
+ txt_key = attn.add_k_proj(context).unflatten(-1, (num_kv_heads, -1))
132
+ txt_value = attn.add_v_proj(context).unflatten(-1, (num_kv_heads, -1))
133
+
134
+ if attn.norm_added_k is not None:
135
+ txt_key = attn.norm_added_k(txt_key)
136
+
137
+ if image_rotary_emb is not None:
138
+ _, txt_freqs = image_rotary_emb
139
+ txt_key = _apply_rotary_emb_nucleus(txt_key, txt_freqs, use_real=False)
140
+
141
+ block_state.kv_cache[cache_key] = (txt_key, txt_value)
142
+
143
+ txt_key, txt_value = block_state.kv_cache[cache_key]
144
+
145
+ attn_kwargs = kwargs.get("attention_kwargs") or {}
146
+ attn_kwargs["cached_txt_key"] = txt_key
147
+ attn_kwargs["cached_txt_value"] = txt_value
148
+ kwargs["attention_kwargs"] = attn_kwargs
149
+
150
+ return self.fn_ref.original_forward(*args, **kwargs)
151
+
152
+ def reset_state(self, module: torch.nn.Module):
153
+ self.block_state_manager.reset()
154
+ return module
155
+
156
+
157
+ def apply_text_kv_cache(module: torch.nn.Module, config: TextKVCacheConfig) -> None:
158
+ from ..models.transformers.transformer_nucleusmoe_image import NucleusMoEImageTransformerBlock
159
+
160
+ HookRegistry.check_if_exists_or_initialize(module)
161
+
162
+ state_manager = StateManager(TextKVCacheState)
163
+
164
+ transformer_hook = TextKVCacheTransformerHook(state_manager)
165
+ registry = HookRegistry.check_if_exists_or_initialize(module)
166
+ registry.register_hook(transformer_hook, _TEXT_KV_CACHE_TRANSFORMER_HOOK)
167
+
168
+ for _, submodule in module.named_modules():
169
+ if isinstance(submodule, NucleusMoEImageTransformerBlock):
170
+ block_state_manager = StateManager(TextKVCacheBlockState)
171
+ hook = TextKVCacheBlockHook(state_manager, block_state_manager)
172
+ block_registry = HookRegistry.check_if_exists_or_initialize(submodule)
173
+ block_registry.register_hook(hook, _TEXT_KV_CACHE_BLOCK_HOOK)
third_party/diffusers/src/diffusers/hooks/utils.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import torch
16
+
17
+ from ._common import _ALL_TRANSFORMER_BLOCK_IDENTIFIERS, _ATTENTION_CLASSES, _FEEDFORWARD_CLASSES
18
+
19
+
20
+ def _get_identifiable_transformer_blocks_in_module(module: torch.nn.Module):
21
+ module_list_with_transformer_blocks = []
22
+ for name, submodule in module.named_modules():
23
+ name_endswith_identifier = any(name.endswith(identifier) for identifier in _ALL_TRANSFORMER_BLOCK_IDENTIFIERS)
24
+ is_ModuleList = isinstance(submodule, torch.nn.ModuleList)
25
+ if name_endswith_identifier and is_ModuleList:
26
+ module_list_with_transformer_blocks.append((name, submodule))
27
+ return module_list_with_transformer_blocks
28
+
29
+
30
+ def _get_identifiable_attention_layers_in_module(module: torch.nn.Module):
31
+ attention_layers = []
32
+ for name, submodule in module.named_modules():
33
+ if isinstance(submodule, _ATTENTION_CLASSES):
34
+ attention_layers.append((name, submodule))
35
+ return attention_layers
36
+
37
+
38
+ def _get_identifiable_feedforward_layers_in_module(module: torch.nn.Module):
39
+ feedforward_layers = []
40
+ for name, submodule in module.named_modules():
41
+ if isinstance(submodule, _FEEDFORWARD_CLASSES):
42
+ feedforward_layers.append((name, submodule))
43
+ return feedforward_layers
third_party/diffusers/src/diffusers/image_processor.py ADDED
@@ -0,0 +1,1468 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import math
16
+ import warnings
17
+
18
+ import numpy as np
19
+ import PIL.Image
20
+ import torch
21
+ import torch.nn.functional as F
22
+ from PIL import Image, ImageFilter, ImageOps
23
+
24
+ from .configuration_utils import ConfigMixin, register_to_config
25
+ from .utils import CONFIG_NAME, PIL_INTERPOLATION, deprecate
26
+
27
+
28
+ PipelineImageInput = (
29
+ PIL.Image.Image | np.ndarray | torch.Tensor | list[PIL.Image.Image] | list[np.ndarray] | list[torch.Tensor]
30
+ )
31
+
32
+ PipelineDepthInput = PipelineImageInput
33
+
34
+
35
+ def is_valid_image(image) -> bool:
36
+ r"""
37
+ Checks if the input is a valid image.
38
+
39
+ A valid image can be:
40
+ - A `PIL.Image.Image`.
41
+ - A 2D or 3D `np.ndarray` or `torch.Tensor` (grayscale or color image).
42
+
43
+ Args:
44
+ image (`PIL.Image.Image | np.ndarray | torch.Tensor`):
45
+ The image to validate. It can be a PIL image, a NumPy array, or a torch tensor.
46
+
47
+ Returns:
48
+ `bool`:
49
+ `True` if the input is a valid image, `False` otherwise.
50
+ """
51
+ return isinstance(image, PIL.Image.Image) or isinstance(image, (np.ndarray, torch.Tensor)) and image.ndim in (2, 3)
52
+
53
+
54
+ def is_valid_image_imagelist(images):
55
+ r"""
56
+ Checks if the input is a valid image or list of images.
57
+
58
+ The input can be one of the following formats:
59
+ - A 4D tensor or numpy array (batch of images).
60
+ - A valid single image: `PIL.Image.Image`, 2D `np.ndarray` or `torch.Tensor` (grayscale image), 3D `np.ndarray` or
61
+ `torch.Tensor`.
62
+ - A list of valid images.
63
+
64
+ Args:
65
+ images (`np.ndarray | torch.Tensor | PIL.Image.Image | list`):
66
+ The image(s) to check. Can be a batch of images (4D tensor/array), a single image, or a list of valid
67
+ images.
68
+
69
+ Returns:
70
+ `bool`:
71
+ `True` if the input is valid, `False` otherwise.
72
+ """
73
+ if isinstance(images, (np.ndarray, torch.Tensor)) and images.ndim == 4:
74
+ return True
75
+ elif is_valid_image(images):
76
+ return True
77
+ elif isinstance(images, list):
78
+ return all(is_valid_image(image) for image in images)
79
+ return False
80
+
81
+
82
+ class VaeImageProcessor(ConfigMixin):
83
+ """
84
+ Image processor for VAE.
85
+
86
+ Args:
87
+ do_resize (`bool`, *optional*, defaults to `True`):
88
+ Whether to downscale the image's (height, width) dimensions to multiples of `vae_scale_factor`. Can accept
89
+ `height` and `width` arguments from [`image_processor.VaeImageProcessor.preprocess`] method.
90
+ vae_scale_factor (`int`, *optional*, defaults to `8`):
91
+ VAE scale factor. If `do_resize` is `True`, the image is automatically resized to multiples of this factor.
92
+ resample (`str`, *optional*, defaults to `lanczos`):
93
+ Resampling filter to use when resizing the image.
94
+ do_normalize (`bool`, *optional*, defaults to `True`):
95
+ Whether to normalize the image to [-1,1].
96
+ do_binarize (`bool`, *optional*, defaults to `False`):
97
+ Whether to binarize the image to 0/1.
98
+ do_convert_rgb (`bool`, *optional*, defaults to be `False`):
99
+ Whether to convert the images to RGB format.
100
+ do_convert_grayscale (`bool`, *optional*, defaults to be `False`):
101
+ Whether to convert the images to grayscale format.
102
+ """
103
+
104
+ config_name = CONFIG_NAME
105
+
106
+ @register_to_config
107
+ def __init__(
108
+ self,
109
+ do_resize: bool = True,
110
+ vae_scale_factor: int = 8,
111
+ vae_latent_channels: int = 4,
112
+ resample: str = "lanczos",
113
+ reducing_gap: int | None = None,
114
+ do_normalize: bool = True,
115
+ do_binarize: bool = False,
116
+ do_convert_rgb: bool = False,
117
+ do_convert_grayscale: bool = False,
118
+ ):
119
+ super().__init__()
120
+ if do_convert_rgb and do_convert_grayscale:
121
+ raise ValueError(
122
+ "`do_convert_rgb` and `do_convert_grayscale` can not both be set to `True`,"
123
+ " if you intended to convert the image into RGB format, please set `do_convert_grayscale = False`.",
124
+ " if you intended to convert the image into grayscale format, please set `do_convert_rgb = False`",
125
+ )
126
+
127
+ @staticmethod
128
+ def numpy_to_pil(images: np.ndarray) -> list[PIL.Image.Image]:
129
+ r"""
130
+ Convert a numpy image or a batch of images to a PIL image.
131
+
132
+ Args:
133
+ images (`np.ndarray`):
134
+ The image array to convert to PIL format.
135
+
136
+ Returns:
137
+ `list[PIL.Image.Image]`:
138
+ A list of PIL images.
139
+ """
140
+ if images.ndim == 3:
141
+ images = images[None, ...]
142
+ images = (images * 255).round().astype("uint8")
143
+ if images.shape[-1] == 1:
144
+ # special case for grayscale (single channel) images
145
+ pil_images = [Image.fromarray(image.squeeze(), mode="L") for image in images]
146
+ else:
147
+ pil_images = [Image.fromarray(image) for image in images]
148
+
149
+ return pil_images
150
+
151
+ @staticmethod
152
+ def pil_to_numpy(images: list[PIL.Image.Image] | PIL.Image.Image) -> np.ndarray:
153
+ r"""
154
+ Convert a PIL image or a list of PIL images to NumPy arrays.
155
+
156
+ Args:
157
+ images (`PIL.Image.Image` or `list[PIL.Image.Image]`):
158
+ The PIL image or list of images to convert to NumPy format.
159
+
160
+ Returns:
161
+ `np.ndarray`:
162
+ A NumPy array representation of the images.
163
+ """
164
+ if not isinstance(images, list):
165
+ images = [images]
166
+ images = [np.array(image).astype(np.float32) / 255.0 for image in images]
167
+ images = np.stack(images, axis=0)
168
+
169
+ return images
170
+
171
+ @staticmethod
172
+ def numpy_to_pt(images: np.ndarray) -> torch.Tensor:
173
+ r"""
174
+ Convert a NumPy image to a PyTorch tensor.
175
+
176
+ Args:
177
+ images (`np.ndarray`):
178
+ The NumPy image array to convert to PyTorch format.
179
+
180
+ Returns:
181
+ `torch.Tensor`:
182
+ A PyTorch tensor representation of the images.
183
+ """
184
+ if images.ndim == 3:
185
+ images = images[..., None]
186
+
187
+ images = torch.from_numpy(images.transpose(0, 3, 1, 2))
188
+ return images
189
+
190
+ @staticmethod
191
+ def pt_to_numpy(images: torch.Tensor) -> np.ndarray:
192
+ r"""
193
+ Convert a PyTorch tensor to a NumPy image.
194
+
195
+ Args:
196
+ images (`torch.Tensor`):
197
+ The PyTorch tensor to convert to NumPy format.
198
+
199
+ Returns:
200
+ `np.ndarray`:
201
+ A NumPy array representation of the images.
202
+ """
203
+ images = images.cpu().permute(0, 2, 3, 1).float().numpy()
204
+ return images
205
+
206
+ @staticmethod
207
+ def normalize(images: np.ndarray | torch.Tensor) -> np.ndarray | torch.Tensor:
208
+ r"""
209
+ Normalize an image array to [-1,1].
210
+
211
+ Args:
212
+ images (`np.ndarray` or `torch.Tensor`):
213
+ The image array to normalize.
214
+
215
+ Returns:
216
+ `np.ndarray` or `torch.Tensor`:
217
+ The normalized image array.
218
+ """
219
+ return 2.0 * images - 1.0
220
+
221
+ @staticmethod
222
+ def denormalize(images: np.ndarray | torch.Tensor) -> np.ndarray | torch.Tensor:
223
+ r"""
224
+ Denormalize an image array to [0,1].
225
+
226
+ Args:
227
+ images (`np.ndarray` or `torch.Tensor`):
228
+ The image array to denormalize.
229
+
230
+ Returns:
231
+ `np.ndarray` or `torch.Tensor`:
232
+ The denormalized image array.
233
+ """
234
+ return (images * 0.5 + 0.5).clamp(0, 1)
235
+
236
+ @staticmethod
237
+ def convert_to_rgb(image: PIL.Image.Image) -> PIL.Image.Image:
238
+ r"""
239
+ Converts a PIL image to RGB format.
240
+
241
+ Args:
242
+ image (`PIL.Image.Image`):
243
+ The PIL image to convert to RGB.
244
+
245
+ Returns:
246
+ `PIL.Image.Image`:
247
+ The RGB-converted PIL image.
248
+ """
249
+ image = image.convert("RGB")
250
+
251
+ return image
252
+
253
+ @staticmethod
254
+ def convert_to_grayscale(image: PIL.Image.Image) -> PIL.Image.Image:
255
+ r"""
256
+ Converts a given PIL image to grayscale.
257
+
258
+ Args:
259
+ image (`PIL.Image.Image`):
260
+ The input image to convert.
261
+
262
+ Returns:
263
+ `PIL.Image.Image`:
264
+ The image converted to grayscale.
265
+ """
266
+ image = image.convert("L")
267
+
268
+ return image
269
+
270
+ @staticmethod
271
+ def blur(image: PIL.Image.Image, blur_factor: int = 4) -> PIL.Image.Image:
272
+ r"""
273
+ Applies Gaussian blur to an image.
274
+
275
+ Args:
276
+ image (`PIL.Image.Image`):
277
+ The PIL image to convert to grayscale.
278
+
279
+ Returns:
280
+ `PIL.Image.Image`:
281
+ The grayscale-converted PIL image.
282
+ """
283
+ image = image.filter(ImageFilter.GaussianBlur(blur_factor))
284
+
285
+ return image
286
+
287
+ @staticmethod
288
+ def get_crop_region(mask_image: PIL.Image.Image, width: int, height: int, pad=0):
289
+ r"""
290
+ Finds a rectangular region that contains all masked ares in an image, and expands region to match the aspect
291
+ ratio of the original image; for example, if user drew mask in a 128x32 region, and the dimensions for
292
+ processing are 512x512, the region will be expanded to 128x128.
293
+
294
+ Args:
295
+ mask_image (PIL.Image.Image): Mask image.
296
+ width (int): Width of the image to be processed.
297
+ height (int): Height of the image to be processed.
298
+ pad (int, optional): Padding to be added to the crop region. Defaults to 0.
299
+
300
+ Returns:
301
+ tuple: (x1, y1, x2, y2) represent a rectangular region that contains all masked ares in an image and
302
+ matches the original aspect ratio.
303
+ """
304
+
305
+ mask_image = mask_image.convert("L")
306
+ mask = np.array(mask_image)
307
+
308
+ # 1. find a rectangular region that contains all masked ares in an image
309
+ h, w = mask.shape
310
+ crop_left = 0
311
+ for i in range(w):
312
+ if not (mask[:, i] == 0).all():
313
+ break
314
+ crop_left += 1
315
+
316
+ crop_right = 0
317
+ for i in reversed(range(w)):
318
+ if not (mask[:, i] == 0).all():
319
+ break
320
+ crop_right += 1
321
+
322
+ crop_top = 0
323
+ for i in range(h):
324
+ if not (mask[i] == 0).all():
325
+ break
326
+ crop_top += 1
327
+
328
+ crop_bottom = 0
329
+ for i in reversed(range(h)):
330
+ if not (mask[i] == 0).all():
331
+ break
332
+ crop_bottom += 1
333
+
334
+ # 2. add padding to the crop region
335
+ x1, y1, x2, y2 = (
336
+ int(max(crop_left - pad, 0)),
337
+ int(max(crop_top - pad, 0)),
338
+ int(min(w - crop_right + pad, w)),
339
+ int(min(h - crop_bottom + pad, h)),
340
+ )
341
+
342
+ # 3. expands crop region to match the aspect ratio of the image to be processed
343
+ ratio_crop_region = (x2 - x1) / (y2 - y1)
344
+ ratio_processing = width / height
345
+
346
+ if ratio_crop_region > ratio_processing:
347
+ desired_height = (x2 - x1) / ratio_processing
348
+ desired_height_diff = int(desired_height - (y2 - y1))
349
+ y1 -= desired_height_diff // 2
350
+ y2 += desired_height_diff - desired_height_diff // 2
351
+ if y2 >= mask_image.height:
352
+ diff = y2 - mask_image.height
353
+ y2 -= diff
354
+ y1 -= diff
355
+ if y1 < 0:
356
+ y2 -= y1
357
+ y1 -= y1
358
+ if y2 >= mask_image.height:
359
+ y2 = mask_image.height
360
+ else:
361
+ desired_width = (y2 - y1) * ratio_processing
362
+ desired_width_diff = int(desired_width - (x2 - x1))
363
+ x1 -= desired_width_diff // 2
364
+ x2 += desired_width_diff - desired_width_diff // 2
365
+ if x2 >= mask_image.width:
366
+ diff = x2 - mask_image.width
367
+ x2 -= diff
368
+ x1 -= diff
369
+ if x1 < 0:
370
+ x2 -= x1
371
+ x1 -= x1
372
+ if x2 >= mask_image.width:
373
+ x2 = mask_image.width
374
+
375
+ return x1, y1, x2, y2
376
+
377
+ def _resize_and_fill(
378
+ self,
379
+ image: PIL.Image.Image,
380
+ width: int,
381
+ height: int,
382
+ ) -> PIL.Image.Image:
383
+ r"""
384
+ Resize the image to fit within the specified width and height, maintaining the aspect ratio, and then center
385
+ the image within the dimensions, filling empty with data from image.
386
+
387
+ Args:
388
+ image (`PIL.Image.Image`):
389
+ The image to resize and fill.
390
+ width (`int`):
391
+ The width to resize the image to.
392
+ height (`int`):
393
+ The height to resize the image to.
394
+
395
+ Returns:
396
+ `PIL.Image.Image`:
397
+ The resized and filled image.
398
+ """
399
+
400
+ ratio = width / height
401
+ src_ratio = image.width / image.height
402
+
403
+ src_w = width if ratio < src_ratio else image.width * height // image.height
404
+ src_h = height if ratio >= src_ratio else image.height * width // image.width
405
+
406
+ resized = image.resize((src_w, src_h), resample=PIL_INTERPOLATION[self.config.resample])
407
+ res = Image.new("RGB", (width, height))
408
+ res.paste(resized, box=(width // 2 - src_w // 2, height // 2 - src_h // 2))
409
+
410
+ if ratio < src_ratio:
411
+ fill_height = height // 2 - src_h // 2
412
+ if fill_height > 0:
413
+ res.paste(resized.resize((width, fill_height), box=(0, 0, width, 0)), box=(0, 0))
414
+ res.paste(
415
+ resized.resize((width, fill_height), box=(0, resized.height, width, resized.height)),
416
+ box=(0, fill_height + src_h),
417
+ )
418
+ elif ratio > src_ratio:
419
+ fill_width = width // 2 - src_w // 2
420
+ if fill_width > 0:
421
+ res.paste(resized.resize((fill_width, height), box=(0, 0, 0, height)), box=(0, 0))
422
+ res.paste(
423
+ resized.resize((fill_width, height), box=(resized.width, 0, resized.width, height)),
424
+ box=(fill_width + src_w, 0),
425
+ )
426
+
427
+ return res
428
+
429
+ def _resize_and_crop(
430
+ self,
431
+ image: PIL.Image.Image,
432
+ width: int,
433
+ height: int,
434
+ ) -> PIL.Image.Image:
435
+ r"""
436
+ Resize the image to fit within the specified width and height, maintaining the aspect ratio, and then center
437
+ the image within the dimensions, cropping the excess.
438
+
439
+ Args:
440
+ image (`PIL.Image.Image`):
441
+ The image to resize and crop.
442
+ width (`int`):
443
+ The width to resize the image to.
444
+ height (`int`):
445
+ The height to resize the image to.
446
+
447
+ Returns:
448
+ `PIL.Image.Image`:
449
+ The resized and cropped image.
450
+ """
451
+ ratio = width / height
452
+ src_ratio = image.width / image.height
453
+
454
+ src_w = width if ratio > src_ratio else image.width * height // image.height
455
+ src_h = height if ratio <= src_ratio else image.height * width // image.width
456
+
457
+ resized = image.resize((src_w, src_h), resample=PIL_INTERPOLATION[self.config.resample])
458
+ res = Image.new("RGB", (width, height))
459
+ res.paste(resized, box=(width // 2 - src_w // 2, height // 2 - src_h // 2))
460
+ return res
461
+
462
+ def resize(
463
+ self,
464
+ image: PIL.Image.Image | np.ndarray | torch.Tensor,
465
+ height: int,
466
+ width: int,
467
+ resize_mode: str = "default", # "default", "fill", "crop"
468
+ ) -> PIL.Image.Image | np.ndarray | torch.Tensor:
469
+ """
470
+ Resize image.
471
+
472
+ Args:
473
+ image (`PIL.Image.Image`, `np.ndarray` or `torch.Tensor`):
474
+ The image input, can be a PIL image, numpy array or pytorch tensor.
475
+ height (`int`):
476
+ The height to resize to.
477
+ width (`int`):
478
+ The width to resize to.
479
+ resize_mode (`str`, *optional*, defaults to `default`):
480
+ The resize mode to use, can be one of `default` or `fill`. If `default`, will resize the image to fit
481
+ within the specified width and height, and it may not maintaining the original aspect ratio. If `fill`,
482
+ will resize the image to fit within the specified width and height, maintaining the aspect ratio, and
483
+ then center the image within the dimensions, filling empty with data from image. If `crop`, will resize
484
+ the image to fit within the specified width and height, maintaining the aspect ratio, and then center
485
+ the image within the dimensions, cropping the excess. Note that resize_mode `fill` and `crop` are only
486
+ supported for PIL image input.
487
+
488
+ Returns:
489
+ `PIL.Image.Image`, `np.ndarray` or `torch.Tensor`:
490
+ The resized image.
491
+ """
492
+ if resize_mode != "default" and not isinstance(image, PIL.Image.Image):
493
+ raise ValueError(f"Only PIL image input is supported for resize_mode {resize_mode}")
494
+ if isinstance(image, PIL.Image.Image):
495
+ if resize_mode == "default":
496
+ image = image.resize(
497
+ (width, height),
498
+ resample=PIL_INTERPOLATION[self.config.resample],
499
+ reducing_gap=self.config.reducing_gap,
500
+ )
501
+ elif resize_mode == "fill":
502
+ image = self._resize_and_fill(image, width, height)
503
+ elif resize_mode == "crop":
504
+ image = self._resize_and_crop(image, width, height)
505
+ else:
506
+ raise ValueError(f"resize_mode {resize_mode} is not supported")
507
+
508
+ elif isinstance(image, torch.Tensor):
509
+ image = torch.nn.functional.interpolate(
510
+ image,
511
+ size=(height, width),
512
+ )
513
+ elif isinstance(image, np.ndarray):
514
+ image = self.numpy_to_pt(image)
515
+ image = torch.nn.functional.interpolate(
516
+ image,
517
+ size=(height, width),
518
+ )
519
+ image = self.pt_to_numpy(image)
520
+
521
+ return image
522
+
523
+ def binarize(self, image: PIL.Image.Image) -> PIL.Image.Image:
524
+ """
525
+ Create a mask.
526
+
527
+ Args:
528
+ image (`PIL.Image.Image`):
529
+ The image input, should be a PIL image.
530
+
531
+ Returns:
532
+ `PIL.Image.Image`:
533
+ The binarized image. Values less than 0.5 are set to 0, values greater than 0.5 are set to 1.
534
+ """
535
+ image[image < 0.5] = 0
536
+ image[image >= 0.5] = 1
537
+
538
+ return image
539
+
540
+ def _denormalize_conditionally(
541
+ self, images: torch.Tensor, do_denormalize: list[bool] | None = None
542
+ ) -> torch.Tensor:
543
+ r"""
544
+ Denormalize a batch of images based on a condition list.
545
+
546
+ Args:
547
+ images (`torch.Tensor`):
548
+ The input image tensor.
549
+ do_denormalize (`Optional[list[bool]`, *optional*, defaults to `None`):
550
+ A list of booleans indicating whether to denormalize each image in the batch. If `None`, will use the
551
+ value of `do_normalize` in the `VaeImageProcessor` config.
552
+ """
553
+ if do_denormalize is None:
554
+ return self.denormalize(images) if self.config.do_normalize else images
555
+
556
+ return torch.stack(
557
+ [self.denormalize(images[i]) if do_denormalize[i] else images[i] for i in range(images.shape[0])]
558
+ )
559
+
560
+ def get_default_height_width(
561
+ self,
562
+ image: PIL.Image.Image | np.ndarray | torch.Tensor,
563
+ height: int | None = None,
564
+ width: int | None = None,
565
+ ) -> tuple[int, int]:
566
+ r"""
567
+ Returns the height and width of the image, downscaled to the next integer multiple of `vae_scale_factor`.
568
+
569
+ Args:
570
+ image (`PIL.Image.Image | np.ndarray | torch.Tensor`):
571
+ The image input, which can be a PIL image, NumPy array, or PyTorch tensor. If it is a NumPy array, it
572
+ should have shape `[batch, height, width]` or `[batch, height, width, channels]`. If it is a PyTorch
573
+ tensor, it should have shape `[batch, channels, height, width]`.
574
+ height (`int | None`, *optional*, defaults to `None`):
575
+ The height of the preprocessed image. If `None`, the height of the `image` input will be used.
576
+ width (`int | None`, *optional*, defaults to `None`):
577
+ The width of the preprocessed image. If `None`, the width of the `image` input will be used.
578
+
579
+ Returns:
580
+ `tuple[int, int]`:
581
+ A tuple containing the height and width, both resized to the nearest integer multiple of
582
+ `vae_scale_factor`.
583
+ """
584
+
585
+ if height is None:
586
+ if isinstance(image, PIL.Image.Image):
587
+ height = image.height
588
+ elif isinstance(image, torch.Tensor):
589
+ height = image.shape[2]
590
+ else:
591
+ height = image.shape[1]
592
+
593
+ if width is None:
594
+ if isinstance(image, PIL.Image.Image):
595
+ width = image.width
596
+ elif isinstance(image, torch.Tensor):
597
+ width = image.shape[3]
598
+ else:
599
+ width = image.shape[2]
600
+
601
+ width, height = (
602
+ x - x % self.config.vae_scale_factor for x in (width, height)
603
+ ) # resize to integer multiple of vae_scale_factor
604
+
605
+ return height, width
606
+
607
+ def preprocess(
608
+ self,
609
+ image: PipelineImageInput,
610
+ height: int | None = None,
611
+ width: int | None = None,
612
+ resize_mode: str = "default", # "default", "fill", "crop"
613
+ crops_coords: tuple[int, int, int, int] | None = None,
614
+ ) -> torch.Tensor:
615
+ """
616
+ Preprocess the image input.
617
+
618
+ Args:
619
+ image (`PipelineImageInput`):
620
+ The image input, accepted formats are PIL images, NumPy arrays, PyTorch tensors; Also accept list of
621
+ supported formats.
622
+ height (`int`, *optional*):
623
+ The height in preprocessed image. If `None`, will use the `get_default_height_width()` to get default
624
+ height.
625
+ width (`int`, *optional*):
626
+ The width in preprocessed. If `None`, will use get_default_height_width()` to get the default width.
627
+ resize_mode (`str`, *optional*, defaults to `default`):
628
+ The resize mode, can be one of `default` or `fill`. If `default`, will resize the image to fit within
629
+ the specified width and height, and it may not maintaining the original aspect ratio. If `fill`, will
630
+ resize the image to fit within the specified width and height, maintaining the aspect ratio, and then
631
+ center the image within the dimensions, filling empty with data from image. If `crop`, will resize the
632
+ image to fit within the specified width and height, maintaining the aspect ratio, and then center the
633
+ image within the dimensions, cropping the excess. Note that resize_mode `fill` and `crop` are only
634
+ supported for PIL image input.
635
+ crops_coords (`list[tuple[int, int, int, int]]`, *optional*, defaults to `None`):
636
+ The crop coordinates for each image in the batch. If `None`, will not crop the image.
637
+
638
+ Returns:
639
+ `torch.Tensor`:
640
+ The preprocessed image.
641
+ """
642
+ supported_formats = (PIL.Image.Image, np.ndarray, torch.Tensor)
643
+
644
+ # Expand the missing dimension for 3-dimensional pytorch tensor or numpy array that represents grayscale image
645
+ if self.config.do_convert_grayscale and isinstance(image, (torch.Tensor, np.ndarray)) and image.ndim == 3:
646
+ if isinstance(image, torch.Tensor):
647
+ # if image is a pytorch tensor could have 2 possible shapes:
648
+ # 1. batch x height x width: we should insert the channel dimension at position 1
649
+ # 2. channel x height x width: we should insert batch dimension at position 0,
650
+ # however, since both channel and batch dimension has same size 1, it is same to insert at position 1
651
+ # for simplicity, we insert a dimension of size 1 at position 1 for both cases
652
+ image = image.unsqueeze(1)
653
+ else:
654
+ # if it is a numpy array, it could have 2 possible shapes:
655
+ # 1. batch x height x width: insert channel dimension on last position
656
+ # 2. height x width x channel: insert batch dimension on first position
657
+ if image.shape[-1] == 1:
658
+ image = np.expand_dims(image, axis=0)
659
+ else:
660
+ image = np.expand_dims(image, axis=-1)
661
+
662
+ if isinstance(image, list) and isinstance(image[0], np.ndarray) and image[0].ndim == 4:
663
+ warnings.warn(
664
+ "Passing `image` as a list of 4d np.ndarray is deprecated."
665
+ "Please concatenate the list along the batch dimension and pass it as a single 4d np.ndarray",
666
+ FutureWarning,
667
+ )
668
+ image = np.concatenate(image, axis=0)
669
+ if isinstance(image, list) and isinstance(image[0], torch.Tensor) and image[0].ndim == 4:
670
+ warnings.warn(
671
+ "Passing `image` as a list of 4d torch.Tensor is deprecated."
672
+ "Please concatenate the list along the batch dimension and pass it as a single 4d torch.Tensor",
673
+ FutureWarning,
674
+ )
675
+ image = torch.cat(image, axis=0)
676
+
677
+ if not is_valid_image_imagelist(image):
678
+ raise ValueError(
679
+ f"Input is in incorrect format. Currently, we only support {', '.join(str(x) for x in supported_formats)}"
680
+ )
681
+ if not isinstance(image, list):
682
+ image = [image]
683
+
684
+ if isinstance(image[0], PIL.Image.Image):
685
+ if crops_coords is not None:
686
+ image = [i.crop(crops_coords) for i in image]
687
+ if self.config.do_resize:
688
+ height, width = self.get_default_height_width(image[0], height, width)
689
+ image = [self.resize(i, height, width, resize_mode=resize_mode) for i in image]
690
+ if self.config.do_convert_rgb:
691
+ image = [self.convert_to_rgb(i) for i in image]
692
+ elif self.config.do_convert_grayscale:
693
+ image = [self.convert_to_grayscale(i) for i in image]
694
+ image = self.pil_to_numpy(image) # to np
695
+ image = self.numpy_to_pt(image) # to pt
696
+
697
+ elif isinstance(image[0], np.ndarray):
698
+ image = np.concatenate(image, axis=0) if image[0].ndim == 4 else np.stack(image, axis=0)
699
+
700
+ image = self.numpy_to_pt(image)
701
+
702
+ height, width = self.get_default_height_width(image, height, width)
703
+ if self.config.do_resize:
704
+ image = self.resize(image, height, width)
705
+
706
+ elif isinstance(image[0], torch.Tensor):
707
+ image = torch.cat(image, axis=0) if image[0].ndim == 4 else torch.stack(image, axis=0)
708
+
709
+ if self.config.do_convert_grayscale and image.ndim == 3:
710
+ image = image.unsqueeze(1)
711
+
712
+ channel = image.shape[1]
713
+ # don't need any preprocess if the image is latents
714
+ if channel == self.config.vae_latent_channels:
715
+ return image
716
+
717
+ height, width = self.get_default_height_width(image, height, width)
718
+ if self.config.do_resize:
719
+ image = self.resize(image, height, width)
720
+
721
+ # expected range [0,1], normalize to [-1,1]
722
+ do_normalize = self.config.do_normalize
723
+ if do_normalize and image.min() < 0:
724
+ warnings.warn(
725
+ "Passing `image` as torch tensor with value range in [-1,1] is deprecated. The expected value range for image tensor is [0,1] "
726
+ f"when passing as pytorch tensor or numpy Array. You passed `image` with value range [{image.min()},{image.max()}]",
727
+ FutureWarning,
728
+ )
729
+ do_normalize = False
730
+ if do_normalize:
731
+ image = self.normalize(image)
732
+
733
+ if self.config.do_binarize:
734
+ image = self.binarize(image)
735
+
736
+ return image
737
+
738
+ def postprocess(
739
+ self,
740
+ image: torch.Tensor,
741
+ output_type: str = "pil",
742
+ do_denormalize: list[bool] | None = None,
743
+ ) -> PIL.Image.Image | np.ndarray | torch.Tensor:
744
+ """
745
+ Postprocess the image output from tensor to `output_type`.
746
+
747
+ Args:
748
+ image (`torch.Tensor`):
749
+ The image input, should be a pytorch tensor with shape `B x C x H x W`.
750
+ output_type (`str`, *optional*, defaults to `pil`):
751
+ The output type of the image, can be one of `pil`, `np`, `pt`, `latent`.
752
+ do_denormalize (`list[bool]`, *optional*, defaults to `None`):
753
+ Whether to denormalize the image to [0,1]. If `None`, will use the value of `do_normalize` in the
754
+ `VaeImageProcessor` config.
755
+
756
+ Returns:
757
+ `PIL.Image.Image`, `np.ndarray` or `torch.Tensor`:
758
+ The postprocessed image.
759
+ """
760
+ if not isinstance(image, torch.Tensor):
761
+ raise ValueError(
762
+ f"Input for postprocessing is in incorrect format: {type(image)}. We only support pytorch tensor"
763
+ )
764
+ if output_type not in ["latent", "pt", "np", "pil"]:
765
+ deprecation_message = (
766
+ f"the output_type {output_type} is outdated and has been set to `np`. Please make sure to set it to one of these instead: "
767
+ "`pil`, `np`, `pt`, `latent`"
768
+ )
769
+ deprecate("Unsupported output_type", "1.0.0", deprecation_message, standard_warn=False)
770
+ output_type = "np"
771
+
772
+ if output_type == "latent":
773
+ return image
774
+
775
+ image = self._denormalize_conditionally(image, do_denormalize)
776
+
777
+ if output_type == "pt":
778
+ return image
779
+
780
+ image = self.pt_to_numpy(image)
781
+
782
+ if output_type == "np":
783
+ return image
784
+
785
+ if output_type == "pil":
786
+ return self.numpy_to_pil(image)
787
+
788
+ def apply_overlay(
789
+ self,
790
+ mask: PIL.Image.Image,
791
+ init_image: PIL.Image.Image,
792
+ image: PIL.Image.Image,
793
+ crop_coords: tuple[int, int, int, int] | None = None,
794
+ ) -> PIL.Image.Image:
795
+ r"""
796
+ Applies an overlay of the mask and the inpainted image on the original image.
797
+
798
+ Args:
799
+ mask (`PIL.Image.Image`):
800
+ The mask image that highlights regions to overlay.
801
+ init_image (`PIL.Image.Image`):
802
+ The original image to which the overlay is applied.
803
+ image (`PIL.Image.Image`):
804
+ The image to overlay onto the original.
805
+ crop_coords (`tuple[int, int, int, int]`, *optional*):
806
+ Coordinates to crop the image. If provided, the image will be cropped accordingly.
807
+
808
+ Returns:
809
+ `PIL.Image.Image`:
810
+ The final image with the overlay applied.
811
+ """
812
+
813
+ width, height = init_image.width, init_image.height
814
+
815
+ init_image_masked = PIL.Image.new("RGBa", (width, height))
816
+ init_image_masked.paste(init_image.convert("RGBA").convert("RGBa"), mask=ImageOps.invert(mask.convert("L")))
817
+
818
+ init_image_masked = init_image_masked.convert("RGBA")
819
+
820
+ if crop_coords is not None:
821
+ x, y, x2, y2 = crop_coords
822
+ w = x2 - x
823
+ h = y2 - y
824
+ base_image = PIL.Image.new("RGBA", (width, height))
825
+ image = self.resize(image, height=h, width=w, resize_mode="crop")
826
+ base_image.paste(image, (x, y))
827
+ image = base_image.convert("RGB")
828
+
829
+ image = image.convert("RGBA")
830
+ image.alpha_composite(init_image_masked)
831
+ image = image.convert("RGB")
832
+
833
+ return image
834
+
835
+
836
+ class InpaintProcessor(ConfigMixin):
837
+ """
838
+ Image processor for inpainting image and mask.
839
+ """
840
+
841
+ config_name = CONFIG_NAME
842
+
843
+ @register_to_config
844
+ def __init__(
845
+ self,
846
+ do_resize: bool = True,
847
+ vae_scale_factor: int = 8,
848
+ vae_latent_channels: int = 4,
849
+ resample: str = "lanczos",
850
+ reducing_gap: int | None = None,
851
+ do_normalize: bool = True,
852
+ do_binarize: bool = False,
853
+ do_convert_grayscale: bool = False,
854
+ mask_do_normalize: bool = False,
855
+ mask_do_binarize: bool = True,
856
+ mask_do_convert_grayscale: bool = True,
857
+ ):
858
+ super().__init__()
859
+
860
+ self._image_processor = VaeImageProcessor(
861
+ do_resize=do_resize,
862
+ vae_scale_factor=vae_scale_factor,
863
+ vae_latent_channels=vae_latent_channels,
864
+ resample=resample,
865
+ reducing_gap=reducing_gap,
866
+ do_normalize=do_normalize,
867
+ do_binarize=do_binarize,
868
+ do_convert_grayscale=do_convert_grayscale,
869
+ )
870
+ self._mask_processor = VaeImageProcessor(
871
+ do_resize=do_resize,
872
+ vae_scale_factor=vae_scale_factor,
873
+ vae_latent_channels=vae_latent_channels,
874
+ resample=resample,
875
+ reducing_gap=reducing_gap,
876
+ do_normalize=mask_do_normalize,
877
+ do_binarize=mask_do_binarize,
878
+ do_convert_grayscale=mask_do_convert_grayscale,
879
+ )
880
+
881
+ def preprocess(
882
+ self,
883
+ image: PIL.Image.Image,
884
+ mask: PIL.Image.Image | None = None,
885
+ height: int | None = None,
886
+ width: int | None = None,
887
+ padding_mask_crop: int | None = None,
888
+ ) -> tuple[torch.Tensor, torch.Tensor]:
889
+ """
890
+ Preprocess the image and mask.
891
+ """
892
+ if mask is None and padding_mask_crop is not None:
893
+ raise ValueError("mask must be provided if padding_mask_crop is provided")
894
+
895
+ # if mask is None, same behavior as regular image processor
896
+ if mask is None:
897
+ return self._image_processor.preprocess(image, height=height, width=width)
898
+
899
+ if padding_mask_crop is not None:
900
+ crops_coords = self._image_processor.get_crop_region(mask, width, height, pad=padding_mask_crop)
901
+ resize_mode = "fill"
902
+ else:
903
+ crops_coords = None
904
+ resize_mode = "default"
905
+
906
+ processed_image = self._image_processor.preprocess(
907
+ image,
908
+ height=height,
909
+ width=width,
910
+ crops_coords=crops_coords,
911
+ resize_mode=resize_mode,
912
+ )
913
+
914
+ processed_mask = self._mask_processor.preprocess(
915
+ mask,
916
+ height=height,
917
+ width=width,
918
+ resize_mode=resize_mode,
919
+ crops_coords=crops_coords,
920
+ )
921
+
922
+ if crops_coords is not None:
923
+ postprocessing_kwargs = {
924
+ "crops_coords": crops_coords,
925
+ "original_image": image,
926
+ "original_mask": mask,
927
+ }
928
+ else:
929
+ postprocessing_kwargs = {
930
+ "crops_coords": None,
931
+ "original_image": None,
932
+ "original_mask": None,
933
+ }
934
+
935
+ return processed_image, processed_mask, postprocessing_kwargs
936
+
937
+ def postprocess(
938
+ self,
939
+ image: torch.Tensor,
940
+ output_type: str = "pil",
941
+ original_image: PIL.Image.Image | None = None,
942
+ original_mask: PIL.Image.Image | None = None,
943
+ crops_coords: tuple[int, int, int, int] | None = None,
944
+ ) -> tuple[PIL.Image.Image, PIL.Image.Image]:
945
+ """
946
+ Postprocess the image, optionally apply mask overlay
947
+ """
948
+ image = self._image_processor.postprocess(
949
+ image,
950
+ output_type=output_type,
951
+ )
952
+ # optionally apply the mask overlay
953
+ if crops_coords is not None and (original_image is None or original_mask is None):
954
+ raise ValueError("original_image and original_mask must be provided if crops_coords is provided")
955
+
956
+ elif crops_coords is not None and output_type != "pil":
957
+ raise ValueError("output_type must be 'pil' if crops_coords is provided")
958
+
959
+ elif crops_coords is not None:
960
+ image = [
961
+ self._image_processor.apply_overlay(original_mask, original_image, i, crops_coords) for i in image
962
+ ]
963
+
964
+ return image
965
+
966
+
967
+ class VaeImageProcessorLDM3D(VaeImageProcessor):
968
+ """
969
+ Image processor for VAE LDM3D.
970
+
971
+ Args:
972
+ do_resize (`bool`, *optional*, defaults to `True`):
973
+ Whether to downscale the image's (height, width) dimensions to multiples of `vae_scale_factor`.
974
+ vae_scale_factor (`int`, *optional*, defaults to `8`):
975
+ VAE scale factor. If `do_resize` is `True`, the image is automatically resized to multiples of this factor.
976
+ resample (`str`, *optional*, defaults to `lanczos`):
977
+ Resampling filter to use when resizing the image.
978
+ do_normalize (`bool`, *optional*, defaults to `True`):
979
+ Whether to normalize the image to [-1,1].
980
+ """
981
+
982
+ config_name = CONFIG_NAME
983
+
984
+ @register_to_config
985
+ def __init__(
986
+ self,
987
+ do_resize: bool = True,
988
+ vae_scale_factor: int = 8,
989
+ resample: str = "lanczos",
990
+ do_normalize: bool = True,
991
+ ):
992
+ super().__init__()
993
+
994
+ @staticmethod
995
+ def numpy_to_pil(images: np.ndarray) -> list[PIL.Image.Image]:
996
+ r"""
997
+ Convert a NumPy image or a batch of images to a list of PIL images.
998
+
999
+ Args:
1000
+ images (`np.ndarray`):
1001
+ The input NumPy array of images, which can be a single image or a batch.
1002
+
1003
+ Returns:
1004
+ `list[PIL.Image.Image]`:
1005
+ A list of PIL images converted from the input NumPy array.
1006
+ """
1007
+ if images.ndim == 3:
1008
+ images = images[None, ...]
1009
+ images = (images * 255).round().astype("uint8")
1010
+ if images.shape[-1] == 1:
1011
+ # special case for grayscale (single channel) images
1012
+ pil_images = [Image.fromarray(image.squeeze(), mode="L") for image in images]
1013
+ else:
1014
+ pil_images = [Image.fromarray(image[:, :, :3]) for image in images]
1015
+
1016
+ return pil_images
1017
+
1018
+ @staticmethod
1019
+ def depth_pil_to_numpy(images: list[PIL.Image.Image] | PIL.Image.Image) -> np.ndarray:
1020
+ r"""
1021
+ Convert a PIL image or a list of PIL images to NumPy arrays.
1022
+
1023
+ Args:
1024
+ images (`list[PIL.Image.Image, PIL.Image.Image]`):
1025
+ The input image or list of images to be converted.
1026
+
1027
+ Returns:
1028
+ `np.ndarray`:
1029
+ A NumPy array of the converted images.
1030
+ """
1031
+ if not isinstance(images, list):
1032
+ images = [images]
1033
+
1034
+ images = [np.array(image).astype(np.float32) / (2**16 - 1) for image in images]
1035
+ images = np.stack(images, axis=0)
1036
+ return images
1037
+
1038
+ @staticmethod
1039
+ def rgblike_to_depthmap(image: np.ndarray | torch.Tensor) -> np.ndarray | torch.Tensor:
1040
+ r"""
1041
+ Convert an RGB-like depth image to a depth map.
1042
+ """
1043
+ # 1. Cast the tensor to a larger integer type (e.g., int32)
1044
+ # to safely perform the multiplication by 256.
1045
+ # 2. Perform the 16-bit combination: High-byte * 256 + Low-byte.
1046
+ # 3. Cast the final result to the desired depth map type (uint16) if needed
1047
+ # before returning, though leaving it as int32/int64 is often safer
1048
+ # for return value from a library function.
1049
+
1050
+ if isinstance(image, torch.Tensor):
1051
+ # Cast to a safe dtype (e.g., int32 or int64) for the calculation
1052
+ original_dtype = image.dtype
1053
+ image_safe = image.to(torch.int32)
1054
+
1055
+ # Calculate the depth map
1056
+ depth_map = image_safe[:, :, 1] * 256 + image_safe[:, :, 2]
1057
+
1058
+ # You may want to cast the final result to uint16, but casting to a
1059
+ # larger int type (like int32) is sufficient to fix the overflow.
1060
+ # depth_map = depth_map.to(torch.uint16) # Uncomment if uint16 is strictly required
1061
+ return depth_map.to(original_dtype)
1062
+
1063
+ elif isinstance(image, np.ndarray):
1064
+ # NumPy equivalent: Cast to a safe dtype (e.g., np.int32)
1065
+ original_dtype = image.dtype
1066
+ image_safe = image.astype(np.int32)
1067
+
1068
+ # Calculate the depth map
1069
+ depth_map = image_safe[:, :, 1] * 256 + image_safe[:, :, 2]
1070
+
1071
+ # depth_map = depth_map.astype(np.uint16) # Uncomment if uint16 is strictly required
1072
+ return depth_map.astype(original_dtype)
1073
+ else:
1074
+ raise TypeError("Input image must be a torch.Tensor or np.ndarray")
1075
+
1076
+ def numpy_to_depth(self, images: np.ndarray) -> list[PIL.Image.Image]:
1077
+ r"""
1078
+ Convert a NumPy depth image or a batch of images to a list of PIL images.
1079
+
1080
+ Args:
1081
+ images (`np.ndarray`):
1082
+ The input NumPy array of depth images, which can be a single image or a batch.
1083
+
1084
+ Returns:
1085
+ `list[PIL.Image.Image]`:
1086
+ A list of PIL images converted from the input NumPy depth images.
1087
+ """
1088
+ if images.ndim == 3:
1089
+ images = images[None, ...]
1090
+ images_depth = images[:, :, :, 3:]
1091
+ if images.shape[-1] == 6:
1092
+ images_depth = (images_depth * 255).round().astype("uint8")
1093
+ pil_images = [
1094
+ Image.fromarray(self.rgblike_to_depthmap(image_depth), mode="I;16") for image_depth in images_depth
1095
+ ]
1096
+ elif images.shape[-1] == 4:
1097
+ images_depth = (images_depth * 65535.0).astype(np.uint16)
1098
+ pil_images = [Image.fromarray(image_depth, mode="I;16") for image_depth in images_depth]
1099
+ else:
1100
+ raise Exception("Not supported")
1101
+
1102
+ return pil_images
1103
+
1104
+ def postprocess(
1105
+ self,
1106
+ image: torch.Tensor,
1107
+ output_type: str = "pil",
1108
+ do_denormalize: list[bool] | None = None,
1109
+ ) -> PIL.Image.Image | np.ndarray | torch.Tensor:
1110
+ """
1111
+ Postprocess the image output from tensor to `output_type`.
1112
+
1113
+ Args:
1114
+ image (`torch.Tensor`):
1115
+ The image input, should be a pytorch tensor with shape `B x C x H x W`.
1116
+ output_type (`str`, *optional*, defaults to `pil`):
1117
+ The output type of the image, can be one of `pil`, `np`, `pt`, `latent`.
1118
+ do_denormalize (`list[bool]`, *optional*, defaults to `None`):
1119
+ Whether to denormalize the image to [0,1]. If `None`, will use the value of `do_normalize` in the
1120
+ `VaeImageProcessor` config.
1121
+
1122
+ Returns:
1123
+ `PIL.Image.Image`, `np.ndarray` or `torch.Tensor`:
1124
+ The postprocessed image.
1125
+ """
1126
+ if not isinstance(image, torch.Tensor):
1127
+ raise ValueError(
1128
+ f"Input for postprocessing is in incorrect format: {type(image)}. We only support pytorch tensor"
1129
+ )
1130
+ if output_type not in ["latent", "pt", "np", "pil"]:
1131
+ deprecation_message = (
1132
+ f"the output_type {output_type} is outdated and has been set to `np`. Please make sure to set it to one of these instead: "
1133
+ "`pil`, `np`, `pt`, `latent`"
1134
+ )
1135
+ deprecate("Unsupported output_type", "1.0.0", deprecation_message, standard_warn=False)
1136
+ output_type = "np"
1137
+
1138
+ image = self._denormalize_conditionally(image, do_denormalize)
1139
+
1140
+ image = self.pt_to_numpy(image)
1141
+
1142
+ if output_type == "np":
1143
+ if image.shape[-1] == 6:
1144
+ image_depth = np.stack([self.rgblike_to_depthmap(im[:, :, 3:]) for im in image], axis=0)
1145
+ else:
1146
+ image_depth = image[:, :, :, 3:]
1147
+ return image[:, :, :, :3], image_depth
1148
+
1149
+ if output_type == "pil":
1150
+ return self.numpy_to_pil(image), self.numpy_to_depth(image)
1151
+ else:
1152
+ raise Exception(f"This type {output_type} is not supported")
1153
+
1154
+ def preprocess(
1155
+ self,
1156
+ rgb: torch.Tensor | PIL.Image.Image | np.ndarray,
1157
+ depth: torch.Tensor | PIL.Image.Image | np.ndarray,
1158
+ height: int | None = None,
1159
+ width: int | None = None,
1160
+ target_res: int | None = None,
1161
+ ) -> torch.Tensor:
1162
+ r"""
1163
+ Preprocess the image input. Accepted formats are PIL images, NumPy arrays, or PyTorch tensors.
1164
+
1165
+ Args:
1166
+ rgb (`torch.Tensor | PIL.Image.Image | np.ndarray`):
1167
+ The RGB input image, which can be a single image or a batch.
1168
+ depth (`torch.Tensor | PIL.Image.Image | np.ndarray`):
1169
+ The depth input image, which can be a single image or a batch.
1170
+ height (`int | None`, *optional*, defaults to `None`):
1171
+ The desired height of the processed image. If `None`, defaults to the height of the input image.
1172
+ width (`int | None`, *optional*, defaults to `None`):
1173
+ The desired width of the processed image. If `None`, defaults to the width of the input image.
1174
+ target_res (`int | None`, *optional*, defaults to `None`):
1175
+ Target resolution for resizing the images. If specified, overrides height and width.
1176
+
1177
+ Returns:
1178
+ `tuple[torch.Tensor, torch.Tensor]`:
1179
+ A tuple containing the processed RGB and depth images as PyTorch tensors.
1180
+ """
1181
+ supported_formats = (PIL.Image.Image, np.ndarray, torch.Tensor)
1182
+
1183
+ # Expand the missing dimension for 3-dimensional pytorch tensor or numpy array that represents grayscale image
1184
+ if self.config.do_convert_grayscale and isinstance(rgb, (torch.Tensor, np.ndarray)) and rgb.ndim == 3:
1185
+ raise Exception("This is not yet supported")
1186
+
1187
+ if isinstance(rgb, supported_formats):
1188
+ rgb = [rgb]
1189
+ depth = [depth]
1190
+ elif not (isinstance(rgb, list) and all(isinstance(i, supported_formats) for i in rgb)):
1191
+ raise ValueError(
1192
+ f"Input is in incorrect format: {[type(i) for i in rgb]}. Currently, we only support {', '.join(supported_formats)}"
1193
+ )
1194
+
1195
+ if isinstance(rgb[0], PIL.Image.Image):
1196
+ if self.config.do_convert_rgb:
1197
+ raise Exception("This is not yet supported")
1198
+ # rgb = [self.convert_to_rgb(i) for i in rgb]
1199
+ # depth = [self.convert_to_depth(i) for i in depth] #TODO define convert_to_depth
1200
+ if self.config.do_resize or target_res:
1201
+ height, width = self.get_default_height_width(rgb[0], height, width) if not target_res else target_res
1202
+ rgb = [self.resize(i, height, width) for i in rgb]
1203
+ depth = [self.resize(i, height, width) for i in depth]
1204
+ rgb = self.pil_to_numpy(rgb) # to np
1205
+ rgb = self.numpy_to_pt(rgb) # to pt
1206
+
1207
+ depth = self.depth_pil_to_numpy(depth) # to np
1208
+ depth = self.numpy_to_pt(depth) # to pt
1209
+
1210
+ elif isinstance(rgb[0], np.ndarray):
1211
+ rgb = np.concatenate(rgb, axis=0) if rgb[0].ndim == 4 else np.stack(rgb, axis=0)
1212
+ rgb = self.numpy_to_pt(rgb)
1213
+ height, width = self.get_default_height_width(rgb, height, width)
1214
+ if self.config.do_resize:
1215
+ rgb = self.resize(rgb, height, width)
1216
+
1217
+ depth = np.concatenate(depth, axis=0) if rgb[0].ndim == 4 else np.stack(depth, axis=0)
1218
+ depth = self.numpy_to_pt(depth)
1219
+ height, width = self.get_default_height_width(depth, height, width)
1220
+ if self.config.do_resize:
1221
+ depth = self.resize(depth, height, width)
1222
+
1223
+ elif isinstance(rgb[0], torch.Tensor):
1224
+ raise Exception("This is not yet supported")
1225
+ # rgb = torch.cat(rgb, axis=0) if rgb[0].ndim == 4 else torch.stack(rgb, axis=0)
1226
+
1227
+ # if self.config.do_convert_grayscale and rgb.ndim == 3:
1228
+ # rgb = rgb.unsqueeze(1)
1229
+
1230
+ # channel = rgb.shape[1]
1231
+
1232
+ # height, width = self.get_default_height_width(rgb, height, width)
1233
+ # if self.config.do_resize:
1234
+ # rgb = self.resize(rgb, height, width)
1235
+
1236
+ # depth = torch.cat(depth, axis=0) if depth[0].ndim == 4 else torch.stack(depth, axis=0)
1237
+
1238
+ # if self.config.do_convert_grayscale and depth.ndim == 3:
1239
+ # depth = depth.unsqueeze(1)
1240
+
1241
+ # channel = depth.shape[1]
1242
+ # # don't need any preprocess if the image is latents
1243
+ # if depth == 4:
1244
+ # return rgb, depth
1245
+
1246
+ # height, width = self.get_default_height_width(depth, height, width)
1247
+ # if self.config.do_resize:
1248
+ # depth = self.resize(depth, height, width)
1249
+ # expected range [0,1], normalize to [-1,1]
1250
+ do_normalize = self.config.do_normalize
1251
+ if rgb.min() < 0 and do_normalize:
1252
+ warnings.warn(
1253
+ "Passing `image` as torch tensor with value range in [-1,1] is deprecated. The expected value range for image tensor is [0,1] "
1254
+ f"when passing as pytorch tensor or numpy Array. You passed `image` with value range [{rgb.min()},{rgb.max()}]",
1255
+ FutureWarning,
1256
+ )
1257
+ do_normalize = False
1258
+
1259
+ if do_normalize:
1260
+ rgb = self.normalize(rgb)
1261
+ depth = self.normalize(depth)
1262
+
1263
+ if self.config.do_binarize:
1264
+ rgb = self.binarize(rgb)
1265
+ depth = self.binarize(depth)
1266
+
1267
+ return rgb, depth
1268
+
1269
+
1270
+ class IPAdapterMaskProcessor(VaeImageProcessor):
1271
+ """
1272
+ Image processor for IP Adapter image masks.
1273
+
1274
+ Args:
1275
+ do_resize (`bool`, *optional*, defaults to `True`):
1276
+ Whether to downscale the image's (height, width) dimensions to multiples of `vae_scale_factor`.
1277
+ vae_scale_factor (`int`, *optional*, defaults to `8`):
1278
+ VAE scale factor. If `do_resize` is `True`, the image is automatically resized to multiples of this factor.
1279
+ resample (`str`, *optional*, defaults to `lanczos`):
1280
+ Resampling filter to use when resizing the image.
1281
+ do_normalize (`bool`, *optional*, defaults to `False`):
1282
+ Whether to normalize the image to [-1,1].
1283
+ do_binarize (`bool`, *optional*, defaults to `True`):
1284
+ Whether to binarize the image to 0/1.
1285
+ do_convert_grayscale (`bool`, *optional*, defaults to be `True`):
1286
+ Whether to convert the images to grayscale format.
1287
+
1288
+ """
1289
+
1290
+ config_name = CONFIG_NAME
1291
+
1292
+ @register_to_config
1293
+ def __init__(
1294
+ self,
1295
+ do_resize: bool = True,
1296
+ vae_scale_factor: int = 8,
1297
+ resample: str = "lanczos",
1298
+ do_normalize: bool = False,
1299
+ do_binarize: bool = True,
1300
+ do_convert_grayscale: bool = True,
1301
+ ):
1302
+ super().__init__(
1303
+ do_resize=do_resize,
1304
+ vae_scale_factor=vae_scale_factor,
1305
+ resample=resample,
1306
+ do_normalize=do_normalize,
1307
+ do_binarize=do_binarize,
1308
+ do_convert_grayscale=do_convert_grayscale,
1309
+ )
1310
+
1311
+ @staticmethod
1312
+ def downsample(mask: torch.Tensor, batch_size: int, num_queries: int, value_embed_dim: int):
1313
+ """
1314
+ Downsamples the provided mask tensor to match the expected dimensions for scaled dot-product attention. If the
1315
+ aspect ratio of the mask does not match the aspect ratio of the output image, a warning is issued.
1316
+
1317
+ Args:
1318
+ mask (`torch.Tensor`):
1319
+ The input mask tensor generated with `IPAdapterMaskProcessor.preprocess()`.
1320
+ batch_size (`int`):
1321
+ The batch size.
1322
+ num_queries (`int`):
1323
+ The number of queries.
1324
+ value_embed_dim (`int`):
1325
+ The dimensionality of the value embeddings.
1326
+
1327
+ Returns:
1328
+ `torch.Tensor`:
1329
+ The downsampled mask tensor.
1330
+
1331
+ """
1332
+ o_h = mask.shape[1]
1333
+ o_w = mask.shape[2]
1334
+ ratio = o_w / o_h
1335
+ mask_h = int(math.sqrt(num_queries / ratio))
1336
+ mask_h = int(mask_h) + int((num_queries % int(mask_h)) != 0)
1337
+ mask_w = num_queries // mask_h
1338
+
1339
+ mask_downsample = F.interpolate(mask.unsqueeze(0), size=(mask_h, mask_w), mode="bicubic").squeeze(0)
1340
+
1341
+ # Repeat batch_size times
1342
+ if mask_downsample.shape[0] < batch_size:
1343
+ mask_downsample = mask_downsample.repeat(batch_size, 1, 1)
1344
+
1345
+ mask_downsample = mask_downsample.view(mask_downsample.shape[0], -1)
1346
+
1347
+ downsampled_area = mask_h * mask_w
1348
+ # If the output image and the mask do not have the same aspect ratio, tensor shapes will not match
1349
+ # Pad tensor if downsampled_mask.shape[1] is smaller than num_queries
1350
+ if downsampled_area < num_queries:
1351
+ warnings.warn(
1352
+ "The aspect ratio of the mask does not match the aspect ratio of the output image. "
1353
+ "Please update your masks or adjust the output size for optimal performance.",
1354
+ UserWarning,
1355
+ )
1356
+ mask_downsample = F.pad(mask_downsample, (0, num_queries - mask_downsample.shape[1]), value=0.0)
1357
+ # Discard last embeddings if downsampled_mask.shape[1] is bigger than num_queries
1358
+ if downsampled_area > num_queries:
1359
+ warnings.warn(
1360
+ "The aspect ratio of the mask does not match the aspect ratio of the output image. "
1361
+ "Please update your masks or adjust the output size for optimal performance.",
1362
+ UserWarning,
1363
+ )
1364
+ mask_downsample = mask_downsample[:, :num_queries]
1365
+
1366
+ # Repeat last dimension to match SDPA output shape
1367
+ mask_downsample = mask_downsample.view(mask_downsample.shape[0], mask_downsample.shape[1], 1).repeat(
1368
+ 1, 1, value_embed_dim
1369
+ )
1370
+
1371
+ return mask_downsample
1372
+
1373
+
1374
+ class PixArtImageProcessor(VaeImageProcessor):
1375
+ """
1376
+ Image processor for PixArt image resize and crop.
1377
+
1378
+ Args:
1379
+ do_resize (`bool`, *optional*, defaults to `True`):
1380
+ Whether to downscale the image's (height, width) dimensions to multiples of `vae_scale_factor`. Can accept
1381
+ `height` and `width` arguments from [`image_processor.VaeImageProcessor.preprocess`] method.
1382
+ vae_scale_factor (`int`, *optional*, defaults to `8`):
1383
+ VAE scale factor. If `do_resize` is `True`, the image is automatically resized to multiples of this factor.
1384
+ resample (`str`, *optional*, defaults to `lanczos`):
1385
+ Resampling filter to use when resizing the image.
1386
+ do_normalize (`bool`, *optional*, defaults to `True`):
1387
+ Whether to normalize the image to [-1,1].
1388
+ do_binarize (`bool`, *optional*, defaults to `False`):
1389
+ Whether to binarize the image to 0/1.
1390
+ do_convert_rgb (`bool`, *optional*, defaults to be `False`):
1391
+ Whether to convert the images to RGB format.
1392
+ do_convert_grayscale (`bool`, *optional*, defaults to be `False`):
1393
+ Whether to convert the images to grayscale format.
1394
+ """
1395
+
1396
+ @register_to_config
1397
+ def __init__(
1398
+ self,
1399
+ do_resize: bool = True,
1400
+ vae_scale_factor: int = 8,
1401
+ resample: str = "lanczos",
1402
+ do_normalize: bool = True,
1403
+ do_binarize: bool = False,
1404
+ do_convert_grayscale: bool = False,
1405
+ ):
1406
+ super().__init__(
1407
+ do_resize=do_resize,
1408
+ vae_scale_factor=vae_scale_factor,
1409
+ resample=resample,
1410
+ do_normalize=do_normalize,
1411
+ do_binarize=do_binarize,
1412
+ do_convert_grayscale=do_convert_grayscale,
1413
+ )
1414
+
1415
+ @staticmethod
1416
+ def classify_height_width_bin(height: int, width: int, ratios: dict) -> tuple[int, int]:
1417
+ r"""
1418
+ Returns the binned height and width based on the aspect ratio.
1419
+
1420
+ Args:
1421
+ height (`int`): The height of the image.
1422
+ width (`int`): The width of the image.
1423
+ ratios (`dict`): A dictionary where keys are aspect ratios and values are tuples of (height, width).
1424
+
1425
+ Returns:
1426
+ `tuple[int, int]`: The closest binned height and width.
1427
+ """
1428
+ ar = float(height / width)
1429
+ closest_ratio = min(ratios.keys(), key=lambda ratio: abs(float(ratio) - ar))
1430
+ default_hw = ratios[closest_ratio]
1431
+ return int(default_hw[0]), int(default_hw[1])
1432
+
1433
+ @staticmethod
1434
+ def resize_and_crop_tensor(samples: torch.Tensor, new_width: int, new_height: int) -> torch.Tensor:
1435
+ r"""
1436
+ Resizes and crops a tensor of images to the specified dimensions.
1437
+
1438
+ Args:
1439
+ samples (`torch.Tensor`):
1440
+ A tensor of shape (N, C, H, W) where N is the batch size, C is the number of channels, H is the height,
1441
+ and W is the width.
1442
+ new_width (`int`): The desired width of the output images.
1443
+ new_height (`int`): The desired height of the output images.
1444
+
1445
+ Returns:
1446
+ `torch.Tensor`: A tensor containing the resized and cropped images.
1447
+ """
1448
+ orig_height, orig_width = samples.shape[2], samples.shape[3]
1449
+
1450
+ # Check if resizing is needed
1451
+ if orig_height != new_height or orig_width != new_width:
1452
+ ratio = max(new_height / orig_height, new_width / orig_width)
1453
+ resized_width = int(orig_width * ratio)
1454
+ resized_height = int(orig_height * ratio)
1455
+
1456
+ # Resize
1457
+ samples = F.interpolate(
1458
+ samples, size=(resized_height, resized_width), mode="bilinear", align_corners=False
1459
+ )
1460
+
1461
+ # Center Crop
1462
+ start_x = (resized_width - new_width) // 2
1463
+ end_x = start_x + new_width
1464
+ start_y = (resized_height - new_height) // 2
1465
+ end_y = start_y + new_height
1466
+ samples = samples[:, :, start_y:end_y, start_x:end_x]
1467
+
1468
+ return samples
third_party/diffusers/src/diffusers/optimization.py ADDED
@@ -0,0 +1,360 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ # Copyright 2025 The HuggingFace Inc. team.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """PyTorch optimization for diffusion models."""
16
+
17
+ import math
18
+ from enum import Enum
19
+
20
+ from torch.optim import Optimizer
21
+ from torch.optim.lr_scheduler import LambdaLR
22
+
23
+ from .utils import logging
24
+
25
+
26
+ logger = logging.get_logger(__name__)
27
+
28
+
29
+ class SchedulerType(Enum):
30
+ LINEAR = "linear"
31
+ COSINE = "cosine"
32
+ COSINE_WITH_RESTARTS = "cosine_with_restarts"
33
+ POLYNOMIAL = "polynomial"
34
+ CONSTANT = "constant"
35
+ CONSTANT_WITH_WARMUP = "constant_with_warmup"
36
+ PIECEWISE_CONSTANT = "piecewise_constant"
37
+
38
+
39
+ def get_constant_schedule(optimizer: Optimizer, last_epoch: int = -1) -> LambdaLR:
40
+ """
41
+ Create a schedule with a constant learning rate, using the learning rate set in optimizer.
42
+
43
+ Args:
44
+ optimizer ([`~torch.optim.Optimizer`]):
45
+ The optimizer for which to schedule the learning rate.
46
+ last_epoch (`int`, *optional*, defaults to -1):
47
+ The index of the last epoch when resuming training.
48
+
49
+ Return:
50
+ `torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
51
+ """
52
+ return LambdaLR(optimizer, lambda _: 1, last_epoch=last_epoch)
53
+
54
+
55
+ def get_constant_schedule_with_warmup(optimizer: Optimizer, num_warmup_steps: int, last_epoch: int = -1) -> LambdaLR:
56
+ """
57
+ Create a schedule with a constant learning rate preceded by a warmup period during which the learning rate
58
+ increases linearly between 0 and the initial lr set in the optimizer.
59
+
60
+ Args:
61
+ optimizer ([`~torch.optim.Optimizer`]):
62
+ The optimizer for which to schedule the learning rate.
63
+ num_warmup_steps (`int`):
64
+ The number of steps for the warmup phase.
65
+ last_epoch (`int`, *optional*, defaults to -1):
66
+ The index of the last epoch when resuming training.
67
+
68
+ Return:
69
+ `torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
70
+ """
71
+
72
+ def lr_lambda(current_step: int):
73
+ if current_step < num_warmup_steps:
74
+ return float(current_step) / float(max(1.0, num_warmup_steps))
75
+ return 1.0
76
+
77
+ return LambdaLR(optimizer, lr_lambda, last_epoch=last_epoch)
78
+
79
+
80
+ def get_piecewise_constant_schedule(optimizer: Optimizer, step_rules: str, last_epoch: int = -1) -> LambdaLR:
81
+ """
82
+ Create a schedule with a constant learning rate, using the learning rate set in optimizer.
83
+
84
+ Args:
85
+ optimizer ([`~torch.optim.Optimizer`]):
86
+ The optimizer for which to schedule the learning rate.
87
+ step_rules (`string`):
88
+ The rules for the learning rate. ex: rule_steps="1:10,0.1:20,0.01:30,0.005" it means that the learning rate
89
+ if multiple 1 for the first 10 steps, multiple 0.1 for the next 20 steps, multiple 0.01 for the next 30
90
+ steps and multiple 0.005 for the other steps.
91
+ last_epoch (`int`, *optional*, defaults to -1):
92
+ The index of the last epoch when resuming training.
93
+
94
+ Return:
95
+ `torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
96
+ """
97
+
98
+ rules_dict = {}
99
+ rule_list = step_rules.split(",")
100
+ for rule_str in rule_list[:-1]:
101
+ value_str, steps_str = rule_str.split(":")
102
+ steps = int(steps_str)
103
+ value = float(value_str)
104
+ rules_dict[steps] = value
105
+ last_lr_multiple = float(rule_list[-1])
106
+
107
+ def create_rules_function(rules_dict, last_lr_multiple):
108
+ def rule_func(steps: int) -> float:
109
+ sorted_steps = sorted(rules_dict.keys())
110
+ for i, sorted_step in enumerate(sorted_steps):
111
+ if steps < sorted_step:
112
+ return rules_dict[sorted_steps[i]]
113
+ return last_lr_multiple
114
+
115
+ return rule_func
116
+
117
+ rules_func = create_rules_function(rules_dict, last_lr_multiple)
118
+
119
+ return LambdaLR(optimizer, rules_func, last_epoch=last_epoch)
120
+
121
+
122
+ def get_linear_schedule_with_warmup(
123
+ optimizer: Optimizer, num_warmup_steps: int, num_training_steps: int, last_epoch: int = -1
124
+ ) -> LambdaLR:
125
+ """
126
+ Create a schedule with a learning rate that decreases linearly from the initial lr set in the optimizer to 0, after
127
+ a warmup period during which it increases linearly from 0 to the initial lr set in the optimizer.
128
+
129
+ Args:
130
+ optimizer ([`~torch.optim.Optimizer`]):
131
+ The optimizer for which to schedule the learning rate.
132
+ num_warmup_steps (`int`):
133
+ The number of steps for the warmup phase.
134
+ num_training_steps (`int`):
135
+ The total number of training steps.
136
+ last_epoch (`int`, *optional*, defaults to -1):
137
+ The index of the last epoch when resuming training.
138
+
139
+ Return:
140
+ `torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
141
+ """
142
+
143
+ def lr_lambda(current_step: int):
144
+ if current_step < num_warmup_steps:
145
+ return float(current_step) / float(max(1, num_warmup_steps))
146
+ return max(
147
+ 0.0, float(num_training_steps - current_step) / float(max(1, num_training_steps - num_warmup_steps))
148
+ )
149
+
150
+ return LambdaLR(optimizer, lr_lambda, last_epoch)
151
+
152
+
153
+ def get_cosine_schedule_with_warmup(
154
+ optimizer: Optimizer, num_warmup_steps: int, num_training_steps: int, num_cycles: float = 0.5, last_epoch: int = -1
155
+ ) -> LambdaLR:
156
+ """
157
+ Create a schedule with a learning rate that decreases following the values of the cosine function between the
158
+ initial lr set in the optimizer to 0, after a warmup period during which it increases linearly between 0 and the
159
+ initial lr set in the optimizer.
160
+
161
+ Args:
162
+ optimizer ([`~torch.optim.Optimizer`]):
163
+ The optimizer for which to schedule the learning rate.
164
+ num_warmup_steps (`int`):
165
+ The number of steps for the warmup phase.
166
+ num_training_steps (`int`):
167
+ The total number of training steps.
168
+ num_periods (`float`, *optional*, defaults to 0.5):
169
+ The number of periods of the cosine function in a schedule (the default is to just decrease from the max
170
+ value to 0 following a half-cosine).
171
+ last_epoch (`int`, *optional*, defaults to -1):
172
+ The index of the last epoch when resuming training.
173
+
174
+ Return:
175
+ `torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
176
+ """
177
+
178
+ def lr_lambda(current_step):
179
+ if current_step < num_warmup_steps:
180
+ return float(current_step) / float(max(1, num_warmup_steps))
181
+ progress = float(current_step - num_warmup_steps) / float(max(1, num_training_steps - num_warmup_steps))
182
+ return max(0.0, 0.5 * (1.0 + math.cos(math.pi * float(num_cycles) * 2.0 * progress)))
183
+
184
+ return LambdaLR(optimizer, lr_lambda, last_epoch)
185
+
186
+
187
+ def get_cosine_with_hard_restarts_schedule_with_warmup(
188
+ optimizer: Optimizer, num_warmup_steps: int, num_training_steps: int, num_cycles: int = 1, last_epoch: int = -1
189
+ ) -> LambdaLR:
190
+ """
191
+ Create a schedule with a learning rate that decreases following the values of the cosine function between the
192
+ initial lr set in the optimizer to 0, with several hard restarts, after a warmup period during which it increases
193
+ linearly between 0 and the initial lr set in the optimizer.
194
+
195
+ Args:
196
+ optimizer ([`~torch.optim.Optimizer`]):
197
+ The optimizer for which to schedule the learning rate.
198
+ num_warmup_steps (`int`):
199
+ The number of steps for the warmup phase.
200
+ num_training_steps (`int`):
201
+ The total number of training steps.
202
+ num_cycles (`int`, *optional*, defaults to 1):
203
+ The number of hard restarts to use.
204
+ last_epoch (`int`, *optional*, defaults to -1):
205
+ The index of the last epoch when resuming training.
206
+
207
+ Return:
208
+ `torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
209
+ """
210
+
211
+ def lr_lambda(current_step):
212
+ if current_step < num_warmup_steps:
213
+ return float(current_step) / float(max(1, num_warmup_steps))
214
+ progress = float(current_step - num_warmup_steps) / float(max(1, num_training_steps - num_warmup_steps))
215
+ if progress >= 1.0:
216
+ return 0.0
217
+ return max(0.0, 0.5 * (1.0 + math.cos(math.pi * ((float(num_cycles) * progress) % 1.0))))
218
+
219
+ return LambdaLR(optimizer, lr_lambda, last_epoch)
220
+
221
+
222
+ def get_polynomial_decay_schedule_with_warmup(
223
+ optimizer: Optimizer,
224
+ num_warmup_steps: int,
225
+ num_training_steps: int,
226
+ lr_end: float = 1e-7,
227
+ power: float = 1.0,
228
+ last_epoch: int = -1,
229
+ ) -> LambdaLR:
230
+ """
231
+ Create a schedule with a learning rate that decreases as a polynomial decay from the initial lr set in the
232
+ optimizer to end lr defined by *lr_end*, after a warmup period during which it increases linearly from 0 to the
233
+ initial lr set in the optimizer.
234
+
235
+ Args:
236
+ optimizer ([`~torch.optim.Optimizer`]):
237
+ The optimizer for which to schedule the learning rate.
238
+ num_warmup_steps (`int`):
239
+ The number of steps for the warmup phase.
240
+ num_training_steps (`int`):
241
+ The total number of training steps.
242
+ lr_end (`float`, *optional*, defaults to 1e-7):
243
+ The end LR.
244
+ power (`float`, *optional*, defaults to 1.0):
245
+ Power factor.
246
+ last_epoch (`int`, *optional*, defaults to -1):
247
+ The index of the last epoch when resuming training.
248
+
249
+ Note: *power* defaults to 1.0 as in the fairseq implementation, which in turn is based on the original BERT
250
+ implementation at
251
+ https://github.com/google-research/bert/blob/f39e881b169b9d53bea03d2d341b31707a6c052b/optimization.py#L37
252
+
253
+ Return:
254
+ `torch.optim.lr_scheduler.LambdaLR` with the appropriate schedule.
255
+
256
+ """
257
+
258
+ lr_init = optimizer.defaults["lr"]
259
+ if not (lr_init > lr_end):
260
+ raise ValueError(f"lr_end ({lr_end}) must be smaller than initial lr ({lr_init})")
261
+
262
+ def lr_lambda(current_step: int):
263
+ if current_step < num_warmup_steps:
264
+ return float(current_step) / float(max(1, num_warmup_steps))
265
+ elif current_step > num_training_steps:
266
+ return lr_end / lr_init # as LambdaLR multiplies by lr_init
267
+ else:
268
+ lr_range = lr_init - lr_end
269
+ decay_steps = num_training_steps - num_warmup_steps
270
+ pct_remaining = 1 - (current_step - num_warmup_steps) / decay_steps
271
+ decay = lr_range * pct_remaining**power + lr_end
272
+ return decay / lr_init # as LambdaLR multiplies by lr_init
273
+
274
+ return LambdaLR(optimizer, lr_lambda, last_epoch)
275
+
276
+
277
+ TYPE_TO_SCHEDULER_FUNCTION = {
278
+ SchedulerType.LINEAR: get_linear_schedule_with_warmup,
279
+ SchedulerType.COSINE: get_cosine_schedule_with_warmup,
280
+ SchedulerType.COSINE_WITH_RESTARTS: get_cosine_with_hard_restarts_schedule_with_warmup,
281
+ SchedulerType.POLYNOMIAL: get_polynomial_decay_schedule_with_warmup,
282
+ SchedulerType.CONSTANT: get_constant_schedule,
283
+ SchedulerType.CONSTANT_WITH_WARMUP: get_constant_schedule_with_warmup,
284
+ SchedulerType.PIECEWISE_CONSTANT: get_piecewise_constant_schedule,
285
+ }
286
+
287
+
288
+ def get_scheduler(
289
+ name: str | SchedulerType,
290
+ optimizer: Optimizer,
291
+ step_rules: str | None = None,
292
+ num_warmup_steps: int | None = None,
293
+ num_training_steps: int | None = None,
294
+ num_cycles: int = 1,
295
+ power: float = 1.0,
296
+ last_epoch: int = -1,
297
+ ) -> LambdaLR:
298
+ """
299
+ Unified API to get any scheduler from its name.
300
+
301
+ Args:
302
+ name (`str` or `SchedulerType`):
303
+ The name of the scheduler to use.
304
+ optimizer (`torch.optim.Optimizer`):
305
+ The optimizer that will be used during training.
306
+ step_rules (`str`, *optional*):
307
+ A string representing the step rules to use. This is only used by the `PIECEWISE_CONSTANT` scheduler.
308
+ num_warmup_steps (`int`, *optional*):
309
+ The number of warmup steps to do. This is not required by all schedulers (hence the argument being
310
+ optional), the function will raise an error if it's unset and the scheduler type requires it.
311
+ num_training_steps (`int``, *optional*):
312
+ The number of training steps to do. This is not required by all schedulers (hence the argument being
313
+ optional), the function will raise an error if it's unset and the scheduler type requires it.
314
+ num_cycles (`int`, *optional*):
315
+ The number of hard restarts used in `COSINE_WITH_RESTARTS` scheduler.
316
+ power (`float`, *optional*, defaults to 1.0):
317
+ Power factor. See `POLYNOMIAL` scheduler
318
+ last_epoch (`int`, *optional*, defaults to -1):
319
+ The index of the last epoch when resuming training.
320
+ """
321
+ name = SchedulerType(name)
322
+ schedule_func = TYPE_TO_SCHEDULER_FUNCTION[name]
323
+ if name == SchedulerType.CONSTANT:
324
+ return schedule_func(optimizer, last_epoch=last_epoch)
325
+
326
+ if name == SchedulerType.PIECEWISE_CONSTANT:
327
+ return schedule_func(optimizer, step_rules=step_rules, last_epoch=last_epoch)
328
+
329
+ # All other schedulers require `num_warmup_steps`
330
+ if num_warmup_steps is None:
331
+ raise ValueError(f"{name} requires `num_warmup_steps`, please provide that argument.")
332
+
333
+ if name == SchedulerType.CONSTANT_WITH_WARMUP:
334
+ return schedule_func(optimizer, num_warmup_steps=num_warmup_steps, last_epoch=last_epoch)
335
+
336
+ # All other schedulers require `num_training_steps`
337
+ if num_training_steps is None:
338
+ raise ValueError(f"{name} requires `num_training_steps`, please provide that argument.")
339
+
340
+ if name == SchedulerType.COSINE_WITH_RESTARTS:
341
+ return schedule_func(
342
+ optimizer,
343
+ num_warmup_steps=num_warmup_steps,
344
+ num_training_steps=num_training_steps,
345
+ num_cycles=num_cycles,
346
+ last_epoch=last_epoch,
347
+ )
348
+
349
+ if name == SchedulerType.POLYNOMIAL:
350
+ return schedule_func(
351
+ optimizer,
352
+ num_warmup_steps=num_warmup_steps,
353
+ num_training_steps=num_training_steps,
354
+ power=power,
355
+ last_epoch=last_epoch,
356
+ )
357
+
358
+ return schedule_func(
359
+ optimizer, num_warmup_steps=num_warmup_steps, num_training_steps=num_training_steps, last_epoch=last_epoch
360
+ )
third_party/diffusers/src/diffusers/pipelines/README.md ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🧨 Diffusers Pipelines
2
+
3
+ Pipelines provide a simple way to run state-of-the-art diffusion models in inference.
4
+ Most diffusion systems consist of multiple independently-trained models and highly adaptable scheduler
5
+ components - all of which are needed to have a functioning end-to-end diffusion system.
6
+
7
+ As an example, [Stable Diffusion](https://huggingface.co/blog/stable_diffusion) has three independently trained models:
8
+ - [Autoencoder](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/models/vae.py#L392)
9
+ - [Conditional Unet](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/models/unet_2d_condition.py#L12)
10
+ - [CLIP text encoder](https://huggingface.co/docs/transformers/main/en/model_doc/clip#transformers.CLIPTextModel)
11
+ - a scheduler component, [scheduler](https://github.com/huggingface/diffusers/blob/main/src/diffusers/schedulers/scheduling_pndm.py),
12
+ - a [CLIPImageProcessor](https://huggingface.co/docs/transformers/main/en/model_doc/clip#transformers.CLIPImageProcessor),
13
+ - as well as a [safety checker](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion/safety_checker.py).
14
+ All of these components are necessary to run stable diffusion in inference even though they were trained
15
+ or created independently from each other.
16
+
17
+ To that end, we strive to offer all open-sourced, state-of-the-art diffusion system under a unified API.
18
+ More specifically, we strive to provide pipelines that
19
+ - 1. can load the officially published weights and yield 1-to-1 the same outputs as the original implementation according to the corresponding paper (*e.g.* [LDMTextToImagePipeline](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines/latent_diffusion), uses the officially released weights of [High-Resolution Image Synthesis with Latent Diffusion Models](https://huggingface.co/papers/2112.10752)),
20
+ - 2. have a simple user interface to run the model in inference (see the [Pipelines API](#pipelines-api) section),
21
+ - 3. are easy to understand with code that is self-explanatory and can be read along-side the official paper (see [Pipelines summary](#pipelines-summary)),
22
+ - 4. can easily be contributed by the community (see the [Contribution](#contribution) section).
23
+
24
+ **Note** that pipelines do not (and should not) offer any training functionality.
25
+ If you are looking for *official* training examples, please have a look at [examples](https://github.com/huggingface/diffusers/tree/main/examples).
26
+
27
+
28
+ ## Pipelines Summary
29
+
30
+ The following table summarizes all officially supported pipelines, their corresponding paper, and if
31
+ available a colab notebook to directly try them out.
32
+
33
+ | Pipeline | Source | Tasks | Colab
34
+ |-------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------|:---:|:---:|
35
+ | [dance diffusion](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/dance_diffusion) | [**Dance Diffusion**](https://github.com/Harmonai-org/sample-generator) | *Unconditional Audio Generation* |
36
+ | [ddpm](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/ddpm) | [**Denoising Diffusion Probabilistic Models**](https://huggingface.co/papers/2006.11239) | *Unconditional Image Generation* |
37
+ | [ddim](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/ddim) | [**Denoising Diffusion Implicit Models**](https://huggingface.co/papers/2010.02502) | *Unconditional Image Generation* | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/training_example.ipynb)
38
+ | [latent_diffusion](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/latent_diffusion) | [**High-Resolution Image Synthesis with Latent Diffusion Models**](https://huggingface.co/papers/2112.10752) | *Text-to-Image Generation* |
39
+ | [latent_diffusion_uncond](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/latent_diffusion_uncond) | [**High-Resolution Image Synthesis with Latent Diffusion Models**](https://huggingface.co/papers/2112.10752) | *Unconditional Image Generation* |
40
+ | [pndm](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/pndm) | [**Pseudo Numerical Methods for Diffusion Models on Manifolds**](https://huggingface.co/papers/2202.09778) | *Unconditional Image Generation* |
41
+ | [score_sde_ve](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/score_sde_ve) | [**Score-Based Generative Modeling through Stochastic Differential Equations**](https://openreview.net/forum?id=PxTIG12RRHS) | *Unconditional Image Generation* |
42
+ | [score_sde_vp](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/score_sde_vp) | [**Score-Based Generative Modeling through Stochastic Differential Equations**](https://openreview.net/forum?id=PxTIG12RRHS) | *Unconditional Image Generation* |
43
+ | [stable_diffusion](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion) | [**Stable Diffusion**](https://stability.ai/blog/stable-diffusion-public-release) | *Text-to-Image Generation* | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/stable_diffusion.ipynb)
44
+ | [stable_diffusion](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion) | [**Stable Diffusion**](https://stability.ai/blog/stable-diffusion-public-release) | *Image-to-Image Text-Guided Generation* | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/image_2_image_using_diffusers.ipynb)
45
+ | [stable_diffusion](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion) | [**Stable Diffusion**](https://stability.ai/blog/stable-diffusion-public-release) | *Text-Guided Image Inpainting* | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/in_painting_with_stable_diffusion_using_diffusers.ipynb)
46
+ | [stochastic_karras_ve](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stochastic_karras_ve) | [**Elucidating the Design Space of Diffusion-Based Generative Models**](https://huggingface.co/papers/2206.00364) | *Unconditional Image Generation* |
47
+
48
+ **Note**: Pipelines are simple examples of how to play around with the diffusion systems as described in the corresponding papers.
49
+ However, most of them can be adapted to use different scheduler components or even different model components. Some pipeline examples are shown in the [Examples](#examples) below.
50
+
51
+ ## Pipelines API
52
+
53
+ Diffusion models often consist of multiple independently-trained models or other previously existing components.
54
+
55
+
56
+ Each model has been trained independently on a different task and the scheduler can easily be swapped out and replaced with a different one.
57
+ During inference, we however want to be able to easily load all components and use them in inference - even if one component, *e.g.* CLIP's text encoder, originates from a different library, such as [Transformers](https://github.com/huggingface/transformers). To that end, all pipelines provide the following functionality:
58
+
59
+ - [`from_pretrained` method](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/pipeline_utils.py#L139) that accepts a Hugging Face Hub repository id, *e.g.* [stable-diffusion-v1-5/stable-diffusion-v1-5](https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5) or a path to a local directory, *e.g.*
60
+ "./stable-diffusion". To correctly retrieve which models and components should be loaded, one has to provide a `model_index.json` file, *e.g.* [stable-diffusion-v1-5/stable-diffusion-v1-5/model_index.json](https://huggingface.co/stable-diffusion-v1-5/stable-diffusion-v1-5/blob/main/model_index.json), which defines all components that should be
61
+ loaded into the pipelines. More specifically, for each model/component one needs to define the format `<name>: ["<library>", "<class name>"]`. `<name>` is the attribute name given to the loaded instance of `<class name>` which can be found in the library or pipeline folder called `"<library>"`.
62
+ - [`save_pretrained`](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/pipeline_utils.py#L90) that accepts a local path, *e.g.* `./stable-diffusion` under which all models/components of the pipeline will be saved. For each component/model a folder is created inside the local path that is named after the given attribute name, *e.g.* `./stable_diffusion/unet`.
63
+ In addition, a `model_index.json` file is created at the root of the local path, *e.g.* `./stable_diffusion/model_index.json` so that the complete pipeline can again be instantiated
64
+ from the local path.
65
+ - [`to`](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/pipeline_utils.py#L118) which accepts a `string` or `torch.device` to move all models that are of type `torch.nn.Module` to the passed device. The behavior is fully analogous to [PyTorch's `to` method](https://pytorch.org/docs/stable/generated/torch.nn.Module.html#torch.nn.Module.to).
66
+ - [`__call__`] method to use the pipeline in inference. `__call__` defines inference logic of the pipeline and should ideally encompass all aspects of it, from pre-processing to forwarding tensors to the different models and schedulers, as well as post-processing. The API of the `__call__` method can strongly vary from pipeline to pipeline. *E.g.* a text-to-image pipeline, such as [`StableDiffusionPipeline`](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines/stable_diffusion/pipeline_stable_diffusion.py) should accept among other things the text prompt to generate the image. A pure image generation pipeline, such as [DDPMPipeline](https://github.com/huggingface/diffusers/tree/main/src/diffusers/pipelines/ddpm) on the other hand can be run without providing any inputs. To better understand what inputs can be adapted for
67
+ each pipeline, one should look directly into the respective pipeline.
68
+
69
+ **Note**: All pipelines have PyTorch's autograd disabled by decorating the `__call__` method with a [`torch.no_grad`](https://pytorch.org/docs/stable/generated/torch.no_grad.html) decorator because pipelines should
70
+ not be used for training. If you want to store the gradients during the forward pass, we recommend writing your own pipeline, see also our [community-examples](https://github.com/huggingface/diffusers/tree/main/examples/community)
71
+
72
+ ## Contribution
73
+
74
+ We are more than happy about any contribution to the officially supported pipelines 🤗. We aspire
75
+ all of our pipelines to be **self-contained**, **easy-to-tweak**, **beginner-friendly** and for **one-purpose-only**.
76
+
77
+ - **Self-contained**: A pipeline shall be as self-contained as possible. More specifically, this means that all functionality should be either directly defined in the pipeline file itself, should be inherited from (and only from) the [`DiffusionPipeline` class](https://github.com/huggingface/diffusers/blob/5cbed8e0d157f65d3ddc2420dfd09f2df630e978/src/diffusers/pipeline_utils.py#L56) or be directly attached to the model and scheduler components of the pipeline.
78
+ - **Easy-to-use**: Pipelines should be extremely easy to use - one should be able to load the pipeline and
79
+ use it for its designated task, *e.g.* text-to-image generation, in just a couple of lines of code. Most
80
+ logic including pre-processing, an unrolled diffusion loop, and post-processing should all happen inside the `__call__` method.
81
+ - **Easy-to-tweak**: Certain pipelines will not be able to handle all use cases and tasks that you might like them to. If you want to use a certain pipeline for a specific use case that is not yet supported, you might have to copy the pipeline file and tweak the code to your needs. We try to make the pipeline code as readable as possible so that each part –from pre-processing to diffusing to post-processing– can easily be adapted. If you would like the community to benefit from your customized pipeline, we would love to see a contribution to our [community-examples](https://github.com/huggingface/diffusers/tree/main/examples/community). If you feel that an important pipeline should be part of the official pipelines but isn't, a contribution to the [official pipelines](https://github.com/huggingface/diffusers/blob/main/src/diffusers/pipelines) would be even better.
82
+ - **One-purpose-only**: Pipelines should be used for one task and one task only. Even if two tasks are very similar from a modeling point of view, *e.g.* image2image translation and in-painting, pipelines shall be used for one task only to keep them *easy-to-tweak* and *readable*.
83
+
84
+ ## Examples
85
+
86
+ ### Text-to-Image generation with Stable Diffusion
87
+
88
+ ```python
89
+ # make sure you're logged in with `hf auth login`
90
+ from diffusers import StableDiffusionPipeline, LMSDiscreteScheduler
91
+
92
+ pipe = StableDiffusionPipeline.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5")
93
+ pipe = pipe.to("cuda")
94
+
95
+ prompt = "a photo of an astronaut riding a horse on mars"
96
+ image = pipe(prompt).images[0]
97
+
98
+ image.save("astronaut_rides_horse.png")
99
+ ```
100
+
101
+ ### Image-to-Image text-guided generation with Stable Diffusion
102
+
103
+ The `StableDiffusionImg2ImgPipeline` lets you pass a text prompt and an initial image to condition the generation of new images.
104
+
105
+ ```python
106
+ import requests
107
+ from PIL import Image
108
+ from io import BytesIO
109
+
110
+ from diffusers import StableDiffusionImg2ImgPipeline
111
+
112
+ # load the pipeline
113
+ device = "cuda"
114
+ pipe = StableDiffusionImg2ImgPipeline.from_pretrained(
115
+ "stable-diffusion-v1-5/stable-diffusion-v1-5",
116
+ torch_dtype=torch.float16,
117
+ ).to(device)
118
+
119
+ # let's download an initial image
120
+ url = "https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg"
121
+
122
+ response = requests.get(url)
123
+ init_image = Image.open(BytesIO(response.content)).convert("RGB")
124
+ init_image = init_image.resize((768, 512))
125
+
126
+ prompt = "A fantasy landscape, trending on artstation"
127
+
128
+ images = pipe(prompt=prompt, image=init_image, strength=0.75, guidance_scale=7.5).images
129
+
130
+ images[0].save("fantasy_landscape.png")
131
+ ```
132
+ You can also run this example on colab [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/image_2_image_using_diffusers.ipynb)
133
+
134
+ ### Tweak prompts reusing seeds and latents
135
+
136
+ You can generate your own latents to reproduce results, or tweak your prompt on a specific result you liked. [This notebook](https://github.com/pcuenca/diffusers-examples/blob/main/notebooks/stable-diffusion-seeds.ipynb) shows how to do it step by step. You can also run it in Google Colab [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/pcuenca/diffusers-examples/blob/main/notebooks/stable-diffusion-seeds.ipynb).
137
+
138
+
139
+ ### In-painting using Stable Diffusion
140
+
141
+ The `StableDiffusionInpaintPipeline` lets you edit specific parts of an image by providing a mask and text prompt.
142
+
143
+ ```python
144
+ import PIL
145
+ import requests
146
+ import torch
147
+ from io import BytesIO
148
+
149
+ from diffusers import StableDiffusionInpaintPipeline
150
+
151
+ def download_image(url):
152
+ response = requests.get(url)
153
+ return PIL.Image.open(BytesIO(response.content)).convert("RGB")
154
+
155
+ img_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo.png"
156
+ mask_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo_mask.png"
157
+
158
+ init_image = download_image(img_url).resize((512, 512))
159
+ mask_image = download_image(mask_url).resize((512, 512))
160
+
161
+ pipe = StableDiffusionInpaintPipeline.from_pretrained(
162
+ "stable-diffusion-v1-5/stable-diffusion-inpainting",
163
+ torch_dtype=torch.float16,
164
+ )
165
+ pipe = pipe.to("cuda")
166
+
167
+ prompt = "Face of a yellow cat, high resolution, sitting on a park bench"
168
+ image = pipe(prompt=prompt, image=init_image, mask_image=mask_image).images[0]
169
+ ```
170
+
171
+ You can also run this example on colab [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/huggingface/notebooks/blob/main/diffusers/in_painting_with_stable_diffusion_using_diffusers.ipynb)
third_party/diffusers/src/diffusers/pipelines/__init__.py ADDED
@@ -0,0 +1,963 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import TYPE_CHECKING
2
+
3
+ from ..utils import (
4
+ DIFFUSERS_SLOW_IMPORT,
5
+ OptionalDependencyNotAvailable,
6
+ _LazyModule,
7
+ get_objects_from_module,
8
+ is_flax_available,
9
+ is_librosa_available,
10
+ is_note_seq_available,
11
+ is_onnx_available,
12
+ is_opencv_available,
13
+ is_sentencepiece_available,
14
+ is_torch_available,
15
+ is_torch_npu_available,
16
+ is_transformers_available,
17
+ is_transformers_version,
18
+ )
19
+
20
+
21
+ # These modules contain pipelines from multiple libraries/frameworks
22
+ _dummy_objects = {}
23
+ _import_structure = {
24
+ "controlnet": [],
25
+ "controlnet_hunyuandit": [],
26
+ "controlnet_sd3": [],
27
+ "deprecated": [],
28
+ "latent_diffusion": [],
29
+ "ledits_pp": [],
30
+ "marigold": [],
31
+ "pag": [],
32
+ "stable_diffusion": [],
33
+ "stable_diffusion_xl": [],
34
+ }
35
+
36
+ try:
37
+ if not is_torch_available():
38
+ raise OptionalDependencyNotAvailable()
39
+ except OptionalDependencyNotAvailable:
40
+ from ..utils import dummy_pt_objects # noqa F403
41
+
42
+ _dummy_objects.update(get_objects_from_module(dummy_pt_objects))
43
+ else:
44
+ _import_structure["auto_pipeline"] = [
45
+ "AutoPipelineForImage2Image",
46
+ "AutoPipelineForInpainting",
47
+ "AutoPipelineForText2Image",
48
+ ]
49
+ _import_structure["consistency_models"] = ["ConsistencyModelPipeline"]
50
+ _import_structure["ddim"] = ["DDIMPipeline"]
51
+ _import_structure["ddpm"] = ["DDPMPipeline"]
52
+ _import_structure["dit"] = ["DiTPipeline"]
53
+ _import_structure["latent_diffusion"].extend(["LDMSuperResolutionPipeline"])
54
+ _import_structure["pipeline_utils"] = [
55
+ "AudioPipelineOutput",
56
+ "DiffusionPipeline",
57
+ "StableDiffusionMixin",
58
+ "ImagePipelineOutput",
59
+ ]
60
+ _import_structure["deprecated"].extend(
61
+ [
62
+ "DanceDiffusionPipeline",
63
+ "PNDMPipeline",
64
+ "LDMPipeline",
65
+ "RePaintPipeline",
66
+ "ScoreSdeVePipeline",
67
+ "KarrasVePipeline",
68
+ ]
69
+ )
70
+ try:
71
+ if not (is_torch_available() and is_librosa_available()):
72
+ raise OptionalDependencyNotAvailable()
73
+ except OptionalDependencyNotAvailable:
74
+ from ..utils import dummy_torch_and_librosa_objects # noqa F403
75
+
76
+ _dummy_objects.update(get_objects_from_module(dummy_torch_and_librosa_objects))
77
+ else:
78
+ _import_structure["deprecated"].extend(["AudioDiffusionPipeline", "Mel"])
79
+
80
+ try:
81
+ if not (is_transformers_available() and is_torch_available() and is_note_seq_available()):
82
+ raise OptionalDependencyNotAvailable()
83
+ except OptionalDependencyNotAvailable:
84
+ from ..utils import dummy_transformers_and_torch_and_note_seq_objects # noqa F403
85
+
86
+ _dummy_objects.update(get_objects_from_module(dummy_transformers_and_torch_and_note_seq_objects))
87
+ else:
88
+ _import_structure["deprecated"].extend(
89
+ [
90
+ "MidiProcessor",
91
+ "SpectrogramDiffusionPipeline",
92
+ ]
93
+ )
94
+
95
+ try:
96
+ if not (is_torch_available() and is_transformers_available()):
97
+ raise OptionalDependencyNotAvailable()
98
+ except OptionalDependencyNotAvailable:
99
+ from ..utils import dummy_torch_and_transformers_objects # noqa F403
100
+
101
+ _dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
102
+ else:
103
+ _import_structure["deprecated"].extend(
104
+ [
105
+ "AmusedImg2ImgPipeline",
106
+ "AmusedInpaintPipeline",
107
+ "AmusedPipeline",
108
+ "AudioLDMPipeline",
109
+ "BlipDiffusionPipeline",
110
+ "I2VGenXLPipeline",
111
+ "ImageTextPipelineOutput",
112
+ "MusicLDMPipeline",
113
+ "PIAPipeline",
114
+ "PaintByExamplePipeline",
115
+ "SemanticStableDiffusionPipeline",
116
+ "StableDiffusionAttendAndExcitePipeline",
117
+ "StableDiffusionControlNetXSPipeline",
118
+ "StableDiffusionDiffEditPipeline",
119
+ "StableDiffusionGLIGENPipeline",
120
+ "StableDiffusionGLIGENTextImagePipeline",
121
+ "StableDiffusionLDM3DPipeline",
122
+ "StableDiffusionPanoramaPipeline",
123
+ "StableDiffusionPipelineSafe",
124
+ "StableDiffusionSAGPipeline",
125
+ "StableDiffusionXLControlNetXSPipeline",
126
+ "TextToVideoSDPipeline",
127
+ "TextToVideoZeroPipeline",
128
+ "TextToVideoZeroSDXLPipeline",
129
+ "UnCLIPImageVariationPipeline",
130
+ "UnCLIPPipeline",
131
+ "UniDiffuserModel",
132
+ "UniDiffuserPipeline",
133
+ "UniDiffuserTextDecoder",
134
+ "VQDiffusionPipeline",
135
+ "AltDiffusionPipeline",
136
+ "AltDiffusionImg2ImgPipeline",
137
+ "CycleDiffusionPipeline",
138
+ "StableDiffusionInpaintPipelineLegacy",
139
+ "StableDiffusionPix2PixZeroPipeline",
140
+ "StableDiffusionParadigmsPipeline",
141
+ "StableDiffusionModelEditingPipeline",
142
+ "VersatileDiffusionDualGuidedPipeline",
143
+ "VersatileDiffusionImageVariationPipeline",
144
+ "VersatileDiffusionPipeline",
145
+ "VersatileDiffusionTextToImagePipeline",
146
+ "VideoToVideoSDPipeline",
147
+ "WuerstchenCombinedPipeline",
148
+ "WuerstchenDecoderPipeline",
149
+ "WuerstchenPriorPipeline",
150
+ ]
151
+ )
152
+ _import_structure["allegro"] = ["AllegroPipeline"]
153
+ _import_structure["animatediff"] = [
154
+ "AnimateDiffPipeline",
155
+ "AnimateDiffControlNetPipeline",
156
+ "AnimateDiffSDXLPipeline",
157
+ "AnimateDiffSparseControlNetPipeline",
158
+ "AnimateDiffVideoToVideoPipeline",
159
+ "AnimateDiffVideoToVideoControlNetPipeline",
160
+ ]
161
+ _import_structure["bria"] = ["BriaPipeline"]
162
+ _import_structure["bria_fibo"] = ["BriaFiboPipeline", "BriaFiboEditPipeline"]
163
+ _import_structure["flux2"] = ["Flux2Pipeline", "Flux2KleinPipeline", "Flux2KleinKVPipeline"]
164
+ _import_structure["flux"] = [
165
+ "FluxControlPipeline",
166
+ "FluxControlInpaintPipeline",
167
+ "FluxControlImg2ImgPipeline",
168
+ "FluxControlNetPipeline",
169
+ "FluxControlNetImg2ImgPipeline",
170
+ "FluxControlNetInpaintPipeline",
171
+ "FluxImg2ImgPipeline",
172
+ "FluxInpaintPipeline",
173
+ "FluxPipeline",
174
+ "FluxFillPipeline",
175
+ "FluxPriorReduxPipeline",
176
+ "ReduxImageEncoder",
177
+ "FluxKontextPipeline",
178
+ "FluxKontextInpaintPipeline",
179
+ ]
180
+ _import_structure["prx"] = ["PRXPipeline"]
181
+ _import_structure["audioldm2"] = [
182
+ "AudioLDM2Pipeline",
183
+ "AudioLDM2ProjectionModel",
184
+ "AudioLDM2UNet2DConditionModel",
185
+ ]
186
+ _import_structure["chroma"] = ["ChromaPipeline", "ChromaImg2ImgPipeline", "ChromaInpaintPipeline"]
187
+ _import_structure["cogvideo"] = [
188
+ "CogVideoXPipeline",
189
+ "CogVideoXImageToVideoPipeline",
190
+ "CogVideoXVideoToVideoPipeline",
191
+ "CogVideoXFunControlPipeline",
192
+ ]
193
+ _import_structure["cogview3"] = ["CogView3PlusPipeline"]
194
+ _import_structure["cogview4"] = ["CogView4Pipeline", "CogView4ControlPipeline"]
195
+ _import_structure["consisid"] = ["ConsisIDPipeline"]
196
+ _import_structure["cosmos"] = [
197
+ "Cosmos2_5_PredictBasePipeline",
198
+ "Cosmos2_5_TransferPipeline",
199
+ "Cosmos2TextToImagePipeline",
200
+ "CosmosTextToWorldPipeline",
201
+ "CosmosVideoToWorldPipeline",
202
+ "Cosmos2VideoToWorldPipeline",
203
+ ]
204
+ _import_structure["controlnet"].extend(
205
+ [
206
+ "BlipDiffusionControlNetPipeline",
207
+ "StableDiffusionControlNetImg2ImgPipeline",
208
+ "StableDiffusionControlNetInpaintPipeline",
209
+ "StableDiffusionControlNetPipeline",
210
+ "StableDiffusionXLControlNetImg2ImgPipeline",
211
+ "StableDiffusionXLControlNetInpaintPipeline",
212
+ "StableDiffusionXLControlNetPipeline",
213
+ "StableDiffusionXLControlNetUnionPipeline",
214
+ "StableDiffusionXLControlNetUnionInpaintPipeline",
215
+ "StableDiffusionXLControlNetUnionImg2ImgPipeline",
216
+ ]
217
+ )
218
+ _import_structure["pag"].extend(
219
+ [
220
+ "StableDiffusionControlNetPAGInpaintPipeline",
221
+ "AnimateDiffPAGPipeline",
222
+ "KolorsPAGPipeline",
223
+ "HunyuanDiTPAGPipeline",
224
+ "StableDiffusion3PAGPipeline",
225
+ "StableDiffusion3PAGImg2ImgPipeline",
226
+ "StableDiffusionPAGPipeline",
227
+ "StableDiffusionPAGImg2ImgPipeline",
228
+ "StableDiffusionPAGInpaintPipeline",
229
+ "StableDiffusionControlNetPAGPipeline",
230
+ "StableDiffusionXLPAGPipeline",
231
+ "StableDiffusionXLPAGInpaintPipeline",
232
+ "StableDiffusionXLControlNetPAGImg2ImgPipeline",
233
+ "StableDiffusionXLControlNetPAGPipeline",
234
+ "StableDiffusionXLPAGImg2ImgPipeline",
235
+ "PixArtSigmaPAGPipeline",
236
+ "SanaPAGPipeline",
237
+ ]
238
+ )
239
+ _import_structure["controlnet_hunyuandit"].extend(
240
+ [
241
+ "HunyuanDiTControlNetPipeline",
242
+ ]
243
+ )
244
+ _import_structure["controlnet_sd3"].extend(
245
+ [
246
+ "StableDiffusion3ControlNetPipeline",
247
+ "StableDiffusion3ControlNetInpaintingPipeline",
248
+ ]
249
+ )
250
+ _import_structure["deepfloyd_if"] = [
251
+ "IFImg2ImgPipeline",
252
+ "IFImg2ImgSuperResolutionPipeline",
253
+ "IFInpaintingPipeline",
254
+ "IFInpaintingSuperResolutionPipeline",
255
+ "IFPipeline",
256
+ "IFSuperResolutionPipeline",
257
+ ]
258
+ _import_structure["easyanimate"] = [
259
+ "EasyAnimatePipeline",
260
+ "EasyAnimateInpaintPipeline",
261
+ "EasyAnimateControlPipeline",
262
+ ]
263
+ _import_structure["helios"] = ["HeliosPipeline", "HeliosPyramidPipeline"]
264
+ _import_structure["hidream_image"] = ["HiDreamImagePipeline"]
265
+ _import_structure["hunyuandit"] = ["HunyuanDiTPipeline"]
266
+ _import_structure["hunyuan_video"] = [
267
+ "HunyuanVideoPipeline",
268
+ "HunyuanSkyreelsImageToVideoPipeline",
269
+ "HunyuanVideoImageToVideoPipeline",
270
+ "HunyuanVideoFramepackPipeline",
271
+ ]
272
+ _import_structure["hunyuan_video1_5"] = ["HunyuanVideo15Pipeline", "HunyuanVideo15ImageToVideoPipeline"]
273
+ _import_structure["hunyuan_image"] = ["HunyuanImagePipeline", "HunyuanImageRefinerPipeline"]
274
+ _import_structure["kandinsky"] = [
275
+ "KandinskyCombinedPipeline",
276
+ "KandinskyImg2ImgCombinedPipeline",
277
+ "KandinskyImg2ImgPipeline",
278
+ "KandinskyInpaintCombinedPipeline",
279
+ "KandinskyInpaintPipeline",
280
+ "KandinskyPipeline",
281
+ "KandinskyPriorPipeline",
282
+ ]
283
+ _import_structure["kandinsky2_2"] = [
284
+ "KandinskyV22CombinedPipeline",
285
+ "KandinskyV22ControlnetImg2ImgPipeline",
286
+ "KandinskyV22ControlnetPipeline",
287
+ "KandinskyV22Img2ImgCombinedPipeline",
288
+ "KandinskyV22Img2ImgPipeline",
289
+ "KandinskyV22InpaintCombinedPipeline",
290
+ "KandinskyV22InpaintPipeline",
291
+ "KandinskyV22Pipeline",
292
+ "KandinskyV22PriorEmb2EmbPipeline",
293
+ "KandinskyV22PriorPipeline",
294
+ ]
295
+ _import_structure["kandinsky3"] = [
296
+ "Kandinsky3Img2ImgPipeline",
297
+ "Kandinsky3Pipeline",
298
+ ]
299
+ _import_structure["latent_consistency_models"] = [
300
+ "LatentConsistencyModelImg2ImgPipeline",
301
+ "LatentConsistencyModelPipeline",
302
+ ]
303
+ _import_structure["latent_diffusion"].extend(["LDMTextToImagePipeline"])
304
+ _import_structure["ledits_pp"].extend(
305
+ [
306
+ "LEditsPPPipelineStableDiffusion",
307
+ "LEditsPPPipelineStableDiffusionXL",
308
+ ]
309
+ )
310
+ _import_structure["latte"] = ["LattePipeline"]
311
+ _import_structure["llada2"] = ["LLaDA2Pipeline", "LLaDA2PipelineOutput"]
312
+ _import_structure["ltx"] = [
313
+ "LTXPipeline",
314
+ "LTXImageToVideoPipeline",
315
+ "LTXConditionPipeline",
316
+ "LTXLatentUpsamplePipeline",
317
+ "LTXI2VLongMultiPromptPipeline",
318
+ ]
319
+ _import_structure["ltx2"] = [
320
+ "LTX2Pipeline",
321
+ "LTX2ConditionPipeline",
322
+ "LTX2ImageToVideoPipeline",
323
+ "LTX2LatentUpsamplePipeline",
324
+ ]
325
+ _import_structure["lumina"] = ["LuminaPipeline", "LuminaText2ImgPipeline"]
326
+ _import_structure["lumina2"] = ["Lumina2Pipeline", "Lumina2Text2ImgPipeline"]
327
+ _import_structure["lucy"] = ["LucyEditPipeline"]
328
+ _import_structure["longcat_image"] = ["LongCatImagePipeline", "LongCatImageEditPipeline"]
329
+ _import_structure["marigold"].extend(
330
+ [
331
+ "MarigoldDepthPipeline",
332
+ "MarigoldIntrinsicsPipeline",
333
+ "MarigoldNormalsPipeline",
334
+ ]
335
+ )
336
+ _import_structure["mochi"] = ["MochiPipeline"]
337
+ _import_structure["omnigen"] = ["OmniGenPipeline"]
338
+ _import_structure["ovis_image"] = ["OvisImagePipeline"]
339
+ _import_structure["visualcloze"] = ["VisualClozePipeline", "VisualClozeGenerationPipeline"]
340
+ _import_structure["pixart_alpha"] = ["PixArtAlphaPipeline", "PixArtSigmaPipeline"]
341
+ _import_structure["sana"] = [
342
+ "SanaPipeline",
343
+ "SanaSprintPipeline",
344
+ "SanaControlNetPipeline",
345
+ "SanaSprintImg2ImgPipeline",
346
+ ]
347
+ _import_structure["sana_video"] = [
348
+ "SanaVideoPipeline",
349
+ "SanaImageToVideoPipeline",
350
+ ]
351
+ _import_structure["shap_e"] = ["ShapEImg2ImgPipeline", "ShapEPipeline"]
352
+ _import_structure["stable_audio"] = [
353
+ "StableAudioProjectionModel",
354
+ "StableAudioPipeline",
355
+ ]
356
+ _import_structure["stable_cascade"] = [
357
+ "StableCascadeCombinedPipeline",
358
+ "StableCascadeDecoderPipeline",
359
+ "StableCascadePriorPipeline",
360
+ ]
361
+ _import_structure["stable_diffusion"].extend(
362
+ [
363
+ "CLIPImageProjection",
364
+ "StableDiffusionDepth2ImgPipeline",
365
+ "StableDiffusionImageVariationPipeline",
366
+ "StableDiffusionImg2ImgPipeline",
367
+ "StableDiffusionInpaintPipeline",
368
+ "StableDiffusionInstructPix2PixPipeline",
369
+ "StableDiffusionLatentUpscalePipeline",
370
+ "StableDiffusionPipeline",
371
+ "StableDiffusionUpscalePipeline",
372
+ "StableUnCLIPImg2ImgPipeline",
373
+ "StableUnCLIPPipeline",
374
+ ]
375
+ )
376
+ _import_structure["aura_flow"] = ["AuraFlowPipeline"]
377
+ _import_structure["stable_diffusion_3"] = [
378
+ "StableDiffusion3Pipeline",
379
+ "StableDiffusion3Img2ImgPipeline",
380
+ "StableDiffusion3InpaintPipeline",
381
+ ]
382
+ _import_structure["stable_video_diffusion"] = ["StableVideoDiffusionPipeline"]
383
+ _import_structure["stable_diffusion_xl"].extend(
384
+ [
385
+ "StableDiffusionXLImg2ImgPipeline",
386
+ "StableDiffusionXLInpaintPipeline",
387
+ "StableDiffusionXLInstructPix2PixPipeline",
388
+ "StableDiffusionXLPipeline",
389
+ ]
390
+ )
391
+ _import_structure["t2i_adapter"] = [
392
+ "StableDiffusionAdapterPipeline",
393
+ "StableDiffusionXLAdapterPipeline",
394
+ ]
395
+ _import_structure["wan"] = [
396
+ "WanPipeline",
397
+ "WanImageToVideoPipeline",
398
+ "WanVideoToVideoPipeline",
399
+ "WanVACEPipeline",
400
+ "WanAnimatePipeline",
401
+ ]
402
+ _import_structure["kandinsky5"] = [
403
+ "Kandinsky5T2VPipeline",
404
+ "Kandinsky5I2VPipeline",
405
+ "Kandinsky5T2IPipeline",
406
+ "Kandinsky5I2IPipeline",
407
+ ]
408
+ _import_structure["z_image"] = [
409
+ "ZImageControlNetInpaintPipeline",
410
+ "ZImageControlNetPipeline",
411
+ "ZImageImg2ImgPipeline",
412
+ "ZImageInpaintPipeline",
413
+ "ZImageOmniPipeline",
414
+ "ZImagePipeline",
415
+ ]
416
+ _import_structure["skyreels_v2"] = [
417
+ "SkyReelsV2DiffusionForcingPipeline",
418
+ "SkyReelsV2DiffusionForcingImageToVideoPipeline",
419
+ "SkyReelsV2DiffusionForcingVideoToVideoPipeline",
420
+ "SkyReelsV2ImageToVideoPipeline",
421
+ "SkyReelsV2Pipeline",
422
+ ]
423
+ _import_structure["nucleusmoe_image"] = ["NucleusMoEImagePipeline"]
424
+ _import_structure["qwenimage"] = [
425
+ "QwenImagePipeline",
426
+ "QwenImageImg2ImgPipeline",
427
+ "QwenImageInpaintPipeline",
428
+ "QwenImageEditPipeline",
429
+ "QwenImageEditPlusPipeline",
430
+ "QwenImageEditInpaintPipeline",
431
+ "QwenImageControlNetInpaintPipeline",
432
+ "QwenImageControlNetPipeline",
433
+ "QwenImageLayeredPipeline",
434
+ ]
435
+ _import_structure["chronoedit"] = ["ChronoEditPipeline"]
436
+ _import_structure["glm_image"] = ["GlmImagePipeline"]
437
+
438
+ try:
439
+ if not is_onnx_available():
440
+ raise OptionalDependencyNotAvailable()
441
+ except OptionalDependencyNotAvailable:
442
+ from ..utils import dummy_onnx_objects # noqa F403
443
+
444
+ _dummy_objects.update(get_objects_from_module(dummy_onnx_objects))
445
+ else:
446
+ _import_structure["onnx_utils"] = ["OnnxRuntimeModel"]
447
+ try:
448
+ if not (is_torch_available() and is_transformers_available() and is_onnx_available()):
449
+ raise OptionalDependencyNotAvailable()
450
+ except OptionalDependencyNotAvailable:
451
+ from ..utils import dummy_torch_and_transformers_and_onnx_objects # noqa F403
452
+
453
+ _dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_and_onnx_objects))
454
+ else:
455
+ _import_structure["stable_diffusion"].extend(
456
+ [
457
+ "OnnxStableDiffusionImg2ImgPipeline",
458
+ "OnnxStableDiffusionInpaintPipeline",
459
+ "OnnxStableDiffusionPipeline",
460
+ "OnnxStableDiffusionUpscalePipeline",
461
+ "StableDiffusionOnnxPipeline",
462
+ ]
463
+ )
464
+
465
+ try:
466
+ if not (is_torch_available() and is_transformers_available() and is_sentencepiece_available()):
467
+ raise OptionalDependencyNotAvailable()
468
+ except OptionalDependencyNotAvailable:
469
+ from ..utils import (
470
+ dummy_torch_and_transformers_and_sentencepiece_objects,
471
+ )
472
+
473
+ _dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_and_sentencepiece_objects))
474
+ else:
475
+ _import_structure["kolors"] = [
476
+ "KolorsPipeline",
477
+ "KolorsImg2ImgPipeline",
478
+ ]
479
+
480
+ try:
481
+ if not (is_torch_available() and is_transformers_available() and is_opencv_available()):
482
+ raise OptionalDependencyNotAvailable()
483
+ except OptionalDependencyNotAvailable:
484
+ from ..utils import (
485
+ dummy_torch_and_transformers_and_opencv_objects,
486
+ )
487
+
488
+ _dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_and_opencv_objects))
489
+ else:
490
+ _import_structure["consisid"] = ["ConsisIDPipeline"]
491
+
492
+ try:
493
+ if not is_flax_available():
494
+ raise OptionalDependencyNotAvailable()
495
+ except OptionalDependencyNotAvailable:
496
+ from ..utils import dummy_flax_objects # noqa F403
497
+
498
+ _dummy_objects.update(get_objects_from_module(dummy_flax_objects))
499
+ else:
500
+ _import_structure["pipeline_flax_utils"] = ["FlaxDiffusionPipeline"]
501
+ try:
502
+ if not (is_flax_available() and is_transformers_available()):
503
+ raise OptionalDependencyNotAvailable()
504
+ except OptionalDependencyNotAvailable:
505
+ from ..utils import dummy_flax_and_transformers_objects # noqa F403
506
+
507
+ _dummy_objects.update(get_objects_from_module(dummy_flax_and_transformers_objects))
508
+ else:
509
+ _import_structure["controlnet"].extend(["FlaxStableDiffusionControlNetPipeline"])
510
+ _import_structure["stable_diffusion"].extend(
511
+ [
512
+ "FlaxStableDiffusionImg2ImgPipeline",
513
+ "FlaxStableDiffusionInpaintPipeline",
514
+ "FlaxStableDiffusionPipeline",
515
+ ]
516
+ )
517
+ _import_structure["stable_diffusion_xl"].extend(
518
+ [
519
+ "FlaxStableDiffusionXLPipeline",
520
+ ]
521
+ )
522
+
523
+ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
524
+ try:
525
+ if not is_torch_available():
526
+ raise OptionalDependencyNotAvailable()
527
+ except OptionalDependencyNotAvailable:
528
+ from ..utils.dummy_pt_objects import * # noqa F403
529
+
530
+ else:
531
+ from .auto_pipeline import (
532
+ AutoPipelineForImage2Image,
533
+ AutoPipelineForInpainting,
534
+ AutoPipelineForText2Image,
535
+ )
536
+ from .consistency_models import ConsistencyModelPipeline
537
+ from .ddim import DDIMPipeline
538
+ from .ddpm import DDPMPipeline
539
+ from .deprecated import (
540
+ DanceDiffusionPipeline,
541
+ KarrasVePipeline,
542
+ LDMPipeline,
543
+ PNDMPipeline,
544
+ RePaintPipeline,
545
+ ScoreSdeVePipeline,
546
+ )
547
+ from .dit import DiTPipeline
548
+ from .latent_diffusion import LDMSuperResolutionPipeline
549
+ from .pipeline_utils import (
550
+ AudioPipelineOutput,
551
+ DiffusionPipeline,
552
+ ImagePipelineOutput,
553
+ StableDiffusionMixin,
554
+ )
555
+
556
+ try:
557
+ if not (is_torch_available() and is_librosa_available()):
558
+ raise OptionalDependencyNotAvailable()
559
+ except OptionalDependencyNotAvailable:
560
+ from ..utils.dummy_torch_and_librosa_objects import *
561
+ else:
562
+ from .deprecated import AudioDiffusionPipeline, Mel
563
+
564
+ try:
565
+ if not (is_torch_available() and is_transformers_available()):
566
+ raise OptionalDependencyNotAvailable()
567
+ except OptionalDependencyNotAvailable:
568
+ from ..utils.dummy_torch_and_transformers_objects import *
569
+ else:
570
+ from .allegro import AllegroPipeline
571
+ from .animatediff import (
572
+ AnimateDiffControlNetPipeline,
573
+ AnimateDiffPipeline,
574
+ AnimateDiffSDXLPipeline,
575
+ AnimateDiffSparseControlNetPipeline,
576
+ AnimateDiffVideoToVideoControlNetPipeline,
577
+ AnimateDiffVideoToVideoPipeline,
578
+ )
579
+ from .audioldm2 import (
580
+ AudioLDM2Pipeline,
581
+ AudioLDM2ProjectionModel,
582
+ AudioLDM2UNet2DConditionModel,
583
+ )
584
+ from .aura_flow import AuraFlowPipeline
585
+ from .bria import BriaPipeline
586
+ from .bria_fibo import BriaFiboEditPipeline, BriaFiboPipeline
587
+ from .chroma import ChromaImg2ImgPipeline, ChromaInpaintPipeline, ChromaPipeline
588
+ from .chronoedit import ChronoEditPipeline
589
+ from .cogvideo import (
590
+ CogVideoXFunControlPipeline,
591
+ CogVideoXImageToVideoPipeline,
592
+ CogVideoXPipeline,
593
+ CogVideoXVideoToVideoPipeline,
594
+ )
595
+ from .cogview3 import CogView3PlusPipeline
596
+ from .cogview4 import CogView4ControlPipeline, CogView4Pipeline
597
+ from .controlnet import (
598
+ BlipDiffusionControlNetPipeline,
599
+ StableDiffusionControlNetImg2ImgPipeline,
600
+ StableDiffusionControlNetInpaintPipeline,
601
+ StableDiffusionControlNetPipeline,
602
+ StableDiffusionXLControlNetImg2ImgPipeline,
603
+ StableDiffusionXLControlNetInpaintPipeline,
604
+ StableDiffusionXLControlNetPipeline,
605
+ StableDiffusionXLControlNetUnionImg2ImgPipeline,
606
+ StableDiffusionXLControlNetUnionInpaintPipeline,
607
+ StableDiffusionXLControlNetUnionPipeline,
608
+ )
609
+ from .controlnet_hunyuandit import (
610
+ HunyuanDiTControlNetPipeline,
611
+ )
612
+ from .controlnet_sd3 import StableDiffusion3ControlNetInpaintingPipeline, StableDiffusion3ControlNetPipeline
613
+ from .cosmos import (
614
+ Cosmos2_5_PredictBasePipeline,
615
+ Cosmos2_5_TransferPipeline,
616
+ Cosmos2TextToImagePipeline,
617
+ Cosmos2VideoToWorldPipeline,
618
+ CosmosTextToWorldPipeline,
619
+ CosmosVideoToWorldPipeline,
620
+ )
621
+ from .deepfloyd_if import (
622
+ IFImg2ImgPipeline,
623
+ IFImg2ImgSuperResolutionPipeline,
624
+ IFInpaintingPipeline,
625
+ IFInpaintingSuperResolutionPipeline,
626
+ IFPipeline,
627
+ IFSuperResolutionPipeline,
628
+ )
629
+ from .deprecated import (
630
+ AltDiffusionImg2ImgPipeline,
631
+ AltDiffusionPipeline,
632
+ AmusedImg2ImgPipeline,
633
+ AmusedInpaintPipeline,
634
+ AmusedPipeline,
635
+ AudioLDMPipeline,
636
+ BlipDiffusionPipeline,
637
+ CycleDiffusionPipeline,
638
+ I2VGenXLPipeline,
639
+ ImageTextPipelineOutput,
640
+ MusicLDMPipeline,
641
+ PaintByExamplePipeline,
642
+ PIAPipeline,
643
+ SemanticStableDiffusionPipeline,
644
+ StableDiffusionAttendAndExcitePipeline,
645
+ StableDiffusionControlNetXSPipeline,
646
+ StableDiffusionDiffEditPipeline,
647
+ StableDiffusionGLIGENPipeline,
648
+ StableDiffusionGLIGENTextImagePipeline,
649
+ StableDiffusionInpaintPipelineLegacy,
650
+ StableDiffusionLDM3DPipeline,
651
+ StableDiffusionModelEditingPipeline,
652
+ StableDiffusionPanoramaPipeline,
653
+ StableDiffusionParadigmsPipeline,
654
+ StableDiffusionPipelineSafe,
655
+ StableDiffusionPix2PixZeroPipeline,
656
+ StableDiffusionSAGPipeline,
657
+ StableDiffusionXLControlNetXSPipeline,
658
+ TextToVideoSDPipeline,
659
+ TextToVideoZeroPipeline,
660
+ TextToVideoZeroSDXLPipeline,
661
+ UnCLIPImageVariationPipeline,
662
+ UnCLIPPipeline,
663
+ UniDiffuserModel,
664
+ UniDiffuserPipeline,
665
+ UniDiffuserTextDecoder,
666
+ VersatileDiffusionDualGuidedPipeline,
667
+ VersatileDiffusionImageVariationPipeline,
668
+ VersatileDiffusionPipeline,
669
+ VersatileDiffusionTextToImagePipeline,
670
+ VideoToVideoSDPipeline,
671
+ VQDiffusionPipeline,
672
+ WuerstchenCombinedPipeline,
673
+ WuerstchenDecoderPipeline,
674
+ WuerstchenPriorPipeline,
675
+ )
676
+ from .easyanimate import (
677
+ EasyAnimateControlPipeline,
678
+ EasyAnimateInpaintPipeline,
679
+ EasyAnimatePipeline,
680
+ )
681
+ from .flux import (
682
+ FluxControlImg2ImgPipeline,
683
+ FluxControlInpaintPipeline,
684
+ FluxControlNetImg2ImgPipeline,
685
+ FluxControlNetInpaintPipeline,
686
+ FluxControlNetPipeline,
687
+ FluxControlPipeline,
688
+ FluxFillPipeline,
689
+ FluxImg2ImgPipeline,
690
+ FluxInpaintPipeline,
691
+ FluxKontextInpaintPipeline,
692
+ FluxKontextPipeline,
693
+ FluxPipeline,
694
+ FluxPriorReduxPipeline,
695
+ ReduxImageEncoder,
696
+ )
697
+ from .flux2 import Flux2KleinKVPipeline, Flux2KleinPipeline, Flux2Pipeline
698
+ from .glm_image import GlmImagePipeline
699
+ from .helios import HeliosPipeline, HeliosPyramidPipeline
700
+ from .hidream_image import HiDreamImagePipeline
701
+ from .hunyuan_image import HunyuanImagePipeline, HunyuanImageRefinerPipeline
702
+ from .hunyuan_video import (
703
+ HunyuanSkyreelsImageToVideoPipeline,
704
+ HunyuanVideoFramepackPipeline,
705
+ HunyuanVideoImageToVideoPipeline,
706
+ HunyuanVideoPipeline,
707
+ )
708
+ from .hunyuan_video1_5 import HunyuanVideo15ImageToVideoPipeline, HunyuanVideo15Pipeline
709
+ from .hunyuandit import HunyuanDiTPipeline
710
+ from .kandinsky import (
711
+ KandinskyCombinedPipeline,
712
+ KandinskyImg2ImgCombinedPipeline,
713
+ KandinskyImg2ImgPipeline,
714
+ KandinskyInpaintCombinedPipeline,
715
+ KandinskyInpaintPipeline,
716
+ KandinskyPipeline,
717
+ KandinskyPriorPipeline,
718
+ )
719
+ from .kandinsky2_2 import (
720
+ KandinskyV22CombinedPipeline,
721
+ KandinskyV22ControlnetImg2ImgPipeline,
722
+ KandinskyV22ControlnetPipeline,
723
+ KandinskyV22Img2ImgCombinedPipeline,
724
+ KandinskyV22Img2ImgPipeline,
725
+ KandinskyV22InpaintCombinedPipeline,
726
+ KandinskyV22InpaintPipeline,
727
+ KandinskyV22Pipeline,
728
+ KandinskyV22PriorEmb2EmbPipeline,
729
+ KandinskyV22PriorPipeline,
730
+ )
731
+ from .kandinsky3 import (
732
+ Kandinsky3Img2ImgPipeline,
733
+ Kandinsky3Pipeline,
734
+ )
735
+ from .kandinsky5 import (
736
+ Kandinsky5I2IPipeline,
737
+ Kandinsky5I2VPipeline,
738
+ Kandinsky5T2IPipeline,
739
+ Kandinsky5T2VPipeline,
740
+ )
741
+ from .latent_consistency_models import (
742
+ LatentConsistencyModelImg2ImgPipeline,
743
+ LatentConsistencyModelPipeline,
744
+ )
745
+ from .latent_diffusion import LDMTextToImagePipeline
746
+ from .latte import LattePipeline
747
+ from .ledits_pp import (
748
+ LEditsPPDiffusionPipelineOutput,
749
+ LEditsPPInversionPipelineOutput,
750
+ LEditsPPPipelineStableDiffusion,
751
+ LEditsPPPipelineStableDiffusionXL,
752
+ )
753
+ from .llada2 import LLaDA2Pipeline, LLaDA2PipelineOutput
754
+ from .longcat_image import LongCatImageEditPipeline, LongCatImagePipeline
755
+ from .ltx import (
756
+ LTXConditionPipeline,
757
+ LTXI2VLongMultiPromptPipeline,
758
+ LTXImageToVideoPipeline,
759
+ LTXLatentUpsamplePipeline,
760
+ LTXPipeline,
761
+ )
762
+ from .ltx2 import LTX2ConditionPipeline, LTX2ImageToVideoPipeline, LTX2LatentUpsamplePipeline, LTX2Pipeline
763
+ from .lucy import LucyEditPipeline
764
+ from .lumina import LuminaPipeline, LuminaText2ImgPipeline
765
+ from .lumina2 import Lumina2Pipeline, Lumina2Text2ImgPipeline
766
+ from .marigold import (
767
+ MarigoldDepthPipeline,
768
+ MarigoldIntrinsicsPipeline,
769
+ MarigoldNormalsPipeline,
770
+ )
771
+ from .mochi import MochiPipeline
772
+ from .nucleusmoe_image import NucleusMoEImagePipeline
773
+ from .omnigen import OmniGenPipeline
774
+ from .ovis_image import OvisImagePipeline
775
+ from .pag import (
776
+ AnimateDiffPAGPipeline,
777
+ HunyuanDiTPAGPipeline,
778
+ KolorsPAGPipeline,
779
+ PixArtSigmaPAGPipeline,
780
+ SanaPAGPipeline,
781
+ StableDiffusion3PAGImg2ImgPipeline,
782
+ StableDiffusion3PAGPipeline,
783
+ StableDiffusionControlNetPAGInpaintPipeline,
784
+ StableDiffusionControlNetPAGPipeline,
785
+ StableDiffusionPAGImg2ImgPipeline,
786
+ StableDiffusionPAGInpaintPipeline,
787
+ StableDiffusionPAGPipeline,
788
+ StableDiffusionXLControlNetPAGImg2ImgPipeline,
789
+ StableDiffusionXLControlNetPAGPipeline,
790
+ StableDiffusionXLPAGImg2ImgPipeline,
791
+ StableDiffusionXLPAGInpaintPipeline,
792
+ StableDiffusionXLPAGPipeline,
793
+ )
794
+ from .pixart_alpha import PixArtAlphaPipeline, PixArtSigmaPipeline
795
+ from .prx import PRXPipeline
796
+ from .qwenimage import (
797
+ QwenImageControlNetInpaintPipeline,
798
+ QwenImageControlNetPipeline,
799
+ QwenImageEditInpaintPipeline,
800
+ QwenImageEditPipeline,
801
+ QwenImageEditPlusPipeline,
802
+ QwenImageImg2ImgPipeline,
803
+ QwenImageInpaintPipeline,
804
+ QwenImageLayeredPipeline,
805
+ QwenImagePipeline,
806
+ )
807
+ from .sana import (
808
+ SanaControlNetPipeline,
809
+ SanaPipeline,
810
+ SanaSprintImg2ImgPipeline,
811
+ SanaSprintPipeline,
812
+ )
813
+ from .sana_video import SanaImageToVideoPipeline, SanaVideoPipeline
814
+ from .shap_e import ShapEImg2ImgPipeline, ShapEPipeline
815
+ from .stable_audio import StableAudioPipeline, StableAudioProjectionModel
816
+ from .stable_cascade import (
817
+ StableCascadeCombinedPipeline,
818
+ StableCascadeDecoderPipeline,
819
+ StableCascadePriorPipeline,
820
+ )
821
+ from .stable_diffusion import (
822
+ CLIPImageProjection,
823
+ StableDiffusionDepth2ImgPipeline,
824
+ StableDiffusionImageVariationPipeline,
825
+ StableDiffusionImg2ImgPipeline,
826
+ StableDiffusionInpaintPipeline,
827
+ StableDiffusionInstructPix2PixPipeline,
828
+ StableDiffusionLatentUpscalePipeline,
829
+ StableDiffusionPipeline,
830
+ StableDiffusionUpscalePipeline,
831
+ StableUnCLIPImg2ImgPipeline,
832
+ StableUnCLIPPipeline,
833
+ )
834
+ from .stable_diffusion_3 import (
835
+ StableDiffusion3Img2ImgPipeline,
836
+ StableDiffusion3InpaintPipeline,
837
+ StableDiffusion3Pipeline,
838
+ )
839
+ from .stable_diffusion_xl import (
840
+ StableDiffusionXLImg2ImgPipeline,
841
+ StableDiffusionXLInpaintPipeline,
842
+ StableDiffusionXLInstructPix2PixPipeline,
843
+ StableDiffusionXLPipeline,
844
+ )
845
+ from .stable_video_diffusion import StableVideoDiffusionPipeline
846
+ from .t2i_adapter import (
847
+ StableDiffusionAdapterPipeline,
848
+ StableDiffusionXLAdapterPipeline,
849
+ )
850
+ from .visualcloze import VisualClozeGenerationPipeline, VisualClozePipeline
851
+ from .wan import (
852
+ WanAnimatePipeline,
853
+ WanImageToVideoPipeline,
854
+ WanPipeline,
855
+ WanVACEPipeline,
856
+ WanVideoToVideoPipeline,
857
+ )
858
+ from .z_image import (
859
+ ZImageControlNetInpaintPipeline,
860
+ ZImageControlNetPipeline,
861
+ ZImageImg2ImgPipeline,
862
+ ZImageInpaintPipeline,
863
+ ZImageOmniPipeline,
864
+ ZImagePipeline,
865
+ )
866
+
867
+ try:
868
+ if not is_onnx_available():
869
+ raise OptionalDependencyNotAvailable()
870
+ except OptionalDependencyNotAvailable:
871
+ from ..utils.dummy_onnx_objects import * # noqa F403
872
+
873
+ else:
874
+ from .onnx_utils import OnnxRuntimeModel
875
+
876
+ try:
877
+ if not (is_torch_available() and is_transformers_available() and is_onnx_available()):
878
+ raise OptionalDependencyNotAvailable()
879
+ except OptionalDependencyNotAvailable:
880
+ from ..utils.dummy_torch_and_transformers_and_onnx_objects import *
881
+ else:
882
+ from .stable_diffusion import (
883
+ OnnxStableDiffusionImg2ImgPipeline,
884
+ OnnxStableDiffusionInpaintPipeline,
885
+ OnnxStableDiffusionPipeline,
886
+ OnnxStableDiffusionUpscalePipeline,
887
+ StableDiffusionOnnxPipeline,
888
+ )
889
+
890
+ try:
891
+ if not (is_torch_available() and is_transformers_available() and is_sentencepiece_available()):
892
+ raise OptionalDependencyNotAvailable()
893
+ except OptionalDependencyNotAvailable:
894
+ from ..utils.dummy_torch_and_transformers_and_sentencepiece_objects import *
895
+ else:
896
+ from .kolors import (
897
+ KolorsImg2ImgPipeline,
898
+ KolorsPipeline,
899
+ )
900
+
901
+ try:
902
+ if not (is_torch_available() and is_transformers_available() and is_opencv_available()):
903
+ raise OptionalDependencyNotAvailable()
904
+ except OptionalDependencyNotAvailable:
905
+ from ..utils.dummy_torch_and_transformers_and_opencv_objects import *
906
+ else:
907
+ from .consisid import ConsisIDPipeline
908
+
909
+ try:
910
+ if not is_flax_available():
911
+ raise OptionalDependencyNotAvailable()
912
+ except OptionalDependencyNotAvailable:
913
+ from ..utils.dummy_flax_objects import * # noqa F403
914
+ else:
915
+ from .pipeline_flax_utils import FlaxDiffusionPipeline
916
+
917
+ try:
918
+ if not (is_flax_available() and is_transformers_available()):
919
+ raise OptionalDependencyNotAvailable()
920
+ except OptionalDependencyNotAvailable:
921
+ from ..utils.dummy_flax_and_transformers_objects import *
922
+ else:
923
+ from .controlnet import FlaxStableDiffusionControlNetPipeline
924
+ from .stable_diffusion import (
925
+ FlaxStableDiffusionImg2ImgPipeline,
926
+ FlaxStableDiffusionInpaintPipeline,
927
+ FlaxStableDiffusionPipeline,
928
+ )
929
+ from .stable_diffusion_xl import (
930
+ FlaxStableDiffusionXLPipeline,
931
+ )
932
+
933
+ try:
934
+ if not (is_transformers_available() and is_torch_available() and is_note_seq_available()):
935
+ raise OptionalDependencyNotAvailable()
936
+ except OptionalDependencyNotAvailable:
937
+ from ..utils.dummy_transformers_and_torch_and_note_seq_objects import * # noqa F403
938
+
939
+ else:
940
+ from .deprecated import (
941
+ MidiProcessor,
942
+ SpectrogramDiffusionPipeline,
943
+ )
944
+
945
+ from .skyreels_v2 import (
946
+ SkyReelsV2DiffusionForcingImageToVideoPipeline,
947
+ SkyReelsV2DiffusionForcingPipeline,
948
+ SkyReelsV2DiffusionForcingVideoToVideoPipeline,
949
+ SkyReelsV2ImageToVideoPipeline,
950
+ SkyReelsV2Pipeline,
951
+ )
952
+
953
+ else:
954
+ import sys
955
+
956
+ sys.modules[__name__] = _LazyModule(
957
+ __name__,
958
+ globals()["__file__"],
959
+ _import_structure,
960
+ module_spec=__spec__,
961
+ )
962
+ for name, value in _dummy_objects.items():
963
+ setattr(sys.modules[__name__], name, value)
third_party/diffusers/src/diffusers/pipelines/auto_pipeline.py ADDED
@@ -0,0 +1,1237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # coding=utf-8
2
+ # Copyright 2025 The HuggingFace Inc. team.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ from collections import OrderedDict
17
+
18
+ from huggingface_hub.utils import validate_hf_hub_args
19
+
20
+ from ..configuration_utils import ConfigMixin
21
+ from ..models.controlnets import ControlNetUnionModel
22
+ from ..utils import is_sentencepiece_available
23
+ from .aura_flow import AuraFlowPipeline
24
+ from .chroma import ChromaPipeline
25
+ from .cogview3 import CogView3PlusPipeline
26
+ from .cogview4 import CogView4ControlPipeline, CogView4Pipeline
27
+ from .controlnet import (
28
+ StableDiffusionControlNetImg2ImgPipeline,
29
+ StableDiffusionControlNetInpaintPipeline,
30
+ StableDiffusionControlNetPipeline,
31
+ StableDiffusionXLControlNetImg2ImgPipeline,
32
+ StableDiffusionXLControlNetInpaintPipeline,
33
+ StableDiffusionXLControlNetPipeline,
34
+ StableDiffusionXLControlNetUnionImg2ImgPipeline,
35
+ StableDiffusionXLControlNetUnionInpaintPipeline,
36
+ StableDiffusionXLControlNetUnionPipeline,
37
+ )
38
+ from .controlnet_sd3 import (
39
+ StableDiffusion3ControlNetInpaintingPipeline,
40
+ StableDiffusion3ControlNetPipeline,
41
+ )
42
+ from .deepfloyd_if import IFImg2ImgPipeline, IFInpaintingPipeline, IFPipeline
43
+ from .deprecated.wuerstchen import WuerstchenCombinedPipeline, WuerstchenDecoderPipeline
44
+ from .flux import (
45
+ FluxControlImg2ImgPipeline,
46
+ FluxControlInpaintPipeline,
47
+ FluxControlNetImg2ImgPipeline,
48
+ FluxControlNetInpaintPipeline,
49
+ FluxControlNetPipeline,
50
+ FluxControlPipeline,
51
+ FluxImg2ImgPipeline,
52
+ FluxInpaintPipeline,
53
+ FluxKontextPipeline,
54
+ FluxPipeline,
55
+ )
56
+ from .flux2 import Flux2KleinPipeline, Flux2Pipeline
57
+ from .glm_image import GlmImagePipeline
58
+ from .helios import HeliosPipeline, HeliosPyramidPipeline
59
+ from .hunyuandit import HunyuanDiTPipeline
60
+ from .kandinsky import (
61
+ KandinskyCombinedPipeline,
62
+ KandinskyImg2ImgCombinedPipeline,
63
+ KandinskyImg2ImgPipeline,
64
+ KandinskyInpaintCombinedPipeline,
65
+ KandinskyInpaintPipeline,
66
+ KandinskyPipeline,
67
+ )
68
+ from .kandinsky2_2 import (
69
+ KandinskyV22CombinedPipeline,
70
+ KandinskyV22Img2ImgCombinedPipeline,
71
+ KandinskyV22Img2ImgPipeline,
72
+ KandinskyV22InpaintCombinedPipeline,
73
+ KandinskyV22InpaintPipeline,
74
+ KandinskyV22Pipeline,
75
+ )
76
+ from .kandinsky3 import Kandinsky3Img2ImgPipeline, Kandinsky3Pipeline
77
+ from .latent_consistency_models import LatentConsistencyModelImg2ImgPipeline, LatentConsistencyModelPipeline
78
+ from .lumina import LuminaPipeline
79
+ from .lumina2 import Lumina2Pipeline
80
+ from .nucleusmoe_image import NucleusMoEImagePipeline
81
+ from .ovis_image import OvisImagePipeline
82
+ from .pag import (
83
+ HunyuanDiTPAGPipeline,
84
+ PixArtSigmaPAGPipeline,
85
+ SanaPAGPipeline,
86
+ StableDiffusion3PAGImg2ImgPipeline,
87
+ StableDiffusion3PAGPipeline,
88
+ StableDiffusionControlNetPAGInpaintPipeline,
89
+ StableDiffusionControlNetPAGPipeline,
90
+ StableDiffusionPAGImg2ImgPipeline,
91
+ StableDiffusionPAGInpaintPipeline,
92
+ StableDiffusionPAGPipeline,
93
+ StableDiffusionXLControlNetPAGImg2ImgPipeline,
94
+ StableDiffusionXLControlNetPAGPipeline,
95
+ StableDiffusionXLPAGImg2ImgPipeline,
96
+ StableDiffusionXLPAGInpaintPipeline,
97
+ StableDiffusionXLPAGPipeline,
98
+ )
99
+ from .pixart_alpha import PixArtAlphaPipeline, PixArtSigmaPipeline
100
+ from .prx import PRXPipeline
101
+ from .qwenimage import (
102
+ QwenImageControlNetPipeline,
103
+ QwenImageEditInpaintPipeline,
104
+ QwenImageEditPipeline,
105
+ QwenImageEditPlusPipeline,
106
+ QwenImageImg2ImgPipeline,
107
+ QwenImageInpaintPipeline,
108
+ QwenImageLayeredPipeline,
109
+ QwenImagePipeline,
110
+ )
111
+ from .sana import SanaPipeline
112
+ from .stable_cascade import StableCascadeCombinedPipeline, StableCascadeDecoderPipeline
113
+ from .stable_diffusion import (
114
+ StableDiffusionImg2ImgPipeline,
115
+ StableDiffusionInpaintPipeline,
116
+ StableDiffusionPipeline,
117
+ )
118
+ from .stable_diffusion_3 import (
119
+ StableDiffusion3Img2ImgPipeline,
120
+ StableDiffusion3InpaintPipeline,
121
+ StableDiffusion3Pipeline,
122
+ )
123
+ from .stable_diffusion_xl import (
124
+ StableDiffusionXLImg2ImgPipeline,
125
+ StableDiffusionXLInpaintPipeline,
126
+ StableDiffusionXLPipeline,
127
+ )
128
+ from .wan import WanImageToVideoPipeline, WanPipeline, WanVideoToVideoPipeline
129
+ from .z_image import (
130
+ ZImageControlNetInpaintPipeline,
131
+ ZImageControlNetPipeline,
132
+ ZImageImg2ImgPipeline,
133
+ ZImageInpaintPipeline,
134
+ ZImageOmniPipeline,
135
+ ZImagePipeline,
136
+ )
137
+
138
+
139
+ AUTO_TEXT2IMAGE_PIPELINES_MAPPING = OrderedDict(
140
+ [
141
+ ("stable-diffusion", StableDiffusionPipeline),
142
+ ("stable-diffusion-xl", StableDiffusionXLPipeline),
143
+ ("stable-diffusion-3", StableDiffusion3Pipeline),
144
+ ("stable-diffusion-3-pag", StableDiffusion3PAGPipeline),
145
+ ("if", IFPipeline),
146
+ ("hunyuan", HunyuanDiTPipeline),
147
+ ("hunyuan-pag", HunyuanDiTPAGPipeline),
148
+ ("kandinsky", KandinskyCombinedPipeline),
149
+ ("kandinsky22", KandinskyV22CombinedPipeline),
150
+ ("kandinsky3", Kandinsky3Pipeline),
151
+ ("stable-diffusion-controlnet", StableDiffusionControlNetPipeline),
152
+ ("stable-diffusion-xl-controlnet", StableDiffusionXLControlNetPipeline),
153
+ ("stable-diffusion-xl-controlnet-union", StableDiffusionXLControlNetUnionPipeline),
154
+ ("stable-diffusion-3-controlnet", StableDiffusion3ControlNetPipeline),
155
+ ("wuerstchen", WuerstchenCombinedPipeline),
156
+ ("cascade", StableCascadeCombinedPipeline),
157
+ ("lcm", LatentConsistencyModelPipeline),
158
+ ("pixart-alpha", PixArtAlphaPipeline),
159
+ ("pixart-sigma", PixArtSigmaPipeline),
160
+ ("sana", SanaPipeline),
161
+ ("sana-pag", SanaPAGPipeline),
162
+ ("stable-diffusion-pag", StableDiffusionPAGPipeline),
163
+ ("stable-diffusion-controlnet-pag", StableDiffusionControlNetPAGPipeline),
164
+ ("stable-diffusion-xl-pag", StableDiffusionXLPAGPipeline),
165
+ ("stable-diffusion-xl-controlnet-pag", StableDiffusionXLControlNetPAGPipeline),
166
+ ("pixart-sigma-pag", PixArtSigmaPAGPipeline),
167
+ ("auraflow", AuraFlowPipeline),
168
+ ("flux", FluxPipeline),
169
+ ("flux-control", FluxControlPipeline),
170
+ ("flux-controlnet", FluxControlNetPipeline),
171
+ ("flux-kontext", FluxKontextPipeline),
172
+ ("flux2-klein", Flux2KleinPipeline),
173
+ ("flux2", Flux2Pipeline),
174
+ ("lumina", LuminaPipeline),
175
+ ("lumina2", Lumina2Pipeline),
176
+ ("chroma", ChromaPipeline),
177
+ ("cogview3", CogView3PlusPipeline),
178
+ ("cogview4", CogView4Pipeline),
179
+ ("glm_image", GlmImagePipeline),
180
+ ("helios", HeliosPipeline),
181
+ ("helios-pyramid", HeliosPyramidPipeline),
182
+ ("cogview4-control", CogView4ControlPipeline),
183
+ ("nucleusmoe-image", NucleusMoEImagePipeline),
184
+ ("qwenimage", QwenImagePipeline),
185
+ ("qwenimage-controlnet", QwenImageControlNetPipeline),
186
+ ("z-image", ZImagePipeline),
187
+ ("z-image-controlnet", ZImageControlNetPipeline),
188
+ ("z-image-controlnet-inpaint", ZImageControlNetInpaintPipeline),
189
+ ("z-image-omni", ZImageOmniPipeline),
190
+ ("ovis", OvisImagePipeline),
191
+ ("prx", PRXPipeline),
192
+ ]
193
+ )
194
+
195
+ AUTO_IMAGE2IMAGE_PIPELINES_MAPPING = OrderedDict(
196
+ [
197
+ ("stable-diffusion", StableDiffusionImg2ImgPipeline),
198
+ ("stable-diffusion-xl", StableDiffusionXLImg2ImgPipeline),
199
+ ("stable-diffusion-3", StableDiffusion3Img2ImgPipeline),
200
+ ("stable-diffusion-3-pag", StableDiffusion3PAGImg2ImgPipeline),
201
+ ("if", IFImg2ImgPipeline),
202
+ ("kandinsky", KandinskyImg2ImgCombinedPipeline),
203
+ ("kandinsky22", KandinskyV22Img2ImgCombinedPipeline),
204
+ ("kandinsky3", Kandinsky3Img2ImgPipeline),
205
+ ("stable-diffusion-controlnet", StableDiffusionControlNetImg2ImgPipeline),
206
+ ("stable-diffusion-pag", StableDiffusionPAGImg2ImgPipeline),
207
+ ("stable-diffusion-xl-controlnet", StableDiffusionXLControlNetImg2ImgPipeline),
208
+ ("stable-diffusion-xl-controlnet-union", StableDiffusionXLControlNetUnionImg2ImgPipeline),
209
+ ("stable-diffusion-xl-pag", StableDiffusionXLPAGImg2ImgPipeline),
210
+ ("stable-diffusion-xl-controlnet-pag", StableDiffusionXLControlNetPAGImg2ImgPipeline),
211
+ ("lcm", LatentConsistencyModelImg2ImgPipeline),
212
+ ("flux", FluxImg2ImgPipeline),
213
+ ("flux-controlnet", FluxControlNetImg2ImgPipeline),
214
+ ("flux-control", FluxControlImg2ImgPipeline),
215
+ ("flux-kontext", FluxKontextPipeline),
216
+ ("flux2-klein", Flux2KleinPipeline),
217
+ ("flux2", Flux2Pipeline),
218
+ ("qwenimage", QwenImageImg2ImgPipeline),
219
+ ("qwenimage-edit", QwenImageEditPipeline),
220
+ ("qwenimage-edit-plus", QwenImageEditPlusPipeline),
221
+ ("qwenimage-layered", QwenImageLayeredPipeline),
222
+ ("z-image", ZImageImg2ImgPipeline),
223
+ ]
224
+ )
225
+
226
+ AUTO_INPAINT_PIPELINES_MAPPING = OrderedDict(
227
+ [
228
+ ("stable-diffusion", StableDiffusionInpaintPipeline),
229
+ ("stable-diffusion-xl", StableDiffusionXLInpaintPipeline),
230
+ ("stable-diffusion-3", StableDiffusion3InpaintPipeline),
231
+ ("if", IFInpaintingPipeline),
232
+ ("kandinsky", KandinskyInpaintCombinedPipeline),
233
+ ("kandinsky22", KandinskyV22InpaintCombinedPipeline),
234
+ ("stable-diffusion-controlnet", StableDiffusionControlNetInpaintPipeline),
235
+ ("stable-diffusion-controlnet-pag", StableDiffusionControlNetPAGInpaintPipeline),
236
+ ("stable-diffusion-xl-controlnet", StableDiffusionXLControlNetInpaintPipeline),
237
+ ("stable-diffusion-xl-controlnet-union", StableDiffusionXLControlNetUnionInpaintPipeline),
238
+ ("stable-diffusion-3-controlnet", StableDiffusion3ControlNetInpaintingPipeline),
239
+ ("stable-diffusion-xl-pag", StableDiffusionXLPAGInpaintPipeline),
240
+ ("flux", FluxInpaintPipeline),
241
+ ("flux-controlnet", FluxControlNetInpaintPipeline),
242
+ ("flux-control", FluxControlInpaintPipeline),
243
+ ("stable-diffusion-pag", StableDiffusionPAGInpaintPipeline),
244
+ ("qwenimage", QwenImageInpaintPipeline),
245
+ ("qwenimage-edit", QwenImageEditInpaintPipeline),
246
+ ("z-image", ZImageInpaintPipeline),
247
+ ]
248
+ )
249
+
250
+ AUTO_TEXT2VIDEO_PIPELINES_MAPPING = OrderedDict(
251
+ [
252
+ ("wan", WanPipeline),
253
+ ]
254
+ )
255
+
256
+ AUTO_IMAGE2VIDEO_PIPELINES_MAPPING = OrderedDict(
257
+ [
258
+ ("wan-i2v", WanImageToVideoPipeline),
259
+ ]
260
+ )
261
+
262
+ AUTO_VIDEO2VIDEO_PIPELINES_MAPPING = OrderedDict(
263
+ [
264
+ ("wan", WanVideoToVideoPipeline),
265
+ ]
266
+ )
267
+
268
+ _AUTO_TEXT2IMAGE_DECODER_PIPELINES_MAPPING = OrderedDict(
269
+ [
270
+ ("kandinsky", KandinskyPipeline),
271
+ ("kandinsky22", KandinskyV22Pipeline),
272
+ ("wuerstchen", WuerstchenDecoderPipeline),
273
+ ("cascade", StableCascadeDecoderPipeline),
274
+ ]
275
+ )
276
+ _AUTO_IMAGE2IMAGE_DECODER_PIPELINES_MAPPING = OrderedDict(
277
+ [
278
+ ("kandinsky", KandinskyImg2ImgPipeline),
279
+ ("kandinsky22", KandinskyV22Img2ImgPipeline),
280
+ ]
281
+ )
282
+ _AUTO_INPAINT_DECODER_PIPELINES_MAPPING = OrderedDict(
283
+ [
284
+ ("kandinsky", KandinskyInpaintPipeline),
285
+ ("kandinsky22", KandinskyV22InpaintPipeline),
286
+ ]
287
+ )
288
+
289
+ if is_sentencepiece_available():
290
+ from .kolors import KolorsImg2ImgPipeline, KolorsPipeline
291
+ from .pag import KolorsPAGPipeline
292
+
293
+ AUTO_TEXT2IMAGE_PIPELINES_MAPPING["kolors"] = KolorsPipeline
294
+ AUTO_TEXT2IMAGE_PIPELINES_MAPPING["kolors-pag"] = KolorsPAGPipeline
295
+ AUTO_IMAGE2IMAGE_PIPELINES_MAPPING["kolors"] = KolorsImg2ImgPipeline
296
+
297
+ SUPPORTED_TASKS_MAPPINGS = [
298
+ AUTO_TEXT2IMAGE_PIPELINES_MAPPING,
299
+ AUTO_IMAGE2IMAGE_PIPELINES_MAPPING,
300
+ AUTO_INPAINT_PIPELINES_MAPPING,
301
+ AUTO_TEXT2VIDEO_PIPELINES_MAPPING,
302
+ AUTO_IMAGE2VIDEO_PIPELINES_MAPPING,
303
+ AUTO_VIDEO2VIDEO_PIPELINES_MAPPING,
304
+ _AUTO_TEXT2IMAGE_DECODER_PIPELINES_MAPPING,
305
+ _AUTO_IMAGE2IMAGE_DECODER_PIPELINES_MAPPING,
306
+ _AUTO_INPAINT_DECODER_PIPELINES_MAPPING,
307
+ ]
308
+
309
+
310
+ def _get_connected_pipeline(pipeline_cls):
311
+ # for now connected pipelines can only be loaded from decoder pipelines, such as kandinsky-community/kandinsky-2-2-decoder
312
+ if pipeline_cls in _AUTO_TEXT2IMAGE_DECODER_PIPELINES_MAPPING.values():
313
+ return _get_task_class(
314
+ AUTO_TEXT2IMAGE_PIPELINES_MAPPING, pipeline_cls.__name__, throw_error_if_not_exist=False
315
+ )
316
+ if pipeline_cls in _AUTO_IMAGE2IMAGE_DECODER_PIPELINES_MAPPING.values():
317
+ return _get_task_class(
318
+ AUTO_IMAGE2IMAGE_PIPELINES_MAPPING, pipeline_cls.__name__, throw_error_if_not_exist=False
319
+ )
320
+ if pipeline_cls in _AUTO_INPAINT_DECODER_PIPELINES_MAPPING.values():
321
+ return _get_task_class(AUTO_INPAINT_PIPELINES_MAPPING, pipeline_cls.__name__, throw_error_if_not_exist=False)
322
+
323
+
324
+ def _get_model(pipeline_class_name):
325
+ for task_mapping in SUPPORTED_TASKS_MAPPINGS:
326
+ for model_name, pipeline in task_mapping.items():
327
+ if pipeline.__name__ == pipeline_class_name:
328
+ return model_name
329
+
330
+
331
+ def _get_task_class(mapping, pipeline_class_name, throw_error_if_not_exist: bool = True):
332
+ model_name = _get_model(pipeline_class_name)
333
+
334
+ if model_name is not None:
335
+ task_class = mapping.get(model_name, None)
336
+ if task_class is not None:
337
+ return task_class
338
+
339
+ if throw_error_if_not_exist:
340
+ raise ValueError(f"AutoPipeline can't find a pipeline linked to {pipeline_class_name} for {model_name}")
341
+
342
+
343
+ class AutoPipelineForText2Image(ConfigMixin):
344
+ r"""
345
+
346
+ [`AutoPipelineForText2Image`] is a generic pipeline class that instantiates a text-to-image pipeline class. The
347
+ specific underlying pipeline class is automatically selected from either the
348
+ [`~AutoPipelineForText2Image.from_pretrained`] or [`~AutoPipelineForText2Image.from_pipe`] methods.
349
+
350
+ This class cannot be instantiated using `__init__()` (throws an error).
351
+
352
+ Class attributes:
353
+
354
+ - **config_name** (`str`) -- The configuration filename that stores the class and module names of all the
355
+ diffusion pipeline's components.
356
+
357
+ """
358
+
359
+ config_name = "model_index.json"
360
+
361
+ def __init__(self, *args, **kwargs):
362
+ raise EnvironmentError(
363
+ f"{self.__class__.__name__} is designed to be instantiated "
364
+ f"using the `{self.__class__.__name__}.from_pretrained(pretrained_model_name_or_path)` or "
365
+ f"`{self.__class__.__name__}.from_pipe(pipeline)` methods."
366
+ )
367
+
368
+ @classmethod
369
+ @validate_hf_hub_args
370
+ def from_pretrained(cls, pretrained_model_or_path, **kwargs):
371
+ r"""
372
+ Instantiates a text-to-image Pytorch diffusion pipeline from pretrained pipeline weight.
373
+
374
+ The from_pretrained() method takes care of returning the correct pipeline class instance by:
375
+ 1. Detect the pipeline class of the pretrained_model_or_path based on the _class_name property of its
376
+ config object
377
+ 2. Find the text-to-image pipeline linked to the pipeline class using pattern matching on pipeline class
378
+ name.
379
+
380
+ If a `controlnet` argument is passed, it will instantiate a [`StableDiffusionControlNetPipeline`] object.
381
+
382
+ The pipeline is set in evaluation mode (`model.eval()`) by default.
383
+
384
+ If you get the error message below, you need to finetune the weights for your downstream task:
385
+
386
+ ```
387
+ Some weights of UNet2DConditionModel were not initialized from the model checkpoint at stable-diffusion-v1-5/stable-diffusion-v1-5 and are newly initialized because the shapes did not match:
388
+ - conv_in.weight: found shape torch.Size([320, 4, 3, 3]) in the checkpoint and torch.Size([320, 9, 3, 3]) in the model instantiated
389
+ You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
390
+ ```
391
+
392
+ Parameters:
393
+ pretrained_model_or_path (`str` or `os.PathLike`, *optional*):
394
+ Can be either:
395
+
396
+ - A string, the *repo id* (for example `CompVis/ldm-text2im-large-256`) of a pretrained pipeline
397
+ hosted on the Hub.
398
+ - A path to a *directory* (for example `./my_pipeline_directory/`) containing pipeline weights
399
+ saved using
400
+ [`~DiffusionPipeline.save_pretrained`].
401
+ torch_dtype (`torch.dtype`, *optional*):
402
+ Override the default `torch.dtype` and load the model with another dtype.
403
+ force_download (`bool`, *optional*, defaults to `False`):
404
+ Whether or not to force the (re-)download of the model weights and configuration files, overriding the
405
+ cached versions if they exist.
406
+ cache_dir (`str | os.PathLike`, *optional*):
407
+ Path to a directory where a downloaded pretrained model configuration is cached if the standard cache
408
+ is not used.
409
+
410
+ proxies (`dict[str, str]`, *optional*):
411
+ A dictionary of proxy servers to use by protocol or endpoint, for example, `{'http': 'foo.bar:3128',
412
+ 'http://hostname': 'foo.bar:4012'}`. The proxies are used on each request.
413
+ output_loading_info(`bool`, *optional*, defaults to `False`):
414
+ Whether or not to also return a dictionary containing missing keys, unexpected keys and error messages.
415
+ local_files_only (`bool`, *optional*, defaults to `False`):
416
+ Whether to only load local model weights and configuration files or not. If set to `True`, the model
417
+ won't be downloaded from the Hub.
418
+ token (`str` or *bool*, *optional*):
419
+ The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
420
+ `diffusers-cli login` (stored in `~/.huggingface`) is used.
421
+ revision (`str`, *optional*, defaults to `"main"`):
422
+ The specific model version to use. It can be a branch name, a tag name, a commit id, or any identifier
423
+ allowed by Git.
424
+ custom_revision (`str`, *optional*, defaults to `"main"`):
425
+ The specific model version to use. It can be a branch name, a tag name, or a commit id similar to
426
+ `revision` when loading a custom pipeline from the Hub. It can be a 🤗 Diffusers version when loading a
427
+ custom pipeline from GitHub, otherwise it defaults to `"main"` when loading from the Hub.
428
+ mirror (`str`, *optional*):
429
+ Mirror source to resolve accessibility issues if you’re downloading a model in China. We do not
430
+ guarantee the timeliness or safety of the source, and you should refer to the mirror site for more
431
+ information.
432
+ device_map (`str` or `dict[str, int | str | torch.device]`, *optional*):
433
+ A map that specifies where each submodule should go. It doesn’t need to be defined for each
434
+ parameter/buffer name; once a given module name is inside, every submodule of it will be sent to the
435
+ same device.
436
+
437
+ Set `device_map="auto"` to have 🤗 Accelerate automatically compute the most optimized `device_map`. For
438
+ more information about each option see [designing a device
439
+ map](https://hf.co/docs/accelerate/main/en/usage_guides/big_modeling#designing-a-device-map).
440
+ max_memory (`Dict`, *optional*):
441
+ A dictionary device identifier for the maximum memory. Will default to the maximum memory available for
442
+ each GPU and the available CPU RAM if unset.
443
+ offload_folder (`str` or `os.PathLike`, *optional*):
444
+ The path to offload weights if device_map contains the value `"disk"`.
445
+ offload_state_dict (`bool`, *optional*):
446
+ If `True`, temporarily offloads the CPU state dict to the hard drive to avoid running out of CPU RAM if
447
+ the weight of the CPU state dict + the biggest shard of the checkpoint does not fit. Defaults to `True`
448
+ when there is some disk offload.
449
+ low_cpu_mem_usage (`bool`, *optional*, defaults to `True` if torch version >= 1.9.0 else `False`):
450
+ Speed up model loading only loading the pretrained weights and not initializing the weights. This also
451
+ tries to not use more than 1x model size in CPU memory (including peak memory) while loading the model.
452
+ Only supported for PyTorch >= 1.9.0. If you are using an older version of PyTorch, setting this
453
+ argument to `True` will raise an error.
454
+ use_safetensors (`bool`, *optional*, defaults to `None`):
455
+ If set to `None`, the safetensors weights are downloaded if they're available **and** if the
456
+ safetensors library is installed. If set to `True`, the model is forcibly loaded from safetensors
457
+ weights. If set to `False`, safetensors weights are not loaded.
458
+ kwargs (remaining dictionary of keyword arguments, *optional*):
459
+ Can be used to overwrite load and saveable variables (the pipeline components of the specific pipeline
460
+ class). The overwritten components are passed directly to the pipelines `__init__` method. See example
461
+ below for more information.
462
+ variant (`str`, *optional*):
463
+ Load weights from a specified variant filename such as `"fp16"` or `"ema"`. This is ignored when
464
+ loading `from_flax`.
465
+
466
+ > [!TIP] > To use private or [gated](https://huggingface.co/docs/hub/models-gated#gated-models) models, log-in
467
+ with `hf > auth login`.
468
+
469
+ Examples:
470
+
471
+ ```py
472
+ >>> from diffusers import AutoPipelineForText2Image
473
+
474
+ >>> pipeline = AutoPipelineForText2Image.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5")
475
+ >>> image = pipeline(prompt).images[0]
476
+ ```
477
+ """
478
+ cache_dir = kwargs.pop("cache_dir", None)
479
+ force_download = kwargs.pop("force_download", False)
480
+ proxies = kwargs.pop("proxies", None)
481
+ token = kwargs.pop("token", None)
482
+ local_files_only = kwargs.pop("local_files_only", False)
483
+ revision = kwargs.pop("revision", None)
484
+
485
+ load_config_kwargs = {
486
+ "cache_dir": cache_dir,
487
+ "force_download": force_download,
488
+ "proxies": proxies,
489
+ "token": token,
490
+ "local_files_only": local_files_only,
491
+ "revision": revision,
492
+ }
493
+
494
+ config = cls.load_config(pretrained_model_or_path, **load_config_kwargs)
495
+ orig_class_name = config["_class_name"]
496
+ if "ControlPipeline" in orig_class_name:
497
+ to_replace = "ControlPipeline"
498
+ else:
499
+ to_replace = "Pipeline"
500
+
501
+ if "controlnet" in kwargs:
502
+ if isinstance(kwargs["controlnet"], ControlNetUnionModel):
503
+ orig_class_name = config["_class_name"].replace(to_replace, "ControlNetUnionPipeline")
504
+ else:
505
+ orig_class_name = config["_class_name"].replace(to_replace, "ControlNetPipeline")
506
+ if "enable_pag" in kwargs:
507
+ enable_pag = kwargs.pop("enable_pag")
508
+ if enable_pag:
509
+ orig_class_name = orig_class_name.replace(to_replace, "PAGPipeline")
510
+
511
+ text_2_image_cls = _get_task_class(AUTO_TEXT2IMAGE_PIPELINES_MAPPING, orig_class_name)
512
+
513
+ kwargs = {**load_config_kwargs, **kwargs}
514
+ return text_2_image_cls.from_pretrained(pretrained_model_or_path, **kwargs)
515
+
516
+ @classmethod
517
+ def from_pipe(cls, pipeline, **kwargs):
518
+ r"""
519
+ Instantiates a text-to-image Pytorch diffusion pipeline from another instantiated diffusion pipeline class.
520
+
521
+ The from_pipe() method takes care of returning the correct pipeline class instance by finding the text-to-image
522
+ pipeline linked to the pipeline class using pattern matching on pipeline class name.
523
+
524
+ All the modules the pipeline contains will be used to initialize the new pipeline without reallocating
525
+ additional memory.
526
+
527
+ The pipeline is set in evaluation mode (`model.eval()`) by default.
528
+
529
+ Parameters:
530
+ pipeline (`DiffusionPipeline`):
531
+ an instantiated `DiffusionPipeline` object
532
+
533
+ ```py
534
+ >>> from diffusers import AutoPipelineForText2Image, AutoPipelineForImage2Image
535
+
536
+ >>> pipe_i2i = AutoPipelineForImage2Image.from_pretrained(
537
+ ... "stable-diffusion-v1-5/stable-diffusion-v1-5", requires_safety_checker=False
538
+ ... )
539
+
540
+ >>> pipe_t2i = AutoPipelineForText2Image.from_pipe(pipe_i2i)
541
+ >>> image = pipe_t2i(prompt).images[0]
542
+ ```
543
+ """
544
+
545
+ original_config = dict(pipeline.config)
546
+ original_cls_name = pipeline.__class__.__name__
547
+
548
+ # derive the pipeline class to instantiate
549
+ text_2_image_cls = _get_task_class(AUTO_TEXT2IMAGE_PIPELINES_MAPPING, original_cls_name)
550
+
551
+ if "controlnet" in kwargs:
552
+ if kwargs["controlnet"] is not None:
553
+ to_replace = "PAGPipeline" if "PAG" in text_2_image_cls.__name__ else "Pipeline"
554
+ text_2_image_cls = _get_task_class(
555
+ AUTO_TEXT2IMAGE_PIPELINES_MAPPING,
556
+ text_2_image_cls.__name__.replace("ControlNet", "").replace(to_replace, "ControlNet" + to_replace),
557
+ )
558
+ else:
559
+ text_2_image_cls = _get_task_class(
560
+ AUTO_TEXT2IMAGE_PIPELINES_MAPPING,
561
+ text_2_image_cls.__name__.replace("ControlNet", ""),
562
+ )
563
+
564
+ if "enable_pag" in kwargs:
565
+ enable_pag = kwargs.pop("enable_pag")
566
+ if enable_pag:
567
+ text_2_image_cls = _get_task_class(
568
+ AUTO_TEXT2IMAGE_PIPELINES_MAPPING,
569
+ text_2_image_cls.__name__.replace("PAG", "").replace("Pipeline", "PAGPipeline"),
570
+ )
571
+ else:
572
+ text_2_image_cls = _get_task_class(
573
+ AUTO_TEXT2IMAGE_PIPELINES_MAPPING,
574
+ text_2_image_cls.__name__.replace("PAG", ""),
575
+ )
576
+
577
+ # define expected module and optional kwargs given the pipeline signature
578
+ expected_modules, optional_kwargs = text_2_image_cls._get_signature_keys(text_2_image_cls)
579
+
580
+ pretrained_model_name_or_path = original_config.pop("_name_or_path", None)
581
+
582
+ # allow users pass modules in `kwargs` to override the original pipeline's components
583
+ passed_class_obj = {k: kwargs.pop(k) for k in expected_modules if k in kwargs}
584
+ original_class_obj = {
585
+ k: pipeline.components[k]
586
+ for k, v in pipeline.components.items()
587
+ if k in expected_modules and k not in passed_class_obj
588
+ }
589
+
590
+ # allow users pass optional kwargs to override the original pipelines config attribute
591
+ passed_pipe_kwargs = {k: kwargs.pop(k) for k in optional_kwargs if k in kwargs}
592
+ original_pipe_kwargs = {
593
+ k: original_config[k]
594
+ for k, v in original_config.items()
595
+ if k in optional_kwargs and k not in passed_pipe_kwargs
596
+ }
597
+
598
+ # config that were not expected by original pipeline is stored as private attribute
599
+ # we will pass them as optional arguments if they can be accepted by the pipeline
600
+ additional_pipe_kwargs = [
601
+ k[1:]
602
+ for k in original_config.keys()
603
+ if k.startswith("_") and k[1:] in optional_kwargs and k[1:] not in passed_pipe_kwargs
604
+ ]
605
+ for k in additional_pipe_kwargs:
606
+ original_pipe_kwargs[k] = original_config.pop(f"_{k}")
607
+
608
+ text_2_image_kwargs = {**passed_class_obj, **original_class_obj, **passed_pipe_kwargs, **original_pipe_kwargs}
609
+
610
+ # store unused config as private attribute
611
+ unused_original_config = {
612
+ f"{'' if k.startswith('_') else '_'}{k}": original_config[k]
613
+ for k, v in original_config.items()
614
+ if k not in text_2_image_kwargs
615
+ }
616
+
617
+ missing_modules = (
618
+ set(expected_modules) - set(text_2_image_cls._optional_components) - set(text_2_image_kwargs.keys())
619
+ )
620
+
621
+ if len(missing_modules) > 0:
622
+ raise ValueError(
623
+ f"Pipeline {text_2_image_cls} expected {expected_modules}, but only {set(list(passed_class_obj.keys()) + list(original_class_obj.keys()))} were passed"
624
+ )
625
+
626
+ model = text_2_image_cls(**text_2_image_kwargs)
627
+ model.register_to_config(_name_or_path=pretrained_model_name_or_path)
628
+ model.register_to_config(**unused_original_config)
629
+
630
+ return model
631
+
632
+
633
+ class AutoPipelineForImage2Image(ConfigMixin):
634
+ r"""
635
+
636
+ [`AutoPipelineForImage2Image`] is a generic pipeline class that instantiates an image-to-image pipeline class. The
637
+ specific underlying pipeline class is automatically selected from either the
638
+ [`~AutoPipelineForImage2Image.from_pretrained`] or [`~AutoPipelineForImage2Image.from_pipe`] methods.
639
+
640
+ This class cannot be instantiated using `__init__()` (throws an error).
641
+
642
+ Class attributes:
643
+
644
+ - **config_name** (`str`) -- The configuration filename that stores the class and module names of all the
645
+ diffusion pipeline's components.
646
+
647
+ """
648
+
649
+ config_name = "model_index.json"
650
+
651
+ def __init__(self, *args, **kwargs):
652
+ raise EnvironmentError(
653
+ f"{self.__class__.__name__} is designed to be instantiated "
654
+ f"using the `{self.__class__.__name__}.from_pretrained(pretrained_model_name_or_path)` or "
655
+ f"`{self.__class__.__name__}.from_pipe(pipeline)` methods."
656
+ )
657
+
658
+ @classmethod
659
+ @validate_hf_hub_args
660
+ def from_pretrained(cls, pretrained_model_or_path, **kwargs):
661
+ r"""
662
+ Instantiates a image-to-image Pytorch diffusion pipeline from pretrained pipeline weight.
663
+
664
+ The from_pretrained() method takes care of returning the correct pipeline class instance by:
665
+ 1. Detect the pipeline class of the pretrained_model_or_path based on the _class_name property of its
666
+ config object
667
+ 2. Find the image-to-image pipeline linked to the pipeline class using pattern matching on pipeline class
668
+ name.
669
+
670
+ If a `controlnet` argument is passed, it will instantiate a [`StableDiffusionControlNetImg2ImgPipeline`]
671
+ object.
672
+
673
+ The pipeline is set in evaluation mode (`model.eval()`) by default.
674
+
675
+ If you get the error message below, you need to finetune the weights for your downstream task:
676
+
677
+ ```
678
+ Some weights of UNet2DConditionModel were not initialized from the model checkpoint at stable-diffusion-v1-5/stable-diffusion-v1-5 and are newly initialized because the shapes did not match:
679
+ - conv_in.weight: found shape torch.Size([320, 4, 3, 3]) in the checkpoint and torch.Size([320, 9, 3, 3]) in the model instantiated
680
+ You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
681
+ ```
682
+
683
+ Parameters:
684
+ pretrained_model_or_path (`str` or `os.PathLike`, *optional*):
685
+ Can be either:
686
+
687
+ - A string, the *repo id* (for example `CompVis/ldm-text2im-large-256`) of a pretrained pipeline
688
+ hosted on the Hub.
689
+ - A path to a *directory* (for example `./my_pipeline_directory/`) containing pipeline weights
690
+ saved using
691
+ [`~DiffusionPipeline.save_pretrained`].
692
+ torch_dtype (`str` or `torch.dtype`, *optional*):
693
+ Override the default `torch.dtype` and load the model with another dtype.
694
+ force_download (`bool`, *optional*, defaults to `False`):
695
+ Whether or not to force the (re-)download of the model weights and configuration files, overriding the
696
+ cached versions if they exist.
697
+ cache_dir (`str | os.PathLike`, *optional*):
698
+ Path to a directory where a downloaded pretrained model configuration is cached if the standard cache
699
+ is not used.
700
+
701
+ proxies (`dict[str, str]`, *optional*):
702
+ A dictionary of proxy servers to use by protocol or endpoint, for example, `{'http': 'foo.bar:3128',
703
+ 'http://hostname': 'foo.bar:4012'}`. The proxies are used on each request.
704
+ output_loading_info(`bool`, *optional*, defaults to `False`):
705
+ Whether or not to also return a dictionary containing missing keys, unexpected keys and error messages.
706
+ local_files_only (`bool`, *optional*, defaults to `False`):
707
+ Whether to only load local model weights and configuration files or not. If set to `True`, the model
708
+ won't be downloaded from the Hub.
709
+ token (`str` or *bool*, *optional*):
710
+ The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
711
+ `diffusers-cli login` (stored in `~/.huggingface`) is used.
712
+ revision (`str`, *optional*, defaults to `"main"`):
713
+ The specific model version to use. It can be a branch name, a tag name, a commit id, or any identifier
714
+ allowed by Git.
715
+ custom_revision (`str`, *optional*, defaults to `"main"`):
716
+ The specific model version to use. It can be a branch name, a tag name, or a commit id similar to
717
+ `revision` when loading a custom pipeline from the Hub. It can be a 🤗 Diffusers version when loading a
718
+ custom pipeline from GitHub, otherwise it defaults to `"main"` when loading from the Hub.
719
+ mirror (`str`, *optional*):
720
+ Mirror source to resolve accessibility issues if you’re downloading a model in China. We do not
721
+ guarantee the timeliness or safety of the source, and you should refer to the mirror site for more
722
+ information.
723
+ device_map (`str` or `dict[str, int | str | torch.device]`, *optional*):
724
+ A map that specifies where each submodule should go. It doesn’t need to be defined for each
725
+ parameter/buffer name; once a given module name is inside, every submodule of it will be sent to the
726
+ same device.
727
+
728
+ Set `device_map="auto"` to have 🤗 Accelerate automatically compute the most optimized `device_map`. For
729
+ more information about each option see [designing a device
730
+ map](https://hf.co/docs/accelerate/main/en/usage_guides/big_modeling#designing-a-device-map).
731
+ max_memory (`Dict`, *optional*):
732
+ A dictionary device identifier for the maximum memory. Will default to the maximum memory available for
733
+ each GPU and the available CPU RAM if unset.
734
+ offload_folder (`str` or `os.PathLike`, *optional*):
735
+ The path to offload weights if device_map contains the value `"disk"`.
736
+ offload_state_dict (`bool`, *optional*):
737
+ If `True`, temporarily offloads the CPU state dict to the hard drive to avoid running out of CPU RAM if
738
+ the weight of the CPU state dict + the biggest shard of the checkpoint does not fit. Defaults to `True`
739
+ when there is some disk offload.
740
+ low_cpu_mem_usage (`bool`, *optional*, defaults to `True` if torch version >= 1.9.0 else `False`):
741
+ Speed up model loading only loading the pretrained weights and not initializing the weights. This also
742
+ tries to not use more than 1x model size in CPU memory (including peak memory) while loading the model.
743
+ Only supported for PyTorch >= 1.9.0. If you are using an older version of PyTorch, setting this
744
+ argument to `True` will raise an error.
745
+ use_safetensors (`bool`, *optional*, defaults to `None`):
746
+ If set to `None`, the safetensors weights are downloaded if they're available **and** if the
747
+ safetensors library is installed. If set to `True`, the model is forcibly loaded from safetensors
748
+ weights. If set to `False`, safetensors weights are not loaded.
749
+ kwargs (remaining dictionary of keyword arguments, *optional*):
750
+ Can be used to overwrite load and saveable variables (the pipeline components of the specific pipeline
751
+ class). The overwritten components are passed directly to the pipelines `__init__` method. See example
752
+ below for more information.
753
+ variant (`str`, *optional*):
754
+ Load weights from a specified variant filename such as `"fp16"` or `"ema"`. This is ignored when
755
+ loading `from_flax`.
756
+
757
+ > [!TIP] > To use private or [gated](https://huggingface.co/docs/hub/models-gated#gated-models) models, log-in
758
+ with `hf > auth login`.
759
+
760
+ Examples:
761
+
762
+ ```py
763
+ >>> from diffusers import AutoPipelineForImage2Image
764
+
765
+ >>> pipeline = AutoPipelineForImage2Image.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5")
766
+ >>> image = pipeline(prompt, image).images[0]
767
+ ```
768
+ """
769
+ cache_dir = kwargs.pop("cache_dir", None)
770
+ force_download = kwargs.pop("force_download", False)
771
+ proxies = kwargs.pop("proxies", None)
772
+ token = kwargs.pop("token", None)
773
+ local_files_only = kwargs.pop("local_files_only", False)
774
+ revision = kwargs.pop("revision", None)
775
+
776
+ load_config_kwargs = {
777
+ "cache_dir": cache_dir,
778
+ "force_download": force_download,
779
+ "proxies": proxies,
780
+ "token": token,
781
+ "local_files_only": local_files_only,
782
+ "revision": revision,
783
+ }
784
+
785
+ config = cls.load_config(pretrained_model_or_path, **load_config_kwargs)
786
+ orig_class_name = config["_class_name"]
787
+
788
+ # the `orig_class_name` can be:
789
+ # `- *Pipeline` (for regular text-to-image checkpoint)
790
+ # - `*ControlPipeline` (for Flux tools specific checkpoint)
791
+ # `- *Img2ImgPipeline` (for refiner checkpoint)
792
+ if "Img2Img" in orig_class_name:
793
+ to_replace = "Img2ImgPipeline"
794
+ elif "ControlPipeline" in orig_class_name:
795
+ to_replace = "ControlPipeline"
796
+ else:
797
+ to_replace = "Pipeline"
798
+
799
+ if "controlnet" in kwargs:
800
+ if isinstance(kwargs["controlnet"], ControlNetUnionModel):
801
+ orig_class_name = orig_class_name.replace(to_replace, "ControlNetUnion" + to_replace)
802
+ else:
803
+ orig_class_name = orig_class_name.replace(to_replace, "ControlNet" + to_replace)
804
+ if "enable_pag" in kwargs:
805
+ enable_pag = kwargs.pop("enable_pag")
806
+ if enable_pag:
807
+ orig_class_name = orig_class_name.replace(to_replace, "PAG" + to_replace)
808
+
809
+ if to_replace == "ControlPipeline":
810
+ orig_class_name = orig_class_name.replace(to_replace, "ControlImg2ImgPipeline")
811
+
812
+ image_2_image_cls = _get_task_class(AUTO_IMAGE2IMAGE_PIPELINES_MAPPING, orig_class_name)
813
+
814
+ kwargs = {**load_config_kwargs, **kwargs}
815
+ return image_2_image_cls.from_pretrained(pretrained_model_or_path, **kwargs)
816
+
817
+ @classmethod
818
+ def from_pipe(cls, pipeline, **kwargs):
819
+ r"""
820
+ Instantiates a image-to-image Pytorch diffusion pipeline from another instantiated diffusion pipeline class.
821
+
822
+ The from_pipe() method takes care of returning the correct pipeline class instance by finding the
823
+ image-to-image pipeline linked to the pipeline class using pattern matching on pipeline class name.
824
+
825
+ All the modules the pipeline contains will be used to initialize the new pipeline without reallocating
826
+ additional memory.
827
+
828
+ The pipeline is set in evaluation mode (`model.eval()`) by default.
829
+
830
+ Parameters:
831
+ pipeline (`DiffusionPipeline`):
832
+ an instantiated `DiffusionPipeline` object
833
+
834
+ Examples:
835
+
836
+ ```py
837
+ >>> from diffusers import AutoPipelineForText2Image, AutoPipelineForImage2Image
838
+
839
+ >>> pipe_t2i = AutoPipelineForText2Image.from_pretrained(
840
+ ... "stable-diffusion-v1-5/stable-diffusion-v1-5", requires_safety_checker=False
841
+ ... )
842
+
843
+ >>> pipe_i2i = AutoPipelineForImage2Image.from_pipe(pipe_t2i)
844
+ >>> image = pipe_i2i(prompt, image).images[0]
845
+ ```
846
+ """
847
+
848
+ original_config = dict(pipeline.config)
849
+ original_cls_name = pipeline.__class__.__name__
850
+
851
+ # derive the pipeline class to instantiate
852
+ image_2_image_cls = _get_task_class(AUTO_IMAGE2IMAGE_PIPELINES_MAPPING, original_cls_name)
853
+
854
+ if "controlnet" in kwargs:
855
+ if kwargs["controlnet"] is not None:
856
+ to_replace = "Img2ImgPipeline"
857
+ if "PAG" in image_2_image_cls.__name__:
858
+ to_replace = "PAG" + to_replace
859
+ image_2_image_cls = _get_task_class(
860
+ AUTO_IMAGE2IMAGE_PIPELINES_MAPPING,
861
+ image_2_image_cls.__name__.replace("ControlNet", "").replace(
862
+ to_replace, "ControlNet" + to_replace
863
+ ),
864
+ )
865
+ else:
866
+ image_2_image_cls = _get_task_class(
867
+ AUTO_IMAGE2IMAGE_PIPELINES_MAPPING,
868
+ image_2_image_cls.__name__.replace("ControlNet", ""),
869
+ )
870
+
871
+ if "enable_pag" in kwargs:
872
+ enable_pag = kwargs.pop("enable_pag")
873
+ if enable_pag:
874
+ image_2_image_cls = _get_task_class(
875
+ AUTO_IMAGE2IMAGE_PIPELINES_MAPPING,
876
+ image_2_image_cls.__name__.replace("PAG", "").replace("Img2ImgPipeline", "PAGImg2ImgPipeline"),
877
+ )
878
+ else:
879
+ image_2_image_cls = _get_task_class(
880
+ AUTO_IMAGE2IMAGE_PIPELINES_MAPPING,
881
+ image_2_image_cls.__name__.replace("PAG", ""),
882
+ )
883
+
884
+ # define expected module and optional kwargs given the pipeline signature
885
+ expected_modules, optional_kwargs = image_2_image_cls._get_signature_keys(image_2_image_cls)
886
+
887
+ pretrained_model_name_or_path = original_config.pop("_name_or_path", None)
888
+
889
+ # allow users pass modules in `kwargs` to override the original pipeline's components
890
+ passed_class_obj = {k: kwargs.pop(k) for k in expected_modules if k in kwargs}
891
+ original_class_obj = {
892
+ k: pipeline.components[k]
893
+ for k, v in pipeline.components.items()
894
+ if k in expected_modules and k not in passed_class_obj
895
+ }
896
+
897
+ # allow users pass optional kwargs to override the original pipelines config attribute
898
+ passed_pipe_kwargs = {k: kwargs.pop(k) for k in optional_kwargs if k in kwargs}
899
+ original_pipe_kwargs = {
900
+ k: original_config[k]
901
+ for k, v in original_config.items()
902
+ if k in optional_kwargs and k not in passed_pipe_kwargs
903
+ }
904
+
905
+ # config attribute that were not expected by original pipeline is stored as its private attribute
906
+ # we will pass them as optional arguments if they can be accepted by the pipeline
907
+ additional_pipe_kwargs = [
908
+ k[1:]
909
+ for k in original_config.keys()
910
+ if k.startswith("_") and k[1:] in optional_kwargs and k[1:] not in passed_pipe_kwargs
911
+ ]
912
+ for k in additional_pipe_kwargs:
913
+ original_pipe_kwargs[k] = original_config.pop(f"_{k}")
914
+
915
+ image_2_image_kwargs = {**passed_class_obj, **original_class_obj, **passed_pipe_kwargs, **original_pipe_kwargs}
916
+
917
+ # store unused config as private attribute
918
+ unused_original_config = {
919
+ f"{'' if k.startswith('_') else '_'}{k}": original_config[k]
920
+ for k, v in original_config.items()
921
+ if k not in image_2_image_kwargs
922
+ }
923
+
924
+ missing_modules = (
925
+ set(expected_modules) - set(image_2_image_cls._optional_components) - set(image_2_image_kwargs.keys())
926
+ )
927
+
928
+ if len(missing_modules) > 0:
929
+ raise ValueError(
930
+ f"Pipeline {image_2_image_cls} expected {expected_modules}, but only {set(list(passed_class_obj.keys()) + list(original_class_obj.keys()))} were passed"
931
+ )
932
+
933
+ model = image_2_image_cls(**image_2_image_kwargs)
934
+ model.register_to_config(_name_or_path=pretrained_model_name_or_path)
935
+ model.register_to_config(**unused_original_config)
936
+
937
+ return model
938
+
939
+
940
+ class AutoPipelineForInpainting(ConfigMixin):
941
+ r"""
942
+
943
+ [`AutoPipelineForInpainting`] is a generic pipeline class that instantiates an inpainting pipeline class. The
944
+ specific underlying pipeline class is automatically selected from either the
945
+ [`~AutoPipelineForInpainting.from_pretrained`] or [`~AutoPipelineForInpainting.from_pipe`] methods.
946
+
947
+ This class cannot be instantiated using `__init__()` (throws an error).
948
+
949
+ Class attributes:
950
+
951
+ - **config_name** (`str`) -- The configuration filename that stores the class and module names of all the
952
+ diffusion pipeline's components.
953
+
954
+ """
955
+
956
+ config_name = "model_index.json"
957
+
958
+ def __init__(self, *args, **kwargs):
959
+ raise EnvironmentError(
960
+ f"{self.__class__.__name__} is designed to be instantiated "
961
+ f"using the `{self.__class__.__name__}.from_pretrained(pretrained_model_name_or_path)` or "
962
+ f"`{self.__class__.__name__}.from_pipe(pipeline)` methods."
963
+ )
964
+
965
+ @classmethod
966
+ @validate_hf_hub_args
967
+ def from_pretrained(cls, pretrained_model_or_path, **kwargs):
968
+ r"""
969
+ Instantiates a inpainting Pytorch diffusion pipeline from pretrained pipeline weight.
970
+
971
+ The from_pretrained() method takes care of returning the correct pipeline class instance by:
972
+ 1. Detect the pipeline class of the pretrained_model_or_path based on the _class_name property of its
973
+ config object
974
+ 2. Find the inpainting pipeline linked to the pipeline class using pattern matching on pipeline class name.
975
+
976
+ If a `controlnet` argument is passed, it will instantiate a [`StableDiffusionControlNetInpaintPipeline`]
977
+ object.
978
+
979
+ The pipeline is set in evaluation mode (`model.eval()`) by default.
980
+
981
+ If you get the error message below, you need to finetune the weights for your downstream task:
982
+
983
+ ```
984
+ Some weights of UNet2DConditionModel were not initialized from the model checkpoint at stable-diffusion-v1-5/stable-diffusion-v1-5 and are newly initialized because the shapes did not match:
985
+ - conv_in.weight: found shape torch.Size([320, 4, 3, 3]) in the checkpoint and torch.Size([320, 9, 3, 3]) in the model instantiated
986
+ You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
987
+ ```
988
+
989
+ Parameters:
990
+ pretrained_model_or_path (`str` or `os.PathLike`, *optional*):
991
+ Can be either:
992
+
993
+ - A string, the *repo id* (for example `CompVis/ldm-text2im-large-256`) of a pretrained pipeline
994
+ hosted on the Hub.
995
+ - A path to a *directory* (for example `./my_pipeline_directory/`) containing pipeline weights
996
+ saved using
997
+ [`~DiffusionPipeline.save_pretrained`].
998
+ torch_dtype (`str` or `torch.dtype`, *optional*):
999
+ Override the default `torch.dtype` and load the model with another dtype.
1000
+ force_download (`bool`, *optional*, defaults to `False`):
1001
+ Whether or not to force the (re-)download of the model weights and configuration files, overriding the
1002
+ cached versions if they exist.
1003
+ cache_dir (`str | os.PathLike`, *optional*):
1004
+ Path to a directory where a downloaded pretrained model configuration is cached if the standard cache
1005
+ is not used.
1006
+
1007
+ proxies (`dict[str, str]`, *optional*):
1008
+ A dictionary of proxy servers to use by protocol or endpoint, for example, `{'http': 'foo.bar:3128',
1009
+ 'http://hostname': 'foo.bar:4012'}`. The proxies are used on each request.
1010
+ output_loading_info(`bool`, *optional*, defaults to `False`):
1011
+ Whether or not to also return a dictionary containing missing keys, unexpected keys and error messages.
1012
+ local_files_only (`bool`, *optional*, defaults to `False`):
1013
+ Whether to only load local model weights and configuration files or not. If set to `True`, the model
1014
+ won't be downloaded from the Hub.
1015
+ token (`str` or *bool*, *optional*):
1016
+ The token to use as HTTP bearer authorization for remote files. If `True`, the token generated from
1017
+ `diffusers-cli login` (stored in `~/.huggingface`) is used.
1018
+ revision (`str`, *optional*, defaults to `"main"`):
1019
+ The specific model version to use. It can be a branch name, a tag name, a commit id, or any identifier
1020
+ allowed by Git.
1021
+ custom_revision (`str`, *optional*, defaults to `"main"`):
1022
+ The specific model version to use. It can be a branch name, a tag name, or a commit id similar to
1023
+ `revision` when loading a custom pipeline from the Hub. It can be a 🤗 Diffusers version when loading a
1024
+ custom pipeline from GitHub, otherwise it defaults to `"main"` when loading from the Hub.
1025
+ mirror (`str`, *optional*):
1026
+ Mirror source to resolve accessibility issues if you’re downloading a model in China. We do not
1027
+ guarantee the timeliness or safety of the source, and you should refer to the mirror site for more
1028
+ information.
1029
+ device_map (`str` or `dict[str, int | str | torch.device]`, *optional*):
1030
+ A map that specifies where each submodule should go. It doesn’t need to be defined for each
1031
+ parameter/buffer name; once a given module name is inside, every submodule of it will be sent to the
1032
+ same device.
1033
+
1034
+ Set `device_map="auto"` to have 🤗 Accelerate automatically compute the most optimized `device_map`. For
1035
+ more information about each option see [designing a device
1036
+ map](https://hf.co/docs/accelerate/main/en/usage_guides/big_modeling#designing-a-device-map).
1037
+ max_memory (`Dict`, *optional*):
1038
+ A dictionary device identifier for the maximum memory. Will default to the maximum memory available for
1039
+ each GPU and the available CPU RAM if unset.
1040
+ offload_folder (`str` or `os.PathLike`, *optional*):
1041
+ The path to offload weights if device_map contains the value `"disk"`.
1042
+ offload_state_dict (`bool`, *optional*):
1043
+ If `True`, temporarily offloads the CPU state dict to the hard drive to avoid running out of CPU RAM if
1044
+ the weight of the CPU state dict + the biggest shard of the checkpoint does not fit. Defaults to `True`
1045
+ when there is some disk offload.
1046
+ low_cpu_mem_usage (`bool`, *optional*, defaults to `True` if torch version >= 1.9.0 else `False`):
1047
+ Speed up model loading only loading the pretrained weights and not initializing the weights. This also
1048
+ tries to not use more than 1x model size in CPU memory (including peak memory) while loading the model.
1049
+ Only supported for PyTorch >= 1.9.0. If you are using an older version of PyTorch, setting this
1050
+ argument to `True` will raise an error.
1051
+ use_safetensors (`bool`, *optional*, defaults to `None`):
1052
+ If set to `None`, the safetensors weights are downloaded if they're available **and** if the
1053
+ safetensors library is installed. If set to `True`, the model is forcibly loaded from safetensors
1054
+ weights. If set to `False`, safetensors weights are not loaded.
1055
+ kwargs (remaining dictionary of keyword arguments, *optional*):
1056
+ Can be used to overwrite load and saveable variables (the pipeline components of the specific pipeline
1057
+ class). The overwritten components are passed directly to the pipelines `__init__` method. See example
1058
+ below for more information.
1059
+ variant (`str`, *optional*):
1060
+ Load weights from a specified variant filename such as `"fp16"` or `"ema"`. This is ignored when
1061
+ loading `from_flax`.
1062
+
1063
+ > [!TIP] > To use private or [gated](https://huggingface.co/docs/hub/models-gated#gated-models) models, log-in
1064
+ with `hf > auth login`.
1065
+
1066
+ Examples:
1067
+
1068
+ ```py
1069
+ >>> from diffusers import AutoPipelineForInpainting
1070
+
1071
+ >>> pipeline = AutoPipelineForInpainting.from_pretrained("stable-diffusion-v1-5/stable-diffusion-v1-5")
1072
+ >>> image = pipeline(prompt, image=init_image, mask_image=mask_image).images[0]
1073
+ ```
1074
+ """
1075
+ cache_dir = kwargs.pop("cache_dir", None)
1076
+ force_download = kwargs.pop("force_download", False)
1077
+ proxies = kwargs.pop("proxies", None)
1078
+ token = kwargs.pop("token", None)
1079
+ local_files_only = kwargs.pop("local_files_only", False)
1080
+ revision = kwargs.pop("revision", None)
1081
+
1082
+ load_config_kwargs = {
1083
+ "cache_dir": cache_dir,
1084
+ "force_download": force_download,
1085
+ "proxies": proxies,
1086
+ "token": token,
1087
+ "local_files_only": local_files_only,
1088
+ "revision": revision,
1089
+ }
1090
+
1091
+ config = cls.load_config(pretrained_model_or_path, **load_config_kwargs)
1092
+ orig_class_name = config["_class_name"]
1093
+
1094
+ # The `orig_class_name`` can be:
1095
+ # `- *InpaintPipeline` (for inpaint-specific checkpoint)
1096
+ # - `*ControlPipeline` (for Flux tools specific checkpoint)
1097
+ # - or *Pipeline (for regular text-to-image checkpoint)
1098
+ if "Inpaint" in orig_class_name:
1099
+ to_replace = "InpaintPipeline"
1100
+ elif "ControlPipeline" in orig_class_name:
1101
+ to_replace = "ControlPipeline"
1102
+ else:
1103
+ to_replace = "Pipeline"
1104
+
1105
+ if "controlnet" in kwargs:
1106
+ if isinstance(kwargs["controlnet"], ControlNetUnionModel):
1107
+ orig_class_name = orig_class_name.replace(to_replace, "ControlNetUnion" + to_replace)
1108
+ else:
1109
+ orig_class_name = orig_class_name.replace(to_replace, "ControlNet" + to_replace)
1110
+ if "enable_pag" in kwargs:
1111
+ enable_pag = kwargs.pop("enable_pag")
1112
+ if enable_pag:
1113
+ orig_class_name = orig_class_name.replace(to_replace, "PAG" + to_replace)
1114
+ if to_replace == "ControlPipeline":
1115
+ orig_class_name = orig_class_name.replace(to_replace, "ControlInpaintPipeline")
1116
+ inpainting_cls = _get_task_class(AUTO_INPAINT_PIPELINES_MAPPING, orig_class_name)
1117
+
1118
+ kwargs = {**load_config_kwargs, **kwargs}
1119
+ return inpainting_cls.from_pretrained(pretrained_model_or_path, **kwargs)
1120
+
1121
+ @classmethod
1122
+ def from_pipe(cls, pipeline, **kwargs):
1123
+ r"""
1124
+ Instantiates a inpainting Pytorch diffusion pipeline from another instantiated diffusion pipeline class.
1125
+
1126
+ The from_pipe() method takes care of returning the correct pipeline class instance by finding the inpainting
1127
+ pipeline linked to the pipeline class using pattern matching on pipeline class name.
1128
+
1129
+ All the modules the pipeline class contain will be used to initialize the new pipeline without reallocating
1130
+ additional memory.
1131
+
1132
+ The pipeline is set in evaluation mode (`model.eval()`) by default.
1133
+
1134
+ Parameters:
1135
+ pipeline (`DiffusionPipeline`):
1136
+ an instantiated `DiffusionPipeline` object
1137
+
1138
+ Examples:
1139
+
1140
+ ```py
1141
+ >>> from diffusers import AutoPipelineForText2Image, AutoPipelineForInpainting
1142
+
1143
+ >>> pipe_t2i = AutoPipelineForText2Image.from_pretrained(
1144
+ ... "DeepFloyd/IF-I-XL-v1.0", requires_safety_checker=False
1145
+ ... )
1146
+
1147
+ >>> pipe_inpaint = AutoPipelineForInpainting.from_pipe(pipe_t2i)
1148
+ >>> image = pipe_inpaint(prompt, image=init_image, mask_image=mask_image).images[0]
1149
+ ```
1150
+ """
1151
+ original_config = dict(pipeline.config)
1152
+ original_cls_name = pipeline.__class__.__name__
1153
+
1154
+ # derive the pipeline class to instantiate
1155
+ inpainting_cls = _get_task_class(AUTO_INPAINT_PIPELINES_MAPPING, original_cls_name)
1156
+
1157
+ if "controlnet" in kwargs:
1158
+ if kwargs["controlnet"] is not None:
1159
+ inpainting_cls = _get_task_class(
1160
+ AUTO_INPAINT_PIPELINES_MAPPING,
1161
+ inpainting_cls.__name__.replace("ControlNet", "").replace(
1162
+ "InpaintPipeline", "ControlNetInpaintPipeline"
1163
+ ),
1164
+ )
1165
+ else:
1166
+ inpainting_cls = _get_task_class(
1167
+ AUTO_INPAINT_PIPELINES_MAPPING,
1168
+ inpainting_cls.__name__.replace("ControlNetInpaintPipeline", "InpaintPipeline"),
1169
+ )
1170
+
1171
+ if "enable_pag" in kwargs:
1172
+ enable_pag = kwargs.pop("enable_pag")
1173
+ if enable_pag:
1174
+ inpainting_cls = _get_task_class(
1175
+ AUTO_INPAINT_PIPELINES_MAPPING,
1176
+ inpainting_cls.__name__.replace("PAG", "").replace("InpaintPipeline", "PAGInpaintPipeline"),
1177
+ )
1178
+ else:
1179
+ inpainting_cls = _get_task_class(
1180
+ AUTO_INPAINT_PIPELINES_MAPPING,
1181
+ inpainting_cls.__name__.replace("PAGInpaintPipeline", "InpaintPipeline"),
1182
+ )
1183
+
1184
+ # define expected module and optional kwargs given the pipeline signature
1185
+ expected_modules, optional_kwargs = inpainting_cls._get_signature_keys(inpainting_cls)
1186
+
1187
+ pretrained_model_name_or_path = original_config.pop("_name_or_path", None)
1188
+
1189
+ # allow users pass modules in `kwargs` to override the original pipeline's components
1190
+ passed_class_obj = {k: kwargs.pop(k) for k in expected_modules if k in kwargs}
1191
+ original_class_obj = {
1192
+ k: pipeline.components[k]
1193
+ for k, v in pipeline.components.items()
1194
+ if k in expected_modules and k not in passed_class_obj
1195
+ }
1196
+
1197
+ # allow users pass optional kwargs to override the original pipelines config attribute
1198
+ passed_pipe_kwargs = {k: kwargs.pop(k) for k in optional_kwargs if k in kwargs}
1199
+ original_pipe_kwargs = {
1200
+ k: original_config[k]
1201
+ for k, v in original_config.items()
1202
+ if k in optional_kwargs and k not in passed_pipe_kwargs
1203
+ }
1204
+
1205
+ # config that were not expected by original pipeline is stored as private attribute
1206
+ # we will pass them as optional arguments if they can be accepted by the pipeline
1207
+ additional_pipe_kwargs = [
1208
+ k[1:]
1209
+ for k in original_config.keys()
1210
+ if k.startswith("_") and k[1:] in optional_kwargs and k[1:] not in passed_pipe_kwargs
1211
+ ]
1212
+ for k in additional_pipe_kwargs:
1213
+ original_pipe_kwargs[k] = original_config.pop(f"_{k}")
1214
+
1215
+ inpainting_kwargs = {**passed_class_obj, **original_class_obj, **passed_pipe_kwargs, **original_pipe_kwargs}
1216
+
1217
+ # store unused config as private attribute
1218
+ unused_original_config = {
1219
+ f"{'' if k.startswith('_') else '_'}{k}": original_config[k]
1220
+ for k, v in original_config.items()
1221
+ if k not in inpainting_kwargs
1222
+ }
1223
+
1224
+ missing_modules = (
1225
+ set(expected_modules) - set(inpainting_cls._optional_components) - set(inpainting_kwargs.keys())
1226
+ )
1227
+
1228
+ if len(missing_modules) > 0:
1229
+ raise ValueError(
1230
+ f"Pipeline {inpainting_cls} expected {expected_modules}, but only {set(list(passed_class_obj.keys()) + list(original_class_obj.keys()))} were passed"
1231
+ )
1232
+
1233
+ model = inpainting_cls(**inpainting_kwargs)
1234
+ model.register_to_config(_name_or_path=pretrained_model_name_or_path)
1235
+ model.register_to_config(**unused_original_config)
1236
+
1237
+ return model
third_party/diffusers/src/diffusers/pipelines/chroma/__init__.py ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import TYPE_CHECKING
2
+
3
+ from ...utils import (
4
+ DIFFUSERS_SLOW_IMPORT,
5
+ OptionalDependencyNotAvailable,
6
+ _LazyModule,
7
+ get_objects_from_module,
8
+ is_torch_available,
9
+ is_transformers_available,
10
+ )
11
+
12
+
13
+ _dummy_objects = {}
14
+ _additional_imports = {}
15
+ _import_structure = {"pipeline_output": ["ChromaPipelineOutput"]}
16
+
17
+ try:
18
+ if not (is_transformers_available() and is_torch_available()):
19
+ raise OptionalDependencyNotAvailable()
20
+ except OptionalDependencyNotAvailable:
21
+ from ...utils import dummy_torch_and_transformers_objects # noqa F403
22
+
23
+ _dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
24
+ else:
25
+ _import_structure["pipeline_chroma"] = ["ChromaPipeline"]
26
+ _import_structure["pipeline_chroma_img2img"] = ["ChromaImg2ImgPipeline"]
27
+ _import_structure["pipeline_chroma_inpainting"] = ["ChromaInpaintPipeline"]
28
+ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
29
+ try:
30
+ if not (is_transformers_available() and is_torch_available()):
31
+ raise OptionalDependencyNotAvailable()
32
+ except OptionalDependencyNotAvailable:
33
+ from ...utils.dummy_torch_and_transformers_objects import * # noqa F403
34
+ else:
35
+ from .pipeline_chroma import ChromaPipeline
36
+ from .pipeline_chroma_img2img import ChromaImg2ImgPipeline
37
+ from .pipeline_chroma_inpainting import ChromaInpaintPipeline
38
+ else:
39
+ import sys
40
+
41
+ sys.modules[__name__] = _LazyModule(
42
+ __name__,
43
+ globals()["__file__"],
44
+ _import_structure,
45
+ module_spec=__spec__,
46
+ )
47
+
48
+ for name, value in _dummy_objects.items():
49
+ setattr(sys.modules[__name__], name, value)
50
+ for name, value in _additional_imports.items():
51
+ setattr(sys.modules[__name__], name, value)
third_party/diffusers/src/diffusers/pipelines/chroma/pipeline_chroma.py ADDED
@@ -0,0 +1,976 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 Black Forest Labs and The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ from typing import Any, Callable
17
+
18
+ import numpy as np
19
+ import torch
20
+ from transformers import CLIPImageProcessor, CLIPVisionModelWithProjection, T5EncoderModel, T5TokenizerFast
21
+
22
+ from ...image_processor import PipelineImageInput, VaeImageProcessor
23
+ from ...loaders import FluxIPAdapterMixin, FluxLoraLoaderMixin, FromSingleFileMixin, TextualInversionLoaderMixin
24
+ from ...models import AutoencoderKL, ChromaTransformer2DModel
25
+ from ...schedulers import FlowMatchEulerDiscreteScheduler
26
+ from ...utils import (
27
+ USE_PEFT_BACKEND,
28
+ deprecate,
29
+ is_torch_xla_available,
30
+ logging,
31
+ replace_example_docstring,
32
+ scale_lora_layers,
33
+ unscale_lora_layers,
34
+ )
35
+ from ...utils.torch_utils import randn_tensor
36
+ from ..pipeline_utils import DiffusionPipeline
37
+ from .pipeline_output import ChromaPipelineOutput
38
+
39
+
40
+ if is_torch_xla_available():
41
+ import torch_xla.core.xla_model as xm
42
+
43
+ XLA_AVAILABLE = True
44
+ else:
45
+ XLA_AVAILABLE = False
46
+
47
+
48
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
49
+
50
+ EXAMPLE_DOC_STRING = """
51
+ Examples:
52
+ ```py
53
+ >>> import torch
54
+ >>> from diffusers import ChromaPipeline
55
+
56
+ >>> model_id = "lodestones/Chroma1-HD"
57
+ >>> ckpt_path = "https://huggingface.co/lodestones/Chroma1-HD/blob/main/Chroma1-HD.safetensors"
58
+ >>> transformer = ChromaTransformer2DModel.from_single_file(ckpt_path, torch_dtype=torch.bfloat16)
59
+ >>> pipe = ChromaPipeline.from_pretrained(
60
+ ... model_id,
61
+ ... transformer=transformer,
62
+ ... torch_dtype=torch.bfloat16,
63
+ ... )
64
+ >>> pipe.enable_model_cpu_offload()
65
+ >>> prompt = [
66
+ ... "A high-fashion close-up portrait of a blonde woman in clear sunglasses. The image uses a bold teal and red color split for dramatic lighting. The background is a simple teal-green. The photo is sharp and well-composed, and is designed for viewing with anaglyph 3D glasses for optimal effect. It looks professionally done."
67
+ ... ]
68
+ >>> negative_prompt = [
69
+ ... "low quality, ugly, unfinished, out of focus, deformed, disfigure, blurry, smudged, restricted palette, flat colors"
70
+ ... ]
71
+ >>> image = pipe(prompt, negative_prompt=negative_prompt).images[0]
72
+ >>> image.save("chroma.png")
73
+ ```
74
+ """
75
+
76
+
77
+ # Copied from diffusers.pipelines.flux.pipeline_flux.calculate_shift
78
+ def calculate_shift(
79
+ image_seq_len,
80
+ base_seq_len: int = 256,
81
+ max_seq_len: int = 4096,
82
+ base_shift: float = 0.5,
83
+ max_shift: float = 1.15,
84
+ ):
85
+ m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
86
+ b = base_shift - m * base_seq_len
87
+ mu = image_seq_len * m + b
88
+ return mu
89
+
90
+
91
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
92
+ def retrieve_timesteps(
93
+ scheduler,
94
+ num_inference_steps: int | None = None,
95
+ device: str | torch.device | None = None,
96
+ timesteps: list[int] | None = None,
97
+ sigmas: list[float] | None = None,
98
+ **kwargs,
99
+ ):
100
+ r"""
101
+ Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
102
+ custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
103
+
104
+ Args:
105
+ scheduler (`SchedulerMixin`):
106
+ The scheduler to get timesteps from.
107
+ num_inference_steps (`int`):
108
+ The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
109
+ must be `None`.
110
+ device (`str` or `torch.device`, *optional*):
111
+ The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
112
+ timesteps (`list[int]`, *optional*):
113
+ Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
114
+ `num_inference_steps` and `sigmas` must be `None`.
115
+ sigmas (`list[float]`, *optional*):
116
+ Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
117
+ `num_inference_steps` and `timesteps` must be `None`.
118
+
119
+ Returns:
120
+ `tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
121
+ second element is the number of inference steps.
122
+ """
123
+ if timesteps is not None and sigmas is not None:
124
+ raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
125
+ if timesteps is not None:
126
+ accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
127
+ if not accepts_timesteps:
128
+ raise ValueError(
129
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
130
+ f" timestep schedules. Please check whether you are using the correct scheduler."
131
+ )
132
+ scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
133
+ timesteps = scheduler.timesteps
134
+ num_inference_steps = len(timesteps)
135
+ elif sigmas is not None:
136
+ accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
137
+ if not accept_sigmas:
138
+ raise ValueError(
139
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
140
+ f" sigmas schedules. Please check whether you are using the correct scheduler."
141
+ )
142
+ scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
143
+ timesteps = scheduler.timesteps
144
+ num_inference_steps = len(timesteps)
145
+ else:
146
+ scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
147
+ timesteps = scheduler.timesteps
148
+ return timesteps, num_inference_steps
149
+
150
+
151
+ class ChromaPipeline(
152
+ DiffusionPipeline,
153
+ FluxLoraLoaderMixin,
154
+ FromSingleFileMixin,
155
+ TextualInversionLoaderMixin,
156
+ FluxIPAdapterMixin,
157
+ ):
158
+ r"""
159
+ The Chroma pipeline for text-to-image generation.
160
+
161
+ Reference: https://huggingface.co/lodestones/Chroma1-HD/
162
+
163
+ Args:
164
+ transformer ([`ChromaTransformer2DModel`]):
165
+ Conditional Transformer (MMDiT) architecture to denoise the encoded image latents.
166
+ scheduler ([`FlowMatchEulerDiscreteScheduler`]):
167
+ A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
168
+ vae ([`AutoencoderKL`]):
169
+ Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representation
170
+ text_encoder ([`T5EncoderModel`]):
171
+ [T5](https://huggingface.co/docs/transformers/en/model_doc/t5#transformers.T5EncoderModel), specifically
172
+ the [google/t5-v1_1-xxl](https://huggingface.co/google/t5-v1_1-xxl) variant.
173
+ tokenizer (`T5TokenizerFast`):
174
+ Second Tokenizer of class
175
+ [T5TokenizerFast](https://huggingface.co/docs/transformers/en/model_doc/t5#transformers.T5TokenizerFast).
176
+ """
177
+
178
+ model_cpu_offload_seq = "text_encoder->image_encoder->transformer->vae"
179
+ _optional_components = ["image_encoder", "feature_extractor"]
180
+ _callback_tensor_inputs = ["latents", "prompt_embeds"]
181
+
182
+ def __init__(
183
+ self,
184
+ scheduler: FlowMatchEulerDiscreteScheduler,
185
+ vae: AutoencoderKL,
186
+ text_encoder: T5EncoderModel,
187
+ tokenizer: T5TokenizerFast,
188
+ transformer: ChromaTransformer2DModel,
189
+ image_encoder: CLIPVisionModelWithProjection = None,
190
+ feature_extractor: CLIPImageProcessor = None,
191
+ ):
192
+ super().__init__()
193
+
194
+ self.register_modules(
195
+ vae=vae,
196
+ text_encoder=text_encoder,
197
+ tokenizer=tokenizer,
198
+ transformer=transformer,
199
+ scheduler=scheduler,
200
+ image_encoder=image_encoder,
201
+ feature_extractor=feature_extractor,
202
+ )
203
+ self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) if getattr(self, "vae", None) else 8
204
+ # Flux latents are turned into 2x2 patches and packed. This means the latent width and height has to be divisible
205
+ # by the patch size. So the vae scale factor is multiplied by the patch size to account for this
206
+ self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor * 2)
207
+ self.default_sample_size = 128
208
+
209
+ def _get_t5_prompt_embeds(
210
+ self,
211
+ prompt: str | list[str] = None,
212
+ num_images_per_prompt: int = 1,
213
+ max_sequence_length: int = 512,
214
+ device: torch.device | None = None,
215
+ dtype: torch.dtype | None = None,
216
+ ):
217
+ device = device or self._execution_device
218
+ dtype = dtype or self.text_encoder.dtype
219
+
220
+ prompt = [prompt] if isinstance(prompt, str) else prompt
221
+ batch_size = len(prompt)
222
+
223
+ if isinstance(self, TextualInversionLoaderMixin):
224
+ prompt = self.maybe_convert_prompt(prompt, self.tokenizer)
225
+
226
+ text_inputs = self.tokenizer(
227
+ prompt,
228
+ padding="max_length",
229
+ max_length=max_sequence_length,
230
+ truncation=True,
231
+ return_length=False,
232
+ return_overflowing_tokens=False,
233
+ return_tensors="pt",
234
+ )
235
+ text_input_ids = text_inputs.input_ids
236
+ tokenizer_mask = text_inputs.attention_mask
237
+
238
+ tokenizer_mask_device = tokenizer_mask.to(device)
239
+
240
+ # unlike FLUX, Chroma uses the attention mask when generating the T5 embedding
241
+ prompt_embeds = self.text_encoder(
242
+ text_input_ids.to(device),
243
+ output_hidden_states=False,
244
+ attention_mask=tokenizer_mask_device,
245
+ )[0]
246
+
247
+ prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
248
+
249
+ # for the text tokens, chroma requires that all except the first padding token are masked out during the forward pass through the transformer
250
+ seq_lengths = tokenizer_mask_device.sum(dim=1)
251
+ mask_indices = torch.arange(tokenizer_mask_device.size(1), device=device).unsqueeze(0).expand(batch_size, -1)
252
+ attention_mask = (mask_indices <= seq_lengths.unsqueeze(1)).to(dtype=dtype, device=device)
253
+
254
+ _, seq_len, _ = prompt_embeds.shape
255
+
256
+ # duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method
257
+ prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
258
+ prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
259
+
260
+ attention_mask = attention_mask.repeat(1, num_images_per_prompt)
261
+ attention_mask = attention_mask.view(batch_size * num_images_per_prompt, seq_len)
262
+
263
+ return prompt_embeds, attention_mask
264
+
265
+ def encode_prompt(
266
+ self,
267
+ prompt: str | list[str],
268
+ negative_prompt: str | list[str] = None,
269
+ device: torch.device | None = None,
270
+ num_images_per_prompt: int = 1,
271
+ prompt_embeds: torch.Tensor | None = None,
272
+ negative_prompt_embeds: torch.Tensor | None = None,
273
+ prompt_attention_mask: torch.Tensor | None = None,
274
+ negative_prompt_attention_mask: torch.Tensor | None = None,
275
+ do_classifier_free_guidance: bool = True,
276
+ max_sequence_length: int = 512,
277
+ lora_scale: float | None = None,
278
+ ):
279
+ r"""
280
+
281
+ Args:
282
+ prompt (`str` or `list[str]`, *optional*):
283
+ prompt to be encoded
284
+ negative_prompt (`str` or `list[str]`, *optional*):
285
+ The prompt not to guide the image generation. If not defined, one has to pass `negative_prompt_embeds`
286
+ instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is less than `1`).
287
+ device: (`torch.device`):
288
+ torch device
289
+ num_images_per_prompt (`int`):
290
+ number of images that should be generated per prompt
291
+ prompt_embeds (`torch.Tensor`, *optional*):
292
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
293
+ provided, text embeddings will be generated from `prompt` input argument.
294
+ lora_scale (`float`, *optional*):
295
+ A lora scale that will be applied to all LoRA layers of the text encoder if LoRA layers are loaded.
296
+ """
297
+ device = device or self._execution_device
298
+
299
+ # set lora scale so that monkey patched LoRA
300
+ # function of text encoder can correctly access it
301
+ if lora_scale is not None and isinstance(self, FluxLoraLoaderMixin):
302
+ self._lora_scale = lora_scale
303
+
304
+ # dynamically adjust the LoRA scale
305
+ if self.text_encoder is not None and USE_PEFT_BACKEND:
306
+ scale_lora_layers(self.text_encoder, lora_scale)
307
+
308
+ prompt = [prompt] if isinstance(prompt, str) else prompt
309
+
310
+ if prompt is not None:
311
+ batch_size = len(prompt)
312
+ else:
313
+ batch_size = prompt_embeds.shape[0]
314
+
315
+ if prompt_embeds is None:
316
+ prompt_embeds, prompt_attention_mask = self._get_t5_prompt_embeds(
317
+ prompt=prompt,
318
+ num_images_per_prompt=num_images_per_prompt,
319
+ max_sequence_length=max_sequence_length,
320
+ device=device,
321
+ )
322
+
323
+ dtype = self.text_encoder.dtype if self.text_encoder is not None else self.transformer.dtype
324
+ text_ids = torch.zeros(prompt_embeds.shape[1], 3).to(device=device, dtype=dtype)
325
+ negative_text_ids = None
326
+
327
+ if do_classifier_free_guidance:
328
+ if negative_prompt_embeds is None:
329
+ negative_prompt = negative_prompt or ""
330
+ negative_prompt = (
331
+ batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
332
+ )
333
+
334
+ if prompt is not None and type(prompt) is not type(negative_prompt):
335
+ raise TypeError(
336
+ f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
337
+ f" {type(prompt)}."
338
+ )
339
+ elif batch_size != len(negative_prompt):
340
+ raise ValueError(
341
+ f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
342
+ f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
343
+ " the batch size of `prompt`."
344
+ )
345
+
346
+ negative_prompt_embeds, negative_prompt_attention_mask = self._get_t5_prompt_embeds(
347
+ prompt=negative_prompt,
348
+ num_images_per_prompt=num_images_per_prompt,
349
+ max_sequence_length=max_sequence_length,
350
+ device=device,
351
+ )
352
+
353
+ negative_text_ids = torch.zeros(negative_prompt_embeds.shape[1], 3).to(device=device, dtype=dtype)
354
+
355
+ if self.text_encoder is not None:
356
+ if isinstance(self, FluxLoraLoaderMixin) and USE_PEFT_BACKEND:
357
+ # Retrieve the original scale by scaling back the LoRA layers
358
+ unscale_lora_layers(self.text_encoder, lora_scale)
359
+
360
+ return (
361
+ prompt_embeds,
362
+ text_ids,
363
+ prompt_attention_mask,
364
+ negative_prompt_embeds,
365
+ negative_text_ids,
366
+ negative_prompt_attention_mask,
367
+ )
368
+
369
+ # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.encode_image
370
+ def encode_image(self, image, device, num_images_per_prompt):
371
+ dtype = next(self.image_encoder.parameters()).dtype
372
+
373
+ if not isinstance(image, torch.Tensor):
374
+ image = self.feature_extractor(image, return_tensors="pt").pixel_values
375
+
376
+ image = image.to(device=device, dtype=dtype)
377
+ image_embeds = self.image_encoder(image).image_embeds
378
+ image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
379
+ return image_embeds
380
+
381
+ # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.prepare_ip_adapter_image_embeds
382
+ def prepare_ip_adapter_image_embeds(
383
+ self, ip_adapter_image, ip_adapter_image_embeds, device, num_images_per_prompt
384
+ ):
385
+ image_embeds = []
386
+ if ip_adapter_image_embeds is None:
387
+ if not isinstance(ip_adapter_image, list):
388
+ ip_adapter_image = [ip_adapter_image]
389
+
390
+ if len(ip_adapter_image) != self.transformer.encoder_hid_proj.num_ip_adapters:
391
+ raise ValueError(
392
+ f"`ip_adapter_image` must have same length as the number of IP Adapters. Got {len(ip_adapter_image)} images and {self.transformer.encoder_hid_proj.num_ip_adapters} IP Adapters."
393
+ )
394
+
395
+ for single_ip_adapter_image in ip_adapter_image:
396
+ single_image_embeds = self.encode_image(single_ip_adapter_image, device, 1)
397
+ image_embeds.append(single_image_embeds[None, :])
398
+ else:
399
+ if not isinstance(ip_adapter_image_embeds, list):
400
+ ip_adapter_image_embeds = [ip_adapter_image_embeds]
401
+
402
+ if len(ip_adapter_image_embeds) != self.transformer.encoder_hid_proj.num_ip_adapters:
403
+ raise ValueError(
404
+ f"`ip_adapter_image_embeds` must have same length as the number of IP Adapters. Got {len(ip_adapter_image_embeds)} image embeds and {self.transformer.encoder_hid_proj.num_ip_adapters} IP Adapters."
405
+ )
406
+
407
+ for single_image_embeds in ip_adapter_image_embeds:
408
+ image_embeds.append(single_image_embeds)
409
+
410
+ ip_adapter_image_embeds = []
411
+ for single_image_embeds in image_embeds:
412
+ single_image_embeds = torch.cat([single_image_embeds] * num_images_per_prompt, dim=0)
413
+ single_image_embeds = single_image_embeds.to(device=device)
414
+ ip_adapter_image_embeds.append(single_image_embeds)
415
+
416
+ return ip_adapter_image_embeds
417
+
418
+ def check_inputs(
419
+ self,
420
+ prompt,
421
+ height,
422
+ width,
423
+ negative_prompt=None,
424
+ prompt_embeds=None,
425
+ prompt_attention_mask=None,
426
+ negative_prompt_embeds=None,
427
+ negative_prompt_attention_mask=None,
428
+ callback_on_step_end_tensor_inputs=None,
429
+ max_sequence_length=None,
430
+ ):
431
+ if height % (self.vae_scale_factor * 2) != 0 or width % (self.vae_scale_factor * 2) != 0:
432
+ logger.warning(
433
+ f"`height` and `width` have to be divisible by {self.vae_scale_factor * 2} but are {height} and {width}. Dimensions will be resized accordingly"
434
+ )
435
+
436
+ if callback_on_step_end_tensor_inputs is not None and not all(
437
+ k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
438
+ ):
439
+ raise ValueError(
440
+ f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
441
+ )
442
+
443
+ if prompt is not None and prompt_embeds is not None:
444
+ raise ValueError(
445
+ f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
446
+ " only forward one of the two."
447
+ )
448
+ elif prompt is None and prompt_embeds is None:
449
+ raise ValueError(
450
+ "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
451
+ )
452
+ elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
453
+ raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
454
+
455
+ if negative_prompt is not None and negative_prompt_embeds is not None:
456
+ raise ValueError(
457
+ f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"
458
+ f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
459
+ )
460
+
461
+ if prompt_embeds is not None and prompt_attention_mask is None:
462
+ raise ValueError("Cannot provide `prompt_embeds` without also providing `prompt_attention_mask")
463
+
464
+ if negative_prompt_embeds is not None and negative_prompt_attention_mask is None:
465
+ raise ValueError(
466
+ "Cannot provide `negative_prompt_embeds` without also providing `negative_prompt_attention_mask"
467
+ )
468
+
469
+ if max_sequence_length is not None and max_sequence_length > 512:
470
+ raise ValueError(f"`max_sequence_length` cannot be greater than 512 but is {max_sequence_length}")
471
+
472
+ @staticmethod
473
+ def _prepare_latent_image_ids(batch_size, height, width, device, dtype):
474
+ latent_image_ids = torch.zeros(height, width, 3)
475
+ latent_image_ids[..., 1] = latent_image_ids[..., 1] + torch.arange(height)[:, None]
476
+ latent_image_ids[..., 2] = latent_image_ids[..., 2] + torch.arange(width)[None, :]
477
+
478
+ latent_image_id_height, latent_image_id_width, latent_image_id_channels = latent_image_ids.shape
479
+
480
+ latent_image_ids = latent_image_ids.reshape(
481
+ latent_image_id_height * latent_image_id_width, latent_image_id_channels
482
+ )
483
+
484
+ return latent_image_ids.to(device=device, dtype=dtype)
485
+
486
+ @staticmethod
487
+ def _pack_latents(latents, batch_size, num_channels_latents, height, width):
488
+ latents = latents.view(batch_size, num_channels_latents, height // 2, 2, width // 2, 2)
489
+ latents = latents.permute(0, 2, 4, 1, 3, 5)
490
+ latents = latents.reshape(batch_size, (height // 2) * (width // 2), num_channels_latents * 4)
491
+
492
+ return latents
493
+
494
+ @staticmethod
495
+ def _unpack_latents(latents, height, width, vae_scale_factor):
496
+ batch_size, num_patches, channels = latents.shape
497
+
498
+ # VAE applies 8x compression on images but we must also account for packing which requires
499
+ # latent height and width to be divisible by 2.
500
+ height = 2 * (int(height) // (vae_scale_factor * 2))
501
+ width = 2 * (int(width) // (vae_scale_factor * 2))
502
+
503
+ latents = latents.view(batch_size, height // 2, width // 2, channels // 4, 2, 2)
504
+ latents = latents.permute(0, 3, 1, 4, 2, 5)
505
+
506
+ latents = latents.reshape(batch_size, channels // (2 * 2), height, width)
507
+
508
+ return latents
509
+
510
+ def enable_vae_slicing(self):
511
+ r"""
512
+ Enable sliced VAE decoding. When this option is enabled, the VAE will split the input tensor in slices to
513
+ compute decoding in several steps. This is useful to save some memory and allow larger batch sizes.
514
+ """
515
+ depr_message = f"Calling `enable_vae_slicing()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.enable_slicing()`."
516
+ deprecate(
517
+ "enable_vae_slicing",
518
+ "0.40.0",
519
+ depr_message,
520
+ )
521
+ self.vae.enable_slicing()
522
+
523
+ def disable_vae_slicing(self):
524
+ r"""
525
+ Disable sliced VAE decoding. If `enable_vae_slicing` was previously enabled, this method will go back to
526
+ computing decoding in one step.
527
+ """
528
+ depr_message = f"Calling `disable_vae_slicing()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.disable_slicing()`."
529
+ deprecate(
530
+ "disable_vae_slicing",
531
+ "0.40.0",
532
+ depr_message,
533
+ )
534
+ self.vae.disable_slicing()
535
+
536
+ def enable_vae_tiling(self):
537
+ r"""
538
+ Enable tiled VAE decoding. When this option is enabled, the VAE will split the input tensor into tiles to
539
+ compute decoding and encoding in several steps. This is useful for saving a large amount of memory and to allow
540
+ processing larger images.
541
+ """
542
+ depr_message = f"Calling `enable_vae_tiling()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.enable_tiling()`."
543
+ deprecate(
544
+ "enable_vae_tiling",
545
+ "0.40.0",
546
+ depr_message,
547
+ )
548
+ self.vae.enable_tiling()
549
+
550
+ def disable_vae_tiling(self):
551
+ r"""
552
+ Disable tiled VAE decoding. If `enable_vae_tiling` was previously enabled, this method will go back to
553
+ computing decoding in one step.
554
+ """
555
+ depr_message = f"Calling `disable_vae_tiling()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.disable_tiling()`."
556
+ deprecate(
557
+ "disable_vae_tiling",
558
+ "0.40.0",
559
+ depr_message,
560
+ )
561
+ self.vae.disable_tiling()
562
+
563
+ # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.prepare_latents
564
+ def prepare_latents(
565
+ self,
566
+ batch_size,
567
+ num_channels_latents,
568
+ height,
569
+ width,
570
+ dtype,
571
+ device,
572
+ generator,
573
+ latents=None,
574
+ ):
575
+ # VAE applies 8x compression on images but we must also account for packing which requires
576
+ # latent height and width to be divisible by 2.
577
+ height = 2 * (int(height) // (self.vae_scale_factor * 2))
578
+ width = 2 * (int(width) // (self.vae_scale_factor * 2))
579
+
580
+ shape = (batch_size, num_channels_latents, height, width)
581
+
582
+ if latents is not None:
583
+ latent_image_ids = self._prepare_latent_image_ids(batch_size, height // 2, width // 2, device, dtype)
584
+ return latents.to(device=device, dtype=dtype), latent_image_ids
585
+
586
+ if isinstance(generator, list) and len(generator) != batch_size:
587
+ raise ValueError(
588
+ f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
589
+ f" size of {batch_size}. Make sure the batch size matches the length of the generators."
590
+ )
591
+
592
+ latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
593
+ latents = self._pack_latents(latents, batch_size, num_channels_latents, height, width)
594
+
595
+ latent_image_ids = self._prepare_latent_image_ids(batch_size, height // 2, width // 2, device, dtype)
596
+
597
+ return latents, latent_image_ids
598
+
599
+ def _prepare_attention_mask(
600
+ self,
601
+ batch_size,
602
+ sequence_length,
603
+ dtype,
604
+ attention_mask=None,
605
+ ):
606
+ if attention_mask is None:
607
+ return attention_mask
608
+
609
+ # Extend the prompt attention mask to account for image tokens in the final sequence
610
+ attention_mask = torch.cat(
611
+ [attention_mask, torch.ones(batch_size, sequence_length, device=attention_mask.device, dtype=torch.bool)],
612
+ dim=1,
613
+ )
614
+
615
+ return attention_mask
616
+
617
+ @property
618
+ def guidance_scale(self):
619
+ return self._guidance_scale
620
+
621
+ @property
622
+ def joint_attention_kwargs(self):
623
+ return self._joint_attention_kwargs
624
+
625
+ @property
626
+ def do_classifier_free_guidance(self):
627
+ return self._guidance_scale > 1
628
+
629
+ @property
630
+ def num_timesteps(self):
631
+ return self._num_timesteps
632
+
633
+ @property
634
+ def current_timestep(self):
635
+ return self._current_timestep
636
+
637
+ @property
638
+ def interrupt(self):
639
+ return self._interrupt
640
+
641
+ @torch.no_grad()
642
+ @replace_example_docstring(EXAMPLE_DOC_STRING)
643
+ def __call__(
644
+ self,
645
+ prompt: str | list[str] = None,
646
+ negative_prompt: str | list[str] = None,
647
+ height: int | None = None,
648
+ width: int | None = None,
649
+ num_inference_steps: int = 35,
650
+ sigmas: list[float] | None = None,
651
+ guidance_scale: float = 5.0,
652
+ num_images_per_prompt: int | None = 1,
653
+ generator: torch.Generator | list[torch.Generator] | None = None,
654
+ latents: torch.Tensor | None = None,
655
+ prompt_embeds: torch.Tensor | None = None,
656
+ ip_adapter_image: PipelineImageInput | None = None,
657
+ ip_adapter_image_embeds: list[torch.Tensor] | None = None,
658
+ negative_ip_adapter_image: PipelineImageInput | None = None,
659
+ negative_ip_adapter_image_embeds: list[torch.Tensor] | None = None,
660
+ negative_prompt_embeds: torch.Tensor | None = None,
661
+ prompt_attention_mask: torch.Tensor | None = None,
662
+ negative_prompt_attention_mask: torch.Tensor | None = None,
663
+ output_type: str | None = "pil",
664
+ return_dict: bool = True,
665
+ joint_attention_kwargs: dict[str, Any] | None = None,
666
+ callback_on_step_end: Callable[[int, int], None] | None = None,
667
+ callback_on_step_end_tensor_inputs: list[str] = ["latents"],
668
+ max_sequence_length: int = 512,
669
+ ):
670
+ r"""
671
+ Function invoked when calling the pipeline for generation.
672
+
673
+ Args:
674
+ prompt (`str` or `list[str]`, *optional*):
675
+ The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
676
+ instead.
677
+ negative_prompt (`str` or `list[str]`, *optional*):
678
+ The prompt or prompts not to guide the image generation. If not defined, one has to pass
679
+ `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
680
+ not greater than `1`).
681
+ height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
682
+ The height in pixels of the generated image. This is set to 1024 by default for the best results.
683
+ width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
684
+ The width in pixels of the generated image. This is set to 1024 by default for the best results.
685
+ num_inference_steps (`int`, *optional*, defaults to 50):
686
+ The number of denoising steps. More denoising steps usually lead to a higher quality image at the
687
+ expense of slower inference.
688
+ sigmas (`list[float]`, *optional*):
689
+ Custom sigmas to use for the denoising process with schedulers which support a `sigmas` argument in
690
+ their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is passed
691
+ will be used.
692
+ guidance_scale (`float`, *optional*, defaults to 3.5):
693
+ Guidance scale as defined in [Classifier-Free Diffusion
694
+ Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
695
+ of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
696
+ `guidance_scale > 1`. Higher guidance scale encourages to generate images that are closely linked to
697
+ the text `prompt`, usually at the expense of lower image quality.
698
+ num_images_per_prompt (`int`, *optional*, defaults to 1):
699
+ The number of images to generate per prompt.
700
+ generator (`torch.Generator` or `list[torch.Generator]`, *optional*):
701
+ One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)
702
+ to make generation deterministic.
703
+ latents (`torch.Tensor`, *optional*):
704
+ Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image
705
+ generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
706
+ tensor will be generated by sampling using the supplied random `generator`.
707
+ prompt_embeds (`torch.Tensor`, *optional*):
708
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
709
+ provided, text embeddings will be generated from `prompt` input argument.
710
+ ip_adapter_image: (`PipelineImageInput`, *optional*): Optional image input to work with IP Adapters.
711
+ ip_adapter_image_embeds (`list[torch.Tensor]`, *optional*):
712
+ Pre-generated image embeddings for IP-Adapter. It should be a list of length same as number of
713
+ IP-adapters. Each element should be a tensor of shape `(batch_size, num_images, emb_dim)`. If not
714
+ provided, embeddings are computed from the `ip_adapter_image` input argument.
715
+ negative_ip_adapter_image:
716
+ (`PipelineImageInput`, *optional*): Optional image input to work with IP Adapters.
717
+ negative_ip_adapter_image_embeds (`list[torch.Tensor]`, *optional*):
718
+ Pre-generated image embeddings for IP-Adapter. It should be a list of length same as number of
719
+ IP-adapters. Each element should be a tensor of shape `(batch_size, num_images, emb_dim)`. If not
720
+ provided, embeddings are computed from the `ip_adapter_image` input argument.
721
+ negative_prompt_embeds (`torch.Tensor`, *optional*):
722
+ Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
723
+ weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
724
+ argument.
725
+ prompt_attention_mask (torch.Tensor, *optional*):
726
+ Attention mask for the prompt embeddings. Used to mask out padding tokens in the prompt sequence.
727
+ Chroma requires a single padding token remain unmasked. Please refer to
728
+ https://huggingface.co/lodestones/Chroma#tldr-masking-t5-padding-tokens-enhanced-fidelity-and-increased-stability-during-training
729
+ negative_prompt_attention_mask (torch.Tensor, *optional*):
730
+ Attention mask for the negative prompt embeddings. Used to mask out padding tokens in the negative
731
+ prompt sequence. Chroma requires a single padding token remain unmasked. PLease refer to
732
+ https://huggingface.co/lodestones/Chroma#tldr-masking-t5-padding-tokens-enhanced-fidelity-and-increased-stability-during-training
733
+ output_type (`str`, *optional*, defaults to `"pil"`):
734
+ The output format of the generate image. Choose between
735
+ [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`.
736
+ return_dict (`bool`, *optional*, defaults to `True`):
737
+ Whether or not to return a [`~pipelines.flux.ChromaPipelineOutput`] instead of a plain tuple.
738
+ joint_attention_kwargs (`dict`, *optional*):
739
+ A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
740
+ `self.processor` in
741
+ [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
742
+ callback_on_step_end (`Callable`, *optional*):
743
+ A function that calls at the end of each denoising steps during the inference. The function is called
744
+ with the following arguments: `callback_on_step_end(self: DiffusionPipeline, step: int, timestep: int,
745
+ callback_kwargs: Dict)`. `callback_kwargs` will include a list of all tensors as specified by
746
+ `callback_on_step_end_tensor_inputs`.
747
+ callback_on_step_end_tensor_inputs (`list`, *optional*):
748
+ The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
749
+ will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
750
+ `._callback_tensor_inputs` attribute of your pipeline class.
751
+ max_sequence_length (`int` defaults to 512): Maximum sequence length to use with the `prompt`.
752
+
753
+ Examples:
754
+
755
+ Returns:
756
+ [`~pipelines.chroma.ChromaPipelineOutput`] or `tuple`: [`~pipelines.chroma.ChromaPipelineOutput`] if
757
+ `return_dict` is True, otherwise a `tuple`. When returning a tuple, the first element is a list with the
758
+ generated images.
759
+ """
760
+
761
+ height = height or self.default_sample_size * self.vae_scale_factor
762
+ width = width or self.default_sample_size * self.vae_scale_factor
763
+
764
+ # 1. Check inputs. Raise error if not correct
765
+ self.check_inputs(
766
+ prompt,
767
+ height,
768
+ width,
769
+ negative_prompt=negative_prompt,
770
+ prompt_embeds=prompt_embeds,
771
+ prompt_attention_mask=prompt_attention_mask,
772
+ negative_prompt_embeds=negative_prompt_embeds,
773
+ negative_prompt_attention_mask=negative_prompt_attention_mask,
774
+ callback_on_step_end_tensor_inputs=callback_on_step_end_tensor_inputs,
775
+ max_sequence_length=max_sequence_length,
776
+ )
777
+
778
+ self._guidance_scale = guidance_scale
779
+ self._joint_attention_kwargs = joint_attention_kwargs
780
+ self._current_timestep = None
781
+ self._interrupt = False
782
+
783
+ # 2. Define call parameters
784
+ if prompt is not None and isinstance(prompt, str):
785
+ batch_size = 1
786
+ elif prompt is not None and isinstance(prompt, list):
787
+ batch_size = len(prompt)
788
+ else:
789
+ batch_size = prompt_embeds.shape[0]
790
+
791
+ device = self._execution_device
792
+
793
+ lora_scale = (
794
+ self.joint_attention_kwargs.get("scale", None) if self.joint_attention_kwargs is not None else None
795
+ )
796
+ (
797
+ prompt_embeds,
798
+ text_ids,
799
+ prompt_attention_mask,
800
+ negative_prompt_embeds,
801
+ negative_text_ids,
802
+ negative_prompt_attention_mask,
803
+ ) = self.encode_prompt(
804
+ prompt=prompt,
805
+ negative_prompt=negative_prompt,
806
+ prompt_embeds=prompt_embeds,
807
+ negative_prompt_embeds=negative_prompt_embeds,
808
+ prompt_attention_mask=prompt_attention_mask,
809
+ negative_prompt_attention_mask=negative_prompt_attention_mask,
810
+ do_classifier_free_guidance=self.do_classifier_free_guidance,
811
+ device=device,
812
+ num_images_per_prompt=num_images_per_prompt,
813
+ max_sequence_length=max_sequence_length,
814
+ lora_scale=lora_scale,
815
+ )
816
+
817
+ # 4. Prepare latent variables
818
+ num_channels_latents = self.transformer.config.in_channels // 4
819
+ latents, latent_image_ids = self.prepare_latents(
820
+ batch_size * num_images_per_prompt,
821
+ num_channels_latents,
822
+ height,
823
+ width,
824
+ prompt_embeds.dtype,
825
+ device,
826
+ generator,
827
+ latents,
828
+ )
829
+
830
+ # 5. Prepare timesteps
831
+ sigmas = np.linspace(1.0, 1 / num_inference_steps, num_inference_steps) if sigmas is None else sigmas
832
+ image_seq_len = latents.shape[1]
833
+ mu = calculate_shift(
834
+ image_seq_len,
835
+ self.scheduler.config.get("base_image_seq_len", 256),
836
+ self.scheduler.config.get("max_image_seq_len", 4096),
837
+ self.scheduler.config.get("base_shift", 0.5),
838
+ self.scheduler.config.get("max_shift", 1.15),
839
+ )
840
+
841
+ attention_mask = self._prepare_attention_mask(
842
+ batch_size=latents.shape[0],
843
+ sequence_length=image_seq_len,
844
+ dtype=latents.dtype,
845
+ attention_mask=prompt_attention_mask,
846
+ )
847
+ negative_attention_mask = self._prepare_attention_mask(
848
+ batch_size=latents.shape[0],
849
+ sequence_length=image_seq_len,
850
+ dtype=latents.dtype,
851
+ attention_mask=negative_prompt_attention_mask,
852
+ )
853
+
854
+ timesteps, num_inference_steps = retrieve_timesteps(
855
+ self.scheduler,
856
+ num_inference_steps,
857
+ device,
858
+ sigmas=sigmas,
859
+ mu=mu,
860
+ )
861
+ num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
862
+ self._num_timesteps = len(timesteps)
863
+
864
+ if (ip_adapter_image is not None or ip_adapter_image_embeds is not None) and (
865
+ negative_ip_adapter_image is None and negative_ip_adapter_image_embeds is None
866
+ ):
867
+ negative_ip_adapter_image = np.zeros((width, height, 3), dtype=np.uint8)
868
+ negative_ip_adapter_image = [negative_ip_adapter_image] * self.transformer.encoder_hid_proj.num_ip_adapters
869
+
870
+ elif (ip_adapter_image is None and ip_adapter_image_embeds is None) and (
871
+ negative_ip_adapter_image is not None or negative_ip_adapter_image_embeds is not None
872
+ ):
873
+ ip_adapter_image = np.zeros((width, height, 3), dtype=np.uint8)
874
+ ip_adapter_image = [ip_adapter_image] * self.transformer.encoder_hid_proj.num_ip_adapters
875
+
876
+ if self.joint_attention_kwargs is None:
877
+ self._joint_attention_kwargs = {}
878
+
879
+ image_embeds = None
880
+ negative_image_embeds = None
881
+ if ip_adapter_image is not None or ip_adapter_image_embeds is not None:
882
+ image_embeds = self.prepare_ip_adapter_image_embeds(
883
+ ip_adapter_image,
884
+ ip_adapter_image_embeds,
885
+ device,
886
+ batch_size * num_images_per_prompt,
887
+ )
888
+ if negative_ip_adapter_image is not None or negative_ip_adapter_image_embeds is not None:
889
+ negative_image_embeds = self.prepare_ip_adapter_image_embeds(
890
+ negative_ip_adapter_image,
891
+ negative_ip_adapter_image_embeds,
892
+ device,
893
+ batch_size * num_images_per_prompt,
894
+ )
895
+
896
+ # 6. Denoising loop
897
+ with self.progress_bar(total=num_inference_steps) as progress_bar:
898
+ for i, t in enumerate(timesteps):
899
+ if self.interrupt:
900
+ continue
901
+
902
+ self._current_timestep = t
903
+ if image_embeds is not None:
904
+ self._joint_attention_kwargs["ip_adapter_image_embeds"] = image_embeds
905
+
906
+ # broadcast to batch dimension in a way that's compatible with ONNX/Core ML
907
+ timestep = t.expand(latents.shape[0]).to(latents.dtype)
908
+
909
+ noise_pred = self.transformer(
910
+ hidden_states=latents,
911
+ timestep=timestep / 1000,
912
+ encoder_hidden_states=prompt_embeds,
913
+ txt_ids=text_ids,
914
+ img_ids=latent_image_ids,
915
+ attention_mask=attention_mask,
916
+ joint_attention_kwargs=self.joint_attention_kwargs,
917
+ return_dict=False,
918
+ )[0]
919
+
920
+ if self.do_classifier_free_guidance:
921
+ if negative_image_embeds is not None:
922
+ self._joint_attention_kwargs["ip_adapter_image_embeds"] = negative_image_embeds
923
+ neg_noise_pred = self.transformer(
924
+ hidden_states=latents,
925
+ timestep=timestep / 1000,
926
+ encoder_hidden_states=negative_prompt_embeds,
927
+ txt_ids=negative_text_ids,
928
+ img_ids=latent_image_ids,
929
+ attention_mask=negative_attention_mask,
930
+ joint_attention_kwargs=self.joint_attention_kwargs,
931
+ return_dict=False,
932
+ )[0]
933
+ noise_pred = neg_noise_pred + guidance_scale * (noise_pred - neg_noise_pred)
934
+
935
+ # compute the previous noisy sample x_t -> x_t-1
936
+ latents_dtype = latents.dtype
937
+ latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
938
+
939
+ if latents.dtype != latents_dtype:
940
+ if torch.backends.mps.is_available():
941
+ # some platforms (eg. apple mps) misbehave due to a pytorch bug: https://github.com/pytorch/pytorch/pull/99272
942
+ latents = latents.to(latents_dtype)
943
+
944
+ if callback_on_step_end is not None:
945
+ callback_kwargs = {}
946
+ for k in callback_on_step_end_tensor_inputs:
947
+ callback_kwargs[k] = locals()[k]
948
+ callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
949
+
950
+ latents = callback_outputs.pop("latents", latents)
951
+ prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
952
+
953
+ # call the callback, if provided
954
+ if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
955
+ progress_bar.update()
956
+
957
+ if XLA_AVAILABLE:
958
+ xm.mark_step()
959
+
960
+ self._current_timestep = None
961
+
962
+ if output_type == "latent":
963
+ image = latents
964
+ else:
965
+ latents = self._unpack_latents(latents, height, width, self.vae_scale_factor)
966
+ latents = (latents / self.vae.config.scaling_factor) + self.vae.config.shift_factor
967
+ image = self.vae.decode(latents, return_dict=False)[0]
968
+ image = self.image_processor.postprocess(image, output_type=output_type)
969
+
970
+ # Offload all models
971
+ self.maybe_free_model_hooks()
972
+
973
+ if not return_dict:
974
+ return (image,)
975
+
976
+ return ChromaPipelineOutput(images=image)
third_party/diffusers/src/diffusers/pipelines/chroma/pipeline_chroma_img2img.py ADDED
@@ -0,0 +1,1060 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 Black Forest Labs and The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ from typing import Any, Callable
17
+
18
+ import numpy as np
19
+ import torch
20
+ from transformers import CLIPImageProcessor, CLIPVisionModelWithProjection, T5EncoderModel, T5TokenizerFast
21
+
22
+ from ...image_processor import PipelineImageInput, VaeImageProcessor
23
+ from ...loaders import FluxIPAdapterMixin, FluxLoraLoaderMixin, FromSingleFileMixin, TextualInversionLoaderMixin
24
+ from ...models import AutoencoderKL, ChromaTransformer2DModel
25
+ from ...schedulers import FlowMatchEulerDiscreteScheduler
26
+ from ...utils import (
27
+ USE_PEFT_BACKEND,
28
+ deprecate,
29
+ is_torch_xla_available,
30
+ logging,
31
+ replace_example_docstring,
32
+ scale_lora_layers,
33
+ unscale_lora_layers,
34
+ )
35
+ from ...utils.torch_utils import randn_tensor
36
+ from ..pipeline_utils import DiffusionPipeline
37
+ from .pipeline_output import ChromaPipelineOutput
38
+
39
+
40
+ if is_torch_xla_available():
41
+ import torch_xla.core.xla_model as xm
42
+
43
+ XLA_AVAILABLE = True
44
+ else:
45
+ XLA_AVAILABLE = False
46
+
47
+
48
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
49
+
50
+ EXAMPLE_DOC_STRING = """
51
+ Examples:
52
+ ```py
53
+ >>> import torch
54
+ >>> from diffusers import ChromaTransformer2DModel, ChromaImg2ImgPipeline
55
+
56
+ >>> model_id = "lodestones/Chroma1-HD"
57
+ >>> ckpt_path = "https://huggingface.co/lodestones/Chroma1-HD/blob/main/Chroma1-HD.safetensors"
58
+ >>> pipe = ChromaImg2ImgPipeline.from_pretrained(
59
+ ... model_id,
60
+ ... transformer=transformer,
61
+ ... torch_dtype=torch.bfloat16,
62
+ ... )
63
+ >>> pipe.enable_model_cpu_offload()
64
+ >>> init_image = load_image(
65
+ ... "https://raw.githubusercontent.com/CompVis/stable-diffusion/main/assets/stable-samples/img2img/sketch-mountains-input.jpg"
66
+ ... )
67
+ >>> prompt = "a scenic fastasy landscape with a river and mountains in the background, vibrant colors, detailed, high resolution"
68
+ >>> negative_prompt = "low quality, ugly, unfinished, out of focus, deformed, disfigure, blurry, smudged, restricted palette, flat colors"
69
+ >>> image = pipe(prompt, image=init_image, negative_prompt=negative_prompt).images[0]
70
+ >>> image.save("chroma-img2img.png")
71
+ ```
72
+ """
73
+
74
+
75
+ # Copied from diffusers.pipelines.flux.pipeline_flux.calculate_shift
76
+ def calculate_shift(
77
+ image_seq_len,
78
+ base_seq_len: int = 256,
79
+ max_seq_len: int = 4096,
80
+ base_shift: float = 0.5,
81
+ max_shift: float = 1.15,
82
+ ):
83
+ m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
84
+ b = base_shift - m * base_seq_len
85
+ mu = image_seq_len * m + b
86
+ return mu
87
+
88
+
89
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.retrieve_latents
90
+ def retrieve_latents(
91
+ encoder_output: torch.Tensor, generator: torch.Generator | None = None, sample_mode: str = "sample"
92
+ ):
93
+ if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
94
+ return encoder_output.latent_dist.sample(generator)
95
+ elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
96
+ return encoder_output.latent_dist.mode()
97
+ elif hasattr(encoder_output, "latents"):
98
+ return encoder_output.latents
99
+ else:
100
+ raise AttributeError("Could not access latents of provided encoder_output")
101
+
102
+
103
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
104
+ def retrieve_timesteps(
105
+ scheduler,
106
+ num_inference_steps: int | None = None,
107
+ device: str | torch.device | None = None,
108
+ timesteps: list[int] | None = None,
109
+ sigmas: list[float] | None = None,
110
+ **kwargs,
111
+ ):
112
+ r"""
113
+ Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
114
+ custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
115
+
116
+ Args:
117
+ scheduler (`SchedulerMixin`):
118
+ The scheduler to get timesteps from.
119
+ num_inference_steps (`int`):
120
+ The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
121
+ must be `None`.
122
+ device (`str` or `torch.device`, *optional*):
123
+ The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
124
+ timesteps (`list[int]`, *optional*):
125
+ Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
126
+ `num_inference_steps` and `sigmas` must be `None`.
127
+ sigmas (`list[float]`, *optional*):
128
+ Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
129
+ `num_inference_steps` and `timesteps` must be `None`.
130
+
131
+ Returns:
132
+ `tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
133
+ second element is the number of inference steps.
134
+ """
135
+ if timesteps is not None and sigmas is not None:
136
+ raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
137
+ if timesteps is not None:
138
+ accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
139
+ if not accepts_timesteps:
140
+ raise ValueError(
141
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
142
+ f" timestep schedules. Please check whether you are using the correct scheduler."
143
+ )
144
+ scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
145
+ timesteps = scheduler.timesteps
146
+ num_inference_steps = len(timesteps)
147
+ elif sigmas is not None:
148
+ accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
149
+ if not accept_sigmas:
150
+ raise ValueError(
151
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
152
+ f" sigmas schedules. Please check whether you are using the correct scheduler."
153
+ )
154
+ scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
155
+ timesteps = scheduler.timesteps
156
+ num_inference_steps = len(timesteps)
157
+ else:
158
+ scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
159
+ timesteps = scheduler.timesteps
160
+ return timesteps, num_inference_steps
161
+
162
+
163
+ class ChromaImg2ImgPipeline(
164
+ DiffusionPipeline,
165
+ FluxLoraLoaderMixin,
166
+ FromSingleFileMixin,
167
+ TextualInversionLoaderMixin,
168
+ FluxIPAdapterMixin,
169
+ ):
170
+ r"""
171
+ The Chroma pipeline for image-to-image generation.
172
+
173
+ Reference: https://huggingface.co/lodestones/Chroma1-HD/
174
+
175
+ Args:
176
+ transformer ([`ChromaTransformer2DModel`]):
177
+ Conditional Transformer (MMDiT) architecture to denoise the encoded image latents.
178
+ scheduler ([`FlowMatchEulerDiscreteScheduler`]):
179
+ A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
180
+ vae ([`AutoencoderKL`]):
181
+ Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representation
182
+ text_encoder ([`T5EncoderModel`]):
183
+ [T5](https://huggingface.co/docs/transformers/en/model_doc/t5#transformers.T5EncoderModel), specifically
184
+ the [google/t5-v1_1-xxl](https://huggingface.co/google/t5-v1_1-xxl) variant.
185
+ tokenizer (`T5TokenizerFast`):
186
+ Second Tokenizer of class
187
+ [T5TokenizerFast](https://huggingface.co/docs/transformers/en/model_doc/t5#transformers.T5TokenizerFast).
188
+ """
189
+
190
+ model_cpu_offload_seq = "text_encoder->image_encoder->transformer->vae"
191
+ _optional_components = ["image_encoder", "feature_extractor"]
192
+ _callback_tensor_inputs = ["latents", "prompt_embeds"]
193
+
194
+ def __init__(
195
+ self,
196
+ scheduler: FlowMatchEulerDiscreteScheduler,
197
+ vae: AutoencoderKL,
198
+ text_encoder: T5EncoderModel,
199
+ tokenizer: T5TokenizerFast,
200
+ transformer: ChromaTransformer2DModel,
201
+ image_encoder: CLIPVisionModelWithProjection = None,
202
+ feature_extractor: CLIPImageProcessor = None,
203
+ ):
204
+ super().__init__()
205
+
206
+ self.register_modules(
207
+ vae=vae,
208
+ text_encoder=text_encoder,
209
+ tokenizer=tokenizer,
210
+ transformer=transformer,
211
+ scheduler=scheduler,
212
+ image_encoder=image_encoder,
213
+ feature_extractor=feature_extractor,
214
+ )
215
+ self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) if getattr(self, "vae", None) else 8
216
+ self.latent_channels = self.vae.config.latent_channels if getattr(self, "vae", None) else 16
217
+
218
+ # Flux latents are turned into 2x2 patches and packed. This means the latent width and height has to be divisible
219
+ # by the patch size. So the vae scale factor is multiplied by the patch size to account for this
220
+ self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor * 2)
221
+ self.default_sample_size = 128
222
+
223
+ def _get_t5_prompt_embeds(
224
+ self,
225
+ prompt: str | list[str] = None,
226
+ num_images_per_prompt: int = 1,
227
+ max_sequence_length: int = 512,
228
+ device: torch.device | None = None,
229
+ dtype: torch.dtype | None = None,
230
+ ):
231
+ device = device or self._execution_device
232
+ dtype = dtype or self.text_encoder.dtype
233
+
234
+ prompt = [prompt] if isinstance(prompt, str) else prompt
235
+ batch_size = len(prompt)
236
+
237
+ if isinstance(self, TextualInversionLoaderMixin):
238
+ prompt = self.maybe_convert_prompt(prompt, self.tokenizer)
239
+
240
+ text_inputs = self.tokenizer(
241
+ prompt,
242
+ padding="max_length",
243
+ max_length=max_sequence_length,
244
+ truncation=True,
245
+ return_length=False,
246
+ return_overflowing_tokens=False,
247
+ return_tensors="pt",
248
+ )
249
+ text_input_ids = text_inputs.input_ids
250
+ tokenizer_mask = text_inputs.attention_mask
251
+
252
+ tokenizer_mask_device = tokenizer_mask.to(device)
253
+
254
+ prompt_embeds = self.text_encoder(
255
+ text_input_ids.to(device),
256
+ output_hidden_states=False,
257
+ attention_mask=tokenizer_mask_device,
258
+ )[0]
259
+
260
+ prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
261
+
262
+ seq_lengths = tokenizer_mask_device.sum(dim=1)
263
+ mask_indices = torch.arange(tokenizer_mask_device.size(1), device=device).unsqueeze(0).expand(batch_size, -1)
264
+ attention_mask = (mask_indices <= seq_lengths.unsqueeze(1)).to(dtype=dtype, device=device)
265
+
266
+ _, seq_len, _ = prompt_embeds.shape
267
+
268
+ # duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method
269
+ prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
270
+ prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
271
+
272
+ attention_mask = attention_mask.repeat(1, num_images_per_prompt)
273
+ attention_mask = attention_mask.view(batch_size * num_images_per_prompt, seq_len)
274
+
275
+ return prompt_embeds, attention_mask
276
+
277
+ # Copied from diffusers.pipelines.stable_diffusion_3.pipeline_stable_diffusion_3_inpaint.StableDiffusion3InpaintPipeline._encode_vae_image
278
+ def _encode_vae_image(self, image: torch.Tensor, generator: torch.Generator):
279
+ if isinstance(generator, list):
280
+ image_latents = [
281
+ retrieve_latents(self.vae.encode(image[i : i + 1]), generator=generator[i])
282
+ for i in range(image.shape[0])
283
+ ]
284
+ image_latents = torch.cat(image_latents, dim=0)
285
+ else:
286
+ image_latents = retrieve_latents(self.vae.encode(image), generator=generator)
287
+
288
+ image_latents = (image_latents - self.vae.config.shift_factor) * self.vae.config.scaling_factor
289
+
290
+ return image_latents
291
+
292
+ def encode_prompt(
293
+ self,
294
+ prompt: str | list[str],
295
+ negative_prompt: str | list[str] = None,
296
+ device: torch.device | None = None,
297
+ num_images_per_prompt: int = 1,
298
+ prompt_embeds: torch.Tensor | None = None,
299
+ negative_prompt_embeds: torch.Tensor | None = None,
300
+ prompt_attention_mask: torch.Tensor | None = None,
301
+ negative_prompt_attention_mask: torch.Tensor | None = None,
302
+ do_classifier_free_guidance: bool = True,
303
+ max_sequence_length: int = 512,
304
+ lora_scale: float | None = None,
305
+ ):
306
+ r"""
307
+
308
+ Args:
309
+ prompt (`str` or `list[str]`, *optional*):
310
+ prompt to be encoded
311
+ negative_prompt (`str` or `list[str]`, *optional*):
312
+ The prompt not to guide the image generation. If not defined, one has to pass `negative_prompt_embeds`
313
+ instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is less than `1`).
314
+ device: (`torch.device`):
315
+ torch device
316
+ num_images_per_prompt (`int`):
317
+ number of images that should be generated per prompt
318
+ prompt_embeds (`torch.Tensor`, *optional*):
319
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
320
+ provided, text embeddings will be generated from `prompt` input argument.
321
+ lora_scale (`float`, *optional*):
322
+ A lora scale that will be applied to all LoRA layers of the text encoder if LoRA layers are loaded.
323
+ """
324
+ device = device or self._execution_device
325
+
326
+ # set lora scale so that monkey patched LoRA
327
+ # function of text encoder can correctly access it
328
+ if lora_scale is not None and isinstance(self, FluxLoraLoaderMixin):
329
+ self._lora_scale = lora_scale
330
+
331
+ # dynamically adjust the LoRA scale
332
+ if self.text_encoder is not None and USE_PEFT_BACKEND:
333
+ scale_lora_layers(self.text_encoder, lora_scale)
334
+
335
+ prompt = [prompt] if isinstance(prompt, str) else prompt
336
+
337
+ if prompt is not None:
338
+ batch_size = len(prompt)
339
+ else:
340
+ batch_size = prompt_embeds.shape[0]
341
+
342
+ if prompt_embeds is None:
343
+ prompt_embeds, prompt_attention_mask = self._get_t5_prompt_embeds(
344
+ prompt=prompt,
345
+ num_images_per_prompt=num_images_per_prompt,
346
+ max_sequence_length=max_sequence_length,
347
+ device=device,
348
+ )
349
+
350
+ dtype = self.text_encoder.dtype if self.text_encoder is not None else self.transformer.dtype
351
+ text_ids = torch.zeros(prompt_embeds.shape[1], 3).to(device=device, dtype=dtype)
352
+ negative_text_ids = None
353
+
354
+ if do_classifier_free_guidance:
355
+ if negative_prompt_embeds is None:
356
+ negative_prompt = negative_prompt or ""
357
+ negative_prompt = (
358
+ batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
359
+ )
360
+
361
+ if prompt is not None and type(prompt) is not type(negative_prompt):
362
+ raise TypeError(
363
+ f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
364
+ f" {type(prompt)}."
365
+ )
366
+ elif batch_size != len(negative_prompt):
367
+ raise ValueError(
368
+ f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
369
+ f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
370
+ " the batch size of `prompt`."
371
+ )
372
+
373
+ negative_prompt_embeds, negative_prompt_attention_mask = self._get_t5_prompt_embeds(
374
+ prompt=negative_prompt,
375
+ num_images_per_prompt=num_images_per_prompt,
376
+ max_sequence_length=max_sequence_length,
377
+ device=device,
378
+ )
379
+
380
+ negative_text_ids = torch.zeros(negative_prompt_embeds.shape[1], 3).to(device=device, dtype=dtype)
381
+
382
+ if self.text_encoder is not None:
383
+ if isinstance(self, FluxLoraLoaderMixin) and USE_PEFT_BACKEND:
384
+ # Retrieve the original scale by scaling back the LoRA layers
385
+ unscale_lora_layers(self.text_encoder, lora_scale)
386
+
387
+ return (
388
+ prompt_embeds,
389
+ text_ids,
390
+ prompt_attention_mask,
391
+ negative_prompt_embeds,
392
+ negative_text_ids,
393
+ negative_prompt_attention_mask,
394
+ )
395
+
396
+ # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.encode_image
397
+ def encode_image(self, image, device, num_images_per_prompt):
398
+ dtype = next(self.image_encoder.parameters()).dtype
399
+
400
+ if not isinstance(image, torch.Tensor):
401
+ image = self.feature_extractor(image, return_tensors="pt").pixel_values
402
+
403
+ image = image.to(device=device, dtype=dtype)
404
+ image_embeds = self.image_encoder(image).image_embeds
405
+ image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
406
+ return image_embeds
407
+
408
+ def prepare_ip_adapter_image_embeds(
409
+ self, ip_adapter_image, ip_adapter_image_embeds, device, num_images_per_prompt
410
+ ):
411
+ device = device or self._execution_device
412
+
413
+ image_embeds = []
414
+ if ip_adapter_image_embeds is None:
415
+ if not isinstance(ip_adapter_image, list):
416
+ ip_adapter_image = [ip_adapter_image]
417
+
418
+ if len(ip_adapter_image) != self.transformer.encoder_hid_proj.num_ip_adapters:
419
+ raise ValueError(
420
+ f"`ip_adapter_image` must have same length as the number of IP Adapters. Got {len(ip_adapter_image)} images and {self.transformer.encoder_hid_proj.num_ip_adapters} IP Adapters."
421
+ )
422
+
423
+ for single_ip_adapter_image in ip_adapter_image:
424
+ single_image_embeds = self.encode_image(single_ip_adapter_image, device, 1)
425
+ image_embeds.append(single_image_embeds[None, :])
426
+ else:
427
+ if not isinstance(ip_adapter_image_embeds, list):
428
+ ip_adapter_image_embeds = [ip_adapter_image_embeds]
429
+
430
+ if len(ip_adapter_image_embeds) != self.transformer.encoder_hid_proj.num_ip_adapters:
431
+ raise ValueError(
432
+ f"`ip_adapter_image_embeds` must have same length as the number of IP Adapters. Got {len(ip_adapter_image_embeds)} image embeds and {self.transformer.encoder_hid_proj.num_ip_adapters} IP Adapters."
433
+ )
434
+
435
+ for single_image_embeds in ip_adapter_image_embeds:
436
+ image_embeds.append(single_image_embeds)
437
+
438
+ ip_adapter_image_embeds = []
439
+ for single_image_embeds in image_embeds:
440
+ single_image_embeds = torch.cat([single_image_embeds] * num_images_per_prompt, dim=0)
441
+ single_image_embeds = single_image_embeds.to(device=device)
442
+ ip_adapter_image_embeds.append(single_image_embeds)
443
+
444
+ return ip_adapter_image_embeds
445
+
446
+ def check_inputs(
447
+ self,
448
+ prompt,
449
+ height,
450
+ width,
451
+ strength,
452
+ negative_prompt=None,
453
+ prompt_embeds=None,
454
+ negative_prompt_embeds=None,
455
+ prompt_attention_mask=None,
456
+ negative_prompt_attention_mask=None,
457
+ callback_on_step_end_tensor_inputs=None,
458
+ max_sequence_length=None,
459
+ ):
460
+ if strength < 0 or strength > 1:
461
+ raise ValueError(f"The value of strength should in [0.0, 1.0] but is {strength}")
462
+
463
+ if height % (self.vae_scale_factor * 2) != 0 or width % (self.vae_scale_factor * 2) != 0:
464
+ logger.warning(
465
+ f"`height` and `width` have to be divisible by {self.vae_scale_factor * 2} but are {height} and {width}. Dimensions will be resized accordingly"
466
+ )
467
+
468
+ if callback_on_step_end_tensor_inputs is not None and not all(
469
+ k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
470
+ ):
471
+ raise ValueError(
472
+ f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
473
+ )
474
+
475
+ if prompt is not None and prompt_embeds is not None:
476
+ raise ValueError(
477
+ f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
478
+ " only forward one of the two."
479
+ )
480
+ elif prompt is None and prompt_embeds is None:
481
+ raise ValueError(
482
+ "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
483
+ )
484
+ elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
485
+ raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
486
+
487
+ if negative_prompt is not None and negative_prompt_embeds is not None:
488
+ raise ValueError(
489
+ f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"
490
+ f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
491
+ )
492
+
493
+ if prompt_embeds is not None and prompt_attention_mask is None:
494
+ raise ValueError("Cannot provide `prompt_embeds` without also providing `prompt_attention_mask")
495
+
496
+ if negative_prompt_embeds is not None and negative_prompt_attention_mask is None:
497
+ raise ValueError(
498
+ "Cannot provide `negative_prompt_embeds` without also providing `negative_prompt_attention_mask"
499
+ )
500
+
501
+ if max_sequence_length is not None and max_sequence_length > 512:
502
+ raise ValueError(f"`max_sequence_length` cannot be greater than 512 but is {max_sequence_length}")
503
+
504
+ @staticmethod
505
+ def _prepare_latent_image_ids(height, width, device, dtype):
506
+ latent_image_ids = torch.zeros(height, width, 3)
507
+ latent_image_ids[..., 1] = latent_image_ids[..., 1] + torch.arange(height)[:, None]
508
+ latent_image_ids[..., 2] = latent_image_ids[..., 2] + torch.arange(width)[None, :]
509
+
510
+ latent_image_id_height, latent_image_id_width, latent_image_id_channels = latent_image_ids.shape
511
+
512
+ latent_image_ids = latent_image_ids.reshape(
513
+ latent_image_id_height * latent_image_id_width, latent_image_id_channels
514
+ )
515
+
516
+ return latent_image_ids.to(device=device, dtype=dtype)
517
+
518
+ @staticmethod
519
+ def _pack_latents(latents, batch_size, num_channels_latents, height, width):
520
+ latents = latents.view(batch_size, num_channels_latents, height // 2, 2, width // 2, 2)
521
+ latents = latents.permute(0, 2, 4, 1, 3, 5)
522
+ latents = latents.reshape(batch_size, (height // 2) * (width // 2), num_channels_latents * 4)
523
+
524
+ return latents
525
+
526
+ @staticmethod
527
+ def _unpack_latents(latents, height, width, vae_scale_factor):
528
+ batch_size, num_patches, channels = latents.shape
529
+
530
+ # VAE applies 8x compression on images but we must also account for packing which requires
531
+ # latent height and width to be divisible by 2.
532
+ height = 2 * (int(height) // (vae_scale_factor * 2))
533
+ width = 2 * (int(width) // (vae_scale_factor * 2))
534
+
535
+ latents = latents.view(batch_size, height // 2, width // 2, channels // 4, 2, 2)
536
+ latents = latents.permute(0, 3, 1, 4, 2, 5)
537
+
538
+ latents = latents.reshape(batch_size, channels // (2 * 2), height, width)
539
+
540
+ return latents
541
+
542
+ def enable_vae_slicing(self):
543
+ r"""
544
+ Enable sliced VAE decoding. When this option is enabled, the VAE will split the input tensor in slices to
545
+ compute decoding in several steps. This is useful to save some memory and allow larger batch sizes.
546
+ """
547
+ depr_message = f"Calling `enable_vae_slicing()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.enable_slicing()`."
548
+ deprecate(
549
+ "enable_vae_slicing",
550
+ "0.40.0",
551
+ depr_message,
552
+ )
553
+ self.vae.enable_slicing()
554
+
555
+ def disable_vae_slicing(self):
556
+ r"""
557
+ Disable sliced VAE decoding. If `enable_vae_slicing` was previously enabled, this method will go back to
558
+ computing decoding in one step.
559
+ """
560
+ depr_message = f"Calling `disable_vae_slicing()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.disable_slicing()`."
561
+ deprecate(
562
+ "disable_vae_slicing",
563
+ "0.40.0",
564
+ depr_message,
565
+ )
566
+ self.vae.disable_slicing()
567
+
568
+ def enable_vae_tiling(self):
569
+ r"""
570
+ Enable tiled VAE decoding. When this option is enabled, the VAE will split the input tensor into tiles to
571
+ compute decoding and encoding in several steps. This is useful for saving a large amount of memory and to allow
572
+ processing larger images.
573
+ """
574
+ depr_message = f"Calling `enable_vae_tiling()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.enable_tiling()`."
575
+ deprecate(
576
+ "enable_vae_tiling",
577
+ "0.40.0",
578
+ depr_message,
579
+ )
580
+ self.vae.enable_tiling()
581
+
582
+ def disable_vae_tiling(self):
583
+ r"""
584
+ Disable tiled VAE decoding. If `enable_vae_tiling` was previously enabled, this method will go back to
585
+ computing decoding in one step.
586
+ """
587
+ depr_message = f"Calling `disable_vae_tiling()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.disable_tiling()`."
588
+ deprecate(
589
+ "disable_vae_tiling",
590
+ "0.40.0",
591
+ depr_message,
592
+ )
593
+ self.vae.disable_tiling()
594
+
595
+ # Copied from diffusers.pipelines.stable_diffusion_3.pipeline_stable_diffusion_3_img2img.StableDiffusion3Img2ImgPipeline.get_timesteps
596
+ def get_timesteps(self, num_inference_steps, strength, device):
597
+ # get the original timestep using init_timestep
598
+ init_timestep = min(num_inference_steps * strength, num_inference_steps)
599
+
600
+ t_start = int(max(num_inference_steps - init_timestep, 0))
601
+ timesteps = self.scheduler.timesteps[t_start * self.scheduler.order :]
602
+ if hasattr(self.scheduler, "set_begin_index"):
603
+ self.scheduler.set_begin_index(t_start * self.scheduler.order)
604
+
605
+ return timesteps, num_inference_steps - t_start
606
+
607
+ def prepare_latents(
608
+ self,
609
+ image,
610
+ timestep,
611
+ batch_size,
612
+ num_channels_latents,
613
+ height,
614
+ width,
615
+ dtype,
616
+ device,
617
+ generator,
618
+ latents=None,
619
+ ):
620
+ if isinstance(generator, list) and len(generator) != batch_size:
621
+ raise ValueError(
622
+ f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
623
+ f" size of {batch_size}. Make sure the batch size matches the length of the generators."
624
+ )
625
+
626
+ # VAE applies 8x compression on images but we must also account for packing which requires
627
+ # latent height and width to be divisible by 2.
628
+ height = 2 * (int(height) // (self.vae_scale_factor * 2))
629
+ width = 2 * (int(width) // (self.vae_scale_factor * 2))
630
+ shape = (batch_size, num_channels_latents, height, width)
631
+ latent_image_ids = self._prepare_latent_image_ids(height // 2, width // 2, device, dtype)
632
+
633
+ if latents is not None:
634
+ return latents.to(device=device, dtype=dtype), latent_image_ids
635
+
636
+ image = image.to(device=device, dtype=dtype)
637
+ if image.shape[1] != self.latent_channels:
638
+ image_latents = self._encode_vae_image(image=image, generator=generator)
639
+ else:
640
+ image_latents = image
641
+ if batch_size > image_latents.shape[0] and batch_size % image_latents.shape[0] == 0:
642
+ # expand init_latents for batch_size
643
+ additional_image_per_prompt = batch_size // image_latents.shape[0]
644
+ image_latents = torch.cat([image_latents] * additional_image_per_prompt, dim=0)
645
+ elif batch_size > image_latents.shape[0] and batch_size % image_latents.shape[0] != 0:
646
+ raise ValueError(
647
+ f"Cannot duplicate `image` of batch size {image_latents.shape[0]} to {batch_size} text prompts."
648
+ )
649
+ else:
650
+ image_latents = torch.cat([image_latents], dim=0)
651
+
652
+ noise = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
653
+ latents = self.scheduler.scale_noise(image_latents, timestep, noise)
654
+ latents = self._pack_latents(latents, batch_size, num_channels_latents, height, width)
655
+ return latents, latent_image_ids
656
+
657
+ def _prepare_attention_mask(
658
+ self,
659
+ batch_size,
660
+ sequence_length,
661
+ dtype,
662
+ attention_mask=None,
663
+ ):
664
+ if attention_mask is None:
665
+ return attention_mask
666
+
667
+ # Extend the prompt attention mask to account for image tokens in the final sequence
668
+ attention_mask = torch.cat(
669
+ [attention_mask, torch.ones(batch_size, sequence_length, device=attention_mask.device)],
670
+ dim=1,
671
+ )
672
+ attention_mask = attention_mask.to(dtype)
673
+
674
+ return attention_mask
675
+
676
+ @property
677
+ def guidance_scale(self):
678
+ return self._guidance_scale
679
+
680
+ @property
681
+ def joint_attention_kwargs(self):
682
+ return self._joint_attention_kwargs
683
+
684
+ @property
685
+ def do_classifier_free_guidance(self):
686
+ return self._guidance_scale > 1
687
+
688
+ @property
689
+ def num_timesteps(self):
690
+ return self._num_timesteps
691
+
692
+ @property
693
+ def current_timestep(self):
694
+ return self._current_timestep
695
+
696
+ @property
697
+ def interrupt(self):
698
+ return self._interrupt
699
+
700
+ @torch.no_grad()
701
+ @replace_example_docstring(EXAMPLE_DOC_STRING)
702
+ def __call__(
703
+ self,
704
+ prompt: str | list[str] = None,
705
+ negative_prompt: str | list[str] = None,
706
+ image: PipelineImageInput = None,
707
+ height: int | None = None,
708
+ width: int | None = None,
709
+ num_inference_steps: int = 35,
710
+ sigmas: list[float] | None = None,
711
+ guidance_scale: float = 5.0,
712
+ strength: float = 0.9,
713
+ num_images_per_prompt: int | None = 1,
714
+ generator: torch.Generator | list[torch.Generator] | None = None,
715
+ latents: torch.Tensor | None = None,
716
+ prompt_embeds: torch.Tensor | None = None,
717
+ ip_adapter_image: PipelineImageInput | None = None,
718
+ ip_adapter_image_embeds: list[torch.Tensor] | None = None,
719
+ negative_ip_adapter_image: PipelineImageInput | None = None,
720
+ negative_ip_adapter_image_embeds: list[torch.Tensor] | None = None,
721
+ negative_prompt_embeds: torch.Tensor | None = None,
722
+ prompt_attention_mask: torch.Tensor | None = None,
723
+ negative_prompt_attention_mask: torch.Tensor | None = None,
724
+ output_type: str | None = "pil",
725
+ return_dict: bool = True,
726
+ joint_attention_kwargs: dict[str, Any] | None = None,
727
+ callback_on_step_end: Callable[[int, int], None] | None = None,
728
+ callback_on_step_end_tensor_inputs: list[str] = ["latents"],
729
+ max_sequence_length: int = 512,
730
+ ):
731
+ r"""
732
+ Function invoked when calling the pipeline for generation.
733
+
734
+ Args:
735
+ prompt (`str` or `list[str]`, *optional*):
736
+ The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
737
+ instead.
738
+ negative_prompt (`str` or `list[str]`, *optional*):
739
+ The prompt or prompts not to guide the image generation. If not defined, one has to pass
740
+ `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
741
+ not greater than `1`).
742
+ height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
743
+ The height in pixels of the generated image. This is set to 1024 by default for the best results.
744
+ width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
745
+ The width in pixels of the generated image. This is set to 1024 by default for the best results.
746
+ num_inference_steps (`int`, *optional*, defaults to 35):
747
+ The number of denoising steps. More denoising steps usually lead to a higher quality image at the
748
+ expense of slower inference.
749
+ sigmas (`list[float]`, *optional*):
750
+ Custom sigmas to use for the denoising process with schedulers which support a `sigmas` argument in
751
+ their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is passed
752
+ will be used.
753
+ guidance_scale (`float`, *optional*, defaults to 3.5):
754
+ Guidance scale as defined in [Classifier-Free Diffusion
755
+ Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
756
+ of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
757
+ `guidance_scale > 1`. Higher guidance scale encourages to generate images that are closely linked to
758
+ the text `prompt`, usually at the expense of lower image quality.
759
+ strength (`float, *optional*, defaults to 0.9):
760
+ Conceptually, indicates how much to transform the reference image. Must be between 0 and 1. image will
761
+ be used as a starting point, adding more noise to it the larger the strength. The number of denoising
762
+ steps depends on the amount of noise initially added. When strength is 1, added noise will be maximum
763
+ and the denoising process will run for the full number of iterations specified in num_inference_steps.
764
+ A value of 1, therefore, essentially ignores image.
765
+ num_images_per_prompt (`int`, *optional*, defaults to 1):
766
+ The number of images to generate per prompt.
767
+ generator (`torch.Generator` or `list[torch.Generator]`, *optional*):
768
+ One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)
769
+ to make generation deterministic.
770
+ latents (`torch.Tensor`, *optional*):
771
+ Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image
772
+ generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
773
+ tensor will be generated by sampling using the supplied random `generator`.
774
+ prompt_embeds (`torch.Tensor`, *optional*):
775
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
776
+ provided, text embeddings will be generated from `prompt` input argument.
777
+ ip_adapter_image: (`PipelineImageInput`, *optional*): Optional image input to work with IP Adapters.
778
+ ip_adapter_image_embeds (`list[torch.Tensor]`, *optional*):
779
+ Pre-generated image embeddings for IP-Adapter. It should be a list of length same as number of
780
+ IP-adapters. Each element should be a tensor of shape `(batch_size, num_images, emb_dim)`. If not
781
+ provided, embeddings are computed from the `ip_adapter_image` input argument.
782
+ negative_ip_adapter_image:
783
+ (`PipelineImageInput`, *optional*): Optional image input to work with IP Adapters.
784
+ negative_ip_adapter_image_embeds (`list[torch.Tensor]`, *optional*):
785
+ Pre-generated image embeddings for IP-Adapter. It should be a list of length same as number of
786
+ IP-adapters. Each element should be a tensor of shape `(batch_size, num_images, emb_dim)`. If not
787
+ provided, embeddings are computed from the `ip_adapter_image` input argument.
788
+ negative_prompt_embeds (`torch.Tensor`, *optional*):
789
+ Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
790
+ weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
791
+ argument.
792
+ prompt_attention_mask (torch.Tensor, *optional*):
793
+ Attention mask for the prompt embeddings. Used to mask out padding tokens in the prompt sequence.
794
+ Chroma requires a single padding token remain unmasked. Please refer to
795
+ https://huggingface.co/lodestones/Chroma#tldr-masking-t5-padding-tokens-enhanced-fidelity-and-increased-stability-during-training
796
+ negative_prompt_attention_mask (torch.Tensor, *optional*):
797
+ Attention mask for the negative prompt embeddings. Used to mask out padding tokens in the negative
798
+ prompt sequence. Chroma requires a single padding token remain unmasked. PLease refer to
799
+ https://huggingface.co/lodestones/Chroma#tldr-masking-t5-padding-tokens-enhanced-fidelity-and-increased-stability-during-training
800
+ output_type (`str`, *optional*, defaults to `"pil"`):
801
+ The output format of the generate image. Choose between
802
+ [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`.
803
+ return_dict (`bool`, *optional*, defaults to `True`):
804
+ Whether or not to return a [`~pipelines.flux.ChromaPipelineOutput`] instead of a plain tuple.
805
+ joint_attention_kwargs (`dict`, *optional*):
806
+ A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
807
+ `self.processor` in
808
+ [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
809
+ callback_on_step_end (`Callable`, *optional*):
810
+ A function that calls at the end of each denoising steps during the inference. The function is called
811
+ with the following arguments: `callback_on_step_end(self: DiffusionPipeline, step: int, timestep: int,
812
+ callback_kwargs: Dict)`. `callback_kwargs` will include a list of all tensors as specified by
813
+ `callback_on_step_end_tensor_inputs`.
814
+ callback_on_step_end_tensor_inputs (`list`, *optional*):
815
+ The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
816
+ will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
817
+ `._callback_tensor_inputs` attribute of your pipeline class.
818
+ max_sequence_length (`int` defaults to 512): Maximum sequence length to use with the `prompt`.
819
+
820
+ Examples:
821
+
822
+ Returns:
823
+ [`~pipelines.chroma.ChromaPipelineOutput`] or `tuple`: [`~pipelines.chroma.ChromaPipelineOutput`] if
824
+ `return_dict` is True, otherwise a `tuple`. When returning a tuple, the first element is a list with the
825
+ generated images.
826
+ """
827
+
828
+ height = height or self.default_sample_size * self.vae_scale_factor
829
+ width = width or self.default_sample_size * self.vae_scale_factor
830
+
831
+ # 1. Check inputs. Raise error if not correct
832
+ self.check_inputs(
833
+ prompt,
834
+ height,
835
+ width,
836
+ strength,
837
+ negative_prompt=negative_prompt,
838
+ prompt_embeds=prompt_embeds,
839
+ negative_prompt_embeds=negative_prompt_embeds,
840
+ prompt_attention_mask=prompt_attention_mask,
841
+ negative_prompt_attention_mask=negative_prompt_attention_mask,
842
+ callback_on_step_end_tensor_inputs=callback_on_step_end_tensor_inputs,
843
+ max_sequence_length=max_sequence_length,
844
+ )
845
+
846
+ self._guidance_scale = guidance_scale
847
+ self._joint_attention_kwargs = joint_attention_kwargs
848
+ self._current_timestep = None
849
+ self._interrupt = False
850
+
851
+ # 2. Preprocess image
852
+ init_image = self.image_processor.preprocess(image, height=height, width=width)
853
+ init_image = init_image.to(dtype=torch.float32)
854
+
855
+ # 3. Define call parameters
856
+ if prompt is not None and isinstance(prompt, str):
857
+ batch_size = 1
858
+ elif prompt is not None and isinstance(prompt, list):
859
+ batch_size = len(prompt)
860
+ else:
861
+ batch_size = prompt_embeds.shape[0]
862
+
863
+ device = self._execution_device
864
+ lora_scale = (
865
+ self.joint_attention_kwargs.get("scale", None) if self.joint_attention_kwargs is not None else None
866
+ )
867
+
868
+ (
869
+ prompt_embeds,
870
+ text_ids,
871
+ prompt_attention_mask,
872
+ negative_prompt_embeds,
873
+ negative_text_ids,
874
+ negative_prompt_attention_mask,
875
+ ) = self.encode_prompt(
876
+ prompt=prompt,
877
+ negative_prompt=negative_prompt,
878
+ prompt_embeds=prompt_embeds,
879
+ negative_prompt_embeds=negative_prompt_embeds,
880
+ prompt_attention_mask=prompt_attention_mask,
881
+ negative_prompt_attention_mask=negative_prompt_attention_mask,
882
+ do_classifier_free_guidance=self.do_classifier_free_guidance,
883
+ device=device,
884
+ num_images_per_prompt=num_images_per_prompt,
885
+ max_sequence_length=max_sequence_length,
886
+ lora_scale=lora_scale,
887
+ )
888
+
889
+ # 4. Prepare timesteps
890
+ sigmas = np.linspace(1.0, 1 / num_inference_steps, num_inference_steps) if sigmas is None else sigmas
891
+ image_seq_len = (int(height) // self.vae_scale_factor // 2) * (int(width) // self.vae_scale_factor // 2)
892
+ mu = calculate_shift(
893
+ image_seq_len,
894
+ self.scheduler.config.get("base_image_seq_len", 256),
895
+ self.scheduler.config.get("max_image_seq_len", 4096),
896
+ self.scheduler.config.get("base_shift", 0.5),
897
+ self.scheduler.config.get("max_shift", 1.15),
898
+ )
899
+ timesteps, num_inference_steps = retrieve_timesteps(
900
+ self.scheduler,
901
+ num_inference_steps,
902
+ device,
903
+ sigmas=sigmas,
904
+ mu=mu,
905
+ )
906
+ timesteps, num_inference_steps = self.get_timesteps(num_inference_steps, strength, device)
907
+
908
+ num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
909
+ self._num_timesteps = len(timesteps)
910
+
911
+ if num_inference_steps < 1:
912
+ raise ValueError(
913
+ f"After adjusting the num_inference_steps by strength parameter: {strength}, the number of pipeline"
914
+ f"steps is {num_inference_steps} which is < 1 and not appropriate for this pipeline."
915
+ )
916
+ latent_timestep = timesteps[:1].repeat(batch_size * num_images_per_prompt)
917
+
918
+ # 5. Prepare latent variables
919
+ num_channels_latents = self.transformer.config.in_channels // 4
920
+ latents, latent_image_ids = self.prepare_latents(
921
+ init_image,
922
+ latent_timestep,
923
+ batch_size * num_images_per_prompt,
924
+ num_channels_latents,
925
+ height,
926
+ width,
927
+ prompt_embeds.dtype,
928
+ device,
929
+ generator,
930
+ latents,
931
+ )
932
+
933
+ attention_mask = self._prepare_attention_mask(
934
+ batch_size=latents.shape[0],
935
+ sequence_length=image_seq_len,
936
+ dtype=latents.dtype,
937
+ attention_mask=prompt_attention_mask,
938
+ )
939
+ negative_attention_mask = self._prepare_attention_mask(
940
+ batch_size=latents.shape[0],
941
+ sequence_length=image_seq_len,
942
+ dtype=latents.dtype,
943
+ attention_mask=negative_prompt_attention_mask,
944
+ )
945
+
946
+ # 6. Prepare image embeddings
947
+ if (ip_adapter_image is not None or ip_adapter_image_embeds is not None) and (
948
+ negative_ip_adapter_image is None and negative_ip_adapter_image_embeds is None
949
+ ):
950
+ negative_ip_adapter_image = np.zeros((width, height, 3), dtype=np.uint8)
951
+ negative_ip_adapter_image = [negative_ip_adapter_image] * self.transformer.encoder_hid_proj.num_ip_adapters
952
+
953
+ elif (ip_adapter_image is None and ip_adapter_image_embeds is None) and (
954
+ negative_ip_adapter_image is not None or negative_ip_adapter_image_embeds is not None
955
+ ):
956
+ ip_adapter_image = np.zeros((width, height, 3), dtype=np.uint8)
957
+ ip_adapter_image = [ip_adapter_image] * self.transformer.encoder_hid_proj.num_ip_adapters
958
+
959
+ if self.joint_attention_kwargs is None:
960
+ self._joint_attention_kwargs = {}
961
+
962
+ image_embeds = None
963
+ negative_image_embeds = None
964
+ if ip_adapter_image is not None or ip_adapter_image_embeds is not None:
965
+ image_embeds = self.prepare_ip_adapter_image_embeds(
966
+ ip_adapter_image,
967
+ ip_adapter_image_embeds,
968
+ device,
969
+ batch_size * num_images_per_prompt,
970
+ )
971
+ if negative_ip_adapter_image is not None or negative_ip_adapter_image_embeds is not None:
972
+ negative_image_embeds = self.prepare_ip_adapter_image_embeds(
973
+ negative_ip_adapter_image,
974
+ negative_ip_adapter_image_embeds,
975
+ device,
976
+ batch_size * num_images_per_prompt,
977
+ )
978
+
979
+ # 6. Denoising loop
980
+ with self.progress_bar(total=num_inference_steps) as progress_bar:
981
+ for i, t in enumerate(timesteps):
982
+ if self.interrupt:
983
+ continue
984
+
985
+ self._current_timestep = t
986
+ # broadcast to batch dimension in a way that's compatible with ONNX/Core ML
987
+ timestep = t.expand(latents.shape[0])
988
+
989
+ if image_embeds is not None:
990
+ self._joint_attention_kwargs["ip_adapter_image_embeds"] = image_embeds
991
+
992
+ noise_pred = self.transformer(
993
+ hidden_states=latents,
994
+ timestep=timestep / 1000,
995
+ encoder_hidden_states=prompt_embeds,
996
+ txt_ids=text_ids,
997
+ img_ids=latent_image_ids,
998
+ attention_mask=attention_mask,
999
+ joint_attention_kwargs=self.joint_attention_kwargs,
1000
+ return_dict=False,
1001
+ )[0]
1002
+
1003
+ if self.do_classifier_free_guidance:
1004
+ if negative_image_embeds is not None:
1005
+ self._joint_attention_kwargs["ip_adapter_image_embeds"] = negative_image_embeds
1006
+
1007
+ noise_pred_uncond = self.transformer(
1008
+ hidden_states=latents,
1009
+ timestep=timestep / 1000,
1010
+ encoder_hidden_states=negative_prompt_embeds,
1011
+ txt_ids=negative_text_ids,
1012
+ img_ids=latent_image_ids,
1013
+ attention_mask=negative_attention_mask,
1014
+ joint_attention_kwargs=self.joint_attention_kwargs,
1015
+ return_dict=False,
1016
+ )[0]
1017
+ noise_pred = noise_pred_uncond + guidance_scale * (noise_pred - noise_pred_uncond)
1018
+
1019
+ # compute the previous noisy sample x_t -> x_t-1
1020
+ latents_dtype = latents.dtype
1021
+ latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
1022
+
1023
+ if latents.dtype != latents_dtype:
1024
+ if torch.backends.mps.is_available():
1025
+ # some platforms (eg. apple mps) misbehave due to a pytorch bug: https://github.com/pytorch/pytorch/pull/99272
1026
+ latents = latents.to(latents_dtype)
1027
+
1028
+ if callback_on_step_end is not None:
1029
+ callback_kwargs = {}
1030
+ for k in callback_on_step_end_tensor_inputs:
1031
+ callback_kwargs[k] = locals()[k]
1032
+ callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
1033
+
1034
+ latents = callback_outputs.pop("latents", latents)
1035
+ prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
1036
+
1037
+ # call the callback, if provided
1038
+ if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
1039
+ progress_bar.update()
1040
+
1041
+ if XLA_AVAILABLE:
1042
+ xm.mark_step()
1043
+
1044
+ self._current_timestep = None
1045
+
1046
+ if output_type == "latent":
1047
+ image = latents
1048
+ else:
1049
+ latents = self._unpack_latents(latents, height, width, self.vae_scale_factor)
1050
+ latents = (latents / self.vae.config.scaling_factor) + self.vae.config.shift_factor
1051
+ image = self.vae.decode(latents, return_dict=False)[0]
1052
+ image = self.image_processor.postprocess(image, output_type=output_type)
1053
+
1054
+ # Offload all models
1055
+ self.maybe_free_model_hooks()
1056
+
1057
+ if not return_dict:
1058
+ return (image,)
1059
+
1060
+ return ChromaPipelineOutput(images=image)
third_party/diffusers/src/diffusers/pipelines/chroma/pipeline_chroma_inpainting.py ADDED
@@ -0,0 +1,1184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ChromaInpaintPipeline implements a text-guided image inpainting pipeline for the lodestones/Chroma1-HD model, based on
3
+ the ChromaPipeline from Hugging Face Diffusers:contentReference[oaicite:0]{index=0} and the Stable Diffusion inpainting
4
+ approach:contentReference[oaicite:1]{index=1}.
5
+ """
6
+
7
+ # Copyright 2025 Black Forest Labs and The HuggingFace Team. All rights reserved.
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+ import inspect
21
+ from typing import Any, Callable, Dict, List, Optional, Union
22
+
23
+ import numpy as np
24
+ import PIL.Image
25
+ import torch
26
+ from transformers import (
27
+ CLIPImageProcessor,
28
+ CLIPVisionModelWithProjection,
29
+ T5EncoderModel,
30
+ T5TokenizerFast,
31
+ )
32
+
33
+ from ...image_processor import PipelineImageInput, VaeImageProcessor
34
+ from ...loaders import FluxIPAdapterMixin, FluxLoraLoaderMixin, FromSingleFileMixin, TextualInversionLoaderMixin
35
+ from ...models.autoencoders import AutoencoderKL
36
+ from ...models.transformers import ChromaTransformer2DModel
37
+ from ...schedulers import FlowMatchEulerDiscreteScheduler
38
+ from ...utils import (
39
+ USE_PEFT_BACKEND,
40
+ is_torch_xla_available,
41
+ logging,
42
+ replace_example_docstring,
43
+ scale_lora_layers,
44
+ unscale_lora_layers,
45
+ )
46
+ from ...utils.torch_utils import randn_tensor
47
+ from ..chroma.pipeline_output import ChromaPipelineOutput
48
+ from ..pipeline_utils import DiffusionPipeline
49
+
50
+
51
+ if is_torch_xla_available():
52
+ import torch_xla.core.xla_model as xm
53
+
54
+ XLA_AVAILABLE = True
55
+ else:
56
+ XLA_AVAILABLE = False
57
+
58
+
59
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
60
+
61
+ EXAMPLE_DOC_STRING = """
62
+ Examples:
63
+ ```py
64
+ >>> import torch
65
+ >>> from diffusers import ChromaInpaintPipeline
66
+ >>> from diffusers.utils import load_image
67
+
68
+ >>> pipe = ChromaInpaintPipeline.from_pretrained("lodestones/Chroma1-HD", torch_dtype=torch.bfloat16)
69
+ >>> pipe.to("cuda")
70
+ >>> prompt = "Face of a yellow cat, high resolution, sitting on a park bench"
71
+ >>> img_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo.png"
72
+ >>> mask_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo_mask.png"
73
+ >>> source = load_image(img_url)
74
+ >>> mask = load_image(mask_url)
75
+ >>> image = pipe(prompt=prompt, image=source, mask_image=mask).images[0]
76
+ >>> image.save("chroma_inpainting.png")
77
+ ```
78
+ """
79
+
80
+
81
+ # Copied from diffusers.pipelines.flux.pipeline_flux.calculate_shift
82
+ def calculate_shift(
83
+ image_seq_len,
84
+ base_seq_len: int = 256,
85
+ max_seq_len: int = 4096,
86
+ base_shift: float = 0.5,
87
+ max_shift: float = 1.15,
88
+ ):
89
+ m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
90
+ b = base_shift - m * base_seq_len
91
+ mu = image_seq_len * m + b
92
+ return mu
93
+
94
+
95
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.retrieve_latents
96
+ def retrieve_latents(
97
+ encoder_output: torch.Tensor, generator: torch.Generator | None = None, sample_mode: str = "sample"
98
+ ):
99
+ if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
100
+ return encoder_output.latent_dist.sample(generator)
101
+ elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
102
+ return encoder_output.latent_dist.mode()
103
+ elif hasattr(encoder_output, "latents"):
104
+ return encoder_output.latents
105
+ else:
106
+ raise AttributeError("Could not access latents of provided encoder_output")
107
+
108
+
109
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
110
+ def retrieve_timesteps(
111
+ scheduler,
112
+ num_inference_steps: int | None = None,
113
+ device: str | torch.device | None = None,
114
+ timesteps: list[int] | None = None,
115
+ sigmas: list[float] | None = None,
116
+ **kwargs,
117
+ ):
118
+ r"""
119
+ Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
120
+ custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
121
+
122
+ Args:
123
+ scheduler (`SchedulerMixin`):
124
+ The scheduler to get timesteps from.
125
+ num_inference_steps (`int`):
126
+ The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
127
+ must be `None`.
128
+ device (`str` or `torch.device`, *optional*):
129
+ The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
130
+ timesteps (`list[int]`, *optional*):
131
+ Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
132
+ `num_inference_steps` and `sigmas` must be `None`.
133
+ sigmas (`list[float]`, *optional*):
134
+ Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
135
+ `num_inference_steps` and `timesteps` must be `None`.
136
+
137
+ Returns:
138
+ `tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
139
+ second element is the number of inference steps.
140
+ """
141
+ if timesteps is not None and sigmas is not None:
142
+ raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
143
+ if timesteps is not None:
144
+ accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
145
+ if not accepts_timesteps:
146
+ raise ValueError(
147
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
148
+ f" timestep schedules. Please check whether you are using the correct scheduler."
149
+ )
150
+ scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
151
+ timesteps = scheduler.timesteps
152
+ num_inference_steps = len(timesteps)
153
+ elif sigmas is not None:
154
+ accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
155
+ if not accept_sigmas:
156
+ raise ValueError(
157
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
158
+ f" sigmas schedules. Please check whether you are using the correct scheduler."
159
+ )
160
+ scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
161
+ timesteps = scheduler.timesteps
162
+ num_inference_steps = len(timesteps)
163
+ else:
164
+ scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
165
+ timesteps = scheduler.timesteps
166
+ return timesteps, num_inference_steps
167
+
168
+
169
+ class ChromaInpaintPipeline(
170
+ DiffusionPipeline, FluxLoraLoaderMixin, FromSingleFileMixin, TextualInversionLoaderMixin, FluxIPAdapterMixin
171
+ ):
172
+ r"""
173
+ The Flux pipeline for image inpainting.
174
+
175
+ Reference: https://blackforestlabs.ai/announcing-black-forest-labs/
176
+
177
+ Args:
178
+ transformer ([`ChromaTransformer2DModel`]):
179
+ Conditional Transformer (MMDiT) architecture to denoise the encoded image latents.
180
+ scheduler ([`DDIMScheduler`]):
181
+ A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
182
+ vae ([`AutoencoderKL`]):
183
+ Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations.
184
+ text_encoder ([`CLIPTextModel`]):
185
+ [CLIP](https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPTextModel), specifically
186
+ the [clip-vit-large-patch14](https://huggingface.co/openai/clip-vit-large-patch14) variant.
187
+ text_encoder_2 ([`T5EncoderModel`]):
188
+ [T5](https://huggingface.co/docs/transformers/en/model_doc/t5#transformers.T5EncoderModel), specifically
189
+ the [google/t5-v1_1-xxl](https://huggingface.co/google/t5-v1_1-xxl) variant.
190
+ tokenizer (`CLIPTokenizer`):
191
+ Tokenizer of class
192
+ [CLIPTokenizer](https://huggingface.co/docs/transformers/en/model_doc/clip#transformers.CLIPTokenizer).
193
+ tokenizer_2 (`T5TokenizerFast`):
194
+ Second Tokenizer of class
195
+ [T5TokenizerFast](https://huggingface.co/docs/transformers/en/model_doc/t5#transformers.T5TokenizerFast).
196
+ """
197
+
198
+ model_cpu_offload_seq = "text_encoder->text_encoder_2->image_encoder->transformer->vae"
199
+ _optional_components = ["image_encoder", "feature_extractor"]
200
+ _callback_tensor_inputs = ["latents", "prompt_embeds"]
201
+
202
+ def __init__(
203
+ self,
204
+ scheduler: FlowMatchEulerDiscreteScheduler,
205
+ vae: AutoencoderKL,
206
+ text_encoder: T5EncoderModel,
207
+ tokenizer: T5TokenizerFast,
208
+ transformer: ChromaTransformer2DModel,
209
+ image_encoder: CLIPVisionModelWithProjection = None,
210
+ feature_extractor: CLIPImageProcessor = None,
211
+ ):
212
+ super().__init__()
213
+
214
+ self.register_modules(
215
+ vae=vae,
216
+ text_encoder=text_encoder,
217
+ tokenizer=tokenizer,
218
+ transformer=transformer,
219
+ scheduler=scheduler,
220
+ image_encoder=image_encoder,
221
+ feature_extractor=feature_extractor,
222
+ )
223
+ self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) if getattr(self, "vae", None) else 8
224
+ self.latent_channels = self.vae.config.latent_channels if getattr(self, "vae", None) else 16
225
+
226
+ # Flux latents are turned into 2x2 patches and packed. This means the latent width and height has to be divisible
227
+ # by the patch size. So the vae scale factor is multiplied by the patch size to account for this
228
+ self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor * 2)
229
+ self.default_sample_size = 128
230
+
231
+ self.mask_processor = VaeImageProcessor(
232
+ vae_scale_factor=self.vae_scale_factor * 2,
233
+ vae_latent_channels=self.latent_channels,
234
+ do_normalize=False,
235
+ do_binarize=True,
236
+ do_convert_grayscale=True,
237
+ )
238
+
239
+ def _get_t5_prompt_embeds(
240
+ self,
241
+ prompt: Union[str, List[str], None] = None,
242
+ num_images_per_prompt: int = 1,
243
+ max_sequence_length: int = 512,
244
+ device: torch.device | None = None,
245
+ dtype: torch.dtype | None = None,
246
+ ):
247
+ device = device or self._execution_device
248
+ dtype = dtype or self.text_encoder.dtype
249
+
250
+ prompt = [prompt] if isinstance(prompt, str) else prompt
251
+ batch_size = len(prompt)
252
+
253
+ if isinstance(self, TextualInversionLoaderMixin):
254
+ prompt = self.maybe_convert_prompt(prompt, self.tokenizer)
255
+
256
+ text_inputs = self.tokenizer(
257
+ prompt,
258
+ padding="max_length",
259
+ max_length=max_sequence_length,
260
+ truncation=True,
261
+ return_length=False,
262
+ return_overflowing_tokens=False,
263
+ return_tensors="pt",
264
+ )
265
+ text_input_ids = text_inputs.input_ids
266
+ tokenizer_mask = text_inputs.attention_mask
267
+
268
+ tokenizer_mask = tokenizer_mask.to(device)
269
+
270
+ prompt_embeds = self.text_encoder(
271
+ text_input_ids.to(device),
272
+ output_hidden_states=False,
273
+ attention_mask=tokenizer_mask,
274
+ )[0]
275
+
276
+ prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
277
+
278
+ seq_lengths = tokenizer_mask.sum(dim=1)
279
+ mask_indices = torch.arange(tokenizer_mask.size(1), device=device).unsqueeze(0).expand(batch_size, -1)
280
+ attention_mask = (mask_indices <= seq_lengths.unsqueeze(1)).to(dtype=dtype, device=device)
281
+
282
+ _, seq_len, _ = prompt_embeds.shape
283
+
284
+ # duplicate text embeddings and attention mask for each generation per prompt, using mps friendly method
285
+ prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
286
+ prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
287
+
288
+ attention_mask = attention_mask.repeat(1, num_images_per_prompt)
289
+ attention_mask = attention_mask.view(batch_size * num_images_per_prompt, seq_len)
290
+
291
+ return prompt_embeds, attention_mask
292
+
293
+ def encode_prompt(
294
+ self,
295
+ prompt: Union[str, List[str]],
296
+ negative_prompt: Union[str, List[str], None] = None,
297
+ device: torch.device | None = None,
298
+ num_images_per_prompt: int = 1,
299
+ prompt_embeds: torch.Tensor | None = None,
300
+ negative_prompt_embeds: torch.Tensor | None = None,
301
+ prompt_attention_mask: torch.Tensor | None = None,
302
+ negative_prompt_attention_mask: torch.Tensor | None = None,
303
+ do_classifier_free_guidance: bool = True,
304
+ max_sequence_length: int = 256,
305
+ lora_scale: bool | None = None,
306
+ ):
307
+ r"""
308
+
309
+ Args:
310
+ prompt (`str` or `List[str]`, *optional*):
311
+ prompt to be encoded
312
+ negative_prompt (`str` or `List[str]`, *optional*):
313
+ The prompt not to guide the image generation. If not defined, one has to pass `negative_prompt_embeds`
314
+ instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is less than `1`).
315
+ device: (`torch.device`):
316
+ torch device
317
+ num_images_per_prompt (`int`):
318
+ number of images that should be generated per prompt
319
+ prompt_embeds (`torch.Tensor`, *optional*):
320
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
321
+ provided, text embeddings will be generated from `prompt` input argument.
322
+ lora_scale (`float`, *optional*):
323
+ A lora scale that will be applied to all LoRA layers of the text encoder if LoRA layers are loaded.
324
+ """
325
+ device = device or self._execution_device
326
+
327
+ # set lora scale so that monkey patched LoRA
328
+ # function of text encoder can correctly access it
329
+ if lora_scale is not None and isinstance(self, FluxLoraLoaderMixin):
330
+ self._lora_scale = lora_scale
331
+
332
+ # dynamically adjust the LoRA scale
333
+ if self.text_encoder is not None and USE_PEFT_BACKEND:
334
+ scale_lora_layers(self.text_encoder, lora_scale)
335
+
336
+ prompt = [prompt] if isinstance(prompt, str) else prompt
337
+
338
+ if prompt is not None:
339
+ batch_size = len(prompt)
340
+ else:
341
+ batch_size = prompt_embeds.shape[0]
342
+
343
+ if prompt_embeds is None:
344
+ prompt_embeds, prompt_attention_mask = self._get_t5_prompt_embeds(
345
+ prompt=prompt,
346
+ num_images_per_prompt=num_images_per_prompt,
347
+ max_sequence_length=max_sequence_length,
348
+ device=device,
349
+ )
350
+
351
+ dtype = self.text_encoder.dtype if self.text_encoder is not None else self.transformer.dtype
352
+ text_ids = torch.zeros(prompt_embeds.shape[1], 3, device=device, dtype=dtype)
353
+ negative_text_ids = None
354
+
355
+ if do_classifier_free_guidance:
356
+ if negative_prompt_embeds is None:
357
+ negative_prompt = negative_prompt or ""
358
+ negative_prompt = (
359
+ batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
360
+ )
361
+
362
+ if prompt is not None and type(prompt) is not type(negative_prompt):
363
+ raise TypeError(
364
+ f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
365
+ f" {type(prompt)}."
366
+ )
367
+ elif batch_size != len(negative_prompt):
368
+ raise ValueError(
369
+ f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
370
+ f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
371
+ " the batch size of `prompt`."
372
+ )
373
+
374
+ negative_prompt_embeds, negative_prompt_attention_mask = self._get_t5_prompt_embeds(
375
+ prompt=negative_prompt,
376
+ num_images_per_prompt=num_images_per_prompt,
377
+ max_sequence_length=max_sequence_length,
378
+ device=device,
379
+ )
380
+
381
+ negative_text_ids = torch.zeros(negative_prompt_embeds.shape[1], 3, device=device, dtype=dtype)
382
+
383
+ if self.text_encoder is not None:
384
+ if isinstance(self, FluxLoraLoaderMixin) and USE_PEFT_BACKEND:
385
+ # Retrieve the original scale by scaling back the LoRA layers
386
+ unscale_lora_layers(self.text_encoder, lora_scale)
387
+
388
+ return (
389
+ prompt_embeds,
390
+ text_ids,
391
+ prompt_attention_mask,
392
+ negative_prompt_embeds,
393
+ negative_text_ids,
394
+ negative_prompt_attention_mask,
395
+ )
396
+
397
+ # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.encode_image
398
+ def encode_image(self, image, device, num_images_per_prompt):
399
+ dtype = next(self.image_encoder.parameters()).dtype
400
+
401
+ if not isinstance(image, torch.Tensor):
402
+ image = self.feature_extractor(image, return_tensors="pt").pixel_values
403
+
404
+ image = image.to(device=device, dtype=dtype)
405
+ image_embeds = self.image_encoder(image).image_embeds
406
+ image_embeds = image_embeds.repeat_interleave(num_images_per_prompt, dim=0)
407
+ return image_embeds
408
+
409
+ # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline.prepare_ip_adapter_image_embeds
410
+ def prepare_ip_adapter_image_embeds(
411
+ self, ip_adapter_image, ip_adapter_image_embeds, device, num_images_per_prompt
412
+ ):
413
+ image_embeds = []
414
+ if ip_adapter_image_embeds is None:
415
+ if not isinstance(ip_adapter_image, list):
416
+ ip_adapter_image = [ip_adapter_image]
417
+
418
+ if len(ip_adapter_image) != self.transformer.encoder_hid_proj.num_ip_adapters:
419
+ raise ValueError(
420
+ f"`ip_adapter_image` must have same length as the number of IP Adapters. Got {len(ip_adapter_image)} images and {self.transformer.encoder_hid_proj.num_ip_adapters} IP Adapters."
421
+ )
422
+
423
+ for single_ip_adapter_image in ip_adapter_image:
424
+ single_image_embeds = self.encode_image(single_ip_adapter_image, device, 1)
425
+ image_embeds.append(single_image_embeds[None, :])
426
+ else:
427
+ if not isinstance(ip_adapter_image_embeds, list):
428
+ ip_adapter_image_embeds = [ip_adapter_image_embeds]
429
+
430
+ if len(ip_adapter_image_embeds) != self.transformer.encoder_hid_proj.num_ip_adapters:
431
+ raise ValueError(
432
+ f"`ip_adapter_image_embeds` must have same length as the number of IP Adapters. Got {len(ip_adapter_image_embeds)} image embeds and {self.transformer.encoder_hid_proj.num_ip_adapters} IP Adapters."
433
+ )
434
+
435
+ for single_image_embeds in ip_adapter_image_embeds:
436
+ image_embeds.append(single_image_embeds)
437
+
438
+ ip_adapter_image_embeds = []
439
+ for single_image_embeds in image_embeds:
440
+ single_image_embeds = torch.cat([single_image_embeds] * num_images_per_prompt, dim=0)
441
+ single_image_embeds = single_image_embeds.to(device=device)
442
+ ip_adapter_image_embeds.append(single_image_embeds)
443
+
444
+ return ip_adapter_image_embeds
445
+
446
+ # Copied from diffusers.pipelines.stable_diffusion_3.pipeline_stable_diffusion_3_inpaint.StableDiffusion3InpaintPipeline._encode_vae_image
447
+ def _encode_vae_image(self, image: torch.Tensor, generator: torch.Generator):
448
+ if isinstance(generator, list):
449
+ image_latents = [
450
+ retrieve_latents(self.vae.encode(image[i : i + 1]), generator=generator[i])
451
+ for i in range(image.shape[0])
452
+ ]
453
+ image_latents = torch.cat(image_latents, dim=0)
454
+ else:
455
+ image_latents = retrieve_latents(self.vae.encode(image), generator=generator)
456
+
457
+ image_latents = (image_latents - self.vae.config.shift_factor) * self.vae.config.scaling_factor
458
+
459
+ return image_latents
460
+
461
+ # Copied from diffusers.pipelines.stable_diffusion_3.pipeline_stable_diffusion_3_img2img.StableDiffusion3Img2ImgPipeline.get_timesteps
462
+ def get_timesteps(self, num_inference_steps, strength, device):
463
+ # get the original timestep using init_timestep
464
+ init_timestep = min(num_inference_steps * strength, num_inference_steps)
465
+
466
+ t_start = int(max(num_inference_steps - init_timestep, 0))
467
+ timesteps = self.scheduler.timesteps[t_start * self.scheduler.order :]
468
+ if hasattr(self.scheduler, "set_begin_index"):
469
+ self.scheduler.set_begin_index(t_start * self.scheduler.order)
470
+
471
+ return timesteps, num_inference_steps - t_start
472
+
473
+ def check_inputs(
474
+ self,
475
+ prompt,
476
+ image,
477
+ mask_image,
478
+ strength,
479
+ height,
480
+ width,
481
+ output_type,
482
+ negative_prompt=None,
483
+ prompt_embeds=None,
484
+ negative_prompt_embeds=None,
485
+ callback_on_step_end_tensor_inputs=None,
486
+ padding_mask_crop=None,
487
+ max_sequence_length=None,
488
+ prompt_attention_mask=None,
489
+ negative_prompt_attention_mask=None,
490
+ ):
491
+ if strength < 0 or strength > 1:
492
+ raise ValueError(f"The value of strength should in [0.0, 1.0] but is {strength}")
493
+
494
+ if height % (self.vae_scale_factor * 2) != 0 or width % (self.vae_scale_factor * 2) != 0:
495
+ logger.warning(
496
+ f"`height` and `width` have to be divisible by {self.vae_scale_factor * 2} but are {height} and {width}. Dimensions will be resized accordingly"
497
+ )
498
+
499
+ if callback_on_step_end_tensor_inputs is not None and not all(
500
+ k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
501
+ ):
502
+ raise ValueError(
503
+ f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
504
+ )
505
+
506
+ if prompt is not None and prompt_embeds is not None:
507
+ raise ValueError(
508
+ f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
509
+ " only forward one of the two."
510
+ )
511
+ elif prompt is None and prompt_embeds is None:
512
+ raise ValueError(
513
+ "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
514
+ )
515
+ elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
516
+ raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
517
+
518
+ if negative_prompt is not None and negative_prompt_embeds is not None:
519
+ raise ValueError(
520
+ f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_prompt_embeds`:"
521
+ f" {negative_prompt_embeds}. Please make sure to only forward one of the two."
522
+ )
523
+
524
+ if prompt_embeds is not None and negative_prompt_embeds is not None:
525
+ if prompt_embeds.shape != negative_prompt_embeds.shape:
526
+ raise ValueError(
527
+ "`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but"
528
+ f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`"
529
+ f" {negative_prompt_embeds.shape}."
530
+ )
531
+
532
+ if prompt_embeds is not None and prompt_attention_mask is None:
533
+ raise ValueError("Cannot provide `prompt_embeds` without also providing `prompt_attention_mask")
534
+
535
+ if negative_prompt_embeds is not None and negative_prompt_attention_mask is None:
536
+ raise ValueError(
537
+ "Cannot provide `negative_prompt_embeds` without also providing `negative_prompt_attention_mask"
538
+ )
539
+
540
+ if padding_mask_crop is not None:
541
+ if not isinstance(image, PIL.Image.Image):
542
+ raise ValueError(
543
+ f"The image should be a PIL image when inpainting mask crop, but is of type {type(image)}."
544
+ )
545
+ if not isinstance(mask_image, PIL.Image.Image):
546
+ raise ValueError(
547
+ f"The mask image should be a PIL image when inpainting mask crop, but is of type"
548
+ f" {type(mask_image)}."
549
+ )
550
+ if output_type != "pil":
551
+ raise ValueError(f"The output type should be PIL when inpainting mask crop, but is {output_type}.")
552
+
553
+ if max_sequence_length is not None and max_sequence_length > 512:
554
+ raise ValueError(f"`max_sequence_length` cannot be greater than 512 but is {max_sequence_length}")
555
+
556
+ @staticmethod
557
+ # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline._prepare_latent_image_ids
558
+ def _prepare_latent_image_ids(batch_size, height, width, device, dtype):
559
+ latent_image_ids = torch.zeros(height, width, 3)
560
+ latent_image_ids[..., 1] = latent_image_ids[..., 1] + torch.arange(height)[:, None]
561
+ latent_image_ids[..., 2] = latent_image_ids[..., 2] + torch.arange(width)[None, :]
562
+
563
+ latent_image_id_height, latent_image_id_width, latent_image_id_channels = latent_image_ids.shape
564
+
565
+ latent_image_ids = latent_image_ids.reshape(
566
+ latent_image_id_height * latent_image_id_width, latent_image_id_channels
567
+ )
568
+
569
+ return latent_image_ids.to(device=device, dtype=dtype)
570
+
571
+ @staticmethod
572
+ # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline._pack_latents
573
+ def _pack_latents(latents, batch_size, num_channels_latents, height, width):
574
+ latents = latents.view(batch_size, num_channels_latents, height // 2, 2, width // 2, 2)
575
+ latents = latents.permute(0, 2, 4, 1, 3, 5)
576
+ latents = latents.reshape(batch_size, (height // 2) * (width // 2), num_channels_latents * 4)
577
+
578
+ return latents
579
+
580
+ @staticmethod
581
+ # Copied from diffusers.pipelines.flux.pipeline_flux.FluxPipeline._unpack_latents
582
+ def _unpack_latents(latents, height, width, vae_scale_factor):
583
+ batch_size, num_patches, channels = latents.shape
584
+
585
+ # VAE applies 8x compression on images but we must also account for packing which requires
586
+ # latent height and width to be divisible by 2.
587
+ height = 2 * (int(height) // (vae_scale_factor * 2))
588
+ width = 2 * (int(width) // (vae_scale_factor * 2))
589
+
590
+ latents = latents.view(batch_size, height // 2, width // 2, channels // 4, 2, 2)
591
+ latents = latents.permute(0, 3, 1, 4, 2, 5)
592
+
593
+ latents = latents.reshape(batch_size, channels // (2 * 2), height, width)
594
+
595
+ return latents
596
+
597
+ def prepare_latents(
598
+ self,
599
+ image,
600
+ timestep,
601
+ batch_size,
602
+ num_channels_latents,
603
+ height,
604
+ width,
605
+ dtype,
606
+ device,
607
+ generator,
608
+ latents=None,
609
+ ):
610
+ if isinstance(generator, list) and len(generator) != batch_size:
611
+ raise ValueError(
612
+ f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
613
+ f" size of {batch_size}. Make sure the batch size matches the length of the generators."
614
+ )
615
+
616
+ # VAE applies 8x compression on images but we must also account for packing which requires
617
+ # latent height and width to be divisible by 2.
618
+ height = 2 * (int(height) // (self.vae_scale_factor * 2))
619
+ width = 2 * (int(width) // (self.vae_scale_factor * 2))
620
+ shape = (batch_size, num_channels_latents, height, width)
621
+ latent_image_ids = self._prepare_latent_image_ids(batch_size, height // 2, width // 2, device, dtype)
622
+
623
+ image = image.to(device=device, dtype=dtype)
624
+ if image.shape[1] != self.latent_channels:
625
+ image_latents = self._encode_vae_image(image=image, generator=generator)
626
+ else:
627
+ image_latents = image
628
+
629
+ if batch_size > image_latents.shape[0] and batch_size % image_latents.shape[0] == 0:
630
+ # expand init_latents for batch_size
631
+ additional_image_per_prompt = batch_size // image_latents.shape[0]
632
+ image_latents = torch.cat([image_latents] * additional_image_per_prompt, dim=0)
633
+ elif batch_size > image_latents.shape[0] and batch_size % image_latents.shape[0] != 0:
634
+ raise ValueError(
635
+ f"Cannot duplicate `image` of batch size {image_latents.shape[0]} to {batch_size} text prompts."
636
+ )
637
+ else:
638
+ image_latents = torch.cat([image_latents], dim=0)
639
+
640
+ if latents is None:
641
+ noise = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
642
+ latents = self.scheduler.scale_noise(image_latents, timestep, noise)
643
+ else:
644
+ noise = latents.to(device)
645
+ latents = noise
646
+
647
+ noise = self._pack_latents(noise, batch_size, num_channels_latents, height, width)
648
+ image_latents = self._pack_latents(image_latents, batch_size, num_channels_latents, height, width)
649
+ latents = self._pack_latents(latents, batch_size, num_channels_latents, height, width)
650
+ return latents, noise, image_latents, latent_image_ids
651
+
652
+ def prepare_mask_latents(
653
+ self,
654
+ mask,
655
+ masked_image,
656
+ batch_size,
657
+ num_channels_latents,
658
+ num_images_per_prompt,
659
+ height,
660
+ width,
661
+ dtype,
662
+ device,
663
+ generator,
664
+ ):
665
+ # VAE applies 8x compression on images but we must also account for packing which requires
666
+ # latent height and width to be divisible by 2.
667
+ height = 2 * (int(height) // (self.vae_scale_factor * 2))
668
+ width = 2 * (int(width) // (self.vae_scale_factor * 2))
669
+ # resize the mask to latents shape as we concatenate the mask to the latents
670
+ # we do that before converting to dtype to avoid breaking in case we're using cpu_offload
671
+ # and half precision
672
+ mask = torch.nn.functional.interpolate(mask, size=(height, width))
673
+ mask = mask.to(device=device, dtype=dtype)
674
+
675
+ batch_size = batch_size * num_images_per_prompt
676
+
677
+ masked_image = masked_image.to(device=device, dtype=dtype)
678
+
679
+ if masked_image.shape[1] == 16:
680
+ masked_image_latents = masked_image
681
+ else:
682
+ masked_image_latents = retrieve_latents(self.vae.encode(masked_image), generator=generator)
683
+
684
+ masked_image_latents = (
685
+ masked_image_latents - self.vae.config.shift_factor
686
+ ) * self.vae.config.scaling_factor
687
+
688
+ # duplicate mask and masked_image_latents for each generation per prompt, using mps friendly method
689
+ if mask.shape[0] < batch_size:
690
+ if not batch_size % mask.shape[0] == 0:
691
+ raise ValueError(
692
+ "The passed mask and the required batch size don't match. Masks are supposed to be duplicated to"
693
+ f" a total batch size of {batch_size}, but {mask.shape[0]} masks were passed. Make sure the number"
694
+ " of masks that you pass is divisible by the total requested batch size."
695
+ )
696
+ mask = mask.repeat(batch_size // mask.shape[0], 1, 1, 1)
697
+ if masked_image_latents.shape[0] < batch_size:
698
+ if not batch_size % masked_image_latents.shape[0] == 0:
699
+ raise ValueError(
700
+ "The passed images and the required batch size don't match. Images are supposed to be duplicated"
701
+ f" to a total batch size of {batch_size}, but {masked_image_latents.shape[0]} images were passed."
702
+ " Make sure the number of images that you pass is divisible by the total requested batch size."
703
+ )
704
+ masked_image_latents = masked_image_latents.repeat(batch_size // masked_image_latents.shape[0], 1, 1, 1)
705
+
706
+ # aligning device to prevent device errors when concating it with the latent model input
707
+ masked_image_latents = masked_image_latents.to(device=device, dtype=dtype)
708
+ masked_image_latents = self._pack_latents(
709
+ masked_image_latents,
710
+ batch_size,
711
+ num_channels_latents,
712
+ height,
713
+ width,
714
+ )
715
+ mask = self._pack_latents(
716
+ mask.repeat(1, num_channels_latents, 1, 1),
717
+ batch_size,
718
+ num_channels_latents,
719
+ height,
720
+ width,
721
+ )
722
+
723
+ return mask, masked_image_latents
724
+
725
+ @property
726
+ def guidance_scale(self):
727
+ return self._guidance_scale
728
+
729
+ @property
730
+ def joint_attention_kwargs(self):
731
+ return self._joint_attention_kwargs
732
+
733
+ @property
734
+ def num_timesteps(self):
735
+ return self._num_timesteps
736
+
737
+ @property
738
+ def interrupt(self):
739
+ return self._interrupt
740
+
741
+ def _prepare_attention_mask(
742
+ self,
743
+ batch_size,
744
+ sequence_length,
745
+ dtype,
746
+ attention_mask=None,
747
+ ):
748
+ if attention_mask is None:
749
+ return attention_mask
750
+
751
+ # Extend the prompt attention mask to account for image tokens in the final sequence
752
+ attention_mask = torch.cat(
753
+ [attention_mask, torch.ones(batch_size, sequence_length, device=attention_mask.device, dtype=torch.bool)],
754
+ dim=1,
755
+ )
756
+ attention_mask = attention_mask.to(dtype)
757
+
758
+ return attention_mask
759
+
760
+ @property
761
+ def do_classifier_free_guidance(self):
762
+ return self._guidance_scale > 1
763
+
764
+ @replace_example_docstring(EXAMPLE_DOC_STRING)
765
+ @torch.no_grad()
766
+ def __call__(
767
+ self,
768
+ prompt: Union[str, List[str]] = None,
769
+ negative_prompt: Union[str, List[str]] = None,
770
+ true_cfg_scale: float = 1.0,
771
+ image: PipelineImageInput = None,
772
+ mask_image: PipelineImageInput = None,
773
+ masked_image_latents: PipelineImageInput = None,
774
+ height: int | None = None,
775
+ width: int | None = None,
776
+ padding_mask_crop: int | None = None,
777
+ strength: float = 0.6,
778
+ num_inference_steps: int = 28,
779
+ sigmas: list[float] | None = None,
780
+ guidance_scale: float = 7.0,
781
+ num_images_per_prompt: Optional[int] = 1,
782
+ generator: torch.Generator | list[torch.Generator] | None = None,
783
+ latents: Optional[torch.FloatTensor] = None,
784
+ prompt_embeds: Optional[torch.FloatTensor] = None,
785
+ ip_adapter_image: Optional[PipelineImageInput] = None,
786
+ ip_adapter_image_embeds: Optional[List[torch.Tensor]] = None,
787
+ negative_ip_adapter_image: Optional[PipelineImageInput] = None,
788
+ negative_ip_adapter_image_embeds: Optional[List[torch.Tensor]] = None,
789
+ negative_prompt_embeds: Optional[torch.FloatTensor] = None,
790
+ output_type: str = "pil",
791
+ return_dict: bool = True,
792
+ joint_attention_kwargs: Optional[Dict[str, Any]] = None,
793
+ callback_on_step_end: Optional[Callable[[int, int, Dict], None]] = None,
794
+ callback_on_step_end_tensor_inputs: List[str] = ["latents"],
795
+ max_sequence_length: int = 256,
796
+ prompt_attention_mask: torch.Tensor | None = None,
797
+ negative_prompt_attention_mask: torch.Tensor | None = None,
798
+ ):
799
+ r"""
800
+ Function invoked when calling the pipeline for generation.
801
+
802
+ Args:
803
+ prompt (`str` or `List[str]`, *optional*):
804
+ The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
805
+ instead.
806
+ negative_prompt (`str` or `List[str]`, *optional*):
807
+ The prompt or prompts not to guide the image generation. If not defined, one has to pass
808
+ `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
809
+ not greater than `1`).
810
+ height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
811
+ The height in pixels of the generated image. This is set to 1024 by default for the best results.
812
+ width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
813
+ The width in pixels of the generated image. This is set to 1024 by default for the best results.
814
+ num_inference_steps (`int`, *optional*, defaults to 35):
815
+ The number of denoising steps. More denoising steps usually lead to a higher quality image at the
816
+ expense of slower inference.
817
+ sigmas (`List[float]`, *optional*):
818
+ Custom sigmas to use for the denoising process with schedulers which support a `sigmas` argument in
819
+ their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is passed
820
+ will be used.
821
+ guidance_scale (`float`, *optional*, defaults to 3.5):
822
+ Guidance scale as defined in [Classifier-Free Diffusion
823
+ Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
824
+ of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
825
+ `guidance_scale > 1`. Higher guidance scale encourages to generate images that are closely linked to
826
+ the text `prompt`, usually at the expense of lower image quality.
827
+ strength (`float, *optional*, defaults to 0.9):
828
+ Conceptually, indicates how much to transform the reference image. Must be between 0 and 1. image will
829
+ be used as a starting point, adding more noise to it the larger the strength. The number of denoising
830
+ steps depends on the amount of noise initially added. When strength is 1, added noise will be maximum
831
+ and the denoising process will run for the full number of iterations specified in num_inference_steps.
832
+ A value of 1, therefore, essentially ignores image.
833
+ num_images_per_prompt (`int`, *optional*, defaults to 1):
834
+ The number of images to generate per prompt.
835
+ generator (`torch.Generator` or `List[torch.Generator]`, *optional*):
836
+ One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)
837
+ to make generation deterministic.
838
+ latents (`torch.Tensor`, *optional*):
839
+ Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image
840
+ generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
841
+ tensor will be generated by sampling using the supplied random `generator`.
842
+ prompt_embeds (`torch.Tensor`, *optional*):
843
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
844
+ provided, text embeddings will be generated from `prompt` input argument.
845
+ ip_adapter_image: (`PipelineImageInput`, *optional*): Optional image input to work with IP Adapters.
846
+ ip_adapter_image_embeds (`List[torch.Tensor]`, *optional*):
847
+ Pre-generated image embeddings for IP-Adapter. It should be a list of length same as number of
848
+ IP-adapters. Each element should be a tensor of shape `(batch_size, num_images, emb_dim)`. If not
849
+ provided, embeddings are computed from the `ip_adapter_image` input argument.
850
+ negative_ip_adapter_image:
851
+ (`PipelineImageInput`, *optional*): Optional image input to work with IP Adapters.
852
+ negative_ip_adapter_image_embeds (`List[torch.Tensor]`, *optional*):
853
+ Pre-generated image embeddings for IP-Adapter. It should be a list of length same as number of
854
+ IP-adapters. Each element should be a tensor of shape `(batch_size, num_images, emb_dim)`. If not
855
+ provided, embeddings are computed from the `ip_adapter_image` input argument.
856
+ negative_prompt_embeds (`torch.Tensor`, *optional*):
857
+ Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
858
+ weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
859
+ argument.
860
+ prompt_attention_mask (torch.Tensor, *optional*):
861
+ Attention mask for the prompt embeddings. Used to mask out padding tokens in the prompt sequence.
862
+ Chroma requires a single padding token remain unmasked. Please refer to
863
+ https://huggingface.co/lodestones/Chroma#tldr-masking-t5-padding-tokens-enhanced-fidelity-and-increased-stability-during-training
864
+ negative_prompt_attention_mask (torch.Tensor, *optional*):
865
+ Attention mask for the negative prompt embeddings. Used to mask out padding tokens in the negative
866
+ prompt sequence. Chroma requires a single padding token remain unmasked. PLease refer to
867
+ https://huggingface.co/lodestones/Chroma#tldr-masking-t5-padding-tokens-enhanced-fidelity-and-increased-stability-during-training
868
+ output_type (`str`, *optional*, defaults to `"pil"`):
869
+ The output format of the generate image. Choose between
870
+ [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`.
871
+ return_dict (`bool`, *optional*, defaults to `True`):
872
+ Whether or not to return a [`~pipelines.chroma.ChromaPipelineOutput`] instead of a plain tuple.
873
+ joint_attention_kwargs (`dict`, *optional*):
874
+ A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
875
+ `self.processor` in
876
+ [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
877
+ callback_on_step_end (`Callable`, *optional*):
878
+ A function that calls at the end of each denoising steps during the inference. The function is called
879
+ with the following arguments: `callback_on_step_end(self: DiffusionPipeline, step: int, timestep: int,
880
+ callback_kwargs: Dict)`. `callback_kwargs` will include a list of all tensors as specified by
881
+ `callback_on_step_end_tensor_inputs`.
882
+ callback_on_step_end_tensor_inputs (`List`, *optional*):
883
+ The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
884
+ will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
885
+ `._callback_tensor_inputs` attribute of your pipeline class.
886
+ max_sequence_length (`int` defaults to 512): Maximum sequence length to use with the `prompt`.
887
+
888
+ Examples:
889
+
890
+ Returns:
891
+ [`~pipelines.chroma.ChromaPipelineOutput`] or `tuple`: [`~pipelines.chroma.ChromaPipelineOutput`] if
892
+ `return_dict` is True, otherwise a `tuple`. When returning a tuple, the first element is a list with the
893
+ generated images.
894
+ """
895
+
896
+ height = height or self.default_sample_size * self.vae_scale_factor
897
+ width = width or self.default_sample_size * self.vae_scale_factor
898
+
899
+ # 1. Check inputs. Raise error if not correct
900
+ self.check_inputs(
901
+ prompt=prompt,
902
+ height=height,
903
+ width=width,
904
+ output_type=output_type,
905
+ strength=strength,
906
+ negative_prompt=negative_prompt,
907
+ prompt_embeds=prompt_embeds,
908
+ negative_prompt_embeds=negative_prompt_embeds,
909
+ prompt_attention_mask=prompt_attention_mask,
910
+ negative_prompt_attention_mask=negative_prompt_attention_mask,
911
+ callback_on_step_end_tensor_inputs=callback_on_step_end_tensor_inputs,
912
+ max_sequence_length=max_sequence_length,
913
+ image=image,
914
+ mask_image=mask_image,
915
+ padding_mask_crop=padding_mask_crop,
916
+ )
917
+
918
+ self._guidance_scale = guidance_scale
919
+ self._joint_attention_kwargs = joint_attention_kwargs
920
+ self._current_timestep = None
921
+ self._interrupt = False
922
+
923
+ # 2. Preprocess mask and image
924
+ if padding_mask_crop is not None:
925
+ crops_coords = self.mask_processor.get_crop_region(mask_image, width, height, pad=padding_mask_crop)
926
+ resize_mode = "fill"
927
+ else:
928
+ crops_coords = None
929
+ resize_mode = "default"
930
+
931
+ original_image = image
932
+ init_image = self.image_processor.preprocess(
933
+ image, height=height, width=width, crops_coords=crops_coords, resize_mode=resize_mode
934
+ )
935
+ init_image = init_image.to(dtype=torch.float32)
936
+
937
+ # 3. Define call parameters
938
+ if prompt is not None and isinstance(prompt, str):
939
+ batch_size = 1
940
+ elif prompt is not None and isinstance(prompt, list):
941
+ batch_size = len(prompt)
942
+ else:
943
+ batch_size = prompt_embeds.shape[0]
944
+
945
+ device = self._execution_device
946
+ lora_scale = (
947
+ self.joint_attention_kwargs.get("scale", None) if self.joint_attention_kwargs is not None else None
948
+ )
949
+
950
+ (
951
+ prompt_embeds,
952
+ text_ids,
953
+ prompt_attention_mask,
954
+ negative_prompt_embeds,
955
+ negative_text_ids,
956
+ negative_prompt_attention_mask,
957
+ ) = self.encode_prompt(
958
+ prompt=prompt,
959
+ negative_prompt=negative_prompt,
960
+ prompt_embeds=prompt_embeds,
961
+ negative_prompt_embeds=negative_prompt_embeds,
962
+ prompt_attention_mask=prompt_attention_mask,
963
+ negative_prompt_attention_mask=negative_prompt_attention_mask,
964
+ do_classifier_free_guidance=self.do_classifier_free_guidance,
965
+ device=device,
966
+ num_images_per_prompt=num_images_per_prompt,
967
+ max_sequence_length=max_sequence_length,
968
+ lora_scale=lora_scale,
969
+ )
970
+
971
+ # 4. Prepare timesteps
972
+ sigmas = np.linspace(1.0, 1 / num_inference_steps, num_inference_steps) if sigmas is None else sigmas
973
+ image_seq_len = (int(height) // self.vae_scale_factor // 2) * (int(width) // self.vae_scale_factor // 2)
974
+ mu = calculate_shift(
975
+ image_seq_len,
976
+ self.scheduler.config.get("base_image_seq_len", 256),
977
+ self.scheduler.config.get("max_image_seq_len", 4096),
978
+ self.scheduler.config.get("base_shift", 0.5),
979
+ self.scheduler.config.get("max_shift", 1.15),
980
+ )
981
+ timesteps, num_inference_steps = retrieve_timesteps(
982
+ self.scheduler,
983
+ num_inference_steps,
984
+ device,
985
+ sigmas=sigmas,
986
+ mu=mu,
987
+ )
988
+ timesteps, num_inference_steps = self.get_timesteps(num_inference_steps, strength, device)
989
+
990
+ num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
991
+ self._num_timesteps = len(timesteps)
992
+
993
+ if num_inference_steps < 1:
994
+ raise ValueError(
995
+ f"After adjusting the num_inference_steps by strength parameter: {strength}, the number of pipeline"
996
+ f"steps is {num_inference_steps} which is < 1 and not appropriate for this pipeline."
997
+ )
998
+ latent_timestep = timesteps[:1].repeat(batch_size * num_images_per_prompt)
999
+
1000
+ # 5. Prepare latent variables
1001
+ num_channels_latents = self.transformer.config.in_channels // 4
1002
+ num_channels_transformer = self.transformer.config.in_channels
1003
+
1004
+ latents, noise, image_latents, latent_image_ids = self.prepare_latents(
1005
+ init_image,
1006
+ latent_timestep,
1007
+ batch_size * num_images_per_prompt,
1008
+ num_channels_latents,
1009
+ height,
1010
+ width,
1011
+ prompt_embeds.dtype,
1012
+ device,
1013
+ generator,
1014
+ latents,
1015
+ )
1016
+
1017
+ mask_condition = self.mask_processor.preprocess(
1018
+ mask_image, height=height, width=width, resize_mode=resize_mode, crops_coords=crops_coords
1019
+ )
1020
+
1021
+ if masked_image_latents is None:
1022
+ masked_image = init_image * (mask_condition < 0.5)
1023
+ else:
1024
+ masked_image = masked_image_latents
1025
+
1026
+ mask, masked_image_latents = self.prepare_mask_latents(
1027
+ mask_condition,
1028
+ masked_image,
1029
+ batch_size,
1030
+ num_channels_latents,
1031
+ num_images_per_prompt,
1032
+ height,
1033
+ width,
1034
+ prompt_embeds.dtype,
1035
+ device,
1036
+ generator,
1037
+ )
1038
+
1039
+ num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
1040
+ self._num_timesteps = len(timesteps)
1041
+
1042
+ # handle guidance
1043
+ if self.transformer.config.guidance_embeds:
1044
+ guidance = torch.full([1], guidance_scale, device=device, dtype=torch.float32)
1045
+ guidance = guidance.expand(latents.shape[0])
1046
+ else:
1047
+ guidance = None
1048
+
1049
+ if (ip_adapter_image is not None or ip_adapter_image_embeds is not None) and (
1050
+ negative_ip_adapter_image is None and negative_ip_adapter_image_embeds is None
1051
+ ):
1052
+ negative_ip_adapter_image = np.zeros((width, height, 3), dtype=np.uint8)
1053
+ elif (ip_adapter_image is None and ip_adapter_image_embeds is None) and (
1054
+ negative_ip_adapter_image is not None or negative_ip_adapter_image_embeds is not None
1055
+ ):
1056
+ ip_adapter_image = np.zeros((width, height, 3), dtype=np.uint8)
1057
+
1058
+ if self.joint_attention_kwargs is None:
1059
+ self._joint_attention_kwargs = {}
1060
+
1061
+ image_embeds = None
1062
+ negative_image_embeds = None
1063
+ if ip_adapter_image is not None or ip_adapter_image_embeds is not None:
1064
+ image_embeds = self.prepare_ip_adapter_image_embeds(
1065
+ ip_adapter_image,
1066
+ ip_adapter_image_embeds,
1067
+ device,
1068
+ batch_size * num_images_per_prompt,
1069
+ )
1070
+ if negative_ip_adapter_image is not None or negative_ip_adapter_image_embeds is not None:
1071
+ negative_image_embeds = self.prepare_ip_adapter_image_embeds(
1072
+ negative_ip_adapter_image,
1073
+ negative_ip_adapter_image_embeds,
1074
+ device,
1075
+ batch_size * num_images_per_prompt,
1076
+ )
1077
+
1078
+ attention_mask = self._prepare_attention_mask(
1079
+ batch_size=latents.shape[0],
1080
+ sequence_length=image_seq_len,
1081
+ dtype=latents.dtype,
1082
+ attention_mask=prompt_attention_mask,
1083
+ )
1084
+ negative_attention_mask = self._prepare_attention_mask(
1085
+ batch_size=latents.shape[0],
1086
+ sequence_length=image_seq_len,
1087
+ dtype=latents.dtype,
1088
+ attention_mask=negative_prompt_attention_mask,
1089
+ )
1090
+
1091
+ # 6. Denoising loop
1092
+ with self.progress_bar(total=num_inference_steps) as progress_bar:
1093
+ for i, t in enumerate(timesteps):
1094
+ if self.interrupt:
1095
+ continue
1096
+
1097
+ self._current_timestep = t
1098
+ # broadcast to batch dimension in a way that's compatible with ONNX/Core ML
1099
+ timestep = t.expand(latents.shape[0]).to(latents.dtype)
1100
+
1101
+ if image_embeds is not None:
1102
+ self._joint_attention_kwargs["ip_adapter_image_embeds"] = image_embeds
1103
+
1104
+ noise_pred = self.transformer(
1105
+ hidden_states=latents,
1106
+ timestep=timestep / 1000,
1107
+ encoder_hidden_states=prompt_embeds,
1108
+ txt_ids=text_ids,
1109
+ img_ids=latent_image_ids,
1110
+ attention_mask=attention_mask,
1111
+ joint_attention_kwargs=self.joint_attention_kwargs,
1112
+ return_dict=False,
1113
+ )[0]
1114
+
1115
+ if self.do_classifier_free_guidance:
1116
+ if negative_image_embeds is not None:
1117
+ self._joint_attention_kwargs["ip_adapter_image_embeds"] = negative_image_embeds
1118
+
1119
+ noise_pred_uncond = self.transformer(
1120
+ hidden_states=latents,
1121
+ timestep=timestep / 1000,
1122
+ encoder_hidden_states=negative_prompt_embeds,
1123
+ txt_ids=negative_text_ids,
1124
+ img_ids=latent_image_ids,
1125
+ attention_mask=negative_attention_mask,
1126
+ joint_attention_kwargs=self.joint_attention_kwargs,
1127
+ return_dict=False,
1128
+ )[0]
1129
+ noise_pred = noise_pred_uncond + guidance_scale * (noise_pred - noise_pred_uncond)
1130
+
1131
+ # compute the previous noisy sample x_t -> x_t-1
1132
+ latents_dtype = latents.dtype
1133
+ latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
1134
+
1135
+ # for 64 channel transformer only.
1136
+ init_latents_proper = image_latents
1137
+ init_mask = mask
1138
+
1139
+ if i < len(timesteps) - 1:
1140
+ noise_timestep = timesteps[i + 1]
1141
+ init_latents_proper = self.scheduler.scale_noise(
1142
+ init_latents_proper, torch.tensor([noise_timestep]), noise
1143
+ )
1144
+
1145
+ latents = (1 - init_mask) * init_latents_proper + init_mask * latents
1146
+
1147
+ if latents.dtype != latents_dtype:
1148
+ if torch.backends.mps.is_available():
1149
+ # some platforms (eg. apple mps) misbehave due to a pytorch bug: https://github.com/pytorch/pytorch/pull/99272
1150
+ latents = latents.to(latents_dtype)
1151
+
1152
+ if callback_on_step_end is not None:
1153
+ callback_kwargs = {}
1154
+ for k in callback_on_step_end_tensor_inputs:
1155
+ callback_kwargs[k] = locals()[k]
1156
+ callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
1157
+
1158
+ latents = callback_outputs.pop("latents", latents)
1159
+ prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
1160
+
1161
+ # call the callback, if provided
1162
+ if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
1163
+ progress_bar.update()
1164
+
1165
+ if XLA_AVAILABLE:
1166
+ xm.mark_step()
1167
+
1168
+ self._current_timestep = None
1169
+
1170
+ if output_type == "latent":
1171
+ image = latents
1172
+ else:
1173
+ latents = self._unpack_latents(latents, height, width, self.vae_scale_factor)
1174
+ latents = (latents / self.vae.config.scaling_factor) + self.vae.config.shift_factor
1175
+ image = self.vae.decode(latents, return_dict=False)[0]
1176
+ image = self.image_processor.postprocess(image, output_type=output_type)
1177
+
1178
+ # Offload all models
1179
+ self.maybe_free_model_hooks()
1180
+
1181
+ if not return_dict:
1182
+ return (image,)
1183
+
1184
+ return ChromaPipelineOutput(images=image)
third_party/diffusers/src/diffusers/pipelines/chroma/pipeline_output.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass
2
+
3
+ import numpy as np
4
+ import PIL.Image
5
+
6
+ from ...utils import BaseOutput
7
+
8
+
9
+ @dataclass
10
+ class ChromaPipelineOutput(BaseOutput):
11
+ """
12
+ Output class for Stable Diffusion pipelines.
13
+
14
+ Args:
15
+ images (`list[PIL.Image.Image]` or `np.ndarray`)
16
+ list of denoised PIL images of length `batch_size` or numpy array of shape `(batch_size, height, width,
17
+ num_channels)`. PIL images or numpy array present the denoised images of the diffusion pipeline.
18
+ """
19
+
20
+ images: list[PIL.Image.Image] | np.ndarray
third_party/diffusers/src/diffusers/pipelines/cosmos/__init__.py ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import TYPE_CHECKING
2
+
3
+ from ...utils import (
4
+ DIFFUSERS_SLOW_IMPORT,
5
+ OptionalDependencyNotAvailable,
6
+ _LazyModule,
7
+ get_objects_from_module,
8
+ is_torch_available,
9
+ is_transformers_available,
10
+ )
11
+
12
+
13
+ _dummy_objects = {}
14
+ _import_structure = {}
15
+
16
+
17
+ try:
18
+ if not (is_transformers_available() and is_torch_available()):
19
+ raise OptionalDependencyNotAvailable()
20
+ except OptionalDependencyNotAvailable:
21
+ from ...utils import dummy_torch_and_transformers_objects # noqa F403
22
+
23
+ _dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
24
+ else:
25
+ _import_structure["pipeline_cosmos2_5_predict"] = [
26
+ "Cosmos2_5_PredictBasePipeline",
27
+ ]
28
+ _import_structure["pipeline_cosmos2_5_transfer"] = [
29
+ "Cosmos2_5_TransferPipeline",
30
+ ]
31
+ _import_structure["pipeline_cosmos2_text2image"] = ["Cosmos2TextToImagePipeline"]
32
+ _import_structure["pipeline_cosmos2_video2world"] = ["Cosmos2VideoToWorldPipeline"]
33
+ _import_structure["pipeline_cosmos_text2world"] = ["CosmosTextToWorldPipeline"]
34
+ _import_structure["pipeline_cosmos_video2world"] = ["CosmosVideoToWorldPipeline"]
35
+
36
+ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
37
+ try:
38
+ if not (is_transformers_available() and is_torch_available()):
39
+ raise OptionalDependencyNotAvailable()
40
+
41
+ except OptionalDependencyNotAvailable:
42
+ from ...utils.dummy_torch_and_transformers_objects import *
43
+ else:
44
+ from .pipeline_cosmos2_5_predict import (
45
+ Cosmos2_5_PredictBasePipeline,
46
+ )
47
+ from .pipeline_cosmos2_5_transfer import Cosmos2_5_TransferPipeline
48
+ from .pipeline_cosmos2_text2image import Cosmos2TextToImagePipeline
49
+ from .pipeline_cosmos2_video2world import Cosmos2VideoToWorldPipeline
50
+ from .pipeline_cosmos_text2world import CosmosTextToWorldPipeline
51
+ from .pipeline_cosmos_video2world import CosmosVideoToWorldPipeline
52
+
53
+ else:
54
+ import sys
55
+
56
+ sys.modules[__name__] = _LazyModule(
57
+ __name__,
58
+ globals()["__file__"],
59
+ _import_structure,
60
+ module_spec=__spec__,
61
+ )
62
+
63
+ for name, value in _dummy_objects.items():
64
+ setattr(sys.modules[__name__], name, value)
third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_cosmos2_5_predict.py ADDED
@@ -0,0 +1,888 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The NVIDIA Team and The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from typing import Callable
16
+
17
+ import numpy as np
18
+ import torch
19
+ from transformers import AutoTokenizer, Qwen2_5_VLForConditionalGeneration
20
+
21
+ from ...callbacks import MultiPipelineCallbacks, PipelineCallback
22
+ from ...image_processor import PipelineImageInput
23
+ from ...models import AutoencoderKLWan, CosmosTransformer3DModel
24
+ from ...schedulers import UniPCMultistepScheduler
25
+ from ...utils import (
26
+ is_cosmos_guardrail_available,
27
+ is_torch_xla_available,
28
+ is_torchvision_available,
29
+ logging,
30
+ replace_example_docstring,
31
+ )
32
+ from ...utils.torch_utils import randn_tensor
33
+ from ...video_processor import VideoProcessor
34
+ from ..pipeline_utils import DiffusionPipeline
35
+ from .pipeline_output import CosmosPipelineOutput
36
+
37
+
38
+ if is_torchvision_available():
39
+ import torchvision.transforms.functional
40
+
41
+
42
+ if is_cosmos_guardrail_available():
43
+ from cosmos_guardrail import CosmosSafetyChecker
44
+ else:
45
+
46
+ class CosmosSafetyChecker:
47
+ def __init__(self, *args, **kwargs):
48
+ raise ImportError(
49
+ "`cosmos_guardrail` is not installed. Please install it to use the safety checker for Cosmos: `pip install cosmos_guardrail`."
50
+ )
51
+
52
+
53
+ if is_torch_xla_available():
54
+ import torch_xla.core.xla_model as xm
55
+
56
+ XLA_AVAILABLE = True
57
+ else:
58
+ XLA_AVAILABLE = False
59
+
60
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
61
+
62
+ DEFAULT_NEGATIVE_PROMPT = (
63
+ "The video captures a series of frames showing ugly scenes, static with no motion, motion blur, "
64
+ "over-saturation, shaky footage, low resolution, grainy texture, pixelated images, poorly lit areas, "
65
+ "underexposed and overexposed scenes, poor color balance, washed out colors, choppy sequences, "
66
+ "jerky movements, low frame rate, artifacting, color banding, unnatural transitions, outdated special effects, "
67
+ "fake elements, unconvincing visuals, poorly edited content, jump cuts, visual noise, and flickering. "
68
+ "Overall, the video is of poor quality."
69
+ )
70
+
71
+
72
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.retrieve_latents
73
+ def retrieve_latents(
74
+ encoder_output: torch.Tensor, generator: torch.Generator | None = None, sample_mode: str = "sample"
75
+ ):
76
+ if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
77
+ return encoder_output.latent_dist.sample(generator)
78
+ elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
79
+ return encoder_output.latent_dist.mode()
80
+ elif hasattr(encoder_output, "latents"):
81
+ return encoder_output.latents
82
+ else:
83
+ raise AttributeError("Could not access latents of provided encoder_output")
84
+
85
+
86
+ EXAMPLE_DOC_STRING = """
87
+ Examples:
88
+ ```python
89
+ >>> import torch
90
+ >>> from diffusers import Cosmos2_5_PredictBasePipeline
91
+ >>> from diffusers.utils import export_to_video, load_image, load_video
92
+
93
+ >>> model_id = "nvidia/Cosmos-Predict2.5-2B"
94
+ >>> pipe = Cosmos2_5_PredictBasePipeline.from_pretrained(
95
+ ... model_id, revision="diffusers/base/post-trained", torch_dtype=torch.bfloat16
96
+ ... )
97
+ >>> pipe = pipe.to("cuda")
98
+
99
+ >>> # Common negative prompt reused across modes.
100
+ >>> negative_prompt = (
101
+ ... "The video captures a series of frames showing ugly scenes, static with no motion, motion blur, "
102
+ ... "over-saturation, shaky footage, low resolution, grainy texture, pixelated images, poorly lit areas, "
103
+ ... "underexposed and overexposed scenes, poor color balance, washed out colors, choppy sequences, jerky "
104
+ ... "movements, low frame rate, artifacting, color banding, unnatural transitions, outdated special effects, "
105
+ ... "fake elements, unconvincing visuals, poorly edited content, jump cuts, visual noise, and flickering. "
106
+ ... "Overall, the video is of poor quality."
107
+ ... )
108
+
109
+ >>> # Text2World: generate a 93-frame world video from text only.
110
+ >>> prompt = (
111
+ ... "As the red light shifts to green, the red bus at the intersection begins to move forward, its headlights "
112
+ ... "cutting through the falling snow. The snowy tire tracks deepen as the vehicle inches ahead, casting fresh "
113
+ ... "lines onto the slushy road. Around it, streetlights glow warmer, illuminating the drifting flakes and wet "
114
+ ... "reflections on the asphalt. Other cars behind start to edge forward, their beams joining the scene. "
115
+ ... "The stillness of the urban street transitions into motion as the quiet snowfall is punctuated by the slow "
116
+ ... "advance of traffic through the frosty city corridor."
117
+ ... )
118
+ >>> video = pipe(
119
+ ... image=None,
120
+ ... video=None,
121
+ ... prompt=prompt,
122
+ ... negative_prompt=negative_prompt,
123
+ ... num_frames=93,
124
+ ... generator=torch.Generator().manual_seed(1),
125
+ ... ).frames[0]
126
+ >>> export_to_video(video, "text2world.mp4", fps=16)
127
+
128
+ >>> # Image2World: condition on a single image and generate a 93-frame world video.
129
+ >>> prompt = (
130
+ ... "A high-definition video captures the precision of robotic welding in an industrial setting. "
131
+ ... "The first frame showcases a robotic arm, equipped with a welding torch, positioned over a large metal structure. "
132
+ ... "The welding process is in full swing, with bright sparks and intense light illuminating the scene, creating a vivid "
133
+ ... "display of blue and white hues. A significant amount of smoke billows around the welding area, partially obscuring "
134
+ ... "the view but emphasizing the heat and activity. The background reveals parts of the workshop environment, including a "
135
+ ... "ventilation system and various pieces of machinery, indicating a busy and functional industrial workspace. As the video "
136
+ ... "progresses, the robotic arm maintains its steady position, continuing the welding process and moving to its left. "
137
+ ... "The welding torch consistently emits sparks and light, and the smoke continues to rise, diffusing slightly as it moves upward. "
138
+ ... "The metal surface beneath the torch shows ongoing signs of heating and melting. The scene retains its industrial ambiance, with "
139
+ ... "the welding sparks and smoke dominating the visual field, underscoring the ongoing nature of the welding operation."
140
+ ... )
141
+ >>> image = load_image(
142
+ ... "https://media.githubusercontent.com/media/nvidia-cosmos/cosmos-predict2.5/refs/heads/main/assets/base/robot_welding.jpg"
143
+ ... )
144
+ >>> video = pipe(
145
+ ... image=image,
146
+ ... video=None,
147
+ ... prompt=prompt,
148
+ ... negative_prompt=negative_prompt,
149
+ ... num_frames=93,
150
+ ... generator=torch.Generator().manual_seed(1),
151
+ ... ).frames[0]
152
+ >>> export_to_video(video, "image2world.mp4", fps=16)
153
+
154
+ >>> # Video2World: condition on an input clip and predict a 93-frame world video.
155
+ >>> prompt = (
156
+ ... "The video opens with an aerial view of a large-scale sand mining construction operation, showcasing extensive piles "
157
+ ... "of brown sand meticulously arranged in parallel rows. A central water channel, fed by a water pipe, flows through the "
158
+ ... "middle of these sand heaps, creating ripples and movement as it cascades down. The surrounding area features dense green "
159
+ ... "vegetation on the left, contrasting with the sandy terrain, while a body of water is visible in the background on the right. "
160
+ ... "As the video progresses, a piece of heavy machinery, likely a bulldozer, enters the frame from the right, moving slowly along "
161
+ ... "the edge of the sand piles. This machinery's presence indicates ongoing construction work in the operation. The final frame "
162
+ ... "captures the same scene, with the water continuing its flow and the bulldozer still in motion, maintaining the dynamic yet "
163
+ ... "steady pace of the construction activity."
164
+ ... )
165
+ >>> input_video = load_video(
166
+ ... "https://github.com/nvidia-cosmos/cosmos-predict2.5/raw/refs/heads/main/assets/base/sand_mining.mp4"
167
+ ... )
168
+ >>> video = pipe(
169
+ ... image=None,
170
+ ... video=input_video,
171
+ ... prompt=prompt,
172
+ ... negative_prompt=negative_prompt,
173
+ ... num_frames=93,
174
+ ... generator=torch.Generator().manual_seed(1),
175
+ ... ).frames[0]
176
+ >>> export_to_video(video, "video2world.mp4", fps=16)
177
+
178
+ >>> # To produce an image instead of a world (video) clip, set num_frames=1 and
179
+ >>> # save the first frame: pipe(..., num_frames=1).frames[0][0].
180
+ ```
181
+ """
182
+
183
+
184
+ class Cosmos2_5_PredictBasePipeline(DiffusionPipeline):
185
+ r"""
186
+ Pipeline for [Cosmos Predict2.5](https://github.com/nvidia-cosmos/cosmos-predict2.5) base model.
187
+
188
+ This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
189
+ implemented for all pipelines (downloading, saving, running on a particular device, etc.).
190
+
191
+ Args:
192
+ text_encoder ([`Qwen2_5_VLForConditionalGeneration`]):
193
+ Frozen text-encoder. Cosmos Predict2.5 uses the [Qwen2.5
194
+ VL](https://huggingface.co/Qwen/Qwen2.5-VL-7B-Instruct) encoder.
195
+ tokenizer (`AutoTokenizer`):
196
+ Tokenizer associated with the Qwen2.5 VL encoder.
197
+ transformer ([`CosmosTransformer3DModel`]):
198
+ Conditional Transformer to denoise the encoded image latents.
199
+ scheduler ([`UniPCMultistepScheduler`]):
200
+ A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
201
+ vae ([`AutoencoderKLWan`]):
202
+ Variational Auto-Encoder (VAE) Model to encode and decode videos to and from latent representations.
203
+ """
204
+
205
+ model_cpu_offload_seq = "text_encoder->transformer->vae"
206
+ _callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds"]
207
+ # We mark safety_checker as optional here to get around some test failures, but it is not really optional
208
+ _optional_components = ["safety_checker"]
209
+ _exclude_from_cpu_offload = ["safety_checker"]
210
+
211
+ def __init__(
212
+ self,
213
+ text_encoder: Qwen2_5_VLForConditionalGeneration,
214
+ tokenizer: AutoTokenizer,
215
+ transformer: CosmosTransformer3DModel,
216
+ vae: AutoencoderKLWan,
217
+ scheduler: UniPCMultistepScheduler,
218
+ safety_checker: CosmosSafetyChecker = None,
219
+ ):
220
+ super().__init__()
221
+
222
+ if safety_checker is None:
223
+ safety_checker = CosmosSafetyChecker()
224
+
225
+ self.register_modules(
226
+ vae=vae,
227
+ text_encoder=text_encoder,
228
+ tokenizer=tokenizer,
229
+ transformer=transformer,
230
+ scheduler=scheduler,
231
+ safety_checker=safety_checker,
232
+ )
233
+
234
+ self.vae_scale_factor_temporal = 2 ** sum(self.vae.temperal_downsample) if getattr(self, "vae", None) else 4
235
+ self.vae_scale_factor_spatial = 2 ** len(self.vae.temperal_downsample) if getattr(self, "vae", None) else 8
236
+ self.video_processor = VideoProcessor(vae_scale_factor=self.vae_scale_factor_spatial)
237
+
238
+ latents_mean = (
239
+ torch.tensor(self.vae.config.latents_mean).view(1, self.vae.config.z_dim, 1, 1, 1).float()
240
+ if getattr(self.vae.config, "latents_mean", None) is not None
241
+ else None
242
+ )
243
+ latents_std = (
244
+ torch.tensor(self.vae.config.latents_std).view(1, self.vae.config.z_dim, 1, 1, 1).float()
245
+ if getattr(self.vae.config, "latents_std", None) is not None
246
+ else None
247
+ )
248
+ self.latents_mean = latents_mean
249
+ self.latents_std = latents_std
250
+
251
+ if self.latents_mean is None or self.latents_std is None:
252
+ raise ValueError("VAE configuration must define both `latents_mean` and `latents_std`.")
253
+
254
+ def _get_prompt_embeds(
255
+ self,
256
+ prompt: str | list[str] = None,
257
+ max_sequence_length: int = 512,
258
+ device: torch.device | None = None,
259
+ dtype: torch.dtype | None = None,
260
+ ):
261
+ device = device or self._execution_device
262
+ dtype = dtype or self.text_encoder.dtype
263
+ prompt = [prompt] if isinstance(prompt, str) else prompt
264
+
265
+ input_ids_batch = []
266
+
267
+ for sample_idx in range(len(prompt)):
268
+ conversations = [
269
+ {
270
+ "role": "system",
271
+ "content": [
272
+ {
273
+ "type": "text",
274
+ "text": "You are a helpful assistant who will provide prompts to an image generator.",
275
+ }
276
+ ],
277
+ },
278
+ {
279
+ "role": "user",
280
+ "content": [
281
+ {
282
+ "type": "text",
283
+ "text": prompt[sample_idx],
284
+ }
285
+ ],
286
+ },
287
+ ]
288
+ input_ids = self.tokenizer.apply_chat_template(
289
+ conversations,
290
+ tokenize=True,
291
+ add_generation_prompt=False,
292
+ add_vision_id=False,
293
+ max_length=max_sequence_length,
294
+ truncation=True,
295
+ padding="max_length",
296
+ )
297
+ input_ids = (
298
+ input_ids["input_ids"] if not isinstance(input_ids, list) and "input_ids" in input_ids else input_ids
299
+ )
300
+ input_ids = torch.LongTensor(input_ids)
301
+ input_ids_batch.append(input_ids)
302
+
303
+ input_ids_batch = torch.stack(input_ids_batch, dim=0)
304
+
305
+ outputs = self.text_encoder(
306
+ input_ids_batch.to(device),
307
+ output_hidden_states=True,
308
+ )
309
+ hidden_states = outputs.hidden_states
310
+
311
+ normalized_hidden_states = []
312
+ for layer_idx in range(1, len(hidden_states)):
313
+ normalized_state = (hidden_states[layer_idx] - hidden_states[layer_idx].mean(dim=-1, keepdim=True)) / (
314
+ hidden_states[layer_idx].std(dim=-1, keepdim=True) + 1e-8
315
+ )
316
+ normalized_hidden_states.append(normalized_state)
317
+
318
+ prompt_embeds = torch.cat(normalized_hidden_states, dim=-1)
319
+ prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
320
+
321
+ return prompt_embeds
322
+
323
+ # Modified from diffusers.pipelines.cosmos.pipeline_cosmos_text2world.CosmosTextToWorldPipeline.encode_prompt
324
+ def encode_prompt(
325
+ self,
326
+ prompt: str | list[str],
327
+ negative_prompt: str | list[str] | None = None,
328
+ do_classifier_free_guidance: bool = True,
329
+ num_videos_per_prompt: int = 1,
330
+ prompt_embeds: torch.Tensor | None = None,
331
+ negative_prompt_embeds: torch.Tensor | None = None,
332
+ max_sequence_length: int = 512,
333
+ device: torch.device | None = None,
334
+ dtype: torch.dtype | None = None,
335
+ ):
336
+ r"""
337
+ Encodes the prompt into text encoder hidden states.
338
+
339
+ Args:
340
+ prompt (`str` or `list[str]`, *optional*):
341
+ prompt to be encoded
342
+ negative_prompt (`str` or `list[str]`, *optional*):
343
+ The prompt or prompts not to guide the image generation. If not defined, one has to pass
344
+ `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
345
+ less than `1`).
346
+ do_classifier_free_guidance (`bool`, *optional*, defaults to `True`):
347
+ Whether to use classifier free guidance or not.
348
+ num_videos_per_prompt (`int`, *optional*, defaults to 1):
349
+ Number of videos that should be generated per prompt. torch device to place the resulting embeddings on
350
+ prompt_embeds (`torch.Tensor`, *optional*):
351
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
352
+ provided, text embeddings will be generated from `prompt` input argument.
353
+ negative_prompt_embeds (`torch.Tensor`, *optional*):
354
+ Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
355
+ weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
356
+ argument.
357
+ device: (`torch.device`, *optional*):
358
+ torch device
359
+ dtype: (`torch.dtype`, *optional*):
360
+ torch dtype
361
+ """
362
+ device = device or self._execution_device
363
+
364
+ prompt = [prompt] if isinstance(prompt, str) else prompt
365
+ if prompt is not None:
366
+ batch_size = len(prompt)
367
+ else:
368
+ batch_size = prompt_embeds.shape[0]
369
+
370
+ if prompt_embeds is None:
371
+ prompt_embeds = self._get_prompt_embeds(
372
+ prompt=prompt, max_sequence_length=max_sequence_length, device=device, dtype=dtype
373
+ )
374
+
375
+ # duplicate text embeddings for each generation per prompt, using mps friendly method
376
+ _, seq_len, _ = prompt_embeds.shape
377
+ prompt_embeds = prompt_embeds.repeat(1, num_videos_per_prompt, 1)
378
+ prompt_embeds = prompt_embeds.view(batch_size * num_videos_per_prompt, seq_len, -1)
379
+
380
+ if do_classifier_free_guidance and negative_prompt_embeds is None:
381
+ negative_prompt = negative_prompt if negative_prompt is not None else DEFAULT_NEGATIVE_PROMPT
382
+ negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
383
+
384
+ if prompt is not None and type(prompt) is not type(negative_prompt):
385
+ raise TypeError(
386
+ f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
387
+ f" {type(prompt)}."
388
+ )
389
+ elif batch_size != len(negative_prompt):
390
+ raise ValueError(
391
+ f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
392
+ f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
393
+ " the batch size of `prompt`."
394
+ )
395
+
396
+ negative_prompt_embeds = self._get_prompt_embeds(
397
+ prompt=negative_prompt, max_sequence_length=max_sequence_length, device=device, dtype=dtype
398
+ )
399
+
400
+ # duplicate text embeddings for each generation per prompt, using mps friendly method
401
+ _, seq_len, _ = negative_prompt_embeds.shape
402
+ negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_videos_per_prompt, 1)
403
+ negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_videos_per_prompt, seq_len, -1)
404
+
405
+ return prompt_embeds, negative_prompt_embeds
406
+
407
+ # Modified from diffusers.pipelines.cosmos.pipeline_cosmos2_video2world.Cosmos2VideoToWorldPipeline.prepare_latents and
408
+ # diffusers.pipelines.cosmos.pipeline_cosmos2_video2world.Cosmos2TextToImagePipeline.prepare_latents
409
+ def prepare_latents(
410
+ self,
411
+ video: torch.Tensor | None,
412
+ batch_size: int,
413
+ num_channels_latents: int = 16,
414
+ height: int = 704,
415
+ width: int = 1280,
416
+ num_frames_in: int = 93,
417
+ num_frames_out: int = 93,
418
+ do_classifier_free_guidance: bool = True,
419
+ dtype: torch.dtype | None = None,
420
+ device: torch.device | None = None,
421
+ generator: torch.Generator | list[torch.Generator] | None = None,
422
+ latents: torch.Tensor | None = None,
423
+ ) -> torch.Tensor:
424
+ if isinstance(generator, list) and len(generator) != batch_size:
425
+ raise ValueError(
426
+ f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
427
+ f" size of {batch_size}. Make sure the batch size matches the length of the generators."
428
+ )
429
+
430
+ B = batch_size
431
+ C = num_channels_latents
432
+ T = (num_frames_out - 1) // self.vae_scale_factor_temporal + 1
433
+ H = height // self.vae_scale_factor_spatial
434
+ W = width // self.vae_scale_factor_spatial
435
+ shape = (B, C, T, H, W)
436
+
437
+ if num_frames_in == 0:
438
+ if latents is None:
439
+ latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
440
+
441
+ cond_mask = torch.zeros((B, 1, T, H, W), dtype=latents.dtype, device=latents.device)
442
+ cond_indicator = torch.zeros((B, 1, T, 1, 1), dtype=latents.dtype, device=latents.device)
443
+
444
+ cond_latents = torch.zeros_like(latents)
445
+
446
+ return (
447
+ latents,
448
+ cond_latents,
449
+ cond_mask,
450
+ cond_indicator,
451
+ )
452
+ else:
453
+ if video is None:
454
+ raise ValueError("`video` must be provided when `num_frames_in` is greater than 0.")
455
+ needs_preprocessing = not (isinstance(video, torch.Tensor) and video.ndim == 5 and video.shape[1] == 3)
456
+ if needs_preprocessing:
457
+ video = self.video_processor.preprocess_video(video, height, width)
458
+ video = video.to(device=device, dtype=self.vae.dtype)
459
+ if isinstance(generator, list):
460
+ cond_latents = [
461
+ retrieve_latents(self.vae.encode(video[i].unsqueeze(0)), generator=generator[i])
462
+ for i in range(batch_size)
463
+ ]
464
+ else:
465
+ cond_latents = [retrieve_latents(self.vae.encode(vid.unsqueeze(0)), generator) for vid in video]
466
+
467
+ cond_latents = torch.cat(cond_latents, dim=0).to(dtype)
468
+
469
+ latents_mean = self.latents_mean.to(device=device, dtype=dtype)
470
+ latents_std = self.latents_std.to(device=device, dtype=dtype)
471
+ cond_latents = (cond_latents - latents_mean) / latents_std
472
+
473
+ if latents is None:
474
+ latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
475
+ else:
476
+ latents = latents.to(device=device, dtype=dtype)
477
+
478
+ padding_shape = (B, 1, T, H, W)
479
+ ones_padding = latents.new_ones(padding_shape)
480
+ zeros_padding = latents.new_zeros(padding_shape)
481
+
482
+ num_cond_latent_frames = (num_frames_in - 1) // self.vae_scale_factor_temporal + 1
483
+ cond_indicator = latents.new_zeros(1, 1, latents.size(2), 1, 1)
484
+ cond_indicator[:, :, 0:num_cond_latent_frames] = 1.0
485
+ cond_mask = cond_indicator * ones_padding + (1 - cond_indicator) * zeros_padding
486
+
487
+ return (
488
+ latents,
489
+ cond_latents,
490
+ cond_mask,
491
+ cond_indicator,
492
+ )
493
+
494
+ # Copied from diffusers.pipelines.cosmos.pipeline_cosmos_text2world.CosmosTextToWorldPipeline.check_inputs
495
+ def check_inputs(
496
+ self,
497
+ prompt,
498
+ height,
499
+ width,
500
+ prompt_embeds=None,
501
+ callback_on_step_end_tensor_inputs=None,
502
+ ):
503
+ if height % 16 != 0 or width % 16 != 0:
504
+ raise ValueError(f"`height` and `width` have to be divisible by 16 but are {height} and {width}.")
505
+
506
+ if callback_on_step_end_tensor_inputs is not None and not all(
507
+ k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
508
+ ):
509
+ raise ValueError(
510
+ f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
511
+ )
512
+
513
+ if prompt is not None and prompt_embeds is not None:
514
+ raise ValueError(
515
+ f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
516
+ " only forward one of the two."
517
+ )
518
+ elif prompt is None and prompt_embeds is None:
519
+ raise ValueError(
520
+ "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
521
+ )
522
+ elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
523
+ raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
524
+
525
+ @property
526
+ def guidance_scale(self):
527
+ return self._guidance_scale
528
+
529
+ @property
530
+ def do_classifier_free_guidance(self):
531
+ return self._guidance_scale > 1.0
532
+
533
+ @property
534
+ def num_timesteps(self):
535
+ return self._num_timesteps
536
+
537
+ @property
538
+ def current_timestep(self):
539
+ return self._current_timestep
540
+
541
+ @property
542
+ def interrupt(self):
543
+ return self._interrupt
544
+
545
+ @torch.no_grad()
546
+ @replace_example_docstring(EXAMPLE_DOC_STRING)
547
+ def __call__(
548
+ self,
549
+ image: PipelineImageInput | None = None,
550
+ video: list[PipelineImageInput] | None = None,
551
+ prompt: str | list[str] | None = None,
552
+ negative_prompt: str | list[str] | None = None,
553
+ height: int = 704,
554
+ width: int = 1280,
555
+ num_frames: int = 93,
556
+ num_inference_steps: int = 36,
557
+ guidance_scale: float = 7.0,
558
+ num_videos_per_prompt: int | None = 1,
559
+ generator: torch.Generator | list[torch.Generator] | None = None,
560
+ latents: torch.Tensor | None = None,
561
+ prompt_embeds: torch.Tensor | None = None,
562
+ negative_prompt_embeds: torch.Tensor | None = None,
563
+ output_type: str | None = "pil",
564
+ return_dict: bool = True,
565
+ callback_on_step_end: Callable[[int, int, None], PipelineCallback | MultiPipelineCallbacks] | None = None,
566
+ callback_on_step_end_tensor_inputs: list[str] = ["latents"],
567
+ max_sequence_length: int = 512,
568
+ conditional_frame_timestep: float = 0.1,
569
+ num_latent_conditional_frames: int = 2,
570
+ ):
571
+ r"""
572
+ The call function to the pipeline for generation. Supports three modes:
573
+
574
+ - **Text2World**: `image=None`, `video=None`, `prompt` provided. Generates a world clip.
575
+ - **Image2World**: `image` provided, `video=None`, `prompt` provided. Conditions on a single frame.
576
+ - **Video2World**: `video` provided, `image=None`, `prompt` provided. Conditions on an input clip.
577
+
578
+ Set `num_frames=93` (default) to produce a world video, or `num_frames=1` to produce a single image frame (the
579
+ above in "*2Image mode").
580
+
581
+ Outputs follow `output_type` (e.g., `"pil"` returns a list of `num_frames` PIL images per prompt).
582
+
583
+ Args:
584
+ image (`PIL.Image.Image`, `np.ndarray`, `torch.Tensor`, *optional*):
585
+ Optional single image for Image2World conditioning. Must be `None` when `video` is provided.
586
+ video (`list[PIL.Image.Image]`, `np.ndarray`, `torch.Tensor`, *optional*):
587
+ Optional input video for Video2World conditioning. Must be `None` when `image` is provided.
588
+ prompt (`str` or `list[str]`, *optional*):
589
+ The prompt or prompts to guide generation. Required unless `prompt_embeds` is supplied.
590
+ height (`int`, defaults to `704`):
591
+ The height in pixels of the generated image.
592
+ width (`int`, defaults to `1280`):
593
+ The width in pixels of the generated image.
594
+ num_frames (`int`, defaults to `93`):
595
+ Number of output frames. Use `93` for world (video) generation; set to `1` to return a single frame.
596
+ num_inference_steps (`int`, defaults to `35`):
597
+ The number of denoising steps. More denoising steps usually lead to a higher quality image at the
598
+ expense of slower inference.
599
+ guidance_scale (`float`, defaults to `7.0`):
600
+ Guidance scale as defined in [Classifier-Free Diffusion
601
+ Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
602
+ of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
603
+ `guidance_scale > 1`.
604
+ num_videos_per_prompt (`int`, *optional*, defaults to 1):
605
+ The number of images to generate per prompt.
606
+ generator (`torch.Generator` or `list[torch.Generator]`, *optional*):
607
+ A [`torch.Generator`](https://pytorch.org/docs/stable/generated/torch.Generator.html) to make
608
+ generation deterministic.
609
+ latents (`torch.Tensor`, *optional*):
610
+ Pre-generated noisy latents sampled from a Gaussian distribution, to be used as inputs for image
611
+ generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
612
+ tensor is generated by sampling using the supplied random `generator`.
613
+ prompt_embeds (`torch.Tensor`, *optional*):
614
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
615
+ provided, text embeddings will be generated from `prompt` input argument.
616
+ negative_prompt_embeds (`torch.FloatTensor`, *optional*):
617
+ Pre-generated negative text embeddings. For PixArt-Sigma this negative prompt should be "". If not
618
+ provided, negative_prompt_embeds will be generated from `negative_prompt` input argument.
619
+ output_type (`str`, *optional*, defaults to `"pil"`):
620
+ The output format of the generated image. Choose between `PIL.Image` or `np.array`.
621
+ return_dict (`bool`, *optional*, defaults to `True`):
622
+ Whether or not to return a [`CosmosPipelineOutput`] instead of a plain tuple.
623
+ callback_on_step_end (`Callable`, `PipelineCallback`, `MultiPipelineCallbacks`, *optional*):
624
+ A function or a subclass of `PipelineCallback` or `MultiPipelineCallbacks` that is called at the end of
625
+ each denoising step during the inference. with the following arguments: `callback_on_step_end(self:
626
+ DiffusionPipeline, step: int, timestep: int, callback_kwargs: Dict)`. `callback_kwargs` will include a
627
+ list of all tensors as specified by `callback_on_step_end_tensor_inputs`.
628
+ callback_on_step_end_tensor_inputs (`List`, *optional*):
629
+ The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
630
+ will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
631
+ `._callback_tensor_inputs` attribute of your pipeline class.
632
+ max_sequence_length (`int`, defaults to `512`):
633
+ The maximum number of tokens in the prompt. If the prompt exceeds this length, it will be truncated. If
634
+ the prompt is shorter than this length, it will be padded.
635
+ num_latent_conditional_frames (`int`, defaults to `2`):
636
+ Number of latent conditional frames to use for Video2World conditioning. The number of pixel frames
637
+ extracted from the input video is calculated as `4 * (num_latent_conditional_frames - 1) + 1`. Set to 1
638
+ for Image2World-like behavior (single frame conditioning).
639
+
640
+ Examples:
641
+
642
+ Returns:
643
+ [`~CosmosPipelineOutput`] or `tuple`:
644
+ If `return_dict` is `True`, [`CosmosPipelineOutput`] is returned, otherwise a `tuple` is returned where
645
+ the first element is a list with the generated images and the second element is a list of `bool`s
646
+ indicating whether the corresponding generated image contains "not-safe-for-work" (nsfw) content.
647
+ """
648
+ if self.safety_checker is None:
649
+ raise ValueError(
650
+ f"You have disabled the safety checker for {self.__class__}. This is in violation of the "
651
+ "[NVIDIA Open Model License Agreement](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license). "
652
+ f"Please ensure that you are compliant with the license agreement."
653
+ )
654
+
655
+ if isinstance(callback_on_step_end, (PipelineCallback, MultiPipelineCallbacks)):
656
+ callback_on_step_end_tensor_inputs = callback_on_step_end.tensor_inputs
657
+
658
+ # Check inputs. Raise error if not correct
659
+ self.check_inputs(prompt, height, width, prompt_embeds, callback_on_step_end_tensor_inputs)
660
+
661
+ self._guidance_scale = guidance_scale
662
+ self._current_timestep = None
663
+ self._interrupt = False
664
+
665
+ device = self._execution_device
666
+
667
+ if self.safety_checker is not None:
668
+ self.safety_checker.to(device)
669
+ if prompt is not None:
670
+ prompt_list = [prompt] if isinstance(prompt, str) else prompt
671
+ for p in prompt_list:
672
+ if not self.safety_checker.check_text_safety(p):
673
+ raise ValueError(
674
+ f"Cosmos Guardrail detected unsafe text in the prompt: {p}. Please ensure that the "
675
+ f"prompt abides by the NVIDIA Open Model License Agreement."
676
+ )
677
+
678
+ # Define call parameters
679
+ if prompt is not None and isinstance(prompt, str):
680
+ batch_size = 1
681
+ elif prompt is not None and isinstance(prompt, list):
682
+ batch_size = len(prompt)
683
+ else:
684
+ batch_size = prompt_embeds.shape[0]
685
+
686
+ # Encode input prompt
687
+ (
688
+ prompt_embeds,
689
+ negative_prompt_embeds,
690
+ ) = self.encode_prompt(
691
+ prompt=prompt,
692
+ negative_prompt=negative_prompt,
693
+ do_classifier_free_guidance=self.do_classifier_free_guidance,
694
+ num_videos_per_prompt=num_videos_per_prompt,
695
+ prompt_embeds=prompt_embeds,
696
+ negative_prompt_embeds=negative_prompt_embeds,
697
+ device=device,
698
+ max_sequence_length=max_sequence_length,
699
+ )
700
+
701
+ vae_dtype = self.vae.dtype
702
+ transformer_dtype = self.transformer.dtype
703
+
704
+ num_frames_in = None
705
+ if image is not None:
706
+ if batch_size != 1:
707
+ raise ValueError(f"batch_size must be 1 for image input (given {batch_size})")
708
+
709
+ image = torchvision.transforms.functional.to_tensor(image).unsqueeze(0)
710
+ video = torch.cat([image, torch.zeros_like(image).repeat(num_frames - 1, 1, 1, 1)], dim=0)
711
+ video = video.unsqueeze(0)
712
+ num_frames_in = 1
713
+ elif video is None:
714
+ video = torch.zeros(batch_size, num_frames, 3, height, width, dtype=torch.uint8)
715
+ num_frames_in = 0
716
+ else:
717
+ if batch_size != 1:
718
+ raise ValueError(f"batch_size must be 1 for video input (given {batch_size})")
719
+
720
+ if num_latent_conditional_frames not in [1, 2]:
721
+ raise ValueError(
722
+ f"num_latent_conditional_frames must be 1 or 2, but got {num_latent_conditional_frames}"
723
+ )
724
+
725
+ frames_to_extract = 4 * (num_latent_conditional_frames - 1) + 1
726
+
727
+ total_input_frames = len(video)
728
+
729
+ if total_input_frames < frames_to_extract:
730
+ raise ValueError(
731
+ f"Input video has only {total_input_frames} frames but Video2World requires at least "
732
+ f"{frames_to_extract} frames for conditioning."
733
+ )
734
+
735
+ num_frames_in = frames_to_extract
736
+
737
+ assert video is not None
738
+ video = self.video_processor.preprocess_video(video, height, width)
739
+
740
+ # For Video2World: extract last frames_to_extract frames from input, then pad
741
+ if image is None and num_frames_in > 0 and num_frames_in < video.shape[2]:
742
+ video = video[:, :, -num_frames_in:, :, :]
743
+
744
+ num_frames_out = num_frames
745
+
746
+ if video.shape[2] < num_frames_out:
747
+ n_pad_frames = num_frames_out - video.shape[2]
748
+ last_frame = video[:, :, -1:, :, :] # [B, C, T==1, H, W]
749
+ pad_frames = last_frame.repeat(1, 1, n_pad_frames, 1, 1) # [B, C, T, H, W]
750
+ video = torch.cat((video, pad_frames), dim=2)
751
+
752
+ assert num_frames_in <= num_frames_out, f"expected ({num_frames_in=}) <= ({num_frames_out=})"
753
+
754
+ video = video.to(device=device, dtype=vae_dtype)
755
+
756
+ num_channels_latents = self.transformer.config.in_channels - 1
757
+ latents, cond_latent, cond_mask, cond_indicator = self.prepare_latents(
758
+ video=video,
759
+ batch_size=batch_size * num_videos_per_prompt,
760
+ num_channels_latents=num_channels_latents,
761
+ height=height,
762
+ width=width,
763
+ num_frames_in=num_frames_in,
764
+ num_frames_out=num_frames,
765
+ do_classifier_free_guidance=self.do_classifier_free_guidance,
766
+ dtype=torch.float32,
767
+ device=device,
768
+ generator=generator,
769
+ latents=latents,
770
+ )
771
+ cond_timestep = torch.ones_like(cond_indicator) * conditional_frame_timestep
772
+ cond_mask = cond_mask.to(transformer_dtype)
773
+
774
+ padding_mask = latents.new_zeros(1, 1, height, width, dtype=transformer_dtype)
775
+
776
+ # Denoising loop
777
+ self.scheduler.set_timesteps(num_inference_steps, device=device)
778
+ timesteps = self.scheduler.timesteps
779
+ self._num_timesteps = len(timesteps)
780
+ num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
781
+
782
+ gt_velocity = (latents - cond_latent) * cond_mask
783
+ with self.progress_bar(total=num_inference_steps) as progress_bar:
784
+ for i, t in enumerate(timesteps):
785
+ if self.interrupt:
786
+ continue
787
+
788
+ self._current_timestep = t.cpu().item()
789
+
790
+ # NOTE: assumes sigma(t) \in [0, 1]
791
+ sigma_t = (
792
+ torch.tensor(self.scheduler.sigmas[i].item())
793
+ .unsqueeze(0)
794
+ .to(device=device, dtype=transformer_dtype)
795
+ )
796
+
797
+ in_latents = cond_mask * cond_latent + (1 - cond_mask) * latents
798
+ in_latents = in_latents.to(transformer_dtype)
799
+ in_timestep = cond_indicator * cond_timestep + (1 - cond_indicator) * sigma_t
800
+ noise_pred = self.transformer(
801
+ hidden_states=in_latents,
802
+ condition_mask=cond_mask,
803
+ timestep=in_timestep,
804
+ encoder_hidden_states=prompt_embeds,
805
+ padding_mask=padding_mask,
806
+ return_dict=False,
807
+ )[0]
808
+ # NOTE: replace velocity (noise_pred) with gt_velocity for conditioning inputs only
809
+ noise_pred = gt_velocity + noise_pred * (1 - cond_mask)
810
+
811
+ if self.do_classifier_free_guidance:
812
+ noise_pred_neg = self.transformer(
813
+ hidden_states=in_latents,
814
+ condition_mask=cond_mask,
815
+ timestep=in_timestep,
816
+ encoder_hidden_states=negative_prompt_embeds,
817
+ padding_mask=padding_mask,
818
+ return_dict=False,
819
+ )[0]
820
+ # NOTE: replace velocity (noise_pred_neg) with gt_velocity for conditioning inputs only
821
+ noise_pred_neg = gt_velocity + noise_pred_neg * (1 - cond_mask)
822
+ noise_pred = noise_pred + self.guidance_scale * (noise_pred - noise_pred_neg)
823
+
824
+ latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
825
+
826
+ if callback_on_step_end is not None:
827
+ callback_kwargs = {}
828
+ for k in callback_on_step_end_tensor_inputs:
829
+ callback_kwargs[k] = locals()[k]
830
+ callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
831
+
832
+ latents = callback_outputs.pop("latents", latents)
833
+ prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
834
+ negative_prompt_embeds = callback_outputs.pop("negative_prompt_embeds", negative_prompt_embeds)
835
+
836
+ # call the callback, if provided
837
+ if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
838
+ progress_bar.update()
839
+
840
+ if XLA_AVAILABLE:
841
+ xm.mark_step()
842
+
843
+ self._current_timestep = None
844
+
845
+ if not output_type == "latent":
846
+ latents_mean = self.latents_mean.to(latents.device, latents.dtype)
847
+ latents_std = self.latents_std.to(latents.device, latents.dtype)
848
+ latents = latents * latents_std + latents_mean
849
+ video = self.vae.decode(latents.to(self.vae.dtype), return_dict=False)[0]
850
+ video = self._match_num_frames(video, num_frames)
851
+
852
+ assert self.safety_checker is not None
853
+ self.safety_checker.to(device)
854
+ video = self.video_processor.postprocess_video(video, output_type="np")
855
+ video = (video * 255).astype(np.uint8)
856
+ video_batch = []
857
+ for vid in video:
858
+ vid = self.safety_checker.check_video_safety(vid)
859
+ video_batch.append(vid)
860
+ video = np.stack(video_batch).astype(np.float32) / 255.0 * 2 - 1
861
+ video = torch.from_numpy(video).permute(0, 4, 1, 2, 3)
862
+ video = self.video_processor.postprocess_video(video, output_type=output_type)
863
+ else:
864
+ video = latents
865
+
866
+ # Offload all models
867
+ self.maybe_free_model_hooks()
868
+
869
+ if not return_dict:
870
+ return (video,)
871
+
872
+ return CosmosPipelineOutput(frames=video)
873
+
874
+ def _match_num_frames(self, video: torch.Tensor, target_num_frames: int) -> torch.Tensor:
875
+ if target_num_frames <= 0 or video.shape[2] == target_num_frames:
876
+ return video
877
+
878
+ frames_per_latent = max(self.vae_scale_factor_temporal, 1)
879
+ video = torch.repeat_interleave(video, repeats=frames_per_latent, dim=2)
880
+
881
+ current_frames = video.shape[2]
882
+ if current_frames < target_num_frames:
883
+ pad = video[:, :, -1:, :, :].repeat(1, 1, target_num_frames - current_frames, 1, 1)
884
+ video = torch.cat([video, pad], dim=2)
885
+ elif current_frames > target_num_frames:
886
+ video = video[:, :, :target_num_frames]
887
+
888
+ return video
third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_cosmos2_text2image.py ADDED
@@ -0,0 +1,679 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The NVIDIA Team and The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ from typing import Callable
17
+
18
+ import numpy as np
19
+ import torch
20
+ from transformers import T5EncoderModel, T5TokenizerFast
21
+
22
+ from ...callbacks import MultiPipelineCallbacks, PipelineCallback
23
+ from ...models import AutoencoderKLWan, CosmosTransformer3DModel
24
+ from ...schedulers import FlowMatchEulerDiscreteScheduler
25
+ from ...utils import is_cosmos_guardrail_available, is_torch_xla_available, logging, replace_example_docstring
26
+ from ...utils.torch_utils import randn_tensor
27
+ from ...video_processor import VideoProcessor
28
+ from ..pipeline_utils import DiffusionPipeline
29
+ from .pipeline_output import CosmosImagePipelineOutput
30
+
31
+
32
+ if is_cosmos_guardrail_available():
33
+ from cosmos_guardrail import CosmosSafetyChecker
34
+ else:
35
+
36
+ class CosmosSafetyChecker:
37
+ def __init__(self, *args, **kwargs):
38
+ raise ImportError(
39
+ "`cosmos_guardrail` is not installed. Please install it to use the safety checker for Cosmos: `pip install cosmos_guardrail`."
40
+ )
41
+
42
+
43
+ if is_torch_xla_available():
44
+ import torch_xla.core.xla_model as xm
45
+
46
+ XLA_AVAILABLE = True
47
+ else:
48
+ XLA_AVAILABLE = False
49
+
50
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
51
+
52
+ DEFAULT_NEGATIVE_PROMPT = (
53
+ "The video captures a series of frames showing ugly scenes, static with no motion, motion blur, "
54
+ "over-saturation, shaky footage, low resolution, grainy texture, pixelated images, poorly lit areas, "
55
+ "underexposed and overexposed scenes, poor color balance, washed out colors, choppy sequences, "
56
+ "jerky movements, low frame rate, artifacting, color banding, unnatural transitions, outdated special effects, "
57
+ "fake elements, unconvincing visuals, poorly edited content, jump cuts, visual noise, and flickering. "
58
+ "Overall, the video is of poor quality."
59
+ )
60
+
61
+ EXAMPLE_DOC_STRING = """
62
+ Examples:
63
+ ```python
64
+ >>> import torch
65
+ >>> from diffusers import Cosmos2TextToImagePipeline
66
+
67
+ >>> # Available checkpoints: nvidia/Cosmos-Predict2-2B-Text2Image, nvidia/Cosmos-Predict2-14B-Text2Image
68
+ >>> model_id = "nvidia/Cosmos-Predict2-2B-Text2Image"
69
+ >>> pipe = Cosmos2TextToImagePipeline.from_pretrained(model_id, torch_dtype=torch.bfloat16)
70
+ >>> pipe.to("cuda")
71
+
72
+ >>> prompt = "A close-up shot captures a vibrant yellow scrubber vigorously working on a grimy plate, its bristles moving in circular motions to lift stubborn grease and food residue. The dish, once covered in remnants of a hearty meal, gradually reveals its original glossy surface. Suds form and bubble around the scrubber, creating a satisfying visual of cleanliness in progress. The sound of scrubbing fills the air, accompanied by the gentle clinking of the dish against the sink. As the scrubber continues its task, the dish transforms, gleaming under the bright kitchen lights, symbolizing the triumph of cleanliness over mess."
73
+ >>> negative_prompt = "The video captures a series of frames showing ugly scenes, static with no motion, motion blur, over-saturation, shaky footage, low resolution, grainy texture, pixelated images, poorly lit areas, underexposed and overexposed scenes, poor color balance, washed out colors, choppy sequences, jerky movements, low frame rate, artifacting, color banding, unnatural transitions, outdated special effects, fake elements, unconvincing visuals, poorly edited content, jump cuts, visual noise, and flickering. Overall, the video is of poor quality."
74
+
75
+ >>> output = pipe(
76
+ ... prompt=prompt, negative_prompt=negative_prompt, generator=torch.Generator().manual_seed(1)
77
+ ... ).images[0]
78
+ >>> output.save("output.png")
79
+ ```
80
+ """
81
+
82
+
83
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
84
+ def retrieve_timesteps(
85
+ scheduler,
86
+ num_inference_steps: int | None = None,
87
+ device: str | torch.device | None = None,
88
+ timesteps: list[int] | None = None,
89
+ sigmas: list[float] | None = None,
90
+ **kwargs,
91
+ ):
92
+ r"""
93
+ Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
94
+ custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
95
+
96
+ Args:
97
+ scheduler (`SchedulerMixin`):
98
+ The scheduler to get timesteps from.
99
+ num_inference_steps (`int`):
100
+ The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
101
+ must be `None`.
102
+ device (`str` or `torch.device`, *optional*):
103
+ The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
104
+ timesteps (`list[int]`, *optional*):
105
+ Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
106
+ `num_inference_steps` and `sigmas` must be `None`.
107
+ sigmas (`list[float]`, *optional*):
108
+ Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
109
+ `num_inference_steps` and `timesteps` must be `None`.
110
+
111
+ Returns:
112
+ `tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
113
+ second element is the number of inference steps.
114
+ """
115
+ if timesteps is not None and sigmas is not None:
116
+ raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
117
+ if timesteps is not None:
118
+ accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
119
+ if not accepts_timesteps:
120
+ raise ValueError(
121
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
122
+ f" timestep schedules. Please check whether you are using the correct scheduler."
123
+ )
124
+ scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
125
+ timesteps = scheduler.timesteps
126
+ num_inference_steps = len(timesteps)
127
+ elif sigmas is not None:
128
+ accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
129
+ if not accept_sigmas:
130
+ raise ValueError(
131
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
132
+ f" sigmas schedules. Please check whether you are using the correct scheduler."
133
+ )
134
+ scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
135
+ timesteps = scheduler.timesteps
136
+ num_inference_steps = len(timesteps)
137
+ else:
138
+ scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
139
+ timesteps = scheduler.timesteps
140
+ return timesteps, num_inference_steps
141
+
142
+
143
+ class Cosmos2TextToImagePipeline(DiffusionPipeline):
144
+ r"""
145
+ Pipeline for text-to-image generation using [Cosmos Predict2](https://github.com/nvidia-cosmos/cosmos-predict2).
146
+
147
+ This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
148
+ implemented for all pipelines (downloading, saving, running on a particular device, etc.).
149
+
150
+ Args:
151
+ text_encoder ([`T5EncoderModel`]):
152
+ Frozen text-encoder. Cosmos uses
153
+ [T5](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5EncoderModel); specifically the
154
+ [t5-11b](https://huggingface.co/google-t5/t5-11b) variant.
155
+ tokenizer (`T5TokenizerFast`):
156
+ Tokenizer of class
157
+ [T5Tokenizer](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5Tokenizer).
158
+ transformer ([`CosmosTransformer3DModel`]):
159
+ Conditional Transformer to denoise the encoded image latents.
160
+ scheduler ([`FlowMatchEulerDiscreteScheduler`]):
161
+ A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
162
+ vae ([`AutoencoderKLWan`]):
163
+ Variational Auto-Encoder (VAE) Model to encode and decode videos to and from latent representations.
164
+ """
165
+
166
+ model_cpu_offload_seq = "text_encoder->transformer->vae"
167
+ _callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds"]
168
+ # We mark safety_checker as optional here to get around some test failures, but it is not really optional
169
+ _optional_components = ["safety_checker"]
170
+
171
+ def __init__(
172
+ self,
173
+ text_encoder: T5EncoderModel,
174
+ tokenizer: T5TokenizerFast,
175
+ transformer: CosmosTransformer3DModel,
176
+ vae: AutoencoderKLWan,
177
+ scheduler: FlowMatchEulerDiscreteScheduler,
178
+ safety_checker: CosmosSafetyChecker = None,
179
+ ):
180
+ super().__init__()
181
+
182
+ if safety_checker is None:
183
+ safety_checker = CosmosSafetyChecker()
184
+
185
+ self.register_modules(
186
+ vae=vae,
187
+ text_encoder=text_encoder,
188
+ tokenizer=tokenizer,
189
+ transformer=transformer,
190
+ scheduler=scheduler,
191
+ safety_checker=safety_checker,
192
+ )
193
+
194
+ self.vae_scale_factor_temporal = 2 ** sum(self.vae.temperal_downsample) if getattr(self, "vae", None) else 4
195
+ self.vae_scale_factor_spatial = 2 ** len(self.vae.temperal_downsample) if getattr(self, "vae", None) else 8
196
+ self.video_processor = VideoProcessor(vae_scale_factor=self.vae_scale_factor_spatial)
197
+
198
+ self.sigma_max = 80.0
199
+ self.sigma_min = 0.002
200
+ self.sigma_data = 1.0
201
+ self.final_sigmas_type = "sigma_min"
202
+ if self.scheduler is not None:
203
+ self.scheduler.register_to_config(
204
+ sigma_max=self.sigma_max,
205
+ sigma_min=self.sigma_min,
206
+ sigma_data=self.sigma_data,
207
+ final_sigmas_type=self.final_sigmas_type,
208
+ )
209
+
210
+ # Copied from diffusers.pipelines.cosmos.pipeline_cosmos_text2world.CosmosTextToWorldPipeline._get_t5_prompt_embeds
211
+ def _get_t5_prompt_embeds(
212
+ self,
213
+ prompt: str | list[str] = None,
214
+ max_sequence_length: int = 512,
215
+ device: torch.device | None = None,
216
+ dtype: torch.dtype | None = None,
217
+ ):
218
+ device = device or self._execution_device
219
+ dtype = dtype or self.text_encoder.dtype
220
+ prompt = [prompt] if isinstance(prompt, str) else prompt
221
+
222
+ text_inputs = self.tokenizer(
223
+ prompt,
224
+ padding="max_length",
225
+ max_length=max_sequence_length,
226
+ truncation=True,
227
+ return_tensors="pt",
228
+ return_length=True,
229
+ return_offsets_mapping=False,
230
+ )
231
+ text_input_ids = text_inputs.input_ids
232
+ prompt_attention_mask = text_inputs.attention_mask.bool().to(device)
233
+
234
+ untruncated_ids = self.tokenizer(prompt, padding="longest", return_tensors="pt").input_ids
235
+ if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
236
+ removed_text = self.tokenizer.batch_decode(untruncated_ids[:, max_sequence_length - 1 : -1])
237
+ logger.warning(
238
+ "The following part of your input was truncated because `max_sequence_length` is set to "
239
+ f" {max_sequence_length} tokens: {removed_text}"
240
+ )
241
+
242
+ prompt_embeds = self.text_encoder(
243
+ text_input_ids.to(device), attention_mask=prompt_attention_mask
244
+ ).last_hidden_state
245
+ prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
246
+
247
+ lengths = prompt_attention_mask.sum(dim=1).cpu()
248
+ for i, length in enumerate(lengths):
249
+ prompt_embeds[i, length:] = 0
250
+
251
+ return prompt_embeds
252
+
253
+ # Copied from diffusers.pipelines.cosmos.pipeline_cosmos_text2world.CosmosTextToWorldPipeline.encode_prompt with num_videos_per_prompt->num_images_per_prompt
254
+ def encode_prompt(
255
+ self,
256
+ prompt: str | list[str],
257
+ negative_prompt: str | list[str] | None = None,
258
+ do_classifier_free_guidance: bool = True,
259
+ num_images_per_prompt: int = 1,
260
+ prompt_embeds: torch.Tensor | None = None,
261
+ negative_prompt_embeds: torch.Tensor | None = None,
262
+ max_sequence_length: int = 512,
263
+ device: torch.device | None = None,
264
+ dtype: torch.dtype | None = None,
265
+ ):
266
+ r"""
267
+ Encodes the prompt into text encoder hidden states.
268
+
269
+ Args:
270
+ prompt (`str` or `list[str]`, *optional*):
271
+ prompt to be encoded
272
+ negative_prompt (`str` or `list[str]`, *optional*):
273
+ The prompt or prompts not to guide the image generation. If not defined, one has to pass
274
+ `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
275
+ less than `1`).
276
+ do_classifier_free_guidance (`bool`, *optional*, defaults to `True`):
277
+ Whether to use classifier free guidance or not.
278
+ num_images_per_prompt (`int`, *optional*, defaults to 1):
279
+ Number of videos that should be generated per prompt. torch device to place the resulting embeddings on
280
+ prompt_embeds (`torch.Tensor`, *optional*):
281
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
282
+ provided, text embeddings will be generated from `prompt` input argument.
283
+ negative_prompt_embeds (`torch.Tensor`, *optional*):
284
+ Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
285
+ weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
286
+ argument.
287
+ device: (`torch.device`, *optional*):
288
+ torch device
289
+ dtype: (`torch.dtype`, *optional*):
290
+ torch dtype
291
+ """
292
+ device = device or self._execution_device
293
+
294
+ prompt = [prompt] if isinstance(prompt, str) else prompt
295
+ if prompt is not None:
296
+ batch_size = len(prompt)
297
+ else:
298
+ batch_size = prompt_embeds.shape[0]
299
+
300
+ if prompt_embeds is None:
301
+ prompt_embeds = self._get_t5_prompt_embeds(
302
+ prompt=prompt, max_sequence_length=max_sequence_length, device=device, dtype=dtype
303
+ )
304
+
305
+ # duplicate text embeddings for each generation per prompt, using mps friendly method
306
+ _, seq_len, _ = prompt_embeds.shape
307
+ prompt_embeds = prompt_embeds.repeat(1, num_images_per_prompt, 1)
308
+ prompt_embeds = prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
309
+
310
+ if do_classifier_free_guidance and negative_prompt_embeds is None:
311
+ negative_prompt = negative_prompt if negative_prompt is not None else DEFAULT_NEGATIVE_PROMPT
312
+ negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
313
+
314
+ if prompt is not None and type(prompt) is not type(negative_prompt):
315
+ raise TypeError(
316
+ f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
317
+ f" {type(prompt)}."
318
+ )
319
+ elif batch_size != len(negative_prompt):
320
+ raise ValueError(
321
+ f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
322
+ f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
323
+ " the batch size of `prompt`."
324
+ )
325
+
326
+ negative_prompt_embeds = self._get_t5_prompt_embeds(
327
+ prompt=negative_prompt, max_sequence_length=max_sequence_length, device=device, dtype=dtype
328
+ )
329
+
330
+ # duplicate text embeddings for each generation per prompt, using mps friendly method
331
+ _, seq_len, _ = negative_prompt_embeds.shape
332
+ negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_images_per_prompt, 1)
333
+ negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_images_per_prompt, seq_len, -1)
334
+
335
+ return prompt_embeds, negative_prompt_embeds
336
+
337
+ def prepare_latents(
338
+ self,
339
+ batch_size: int,
340
+ num_channels_latents: 16,
341
+ height: int = 768,
342
+ width: int = 1360,
343
+ num_frames: int = 1,
344
+ dtype: torch.dtype | None = None,
345
+ device: torch.device | None = None,
346
+ generator: torch.Generator | list[torch.Generator] | None = None,
347
+ latents: torch.Tensor | None = None,
348
+ ) -> torch.Tensor:
349
+ if latents is not None:
350
+ return latents.to(device=device, dtype=dtype) * self.scheduler.config.sigma_max
351
+
352
+ num_latent_frames = (num_frames - 1) // self.vae_scale_factor_temporal + 1
353
+ latent_height = height // self.vae_scale_factor_spatial
354
+ latent_width = width // self.vae_scale_factor_spatial
355
+ shape = (batch_size, num_channels_latents, num_latent_frames, latent_height, latent_width)
356
+
357
+ if isinstance(generator, list) and len(generator) != batch_size:
358
+ raise ValueError(
359
+ f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
360
+ f" size of {batch_size}. Make sure the batch size matches the length of the generators."
361
+ )
362
+
363
+ latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
364
+ return latents * self.scheduler.config.sigma_max
365
+
366
+ # Copied from diffusers.pipelines.cosmos.pipeline_cosmos_text2world.CosmosTextToWorldPipeline.check_inputs
367
+ def check_inputs(
368
+ self,
369
+ prompt,
370
+ height,
371
+ width,
372
+ prompt_embeds=None,
373
+ callback_on_step_end_tensor_inputs=None,
374
+ ):
375
+ if height % 16 != 0 or width % 16 != 0:
376
+ raise ValueError(f"`height` and `width` have to be divisible by 16 but are {height} and {width}.")
377
+
378
+ if callback_on_step_end_tensor_inputs is not None and not all(
379
+ k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
380
+ ):
381
+ raise ValueError(
382
+ f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
383
+ )
384
+
385
+ if prompt is not None and prompt_embeds is not None:
386
+ raise ValueError(
387
+ f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
388
+ " only forward one of the two."
389
+ )
390
+ elif prompt is None and prompt_embeds is None:
391
+ raise ValueError(
392
+ "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
393
+ )
394
+ elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
395
+ raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
396
+
397
+ @property
398
+ def guidance_scale(self):
399
+ return self._guidance_scale
400
+
401
+ @property
402
+ def do_classifier_free_guidance(self):
403
+ return self._guidance_scale > 1.0
404
+
405
+ @property
406
+ def num_timesteps(self):
407
+ return self._num_timesteps
408
+
409
+ @property
410
+ def current_timestep(self):
411
+ return self._current_timestep
412
+
413
+ @property
414
+ def interrupt(self):
415
+ return self._interrupt
416
+
417
+ @torch.no_grad()
418
+ @replace_example_docstring(EXAMPLE_DOC_STRING)
419
+ def __call__(
420
+ self,
421
+ prompt: str | list[str] = None,
422
+ negative_prompt: str | list[str] | None = None,
423
+ height: int = 768,
424
+ width: int = 1360,
425
+ num_inference_steps: int = 35,
426
+ guidance_scale: float = 7.0,
427
+ num_images_per_prompt: int | None = 1,
428
+ generator: torch.Generator | list[torch.Generator] | None = None,
429
+ latents: torch.Tensor | None = None,
430
+ prompt_embeds: torch.Tensor | None = None,
431
+ negative_prompt_embeds: torch.Tensor | None = None,
432
+ output_type: str | None = "pil",
433
+ return_dict: bool = True,
434
+ callback_on_step_end: Callable[[int, int], None] | PipelineCallback | MultiPipelineCallbacks | None = None,
435
+ callback_on_step_end_tensor_inputs: list[str] = ["latents"],
436
+ max_sequence_length: int = 512,
437
+ ):
438
+ r"""
439
+ The call function to the pipeline for generation.
440
+
441
+ Args:
442
+ prompt (`str` or `list[str]`, *optional*):
443
+ The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
444
+ instead.
445
+ height (`int`, defaults to `768`):
446
+ The height in pixels of the generated image.
447
+ width (`int`, defaults to `1360`):
448
+ The width in pixels of the generated image.
449
+ num_inference_steps (`int`, defaults to `35`):
450
+ The number of denoising steps. More denoising steps usually lead to a higher quality image at the
451
+ expense of slower inference.
452
+ guidance_scale (`float`, defaults to `7.0`):
453
+ Guidance scale as defined in [Classifier-Free Diffusion
454
+ Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
455
+ of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
456
+ `guidance_scale > 1`.
457
+ num_images_per_prompt (`int`, *optional*, defaults to 1):
458
+ The number of images to generate per prompt.
459
+ generator (`torch.Generator` or `list[torch.Generator]`, *optional*):
460
+ A [`torch.Generator`](https://pytorch.org/docs/stable/generated/torch.Generator.html) to make
461
+ generation deterministic.
462
+ latents (`torch.Tensor`, *optional*):
463
+ Pre-generated noisy latents sampled from a Gaussian distribution, to be used as inputs for image
464
+ generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
465
+ tensor is generated by sampling using the supplied random `generator`.
466
+ prompt_embeds (`torch.Tensor`, *optional*):
467
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
468
+ provided, text embeddings will be generated from `prompt` input argument.
469
+ negative_prompt_embeds (`torch.FloatTensor`, *optional*):
470
+ Pre-generated negative text embeddings. For PixArt-Sigma this negative prompt should be "". If not
471
+ provided, negative_prompt_embeds will be generated from `negative_prompt` input argument.
472
+ output_type (`str`, *optional*, defaults to `"pil"`):
473
+ The output format of the generated image. Choose between `PIL.Image` or `np.array`.
474
+ return_dict (`bool`, *optional*, defaults to `True`):
475
+ Whether or not to return a [`CosmosImagePipelineOutput`] instead of a plain tuple.
476
+ callback_on_step_end (`Callable`, `PipelineCallback`, `MultiPipelineCallbacks`, *optional*):
477
+ A function or a subclass of `PipelineCallback` or `MultiPipelineCallbacks` that is called at the end of
478
+ each denoising step during the inference. with the following arguments: `callback_on_step_end(self:
479
+ DiffusionPipeline, step: int, timestep: int, callback_kwargs: Dict)`. `callback_kwargs` will include a
480
+ list of all tensors as specified by `callback_on_step_end_tensor_inputs`.
481
+ callback_on_step_end_tensor_inputs (`list`, *optional*):
482
+ The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
483
+ will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
484
+ `._callback_tensor_inputs` attribute of your pipeline class.
485
+
486
+ Examples:
487
+
488
+ Returns:
489
+ [`~CosmosImagePipelineOutput`] or `tuple`:
490
+ If `return_dict` is `True`, [`CosmosImagePipelineOutput`] is returned, otherwise a `tuple` is returned
491
+ where the first element is a list with the generated images and the second element is a list of `bool`s
492
+ indicating whether the corresponding generated image contains "not-safe-for-work" (nsfw) content.
493
+ """
494
+
495
+ if self.safety_checker is None:
496
+ raise ValueError(
497
+ f"You have disabled the safety checker for {self.__class__}. This is in violation of the "
498
+ "[NVIDIA Open Model License Agreement](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license). "
499
+ f"Please ensure that you are compliant with the license agreement."
500
+ )
501
+
502
+ if isinstance(callback_on_step_end, (PipelineCallback, MultiPipelineCallbacks)):
503
+ callback_on_step_end_tensor_inputs = callback_on_step_end.tensor_inputs
504
+
505
+ num_frames = 1
506
+
507
+ # 1. Check inputs. Raise error if not correct
508
+ self.check_inputs(prompt, height, width, prompt_embeds, callback_on_step_end_tensor_inputs)
509
+
510
+ self._guidance_scale = guidance_scale
511
+ self._current_timestep = None
512
+ self._interrupt = False
513
+
514
+ device = self._execution_device
515
+
516
+ if self.safety_checker is not None:
517
+ self.safety_checker.to(device)
518
+ if prompt is not None:
519
+ prompt_list = [prompt] if isinstance(prompt, str) else prompt
520
+ for p in prompt_list:
521
+ if not self.safety_checker.check_text_safety(p):
522
+ raise ValueError(
523
+ f"Cosmos Guardrail detected unsafe text in the prompt: {p}. Please ensure that the "
524
+ f"prompt abides by the NVIDIA Open Model License Agreement."
525
+ )
526
+ self.safety_checker.to("cpu")
527
+
528
+ # 2. Define call parameters
529
+ if prompt is not None and isinstance(prompt, str):
530
+ batch_size = 1
531
+ elif prompt is not None and isinstance(prompt, list):
532
+ batch_size = len(prompt)
533
+ else:
534
+ batch_size = prompt_embeds.shape[0]
535
+
536
+ # 3. Encode input prompt
537
+ (
538
+ prompt_embeds,
539
+ negative_prompt_embeds,
540
+ ) = self.encode_prompt(
541
+ prompt=prompt,
542
+ negative_prompt=negative_prompt,
543
+ do_classifier_free_guidance=self.do_classifier_free_guidance,
544
+ num_images_per_prompt=num_images_per_prompt,
545
+ prompt_embeds=prompt_embeds,
546
+ negative_prompt_embeds=negative_prompt_embeds,
547
+ device=device,
548
+ max_sequence_length=max_sequence_length,
549
+ )
550
+
551
+ # 4. Prepare timesteps
552
+ sigmas_dtype = torch.float32 if torch.backends.mps.is_available() else torch.float64
553
+ sigmas = torch.linspace(0, 1, num_inference_steps, dtype=sigmas_dtype)
554
+ timesteps, num_inference_steps = retrieve_timesteps(self.scheduler, device=device, sigmas=sigmas)
555
+ if self.scheduler.config.get("final_sigmas_type", "zero") == "sigma_min":
556
+ # Replace the last sigma (which is zero) with the minimum sigma value
557
+ self.scheduler.sigmas[-1] = self.scheduler.sigmas[-2]
558
+
559
+ # 5. Prepare latent variables
560
+ transformer_dtype = self.transformer.dtype
561
+ num_channels_latents = self.transformer.config.in_channels
562
+ latents = self.prepare_latents(
563
+ batch_size * num_images_per_prompt,
564
+ num_channels_latents,
565
+ height,
566
+ width,
567
+ num_frames,
568
+ torch.float32,
569
+ device,
570
+ generator,
571
+ latents,
572
+ )
573
+
574
+ padding_mask = latents.new_zeros(1, 1, height, width, dtype=transformer_dtype)
575
+
576
+ # 6. Denoising loop
577
+ num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
578
+ self._num_timesteps = len(timesteps)
579
+
580
+ with self.progress_bar(total=num_inference_steps) as progress_bar:
581
+ for i, t in enumerate(timesteps):
582
+ if self.interrupt:
583
+ continue
584
+
585
+ self._current_timestep = t
586
+ current_sigma = self.scheduler.sigmas[i]
587
+
588
+ current_t = current_sigma / (current_sigma + 1)
589
+ c_in = 1 - current_t
590
+ c_skip = 1 - current_t
591
+ c_out = -current_t
592
+ timestep = current_t.expand(latents.shape[0]).to(transformer_dtype) # [B, 1, T, 1, 1]
593
+
594
+ latent_model_input = latents * c_in
595
+ latent_model_input = latent_model_input.to(transformer_dtype)
596
+
597
+ noise_pred = self.transformer(
598
+ hidden_states=latent_model_input,
599
+ timestep=timestep,
600
+ encoder_hidden_states=prompt_embeds,
601
+ padding_mask=padding_mask,
602
+ return_dict=False,
603
+ )[0]
604
+ noise_pred = (c_skip * latents + c_out * noise_pred.float()).to(transformer_dtype)
605
+
606
+ if self.do_classifier_free_guidance:
607
+ noise_pred_uncond = self.transformer(
608
+ hidden_states=latent_model_input,
609
+ timestep=timestep,
610
+ encoder_hidden_states=negative_prompt_embeds,
611
+ padding_mask=padding_mask,
612
+ return_dict=False,
613
+ )[0]
614
+ noise_pred_uncond = (c_skip * latents + c_out * noise_pred_uncond.float()).to(transformer_dtype)
615
+ noise_pred = noise_pred + self.guidance_scale * (noise_pred - noise_pred_uncond)
616
+
617
+ noise_pred = (latents - noise_pred) / current_sigma
618
+ latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
619
+
620
+ if callback_on_step_end is not None:
621
+ callback_kwargs = {}
622
+ for k in callback_on_step_end_tensor_inputs:
623
+ callback_kwargs[k] = locals()[k]
624
+ callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
625
+
626
+ latents = callback_outputs.pop("latents", latents)
627
+ prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
628
+ negative_prompt_embeds = callback_outputs.pop("negative_prompt_embeds", negative_prompt_embeds)
629
+
630
+ # call the callback, if provided
631
+ if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
632
+ progress_bar.update()
633
+
634
+ if XLA_AVAILABLE:
635
+ xm.mark_step()
636
+
637
+ self._current_timestep = None
638
+
639
+ if not output_type == "latent":
640
+ latents_mean = (
641
+ torch.tensor(self.vae.config.latents_mean)
642
+ .view(1, self.vae.config.z_dim, 1, 1, 1)
643
+ .to(latents.device, latents.dtype)
644
+ )
645
+ latents_std = 1.0 / torch.tensor(self.vae.config.latents_std).view(1, self.vae.config.z_dim, 1, 1, 1).to(
646
+ latents.device, latents.dtype
647
+ )
648
+ latents = latents / latents_std / self.scheduler.config.sigma_data + latents_mean
649
+ video = self.vae.decode(latents.to(self.vae.dtype), return_dict=False)[0]
650
+
651
+ if self.safety_checker is not None:
652
+ self.safety_checker.to(device)
653
+ video = self.video_processor.postprocess_video(video, output_type="np")
654
+ video = (video * 255).astype(np.uint8)
655
+ video_batch = []
656
+ for vid in video:
657
+ vid = self.safety_checker.check_video_safety(vid)
658
+ video_batch.append(vid)
659
+ video = np.stack(video_batch).astype(np.float32) / 255.0 * 2 - 1
660
+ video = torch.from_numpy(video).permute(0, 4, 1, 2, 3)
661
+ video = self.video_processor.postprocess_video(video, output_type=output_type)
662
+ self.safety_checker.to("cpu")
663
+ else:
664
+ video = self.video_processor.postprocess_video(video, output_type=output_type)
665
+ image = [batch[0] for batch in video]
666
+ if isinstance(video, torch.Tensor):
667
+ image = torch.stack(image)
668
+ elif isinstance(video, np.ndarray):
669
+ image = np.stack(image)
670
+ else:
671
+ image = latents[:, :, 0]
672
+
673
+ # Offload all models
674
+ self.maybe_free_model_hooks()
675
+
676
+ if not return_dict:
677
+ return (image,)
678
+
679
+ return CosmosImagePipelineOutput(images=image)
third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_cosmos_text2world.py ADDED
@@ -0,0 +1,670 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The NVIDIA Team and The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ from typing import Callable
17
+
18
+ import numpy as np
19
+ import torch
20
+ from transformers import T5EncoderModel, T5TokenizerFast
21
+
22
+ from ...callbacks import MultiPipelineCallbacks, PipelineCallback
23
+ from ...models import AutoencoderKLCosmos, CosmosTransformer3DModel
24
+ from ...schedulers import EDMEulerScheduler
25
+ from ...utils import is_cosmos_guardrail_available, is_torch_xla_available, logging, replace_example_docstring
26
+ from ...utils.torch_utils import randn_tensor
27
+ from ...video_processor import VideoProcessor
28
+ from ..pipeline_utils import DiffusionPipeline
29
+ from .pipeline_output import CosmosPipelineOutput
30
+
31
+
32
+ if is_cosmos_guardrail_available():
33
+ from cosmos_guardrail import CosmosSafetyChecker
34
+ else:
35
+
36
+ class CosmosSafetyChecker:
37
+ def __init__(self, *args, **kwargs):
38
+ raise ImportError(
39
+ "`cosmos_guardrail` is not installed. Please install it to use the safety checker for Cosmos: `pip install cosmos_guardrail`."
40
+ )
41
+
42
+
43
+ if is_torch_xla_available():
44
+ import torch_xla.core.xla_model as xm
45
+
46
+ XLA_AVAILABLE = True
47
+ else:
48
+ XLA_AVAILABLE = False
49
+
50
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
51
+
52
+ DEFAULT_NEGATIVE_PROMPT = (
53
+ "The video captures a series of frames showing ugly scenes, static with no motion, motion blur, "
54
+ "over-saturation, shaky footage, low resolution, grainy texture, pixelated images, poorly lit areas, "
55
+ "underexposed and overexposed scenes, poor color balance, washed out colors, choppy sequences, "
56
+ "jerky movements, low frame rate, artifacting, color banding, unnatural transitions, outdated special effects, "
57
+ "fake elements, unconvincing visuals, poorly edited content, jump cuts, visual noise, and flickering. "
58
+ "Overall, the video is of poor quality."
59
+ )
60
+
61
+ EXAMPLE_DOC_STRING = """
62
+ Examples:
63
+ ```python
64
+ >>> import torch
65
+ >>> from diffusers import CosmosTextToWorldPipeline
66
+ >>> from diffusers.utils import export_to_video
67
+
68
+ >>> model_id = "nvidia/Cosmos-1.0-Diffusion-7B-Text2World"
69
+ >>> pipe = CosmosTextToWorldPipeline.from_pretrained(model_id, torch_dtype=torch.bfloat16)
70
+ >>> pipe.to("cuda")
71
+
72
+ >>> prompt = "A sleek, humanoid robot stands in a vast warehouse filled with neatly stacked cardboard boxes on industrial shelves. The robot's metallic body gleams under the bright, even lighting, highlighting its futuristic design and intricate joints. A glowing blue light emanates from its chest, adding a touch of advanced technology. The background is dominated by rows of boxes, suggesting a highly organized storage system. The floor is lined with wooden pallets, enhancing the industrial setting. The camera remains static, capturing the robot's poised stance amidst the orderly environment, with a shallow depth of field that keeps the focus on the robot while subtly blurring the background for a cinematic effect."
73
+
74
+ >>> output = pipe(prompt=prompt).frames[0]
75
+ >>> export_to_video(output, "output.mp4", fps=30)
76
+ ```
77
+ """
78
+
79
+
80
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
81
+ def retrieve_timesteps(
82
+ scheduler,
83
+ num_inference_steps: int | None = None,
84
+ device: str | torch.device | None = None,
85
+ timesteps: list[int] | None = None,
86
+ sigmas: list[float] | None = None,
87
+ **kwargs,
88
+ ):
89
+ r"""
90
+ Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
91
+ custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
92
+
93
+ Args:
94
+ scheduler (`SchedulerMixin`):
95
+ The scheduler to get timesteps from.
96
+ num_inference_steps (`int`):
97
+ The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
98
+ must be `None`.
99
+ device (`str` or `torch.device`, *optional*):
100
+ The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
101
+ timesteps (`list[int]`, *optional*):
102
+ Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
103
+ `num_inference_steps` and `sigmas` must be `None`.
104
+ sigmas (`list[float]`, *optional*):
105
+ Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
106
+ `num_inference_steps` and `timesteps` must be `None`.
107
+
108
+ Returns:
109
+ `tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
110
+ second element is the number of inference steps.
111
+ """
112
+ if timesteps is not None and sigmas is not None:
113
+ raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
114
+ if timesteps is not None:
115
+ accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
116
+ if not accepts_timesteps:
117
+ raise ValueError(
118
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
119
+ f" timestep schedules. Please check whether you are using the correct scheduler."
120
+ )
121
+ scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
122
+ timesteps = scheduler.timesteps
123
+ num_inference_steps = len(timesteps)
124
+ elif sigmas is not None:
125
+ accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
126
+ if not accept_sigmas:
127
+ raise ValueError(
128
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
129
+ f" sigmas schedules. Please check whether you are using the correct scheduler."
130
+ )
131
+ scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
132
+ timesteps = scheduler.timesteps
133
+ num_inference_steps = len(timesteps)
134
+ else:
135
+ scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
136
+ timesteps = scheduler.timesteps
137
+ return timesteps, num_inference_steps
138
+
139
+
140
+ class CosmosTextToWorldPipeline(DiffusionPipeline):
141
+ r"""
142
+ Pipeline for text-to-world generation using [Cosmos Predict1](https://github.com/nvidia-cosmos/cosmos-predict1).
143
+
144
+ This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
145
+ implemented for all pipelines (downloading, saving, running on a particular device, etc.).
146
+
147
+ Args:
148
+ text_encoder ([`T5EncoderModel`]):
149
+ Frozen text-encoder. Cosmos uses
150
+ [T5](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5EncoderModel); specifically the
151
+ [t5-11b](https://huggingface.co/google-t5/t5-11b) variant.
152
+ tokenizer (`T5TokenizerFast`):
153
+ Tokenizer of class
154
+ [T5Tokenizer](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5Tokenizer).
155
+ transformer ([`CosmosTransformer3DModel`]):
156
+ Conditional Transformer to denoise the encoded image latents.
157
+ scheduler ([`FlowMatchEulerDiscreteScheduler`]):
158
+ A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
159
+ vae ([`AutoencoderKLCosmos`]):
160
+ Variational Auto-Encoder (VAE) Model to encode and decode videos to and from latent representations.
161
+ """
162
+
163
+ model_cpu_offload_seq = "text_encoder->transformer->vae"
164
+ _callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds"]
165
+ # We mark safety_checker as optional here to get around some test failures, but it is not really optional
166
+ _optional_components = ["safety_checker"]
167
+
168
+ def __init__(
169
+ self,
170
+ text_encoder: T5EncoderModel,
171
+ tokenizer: T5TokenizerFast,
172
+ transformer: CosmosTransformer3DModel,
173
+ vae: AutoencoderKLCosmos,
174
+ scheduler: EDMEulerScheduler,
175
+ safety_checker: CosmosSafetyChecker = None,
176
+ ):
177
+ super().__init__()
178
+
179
+ if safety_checker is None:
180
+ safety_checker = CosmosSafetyChecker()
181
+
182
+ self.register_modules(
183
+ vae=vae,
184
+ text_encoder=text_encoder,
185
+ tokenizer=tokenizer,
186
+ transformer=transformer,
187
+ scheduler=scheduler,
188
+ safety_checker=safety_checker,
189
+ )
190
+
191
+ self.vae_scale_factor_temporal = (
192
+ self.vae.config.temporal_compression_ratio if getattr(self, "vae", None) else 8
193
+ )
194
+ self.vae_scale_factor_spatial = self.vae.config.spatial_compression_ratio if getattr(self, "vae", None) else 8
195
+ self.video_processor = VideoProcessor(vae_scale_factor=self.vae_scale_factor_spatial)
196
+
197
+ def _get_t5_prompt_embeds(
198
+ self,
199
+ prompt: str | list[str] = None,
200
+ max_sequence_length: int = 512,
201
+ device: torch.device | None = None,
202
+ dtype: torch.dtype | None = None,
203
+ ):
204
+ device = device or self._execution_device
205
+ dtype = dtype or self.text_encoder.dtype
206
+ prompt = [prompt] if isinstance(prompt, str) else prompt
207
+
208
+ text_inputs = self.tokenizer(
209
+ prompt,
210
+ padding="max_length",
211
+ max_length=max_sequence_length,
212
+ truncation=True,
213
+ return_tensors="pt",
214
+ return_length=True,
215
+ return_offsets_mapping=False,
216
+ )
217
+ text_input_ids = text_inputs.input_ids
218
+ prompt_attention_mask = text_inputs.attention_mask.bool().to(device)
219
+
220
+ untruncated_ids = self.tokenizer(prompt, padding="longest", return_tensors="pt").input_ids
221
+ if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
222
+ removed_text = self.tokenizer.batch_decode(untruncated_ids[:, max_sequence_length - 1 : -1])
223
+ logger.warning(
224
+ "The following part of your input was truncated because `max_sequence_length` is set to "
225
+ f" {max_sequence_length} tokens: {removed_text}"
226
+ )
227
+
228
+ prompt_embeds = self.text_encoder(
229
+ text_input_ids.to(device), attention_mask=prompt_attention_mask
230
+ ).last_hidden_state
231
+ prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
232
+
233
+ lengths = prompt_attention_mask.sum(dim=1).cpu()
234
+ for i, length in enumerate(lengths):
235
+ prompt_embeds[i, length:] = 0
236
+
237
+ return prompt_embeds
238
+
239
+ def encode_prompt(
240
+ self,
241
+ prompt: str | list[str],
242
+ negative_prompt: str | list[str] | None = None,
243
+ do_classifier_free_guidance: bool = True,
244
+ num_videos_per_prompt: int = 1,
245
+ prompt_embeds: torch.Tensor | None = None,
246
+ negative_prompt_embeds: torch.Tensor | None = None,
247
+ max_sequence_length: int = 512,
248
+ device: torch.device | None = None,
249
+ dtype: torch.dtype | None = None,
250
+ ):
251
+ r"""
252
+ Encodes the prompt into text encoder hidden states.
253
+
254
+ Args:
255
+ prompt (`str` or `list[str]`, *optional*):
256
+ prompt to be encoded
257
+ negative_prompt (`str` or `list[str]`, *optional*):
258
+ The prompt or prompts not to guide the image generation. If not defined, one has to pass
259
+ `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
260
+ less than `1`).
261
+ do_classifier_free_guidance (`bool`, *optional*, defaults to `True`):
262
+ Whether to use classifier free guidance or not.
263
+ num_videos_per_prompt (`int`, *optional*, defaults to 1):
264
+ Number of videos that should be generated per prompt. torch device to place the resulting embeddings on
265
+ prompt_embeds (`torch.Tensor`, *optional*):
266
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
267
+ provided, text embeddings will be generated from `prompt` input argument.
268
+ negative_prompt_embeds (`torch.Tensor`, *optional*):
269
+ Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
270
+ weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
271
+ argument.
272
+ device: (`torch.device`, *optional*):
273
+ torch device
274
+ dtype: (`torch.dtype`, *optional*):
275
+ torch dtype
276
+ """
277
+ device = device or self._execution_device
278
+
279
+ prompt = [prompt] if isinstance(prompt, str) else prompt
280
+ if prompt is not None:
281
+ batch_size = len(prompt)
282
+ else:
283
+ batch_size = prompt_embeds.shape[0]
284
+
285
+ if prompt_embeds is None:
286
+ prompt_embeds = self._get_t5_prompt_embeds(
287
+ prompt=prompt, max_sequence_length=max_sequence_length, device=device, dtype=dtype
288
+ )
289
+
290
+ # duplicate text embeddings for each generation per prompt, using mps friendly method
291
+ _, seq_len, _ = prompt_embeds.shape
292
+ prompt_embeds = prompt_embeds.repeat(1, num_videos_per_prompt, 1)
293
+ prompt_embeds = prompt_embeds.view(batch_size * num_videos_per_prompt, seq_len, -1)
294
+
295
+ if do_classifier_free_guidance and negative_prompt_embeds is None:
296
+ negative_prompt = negative_prompt if negative_prompt is not None else DEFAULT_NEGATIVE_PROMPT
297
+ negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
298
+
299
+ if prompt is not None and type(prompt) is not type(negative_prompt):
300
+ raise TypeError(
301
+ f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
302
+ f" {type(prompt)}."
303
+ )
304
+ elif batch_size != len(negative_prompt):
305
+ raise ValueError(
306
+ f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
307
+ f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
308
+ " the batch size of `prompt`."
309
+ )
310
+
311
+ negative_prompt_embeds = self._get_t5_prompt_embeds(
312
+ prompt=negative_prompt, max_sequence_length=max_sequence_length, device=device, dtype=dtype
313
+ )
314
+
315
+ # duplicate text embeddings for each generation per prompt, using mps friendly method
316
+ _, seq_len, _ = negative_prompt_embeds.shape
317
+ negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_videos_per_prompt, 1)
318
+ negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_videos_per_prompt, seq_len, -1)
319
+
320
+ return prompt_embeds, negative_prompt_embeds
321
+
322
+ def prepare_latents(
323
+ self,
324
+ batch_size: int,
325
+ num_channels_latents: 16,
326
+ height: int = 704,
327
+ width: int = 1280,
328
+ num_frames: int = 121,
329
+ dtype: torch.dtype | None = None,
330
+ device: torch.device | None = None,
331
+ generator: torch.Generator | list[torch.Generator] | None = None,
332
+ latents: torch.Tensor | None = None,
333
+ ) -> torch.Tensor:
334
+ if latents is not None:
335
+ return latents.to(device=device, dtype=dtype) * self.scheduler.config.sigma_max
336
+
337
+ num_latent_frames = (num_frames - 1) // self.vae_scale_factor_temporal + 1
338
+ latent_height = height // self.vae_scale_factor_spatial
339
+ latent_width = width // self.vae_scale_factor_spatial
340
+ shape = (batch_size, num_channels_latents, num_latent_frames, latent_height, latent_width)
341
+
342
+ if isinstance(generator, list) and len(generator) != batch_size:
343
+ raise ValueError(
344
+ f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
345
+ f" size of {batch_size}. Make sure the batch size matches the length of the generators."
346
+ )
347
+
348
+ latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
349
+ return latents * self.scheduler.config.sigma_max
350
+
351
+ def check_inputs(
352
+ self,
353
+ prompt,
354
+ height,
355
+ width,
356
+ prompt_embeds=None,
357
+ callback_on_step_end_tensor_inputs=None,
358
+ ):
359
+ if height % 16 != 0 or width % 16 != 0:
360
+ raise ValueError(f"`height` and `width` have to be divisible by 16 but are {height} and {width}.")
361
+
362
+ if callback_on_step_end_tensor_inputs is not None and not all(
363
+ k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
364
+ ):
365
+ raise ValueError(
366
+ f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
367
+ )
368
+
369
+ if prompt is not None and prompt_embeds is not None:
370
+ raise ValueError(
371
+ f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
372
+ " only forward one of the two."
373
+ )
374
+ elif prompt is None and prompt_embeds is None:
375
+ raise ValueError(
376
+ "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
377
+ )
378
+ elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
379
+ raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
380
+
381
+ @property
382
+ def guidance_scale(self):
383
+ return self._guidance_scale
384
+
385
+ @property
386
+ def do_classifier_free_guidance(self):
387
+ return self._guidance_scale > 1.0
388
+
389
+ @property
390
+ def num_timesteps(self):
391
+ return self._num_timesteps
392
+
393
+ @property
394
+ def current_timestep(self):
395
+ return self._current_timestep
396
+
397
+ @property
398
+ def interrupt(self):
399
+ return self._interrupt
400
+
401
+ @torch.no_grad()
402
+ @replace_example_docstring(EXAMPLE_DOC_STRING)
403
+ def __call__(
404
+ self,
405
+ prompt: str | list[str] = None,
406
+ negative_prompt: str | list[str] | None = None,
407
+ height: int = 704,
408
+ width: int = 1280,
409
+ num_frames: int = 121,
410
+ num_inference_steps: int = 36,
411
+ guidance_scale: float = 7.0,
412
+ fps: int = 30,
413
+ num_videos_per_prompt: int | None = 1,
414
+ generator: torch.Generator | list[torch.Generator] | None = None,
415
+ latents: torch.Tensor | None = None,
416
+ prompt_embeds: torch.Tensor | None = None,
417
+ negative_prompt_embeds: torch.Tensor | None = None,
418
+ output_type: str | None = "pil",
419
+ return_dict: bool = True,
420
+ callback_on_step_end: Callable[[int, int], None] | PipelineCallback | MultiPipelineCallbacks | None = None,
421
+ callback_on_step_end_tensor_inputs: list[str] = ["latents"],
422
+ max_sequence_length: int = 512,
423
+ ):
424
+ r"""
425
+ The call function to the pipeline for generation.
426
+
427
+ Args:
428
+ prompt (`str` or `list[str]`, *optional*):
429
+ The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
430
+ instead.
431
+ height (`int`, defaults to `720`):
432
+ The height in pixels of the generated image.
433
+ width (`int`, defaults to `1280`):
434
+ The width in pixels of the generated image.
435
+ num_frames (`int`, defaults to `121`):
436
+ The number of frames in the generated video.
437
+ num_inference_steps (`int`, defaults to `36`):
438
+ The number of denoising steps. More denoising steps usually lead to a higher quality image at the
439
+ expense of slower inference.
440
+ guidance_scale (`float`, defaults to `7.0`):
441
+ Guidance scale as defined in [Classifier-Free Diffusion
442
+ Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
443
+ of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
444
+ `guidance_scale > 1`.
445
+ fps (`int`, defaults to `30`):
446
+ The frames per second of the generated video.
447
+ num_videos_per_prompt (`int`, *optional*, defaults to 1):
448
+ The number of images to generate per prompt.
449
+ generator (`torch.Generator` or `list[torch.Generator]`, *optional*):
450
+ A [`torch.Generator`](https://pytorch.org/docs/stable/generated/torch.Generator.html) to make
451
+ generation deterministic.
452
+ latents (`torch.Tensor`, *optional*):
453
+ Pre-generated noisy latents sampled from a Gaussian distribution, to be used as inputs for image
454
+ generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
455
+ tensor is generated by sampling using the supplied random `generator`.
456
+ prompt_embeds (`torch.Tensor`, *optional*):
457
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
458
+ provided, text embeddings will be generated from `prompt` input argument.
459
+ negative_prompt_embeds (`torch.FloatTensor`, *optional*):
460
+ Pre-generated negative text embeddings. For PixArt-Sigma this negative prompt should be "". If not
461
+ provided, negative_prompt_embeds will be generated from `negative_prompt` input argument.
462
+ output_type (`str`, *optional*, defaults to `"pil"`):
463
+ The output format of the generated image. Choose between `PIL.Image` or `np.array`.
464
+ return_dict (`bool`, *optional*, defaults to `True`):
465
+ Whether or not to return a [`CosmosPipelineOutput`] instead of a plain tuple.
466
+ callback_on_step_end (`Callable`, `PipelineCallback`, `MultiPipelineCallbacks`, *optional*):
467
+ A function or a subclass of `PipelineCallback` or `MultiPipelineCallbacks` that is called at the end of
468
+ each denoising step during the inference. with the following arguments: `callback_on_step_end(self:
469
+ DiffusionPipeline, step: int, timestep: int, callback_kwargs: Dict)`. `callback_kwargs` will include a
470
+ list of all tensors as specified by `callback_on_step_end_tensor_inputs`.
471
+ callback_on_step_end_tensor_inputs (`list`, *optional*):
472
+ The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
473
+ will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
474
+ `._callback_tensor_inputs` attribute of your pipeline class.
475
+
476
+ Examples:
477
+
478
+ Returns:
479
+ [`~CosmosPipelineOutput`] or `tuple`:
480
+ If `return_dict` is `True`, [`CosmosPipelineOutput`] is returned, otherwise a `tuple` is returned where
481
+ the first element is a list with the generated images and the second element is a list of `bool`s
482
+ indicating whether the corresponding generated image contains "not-safe-for-work" (nsfw) content.
483
+ """
484
+
485
+ if self.safety_checker is None:
486
+ raise ValueError(
487
+ f"You have disabled the safety checker for {self.__class__}. This is in violation of the "
488
+ "[NVIDIA Open Model License Agreement](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license). "
489
+ f"Please ensure that you are compliant with the license agreement."
490
+ )
491
+
492
+ if isinstance(callback_on_step_end, (PipelineCallback, MultiPipelineCallbacks)):
493
+ callback_on_step_end_tensor_inputs = callback_on_step_end.tensor_inputs
494
+
495
+ # 1. Check inputs. Raise error if not correct
496
+ self.check_inputs(prompt, height, width, prompt_embeds, callback_on_step_end_tensor_inputs)
497
+
498
+ self._guidance_scale = guidance_scale
499
+ self._current_timestep = None
500
+ self._interrupt = False
501
+
502
+ device = self._execution_device
503
+
504
+ if self.safety_checker is not None:
505
+ self.safety_checker.to(device)
506
+ if prompt is not None:
507
+ prompt_list = [prompt] if isinstance(prompt, str) else prompt
508
+ for p in prompt_list:
509
+ if not self.safety_checker.check_text_safety(p):
510
+ raise ValueError(
511
+ f"Cosmos Guardrail detected unsafe text in the prompt: {p}. Please ensure that the "
512
+ f"prompt abides by the NVIDIA Open Model License Agreement."
513
+ )
514
+ self.safety_checker.to("cpu")
515
+
516
+ # 2. Define call parameters
517
+ if prompt is not None and isinstance(prompt, str):
518
+ batch_size = 1
519
+ elif prompt is not None and isinstance(prompt, list):
520
+ batch_size = len(prompt)
521
+ else:
522
+ batch_size = prompt_embeds.shape[0]
523
+
524
+ # 3. Encode input prompt
525
+ (
526
+ prompt_embeds,
527
+ negative_prompt_embeds,
528
+ ) = self.encode_prompt(
529
+ prompt=prompt,
530
+ negative_prompt=negative_prompt,
531
+ do_classifier_free_guidance=self.do_classifier_free_guidance,
532
+ num_videos_per_prompt=num_videos_per_prompt,
533
+ prompt_embeds=prompt_embeds,
534
+ negative_prompt_embeds=negative_prompt_embeds,
535
+ device=device,
536
+ max_sequence_length=max_sequence_length,
537
+ )
538
+
539
+ # 4. Prepare timesteps
540
+ timesteps, num_inference_steps = retrieve_timesteps(self.scheduler, num_inference_steps, device)
541
+
542
+ # 5. Prepare latent variables
543
+ transformer_dtype = self.transformer.dtype
544
+ num_channels_latents = self.transformer.config.in_channels
545
+ latents = self.prepare_latents(
546
+ batch_size * num_videos_per_prompt,
547
+ num_channels_latents,
548
+ height,
549
+ width,
550
+ num_frames,
551
+ torch.float32,
552
+ device,
553
+ generator,
554
+ latents,
555
+ )
556
+
557
+ padding_mask = latents.new_zeros(1, 1, height, width, dtype=transformer_dtype)
558
+
559
+ # 6. Denoising loop
560
+ num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
561
+ self._num_timesteps = len(timesteps)
562
+
563
+ with self.progress_bar(total=num_inference_steps) as progress_bar:
564
+ for i, t in enumerate(timesteps):
565
+ if self.interrupt:
566
+ continue
567
+
568
+ self._current_timestep = t
569
+ timestep = t.expand(latents.shape[0]).to(transformer_dtype)
570
+
571
+ latent_model_input = latents
572
+ latent_model_input = self.scheduler.scale_model_input(latent_model_input, t)
573
+ latent_model_input = latent_model_input.to(transformer_dtype)
574
+
575
+ noise_pred = self.transformer(
576
+ hidden_states=latent_model_input,
577
+ timestep=timestep,
578
+ encoder_hidden_states=prompt_embeds,
579
+ fps=fps,
580
+ padding_mask=padding_mask,
581
+ return_dict=False,
582
+ )[0]
583
+
584
+ sample = latents
585
+ if self.do_classifier_free_guidance:
586
+ noise_pred_uncond = self.transformer(
587
+ hidden_states=latent_model_input,
588
+ timestep=timestep,
589
+ encoder_hidden_states=negative_prompt_embeds,
590
+ fps=fps,
591
+ padding_mask=padding_mask,
592
+ return_dict=False,
593
+ )[0]
594
+ noise_pred = torch.cat([noise_pred_uncond, noise_pred])
595
+ sample = torch.cat([sample, sample])
596
+
597
+ # pred_original_sample (x0)
598
+ noise_pred = self.scheduler.step(noise_pred, t, sample, return_dict=False)[1]
599
+ self.scheduler._step_index -= 1
600
+
601
+ if self.do_classifier_free_guidance:
602
+ noise_pred_uncond, noise_pred_cond = noise_pred.chunk(2)
603
+ noise_pred = noise_pred_cond + self.guidance_scale * (noise_pred_cond - noise_pred_uncond)
604
+
605
+ # pred_sample (eps)
606
+ latents = self.scheduler.step(
607
+ noise_pred, t, latents, return_dict=False, pred_original_sample=noise_pred
608
+ )[0]
609
+
610
+ if callback_on_step_end is not None:
611
+ callback_kwargs = {}
612
+ for k in callback_on_step_end_tensor_inputs:
613
+ callback_kwargs[k] = locals()[k]
614
+ callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
615
+
616
+ latents = callback_outputs.pop("latents", latents)
617
+ prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
618
+ negative_prompt_embeds = callback_outputs.pop("negative_prompt_embeds", negative_prompt_embeds)
619
+
620
+ # call the callback, if provided
621
+ if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
622
+ progress_bar.update()
623
+
624
+ if XLA_AVAILABLE:
625
+ xm.mark_step()
626
+
627
+ self._current_timestep = None
628
+
629
+ if not output_type == "latent":
630
+ if self.vae.config.latents_mean is not None:
631
+ latents_mean, latents_std = self.vae.config.latents_mean, self.vae.config.latents_std
632
+ latents_mean = (
633
+ torch.tensor(latents_mean)
634
+ .view(1, self.vae.config.latent_channels, -1, 1, 1)[:, :, : latents.size(2)]
635
+ .to(latents)
636
+ )
637
+ latents_std = (
638
+ torch.tensor(latents_std)
639
+ .view(1, self.vae.config.latent_channels, -1, 1, 1)[:, :, : latents.size(2)]
640
+ .to(latents)
641
+ )
642
+ latents = latents * latents_std / self.scheduler.config.sigma_data + latents_mean
643
+ else:
644
+ latents = latents / self.scheduler.config.sigma_data
645
+ video = self.vae.decode(latents.to(self.vae.dtype), return_dict=False)[0]
646
+
647
+ if self.safety_checker is not None:
648
+ self.safety_checker.to(device)
649
+ video = self.video_processor.postprocess_video(video, output_type="np")
650
+ video = (video * 255).astype(np.uint8)
651
+ video_batch = []
652
+ for vid in video:
653
+ vid = self.safety_checker.check_video_safety(vid)
654
+ video_batch.append(vid)
655
+ video = np.stack(video_batch).astype(np.float32) / 255.0 * 2 - 1
656
+ video = torch.from_numpy(video).permute(0, 4, 1, 2, 3)
657
+ video = self.video_processor.postprocess_video(video, output_type=output_type)
658
+ self.safety_checker.to("cpu")
659
+ else:
660
+ video = self.video_processor.postprocess_video(video, output_type=output_type)
661
+ else:
662
+ video = latents
663
+
664
+ # Offload all models
665
+ self.maybe_free_model_hooks()
666
+
667
+ if not return_dict:
668
+ return (video,)
669
+
670
+ return CosmosPipelineOutput(frames=video)
third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_cosmos_video2world.py ADDED
@@ -0,0 +1,832 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The NVIDIA Team and The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ from typing import Callable
17
+
18
+ import numpy as np
19
+ import torch
20
+ from transformers import T5EncoderModel, T5TokenizerFast
21
+
22
+ from ...callbacks import MultiPipelineCallbacks, PipelineCallback
23
+ from ...image_processor import PipelineImageInput
24
+ from ...models import AutoencoderKLCosmos, CosmosTransformer3DModel
25
+ from ...schedulers import EDMEulerScheduler
26
+ from ...utils import is_cosmos_guardrail_available, is_torch_xla_available, logging, replace_example_docstring
27
+ from ...utils.torch_utils import randn_tensor
28
+ from ...video_processor import VideoProcessor
29
+ from ..pipeline_utils import DiffusionPipeline
30
+ from .pipeline_output import CosmosPipelineOutput
31
+
32
+
33
+ if is_cosmos_guardrail_available():
34
+ from cosmos_guardrail import CosmosSafetyChecker
35
+ else:
36
+
37
+ class CosmosSafetyChecker:
38
+ def __init__(self, *args, **kwargs):
39
+ raise ImportError(
40
+ "`cosmos_guardrail` is not installed. Please install it to use the safety checker for Cosmos: `pip install cosmos_guardrail`."
41
+ )
42
+
43
+
44
+ if is_torch_xla_available():
45
+ import torch_xla.core.xla_model as xm
46
+
47
+ XLA_AVAILABLE = True
48
+ else:
49
+ XLA_AVAILABLE = False
50
+
51
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
52
+
53
+ DEFAULT_NEGATIVE_PROMPT = (
54
+ "The video captures a series of frames showing ugly scenes, static with no motion, motion blur, "
55
+ "over-saturation, shaky footage, low resolution, grainy texture, pixelated images, poorly lit areas, "
56
+ "underexposed and overexposed scenes, poor color balance, washed out colors, choppy sequences, "
57
+ "jerky movements, low frame rate, artifacting, color banding, unnatural transitions, outdated special effects, "
58
+ "fake elements, unconvincing visuals, poorly edited content, jump cuts, visual noise, and flickering. "
59
+ "Overall, the video is of poor quality."
60
+ )
61
+
62
+ EXAMPLE_DOC_STRING = """
63
+ Examples:
64
+ Image conditioning:
65
+
66
+ ```python
67
+ >>> import torch
68
+ >>> from diffusers import CosmosVideoToWorldPipeline
69
+ >>> from diffusers.utils import export_to_video, load_image
70
+
71
+ >>> model_id = "nvidia/Cosmos-1.0-Diffusion-7B-Video2World"
72
+ >>> pipe = CosmosVideoToWorldPipeline.from_pretrained(model_id, torch_dtype=torch.bfloat16)
73
+ >>> pipe.to("cuda")
74
+
75
+ >>> prompt = "The video depicts a long, straight highway stretching into the distance, flanked by metal guardrails. The road is divided into multiple lanes, with a few vehicles visible in the far distance. The surrounding landscape features dry, grassy fields on one side and rolling hills on the other. The sky is mostly clear with a few scattered clouds, suggesting a bright, sunny day."
76
+ >>> image = load_image(
77
+ ... "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/cosmos/cosmos-video2world-input.jpg"
78
+ ... )
79
+
80
+ >>> video = pipe(image=image, prompt=prompt).frames[0]
81
+ >>> export_to_video(video, "output.mp4", fps=30)
82
+ ```
83
+
84
+ Video conditioning:
85
+
86
+ ```python
87
+ >>> import torch
88
+ >>> from diffusers import CosmosVideoToWorldPipeline
89
+ >>> from diffusers.utils import export_to_video, load_video
90
+
91
+ >>> model_id = "nvidia/Cosmos-1.0-Diffusion-7B-Video2World"
92
+ >>> pipe = CosmosVideoToWorldPipeline.from_pretrained(model_id, torch_dtype=torch.bfloat16)
93
+ >>> pipe.transformer = torch.compile(pipe.transformer)
94
+ >>> pipe.to("cuda")
95
+
96
+ >>> prompt = "The video depicts a winding mountain road covered in snow, with a single vehicle traveling along it. The road is flanked by steep, rocky cliffs and sparse vegetation. The landscape is characterized by rugged terrain and a river visible in the distance. The scene captures the solitude and beauty of a winter drive through a mountainous region."
97
+ >>> video = load_video(
98
+ ... "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/cosmos/cosmos-video2world-input-vid.mp4"
99
+ ... )[
100
+ ... :21
101
+ ... ] # This example uses only the first 21 frames
102
+
103
+ >>> video = pipe(video=video, prompt=prompt).frames[0]
104
+ >>> export_to_video(video, "output.mp4", fps=30)
105
+ ```
106
+ """
107
+
108
+
109
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
110
+ def retrieve_timesteps(
111
+ scheduler,
112
+ num_inference_steps: int | None = None,
113
+ device: str | torch.device | None = None,
114
+ timesteps: list[int] | None = None,
115
+ sigmas: list[float] | None = None,
116
+ **kwargs,
117
+ ):
118
+ r"""
119
+ Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
120
+ custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
121
+
122
+ Args:
123
+ scheduler (`SchedulerMixin`):
124
+ The scheduler to get timesteps from.
125
+ num_inference_steps (`int`):
126
+ The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
127
+ must be `None`.
128
+ device (`str` or `torch.device`, *optional*):
129
+ The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
130
+ timesteps (`list[int]`, *optional*):
131
+ Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
132
+ `num_inference_steps` and `sigmas` must be `None`.
133
+ sigmas (`list[float]`, *optional*):
134
+ Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
135
+ `num_inference_steps` and `timesteps` must be `None`.
136
+
137
+ Returns:
138
+ `tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
139
+ second element is the number of inference steps.
140
+ """
141
+ if timesteps is not None and sigmas is not None:
142
+ raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
143
+ if timesteps is not None:
144
+ accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
145
+ if not accepts_timesteps:
146
+ raise ValueError(
147
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
148
+ f" timestep schedules. Please check whether you are using the correct scheduler."
149
+ )
150
+ scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
151
+ timesteps = scheduler.timesteps
152
+ num_inference_steps = len(timesteps)
153
+ elif sigmas is not None:
154
+ accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
155
+ if not accept_sigmas:
156
+ raise ValueError(
157
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
158
+ f" sigmas schedules. Please check whether you are using the correct scheduler."
159
+ )
160
+ scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
161
+ timesteps = scheduler.timesteps
162
+ num_inference_steps = len(timesteps)
163
+ else:
164
+ scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
165
+ timesteps = scheduler.timesteps
166
+ return timesteps, num_inference_steps
167
+
168
+
169
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.retrieve_latents
170
+ def retrieve_latents(
171
+ encoder_output: torch.Tensor, generator: torch.Generator | None = None, sample_mode: str = "sample"
172
+ ):
173
+ if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
174
+ return encoder_output.latent_dist.sample(generator)
175
+ elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
176
+ return encoder_output.latent_dist.mode()
177
+ elif hasattr(encoder_output, "latents"):
178
+ return encoder_output.latents
179
+ else:
180
+ raise AttributeError("Could not access latents of provided encoder_output")
181
+
182
+
183
+ class CosmosVideoToWorldPipeline(DiffusionPipeline):
184
+ r"""
185
+ Pipeline for image-to-world and video-to-world generation using [Cosmos
186
+ Predict-1](https://github.com/nvidia-cosmos/cosmos-predict1).
187
+
188
+ This model inherits from [`DiffusionPipeline`]. Check the superclass documentation for the generic methods
189
+ implemented for all pipelines (downloading, saving, running on a particular device, etc.).
190
+
191
+ Args:
192
+ text_encoder ([`T5EncoderModel`]):
193
+ Frozen text-encoder. Cosmos uses
194
+ [T5](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5EncoderModel); specifically the
195
+ [t5-11b](https://huggingface.co/google-t5/t5-11b) variant.
196
+ tokenizer (`T5TokenizerFast`):
197
+ Tokenizer of class
198
+ [T5Tokenizer](https://huggingface.co/docs/transformers/model_doc/t5#transformers.T5Tokenizer).
199
+ transformer ([`CosmosTransformer3DModel`]):
200
+ Conditional Transformer to denoise the encoded image latents.
201
+ scheduler ([`FlowMatchEulerDiscreteScheduler`]):
202
+ A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
203
+ vae ([`AutoencoderKLCosmos`]):
204
+ Variational Auto-Encoder (VAE) Model to encode and decode videos to and from latent representations.
205
+ """
206
+
207
+ model_cpu_offload_seq = "text_encoder->transformer->vae"
208
+ _callback_tensor_inputs = ["latents", "prompt_embeds", "negative_prompt_embeds"]
209
+ # We mark safety_checker as optional here to get around some test failures, but it is not really optional
210
+ _optional_components = ["safety_checker"]
211
+
212
+ def __init__(
213
+ self,
214
+ text_encoder: T5EncoderModel,
215
+ tokenizer: T5TokenizerFast,
216
+ transformer: CosmosTransformer3DModel,
217
+ vae: AutoencoderKLCosmos,
218
+ scheduler: EDMEulerScheduler,
219
+ safety_checker: CosmosSafetyChecker = None,
220
+ ):
221
+ super().__init__()
222
+
223
+ if safety_checker is None:
224
+ safety_checker = CosmosSafetyChecker()
225
+
226
+ self.register_modules(
227
+ vae=vae,
228
+ text_encoder=text_encoder,
229
+ tokenizer=tokenizer,
230
+ transformer=transformer,
231
+ scheduler=scheduler,
232
+ safety_checker=safety_checker,
233
+ )
234
+
235
+ self.vae_scale_factor_temporal = (
236
+ self.vae.config.temporal_compression_ratio if getattr(self, "vae", None) else 8
237
+ )
238
+ self.vae_scale_factor_spatial = self.vae.config.spatial_compression_ratio if getattr(self, "vae", None) else 8
239
+ self.video_processor = VideoProcessor(vae_scale_factor=self.vae_scale_factor_spatial)
240
+
241
+ # Copied from diffusers.pipelines.cosmos.pipeline_cosmos_text2world.CosmosTextToWorldPipeline._get_t5_prompt_embeds
242
+ def _get_t5_prompt_embeds(
243
+ self,
244
+ prompt: str | list[str] = None,
245
+ max_sequence_length: int = 512,
246
+ device: torch.device | None = None,
247
+ dtype: torch.dtype | None = None,
248
+ ):
249
+ device = device or self._execution_device
250
+ dtype = dtype or self.text_encoder.dtype
251
+ prompt = [prompt] if isinstance(prompt, str) else prompt
252
+
253
+ text_inputs = self.tokenizer(
254
+ prompt,
255
+ padding="max_length",
256
+ max_length=max_sequence_length,
257
+ truncation=True,
258
+ return_tensors="pt",
259
+ return_length=True,
260
+ return_offsets_mapping=False,
261
+ )
262
+ text_input_ids = text_inputs.input_ids
263
+ prompt_attention_mask = text_inputs.attention_mask.bool().to(device)
264
+
265
+ untruncated_ids = self.tokenizer(prompt, padding="longest", return_tensors="pt").input_ids
266
+ if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
267
+ removed_text = self.tokenizer.batch_decode(untruncated_ids[:, max_sequence_length - 1 : -1])
268
+ logger.warning(
269
+ "The following part of your input was truncated because `max_sequence_length` is set to "
270
+ f" {max_sequence_length} tokens: {removed_text}"
271
+ )
272
+
273
+ prompt_embeds = self.text_encoder(
274
+ text_input_ids.to(device), attention_mask=prompt_attention_mask
275
+ ).last_hidden_state
276
+ prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
277
+
278
+ lengths = prompt_attention_mask.sum(dim=1).cpu()
279
+ for i, length in enumerate(lengths):
280
+ prompt_embeds[i, length:] = 0
281
+
282
+ return prompt_embeds
283
+
284
+ # Copied from diffusers.pipelines.cosmos.pipeline_cosmos_text2world.CosmosTextToWorldPipeline.encode_prompt
285
+ def encode_prompt(
286
+ self,
287
+ prompt: str | list[str],
288
+ negative_prompt: str | list[str] | None = None,
289
+ do_classifier_free_guidance: bool = True,
290
+ num_videos_per_prompt: int = 1,
291
+ prompt_embeds: torch.Tensor | None = None,
292
+ negative_prompt_embeds: torch.Tensor | None = None,
293
+ max_sequence_length: int = 512,
294
+ device: torch.device | None = None,
295
+ dtype: torch.dtype | None = None,
296
+ ):
297
+ r"""
298
+ Encodes the prompt into text encoder hidden states.
299
+
300
+ Args:
301
+ prompt (`str` or `list[str]`, *optional*):
302
+ prompt to be encoded
303
+ negative_prompt (`str` or `list[str]`, *optional*):
304
+ The prompt or prompts not to guide the image generation. If not defined, one has to pass
305
+ `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `guidance_scale` is
306
+ less than `1`).
307
+ do_classifier_free_guidance (`bool`, *optional*, defaults to `True`):
308
+ Whether to use classifier free guidance or not.
309
+ num_videos_per_prompt (`int`, *optional*, defaults to 1):
310
+ Number of videos that should be generated per prompt. torch device to place the resulting embeddings on
311
+ prompt_embeds (`torch.Tensor`, *optional*):
312
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
313
+ provided, text embeddings will be generated from `prompt` input argument.
314
+ negative_prompt_embeds (`torch.Tensor`, *optional*):
315
+ Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
316
+ weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
317
+ argument.
318
+ device: (`torch.device`, *optional*):
319
+ torch device
320
+ dtype: (`torch.dtype`, *optional*):
321
+ torch dtype
322
+ """
323
+ device = device or self._execution_device
324
+
325
+ prompt = [prompt] if isinstance(prompt, str) else prompt
326
+ if prompt is not None:
327
+ batch_size = len(prompt)
328
+ else:
329
+ batch_size = prompt_embeds.shape[0]
330
+
331
+ if prompt_embeds is None:
332
+ prompt_embeds = self._get_t5_prompt_embeds(
333
+ prompt=prompt, max_sequence_length=max_sequence_length, device=device, dtype=dtype
334
+ )
335
+
336
+ # duplicate text embeddings for each generation per prompt, using mps friendly method
337
+ _, seq_len, _ = prompt_embeds.shape
338
+ prompt_embeds = prompt_embeds.repeat(1, num_videos_per_prompt, 1)
339
+ prompt_embeds = prompt_embeds.view(batch_size * num_videos_per_prompt, seq_len, -1)
340
+
341
+ if do_classifier_free_guidance and negative_prompt_embeds is None:
342
+ negative_prompt = negative_prompt if negative_prompt is not None else DEFAULT_NEGATIVE_PROMPT
343
+ negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
344
+
345
+ if prompt is not None and type(prompt) is not type(negative_prompt):
346
+ raise TypeError(
347
+ f"`negative_prompt` should be the same type to `prompt`, but got {type(negative_prompt)} !="
348
+ f" {type(prompt)}."
349
+ )
350
+ elif batch_size != len(negative_prompt):
351
+ raise ValueError(
352
+ f"`negative_prompt`: {negative_prompt} has batch size {len(negative_prompt)}, but `prompt`:"
353
+ f" {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches"
354
+ " the batch size of `prompt`."
355
+ )
356
+
357
+ negative_prompt_embeds = self._get_t5_prompt_embeds(
358
+ prompt=negative_prompt, max_sequence_length=max_sequence_length, device=device, dtype=dtype
359
+ )
360
+
361
+ # duplicate text embeddings for each generation per prompt, using mps friendly method
362
+ _, seq_len, _ = negative_prompt_embeds.shape
363
+ negative_prompt_embeds = negative_prompt_embeds.repeat(1, num_videos_per_prompt, 1)
364
+ negative_prompt_embeds = negative_prompt_embeds.view(batch_size * num_videos_per_prompt, seq_len, -1)
365
+
366
+ return prompt_embeds, negative_prompt_embeds
367
+
368
+ def prepare_latents(
369
+ self,
370
+ video: torch.Tensor,
371
+ batch_size: int,
372
+ num_channels_latents: 16,
373
+ height: int = 704,
374
+ width: int = 1280,
375
+ num_frames: int = 121,
376
+ do_classifier_free_guidance: bool = True,
377
+ input_frames_guidance: bool = False,
378
+ dtype: torch.dtype | None = None,
379
+ device: torch.device | None = None,
380
+ generator: torch.Generator | list[torch.Generator] | None = None,
381
+ latents: torch.Tensor | None = None,
382
+ ) -> torch.Tensor:
383
+ if isinstance(generator, list) and len(generator) != batch_size:
384
+ raise ValueError(
385
+ f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
386
+ f" size of {batch_size}. Make sure the batch size matches the length of the generators."
387
+ )
388
+
389
+ num_cond_frames = video.size(2)
390
+ if num_cond_frames >= num_frames:
391
+ # Take the last `num_frames` frames for conditioning
392
+ num_cond_latent_frames = (num_frames - 1) // self.vae_scale_factor_temporal + 1
393
+ video = video[:, :, -num_frames:]
394
+ else:
395
+ num_cond_latent_frames = (num_cond_frames - 1) // self.vae_scale_factor_temporal + 1
396
+ num_padding_frames = num_frames - num_cond_frames
397
+ padding = video.new_zeros(video.size(0), video.size(1), num_padding_frames, video.size(3), video.size(4))
398
+ video = torch.cat([video, padding], dim=2)
399
+
400
+ if isinstance(generator, list):
401
+ init_latents = [
402
+ retrieve_latents(self.vae.encode(video[i].unsqueeze(0)), generator=generator[i])
403
+ for i in range(batch_size)
404
+ ]
405
+ else:
406
+ init_latents = [retrieve_latents(self.vae.encode(vid.unsqueeze(0)), generator) for vid in video]
407
+
408
+ init_latents = torch.cat(init_latents, dim=0).to(dtype)
409
+
410
+ if self.vae.config.latents_mean is not None:
411
+ latents_mean, latents_std = self.vae.config.latents_mean, self.vae.config.latents_std
412
+ latents_mean = (
413
+ torch.tensor(latents_mean)
414
+ .view(1, self.vae.config.latent_channels, -1, 1, 1)[:, :, : init_latents.size(2)]
415
+ .to(init_latents)
416
+ )
417
+ latents_std = (
418
+ torch.tensor(latents_std)
419
+ .view(1, self.vae.config.latent_channels, -1, 1, 1)[:, :, : init_latents.size(2)]
420
+ .to(init_latents)
421
+ )
422
+ init_latents = (init_latents - latents_mean) * self.scheduler.config.sigma_data / latents_std
423
+ else:
424
+ init_latents = init_latents * self.scheduler.config.sigma_data
425
+
426
+ num_latent_frames = (num_frames - 1) // self.vae_scale_factor_temporal + 1
427
+ latent_height = height // self.vae_scale_factor_spatial
428
+ latent_width = width // self.vae_scale_factor_spatial
429
+ shape = (batch_size, num_channels_latents, num_latent_frames, latent_height, latent_width)
430
+
431
+ if latents is None:
432
+ latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
433
+ else:
434
+ latents = latents.to(device=device, dtype=dtype)
435
+
436
+ latents = latents * self.scheduler.config.sigma_max
437
+
438
+ padding_shape = (batch_size, 1, num_latent_frames, latent_height, latent_width)
439
+ ones_padding = latents.new_ones(padding_shape)
440
+ zeros_padding = latents.new_zeros(padding_shape)
441
+
442
+ cond_indicator = latents.new_zeros(1, 1, latents.size(2), 1, 1)
443
+ cond_indicator[:, :, :num_cond_latent_frames] = 1.0
444
+ cond_mask = cond_indicator * ones_padding + (1 - cond_indicator) * zeros_padding
445
+
446
+ uncond_indicator = uncond_mask = None
447
+ if do_classifier_free_guidance:
448
+ uncond_indicator = latents.new_zeros(1, 1, latents.size(2), 1, 1)
449
+ uncond_indicator[:, :, :num_cond_latent_frames] = 1.0
450
+ uncond_mask = zeros_padding
451
+ if not input_frames_guidance:
452
+ uncond_mask = uncond_indicator * ones_padding + (1 - uncond_indicator) * zeros_padding
453
+
454
+ return latents, init_latents, cond_indicator, uncond_indicator, cond_mask, uncond_mask
455
+
456
+ def check_inputs(
457
+ self,
458
+ prompt,
459
+ height,
460
+ width,
461
+ prompt_embeds=None,
462
+ callback_on_step_end_tensor_inputs=None,
463
+ image=None,
464
+ video=None,
465
+ ):
466
+ if height % 16 != 0 or width % 16 != 0:
467
+ raise ValueError(f"`height` and `width` have to be divisible by 16 but are {height} and {width}.")
468
+
469
+ if callback_on_step_end_tensor_inputs is not None and not all(
470
+ k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
471
+ ):
472
+ raise ValueError(
473
+ f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
474
+ )
475
+
476
+ if prompt is not None and prompt_embeds is not None:
477
+ raise ValueError(
478
+ f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
479
+ " only forward one of the two."
480
+ )
481
+ elif prompt is None and prompt_embeds is None:
482
+ raise ValueError(
483
+ "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
484
+ )
485
+ elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
486
+ raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
487
+
488
+ if image is None and video is None:
489
+ raise ValueError("Either `image` or `video` has to be provided.")
490
+ if image is not None and video is not None:
491
+ raise ValueError("Only one of `image` or `video` has to be provided.")
492
+
493
+ @property
494
+ def guidance_scale(self):
495
+ return self._guidance_scale
496
+
497
+ @property
498
+ def do_classifier_free_guidance(self):
499
+ return self._guidance_scale > 1.0
500
+
501
+ @property
502
+ def num_timesteps(self):
503
+ return self._num_timesteps
504
+
505
+ @property
506
+ def current_timestep(self):
507
+ return self._current_timestep
508
+
509
+ @property
510
+ def interrupt(self):
511
+ return self._interrupt
512
+
513
+ @torch.no_grad()
514
+ @replace_example_docstring(EXAMPLE_DOC_STRING)
515
+ def __call__(
516
+ self,
517
+ image: PipelineImageInput = None,
518
+ video: list[PipelineImageInput] = None,
519
+ prompt: str | list[str] = None,
520
+ negative_prompt: str | list[str] | None = None,
521
+ height: int = 704,
522
+ width: int = 1280,
523
+ num_frames: int = 121,
524
+ num_inference_steps: int = 36,
525
+ guidance_scale: float = 7.0,
526
+ input_frames_guidance: bool = False,
527
+ augment_sigma: float = 0.001,
528
+ fps: int = 30,
529
+ num_videos_per_prompt: int | None = 1,
530
+ generator: torch.Generator | list[torch.Generator] | None = None,
531
+ latents: torch.Tensor | None = None,
532
+ prompt_embeds: torch.Tensor | None = None,
533
+ negative_prompt_embeds: torch.Tensor | None = None,
534
+ output_type: str | None = "pil",
535
+ return_dict: bool = True,
536
+ callback_on_step_end: Callable[[int, int], None] | PipelineCallback | MultiPipelineCallbacks | None = None,
537
+ callback_on_step_end_tensor_inputs: list[str] = ["latents"],
538
+ max_sequence_length: int = 512,
539
+ ):
540
+ r"""
541
+ The call function to the pipeline for generation.
542
+
543
+ Args:
544
+ prompt (`str` or `list[str]`, *optional*):
545
+ The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
546
+ instead.
547
+ height (`int`, defaults to `720`):
548
+ The height in pixels of the generated image.
549
+ width (`int`, defaults to `1280`):
550
+ The width in pixels of the generated image.
551
+ num_frames (`int`, defaults to `121`):
552
+ The number of frames in the generated video.
553
+ num_inference_steps (`int`, defaults to `36`):
554
+ The number of denoising steps. More denoising steps usually lead to a higher quality image at the
555
+ expense of slower inference.
556
+ guidance_scale (`float`, defaults to `7.0`):
557
+ Guidance scale as defined in [Classifier-Free Diffusion
558
+ Guidance](https://huggingface.co/papers/2207.12598). `guidance_scale` is defined as `w` of equation 2.
559
+ of [Imagen Paper](https://huggingface.co/papers/2205.11487). Guidance scale is enabled by setting
560
+ `guidance_scale > 1`.
561
+ fps (`int`, defaults to `30`):
562
+ The frames per second of the generated video.
563
+ num_videos_per_prompt (`int`, *optional*, defaults to 1):
564
+ The number of images to generate per prompt.
565
+ generator (`torch.Generator` or `list[torch.Generator]`, *optional*):
566
+ A [`torch.Generator`](https://pytorch.org/docs/stable/generated/torch.Generator.html) to make
567
+ generation deterministic.
568
+ latents (`torch.Tensor`, *optional*):
569
+ Pre-generated noisy latents sampled from a Gaussian distribution, to be used as inputs for image
570
+ generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
571
+ tensor is generated by sampling using the supplied random `generator`.
572
+ prompt_embeds (`torch.Tensor`, *optional*):
573
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
574
+ provided, text embeddings will be generated from `prompt` input argument.
575
+ negative_prompt_embeds (`torch.FloatTensor`, *optional*):
576
+ Pre-generated negative text embeddings. For PixArt-Sigma this negative prompt should be "". If not
577
+ provided, negative_prompt_embeds will be generated from `negative_prompt` input argument.
578
+ output_type (`str`, *optional*, defaults to `"pil"`):
579
+ The output format of the generated image. Choose between `PIL.Image` or `np.array`.
580
+ return_dict (`bool`, *optional*, defaults to `True`):
581
+ Whether or not to return a [`CosmosPipelineOutput`] instead of a plain tuple.
582
+ callback_on_step_end (`Callable`, `PipelineCallback`, `MultiPipelineCallbacks`, *optional*):
583
+ A function or a subclass of `PipelineCallback` or `MultiPipelineCallbacks` that is called at the end of
584
+ each denoising step during the inference. with the following arguments: `callback_on_step_end(self:
585
+ DiffusionPipeline, step: int, timestep: int, callback_kwargs: Dict)`. `callback_kwargs` will include a
586
+ list of all tensors as specified by `callback_on_step_end_tensor_inputs`.
587
+ callback_on_step_end_tensor_inputs (`list`, *optional*):
588
+ The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
589
+ will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
590
+ `._callback_tensor_inputs` attribute of your pipeline class.
591
+
592
+ Examples:
593
+
594
+ Returns:
595
+ [`~CosmosPipelineOutput`] or `tuple`:
596
+ If `return_dict` is `True`, [`CosmosPipelineOutput`] is returned, otherwise a `tuple` is returned where
597
+ the first element is a list with the generated images and the second element is a list of `bool`s
598
+ indicating whether the corresponding generated image contains "not-safe-for-work" (nsfw) content.
599
+ """
600
+
601
+ if self.safety_checker is None:
602
+ raise ValueError(
603
+ f"You have disabled the safety checker for {self.__class__}. This is in violation of the "
604
+ "[NVIDIA Open Model License Agreement](https://www.nvidia.com/en-us/agreements/enterprise-software/nvidia-open-model-license). "
605
+ f"Please ensure that you are compliant with the license agreement."
606
+ )
607
+
608
+ if isinstance(callback_on_step_end, (PipelineCallback, MultiPipelineCallbacks)):
609
+ callback_on_step_end_tensor_inputs = callback_on_step_end.tensor_inputs
610
+
611
+ # 1. Check inputs. Raise error if not correct
612
+ self.check_inputs(prompt, height, width, prompt_embeds, callback_on_step_end_tensor_inputs, image, video)
613
+
614
+ self._guidance_scale = guidance_scale
615
+ self._current_timestep = None
616
+ self._interrupt = False
617
+
618
+ device = self._execution_device
619
+
620
+ if self.safety_checker is not None:
621
+ self.safety_checker.to(device)
622
+ if prompt is not None:
623
+ prompt_list = [prompt] if isinstance(prompt, str) else prompt
624
+ for p in prompt_list:
625
+ if not self.safety_checker.check_text_safety(p):
626
+ raise ValueError(
627
+ f"Cosmos Guardrail detected unsafe text in the prompt: {p}. Please ensure that the "
628
+ f"prompt abides by the NVIDIA Open Model License Agreement."
629
+ )
630
+ self.safety_checker.to("cpu")
631
+
632
+ # 2. Define call parameters
633
+ if prompt is not None and isinstance(prompt, str):
634
+ batch_size = 1
635
+ elif prompt is not None and isinstance(prompt, list):
636
+ batch_size = len(prompt)
637
+ else:
638
+ batch_size = prompt_embeds.shape[0]
639
+
640
+ # 3. Encode input prompt
641
+ (
642
+ prompt_embeds,
643
+ negative_prompt_embeds,
644
+ ) = self.encode_prompt(
645
+ prompt=prompt,
646
+ negative_prompt=negative_prompt,
647
+ do_classifier_free_guidance=self.do_classifier_free_guidance,
648
+ num_videos_per_prompt=num_videos_per_prompt,
649
+ prompt_embeds=prompt_embeds,
650
+ negative_prompt_embeds=negative_prompt_embeds,
651
+ device=device,
652
+ max_sequence_length=max_sequence_length,
653
+ )
654
+
655
+ # 4. Prepare timesteps
656
+ timesteps, num_inference_steps = retrieve_timesteps(self.scheduler, num_inference_steps, device)
657
+
658
+ # 5. Prepare latent variables
659
+ vae_dtype = self.vae.dtype
660
+ transformer_dtype = self.transformer.dtype
661
+
662
+ if image is not None:
663
+ video = self.video_processor.preprocess(image, height, width).unsqueeze(2)
664
+ else:
665
+ video = self.video_processor.preprocess_video(video, height, width)
666
+ video = video.to(device=device, dtype=vae_dtype)
667
+
668
+ num_channels_latents = self.transformer.config.in_channels - 1
669
+ latents, conditioning_latents, cond_indicator, uncond_indicator, cond_mask, uncond_mask = self.prepare_latents(
670
+ video,
671
+ batch_size * num_videos_per_prompt,
672
+ num_channels_latents,
673
+ height,
674
+ width,
675
+ num_frames,
676
+ self.do_classifier_free_guidance,
677
+ input_frames_guidance,
678
+ torch.float32,
679
+ device,
680
+ generator,
681
+ latents,
682
+ )
683
+ cond_mask = cond_mask.to(transformer_dtype)
684
+ if self.do_classifier_free_guidance:
685
+ uncond_mask = uncond_mask.to(transformer_dtype)
686
+
687
+ augment_sigma = torch.tensor([augment_sigma], device=device, dtype=torch.float32)
688
+ padding_mask = latents.new_zeros(1, 1, height, width, dtype=transformer_dtype)
689
+
690
+ # 6. Denoising loop
691
+ num_warmup_steps = len(timesteps) - num_inference_steps * self.scheduler.order
692
+ self._num_timesteps = len(timesteps)
693
+
694
+ with self.progress_bar(total=num_inference_steps) as progress_bar:
695
+ for i, t in enumerate(timesteps):
696
+ if self.interrupt:
697
+ continue
698
+
699
+ self._current_timestep = t
700
+ timestep = t.expand(latents.shape[0]).to(transformer_dtype)
701
+
702
+ current_sigma = self.scheduler.sigmas[i]
703
+ is_augment_sigma_greater = augment_sigma >= current_sigma
704
+
705
+ c_in_augment = self.scheduler._get_conditioning_c_in(augment_sigma)
706
+ c_in_original = self.scheduler._get_conditioning_c_in(current_sigma)
707
+
708
+ current_cond_indicator = cond_indicator * 0 if is_augment_sigma_greater else cond_indicator
709
+ cond_noise = randn_tensor(latents.shape, generator=generator, device=device, dtype=torch.float32)
710
+ cond_latent = conditioning_latents + cond_noise * augment_sigma[:, None, None, None, None]
711
+ cond_latent = cond_latent * c_in_augment / c_in_original
712
+ cond_latent = current_cond_indicator * cond_latent + (1 - current_cond_indicator) * latents
713
+ cond_latent = self.scheduler.scale_model_input(cond_latent, t)
714
+ cond_latent = cond_latent.to(transformer_dtype)
715
+
716
+ noise_pred = self.transformer(
717
+ hidden_states=cond_latent,
718
+ timestep=timestep,
719
+ encoder_hidden_states=prompt_embeds,
720
+ fps=fps,
721
+ condition_mask=cond_mask,
722
+ padding_mask=padding_mask,
723
+ return_dict=False,
724
+ )[0]
725
+
726
+ sample = latents
727
+ if self.do_classifier_free_guidance:
728
+ current_uncond_indicator = uncond_indicator * 0 if is_augment_sigma_greater else uncond_indicator
729
+ uncond_noise = randn_tensor(latents.shape, generator=generator, device=device, dtype=torch.float32)
730
+ uncond_latent = conditioning_latents + uncond_noise * augment_sigma[:, None, None, None, None]
731
+ uncond_latent = uncond_latent * c_in_augment / c_in_original
732
+ uncond_latent = current_uncond_indicator * uncond_latent + (1 - current_uncond_indicator) * latents
733
+ uncond_latent = self.scheduler.scale_model_input(uncond_latent, t)
734
+ uncond_latent = uncond_latent.to(transformer_dtype)
735
+
736
+ noise_pred_uncond = self.transformer(
737
+ hidden_states=uncond_latent,
738
+ timestep=timestep,
739
+ encoder_hidden_states=negative_prompt_embeds,
740
+ fps=fps,
741
+ condition_mask=uncond_mask,
742
+ padding_mask=padding_mask,
743
+ return_dict=False,
744
+ )[0]
745
+ noise_pred = torch.cat([noise_pred_uncond, noise_pred])
746
+ sample = torch.cat([sample, sample])
747
+
748
+ # pred_original_sample (x0)
749
+ noise_pred = self.scheduler.step(noise_pred, t, sample, return_dict=False)[1]
750
+ self.scheduler._step_index -= 1
751
+
752
+ if self.do_classifier_free_guidance:
753
+ noise_pred_uncond, noise_pred_cond = noise_pred.chunk(2, dim=0)
754
+ noise_pred_uncond = (
755
+ current_uncond_indicator * conditioning_latents
756
+ + (1 - current_uncond_indicator) * noise_pred_uncond
757
+ )
758
+ noise_pred_cond = (
759
+ current_cond_indicator * conditioning_latents + (1 - current_cond_indicator) * noise_pred_cond
760
+ )
761
+ noise_pred = noise_pred_cond + self.guidance_scale * (noise_pred_cond - noise_pred_uncond)
762
+ else:
763
+ noise_pred = (
764
+ current_cond_indicator * conditioning_latents + (1 - current_cond_indicator) * noise_pred
765
+ )
766
+
767
+ # pred_sample (eps)
768
+ latents = self.scheduler.step(
769
+ noise_pred, t, latents, return_dict=False, pred_original_sample=noise_pred
770
+ )[0]
771
+
772
+ if callback_on_step_end is not None:
773
+ callback_kwargs = {}
774
+ for k in callback_on_step_end_tensor_inputs:
775
+ callback_kwargs[k] = locals()[k]
776
+ callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
777
+
778
+ latents = callback_outputs.pop("latents", latents)
779
+ prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
780
+ negative_prompt_embeds = callback_outputs.pop("negative_prompt_embeds", negative_prompt_embeds)
781
+
782
+ # call the callback, if provided
783
+ if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
784
+ progress_bar.update()
785
+
786
+ if XLA_AVAILABLE:
787
+ xm.mark_step()
788
+
789
+ self._current_timestep = None
790
+
791
+ if not output_type == "latent":
792
+ if self.vae.config.latents_mean is not None:
793
+ latents_mean, latents_std = self.vae.config.latents_mean, self.vae.config.latents_std
794
+ latents_mean = (
795
+ torch.tensor(latents_mean)
796
+ .view(1, self.vae.config.latent_channels, -1, 1, 1)[:, :, : latents.size(2)]
797
+ .to(latents)
798
+ )
799
+ latents_std = (
800
+ torch.tensor(latents_std)
801
+ .view(1, self.vae.config.latent_channels, -1, 1, 1)[:, :, : latents.size(2)]
802
+ .to(latents)
803
+ )
804
+ latents = latents * latents_std / self.scheduler.config.sigma_data + latents_mean
805
+ else:
806
+ latents = latents / self.scheduler.config.sigma_data
807
+ video = self.vae.decode(latents.to(vae_dtype), return_dict=False)[0]
808
+
809
+ if self.safety_checker is not None:
810
+ self.safety_checker.to(device)
811
+ video = self.video_processor.postprocess_video(video, output_type="np")
812
+ video = (video * 255).astype(np.uint8)
813
+ video_batch = []
814
+ for vid in video:
815
+ vid = self.safety_checker.check_video_safety(vid)
816
+ video_batch.append(vid)
817
+ video = np.stack(video_batch).astype(np.float32) / 255.0 * 2 - 1
818
+ video = torch.from_numpy(video).permute(0, 4, 1, 2, 3)
819
+ video = self.video_processor.postprocess_video(video, output_type=output_type)
820
+ self.safety_checker.to("cpu")
821
+ else:
822
+ video = self.video_processor.postprocess_video(video, output_type=output_type)
823
+ else:
824
+ video = latents
825
+
826
+ # Offload all models
827
+ self.maybe_free_model_hooks()
828
+
829
+ if not return_dict:
830
+ return (video,)
831
+
832
+ return CosmosPipelineOutput(frames=video)
third_party/diffusers/src/diffusers/pipelines/cosmos/pipeline_output.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass
2
+
3
+ import numpy as np
4
+ import PIL.Image
5
+ import torch
6
+
7
+ from diffusers.utils import BaseOutput, get_logger
8
+
9
+
10
+ logger = get_logger(__name__)
11
+
12
+
13
+ @dataclass
14
+ class CosmosPipelineOutput(BaseOutput):
15
+ r"""
16
+ Output class for Cosmos any-to-world/video pipelines.
17
+
18
+ Args:
19
+ frames (`torch.Tensor`, `np.ndarray`, or list[list[PIL.Image.Image]]):
20
+ list of video outputs - It can be a nested list of length `batch_size,` with each sub-list containing
21
+ denoised PIL image sequences of length `num_frames.` It can also be a NumPy array or Torch tensor of shape
22
+ `(batch_size, num_frames, channels, height, width)`.
23
+ """
24
+
25
+ frames: torch.Tensor
26
+
27
+
28
+ @dataclass
29
+ class CosmosImagePipelineOutput(BaseOutput):
30
+ """
31
+ Output class for Cosmos any-to-image pipelines.
32
+
33
+ Args:
34
+ images (`list[PIL.Image.Image]` or `np.ndarray`)
35
+ list of denoised PIL images of length `batch_size` or numpy array of shape `(batch_size, height, width,
36
+ num_channels)`. PIL images or numpy array present the denoised images of the diffusion pipeline.
37
+ """
38
+
39
+ images: list[PIL.Image.Image] | np.ndarray
third_party/diffusers/src/diffusers/pipelines/free_init_utils.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import math
16
+
17
+ import torch
18
+ import torch.fft as fft
19
+
20
+ from ..utils.torch_utils import randn_tensor
21
+
22
+
23
+ class FreeInitMixin:
24
+ r"""Mixin class for FreeInit."""
25
+
26
+ def enable_free_init(
27
+ self,
28
+ num_iters: int = 3,
29
+ use_fast_sampling: bool = False,
30
+ method: str = "butterworth",
31
+ order: int = 4,
32
+ spatial_stop_frequency: float = 0.25,
33
+ temporal_stop_frequency: float = 0.25,
34
+ ):
35
+ """Enables the FreeInit mechanism as in https://huggingface.co/papers/2312.07537.
36
+
37
+ This implementation has been adapted from the [official repository](https://github.com/TianxingWu/FreeInit).
38
+
39
+ Args:
40
+ num_iters (`int`, *optional*, defaults to `3`):
41
+ Number of FreeInit noise re-initialization iterations.
42
+ use_fast_sampling (`bool`, *optional*, defaults to `False`):
43
+ Whether or not to speedup sampling procedure at the cost of probably lower quality results. Enables the
44
+ "Coarse-to-Fine Sampling" strategy, as mentioned in the paper, if set to `True`.
45
+ method (`str`, *optional*, defaults to `butterworth`):
46
+ Must be one of `butterworth`, `ideal` or `gaussian` to use as the filtering method for the FreeInit low
47
+ pass filter.
48
+ order (`int`, *optional*, defaults to `4`):
49
+ Order of the filter used in `butterworth` method. Larger values lead to `ideal` method behaviour
50
+ whereas lower values lead to `gaussian` method behaviour.
51
+ spatial_stop_frequency (`float`, *optional*, defaults to `0.25`):
52
+ Normalized stop frequency for spatial dimensions. Must be between 0 to 1. Referred to as `d_s` in the
53
+ original implementation.
54
+ temporal_stop_frequency (`float`, *optional*, defaults to `0.25`):
55
+ Normalized stop frequency for temporal dimensions. Must be between 0 to 1. Referred to as `d_t` in the
56
+ original implementation.
57
+ """
58
+ self._free_init_num_iters = num_iters
59
+ self._free_init_use_fast_sampling = use_fast_sampling
60
+ self._free_init_method = method
61
+ self._free_init_order = order
62
+ self._free_init_spatial_stop_frequency = spatial_stop_frequency
63
+ self._free_init_temporal_stop_frequency = temporal_stop_frequency
64
+
65
+ def disable_free_init(self):
66
+ """Disables the FreeInit mechanism if enabled."""
67
+ self._free_init_num_iters = None
68
+
69
+ @property
70
+ def free_init_enabled(self):
71
+ return hasattr(self, "_free_init_num_iters") and self._free_init_num_iters is not None
72
+
73
+ def _get_free_init_freq_filter(
74
+ self,
75
+ shape: tuple[int, ...],
76
+ device: str | torch.dtype,
77
+ filter_type: str,
78
+ order: float,
79
+ spatial_stop_frequency: float,
80
+ temporal_stop_frequency: float,
81
+ ) -> torch.Tensor:
82
+ r"""Returns the FreeInit filter based on filter type and other input conditions."""
83
+
84
+ time, height, width = shape[-3], shape[-2], shape[-1]
85
+ mask = torch.zeros(shape)
86
+
87
+ if spatial_stop_frequency == 0 or temporal_stop_frequency == 0:
88
+ return mask
89
+
90
+ if filter_type == "butterworth":
91
+
92
+ def retrieve_mask(x):
93
+ return 1 / (1 + (x / spatial_stop_frequency**2) ** order)
94
+ elif filter_type == "gaussian":
95
+
96
+ def retrieve_mask(x):
97
+ return math.exp(-1 / (2 * spatial_stop_frequency**2) * x)
98
+ elif filter_type == "ideal":
99
+
100
+ def retrieve_mask(x):
101
+ return 1 if x <= spatial_stop_frequency * 2 else 0
102
+ else:
103
+ raise NotImplementedError("`filter_type` must be one of gaussian, butterworth or ideal")
104
+
105
+ for t in range(time):
106
+ for h in range(height):
107
+ for w in range(width):
108
+ d_square = (
109
+ ((spatial_stop_frequency / temporal_stop_frequency) * (2 * t / time - 1)) ** 2
110
+ + (2 * h / height - 1) ** 2
111
+ + (2 * w / width - 1) ** 2
112
+ )
113
+ mask[..., t, h, w] = retrieve_mask(d_square)
114
+
115
+ return mask.to(device)
116
+
117
+ def _apply_freq_filter(self, x: torch.Tensor, noise: torch.Tensor, low_pass_filter: torch.Tensor) -> torch.Tensor:
118
+ r"""Noise reinitialization."""
119
+ # FFT
120
+ x_freq = fft.fftn(x, dim=(-3, -2, -1))
121
+ x_freq = fft.fftshift(x_freq, dim=(-3, -2, -1))
122
+ noise_freq = fft.fftn(noise, dim=(-3, -2, -1))
123
+ noise_freq = fft.fftshift(noise_freq, dim=(-3, -2, -1))
124
+
125
+ # frequency mix
126
+ high_pass_filter = 1 - low_pass_filter
127
+ x_freq_low = x_freq * low_pass_filter
128
+ noise_freq_high = noise_freq * high_pass_filter
129
+ x_freq_mixed = x_freq_low + noise_freq_high # mix in freq domain
130
+
131
+ # IFFT
132
+ x_freq_mixed = fft.ifftshift(x_freq_mixed, dim=(-3, -2, -1))
133
+ x_mixed = fft.ifftn(x_freq_mixed, dim=(-3, -2, -1)).real
134
+
135
+ return x_mixed
136
+
137
+ def _apply_free_init(
138
+ self,
139
+ latents: torch.Tensor,
140
+ free_init_iteration: int,
141
+ num_inference_steps: int,
142
+ device: torch.device,
143
+ dtype: torch.dtype,
144
+ generator: torch.Generator,
145
+ ):
146
+ if free_init_iteration == 0:
147
+ self._free_init_initial_noise = latents.detach().clone()
148
+ else:
149
+ latent_shape = latents.shape
150
+
151
+ free_init_filter_shape = (1, *latent_shape[1:])
152
+ free_init_freq_filter = self._get_free_init_freq_filter(
153
+ shape=free_init_filter_shape,
154
+ device=device,
155
+ filter_type=self._free_init_method,
156
+ order=self._free_init_order,
157
+ spatial_stop_frequency=self._free_init_spatial_stop_frequency,
158
+ temporal_stop_frequency=self._free_init_temporal_stop_frequency,
159
+ )
160
+
161
+ current_diffuse_timestep = self.scheduler.config.num_train_timesteps - 1
162
+ diffuse_timesteps = torch.full((latent_shape[0],), current_diffuse_timestep).long()
163
+
164
+ z_t = self.scheduler.add_noise(
165
+ original_samples=latents, noise=self._free_init_initial_noise, timesteps=diffuse_timesteps.to(device)
166
+ ).to(dtype=torch.float32)
167
+
168
+ z_rand = randn_tensor(
169
+ shape=latent_shape,
170
+ generator=generator,
171
+ device=device,
172
+ dtype=torch.float32,
173
+ )
174
+ latents = self._apply_freq_filter(z_t, z_rand, low_pass_filter=free_init_freq_filter)
175
+ latents = latents.to(dtype)
176
+
177
+ # Coarse-to-Fine Sampling for faster inference (can lead to lower quality)
178
+ if self._free_init_use_fast_sampling:
179
+ num_inference_steps = max(
180
+ 1, int(num_inference_steps / self._free_init_num_iters * (free_init_iteration + 1))
181
+ )
182
+
183
+ if num_inference_steps > 0:
184
+ self.scheduler.set_timesteps(num_inference_steps, device=device)
185
+
186
+ return latents, self.scheduler.timesteps
third_party/diffusers/src/diffusers/pipelines/free_noise_utils.py ADDED
@@ -0,0 +1,597 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from typing import Callable
16
+
17
+ import torch
18
+ import torch.nn as nn
19
+
20
+ from ..models.attention import BasicTransformerBlock, FreeNoiseTransformerBlock
21
+ from ..models.resnet import Downsample2D, ResnetBlock2D, Upsample2D
22
+ from ..models.transformers.transformer_2d import Transformer2DModel
23
+ from ..models.unets.unet_motion_model import (
24
+ AnimateDiffTransformer3D,
25
+ CrossAttnDownBlockMotion,
26
+ DownBlockMotion,
27
+ UpBlockMotion,
28
+ )
29
+ from ..pipelines.pipeline_utils import DiffusionPipeline
30
+ from ..utils import logging
31
+ from ..utils.torch_utils import randn_tensor
32
+
33
+
34
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
35
+
36
+
37
+ class SplitInferenceModule(nn.Module):
38
+ r"""
39
+ A wrapper module class that splits inputs along a specified dimension before performing a forward pass.
40
+
41
+ This module is useful when you need to perform inference on large tensors in a memory-efficient way by breaking
42
+ them into smaller chunks, processing each chunk separately, and then reassembling the results.
43
+
44
+ Args:
45
+ module (`nn.Module`):
46
+ The underlying PyTorch module that will be applied to each chunk of split inputs.
47
+ split_size (`int`, defaults to `1`):
48
+ The size of each chunk after splitting the input tensor.
49
+ split_dim (`int`, defaults to `0`):
50
+ The dimension along which the input tensors are split.
51
+ input_kwargs_to_split (`list[str]`, defaults to `["hidden_states"]`):
52
+ A list of keyword arguments (strings) that represent the input tensors to be split.
53
+
54
+ Workflow:
55
+ 1. The keyword arguments specified in `input_kwargs_to_split` are split into smaller chunks using
56
+ `torch.split()` along the dimension `split_dim` and with a chunk size of `split_size`.
57
+ 2. The `module` is invoked once for each split with both the split inputs and any unchanged arguments
58
+ that were passed.
59
+ 3. The output tensors from each split are concatenated back together along `split_dim` before returning.
60
+
61
+ Example:
62
+ ```python
63
+ >>> import torch
64
+ >>> import torch.nn as nn
65
+
66
+ >>> model = nn.Linear(1000, 1000)
67
+ >>> split_module = SplitInferenceModule(model, split_size=2, split_dim=0, input_kwargs_to_split=["input"])
68
+
69
+ >>> input_tensor = torch.randn(42, 1000)
70
+ >>> # Will split the tensor into 21 slices of shape [2, 1000].
71
+ >>> output = split_module(input=input_tensor)
72
+ ```
73
+
74
+ It is also possible to nest `SplitInferenceModule` across different split dimensions for more complex
75
+ multi-dimensional splitting.
76
+ """
77
+
78
+ def __init__(
79
+ self,
80
+ module: nn.Module,
81
+ split_size: int = 1,
82
+ split_dim: int = 0,
83
+ input_kwargs_to_split: list[str] = ["hidden_states"],
84
+ ) -> None:
85
+ super().__init__()
86
+
87
+ self.module = module
88
+ self.split_size = split_size
89
+ self.split_dim = split_dim
90
+ self.input_kwargs_to_split = set(input_kwargs_to_split)
91
+
92
+ def forward(self, *args, **kwargs) -> torch.Tensor | tuple[torch.Tensor]:
93
+ r"""Forward method for the `SplitInferenceModule`.
94
+
95
+ This method processes the input by splitting specified keyword arguments along a given dimension, running the
96
+ underlying module on each split, and then concatenating the results. The splitting is controlled by the
97
+ `split_size` and `split_dim` parameters specified during initialization.
98
+
99
+ Args:
100
+ *args (`Any`):
101
+ Positional arguments that are passed directly to the `module` without modification.
102
+ **kwargs (`dict[str, torch.Tensor]`):
103
+ Keyword arguments passed to the underlying `module`. Only keyword arguments whose names match the
104
+ entries in `input_kwargs_to_split` and are of type `torch.Tensor` will be split. The remaining keyword
105
+ arguments are passed unchanged.
106
+
107
+ Returns:
108
+ `torch.Tensor | tuple[torch.Tensor]`:
109
+ The outputs obtained from `SplitInferenceModule` are the same as if the underlying module was inferred
110
+ without it.
111
+ - If the underlying module returns a single tensor, the result will be a single concatenated tensor
112
+ along the same `split_dim` after processing all splits.
113
+ - If the underlying module returns a tuple of tensors, each element of the tuple will be concatenated
114
+ along the `split_dim` across all splits, and the final result will be a tuple of concatenated tensors.
115
+ """
116
+ split_inputs = {}
117
+
118
+ # 1. Split inputs that were specified during initialization and also present in passed kwargs
119
+ for key in list(kwargs.keys()):
120
+ if key not in self.input_kwargs_to_split or not torch.is_tensor(kwargs[key]):
121
+ continue
122
+ split_inputs[key] = torch.split(kwargs[key], self.split_size, self.split_dim)
123
+ kwargs.pop(key)
124
+
125
+ # 2. Invoke forward pass across each split
126
+ results = []
127
+ for split_input in zip(*split_inputs.values()):
128
+ inputs = dict(zip(split_inputs.keys(), split_input))
129
+ inputs.update(kwargs)
130
+
131
+ intermediate_tensor_or_tensor_tuple = self.module(*args, **inputs)
132
+ results.append(intermediate_tensor_or_tensor_tuple)
133
+
134
+ # 3. Concatenate split restuls to obtain final outputs
135
+ if isinstance(results[0], torch.Tensor):
136
+ return torch.cat(results, dim=self.split_dim)
137
+ elif isinstance(results[0], tuple):
138
+ return tuple([torch.cat(x, dim=self.split_dim) for x in zip(*results)])
139
+ else:
140
+ raise ValueError(
141
+ "In order to use the SplitInferenceModule, it is necessary for the underlying `module` to either return a torch.Tensor or a tuple of torch.Tensor's."
142
+ )
143
+
144
+
145
+ class AnimateDiffFreeNoiseMixin:
146
+ r"""Mixin class for [FreeNoise](https://huggingface.co/papers/2310.15169)."""
147
+
148
+ def _enable_free_noise_in_block(self, block: CrossAttnDownBlockMotion | DownBlockMotion | UpBlockMotion):
149
+ r"""Helper function to enable FreeNoise in transformer blocks."""
150
+
151
+ for motion_module in block.motion_modules:
152
+ num_transformer_blocks = len(motion_module.transformer_blocks)
153
+
154
+ for i in range(num_transformer_blocks):
155
+ if isinstance(motion_module.transformer_blocks[i], FreeNoiseTransformerBlock):
156
+ motion_module.transformer_blocks[i].set_free_noise_properties(
157
+ self._free_noise_context_length,
158
+ self._free_noise_context_stride,
159
+ self._free_noise_weighting_scheme,
160
+ )
161
+ else:
162
+ assert isinstance(motion_module.transformer_blocks[i], BasicTransformerBlock)
163
+ basic_transfomer_block = motion_module.transformer_blocks[i]
164
+
165
+ motion_module.transformer_blocks[i] = FreeNoiseTransformerBlock(
166
+ dim=basic_transfomer_block.dim,
167
+ num_attention_heads=basic_transfomer_block.num_attention_heads,
168
+ attention_head_dim=basic_transfomer_block.attention_head_dim,
169
+ dropout=basic_transfomer_block.dropout,
170
+ cross_attention_dim=basic_transfomer_block.cross_attention_dim,
171
+ activation_fn=basic_transfomer_block.activation_fn,
172
+ attention_bias=basic_transfomer_block.attention_bias,
173
+ only_cross_attention=basic_transfomer_block.only_cross_attention,
174
+ double_self_attention=basic_transfomer_block.double_self_attention,
175
+ positional_embeddings=basic_transfomer_block.positional_embeddings,
176
+ num_positional_embeddings=basic_transfomer_block.num_positional_embeddings,
177
+ context_length=self._free_noise_context_length,
178
+ context_stride=self._free_noise_context_stride,
179
+ weighting_scheme=self._free_noise_weighting_scheme,
180
+ ).to(device=self.device, dtype=self.dtype)
181
+
182
+ motion_module.transformer_blocks[i].load_state_dict(
183
+ basic_transfomer_block.state_dict(), strict=True
184
+ )
185
+ motion_module.transformer_blocks[i].set_chunk_feed_forward(
186
+ basic_transfomer_block._chunk_size, basic_transfomer_block._chunk_dim
187
+ )
188
+
189
+ def _disable_free_noise_in_block(self, block: CrossAttnDownBlockMotion | DownBlockMotion | UpBlockMotion):
190
+ r"""Helper function to disable FreeNoise in transformer blocks."""
191
+
192
+ for motion_module in block.motion_modules:
193
+ num_transformer_blocks = len(motion_module.transformer_blocks)
194
+
195
+ for i in range(num_transformer_blocks):
196
+ if isinstance(motion_module.transformer_blocks[i], FreeNoiseTransformerBlock):
197
+ free_noise_transfomer_block = motion_module.transformer_blocks[i]
198
+
199
+ motion_module.transformer_blocks[i] = BasicTransformerBlock(
200
+ dim=free_noise_transfomer_block.dim,
201
+ num_attention_heads=free_noise_transfomer_block.num_attention_heads,
202
+ attention_head_dim=free_noise_transfomer_block.attention_head_dim,
203
+ dropout=free_noise_transfomer_block.dropout,
204
+ cross_attention_dim=free_noise_transfomer_block.cross_attention_dim,
205
+ activation_fn=free_noise_transfomer_block.activation_fn,
206
+ attention_bias=free_noise_transfomer_block.attention_bias,
207
+ only_cross_attention=free_noise_transfomer_block.only_cross_attention,
208
+ double_self_attention=free_noise_transfomer_block.double_self_attention,
209
+ positional_embeddings=free_noise_transfomer_block.positional_embeddings,
210
+ num_positional_embeddings=free_noise_transfomer_block.num_positional_embeddings,
211
+ ).to(device=self.device, dtype=self.dtype)
212
+
213
+ motion_module.transformer_blocks[i].load_state_dict(
214
+ free_noise_transfomer_block.state_dict(), strict=True
215
+ )
216
+ motion_module.transformer_blocks[i].set_chunk_feed_forward(
217
+ free_noise_transfomer_block._chunk_size, free_noise_transfomer_block._chunk_dim
218
+ )
219
+
220
+ def _check_inputs_free_noise(
221
+ self,
222
+ prompt,
223
+ negative_prompt,
224
+ prompt_embeds,
225
+ negative_prompt_embeds,
226
+ num_frames,
227
+ ) -> None:
228
+ if not isinstance(prompt, (str, dict)):
229
+ raise ValueError(f"Expected `prompt` to have type `str` or `dict` but found {type(prompt)=}")
230
+
231
+ if negative_prompt is not None:
232
+ if not isinstance(negative_prompt, (str, dict)):
233
+ raise ValueError(
234
+ f"Expected `negative_prompt` to have type `str` or `dict` but found {type(negative_prompt)=}"
235
+ )
236
+
237
+ if prompt_embeds is not None or negative_prompt_embeds is not None:
238
+ raise ValueError("`prompt_embeds` and `negative_prompt_embeds` is not supported in FreeNoise yet.")
239
+
240
+ frame_indices = [isinstance(x, int) for x in prompt.keys()]
241
+ frame_prompts = [isinstance(x, str) for x in prompt.values()]
242
+ min_frame = min(list(prompt.keys()))
243
+ max_frame = max(list(prompt.keys()))
244
+
245
+ if not all(frame_indices):
246
+ raise ValueError("Expected integer keys in `prompt` dict for FreeNoise.")
247
+ if not all(frame_prompts):
248
+ raise ValueError("Expected str values in `prompt` dict for FreeNoise.")
249
+ if min_frame != 0:
250
+ raise ValueError("The minimum frame index in `prompt` dict must be 0 as a starting prompt is necessary.")
251
+ if max_frame >= num_frames:
252
+ raise ValueError(
253
+ f"The maximum frame index in `prompt` dict must be lesser than {num_frames=} and follow 0-based indexing."
254
+ )
255
+
256
+ def _encode_prompt_free_noise(
257
+ self,
258
+ prompt: str | dict[int, str],
259
+ num_frames: int,
260
+ device: torch.device,
261
+ num_videos_per_prompt: int,
262
+ do_classifier_free_guidance: bool,
263
+ negative_prompt: str | dict[int, str] | None = None,
264
+ prompt_embeds: torch.Tensor | None = None,
265
+ negative_prompt_embeds: torch.Tensor | None = None,
266
+ lora_scale: float | None = None,
267
+ clip_skip: int | None = None,
268
+ ) -> torch.Tensor:
269
+ if negative_prompt is None:
270
+ negative_prompt = ""
271
+
272
+ # Ensure that we have a dictionary of prompts
273
+ if isinstance(prompt, str):
274
+ prompt = {0: prompt}
275
+ if isinstance(negative_prompt, str):
276
+ negative_prompt = {0: negative_prompt}
277
+
278
+ self._check_inputs_free_noise(prompt, negative_prompt, prompt_embeds, negative_prompt_embeds, num_frames)
279
+
280
+ # Sort the prompts based on frame indices
281
+ prompt = dict(sorted(prompt.items()))
282
+ negative_prompt = dict(sorted(negative_prompt.items()))
283
+
284
+ # Ensure that we have a prompt for the last frame index
285
+ prompt[num_frames - 1] = prompt[list(prompt.keys())[-1]]
286
+ negative_prompt[num_frames - 1] = negative_prompt[list(negative_prompt.keys())[-1]]
287
+
288
+ frame_indices = list(prompt.keys())
289
+ frame_prompts = list(prompt.values())
290
+ frame_negative_indices = list(negative_prompt.keys())
291
+ frame_negative_prompts = list(negative_prompt.values())
292
+
293
+ # Generate and interpolate positive prompts
294
+ prompt_embeds, _ = self.encode_prompt(
295
+ prompt=frame_prompts,
296
+ device=device,
297
+ num_images_per_prompt=num_videos_per_prompt,
298
+ do_classifier_free_guidance=False,
299
+ negative_prompt=None,
300
+ prompt_embeds=None,
301
+ negative_prompt_embeds=None,
302
+ lora_scale=lora_scale,
303
+ clip_skip=clip_skip,
304
+ )
305
+
306
+ shape = (num_frames, *prompt_embeds.shape[1:])
307
+ prompt_interpolation_embeds = prompt_embeds.new_zeros(shape)
308
+
309
+ for i in range(len(frame_indices) - 1):
310
+ start_frame = frame_indices[i]
311
+ end_frame = frame_indices[i + 1]
312
+ start_tensor = prompt_embeds[i].unsqueeze(0)
313
+ end_tensor = prompt_embeds[i + 1].unsqueeze(0)
314
+
315
+ prompt_interpolation_embeds[start_frame : end_frame + 1] = self._free_noise_prompt_interpolation_callback(
316
+ start_frame, end_frame, start_tensor, end_tensor
317
+ )
318
+
319
+ # Generate and interpolate negative prompts
320
+ negative_prompt_embeds = None
321
+ negative_prompt_interpolation_embeds = None
322
+
323
+ if do_classifier_free_guidance:
324
+ _, negative_prompt_embeds = self.encode_prompt(
325
+ prompt=[""] * len(frame_negative_prompts),
326
+ device=device,
327
+ num_images_per_prompt=num_videos_per_prompt,
328
+ do_classifier_free_guidance=True,
329
+ negative_prompt=frame_negative_prompts,
330
+ prompt_embeds=None,
331
+ negative_prompt_embeds=None,
332
+ lora_scale=lora_scale,
333
+ clip_skip=clip_skip,
334
+ )
335
+
336
+ negative_prompt_interpolation_embeds = negative_prompt_embeds.new_zeros(shape)
337
+
338
+ for i in range(len(frame_negative_indices) - 1):
339
+ start_frame = frame_negative_indices[i]
340
+ end_frame = frame_negative_indices[i + 1]
341
+ start_tensor = negative_prompt_embeds[i].unsqueeze(0)
342
+ end_tensor = negative_prompt_embeds[i + 1].unsqueeze(0)
343
+
344
+ negative_prompt_interpolation_embeds[start_frame : end_frame + 1] = (
345
+ self._free_noise_prompt_interpolation_callback(start_frame, end_frame, start_tensor, end_tensor)
346
+ )
347
+
348
+ prompt_embeds = prompt_interpolation_embeds
349
+ negative_prompt_embeds = negative_prompt_interpolation_embeds
350
+
351
+ if do_classifier_free_guidance:
352
+ prompt_embeds = torch.cat([negative_prompt_embeds, prompt_embeds])
353
+
354
+ return prompt_embeds, negative_prompt_embeds
355
+
356
+ def _prepare_latents_free_noise(
357
+ self,
358
+ batch_size: int,
359
+ num_channels_latents: int,
360
+ num_frames: int,
361
+ height: int,
362
+ width: int,
363
+ dtype: torch.dtype,
364
+ device: torch.device,
365
+ generator: torch.Generator | None = None,
366
+ latents: torch.Tensor | None = None,
367
+ ):
368
+ if isinstance(generator, list) and len(generator) != batch_size:
369
+ raise ValueError(
370
+ f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
371
+ f" size of {batch_size}. Make sure the batch size matches the length of the generators."
372
+ )
373
+
374
+ context_num_frames = (
375
+ self._free_noise_context_length if self._free_noise_context_length == "repeat_context" else num_frames
376
+ )
377
+
378
+ shape = (
379
+ batch_size,
380
+ num_channels_latents,
381
+ context_num_frames,
382
+ height // self.vae_scale_factor,
383
+ width // self.vae_scale_factor,
384
+ )
385
+
386
+ if latents is None:
387
+ latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
388
+ if self._free_noise_noise_type == "random":
389
+ return latents
390
+ else:
391
+ if latents.size(2) == num_frames:
392
+ return latents
393
+ elif latents.size(2) != self._free_noise_context_length:
394
+ raise ValueError(
395
+ f"You have passed `latents` as a parameter to FreeNoise. The expected number of frames is either {num_frames} or {self._free_noise_context_length}, but found {latents.size(2)}"
396
+ )
397
+ latents = latents.to(device)
398
+
399
+ if self._free_noise_noise_type == "shuffle_context":
400
+ for i in range(self._free_noise_context_length, num_frames, self._free_noise_context_stride):
401
+ # ensure window is within bounds
402
+ window_start = max(0, i - self._free_noise_context_length)
403
+ window_end = min(num_frames, window_start + self._free_noise_context_stride)
404
+ window_length = window_end - window_start
405
+
406
+ if window_length == 0:
407
+ break
408
+
409
+ indices = torch.LongTensor(list(range(window_start, window_end)))
410
+ shuffled_indices = indices[torch.randperm(window_length, generator=generator)]
411
+
412
+ current_start = i
413
+ current_end = min(num_frames, current_start + window_length)
414
+ if current_end == current_start + window_length:
415
+ # batch of frames perfectly fits the window
416
+ latents[:, :, current_start:current_end] = latents[:, :, shuffled_indices]
417
+ else:
418
+ # handle the case where the last batch of frames does not fit perfectly with the window
419
+ prefix_length = current_end - current_start
420
+ shuffled_indices = shuffled_indices[:prefix_length]
421
+ latents[:, :, current_start:current_end] = latents[:, :, shuffled_indices]
422
+
423
+ elif self._free_noise_noise_type == "repeat_context":
424
+ num_repeats = (num_frames + self._free_noise_context_length - 1) // self._free_noise_context_length
425
+ latents = torch.cat([latents] * num_repeats, dim=2)
426
+
427
+ latents = latents[:, :, :num_frames]
428
+ return latents
429
+
430
+ def _lerp(
431
+ self, start_index: int, end_index: int, start_tensor: torch.Tensor, end_tensor: torch.Tensor
432
+ ) -> torch.Tensor:
433
+ num_indices = end_index - start_index + 1
434
+ interpolated_tensors = []
435
+
436
+ for i in range(num_indices):
437
+ alpha = i / (num_indices - 1)
438
+ interpolated_tensor = (1 - alpha) * start_tensor + alpha * end_tensor
439
+ interpolated_tensors.append(interpolated_tensor)
440
+
441
+ interpolated_tensors = torch.cat(interpolated_tensors)
442
+ return interpolated_tensors
443
+
444
+ def enable_free_noise(
445
+ self,
446
+ context_length: int | None = 16,
447
+ context_stride: int = 4,
448
+ weighting_scheme: str = "pyramid",
449
+ noise_type: str = "shuffle_context",
450
+ prompt_interpolation_callback: Callable[
451
+ [DiffusionPipeline, int, int, torch.Tensor, torch.Tensor], torch.Tensor
452
+ ]
453
+ | None = None,
454
+ ) -> None:
455
+ r"""
456
+ Enable long video generation using FreeNoise.
457
+
458
+ Args:
459
+ context_length (`int`, defaults to `16`, *optional*):
460
+ The number of video frames to process at once. It's recommended to set this to the maximum frames the
461
+ Motion Adapter was trained with (usually 16/24/32). If `None`, the default value from the motion
462
+ adapter config is used.
463
+ context_stride (`int`, *optional*):
464
+ Long videos are generated by processing many frames. FreeNoise processes these frames in sliding
465
+ windows of size `context_length`. Context stride allows you to specify how many frames to skip between
466
+ each window. For example, a context length of 16 and context stride of 4 would process 24 frames as:
467
+ [0, 15], [4, 19], [8, 23] (0-based indexing)
468
+ weighting_scheme (`str`, defaults to `pyramid`):
469
+ Weighting scheme for averaging latents after accumulation in FreeNoise blocks. The following weighting
470
+ schemes are supported currently:
471
+ - "flat"
472
+ Performs weighting averaging with a flat weight pattern: [1, 1, 1, 1, 1].
473
+ - "pyramid"
474
+ Performs weighted averaging with a pyramid like weight pattern: [1, 2, 3, 2, 1].
475
+ - "delayed_reverse_sawtooth"
476
+ Performs weighted averaging with low weights for earlier frames and high-to-low weights for
477
+ later frames: [0.01, 0.01, 3, 2, 1].
478
+ noise_type (`str`, defaults to "shuffle_context"):
479
+ Must be one of ["shuffle_context", "repeat_context", "random"].
480
+ - "shuffle_context"
481
+ Shuffles a fixed batch of `context_length` latents to create a final latent of size
482
+ `num_frames`. This is usually the best setting for most generation scenarios. However, there
483
+ might be visible repetition noticeable in the kinds of motion/animation generated.
484
+ - "repeated_context"
485
+ Repeats a fixed batch of `context_length` latents to create a final latent of size
486
+ `num_frames`.
487
+ - "random"
488
+ The final latents are random without any repetition.
489
+ """
490
+
491
+ allowed_weighting_scheme = ["flat", "pyramid", "delayed_reverse_sawtooth"]
492
+ allowed_noise_type = ["shuffle_context", "repeat_context", "random"]
493
+
494
+ if context_length > self.motion_adapter.config.motion_max_seq_length:
495
+ logger.warning(
496
+ f"You have set {context_length=} which is greater than {self.motion_adapter.config.motion_max_seq_length=}. This can lead to bad generation results."
497
+ )
498
+ if weighting_scheme not in allowed_weighting_scheme:
499
+ raise ValueError(
500
+ f"The parameter `weighting_scheme` must be one of {allowed_weighting_scheme}, but got {weighting_scheme=}"
501
+ )
502
+ if noise_type not in allowed_noise_type:
503
+ raise ValueError(f"The parameter `noise_type` must be one of {allowed_noise_type}, but got {noise_type=}")
504
+
505
+ self._free_noise_context_length = context_length or self.motion_adapter.config.motion_max_seq_length
506
+ self._free_noise_context_stride = context_stride
507
+ self._free_noise_weighting_scheme = weighting_scheme
508
+ self._free_noise_noise_type = noise_type
509
+ self._free_noise_prompt_interpolation_callback = prompt_interpolation_callback or self._lerp
510
+
511
+ if hasattr(self.unet.mid_block, "motion_modules"):
512
+ blocks = [*self.unet.down_blocks, self.unet.mid_block, *self.unet.up_blocks]
513
+ else:
514
+ blocks = [*self.unet.down_blocks, *self.unet.up_blocks]
515
+
516
+ for block in blocks:
517
+ self._enable_free_noise_in_block(block)
518
+
519
+ def disable_free_noise(self) -> None:
520
+ r"""Disable the FreeNoise sampling mechanism."""
521
+ self._free_noise_context_length = None
522
+
523
+ if hasattr(self.unet.mid_block, "motion_modules"):
524
+ blocks = [*self.unet.down_blocks, self.unet.mid_block, *self.unet.up_blocks]
525
+ else:
526
+ blocks = [*self.unet.down_blocks, *self.unet.up_blocks]
527
+
528
+ blocks = [*self.unet.down_blocks, self.unet.mid_block, *self.unet.up_blocks]
529
+ for block in blocks:
530
+ self._disable_free_noise_in_block(block)
531
+
532
+ def _enable_split_inference_motion_modules_(
533
+ self, motion_modules: list[AnimateDiffTransformer3D], spatial_split_size: int
534
+ ) -> None:
535
+ for motion_module in motion_modules:
536
+ motion_module.proj_in = SplitInferenceModule(motion_module.proj_in, spatial_split_size, 0, ["input"])
537
+
538
+ for i in range(len(motion_module.transformer_blocks)):
539
+ motion_module.transformer_blocks[i] = SplitInferenceModule(
540
+ motion_module.transformer_blocks[i],
541
+ spatial_split_size,
542
+ 0,
543
+ ["hidden_states", "encoder_hidden_states"],
544
+ )
545
+
546
+ motion_module.proj_out = SplitInferenceModule(motion_module.proj_out, spatial_split_size, 0, ["input"])
547
+
548
+ def _enable_split_inference_attentions_(
549
+ self, attentions: list[Transformer2DModel], temporal_split_size: int
550
+ ) -> None:
551
+ for i in range(len(attentions)):
552
+ attentions[i] = SplitInferenceModule(
553
+ attentions[i], temporal_split_size, 0, ["hidden_states", "encoder_hidden_states"]
554
+ )
555
+
556
+ def _enable_split_inference_resnets_(self, resnets: list[ResnetBlock2D], temporal_split_size: int) -> None:
557
+ for i in range(len(resnets)):
558
+ resnets[i] = SplitInferenceModule(resnets[i], temporal_split_size, 0, ["input_tensor", "temb"])
559
+
560
+ def _enable_split_inference_samplers_(
561
+ self, samplers: list[Downsample2D] | list[Upsample2D], temporal_split_size: int
562
+ ) -> None:
563
+ for i in range(len(samplers)):
564
+ samplers[i] = SplitInferenceModule(samplers[i], temporal_split_size, 0, ["hidden_states"])
565
+
566
+ def enable_free_noise_split_inference(self, spatial_split_size: int = 256, temporal_split_size: int = 16) -> None:
567
+ r"""
568
+ Enable FreeNoise memory optimizations by utilizing
569
+ [`~diffusers.pipelines.free_noise_utils.SplitInferenceModule`] across different intermediate modeling blocks.
570
+
571
+ Args:
572
+ spatial_split_size (`int`, defaults to `256`):
573
+ The split size across spatial dimensions for internal blocks. This is used in facilitating split
574
+ inference across the effective batch dimension (`[B x H x W, F, C]`) of intermediate tensors in motion
575
+ modeling blocks.
576
+ temporal_split_size (`int`, defaults to `16`):
577
+ The split size across temporal dimensions for internal blocks. This is used in facilitating split
578
+ inference across the effective batch dimension (`[B x F, H x W, C]`) of intermediate tensors in spatial
579
+ attention, resnets, downsampling and upsampling blocks.
580
+ """
581
+ # TODO(aryan): Discuss on what's the best way to provide more control to users
582
+ blocks = [*self.unet.down_blocks, self.unet.mid_block, *self.unet.up_blocks]
583
+ for block in blocks:
584
+ if getattr(block, "motion_modules", None) is not None:
585
+ self._enable_split_inference_motion_modules_(block.motion_modules, spatial_split_size)
586
+ if getattr(block, "attentions", None) is not None:
587
+ self._enable_split_inference_attentions_(block.attentions, temporal_split_size)
588
+ if getattr(block, "resnets", None) is not None:
589
+ self._enable_split_inference_resnets_(block.resnets, temporal_split_size)
590
+ if getattr(block, "downsamplers", None) is not None:
591
+ self._enable_split_inference_samplers_(block.downsamplers, temporal_split_size)
592
+ if getattr(block, "upsamplers", None) is not None:
593
+ self._enable_split_inference_samplers_(block.upsamplers, temporal_split_size)
594
+
595
+ @property
596
+ def free_noise_enabled(self):
597
+ return hasattr(self, "_free_noise_context_length") and self._free_noise_context_length is not None
third_party/diffusers/src/diffusers/pipelines/glm_image/__init__.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import TYPE_CHECKING
2
+
3
+ from ...utils import (
4
+ DIFFUSERS_SLOW_IMPORT,
5
+ OptionalDependencyNotAvailable,
6
+ _LazyModule,
7
+ get_objects_from_module,
8
+ is_torch_available,
9
+ is_transformers_available,
10
+ is_transformers_version,
11
+ )
12
+
13
+
14
+ _dummy_objects = {}
15
+ _additional_imports = {}
16
+ _import_structure = {"pipeline_output": ["GlmImagePipelineOutput"]}
17
+
18
+ # Import transformers components so they can be resolved during pipeline loading
19
+
20
+ if is_transformers_available() and is_transformers_version(">=", "4.57.4"):
21
+ try:
22
+ from transformers import GlmImageForConditionalGeneration, GlmImageProcessor
23
+
24
+ _additional_imports["GlmImageForConditionalGeneration"] = GlmImageForConditionalGeneration
25
+ _additional_imports["GlmImageProcessor"] = GlmImageProcessor
26
+ except ImportError:
27
+ pass
28
+
29
+ try:
30
+ if not (is_transformers_available() and is_torch_available()):
31
+ raise OptionalDependencyNotAvailable()
32
+ except OptionalDependencyNotAvailable:
33
+ from ...utils import dummy_torch_and_transformers_objects # noqa F403
34
+
35
+ _dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
36
+ else:
37
+ _import_structure["pipeline_glm_image"] = ["GlmImagePipeline"]
38
+ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
39
+ try:
40
+ if not (is_transformers_available() and is_torch_available()):
41
+ raise OptionalDependencyNotAvailable()
42
+ except OptionalDependencyNotAvailable:
43
+ from ...utils.dummy_torch_and_transformers_objects import * # noqa F403
44
+ else:
45
+ from .pipeline_glm_image import GlmImagePipeline
46
+ else:
47
+ import sys
48
+
49
+ sys.modules[__name__] = _LazyModule(
50
+ __name__,
51
+ globals()["__file__"],
52
+ _import_structure,
53
+ module_spec=__spec__,
54
+ )
55
+
56
+ for name, value in _dummy_objects.items():
57
+ setattr(sys.modules[__name__], name, value)
58
+ for name, value in _additional_imports.items():
59
+ setattr(sys.modules[__name__], name, value)
third_party/diffusers/src/diffusers/pipelines/glm_image/pipeline_glm_image.py ADDED
@@ -0,0 +1,1049 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 The CogVideoX team, Tsinghua University & ZhipuAI and The HuggingFace Team.
2
+ # All rights reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import inspect
17
+ import re
18
+ from typing import Any, Callable
19
+
20
+ import numpy as np
21
+ import PIL
22
+ import torch
23
+ from transformers import ByT5Tokenizer, PreTrainedModel, ProcessorMixin, T5EncoderModel
24
+
25
+ from ...callbacks import MultiPipelineCallbacks, PipelineCallback
26
+ from ...image_processor import VaeImageProcessor
27
+ from ...models import AutoencoderKL, GlmImageTransformer2DModel
28
+ from ...models.transformers.transformer_glm_image import GlmImageKVCache
29
+ from ...pipelines.pipeline_utils import DiffusionPipeline
30
+ from ...schedulers import FlowMatchEulerDiscreteScheduler
31
+ from ...utils import is_torch_xla_available, is_transformers_version, logging, replace_example_docstring
32
+ from ...utils.torch_utils import randn_tensor
33
+ from .pipeline_output import GlmImagePipelineOutput
34
+
35
+
36
+ # Because it's not released in stable as of 13/01/2026. So this is just a proxy.
37
+ GlmImageProcessor = ProcessorMixin
38
+ GlmImageForConditionalGeneration = PreTrainedModel
39
+ if is_transformers_version(">=", "5.0.0.dev0"):
40
+ from transformers import GlmImageForConditionalGeneration, GlmImageProcessor
41
+
42
+
43
+ if is_torch_xla_available():
44
+ import torch_xla.core.xla_model as xm
45
+
46
+ XLA_AVAILABLE = True
47
+ else:
48
+ XLA_AVAILABLE = False
49
+
50
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
51
+
52
+ EXAMPLE_DOC_STRING = """
53
+ Examples:
54
+ ```python
55
+ >>> import torch
56
+ >>> from diffusers import GlmImagePipeline
57
+
58
+ >>> pipe = GlmImagePipeline.from_pretrained("zai-org/GLM-Image", torch_dtype=torch.bfloat16)
59
+ >>> pipe.to("cuda")
60
+
61
+ >>> prompt = "A photo of an astronaut riding a horse on mars"
62
+ >>> image = pipe(prompt).images[0]
63
+ >>> image.save("output.png")
64
+ ```
65
+ """
66
+
67
+
68
+ def calculate_shift(
69
+ image_seq_len,
70
+ base_seq_len: int = 256,
71
+ base_shift: float = 0.25,
72
+ max_shift: float = 0.75,
73
+ ) -> float:
74
+ m = (image_seq_len / base_seq_len) ** 0.5
75
+ mu = m * max_shift + base_shift
76
+ return mu
77
+
78
+
79
+ # Copied from diffusers.pipelines.cogview4.pipeline_cogview4.retrieve_timesteps
80
+ def retrieve_timesteps(
81
+ scheduler,
82
+ num_inference_steps: int | None = None,
83
+ device: str | torch.device | None = None,
84
+ timesteps: list[int] | None = None,
85
+ sigmas: list[float] | None = None,
86
+ **kwargs,
87
+ ):
88
+ r"""
89
+ Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
90
+ custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
91
+
92
+ Args:
93
+ scheduler (`SchedulerMixin`):
94
+ The scheduler to get timesteps from.
95
+ num_inference_steps (`int`):
96
+ The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
97
+ must be `None`.
98
+ device (`str` or `torch.device`, *optional*):
99
+ The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
100
+ timesteps (`list[int]`, *optional*):
101
+ Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
102
+ `num_inference_steps` and `sigmas` must be `None`.
103
+ sigmas (`list[float]`, *optional*):
104
+ Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
105
+ `num_inference_steps` and `timesteps` must be `None`.
106
+
107
+ Returns:
108
+ `tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
109
+ second element is the number of inference steps.
110
+ """
111
+ accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
112
+ accepts_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
113
+
114
+ if timesteps is not None and sigmas is not None:
115
+ if not accepts_timesteps and not accepts_sigmas:
116
+ raise ValueError(
117
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
118
+ f" timestep or sigma schedules. Please check whether you are using the correct scheduler."
119
+ )
120
+ scheduler.set_timesteps(timesteps=timesteps, sigmas=sigmas, device=device, **kwargs)
121
+ timesteps = scheduler.timesteps
122
+ num_inference_steps = len(timesteps)
123
+ elif timesteps is not None and sigmas is None:
124
+ if not accepts_timesteps:
125
+ raise ValueError(
126
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
127
+ f" timestep schedules. Please check whether you are using the correct scheduler."
128
+ )
129
+ scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
130
+ timesteps = scheduler.timesteps
131
+ num_inference_steps = len(timesteps)
132
+ elif timesteps is None and sigmas is not None:
133
+ if not accepts_sigmas:
134
+ raise ValueError(
135
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
136
+ f" sigmas schedules. Please check whether you are using the correct scheduler."
137
+ )
138
+ scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
139
+ timesteps = scheduler.timesteps
140
+ num_inference_steps = len(timesteps)
141
+ else:
142
+ scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
143
+ timesteps = scheduler.timesteps
144
+ return timesteps, num_inference_steps
145
+
146
+
147
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion_img2img.retrieve_latents
148
+ def retrieve_latents(
149
+ encoder_output: torch.Tensor, generator: torch.Generator | None = None, sample_mode: str = "sample"
150
+ ):
151
+ if hasattr(encoder_output, "latent_dist") and sample_mode == "sample":
152
+ return encoder_output.latent_dist.sample(generator)
153
+ elif hasattr(encoder_output, "latent_dist") and sample_mode == "argmax":
154
+ return encoder_output.latent_dist.mode()
155
+ elif hasattr(encoder_output, "latents"):
156
+ return encoder_output.latents
157
+ else:
158
+ raise AttributeError("Could not access latents of provided encoder_output")
159
+
160
+
161
+ class GlmImagePipeline(DiffusionPipeline):
162
+ r"""
163
+ Pipeline for text-to-image generation using GLM-Image.
164
+
165
+ This pipeline integrates both the AR (autoregressive) model for token generation and the DiT (diffusion
166
+ transformer) model for image decoding.
167
+
168
+ Args:
169
+ tokenizer (`PreTrainedTokenizer`):
170
+ Tokenizer for the text encoder.
171
+ processor (`AutoProcessor`):
172
+ Processor for the AR model to handle chat templates and tokenization.
173
+ text_encoder ([`T5EncoderModel`]):
174
+ Frozen text-encoder for glyph embeddings.
175
+ vision_language_encoder ([`GlmImageForConditionalGeneration`]):
176
+ The AR model that generates image tokens from text prompts.
177
+ vae ([`AutoencoderKL`]):
178
+ Variational Auto-Encoder (VAE) Model to encode and decode images to and from latent representations.
179
+ transformer ([`GlmImageTransformer2DModel`]):
180
+ A text conditioned transformer to denoise the encoded image latents (DiT).
181
+ scheduler ([`SchedulerMixin`]):
182
+ A scheduler to be used in combination with `transformer` to denoise the encoded image latents.
183
+ """
184
+
185
+ _optional_components = []
186
+ model_cpu_offload_seq = "vision_language_encoder->text_encoder->transformer->vae"
187
+ _callback_tensor_inputs = ["latents", "prompt_embeds"]
188
+
189
+ def __init__(
190
+ self,
191
+ tokenizer: ByT5Tokenizer,
192
+ processor: GlmImageProcessor,
193
+ text_encoder: T5EncoderModel,
194
+ vision_language_encoder: GlmImageForConditionalGeneration,
195
+ vae: AutoencoderKL,
196
+ transformer: GlmImageTransformer2DModel,
197
+ scheduler: FlowMatchEulerDiscreteScheduler,
198
+ ):
199
+ super().__init__()
200
+
201
+ self.register_modules(
202
+ tokenizer=tokenizer,
203
+ processor=processor,
204
+ text_encoder=text_encoder,
205
+ vision_language_encoder=vision_language_encoder,
206
+ vae=vae,
207
+ transformer=transformer,
208
+ scheduler=scheduler,
209
+ )
210
+ self.vae_scale_factor = 2 ** (len(self.vae.config.block_out_channels) - 1) if getattr(self, "vae", None) else 8
211
+ self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor)
212
+
213
+ self.default_sample_size = (
214
+ self.transformer.config.sample_size
215
+ if hasattr(self, "transformer")
216
+ and self.transformer is not None
217
+ and hasattr(self.transformer.config, "sample_size")
218
+ else 128
219
+ )
220
+
221
+ @staticmethod
222
+ def _compute_generation_params(
223
+ image_grid_thw,
224
+ is_text_to_image: bool,
225
+ ):
226
+ grid_sizes = []
227
+ grid_hw = []
228
+
229
+ for i in range(image_grid_thw.shape[0]):
230
+ t, h, w = image_grid_thw[i].tolist()
231
+ grid_sizes.append(int(h * w))
232
+ grid_hw.append((int(h), int(w)))
233
+
234
+ if not is_text_to_image:
235
+ max_new_tokens = grid_sizes[-1] + 1
236
+ large_image_start_offset = 0
237
+ target_grid_h, target_grid_w = grid_hw[-1]
238
+ else:
239
+ total_tokens = sum(grid_sizes)
240
+ max_new_tokens = total_tokens + 1
241
+ large_image_start_offset = sum(grid_sizes[1:])
242
+ target_grid_h, target_grid_w = grid_hw[0]
243
+ return max_new_tokens, large_image_start_offset, target_grid_h, target_grid_w
244
+
245
+ @staticmethod
246
+ def _extract_large_image_tokens(
247
+ outputs: torch.Tensor, input_length: int, large_image_start_offset: int, large_image_tokens: int
248
+ ) -> torch.Tensor:
249
+ generated_tokens = outputs[0][input_length:]
250
+ large_image_start = large_image_start_offset
251
+ large_image_end = large_image_start + large_image_tokens
252
+ return generated_tokens[large_image_start:large_image_end]
253
+
254
+ @staticmethod
255
+ def _upsample_token_ids(token_ids: torch.Tensor, token_h: int, token_w: int) -> torch.Tensor:
256
+ token_ids = token_ids.view(1, 1, token_h, token_w)
257
+ token_ids = torch.nn.functional.interpolate(token_ids.float(), scale_factor=2, mode="nearest").to(
258
+ dtype=torch.long
259
+ )
260
+ token_ids = token_ids.view(1, -1)
261
+ return token_ids
262
+
263
+ @staticmethod
264
+ def _validate_and_normalize_images(
265
+ image: list[PIL.Image.Image] | list[list[PIL.Image.Image]],
266
+ batch_size: int,
267
+ ) -> list[list[PIL.Image.Image]]:
268
+ """
269
+ Validate and normalize image inputs to List[List[PIL.Image]].
270
+
271
+ Rules:
272
+ - batch_size > 1: Only accepts List[List[PIL.Image]], each sublist must have equal length
273
+ - batch_size == 1: Accepts List[PIL.Image] for legacy compatibility (converted to [[img1, img2, ...]])
274
+ - Other formats raise ValueError
275
+
276
+ Args:
277
+ image: Input images in various formats
278
+ batch_size: Number of prompts in the batch
279
+
280
+ Returns:
281
+ Normalized images as List[List[PIL.Image]], or None if no images provided
282
+ """
283
+ if image is None or len(image) == 0:
284
+ return None
285
+
286
+ first_element = image[0]
287
+
288
+ if batch_size == 1:
289
+ # Legacy format: List[PIL.Image] -> [[img1, img2, ...]]
290
+ if not isinstance(first_element, (list, tuple)):
291
+ return [list(image)]
292
+ # Already in List[List[PIL.Image]] format
293
+ if len(image) != 1:
294
+ raise ValueError(
295
+ f"For batch_size=1 with List[List[PIL.Image]] format, expected 1 image list, got {len(image)}."
296
+ )
297
+ return [list(image[0])]
298
+
299
+ # batch_size > 1: must be List[List[PIL.Image]]
300
+ if not isinstance(first_element, (list, tuple)):
301
+ raise ValueError(
302
+ f"For batch_size > 1, images must be List[List[PIL.Image]] format. "
303
+ f"Got List[{type(first_element).__name__}] instead. "
304
+ f"Each prompt requires its own list of condition images."
305
+ )
306
+
307
+ if len(image) != batch_size:
308
+ raise ValueError(f"Number of image lists ({len(image)}) must match batch size ({batch_size}).")
309
+
310
+ # Validate homogeneous: all sublists must have same length
311
+ num_input_images_per_prompt = len(image[0])
312
+ for idx, imgs in enumerate(image):
313
+ if len(imgs) != num_input_images_per_prompt:
314
+ raise ValueError(
315
+ f"All prompts must have the same number of condition images. "
316
+ f"Prompt 0 has {num_input_images_per_prompt} images, but prompt {idx} has {len(imgs)} images."
317
+ )
318
+
319
+ return [list(imgs) for imgs in image]
320
+
321
+ def generate_prior_tokens(
322
+ self,
323
+ prompt: str | list[str],
324
+ height: int,
325
+ width: int,
326
+ image: list[list[PIL.Image.Image]] | None = None,
327
+ device: torch.device | None = None,
328
+ generator: torch.Generator | None = None,
329
+ ):
330
+ """
331
+ Generate prior tokens for the DiT model using the AR model.
332
+
333
+ Args:
334
+ prompt: Single prompt or list of prompts
335
+ height: Target image height
336
+ width: Target image width
337
+ image: Normalized image input as List[List[PIL.Image]]. Should be pre-validated
338
+ using _validate_and_normalize_images() before calling this method.
339
+ device: Target device
340
+ generator: Random generator for reproducibility
341
+
342
+ Returns:
343
+ Tuple of:
344
+ - prior_token_ids: Tensor of shape (batch_size, num_tokens) with upsampled prior tokens
345
+ - prior_token_image_ids_per_sample: List of tensors, one per sample. Each tensor contains
346
+ the upsampled prior token ids for all condition images in that sample. None for t2i.
347
+ - source_image_grid_thw_per_sample: List of tensors, one per sample. Each tensor has shape
348
+ (num_condition_images, 3) with upsampled grid info. None for t2i.
349
+ """
350
+ device = device or self._execution_device
351
+
352
+ # Normalize prompt to list format
353
+ prompt_list = [prompt] if isinstance(prompt, str) else prompt
354
+ batch_size = len(prompt_list)
355
+
356
+ # Image is already normalized by _validate_and_normalize_images(): None or List[List[PIL.Image]]
357
+ is_text_to_image = image is None
358
+ # Build messages for each sample in the batch
359
+ all_messages = []
360
+ for idx, p in enumerate(prompt_list):
361
+ content = []
362
+ if not is_text_to_image:
363
+ for img in image[idx]:
364
+ content.append({"type": "image", "image": img})
365
+ content.append({"type": "text", "text": p})
366
+ all_messages.append([{"role": "user", "content": content}])
367
+ # Process with the processor (supports batch with left padding)
368
+ inputs = self.processor.apply_chat_template(
369
+ all_messages,
370
+ tokenize=True,
371
+ padding=True if batch_size > 1 else False,
372
+ target_h=height,
373
+ target_w=width,
374
+ return_dict=True,
375
+ return_tensors="pt",
376
+ ).to(device)
377
+
378
+ image_grid_thw = inputs.get("image_grid_thw")
379
+ images_per_sample = inputs.get("images_per_sample")
380
+
381
+ # Determine number of condition images and grids per sample
382
+ num_condition_images = 0 if is_text_to_image else len(image[0])
383
+ if images_per_sample is not None:
384
+ num_grids_per_sample = images_per_sample[0].item()
385
+ else:
386
+ # Fallback for batch_size=1: total grids is for single sample
387
+ num_grids_per_sample = image_grid_thw.shape[0]
388
+
389
+ # Compute generation params (same for all samples in homogeneous batch)
390
+ first_sample_grids = image_grid_thw[:num_grids_per_sample]
391
+ max_new_tokens, large_image_offset, token_h, token_w = self._compute_generation_params(
392
+ image_grid_thw=first_sample_grids, is_text_to_image=is_text_to_image
393
+ )
394
+
395
+ # Generate source image tokens (prior_token_image_ids) for i2i mode
396
+ prior_token_image_ids = None
397
+ source_image_grid_thw = None
398
+ if not is_text_to_image:
399
+ # Extract source grids by selecting condition image indices (skip target grids)
400
+ # Grid order from processor: [s0_cond1, s0_cond2, ..., s0_target, s1_cond1, s1_cond2, ..., s1_target, ...]
401
+ # We need indices: [0, 1, ..., num_condition_images-1, num_grids_per_sample, num_grids_per_sample+1, ...]
402
+ source_indices = []
403
+ for sample_idx in range(batch_size):
404
+ base = sample_idx * num_grids_per_sample
405
+ source_indices.extend(range(base, base + num_condition_images))
406
+ source_grids = image_grid_thw[source_indices]
407
+
408
+ if len(source_grids) > 0:
409
+ prior_token_image_embed = self.vision_language_encoder.get_image_features(
410
+ inputs["pixel_values"], source_grids
411
+ ).pooler_output
412
+ prior_token_image_embed = torch.cat(prior_token_image_embed, dim=0)
413
+ prior_token_image_ids_d32 = self.vision_language_encoder.get_image_tokens(
414
+ prior_token_image_embed, source_grids
415
+ )
416
+ # Upsample each source image's prior tokens to match VAE/DiT resolution
417
+ split_sizes = source_grids.prod(dim=-1).tolist()
418
+ prior_ids_per_source = torch.split(prior_token_image_ids_d32, split_sizes)
419
+ upsampled_prior_ids = []
420
+ for i, prior_ids in enumerate(prior_ids_per_source):
421
+ t, h, w = source_grids[i].tolist()
422
+ upsampled = self._upsample_token_ids(prior_ids, int(h), int(w))
423
+ upsampled_prior_ids.append(upsampled.squeeze(0))
424
+ prior_token_image_ids = torch.cat(upsampled_prior_ids, dim=0)
425
+ # Upsample grid dimensions for later splitting
426
+ upsampled_grids = source_grids.clone()
427
+ upsampled_grids[:, 1] = upsampled_grids[:, 1] * 2
428
+ upsampled_grids[:, 2] = upsampled_grids[:, 2] * 2
429
+ source_image_grid_thw = upsampled_grids
430
+
431
+ # Generate with AR model
432
+ # Set torch random seed from generator for reproducibility
433
+ # (transformers generate() doesn't accept generator parameter)
434
+ if generator is not None:
435
+ seed = generator.initial_seed()
436
+ torch.manual_seed(seed)
437
+ if device is not None and device.type == "cuda":
438
+ torch.cuda.manual_seed(seed)
439
+ outputs = self.vision_language_encoder.generate(
440
+ **inputs,
441
+ max_new_tokens=max_new_tokens,
442
+ do_sample=True,
443
+ )
444
+
445
+ # Extract and upsample prior tokens for each sample
446
+ # For left-padded inputs, generated tokens start after the padded input sequence
447
+ all_prior_token_ids = []
448
+ max_input_length = inputs["input_ids"].shape[-1]
449
+ for idx in range(batch_size):
450
+ # For left-padded sequences, generated tokens start at max_input_length
451
+ # (padding is on the left, so all sequences end at the same position)
452
+ prior_token_ids_d32 = self._extract_large_image_tokens(
453
+ outputs[idx : idx + 1], max_input_length, large_image_offset, token_h * token_w
454
+ )
455
+ prior_token_ids = self._upsample_token_ids(prior_token_ids_d32, token_h, token_w)
456
+ all_prior_token_ids.append(prior_token_ids)
457
+ prior_token_ids = torch.cat(all_prior_token_ids, dim=0)
458
+
459
+ # Split prior_token_image_ids and source_image_grid_thw into per-sample lists for easier consumption
460
+ prior_token_image_ids_per_sample = None
461
+ source_image_grid_thw_per_sample = None
462
+ if prior_token_image_ids is not None and source_image_grid_thw is not None:
463
+ # Split grids: each sample has num_condition_images grids
464
+ source_image_grid_thw_per_sample = list(torch.split(source_image_grid_thw, num_condition_images))
465
+ # Split prior_token_image_ids: tokens per sample may vary due to different image sizes
466
+ tokens_per_image = source_image_grid_thw.prod(dim=-1).tolist()
467
+ tokens_per_sample = []
468
+ for i in range(batch_size):
469
+ start_idx = i * num_condition_images
470
+ end_idx = start_idx + num_condition_images
471
+ tokens_per_sample.append(sum(tokens_per_image[start_idx:end_idx]))
472
+ prior_token_image_ids_per_sample = list(torch.split(prior_token_image_ids, tokens_per_sample))
473
+
474
+ return prior_token_ids, prior_token_image_ids_per_sample, source_image_grid_thw_per_sample
475
+
476
+ def get_glyph_texts(self, prompt):
477
+ """Extract glyph texts from prompt(s). Returns a list of lists for batch processing."""
478
+ if isinstance(prompt, str):
479
+ prompt = [prompt]
480
+ all_ocr_texts = []
481
+ for p in prompt:
482
+ ocr_texts = (
483
+ re.findall(r"'([^']*)'", p)
484
+ + re.findall(r"\u201c([^\u201c\u201d]*)\u201d", p)
485
+ + re.findall(r'"([^"]*)"', p)
486
+ + re.findall(r"「([^「」]*)」", p)
487
+ )
488
+ all_ocr_texts.append(ocr_texts)
489
+ return all_ocr_texts
490
+
491
+ def _get_glyph_embeds(
492
+ self,
493
+ prompt: str | list[str] = None,
494
+ max_sequence_length: int = 2048,
495
+ device: torch.device | None = None,
496
+ dtype: torch.dtype | None = None,
497
+ ):
498
+ """Get glyph embeddings for each prompt in the batch."""
499
+ device = device or self._execution_device
500
+ dtype = dtype or self.text_encoder.dtype
501
+
502
+ # get_glyph_texts now returns a list of lists (one per prompt)
503
+ all_glyph_texts = self.get_glyph_texts(prompt)
504
+
505
+ all_glyph_embeds = []
506
+ for glyph_texts in all_glyph_texts:
507
+ if len(glyph_texts) == 0:
508
+ glyph_texts = [""]
509
+ input_ids = self.tokenizer(
510
+ glyph_texts,
511
+ max_length=max_sequence_length,
512
+ truncation=True,
513
+ ).input_ids
514
+ input_ids = [
515
+ [self.tokenizer.pad_token_id] * ((len(input_ids) + 1) % 2) + input_ids_ for input_ids_ in input_ids
516
+ ]
517
+ max_length = max(len(input_ids_) for input_ids_ in input_ids)
518
+ attention_mask = torch.tensor(
519
+ [[1] * len(input_ids_) + [0] * (max_length - len(input_ids_)) for input_ids_ in input_ids],
520
+ device=device,
521
+ )
522
+ input_ids = torch.tensor(
523
+ [
524
+ input_ids_ + [self.tokenizer.pad_token_id] * (max_length - len(input_ids_))
525
+ for input_ids_ in input_ids
526
+ ],
527
+ device=device,
528
+ )
529
+ outputs = self.text_encoder(input_ids, attention_mask=attention_mask)
530
+ glyph_embeds = outputs.last_hidden_state[attention_mask.bool()].unsqueeze(0)
531
+ all_glyph_embeds.append(glyph_embeds)
532
+
533
+ # Pad to same sequence length and stack (use left padding to match transformers)
534
+ max_seq_len = max(emb.size(1) for emb in all_glyph_embeds)
535
+ padded_embeds = []
536
+ for emb in all_glyph_embeds:
537
+ if emb.size(1) < max_seq_len:
538
+ pad = torch.zeros(emb.size(0), max_seq_len - emb.size(1), emb.size(2), device=device, dtype=emb.dtype)
539
+ emb = torch.cat([pad, emb], dim=1) # left padding
540
+ padded_embeds.append(emb)
541
+
542
+ glyph_embeds = torch.cat(padded_embeds, dim=0)
543
+ return glyph_embeds.to(device=device, dtype=dtype)
544
+
545
+ def encode_prompt(
546
+ self,
547
+ prompt: str | list[str],
548
+ do_classifier_free_guidance: bool = True,
549
+ num_images_per_prompt: int = 1,
550
+ prompt_embeds: torch.Tensor | None = None,
551
+ negative_prompt_embeds: torch.Tensor | None = None,
552
+ device: torch.device | None = None,
553
+ dtype: torch.dtype | None = None,
554
+ max_sequence_length: int = 2048,
555
+ ):
556
+ r"""
557
+ Encodes the prompt into text encoder hidden states.
558
+
559
+ Args:
560
+ prompt (`str` or `list[str]`, *optional*):
561
+ prompt to be encoded
562
+ do_classifier_free_guidance (`bool`, *optional*, defaults to `True`):
563
+ Whether to use classifier free guidance or not.
564
+ num_images_per_prompt (`int`, *optional*, defaults to 1):
565
+ Number of images that should be generated per prompt. torch device to place the resulting embeddings on
566
+ prompt_embeds (`torch.Tensor`, *optional*):
567
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
568
+ provided, text embeddings will be generated from `prompt` input argument.
569
+ device: (`torch.device`, *optional*):
570
+ torch device
571
+ dtype: (`torch.dtype`, *optional*):
572
+ torch dtype
573
+ max_sequence_length (`int`, defaults to `2048`):
574
+ Maximum sequence length in encoded prompt. Can be set to other values but may lead to poorer results.
575
+ """
576
+ device = device or self._execution_device
577
+
578
+ prompt = [prompt] if isinstance(prompt, str) else prompt
579
+ if prompt is not None:
580
+ batch_size = len(prompt)
581
+ else:
582
+ batch_size = prompt_embeds.shape[0]
583
+
584
+ if prompt_embeds is None:
585
+ prompt_embeds = self._get_glyph_embeds(prompt, max_sequence_length, device, dtype)
586
+
587
+ # Repeat embeddings for num_images_per_prompt
588
+ if num_images_per_prompt > 1:
589
+ prompt_embeds = prompt_embeds.repeat_interleave(num_images_per_prompt, dim=0)
590
+
591
+ # For GLM-Image, negative_prompt must be "" instead of None
592
+ if do_classifier_free_guidance and negative_prompt_embeds is None:
593
+ negative_prompt = ""
594
+ negative_prompt = batch_size * [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
595
+ negative_prompt_embeds = self._get_glyph_embeds(negative_prompt, max_sequence_length, device, dtype)
596
+
597
+ if num_images_per_prompt > 1:
598
+ negative_prompt_embeds = negative_prompt_embeds.repeat_interleave(num_images_per_prompt, dim=0)
599
+
600
+ return prompt_embeds, negative_prompt_embeds
601
+
602
+ def prepare_latents(self, batch_size, num_channels_latents, height, width, dtype, device, generator, latents=None):
603
+ if latents is not None:
604
+ return latents.to(device)
605
+
606
+ shape = (
607
+ batch_size,
608
+ num_channels_latents,
609
+ int(height) // self.vae_scale_factor,
610
+ int(width) // self.vae_scale_factor,
611
+ )
612
+ if isinstance(generator, list) and len(generator) != batch_size:
613
+ raise ValueError(
614
+ f"You have passed a list of generators of length {len(generator)}, but requested an effective batch"
615
+ f" size of {batch_size}. Make sure the batch size matches the length of the generators."
616
+ )
617
+ latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
618
+ return latents
619
+
620
+ def check_inputs(
621
+ self,
622
+ prompt,
623
+ height,
624
+ width,
625
+ callback_on_step_end_tensor_inputs,
626
+ prompt_embeds=None,
627
+ negative_prompt_embeds=None,
628
+ prior_token_ids=None,
629
+ prior_token_image_ids=None,
630
+ source_image_grid_thw=None,
631
+ image=None,
632
+ ):
633
+ if (
634
+ height is not None
635
+ and height % (self.vae_scale_factor * self.transformer.config.patch_size * 2) != 0
636
+ or width is not None
637
+ and width % (self.transformer.config.patch_size * 2) != 0
638
+ ):
639
+ # GLM-Image uses 32× downsampling, so the image dimensions must be multiples of 32.
640
+ raise ValueError(
641
+ f"`height` and `width` have to be divisible by {self.vae_scale_factor * 4} but are {height} and {width}."
642
+ )
643
+
644
+ if callback_on_step_end_tensor_inputs is not None and not all(
645
+ k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
646
+ ):
647
+ raise ValueError(
648
+ f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
649
+ )
650
+ if prompt is not None and prompt_embeds is not None:
651
+ raise ValueError(
652
+ f"Cannot forward both `prompt`: {prompt} and `prompt_embeds`: {prompt_embeds}. Please make sure to"
653
+ " only forward one of the two."
654
+ )
655
+ elif prompt is None and prompt_embeds is None:
656
+ raise ValueError(
657
+ "Provide either `prompt` or `prompt_embeds`. Cannot leave both `prompt` and `prompt_embeds` undefined."
658
+ )
659
+ elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
660
+ raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
661
+ if prompt is None and prior_token_ids is None:
662
+ raise ValueError(
663
+ "Provide either `prompt` or `prior_token_ids`. Cannot leave both `prompt` and `prior_token_ids` undefined."
664
+ )
665
+ if prompt_embeds is not None and negative_prompt_embeds is not None:
666
+ if prompt_embeds.shape != negative_prompt_embeds.shape:
667
+ raise ValueError(
668
+ "`prompt_embeds` and `negative_prompt_embeds` must have the same shape when passed directly, but"
669
+ f" got: `prompt_embeds` {prompt_embeds.shape} != `negative_prompt_embeds`"
670
+ f" {negative_prompt_embeds.shape}."
671
+ )
672
+ # Validate prior token inputs: for i2i mode, all three must be provided together
673
+ # For t2i mode, only prior_token_ids is needed (prior_token_image_ids and source_image_grid_thw should be None)
674
+ prior_image_inputs = [prior_token_image_ids, source_image_grid_thw]
675
+ num_prior_image_inputs = sum(x is not None for x in prior_image_inputs)
676
+ if num_prior_image_inputs > 0 and num_prior_image_inputs < len(prior_image_inputs):
677
+ raise ValueError(
678
+ "`prior_token_image_ids` and `source_image_grid_thw` must be provided together for i2i mode. "
679
+ f"Got prior_token_image_ids={prior_token_image_ids is not None}, "
680
+ f"source_image_grid_thw={source_image_grid_thw is not None}."
681
+ )
682
+ if num_prior_image_inputs > 0 and prior_token_ids is None:
683
+ raise ValueError(
684
+ "`prior_token_ids` must be provided when `prior_token_image_ids` and `source_image_grid_thw` are provided."
685
+ )
686
+ if num_prior_image_inputs > 0 and image is None:
687
+ raise ValueError(
688
+ "`image` must be provided when `prior_token_image_ids` and `source_image_grid_thw` are provided "
689
+ "for i2i mode, as the images are needed for VAE encoding to build the KV cache."
690
+ )
691
+
692
+ if prior_token_ids is not None and prompt_embeds is None and prompt is None:
693
+ raise ValueError("`prompt_embeds` or `prompt` must also be provided with `prior_token_ids`.")
694
+
695
+ @property
696
+ def guidance_scale(self):
697
+ return self._guidance_scale
698
+
699
+ @property
700
+ def do_classifier_free_guidance(self):
701
+ return self._guidance_scale > 1
702
+
703
+ @property
704
+ def num_timesteps(self):
705
+ return self._num_timesteps
706
+
707
+ @property
708
+ def attention_kwargs(self):
709
+ return self._attention_kwargs
710
+
711
+ @property
712
+ def current_timestep(self):
713
+ return self._current_timestep
714
+
715
+ @property
716
+ def interrupt(self):
717
+ return self._interrupt
718
+
719
+ @torch.no_grad()
720
+ @replace_example_docstring(EXAMPLE_DOC_STRING)
721
+ def __call__(
722
+ self,
723
+ prompt: str | list[str] | None = None,
724
+ image: torch.Tensor
725
+ | PIL.Image.Image
726
+ | np.ndarray
727
+ | list[torch.Tensor]
728
+ | list[PIL.Image.Image]
729
+ | list[np.ndarray]
730
+ | None = None,
731
+ height: int | None = None,
732
+ width: int | None = None,
733
+ num_inference_steps: int = 50,
734
+ timesteps: list[int] | None = None,
735
+ sigmas: list[float] | None = None,
736
+ guidance_scale: float = 1.5,
737
+ num_images_per_prompt: int = 1,
738
+ generator: torch.Generator | list[torch.Generator] | None = None,
739
+ latents: torch.Tensor | None = None,
740
+ prompt_embeds: torch.Tensor | None = None,
741
+ negative_prompt_embeds: torch.Tensor | None = None,
742
+ prior_token_ids: torch.Tensor | None = None,
743
+ prior_token_image_ids: list[torch.Tensor] | None = None,
744
+ source_image_grid_thw: list[torch.Tensor] | None = None,
745
+ crops_coords_top_left: tuple[int, int] = (0, 0),
746
+ output_type: str = "pil",
747
+ return_dict: bool = True,
748
+ attention_kwargs: dict[str, Any] | None = None,
749
+ callback_on_step_end: Callable[[int, int, dict], None]
750
+ | PipelineCallback
751
+ | MultiPipelineCallbacks
752
+ | None = None,
753
+ callback_on_step_end_tensor_inputs: list[str] = ["latents"],
754
+ max_sequence_length: int = 2048,
755
+ ) -> GlmImagePipelineOutput | tuple:
756
+ """
757
+ Function invoked when calling the pipeline for generation.
758
+
759
+ Args:
760
+ prompt (`str` or `list[str]`, *optional*):
761
+ The prompt or prompts to guide the image generation. Must contain shape info in the format '<sop>H
762
+ W<eop>' where H and W are token dimensions (d32). Example: "A beautiful sunset<sop>36 24<eop>"
763
+ generates a 1152x768 image.
764
+ image: Optional condition images for image-to-image generation.
765
+ height (`int`, *optional*):
766
+ The height in pixels. If not provided, derived from prompt shape info.
767
+ width (`int`, *optional*):
768
+ The width in pixels. If not provided, derived from prompt shape info.
769
+ num_inference_steps (`int`, *optional*, defaults to `50`):
770
+ The number of denoising steps for DiT.
771
+ guidance_scale (`float`, *optional*, defaults to `1.5`):
772
+ Guidance scale for classifier-free guidance.
773
+ num_images_per_prompt (`int`, *optional*, defaults to `1`):
774
+ The number of images to generate per prompt.
775
+ generator (`torch.Generator`, *optional*):
776
+ Random generator for reproducibility.
777
+ output_type (`str`, *optional*, defaults to `"pil"`):
778
+ Output format: "pil", "np", or "latent".
779
+
780
+ Examples:
781
+
782
+ Returns:
783
+ [`GlmImagePipelineOutput`] or `tuple`: Generated images.
784
+ """
785
+
786
+ if isinstance(callback_on_step_end, (PipelineCallback, MultiPipelineCallbacks)):
787
+ callback_on_step_end_tensor_inputs = callback_on_step_end.tensor_inputs
788
+
789
+ # 1. Check inputs
790
+ self.check_inputs(
791
+ prompt,
792
+ height,
793
+ width,
794
+ callback_on_step_end_tensor_inputs,
795
+ prompt_embeds,
796
+ negative_prompt_embeds,
797
+ prior_token_ids,
798
+ prior_token_image_ids,
799
+ source_image_grid_thw,
800
+ image,
801
+ )
802
+ self._guidance_scale = guidance_scale
803
+ self._attention_kwargs = attention_kwargs
804
+ self._current_timestep = None
805
+ self._interrupt = False
806
+
807
+ if prompt is not None and isinstance(prompt, str):
808
+ batch_size = 1
809
+ elif prompt is not None and isinstance(prompt, list):
810
+ batch_size = len(prompt)
811
+ else:
812
+ batch_size = prompt_embeds.shape[0]
813
+
814
+ device = self._execution_device
815
+
816
+ # 2. Validate and normalize image format
817
+ normalized_image = self._validate_and_normalize_images(image, batch_size)
818
+
819
+ # 3. Generate prior tokens (batch mode)
820
+ # Get a single generator for AR model (use first if list provided)
821
+ ar_generator = generator[0] if isinstance(generator, list) else generator
822
+ if prior_token_ids is None:
823
+ prior_token_ids, prior_token_image_ids_per_sample, source_image_grid_thw_per_sample = (
824
+ self.generate_prior_tokens(
825
+ prompt=prompt,
826
+ image=normalized_image,
827
+ height=height,
828
+ width=width,
829
+ device=device,
830
+ generator=ar_generator,
831
+ )
832
+ )
833
+ else:
834
+ # User provided prior_token_ids directly (from generate_prior_tokens)
835
+ prior_token_image_ids_per_sample = prior_token_image_ids
836
+ source_image_grid_thw_per_sample = source_image_grid_thw
837
+
838
+ # 4. Preprocess images for VAE encoding
839
+ preprocessed_images = None
840
+ if normalized_image is not None:
841
+ preprocessed_images = []
842
+ for prompt_images in normalized_image:
843
+ prompt_preprocessed = []
844
+ for img in prompt_images:
845
+ image_height, image_width = img.size[::-1] if isinstance(img, PIL.Image.Image) else img.shape[:2]
846
+ multiple_of = self.vae_scale_factor * self.transformer.config.patch_size
847
+ image_height = (image_height // multiple_of) * multiple_of
848
+ image_width = (image_width // multiple_of) * multiple_of
849
+ img = self.image_processor.preprocess(img, height=image_height, width=image_width)
850
+ prompt_preprocessed.append(img)
851
+ height = height or image_height
852
+ width = width or image_width
853
+ preprocessed_images.append(prompt_preprocessed)
854
+
855
+ # 5. Encode input prompt
856
+ prompt_embeds, negative_prompt_embeds = self.encode_prompt(
857
+ prompt,
858
+ self.do_classifier_free_guidance,
859
+ num_images_per_prompt=num_images_per_prompt,
860
+ prompt_embeds=prompt_embeds,
861
+ negative_prompt_embeds=negative_prompt_embeds,
862
+ max_sequence_length=max_sequence_length,
863
+ device=device,
864
+ dtype=self.dtype,
865
+ )
866
+
867
+ # 6. Prepare latents and (optional) image kv cache
868
+ latent_channels = self.transformer.config.in_channels
869
+ latents = self.prepare_latents(
870
+ batch_size=batch_size * num_images_per_prompt,
871
+ num_channels_latents=latent_channels,
872
+ height=height,
873
+ width=width,
874
+ dtype=prompt_embeds.dtype,
875
+ device=device,
876
+ generator=generator,
877
+ latents=latents,
878
+ )
879
+ kv_caches = GlmImageKVCache(num_layers=self.transformer.config.num_layers)
880
+
881
+ if normalized_image is not None:
882
+ kv_caches.set_mode("write")
883
+ latents_mean = torch.tensor(self.vae.config.latents_mean).view(1, self.vae.config.latent_channels, 1, 1)
884
+ latents_std = torch.tensor(self.vae.config.latents_std).view(1, self.vae.config.latent_channels, 1, 1)
885
+
886
+ latents_mean = latents_mean.to(device=device, dtype=prompt_embeds.dtype)
887
+ latents_std = latents_std.to(device=device, dtype=prompt_embeds.dtype)
888
+
889
+ # Process each sample's condition images
890
+ for prompt_idx in range(batch_size):
891
+ prompt_images = preprocessed_images[prompt_idx]
892
+ prompt_prior_ids = prior_token_image_ids_per_sample[prompt_idx]
893
+ prompt_grid_thw = source_image_grid_thw_per_sample[prompt_idx]
894
+
895
+ # Split this sample's prior_token_image_ids by each image's token count
896
+ split_sizes = prompt_grid_thw.prod(dim=-1).tolist()
897
+ prior_ids_per_image = torch.split(prompt_prior_ids, split_sizes)
898
+ # Process each condition image for this sample
899
+ for condition_image, condition_image_prior_token_id in zip(prompt_images, prior_ids_per_image):
900
+ condition_image = condition_image.to(device=device, dtype=prompt_embeds.dtype)
901
+ condition_latent = retrieve_latents(
902
+ self.vae.encode(condition_image), generator=generator, sample_mode="argmax"
903
+ )
904
+ condition_latent = (condition_latent - latents_mean) / latents_std
905
+
906
+ _ = self.transformer(
907
+ hidden_states=condition_latent,
908
+ encoder_hidden_states=torch.zeros_like(prompt_embeds)[:1, :0, ...],
909
+ prior_token_id=condition_image_prior_token_id,
910
+ prior_token_drop=torch.full_like(condition_image_prior_token_id, False, dtype=torch.bool),
911
+ timestep=torch.zeros((1,), device=device),
912
+ target_size=torch.tensor([condition_image.shape[-2:]], device=device),
913
+ crop_coords=torch.zeros((1, 2), device=device),
914
+ attention_kwargs=attention_kwargs,
915
+ kv_caches=kv_caches,
916
+ )
917
+ # Move to next sample's cache slot
918
+ kv_caches.next_sample()
919
+
920
+ # 7. Prepare additional timestep conditions
921
+ target_size = (height, width)
922
+ target_size = torch.tensor([target_size], dtype=prompt_embeds.dtype, device=device)
923
+ crops_coords_top_left = torch.tensor([crops_coords_top_left], dtype=prompt_embeds.dtype, device=device)
924
+
925
+ target_size = target_size.repeat(batch_size * num_images_per_prompt, 1)
926
+ crops_coords_top_left = crops_coords_top_left.repeat(batch_size * num_images_per_prompt, 1)
927
+
928
+ # Prepare timesteps
929
+ image_seq_len = ((height // self.vae_scale_factor) * (width // self.vae_scale_factor)) // (
930
+ self.transformer.config.patch_size**2
931
+ )
932
+ timesteps = (
933
+ np.linspace(self.scheduler.config.num_train_timesteps, 1.0, num_inference_steps + 1)[:-1]
934
+ if timesteps is None
935
+ else np.array(timesteps)
936
+ )
937
+ timesteps = timesteps.astype(np.int64).astype(np.float32)
938
+ sigmas = timesteps / self.scheduler.config.num_train_timesteps if sigmas is None else sigmas
939
+ mu = calculate_shift(
940
+ image_seq_len,
941
+ self.scheduler.config.get("base_image_seq_len", 256),
942
+ self.scheduler.config.get("base_shift", 0.25),
943
+ self.scheduler.config.get("max_shift", 0.75),
944
+ )
945
+ timesteps, num_inference_steps = retrieve_timesteps(
946
+ self.scheduler, num_inference_steps, device, timesteps, sigmas, mu=mu
947
+ )
948
+ self._num_timesteps = len(timesteps)
949
+
950
+ # 8. Denoising loop
951
+ transformer_dtype = self.transformer.dtype
952
+ num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
953
+
954
+ # Repeat prior_token_ids for num_images_per_prompt
955
+ if num_images_per_prompt > 1:
956
+ prior_token_ids = prior_token_ids.repeat_interleave(num_images_per_prompt, dim=0)
957
+ prior_token_drop_cond = torch.full_like(prior_token_ids, False, dtype=torch.bool)
958
+ prior_token_drop_uncond = torch.full_like(prior_token_ids, True, dtype=torch.bool)
959
+ with self.progress_bar(total=num_inference_steps) as progress_bar:
960
+ for i, t in enumerate(timesteps):
961
+ if self.interrupt:
962
+ continue
963
+
964
+ self._current_timestep = t
965
+ latent_model_input = latents.to(transformer_dtype)
966
+
967
+ timestep = t.expand(latents.shape[0]) - 1
968
+
969
+ if prior_token_image_ids_per_sample is not None:
970
+ kv_caches.set_mode("read")
971
+
972
+ noise_pred_cond = self.transformer(
973
+ hidden_states=latent_model_input,
974
+ encoder_hidden_states=prompt_embeds,
975
+ prior_token_id=prior_token_ids,
976
+ prior_token_drop=prior_token_drop_cond,
977
+ timestep=timestep,
978
+ target_size=target_size,
979
+ crop_coords=crops_coords_top_left,
980
+ attention_kwargs=attention_kwargs,
981
+ return_dict=False,
982
+ kv_caches=kv_caches,
983
+ )[0].float()
984
+
985
+ # perform guidance
986
+ if self.do_classifier_free_guidance:
987
+ if prior_token_image_ids_per_sample is not None:
988
+ kv_caches.set_mode("skip")
989
+ noise_pred_uncond = self.transformer(
990
+ hidden_states=latent_model_input,
991
+ encoder_hidden_states=negative_prompt_embeds,
992
+ prior_token_id=prior_token_ids,
993
+ prior_token_drop=prior_token_drop_uncond,
994
+ timestep=timestep,
995
+ target_size=target_size,
996
+ crop_coords=crops_coords_top_left,
997
+ attention_kwargs=attention_kwargs,
998
+ return_dict=False,
999
+ kv_caches=kv_caches,
1000
+ )[0].float()
1001
+
1002
+ noise_pred = noise_pred_uncond + self.guidance_scale * (noise_pred_cond - noise_pred_uncond)
1003
+ else:
1004
+ noise_pred = noise_pred_cond
1005
+
1006
+ latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
1007
+
1008
+ if callback_on_step_end is not None:
1009
+ callback_kwargs = {}
1010
+ for k in callback_on_step_end_tensor_inputs:
1011
+ callback_kwargs[k] = locals()[k]
1012
+ callback_outputs = callback_on_step_end(self, i, self.scheduler.sigmas[i], callback_kwargs)
1013
+ latents = callback_outputs.pop("latents", latents)
1014
+ prompt_embeds = callback_outputs.pop("prompt_embeds", prompt_embeds)
1015
+
1016
+ if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
1017
+ progress_bar.update()
1018
+
1019
+ if XLA_AVAILABLE:
1020
+ xm.mark_step()
1021
+
1022
+ self._current_timestep = None
1023
+ kv_caches.clear()
1024
+
1025
+ if not output_type == "latent":
1026
+ latents = latents.to(self.vae.dtype)
1027
+ latents_mean = (
1028
+ torch.tensor(self.vae.config.latents_mean)
1029
+ .view(1, self.vae.config.latent_channels, 1, 1)
1030
+ .to(latents.device, latents.dtype)
1031
+ )
1032
+ latents_std = (
1033
+ torch.tensor(self.vae.config.latents_std)
1034
+ .view(1, self.vae.config.latent_channels, 1, 1)
1035
+ .to(latents.device, latents.dtype)
1036
+ )
1037
+ latents = latents * latents_std + latents_mean
1038
+ image = self.vae.decode(latents, return_dict=False, generator=generator)[0]
1039
+ image = self.image_processor.postprocess(image, output_type=output_type)
1040
+ else:
1041
+ image = latents
1042
+
1043
+ # Offload all models
1044
+ self.maybe_free_model_hooks()
1045
+
1046
+ if not return_dict:
1047
+ return (image,)
1048
+
1049
+ return GlmImagePipelineOutput(images=image)
third_party/diffusers/src/diffusers/pipelines/glm_image/pipeline_output.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass
2
+
3
+ import numpy as np
4
+ import PIL.Image
5
+
6
+ from ...utils import BaseOutput
7
+
8
+
9
+ @dataclass
10
+ class GlmImagePipelineOutput(BaseOutput):
11
+ """
12
+ Output class for CogView3 pipelines.
13
+
14
+ Args:
15
+ images (`List[PIL.Image.Image]` or `np.ndarray`)
16
+ List of denoised PIL images of length `batch_size` or numpy array of shape `(batch_size, height, width,
17
+ num_channels)`. PIL images or numpy array present the denoised images of the diffusion pipeline.
18
+ """
19
+
20
+ images: list[PIL.Image.Image] | np.ndarray
third_party/diffusers/src/diffusers/pipelines/hidream_image/__init__.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import TYPE_CHECKING
2
+
3
+ from ...utils import (
4
+ DIFFUSERS_SLOW_IMPORT,
5
+ OptionalDependencyNotAvailable,
6
+ _LazyModule,
7
+ get_objects_from_module,
8
+ is_torch_available,
9
+ is_transformers_available,
10
+ )
11
+
12
+
13
+ _dummy_objects = {}
14
+ _additional_imports = {}
15
+ _import_structure = {"pipeline_output": ["HiDreamImagePipelineOutput"]}
16
+
17
+ try:
18
+ if not (is_transformers_available() and is_torch_available()):
19
+ raise OptionalDependencyNotAvailable()
20
+ except OptionalDependencyNotAvailable:
21
+ from ...utils import dummy_torch_and_transformers_objects # noqa F403
22
+
23
+ _dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
24
+ else:
25
+ _import_structure["pipeline_hidream_image"] = ["HiDreamImagePipeline"]
26
+ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
27
+ try:
28
+ if not (is_transformers_available() and is_torch_available()):
29
+ raise OptionalDependencyNotAvailable()
30
+ except OptionalDependencyNotAvailable:
31
+ from ...utils.dummy_torch_and_transformers_objects import * # noqa F403
32
+ else:
33
+ from .pipeline_hidream_image import HiDreamImagePipeline
34
+ else:
35
+ import sys
36
+
37
+ sys.modules[__name__] = _LazyModule(
38
+ __name__,
39
+ globals()["__file__"],
40
+ _import_structure,
41
+ module_spec=__spec__,
42
+ )
43
+
44
+ for name, value in _dummy_objects.items():
45
+ setattr(sys.modules[__name__], name, value)
46
+ for name, value in _additional_imports.items():
47
+ setattr(sys.modules[__name__], name, value)
third_party/diffusers/src/diffusers/pipelines/hidream_image/pipeline_hidream_image.py ADDED
@@ -0,0 +1,1053 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 HiDream-ai Team and The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import inspect
16
+ import math
17
+ from typing import Any, Callable
18
+
19
+ import torch
20
+ from transformers import (
21
+ CLIPTextModelWithProjection,
22
+ CLIPTokenizer,
23
+ LlamaForCausalLM,
24
+ PreTrainedTokenizerFast,
25
+ T5EncoderModel,
26
+ T5Tokenizer,
27
+ )
28
+
29
+ from ...image_processor import VaeImageProcessor
30
+ from ...loaders import HiDreamImageLoraLoaderMixin
31
+ from ...models import AutoencoderKL, HiDreamImageTransformer2DModel
32
+ from ...schedulers import FlowMatchEulerDiscreteScheduler, UniPCMultistepScheduler
33
+ from ...utils import deprecate, is_torch_xla_available, logging, replace_example_docstring
34
+ from ...utils.torch_utils import randn_tensor
35
+ from ..pipeline_utils import DiffusionPipeline
36
+ from .pipeline_output import HiDreamImagePipelineOutput
37
+
38
+
39
+ if is_torch_xla_available():
40
+ import torch_xla.core.xla_model as xm
41
+
42
+ XLA_AVAILABLE = True
43
+ else:
44
+ XLA_AVAILABLE = False
45
+
46
+
47
+ logger = logging.get_logger(__name__) # pylint: disable=invalid-name
48
+
49
+ EXAMPLE_DOC_STRING = """
50
+ Examples:
51
+ ```py
52
+ >>> import torch
53
+ >>> from transformers import AutoTokenizer, LlamaForCausalLM
54
+ >>> from diffusers import HiDreamImagePipeline
55
+
56
+ >>> tokenizer_4 = AutoTokenizer.from_pretrained("meta-llama/Meta-Llama-3.1-8B-Instruct")
57
+ >>> text_encoder_4 = LlamaForCausalLM.from_pretrained(
58
+ ... "meta-llama/Meta-Llama-3.1-8B-Instruct",
59
+ ... output_hidden_states=True,
60
+ ... output_attentions=True,
61
+ ... torch_dtype=torch.bfloat16,
62
+ ... )
63
+
64
+ >>> pipe = HiDreamImagePipeline.from_pretrained(
65
+ ... "HiDream-ai/HiDream-I1-Full",
66
+ ... tokenizer_4=tokenizer_4,
67
+ ... text_encoder_4=text_encoder_4,
68
+ ... torch_dtype=torch.bfloat16,
69
+ ... )
70
+ >>> pipe.enable_model_cpu_offload()
71
+
72
+ >>> image = pipe(
73
+ ... 'A cat holding a sign that says "Hi-Dreams.ai".',
74
+ ... height=1024,
75
+ ... width=1024,
76
+ ... guidance_scale=5.0,
77
+ ... num_inference_steps=50,
78
+ ... generator=torch.Generator("cuda").manual_seed(0),
79
+ ... ).images[0]
80
+ >>> image.save("output.png")
81
+ ```
82
+ """
83
+
84
+
85
+ # Copied from diffusers.pipelines.flux.pipeline_flux.calculate_shift
86
+ def calculate_shift(
87
+ image_seq_len,
88
+ base_seq_len: int = 256,
89
+ max_seq_len: int = 4096,
90
+ base_shift: float = 0.5,
91
+ max_shift: float = 1.15,
92
+ ):
93
+ m = (max_shift - base_shift) / (max_seq_len - base_seq_len)
94
+ b = base_shift - m * base_seq_len
95
+ mu = image_seq_len * m + b
96
+ return mu
97
+
98
+
99
+ # Copied from diffusers.pipelines.stable_diffusion.pipeline_stable_diffusion.retrieve_timesteps
100
+ def retrieve_timesteps(
101
+ scheduler,
102
+ num_inference_steps: int | None = None,
103
+ device: str | torch.device | None = None,
104
+ timesteps: list[int] | None = None,
105
+ sigmas: list[float] | None = None,
106
+ **kwargs,
107
+ ):
108
+ r"""
109
+ Calls the scheduler's `set_timesteps` method and retrieves timesteps from the scheduler after the call. Handles
110
+ custom timesteps. Any kwargs will be supplied to `scheduler.set_timesteps`.
111
+
112
+ Args:
113
+ scheduler (`SchedulerMixin`):
114
+ The scheduler to get timesteps from.
115
+ num_inference_steps (`int`):
116
+ The number of diffusion steps used when generating samples with a pre-trained model. If used, `timesteps`
117
+ must be `None`.
118
+ device (`str` or `torch.device`, *optional*):
119
+ The device to which the timesteps should be moved to. If `None`, the timesteps are not moved.
120
+ timesteps (`list[int]`, *optional*):
121
+ Custom timesteps used to override the timestep spacing strategy of the scheduler. If `timesteps` is passed,
122
+ `num_inference_steps` and `sigmas` must be `None`.
123
+ sigmas (`list[float]`, *optional*):
124
+ Custom sigmas used to override the timestep spacing strategy of the scheduler. If `sigmas` is passed,
125
+ `num_inference_steps` and `timesteps` must be `None`.
126
+
127
+ Returns:
128
+ `tuple[torch.Tensor, int]`: A tuple where the first element is the timestep schedule from the scheduler and the
129
+ second element is the number of inference steps.
130
+ """
131
+ if timesteps is not None and sigmas is not None:
132
+ raise ValueError("Only one of `timesteps` or `sigmas` can be passed. Please choose one to set custom values")
133
+ if timesteps is not None:
134
+ accepts_timesteps = "timesteps" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
135
+ if not accepts_timesteps:
136
+ raise ValueError(
137
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
138
+ f" timestep schedules. Please check whether you are using the correct scheduler."
139
+ )
140
+ scheduler.set_timesteps(timesteps=timesteps, device=device, **kwargs)
141
+ timesteps = scheduler.timesteps
142
+ num_inference_steps = len(timesteps)
143
+ elif sigmas is not None:
144
+ accept_sigmas = "sigmas" in set(inspect.signature(scheduler.set_timesteps).parameters.keys())
145
+ if not accept_sigmas:
146
+ raise ValueError(
147
+ f"The current scheduler class {scheduler.__class__}'s `set_timesteps` does not support custom"
148
+ f" sigmas schedules. Please check whether you are using the correct scheduler."
149
+ )
150
+ scheduler.set_timesteps(sigmas=sigmas, device=device, **kwargs)
151
+ timesteps = scheduler.timesteps
152
+ num_inference_steps = len(timesteps)
153
+ else:
154
+ scheduler.set_timesteps(num_inference_steps, device=device, **kwargs)
155
+ timesteps = scheduler.timesteps
156
+ return timesteps, num_inference_steps
157
+
158
+
159
+ class HiDreamImagePipeline(DiffusionPipeline, HiDreamImageLoraLoaderMixin):
160
+ model_cpu_offload_seq = "text_encoder->text_encoder_2->text_encoder_3->text_encoder_4->transformer->vae"
161
+ _callback_tensor_inputs = ["latents", "prompt_embeds_t5", "prompt_embeds_llama3", "pooled_prompt_embeds"]
162
+
163
+ def __init__(
164
+ self,
165
+ scheduler: FlowMatchEulerDiscreteScheduler,
166
+ vae: AutoencoderKL,
167
+ text_encoder: CLIPTextModelWithProjection,
168
+ tokenizer: CLIPTokenizer,
169
+ text_encoder_2: CLIPTextModelWithProjection,
170
+ tokenizer_2: CLIPTokenizer,
171
+ text_encoder_3: T5EncoderModel,
172
+ tokenizer_3: T5Tokenizer,
173
+ text_encoder_4: LlamaForCausalLM,
174
+ tokenizer_4: PreTrainedTokenizerFast,
175
+ transformer: HiDreamImageTransformer2DModel,
176
+ ):
177
+ super().__init__()
178
+
179
+ self.register_modules(
180
+ vae=vae,
181
+ text_encoder=text_encoder,
182
+ text_encoder_2=text_encoder_2,
183
+ text_encoder_3=text_encoder_3,
184
+ text_encoder_4=text_encoder_4,
185
+ tokenizer=tokenizer,
186
+ tokenizer_2=tokenizer_2,
187
+ tokenizer_3=tokenizer_3,
188
+ tokenizer_4=tokenizer_4,
189
+ scheduler=scheduler,
190
+ transformer=transformer,
191
+ )
192
+ self.vae_scale_factor = (
193
+ 2 ** (len(self.vae.config.block_out_channels) - 1) if hasattr(self, "vae") and self.vae is not None else 8
194
+ )
195
+ # HiDreamImage latents are turned into 2x2 patches and packed. This means the latent width and height has to be divisible
196
+ # by the patch size. So the vae scale factor is multiplied by the patch size to account for this
197
+ self.image_processor = VaeImageProcessor(vae_scale_factor=self.vae_scale_factor * 2)
198
+ self.default_sample_size = 128
199
+ if getattr(self, "tokenizer_4", None) is not None:
200
+ self.tokenizer_4.pad_token = self.tokenizer_4.eos_token
201
+
202
+ def _get_t5_prompt_embeds(
203
+ self,
204
+ prompt: str | list[str] = None,
205
+ max_sequence_length: int = 128,
206
+ device: torch.device | None = None,
207
+ dtype: torch.dtype | None = None,
208
+ ):
209
+ device = device or self._execution_device
210
+ dtype = dtype or self.text_encoder_3.dtype
211
+
212
+ prompt = [prompt] if isinstance(prompt, str) else prompt
213
+
214
+ text_inputs = self.tokenizer_3(
215
+ prompt,
216
+ padding="max_length",
217
+ max_length=min(max_sequence_length, self.tokenizer_3.model_max_length),
218
+ truncation=True,
219
+ add_special_tokens=True,
220
+ return_tensors="pt",
221
+ )
222
+ text_input_ids = text_inputs.input_ids
223
+ attention_mask = text_inputs.attention_mask
224
+ untruncated_ids = self.tokenizer_3(prompt, padding="longest", return_tensors="pt").input_ids
225
+
226
+ if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
227
+ removed_text = self.tokenizer_3.batch_decode(
228
+ untruncated_ids[:, min(max_sequence_length, self.tokenizer_3.model_max_length) - 1 : -1]
229
+ )
230
+ logger.warning(
231
+ "The following part of your input was truncated because `max_sequence_length` is set to "
232
+ f" {min(max_sequence_length, self.tokenizer_3.model_max_length)} tokens: {removed_text}"
233
+ )
234
+
235
+ prompt_embeds = self.text_encoder_3(text_input_ids.to(device), attention_mask=attention_mask.to(device))[0]
236
+ prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
237
+ return prompt_embeds
238
+
239
+ def _get_clip_prompt_embeds(
240
+ self,
241
+ tokenizer,
242
+ text_encoder,
243
+ prompt: str | list[str],
244
+ max_sequence_length: int = 128,
245
+ device: torch.device | None = None,
246
+ dtype: torch.dtype | None = None,
247
+ ):
248
+ device = device or self._execution_device
249
+ dtype = dtype or text_encoder.dtype
250
+
251
+ prompt = [prompt] if isinstance(prompt, str) else prompt
252
+
253
+ text_inputs = tokenizer(
254
+ prompt,
255
+ padding="max_length",
256
+ max_length=min(max_sequence_length, 218),
257
+ truncation=True,
258
+ return_tensors="pt",
259
+ )
260
+
261
+ text_input_ids = text_inputs.input_ids
262
+ untruncated_ids = tokenizer(prompt, padding="longest", return_tensors="pt").input_ids
263
+ if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
264
+ removed_text = tokenizer.batch_decode(untruncated_ids[:, 218 - 1 : -1])
265
+ logger.warning(
266
+ "The following part of your input was truncated because CLIP can only handle sequences up to"
267
+ f" {218} tokens: {removed_text}"
268
+ )
269
+ prompt_embeds = text_encoder(text_input_ids.to(device), output_hidden_states=True)
270
+
271
+ # Use pooled output of CLIPTextModel
272
+ prompt_embeds = prompt_embeds[0]
273
+ prompt_embeds = prompt_embeds.to(dtype=dtype, device=device)
274
+ return prompt_embeds
275
+
276
+ def _get_llama3_prompt_embeds(
277
+ self,
278
+ prompt: str | list[str] = None,
279
+ max_sequence_length: int = 128,
280
+ device: torch.device | None = None,
281
+ dtype: torch.dtype | None = None,
282
+ ):
283
+ device = device or self._execution_device
284
+ dtype = dtype or self.text_encoder_4.dtype
285
+
286
+ prompt = [prompt] if isinstance(prompt, str) else prompt
287
+
288
+ text_inputs = self.tokenizer_4(
289
+ prompt,
290
+ padding="max_length",
291
+ max_length=min(max_sequence_length, self.tokenizer_4.model_max_length),
292
+ truncation=True,
293
+ add_special_tokens=True,
294
+ return_tensors="pt",
295
+ )
296
+ text_input_ids = text_inputs.input_ids
297
+ attention_mask = text_inputs.attention_mask
298
+ untruncated_ids = self.tokenizer_4(prompt, padding="longest", return_tensors="pt").input_ids
299
+
300
+ if untruncated_ids.shape[-1] >= text_input_ids.shape[-1] and not torch.equal(text_input_ids, untruncated_ids):
301
+ removed_text = self.tokenizer_4.batch_decode(
302
+ untruncated_ids[:, min(max_sequence_length, self.tokenizer_4.model_max_length) - 1 : -1]
303
+ )
304
+ logger.warning(
305
+ "The following part of your input was truncated because `max_sequence_length` is set to "
306
+ f" {min(max_sequence_length, self.tokenizer_4.model_max_length)} tokens: {removed_text}"
307
+ )
308
+
309
+ outputs = self.text_encoder_4(
310
+ text_input_ids.to(device),
311
+ attention_mask=attention_mask.to(device),
312
+ output_hidden_states=True,
313
+ output_attentions=True,
314
+ )
315
+
316
+ prompt_embeds = outputs.hidden_states[1:]
317
+ prompt_embeds = torch.stack(prompt_embeds, dim=0)
318
+ return prompt_embeds
319
+
320
+ def encode_prompt(
321
+ self,
322
+ prompt: str | list[str] | None = None,
323
+ prompt_2: str | list[str] | None = None,
324
+ prompt_3: str | list[str] | None = None,
325
+ prompt_4: str | list[str] | None = None,
326
+ device: torch.device | None = None,
327
+ dtype: torch.dtype | None = None,
328
+ num_images_per_prompt: int = 1,
329
+ do_classifier_free_guidance: bool = True,
330
+ negative_prompt: str | list[str] | None = None,
331
+ negative_prompt_2: str | list[str] | None = None,
332
+ negative_prompt_3: str | list[str] | None = None,
333
+ negative_prompt_4: str | list[str] | None = None,
334
+ prompt_embeds_t5: list[torch.FloatTensor] | None = None,
335
+ prompt_embeds_llama3: list[torch.FloatTensor] | None = None,
336
+ negative_prompt_embeds_t5: list[torch.FloatTensor] | None = None,
337
+ negative_prompt_embeds_llama3: list[torch.FloatTensor] | None = None,
338
+ pooled_prompt_embeds: torch.FloatTensor | None = None,
339
+ negative_pooled_prompt_embeds: torch.FloatTensor | None = None,
340
+ max_sequence_length: int = 128,
341
+ lora_scale: float | None = None,
342
+ ):
343
+ prompt = [prompt] if isinstance(prompt, str) else prompt
344
+ if prompt is not None:
345
+ batch_size = len(prompt)
346
+ else:
347
+ batch_size = pooled_prompt_embeds.shape[0]
348
+
349
+ device = device or self._execution_device
350
+
351
+ if pooled_prompt_embeds is None:
352
+ pooled_prompt_embeds_1 = self._get_clip_prompt_embeds(
353
+ self.tokenizer, self.text_encoder, prompt, max_sequence_length, device, dtype
354
+ )
355
+
356
+ if do_classifier_free_guidance and negative_pooled_prompt_embeds is None:
357
+ negative_prompt = negative_prompt or ""
358
+ negative_prompt = [negative_prompt] if isinstance(negative_prompt, str) else negative_prompt
359
+
360
+ if len(negative_prompt) > 1 and len(negative_prompt) != batch_size:
361
+ raise ValueError(f"negative_prompt must be of length 1 or {batch_size}")
362
+
363
+ negative_pooled_prompt_embeds_1 = self._get_clip_prompt_embeds(
364
+ self.tokenizer, self.text_encoder, negative_prompt, max_sequence_length, device, dtype
365
+ )
366
+
367
+ if negative_pooled_prompt_embeds_1.shape[0] == 1 and batch_size > 1:
368
+ negative_pooled_prompt_embeds_1 = negative_pooled_prompt_embeds_1.repeat(batch_size, 1)
369
+
370
+ if pooled_prompt_embeds is None:
371
+ prompt_2 = prompt_2 or prompt
372
+ prompt_2 = [prompt_2] if isinstance(prompt_2, str) else prompt_2
373
+
374
+ if len(prompt_2) > 1 and len(prompt_2) != batch_size:
375
+ raise ValueError(f"prompt_2 must be of length 1 or {batch_size}")
376
+
377
+ pooled_prompt_embeds_2 = self._get_clip_prompt_embeds(
378
+ self.tokenizer_2, self.text_encoder_2, prompt_2, max_sequence_length, device, dtype
379
+ )
380
+
381
+ if pooled_prompt_embeds_2.shape[0] == 1 and batch_size > 1:
382
+ pooled_prompt_embeds_2 = pooled_prompt_embeds_2.repeat(batch_size, 1)
383
+
384
+ if do_classifier_free_guidance and negative_pooled_prompt_embeds is None:
385
+ negative_prompt_2 = negative_prompt_2 or negative_prompt
386
+ negative_prompt_2 = [negative_prompt_2] if isinstance(negative_prompt_2, str) else negative_prompt_2
387
+
388
+ if len(negative_prompt_2) > 1 and len(negative_prompt_2) != batch_size:
389
+ raise ValueError(f"negative_prompt_2 must be of length 1 or {batch_size}")
390
+
391
+ negative_pooled_prompt_embeds_2 = self._get_clip_prompt_embeds(
392
+ self.tokenizer_2, self.text_encoder_2, negative_prompt_2, max_sequence_length, device, dtype
393
+ )
394
+
395
+ if negative_pooled_prompt_embeds_2.shape[0] == 1 and batch_size > 1:
396
+ negative_pooled_prompt_embeds_2 = negative_pooled_prompt_embeds_2.repeat(batch_size, 1)
397
+
398
+ if pooled_prompt_embeds is None:
399
+ pooled_prompt_embeds = torch.cat([pooled_prompt_embeds_1, pooled_prompt_embeds_2], dim=-1)
400
+
401
+ if do_classifier_free_guidance and negative_pooled_prompt_embeds is None:
402
+ negative_pooled_prompt_embeds = torch.cat(
403
+ [negative_pooled_prompt_embeds_1, negative_pooled_prompt_embeds_2], dim=-1
404
+ )
405
+
406
+ if prompt_embeds_t5 is None:
407
+ prompt_3 = prompt_3 or prompt
408
+ prompt_3 = [prompt_3] if isinstance(prompt_3, str) else prompt_3
409
+
410
+ if len(prompt_3) > 1 and len(prompt_3) != batch_size:
411
+ raise ValueError(f"prompt_3 must be of length 1 or {batch_size}")
412
+
413
+ prompt_embeds_t5 = self._get_t5_prompt_embeds(prompt_3, max_sequence_length, device, dtype)
414
+
415
+ if prompt_embeds_t5.shape[0] == 1 and batch_size > 1:
416
+ prompt_embeds_t5 = prompt_embeds_t5.repeat(batch_size, 1, 1)
417
+
418
+ if do_classifier_free_guidance and negative_prompt_embeds_t5 is None:
419
+ negative_prompt_3 = negative_prompt_3 or negative_prompt
420
+ negative_prompt_3 = [negative_prompt_3] if isinstance(negative_prompt_3, str) else negative_prompt_3
421
+
422
+ if len(negative_prompt_3) > 1 and len(negative_prompt_3) != batch_size:
423
+ raise ValueError(f"negative_prompt_3 must be of length 1 or {batch_size}")
424
+
425
+ negative_prompt_embeds_t5 = self._get_t5_prompt_embeds(
426
+ negative_prompt_3, max_sequence_length, device, dtype
427
+ )
428
+
429
+ if negative_prompt_embeds_t5.shape[0] == 1 and batch_size > 1:
430
+ negative_prompt_embeds_t5 = negative_prompt_embeds_t5.repeat(batch_size, 1, 1)
431
+
432
+ if prompt_embeds_llama3 is None:
433
+ prompt_4 = prompt_4 or prompt
434
+ prompt_4 = [prompt_4] if isinstance(prompt_4, str) else prompt_4
435
+
436
+ if len(prompt_4) > 1 and len(prompt_4) != batch_size:
437
+ raise ValueError(f"prompt_4 must be of length 1 or {batch_size}")
438
+
439
+ prompt_embeds_llama3 = self._get_llama3_prompt_embeds(prompt_4, max_sequence_length, device, dtype)
440
+
441
+ if prompt_embeds_llama3.shape[0] == 1 and batch_size > 1:
442
+ prompt_embeds_llama3 = prompt_embeds_llama3.repeat(1, batch_size, 1, 1)
443
+
444
+ if do_classifier_free_guidance and negative_prompt_embeds_llama3 is None:
445
+ negative_prompt_4 = negative_prompt_4 or negative_prompt
446
+ negative_prompt_4 = [negative_prompt_4] if isinstance(negative_prompt_4, str) else negative_prompt_4
447
+
448
+ if len(negative_prompt_4) > 1 and len(negative_prompt_4) != batch_size:
449
+ raise ValueError(f"negative_prompt_4 must be of length 1 or {batch_size}")
450
+
451
+ negative_prompt_embeds_llama3 = self._get_llama3_prompt_embeds(
452
+ negative_prompt_4, max_sequence_length, device, dtype
453
+ )
454
+
455
+ if negative_prompt_embeds_llama3.shape[0] == 1 and batch_size > 1:
456
+ negative_prompt_embeds_llama3 = negative_prompt_embeds_llama3.repeat(1, batch_size, 1, 1)
457
+
458
+ # duplicate pooled_prompt_embeds for each generation per prompt
459
+ pooled_prompt_embeds = pooled_prompt_embeds.repeat(1, num_images_per_prompt)
460
+ pooled_prompt_embeds = pooled_prompt_embeds.view(batch_size * num_images_per_prompt, -1)
461
+
462
+ # duplicate t5_prompt_embeds for batch_size and num_images_per_prompt
463
+ bs_embed, seq_len, _ = prompt_embeds_t5.shape
464
+ if bs_embed == 1 and batch_size > 1:
465
+ prompt_embeds_t5 = prompt_embeds_t5.repeat(batch_size, 1, 1)
466
+ elif bs_embed > 1 and bs_embed != batch_size:
467
+ raise ValueError(f"cannot duplicate prompt_embeds_t5 of batch size {bs_embed}")
468
+ prompt_embeds_t5 = prompt_embeds_t5.repeat(1, num_images_per_prompt, 1)
469
+ prompt_embeds_t5 = prompt_embeds_t5.view(batch_size * num_images_per_prompt, seq_len, -1)
470
+
471
+ # duplicate llama3_prompt_embeds for batch_size and num_images_per_prompt
472
+ _, bs_embed, seq_len, dim = prompt_embeds_llama3.shape
473
+ if bs_embed == 1 and batch_size > 1:
474
+ prompt_embeds_llama3 = prompt_embeds_llama3.repeat(1, batch_size, 1, 1)
475
+ elif bs_embed > 1 and bs_embed != batch_size:
476
+ raise ValueError(f"cannot duplicate prompt_embeds_llama3 of batch size {bs_embed}")
477
+ prompt_embeds_llama3 = prompt_embeds_llama3.repeat(1, 1, num_images_per_prompt, 1)
478
+ prompt_embeds_llama3 = prompt_embeds_llama3.view(-1, batch_size * num_images_per_prompt, seq_len, dim)
479
+
480
+ if do_classifier_free_guidance:
481
+ # duplicate negative_pooled_prompt_embeds for batch_size and num_images_per_prompt
482
+ bs_embed, seq_len = negative_pooled_prompt_embeds.shape
483
+ if bs_embed == 1 and batch_size > 1:
484
+ negative_pooled_prompt_embeds = negative_pooled_prompt_embeds.repeat(batch_size, 1)
485
+ elif bs_embed > 1 and bs_embed != batch_size:
486
+ raise ValueError(f"cannot duplicate negative_pooled_prompt_embeds of batch size {bs_embed}")
487
+ negative_pooled_prompt_embeds = negative_pooled_prompt_embeds.repeat(1, num_images_per_prompt)
488
+ negative_pooled_prompt_embeds = negative_pooled_prompt_embeds.view(batch_size * num_images_per_prompt, -1)
489
+
490
+ # duplicate negative_t5_prompt_embeds for batch_size and num_images_per_prompt
491
+ bs_embed, seq_len, _ = negative_prompt_embeds_t5.shape
492
+ if bs_embed == 1 and batch_size > 1:
493
+ negative_prompt_embeds_t5 = negative_prompt_embeds_t5.repeat(batch_size, 1, 1)
494
+ elif bs_embed > 1 and bs_embed != batch_size:
495
+ raise ValueError(f"cannot duplicate negative_prompt_embeds_t5 of batch size {bs_embed}")
496
+ negative_prompt_embeds_t5 = negative_prompt_embeds_t5.repeat(1, num_images_per_prompt, 1)
497
+ negative_prompt_embeds_t5 = negative_prompt_embeds_t5.view(batch_size * num_images_per_prompt, seq_len, -1)
498
+
499
+ # duplicate negative_prompt_embeds_llama3 for batch_size and num_images_per_prompt
500
+ _, bs_embed, seq_len, dim = negative_prompt_embeds_llama3.shape
501
+ if bs_embed == 1 and batch_size > 1:
502
+ negative_prompt_embeds_llama3 = negative_prompt_embeds_llama3.repeat(1, batch_size, 1, 1)
503
+ elif bs_embed > 1 and bs_embed != batch_size:
504
+ raise ValueError(f"cannot duplicate negative_prompt_embeds_llama3 of batch size {bs_embed}")
505
+ negative_prompt_embeds_llama3 = negative_prompt_embeds_llama3.repeat(1, 1, num_images_per_prompt, 1)
506
+ negative_prompt_embeds_llama3 = negative_prompt_embeds_llama3.view(
507
+ -1, batch_size * num_images_per_prompt, seq_len, dim
508
+ )
509
+
510
+ return (
511
+ prompt_embeds_t5,
512
+ negative_prompt_embeds_t5,
513
+ prompt_embeds_llama3,
514
+ negative_prompt_embeds_llama3,
515
+ pooled_prompt_embeds,
516
+ negative_pooled_prompt_embeds,
517
+ )
518
+
519
+ def enable_vae_slicing(self):
520
+ r"""
521
+ Enable sliced VAE decoding. When this option is enabled, the VAE will split the input tensor in slices to
522
+ compute decoding in several steps. This is useful to save some memory and allow larger batch sizes.
523
+ """
524
+ depr_message = f"Calling `enable_vae_slicing()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.enable_slicing()`."
525
+ deprecate(
526
+ "enable_vae_slicing",
527
+ "0.40.0",
528
+ depr_message,
529
+ )
530
+ self.vae.enable_slicing()
531
+
532
+ def disable_vae_slicing(self):
533
+ r"""
534
+ Disable sliced VAE decoding. If `enable_vae_slicing` was previously enabled, this method will go back to
535
+ computing decoding in one step.
536
+ """
537
+ depr_message = f"Calling `disable_vae_slicing()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.disable_slicing()`."
538
+ deprecate(
539
+ "disable_vae_slicing",
540
+ "0.40.0",
541
+ depr_message,
542
+ )
543
+ self.vae.disable_slicing()
544
+
545
+ def enable_vae_tiling(self):
546
+ r"""
547
+ Enable tiled VAE decoding. When this option is enabled, the VAE will split the input tensor into tiles to
548
+ compute decoding and encoding in several steps. This is useful for saving a large amount of memory and to allow
549
+ processing larger images.
550
+ """
551
+ depr_message = f"Calling `enable_vae_tiling()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.enable_tiling()`."
552
+ deprecate(
553
+ "enable_vae_tiling",
554
+ "0.40.0",
555
+ depr_message,
556
+ )
557
+ self.vae.enable_tiling()
558
+
559
+ def disable_vae_tiling(self):
560
+ r"""
561
+ Disable tiled VAE decoding. If `enable_vae_tiling` was previously enabled, this method will go back to
562
+ computing decoding in one step.
563
+ """
564
+ depr_message = f"Calling `disable_vae_tiling()` on a `{self.__class__.__name__}` is deprecated and this method will be removed in a future version. Please use `pipe.vae.disable_tiling()`."
565
+ deprecate(
566
+ "disable_vae_tiling",
567
+ "0.40.0",
568
+ depr_message,
569
+ )
570
+ self.vae.disable_tiling()
571
+
572
+ def check_inputs(
573
+ self,
574
+ prompt,
575
+ prompt_2,
576
+ prompt_3,
577
+ prompt_4,
578
+ negative_prompt=None,
579
+ negative_prompt_2=None,
580
+ negative_prompt_3=None,
581
+ negative_prompt_4=None,
582
+ prompt_embeds_t5=None,
583
+ prompt_embeds_llama3=None,
584
+ negative_prompt_embeds_t5=None,
585
+ negative_prompt_embeds_llama3=None,
586
+ pooled_prompt_embeds=None,
587
+ negative_pooled_prompt_embeds=None,
588
+ callback_on_step_end_tensor_inputs=None,
589
+ ):
590
+ if callback_on_step_end_tensor_inputs is not None and not all(
591
+ k in self._callback_tensor_inputs for k in callback_on_step_end_tensor_inputs
592
+ ):
593
+ raise ValueError(
594
+ f"`callback_on_step_end_tensor_inputs` has to be in {self._callback_tensor_inputs}, but found {[k for k in callback_on_step_end_tensor_inputs if k not in self._callback_tensor_inputs]}"
595
+ )
596
+
597
+ if prompt is not None and pooled_prompt_embeds is not None:
598
+ raise ValueError(
599
+ f"Cannot forward both `prompt`: {prompt} and `pooled_prompt_embeds`: {pooled_prompt_embeds}. Please make sure to"
600
+ " only forward one of the two."
601
+ )
602
+ elif prompt_2 is not None and pooled_prompt_embeds is not None:
603
+ raise ValueError(
604
+ f"Cannot forward both `prompt_2`: {prompt_2} and `pooled_prompt_embeds`: {pooled_prompt_embeds}. Please make sure to"
605
+ " only forward one of the two."
606
+ )
607
+ elif prompt_3 is not None and prompt_embeds_t5 is not None:
608
+ raise ValueError(
609
+ f"Cannot forward both `prompt_3`: {prompt_3} and `prompt_embeds_t5`: {prompt_embeds_t5}. Please make sure to"
610
+ " only forward one of the two."
611
+ )
612
+ elif prompt_4 is not None and prompt_embeds_llama3 is not None:
613
+ raise ValueError(
614
+ f"Cannot forward both `prompt_4`: {prompt_4} and `prompt_embeds_llama3`: {prompt_embeds_llama3}. Please make sure to"
615
+ " only forward one of the two."
616
+ )
617
+ elif prompt is None and pooled_prompt_embeds is None:
618
+ raise ValueError(
619
+ "Provide either `prompt` or `pooled_prompt_embeds`. Cannot leave both `prompt` and `pooled_prompt_embeds` undefined."
620
+ )
621
+ elif prompt is None and prompt_embeds_t5 is None:
622
+ raise ValueError(
623
+ "Provide either `prompt` or `prompt_embeds_t5`. Cannot leave both `prompt` and `prompt_embeds_t5` undefined."
624
+ )
625
+ elif prompt is None and prompt_embeds_llama3 is None:
626
+ raise ValueError(
627
+ "Provide either `prompt` or `prompt_embeds_llama3`. Cannot leave both `prompt` and `prompt_embeds_llama3` undefined."
628
+ )
629
+ elif prompt is not None and (not isinstance(prompt, str) and not isinstance(prompt, list)):
630
+ raise ValueError(f"`prompt` has to be of type `str` or `list` but is {type(prompt)}")
631
+ elif prompt_2 is not None and (not isinstance(prompt_2, str) and not isinstance(prompt_2, list)):
632
+ raise ValueError(f"`prompt_2` has to be of type `str` or `list` but is {type(prompt_2)}")
633
+ elif prompt_3 is not None and (not isinstance(prompt_3, str) and not isinstance(prompt_3, list)):
634
+ raise ValueError(f"`prompt_3` has to be of type `str` or `list` but is {type(prompt_3)}")
635
+ elif prompt_4 is not None and (not isinstance(prompt_4, str) and not isinstance(prompt_4, list)):
636
+ raise ValueError(f"`prompt_4` has to be of type `str` or `list` but is {type(prompt_4)}")
637
+
638
+ if negative_prompt is not None and negative_pooled_prompt_embeds is not None:
639
+ raise ValueError(
640
+ f"Cannot forward both `negative_prompt`: {negative_prompt} and `negative_pooled_prompt_embeds`:"
641
+ f" {negative_pooled_prompt_embeds}. Please make sure to only forward one of the two."
642
+ )
643
+ elif negative_prompt_2 is not None and negative_pooled_prompt_embeds is not None:
644
+ raise ValueError(
645
+ f"Cannot forward both `negative_prompt_2`: {negative_prompt_2} and `negative_pooled_prompt_embeds`:"
646
+ f" {negative_pooled_prompt_embeds}. Please make sure to only forward one of the two."
647
+ )
648
+ elif negative_prompt_3 is not None and negative_prompt_embeds_t5 is not None:
649
+ raise ValueError(
650
+ f"Cannot forward both `negative_prompt_3`: {negative_prompt_3} and `negative_prompt_embeds_t5`:"
651
+ f" {negative_prompt_embeds_t5}. Please make sure to only forward one of the two."
652
+ )
653
+ elif negative_prompt_4 is not None and negative_prompt_embeds_llama3 is not None:
654
+ raise ValueError(
655
+ f"Cannot forward both `negative_prompt_4`: {negative_prompt_4} and `negative_prompt_embeds_llama3`:"
656
+ f" {negative_prompt_embeds_llama3}. Please make sure to only forward one of the two."
657
+ )
658
+
659
+ if pooled_prompt_embeds is not None and negative_pooled_prompt_embeds is not None:
660
+ if pooled_prompt_embeds.shape != negative_pooled_prompt_embeds.shape:
661
+ raise ValueError(
662
+ "`pooled_prompt_embeds` and `negative_pooled_prompt_embeds` must have the same shape when passed directly, but"
663
+ f" got: `pooled_prompt_embeds` {pooled_prompt_embeds.shape} != `negative_pooled_prompt_embeds`"
664
+ f" {negative_pooled_prompt_embeds.shape}."
665
+ )
666
+ if prompt_embeds_t5 is not None and negative_prompt_embeds_t5 is not None:
667
+ if prompt_embeds_t5.shape != negative_prompt_embeds_t5.shape:
668
+ raise ValueError(
669
+ "`prompt_embeds_t5` and `negative_prompt_embeds_t5` must have the same shape when passed directly, but"
670
+ f" got: `prompt_embeds_t5` {prompt_embeds_t5.shape} != `negative_prompt_embeds_t5`"
671
+ f" {negative_prompt_embeds_t5.shape}."
672
+ )
673
+ if prompt_embeds_llama3 is not None and negative_prompt_embeds_llama3 is not None:
674
+ if prompt_embeds_llama3.shape != negative_prompt_embeds_llama3.shape:
675
+ raise ValueError(
676
+ "`prompt_embeds_llama3` and `negative_prompt_embeds_llama3` must have the same shape when passed directly, but"
677
+ f" got: `prompt_embeds_llama3` {prompt_embeds_llama3.shape} != `negative_prompt_embeds_llama3`"
678
+ f" {negative_prompt_embeds_llama3.shape}."
679
+ )
680
+
681
+ def prepare_latents(
682
+ self,
683
+ batch_size,
684
+ num_channels_latents,
685
+ height,
686
+ width,
687
+ dtype,
688
+ device,
689
+ generator,
690
+ latents=None,
691
+ ):
692
+ # VAE applies 8x compression on images but we must also account for packing which requires
693
+ # latent height and width to be divisible by 2.
694
+ height = 2 * (int(height) // (self.vae_scale_factor * 2))
695
+ width = 2 * (int(width) // (self.vae_scale_factor * 2))
696
+
697
+ shape = (batch_size, num_channels_latents, height, width)
698
+
699
+ if latents is None:
700
+ latents = randn_tensor(shape, generator=generator, device=device, dtype=dtype)
701
+ else:
702
+ if latents.shape != shape:
703
+ raise ValueError(f"Unexpected latents shape, got {latents.shape}, expected {shape}")
704
+ latents = latents.to(device)
705
+ return latents
706
+
707
+ @property
708
+ def guidance_scale(self):
709
+ return self._guidance_scale
710
+
711
+ @property
712
+ def do_classifier_free_guidance(self):
713
+ return self._guidance_scale > 1
714
+
715
+ @property
716
+ def attention_kwargs(self):
717
+ return self._attention_kwargs
718
+
719
+ @property
720
+ def num_timesteps(self):
721
+ return self._num_timesteps
722
+
723
+ @property
724
+ def interrupt(self):
725
+ return self._interrupt
726
+
727
+ @torch.no_grad()
728
+ @replace_example_docstring(EXAMPLE_DOC_STRING)
729
+ def __call__(
730
+ self,
731
+ prompt: str | list[str] = None,
732
+ prompt_2: str | list[str] | None = None,
733
+ prompt_3: str | list[str] | None = None,
734
+ prompt_4: str | list[str] | None = None,
735
+ height: int | None = None,
736
+ width: int | None = None,
737
+ num_inference_steps: int = 50,
738
+ sigmas: list[float] | None = None,
739
+ guidance_scale: float = 5.0,
740
+ negative_prompt: str | list[str] | None = None,
741
+ negative_prompt_2: str | list[str] | None = None,
742
+ negative_prompt_3: str | list[str] | None = None,
743
+ negative_prompt_4: str | list[str] | None = None,
744
+ num_images_per_prompt: int | None = 1,
745
+ generator: torch.Generator | list[torch.Generator] | None = None,
746
+ latents: torch.FloatTensor | None = None,
747
+ prompt_embeds_t5: torch.FloatTensor | None = None,
748
+ prompt_embeds_llama3: torch.FloatTensor | None = None,
749
+ negative_prompt_embeds_t5: torch.FloatTensor | None = None,
750
+ negative_prompt_embeds_llama3: torch.FloatTensor | None = None,
751
+ pooled_prompt_embeds: torch.FloatTensor | None = None,
752
+ negative_pooled_prompt_embeds: torch.FloatTensor | None = None,
753
+ output_type: str | None = "pil",
754
+ return_dict: bool = True,
755
+ attention_kwargs: dict[str, Any] | None = None,
756
+ callback_on_step_end: Callable[[int, int], None] | None = None,
757
+ callback_on_step_end_tensor_inputs: list[str] = ["latents"],
758
+ max_sequence_length: int = 128,
759
+ **kwargs,
760
+ ):
761
+ r"""
762
+ Function invoked when calling the pipeline for generation.
763
+
764
+ Args:
765
+ prompt (`str` or `list[str]`, *optional*):
766
+ The prompt or prompts to guide the image generation. If not defined, one has to pass `prompt_embeds`.
767
+ instead.
768
+ prompt_2 (`str` or `list[str]`, *optional*):
769
+ The prompt or prompts to be sent to `tokenizer_2` and `text_encoder_2`. If not defined, `prompt` is
770
+ will be used instead.
771
+ prompt_3 (`str` or `list[str]`, *optional*):
772
+ The prompt or prompts to be sent to `tokenizer_3` and `text_encoder_3`. If not defined, `prompt` is
773
+ will be used instead.
774
+ prompt_4 (`str` or `list[str]`, *optional*):
775
+ The prompt or prompts to be sent to `tokenizer_4` and `text_encoder_4`. If not defined, `prompt` is
776
+ will be used instead.
777
+ height (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
778
+ The height in pixels of the generated image. This is set to 1024 by default for the best results.
779
+ width (`int`, *optional*, defaults to self.unet.config.sample_size * self.vae_scale_factor):
780
+ The width in pixels of the generated image. This is set to 1024 by default for the best results.
781
+ num_inference_steps (`int`, *optional*, defaults to 50):
782
+ The number of denoising steps. More denoising steps usually lead to a higher quality image at the
783
+ expense of slower inference.
784
+ sigmas (`list[float]`, *optional*):
785
+ Custom sigmas to use for the denoising process with schedulers which support a `sigmas` argument in
786
+ their `set_timesteps` method. If not defined, the default behavior when `num_inference_steps` is passed
787
+ will be used.
788
+ guidance_scale (`float`, *optional*, defaults to 3.5):
789
+ Embedded guiddance scale is enabled by setting `guidance_scale` > 1. Higher `guidance_scale` encourages
790
+ a model to generate images more aligned with `prompt` at the expense of lower image quality.
791
+
792
+ Guidance-distilled models approximates true classifer-free guidance for `guidance_scale` > 1. Refer to
793
+ the [paper](https://huggingface.co/papers/2210.03142) to learn more.
794
+ negative_prompt (`str` or `list[str]`, *optional*):
795
+ The prompt or prompts not to guide the image generation. If not defined, one has to pass
796
+ `negative_prompt_embeds` instead. Ignored when not using guidance (i.e., ignored if `true_cfg_scale` is
797
+ not greater than `1`).
798
+ negative_prompt_2 (`str` or `list[str]`, *optional*):
799
+ The prompt or prompts not to guide the image generation to be sent to `tokenizer_2` and
800
+ `text_encoder_2`. If not defined, `negative_prompt` is used in all the text-encoders.
801
+ negative_prompt_3 (`str` or `list[str]`, *optional*):
802
+ The prompt or prompts not to guide the image generation to be sent to `tokenizer_3` and
803
+ `text_encoder_3`. If not defined, `negative_prompt` is used in all the text-encoders.
804
+ negative_prompt_4 (`str` or `list[str]`, *optional*):
805
+ The prompt or prompts not to guide the image generation to be sent to `tokenizer_4` and
806
+ `text_encoder_4`. If not defined, `negative_prompt` is used in all the text-encoders.
807
+ num_images_per_prompt (`int`, *optional*, defaults to 1):
808
+ The number of images to generate per prompt.
809
+ generator (`torch.Generator` or `list[torch.Generator]`, *optional*):
810
+ One or a list of [torch generator(s)](https://pytorch.org/docs/stable/generated/torch.Generator.html)
811
+ to make generation deterministic.
812
+ latents (`torch.FloatTensor`, *optional*):
813
+ Pre-generated noisy latents, sampled from a Gaussian distribution, to be used as inputs for image
814
+ generation. Can be used to tweak the same generation with different prompts. If not provided, a latents
815
+ tensor will be generated by sampling using the supplied random `generator`.
816
+ prompt_embeds (`torch.FloatTensor`, *optional*):
817
+ Pre-generated text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting. If not
818
+ provided, text embeddings will be generated from `prompt` input argument.
819
+ negative_prompt_embeds (`torch.FloatTensor`, *optional*):
820
+ Pre-generated negative text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
821
+ weighting. If not provided, negative_prompt_embeds will be generated from `negative_prompt` input
822
+ argument.
823
+ pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
824
+ Pre-generated pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt weighting.
825
+ If not provided, pooled text embeddings will be generated from `prompt` input argument.
826
+ negative_pooled_prompt_embeds (`torch.FloatTensor`, *optional*):
827
+ Pre-generated negative pooled text embeddings. Can be used to easily tweak text inputs, *e.g.* prompt
828
+ weighting. If not provided, pooled negative_prompt_embeds will be generated from `negative_prompt`
829
+ input argument.
830
+ output_type (`str`, *optional*, defaults to `"pil"`):
831
+ The output format of the generate image. Choose between
832
+ [PIL](https://pillow.readthedocs.io/en/stable/): `PIL.Image.Image` or `np.array`.
833
+ return_dict (`bool`, *optional*, defaults to `True`):
834
+ Whether or not to return a [`~pipelines.flux.FluxPipelineOutput`] instead of a plain tuple.
835
+ attention_kwargs (`dict`, *optional*):
836
+ A kwargs dictionary that if specified is passed along to the `AttentionProcessor` as defined under
837
+ `self.processor` in
838
+ [diffusers.models.attention_processor](https://github.com/huggingface/diffusers/blob/main/src/diffusers/models/attention_processor.py).
839
+ callback_on_step_end (`Callable`, *optional*):
840
+ A function that calls at the end of each denoising steps during the inference. The function is called
841
+ with the following arguments: `callback_on_step_end(self: DiffusionPipeline, step: int, timestep: int,
842
+ callback_kwargs: Dict)`. `callback_kwargs` will include a list of all tensors as specified by
843
+ `callback_on_step_end_tensor_inputs`.
844
+ callback_on_step_end_tensor_inputs (`list`, *optional*):
845
+ The list of tensor inputs for the `callback_on_step_end` function. The tensors specified in the list
846
+ will be passed as `callback_kwargs` argument. You will only be able to include variables listed in the
847
+ `._callback_tensor_inputs` attribute of your pipeline class.
848
+ max_sequence_length (`int` defaults to 128): Maximum sequence length to use with the `prompt`.
849
+
850
+ Examples:
851
+
852
+ Returns:
853
+ [`~pipelines.hidream_image.HiDreamImagePipelineOutput`] or `tuple`:
854
+ [`~pipelines.hidream_image.HiDreamImagePipelineOutput`] if `return_dict` is True, otherwise a `tuple`. When
855
+ returning a tuple, the first element is a list with the generated. images.
856
+ """
857
+
858
+ prompt_embeds = kwargs.get("prompt_embeds", None)
859
+ negative_prompt_embeds = kwargs.get("negative_prompt_embeds", None)
860
+
861
+ if prompt_embeds is not None:
862
+ deprecation_message = "The `prompt_embeds` argument is deprecated. Please use `prompt_embeds_t5` and `prompt_embeds_llama3` instead."
863
+ deprecate("prompt_embeds", "0.35.0", deprecation_message)
864
+ prompt_embeds_t5 = prompt_embeds[0]
865
+ prompt_embeds_llama3 = prompt_embeds[1]
866
+
867
+ if negative_prompt_embeds is not None:
868
+ deprecation_message = "The `negative_prompt_embeds` argument is deprecated. Please use `negative_prompt_embeds_t5` and `negative_prompt_embeds_llama3` instead."
869
+ deprecate("negative_prompt_embeds", "0.35.0", deprecation_message)
870
+ negative_prompt_embeds_t5 = negative_prompt_embeds[0]
871
+ negative_prompt_embeds_llama3 = negative_prompt_embeds[1]
872
+
873
+ height = height or self.default_sample_size * self.vae_scale_factor
874
+ width = width or self.default_sample_size * self.vae_scale_factor
875
+
876
+ division = self.vae_scale_factor * 2
877
+ S_max = (self.default_sample_size * self.vae_scale_factor) ** 2
878
+ scale = S_max / (width * height)
879
+ scale = math.sqrt(scale)
880
+ width, height = int(width * scale // division * division), int(height * scale // division * division)
881
+
882
+ # 1. Check inputs. Raise error if not correct
883
+ self.check_inputs(
884
+ prompt,
885
+ prompt_2,
886
+ prompt_3,
887
+ prompt_4,
888
+ negative_prompt=negative_prompt,
889
+ negative_prompt_2=negative_prompt_2,
890
+ negative_prompt_3=negative_prompt_3,
891
+ negative_prompt_4=negative_prompt_4,
892
+ prompt_embeds_t5=prompt_embeds_t5,
893
+ prompt_embeds_llama3=prompt_embeds_llama3,
894
+ negative_prompt_embeds_t5=negative_prompt_embeds_t5,
895
+ negative_prompt_embeds_llama3=negative_prompt_embeds_llama3,
896
+ pooled_prompt_embeds=pooled_prompt_embeds,
897
+ negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
898
+ callback_on_step_end_tensor_inputs=callback_on_step_end_tensor_inputs,
899
+ )
900
+
901
+ self._guidance_scale = guidance_scale
902
+ self._attention_kwargs = attention_kwargs
903
+ self._interrupt = False
904
+
905
+ # 2. Define call parameters
906
+ if prompt is not None and isinstance(prompt, str):
907
+ batch_size = 1
908
+ elif prompt is not None and isinstance(prompt, list):
909
+ batch_size = len(prompt)
910
+ elif pooled_prompt_embeds is not None:
911
+ batch_size = pooled_prompt_embeds.shape[0]
912
+
913
+ device = self._execution_device
914
+
915
+ # 3. Encode prompt
916
+ lora_scale = self.attention_kwargs.get("scale", None) if self.attention_kwargs is not None else None
917
+ (
918
+ prompt_embeds_t5,
919
+ negative_prompt_embeds_t5,
920
+ prompt_embeds_llama3,
921
+ negative_prompt_embeds_llama3,
922
+ pooled_prompt_embeds,
923
+ negative_pooled_prompt_embeds,
924
+ ) = self.encode_prompt(
925
+ prompt=prompt,
926
+ prompt_2=prompt_2,
927
+ prompt_3=prompt_3,
928
+ prompt_4=prompt_4,
929
+ negative_prompt=negative_prompt,
930
+ negative_prompt_2=negative_prompt_2,
931
+ negative_prompt_3=negative_prompt_3,
932
+ negative_prompt_4=negative_prompt_4,
933
+ do_classifier_free_guidance=self.do_classifier_free_guidance,
934
+ prompt_embeds_t5=prompt_embeds_t5,
935
+ prompt_embeds_llama3=prompt_embeds_llama3,
936
+ negative_prompt_embeds_t5=negative_prompt_embeds_t5,
937
+ negative_prompt_embeds_llama3=negative_prompt_embeds_llama3,
938
+ pooled_prompt_embeds=pooled_prompt_embeds,
939
+ negative_pooled_prompt_embeds=negative_pooled_prompt_embeds,
940
+ device=device,
941
+ num_images_per_prompt=num_images_per_prompt,
942
+ max_sequence_length=max_sequence_length,
943
+ lora_scale=lora_scale,
944
+ )
945
+
946
+ if self.do_classifier_free_guidance:
947
+ prompt_embeds_t5 = torch.cat([negative_prompt_embeds_t5, prompt_embeds_t5], dim=0)
948
+ prompt_embeds_llama3 = torch.cat([negative_prompt_embeds_llama3, prompt_embeds_llama3], dim=1)
949
+ pooled_prompt_embeds = torch.cat([negative_pooled_prompt_embeds, pooled_prompt_embeds], dim=0)
950
+
951
+ # 4. Prepare latent variables
952
+ num_channels_latents = self.transformer.config.in_channels
953
+ latents = self.prepare_latents(
954
+ batch_size * num_images_per_prompt,
955
+ num_channels_latents,
956
+ height,
957
+ width,
958
+ pooled_prompt_embeds.dtype,
959
+ device,
960
+ generator,
961
+ latents,
962
+ )
963
+
964
+ # 5. Prepare timesteps
965
+ mu = calculate_shift(self.transformer.max_seq)
966
+ scheduler_kwargs = {"mu": mu}
967
+ if XLA_AVAILABLE:
968
+ timestep_device = "cpu"
969
+ else:
970
+ timestep_device = device
971
+ if isinstance(self.scheduler, UniPCMultistepScheduler):
972
+ self.scheduler.set_timesteps(num_inference_steps, device=timestep_device) # , shift=math.exp(mu))
973
+ timesteps = self.scheduler.timesteps
974
+ else:
975
+ timesteps, num_inference_steps = retrieve_timesteps(
976
+ self.scheduler,
977
+ num_inference_steps,
978
+ timestep_device,
979
+ sigmas=sigmas,
980
+ **scheduler_kwargs,
981
+ )
982
+ num_warmup_steps = max(len(timesteps) - num_inference_steps * self.scheduler.order, 0)
983
+ self._num_timesteps = len(timesteps)
984
+
985
+ # 6. Denoising loop
986
+ with self.progress_bar(total=num_inference_steps) as progress_bar:
987
+ for i, t in enumerate(timesteps):
988
+ if self.interrupt:
989
+ continue
990
+
991
+ # expand the latents if we are doing classifier free guidance
992
+ latent_model_input = torch.cat([latents] * 2) if self.do_classifier_free_guidance else latents
993
+ # broadcast to batch dimension in a way that's compatible with ONNX/Core ML
994
+ timestep = t.expand(latent_model_input.shape[0])
995
+
996
+ noise_pred = self.transformer(
997
+ hidden_states=latent_model_input,
998
+ timesteps=timestep,
999
+ encoder_hidden_states_t5=prompt_embeds_t5,
1000
+ encoder_hidden_states_llama3=prompt_embeds_llama3,
1001
+ pooled_embeds=pooled_prompt_embeds,
1002
+ return_dict=False,
1003
+ )[0]
1004
+ noise_pred = -noise_pred
1005
+
1006
+ # perform guidance
1007
+ if self.do_classifier_free_guidance:
1008
+ noise_pred_uncond, noise_pred_text = noise_pred.chunk(2)
1009
+ noise_pred = noise_pred_uncond + self.guidance_scale * (noise_pred_text - noise_pred_uncond)
1010
+
1011
+ # compute the previous noisy sample x_t -> x_t-1
1012
+ latents_dtype = latents.dtype
1013
+ latents = self.scheduler.step(noise_pred, t, latents, return_dict=False)[0]
1014
+
1015
+ if latents.dtype != latents_dtype:
1016
+ if torch.backends.mps.is_available():
1017
+ # some platforms (eg. apple mps) misbehave due to a pytorch bug: https://github.com/pytorch/pytorch/pull/99272
1018
+ latents = latents.to(latents_dtype)
1019
+
1020
+ if callback_on_step_end is not None:
1021
+ callback_kwargs = {}
1022
+ for k in callback_on_step_end_tensor_inputs:
1023
+ callback_kwargs[k] = locals()[k]
1024
+ callback_outputs = callback_on_step_end(self, i, t, callback_kwargs)
1025
+
1026
+ latents = callback_outputs.pop("latents", latents)
1027
+ prompt_embeds_t5 = callback_outputs.pop("prompt_embeds_t5", prompt_embeds_t5)
1028
+ prompt_embeds_llama3 = callback_outputs.pop("prompt_embeds_llama3", prompt_embeds_llama3)
1029
+ pooled_prompt_embeds = callback_outputs.pop("pooled_prompt_embeds", pooled_prompt_embeds)
1030
+
1031
+ # call the callback, if provided
1032
+ if i == len(timesteps) - 1 or ((i + 1) > num_warmup_steps and (i + 1) % self.scheduler.order == 0):
1033
+ progress_bar.update()
1034
+
1035
+ if XLA_AVAILABLE:
1036
+ xm.mark_step()
1037
+
1038
+ if output_type == "latent":
1039
+ image = latents
1040
+
1041
+ else:
1042
+ latents = (latents / self.vae.config.scaling_factor) + self.vae.config.shift_factor
1043
+
1044
+ image = self.vae.decode(latents, return_dict=False)[0]
1045
+ image = self.image_processor.postprocess(image, output_type=output_type)
1046
+
1047
+ # Offload all models
1048
+ self.maybe_free_model_hooks()
1049
+
1050
+ if not return_dict:
1051
+ return (image,)
1052
+
1053
+ return HiDreamImagePipelineOutput(images=image)
third_party/diffusers/src/diffusers/pipelines/hidream_image/pipeline_output.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright 2025 HiDream-ai Team and The HuggingFace Team. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from dataclasses import dataclass
16
+
17
+ import numpy as np
18
+ import PIL.Image
19
+
20
+ from ...utils import BaseOutput
21
+
22
+
23
+ @dataclass
24
+ class HiDreamImagePipelineOutput(BaseOutput):
25
+ """
26
+ Output class for HiDreamImage pipelines.
27
+
28
+ Args:
29
+ images (`list[PIL.Image.Image]` or `np.ndarray`)
30
+ list of denoised PIL images of length `batch_size` or numpy array of shape `(batch_size, height, width,
31
+ num_channels)`. PIL images or numpy array present the denoised images of the diffusion pipeline.
32
+ """
33
+
34
+ images: list[PIL.Image.Image] | np.ndarray
third_party/diffusers/src/diffusers/pipelines/latte/__init__.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import TYPE_CHECKING
2
+
3
+ from ...utils import (
4
+ DIFFUSERS_SLOW_IMPORT,
5
+ OptionalDependencyNotAvailable,
6
+ _LazyModule,
7
+ get_objects_from_module,
8
+ is_torch_available,
9
+ is_transformers_available,
10
+ )
11
+
12
+
13
+ _dummy_objects = {}
14
+ _import_structure = {}
15
+
16
+
17
+ try:
18
+ if not (is_transformers_available() and is_torch_available()):
19
+ raise OptionalDependencyNotAvailable()
20
+ except OptionalDependencyNotAvailable:
21
+ from ...utils import dummy_torch_and_transformers_objects # noqa F403
22
+
23
+ _dummy_objects.update(get_objects_from_module(dummy_torch_and_transformers_objects))
24
+ else:
25
+ _import_structure["pipeline_latte"] = ["LattePipeline"]
26
+
27
+ if TYPE_CHECKING or DIFFUSERS_SLOW_IMPORT:
28
+ try:
29
+ if not (is_transformers_available() and is_torch_available()):
30
+ raise OptionalDependencyNotAvailable()
31
+
32
+ except OptionalDependencyNotAvailable:
33
+ from ...utils.dummy_torch_and_transformers_objects import *
34
+ else:
35
+ from .pipeline_latte import LattePipeline
36
+
37
+ else:
38
+ import sys
39
+
40
+ sys.modules[__name__] = _LazyModule(
41
+ __name__,
42
+ globals()["__file__"],
43
+ _import_structure,
44
+ module_spec=__spec__,
45
+ )
46
+
47
+ for name, value in _dummy_objects.items():
48
+ setattr(sys.modules[__name__], name, value)