mainacc888 commited on
Commit
ebd8195
·
verified ·
1 Parent(s): 6582f64

Upload folder using huggingface_hub (part 2)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. ComfyUI/comfy/ldm/cascade/controlnet.py +92 -0
  3. ComfyUI/comfy/ldm/cascade/stage_a.py +250 -0
  4. ComfyUI/comfy/ldm/cascade/stage_b.py +256 -0
  5. ComfyUI/comfy/ldm/cascade/stage_c.py +273 -0
  6. ComfyUI/comfy/ldm/cascade/stage_c_coder.py +98 -0
  7. ComfyUI/comfy/ldm/chroma/layers.py +62 -0
  8. ComfyUI/comfy/ldm/chroma/model.py +295 -0
  9. ComfyUI/comfy/ldm/chroma_radiance/layers.py +204 -0
  10. ComfyUI/comfy/ldm/chroma_radiance/model.py +343 -0
  11. ComfyUI/comfy/ldm/cogvideo/__init__.py +0 -0
  12. ComfyUI/comfy/ldm/cogvideo/model.py +573 -0
  13. ComfyUI/comfy/ldm/cogvideo/vae.py +566 -0
  14. ComfyUI/comfy/ldm/colormap.py +25 -0
  15. ComfyUI/comfy/ldm/common_dit.py +16 -0
  16. ComfyUI/comfy/ldm/cosmos/blocks.py +805 -0
  17. ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/layers3d.py +1041 -0
  18. ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/patching.py +377 -0
  19. ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/utils.py +113 -0
  20. ComfyUI/comfy/ldm/cosmos/model.py +552 -0
  21. ComfyUI/comfy/ldm/cosmos/position_embedding.py +207 -0
  22. ComfyUI/comfy/ldm/cosmos/predict2.py +932 -0
  23. ComfyUI/comfy/ldm/cosmos/vae.py +131 -0
  24. ComfyUI/comfy/ldm/depth_anything_3/camera.py +177 -0
  25. ComfyUI/comfy/ldm/depth_anything_3/dpt.py +489 -0
  26. ComfyUI/comfy/ldm/depth_anything_3/model.py +236 -0
  27. ComfyUI/comfy/ldm/depth_anything_3/preprocess.py +128 -0
  28. ComfyUI/comfy/ldm/depth_anything_3/ray_pose.py +272 -0
  29. ComfyUI/comfy/ldm/depth_anything_3/reference_view_selector.py +87 -0
  30. ComfyUI/comfy/ldm/depth_anything_3/transform.py +160 -0
  31. ComfyUI/comfy/ldm/ernie/model.py +307 -0
  32. ComfyUI/comfy/ldm/flux/controlnet.py +208 -0
  33. ComfyUI/comfy/ldm/flux/layers.py +372 -0
  34. ComfyUI/comfy/ldm/flux/math.py +58 -0
  35. ComfyUI/comfy/ldm/flux/model.py +408 -0
  36. ComfyUI/comfy/ldm/flux/redux.py +25 -0
  37. ComfyUI/comfy/ldm/genmo/joint_model/asymm_models_joint.py +561 -0
  38. ComfyUI/comfy/ldm/genmo/joint_model/layers.py +153 -0
  39. ComfyUI/comfy/ldm/genmo/joint_model/rope_mixed.py +88 -0
  40. ComfyUI/comfy/ldm/genmo/joint_model/temporal_rope.py +34 -0
  41. ComfyUI/comfy/ldm/genmo/joint_model/utils.py +102 -0
  42. ComfyUI/comfy/ldm/genmo/vae/model.py +711 -0
  43. ComfyUI/comfy/ldm/hidream/model.py +829 -0
  44. ComfyUI/comfy/ldm/hidream_o1/attention.py +41 -0
  45. ComfyUI/comfy/ldm/hidream_o1/conditioning.py +230 -0
  46. ComfyUI/comfy/ldm/hidream_o1/model.py +306 -0
  47. ComfyUI/comfy/ldm/hidream_o1/utils.py +173 -0
  48. ComfyUI/comfy/ldm/hunyuan3d/model.py +148 -0
  49. ComfyUI/comfy/ldm/hunyuan3d/vae.py +988 -0
  50. ComfyUI/comfy/ldm/hunyuan3dv2_1/hunyuandit.py +668 -0
.gitattributes CHANGED
@@ -6,3 +6,4 @@ unstableRevolution_V3Fp16.safetensors filter=lfs diff=lfs merge=lfs -text
6
  ae.safetensors filter=lfs diff=lfs merge=lfs -text
7
  qwen_3_4b.safetensors filter=lfs diff=lfs merge=lfs -text
8
  pornmaster_proSDXLV8.safetensors filter=lfs diff=lfs merge=lfs -text
 
 
6
  ae.safetensors filter=lfs diff=lfs merge=lfs -text
7
  qwen_3_4b.safetensors filter=lfs diff=lfs merge=lfs -text
8
  pornmaster_proSDXLV8.safetensors filter=lfs diff=lfs merge=lfs -text
9
+ ComfyUI/comfy/text_encoders/t5_pile_tokenizer/tokenizer.model filter=lfs diff=lfs merge=lfs -text
ComfyUI/comfy/ldm/cascade/controlnet.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This file is part of ComfyUI.
3
+ Copyright (C) 2024 Stability AI
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ """
18
+
19
+ import torchvision
20
+ from torch import nn
21
+ from .common import LayerNorm2d_op
22
+
23
+
24
+ class CNetResBlock(nn.Module):
25
+ def __init__(self, c, dtype=None, device=None, operations=None):
26
+ super().__init__()
27
+ self.blocks = nn.Sequential(
28
+ LayerNorm2d_op(operations)(c, dtype=dtype, device=device),
29
+ nn.GELU(),
30
+ operations.Conv2d(c, c, kernel_size=3, padding=1),
31
+ LayerNorm2d_op(operations)(c, dtype=dtype, device=device),
32
+ nn.GELU(),
33
+ operations.Conv2d(c, c, kernel_size=3, padding=1),
34
+ )
35
+
36
+ def forward(self, x):
37
+ return x + self.blocks(x)
38
+
39
+
40
+ class ControlNet(nn.Module):
41
+ def __init__(self, c_in=3, c_proj=2048, proj_blocks=None, bottleneck_mode=None, dtype=None, device=None, operations=nn):
42
+ super().__init__()
43
+ if bottleneck_mode is None:
44
+ bottleneck_mode = 'effnet'
45
+ self.proj_blocks = proj_blocks
46
+ if bottleneck_mode == 'effnet':
47
+ embd_channels = 1280
48
+ self.backbone = torchvision.models.efficientnet_v2_s().features.eval()
49
+ if c_in != 3:
50
+ in_weights = self.backbone[0][0].weight.data
51
+ self.backbone[0][0] = operations.Conv2d(c_in, 24, kernel_size=3, stride=2, bias=False, dtype=dtype, device=device)
52
+ if c_in > 3:
53
+ # nn.init.constant_(self.backbone[0][0].weight, 0)
54
+ self.backbone[0][0].weight.data[:, :3] = in_weights[:, :3].clone()
55
+ else:
56
+ self.backbone[0][0].weight.data = in_weights[:, :c_in].clone()
57
+ elif bottleneck_mode == 'simple':
58
+ embd_channels = c_in
59
+ self.backbone = nn.Sequential(
60
+ operations.Conv2d(embd_channels, embd_channels * 4, kernel_size=3, padding=1, dtype=dtype, device=device),
61
+ nn.LeakyReLU(0.2, inplace=True),
62
+ operations.Conv2d(embd_channels * 4, embd_channels, kernel_size=3, padding=1, dtype=dtype, device=device),
63
+ )
64
+ elif bottleneck_mode == 'large':
65
+ self.backbone = nn.Sequential(
66
+ operations.Conv2d(c_in, 4096 * 4, kernel_size=1, dtype=dtype, device=device),
67
+ nn.LeakyReLU(0.2, inplace=True),
68
+ operations.Conv2d(4096 * 4, 1024, kernel_size=1, dtype=dtype, device=device),
69
+ *[CNetResBlock(1024, dtype=dtype, device=device, operations=operations) for _ in range(8)],
70
+ operations.Conv2d(1024, 1280, kernel_size=1, dtype=dtype, device=device),
71
+ )
72
+ embd_channels = 1280
73
+ else:
74
+ raise ValueError(f'Unknown bottleneck mode: {bottleneck_mode}')
75
+ self.projections = nn.ModuleList()
76
+ for _ in range(len(proj_blocks)):
77
+ self.projections.append(nn.Sequential(
78
+ operations.Conv2d(embd_channels, embd_channels, kernel_size=1, bias=False, dtype=dtype, device=device),
79
+ nn.LeakyReLU(0.2, inplace=True),
80
+ operations.Conv2d(embd_channels, c_proj, kernel_size=1, bias=False, dtype=dtype, device=device),
81
+ ))
82
+ # nn.init.constant_(self.projections[-1][-1].weight, 0) # zero output projection
83
+ self.xl = False
84
+ self.input_channels = c_in
85
+ self.unshuffle_amount = 8
86
+
87
+ def forward(self, x):
88
+ x = self.backbone(x)
89
+ proj_outputs = [None for _ in range(max(self.proj_blocks) + 1)]
90
+ for i, idx in enumerate(self.proj_blocks):
91
+ proj_outputs[idx] = self.projections[i](x)
92
+ return {"input": proj_outputs[::-1]}
ComfyUI/comfy/ldm/cascade/stage_a.py ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This file is part of ComfyUI.
3
+ Copyright (C) 2024 Stability AI
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ """
18
+
19
+ import torch
20
+ from torch import nn
21
+ from torch.autograd import Function
22
+ import comfy.ops
23
+
24
+ ops = comfy.ops.disable_weight_init
25
+
26
+
27
+ class vector_quantize(Function):
28
+ @staticmethod
29
+ def forward(ctx, x, codebook):
30
+ with torch.no_grad():
31
+ codebook_sqr = torch.sum(codebook ** 2, dim=1)
32
+ x_sqr = torch.sum(x ** 2, dim=1, keepdim=True)
33
+
34
+ dist = torch.addmm(codebook_sqr + x_sqr, x, codebook.t(), alpha=-2.0, beta=1.0)
35
+ _, indices = dist.min(dim=1)
36
+
37
+ ctx.save_for_backward(indices, codebook)
38
+ ctx.mark_non_differentiable(indices)
39
+
40
+ nn = torch.index_select(codebook, 0, indices)
41
+ return nn, indices
42
+
43
+ @staticmethod
44
+ def backward(ctx, grad_output, grad_indices):
45
+ grad_inputs, grad_codebook = None, None
46
+
47
+ if ctx.needs_input_grad[0]:
48
+ grad_inputs = grad_output.clone()
49
+ if ctx.needs_input_grad[1]:
50
+ # Gradient wrt. the codebook
51
+ indices, codebook = ctx.saved_tensors
52
+
53
+ grad_codebook = torch.zeros_like(codebook)
54
+ grad_codebook.index_add_(0, indices, grad_output)
55
+
56
+ return (grad_inputs, grad_codebook)
57
+
58
+
59
+ class VectorQuantize(nn.Module):
60
+ def __init__(self, embedding_size, k, ema_decay=0.99, ema_loss=False):
61
+ """
62
+ Takes an input of variable size (as long as the last dimension matches the embedding size).
63
+ Returns one tensor containing the nearest neigbour embeddings to each of the inputs,
64
+ with the same size as the input, vq and commitment components for the loss as a touple
65
+ in the second output and the indices of the quantized vectors in the third:
66
+ quantized, (vq_loss, commit_loss), indices
67
+ """
68
+ super(VectorQuantize, self).__init__()
69
+
70
+ self.codebook = nn.Embedding(k, embedding_size)
71
+ self.codebook.weight.data.uniform_(-1./k, 1./k)
72
+ self.vq = vector_quantize.apply
73
+
74
+ self.ema_decay = ema_decay
75
+ self.ema_loss = ema_loss
76
+ if ema_loss:
77
+ self.register_buffer('ema_element_count', torch.ones(k))
78
+ self.register_buffer('ema_weight_sum', torch.zeros_like(self.codebook.weight))
79
+
80
+ def _laplace_smoothing(self, x, epsilon):
81
+ n = torch.sum(x)
82
+ return ((x + epsilon) / (n + x.size(0) * epsilon) * n)
83
+
84
+ def _updateEMA(self, z_e_x, indices):
85
+ mask = nn.functional.one_hot(indices, self.ema_element_count.size(0)).float()
86
+ elem_count = mask.sum(dim=0)
87
+ weight_sum = torch.mm(mask.t(), z_e_x)
88
+
89
+ self.ema_element_count = (self.ema_decay * self.ema_element_count) + ((1-self.ema_decay) * elem_count)
90
+ self.ema_element_count = self._laplace_smoothing(self.ema_element_count, 1e-5)
91
+ self.ema_weight_sum = (self.ema_decay * self.ema_weight_sum) + ((1-self.ema_decay) * weight_sum)
92
+
93
+ self.codebook.weight.data = self.ema_weight_sum / self.ema_element_count.unsqueeze(-1)
94
+
95
+ def idx2vq(self, idx, dim=-1):
96
+ q_idx = self.codebook(idx)
97
+ if dim != -1:
98
+ q_idx = q_idx.movedim(-1, dim)
99
+ return q_idx
100
+
101
+ def forward(self, x, get_losses=True, dim=-1):
102
+ if dim != -1:
103
+ x = x.movedim(dim, -1)
104
+ z_e_x = x.contiguous().view(-1, x.size(-1)) if len(x.shape) > 2 else x
105
+ z_q_x, indices = self.vq(z_e_x, self.codebook.weight.detach())
106
+ vq_loss, commit_loss = None, None
107
+ if self.ema_loss and self.training:
108
+ self._updateEMA(z_e_x.detach(), indices.detach())
109
+ # pick the graded embeddings after updating the codebook in order to have a more accurate commitment loss
110
+ z_q_x_grd = torch.index_select(self.codebook.weight, dim=0, index=indices)
111
+ if get_losses:
112
+ vq_loss = (z_q_x_grd - z_e_x.detach()).pow(2).mean()
113
+ commit_loss = (z_e_x - z_q_x_grd.detach()).pow(2).mean()
114
+
115
+ z_q_x = z_q_x.view(x.shape)
116
+ if dim != -1:
117
+ z_q_x = z_q_x.movedim(-1, dim)
118
+ return z_q_x, (vq_loss, commit_loss), indices.view(x.shape[:-1])
119
+
120
+
121
+ class ResBlock(nn.Module):
122
+ def __init__(self, c, c_hidden):
123
+ super().__init__()
124
+ # depthwise/attention
125
+ self.norm1 = nn.LayerNorm(c, elementwise_affine=False, eps=1e-6)
126
+ self.depthwise = nn.Sequential(
127
+ nn.ReplicationPad2d(1),
128
+ ops.Conv2d(c, c, kernel_size=3, groups=c)
129
+ )
130
+
131
+ # channelwise
132
+ self.norm2 = nn.LayerNorm(c, elementwise_affine=False, eps=1e-6)
133
+ self.channelwise = nn.Sequential(
134
+ ops.Linear(c, c_hidden),
135
+ nn.GELU(),
136
+ ops.Linear(c_hidden, c),
137
+ )
138
+
139
+ self.gammas = nn.Parameter(torch.zeros(6), requires_grad=False)
140
+
141
+ def _norm(self, x, norm):
142
+ return norm(x.permute(0, 2, 3, 1)).permute(0, 3, 1, 2)
143
+
144
+ def forward(self, x):
145
+ mods = self.gammas
146
+
147
+ x_temp = self._norm(x, self.norm1) * (1 + mods[0]) + mods[1]
148
+ try:
149
+ x = x + self.depthwise(x_temp) * mods[2]
150
+ except: #operation not implemented for bf16
151
+ x_temp = self.depthwise[0](x_temp.float()).to(x.dtype)
152
+ x = x + self.depthwise[1](x_temp) * mods[2]
153
+
154
+ x_temp = self._norm(x, self.norm2) * (1 + mods[3]) + mods[4]
155
+ x = x + self.channelwise(x_temp.permute(0, 2, 3, 1)).permute(0, 3, 1, 2) * mods[5]
156
+
157
+ return x
158
+
159
+
160
+ class StageA(nn.Module):
161
+ def __init__(self, levels=2, bottleneck_blocks=12, c_hidden=384, c_latent=4, codebook_size=8192):
162
+ super().__init__()
163
+ self.c_latent = c_latent
164
+ c_levels = [c_hidden // (2 ** i) for i in reversed(range(levels))]
165
+
166
+ # Encoder blocks
167
+ self.in_block = nn.Sequential(
168
+ nn.PixelUnshuffle(2),
169
+ ops.Conv2d(3 * 4, c_levels[0], kernel_size=1)
170
+ )
171
+ down_blocks = []
172
+ for i in range(levels):
173
+ if i > 0:
174
+ down_blocks.append(ops.Conv2d(c_levels[i - 1], c_levels[i], kernel_size=4, stride=2, padding=1))
175
+ block = ResBlock(c_levels[i], c_levels[i] * 4)
176
+ down_blocks.append(block)
177
+ down_blocks.append(nn.Sequential(
178
+ ops.Conv2d(c_levels[-1], c_latent, kernel_size=1, bias=False),
179
+ nn.BatchNorm2d(c_latent), # then normalize them to have mean 0 and std 1
180
+ ))
181
+ self.down_blocks = nn.Sequential(*down_blocks)
182
+ self.down_blocks[0]
183
+
184
+ self.codebook_size = codebook_size
185
+ self.vquantizer = VectorQuantize(c_latent, k=codebook_size)
186
+
187
+ # Decoder blocks
188
+ up_blocks = [nn.Sequential(
189
+ ops.Conv2d(c_latent, c_levels[-1], kernel_size=1)
190
+ )]
191
+ for i in range(levels):
192
+ for j in range(bottleneck_blocks if i == 0 else 1):
193
+ block = ResBlock(c_levels[levels - 1 - i], c_levels[levels - 1 - i] * 4)
194
+ up_blocks.append(block)
195
+ if i < levels - 1:
196
+ up_blocks.append(
197
+ ops.ConvTranspose2d(c_levels[levels - 1 - i], c_levels[levels - 2 - i], kernel_size=4, stride=2,
198
+ padding=1))
199
+ self.up_blocks = nn.Sequential(*up_blocks)
200
+ self.out_block = nn.Sequential(
201
+ ops.Conv2d(c_levels[0], 3 * 4, kernel_size=1),
202
+ nn.PixelShuffle(2),
203
+ )
204
+
205
+ def encode(self, x, quantize=False):
206
+ x = self.in_block(x)
207
+ x = self.down_blocks(x)
208
+ if quantize:
209
+ qe, (vq_loss, commit_loss), indices = self.vquantizer.forward(x, dim=1)
210
+ return qe, x, indices, vq_loss + commit_loss * 0.25
211
+ else:
212
+ return x
213
+
214
+ def decode(self, x):
215
+ x = self.up_blocks(x)
216
+ x = self.out_block(x)
217
+ return x
218
+
219
+ def forward(self, x, quantize=False):
220
+ qe, x, _, vq_loss = self.encode(x, quantize)
221
+ x = self.decode(qe)
222
+ return x, vq_loss
223
+
224
+
225
+ class Discriminator(nn.Module):
226
+ def __init__(self, c_in=3, c_cond=0, c_hidden=512, depth=6):
227
+ super().__init__()
228
+ d = max(depth - 3, 3)
229
+ layers = [
230
+ nn.utils.spectral_norm(ops.Conv2d(c_in, c_hidden // (2 ** d), kernel_size=3, stride=2, padding=1)),
231
+ nn.LeakyReLU(0.2),
232
+ ]
233
+ for i in range(depth - 1):
234
+ c_in = c_hidden // (2 ** max((d - i), 0))
235
+ c_out = c_hidden // (2 ** max((d - 1 - i), 0))
236
+ layers.append(nn.utils.spectral_norm(ops.Conv2d(c_in, c_out, kernel_size=3, stride=2, padding=1)))
237
+ layers.append(nn.InstanceNorm2d(c_out))
238
+ layers.append(nn.LeakyReLU(0.2))
239
+ self.encoder = nn.Sequential(*layers)
240
+ self.shuffle = ops.Conv2d((c_hidden + c_cond) if c_cond > 0 else c_hidden, 1, kernel_size=1)
241
+ self.logits = nn.Sigmoid()
242
+
243
+ def forward(self, x, cond=None):
244
+ x = self.encoder(x)
245
+ if cond is not None:
246
+ cond = cond.view(cond.size(0), cond.size(1), 1, 1, ).expand(-1, -1, x.size(-2), x.size(-1))
247
+ x = torch.cat([x, cond], dim=1)
248
+ x = self.shuffle(x)
249
+ x = self.logits(x)
250
+ return x
ComfyUI/comfy/ldm/cascade/stage_b.py ADDED
@@ -0,0 +1,256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This file is part of ComfyUI.
3
+ Copyright (C) 2024 Stability AI
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ """
18
+
19
+ import math
20
+ import torch
21
+ from torch import nn
22
+ from .common import AttnBlock, LayerNorm2d_op, ResBlock, FeedForwardBlock, TimestepBlock
23
+
24
+ class StageB(nn.Module):
25
+ def __init__(self, c_in=4, c_out=4, c_r=64, patch_size=2, c_cond=1280, c_hidden=[320, 640, 1280, 1280],
26
+ nhead=[-1, -1, 20, 20], blocks=[[2, 6, 28, 6], [6, 28, 6, 2]],
27
+ block_repeat=[[1, 1, 1, 1], [3, 3, 2, 2]], level_config=['CT', 'CT', 'CTA', 'CTA'], c_clip=1280,
28
+ c_clip_seq=4, c_effnet=16, c_pixels=3, kernel_size=3, dropout=[0, 0, 0.0, 0.0], self_attn=True,
29
+ t_conds=['sca'], stable_cascade_stage=None, dtype=None, device=None, operations=None):
30
+ super().__init__()
31
+ self.dtype = dtype
32
+ self.c_r = c_r
33
+ self.t_conds = t_conds
34
+ self.c_clip_seq = c_clip_seq
35
+ if not isinstance(dropout, list):
36
+ dropout = [dropout] * len(c_hidden)
37
+ if not isinstance(self_attn, list):
38
+ self_attn = [self_attn] * len(c_hidden)
39
+
40
+ # CONDITIONING
41
+ self.effnet_mapper = nn.Sequential(
42
+ operations.Conv2d(c_effnet, c_hidden[0] * 4, kernel_size=1, dtype=dtype, device=device),
43
+ nn.GELU(),
44
+ operations.Conv2d(c_hidden[0] * 4, c_hidden[0], kernel_size=1, dtype=dtype, device=device),
45
+ LayerNorm2d_op(operations)(c_hidden[0], elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
46
+ )
47
+ self.pixels_mapper = nn.Sequential(
48
+ operations.Conv2d(c_pixels, c_hidden[0] * 4, kernel_size=1, dtype=dtype, device=device),
49
+ nn.GELU(),
50
+ operations.Conv2d(c_hidden[0] * 4, c_hidden[0], kernel_size=1, dtype=dtype, device=device),
51
+ LayerNorm2d_op(operations)(c_hidden[0], elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
52
+ )
53
+ self.clip_mapper = operations.Linear(c_clip, c_cond * c_clip_seq, dtype=dtype, device=device)
54
+ self.clip_norm = operations.LayerNorm(c_cond, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
55
+
56
+ self.embedding = nn.Sequential(
57
+ nn.PixelUnshuffle(patch_size),
58
+ operations.Conv2d(c_in * (patch_size ** 2), c_hidden[0], kernel_size=1, dtype=dtype, device=device),
59
+ LayerNorm2d_op(operations)(c_hidden[0], elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
60
+ )
61
+
62
+ def get_block(block_type, c_hidden, nhead, c_skip=0, dropout=0, self_attn=True):
63
+ if block_type == 'C':
64
+ return ResBlock(c_hidden, c_skip, kernel_size=kernel_size, dropout=dropout, dtype=dtype, device=device, operations=operations)
65
+ elif block_type == 'A':
66
+ return AttnBlock(c_hidden, c_cond, nhead, self_attn=self_attn, dropout=dropout, dtype=dtype, device=device, operations=operations)
67
+ elif block_type == 'F':
68
+ return FeedForwardBlock(c_hidden, dropout=dropout, dtype=dtype, device=device, operations=operations)
69
+ elif block_type == 'T':
70
+ return TimestepBlock(c_hidden, c_r, conds=t_conds, dtype=dtype, device=device, operations=operations)
71
+ else:
72
+ raise Exception(f'Block type {block_type} not supported')
73
+
74
+ # BLOCKS
75
+ # -- down blocks
76
+ self.down_blocks = nn.ModuleList()
77
+ self.down_downscalers = nn.ModuleList()
78
+ self.down_repeat_mappers = nn.ModuleList()
79
+ for i in range(len(c_hidden)):
80
+ if i > 0:
81
+ self.down_downscalers.append(nn.Sequential(
82
+ LayerNorm2d_op(operations)(c_hidden[i - 1], elementwise_affine=False, eps=1e-6, dtype=dtype, device=device),
83
+ operations.Conv2d(c_hidden[i - 1], c_hidden[i], kernel_size=2, stride=2, dtype=dtype, device=device),
84
+ ))
85
+ else:
86
+ self.down_downscalers.append(nn.Identity())
87
+ down_block = nn.ModuleList()
88
+ for _ in range(blocks[0][i]):
89
+ for block_type in level_config[i]:
90
+ block = get_block(block_type, c_hidden[i], nhead[i], dropout=dropout[i], self_attn=self_attn[i])
91
+ down_block.append(block)
92
+ self.down_blocks.append(down_block)
93
+ if block_repeat is not None:
94
+ block_repeat_mappers = nn.ModuleList()
95
+ for _ in range(block_repeat[0][i] - 1):
96
+ block_repeat_mappers.append(operations.Conv2d(c_hidden[i], c_hidden[i], kernel_size=1, dtype=dtype, device=device))
97
+ self.down_repeat_mappers.append(block_repeat_mappers)
98
+
99
+ # -- up blocks
100
+ self.up_blocks = nn.ModuleList()
101
+ self.up_upscalers = nn.ModuleList()
102
+ self.up_repeat_mappers = nn.ModuleList()
103
+ for i in reversed(range(len(c_hidden))):
104
+ if i > 0:
105
+ self.up_upscalers.append(nn.Sequential(
106
+ LayerNorm2d_op(operations)(c_hidden[i], elementwise_affine=False, eps=1e-6, dtype=dtype, device=device),
107
+ operations.ConvTranspose2d(c_hidden[i], c_hidden[i - 1], kernel_size=2, stride=2, dtype=dtype, device=device),
108
+ ))
109
+ else:
110
+ self.up_upscalers.append(nn.Identity())
111
+ up_block = nn.ModuleList()
112
+ for j in range(blocks[1][::-1][i]):
113
+ for k, block_type in enumerate(level_config[i]):
114
+ c_skip = c_hidden[i] if i < len(c_hidden) - 1 and j == k == 0 else 0
115
+ block = get_block(block_type, c_hidden[i], nhead[i], c_skip=c_skip, dropout=dropout[i],
116
+ self_attn=self_attn[i])
117
+ up_block.append(block)
118
+ self.up_blocks.append(up_block)
119
+ if block_repeat is not None:
120
+ block_repeat_mappers = nn.ModuleList()
121
+ for _ in range(block_repeat[1][::-1][i] - 1):
122
+ block_repeat_mappers.append(operations.Conv2d(c_hidden[i], c_hidden[i], kernel_size=1, dtype=dtype, device=device))
123
+ self.up_repeat_mappers.append(block_repeat_mappers)
124
+
125
+ # OUTPUT
126
+ self.clf = nn.Sequential(
127
+ LayerNorm2d_op(operations)(c_hidden[0], elementwise_affine=False, eps=1e-6, dtype=dtype, device=device),
128
+ operations.Conv2d(c_hidden[0], c_out * (patch_size ** 2), kernel_size=1, dtype=dtype, device=device),
129
+ nn.PixelShuffle(patch_size),
130
+ )
131
+
132
+ # --- WEIGHT INIT ---
133
+ # self.apply(self._init_weights) # General init
134
+ # nn.init.normal_(self.clip_mapper.weight, std=0.02) # conditionings
135
+ # nn.init.normal_(self.effnet_mapper[0].weight, std=0.02) # conditionings
136
+ # nn.init.normal_(self.effnet_mapper[2].weight, std=0.02) # conditionings
137
+ # nn.init.normal_(self.pixels_mapper[0].weight, std=0.02) # conditionings
138
+ # nn.init.normal_(self.pixels_mapper[2].weight, std=0.02) # conditionings
139
+ # torch.nn.init.xavier_uniform_(self.embedding[1].weight, 0.02) # inputs
140
+ # nn.init.constant_(self.clf[1].weight, 0) # outputs
141
+ #
142
+ # # blocks
143
+ # for level_block in self.down_blocks + self.up_blocks:
144
+ # for block in level_block:
145
+ # if isinstance(block, ResBlock) or isinstance(block, FeedForwardBlock):
146
+ # block.channelwise[-1].weight.data *= np.sqrt(1 / sum(blocks[0]))
147
+ # elif isinstance(block, TimestepBlock):
148
+ # for layer in block.modules():
149
+ # if isinstance(layer, nn.Linear):
150
+ # nn.init.constant_(layer.weight, 0)
151
+ #
152
+ # def _init_weights(self, m):
153
+ # if isinstance(m, (nn.Conv2d, nn.Linear)):
154
+ # torch.nn.init.xavier_uniform_(m.weight)
155
+ # if m.bias is not None:
156
+ # nn.init.constant_(m.bias, 0)
157
+
158
+ def gen_r_embedding(self, r, max_positions=10000):
159
+ r = r * max_positions
160
+ half_dim = self.c_r // 2
161
+ emb = math.log(max_positions) / (half_dim - 1)
162
+ emb = torch.arange(half_dim, device=r.device).float().mul(-emb).exp()
163
+ emb = r[:, None] * emb[None, :]
164
+ emb = torch.cat([emb.sin(), emb.cos()], dim=1)
165
+ if self.c_r % 2 == 1: # zero pad
166
+ emb = nn.functional.pad(emb, (0, 1), mode='constant')
167
+ return emb
168
+
169
+ def gen_c_embeddings(self, clip):
170
+ if len(clip.shape) == 2:
171
+ clip = clip.unsqueeze(1)
172
+ clip = self.clip_mapper(clip).view(clip.size(0), clip.size(1) * self.c_clip_seq, -1)
173
+ clip = self.clip_norm(clip)
174
+ return clip
175
+
176
+ def _down_encode(self, x, r_embed, clip, transformer_options={}):
177
+ level_outputs = []
178
+ block_group = zip(self.down_blocks, self.down_downscalers, self.down_repeat_mappers)
179
+ for down_block, downscaler, repmap in block_group:
180
+ x = downscaler(x)
181
+ for i in range(len(repmap) + 1):
182
+ for block in down_block:
183
+ if isinstance(block, ResBlock) or (
184
+ hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
185
+ ResBlock)):
186
+ x = block(x)
187
+ elif isinstance(block, AttnBlock) or (
188
+ hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
189
+ AttnBlock)):
190
+ x = block(x, clip, transformer_options=transformer_options)
191
+ elif isinstance(block, TimestepBlock) or (
192
+ hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
193
+ TimestepBlock)):
194
+ x = block(x, r_embed)
195
+ else:
196
+ x = block(x)
197
+ if i < len(repmap):
198
+ x = repmap[i](x)
199
+ level_outputs.insert(0, x)
200
+ return level_outputs
201
+
202
+ def _up_decode(self, level_outputs, r_embed, clip, transformer_options={}):
203
+ x = level_outputs[0]
204
+ block_group = zip(self.up_blocks, self.up_upscalers, self.up_repeat_mappers)
205
+ for i, (up_block, upscaler, repmap) in enumerate(block_group):
206
+ for j in range(len(repmap) + 1):
207
+ for k, block in enumerate(up_block):
208
+ if isinstance(block, ResBlock) or (
209
+ hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
210
+ ResBlock)):
211
+ skip = level_outputs[i] if k == 0 and i > 0 else None
212
+ if skip is not None and (x.size(-1) != skip.size(-1) or x.size(-2) != skip.size(-2)):
213
+ x = torch.nn.functional.interpolate(x, skip.shape[-2:], mode='bilinear',
214
+ align_corners=True)
215
+ x = block(x, skip)
216
+ elif isinstance(block, AttnBlock) or (
217
+ hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
218
+ AttnBlock)):
219
+ x = block(x, clip, transformer_options=transformer_options)
220
+ elif isinstance(block, TimestepBlock) or (
221
+ hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
222
+ TimestepBlock)):
223
+ x = block(x, r_embed)
224
+ else:
225
+ x = block(x)
226
+ if j < len(repmap):
227
+ x = repmap[j](x)
228
+ x = upscaler(x)
229
+ return x
230
+
231
+ def forward(self, x, r, effnet, clip, pixels=None, transformer_options={}, **kwargs):
232
+ if pixels is None:
233
+ pixels = x.new_zeros(x.size(0), 3, 8, 8)
234
+
235
+ # Process the conditioning embeddings
236
+ r_embed = self.gen_r_embedding(r).to(dtype=x.dtype)
237
+ for c in self.t_conds:
238
+ t_cond = kwargs.get(c, torch.zeros_like(r))
239
+ r_embed = torch.cat([r_embed, self.gen_r_embedding(t_cond).to(dtype=x.dtype)], dim=1)
240
+ clip = self.gen_c_embeddings(clip)
241
+
242
+ # Model Blocks
243
+ x = self.embedding(x)
244
+ x = x + self.effnet_mapper(
245
+ nn.functional.interpolate(effnet, size=x.shape[-2:], mode='bilinear', align_corners=True))
246
+ x = x + nn.functional.interpolate(self.pixels_mapper(pixels), size=x.shape[-2:], mode='bilinear',
247
+ align_corners=True)
248
+ level_outputs = self._down_encode(x, r_embed, clip, transformer_options=transformer_options)
249
+ x = self._up_decode(level_outputs, r_embed, clip, transformer_options=transformer_options)
250
+ return self.clf(x)
251
+
252
+ def update_weights_ema(self, src_model, beta=0.999):
253
+ for self_params, src_params in zip(self.parameters(), src_model.parameters()):
254
+ self_params.data = self_params.data * beta + src_params.data.clone().to(self_params.device) * (1 - beta)
255
+ for self_buffers, src_buffers in zip(self.buffers(), src_model.buffers()):
256
+ self_buffers.data = self_buffers.data * beta + src_buffers.data.clone().to(self_buffers.device) * (1 - beta)
ComfyUI/comfy/ldm/cascade/stage_c.py ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This file is part of ComfyUI.
3
+ Copyright (C) 2024 Stability AI
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ """
18
+
19
+ import torch
20
+ from torch import nn
21
+ import math
22
+ from .common import AttnBlock, LayerNorm2d_op, ResBlock, FeedForwardBlock, TimestepBlock
23
+ # from .controlnet import ControlNetDeliverer
24
+
25
+ class UpDownBlock2d(nn.Module):
26
+ def __init__(self, c_in, c_out, mode, enabled=True, dtype=None, device=None, operations=None):
27
+ super().__init__()
28
+ assert mode in ['up', 'down']
29
+ interpolation = nn.Upsample(scale_factor=2 if mode == 'up' else 0.5, mode='bilinear',
30
+ align_corners=True) if enabled else nn.Identity()
31
+ mapping = operations.Conv2d(c_in, c_out, kernel_size=1, dtype=dtype, device=device)
32
+ self.blocks = nn.ModuleList([interpolation, mapping] if mode == 'up' else [mapping, interpolation])
33
+
34
+ def forward(self, x):
35
+ for block in self.blocks:
36
+ x = block(x)
37
+ return x
38
+
39
+
40
+ class StageC(nn.Module):
41
+ def __init__(self, c_in=16, c_out=16, c_r=64, patch_size=1, c_cond=2048, c_hidden=[2048, 2048], nhead=[32, 32],
42
+ blocks=[[8, 24], [24, 8]], block_repeat=[[1, 1], [1, 1]], level_config=['CTA', 'CTA'],
43
+ c_clip_text=1280, c_clip_text_pooled=1280, c_clip_img=768, c_clip_seq=4, kernel_size=3,
44
+ dropout=[0.0, 0.0], self_attn=True, t_conds=['sca', 'crp'], switch_level=[False], stable_cascade_stage=None,
45
+ dtype=None, device=None, operations=None):
46
+ super().__init__()
47
+ self.dtype = dtype
48
+ self.c_r = c_r
49
+ self.t_conds = t_conds
50
+ self.c_clip_seq = c_clip_seq
51
+ if not isinstance(dropout, list):
52
+ dropout = [dropout] * len(c_hidden)
53
+ if not isinstance(self_attn, list):
54
+ self_attn = [self_attn] * len(c_hidden)
55
+
56
+ # CONDITIONING
57
+ self.clip_txt_mapper = operations.Linear(c_clip_text, c_cond, dtype=dtype, device=device)
58
+ self.clip_txt_pooled_mapper = operations.Linear(c_clip_text_pooled, c_cond * c_clip_seq, dtype=dtype, device=device)
59
+ self.clip_img_mapper = operations.Linear(c_clip_img, c_cond * c_clip_seq, dtype=dtype, device=device)
60
+ self.clip_norm = operations.LayerNorm(c_cond, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
61
+
62
+ self.embedding = nn.Sequential(
63
+ nn.PixelUnshuffle(patch_size),
64
+ operations.Conv2d(c_in * (patch_size ** 2), c_hidden[0], kernel_size=1, dtype=dtype, device=device),
65
+ LayerNorm2d_op(operations)(c_hidden[0], elementwise_affine=False, eps=1e-6)
66
+ )
67
+
68
+ def get_block(block_type, c_hidden, nhead, c_skip=0, dropout=0, self_attn=True):
69
+ if block_type == 'C':
70
+ return ResBlock(c_hidden, c_skip, kernel_size=kernel_size, dropout=dropout, dtype=dtype, device=device, operations=operations)
71
+ elif block_type == 'A':
72
+ return AttnBlock(c_hidden, c_cond, nhead, self_attn=self_attn, dropout=dropout, dtype=dtype, device=device, operations=operations)
73
+ elif block_type == 'F':
74
+ return FeedForwardBlock(c_hidden, dropout=dropout, dtype=dtype, device=device, operations=operations)
75
+ elif block_type == 'T':
76
+ return TimestepBlock(c_hidden, c_r, conds=t_conds, dtype=dtype, device=device, operations=operations)
77
+ else:
78
+ raise Exception(f'Block type {block_type} not supported')
79
+
80
+ # BLOCKS
81
+ # -- down blocks
82
+ self.down_blocks = nn.ModuleList()
83
+ self.down_downscalers = nn.ModuleList()
84
+ self.down_repeat_mappers = nn.ModuleList()
85
+ for i in range(len(c_hidden)):
86
+ if i > 0:
87
+ self.down_downscalers.append(nn.Sequential(
88
+ LayerNorm2d_op(operations)(c_hidden[i - 1], elementwise_affine=False, eps=1e-6),
89
+ UpDownBlock2d(c_hidden[i - 1], c_hidden[i], mode='down', enabled=switch_level[i - 1], dtype=dtype, device=device, operations=operations)
90
+ ))
91
+ else:
92
+ self.down_downscalers.append(nn.Identity())
93
+ down_block = nn.ModuleList()
94
+ for _ in range(blocks[0][i]):
95
+ for block_type in level_config[i]:
96
+ block = get_block(block_type, c_hidden[i], nhead[i], dropout=dropout[i], self_attn=self_attn[i])
97
+ down_block.append(block)
98
+ self.down_blocks.append(down_block)
99
+ if block_repeat is not None:
100
+ block_repeat_mappers = nn.ModuleList()
101
+ for _ in range(block_repeat[0][i] - 1):
102
+ block_repeat_mappers.append(operations.Conv2d(c_hidden[i], c_hidden[i], kernel_size=1, dtype=dtype, device=device))
103
+ self.down_repeat_mappers.append(block_repeat_mappers)
104
+
105
+ # -- up blocks
106
+ self.up_blocks = nn.ModuleList()
107
+ self.up_upscalers = nn.ModuleList()
108
+ self.up_repeat_mappers = nn.ModuleList()
109
+ for i in reversed(range(len(c_hidden))):
110
+ if i > 0:
111
+ self.up_upscalers.append(nn.Sequential(
112
+ LayerNorm2d_op(operations)(c_hidden[i], elementwise_affine=False, eps=1e-6),
113
+ UpDownBlock2d(c_hidden[i], c_hidden[i - 1], mode='up', enabled=switch_level[i - 1], dtype=dtype, device=device, operations=operations)
114
+ ))
115
+ else:
116
+ self.up_upscalers.append(nn.Identity())
117
+ up_block = nn.ModuleList()
118
+ for j in range(blocks[1][::-1][i]):
119
+ for k, block_type in enumerate(level_config[i]):
120
+ c_skip = c_hidden[i] if i < len(c_hidden) - 1 and j == k == 0 else 0
121
+ block = get_block(block_type, c_hidden[i], nhead[i], c_skip=c_skip, dropout=dropout[i],
122
+ self_attn=self_attn[i])
123
+ up_block.append(block)
124
+ self.up_blocks.append(up_block)
125
+ if block_repeat is not None:
126
+ block_repeat_mappers = nn.ModuleList()
127
+ for _ in range(block_repeat[1][::-1][i] - 1):
128
+ block_repeat_mappers.append(operations.Conv2d(c_hidden[i], c_hidden[i], kernel_size=1, dtype=dtype, device=device))
129
+ self.up_repeat_mappers.append(block_repeat_mappers)
130
+
131
+ # OUTPUT
132
+ self.clf = nn.Sequential(
133
+ LayerNorm2d_op(operations)(c_hidden[0], elementwise_affine=False, eps=1e-6, dtype=dtype, device=device),
134
+ operations.Conv2d(c_hidden[0], c_out * (patch_size ** 2), kernel_size=1, dtype=dtype, device=device),
135
+ nn.PixelShuffle(patch_size),
136
+ )
137
+
138
+ # --- WEIGHT INIT ---
139
+ # self.apply(self._init_weights) # General init
140
+ # nn.init.normal_(self.clip_txt_mapper.weight, std=0.02) # conditionings
141
+ # nn.init.normal_(self.clip_txt_pooled_mapper.weight, std=0.02) # conditionings
142
+ # nn.init.normal_(self.clip_img_mapper.weight, std=0.02) # conditionings
143
+ # torch.nn.init.xavier_uniform_(self.embedding[1].weight, 0.02) # inputs
144
+ # nn.init.constant_(self.clf[1].weight, 0) # outputs
145
+ #
146
+ # # blocks
147
+ # for level_block in self.down_blocks + self.up_blocks:
148
+ # for block in level_block:
149
+ # if isinstance(block, ResBlock) or isinstance(block, FeedForwardBlock):
150
+ # block.channelwise[-1].weight.data *= np.sqrt(1 / sum(blocks[0]))
151
+ # elif isinstance(block, TimestepBlock):
152
+ # for layer in block.modules():
153
+ # if isinstance(layer, nn.Linear):
154
+ # nn.init.constant_(layer.weight, 0)
155
+ #
156
+ # def _init_weights(self, m):
157
+ # if isinstance(m, (nn.Conv2d, nn.Linear)):
158
+ # torch.nn.init.xavier_uniform_(m.weight)
159
+ # if m.bias is not None:
160
+ # nn.init.constant_(m.bias, 0)
161
+
162
+ def gen_r_embedding(self, r, max_positions=10000):
163
+ r = r * max_positions
164
+ half_dim = self.c_r // 2
165
+ emb = math.log(max_positions) / (half_dim - 1)
166
+ emb = torch.arange(half_dim, device=r.device).float().mul(-emb).exp()
167
+ emb = r[:, None] * emb[None, :]
168
+ emb = torch.cat([emb.sin(), emb.cos()], dim=1)
169
+ if self.c_r % 2 == 1: # zero pad
170
+ emb = nn.functional.pad(emb, (0, 1), mode='constant')
171
+ return emb
172
+
173
+ def gen_c_embeddings(self, clip_txt, clip_txt_pooled, clip_img):
174
+ clip_txt = self.clip_txt_mapper(clip_txt)
175
+ if len(clip_txt_pooled.shape) == 2:
176
+ clip_txt_pooled = clip_txt_pooled.unsqueeze(1)
177
+ if len(clip_img.shape) == 2:
178
+ clip_img = clip_img.unsqueeze(1)
179
+ clip_txt_pool = self.clip_txt_pooled_mapper(clip_txt_pooled).view(clip_txt_pooled.size(0), clip_txt_pooled.size(1) * self.c_clip_seq, -1)
180
+ clip_img = self.clip_img_mapper(clip_img).view(clip_img.size(0), clip_img.size(1) * self.c_clip_seq, -1)
181
+ clip = torch.cat([clip_txt, clip_txt_pool, clip_img], dim=1)
182
+ clip = self.clip_norm(clip)
183
+ return clip
184
+
185
+ def _down_encode(self, x, r_embed, clip, cnet=None, transformer_options={}):
186
+ level_outputs = []
187
+ block_group = zip(self.down_blocks, self.down_downscalers, self.down_repeat_mappers)
188
+ for down_block, downscaler, repmap in block_group:
189
+ x = downscaler(x)
190
+ for i in range(len(repmap) + 1):
191
+ for block in down_block:
192
+ if isinstance(block, ResBlock) or (
193
+ hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
194
+ ResBlock)):
195
+ if cnet is not None:
196
+ next_cnet = cnet.pop()
197
+ if next_cnet is not None:
198
+ x = x + nn.functional.interpolate(next_cnet, size=x.shape[-2:], mode='bilinear',
199
+ align_corners=True).to(x.dtype)
200
+ x = block(x)
201
+ elif isinstance(block, AttnBlock) or (
202
+ hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
203
+ AttnBlock)):
204
+ x = block(x, clip, transformer_options=transformer_options)
205
+ elif isinstance(block, TimestepBlock) or (
206
+ hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
207
+ TimestepBlock)):
208
+ x = block(x, r_embed)
209
+ else:
210
+ x = block(x)
211
+ if i < len(repmap):
212
+ x = repmap[i](x)
213
+ level_outputs.insert(0, x)
214
+ return level_outputs
215
+
216
+ def _up_decode(self, level_outputs, r_embed, clip, cnet=None, transformer_options={}):
217
+ x = level_outputs[0]
218
+ block_group = zip(self.up_blocks, self.up_upscalers, self.up_repeat_mappers)
219
+ for i, (up_block, upscaler, repmap) in enumerate(block_group):
220
+ for j in range(len(repmap) + 1):
221
+ for k, block in enumerate(up_block):
222
+ if isinstance(block, ResBlock) or (
223
+ hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
224
+ ResBlock)):
225
+ skip = level_outputs[i] if k == 0 and i > 0 else None
226
+ if skip is not None and (x.size(-1) != skip.size(-1) or x.size(-2) != skip.size(-2)):
227
+ x = torch.nn.functional.interpolate(x, skip.shape[-2:], mode='bilinear',
228
+ align_corners=True)
229
+ if cnet is not None:
230
+ next_cnet = cnet.pop()
231
+ if next_cnet is not None:
232
+ x = x + nn.functional.interpolate(next_cnet, size=x.shape[-2:], mode='bilinear',
233
+ align_corners=True).to(x.dtype)
234
+ x = block(x, skip)
235
+ elif isinstance(block, AttnBlock) or (
236
+ hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
237
+ AttnBlock)):
238
+ x = block(x, clip, transformer_options=transformer_options)
239
+ elif isinstance(block, TimestepBlock) or (
240
+ hasattr(block, '_fsdp_wrapped_module') and isinstance(block._fsdp_wrapped_module,
241
+ TimestepBlock)):
242
+ x = block(x, r_embed)
243
+ else:
244
+ x = block(x)
245
+ if j < len(repmap):
246
+ x = repmap[j](x)
247
+ x = upscaler(x)
248
+ return x
249
+
250
+ def forward(self, x, r, clip_text, clip_text_pooled, clip_img, control=None, transformer_options={}, **kwargs):
251
+ # Process the conditioning embeddings
252
+ r_embed = self.gen_r_embedding(r).to(dtype=x.dtype)
253
+ for c in self.t_conds:
254
+ t_cond = kwargs.get(c, torch.zeros_like(r))
255
+ r_embed = torch.cat([r_embed, self.gen_r_embedding(t_cond).to(dtype=x.dtype)], dim=1)
256
+ clip = self.gen_c_embeddings(clip_text, clip_text_pooled, clip_img)
257
+
258
+ if control is not None:
259
+ cnet = control.get("input")
260
+ else:
261
+ cnet = None
262
+
263
+ # Model Blocks
264
+ x = self.embedding(x)
265
+ level_outputs = self._down_encode(x, r_embed, clip, cnet, transformer_options=transformer_options)
266
+ x = self._up_decode(level_outputs, r_embed, clip, cnet, transformer_options=transformer_options)
267
+ return self.clf(x)
268
+
269
+ def update_weights_ema(self, src_model, beta=0.999):
270
+ for self_params, src_params in zip(self.parameters(), src_model.parameters()):
271
+ self_params.data = self_params.data * beta + src_params.data.clone().to(self_params.device) * (1 - beta)
272
+ for self_buffers, src_buffers in zip(self.buffers(), src_model.buffers()):
273
+ self_buffers.data = self_buffers.data * beta + src_buffers.data.clone().to(self_buffers.device) * (1 - beta)
ComfyUI/comfy/ldm/cascade/stage_c_coder.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ This file is part of ComfyUI.
3
+ Copyright (C) 2024 Stability AI
4
+
5
+ This program is free software: you can redistribute it and/or modify
6
+ it under the terms of the GNU General Public License as published by
7
+ the Free Software Foundation, either version 3 of the License, or
8
+ (at your option) any later version.
9
+
10
+ This program is distributed in the hope that it will be useful,
11
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License
16
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
17
+ """
18
+ import torch
19
+ import torchvision
20
+ from torch import nn
21
+
22
+ import comfy.ops
23
+
24
+ ops = comfy.ops.disable_weight_init
25
+
26
+ # EfficientNet
27
+ class EfficientNetEncoder(nn.Module):
28
+ def __init__(self, c_latent=16):
29
+ super().__init__()
30
+ self.backbone = torchvision.models.efficientnet_v2_s().features.eval()
31
+ self.mapper = nn.Sequential(
32
+ ops.Conv2d(1280, c_latent, kernel_size=1, bias=False),
33
+ nn.BatchNorm2d(c_latent, affine=False), # then normalize them to have mean 0 and std 1
34
+ )
35
+ self.mean = nn.Parameter(torch.tensor([0.485, 0.456, 0.406]))
36
+ self.std = nn.Parameter(torch.tensor([0.229, 0.224, 0.225]))
37
+
38
+ def forward(self, x):
39
+ x = x * 0.5 + 0.5
40
+ x = (x - self.mean.view([3,1,1]).to(device=x.device, dtype=x.dtype)) / self.std.view([3,1,1]).to(device=x.device, dtype=x.dtype)
41
+ o = self.mapper(self.backbone(x))
42
+ return o
43
+
44
+
45
+ # Fast Decoder for Stage C latents. E.g. 16 x 24 x 24 -> 3 x 192 x 192
46
+ class Previewer(nn.Module):
47
+ def __init__(self, c_in=16, c_hidden=512, c_out=3):
48
+ super().__init__()
49
+ self.blocks = nn.Sequential(
50
+ ops.Conv2d(c_in, c_hidden, kernel_size=1), # 16 channels to 512 channels
51
+ nn.GELU(),
52
+ nn.BatchNorm2d(c_hidden),
53
+
54
+ ops.Conv2d(c_hidden, c_hidden, kernel_size=3, padding=1),
55
+ nn.GELU(),
56
+ nn.BatchNorm2d(c_hidden),
57
+
58
+ ops.ConvTranspose2d(c_hidden, c_hidden // 2, kernel_size=2, stride=2), # 16 -> 32
59
+ nn.GELU(),
60
+ nn.BatchNorm2d(c_hidden // 2),
61
+
62
+ ops.Conv2d(c_hidden // 2, c_hidden // 2, kernel_size=3, padding=1),
63
+ nn.GELU(),
64
+ nn.BatchNorm2d(c_hidden // 2),
65
+
66
+ ops.ConvTranspose2d(c_hidden // 2, c_hidden // 4, kernel_size=2, stride=2), # 32 -> 64
67
+ nn.GELU(),
68
+ nn.BatchNorm2d(c_hidden // 4),
69
+
70
+ ops.Conv2d(c_hidden // 4, c_hidden // 4, kernel_size=3, padding=1),
71
+ nn.GELU(),
72
+ nn.BatchNorm2d(c_hidden // 4),
73
+
74
+ ops.ConvTranspose2d(c_hidden // 4, c_hidden // 4, kernel_size=2, stride=2), # 64 -> 128
75
+ nn.GELU(),
76
+ nn.BatchNorm2d(c_hidden // 4),
77
+
78
+ ops.Conv2d(c_hidden // 4, c_hidden // 4, kernel_size=3, padding=1),
79
+ nn.GELU(),
80
+ nn.BatchNorm2d(c_hidden // 4),
81
+
82
+ ops.Conv2d(c_hidden // 4, c_out, kernel_size=1),
83
+ )
84
+
85
+ def forward(self, x):
86
+ return (self.blocks(x) - 0.5) * 2.0
87
+
88
+ class StageC_coder(nn.Module):
89
+ def __init__(self):
90
+ super().__init__()
91
+ self.previewer = Previewer()
92
+ self.encoder = EfficientNetEncoder()
93
+
94
+ def encode(self, x):
95
+ return self.encoder(x)
96
+
97
+ def decode(self, x):
98
+ return self.previewer(x)
ComfyUI/comfy/ldm/chroma/layers.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import Tensor, nn
3
+
4
+ from comfy.ldm.flux.layers import (
5
+ MLPEmbedder,
6
+ ModulationOut,
7
+ )
8
+
9
+ # TODO: remove this in a few months
10
+ SingleStreamBlock = None
11
+ DoubleStreamBlock = None
12
+
13
+
14
+ class ChromaModulationOut(ModulationOut):
15
+ @classmethod
16
+ def from_offset(cls, tensor: torch.Tensor, offset: int = 0) -> ModulationOut:
17
+ return cls(
18
+ shift=tensor[:, offset : offset + 1, :],
19
+ scale=tensor[:, offset + 1 : offset + 2, :],
20
+ gate=tensor[:, offset + 2 : offset + 3, :],
21
+ )
22
+
23
+
24
+
25
+
26
+ class Approximator(nn.Module):
27
+ def __init__(self, in_dim: int, out_dim: int, hidden_dim: int, n_layers = 5, dtype=None, device=None, operations=None):
28
+ super().__init__()
29
+ self.in_proj = operations.Linear(in_dim, hidden_dim, bias=True, dtype=dtype, device=device)
30
+ self.layers = nn.ModuleList([MLPEmbedder(hidden_dim, hidden_dim, dtype=dtype, device=device, operations=operations) for x in range( n_layers)])
31
+ self.norms = nn.ModuleList([operations.RMSNorm(hidden_dim, dtype=dtype, device=device) for x in range( n_layers)])
32
+ self.out_proj = operations.Linear(hidden_dim, out_dim, dtype=dtype, device=device)
33
+
34
+ @property
35
+ def device(self):
36
+ # Get the device of the module (assumes all parameters are on the same device)
37
+ return next(self.parameters()).device
38
+
39
+ def forward(self, x: Tensor) -> Tensor:
40
+ x = self.in_proj(x)
41
+
42
+ for layer, norms in zip(self.layers, self.norms):
43
+ x = x + layer(norms(x))
44
+
45
+ x = self.out_proj(x)
46
+
47
+ return x
48
+
49
+
50
+ class LastLayer(nn.Module):
51
+ def __init__(self, hidden_size: int, patch_size: int, out_channels: int, dtype=None, device=None, operations=None):
52
+ super().__init__()
53
+ self.norm_final = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
54
+ self.linear = operations.Linear(hidden_size, out_channels, bias=True, dtype=dtype, device=device)
55
+
56
+ def forward(self, x: Tensor, vec: Tensor) -> Tensor:
57
+ shift, scale = vec
58
+ shift = shift.squeeze(1)
59
+ scale = scale.squeeze(1)
60
+ x = torch.addcmul(shift[:, None, :], 1 + scale[:, None, :], self.norm_final(x))
61
+ x = self.linear(x)
62
+ return x
ComfyUI/comfy/ldm/chroma/model.py ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #Original code can be found on: https://github.com/black-forest-labs/flux
2
+
3
+ from dataclasses import dataclass
4
+
5
+ import torch
6
+ from torch import Tensor, nn
7
+ from einops import rearrange, repeat
8
+ import comfy.patcher_extension
9
+ import comfy.ldm.common_dit
10
+
11
+ from comfy.ldm.flux.layers import (
12
+ EmbedND,
13
+ timestep_embedding,
14
+ DoubleStreamBlock,
15
+ SingleStreamBlock,
16
+ )
17
+
18
+ from .layers import (
19
+ LastLayer,
20
+ Approximator,
21
+ ChromaModulationOut,
22
+ )
23
+
24
+
25
+ @dataclass
26
+ class ChromaParams:
27
+ in_channels: int
28
+ out_channels: int
29
+ context_in_dim: int
30
+ hidden_size: int
31
+ mlp_ratio: float
32
+ num_heads: int
33
+ depth: int
34
+ depth_single_blocks: int
35
+ axes_dim: list
36
+ theta: int
37
+ patch_size: int
38
+ qkv_bias: bool
39
+ in_dim: int
40
+ out_dim: int
41
+ hidden_dim: int
42
+ n_layers: int
43
+ txt_ids_dims: list
44
+ vec_in_dim: int
45
+
46
+
47
+
48
+ class Chroma(nn.Module):
49
+ """
50
+ Transformer model for flow matching on sequences.
51
+ """
52
+
53
+ def __init__(self, image_model=None, final_layer=True, dtype=None, device=None, operations=None, **kwargs):
54
+ super().__init__()
55
+ self.dtype = dtype
56
+ params = ChromaParams(**kwargs)
57
+ self.params = params
58
+ self.patch_size = params.patch_size
59
+ self.in_channels = params.in_channels
60
+ self.out_channels = params.out_channels
61
+ if params.hidden_size % params.num_heads != 0:
62
+ raise ValueError(
63
+ f"Hidden size {params.hidden_size} must be divisible by num_heads {params.num_heads}"
64
+ )
65
+ pe_dim = params.hidden_size // params.num_heads
66
+ if sum(params.axes_dim) != pe_dim:
67
+ raise ValueError(f"Got {params.axes_dim} but expected positional dim {pe_dim}")
68
+ self.hidden_size = params.hidden_size
69
+ self.num_heads = params.num_heads
70
+ self.in_dim = params.in_dim
71
+ self.out_dim = params.out_dim
72
+ self.hidden_dim = params.hidden_dim
73
+ self.n_layers = params.n_layers
74
+ self.pe_embedder = EmbedND(dim=pe_dim, theta=params.theta, axes_dim=params.axes_dim)
75
+ self.img_in = operations.Linear(self.in_channels, self.hidden_size, bias=True, dtype=dtype, device=device)
76
+ self.txt_in = operations.Linear(params.context_in_dim, self.hidden_size, dtype=dtype, device=device)
77
+ # set as nn identity for now, will overwrite it later.
78
+ self.distilled_guidance_layer = Approximator(
79
+ in_dim=self.in_dim,
80
+ hidden_dim=self.hidden_dim,
81
+ out_dim=self.out_dim,
82
+ n_layers=self.n_layers,
83
+ dtype=dtype, device=device, operations=operations
84
+ )
85
+
86
+
87
+ self.double_blocks = nn.ModuleList(
88
+ [
89
+ DoubleStreamBlock(
90
+ self.hidden_size,
91
+ self.num_heads,
92
+ mlp_ratio=params.mlp_ratio,
93
+ qkv_bias=params.qkv_bias,
94
+ modulation=False,
95
+ dtype=dtype, device=device, operations=operations
96
+ )
97
+ for _ in range(params.depth)
98
+ ]
99
+ )
100
+
101
+ self.single_blocks = nn.ModuleList(
102
+ [
103
+ SingleStreamBlock(self.hidden_size, self.num_heads, mlp_ratio=params.mlp_ratio, modulation=False, dtype=dtype, device=device, operations=operations)
104
+ for _ in range(params.depth_single_blocks)
105
+ ]
106
+ )
107
+
108
+ if final_layer:
109
+ self.final_layer = LastLayer(self.hidden_size, 1, self.out_channels, dtype=dtype, device=device, operations=operations)
110
+
111
+ self.skip_mmdit = []
112
+ self.skip_dit = []
113
+ self.lite = False
114
+
115
+ def get_modulations(self, tensor: torch.Tensor, block_type: str, *, idx: int = 0):
116
+ # This function slices up the modulations tensor which has the following layout:
117
+ # single : num_single_blocks * 3 elements
118
+ # double_img : num_double_blocks * 6 elements
119
+ # double_txt : num_double_blocks * 6 elements
120
+ # final : 2 elements
121
+ if block_type == "final":
122
+ return (tensor[:, -2:-1, :], tensor[:, -1:, :])
123
+ single_block_count = self.params.depth_single_blocks
124
+ double_block_count = self.params.depth
125
+ offset = 3 * idx
126
+ if block_type == "single":
127
+ return ChromaModulationOut.from_offset(tensor, offset)
128
+ # Double block modulations are 6 elements so we double 3 * idx.
129
+ offset *= 2
130
+ if block_type in {"double_img", "double_txt"}:
131
+ # Advance past the single block modulations.
132
+ offset += 3 * single_block_count
133
+ if block_type == "double_txt":
134
+ # Advance past the double block img modulations.
135
+ offset += 6 * double_block_count
136
+ return (
137
+ ChromaModulationOut.from_offset(tensor, offset),
138
+ ChromaModulationOut.from_offset(tensor, offset + 3),
139
+ )
140
+ raise ValueError("Bad block_type")
141
+
142
+
143
+ def forward_orig(
144
+ self,
145
+ img: Tensor,
146
+ img_ids: Tensor,
147
+ txt: Tensor,
148
+ txt_ids: Tensor,
149
+ timesteps: Tensor,
150
+ guidance: Tensor = None,
151
+ control = None,
152
+ transformer_options={},
153
+ attn_mask: Tensor = None,
154
+ ) -> Tensor:
155
+ transformer_options = transformer_options.copy()
156
+ patches_replace = transformer_options.get("patches_replace", {})
157
+
158
+ # running on sequences img
159
+ img = self.img_in(img)
160
+
161
+ # distilled vector guidance
162
+ mod_index_length = 344
163
+ distill_timestep = timestep_embedding(timesteps.detach().clone(), 16).to(img.device, img.dtype)
164
+ # guidance = guidance *
165
+ distil_guidance = timestep_embedding(guidance.detach().clone(), 16).to(img.device, img.dtype)
166
+
167
+ # get all modulation index
168
+ modulation_index = timestep_embedding(torch.arange(mod_index_length, device=img.device), 32).to(img.device, img.dtype)
169
+ # we need to broadcast the modulation index here so each batch has all of the index
170
+ modulation_index = modulation_index.unsqueeze(0).repeat(img.shape[0], 1, 1).to(img.device, img.dtype)
171
+ # and we need to broadcast timestep and guidance along too
172
+ timestep_guidance = torch.cat([distill_timestep, distil_guidance], dim=1).unsqueeze(1).repeat(1, mod_index_length, 1).to(img.dtype).to(img.device, img.dtype)
173
+ # then and only then we could concatenate it together
174
+ input_vec = torch.cat([timestep_guidance, modulation_index], dim=-1).to(img.device, img.dtype)
175
+
176
+ mod_vectors = self.distilled_guidance_layer(input_vec)
177
+
178
+ txt = self.txt_in(txt)
179
+
180
+ ids = torch.cat((txt_ids, img_ids), dim=1)
181
+ pe = self.pe_embedder(ids)
182
+
183
+ blocks_replace = patches_replace.get("dit", {})
184
+ transformer_options["total_blocks"] = len(self.double_blocks)
185
+ transformer_options["block_type"] = "double"
186
+ for i, block in enumerate(self.double_blocks):
187
+ transformer_options["block_index"] = i
188
+ if i not in self.skip_mmdit:
189
+ double_mod = (
190
+ self.get_modulations(mod_vectors, "double_img", idx=i),
191
+ self.get_modulations(mod_vectors, "double_txt", idx=i),
192
+ )
193
+ if ("double_block", i) in blocks_replace:
194
+ def block_wrap(args):
195
+ out = {}
196
+ out["img"], out["txt"] = block(img=args["img"],
197
+ txt=args["txt"],
198
+ vec=args["vec"],
199
+ pe=args["pe"],
200
+ attn_mask=args.get("attn_mask"),
201
+ transformer_options=args.get("transformer_options"))
202
+ return out
203
+
204
+ out = blocks_replace[("double_block", i)]({"img": img,
205
+ "txt": txt,
206
+ "vec": double_mod,
207
+ "pe": pe,
208
+ "attn_mask": attn_mask,
209
+ "transformer_options": transformer_options},
210
+ {"original_block": block_wrap})
211
+ txt = out["txt"]
212
+ img = out["img"]
213
+ else:
214
+ img, txt = block(img=img,
215
+ txt=txt,
216
+ vec=double_mod,
217
+ pe=pe,
218
+ attn_mask=attn_mask,
219
+ transformer_options=transformer_options)
220
+
221
+ if control is not None: # Controlnet
222
+ control_i = control.get("input")
223
+ if i < len(control_i):
224
+ add = control_i[i]
225
+ if add is not None:
226
+ img += add
227
+
228
+ img = torch.cat((txt, img), 1)
229
+
230
+ transformer_options["total_blocks"] = len(self.single_blocks)
231
+ transformer_options["block_type"] = "single"
232
+ transformer_options["img_slice"] = [txt.shape[1], img.shape[1]]
233
+ for i, block in enumerate(self.single_blocks):
234
+ transformer_options["block_index"] = i
235
+ if i not in self.skip_dit:
236
+ single_mod = self.get_modulations(mod_vectors, "single", idx=i)
237
+ if ("single_block", i) in blocks_replace:
238
+ def block_wrap(args):
239
+ out = {}
240
+ out["img"] = block(args["img"],
241
+ vec=args["vec"],
242
+ pe=args["pe"],
243
+ attn_mask=args.get("attn_mask"),
244
+ transformer_options=args.get("transformer_options"))
245
+ return out
246
+
247
+ out = blocks_replace[("single_block", i)]({"img": img,
248
+ "vec": single_mod,
249
+ "pe": pe,
250
+ "attn_mask": attn_mask,
251
+ "transformer_options": transformer_options},
252
+ {"original_block": block_wrap})
253
+ img = out["img"]
254
+ else:
255
+ img = block(img, vec=single_mod, pe=pe, attn_mask=attn_mask, transformer_options=transformer_options)
256
+
257
+ if control is not None: # Controlnet
258
+ control_o = control.get("output")
259
+ if i < len(control_o):
260
+ add = control_o[i]
261
+ if add is not None:
262
+ img[:, txt.shape[1] :, ...] += add
263
+
264
+ img = img[:, txt.shape[1] :, ...]
265
+ if hasattr(self, "final_layer"):
266
+ final_mod = self.get_modulations(mod_vectors, "final")
267
+ img = self.final_layer(img, vec=final_mod) # (N, T, patch_size ** 2 * out_channels)
268
+ return img
269
+
270
+ def forward(self, x, timestep, context, guidance, control=None, transformer_options={}, **kwargs):
271
+ return comfy.patcher_extension.WrapperExecutor.new_class_executor(
272
+ self._forward,
273
+ self,
274
+ comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, transformer_options)
275
+ ).execute(x, timestep, context, guidance, control, transformer_options, **kwargs)
276
+
277
+ def _forward(self, x, timestep, context, guidance, control=None, transformer_options={}, **kwargs):
278
+ bs, c, h, w = x.shape
279
+ x = comfy.ldm.common_dit.pad_to_patch_size(x, (self.patch_size, self.patch_size))
280
+
281
+ img = rearrange(x, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=self.patch_size, pw=self.patch_size)
282
+
283
+ if img.ndim != 3 or context.ndim != 3:
284
+ raise ValueError("Input img and txt tensors must have 3 dimensions.")
285
+
286
+ h_len = ((h + (self.patch_size // 2)) // self.patch_size)
287
+ w_len = ((w + (self.patch_size // 2)) // self.patch_size)
288
+ img_ids = torch.zeros((h_len, w_len, 3), device=x.device, dtype=x.dtype)
289
+ img_ids[:, :, 1] = img_ids[:, :, 1] + torch.linspace(0, h_len - 1, steps=h_len, device=x.device, dtype=x.dtype).unsqueeze(1)
290
+ img_ids[:, :, 2] = img_ids[:, :, 2] + torch.linspace(0, w_len - 1, steps=w_len, device=x.device, dtype=x.dtype).unsqueeze(0)
291
+ img_ids = repeat(img_ids, "h w c -> b (h w) c", b=bs)
292
+
293
+ txt_ids = torch.zeros((bs, context.shape[1], 3), device=x.device, dtype=x.dtype)
294
+ out = self.forward_orig(img, img_ids, context, txt_ids, timestep, guidance, control, transformer_options, attn_mask=kwargs.get("attention_mask", None))
295
+ return rearrange(out, "b (h w) (c ph pw) -> b c (h ph) (w pw)", h=h_len, w=w_len, ph=self.patch_size, pw=self.patch_size)[:,:,:h,:w]
ComfyUI/comfy/ldm/chroma_radiance/layers.py ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Adapted from https://github.com/lodestone-rock/flow
2
+ from functools import lru_cache
3
+
4
+ import torch
5
+ from torch import nn
6
+
7
+
8
+ class NerfEmbedder(nn.Module):
9
+ """
10
+ An embedder module that combines input features with a 2D positional
11
+ encoding that mimics the Discrete Cosine Transform (DCT).
12
+
13
+ This module takes an input tensor of shape (B, P^2, C), where P is the
14
+ patch size, and enriches it with positional information before projecting
15
+ it to a new hidden size.
16
+ """
17
+ def __init__(
18
+ self,
19
+ in_channels: int,
20
+ hidden_size_input: int,
21
+ max_freqs: int,
22
+ dtype=None,
23
+ device=None,
24
+ operations=None,
25
+ ):
26
+ """
27
+ Initializes the NerfEmbedder.
28
+
29
+ Args:
30
+ in_channels (int): The number of channels in the input tensor.
31
+ hidden_size_input (int): The desired dimension of the output embedding.
32
+ max_freqs (int): The number of frequency components to use for both
33
+ the x and y dimensions of the positional encoding.
34
+ The total number of positional features will be max_freqs^2.
35
+ """
36
+ super().__init__()
37
+ self.dtype = dtype
38
+ self.max_freqs = max_freqs
39
+ self.hidden_size_input = hidden_size_input
40
+
41
+ # A linear layer to project the concatenated input features and
42
+ # positional encodings to the final output dimension.
43
+ self.embedder = nn.Sequential(
44
+ operations.Linear(in_channels + max_freqs**2, hidden_size_input, dtype=dtype, device=device)
45
+ )
46
+
47
+ @lru_cache(maxsize=4)
48
+ def fetch_pos(self, patch_size: int, device: torch.device, dtype: torch.dtype) -> torch.Tensor:
49
+ """
50
+ Generates and caches 2D DCT-like positional embeddings for a given patch size.
51
+
52
+ The LRU cache is a performance optimization that avoids recomputing the
53
+ same positional grid on every forward pass.
54
+
55
+ Args:
56
+ patch_size (int): The side length of the square input patch.
57
+ device: The torch device to create the tensors on.
58
+ dtype: The torch dtype for the tensors.
59
+
60
+ Returns:
61
+ A tensor of shape (1, patch_size^2, max_freqs^2) containing the
62
+ positional embeddings.
63
+ """
64
+ # Create normalized 1D coordinate grids from 0 to 1.
65
+ pos_x = torch.linspace(0, 1, patch_size, device=device, dtype=dtype)
66
+ pos_y = torch.linspace(0, 1, patch_size, device=device, dtype=dtype)
67
+
68
+ # Create a 2D meshgrid of coordinates.
69
+ pos_y, pos_x = torch.meshgrid(pos_y, pos_x, indexing="ij")
70
+
71
+ # Reshape positions to be broadcastable with frequencies.
72
+ # Shape becomes (patch_size^2, 1, 1).
73
+ pos_x = pos_x.reshape(-1, 1, 1)
74
+ pos_y = pos_y.reshape(-1, 1, 1)
75
+
76
+ # Create a 1D tensor of frequency values from 0 to max_freqs-1.
77
+ freqs = torch.linspace(0, self.max_freqs - 1, self.max_freqs, dtype=dtype, device=device)
78
+
79
+ # Reshape frequencies to be broadcastable for creating 2D basis functions.
80
+ # freqs_x shape: (1, max_freqs, 1)
81
+ # freqs_y shape: (1, 1, max_freqs)
82
+ freqs_x = freqs[None, :, None]
83
+ freqs_y = freqs[None, None, :]
84
+
85
+ # A custom weighting coefficient, not part of standard DCT.
86
+ # This seems to down-weight the contribution of higher-frequency interactions.
87
+ coeffs = (1 + freqs_x * freqs_y) ** -1
88
+
89
+ # Calculate the 1D cosine basis functions for x and y coordinates.
90
+ # This is the core of the DCT formulation.
91
+ dct_x = torch.cos(pos_x * freqs_x * torch.pi)
92
+ dct_y = torch.cos(pos_y * freqs_y * torch.pi)
93
+
94
+ # Combine the 1D basis functions to create 2D basis functions by element-wise
95
+ # multiplication, and apply the custom coefficients. Broadcasting handles the
96
+ # combination of all (pos_x, freqs_x) with all (pos_y, freqs_y).
97
+ # The result is flattened into a feature vector for each position.
98
+ dct = (dct_x * dct_y * coeffs).view(1, -1, self.max_freqs ** 2)
99
+
100
+ return dct
101
+
102
+ def forward(self, inputs: torch.Tensor) -> torch.Tensor:
103
+ """
104
+ Forward pass for the embedder.
105
+
106
+ Args:
107
+ inputs (Tensor): The input tensor of shape (B, P^2, C).
108
+
109
+ Returns:
110
+ Tensor: The output tensor of shape (B, P^2, hidden_size_input).
111
+ """
112
+ # Get the batch size, number of pixels, and number of channels.
113
+ B, P2, C = inputs.shape
114
+
115
+ # Infer the patch side length from the number of pixels (P^2).
116
+ patch_size = int(P2 ** 0.5)
117
+
118
+ input_dtype = inputs.dtype
119
+ inputs = inputs.to(dtype=self.dtype)
120
+
121
+ # Fetch the pre-computed or cached positional embeddings.
122
+ dct = self.fetch_pos(patch_size, inputs.device, self.dtype)
123
+
124
+ # Repeat the positional embeddings for each item in the batch.
125
+ dct = dct.repeat(B, 1, 1)
126
+
127
+ # Concatenate the original input features with the positional embeddings
128
+ # along the feature dimension.
129
+ inputs = torch.cat((inputs, dct), dim=-1)
130
+
131
+ # Project the combined tensor to the target hidden size.
132
+ return self.embedder(inputs).to(dtype=input_dtype)
133
+
134
+
135
+ class NerfGLUBlock(nn.Module):
136
+ """
137
+ A NerfBlock using a Gated Linear Unit (GLU) like MLP.
138
+ """
139
+ def __init__(self, hidden_size_s: int, hidden_size_x: int, mlp_ratio, dtype=None, device=None, operations=None):
140
+ super().__init__()
141
+ # The total number of parameters for the MLP is increased to accommodate
142
+ # the gate, value, and output projection matrices.
143
+ # We now need to generate parameters for 3 matrices.
144
+ total_params = 3 * hidden_size_x**2 * mlp_ratio
145
+ self.param_generator = operations.Linear(hidden_size_s, total_params, dtype=dtype, device=device)
146
+ self.norm = operations.RMSNorm(hidden_size_x, dtype=dtype, device=device)
147
+ self.mlp_ratio = mlp_ratio
148
+
149
+
150
+ def forward(self, x: torch.Tensor, s: torch.Tensor) -> torch.Tensor:
151
+ batch_size, num_x, hidden_size_x = x.shape
152
+ mlp_params = self.param_generator(s)
153
+
154
+ # Split the generated parameters into three parts for the gate, value, and output projection.
155
+ fc1_gate_params, fc1_value_params, fc2_params = mlp_params.chunk(3, dim=-1)
156
+
157
+ # Reshape the parameters into matrices for batch matrix multiplication.
158
+ fc1_gate = fc1_gate_params.view(batch_size, hidden_size_x, hidden_size_x * self.mlp_ratio)
159
+ fc1_value = fc1_value_params.view(batch_size, hidden_size_x, hidden_size_x * self.mlp_ratio)
160
+ fc2 = fc2_params.view(batch_size, hidden_size_x * self.mlp_ratio, hidden_size_x)
161
+
162
+ # Normalize the generated weight matrices as in the original implementation.
163
+ fc1_gate = torch.nn.functional.normalize(fc1_gate, dim=-2)
164
+ fc1_value = torch.nn.functional.normalize(fc1_value, dim=-2)
165
+ fc2 = torch.nn.functional.normalize(fc2, dim=-2)
166
+
167
+ res_x = x
168
+ x = self.norm(x)
169
+
170
+ # Apply the final output projection.
171
+ x = torch.bmm(torch.nn.functional.silu(torch.bmm(x, fc1_gate)) * torch.bmm(x, fc1_value), fc2)
172
+
173
+ return x + res_x
174
+
175
+
176
+ class NerfFinalLayer(nn.Module):
177
+ def __init__(self, hidden_size, out_channels, dtype=None, device=None, operations=None):
178
+ super().__init__()
179
+ self.norm = operations.RMSNorm(hidden_size, dtype=dtype, device=device)
180
+ self.linear = operations.Linear(hidden_size, out_channels, dtype=dtype, device=device)
181
+
182
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
183
+ # RMSNorm normalizes over the last dimension, but our channel dim (C) is at dim=1.
184
+ # So we temporarily move the channel dimension to the end for the norm operation.
185
+ return self.linear(self.norm(x.movedim(1, -1))).movedim(-1, 1)
186
+
187
+
188
+ class NerfFinalLayerConv(nn.Module):
189
+ def __init__(self, hidden_size: int, out_channels: int, dtype=None, device=None, operations=None):
190
+ super().__init__()
191
+ self.norm = operations.RMSNorm(hidden_size, dtype=dtype, device=device)
192
+ self.conv = operations.Conv2d(
193
+ in_channels=hidden_size,
194
+ out_channels=out_channels,
195
+ kernel_size=3,
196
+ padding=1,
197
+ dtype=dtype,
198
+ device=device,
199
+ )
200
+
201
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
202
+ # RMSNorm normalizes over the last dimension, but our channel dim (C) is at dim=1.
203
+ # So we temporarily move the channel dimension to the end for the norm operation.
204
+ return self.conv(self.norm(x.movedim(1, -1)).movedim(-1, 1))
ComfyUI/comfy/ldm/chroma_radiance/model.py ADDED
@@ -0,0 +1,343 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Credits:
2
+ # Original Flux code can be found on: https://github.com/black-forest-labs/flux
3
+ # Chroma Radiance adaption referenced from https://github.com/lodestone-rock/flow
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Optional
7
+
8
+ import torch
9
+ from torch import Tensor, nn
10
+ from einops import repeat
11
+ import comfy.ldm.common_dit
12
+
13
+ from comfy.ldm.flux.layers import EmbedND, DoubleStreamBlock, SingleStreamBlock
14
+
15
+ from comfy.ldm.chroma.model import Chroma, ChromaParams
16
+ from comfy.ldm.chroma.layers import (
17
+ Approximator,
18
+ )
19
+ from .layers import (
20
+ NerfEmbedder,
21
+ NerfGLUBlock,
22
+ NerfFinalLayer,
23
+ NerfFinalLayerConv,
24
+ )
25
+
26
+
27
+ @dataclass
28
+ class ChromaRadianceParams(ChromaParams):
29
+ patch_size: int
30
+ nerf_hidden_size: int
31
+ nerf_mlp_ratio: int
32
+ nerf_depth: int
33
+ nerf_max_freqs: int
34
+ # Setting nerf_tile_size to 0 disables tiling.
35
+ nerf_tile_size: int
36
+ # Currently one of linear (legacy) or conv.
37
+ nerf_final_head_type: str
38
+ # None means use the same dtype as the model.
39
+ nerf_embedder_dtype: Optional[torch.dtype]
40
+ use_x0: bool
41
+ # Use sequential txt_ids instead of zeros
42
+ use_sequential_txt_ids: bool
43
+
44
+ class ChromaRadiance(Chroma):
45
+ """
46
+ Transformer model for flow matching on sequences.
47
+ """
48
+
49
+ def __init__(self, image_model=None, final_layer=True, dtype=None, device=None, operations=None, **kwargs):
50
+ if operations is None:
51
+ raise RuntimeError("Attempt to create ChromaRadiance object without setting operations")
52
+ nn.Module.__init__(self)
53
+ self.dtype = dtype
54
+ params = ChromaRadianceParams(**kwargs)
55
+ self.params = params
56
+ self.patch_size = params.patch_size
57
+ self.in_channels = params.in_channels
58
+ self.out_channels = params.out_channels
59
+ if params.hidden_size % params.num_heads != 0:
60
+ raise ValueError(
61
+ f"Hidden size {params.hidden_size} must be divisible by num_heads {params.num_heads}"
62
+ )
63
+ pe_dim = params.hidden_size // params.num_heads
64
+ if sum(params.axes_dim) != pe_dim:
65
+ raise ValueError(f"Got {params.axes_dim} but expected positional dim {pe_dim}")
66
+ self.hidden_size = params.hidden_size
67
+ self.num_heads = params.num_heads
68
+ self.in_dim = params.in_dim
69
+ self.out_dim = params.out_dim
70
+ self.hidden_dim = params.hidden_dim
71
+ self.n_layers = params.n_layers
72
+ self.pe_embedder = EmbedND(dim=pe_dim, theta=params.theta, axes_dim=params.axes_dim)
73
+ self.img_in_patch = operations.Conv2d(
74
+ params.in_channels,
75
+ params.hidden_size,
76
+ kernel_size=params.patch_size,
77
+ stride=params.patch_size,
78
+ bias=True,
79
+ dtype=dtype,
80
+ device=device,
81
+ )
82
+ self.txt_in = operations.Linear(params.context_in_dim, self.hidden_size, dtype=dtype, device=device)
83
+ # set as nn identity for now, will overwrite it later.
84
+ self.distilled_guidance_layer = Approximator(
85
+ in_dim=self.in_dim,
86
+ hidden_dim=self.hidden_dim,
87
+ out_dim=self.out_dim,
88
+ n_layers=self.n_layers,
89
+ dtype=dtype, device=device, operations=operations
90
+ )
91
+
92
+ self.double_blocks = nn.ModuleList(
93
+ [
94
+ DoubleStreamBlock(
95
+ self.hidden_size,
96
+ self.num_heads,
97
+ mlp_ratio=params.mlp_ratio,
98
+ qkv_bias=params.qkv_bias,
99
+ modulation=False,
100
+ dtype=dtype, device=device, operations=operations
101
+ )
102
+ for _ in range(params.depth)
103
+ ]
104
+ )
105
+
106
+ self.single_blocks = nn.ModuleList(
107
+ [
108
+ SingleStreamBlock(
109
+ self.hidden_size,
110
+ self.num_heads,
111
+ mlp_ratio=params.mlp_ratio,
112
+ modulation=False,
113
+ dtype=dtype, device=device, operations=operations,
114
+ )
115
+ for _ in range(params.depth_single_blocks)
116
+ ]
117
+ )
118
+
119
+ # pixel channel concat with DCT
120
+ self.nerf_image_embedder = NerfEmbedder(
121
+ in_channels=params.in_channels,
122
+ hidden_size_input=params.nerf_hidden_size,
123
+ max_freqs=params.nerf_max_freqs,
124
+ dtype=params.nerf_embedder_dtype or dtype,
125
+ device=device,
126
+ operations=operations,
127
+ )
128
+
129
+ self.nerf_blocks = nn.ModuleList([
130
+ NerfGLUBlock(
131
+ hidden_size_s=params.hidden_size,
132
+ hidden_size_x=params.nerf_hidden_size,
133
+ mlp_ratio=params.nerf_mlp_ratio,
134
+ dtype=dtype,
135
+ device=device,
136
+ operations=operations,
137
+ ) for _ in range(params.nerf_depth)
138
+ ])
139
+
140
+ if params.nerf_final_head_type == "linear":
141
+ self.nerf_final_layer = NerfFinalLayer(
142
+ params.nerf_hidden_size,
143
+ out_channels=params.in_channels,
144
+ dtype=dtype,
145
+ device=device,
146
+ operations=operations,
147
+ )
148
+ elif params.nerf_final_head_type == "conv":
149
+ self.nerf_final_layer_conv = NerfFinalLayerConv(
150
+ params.nerf_hidden_size,
151
+ out_channels=params.in_channels,
152
+ dtype=dtype,
153
+ device=device,
154
+ operations=operations,
155
+ )
156
+ else:
157
+ errstr = f"Unsupported nerf_final_head_type {params.nerf_final_head_type}"
158
+ raise ValueError(errstr)
159
+
160
+ self.skip_mmdit = []
161
+ self.skip_dit = []
162
+ self.lite = False
163
+
164
+ if params.use_x0:
165
+ self.register_buffer("__x0__", torch.tensor([]))
166
+
167
+ if params.use_sequential_txt_ids:
168
+ self.register_buffer("__sequential__", torch.tensor([]))
169
+
170
+ @property
171
+ def _nerf_final_layer(self) -> nn.Module:
172
+ if self.params.nerf_final_head_type == "linear":
173
+ return self.nerf_final_layer
174
+ if self.params.nerf_final_head_type == "conv":
175
+ return self.nerf_final_layer_conv
176
+ # Impossible to get here as we raise an error on unexpected types on initialization.
177
+ raise NotImplementedError
178
+
179
+ def img_in(self, img: Tensor) -> Tensor:
180
+ img = self.img_in_patch(img) # -> [B, Hidden, H/P, W/P]
181
+ # flatten into a sequence for the transformer.
182
+ return img.flatten(2).transpose(1, 2) # -> [B, NumPatches, Hidden]
183
+
184
+ def forward_nerf(
185
+ self,
186
+ img_orig: Tensor,
187
+ img_out: Tensor,
188
+ params: ChromaRadianceParams,
189
+ ) -> Tensor:
190
+ B, C, H, W = img_orig.shape
191
+ num_patches = img_out.shape[1]
192
+ patch_size = params.patch_size
193
+
194
+ # Store the raw pixel values of each patch for the NeRF head later.
195
+ # unfold creates patches: [B, C * P * P, NumPatches]
196
+ nerf_pixels = nn.functional.unfold(img_orig, kernel_size=patch_size, stride=patch_size)
197
+ nerf_pixels = nerf_pixels.transpose(1, 2) # -> [B, NumPatches, C * P * P]
198
+
199
+ # Reshape for per-patch processing
200
+ nerf_hidden = img_out.reshape(B * num_patches, params.hidden_size)
201
+ nerf_pixels = nerf_pixels.reshape(B * num_patches, C, patch_size**2).transpose(1, 2)
202
+
203
+ if params.nerf_tile_size > 0 and num_patches > params.nerf_tile_size:
204
+ # Enable tiling if nerf_tile_size isn't 0 and we actually have more patches than
205
+ # the tile size.
206
+ img_dct = self.forward_tiled_nerf(nerf_hidden, nerf_pixels, B, C, num_patches, patch_size, params)
207
+ else:
208
+ # Get DCT-encoded pixel embeddings [pixel-dct]
209
+ img_dct = self.nerf_image_embedder(nerf_pixels)
210
+
211
+ # Pass through the dynamic MLP blocks (the NeRF)
212
+ for block in self.nerf_blocks:
213
+ img_dct = block(img_dct, nerf_hidden)
214
+
215
+ # Reassemble the patches into the final image.
216
+ img_dct = img_dct.transpose(1, 2) # -> [B*NumPatches, C, P*P]
217
+ # Reshape to combine with batch dimension for fold
218
+ img_dct = img_dct.reshape(B, num_patches, -1) # -> [B, NumPatches, C*P*P]
219
+ img_dct = img_dct.transpose(1, 2) # -> [B, C*P*P, NumPatches]
220
+ img_dct = nn.functional.fold(
221
+ img_dct,
222
+ output_size=(H, W),
223
+ kernel_size=patch_size,
224
+ stride=patch_size,
225
+ )
226
+ return self._nerf_final_layer(img_dct)
227
+
228
+ def forward_tiled_nerf(
229
+ self,
230
+ nerf_hidden: Tensor,
231
+ nerf_pixels: Tensor,
232
+ batch: int,
233
+ channels: int,
234
+ num_patches: int,
235
+ patch_size: int,
236
+ params: ChromaRadianceParams,
237
+ ) -> Tensor:
238
+ """
239
+ Processes the NeRF head in tiles to save memory.
240
+ nerf_hidden has shape [B, L, D]
241
+ nerf_pixels has shape [B, L, C * P * P]
242
+ """
243
+ tile_size = params.nerf_tile_size
244
+ output_tiles = []
245
+ # Iterate over the patches in tiles. The dimension L (num_patches) is at index 1.
246
+ for i in range(0, num_patches, tile_size):
247
+ end = min(i + tile_size, num_patches)
248
+
249
+ # Slice the current tile from the input tensors
250
+ nerf_hidden_tile = nerf_hidden[i * batch:end * batch]
251
+ nerf_pixels_tile = nerf_pixels[i * batch:end * batch]
252
+
253
+ # get DCT-encoded pixel embeddings [pixel-dct]
254
+ img_dct_tile = self.nerf_image_embedder(nerf_pixels_tile)
255
+
256
+ # pass through the dynamic MLP blocks (the NeRF)
257
+ for block in self.nerf_blocks:
258
+ img_dct_tile = block(img_dct_tile, nerf_hidden_tile)
259
+
260
+ output_tiles.append(img_dct_tile)
261
+
262
+ # Concatenate the processed tiles along the patch dimension
263
+ return torch.cat(output_tiles, dim=0)
264
+
265
+ def radiance_get_override_params(self, overrides: dict) -> ChromaRadianceParams:
266
+ params = self.params
267
+ if not overrides:
268
+ return params
269
+ params_dict = {k: getattr(params, k) for k in params.__dataclass_fields__}
270
+ nullable_keys = frozenset(("nerf_embedder_dtype",))
271
+ bad_keys = tuple(k for k in overrides if k not in params_dict)
272
+ if bad_keys:
273
+ e = f"Unknown key(s) in transformer_options chroma_radiance_options: {', '.join(bad_keys)}"
274
+ raise ValueError(e)
275
+ bad_keys = tuple(
276
+ k
277
+ for k, v in overrides.items()
278
+ if not isinstance(v, type(getattr(params, k))) and (v is not None or k not in nullable_keys)
279
+ )
280
+ if bad_keys:
281
+ e = f"Invalid value(s) in transformer_options chroma_radiance_options: {', '.join(bad_keys)}"
282
+ raise ValueError(e)
283
+ # At this point it's all valid keys and values so we can merge with the existing params.
284
+ params_dict |= overrides
285
+ return params.__class__(**params_dict)
286
+
287
+ def _apply_x0_residual(self, predicted, noisy, timesteps):
288
+
289
+ # non zero during training to prevent 0 div
290
+ eps = 0.0
291
+ return (noisy - predicted) / (timesteps.view(-1,1,1,1) + eps)
292
+
293
+ def _forward(
294
+ self,
295
+ x: Tensor,
296
+ timestep: Tensor,
297
+ context: Tensor,
298
+ guidance: Optional[Tensor],
299
+ control: Optional[dict]=None,
300
+ transformer_options: dict={},
301
+ **kwargs: dict,
302
+ ) -> Tensor:
303
+ bs, c, h, w = x.shape
304
+ img = comfy.ldm.common_dit.pad_to_patch_size(x, (self.patch_size, self.patch_size))
305
+
306
+ if img.ndim != 4:
307
+ raise ValueError("Input img tensor must be in [B, C, H, W] format.")
308
+ if context.ndim != 3:
309
+ raise ValueError("Input txt tensors must have 3 dimensions.")
310
+
311
+ params = self.radiance_get_override_params(transformer_options.get("chroma_radiance_options", {}))
312
+
313
+ h_len = (img.shape[-2] // self.patch_size)
314
+ w_len = (img.shape[-1] // self.patch_size)
315
+
316
+ img_ids = torch.zeros((h_len, w_len, 3), device=x.device, dtype=x.dtype)
317
+ img_ids[:, :, 1] = img_ids[:, :, 1] + torch.linspace(0, h_len - 1, steps=h_len, device=x.device, dtype=x.dtype).unsqueeze(1)
318
+ img_ids[:, :, 2] = img_ids[:, :, 2] + torch.linspace(0, w_len - 1, steps=w_len, device=x.device, dtype=x.dtype).unsqueeze(0)
319
+ img_ids = repeat(img_ids, "h w c -> b (h w) c", b=bs)
320
+ txt_ids = torch.zeros((bs, context.shape[1], 3), device=x.device, dtype=x.dtype)
321
+ # Radiance after 2026-05-22 uses sequential txt_ids instead of zeros
322
+ if params.use_sequential_txt_ids:
323
+ txt_ids[:, :, 0] = torch.arange(context.shape[1], device=x.device, dtype=x.dtype).unsqueeze(0).expand(bs, -1)
324
+
325
+ img_out = self.forward_orig(
326
+ img,
327
+ img_ids,
328
+ context,
329
+ txt_ids,
330
+ timestep,
331
+ guidance,
332
+ control,
333
+ transformer_options,
334
+ attn_mask=kwargs.get("attention_mask", None),
335
+ )
336
+
337
+ out = self.forward_nerf(img, img_out, params)[:, :, :h, :w]
338
+
339
+ # If x0 variant → v-pred, just return this instead
340
+ if hasattr(self, "__x0__"):
341
+ out = self._apply_x0_residual(out, img, timestep)
342
+ return out
343
+
ComfyUI/comfy/ldm/cogvideo/__init__.py ADDED
File without changes
ComfyUI/comfy/ldm/cogvideo/model.py ADDED
@@ -0,0 +1,573 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # CogVideoX 3D Transformer - ported to ComfyUI native ops
2
+ # Architecture reference: diffusers CogVideoXTransformer3DModel
3
+ # Style reference: comfy/ldm/wan/model.py
4
+
5
+ import math
6
+ import torch
7
+ import torch.nn as nn
8
+ import torch.nn.functional as F
9
+
10
+ from comfy.ldm.modules.attention import optimized_attention
11
+ import comfy.patcher_extension
12
+ import comfy.ldm.common_dit
13
+
14
+
15
+ def _get_1d_rotary_pos_embed(dim, pos, theta=10000.0):
16
+ """Returns (cos, sin) each with shape [seq_len, dim].
17
+
18
+ Frequencies are computed at dim//2 resolution then repeat_interleaved
19
+ to full dim, matching CogVideoX's interleaved (real, imag) pair format.
20
+ """
21
+ freqs = 1.0 / (theta ** (torch.arange(0, dim, 2, dtype=torch.float32, device=pos.device) / dim))
22
+ angles = torch.outer(pos.float(), freqs.float())
23
+ cos = angles.cos().repeat_interleave(2, dim=-1).float()
24
+ sin = angles.sin().repeat_interleave(2, dim=-1).float()
25
+ return (cos, sin)
26
+
27
+
28
+ def apply_rotary_emb(x, freqs_cos_sin):
29
+ """Apply CogVideoX rotary embedding to query or key tensor.
30
+
31
+ x: [B, heads, seq_len, head_dim]
32
+ freqs_cos_sin: (cos, sin) each [seq_len, head_dim//2]
33
+
34
+ Uses interleaved pair rotation (same as diffusers CogVideoX/Flux).
35
+ head_dim is reshaped to (-1, 2) pairs, rotated, then flattened back.
36
+ """
37
+ cos, sin = freqs_cos_sin
38
+ cos = cos[None, None, :, :].to(x.device)
39
+ sin = sin[None, None, :, :].to(x.device)
40
+
41
+ # Interleaved pairs: [B, H, S, D] -> [B, H, S, D//2, 2] -> (real, imag)
42
+ x_real, x_imag = x.reshape(*x.shape[:-1], -1, 2).unbind(-1)
43
+ x_rotated = torch.stack([-x_imag, x_real], dim=-1).flatten(3)
44
+
45
+ return (x.float() * cos + x_rotated.float() * sin).to(x.dtype)
46
+
47
+
48
+ def get_timestep_embedding(timesteps, dim, flip_sin_to_cos=True, downscale_freq_shift=0, scale=1, max_period=10000):
49
+ half = dim // 2
50
+ freqs = torch.exp(-math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32, device=timesteps.device) / half)
51
+ args = timesteps[:, None].float() * freqs[None] * scale
52
+ embedding = torch.cat([torch.sin(args), torch.cos(args)], dim=-1)
53
+ if flip_sin_to_cos:
54
+ embedding = torch.cat([embedding[:, half:], embedding[:, :half]], dim=-1)
55
+ if dim % 2:
56
+ embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1)
57
+ return embedding
58
+
59
+
60
+ def get_3d_sincos_pos_embed(embed_dim, spatial_size, temporal_size, spatial_interpolation_scale=1.0, temporal_interpolation_scale=1.0, device=None):
61
+ if isinstance(spatial_size, int):
62
+ spatial_size = (spatial_size, spatial_size)
63
+
64
+ grid_w = torch.arange(spatial_size[0], dtype=torch.float32, device=device) / spatial_interpolation_scale
65
+ grid_h = torch.arange(spatial_size[1], dtype=torch.float32, device=device) / spatial_interpolation_scale
66
+ grid_t = torch.arange(temporal_size, dtype=torch.float32, device=device) / temporal_interpolation_scale
67
+
68
+ grid_t, grid_h, grid_w = torch.meshgrid(grid_t, grid_h, grid_w, indexing="ij")
69
+
70
+ embed_dim_spatial = 2 * (embed_dim // 3)
71
+ embed_dim_temporal = embed_dim // 3
72
+
73
+ pos_embed_spatial = _get_2d_sincos_pos_embed(embed_dim_spatial, grid_h, grid_w, device=device)
74
+ pos_embed_temporal = _get_1d_sincos_pos_embed(embed_dim_temporal, grid_t[:, 0, 0], device=device)
75
+
76
+ T, H, W = grid_t.shape
77
+ pos_embed_temporal = pos_embed_temporal.unsqueeze(1).unsqueeze(1).expand(-1, H, W, -1)
78
+ pos_embed = torch.cat([pos_embed_temporal, pos_embed_spatial], dim=-1)
79
+
80
+ return pos_embed
81
+
82
+
83
+ def _get_2d_sincos_pos_embed(embed_dim, grid_h, grid_w, device=None):
84
+ T, H, W = grid_h.shape
85
+ half_dim = embed_dim // 2
86
+ pos_h = _get_1d_sincos_pos_embed(half_dim, grid_h.reshape(-1), device=device).reshape(T, H, W, half_dim)
87
+ pos_w = _get_1d_sincos_pos_embed(half_dim, grid_w.reshape(-1), device=device).reshape(T, H, W, half_dim)
88
+ return torch.cat([pos_h, pos_w], dim=-1)
89
+
90
+
91
+ def _get_1d_sincos_pos_embed(embed_dim, pos, device=None):
92
+ half = embed_dim // 2
93
+ freqs = torch.exp(-math.log(10000.0) * torch.arange(start=0, end=half, dtype=torch.float32, device=device) / half)
94
+ args = pos.float().reshape(-1)[:, None] * freqs[None]
95
+ embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
96
+ if embed_dim % 2:
97
+ embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1)
98
+ return embedding
99
+
100
+
101
+
102
+ class CogVideoXPatchEmbed(nn.Module):
103
+ def __init__(self, patch_size=2, patch_size_t=None, in_channels=16, dim=1920,
104
+ text_dim=4096, bias=True, sample_width=90, sample_height=60,
105
+ sample_frames=49, temporal_compression_ratio=4,
106
+ max_text_seq_length=226, spatial_interpolation_scale=1.875,
107
+ temporal_interpolation_scale=1.0, use_positional_embeddings=True,
108
+ use_learned_positional_embeddings=True,
109
+ device=None, dtype=None, operations=None):
110
+ super().__init__()
111
+ self.patch_size = patch_size
112
+ self.patch_size_t = patch_size_t
113
+ self.dim = dim
114
+ self.sample_height = sample_height
115
+ self.sample_width = sample_width
116
+ self.sample_frames = sample_frames
117
+ self.temporal_compression_ratio = temporal_compression_ratio
118
+ self.max_text_seq_length = max_text_seq_length
119
+ self.spatial_interpolation_scale = spatial_interpolation_scale
120
+ self.temporal_interpolation_scale = temporal_interpolation_scale
121
+ self.use_positional_embeddings = use_positional_embeddings
122
+ self.use_learned_positional_embeddings = use_learned_positional_embeddings
123
+
124
+ if patch_size_t is None:
125
+ self.proj = operations.Conv2d(in_channels, dim, kernel_size=patch_size, stride=patch_size, bias=bias, device=device, dtype=dtype)
126
+ else:
127
+ self.proj = operations.Linear(in_channels * patch_size * patch_size * patch_size_t, dim, device=device, dtype=dtype)
128
+
129
+ self.text_proj = operations.Linear(text_dim, dim, device=device, dtype=dtype)
130
+
131
+ if use_positional_embeddings or use_learned_positional_embeddings:
132
+ persistent = use_learned_positional_embeddings
133
+ pos_embedding = self._get_positional_embeddings(sample_height, sample_width, sample_frames)
134
+ self.register_buffer("pos_embedding", pos_embedding, persistent=persistent)
135
+
136
+ def _get_positional_embeddings(self, sample_height, sample_width, sample_frames, device=None):
137
+ post_patch_height = sample_height // self.patch_size
138
+ post_patch_width = sample_width // self.patch_size
139
+ post_time_compression_frames = (sample_frames - 1) // self.temporal_compression_ratio + 1
140
+ if self.patch_size_t is not None:
141
+ post_time_compression_frames = post_time_compression_frames // self.patch_size_t
142
+ num_patches = post_patch_height * post_patch_width * post_time_compression_frames
143
+
144
+ pos_embedding = get_3d_sincos_pos_embed(
145
+ self.dim,
146
+ (post_patch_width, post_patch_height),
147
+ post_time_compression_frames,
148
+ self.spatial_interpolation_scale,
149
+ self.temporal_interpolation_scale,
150
+ device=device,
151
+ )
152
+ pos_embedding = pos_embedding.reshape(-1, self.dim)
153
+ joint_pos_embedding = pos_embedding.new_zeros(
154
+ 1, self.max_text_seq_length + num_patches, self.dim, requires_grad=False
155
+ )
156
+ joint_pos_embedding.data[:, self.max_text_seq_length:].copy_(pos_embedding)
157
+ return joint_pos_embedding
158
+
159
+ def forward(self, text_embeds, image_embeds):
160
+ input_dtype = text_embeds.dtype
161
+ text_embeds = self.text_proj(text_embeds.to(self.text_proj.weight.dtype)).to(input_dtype)
162
+ batch_size, num_frames, channels, height, width = image_embeds.shape
163
+
164
+ proj_dtype = self.proj.weight.dtype
165
+ if self.patch_size_t is None:
166
+ image_embeds = image_embeds.reshape(-1, channels, height, width)
167
+ image_embeds = self.proj(image_embeds.to(proj_dtype)).to(input_dtype)
168
+ image_embeds = image_embeds.view(batch_size, num_frames, *image_embeds.shape[1:])
169
+ image_embeds = image_embeds.flatten(3).transpose(2, 3)
170
+ image_embeds = image_embeds.flatten(1, 2)
171
+ else:
172
+ p = self.patch_size
173
+ p_t = self.patch_size_t
174
+ image_embeds = image_embeds.permute(0, 1, 3, 4, 2)
175
+ image_embeds = image_embeds.reshape(
176
+ batch_size, num_frames // p_t, p_t, height // p, p, width // p, p, channels
177
+ )
178
+ image_embeds = image_embeds.permute(0, 1, 3, 5, 7, 2, 4, 6).flatten(4, 7).flatten(1, 3)
179
+ image_embeds = self.proj(image_embeds.to(proj_dtype)).to(input_dtype)
180
+
181
+ embeds = torch.cat([text_embeds, image_embeds], dim=1).contiguous()
182
+
183
+ if self.use_positional_embeddings or self.use_learned_positional_embeddings:
184
+ text_seq_length = text_embeds.shape[1]
185
+ num_image_patches = image_embeds.shape[1]
186
+
187
+ if self.use_learned_positional_embeddings:
188
+ image_pos = self.pos_embedding[
189
+ :, self.max_text_seq_length:self.max_text_seq_length + num_image_patches
190
+ ].to(device=embeds.device, dtype=embeds.dtype)
191
+ else:
192
+ image_pos = get_3d_sincos_pos_embed(
193
+ self.dim,
194
+ (width // self.patch_size, height // self.patch_size),
195
+ num_image_patches // ((height // self.patch_size) * (width // self.patch_size)),
196
+ self.spatial_interpolation_scale,
197
+ self.temporal_interpolation_scale,
198
+ device=embeds.device,
199
+ ).reshape(1, num_image_patches, self.dim).to(dtype=embeds.dtype)
200
+
201
+ # Build joint: zeros for text + sincos for image
202
+ joint_pos = torch.zeros(1, text_seq_length + num_image_patches, self.dim, device=embeds.device, dtype=embeds.dtype)
203
+ joint_pos[:, text_seq_length:] = image_pos
204
+ embeds = embeds + joint_pos
205
+
206
+ return embeds
207
+
208
+
209
+ class CogVideoXLayerNormZero(nn.Module):
210
+ def __init__(self, time_dim, dim, elementwise_affine=True, eps=1e-5, bias=True,
211
+ device=None, dtype=None, operations=None):
212
+ super().__init__()
213
+ self.silu = nn.SiLU()
214
+ self.linear = operations.Linear(time_dim, 6 * dim, bias=bias, device=device, dtype=dtype)
215
+ self.norm = operations.LayerNorm(dim, eps=eps, elementwise_affine=elementwise_affine, device=device, dtype=dtype)
216
+
217
+ def forward(self, hidden_states, encoder_hidden_states, temb):
218
+ shift, scale, gate, enc_shift, enc_scale, enc_gate = self.linear(self.silu(temb)).chunk(6, dim=1)
219
+ hidden_states = self.norm(hidden_states) * (1 + scale)[:, None, :] + shift[:, None, :]
220
+ encoder_hidden_states = self.norm(encoder_hidden_states) * (1 + enc_scale)[:, None, :] + enc_shift[:, None, :]
221
+ return hidden_states, encoder_hidden_states, gate[:, None, :], enc_gate[:, None, :]
222
+
223
+
224
+ class CogVideoXAdaLayerNorm(nn.Module):
225
+ def __init__(self, time_dim, dim, elementwise_affine=True, eps=1e-5,
226
+ device=None, dtype=None, operations=None):
227
+ super().__init__()
228
+ self.silu = nn.SiLU()
229
+ self.linear = operations.Linear(time_dim, 2 * dim, device=device, dtype=dtype)
230
+ self.norm = operations.LayerNorm(dim, eps=eps, elementwise_affine=elementwise_affine, device=device, dtype=dtype)
231
+
232
+ def forward(self, x, temb):
233
+ temb = self.linear(self.silu(temb))
234
+ shift, scale = temb.chunk(2, dim=1)
235
+ x = self.norm(x) * (1 + scale)[:, None, :] + shift[:, None, :]
236
+ return x
237
+
238
+
239
+ class CogVideoXBlock(nn.Module):
240
+ def __init__(self, dim, num_heads, head_dim, time_dim,
241
+ eps=1e-5, ff_inner_dim=None, ff_bias=True,
242
+ device=None, dtype=None, operations=None):
243
+ super().__init__()
244
+ self.dim = dim
245
+ self.num_heads = num_heads
246
+ self.head_dim = head_dim
247
+
248
+ self.norm1 = CogVideoXLayerNormZero(time_dim, dim, eps=eps, device=device, dtype=dtype, operations=operations)
249
+
250
+ # Self-attention (joint text + latent)
251
+ self.q = operations.Linear(dim, dim, bias=True, device=device, dtype=dtype)
252
+ self.k = operations.Linear(dim, dim, bias=True, device=device, dtype=dtype)
253
+ self.v = operations.Linear(dim, dim, bias=True, device=device, dtype=dtype)
254
+ self.norm_q = operations.LayerNorm(head_dim, eps=1e-6, elementwise_affine=True, device=device, dtype=dtype)
255
+ self.norm_k = operations.LayerNorm(head_dim, eps=1e-6, elementwise_affine=True, device=device, dtype=dtype)
256
+ self.attn_out = operations.Linear(dim, dim, bias=True, device=device, dtype=dtype)
257
+
258
+ self.norm2 = CogVideoXLayerNormZero(time_dim, dim, eps=eps, device=device, dtype=dtype, operations=operations)
259
+
260
+ # Feed-forward (GELU approximate)
261
+ inner_dim = ff_inner_dim or dim * 4
262
+ self.ff_proj = operations.Linear(dim, inner_dim, bias=ff_bias, device=device, dtype=dtype)
263
+ self.ff_out = operations.Linear(inner_dim, dim, bias=ff_bias, device=device, dtype=dtype)
264
+
265
+ def forward(self, hidden_states, encoder_hidden_states, temb, image_rotary_emb=None, transformer_options=None):
266
+ if transformer_options is None:
267
+ transformer_options = {}
268
+ text_seq_length = encoder_hidden_states.size(1)
269
+
270
+ # Norm & modulate
271
+ norm_hidden, norm_encoder, gate_msa, enc_gate_msa = self.norm1(hidden_states, encoder_hidden_states, temb)
272
+
273
+ # Joint self-attention
274
+ qkv_input = torch.cat([norm_encoder, norm_hidden], dim=1)
275
+ b, s, _ = qkv_input.shape
276
+ n, d = self.num_heads, self.head_dim
277
+
278
+ q = self.q(qkv_input).view(b, s, n, d)
279
+ k = self.k(qkv_input).view(b, s, n, d)
280
+ v = self.v(qkv_input)
281
+
282
+ q = self.norm_q(q).view(b, s, n, d)
283
+ k = self.norm_k(k).view(b, s, n, d)
284
+
285
+ # Apply rotary embeddings to image tokens only (diffusers format: [B, heads, seq, head_dim])
286
+ if image_rotary_emb is not None:
287
+ q_img = q[:, text_seq_length:].transpose(1, 2) # [B, heads, img_seq, head_dim]
288
+ k_img = k[:, text_seq_length:].transpose(1, 2)
289
+ q_img = apply_rotary_emb(q_img, image_rotary_emb)
290
+ k_img = apply_rotary_emb(k_img, image_rotary_emb)
291
+ q = torch.cat([q[:, :text_seq_length], q_img.transpose(1, 2)], dim=1)
292
+ k = torch.cat([k[:, :text_seq_length], k_img.transpose(1, 2)], dim=1)
293
+
294
+ attn_out = optimized_attention(
295
+ q.reshape(b, s, n * d),
296
+ k.reshape(b, s, n * d),
297
+ v,
298
+ heads=self.num_heads,
299
+ transformer_options=transformer_options,
300
+ )
301
+
302
+ attn_out = self.attn_out(attn_out)
303
+
304
+ attn_encoder, attn_hidden = attn_out.split([text_seq_length, s - text_seq_length], dim=1)
305
+
306
+ hidden_states = hidden_states + gate_msa * attn_hidden
307
+ encoder_hidden_states = encoder_hidden_states + enc_gate_msa * attn_encoder
308
+
309
+ # Norm & modulate for FF
310
+ norm_hidden, norm_encoder, gate_ff, enc_gate_ff = self.norm2(hidden_states, encoder_hidden_states, temb)
311
+
312
+ # Feed-forward (GELU on concatenated text + latent)
313
+ ff_input = torch.cat([norm_encoder, norm_hidden], dim=1)
314
+ ff_output = self.ff_out(F.gelu(self.ff_proj(ff_input), approximate="tanh"))
315
+
316
+ hidden_states = hidden_states + gate_ff * ff_output[:, text_seq_length:]
317
+ encoder_hidden_states = encoder_hidden_states + enc_gate_ff * ff_output[:, :text_seq_length]
318
+
319
+ return hidden_states, encoder_hidden_states
320
+
321
+
322
+ class CogVideoXTransformer3DModel(nn.Module):
323
+ def __init__(self,
324
+ num_attention_heads=30,
325
+ attention_head_dim=64,
326
+ in_channels=16,
327
+ out_channels=16,
328
+ flip_sin_to_cos=True,
329
+ freq_shift=0,
330
+ time_embed_dim=512,
331
+ ofs_embed_dim=None,
332
+ text_embed_dim=4096,
333
+ num_layers=30,
334
+ dropout=0.0,
335
+ attention_bias=True,
336
+ sample_width=90,
337
+ sample_height=60,
338
+ sample_frames=49,
339
+ patch_size=2,
340
+ patch_size_t=None,
341
+ temporal_compression_ratio=4,
342
+ max_text_seq_length=226,
343
+ spatial_interpolation_scale=1.875,
344
+ temporal_interpolation_scale=1.0,
345
+ use_rotary_positional_embeddings=False,
346
+ use_learned_positional_embeddings=False,
347
+ patch_bias=True,
348
+ image_model=None,
349
+ device=None,
350
+ dtype=None,
351
+ operations=None,
352
+ ):
353
+ super().__init__()
354
+ self.dtype = dtype
355
+ dim = num_attention_heads * attention_head_dim
356
+ self.dim = dim
357
+ self.num_attention_heads = num_attention_heads
358
+ self.attention_head_dim = attention_head_dim
359
+ self.in_channels = in_channels
360
+ self.out_channels = out_channels
361
+ self.patch_size = patch_size
362
+ self.patch_size_t = patch_size_t
363
+ self.max_text_seq_length = max_text_seq_length
364
+ self.use_rotary_positional_embeddings = use_rotary_positional_embeddings
365
+
366
+ # 1. Patch embedding
367
+ self.patch_embed = CogVideoXPatchEmbed(
368
+ patch_size=patch_size,
369
+ patch_size_t=patch_size_t,
370
+ in_channels=in_channels,
371
+ dim=dim,
372
+ text_dim=text_embed_dim,
373
+ bias=patch_bias,
374
+ sample_width=sample_width,
375
+ sample_height=sample_height,
376
+ sample_frames=sample_frames,
377
+ temporal_compression_ratio=temporal_compression_ratio,
378
+ max_text_seq_length=max_text_seq_length,
379
+ spatial_interpolation_scale=spatial_interpolation_scale,
380
+ temporal_interpolation_scale=temporal_interpolation_scale,
381
+ use_positional_embeddings=not use_rotary_positional_embeddings,
382
+ use_learned_positional_embeddings=use_learned_positional_embeddings,
383
+ device=device, dtype=torch.float32, operations=operations,
384
+ )
385
+
386
+ # 2. Time embedding
387
+ self.time_proj_dim = dim
388
+ self.time_proj_flip = flip_sin_to_cos
389
+ self.time_proj_shift = freq_shift
390
+ self.time_embedding_linear_1 = operations.Linear(dim, time_embed_dim, device=device, dtype=dtype)
391
+ self.time_embedding_act = nn.SiLU()
392
+ self.time_embedding_linear_2 = operations.Linear(time_embed_dim, time_embed_dim, device=device, dtype=dtype)
393
+
394
+ # Optional OFS embedding (CogVideoX 1.5 I2V)
395
+ self.ofs_proj_dim = ofs_embed_dim
396
+ if ofs_embed_dim:
397
+ self.ofs_embedding_linear_1 = operations.Linear(ofs_embed_dim, ofs_embed_dim, device=device, dtype=dtype)
398
+ self.ofs_embedding_act = nn.SiLU()
399
+ self.ofs_embedding_linear_2 = operations.Linear(ofs_embed_dim, ofs_embed_dim, device=device, dtype=dtype)
400
+ else:
401
+ self.ofs_embedding_linear_1 = None
402
+
403
+ # 3. Transformer blocks
404
+ self.blocks = nn.ModuleList([
405
+ CogVideoXBlock(
406
+ dim=dim,
407
+ num_heads=num_attention_heads,
408
+ head_dim=attention_head_dim,
409
+ time_dim=time_embed_dim,
410
+ eps=1e-5,
411
+ device=device, dtype=dtype, operations=operations,
412
+ )
413
+ for _ in range(num_layers)
414
+ ])
415
+
416
+ self.norm_final = operations.LayerNorm(dim, eps=1e-5, elementwise_affine=True, device=device, dtype=dtype)
417
+
418
+ # 4. Output
419
+ self.norm_out = CogVideoXAdaLayerNorm(
420
+ time_dim=time_embed_dim, dim=dim, eps=1e-5,
421
+ device=device, dtype=dtype, operations=operations,
422
+ )
423
+
424
+ if patch_size_t is None:
425
+ output_dim = patch_size * patch_size * out_channels
426
+ else:
427
+ output_dim = patch_size * patch_size * patch_size_t * out_channels
428
+
429
+ self.proj_out = operations.Linear(dim, output_dim, device=device, dtype=dtype)
430
+
431
+ self.spatial_interpolation_scale = spatial_interpolation_scale
432
+ self.temporal_interpolation_scale = temporal_interpolation_scale
433
+ self.temporal_compression_ratio = temporal_compression_ratio
434
+
435
+ def forward(self, x, timestep, context, ofs=None, transformer_options=None, **kwargs):
436
+ if transformer_options is None:
437
+ transformer_options = {}
438
+ return comfy.patcher_extension.WrapperExecutor.new_class_executor(
439
+ self._forward,
440
+ self,
441
+ comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, transformer_options)
442
+ ).execute(x, timestep, context, ofs, transformer_options, **kwargs)
443
+
444
+ def _forward(self, x, timestep, context, ofs=None, transformer_options=None, **kwargs):
445
+ if transformer_options is None:
446
+ transformer_options = {}
447
+ # ComfyUI passes [B, C, T, H, W]
448
+ batch_size, channels, t, h, w = x.shape
449
+
450
+ # Pad to patch size (temporal + spatial), same pattern as WAN
451
+ p_t = self.patch_size_t if self.patch_size_t is not None else 1
452
+ x = comfy.ldm.common_dit.pad_to_patch_size(x, (p_t, self.patch_size, self.patch_size))
453
+
454
+ # CogVideoX expects [B, T, C, H, W]
455
+ x = x.permute(0, 2, 1, 3, 4)
456
+ batch_size, num_frames, channels, height, width = x.shape
457
+
458
+ # Time embedding
459
+ t_emb = get_timestep_embedding(timestep, self.time_proj_dim, self.time_proj_flip, self.time_proj_shift)
460
+ t_emb = t_emb.to(dtype=x.dtype)
461
+ emb = self.time_embedding_linear_2(self.time_embedding_act(self.time_embedding_linear_1(t_emb)))
462
+
463
+ if self.ofs_embedding_linear_1 is not None and ofs is not None:
464
+ ofs_emb = get_timestep_embedding(ofs, self.ofs_proj_dim, self.time_proj_flip, self.time_proj_shift)
465
+ ofs_emb = ofs_emb.to(dtype=x.dtype)
466
+ ofs_emb = self.ofs_embedding_linear_2(self.ofs_embedding_act(self.ofs_embedding_linear_1(ofs_emb)))
467
+ emb = emb + ofs_emb
468
+
469
+ # Patch embedding
470
+ hidden_states = self.patch_embed(context, x)
471
+
472
+ text_seq_length = context.shape[1]
473
+ encoder_hidden_states = hidden_states[:, :text_seq_length]
474
+ hidden_states = hidden_states[:, text_seq_length:]
475
+
476
+ # Rotary embeddings (if used)
477
+ image_rotary_emb = None
478
+ if self.use_rotary_positional_embeddings:
479
+ post_patch_height = height // self.patch_size
480
+ post_patch_width = width // self.patch_size
481
+ if self.patch_size_t is None:
482
+ post_time = num_frames
483
+ else:
484
+ post_time = num_frames // self.patch_size_t
485
+ image_rotary_emb = self._get_rotary_emb(post_patch_height, post_patch_width, post_time, device=x.device)
486
+
487
+ # Transformer blocks
488
+ for i, block in enumerate(self.blocks):
489
+ hidden_states, encoder_hidden_states = block(
490
+ hidden_states=hidden_states,
491
+ encoder_hidden_states=encoder_hidden_states,
492
+ temb=emb,
493
+ image_rotary_emb=image_rotary_emb,
494
+ transformer_options=transformer_options,
495
+ )
496
+
497
+ hidden_states = self.norm_final(hidden_states)
498
+
499
+ # Output projection
500
+ hidden_states = self.norm_out(hidden_states, temb=emb)
501
+ hidden_states = self.proj_out(hidden_states)
502
+
503
+ # Unpatchify
504
+ p = self.patch_size
505
+ p_t = self.patch_size_t
506
+
507
+ if p_t is None:
508
+ output = hidden_states.reshape(batch_size, num_frames, height // p, width // p, -1, p, p)
509
+ output = output.permute(0, 1, 4, 2, 5, 3, 6).flatten(5, 6).flatten(3, 4)
510
+ else:
511
+ output = hidden_states.reshape(
512
+ batch_size, (num_frames + p_t - 1) // p_t, height // p, width // p, -1, p_t, p, p
513
+ )
514
+ output = output.permute(0, 1, 5, 4, 2, 6, 3, 7).flatten(6, 7).flatten(4, 5).flatten(1, 2)
515
+
516
+ # Back to ComfyUI format [B, C, T, H, W] and crop padding
517
+ output = output.permute(0, 2, 1, 3, 4)[:, :, :t, :h, :w]
518
+ return output
519
+
520
+ def _get_rotary_emb(self, h, w, t, device):
521
+ """Compute CogVideoX 3D rotary positional embeddings.
522
+
523
+ For CogVideoX 1.5 (patch_size_t != None): uses "slice" mode — grid positions
524
+ are integer arange computed at max_size, then sliced to actual size.
525
+ For CogVideoX 1.0 (patch_size_t == None): uses "linspace" mode with crop coords
526
+ scaled by spatial_interpolation_scale.
527
+ """
528
+ d = self.attention_head_dim
529
+ dim_t = d // 4
530
+ dim_h = d // 8 * 3
531
+ dim_w = d // 8 * 3
532
+
533
+ if self.patch_size_t is not None:
534
+ # CogVideoX 1.5: "slice" mode — positions are simple integer indices
535
+ # Compute at max(sample_size, actual_size) then slice to actual
536
+ base_h = self.patch_embed.sample_height // self.patch_size
537
+ base_w = self.patch_embed.sample_width // self.patch_size
538
+ max_h = max(base_h, h)
539
+ max_w = max(base_w, w)
540
+
541
+ grid_h = torch.arange(max_h, device=device, dtype=torch.float32)
542
+ grid_w = torch.arange(max_w, device=device, dtype=torch.float32)
543
+ grid_t = torch.arange(t, device=device, dtype=torch.float32)
544
+ else:
545
+ # CogVideoX 1.0: "linspace" mode with interpolation scale
546
+ grid_h = torch.linspace(0, h - 1, h, device=device, dtype=torch.float32) * self.spatial_interpolation_scale
547
+ grid_w = torch.linspace(0, w - 1, w, device=device, dtype=torch.float32) * self.spatial_interpolation_scale
548
+ grid_t = torch.arange(t, device=device, dtype=torch.float32)
549
+
550
+ freqs_t = _get_1d_rotary_pos_embed(dim_t, grid_t)
551
+ freqs_h = _get_1d_rotary_pos_embed(dim_h, grid_h)
552
+ freqs_w = _get_1d_rotary_pos_embed(dim_w, grid_w)
553
+
554
+ t_cos, t_sin = freqs_t
555
+ h_cos, h_sin = freqs_h
556
+ w_cos, w_sin = freqs_w
557
+
558
+ # Slice to actual size (for "slice" mode where grids may be larger)
559
+ t_cos, t_sin = t_cos[:t], t_sin[:t]
560
+ h_cos, h_sin = h_cos[:h], h_sin[:h]
561
+ w_cos, w_sin = w_cos[:w], w_sin[:w]
562
+
563
+ # Broadcast and concatenate into [T*H*W, head_dim]
564
+ t_cos = t_cos[:, None, None, :].expand(-1, h, w, -1)
565
+ t_sin = t_sin[:, None, None, :].expand(-1, h, w, -1)
566
+ h_cos = h_cos[None, :, None, :].expand(t, -1, w, -1)
567
+ h_sin = h_sin[None, :, None, :].expand(t, -1, w, -1)
568
+ w_cos = w_cos[None, None, :, :].expand(t, h, -1, -1)
569
+ w_sin = w_sin[None, None, :, :].expand(t, h, -1, -1)
570
+
571
+ cos = torch.cat([t_cos, h_cos, w_cos], dim=-1).reshape(t * h * w, -1)
572
+ sin = torch.cat([t_sin, h_sin, w_sin], dim=-1).reshape(t * h * w, -1)
573
+ return (cos, sin)
ComfyUI/comfy/ldm/cogvideo/vae.py ADDED
@@ -0,0 +1,566 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # CogVideoX VAE - ported to ComfyUI native ops
2
+ # Architecture reference: diffusers AutoencoderKLCogVideoX
3
+ # Style reference: comfy/ldm/wan/vae.py
4
+
5
+ import numpy as np
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ import torch.nn.functional as F
10
+
11
+ import comfy.ops
12
+ ops = comfy.ops.disable_weight_init
13
+
14
+
15
+ class CausalConv3d(nn.Module):
16
+ """Causal 3D convolution with temporal padding.
17
+
18
+ Uses comfy.ops.Conv3d with autopad='causal_zero' fast path: when input has
19
+ a single temporal frame and no cache, the 3D conv weight is sliced to act
20
+ as a 2D conv, avoiding computation on zero-padded temporal dimensions.
21
+ """
22
+ def __init__(self, in_channels, out_channels, kernel_size, stride=1, dilation=1, pad_mode="constant"):
23
+ super().__init__()
24
+ if isinstance(kernel_size, int):
25
+ kernel_size = (kernel_size,) * 3
26
+
27
+ time_kernel, height_kernel, width_kernel = kernel_size
28
+ self.time_kernel_size = time_kernel
29
+ self.pad_mode = pad_mode
30
+
31
+ height_pad = (height_kernel - 1) // 2
32
+ width_pad = (width_kernel - 1) // 2
33
+ self.time_causal_padding = (width_pad, width_pad, height_pad, height_pad, time_kernel - 1, 0)
34
+
35
+ stride = stride if isinstance(stride, tuple) else (stride, 1, 1)
36
+ dilation = (dilation, 1, 1)
37
+ self.conv = ops.Conv3d(
38
+ in_channels, out_channels, kernel_size,
39
+ stride=stride, dilation=dilation,
40
+ padding=(0, height_pad, width_pad),
41
+ )
42
+
43
+ def forward(self, x, conv_cache=None):
44
+ if self.pad_mode == "replicate":
45
+ x = F.pad(x, self.time_causal_padding, mode="replicate")
46
+ conv_cache = None
47
+ else:
48
+ kernel_t = self.time_kernel_size
49
+ if kernel_t > 1:
50
+ if conv_cache is None and x.shape[2] == 1:
51
+ # Fast path: single frame, no cache. All temporal padding
52
+ # frames are copies of the input (replicate-style), so the
53
+ # 3D conv reduces to a 2D conv with summed temporal kernel.
54
+ w = comfy.ops.cast_to_input(self.conv.weight, x)
55
+ b = comfy.ops.cast_to_input(self.conv.bias, x) if self.conv.bias is not None else None
56
+ w2d = w.sum(dim=2, keepdim=True)
57
+ out = F.conv3d(x, w2d, b,
58
+ self.conv.stride, self.conv.padding,
59
+ self.conv.dilation, self.conv.groups)
60
+ return out, None
61
+ cached = [conv_cache] if conv_cache is not None else [x[:, :, :1]] * (kernel_t - 1)
62
+ x = torch.cat(cached + [x], dim=2)
63
+ conv_cache = x[:, :, -self.time_kernel_size + 1:].clone() if self.time_kernel_size > 1 else None
64
+
65
+ out = self.conv(x)
66
+ return out, conv_cache
67
+
68
+
69
+ def _interpolate_zq(zq, target_size):
70
+ """Interpolate latent z to target (T, H, W), matching CogVideoX's first-frame-special handling."""
71
+ t = target_size[0]
72
+ if t > 1 and t % 2 == 1:
73
+ z_first = F.interpolate(zq[:, :, :1], size=(1, target_size[1], target_size[2]))
74
+ z_rest = F.interpolate(zq[:, :, 1:], size=(t - 1, target_size[1], target_size[2]))
75
+ return torch.cat([z_first, z_rest], dim=2)
76
+ return F.interpolate(zq, size=target_size)
77
+
78
+
79
+ class SpatialNorm3D(nn.Module):
80
+ """Spatially conditioned normalization."""
81
+ def __init__(self, f_channels, zq_channels, groups=32):
82
+ super().__init__()
83
+ self.norm_layer = ops.GroupNorm(num_channels=f_channels, num_groups=groups, eps=1e-6, affine=True)
84
+ self.conv_y = CausalConv3d(zq_channels, f_channels, kernel_size=1, stride=1)
85
+ self.conv_b = CausalConv3d(zq_channels, f_channels, kernel_size=1, stride=1)
86
+
87
+ def forward(self, f, zq, conv_cache=None):
88
+ new_cache = {}
89
+ conv_cache = conv_cache or {}
90
+
91
+ if zq.shape[-3:] != f.shape[-3:]:
92
+ zq = _interpolate_zq(zq, f.shape[-3:])
93
+
94
+ conv_y, new_cache["conv_y"] = self.conv_y(zq, conv_cache=conv_cache.get("conv_y"))
95
+ conv_b, new_cache["conv_b"] = self.conv_b(zq, conv_cache=conv_cache.get("conv_b"))
96
+
97
+ return self.norm_layer(f) * conv_y + conv_b, new_cache
98
+
99
+
100
+ class ResnetBlock3D(nn.Module):
101
+ """3D ResNet block with optional spatial norm."""
102
+ def __init__(self, in_channels, out_channels=None, temb_channels=512, groups=32,
103
+ eps=1e-6, act_fn="silu", spatial_norm_dim=None, pad_mode="first"):
104
+ super().__init__()
105
+ out_channels = out_channels or in_channels
106
+ self.in_channels = in_channels
107
+ self.out_channels = out_channels
108
+ self.spatial_norm_dim = spatial_norm_dim
109
+
110
+ if act_fn == "silu":
111
+ self.nonlinearity = nn.SiLU()
112
+ elif act_fn == "swish":
113
+ self.nonlinearity = nn.SiLU()
114
+ else:
115
+ self.nonlinearity = nn.SiLU()
116
+
117
+ if spatial_norm_dim is None:
118
+ self.norm1 = ops.GroupNorm(num_channels=in_channels, num_groups=groups, eps=eps)
119
+ self.norm2 = ops.GroupNorm(num_channels=out_channels, num_groups=groups, eps=eps)
120
+ else:
121
+ self.norm1 = SpatialNorm3D(in_channels, spatial_norm_dim, groups=groups)
122
+ self.norm2 = SpatialNorm3D(out_channels, spatial_norm_dim, groups=groups)
123
+
124
+ self.conv1 = CausalConv3d(in_channels, out_channels, kernel_size=3, pad_mode=pad_mode)
125
+
126
+ if temb_channels > 0:
127
+ self.temb_proj = ops.Linear(temb_channels, out_channels)
128
+
129
+ self.conv2 = CausalConv3d(out_channels, out_channels, kernel_size=3, pad_mode=pad_mode)
130
+
131
+ if in_channels != out_channels:
132
+ self.conv_shortcut = ops.Conv3d(in_channels, out_channels, kernel_size=1, stride=1, padding=0)
133
+ else:
134
+ self.conv_shortcut = None
135
+
136
+ def forward(self, x, temb=None, zq=None, conv_cache=None):
137
+ new_cache = {}
138
+ conv_cache = conv_cache or {}
139
+ residual = x
140
+
141
+ if zq is not None:
142
+ x, new_cache["norm1"] = self.norm1(x, zq, conv_cache=conv_cache.get("norm1"))
143
+ else:
144
+ x = self.norm1(x)
145
+
146
+ x = self.nonlinearity(x)
147
+ x, new_cache["conv1"] = self.conv1(x, conv_cache=conv_cache.get("conv1"))
148
+
149
+ if temb is not None and hasattr(self, "temb_proj"):
150
+ x = x + self.temb_proj(self.nonlinearity(temb))[:, :, None, None, None]
151
+
152
+ if zq is not None:
153
+ x, new_cache["norm2"] = self.norm2(x, zq, conv_cache=conv_cache.get("norm2"))
154
+ else:
155
+ x = self.norm2(x)
156
+
157
+ x = self.nonlinearity(x)
158
+ x, new_cache["conv2"] = self.conv2(x, conv_cache=conv_cache.get("conv2"))
159
+
160
+ if self.conv_shortcut is not None:
161
+ residual = self.conv_shortcut(residual)
162
+
163
+ return x + residual, new_cache
164
+
165
+
166
+ class Downsample3D(nn.Module):
167
+ """3D downsampling with optional temporal compression."""
168
+ def __init__(self, in_channels, out_channels, kernel_size=3, stride=2, padding=0, compress_time=False):
169
+ super().__init__()
170
+ self.conv = ops.Conv2d(in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=padding)
171
+ self.compress_time = compress_time
172
+
173
+ def forward(self, x):
174
+ if self.compress_time:
175
+ b, c, t, h, w = x.shape
176
+ x = x.permute(0, 3, 4, 1, 2).reshape(b * h * w, c, t)
177
+ if t % 2 == 1:
178
+ x_first, x_rest = x[..., 0], x[..., 1:]
179
+ if x_rest.shape[-1] > 0:
180
+ x_rest = F.avg_pool1d(x_rest, kernel_size=2, stride=2)
181
+ x = torch.cat([x_first[..., None], x_rest], dim=-1)
182
+ x = x.reshape(b, h, w, c, x.shape[-1]).permute(0, 3, 4, 1, 2)
183
+ else:
184
+ x = F.avg_pool1d(x, kernel_size=2, stride=2)
185
+ x = x.reshape(b, h, w, c, x.shape[-1]).permute(0, 3, 4, 1, 2)
186
+
187
+ pad = (0, 1, 0, 1)
188
+ x = F.pad(x, pad, mode="constant", value=0)
189
+ b, c, t, h, w = x.shape
190
+ x = x.permute(0, 2, 1, 3, 4).reshape(b * t, c, h, w)
191
+ x = self.conv(x)
192
+ x = x.reshape(b, t, x.shape[1], x.shape[2], x.shape[3]).permute(0, 2, 1, 3, 4)
193
+ return x
194
+
195
+
196
+ class Upsample3D(nn.Module):
197
+ """3D upsampling with optional temporal decompression."""
198
+ def __init__(self, in_channels, out_channels, kernel_size=3, stride=1, padding=1, compress_time=False):
199
+ super().__init__()
200
+ self.conv = ops.Conv2d(in_channels, out_channels, kernel_size=kernel_size, stride=stride, padding=padding)
201
+ self.compress_time = compress_time
202
+
203
+ def forward(self, x):
204
+ if self.compress_time:
205
+ if x.shape[2] > 1 and x.shape[2] % 2 == 1:
206
+ x_first, x_rest = x[:, :, 0], x[:, :, 1:]
207
+ x_first = F.interpolate(x_first, scale_factor=2.0)
208
+ x_rest = F.interpolate(x_rest, scale_factor=2.0)
209
+ x = torch.cat([x_first[:, :, None, :, :], x_rest], dim=2)
210
+ elif x.shape[2] > 1:
211
+ x = F.interpolate(x, scale_factor=2.0)
212
+ else:
213
+ x = x.squeeze(2)
214
+ x = F.interpolate(x, scale_factor=2.0)
215
+ x = x[:, :, None, :, :]
216
+ else:
217
+ b, c, t, h, w = x.shape
218
+ x = x.permute(0, 2, 1, 3, 4).reshape(b * t, c, h, w)
219
+ x = F.interpolate(x, scale_factor=2.0)
220
+ x = x.reshape(b, t, c, *x.shape[2:]).permute(0, 2, 1, 3, 4)
221
+
222
+ b, c, t, h, w = x.shape
223
+ x = x.permute(0, 2, 1, 3, 4).reshape(b * t, c, h, w)
224
+ x = self.conv(x)
225
+ x = x.reshape(b, t, *x.shape[1:]).permute(0, 2, 1, 3, 4)
226
+ return x
227
+
228
+
229
+ class DownBlock3D(nn.Module):
230
+ def __init__(self, in_channels, out_channels, temb_channels=0, num_layers=1,
231
+ eps=1e-6, act_fn="silu", groups=32, add_downsample=True,
232
+ compress_time=False, pad_mode="first"):
233
+ super().__init__()
234
+ self.resnets = nn.ModuleList([
235
+ ResnetBlock3D(
236
+ in_channels=in_channels if i == 0 else out_channels,
237
+ out_channels=out_channels,
238
+ temb_channels=temb_channels,
239
+ groups=groups, eps=eps, act_fn=act_fn, pad_mode=pad_mode,
240
+ )
241
+ for i in range(num_layers)
242
+ ])
243
+ self.downsamplers = nn.ModuleList([Downsample3D(out_channels, out_channels, compress_time=compress_time)]) if add_downsample else None
244
+
245
+ def forward(self, x, temb=None, zq=None, conv_cache=None):
246
+ new_cache = {}
247
+ conv_cache = conv_cache or {}
248
+ for i, resnet in enumerate(self.resnets):
249
+ x, new_cache[f"resnet_{i}"] = resnet(x, temb, zq, conv_cache=conv_cache.get(f"resnet_{i}"))
250
+ if self.downsamplers is not None:
251
+ for ds in self.downsamplers:
252
+ x = ds(x)
253
+ return x, new_cache
254
+
255
+
256
+ class MidBlock3D(nn.Module):
257
+ def __init__(self, in_channels, temb_channels=0, num_layers=1,
258
+ eps=1e-6, act_fn="silu", groups=32, spatial_norm_dim=None, pad_mode="first"):
259
+ super().__init__()
260
+ self.resnets = nn.ModuleList([
261
+ ResnetBlock3D(
262
+ in_channels=in_channels, out_channels=in_channels,
263
+ temb_channels=temb_channels, groups=groups, eps=eps,
264
+ act_fn=act_fn, spatial_norm_dim=spatial_norm_dim, pad_mode=pad_mode,
265
+ )
266
+ for _ in range(num_layers)
267
+ ])
268
+
269
+ def forward(self, x, temb=None, zq=None, conv_cache=None):
270
+ new_cache = {}
271
+ conv_cache = conv_cache or {}
272
+ for i, resnet in enumerate(self.resnets):
273
+ x, new_cache[f"resnet_{i}"] = resnet(x, temb, zq, conv_cache=conv_cache.get(f"resnet_{i}"))
274
+ return x, new_cache
275
+
276
+
277
+ class UpBlock3D(nn.Module):
278
+ def __init__(self, in_channels, out_channels, temb_channels=0, num_layers=1,
279
+ eps=1e-6, act_fn="silu", groups=32, spatial_norm_dim=16,
280
+ add_upsample=True, compress_time=False, pad_mode="first"):
281
+ super().__init__()
282
+ self.resnets = nn.ModuleList([
283
+ ResnetBlock3D(
284
+ in_channels=in_channels if i == 0 else out_channels,
285
+ out_channels=out_channels,
286
+ temb_channels=temb_channels, groups=groups, eps=eps,
287
+ act_fn=act_fn, spatial_norm_dim=spatial_norm_dim, pad_mode=pad_mode,
288
+ )
289
+ for i in range(num_layers)
290
+ ])
291
+ self.upsamplers = nn.ModuleList([Upsample3D(out_channels, out_channels, compress_time=compress_time)]) if add_upsample else None
292
+
293
+ def forward(self, x, temb=None, zq=None, conv_cache=None):
294
+ new_cache = {}
295
+ conv_cache = conv_cache or {}
296
+ for i, resnet in enumerate(self.resnets):
297
+ x, new_cache[f"resnet_{i}"] = resnet(x, temb, zq, conv_cache=conv_cache.get(f"resnet_{i}"))
298
+ if self.upsamplers is not None:
299
+ for us in self.upsamplers:
300
+ x = us(x)
301
+ return x, new_cache
302
+
303
+
304
+ class Encoder3D(nn.Module):
305
+ def __init__(self, in_channels=3, out_channels=16,
306
+ block_out_channels=(128, 256, 256, 512),
307
+ layers_per_block=3, act_fn="silu",
308
+ eps=1e-6, groups=32, pad_mode="first",
309
+ temporal_compression_ratio=4):
310
+ super().__init__()
311
+ temporal_compress_level = int(np.log2(temporal_compression_ratio))
312
+
313
+ self.conv_in = CausalConv3d(in_channels, block_out_channels[0], kernel_size=3, pad_mode=pad_mode)
314
+
315
+ self.down_blocks = nn.ModuleList()
316
+ output_channel = block_out_channels[0]
317
+ for i in range(len(block_out_channels)):
318
+ input_channel = output_channel
319
+ output_channel = block_out_channels[i]
320
+ is_final = i == len(block_out_channels) - 1
321
+ compress_time = i < temporal_compress_level
322
+
323
+ self.down_blocks.append(DownBlock3D(
324
+ in_channels=input_channel, out_channels=output_channel,
325
+ temb_channels=0, num_layers=layers_per_block,
326
+ eps=eps, act_fn=act_fn, groups=groups,
327
+ add_downsample=not is_final, compress_time=compress_time,
328
+ ))
329
+
330
+ self.mid_block = MidBlock3D(
331
+ in_channels=block_out_channels[-1], temb_channels=0,
332
+ num_layers=2, eps=eps, act_fn=act_fn, groups=groups, pad_mode=pad_mode,
333
+ )
334
+
335
+ self.norm_out = ops.GroupNorm(groups, block_out_channels[-1], eps=1e-6)
336
+ self.conv_act = nn.SiLU()
337
+ self.conv_out = CausalConv3d(block_out_channels[-1], 2 * out_channels, kernel_size=3, pad_mode=pad_mode)
338
+
339
+ def forward(self, x, conv_cache=None):
340
+ new_cache = {}
341
+ conv_cache = conv_cache or {}
342
+
343
+ x, new_cache["conv_in"] = self.conv_in(x, conv_cache=conv_cache.get("conv_in"))
344
+
345
+ for i, block in enumerate(self.down_blocks):
346
+ key = f"down_block_{i}"
347
+ x, new_cache[key] = block(x, None, None, conv_cache.get(key))
348
+
349
+ x, new_cache["mid_block"] = self.mid_block(x, None, None, conv_cache=conv_cache.get("mid_block"))
350
+
351
+ x = self.norm_out(x)
352
+ x = self.conv_act(x)
353
+ x, new_cache["conv_out"] = self.conv_out(x, conv_cache=conv_cache.get("conv_out"))
354
+
355
+ return x, new_cache
356
+
357
+
358
+ class Decoder3D(nn.Module):
359
+ def __init__(self, in_channels=16, out_channels=3,
360
+ block_out_channels=(128, 256, 256, 512),
361
+ layers_per_block=3, act_fn="silu",
362
+ eps=1e-6, groups=32, pad_mode="first",
363
+ temporal_compression_ratio=4):
364
+ super().__init__()
365
+ reversed_channels = list(reversed(block_out_channels))
366
+ temporal_compress_level = int(np.log2(temporal_compression_ratio))
367
+
368
+ self.conv_in = CausalConv3d(in_channels, reversed_channels[0], kernel_size=3, pad_mode=pad_mode)
369
+
370
+ self.mid_block = MidBlock3D(
371
+ in_channels=reversed_channels[0], temb_channels=0,
372
+ num_layers=2, eps=eps, act_fn=act_fn, groups=groups,
373
+ spatial_norm_dim=in_channels, pad_mode=pad_mode,
374
+ )
375
+
376
+ self.up_blocks = nn.ModuleList()
377
+ output_channel = reversed_channels[0]
378
+ for i in range(len(block_out_channels)):
379
+ prev_channel = output_channel
380
+ output_channel = reversed_channels[i]
381
+ is_final = i == len(block_out_channels) - 1
382
+ compress_time = i < temporal_compress_level
383
+
384
+ self.up_blocks.append(UpBlock3D(
385
+ in_channels=prev_channel, out_channels=output_channel,
386
+ temb_channels=0, num_layers=layers_per_block + 1,
387
+ eps=eps, act_fn=act_fn, groups=groups,
388
+ spatial_norm_dim=in_channels,
389
+ add_upsample=not is_final, compress_time=compress_time,
390
+ ))
391
+
392
+ self.norm_out = SpatialNorm3D(reversed_channels[-1], in_channels, groups=groups)
393
+ self.conv_act = nn.SiLU()
394
+ self.conv_out = CausalConv3d(reversed_channels[-1], out_channels, kernel_size=3, pad_mode=pad_mode)
395
+
396
+ def forward(self, sample, conv_cache=None):
397
+ new_cache = {}
398
+ conv_cache = conv_cache or {}
399
+
400
+ x, new_cache["conv_in"] = self.conv_in(sample, conv_cache=conv_cache.get("conv_in"))
401
+
402
+ x, new_cache["mid_block"] = self.mid_block(x, None, sample, conv_cache=conv_cache.get("mid_block"))
403
+
404
+ for i, block in enumerate(self.up_blocks):
405
+ key = f"up_block_{i}"
406
+ x, new_cache[key] = block(x, None, sample, conv_cache=conv_cache.get(key))
407
+
408
+ x, new_cache["norm_out"] = self.norm_out(x, sample, conv_cache=conv_cache.get("norm_out"))
409
+ x = self.conv_act(x)
410
+ x, new_cache["conv_out"] = self.conv_out(x, conv_cache=conv_cache.get("conv_out"))
411
+
412
+ return x, new_cache
413
+
414
+
415
+
416
+ class AutoencoderKLCogVideoX(nn.Module):
417
+ """CogVideoX VAE. Spatial tiling/slicing handled by ComfyUI's VAE wrapper.
418
+
419
+ Uses rolling temporal decode: conv_in + mid_block + temporal up_blocks run
420
+ on the full (low-res) tensor, then the expensive spatial-only up_blocks +
421
+ norm_out + conv_out are processed in small temporal chunks with conv_cache
422
+ carrying causal state between chunks. This keeps peak VRAM proportional to
423
+ chunk_size rather than total frame count.
424
+ """
425
+
426
+ def __init__(self,
427
+ in_channels=3, out_channels=3,
428
+ block_out_channels=(128, 256, 256, 512),
429
+ latent_channels=16, layers_per_block=3,
430
+ act_fn="silu", eps=1e-6, groups=32,
431
+ temporal_compression_ratio=4,
432
+ ):
433
+ super().__init__()
434
+ self.latent_channels = latent_channels
435
+ self.temporal_compression_ratio = temporal_compression_ratio
436
+
437
+ self.encoder = Encoder3D(
438
+ in_channels=in_channels, out_channels=latent_channels,
439
+ block_out_channels=block_out_channels, layers_per_block=layers_per_block,
440
+ act_fn=act_fn, eps=eps, groups=groups,
441
+ temporal_compression_ratio=temporal_compression_ratio,
442
+ )
443
+ self.decoder = Decoder3D(
444
+ in_channels=latent_channels, out_channels=out_channels,
445
+ block_out_channels=block_out_channels, layers_per_block=layers_per_block,
446
+ act_fn=act_fn, eps=eps, groups=groups,
447
+ temporal_compression_ratio=temporal_compression_ratio,
448
+ )
449
+
450
+ self.num_latent_frames_batch_size = 2
451
+ self.num_sample_frames_batch_size = 8
452
+
453
+ def encode(self, x):
454
+ t = x.shape[2]
455
+ frame_batch = self.num_sample_frames_batch_size
456
+ remainder = t % frame_batch
457
+ conv_cache = None
458
+ enc = []
459
+
460
+ # Process remainder frames first so only the first chunk can have an
461
+ # odd temporal dimension — where Downsample3D's first-frame-special
462
+ # handling in temporal compression is actually correct.
463
+ if remainder > 0:
464
+ chunk, conv_cache = self.encoder(x[:, :, :remainder], conv_cache=conv_cache)
465
+ enc.append(chunk.to(x.device))
466
+
467
+ for start in range(remainder, t, frame_batch):
468
+ chunk, conv_cache = self.encoder(x[:, :, start:start + frame_batch], conv_cache=conv_cache)
469
+ enc.append(chunk.to(x.device))
470
+
471
+ enc = torch.cat(enc, dim=2)
472
+ mean, _ = enc.chunk(2, dim=1)
473
+ return mean
474
+
475
+ def decode(self, z):
476
+ return self._decode_rolling(z)
477
+
478
+ def _decode_batched(self, z):
479
+ """Original batched decode - processes 2 latent frames through full decoder."""
480
+ t = z.shape[2]
481
+ frame_batch = self.num_latent_frames_batch_size
482
+ num_batches = max(t // frame_batch, 1)
483
+ conv_cache = None
484
+ dec = []
485
+ for i in range(num_batches):
486
+ remaining = t % frame_batch
487
+ start = frame_batch * i + (0 if i == 0 else remaining)
488
+ end = frame_batch * (i + 1) + remaining
489
+ chunk, conv_cache = self.decoder(z[:, :, start:end], conv_cache=conv_cache)
490
+ dec.append(chunk.cpu())
491
+ return torch.cat(dec, dim=2).to(z.device)
492
+
493
+ def _decode_rolling(self, z):
494
+ """Rolling decode - processes low-res layers on full tensor, then rolls
495
+ through expensive high-res layers in temporal chunks."""
496
+ decoder = self.decoder
497
+ device = z.device
498
+
499
+ # Determine which up_blocks have temporal upsample vs spatial-only.
500
+ # Temporal up_blocks are cheap (low res), spatial-only are expensive.
501
+ temporal_compress_level = int(np.log2(self.temporal_compression_ratio))
502
+ split_at = temporal_compress_level # first N up_blocks do temporal upsample
503
+
504
+ # Phase 1: conv_in + mid_block + temporal up_blocks on full tensor (low/medium res)
505
+ x, _ = decoder.conv_in(z)
506
+ x, _ = decoder.mid_block(x, None, z)
507
+
508
+ for i in range(split_at):
509
+ x, _ = decoder.up_blocks[i](x, None, z)
510
+
511
+ # Phase 2: remaining spatial-only up_blocks + norm_out + conv_out in temporal chunks
512
+ remaining_blocks = list(range(split_at, len(decoder.up_blocks)))
513
+ chunk_size = 4 # pixel frames per chunk through high-res layers
514
+ t_expanded = x.shape[2]
515
+
516
+ if t_expanded <= chunk_size or len(remaining_blocks) == 0:
517
+ # Small enough to process in one go
518
+ for i in remaining_blocks:
519
+ x, _ = decoder.up_blocks[i](x, None, z)
520
+ x, _ = decoder.norm_out(x, z)
521
+ x = decoder.conv_act(x)
522
+ x, _ = decoder.conv_out(x)
523
+ return x
524
+
525
+ # Expand z temporally once to match Phase 2's time dimension.
526
+ # z stays at latent spatial resolution so this is small (~16 MB vs ~1.3 GB
527
+ # for the old approach of pre-interpolating to every pixel resolution).
528
+ z_time_expanded = _interpolate_zq(z, (t_expanded, z.shape[3], z.shape[4]))
529
+
530
+ # Process in temporal chunks, interpolating spatially per-chunk to avoid
531
+ # allocating full [B, C, t_expanded, H, W] tensors at each resolution.
532
+ dec_out = []
533
+ conv_caches = {}
534
+
535
+ for chunk_start in range(0, t_expanded, chunk_size):
536
+ chunk_end = min(chunk_start + chunk_size, t_expanded)
537
+ x_chunk = x[:, :, chunk_start:chunk_end]
538
+ z_t_chunk = z_time_expanded[:, :, chunk_start:chunk_end]
539
+ z_spatial_cache = {}
540
+
541
+ for i in remaining_blocks:
542
+ block = decoder.up_blocks[i]
543
+ cache_key = f"up_block_{i}"
544
+ hw_key = (x_chunk.shape[3], x_chunk.shape[4])
545
+ if hw_key not in z_spatial_cache:
546
+ if z_t_chunk.shape[3] == hw_key[0] and z_t_chunk.shape[4] == hw_key[1]:
547
+ z_spatial_cache[hw_key] = z_t_chunk
548
+ else:
549
+ z_spatial_cache[hw_key] = F.interpolate(z_t_chunk, size=(z_t_chunk.shape[2], hw_key[0], hw_key[1]))
550
+ x_chunk, new_cache = block(x_chunk, None, z_spatial_cache[hw_key], conv_cache=conv_caches.get(cache_key))
551
+ conv_caches[cache_key] = new_cache
552
+
553
+ hw_key = (x_chunk.shape[3], x_chunk.shape[4])
554
+ if hw_key not in z_spatial_cache:
555
+ z_spatial_cache[hw_key] = F.interpolate(z_t_chunk, size=(z_t_chunk.shape[2], hw_key[0], hw_key[1]))
556
+ x_chunk, new_cache = decoder.norm_out(x_chunk, z_spatial_cache[hw_key], conv_cache=conv_caches.get("norm_out"))
557
+ conv_caches["norm_out"] = new_cache
558
+ x_chunk = decoder.conv_act(x_chunk)
559
+ x_chunk, new_cache = decoder.conv_out(x_chunk, conv_cache=conv_caches.get("conv_out"))
560
+ conv_caches["conv_out"] = new_cache
561
+
562
+ dec_out.append(x_chunk.cpu())
563
+ del z_spatial_cache
564
+
565
+ del x, z_time_expanded
566
+ return torch.cat(dec_out, dim=2).to(device)
ComfyUI/comfy/ldm/colormap.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Colormap utilities for depth and geometry visualisation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import torch
6
+
7
+
8
+ def turbo(x: torch.Tensor) -> torch.Tensor:
9
+ """Anton Mikhailov polynomial approximation of the Turbo colormap.
10
+
11
+ Args:
12
+ x: Float tensor with values in [0, 1].
13
+
14
+ Returns:
15
+ RGB tensor of the same shape as ``x`` with a trailing size-3 dimension.
16
+ """
17
+ x = x.clamp(0.0, 1.0)
18
+ x2 = x * x
19
+ x3 = x2 * x
20
+ x4 = x2 * x2
21
+ x5 = x4 * x
22
+ r = 0.13572138 + 4.61539260*x - 42.66032258*x2 + 132.13108234*x3 - 152.94239396*x4 + 59.28637943*x5
23
+ g = 0.09140261 + 2.19418839*x + 4.84296658*x2 - 14.18503333*x3 + 4.27729857*x4 + 2.82956604*x5
24
+ b = 0.10667330 + 12.64194608*x - 60.58204836*x2 + 110.36276771*x3 - 89.90310912*x4 + 27.34824973*x5
25
+ return torch.stack([r, g, b], dim=-1).clamp(0.0, 1.0)
ComfyUI/comfy/ldm/common_dit.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import comfy.rmsnorm
3
+
4
+
5
+ def pad_to_patch_size(img, patch_size=(2, 2), padding_mode="circular"):
6
+ if padding_mode == "circular" and (torch.jit.is_tracing() or torch.jit.is_scripting()):
7
+ padding_mode = "reflect"
8
+
9
+ pad = ()
10
+ for i in range(img.ndim - 2):
11
+ pad = (0, (patch_size[i] - img.shape[i + 2] % patch_size[i]) % patch_size[i]) + pad
12
+
13
+ return torch.nn.functional.pad(img, pad, mode=padding_mode)
14
+
15
+
16
+ rms_norm = comfy.rmsnorm.rms_norm
ComfyUI/comfy/ldm/cosmos/blocks.py ADDED
@@ -0,0 +1,805 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
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 math
17
+ from typing import Optional
18
+ import logging
19
+
20
+ import numpy as np
21
+ import torch
22
+ from einops import rearrange, repeat
23
+ from einops.layers.torch import Rearrange
24
+ from torch import nn
25
+
26
+ from comfy.ldm.modules.attention import optimized_attention
27
+
28
+
29
+ def get_normalization(name: str, channels: int, weight_args={}, operations=None):
30
+ if name == "I":
31
+ return nn.Identity()
32
+ elif name == "R":
33
+ return operations.RMSNorm(channels, elementwise_affine=True, eps=1e-6, **weight_args)
34
+ else:
35
+ raise ValueError(f"Normalization {name} not found")
36
+
37
+
38
+ class BaseAttentionOp(nn.Module):
39
+ def __init__(self):
40
+ super().__init__()
41
+
42
+
43
+ class Attention(nn.Module):
44
+ """
45
+ Generalized attention impl.
46
+
47
+ Allowing for both self-attention and cross-attention configurations depending on whether a `context_dim` is provided.
48
+ If `context_dim` is None, self-attention is assumed.
49
+
50
+ Parameters:
51
+ query_dim (int): Dimension of each query vector.
52
+ context_dim (int, optional): Dimension of each context vector. If None, self-attention is assumed.
53
+ heads (int, optional): Number of attention heads. Defaults to 8.
54
+ dim_head (int, optional): Dimension of each head. Defaults to 64.
55
+ dropout (float, optional): Dropout rate applied to the output of the attention block. Defaults to 0.0.
56
+ attn_op (BaseAttentionOp, optional): Custom attention operation to be used instead of the default.
57
+ qkv_bias (bool, optional): If True, adds a learnable bias to query, key, and value projections. Defaults to False.
58
+ out_bias (bool, optional): If True, adds a learnable bias to the output projection. Defaults to False.
59
+ qkv_norm (str, optional): A string representing normalization strategies for query, key, and value projections.
60
+ Defaults to "SSI".
61
+ qkv_norm_mode (str, optional): A string representing normalization mode for query, key, and value projections.
62
+ Defaults to 'per_head'. Only support 'per_head'.
63
+
64
+ Examples:
65
+ >>> attn = Attention(query_dim=128, context_dim=256, heads=4, dim_head=32, dropout=0.1)
66
+ >>> query = torch.randn(10, 128) # Batch size of 10
67
+ >>> context = torch.randn(10, 256) # Batch size of 10
68
+ >>> output = attn(query, context) # Perform the attention operation
69
+
70
+ Note:
71
+ https://github.com/MatthieuTPHR/diffusers/blob/d80b531ff8060ec1ea982b65a1b8df70f73aa67c/src/diffusers/models/attention.py#L223
72
+ """
73
+
74
+ def __init__(
75
+ self,
76
+ query_dim: int,
77
+ context_dim=None,
78
+ heads=8,
79
+ dim_head=64,
80
+ dropout=0.0,
81
+ attn_op: Optional[BaseAttentionOp] = None,
82
+ qkv_bias: bool = False,
83
+ out_bias: bool = False,
84
+ qkv_norm: str = "SSI",
85
+ qkv_norm_mode: str = "per_head",
86
+ backend: str = "transformer_engine",
87
+ qkv_format: str = "bshd",
88
+ weight_args={},
89
+ operations=None,
90
+ ) -> None:
91
+ super().__init__()
92
+
93
+ self.is_selfattn = context_dim is None # self attention
94
+
95
+ inner_dim = dim_head * heads
96
+ context_dim = query_dim if context_dim is None else context_dim
97
+
98
+ self.heads = heads
99
+ self.dim_head = dim_head
100
+ self.qkv_norm_mode = qkv_norm_mode
101
+ self.qkv_format = qkv_format
102
+
103
+ if self.qkv_norm_mode == "per_head":
104
+ norm_dim = dim_head
105
+ else:
106
+ raise ValueError(f"Normalization mode {self.qkv_norm_mode} not found, only support 'per_head'")
107
+
108
+ self.backend = backend
109
+
110
+ self.to_q = nn.Sequential(
111
+ operations.Linear(query_dim, inner_dim, bias=qkv_bias, **weight_args),
112
+ get_normalization(qkv_norm[0], norm_dim, weight_args=weight_args, operations=operations),
113
+ )
114
+ self.to_k = nn.Sequential(
115
+ operations.Linear(context_dim, inner_dim, bias=qkv_bias, **weight_args),
116
+ get_normalization(qkv_norm[1], norm_dim, weight_args=weight_args, operations=operations),
117
+ )
118
+ self.to_v = nn.Sequential(
119
+ operations.Linear(context_dim, inner_dim, bias=qkv_bias, **weight_args),
120
+ get_normalization(qkv_norm[2], norm_dim, weight_args=weight_args, operations=operations),
121
+ )
122
+
123
+ self.to_out = nn.Sequential(
124
+ operations.Linear(inner_dim, query_dim, bias=out_bias, **weight_args),
125
+ nn.Dropout(dropout),
126
+ )
127
+
128
+ def cal_qkv(
129
+ self, x, context=None, mask=None, rope_emb=None, **kwargs
130
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
131
+ del kwargs
132
+
133
+
134
+ """
135
+ self.to_q, self.to_k, self.to_v are nn.Sequential with projection + normalization layers.
136
+ Before 07/24/2024, these modules normalize across all heads.
137
+ After 07/24/2024, to support tensor parallelism and follow the common practice in the community,
138
+ we support to normalize per head.
139
+ To keep the checkpoint copatibility with the previous code,
140
+ we keep the nn.Sequential but call the projection and the normalization layers separately.
141
+ We use a flag `self.qkv_norm_mode` to control the normalization behavior.
142
+ The default value of `self.qkv_norm_mode` is "per_head", which means we normalize per head.
143
+ """
144
+ if self.qkv_norm_mode == "per_head":
145
+ q = self.to_q[0](x)
146
+ context = x if context is None else context
147
+ k = self.to_k[0](context)
148
+ v = self.to_v[0](context)
149
+ q, k, v = map(
150
+ lambda t: rearrange(t, "s b (n c) -> b n s c", n=self.heads, c=self.dim_head),
151
+ (q, k, v),
152
+ )
153
+ else:
154
+ raise ValueError(f"Normalization mode {self.qkv_norm_mode} not found, only support 'per_head'")
155
+
156
+ q = self.to_q[1](q)
157
+ k = self.to_k[1](k)
158
+ v = self.to_v[1](v)
159
+ if self.is_selfattn and rope_emb is not None: # only apply to self-attention!
160
+ # apply_rotary_pos_emb inlined
161
+ q_shape = q.shape
162
+ q = q.reshape(*q.shape[:-1], 2, -1).movedim(-2, -1).unsqueeze(-2)
163
+ q = rope_emb[..., 0] * q[..., 0] + rope_emb[..., 1] * q[..., 1]
164
+ q = q.movedim(-1, -2).reshape(*q_shape).to(x.dtype)
165
+
166
+ # apply_rotary_pos_emb inlined
167
+ k_shape = k.shape
168
+ k = k.reshape(*k.shape[:-1], 2, -1).movedim(-2, -1).unsqueeze(-2)
169
+ k = rope_emb[..., 0] * k[..., 0] + rope_emb[..., 1] * k[..., 1]
170
+ k = k.movedim(-1, -2).reshape(*k_shape).to(x.dtype)
171
+ return q, k, v
172
+
173
+ def forward(
174
+ self,
175
+ x,
176
+ context=None,
177
+ mask=None,
178
+ rope_emb=None,
179
+ transformer_options={},
180
+ **kwargs,
181
+ ):
182
+ """
183
+ Args:
184
+ x (Tensor): The query tensor of shape [B, Mq, K]
185
+ context (Optional[Tensor]): The key tensor of shape [B, Mk, K] or use x as context [self attention] if None
186
+ """
187
+ q, k, v = self.cal_qkv(x, context, mask, rope_emb=rope_emb, **kwargs)
188
+ out = optimized_attention(q, k, v, self.heads, skip_reshape=True, mask=mask, skip_output_reshape=True, transformer_options=transformer_options)
189
+ del q, k, v
190
+ out = rearrange(out, " b n s c -> s b (n c)")
191
+ return self.to_out(out)
192
+
193
+
194
+ class FeedForward(nn.Module):
195
+ """
196
+ Transformer FFN with optional gating
197
+
198
+ Parameters:
199
+ d_model (int): Dimensionality of input features.
200
+ d_ff (int): Dimensionality of the hidden layer.
201
+ dropout (float, optional): Dropout rate applied after the activation function. Defaults to 0.1.
202
+ activation (callable, optional): The activation function applied after the first linear layer.
203
+ Defaults to nn.ReLU().
204
+ is_gated (bool, optional): If set to True, incorporates gating mechanism to the feed-forward layer.
205
+ Defaults to False.
206
+ bias (bool, optional): If set to True, adds a bias to the linear layers. Defaults to True.
207
+
208
+ Example:
209
+ >>> ff = FeedForward(d_model=512, d_ff=2048)
210
+ >>> x = torch.randn(64, 10, 512) # Example input tensor
211
+ >>> output = ff(x)
212
+ >>> print(output.shape) # Expected shape: (64, 10, 512)
213
+ """
214
+
215
+ def __init__(
216
+ self,
217
+ d_model: int,
218
+ d_ff: int,
219
+ dropout: float = 0.1,
220
+ activation=nn.ReLU(),
221
+ is_gated: bool = False,
222
+ bias: bool = False,
223
+ weight_args={},
224
+ operations=None,
225
+ ) -> None:
226
+ super().__init__()
227
+
228
+ self.layer1 = operations.Linear(d_model, d_ff, bias=bias, **weight_args)
229
+ self.layer2 = operations.Linear(d_ff, d_model, bias=bias, **weight_args)
230
+
231
+ self.dropout = nn.Dropout(dropout)
232
+ self.activation = activation
233
+ self.is_gated = is_gated
234
+ if is_gated:
235
+ self.linear_gate = operations.Linear(d_model, d_ff, bias=False, **weight_args)
236
+
237
+ def forward(self, x: torch.Tensor):
238
+ g = self.activation(self.layer1(x))
239
+ if self.is_gated:
240
+ x = g * self.linear_gate(x)
241
+ else:
242
+ x = g
243
+ assert self.dropout.p == 0.0, "we skip dropout"
244
+ return self.layer2(x)
245
+
246
+
247
+ class GPT2FeedForward(FeedForward):
248
+ def __init__(self, d_model: int, d_ff: int, dropout: float = 0.1, bias: bool = False, weight_args={}, operations=None):
249
+ super().__init__(
250
+ d_model=d_model,
251
+ d_ff=d_ff,
252
+ dropout=dropout,
253
+ activation=nn.GELU(),
254
+ is_gated=False,
255
+ bias=bias,
256
+ weight_args=weight_args,
257
+ operations=operations,
258
+ )
259
+
260
+ def forward(self, x: torch.Tensor):
261
+ assert self.dropout.p == 0.0, "we skip dropout"
262
+
263
+ x = self.layer1(x)
264
+ x = self.activation(x)
265
+ x = self.layer2(x)
266
+
267
+ return x
268
+
269
+
270
+ def modulate(x, shift, scale):
271
+ return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1)
272
+
273
+
274
+ class Timesteps(nn.Module):
275
+ def __init__(self, num_channels):
276
+ super().__init__()
277
+ self.num_channels = num_channels
278
+
279
+ def forward(self, timesteps):
280
+ half_dim = self.num_channels // 2
281
+ exponent = -math.log(10000) * torch.arange(half_dim, dtype=torch.float32, device=timesteps.device)
282
+ exponent = exponent / (half_dim - 0.0)
283
+
284
+ emb = torch.exp(exponent)
285
+ emb = timesteps[:, None].float() * emb[None, :]
286
+
287
+ sin_emb = torch.sin(emb)
288
+ cos_emb = torch.cos(emb)
289
+ emb = torch.cat([cos_emb, sin_emb], dim=-1)
290
+
291
+ return emb
292
+
293
+
294
+ class TimestepEmbedding(nn.Module):
295
+ def __init__(self, in_features: int, out_features: int, use_adaln_lora: bool = False, weight_args={}, operations=None):
296
+ super().__init__()
297
+ logging.debug(
298
+ f"Using AdaLN LoRA Flag: {use_adaln_lora}. We enable bias if no AdaLN LoRA for backward compatibility."
299
+ )
300
+ self.linear_1 = operations.Linear(in_features, out_features, bias=not use_adaln_lora, **weight_args)
301
+ self.activation = nn.SiLU()
302
+ self.use_adaln_lora = use_adaln_lora
303
+ if use_adaln_lora:
304
+ self.linear_2 = operations.Linear(out_features, 3 * out_features, bias=False, **weight_args)
305
+ else:
306
+ self.linear_2 = operations.Linear(out_features, out_features, bias=True, **weight_args)
307
+
308
+ def forward(self, sample: torch.Tensor) -> torch.Tensor:
309
+ emb = self.linear_1(sample)
310
+ emb = self.activation(emb)
311
+ emb = self.linear_2(emb)
312
+
313
+ if self.use_adaln_lora:
314
+ adaln_lora_B_3D = emb
315
+ emb_B_D = sample
316
+ else:
317
+ emb_B_D = emb
318
+ adaln_lora_B_3D = None
319
+
320
+ return emb_B_D, adaln_lora_B_3D
321
+
322
+
323
+ class FourierFeatures(nn.Module):
324
+ """
325
+ Implements a layer that generates Fourier features from input tensors, based on randomly sampled
326
+ frequencies and phases. This can help in learning high-frequency functions in low-dimensional problems.
327
+
328
+ [B] -> [B, D]
329
+
330
+ Parameters:
331
+ num_channels (int): The number of Fourier features to generate.
332
+ bandwidth (float, optional): The scaling factor for the frequency of the Fourier features. Defaults to 1.
333
+ normalize (bool, optional): If set to True, the outputs are scaled by sqrt(2), usually to normalize
334
+ the variance of the features. Defaults to False.
335
+
336
+ Example:
337
+ >>> layer = FourierFeatures(num_channels=256, bandwidth=0.5, normalize=True)
338
+ >>> x = torch.randn(10, 256) # Example input tensor
339
+ >>> output = layer(x)
340
+ >>> print(output.shape) # Expected shape: (10, 256)
341
+ """
342
+
343
+ def __init__(self, num_channels, bandwidth=1, normalize=False):
344
+ super().__init__()
345
+ self.register_buffer("freqs", 2 * np.pi * bandwidth * torch.randn(num_channels), persistent=True)
346
+ self.register_buffer("phases", 2 * np.pi * torch.rand(num_channels), persistent=True)
347
+ self.gain = np.sqrt(2) if normalize else 1
348
+
349
+ def forward(self, x, gain: float = 1.0):
350
+ """
351
+ Apply the Fourier feature transformation to the input tensor.
352
+
353
+ Args:
354
+ x (torch.Tensor): The input tensor.
355
+ gain (float, optional): An additional gain factor applied during the forward pass. Defaults to 1.
356
+
357
+ Returns:
358
+ torch.Tensor: The transformed tensor, with Fourier features applied.
359
+ """
360
+ in_dtype = x.dtype
361
+ x = x.to(torch.float32).ger(self.freqs.to(torch.float32)).add(self.phases.to(torch.float32))
362
+ x = x.cos().mul(self.gain * gain).to(in_dtype)
363
+ return x
364
+
365
+
366
+ class PatchEmbed(nn.Module):
367
+ """
368
+ PatchEmbed is a module for embedding patches from an input tensor by applying either 3D or 2D convolutional layers,
369
+ depending on the . This module can process inputs with temporal (video) and spatial (image) dimensions,
370
+ making it suitable for video and image processing tasks. It supports dividing the input into patches
371
+ and embedding each patch into a vector of size `out_channels`.
372
+
373
+ Parameters:
374
+ - spatial_patch_size (int): The size of each spatial patch.
375
+ - temporal_patch_size (int): The size of each temporal patch.
376
+ - in_channels (int): Number of input channels. Default: 3.
377
+ - out_channels (int): The dimension of the embedding vector for each patch. Default: 768.
378
+ - bias (bool): If True, adds a learnable bias to the output of the convolutional layers. Default: True.
379
+ """
380
+
381
+ def __init__(
382
+ self,
383
+ spatial_patch_size,
384
+ temporal_patch_size,
385
+ in_channels=3,
386
+ out_channels=768,
387
+ bias=True,
388
+ weight_args={},
389
+ operations=None,
390
+ ):
391
+ super().__init__()
392
+ self.spatial_patch_size = spatial_patch_size
393
+ self.temporal_patch_size = temporal_patch_size
394
+
395
+ self.proj = nn.Sequential(
396
+ Rearrange(
397
+ "b c (t r) (h m) (w n) -> b t h w (c r m n)",
398
+ r=temporal_patch_size,
399
+ m=spatial_patch_size,
400
+ n=spatial_patch_size,
401
+ ),
402
+ operations.Linear(
403
+ in_channels * spatial_patch_size * spatial_patch_size * temporal_patch_size, out_channels, bias=bias, **weight_args
404
+ ),
405
+ )
406
+ self.out = nn.Identity()
407
+
408
+ def forward(self, x):
409
+ """
410
+ Forward pass of the PatchEmbed module.
411
+
412
+ Parameters:
413
+ - x (torch.Tensor): The input tensor of shape (B, C, T, H, W) where
414
+ B is the batch size,
415
+ C is the number of channels,
416
+ T is the temporal dimension,
417
+ H is the height, and
418
+ W is the width of the input.
419
+
420
+ Returns:
421
+ - torch.Tensor: The embedded patches as a tensor, with shape b t h w c.
422
+ """
423
+ assert x.dim() == 5
424
+ _, _, T, H, W = x.shape
425
+ assert H % self.spatial_patch_size == 0 and W % self.spatial_patch_size == 0
426
+ assert T % self.temporal_patch_size == 0
427
+ x = self.proj(x)
428
+ return self.out(x)
429
+
430
+
431
+ class FinalLayer(nn.Module):
432
+ """
433
+ The final layer of video DiT.
434
+ """
435
+
436
+ def __init__(
437
+ self,
438
+ hidden_size,
439
+ spatial_patch_size,
440
+ temporal_patch_size,
441
+ out_channels,
442
+ use_adaln_lora: bool = False,
443
+ adaln_lora_dim: int = 256,
444
+ weight_args={},
445
+ operations=None,
446
+ ):
447
+ super().__init__()
448
+ self.norm_final = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, **weight_args)
449
+ self.linear = operations.Linear(
450
+ hidden_size, spatial_patch_size * spatial_patch_size * temporal_patch_size * out_channels, bias=False, **weight_args
451
+ )
452
+ self.hidden_size = hidden_size
453
+ self.n_adaln_chunks = 2
454
+ self.use_adaln_lora = use_adaln_lora
455
+ if use_adaln_lora:
456
+ self.adaLN_modulation = nn.Sequential(
457
+ nn.SiLU(),
458
+ operations.Linear(hidden_size, adaln_lora_dim, bias=False, **weight_args),
459
+ operations.Linear(adaln_lora_dim, self.n_adaln_chunks * hidden_size, bias=False, **weight_args),
460
+ )
461
+ else:
462
+ self.adaLN_modulation = nn.Sequential(
463
+ nn.SiLU(), operations.Linear(hidden_size, self.n_adaln_chunks * hidden_size, bias=False, **weight_args)
464
+ )
465
+
466
+ def forward(
467
+ self,
468
+ x_BT_HW_D,
469
+ emb_B_D,
470
+ adaln_lora_B_3D: Optional[torch.Tensor] = None,
471
+ ):
472
+ if self.use_adaln_lora:
473
+ assert adaln_lora_B_3D is not None
474
+ shift_B_D, scale_B_D = (self.adaLN_modulation(emb_B_D) + adaln_lora_B_3D[:, : 2 * self.hidden_size]).chunk(
475
+ 2, dim=1
476
+ )
477
+ else:
478
+ shift_B_D, scale_B_D = self.adaLN_modulation(emb_B_D).chunk(2, dim=1)
479
+
480
+ B = emb_B_D.shape[0]
481
+ T = x_BT_HW_D.shape[0] // B
482
+ shift_BT_D, scale_BT_D = repeat(shift_B_D, "b d -> (b t) d", t=T), repeat(scale_B_D, "b d -> (b t) d", t=T)
483
+ x_BT_HW_D = modulate(self.norm_final(x_BT_HW_D), shift_BT_D, scale_BT_D)
484
+
485
+ x_BT_HW_D = self.linear(x_BT_HW_D)
486
+ return x_BT_HW_D
487
+
488
+
489
+ class VideoAttn(nn.Module):
490
+ """
491
+ Implements video attention with optional cross-attention capabilities.
492
+
493
+ This module processes video features while maintaining their spatio-temporal structure. It can perform
494
+ self-attention within the video features or cross-attention with external context features.
495
+
496
+ Parameters:
497
+ x_dim (int): Dimension of input feature vectors
498
+ context_dim (Optional[int]): Dimension of context features for cross-attention. None for self-attention
499
+ num_heads (int): Number of attention heads
500
+ bias (bool): Whether to include bias in attention projections. Default: False
501
+ qkv_norm_mode (str): Normalization mode for query/key/value projections. Must be "per_head". Default: "per_head"
502
+ x_format (str): Format of input tensor. Must be "BTHWD". Default: "BTHWD"
503
+
504
+ Input shape:
505
+ - x: (T, H, W, B, D) video features
506
+ - context (optional): (M, B, D) context features for cross-attention
507
+ where:
508
+ T: temporal dimension
509
+ H: height
510
+ W: width
511
+ B: batch size
512
+ D: feature dimension
513
+ M: context sequence length
514
+ """
515
+
516
+ def __init__(
517
+ self,
518
+ x_dim: int,
519
+ context_dim: Optional[int],
520
+ num_heads: int,
521
+ bias: bool = False,
522
+ qkv_norm_mode: str = "per_head",
523
+ x_format: str = "BTHWD",
524
+ weight_args={},
525
+ operations=None,
526
+ ) -> None:
527
+ super().__init__()
528
+ self.x_format = x_format
529
+
530
+ self.attn = Attention(
531
+ x_dim,
532
+ context_dim,
533
+ num_heads,
534
+ x_dim // num_heads,
535
+ qkv_bias=bias,
536
+ qkv_norm="RRI",
537
+ out_bias=bias,
538
+ qkv_norm_mode=qkv_norm_mode,
539
+ qkv_format="sbhd",
540
+ weight_args=weight_args,
541
+ operations=operations,
542
+ )
543
+
544
+ def forward(
545
+ self,
546
+ x: torch.Tensor,
547
+ context: Optional[torch.Tensor] = None,
548
+ crossattn_mask: Optional[torch.Tensor] = None,
549
+ rope_emb_L_1_1_D: Optional[torch.Tensor] = None,
550
+ transformer_options: Optional[dict] = {},
551
+ ) -> torch.Tensor:
552
+ """
553
+ Forward pass for video attention.
554
+
555
+ Args:
556
+ x (Tensor): Input tensor of shape (B, T, H, W, D) or (T, H, W, B, D) representing batches of video data.
557
+ context (Tensor): Context tensor of shape (B, M, D) or (M, B, D),
558
+ where M is the sequence length of the context.
559
+ crossattn_mask (Optional[Tensor]): An optional mask for cross-attention mechanisms.
560
+ rope_emb_L_1_1_D (Optional[Tensor]):
561
+ Rotary positional embedding tensor of shape (L, 1, 1, D). L == THW for current video training.
562
+
563
+ Returns:
564
+ Tensor: The output tensor with applied attention, maintaining the input shape.
565
+ """
566
+
567
+ x_T_H_W_B_D = x
568
+ context_M_B_D = context
569
+ T, H, W, B, D = x_T_H_W_B_D.shape
570
+ x_THW_B_D = rearrange(x_T_H_W_B_D, "t h w b d -> (t h w) b d")
571
+ x_THW_B_D = self.attn(
572
+ x_THW_B_D,
573
+ context_M_B_D,
574
+ crossattn_mask,
575
+ rope_emb=rope_emb_L_1_1_D,
576
+ transformer_options=transformer_options,
577
+ )
578
+ x_T_H_W_B_D = rearrange(x_THW_B_D, "(t h w) b d -> t h w b d", h=H, w=W)
579
+ return x_T_H_W_B_D
580
+
581
+
582
+ def adaln_norm_state(norm_state, x, scale, shift):
583
+ normalized = norm_state(x)
584
+ return normalized * (1 + scale) + shift
585
+
586
+
587
+ class DITBuildingBlock(nn.Module):
588
+ """
589
+ A building block for the DiT (Diffusion Transformer) architecture that supports different types of
590
+ attention and MLP operations with adaptive layer normalization.
591
+
592
+ Parameters:
593
+ block_type (str): Type of block - one of:
594
+ - "cross_attn"/"ca": Cross-attention
595
+ - "full_attn"/"fa": Full self-attention
596
+ - "mlp"/"ff": MLP/feedforward block
597
+ x_dim (int): Dimension of input features
598
+ context_dim (Optional[int]): Dimension of context features for cross-attention
599
+ num_heads (int): Number of attention heads
600
+ mlp_ratio (float): MLP hidden dimension multiplier. Default: 4.0
601
+ bias (bool): Whether to use bias in layers. Default: False
602
+ mlp_dropout (float): Dropout rate for MLP. Default: 0.0
603
+ qkv_norm_mode (str): QKV normalization mode. Default: "per_head"
604
+ x_format (str): Input tensor format. Default: "BTHWD"
605
+ use_adaln_lora (bool): Whether to use AdaLN-LoRA. Default: False
606
+ adaln_lora_dim (int): Dimension for AdaLN-LoRA. Default: 256
607
+ """
608
+
609
+ def __init__(
610
+ self,
611
+ block_type: str,
612
+ x_dim: int,
613
+ context_dim: Optional[int],
614
+ num_heads: int,
615
+ mlp_ratio: float = 4.0,
616
+ bias: bool = False,
617
+ mlp_dropout: float = 0.0,
618
+ qkv_norm_mode: str = "per_head",
619
+ x_format: str = "BTHWD",
620
+ use_adaln_lora: bool = False,
621
+ adaln_lora_dim: int = 256,
622
+ weight_args={},
623
+ operations=None
624
+ ) -> None:
625
+ block_type = block_type.lower()
626
+
627
+ super().__init__()
628
+ self.x_format = x_format
629
+ if block_type in ["cross_attn", "ca"]:
630
+ self.block = VideoAttn(
631
+ x_dim,
632
+ context_dim,
633
+ num_heads,
634
+ bias=bias,
635
+ qkv_norm_mode=qkv_norm_mode,
636
+ x_format=self.x_format,
637
+ weight_args=weight_args,
638
+ operations=operations,
639
+ )
640
+ elif block_type in ["full_attn", "fa"]:
641
+ self.block = VideoAttn(
642
+ x_dim, None, num_heads, bias=bias, qkv_norm_mode=qkv_norm_mode, x_format=self.x_format, weight_args=weight_args, operations=operations
643
+ )
644
+ elif block_type in ["mlp", "ff"]:
645
+ self.block = GPT2FeedForward(x_dim, int(x_dim * mlp_ratio), dropout=mlp_dropout, bias=bias, weight_args=weight_args, operations=operations)
646
+ else:
647
+ raise ValueError(f"Unknown block type: {block_type}")
648
+
649
+ self.block_type = block_type
650
+ self.use_adaln_lora = use_adaln_lora
651
+
652
+ self.norm_state = nn.LayerNorm(x_dim, elementwise_affine=False, eps=1e-6)
653
+ self.n_adaln_chunks = 3
654
+ if use_adaln_lora:
655
+ self.adaLN_modulation = nn.Sequential(
656
+ nn.SiLU(),
657
+ operations.Linear(x_dim, adaln_lora_dim, bias=False, **weight_args),
658
+ operations.Linear(adaln_lora_dim, self.n_adaln_chunks * x_dim, bias=False, **weight_args),
659
+ )
660
+ else:
661
+ self.adaLN_modulation = nn.Sequential(nn.SiLU(), operations.Linear(x_dim, self.n_adaln_chunks * x_dim, bias=False, **weight_args))
662
+
663
+ def forward(
664
+ self,
665
+ x: torch.Tensor,
666
+ emb_B_D: torch.Tensor,
667
+ crossattn_emb: torch.Tensor,
668
+ crossattn_mask: Optional[torch.Tensor] = None,
669
+ rope_emb_L_1_1_D: Optional[torch.Tensor] = None,
670
+ adaln_lora_B_3D: Optional[torch.Tensor] = None,
671
+ transformer_options: Optional[dict] = {},
672
+ ) -> torch.Tensor:
673
+ """
674
+ Forward pass for dynamically configured blocks with adaptive normalization.
675
+
676
+ Args:
677
+ x (Tensor): Input tensor of shape (B, T, H, W, D) or (T, H, W, B, D).
678
+ emb_B_D (Tensor): Embedding tensor for adaptive layer normalization modulation.
679
+ crossattn_emb (Tensor): Tensor for cross-attention blocks.
680
+ crossattn_mask (Optional[Tensor]): Optional mask for cross-attention.
681
+ rope_emb_L_1_1_D (Optional[Tensor]):
682
+ Rotary positional embedding tensor of shape (L, 1, 1, D). L == THW for current video training.
683
+
684
+ Returns:
685
+ Tensor: The output tensor after processing through the configured block and adaptive normalization.
686
+ """
687
+ if self.use_adaln_lora:
688
+ shift_B_D, scale_B_D, gate_B_D = (self.adaLN_modulation(emb_B_D) + adaln_lora_B_3D).chunk(
689
+ self.n_adaln_chunks, dim=1
690
+ )
691
+ else:
692
+ shift_B_D, scale_B_D, gate_B_D = self.adaLN_modulation(emb_B_D).chunk(self.n_adaln_chunks, dim=1)
693
+
694
+ shift_1_1_1_B_D, scale_1_1_1_B_D, gate_1_1_1_B_D = (
695
+ shift_B_D.unsqueeze(0).unsqueeze(0).unsqueeze(0),
696
+ scale_B_D.unsqueeze(0).unsqueeze(0).unsqueeze(0),
697
+ gate_B_D.unsqueeze(0).unsqueeze(0).unsqueeze(0),
698
+ )
699
+
700
+ if self.block_type in ["mlp", "ff"]:
701
+ x = x + gate_1_1_1_B_D * self.block(
702
+ adaln_norm_state(self.norm_state, x, scale_1_1_1_B_D, shift_1_1_1_B_D),
703
+ )
704
+ elif self.block_type in ["full_attn", "fa"]:
705
+ x = x + gate_1_1_1_B_D * self.block(
706
+ adaln_norm_state(self.norm_state, x, scale_1_1_1_B_D, shift_1_1_1_B_D),
707
+ context=None,
708
+ rope_emb_L_1_1_D=rope_emb_L_1_1_D,
709
+ transformer_options=transformer_options,
710
+ )
711
+ elif self.block_type in ["cross_attn", "ca"]:
712
+ x = x + gate_1_1_1_B_D * self.block(
713
+ adaln_norm_state(self.norm_state, x, scale_1_1_1_B_D, shift_1_1_1_B_D),
714
+ context=crossattn_emb,
715
+ crossattn_mask=crossattn_mask,
716
+ rope_emb_L_1_1_D=rope_emb_L_1_1_D,
717
+ transformer_options=transformer_options,
718
+ )
719
+ else:
720
+ raise ValueError(f"Unknown block type: {self.block_type}")
721
+
722
+ return x
723
+
724
+
725
+ class GeneralDITTransformerBlock(nn.Module):
726
+ """
727
+ A wrapper module that manages a sequence of DITBuildingBlocks to form a complete transformer layer.
728
+ Each block in the sequence is specified by a block configuration string.
729
+
730
+ Parameters:
731
+ x_dim (int): Dimension of input features
732
+ context_dim (int): Dimension of context features for cross-attention blocks
733
+ num_heads (int): Number of attention heads
734
+ block_config (str): String specifying block sequence (e.g. "ca-fa-mlp" for cross-attention,
735
+ full-attention, then MLP)
736
+ mlp_ratio (float): MLP hidden dimension multiplier. Default: 4.0
737
+ x_format (str): Input tensor format. Default: "BTHWD"
738
+ use_adaln_lora (bool): Whether to use AdaLN-LoRA. Default: False
739
+ adaln_lora_dim (int): Dimension for AdaLN-LoRA. Default: 256
740
+
741
+ The block_config string uses "-" to separate block types:
742
+ - "ca"/"cross_attn": Cross-attention block
743
+ - "fa"/"full_attn": Full self-attention block
744
+ - "mlp"/"ff": MLP/feedforward block
745
+
746
+ Example:
747
+ block_config = "ca-fa-mlp" creates a sequence of:
748
+ 1. Cross-attention block
749
+ 2. Full self-attention block
750
+ 3. MLP block
751
+ """
752
+
753
+ def __init__(
754
+ self,
755
+ x_dim: int,
756
+ context_dim: int,
757
+ num_heads: int,
758
+ block_config: str,
759
+ mlp_ratio: float = 4.0,
760
+ x_format: str = "BTHWD",
761
+ use_adaln_lora: bool = False,
762
+ adaln_lora_dim: int = 256,
763
+ weight_args={},
764
+ operations=None
765
+ ):
766
+ super().__init__()
767
+ self.blocks = nn.ModuleList()
768
+ self.x_format = x_format
769
+ for block_type in block_config.split("-"):
770
+ self.blocks.append(
771
+ DITBuildingBlock(
772
+ block_type,
773
+ x_dim,
774
+ context_dim,
775
+ num_heads,
776
+ mlp_ratio,
777
+ x_format=self.x_format,
778
+ use_adaln_lora=use_adaln_lora,
779
+ adaln_lora_dim=adaln_lora_dim,
780
+ weight_args=weight_args,
781
+ operations=operations,
782
+ )
783
+ )
784
+
785
+ def forward(
786
+ self,
787
+ x: torch.Tensor,
788
+ emb_B_D: torch.Tensor,
789
+ crossattn_emb: torch.Tensor,
790
+ crossattn_mask: Optional[torch.Tensor] = None,
791
+ rope_emb_L_1_1_D: Optional[torch.Tensor] = None,
792
+ adaln_lora_B_3D: Optional[torch.Tensor] = None,
793
+ transformer_options: Optional[dict] = {},
794
+ ) -> torch.Tensor:
795
+ for block in self.blocks:
796
+ x = block(
797
+ x,
798
+ emb_B_D,
799
+ crossattn_emb,
800
+ crossattn_mask,
801
+ rope_emb_L_1_1_D=rope_emb_L_1_1_D,
802
+ adaln_lora_B_3D=adaln_lora_B_3D,
803
+ transformer_options=transformer_options,
804
+ )
805
+ return x
ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/layers3d.py ADDED
@@ -0,0 +1,1041 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
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
+ """The model definition for 3D layers
16
+
17
+ Adapted from: https://github.com/lucidrains/magvit2-pytorch/blob/
18
+ 9f49074179c912736e617d61b32be367eb5f993a/magvit2_pytorch/magvit2_pytorch.py#L889
19
+
20
+ [MIT License Copyright (c) 2023 Phil Wang]
21
+ https://github.com/lucidrains/magvit2-pytorch/blob/
22
+ 9f49074179c912736e617d61b32be367eb5f993a/LICENSE
23
+ """
24
+ import math
25
+ from typing import Tuple, Union
26
+
27
+ import numpy as np
28
+ import torch
29
+ import torch.nn as nn
30
+ import torch.nn.functional as F
31
+ import logging
32
+
33
+ from comfy.ldm.modules.diffusionmodules.model import vae_attention
34
+
35
+ from .patching import (
36
+ Patcher,
37
+ Patcher3D,
38
+ UnPatcher,
39
+ UnPatcher3D,
40
+ )
41
+ from .utils import (
42
+ CausalNormalize,
43
+ batch2space,
44
+ batch2time,
45
+ cast_tuple,
46
+ is_odd,
47
+ nonlinearity,
48
+ replication_pad,
49
+ space2batch,
50
+ time2batch,
51
+ )
52
+
53
+ import comfy.ops
54
+ ops = comfy.ops.disable_weight_init
55
+
56
+ _LEGACY_NUM_GROUPS = 32
57
+
58
+
59
+ class CausalConv3d(nn.Module):
60
+ def __init__(
61
+ self,
62
+ chan_in: int = 1,
63
+ chan_out: int = 1,
64
+ kernel_size: Union[int, Tuple[int, int, int]] = 3,
65
+ pad_mode: str = "constant",
66
+ **kwargs,
67
+ ):
68
+ super().__init__()
69
+ kernel_size = cast_tuple(kernel_size, 3)
70
+
71
+ time_kernel_size, height_kernel_size, width_kernel_size = kernel_size
72
+
73
+ assert is_odd(height_kernel_size) and is_odd(width_kernel_size)
74
+
75
+ dilation = kwargs.pop("dilation", 1)
76
+ stride = kwargs.pop("stride", 1)
77
+ time_stride = kwargs.pop("time_stride", 1)
78
+ time_dilation = kwargs.pop("time_dilation", 1)
79
+ padding = kwargs.pop("padding", 1)
80
+
81
+ self.pad_mode = pad_mode
82
+ time_pad = time_dilation * (time_kernel_size - 1) + (1 - time_stride)
83
+ self.time_pad = time_pad
84
+
85
+ self.spatial_pad = (padding, padding, padding, padding)
86
+
87
+ stride = (time_stride, stride, stride)
88
+ dilation = (time_dilation, dilation, dilation)
89
+ self.conv3d = ops.Conv3d(
90
+ chan_in,
91
+ chan_out,
92
+ kernel_size,
93
+ stride=stride,
94
+ dilation=dilation,
95
+ **kwargs,
96
+ )
97
+
98
+ def _replication_pad(self, x: torch.Tensor) -> torch.Tensor:
99
+ x_prev = x[:, :, :1, ...].repeat(1, 1, self.time_pad, 1, 1)
100
+ x = torch.cat([x_prev, x], dim=2)
101
+ padding = self.spatial_pad + (0, 0)
102
+ return F.pad(x, padding, mode=self.pad_mode, value=0.0)
103
+
104
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
105
+ x = self._replication_pad(x)
106
+ return self.conv3d(x)
107
+
108
+
109
+ class CausalUpsample3d(nn.Module):
110
+ def __init__(self, in_channels: int) -> None:
111
+ super().__init__()
112
+ self.conv = CausalConv3d(
113
+ in_channels, in_channels, kernel_size=3, stride=1, padding=1
114
+ )
115
+
116
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
117
+ x = x.repeat_interleave(2, dim=3).repeat_interleave(2, dim=4)
118
+ time_factor = 1.0 + 1.0 * (x.shape[2] > 1)
119
+ if isinstance(time_factor, torch.Tensor):
120
+ time_factor = time_factor.item()
121
+ x = x.repeat_interleave(int(time_factor), dim=2)
122
+ # TODO(freda): Check if this causes temporal inconsistency.
123
+ # Shoule reverse the order of the following two ops,
124
+ # better perf and better temporal smoothness.
125
+ x = self.conv(x)
126
+ return x[..., int(time_factor - 1) :, :, :]
127
+
128
+
129
+ class CausalDownsample3d(nn.Module):
130
+ def __init__(self, in_channels: int) -> None:
131
+ super().__init__()
132
+ self.conv = CausalConv3d(
133
+ in_channels,
134
+ in_channels,
135
+ kernel_size=3,
136
+ stride=2,
137
+ time_stride=2,
138
+ padding=0,
139
+ )
140
+
141
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
142
+ pad = (0, 1, 0, 1, 0, 0)
143
+ x = F.pad(x, pad, mode="constant", value=0)
144
+ x = replication_pad(x)
145
+ x = self.conv(x)
146
+ return x
147
+
148
+
149
+ class CausalHybridUpsample3d(nn.Module):
150
+ def __init__(
151
+ self,
152
+ in_channels: int,
153
+ spatial_up: bool = True,
154
+ temporal_up: bool = True,
155
+ **kwargs,
156
+ ) -> None:
157
+ super().__init__()
158
+ self.spatial_up = spatial_up
159
+ self.temporal_up = temporal_up
160
+ if not self.spatial_up and not self.temporal_up:
161
+ return
162
+
163
+ self.conv1 = CausalConv3d(
164
+ in_channels,
165
+ in_channels,
166
+ kernel_size=(3, 1, 1),
167
+ stride=1,
168
+ time_stride=1,
169
+ padding=0,
170
+ )
171
+ self.conv2 = CausalConv3d(
172
+ in_channels,
173
+ in_channels,
174
+ kernel_size=(1, 3, 3),
175
+ stride=1,
176
+ time_stride=1,
177
+ padding=1,
178
+ )
179
+ self.conv3 = CausalConv3d(
180
+ in_channels,
181
+ in_channels,
182
+ kernel_size=1,
183
+ stride=1,
184
+ time_stride=1,
185
+ padding=0,
186
+ )
187
+
188
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
189
+ if not self.spatial_up and not self.temporal_up:
190
+ return x
191
+
192
+ # hybrid upsample temporally.
193
+ if self.temporal_up:
194
+ time_factor = 1.0 + 1.0 * (x.shape[2] > 1)
195
+ if isinstance(time_factor, torch.Tensor):
196
+ time_factor = time_factor.item()
197
+ x = x.repeat_interleave(int(time_factor), dim=2)
198
+ x = x[..., int(time_factor - 1) :, :, :]
199
+ x = self.conv1(x) + x
200
+
201
+ # hybrid upsample spatially.
202
+ if self.spatial_up:
203
+ x = x.repeat_interleave(2, dim=3).repeat_interleave(2, dim=4)
204
+ x = self.conv2(x) + x
205
+
206
+ # final 1x1x1 conv.
207
+ x = self.conv3(x)
208
+ return x
209
+
210
+
211
+ class CausalHybridDownsample3d(nn.Module):
212
+ def __init__(
213
+ self,
214
+ in_channels: int,
215
+ spatial_down: bool = True,
216
+ temporal_down: bool = True,
217
+ **kwargs,
218
+ ) -> None:
219
+ super().__init__()
220
+ self.spatial_down = spatial_down
221
+ self.temporal_down = temporal_down
222
+ if not self.spatial_down and not self.temporal_down:
223
+ return
224
+
225
+ self.conv1 = CausalConv3d(
226
+ in_channels,
227
+ in_channels,
228
+ kernel_size=(1, 3, 3),
229
+ stride=2,
230
+ time_stride=1,
231
+ padding=0,
232
+ )
233
+ self.conv2 = CausalConv3d(
234
+ in_channels,
235
+ in_channels,
236
+ kernel_size=(3, 1, 1),
237
+ stride=1,
238
+ time_stride=2,
239
+ padding=0,
240
+ )
241
+ self.conv3 = CausalConv3d(
242
+ in_channels,
243
+ in_channels,
244
+ kernel_size=1,
245
+ stride=1,
246
+ time_stride=1,
247
+ padding=0,
248
+ )
249
+
250
+
251
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
252
+ if not self.spatial_down and not self.temporal_down:
253
+ return x
254
+
255
+ # hybrid downsample spatially.
256
+ if self.spatial_down:
257
+ pad = (0, 1, 0, 1, 0, 0)
258
+ x = F.pad(x, pad, mode="constant", value=0)
259
+ x1 = self.conv1(x)
260
+ x2 = F.avg_pool3d(x, kernel_size=(1, 2, 2), stride=(1, 2, 2))
261
+ x = x1 + x2
262
+
263
+ # hybrid downsample temporally.
264
+ if self.temporal_down:
265
+ x = replication_pad(x)
266
+ x1 = self.conv2(x)
267
+ x2 = F.avg_pool3d(x, kernel_size=(2, 1, 1), stride=(2, 1, 1))
268
+ x = x1 + x2
269
+
270
+ # final 1x1x1 conv.
271
+ x = self.conv3(x)
272
+ return x
273
+
274
+
275
+ class CausalResnetBlock3d(nn.Module):
276
+ def __init__(
277
+ self,
278
+ *,
279
+ in_channels: int,
280
+ out_channels: int = None,
281
+ dropout: float,
282
+ num_groups: int,
283
+ ) -> None:
284
+ super().__init__()
285
+ self.in_channels = in_channels
286
+ out_channels = in_channels if out_channels is None else out_channels
287
+
288
+ self.norm1 = CausalNormalize(in_channels, num_groups=num_groups)
289
+ self.conv1 = CausalConv3d(
290
+ in_channels, out_channels, kernel_size=3, stride=1, padding=1
291
+ )
292
+ self.norm2 = CausalNormalize(out_channels, num_groups=num_groups)
293
+ self.dropout = torch.nn.Dropout(dropout)
294
+ self.conv2 = CausalConv3d(
295
+ out_channels, out_channels, kernel_size=3, stride=1, padding=1
296
+ )
297
+ self.nin_shortcut = (
298
+ CausalConv3d(in_channels, out_channels, kernel_size=1, stride=1, padding=0)
299
+ if in_channels != out_channels
300
+ else nn.Identity()
301
+ )
302
+
303
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
304
+ h = x
305
+ h = self.norm1(h)
306
+ h = nonlinearity(h)
307
+ h = self.conv1(h)
308
+
309
+ h = self.norm2(h)
310
+ h = nonlinearity(h)
311
+ h = self.dropout(h)
312
+ h = self.conv2(h)
313
+ x = self.nin_shortcut(x)
314
+
315
+ return x + h
316
+
317
+
318
+ class CausalResnetBlockFactorized3d(nn.Module):
319
+ def __init__(
320
+ self,
321
+ *,
322
+ in_channels: int,
323
+ out_channels: int = None,
324
+ dropout: float,
325
+ num_groups: int,
326
+ ) -> None:
327
+ super().__init__()
328
+ self.in_channels = in_channels
329
+ out_channels = in_channels if out_channels is None else out_channels
330
+
331
+ self.norm1 = CausalNormalize(in_channels, num_groups=1)
332
+ self.conv1 = nn.Sequential(
333
+ CausalConv3d(
334
+ in_channels,
335
+ out_channels,
336
+ kernel_size=(1, 3, 3),
337
+ stride=1,
338
+ padding=1,
339
+ ),
340
+ CausalConv3d(
341
+ out_channels,
342
+ out_channels,
343
+ kernel_size=(3, 1, 1),
344
+ stride=1,
345
+ padding=0,
346
+ ),
347
+ )
348
+ self.norm2 = CausalNormalize(out_channels, num_groups=num_groups)
349
+ self.dropout = torch.nn.Dropout(dropout)
350
+ self.conv2 = nn.Sequential(
351
+ CausalConv3d(
352
+ out_channels,
353
+ out_channels,
354
+ kernel_size=(1, 3, 3),
355
+ stride=1,
356
+ padding=1,
357
+ ),
358
+ CausalConv3d(
359
+ out_channels,
360
+ out_channels,
361
+ kernel_size=(3, 1, 1),
362
+ stride=1,
363
+ padding=0,
364
+ ),
365
+ )
366
+ self.nin_shortcut = (
367
+ CausalConv3d(in_channels, out_channels, kernel_size=1, stride=1, padding=0)
368
+ if in_channels != out_channels
369
+ else nn.Identity()
370
+ )
371
+
372
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
373
+ h = x
374
+ h = self.norm1(h)
375
+ h = nonlinearity(h)
376
+ h = self.conv1(h)
377
+
378
+ h = self.norm2(h)
379
+ h = nonlinearity(h)
380
+ h = self.dropout(h)
381
+ h = self.conv2(h)
382
+ x = self.nin_shortcut(x)
383
+
384
+ return x + h
385
+
386
+
387
+ class CausalAttnBlock(nn.Module):
388
+ def __init__(self, in_channels: int, num_groups: int) -> None:
389
+ super().__init__()
390
+
391
+ self.norm = CausalNormalize(in_channels, num_groups=num_groups)
392
+ self.q = CausalConv3d(
393
+ in_channels, in_channels, kernel_size=1, stride=1, padding=0
394
+ )
395
+ self.k = CausalConv3d(
396
+ in_channels, in_channels, kernel_size=1, stride=1, padding=0
397
+ )
398
+ self.v = CausalConv3d(
399
+ in_channels, in_channels, kernel_size=1, stride=1, padding=0
400
+ )
401
+ self.proj_out = CausalConv3d(
402
+ in_channels, in_channels, kernel_size=1, stride=1, padding=0
403
+ )
404
+
405
+ self.optimized_attention = vae_attention()
406
+
407
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
408
+ h_ = x
409
+ h_ = self.norm(h_)
410
+ q = self.q(h_)
411
+ k = self.k(h_)
412
+ v = self.v(h_)
413
+
414
+ # compute attention
415
+ q, batch_size = time2batch(q)
416
+ k, batch_size = time2batch(k)
417
+ v, batch_size = time2batch(v)
418
+
419
+ b, c, h, w = q.shape
420
+ h_ = self.optimized_attention(q, k, v)
421
+
422
+ h_ = batch2time(h_, batch_size)
423
+ h_ = self.proj_out(h_)
424
+ return x + h_
425
+
426
+
427
+ class CausalTemporalAttnBlock(nn.Module):
428
+ def __init__(self, in_channels: int, num_groups: int) -> None:
429
+ super().__init__()
430
+
431
+ self.norm = CausalNormalize(in_channels, num_groups=num_groups)
432
+ self.q = CausalConv3d(
433
+ in_channels, in_channels, kernel_size=1, stride=1, padding=0
434
+ )
435
+ self.k = CausalConv3d(
436
+ in_channels, in_channels, kernel_size=1, stride=1, padding=0
437
+ )
438
+ self.v = CausalConv3d(
439
+ in_channels, in_channels, kernel_size=1, stride=1, padding=0
440
+ )
441
+ self.proj_out = CausalConv3d(
442
+ in_channels, in_channels, kernel_size=1, stride=1, padding=0
443
+ )
444
+
445
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
446
+ h_ = x
447
+ h_ = self.norm(h_)
448
+ q = self.q(h_)
449
+ k = self.k(h_)
450
+ v = self.v(h_)
451
+
452
+ # compute attention
453
+ q, batch_size, height = space2batch(q)
454
+ k, _, _ = space2batch(k)
455
+ v, _, _ = space2batch(v)
456
+
457
+ bhw, c, t = q.shape
458
+ q = q.permute(0, 2, 1) # (bhw, t, c)
459
+ k = k.permute(0, 2, 1) # (bhw, t, c)
460
+ v = v.permute(0, 2, 1) # (bhw, t, c)
461
+
462
+ w_ = torch.bmm(q, k.permute(0, 2, 1)) # (bhw, t, t)
463
+ w_ = w_ * (int(c) ** (-0.5))
464
+
465
+ # Apply causal mask
466
+ mask = torch.tril(torch.ones_like(w_))
467
+ w_ = w_.masked_fill(mask == 0, float("-inf"))
468
+ w_ = F.softmax(w_, dim=2)
469
+
470
+ # attend to values
471
+ h_ = torch.bmm(w_, v) # (bhw, t, c)
472
+ h_ = h_.permute(0, 2, 1).reshape(bhw, c, t) # (bhw, c, t)
473
+
474
+ h_ = batch2space(h_, batch_size, height)
475
+ h_ = self.proj_out(h_)
476
+ return x + h_
477
+
478
+
479
+ class EncoderBase(nn.Module):
480
+ def __init__(
481
+ self,
482
+ in_channels: int,
483
+ channels: int,
484
+ channels_mult: list[int],
485
+ num_res_blocks: int,
486
+ attn_resolutions: list[int],
487
+ dropout: float,
488
+ resolution: int,
489
+ z_channels: int,
490
+ **ignore_kwargs,
491
+ ) -> None:
492
+ super().__init__()
493
+ self.num_resolutions = len(channels_mult)
494
+ self.num_res_blocks = num_res_blocks
495
+
496
+ # Patcher.
497
+ patch_size = ignore_kwargs.get("patch_size", 1)
498
+ self.patcher = Patcher(
499
+ patch_size, ignore_kwargs.get("patch_method", "rearrange")
500
+ )
501
+ in_channels = in_channels * patch_size * patch_size
502
+
503
+ # downsampling
504
+ self.conv_in = CausalConv3d(
505
+ in_channels, channels, kernel_size=3, stride=1, padding=1
506
+ )
507
+
508
+ # num of groups for GroupNorm, num_groups=1 for LayerNorm.
509
+ num_groups = ignore_kwargs.get("num_groups", _LEGACY_NUM_GROUPS)
510
+ curr_res = resolution // patch_size
511
+ in_ch_mult = (1,) + tuple(channels_mult)
512
+ self.in_ch_mult = in_ch_mult
513
+ self.down = nn.ModuleList()
514
+ for i_level in range(self.num_resolutions):
515
+ block = nn.ModuleList()
516
+ attn = nn.ModuleList()
517
+ block_in = channels * in_ch_mult[i_level]
518
+ block_out = channels * channels_mult[i_level]
519
+ for _ in range(self.num_res_blocks):
520
+ block.append(
521
+ CausalResnetBlock3d(
522
+ in_channels=block_in,
523
+ out_channels=block_out,
524
+ dropout=dropout,
525
+ num_groups=num_groups,
526
+ )
527
+ )
528
+ block_in = block_out
529
+ if curr_res in attn_resolutions:
530
+ attn.append(CausalAttnBlock(block_in, num_groups=num_groups))
531
+ down = nn.Module()
532
+ down.block = block
533
+ down.attn = attn
534
+ if i_level != self.num_resolutions - 1:
535
+ down.downsample = CausalDownsample3d(block_in)
536
+ curr_res = curr_res // 2
537
+ self.down.append(down)
538
+
539
+ # middle
540
+ self.mid = nn.Module()
541
+ self.mid.block_1 = CausalResnetBlock3d(
542
+ in_channels=block_in,
543
+ out_channels=block_in,
544
+ dropout=dropout,
545
+ num_groups=num_groups,
546
+ )
547
+ self.mid.attn_1 = CausalAttnBlock(block_in, num_groups=num_groups)
548
+ self.mid.block_2 = CausalResnetBlock3d(
549
+ in_channels=block_in,
550
+ out_channels=block_in,
551
+ dropout=dropout,
552
+ num_groups=num_groups,
553
+ )
554
+
555
+ # end
556
+ self.norm_out = CausalNormalize(block_in, num_groups=num_groups)
557
+ self.conv_out = CausalConv3d(
558
+ block_in, z_channels, kernel_size=3, stride=1, padding=1
559
+ )
560
+
561
+ def patcher3d(self, x: torch.Tensor) -> torch.Tensor:
562
+ x, batch_size = time2batch(x)
563
+ x = self.patcher(x)
564
+ x = batch2time(x, batch_size)
565
+ return x
566
+
567
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
568
+ x = self.patcher3d(x)
569
+
570
+ # downsampling
571
+ hs = [self.conv_in(x)]
572
+ for i_level in range(self.num_resolutions):
573
+ for i_block in range(self.num_res_blocks):
574
+ h = self.down[i_level].block[i_block](hs[-1])
575
+ if len(self.down[i_level].attn) > 0:
576
+ h = self.down[i_level].attn[i_block](h)
577
+ hs.append(h)
578
+ if i_level != self.num_resolutions - 1:
579
+ hs.append(self.down[i_level].downsample(hs[-1]))
580
+ else:
581
+ # temporal downsample (last level)
582
+ time_factor = 1 + 1 * (hs[-1].shape[2] > 1)
583
+ if isinstance(time_factor, torch.Tensor):
584
+ time_factor = time_factor.item()
585
+ hs[-1] = replication_pad(hs[-1])
586
+ hs.append(
587
+ F.avg_pool3d(
588
+ hs[-1],
589
+ kernel_size=[time_factor, 1, 1],
590
+ stride=[2, 1, 1],
591
+ )
592
+ )
593
+
594
+ # middle
595
+ h = hs[-1]
596
+ h = self.mid.block_1(h)
597
+ h = self.mid.attn_1(h)
598
+ h = self.mid.block_2(h)
599
+
600
+ # end
601
+ h = self.norm_out(h)
602
+ h = nonlinearity(h)
603
+ h = self.conv_out(h)
604
+ return h
605
+
606
+
607
+ class DecoderBase(nn.Module):
608
+ def __init__(
609
+ self,
610
+ out_channels: int,
611
+ channels: int,
612
+ channels_mult: list[int],
613
+ num_res_blocks: int,
614
+ attn_resolutions: list[int],
615
+ dropout: float,
616
+ resolution: int,
617
+ z_channels: int,
618
+ **ignore_kwargs,
619
+ ):
620
+ super().__init__()
621
+ self.num_resolutions = len(channels_mult)
622
+ self.num_res_blocks = num_res_blocks
623
+
624
+ # UnPatcher.
625
+ patch_size = ignore_kwargs.get("patch_size", 1)
626
+ self.unpatcher = UnPatcher(
627
+ patch_size, ignore_kwargs.get("patch_method", "rearrange")
628
+ )
629
+ out_ch = out_channels * patch_size * patch_size
630
+
631
+ block_in = channels * channels_mult[self.num_resolutions - 1]
632
+ curr_res = (resolution // patch_size) // 2 ** (self.num_resolutions - 1)
633
+ self.z_shape = (1, z_channels, curr_res, curr_res)
634
+ logging.debug(
635
+ "Working with z of shape {} = {} dimensions.".format(
636
+ self.z_shape, np.prod(self.z_shape)
637
+ )
638
+ )
639
+
640
+ # z to block_in
641
+ self.conv_in = CausalConv3d(
642
+ z_channels, block_in, kernel_size=3, stride=1, padding=1
643
+ )
644
+
645
+ # num of groups for GroupNorm, num_groups=1 for LayerNorm.
646
+ num_groups = ignore_kwargs.get("num_groups", _LEGACY_NUM_GROUPS)
647
+
648
+ # middle
649
+ self.mid = nn.Module()
650
+ self.mid.block_1 = CausalResnetBlock3d(
651
+ in_channels=block_in,
652
+ out_channels=block_in,
653
+ dropout=dropout,
654
+ num_groups=num_groups,
655
+ )
656
+ self.mid.attn_1 = CausalAttnBlock(block_in, num_groups=num_groups)
657
+ self.mid.block_2 = CausalResnetBlock3d(
658
+ in_channels=block_in,
659
+ out_channels=block_in,
660
+ dropout=dropout,
661
+ num_groups=num_groups,
662
+ )
663
+
664
+ # upsampling
665
+ self.up = nn.ModuleList()
666
+ for i_level in reversed(range(self.num_resolutions)):
667
+ block = nn.ModuleList()
668
+ attn = nn.ModuleList()
669
+ block_out = channels * channels_mult[i_level]
670
+ for _ in range(self.num_res_blocks + 1):
671
+ block.append(
672
+ CausalResnetBlock3d(
673
+ in_channels=block_in,
674
+ out_channels=block_out,
675
+ dropout=dropout,
676
+ num_groups=num_groups,
677
+ )
678
+ )
679
+ block_in = block_out
680
+ if curr_res in attn_resolutions:
681
+ attn.append(CausalAttnBlock(block_in, num_groups=num_groups))
682
+ up = nn.Module()
683
+ up.block = block
684
+ up.attn = attn
685
+ if i_level != 0:
686
+ up.upsample = CausalUpsample3d(block_in)
687
+ curr_res = curr_res * 2
688
+ self.up.insert(0, up) # prepend to get consistent order
689
+
690
+ # end
691
+ self.norm_out = CausalNormalize(block_in, num_groups=num_groups)
692
+ self.conv_out = CausalConv3d(
693
+ block_in, out_ch, kernel_size=3, stride=1, padding=1
694
+ )
695
+
696
+ def unpatcher3d(self, x: torch.Tensor) -> torch.Tensor:
697
+ x, batch_size = time2batch(x)
698
+ x = self.unpatcher(x)
699
+ x = batch2time(x, batch_size)
700
+
701
+ return x
702
+
703
+ def forward(self, z):
704
+ h = self.conv_in(z)
705
+
706
+ # middle block.
707
+ h = self.mid.block_1(h)
708
+ h = self.mid.attn_1(h)
709
+ h = self.mid.block_2(h)
710
+
711
+ # decoder blocks.
712
+ for i_level in reversed(range(self.num_resolutions)):
713
+ for i_block in range(self.num_res_blocks + 1):
714
+ h = self.up[i_level].block[i_block](h)
715
+ if len(self.up[i_level].attn) > 0:
716
+ h = self.up[i_level].attn[i_block](h)
717
+ if i_level != 0:
718
+ h = self.up[i_level].upsample(h)
719
+ else:
720
+ # temporal upsample (last level)
721
+ time_factor = 1.0 + 1.0 * (h.shape[2] > 1)
722
+ if isinstance(time_factor, torch.Tensor):
723
+ time_factor = time_factor.item()
724
+ h = h.repeat_interleave(int(time_factor), dim=2)
725
+ h = h[..., int(time_factor - 1) :, :, :]
726
+
727
+ h = self.norm_out(h)
728
+ h = nonlinearity(h)
729
+ h = self.conv_out(h)
730
+ h = self.unpatcher3d(h)
731
+ return h
732
+
733
+
734
+ class EncoderFactorized(nn.Module):
735
+ def __init__(
736
+ self,
737
+ in_channels: int,
738
+ channels: int,
739
+ channels_mult: list[int],
740
+ num_res_blocks: int,
741
+ attn_resolutions: list[int],
742
+ dropout: float,
743
+ resolution: int,
744
+ z_channels: int,
745
+ spatial_compression: int = 8,
746
+ temporal_compression: int = 8,
747
+ **ignore_kwargs,
748
+ ) -> None:
749
+ super().__init__()
750
+ self.num_resolutions = len(channels_mult)
751
+ self.num_res_blocks = num_res_blocks
752
+
753
+ # Patcher.
754
+ patch_size = ignore_kwargs.get("patch_size", 1)
755
+ self.patcher3d = Patcher3D(
756
+ patch_size, ignore_kwargs.get("patch_method", "haar")
757
+ )
758
+ in_channels = in_channels * patch_size * patch_size * patch_size
759
+
760
+ # calculate the number of downsample operations
761
+ self.num_spatial_downs = int(math.log2(spatial_compression)) - int(
762
+ math.log2(patch_size)
763
+ )
764
+ assert (
765
+ self.num_spatial_downs <= self.num_resolutions
766
+ ), f"Spatially downsample {self.num_resolutions} times at most"
767
+
768
+ self.num_temporal_downs = int(math.log2(temporal_compression)) - int(
769
+ math.log2(patch_size)
770
+ )
771
+ assert (
772
+ self.num_temporal_downs <= self.num_resolutions
773
+ ), f"Temporally downsample {self.num_resolutions} times at most"
774
+
775
+ # downsampling
776
+ self.conv_in = nn.Sequential(
777
+ CausalConv3d(
778
+ in_channels,
779
+ channels,
780
+ kernel_size=(1, 3, 3),
781
+ stride=1,
782
+ padding=1,
783
+ ),
784
+ CausalConv3d(
785
+ channels, channels, kernel_size=(3, 1, 1), stride=1, padding=0
786
+ ),
787
+ )
788
+
789
+ curr_res = resolution // patch_size
790
+ in_ch_mult = (1,) + tuple(channels_mult)
791
+ self.in_ch_mult = in_ch_mult
792
+ self.down = nn.ModuleList()
793
+ for i_level in range(self.num_resolutions):
794
+ block = nn.ModuleList()
795
+ attn = nn.ModuleList()
796
+ block_in = channels * in_ch_mult[i_level]
797
+ block_out = channels * channels_mult[i_level]
798
+ for _ in range(self.num_res_blocks):
799
+ block.append(
800
+ CausalResnetBlockFactorized3d(
801
+ in_channels=block_in,
802
+ out_channels=block_out,
803
+ dropout=dropout,
804
+ num_groups=1,
805
+ )
806
+ )
807
+ block_in = block_out
808
+ if curr_res in attn_resolutions:
809
+ attn.append(
810
+ nn.Sequential(
811
+ CausalAttnBlock(block_in, num_groups=1),
812
+ CausalTemporalAttnBlock(block_in, num_groups=1),
813
+ )
814
+ )
815
+ down = nn.Module()
816
+ down.block = block
817
+ down.attn = attn
818
+ if i_level != self.num_resolutions - 1:
819
+ spatial_down = i_level < self.num_spatial_downs
820
+ temporal_down = i_level < self.num_temporal_downs
821
+ down.downsample = CausalHybridDownsample3d(
822
+ block_in,
823
+ spatial_down=spatial_down,
824
+ temporal_down=temporal_down,
825
+ )
826
+ curr_res = curr_res // 2
827
+ self.down.append(down)
828
+
829
+ # middle
830
+ self.mid = nn.Module()
831
+ self.mid.block_1 = CausalResnetBlockFactorized3d(
832
+ in_channels=block_in,
833
+ out_channels=block_in,
834
+ dropout=dropout,
835
+ num_groups=1,
836
+ )
837
+ self.mid.attn_1 = nn.Sequential(
838
+ CausalAttnBlock(block_in, num_groups=1),
839
+ CausalTemporalAttnBlock(block_in, num_groups=1),
840
+ )
841
+ self.mid.block_2 = CausalResnetBlockFactorized3d(
842
+ in_channels=block_in,
843
+ out_channels=block_in,
844
+ dropout=dropout,
845
+ num_groups=1,
846
+ )
847
+
848
+ # end
849
+ self.norm_out = CausalNormalize(block_in, num_groups=1)
850
+ self.conv_out = nn.Sequential(
851
+ CausalConv3d(
852
+ block_in, z_channels, kernel_size=(1, 3, 3), stride=1, padding=1
853
+ ),
854
+ CausalConv3d(
855
+ z_channels,
856
+ z_channels,
857
+ kernel_size=(3, 1, 1),
858
+ stride=1,
859
+ padding=0,
860
+ ),
861
+ )
862
+
863
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
864
+ x = self.patcher3d(x)
865
+
866
+ # downsampling
867
+ h = self.conv_in(x)
868
+ for i_level in range(self.num_resolutions):
869
+ for i_block in range(self.num_res_blocks):
870
+ h = self.down[i_level].block[i_block](h)
871
+ if len(self.down[i_level].attn) > 0:
872
+ h = self.down[i_level].attn[i_block](h)
873
+ if i_level != self.num_resolutions - 1:
874
+ h = self.down[i_level].downsample(h)
875
+
876
+ # middle
877
+ h = self.mid.block_1(h)
878
+ h = self.mid.attn_1(h)
879
+ h = self.mid.block_2(h)
880
+
881
+ # end
882
+ h = self.norm_out(h)
883
+ h = nonlinearity(h)
884
+ h = self.conv_out(h)
885
+ return h
886
+
887
+
888
+ class DecoderFactorized(nn.Module):
889
+ def __init__(
890
+ self,
891
+ out_channels: int,
892
+ channels: int,
893
+ channels_mult: list[int],
894
+ num_res_blocks: int,
895
+ attn_resolutions: list[int],
896
+ dropout: float,
897
+ resolution: int,
898
+ z_channels: int,
899
+ spatial_compression: int = 8,
900
+ temporal_compression: int = 8,
901
+ **ignore_kwargs,
902
+ ):
903
+ super().__init__()
904
+ self.num_resolutions = len(channels_mult)
905
+ self.num_res_blocks = num_res_blocks
906
+
907
+ # UnPatcher.
908
+ patch_size = ignore_kwargs.get("patch_size", 1)
909
+ self.unpatcher3d = UnPatcher3D(
910
+ patch_size, ignore_kwargs.get("patch_method", "haar")
911
+ )
912
+ out_ch = out_channels * patch_size * patch_size * patch_size
913
+
914
+ # calculate the number of upsample operations
915
+ self.num_spatial_ups = int(math.log2(spatial_compression)) - int(
916
+ math.log2(patch_size)
917
+ )
918
+ assert (
919
+ self.num_spatial_ups <= self.num_resolutions
920
+ ), f"Spatially upsample {self.num_resolutions} times at most"
921
+ self.num_temporal_ups = int(math.log2(temporal_compression)) - int(
922
+ math.log2(patch_size)
923
+ )
924
+ assert (
925
+ self.num_temporal_ups <= self.num_resolutions
926
+ ), f"Temporally upsample {self.num_resolutions} times at most"
927
+
928
+ block_in = channels * channels_mult[self.num_resolutions - 1]
929
+ curr_res = (resolution // patch_size) // 2 ** (self.num_resolutions - 1)
930
+ self.z_shape = (1, z_channels, curr_res, curr_res)
931
+ logging.debug(
932
+ "Working with z of shape {} = {} dimensions.".format(
933
+ self.z_shape, np.prod(self.z_shape)
934
+ )
935
+ )
936
+
937
+ # z to block_in
938
+ self.conv_in = nn.Sequential(
939
+ CausalConv3d(
940
+ z_channels, block_in, kernel_size=(1, 3, 3), stride=1, padding=1
941
+ ),
942
+ CausalConv3d(
943
+ block_in, block_in, kernel_size=(3, 1, 1), stride=1, padding=0
944
+ ),
945
+ )
946
+
947
+ # middle
948
+ self.mid = nn.Module()
949
+ self.mid.block_1 = CausalResnetBlockFactorized3d(
950
+ in_channels=block_in,
951
+ out_channels=block_in,
952
+ dropout=dropout,
953
+ num_groups=1,
954
+ )
955
+ self.mid.attn_1 = nn.Sequential(
956
+ CausalAttnBlock(block_in, num_groups=1),
957
+ CausalTemporalAttnBlock(block_in, num_groups=1),
958
+ )
959
+ self.mid.block_2 = CausalResnetBlockFactorized3d(
960
+ in_channels=block_in,
961
+ out_channels=block_in,
962
+ dropout=dropout,
963
+ num_groups=1,
964
+ )
965
+
966
+ legacy_mode = ignore_kwargs.get("legacy_mode", False)
967
+ # upsampling
968
+ self.up = nn.ModuleList()
969
+ for i_level in reversed(range(self.num_resolutions)):
970
+ block = nn.ModuleList()
971
+ attn = nn.ModuleList()
972
+ block_out = channels * channels_mult[i_level]
973
+ for _ in range(self.num_res_blocks + 1):
974
+ block.append(
975
+ CausalResnetBlockFactorized3d(
976
+ in_channels=block_in,
977
+ out_channels=block_out,
978
+ dropout=dropout,
979
+ num_groups=1,
980
+ )
981
+ )
982
+ block_in = block_out
983
+ if curr_res in attn_resolutions:
984
+ attn.append(
985
+ nn.Sequential(
986
+ CausalAttnBlock(block_in, num_groups=1),
987
+ CausalTemporalAttnBlock(block_in, num_groups=1),
988
+ )
989
+ )
990
+ up = nn.Module()
991
+ up.block = block
992
+ up.attn = attn
993
+ if i_level != 0:
994
+ # The layer index for temporal/spatial downsampling performed
995
+ # in the encoder should correspond to the layer index in
996
+ # reverse order where upsampling is performed in the decoder.
997
+ # If you've a pre-trained model, you can simply finetune.
998
+ i_level_reverse = self.num_resolutions - i_level - 1
999
+ if legacy_mode:
1000
+ temporal_up = i_level_reverse < self.num_temporal_ups
1001
+ else:
1002
+ temporal_up = 0 < i_level_reverse < self.num_temporal_ups + 1
1003
+ spatial_up = temporal_up or (
1004
+ i_level_reverse < self.num_spatial_ups
1005
+ and self.num_spatial_ups > self.num_temporal_ups
1006
+ )
1007
+ up.upsample = CausalHybridUpsample3d(
1008
+ block_in, spatial_up=spatial_up, temporal_up=temporal_up
1009
+ )
1010
+ curr_res = curr_res * 2
1011
+ self.up.insert(0, up) # prepend to get consistent order
1012
+
1013
+ # end
1014
+ self.norm_out = CausalNormalize(block_in, num_groups=1)
1015
+ self.conv_out = nn.Sequential(
1016
+ CausalConv3d(block_in, out_ch, kernel_size=(1, 3, 3), stride=1, padding=1),
1017
+ CausalConv3d(out_ch, out_ch, kernel_size=(3, 1, 1), stride=1, padding=0),
1018
+ )
1019
+
1020
+ def forward(self, z):
1021
+ h = self.conv_in(z)
1022
+
1023
+ # middle block.
1024
+ h = self.mid.block_1(h)
1025
+ h = self.mid.attn_1(h)
1026
+ h = self.mid.block_2(h)
1027
+
1028
+ # decoder blocks.
1029
+ for i_level in reversed(range(self.num_resolutions)):
1030
+ for i_block in range(self.num_res_blocks + 1):
1031
+ h = self.up[i_level].block[i_block](h)
1032
+ if len(self.up[i_level].attn) > 0:
1033
+ h = self.up[i_level].attn[i_block](h)
1034
+ if i_level != 0:
1035
+ h = self.up[i_level].upsample(h)
1036
+
1037
+ h = self.norm_out(h)
1038
+ h = nonlinearity(h)
1039
+ h = self.conv_out(h)
1040
+ h = self.unpatcher3d(h)
1041
+ return h
ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/patching.py ADDED
@@ -0,0 +1,377 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
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
+ """The patcher and unpatcher implementation for 2D and 3D data.
16
+
17
+ The idea of Haar wavelet is to compute LL, LH, HL, HH component as two 1D convolutions.
18
+ One on the rows and one on the columns.
19
+ For example, in 1D signal, we have [a, b], then the low-freq compoenent is [a + b] / 2 and high-freq is [a - b] / 2.
20
+ We can use a 1D convolution with kernel [1, 1] and stride 2 to represent the L component.
21
+ For H component, we can use a 1D convolution with kernel [1, -1] and stride 2.
22
+ Although in principle, we typically only do additional Haar wavelet over the LL component. But here we do it for all
23
+ as we need to support downsampling for more than 2x.
24
+ For example, 4x downsampling can be done by 2x Haar and additional 2x Haar, and the shape would be.
25
+ [3, 256, 256] -> [12, 128, 128] -> [48, 64, 64]
26
+ """
27
+
28
+ import torch
29
+ import torch.nn.functional as F
30
+ from einops import rearrange
31
+
32
+ _WAVELETS = {
33
+ "haar": torch.tensor([0.7071067811865476, 0.7071067811865476]),
34
+ "rearrange": torch.tensor([1.0, 1.0]),
35
+ }
36
+ _PERSISTENT = False
37
+
38
+
39
+ class Patcher(torch.nn.Module):
40
+ """A module to convert image tensors into patches using torch operations.
41
+
42
+ The main difference from `class Patching` is that this module implements
43
+ all operations using torch, rather than python or numpy, for efficiency purpose.
44
+
45
+ It's bit-wise identical to the Patching module outputs, with the added
46
+ benefit of being torch.jit scriptable.
47
+ """
48
+
49
+ def __init__(self, patch_size=1, patch_method="haar"):
50
+ super().__init__()
51
+ self.patch_size = patch_size
52
+ self.patch_method = patch_method
53
+ self.register_buffer(
54
+ "wavelets", _WAVELETS[patch_method], persistent=_PERSISTENT
55
+ )
56
+ self.range = range(int(torch.log2(torch.tensor(self.patch_size)).item()))
57
+ self.register_buffer(
58
+ "_arange",
59
+ torch.arange(_WAVELETS[patch_method].shape[0]),
60
+ persistent=_PERSISTENT,
61
+ )
62
+ for param in self.parameters():
63
+ param.requires_grad = False
64
+
65
+ def forward(self, x):
66
+ if self.patch_method == "haar":
67
+ return self._haar(x)
68
+ elif self.patch_method == "rearrange":
69
+ return self._arrange(x)
70
+ else:
71
+ raise ValueError("Unknown patch method: " + self.patch_method)
72
+
73
+ def _dwt(self, x, mode="reflect", rescale=False):
74
+ dtype = x.dtype
75
+ h = self.wavelets.to(device=x.device)
76
+
77
+ n = h.shape[0]
78
+ g = x.shape[1]
79
+ hl = h.flip(0).reshape(1, 1, -1).repeat(g, 1, 1)
80
+ hh = (h * ((-1) ** self._arange.to(device=x.device))).reshape(1, 1, -1).repeat(g, 1, 1)
81
+ hh = hh.to(dtype=dtype)
82
+ hl = hl.to(dtype=dtype)
83
+
84
+ x = F.pad(x, pad=(n - 2, n - 1, n - 2, n - 1), mode=mode).to(dtype)
85
+ xl = F.conv2d(x, hl.unsqueeze(2), groups=g, stride=(1, 2))
86
+ xh = F.conv2d(x, hh.unsqueeze(2), groups=g, stride=(1, 2))
87
+ xll = F.conv2d(xl, hl.unsqueeze(3), groups=g, stride=(2, 1))
88
+ xlh = F.conv2d(xl, hh.unsqueeze(3), groups=g, stride=(2, 1))
89
+ xhl = F.conv2d(xh, hl.unsqueeze(3), groups=g, stride=(2, 1))
90
+ xhh = F.conv2d(xh, hh.unsqueeze(3), groups=g, stride=(2, 1))
91
+
92
+ out = torch.cat([xll, xlh, xhl, xhh], dim=1)
93
+ if rescale:
94
+ out = out / 2
95
+ return out
96
+
97
+ def _haar(self, x):
98
+ for _ in self.range:
99
+ x = self._dwt(x, rescale=True)
100
+ return x
101
+
102
+ def _arrange(self, x):
103
+ x = rearrange(
104
+ x,
105
+ "b c (h p1) (w p2) -> b (c p1 p2) h w",
106
+ p1=self.patch_size,
107
+ p2=self.patch_size,
108
+ ).contiguous()
109
+ return x
110
+
111
+
112
+ class Patcher3D(Patcher):
113
+ """A 3D discrete wavelet transform for video data, expects 5D tensor, i.e. a batch of videos."""
114
+
115
+ def __init__(self, patch_size=1, patch_method="haar"):
116
+ super().__init__(patch_method=patch_method, patch_size=patch_size)
117
+ self.register_buffer(
118
+ "patch_size_buffer",
119
+ patch_size * torch.ones([1], dtype=torch.int32),
120
+ persistent=_PERSISTENT,
121
+ )
122
+
123
+ def _dwt(self, x, wavelet, mode="reflect", rescale=False):
124
+ dtype = x.dtype
125
+ h = self.wavelets.to(device=x.device)
126
+
127
+ n = h.shape[0]
128
+ g = x.shape[1]
129
+ hl = h.flip(0).reshape(1, 1, -1).repeat(g, 1, 1)
130
+ hh = (h * ((-1) ** self._arange.to(device=x.device))).reshape(1, 1, -1).repeat(g, 1, 1)
131
+ hh = hh.to(dtype=dtype)
132
+ hl = hl.to(dtype=dtype)
133
+
134
+ # Handles temporal axis.
135
+ x = F.pad(
136
+ x, pad=(max(0, n - 2), n - 1, n - 2, n - 1, n - 2, n - 1), mode=mode
137
+ ).to(dtype)
138
+ xl = F.conv3d(x, hl.unsqueeze(3).unsqueeze(4), groups=g, stride=(2, 1, 1))
139
+ xh = F.conv3d(x, hh.unsqueeze(3).unsqueeze(4), groups=g, stride=(2, 1, 1))
140
+
141
+ # Handles spatial axes.
142
+ xll = F.conv3d(xl, hl.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1))
143
+ xlh = F.conv3d(xl, hh.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1))
144
+ xhl = F.conv3d(xh, hl.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1))
145
+ xhh = F.conv3d(xh, hh.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1))
146
+
147
+ xlll = F.conv3d(xll, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2))
148
+ xllh = F.conv3d(xll, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2))
149
+ xlhl = F.conv3d(xlh, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2))
150
+ xlhh = F.conv3d(xlh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2))
151
+ xhll = F.conv3d(xhl, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2))
152
+ xhlh = F.conv3d(xhl, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2))
153
+ xhhl = F.conv3d(xhh, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2))
154
+ xhhh = F.conv3d(xhh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2))
155
+
156
+ out = torch.cat([xlll, xllh, xlhl, xlhh, xhll, xhlh, xhhl, xhhh], dim=1)
157
+ if rescale:
158
+ out = out / (2 * torch.sqrt(torch.tensor(2.0)))
159
+ return out
160
+
161
+ def _haar(self, x):
162
+ xi, xv = torch.split(x, [1, x.shape[2] - 1], dim=2)
163
+ x = torch.cat([xi.repeat_interleave(self.patch_size, dim=2), xv], dim=2)
164
+ for _ in self.range:
165
+ x = self._dwt(x, "haar", rescale=True)
166
+ return x
167
+
168
+ def _arrange(self, x):
169
+ xi, xv = torch.split(x, [1, x.shape[2] - 1], dim=2)
170
+ x = torch.cat([xi.repeat_interleave(self.patch_size, dim=2), xv], dim=2)
171
+ x = rearrange(
172
+ x,
173
+ "b c (t p1) (h p2) (w p3) -> b (c p1 p2 p3) t h w",
174
+ p1=self.patch_size,
175
+ p2=self.patch_size,
176
+ p3=self.patch_size,
177
+ ).contiguous()
178
+ return x
179
+
180
+
181
+ class UnPatcher(torch.nn.Module):
182
+ """A module to convert patches into image tensorsusing torch operations.
183
+
184
+ The main difference from `class Unpatching` is that this module implements
185
+ all operations using torch, rather than python or numpy, for efficiency purpose.
186
+
187
+ It's bit-wise identical to the Unpatching module outputs, with the added
188
+ benefit of being torch.jit scriptable.
189
+ """
190
+
191
+ def __init__(self, patch_size=1, patch_method="haar"):
192
+ super().__init__()
193
+ self.patch_size = patch_size
194
+ self.patch_method = patch_method
195
+ self.register_buffer(
196
+ "wavelets", _WAVELETS[patch_method], persistent=_PERSISTENT
197
+ )
198
+ self.range = range(int(torch.log2(torch.tensor(self.patch_size)).item()))
199
+ self.register_buffer(
200
+ "_arange",
201
+ torch.arange(_WAVELETS[patch_method].shape[0]),
202
+ persistent=_PERSISTENT,
203
+ )
204
+ for param in self.parameters():
205
+ param.requires_grad = False
206
+
207
+ def forward(self, x):
208
+ if self.patch_method == "haar":
209
+ return self._ihaar(x)
210
+ elif self.patch_method == "rearrange":
211
+ return self._iarrange(x)
212
+ else:
213
+ raise ValueError("Unknown patch method: " + self.patch_method)
214
+
215
+ def _idwt(self, x, wavelet="haar", mode="reflect", rescale=False):
216
+ dtype = x.dtype
217
+ h = self.wavelets.to(device=x.device)
218
+ n = h.shape[0]
219
+
220
+ g = x.shape[1] // 4
221
+ hl = h.flip([0]).reshape(1, 1, -1).repeat([g, 1, 1])
222
+ hh = (h * ((-1) ** self._arange.to(device=x.device))).reshape(1, 1, -1).repeat(g, 1, 1)
223
+ hh = hh.to(dtype=dtype)
224
+ hl = hl.to(dtype=dtype)
225
+
226
+ xll, xlh, xhl, xhh = torch.chunk(x.to(dtype), 4, dim=1)
227
+
228
+ # Inverse transform.
229
+ yl = torch.nn.functional.conv_transpose2d(
230
+ xll, hl.unsqueeze(3), groups=g, stride=(2, 1), padding=(n - 2, 0)
231
+ )
232
+ yl += torch.nn.functional.conv_transpose2d(
233
+ xlh, hh.unsqueeze(3), groups=g, stride=(2, 1), padding=(n - 2, 0)
234
+ )
235
+ yh = torch.nn.functional.conv_transpose2d(
236
+ xhl, hl.unsqueeze(3), groups=g, stride=(2, 1), padding=(n - 2, 0)
237
+ )
238
+ yh += torch.nn.functional.conv_transpose2d(
239
+ xhh, hh.unsqueeze(3), groups=g, stride=(2, 1), padding=(n - 2, 0)
240
+ )
241
+ y = torch.nn.functional.conv_transpose2d(
242
+ yl, hl.unsqueeze(2), groups=g, stride=(1, 2), padding=(0, n - 2)
243
+ )
244
+ y += torch.nn.functional.conv_transpose2d(
245
+ yh, hh.unsqueeze(2), groups=g, stride=(1, 2), padding=(0, n - 2)
246
+ )
247
+
248
+ if rescale:
249
+ y = y * 2
250
+ return y
251
+
252
+ def _ihaar(self, x):
253
+ for _ in self.range:
254
+ x = self._idwt(x, "haar", rescale=True)
255
+ return x
256
+
257
+ def _iarrange(self, x):
258
+ x = rearrange(
259
+ x,
260
+ "b (c p1 p2) h w -> b c (h p1) (w p2)",
261
+ p1=self.patch_size,
262
+ p2=self.patch_size,
263
+ )
264
+ return x
265
+
266
+
267
+ class UnPatcher3D(UnPatcher):
268
+ """A 3D inverse discrete wavelet transform for video wavelet decompositions."""
269
+
270
+ def __init__(self, patch_size=1, patch_method="haar"):
271
+ super().__init__(patch_method=patch_method, patch_size=patch_size)
272
+
273
+ def _idwt(self, x, wavelet="haar", mode="reflect", rescale=False):
274
+ dtype = x.dtype
275
+ h = self.wavelets.to(device=x.device)
276
+
277
+ g = x.shape[1] // 8 # split into 8 spatio-temporal filtered tesnors.
278
+ hl = h.flip([0]).reshape(1, 1, -1).repeat([g, 1, 1])
279
+ hh = (h * ((-1) ** self._arange.to(device=x.device))).reshape(1, 1, -1).repeat(g, 1, 1)
280
+ hl = hl.to(dtype=dtype)
281
+ hh = hh.to(dtype=dtype)
282
+
283
+ xlll, xllh, xlhl, xlhh, xhll, xhlh, xhhl, xhhh = torch.chunk(x, 8, dim=1)
284
+ del x
285
+
286
+ # Height height transposed convolutions.
287
+ xll = F.conv_transpose3d(
288
+ xlll, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
289
+ )
290
+ del xlll
291
+
292
+ xll += F.conv_transpose3d(
293
+ xllh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
294
+ )
295
+ del xllh
296
+
297
+ xlh = F.conv_transpose3d(
298
+ xlhl, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
299
+ )
300
+ del xlhl
301
+
302
+ xlh += F.conv_transpose3d(
303
+ xlhh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
304
+ )
305
+ del xlhh
306
+
307
+ xhl = F.conv_transpose3d(
308
+ xhll, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
309
+ )
310
+ del xhll
311
+
312
+ xhl += F.conv_transpose3d(
313
+ xhlh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
314
+ )
315
+ del xhlh
316
+
317
+ xhh = F.conv_transpose3d(
318
+ xhhl, hl.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
319
+ )
320
+ del xhhl
321
+
322
+ xhh += F.conv_transpose3d(
323
+ xhhh, hh.unsqueeze(2).unsqueeze(3), groups=g, stride=(1, 1, 2)
324
+ )
325
+ del xhhh
326
+
327
+ # Handles width transposed convolutions.
328
+ xl = F.conv_transpose3d(
329
+ xll, hl.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1)
330
+ )
331
+ del xll
332
+
333
+ xl += F.conv_transpose3d(
334
+ xlh, hh.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1)
335
+ )
336
+ del xlh
337
+
338
+ xh = F.conv_transpose3d(
339
+ xhl, hl.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1)
340
+ )
341
+ del xhl
342
+
343
+ xh += F.conv_transpose3d(
344
+ xhh, hh.unsqueeze(2).unsqueeze(4), groups=g, stride=(1, 2, 1)
345
+ )
346
+ del xhh
347
+
348
+ # Handles time axis transposed convolutions.
349
+ x = F.conv_transpose3d(
350
+ xl, hl.unsqueeze(3).unsqueeze(4), groups=g, stride=(2, 1, 1)
351
+ )
352
+ del xl
353
+
354
+ x += F.conv_transpose3d(
355
+ xh, hh.unsqueeze(3).unsqueeze(4), groups=g, stride=(2, 1, 1)
356
+ )
357
+
358
+ if rescale:
359
+ x = x * (2 * torch.sqrt(torch.tensor(2.0)))
360
+ return x
361
+
362
+ def _ihaar(self, x):
363
+ for _ in self.range:
364
+ x = self._idwt(x, "haar", rescale=True)
365
+ x = x[:, :, self.patch_size - 1 :, ...]
366
+ return x
367
+
368
+ def _iarrange(self, x):
369
+ x = rearrange(
370
+ x,
371
+ "b (c p1 p2 p3) t h w -> b c (t p1) (h p2) (w p3)",
372
+ p1=self.patch_size,
373
+ p2=self.patch_size,
374
+ p3=self.patch_size,
375
+ )
376
+ x = x[:, :, self.patch_size - 1 :, ...]
377
+ return x
ComfyUI/comfy/ldm/cosmos/cosmos_tokenizer/utils.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
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
+ """Shared utilities for the networks module."""
16
+
17
+ from typing import Any
18
+
19
+ import torch
20
+ from einops import rearrange
21
+
22
+
23
+ import comfy.ops
24
+ ops = comfy.ops.disable_weight_init
25
+
26
+ def time2batch(x: torch.Tensor) -> tuple[torch.Tensor, int]:
27
+ batch_size = x.shape[0]
28
+ return rearrange(x, "b c t h w -> (b t) c h w"), batch_size
29
+
30
+
31
+ def batch2time(x: torch.Tensor, batch_size: int) -> torch.Tensor:
32
+ return rearrange(x, "(b t) c h w -> b c t h w", b=batch_size)
33
+
34
+
35
+ def space2batch(x: torch.Tensor) -> tuple[torch.Tensor, int]:
36
+ batch_size, height = x.shape[0], x.shape[-2]
37
+ return rearrange(x, "b c t h w -> (b h w) c t"), batch_size, height
38
+
39
+
40
+ def batch2space(x: torch.Tensor, batch_size: int, height: int) -> torch.Tensor:
41
+ return rearrange(x, "(b h w) c t -> b c t h w", b=batch_size, h=height)
42
+
43
+
44
+ def cast_tuple(t: Any, length: int = 1) -> Any:
45
+ return t if isinstance(t, tuple) else ((t,) * length)
46
+
47
+
48
+ def replication_pad(x):
49
+ return torch.cat([x[:, :, :1, ...], x], dim=2)
50
+
51
+
52
+ def divisible_by(num: int, den: int) -> bool:
53
+ return (num % den) == 0
54
+
55
+
56
+ def is_odd(n: int) -> bool:
57
+ return not divisible_by(n, 2)
58
+
59
+
60
+ def nonlinearity(x):
61
+ # x * sigmoid(x)
62
+ return torch.nn.functional.silu(x)
63
+
64
+
65
+ def Normalize(in_channels, num_groups=32):
66
+ return ops.GroupNorm(
67
+ num_groups=num_groups, num_channels=in_channels, eps=1e-6, affine=True
68
+ )
69
+
70
+
71
+ class CausalNormalize(torch.nn.Module):
72
+ def __init__(self, in_channels, num_groups=1):
73
+ super().__init__()
74
+ self.norm = ops.GroupNorm(
75
+ num_groups=num_groups,
76
+ num_channels=in_channels,
77
+ eps=1e-6,
78
+ affine=True,
79
+ )
80
+ self.num_groups = num_groups
81
+
82
+ def forward(self, x):
83
+ # if num_groups !=1, we apply a spatio-temporal groupnorm for backward compatibility purpose.
84
+ # All new models should use num_groups=1, otherwise causality is not guaranteed.
85
+ if self.num_groups == 1:
86
+ x, batch_size = time2batch(x)
87
+ return batch2time(self.norm(x), batch_size)
88
+ return self.norm(x)
89
+
90
+
91
+ def exists(v):
92
+ return v is not None
93
+
94
+
95
+ def default(*args):
96
+ for arg in args:
97
+ if exists(arg):
98
+ return arg
99
+ return None
100
+
101
+
102
+ def round_ste(z: torch.Tensor) -> torch.Tensor:
103
+ """Round with straight through gradients."""
104
+ zhat = z.round()
105
+ return z + (zhat - z).detach()
106
+
107
+
108
+ def log(t, eps=1e-5):
109
+ return t.clamp(min=eps).log()
110
+
111
+
112
+ def entropy(prob):
113
+ return (-prob * log(prob)).sum(dim=-1)
ComfyUI/comfy/ldm/cosmos/model.py ADDED
@@ -0,0 +1,552 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
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
+ A general implementation of adaln-modulated VIT-like~(DiT) transformer for video processing.
18
+ """
19
+
20
+ from typing import Optional, Tuple
21
+
22
+ import torch
23
+ from einops import rearrange
24
+ from torch import nn
25
+ from torchvision import transforms
26
+
27
+ from enum import Enum
28
+ import logging
29
+
30
+ import comfy.patcher_extension
31
+
32
+ from .blocks import (
33
+ FinalLayer,
34
+ GeneralDITTransformerBlock,
35
+ PatchEmbed,
36
+ TimestepEmbedding,
37
+ Timesteps,
38
+ )
39
+
40
+ from .position_embedding import LearnablePosEmbAxis, VideoRopePosition3DEmb
41
+
42
+
43
+ class DataType(Enum):
44
+ IMAGE = "image"
45
+ VIDEO = "video"
46
+
47
+
48
+ class GeneralDIT(nn.Module):
49
+ """
50
+ A general implementation of adaln-modulated VIT-like~(DiT) transformer for video processing.
51
+
52
+ Args:
53
+ max_img_h (int): Maximum height of the input images.
54
+ max_img_w (int): Maximum width of the input images.
55
+ max_frames (int): Maximum number of frames in the video sequence.
56
+ in_channels (int): Number of input channels (e.g., RGB channels for color images).
57
+ out_channels (int): Number of output channels.
58
+ patch_spatial (tuple): Spatial resolution of patches for input processing.
59
+ patch_temporal (int): Temporal resolution of patches for input processing.
60
+ concat_padding_mask (bool): If True, includes a mask channel in the input to handle padding.
61
+ block_config (str): Configuration of the transformer block. See Notes for supported block types.
62
+ model_channels (int): Base number of channels used throughout the model.
63
+ num_blocks (int): Number of transformer blocks.
64
+ num_heads (int): Number of heads in the multi-head attention layers.
65
+ mlp_ratio (float): Expansion ratio for MLP blocks.
66
+ block_x_format (str): Format of input tensor for transformer blocks ('BTHWD' or 'THWBD').
67
+ crossattn_emb_channels (int): Number of embedding channels for cross-attention.
68
+ use_cross_attn_mask (bool): Whether to use mask in cross-attention.
69
+ pos_emb_cls (str): Type of positional embeddings.
70
+ pos_emb_learnable (bool): Whether positional embeddings are learnable.
71
+ pos_emb_interpolation (str): Method for interpolating positional embeddings.
72
+ affline_emb_norm (bool): Whether to normalize affine embeddings.
73
+ use_adaln_lora (bool): Whether to use AdaLN-LoRA.
74
+ adaln_lora_dim (int): Dimension for AdaLN-LoRA.
75
+ rope_h_extrapolation_ratio (float): Height extrapolation ratio for RoPE.
76
+ rope_w_extrapolation_ratio (float): Width extrapolation ratio for RoPE.
77
+ rope_t_extrapolation_ratio (float): Temporal extrapolation ratio for RoPE.
78
+ extra_per_block_abs_pos_emb (bool): Whether to use extra per-block absolute positional embeddings.
79
+ extra_per_block_abs_pos_emb_type (str): Type of extra per-block positional embeddings.
80
+ extra_h_extrapolation_ratio (float): Height extrapolation ratio for extra embeddings.
81
+ extra_w_extrapolation_ratio (float): Width extrapolation ratio for extra embeddings.
82
+ extra_t_extrapolation_ratio (float): Temporal extrapolation ratio for extra embeddings.
83
+
84
+ Notes:
85
+ Supported block types in block_config:
86
+ * cross_attn, ca: Cross attention
87
+ * full_attn: Full attention on all flattened tokens
88
+ * mlp, ff: Feed forward block
89
+ """
90
+
91
+ def __init__(
92
+ self,
93
+ max_img_h: int,
94
+ max_img_w: int,
95
+ max_frames: int,
96
+ in_channels: int,
97
+ out_channels: int,
98
+ patch_spatial: tuple,
99
+ patch_temporal: int,
100
+ concat_padding_mask: bool = True,
101
+ # attention settings
102
+ block_config: str = "FA-CA-MLP",
103
+ model_channels: int = 768,
104
+ num_blocks: int = 10,
105
+ num_heads: int = 16,
106
+ mlp_ratio: float = 4.0,
107
+ block_x_format: str = "BTHWD",
108
+ # cross attention settings
109
+ crossattn_emb_channels: int = 1024,
110
+ use_cross_attn_mask: bool = False,
111
+ # positional embedding settings
112
+ pos_emb_cls: str = "sincos",
113
+ pos_emb_learnable: bool = False,
114
+ pos_emb_interpolation: str = "crop",
115
+ affline_emb_norm: bool = False, # whether or not to normalize the affine embedding
116
+ use_adaln_lora: bool = False,
117
+ adaln_lora_dim: int = 256,
118
+ rope_h_extrapolation_ratio: float = 1.0,
119
+ rope_w_extrapolation_ratio: float = 1.0,
120
+ rope_t_extrapolation_ratio: float = 1.0,
121
+ extra_per_block_abs_pos_emb: bool = False,
122
+ extra_per_block_abs_pos_emb_type: str = "sincos",
123
+ extra_h_extrapolation_ratio: float = 1.0,
124
+ extra_w_extrapolation_ratio: float = 1.0,
125
+ extra_t_extrapolation_ratio: float = 1.0,
126
+ image_model=None,
127
+ device=None,
128
+ dtype=None,
129
+ operations=None,
130
+ ) -> None:
131
+ super().__init__()
132
+ self.max_img_h = max_img_h
133
+ self.max_img_w = max_img_w
134
+ self.max_frames = max_frames
135
+ self.in_channels = in_channels
136
+ self.out_channels = out_channels
137
+ self.patch_spatial = patch_spatial
138
+ self.patch_temporal = patch_temporal
139
+ self.num_heads = num_heads
140
+ self.num_blocks = num_blocks
141
+ self.model_channels = model_channels
142
+ self.use_cross_attn_mask = use_cross_attn_mask
143
+ self.concat_padding_mask = concat_padding_mask
144
+ # positional embedding settings
145
+ self.pos_emb_cls = pos_emb_cls
146
+ self.pos_emb_learnable = pos_emb_learnable
147
+ self.pos_emb_interpolation = pos_emb_interpolation
148
+ self.affline_emb_norm = affline_emb_norm
149
+ self.rope_h_extrapolation_ratio = rope_h_extrapolation_ratio
150
+ self.rope_w_extrapolation_ratio = rope_w_extrapolation_ratio
151
+ self.rope_t_extrapolation_ratio = rope_t_extrapolation_ratio
152
+ self.extra_per_block_abs_pos_emb = extra_per_block_abs_pos_emb
153
+ self.extra_per_block_abs_pos_emb_type = extra_per_block_abs_pos_emb_type.lower()
154
+ self.extra_h_extrapolation_ratio = extra_h_extrapolation_ratio
155
+ self.extra_w_extrapolation_ratio = extra_w_extrapolation_ratio
156
+ self.extra_t_extrapolation_ratio = extra_t_extrapolation_ratio
157
+ self.dtype = dtype
158
+ weight_args = {"device": device, "dtype": dtype}
159
+
160
+ in_channels = in_channels + 1 if concat_padding_mask else in_channels
161
+ self.x_embedder = PatchEmbed(
162
+ spatial_patch_size=patch_spatial,
163
+ temporal_patch_size=patch_temporal,
164
+ in_channels=in_channels,
165
+ out_channels=model_channels,
166
+ bias=False,
167
+ weight_args=weight_args,
168
+ operations=operations,
169
+ )
170
+
171
+ self.build_pos_embed(device=device, dtype=dtype)
172
+ self.block_x_format = block_x_format
173
+ self.use_adaln_lora = use_adaln_lora
174
+ self.adaln_lora_dim = adaln_lora_dim
175
+ self.t_embedder = nn.ModuleList(
176
+ [Timesteps(model_channels),
177
+ TimestepEmbedding(model_channels, model_channels, use_adaln_lora=use_adaln_lora, weight_args=weight_args, operations=operations),]
178
+ )
179
+
180
+ self.blocks = nn.ModuleDict()
181
+
182
+ for idx in range(num_blocks):
183
+ self.blocks[f"block{idx}"] = GeneralDITTransformerBlock(
184
+ x_dim=model_channels,
185
+ context_dim=crossattn_emb_channels,
186
+ num_heads=num_heads,
187
+ block_config=block_config,
188
+ mlp_ratio=mlp_ratio,
189
+ x_format=self.block_x_format,
190
+ use_adaln_lora=use_adaln_lora,
191
+ adaln_lora_dim=adaln_lora_dim,
192
+ weight_args=weight_args,
193
+ operations=operations,
194
+ )
195
+
196
+ if self.affline_emb_norm:
197
+ logging.debug("Building affine embedding normalization layer")
198
+ self.affline_norm = operations.RMSNorm(model_channels, elementwise_affine=True, eps=1e-6, device=device, dtype=dtype)
199
+ else:
200
+ self.affline_norm = nn.Identity()
201
+
202
+ self.final_layer = FinalLayer(
203
+ hidden_size=self.model_channels,
204
+ spatial_patch_size=self.patch_spatial,
205
+ temporal_patch_size=self.patch_temporal,
206
+ out_channels=self.out_channels,
207
+ use_adaln_lora=self.use_adaln_lora,
208
+ adaln_lora_dim=self.adaln_lora_dim,
209
+ weight_args=weight_args,
210
+ operations=operations,
211
+ )
212
+
213
+ def build_pos_embed(self, device=None, dtype=None):
214
+ if self.pos_emb_cls == "rope3d":
215
+ cls_type = VideoRopePosition3DEmb
216
+ else:
217
+ raise ValueError(f"Unknown pos_emb_cls {self.pos_emb_cls}")
218
+
219
+ logging.debug(f"Building positional embedding with {self.pos_emb_cls} class, impl {cls_type}")
220
+ kwargs = dict(
221
+ model_channels=self.model_channels,
222
+ len_h=self.max_img_h // self.patch_spatial,
223
+ len_w=self.max_img_w // self.patch_spatial,
224
+ len_t=self.max_frames // self.patch_temporal,
225
+ is_learnable=self.pos_emb_learnable,
226
+ interpolation=self.pos_emb_interpolation,
227
+ head_dim=self.model_channels // self.num_heads,
228
+ h_extrapolation_ratio=self.rope_h_extrapolation_ratio,
229
+ w_extrapolation_ratio=self.rope_w_extrapolation_ratio,
230
+ t_extrapolation_ratio=self.rope_t_extrapolation_ratio,
231
+ device=device,
232
+ )
233
+ self.pos_embedder = cls_type(
234
+ **kwargs,
235
+ )
236
+
237
+ if self.extra_per_block_abs_pos_emb:
238
+ assert self.extra_per_block_abs_pos_emb_type in [
239
+ "learnable",
240
+ ], f"Unknown extra_per_block_abs_pos_emb_type {self.extra_per_block_abs_pos_emb_type}"
241
+ kwargs["h_extrapolation_ratio"] = self.extra_h_extrapolation_ratio
242
+ kwargs["w_extrapolation_ratio"] = self.extra_w_extrapolation_ratio
243
+ kwargs["t_extrapolation_ratio"] = self.extra_t_extrapolation_ratio
244
+ kwargs["device"] = device
245
+ kwargs["dtype"] = dtype
246
+ self.extra_pos_embedder = LearnablePosEmbAxis(
247
+ **kwargs,
248
+ )
249
+
250
+ def prepare_embedded_sequence(
251
+ self,
252
+ x_B_C_T_H_W: torch.Tensor,
253
+ fps: Optional[torch.Tensor] = None,
254
+ padding_mask: Optional[torch.Tensor] = None,
255
+ latent_condition: Optional[torch.Tensor] = None,
256
+ latent_condition_sigma: Optional[torch.Tensor] = None,
257
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor]]:
258
+ """
259
+ Prepares an embedded sequence tensor by applying positional embeddings and handling padding masks.
260
+
261
+ Args:
262
+ x_B_C_T_H_W (torch.Tensor): video
263
+ fps (Optional[torch.Tensor]): Frames per second tensor to be used for positional embedding when required.
264
+ If None, a default value (`self.base_fps`) will be used.
265
+ padding_mask (Optional[torch.Tensor]): current it is not used
266
+
267
+ Returns:
268
+ Tuple[torch.Tensor, Optional[torch.Tensor]]:
269
+ - A tensor of shape (B, T, H, W, D) with the embedded sequence.
270
+ - An optional positional embedding tensor, returned only if the positional embedding class
271
+ (`self.pos_emb_cls`) includes 'rope'. Otherwise, None.
272
+
273
+ Notes:
274
+ - If `self.concat_padding_mask` is True, a padding mask channel is concatenated to the input tensor.
275
+ - The method of applying positional embeddings depends on the value of `self.pos_emb_cls`.
276
+ - If 'rope' is in `self.pos_emb_cls` (case insensitive), the positional embeddings are generated using
277
+ the `self.pos_embedder` with the shape [T, H, W].
278
+ - If "fps_aware" is in `self.pos_emb_cls`, the positional embeddings are generated using the
279
+ `self.pos_embedder` with the fps tensor.
280
+ - Otherwise, the positional embeddings are generated without considering fps.
281
+ """
282
+ if self.concat_padding_mask:
283
+ if padding_mask is not None:
284
+ padding_mask = transforms.functional.resize(
285
+ padding_mask, list(x_B_C_T_H_W.shape[-2:]), interpolation=transforms.InterpolationMode.NEAREST
286
+ )
287
+ else:
288
+ padding_mask = torch.zeros((x_B_C_T_H_W.shape[0], 1, x_B_C_T_H_W.shape[-2], x_B_C_T_H_W.shape[-1]), dtype=x_B_C_T_H_W.dtype, device=x_B_C_T_H_W.device)
289
+
290
+ x_B_C_T_H_W = torch.cat(
291
+ [x_B_C_T_H_W, padding_mask.unsqueeze(1).repeat(1, 1, x_B_C_T_H_W.shape[2], 1, 1)], dim=1
292
+ )
293
+ x_B_T_H_W_D = self.x_embedder(x_B_C_T_H_W)
294
+
295
+ if self.extra_per_block_abs_pos_emb:
296
+ extra_pos_emb = self.extra_pos_embedder(x_B_T_H_W_D, fps=fps, device=x_B_C_T_H_W.device, dtype=x_B_C_T_H_W.dtype)
297
+ else:
298
+ extra_pos_emb = None
299
+
300
+ if "rope" in self.pos_emb_cls.lower():
301
+ return x_B_T_H_W_D, self.pos_embedder(x_B_T_H_W_D, fps=fps, device=x_B_C_T_H_W.device), extra_pos_emb
302
+
303
+ if "fps_aware" in self.pos_emb_cls:
304
+ x_B_T_H_W_D = x_B_T_H_W_D + self.pos_embedder(x_B_T_H_W_D, fps=fps, device=x_B_C_T_H_W.device) # [B, T, H, W, D]
305
+ else:
306
+ x_B_T_H_W_D = x_B_T_H_W_D + self.pos_embedder(x_B_T_H_W_D, device=x_B_C_T_H_W.device) # [B, T, H, W, D]
307
+
308
+ return x_B_T_H_W_D, None, extra_pos_emb
309
+
310
+ def decoder_head(
311
+ self,
312
+ x_B_T_H_W_D: torch.Tensor,
313
+ emb_B_D: torch.Tensor,
314
+ crossattn_emb: torch.Tensor,
315
+ origin_shape: Tuple[int, int, int, int, int], # [B, C, T, H, W]
316
+ crossattn_mask: Optional[torch.Tensor] = None,
317
+ adaln_lora_B_3D: Optional[torch.Tensor] = None,
318
+ ) -> torch.Tensor:
319
+ del crossattn_emb, crossattn_mask
320
+ B, C, T_before_patchify, H_before_patchify, W_before_patchify = origin_shape
321
+ x_BT_HW_D = rearrange(x_B_T_H_W_D, "B T H W D -> (B T) (H W) D")
322
+ x_BT_HW_D = self.final_layer(x_BT_HW_D, emb_B_D, adaln_lora_B_3D=adaln_lora_B_3D)
323
+ # This is to ensure x_BT_HW_D has the correct shape because
324
+ # when we merge T, H, W into one dimension, x_BT_HW_D has shape (B * T * H * W, 1*1, D).
325
+ x_BT_HW_D = x_BT_HW_D.view(
326
+ B * T_before_patchify // self.patch_temporal,
327
+ H_before_patchify // self.patch_spatial * W_before_patchify // self.patch_spatial,
328
+ -1,
329
+ )
330
+ x_B_D_T_H_W = rearrange(
331
+ x_BT_HW_D,
332
+ "(B T) (H W) (p1 p2 t C) -> B C (T t) (H p1) (W p2)",
333
+ p1=self.patch_spatial,
334
+ p2=self.patch_spatial,
335
+ H=H_before_patchify // self.patch_spatial,
336
+ W=W_before_patchify // self.patch_spatial,
337
+ t=self.patch_temporal,
338
+ B=B,
339
+ )
340
+ return x_B_D_T_H_W
341
+
342
+ def forward_before_blocks(
343
+ self,
344
+ x: torch.Tensor,
345
+ timesteps: torch.Tensor,
346
+ crossattn_emb: torch.Tensor,
347
+ crossattn_mask: Optional[torch.Tensor] = None,
348
+ fps: Optional[torch.Tensor] = None,
349
+ image_size: Optional[torch.Tensor] = None,
350
+ padding_mask: Optional[torch.Tensor] = None,
351
+ scalar_feature: Optional[torch.Tensor] = None,
352
+ data_type: Optional[DataType] = DataType.VIDEO,
353
+ latent_condition: Optional[torch.Tensor] = None,
354
+ latent_condition_sigma: Optional[torch.Tensor] = None,
355
+ **kwargs,
356
+ ) -> torch.Tensor:
357
+ """
358
+ Args:
359
+ x: (B, C, T, H, W) tensor of spatial-temp inputs
360
+ timesteps: (B, ) tensor of timesteps
361
+ crossattn_emb: (B, N, D) tensor of cross-attention embeddings
362
+ crossattn_mask: (B, N) tensor of cross-attention masks
363
+ """
364
+ del kwargs
365
+ assert isinstance(
366
+ data_type, DataType
367
+ ), f"Expected DataType, got {type(data_type)}. We need discuss this flag later."
368
+ original_shape = x.shape
369
+ x_B_T_H_W_D, rope_emb_L_1_1_D, extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D = self.prepare_embedded_sequence(
370
+ x,
371
+ fps=fps,
372
+ padding_mask=padding_mask,
373
+ latent_condition=latent_condition,
374
+ latent_condition_sigma=latent_condition_sigma,
375
+ )
376
+ # logging affline scale information
377
+ affline_scale_log_info = {}
378
+
379
+ timesteps_B_D, adaln_lora_B_3D = self.t_embedder[1](self.t_embedder[0](timesteps.flatten()).to(x.dtype))
380
+ affline_emb_B_D = timesteps_B_D
381
+ affline_scale_log_info["timesteps_B_D"] = timesteps_B_D.detach()
382
+
383
+ if scalar_feature is not None:
384
+ raise NotImplementedError("Scalar feature is not implemented yet.")
385
+
386
+ affline_scale_log_info["affline_emb_B_D"] = affline_emb_B_D.detach()
387
+ affline_emb_B_D = self.affline_norm(affline_emb_B_D)
388
+
389
+ if self.use_cross_attn_mask:
390
+ if crossattn_mask is not None and not torch.is_floating_point(crossattn_mask):
391
+ crossattn_mask = (crossattn_mask - 1).to(x.dtype) * torch.finfo(x.dtype).max
392
+ crossattn_mask = crossattn_mask[:, None, None, :] # .to(dtype=torch.bool) # [B, 1, 1, length]
393
+ else:
394
+ crossattn_mask = None
395
+
396
+ if self.blocks["block0"].x_format == "THWBD":
397
+ x = rearrange(x_B_T_H_W_D, "B T H W D -> T H W B D")
398
+ if extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D is not None:
399
+ extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D = rearrange(
400
+ extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D, "B T H W D -> T H W B D"
401
+ )
402
+ crossattn_emb = rearrange(crossattn_emb, "B M D -> M B D")
403
+
404
+ if crossattn_mask:
405
+ crossattn_mask = rearrange(crossattn_mask, "B M -> M B")
406
+
407
+ elif self.blocks["block0"].x_format == "BTHWD":
408
+ x = x_B_T_H_W_D
409
+ else:
410
+ raise ValueError(f"Unknown x_format {self.blocks[0].x_format}")
411
+ output = {
412
+ "x": x,
413
+ "affline_emb_B_D": affline_emb_B_D,
414
+ "crossattn_emb": crossattn_emb,
415
+ "crossattn_mask": crossattn_mask,
416
+ "rope_emb_L_1_1_D": rope_emb_L_1_1_D,
417
+ "adaln_lora_B_3D": adaln_lora_B_3D,
418
+ "original_shape": original_shape,
419
+ "extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D": extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D,
420
+ }
421
+ return output
422
+
423
+ def forward(
424
+ self,
425
+ x: torch.Tensor,
426
+ timesteps: torch.Tensor,
427
+ context: torch.Tensor,
428
+ attention_mask: Optional[torch.Tensor] = None,
429
+ # crossattn_emb: torch.Tensor,
430
+ # crossattn_mask: Optional[torch.Tensor] = None,
431
+ fps: Optional[torch.Tensor] = None,
432
+ image_size: Optional[torch.Tensor] = None,
433
+ padding_mask: Optional[torch.Tensor] = None,
434
+ scalar_feature: Optional[torch.Tensor] = None,
435
+ data_type: Optional[DataType] = DataType.VIDEO,
436
+ latent_condition: Optional[torch.Tensor] = None,
437
+ latent_condition_sigma: Optional[torch.Tensor] = None,
438
+ condition_video_augment_sigma: Optional[torch.Tensor] = None,
439
+ **kwargs,
440
+ ):
441
+ return comfy.patcher_extension.WrapperExecutor.new_class_executor(
442
+ self._forward,
443
+ self,
444
+ comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, kwargs.get("transformer_options", {}))
445
+ ).execute(x,
446
+ timesteps,
447
+ context,
448
+ attention_mask,
449
+ fps,
450
+ image_size,
451
+ padding_mask,
452
+ scalar_feature,
453
+ data_type,
454
+ latent_condition,
455
+ latent_condition_sigma,
456
+ condition_video_augment_sigma,
457
+ **kwargs)
458
+
459
+ def _forward(
460
+ self,
461
+ x: torch.Tensor,
462
+ timesteps: torch.Tensor,
463
+ context: torch.Tensor,
464
+ attention_mask: Optional[torch.Tensor] = None,
465
+ # crossattn_emb: torch.Tensor,
466
+ # crossattn_mask: Optional[torch.Tensor] = None,
467
+ fps: Optional[torch.Tensor] = None,
468
+ image_size: Optional[torch.Tensor] = None,
469
+ padding_mask: Optional[torch.Tensor] = None,
470
+ scalar_feature: Optional[torch.Tensor] = None,
471
+ data_type: Optional[DataType] = DataType.VIDEO,
472
+ latent_condition: Optional[torch.Tensor] = None,
473
+ latent_condition_sigma: Optional[torch.Tensor] = None,
474
+ condition_video_augment_sigma: Optional[torch.Tensor] = None,
475
+ **kwargs,
476
+ ):
477
+ """
478
+ Args:
479
+ x: (B, C, T, H, W) tensor of spatial-temp inputs
480
+ timesteps: (B, ) tensor of timesteps
481
+ crossattn_emb: (B, N, D) tensor of cross-attention embeddings
482
+ crossattn_mask: (B, N) tensor of cross-attention masks
483
+ condition_video_augment_sigma: (B,) used in lvg(long video generation), we add noise with this sigma to
484
+ augment condition input, the lvg model will condition on the condition_video_augment_sigma value;
485
+ we need forward_before_blocks pass to the forward_before_blocks function.
486
+ """
487
+
488
+ crossattn_emb = context
489
+ crossattn_mask = attention_mask
490
+
491
+ inputs = self.forward_before_blocks(
492
+ x=x,
493
+ timesteps=timesteps,
494
+ crossattn_emb=crossattn_emb,
495
+ crossattn_mask=crossattn_mask,
496
+ fps=fps,
497
+ image_size=image_size,
498
+ padding_mask=padding_mask,
499
+ scalar_feature=scalar_feature,
500
+ data_type=data_type,
501
+ latent_condition=latent_condition,
502
+ latent_condition_sigma=latent_condition_sigma,
503
+ condition_video_augment_sigma=condition_video_augment_sigma,
504
+ **kwargs,
505
+ )
506
+ x, affline_emb_B_D, crossattn_emb, crossattn_mask, rope_emb_L_1_1_D, adaln_lora_B_3D, original_shape = (
507
+ inputs["x"],
508
+ inputs["affline_emb_B_D"],
509
+ inputs["crossattn_emb"],
510
+ inputs["crossattn_mask"],
511
+ inputs["rope_emb_L_1_1_D"],
512
+ inputs["adaln_lora_B_3D"],
513
+ inputs["original_shape"],
514
+ )
515
+ extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D = inputs["extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D"].to(x.dtype)
516
+ del inputs
517
+
518
+ if extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D is not None:
519
+ assert (
520
+ x.shape == extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D.shape
521
+ ), f"{x.shape} != {extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D.shape} {original_shape}"
522
+
523
+ transformer_options = kwargs.get("transformer_options", {})
524
+ for _, block in self.blocks.items():
525
+ assert (
526
+ self.blocks["block0"].x_format == block.x_format
527
+ ), f"First block has x_format {self.blocks[0].x_format}, got {block.x_format}"
528
+
529
+ if extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D is not None:
530
+ x += extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D
531
+ x = block(
532
+ x,
533
+ affline_emb_B_D,
534
+ crossattn_emb,
535
+ crossattn_mask,
536
+ rope_emb_L_1_1_D=rope_emb_L_1_1_D,
537
+ adaln_lora_B_3D=adaln_lora_B_3D,
538
+ transformer_options=transformer_options,
539
+ )
540
+
541
+ x_B_T_H_W_D = rearrange(x, "T H W B D -> B T H W D")
542
+
543
+ x_B_D_T_H_W = self.decoder_head(
544
+ x_B_T_H_W_D=x_B_T_H_W_D,
545
+ emb_B_D=affline_emb_B_D,
546
+ crossattn_emb=None,
547
+ origin_shape=original_shape,
548
+ crossattn_mask=None,
549
+ adaln_lora_B_3D=adaln_lora_B_3D,
550
+ )
551
+
552
+ return x_B_D_T_H_W
ComfyUI/comfy/ldm/cosmos/position_embedding.py ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
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 typing import List, Optional
17
+
18
+ import torch
19
+ from einops import rearrange, repeat
20
+ from torch import nn
21
+ import math
22
+
23
+
24
+ def normalize(x: torch.Tensor, dim: Optional[List[int]] = None, eps: float = 0) -> torch.Tensor:
25
+ """
26
+ Normalizes the input tensor along specified dimensions such that the average square norm of elements is adjusted.
27
+
28
+ Args:
29
+ x (torch.Tensor): The input tensor to normalize.
30
+ dim (list, optional): The dimensions over which to normalize. If None, normalizes over all dimensions except the first.
31
+ eps (float, optional): A small constant to ensure numerical stability during division.
32
+
33
+ Returns:
34
+ torch.Tensor: The normalized tensor.
35
+ """
36
+ if dim is None:
37
+ dim = list(range(1, x.ndim))
38
+ norm = torch.linalg.vector_norm(x, dim=dim, keepdim=True, dtype=torch.float32)
39
+ norm = torch.add(eps, norm, alpha=math.sqrt(norm.numel() / x.numel()))
40
+ return x / norm.to(x.dtype)
41
+
42
+
43
+ class VideoPositionEmb(nn.Module):
44
+ def forward(self, x_B_T_H_W_C: torch.Tensor, fps=Optional[torch.Tensor], device=None, dtype=None) -> torch.Tensor:
45
+ """
46
+ It delegates the embedding generation to generate_embeddings function.
47
+ """
48
+ B_T_H_W_C = x_B_T_H_W_C.shape
49
+ embeddings = self.generate_embeddings(B_T_H_W_C, fps=fps, device=device, dtype=dtype)
50
+
51
+ return embeddings
52
+
53
+ def generate_embeddings(self, B_T_H_W_C: torch.Size, fps=Optional[torch.Tensor], device=None):
54
+ raise NotImplementedError
55
+
56
+
57
+ class VideoRopePosition3DEmb(VideoPositionEmb):
58
+ def __init__(
59
+ self,
60
+ *, # enforce keyword arguments
61
+ head_dim: int,
62
+ len_h: int,
63
+ len_w: int,
64
+ len_t: int,
65
+ base_fps: int = 24,
66
+ h_extrapolation_ratio: float = 1.0,
67
+ w_extrapolation_ratio: float = 1.0,
68
+ t_extrapolation_ratio: float = 1.0,
69
+ enable_fps_modulation: bool = True,
70
+ device=None,
71
+ **kwargs, # used for compatibility with other positional embeddings; unused in this class
72
+ ):
73
+ del kwargs
74
+ super().__init__()
75
+ self.base_fps = base_fps
76
+ self.max_h = len_h
77
+ self.max_w = len_w
78
+ self.enable_fps_modulation = enable_fps_modulation
79
+
80
+ dim = head_dim
81
+ dim_h = dim // 6 * 2
82
+ dim_w = dim_h
83
+ dim_t = dim - 2 * dim_h
84
+ assert dim == dim_h + dim_w + dim_t, f"bad dim: {dim} != {dim_h} + {dim_w} + {dim_t}"
85
+ self.register_buffer(
86
+ "dim_spatial_range",
87
+ torch.arange(0, dim_h, 2, device=device)[: (dim_h // 2)].float() / dim_h,
88
+ persistent=False,
89
+ )
90
+ self.register_buffer(
91
+ "dim_temporal_range",
92
+ torch.arange(0, dim_t, 2, device=device)[: (dim_t // 2)].float() / dim_t,
93
+ persistent=False,
94
+ )
95
+
96
+ self.h_ntk_factor = h_extrapolation_ratio ** (dim_h / (dim_h - 2))
97
+ self.w_ntk_factor = w_extrapolation_ratio ** (dim_w / (dim_w - 2))
98
+ self.t_ntk_factor = t_extrapolation_ratio ** (dim_t / (dim_t - 2))
99
+
100
+ def generate_embeddings(
101
+ self,
102
+ B_T_H_W_C: torch.Size,
103
+ fps: Optional[torch.Tensor] = None,
104
+ h_ntk_factor: Optional[float] = None,
105
+ w_ntk_factor: Optional[float] = None,
106
+ t_ntk_factor: Optional[float] = None,
107
+ device=None,
108
+ dtype=None,
109
+ ):
110
+ """
111
+ Generate embeddings for the given input size.
112
+
113
+ Args:
114
+ B_T_H_W_C (torch.Size): Input tensor size (Batch, Time, Height, Width, Channels).
115
+ fps (Optional[torch.Tensor], optional): Frames per second. Defaults to None.
116
+ h_ntk_factor (Optional[float], optional): Height NTK factor. If None, uses self.h_ntk_factor.
117
+ w_ntk_factor (Optional[float], optional): Width NTK factor. If None, uses self.w_ntk_factor.
118
+ t_ntk_factor (Optional[float], optional): Time NTK factor. If None, uses self.t_ntk_factor.
119
+
120
+ Returns:
121
+ Not specified in the original code snippet.
122
+ """
123
+ h_ntk_factor = h_ntk_factor if h_ntk_factor is not None else self.h_ntk_factor
124
+ w_ntk_factor = w_ntk_factor if w_ntk_factor is not None else self.w_ntk_factor
125
+ t_ntk_factor = t_ntk_factor if t_ntk_factor is not None else self.t_ntk_factor
126
+
127
+ h_theta = 10000.0 * h_ntk_factor
128
+ w_theta = 10000.0 * w_ntk_factor
129
+ t_theta = 10000.0 * t_ntk_factor
130
+
131
+ h_spatial_freqs = 1.0 / (h_theta**self.dim_spatial_range.to(device=device))
132
+ w_spatial_freqs = 1.0 / (w_theta**self.dim_spatial_range.to(device=device))
133
+ temporal_freqs = 1.0 / (t_theta**self.dim_temporal_range.to(device=device))
134
+
135
+ B, T, H, W, _ = B_T_H_W_C
136
+ seq = torch.arange(max(H, W, T), dtype=torch.float, device=device)
137
+ uniform_fps = (fps is None) or isinstance(fps, (int, float)) or (fps.min() == fps.max())
138
+ assert (
139
+ uniform_fps or B == 1 or T == 1
140
+ ), "For video batch, batch size should be 1 for non-uniform fps. For image batch, T should be 1"
141
+ half_emb_h = torch.outer(seq[:H].to(device=device), h_spatial_freqs)
142
+ half_emb_w = torch.outer(seq[:W].to(device=device), w_spatial_freqs)
143
+
144
+ # apply sequence scaling in temporal dimension
145
+ if fps is None or self.enable_fps_modulation is False: # image case
146
+ half_emb_t = torch.outer(seq[:T].to(device=device), temporal_freqs)
147
+ else:
148
+ half_emb_t = torch.outer(seq[:T].to(device=device) / fps * self.base_fps, temporal_freqs)
149
+
150
+ half_emb_h = torch.stack([torch.cos(half_emb_h), -torch.sin(half_emb_h), torch.sin(half_emb_h), torch.cos(half_emb_h)], dim=-1)
151
+ half_emb_w = torch.stack([torch.cos(half_emb_w), -torch.sin(half_emb_w), torch.sin(half_emb_w), torch.cos(half_emb_w)], dim=-1)
152
+ half_emb_t = torch.stack([torch.cos(half_emb_t), -torch.sin(half_emb_t), torch.sin(half_emb_t), torch.cos(half_emb_t)], dim=-1)
153
+
154
+ em_T_H_W_D = torch.cat(
155
+ [
156
+ repeat(half_emb_t, "t d x -> t h w d x", h=H, w=W),
157
+ repeat(half_emb_h, "h d x -> t h w d x", t=T, w=W),
158
+ repeat(half_emb_w, "w d x -> t h w d x", t=T, h=H),
159
+ ]
160
+ , dim=-2,
161
+ )
162
+
163
+ return rearrange(em_T_H_W_D, "t h w d (i j) -> (t h w) d i j", i=2, j=2).float()
164
+
165
+
166
+ class LearnablePosEmbAxis(VideoPositionEmb):
167
+ def __init__(
168
+ self,
169
+ *, # enforce keyword arguments
170
+ interpolation: str,
171
+ model_channels: int,
172
+ len_h: int,
173
+ len_w: int,
174
+ len_t: int,
175
+ device=None,
176
+ dtype=None,
177
+ **kwargs,
178
+ ):
179
+ """
180
+ Args:
181
+ interpolation (str): we curretly only support "crop", ideally when we need extrapolation capacity, we should adjust frequency or other more advanced methods. they are not implemented yet.
182
+ """
183
+ del kwargs # unused
184
+ super().__init__()
185
+ self.interpolation = interpolation
186
+ assert self.interpolation in ["crop"], f"Unknown interpolation method {self.interpolation}"
187
+
188
+ self.pos_emb_h = nn.Parameter(torch.empty(len_h, model_channels, device=device, dtype=dtype))
189
+ self.pos_emb_w = nn.Parameter(torch.empty(len_w, model_channels, device=device, dtype=dtype))
190
+ self.pos_emb_t = nn.Parameter(torch.empty(len_t, model_channels, device=device, dtype=dtype))
191
+
192
+ def generate_embeddings(self, B_T_H_W_C: torch.Size, fps=Optional[torch.Tensor], device=None, dtype=None) -> torch.Tensor:
193
+ B, T, H, W, _ = B_T_H_W_C
194
+ if self.interpolation == "crop":
195
+ emb_h_H = self.pos_emb_h[:H].to(device=device, dtype=dtype)
196
+ emb_w_W = self.pos_emb_w[:W].to(device=device, dtype=dtype)
197
+ emb_t_T = self.pos_emb_t[:T].to(device=device, dtype=dtype)
198
+ emb = (
199
+ repeat(emb_t_T, "t d-> b t h w d", b=B, h=H, w=W)
200
+ + repeat(emb_h_H, "h d-> b t h w d", b=B, t=T, w=W)
201
+ + repeat(emb_w_W, "w d-> b t h w d", b=B, t=T, h=H)
202
+ )
203
+ assert list(emb.shape)[:4] == [B, T, H, W], f"bad shape: {list(emb.shape)[:4]} != {B, T, H, W}"
204
+ else:
205
+ raise ValueError(f"Unknown interpolation method {self.interpolation}")
206
+
207
+ return normalize(emb, dim=-1, eps=1e-6)
ComfyUI/comfy/ldm/cosmos/predict2.py ADDED
@@ -0,0 +1,932 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # original code from: https://github.com/nvidia-cosmos/cosmos-predict2
2
+
3
+ import torch
4
+ from torch import nn
5
+ from einops import rearrange
6
+ from einops.layers.torch import Rearrange
7
+ import logging
8
+ from typing import Callable, Optional, Tuple
9
+ import math
10
+
11
+ from .position_embedding import VideoRopePosition3DEmb, LearnablePosEmbAxis
12
+ from torchvision import transforms
13
+
14
+ import comfy.patcher_extension
15
+ from comfy.ldm.modules.attention import optimized_attention
16
+ import comfy.ldm.common_dit
17
+ import comfy.ops
18
+ import comfy.quant_ops
19
+
20
+
21
+ # ---------------------- Feed Forward Network -----------------------
22
+ class GPT2FeedForward(nn.Module):
23
+ def __init__(self, d_model: int, d_ff: int, device=None, dtype=None, operations=None) -> None:
24
+ super().__init__()
25
+ self.activation = nn.GELU()
26
+ self.layer1 = operations.Linear(d_model, d_ff, bias=False, device=device, dtype=dtype)
27
+ self.layer2 = operations.Linear(d_ff, d_model, bias=False, device=device, dtype=dtype)
28
+
29
+ self._layer_id = None
30
+ self._dim = d_model
31
+ self._hidden_dim = d_ff
32
+
33
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
34
+ x = self.layer1(x)
35
+
36
+ x = self.activation(x)
37
+ x = self.layer2(x)
38
+ return x
39
+
40
+
41
+ def torch_attention_op(q_B_S_H_D: torch.Tensor, k_B_S_H_D: torch.Tensor, v_B_S_H_D: torch.Tensor, transformer_options: Optional[dict] = {}) -> torch.Tensor:
42
+ """Computes multi-head attention using PyTorch's native implementation.
43
+
44
+ This function provides a PyTorch backend alternative to Transformer Engine's attention operation.
45
+ It rearranges the input tensors to match PyTorch's expected format, computes scaled dot-product
46
+ attention, and rearranges the output back to the original format.
47
+
48
+ The input tensor names use the following dimension conventions:
49
+
50
+ - B: batch size
51
+ - S: sequence length
52
+ - H: number of attention heads
53
+ - D: head dimension
54
+
55
+ Args:
56
+ q_B_S_H_D: Query tensor with shape (batch, seq_len, n_heads, head_dim)
57
+ k_B_S_H_D: Key tensor with shape (batch, seq_len, n_heads, head_dim)
58
+ v_B_S_H_D: Value tensor with shape (batch, seq_len, n_heads, head_dim)
59
+
60
+ Returns:
61
+ Attention output tensor with shape (batch, seq_len, n_heads * head_dim)
62
+ """
63
+ in_q_shape = q_B_S_H_D.shape
64
+ in_k_shape = k_B_S_H_D.shape
65
+ q_B_H_S_D = rearrange(q_B_S_H_D, "b ... h k -> b h ... k").view(in_q_shape[0], in_q_shape[-2], -1, in_q_shape[-1])
66
+ k_B_H_S_D = rearrange(k_B_S_H_D, "b ... h v -> b h ... v").view(in_k_shape[0], in_k_shape[-2], -1, in_k_shape[-1])
67
+ v_B_H_S_D = rearrange(v_B_S_H_D, "b ... h v -> b h ... v").view(in_k_shape[0], in_k_shape[-2], -1, in_k_shape[-1])
68
+ return optimized_attention(q_B_H_S_D, k_B_H_S_D, v_B_H_S_D, in_q_shape[-2], skip_reshape=True, transformer_options=transformer_options)
69
+
70
+
71
+ class Attention(nn.Module):
72
+ """
73
+ A flexible attention module supporting both self-attention and cross-attention mechanisms.
74
+
75
+ This module implements a multi-head attention layer that can operate in either self-attention
76
+ or cross-attention mode. The mode is determined by whether a context dimension is provided.
77
+ The implementation uses scaled dot-product attention and supports optional bias terms and
78
+ dropout regularization.
79
+
80
+ Args:
81
+ query_dim (int): The dimensionality of the query vectors.
82
+ context_dim (int, optional): The dimensionality of the context (key/value) vectors.
83
+ If None, the module operates in self-attention mode using query_dim. Default: None
84
+ n_heads (int, optional): Number of attention heads for multi-head attention. Default: 8
85
+ head_dim (int, optional): The dimension of each attention head. Default: 64
86
+ dropout (float, optional): Dropout probability applied to the output. Default: 0.0
87
+ qkv_format (str, optional): Format specification for QKV tensors. Default: "bshd"
88
+ backend (str, optional): Backend to use for the attention operation. Default: "transformer_engine"
89
+
90
+ Examples:
91
+ >>> # Self-attention with 512 dimensions and 8 heads
92
+ >>> self_attn = Attention(query_dim=512)
93
+ >>> x = torch.randn(32, 16, 512) # (batch_size, seq_len, dim)
94
+ >>> out = self_attn(x) # (32, 16, 512)
95
+
96
+ >>> # Cross-attention
97
+ >>> cross_attn = Attention(query_dim=512, context_dim=256)
98
+ >>> query = torch.randn(32, 16, 512)
99
+ >>> context = torch.randn(32, 8, 256)
100
+ >>> out = cross_attn(query, context) # (32, 16, 512)
101
+ """
102
+
103
+ def __init__(
104
+ self,
105
+ query_dim: int,
106
+ context_dim: Optional[int] = None,
107
+ n_heads: int = 8,
108
+ head_dim: int = 64,
109
+ dropout: float = 0.0,
110
+ device=None,
111
+ dtype=None,
112
+ operations=None,
113
+ ) -> None:
114
+ super().__init__()
115
+ logging.debug(
116
+ f"Setting up {self.__class__.__name__}. Query dim is {query_dim}, context_dim is {context_dim} and using "
117
+ f"{n_heads} heads with a dimension of {head_dim}."
118
+ )
119
+ self.is_selfattn = context_dim is None # self attention
120
+
121
+ context_dim = query_dim if context_dim is None else context_dim
122
+ inner_dim = head_dim * n_heads
123
+
124
+ self.n_heads = n_heads
125
+ self.head_dim = head_dim
126
+ self.query_dim = query_dim
127
+ self.context_dim = context_dim
128
+
129
+ self.q_proj = operations.Linear(query_dim, inner_dim, bias=False, device=device, dtype=dtype)
130
+ self.q_norm = operations.RMSNorm(self.head_dim, eps=1e-6, device=device, dtype=dtype)
131
+
132
+ self.k_proj = operations.Linear(context_dim, inner_dim, bias=False, device=device, dtype=dtype)
133
+ self.k_norm = operations.RMSNorm(self.head_dim, eps=1e-6, device=device, dtype=dtype)
134
+
135
+ self.v_proj = operations.Linear(context_dim, inner_dim, bias=False, device=device, dtype=dtype)
136
+ self.v_norm = nn.Identity()
137
+
138
+ self.output_proj = operations.Linear(inner_dim, query_dim, bias=False, device=device, dtype=dtype)
139
+ self.output_dropout = nn.Dropout(dropout) if dropout > 1e-4 else nn.Identity()
140
+
141
+ self.attn_op = torch_attention_op
142
+
143
+ self._query_dim = query_dim
144
+ self._context_dim = context_dim
145
+ self._inner_dim = inner_dim
146
+
147
+ def compute_qkv(
148
+ self,
149
+ x: torch.Tensor,
150
+ context: Optional[torch.Tensor] = None,
151
+ rope_emb: Optional[torch.Tensor] = None,
152
+ transformer_options: Optional[dict] = {},
153
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
154
+ context = x if context is None else context
155
+ q_input = x
156
+ k_input = context
157
+ v_input = context
158
+
159
+ transformer_patches = transformer_options.get("patches", {})
160
+ patch_name = "attn1_patch" if self.is_selfattn else "attn2_patch"
161
+ if patch_name in transformer_patches:
162
+ extra_options = transformer_options.copy()
163
+ extra_options["n_heads"] = self.n_heads
164
+ extra_options["dim_head"] = self.head_dim
165
+ for patch in transformer_patches[patch_name]:
166
+ out = patch(q_input, k_input, v_input, pe=rope_emb, attn_mask=None, extra_options=extra_options)
167
+ q_input = out.get("q", q_input)
168
+ k_input = out.get("k", k_input)
169
+ v_input = out.get("v", v_input)
170
+ rope_emb = out.get("pe", rope_emb)
171
+
172
+ q = self.q_proj(q_input)
173
+ k = self.k_proj(k_input)
174
+ v = self.v_proj(v_input)
175
+ q, k, v = map(
176
+ lambda t: rearrange(t, "b ... (h d) -> b ... h d", h=self.n_heads, d=self.head_dim),
177
+ (q, k, v),
178
+ )
179
+
180
+ def apply_norm_and_rotary_pos_emb(
181
+ q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, rope_emb: Optional[torch.Tensor]
182
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
183
+ v = self.v_norm(v)
184
+ if self.is_selfattn and rope_emb is not None: # only apply to self-attention!
185
+ q_scale, _, q_offload_stream = comfy.ops.cast_bias_weight(self.q_norm, q, offloadable=True)
186
+ k_scale, _, k_offload_stream = comfy.ops.cast_bias_weight(self.k_norm, k, offloadable=True)
187
+ q, k = comfy.quant_ops.ck.rms_rope_split_half(q, k, rope_emb, q_scale, k_scale, self.q_norm.eps)
188
+ comfy.ops.uncast_bias_weight(self.q_norm, q_scale, None, q_offload_stream)
189
+ comfy.ops.uncast_bias_weight(self.k_norm, k_scale, None, k_offload_stream)
190
+ else:
191
+ q = self.q_norm(q)
192
+ k = self.k_norm(k)
193
+ return q, k, v
194
+
195
+ q, k, v = apply_norm_and_rotary_pos_emb(q, k, v, rope_emb)
196
+
197
+ return q, k, v
198
+
199
+ def compute_attention(self, q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, transformer_options: Optional[dict] = {}) -> torch.Tensor:
200
+ result = self.attn_op(q, k, v, transformer_options=transformer_options) # [B, S, H, D]
201
+ return self.output_dropout(self.output_proj(result))
202
+
203
+ def forward(
204
+ self,
205
+ x: torch.Tensor,
206
+ context: Optional[torch.Tensor] = None,
207
+ rope_emb: Optional[torch.Tensor] = None,
208
+ transformer_options: Optional[dict] = {},
209
+ ) -> torch.Tensor:
210
+ """
211
+ Args:
212
+ x (Tensor): The query tensor of shape [B, Mq, K]
213
+ context (Optional[Tensor]): The key tensor of shape [B, Mk, K] or use x as context [self attention] if None
214
+ """
215
+ q, k, v = self.compute_qkv(x, context, rope_emb=rope_emb, transformer_options=transformer_options)
216
+ return self.compute_attention(q, k, v, transformer_options=transformer_options)
217
+
218
+
219
+ class Timesteps(nn.Module):
220
+ def __init__(self, num_channels: int):
221
+ super().__init__()
222
+ self.num_channels = num_channels
223
+
224
+ def forward(self, timesteps_B_T: torch.Tensor) -> torch.Tensor:
225
+ assert timesteps_B_T.ndim == 2, f"Expected 2D input, got {timesteps_B_T.ndim}"
226
+ timesteps = timesteps_B_T.flatten().float()
227
+ half_dim = self.num_channels // 2
228
+ exponent = -math.log(10000) * torch.arange(half_dim, dtype=torch.float32, device=timesteps.device)
229
+ exponent = exponent / (half_dim - 0.0)
230
+
231
+ emb = torch.exp(exponent)
232
+ emb = timesteps[:, None].float() * emb[None, :]
233
+
234
+ sin_emb = torch.sin(emb)
235
+ cos_emb = torch.cos(emb)
236
+ emb = torch.cat([cos_emb, sin_emb], dim=-1)
237
+
238
+ return rearrange(emb, "(b t) d -> b t d", b=timesteps_B_T.shape[0], t=timesteps_B_T.shape[1])
239
+
240
+
241
+ class TimestepEmbedding(nn.Module):
242
+ def __init__(self, in_features: int, out_features: int, use_adaln_lora: bool = False, device=None, dtype=None, operations=None):
243
+ super().__init__()
244
+ logging.debug(
245
+ f"Using AdaLN LoRA Flag: {use_adaln_lora}. We enable bias if no AdaLN LoRA for backward compatibility."
246
+ )
247
+ self.in_dim = in_features
248
+ self.out_dim = out_features
249
+ self.linear_1 = operations.Linear(in_features, out_features, bias=not use_adaln_lora, device=device, dtype=dtype)
250
+ self.activation = nn.SiLU()
251
+ self.use_adaln_lora = use_adaln_lora
252
+ if use_adaln_lora:
253
+ self.linear_2 = operations.Linear(out_features, 3 * out_features, bias=False, device=device, dtype=dtype)
254
+ else:
255
+ self.linear_2 = operations.Linear(out_features, out_features, bias=False, device=device, dtype=dtype)
256
+
257
+ def forward(self, sample: torch.Tensor) -> Tuple[torch.Tensor, Optional[torch.Tensor]]:
258
+ emb = self.linear_1(sample)
259
+ emb = self.activation(emb)
260
+ emb = self.linear_2(emb)
261
+
262
+ if self.use_adaln_lora:
263
+ adaln_lora_B_T_3D = emb
264
+ emb_B_T_D = sample
265
+ else:
266
+ adaln_lora_B_T_3D = None
267
+ emb_B_T_D = emb
268
+
269
+ return emb_B_T_D, adaln_lora_B_T_3D
270
+
271
+
272
+ class PatchEmbed(nn.Module):
273
+ """
274
+ PatchEmbed is a module for embedding patches from an input tensor by applying either 3D or 2D convolutional layers,
275
+ depending on the . This module can process inputs with temporal (video) and spatial (image) dimensions,
276
+ making it suitable for video and image processing tasks. It supports dividing the input into patches
277
+ and embedding each patch into a vector of size `out_channels`.
278
+
279
+ Parameters:
280
+ - spatial_patch_size (int): The size of each spatial patch.
281
+ - temporal_patch_size (int): The size of each temporal patch.
282
+ - in_channels (int): Number of input channels. Default: 3.
283
+ - out_channels (int): The dimension of the embedding vector for each patch. Default: 768.
284
+ - bias (bool): If True, adds a learnable bias to the output of the convolutional layers. Default: True.
285
+ """
286
+
287
+ def __init__(
288
+ self,
289
+ spatial_patch_size: int,
290
+ temporal_patch_size: int,
291
+ in_channels: int = 3,
292
+ out_channels: int = 768,
293
+ device=None, dtype=None, operations=None
294
+ ):
295
+ super().__init__()
296
+ self.spatial_patch_size = spatial_patch_size
297
+ self.temporal_patch_size = temporal_patch_size
298
+
299
+ self.proj = nn.Sequential(
300
+ Rearrange(
301
+ "b c (t r) (h m) (w n) -> b t h w (c r m n)",
302
+ r=temporal_patch_size,
303
+ m=spatial_patch_size,
304
+ n=spatial_patch_size,
305
+ ),
306
+ operations.Linear(
307
+ in_channels * spatial_patch_size * spatial_patch_size * temporal_patch_size, out_channels, bias=False, device=device, dtype=dtype
308
+ ),
309
+ )
310
+ self.dim = in_channels * spatial_patch_size * spatial_patch_size * temporal_patch_size
311
+
312
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
313
+ """
314
+ Forward pass of the PatchEmbed module.
315
+
316
+ Parameters:
317
+ - x (torch.Tensor): The input tensor of shape (B, C, T, H, W) where
318
+ B is the batch size,
319
+ C is the number of channels,
320
+ T is the temporal dimension,
321
+ H is the height, and
322
+ W is the width of the input.
323
+
324
+ Returns:
325
+ - torch.Tensor: The embedded patches as a tensor, with shape b t h w c.
326
+ """
327
+ assert x.dim() == 5
328
+ _, _, T, H, W = x.shape
329
+ assert (
330
+ H % self.spatial_patch_size == 0 and W % self.spatial_patch_size == 0
331
+ ), f"H,W {(H, W)} should be divisible by spatial_patch_size {self.spatial_patch_size}"
332
+ assert T % self.temporal_patch_size == 0
333
+ x = self.proj(x)
334
+ return x
335
+
336
+
337
+ class FinalLayer(nn.Module):
338
+ """
339
+ The final layer of video DiT.
340
+ """
341
+
342
+ def __init__(
343
+ self,
344
+ hidden_size: int,
345
+ spatial_patch_size: int,
346
+ temporal_patch_size: int,
347
+ out_channels: int,
348
+ use_adaln_lora: bool = False,
349
+ adaln_lora_dim: int = 256,
350
+ device=None, dtype=None, operations=None
351
+ ):
352
+ super().__init__()
353
+ self.layer_norm = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
354
+ self.linear = operations.Linear(
355
+ hidden_size, spatial_patch_size * spatial_patch_size * temporal_patch_size * out_channels, bias=False, device=device, dtype=dtype
356
+ )
357
+ self.hidden_size = hidden_size
358
+ self.n_adaln_chunks = 2
359
+ self.use_adaln_lora = use_adaln_lora
360
+ self.adaln_lora_dim = adaln_lora_dim
361
+ if use_adaln_lora:
362
+ self.adaln_modulation = nn.Sequential(
363
+ nn.SiLU(),
364
+ operations.Linear(hidden_size, adaln_lora_dim, bias=False, device=device, dtype=dtype),
365
+ operations.Linear(adaln_lora_dim, self.n_adaln_chunks * hidden_size, bias=False, device=device, dtype=dtype),
366
+ )
367
+ else:
368
+ self.adaln_modulation = nn.Sequential(
369
+ nn.SiLU(), operations.Linear(hidden_size, self.n_adaln_chunks * hidden_size, bias=False, device=device, dtype=dtype)
370
+ )
371
+
372
+ def forward(
373
+ self,
374
+ x_B_T_H_W_D: torch.Tensor,
375
+ emb_B_T_D: torch.Tensor,
376
+ adaln_lora_B_T_3D: Optional[torch.Tensor] = None,
377
+ ):
378
+ if self.use_adaln_lora:
379
+ assert adaln_lora_B_T_3D is not None
380
+ shift_B_T_D, scale_B_T_D = (
381
+ self.adaln_modulation(emb_B_T_D) + adaln_lora_B_T_3D[:, :, : 2 * self.hidden_size]
382
+ ).chunk(2, dim=-1)
383
+ else:
384
+ shift_B_T_D, scale_B_T_D = self.adaln_modulation(emb_B_T_D).chunk(2, dim=-1)
385
+
386
+ shift_B_T_1_1_D, scale_B_T_1_1_D = rearrange(shift_B_T_D, "b t d -> b t 1 1 d"), rearrange(
387
+ scale_B_T_D, "b t d -> b t 1 1 d"
388
+ )
389
+
390
+ def _fn(
391
+ _x_B_T_H_W_D: torch.Tensor,
392
+ _norm_layer: nn.Module,
393
+ _scale_B_T_1_1_D: torch.Tensor,
394
+ _shift_B_T_1_1_D: torch.Tensor,
395
+ ) -> torch.Tensor:
396
+ return _norm_layer(_x_B_T_H_W_D) * (1 + _scale_B_T_1_1_D) + _shift_B_T_1_1_D
397
+
398
+ x_B_T_H_W_D = _fn(x_B_T_H_W_D, self.layer_norm, scale_B_T_1_1_D, shift_B_T_1_1_D)
399
+ x_B_T_H_W_O = self.linear(x_B_T_H_W_D)
400
+ return x_B_T_H_W_O
401
+
402
+
403
+ class Block(nn.Module):
404
+ """
405
+ A transformer block that combines self-attention, cross-attention and MLP layers with AdaLN modulation.
406
+ Each component (self-attention, cross-attention, MLP) has its own layer normalization and AdaLN modulation.
407
+
408
+ Parameters:
409
+ x_dim (int): Dimension of input features
410
+ context_dim (int): Dimension of context features for cross-attention
411
+ num_heads (int): Number of attention heads
412
+ mlp_ratio (float): Multiplier for MLP hidden dimension. Default: 4.0
413
+ use_adaln_lora (bool): Whether to use AdaLN-LoRA modulation. Default: False
414
+ adaln_lora_dim (int): Hidden dimension for AdaLN-LoRA layers. Default: 256
415
+
416
+ The block applies the following sequence:
417
+ 1. Self-attention with AdaLN modulation
418
+ 2. Cross-attention with AdaLN modulation
419
+ 3. MLP with AdaLN modulation
420
+
421
+ Each component uses skip connections and layer normalization.
422
+ """
423
+
424
+ def __init__(
425
+ self,
426
+ x_dim: int,
427
+ context_dim: int,
428
+ num_heads: int,
429
+ mlp_ratio: float = 4.0,
430
+ use_adaln_lora: bool = False,
431
+ adaln_lora_dim: int = 256,
432
+ device=None,
433
+ dtype=None,
434
+ operations=None,
435
+ ):
436
+ super().__init__()
437
+ self.x_dim = x_dim
438
+ self.layer_norm_self_attn = operations.LayerNorm(x_dim, elementwise_affine=False, eps=1e-6, device=device, dtype=dtype)
439
+ self.self_attn = Attention(x_dim, None, num_heads, x_dim // num_heads, device=device, dtype=dtype, operations=operations)
440
+
441
+ self.layer_norm_cross_attn = operations.LayerNorm(x_dim, elementwise_affine=False, eps=1e-6, device=device, dtype=dtype)
442
+ self.cross_attn = Attention(
443
+ x_dim, context_dim, num_heads, x_dim // num_heads, device=device, dtype=dtype, operations=operations
444
+ )
445
+
446
+ self.layer_norm_mlp = operations.LayerNorm(x_dim, elementwise_affine=False, eps=1e-6, device=device, dtype=dtype)
447
+ self.mlp = GPT2FeedForward(x_dim, int(x_dim * mlp_ratio), device=device, dtype=dtype, operations=operations)
448
+
449
+ self.use_adaln_lora = use_adaln_lora
450
+ if self.use_adaln_lora:
451
+ self.adaln_modulation_self_attn = nn.Sequential(
452
+ nn.SiLU(),
453
+ operations.Linear(x_dim, adaln_lora_dim, bias=False, device=device, dtype=dtype),
454
+ operations.Linear(adaln_lora_dim, 3 * x_dim, bias=False, device=device, dtype=dtype),
455
+ )
456
+ self.adaln_modulation_cross_attn = nn.Sequential(
457
+ nn.SiLU(),
458
+ operations.Linear(x_dim, adaln_lora_dim, bias=False, device=device, dtype=dtype),
459
+ operations.Linear(adaln_lora_dim, 3 * x_dim, bias=False, device=device, dtype=dtype),
460
+ )
461
+ self.adaln_modulation_mlp = nn.Sequential(
462
+ nn.SiLU(),
463
+ operations.Linear(x_dim, adaln_lora_dim, bias=False, device=device, dtype=dtype),
464
+ operations.Linear(adaln_lora_dim, 3 * x_dim, bias=False, device=device, dtype=dtype),
465
+ )
466
+ else:
467
+ self.adaln_modulation_self_attn = nn.Sequential(nn.SiLU(), operations.Linear(x_dim, 3 * x_dim, bias=False, device=device, dtype=dtype))
468
+ self.adaln_modulation_cross_attn = nn.Sequential(nn.SiLU(), operations.Linear(x_dim, 3 * x_dim, bias=False, device=device, dtype=dtype))
469
+ self.adaln_modulation_mlp = nn.Sequential(nn.SiLU(), operations.Linear(x_dim, 3 * x_dim, bias=False, device=device, dtype=dtype))
470
+
471
+ def forward(
472
+ self,
473
+ x_B_T_H_W_D: torch.Tensor,
474
+ emb_B_T_D: torch.Tensor,
475
+ crossattn_emb: torch.Tensor,
476
+ rope_emb_L_1_1_D: Optional[torch.Tensor] = None,
477
+ adaln_lora_B_T_3D: Optional[torch.Tensor] = None,
478
+ extra_per_block_pos_emb: Optional[torch.Tensor] = None,
479
+ transformer_options: Optional[dict] = {},
480
+ ) -> torch.Tensor:
481
+ residual_dtype = x_B_T_H_W_D.dtype
482
+ compute_dtype = emb_B_T_D.dtype
483
+ if extra_per_block_pos_emb is not None:
484
+ x_B_T_H_W_D = x_B_T_H_W_D + extra_per_block_pos_emb
485
+
486
+ if self.use_adaln_lora:
487
+ shift_self_attn_B_T_D, scale_self_attn_B_T_D, gate_self_attn_B_T_D = (
488
+ self.adaln_modulation_self_attn(emb_B_T_D) + adaln_lora_B_T_3D
489
+ ).chunk(3, dim=-1)
490
+ shift_cross_attn_B_T_D, scale_cross_attn_B_T_D, gate_cross_attn_B_T_D = (
491
+ self.adaln_modulation_cross_attn(emb_B_T_D) + adaln_lora_B_T_3D
492
+ ).chunk(3, dim=-1)
493
+ shift_mlp_B_T_D, scale_mlp_B_T_D, gate_mlp_B_T_D = (
494
+ self.adaln_modulation_mlp(emb_B_T_D) + adaln_lora_B_T_3D
495
+ ).chunk(3, dim=-1)
496
+ else:
497
+ shift_self_attn_B_T_D, scale_self_attn_B_T_D, gate_self_attn_B_T_D = self.adaln_modulation_self_attn(
498
+ emb_B_T_D
499
+ ).chunk(3, dim=-1)
500
+ shift_cross_attn_B_T_D, scale_cross_attn_B_T_D, gate_cross_attn_B_T_D = self.adaln_modulation_cross_attn(
501
+ emb_B_T_D
502
+ ).chunk(3, dim=-1)
503
+ shift_mlp_B_T_D, scale_mlp_B_T_D, gate_mlp_B_T_D = self.adaln_modulation_mlp(emb_B_T_D).chunk(3, dim=-1)
504
+
505
+ # Reshape tensors from (B, T, D) to (B, T, 1, 1, D) for broadcasting
506
+ shift_self_attn_B_T_1_1_D = rearrange(shift_self_attn_B_T_D, "b t d -> b t 1 1 d")
507
+ scale_self_attn_B_T_1_1_D = rearrange(scale_self_attn_B_T_D, "b t d -> b t 1 1 d")
508
+ gate_self_attn_B_T_1_1_D = rearrange(gate_self_attn_B_T_D, "b t d -> b t 1 1 d")
509
+
510
+ shift_cross_attn_B_T_1_1_D = rearrange(shift_cross_attn_B_T_D, "b t d -> b t 1 1 d")
511
+ scale_cross_attn_B_T_1_1_D = rearrange(scale_cross_attn_B_T_D, "b t d -> b t 1 1 d")
512
+ gate_cross_attn_B_T_1_1_D = rearrange(gate_cross_attn_B_T_D, "b t d -> b t 1 1 d")
513
+
514
+ shift_mlp_B_T_1_1_D = rearrange(shift_mlp_B_T_D, "b t d -> b t 1 1 d")
515
+ scale_mlp_B_T_1_1_D = rearrange(scale_mlp_B_T_D, "b t d -> b t 1 1 d")
516
+ gate_mlp_B_T_1_1_D = rearrange(gate_mlp_B_T_D, "b t d -> b t 1 1 d")
517
+
518
+ B, T, H, W, D = x_B_T_H_W_D.shape
519
+
520
+ def _fn(_x_B_T_H_W_D, _norm_layer, _scale_B_T_1_1_D, _shift_B_T_1_1_D):
521
+ return _norm_layer(_x_B_T_H_W_D) * (1 + _scale_B_T_1_1_D) + _shift_B_T_1_1_D
522
+
523
+ normalized_x_B_T_H_W_D = _fn(
524
+ x_B_T_H_W_D,
525
+ self.layer_norm_self_attn,
526
+ scale_self_attn_B_T_1_1_D,
527
+ shift_self_attn_B_T_1_1_D,
528
+ )
529
+ result_B_T_H_W_D = rearrange(
530
+ self.self_attn(
531
+ # normalized_x_B_T_HW_D,
532
+ rearrange(normalized_x_B_T_H_W_D.to(compute_dtype), "b t h w d -> b (t h w) d"),
533
+ None,
534
+ rope_emb=rope_emb_L_1_1_D,
535
+ transformer_options=transformer_options,
536
+ ),
537
+ "b (t h w) d -> b t h w d",
538
+ t=T,
539
+ h=H,
540
+ w=W,
541
+ )
542
+ x_B_T_H_W_D = torch.addcmul(x_B_T_H_W_D, gate_self_attn_B_T_1_1_D.to(residual_dtype), result_B_T_H_W_D.to(residual_dtype))
543
+
544
+ def _x_fn(
545
+ _x_B_T_H_W_D: torch.Tensor,
546
+ layer_norm_cross_attn: Callable,
547
+ _scale_cross_attn_B_T_1_1_D: torch.Tensor,
548
+ _shift_cross_attn_B_T_1_1_D: torch.Tensor,
549
+ transformer_options: Optional[dict] = {},
550
+ ) -> torch.Tensor:
551
+ _normalized_x_B_T_H_W_D = _fn(
552
+ _x_B_T_H_W_D, layer_norm_cross_attn, _scale_cross_attn_B_T_1_1_D, _shift_cross_attn_B_T_1_1_D
553
+ )
554
+ _result_B_T_H_W_D = rearrange(
555
+ self.cross_attn(
556
+ rearrange(_normalized_x_B_T_H_W_D.to(compute_dtype), "b t h w d -> b (t h w) d"),
557
+ crossattn_emb,
558
+ rope_emb=rope_emb_L_1_1_D,
559
+ transformer_options=transformer_options,
560
+ ),
561
+ "b (t h w) d -> b t h w d",
562
+ t=T,
563
+ h=H,
564
+ w=W,
565
+ )
566
+ return _result_B_T_H_W_D
567
+
568
+ result_B_T_H_W_D = _x_fn(
569
+ x_B_T_H_W_D,
570
+ self.layer_norm_cross_attn,
571
+ scale_cross_attn_B_T_1_1_D,
572
+ shift_cross_attn_B_T_1_1_D,
573
+ transformer_options=transformer_options,
574
+ )
575
+ x_B_T_H_W_D = torch.addcmul(x_B_T_H_W_D, gate_cross_attn_B_T_1_1_D.to(residual_dtype), result_B_T_H_W_D.to(residual_dtype))
576
+
577
+ normalized_x_B_T_H_W_D = _fn(
578
+ x_B_T_H_W_D,
579
+ self.layer_norm_mlp,
580
+ scale_mlp_B_T_1_1_D,
581
+ shift_mlp_B_T_1_1_D,
582
+ ).to(compute_dtype)
583
+ patches = transformer_options.get("patches", {})
584
+ if "mlp_patch" in patches:
585
+ args = {"x": normalized_x_B_T_H_W_D, "transformer_options": transformer_options}
586
+ for patch in patches["mlp_patch"]:
587
+ args = patch(args)
588
+ normalized_x_B_T_H_W_D = args["x"]
589
+ result_B_T_H_W_D = self.mlp(normalized_x_B_T_H_W_D)
590
+ x_B_T_H_W_D = torch.addcmul(x_B_T_H_W_D, gate_mlp_B_T_1_1_D.to(residual_dtype), result_B_T_H_W_D.to(residual_dtype))
591
+ return x_B_T_H_W_D
592
+
593
+
594
+ class MiniTrainDIT(nn.Module):
595
+ """
596
+ A clean impl of DIT that can load and reproduce the training results of the original DIT model in~(cosmos 1)
597
+ A general implementation of adaln-modulated VIT-like~(DiT) transformer for video processing.
598
+
599
+ Args:
600
+ max_img_h (int): Maximum height of the input images.
601
+ max_img_w (int): Maximum width of the input images.
602
+ max_frames (int): Maximum number of frames in the video sequence.
603
+ in_channels (int): Number of input channels (e.g., RGB channels for color images).
604
+ out_channels (int): Number of output channels.
605
+ patch_spatial (tuple): Spatial resolution of patches for input processing.
606
+ patch_temporal (int): Temporal resolution of patches for input processing.
607
+ concat_padding_mask (bool): If True, includes a mask channel in the input to handle padding.
608
+ model_channels (int): Base number of channels used throughout the model.
609
+ num_blocks (int): Number of transformer blocks.
610
+ num_heads (int): Number of heads in the multi-head attention layers.
611
+ mlp_ratio (float): Expansion ratio for MLP blocks.
612
+ crossattn_emb_channels (int): Number of embedding channels for cross-attention.
613
+ pos_emb_cls (str): Type of positional embeddings.
614
+ pos_emb_learnable (bool): Whether positional embeddings are learnable.
615
+ pos_emb_interpolation (str): Method for interpolating positional embeddings.
616
+ min_fps (int): Minimum frames per second.
617
+ max_fps (int): Maximum frames per second.
618
+ use_adaln_lora (bool): Whether to use AdaLN-LoRA.
619
+ adaln_lora_dim (int): Dimension for AdaLN-LoRA.
620
+ rope_h_extrapolation_ratio (float): Height extrapolation ratio for RoPE.
621
+ rope_w_extrapolation_ratio (float): Width extrapolation ratio for RoPE.
622
+ rope_t_extrapolation_ratio (float): Temporal extrapolation ratio for RoPE.
623
+ extra_per_block_abs_pos_emb (bool): Whether to use extra per-block absolute positional embeddings.
624
+ extra_h_extrapolation_ratio (float): Height extrapolation ratio for extra embeddings.
625
+ extra_w_extrapolation_ratio (float): Width extrapolation ratio for extra embeddings.
626
+ extra_t_extrapolation_ratio (float): Temporal extrapolation ratio for extra embeddings.
627
+ """
628
+
629
+ def __init__(
630
+ self,
631
+ max_img_h: int,
632
+ max_img_w: int,
633
+ max_frames: int,
634
+ in_channels: int,
635
+ out_channels: int,
636
+ patch_spatial: int, # tuple,
637
+ patch_temporal: int,
638
+ concat_padding_mask: bool = True,
639
+ # attention settings
640
+ model_channels: int = 768,
641
+ num_blocks: int = 10,
642
+ num_heads: int = 16,
643
+ mlp_ratio: float = 4.0,
644
+ # cross attention settings
645
+ crossattn_emb_channels: int = 1024,
646
+ # positional embedding settings
647
+ pos_emb_cls: str = "sincos",
648
+ pos_emb_learnable: bool = False,
649
+ pos_emb_interpolation: str = "crop",
650
+ min_fps: int = 1,
651
+ max_fps: int = 30,
652
+ use_adaln_lora: bool = False,
653
+ adaln_lora_dim: int = 256,
654
+ rope_h_extrapolation_ratio: float = 1.0,
655
+ rope_w_extrapolation_ratio: float = 1.0,
656
+ rope_t_extrapolation_ratio: float = 1.0,
657
+ extra_per_block_abs_pos_emb: bool = False,
658
+ extra_h_extrapolation_ratio: float = 1.0,
659
+ extra_w_extrapolation_ratio: float = 1.0,
660
+ extra_t_extrapolation_ratio: float = 1.0,
661
+ rope_enable_fps_modulation: bool = True,
662
+ image_model=None,
663
+ device=None,
664
+ dtype=None,
665
+ operations=None,
666
+ ) -> None:
667
+ super().__init__()
668
+ self.dtype = dtype
669
+ self.max_img_h = max_img_h
670
+ self.max_img_w = max_img_w
671
+ self.max_frames = max_frames
672
+ self.in_channels = in_channels
673
+ self.out_channels = out_channels
674
+ self.patch_spatial = patch_spatial
675
+ self.patch_temporal = patch_temporal
676
+ self.num_heads = num_heads
677
+ self.num_blocks = num_blocks
678
+ self.model_channels = model_channels
679
+ self.concat_padding_mask = concat_padding_mask
680
+ # positional embedding settings
681
+ self.pos_emb_cls = pos_emb_cls
682
+ self.pos_emb_learnable = pos_emb_learnable
683
+ self.pos_emb_interpolation = pos_emb_interpolation
684
+ self.min_fps = min_fps
685
+ self.max_fps = max_fps
686
+ self.rope_h_extrapolation_ratio = rope_h_extrapolation_ratio
687
+ self.rope_w_extrapolation_ratio = rope_w_extrapolation_ratio
688
+ self.rope_t_extrapolation_ratio = rope_t_extrapolation_ratio
689
+ self.extra_per_block_abs_pos_emb = extra_per_block_abs_pos_emb
690
+ self.extra_h_extrapolation_ratio = extra_h_extrapolation_ratio
691
+ self.extra_w_extrapolation_ratio = extra_w_extrapolation_ratio
692
+ self.extra_t_extrapolation_ratio = extra_t_extrapolation_ratio
693
+ self.rope_enable_fps_modulation = rope_enable_fps_modulation
694
+
695
+ self.build_pos_embed(device=device, dtype=dtype)
696
+ self.use_adaln_lora = use_adaln_lora
697
+ self.adaln_lora_dim = adaln_lora_dim
698
+ self.t_embedder = nn.Sequential(
699
+ Timesteps(model_channels),
700
+ TimestepEmbedding(model_channels, model_channels, use_adaln_lora=use_adaln_lora, device=device, dtype=dtype, operations=operations,),
701
+ )
702
+
703
+ in_channels = in_channels + 1 if concat_padding_mask else in_channels
704
+ self.x_embedder = PatchEmbed(
705
+ spatial_patch_size=patch_spatial,
706
+ temporal_patch_size=patch_temporal,
707
+ in_channels=in_channels,
708
+ out_channels=model_channels,
709
+ device=device, dtype=dtype, operations=operations,
710
+ )
711
+
712
+ self.blocks = nn.ModuleList(
713
+ [
714
+ Block(
715
+ x_dim=model_channels,
716
+ context_dim=crossattn_emb_channels,
717
+ num_heads=num_heads,
718
+ mlp_ratio=mlp_ratio,
719
+ use_adaln_lora=use_adaln_lora,
720
+ adaln_lora_dim=adaln_lora_dim,
721
+ device=device, dtype=dtype, operations=operations,
722
+ )
723
+ for _ in range(num_blocks)
724
+ ]
725
+ )
726
+
727
+ self.final_layer = FinalLayer(
728
+ hidden_size=self.model_channels,
729
+ spatial_patch_size=self.patch_spatial,
730
+ temporal_patch_size=self.patch_temporal,
731
+ out_channels=self.out_channels,
732
+ use_adaln_lora=self.use_adaln_lora,
733
+ adaln_lora_dim=self.adaln_lora_dim,
734
+ device=device, dtype=dtype, operations=operations,
735
+ )
736
+
737
+ self.t_embedding_norm = operations.RMSNorm(model_channels, eps=1e-6, device=device, dtype=dtype)
738
+
739
+ def build_pos_embed(self, device=None, dtype=None) -> None:
740
+ if self.pos_emb_cls == "rope3d":
741
+ cls_type = VideoRopePosition3DEmb
742
+ else:
743
+ raise ValueError(f"Unknown pos_emb_cls {self.pos_emb_cls}")
744
+
745
+ logging.debug(f"Building positional embedding with {self.pos_emb_cls} class, impl {cls_type}")
746
+ kwargs = dict(
747
+ model_channels=self.model_channels,
748
+ len_h=self.max_img_h // self.patch_spatial,
749
+ len_w=self.max_img_w // self.patch_spatial,
750
+ len_t=self.max_frames // self.patch_temporal,
751
+ max_fps=self.max_fps,
752
+ min_fps=self.min_fps,
753
+ is_learnable=self.pos_emb_learnable,
754
+ interpolation=self.pos_emb_interpolation,
755
+ head_dim=self.model_channels // self.num_heads,
756
+ h_extrapolation_ratio=self.rope_h_extrapolation_ratio,
757
+ w_extrapolation_ratio=self.rope_w_extrapolation_ratio,
758
+ t_extrapolation_ratio=self.rope_t_extrapolation_ratio,
759
+ enable_fps_modulation=self.rope_enable_fps_modulation,
760
+ device=device,
761
+ )
762
+ self.pos_embedder = cls_type(
763
+ **kwargs, # type: ignore
764
+ )
765
+
766
+ if self.extra_per_block_abs_pos_emb:
767
+ kwargs["h_extrapolation_ratio"] = self.extra_h_extrapolation_ratio
768
+ kwargs["w_extrapolation_ratio"] = self.extra_w_extrapolation_ratio
769
+ kwargs["t_extrapolation_ratio"] = self.extra_t_extrapolation_ratio
770
+ kwargs["device"] = device
771
+ kwargs["dtype"] = dtype
772
+ self.extra_pos_embedder = LearnablePosEmbAxis(
773
+ **kwargs, # type: ignore
774
+ )
775
+
776
+ def prepare_embedded_sequence(
777
+ self,
778
+ x_B_C_T_H_W: torch.Tensor,
779
+ fps: Optional[torch.Tensor] = None,
780
+ padding_mask: Optional[torch.Tensor] = None,
781
+ ) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[torch.Tensor]]:
782
+ """
783
+ Prepares an embedded sequence tensor by applying positional embeddings and handling padding masks.
784
+
785
+ Args:
786
+ x_B_C_T_H_W (torch.Tensor): video
787
+ fps (Optional[torch.Tensor]): Frames per second tensor to be used for positional embedding when required.
788
+ If None, a default value (`self.base_fps`) will be used.
789
+ padding_mask (Optional[torch.Tensor]): current it is not used
790
+
791
+ Returns:
792
+ Tuple[torch.Tensor, Optional[torch.Tensor]]:
793
+ - A tensor of shape (B, T, H, W, D) with the embedded sequence.
794
+ - An optional positional embedding tensor, returned only if the positional embedding class
795
+ (`self.pos_emb_cls`) includes 'rope'. Otherwise, None.
796
+
797
+ Notes:
798
+ - If `self.concat_padding_mask` is True, a padding mask channel is concatenated to the input tensor.
799
+ - The method of applying positional embeddings depends on the value of `self.pos_emb_cls`.
800
+ - If 'rope' is in `self.pos_emb_cls` (case insensitive), the positional embeddings are generated using
801
+ the `self.pos_embedder` with the shape [T, H, W].
802
+ - If "fps_aware" is in `self.pos_emb_cls`, the positional embeddings are generated using the
803
+ `self.pos_embedder` with the fps tensor.
804
+ - Otherwise, the positional embeddings are generated without considering fps.
805
+ """
806
+ if self.concat_padding_mask:
807
+ if padding_mask is None:
808
+ padding_mask = torch.zeros(x_B_C_T_H_W.shape[0], 1, x_B_C_T_H_W.shape[3], x_B_C_T_H_W.shape[4], dtype=x_B_C_T_H_W.dtype, device=x_B_C_T_H_W.device)
809
+ else:
810
+ padding_mask = transforms.functional.resize(
811
+ padding_mask, list(x_B_C_T_H_W.shape[-2:]), interpolation=transforms.InterpolationMode.NEAREST
812
+ )
813
+ x_B_C_T_H_W = torch.cat(
814
+ [x_B_C_T_H_W, padding_mask.unsqueeze(1).repeat(1, 1, x_B_C_T_H_W.shape[2], 1, 1)], dim=1
815
+ )
816
+ x_B_T_H_W_D = self.x_embedder(x_B_C_T_H_W)
817
+
818
+ if self.extra_per_block_abs_pos_emb:
819
+ extra_pos_emb = self.extra_pos_embedder(x_B_T_H_W_D, fps=fps, device=x_B_C_T_H_W.device, dtype=x_B_C_T_H_W.dtype)
820
+ else:
821
+ extra_pos_emb = None
822
+
823
+ if "rope" in self.pos_emb_cls.lower():
824
+ return x_B_T_H_W_D, self.pos_embedder(x_B_T_H_W_D, fps=fps, device=x_B_C_T_H_W.device), extra_pos_emb
825
+ x_B_T_H_W_D = x_B_T_H_W_D + self.pos_embedder(x_B_T_H_W_D, device=x_B_C_T_H_W.device) # [B, T, H, W, D]
826
+
827
+ return x_B_T_H_W_D, None, extra_pos_emb
828
+
829
+ def unpatchify(self, x_B_T_H_W_M: torch.Tensor) -> torch.Tensor:
830
+ x_B_C_Tt_Hp_Wp = rearrange(
831
+ x_B_T_H_W_M,
832
+ "B T H W (p1 p2 t C) -> B C (T t) (H p1) (W p2)",
833
+ p1=self.patch_spatial,
834
+ p2=self.patch_spatial,
835
+ t=self.patch_temporal,
836
+ )
837
+ return x_B_C_Tt_Hp_Wp
838
+
839
+ def forward(self,
840
+ x: torch.Tensor,
841
+ timesteps: torch.Tensor,
842
+ context: torch.Tensor,
843
+ fps: Optional[torch.Tensor] = None,
844
+ padding_mask: Optional[torch.Tensor] = None,
845
+ **kwargs,
846
+ ):
847
+ return comfy.patcher_extension.WrapperExecutor.new_class_executor(
848
+ self._forward,
849
+ self,
850
+ comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, kwargs.get("transformer_options", {}))
851
+ ).execute(x, timesteps, context, fps, padding_mask, **kwargs)
852
+
853
+ def _forward(
854
+ self,
855
+ x: torch.Tensor,
856
+ timesteps: torch.Tensor,
857
+ context: torch.Tensor,
858
+ fps: Optional[torch.Tensor] = None,
859
+ padding_mask: Optional[torch.Tensor] = None,
860
+ **kwargs,
861
+ ):
862
+ orig_shape = list(x.shape)
863
+ x = comfy.ldm.common_dit.pad_to_patch_size(x, (self.patch_temporal, self.patch_spatial, self.patch_spatial))
864
+ x_B_C_T_H_W = x
865
+ timesteps_B_T = timesteps
866
+ crossattn_emb = context
867
+ """
868
+ Args:
869
+ x: (B, C, T, H, W) tensor of spatial-temp inputs
870
+ timesteps: (B, ) tensor of timesteps
871
+ crossattn_emb: (B, N, D) tensor of cross-attention embeddings
872
+ """
873
+ x_B_T_H_W_D, rope_emb_L_1_1_D, extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D = self.prepare_embedded_sequence(
874
+ x_B_C_T_H_W,
875
+ fps=fps,
876
+ padding_mask=padding_mask,
877
+ )
878
+
879
+ if timesteps_B_T.ndim == 1:
880
+ timesteps_B_T = timesteps_B_T.unsqueeze(1)
881
+ t_embedding_B_T_D, adaln_lora_B_T_3D = self.t_embedder[1](self.t_embedder[0](timesteps_B_T).to(x_B_T_H_W_D.dtype))
882
+ t_embedding_B_T_D = self.t_embedding_norm(t_embedding_B_T_D)
883
+
884
+ # for logging purpose
885
+ affline_scale_log_info = {}
886
+ affline_scale_log_info["t_embedding_B_T_D"] = t_embedding_B_T_D.detach()
887
+ self.affline_scale_log_info = affline_scale_log_info
888
+ self.affline_emb = t_embedding_B_T_D
889
+ self.crossattn_emb = crossattn_emb
890
+
891
+ if extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D is not None:
892
+ assert (
893
+ x_B_T_H_W_D.shape == extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D.shape
894
+ ), f"{x_B_T_H_W_D.shape} != {extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D.shape}"
895
+
896
+ transformer_options = kwargs.get("transformer_options", {})
897
+ patches = transformer_options.get("patches", {})
898
+ if "post_input" in patches:
899
+ transformer_options = transformer_options.copy()
900
+ transformer_options["model_patch_data"] = {}
901
+
902
+ if "post_input" in patches:
903
+ for patch in patches["post_input"]:
904
+ out = patch({"img": x_B_T_H_W_D, "x": x_B_C_T_H_W, "transformer_options": transformer_options})
905
+ x_B_T_H_W_D = out["img"]
906
+
907
+ block_kwargs = {
908
+ "rope_emb_L_1_1_D": rope_emb_L_1_1_D.unsqueeze(1).unsqueeze(0),
909
+ "adaln_lora_B_T_3D": adaln_lora_B_T_3D,
910
+ "extra_per_block_pos_emb": extra_pos_emb_B_T_H_W_D_or_T_H_W_B_D,
911
+ "transformer_options": transformer_options,
912
+ }
913
+
914
+ # The residual stream for this model has large values. To make fp16 compute_dtype work, we keep the residual stream
915
+ # in fp32, but run attention and MLP modules in fp16.
916
+ # An alternate method that clamps fp16 values "works" in the sense that it makes coherent images, but there is noticeable
917
+ # quality degradation and visual artifacts.
918
+ if x_B_T_H_W_D.dtype == torch.float16:
919
+ x_B_T_H_W_D = x_B_T_H_W_D.float()
920
+
921
+ for block_index, block in enumerate(self.blocks):
922
+ transformer_options["block_index"] = block_index
923
+ x_B_T_H_W_D = block(
924
+ x_B_T_H_W_D,
925
+ t_embedding_B_T_D,
926
+ crossattn_emb,
927
+ **block_kwargs,
928
+ )
929
+
930
+ x_B_T_H_W_O = self.final_layer(x_B_T_H_W_D.to(crossattn_emb.dtype), t_embedding_B_T_D, adaln_lora_B_T_3D=adaln_lora_B_T_3D)
931
+ x_B_C_Tt_Hp_Wp = self.unpatchify(x_B_T_H_W_O)[:, :, :orig_shape[-3], :orig_shape[-2], :orig_shape[-1]]
932
+ return x_B_C_Tt_Hp_Wp
ComfyUI/comfy/ldm/cosmos/vae.py ADDED
@@ -0,0 +1,131 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
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
+ """The causal continuous video tokenizer with VAE or AE formulation for 3D data.."""
16
+
17
+ import logging
18
+ import torch
19
+ from torch import nn
20
+ from enum import Enum
21
+ import math
22
+
23
+ from .cosmos_tokenizer.layers3d import (
24
+ EncoderFactorized,
25
+ DecoderFactorized,
26
+ CausalConv3d,
27
+ )
28
+
29
+
30
+ class IdentityDistribution(torch.nn.Module):
31
+ def __init__(self):
32
+ super().__init__()
33
+
34
+ def forward(self, parameters):
35
+ return parameters, (torch.tensor([0.0]), torch.tensor([0.0]))
36
+
37
+
38
+ class GaussianDistribution(torch.nn.Module):
39
+ def __init__(self, min_logvar: float = -30.0, max_logvar: float = 20.0):
40
+ super().__init__()
41
+ self.min_logvar = min_logvar
42
+ self.max_logvar = max_logvar
43
+
44
+ def sample(self, mean, logvar):
45
+ std = torch.exp(0.5 * logvar)
46
+ return mean + std * torch.randn_like(mean)
47
+
48
+ def forward(self, parameters):
49
+ mean, logvar = torch.chunk(parameters, 2, dim=1)
50
+ logvar = torch.clamp(logvar, self.min_logvar, self.max_logvar)
51
+ return self.sample(mean, logvar), (mean, logvar)
52
+
53
+
54
+ class ContinuousFormulation(Enum):
55
+ VAE = GaussianDistribution
56
+ AE = IdentityDistribution
57
+
58
+
59
+ class CausalContinuousVideoTokenizer(nn.Module):
60
+ def __init__(
61
+ self, z_channels: int, z_factor: int, latent_channels: int, **kwargs
62
+ ) -> None:
63
+ super().__init__()
64
+ self.name = kwargs.get("name", "CausalContinuousVideoTokenizer")
65
+ self.latent_channels = latent_channels
66
+ self.sigma_data = 0.5
67
+
68
+ # encoder_name = kwargs.get("encoder", Encoder3DType.BASE.name)
69
+ self.encoder = EncoderFactorized(
70
+ z_channels=z_factor * z_channels, **kwargs
71
+ )
72
+ if kwargs.get("temporal_compression", 4) == 4:
73
+ kwargs["channels_mult"] = [2, 4]
74
+ # decoder_name = kwargs.get("decoder", Decoder3DType.BASE.name)
75
+ self.decoder = DecoderFactorized(
76
+ z_channels=z_channels, **kwargs
77
+ )
78
+
79
+ self.quant_conv = CausalConv3d(
80
+ z_factor * z_channels,
81
+ z_factor * latent_channels,
82
+ kernel_size=1,
83
+ padding=0,
84
+ )
85
+ self.post_quant_conv = CausalConv3d(
86
+ latent_channels, z_channels, kernel_size=1, padding=0
87
+ )
88
+
89
+ # formulation_name = kwargs.get("formulation", ContinuousFormulation.AE.name)
90
+ self.distribution = IdentityDistribution() # ContinuousFormulation[formulation_name].value()
91
+
92
+ num_parameters = sum(param.numel() for param in self.parameters())
93
+ logging.debug(f"model={self.name}, num_parameters={num_parameters:,}")
94
+ logging.debug(
95
+ f"z_channels={z_channels}, latent_channels={self.latent_channels}."
96
+ )
97
+
98
+ latent_temporal_chunk = 16
99
+ self.latent_mean = nn.Parameter(torch.zeros([self.latent_channels * latent_temporal_chunk], dtype=torch.float32))
100
+ self.latent_std = nn.Parameter(torch.ones([self.latent_channels * latent_temporal_chunk], dtype=torch.float32))
101
+
102
+
103
+ def encode(self, x):
104
+ h = self.encoder(x)
105
+ moments = self.quant_conv(h)
106
+ z, posteriors = self.distribution(moments)
107
+ latent_ch = z.shape[1]
108
+ latent_t = z.shape[2]
109
+ in_dtype = z.dtype
110
+ mean = self.latent_mean.view(latent_ch, -1)
111
+ std = self.latent_std.view(latent_ch, -1)
112
+
113
+ mean = mean.repeat(1, math.ceil(latent_t / mean.shape[-1]))[:, : latent_t].reshape([1, latent_ch, -1, 1, 1]).to(dtype=in_dtype, device=z.device)
114
+ std = std.repeat(1, math.ceil(latent_t / std.shape[-1]))[:, : latent_t].reshape([1, latent_ch, -1, 1, 1]).to(dtype=in_dtype, device=z.device)
115
+ return ((z - mean) / std) * self.sigma_data
116
+
117
+ def decode(self, z):
118
+ in_dtype = z.dtype
119
+ latent_ch = z.shape[1]
120
+ latent_t = z.shape[2]
121
+ mean = self.latent_mean.view(latent_ch, -1)
122
+ std = self.latent_std.view(latent_ch, -1)
123
+
124
+ mean = mean.repeat(1, math.ceil(latent_t / mean.shape[-1]))[:, : latent_t].reshape([1, latent_ch, -1, 1, 1]).to(dtype=in_dtype, device=z.device)
125
+ std = std.repeat(1, math.ceil(latent_t / std.shape[-1]))[:, : latent_t].reshape([1, latent_ch, -1, 1, 1]).to(dtype=in_dtype, device=z.device)
126
+
127
+ z = z / self.sigma_data
128
+ z = z * std + mean
129
+ z = self.post_quant_conv(z)
130
+ return self.decoder(z)
131
+
ComfyUI/comfy/ldm/depth_anything_3/camera.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Camera-token encoder and decoder for Depth Anything 3."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ import torch.nn.functional as F
8
+
9
+ from comfy.ldm.modules.attention import optimized_attention_for_device
10
+ from .transform import affine_inverse, extri_intri_to_pose_encoding
11
+
12
+
13
+ # -----------------------------------------------------------------------
14
+ # Building blocks (mirror depth_anything_3.model.utils.{attention,block})
15
+ # -----------------------------------------------------------------------
16
+
17
+
18
+ class _Mlp(nn.Module):
19
+ """Standard 2-layer MLP with GELU. Matches upstream ``utils.attention.Mlp``."""
20
+
21
+ def __init__(self, in_features, hidden_features=None, out_features=None, *, device=None, dtype=None, operations=None):
22
+ super().__init__()
23
+ out_features = out_features or in_features
24
+ hidden_features = hidden_features or in_features
25
+ self.fc1 = operations.Linear(in_features, hidden_features, bias=True, device=device, dtype=dtype)
26
+ self.fc2 = operations.Linear(hidden_features, out_features, bias=True, device=device, dtype=dtype)
27
+
28
+ def forward(self, x):
29
+ return self.fc2(F.gelu(self.fc1(x)))
30
+
31
+
32
+ class _LayerScale(nn.Module):
33
+ """Per-channel learnable scaling. Matches upstream LayerScale."""
34
+
35
+ def __init__(self, dim, *, device=None, dtype=None):
36
+ super().__init__()
37
+ self.gamma = nn.Parameter(torch.empty(dim, device=device, dtype=dtype))
38
+
39
+ def forward(self, x):
40
+ return x * self.gamma.to(dtype=x.dtype, device=x.device)
41
+
42
+
43
+ class _Attention(nn.Module):
44
+ """ Self-attention with fused QKV projection. Mirrors upstream utils.attention.Attention;
45
+ Layout matches the HF safetensors (attn.qkv.{weight,bias} and attn.proj.{weight,bias})."""
46
+
47
+ def __init__(self, dim, num_heads, *, device=None, dtype=None, operations=None):
48
+ super().__init__()
49
+ assert dim % num_heads == 0
50
+ self.num_heads = num_heads
51
+ self.head_dim = dim // num_heads
52
+ self.qkv = operations.Linear(dim, dim * 3, bias=True, device=device, dtype=dtype)
53
+ self.proj = operations.Linear(dim, dim, bias=True, device=device, dtype=dtype)
54
+
55
+ def forward(self, x):
56
+ B, N, C = x.shape
57
+ qkv = self.qkv(x).reshape(B, N, 3, C)
58
+ q, k, v = qkv.unbind(2) # each (B, N, C)
59
+ attn_fn = optimized_attention_for_device(x.device, small_input=True)
60
+ out = attn_fn(q, k, v, heads=self.num_heads)
61
+ return self.proj(out)
62
+
63
+
64
+ class _Block(nn.Module):
65
+ """Pre-norm transformer block with LayerScale. Used by :class:CameraEnc. Layout follows upstream utils.block.Block."""
66
+
67
+ def __init__(self, dim, num_heads, mlp_ratio=4, init_values=0.01, *, device=None, dtype=None, operations=None):
68
+ super().__init__()
69
+ self.norm1 = operations.LayerNorm(dim, device=device, dtype=dtype)
70
+ self.attn = _Attention(dim, num_heads, device=device, dtype=dtype, operations=operations)
71
+ self.ls1 = _LayerScale(dim, device=device, dtype=dtype) if init_values else nn.Identity()
72
+ self.norm2 = operations.LayerNorm(dim, device=device, dtype=dtype)
73
+ self.mlp = _Mlp(in_features=dim, hidden_features=int(dim * mlp_ratio), device=device, dtype=dtype, operations=operations)
74
+ self.ls2 = _LayerScale(dim, device=device, dtype=dtype) if init_values else nn.Identity()
75
+
76
+ def forward(self, x):
77
+ x = x + self.ls1(self.attn(self.norm1(x)))
78
+ x = x + self.ls2(self.mlp(self.norm2(x)))
79
+ return x
80
+
81
+
82
+ class CameraEnc(nn.Module):
83
+ """Encode per-view (extrinsics, intrinsics) into a camera token.
84
+
85
+ Maps a 9-D pose-encoding vector through a small MLP up to the backbone's
86
+ ``embed_dim``, then runs ``trunk_depth`` transformer blocks. The output
87
+ has shape ``(B, S, embed_dim)`` and is injected at block ``alt_start``
88
+ of the DINOv2 backbone in place of the cls token.
89
+
90
+ Parameters mirror the upstream ``cam_enc.py`` so HF weights load directly.
91
+ """
92
+
93
+ def __init__(
94
+ self,
95
+ dim_out: int = 1024,
96
+ dim_in: int = 9,
97
+ trunk_depth: int = 4,
98
+ target_dim: int = 9,
99
+ num_heads: int = 16,
100
+ mlp_ratio: int = 4,
101
+ init_values: float = 0.01,
102
+ *,
103
+ device=None, dtype=None, operations=None,
104
+ **_kwargs,
105
+ ):
106
+ super().__init__()
107
+ self.target_dim = target_dim
108
+ self.trunk_depth = trunk_depth
109
+ self.trunk = nn.Sequential(*[
110
+ _Block(dim_out, num_heads=num_heads, mlp_ratio=mlp_ratio,
111
+ init_values=init_values,
112
+ device=device, dtype=dtype, operations=operations)
113
+ for _ in range(trunk_depth)
114
+ ])
115
+ self.token_norm = operations.LayerNorm(dim_out, device=device, dtype=dtype)
116
+ self.trunk_norm = operations.LayerNorm(dim_out, device=device, dtype=dtype)
117
+ self.pose_branch = _Mlp(
118
+ in_features=dim_in,
119
+ hidden_features=dim_out // 2,
120
+ out_features=dim_out,
121
+ device=device, dtype=dtype, operations=operations,
122
+ )
123
+
124
+ def forward(self, extrinsics: torch.Tensor, intrinsics: torch.Tensor,
125
+ image_size_hw) -> torch.Tensor:
126
+ """Encode camera parameters into ``(B, S, dim_out)`` tokens."""
127
+ c2ws = affine_inverse(extrinsics)
128
+ pose_encoding = extri_intri_to_pose_encoding(c2ws, intrinsics, image_size_hw)
129
+ tokens = self.pose_branch(pose_encoding.to(self.pose_branch.fc1.weight.dtype))
130
+ tokens = self.token_norm(tokens)
131
+ tokens = self.trunk(tokens)
132
+ tokens = self.trunk_norm(tokens)
133
+ return tokens
134
+
135
+
136
+ class CameraDec(nn.Module):
137
+ """Decode the final cam token into a 9-D pose encoding.
138
+
139
+ Output layout: ``[T(3), quat_xyzw(4), fov_h, fov_w]``. The translation is
140
+ always predicted by the network; the quaternion and FoV can either be
141
+ predicted or supplied via ``camera_encoding`` (used at training time
142
+ when GT cameras are available -- not exercised at inference here).
143
+
144
+ Parameters mirror the upstream ``cam_dec.py`` so HF weights load directly.
145
+ """
146
+
147
+ def __init__(self, dim_in: int = 1536,
148
+ *, device=None, dtype=None, operations=None, **_kwargs):
149
+ super().__init__()
150
+ d = dim_in
151
+ self.backbone = nn.Sequential(
152
+ operations.Linear(d, d, device=device, dtype=dtype),
153
+ nn.ReLU(),
154
+ operations.Linear(d, d, device=device, dtype=dtype),
155
+ nn.ReLU(),
156
+ )
157
+ self.fc_t = operations.Linear(d, 3, device=device, dtype=dtype)
158
+ self.fc_qvec = operations.Linear(d, 4, device=device, dtype=dtype)
159
+ self.fc_fov = nn.Sequential(
160
+ operations.Linear(d, 2, device=device, dtype=dtype),
161
+ nn.ReLU(),
162
+ )
163
+
164
+ def forward(self, feat: torch.Tensor,
165
+ camera_encoding: "torch.Tensor | None" = None) -> torch.Tensor:
166
+ """Decode ``(B, N, dim_in)`` cam tokens into ``(B, N, 9)`` pose enc."""
167
+ B, N = feat.shape[:2]
168
+ feat = feat.reshape(B * N, -1)
169
+ feat = self.backbone(feat)
170
+ out_t = self.fc_t(feat.float()).reshape(B, N, 3)
171
+ if camera_encoding is None:
172
+ out_qvec = self.fc_qvec(feat.float()).reshape(B, N, 4)
173
+ out_fov = self.fc_fov(feat.float()).reshape(B, N, 2)
174
+ else:
175
+ out_qvec = camera_encoding[..., 3:7]
176
+ out_fov = camera_encoding[..., -2:]
177
+ return torch.cat([out_t, out_qvec, out_fov], dim=-1)
ComfyUI/comfy/ldm/depth_anything_3/dpt.py ADDED
@@ -0,0 +1,489 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """DPT / DualDPT heads for Depth Anything 3."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import List, Optional, Sequence, Tuple
6
+
7
+ import torch
8
+ import torch.nn as nn
9
+ import torch.nn.functional as F
10
+
11
+
12
+ class Permute(nn.Module):
13
+ def __init__(self, dims: Tuple[int, ...]):
14
+ super().__init__()
15
+ self.dims = dims
16
+
17
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
18
+ return x.permute(*self.dims)
19
+
20
+
21
+ def _custom_interpolate(
22
+ x: torch.Tensor,
23
+ size: Optional[Tuple[int, int]] = None,
24
+ scale_factor: Optional[float] = None,
25
+ mode: str = "bilinear",
26
+ align_corners: bool = True,
27
+ ) -> torch.Tensor:
28
+ if size is None:
29
+ assert scale_factor is not None
30
+ size = (int(x.shape[-2] * scale_factor), int(x.shape[-1] * scale_factor))
31
+ INT_MAX = 1610612736
32
+ total = size[0] * size[1] * x.shape[0] * x.shape[1]
33
+ if total > INT_MAX:
34
+ chunks = torch.chunk(x, chunks=(total // INT_MAX) + 1, dim=0)
35
+ outs = [F.interpolate(c, size=size, mode=mode, align_corners=align_corners) for c in chunks]
36
+ return torch.cat(outs, dim=0).contiguous()
37
+ return F.interpolate(x, size=size, mode=mode, align_corners=align_corners)
38
+
39
+
40
+ def _create_uv_grid(width: int, height: int, aspect_ratio: float, dtype, device) -> torch.Tensor:
41
+ """Normalised UV grid spanning (-x_span, -y_span)..(x_span, y_span)."""
42
+ diag_factor = (aspect_ratio ** 2 + 1.0) ** 0.5
43
+ span_x = aspect_ratio / diag_factor
44
+ span_y = 1.0 / diag_factor
45
+ left_x = -span_x * (width - 1) / width
46
+ right_x = span_x * (width - 1) / width
47
+ top_y = -span_y * (height - 1) / height
48
+ bottom_y = span_y * (height - 1) / height
49
+ x_coords = torch.linspace(left_x, right_x, steps=width, dtype=dtype, device=device)
50
+ y_coords = torch.linspace(top_y, bottom_y, steps=height, dtype=dtype, device=device)
51
+ uu, vv = torch.meshgrid(x_coords, y_coords, indexing="xy")
52
+ return torch.stack((uu, vv), dim=-1) # (H, W, 2)
53
+
54
+
55
+ def _make_sincos_pos_embed(embed_dim: int, pos: torch.Tensor, omega_0: float = 100.0) -> torch.Tensor:
56
+ omega = torch.arange(embed_dim // 2, dtype=torch.float32, device=pos.device)
57
+ omega = 1.0 / omega_0 ** (omega / (embed_dim / 2.0))
58
+ pos = pos.reshape(-1)
59
+ out = torch.einsum("m,d->md", pos, omega)
60
+ return torch.cat([out.sin(), out.cos()], dim=1).float()
61
+
62
+
63
+ def _position_grid_to_embed(pos_grid: torch.Tensor, embed_dim: int, omega_0: float = 100.0) -> torch.Tensor:
64
+ H, W, _ = pos_grid.shape
65
+ pos_flat = pos_grid.reshape(-1, 2)
66
+ emb_x = _make_sincos_pos_embed(embed_dim // 2, pos_flat[:, 0], omega_0=omega_0)
67
+ emb_y = _make_sincos_pos_embed(embed_dim // 2, pos_flat[:, 1], omega_0=omega_0)
68
+ emb = torch.cat([emb_x, emb_y], dim=-1)
69
+ return emb.view(H, W, embed_dim)
70
+
71
+
72
+ def _add_pos_embed(x: torch.Tensor, W: int, H: int, ratio: float = 0.1) -> torch.Tensor:
73
+ """Stateless UV positional embedding added to a feature map (B, C, h, w)."""
74
+ pw, ph = x.shape[-1], x.shape[-2]
75
+ pe = _create_uv_grid(pw, ph, aspect_ratio=W / H, dtype=x.dtype, device=x.device)
76
+ pe = _position_grid_to_embed(pe, x.shape[1]) * ratio
77
+ pe = pe.permute(2, 0, 1)[None].expand(x.shape[0], -1, -1, -1).to(dtype=x.dtype)
78
+ return x + pe
79
+
80
+
81
+ def _apply_activation(x: torch.Tensor, activation: str) -> torch.Tensor:
82
+ act = (activation or "linear").lower()
83
+ if act == "exp":
84
+ return torch.exp(x)
85
+ if act == "expp1":
86
+ return torch.exp(x) + 1
87
+ if act == "expm1":
88
+ return torch.expm1(x)
89
+ if act == "relu":
90
+ return torch.relu(x)
91
+ if act == "sigmoid":
92
+ return torch.sigmoid(x)
93
+ if act == "softplus":
94
+ return F.softplus(x)
95
+ if act == "tanh":
96
+ return torch.tanh(x)
97
+ return x
98
+
99
+
100
+ # -----------------------------------------------------------------------------
101
+ # Fusion building blocks
102
+ # -----------------------------------------------------------------------------
103
+
104
+
105
+ class ResidualConvUnit(nn.Module):
106
+ def __init__(self, features: int, device=None, dtype=None, operations=None):
107
+ super().__init__()
108
+ self.conv1 = operations.Conv2d(features, features, 3, 1, 1, bias=True, device=device, dtype=dtype)
109
+ self.conv2 = operations.Conv2d(features, features, 3, 1, 1, bias=True, device=device, dtype=dtype)
110
+ self.activation = nn.ReLU(inplace=False)
111
+
112
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
113
+ out = self.activation(x)
114
+ out = self.conv1(out)
115
+ out = self.activation(out)
116
+ out = self.conv2(out)
117
+ return out + x
118
+
119
+
120
+ class FeatureFusionBlock(nn.Module):
121
+ def __init__(self, features: int, has_residual: bool = True, align_corners: bool = True, device=None, dtype=None, operations=None):
122
+ super().__init__()
123
+ self.align_corners = align_corners
124
+ self.has_residual = has_residual
125
+ if has_residual:
126
+ self.resConfUnit1 = ResidualConvUnit(features, device=device, dtype=dtype, operations=operations)
127
+ else:
128
+ self.resConfUnit1 = None
129
+ self.resConfUnit2 = ResidualConvUnit(features, device=device, dtype=dtype, operations=operations)
130
+ self.out_conv = operations.Conv2d(features, features, 1, 1, 0, bias=True, device=device, dtype=dtype)
131
+
132
+ def forward(self, *xs: torch.Tensor, size: Optional[Tuple[int, int]] = None) -> torch.Tensor:
133
+ y = xs[0]
134
+ if self.has_residual and len(xs) > 1 and self.resConfUnit1 is not None:
135
+ y = y + self.resConfUnit1(xs[1])
136
+ y = self.resConfUnit2(y)
137
+ if size is None:
138
+ up_kwargs = {"scale_factor": 2.0}
139
+ else:
140
+ up_kwargs = {"size": size}
141
+ y = _custom_interpolate(y, **up_kwargs, mode="bilinear", align_corners=self.align_corners)
142
+ y = self.out_conv(y)
143
+ return y
144
+
145
+
146
+ class _Scratch(nn.Module):
147
+ """Container that mirrors upstream ``scratch`` attribute layout."""
148
+
149
+
150
+ def _make_scratch(in_shape: List[int], out_shape: int, device=None, dtype=None, operations=None) -> _Scratch:
151
+ scratch = _Scratch()
152
+ scratch.layer1_rn = operations.Conv2d(in_shape[0], out_shape, 3, 1, 1, bias=False, device=device, dtype=dtype)
153
+ scratch.layer2_rn = operations.Conv2d(in_shape[1], out_shape, 3, 1, 1, bias=False, device=device, dtype=dtype)
154
+ scratch.layer3_rn = operations.Conv2d(in_shape[2], out_shape, 3, 1, 1, bias=False, device=device, dtype=dtype)
155
+ scratch.layer4_rn = operations.Conv2d(in_shape[3], out_shape, 3, 1, 1, bias=False, device=device, dtype=dtype)
156
+ return scratch
157
+
158
+
159
+ def _make_fusion_block(features: int, has_residual: bool = True, device=None, dtype=None, operations=None) -> FeatureFusionBlock:
160
+ return FeatureFusionBlock(features, has_residual=has_residual, align_corners=True, device=device, dtype=dtype, operations=operations)
161
+
162
+
163
+ # -----------------------------------------------------------------------------
164
+ # DPT (single head + optional sky head) -- used by DA3Mono/Metric
165
+ # -----------------------------------------------------------------------------
166
+
167
+
168
+ class DPT(nn.Module):
169
+ """Single-head DPT used by DA3Mono-Large and DA3Metric-Large."""
170
+
171
+ def __init__(
172
+ self,
173
+ dim_in: int,
174
+ patch_size: int = 14,
175
+ output_dim: int = 1,
176
+ activation: str = "exp",
177
+ conf_activation: str = "expp1",
178
+ features: int = 256,
179
+ out_channels: Sequence[int] = (256, 512, 1024, 1024),
180
+ pos_embed: bool = False,
181
+ down_ratio: int = 1,
182
+ head_name: str = "depth",
183
+ use_sky_head: bool = True,
184
+ sky_name: str = "sky",
185
+ sky_activation: str = "relu",
186
+ norm_type: str = "idt",
187
+ device=None, dtype=None, operations=None,
188
+ ):
189
+ super().__init__()
190
+ self.patch_size = patch_size
191
+ self.activation = activation
192
+ self.conf_activation = conf_activation
193
+ self.pos_embed = pos_embed
194
+ self.down_ratio = down_ratio
195
+ self.head_main = head_name
196
+ self.sky_name = sky_name
197
+ self.out_dim = output_dim
198
+ self.has_conf = output_dim > 1
199
+ self.use_sky_head = use_sky_head
200
+ self.sky_activation = sky_activation
201
+ self.intermediate_layer_idx: Tuple[int, int, int, int] = (0, 1, 2, 3)
202
+
203
+ if norm_type == "layer":
204
+ self.norm = operations.LayerNorm(dim_in, device=device, dtype=dtype)
205
+ else:
206
+ self.norm = nn.Identity()
207
+
208
+ out_channels = list(out_channels)
209
+ self.projects = nn.ModuleList([
210
+ operations.Conv2d(dim_in, oc, kernel_size=1, stride=1, padding=0, device=device, dtype=dtype)
211
+ for oc in out_channels
212
+ ])
213
+ self.resize_layers = nn.ModuleList([
214
+ operations.ConvTranspose2d(out_channels[0], out_channels[0], kernel_size=4, stride=4, padding=0, device=device, dtype=dtype),
215
+ operations.ConvTranspose2d(out_channels[1], out_channels[1], kernel_size=2, stride=2, padding=0, device=device, dtype=dtype),
216
+ nn.Identity(),
217
+ operations.Conv2d(out_channels[3], out_channels[3], kernel_size=3, stride=2, padding=1, device=device, dtype=dtype),
218
+ ])
219
+
220
+ self.scratch = _make_scratch(out_channels, features, device=device, dtype=dtype, operations=operations)
221
+ self.scratch.refinenet1 = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
222
+ self.scratch.refinenet2 = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
223
+ self.scratch.refinenet3 = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
224
+ self.scratch.refinenet4 = _make_fusion_block(features, has_residual=False, device=device, dtype=dtype, operations=operations)
225
+
226
+ head_features_1 = features
227
+ head_features_2 = 32
228
+ self.scratch.output_conv1 = operations.Conv2d(
229
+ head_features_1, head_features_1 // 2, kernel_size=3, stride=1, padding=1,
230
+ device=device, dtype=dtype,
231
+ )
232
+ self.scratch.output_conv2 = nn.Sequential(
233
+ operations.Conv2d(head_features_1 // 2, head_features_2, kernel_size=3, stride=1, padding=1, device=device, dtype=dtype),
234
+ nn.ReLU(inplace=False),
235
+ operations.Conv2d(head_features_2, output_dim, kernel_size=1, stride=1, padding=0, device=device, dtype=dtype),
236
+ )
237
+
238
+ if self.use_sky_head:
239
+ self.scratch.sky_output_conv2 = nn.Sequential(
240
+ operations.Conv2d(head_features_1 // 2, head_features_2, kernel_size=3, stride=1, padding=1, device=device, dtype=dtype),
241
+ nn.ReLU(inplace=False),
242
+ operations.Conv2d(head_features_2, 1, kernel_size=1, stride=1, padding=0, device=device, dtype=dtype),
243
+ )
244
+
245
+ def forward(self, feats: List[torch.Tensor], H: int, W: int, patch_start_idx: int = 0, **_kwargs) -> dict:
246
+ # feats[i][0] is the patch-token tensor with shape (B, S, N_patch, C)
247
+ B, S, N, C = feats[0][0].shape
248
+ feats_flat = [feat[0].reshape(B * S, N, C) for feat in feats]
249
+
250
+ ph, pw = H // self.patch_size, W // self.patch_size
251
+ resized = []
252
+ for stage_idx, take_idx in enumerate(self.intermediate_layer_idx):
253
+ x = feats_flat[take_idx][:, patch_start_idx:]
254
+ x = self.norm(x)
255
+ x = x.permute(0, 2, 1).contiguous().reshape(B * S, C, ph, pw)
256
+ x = self.projects[stage_idx](x)
257
+ if self.pos_embed:
258
+ x = _add_pos_embed(x, W, H)
259
+ x = self.resize_layers[stage_idx](x)
260
+ resized.append(x)
261
+
262
+ l1_rn = self.scratch.layer1_rn(resized[0])
263
+ l2_rn = self.scratch.layer2_rn(resized[1])
264
+ l3_rn = self.scratch.layer3_rn(resized[2])
265
+ l4_rn = self.scratch.layer4_rn(resized[3])
266
+
267
+ out = self.scratch.refinenet4(l4_rn, size=l3_rn.shape[2:])
268
+ out = self.scratch.refinenet3(out, l3_rn, size=l2_rn.shape[2:])
269
+ out = self.scratch.refinenet2(out, l2_rn, size=l1_rn.shape[2:])
270
+ out = self.scratch.refinenet1(out, l1_rn)
271
+
272
+ h_out = int(ph * self.patch_size / self.down_ratio)
273
+ w_out = int(pw * self.patch_size / self.down_ratio)
274
+
275
+ fused = self.scratch.output_conv1(out)
276
+ fused = _custom_interpolate(fused, (h_out, w_out), mode="bilinear", align_corners=True)
277
+ if self.pos_embed:
278
+ fused = _add_pos_embed(fused, W, H)
279
+ feat = fused
280
+
281
+ main_logits = self.scratch.output_conv2(feat)
282
+ outs = {}
283
+ if self.has_conf:
284
+ fmap = main_logits.permute(0, 2, 3, 1)
285
+ pred = _apply_activation(fmap[..., :-1], self.activation)
286
+ conf = _apply_activation(fmap[..., -1], self.conf_activation)
287
+ outs[self.head_main] = pred.squeeze(-1).view(B, S, *pred.shape[1:-1])
288
+ outs[f"{self.head_main}_conf"] = conf.view(B, S, *conf.shape[1:])
289
+ else:
290
+ pred = _apply_activation(main_logits, self.activation)
291
+ outs[self.head_main] = pred.squeeze(1).view(B, S, *pred.shape[2:])
292
+
293
+ if self.use_sky_head:
294
+ sky_logits = self.scratch.sky_output_conv2(feat)
295
+ if self.sky_activation.lower() == "sigmoid":
296
+ sky = torch.sigmoid(sky_logits)
297
+ elif self.sky_activation.lower() == "relu":
298
+ sky = F.relu(sky_logits)
299
+ else:
300
+ sky = sky_logits
301
+ outs[self.sky_name] = sky.squeeze(1).view(B, S, *sky.shape[2:])
302
+
303
+ return outs
304
+
305
+
306
+ # -----------------------------------------------------------------------------
307
+ # DualDPT (depth + auxiliary "ray" head) -- used by DA3-Small / DA3-Base
308
+ # -----------------------------------------------------------------------------
309
+
310
+
311
+ class DualDPT(nn.Module):
312
+ """Two-head DPT used by DA3-Small / DA3-Base."""
313
+
314
+ def __init__(
315
+ self,
316
+ dim_in: int,
317
+ patch_size: int = 14,
318
+ output_dim: int = 2,
319
+ activation: str = "exp",
320
+ conf_activation: str = "expp1",
321
+ features: int = 256,
322
+ out_channels: Sequence[int] = (256, 512, 1024, 1024),
323
+ pos_embed: bool = True,
324
+ down_ratio: int = 1,
325
+ aux_pyramid_levels: int = 4,
326
+ aux_out1_conv_num: int = 5,
327
+ head_names: Tuple[str, str] = ("depth", "ray"),
328
+ device=None, dtype=None, operations=None,
329
+ ):
330
+ super().__init__()
331
+ self.patch_size = patch_size
332
+ self.activation = activation
333
+ self.conf_activation = conf_activation
334
+ self.pos_embed = pos_embed
335
+ self.down_ratio = down_ratio
336
+ self.aux_levels = aux_pyramid_levels
337
+ self.aux_out1_conv_num = aux_out1_conv_num
338
+ self.head_main, self.head_aux = head_names
339
+ self.intermediate_layer_idx: Tuple[int, int, int, int] = (0, 1, 2, 3)
340
+ # Toggle the auxiliary ray branch at runtime. Default off (mono path).
341
+ # DepthAnything3Net flips this on when running multi-view + ray-pose.
342
+ self.enable_aux: bool = False
343
+
344
+ self.norm = operations.LayerNorm(dim_in, device=device, dtype=dtype)
345
+ out_channels = list(out_channels)
346
+ self.projects = nn.ModuleList([
347
+ operations.Conv2d(dim_in, oc, kernel_size=1, stride=1, padding=0, device=device, dtype=dtype)
348
+ for oc in out_channels
349
+ ])
350
+ self.resize_layers = nn.ModuleList([
351
+ operations.ConvTranspose2d(out_channels[0], out_channels[0], kernel_size=4, stride=4, padding=0, device=device, dtype=dtype),
352
+ operations.ConvTranspose2d(out_channels[1], out_channels[1], kernel_size=2, stride=2, padding=0, device=device, dtype=dtype),
353
+ nn.Identity(),
354
+ operations.Conv2d(out_channels[3], out_channels[3], kernel_size=3, stride=2, padding=1, device=device, dtype=dtype),
355
+ ])
356
+
357
+ self.scratch = _make_scratch(out_channels, features, device=device, dtype=dtype, operations=operations)
358
+ # Main fusion chain
359
+ self.scratch.refinenet1 = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
360
+ self.scratch.refinenet2 = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
361
+ self.scratch.refinenet3 = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
362
+ self.scratch.refinenet4 = _make_fusion_block(features, has_residual=False, device=device, dtype=dtype, operations=operations)
363
+ # Auxiliary fusion chain (separate copies)
364
+ self.scratch.refinenet1_aux = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
365
+ self.scratch.refinenet2_aux = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
366
+ self.scratch.refinenet3_aux = _make_fusion_block(features, device=device, dtype=dtype, operations=operations)
367
+ self.scratch.refinenet4_aux = _make_fusion_block(features, has_residual=False, device=device, dtype=dtype, operations=operations)
368
+
369
+ head_features_1 = features
370
+ head_features_2 = 32
371
+
372
+ # Main head neck + final projection
373
+ self.scratch.output_conv1 = operations.Conv2d(
374
+ head_features_1, head_features_1 // 2, kernel_size=3, stride=1, padding=1,
375
+ device=device, dtype=dtype,
376
+ )
377
+ self.scratch.output_conv2 = nn.Sequential(
378
+ operations.Conv2d(head_features_1 // 2, head_features_2, kernel_size=3, stride=1, padding=1, device=device, dtype=dtype),
379
+ nn.ReLU(inplace=False),
380
+ operations.Conv2d(head_features_2, output_dim, kernel_size=1, stride=1, padding=0, device=device, dtype=dtype),
381
+ )
382
+
383
+ # Aux pre-head per level (multi-level pyramid)
384
+ self.scratch.output_conv1_aux = nn.ModuleList([
385
+ self._make_aux_out1_block(head_features_1, device=device, dtype=dtype, operations=operations)
386
+ for _ in range(self.aux_levels)
387
+ ])
388
+
389
+ # Aux final projection per level (includes LayerNorm permute path).
390
+ ln_seq = [Permute((0, 2, 3, 1)),
391
+ operations.LayerNorm(head_features_2, device=device, dtype=dtype),
392
+ Permute((0, 3, 1, 2))]
393
+ self.scratch.output_conv2_aux = nn.ModuleList([
394
+ nn.Sequential(
395
+ operations.Conv2d(head_features_1 // 2, head_features_2, kernel_size=3, stride=1, padding=1, device=device, dtype=dtype),
396
+ *ln_seq,
397
+ nn.ReLU(inplace=False),
398
+ operations.Conv2d(head_features_2, 7, kernel_size=1, stride=1, padding=0, device=device, dtype=dtype),
399
+ )
400
+ for _ in range(self.aux_levels)
401
+ ])
402
+
403
+ @staticmethod
404
+ def _make_aux_out1_block(in_ch: int, *, device=None, dtype=None, operations=None) -> nn.Sequential:
405
+ # aux_out1_conv_num=5 in all Apache-2.0 variants.
406
+ return nn.Sequential(
407
+ operations.Conv2d(in_ch, in_ch // 2, 3, 1, 1, device=device, dtype=dtype),
408
+ operations.Conv2d(in_ch // 2, in_ch, 3, 1, 1, device=device, dtype=dtype),
409
+ operations.Conv2d(in_ch, in_ch // 2, 3, 1, 1, device=device, dtype=dtype),
410
+ operations.Conv2d(in_ch // 2, in_ch, 3, 1, 1, device=device, dtype=dtype),
411
+ operations.Conv2d(in_ch, in_ch // 2, 3, 1, 1, device=device, dtype=dtype),
412
+ )
413
+
414
+ def forward(self, feats: List[torch.Tensor], H: int, W: int, patch_start_idx: int = 0, **_kwargs) -> dict:
415
+ B, S, N, C = feats[0][0].shape
416
+ feats_flat = [feat[0].reshape(B * S, N, C) for feat in feats]
417
+
418
+ ph, pw = H // self.patch_size, W // self.patch_size
419
+ resized = []
420
+ for stage_idx, take_idx in enumerate(self.intermediate_layer_idx):
421
+ x = feats_flat[take_idx][:, patch_start_idx:]
422
+ x = self.norm(x)
423
+ x = x.permute(0, 2, 1).contiguous().reshape(B * S, C, ph, pw)
424
+ x = self.projects[stage_idx](x)
425
+ if self.pos_embed:
426
+ x = _add_pos_embed(x, W, H)
427
+ x = self.resize_layers[stage_idx](x)
428
+ resized.append(x)
429
+
430
+ l1_rn = self.scratch.layer1_rn(resized[0])
431
+ l2_rn = self.scratch.layer2_rn(resized[1])
432
+ l3_rn = self.scratch.layer3_rn(resized[2])
433
+ l4_rn = self.scratch.layer4_rn(resized[3])
434
+
435
+ # Main pyramid (output_conv1 is applied inside the upstream `_fuse`,
436
+ # before interpolation -- replicate that order here).
437
+ m = self.scratch.refinenet4(l4_rn, size=l3_rn.shape[2:])
438
+ if self.enable_aux:
439
+ a4 = self.scratch.refinenet4_aux(l4_rn, size=l3_rn.shape[2:])
440
+ aux_pyr = [a4]
441
+ m = self.scratch.refinenet3(m, l3_rn, size=l2_rn.shape[2:])
442
+ if self.enable_aux:
443
+ aux_pyr.append(self.scratch.refinenet3_aux(aux_pyr[-1], l3_rn, size=l2_rn.shape[2:]))
444
+ m = self.scratch.refinenet2(m, l2_rn, size=l1_rn.shape[2:])
445
+ if self.enable_aux:
446
+ aux_pyr.append(self.scratch.refinenet2_aux(aux_pyr[-1], l2_rn, size=l1_rn.shape[2:]))
447
+ m = self.scratch.refinenet1(m, l1_rn)
448
+ if self.enable_aux:
449
+ aux_pyr.append(self.scratch.refinenet1_aux(aux_pyr[-1], l1_rn))
450
+ m = self.scratch.output_conv1(m)
451
+
452
+ h_out = int(ph * self.patch_size / self.down_ratio)
453
+ w_out = int(pw * self.patch_size / self.down_ratio)
454
+
455
+ m = _custom_interpolate(m, (h_out, w_out), mode="bilinear", align_corners=True)
456
+ if self.pos_embed:
457
+ m = _add_pos_embed(m, W, H)
458
+ main_logits = self.scratch.output_conv2(m)
459
+ fmap = main_logits.permute(0, 2, 3, 1)
460
+ depth_pred = _apply_activation(fmap[..., :-1], self.activation)
461
+ depth_conf = _apply_activation(fmap[..., -1], self.conf_activation)
462
+
463
+ outs = {
464
+ self.head_main: depth_pred.squeeze(-1).view(B, S, *depth_pred.shape[1:-1]),
465
+ f"{self.head_main}_conf": depth_conf.view(B, S, *depth_conf.shape[1:]),
466
+ }
467
+
468
+ if self.enable_aux:
469
+ # Auxiliary "ray" head (multi-level inside) -- only the last level
470
+ # is returned. Mirrors upstream ``DualDPT._fuse`` + ``_forward_impl``:
471
+ # each aux pyramid level goes through ``output_conv1_aux[i]``
472
+ # (5-layer conv stack that ends at ``features // 2`` channels),
473
+ # then the last level optionally gets a pos-embed and finally
474
+ # ``output_conv2_aux[-1]``.
475
+ aux_processed = [
476
+ self.scratch.output_conv1_aux[i](a) for i, a in enumerate(aux_pyr)
477
+ ]
478
+ last_aux = aux_processed[-1]
479
+ if self.pos_embed:
480
+ last_aux = _add_pos_embed(last_aux, W, H)
481
+ last_aux_logits = self.scratch.output_conv2_aux[-1](last_aux)
482
+ fmap_last = last_aux_logits.permute(0, 2, 3, 1)
483
+ # Channels: [ray(6), ray_conf(1)]; ray uses 'linear' activation.
484
+ aux_pred = fmap_last[..., :-1]
485
+ aux_conf = _apply_activation(fmap_last[..., -1], self.conf_activation)
486
+ outs[self.head_aux] = aux_pred.view(B, S, *aux_pred.shape[1:])
487
+ outs[f"{self.head_aux}_conf"] = aux_conf.view(B, S, *aux_conf.shape[1:])
488
+
489
+ return outs
ComfyUI/comfy/ldm/depth_anything_3/model.py ADDED
@@ -0,0 +1,236 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from typing import Dict, Optional, Sequence
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+
8
+ from comfy.image_encoders.dino2 import Dinov2Model
9
+
10
+ from .camera import CameraDec, CameraEnc
11
+ from .dpt import DPT, DualDPT
12
+ from .ray_pose import get_extrinsic_from_camray
13
+ from .transform import affine_inverse, pose_encoding_to_extri_intri
14
+
15
+
16
+ _HEAD_REGISTRY = {
17
+ "dpt": DPT,
18
+ "dualdpt": DualDPT,
19
+ }
20
+
21
+
22
+ # Backbone presets (mirror the upstream DINOv2 ViT variants).
23
+ _BACKBONE_PRESETS = {
24
+ "vits": dict(hidden_size=384, num_hidden_layers=12, num_attention_heads=6, use_swiglu_ffn=False),
25
+ "vitb": dict(hidden_size=768, num_hidden_layers=12, num_attention_heads=12, use_swiglu_ffn=False),
26
+ "vitl": dict(hidden_size=1024, num_hidden_layers=24, num_attention_heads=16, use_swiglu_ffn=False),
27
+ "vitg": dict(hidden_size=1536, num_hidden_layers=40, num_attention_heads=24, use_swiglu_ffn=True),
28
+ }
29
+
30
+
31
+ def _build_backbone_config(
32
+ backbone_name: str,
33
+ *,
34
+ alt_start: int,
35
+ qknorm_start: int,
36
+ rope_start: int,
37
+ cat_token: bool,
38
+ ) -> dict:
39
+ if backbone_name not in _BACKBONE_PRESETS:
40
+ raise ValueError(f"Unknown DINOv2 backbone variant: {backbone_name!r}")
41
+ cfg = dict(_BACKBONE_PRESETS[backbone_name])
42
+ cfg.update(dict(
43
+ layer_norm_eps=1e-6,
44
+ patch_size=14,
45
+ image_size=518,
46
+ # No mask_token in DA3 weights; omit param to avoid load warnings.
47
+ use_mask_token=False,
48
+ alt_start=alt_start,
49
+ qknorm_start=qknorm_start,
50
+ rope_start=rope_start,
51
+ cat_token=cat_token,
52
+ rope_freq=100.0,
53
+ ))
54
+ return cfg
55
+
56
+
57
+ class DepthAnything3Net(nn.Module):
58
+
59
+ PATCH_SIZE = 14
60
+
61
+ def __init__(
62
+ self,
63
+ # --- Backbone ---
64
+ backbone_name: str = "vitl",
65
+ out_layers: Sequence[int] = (4, 11, 17, 23),
66
+ alt_start: int = -1,
67
+ qknorm_start: int = -1,
68
+ rope_start: int = -1,
69
+ cat_token: bool = False,
70
+ # --- Head ---
71
+ head_type: str = "dpt", # dpt or dualdpt
72
+ head_dim_in: int = 1024,
73
+ head_output_dim: int = 1, # 1 = depth only, 2 = depth+conf
74
+ head_features: int = 256,
75
+ head_out_channels: Sequence[int] = (256, 512, 1024, 1024),
76
+ head_use_sky_head: bool = True, # ignored by DualDPT
77
+ head_pos_embed: Optional[bool] = None, # default: True for DualDPT, False for DPT
78
+ # --- Camera (multi-view) ---
79
+ has_cam_enc: bool = False,
80
+ has_cam_dec: bool = False,
81
+ cam_dim_out: Optional[int] = None, # CameraEnc dim_out (defaults to embed_dim)
82
+ cam_dec_dim_in: Optional[int] = None, # CameraDec dim_in (defaults to 2*embed_dim with cat_token)
83
+ # ComfyUI plumbing
84
+ device=None, dtype=None, operations=None,
85
+ **_ignored,
86
+ ):
87
+ super().__init__()
88
+ head_cls = _HEAD_REGISTRY[head_type.lower()]
89
+ self.head_type = head_type.lower()
90
+ self.has_sky = (self.head_type == "dpt") and head_use_sky_head
91
+ self.has_conf = head_output_dim > 1
92
+ self.out_layers = list(out_layers)
93
+
94
+ backbone_cfg = _build_backbone_config(
95
+ backbone_name,
96
+ alt_start=alt_start,
97
+ qknorm_start=qknorm_start,
98
+ rope_start=rope_start,
99
+ cat_token=cat_token,
100
+ )
101
+ self.backbone = Dinov2Model(backbone_cfg, dtype, device, operations)
102
+
103
+ head_kwargs = dict(
104
+ dim_in=head_dim_in,
105
+ patch_size=self.PATCH_SIZE,
106
+ output_dim=head_output_dim,
107
+ features=head_features,
108
+ out_channels=tuple(head_out_channels),
109
+ device=device, dtype=dtype, operations=operations,
110
+ )
111
+ if self.head_type == "dpt":
112
+ head_kwargs.update(
113
+ use_sky_head=head_use_sky_head,
114
+ pos_embed=(False if head_pos_embed is None else head_pos_embed),
115
+ )
116
+ else: # dualdpt
117
+ head_kwargs.update(
118
+ pos_embed=(True if head_pos_embed is None else head_pos_embed),
119
+ )
120
+ self.head = head_cls(**head_kwargs)
121
+
122
+ # Built only if checkpoint has weights; cam_enc output dim == embed_dim.
123
+ embed_dim = backbone_cfg["hidden_size"]
124
+ if has_cam_enc:
125
+ self.cam_enc = CameraEnc(
126
+ dim_out=cam_dim_out if cam_dim_out is not None else embed_dim,
127
+ num_heads=max(1, embed_dim // 64),
128
+ device=device, dtype=dtype, operations=operations,
129
+ )
130
+ else:
131
+ self.cam_enc = None
132
+ if has_cam_dec:
133
+ default_dim = embed_dim * (2 if cat_token else 1)
134
+ self.cam_dec = CameraDec(
135
+ dim_in=cam_dec_dim_in if cam_dec_dim_in is not None else default_dim,
136
+ device=device, dtype=dtype, operations=operations,
137
+ )
138
+ else:
139
+ self.cam_dec = None
140
+
141
+ self.dtype = dtype
142
+
143
+ def forward(
144
+ self,
145
+ image: torch.Tensor,
146
+ extrinsics: Optional[torch.Tensor] = None,
147
+ intrinsics: Optional[torch.Tensor] = None,
148
+ *,
149
+ use_ray_pose: bool = False,
150
+ ref_view_strategy: str = "saddle_balanced",
151
+ export_feat_layers: Optional[Sequence[int]] = None,
152
+ **_unused,
153
+ ) -> Dict[str, torch.Tensor]:
154
+ """Run depth and optionally pose prediction."""
155
+ if image.ndim == 4:
156
+ image = image.unsqueeze(1) # (B, 1, 3, H, W)
157
+ assert image.ndim == 5 and image.shape[2] == 3, \
158
+ f"image must be (B,3,H,W) or (B,S,3,H,W); got {tuple(image.shape)}"
159
+
160
+ B, S, _, H, W = image.shape
161
+ assert H % self.PATCH_SIZE == 0 and W % self.PATCH_SIZE == 0, \
162
+ f"image H,W must be multiples of {self.PATCH_SIZE}; got {(H, W)}"
163
+
164
+ # Camera-token preparation (multi-view path).
165
+ cam_token = None
166
+ if extrinsics is not None and intrinsics is not None and self.cam_enc is not None:
167
+ cam_token = self.cam_enc(extrinsics, intrinsics, (H, W))
168
+
169
+ # Toggle aux ray output on/off depending on what the caller asked for.
170
+ if isinstance(self.head, DualDPT):
171
+ self.head.enable_aux = bool(use_ray_pose)
172
+
173
+ feats, aux_feats = self.backbone.get_intermediate_layers_da3(
174
+ image, self.out_layers, cam_token=cam_token,
175
+ ref_view_strategy=ref_view_strategy,
176
+ export_feat_layers=export_feat_layers,
177
+ )
178
+ head_out = self.head(feats, H=H, W=W, patch_start_idx=0)
179
+
180
+ # Pose prediction.
181
+ out: Dict[str, torch.Tensor] = {}
182
+ if use_ray_pose and "ray" in head_out and "ray_conf" in head_out:
183
+ ray = head_out["ray"]
184
+ ray_conf = head_out["ray_conf"]
185
+ extr_c2w, focal, pp = get_extrinsic_from_camray(
186
+ ray, ray_conf, ray.shape[-3], ray.shape[-2],
187
+ )
188
+ # Match the upstream output: w2c, drop the homogeneous row.
189
+ extr_w2c = affine_inverse(extr_c2w)[:, :, :3, :]
190
+ # Build pixel-space intrinsics from the normalised focal/pp output.
191
+ intr = torch.eye(3, device=ray.device, dtype=ray.dtype)
192
+ intr = intr[None, None].expand(extr_c2w.shape[0], extr_c2w.shape[1], 3, 3).clone()
193
+ intr[:, :, 0, 0] = focal[:, :, 0] / 2 * W
194
+ intr[:, :, 1, 1] = focal[:, :, 1] / 2 * H
195
+ intr[:, :, 0, 2] = pp[:, :, 0] * W * 0.5
196
+ intr[:, :, 1, 2] = pp[:, :, 1] * H * 0.5
197
+ out["extrinsics"] = extr_w2c
198
+ out["intrinsics"] = intr
199
+ elif self.cam_dec is not None and S > 1:
200
+ # Decode the cam-token of the final out_layer into a pose encoding.
201
+ cam_feat = feats[-1][1] # (B, S, dim_in_to_cam_dec)
202
+ pose_enc = self.cam_dec(cam_feat)
203
+ c2w_3x4, intr = pose_encoding_to_extri_intri(pose_enc, (H, W))
204
+ # Match the upstream output convention: w2c (world->camera), 3x4.
205
+ c2w_4x4 = torch.cat([
206
+ c2w_3x4,
207
+ torch.tensor([0, 0, 0, 1], device=c2w_3x4.device, dtype=c2w_3x4.dtype)
208
+ .view(1, 1, 1, 4).expand(B, S, 1, 4),
209
+ ], dim=-2)
210
+ out["extrinsics"] = affine_inverse(c2w_4x4)[:, :, :3, :]
211
+ out["intrinsics"] = intr
212
+
213
+ # Flatten the views axis for per-pixel outputs (depth/conf/sky) so the
214
+ # per-image consumer keeps its (B*S, H, W) interface.
215
+ for k, v in head_out.items():
216
+ if k in ("ray", "ray_conf"):
217
+ # Keep multi-view shape for downstream pose work.
218
+ out[k] = v
219
+ elif v.ndim >= 3 and v.shape[0] == B and v.shape[1] == S:
220
+ out[k] = v.reshape(B * S, *v.shape[2:])
221
+ else:
222
+ out[k] = v
223
+
224
+ if export_feat_layers:
225
+ out["aux_features"] = self._reshape_aux_features(aux_feats, H, W)
226
+ return out
227
+
228
+ def _reshape_aux_features(self, aux_feats, H: int, W: int):
229
+ """Reshape (B, S, N, C) aux features into (B, S, h_p, w_p, C)."""
230
+ ph, pw = H // self.PATCH_SIZE, W // self.PATCH_SIZE
231
+ out = []
232
+ for f in aux_feats:
233
+ B, S, N, C = f.shape
234
+ assert N == ph * pw, f"aux feature seq mismatch: {N} != {ph}*{pw}"
235
+ out.append(f.reshape(B, S, ph, pw, C))
236
+ return out
ComfyUI/comfy/ldm/depth_anything_3/preprocess.py ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Input/output preprocessing helpers for Depth Anything 3."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Tuple
6
+
7
+ import torch
8
+
9
+ import comfy.utils
10
+
11
+ PATCH_SIZE = 14
12
+
13
+ # ImageNet normalization constants used during DA3 training.
14
+ _IMAGENET_MEAN = torch.tensor([0.485, 0.456, 0.406])
15
+ _IMAGENET_STD = torch.tensor([0.229, 0.224, 0.225])
16
+
17
+
18
+ def _round_to_patch(x: int, patch: int = PATCH_SIZE) -> int:
19
+ down = (x // patch) * patch
20
+ up = down + patch
21
+ return up if abs(up - x) <= abs(x - down) else down
22
+
23
+
24
+ def compute_target_size(orig_h: int, orig_w: int, process_res: int, method: str = "upper_bound_resize") -> Tuple[int, int]:
25
+ """Compute (target_h, target_w) for a single image.
26
+ upper_bound_resize: scale longest side to process_res, then round each dim to nearest multiple of 14 (default upstream method).
27
+ lower_bound_resize: scale shortest side to process_res, then round."""
28
+
29
+ if method == "upper_bound_resize":
30
+ longest = max(orig_h, orig_w)
31
+ scale = process_res / float(longest)
32
+ elif method == "lower_bound_resize":
33
+ shortest = min(orig_h, orig_w)
34
+ scale = process_res / float(shortest)
35
+ else:
36
+ raise ValueError(f"Unsupported process_res_method: {method}")
37
+
38
+ new_w = max(1, _round_to_patch(int(round(orig_w * scale))))
39
+ new_h = max(1, _round_to_patch(int(round(orig_h * scale))))
40
+ return new_h, new_w
41
+
42
+
43
+ def preprocess_image(image: torch.Tensor, process_res: int = 504, method: str = "upper_bound_resize") -> torch.Tensor:
44
+ assert image.ndim == 4 and image.shape[-1] == 3, f"expected (B,H,W,3) IMAGE; got {tuple(image.shape)}"
45
+ B, H, W, _ = image.shape
46
+ target_h, target_w = compute_target_size(H, W, process_res, method)
47
+
48
+ # (B, H, W, 3) -> (B, 3, H, W)
49
+ x = image.movedim(-1, 1).contiguous()
50
+ if (target_h, target_w) != (H, W):
51
+ # Upstream uses cv2 INTER_CUBIC (upscale) / INTER_AREA (downscale).
52
+ # Lanczos in ``common_upscale`` is anti-aliased and produces the
53
+ # closest pixel-wise match in a sweep across {bilinear, bicubic,
54
+ # area, lanczos, bislerp}. Used in both directions for simplicity.
55
+ x = comfy.utils.common_upscale(x.float(), target_w, target_h, "lanczos", "disabled",)
56
+ x = x.clamp(0.0, 1.0)
57
+
58
+ mean = _IMAGENET_MEAN.to(device=x.device, dtype=x.dtype).view(1, 3, 1, 1)
59
+ std = _IMAGENET_STD.to(device=x.device, dtype=x.dtype).view(1, 3, 1, 1)
60
+ x = (x - mean) / std
61
+ return x
62
+
63
+
64
+ # -----------------------------------------------------------------------------
65
+ # Output post-processing (sky-aware clipping for Mono/Metric variants)
66
+ # -----------------------------------------------------------------------------
67
+
68
+
69
+ def compute_non_sky_mask(sky_prediction: torch.Tensor, threshold: float = 0.3) -> torch.Tensor:
70
+ """Boolean mask: True for non-sky pixels (sky probability < threshold)."""
71
+ return sky_prediction < threshold
72
+
73
+
74
+ def apply_sky_aware_clip(depth: torch.Tensor, sky: torch.Tensor, threshold: float = 0.3, quantile: float = 0.99) -> torch.Tensor:
75
+ """Clips sky regions to the 99th percentile of non-sky depth. Returns a new depth tensor."""
76
+ non_sky = compute_non_sky_mask(sky, threshold=threshold)
77
+ if non_sky.sum() <= 10 or (~non_sky).sum() <= 10:
78
+ return depth.clone()
79
+
80
+ non_sky_depth = depth[non_sky]
81
+ if non_sky_depth.numel() > 100_000:
82
+ idx = torch.randint(0, non_sky_depth.numel(), (100_000,), device=non_sky_depth.device)
83
+ sampled = non_sky_depth[idx]
84
+ else:
85
+ sampled = non_sky_depth
86
+
87
+ max_depth = torch.quantile(sampled, quantile)
88
+ out = depth.clone()
89
+ out[~non_sky] = max_depth
90
+ return out
91
+
92
+
93
+ def normalize_depth_v2_style(depth: torch.Tensor, sky: torch.Tensor | None = None, low_quantile: float = 0.01, high_quantile: float = 0.99) -> torch.Tensor:
94
+ """V2-style normalization computes percentile bounds over non-sky pixels (when available), then maps depth into [0, 1] with near = white (1.0)."""
95
+ if sky is not None:
96
+ mask = compute_non_sky_mask(sky)
97
+ if mask.any():
98
+ valid = depth[mask]
99
+ else:
100
+ valid = depth.flatten()
101
+ else:
102
+ valid = depth.flatten()
103
+
104
+ if valid.numel() > 100_000:
105
+ idx = torch.randint(0, valid.numel(), (100_000,), device=valid.device)
106
+ sample = valid[idx]
107
+ else:
108
+ sample = valid
109
+
110
+ lo = torch.quantile(sample, low_quantile)
111
+ hi = torch.quantile(sample, high_quantile)
112
+ rng = (hi - lo).clamp(min=1e-6)
113
+ norm = ((depth - lo) / rng).clamp(0.0, 1.0)
114
+ # Nearer pixels are brighter (1.0)
115
+ norm = 1.0 - norm
116
+ if sky is not None:
117
+ # Sky pixels become black (far / unknown)
118
+ sky_mask = ~compute_non_sky_mask(sky)
119
+ norm = torch.where(sky_mask, torch.zeros_like(norm), norm)
120
+ return norm
121
+
122
+
123
+ def normalize_depth_min_max(depth: torch.Tensor) -> torch.Tensor:
124
+ """Simple per-frame min/max normalization with near=1.0 convention."""
125
+ lo = depth.amin(dim=(-2, -1), keepdim=True)
126
+ hi = depth.amax(dim=(-2, -1), keepdim=True)
127
+ rng = (hi - lo).clamp(min=1e-6)
128
+ return 1.0 - ((depth - lo) / rng).clamp(0.0, 1.0)
ComfyUI/comfy/ldm/depth_anything_3/ray_pose.py ADDED
@@ -0,0 +1,272 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Ray-to-pose conversion for the multi-view path of Depth Anything 3."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional, Tuple
6
+
7
+ import torch
8
+
9
+
10
+ # qr/svd use fp32: CUDA often has no fp16/bf16 kernels for these ops.
11
+
12
+
13
+ def _ql_decomposition(A: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
14
+ """Decompose A = Q @ L with Q orthogonal and L lower-triangular.
15
+ Implemented in terms of QR by reversing the columns/rows; the standard
16
+ trick from the upstream reference. Inputs A are (3, 3)."""
17
+ P = torch.tensor([[0, 0, 1], [0, 1, 0], [1, 0, 0]], device=A.device, dtype=A.dtype)
18
+ A_tilde = A @ P
19
+ # CUDA QR is not implemented for fp16/bf16; upcast just for this call.
20
+ Q_tilde, R_tilde = torch.linalg.qr(A_tilde.float())
21
+ Q_tilde = Q_tilde.to(A.dtype)
22
+ R_tilde = R_tilde.to(A.dtype)
23
+ Q = Q_tilde @ P
24
+ L = P @ R_tilde @ P
25
+ d = torch.diag(L)
26
+ sign = torch.sign(d)
27
+ Q = Q * sign[None, :] # scale columns of Q
28
+ L = L * sign[:, None] # scale rows of L
29
+ return Q, L
30
+
31
+
32
+ def _homogenize_points(points: torch.Tensor) -> torch.Tensor:
33
+ return torch.cat([points, torch.ones_like(points[..., :1])], dim=-1)
34
+
35
+
36
+ # -----------------------------------------------------------------------------
37
+ # Weighted-LSQ + RANSAC homography (batched)
38
+ # -----------------------------------------------------------------------------
39
+
40
+
41
+ def _find_homography_weighted_lsq(src_pts: torch.Tensor, dst_pts: torch.Tensor, confident_weight: torch.Tensor,) -> torch.Tensor:
42
+ """Solve a single H with weighted least-squares (DLT)."""
43
+ N = src_pts.shape[0]
44
+ if N < 4:
45
+ raise ValueError("At least 4 points are required to compute a homography.")
46
+ w = confident_weight.sqrt().unsqueeze(1) # (N, 1)
47
+ x = src_pts[:, 0:1]
48
+ y = src_pts[:, 1:2]
49
+ u = dst_pts[:, 0:1]
50
+ v = dst_pts[:, 1:2]
51
+ zeros = torch.zeros_like(x)
52
+ A1 = torch.cat([-x * w, -y * w, -w, zeros, zeros, zeros, x * u * w, y * u * w, u * w], dim=1)
53
+ A2 = torch.cat([zeros, zeros, zeros, -x * w, -y * w, -w, x * v * w, y * v * w, v * w], dim=1)
54
+ A = torch.cat([A1, A2], dim=0) # (2N, 9)
55
+ # CUDA SVD is not implemented for fp16/bf16; upcast just for this call.
56
+ _, _, Vh = torch.linalg.svd(A.float())
57
+ Vh = Vh.to(A.dtype)
58
+ H = Vh[-1].reshape(3, 3)
59
+ return H / H[-1, -1]
60
+
61
+
62
+ def _find_homography_weighted_lsq_batched(src_pts_batch: torch.Tensor, dst_pts_batch: torch.Tensor, confident_weight_batch: torch.Tensor) -> torch.Tensor:
63
+ """Batched DLT solver. Inputs (B, K, 2) / (B, K); output (B, 3, 3)."""
64
+ B, K, _ = src_pts_batch.shape
65
+ w = confident_weight_batch.sqrt().unsqueeze(2)
66
+ x = src_pts_batch[:, :, 0:1]
67
+ y = src_pts_batch[:, :, 1:2]
68
+ u = dst_pts_batch[:, :, 0:1]
69
+ v = dst_pts_batch[:, :, 1:2]
70
+ zeros = torch.zeros_like(x)
71
+ A1 = torch.cat([-x * w, -y * w, -w, zeros, zeros, zeros, x * u * w, y * u * w, u * w], dim=2)
72
+ A2 = torch.cat([zeros, zeros, zeros, -x * w, -y * w, -w, x * v * w, y * v * w, v * w], dim=2)
73
+ A = torch.cat([A1, A2], dim=1) # (B, 2K, 9)
74
+ # CUDA SVD is not implemented for fp16/bf16; upcast just for this call.
75
+ _, _, Vh = torch.linalg.svd(A.float())
76
+ Vh = Vh.to(A.dtype)
77
+ H = Vh[:, -1].reshape(B, 3, 3)
78
+ return H / H[:, 2:3, 2:3]
79
+
80
+
81
+ def _ransac_find_homography_weighted_batched(
82
+ src_pts: torch.Tensor, # (B, N, 2)
83
+ dst_pts: torch.Tensor, # (B, N, 2)
84
+ confident_weight: torch.Tensor, # (B, N)
85
+ n_sample: int,
86
+ n_iter: int = 100,
87
+ reproj_threshold: float = 3.0,
88
+ num_sample_for_ransac: int = 8,
89
+ max_inlier_num: int = 10000,
90
+ rand_sample_iters_idx: Optional[torch.Tensor] = None,
91
+ ) -> torch.Tensor:
92
+ """Batched weighted-RANSAC homography estimator. Returns (B, 3, 3) homography matrices."""
93
+ B, N, _ = src_pts.shape
94
+ assert N >= 4
95
+ device = src_pts.device
96
+
97
+ sorted_idx = torch.argsort(confident_weight, descending=True, dim=1)
98
+ candidate_idx = sorted_idx[:, :n_sample] # (B, n_sample)
99
+
100
+ if rand_sample_iters_idx is None:
101
+ rand_sample_iters_idx = torch.stack(
102
+ [torch.randperm(n_sample, device=device)[:num_sample_for_ransac]
103
+ for _ in range(n_iter)],
104
+ dim=0,
105
+ )
106
+
107
+ rand_idx = candidate_idx[:, rand_sample_iters_idx] # (B, n_iter, k)
108
+ b_idx = (
109
+ torch.arange(B, device=device)
110
+ .view(B, 1, 1)
111
+ .expand(B, n_iter, num_sample_for_ransac)
112
+ )
113
+ src_b = src_pts[b_idx, rand_idx]
114
+ dst_b = dst_pts[b_idx, rand_idx]
115
+ w_b = confident_weight[b_idx, rand_idx]
116
+
117
+ cB, cN = src_b.shape[:2]
118
+ H_batch = _find_homography_weighted_lsq_batched(
119
+ src_b.flatten(0, 1), dst_b.flatten(0, 1), w_b.flatten(0, 1),
120
+ ).unflatten(0, (cB, cN)) # (B, n_iter, 3, 3)
121
+
122
+ src_homo = torch.cat([src_pts, torch.ones(B, N, 1, device=device, dtype=src_pts.dtype)], dim=2)
123
+ proj = torch.bmm(
124
+ src_homo.unsqueeze(1).expand(B, n_iter, N, 3).reshape(-1, N, 3),
125
+ H_batch.reshape(-1, 3, 3).transpose(1, 2),
126
+ ) # (B*n_iter, N, 3)
127
+ proj_xy = (proj[:, :, :2] / proj[:, :, 2:3]).reshape(B, n_iter, N, 2)
128
+ err = ((proj_xy - dst_pts.unsqueeze(1)) ** 2).sum(-1).sqrt() # (B, n_iter, N)
129
+ inlier_mask = err < reproj_threshold
130
+ score = (inlier_mask * confident_weight.unsqueeze(1)).sum(dim=2)
131
+ best_idx = torch.argmax(score, dim=1)
132
+ best_inlier_mask = inlier_mask[torch.arange(B, device=device), best_idx]
133
+
134
+ # Refit with the inlier set (per-batch, since the inlier counts vary).
135
+ H_inlier_list = []
136
+ for b in range(B):
137
+ mask = best_inlier_mask[b]
138
+ in_src = src_pts[b][mask]
139
+ in_dst = dst_pts[b][mask]
140
+ in_w = confident_weight[b][mask]
141
+ if in_src.shape[0] < 4:
142
+ # Fall back to identity when RANSAC fails to find enough inliers.
143
+ H_inlier_list.append(torch.eye(3, device=device, dtype=src_pts.dtype))
144
+ continue
145
+ sorted_w = torch.argsort(in_w, descending=True)
146
+ if len(sorted_w) > max_inlier_num:
147
+ keep = max(int(len(sorted_w) * 0.95), max_inlier_num)
148
+ sorted_w = sorted_w[:keep][torch.randperm(keep, device=device)[:max_inlier_num]]
149
+ H_inlier_list.append(
150
+ _find_homography_weighted_lsq(in_src[sorted_w], in_dst[sorted_w], in_w[sorted_w])
151
+ )
152
+ return torch.stack(H_inlier_list, dim=0)
153
+
154
+
155
+ # -----------------------------------------------------------------------------
156
+ # Camera-ray utilities
157
+ # -----------------------------------------------------------------------------
158
+
159
+
160
+ def _unproject_identity(num_y: int, num_x: int, B: int, S: int, device, dtype) -> torch.Tensor:
161
+ """Camera-space unit rays for an identity intrinsic on a 2x2 image plane."""
162
+ dx = 1.0 / num_x
163
+ dy = 1.0 / num_y
164
+ # Centered camera-space coords directly (skip the K^-1 step since it's
165
+ # just a translation by -1 on x and y when K is identity-with-center=1).
166
+ y = torch.linspace(-(1 - dy), (1 - dy), num_y, device=device, dtype=dtype)
167
+ x = torch.linspace(-(1 - dx), (1 - dx), num_x, device=device, dtype=dtype)
168
+ yy, xx = torch.meshgrid(y, x, indexing="ij")
169
+ grid = torch.stack((xx, yy), dim=-1) # (h, w, 2)
170
+ grid = grid.unsqueeze(0).unsqueeze(0).expand(B, S, num_y, num_x, 2)
171
+ return torch.cat([grid, torch.ones_like(grid[..., :1])], dim=-1)
172
+
173
+
174
+ def _camray_to_caminfo(
175
+ camray: torch.Tensor, # (B, S, h, w, 6)
176
+ confidence: Optional[torch.Tensor] = None, # (B, S, h, w)
177
+ reproj_threshold: float = 0.2,
178
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
179
+ """Convert per-pixel camera rays to per-view (R, T, focal, principal)."""
180
+ if confidence is None:
181
+ confidence = torch.ones_like(camray[..., 0])
182
+ B, S, h, w, _ = camray.shape
183
+ device = camray.device
184
+ dtype = camray.dtype
185
+
186
+ rays_target = camray[..., :3] # (B, S, h, w, 3)
187
+ rays_origin = _unproject_identity(h, w, B, S, device, dtype)
188
+
189
+ # Flatten (B*S, h*w, *) for the RANSAC routine.
190
+ rays_target = rays_target.flatten(0, 1).flatten(1, 2)
191
+ rays_origin = rays_origin.flatten(0, 1).flatten(1, 2)
192
+ weights = confidence.flatten(0, 1).flatten(1, 2).clone()
193
+
194
+ # Project to 2D in homogeneous form (the upstream calls this "perspective division").
195
+ z_thresh = 1e-4
196
+ mask = (rays_target[:, :, 2].abs() > z_thresh) & (rays_origin[:, :, 2].abs() > z_thresh)
197
+ weights = torch.where(mask, weights, torch.zeros_like(weights))
198
+ src = rays_origin.clone()
199
+ dst = rays_target.clone()
200
+ src[..., 0] = torch.where(mask, src[..., 0] / src[..., 2], src[..., 0])
201
+ src[..., 1] = torch.where(mask, src[..., 1] / src[..., 2], src[..., 1])
202
+ dst[..., 0] = torch.where(mask, dst[..., 0] / dst[..., 2], dst[..., 0])
203
+ dst[..., 1] = torch.where(mask, dst[..., 1] / dst[..., 2], dst[..., 1])
204
+ src = src[..., :2]
205
+ dst = dst[..., :2]
206
+
207
+ N = src.shape[1]
208
+ n_iter = 100
209
+ sample_ratio = 0.3
210
+ num_sample_for_ransac = 8
211
+ n_sample = max(num_sample_for_ransac, int(N * sample_ratio))
212
+ rand_idx = torch.stack(
213
+ [torch.randperm(n_sample, device=device)[:num_sample_for_ransac] for _ in range(n_iter)],
214
+ dim=0,
215
+ )
216
+
217
+ # Chunk along the view axis to keep peak memory predictable.
218
+ chunk = 2
219
+ A_list = []
220
+ for i in range(0, src.shape[0], chunk):
221
+ A = _ransac_find_homography_weighted_batched(
222
+ src[i:i + chunk], dst[i:i + chunk], weights[i:i + chunk],
223
+ n_sample=n_sample, n_iter=n_iter,
224
+ num_sample_for_ransac=num_sample_for_ransac,
225
+ reproj_threshold=reproj_threshold,
226
+ rand_sample_iters_idx=rand_idx,
227
+ max_inlier_num=8000,
228
+ )
229
+ # Flip sign on dets that come out < 0 (so that the QL produces a
230
+ # right-handed rotation). ``det`` lacks fp16/bf16 CUDA kernels, so
231
+ # do the comparison in fp32.
232
+ flip = torch.linalg.det(A.float()) < 0
233
+ A = torch.where(flip[:, None, None], -A, A)
234
+ A_list.append(A)
235
+ A = torch.cat(A_list, dim=0) # (B*S, 3, 3)
236
+
237
+ R_list, f_list, pp_list = [], [], []
238
+ for i in range(A.shape[0]):
239
+ R, L = _ql_decomposition(A[i])
240
+ L = L / L[2][2]
241
+ f_list.append(torch.stack((L[0][0], L[1][1])))
242
+ pp_list.append(torch.stack((L[2][0], L[2][1])))
243
+ R_list.append(R)
244
+ R = torch.stack(R_list).reshape(B, S, 3, 3)
245
+ focal = torch.stack(f_list).reshape(B, S, 2)
246
+ pp = torch.stack(pp_list).reshape(B, S, 2)
247
+
248
+ # Translation: confidence-weighted average of camray direction(s).
249
+ cf = confidence.flatten(0, 1).flatten(1, 2)
250
+ T = (camray.flatten(0, 1).flatten(1, 2)[..., 3:] * cf.unsqueeze(-1)).sum(dim=1)
251
+ T = T / cf.sum(dim=-1, keepdim=True)
252
+ T = T.reshape(B, S, 3)
253
+
254
+ # Match upstream output convention: focal -> 1/focal, pp + 1.
255
+ return R, T, 1.0 / focal, pp + 1.0
256
+
257
+
258
+ def get_extrinsic_from_camray(
259
+ camray: torch.Tensor, # (B, S, h, w, 6)
260
+ conf: torch.Tensor, # (B, S, h, w, 1) or (B, S, h, w)
261
+ patch_size_y: int,
262
+ patch_size_x: int,
263
+ ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
264
+ """Wrap a 4x4 extrinsic + per-view focal + principal-point output."""
265
+ if conf.ndim == 5 and conf.shape[-1] == 1:
266
+ conf = conf.squeeze(-1)
267
+ R, T, focal, pp = _camray_to_caminfo(camray, confidence=conf)
268
+ extr = torch.cat([R, T.unsqueeze(-1)], dim=-1) # (B, S, 3, 4)
269
+ homo_row = torch.tensor([0, 0, 0, 1], dtype=R.dtype, device=R.device)
270
+ homo_row = homo_row.view(1, 1, 1, 4).expand(R.shape[0], R.shape[1], 1, 4)
271
+ extr = torch.cat([extr, homo_row], dim=-2) # (B, S, 4, 4)
272
+ return extr, focal, pp
ComfyUI/comfy/ldm/depth_anything_3/reference_view_selector.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Reference-view selection for the multi-view path of Depth Anything 3."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Literal
6
+
7
+ import torch
8
+
9
+
10
+ RefViewStrategy = Literal["first", "middle", "saddle_balanced", "saddle_sim_range"]
11
+
12
+
13
+ # Per the upstream constants module: ``THRESH_FOR_REF_SELECTION = 3``.
14
+ # Reference selection only runs when there are at least this many views.
15
+ THRESH_FOR_REF_SELECTION: int = 3
16
+
17
+
18
+ def select_reference_view(x: torch.Tensor, strategy: RefViewStrategy = "saddle_balanced") -> torch.Tensor:
19
+ """Pick a reference view index per batch element."""
20
+ B, S, _, _ = x.shape
21
+ if S <= 1:
22
+ return torch.zeros(B, dtype=torch.long, device=x.device)
23
+ if strategy == "first":
24
+ return torch.zeros(B, dtype=torch.long, device=x.device)
25
+ if strategy == "middle":
26
+ return torch.full((B,), S // 2, dtype=torch.long, device=x.device)
27
+
28
+ # Feature-based strategies: normalised cls/cam token per view.
29
+ img_class_feat = x[:, :, 0] / x[:, :, 0].norm(dim=-1, keepdim=True) # (B,S,C)
30
+
31
+ if strategy == "saddle_balanced":
32
+ sim = torch.matmul(img_class_feat, img_class_feat.transpose(1, 2)) # (B,S,S)
33
+ sim_no_diag = sim - torch.eye(S, device=sim.device).unsqueeze(0)
34
+ sim_score = sim_no_diag.sum(dim=-1) / (S - 1) # (B,S)
35
+ feat_norm = x[:, :, 0].norm(dim=-1) # (B,S)
36
+ feat_var = img_class_feat.var(dim=-1) # (B,S)
37
+
38
+ def _normalize(metric):
39
+ mn = metric.min(dim=1, keepdim=True).values
40
+ mx = metric.max(dim=1, keepdim=True).values
41
+ return (metric - mn) / (mx - mn + 1e-8)
42
+
43
+ sim_n, norm_n, var_n = _normalize(sim_score), _normalize(feat_norm), _normalize(feat_var)
44
+ balance = (sim_n - 0.5).abs() + (norm_n - 0.5).abs() + (var_n - 0.5).abs()
45
+ return balance.argmin(dim=1)
46
+
47
+ if strategy == "saddle_sim_range":
48
+ sim = torch.matmul(img_class_feat, img_class_feat.transpose(1, 2))
49
+ sim_no_diag = sim - torch.eye(S, device=sim.device).unsqueeze(0)
50
+ sim_max = sim_no_diag.max(dim=-1).values
51
+ sim_min = sim_no_diag.min(dim=-1).values
52
+ return (sim_max - sim_min).argmax(dim=1)
53
+
54
+ raise ValueError(
55
+ f"Unknown reference view selection strategy: {strategy!r}. "
56
+ f"Must be one of: 'first', 'middle', 'saddle_balanced', 'saddle_sim_range'"
57
+ )
58
+
59
+
60
+ def reorder_by_reference(x: torch.Tensor, b_idx: torch.Tensor) -> torch.Tensor:
61
+ """Reorder x so the reference view is at position 0 in axis S."""
62
+ B, S = x.shape[0], x.shape[1]
63
+ if S <= 1:
64
+ return x
65
+ positions = torch.arange(S, device=x.device).unsqueeze(0).expand(B, -1)
66
+ b_idx_exp = b_idx.unsqueeze(1)
67
+ reorder = torch.where(
68
+ (positions > 0) & (positions <= b_idx_exp),
69
+ positions - 1,
70
+ positions,
71
+ )
72
+ reorder[:, 0] = b_idx
73
+ batch = torch.arange(B, device=x.device).unsqueeze(1)
74
+ return x[batch, reorder]
75
+
76
+
77
+ def restore_original_order(x: torch.Tensor, b_idx: torch.Tensor) -> torch.Tensor:
78
+ """Inverse of reorder_by_reference."""
79
+ B, S = x.shape[0], x.shape[1]
80
+ if S <= 1:
81
+ return x
82
+ target_positions = torch.arange(S, device=x.device).unsqueeze(0).expand(B, -1)
83
+ b_idx_exp = b_idx.unsqueeze(1)
84
+ restore = torch.where(target_positions < b_idx_exp, target_positions + 1, target_positions)
85
+ restore = torch.scatter(restore, dim=1, index=b_idx_exp, src=torch.zeros_like(b_idx_exp))
86
+ batch = torch.arange(B, device=x.device).unsqueeze(1)
87
+ return x[batch, restore]
ComfyUI/comfy/ldm/depth_anything_3/transform.py ADDED
@@ -0,0 +1,160 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Geometry / camera transform helpers for Depth Anything 3."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Tuple
6
+
7
+ import torch
8
+ import torch.nn.functional as F
9
+
10
+
11
+ # -----------------------------------------------------------------------------
12
+ # Affine 4x4 helpers
13
+ # -----------------------------------------------------------------------------
14
+
15
+
16
+ def as_homogeneous(ext: torch.Tensor) -> torch.Tensor:
17
+ """Promote (...,3,4) extrinsics to (...,4,4) homogeneous form. No-op when the input is already ``(...,4,4)``."""
18
+ if ext.shape[-2:] == (4, 4):
19
+ return ext
20
+ if ext.shape[-2:] == (3, 4):
21
+ ones = torch.zeros_like(ext[..., :1, :4])
22
+ ones[..., 0, 3] = 1.0
23
+ return torch.cat([ext, ones], dim=-2)
24
+ raise ValueError(f"Invalid affine shape: {ext.shape}")
25
+
26
+
27
+ def affine_inverse(A: torch.Tensor) -> torch.Tensor:
28
+ """Inverse of an affine matrix ``[R|T; 0 0 0 1]``."""
29
+ R = A[..., :3, :3]
30
+ T = A[..., :3, 3:]
31
+ P = A[..., 3:, :]
32
+ return torch.cat([torch.cat([R.mT, -R.mT @ T], dim=-1), P], dim=-2)
33
+
34
+
35
+ # -----------------------------------------------------------------------------
36
+ # Quaternion <-> rotation matrix (xyzw / scalar-last)
37
+ # -----------------------------------------------------------------------------
38
+
39
+
40
+ def _sqrt_positive_part(x: torch.Tensor) -> torch.Tensor:
41
+ """sqrt(max(0, x)) with a zero subgradient where x == 0."""
42
+ ret = torch.zeros_like(x)
43
+ positive_mask = x > 0
44
+ if torch.is_grad_enabled():
45
+ ret[positive_mask] = torch.sqrt(x[positive_mask])
46
+ else:
47
+ ret = torch.where(positive_mask, torch.sqrt(x), ret)
48
+ return ret
49
+
50
+
51
+ def standardize_quaternion(quaternions: torch.Tensor) -> torch.Tensor:
52
+ """Force the real part of a unit quaternion (xyzw) to be non-negative."""
53
+ return torch.where(quaternions[..., 3:4] < 0, -quaternions, quaternions)
54
+
55
+
56
+ def quat_to_mat(quaternions: torch.Tensor) -> torch.Tensor:
57
+ """Convert quaternions (xyzw) to (...,3,3) rotation matrices."""
58
+ i, j, k, r = torch.unbind(quaternions, -1)
59
+ two_s = 2.0 / (quaternions * quaternions).sum(-1)
60
+ o = torch.stack(
61
+ (
62
+ 1 - two_s * (j * j + k * k),
63
+ two_s * (i * j - k * r),
64
+ two_s * (i * k + j * r),
65
+ two_s * (i * j + k * r),
66
+ 1 - two_s * (i * i + k * k),
67
+ two_s * (j * k - i * r),
68
+ two_s * (i * k - j * r),
69
+ two_s * (j * k + i * r),
70
+ 1 - two_s * (i * i + j * j),
71
+ ),
72
+ -1,
73
+ )
74
+ return o.reshape(quaternions.shape[:-1] + (3, 3))
75
+
76
+
77
+ def mat_to_quat(matrix: torch.Tensor) -> torch.Tensor:
78
+ """Convert (...,3,3) rotation matrices to quaternions (xyzw)."""
79
+ if matrix.size(-1) != 3 or matrix.size(-2) != 3:
80
+ raise ValueError(f"Invalid rotation matrix shape {matrix.shape}.")
81
+
82
+ batch_dim = matrix.shape[:-2]
83
+ m00, m01, m02, m10, m11, m12, m20, m21, m22 = torch.unbind(
84
+ matrix.reshape(batch_dim + (9,)), dim=-1
85
+ )
86
+
87
+ q_abs = _sqrt_positive_part(
88
+ torch.stack(
89
+ [
90
+ 1.0 + m00 + m11 + m22,
91
+ 1.0 + m00 - m11 - m22,
92
+ 1.0 - m00 + m11 - m22,
93
+ 1.0 - m00 - m11 + m22,
94
+ ],
95
+ dim=-1,
96
+ )
97
+ )
98
+
99
+ quat_by_rijk = torch.stack(
100
+ [
101
+ torch.stack([q_abs[..., 0] ** 2, m21 - m12, m02 - m20, m10 - m01], dim=-1),
102
+ torch.stack([m21 - m12, q_abs[..., 1] ** 2, m10 + m01, m02 + m20], dim=-1),
103
+ torch.stack([m02 - m20, m10 + m01, q_abs[..., 2] ** 2, m12 + m21], dim=-1),
104
+ torch.stack([m10 - m01, m20 + m02, m21 + m12, q_abs[..., 3] ** 2], dim=-1),
105
+ ],
106
+ dim=-2,
107
+ )
108
+
109
+ flr = torch.tensor(0.1).to(dtype=q_abs.dtype, device=q_abs.device)
110
+ quat_candidates = quat_by_rijk / (2.0 * q_abs[..., None].max(flr))
111
+
112
+ out = quat_candidates[F.one_hot(q_abs.argmax(dim=-1), num_classes=4) > 0.5, :].reshape(
113
+ batch_dim + (4,)
114
+ )
115
+ # Reorder rijk -> xyzw (i.e. ijkr).
116
+ out = out[..., [1, 2, 3, 0]]
117
+ return standardize_quaternion(out)
118
+
119
+
120
+ # -----------------------------------------------------------------------------
121
+ # Pose-encoding <-> extrinsics + intrinsics
122
+ # -----------------------------------------------------------------------------
123
+
124
+
125
+ def extri_intri_to_pose_encoding(extrinsics: torch.Tensor, intrinsics: torch.Tensor, image_size_hw: Tuple[int, int]) -> torch.Tensor:
126
+ """Pack (extr, intr, image_size) into the 9-D pose-encoding vector.
127
+ extrinsics: camera-to-world (c2w) (B,S,4,4) matrices,
128
+ intrinsics: pixel-space (B,S,3,3) matrices,
129
+ image_size_hw: is a (H, W) pair.
130
+ """
131
+ R = extrinsics[..., :3, :3]
132
+ T = extrinsics[..., :3, 3]
133
+ quat = mat_to_quat(R)
134
+ H, W = image_size_hw
135
+ fov_h = 2 * torch.atan((H / 2) / intrinsics[..., 1, 1])
136
+ fov_w = 2 * torch.atan((W / 2) / intrinsics[..., 0, 0])
137
+ return torch.cat([T, quat, fov_h[..., None], fov_w[..., None]], dim=-1).float()
138
+
139
+
140
+ def pose_encoding_to_extri_intri(pose_encoding: torch.Tensor, image_size_hw: Tuple[int, int]) -> Tuple[torch.Tensor, torch.Tensor]:
141
+ """Inverse of extri_intri_to_pose_encoding."""
142
+ T = pose_encoding[..., :3]
143
+ quat = pose_encoding[..., 3:7]
144
+ fov_h = pose_encoding[..., 7]
145
+ fov_w = pose_encoding[..., 8]
146
+ # Normalize to unit quaternion. CameraDec outputs raw values; a near-zero
147
+ # quaternion causes two_s = 2/norm² → inf in quat_to_mat → NaN extrinsics.
148
+ quat = quat / quat.norm(dim=-1, keepdim=True).clamp(min=1e-6)
149
+ R = quat_to_mat(quat)
150
+ extrinsics = torch.cat([R, T[..., None]], dim=-1)
151
+ H, W = image_size_hw
152
+ fy = (H / 2.0) / torch.clamp(torch.tan(fov_h / 2.0), 1e-6)
153
+ fx = (W / 2.0) / torch.clamp(torch.tan(fov_w / 2.0), 1e-6)
154
+ intrinsics = torch.zeros(pose_encoding.shape[:2] + (3, 3), device=pose_encoding.device, dtype=pose_encoding.dtype)
155
+ intrinsics[..., 0, 0] = fx
156
+ intrinsics[..., 1, 1] = fy
157
+ intrinsics[..., 0, 2] = W / 2
158
+ intrinsics[..., 1, 2] = H / 2
159
+ intrinsics[..., 2, 2] = 1.0
160
+ return extrinsics, intrinsics
ComfyUI/comfy/ldm/ernie/model.py ADDED
@@ -0,0 +1,307 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+
6
+ from comfy.ldm.modules.attention import optimized_attention
7
+ import comfy.model_management
8
+ import comfy.ops
9
+ import comfy.quant_ops
10
+
11
+ def rope(pos: torch.Tensor, dim: int, theta: int) -> torch.Tensor:
12
+ assert dim % 2 == 0
13
+ if not comfy.model_management.supports_fp64(pos.device):
14
+ device = torch.device("cpu")
15
+ else:
16
+ device = pos.device
17
+
18
+ scale = torch.arange(0, dim, 2, dtype=torch.float64, device=device) / dim
19
+ omega = 1.0 / (theta**scale)
20
+ out = torch.einsum("...n,d->...nd", pos.to(device), omega)
21
+ out = torch.stack([torch.cos(out), torch.sin(out)], dim=0)
22
+ return out.to(dtype=torch.float32, device=pos.device)
23
+
24
+ class ErnieImageEmbedND3(nn.Module):
25
+ def __init__(self, dim: int, theta: int, axes_dim: tuple):
26
+ super().__init__()
27
+ self.dim = dim
28
+ self.theta = theta
29
+ self.axes_dim = list(axes_dim)
30
+
31
+ def forward(self, ids: torch.Tensor) -> torch.Tensor:
32
+ emb = torch.cat([rope(ids[..., i], self.axes_dim[i], self.theta) for i in range(3)], dim=-1)
33
+ cos_ = emb[0]
34
+ sin_ = emb[1]
35
+ N = cos_.shape[-1]
36
+ half = N // 2
37
+ cos_top = cos_[..., :half].repeat_interleave(2, dim=-1)
38
+ sin_top = sin_[..., :half].repeat_interleave(2, dim=-1)
39
+ cos_bot = cos_[..., half:].repeat_interleave(2, dim=-1)
40
+ sin_bot = sin_[..., half:].repeat_interleave(2, dim=-1)
41
+ rot = torch.stack([cos_top, -sin_top, sin_bot, cos_bot], dim=-1)
42
+ return rot.reshape(*rot.shape[:-1], 2, 2).unsqueeze(2)
43
+
44
+ class ErnieImagePatchEmbedDynamic(nn.Module):
45
+ def __init__(self, in_channels: int, embed_dim: int, patch_size: int, operations, device=None, dtype=None):
46
+ super().__init__()
47
+ self.patch_size = patch_size
48
+ self.proj = operations.Conv2d(in_channels, embed_dim, kernel_size=patch_size, stride=patch_size, bias=True, device=device, dtype=dtype)
49
+
50
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
51
+ x = self.proj(x)
52
+ batch_size, dim, height, width = x.shape
53
+ return x.reshape(batch_size, dim, height * width).transpose(1, 2).contiguous()
54
+
55
+ class Timesteps(nn.Module):
56
+ def __init__(self, num_channels: int, flip_sin_to_cos: bool = False):
57
+ super().__init__()
58
+ self.num_channels = num_channels
59
+ self.flip_sin_to_cos = flip_sin_to_cos
60
+
61
+ def forward(self, timesteps: torch.Tensor) -> torch.Tensor:
62
+ half_dim = self.num_channels // 2
63
+ exponent = -math.log(10000) * torch.arange(half_dim, dtype=torch.float32, device=timesteps.device) / half_dim
64
+ emb = torch.exp(exponent)
65
+ emb = timesteps[:, None].float() * emb[None, :]
66
+ if self.flip_sin_to_cos:
67
+ emb = torch.cat([torch.cos(emb), torch.sin(emb)], dim=-1)
68
+ else:
69
+ emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=-1)
70
+ return emb
71
+
72
+ class TimestepEmbedding(nn.Module):
73
+ def __init__(self, in_channels: int, time_embed_dim: int, operations, device=None, dtype=None):
74
+ super().__init__()
75
+ Linear = operations.Linear
76
+ self.linear_1 = Linear(in_channels, time_embed_dim, bias=True, device=device, dtype=dtype)
77
+ self.act = nn.SiLU()
78
+ self.linear_2 = Linear(time_embed_dim, time_embed_dim, bias=True, device=device, dtype=dtype)
79
+
80
+ def forward(self, sample: torch.Tensor) -> torch.Tensor:
81
+ sample = self.linear_1(sample)
82
+ sample = self.act(sample)
83
+ sample = self.linear_2(sample)
84
+ return sample
85
+
86
+ class ErnieImageAttention(nn.Module):
87
+ def __init__(self, query_dim: int, heads: int, dim_head: int, eps: float = 1e-6, operations=None, device=None, dtype=None):
88
+ super().__init__()
89
+ self.heads = heads
90
+ self.head_dim = dim_head
91
+ self.inner_dim = heads * dim_head
92
+
93
+ Linear = operations.Linear
94
+ RMSNorm = operations.RMSNorm
95
+
96
+ self.to_q = Linear(query_dim, self.inner_dim, bias=False, device=device, dtype=dtype)
97
+ self.to_k = Linear(query_dim, self.inner_dim, bias=False, device=device, dtype=dtype)
98
+ self.to_v = Linear(query_dim, self.inner_dim, bias=False, device=device, dtype=dtype)
99
+
100
+ self.norm_q = RMSNorm(dim_head, eps=eps, elementwise_affine=True, device=device, dtype=dtype)
101
+ self.norm_k = RMSNorm(dim_head, eps=eps, elementwise_affine=True, device=device, dtype=dtype)
102
+
103
+ self.to_out = nn.ModuleList([Linear(self.inner_dim, query_dim, bias=False, device=device, dtype=dtype)])
104
+
105
+ def forward(self, x: torch.Tensor, attention_mask: torch.Tensor = None, image_rotary_emb: torch.Tensor = None) -> torch.Tensor:
106
+ B, S, _ = x.shape
107
+
108
+ q_flat = self.to_q(x)
109
+ k_flat = self.to_k(x)
110
+ v_flat = self.to_v(x)
111
+
112
+ query = q_flat.view(B, S, self.heads, self.head_dim)
113
+ key = k_flat.view(B, S, self.heads, self.head_dim)
114
+
115
+ if image_rotary_emb is not None and not comfy.model_management.in_training:
116
+ q_scale, _, q_offload_stream = comfy.ops.cast_bias_weight(self.norm_q, query, offloadable=True)
117
+ k_scale, _, k_offload_stream = comfy.ops.cast_bias_weight(self.norm_k, key, offloadable=True)
118
+ query, key = comfy.quant_ops.ck.rms_rope_split_half(query, key, image_rotary_emb, q_scale, k_scale, self.norm_q.eps)
119
+ comfy.ops.uncast_bias_weight(self.norm_q, q_scale, None, q_offload_stream)
120
+ comfy.ops.uncast_bias_weight(self.norm_k, k_scale, None, k_offload_stream)
121
+ else:
122
+ query = self.norm_q(query)
123
+ key = self.norm_k(key)
124
+ if image_rotary_emb is not None:
125
+ query, key = comfy.quant_ops.ck.apply_rope_split_half(query, key, image_rotary_emb)
126
+
127
+ q_flat = query.reshape(B, S, -1)
128
+ k_flat = key.reshape(B, S, -1)
129
+
130
+ hidden_states = optimized_attention(q_flat, k_flat, v_flat, self.heads, mask=attention_mask)
131
+
132
+ return self.to_out[0](hidden_states)
133
+
134
+ class ErnieImageFeedForward(nn.Module):
135
+ def __init__(self, hidden_size: int, ffn_hidden_size: int, operations, device=None, dtype=None):
136
+ super().__init__()
137
+ Linear = operations.Linear
138
+ self.gate_proj = Linear(hidden_size, ffn_hidden_size, bias=False, device=device, dtype=dtype)
139
+ self.up_proj = Linear(hidden_size, ffn_hidden_size, bias=False, device=device, dtype=dtype)
140
+ self.linear_fc2 = Linear(ffn_hidden_size, hidden_size, bias=False, device=device, dtype=dtype)
141
+
142
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
143
+ return self.linear_fc2(self.up_proj(x) * F.gelu(self.gate_proj(x)))
144
+
145
+ class ErnieImageSharedAdaLNBlock(nn.Module):
146
+ def __init__(self, hidden_size: int, num_heads: int, ffn_hidden_size: int, eps: float = 1e-6, operations=None, device=None, dtype=None):
147
+ super().__init__()
148
+ RMSNorm = operations.RMSNorm
149
+
150
+ self.adaLN_sa_ln = RMSNorm(hidden_size, eps=eps, device=device, dtype=dtype)
151
+ self.self_attention = ErnieImageAttention(
152
+ query_dim=hidden_size,
153
+ dim_head=hidden_size // num_heads,
154
+ heads=num_heads,
155
+ eps=eps,
156
+ operations=operations,
157
+ device=device,
158
+ dtype=dtype
159
+ )
160
+ self.adaLN_mlp_ln = RMSNorm(hidden_size, eps=eps, device=device, dtype=dtype)
161
+ self.mlp = ErnieImageFeedForward(hidden_size, ffn_hidden_size, operations=operations, device=device, dtype=dtype)
162
+
163
+ def forward(self, x, rotary_pos_emb, temb, attention_mask=None):
164
+ shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = temb
165
+
166
+ residual = x
167
+ x_norm = self.adaLN_sa_ln(x)
168
+ x_norm = x_norm * (1 + scale_msa) + shift_msa
169
+
170
+ attn_out = self.self_attention(x_norm, attention_mask=attention_mask, image_rotary_emb=rotary_pos_emb)
171
+ x = residual + gate_msa * attn_out
172
+
173
+ residual = x
174
+ x_norm = self.adaLN_mlp_ln(x)
175
+ x_norm = x_norm * (1 + scale_mlp) + shift_mlp
176
+
177
+ return residual + gate_mlp * self.mlp(x_norm)
178
+
179
+ class ErnieImageAdaLNContinuous(nn.Module):
180
+ def __init__(self, hidden_size: int, eps: float = 1e-6, operations=None, device=None, dtype=None):
181
+ super().__init__()
182
+ LayerNorm = operations.LayerNorm
183
+ Linear = operations.Linear
184
+ self.norm = LayerNorm(hidden_size, elementwise_affine=False, eps=eps, device=device, dtype=dtype)
185
+ self.linear = Linear(hidden_size, hidden_size * 2, device=device, dtype=dtype)
186
+
187
+ def forward(self, x: torch.Tensor, conditioning: torch.Tensor) -> torch.Tensor:
188
+ scale, shift = self.linear(conditioning).chunk(2, dim=-1)
189
+ x = self.norm(x)
190
+ x = torch.addcmul(shift.unsqueeze(1), x, 1 + scale.unsqueeze(1))
191
+ return x
192
+
193
+ class ErnieImageModel(nn.Module):
194
+ def __init__(
195
+ self,
196
+ hidden_size: int = 4096,
197
+ num_attention_heads: int = 32,
198
+ num_layers: int = 36,
199
+ ffn_hidden_size: int = 12288,
200
+ in_channels: int = 128,
201
+ out_channels: int = 128,
202
+ patch_size: int = 1,
203
+ text_in_dim: int = 3072,
204
+ rope_theta: int = 256,
205
+ rope_axes_dim: tuple = (32, 48, 48),
206
+ eps: float = 1e-6,
207
+ qk_layernorm: bool = True,
208
+ device=None,
209
+ dtype=None,
210
+ operations=None,
211
+ **kwargs
212
+ ):
213
+ super().__init__()
214
+ self.dtype = dtype
215
+ self.hidden_size = hidden_size
216
+ self.num_heads = num_attention_heads
217
+ self.head_dim = hidden_size // num_attention_heads
218
+ self.patch_size = patch_size
219
+ self.out_channels = out_channels
220
+
221
+ Linear = operations.Linear
222
+
223
+ self.x_embedder = ErnieImagePatchEmbedDynamic(in_channels, hidden_size, patch_size, operations, device, dtype)
224
+ self.text_proj = Linear(text_in_dim, hidden_size, bias=False, device=device, dtype=dtype) if text_in_dim != hidden_size else None
225
+
226
+ self.time_proj = Timesteps(hidden_size, flip_sin_to_cos=False)
227
+ self.time_embedding = TimestepEmbedding(hidden_size, hidden_size, operations, device, dtype)
228
+
229
+ self.pos_embed = ErnieImageEmbedND3(dim=self.head_dim, theta=rope_theta, axes_dim=rope_axes_dim)
230
+
231
+ self.adaLN_modulation = nn.Sequential(
232
+ nn.SiLU(),
233
+ Linear(hidden_size, 6 * hidden_size, device=device, dtype=dtype)
234
+ )
235
+
236
+ self.layers = nn.ModuleList([
237
+ ErnieImageSharedAdaLNBlock(hidden_size, num_attention_heads, ffn_hidden_size, eps, operations, device, dtype)
238
+ for _ in range(num_layers)
239
+ ])
240
+
241
+ self.final_norm = ErnieImageAdaLNContinuous(hidden_size, eps, operations, device, dtype)
242
+ self.final_linear = Linear(hidden_size, patch_size * patch_size * out_channels, device=device, dtype=dtype)
243
+
244
+ def forward(self, x, timesteps, context, **kwargs):
245
+ device, dtype = x.device, x.dtype
246
+ B, C, H, W = x.shape
247
+ p, Hp, Wp = self.patch_size, H // self.patch_size, W // self.patch_size
248
+ N_img = Hp * Wp
249
+
250
+ img_bsh = self.x_embedder(x)
251
+
252
+ text_bth = context
253
+ if self.text_proj is not None and text_bth.numel() > 0:
254
+ text_bth = self.text_proj(text_bth)
255
+ Tmax = text_bth.shape[1]
256
+
257
+ hidden_states = torch.cat([img_bsh, text_bth], dim=1)
258
+
259
+ text_ids = torch.zeros((B, Tmax, 3), device=device, dtype=torch.float32)
260
+ text_ids[:, :, 0] = torch.linspace(0, Tmax - 1, steps=Tmax, device=x.device, dtype=torch.float32)
261
+ index = float(Tmax)
262
+
263
+ transformer_options = kwargs.get("transformer_options", {})
264
+ rope_options = transformer_options.get("rope_options", None)
265
+
266
+ h_len, w_len = float(Hp), float(Wp)
267
+ h_offset, w_offset = 0.0, 0.0
268
+
269
+ if rope_options is not None:
270
+ h_len = (h_len - 1.0) * rope_options.get("scale_y", 1.0) + 1.0
271
+ w_len = (w_len - 1.0) * rope_options.get("scale_x", 1.0) + 1.0
272
+ index += rope_options.get("shift_t", 0.0)
273
+ h_offset += rope_options.get("shift_y", 0.0)
274
+ w_offset += rope_options.get("shift_x", 0.0)
275
+
276
+ image_ids = torch.zeros((Hp, Wp, 3), device=device, dtype=torch.float32)
277
+ image_ids[:, :, 0] = image_ids[:, :, 1] + index
278
+ image_ids[:, :, 1] = image_ids[:, :, 1] + torch.linspace(h_offset, h_len - 1 + h_offset, steps=Hp, device=device, dtype=torch.float32).unsqueeze(1)
279
+ image_ids[:, :, 2] = image_ids[:, :, 2] + torch.linspace(w_offset, w_len - 1 + w_offset, steps=Wp, device=device, dtype=torch.float32).unsqueeze(0)
280
+
281
+ image_ids = image_ids.view(1, N_img, 3).expand(B, -1, -1)
282
+
283
+ rotary_pos_emb = self.pos_embed(torch.cat([image_ids, text_ids], dim=1))
284
+ del image_ids, text_ids
285
+
286
+ sample = self.time_proj(timesteps).to(dtype)
287
+ c = self.time_embedding(sample)
288
+
289
+ shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp = [
290
+ t.unsqueeze(1).contiguous() for t in self.adaLN_modulation(c).chunk(6, dim=-1)
291
+ ]
292
+
293
+ temb = [shift_msa, scale_msa, gate_msa, shift_mlp, scale_mlp, gate_mlp]
294
+ for layer in self.layers:
295
+ hidden_states = layer(hidden_states, rotary_pos_emb, temb)
296
+
297
+ hidden_states = self.final_norm(hidden_states, c).type_as(hidden_states)
298
+
299
+ patches = self.final_linear(hidden_states)[:, :N_img, :]
300
+ output = (
301
+ patches.view(B, Hp, Wp, p, p, self.out_channels)
302
+ .permute(0, 5, 1, 3, 2, 4)
303
+ .contiguous()
304
+ .view(B, self.out_channels, H, W)
305
+ )
306
+
307
+ return output
ComfyUI/comfy/ldm/flux/controlnet.py ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #Original code can be found on: https://github.com/XLabs-AI/x-flux/blob/main/src/flux/controlnet.py
2
+ #modified to support different types of flux controlnets
3
+
4
+ import torch
5
+ import math
6
+ from torch import Tensor, nn
7
+ from einops import rearrange, repeat
8
+
9
+ from .layers import (timestep_embedding)
10
+
11
+ from .model import Flux
12
+ import comfy.ldm.common_dit
13
+
14
+ class MistolineCondDownsamplBlock(nn.Module):
15
+ def __init__(self, dtype=None, device=None, operations=None):
16
+ super().__init__()
17
+ self.encoder = nn.Sequential(
18
+ operations.Conv2d(3, 16, 3, padding=1, dtype=dtype, device=device),
19
+ nn.SiLU(),
20
+ operations.Conv2d(16, 16, 1, dtype=dtype, device=device),
21
+ nn.SiLU(),
22
+ operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device),
23
+ nn.SiLU(),
24
+ operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device),
25
+ nn.SiLU(),
26
+ operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device),
27
+ nn.SiLU(),
28
+ operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device),
29
+ nn.SiLU(),
30
+ operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device),
31
+ nn.SiLU(),
32
+ operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device),
33
+ nn.SiLU(),
34
+ operations.Conv2d(16, 16, 1, dtype=dtype, device=device),
35
+ nn.SiLU(),
36
+ operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device)
37
+ )
38
+
39
+ def forward(self, x):
40
+ return self.encoder(x)
41
+
42
+ class MistolineControlnetBlock(nn.Module):
43
+ def __init__(self, hidden_size, dtype=None, device=None, operations=None):
44
+ super().__init__()
45
+ self.linear = operations.Linear(hidden_size, hidden_size, dtype=dtype, device=device)
46
+ self.act = nn.SiLU()
47
+
48
+ def forward(self, x):
49
+ return self.act(self.linear(x))
50
+
51
+
52
+ class ControlNetFlux(Flux):
53
+ def __init__(self, latent_input=False, num_union_modes=0, mistoline=False, control_latent_channels=None, image_model=None, dtype=None, device=None, operations=None, **kwargs):
54
+ super().__init__(final_layer=False, dtype=dtype, device=device, operations=operations, **kwargs)
55
+
56
+ self.main_model_double = 19
57
+ self.main_model_single = 38
58
+
59
+ self.mistoline = mistoline
60
+ # add ControlNet blocks
61
+ if self.mistoline:
62
+ control_block = lambda : MistolineControlnetBlock(self.hidden_size, dtype=dtype, device=device, operations=operations)
63
+ else:
64
+ control_block = lambda : operations.Linear(self.hidden_size, self.hidden_size, dtype=dtype, device=device)
65
+
66
+ self.controlnet_blocks = nn.ModuleList([])
67
+ for _ in range(self.params.depth):
68
+ self.controlnet_blocks.append(control_block())
69
+
70
+ self.controlnet_single_blocks = nn.ModuleList([])
71
+ for _ in range(self.params.depth_single_blocks):
72
+ self.controlnet_single_blocks.append(control_block())
73
+
74
+ self.num_union_modes = num_union_modes
75
+ self.controlnet_mode_embedder = None
76
+ if self.num_union_modes > 0:
77
+ self.controlnet_mode_embedder = operations.Embedding(self.num_union_modes, self.hidden_size, dtype=dtype, device=device)
78
+
79
+ self.gradient_checkpointing = False
80
+ self.latent_input = latent_input
81
+ if control_latent_channels is None:
82
+ control_latent_channels = self.in_channels
83
+ else:
84
+ control_latent_channels *= 2 * 2 #patch size
85
+
86
+ self.pos_embed_input = operations.Linear(control_latent_channels, self.hidden_size, bias=True, dtype=dtype, device=device)
87
+ if not self.latent_input:
88
+ if self.mistoline:
89
+ self.input_cond_block = MistolineCondDownsamplBlock(dtype=dtype, device=device, operations=operations)
90
+ else:
91
+ self.input_hint_block = nn.Sequential(
92
+ operations.Conv2d(3, 16, 3, padding=1, dtype=dtype, device=device),
93
+ nn.SiLU(),
94
+ operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device),
95
+ nn.SiLU(),
96
+ operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device),
97
+ nn.SiLU(),
98
+ operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device),
99
+ nn.SiLU(),
100
+ operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device),
101
+ nn.SiLU(),
102
+ operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device),
103
+ nn.SiLU(),
104
+ operations.Conv2d(16, 16, 3, padding=1, stride=2, dtype=dtype, device=device),
105
+ nn.SiLU(),
106
+ operations.Conv2d(16, 16, 3, padding=1, dtype=dtype, device=device)
107
+ )
108
+
109
+ def forward_orig(
110
+ self,
111
+ img: Tensor,
112
+ img_ids: Tensor,
113
+ controlnet_cond: Tensor,
114
+ txt: Tensor,
115
+ txt_ids: Tensor,
116
+ timesteps: Tensor,
117
+ y: Tensor,
118
+ guidance: Tensor = None,
119
+ control_type: Tensor = None,
120
+ ) -> Tensor:
121
+ if img.ndim != 3 or txt.ndim != 3:
122
+ raise ValueError("Input img and txt tensors must have 3 dimensions.")
123
+
124
+ if y is None:
125
+ y = torch.zeros((img.shape[0], self.params.vec_in_dim), device=img.device, dtype=img.dtype)
126
+ else:
127
+ y = y[:, :self.params.vec_in_dim]
128
+
129
+ # running on sequences img
130
+ img = self.img_in(img)
131
+
132
+ controlnet_cond = self.pos_embed_input(controlnet_cond)
133
+ img = img + controlnet_cond
134
+ vec = self.time_in(timestep_embedding(timesteps, 256))
135
+ if self.params.guidance_embed:
136
+ vec = vec + self.guidance_in(timestep_embedding(guidance, 256))
137
+ vec = vec + self.vector_in(y)
138
+ txt = self.txt_in(txt)
139
+
140
+ if self.controlnet_mode_embedder is not None and len(control_type) > 0:
141
+ control_cond = self.controlnet_mode_embedder(torch.tensor(control_type, device=img.device), out_dtype=img.dtype).unsqueeze(0).repeat((txt.shape[0], 1, 1))
142
+ txt = torch.cat([control_cond, txt], dim=1)
143
+ txt_ids = torch.cat([txt_ids[:,:1], txt_ids], dim=1)
144
+
145
+ ids = torch.cat((txt_ids, img_ids), dim=1)
146
+ pe = self.pe_embedder(ids)
147
+
148
+ controlnet_double = ()
149
+
150
+ for i in range(len(self.double_blocks)):
151
+ img, txt = self.double_blocks[i](img=img, txt=txt, vec=vec, pe=pe)
152
+ controlnet_double = controlnet_double + (self.controlnet_blocks[i](img),)
153
+
154
+ img = torch.cat((txt, img), 1)
155
+
156
+ controlnet_single = ()
157
+
158
+ for i in range(len(self.single_blocks)):
159
+ img = self.single_blocks[i](img, vec=vec, pe=pe)
160
+ controlnet_single = controlnet_single + (self.controlnet_single_blocks[i](img[:, txt.shape[1] :, ...]),)
161
+
162
+ repeat = math.ceil(self.main_model_double / len(controlnet_double))
163
+ if self.latent_input:
164
+ out_input = ()
165
+ for x in controlnet_double:
166
+ out_input += (x,) * repeat
167
+ else:
168
+ out_input = (controlnet_double * repeat)
169
+
170
+ out = {"input": out_input[:self.main_model_double]}
171
+ if len(controlnet_single) > 0:
172
+ repeat = math.ceil(self.main_model_single / len(controlnet_single))
173
+ out_output = ()
174
+ if self.latent_input:
175
+ for x in controlnet_single:
176
+ out_output += (x,) * repeat
177
+ else:
178
+ out_output = (controlnet_single * repeat)
179
+ out["output"] = out_output[:self.main_model_single]
180
+ return out
181
+
182
+ def forward(self, x, timesteps, context, y=None, guidance=None, hint=None, **kwargs):
183
+ patch_size = 2
184
+ if self.latent_input:
185
+ hint = comfy.ldm.common_dit.pad_to_patch_size(hint, (patch_size, patch_size))
186
+ elif self.mistoline:
187
+ hint = hint * 2.0 - 1.0
188
+ hint = self.input_cond_block(hint)
189
+ else:
190
+ hint = hint * 2.0 - 1.0
191
+ hint = self.input_hint_block(hint)
192
+
193
+ hint = rearrange(hint, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size)
194
+
195
+ bs, c, h, w = x.shape
196
+ x = comfy.ldm.common_dit.pad_to_patch_size(x, (patch_size, patch_size))
197
+
198
+ img = rearrange(x, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size)
199
+
200
+ h_len = ((h + (patch_size // 2)) // patch_size)
201
+ w_len = ((w + (patch_size // 2)) // patch_size)
202
+ img_ids = torch.zeros((h_len, w_len, 3), device=x.device, dtype=x.dtype)
203
+ img_ids[..., 1] = img_ids[..., 1] + torch.linspace(0, h_len - 1, steps=h_len, device=x.device, dtype=x.dtype)[:, None]
204
+ img_ids[..., 2] = img_ids[..., 2] + torch.linspace(0, w_len - 1, steps=w_len, device=x.device, dtype=x.dtype)[None, :]
205
+ img_ids = repeat(img_ids, "h w c -> b (h w) c", b=bs)
206
+
207
+ txt_ids = torch.zeros((bs, context.shape[1], 3), device=x.device, dtype=x.dtype)
208
+ return self.forward_orig(img, img_ids, hint, context, txt_ids, timesteps, y, guidance, control_type=kwargs.get("control_type", []))
ComfyUI/comfy/ldm/flux/layers.py ADDED
@@ -0,0 +1,372 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ from dataclasses import dataclass
3
+
4
+ import torch
5
+ from torch import Tensor, nn
6
+
7
+ from .math import attention, rope
8
+
9
+ # Fix import for some custom nodes, TODO: delete eventually.
10
+ RMSNorm = None
11
+
12
+ class EmbedND(nn.Module):
13
+ def __init__(self, dim: int, theta: int, axes_dim: list):
14
+ super().__init__()
15
+ self.dim = dim
16
+ self.theta = theta
17
+ self.axes_dim = axes_dim
18
+
19
+ def forward(self, ids: Tensor) -> Tensor:
20
+ n_axes = ids.shape[-1]
21
+ emb = torch.cat(
22
+ [rope(ids[..., i], self.axes_dim[i], self.theta) for i in range(n_axes)],
23
+ dim=-3,
24
+ )
25
+
26
+ return emb.unsqueeze(1)
27
+
28
+
29
+ def timestep_embedding(t: Tensor, dim, max_period=10000, time_factor: float = 1000.0):
30
+ """
31
+ Create sinusoidal timestep embeddings.
32
+ :param t: a 1-D Tensor of N indices, one per batch element.
33
+ These may be fractional.
34
+ :param dim: the dimension of the output.
35
+ :param max_period: controls the minimum frequency of the embeddings.
36
+ :return: an (N, D) Tensor of positional embeddings.
37
+ """
38
+ t = time_factor * t
39
+ half = dim // 2
40
+ freqs = torch.exp(-math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32, device=t.device) / half)
41
+
42
+ args = t[:, None].float() * freqs[None]
43
+ embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
44
+ if dim % 2:
45
+ embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1)
46
+ if torch.is_floating_point(t):
47
+ embedding = embedding.to(t)
48
+ return embedding
49
+
50
+ class MLPEmbedder(nn.Module):
51
+ def __init__(self, in_dim: int, hidden_dim: int, bias=True, dtype=None, device=None, operations=None):
52
+ super().__init__()
53
+ self.in_layer = operations.Linear(in_dim, hidden_dim, bias=bias, dtype=dtype, device=device)
54
+ self.silu = nn.SiLU()
55
+ self.out_layer = operations.Linear(hidden_dim, hidden_dim, bias=bias, dtype=dtype, device=device)
56
+
57
+ def forward(self, x: Tensor) -> Tensor:
58
+ return self.out_layer(self.silu(self.in_layer(x)))
59
+
60
+ class YakMLP(nn.Module):
61
+ def __init__(self, hidden_size: int, intermediate_size: int, dtype=None, device=None, operations=None):
62
+ super().__init__()
63
+ self.hidden_size = hidden_size
64
+ self.intermediate_size = intermediate_size
65
+ self.gate_proj = operations.Linear(self.hidden_size, self.intermediate_size, bias=True, dtype=dtype, device=device)
66
+ self.up_proj = operations.Linear(self.hidden_size, self.intermediate_size, bias=True, dtype=dtype, device=device)
67
+ self.down_proj = operations.Linear(self.intermediate_size, self.hidden_size, bias=True, dtype=dtype, device=device)
68
+ self.act_fn = nn.SiLU()
69
+
70
+ def forward(self, x: Tensor) -> Tensor:
71
+ down_proj = self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
72
+ return down_proj
73
+
74
+ def build_mlp(hidden_size, mlp_hidden_dim, mlp_silu_act=False, yak_mlp=False, dtype=None, device=None, operations=None):
75
+ if yak_mlp:
76
+ return YakMLP(hidden_size, mlp_hidden_dim, dtype=dtype, device=device, operations=operations)
77
+ if mlp_silu_act:
78
+ return nn.Sequential(
79
+ operations.Linear(hidden_size, mlp_hidden_dim * 2, bias=False, dtype=dtype, device=device),
80
+ SiLUActivation(),
81
+ operations.Linear(mlp_hidden_dim, hidden_size, bias=False, dtype=dtype, device=device),
82
+ )
83
+ else:
84
+ return nn.Sequential(
85
+ operations.Linear(hidden_size, mlp_hidden_dim, bias=True, dtype=dtype, device=device),
86
+ nn.GELU(approximate="tanh"),
87
+ operations.Linear(mlp_hidden_dim, hidden_size, bias=True, dtype=dtype, device=device),
88
+ )
89
+
90
+
91
+ class QKNorm(torch.nn.Module):
92
+ def __init__(self, dim: int, dtype=None, device=None, operations=None):
93
+ super().__init__()
94
+ self.query_norm = operations.RMSNorm(dim, dtype=dtype, device=device)
95
+ self.key_norm = operations.RMSNorm(dim, dtype=dtype, device=device)
96
+
97
+ def forward(self, q: Tensor, k: Tensor, v: Tensor) -> tuple:
98
+ q = self.query_norm(q)
99
+ k = self.key_norm(k)
100
+ return q.to(v), k.to(v)
101
+
102
+
103
+ class SelfAttention(nn.Module):
104
+ def __init__(self, dim: int, num_heads: int = 8, qkv_bias: bool = False, proj_bias: bool = True, dtype=None, device=None, operations=None):
105
+ super().__init__()
106
+ self.num_heads = num_heads
107
+ head_dim = dim // num_heads
108
+
109
+ self.qkv = operations.Linear(dim, dim * 3, bias=qkv_bias, dtype=dtype, device=device)
110
+ self.norm = QKNorm(head_dim, dtype=dtype, device=device, operations=operations)
111
+ self.proj = operations.Linear(dim, dim, bias=proj_bias, dtype=dtype, device=device)
112
+
113
+
114
+ @dataclass
115
+ class ModulationOut:
116
+ shift: Tensor
117
+ scale: Tensor
118
+ gate: Tensor
119
+
120
+
121
+ class Modulation(nn.Module):
122
+ def __init__(self, dim: int, double: bool, bias=True, dtype=None, device=None, operations=None):
123
+ super().__init__()
124
+ self.is_double = double
125
+ self.multiplier = 6 if double else 3
126
+ self.lin = operations.Linear(dim, self.multiplier * dim, bias=bias, dtype=dtype, device=device)
127
+
128
+ def forward(self, vec: Tensor) -> tuple:
129
+ if vec.ndim == 2:
130
+ vec = vec[:, None, :]
131
+ out = self.lin(nn.functional.silu(vec)).chunk(self.multiplier, dim=-1)
132
+
133
+ return (
134
+ ModulationOut(*out[:3]),
135
+ ModulationOut(*out[3:]) if self.is_double else None,
136
+ )
137
+
138
+
139
+ def apply_mod(tensor, m_mult, m_add=None, modulation_dims=None):
140
+ if modulation_dims is None:
141
+ if m_add is not None:
142
+ return torch.addcmul(m_add, tensor, m_mult)
143
+ else:
144
+ return tensor * m_mult
145
+ else:
146
+ for d in modulation_dims:
147
+ tensor[:, d[0]:d[1]] *= m_mult[:, d[2]:d[2] + 1]
148
+ if m_add is not None:
149
+ tensor[:, d[0]:d[1]] += m_add[:, d[2]:d[2] + 1]
150
+ return tensor
151
+
152
+
153
+ class SiLUActivation(nn.Module):
154
+ def __init__(self):
155
+ super().__init__()
156
+ self.gate_fn = nn.SiLU()
157
+
158
+ def forward(self, x: Tensor) -> Tensor:
159
+ x1, x2 = x.chunk(2, dim=-1)
160
+ return self.gate_fn(x1) * x2
161
+
162
+
163
+ class DoubleStreamBlock(nn.Module):
164
+ def __init__(self, hidden_size: int, num_heads: int, mlp_ratio: float, qkv_bias: bool = False, modulation=True, mlp_silu_act=False, proj_bias=True, yak_mlp=False, dtype=None, device=None, operations=None):
165
+ super().__init__()
166
+
167
+ mlp_hidden_dim = int(hidden_size * mlp_ratio)
168
+ self.num_heads = num_heads
169
+ self.hidden_size = hidden_size
170
+ self.modulation = modulation
171
+
172
+ if self.modulation:
173
+ self.img_mod = Modulation(hidden_size, double=True, dtype=dtype, device=device, operations=operations)
174
+
175
+ self.img_norm1 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
176
+ self.img_attn = SelfAttention(dim=hidden_size, num_heads=num_heads, qkv_bias=qkv_bias, proj_bias=proj_bias, dtype=dtype, device=device, operations=operations)
177
+
178
+ self.img_norm2 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
179
+
180
+ self.img_mlp = build_mlp(hidden_size, mlp_hidden_dim, mlp_silu_act=mlp_silu_act, yak_mlp=yak_mlp, dtype=dtype, device=device, operations=operations)
181
+
182
+ if self.modulation:
183
+ self.txt_mod = Modulation(hidden_size, double=True, dtype=dtype, device=device, operations=operations)
184
+
185
+ self.txt_norm1 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
186
+ self.txt_attn = SelfAttention(dim=hidden_size, num_heads=num_heads, qkv_bias=qkv_bias, proj_bias=proj_bias, dtype=dtype, device=device, operations=operations)
187
+
188
+ self.txt_norm2 = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
189
+
190
+ self.txt_mlp = build_mlp(hidden_size, mlp_hidden_dim, mlp_silu_act=mlp_silu_act, yak_mlp=yak_mlp, dtype=dtype, device=device, operations=operations)
191
+
192
+ def forward(self, img: Tensor, txt: Tensor, vec: Tensor, pe: Tensor, attn_mask=None, modulation_dims_img=None, modulation_dims_txt=None, transformer_options={}):
193
+ if self.modulation:
194
+ img_mod1, img_mod2 = self.img_mod(vec)
195
+ txt_mod1, txt_mod2 = self.txt_mod(vec)
196
+ else:
197
+ (img_mod1, img_mod2), (txt_mod1, txt_mod2) = vec
198
+
199
+ transformer_patches = transformer_options.get("patches", {})
200
+ extra_options = transformer_options.copy()
201
+
202
+ # prepare image for attention
203
+ img_modulated = self.img_norm1(img)
204
+ img_modulated = apply_mod(img_modulated, (1 + img_mod1.scale), img_mod1.shift, modulation_dims_img)
205
+ img_qkv = self.img_attn.qkv(img_modulated)
206
+ del img_modulated
207
+ img_q, img_k, img_v = img_qkv.view(img_qkv.shape[0], img_qkv.shape[1], 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
208
+ del img_qkv
209
+ img_q, img_k = self.img_attn.norm(img_q, img_k, img_v)
210
+
211
+ # prepare txt for attention
212
+ txt_modulated = self.txt_norm1(txt)
213
+ txt_modulated = apply_mod(txt_modulated, (1 + txt_mod1.scale), txt_mod1.shift, modulation_dims_txt)
214
+ txt_qkv = self.txt_attn.qkv(txt_modulated)
215
+ del txt_modulated
216
+ txt_q, txt_k, txt_v = txt_qkv.view(txt_qkv.shape[0], txt_qkv.shape[1], 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
217
+ del txt_qkv
218
+ txt_q, txt_k = self.txt_attn.norm(txt_q, txt_k, txt_v)
219
+
220
+ q = torch.cat((txt_q, img_q), dim=2)
221
+ del txt_q, img_q
222
+ k = torch.cat((txt_k, img_k), dim=2)
223
+ del txt_k, img_k
224
+ v = torch.cat((txt_v, img_v), dim=2)
225
+ del txt_v, img_v
226
+
227
+ extra_options["img_slice"] = [txt.shape[1], q.shape[2]]
228
+ if "attn1_patch" in transformer_patches:
229
+ patch = transformer_patches["attn1_patch"]
230
+ for p in patch:
231
+ out = p(q, k, v, pe=pe, attn_mask=attn_mask, extra_options=extra_options)
232
+ q, k, v, pe, attn_mask = out.get("q", q), out.get("k", k), out.get("v", v), out.get("pe", pe), out.get("attn_mask", attn_mask)
233
+
234
+ # run actual attention
235
+ attn = attention(q, k, v, pe=pe, mask=attn_mask, transformer_options=transformer_options)
236
+ del q, k, v
237
+
238
+ if "attn1_output_patch" in transformer_patches:
239
+ patch = transformer_patches["attn1_output_patch"]
240
+ for p in patch:
241
+ attn = p(attn, extra_options)
242
+
243
+ txt_attn, img_attn = attn[:, : txt.shape[1]], attn[:, txt.shape[1]:]
244
+
245
+ # calculate the img bloks
246
+ img += apply_mod(self.img_attn.proj(img_attn), img_mod1.gate, None, modulation_dims_img)
247
+ del img_attn
248
+ img += apply_mod(self.img_mlp(apply_mod(self.img_norm2(img), (1 + img_mod2.scale), img_mod2.shift, modulation_dims_img)), img_mod2.gate, None, modulation_dims_img)
249
+
250
+ # calculate the txt bloks
251
+ txt += apply_mod(self.txt_attn.proj(txt_attn), txt_mod1.gate, None, modulation_dims_txt)
252
+ del txt_attn
253
+ txt += apply_mod(self.txt_mlp(apply_mod(self.txt_norm2(txt), (1 + txt_mod2.scale), txt_mod2.shift, modulation_dims_txt)), txt_mod2.gate, None, modulation_dims_txt)
254
+
255
+ if txt.dtype == torch.float16:
256
+ txt = torch.nan_to_num(txt, nan=0.0, posinf=65504, neginf=-65504)
257
+
258
+ return img, txt
259
+
260
+
261
+ class SingleStreamBlock(nn.Module):
262
+ """
263
+ A DiT block with parallel linear layers as described in
264
+ https://arxiv.org/abs/2302.05442 and adapted modulation interface.
265
+ """
266
+
267
+ def __init__(
268
+ self,
269
+ hidden_size: int,
270
+ num_heads: int,
271
+ mlp_ratio: float = 4.0,
272
+ qk_scale: float = None,
273
+ modulation=True,
274
+ mlp_silu_act=False,
275
+ bias=True,
276
+ yak_mlp=False,
277
+ dtype=None,
278
+ device=None,
279
+ operations=None
280
+ ):
281
+ super().__init__()
282
+ self.hidden_dim = hidden_size
283
+ self.num_heads = num_heads
284
+ head_dim = hidden_size // num_heads
285
+ self.scale = qk_scale or head_dim**-0.5
286
+
287
+ self.mlp_hidden_dim = int(hidden_size * mlp_ratio)
288
+
289
+ self.mlp_hidden_dim_first = self.mlp_hidden_dim
290
+ self.yak_mlp = yak_mlp
291
+ if mlp_silu_act:
292
+ self.mlp_hidden_dim_first = int(hidden_size * mlp_ratio * 2)
293
+ self.mlp_act = SiLUActivation()
294
+ else:
295
+ self.mlp_act = nn.GELU(approximate="tanh")
296
+
297
+ if self.yak_mlp:
298
+ self.mlp_hidden_dim_first *= 2
299
+ self.mlp_act = nn.SiLU()
300
+
301
+ # qkv and mlp_in
302
+ self.linear1 = operations.Linear(hidden_size, hidden_size * 3 + self.mlp_hidden_dim_first, bias=bias, dtype=dtype, device=device)
303
+ # proj and mlp_out
304
+ self.linear2 = operations.Linear(hidden_size + self.mlp_hidden_dim, hidden_size, bias=bias, dtype=dtype, device=device)
305
+
306
+ self.norm = QKNorm(head_dim, dtype=dtype, device=device, operations=operations)
307
+
308
+ self.hidden_size = hidden_size
309
+ self.pre_norm = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
310
+
311
+ if modulation:
312
+ self.modulation = Modulation(hidden_size, double=False, dtype=dtype, device=device, operations=operations)
313
+ else:
314
+ self.modulation = None
315
+
316
+ def forward(self, x: Tensor, vec: Tensor, pe: Tensor, attn_mask=None, modulation_dims=None, transformer_options={}) -> Tensor:
317
+ if self.modulation:
318
+ mod, _ = self.modulation(vec)
319
+ else:
320
+ mod = vec
321
+
322
+ transformer_patches = transformer_options.get("patches", {})
323
+ extra_options = transformer_options.copy()
324
+
325
+ qkv, mlp = torch.split(self.linear1(apply_mod(self.pre_norm(x), (1 + mod.scale), mod.shift, modulation_dims)), [3 * self.hidden_size, self.mlp_hidden_dim_first], dim=-1)
326
+
327
+ q, k, v = qkv.view(qkv.shape[0], qkv.shape[1], 3, self.num_heads, -1).permute(2, 0, 3, 1, 4)
328
+ del qkv
329
+ q, k = self.norm(q, k, v)
330
+
331
+ if "attn1_patch" in transformer_patches:
332
+ patch = transformer_patches["attn1_patch"]
333
+ for p in patch:
334
+ out = p(q, k, v, pe=pe, attn_mask=attn_mask, extra_options=extra_options)
335
+ q, k, v, pe, attn_mask = out.get("q", q), out.get("k", k), out.get("v", v), out.get("pe", pe), out.get("attn_mask", attn_mask)
336
+
337
+ # compute attention
338
+ attn = attention(q, k, v, pe=pe, mask=attn_mask, transformer_options=transformer_options)
339
+ del q, k, v
340
+
341
+ if "attn1_output_patch" in transformer_patches:
342
+ patch = transformer_patches["attn1_output_patch"]
343
+ for p in patch:
344
+ attn = p(attn, extra_options)
345
+
346
+ # compute activation in mlp stream, cat again and run second linear layer
347
+ if self.yak_mlp:
348
+ mlp = self.mlp_act(mlp[..., self.mlp_hidden_dim_first // 2:]) * mlp[..., :self.mlp_hidden_dim_first // 2]
349
+ else:
350
+ mlp = self.mlp_act(mlp)
351
+ output = self.linear2(torch.cat((attn, mlp), 2))
352
+ x += apply_mod(output, mod.gate, None, modulation_dims)
353
+ if x.dtype == torch.float16:
354
+ x = torch.nan_to_num(x, nan=0.0, posinf=65504, neginf=-65504)
355
+ return x
356
+
357
+
358
+ class LastLayer(nn.Module):
359
+ def __init__(self, hidden_size: int, patch_size: int, out_channels: int, bias=True, dtype=None, device=None, operations=None):
360
+ super().__init__()
361
+ self.norm_final = operations.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6, dtype=dtype, device=device)
362
+ self.linear = operations.Linear(hidden_size, patch_size * patch_size * out_channels, bias=bias, dtype=dtype, device=device)
363
+ self.adaLN_modulation = nn.Sequential(nn.SiLU(), operations.Linear(hidden_size, 2 * hidden_size, bias=bias, dtype=dtype, device=device))
364
+
365
+ def forward(self, x: Tensor, vec: Tensor, modulation_dims=None) -> Tensor:
366
+ if vec.ndim == 2:
367
+ vec = vec[:, None, :]
368
+
369
+ shift, scale = self.adaLN_modulation(vec).chunk(2, dim=-1)
370
+ x = apply_mod(self.norm_final(x), (1 + scale), shift, modulation_dims)
371
+ x = self.linear(x)
372
+ return x
ComfyUI/comfy/ldm/flux/math.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from einops import rearrange
3
+ from torch import Tensor
4
+
5
+ from comfy.ldm.modules.attention import optimized_attention
6
+ import comfy.model_management
7
+ import comfy.quant_ops
8
+
9
+
10
+ def attention(q: Tensor, k: Tensor, v: Tensor, pe: Tensor, mask=None, transformer_options={}) -> Tensor:
11
+ if pe is not None:
12
+ q, k = apply_rope(q, k, pe)
13
+ heads = q.shape[1]
14
+ x = optimized_attention(q, k, v, heads, skip_reshape=True, mask=mask, transformer_options=transformer_options)
15
+ return x
16
+
17
+ def rope(pos: Tensor, dim: int, theta: int) -> Tensor:
18
+ assert dim % 2 == 0
19
+ if not comfy.model_management.supports_fp64(pos.device):
20
+ device = torch.device("cpu")
21
+ else:
22
+ device = pos.device
23
+
24
+ scale = torch.linspace(0, (dim - 2) / dim, steps=dim//2, dtype=torch.float64, device=device)
25
+ omega = 1.0 / (theta**scale)
26
+ out = torch.einsum("...n,d->...nd", pos.to(dtype=torch.float32, device=device), omega)
27
+ out = torch.stack([torch.cos(out), -torch.sin(out), torch.sin(out), torch.cos(out)], dim=-1)
28
+ out = rearrange(out, "b n d (i j) -> b n d i j", i=2, j=2)
29
+ return out.to(dtype=torch.float32, device=pos.device)
30
+
31
+
32
+ def _apply_rope1(x: Tensor, freqs_cis: Tensor):
33
+ x_ = x.to(dtype=freqs_cis.dtype).reshape(*x.shape[:-1], -1, 1, 2)
34
+ if x_.shape[2] != 1 and freqs_cis.shape[2] != 1 and x_.shape[2] != freqs_cis.shape[2]:
35
+ freqs_cis = freqs_cis[:, :, :x_.shape[2]]
36
+
37
+ x_out = freqs_cis[..., 0] * x_[..., 0]
38
+ x_out.addcmul_(freqs_cis[..., 1], x_[..., 1])
39
+
40
+ return x_out.reshape(*x.shape).type_as(x)
41
+
42
+
43
+ def _apply_rope(xq: Tensor, xk: Tensor, freqs_cis: Tensor):
44
+ return apply_rope1(xq, freqs_cis), apply_rope1(xk, freqs_cis)
45
+
46
+
47
+ def apply_rope(xq, xk, freqs_cis):
48
+ if comfy.model_management.in_training:
49
+ return _apply_rope(xq, xk, freqs_cis)
50
+ else:
51
+ return comfy.quant_ops.ck.apply_rope(xq, xk, freqs_cis)
52
+
53
+
54
+ def apply_rope1(x, freqs_cis):
55
+ if comfy.model_management.in_training:
56
+ return _apply_rope1(x, freqs_cis)
57
+ else:
58
+ return comfy.quant_ops.ck.apply_rope1(x, freqs_cis)
ComfyUI/comfy/ldm/flux/model.py ADDED
@@ -0,0 +1,408 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #Original code can be found on: https://github.com/black-forest-labs/flux
2
+
3
+ from dataclasses import dataclass
4
+
5
+ import torch
6
+ from torch import Tensor, nn
7
+ from einops import rearrange, repeat
8
+ import comfy.ldm.common_dit
9
+ import comfy.patcher_extension
10
+
11
+ from .layers import (
12
+ DoubleStreamBlock,
13
+ EmbedND,
14
+ LastLayer,
15
+ MLPEmbedder,
16
+ SingleStreamBlock,
17
+ timestep_embedding,
18
+ Modulation,
19
+ )
20
+
21
+ @dataclass
22
+ class FluxParams:
23
+ in_channels: int
24
+ out_channels: int
25
+ vec_in_dim: int
26
+ context_in_dim: int
27
+ hidden_size: int
28
+ mlp_ratio: float
29
+ num_heads: int
30
+ depth: int
31
+ depth_single_blocks: int
32
+ axes_dim: list
33
+ theta: int
34
+ patch_size: int
35
+ qkv_bias: bool
36
+ guidance_embed: bool
37
+ txt_ids_dims: list
38
+ global_modulation: bool = False
39
+ mlp_silu_act: bool = False
40
+ ops_bias: bool = True
41
+ default_ref_method: str = "offset"
42
+ ref_index_scale: float = 1.0
43
+ yak_mlp: bool = False
44
+ txt_norm: bool = False
45
+
46
+
47
+ def invert_slices(slices, length):
48
+ sorted_slices = sorted(slices)
49
+ result = []
50
+ current = 0
51
+
52
+ for start, end in sorted_slices:
53
+ if current < start:
54
+ result.append((current, start))
55
+ current = max(current, end)
56
+
57
+ if current < length:
58
+ result.append((current, length))
59
+
60
+ return result
61
+
62
+
63
+ class Flux(nn.Module):
64
+ """
65
+ Transformer model for flow matching on sequences.
66
+ """
67
+
68
+ def __init__(self, image_model=None, final_layer=True, dtype=None, device=None, operations=None, **kwargs):
69
+ super().__init__()
70
+ self.dtype = dtype
71
+ params = FluxParams(**kwargs)
72
+ self.params = params
73
+ self.patch_size = params.patch_size
74
+ self.in_channels = params.in_channels * params.patch_size * params.patch_size
75
+ self.out_channels = params.out_channels * params.patch_size * params.patch_size
76
+ if params.hidden_size % params.num_heads != 0:
77
+ raise ValueError(
78
+ f"Hidden size {params.hidden_size} must be divisible by num_heads {params.num_heads}"
79
+ )
80
+ pe_dim = params.hidden_size // params.num_heads
81
+ if sum(params.axes_dim) != pe_dim:
82
+ raise ValueError(f"Got {params.axes_dim} but expected positional dim {pe_dim}")
83
+ self.hidden_size = params.hidden_size
84
+ self.num_heads = params.num_heads
85
+ self.pe_embedder = EmbedND(dim=pe_dim, theta=params.theta, axes_dim=params.axes_dim)
86
+ self.img_in = operations.Linear(self.in_channels, self.hidden_size, bias=params.ops_bias, dtype=dtype, device=device)
87
+ self.time_in = MLPEmbedder(in_dim=256, hidden_dim=self.hidden_size, bias=params.ops_bias, dtype=dtype, device=device, operations=operations)
88
+ if params.vec_in_dim is not None:
89
+ self.vector_in = MLPEmbedder(params.vec_in_dim, self.hidden_size, dtype=dtype, device=device, operations=operations)
90
+ else:
91
+ self.vector_in = None
92
+
93
+ self.guidance_in = (
94
+ MLPEmbedder(in_dim=256, hidden_dim=self.hidden_size, bias=params.ops_bias, dtype=dtype, device=device, operations=operations) if params.guidance_embed else nn.Identity()
95
+ )
96
+ self.txt_in = operations.Linear(params.context_in_dim, self.hidden_size, bias=params.ops_bias, dtype=dtype, device=device)
97
+
98
+ if params.txt_norm:
99
+ self.txt_norm = operations.RMSNorm(params.context_in_dim, dtype=dtype, device=device)
100
+ else:
101
+ self.txt_norm = None
102
+
103
+ self.double_blocks = nn.ModuleList(
104
+ [
105
+ DoubleStreamBlock(
106
+ self.hidden_size,
107
+ self.num_heads,
108
+ mlp_ratio=params.mlp_ratio,
109
+ qkv_bias=params.qkv_bias,
110
+ modulation=params.global_modulation is False,
111
+ mlp_silu_act=params.mlp_silu_act,
112
+ proj_bias=params.ops_bias,
113
+ yak_mlp=params.yak_mlp,
114
+ dtype=dtype, device=device, operations=operations
115
+ )
116
+ for _ in range(params.depth)
117
+ ]
118
+ )
119
+
120
+ self.single_blocks = nn.ModuleList(
121
+ [
122
+ SingleStreamBlock(self.hidden_size, self.num_heads, mlp_ratio=params.mlp_ratio, modulation=params.global_modulation is False, mlp_silu_act=params.mlp_silu_act, bias=params.ops_bias, yak_mlp=params.yak_mlp, dtype=dtype, device=device, operations=operations)
123
+ for _ in range(params.depth_single_blocks)
124
+ ]
125
+ )
126
+
127
+ if final_layer:
128
+ self.final_layer = LastLayer(self.hidden_size, 1, self.out_channels, bias=params.ops_bias, dtype=dtype, device=device, operations=operations)
129
+
130
+ if params.global_modulation:
131
+ self.double_stream_modulation_img = Modulation(
132
+ self.hidden_size,
133
+ double=True,
134
+ bias=False,
135
+ dtype=dtype, device=device, operations=operations
136
+ )
137
+ self.double_stream_modulation_txt = Modulation(
138
+ self.hidden_size,
139
+ double=True,
140
+ bias=False,
141
+ dtype=dtype, device=device, operations=operations
142
+ )
143
+ self.single_stream_modulation = Modulation(
144
+ self.hidden_size, double=False, bias=False, dtype=dtype, device=device, operations=operations
145
+ )
146
+
147
+ def forward_orig(
148
+ self,
149
+ img: Tensor,
150
+ img_ids: Tensor,
151
+ txt: Tensor,
152
+ txt_ids: Tensor,
153
+ timesteps: Tensor,
154
+ y: Tensor,
155
+ guidance: Tensor = None,
156
+ control = None,
157
+ timestep_zero_index=None,
158
+ transformer_options={},
159
+ attn_mask: Tensor = None,
160
+ ) -> Tensor:
161
+
162
+ transformer_options = transformer_options.copy()
163
+ patches = transformer_options.get("patches", {})
164
+ patches_replace = transformer_options.get("patches_replace", {})
165
+ if img.ndim != 3 or txt.ndim != 3:
166
+ raise ValueError("Input img and txt tensors must have 3 dimensions.")
167
+
168
+ # running on sequences img
169
+ img = self.img_in(img)
170
+ vec = self.time_in(timestep_embedding(timesteps, 256).to(img.dtype))
171
+ if self.params.guidance_embed:
172
+ if guidance is not None:
173
+ vec = vec + self.guidance_in(timestep_embedding(guidance, 256).to(img.dtype))
174
+
175
+ if self.vector_in is not None:
176
+ if y is None:
177
+ y = torch.zeros((img.shape[0], self.params.vec_in_dim), device=img.device, dtype=img.dtype)
178
+ vec = vec + self.vector_in(y[:, :self.params.vec_in_dim])
179
+
180
+ if self.txt_norm is not None:
181
+ txt = self.txt_norm(txt)
182
+ txt = self.txt_in(txt)
183
+
184
+ if "post_input" in patches:
185
+ for p in patches["post_input"]:
186
+ out = p({"img": img, "txt": txt, "img_ids": img_ids, "txt_ids": txt_ids, "transformer_options": transformer_options})
187
+ img = out["img"]
188
+ txt = out["txt"]
189
+ img_ids = out["img_ids"]
190
+ txt_ids = out["txt_ids"]
191
+
192
+ if img_ids is not None:
193
+ ids = torch.cat((txt_ids, img_ids), dim=1)
194
+ pe = self.pe_embedder(ids)
195
+ else:
196
+ pe = None
197
+
198
+ vec_orig = vec
199
+ txt_vec = vec
200
+ extra_kwargs = {}
201
+ if timestep_zero_index is not None:
202
+ modulation_dims = []
203
+ batch = vec.shape[0] // 2
204
+ vec_orig = vec_orig.reshape(2, batch, vec.shape[1]).movedim(0, 1)
205
+ invert = invert_slices(timestep_zero_index, img.shape[1])
206
+ for s in invert:
207
+ modulation_dims.append((s[0], s[1], 0))
208
+ for s in timestep_zero_index:
209
+ modulation_dims.append((s[0], s[1], 1))
210
+ extra_kwargs["modulation_dims_img"] = modulation_dims
211
+ txt_vec = vec[:batch]
212
+
213
+ if self.params.global_modulation:
214
+ vec = (self.double_stream_modulation_img(vec_orig), self.double_stream_modulation_txt(txt_vec))
215
+
216
+ blocks_replace = patches_replace.get("dit", {})
217
+ transformer_options["total_blocks"] = len(self.double_blocks)
218
+ transformer_options["block_type"] = "double"
219
+ for i, block in enumerate(self.double_blocks):
220
+ transformer_options["block_index"] = i
221
+ if ("double_block", i) in blocks_replace:
222
+ def block_wrap(args):
223
+ out = {}
224
+ out["img"], out["txt"] = block(img=args["img"],
225
+ txt=args["txt"],
226
+ vec=args["vec"],
227
+ pe=args["pe"],
228
+ attn_mask=args.get("attn_mask"),
229
+ transformer_options=args.get("transformer_options"),
230
+ **extra_kwargs)
231
+ return out
232
+
233
+ out = blocks_replace[("double_block", i)]({"img": img,
234
+ "txt": txt,
235
+ "vec": vec,
236
+ "pe": pe,
237
+ "attn_mask": attn_mask,
238
+ "transformer_options": transformer_options},
239
+ {"original_block": block_wrap})
240
+ txt = out["txt"]
241
+ img = out["img"]
242
+ else:
243
+ img, txt = block(img=img,
244
+ txt=txt,
245
+ vec=vec,
246
+ pe=pe,
247
+ attn_mask=attn_mask,
248
+ transformer_options=transformer_options,
249
+ **extra_kwargs)
250
+
251
+ if control is not None: # Controlnet
252
+ control_i = control.get("input")
253
+ if i < len(control_i):
254
+ add = control_i[i]
255
+ if add is not None:
256
+ img[:, :add.shape[1]] += add
257
+
258
+ if img.dtype == torch.float16:
259
+ img = torch.nan_to_num(img, nan=0.0, posinf=65504, neginf=-65504)
260
+
261
+ img = torch.cat((txt, img), 1)
262
+
263
+ if self.params.global_modulation:
264
+ vec, _ = self.single_stream_modulation(vec_orig)
265
+
266
+ extra_kwargs = {}
267
+ if timestep_zero_index is not None:
268
+ lambda a: 0 if a == 0 else a + txt.shape[1]
269
+ modulation_dims_combined = list(map(lambda x: (0 if x[0] == 0 else x[0] + txt.shape[1], x[1] + txt.shape[1], x[2]), modulation_dims))
270
+ extra_kwargs["modulation_dims"] = modulation_dims_combined
271
+
272
+ transformer_options["total_blocks"] = len(self.single_blocks)
273
+ transformer_options["block_type"] = "single"
274
+ transformer_options["img_slice"] = [txt.shape[1], img.shape[1]]
275
+ for i, block in enumerate(self.single_blocks):
276
+ transformer_options["block_index"] = i
277
+ if ("single_block", i) in blocks_replace:
278
+ def block_wrap(args):
279
+ out = {}
280
+ out["img"] = block(args["img"],
281
+ vec=args["vec"],
282
+ pe=args["pe"],
283
+ attn_mask=args.get("attn_mask"),
284
+ transformer_options=args.get("transformer_options"),
285
+ **extra_kwargs)
286
+ return out
287
+
288
+ out = blocks_replace[("single_block", i)]({"img": img,
289
+ "vec": vec,
290
+ "pe": pe,
291
+ "attn_mask": attn_mask,
292
+ "transformer_options": transformer_options},
293
+ {"original_block": block_wrap})
294
+ img = out["img"]
295
+ else:
296
+ img = block(img, vec=vec, pe=pe, attn_mask=attn_mask, transformer_options=transformer_options, **extra_kwargs)
297
+
298
+ if control is not None: # Controlnet
299
+ control_o = control.get("output")
300
+ if i < len(control_o):
301
+ add = control_o[i]
302
+ if add is not None:
303
+ img[:, txt.shape[1] : txt.shape[1] + add.shape[1], ...] += add
304
+
305
+ img = img[:, txt.shape[1] :, ...]
306
+
307
+ extra_kwargs = {}
308
+ if timestep_zero_index is not None:
309
+ extra_kwargs["modulation_dims"] = modulation_dims
310
+
311
+ img = self.final_layer(img, vec_orig, **extra_kwargs) # (N, T, patch_size ** 2 * out_channels)
312
+ return img
313
+
314
+ def process_img(self, x, index=0, h_offset=0, w_offset=0, transformer_options={}):
315
+ bs, c, h, w = x.shape
316
+ patch_size = self.patch_size
317
+ x = comfy.ldm.common_dit.pad_to_patch_size(x, (patch_size, patch_size))
318
+
319
+ img = rearrange(x, "b c (h ph) (w pw) -> b (h w) (c ph pw)", ph=patch_size, pw=patch_size)
320
+ h_len = ((h + (patch_size // 2)) // patch_size)
321
+ w_len = ((w + (patch_size // 2)) // patch_size)
322
+
323
+ h_offset = ((h_offset + (patch_size // 2)) // patch_size)
324
+ w_offset = ((w_offset + (patch_size // 2)) // patch_size)
325
+
326
+ steps_h = h_len
327
+ steps_w = w_len
328
+
329
+ rope_options = transformer_options.get("rope_options", None)
330
+ if rope_options is not None:
331
+ h_len = (h_len - 1.0) * rope_options.get("scale_y", 1.0) + 1.0
332
+ w_len = (w_len - 1.0) * rope_options.get("scale_x", 1.0) + 1.0
333
+
334
+ index += rope_options.get("shift_t", 0.0)
335
+ h_offset += rope_options.get("shift_y", 0.0)
336
+ w_offset += rope_options.get("shift_x", 0.0)
337
+
338
+ img_ids = torch.zeros((steps_h, steps_w, len(self.params.axes_dim)), device=x.device, dtype=torch.float32)
339
+ img_ids[:, :, 0] = img_ids[:, :, 1] + index
340
+ img_ids[:, :, 1] = img_ids[:, :, 1] + torch.linspace(h_offset, h_len - 1 + h_offset, steps=steps_h, device=x.device, dtype=torch.float32).unsqueeze(1)
341
+ img_ids[:, :, 2] = img_ids[:, :, 2] + torch.linspace(w_offset, w_len - 1 + w_offset, steps=steps_w, device=x.device, dtype=torch.float32).unsqueeze(0)
342
+ return img, repeat(img_ids, "h w c -> b (h w) c", b=bs)
343
+
344
+ def forward(self, x, timestep, context, y=None, guidance=None, ref_latents=None, control=None, transformer_options={}, **kwargs):
345
+ return comfy.patcher_extension.WrapperExecutor.new_class_executor(
346
+ self._forward,
347
+ self,
348
+ comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, transformer_options)
349
+ ).execute(x, timestep, context, y, guidance, ref_latents, control, transformer_options, **kwargs)
350
+
351
+ def _forward(self, x, timestep, context, y=None, guidance=None, ref_latents=None, control=None, transformer_options={}, **kwargs):
352
+ bs, c, h_orig, w_orig = x.shape
353
+ patch_size = self.patch_size
354
+
355
+ h_len = ((h_orig + (patch_size // 2)) // patch_size)
356
+ w_len = ((w_orig + (patch_size // 2)) // patch_size)
357
+ img, img_ids = self.process_img(x, transformer_options=transformer_options)
358
+ img_tokens = img.shape[1]
359
+ timestep_zero_index = None
360
+ if ref_latents is not None:
361
+ ref_num_tokens = []
362
+ h = 0
363
+ w = 0
364
+ index = 0
365
+ ref_latents_method = kwargs.get("ref_latents_method", self.params.default_ref_method)
366
+ timestep_zero = ref_latents_method == "index_timestep_zero"
367
+ for ref in ref_latents:
368
+ if ref_latents_method in ("index", "index_timestep_zero"):
369
+ index += self.params.ref_index_scale
370
+ h_offset = 0
371
+ w_offset = 0
372
+ elif ref_latents_method == "uxo":
373
+ index = 0
374
+ h_offset = h_len * patch_size + h
375
+ w_offset = w_len * patch_size + w
376
+ h += ref.shape[-2]
377
+ w += ref.shape[-1]
378
+ else:
379
+ index = 1
380
+ h_offset = 0
381
+ w_offset = 0
382
+ if ref.shape[-2] + h > ref.shape[-1] + w:
383
+ w_offset = w
384
+ else:
385
+ h_offset = h
386
+ h = max(h, ref.shape[-2] + h_offset)
387
+ w = max(w, ref.shape[-1] + w_offset)
388
+
389
+ kontext, kontext_ids = self.process_img(ref, index=index, h_offset=h_offset, w_offset=w_offset, transformer_options=transformer_options)
390
+ img = torch.cat([img, kontext], dim=1)
391
+ img_ids = torch.cat([img_ids, kontext_ids], dim=1)
392
+ ref_num_tokens.append(kontext.shape[1])
393
+ if timestep_zero:
394
+ if index > 0:
395
+ timestep = torch.cat([timestep, timestep * 0], dim=0)
396
+ timestep_zero_index = [[img_tokens, img_ids.shape[1]]]
397
+ transformer_options = transformer_options.copy()
398
+ transformer_options["reference_image_num_tokens"] = ref_num_tokens
399
+
400
+ txt_ids = torch.zeros((bs, context.shape[1], len(self.params.axes_dim)), device=x.device, dtype=torch.float32)
401
+
402
+ if len(self.params.txt_ids_dims) > 0:
403
+ for i in self.params.txt_ids_dims:
404
+ txt_ids[:, :, i] = torch.linspace(0, context.shape[1] - 1, steps=context.shape[1], device=x.device, dtype=torch.float32)
405
+
406
+ out = self.forward_orig(img, img_ids, context, txt_ids, timestep, y, guidance, control, timestep_zero_index=timestep_zero_index, transformer_options=transformer_options, attn_mask=kwargs.get("attention_mask", None))
407
+ out = out[:, :img_tokens]
408
+ return rearrange(out, "b (h w) (c ph pw) -> b c (h ph) (w pw)", h=h_len, w=w_len, ph=self.patch_size, pw=self.patch_size)[:,:,:h_orig,:w_orig]
ComfyUI/comfy/ldm/flux/redux.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import comfy.ops
3
+
4
+ ops = comfy.ops.manual_cast
5
+
6
+ class ReduxImageEncoder(torch.nn.Module):
7
+ def __init__(
8
+ self,
9
+ redux_dim: int = 1152,
10
+ txt_in_features: int = 4096,
11
+ device=None,
12
+ dtype=None,
13
+ ) -> None:
14
+ super().__init__()
15
+
16
+ self.redux_dim = redux_dim
17
+ self.device = device
18
+ self.dtype = dtype
19
+
20
+ self.redux_up = ops.Linear(redux_dim, txt_in_features * 3, dtype=dtype)
21
+ self.redux_down = ops.Linear(txt_in_features * 3, txt_in_features, dtype=dtype)
22
+
23
+ def forward(self, sigclip_embeds) -> torch.Tensor:
24
+ projected_x = self.redux_down(torch.nn.functional.silu(self.redux_up(sigclip_embeds)))
25
+ return projected_x
ComfyUI/comfy/ldm/genmo/joint_model/asymm_models_joint.py ADDED
@@ -0,0 +1,561 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #original code from https://github.com/genmoai/models under apache 2.0 license
2
+ #adapted to ComfyUI
3
+
4
+ from typing import Dict, List, Optional, Tuple
5
+
6
+ import torch
7
+ import torch.nn as nn
8
+ import torch.nn.functional as F
9
+ from einops import rearrange
10
+ # from flash_attn import flash_attn_varlen_qkvpacked_func
11
+ from comfy.ldm.modules.attention import optimized_attention
12
+
13
+ from .layers import (
14
+ FeedForward,
15
+ PatchEmbed,
16
+ TimestepEmbedder,
17
+ )
18
+
19
+ from .rope_mixed import (
20
+ compute_mixed_rotation,
21
+ create_position_matrix,
22
+ )
23
+ from .temporal_rope import apply_rotary_emb_qk_real
24
+ from .utils import (
25
+ AttentionPool,
26
+ modulate,
27
+ )
28
+
29
+ import comfy.ldm.common_dit
30
+ import comfy.ops
31
+
32
+
33
+ def modulated_rmsnorm(x, scale, eps=1e-6):
34
+ # Normalize and modulate
35
+ x_normed = comfy.ldm.common_dit.rms_norm(x, eps=eps)
36
+ x_modulated = x_normed * (1 + scale.unsqueeze(1))
37
+
38
+ return x_modulated
39
+
40
+
41
+ def residual_tanh_gated_rmsnorm(x, x_res, gate, eps=1e-6):
42
+ # Apply tanh to gate
43
+ tanh_gate = torch.tanh(gate).unsqueeze(1)
44
+
45
+ # Normalize and apply gated scaling
46
+ x_normed = comfy.ldm.common_dit.rms_norm(x_res, eps=eps) * tanh_gate
47
+
48
+ # Apply residual connection
49
+ output = x + x_normed
50
+
51
+ return output
52
+
53
+ class AsymmetricAttention(nn.Module):
54
+ def __init__(
55
+ self,
56
+ dim_x: int,
57
+ dim_y: int,
58
+ num_heads: int = 8,
59
+ qkv_bias: bool = True,
60
+ qk_norm: bool = False,
61
+ attn_drop: float = 0.0,
62
+ update_y: bool = True,
63
+ out_bias: bool = True,
64
+ attend_to_padding: bool = False,
65
+ softmax_scale: Optional[float] = None,
66
+ device: Optional[torch.device] = None,
67
+ dtype=None,
68
+ operations=None,
69
+ ):
70
+ super().__init__()
71
+ self.dim_x = dim_x
72
+ self.dim_y = dim_y
73
+ self.num_heads = num_heads
74
+ self.head_dim = dim_x // num_heads
75
+ self.attn_drop = attn_drop
76
+ self.update_y = update_y
77
+ self.attend_to_padding = attend_to_padding
78
+ self.softmax_scale = softmax_scale
79
+ if dim_x % num_heads != 0:
80
+ raise ValueError(
81
+ f"dim_x={dim_x} should be divisible by num_heads={num_heads}"
82
+ )
83
+
84
+ # Input layers.
85
+ self.qkv_bias = qkv_bias
86
+ self.qkv_x = operations.Linear(dim_x, 3 * dim_x, bias=qkv_bias, device=device, dtype=dtype)
87
+ # Project text features to match visual features (dim_y -> dim_x)
88
+ self.qkv_y = operations.Linear(dim_y, 3 * dim_x, bias=qkv_bias, device=device, dtype=dtype)
89
+
90
+ # Query and key normalization for stability.
91
+ assert qk_norm
92
+ self.q_norm_x = operations.RMSNorm(self.head_dim, eps=1e-5, device=device, dtype=dtype)
93
+ self.k_norm_x = operations.RMSNorm(self.head_dim, eps=1e-5, device=device, dtype=dtype)
94
+ self.q_norm_y = operations.RMSNorm(self.head_dim, eps=1e-5, device=device, dtype=dtype)
95
+ self.k_norm_y = operations.RMSNorm(self.head_dim, eps=1e-5, device=device, dtype=dtype)
96
+
97
+ # Output layers. y features go back down from dim_x -> dim_y.
98
+ self.proj_x = operations.Linear(dim_x, dim_x, bias=out_bias, device=device, dtype=dtype)
99
+ self.proj_y = (
100
+ operations.Linear(dim_x, dim_y, bias=out_bias, device=device, dtype=dtype)
101
+ if update_y
102
+ else nn.Identity()
103
+ )
104
+
105
+ def forward(
106
+ self,
107
+ x: torch.Tensor, # (B, N, dim_x)
108
+ y: torch.Tensor, # (B, L, dim_y)
109
+ scale_x: torch.Tensor, # (B, dim_x), modulation for pre-RMSNorm.
110
+ scale_y: torch.Tensor, # (B, dim_y), modulation for pre-RMSNorm.
111
+ crop_y,
112
+ transformer_options={},
113
+ **rope_rotation,
114
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
115
+ rope_cos = rope_rotation.get("rope_cos")
116
+ rope_sin = rope_rotation.get("rope_sin")
117
+ # Pre-norm for visual features
118
+ x = modulated_rmsnorm(x, scale_x) # (B, M, dim_x) where M = N / cp_group_size
119
+
120
+ # Process visual features
121
+ # qkv_x = self.qkv_x(x) # (B, M, 3 * dim_x)
122
+ # assert qkv_x.dtype == torch.bfloat16
123
+ # qkv_x = all_to_all_collect_tokens(
124
+ # qkv_x, self.num_heads
125
+ # ) # (3, B, N, local_h, head_dim)
126
+
127
+ # Process text features
128
+ y = modulated_rmsnorm(y, scale_y) # (B, L, dim_y)
129
+ q_y, k_y, v_y = self.qkv_y(y).view(y.shape[0], y.shape[1], 3, self.num_heads, -1).unbind(2) # (B, N, local_h, head_dim)
130
+
131
+ q_y = self.q_norm_y(q_y)
132
+ k_y = self.k_norm_y(k_y)
133
+
134
+ # Split qkv_x into q, k, v
135
+ q_x, k_x, v_x = self.qkv_x(x).view(x.shape[0], x.shape[1], 3, self.num_heads, -1).unbind(2) # (B, N, local_h, head_dim)
136
+ q_x = self.q_norm_x(q_x)
137
+ q_x = apply_rotary_emb_qk_real(q_x, rope_cos, rope_sin)
138
+ k_x = self.k_norm_x(k_x)
139
+ k_x = apply_rotary_emb_qk_real(k_x, rope_cos, rope_sin)
140
+
141
+ q = torch.cat([q_x, q_y[:, :crop_y]], dim=1).transpose(1, 2)
142
+ k = torch.cat([k_x, k_y[:, :crop_y]], dim=1).transpose(1, 2)
143
+ v = torch.cat([v_x, v_y[:, :crop_y]], dim=1).transpose(1, 2)
144
+
145
+ xy = optimized_attention(q,
146
+ k,
147
+ v, self.num_heads, skip_reshape=True, transformer_options=transformer_options)
148
+
149
+ x, y = torch.tensor_split(xy, (q_x.shape[1],), dim=1)
150
+ x = self.proj_x(x)
151
+ o = torch.zeros(y.shape[0], q_y.shape[1], y.shape[-1], device=y.device, dtype=y.dtype)
152
+ o[:, :y.shape[1]] = y
153
+
154
+ y = self.proj_y(o)
155
+ # print("ox", x)
156
+ # print("oy", y)
157
+ return x, y
158
+
159
+
160
+ class AsymmetricJointBlock(nn.Module):
161
+ def __init__(
162
+ self,
163
+ hidden_size_x: int,
164
+ hidden_size_y: int,
165
+ num_heads: int,
166
+ *,
167
+ mlp_ratio_x: float = 8.0, # Ratio of hidden size to d_model for MLP for visual tokens.
168
+ mlp_ratio_y: float = 4.0, # Ratio of hidden size to d_model for MLP for text tokens.
169
+ update_y: bool = True, # Whether to update text tokens in this block.
170
+ device: Optional[torch.device] = None,
171
+ dtype=None,
172
+ operations=None,
173
+ **block_kwargs,
174
+ ):
175
+ super().__init__()
176
+ self.update_y = update_y
177
+ self.hidden_size_x = hidden_size_x
178
+ self.hidden_size_y = hidden_size_y
179
+ self.mod_x = operations.Linear(hidden_size_x, 4 * hidden_size_x, device=device, dtype=dtype)
180
+ if self.update_y:
181
+ self.mod_y = operations.Linear(hidden_size_x, 4 * hidden_size_y, device=device, dtype=dtype)
182
+ else:
183
+ self.mod_y = operations.Linear(hidden_size_x, hidden_size_y, device=device, dtype=dtype)
184
+
185
+ # Self-attention:
186
+ self.attn = AsymmetricAttention(
187
+ hidden_size_x,
188
+ hidden_size_y,
189
+ num_heads=num_heads,
190
+ update_y=update_y,
191
+ device=device,
192
+ dtype=dtype,
193
+ operations=operations,
194
+ **block_kwargs,
195
+ )
196
+
197
+ # MLP.
198
+ mlp_hidden_dim_x = int(hidden_size_x * mlp_ratio_x)
199
+ assert mlp_hidden_dim_x == int(1536 * 8)
200
+ self.mlp_x = FeedForward(
201
+ in_features=hidden_size_x,
202
+ hidden_size=mlp_hidden_dim_x,
203
+ multiple_of=256,
204
+ ffn_dim_multiplier=None,
205
+ device=device,
206
+ dtype=dtype,
207
+ operations=operations,
208
+ )
209
+
210
+ # MLP for text not needed in last block.
211
+ if self.update_y:
212
+ mlp_hidden_dim_y = int(hidden_size_y * mlp_ratio_y)
213
+ self.mlp_y = FeedForward(
214
+ in_features=hidden_size_y,
215
+ hidden_size=mlp_hidden_dim_y,
216
+ multiple_of=256,
217
+ ffn_dim_multiplier=None,
218
+ device=device,
219
+ dtype=dtype,
220
+ operations=operations,
221
+ )
222
+
223
+ def forward(
224
+ self,
225
+ x: torch.Tensor,
226
+ c: torch.Tensor,
227
+ y: torch.Tensor,
228
+ transformer_options={},
229
+ **attn_kwargs,
230
+ ):
231
+ """Forward pass of a block.
232
+
233
+ Args:
234
+ x: (B, N, dim) tensor of visual tokens
235
+ c: (B, dim) tensor of conditioned features
236
+ y: (B, L, dim) tensor of text tokens
237
+ num_frames: Number of frames in the video. N = num_frames * num_spatial_tokens
238
+
239
+ Returns:
240
+ x: (B, N, dim) tensor of visual tokens after block
241
+ y: (B, L, dim) tensor of text tokens after block
242
+ """
243
+ N = x.size(1)
244
+
245
+ c = F.silu(c)
246
+ mod_x = self.mod_x(c)
247
+ scale_msa_x, gate_msa_x, scale_mlp_x, gate_mlp_x = mod_x.chunk(4, dim=1)
248
+
249
+ mod_y = self.mod_y(c)
250
+ if self.update_y:
251
+ scale_msa_y, gate_msa_y, scale_mlp_y, gate_mlp_y = mod_y.chunk(4, dim=1)
252
+ else:
253
+ scale_msa_y = mod_y
254
+
255
+ # Self-attention block.
256
+ x_attn, y_attn = self.attn(
257
+ x,
258
+ y,
259
+ scale_x=scale_msa_x,
260
+ scale_y=scale_msa_y,
261
+ transformer_options=transformer_options,
262
+ **attn_kwargs,
263
+ )
264
+
265
+ assert x_attn.size(1) == N
266
+ x = residual_tanh_gated_rmsnorm(x, x_attn, gate_msa_x)
267
+ if self.update_y:
268
+ y = residual_tanh_gated_rmsnorm(y, y_attn, gate_msa_y)
269
+
270
+ # MLP block.
271
+ x = self.ff_block_x(x, scale_mlp_x, gate_mlp_x)
272
+ if self.update_y:
273
+ y = self.ff_block_y(y, scale_mlp_y, gate_mlp_y)
274
+
275
+ return x, y
276
+
277
+ def ff_block_x(self, x, scale_x, gate_x):
278
+ x_mod = modulated_rmsnorm(x, scale_x)
279
+ x_res = self.mlp_x(x_mod)
280
+ x = residual_tanh_gated_rmsnorm(x, x_res, gate_x) # Sandwich norm
281
+ return x
282
+
283
+ def ff_block_y(self, y, scale_y, gate_y):
284
+ y_mod = modulated_rmsnorm(y, scale_y)
285
+ y_res = self.mlp_y(y_mod)
286
+ y = residual_tanh_gated_rmsnorm(y, y_res, gate_y) # Sandwich norm
287
+ return y
288
+
289
+
290
+ class FinalLayer(nn.Module):
291
+ """
292
+ The final layer of DiT.
293
+ """
294
+
295
+ def __init__(
296
+ self,
297
+ hidden_size,
298
+ patch_size,
299
+ out_channels,
300
+ device: Optional[torch.device] = None,
301
+ dtype=None,
302
+ operations=None,
303
+ ):
304
+ super().__init__()
305
+ self.norm_final = operations.LayerNorm(
306
+ hidden_size, elementwise_affine=False, eps=1e-6, device=device, dtype=dtype
307
+ )
308
+ self.mod = operations.Linear(hidden_size, 2 * hidden_size, device=device, dtype=dtype)
309
+ self.linear = operations.Linear(
310
+ hidden_size, patch_size * patch_size * out_channels, device=device, dtype=dtype
311
+ )
312
+
313
+ def forward(self, x, c):
314
+ c = F.silu(c)
315
+ shift, scale = self.mod(c).chunk(2, dim=1)
316
+ x = modulate(self.norm_final(x), shift, scale)
317
+ x = self.linear(x)
318
+ return x
319
+
320
+
321
+ class AsymmDiTJoint(nn.Module):
322
+ """
323
+ Diffusion model with a Transformer backbone.
324
+
325
+ Ingests text embeddings instead of a label.
326
+ """
327
+
328
+ def __init__(
329
+ self,
330
+ *,
331
+ patch_size=2,
332
+ in_channels=4,
333
+ hidden_size_x=1152,
334
+ hidden_size_y=1152,
335
+ depth=48,
336
+ num_heads=16,
337
+ mlp_ratio_x=8.0,
338
+ mlp_ratio_y=4.0,
339
+ use_t5: bool = False,
340
+ t5_feat_dim: int = 4096,
341
+ t5_token_length: int = 256,
342
+ learn_sigma=True,
343
+ patch_embed_bias: bool = True,
344
+ timestep_mlp_bias: bool = True,
345
+ attend_to_padding: bool = False,
346
+ timestep_scale: Optional[float] = None,
347
+ use_extended_posenc: bool = False,
348
+ posenc_preserve_area: bool = False,
349
+ rope_theta: float = 10000.0,
350
+ image_model=None,
351
+ device: Optional[torch.device] = None,
352
+ dtype=None,
353
+ operations=None,
354
+ **block_kwargs,
355
+ ):
356
+ super().__init__()
357
+
358
+ self.dtype = dtype
359
+ self.learn_sigma = learn_sigma
360
+ self.in_channels = in_channels
361
+ self.out_channels = in_channels * 2 if learn_sigma else in_channels
362
+ self.patch_size = patch_size
363
+ self.num_heads = num_heads
364
+ self.hidden_size_x = hidden_size_x
365
+ self.hidden_size_y = hidden_size_y
366
+ self.head_dim = (
367
+ hidden_size_x // num_heads
368
+ ) # Head dimension and count is determined by visual.
369
+ self.attend_to_padding = attend_to_padding
370
+ self.use_extended_posenc = use_extended_posenc
371
+ self.posenc_preserve_area = posenc_preserve_area
372
+ self.use_t5 = use_t5
373
+ self.t5_token_length = t5_token_length
374
+ self.t5_feat_dim = t5_feat_dim
375
+ self.rope_theta = (
376
+ rope_theta # Scaling factor for frequency computation for temporal RoPE.
377
+ )
378
+
379
+ self.x_embedder = PatchEmbed(
380
+ patch_size=patch_size,
381
+ in_chans=in_channels,
382
+ embed_dim=hidden_size_x,
383
+ bias=patch_embed_bias,
384
+ dtype=dtype,
385
+ device=device,
386
+ operations=operations
387
+ )
388
+ # Conditionings
389
+ # Timestep
390
+ self.t_embedder = TimestepEmbedder(
391
+ hidden_size_x, bias=timestep_mlp_bias, timestep_scale=timestep_scale, dtype=dtype, device=device, operations=operations
392
+ )
393
+
394
+ if self.use_t5:
395
+ # Caption Pooling (T5)
396
+ self.t5_y_embedder = AttentionPool(
397
+ t5_feat_dim, num_heads=8, output_dim=hidden_size_x, dtype=dtype, device=device, operations=operations
398
+ )
399
+
400
+ # Dense Embedding Projection (T5)
401
+ self.t5_yproj = operations.Linear(
402
+ t5_feat_dim, hidden_size_y, bias=True, dtype=dtype, device=device
403
+ )
404
+
405
+ # Initialize pos_frequencies as an empty parameter.
406
+ self.pos_frequencies = nn.Parameter(
407
+ torch.empty(3, self.num_heads, self.head_dim // 2, dtype=dtype, device=device)
408
+ )
409
+
410
+ assert not self.attend_to_padding
411
+
412
+ # for depth 48:
413
+ # b = 0: AsymmetricJointBlock, update_y=True
414
+ # b = 1: AsymmetricJointBlock, update_y=True
415
+ # ...
416
+ # b = 46: AsymmetricJointBlock, update_y=True
417
+ # b = 47: AsymmetricJointBlock, update_y=False. No need to update text features.
418
+ blocks = []
419
+ for b in range(depth):
420
+ # Joint multi-modal block
421
+ update_y = b < depth - 1
422
+ block = AsymmetricJointBlock(
423
+ hidden_size_x,
424
+ hidden_size_y,
425
+ num_heads,
426
+ mlp_ratio_x=mlp_ratio_x,
427
+ mlp_ratio_y=mlp_ratio_y,
428
+ update_y=update_y,
429
+ attend_to_padding=attend_to_padding,
430
+ device=device,
431
+ dtype=dtype,
432
+ operations=operations,
433
+ **block_kwargs,
434
+ )
435
+
436
+ blocks.append(block)
437
+ self.blocks = nn.ModuleList(blocks)
438
+
439
+ self.final_layer = FinalLayer(
440
+ hidden_size_x, patch_size, self.out_channels, dtype=dtype, device=device, operations=operations
441
+ )
442
+
443
+ def embed_x(self, x: torch.Tensor) -> torch.Tensor:
444
+ """
445
+ Args:
446
+ x: (B, C=12, T, H, W) tensor of visual tokens
447
+
448
+ Returns:
449
+ x: (B, C=3072, N) tensor of visual tokens with positional embedding.
450
+ """
451
+ return self.x_embedder(x) # Convert BcTHW to BCN
452
+
453
+ def prepare(
454
+ self,
455
+ x: torch.Tensor,
456
+ sigma: torch.Tensor,
457
+ t5_feat: torch.Tensor,
458
+ t5_mask: torch.Tensor,
459
+ ):
460
+ """Prepare input and conditioning embeddings."""
461
+ # Visual patch embeddings with positional encoding.
462
+ T, H, W = x.shape[-3:]
463
+ pH, pW = H // self.patch_size, W // self.patch_size
464
+ x = self.embed_x(x) # (B, N, D), where N = T * H * W / patch_size ** 2
465
+ assert x.ndim == 3
466
+
467
+ pH, pW = H // self.patch_size, W // self.patch_size
468
+ N = T * pH * pW
469
+ assert x.size(1) == N
470
+ pos = create_position_matrix(
471
+ T, pH=pH, pW=pW, device=x.device, dtype=torch.float32
472
+ ) # (N, 3)
473
+ rope_cos, rope_sin = compute_mixed_rotation(
474
+ freqs=comfy.ops.cast_to(self.pos_frequencies, dtype=x.dtype, device=x.device), pos=pos
475
+ ) # Each are (N, num_heads, dim // 2)
476
+
477
+ c_t = self.t_embedder(1 - sigma, out_dtype=x.dtype) # (B, D)
478
+
479
+ t5_y_pool = self.t5_y_embedder(t5_feat, t5_mask) # (B, D)
480
+
481
+ c = c_t + t5_y_pool
482
+
483
+ y_feat = self.t5_yproj(t5_feat) # (B, L, t5_feat_dim) --> (B, L, D)
484
+
485
+ return x, c, y_feat, rope_cos, rope_sin
486
+
487
+ def forward(
488
+ self,
489
+ x: torch.Tensor,
490
+ timestep: torch.Tensor,
491
+ context: List[torch.Tensor],
492
+ attention_mask: List[torch.Tensor],
493
+ num_tokens=256,
494
+ packed_indices: Dict[str, torch.Tensor] = None,
495
+ rope_cos: torch.Tensor = None,
496
+ rope_sin: torch.Tensor = None,
497
+ control=None, transformer_options={}, **kwargs
498
+ ):
499
+ patches_replace = transformer_options.get("patches_replace", {})
500
+ y_feat = context
501
+ y_mask = attention_mask
502
+ sigma = timestep
503
+ """Forward pass of DiT.
504
+
505
+ Args:
506
+ x: (B, C, T, H, W) tensor of spatial inputs (images or latent representations of images)
507
+ sigma: (B,) tensor of noise standard deviations
508
+ y_feat: List((B, L, y_feat_dim) tensor of caption token features. For SDXL text encoders: L=77, y_feat_dim=2048)
509
+ y_mask: List((B, L) boolean tensor indicating which tokens are not padding)
510
+ packed_indices: Dict with keys for Flash Attention. Result of compute_packed_indices.
511
+ """
512
+ B, _, T, H, W = x.shape
513
+
514
+ x, c, y_feat, rope_cos, rope_sin = self.prepare(
515
+ x, sigma, y_feat, y_mask
516
+ )
517
+ del y_mask
518
+
519
+ blocks_replace = patches_replace.get("dit", {})
520
+ for i, block in enumerate(self.blocks):
521
+ if ("double_block", i) in blocks_replace:
522
+ def block_wrap(args):
523
+ out = {}
524
+ out["img"], out["txt"] = block(
525
+ args["img"],
526
+ args["vec"],
527
+ args["txt"],
528
+ rope_cos=args["rope_cos"],
529
+ rope_sin=args["rope_sin"],
530
+ crop_y=args["num_tokens"],
531
+ transformer_options=args["transformer_options"]
532
+ )
533
+ return out
534
+ out = blocks_replace[("double_block", i)]({"img": x, "txt": y_feat, "vec": c, "rope_cos": rope_cos, "rope_sin": rope_sin, "num_tokens": num_tokens, "transformer_options": transformer_options}, {"original_block": block_wrap})
535
+ y_feat = out["txt"]
536
+ x = out["img"]
537
+ else:
538
+ x, y_feat = block(
539
+ x,
540
+ c,
541
+ y_feat,
542
+ rope_cos=rope_cos,
543
+ rope_sin=rope_sin,
544
+ crop_y=num_tokens,
545
+ transformer_options=transformer_options,
546
+ ) # (B, M, D), (B, L, D)
547
+ del y_feat # Final layers don't use dense text features.
548
+
549
+ x = self.final_layer(x, c) # (B, M, patch_size ** 2 * out_channels)
550
+ x = rearrange(
551
+ x,
552
+ "B (T hp wp) (p1 p2 c) -> B c T (hp p1) (wp p2)",
553
+ T=T,
554
+ hp=H // self.patch_size,
555
+ wp=W // self.patch_size,
556
+ p1=self.patch_size,
557
+ p2=self.patch_size,
558
+ c=self.out_channels,
559
+ )
560
+
561
+ return -x
ComfyUI/comfy/ldm/genmo/joint_model/layers.py ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #original code from https://github.com/genmoai/models under apache 2.0 license
2
+ #adapted to ComfyUI
3
+
4
+ import collections.abc
5
+ import math
6
+ from itertools import repeat
7
+ from typing import Callable, Optional
8
+
9
+ import torch
10
+ import torch.nn as nn
11
+ import torch.nn.functional as F
12
+ from einops import rearrange
13
+ import comfy.ldm.common_dit
14
+
15
+
16
+ # From PyTorch internals
17
+ def _ntuple(n):
18
+ def parse(x):
19
+ if isinstance(x, collections.abc.Iterable) and not isinstance(x, str):
20
+ return tuple(x)
21
+ return tuple(repeat(x, n))
22
+
23
+ return parse
24
+
25
+
26
+ to_2tuple = _ntuple(2)
27
+
28
+
29
+ class TimestepEmbedder(nn.Module):
30
+ def __init__(
31
+ self,
32
+ hidden_size: int,
33
+ frequency_embedding_size: int = 256,
34
+ *,
35
+ bias: bool = True,
36
+ timestep_scale: Optional[float] = None,
37
+ dtype=None,
38
+ device=None,
39
+ operations=None,
40
+ ):
41
+ super().__init__()
42
+ self.mlp = nn.Sequential(
43
+ operations.Linear(frequency_embedding_size, hidden_size, bias=bias, dtype=dtype, device=device),
44
+ nn.SiLU(),
45
+ operations.Linear(hidden_size, hidden_size, bias=bias, dtype=dtype, device=device),
46
+ )
47
+ self.frequency_embedding_size = frequency_embedding_size
48
+ self.timestep_scale = timestep_scale
49
+
50
+ @staticmethod
51
+ def timestep_embedding(t, dim, max_period=10000):
52
+ half = dim // 2
53
+ freqs = torch.arange(start=0, end=half, dtype=torch.float32, device=t.device)
54
+ freqs.mul_(-math.log(max_period) / half).exp_()
55
+ args = t[:, None].float() * freqs[None]
56
+ embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
57
+ if dim % 2:
58
+ embedding = torch.cat(
59
+ [embedding, torch.zeros_like(embedding[:, :1])], dim=-1
60
+ )
61
+ return embedding
62
+
63
+ def forward(self, t, out_dtype):
64
+ if self.timestep_scale is not None:
65
+ t = t * self.timestep_scale
66
+ t_freq = self.timestep_embedding(t, self.frequency_embedding_size).to(dtype=out_dtype)
67
+ t_emb = self.mlp(t_freq)
68
+ return t_emb
69
+
70
+
71
+ class FeedForward(nn.Module):
72
+ def __init__(
73
+ self,
74
+ in_features: int,
75
+ hidden_size: int,
76
+ multiple_of: int,
77
+ ffn_dim_multiplier: Optional[float],
78
+ device: Optional[torch.device] = None,
79
+ dtype=None,
80
+ operations=None,
81
+ ):
82
+ super().__init__()
83
+ # keep parameter count and computation constant compared to standard FFN
84
+ hidden_size = int(2 * hidden_size / 3)
85
+ # custom dim factor multiplier
86
+ if ffn_dim_multiplier is not None:
87
+ hidden_size = int(ffn_dim_multiplier * hidden_size)
88
+ hidden_size = multiple_of * ((hidden_size + multiple_of - 1) // multiple_of)
89
+
90
+ self.hidden_dim = hidden_size
91
+ self.w1 = operations.Linear(in_features, 2 * hidden_size, bias=False, device=device, dtype=dtype)
92
+ self.w2 = operations.Linear(hidden_size, in_features, bias=False, device=device, dtype=dtype)
93
+
94
+ def forward(self, x):
95
+ x, gate = self.w1(x).chunk(2, dim=-1)
96
+ x = self.w2(F.silu(x) * gate)
97
+ return x
98
+
99
+
100
+ class PatchEmbed(nn.Module):
101
+ def __init__(
102
+ self,
103
+ patch_size: int = 16,
104
+ in_chans: int = 3,
105
+ embed_dim: int = 768,
106
+ norm_layer: Optional[Callable] = None,
107
+ flatten: bool = True,
108
+ bias: bool = True,
109
+ dynamic_img_pad: bool = False,
110
+ dtype=None,
111
+ device=None,
112
+ operations=None,
113
+ ):
114
+ super().__init__()
115
+ self.patch_size = to_2tuple(patch_size)
116
+ self.flatten = flatten
117
+ self.dynamic_img_pad = dynamic_img_pad
118
+
119
+ self.proj = operations.Conv2d(
120
+ in_chans,
121
+ embed_dim,
122
+ kernel_size=patch_size,
123
+ stride=patch_size,
124
+ bias=bias,
125
+ device=device,
126
+ dtype=dtype,
127
+ )
128
+ assert norm_layer is None
129
+ self.norm = (
130
+ norm_layer(embed_dim, device=device) if norm_layer else nn.Identity()
131
+ )
132
+
133
+ def forward(self, x):
134
+ B, _C, T, H, W = x.shape
135
+ if not self.dynamic_img_pad:
136
+ assert H % self.patch_size[0] == 0, f"Input height ({H}) should be divisible by patch size ({self.patch_size[0]})."
137
+ assert W % self.patch_size[1] == 0, f"Input width ({W}) should be divisible by patch size ({self.patch_size[1]})."
138
+ else:
139
+ pad_h = (self.patch_size[0] - H % self.patch_size[0]) % self.patch_size[0]
140
+ pad_w = (self.patch_size[1] - W % self.patch_size[1]) % self.patch_size[1]
141
+ x = F.pad(x, (0, pad_w, 0, pad_h))
142
+
143
+ x = rearrange(x, "B C T H W -> (B T) C H W", B=B, T=T)
144
+ x = comfy.ldm.common_dit.pad_to_patch_size(x, self.patch_size, padding_mode='circular')
145
+ x = self.proj(x)
146
+
147
+ # Flatten temporal and spatial dimensions.
148
+ if not self.flatten:
149
+ raise NotImplementedError("Must flatten output.")
150
+ x = rearrange(x, "(B T) C H W -> B (T H W) C", B=B, T=T)
151
+
152
+ x = self.norm(x)
153
+ return x
ComfyUI/comfy/ldm/genmo/joint_model/rope_mixed.py ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #original code from https://github.com/genmoai/models under apache 2.0 license
2
+
3
+ # import functools
4
+ import math
5
+
6
+ import torch
7
+
8
+
9
+ def centers(start: float, stop, num, dtype=None, device=None):
10
+ """linspace through bin centers.
11
+
12
+ Args:
13
+ start (float): Start of the range.
14
+ stop (float): End of the range.
15
+ num (int): Number of points.
16
+ dtype (torch.dtype): Data type of the points.
17
+ device (torch.device): Device of the points.
18
+
19
+ Returns:
20
+ centers (Tensor): Centers of the bins. Shape: (num,).
21
+ """
22
+ edges = torch.linspace(start, stop, num + 1, dtype=dtype, device=device)
23
+ return (edges[:-1] + edges[1:]) / 2
24
+
25
+
26
+ # @functools.lru_cache(maxsize=1)
27
+ def create_position_matrix(
28
+ T: int,
29
+ pH: int,
30
+ pW: int,
31
+ device: torch.device,
32
+ dtype: torch.dtype,
33
+ *,
34
+ target_area: float = 36864,
35
+ ):
36
+ """
37
+ Args:
38
+ T: int - Temporal dimension
39
+ pH: int - Height dimension after patchify
40
+ pW: int - Width dimension after patchify
41
+
42
+ Returns:
43
+ pos: [T * pH * pW, 3] - position matrix
44
+ """
45
+ # Create 1D tensors for each dimension
46
+ t = torch.arange(T, dtype=dtype)
47
+
48
+ # Positionally interpolate to area 36864.
49
+ # (3072x3072 frame with 16x16 patches = 192x192 latents).
50
+ # This automatically scales rope positions when the resolution changes.
51
+ # We use a large target area so the model is more sensitive
52
+ # to changes in the learned pos_frequencies matrix.
53
+ scale = math.sqrt(target_area / (pW * pH))
54
+ w = centers(-pW * scale / 2, pW * scale / 2, pW)
55
+ h = centers(-pH * scale / 2, pH * scale / 2, pH)
56
+
57
+ # Use meshgrid to create 3D grids
58
+ grid_t, grid_h, grid_w = torch.meshgrid(t, h, w, indexing="ij")
59
+
60
+ # Stack and reshape the grids.
61
+ pos = torch.stack([grid_t, grid_h, grid_w], dim=-1) # [T, pH, pW, 3]
62
+ pos = pos.view(-1, 3) # [T * pH * pW, 3]
63
+ pos = pos.to(dtype=dtype, device=device)
64
+
65
+ return pos
66
+
67
+
68
+ def compute_mixed_rotation(
69
+ freqs: torch.Tensor,
70
+ pos: torch.Tensor,
71
+ ):
72
+ """
73
+ Project each 3-dim position into per-head, per-head-dim 1D frequencies.
74
+
75
+ Args:
76
+ freqs: [3, num_heads, num_freqs] - learned rotation frequency (for t, row, col) for each head position
77
+ pos: [N, 3] - position of each token
78
+ num_heads: int
79
+
80
+ Returns:
81
+ freqs_cos: [N, num_heads, num_freqs] - cosine components
82
+ freqs_sin: [N, num_heads, num_freqs] - sine components
83
+ """
84
+ assert freqs.ndim == 3
85
+ freqs_sum = torch.einsum("Nd,dhf->Nhf", pos.to(freqs), freqs)
86
+ freqs_cos = torch.cos(freqs_sum)
87
+ freqs_sin = torch.sin(freqs_sum)
88
+ return freqs_cos, freqs_sin
ComfyUI/comfy/ldm/genmo/joint_model/temporal_rope.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #original code from https://github.com/genmoai/models under apache 2.0 license
2
+
3
+ # Based on Llama3 Implementation.
4
+ import torch
5
+
6
+
7
+ def apply_rotary_emb_qk_real(
8
+ xqk: torch.Tensor,
9
+ freqs_cos: torch.Tensor,
10
+ freqs_sin: torch.Tensor,
11
+ ) -> torch.Tensor:
12
+ """
13
+ Apply rotary embeddings to input tensors using the given frequency tensor without complex numbers.
14
+
15
+ Args:
16
+ xqk (torch.Tensor): Query and/or Key tensors to apply rotary embeddings. Shape: (B, S, *, num_heads, D)
17
+ Can be either just query or just key, or both stacked along some batch or * dim.
18
+ freqs_cos (torch.Tensor): Precomputed cosine frequency tensor.
19
+ freqs_sin (torch.Tensor): Precomputed sine frequency tensor.
20
+
21
+ Returns:
22
+ torch.Tensor: The input tensor with rotary embeddings applied.
23
+ """
24
+ # Split the last dimension into even and odd parts
25
+ xqk_even = xqk[..., 0::2]
26
+ xqk_odd = xqk[..., 1::2]
27
+
28
+ # Apply rotation
29
+ cos_part = (xqk_even * freqs_cos - xqk_odd * freqs_sin).type_as(xqk)
30
+ sin_part = (xqk_even * freqs_sin + xqk_odd * freqs_cos).type_as(xqk)
31
+
32
+ # Interleave the results back into the original shape
33
+ out = torch.stack([cos_part, sin_part], dim=-1).flatten(-2)
34
+ return out
ComfyUI/comfy/ldm/genmo/joint_model/utils.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #original code from https://github.com/genmoai/models under apache 2.0 license
2
+ #adapted to ComfyUI
3
+
4
+ from typing import Optional
5
+
6
+ import torch
7
+ import torch.nn as nn
8
+ import torch.nn.functional as F
9
+
10
+
11
+ def modulate(x, shift, scale):
12
+ return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1)
13
+
14
+
15
+ def pool_tokens(x: torch.Tensor, mask: torch.Tensor, *, keepdim=False) -> torch.Tensor:
16
+ """
17
+ Pool tokens in x using mask.
18
+
19
+ NOTE: We assume x does not require gradients.
20
+
21
+ Args:
22
+ x: (B, L, D) tensor of tokens.
23
+ mask: (B, L) boolean tensor indicating which tokens are not padding.
24
+
25
+ Returns:
26
+ pooled: (B, D) tensor of pooled tokens.
27
+ """
28
+ assert x.size(1) == mask.size(1) # Expected mask to have same length as tokens.
29
+ assert x.size(0) == mask.size(0) # Expected mask to have same batch size as tokens.
30
+ mask = mask[:, :, None].to(dtype=x.dtype)
31
+ mask = mask / mask.sum(dim=1, keepdim=True).clamp(min=1)
32
+ pooled = (x * mask).sum(dim=1, keepdim=keepdim)
33
+ return pooled
34
+
35
+
36
+ class AttentionPool(nn.Module):
37
+ def __init__(
38
+ self,
39
+ embed_dim: int,
40
+ num_heads: int,
41
+ output_dim: int = None,
42
+ device: Optional[torch.device] = None,
43
+ dtype=None,
44
+ operations=None,
45
+ ):
46
+ """
47
+ Args:
48
+ spatial_dim (int): Number of tokens in sequence length.
49
+ embed_dim (int): Dimensionality of input tokens.
50
+ num_heads (int): Number of attention heads.
51
+ output_dim (int): Dimensionality of output tokens. Defaults to embed_dim.
52
+ """
53
+ super().__init__()
54
+ self.num_heads = num_heads
55
+ self.to_kv = operations.Linear(embed_dim, 2 * embed_dim, device=device, dtype=dtype)
56
+ self.to_q = operations.Linear(embed_dim, embed_dim, device=device, dtype=dtype)
57
+ self.to_out = operations.Linear(embed_dim, output_dim or embed_dim, device=device, dtype=dtype)
58
+
59
+ def forward(self, x, mask):
60
+ """
61
+ Args:
62
+ x (torch.Tensor): (B, L, D) tensor of input tokens.
63
+ mask (torch.Tensor): (B, L) boolean tensor indicating which tokens are not padding.
64
+
65
+ NOTE: We assume x does not require gradients.
66
+
67
+ Returns:
68
+ x (torch.Tensor): (B, D) tensor of pooled tokens.
69
+ """
70
+ D = x.size(2)
71
+
72
+ # Construct attention mask, shape: (B, 1, num_queries=1, num_keys=1+L).
73
+ attn_mask = mask[:, None, None, :].bool() # (B, 1, 1, L).
74
+ attn_mask = F.pad(attn_mask, (1, 0), value=True) # (B, 1, 1, 1+L).
75
+
76
+ # Average non-padding token features. These will be used as the query.
77
+ x_pool = pool_tokens(x, mask, keepdim=True) # (B, 1, D)
78
+
79
+ # Concat pooled features to input sequence.
80
+ x = torch.cat([x_pool, x], dim=1) # (B, L+1, D)
81
+
82
+ # Compute queries, keys, values. Only the mean token is used to create a query.
83
+ kv = self.to_kv(x) # (B, L+1, 2 * D)
84
+ q = self.to_q(x[:, 0]) # (B, D)
85
+
86
+ # Extract heads.
87
+ head_dim = D // self.num_heads
88
+ kv = kv.unflatten(2, (2, self.num_heads, head_dim)) # (B, 1+L, 2, H, head_dim)
89
+ kv = kv.transpose(1, 3) # (B, H, 2, 1+L, head_dim)
90
+ k, v = kv.unbind(2) # (B, H, 1+L, head_dim)
91
+ q = q.unflatten(1, (self.num_heads, head_dim)) # (B, H, head_dim)
92
+ q = q.unsqueeze(2) # (B, H, 1, head_dim)
93
+
94
+ # Compute attention.
95
+ x = F.scaled_dot_product_attention(
96
+ q, k, v, attn_mask=attn_mask, dropout_p=0.0
97
+ ) # (B, H, 1, head_dim)
98
+
99
+ # Concatenate heads and run output.
100
+ x = x.squeeze(2).flatten(1, 2) # (B, D = H * head_dim)
101
+ x = self.to_out(x)
102
+ return x
ComfyUI/comfy/ldm/genmo/vae/model.py ADDED
@@ -0,0 +1,711 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #original code from https://github.com/genmoai/models under apache 2.0 license
2
+ #adapted to ComfyUI
3
+
4
+ from typing import List, Optional, Tuple, Union
5
+ from functools import partial
6
+ import math
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ import torch.nn.functional as F
11
+ from einops import rearrange
12
+
13
+ from comfy.ldm.modules.attention import optimized_attention
14
+
15
+ import comfy.ops
16
+ ops = comfy.ops.disable_weight_init
17
+
18
+ # import mochi_preview.dit.joint_model.context_parallel as cp
19
+ # from mochi_preview.vae.cp_conv import cp_pass_frames, gather_all_frames
20
+
21
+
22
+ def cast_tuple(t, length=1):
23
+ return t if isinstance(t, tuple) else ((t,) * length)
24
+
25
+
26
+ class GroupNormSpatial(ops.GroupNorm):
27
+ """
28
+ GroupNorm applied per-frame.
29
+ """
30
+
31
+ def forward(self, x: torch.Tensor, *, chunk_size: int = 8):
32
+ B, C, T, H, W = x.shape
33
+ x = rearrange(x, "B C T H W -> (B T) C H W")
34
+ # Run group norm in chunks.
35
+ output = torch.empty_like(x)
36
+ for b in range(0, B * T, chunk_size):
37
+ output[b : b + chunk_size] = super().forward(x[b : b + chunk_size])
38
+ return rearrange(output, "(B T) C H W -> B C T H W", B=B, T=T)
39
+
40
+ class PConv3d(ops.Conv3d):
41
+ def __init__(
42
+ self,
43
+ in_channels,
44
+ out_channels,
45
+ kernel_size: Union[int, Tuple[int, int, int]],
46
+ stride: Union[int, Tuple[int, int, int]],
47
+ causal: bool = True,
48
+ context_parallel: bool = True,
49
+ **kwargs,
50
+ ):
51
+ self.causal = causal
52
+ self.context_parallel = context_parallel
53
+ kernel_size = cast_tuple(kernel_size, 3)
54
+ stride = cast_tuple(stride, 3)
55
+ height_pad = (kernel_size[1] - 1) // 2
56
+ width_pad = (kernel_size[2] - 1) // 2
57
+
58
+ super().__init__(
59
+ in_channels=in_channels,
60
+ out_channels=out_channels,
61
+ kernel_size=kernel_size,
62
+ stride=stride,
63
+ dilation=(1, 1, 1),
64
+ padding=(0, height_pad, width_pad),
65
+ **kwargs,
66
+ )
67
+
68
+ def forward(self, x: torch.Tensor):
69
+ # Compute padding amounts.
70
+ context_size = self.kernel_size[0] - 1
71
+ if self.causal:
72
+ pad_front = context_size
73
+ pad_back = 0
74
+ else:
75
+ pad_front = context_size // 2
76
+ pad_back = context_size - pad_front
77
+
78
+ # Apply padding.
79
+ assert self.padding_mode == "replicate" # DEBUG
80
+ mode = "constant" if self.padding_mode == "zeros" else self.padding_mode
81
+ x = F.pad(x, (0, 0, 0, 0, pad_front, pad_back), mode=mode)
82
+ return super().forward(x)
83
+
84
+
85
+ class Conv1x1(ops.Linear):
86
+ """*1x1 Conv implemented with a linear layer."""
87
+
88
+ def __init__(self, in_features: int, out_features: int, *args, **kwargs):
89
+ super().__init__(in_features, out_features, *args, **kwargs)
90
+
91
+ def forward(self, x: torch.Tensor):
92
+ """Forward pass.
93
+
94
+ Args:
95
+ x: Input tensor. Shape: [B, C, *] or [B, *, C].
96
+
97
+ Returns:
98
+ x: Output tensor. Shape: [B, C', *] or [B, *, C'].
99
+ """
100
+ x = x.movedim(1, -1)
101
+ x = super().forward(x)
102
+ x = x.movedim(-1, 1)
103
+ return x
104
+
105
+
106
+ class DepthToSpaceTime(nn.Module):
107
+ def __init__(
108
+ self,
109
+ temporal_expansion: int,
110
+ spatial_expansion: int,
111
+ ):
112
+ super().__init__()
113
+ self.temporal_expansion = temporal_expansion
114
+ self.spatial_expansion = spatial_expansion
115
+
116
+ # When printed, this module should show the temporal and spatial expansion factors.
117
+ def extra_repr(self):
118
+ return f"texp={self.temporal_expansion}, sexp={self.spatial_expansion}"
119
+
120
+ def forward(self, x: torch.Tensor):
121
+ """Forward pass.
122
+
123
+ Args:
124
+ x: Input tensor. Shape: [B, C, T, H, W].
125
+
126
+ Returns:
127
+ x: Rearranged tensor. Shape: [B, C/(st*s*s), T*st, H*s, W*s].
128
+ """
129
+ x = rearrange(
130
+ x,
131
+ "B (C st sh sw) T H W -> B C (T st) (H sh) (W sw)",
132
+ st=self.temporal_expansion,
133
+ sh=self.spatial_expansion,
134
+ sw=self.spatial_expansion,
135
+ )
136
+
137
+ # cp_rank, _ = cp.get_cp_rank_size()
138
+ if self.temporal_expansion > 1: # and cp_rank == 0:
139
+ # Drop the first self.temporal_expansion - 1 frames.
140
+ # This is because we always want the 3x3x3 conv filter to only apply
141
+ # to the first frame, and the first frame doesn't need to be repeated.
142
+ assert all(x.shape)
143
+ x = x[:, :, self.temporal_expansion - 1 :]
144
+ assert all(x.shape)
145
+
146
+ return x
147
+
148
+
149
+ def norm_fn(
150
+ in_channels: int,
151
+ affine: bool = True,
152
+ ):
153
+ return GroupNormSpatial(affine=affine, num_groups=32, num_channels=in_channels)
154
+
155
+
156
+ class ResBlock(nn.Module):
157
+ """Residual block that preserves the spatial dimensions."""
158
+
159
+ def __init__(
160
+ self,
161
+ channels: int,
162
+ *,
163
+ affine: bool = True,
164
+ attn_block: Optional[nn.Module] = None,
165
+ causal: bool = True,
166
+ prune_bottleneck: bool = False,
167
+ padding_mode: str,
168
+ bias: bool = True,
169
+ ):
170
+ super().__init__()
171
+ self.channels = channels
172
+
173
+ assert causal
174
+ self.stack = nn.Sequential(
175
+ norm_fn(channels, affine=affine),
176
+ nn.SiLU(inplace=True),
177
+ PConv3d(
178
+ in_channels=channels,
179
+ out_channels=channels // 2 if prune_bottleneck else channels,
180
+ kernel_size=(3, 3, 3),
181
+ stride=(1, 1, 1),
182
+ padding_mode=padding_mode,
183
+ bias=bias,
184
+ causal=causal,
185
+ ),
186
+ norm_fn(channels, affine=affine),
187
+ nn.SiLU(inplace=True),
188
+ PConv3d(
189
+ in_channels=channels // 2 if prune_bottleneck else channels,
190
+ out_channels=channels,
191
+ kernel_size=(3, 3, 3),
192
+ stride=(1, 1, 1),
193
+ padding_mode=padding_mode,
194
+ bias=bias,
195
+ causal=causal,
196
+ ),
197
+ )
198
+
199
+ self.attn_block = attn_block if attn_block else nn.Identity()
200
+
201
+ def forward(self, x: torch.Tensor):
202
+ """Forward pass.
203
+
204
+ Args:
205
+ x: Input tensor. Shape: [B, C, T, H, W].
206
+ """
207
+ residual = x
208
+ x = self.stack(x)
209
+ x = x + residual
210
+ del residual
211
+
212
+ return self.attn_block(x)
213
+
214
+
215
+ class Attention(nn.Module):
216
+ def __init__(
217
+ self,
218
+ dim: int,
219
+ head_dim: int = 32,
220
+ qkv_bias: bool = False,
221
+ out_bias: bool = True,
222
+ qk_norm: bool = True,
223
+ ) -> None:
224
+ super().__init__()
225
+ self.head_dim = head_dim
226
+ self.num_heads = dim // head_dim
227
+ self.qk_norm = qk_norm
228
+
229
+ self.qkv = nn.Linear(dim, 3 * dim, bias=qkv_bias)
230
+ self.out = nn.Linear(dim, dim, bias=out_bias)
231
+
232
+ def forward(
233
+ self,
234
+ x: torch.Tensor,
235
+ ) -> torch.Tensor:
236
+ """Compute temporal self-attention.
237
+
238
+ Args:
239
+ x: Input tensor. Shape: [B, C, T, H, W].
240
+ chunk_size: Chunk size for large tensors.
241
+
242
+ Returns:
243
+ x: Output tensor. Shape: [B, C, T, H, W].
244
+ """
245
+ B, _, T, H, W = x.shape
246
+
247
+ if T == 1:
248
+ # No attention for single frame.
249
+ x = x.movedim(1, -1) # [B, C, T, H, W] -> [B, T, H, W, C]
250
+ qkv = self.qkv(x)
251
+ _, _, x = qkv.chunk(3, dim=-1) # Throw away queries and keys.
252
+ x = self.out(x)
253
+ return x.movedim(-1, 1) # [B, T, H, W, C] -> [B, C, T, H, W]
254
+
255
+ # 1D temporal attention.
256
+ x = rearrange(x, "B C t h w -> (B h w) t C")
257
+ qkv = self.qkv(x)
258
+
259
+ # Input: qkv with shape [B, t, 3 * num_heads * head_dim]
260
+ # Output: x with shape [B, num_heads, t, head_dim]
261
+ q, k, v = qkv.view(qkv.shape[0], qkv.shape[1], 3, self.num_heads, self.head_dim).transpose(1, 3).unbind(2)
262
+
263
+ if self.qk_norm:
264
+ q = F.normalize(q, p=2, dim=-1)
265
+ k = F.normalize(k, p=2, dim=-1)
266
+
267
+ x = optimized_attention(q, k, v, self.num_heads, skip_reshape=True)
268
+
269
+ assert x.size(0) == q.size(0)
270
+
271
+ x = self.out(x)
272
+ x = rearrange(x, "(B h w) t C -> B C t h w", B=B, h=H, w=W)
273
+ return x
274
+
275
+
276
+ class AttentionBlock(nn.Module):
277
+ def __init__(
278
+ self,
279
+ dim: int,
280
+ **attn_kwargs,
281
+ ) -> None:
282
+ super().__init__()
283
+ self.norm = norm_fn(dim)
284
+ self.attn = Attention(dim, **attn_kwargs)
285
+
286
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
287
+ return x + self.attn(self.norm(x))
288
+
289
+
290
+ class CausalUpsampleBlock(nn.Module):
291
+ def __init__(
292
+ self,
293
+ in_channels: int,
294
+ out_channels: int,
295
+ num_res_blocks: int,
296
+ *,
297
+ temporal_expansion: int = 2,
298
+ spatial_expansion: int = 2,
299
+ **block_kwargs,
300
+ ):
301
+ super().__init__()
302
+
303
+ blocks = []
304
+ for _ in range(num_res_blocks):
305
+ blocks.append(block_fn(in_channels, **block_kwargs))
306
+ self.blocks = nn.Sequential(*blocks)
307
+
308
+ self.temporal_expansion = temporal_expansion
309
+ self.spatial_expansion = spatial_expansion
310
+
311
+ # Change channels in the final convolution layer.
312
+ self.proj = Conv1x1(
313
+ in_channels,
314
+ out_channels * temporal_expansion * (spatial_expansion**2),
315
+ )
316
+
317
+ self.d2st = DepthToSpaceTime(
318
+ temporal_expansion=temporal_expansion, spatial_expansion=spatial_expansion
319
+ )
320
+
321
+ def forward(self, x):
322
+ x = self.blocks(x)
323
+ x = self.proj(x)
324
+ x = self.d2st(x)
325
+ return x
326
+
327
+
328
+ def block_fn(channels, *, affine: bool = True, has_attention: bool = False, **block_kwargs):
329
+ attn_block = AttentionBlock(channels) if has_attention else None
330
+ return ResBlock(channels, affine=affine, attn_block=attn_block, **block_kwargs)
331
+
332
+
333
+ class DownsampleBlock(nn.Module):
334
+ def __init__(
335
+ self,
336
+ in_channels: int,
337
+ out_channels: int,
338
+ num_res_blocks,
339
+ *,
340
+ temporal_reduction=2,
341
+ spatial_reduction=2,
342
+ **block_kwargs,
343
+ ):
344
+ """
345
+ Downsample block for the VAE encoder.
346
+
347
+ Args:
348
+ in_channels: Number of input channels.
349
+ out_channels: Number of output channels.
350
+ num_res_blocks: Number of residual blocks.
351
+ temporal_reduction: Temporal reduction factor.
352
+ spatial_reduction: Spatial reduction factor.
353
+ """
354
+ super().__init__()
355
+ layers = []
356
+
357
+ # Change the channel count in the strided convolution.
358
+ # This lets the ResBlock have uniform channel count,
359
+ # as in ConvNeXt.
360
+ assert in_channels != out_channels
361
+ layers.append(
362
+ PConv3d(
363
+ in_channels=in_channels,
364
+ out_channels=out_channels,
365
+ kernel_size=(temporal_reduction, spatial_reduction, spatial_reduction),
366
+ stride=(temporal_reduction, spatial_reduction, spatial_reduction),
367
+ # First layer in each block always uses replicate padding
368
+ padding_mode="replicate",
369
+ bias=block_kwargs["bias"],
370
+ )
371
+ )
372
+
373
+ for _ in range(num_res_blocks):
374
+ layers.append(block_fn(out_channels, **block_kwargs))
375
+
376
+ self.layers = nn.Sequential(*layers)
377
+
378
+ def forward(self, x):
379
+ return self.layers(x)
380
+
381
+
382
+ def add_fourier_features(inputs: torch.Tensor, start=6, stop=8, step=1):
383
+ num_freqs = (stop - start) // step
384
+ assert inputs.ndim == 5
385
+ C = inputs.size(1)
386
+
387
+ # Create Base 2 Fourier features.
388
+ freqs = torch.arange(start, stop, step, dtype=inputs.dtype, device=inputs.device)
389
+ assert num_freqs == len(freqs)
390
+ w = torch.pow(2.0, freqs) * (2 * torch.pi) # [num_freqs]
391
+ C = inputs.shape[1]
392
+ w = w.repeat(C)[None, :, None, None, None] # [1, C * num_freqs, 1, 1, 1]
393
+
394
+ # Interleaved repeat of input channels to match w.
395
+ h = inputs.repeat_interleave(num_freqs, dim=1) # [B, C * num_freqs, T, H, W]
396
+ # Scale channels by frequency.
397
+ h = w * h
398
+
399
+ return torch.cat(
400
+ [
401
+ inputs,
402
+ torch.sin(h),
403
+ torch.cos(h),
404
+ ],
405
+ dim=1,
406
+ )
407
+
408
+
409
+ class FourierFeatures(nn.Module):
410
+ def __init__(self, start: int = 6, stop: int = 8, step: int = 1):
411
+ super().__init__()
412
+ self.start = start
413
+ self.stop = stop
414
+ self.step = step
415
+
416
+ def forward(self, inputs):
417
+ """Add Fourier features to inputs.
418
+
419
+ Args:
420
+ inputs: Input tensor. Shape: [B, C, T, H, W]
421
+
422
+ Returns:
423
+ h: Output tensor. Shape: [B, (1 + 2 * num_freqs) * C, T, H, W]
424
+ """
425
+ return add_fourier_features(inputs, self.start, self.stop, self.step)
426
+
427
+
428
+ class Decoder(nn.Module):
429
+ def __init__(
430
+ self,
431
+ *,
432
+ out_channels: int = 3,
433
+ latent_dim: int,
434
+ base_channels: int,
435
+ channel_multipliers: List[int],
436
+ num_res_blocks: List[int],
437
+ temporal_expansions: Optional[List[int]] = None,
438
+ spatial_expansions: Optional[List[int]] = None,
439
+ has_attention: List[bool],
440
+ output_norm: bool = True,
441
+ nonlinearity: str = "silu",
442
+ output_nonlinearity: str = "silu",
443
+ causal: bool = True,
444
+ **block_kwargs,
445
+ ):
446
+ super().__init__()
447
+ self.input_channels = latent_dim
448
+ self.base_channels = base_channels
449
+ self.channel_multipliers = channel_multipliers
450
+ self.num_res_blocks = num_res_blocks
451
+ self.output_nonlinearity = output_nonlinearity
452
+ assert nonlinearity == "silu"
453
+ assert causal
454
+
455
+ ch = [mult * base_channels for mult in channel_multipliers]
456
+ self.num_up_blocks = len(ch) - 1
457
+ assert len(num_res_blocks) == self.num_up_blocks + 2
458
+
459
+ blocks = []
460
+
461
+ first_block = [
462
+ ops.Conv3d(latent_dim, ch[-1], kernel_size=(1, 1, 1))
463
+ ] # Input layer.
464
+ # First set of blocks preserve channel count.
465
+ for _ in range(num_res_blocks[-1]):
466
+ first_block.append(
467
+ block_fn(
468
+ ch[-1],
469
+ has_attention=has_attention[-1],
470
+ causal=causal,
471
+ **block_kwargs,
472
+ )
473
+ )
474
+ blocks.append(nn.Sequential(*first_block))
475
+
476
+ assert len(temporal_expansions) == len(spatial_expansions) == self.num_up_blocks
477
+ assert len(num_res_blocks) == len(has_attention) == self.num_up_blocks + 2
478
+
479
+ upsample_block_fn = CausalUpsampleBlock
480
+
481
+ for i in range(self.num_up_blocks):
482
+ block = upsample_block_fn(
483
+ ch[-i - 1],
484
+ ch[-i - 2],
485
+ num_res_blocks=num_res_blocks[-i - 2],
486
+ has_attention=has_attention[-i - 2],
487
+ temporal_expansion=temporal_expansions[-i - 1],
488
+ spatial_expansion=spatial_expansions[-i - 1],
489
+ causal=causal,
490
+ **block_kwargs,
491
+ )
492
+ blocks.append(block)
493
+
494
+ assert not output_norm
495
+
496
+ # Last block. Preserve channel count.
497
+ last_block = []
498
+ for _ in range(num_res_blocks[0]):
499
+ last_block.append(
500
+ block_fn(
501
+ ch[0], has_attention=has_attention[0], causal=causal, **block_kwargs
502
+ )
503
+ )
504
+ blocks.append(nn.Sequential(*last_block))
505
+
506
+ self.blocks = nn.ModuleList(blocks)
507
+ self.output_proj = Conv1x1(ch[0], out_channels)
508
+
509
+ def forward(self, x):
510
+ """Forward pass.
511
+
512
+ Args:
513
+ x: Latent tensor. Shape: [B, input_channels, t, h, w]. Scaled [-1, 1].
514
+
515
+ Returns:
516
+ x: Reconstructed video tensor. Shape: [B, C, T, H, W]. Scaled to [-1, 1].
517
+ T + 1 = (t - 1) * 4.
518
+ H = h * 16, W = w * 16.
519
+ """
520
+ for block in self.blocks:
521
+ x = block(x)
522
+
523
+ if self.output_nonlinearity == "silu":
524
+ x = F.silu(x, inplace=not self.training)
525
+ else:
526
+ assert (
527
+ not self.output_nonlinearity
528
+ ) # StyleGAN3 omits the to-RGB nonlinearity.
529
+
530
+ return self.output_proj(x).contiguous()
531
+
532
+ class LatentDistribution:
533
+ def __init__(self, mean: torch.Tensor, logvar: torch.Tensor):
534
+ """Initialize latent distribution.
535
+
536
+ Args:
537
+ mean: Mean of the distribution. Shape: [B, C, T, H, W].
538
+ logvar: Logarithm of variance of the distribution. Shape: [B, C, T, H, W].
539
+ """
540
+ assert mean.shape == logvar.shape
541
+ self.mean = mean
542
+ self.logvar = logvar
543
+
544
+ def sample(self, temperature=1.0, generator: torch.Generator = None, noise=None):
545
+ if temperature == 0.0:
546
+ return self.mean
547
+
548
+ if noise is None:
549
+ noise = torch.randn(self.mean.shape, device=self.mean.device, dtype=self.mean.dtype, generator=generator)
550
+ else:
551
+ assert noise.device == self.mean.device
552
+ noise = noise.to(self.mean.dtype)
553
+
554
+ if temperature != 1.0:
555
+ raise NotImplementedError(f"Temperature {temperature} is not supported.")
556
+
557
+ # Just Gaussian sample with no scaling of variance.
558
+ return noise * torch.exp(self.logvar * 0.5) + self.mean
559
+
560
+ def mode(self):
561
+ return self.mean
562
+
563
+ class Encoder(nn.Module):
564
+ def __init__(
565
+ self,
566
+ *,
567
+ in_channels: int,
568
+ base_channels: int,
569
+ channel_multipliers: List[int],
570
+ num_res_blocks: List[int],
571
+ latent_dim: int,
572
+ temporal_reductions: List[int],
573
+ spatial_reductions: List[int],
574
+ prune_bottlenecks: List[bool],
575
+ has_attentions: List[bool],
576
+ affine: bool = True,
577
+ bias: bool = True,
578
+ input_is_conv_1x1: bool = False,
579
+ padding_mode: str,
580
+ ):
581
+ super().__init__()
582
+ self.temporal_reductions = temporal_reductions
583
+ self.spatial_reductions = spatial_reductions
584
+ self.base_channels = base_channels
585
+ self.channel_multipliers = channel_multipliers
586
+ self.num_res_blocks = num_res_blocks
587
+ self.latent_dim = latent_dim
588
+
589
+ self.fourier_features = FourierFeatures()
590
+ ch = [mult * base_channels for mult in channel_multipliers]
591
+ num_down_blocks = len(ch) - 1
592
+ assert len(num_res_blocks) == num_down_blocks + 2
593
+
594
+ layers = (
595
+ [ops.Conv3d(in_channels, ch[0], kernel_size=(1, 1, 1), bias=True)]
596
+ if not input_is_conv_1x1
597
+ else [Conv1x1(in_channels, ch[0])]
598
+ )
599
+
600
+ assert len(prune_bottlenecks) == num_down_blocks + 2
601
+ assert len(has_attentions) == num_down_blocks + 2
602
+ block = partial(block_fn, padding_mode=padding_mode, affine=affine, bias=bias)
603
+
604
+ for _ in range(num_res_blocks[0]):
605
+ layers.append(block(ch[0], has_attention=has_attentions[0], prune_bottleneck=prune_bottlenecks[0]))
606
+ prune_bottlenecks = prune_bottlenecks[1:]
607
+ has_attentions = has_attentions[1:]
608
+
609
+ assert len(temporal_reductions) == len(spatial_reductions) == len(ch) - 1
610
+ for i in range(num_down_blocks):
611
+ layer = DownsampleBlock(
612
+ ch[i],
613
+ ch[i + 1],
614
+ num_res_blocks=num_res_blocks[i + 1],
615
+ temporal_reduction=temporal_reductions[i],
616
+ spatial_reduction=spatial_reductions[i],
617
+ prune_bottleneck=prune_bottlenecks[i],
618
+ has_attention=has_attentions[i],
619
+ affine=affine,
620
+ bias=bias,
621
+ padding_mode=padding_mode,
622
+ )
623
+
624
+ layers.append(layer)
625
+
626
+ # Additional blocks.
627
+ for _ in range(num_res_blocks[-1]):
628
+ layers.append(block(ch[-1], has_attention=has_attentions[-1], prune_bottleneck=prune_bottlenecks[-1]))
629
+
630
+ self.layers = nn.Sequential(*layers)
631
+
632
+ # Output layers.
633
+ self.output_norm = norm_fn(ch[-1])
634
+ self.output_proj = Conv1x1(ch[-1], 2 * latent_dim, bias=False)
635
+
636
+ @property
637
+ def temporal_downsample(self):
638
+ return math.prod(self.temporal_reductions)
639
+
640
+ @property
641
+ def spatial_downsample(self):
642
+ return math.prod(self.spatial_reductions)
643
+
644
+ def forward(self, x) -> LatentDistribution:
645
+ """Forward pass.
646
+
647
+ Args:
648
+ x: Input video tensor. Shape: [B, C, T, H, W]. Scaled to [-1, 1]
649
+
650
+ Returns:
651
+ means: Latent tensor. Shape: [B, latent_dim, t, h, w]. Scaled [-1, 1].
652
+ h = H // 8, w = W // 8, t - 1 = (T - 1) // 6
653
+ logvar: Shape: [B, latent_dim, t, h, w].
654
+ """
655
+ assert x.ndim == 5, f"Expected 5D input, got {x.shape}"
656
+ x = self.fourier_features(x)
657
+
658
+ x = self.layers(x)
659
+
660
+ x = self.output_norm(x)
661
+ x = F.silu(x, inplace=True)
662
+ x = self.output_proj(x)
663
+
664
+ means, logvar = torch.chunk(x, 2, dim=1)
665
+
666
+ assert means.ndim == 5
667
+ assert logvar.shape == means.shape
668
+ assert means.size(1) == self.latent_dim
669
+
670
+ return LatentDistribution(means, logvar)
671
+
672
+
673
+ class VideoVAE(nn.Module):
674
+ def __init__(self):
675
+ super().__init__()
676
+ self.encoder = Encoder(
677
+ in_channels=15,
678
+ base_channels=64,
679
+ channel_multipliers=[1, 2, 4, 6],
680
+ num_res_blocks=[3, 3, 4, 6, 3],
681
+ latent_dim=12,
682
+ temporal_reductions=[1, 2, 3],
683
+ spatial_reductions=[2, 2, 2],
684
+ prune_bottlenecks=[False, False, False, False, False],
685
+ has_attentions=[False, True, True, True, True],
686
+ affine=True,
687
+ bias=True,
688
+ input_is_conv_1x1=True,
689
+ padding_mode="replicate"
690
+ )
691
+ self.decoder = Decoder(
692
+ out_channels=3,
693
+ base_channels=128,
694
+ channel_multipliers=[1, 2, 4, 6],
695
+ temporal_expansions=[1, 2, 3],
696
+ spatial_expansions=[2, 2, 2],
697
+ num_res_blocks=[3, 3, 4, 6, 3],
698
+ latent_dim=12,
699
+ has_attention=[False, False, False, False, False],
700
+ padding_mode="replicate",
701
+ output_norm=False,
702
+ nonlinearity="silu",
703
+ output_nonlinearity="silu",
704
+ causal=True,
705
+ )
706
+
707
+ def encode(self, x):
708
+ return self.encoder(x).mode()
709
+
710
+ def decode(self, x):
711
+ return self.decoder(x)
ComfyUI/comfy/ldm/hidream/model.py ADDED
@@ -0,0 +1,829 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional, Tuple, List
2
+
3
+ import torch
4
+ import torch.nn as nn
5
+ import einops
6
+ from einops import repeat
7
+
8
+ from comfy.ldm.lightricks.model import TimestepEmbedding, Timesteps
9
+ import torch.nn.functional as F
10
+
11
+ from comfy.ldm.flux.math import apply_rope, rope
12
+ from comfy.ldm.flux.layers import LastLayer
13
+
14
+ from comfy.ldm.modules.attention import optimized_attention
15
+ import comfy.model_management
16
+ import comfy.patcher_extension
17
+ import comfy.ldm.common_dit
18
+
19
+
20
+ # Copied from https://github.com/black-forest-labs/flux/blob/main/src/flux/modules/layers.py
21
+ class EmbedND(nn.Module):
22
+ def __init__(self, theta: int, axes_dim: List[int]):
23
+ super().__init__()
24
+ self.theta = theta
25
+ self.axes_dim = axes_dim
26
+
27
+ def forward(self, ids: torch.Tensor) -> torch.Tensor:
28
+ n_axes = ids.shape[-1]
29
+ emb = torch.cat(
30
+ [rope(ids[..., i], self.axes_dim[i], self.theta) for i in range(n_axes)],
31
+ dim=-3,
32
+ )
33
+ return emb.unsqueeze(2)
34
+
35
+
36
+ class PatchEmbed(nn.Module):
37
+ def __init__(
38
+ self,
39
+ patch_size=2,
40
+ in_channels=4,
41
+ out_channels=1024,
42
+ dtype=None, device=None, operations=None
43
+ ):
44
+ super().__init__()
45
+ self.patch_size = patch_size
46
+ self.out_channels = out_channels
47
+ self.proj = operations.Linear(in_channels * patch_size * patch_size, out_channels, bias=True, dtype=dtype, device=device)
48
+
49
+ def forward(self, latent):
50
+ latent = self.proj(latent)
51
+ return latent
52
+
53
+
54
+ class PooledEmbed(nn.Module):
55
+ def __init__(self, text_emb_dim, hidden_size, dtype=None, device=None, operations=None):
56
+ super().__init__()
57
+ self.pooled_embedder = TimestepEmbedding(in_channels=text_emb_dim, time_embed_dim=hidden_size, dtype=dtype, device=device, operations=operations)
58
+
59
+ def forward(self, pooled_embed):
60
+ return self.pooled_embedder(pooled_embed)
61
+
62
+
63
+ class TimestepEmbed(nn.Module):
64
+ def __init__(self, hidden_size, frequency_embedding_size=256, dtype=None, device=None, operations=None):
65
+ super().__init__()
66
+ self.time_proj = Timesteps(num_channels=frequency_embedding_size, flip_sin_to_cos=True, downscale_freq_shift=0)
67
+ self.timestep_embedder = TimestepEmbedding(in_channels=frequency_embedding_size, time_embed_dim=hidden_size, dtype=dtype, device=device, operations=operations)
68
+
69
+ def forward(self, timesteps, wdtype):
70
+ t_emb = self.time_proj(timesteps).to(dtype=wdtype)
71
+ t_emb = self.timestep_embedder(t_emb)
72
+ return t_emb
73
+
74
+
75
+ def attention(query: torch.Tensor, key: torch.Tensor, value: torch.Tensor, transformer_options={}):
76
+ return optimized_attention(query.view(query.shape[0], -1, query.shape[-1] * query.shape[-2]), key.view(key.shape[0], -1, key.shape[-1] * key.shape[-2]), value.view(value.shape[0], -1, value.shape[-1] * value.shape[-2]), query.shape[2], transformer_options=transformer_options)
77
+
78
+
79
+ class HiDreamAttnProcessor_flashattn:
80
+ """Attention processor used typically in processing the SD3-like self-attention projections."""
81
+
82
+ def __call__(
83
+ self,
84
+ attn,
85
+ image_tokens: torch.FloatTensor,
86
+ image_tokens_masks: Optional[torch.FloatTensor] = None,
87
+ text_tokens: Optional[torch.FloatTensor] = None,
88
+ rope: torch.FloatTensor = None,
89
+ transformer_options={},
90
+ *args,
91
+ **kwargs,
92
+ ) -> torch.FloatTensor:
93
+ dtype = image_tokens.dtype
94
+ batch_size = image_tokens.shape[0]
95
+
96
+ query_i = attn.q_rms_norm(attn.to_q(image_tokens)).to(dtype=dtype)
97
+ key_i = attn.k_rms_norm(attn.to_k(image_tokens)).to(dtype=dtype)
98
+ value_i = attn.to_v(image_tokens)
99
+
100
+ inner_dim = key_i.shape[-1]
101
+ head_dim = inner_dim // attn.heads
102
+
103
+ query_i = query_i.view(batch_size, -1, attn.heads, head_dim)
104
+ key_i = key_i.view(batch_size, -1, attn.heads, head_dim)
105
+ value_i = value_i.view(batch_size, -1, attn.heads, head_dim)
106
+ if image_tokens_masks is not None:
107
+ key_i = key_i * image_tokens_masks.view(batch_size, -1, 1, 1)
108
+
109
+ if not attn.single:
110
+ query_t = attn.q_rms_norm_t(attn.to_q_t(text_tokens)).to(dtype=dtype)
111
+ key_t = attn.k_rms_norm_t(attn.to_k_t(text_tokens)).to(dtype=dtype)
112
+ value_t = attn.to_v_t(text_tokens)
113
+
114
+ query_t = query_t.view(batch_size, -1, attn.heads, head_dim)
115
+ key_t = key_t.view(batch_size, -1, attn.heads, head_dim)
116
+ value_t = value_t.view(batch_size, -1, attn.heads, head_dim)
117
+
118
+ num_image_tokens = query_i.shape[1]
119
+ num_text_tokens = query_t.shape[1]
120
+ query = torch.cat([query_i, query_t], dim=1)
121
+ key = torch.cat([key_i, key_t], dim=1)
122
+ value = torch.cat([value_i, value_t], dim=1)
123
+ else:
124
+ query = query_i
125
+ key = key_i
126
+ value = value_i
127
+
128
+ if query.shape[-1] == rope.shape[-3] * 2:
129
+ query, key = apply_rope(query, key, rope)
130
+ else:
131
+ query_1, query_2 = query.chunk(2, dim=-1)
132
+ key_1, key_2 = key.chunk(2, dim=-1)
133
+ query_1, key_1 = apply_rope(query_1, key_1, rope)
134
+ query = torch.cat([query_1, query_2], dim=-1)
135
+ key = torch.cat([key_1, key_2], dim=-1)
136
+
137
+ hidden_states = attention(query, key, value, transformer_options=transformer_options)
138
+
139
+ if not attn.single:
140
+ hidden_states_i, hidden_states_t = torch.split(hidden_states, [num_image_tokens, num_text_tokens], dim=1)
141
+ hidden_states_i = attn.to_out(hidden_states_i)
142
+ hidden_states_t = attn.to_out_t(hidden_states_t)
143
+ return hidden_states_i, hidden_states_t
144
+ else:
145
+ hidden_states = attn.to_out(hidden_states)
146
+ return hidden_states
147
+
148
+ class HiDreamAttention(nn.Module):
149
+ def __init__(
150
+ self,
151
+ query_dim: int,
152
+ heads: int = 8,
153
+ dim_head: int = 64,
154
+ upcast_attention: bool = False,
155
+ upcast_softmax: bool = False,
156
+ scale_qk: bool = True,
157
+ eps: float = 1e-5,
158
+ processor = None,
159
+ out_dim: int = None,
160
+ single: bool = False,
161
+ dtype=None, device=None, operations=None
162
+ ):
163
+ # super(Attention, self).__init__()
164
+ super().__init__()
165
+ self.inner_dim = out_dim if out_dim is not None else dim_head * heads
166
+ self.query_dim = query_dim
167
+ self.upcast_attention = upcast_attention
168
+ self.upcast_softmax = upcast_softmax
169
+ self.out_dim = out_dim if out_dim is not None else query_dim
170
+
171
+ self.scale_qk = scale_qk
172
+ self.scale = dim_head**-0.5 if self.scale_qk else 1.0
173
+
174
+ self.heads = out_dim // dim_head if out_dim is not None else heads
175
+ self.sliceable_head_dim = heads
176
+ self.single = single
177
+
178
+ linear_cls = operations.Linear
179
+ self.linear_cls = linear_cls
180
+ self.to_q = linear_cls(query_dim, self.inner_dim, dtype=dtype, device=device)
181
+ self.to_k = linear_cls(self.inner_dim, self.inner_dim, dtype=dtype, device=device)
182
+ self.to_v = linear_cls(self.inner_dim, self.inner_dim, dtype=dtype, device=device)
183
+ self.to_out = linear_cls(self.inner_dim, self.out_dim, dtype=dtype, device=device)
184
+ self.q_rms_norm = operations.RMSNorm(self.inner_dim, eps, dtype=dtype, device=device)
185
+ self.k_rms_norm = operations.RMSNorm(self.inner_dim, eps, dtype=dtype, device=device)
186
+
187
+ if not single:
188
+ self.to_q_t = linear_cls(query_dim, self.inner_dim, dtype=dtype, device=device)
189
+ self.to_k_t = linear_cls(self.inner_dim, self.inner_dim, dtype=dtype, device=device)
190
+ self.to_v_t = linear_cls(self.inner_dim, self.inner_dim, dtype=dtype, device=device)
191
+ self.to_out_t = linear_cls(self.inner_dim, self.out_dim, dtype=dtype, device=device)
192
+ self.q_rms_norm_t = operations.RMSNorm(self.inner_dim, eps, dtype=dtype, device=device)
193
+ self.k_rms_norm_t = operations.RMSNorm(self.inner_dim, eps, dtype=dtype, device=device)
194
+
195
+ self.processor = processor
196
+
197
+ def forward(
198
+ self,
199
+ norm_image_tokens: torch.FloatTensor,
200
+ image_tokens_masks: torch.FloatTensor = None,
201
+ norm_text_tokens: torch.FloatTensor = None,
202
+ rope: torch.FloatTensor = None,
203
+ transformer_options={},
204
+ ) -> torch.Tensor:
205
+ return self.processor(
206
+ self,
207
+ image_tokens = norm_image_tokens,
208
+ image_tokens_masks = image_tokens_masks,
209
+ text_tokens = norm_text_tokens,
210
+ rope = rope,
211
+ transformer_options=transformer_options,
212
+ )
213
+
214
+
215
+ class FeedForwardSwiGLU(nn.Module):
216
+ def __init__(
217
+ self,
218
+ dim: int,
219
+ hidden_dim: int,
220
+ multiple_of: int = 256,
221
+ ffn_dim_multiplier: Optional[float] = None,
222
+ dtype=None, device=None, operations=None
223
+ ):
224
+ super().__init__()
225
+ hidden_dim = int(2 * hidden_dim / 3)
226
+ # custom dim factor multiplier
227
+ if ffn_dim_multiplier is not None:
228
+ hidden_dim = int(ffn_dim_multiplier * hidden_dim)
229
+ hidden_dim = multiple_of * (
230
+ (hidden_dim + multiple_of - 1) // multiple_of
231
+ )
232
+
233
+ self.w1 = operations.Linear(dim, hidden_dim, bias=False, dtype=dtype, device=device)
234
+ self.w2 = operations.Linear(hidden_dim, dim, bias=False, dtype=dtype, device=device)
235
+ self.w3 = operations.Linear(dim, hidden_dim, bias=False, dtype=dtype, device=device)
236
+
237
+ def forward(self, x):
238
+ return self.w2(torch.nn.functional.silu(self.w1(x)) * self.w3(x))
239
+
240
+
241
+ # Modified from https://github.com/deepseek-ai/DeepSeek-V3/blob/main/inference/model.py
242
+ class MoEGate(nn.Module):
243
+ def __init__(self, embed_dim, num_routed_experts=4, num_activated_experts=2, aux_loss_alpha=0.01, dtype=None, device=None, operations=None):
244
+ super().__init__()
245
+ self.top_k = num_activated_experts
246
+ self.n_routed_experts = num_routed_experts
247
+
248
+ self.scoring_func = 'softmax'
249
+ self.alpha = aux_loss_alpha
250
+ self.seq_aux = False
251
+
252
+ # topk selection algorithm
253
+ self.norm_topk_prob = False
254
+ self.gating_dim = embed_dim
255
+ self.weight = nn.Parameter(torch.empty((self.n_routed_experts, self.gating_dim), dtype=dtype, device=device))
256
+ self.reset_parameters()
257
+
258
+ def reset_parameters(self) -> None:
259
+ pass
260
+ # import torch.nn.init as init
261
+ # init.kaiming_uniform_(self.weight, a=math.sqrt(5))
262
+
263
+ def forward(self, hidden_states):
264
+ bsz, seq_len, h = hidden_states.shape
265
+
266
+ ### compute gating score
267
+ hidden_states = hidden_states.view(-1, h)
268
+ logits = F.linear(hidden_states, comfy.model_management.cast_to(self.weight, dtype=hidden_states.dtype, device=hidden_states.device), None)
269
+ if self.scoring_func == 'softmax':
270
+ scores = logits.softmax(dim=-1)
271
+ else:
272
+ raise NotImplementedError(f'insupportable scoring function for MoE gating: {self.scoring_func}')
273
+
274
+ ### select top-k experts
275
+ topk_weight, topk_idx = torch.topk(scores, k=self.top_k, dim=-1, sorted=False)
276
+
277
+ ### norm gate to sum 1
278
+ if self.top_k > 1 and self.norm_topk_prob:
279
+ denominator = topk_weight.sum(dim=-1, keepdim=True) + 1e-20
280
+ topk_weight = topk_weight / denominator
281
+
282
+ aux_loss = None
283
+ return topk_idx, topk_weight, aux_loss
284
+
285
+
286
+ # Modified from https://github.com/deepseek-ai/DeepSeek-V3/blob/main/inference/model.py
287
+ class MOEFeedForwardSwiGLU(nn.Module):
288
+ def __init__(
289
+ self,
290
+ dim: int,
291
+ hidden_dim: int,
292
+ num_routed_experts: int,
293
+ num_activated_experts: int,
294
+ dtype=None, device=None, operations=None
295
+ ):
296
+ super().__init__()
297
+ self.shared_experts = FeedForwardSwiGLU(dim, hidden_dim // 2, dtype=dtype, device=device, operations=operations)
298
+ self.experts = nn.ModuleList([FeedForwardSwiGLU(dim, hidden_dim, dtype=dtype, device=device, operations=operations) for i in range(num_routed_experts)])
299
+ self.gate = MoEGate(
300
+ embed_dim = dim,
301
+ num_routed_experts = num_routed_experts,
302
+ num_activated_experts = num_activated_experts,
303
+ dtype=dtype, device=device, operations=operations
304
+ )
305
+ self.num_activated_experts = num_activated_experts
306
+
307
+ def forward(self, x):
308
+ wtype = x.dtype
309
+ identity = x
310
+ orig_shape = x.shape
311
+ topk_idx, topk_weight, aux_loss = self.gate(x)
312
+ x = x.view(-1, x.shape[-1])
313
+ flat_topk_idx = topk_idx.view(-1)
314
+ if True: # self.training: # TODO: check which branch performs faster
315
+ x = x.repeat_interleave(self.num_activated_experts, dim=0)
316
+ y = torch.empty_like(x, dtype=wtype)
317
+ for i, expert in enumerate(self.experts):
318
+ y[flat_topk_idx == i] = expert(x[flat_topk_idx == i]).to(dtype=wtype)
319
+ y = (y.view(*topk_weight.shape, -1) * topk_weight.unsqueeze(-1)).sum(dim=1)
320
+ y = y.view(*orig_shape).to(dtype=wtype)
321
+ #y = AddAuxiliaryLoss.apply(y, aux_loss)
322
+ else:
323
+ y = self.moe_infer(x, flat_topk_idx, topk_weight.view(-1, 1)).view(*orig_shape)
324
+ y = y + self.shared_experts(identity)
325
+ return y
326
+
327
+ @torch.no_grad()
328
+ def moe_infer(self, x, flat_expert_indices, flat_expert_weights):
329
+ expert_cache = torch.zeros_like(x)
330
+ idxs = flat_expert_indices.argsort()
331
+ tokens_per_expert = flat_expert_indices.bincount().cpu().numpy().cumsum(0)
332
+ token_idxs = idxs // self.num_activated_experts
333
+ for i, end_idx in enumerate(tokens_per_expert):
334
+ start_idx = 0 if i == 0 else tokens_per_expert[i-1]
335
+ if start_idx == end_idx:
336
+ continue
337
+ expert = self.experts[i]
338
+ exp_token_idx = token_idxs[start_idx:end_idx]
339
+ expert_tokens = x[exp_token_idx]
340
+ expert_out = expert(expert_tokens)
341
+ expert_out.mul_(flat_expert_weights[idxs[start_idx:end_idx]])
342
+
343
+ # for fp16 and other dtype
344
+ expert_cache = expert_cache.to(expert_out.dtype)
345
+ expert_cache.scatter_reduce_(0, exp_token_idx.view(-1, 1).repeat(1, x.shape[-1]), expert_out, reduce='sum')
346
+ return expert_cache
347
+
348
+
349
+ class TextProjection(nn.Module):
350
+ def __init__(self, in_features, hidden_size, dtype=None, device=None, operations=None):
351
+ super().__init__()
352
+ self.linear = operations.Linear(in_features=in_features, out_features=hidden_size, bias=False, dtype=dtype, device=device)
353
+
354
+ def forward(self, caption):
355
+ hidden_states = self.linear(caption)
356
+ return hidden_states
357
+
358
+
359
+ class BlockType:
360
+ TransformerBlock = 1
361
+ SingleTransformerBlock = 2
362
+
363
+
364
+ class HiDreamImageSingleTransformerBlock(nn.Module):
365
+ def __init__(
366
+ self,
367
+ dim: int,
368
+ num_attention_heads: int,
369
+ attention_head_dim: int,
370
+ num_routed_experts: int = 4,
371
+ num_activated_experts: int = 2,
372
+ dtype=None, device=None, operations=None
373
+ ):
374
+ super().__init__()
375
+ self.num_attention_heads = num_attention_heads
376
+ self.adaLN_modulation = nn.Sequential(
377
+ nn.SiLU(),
378
+ operations.Linear(dim, 6 * dim, bias=True, dtype=dtype, device=device)
379
+ )
380
+
381
+ # 1. Attention
382
+ self.norm1_i = operations.LayerNorm(dim, eps = 1e-06, elementwise_affine = False, dtype=dtype, device=device)
383
+ self.attn1 = HiDreamAttention(
384
+ query_dim=dim,
385
+ heads=num_attention_heads,
386
+ dim_head=attention_head_dim,
387
+ processor = HiDreamAttnProcessor_flashattn(),
388
+ single = True,
389
+ dtype=dtype, device=device, operations=operations
390
+ )
391
+
392
+ # 3. Feed-forward
393
+ self.norm3_i = operations.LayerNorm(dim, eps = 1e-06, elementwise_affine = False, dtype=dtype, device=device)
394
+ if num_routed_experts > 0:
395
+ self.ff_i = MOEFeedForwardSwiGLU(
396
+ dim = dim,
397
+ hidden_dim = 4 * dim,
398
+ num_routed_experts = num_routed_experts,
399
+ num_activated_experts = num_activated_experts,
400
+ dtype=dtype, device=device, operations=operations
401
+ )
402
+ else:
403
+ self.ff_i = FeedForwardSwiGLU(dim = dim, hidden_dim = 4 * dim, dtype=dtype, device=device, operations=operations)
404
+
405
+ def forward(
406
+ self,
407
+ image_tokens: torch.FloatTensor,
408
+ image_tokens_masks: Optional[torch.FloatTensor] = None,
409
+ text_tokens: Optional[torch.FloatTensor] = None,
410
+ adaln_input: Optional[torch.FloatTensor] = None,
411
+ rope: torch.FloatTensor = None,
412
+ transformer_options={},
413
+ ) -> torch.FloatTensor:
414
+ wtype = image_tokens.dtype
415
+ shift_msa_i, scale_msa_i, gate_msa_i, shift_mlp_i, scale_mlp_i, gate_mlp_i = \
416
+ self.adaLN_modulation(adaln_input)[:,None].chunk(6, dim=-1)
417
+
418
+ # 1. MM-Attention
419
+ norm_image_tokens = self.norm1_i(image_tokens).to(dtype=wtype)
420
+ norm_image_tokens = norm_image_tokens * (1 + scale_msa_i) + shift_msa_i
421
+ attn_output_i = self.attn1(
422
+ norm_image_tokens,
423
+ image_tokens_masks,
424
+ rope = rope,
425
+ transformer_options=transformer_options,
426
+ )
427
+ image_tokens = gate_msa_i * attn_output_i + image_tokens
428
+
429
+ # 2. Feed-forward
430
+ norm_image_tokens = self.norm3_i(image_tokens).to(dtype=wtype)
431
+ norm_image_tokens = norm_image_tokens * (1 + scale_mlp_i) + shift_mlp_i
432
+ ff_output_i = gate_mlp_i * self.ff_i(norm_image_tokens.to(dtype=wtype))
433
+ image_tokens = ff_output_i + image_tokens
434
+ return image_tokens
435
+
436
+
437
+ class HiDreamImageTransformerBlock(nn.Module):
438
+ def __init__(
439
+ self,
440
+ dim: int,
441
+ num_attention_heads: int,
442
+ attention_head_dim: int,
443
+ num_routed_experts: int = 4,
444
+ num_activated_experts: int = 2,
445
+ dtype=None, device=None, operations=None
446
+ ):
447
+ super().__init__()
448
+ self.num_attention_heads = num_attention_heads
449
+ self.adaLN_modulation = nn.Sequential(
450
+ nn.SiLU(),
451
+ operations.Linear(dim, 12 * dim, bias=True, dtype=dtype, device=device)
452
+ )
453
+ # nn.init.zeros_(self.adaLN_modulation[1].weight)
454
+ # nn.init.zeros_(self.adaLN_modulation[1].bias)
455
+
456
+ # 1. Attention
457
+ self.norm1_i = operations.LayerNorm(dim, eps = 1e-06, elementwise_affine = False, dtype=dtype, device=device)
458
+ self.norm1_t = operations.LayerNorm(dim, eps = 1e-06, elementwise_affine = False, dtype=dtype, device=device)
459
+ self.attn1 = HiDreamAttention(
460
+ query_dim=dim,
461
+ heads=num_attention_heads,
462
+ dim_head=attention_head_dim,
463
+ processor = HiDreamAttnProcessor_flashattn(),
464
+ single = False,
465
+ dtype=dtype, device=device, operations=operations
466
+ )
467
+
468
+ # 3. Feed-forward
469
+ self.norm3_i = operations.LayerNorm(dim, eps = 1e-06, elementwise_affine = False, dtype=dtype, device=device)
470
+ if num_routed_experts > 0:
471
+ self.ff_i = MOEFeedForwardSwiGLU(
472
+ dim = dim,
473
+ hidden_dim = 4 * dim,
474
+ num_routed_experts = num_routed_experts,
475
+ num_activated_experts = num_activated_experts,
476
+ dtype=dtype, device=device, operations=operations
477
+ )
478
+ else:
479
+ self.ff_i = FeedForwardSwiGLU(dim = dim, hidden_dim = 4 * dim, dtype=dtype, device=device, operations=operations)
480
+ self.norm3_t = operations.LayerNorm(dim, eps = 1e-06, elementwise_affine = False)
481
+ self.ff_t = FeedForwardSwiGLU(dim = dim, hidden_dim = 4 * dim, dtype=dtype, device=device, operations=operations)
482
+
483
+ def forward(
484
+ self,
485
+ image_tokens: torch.FloatTensor,
486
+ image_tokens_masks: Optional[torch.FloatTensor] = None,
487
+ text_tokens: Optional[torch.FloatTensor] = None,
488
+ adaln_input: Optional[torch.FloatTensor] = None,
489
+ rope: torch.FloatTensor = None,
490
+ transformer_options={},
491
+ ) -> torch.FloatTensor:
492
+ wtype = image_tokens.dtype
493
+ shift_msa_i, scale_msa_i, gate_msa_i, shift_mlp_i, scale_mlp_i, gate_mlp_i, \
494
+ shift_msa_t, scale_msa_t, gate_msa_t, shift_mlp_t, scale_mlp_t, gate_mlp_t = \
495
+ self.adaLN_modulation(adaln_input)[:,None].chunk(12, dim=-1)
496
+
497
+ # 1. MM-Attention
498
+ norm_image_tokens = self.norm1_i(image_tokens).to(dtype=wtype)
499
+ norm_image_tokens = norm_image_tokens * (1 + scale_msa_i) + shift_msa_i
500
+ norm_text_tokens = self.norm1_t(text_tokens).to(dtype=wtype)
501
+ norm_text_tokens = norm_text_tokens * (1 + scale_msa_t) + shift_msa_t
502
+
503
+ attn_output_i, attn_output_t = self.attn1(
504
+ norm_image_tokens,
505
+ image_tokens_masks,
506
+ norm_text_tokens,
507
+ rope = rope,
508
+ transformer_options=transformer_options,
509
+ )
510
+
511
+ image_tokens = gate_msa_i * attn_output_i + image_tokens
512
+ text_tokens = gate_msa_t * attn_output_t + text_tokens
513
+
514
+ # 2. Feed-forward
515
+ norm_image_tokens = self.norm3_i(image_tokens).to(dtype=wtype)
516
+ norm_image_tokens = norm_image_tokens * (1 + scale_mlp_i) + shift_mlp_i
517
+ norm_text_tokens = self.norm3_t(text_tokens).to(dtype=wtype)
518
+ norm_text_tokens = norm_text_tokens * (1 + scale_mlp_t) + shift_mlp_t
519
+
520
+ ff_output_i = gate_mlp_i * self.ff_i(norm_image_tokens)
521
+ ff_output_t = gate_mlp_t * self.ff_t(norm_text_tokens)
522
+ image_tokens = ff_output_i + image_tokens
523
+ text_tokens = ff_output_t + text_tokens
524
+ return image_tokens, text_tokens
525
+
526
+
527
+ class HiDreamImageBlock(nn.Module):
528
+ def __init__(
529
+ self,
530
+ dim: int,
531
+ num_attention_heads: int,
532
+ attention_head_dim: int,
533
+ num_routed_experts: int = 4,
534
+ num_activated_experts: int = 2,
535
+ block_type: BlockType = BlockType.TransformerBlock,
536
+ dtype=None, device=None, operations=None
537
+ ):
538
+ super().__init__()
539
+ block_classes = {
540
+ BlockType.TransformerBlock: HiDreamImageTransformerBlock,
541
+ BlockType.SingleTransformerBlock: HiDreamImageSingleTransformerBlock,
542
+ }
543
+ self.block = block_classes[block_type](
544
+ dim,
545
+ num_attention_heads,
546
+ attention_head_dim,
547
+ num_routed_experts,
548
+ num_activated_experts,
549
+ dtype=dtype, device=device, operations=operations
550
+ )
551
+
552
+ def forward(
553
+ self,
554
+ image_tokens: torch.FloatTensor,
555
+ image_tokens_masks: Optional[torch.FloatTensor] = None,
556
+ text_tokens: Optional[torch.FloatTensor] = None,
557
+ adaln_input: torch.FloatTensor = None,
558
+ rope: torch.FloatTensor = None,
559
+ transformer_options={},
560
+ ) -> torch.FloatTensor:
561
+ return self.block(
562
+ image_tokens,
563
+ image_tokens_masks,
564
+ text_tokens,
565
+ adaln_input,
566
+ rope,
567
+ transformer_options=transformer_options,
568
+ )
569
+
570
+
571
+ class HiDreamImageTransformer2DModel(nn.Module):
572
+ def __init__(
573
+ self,
574
+ patch_size: Optional[int] = None,
575
+ in_channels: int = 64,
576
+ out_channels: Optional[int] = None,
577
+ num_layers: int = 16,
578
+ num_single_layers: int = 32,
579
+ attention_head_dim: int = 128,
580
+ num_attention_heads: int = 20,
581
+ caption_channels: List[int] = None,
582
+ text_emb_dim: int = 2048,
583
+ num_routed_experts: int = 4,
584
+ num_activated_experts: int = 2,
585
+ axes_dims_rope: Tuple[int, int] = (32, 32),
586
+ max_resolution: Tuple[int, int] = (128, 128),
587
+ llama_layers: List[int] = None,
588
+ image_model=None,
589
+ dtype=None, device=None, operations=None
590
+ ):
591
+ self.patch_size = patch_size
592
+ self.num_attention_heads = num_attention_heads
593
+ self.attention_head_dim = attention_head_dim
594
+ self.num_layers = num_layers
595
+ self.num_single_layers = num_single_layers
596
+
597
+ self.gradient_checkpointing = False
598
+
599
+ super().__init__()
600
+ self.dtype = dtype
601
+ self.out_channels = out_channels or in_channels
602
+ self.inner_dim = self.num_attention_heads * self.attention_head_dim
603
+ self.llama_layers = llama_layers
604
+
605
+ self.t_embedder = TimestepEmbed(self.inner_dim, dtype=dtype, device=device, operations=operations)
606
+ self.p_embedder = PooledEmbed(text_emb_dim, self.inner_dim, dtype=dtype, device=device, operations=operations)
607
+ self.x_embedder = PatchEmbed(
608
+ patch_size = patch_size,
609
+ in_channels = in_channels,
610
+ out_channels = self.inner_dim,
611
+ dtype=dtype, device=device, operations=operations
612
+ )
613
+ self.pe_embedder = EmbedND(theta=10000, axes_dim=axes_dims_rope)
614
+
615
+ self.double_stream_blocks = nn.ModuleList(
616
+ [
617
+ HiDreamImageBlock(
618
+ dim = self.inner_dim,
619
+ num_attention_heads = self.num_attention_heads,
620
+ attention_head_dim = self.attention_head_dim,
621
+ num_routed_experts = num_routed_experts,
622
+ num_activated_experts = num_activated_experts,
623
+ block_type = BlockType.TransformerBlock,
624
+ dtype=dtype, device=device, operations=operations
625
+ )
626
+ for i in range(self.num_layers)
627
+ ]
628
+ )
629
+
630
+ self.single_stream_blocks = nn.ModuleList(
631
+ [
632
+ HiDreamImageBlock(
633
+ dim = self.inner_dim,
634
+ num_attention_heads = self.num_attention_heads,
635
+ attention_head_dim = self.attention_head_dim,
636
+ num_routed_experts = num_routed_experts,
637
+ num_activated_experts = num_activated_experts,
638
+ block_type = BlockType.SingleTransformerBlock,
639
+ dtype=dtype, device=device, operations=operations
640
+ )
641
+ for i in range(self.num_single_layers)
642
+ ]
643
+ )
644
+
645
+ self.final_layer = LastLayer(self.inner_dim, patch_size, self.out_channels, dtype=dtype, device=device, operations=operations)
646
+
647
+ caption_channels = [caption_channels[1], ] * (num_layers + num_single_layers) + [caption_channels[0], ]
648
+ caption_projection = []
649
+ for caption_channel in caption_channels:
650
+ caption_projection.append(TextProjection(in_features=caption_channel, hidden_size=self.inner_dim, dtype=dtype, device=device, operations=operations))
651
+ self.caption_projection = nn.ModuleList(caption_projection)
652
+ self.max_seq = max_resolution[0] * max_resolution[1] // (patch_size * patch_size)
653
+
654
+ def expand_timesteps(self, timesteps, batch_size, device):
655
+ if not torch.is_tensor(timesteps):
656
+ is_mps = device.type == "mps"
657
+ if isinstance(timesteps, float):
658
+ dtype = torch.float32 if is_mps else torch.float64
659
+ else:
660
+ dtype = torch.int32 if is_mps else torch.int64
661
+ timesteps = torch.tensor([timesteps], dtype=dtype, device=device)
662
+ elif len(timesteps.shape) == 0:
663
+ timesteps = timesteps[None].to(device)
664
+ # broadcast to batch dimension in a way that's compatible with ONNX/Core ML
665
+ timesteps = timesteps.expand(batch_size)
666
+ return timesteps
667
+
668
+ def unpatchify(self, x: torch.Tensor, img_sizes: List[Tuple[int, int]]) -> List[torch.Tensor]:
669
+ x_arr = []
670
+ for i, img_size in enumerate(img_sizes):
671
+ pH, pW = img_size
672
+ x_arr.append(
673
+ einops.rearrange(x[i, :pH*pW].reshape(1, pH, pW, -1), 'B H W (p1 p2 C) -> B C (H p1) (W p2)',
674
+ p1=self.patch_size, p2=self.patch_size)
675
+ )
676
+ x = torch.cat(x_arr, dim=0)
677
+ return x
678
+
679
+ def patchify(self, x, max_seq, img_sizes=None):
680
+ pz2 = self.patch_size * self.patch_size
681
+ if isinstance(x, torch.Tensor):
682
+ B = x.shape[0]
683
+ device = x.device
684
+ dtype = x.dtype
685
+ else:
686
+ B = len(x)
687
+ device = x[0].device
688
+ dtype = x[0].dtype
689
+ x_masks = torch.zeros((B, max_seq), dtype=dtype, device=device)
690
+
691
+ if img_sizes is not None:
692
+ for i, img_size in enumerate(img_sizes):
693
+ x_masks[i, 0:img_size[0] * img_size[1]] = 1
694
+ x = einops.rearrange(x, 'B C S p -> B S (p C)', p=pz2)
695
+ elif isinstance(x, torch.Tensor):
696
+ pH, pW = x.shape[-2] // self.patch_size, x.shape[-1] // self.patch_size
697
+ x = einops.rearrange(x, 'B C (H p1) (W p2) -> B (H W) (p1 p2 C)', p1=self.patch_size, p2=self.patch_size)
698
+ img_sizes = [[pH, pW]] * B
699
+ x_masks = None
700
+ else:
701
+ raise NotImplementedError
702
+ return x, x_masks, img_sizes
703
+
704
+ def forward(self,
705
+ x: torch.Tensor,
706
+ t: torch.Tensor,
707
+ y: Optional[torch.Tensor] = None,
708
+ context: Optional[torch.Tensor] = None,
709
+ encoder_hidden_states_llama3=None,
710
+ image_cond=None,
711
+ control = None,
712
+ transformer_options = {},
713
+ ):
714
+ return comfy.patcher_extension.WrapperExecutor.new_class_executor(
715
+ self._forward,
716
+ self,
717
+ comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, transformer_options)
718
+ ).execute(x, t, y, context, encoder_hidden_states_llama3, image_cond, control, transformer_options)
719
+
720
+ def _forward(
721
+ self,
722
+ x: torch.Tensor,
723
+ t: torch.Tensor,
724
+ y: Optional[torch.Tensor] = None,
725
+ context: Optional[torch.Tensor] = None,
726
+ encoder_hidden_states_llama3=None,
727
+ image_cond=None,
728
+ control = None,
729
+ transformer_options = {},
730
+ ) -> torch.Tensor:
731
+ bs, c, h, w = x.shape
732
+ if image_cond is not None:
733
+ x = torch.cat([x, image_cond], dim=-1)
734
+ hidden_states = comfy.ldm.common_dit.pad_to_patch_size(x, (self.patch_size, self.patch_size))
735
+ timesteps = t
736
+ pooled_embeds = y
737
+ T5_encoder_hidden_states = context
738
+
739
+ img_sizes = None
740
+
741
+ # spatial forward
742
+ batch_size = hidden_states.shape[0]
743
+ hidden_states_type = hidden_states.dtype
744
+
745
+ # 0. time
746
+ timesteps = self.expand_timesteps(timesteps, batch_size, hidden_states.device)
747
+ timesteps = self.t_embedder(timesteps, hidden_states_type)
748
+ p_embedder = self.p_embedder(pooled_embeds)
749
+ adaln_input = timesteps + p_embedder
750
+
751
+ hidden_states, image_tokens_masks, img_sizes = self.patchify(hidden_states, self.max_seq, img_sizes)
752
+ if image_tokens_masks is None:
753
+ pH, pW = img_sizes[0]
754
+ img_ids = torch.zeros(pH, pW, 3, device=hidden_states.device)
755
+ img_ids[..., 1] = img_ids[..., 1] + torch.arange(pH, device=hidden_states.device)[:, None]
756
+ img_ids[..., 2] = img_ids[..., 2] + torch.arange(pW, device=hidden_states.device)[None, :]
757
+ img_ids = repeat(img_ids, "h w c -> b (h w) c", b=batch_size)
758
+ hidden_states = self.x_embedder(hidden_states)
759
+
760
+ # T5_encoder_hidden_states = encoder_hidden_states[0]
761
+ encoder_hidden_states = encoder_hidden_states_llama3.movedim(1, 0)
762
+ encoder_hidden_states = [encoder_hidden_states[k] for k in self.llama_layers]
763
+
764
+ if self.caption_projection is not None:
765
+ new_encoder_hidden_states = []
766
+ for i, enc_hidden_state in enumerate(encoder_hidden_states):
767
+ enc_hidden_state = self.caption_projection[i](enc_hidden_state)
768
+ enc_hidden_state = enc_hidden_state.view(batch_size, -1, hidden_states.shape[-1])
769
+ new_encoder_hidden_states.append(enc_hidden_state)
770
+ encoder_hidden_states = new_encoder_hidden_states
771
+ T5_encoder_hidden_states = self.caption_projection[-1](T5_encoder_hidden_states)
772
+ T5_encoder_hidden_states = T5_encoder_hidden_states.view(batch_size, -1, hidden_states.shape[-1])
773
+ encoder_hidden_states.append(T5_encoder_hidden_states)
774
+
775
+ txt_ids = torch.zeros(
776
+ batch_size,
777
+ encoder_hidden_states[-1].shape[1] + encoder_hidden_states[-2].shape[1] + encoder_hidden_states[0].shape[1],
778
+ 3,
779
+ device=img_ids.device, dtype=img_ids.dtype
780
+ )
781
+ ids = torch.cat((img_ids, txt_ids), dim=1)
782
+ rope = self.pe_embedder(ids)
783
+
784
+ # 2. Blocks
785
+ block_id = 0
786
+ initial_encoder_hidden_states = torch.cat([encoder_hidden_states[-1], encoder_hidden_states[-2]], dim=1)
787
+ initial_encoder_hidden_states_seq_len = initial_encoder_hidden_states.shape[1]
788
+ for bid, block in enumerate(self.double_stream_blocks):
789
+ cur_llama31_encoder_hidden_states = encoder_hidden_states[block_id]
790
+ cur_encoder_hidden_states = torch.cat([initial_encoder_hidden_states, cur_llama31_encoder_hidden_states], dim=1)
791
+ hidden_states, initial_encoder_hidden_states = block(
792
+ image_tokens = hidden_states,
793
+ image_tokens_masks = image_tokens_masks,
794
+ text_tokens = cur_encoder_hidden_states,
795
+ adaln_input = adaln_input,
796
+ rope = rope,
797
+ transformer_options=transformer_options,
798
+ )
799
+ initial_encoder_hidden_states = initial_encoder_hidden_states[:, :initial_encoder_hidden_states_seq_len]
800
+ block_id += 1
801
+
802
+ image_tokens_seq_len = hidden_states.shape[1]
803
+ hidden_states = torch.cat([hidden_states, initial_encoder_hidden_states], dim=1)
804
+ hidden_states_seq_len = hidden_states.shape[1]
805
+ if image_tokens_masks is not None:
806
+ encoder_attention_mask_ones = torch.ones(
807
+ (batch_size, initial_encoder_hidden_states.shape[1] + cur_llama31_encoder_hidden_states.shape[1]),
808
+ device=image_tokens_masks.device, dtype=image_tokens_masks.dtype
809
+ )
810
+ image_tokens_masks = torch.cat([image_tokens_masks, encoder_attention_mask_ones], dim=1)
811
+
812
+ for bid, block in enumerate(self.single_stream_blocks):
813
+ cur_llama31_encoder_hidden_states = encoder_hidden_states[block_id]
814
+ hidden_states = torch.cat([hidden_states, cur_llama31_encoder_hidden_states], dim=1)
815
+ hidden_states = block(
816
+ image_tokens=hidden_states,
817
+ image_tokens_masks=image_tokens_masks,
818
+ text_tokens=None,
819
+ adaln_input=adaln_input,
820
+ rope=rope,
821
+ transformer_options=transformer_options,
822
+ )
823
+ hidden_states = hidden_states[:, :hidden_states_seq_len]
824
+ block_id += 1
825
+
826
+ hidden_states = hidden_states[:, :image_tokens_seq_len, ...]
827
+ output = self.final_layer(hidden_states, adaln_input)
828
+ output = self.unpatchify(output, img_sizes)
829
+ return -output[:, :, :h, :w]
ComfyUI/comfy/ldm/hidream_o1/attention.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """HiDream-O1 two-pass attention: tokens [0, ar_len) are causal, [ar_len, T)
2
+ attend full K/V. Splitting Q at the boundary avoids the (B, 1, T, T) additive
3
+ mask the general-purpose path would build (~500 MB at T~16K) and lets the
4
+ gen half hit the user's preferred backend via optimized_attention.
5
+ """
6
+
7
+ import torch
8
+
9
+ import comfy.ops
10
+ from comfy.ldm.modules.attention import optimized_attention
11
+
12
+
13
+ def make_two_pass_attention(ar_len: int, transformer_options=None):
14
+ """Build a two-pass attention callable. AR pass uses SDPA-causal directly, gen pass routes through optimized_attention.
15
+ The AR pass goes through SDPA directand bypasses wrappers, it is only ~1% of T at typical edit sizes.
16
+ """
17
+
18
+ def two_pass_attention(q, k, v, heads, enable_gqa=False, **kwargs):
19
+ B, H, T, D = q.shape
20
+
21
+ if T < k.shape[2]: # KV-cache hot path: Q is shorter than K/V (cached AR prefix is in K/V only), all fresh Q positions are in the gen region, single full-attention call
22
+ out = optimized_attention(q, k, v, heads, mask=None, skip_reshape=True, skip_output_reshape=True, transformer_options=transformer_options, enable_gqa=enable_gqa)
23
+ elif ar_len >= T:
24
+ out = comfy.ops.scaled_dot_product_attention(q, k, v, attn_mask=None, dropout_p=0.0, is_causal=True, enable_gqa=enable_gqa)
25
+ elif ar_len <= 0:
26
+ out = optimized_attention(q, k, v, heads, mask=None, skip_reshape=True, skip_output_reshape=True, transformer_options=transformer_options, enable_gqa=enable_gqa)
27
+ else:
28
+ out_ar = comfy.ops.scaled_dot_product_attention(
29
+ q[:, :, :ar_len], k[:, :, :ar_len], v[:, :, :ar_len],
30
+ attn_mask=None, dropout_p=0.0, is_causal=True, enable_gqa=enable_gqa,
31
+ )
32
+ out_gen = optimized_attention(
33
+ q[:, :, ar_len:], k, v, heads,
34
+ mask=None, skip_reshape=True, skip_output_reshape=True,
35
+ transformer_options=transformer_options, enable_gqa=enable_gqa,
36
+ )
37
+ out = torch.cat([out_ar, out_gen], dim=2)
38
+
39
+ return out.transpose(1, 2).reshape(B, T, H * D)
40
+
41
+ return two_pass_attention
ComfyUI/comfy/ldm/hidream_o1/conditioning.py ADDED
@@ -0,0 +1,230 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """HiDream-O1 conditioning prep — ref-image dual path + extra_conds assembly.
2
+
3
+ Each ref image goes through two paths: a 32x32 patchified stream concatenated
4
+ to the noised target, and a Qwen3-VL ViT path producing tokens that scatter
5
+ into input_ids at <|image_pad|> positions.
6
+ """
7
+
8
+ from typing import List
9
+
10
+ import torch
11
+
12
+ import comfy.utils
13
+ from comfy.text_encoders.qwen_vl import process_qwen2vl_images
14
+
15
+ from .utils import (PATCH_SIZE, calculate_dimensions, cond_image_size, ref_max_size, resize_tensor)
16
+
17
+ # Qwen3-VL ViT preprocessing constants (preprocessor_config.json).
18
+ VIT_PATCH = 16
19
+ VIT_MERGE = 2
20
+ VIT_IMAGE_MEAN = [0.5, 0.5, 0.5]
21
+ VIT_IMAGE_STD = [0.5, 0.5, 0.5]
22
+
23
+
24
+ def prepare_ref_images(
25
+ ref_images: List[torch.Tensor],
26
+ target_h: int,
27
+ target_w: int,
28
+ device: torch.device,
29
+ dtype: torch.dtype,
30
+ ):
31
+ """Build the dual-path tensors for K reference images at (target_h, target_w).
32
+
33
+ Returns None for K=0, else a dict with ref_patches, ref_pixel_values,
34
+ ref_image_grid_thw, per_ref_vit_tokens, per_ref_patch_grids.
35
+ """
36
+ K = len(ref_images)
37
+ if K == 0:
38
+ return None
39
+ max_size = ref_max_size(max(target_h, target_w), K)
40
+ cis = cond_image_size(K)
41
+
42
+ refs_t = [img[0].clamp(0, 1).permute(2, 0, 1).unsqueeze(0).contiguous().float() for img in ref_images]
43
+ refs_t = [resize_tensor(t, max_size, PATCH_SIZE) for t in refs_t]
44
+
45
+ # 32-patch path.
46
+ ref_patches_per = []
47
+ per_ref_patch_grids = []
48
+ for t in refs_t:
49
+ t_norm = (t.squeeze(0) - 0.5) / 0.5 # (3, H, W) in [-1, 1]
50
+ h_p, w_p = t_norm.shape[-2] // PATCH_SIZE, t_norm.shape[-1] // PATCH_SIZE
51
+ per_ref_patch_grids.append((h_p, w_p))
52
+ patches = (
53
+ t_norm.reshape(3, h_p, PATCH_SIZE, w_p, PATCH_SIZE)
54
+ .permute(1, 3, 0, 2, 4)
55
+ .reshape(h_p * w_p, 3 * PATCH_SIZE * PATCH_SIZE)
56
+ )
57
+ ref_patches_per.append(patches)
58
+ ref_patches = torch.cat(ref_patches_per, dim=0).unsqueeze(0).to(device=device, dtype=dtype)
59
+
60
+ # ViT path.
61
+ refs_vlm_t = []
62
+ for t in refs_t:
63
+ _, _, h, w = t.shape
64
+ cond_w, cond_h = calculate_dimensions(cis, w / h)
65
+ cond_w = max(cond_w, VIT_PATCH * VIT_MERGE)
66
+ cond_h = max(cond_h, VIT_PATCH * VIT_MERGE)
67
+ refs_vlm_t.append(comfy.utils.common_upscale(t, cond_w, cond_h, "lanczos", "disabled"))
68
+
69
+ pv_list, grid_list, per_ref_vit_tokens = [], [], []
70
+ for t_v in refs_vlm_t:
71
+ pv, grid_thw = process_qwen2vl_images(
72
+ t_v.permute(0, 2, 3, 1),
73
+ min_pixels=0, max_pixels=10**12,
74
+ patch_size=VIT_PATCH, merge_size=VIT_MERGE,
75
+ image_mean=VIT_IMAGE_MEAN, image_std=VIT_IMAGE_STD,
76
+ )
77
+ grid_thw = grid_thw[0]
78
+ pv_list.append(pv.to(device=device, dtype=dtype))
79
+ grid_list.append(grid_thw.to(device=device))
80
+ # Post-merge token count = number of <|image_pad|> tokens this image expands to in input_ids.
81
+ gh, gw = int(grid_thw[1].item()), int(grid_thw[2].item())
82
+ per_ref_vit_tokens.append((gh // VIT_MERGE) * (gw // VIT_MERGE))
83
+
84
+ return {
85
+ "ref_patches": ref_patches,
86
+ "ref_pixel_values": torch.cat(pv_list, dim=0),
87
+ "ref_image_grid_thw": torch.stack(grid_list, dim=0),
88
+ "per_ref_vit_tokens": per_ref_vit_tokens,
89
+ "per_ref_patch_grids": per_ref_patch_grids,
90
+ }
91
+
92
+
93
+ def build_ref_input_ids(
94
+ text_input_ids: torch.Tensor,
95
+ per_ref_vit_tokens: List[int],
96
+ image_token_id: int,
97
+ vision_start_id: int,
98
+ vision_end_id: int,
99
+ ):
100
+ """Splice [vision_start, image_pad*N, vision_end] blocks into input_ids
101
+ after the [im_start, user, \\n] prefix (matches original chat template).
102
+ """
103
+ ids = text_input_ids[0].tolist()
104
+ inserted = []
105
+ for n_pad in per_ref_vit_tokens:
106
+ inserted.extend([vision_start_id] + [image_token_id] * n_pad + [vision_end_id])
107
+ new_ids = ids[:3] + inserted + ids[3:] # 3 = len([im_start, user, \n])
108
+ return torch.tensor([new_ids], dtype=text_input_ids.dtype, device=text_input_ids.device)
109
+
110
+
111
+ def build_extra_conds(
112
+ text_input_ids: torch.Tensor,
113
+ noise: torch.Tensor,
114
+ ref_images: List[torch.Tensor] = None,
115
+ target_patch_size: int = 32,
116
+ ):
117
+ """Assemble all conditioning tensors for HiDreamO1Transformer.forward:
118
+ input_ids (with ref-vision tokens spliced in for the edit/IP path),
119
+ position_ids (MRoPE), token_types, vinput_mask, plus the ref
120
+ dual-path tensors when refs are provided.
121
+ """
122
+ from .utils import get_rope_index_fix_point
123
+ from comfy.text_encoders.hidream_o1 import (
124
+ IMAGE_TOKEN_ID, VISION_START_ID, VISION_END_ID,
125
+ )
126
+
127
+ if text_input_ids.dim() == 1:
128
+ text_input_ids = text_input_ids.unsqueeze(0)
129
+ text_input_ids = text_input_ids.long().to(noise.device)
130
+ B = noise.shape[0]
131
+ if text_input_ids.shape[0] == 1 and B > 1:
132
+ text_input_ids = text_input_ids.expand(B, -1)
133
+
134
+ H, W = noise.shape[-2], noise.shape[-1]
135
+ h_p, w_p = H // target_patch_size, W // target_patch_size
136
+ image_len = h_p * w_p
137
+ image_grid_thw_tgt = torch.tensor(
138
+ [[1, h_p, w_p]], dtype=torch.long, device=text_input_ids.device,
139
+ )
140
+
141
+ out = {}
142
+ if ref_images:
143
+ ref = prepare_ref_images(ref_images, H, W, device=noise.device, dtype=noise.dtype)
144
+ text_input_ids = build_ref_input_ids(
145
+ text_input_ids, ref["per_ref_vit_tokens"],
146
+ IMAGE_TOKEN_ID, VISION_START_ID, VISION_END_ID,
147
+ )
148
+ new_txt_len = text_input_ids.shape[1]
149
+
150
+ # Each ref's patchified stream gets a [vision_start, image_pad*N-1]
151
+ # block in the position-id stream after the noised target.
152
+ ref_grid_lengths = [hp * wp for (hp, wp) in ref["per_ref_patch_grids"]]
153
+ tgt_vision = torch.full((1, image_len), IMAGE_TOKEN_ID,
154
+ dtype=text_input_ids.dtype, device=text_input_ids.device)
155
+ tgt_vision[:, 0] = VISION_START_ID
156
+ ref_vision_blocks = []
157
+ for rl in ref_grid_lengths:
158
+ blk = torch.full((1, rl), IMAGE_TOKEN_ID,
159
+ dtype=text_input_ids.dtype, device=text_input_ids.device)
160
+ blk[:, 0] = VISION_START_ID
161
+ ref_vision_blocks.append(blk)
162
+ ref_vision_cat = torch.cat([tgt_vision] + ref_vision_blocks, dim=1)
163
+ input_ids_pad = torch.cat([text_input_ids, ref_vision_cat], dim=-1)
164
+ total_ref_patches_len = sum(ref_grid_lengths)
165
+ total_len = new_txt_len + image_len + total_ref_patches_len
166
+
167
+ # K (ViT, post-merge) + 1 (target) + K (ref-patches) image grids.
168
+ K = len(ref_images)
169
+ igthw_cond = ref["ref_image_grid_thw"].clone()
170
+ igthw_cond[:, 1] //= 2
171
+ igthw_cond[:, 2] //= 2
172
+ image_grid_thw_ref = torch.tensor(
173
+ [[1, hp, wp] for (hp, wp) in ref["per_ref_patch_grids"]],
174
+ dtype=torch.long, device=text_input_ids.device,
175
+ )
176
+ igthw_all = torch.cat([
177
+ igthw_cond.to(text_input_ids.device),
178
+ image_grid_thw_tgt,
179
+ image_grid_thw_ref,
180
+ ], dim=0)
181
+ position_ids, _ = get_rope_index_fix_point(
182
+ spatial_merge_size=1,
183
+ image_token_id=IMAGE_TOKEN_ID,
184
+ vision_start_token_id=VISION_START_ID,
185
+ input_ids=input_ids_pad, image_grid_thw=igthw_all,
186
+ attention_mask=None,
187
+ skip_vision_start_token=[0] * K + [1] + [1] * K,
188
+ fix_point=4096,
189
+ )
190
+
191
+ # tms + target_image + ref_patches are all gen.
192
+ tms_pos = new_txt_len - 1
193
+ ar_len = tms_pos
194
+ token_types = torch.zeros(B, total_len, dtype=torch.long, device=noise.device)
195
+ token_types[:, tms_pos:] = 1
196
+ vinput_mask = torch.zeros(B, total_len, dtype=torch.bool, device=noise.device)
197
+ vinput_mask[:, new_txt_len:] = True
198
+
199
+ # Leading batch dim sidesteps CONDRegular.process_cond's repeat_to_batch_size truncation
200
+ out["ref_pixel_values"] = ref["ref_pixel_values"].unsqueeze(0)
201
+ out["ref_image_grid_thw"] = ref["ref_image_grid_thw"].unsqueeze(0)
202
+ out["ref_patches"] = ref["ref_patches"]
203
+ else:
204
+ # T2I: text + noised target only, vision_start replaces the first image token
205
+ txt_len = text_input_ids.shape[1]
206
+ total_len = txt_len + image_len
207
+ vision_tokens = torch.full((B, image_len), IMAGE_TOKEN_ID,
208
+ dtype=text_input_ids.dtype, device=text_input_ids.device)
209
+ vision_tokens[:, 0] = VISION_START_ID
210
+ input_ids_pad = torch.cat([text_input_ids, vision_tokens], dim=-1)
211
+ position_ids, _ = get_rope_index_fix_point(
212
+ spatial_merge_size=1,
213
+ image_token_id=IMAGE_TOKEN_ID,
214
+ vision_start_token_id=VISION_START_ID,
215
+ input_ids=input_ids_pad, image_grid_thw=image_grid_thw_tgt,
216
+ attention_mask=None,
217
+ skip_vision_start_token=[1],
218
+ )
219
+ ar_len = txt_len - 1
220
+ token_types = torch.zeros(B, total_len, dtype=torch.long, device=noise.device)
221
+ token_types[:, ar_len:] = 1
222
+ vinput_mask = torch.zeros(B, total_len, dtype=torch.bool, device=noise.device)
223
+ vinput_mask[:, txt_len:] = True
224
+
225
+ out["input_ids"] = text_input_ids
226
+ out["position_ids"] = position_ids[:, 0].unsqueeze(0) # Collapse position_ids batch and add a leading dim so CONDRegular's batch-resize doesn't truncate the 3-axis MRoPE dim
227
+ out["token_types"] = token_types
228
+ out["vinput_mask"] = vinput_mask
229
+ out["ar_len"] = ar_len
230
+ return out
ComfyUI/comfy/ldm/hidream_o1/model.py ADDED
@@ -0,0 +1,306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """HiDream-O1-Image transformer.
2
+
3
+ Pixel-space DiT built on Qwen3-VL: the vision tower (Qwen35VisionModel)
4
+ encodes ref images, the Qwen3-VL-8B decoder (Llama2_ with interleaved MRoPE)
5
+ processes a unified text+image sequence, and 32x32 patch embed/unembed
6
+ shims map raw RGB in and out of LLM hidden space. The Qwen3-VL deepstack
7
+ mergers go unused — their weights are dropped at load.
8
+ """
9
+
10
+ from dataclasses import dataclass, field
11
+ from typing import List, Optional
12
+
13
+ import einops
14
+ import torch
15
+ import torch.nn as nn
16
+
17
+ import comfy.patcher_extension
18
+ from comfy.ldm.modules.diffusionmodules.mmdit import TimestepEmbedder
19
+ from comfy.text_encoders.llama import Llama2_
20
+ from comfy.text_encoders.qwen35 import Qwen35VisionModel
21
+
22
+ from .attention import make_two_pass_attention
23
+
24
+
25
+ IMAGE_TOKEN_ID = 151655 # Qwen3-VL <|image_pad|>
26
+ TMS_TOKEN_ID = 151673 # HiDream-O1 <|tms_token|>
27
+ PATCH_SIZE = 32
28
+
29
+
30
+ @dataclass
31
+ class HiDreamO1TextConfig:
32
+ """Qwen3-VL-8B text-decoder dims (matches public Qwen3-VL-8B-Instruct)."""
33
+ vocab_size: int = 151936
34
+ hidden_size: int = 4096
35
+ intermediate_size: int = 12288
36
+ num_hidden_layers: int = 36
37
+ num_attention_heads: int = 32
38
+ num_key_value_heads: int = 8
39
+ head_dim: int = 128
40
+ max_position_embeddings: int = 128000
41
+ rms_norm_eps: float = 1e-6
42
+ rope_theta: float = 5000000.0
43
+ rope_scale: Optional[float] = None
44
+ rope_dims: List[int] = field(default_factory=lambda: [24, 20, 20])
45
+ interleaved_mrope: bool = True
46
+ transformer_type: str = "llama"
47
+ rms_norm_add: bool = False
48
+ mlp_activation: str = "silu"
49
+ qkv_bias: bool = False
50
+ q_norm: str = "gemma3"
51
+ k_norm: str = "gemma3"
52
+ final_norm: bool = True
53
+ lm_head: bool = False
54
+ stop_tokens: List[int] = field(default_factory=lambda: [151643, 151645])
55
+
56
+
57
+ QWEN3VL_VISION_DEFAULTS = dict(
58
+ hidden_size=1152,
59
+ num_heads=16,
60
+ intermediate_size=4304,
61
+ depth=27,
62
+ patch_size=16,
63
+ temporal_patch_size=2,
64
+ in_channels=3,
65
+ spatial_merge_size=2,
66
+ num_position_embeddings=2304,
67
+ deepstack_visual_indexes=(8, 16, 24),
68
+ out_hidden_size=4096, # final merger projects directly into LLM hidden
69
+ )
70
+
71
+
72
+ class BottleneckPatchEmbed(nn.Module):
73
+ # 3072 -> 1024 -> 4096 (raw 32x32 RGB patch -> bottleneck -> LLM hidden).
74
+ def __init__(self, patch_size=32, in_chans=3, pca_dim=1024, embed_dim=4096, bias=True, device=None, dtype=None, ops=None):
75
+ super().__init__()
76
+ self.proj1 = ops.Linear(patch_size * patch_size * in_chans, pca_dim, bias=False, device=device, dtype=dtype)
77
+ self.proj2 = ops.Linear(pca_dim, embed_dim, bias=bias, device=device, dtype=dtype)
78
+
79
+ def forward(self, x):
80
+ return self.proj2(self.proj1(x))
81
+
82
+
83
+ class FinalLayer(nn.Module):
84
+ # 4096 -> 3072 (LLM hidden -> flat pixel patch).
85
+ def __init__(self, hidden_size, patch_size=32, out_channels=3, device=None, dtype=None, ops=None):
86
+ super().__init__()
87
+ self.linear = ops.Linear(hidden_size, patch_size * patch_size * out_channels, bias=True, device=device, dtype=dtype)
88
+
89
+ def forward(self, x):
90
+ return self.linear(x)
91
+
92
+
93
+ class HiDreamO1Transformer(nn.Module):
94
+ """HiDream-O1 unified pixel-level transformer."""
95
+
96
+ def __init__(self, image_model=None, dtype=None, device=None, operations=None,
97
+ text_config_overrides=None, vision_config_overrides=None, **kwargs):
98
+ super().__init__()
99
+ self.dtype = dtype
100
+
101
+ text_cfg = HiDreamO1TextConfig(**(text_config_overrides or {}))
102
+ vision_cfg = dict(QWEN3VL_VISION_DEFAULTS)
103
+ if vision_config_overrides:
104
+ vision_cfg.update(vision_config_overrides)
105
+ vision_cfg["out_hidden_size"] = text_cfg.hidden_size
106
+
107
+ self.text_config = text_cfg
108
+ self.vision_config = vision_cfg
109
+ self.hidden_size = text_cfg.hidden_size
110
+ self.patch_size = PATCH_SIZE
111
+ self.in_channels = 3
112
+ self.tms_token_id = TMS_TOKEN_ID
113
+
114
+ self.visual = Qwen35VisionModel(vision_cfg, device=device, dtype=dtype, ops=operations)
115
+ self.language_model = Llama2_(text_cfg, device=device, dtype=dtype, ops=operations)
116
+ self.t_embedder1 = TimestepEmbedder(
117
+ text_cfg.hidden_size, device=device, dtype=dtype, operations=operations,
118
+ )
119
+ self.x_embedder = BottleneckPatchEmbed(
120
+ patch_size=self.patch_size, in_chans=self.in_channels,
121
+ pca_dim=text_cfg.hidden_size // 4, embed_dim=text_cfg.hidden_size,
122
+ bias=True, device=device, dtype=dtype, ops=operations,
123
+ )
124
+ self.final_layer2 = FinalLayer(
125
+ text_cfg.hidden_size, patch_size=self.patch_size,
126
+ out_channels=self.in_channels, device=device, dtype=dtype, ops=operations,
127
+ )
128
+
129
+ self._visual_cache = None
130
+ self._kv_cache_entries = []
131
+
132
+ def clear_kv_cache(self):
133
+ self._kv_cache_entries = []
134
+ self._visual_cache = None
135
+
136
+ def forward(self, x, timesteps, context=None, transformer_options={}, **kwargs):
137
+ return comfy.patcher_extension.WrapperExecutor.new_class_executor(
138
+ self._forward,
139
+ self,
140
+ comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, transformer_options)
141
+ ).execute(x, timesteps, context, transformer_options, **kwargs)
142
+
143
+ def _forward(self, x, timesteps, context=None, transformer_options={}, input_ids=None, attention_mask=None, position_ids=None,
144
+ vinput_mask=None, ar_len=None, ref_pixel_values=None, ref_image_grid_thw=None, ref_patches=None, **kwargs):
145
+ """Returns flow-match velocity (x - x_pred) / sigma"""
146
+
147
+ if input_ids is None or position_ids is None:
148
+ raise ValueError("HiDreamO1Transformer requires input_ids and position_ids in conditioning")
149
+
150
+ B, _, H, W = x.shape
151
+ h_p, w_p = H // self.patch_size, W // self.patch_size
152
+ tgt_image_len = h_p * w_p
153
+
154
+ z = einops.rearrange(
155
+ x, 'B C (H p1) (W p2) -> B (H W) (C p1 p2)',
156
+ p1=self.patch_size, p2=self.patch_size,
157
+ )
158
+ vinputs = torch.cat([z, ref_patches.to(z.dtype)], dim=1) if ref_patches is not None else z
159
+
160
+ inputs_embeds = self.language_model.embed_tokens(input_ids).to(x.dtype)
161
+
162
+ if ref_pixel_values is not None and ref_image_grid_thw is not None:
163
+ # ViT output is constant across sampling steps within a generation
164
+ # identity-key by the input tensor so refs don't recompute every step.
165
+ cached = self._visual_cache
166
+ if cached is not None and cached[0] is ref_pixel_values:
167
+ image_embeds = cached[1]
168
+ else:
169
+ ref_pv = ref_pixel_values.to(inputs_embeds.device)
170
+ ref_grid = ref_image_grid_thw.to(inputs_embeds.device).long()
171
+ # extra_conds wraps with a leading batch dim; refs are model-level so [0] always recovers them.
172
+ if ref_pv.dim() == 3:
173
+ ref_pv = ref_pv[0]
174
+ if ref_grid.dim() == 3:
175
+ ref_grid = ref_grid[0]
176
+ image_embeds = self.visual(ref_pv, ref_grid).to(inputs_embeds.dtype)
177
+ self._visual_cache = (ref_pixel_values, image_embeds)
178
+ # image_pad positions identical across batch (input_ids shared cond/uncond).
179
+ image_idx = (input_ids[0] == IMAGE_TOKEN_ID).nonzero(as_tuple=True)[0]
180
+ if image_idx.shape[0] != image_embeds.shape[0]:
181
+ raise ValueError(
182
+ f"Image-token count {image_idx.shape[0]} != ViT output count "
183
+ f"{image_embeds.shape[0]}; check tokenizer/processor alignment."
184
+ )
185
+ inputs_embeds[:, image_idx] = image_embeds.unsqueeze(0).expand(B, -1, -1)
186
+
187
+ sigma = timesteps.float() / 1000.0
188
+ t_pixeldit = 1.0 - sigma
189
+ t_emb = self.t_embedder1(t_pixeldit * 1000, inputs_embeds.dtype)
190
+ tms_mask_3d = (input_ids == self.tms_token_id).unsqueeze(-1).expand_as(inputs_embeds)
191
+ inputs_embeds = torch.where(tms_mask_3d, t_emb.unsqueeze(1).expand_as(inputs_embeds), inputs_embeds)
192
+
193
+ vinputs_embedded = self.x_embedder(vinputs.to(inputs_embeds.dtype))
194
+ inputs_embeds = torch.cat([inputs_embeds, vinputs_embedded], dim=1)
195
+
196
+ # extra_conds stores position_ids as (1, 3, T); process_cond repeats dim 0 to B. Take row 0.
197
+ freqs_cis = self.language_model.compute_freqs_cis(position_ids[0].to(x.device), x.device)
198
+ freqs_cis = tuple(t.to(x.dtype) for t in freqs_cis)
199
+
200
+ two_pass_attn = make_two_pass_attention(ar_len, transformer_options=transformer_options)
201
+ patches_replace = transformer_options.get("patches_replace", {})
202
+ blocks_replace = patches_replace.get("dit", {})
203
+ transformer_options["total_blocks"] = len(self.language_model.layers)
204
+ transformer_options["block_type"] = "double"
205
+
206
+ # Cache prefix K/V across steps. Key includes input_ids (prompt), ref_id
207
+ # (refs scatter into inputs_embeds), and position_ids (RoPE baked into cached K).
208
+ can_cache = not blocks_replace and ar_len > 0
209
+ cache_len = ar_len if can_cache else 0
210
+ ref_id = id(ref_pixel_values) if ref_pixel_values is not None else None
211
+ pos_ids_key = position_ids[..., :cache_len] if can_cache else position_ids
212
+ cache_entries = self._kv_cache_entries
213
+ # Drop stale entries from a previous device (model was unloaded and reloaded).
214
+ if cache_entries and cache_entries[0]["input_ids"].device != input_ids.device:
215
+ cache_entries = []
216
+ self._kv_cache_entries = []
217
+ kv_cache = None
218
+ if can_cache:
219
+ for entry in cache_entries:
220
+ ck = entry["input_ids"]
221
+ ep = entry["position_ids"]
222
+ if (entry["cache_len"] == cache_len
223
+ and ck.shape == input_ids.shape and torch.equal(ck, input_ids)
224
+ and entry["ref_id"] == ref_id
225
+ and ep.shape == pos_ids_key.shape and torch.equal(ep, pos_ids_key)):
226
+ kv_cache = entry
227
+ break
228
+
229
+ if kv_cache is not None:
230
+ # Hot path: project Q/K/V only for fresh positions; past_key_value prepends cached AR K/V.
231
+ hidden_states = inputs_embeds[:, cache_len:]
232
+ sliced_freqs = tuple(t[..., cache_len:, :] for t in freqs_cis)
233
+ for i, layer in enumerate(self.language_model.layers):
234
+ transformer_options["block_index"] = i
235
+ K_i, V_i = kv_cache["kv"][i]
236
+ hidden_states, _ = layer(
237
+ x=hidden_states, attention_mask=None, freqs_cis=sliced_freqs, optimized_attention=two_pass_attn,
238
+ past_key_value=(K_i, V_i, cache_len),
239
+ )
240
+ else:
241
+ # Cold path: run full sequence; if cacheable, snapshot K/V at AR positions.
242
+ snapshots = [] if can_cache else None
243
+ past_kv_cold = () if can_cache else None
244
+ hidden_states = inputs_embeds
245
+ for i, layer in enumerate(self.language_model.layers):
246
+ transformer_options["block_index"] = i
247
+ if ("double_block", i) in blocks_replace:
248
+ def block_wrap(args, _layer=layer):
249
+ out = {}
250
+ out["x"], _ = _layer(
251
+ x=args["x"], attention_mask=args.get("attention_mask"),
252
+ freqs_cis=args["freqs_cis"], optimized_attention=args["optimized_attention"],
253
+ past_key_value=None,
254
+ )
255
+ return out
256
+ out = blocks_replace[("double_block", i)](
257
+ {"x": hidden_states, "attention_mask": None,
258
+ "freqs_cis": freqs_cis, "optimized_attention": two_pass_attn,
259
+ "transformer_options": transformer_options},
260
+ {"original_block": block_wrap},
261
+ )
262
+ hidden_states = out["x"]
263
+ else:
264
+ hidden_states, present_kv = layer(
265
+ x=hidden_states, attention_mask=None,
266
+ freqs_cis=freqs_cis, optimized_attention=two_pass_attn,
267
+ past_key_value=past_kv_cold,
268
+ )
269
+ if snapshots is not None:
270
+ K, V, _ = present_kv
271
+ snapshots.append((K[:, :, :cache_len].contiguous(),
272
+ V[:, :, :cache_len].contiguous()))
273
+ if snapshots is not None:
274
+ # Cap at 2 entries (cond + uncond). Multi-cond workflows LRU-evict.
275
+ new_entry = {
276
+ "input_ids": input_ids.clone(),
277
+ "cache_len": cache_len,
278
+ "kv": snapshots,
279
+ "ref_id": ref_id,
280
+ "position_ids": pos_ids_key.clone(),
281
+ }
282
+ self._kv_cache_entries = (cache_entries + [new_entry])[-2:]
283
+
284
+ if self.language_model.norm is not None:
285
+ hidden_states = self.language_model.norm(hidden_states)
286
+
287
+ # Slice target-image positions before the final projection so the Linear only runs on tgt_image_len tokens.
288
+ # In the hot path hidden_states starts at original position cache_len, so masks/indices shift by cache_len.
289
+ sliced_offset = cache_len if kv_cache is not None else 0
290
+ if vinput_mask is not None:
291
+ vmask = vinput_mask.to(x.device).bool()
292
+ if sliced_offset > 0:
293
+ vmask = vmask[:, sliced_offset:]
294
+ target_hidden = hidden_states[vmask].view(B, -1, hidden_states.shape[-1])[:, :tgt_image_len]
295
+ else:
296
+ txt_seq_len = input_ids.shape[1]
297
+ start = txt_seq_len - sliced_offset
298
+ target_hidden = hidden_states[:, start:start + tgt_image_len]
299
+ x_pred_tgt = self.final_layer2(target_hidden)
300
+
301
+ # fp32 final subtraction, bf16 here noticeably degrades samples.
302
+ x_pred_img = einops.rearrange(
303
+ x_pred_tgt, 'B (H W) (C p1 p2) -> B C (H p1) (W p2)',
304
+ H=h_p, W=w_p, p1=self.patch_size, p2=self.patch_size,
305
+ )
306
+ return (x.float() - x_pred_img.float()) / sigma.view(B, 1, 1, 1).clamp_min(1e-3)
ComfyUI/comfy/ldm/hidream_o1/utils.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """HiDream-O1 input-prep helpers: image/resolution math and unified-sequence
2
+ RoPE position-id assembly. The fix_point offset in get_rope_index_fix_point
3
+ lets the target image and patchified ref images share spatial RoPE positions
4
+ despite living at different sequence indices — same 2D image plane.
5
+ """
6
+
7
+ import math
8
+ from typing import Optional
9
+
10
+ import torch
11
+
12
+
13
+ PATCH_SIZE = 32
14
+ CONDITION_IMAGE_SIZE = 384 # ViT-side base size for ref images
15
+
16
+
17
+ def resize_tensor(img_t, image_size, patch_size=16):
18
+ """img_t: (1, 3, H, W) float [0, 1]. Fit to image_size**2 area, patch-aligned, center-cropped."""
19
+
20
+ while min(img_t.shape[-2], img_t.shape[-1]) >= 2 * image_size: # Pre-halves with 2x2 box averaging while the image is still very large
21
+ img_t = torch.nn.functional.avg_pool2d(img_t, kernel_size=2, stride=2)
22
+
23
+ _, _, height, width = img_t.shape
24
+ m = patch_size
25
+ s_max = image_size * image_size
26
+ scale = math.sqrt(s_max / (width * height))
27
+
28
+ candidates = [
29
+ (round(width * scale) // m * m, round(height * scale) // m * m),
30
+ (round(width * scale) // m * m, math.floor(height * scale) // m * m),
31
+ (math.floor(width * scale) // m * m, round(height * scale) // m * m),
32
+ (math.floor(width * scale) // m * m, math.floor(height * scale) // m * m),
33
+ ]
34
+ candidates = sorted(candidates, key=lambda x: x[0] * x[1], reverse=True)
35
+ new_size = candidates[-1]
36
+ for c in candidates:
37
+ if c[0] * c[1] <= s_max:
38
+ new_size = c
39
+ break
40
+
41
+ new_w, new_h = new_size
42
+ s1 = width / new_w
43
+ s2 = height / new_h
44
+ if s1 < s2:
45
+ resize_w, resize_h = new_w, round(height / s1)
46
+ else:
47
+ resize_w, resize_h = round(width / s2), new_h
48
+ img_t = torch.nn.functional.interpolate(img_t, size=(resize_h, resize_w), mode="bicubic")
49
+ top = (resize_h - new_h) // 2
50
+ left = (resize_w - new_w) // 2
51
+ return img_t[..., top:top + new_h, left:left + new_w]
52
+
53
+
54
+ def calculate_dimensions(max_size, ratio):
55
+ """(W, H) for an aspect ratio fitting in max_size**2 area, 32-aligned."""
56
+ width = math.sqrt(max_size * max_size * ratio)
57
+ height = width / ratio
58
+ width = int(width / 32) * 32
59
+ height = int(height / 32) * 32
60
+ return width, height
61
+
62
+
63
+ def ref_max_size(target_max_dim, k):
64
+ """K-dependent ref-image max dim before patchifying."""
65
+ if k == 1:
66
+ return target_max_dim
67
+ if k == 2:
68
+ return target_max_dim * 48 // 64
69
+ if k <= 4:
70
+ return target_max_dim // 2
71
+ if k <= 8:
72
+ return target_max_dim * 24 // 64
73
+ return target_max_dim // 4
74
+
75
+
76
+ def cond_image_size(k):
77
+ """K-dependent ViT-side image size."""
78
+ if k <= 4:
79
+ return CONDITION_IMAGE_SIZE
80
+ if k <= 8:
81
+ return CONDITION_IMAGE_SIZE * 48 // 64
82
+ return CONDITION_IMAGE_SIZE // 2
83
+
84
+
85
+ def get_rope_index_fix_point(
86
+ spatial_merge_size: int,
87
+ image_token_id: int,
88
+ vision_start_token_id: int,
89
+ input_ids: Optional[torch.LongTensor] = None,
90
+ image_grid_thw: Optional[torch.LongTensor] = None,
91
+ attention_mask: Optional[torch.Tensor] = None,
92
+ skip_vision_start_token=None,
93
+ fix_point: int = 4096,
94
+ ):
95
+ mrope_position_deltas = []
96
+ if input_ids is not None and image_grid_thw is not None:
97
+ total_input_ids = input_ids
98
+ if attention_mask is None:
99
+ attention_mask = torch.ones_like(total_input_ids)
100
+ position_ids = torch.ones(
101
+ 3, input_ids.shape[0], input_ids.shape[1],
102
+ dtype=input_ids.dtype, device=input_ids.device,
103
+ )
104
+ attention_mask = attention_mask.to(total_input_ids.device)
105
+ for i, input_ids_b in enumerate(total_input_ids):
106
+ fp = fix_point
107
+ image_index = 0
108
+ input_ids_b = input_ids_b[attention_mask[i] == 1]
109
+ vision_start_indices = torch.argwhere(input_ids_b == vision_start_token_id).squeeze(1)
110
+ vision_tokens = input_ids_b[vision_start_indices + 1]
111
+ image_nums = (vision_tokens == image_token_id).sum()
112
+ input_tokens = input_ids_b.tolist()
113
+ llm_pos_ids_list = []
114
+ st = 0
115
+ remain_images = image_nums
116
+ for _ in range(image_nums):
117
+ if image_token_id in input_tokens and remain_images > 0:
118
+ ed = input_tokens.index(image_token_id, st)
119
+ else:
120
+ ed = len(input_tokens) + 1
121
+ t = image_grid_thw[image_index][0]
122
+ h = image_grid_thw[image_index][1]
123
+ w = image_grid_thw[image_index][2]
124
+ image_index += 1
125
+ remain_images -= 1
126
+ llm_grid_t = t.item()
127
+ llm_grid_h = h.item() // spatial_merge_size
128
+ llm_grid_w = w.item() // spatial_merge_size
129
+ text_len = ed - st
130
+ text_len -= skip_vision_start_token[image_index - 1]
131
+ text_len = max(0, text_len)
132
+ st_idx = llm_pos_ids_list[-1].max() + 1 if len(llm_pos_ids_list) > 0 else 0
133
+ llm_pos_ids_list.append(torch.arange(text_len).view(1, -1).expand(3, -1) + st_idx)
134
+
135
+ t_index = torch.arange(llm_grid_t).view(-1, 1).expand(-1, llm_grid_h * llm_grid_w).flatten()
136
+ h_index = torch.arange(llm_grid_h).view(1, -1, 1).expand(llm_grid_t, -1, llm_grid_w).flatten()
137
+ w_index = torch.arange(llm_grid_w).view(1, 1, -1).expand(llm_grid_t, llm_grid_h, -1).flatten()
138
+
139
+ if skip_vision_start_token[image_index - 1]:
140
+ if fp > 0:
141
+ fp = fp - st_idx
142
+ llm_pos_ids_list.append(torch.stack([t_index, h_index, w_index]) + fp + st_idx)
143
+ fp = 0
144
+ else:
145
+ llm_pos_ids_list.append(torch.stack([t_index, h_index, w_index]) + text_len + st_idx)
146
+ st = ed + llm_grid_t * llm_grid_h * llm_grid_w
147
+
148
+ if st < len(input_tokens):
149
+ st_idx = llm_pos_ids_list[-1].max() + 1 if len(llm_pos_ids_list) > 0 else 0
150
+ text_len = len(input_tokens) - st
151
+ llm_pos_ids_list.append(torch.arange(text_len).view(1, -1).expand(3, -1) + st_idx)
152
+
153
+ llm_positions = torch.cat(llm_pos_ids_list, dim=1).reshape(3, -1)
154
+ position_ids[..., i, attention_mask[i] == 1] = llm_positions.to(position_ids.device)
155
+ mrope_position_deltas.append(llm_positions.max() + 1 - len(total_input_ids[i]))
156
+ mrope_position_deltas = torch.tensor(mrope_position_deltas, device=input_ids.device).unsqueeze(1)
157
+ return position_ids, mrope_position_deltas
158
+
159
+ if attention_mask is not None:
160
+ position_ids = attention_mask.long().cumsum(-1) - 1
161
+ position_ids.masked_fill_(attention_mask == 0, 1)
162
+ position_ids = position_ids.unsqueeze(0).expand(3, -1, -1).to(attention_mask.device)
163
+ max_position_ids = position_ids.max(0, keepdim=False)[0].max(-1, keepdim=True)[0]
164
+ mrope_position_deltas = max_position_ids + 1 - attention_mask.shape[-1]
165
+ else:
166
+ position_ids = (
167
+ torch.arange(input_ids.shape[1], device=input_ids.device)
168
+ .view(1, 1, -1).expand(3, input_ids.shape[0], -1)
169
+ )
170
+ mrope_position_deltas = torch.zeros(
171
+ [input_ids.shape[0], 1], device=input_ids.device, dtype=input_ids.dtype,
172
+ )
173
+ return position_ids, mrope_position_deltas
ComfyUI/comfy/ldm/hunyuan3d/model.py ADDED
@@ -0,0 +1,148 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from torch import nn
3
+ from comfy.ldm.flux.layers import (
4
+ DoubleStreamBlock,
5
+ LastLayer,
6
+ MLPEmbedder,
7
+ SingleStreamBlock,
8
+ timestep_embedding,
9
+ )
10
+ import comfy.patcher_extension
11
+
12
+
13
+ class Hunyuan3Dv2(nn.Module):
14
+ def __init__(
15
+ self,
16
+ in_channels=64,
17
+ context_in_dim=1536,
18
+ hidden_size=1024,
19
+ mlp_ratio=4.0,
20
+ num_heads=16,
21
+ depth=16,
22
+ depth_single_blocks=32,
23
+ qkv_bias=True,
24
+ guidance_embed=False,
25
+ image_model=None,
26
+ dtype=None,
27
+ device=None,
28
+ operations=None
29
+ ):
30
+ super().__init__()
31
+ self.dtype = dtype
32
+
33
+ if hidden_size % num_heads != 0:
34
+ raise ValueError(
35
+ f"Hidden size {hidden_size} must be divisible by num_heads {num_heads}"
36
+ )
37
+
38
+ self.max_period = 1000 # While reimplementing the model I noticed that they messed up. This 1000 value was meant to be the time_factor but they set the max_period instead
39
+ self.latent_in = operations.Linear(in_channels, hidden_size, bias=True, dtype=dtype, device=device)
40
+ self.time_in = MLPEmbedder(in_dim=256, hidden_dim=hidden_size, dtype=dtype, device=device, operations=operations)
41
+ self.guidance_in = (
42
+ MLPEmbedder(in_dim=256, hidden_dim=hidden_size, dtype=dtype, device=device, operations=operations) if guidance_embed else None
43
+ )
44
+ self.cond_in = operations.Linear(context_in_dim, hidden_size, dtype=dtype, device=device)
45
+ self.double_blocks = nn.ModuleList(
46
+ [
47
+ DoubleStreamBlock(
48
+ hidden_size,
49
+ num_heads,
50
+ mlp_ratio=mlp_ratio,
51
+ qkv_bias=qkv_bias,
52
+ dtype=dtype, device=device, operations=operations
53
+ )
54
+ for _ in range(depth)
55
+ ]
56
+ )
57
+ self.single_blocks = nn.ModuleList(
58
+ [
59
+ SingleStreamBlock(
60
+ hidden_size,
61
+ num_heads,
62
+ mlp_ratio=mlp_ratio,
63
+ dtype=dtype, device=device, operations=operations
64
+ )
65
+ for _ in range(depth_single_blocks)
66
+ ]
67
+ )
68
+ self.final_layer = LastLayer(hidden_size, 1, in_channels, dtype=dtype, device=device, operations=operations)
69
+
70
+ def forward(self, x, timestep, context, guidance=None, transformer_options={}, **kwargs):
71
+ return comfy.patcher_extension.WrapperExecutor.new_class_executor(
72
+ self._forward,
73
+ self,
74
+ comfy.patcher_extension.get_all_wrappers(comfy.patcher_extension.WrappersMP.DIFFUSION_MODEL, transformer_options)
75
+ ).execute(x, timestep, context, guidance, transformer_options, **kwargs)
76
+
77
+ def _forward(self, x, timestep, context, guidance=None, transformer_options={}, **kwargs):
78
+ x = x.movedim(-1, -2)
79
+ timestep = 1.0 - timestep
80
+ txt = context
81
+ img = self.latent_in(x)
82
+
83
+ vec = self.time_in(timestep_embedding(timestep, 256, self.max_period).to(dtype=img.dtype))
84
+ if self.guidance_in is not None:
85
+ if guidance is not None:
86
+ vec = vec + self.guidance_in(timestep_embedding(guidance, 256, self.max_period).to(img.dtype))
87
+
88
+ txt = self.cond_in(txt)
89
+ pe = None
90
+ attn_mask = None
91
+
92
+ patches_replace = transformer_options.get("patches_replace", {})
93
+ blocks_replace = patches_replace.get("dit", {})
94
+ for i, block in enumerate(self.double_blocks):
95
+ if ("double_block", i) in blocks_replace:
96
+ def block_wrap(args):
97
+ out = {}
98
+ out["img"], out["txt"] = block(img=args["img"],
99
+ txt=args["txt"],
100
+ vec=args["vec"],
101
+ pe=args["pe"],
102
+ attn_mask=args.get("attn_mask"),
103
+ transformer_options=args["transformer_options"])
104
+ return out
105
+
106
+ out = blocks_replace[("double_block", i)]({"img": img,
107
+ "txt": txt,
108
+ "vec": vec,
109
+ "pe": pe,
110
+ "attn_mask": attn_mask,
111
+ "transformer_options": transformer_options},
112
+ {"original_block": block_wrap})
113
+ txt = out["txt"]
114
+ img = out["img"]
115
+ else:
116
+ img, txt = block(img=img,
117
+ txt=txt,
118
+ vec=vec,
119
+ pe=pe,
120
+ attn_mask=attn_mask,
121
+ transformer_options=transformer_options)
122
+
123
+ img = torch.cat((txt, img), 1)
124
+
125
+ for i, block in enumerate(self.single_blocks):
126
+ if ("single_block", i) in blocks_replace:
127
+ def block_wrap(args):
128
+ out = {}
129
+ out["img"] = block(args["img"],
130
+ vec=args["vec"],
131
+ pe=args["pe"],
132
+ attn_mask=args.get("attn_mask"),
133
+ transformer_options=args["transformer_options"])
134
+ return out
135
+
136
+ out = blocks_replace[("single_block", i)]({"img": img,
137
+ "vec": vec,
138
+ "pe": pe,
139
+ "attn_mask": attn_mask,
140
+ "transformer_options": transformer_options},
141
+ {"original_block": block_wrap})
142
+ img = out["img"]
143
+ else:
144
+ img = block(img, vec=vec, pe=pe, attn_mask=attn_mask, transformer_options=transformer_options)
145
+
146
+ img = img[:, txt.shape[1]:, ...]
147
+ img = self.final_layer(img, vec)
148
+ return img.movedim(-2, -1) * (-1.0)
ComfyUI/comfy/ldm/hunyuan3d/vae.py ADDED
@@ -0,0 +1,988 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Original: https://github.com/Tencent/Hunyuan3D-2/blob/main/hy3dgen/shapegen/models/autoencoders/model.py
2
+ # Since the header on their VAE source file was a bit confusing we asked for permission to use this code from tencent under the GPL license used in ComfyUI.
3
+
4
+ import torch
5
+ import torch.nn as nn
6
+ import torch.nn.functional as F
7
+ import numpy as np
8
+ import math
9
+ from tqdm import tqdm
10
+
11
+ from typing import Optional
12
+
13
+ import logging
14
+
15
+ import comfy.ops
16
+ ops = comfy.ops.disable_weight_init
17
+
18
+ def fps(src: torch.Tensor, batch: torch.Tensor, sampling_ratio: float, start_random: bool = True):
19
+
20
+ # manually create the pointer vector
21
+ assert src.size(0) == batch.numel()
22
+
23
+ batch_size = int(batch.max()) + 1
24
+ deg = src.new_zeros(batch_size, dtype = torch.long)
25
+
26
+ deg.scatter_add_(0, batch, torch.ones_like(batch))
27
+
28
+ ptr_vec = deg.new_zeros(batch_size + 1)
29
+ torch.cumsum(deg, 0, out=ptr_vec[1:])
30
+
31
+ #return fps_sampling(src, ptr_vec, ratio)
32
+ sampled_indicies = []
33
+
34
+ for b in range(batch_size):
35
+ # start and the end of each batch
36
+ start, end = ptr_vec[b].item(), ptr_vec[b + 1].item()
37
+ # points from the point cloud
38
+ points = src[start:end]
39
+
40
+ num_points = points.size(0)
41
+ num_samples = max(1, math.ceil(num_points * sampling_ratio))
42
+
43
+ selected = torch.zeros(num_samples, device = src.device, dtype = torch.long)
44
+ distances = torch.full((num_points,), float("inf"), device = src.device)
45
+
46
+ # select a random start point
47
+ if start_random:
48
+ farthest = torch.randint(0, num_points, (1,), device = src.device)
49
+ else:
50
+ farthest = torch.tensor([0], device = src.device, dtype = torch.long)
51
+
52
+ for i in range(num_samples):
53
+ selected[i] = farthest
54
+ centroid = points[farthest].squeeze(0)
55
+ dist = torch.norm(points - centroid, dim = 1) # compute euclidean distance
56
+ distances = torch.minimum(distances, dist)
57
+ farthest = torch.argmax(distances)
58
+
59
+ sampled_indicies.append(torch.arange(start, end)[selected])
60
+
61
+ return torch.cat(sampled_indicies, dim = 0)
62
+ class PointCrossAttention(nn.Module):
63
+ def __init__(self,
64
+ num_latents: int,
65
+ downsample_ratio: float,
66
+ pc_size: int,
67
+ pc_sharpedge_size: int,
68
+ point_feats: int,
69
+ width: int,
70
+ heads: int,
71
+ layers: int,
72
+ fourier_embedder,
73
+ normal_pe: bool = False,
74
+ qkv_bias: bool = False,
75
+ use_ln_post: bool = True,
76
+ qk_norm: bool = True):
77
+
78
+ super().__init__()
79
+
80
+ self.fourier_embedder = fourier_embedder
81
+
82
+ self.pc_size = pc_size
83
+ self.normal_pe = normal_pe
84
+ self.downsample_ratio = downsample_ratio
85
+ self.pc_sharpedge_size = pc_sharpedge_size
86
+ self.num_latents = num_latents
87
+ self.point_feats = point_feats
88
+
89
+ self.input_proj = nn.Linear(self.fourier_embedder.out_dim + point_feats, width)
90
+
91
+ self.cross_attn = ResidualCrossAttentionBlock(
92
+ width = width,
93
+ heads = heads,
94
+ qkv_bias = qkv_bias,
95
+ qk_norm = qk_norm
96
+ )
97
+
98
+ self.self_attn = None
99
+ if layers > 0:
100
+ self.self_attn = Transformer(
101
+ width = width,
102
+ heads = heads,
103
+ qkv_bias = qkv_bias,
104
+ qk_norm = qk_norm,
105
+ layers = layers
106
+ )
107
+
108
+ if use_ln_post:
109
+ self.ln_post = nn.LayerNorm(width)
110
+ else:
111
+ self.ln_post = None
112
+
113
+ def sample_points_and_latents(self, point_cloud: torch.Tensor, features: torch.Tensor):
114
+
115
+ """
116
+ Subsample points randomly from the point cloud (input_pc)
117
+ Further sample the subsampled points to get query_pc
118
+ take the fourier embeddings for both input and query pc
119
+
120
+ Mental Note: FPS-sampled points (query_pc) act as latent tokens that attend to and learn from the broader context in input_pc.
121
+ Goal: get a smaller represenation (query_pc) to represent the entire scence structure by learning from a broader subset (input_pc).
122
+ More computationally efficient.
123
+
124
+ Features are additional information for each point in the cloud
125
+ """
126
+
127
+ B, _, D = point_cloud.shape
128
+
129
+ num_latents = int(self.num_latents)
130
+
131
+ num_random_query = self.pc_size / (self.pc_size + self.pc_sharpedge_size) * num_latents
132
+ num_sharpedge_query = num_latents - num_random_query
133
+
134
+ # Split random and sharpedge surface points
135
+ random_pc, sharpedge_pc = torch.split(point_cloud, [self.pc_size, self.pc_sharpedge_size], dim=1)
136
+
137
+ # assert statements
138
+ assert random_pc.shape[1] <= self.pc_size, "Random surface points size must be less than or equal to pc_size"
139
+ assert sharpedge_pc.shape[1] <= self.pc_sharpedge_size, "Sharpedge surface points size must be less than or equal to pc_sharpedge_size"
140
+
141
+ input_random_pc_size = int(num_random_query * self.downsample_ratio)
142
+ random_query_pc, random_input_pc, random_idx_pc, random_idx_query = \
143
+ self.subsample(pc = random_pc, num_query = num_random_query, input_pc_size = input_random_pc_size)
144
+
145
+ input_sharpedge_pc_size = int(num_sharpedge_query * self.downsample_ratio)
146
+
147
+ if input_sharpedge_pc_size == 0:
148
+ sharpedge_input_pc = torch.zeros(B, 0, D, dtype = random_input_pc.dtype).to(point_cloud.device)
149
+ sharpedge_query_pc = torch.zeros(B, 0, D, dtype= random_query_pc.dtype).to(point_cloud.device)
150
+
151
+ else:
152
+ sharpedge_query_pc, sharpedge_input_pc, sharpedge_idx_pc, sharpedge_idx_query = \
153
+ self.subsample(pc = sharpedge_pc, num_query = num_sharpedge_query, input_pc_size = input_sharpedge_pc_size)
154
+
155
+ # concat the random and sharpedges
156
+ query_pc = torch.cat([random_query_pc, sharpedge_query_pc], dim = 1)
157
+ input_pc = torch.cat([random_input_pc, sharpedge_input_pc], dim = 1)
158
+
159
+ query = self.fourier_embedder(query_pc)
160
+ data = self.fourier_embedder(input_pc)
161
+
162
+ if self.point_feats > 0:
163
+ random_surface_features, sharpedge_surface_features = torch.split(features, [self.pc_size, self.pc_sharpedge_size], dim = 1)
164
+
165
+ input_random_surface_features, query_random_features = \
166
+ self.handle_features(features = random_surface_features, idx_pc = random_idx_pc, batch_size = B,
167
+ input_pc_size = input_random_pc_size, idx_query = random_idx_query)
168
+
169
+ if input_sharpedge_pc_size == 0:
170
+ input_sharpedge_surface_features = torch.zeros(B, 0, self.point_feats,
171
+ dtype = input_random_surface_features.dtype, device = point_cloud.device)
172
+
173
+ query_sharpedge_features = torch.zeros(B, 0, self.point_feats,
174
+ dtype = query_random_features.dtype, device = point_cloud.device)
175
+ else:
176
+
177
+ input_sharpedge_surface_features, query_sharpedge_features = \
178
+ self.handle_features(idx_pc = sharpedge_idx_pc, features = sharpedge_surface_features,
179
+ batch_size = B, idx_query = sharpedge_idx_query, input_pc_size = input_sharpedge_pc_size)
180
+
181
+ query_features = torch.cat([query_random_features, query_sharpedge_features], dim = 1)
182
+ input_features = torch.cat([input_random_surface_features, input_sharpedge_surface_features], dim = 1)
183
+
184
+ if self.normal_pe:
185
+ # apply the fourier embeddings on the first 3 dims (xyz)
186
+ input_features_pe = self.fourier_embedder(input_features[..., :3])
187
+ query_features_pe = self.fourier_embedder(query_features[..., :3])
188
+ # replace the first 3 dims with the new PE ones
189
+ input_features = torch.cat([input_features_pe, input_features[..., :3]], dim = -1)
190
+ query_features = torch.cat([query_features_pe, query_features[..., :3]], dim = -1)
191
+
192
+ # concat at the channels dim
193
+ query = torch.cat([query, query_features], dim = -1)
194
+ data = torch.cat([data, input_features], dim = -1)
195
+
196
+ # don't return pc_info to avoid unnecessary memory usuage
197
+ return query.view(B, -1, query.shape[-1]), data.view(B, -1, data.shape[-1])
198
+
199
+ def forward(self, point_cloud: torch.Tensor, features: torch.Tensor):
200
+
201
+ query, data = self.sample_points_and_latents(point_cloud = point_cloud, features = features)
202
+
203
+ # apply projections
204
+ query = self.input_proj(query)
205
+ data = self.input_proj(data)
206
+
207
+ # apply cross attention between query and data
208
+ latents = self.cross_attn(query, data)
209
+
210
+ if self.self_attn is not None:
211
+ latents = self.self_attn(latents)
212
+
213
+ if self.ln_post is not None:
214
+ latents = self.ln_post(latents)
215
+
216
+ return latents
217
+
218
+
219
+ def subsample(self, pc, num_query, input_pc_size: int):
220
+
221
+ """
222
+ num_query: number of points to keep after FPS
223
+ input_pc_size: number of points to select before FPS
224
+ """
225
+
226
+ B, _, D = pc.shape
227
+ query_ratio = num_query / input_pc_size
228
+
229
+ # random subsampling of points inside the point cloud
230
+ idx_pc = torch.randperm(pc.shape[1], device = pc.device)[:input_pc_size]
231
+ input_pc = pc[:, idx_pc, :]
232
+
233
+ # flatten to allow applying fps across the whole batch
234
+ flattent_input_pc = input_pc.view(B * input_pc_size, D)
235
+
236
+ # construct a batch_down tensor to tell fps
237
+ # which points belong to which batch
238
+ N_down = int(flattent_input_pc.shape[0] / B)
239
+ batch_down = torch.arange(B).to(pc.device)
240
+ batch_down = torch.repeat_interleave(batch_down, N_down)
241
+
242
+ idx_query = fps(flattent_input_pc, batch_down, sampling_ratio = query_ratio)
243
+ query_pc = flattent_input_pc[idx_query].view(B, -1, D)
244
+
245
+ return query_pc, input_pc, idx_pc, idx_query
246
+
247
+ def handle_features(self, features, idx_pc, input_pc_size, batch_size: int, idx_query):
248
+
249
+ B = batch_size
250
+
251
+ input_surface_features = features[:, idx_pc, :]
252
+ flattent_input_features = input_surface_features.view(B * input_pc_size, -1)
253
+ query_features = flattent_input_features[idx_query].view(B, -1,
254
+ flattent_input_features.shape[-1])
255
+
256
+ return input_surface_features, query_features
257
+
258
+ def normalize_mesh(mesh, scale = 0.9999):
259
+ """Normalize mesh to fit in [-scale, scale]. Translate mesh so its center is [0,0,0]"""
260
+
261
+ bbox = mesh.bounds
262
+ center = (bbox[1] + bbox[0]) / 2
263
+
264
+ max_extent = (bbox[1] - bbox[0]).max()
265
+ mesh.apply_translation(-center)
266
+ mesh.apply_scale((2 * scale) / max_extent)
267
+
268
+ return mesh
269
+
270
+ def sample_pointcloud(mesh, num = 200000):
271
+ """ Uniformly sample points from the surface of the mesh """
272
+
273
+ points, face_idx = mesh.sample(num, return_index = True)
274
+ normals = mesh.face_normals[face_idx]
275
+ return torch.from_numpy(points.astype(np.float32)), torch.from_numpy(normals.astype(np.float32))
276
+
277
+ def detect_sharp_edges(mesh, threshold=0.985):
278
+ """Return edge indices (a, b) that lie on sharp boundaries of the mesh."""
279
+
280
+ V, F = mesh.vertices, mesh.faces
281
+ VN, FN = mesh.vertex_normals, mesh.face_normals
282
+
283
+ sharp_mask = np.ones(V.shape[0])
284
+ for i in range(3):
285
+ indices = F[:, i]
286
+ alignment = np.einsum('ij,ij->i', VN[indices], FN)
287
+ dot_stack = np.stack((sharp_mask[indices], alignment), axis=-1)
288
+ sharp_mask[indices] = np.min(dot_stack, axis=-1)
289
+
290
+ edge_a = np.concatenate([F[:, 0], F[:, 1], F[:, 2]])
291
+ edge_b = np.concatenate([F[:, 1], F[:, 2], F[:, 0]])
292
+ sharp_edges = (sharp_mask[edge_a] < threshold) & (sharp_mask[edge_b] < threshold)
293
+
294
+ return edge_a[sharp_edges], edge_b[sharp_edges]
295
+
296
+
297
+ def sharp_sample_pointcloud(mesh, num = 16384):
298
+ """ Sample points preferentially from sharp edges in the mesh. """
299
+
300
+ edge_a, edge_b = detect_sharp_edges(mesh)
301
+ V, VN = mesh.vertices, mesh.vertex_normals
302
+
303
+ va, vb = V[edge_a], V[edge_b]
304
+ na, nb = VN[edge_a], VN[edge_b]
305
+
306
+ edge_lengths = np.linalg.norm(vb - va, axis=-1)
307
+ weights = edge_lengths / edge_lengths.sum()
308
+
309
+ indices = np.searchsorted(np.cumsum(weights), np.random.rand(num))
310
+ t = np.random.rand(num, 1)
311
+
312
+ samples = t * va[indices] + (1 - t) * vb[indices]
313
+ normals = t * na[indices] + (1 - t) * nb[indices]
314
+
315
+ return samples.astype(np.float32), normals.astype(np.float32)
316
+
317
+ def load_surface_sharpedge(mesh, num_points=4096, num_sharp_points=4096, sharpedge_flag = True, device = "cuda"):
318
+ """Load a surface with optional sharp-edge annotations from a trimesh mesh."""
319
+
320
+ import trimesh
321
+
322
+ try:
323
+ mesh_full = trimesh.util.concatenate(mesh.dump())
324
+ except Exception:
325
+ mesh_full = trimesh.util.concatenate(mesh)
326
+
327
+ mesh_full = normalize_mesh(mesh_full)
328
+
329
+ faces = mesh_full.faces
330
+ vertices = mesh_full.vertices
331
+ origin_face_count = faces.shape[0]
332
+
333
+ mesh_surface = trimesh.Trimesh(vertices=vertices, faces=faces[:origin_face_count])
334
+ mesh_fill = trimesh.Trimesh(vertices=vertices, faces=faces[origin_face_count:])
335
+
336
+ area_surface = mesh_surface.area
337
+ area_fill = mesh_fill.area
338
+ total_area = area_surface + area_fill
339
+
340
+ sample_num = 499712 // 2
341
+ fill_ratio = area_fill / total_area if total_area > 0 else 0
342
+
343
+ num_fill = int(sample_num * fill_ratio)
344
+ num_surface = sample_num - num_fill
345
+
346
+ surf_pts, surf_normals = sample_pointcloud(mesh_surface, num_surface)
347
+ fill_pts, fill_normals = (torch.zeros(0, 3), torch.zeros(0, 3)) if num_fill == 0 else sample_pointcloud(mesh_fill, num_fill)
348
+
349
+ sharp_pts, sharp_normals = sharp_sample_pointcloud(mesh_surface, sample_num)
350
+
351
+ def assemble_tensor(points, normals, label=None):
352
+
353
+ data = torch.cat([points, normals], dim=1).half().to(device)
354
+
355
+ if label is not None:
356
+ label_tensor = torch.full((data.shape[0], 1), float(label), dtype=torch.float16).to(device)
357
+ data = torch.cat([data, label_tensor], dim=1)
358
+
359
+ return data
360
+
361
+ surface = assemble_tensor(torch.cat([surf_pts.to(device), fill_pts.to(device)], dim=0),
362
+ torch.cat([surf_normals.to(device), fill_normals.to(device)], dim=0),
363
+ label = 0 if sharpedge_flag else None)
364
+
365
+ sharp_surface = assemble_tensor(torch.from_numpy(sharp_pts), torch.from_numpy(sharp_normals),
366
+ label = 1 if sharpedge_flag else None)
367
+
368
+ rng = np.random.default_rng()
369
+
370
+ surface = surface[rng.choice(surface.shape[0], num_points, replace = False)]
371
+ sharp_surface = sharp_surface[rng.choice(sharp_surface.shape[0], num_sharp_points, replace = False)]
372
+
373
+ full = torch.cat([surface, sharp_surface], dim = 0).unsqueeze(0)
374
+
375
+ return full
376
+
377
+ class SharpEdgeSurfaceLoader:
378
+ """ Load mesh surface and sharp edge samples. """
379
+
380
+ def __init__(self, num_uniform_points = 8192, num_sharp_points = 8192):
381
+
382
+ self.num_uniform_points = num_uniform_points
383
+ self.num_sharp_points = num_sharp_points
384
+ self.total_points = num_uniform_points + num_sharp_points
385
+
386
+ def __call__(self, mesh_input, device = "cuda"):
387
+ mesh = self._load_mesh(mesh_input)
388
+ return load_surface_sharpedge(mesh, self.num_uniform_points, self.num_sharp_points, device = device)
389
+
390
+ @staticmethod
391
+ def _load_mesh(mesh_input):
392
+ import trimesh
393
+
394
+ if isinstance(mesh_input, str):
395
+ mesh = trimesh.load(mesh_input, force="mesh", merge_primitives = True)
396
+ else:
397
+ mesh = mesh_input
398
+
399
+ if isinstance(mesh, trimesh.Scene):
400
+ combined = None
401
+ for obj in mesh.geometry.values():
402
+ combined = obj if combined is None else combined + obj
403
+ return combined
404
+
405
+ return mesh
406
+
407
+ class DiagonalGaussianDistribution:
408
+ def __init__(self, params: torch.Tensor, feature_dim: int = -1):
409
+
410
+ # divide quant channels (8) into mean and log variance
411
+ self.mean, self.logvar = torch.chunk(params, 2, dim = feature_dim)
412
+
413
+ self.logvar = torch.clamp(self.logvar, -30.0, 20.0)
414
+ self.std = torch.exp(0.5 * self.logvar)
415
+
416
+ def sample(self):
417
+
418
+ eps = torch.randn_like(self.std)
419
+ z = self.mean + eps * self.std
420
+
421
+ return z
422
+
423
+ ################################################
424
+ # Volume Decoder
425
+ ################################################
426
+
427
+ class VanillaVolumeDecoder():
428
+ @torch.no_grad()
429
+ def __call__(self, latents: torch.Tensor, geo_decoder: callable, octree_resolution: int, bounds = 1.01,
430
+ num_chunks: int = 10_000, enable_pbar: bool = True, **kwargs):
431
+
432
+ if isinstance(bounds, float):
433
+ bounds = [-bounds, -bounds, -bounds, bounds, bounds, bounds]
434
+
435
+ bbox_min, bbox_max = torch.tensor(bounds[:3]), torch.tensor(bounds[3:])
436
+
437
+ x = torch.linspace(bbox_min[0], bbox_max[0], int(octree_resolution) + 1, dtype = torch.float32)
438
+ y = torch.linspace(bbox_min[1], bbox_max[1], int(octree_resolution) + 1, dtype = torch.float32)
439
+ z = torch.linspace(bbox_min[2], bbox_max[2], int(octree_resolution) + 1, dtype = torch.float32)
440
+
441
+ [xs, ys, zs] = torch.meshgrid(x, y, z, indexing = "ij")
442
+ xyz = torch.stack((xs, ys, zs), axis=-1).to(latents.device, dtype = latents.dtype).contiguous().reshape(-1, 3)
443
+ grid_size = [int(octree_resolution) + 1, int(octree_resolution) + 1, int(octree_resolution) + 1]
444
+
445
+ batch_logits = []
446
+ for start in tqdm(range(0, xyz.shape[0], num_chunks), desc="Volume Decoding",
447
+ disable=not enable_pbar):
448
+
449
+ chunk_queries = xyz[start: start + num_chunks, :]
450
+ chunk_queries = chunk_queries.unsqueeze(0).repeat(latents.shape[0], 1, 1)
451
+ logits = geo_decoder(queries = chunk_queries, latents = latents)
452
+ batch_logits.append(logits)
453
+
454
+ grid_logits = torch.cat(batch_logits, dim = 1)
455
+ grid_logits = grid_logits.view((latents.shape[0], *grid_size)).float()
456
+
457
+ return grid_logits
458
+
459
+ class FourierEmbedder(nn.Module):
460
+ """The sin/cosine positional embedding. Given an input tensor `x` of shape [n_batch, ..., c_dim], it converts
461
+ each feature dimension of `x[..., i]` into:
462
+ [
463
+ sin(x[..., i]),
464
+ sin(f_1*x[..., i]),
465
+ sin(f_2*x[..., i]),
466
+ ...
467
+ sin(f_N * x[..., i]),
468
+ cos(x[..., i]),
469
+ cos(f_1*x[..., i]),
470
+ cos(f_2*x[..., i]),
471
+ ...
472
+ cos(f_N * x[..., i]),
473
+ x[..., i] # only present if include_input is True.
474
+ ], here f_i is the frequency.
475
+
476
+ Denote the space is [0 / num_freqs, 1 / num_freqs, 2 / num_freqs, 3 / num_freqs, ..., (num_freqs - 1) / num_freqs].
477
+ If logspace is True, then the frequency f_i is [2^(0 / num_freqs), ..., 2^(i / num_freqs), ...];
478
+ Otherwise, the frequencies are linearly spaced between [1.0, 2^(num_freqs - 1)].
479
+
480
+ Args:
481
+ num_freqs (int): the number of frequencies, default is 6;
482
+ logspace (bool): If logspace is True, then the frequency f_i is [..., 2^(i / num_freqs), ...],
483
+ otherwise, the frequencies are linearly spaced between [1.0, 2^(num_freqs - 1)];
484
+ input_dim (int): the input dimension, default is 3;
485
+ include_input (bool): include the input tensor or not, default is True.
486
+
487
+ Attributes:
488
+ frequencies (torch.Tensor): If logspace is True, then the frequency f_i is [..., 2^(i / num_freqs), ...],
489
+ otherwise, the frequencies are linearly spaced between [1.0, 2^(num_freqs - 1);
490
+
491
+ out_dim (int): the embedding size, if include_input is True, it is input_dim * (num_freqs * 2 + 1),
492
+ otherwise, it is input_dim * num_freqs * 2.
493
+
494
+ """
495
+
496
+ def __init__(self,
497
+ num_freqs: int = 6,
498
+ logspace: bool = True,
499
+ input_dim: int = 3,
500
+ include_input: bool = True,
501
+ include_pi: bool = True) -> None:
502
+
503
+ """The initialization"""
504
+
505
+ super().__init__()
506
+
507
+ if logspace:
508
+ frequencies = 2.0 ** torch.arange(
509
+ num_freqs,
510
+ dtype=torch.float32
511
+ )
512
+ else:
513
+ frequencies = torch.linspace(
514
+ 1.0,
515
+ 2.0 ** (num_freqs - 1),
516
+ num_freqs,
517
+ dtype=torch.float32
518
+ )
519
+
520
+ if include_pi:
521
+ frequencies *= torch.pi
522
+
523
+ self.register_buffer("frequencies", frequencies, persistent=False)
524
+ self.include_input = include_input
525
+ self.num_freqs = num_freqs
526
+
527
+ self.out_dim = self.get_dims(input_dim)
528
+
529
+ def get_dims(self, input_dim):
530
+ temp = 1 if self.include_input or self.num_freqs == 0 else 0
531
+ out_dim = input_dim * (self.num_freqs * 2 + temp)
532
+
533
+ return out_dim
534
+
535
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
536
+ """ Forward process.
537
+
538
+ Args:
539
+ x: tensor of shape [..., dim]
540
+
541
+ Returns:
542
+ embedding: an embedding of `x` of shape [..., dim * (num_freqs * 2 + temp)]
543
+ where temp is 1 if include_input is True and 0 otherwise.
544
+ """
545
+
546
+ if self.num_freqs > 0:
547
+ embed = (x[..., None].contiguous() * self.frequencies.to(device=x.device, dtype=x.dtype)).view(*x.shape[:-1], -1)
548
+ if self.include_input:
549
+ return torch.cat((x, embed.sin(), embed.cos()), dim=-1)
550
+ else:
551
+ return torch.cat((embed.sin(), embed.cos()), dim=-1)
552
+ else:
553
+ return x
554
+
555
+ class CrossAttentionProcessor:
556
+ def __call__(self, attn, q, k, v):
557
+ out = comfy.ops.scaled_dot_product_attention(q, k, v)
558
+ return out
559
+
560
+ class DropPath(nn.Module):
561
+ """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
562
+ """
563
+
564
+ def __init__(self, drop_prob: float = 0., scale_by_keep: bool = True):
565
+ super(DropPath, self).__init__()
566
+ self.drop_prob = drop_prob
567
+ self.scale_by_keep = scale_by_keep
568
+
569
+ def forward(self, x):
570
+ """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
571
+
572
+ This is the same as the DropConnect impl I created for EfficientNet, etc networks, however,
573
+ the original name is misleading as 'Drop Connect' is a different form of dropout in a separate paper...
574
+ See discussion: https://github.com/tensorflow/tpu/issues/494#issuecomment-532968956 ... I've opted for
575
+ changing the layer and argument names to 'drop path' rather than mix DropConnect as a layer name and use
576
+ 'survival rate' as the argument.
577
+
578
+ """
579
+ if self.drop_prob == 0. or not self.training:
580
+ return x
581
+ keep_prob = 1 - self.drop_prob
582
+ shape = (x.shape[0],) + (1,) * (x.ndim - 1) # work with diff dim tensors, not just 2D ConvNets
583
+ random_tensor = x.new_empty(shape).bernoulli_(keep_prob)
584
+ if keep_prob > 0.0 and self.scale_by_keep:
585
+ random_tensor.div_(keep_prob)
586
+ return x * random_tensor
587
+
588
+ def extra_repr(self):
589
+ return f'drop_prob={round(self.drop_prob, 3):0.3f}'
590
+
591
+
592
+ class MLP(nn.Module):
593
+ def __init__(
594
+ self, *,
595
+ width: int,
596
+ expand_ratio: int = 4,
597
+ output_width: int = None,
598
+ drop_path_rate: float = 0.0
599
+ ):
600
+ super().__init__()
601
+ self.width = width
602
+ self.c_fc = ops.Linear(width, width * expand_ratio)
603
+ self.c_proj = ops.Linear(width * expand_ratio, output_width if output_width is not None else width)
604
+ self.gelu = nn.GELU()
605
+ self.drop_path = DropPath(drop_path_rate) if drop_path_rate > 0. else nn.Identity()
606
+
607
+ def forward(self, x):
608
+ return self.drop_path(self.c_proj(self.gelu(self.c_fc(x))))
609
+
610
+ class QKVMultiheadCrossAttention(nn.Module):
611
+ def __init__(
612
+ self,
613
+ heads: int,
614
+ n_data = None,
615
+ width=None,
616
+ qk_norm=False,
617
+ norm_layer=ops.LayerNorm
618
+ ):
619
+ super().__init__()
620
+ self.heads = heads
621
+ self.n_data = n_data
622
+ self.q_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
623
+ self.k_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
624
+
625
+ def forward(self, q, kv):
626
+
627
+ _, n_ctx, _ = q.shape
628
+ bs, n_data, width = kv.shape
629
+
630
+ attn_ch = width // self.heads // 2
631
+ q = q.view(bs, n_ctx, self.heads, -1)
632
+
633
+ kv = kv.view(bs, n_data, self.heads, -1)
634
+ k, v = torch.split(kv, attn_ch, dim=-1)
635
+
636
+ q = self.q_norm(q)
637
+ k = self.k_norm(k)
638
+
639
+ q, k, v = [t.permute(0, 2, 1, 3) for t in (q, k, v)]
640
+ out = F.scaled_dot_product_attention(q, k, v)
641
+
642
+ out = out.transpose(1, 2).reshape(bs, n_ctx, -1)
643
+
644
+ return out
645
+
646
+ class MultiheadCrossAttention(nn.Module):
647
+ def __init__(
648
+ self,
649
+ *,
650
+ width: int,
651
+ heads: int,
652
+ qkv_bias: bool = True,
653
+ data_width: Optional[int] = None,
654
+ norm_layer=ops.LayerNorm,
655
+ qk_norm: bool = False,
656
+ kv_cache: bool = False,
657
+ ):
658
+ super().__init__()
659
+ self.width = width
660
+ self.heads = heads
661
+ self.data_width = width if data_width is None else data_width
662
+ self.c_q = ops.Linear(width, width, bias=qkv_bias)
663
+ self.c_kv = ops.Linear(self.data_width, width * 2, bias=qkv_bias)
664
+ self.c_proj = ops.Linear(width, width)
665
+ self.attention = QKVMultiheadCrossAttention(
666
+ heads=heads,
667
+ width=width,
668
+ norm_layer=norm_layer,
669
+ qk_norm=qk_norm
670
+ )
671
+ self.kv_cache = kv_cache
672
+ self.data = None
673
+
674
+ def forward(self, x, data):
675
+ x = self.c_q(x)
676
+ if self.kv_cache:
677
+ if self.data is None:
678
+ self.data = self.c_kv(data)
679
+ logging.info('Save kv cache,this should be called only once for one mesh')
680
+ data = self.data
681
+ else:
682
+ data = self.c_kv(data)
683
+ x = self.attention(x, data)
684
+ x = self.c_proj(x)
685
+ return x
686
+
687
+ class ResidualCrossAttentionBlock(nn.Module):
688
+ def __init__(
689
+ self,
690
+ *,
691
+ width: int,
692
+ heads: int,
693
+ mlp_expand_ratio: int = 4,
694
+ data_width: Optional[int] = None,
695
+ qkv_bias: bool = True,
696
+ norm_layer=ops.LayerNorm,
697
+ qk_norm: bool = False
698
+ ):
699
+ super().__init__()
700
+
701
+ if data_width is None:
702
+ data_width = width
703
+
704
+ self.attn = MultiheadCrossAttention(
705
+ width=width,
706
+ heads=heads,
707
+ data_width=data_width,
708
+ qkv_bias=qkv_bias,
709
+ norm_layer=norm_layer,
710
+ qk_norm=qk_norm
711
+ )
712
+ self.ln_1 = norm_layer(width, elementwise_affine=True, eps=1e-6)
713
+ self.ln_2 = norm_layer(data_width, elementwise_affine=True, eps=1e-6)
714
+ self.ln_3 = norm_layer(width, elementwise_affine=True, eps=1e-6)
715
+ self.mlp = MLP(width=width, expand_ratio=mlp_expand_ratio)
716
+
717
+ def forward(self, x: torch.Tensor, data: torch.Tensor):
718
+ x = x + self.attn(self.ln_1(x), self.ln_2(data))
719
+ x = x + self.mlp(self.ln_3(x))
720
+ return x
721
+
722
+
723
+ class QKVMultiheadAttention(nn.Module):
724
+ def __init__(
725
+ self,
726
+ *,
727
+ heads: int,
728
+ width=None,
729
+ qk_norm=False,
730
+ norm_layer=ops.LayerNorm
731
+ ):
732
+ super().__init__()
733
+ self.heads = heads
734
+ self.q_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
735
+ self.k_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
736
+
737
+ def forward(self, qkv):
738
+ bs, n_ctx, width = qkv.shape
739
+ attn_ch = width // self.heads // 3
740
+ qkv = qkv.view(bs, n_ctx, self.heads, -1)
741
+ q, k, v = torch.split(qkv, attn_ch, dim=-1)
742
+
743
+ q = self.q_norm(q)
744
+ k = self.k_norm(k)
745
+
746
+ q, k, v = [t.permute(0, 2, 1, 3) for t in (q, k, v)]
747
+ out = F.scaled_dot_product_attention(q, k, v).transpose(1, 2).reshape(bs, n_ctx, -1)
748
+ return out
749
+
750
+
751
+ class MultiheadAttention(nn.Module):
752
+ def __init__(
753
+ self,
754
+ *,
755
+ width: int,
756
+ heads: int,
757
+ qkv_bias: bool,
758
+ norm_layer=ops.LayerNorm,
759
+ qk_norm: bool = False,
760
+ drop_path_rate: float = 0.0
761
+ ):
762
+ super().__init__()
763
+
764
+ self.c_qkv = ops.Linear(width, width * 3, bias=qkv_bias)
765
+ self.c_proj = ops.Linear(width, width)
766
+ self.attention = QKVMultiheadAttention(
767
+ heads=heads,
768
+ width=width,
769
+ norm_layer=norm_layer,
770
+ qk_norm=qk_norm
771
+ )
772
+ self.drop_path = DropPath(drop_path_rate) if drop_path_rate > 0. else nn.Identity()
773
+
774
+ def forward(self, x):
775
+ x = self.c_qkv(x)
776
+ x = self.attention(x)
777
+ x = self.drop_path(self.c_proj(x))
778
+ return x
779
+
780
+
781
+ class ResidualAttentionBlock(nn.Module):
782
+ def __init__(
783
+ self,
784
+ *,
785
+ width: int,
786
+ heads: int,
787
+ qkv_bias: bool = True,
788
+ norm_layer=ops.LayerNorm,
789
+ qk_norm: bool = False,
790
+ drop_path_rate: float = 0.0,
791
+ ):
792
+ super().__init__()
793
+ self.attn = MultiheadAttention(
794
+ width=width,
795
+ heads=heads,
796
+ qkv_bias=qkv_bias,
797
+ norm_layer=norm_layer,
798
+ qk_norm=qk_norm,
799
+ drop_path_rate=drop_path_rate
800
+ )
801
+ self.ln_1 = norm_layer(width, elementwise_affine=True, eps=1e-6)
802
+ self.mlp = MLP(width=width, drop_path_rate=drop_path_rate)
803
+ self.ln_2 = norm_layer(width, elementwise_affine=True, eps=1e-6)
804
+
805
+ def forward(self, x: torch.Tensor):
806
+ x = x + self.attn(self.ln_1(x))
807
+ x = x + self.mlp(self.ln_2(x))
808
+ return x
809
+
810
+
811
+ class Transformer(nn.Module):
812
+ def __init__(
813
+ self,
814
+ *,
815
+ width: int,
816
+ layers: int,
817
+ heads: int,
818
+ qkv_bias: bool = True,
819
+ norm_layer=ops.LayerNorm,
820
+ qk_norm: bool = False,
821
+ drop_path_rate: float = 0.0
822
+ ):
823
+ super().__init__()
824
+ self.width = width
825
+ self.layers = layers
826
+ self.resblocks = nn.ModuleList(
827
+ [
828
+ ResidualAttentionBlock(
829
+ width=width,
830
+ heads=heads,
831
+ qkv_bias=qkv_bias,
832
+ norm_layer=norm_layer,
833
+ qk_norm=qk_norm,
834
+ drop_path_rate=drop_path_rate
835
+ )
836
+ for _ in range(layers)
837
+ ]
838
+ )
839
+
840
+ def forward(self, x: torch.Tensor):
841
+ for block in self.resblocks:
842
+ x = block(x)
843
+ return x
844
+
845
+
846
+ class CrossAttentionDecoder(nn.Module):
847
+
848
+ def __init__(
849
+ self,
850
+ *,
851
+ out_channels: int,
852
+ fourier_embedder: FourierEmbedder,
853
+ width: int,
854
+ heads: int,
855
+ mlp_expand_ratio: int = 4,
856
+ downsample_ratio: int = 1,
857
+ enable_ln_post: bool = True,
858
+ qkv_bias: bool = True,
859
+ qk_norm: bool = False,
860
+ label_type: str = "binary"
861
+ ):
862
+ super().__init__()
863
+
864
+ self.enable_ln_post = enable_ln_post
865
+ self.fourier_embedder = fourier_embedder
866
+ self.downsample_ratio = downsample_ratio
867
+ self.query_proj = ops.Linear(self.fourier_embedder.out_dim, width)
868
+ if self.downsample_ratio != 1:
869
+ self.latents_proj = ops.Linear(width * downsample_ratio, width)
870
+ if not self.enable_ln_post:
871
+ qk_norm = False
872
+ self.cross_attn_decoder = ResidualCrossAttentionBlock(
873
+ width=width,
874
+ mlp_expand_ratio=mlp_expand_ratio,
875
+ heads=heads,
876
+ qkv_bias=qkv_bias,
877
+ qk_norm=qk_norm
878
+ )
879
+
880
+ if self.enable_ln_post:
881
+ self.ln_post = ops.LayerNorm(width)
882
+ self.output_proj = ops.Linear(width, out_channels)
883
+ self.label_type = label_type
884
+ self.count = 0
885
+
886
+ def forward(self, queries=None, query_embeddings=None, latents=None):
887
+ if query_embeddings is None:
888
+ query_embeddings = self.query_proj(self.fourier_embedder(queries).to(latents.dtype))
889
+ self.count += query_embeddings.shape[1]
890
+ if self.downsample_ratio != 1:
891
+ latents = self.latents_proj(latents)
892
+ x = self.cross_attn_decoder(query_embeddings, latents)
893
+ if self.enable_ln_post:
894
+ x = self.ln_post(x)
895
+ occ = self.output_proj(x)
896
+ return occ
897
+
898
+
899
+ class ShapeVAE(nn.Module):
900
+ def __init__(
901
+ self,
902
+ *,
903
+ num_latents: int = 4096,
904
+ embed_dim: int = 64,
905
+ width: int = 1024,
906
+ heads: int = 16,
907
+ num_decoder_layers: int = 16,
908
+ num_encoder_layers: int = 8,
909
+ pc_size: int = 81920,
910
+ pc_sharpedge_size: int = 0,
911
+ point_feats: int = 4,
912
+ downsample_ratio: int = 20,
913
+ geo_decoder_downsample_ratio: int = 1,
914
+ geo_decoder_mlp_expand_ratio: int = 4,
915
+ geo_decoder_ln_post: bool = True,
916
+ num_freqs: int = 8,
917
+ qkv_bias: bool = False,
918
+ qk_norm: bool = True,
919
+ drop_path_rate: float = 0.0,
920
+ include_pi: bool = False,
921
+ scale_factor: float = 1.0039506158752403,
922
+ label_type: str = "binary",
923
+ ):
924
+ super().__init__()
925
+ self.geo_decoder_ln_post = geo_decoder_ln_post
926
+
927
+ self.fourier_embedder = FourierEmbedder(num_freqs=num_freqs, include_pi=include_pi)
928
+
929
+ self.encoder = PointCrossAttention(layers = num_encoder_layers,
930
+ num_latents = num_latents,
931
+ downsample_ratio = downsample_ratio,
932
+ heads = heads,
933
+ pc_size = pc_size,
934
+ width = width,
935
+ point_feats = point_feats,
936
+ fourier_embedder = self.fourier_embedder,
937
+ pc_sharpedge_size = pc_sharpedge_size)
938
+
939
+ self.post_kl = ops.Linear(embed_dim, width)
940
+
941
+ self.transformer = Transformer(
942
+ width=width,
943
+ layers=num_decoder_layers,
944
+ heads=heads,
945
+ qkv_bias=qkv_bias,
946
+ qk_norm=qk_norm,
947
+ drop_path_rate=drop_path_rate
948
+ )
949
+
950
+ self.geo_decoder = CrossAttentionDecoder(
951
+ fourier_embedder=self.fourier_embedder,
952
+ out_channels=1,
953
+ mlp_expand_ratio=geo_decoder_mlp_expand_ratio,
954
+ downsample_ratio=geo_decoder_downsample_ratio,
955
+ enable_ln_post=self.geo_decoder_ln_post,
956
+ width=width // geo_decoder_downsample_ratio,
957
+ heads=heads // geo_decoder_downsample_ratio,
958
+ qkv_bias=qkv_bias,
959
+ qk_norm=qk_norm,
960
+ label_type=label_type,
961
+ )
962
+
963
+ self.volume_decoder = VanillaVolumeDecoder()
964
+ self.scale_factor = scale_factor
965
+
966
+ def decode(self, latents, **kwargs):
967
+ latents = self.post_kl(latents.movedim(-2, -1))
968
+ latents = self.transformer(latents)
969
+
970
+ bounds = kwargs.get("bounds", 1.01)
971
+ num_chunks = kwargs.get("num_chunks", 8000)
972
+ octree_resolution = kwargs.get("octree_resolution", 256)
973
+ enable_pbar = kwargs.get("enable_pbar", True)
974
+
975
+ grid_logits = self.volume_decoder(latents, self.geo_decoder, bounds=bounds, num_chunks=num_chunks, octree_resolution=octree_resolution, enable_pbar=enable_pbar)
976
+ return grid_logits.movedim(-2, -1)
977
+
978
+ def encode(self, surface):
979
+
980
+ pc, feats = surface[:, :, :3], surface[:, :, 3:]
981
+ latents = self.encoder(pc, feats)
982
+
983
+ moments = self.pre_kl(latents)
984
+ posterior = DiagonalGaussianDistribution(moments, feature_dim = -1)
985
+
986
+ latents = posterior.sample()
987
+
988
+ return latents
ComfyUI/comfy/ldm/hunyuan3dv2_1/hunyuandit.py ADDED
@@ -0,0 +1,668 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+ from comfy.ldm.modules.attention import optimized_attention
6
+ import comfy.model_management
7
+
8
+ class GELU(nn.Module):
9
+
10
+ def __init__(self, dim_in: int, dim_out: int, operations, device, dtype):
11
+ super().__init__()
12
+ self.proj = operations.Linear(dim_in, dim_out, device = device, dtype = dtype)
13
+
14
+ def gelu(self, gate: torch.Tensor) -> torch.Tensor:
15
+
16
+ if gate.device.type == "mps":
17
+ return F.gelu(gate.to(dtype = torch.float32)).to(dtype = gate.dtype)
18
+
19
+ return F.gelu(gate)
20
+
21
+ def forward(self, hidden_states):
22
+
23
+ hidden_states = self.proj(hidden_states)
24
+ hidden_states = self.gelu(hidden_states)
25
+
26
+ return hidden_states
27
+
28
+ class FeedForward(nn.Module):
29
+
30
+ def __init__(self, dim: int, dim_out = None, mult: int = 4,
31
+ dropout: float = 0.0, inner_dim = None, operations = None, device = None, dtype = None):
32
+
33
+ super().__init__()
34
+ if inner_dim is None:
35
+ inner_dim = int(dim * mult)
36
+
37
+ dim_out = dim_out if dim_out is not None else dim
38
+
39
+ act_fn = GELU(dim, inner_dim, operations = operations, device = device, dtype = dtype)
40
+
41
+ self.net = nn.ModuleList([])
42
+ self.net.append(act_fn)
43
+
44
+ self.net.append(nn.Dropout(dropout))
45
+ self.net.append(operations.Linear(inner_dim, dim_out, device = device, dtype = dtype))
46
+
47
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
48
+ for module in self.net:
49
+ hidden_states = module(hidden_states)
50
+ return hidden_states
51
+
52
+ class AddAuxLoss(torch.autograd.Function):
53
+
54
+ @staticmethod
55
+ def forward(ctx, x, loss):
56
+ # do nothing in forward (no computation)
57
+ ctx.requires_aux_loss = loss.requires_grad
58
+ ctx.dtype = loss.dtype
59
+
60
+ return x
61
+
62
+ @staticmethod
63
+ def backward(ctx, grad_output):
64
+ # add the aux loss gradients
65
+ grad_loss = None
66
+ # put the aux grad the same as the main grad loss
67
+ # aux grad contributes equally
68
+ if ctx.requires_aux_loss:
69
+ grad_loss = torch.ones(1, dtype = ctx.dtype, device = grad_output.device)
70
+
71
+ return grad_output, grad_loss
72
+
73
+ class MoEGate(nn.Module):
74
+
75
+ def __init__(self, embed_dim, num_experts=16, num_experts_per_tok=2, aux_loss_alpha=0.01, device = None, dtype = None):
76
+
77
+ super().__init__()
78
+ self.top_k = num_experts_per_tok
79
+ self.n_routed_experts = num_experts
80
+
81
+ self.alpha = aux_loss_alpha
82
+
83
+ self.gating_dim = embed_dim
84
+ self.weight = nn.Parameter(torch.empty((self.n_routed_experts, self.gating_dim), device = device, dtype = dtype))
85
+
86
+ def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
87
+
88
+ # flatten hidden states
89
+ hidden_states = hidden_states.view(-1, hidden_states.size(-1))
90
+
91
+ # get logits and pass it to softmax
92
+ logits = F.linear(hidden_states, comfy.model_management.cast_to(self.weight, dtype=hidden_states.dtype, device=hidden_states.device), bias = None)
93
+ scores = logits.softmax(dim = -1)
94
+
95
+ topk_weight, topk_idx = torch.topk(scores, k = self.top_k, dim = -1, sorted = False)
96
+
97
+ if self.training and self.alpha > 0.0:
98
+ scores_for_aux = scores
99
+
100
+ # used bincount instead of one hot encoding
101
+ counts = torch.bincount(topk_idx.view(-1), minlength = self.n_routed_experts).float()
102
+ ce = counts / topk_idx.numel() # normalized expert usage
103
+
104
+ # mean expert score
105
+ Pi = scores_for_aux.mean(0)
106
+
107
+ # expert balance loss
108
+ aux_loss = (Pi * ce * self.n_routed_experts).sum() * self.alpha
109
+ else:
110
+ aux_loss = None
111
+
112
+ return topk_idx, topk_weight, aux_loss
113
+
114
+ class MoEBlock(nn.Module):
115
+ def __init__(self, dim, num_experts: int = 6, moe_top_k: int = 2, dropout: float = 0.0,
116
+ ff_inner_dim: int = None, operations = None, device = None, dtype = None):
117
+ super().__init__()
118
+
119
+ self.moe_top_k = moe_top_k
120
+ self.num_experts = num_experts
121
+
122
+ self.experts = nn.ModuleList([
123
+ FeedForward(dim, dropout = dropout, inner_dim = ff_inner_dim, operations = operations, device = device, dtype = dtype)
124
+ for _ in range(num_experts)
125
+ ])
126
+
127
+ self.gate = MoEGate(dim, num_experts = num_experts, num_experts_per_tok = moe_top_k, device = device, dtype = dtype)
128
+ self.shared_experts = FeedForward(dim, dropout = dropout, inner_dim = ff_inner_dim, operations = operations, device = device, dtype = dtype)
129
+
130
+ def forward(self, hidden_states) -> torch.Tensor:
131
+
132
+ identity = hidden_states
133
+ orig_shape = hidden_states.shape
134
+ topk_idx, topk_weight, aux_loss = self.gate(hidden_states)
135
+
136
+ hidden_states = hidden_states.view(-1, hidden_states.shape[-1])
137
+ flat_topk_idx = topk_idx.view(-1)
138
+
139
+ if self.training:
140
+
141
+ hidden_states = hidden_states.repeat_interleave(self.moe_top_k, dim = 0)
142
+ y = torch.empty_like(hidden_states, dtype = hidden_states.dtype)
143
+
144
+ for i, expert in enumerate(self.experts):
145
+ tmp = expert(hidden_states[flat_topk_idx == i])
146
+ y[flat_topk_idx == i] = tmp.to(hidden_states.dtype)
147
+
148
+ y = (y.view(*topk_weight.shape, -1) * topk_weight.unsqueeze(-1)).sum(dim = 1)
149
+ y = y.view(*orig_shape)
150
+
151
+ y = AddAuxLoss.apply(y, aux_loss)
152
+ else:
153
+ y = self.moe_infer(hidden_states, flat_expert_indices = flat_topk_idx,flat_expert_weights = topk_weight.view(-1, 1)).view(*orig_shape)
154
+
155
+ y = y + self.shared_experts(identity)
156
+
157
+ return y
158
+
159
+ @torch.no_grad()
160
+ def moe_infer(self, x, flat_expert_indices, flat_expert_weights):
161
+
162
+ expert_cache = torch.zeros_like(x)
163
+ idxs = flat_expert_indices.argsort()
164
+
165
+ # no need for .numpy().cpu() here
166
+ tokens_per_expert = flat_expert_indices.bincount().cumsum(0)
167
+ token_idxs = idxs // self.moe_top_k
168
+
169
+ for i, end_idx in enumerate(tokens_per_expert):
170
+
171
+ start_idx = 0 if i == 0 else tokens_per_expert[i-1]
172
+
173
+ if start_idx == end_idx:
174
+ continue
175
+
176
+ expert = self.experts[i]
177
+ exp_token_idx = token_idxs[start_idx:end_idx]
178
+
179
+ expert_tokens = x[exp_token_idx]
180
+ expert_out = expert(expert_tokens)
181
+
182
+ expert_out.mul_(flat_expert_weights[idxs[start_idx:end_idx]])
183
+
184
+ # use index_add_ with a 1-D index tensor directly avoids building a large [N, D] index map and extra memcopy required by scatter_reduce_
185
+ # + avoid dtype conversion
186
+ expert_cache.index_add_(0, exp_token_idx, expert_out)
187
+
188
+ return expert_cache
189
+
190
+ class Timesteps(nn.Module):
191
+ def __init__(self, num_channels: int, downscale_freq_shift: float = 0.0,
192
+ scale: float = 1.0, max_period: int = 10000):
193
+ super().__init__()
194
+
195
+ self.num_channels = num_channels
196
+ half_dim = num_channels // 2
197
+
198
+ # precompute the “inv_freq” vector once
199
+ exponent = -math.log(max_period) * torch.arange(
200
+ half_dim, dtype=torch.float32
201
+ ) / (half_dim - downscale_freq_shift)
202
+
203
+ inv_freq = torch.exp(exponent)
204
+
205
+ # pad
206
+ if num_channels % 2 == 1:
207
+ # we’ll pad a zero at the end of the cos-half
208
+ inv_freq = torch.cat([inv_freq, inv_freq.new_zeros(1)])
209
+
210
+ # register to buffer so it moves with the device
211
+ self.register_buffer("inv_freq", inv_freq, persistent = False)
212
+ self.scale = scale
213
+
214
+ def forward(self, timesteps: torch.Tensor):
215
+
216
+ x = timesteps.float().unsqueeze(1) * self.inv_freq.to(timesteps.device).unsqueeze(0)
217
+
218
+
219
+ # fused CUDA kernels for sin and cos
220
+ sin_emb = x.sin()
221
+ cos_emb = x.cos()
222
+
223
+ emb = torch.cat([sin_emb, cos_emb], dim = 1)
224
+
225
+ # scale factor
226
+ if self.scale != 1.0:
227
+ emb = emb * self.scale
228
+
229
+ # If we padded inv_freq for odd, emb is already wide enough; otherwise:
230
+ if emb.shape[1] > self.num_channels:
231
+ emb = emb[:, :self.num_channels]
232
+
233
+ return emb
234
+
235
+ class TimestepEmbedder(nn.Module):
236
+ def __init__(self, hidden_size, frequency_embedding_size = 256, cond_proj_dim = None, operations = None, device = None, dtype = None):
237
+ super().__init__()
238
+
239
+ self.mlp = nn.Sequential(
240
+ operations.Linear(hidden_size, frequency_embedding_size, bias=True, device = device, dtype = dtype),
241
+ nn.GELU(),
242
+ operations.Linear(frequency_embedding_size, hidden_size, bias=True, device = device, dtype = dtype),
243
+ )
244
+ self.frequency_embedding_size = frequency_embedding_size
245
+
246
+ if cond_proj_dim is not None:
247
+ self.cond_proj = operations.Linear(cond_proj_dim, frequency_embedding_size, bias=False, device = device, dtype = dtype)
248
+
249
+ self.time_embed = Timesteps(hidden_size)
250
+
251
+ def forward(self, timesteps, condition):
252
+
253
+ timestep_embed = self.time_embed(timesteps).type(self.mlp[0].weight.dtype)
254
+
255
+ if condition is not None:
256
+ cond_embed = self.cond_proj(condition)
257
+ timestep_embed = timestep_embed + cond_embed
258
+
259
+ time_conditioned = self.mlp(timestep_embed)
260
+
261
+ # for broadcasting with image tokens
262
+ return time_conditioned.unsqueeze(1)
263
+
264
+ class MLP(nn.Module):
265
+ def __init__(self, *, width: int, operations = None, device = None, dtype = None):
266
+ super().__init__()
267
+ self.width = width
268
+ self.fc1 = operations.Linear(width, width * 4, device = device, dtype = dtype)
269
+ self.fc2 = operations.Linear(width * 4, width, device = device, dtype = dtype)
270
+ self.gelu = nn.GELU()
271
+
272
+ def forward(self, x):
273
+ return self.fc2(self.gelu(self.fc1(x)))
274
+
275
+ class CrossAttention(nn.Module):
276
+ def __init__(
277
+ self,
278
+ qdim,
279
+ kdim,
280
+ num_heads,
281
+ qkv_bias=True,
282
+ qk_norm=False,
283
+ norm_layer=nn.LayerNorm,
284
+ use_fp16: bool = False,
285
+ operations = None,
286
+ dtype = None,
287
+ device = None,
288
+ **kwargs,
289
+ ):
290
+ super().__init__()
291
+ self.qdim = qdim
292
+ self.kdim = kdim
293
+
294
+ self.num_heads = num_heads
295
+ self.head_dim = self.qdim // num_heads
296
+
297
+ self.scale = self.head_dim ** -0.5
298
+
299
+ self.to_q = operations.Linear(qdim, qdim, bias=qkv_bias, device = device, dtype = dtype)
300
+ self.to_k = operations.Linear(kdim, qdim, bias=qkv_bias, device = device, dtype = dtype)
301
+ self.to_v = operations.Linear(kdim, qdim, bias=qkv_bias, device = device, dtype = dtype)
302
+
303
+ if use_fp16:
304
+ eps = 1.0 / 65504
305
+ else:
306
+ eps = 1e-6
307
+
308
+ if norm_layer == nn.LayerNorm:
309
+ norm_layer = operations.LayerNorm
310
+ else:
311
+ norm_layer = operations.RMSNorm
312
+
313
+ self.q_norm = norm_layer(self.head_dim, elementwise_affine=True, eps = eps, device = device, dtype = dtype) if qk_norm else nn.Identity()
314
+ self.k_norm = norm_layer(self.head_dim, elementwise_affine=True, eps = eps, device = device, dtype = dtype) if qk_norm else nn.Identity()
315
+ self.out_proj = operations.Linear(qdim, qdim, bias=True, device = device, dtype = dtype)
316
+
317
+ def forward(self, x, y):
318
+
319
+ b, s1, _ = x.shape
320
+ _, s2, _ = y.shape
321
+
322
+ y = y.to(next(self.to_k.parameters()).dtype)
323
+
324
+ q = self.to_q(x)
325
+ k = self.to_k(y)
326
+ v = self.to_v(y)
327
+
328
+ kv = torch.cat((k, v), dim=-1)
329
+ split_size = kv.shape[-1] // self.num_heads // 2
330
+
331
+ kv = kv.view(b, -1, self.num_heads, split_size * 2)
332
+ k, v = torch.split(kv, split_size, dim=-1)
333
+
334
+ q = q.view(b, s1, self.num_heads, self.head_dim)
335
+ k = k.view(b, s2, self.num_heads, self.head_dim)
336
+ v = v.reshape(b, s2, self.num_heads * self.head_dim)
337
+
338
+ q = self.q_norm(q)
339
+ k = self.k_norm(k)
340
+
341
+ x = optimized_attention(
342
+ q.reshape(b, s1, self.num_heads * self.head_dim),
343
+ k.reshape(b, s2, self.num_heads * self.head_dim),
344
+ v,
345
+ heads=self.num_heads,
346
+ low_precision_attention=False,
347
+ )
348
+
349
+ out = self.out_proj(x)
350
+
351
+ return out
352
+
353
+ class Attention(nn.Module):
354
+
355
+ def __init__(
356
+ self,
357
+ dim,
358
+ num_heads,
359
+ qkv_bias = True,
360
+ qk_norm = False,
361
+ norm_layer = nn.LayerNorm,
362
+ use_fp16: bool = False,
363
+ operations = None,
364
+ device = None,
365
+ dtype = None
366
+ ):
367
+ super().__init__()
368
+ self.dim = dim
369
+ self.num_heads = num_heads
370
+ self.head_dim = self.dim // num_heads
371
+ self.scale = self.head_dim ** -0.5
372
+
373
+ self.to_q = operations.Linear(dim, dim, bias = qkv_bias, device = device, dtype = dtype)
374
+ self.to_k = operations.Linear(dim, dim, bias = qkv_bias, device = device, dtype = dtype)
375
+ self.to_v = operations.Linear(dim, dim, bias = qkv_bias, device = device, dtype = dtype)
376
+
377
+ if use_fp16:
378
+ eps = 1.0 / 65504
379
+ else:
380
+ eps = 1e-6
381
+
382
+ if norm_layer == nn.LayerNorm:
383
+ norm_layer = operations.LayerNorm
384
+ else:
385
+ norm_layer = operations.RMSNorm
386
+
387
+ self.q_norm = norm_layer(self.head_dim, elementwise_affine=True, eps = eps, device = device, dtype = dtype) if qk_norm else nn.Identity()
388
+ self.k_norm = norm_layer(self.head_dim, elementwise_affine=True, eps = eps, device = device, dtype = dtype) if qk_norm else nn.Identity()
389
+ self.out_proj = operations.Linear(dim, dim, device = device, dtype = dtype)
390
+
391
+ def forward(self, x):
392
+ B, N, _ = x.shape
393
+
394
+ query = self.to_q(x)
395
+ key = self.to_k(x)
396
+ value = self.to_v(x)
397
+
398
+ qkv_combined = torch.cat((query, key, value), dim=-1)
399
+ split_size = qkv_combined.shape[-1] // self.num_heads // 3
400
+
401
+ qkv = qkv_combined.view(B, -1, self.num_heads, split_size * 3)
402
+ query, key, value = torch.split(qkv, split_size, dim=-1)
403
+
404
+ query = query.reshape(B, N, self.num_heads, self.head_dim)
405
+ key = key.reshape(B, N, self.num_heads, self.head_dim)
406
+ value = value.reshape(B, N, self.num_heads * self.head_dim)
407
+
408
+ query = self.q_norm(query)
409
+ key = self.k_norm(key)
410
+
411
+ x = optimized_attention(
412
+ query.reshape(B, N, self.num_heads * self.head_dim),
413
+ key.reshape(B, N, self.num_heads * self.head_dim),
414
+ value,
415
+ heads=self.num_heads,
416
+ low_precision_attention=False,
417
+ )
418
+
419
+ x = self.out_proj(x)
420
+ return x
421
+
422
+ class HunYuanDiTBlock(nn.Module):
423
+ def __init__(
424
+ self,
425
+ hidden_size,
426
+ c_emb_size,
427
+ num_heads,
428
+ text_states_dim=1024,
429
+ qk_norm=False,
430
+ norm_layer=nn.LayerNorm,
431
+ qk_norm_layer=True,
432
+ qkv_bias=True,
433
+ skip_connection=True,
434
+ timested_modulate=False,
435
+ use_moe: bool = False,
436
+ num_experts: int = 8,
437
+ moe_top_k: int = 2,
438
+ use_fp16: bool = False,
439
+ operations = None,
440
+ device = None, dtype = None
441
+ ):
442
+ super().__init__()
443
+
444
+ # eps can't be 1e-6 in fp16 mode because of numerical stability issues
445
+ if use_fp16:
446
+ eps = 1.0 / 65504
447
+ else:
448
+ eps = 1e-6
449
+
450
+ self.norm1 = norm_layer(hidden_size, elementwise_affine = True, eps = eps, device = device, dtype = dtype)
451
+
452
+ self.attn1 = Attention(hidden_size, num_heads=num_heads, qkv_bias=qkv_bias, qk_norm=qk_norm,
453
+ norm_layer=qk_norm_layer, use_fp16 = use_fp16, device = device, dtype = dtype, operations = operations)
454
+
455
+ self.norm2 = norm_layer(hidden_size, elementwise_affine = True, eps = eps, device = device, dtype = dtype)
456
+
457
+ self.timested_modulate = timested_modulate
458
+ if self.timested_modulate:
459
+ self.default_modulation = nn.Sequential(
460
+ nn.SiLU(),
461
+ operations.Linear(c_emb_size, hidden_size, bias=True, device = device, dtype = dtype)
462
+ )
463
+
464
+ self.attn2 = CrossAttention(hidden_size, text_states_dim, num_heads=num_heads, qkv_bias=qkv_bias,
465
+ qk_norm=qk_norm, norm_layer=qk_norm_layer, use_fp16 = use_fp16,
466
+ device = device, dtype = dtype, operations = operations)
467
+
468
+ self.norm3 = norm_layer(hidden_size, elementwise_affine = True, eps = eps, device = device, dtype = dtype)
469
+
470
+ if skip_connection:
471
+ self.skip_norm = norm_layer(hidden_size, elementwise_affine = True, eps = eps, device = device, dtype = dtype)
472
+ self.skip_linear = operations.Linear(2 * hidden_size, hidden_size, device = device, dtype = dtype)
473
+ else:
474
+ self.skip_linear = None
475
+
476
+ self.use_moe = use_moe
477
+
478
+ if self.use_moe:
479
+ self.moe = MoEBlock(
480
+ hidden_size,
481
+ num_experts = num_experts,
482
+ moe_top_k = moe_top_k,
483
+ dropout = 0.0,
484
+ ff_inner_dim = int(hidden_size * 4.0),
485
+ device = device, dtype = dtype,
486
+ operations = operations
487
+ )
488
+ else:
489
+ self.mlp = MLP(width=hidden_size, operations=operations, device = device, dtype = dtype)
490
+
491
+ def forward(self, hidden_states, conditioning=None, text_states=None, skip_tensor=None):
492
+
493
+ if self.skip_linear is not None:
494
+ combined = torch.cat([skip_tensor, hidden_states], dim=-1)
495
+ hidden_states = self.skip_linear(combined)
496
+ hidden_states = self.skip_norm(hidden_states)
497
+
498
+ # self attention
499
+ if self.timested_modulate:
500
+ modulation_shift = self.default_modulation(conditioning).unsqueeze(dim=1)
501
+ hidden_states = hidden_states + modulation_shift
502
+
503
+ self_attn_out = self.attn1(self.norm1(hidden_states))
504
+ hidden_states = hidden_states + self_attn_out
505
+
506
+ # cross attention
507
+ hidden_states = hidden_states + self.attn2(self.norm2(hidden_states), text_states)
508
+
509
+ # MLP Layer
510
+ mlp_input = self.norm3(hidden_states)
511
+
512
+ if self.use_moe:
513
+ hidden_states = hidden_states + self.moe(mlp_input)
514
+ else:
515
+ hidden_states = hidden_states + self.mlp(mlp_input)
516
+
517
+ return hidden_states
518
+
519
+ class FinalLayer(nn.Module):
520
+
521
+ def __init__(self, final_hidden_size, out_channels, operations, use_fp16: bool = False, device = None, dtype = None):
522
+ super().__init__()
523
+
524
+ if use_fp16:
525
+ eps = 1.0 / 65504
526
+ else:
527
+ eps = 1e-6
528
+
529
+ self.norm_final = operations.LayerNorm(final_hidden_size, elementwise_affine = True, eps = eps, device = device, dtype = dtype)
530
+ self.linear = operations.Linear(final_hidden_size, out_channels, bias = True, device = device, dtype = dtype)
531
+
532
+ def forward(self, x):
533
+ x = self.norm_final(x)
534
+ x = x[:, 1:]
535
+ x = self.linear(x)
536
+ return x
537
+
538
+ class HunYuanDiTPlain(nn.Module):
539
+
540
+ # init with the defaults values from https://huggingface.co/tencent/Hunyuan3D-2.1/blob/main/hunyuan3d-dit-v2-1/config.yaml
541
+ def __init__(
542
+ self,
543
+ in_channels: int = 64,
544
+ hidden_size: int = 2048,
545
+ context_dim: int = 1024,
546
+ depth: int = 21,
547
+ num_heads: int = 16,
548
+ qk_norm: bool = True,
549
+ qkv_bias: bool = False,
550
+ num_moe_layers: int = 6,
551
+ guidance_cond_proj_dim = 2048,
552
+ norm_type = 'layer',
553
+ num_experts: int = 8,
554
+ moe_top_k: int = 2,
555
+ use_fp16: bool = False,
556
+ dtype = None,
557
+ device = None,
558
+ operations = None,
559
+ **kwargs
560
+ ):
561
+
562
+ self.dtype = dtype
563
+
564
+ super().__init__()
565
+
566
+ self.depth = depth
567
+
568
+ self.in_channels = in_channels
569
+ self.out_channels = in_channels
570
+
571
+ self.num_heads = num_heads
572
+ self.hidden_size = hidden_size
573
+
574
+ norm = operations.LayerNorm if norm_type == 'layer' else operations.RMSNorm
575
+ qk_norm = operations.RMSNorm
576
+
577
+ self.context_dim = context_dim
578
+ self.guidance_cond_proj_dim = guidance_cond_proj_dim
579
+
580
+ self.x_embedder = operations.Linear(in_channels, hidden_size, bias = True, device = device, dtype = dtype)
581
+ self.t_embedder = TimestepEmbedder(hidden_size, hidden_size * 4, cond_proj_dim = guidance_cond_proj_dim, device = device, dtype = dtype, operations = operations)
582
+
583
+
584
+ # HUnYuanDiT Blocks
585
+ self.blocks = nn.ModuleList([
586
+ HunYuanDiTBlock(hidden_size=hidden_size,
587
+ c_emb_size=hidden_size,
588
+ num_heads=num_heads,
589
+ text_states_dim=context_dim,
590
+ qk_norm=qk_norm,
591
+ norm_layer = norm,
592
+ qk_norm_layer = qk_norm,
593
+ skip_connection=layer > depth // 2,
594
+ qkv_bias=qkv_bias,
595
+ use_moe=True if depth - layer <= num_moe_layers else False,
596
+ num_experts=num_experts,
597
+ moe_top_k=moe_top_k,
598
+ use_fp16 = use_fp16,
599
+ device = device, dtype = dtype, operations = operations)
600
+ for layer in range(depth)
601
+ ])
602
+
603
+ self.depth = depth
604
+
605
+ self.final_layer = FinalLayer(hidden_size, self.out_channels, use_fp16 = use_fp16, operations = operations, device = device, dtype = dtype)
606
+
607
+ def forward(self, x, t, context, transformer_options = {}, **kwargs):
608
+
609
+ x = x.movedim(-1, -2)
610
+
611
+ swap_cfg_halves = context.shape[0] >= 2
612
+
613
+ if swap_cfg_halves:
614
+ first_half, second_half = context.chunk(2, dim = 0)
615
+ context = torch.cat([second_half, first_half], dim = 0)
616
+
617
+ main_condition = context
618
+
619
+ t = 1.0 - t
620
+
621
+ time_embedded = self.t_embedder(t, condition = kwargs.get('guidance_cond'))
622
+
623
+ x = x.to(dtype = next(self.x_embedder.parameters()).dtype)
624
+ x_embedded = self.x_embedder(x)
625
+
626
+ combined = torch.cat([time_embedded, x_embedded], dim=1)
627
+
628
+ def block_wrap(args):
629
+ return block(
630
+ args["x"],
631
+ args["t"],
632
+ args["cond"],
633
+ skip_tensor=args.get("skip"),)
634
+
635
+ skip_stack = []
636
+ patches_replace = transformer_options.get("patches_replace", {})
637
+ blocks_replace = patches_replace.get("dit", {})
638
+ for idx, block in enumerate(self.blocks):
639
+ if idx <= self.depth // 2:
640
+ skip_input = None
641
+ else:
642
+ skip_input = skip_stack.pop()
643
+
644
+ if ("block", idx) in blocks_replace:
645
+
646
+ combined = blocks_replace[("block", idx)](
647
+ {
648
+ "x": combined,
649
+ "t": time_embedded,
650
+ "cond": main_condition,
651
+ "skip": skip_input,
652
+ },
653
+ {"original_block": block_wrap},
654
+ )
655
+ else:
656
+ combined = block(combined, time_embedded, main_condition, skip_tensor=skip_input)
657
+
658
+ if idx < self.depth // 2:
659
+ skip_stack.append(combined)
660
+
661
+ output = self.final_layer(combined)
662
+ output = output.movedim(-2, -1) * (-1.0)
663
+
664
+ if swap_cfg_halves:
665
+ first_half, second_half = output.chunk(2, dim = 0)
666
+ output = torch.cat([second_half, first_half], dim = 0)
667
+
668
+ return output