diffsynth 2.1.1__tar.gz → 2.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (262) hide show
  1. {diffsynth-2.1.1 → diffsynth-2.1.2}/PKG-INFO +1 -1
  2. {diffsynth-2.1.1 → diffsynth-2.1.2}/README.md +8 -0
  3. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/configs/model_configs.py +23 -0
  4. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/configs/vram_management_module_maps.py +4 -0
  5. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/quant/__init__.py +1 -1
  6. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/quant/backends/torchao.py +25 -8
  7. diffsynth-2.1.2/diffsynth/core/quant/base.py +241 -0
  8. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/quant/config.py +126 -34
  9. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/vram/layers.py +16 -7
  10. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/diffusion/flow_match.py +24 -1
  11. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/diffusion/template.py +5 -0
  12. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/diffusion/training_module.py +6 -3
  13. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ideogram4_dit.py +18 -14
  14. diffsynth-2.1.2/diffsynth/models/minimax_h3_dit_comfy_pruned.py +72 -0
  15. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/lingbot_video.py +13 -6
  16. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/minimax_h3_audio_video.py +30 -3
  17. diffsynth-2.1.2/diffsynth/utils/lora/minimax_h3.py +97 -0
  18. diffsynth-2.1.2/diffsynth/utils/quant/serialization.py +57 -0
  19. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth.egg-info/PKG-INFO +1 -1
  20. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth.egg-info/SOURCES.txt +3 -0
  21. {diffsynth-2.1.1 → diffsynth-2.1.2}/pyproject.toml +1 -1
  22. diffsynth-2.1.1/diffsynth/core/quant/base.py +0 -127
  23. {diffsynth-2.1.1 → diffsynth-2.1.2}/LICENSE +0 -0
  24. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/__init__.py +0 -0
  25. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/configs/__init__.py +0 -0
  26. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/__init__.py +0 -0
  27. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/attention/__init__.py +0 -0
  28. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/attention/attention.py +0 -0
  29. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/data/__init__.py +0 -0
  30. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/data/operators.py +0 -0
  31. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/data/unified_dataset.py +0 -0
  32. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/device/__init__.py +0 -0
  33. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/device/npu_compatible_device.py +0 -0
  34. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/gradient/__init__.py +0 -0
  35. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/gradient/gradient_checkpoint.py +0 -0
  36. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/loader/__init__.py +0 -0
  37. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/loader/config.py +0 -0
  38. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/loader/file.py +0 -0
  39. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/loader/model.py +0 -0
  40. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/npu_patch/npu_fused_operator.py +0 -0
  41. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/offload_training/__init__.py +0 -0
  42. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/offload_training/manager.py +0 -0
  43. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/offload_training/memory_buffer.py +0 -0
  44. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/offload_training/offloader.py +0 -0
  45. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/quant/backends/__init__.py +0 -0
  46. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/quant/backends/bitsandbytes.py +0 -0
  47. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/vram/__init__.py +0 -0
  48. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/vram/disk_map.py +0 -0
  49. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/core/vram/initialization.py +0 -0
  50. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/diffusion/__init__.py +0 -0
  51. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/diffusion/base_pipeline.py +0 -0
  52. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/diffusion/ddim_scheduler.py +0 -0
  53. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/diffusion/dmd2.py +0 -0
  54. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/diffusion/logger.py +0 -0
  55. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/diffusion/loss.py +0 -0
  56. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/diffusion/parsers.py +0 -0
  57. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/diffusion/runner.py +0 -0
  58. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/metrics/__init__.py +0 -0
  59. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/metrics/aesthetic.py +0 -0
  60. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/metrics/base.py +0 -0
  61. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/metrics/bioclip.py +0 -0
  62. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/metrics/clip.py +0 -0
  63. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/metrics/fid.py +0 -0
  64. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/metrics/hpsv2.py +0 -0
  65. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/metrics/hpsv3.py +0 -0
  66. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/metrics/image_reward.py +0 -0
  67. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/metrics/lpips.py +0 -0
  68. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/metrics/pickscore.py +0 -0
  69. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/metrics/qwen_image_bench.py +0 -0
  70. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/metrics/unified_reward_2.py +0 -0
  71. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/metrics/unified_reward_edit.py +0 -0
  72. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ace_step_conditioner.py +0 -0
  73. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ace_step_dit.py +0 -0
  74. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ace_step_residual_fsq.py +0 -0
  75. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ace_step_text_encoder.py +0 -0
  76. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ace_step_tokenizer.py +0 -0
  77. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ace_step_vae.py +0 -0
  78. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/aesthetic.py +0 -0
  79. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/anima_dit.py +0 -0
  80. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/bioclip.py +0 -0
  81. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/boogu_image_dit.py +0 -0
  82. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/clip.py +0 -0
  83. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/demucs.py +0 -0
  84. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/dinov3_image_encoder.py +0 -0
  85. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ernie_image_dit.py +0 -0
  86. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ernie_image_text_encoder.py +0 -0
  87. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/fid.py +0 -0
  88. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/flux2_dit.py +0 -0
  89. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/flux2_text_encoder.py +0 -0
  90. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/flux2_vae.py +0 -0
  91. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/flux_controlnet.py +0 -0
  92. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/flux_dit.py +0 -0
  93. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/flux_infiniteyou.py +0 -0
  94. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/flux_ipadapter.py +0 -0
  95. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/flux_lora_encoder.py +0 -0
  96. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/flux_lora_patcher.py +0 -0
  97. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/flux_text_encoder_clip.py +0 -0
  98. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/flux_text_encoder_t5.py +0 -0
  99. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/flux_vae.py +0 -0
  100. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/flux_value_control.py +0 -0
  101. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/general_modules.py +0 -0
  102. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/hidream_common.py +0 -0
  103. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/hidream_o1_image_dit.py +0 -0
  104. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/hpsv2.py +0 -0
  105. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/hpsv3.py +0 -0
  106. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ideogram4_text_encoder.py +0 -0
  107. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ideogram4_vae.py +0 -0
  108. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/image_reward.py +0 -0
  109. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/joyai_image_dit.py +0 -0
  110. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/joyai_image_text_encoder.py +0 -0
  111. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/krea2_dit.py +0 -0
  112. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/krea2_text_encoder.py +0 -0
  113. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/lingbot_video_dit.py +0 -0
  114. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/longcat_video_dit.py +0 -0
  115. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/lpips.py +0 -0
  116. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ltx2_audio_vae.py +0 -0
  117. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ltx2_common.py +0 -0
  118. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ltx2_dit.py +0 -0
  119. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ltx2_text_encoder.py +0 -0
  120. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ltx2_upsampler.py +0 -0
  121. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/ltx2_video_vae.py +0 -0
  122. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/minimax_h3_audio_vae.py +0 -0
  123. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/minimax_h3_dit.py +0 -0
  124. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/minimax_h3_text_encoder.py +0 -0
  125. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/minimax_h3_video_vae.py +0 -0
  126. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/model_loader.py +0 -0
  127. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/mova_audio_dit.py +0 -0
  128. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/mova_audio_vae.py +0 -0
  129. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/mova_dual_tower_bridge.py +0 -0
  130. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/nexus_gen.py +0 -0
  131. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/nexus_gen_ar_model.py +0 -0
  132. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/nexus_gen_projector.py +0 -0
  133. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/pickscore.py +0 -0
  134. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/qwen_image_bench.py +0 -0
  135. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/qwen_image_controlnet.py +0 -0
  136. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/qwen_image_dit.py +0 -0
  137. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/qwen_image_image2lora.py +0 -0
  138. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/qwen_image_text_encoder.py +0 -0
  139. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/qwen_image_vae.py +0 -0
  140. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/sd_text_encoder.py +0 -0
  141. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/siglip2_image_encoder.py +0 -0
  142. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/stable_diffusion_text_encoder.py +0 -0
  143. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/stable_diffusion_unet.py +0 -0
  144. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/stable_diffusion_vae.py +0 -0
  145. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/stable_diffusion_xl_text_encoder.py +0 -0
  146. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/stable_diffusion_xl_unet.py +0 -0
  147. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/step1x_connector.py +0 -0
  148. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/step1x_text_encoder.py +0 -0
  149. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/unified_reward_2.py +0 -0
  150. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/unified_reward_edit.py +0 -0
  151. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/wan_animate_2_dit.py +0 -0
  152. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/wan_video_animate_adapter.py +0 -0
  153. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/wan_video_camera_controller.py +0 -0
  154. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/wan_video_dit.py +0 -0
  155. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/wan_video_dit_s2v.py +0 -0
  156. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/wan_video_image_encoder.py +0 -0
  157. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/wan_video_mot.py +0 -0
  158. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/wan_video_motion_controller.py +0 -0
  159. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/wan_video_text_encoder.py +0 -0
  160. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/wan_video_vace.py +0 -0
  161. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/wan_video_vae.py +0 -0
  162. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/wantodance.py +0 -0
  163. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/wav2vec.py +0 -0
  164. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/z_image_controlnet.py +0 -0
  165. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/z_image_dit.py +0 -0
  166. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/z_image_image2lora.py +0 -0
  167. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/models/z_image_text_encoder.py +0 -0
  168. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/ace_step.py +0 -0
  169. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/anima_image.py +0 -0
  170. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/boogu_image.py +0 -0
  171. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/ernie_image.py +0 -0
  172. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/flux2_image.py +0 -0
  173. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/flux_image.py +0 -0
  174. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/hidream_o1_image.py +0 -0
  175. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/ideogram4.py +0 -0
  176. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/joyai_image.py +0 -0
  177. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/krea2.py +0 -0
  178. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/ltx2_audio_video.py +0 -0
  179. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/mova_audio_video.py +0 -0
  180. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/qwen_image.py +0 -0
  181. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/stable_diffusion.py +0 -0
  182. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/stable_diffusion_xl.py +0 -0
  183. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/wan_video.py +0 -0
  184. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/pipelines/z_image.py +0 -0
  185. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/controlnet/__init__.py +0 -0
  186. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/controlnet/annotator.py +0 -0
  187. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/controlnet/controlnet_input.py +0 -0
  188. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/data/__init__.py +0 -0
  189. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/data/audio.py +0 -0
  190. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/data/audio_video.py +0 -0
  191. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/data/media_io_ltx2.py +0 -0
  192. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/data/minimax_h3.py +0 -0
  193. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/demucs/__init__.py +0 -0
  194. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/dequantizer/__init__.py +0 -0
  195. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/lora/__init__.py +0 -0
  196. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/lora/flux.py +0 -0
  197. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/lora/flux_timestep.py +0 -0
  198. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/lora/general.py +0 -0
  199. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/lora/krea2.py +0 -0
  200. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/lora/merge.py +0 -0
  201. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/lora/reset_rank.py +0 -0
  202. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/lora/sdxl.py +0 -0
  203. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/ses/__init__.py +0 -0
  204. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/ses/ses.py +0 -0
  205. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/__init__.py +0 -0
  206. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/ace_step_conditioner.py +0 -0
  207. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/ace_step_dit.py +0 -0
  208. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/ace_step_text_encoder.py +0 -0
  209. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/ace_step_tokenizer.py +0 -0
  210. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/anima_dit.py +0 -0
  211. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/dino_v3.py +0 -0
  212. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/ernie_image_text_encoder.py +0 -0
  213. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/flux2_text_encoder.py +0 -0
  214. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/flux_controlnet.py +0 -0
  215. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/flux_dit.py +0 -0
  216. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/flux_infiniteyou.py +0 -0
  217. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/flux_ipadapter.py +0 -0
  218. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/flux_text_encoder_clip.py +0 -0
  219. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/flux_text_encoder_t5.py +0 -0
  220. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/flux_vae.py +0 -0
  221. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/ideogram4_text_encoder.py +0 -0
  222. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/image_metrics.py +0 -0
  223. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/joyai_image_text_encoder.py +0 -0
  224. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/krea2_dit.py +0 -0
  225. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/krea2_text_encoder.py +0 -0
  226. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/lingbot_video_dit.py +0 -0
  227. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/ltx2_audio_vae.py +0 -0
  228. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/ltx2_dit.py +0 -0
  229. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/ltx2_text_encoder.py +0 -0
  230. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/ltx2_video_vae.py +0 -0
  231. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/minimax_h3_audio_vae.py +0 -0
  232. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/minimax_h3_text_encoder.py +0 -0
  233. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/minimax_h3_video_vae.py +0 -0
  234. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/nexus_gen.py +0 -0
  235. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/nexus_gen_projector.py +0 -0
  236. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/qwen_image_text_encoder.py +0 -0
  237. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/sdxl.py +0 -0
  238. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/sdxl_text_encoder.py +0 -0
  239. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/sdxl_text_encoder_2.py +0 -0
  240. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/sdxl_vae.py +0 -0
  241. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/stable_diffusion_text_encoder.py +0 -0
  242. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/stable_diffusion_vae.py +0 -0
  243. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/stable_diffusion_xl_text_encoder.py +0 -0
  244. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/step1x_connector.py +0 -0
  245. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/wan_video_animate_adapter.py +0 -0
  246. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/wan_video_dit.py +0 -0
  247. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/wan_video_image_encoder.py +0 -0
  248. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/wan_video_mot.py +0 -0
  249. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/wan_video_vace.py +0 -0
  250. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/wan_video_vae.py +0 -0
  251. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/wans2v_audio_encoder.py +0 -0
  252. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/z_image_dit.py +0 -0
  253. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/state_dict_converters/z_image_text_encoder.py +0 -0
  254. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/tile/__init__.py +0 -0
  255. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/tile/tile_worker.py +0 -0
  256. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/xfuser/__init__.py +0 -0
  257. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/utils/xfuser/xdit_context_parallel.py +0 -0
  258. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth/version.py +0 -0
  259. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth.egg-info/dependency_links.txt +0 -0
  260. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth.egg-info/requires.txt +0 -0
  261. {diffsynth-2.1.1 → diffsynth-2.1.2}/diffsynth.egg-info/top_level.txt +0 -0
  262. {diffsynth-2.1.1 → diffsynth-2.1.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diffsynth
3
- Version: 2.1.1
3
+ Version: 2.1.2
4
4
  Summary: Enjoy the magic of Diffusion models!
5
5
  Author: ModelScope Team
6
6
  License: Apache-2.0
@@ -1527,6 +1527,8 @@ Example code for LingBot-Video is available at: [/examples/lingbot_video/](/exam
1527
1527
  |[Robbyant/lingbot-video-moe-30b-a3b: T2V](https://modelscope.cn/models/Robbyant/lingbot-video-moe-30b-a3b)|[code](/examples/lingbot_video/model_inference/lingbot-video-moe-30b-a3b_t2v.py)|[code](/examples/lingbot_video/model_inference_low_vram/lingbot-video-moe-30b-a3b_t2v.py)|[code](/examples/lingbot_video/model_training/full/lingbot-video-moe-30b-a3b_t2v.sh)|[code](/examples/lingbot_video/model_training/validate_full/lingbot-video-moe-30b-a3b_t2v.py)|[code](/examples/lingbot_video/model_training/lora/lingbot-video-moe-30b-a3b_t2v.sh)|[code](/examples/lingbot_video/model_training/validate_lora/lingbot-video-moe-30b-a3b_t2v.py)|
1528
1528
  |[Robbyant/lingbot-video-moe-30b-a3b: TI2V](https://modelscope.cn/models/Robbyant/lingbot-video-moe-30b-a3b)|[code](/examples/lingbot_video/model_inference/lingbot-video-moe-30b-a3b_ti2v.py)|[code](/examples/lingbot_video/model_inference_low_vram/lingbot-video-moe-30b-a3b_ti2v.py)|[code](/examples/lingbot_video/model_training/full/lingbot-video-moe-30b-a3b_ti2v.sh)|[code](/examples/lingbot_video/model_training/validate_full/lingbot-video-moe-30b-a3b_ti2v.py)|[code](/examples/lingbot_video/model_training/lora/lingbot-video-moe-30b-a3b_ti2v.sh)|[code](/examples/lingbot_video/model_training/validate_lora/lingbot-video-moe-30b-a3b_ti2v.py)|
1529
1529
  |[Robbyant/lingbot-video-moe-30b-a3b: T2I](https://modelscope.cn/models/Robbyant/lingbot-video-moe-30b-a3b)|[code](/examples/lingbot_video/model_inference/lingbot-video-moe-30b-a3b_t2i.py)|[code](/examples/lingbot_video/model_inference_low_vram/lingbot-video-moe-30b-a3b_t2i.py)|-|-|-|-|
1530
+ |[Robbyant/lingbot-video-moe-30b-a3b: T2V + Refinement](https://modelscope.cn/models/Robbyant/lingbot-video-moe-30b-a3b)|[code](/examples/lingbot_video/model_inference/lingbot-video-moe-30b-a3b_t2v_refiner.py)|[code](/examples/lingbot_video/model_inference_low_vram/lingbot-video-moe-30b-a3b_t2v_refiner.py)|-|-|-|-|
1531
+ |[Robbyant/lingbot-video-moe-30b-a3b: TI2V + Refinement](https://modelscope.cn/models/Robbyant/lingbot-video-moe-30b-a3b)|[code](/examples/lingbot_video/model_inference/lingbot-video-moe-30b-a3b_ti2v_refiner.py)|[code](/examples/lingbot_video/model_inference_low_vram/lingbot-video-moe-30b-a3b_ti2v_refiner.py)|-|-|-|-|
1530
1532
 
1531
1533
  </details>
1532
1534
 
@@ -1593,6 +1595,12 @@ Example code for MiniMax-H3 is available at: [/examples/minimax_h3/](/examples/m
1593
1595
  |[MiniMax/MiniMax-H3: Retake](https://www.modelscope.cn/models/MiniMax/MiniMax-H3)|[code](/examples/minimax_h3/model_inference/MiniMax-H3-Retake.py)|[code](/examples/minimax_h3/model_inference_low_vram/MiniMax-H3-Retake.py)|-|-|-|-|
1594
1596
  |[DiffSynth-Studio/MiniMax-H3-NF4: FL2VA](https://www.modelscope.cn/models/DiffSynth-Studio/MiniMax-H3-NF4)|[code](/examples/minimax_h3/model_inference/MiniMax-H3-NF4-FL2VA.py)|[code](/examples/minimax_h3/model_inference_low_vram/MiniMax-H3-NF4-FL2VA.py)|-|-|[code](/examples/minimax_h3/model_training/lora/MiniMax-H3-NF4-FL2VA.sh)|[code](/examples/minimax_h3/model_training/validate_lora/MiniMax-H3-NF4-FL2VA.py)|
1595
1597
  |[DiffSynth-Studio/MiniMax-H3-NF4: Ref2VA](https://www.modelscope.cn/models/DiffSynth-Studio/MiniMax-H3-NF4)|[code](/examples/minimax_h3/model_inference/MiniMax-H3-NF4-Ref2VA.py)|[code](/examples/minimax_h3/model_inference_low_vram/MiniMax-H3-NF4-Ref2VA.py)|-|-|[code](/examples/minimax_h3/model_training/lora/MiniMax-H3-NF4-Ref2VA.sh)|[code](/examples/minimax_h3/model_training/validate_lora/MiniMax-H3-NF4-Ref2VA.py)|
1598
+ |[Comfy-Org/MiniMax-H3: FL2VA pruned](https://www.modelscope.cn/models/Comfy-Org/MiniMax-H3)|[code](/examples/minimax_h3/model_inference/MiniMax-H3-Pruned-FL2VA.py)|[code](/examples/minimax_h3/model_inference_low_vram/MiniMax-H3-Pruned-FL2VA.py)|[code](/examples/minimax_h3/model_training/full/MiniMax-H3-Pruned-FL2VA.sh)|[code](/examples/minimax_h3/model_training/validate_full/MiniMax-H3-Pruned-FL2VA.py)|[code](/examples/minimax_h3/model_training/lora/MiniMax-H3-Pruned-FL2VA.sh)|[code](/examples/minimax_h3/model_training/validate_lora/MiniMax-H3-Pruned-FL2VA.py)|
1599
+ |[Comfy-Org/MiniMax-H3: Ref2VA pruned](https://www.modelscope.cn/models/Comfy-Org/MiniMax-H3)|[code](/examples/minimax_h3/model_inference/MiniMax-H3-Pruned-Ref2VA.py)|[code](/examples/minimax_h3/model_inference_low_vram/MiniMax-H3-Pruned-Ref2VA.py)|[code](/examples/minimax_h3/model_training/full/MiniMax-H3-Pruned-Ref2VA.sh)|[code](/examples/minimax_h3/model_training/validate_full/MiniMax-H3-Pruned-Ref2VA.py)|[code](/examples/minimax_h3/model_training/lora/MiniMax-H3-Pruned-Ref2VA.sh)|[code](/examples/minimax_h3/model_training/validate_lora/MiniMax-H3-Pruned-Ref2VA.py)|
1600
+ |[DiffSynth-Studio/MiniMax-H3-NF4: FL2VA pruned](https://www.modelscope.cn/models/DiffSynth-Studio/MiniMax-H3-NF4)|[code](/examples/minimax_h3/model_inference/MiniMax-H3-Pruned-NF4-FL2VA.py)|[code](/examples/minimax_h3/model_inference_low_vram/MiniMax-H3-Pruned-NF4-FL2VA.py)|-|-|[code](/examples/minimax_h3/model_training/lora/MiniMax-H3-Pruned-NF4-FL2VA.sh)|[code](/examples/minimax_h3/model_training/validate_lora/MiniMax-H3-Pruned-NF4-FL2VA.py)|
1601
+ |[DiffSynth-Studio/MiniMax-H3-NF4: Ref2VA pruned](https://www.modelscope.cn/models/DiffSynth-Studio/MiniMax-H3-NF4)|[code](/examples/minimax_h3/model_inference/MiniMax-H3-Pruned-NF4-Ref2VA.py)|[code](/examples/minimax_h3/model_inference_low_vram/MiniMax-H3-Pruned-NF4-Ref2VA.py)|-|-|[code](/examples/minimax_h3/model_training/lora/MiniMax-H3-Pruned-NF4-Ref2VA.sh)|[code](/examples/minimax_h3/model_training/validate_lora/MiniMax-H3-Pruned-NF4-Ref2VA.py)|
1602
+ |[lightx2v/Minimax-h3-Turbo: FL2VA 4steps](https://www.modelscope.cn/models/lightx2v/Minimax-h3-Turbo)|[code](https://github.com/modelscope/DiffSynth-Studio/blob/main/examples/minimax_h3/model_inference/MiniMax-H3-FL2VA-Turbo.py)|[code](https://github.com/modelscope/DiffSynth-Studio/blob/main/examples/minimax_h3/model_inference_low_vram/MiniMax-H3-FL2VA-Turbo.py)|-|-|-|-|
1603
+ |[DiffSynth-Studio/MiniMax-H3-Text-Embeddings](https://www.modelscope.cn/models/DiffSynth-Studio/MiniMax-H3-Text-Embeddings)|[code](/examples/minimax_h3/model_inference/MiniMax-H3-Text-Embeddings.py)|[code](/examples/minimax_h3/model_inference_low_vram/MiniMax-H3-Text-Embeddings.py)|[code](/examples/minimax_h3/model_training/full/MiniMax-H3-Text-Embeddings.sh)|[code](/examples/minimax_h3/model_training/validate_full/MiniMax-H3-Text-Embeddings.py)|-|-|
1596
1604
 
1597
1605
  </details>
1598
1606
 
@@ -1400,6 +1400,13 @@ minimax_h3_series = [
1400
1400
  "model_name": "minimax_h3_dit",
1401
1401
  "model_class": "diffsynth.models.minimax_h3_dit.MiniMaxH3DiT",
1402
1402
  },
1403
+ {
1404
+ # Example: ModelConfig(model_id="Comfy-Org/MiniMax-H3", origin_file_pattern="diffusion_models/minimax_h3_fl2va_pruned_bf16.safetensors")
1405
+ "model_hash": "b4e0df87da0c079979ae857edbcef49f",
1406
+ "model_name": "minimax_h3_dit",
1407
+ "model_class": "diffsynth.models.minimax_h3_dit_comfy_pruned.MiniMaxH3DiTComfyPruned",
1408
+ "extra_kwargs": {"adaln_curve_grid": 1025, "time_embed_dim": 8},
1409
+ },
1403
1410
  {
1404
1411
  # Example: ModelConfig(model_id="DiffSynth-Studio/MiniMax-H3-NF4", origin_file_pattern="minimax-h3-fl2va-nf4.safetensors")
1405
1412
  "model_hash": "4b27efacefbc4d8670e0d7b876699648",
@@ -1414,6 +1421,22 @@ minimax_h3_series = [
1414
1421
  "model_class": "diffsynth.models.minimax_h3_dit.MiniMaxH3DiT",
1415
1422
  "quant_config": {"method": "bitsandbytes_nf4", "load_prequantized": True, "exclude_modules": ["time_embedder.proj_in", "time_embedder.proj_out", "video_patch_proj", "audio_patch_proj", "condition_proj", "final_layer.video_out", "final_layer.audio_out"]},
1416
1423
  },
1424
+ {
1425
+ # Example: ModelConfig(model_id="DiffSynth-Studio/MiniMax-H3-NF4", origin_file_pattern="minimax-h3-fl2va-pruned-nf4.safetensors")
1426
+ "model_hash": "9b8f1cbc6fb5fcd2e2c02b6691480427",
1427
+ "model_name": "minimax_h3_dit",
1428
+ "model_class": "diffsynth.models.minimax_h3_dit_comfy_pruned.MiniMaxH3DiTComfyPruned",
1429
+ "extra_kwargs": {"adaln_curve_grid": 1025, "time_embed_dim": 8},
1430
+ "quant_config": {"method": "bitsandbytes_nf4", "load_prequantized": True, "exclude_modules": ["time_embedder.proj_in", "time_embedder.proj_out", "video_patch_proj", "audio_patch_proj", "condition_proj", "final_layer.video_out", "final_layer.audio_out", "adaln_proj.linear"]},
1431
+ },
1432
+ {
1433
+ # Example: ModelConfig(model_id="DiffSynth-Studio/MiniMax-H3-NF4", origin_file_pattern="minimax-h3-ref2va-pruned-nf4.safetensors")
1434
+ "model_hash": "072bd13242d6d67df79a154e7c4274b3",
1435
+ "model_name": "minimax_h3_dit",
1436
+ "model_class": "diffsynth.models.minimax_h3_dit_comfy_pruned.MiniMaxH3DiTComfyPruned",
1437
+ "extra_kwargs": {"adaln_curve_grid": 1025, "time_embed_dim": 8},
1438
+ "quant_config": {"method": "bitsandbytes_nf4", "load_prequantized": True, "exclude_modules": ["time_embedder.proj_in", "time_embedder.proj_out", "video_patch_proj", "audio_patch_proj", "condition_proj", "final_layer.video_out", "final_layer.audio_out", "adaln_proj.linear"]},
1439
+ },
1417
1440
  {
1418
1441
  # Example: ModelConfig(model_id="MiniMax/MiniMax-H3-FL2VA", origin_file_pattern="video_vae/source/model.safetensors")
1419
1442
  "model_hash": "24b80900992e2024fab17c991c57da23",
@@ -14,6 +14,10 @@ VRAM_MANAGEMENT_MODULE_MAPS = {
14
14
  "torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
15
15
  "torch.nn.RMSNorm": "diffsynth.core.vram.layers.AutoWrappedModule",
16
16
  },
17
+ "diffsynth.models.minimax_h3_dit_comfy_pruned.MiniMaxH3DiTComfyPruned": {
18
+ "torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
19
+ "torch.nn.RMSNorm": "diffsynth.core.vram.layers.AutoWrappedModule",
20
+ },
17
21
  "diffsynth.models.minimax_h3_text_encoder.MiniMaxH3TextEncoder": {
18
22
  "torch.nn.Linear": "diffsynth.core.vram.layers.AutoWrappedLinear",
19
23
  "torch.nn.Embedding": "diffsynth.core.vram.layers.AutoWrappedModule",
@@ -1,3 +1,3 @@
1
- from .base import QuantBackend, QUANT_BACKENDS, register_quant_backend, check_differentiable
1
+ from .base import QuantBackend, QUANT_BACKENDS, register_quant_backend, check_differentiable, check_backend_contract, resolve_checkpoint_keys
2
2
  from .config import QuantizeConfig, MixedQuantizeConfig, QuantMethodSpec, QUANT_METHODS, register_quant_method, describe_quant_method
3
3
  from . import backends
@@ -1,10 +1,15 @@
1
1
  import importlib.util
2
+ import json
2
3
 
3
4
  import torch
4
5
  from ..base import QuantBackend, register_quant_backend
5
6
  from ..config import register_quant_method
6
7
 
7
8
 
9
+ class TorchaoLinear(torch.nn.Linear):
10
+ """Marker class for torchao-quantized Linears."""
11
+
12
+
8
13
  @register_quant_backend("torchao")
9
14
  class TorchaoQuantBackend(QuantBackend):
10
15
  """Adapter over torchao `quantize_` (weight-only configs); the quantization lives in the weight tensor subclass, not the module class."""
@@ -21,27 +26,32 @@ class TorchaoQuantBackend(QuantBackend):
21
26
  def capabilities(self):
22
27
  return {
23
28
  "is_serializable": True,
24
- "is_differentiable": False,
29
+ "is_differentiable": True,
25
30
  "is_compileable": True,
26
31
  "requires_calibration": False,
27
32
  }
28
33
 
29
- def is_quantized_linear(self, module) -> bool:
30
- weight = getattr(module, "weight", None)
31
- return isinstance(module, torch.nn.Linear) and weight is not None and "torchao" in type(weight).__module__
34
+ def quantized_linear_classes(self):
35
+ return (TorchaoLinear,)
36
+
37
+ def checkpoint_key_patterns(self):
38
+ return ("weight", "_weight_qdata", "_weight_scale", "_weight_zero_point", "bias")
32
39
 
33
40
  def create_quantized_linear(self, linear, compute_device=None, model_device=None):
34
41
  from torchao.quantization import quantize_
35
42
  linear.requires_grad_(False)
36
43
  if compute_device is not None:
37
44
  linear = linear.to(device=compute_device)
38
- quantize_(linear, self.config)
45
+ quant_linear = TorchaoLinear(linear.in_features, linear.out_features, bias=linear.bias is not None, device="meta")
46
+ quant_linear.weight = linear.weight
47
+ quant_linear.bias = linear.bias
48
+ quantize_(quant_linear, self.config)
39
49
  if model_device is not None:
40
- linear = linear.to(device=model_device)
41
- return linear
50
+ quant_linear = quant_linear.to(device=model_device)
51
+ return quant_linear
42
52
 
43
53
  def create_quantized_linear_shell(self, linear, compute_dtype):
44
- return torch.nn.Linear(linear.in_features, linear.out_features, bias=linear.bias is not None, device="meta")
54
+ return TorchaoLinear(linear.in_features, linear.out_features, bias=linear.bias is not None, device="meta")
45
55
 
46
56
  def flatten_state_dict(self, state_dict):
47
57
  self._require_safetensors_support()
@@ -61,9 +71,16 @@ class TorchaoQuantBackend(QuantBackend):
61
71
  "safetensors header), so its tensor subclasses cannot be rebuilt. It was most "
62
72
  "likely not saved by torchao."
63
73
  )
74
+ tensor_names = json.loads(metadata["tensor_names"])
75
+ root_names = [name for name in tensor_names if "." not in name]
76
+ if root_names:
77
+ metadata = {**metadata, "tensor_names": json.dumps([name for name in tensor_names if "." in name])}
64
78
  rebuilt = unflatten_tensor_state_dict(state_dict, metadata)
65
79
  if isinstance(rebuilt, tuple):
66
80
  rebuilt = rebuilt[0]
81
+ for name in root_names:
82
+ if name in state_dict:
83
+ rebuilt[name] = state_dict[name]
67
84
  return rebuilt
68
85
 
69
86
  def _require_safetensors_support(self):
@@ -0,0 +1,241 @@
1
+ from abc import ABC
2
+ import torch
3
+
4
+
5
+ QUANT_BACKENDS = {}
6
+
7
+
8
+ class QuantBackend(ABC):
9
+ """
10
+ Adapter between the framework and a quantization library (bnb / torchao / custom).
11
+ Subclasses are registered in `QUANT_BACKENDS` ({name -> class}) and instantiated by
12
+ `QuantizeConfig` with the method's backend config. A backend operates on single
13
+ Linears; model-level traversal and replacement is done by the `QuantizeConfig` methods.
14
+
15
+ The quantized Linear produced by a backend must satisfy:
16
+ (a) It is an `nn.Linear` drop-in: `forward(x)` internally performs dequant + matmul.
17
+ (b) `.to(...)` moves devices but never re-types the packed weight / quant state:
18
+ a dtype cast (`.to(dtype)` / `.half()` / `.float()`) must leave their storage
19
+ format and values intact.
20
+ (c) `state_dict()` / `load_state_dict(assign=True)` round-trips (optionally via
21
+ `flatten_state_dict` / `unflatten_state_dict`).
22
+ (d) (Training branch only) `forward` is differentiable w.r.t. its input, so
23
+ gradients can pass through frozen quantized layers to reach LoRA branches.
24
+ Declared statically by `capabilities()["is_differentiable"]` and verifiable
25
+ at runtime by calling `check_differentiable` on a quantized Linear the
26
+ backend produced.
27
+ """
28
+
29
+ name: str = ""
30
+
31
+ def __init__(self, config=None):
32
+ self.config = config
33
+
34
+ def capabilities(self) -> dict:
35
+ return {
36
+ "is_serializable": False,
37
+ "is_differentiable": False,
38
+ "is_compileable": False,
39
+ "requires_calibration": False,
40
+ }
41
+
42
+ def validate_environment(self):
43
+ return
44
+
45
+ def create_quantized_linear(self, linear: torch.nn.Linear, compute_device=None, model_device=None) -> torch.nn.Module:
46
+ raise NotImplementedError(
47
+ f"Backend `{self.name}` cannot quantize an fp model online. Use a method whose "
48
+ "backend supports it, or load an already quantized checkpoint."
49
+ )
50
+
51
+ def create_quantized_linear_shell(self, linear: torch.nn.Linear, compute_dtype: torch.dtype) -> torch.nn.Module:
52
+ raise NotImplementedError(
53
+ f"Backend `{self.name}` cannot load pre-quantized checkpoints. Use "
54
+ "`load_prequantized=False` to quantize an fp model online instead."
55
+ )
56
+
57
+ def dequantize_to_linear(self, module: torch.nn.Module, compute_dtype: torch.dtype, compute_device=None, model_device=None) -> torch.nn.Linear:
58
+ raise NotImplementedError(
59
+ f"Backend `{self.name}` cannot dequantize back to `nn.Linear`, so "
60
+ '`mode="dequant_once"` is unavailable.'
61
+ )
62
+
63
+ def quantized_linear_classes(self) -> tuple:
64
+ """The Linear classes this backend produces. MUST be subclass of `torch.nn.Linear`."""
65
+ raise NotImplementedError(
66
+ f"Backend `{self.name}` must declare the Linear classes it produces."
67
+ )
68
+
69
+ def checkpoint_key_patterns(self) -> tuple:
70
+ """Checkpoint entries one quantized Linear needs, relative to its dotted name."""
71
+ return ("weight", "weight.", "bias")
72
+
73
+ def is_quantized_linear(self, module) -> bool:
74
+ return isinstance(module, self.quantized_linear_classes())
75
+
76
+ def flatten_state_dict(self, state_dict: dict):
77
+ self._require_serializable()
78
+ return state_dict, {}
79
+
80
+ def unflatten_state_dict(self, state_dict: dict, metadata: dict):
81
+ self._require_serializable()
82
+ return state_dict
83
+
84
+ def _require_serializable(self):
85
+ if not self.capabilities().get("is_serializable", False):
86
+ raise NotImplementedError(
87
+ f"Backend `{self.name}` declares `is_serializable=False`, so its quantized state "
88
+ "dict cannot be flattened or rebuilt. Override these methods if it actually can."
89
+ )
90
+
91
+ def register_quant_backend(name):
92
+ def decorator(cls):
93
+ cls.name = name
94
+ QUANT_BACKENDS[name] = cls
95
+ return cls
96
+ return decorator
97
+
98
+
99
+ def resolve_checkpoint_keys(patterns, layer_name: str, available_keys) -> list:
100
+ """Expand a backend's `checkpoint_key_patterns` into the absolute keys of one layer.
101
+
102
+ `available_keys` is anything supporting `in` and iteration (a dict or a `DiskMap`).
103
+ Exact patterns are probed with `in`, so a whole-file key index costs O(1) per pattern;
104
+ only patterns ending in "." require scanning the index.
105
+ """
106
+ prefix = f"{layer_name}." if layer_name else ""
107
+ nested_prefixes = tuple(prefix + pattern for pattern in patterns if pattern.endswith("."))
108
+ keys = [prefix + pattern for pattern in patterns
109
+ if not pattern.endswith(".") and prefix + pattern in available_keys]
110
+ if nested_prefixes:
111
+ keys += [key for key in available_keys if key.startswith(nested_prefixes)]
112
+ return list(dict.fromkeys(keys))
113
+
114
+
115
+ def check_differentiable(module: torch.nn.Module, example_input: torch.Tensor = None, verbose: bool = True) -> bool:
116
+ """
117
+ Check whether gradients pass through `module` w.r.t. its input: run a real
118
+ backward pass from the output (`torch.autograd.grad`) and verify a finite
119
+ gradient arrives at the input. This is what LoRA training requires from
120
+ frozen (e.g. quantized) layers. The module is cast to bfloat16 in place and
121
+ probed with a bfloat16 input; if `example_input` is None, a random one is
122
+ built for modules exposing `in_features`.
123
+
124
+ Example (probing a torchao-quantized Linear):
125
+
126
+ import torch
127
+ from diffsynth.core.quant import check_differentiable
128
+ from torchao.quantization import quantize_, Int8WeightOnlyConfig
129
+
130
+ linear = torch.nn.Linear(1024, 1024, dtype=torch.bfloat16, device="cuda")
131
+ quantize_(linear, Int8WeightOnlyConfig(version=2))
132
+ check_differentiable(linear)
133
+ """
134
+ def report(result, detail):
135
+ if verbose:
136
+ print(f"check_differentiable ({type(module).__name__}): {'OK' if result else 'FAIL'} -- {detail}")
137
+ return result
138
+
139
+ try:
140
+ module = module.to(torch.bfloat16)
141
+ if example_input is None:
142
+ if not hasattr(module, "in_features"):
143
+ raise ValueError("`example_input` is required for modules without `in_features`.")
144
+ device = next((t.device for t in list(module.parameters()) + list(module.buffers())), torch.device("cpu"))
145
+ example_input = torch.randn(4, module.in_features, device=device)
146
+ x = example_input.detach().to(torch.bfloat16).requires_grad_(True)
147
+ y = module(x)
148
+ if not y.requires_grad:
149
+ return report(False, "the output does not require grad, so no autograd graph was recorded")
150
+ input_grad = torch.autograd.grad(y, x, grad_outputs=torch.randn_like(y), allow_unused=True)[0]
151
+ except Exception as error:
152
+ return report(False, f"{type(error).__name__}: {error}")
153
+ if input_grad is None:
154
+ return report(False, "backward finished but no gradient reached the input")
155
+ if not torch.isfinite(input_grad.float()).all():
156
+ return report(False, "the input gradient contains non-finite values")
157
+ return report(True, "gradients pass through the module to its input")
158
+
159
+
160
+ def check_backend_contract(backend, in_features: int = 512, out_features: int = 512,
161
+ compute_dtype: torch.dtype = torch.bfloat16,
162
+ compute_device: str = "cuda", verbose: bool = True) -> bool:
163
+ """
164
+ Verify a backend satisfies the quantized-Linear contract: it declares its classes,
165
+ both factory methods return instances of them, and every declared class subclasses
166
+ `torch.nn.Linear` so LoRA target detection and VRAM management can see it. The
167
+ checkpoint key patterns are checked against the keys the backend actually writes,
168
+ since a pattern list that misses a scale makes disk offload load corrupt layers
169
+ without raising.
170
+
171
+ Example:
172
+
173
+ from diffsynth.core.quant import QUANT_BACKENDS, QUANT_METHODS, check_backend_contract
174
+ spec = QUANT_METHODS["bitsandbytes_nf4"]
175
+ check_backend_contract(QUANT_BACKENDS[spec.backend](spec.config_factory({})))
176
+ """
177
+ failures = []
178
+
179
+ def check(condition, detail):
180
+ if not condition:
181
+ failures.append(detail)
182
+ if verbose:
183
+ print(f" [{'PASS' if condition else 'FAIL'}] {detail}")
184
+ return condition
185
+
186
+ if verbose:
187
+ print(f"check_backend_contract ({backend.name}):")
188
+ try:
189
+ classes = backend.quantized_linear_classes()
190
+ except NotImplementedError as error:
191
+ if verbose:
192
+ print(f" [FAIL] quantized_linear_classes() is not implemented: {error}")
193
+ return False
194
+ if not check(len(classes) > 0, f"quantized_linear_classes() is non-empty: {[cls.__name__ for cls in classes]}"):
195
+ return False
196
+ for cls in classes:
197
+ check(issubclass(cls, torch.nn.Linear), f"{cls.__name__} subclasses torch.nn.Linear")
198
+
199
+ plain = torch.nn.Linear(in_features, out_features, bias=True, dtype=compute_dtype, device=compute_device)
200
+ plain.requires_grad_(False)
201
+ check(not backend.is_quantized_linear(plain), "a plain nn.Linear is not reported as quantized")
202
+
203
+ try:
204
+ shell = backend.create_quantized_linear_shell(plain, compute_dtype)
205
+ except NotImplementedError:
206
+ shell = None
207
+ if verbose:
208
+ print(" [SKIP] create_quantized_linear_shell() is unsupported by this backend")
209
+ if shell is not None:
210
+ check(isinstance(shell, classes), f"create_quantized_linear_shell() returns a declared class, got {type(shell).__name__}")
211
+ check(backend.is_quantized_linear(shell), "the shell is recognized before load_state_dict (disk offload routing)")
212
+
213
+ try:
214
+ quantized = backend.create_quantized_linear(plain, compute_device=compute_device)
215
+ except NotImplementedError:
216
+ quantized = None
217
+ if verbose:
218
+ print(" [SKIP] create_quantized_linear() is unsupported by this backend")
219
+ if quantized is not None:
220
+ check(isinstance(quantized, classes), f"create_quantized_linear() returns a declared class, got {type(quantized).__name__}")
221
+
222
+ patterns = backend.checkpoint_key_patterns()
223
+ check(len(patterns) > 0, f"checkpoint_key_patterns() is non-empty: {list(patterns)}")
224
+ saved = quantized if quantized is not None else shell
225
+ if saved is None:
226
+ if verbose:
227
+ print(" [SKIP] neither factory method is supported, so the stored keys cannot be checked")
228
+ else:
229
+ state_dict = {f"proj.{key}": value for key, value in saved.state_dict().items()}
230
+ if backend.capabilities().get("is_serializable", False):
231
+ try:
232
+ state_dict = backend.flatten_state_dict(state_dict)[0]
233
+ except Exception as error:
234
+ if verbose:
235
+ print(f" [SKIP] flatten_state_dict() failed, falling back to the raw state dict keys: {type(error).__name__}: {error}")
236
+ uncovered = sorted(set(state_dict) - set(resolve_checkpoint_keys(patterns, "proj", state_dict)))
237
+ check(len(uncovered) == 0, f"checkpoint_key_patterns() covers every stored key; uncovered: {uncovered}")
238
+
239
+ if verbose:
240
+ print(f" => {'OK' if not failures else str(len(failures)) + ' FAILED'}")
241
+ return not failures
@@ -1,7 +1,7 @@
1
1
  from dataclasses import dataclass, field, fields, is_dataclass
2
2
  from typing import Any, Callable, Optional
3
3
  import torch
4
- from .base import QUANT_BACKENDS
4
+ from .base import QUANT_BACKENDS, resolve_checkpoint_keys
5
5
 
6
6
 
7
7
  QUANT_METHODS = {}
@@ -166,6 +166,40 @@ class QuantizeConfig:
166
166
  """Whether `module` is one of this config's backend-native quantized Linears."""
167
167
  return self.backend.is_quantized_linear(module)
168
168
 
169
+ def checkpoint_keys(self, module, layer_name: str, available_keys) -> list:
170
+ """
171
+ Resolve the backend's `checkpoint_key_patterns` for `layer_name` against
172
+ `available_keys` (anything supporting `in` and iteration, including a `DiskMap`).
173
+ Raises when the layer contributes no key at all, since silently loading a
174
+ quantized layer without its packed weight or scale corrupts it without any error.
175
+
176
+ Parameters:
177
+ module: the quantized layer the keys are fetched for.
178
+ layer_name: its dotted name inside the checkpoint.
179
+ available_keys: the key index of the whole checkpoint.
180
+ """
181
+ keys = resolve_checkpoint_keys(self.backend.checkpoint_key_patterns(), layer_name, available_keys)
182
+ if not keys:
183
+ raise ValueError(
184
+ f"Found no checkpoint entry for the quantized layer `{layer_name}` "
185
+ f"(backend `{self.backend.name}`, patterns {list(self.backend.checkpoint_key_patterns())}). "
186
+ "Check that the checkpoint really holds this layer quantized and that "
187
+ "`target_modules` matches the layers it quantized."
188
+ )
189
+ return keys
190
+
191
+ def build_quantized_shell(self, module, compute_dtype: torch.dtype, **kwargs):
192
+ """
193
+ Build an empty quantized Linear matching `module`, used to release a layer's
194
+ weights while keeping it routable, and to stage a transient copy on the
195
+ computation device.
196
+
197
+ Parameters:
198
+ module: the quantized layer whose shape and bias presence are mirrored.
199
+ compute_dtype: dtype the shell dequantizes to at forward time.
200
+ """
201
+ return self.backend.create_quantized_linear_shell(module, compute_dtype)
202
+
169
203
  def prepare_for_prequantized_load(self, model: torch.nn.Module, compute_dtype: torch.dtype = torch.bfloat16):
170
204
  """
171
205
  Replace the targeted `nn.Linear` layers with empty quantized Linears matching a
@@ -226,6 +260,8 @@ class QuantizeConfig:
226
260
  def _should_quantize(self, full_name, module):
227
261
  if not isinstance(module, torch.nn.Linear):
228
262
  return False
263
+ if self.is_quantized_linear(module):
264
+ return False
229
265
  if self.target_modules is not None and not self._name_matches(full_name, self.target_modules):
230
266
  return False
231
267
  if self._name_matches(full_name, self.exclude_modules):
@@ -302,6 +338,7 @@ class MixedQuantizeConfig:
302
338
  modes = {config.mode for config in self.configs}
303
339
  if len(modes) > 1:
304
340
  raise ValueError(f"All configs in `MixedQuantizeConfig` should share the same `mode`, but got {sorted(modes)}.")
341
+ self._ownership = {}
305
342
 
306
343
  @property
307
344
  def method(self):
@@ -324,7 +361,7 @@ class MixedQuantizeConfig:
324
361
  """
325
362
  if self.load_prequantized:
326
363
  return model
327
- self._validate_disjoint(model)
364
+ self._build_ownership(model)
328
365
  for config in self.configs:
329
366
  config.quantize_model(model, compute_device=compute_device, model_device=model_device)
330
367
  return model
@@ -351,6 +388,43 @@ class MixedQuantizeConfig:
351
388
  """Whether `module` is a quantized Linear of any config's backend."""
352
389
  return any(config.is_quantized_linear(module) for config in self.configs)
353
390
 
391
+ def checkpoint_keys(self, module, layer_name: str, available_keys) -> list:
392
+ """
393
+ Resolve the checkpoint keys of `layer_name` using the config whose backend owns
394
+ that layer, so each layer set is read with its own key shape.
395
+
396
+ Parameters:
397
+ module: the quantized layer the keys are fetched for.
398
+ layer_name: its dotted name inside the checkpoint.
399
+ available_keys: the key index of the whole checkpoint.
400
+ """
401
+ return self._owning_config(module, layer_name).checkpoint_keys(module, layer_name, available_keys)
402
+
403
+ def build_quantized_shell(self, module, compute_dtype: torch.dtype, layer_name: str = None):
404
+ """
405
+ Build an empty quantized Linear for `module` using the config that owns it.
406
+
407
+ Parameters:
408
+ module: the quantized layer whose shape and bias presence are mirrored.
409
+ compute_dtype: dtype the shell dequantizes to at forward time.
410
+ layer_name: its dotted name inside the model; required to pick the right config
411
+ when several configs share one backend, since their quantized Linears are
412
+ then the same class.
413
+ """
414
+ return self._owning_config(module, layer_name).build_quantized_shell(module, compute_dtype)
415
+
416
+ def _owning_config(self, module, layer_name=None):
417
+ config = self._ownership.get(layer_name)
418
+ if config is not None:
419
+ return config
420
+ for config in self.configs:
421
+ if config.is_quantized_linear(module):
422
+ return config
423
+ raise ValueError(
424
+ f"`{type(module).__name__}` is not a quantized Linear of any config in this "
425
+ f"`MixedQuantizeConfig` (methods: {self.method})."
426
+ )
427
+
354
428
  def prepare_for_prequantized_load(self, model: torch.nn.Module, compute_dtype: torch.dtype = torch.bfloat16):
355
429
  """
356
430
  Replace each config's targeted Linears by that backend's shells, after verifying
@@ -360,26 +434,29 @@ class MixedQuantizeConfig:
360
434
  model: the freshly constructed model whose targeted layers become shells.
361
435
  compute_dtype: dtype the quantized layers dequantize to at forward time.
362
436
  """
363
- self._validate_disjoint(model)
437
+ self._build_ownership(model)
364
438
  for config in self.configs:
365
439
  config.prepare_for_prequantized_load(model, compute_dtype=compute_dtype)
366
440
  return model
367
441
 
368
442
  def unflatten_state_dict(self, state_dict: dict, metadata: dict):
369
443
  """
370
- Rebuild composite quantized tensors from a flat (safetensors) state dict,
371
- running each distinct backend's `unflatten_state_dict` once.
444
+ Rebuild composite quantized tensors from a flat (safetensors) state dict.
372
445
  """
373
- for config in self._distinct_backend_configs():
374
- state_dict = config.unflatten_state_dict(state_dict, metadata)
375
- return state_dict
446
+ if not self._ownership:
447
+ for config in self._distinct_backend_configs():
448
+ state_dict = config.unflatten_state_dict(state_dict, metadata)
449
+ return state_dict
450
+ rebuilt = {}
451
+ for config, keys in self._grouped_by_backend(state_dict):
452
+ rebuilt.update(config.unflatten_state_dict(keys, metadata) if config else keys)
453
+ return rebuilt
376
454
 
377
455
  def flatten_state_dict(self, state_dict: dict):
378
456
  """
379
- Flatten the mixed model's state dict into plain tensors and string-only
380
- metadata, running each distinct backend's `flatten_state_dict` once. Returns
381
- (state_dict, metadata), ready for `safetensors.torch.save_file(tensors, path,
382
- metadata=metadata)`.
457
+ Flatten the mixed model's state dict into plain tensors and string-only metadata.
458
+ Returns (state_dict, metadata), ready for `safetensors.torch.save_file(tensors,
459
+ path, metadata=metadata)`.
383
460
  """
384
461
  for config in self.configs:
385
462
  if not config.backend.capabilities().get("is_serializable", False):
@@ -389,10 +466,21 @@ class MixedQuantizeConfig:
389
466
  "state dict cannot be flattened for saving."
390
467
  )
391
468
  merged_metadata = {}
392
- for config in self._distinct_backend_configs():
393
- state_dict, metadata = config.backend.flatten_state_dict(state_dict)
394
- merged_metadata.update(metadata)
395
- tensors = {key: value.contiguous() for key, value in state_dict.items()}
469
+ if not self._ownership:
470
+ flattened = state_dict
471
+ for config in self._distinct_backend_configs():
472
+ flattened, metadata = config.backend.flatten_state_dict(flattened)
473
+ merged_metadata.update(metadata)
474
+ else:
475
+ flattened = {}
476
+ for config, keys in self._grouped_by_backend(state_dict):
477
+ if config is None:
478
+ flattened.update(keys)
479
+ continue
480
+ tensors, metadata = config.backend.flatten_state_dict(keys)
481
+ flattened.update(tensors)
482
+ merged_metadata.update(metadata)
483
+ tensors = {key: value.contiguous() for key, value in flattened.items()}
396
484
  metadata = {"format": "pt", **{key: value if isinstance(value, str) else str(value) for key, value in merged_metadata.items()}}
397
485
  return tensors, metadata
398
486
 
@@ -405,21 +493,25 @@ class MixedQuantizeConfig:
405
493
  distinct.append(config)
406
494
  return distinct
407
495
 
408
- def _validate_disjoint(self, model):
409
- matched = [
410
- {name for name, module in model.named_modules() if name and config._should_quantize(name, module)}
411
- for config in self.configs
412
- ]
413
- for i in range(len(matched)):
414
- for j in range(i + 1, len(matched)):
415
- overlap = matched[i] & matched[j]
416
- if overlap:
417
- samples = ", ".join(sorted(overlap)[:5])
418
- raise ValueError(
419
- f"Configs {i} (`{self.configs[i].method}`) and {j} (`{self.configs[j].method}`) "
420
- f"in `MixedQuantizeConfig` both match {len(overlap)} layers (e.g. {samples}). "
421
- "Layer sets must be pairwise disjoint, because re-quantizing an already "
422
- "quantized layer corrupts its packed weight silently. "
423
- "Adjust `target_modules` / `exclude_modules` so the sets are complementary."
424
- )
425
-
496
+ def _grouped_by_backend(self, state_dict):
497
+ groups = {}
498
+ for key, value in state_dict.items():
499
+ parts = key.split(".")
500
+ config = next((owner for end in range(len(parts) - 1, 0, -1)
501
+ if (owner := self._ownership.get(".".join(parts[:end]))) is not None), None)
502
+ groups.setdefault(config.backend.name if config else None, (config, {}))[1][key] = value
503
+ return groups.values()
504
+
505
+ def _build_ownership(self, model):
506
+ ownership = {}
507
+ for index, config in enumerate(self.configs):
508
+ names = [name for name, module in model.named_modules() if name and config._should_quantize(name, module)]
509
+ overlap = sorted(set(names) & ownership.keys())
510
+ if overlap:
511
+ owner = self.configs.index(ownership[overlap[0]])
512
+ raise ValueError(
513
+ f"Configs {owner} (`{self.configs[owner].method}`) and {index} (`{config.method}`) "
514
+ f"in `MixedQuantizeConfig` both match {len(overlap)} layers (e.g. {', '.join(overlap[:5])}). "
515
+ )
516
+ ownership.update({name: config for name in names})
517
+ self._ownership.update(ownership)