Reverb commited on
Commit
854c275
·
1 Parent(s): 5693ff7

Fix: un-ignore TRELLIS.2/trellis2/models/ — was blocked by 'models/' in .gitignore

Browse files

The .gitignore rule 'models/' matched trellis2/models/ (the Python source
package), preventing all 7 model files from being committed and deployed.
HF container had no models/ directory, causing ModuleNotFoundError on import.

Changed 'models/' to '/models/' (root-only) and added explicit negation
'!TRELLIS.2/trellis2/models/' to ensure the package is always tracked.

Files changed (26) hide show
  1. .gitignore +2 -1
  2. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/__init__.py +28 -0
  3. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/__init__.py +20 -0
  4. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/attention_blocks.py +716 -0
  5. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/attention_processors.py +96 -0
  6. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/model.py +339 -0
  7. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/surface_extractors.py +164 -0
  8. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/volume_decoders.py +435 -0
  9. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/conditioner.py +257 -0
  10. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/denoisers/__init__.py +15 -0
  11. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/denoisers/hunyuan3ddit.py +404 -0
  12. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/denoisers/hunyuandit.py +596 -0
  13. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/denoisers/moe_layers.py +177 -0
  14. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/flow_matching_sit.py +354 -0
  15. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/__init__.py +97 -0
  16. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/integrators.py +142 -0
  17. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/path.py +220 -0
  18. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/transport.py +534 -0
  19. Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/utils.py +54 -0
  20. TRELLIS.2/trellis2/models/__init__.py +78 -0
  21. TRELLIS.2/trellis2/models/sc_vaes/fdg_vae.py +110 -0
  22. TRELLIS.2/trellis2/models/sc_vaes/sparse_unet_vae.py +522 -0
  23. TRELLIS.2/trellis2/models/sparse_elastic_mixin.py +24 -0
  24. TRELLIS.2/trellis2/models/sparse_structure_flow.py +247 -0
  25. TRELLIS.2/trellis2/models/sparse_structure_vae.py +306 -0
  26. TRELLIS.2/trellis2/models/structured_latent_flow.py +207 -0
.gitignore CHANGED
@@ -19,7 +19,8 @@ workspace/history/*
19
 
20
  # Model weights (downloaded at runtime, not committed)
21
  weights/
22
- models/
 
23
  *.safetensors
24
  *.bin
25
  *.pt
 
19
 
20
  # Model weights (downloaded at runtime, not committed)
21
  weights/
22
+ /models/
23
+ !TRELLIS.2/trellis2/models/
24
  *.safetensors
25
  *.bin
26
  *.pt
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/__init__.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Open Source Model Licensed under the Apache License Version 2.0
2
+ # and Other Licenses of the Third-Party Components therein:
3
+ # The below Model in this distribution may have been modified by THL A29 Limited
4
+ # ("Tencent Modifications"). All Tencent Modifications are Copyright (C) 2024 THL A29 Limited.
5
+
6
+ # Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved.
7
+ # The below software and/or models in this distribution may have been
8
+ # modified by THL A29 Limited ("Tencent Modifications").
9
+ # All Tencent Modifications are Copyright (C) THL A29 Limited.
10
+
11
+ # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
12
+ # except for the third-party components listed below.
13
+ # Hunyuan 3D does not impose any additional limitations beyond what is outlined
14
+ # in the repsective licenses of these third-party components.
15
+ # Users must comply with all terms and conditions of original licenses of these third-party
16
+ # components and must ensure that the usage of the third party components adheres to
17
+ # all relevant laws and regulations.
18
+
19
+ # For avoidance of doubts, Hunyuan 3D means the large language models and
20
+ # their software and algorithms, including trained model weights, parameters (including
21
+ # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
22
+ # fine-tuning enabling code and other elements of the foregoing made publicly available
23
+ # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
24
+
25
+
26
+ from .autoencoders import ShapeVAE
27
+ from .conditioner import DualImageEncoder, SingleImageEncoder, DinoImageEncoder, CLIPImageEncoder
28
+ from .denoisers import Hunyuan3DDiT
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/__init__.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
+ # except for the third-party components listed below.
3
+ # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
+ # in the repsective licenses of these third-party components.
5
+ # Users must comply with all terms and conditions of original licenses of these third-party
6
+ # components and must ensure that the usage of the third party components adheres to
7
+ # all relevant laws and regulations.
8
+
9
+ # For avoidance of doubts, Hunyuan 3D means the large language models and
10
+ # their software and algorithms, including trained model weights, parameters (including
11
+ # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
+ # fine-tuning enabling code and other elements of the foregoing made publicly available
13
+ # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
+
15
+ from .attention_blocks import CrossAttentionDecoder
16
+ from .attention_processors import FlashVDMCrossAttentionProcessor, CrossAttentionProcessor, \
17
+ FlashVDMTopMCrossAttentionProcessor
18
+ from .model import ShapeVAE, VectsetVAE
19
+ from .surface_extractors import SurfaceExtractors, MCSurfaceExtractor, DMCSurfaceExtractor, Latent2MeshOutput
20
+ from .volume_decoders import HierarchicalVolumeDecoding, FlashVDMVolumeDecoding, VanillaVolumeDecoder
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/attention_blocks.py ADDED
@@ -0,0 +1,716 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Open Source Model Licensed under the Apache License Version 2.0
2
+ # and Other Licenses of the Third-Party Components therein:
3
+ # The below Model in this distribution may have been modified by THL A29 Limited
4
+ # ("Tencent Modifications"). All Tencent Modifications are Copyright (C) 2024 THL A29 Limited.
5
+
6
+ # Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved.
7
+ # The below software and/or models in this distribution may have been
8
+ # modified by THL A29 Limited ("Tencent Modifications").
9
+ # All Tencent Modifications are Copyright (C) THL A29 Limited.
10
+
11
+ # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
12
+ # except for the third-party components listed below.
13
+ # Hunyuan 3D does not impose any additional limitations beyond what is outlined
14
+ # in the repsective licenses of these third-party components.
15
+ # Users must comply with all terms and conditions of original licenses of these third-party
16
+ # components and must ensure that the usage of the third party components adheres to
17
+ # all relevant laws and regulations.
18
+
19
+ # For avoidance of doubts, Hunyuan 3D means the large language models and
20
+ # their software and algorithms, including trained model weights, parameters (including
21
+ # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
22
+ # fine-tuning enabling code and other elements of the foregoing made publicly available
23
+ # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
24
+
25
+
26
+ import os
27
+ from typing import Optional, Union, List
28
+
29
+ import torch
30
+ import torch.nn as nn
31
+ from einops import rearrange
32
+ from torch import Tensor
33
+
34
+ from .attention_processors import CrossAttentionProcessor
35
+ from ...utils import logger
36
+
37
+ scaled_dot_product_attention = nn.functional.scaled_dot_product_attention
38
+
39
+ if os.environ.get('USE_SAGEATTN', '0') == '1':
40
+ try:
41
+ from sageattention import sageattn
42
+ except ImportError:
43
+ raise ImportError('Please install the package "sageattention" to use this USE_SAGEATTN.')
44
+ scaled_dot_product_attention = sageattn
45
+
46
+
47
+ class FourierEmbedder(nn.Module):
48
+ """The sin/cosine positional embedding. Given an input tensor `x` of shape [n_batch, ..., c_dim], it converts
49
+ each feature dimension of `x[..., i]` into:
50
+ [
51
+ sin(x[..., i]),
52
+ sin(f_1*x[..., i]),
53
+ sin(f_2*x[..., i]),
54
+ ...
55
+ sin(f_N * x[..., i]),
56
+ cos(x[..., i]),
57
+ cos(f_1*x[..., i]),
58
+ cos(f_2*x[..., i]),
59
+ ...
60
+ cos(f_N * x[..., i]),
61
+ x[..., i] # only present if include_input is True.
62
+ ], here f_i is the frequency.
63
+
64
+ Denote the space is [0 / num_freqs, 1 / num_freqs, 2 / num_freqs, 3 / num_freqs, ..., (num_freqs - 1) / num_freqs].
65
+ If logspace is True, then the frequency f_i is [2^(0 / num_freqs), ..., 2^(i / num_freqs), ...];
66
+ Otherwise, the frequencies are linearly spaced between [1.0, 2^(num_freqs - 1)].
67
+
68
+ Args:
69
+ num_freqs (int): the number of frequencies, default is 6;
70
+ logspace (bool): If logspace is True, then the frequency f_i is [..., 2^(i / num_freqs), ...],
71
+ otherwise, the frequencies are linearly spaced between [1.0, 2^(num_freqs - 1)];
72
+ input_dim (int): the input dimension, default is 3;
73
+ include_input (bool): include the input tensor or not, default is True.
74
+
75
+ Attributes:
76
+ frequencies (torch.Tensor): If logspace is True, then the frequency f_i is [..., 2^(i / num_freqs), ...],
77
+ otherwise, the frequencies are linearly spaced between [1.0, 2^(num_freqs - 1);
78
+
79
+ out_dim (int): the embedding size, if include_input is True, it is input_dim * (num_freqs * 2 + 1),
80
+ otherwise, it is input_dim * num_freqs * 2.
81
+
82
+ """
83
+
84
+ def __init__(self,
85
+ num_freqs: int = 6,
86
+ logspace: bool = True,
87
+ input_dim: int = 3,
88
+ include_input: bool = True,
89
+ include_pi: bool = True) -> None:
90
+
91
+ """The initialization"""
92
+
93
+ super().__init__()
94
+
95
+ if logspace:
96
+ frequencies = 2.0 ** torch.arange(
97
+ num_freqs,
98
+ dtype=torch.float32
99
+ )
100
+ else:
101
+ frequencies = torch.linspace(
102
+ 1.0,
103
+ 2.0 ** (num_freqs - 1),
104
+ num_freqs,
105
+ dtype=torch.float32
106
+ )
107
+
108
+ if include_pi:
109
+ frequencies *= torch.pi
110
+
111
+ self.register_buffer("frequencies", frequencies, persistent=False)
112
+ self.include_input = include_input
113
+ self.num_freqs = num_freqs
114
+
115
+ self.out_dim = self.get_dims(input_dim)
116
+
117
+ def get_dims(self, input_dim):
118
+ temp = 1 if self.include_input or self.num_freqs == 0 else 0
119
+ out_dim = input_dim * (self.num_freqs * 2 + temp)
120
+
121
+ return out_dim
122
+
123
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
124
+ """ Forward process.
125
+
126
+ Args:
127
+ x: tensor of shape [..., dim]
128
+
129
+ Returns:
130
+ embedding: an embedding of `x` of shape [..., dim * (num_freqs * 2 + temp)]
131
+ where temp is 1 if include_input is True and 0 otherwise.
132
+ """
133
+
134
+ if self.num_freqs > 0:
135
+ embed = (x[..., None].contiguous() * self.frequencies).view(*x.shape[:-1], -1)
136
+ if self.include_input:
137
+ return torch.cat((x, embed.sin(), embed.cos()), dim=-1)
138
+ else:
139
+ return torch.cat((embed.sin(), embed.cos()), dim=-1)
140
+ else:
141
+ return x
142
+
143
+
144
+ class DropPath(nn.Module):
145
+ """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
146
+ """
147
+
148
+ def __init__(self, drop_prob: float = 0., scale_by_keep: bool = True):
149
+ super(DropPath, self).__init__()
150
+ self.drop_prob = drop_prob
151
+ self.scale_by_keep = scale_by_keep
152
+
153
+ def forward(self, x):
154
+ """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).
155
+
156
+ This is the same as the DropConnect impl I created for EfficientNet, etc networks, however,
157
+ the original name is misleading as 'Drop Connect' is a different form of dropout in a separate paper...
158
+ See discussion: https://github.com/tensorflow/tpu/issues/494#issuecomment-532968956 ... I've opted for
159
+ changing the layer and argument names to 'drop path' rather than mix DropConnect as a layer name and use
160
+ 'survival rate' as the argument.
161
+
162
+ """
163
+ if self.drop_prob == 0. or not self.training:
164
+ return x
165
+ keep_prob = 1 - self.drop_prob
166
+ shape = (x.shape[0],) + (1,) * (x.ndim - 1) # work with diff dim tensors, not just 2D ConvNets
167
+ random_tensor = x.new_empty(shape).bernoulli_(keep_prob)
168
+ if keep_prob > 0.0 and self.scale_by_keep:
169
+ random_tensor.div_(keep_prob)
170
+ return x * random_tensor
171
+
172
+ def extra_repr(self):
173
+ return f'drop_prob={round(self.drop_prob, 3):0.3f}'
174
+
175
+
176
+ class MLP(nn.Module):
177
+ def __init__(
178
+ self, *,
179
+ width: int,
180
+ expand_ratio: int = 4,
181
+ output_width: int = None,
182
+ drop_path_rate: float = 0.0
183
+ ):
184
+ super().__init__()
185
+ self.width = width
186
+ self.c_fc = nn.Linear(width, width * expand_ratio)
187
+ self.c_proj = nn.Linear(width * expand_ratio, output_width if output_width is not None else width)
188
+ self.gelu = nn.GELU()
189
+ self.drop_path = DropPath(drop_path_rate) if drop_path_rate > 0. else nn.Identity()
190
+
191
+ def forward(self, x):
192
+ return self.drop_path(self.c_proj(self.gelu(self.c_fc(x))))
193
+
194
+
195
+ class QKVMultiheadCrossAttention(nn.Module):
196
+ def __init__(
197
+ self,
198
+ *,
199
+ heads: int,
200
+ n_data: Optional[int] = None,
201
+ width=None,
202
+ qk_norm=False,
203
+ norm_layer=nn.LayerNorm
204
+ ):
205
+ super().__init__()
206
+ self.heads = heads
207
+ self.n_data = n_data
208
+ self.q_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
209
+ self.k_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
210
+
211
+ self.attn_processor = CrossAttentionProcessor()
212
+
213
+ def forward(self, q, kv):
214
+ _, n_ctx, _ = q.shape
215
+ bs, n_data, width = kv.shape
216
+ attn_ch = width // self.heads // 2
217
+ q = q.view(bs, n_ctx, self.heads, -1)
218
+ kv = kv.view(bs, n_data, self.heads, -1)
219
+ k, v = torch.split(kv, attn_ch, dim=-1)
220
+
221
+ q = self.q_norm(q)
222
+ k = self.k_norm(k)
223
+ q, k, v = map(lambda t: rearrange(t, 'b n h d -> b h n d', h=self.heads), (q, k, v))
224
+ out = self.attn_processor(self, q, k, v)
225
+ out = out.transpose(1, 2).reshape(bs, n_ctx, -1)
226
+ return out
227
+
228
+
229
+ class MultiheadCrossAttention(nn.Module):
230
+ def __init__(
231
+ self,
232
+ *,
233
+ width: int,
234
+ heads: int,
235
+ qkv_bias: bool = True,
236
+ n_data: Optional[int] = None,
237
+ data_width: Optional[int] = None,
238
+ norm_layer=nn.LayerNorm,
239
+ qk_norm: bool = False,
240
+ kv_cache: bool = False,
241
+ ):
242
+ super().__init__()
243
+ self.n_data = n_data
244
+ self.width = width
245
+ self.heads = heads
246
+ self.data_width = width if data_width is None else data_width
247
+ self.c_q = nn.Linear(width, width, bias=qkv_bias)
248
+ self.c_kv = nn.Linear(self.data_width, width * 2, bias=qkv_bias)
249
+ self.c_proj = nn.Linear(width, width)
250
+ self.attention = QKVMultiheadCrossAttention(
251
+ heads=heads,
252
+ n_data=n_data,
253
+ width=width,
254
+ norm_layer=norm_layer,
255
+ qk_norm=qk_norm
256
+ )
257
+ self.kv_cache = kv_cache
258
+ self.data = None
259
+
260
+ def forward(self, x, data):
261
+ x = self.c_q(x)
262
+ if self.kv_cache:
263
+ if self.data is None:
264
+ self.data = self.c_kv(data)
265
+ logger.info('Save kv cache,this should be called only once for one mesh')
266
+ data = self.data
267
+ else:
268
+ data = self.c_kv(data)
269
+ x = self.attention(x, data)
270
+ x = self.c_proj(x)
271
+ return x
272
+
273
+
274
+ class ResidualCrossAttentionBlock(nn.Module):
275
+ def __init__(
276
+ self,
277
+ *,
278
+ n_data: Optional[int] = None,
279
+ width: int,
280
+ heads: int,
281
+ mlp_expand_ratio: int = 4,
282
+ data_width: Optional[int] = None,
283
+ qkv_bias: bool = True,
284
+ norm_layer=nn.LayerNorm,
285
+ qk_norm: bool = False
286
+ ):
287
+ super().__init__()
288
+
289
+ if data_width is None:
290
+ data_width = width
291
+
292
+ self.attn = MultiheadCrossAttention(
293
+ n_data=n_data,
294
+ width=width,
295
+ heads=heads,
296
+ data_width=data_width,
297
+ qkv_bias=qkv_bias,
298
+ norm_layer=norm_layer,
299
+ qk_norm=qk_norm
300
+ )
301
+ self.ln_1 = norm_layer(width, elementwise_affine=True, eps=1e-6)
302
+ self.ln_2 = norm_layer(data_width, elementwise_affine=True, eps=1e-6)
303
+ self.ln_3 = norm_layer(width, elementwise_affine=True, eps=1e-6)
304
+ self.mlp = MLP(width=width, expand_ratio=mlp_expand_ratio)
305
+
306
+ def forward(self, x: torch.Tensor, data: torch.Tensor):
307
+ x = x + self.attn(self.ln_1(x), self.ln_2(data))
308
+ x = x + self.mlp(self.ln_3(x))
309
+ return x
310
+
311
+
312
+ class QKVMultiheadAttention(nn.Module):
313
+ def __init__(
314
+ self,
315
+ *,
316
+ heads: int,
317
+ n_ctx: int,
318
+ width=None,
319
+ qk_norm=False,
320
+ norm_layer=nn.LayerNorm
321
+ ):
322
+ super().__init__()
323
+ self.heads = heads
324
+ self.n_ctx = n_ctx
325
+ self.q_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
326
+ self.k_norm = norm_layer(width // heads, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
327
+
328
+ def forward(self, qkv):
329
+ bs, n_ctx, width = qkv.shape
330
+ attn_ch = width // self.heads // 3
331
+ qkv = qkv.view(bs, n_ctx, self.heads, -1)
332
+ q, k, v = torch.split(qkv, attn_ch, dim=-1)
333
+
334
+ q = self.q_norm(q)
335
+ k = self.k_norm(k)
336
+
337
+ q, k, v = map(lambda t: rearrange(t, 'b n h d -> b h n d', h=self.heads), (q, k, v))
338
+ out = scaled_dot_product_attention(q, k, v).transpose(1, 2).reshape(bs, n_ctx, -1)
339
+ return out
340
+
341
+
342
+ class MultiheadAttention(nn.Module):
343
+ def __init__(
344
+ self,
345
+ *,
346
+ n_ctx: int,
347
+ width: int,
348
+ heads: int,
349
+ qkv_bias: bool,
350
+ norm_layer=nn.LayerNorm,
351
+ qk_norm: bool = False,
352
+ drop_path_rate: float = 0.0
353
+ ):
354
+ super().__init__()
355
+ self.n_ctx = n_ctx
356
+ self.width = width
357
+ self.heads = heads
358
+ self.c_qkv = nn.Linear(width, width * 3, bias=qkv_bias)
359
+ self.c_proj = nn.Linear(width, width)
360
+ self.attention = QKVMultiheadAttention(
361
+ heads=heads,
362
+ n_ctx=n_ctx,
363
+ width=width,
364
+ norm_layer=norm_layer,
365
+ qk_norm=qk_norm
366
+ )
367
+ self.drop_path = DropPath(drop_path_rate) if drop_path_rate > 0. else nn.Identity()
368
+
369
+ def forward(self, x):
370
+ x = self.c_qkv(x)
371
+ x = self.attention(x)
372
+ x = self.drop_path(self.c_proj(x))
373
+ return x
374
+
375
+
376
+ class ResidualAttentionBlock(nn.Module):
377
+ def __init__(
378
+ self,
379
+ *,
380
+ n_ctx: int,
381
+ width: int,
382
+ heads: int,
383
+ qkv_bias: bool = True,
384
+ norm_layer=nn.LayerNorm,
385
+ qk_norm: bool = False,
386
+ drop_path_rate: float = 0.0,
387
+ ):
388
+ super().__init__()
389
+ self.attn = MultiheadAttention(
390
+ n_ctx=n_ctx,
391
+ width=width,
392
+ heads=heads,
393
+ qkv_bias=qkv_bias,
394
+ norm_layer=norm_layer,
395
+ qk_norm=qk_norm,
396
+ drop_path_rate=drop_path_rate
397
+ )
398
+ self.ln_1 = norm_layer(width, elementwise_affine=True, eps=1e-6)
399
+ self.mlp = MLP(width=width, drop_path_rate=drop_path_rate)
400
+ self.ln_2 = norm_layer(width, elementwise_affine=True, eps=1e-6)
401
+
402
+ def forward(self, x: torch.Tensor):
403
+ x = x + self.attn(self.ln_1(x))
404
+ x = x + self.mlp(self.ln_2(x))
405
+ return x
406
+
407
+
408
+ class Transformer(nn.Module):
409
+ def __init__(
410
+ self,
411
+ *,
412
+ n_ctx: int,
413
+ width: int,
414
+ layers: int,
415
+ heads: int,
416
+ qkv_bias: bool = True,
417
+ norm_layer=nn.LayerNorm,
418
+ qk_norm: bool = False,
419
+ drop_path_rate: float = 0.0
420
+ ):
421
+ super().__init__()
422
+ self.n_ctx = n_ctx
423
+ self.width = width
424
+ self.layers = layers
425
+ self.resblocks = nn.ModuleList(
426
+ [
427
+ ResidualAttentionBlock(
428
+ n_ctx=n_ctx,
429
+ width=width,
430
+ heads=heads,
431
+ qkv_bias=qkv_bias,
432
+ norm_layer=norm_layer,
433
+ qk_norm=qk_norm,
434
+ drop_path_rate=drop_path_rate
435
+ )
436
+ for _ in range(layers)
437
+ ]
438
+ )
439
+
440
+ def forward(self, x: torch.Tensor):
441
+ for block in self.resblocks:
442
+ x = block(x)
443
+ return x
444
+
445
+
446
+ class CrossAttentionDecoder(nn.Module):
447
+
448
+ def __init__(
449
+ self,
450
+ *,
451
+ num_latents: int,
452
+ out_channels: int,
453
+ fourier_embedder: FourierEmbedder,
454
+ width: int,
455
+ heads: int,
456
+ mlp_expand_ratio: int = 4,
457
+ downsample_ratio: int = 1,
458
+ enable_ln_post: bool = True,
459
+ qkv_bias: bool = True,
460
+ qk_norm: bool = False,
461
+ label_type: str = "binary"
462
+ ):
463
+ super().__init__()
464
+
465
+ self.enable_ln_post = enable_ln_post
466
+ self.fourier_embedder = fourier_embedder
467
+ self.downsample_ratio = downsample_ratio
468
+ self.query_proj = nn.Linear(self.fourier_embedder.out_dim, width)
469
+ if self.downsample_ratio != 1:
470
+ self.latents_proj = nn.Linear(width * downsample_ratio, width)
471
+ if self.enable_ln_post == False:
472
+ qk_norm = False
473
+ self.cross_attn_decoder = ResidualCrossAttentionBlock(
474
+ n_data=num_latents,
475
+ width=width,
476
+ mlp_expand_ratio=mlp_expand_ratio,
477
+ heads=heads,
478
+ qkv_bias=qkv_bias,
479
+ qk_norm=qk_norm
480
+ )
481
+
482
+ if self.enable_ln_post:
483
+ self.ln_post = nn.LayerNorm(width)
484
+ self.output_proj = nn.Linear(width, out_channels)
485
+ self.label_type = label_type
486
+ self.count = 0
487
+
488
+ def set_cross_attention_processor(self, processor):
489
+ self.cross_attn_decoder.attn.attention.attn_processor = processor
490
+
491
+ def set_default_cross_attention_processor(self):
492
+ self.cross_attn_decoder.attn.attention.attn_processor = CrossAttentionProcessor
493
+
494
+ def forward(self, queries=None, query_embeddings=None, latents=None):
495
+ if query_embeddings is None:
496
+ query_embeddings = self.query_proj(self.fourier_embedder(queries).to(latents.dtype))
497
+ self.count += query_embeddings.shape[1]
498
+ if self.downsample_ratio != 1:
499
+ latents = self.latents_proj(latents)
500
+ x = self.cross_attn_decoder(query_embeddings, latents)
501
+ if self.enable_ln_post:
502
+ x = self.ln_post(x)
503
+ occ = self.output_proj(x)
504
+ return occ
505
+
506
+
507
+ def fps(
508
+ src: torch.Tensor,
509
+ batch: Optional[Tensor] = None,
510
+ ratio: Optional[Union[Tensor, float]] = None,
511
+ random_start: bool = True,
512
+ batch_size: Optional[int] = None,
513
+ ptr: Optional[Union[Tensor, List[int]]] = None,
514
+ ):
515
+ src = src.float()
516
+ from torch_cluster import fps as fps_fn
517
+ output = fps_fn(src, batch, ratio, random_start, batch_size, ptr)
518
+ return output
519
+
520
+
521
+ class PointCrossAttentionEncoder(nn.Module):
522
+
523
+ def __init__(
524
+ self, *,
525
+ num_latents: int,
526
+ downsample_ratio: float,
527
+ pc_size: int,
528
+ pc_sharpedge_size: int,
529
+ fourier_embedder: FourierEmbedder,
530
+ point_feats: int,
531
+ width: int,
532
+ heads: int,
533
+ layers: int,
534
+ normal_pe: bool = False,
535
+ qkv_bias: bool = True,
536
+ use_ln_post: bool = False,
537
+ use_checkpoint: bool = False,
538
+ qk_norm: bool = False
539
+ ):
540
+
541
+ super().__init__()
542
+
543
+ self.use_checkpoint = use_checkpoint
544
+ self.num_latents = num_latents
545
+ self.downsample_ratio = downsample_ratio
546
+ self.point_feats = point_feats
547
+ self.normal_pe = normal_pe
548
+
549
+ if pc_sharpedge_size == 0:
550
+ print(
551
+ f'PointCrossAttentionEncoder INFO: pc_sharpedge_size is not given, using pc_size as pc_sharpedge_size')
552
+ else:
553
+ print(
554
+ f'PointCrossAttentionEncoder INFO: pc_sharpedge_size is given, using pc_size={pc_size}, pc_sharpedge_size={pc_sharpedge_size}')
555
+
556
+ self.pc_size = pc_size
557
+ self.pc_sharpedge_size = pc_sharpedge_size
558
+
559
+ self.fourier_embedder = fourier_embedder
560
+
561
+ self.input_proj = nn.Linear(self.fourier_embedder.out_dim + point_feats, width)
562
+ self.cross_attn = ResidualCrossAttentionBlock(
563
+ width=width,
564
+ heads=heads,
565
+ qkv_bias=qkv_bias,
566
+ qk_norm=qk_norm
567
+ )
568
+
569
+ self.self_attn = None
570
+ if layers > 0:
571
+ self.self_attn = Transformer(
572
+ n_ctx=num_latents,
573
+ width=width,
574
+ layers=layers,
575
+ heads=heads,
576
+ qkv_bias=qkv_bias,
577
+ qk_norm=qk_norm
578
+ )
579
+
580
+ if use_ln_post:
581
+ self.ln_post = nn.LayerNorm(width)
582
+ else:
583
+ self.ln_post = None
584
+
585
+ def sample_points_and_latents(self, pc: torch.FloatTensor, feats: Optional[torch.FloatTensor] = None):
586
+ B, N, D = pc.shape
587
+ num_pts = self.num_latents * self.downsample_ratio
588
+
589
+ # Compute number of latents
590
+ num_latents = int(num_pts / self.downsample_ratio)
591
+
592
+ # Compute the number of random and sharpedge latents
593
+ num_random_query = self.pc_size / (self.pc_size + self.pc_sharpedge_size) * num_latents
594
+ num_sharpedge_query = num_latents - num_random_query
595
+
596
+ # Split random and sharpedge surface points
597
+ random_pc, sharpedge_pc = torch.split(pc, [self.pc_size, self.pc_sharpedge_size], dim=1)
598
+ assert random_pc.shape[1] <= self.pc_size, "Random surface points size must be less than or equal to pc_size"
599
+ assert sharpedge_pc.shape[
600
+ 1] <= self.pc_sharpedge_size, "Sharpedge surface points size must be less than or equal to pc_sharpedge_size"
601
+
602
+ # Randomly select random surface points and random query points
603
+ input_random_pc_size = int(num_random_query * self.downsample_ratio)
604
+ random_query_ratio = num_random_query / input_random_pc_size
605
+ idx_random_pc = torch.randperm(random_pc.shape[1], device=random_pc.device)[:input_random_pc_size]
606
+ input_random_pc = random_pc[:, idx_random_pc, :]
607
+ flatten_input_random_pc = input_random_pc.view(B * input_random_pc_size, D)
608
+ N_down = int(flatten_input_random_pc.shape[0] / B)
609
+ batch_down = torch.arange(B).to(pc.device)
610
+ batch_down = torch.repeat_interleave(batch_down, N_down)
611
+ idx_query_random = fps(flatten_input_random_pc, batch_down, ratio=random_query_ratio)
612
+ query_random_pc = flatten_input_random_pc[idx_query_random].view(B, -1, D)
613
+
614
+ # Randomly select sharpedge surface points and sharpedge query points
615
+ input_sharpedge_pc_size = int(num_sharpedge_query * self.downsample_ratio)
616
+ if input_sharpedge_pc_size == 0:
617
+ input_sharpedge_pc = torch.zeros(B, 0, D, dtype=input_random_pc.dtype).to(pc.device)
618
+ query_sharpedge_pc = torch.zeros(B, 0, D, dtype=query_random_pc.dtype).to(pc.device)
619
+ else:
620
+ sharpedge_query_ratio = num_sharpedge_query / input_sharpedge_pc_size
621
+ idx_sharpedge_pc = torch.randperm(sharpedge_pc.shape[1], device=sharpedge_pc.device)[
622
+ :input_sharpedge_pc_size]
623
+ input_sharpedge_pc = sharpedge_pc[:, idx_sharpedge_pc, :]
624
+ flatten_input_sharpedge_surface_points = input_sharpedge_pc.view(B * input_sharpedge_pc_size, D)
625
+ N_down = int(flatten_input_sharpedge_surface_points.shape[0] / B)
626
+ batch_down = torch.arange(B).to(pc.device)
627
+ batch_down = torch.repeat_interleave(batch_down, N_down)
628
+ idx_query_sharpedge = fps(flatten_input_sharpedge_surface_points, batch_down, ratio=sharpedge_query_ratio)
629
+ query_sharpedge_pc = flatten_input_sharpedge_surface_points[idx_query_sharpedge].view(B, -1, D)
630
+
631
+ # Concatenate random and sharpedge surface points and query points
632
+ query_pc = torch.cat([query_random_pc, query_sharpedge_pc], dim=1)
633
+ input_pc = torch.cat([input_random_pc, input_sharpedge_pc], dim=1)
634
+
635
+ # PE
636
+ query = self.fourier_embedder(query_pc)
637
+ data = self.fourier_embedder(input_pc)
638
+
639
+ # Concat normal if given
640
+ if self.point_feats != 0:
641
+
642
+ random_surface_feats, sharpedge_surface_feats = torch.split(feats, [self.pc_size, self.pc_sharpedge_size],
643
+ dim=1)
644
+ input_random_surface_feats = random_surface_feats[:, idx_random_pc, :]
645
+ flatten_input_random_surface_feats = input_random_surface_feats.view(B * input_random_pc_size, -1)
646
+ query_random_feats = flatten_input_random_surface_feats[idx_query_random].view(B, -1,
647
+ flatten_input_random_surface_feats.shape[
648
+ -1])
649
+
650
+ if input_sharpedge_pc_size == 0:
651
+ input_sharpedge_surface_feats = torch.zeros(B, 0, self.point_feats,
652
+ dtype=input_random_surface_feats.dtype).to(pc.device)
653
+ query_sharpedge_feats = torch.zeros(B, 0, self.point_feats, dtype=query_random_feats.dtype).to(
654
+ pc.device)
655
+ else:
656
+ input_sharpedge_surface_feats = sharpedge_surface_feats[:, idx_sharpedge_pc, :]
657
+ flatten_input_sharpedge_surface_feats = input_sharpedge_surface_feats.view(B * input_sharpedge_pc_size,
658
+ -1)
659
+ query_sharpedge_feats = flatten_input_sharpedge_surface_feats[idx_query_sharpedge].view(B, -1,
660
+ flatten_input_sharpedge_surface_feats.shape[
661
+ -1])
662
+
663
+ query_feats = torch.cat([query_random_feats, query_sharpedge_feats], dim=1)
664
+ input_feats = torch.cat([input_random_surface_feats, input_sharpedge_surface_feats], dim=1)
665
+
666
+ if self.normal_pe:
667
+ query_normal_pe = self.fourier_embedder(query_feats[..., :3])
668
+ input_normal_pe = self.fourier_embedder(input_feats[..., :3])
669
+ query_feats = torch.cat([query_normal_pe, query_feats[..., 3:]], dim=-1)
670
+ input_feats = torch.cat([input_normal_pe, input_feats[..., 3:]], dim=-1)
671
+
672
+ query = torch.cat([query, query_feats], dim=-1)
673
+ data = torch.cat([data, input_feats], dim=-1)
674
+
675
+ if input_sharpedge_pc_size == 0:
676
+ query_sharpedge_pc = torch.zeros(B, 1, D).to(pc.device)
677
+ input_sharpedge_pc = torch.zeros(B, 1, D).to(pc.device)
678
+
679
+ # print(f'query_pc: {query_pc.shape}')
680
+ # print(f'input_pc: {input_pc.shape}')
681
+ # print(f'query_random_pc: {query_random_pc.shape}')
682
+ # print(f'input_random_pc: {input_random_pc.shape}')
683
+ # print(f'query_sharpedge_pc: {query_sharpedge_pc.shape}')
684
+ # print(f'input_sharpedge_pc: {input_sharpedge_pc.shape}')
685
+
686
+ return query.view(B, -1, query.shape[-1]), data.view(B, -1, data.shape[-1]), [query_pc, input_pc,
687
+ query_random_pc, input_random_pc,
688
+ query_sharpedge_pc,
689
+ input_sharpedge_pc]
690
+
691
+ def forward(self, pc, feats):
692
+ """
693
+
694
+ Args:
695
+ pc (torch.FloatTensor): [B, N, 3]
696
+ feats (torch.FloatTensor or None): [B, N, C]
697
+
698
+ Returns:
699
+
700
+ """
701
+
702
+ query, data, pc_infos = self.sample_points_and_latents(pc, feats)
703
+
704
+ query = self.input_proj(query)
705
+ query = query
706
+ data = self.input_proj(data)
707
+ data = data
708
+
709
+ latents = self.cross_attn(query, data)
710
+ if self.self_attn is not None:
711
+ latents = self.self_attn(latents)
712
+
713
+ if self.ln_post is not None:
714
+ latents = self.ln_post(latents)
715
+
716
+ return latents, pc_infos
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/attention_processors.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
+ # except for the third-party components listed below.
3
+ # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
+ # in the repsective licenses of these third-party components.
5
+ # Users must comply with all terms and conditions of original licenses of these third-party
6
+ # components and must ensure that the usage of the third party components adheres to
7
+ # all relevant laws and regulations.
8
+
9
+ # For avoidance of doubts, Hunyuan 3D means the large language models and
10
+ # their software and algorithms, including trained model weights, parameters (including
11
+ # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
+ # fine-tuning enabling code and other elements of the foregoing made publicly available
13
+ # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
+
15
+ import os
16
+
17
+ import torch
18
+ import torch.nn.functional as F
19
+
20
+ scaled_dot_product_attention = F.scaled_dot_product_attention
21
+ if os.environ.get('CA_USE_SAGEATTN', '0') == '1':
22
+ try:
23
+ from sageattention import sageattn
24
+ except ImportError:
25
+ raise ImportError('Please install the package "sageattention" to use this USE_SAGEATTN.')
26
+ scaled_dot_product_attention = sageattn
27
+
28
+
29
+ class CrossAttentionProcessor:
30
+ def __call__(self, attn, q, k, v):
31
+ out = scaled_dot_product_attention(q, k, v)
32
+ return out
33
+
34
+
35
+ class FlashVDMCrossAttentionProcessor:
36
+ def __init__(self, topk=None):
37
+ self.topk = topk
38
+
39
+ def __call__(self, attn, q, k, v):
40
+ if k.shape[-2] == 3072:
41
+ topk = 1024
42
+ elif k.shape[-2] == 512:
43
+ topk = 256
44
+ else:
45
+ topk = k.shape[-2] // 3
46
+
47
+ if self.topk is True:
48
+ q1 = q[:, :, ::100, :]
49
+ sim = q1 @ k.transpose(-1, -2)
50
+ sim = torch.mean(sim, -2)
51
+ topk_ind = torch.topk(sim, dim=-1, k=topk).indices.squeeze(-2).unsqueeze(-1)
52
+ topk_ind = topk_ind.expand(-1, -1, -1, v.shape[-1])
53
+ v0 = torch.gather(v, dim=-2, index=topk_ind)
54
+ k0 = torch.gather(k, dim=-2, index=topk_ind)
55
+ out = scaled_dot_product_attention(q, k0, v0)
56
+ elif self.topk is False:
57
+ out = scaled_dot_product_attention(q, k, v)
58
+ else:
59
+ idx, counts = self.topk
60
+ start = 0
61
+ outs = []
62
+ for grid_coord, count in zip(idx, counts):
63
+ end = start + count
64
+ q_chunk = q[:, :, start:end, :]
65
+ k0, v0 = self.select_topkv(q_chunk, k, v, topk)
66
+ out = scaled_dot_product_attention(q_chunk, k0, v0)
67
+ outs.append(out)
68
+ start += count
69
+ out = torch.cat(outs, dim=-2)
70
+ self.topk = False
71
+ return out
72
+
73
+ def select_topkv(self, q_chunk, k, v, topk):
74
+ q1 = q_chunk[:, :, ::50, :]
75
+ sim = q1 @ k.transpose(-1, -2)
76
+ sim = torch.mean(sim, -2)
77
+ topk_ind = torch.topk(sim, dim=-1, k=topk).indices.squeeze(-2).unsqueeze(-1)
78
+ topk_ind = topk_ind.expand(-1, -1, -1, v.shape[-1])
79
+ v0 = torch.gather(v, dim=-2, index=topk_ind)
80
+ k0 = torch.gather(k, dim=-2, index=topk_ind)
81
+ return k0, v0
82
+
83
+
84
+ class FlashVDMTopMCrossAttentionProcessor(FlashVDMCrossAttentionProcessor):
85
+ def select_topkv(self, q_chunk, k, v, topk):
86
+ q1 = q_chunk[:, :, ::30, :]
87
+ sim = q1 @ k.transpose(-1, -2)
88
+ # sim = sim.to(torch.float32)
89
+ sim = sim.softmax(-1)
90
+ sim = torch.mean(sim, 1)
91
+ activated_token = torch.where(sim > 1e-6)[2]
92
+ index = torch.unique(activated_token, return_counts=True)[0].unsqueeze(0).unsqueeze(0).unsqueeze(-1)
93
+ index = index.expand(-1, v.shape[1], -1, v.shape[-1])
94
+ v0 = torch.gather(v, dim=-2, index=index)
95
+ k0 = torch.gather(k, dim=-2, index=index)
96
+ return k0, v0
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/model.py ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Open Source Model Licensed under the Apache License Version 2.0
2
+ # and Other Licenses of the Third-Party Components therein:
3
+ # The below Model in this distribution may have been modified by THL A29 Limited
4
+ # ("Tencent Modifications"). All Tencent Modifications are Copyright (C) 2024 THL A29 Limited.
5
+
6
+ # Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved.
7
+ # The below software and/or models in this distribution may have been
8
+ # modified by THL A29 Limited ("Tencent Modifications").
9
+ # All Tencent Modifications are Copyright (C) THL A29 Limited.
10
+
11
+ # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
12
+ # except for the third-party components listed below.
13
+ # Hunyuan 3D does not impose any additional limitations beyond what is outlined
14
+ # in the repsective licenses of these third-party components.
15
+ # Users must comply with all terms and conditions of original licenses of these third-party
16
+ # components and must ensure that the usage of the third party components adheres to
17
+ # all relevant laws and regulations.
18
+
19
+ # For avoidance of doubts, Hunyuan 3D means the large language models and
20
+ # their software and algorithms, including trained model weights, parameters (including
21
+ # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
22
+ # fine-tuning enabling code and other elements of the foregoing made publicly available
23
+ # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
24
+
25
+
26
+ import os
27
+ from typing import Union, List
28
+
29
+ import numpy as np
30
+ import torch
31
+ import torch.nn as nn
32
+ import yaml
33
+
34
+ from .attention_blocks import FourierEmbedder, Transformer, CrossAttentionDecoder, PointCrossAttentionEncoder
35
+ from .surface_extractors import MCSurfaceExtractor, SurfaceExtractors
36
+ from .volume_decoders import VanillaVolumeDecoder, FlashVDMVolumeDecoding, HierarchicalVolumeDecoding
37
+ from ...utils import logger, synchronize_timer, smart_load_model
38
+
39
+
40
+ class DiagonalGaussianDistribution(object):
41
+ def __init__(self, parameters: Union[torch.Tensor, List[torch.Tensor]], deterministic=False, feat_dim=1):
42
+ """
43
+ Initialize a diagonal Gaussian distribution with mean and log-variance parameters.
44
+
45
+ Args:
46
+ parameters (Union[torch.Tensor, List[torch.Tensor]]):
47
+ Either a single tensor containing concatenated mean and log-variance along `feat_dim`,
48
+ or a list of two tensors [mean, logvar].
49
+ deterministic (bool, optional): If True, the distribution is deterministic (zero variance).
50
+ Default is False. feat_dim (int, optional): Dimension along which mean and logvar are
51
+ concatenated if parameters is a single tensor. Default is 1.
52
+ """
53
+ self.feat_dim = feat_dim
54
+ self.parameters = parameters
55
+
56
+ if isinstance(parameters, list):
57
+ self.mean = parameters[0]
58
+ self.logvar = parameters[1]
59
+ else:
60
+ self.mean, self.logvar = torch.chunk(parameters, 2, dim=feat_dim)
61
+
62
+ self.logvar = torch.clamp(self.logvar, -30.0, 20.0)
63
+ self.deterministic = deterministic
64
+ self.std = torch.exp(0.5 * self.logvar)
65
+ self.var = torch.exp(self.logvar)
66
+ if self.deterministic:
67
+ self.var = self.std = torch.zeros_like(self.mean)
68
+
69
+ def sample(self):
70
+ """
71
+ Sample from the diagonal Gaussian distribution.
72
+
73
+ Returns:
74
+ torch.Tensor: A sample tensor with the same shape as the mean.
75
+ """
76
+ x = self.mean + self.std * torch.randn_like(self.mean)
77
+ return x
78
+
79
+ def kl(self, other=None, dims=(1, 2, 3)):
80
+ """
81
+ Compute the Kullback-Leibler (KL) divergence between this distribution and another.
82
+
83
+ If `other` is None, compute KL divergence to a standard normal distribution N(0, I).
84
+
85
+ Args:
86
+ other (DiagonalGaussianDistribution, optional): Another diagonal Gaussian distribution.
87
+ dims (tuple, optional): Dimensions along which to compute the mean KL divergence.
88
+ Default is (1, 2, 3).
89
+
90
+ Returns:
91
+ torch.Tensor: The mean KL divergence value.
92
+ """
93
+ if self.deterministic:
94
+ return torch.Tensor([0.])
95
+ else:
96
+ if other is None:
97
+ return 0.5 * torch.mean(torch.pow(self.mean, 2)
98
+ + self.var - 1.0 - self.logvar,
99
+ dim=dims)
100
+ else:
101
+ return 0.5 * torch.mean(
102
+ torch.pow(self.mean - other.mean, 2) / other.var
103
+ + self.var / other.var - 1.0 - self.logvar + other.logvar,
104
+ dim=dims)
105
+
106
+ def nll(self, sample, dims=(1, 2, 3)):
107
+ if self.deterministic:
108
+ return torch.Tensor([0.])
109
+ logtwopi = np.log(2.0 * np.pi)
110
+ return 0.5 * torch.sum(
111
+ logtwopi + self.logvar + torch.pow(sample - self.mean, 2) / self.var,
112
+ dim=dims)
113
+
114
+ def mode(self):
115
+ return self.mean
116
+
117
+
118
+ class VectsetVAE(nn.Module):
119
+
120
+ @classmethod
121
+ @synchronize_timer('VectsetVAE Model Loading')
122
+ def from_single_file(
123
+ cls,
124
+ ckpt_path,
125
+ config_path,
126
+ device='cuda',
127
+ dtype=torch.float16,
128
+ use_safetensors=None,
129
+ **kwargs,
130
+ ):
131
+ # load config
132
+ with open(config_path, 'r') as f:
133
+ config = yaml.safe_load(f)
134
+
135
+ # load ckpt
136
+ if use_safetensors:
137
+ ckpt_path = ckpt_path.replace('.ckpt', '.safetensors')
138
+ if not os.path.exists(ckpt_path):
139
+ raise FileNotFoundError(f"Model file {ckpt_path} not found")
140
+
141
+ logger.info(f"Loading model from {ckpt_path}")
142
+ if use_safetensors:
143
+ import safetensors.torch
144
+ ckpt = safetensors.torch.load_file(ckpt_path, device='cpu')
145
+ else:
146
+ ckpt = torch.load(ckpt_path, map_location='cpu', weights_only=True)
147
+
148
+ model_kwargs = config['params']
149
+ model_kwargs.update(kwargs)
150
+
151
+ model = cls(**model_kwargs)
152
+ model.load_state_dict(ckpt)
153
+ model.to(device=device, dtype=dtype)
154
+ return model
155
+
156
+ @classmethod
157
+ def from_pretrained(
158
+ cls,
159
+ model_path,
160
+ device='cuda',
161
+ dtype=torch.float16,
162
+ use_safetensors=False,
163
+ variant='fp16',
164
+ subfolder='hunyuan3d-vae-v2-1',
165
+ **kwargs,
166
+ ):
167
+ config_path, ckpt_path = smart_load_model(
168
+ model_path,
169
+ subfolder=subfolder,
170
+ use_safetensors=use_safetensors,
171
+ variant=variant
172
+ )
173
+
174
+ return cls.from_single_file(
175
+ ckpt_path,
176
+ config_path,
177
+ device=device,
178
+ dtype=dtype,
179
+ use_safetensors=use_safetensors,
180
+ **kwargs
181
+ )
182
+
183
+ def init_from_ckpt(self, path, ignore_keys=()):
184
+ state_dict = torch.load(path, map_location="cpu")
185
+ state_dict = state_dict.get("state_dict", state_dict)
186
+ keys = list(state_dict.keys())
187
+ for k in keys:
188
+ for ik in ignore_keys:
189
+ if k.startswith(ik):
190
+ print("Deleting key {} from state_dict.".format(k))
191
+ del state_dict[k]
192
+ missing, unexpected = self.load_state_dict(state_dict, strict=False)
193
+ print(f"Restored from {path} with {len(missing)} missing and {len(unexpected)} unexpected keys")
194
+ if len(missing) > 0:
195
+ print(f"Missing Keys: {missing}")
196
+ print(f"Unexpected Keys: {unexpected}")
197
+
198
+ def __init__(
199
+ self,
200
+ volume_decoder=None,
201
+ surface_extractor=None
202
+ ):
203
+ super().__init__()
204
+ if volume_decoder is None:
205
+ volume_decoder = VanillaVolumeDecoder()
206
+ if surface_extractor is None:
207
+ surface_extractor = MCSurfaceExtractor()
208
+ self.volume_decoder = volume_decoder
209
+ self.surface_extractor = surface_extractor
210
+
211
+ def latents2mesh(self, latents: torch.FloatTensor, **kwargs):
212
+ with synchronize_timer('Volume decoding'):
213
+ grid_logits = self.volume_decoder(latents, self.geo_decoder, **kwargs)
214
+ with synchronize_timer('Surface extraction'):
215
+ outputs = self.surface_extractor(grid_logits, **kwargs)
216
+ return outputs
217
+
218
+ def enable_flashvdm_decoder(
219
+ self,
220
+ enabled: bool = True,
221
+ adaptive_kv_selection=True,
222
+ topk_mode='mean',
223
+ mc_algo='dmc',
224
+ ):
225
+ if enabled:
226
+ if adaptive_kv_selection:
227
+ self.volume_decoder = FlashVDMVolumeDecoding(topk_mode)
228
+ else:
229
+ self.volume_decoder = HierarchicalVolumeDecoding()
230
+ if mc_algo not in SurfaceExtractors.keys():
231
+ raise ValueError(f'Unsupported mc_algo {mc_algo}, available:{list(SurfaceExtractors.keys())}')
232
+ self.surface_extractor = SurfaceExtractors[mc_algo]()
233
+ else:
234
+ self.volume_decoder = VanillaVolumeDecoder()
235
+ self.surface_extractor = MCSurfaceExtractor()
236
+
237
+
238
+ class ShapeVAE(VectsetVAE):
239
+ def __init__(
240
+ self,
241
+ *,
242
+ num_latents: int,
243
+ embed_dim: int,
244
+ width: int,
245
+ heads: int,
246
+ num_decoder_layers: int,
247
+ num_encoder_layers: int = 8,
248
+ pc_size: int = 5120,
249
+ pc_sharpedge_size: int = 5120,
250
+ point_feats: int = 3,
251
+ downsample_ratio: int = 20,
252
+ geo_decoder_downsample_ratio: int = 1,
253
+ geo_decoder_mlp_expand_ratio: int = 4,
254
+ geo_decoder_ln_post: bool = True,
255
+ num_freqs: int = 8,
256
+ include_pi: bool = True,
257
+ qkv_bias: bool = True,
258
+ qk_norm: bool = False,
259
+ label_type: str = "binary",
260
+ drop_path_rate: float = 0.0,
261
+ scale_factor: float = 1.0,
262
+ use_ln_post: bool = True,
263
+ ckpt_path = None
264
+ ):
265
+ super().__init__()
266
+ self.geo_decoder_ln_post = geo_decoder_ln_post
267
+ self.downsample_ratio = downsample_ratio
268
+
269
+ self.fourier_embedder = FourierEmbedder(num_freqs=num_freqs, include_pi=include_pi)
270
+
271
+ self.encoder = PointCrossAttentionEncoder(
272
+ fourier_embedder=self.fourier_embedder,
273
+ num_latents=num_latents,
274
+ downsample_ratio=self.downsample_ratio,
275
+ pc_size=pc_size,
276
+ pc_sharpedge_size=pc_sharpedge_size,
277
+ point_feats=point_feats,
278
+ width=width,
279
+ heads=heads,
280
+ layers=num_encoder_layers,
281
+ qkv_bias=qkv_bias,
282
+ use_ln_post=use_ln_post,
283
+ qk_norm=qk_norm
284
+ )
285
+
286
+ self.pre_kl = nn.Linear(width, embed_dim * 2)
287
+ self.post_kl = nn.Linear(embed_dim, width)
288
+
289
+ self.transformer = Transformer(
290
+ n_ctx=num_latents,
291
+ width=width,
292
+ layers=num_decoder_layers,
293
+ heads=heads,
294
+ qkv_bias=qkv_bias,
295
+ qk_norm=qk_norm,
296
+ drop_path_rate=drop_path_rate
297
+ )
298
+
299
+ self.geo_decoder = CrossAttentionDecoder(
300
+ fourier_embedder=self.fourier_embedder,
301
+ out_channels=1,
302
+ num_latents=num_latents,
303
+ mlp_expand_ratio=geo_decoder_mlp_expand_ratio,
304
+ downsample_ratio=geo_decoder_downsample_ratio,
305
+ enable_ln_post=self.geo_decoder_ln_post,
306
+ width=width // geo_decoder_downsample_ratio,
307
+ heads=heads // geo_decoder_downsample_ratio,
308
+ qkv_bias=qkv_bias,
309
+ qk_norm=qk_norm,
310
+ label_type=label_type,
311
+ )
312
+
313
+ self.scale_factor = scale_factor
314
+ self.latent_shape = (num_latents, embed_dim)
315
+
316
+ if ckpt_path is not None:
317
+ self.init_from_ckpt(ckpt_path)
318
+
319
+ def forward(self, latents):
320
+ latents = self.post_kl(latents)
321
+ latents = self.transformer(latents)
322
+ return latents
323
+
324
+ def encode(self, surface, sample_posterior=True):
325
+ pc, feats = surface[:, :, :3], surface[:, :, 3:]
326
+ latents, _ = self.encoder(pc, feats)
327
+ # print(latents.shape, self.pre_kl.weight.shape)
328
+ moments = self.pre_kl(latents)
329
+ posterior = DiagonalGaussianDistribution(moments, feat_dim=-1)
330
+ if sample_posterior:
331
+ latents = posterior.sample()
332
+ else:
333
+ latents = posterior.mode()
334
+ return latents
335
+
336
+ def decode(self, latents):
337
+ latents = self.post_kl(latents)
338
+ latents = self.transformer(latents)
339
+ return latents
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/surface_extractors.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
+ # except for the third-party components listed below.
3
+ # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
+ # in the repsective licenses of these third-party components.
5
+ # Users must comply with all terms and conditions of original licenses of these third-party
6
+ # components and must ensure that the usage of the third party components adheres to
7
+ # all relevant laws and regulations.
8
+
9
+ # For avoidance of doubts, Hunyuan 3D means the large language models and
10
+ # their software and algorithms, including trained model weights, parameters (including
11
+ # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
+ # fine-tuning enabling code and other elements of the foregoing made publicly available
13
+ # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
+
15
+ from typing import Union, Tuple, List
16
+
17
+ import numpy as np
18
+ import torch
19
+ from skimage import measure
20
+
21
+
22
+ class Latent2MeshOutput:
23
+ def __init__(self, mesh_v=None, mesh_f=None):
24
+ self.mesh_v = mesh_v
25
+ self.mesh_f = mesh_f
26
+
27
+
28
+ def center_vertices(vertices):
29
+ """Translate the vertices so that bounding box is centered at zero."""
30
+ vert_min = vertices.min(dim=0)[0]
31
+ vert_max = vertices.max(dim=0)[0]
32
+ vert_center = 0.5 * (vert_min + vert_max)
33
+ return vertices - vert_center
34
+
35
+
36
+ class SurfaceExtractor:
37
+ def _compute_box_stat(self, bounds: Union[Tuple[float], List[float], float], octree_resolution: int):
38
+ """
39
+ Compute grid size, bounding box minimum coordinates, and bounding box size based on input
40
+ bounds and resolution.
41
+
42
+ Args:
43
+ bounds (Union[Tuple[float], List[float], float]): Bounding box coordinates or a single
44
+ float representing half side length.
45
+ If float, bounds are assumed symmetric around zero in all axes.
46
+ Expected format if list/tuple: [xmin, ymin, zmin, xmax, ymax, zmax].
47
+ octree_resolution (int): Resolution of the octree grid.
48
+
49
+ Returns:
50
+ grid_size (List[int]): Grid size along each axis (x, y, z), each equal to octree_resolution + 1.
51
+ bbox_min (np.ndarray): Minimum coordinates of the bounding box (xmin, ymin, zmin).
52
+ bbox_size (np.ndarray): Size of the bounding box along each axis (xmax - xmin, etc.).
53
+ """
54
+ if isinstance(bounds, float):
55
+ bounds = [-bounds, -bounds, -bounds, bounds, bounds, bounds]
56
+
57
+ bbox_min, bbox_max = np.array(bounds[0:3]), np.array(bounds[3:6])
58
+ bbox_size = bbox_max - bbox_min
59
+ grid_size = [int(octree_resolution) + 1, int(octree_resolution) + 1, int(octree_resolution) + 1]
60
+ return grid_size, bbox_min, bbox_size
61
+
62
+ def run(self, *args, **kwargs):
63
+ """
64
+ Abstract method to extract surface mesh from grid logits.
65
+
66
+ This method should be implemented by subclasses.
67
+
68
+ Raises:
69
+ NotImplementedError: Always, since this is an abstract method.
70
+ """
71
+ return NotImplementedError
72
+
73
+ def __call__(self, grid_logits, **kwargs):
74
+ """
75
+ Process a batch of grid logits to extract surface meshes.
76
+
77
+ Args:
78
+ grid_logits (torch.Tensor): Batch of grid logits with shape (batch_size, ...).
79
+ **kwargs: Additional keyword arguments passed to the `run` method.
80
+
81
+ Returns:
82
+ List[Optional[Latent2MeshOutput]]: List of mesh outputs for each grid in the batch.
83
+ If extraction fails for a grid, None is appended at that position.
84
+ """
85
+ outputs = []
86
+ for i in range(grid_logits.shape[0]):
87
+ try:
88
+ vertices, faces = self.run(grid_logits[i], **kwargs)
89
+ vertices = vertices.astype(np.float32)
90
+ faces = np.ascontiguousarray(faces)
91
+ outputs.append(Latent2MeshOutput(mesh_v=vertices, mesh_f=faces))
92
+
93
+ except Exception:
94
+ import traceback
95
+ traceback.print_exc()
96
+ outputs.append(None)
97
+
98
+ return outputs
99
+
100
+
101
+ class MCSurfaceExtractor(SurfaceExtractor):
102
+ def run(self, grid_logit, *, mc_level, bounds, octree_resolution, **kwargs):
103
+ """
104
+ Extract surface mesh using the Marching Cubes algorithm.
105
+
106
+ Args:
107
+ grid_logit (torch.Tensor): 3D grid logits tensor representing the scalar field.
108
+ mc_level (float): The level (iso-value) at which to extract the surface.
109
+ bounds (Union[Tuple[float], List[float], float]): Bounding box coordinates or half side length.
110
+ octree_resolution (int): Resolution of the octree grid.
111
+ **kwargs: Additional keyword arguments (ignored).
112
+
113
+ Returns:
114
+ Tuple[np.ndarray, np.ndarray]: Tuple containing:
115
+ - vertices (np.ndarray): Extracted mesh vertices, scaled and translated to bounding
116
+ box coordinates.
117
+ - faces (np.ndarray): Extracted mesh faces (triangles).
118
+ """
119
+ vertices, faces, normals, _ = measure.marching_cubes(grid_logit.cpu().numpy(),
120
+ mc_level,
121
+ method="lewiner")
122
+ grid_size, bbox_min, bbox_size = self._compute_box_stat(bounds, octree_resolution)
123
+ vertices = vertices / grid_size * bbox_size + bbox_min
124
+ return vertices, faces
125
+
126
+
127
+ class DMCSurfaceExtractor(SurfaceExtractor):
128
+ def run(self, grid_logit, *, octree_resolution, **kwargs):
129
+ """
130
+ Extract surface mesh using Differentiable Marching Cubes (DMC) algorithm.
131
+
132
+ Args:
133
+ grid_logit (torch.Tensor): 3D grid logits tensor representing the scalar field.
134
+ octree_resolution (int): Resolution of the octree grid.
135
+ **kwargs: Additional keyword arguments (ignored).
136
+
137
+ Returns:
138
+ Tuple[np.ndarray, np.ndarray]: Tuple containing:
139
+ - vertices (np.ndarray): Extracted mesh vertices, centered and converted to numpy.
140
+ - faces (np.ndarray): Extracted mesh faces (triangles), with reversed vertex order.
141
+
142
+ Raises:
143
+ ImportError: If the 'diso' package is not installed.
144
+ """
145
+ device = grid_logit.device
146
+ if not hasattr(self, 'dmc'):
147
+ try:
148
+ from diso import DiffDMC
149
+ self.dmc = DiffDMC(dtype=torch.float32).to(device)
150
+ except:
151
+ raise ImportError("Please install diso via `pip install diso`, or set mc_algo to 'mc'")
152
+ sdf = -grid_logit / octree_resolution
153
+ sdf = sdf.to(torch.float32).contiguous()
154
+ verts, faces = self.dmc(sdf, deform=None, return_quads=False, normalize=True)
155
+ verts = center_vertices(verts)
156
+ vertices = verts.detach().cpu().numpy()
157
+ faces = faces.detach().cpu().numpy()[:, ::-1]
158
+ return vertices, faces
159
+
160
+
161
+ SurfaceExtractors = {
162
+ 'mc': MCSurfaceExtractor,
163
+ 'dmc': DMCSurfaceExtractor,
164
+ }
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/autoencoders/volume_decoders.py ADDED
@@ -0,0 +1,435 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
+ # except for the third-party components listed below.
3
+ # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
+ # in the repsective licenses of these third-party components.
5
+ # Users must comply with all terms and conditions of original licenses of these third-party
6
+ # components and must ensure that the usage of the third party components adheres to
7
+ # all relevant laws and regulations.
8
+
9
+ # For avoidance of doubts, Hunyuan 3D means the large language models and
10
+ # their software and algorithms, including trained model weights, parameters (including
11
+ # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
+ # fine-tuning enabling code and other elements of the foregoing made publicly available
13
+ # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
+
15
+ from typing import Union, Tuple, List, Callable
16
+
17
+ import numpy as np
18
+ import torch
19
+ import torch.nn as nn
20
+ import torch.nn.functional as F
21
+ from einops import repeat
22
+ from tqdm import tqdm
23
+
24
+ from .attention_blocks import CrossAttentionDecoder
25
+ from .attention_processors import FlashVDMCrossAttentionProcessor, FlashVDMTopMCrossAttentionProcessor
26
+ from ...utils import logger
27
+
28
+
29
+ def extract_near_surface_volume_fn(input_tensor: torch.Tensor, alpha: float):
30
+ device = input_tensor.device
31
+ D = input_tensor.shape[0]
32
+ signed_val = 0.0
33
+
34
+ # 添加偏移并处理无效值
35
+ val = input_tensor + alpha
36
+ valid_mask = val > -9000 # 假设-9000是无效值
37
+
38
+ # 改进的邻居获取函数(保持维度一致)
39
+ def get_neighbor(t, shift, axis):
40
+ """根据指定轴进行位移并保持维度一致"""
41
+ if shift == 0:
42
+ return t.clone()
43
+
44
+ # 确定填充轴(输入为[D, D, D]对应z,y,x轴)
45
+ pad_dims = [0, 0, 0, 0, 0, 0] # 格式:[x前,x后,y前,y后,z前,z后]
46
+
47
+ # 根据轴类型设置填充
48
+ if axis == 0: # x轴(最后一个维度)
49
+ pad_idx = 0 if shift > 0 else 1
50
+ pad_dims[pad_idx] = abs(shift)
51
+ elif axis == 1: # y轴(中间维度)
52
+ pad_idx = 2 if shift > 0 else 3
53
+ pad_dims[pad_idx] = abs(shift)
54
+ elif axis == 2: # z轴(第一个维度)
55
+ pad_idx = 4 if shift > 0 else 5
56
+ pad_dims[pad_idx] = abs(shift)
57
+
58
+ # 执行填充(添加batch和channel维度适配F.pad)
59
+ padded = F.pad(t.unsqueeze(0).unsqueeze(0), pad_dims[::-1], mode='replicate') # 反转顺序适配F.pad
60
+
61
+ # 构建动态切片索引
62
+ slice_dims = [slice(None)] * 3 # 初始化为全切片
63
+ if axis == 0: # x轴(dim=2)
64
+ if shift > 0:
65
+ slice_dims[0] = slice(shift, None)
66
+ else:
67
+ slice_dims[0] = slice(None, shift)
68
+ elif axis == 1: # y轴(dim=1)
69
+ if shift > 0:
70
+ slice_dims[1] = slice(shift, None)
71
+ else:
72
+ slice_dims[1] = slice(None, shift)
73
+ elif axis == 2: # z轴(dim=0)
74
+ if shift > 0:
75
+ slice_dims[2] = slice(shift, None)
76
+ else:
77
+ slice_dims[2] = slice(None, shift)
78
+
79
+ # 应用切片并恢复维度
80
+ padded = padded.squeeze(0).squeeze(0)
81
+ sliced = padded[slice_dims]
82
+ return sliced
83
+
84
+ # 获取各方向邻居(确保维度一致)
85
+ left = get_neighbor(val, 1, axis=0) # x方向
86
+ right = get_neighbor(val, -1, axis=0)
87
+ back = get_neighbor(val, 1, axis=1) # y方向
88
+ front = get_neighbor(val, -1, axis=1)
89
+ down = get_neighbor(val, 1, axis=2) # z方向
90
+ up = get_neighbor(val, -1, axis=2)
91
+
92
+ # 处理边界无效值(使用where保持维度一致)
93
+ def safe_where(neighbor):
94
+ return torch.where(neighbor > -9000, neighbor, val)
95
+
96
+ left = safe_where(left)
97
+ right = safe_where(right)
98
+ back = safe_where(back)
99
+ front = safe_where(front)
100
+ down = safe_where(down)
101
+ up = safe_where(up)
102
+
103
+ # 计算符号一致性(转换为float32确保精度)
104
+ sign = torch.sign(val.to(torch.float32))
105
+ neighbors_sign = torch.stack([
106
+ torch.sign(left.to(torch.float32)),
107
+ torch.sign(right.to(torch.float32)),
108
+ torch.sign(back.to(torch.float32)),
109
+ torch.sign(front.to(torch.float32)),
110
+ torch.sign(down.to(torch.float32)),
111
+ torch.sign(up.to(torch.float32))
112
+ ], dim=0)
113
+
114
+ # 检查所有符号是否一致
115
+ same_sign = torch.all(neighbors_sign == sign, dim=0)
116
+
117
+ # 生成最终掩码
118
+ mask = (~same_sign).to(torch.int32)
119
+ return mask * valid_mask.to(torch.int32)
120
+
121
+
122
+ def generate_dense_grid_points(
123
+ bbox_min: np.ndarray,
124
+ bbox_max: np.ndarray,
125
+ octree_resolution: int,
126
+ indexing: str = "ij",
127
+ ):
128
+ length = bbox_max - bbox_min
129
+ num_cells = octree_resolution
130
+
131
+ x = np.linspace(bbox_min[0], bbox_max[0], int(num_cells) + 1, dtype=np.float32)
132
+ y = np.linspace(bbox_min[1], bbox_max[1], int(num_cells) + 1, dtype=np.float32)
133
+ z = np.linspace(bbox_min[2], bbox_max[2], int(num_cells) + 1, dtype=np.float32)
134
+ [xs, ys, zs] = np.meshgrid(x, y, z, indexing=indexing)
135
+ xyz = np.stack((xs, ys, zs), axis=-1)
136
+ grid_size = [int(num_cells) + 1, int(num_cells) + 1, int(num_cells) + 1]
137
+
138
+ return xyz, grid_size, length
139
+
140
+
141
+ class VanillaVolumeDecoder:
142
+ @torch.no_grad()
143
+ def __call__(
144
+ self,
145
+ latents: torch.FloatTensor,
146
+ geo_decoder: Callable,
147
+ bounds: Union[Tuple[float], List[float], float] = 1.01,
148
+ num_chunks: int = 10000,
149
+ octree_resolution: int = None,
150
+ enable_pbar: bool = True,
151
+ **kwargs,
152
+ ):
153
+ device = latents.device
154
+ dtype = latents.dtype
155
+ batch_size = latents.shape[0]
156
+
157
+ # 1. generate query points
158
+ if isinstance(bounds, float):
159
+ bounds = [-bounds, -bounds, -bounds, bounds, bounds, bounds]
160
+
161
+ bbox_min, bbox_max = np.array(bounds[0:3]), np.array(bounds[3:6])
162
+ xyz_samples, grid_size, length = generate_dense_grid_points(
163
+ bbox_min=bbox_min,
164
+ bbox_max=bbox_max,
165
+ octree_resolution=octree_resolution,
166
+ indexing="ij"
167
+ )
168
+ xyz_samples = torch.from_numpy(xyz_samples).to(device, dtype=dtype).contiguous().reshape(-1, 3)
169
+
170
+ # 2. latents to 3d volume
171
+ batch_logits = []
172
+ for start in tqdm(range(0, xyz_samples.shape[0], num_chunks), desc=f"Volume Decoding",
173
+ disable=not enable_pbar):
174
+ chunk_queries = xyz_samples[start: start + num_chunks, :]
175
+ chunk_queries = repeat(chunk_queries, "p c -> b p c", b=batch_size)
176
+ logits = geo_decoder(queries=chunk_queries, latents=latents)
177
+ batch_logits.append(logits)
178
+
179
+ grid_logits = torch.cat(batch_logits, dim=1)
180
+ grid_logits = grid_logits.view((batch_size, *grid_size)).float()
181
+
182
+ return grid_logits
183
+
184
+
185
+ class HierarchicalVolumeDecoding:
186
+ @torch.no_grad()
187
+ def __call__(
188
+ self,
189
+ latents: torch.FloatTensor,
190
+ geo_decoder: Callable,
191
+ bounds: Union[Tuple[float], List[float], float] = 1.01,
192
+ num_chunks: int = 10000,
193
+ mc_level: float = 0.0,
194
+ octree_resolution: int = None,
195
+ min_resolution: int = 63,
196
+ enable_pbar: bool = True,
197
+ **kwargs,
198
+ ):
199
+ device = latents.device
200
+ dtype = latents.dtype
201
+
202
+ resolutions = []
203
+ if octree_resolution < min_resolution:
204
+ resolutions.append(octree_resolution)
205
+ while octree_resolution >= min_resolution:
206
+ resolutions.append(octree_resolution)
207
+ octree_resolution = octree_resolution // 2
208
+ resolutions.reverse()
209
+
210
+ # 1. generate query points
211
+ if isinstance(bounds, float):
212
+ bounds = [-bounds, -bounds, -bounds, bounds, bounds, bounds]
213
+ bbox_min = np.array(bounds[0:3])
214
+ bbox_max = np.array(bounds[3:6])
215
+ bbox_size = bbox_max - bbox_min
216
+
217
+ xyz_samples, grid_size, length = generate_dense_grid_points(
218
+ bbox_min=bbox_min,
219
+ bbox_max=bbox_max,
220
+ octree_resolution=resolutions[0],
221
+ indexing="ij"
222
+ )
223
+
224
+ dilate = nn.Conv3d(1, 1, 3, padding=1, bias=False, device=device, dtype=dtype)
225
+ dilate.weight = torch.nn.Parameter(torch.ones(dilate.weight.shape, dtype=dtype, device=device))
226
+
227
+ grid_size = np.array(grid_size)
228
+ xyz_samples = torch.from_numpy(xyz_samples).to(device, dtype=dtype).contiguous().reshape(-1, 3)
229
+
230
+ # 2. latents to 3d volume
231
+ batch_logits = []
232
+ batch_size = latents.shape[0]
233
+ for start in tqdm(range(0, xyz_samples.shape[0], num_chunks),
234
+ desc=f"Hierarchical Volume Decoding [r{resolutions[0] + 1}]"):
235
+ queries = xyz_samples[start: start + num_chunks, :]
236
+ batch_queries = repeat(queries, "p c -> b p c", b=batch_size)
237
+ logits = geo_decoder(queries=batch_queries, latents=latents)
238
+ batch_logits.append(logits)
239
+
240
+ grid_logits = torch.cat(batch_logits, dim=1).view((batch_size, grid_size[0], grid_size[1], grid_size[2]))
241
+
242
+ for octree_depth_now in resolutions[1:]:
243
+ grid_size = np.array([octree_depth_now + 1] * 3)
244
+ resolution = bbox_size / octree_depth_now
245
+ next_index = torch.zeros(tuple(grid_size), dtype=dtype, device=device)
246
+ next_logits = torch.full(next_index.shape, -10000., dtype=dtype, device=device)
247
+ curr_points = extract_near_surface_volume_fn(grid_logits.squeeze(0), mc_level)
248
+ curr_points += grid_logits.squeeze(0).abs() < 0.95
249
+
250
+ if octree_depth_now == resolutions[-1]:
251
+ expand_num = 0
252
+ else:
253
+ expand_num = 1
254
+ for i in range(expand_num):
255
+ curr_points = dilate(curr_points.unsqueeze(0).to(dtype)).squeeze(0)
256
+ (cidx_x, cidx_y, cidx_z) = torch.where(curr_points > 0)
257
+ next_index[cidx_x * 2, cidx_y * 2, cidx_z * 2] = 1
258
+ for i in range(2 - expand_num):
259
+ next_index = dilate(next_index.unsqueeze(0)).squeeze(0)
260
+ nidx = torch.where(next_index > 0)
261
+
262
+ next_points = torch.stack(nidx, dim=1)
263
+ next_points = (next_points * torch.tensor(resolution, dtype=next_points.dtype, device=device) +
264
+ torch.tensor(bbox_min, dtype=next_points.dtype, device=device))
265
+ batch_logits = []
266
+ for start in tqdm(range(0, next_points.shape[0], num_chunks),
267
+ desc=f"Hierarchical Volume Decoding [r{octree_depth_now + 1}]"):
268
+ queries = next_points[start: start + num_chunks, :]
269
+ batch_queries = repeat(queries, "p c -> b p c", b=batch_size)
270
+ logits = geo_decoder(queries=batch_queries.to(latents.dtype), latents=latents)
271
+ batch_logits.append(logits)
272
+ grid_logits = torch.cat(batch_logits, dim=1)
273
+ next_logits[nidx] = grid_logits[0, ..., 0]
274
+ grid_logits = next_logits.unsqueeze(0)
275
+ grid_logits[grid_logits == -10000.] = float('nan')
276
+
277
+ return grid_logits
278
+
279
+
280
+ class FlashVDMVolumeDecoding:
281
+ def __init__(self, topk_mode='mean'):
282
+ if topk_mode not in ['mean', 'merge']:
283
+ raise ValueError(f'Unsupported topk_mode {topk_mode}, available: {["mean", "merge"]}')
284
+
285
+ if topk_mode == 'mean':
286
+ self.processor = FlashVDMCrossAttentionProcessor()
287
+ else:
288
+ self.processor = FlashVDMTopMCrossAttentionProcessor()
289
+
290
+ @torch.no_grad()
291
+ def __call__(
292
+ self,
293
+ latents: torch.FloatTensor,
294
+ geo_decoder: CrossAttentionDecoder,
295
+ bounds: Union[Tuple[float], List[float], float] = 1.01,
296
+ num_chunks: int = 10000,
297
+ mc_level: float = 0.0,
298
+ octree_resolution: int = None,
299
+ min_resolution: int = 63,
300
+ mini_grid_num: int = 4,
301
+ enable_pbar: bool = True,
302
+ **kwargs,
303
+ ):
304
+ processor = self.processor
305
+ geo_decoder.set_cross_attention_processor(processor)
306
+
307
+ device = latents.device
308
+ dtype = latents.dtype
309
+
310
+ resolutions = []
311
+ if octree_resolution < min_resolution:
312
+ resolutions.append(octree_resolution)
313
+ while octree_resolution >= min_resolution:
314
+ resolutions.append(octree_resolution)
315
+ octree_resolution = octree_resolution // 2
316
+ resolutions.reverse()
317
+ resolutions[0] = round(resolutions[0] / mini_grid_num) * mini_grid_num - 1
318
+ for i, resolution in enumerate(resolutions[1:]):
319
+ resolutions[i + 1] = resolutions[0] * 2 ** (i + 1)
320
+
321
+ logger.info(f"FlashVDMVolumeDecoding Resolution: {resolutions}")
322
+
323
+ # 1. generate query points
324
+ if isinstance(bounds, float):
325
+ bounds = [-bounds, -bounds, -bounds, bounds, bounds, bounds]
326
+ bbox_min = np.array(bounds[0:3])
327
+ bbox_max = np.array(bounds[3:6])
328
+ bbox_size = bbox_max - bbox_min
329
+
330
+ xyz_samples, grid_size, length = generate_dense_grid_points(
331
+ bbox_min=bbox_min,
332
+ bbox_max=bbox_max,
333
+ octree_resolution=resolutions[0],
334
+ indexing="ij"
335
+ )
336
+
337
+ dilate = nn.Conv3d(1, 1, 3, padding=1, bias=False, device=device, dtype=dtype)
338
+ dilate.weight = torch.nn.Parameter(torch.ones(dilate.weight.shape, dtype=dtype, device=device))
339
+
340
+ grid_size = np.array(grid_size)
341
+
342
+ # 2. latents to 3d volume
343
+ xyz_samples = torch.from_numpy(xyz_samples).to(device, dtype=dtype)
344
+ batch_size = latents.shape[0]
345
+ mini_grid_size = xyz_samples.shape[0] // mini_grid_num
346
+ xyz_samples = xyz_samples.view(
347
+ mini_grid_num, mini_grid_size,
348
+ mini_grid_num, mini_grid_size,
349
+ mini_grid_num, mini_grid_size, 3
350
+ ).permute(
351
+ 0, 2, 4, 1, 3, 5, 6
352
+ ).reshape(
353
+ -1, mini_grid_size * mini_grid_size * mini_grid_size, 3
354
+ )
355
+ batch_logits = []
356
+ num_batchs = max(num_chunks // xyz_samples.shape[1], 1)
357
+ for start in tqdm(range(0, xyz_samples.shape[0], num_batchs),
358
+ desc=f"FlashVDM Volume Decoding", disable=not enable_pbar):
359
+ queries = xyz_samples[start: start + num_batchs, :]
360
+ batch = queries.shape[0]
361
+ batch_latents = repeat(latents.squeeze(0), "p c -> b p c", b=batch)
362
+ processor.topk = True
363
+ logits = geo_decoder(queries=queries, latents=batch_latents)
364
+ batch_logits.append(logits)
365
+ grid_logits = torch.cat(batch_logits, dim=0).reshape(
366
+ mini_grid_num, mini_grid_num, mini_grid_num,
367
+ mini_grid_size, mini_grid_size,
368
+ mini_grid_size
369
+ ).permute(0, 3, 1, 4, 2, 5).contiguous().view(
370
+ (batch_size, grid_size[0], grid_size[1], grid_size[2])
371
+ )
372
+
373
+ for octree_depth_now in resolutions[1:]:
374
+ grid_size = np.array([octree_depth_now + 1] * 3)
375
+ resolution = bbox_size / octree_depth_now
376
+ next_index = torch.zeros(tuple(grid_size), dtype=dtype, device=device)
377
+ next_logits = torch.full(next_index.shape, -10000., dtype=dtype, device=device)
378
+ curr_points = extract_near_surface_volume_fn(grid_logits.squeeze(0), mc_level)
379
+ curr_points += grid_logits.squeeze(0).abs() < 0.95
380
+
381
+ if octree_depth_now == resolutions[-1]:
382
+ expand_num = 0
383
+ else:
384
+ expand_num = 1
385
+ for i in range(expand_num):
386
+ curr_points = dilate(curr_points.unsqueeze(0).to(dtype)).squeeze(0)
387
+ (cidx_x, cidx_y, cidx_z) = torch.where(curr_points > 0)
388
+
389
+ next_index[cidx_x * 2, cidx_y * 2, cidx_z * 2] = 1
390
+ for i in range(2 - expand_num):
391
+ next_index = dilate(next_index.unsqueeze(0)).squeeze(0)
392
+ nidx = torch.where(next_index > 0)
393
+
394
+ next_points = torch.stack(nidx, dim=1)
395
+ next_points = (next_points * torch.tensor(resolution, dtype=torch.float32, device=device) +
396
+ torch.tensor(bbox_min, dtype=torch.float32, device=device))
397
+
398
+ query_grid_num = 6
399
+ min_val = next_points.min(axis=0).values
400
+ max_val = next_points.max(axis=0).values
401
+ vol_queries_index = (next_points - min_val) / (max_val - min_val) * (query_grid_num - 0.001)
402
+ index = torch.floor(vol_queries_index).long()
403
+ index = index[..., 0] * (query_grid_num ** 2) + index[..., 1] * query_grid_num + index[..., 2]
404
+ index = index.sort()
405
+ next_points = next_points[index.indices].unsqueeze(0).contiguous()
406
+ unique_values = torch.unique(index.values, return_counts=True)
407
+ grid_logits = torch.zeros((next_points.shape[1]), dtype=latents.dtype, device=latents.device)
408
+ input_grid = [[], []]
409
+ logits_grid_list = []
410
+ start_num = 0
411
+ sum_num = 0
412
+ for grid_index, count in zip(unique_values[0].cpu().tolist(), unique_values[1].cpu().tolist()):
413
+ if sum_num + count < num_chunks or sum_num == 0:
414
+ sum_num += count
415
+ input_grid[0].append(grid_index)
416
+ input_grid[1].append(count)
417
+ else:
418
+ processor.topk = input_grid
419
+ logits_grid = geo_decoder(queries=next_points[:, start_num:start_num + sum_num], latents=latents)
420
+ start_num = start_num + sum_num
421
+ logits_grid_list.append(logits_grid)
422
+ input_grid = [[grid_index], [count]]
423
+ sum_num = count
424
+ if sum_num > 0:
425
+ processor.topk = input_grid
426
+ logits_grid = geo_decoder(queries=next_points[:, start_num:start_num + sum_num], latents=latents)
427
+ logits_grid_list.append(logits_grid)
428
+ logits_grid = torch.cat(logits_grid_list, dim=1)
429
+ grid_logits[index.indices] = logits_grid.squeeze(0).squeeze(-1)
430
+ next_logits[nidx] = grid_logits
431
+ grid_logits = next_logits.unsqueeze(0)
432
+
433
+ grid_logits[grid_logits == -10000.] = float('nan')
434
+
435
+ return grid_logits
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/conditioner.py ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Open Source Model Licensed under the Apache License Version 2.0
2
+ # and Other Licenses of the Third-Party Components therein:
3
+ # The below Model in this distribution may have been modified by THL A29 Limited
4
+ # ("Tencent Modifications"). All Tencent Modifications are Copyright (C) 2024 THL A29 Limited.
5
+
6
+ # Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved.
7
+ # The below software and/or models in this distribution may have been
8
+ # modified by THL A29 Limited ("Tencent Modifications").
9
+ # All Tencent Modifications are Copyright (C) THL A29 Limited.
10
+
11
+ # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
12
+ # except for the third-party components listed below.
13
+ # Hunyuan 3D does not impose any additional limitations beyond what is outlined
14
+ # in the repsective licenses of these third-party components.
15
+ # Users must comply with all terms and conditions of original licenses of these third-party
16
+ # components and must ensure that the usage of the third party components adheres to
17
+ # all relevant laws and regulations.
18
+
19
+ # For avoidance of doubts, Hunyuan 3D means the large language models and
20
+ # their software and algorithms, including trained model weights, parameters (including
21
+ # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
22
+ # fine-tuning enabling code and other elements of the foregoing made publicly available
23
+ # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
24
+
25
+ import numpy as np
26
+ import torch
27
+ import torch.nn as nn
28
+ from torchvision import transforms
29
+ from transformers import (
30
+ CLIPVisionModelWithProjection,
31
+ CLIPVisionConfig,
32
+ Dinov2Model,
33
+ Dinov2Config,
34
+ )
35
+
36
+
37
+ def get_1d_sincos_pos_embed_from_grid(embed_dim, pos):
38
+ """
39
+ embed_dim: output dimension for each position
40
+ pos: a list of positions to be encoded: size (M,)
41
+ out: (M, D)
42
+ """
43
+ assert embed_dim % 2 == 0
44
+ omega = np.arange(embed_dim // 2, dtype=np.float64)
45
+ omega /= embed_dim / 2.
46
+ omega = 1. / 10000 ** omega # (D/2,)
47
+
48
+ pos = pos.reshape(-1) # (M,)
49
+ out = np.einsum('m,d->md', pos, omega) # (M, D/2), outer product
50
+
51
+ emb_sin = np.sin(out) # (M, D/2)
52
+ emb_cos = np.cos(out) # (M, D/2)
53
+
54
+ return np.concatenate([emb_sin, emb_cos], axis=1)
55
+
56
+
57
+ class ImageEncoder(nn.Module):
58
+ def __init__(
59
+ self,
60
+ version=None,
61
+ config=None,
62
+ use_cls_token=True,
63
+ image_size=224,
64
+ **kwargs,
65
+ ):
66
+ super().__init__()
67
+
68
+ if config is None:
69
+ self.model = self.MODEL_CLASS.from_pretrained(version)
70
+ else:
71
+ self.model = self.MODEL_CLASS(self.MODEL_CONFIG_CLASS.from_dict(config))
72
+ self.model.eval()
73
+ self.model.requires_grad_(False)
74
+ self.use_cls_token = use_cls_token
75
+ self.size = image_size // 14
76
+ self.num_patches = (image_size // 14) ** 2
77
+ if self.use_cls_token:
78
+ self.num_patches += 1
79
+
80
+ self.transform = transforms.Compose(
81
+ [
82
+ transforms.Resize(image_size, transforms.InterpolationMode.BILINEAR, antialias=True),
83
+ transforms.CenterCrop(image_size),
84
+ transforms.Normalize(
85
+ mean=self.mean,
86
+ std=self.std,
87
+ ),
88
+ ]
89
+ )
90
+
91
+ def forward(self, image, mask=None, value_range=(-1, 1), **kwargs):
92
+ if value_range is not None:
93
+ low, high = value_range
94
+ image = (image - low) / (high - low)
95
+
96
+ image = image.to(self.model.device, dtype=self.model.dtype)
97
+ inputs = self.transform(image)
98
+ outputs = self.model(inputs)
99
+
100
+ last_hidden_state = outputs.last_hidden_state
101
+ if not self.use_cls_token:
102
+ last_hidden_state = last_hidden_state[:, 1:, :]
103
+
104
+ return last_hidden_state
105
+
106
+ def unconditional_embedding(self, batch_size, **kwargs):
107
+ device = next(self.model.parameters()).device
108
+ dtype = next(self.model.parameters()).dtype
109
+ zero = torch.zeros(
110
+ batch_size,
111
+ self.num_patches,
112
+ self.model.config.hidden_size,
113
+ device=device,
114
+ dtype=dtype,
115
+ )
116
+
117
+ return zero
118
+
119
+
120
+ class CLIPImageEncoder(ImageEncoder):
121
+ MODEL_CLASS = CLIPVisionModelWithProjection
122
+ MODEL_CONFIG_CLASS = CLIPVisionConfig
123
+ mean = [0.48145466, 0.4578275, 0.40821073]
124
+ std = [0.26862954, 0.26130258, 0.27577711]
125
+
126
+
127
+ class DinoImageEncoder(ImageEncoder):
128
+ MODEL_CLASS = Dinov2Model
129
+ MODEL_CONFIG_CLASS = Dinov2Config
130
+ mean = [0.485, 0.456, 0.406]
131
+ std = [0.229, 0.224, 0.225]
132
+
133
+
134
+ class DinoImageEncoderMV(DinoImageEncoder):
135
+ def __init__(
136
+ self,
137
+ version=None,
138
+ config=None,
139
+ use_cls_token=True,
140
+ image_size=224,
141
+ view_num=4,
142
+ **kwargs,
143
+ ):
144
+ super().__init__(version, config, use_cls_token, image_size, **kwargs)
145
+ self.view_num = view_num
146
+ self.num_patches = self.num_patches
147
+ pos = np.arange(self.view_num, dtype=np.float32)
148
+ view_embedding = torch.from_numpy(
149
+ get_1d_sincos_pos_embed_from_grid(self.model.config.hidden_size, pos)).float()
150
+
151
+ view_embedding = view_embedding.unsqueeze(1).repeat(1, self.num_patches, 1)
152
+ self.view_embed = view_embedding.unsqueeze(0)
153
+
154
+ def forward(self, image, mask=None, value_range=(-1, 1), view_idxs=None):
155
+ if value_range is not None:
156
+ low, high = value_range
157
+ image = (image - low) / (high - low)
158
+
159
+ image = image.to(self.model.device, dtype=self.model.dtype)
160
+
161
+ bs, num_views, c, h, w = image.shape
162
+ image = image.view(bs * num_views, c, h, w)
163
+
164
+ inputs = self.transform(image)
165
+ outputs = self.model(inputs)
166
+
167
+ last_hidden_state = outputs.last_hidden_state
168
+ last_hidden_state = last_hidden_state.view(
169
+ bs, num_views, last_hidden_state.shape[-2],
170
+ last_hidden_state.shape[-1]
171
+ )
172
+
173
+ view_embedding = self.view_embed.to(last_hidden_state.dtype).to(last_hidden_state.device)
174
+ if view_idxs is not None:
175
+ assert len(view_idxs) == bs
176
+ view_embeddings = []
177
+ for i in range(bs):
178
+ view_idx = view_idxs[i]
179
+ assert num_views == len(view_idx)
180
+ view_embeddings.append(self.view_embed[:, view_idx, ...])
181
+ view_embedding = torch.cat(view_embeddings, 0).to(last_hidden_state.dtype).to(last_hidden_state.device)
182
+
183
+ if num_views != self.view_num:
184
+ view_embedding = view_embedding[:, :num_views, ...]
185
+ last_hidden_state = last_hidden_state + view_embedding
186
+ last_hidden_state = last_hidden_state.view(bs, num_views * last_hidden_state.shape[-2],
187
+ last_hidden_state.shape[-1])
188
+ return last_hidden_state
189
+
190
+ def unconditional_embedding(self, batch_size, view_idxs=None, **kwargs):
191
+ device = next(self.model.parameters()).device
192
+ dtype = next(self.model.parameters()).dtype
193
+ zero = torch.zeros(
194
+ batch_size,
195
+ self.num_patches * len(view_idxs[0]),
196
+ self.model.config.hidden_size,
197
+ device=device,
198
+ dtype=dtype,
199
+ )
200
+ return zero
201
+
202
+
203
+ def build_image_encoder(config):
204
+ if config['type'] == 'CLIPImageEncoder':
205
+ return CLIPImageEncoder(**config['kwargs'])
206
+ elif config['type'] == 'DinoImageEncoder':
207
+ return DinoImageEncoder(**config['kwargs'])
208
+ elif config['type'] == 'DinoImageEncoderMV':
209
+ return DinoImageEncoderMV(**config['kwargs'])
210
+ else:
211
+ raise ValueError(f'Unknown image encoder type: {config["type"]}')
212
+
213
+
214
+ class DualImageEncoder(nn.Module):
215
+ def __init__(
216
+ self,
217
+ main_image_encoder,
218
+ additional_image_encoder,
219
+ ):
220
+ super().__init__()
221
+ self.main_image_encoder = build_image_encoder(main_image_encoder)
222
+ self.additional_image_encoder = build_image_encoder(additional_image_encoder)
223
+
224
+ def forward(self, image, mask=None, **kwargs):
225
+ outputs = {
226
+ 'main': self.main_image_encoder(image, mask=mask, **kwargs),
227
+ 'additional': self.additional_image_encoder(image, mask=mask, **kwargs),
228
+ }
229
+ return outputs
230
+
231
+ def unconditional_embedding(self, batch_size, **kwargs):
232
+ outputs = {
233
+ 'main': self.main_image_encoder.unconditional_embedding(batch_size, **kwargs),
234
+ 'additional': self.additional_image_encoder.unconditional_embedding(batch_size, **kwargs),
235
+ }
236
+ return outputs
237
+
238
+
239
+ class SingleImageEncoder(nn.Module):
240
+ def __init__(
241
+ self,
242
+ main_image_encoder,
243
+ ):
244
+ super().__init__()
245
+ self.main_image_encoder = build_image_encoder(main_image_encoder)
246
+
247
+ def forward(self, image, mask=None, **kwargs):
248
+ outputs = {
249
+ 'main': self.main_image_encoder(image, mask=mask, **kwargs),
250
+ }
251
+ return outputs
252
+
253
+ def unconditional_embedding(self, batch_size, **kwargs):
254
+ outputs = {
255
+ 'main': self.main_image_encoder.unconditional_embedding(batch_size, **kwargs),
256
+ }
257
+ return outputs
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/denoisers/__init__.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
+ # except for the third-party components listed below.
3
+ # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
+ # in the repsective licenses of these third-party components.
5
+ # Users must comply with all terms and conditions of original licenses of these third-party
6
+ # components and must ensure that the usage of the third party components adheres to
7
+ # all relevant laws and regulations.
8
+
9
+ # For avoidance of doubts, Hunyuan 3D means the large language models and
10
+ # their software and algorithms, including trained model weights, parameters (including
11
+ # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
+ # fine-tuning enabling code and other elements of the foregoing made publicly available
13
+ # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
+
15
+ from .hunyuan3ddit import Hunyuan3DDiT
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/denoisers/hunyuan3ddit.py ADDED
@@ -0,0 +1,404 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
+ # except for the third-party components listed below.
3
+ # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
+ # in the repsective licenses of these third-party components.
5
+ # Users must comply with all terms and conditions of original licenses of these third-party
6
+ # components and must ensure that the usage of the third party components adheres to
7
+ # all relevant laws and regulations.
8
+
9
+ # For avoidance of doubts, Hunyuan 3D means the large language models and
10
+ # their software and algorithms, including trained model weights, parameters (including
11
+ # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
+ # fine-tuning enabling code and other elements of the foregoing made publicly available
13
+ # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
+
15
+ import os
16
+ import math
17
+ from dataclasses import dataclass
18
+ from typing import List, Tuple, Optional
19
+
20
+ import torch
21
+ from torch import Tensor, nn
22
+ from einops import rearrange
23
+
24
+ # set up attention backend
25
+ scaled_dot_product_attention = nn.functional.scaled_dot_product_attention
26
+ if os.environ.get('USE_SAGEATTN', '0') == '1':
27
+ try:
28
+ from sageattention import sageattn
29
+ except ImportError:
30
+ raise ImportError('Please install the package "sageattention" to use this USE_SAGEATTN.')
31
+ scaled_dot_product_attention = sageattn
32
+
33
+
34
+ def attention(q: Tensor, k: Tensor, v: Tensor, **kwargs) -> Tensor:
35
+ x = scaled_dot_product_attention(q, k, v)
36
+ x = rearrange(x, "B H L D -> B L (H D)")
37
+ return x
38
+
39
+
40
+ def timestep_embedding(t: Tensor, dim, max_period=10000, time_factor: float = 1000.0):
41
+ """
42
+ Create sinusoidal timestep embeddings.
43
+ :param t: a 1-D Tensor of N indices, one per batch element.
44
+ These may be fractional.
45
+ :param dim: the dimension of the output.
46
+ :param max_period: controls the minimum frequency of the embeddings.
47
+ :return: an (N, D) Tensor of positional embeddings.
48
+ """
49
+ t = time_factor * t
50
+ half = dim // 2
51
+ freqs = torch.exp(-math.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32) / half)
52
+ freqs = freqs.to(t.device)
53
+
54
+ args = t[:, None].float() * freqs[None]
55
+ embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
56
+ if dim % 2:
57
+ embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1)
58
+ if torch.is_floating_point(t):
59
+ embedding = embedding.to(t)
60
+ return embedding
61
+
62
+
63
+ class GELU(nn.Module):
64
+ def __init__(self, approximate='tanh'):
65
+ super().__init__()
66
+ self.approximate = approximate
67
+
68
+ def forward(self, x: Tensor) -> Tensor:
69
+ return nn.functional.gelu(x.contiguous(), approximate=self.approximate)
70
+
71
+
72
+ class MLPEmbedder(nn.Module):
73
+ def __init__(self, in_dim: int, hidden_dim: int):
74
+ super().__init__()
75
+ self.in_layer = nn.Linear(in_dim, hidden_dim, bias=True)
76
+ self.silu = nn.SiLU()
77
+ self.out_layer = nn.Linear(hidden_dim, hidden_dim, bias=True)
78
+
79
+ def forward(self, x: Tensor) -> Tensor:
80
+ return self.out_layer(self.silu(self.in_layer(x)))
81
+
82
+
83
+ class RMSNorm(torch.nn.Module):
84
+ def __init__(self, dim: int):
85
+ super().__init__()
86
+ self.scale = nn.Parameter(torch.ones(dim))
87
+
88
+ def forward(self, x: Tensor):
89
+ x_dtype = x.dtype
90
+ x = x.float()
91
+ rrms = torch.rsqrt(torch.mean(x ** 2, dim=-1, keepdim=True) + 1e-6)
92
+ return (x * rrms).to(dtype=x_dtype) * self.scale
93
+
94
+
95
+ class QKNorm(torch.nn.Module):
96
+ def __init__(self, dim: int):
97
+ super().__init__()
98
+ self.query_norm = RMSNorm(dim)
99
+ self.key_norm = RMSNorm(dim)
100
+
101
+ def forward(self, q: Tensor, k: Tensor, v: Tensor) -> Tuple[Tensor, Tensor]:
102
+ q = self.query_norm(q)
103
+ k = self.key_norm(k)
104
+ return q.to(v), k.to(v)
105
+
106
+
107
+ class SelfAttention(nn.Module):
108
+ def __init__(
109
+ self,
110
+ dim: int,
111
+ num_heads: int = 8,
112
+ qkv_bias: bool = False,
113
+ ):
114
+ super().__init__()
115
+ self.num_heads = num_heads
116
+ head_dim = dim // num_heads
117
+
118
+ self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias)
119
+ self.norm = QKNorm(head_dim)
120
+ self.proj = nn.Linear(dim, dim)
121
+
122
+ def forward(self, x: Tensor, pe: Tensor) -> Tensor:
123
+ qkv = self.qkv(x)
124
+ q, k, v = rearrange(qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads)
125
+ q, k = self.norm(q, k, v)
126
+ x = attention(q, k, v, pe=pe)
127
+ x = self.proj(x)
128
+ return x
129
+
130
+
131
+ @dataclass
132
+ class ModulationOut:
133
+ shift: Tensor
134
+ scale: Tensor
135
+ gate: Tensor
136
+
137
+
138
+ class Modulation(nn.Module):
139
+ def __init__(self, dim: int, double: bool):
140
+ super().__init__()
141
+ self.is_double = double
142
+ self.multiplier = 6 if double else 3
143
+ self.lin = nn.Linear(dim, self.multiplier * dim, bias=True)
144
+
145
+ def forward(self, vec: Tensor) -> Tuple[ModulationOut, Optional[ModulationOut]]:
146
+ out = self.lin(nn.functional.silu(vec))[:, None, :]
147
+ out = out.chunk(self.multiplier, dim=-1)
148
+
149
+ return (
150
+ ModulationOut(*out[:3]),
151
+ ModulationOut(*out[3:]) if self.is_double else None,
152
+ )
153
+
154
+
155
+ class DoubleStreamBlock(nn.Module):
156
+ def __init__(
157
+ self,
158
+ hidden_size: int,
159
+ num_heads: int,
160
+ mlp_ratio: float,
161
+ qkv_bias: bool = False,
162
+ ):
163
+ super().__init__()
164
+ mlp_hidden_dim = int(hidden_size * mlp_ratio)
165
+ self.num_heads = num_heads
166
+ self.hidden_size = hidden_size
167
+ self.img_mod = Modulation(hidden_size, double=True)
168
+ self.img_norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
169
+ self.img_attn = SelfAttention(dim=hidden_size, num_heads=num_heads, qkv_bias=qkv_bias)
170
+
171
+ self.img_norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
172
+ self.img_mlp = nn.Sequential(
173
+ nn.Linear(hidden_size, mlp_hidden_dim, bias=True),
174
+ GELU(approximate="tanh"),
175
+ nn.Linear(mlp_hidden_dim, hidden_size, bias=True),
176
+ )
177
+
178
+ self.txt_mod = Modulation(hidden_size, double=True)
179
+ self.txt_norm1 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
180
+ self.txt_attn = SelfAttention(dim=hidden_size, num_heads=num_heads, qkv_bias=qkv_bias)
181
+
182
+ self.txt_norm2 = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
183
+ self.txt_mlp = nn.Sequential(
184
+ nn.Linear(hidden_size, mlp_hidden_dim, bias=True),
185
+ GELU(approximate="tanh"),
186
+ nn.Linear(mlp_hidden_dim, hidden_size, bias=True),
187
+ )
188
+
189
+ def forward(self, img: Tensor, txt: Tensor, vec: Tensor, pe: Tensor) -> Tuple[Tensor, Tensor]:
190
+ img_mod1, img_mod2 = self.img_mod(vec)
191
+ txt_mod1, txt_mod2 = self.txt_mod(vec)
192
+
193
+ img_modulated = self.img_norm1(img)
194
+ img_modulated = (1 + img_mod1.scale) * img_modulated + img_mod1.shift
195
+ img_qkv = self.img_attn.qkv(img_modulated)
196
+ img_q, img_k, img_v = rearrange(img_qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads)
197
+ img_q, img_k = self.img_attn.norm(img_q, img_k, img_v)
198
+
199
+ txt_modulated = self.txt_norm1(txt)
200
+ txt_modulated = (1 + txt_mod1.scale) * txt_modulated + txt_mod1.shift
201
+ txt_qkv = self.txt_attn.qkv(txt_modulated)
202
+ txt_q, txt_k, txt_v = rearrange(txt_qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads)
203
+ txt_q, txt_k = self.txt_attn.norm(txt_q, txt_k, txt_v)
204
+
205
+ q = torch.cat((txt_q, img_q), dim=2)
206
+ k = torch.cat((txt_k, img_k), dim=2)
207
+ v = torch.cat((txt_v, img_v), dim=2)
208
+
209
+ attn = attention(q, k, v, pe=pe)
210
+ txt_attn, img_attn = attn[:, : txt.shape[1]], attn[:, txt.shape[1]:]
211
+
212
+ img = img + img_mod1.gate * self.img_attn.proj(img_attn)
213
+ img = img + img_mod2.gate * self.img_mlp((1 + img_mod2.scale) * self.img_norm2(img) + img_mod2.shift)
214
+
215
+ txt = txt + txt_mod1.gate * self.txt_attn.proj(txt_attn)
216
+ txt = txt + txt_mod2.gate * self.txt_mlp((1 + txt_mod2.scale) * self.txt_norm2(txt) + txt_mod2.shift)
217
+ return img, txt
218
+
219
+
220
+ class SingleStreamBlock(nn.Module):
221
+ """
222
+ A DiT block with parallel linear layers as described in
223
+ https://arxiv.org/abs/2302.05442 and adapted modulation interface.
224
+ """
225
+
226
+ def __init__(
227
+ self,
228
+ hidden_size: int,
229
+ num_heads: int,
230
+ mlp_ratio: float = 4.0,
231
+ qk_scale: Optional[float] = None,
232
+ ):
233
+ super().__init__()
234
+
235
+ self.hidden_dim = hidden_size
236
+ self.num_heads = num_heads
237
+ head_dim = hidden_size // num_heads
238
+ self.scale = qk_scale or head_dim ** -0.5
239
+
240
+ self.mlp_hidden_dim = int(hidden_size * mlp_ratio)
241
+ # qkv and mlp_in
242
+ self.linear1 = nn.Linear(hidden_size, hidden_size * 3 + self.mlp_hidden_dim)
243
+ # proj and mlp_out
244
+ self.linear2 = nn.Linear(hidden_size + self.mlp_hidden_dim, hidden_size)
245
+
246
+ self.norm = QKNorm(head_dim)
247
+
248
+ self.hidden_size = hidden_size
249
+ self.pre_norm = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
250
+
251
+ self.mlp_act = GELU(approximate="tanh")
252
+ self.modulation = Modulation(hidden_size, double=False)
253
+
254
+ def forward(self, x: Tensor, vec: Tensor, pe: Tensor) -> Tensor:
255
+ mod, _ = self.modulation(vec)
256
+
257
+ x_mod = (1 + mod.scale) * self.pre_norm(x) + mod.shift
258
+ qkv, mlp = torch.split(self.linear1(x_mod), [3 * self.hidden_size, self.mlp_hidden_dim], dim=-1)
259
+
260
+ q, k, v = rearrange(qkv, "B L (K H D) -> K B H L D", K=3, H=self.num_heads)
261
+ q, k = self.norm(q, k, v)
262
+
263
+ # compute attention
264
+ attn = attention(q, k, v, pe=pe)
265
+ # compute activation in mlp stream, cat again and run second linear layer
266
+ output = self.linear2(torch.cat((attn, self.mlp_act(mlp)), 2))
267
+ return x + mod.gate * output
268
+
269
+
270
+ class LastLayer(nn.Module):
271
+ def __init__(self, hidden_size: int, patch_size: int, out_channels: int):
272
+ super().__init__()
273
+ self.norm_final = nn.LayerNorm(hidden_size, elementwise_affine=False, eps=1e-6)
274
+ self.linear = nn.Linear(hidden_size, patch_size * patch_size * out_channels, bias=True)
275
+ self.adaLN_modulation = nn.Sequential(nn.SiLU(), nn.Linear(hidden_size, 2 * hidden_size, bias=True))
276
+
277
+ def forward(self, x: Tensor, vec: Tensor) -> Tensor:
278
+ shift, scale = self.adaLN_modulation(vec).chunk(2, dim=1)
279
+ x = (1 + scale[:, None, :]) * self.norm_final(x) + shift[:, None, :]
280
+ x = self.linear(x)
281
+ return x
282
+
283
+
284
+ class Hunyuan3DDiT(nn.Module):
285
+ def __init__(
286
+ self,
287
+ in_channels: int = 64,
288
+ context_in_dim: int = 1536,
289
+ hidden_size: int = 1024,
290
+ mlp_ratio: float = 4.0,
291
+ num_heads: int = 16,
292
+ depth: int = 16,
293
+ depth_single_blocks: int = 32,
294
+ axes_dim: List[int] = [64],
295
+ theta: int = 10_000,
296
+ qkv_bias: bool = True,
297
+ time_factor: float = 1000,
298
+ guidance_embed: bool = False,
299
+ ckpt_path: Optional[str] = None,
300
+ **kwargs,
301
+ ):
302
+ super().__init__()
303
+ self.in_channels = in_channels
304
+ self.context_in_dim = context_in_dim
305
+ self.hidden_size = hidden_size
306
+ self.mlp_ratio = mlp_ratio
307
+ self.num_heads = num_heads
308
+ self.depth = depth
309
+ self.depth_single_blocks = depth_single_blocks
310
+ self.axes_dim = axes_dim
311
+ self.theta = theta
312
+ self.qkv_bias = qkv_bias
313
+ self.time_factor = time_factor
314
+ self.out_channels = self.in_channels
315
+ self.guidance_embed = guidance_embed
316
+
317
+ if hidden_size % num_heads != 0:
318
+ raise ValueError(
319
+ f"Hidden size {hidden_size} must be divisible by num_heads {num_heads}"
320
+ )
321
+ pe_dim = hidden_size // num_heads
322
+ if sum(axes_dim) != pe_dim:
323
+ raise ValueError(f"Got {axes_dim} but expected positional dim {pe_dim}")
324
+ self.hidden_size = hidden_size
325
+ self.num_heads = num_heads
326
+ self.latent_in = nn.Linear(self.in_channels, self.hidden_size, bias=True)
327
+ self.time_in = MLPEmbedder(in_dim=256, hidden_dim=self.hidden_size)
328
+ self.cond_in = nn.Linear(context_in_dim, self.hidden_size)
329
+ self.guidance_in = (
330
+ MLPEmbedder(in_dim=256, hidden_dim=self.hidden_size) if guidance_embed else nn.Identity()
331
+ )
332
+
333
+ self.double_blocks = nn.ModuleList(
334
+ [
335
+ DoubleStreamBlock(
336
+ self.hidden_size,
337
+ self.num_heads,
338
+ mlp_ratio=mlp_ratio,
339
+ qkv_bias=qkv_bias,
340
+ )
341
+ for _ in range(depth)
342
+ ]
343
+ )
344
+
345
+ self.single_blocks = nn.ModuleList(
346
+ [
347
+ SingleStreamBlock(
348
+ self.hidden_size,
349
+ self.num_heads,
350
+ mlp_ratio=mlp_ratio,
351
+ )
352
+ for _ in range(depth_single_blocks)
353
+ ]
354
+ )
355
+
356
+ self.final_layer = LastLayer(self.hidden_size, 1, self.out_channels)
357
+
358
+ if ckpt_path is not None:
359
+ print('restored denoiser ckpt', ckpt_path)
360
+
361
+ ckpt = torch.load(ckpt_path, map_location="cpu")
362
+ if 'state_dict' not in ckpt:
363
+ # deepspeed ckpt
364
+ state_dict = {}
365
+ for k in ckpt.keys():
366
+ new_k = k.replace('_forward_module.', '')
367
+ state_dict[new_k] = ckpt[k]
368
+ else:
369
+ state_dict = ckpt["state_dict"]
370
+
371
+ final_state_dict = {}
372
+ for k, v in state_dict.items():
373
+ if k.startswith('model.'):
374
+ final_state_dict[k.replace('model.', '')] = v
375
+ else:
376
+ final_state_dict[k] = v
377
+ missing, unexpected = self.load_state_dict(final_state_dict, strict=False)
378
+ print('unexpected keys:', unexpected)
379
+ print('missing keys:', missing)
380
+
381
+ def forward(self, x, t, contexts, **kwargs) -> Tensor:
382
+ cond = contexts['main']
383
+ latent = self.latent_in(x)
384
+
385
+ vec = self.time_in(timestep_embedding(t, 256, self.time_factor).to(dtype=latent.dtype))
386
+ if self.guidance_embed:
387
+ guidance = kwargs.get('guidance', None)
388
+ if guidance is None:
389
+ raise ValueError("Didn't get guidance strength for guidance distilled model.")
390
+ vec = vec + self.guidance_in(timestep_embedding(guidance, 256, self.time_factor))
391
+
392
+ cond = self.cond_in(cond)
393
+ pe = None
394
+
395
+ for block in self.double_blocks:
396
+ latent, cond = block(img=latent, txt=cond, vec=vec, pe=pe)
397
+
398
+ latent = torch.cat((cond, latent), 1)
399
+ for block in self.single_blocks:
400
+ latent = block(latent, vec=vec, pe=pe)
401
+
402
+ latent = latent[:, cond.shape[1]:, ...]
403
+ latent = self.final_layer(latent, vec)
404
+ return latent
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/denoisers/hunyuandit.py ADDED
@@ -0,0 +1,596 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Open Source Model Licensed under the Apache License Version 2.0
2
+ # and Other Licenses of the Third-Party Components therein:
3
+ # The below Model in this distribution may have been modified by THL A29 Limited
4
+ # ("Tencent Modifications"). All Tencent Modifications are Copyright (C) 2024 THL A29 Limited.
5
+
6
+ # Copyright (C) 2024 THL A29 Limited, a Tencent company. All rights reserved.
7
+ # The below software and/or models in this distribution may have been
8
+ # modified by THL A29 Limited ("Tencent Modifications").
9
+ # All Tencent Modifications are Copyright (C) THL A29 Limited.
10
+
11
+ # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
12
+ # except for the third-party components listed below.
13
+ # Hunyuan 3D does not impose any additional limitations beyond what is outlined
14
+ # in the repsective licenses of these third-party components.
15
+ # Users must comply with all terms and conditions of original licenses of these third-party
16
+ # components and must ensure that the usage of the third party components adheres to
17
+ # all relevant laws and regulations.
18
+
19
+ # For avoidance of doubts, Hunyuan 3D means the large language models and
20
+ # their software and algorithms, including trained model weights, parameters (including
21
+ # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
22
+ # fine-tuning enabling code and other elements of the foregoing made publicly available
23
+ # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
24
+
25
+ import math
26
+
27
+ import numpy as np
28
+ import torch
29
+ import torch.nn as nn
30
+ import torch.nn.functional as F
31
+ from einops import rearrange
32
+
33
+ from .moe_layers import MoEBlock
34
+
35
+
36
+ def modulate(x, shift, scale):
37
+ return x * (1 + scale.unsqueeze(1)) + shift.unsqueeze(1)
38
+
39
+
40
+ def get_1d_sincos_pos_embed_from_grid(embed_dim, pos):
41
+ """
42
+ embed_dim: output dimension for each position
43
+ pos: a list of positions to be encoded: size (M,)
44
+ out: (M, D)
45
+ """
46
+ assert embed_dim % 2 == 0
47
+ omega = np.arange(embed_dim // 2, dtype=np.float64)
48
+ omega /= embed_dim / 2.
49
+ omega = 1. / 10000 ** omega # (D/2,)
50
+
51
+ pos = pos.reshape(-1) # (M,)
52
+ out = np.einsum('m,d->md', pos, omega) # (M, D/2), outer product
53
+
54
+ emb_sin = np.sin(out) # (M, D/2)
55
+ emb_cos = np.cos(out) # (M, D/2)
56
+
57
+ return np.concatenate([emb_sin, emb_cos], axis=1)
58
+
59
+
60
+ class Timesteps(nn.Module):
61
+ def __init__(self,
62
+ num_channels: int,
63
+ downscale_freq_shift: float = 0.0,
64
+ scale: int = 1,
65
+ max_period: int = 10000
66
+ ):
67
+ super().__init__()
68
+ self.num_channels = num_channels
69
+ self.downscale_freq_shift = downscale_freq_shift
70
+ self.scale = scale
71
+ self.max_period = max_period
72
+
73
+ def forward(self, timesteps):
74
+ assert len(timesteps.shape) == 1, "Timesteps should be a 1d-array"
75
+ embedding_dim = self.num_channels
76
+ half_dim = embedding_dim // 2
77
+ exponent = -math.log(self.max_period) * torch.arange(
78
+ start=0, end=half_dim, dtype=torch.float32, device=timesteps.device)
79
+ exponent = exponent / (half_dim - self.downscale_freq_shift)
80
+ emb = torch.exp(exponent)
81
+ emb = timesteps[:, None].float() * emb[None, :]
82
+ emb = self.scale * emb
83
+ emb = torch.cat([torch.sin(emb), torch.cos(emb)], dim=-1)
84
+ if embedding_dim % 2 == 1:
85
+ emb = torch.nn.functional.pad(emb, (0, 1, 0, 0))
86
+ return emb
87
+
88
+
89
+ class TimestepEmbedder(nn.Module):
90
+ """
91
+ Embeds scalar timesteps into vector representations.
92
+ """
93
+
94
+ def __init__(self, hidden_size, frequency_embedding_size=256, cond_proj_dim=None, out_size=None):
95
+ super().__init__()
96
+ if out_size is None:
97
+ out_size = hidden_size
98
+ self.mlp = nn.Sequential(
99
+ nn.Linear(hidden_size, frequency_embedding_size, bias=True),
100
+ nn.GELU(),
101
+ nn.Linear(frequency_embedding_size, out_size, bias=True),
102
+ )
103
+ self.frequency_embedding_size = frequency_embedding_size
104
+
105
+ if cond_proj_dim is not None:
106
+ self.cond_proj = nn.Linear(cond_proj_dim, frequency_embedding_size, bias=False)
107
+
108
+ self.time_embed = Timesteps(hidden_size)
109
+
110
+ def forward(self, t, condition):
111
+
112
+ t_freq = self.time_embed(t).type(self.mlp[0].weight.dtype)
113
+
114
+ # t_freq = timestep_embedding(t, self.frequency_embedding_size).type(self.mlp[0].weight.dtype)
115
+ if condition is not None:
116
+ t_freq = t_freq + self.cond_proj(condition)
117
+
118
+ t = self.mlp(t_freq)
119
+ t = t.unsqueeze(dim=1)
120
+ return t
121
+
122
+
123
+ class MLP(nn.Module):
124
+ def __init__(self, *, width: int):
125
+ super().__init__()
126
+ self.width = width
127
+ self.fc1 = nn.Linear(width, width * 4)
128
+ self.fc2 = nn.Linear(width * 4, width)
129
+ self.gelu = nn.GELU()
130
+
131
+ def forward(self, x):
132
+ return self.fc2(self.gelu(self.fc1(x)))
133
+
134
+
135
+ class CrossAttention(nn.Module):
136
+ def __init__(
137
+ self,
138
+ qdim,
139
+ kdim,
140
+ num_heads,
141
+ qkv_bias=True,
142
+ qk_norm=False,
143
+ norm_layer=nn.LayerNorm,
144
+ with_decoupled_ca=False,
145
+ decoupled_ca_dim=16,
146
+ decoupled_ca_weight=1.0,
147
+ **kwargs,
148
+ ):
149
+ super().__init__()
150
+ self.qdim = qdim
151
+ self.kdim = kdim
152
+ self.num_heads = num_heads
153
+ assert self.qdim % num_heads == 0, "self.qdim must be divisible by num_heads"
154
+ self.head_dim = self.qdim // num_heads
155
+ assert self.head_dim % 8 == 0 and self.head_dim <= 128, "Only support head_dim <= 128 and divisible by 8"
156
+ self.scale = self.head_dim ** -0.5
157
+
158
+ self.to_q = nn.Linear(qdim, qdim, bias=qkv_bias)
159
+ self.to_k = nn.Linear(kdim, qdim, bias=qkv_bias)
160
+ self.to_v = nn.Linear(kdim, qdim, bias=qkv_bias)
161
+
162
+ # TODO: eps should be 1 / 65530 if using fp16
163
+ self.q_norm = norm_layer(self.head_dim, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
164
+ self.k_norm = norm_layer(self.head_dim, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
165
+ self.out_proj = nn.Linear(qdim, qdim, bias=True)
166
+
167
+ self.with_dca = with_decoupled_ca
168
+ if self.with_dca:
169
+ self.kv_proj_dca = nn.Linear(kdim, 2 * qdim, bias=qkv_bias)
170
+ self.k_norm_dca = norm_layer(self.head_dim, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
171
+ self.dca_dim = decoupled_ca_dim
172
+ self.dca_weight = decoupled_ca_weight
173
+
174
+ def forward(self, x, y):
175
+ """
176
+ Parameters
177
+ ----------
178
+ x: torch.Tensor
179
+ (batch, seqlen1, hidden_dim) (where hidden_dim = num heads * head dim)
180
+ y: torch.Tensor
181
+ (batch, seqlen2, hidden_dim2)
182
+ freqs_cis_img: torch.Tensor
183
+ (batch, hidden_dim // 2), RoPE for image
184
+ """
185
+ b, s1, c = x.shape # [b, s1, D]
186
+
187
+ if self.with_dca:
188
+ token_len = y.shape[1]
189
+ context_dca = y[:, -self.dca_dim:, :]
190
+ kv_dca = self.kv_proj_dca(context_dca).view(b, self.dca_dim, 2, self.num_heads, self.head_dim)
191
+ k_dca, v_dca = kv_dca.unbind(dim=2) # [b, s, h, d]
192
+ k_dca = self.k_norm_dca(k_dca)
193
+ y = y[:, :(token_len - self.dca_dim), :]
194
+
195
+ _, s2, c = y.shape # [b, s2, 1024]
196
+ q = self.to_q(x)
197
+ k = self.to_k(y)
198
+ v = self.to_v(y)
199
+
200
+ kv = torch.cat((k, v), dim=-1)
201
+ split_size = kv.shape[-1] // self.num_heads // 2
202
+ kv = kv.view(1, -1, self.num_heads, split_size * 2)
203
+ k, v = torch.split(kv, split_size, dim=-1)
204
+
205
+ q = q.view(b, s1, self.num_heads, self.head_dim) # [b, s1, h, d]
206
+ k = k.view(b, s2, self.num_heads, self.head_dim) # [b, s2, h, d]
207
+ v = v.view(b, s2, self.num_heads, self.head_dim) # [b, s2, h, d]
208
+
209
+ q = self.q_norm(q)
210
+ k = self.k_norm(k)
211
+
212
+ with torch.backends.cuda.sdp_kernel(
213
+ enable_flash=True,
214
+ enable_math=False,
215
+ enable_mem_efficient=True
216
+ ):
217
+ q, k, v = map(lambda t: rearrange(t, 'b n h d -> b h n d', h=self.num_heads), (q, k, v))
218
+ context = F.scaled_dot_product_attention(
219
+ q, k, v
220
+ ).transpose(1, 2).reshape(b, s1, -1)
221
+
222
+ if self.with_dca:
223
+ with torch.backends.cuda.sdp_kernel(
224
+ enable_flash=True,
225
+ enable_math=False,
226
+ enable_mem_efficient=True
227
+ ):
228
+ k_dca, v_dca = map(lambda t: rearrange(t, 'b n h d -> b h n d', h=self.num_heads),
229
+ (k_dca, v_dca))
230
+ context_dca = F.scaled_dot_product_attention(
231
+ q, k_dca, v_dca).transpose(1, 2).reshape(b, s1, -1)
232
+
233
+ context = context + self.dca_weight * context_dca
234
+
235
+ out = self.out_proj(context) # context.reshape - B, L1, -1
236
+
237
+ return out
238
+
239
+
240
+ class Attention(nn.Module):
241
+ """
242
+ We rename some layer names to align with flash attention
243
+ """
244
+
245
+ def __init__(
246
+ self,
247
+ dim,
248
+ num_heads,
249
+ qkv_bias=True,
250
+ qk_norm=False,
251
+ norm_layer=nn.LayerNorm,
252
+ ):
253
+ super().__init__()
254
+ self.dim = dim
255
+ self.num_heads = num_heads
256
+ assert self.dim % num_heads == 0, 'dim should be divisible by num_heads'
257
+ self.head_dim = self.dim // num_heads
258
+ # This assertion is aligned with flash attention
259
+ assert self.head_dim % 8 == 0 and self.head_dim <= 128, "Only support head_dim <= 128 and divisible by 8"
260
+ self.scale = self.head_dim ** -0.5
261
+
262
+ self.to_q = nn.Linear(dim, dim, bias=qkv_bias)
263
+ self.to_k = nn.Linear(dim, dim, bias=qkv_bias)
264
+ self.to_v = nn.Linear(dim, dim, bias=qkv_bias)
265
+ # TODO: eps should be 1 / 65530 if using fp16
266
+ self.q_norm = norm_layer(self.head_dim, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
267
+ self.k_norm = norm_layer(self.head_dim, elementwise_affine=True, eps=1e-6) if qk_norm else nn.Identity()
268
+ self.out_proj = nn.Linear(dim, dim)
269
+
270
+ def forward(self, x):
271
+ B, N, C = x.shape
272
+
273
+ q = self.to_q(x)
274
+ k = self.to_k(x)
275
+ v = self.to_v(x)
276
+
277
+ qkv = torch.cat((q, k, v), dim=-1)
278
+ split_size = qkv.shape[-1] // self.num_heads // 3
279
+ qkv = qkv.view(1, -1, self.num_heads, split_size * 3)
280
+ q, k, v = torch.split(qkv, split_size, dim=-1)
281
+
282
+ q = q.reshape(B, N, self.num_heads, self.head_dim).transpose(1, 2) # [b, h, s, d]
283
+ k = k.reshape(B, N, self.num_heads, self.head_dim).transpose(1, 2) # [b, h, s, d]
284
+ v = v.reshape(B, N, self.num_heads, self.head_dim).transpose(1, 2)
285
+
286
+ q = self.q_norm(q) # [b, h, s, d]
287
+ k = self.k_norm(k) # [b, h, s, d]
288
+
289
+ with torch.backends.cuda.sdp_kernel(
290
+ enable_flash=True,
291
+ enable_math=False,
292
+ enable_mem_efficient=True
293
+ ):
294
+ x = F.scaled_dot_product_attention(q, k, v)
295
+ x = x.transpose(1, 2).reshape(B, N, -1)
296
+
297
+ x = self.out_proj(x)
298
+ return x
299
+
300
+
301
+ class HunYuanDiTBlock(nn.Module):
302
+ def __init__(
303
+ self,
304
+ hidden_size,
305
+ c_emb_size,
306
+ num_heads,
307
+ text_states_dim=1024,
308
+ use_flash_attn=False,
309
+ qk_norm=False,
310
+ norm_layer=nn.LayerNorm,
311
+ qk_norm_layer=nn.RMSNorm,
312
+ with_decoupled_ca=False,
313
+ decoupled_ca_dim=16,
314
+ decoupled_ca_weight=1.0,
315
+ init_scale=1.0,
316
+ qkv_bias=True,
317
+ skip_connection=True,
318
+ timested_modulate=False,
319
+ use_moe: bool = False,
320
+ num_experts: int = 8,
321
+ moe_top_k: int = 2,
322
+ **kwargs,
323
+ ):
324
+ super().__init__()
325
+ self.use_flash_attn = use_flash_attn
326
+ use_ele_affine = True
327
+
328
+ # ========================= Self-Attention =========================
329
+ self.norm1 = norm_layer(hidden_size, elementwise_affine=use_ele_affine, eps=1e-6)
330
+ self.attn1 = Attention(hidden_size, num_heads=num_heads, qkv_bias=qkv_bias, qk_norm=qk_norm,
331
+ norm_layer=qk_norm_layer)
332
+
333
+ # ========================= FFN =========================
334
+ self.norm2 = norm_layer(hidden_size, elementwise_affine=use_ele_affine, eps=1e-6)
335
+
336
+ # ========================= Add =========================
337
+ # Simply use add like SDXL.
338
+ self.timested_modulate = timested_modulate
339
+ if self.timested_modulate:
340
+ self.default_modulation = nn.Sequential(
341
+ nn.SiLU(),
342
+ nn.Linear(c_emb_size, hidden_size, bias=True)
343
+ )
344
+
345
+ # ========================= Cross-Attention =========================
346
+ self.attn2 = CrossAttention(hidden_size, text_states_dim, num_heads=num_heads, qkv_bias=qkv_bias,
347
+ qk_norm=qk_norm, norm_layer=qk_norm_layer,
348
+ with_decoupled_ca=with_decoupled_ca, decoupled_ca_dim=decoupled_ca_dim,
349
+ decoupled_ca_weight=decoupled_ca_weight, init_scale=init_scale,
350
+ )
351
+ self.norm3 = norm_layer(hidden_size, elementwise_affine=True, eps=1e-6)
352
+
353
+ if skip_connection:
354
+ self.skip_norm = norm_layer(hidden_size, elementwise_affine=True, eps=1e-6)
355
+ self.skip_linear = nn.Linear(2 * hidden_size, hidden_size)
356
+ else:
357
+ self.skip_linear = None
358
+
359
+ self.use_moe = use_moe
360
+ if self.use_moe:
361
+ print("using moe")
362
+ self.moe = MoEBlock(
363
+ hidden_size,
364
+ num_experts=num_experts,
365
+ moe_top_k=moe_top_k,
366
+ dropout=0.0,
367
+ activation_fn="gelu",
368
+ final_dropout=False,
369
+ ff_inner_dim=int(hidden_size * 4.0),
370
+ ff_bias=True,
371
+ )
372
+ else:
373
+ self.mlp = MLP(width=hidden_size)
374
+
375
+ def forward(self, x, c=None, text_states=None, skip_value=None):
376
+
377
+ if self.skip_linear is not None:
378
+ cat = torch.cat([skip_value, x], dim=-1)
379
+ x = self.skip_linear(cat)
380
+ x = self.skip_norm(x)
381
+
382
+ # Self-Attention
383
+ if self.timested_modulate:
384
+ shift_msa = self.default_modulation(c).unsqueeze(dim=1)
385
+ x = x + shift_msa
386
+
387
+ attn_out = self.attn1(self.norm1(x))
388
+
389
+ x = x + attn_out
390
+
391
+ # Cross-Attention
392
+ x = x + self.attn2(self.norm2(x), text_states)
393
+
394
+ # FFN Layer
395
+ mlp_inputs = self.norm3(x)
396
+
397
+ if self.use_moe:
398
+ x = x + self.moe(mlp_inputs)
399
+ else:
400
+ x = x + self.mlp(mlp_inputs)
401
+
402
+ return x
403
+
404
+
405
+ class AttentionPool(nn.Module):
406
+ def __init__(self, spacial_dim: int, embed_dim: int, num_heads: int, output_dim: int = None):
407
+ super().__init__()
408
+ self.positional_embedding = nn.Parameter(torch.randn(spacial_dim + 1, embed_dim) / embed_dim ** 0.5)
409
+ self.k_proj = nn.Linear(embed_dim, embed_dim)
410
+ self.q_proj = nn.Linear(embed_dim, embed_dim)
411
+ self.v_proj = nn.Linear(embed_dim, embed_dim)
412
+ self.c_proj = nn.Linear(embed_dim, output_dim or embed_dim)
413
+ self.num_heads = num_heads
414
+
415
+ def forward(self, x, attention_mask=None):
416
+ x = x.permute(1, 0, 2) # NLC -> LNC
417
+ if attention_mask is not None:
418
+ attention_mask = attention_mask.unsqueeze(-1).permute(1, 0, 2)
419
+ global_emb = (x * attention_mask).sum(dim=0) / attention_mask.sum(dim=0)
420
+ x = torch.cat([global_emb[None,], x], dim=0)
421
+
422
+ else:
423
+ x = torch.cat([x.mean(dim=0, keepdim=True), x], dim=0) # (L+1)NC
424
+ x = x + self.positional_embedding[:, None, :].to(x.dtype) # (L+1)NC
425
+ x, _ = F.multi_head_attention_forward(
426
+ query=x[:1], key=x, value=x,
427
+ embed_dim_to_check=x.shape[-1],
428
+ num_heads=self.num_heads,
429
+ q_proj_weight=self.q_proj.weight,
430
+ k_proj_weight=self.k_proj.weight,
431
+ v_proj_weight=self.v_proj.weight,
432
+ in_proj_weight=None,
433
+ in_proj_bias=torch.cat([self.q_proj.bias, self.k_proj.bias, self.v_proj.bias]),
434
+ bias_k=None,
435
+ bias_v=None,
436
+ add_zero_attn=False,
437
+ dropout_p=0,
438
+ out_proj_weight=self.c_proj.weight,
439
+ out_proj_bias=self.c_proj.bias,
440
+ use_separate_proj_weight=True,
441
+ training=self.training,
442
+ need_weights=False
443
+ )
444
+ return x.squeeze(0)
445
+
446
+
447
+ class FinalLayer(nn.Module):
448
+ """
449
+ The final layer of HunYuanDiT.
450
+ """
451
+
452
+ def __init__(self, final_hidden_size, out_channels):
453
+ super().__init__()
454
+ self.final_hidden_size = final_hidden_size
455
+ self.norm_final = nn.LayerNorm(final_hidden_size, elementwise_affine=True, eps=1e-6)
456
+ self.linear = nn.Linear(final_hidden_size, out_channels, bias=True)
457
+
458
+ def forward(self, x):
459
+ x = self.norm_final(x)
460
+ x = x[:, 1:]
461
+ x = self.linear(x)
462
+ return x
463
+
464
+
465
+ class HunYuanDiTPlain(nn.Module):
466
+
467
+ def __init__(
468
+ self,
469
+ input_size=1024,
470
+ in_channels=4,
471
+ hidden_size=1024,
472
+ context_dim=1024,
473
+ depth=24,
474
+ num_heads=16,
475
+ mlp_ratio=4.0,
476
+ norm_type='layer',
477
+ qk_norm_type='rms',
478
+ qk_norm=False,
479
+ text_len=257,
480
+ with_decoupled_ca=False,
481
+ additional_cond_hidden_state=768,
482
+ decoupled_ca_dim=16,
483
+ decoupled_ca_weight=1.0,
484
+ use_pos_emb=False,
485
+ use_attention_pooling=True,
486
+ guidance_cond_proj_dim=None,
487
+ qkv_bias=True,
488
+ num_moe_layers: int = 6,
489
+ num_experts: int = 8,
490
+ moe_top_k: int = 2,
491
+ **kwargs
492
+ ):
493
+ super().__init__()
494
+ self.input_size = input_size
495
+ self.depth = depth
496
+ self.in_channels = in_channels
497
+ self.out_channels = in_channels
498
+ self.num_heads = num_heads
499
+
500
+ self.hidden_size = hidden_size
501
+ self.norm = nn.LayerNorm if norm_type == 'layer' else nn.RMSNorm
502
+ self.qk_norm = nn.RMSNorm if qk_norm_type == 'rms' else nn.LayerNorm
503
+ self.context_dim = context_dim
504
+
505
+ self.with_decoupled_ca = with_decoupled_ca
506
+ self.decoupled_ca_dim = decoupled_ca_dim
507
+ self.decoupled_ca_weight = decoupled_ca_weight
508
+ self.use_pos_emb = use_pos_emb
509
+ self.use_attention_pooling = use_attention_pooling
510
+ self.guidance_cond_proj_dim = guidance_cond_proj_dim
511
+
512
+ self.text_len = text_len
513
+
514
+ self.x_embedder = nn.Linear(in_channels, hidden_size, bias=True)
515
+ self.t_embedder = TimestepEmbedder(hidden_size, hidden_size * 4, cond_proj_dim=guidance_cond_proj_dim)
516
+
517
+ # Will use fixed sin-cos embedding:
518
+ if self.use_pos_emb:
519
+ self.register_buffer("pos_embed", torch.zeros(1, input_size, hidden_size))
520
+ pos = np.arange(self.input_size, dtype=np.float32)
521
+ pos_embed = get_1d_sincos_pos_embed_from_grid(self.pos_embed.shape[-1], pos)
522
+ self.pos_embed.data.copy_(torch.from_numpy(pos_embed).float().unsqueeze(0))
523
+
524
+ self.use_attention_pooling = use_attention_pooling
525
+ if use_attention_pooling:
526
+ self.pooler = AttentionPool(self.text_len, context_dim, num_heads=8, output_dim=1024)
527
+ self.extra_embedder = nn.Sequential(
528
+ nn.Linear(1024, hidden_size * 4),
529
+ nn.SiLU(),
530
+ nn.Linear(hidden_size * 4, hidden_size, bias=True),
531
+ )
532
+
533
+ if with_decoupled_ca:
534
+ self.additional_cond_hidden_state = additional_cond_hidden_state
535
+ self.additional_cond_proj = nn.Sequential(
536
+ nn.Linear(additional_cond_hidden_state, hidden_size * 4),
537
+ nn.SiLU(),
538
+ nn.Linear(hidden_size * 4, 1024, bias=True),
539
+ )
540
+
541
+ # HUnYuanDiT Blocks
542
+ self.blocks = nn.ModuleList([
543
+ HunYuanDiTBlock(hidden_size=hidden_size,
544
+ c_emb_size=hidden_size,
545
+ num_heads=num_heads,
546
+ mlp_ratio=mlp_ratio,
547
+ text_states_dim=context_dim,
548
+ qk_norm=qk_norm,
549
+ norm_layer=self.norm,
550
+ qk_norm_layer=self.qk_norm,
551
+ skip_connection=layer > depth // 2,
552
+ with_decoupled_ca=with_decoupled_ca,
553
+ decoupled_ca_dim=decoupled_ca_dim,
554
+ decoupled_ca_weight=decoupled_ca_weight,
555
+ qkv_bias=qkv_bias,
556
+ use_moe=True if depth - layer <= num_moe_layers else False,
557
+ num_experts=num_experts,
558
+ moe_top_k=moe_top_k
559
+ )
560
+ for layer in range(depth)
561
+ ])
562
+ self.depth = depth
563
+
564
+ self.final_layer = FinalLayer(hidden_size, self.out_channels)
565
+
566
+ def forward(self, x, t, contexts, **kwargs):
567
+ cond = contexts['main']
568
+
569
+ t = self.t_embedder(t, condition=kwargs.get('guidance_cond'))
570
+ x = self.x_embedder(x)
571
+
572
+ if self.use_pos_emb:
573
+ pos_embed = self.pos_embed.to(x.dtype)
574
+ x = x + pos_embed
575
+
576
+ if self.use_attention_pooling:
577
+ extra_vec = self.pooler(cond, None)
578
+ c = t + self.extra_embedder(extra_vec) # [B, D]
579
+ else:
580
+ c = t
581
+
582
+ if self.with_decoupled_ca:
583
+ additional_cond = self.additional_cond_proj(contexts['additional'])
584
+ cond = torch.cat([cond, additional_cond], dim=1)
585
+
586
+ x = torch.cat([c, x], dim=1)
587
+
588
+ skip_value_list = []
589
+ for layer, block in enumerate(self.blocks):
590
+ skip_value = None if layer <= self.depth // 2 else skip_value_list.pop()
591
+ x = block(x, c, cond, skip_value=skip_value)
592
+ if layer < self.depth // 2:
593
+ skip_value_list.append(x)
594
+
595
+ x = self.final_layer(x)
596
+ return x
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/denoisers/moe_layers.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hunyuan 3D is licensed under the TENCENT HUNYUAN NON-COMMERCIAL LICENSE AGREEMENT
2
+ # except for the third-party components listed below.
3
+ # Hunyuan 3D does not impose any additional limitations beyond what is outlined
4
+ # in the repsective licenses of these third-party components.
5
+ # Users must comply with all terms and conditions of original licenses of these third-party
6
+ # components and must ensure that the usage of the third party components adheres to
7
+ # all relevant laws and regulations.
8
+
9
+ # For avoidance of doubts, Hunyuan 3D means the large language models and
10
+ # their software and algorithms, including trained model weights, parameters (including
11
+ # optimizer states), machine-learning model code, inference-enabling code, training-enabling code,
12
+ # fine-tuning enabling code and other elements of the foregoing made publicly available
13
+ # by Tencent in accordance with TENCENT HUNYUAN COMMUNITY LICENSE AGREEMENT.
14
+
15
+ import torch
16
+ import torch.nn as nn
17
+ import numpy as np
18
+ import math
19
+ from timm.models.vision_transformer import PatchEmbed, Attention, Mlp
20
+
21
+ import torch.nn.functional as F
22
+ from diffusers.models.attention import FeedForward
23
+
24
+ class AddAuxiliaryLoss(torch.autograd.Function):
25
+ """
26
+ The trick function of adding auxiliary (aux) loss,
27
+ which includes the gradient of the aux loss during backpropagation.
28
+ """
29
+ @staticmethod
30
+ def forward(ctx, x, loss):
31
+ assert loss.numel() == 1
32
+ ctx.dtype = loss.dtype
33
+ ctx.required_aux_loss = loss.requires_grad
34
+ return x
35
+
36
+ @staticmethod
37
+ def backward(ctx, grad_output):
38
+ grad_loss = None
39
+ if ctx.required_aux_loss:
40
+ grad_loss = torch.ones(1, dtype=ctx.dtype, device=grad_output.device)
41
+ return grad_output, grad_loss
42
+
43
+ class MoEGate(nn.Module):
44
+ def __init__(self, embed_dim, num_experts=16, num_experts_per_tok=2, aux_loss_alpha=0.01):
45
+ super().__init__()
46
+ self.top_k = num_experts_per_tok
47
+ self.n_routed_experts = num_experts
48
+
49
+ self.scoring_func = 'softmax'
50
+ self.alpha = aux_loss_alpha
51
+ self.seq_aux = False
52
+
53
+ # topk selection algorithm
54
+ self.norm_topk_prob = False
55
+ self.gating_dim = embed_dim
56
+ self.weight = nn.Parameter(torch.empty((self.n_routed_experts, self.gating_dim)))
57
+ self.reset_parameters()
58
+
59
+ def reset_parameters(self) -> None:
60
+ import torch.nn.init as init
61
+ init.kaiming_uniform_(self.weight, a=math.sqrt(5))
62
+
63
+ def forward(self, hidden_states):
64
+ bsz, seq_len, h = hidden_states.shape
65
+ # print(bsz, seq_len, h)
66
+ ### compute gating score
67
+ hidden_states = hidden_states.view(-1, h)
68
+ logits = F.linear(hidden_states, self.weight, None)
69
+ if self.scoring_func == 'softmax':
70
+ scores = logits.softmax(dim=-1)
71
+ else:
72
+ raise NotImplementedError(f'insupportable scoring function for MoE gating: {self.scoring_func}')
73
+
74
+ ### select top-k experts
75
+ topk_weight, topk_idx = torch.topk(scores, k=self.top_k, dim=-1, sorted=False)
76
+
77
+ ### norm gate to sum 1
78
+ if self.top_k > 1 and self.norm_topk_prob:
79
+ denominator = topk_weight.sum(dim=-1, keepdim=True) + 1e-20
80
+ topk_weight = topk_weight / denominator
81
+
82
+ ### expert-level computation auxiliary loss
83
+ if self.training and self.alpha > 0.0:
84
+ scores_for_aux = scores
85
+ aux_topk = self.top_k
86
+ # always compute aux loss based on the naive greedy topk method
87
+ topk_idx_for_aux_loss = topk_idx.view(bsz, -1)
88
+ if self.seq_aux:
89
+ scores_for_seq_aux = scores_for_aux.view(bsz, seq_len, -1)
90
+ ce = torch.zeros(bsz, self.n_routed_experts, device=hidden_states.device)
91
+ ce.scatter_add_(
92
+ 1,
93
+ topk_idx_for_aux_loss,
94
+ torch.ones(
95
+ bsz, seq_len * aux_topk,
96
+ device=hidden_states.device
97
+ )
98
+ ).div_(seq_len * aux_topk / self.n_routed_experts)
99
+ aux_loss = (ce * scores_for_seq_aux.mean(dim = 1)).sum(dim = 1).mean()
100
+ aux_loss = aux_loss * self.alpha
101
+ else:
102
+ mask_ce = F.one_hot(topk_idx_for_aux_loss.view(-1),
103
+ num_classes=self.n_routed_experts)
104
+ ce = mask_ce.float().mean(0)
105
+ Pi = scores_for_aux.mean(0)
106
+ fi = ce * self.n_routed_experts
107
+ aux_loss = (Pi * fi).sum() * self.alpha
108
+ else:
109
+ aux_loss = None
110
+ return topk_idx, topk_weight, aux_loss
111
+
112
+ class MoEBlock(nn.Module):
113
+ def __init__(self, dim, num_experts=8, moe_top_k=2,
114
+ activation_fn = "gelu", dropout=0.0, final_dropout = False,
115
+ ff_inner_dim = None, ff_bias = True):
116
+ super().__init__()
117
+ self.moe_top_k = moe_top_k
118
+ self.experts = nn.ModuleList([
119
+ FeedForward(dim,dropout=dropout,
120
+ activation_fn=activation_fn,
121
+ final_dropout=final_dropout,
122
+ inner_dim=ff_inner_dim,
123
+ bias=ff_bias)
124
+ for i in range(num_experts)])
125
+ self.gate = MoEGate(embed_dim=dim, num_experts=num_experts, num_experts_per_tok=moe_top_k)
126
+
127
+ self.shared_experts = FeedForward(dim,dropout=dropout, activation_fn=activation_fn,
128
+ final_dropout=final_dropout, inner_dim=ff_inner_dim,
129
+ bias=ff_bias)
130
+
131
+ def initialize_weight(self):
132
+ pass
133
+
134
+ def forward(self, hidden_states):
135
+ identity = hidden_states
136
+ orig_shape = hidden_states.shape
137
+ topk_idx, topk_weight, aux_loss = self.gate(hidden_states)
138
+
139
+ hidden_states = hidden_states.view(-1, hidden_states.shape[-1])
140
+ flat_topk_idx = topk_idx.view(-1)
141
+ if self.training:
142
+ hidden_states = hidden_states.repeat_interleave(self.moe_top_k, dim=0)
143
+ y = torch.empty_like(hidden_states, dtype=hidden_states.dtype)
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
+ y = (y.view(*topk_weight.shape, -1) * topk_weight.unsqueeze(-1)).sum(dim=1)
148
+ y = y.view(*orig_shape)
149
+ y = AddAuxiliaryLoss.apply(y, aux_loss)
150
+ else:
151
+ y = self.moe_infer(hidden_states, flat_topk_idx, topk_weight.view(-1, 1)).view(*orig_shape)
152
+ y = y + self.shared_experts(identity)
153
+ return y
154
+
155
+
156
+ @torch.no_grad()
157
+ def moe_infer(self, x, flat_expert_indices, flat_expert_weights):
158
+ expert_cache = torch.zeros_like(x)
159
+ idxs = flat_expert_indices.argsort()
160
+ tokens_per_expert = flat_expert_indices.bincount().cpu().numpy().cumsum(0)
161
+ token_idxs = idxs // self.moe_top_k
162
+ for i, end_idx in enumerate(tokens_per_expert):
163
+ start_idx = 0 if i == 0 else tokens_per_expert[i-1]
164
+ if start_idx == end_idx:
165
+ continue
166
+ expert = self.experts[i]
167
+ exp_token_idx = token_idxs[start_idx:end_idx]
168
+ expert_tokens = x[exp_token_idx]
169
+ expert_out = expert(expert_tokens)
170
+ expert_out.mul_(flat_expert_weights[idxs[start_idx:end_idx]])
171
+
172
+ # for fp16 and other dtype
173
+ expert_cache = expert_cache.to(expert_out.dtype)
174
+ expert_cache.scatter_reduce_(0, exp_token_idx.view(-1, 1).repeat(1, x.shape[-1]),
175
+ expert_out,
176
+ reduce='sum')
177
+ return expert_cache
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/flow_matching_sit.py ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from contextlib import contextmanager
3
+ from typing import List, Tuple, Optional, Union
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ from torch.optim import lr_scheduler
8
+ import pytorch_lightning as pl
9
+ from pytorch_lightning.utilities import rank_zero_info
10
+ from pytorch_lightning.utilities import rank_zero_only
11
+
12
+ from ...utils.ema import LitEma
13
+ from ...utils.misc import instantiate_from_config, instantiate_non_trainable_model
14
+
15
+
16
+
17
+ class Diffuser(pl.LightningModule):
18
+ def __init__(
19
+ self,
20
+ *,
21
+ first_stage_config,
22
+ cond_stage_config,
23
+ denoiser_cfg,
24
+ scheduler_cfg,
25
+ optimizer_cfg,
26
+ pipeline_cfg=None,
27
+ image_processor_cfg=None,
28
+ lora_config=None,
29
+ ema_config=None,
30
+ first_stage_key: str = "surface",
31
+ cond_stage_key: str = "image",
32
+ scale_by_std: bool = False,
33
+ z_scale_factor: float = 1.0,
34
+ ckpt_path: Optional[str] = None,
35
+ ignore_keys: Union[Tuple[str], List[str]] = (),
36
+ torch_compile: bool = False,
37
+ ):
38
+ super().__init__()
39
+ self.first_stage_key = first_stage_key
40
+ self.cond_stage_key = cond_stage_key
41
+
42
+ # ========= init optimizer config ========= #
43
+ self.optimizer_cfg = optimizer_cfg
44
+
45
+ # ========= init diffusion scheduler ========= #
46
+ self.scheduler_cfg = scheduler_cfg
47
+ self.sampler = None
48
+ if 'transport' in scheduler_cfg:
49
+ self.transport = instantiate_from_config(scheduler_cfg.transport)
50
+ self.sampler = instantiate_from_config(scheduler_cfg.sampler, transport=self.transport)
51
+ self.sample_fn = self.sampler.sample_ode(**scheduler_cfg.sampler.ode_params)
52
+
53
+ # ========= init the model ========= #
54
+ self.denoiser_cfg = denoiser_cfg
55
+ self.model = instantiate_from_config(denoiser_cfg, device=None, dtype=None)
56
+ self.cond_stage_model = instantiate_from_config(cond_stage_config)
57
+
58
+ self.ckpt_path = ckpt_path
59
+ if ckpt_path is not None:
60
+ self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys)
61
+
62
+ # ========= config lora model ========= #
63
+ if lora_config is not None:
64
+ from peft import LoraConfig, get_peft_model
65
+ loraconfig = LoraConfig(
66
+ r=lora_config.rank,
67
+ lora_alpha=lora_config.rank,
68
+ target_modules=lora_config.get('target_modules')
69
+ )
70
+ self.model = get_peft_model(self.model, loraconfig)
71
+
72
+ # ========= config ema model ========= #
73
+ self.ema_config = ema_config
74
+ if self.ema_config is not None:
75
+ if self.ema_config.ema_model == 'DSEma':
76
+ # from michelangelo.models.modules.ema_deepspeed import DSEma
77
+ from ..utils.ema_deepspeed import DSEma
78
+ self.model_ema = DSEma(self.model, decay=self.ema_config.ema_decay)
79
+ else:
80
+ self.model_ema = LitEma(self.model, decay=self.ema_config.ema_decay)
81
+ #do not initilize EMA weight from ckpt path, since I need to change moe layers
82
+ if ckpt_path is not None:
83
+ self.init_from_ckpt(ckpt_path, ignore_keys=ignore_keys)
84
+ print(f"Keeping EMAs of {len(list(self.model_ema.buffers()))}.")
85
+
86
+ # ========= init vae at last to prevent it is overridden by loaded ckpt ========= #
87
+ self.first_stage_model = instantiate_non_trainable_model(first_stage_config)
88
+
89
+ self.scale_by_std = scale_by_std
90
+ if scale_by_std:
91
+ self.register_buffer("z_scale_factor", torch.tensor(z_scale_factor))
92
+ else:
93
+ self.z_scale_factor = z_scale_factor
94
+
95
+ # ========= init pipeline for inference ========= #
96
+ self.image_processor_cfg = image_processor_cfg
97
+ self.image_processor = None
98
+ if self.image_processor_cfg is not None:
99
+ self.image_processor = instantiate_from_config(self.image_processor_cfg)
100
+ self.pipeline_cfg = pipeline_cfg
101
+ from ...schedulers import FlowMatchEulerDiscreteScheduler
102
+ scheduler = FlowMatchEulerDiscreteScheduler(num_train_timesteps=1000)
103
+ self.pipeline = instantiate_from_config(
104
+ pipeline_cfg,
105
+ vae=self.first_stage_model,
106
+ model=self.model,
107
+ scheduler=scheduler, # self.sampler,
108
+ conditioner=self.cond_stage_model,
109
+ image_processor=self.image_processor,
110
+ )
111
+
112
+ # ========= torch compile to accelerate ========= #
113
+ self.torch_compile = torch_compile
114
+ if self.torch_compile:
115
+ torch.nn.Module.compile(self.model)
116
+ torch.nn.Module.compile(self.first_stage_model)
117
+ torch.nn.Module.compile(self.cond_stage_model)
118
+ print(f'*' * 100)
119
+ print(f'Compile model for acceleration')
120
+ print(f'*' * 100)
121
+
122
+ @contextmanager
123
+ def ema_scope(self, context=None):
124
+ if self.ema_config is not None and self.ema_config.get('ema_inference', False):
125
+ self.model_ema.store(self.model)
126
+ self.model_ema.copy_to(self.model)
127
+ if context is not None:
128
+ print(f"{context}: Switched to EMA weights")
129
+ try:
130
+ yield None
131
+ finally:
132
+ if self.ema_config is not None and self.ema_config.get('ema_inference', False):
133
+ self.model_ema.restore(self.model)
134
+ if context is not None:
135
+ print(f"{context}: Restored training weights")
136
+
137
+ def init_from_ckpt(self, path, ignore_keys=()):
138
+ ckpt = torch.load(path, map_location="cpu")
139
+ if 'state_dict' not in ckpt:
140
+ # deepspeed ckpt
141
+ state_dict = {}
142
+ for k in ckpt.keys():
143
+ new_k = k.replace('_forward_module.', '')
144
+ state_dict[new_k] = ckpt[k]
145
+ else:
146
+ state_dict = ckpt["state_dict"]
147
+
148
+ keys = list(state_dict.keys())
149
+ for k in keys:
150
+ for ik in ignore_keys:
151
+ if ik in k:
152
+ print("Deleting key {} from state_dict.".format(k))
153
+ del state_dict[k]
154
+
155
+ missing, unexpected = self.load_state_dict(state_dict, strict=False)
156
+ print(f"Restored from {path} with {len(missing)} missing and {len(unexpected)} unexpected keys")
157
+ if len(missing) > 0:
158
+ print(f"Missing Keys: {missing}")
159
+ print(f"Unexpected Keys: {unexpected}")
160
+
161
+ def on_load_checkpoint(self, checkpoint):
162
+ """
163
+ The pt_model is trained separately, so we already have access to its
164
+ checkpoint and load it separately with `self.set_pt_model`.
165
+
166
+ However, the PL Trainer is strict about
167
+ checkpoint loading (not configurable), so it expects the loaded state_dict
168
+ to match exactly the keys in the model state_dict.
169
+
170
+ So, when loading the checkpoint, before matching keys, we add all pt_model keys
171
+ from self.state_dict() to the checkpoint state dict, so that they match
172
+ """
173
+ for key in self.state_dict().keys():
174
+ if key.startswith("model_ema") and key not in checkpoint["state_dict"]:
175
+ checkpoint["state_dict"][key] = self.state_dict()[key]
176
+
177
+ def configure_optimizers(self) -> Tuple[List, List]:
178
+ lr = self.learning_rate
179
+
180
+ params_list = []
181
+ trainable_parameters = list(self.model.parameters())
182
+ params_list.append({'params': trainable_parameters, 'lr': lr})
183
+
184
+ no_decay = ['bias', 'norm.weight', 'norm.bias', 'norm1.weight', 'norm1.bias', 'norm2.weight', 'norm2.bias']
185
+
186
+
187
+ if self.optimizer_cfg.get('train_image_encoder', False):
188
+ image_encoder_parameters = list(self.cond_stage_model.named_parameters())
189
+ image_encoder_parameters_decay = [param for name, param in image_encoder_parameters if
190
+ not any((no_decay_name in name) for no_decay_name in no_decay)]
191
+ image_encoder_parameters_nodecay = [param for name, param in image_encoder_parameters if
192
+ any((no_decay_name in name) for no_decay_name in no_decay)]
193
+ # filter trainable params
194
+ image_encoder_parameters_decay = [param for param in image_encoder_parameters_decay if
195
+ param.requires_grad]
196
+ image_encoder_parameters_nodecay = [param for param in image_encoder_parameters_nodecay if
197
+ param.requires_grad]
198
+
199
+ print(f"Image Encoder Params: {len(image_encoder_parameters_decay)} decay, ")
200
+ print(f"Image Encoder Params: {len(image_encoder_parameters_nodecay)} nodecay, ")
201
+
202
+ image_encoder_lr = self.optimizer_cfg['image_encoder_lr']
203
+ image_encoder_lr_multiply = self.optimizer_cfg.get('image_encoder_lr_multiply', 1.0)
204
+ image_encoder_lr = image_encoder_lr if image_encoder_lr is not None else lr * image_encoder_lr_multiply
205
+ params_list.append(
206
+ {'params': image_encoder_parameters_decay, 'lr': image_encoder_lr,
207
+ 'weight_decay': 0.05})
208
+ params_list.append(
209
+ {'params': image_encoder_parameters_nodecay, 'lr': image_encoder_lr,
210
+ 'weight_decay': 0.})
211
+
212
+ optimizer = instantiate_from_config(self.optimizer_cfg.optimizer, params=params_list, lr=lr)
213
+ if hasattr(self.optimizer_cfg, 'scheduler'):
214
+ scheduler_func = instantiate_from_config(
215
+ self.optimizer_cfg.scheduler,
216
+ max_decay_steps=self.trainer.max_steps,
217
+ lr_max=lr
218
+ )
219
+ scheduler = {
220
+ "scheduler": lr_scheduler.LambdaLR(optimizer, lr_lambda=scheduler_func.schedule),
221
+ "interval": "step",
222
+ "frequency": 1
223
+ }
224
+ schedulers = [scheduler]
225
+ else:
226
+ schedulers = []
227
+ optimizers = [optimizer]
228
+
229
+ return optimizers, schedulers
230
+
231
+ @rank_zero_only
232
+ @torch.no_grad()
233
+ def on_train_batch_start(self, batch, batch_idx):
234
+ # only for very first batch
235
+ if self.scale_by_std and self.current_epoch == 0 and self.global_step == 0 \
236
+ and batch_idx == 0 and self.ckpt_path is None:
237
+ # set rescale weight to 1./std of encodings
238
+ print("### USING STD-RESCALING ###")
239
+
240
+ z_q = self.encode_first_stage(batch[self.first_stage_key])
241
+ z = z_q.detach()
242
+
243
+ del self.z_scale_factor
244
+ self.register_buffer("z_scale_factor", 1. / z.flatten().std())
245
+ print(f"setting self.z_scale_factor to {self.z_scale_factor}")
246
+
247
+ print("### USING STD-RESCALING ###")
248
+
249
+ def on_train_batch_end(self, *args, **kwargs):
250
+ if self.ema_config is not None:
251
+ self.model_ema(self.model)
252
+
253
+ def on_train_epoch_start(self) -> None:
254
+ pl.seed_everything(self.trainer.global_rank)
255
+
256
+ def forward(self, batch):
257
+ with torch.autocast(device_type="cuda", dtype=torch.bfloat16): #float32 for text
258
+ contexts = self.cond_stage_model(image=batch.get('image'), text=batch.get('text'), mask=batch.get('mask'))
259
+ # t5_text = contexts['t5_text']['prompt_embeds']
260
+ # nan_count = torch.isnan(t5_text).sum()
261
+ # if nan_count > 0:
262
+ # print("t5_text has %d NaN values"%(nan_count))
263
+ with torch.autocast(device_type="cuda", dtype=torch.float16):
264
+ with torch.no_grad():
265
+ latents = self.first_stage_model.encode(batch[self.first_stage_key], sample_posterior=True)
266
+ latents = self.z_scale_factor * latents
267
+ # print(latents.shape)
268
+
269
+ # check vae encode and decode is ok? answer is ok !
270
+ # import time
271
+ # from hy3dshape.pipelines import export_to_trimesh
272
+ # latents = 1. / self.z_scale_factor * latents
273
+ # latents = self.first_stage_model(latents)
274
+ # outputs = self.first_stage_model.latents2mesh(
275
+ # latents,
276
+ # bounds=1.01,
277
+ # mc_level=0.0,
278
+ # num_chunks=20000,
279
+ # octree_resolution=256,
280
+ # mc_algo='mc',
281
+ # enable_pbar=True
282
+ # )
283
+ # mesh = export_to_trimesh(outputs)
284
+ # if isinstance(mesh, list):
285
+ # for midx, m in enumerate(mesh):
286
+ # m.export(f"check_{midx}_{time.time()}.glb")
287
+ # else:
288
+ # mesh.export(f"check_{time.time()}.glb")
289
+
290
+ with torch.autocast(device_type="cuda", dtype=torch.bfloat16):
291
+ loss = self.transport.training_losses(self.model, latents, dict(contexts=contexts))["loss"].mean()
292
+ return loss
293
+
294
+ def training_step(self, batch, batch_idx, optimizer_idx=0):
295
+ loss = self.forward(batch)
296
+ split = 'train'
297
+ loss_dict = {
298
+ f"{split}/simple": loss.detach(),
299
+ f"{split}/total_loss": loss.detach(),
300
+ f"{split}/lr_abs": self.optimizers().param_groups[0]['lr'],
301
+ }
302
+ self.log_dict(loss_dict, prog_bar=True, logger=True, sync_dist=False, rank_zero_only=True)
303
+
304
+ return loss
305
+
306
+ def validation_step(self, batch, batch_idx, optimizer_idx=0):
307
+ loss = self.forward(batch)
308
+ split = 'val'
309
+ loss_dict = {
310
+ f"{split}/simple": loss.detach(),
311
+ f"{split}/total_loss": loss.detach(),
312
+ f"{split}/lr_abs": self.optimizers().param_groups[0]['lr'],
313
+ }
314
+ self.log_dict(loss_dict, prog_bar=True, logger=True, sync_dist=False, rank_zero_only=True)
315
+
316
+ return loss
317
+
318
+ @torch.no_grad()
319
+ def sample(self, batch, output_type='trimesh', **kwargs):
320
+ self.cond_stage_model.disable_drop = True
321
+
322
+ generator = torch.Generator().manual_seed(0)
323
+
324
+ with self.ema_scope("Sample"):
325
+ with torch.amp.autocast(device_type='cuda'):
326
+ try:
327
+ self.pipeline.device = self.device
328
+ self.pipeline.dtype = self.dtype
329
+ print("### USING PIPELINE ###")
330
+ print(f'device: {self.device} dtype : {self.dtype}')
331
+ additional_params = {'output_type':output_type}
332
+
333
+ image = batch.get("image", None)
334
+ mask = batch.get('mask', None)
335
+
336
+ # if not isinstance(image, torch.Tensor): print(image.shape)
337
+ # if isinstance(mask, torch.Tensor): print(mask.shape)
338
+
339
+ outputs = self.pipeline(image=image,
340
+ mask=mask,
341
+ generator=generator,
342
+ **additional_params)
343
+
344
+ except Exception as e:
345
+ import traceback
346
+ traceback.print_exc()
347
+ print(f"Unexpected {e=}, {type(e)=}")
348
+ with open("error.txt", "a") as f:
349
+ f.write(str(e))
350
+ f.write(traceback.format_exc())
351
+ f.write("\n")
352
+ outputs = [None]
353
+ self.cond_stage_model.disable_drop = False
354
+ return [outputs]
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/__init__.py ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file includes code derived from the SiT project (https://github.com/willisma/SiT),
2
+ # which is licensed under the MIT License.
3
+ #
4
+ # MIT License
5
+ #
6
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be included in all
16
+ # copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # SOFTWARE.
25
+
26
+ from .transport import Transport, ModelType, WeightType, PathType, Sampler
27
+
28
+
29
+ def create_transport(
30
+ path_type='Linear',
31
+ prediction="velocity",
32
+ loss_weight=None,
33
+ train_eps=None,
34
+ sample_eps=None,
35
+ train_sample_type="uniform",
36
+ mean = 0.0,
37
+ std = 1.0,
38
+ shift_scale = 1.0,
39
+ ):
40
+ """function for creating Transport object
41
+ **Note**: model prediction defaults to velocity
42
+ Args:
43
+ - path_type: type of path to use; default to linear
44
+ - learn_score: set model prediction to score
45
+ - learn_noise: set model prediction to noise
46
+ - velocity_weighted: weight loss by velocity weight
47
+ - likelihood_weighted: weight loss by likelihood weight
48
+ - train_eps: small epsilon for avoiding instability during training
49
+ - sample_eps: small epsilon for avoiding instability during sampling
50
+ """
51
+
52
+ if prediction == "noise":
53
+ model_type = ModelType.NOISE
54
+ elif prediction == "score":
55
+ model_type = ModelType.SCORE
56
+ else:
57
+ model_type = ModelType.VELOCITY
58
+
59
+ if loss_weight == "velocity":
60
+ loss_type = WeightType.VELOCITY
61
+ elif loss_weight == "likelihood":
62
+ loss_type = WeightType.LIKELIHOOD
63
+ else:
64
+ loss_type = WeightType.NONE
65
+
66
+ path_choice = {
67
+ "Linear": PathType.LINEAR,
68
+ "GVP": PathType.GVP,
69
+ "VP": PathType.VP,
70
+ }
71
+
72
+ path_type = path_choice[path_type]
73
+
74
+ if (path_type in [PathType.VP]):
75
+ train_eps = 1e-5 if train_eps is None else train_eps
76
+ sample_eps = 1e-3 if train_eps is None else sample_eps
77
+ elif (path_type in [PathType.GVP, PathType.LINEAR] and model_type != ModelType.VELOCITY):
78
+ train_eps = 1e-3 if train_eps is None else train_eps
79
+ sample_eps = 1e-3 if train_eps is None else sample_eps
80
+ else: # velocity & [GVP, LINEAR] is stable everywhere
81
+ train_eps = 0
82
+ sample_eps = 0
83
+
84
+ # create flow state
85
+ state = Transport(
86
+ model_type=model_type,
87
+ path_type=path_type,
88
+ loss_type=loss_type,
89
+ train_eps=train_eps,
90
+ sample_eps=sample_eps,
91
+ train_sample_type=train_sample_type,
92
+ mean=mean,
93
+ std=std,
94
+ shift_scale =shift_scale,
95
+ )
96
+
97
+ return state
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/integrators.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file includes code derived from the SiT project (https://github.com/willisma/SiT),
2
+ # which is licensed under the MIT License.
3
+ #
4
+ # MIT License
5
+ #
6
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be included in all
16
+ # copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # SOFTWARE.
25
+
26
+ import numpy as np
27
+ import torch as th
28
+ import torch.nn as nn
29
+ from torchdiffeq import odeint
30
+ from functools import partial
31
+ from tqdm import tqdm
32
+
33
+ class sde:
34
+ """SDE solver class"""
35
+ def __init__(
36
+ self,
37
+ drift,
38
+ diffusion,
39
+ *,
40
+ t0,
41
+ t1,
42
+ num_steps,
43
+ sampler_type,
44
+ ):
45
+ assert t0 < t1, "SDE sampler has to be in forward time"
46
+
47
+ self.num_timesteps = num_steps
48
+ self.t = th.linspace(t0, t1, num_steps)
49
+ self.dt = self.t[1] - self.t[0]
50
+ self.drift = drift
51
+ self.diffusion = diffusion
52
+ self.sampler_type = sampler_type
53
+
54
+ def __Euler_Maruyama_step(self, x, mean_x, t, model, **model_kwargs):
55
+ w_cur = th.randn(x.size()).to(x)
56
+ t = th.ones(x.size(0)).to(x) * t
57
+ dw = w_cur * th.sqrt(self.dt)
58
+ drift = self.drift(x, t, model, **model_kwargs)
59
+ diffusion = self.diffusion(x, t)
60
+ mean_x = x + drift * self.dt
61
+ x = mean_x + th.sqrt(2 * diffusion) * dw
62
+ return x, mean_x
63
+
64
+ def __Heun_step(self, x, _, t, model, **model_kwargs):
65
+ w_cur = th.randn(x.size()).to(x)
66
+ dw = w_cur * th.sqrt(self.dt)
67
+ t_cur = th.ones(x.size(0)).to(x) * t
68
+ diffusion = self.diffusion(x, t_cur)
69
+ xhat = x + th.sqrt(2 * diffusion) * dw
70
+ K1 = self.drift(xhat, t_cur, model, **model_kwargs)
71
+ xp = xhat + self.dt * K1
72
+ K2 = self.drift(xp, t_cur + self.dt, model, **model_kwargs)
73
+ return xhat + 0.5 * self.dt * (K1 + K2), xhat # at last time point we do not perform the heun step
74
+
75
+ def __forward_fn(self):
76
+ """TODO: generalize here by adding all private functions ending with steps to it"""
77
+ sampler_dict = {
78
+ "Euler": self.__Euler_Maruyama_step,
79
+ "Heun": self.__Heun_step,
80
+ }
81
+
82
+ try:
83
+ sampler = sampler_dict[self.sampler_type]
84
+ except:
85
+ raise NotImplementedError("Smapler type not implemented.")
86
+
87
+ return sampler
88
+
89
+ def sample(self, init, model, **model_kwargs):
90
+ """forward loop of sde"""
91
+ x = init
92
+ mean_x = init
93
+ samples = []
94
+ sampler = self.__forward_fn()
95
+ for ti in self.t[:-1]:
96
+ with th.no_grad():
97
+ x, mean_x = sampler(x, mean_x, ti, model, **model_kwargs)
98
+ samples.append(x)
99
+
100
+ return samples
101
+
102
+ class ode:
103
+ """ODE solver class"""
104
+ def __init__(
105
+ self,
106
+ drift,
107
+ *,
108
+ t0,
109
+ t1,
110
+ sampler_type,
111
+ num_steps,
112
+ atol,
113
+ rtol,
114
+ ):
115
+ assert t0 < t1, "ODE sampler has to be in forward time"
116
+
117
+ self.drift = drift
118
+ self.t = th.linspace(t0, t1, num_steps)
119
+ self.atol = atol
120
+ self.rtol = rtol
121
+ self.sampler_type = sampler_type
122
+
123
+ def sample(self, x, model, **model_kwargs):
124
+
125
+ device = x[0].device if isinstance(x, tuple) else x.device
126
+ def _fn(t, x):
127
+ t = th.ones(x[0].size(0)).to(device) * t if isinstance(x, tuple) else th.ones(x.size(0)).to(device) * t
128
+ model_output = self.drift(x, t, model, **model_kwargs)
129
+ return model_output
130
+
131
+ t = self.t.to(device)
132
+ atol = [self.atol] * len(x) if isinstance(x, tuple) else [self.atol]
133
+ rtol = [self.rtol] * len(x) if isinstance(x, tuple) else [self.rtol]
134
+ samples = odeint(
135
+ _fn,
136
+ x,
137
+ t,
138
+ method=self.sampler_type,
139
+ atol=atol,
140
+ rtol=rtol
141
+ )
142
+ return samples
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/path.py ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file includes code derived from the SiT project (https://github.com/willisma/SiT),
2
+ # which is licensed under the MIT License.
3
+ #
4
+ # MIT License
5
+ #
6
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be included in all
16
+ # copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # SOFTWARE.
25
+
26
+ import torch as th
27
+ import numpy as np
28
+ from functools import partial
29
+
30
+ def expand_t_like_x(t, x):
31
+ """Function to reshape time t to broadcastable dimension of x
32
+ Args:
33
+ t: [batch_dim,], time vector
34
+ x: [batch_dim,...], data point
35
+ """
36
+ dims = [1] * (len(x.size()) - 1)
37
+ t = t.view(t.size(0), *dims)
38
+ return t
39
+
40
+
41
+ #################### Coupling Plans ####################
42
+
43
+ class ICPlan:
44
+ """Linear Coupling Plan"""
45
+ def __init__(self, sigma=0.0):
46
+ self.sigma = sigma
47
+
48
+ def compute_alpha_t(self, t):
49
+ """Compute the data coefficient along the path"""
50
+ return t, 1
51
+
52
+ def compute_sigma_t(self, t):
53
+ """Compute the noise coefficient along the path"""
54
+ return 1 - t, -1
55
+
56
+ def compute_d_alpha_alpha_ratio_t(self, t):
57
+ """Compute the ratio between d_alpha and alpha"""
58
+ return 1 / t
59
+
60
+ def compute_drift(self, x, t):
61
+ """We always output sde according to score parametrization; """
62
+ t = expand_t_like_x(t, x)
63
+ alpha_ratio = self.compute_d_alpha_alpha_ratio_t(t)
64
+ sigma_t, d_sigma_t = self.compute_sigma_t(t)
65
+ drift = alpha_ratio * x
66
+ diffusion = alpha_ratio * (sigma_t ** 2) - sigma_t * d_sigma_t
67
+
68
+ return -drift, diffusion
69
+
70
+ def compute_diffusion(self, x, t, form="constant", norm=1.0):
71
+ """Compute the diffusion term of the SDE
72
+ Args:
73
+ x: [batch_dim, ...], data point
74
+ t: [batch_dim,], time vector
75
+ form: str, form of the diffusion term
76
+ norm: float, norm of the diffusion term
77
+ """
78
+ t = expand_t_like_x(t, x)
79
+ choices = {
80
+ "constant": norm,
81
+ "SBDM": norm * self.compute_drift(x, t)[1],
82
+ "sigma": norm * self.compute_sigma_t(t)[0],
83
+ "linear": norm * (1 - t),
84
+ "decreasing": 0.25 * (norm * th.cos(np.pi * t) + 1) ** 2,
85
+ "inccreasing-decreasing": norm * th.sin(np.pi * t) ** 2,
86
+ }
87
+
88
+ try:
89
+ diffusion = choices[form]
90
+ except KeyError:
91
+ raise NotImplementedError(f"Diffusion form {form} not implemented")
92
+
93
+ return diffusion
94
+
95
+ def get_score_from_velocity(self, velocity, x, t):
96
+ """Wrapper function: transfrom velocity prediction model to score
97
+ Args:
98
+ velocity: [batch_dim, ...] shaped tensor; velocity model output
99
+ x: [batch_dim, ...] shaped tensor; x_t data point
100
+ t: [batch_dim,] time tensor
101
+ """
102
+ t = expand_t_like_x(t, x)
103
+ alpha_t, d_alpha_t = self.compute_alpha_t(t)
104
+ sigma_t, d_sigma_t = self.compute_sigma_t(t)
105
+ mean = x
106
+ reverse_alpha_ratio = alpha_t / d_alpha_t
107
+ var = sigma_t**2 - reverse_alpha_ratio * d_sigma_t * sigma_t
108
+ score = (reverse_alpha_ratio * velocity - mean) / var
109
+ return score
110
+
111
+ def get_noise_from_velocity(self, velocity, x, t):
112
+ """Wrapper function: transfrom velocity prediction model to denoiser
113
+ Args:
114
+ velocity: [batch_dim, ...] shaped tensor; velocity model output
115
+ x: [batch_dim, ...] shaped tensor; x_t data point
116
+ t: [batch_dim,] time tensor
117
+ """
118
+ t = expand_t_like_x(t, x)
119
+ alpha_t, d_alpha_t = self.compute_alpha_t(t)
120
+ sigma_t, d_sigma_t = self.compute_sigma_t(t)
121
+ mean = x
122
+ reverse_alpha_ratio = alpha_t / d_alpha_t
123
+ var = reverse_alpha_ratio * d_sigma_t - sigma_t
124
+ noise = (reverse_alpha_ratio * velocity - mean) / var
125
+ return noise
126
+
127
+ def get_velocity_from_score(self, score, x, t):
128
+ """Wrapper function: transfrom score prediction model to velocity
129
+ Args:
130
+ score: [batch_dim, ...] shaped tensor; score model output
131
+ x: [batch_dim, ...] shaped tensor; x_t data point
132
+ t: [batch_dim,] time tensor
133
+ """
134
+ t = expand_t_like_x(t, x)
135
+ drift, var = self.compute_drift(x, t)
136
+ velocity = var * score - drift
137
+ return velocity
138
+
139
+ def compute_mu_t(self, t, x0, x1):
140
+ """Compute the mean of time-dependent density p_t"""
141
+ t = expand_t_like_x(t, x1)
142
+ alpha_t, _ = self.compute_alpha_t(t)
143
+ sigma_t, _ = self.compute_sigma_t(t)
144
+ # t*x1 + (1-t)*x0 ; t=0 x0; t=1 x1
145
+ return alpha_t * x1 + sigma_t * x0
146
+
147
+ def compute_xt(self, t, x0, x1):
148
+ """Sample xt from time-dependent density p_t; rng is required"""
149
+ xt = self.compute_mu_t(t, x0, x1)
150
+ return xt
151
+
152
+ def compute_ut(self, t, x0, x1, xt):
153
+ """Compute the vector field corresponding to p_t"""
154
+ t = expand_t_like_x(t, x1)
155
+ _, d_alpha_t = self.compute_alpha_t(t)
156
+ _, d_sigma_t = self.compute_sigma_t(t)
157
+ return d_alpha_t * x1 + d_sigma_t * x0
158
+
159
+ def plan(self, t, x0, x1):
160
+ xt = self.compute_xt(t, x0, x1)
161
+ ut = self.compute_ut(t, x0, x1, xt)
162
+ return t, xt, ut
163
+
164
+
165
+ class VPCPlan(ICPlan):
166
+ """class for VP path flow matching"""
167
+
168
+ def __init__(self, sigma_min=0.1, sigma_max=20.0):
169
+ self.sigma_min = sigma_min
170
+ self.sigma_max = sigma_max
171
+ self.log_mean_coeff = lambda t: -0.25 * ((1 - t) ** 2) * \
172
+ (self.sigma_max - self.sigma_min) - 0.5 * (1 - t) * self.sigma_min
173
+ self.d_log_mean_coeff = lambda t: 0.5 * (1 - t) * \
174
+ (self.sigma_max - self.sigma_min) + 0.5 * self.sigma_min
175
+
176
+
177
+ def compute_alpha_t(self, t):
178
+ """Compute coefficient of x1"""
179
+ alpha_t = self.log_mean_coeff(t)
180
+ alpha_t = th.exp(alpha_t)
181
+ d_alpha_t = alpha_t * self.d_log_mean_coeff(t)
182
+ return alpha_t, d_alpha_t
183
+
184
+ def compute_sigma_t(self, t):
185
+ """Compute coefficient of x0"""
186
+ p_sigma_t = 2 * self.log_mean_coeff(t)
187
+ sigma_t = th.sqrt(1 - th.exp(p_sigma_t))
188
+ d_sigma_t = th.exp(p_sigma_t) * (2 * self.d_log_mean_coeff(t)) / (-2 * sigma_t)
189
+ return sigma_t, d_sigma_t
190
+
191
+ def compute_d_alpha_alpha_ratio_t(self, t):
192
+ """Special purposed function for computing numerical stabled d_alpha_t / alpha_t"""
193
+ return self.d_log_mean_coeff(t)
194
+
195
+ def compute_drift(self, x, t):
196
+ """Compute the drift term of the SDE"""
197
+ t = expand_t_like_x(t, x)
198
+ beta_t = self.sigma_min + (1 - t) * (self.sigma_max - self.sigma_min)
199
+ return -0.5 * beta_t * x, beta_t / 2
200
+
201
+
202
+ class GVPCPlan(ICPlan):
203
+ def __init__(self, sigma=0.0):
204
+ super().__init__(sigma)
205
+
206
+ def compute_alpha_t(self, t):
207
+ """Compute coefficient of x1"""
208
+ alpha_t = th.sin(t * np.pi / 2)
209
+ d_alpha_t = np.pi / 2 * th.cos(t * np.pi / 2)
210
+ return alpha_t, d_alpha_t
211
+
212
+ def compute_sigma_t(self, t):
213
+ """Compute coefficient of x0"""
214
+ sigma_t = th.cos(t * np.pi / 2)
215
+ d_sigma_t = -np.pi / 2 * th.sin(t * np.pi / 2)
216
+ return sigma_t, d_sigma_t
217
+
218
+ def compute_d_alpha_alpha_ratio_t(self, t):
219
+ """Special purposed function for computing numerical stabled d_alpha_t / alpha_t"""
220
+ return np.pi / (2 * th.tan(t * np.pi / 2))
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/transport.py ADDED
@@ -0,0 +1,534 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file includes code derived from the SiT project (https://github.com/willisma/SiT),
2
+ # which is licensed under the MIT License.
3
+ #
4
+ # MIT License
5
+ #
6
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be included in all
16
+ # copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # SOFTWARE.
25
+
26
+ import torch as th
27
+ import numpy as np
28
+ import logging
29
+
30
+ import enum
31
+
32
+ from . import path
33
+ from .utils import EasyDict, log_state, mean_flat
34
+ from .integrators import ode, sde
35
+
36
+
37
+ class ModelType(enum.Enum):
38
+ """
39
+ Which type of output the model predicts.
40
+ """
41
+
42
+ NOISE = enum.auto() # the model predicts epsilon
43
+ SCORE = enum.auto() # the model predicts \nabla \log p(x)
44
+ VELOCITY = enum.auto() # the model predicts v(x)
45
+
46
+
47
+ class PathType(enum.Enum):
48
+ """
49
+ Which type of path to use.
50
+ """
51
+
52
+ LINEAR = enum.auto()
53
+ GVP = enum.auto()
54
+ VP = enum.auto()
55
+
56
+
57
+ class WeightType(enum.Enum):
58
+ """
59
+ Which type of weighting to use.
60
+ """
61
+
62
+ NONE = enum.auto()
63
+ VELOCITY = enum.auto()
64
+ LIKELIHOOD = enum.auto()
65
+
66
+
67
+ class Transport:
68
+
69
+ def __init__(
70
+ self,
71
+ *,
72
+ model_type,
73
+ path_type,
74
+ loss_type,
75
+ train_eps,
76
+ sample_eps,
77
+ train_sample_type = "uniform",
78
+ **kwargs,
79
+ ):
80
+ path_options = {
81
+ PathType.LINEAR: path.ICPlan,
82
+ PathType.GVP: path.GVPCPlan,
83
+ PathType.VP: path.VPCPlan,
84
+ }
85
+
86
+ self.loss_type = loss_type
87
+ self.model_type = model_type
88
+ self.path_sampler = path_options[path_type]()
89
+ self.train_eps = train_eps
90
+ self.sample_eps = sample_eps
91
+ self.train_sample_type = train_sample_type
92
+ if self.train_sample_type == "logit_normal":
93
+ self.mean = kwargs['mean']
94
+ self.std = kwargs['std']
95
+ self.shift_scale = kwargs['shift_scale']
96
+ print(f"using logit normal sample, shift scale is {self.shift_scale}")
97
+
98
+ def prior_logp(self, z):
99
+ '''
100
+ Standard multivariate normal prior
101
+ Assume z is batched
102
+ '''
103
+ shape = th.tensor(z.size())
104
+ N = th.prod(shape[1:])
105
+ _fn = lambda x: -N / 2. * np.log(2 * np.pi) - th.sum(x ** 2) / 2.
106
+ return th.vmap(_fn)(z)
107
+
108
+ def check_interval(
109
+ self,
110
+ train_eps,
111
+ sample_eps,
112
+ *,
113
+ diffusion_form="SBDM",
114
+ sde=False,
115
+ reverse=False,
116
+ eval=False,
117
+ last_step_size=0.0,
118
+ ):
119
+ t0 = 0
120
+ t1 = 1
121
+ eps = train_eps if not eval else sample_eps
122
+ if (type(self.path_sampler) in [path.VPCPlan]):
123
+
124
+ t1 = 1 - eps if (not sde or last_step_size == 0) else 1 - last_step_size
125
+
126
+ elif (type(self.path_sampler) in [path.ICPlan, path.GVPCPlan]) \
127
+ and (
128
+ self.model_type != ModelType.VELOCITY or sde): # avoid numerical issue by taking a first semi-implicit step
129
+
130
+ t0 = eps if (diffusion_form == "SBDM" and sde) or self.model_type != ModelType.VELOCITY else 0
131
+ t1 = 1 - eps if (not sde or last_step_size == 0) else 1 - last_step_size
132
+
133
+ if reverse:
134
+ t0, t1 = 1 - t0, 1 - t1
135
+
136
+ return t0, t1
137
+
138
+ def sample(self, x1):
139
+ """Sampling x0 & t based on shape of x1 (if needed)
140
+ Args:
141
+ x1 - data point; [batch, *dim]
142
+ """
143
+
144
+ x0 = th.randn_like(x1)
145
+ if self.train_sample_type=="uniform":
146
+ t0, t1 = self.check_interval(self.train_eps, self.sample_eps)
147
+ t = th.rand((x1.shape[0],)) * (t1 - t0) + t0
148
+ t = t.to(x1)
149
+ elif self.train_sample_type=="logit_normal":
150
+ t = th.randn((x1.shape[0],)) * self.std + self.mean
151
+ t = t.to(x1)
152
+ t = 1/(1+th.exp(-t))
153
+
154
+ t = np.sqrt(self.shift_scale)*t/(1+(np.sqrt(self.shift_scale)-1)*t)
155
+
156
+ return t, x0, x1
157
+
158
+ def training_losses(
159
+ self,
160
+ model,
161
+ x1,
162
+ model_kwargs=None
163
+ ):
164
+ """Loss for training the score model
165
+ Args:
166
+ - model: backbone model; could be score, noise, or velocity
167
+ - x1: datapoint
168
+ - model_kwargs: additional arguments for the model
169
+ """
170
+ if model_kwargs == None:
171
+ model_kwargs = {}
172
+
173
+ t, x0, x1 = self.sample(x1)
174
+ t, xt, ut = self.path_sampler.plan(t, x0, x1)
175
+ model_output = model(xt, t, **model_kwargs)
176
+ B, *_, C = xt.shape
177
+ assert model_output.size() == (B, *xt.size()[1:-1], C)
178
+
179
+ terms = {}
180
+ terms['pred'] = model_output
181
+ if self.model_type == ModelType.VELOCITY:
182
+ terms['loss'] = mean_flat(((model_output - ut) ** 2))
183
+ else:
184
+ _, drift_var = self.path_sampler.compute_drift(xt, t)
185
+ sigma_t, _ = self.path_sampler.compute_sigma_t(path.expand_t_like_x(t, xt))
186
+ if self.loss_type in [WeightType.VELOCITY]:
187
+ weight = (drift_var / sigma_t) ** 2
188
+ elif self.loss_type in [WeightType.LIKELIHOOD]:
189
+ weight = drift_var / (sigma_t ** 2)
190
+ elif self.loss_type in [WeightType.NONE]:
191
+ weight = 1
192
+ else:
193
+ raise NotImplementedError()
194
+
195
+ if self.model_type == ModelType.NOISE:
196
+ terms['loss'] = mean_flat(weight * ((model_output - x0) ** 2))
197
+ else:
198
+ terms['loss'] = mean_flat(weight * ((model_output * sigma_t + x0) ** 2))
199
+
200
+ return terms
201
+
202
+ def get_drift(
203
+ self
204
+ ):
205
+ """member function for obtaining the drift of the probability flow ODE"""
206
+
207
+ def score_ode(x, t, model, **model_kwargs):
208
+ drift_mean, drift_var = self.path_sampler.compute_drift(x, t)
209
+ model_output = model(x, t, **model_kwargs)
210
+ return (-drift_mean + drift_var * model_output) # by change of variable
211
+
212
+ def noise_ode(x, t, model, **model_kwargs):
213
+ drift_mean, drift_var = self.path_sampler.compute_drift(x, t)
214
+ sigma_t, _ = self.path_sampler.compute_sigma_t(path.expand_t_like_x(t, x))
215
+ model_output = model(x, t, **model_kwargs)
216
+ score = model_output / -sigma_t
217
+ return (-drift_mean + drift_var * score)
218
+
219
+ def velocity_ode(x, t, model, **model_kwargs):
220
+ model_output = model(x, t, **model_kwargs)
221
+ return model_output
222
+
223
+ if self.model_type == ModelType.NOISE:
224
+ drift_fn = noise_ode
225
+ elif self.model_type == ModelType.SCORE:
226
+ drift_fn = score_ode
227
+ else:
228
+ drift_fn = velocity_ode
229
+
230
+ def body_fn(x, t, model, **model_kwargs):
231
+ model_output = drift_fn(x, t, model, **model_kwargs)
232
+ assert model_output.shape == x.shape, "Output shape from ODE solver must match input shape"
233
+ return model_output
234
+
235
+ return body_fn
236
+
237
+ def get_score(
238
+ self,
239
+ ):
240
+ """member function for obtaining score of
241
+ x_t = alpha_t * x + sigma_t * eps"""
242
+ if self.model_type == ModelType.NOISE:
243
+ score_fn = lambda x, t, model, **kwargs: model(x, t, **kwargs) / - \
244
+ self.path_sampler.compute_sigma_t(path.expand_t_like_x(t, x))[0]
245
+ elif self.model_type == ModelType.SCORE:
246
+ score_fn = lambda x, t, model, **kwagrs: model(x, t, **kwagrs)
247
+ elif self.model_type == ModelType.VELOCITY:
248
+ score_fn = lambda x, t, model, **kwargs: self.path_sampler.get_score_from_velocity(model(x, t, **kwargs), x,
249
+ t)
250
+ else:
251
+ raise NotImplementedError()
252
+
253
+ return score_fn
254
+
255
+
256
+ class Sampler:
257
+ """Sampler class for the transport model"""
258
+
259
+ def __init__(
260
+ self,
261
+ transport,
262
+ ):
263
+ """Constructor for a general sampler; supporting different sampling methods
264
+ Args:
265
+ - transport: an tranport object specify model prediction & interpolant type
266
+ """
267
+
268
+ self.transport = transport
269
+ self.drift = self.transport.get_drift()
270
+ self.score = self.transport.get_score()
271
+
272
+ def __get_sde_diffusion_and_drift(
273
+ self,
274
+ *,
275
+ diffusion_form="SBDM",
276
+ diffusion_norm=1.0,
277
+ ):
278
+
279
+ def diffusion_fn(x, t):
280
+ diffusion = self.transport.path_sampler.compute_diffusion(x, t, form=diffusion_form, norm=diffusion_norm)
281
+ return diffusion
282
+
283
+ sde_drift = \
284
+ lambda x, t, model, **kwargs: \
285
+ self.drift(x, t, model, **kwargs) + diffusion_fn(x, t) * self.score(x, t, model, **kwargs)
286
+
287
+ sde_diffusion = diffusion_fn
288
+
289
+ return sde_drift, sde_diffusion
290
+
291
+ def __get_last_step(
292
+ self,
293
+ sde_drift,
294
+ *,
295
+ last_step,
296
+ last_step_size,
297
+ ):
298
+ """Get the last step function of the SDE solver"""
299
+
300
+ if last_step is None:
301
+ last_step_fn = \
302
+ lambda x, t, model, **model_kwargs: \
303
+ x
304
+ elif last_step == "Mean":
305
+ last_step_fn = \
306
+ lambda x, t, model, **model_kwargs: \
307
+ x + sde_drift(x, t, model, **model_kwargs) * last_step_size
308
+ elif last_step == "Tweedie":
309
+ alpha = self.transport.path_sampler.compute_alpha_t # simple aliasing; the original name was too long
310
+ sigma = self.transport.path_sampler.compute_sigma_t
311
+ last_step_fn = \
312
+ lambda x, t, model, **model_kwargs: \
313
+ x / alpha(t)[0][0] + (sigma(t)[0][0] ** 2) / alpha(t)[0][0] * self.score(x, t, model,
314
+ **model_kwargs)
315
+ elif last_step == "Euler":
316
+ last_step_fn = \
317
+ lambda x, t, model, **model_kwargs: \
318
+ x + self.drift(x, t, model, **model_kwargs) * last_step_size
319
+ else:
320
+ raise NotImplementedError()
321
+
322
+ return last_step_fn
323
+
324
+ def sample_sde(
325
+ self,
326
+ *,
327
+ sampling_method="Euler",
328
+ diffusion_form="SBDM",
329
+ diffusion_norm=1.0,
330
+ last_step="Mean",
331
+ last_step_size=0.04,
332
+ num_steps=250,
333
+ ):
334
+ """returns a sampling function with given SDE settings
335
+ Args:
336
+ - sampling_method: type of sampler used in solving the SDE; default to be Euler-Maruyama
337
+ - diffusion_form: function form of diffusion coefficient; default to be matching SBDM
338
+ - diffusion_norm: function magnitude of diffusion coefficient; default to 1
339
+ - last_step: type of the last step; default to identity
340
+ - last_step_size: size of the last step; default to match the stride of 250 steps over [0,1]
341
+ - num_steps: total integration step of SDE
342
+ """
343
+
344
+ if last_step is None:
345
+ last_step_size = 0.0
346
+
347
+ sde_drift, sde_diffusion = self.__get_sde_diffusion_and_drift(
348
+ diffusion_form=diffusion_form,
349
+ diffusion_norm=diffusion_norm,
350
+ )
351
+
352
+ t0, t1 = self.transport.check_interval(
353
+ self.transport.train_eps,
354
+ self.transport.sample_eps,
355
+ diffusion_form=diffusion_form,
356
+ sde=True,
357
+ eval=True,
358
+ reverse=False,
359
+ last_step_size=last_step_size,
360
+ )
361
+
362
+ _sde = sde(
363
+ sde_drift,
364
+ sde_diffusion,
365
+ t0=t0,
366
+ t1=t1,
367
+ num_steps=num_steps,
368
+ sampler_type=sampling_method
369
+ )
370
+
371
+ last_step_fn = self.__get_last_step(sde_drift, last_step=last_step, last_step_size=last_step_size)
372
+
373
+ def _sample(init, model, **model_kwargs):
374
+ xs = _sde.sample(init, model, **model_kwargs)
375
+ ts = th.ones(init.size(0), device=init.device) * t1
376
+ x = last_step_fn(xs[-1], ts, model, **model_kwargs)
377
+ xs.append(x)
378
+
379
+ assert len(xs) == num_steps, "Samples does not match the number of steps"
380
+
381
+ return xs
382
+
383
+ return _sample
384
+
385
+ def sample_ode(
386
+ self,
387
+ *,
388
+ sampling_method="dopri5",
389
+ num_steps=50,
390
+ atol=1e-6,
391
+ rtol=1e-3,
392
+ reverse=False,
393
+ ):
394
+ """returns a sampling function with given ODE settings
395
+ Args:
396
+ - sampling_method: type of sampler used in solving the ODE; default to be Dopri5
397
+ - num_steps:
398
+ - fixed solver (Euler, Heun): the actual number of integration steps performed
399
+ - adaptive solver (Dopri5): the number of datapoints saved during integration; produced by interpolation
400
+ - atol: absolute error tolerance for the solver
401
+ - rtol: relative error tolerance for the solver
402
+ - reverse: whether solving the ODE in reverse (data to noise); default to False
403
+ """
404
+ if reverse:
405
+ drift = lambda x, t, model, **kwargs: self.drift(x, th.ones_like(t) * (1 - t), model, **kwargs)
406
+ else:
407
+ drift = self.drift
408
+
409
+ t0, t1 = self.transport.check_interval(
410
+ self.transport.train_eps,
411
+ self.transport.sample_eps,
412
+ sde=False,
413
+ eval=True,
414
+ reverse=reverse,
415
+ last_step_size=0.0,
416
+ )
417
+
418
+ _ode = ode(
419
+ drift=drift,
420
+ t0=t0,
421
+ t1=t1,
422
+ sampler_type=sampling_method,
423
+ num_steps=num_steps,
424
+ atol=atol,
425
+ rtol=rtol,
426
+ )
427
+
428
+ return _ode.sample
429
+
430
+ def sample_ode_intermediate(
431
+ self,
432
+ *,
433
+ sampling_method="dopri5",
434
+ num_steps=50,
435
+ atol=1e-6,
436
+ rtol=1e-3,
437
+ t=0.5,
438
+ reverse=False,
439
+ ):
440
+ """returns a sampling function with given ODE settings
441
+ Args:
442
+ - sampling_method: type of sampler used in solving the ODE; default to be Dopri5
443
+ - num_steps:
444
+ - fixed solver (Euler, Heun): the actual number of integration steps performed
445
+ - adaptive solver (Dopri5): the number of datapoints saved during integration; produced by interpolation
446
+ - atol: absolute error tolerance for the solver
447
+ - rtol: relative error tolerance for the solver
448
+ - reverse: whether solving the ODE in reverse (data to noise); default to False
449
+ """
450
+ if reverse:
451
+ drift = lambda x, t, model, **kwargs: self.drift(x, th.ones_like(t) * (1 - t), model, **kwargs)
452
+ else:
453
+ drift = self.drift
454
+
455
+ t0, t1 = self.transport.check_interval(
456
+ self.transport.train_eps,
457
+ self.transport.sample_eps,
458
+ sde=False,
459
+ eval=True,
460
+ reverse=reverse,
461
+ last_step_size=0.0,
462
+ )
463
+
464
+ _ode = ode(
465
+ drift=drift,
466
+ t0=t,
467
+ t1=t1,
468
+ sampler_type=sampling_method,
469
+ num_steps=num_steps,
470
+ atol=atol,
471
+ rtol=rtol,
472
+ )
473
+
474
+ return _ode.sample
475
+
476
+ def sample_ode_likelihood(
477
+ self,
478
+ *,
479
+ sampling_method="dopri5",
480
+ num_steps=50,
481
+ atol=1e-6,
482
+ rtol=1e-3,
483
+ ):
484
+
485
+ """returns a sampling function for calculating likelihood with given ODE settings
486
+ Args:
487
+ - sampling_method: type of sampler used in solving the ODE; default to be Dopri5
488
+ - num_steps:
489
+ - fixed solver (Euler, Heun): the actual number of integration steps performed
490
+ - adaptive solver (Dopri5): the number of datapoints saved during integration; produced by interpolation
491
+ - atol: absolute error tolerance for the solver
492
+ - rtol: relative error tolerance for the solver
493
+ """
494
+
495
+ def _likelihood_drift(x, t, model, **model_kwargs):
496
+ x, _ = x
497
+ eps = th.randint(2, x.size(), dtype=th.float, device=x.device) * 2 - 1
498
+ t = th.ones_like(t) * (1 - t)
499
+ with th.enable_grad():
500
+ x.requires_grad = True
501
+ grad = th.autograd.grad(th.sum(self.drift(x, t, model, **model_kwargs) * eps), x)[0]
502
+ logp_grad = th.sum(grad * eps, dim=tuple(range(1, len(x.size()))))
503
+ drift = self.drift(x, t, model, **model_kwargs)
504
+ return (-drift, logp_grad)
505
+
506
+ t0, t1 = self.transport.check_interval(
507
+ self.transport.train_eps,
508
+ self.transport.sample_eps,
509
+ sde=False,
510
+ eval=True,
511
+ reverse=False,
512
+ last_step_size=0.0,
513
+ )
514
+
515
+ _ode = ode(
516
+ drift=_likelihood_drift,
517
+ t0=t0,
518
+ t1=t1,
519
+ sampler_type=sampling_method,
520
+ num_steps=num_steps,
521
+ atol=atol,
522
+ rtol=rtol,
523
+ )
524
+
525
+ def _sample_fn(x, model, **model_kwargs):
526
+ init_logp = th.zeros(x.size(0)).to(x)
527
+ input = (x, init_logp)
528
+ drift, delta_logp = _ode.sample(input, model, **model_kwargs)
529
+ drift, delta_logp = drift[-1], delta_logp[-1]
530
+ prior_logp = self.transport.prior_logp(drift)
531
+ logp = prior_logp - delta_logp
532
+ return logp, drift
533
+
534
+ return _sample_fn
Hunyuan3D-2.1/hy3dshape/hy3dshape/models/diffusion/transport/utils.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file includes code derived from the SiT project (https://github.com/willisma/SiT),
2
+ # which is licensed under the MIT License.
3
+ #
4
+ # MIT License
5
+ #
6
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be included in all
16
+ # copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # SOFTWARE.
25
+
26
+ import torch as th
27
+
28
+ class EasyDict:
29
+
30
+ def __init__(self, sub_dict):
31
+ for k, v in sub_dict.items():
32
+ setattr(self, k, v)
33
+
34
+ def __getitem__(self, key):
35
+ return getattr(self, key)
36
+
37
+ def mean_flat(x):
38
+ """
39
+ Take the mean over all non-batch dimensions.
40
+ """
41
+ return th.mean(x, dim=list(range(1, len(x.size()))))
42
+
43
+ def log_state(state):
44
+ result = []
45
+
46
+ sorted_state = dict(sorted(state.items()))
47
+ for key, value in sorted_state.items():
48
+ # Check if the value is an instance of a class
49
+ if "<object" in str(value) or "object at" in str(value):
50
+ result.append(f"{key}: [{value.__class__.__name__}]")
51
+ else:
52
+ result.append(f"{key}: {value}")
53
+
54
+ return '\n'.join(result)
TRELLIS.2/trellis2/models/__init__.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import importlib
2
+
3
+ __attributes = {
4
+ # Sparse Structure
5
+ 'SparseStructureEncoder': 'sparse_structure_vae',
6
+ 'SparseStructureDecoder': 'sparse_structure_vae',
7
+ 'SparseStructureFlowModel': 'sparse_structure_flow',
8
+
9
+ # SLat Generation
10
+ 'SLatFlowModel': 'structured_latent_flow',
11
+ 'ElasticSLatFlowModel': 'structured_latent_flow',
12
+
13
+ # SC-VAEs
14
+ 'SparseUnetVaeEncoder': 'sc_vaes.sparse_unet_vae',
15
+ 'SparseUnetVaeDecoder': 'sc_vaes.sparse_unet_vae',
16
+ 'FlexiDualGridVaeEncoder': 'sc_vaes.fdg_vae',
17
+ 'FlexiDualGridVaeDecoder': 'sc_vaes.fdg_vae'
18
+ }
19
+
20
+ __submodules = []
21
+
22
+ __all__ = list(__attributes.keys()) + __submodules
23
+
24
+ def __getattr__(name):
25
+ if name not in globals():
26
+ if name in __attributes:
27
+ module_name = __attributes[name]
28
+ module = importlib.import_module(f".{module_name}", __name__)
29
+ globals()[name] = getattr(module, name)
30
+ elif name in __submodules:
31
+ module = importlib.import_module(f".{name}", __name__)
32
+ globals()[name] = module
33
+ else:
34
+ raise AttributeError(f"module {__name__} has no attribute {name}")
35
+ return globals()[name]
36
+
37
+
38
+ def from_pretrained(path: str, **kwargs):
39
+ """
40
+ Load a model from a pretrained checkpoint.
41
+
42
+ Args:
43
+ path: The path to the checkpoint. Can be either local path or a Hugging Face model name.
44
+ NOTE: config file and model file should take the name f'{path}.json' and f'{path}.safetensors' respectively.
45
+ **kwargs: Additional arguments for the model constructor.
46
+ """
47
+ import os
48
+ import json
49
+ from safetensors.torch import load_file
50
+ is_local = os.path.exists(f"{path}.json") and os.path.exists(f"{path}.safetensors")
51
+
52
+ if is_local:
53
+ config_file = f"{path}.json"
54
+ model_file = f"{path}.safetensors"
55
+ else:
56
+ from huggingface_hub import hf_hub_download
57
+ path_parts = path.split('/')
58
+ repo_id = f'{path_parts[0]}/{path_parts[1]}'
59
+ model_name = '/'.join(path_parts[2:])
60
+ config_file = hf_hub_download(repo_id, f"{model_name}.json")
61
+ model_file = hf_hub_download(repo_id, f"{model_name}.safetensors")
62
+
63
+ with open(config_file, 'r') as f:
64
+ config = json.load(f)
65
+ model = __getattr__(config['name'])(**config['args'], **kwargs)
66
+ model.load_state_dict(load_file(model_file), strict=False)
67
+
68
+ return model
69
+
70
+
71
+ # For Pylance
72
+ if __name__ == '__main__':
73
+ from .sparse_structure_vae import SparseStructureEncoder, SparseStructureDecoder
74
+ from .sparse_structure_flow import SparseStructureFlowModel
75
+ from .structured_latent_flow import SLatFlowModel, ElasticSLatFlowModel
76
+
77
+ from .sc_vaes.sparse_unet_vae import SparseUnetVaeEncoder, SparseUnetVaeDecoder
78
+ from .sc_vaes.fdg_vae import FlexiDualGridVaeEncoder, FlexiDualGridVaeDecoder
TRELLIS.2/trellis2/models/sc_vaes/fdg_vae.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import *
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+ from ...modules import sparse as sp
6
+ from .sparse_unet_vae import (
7
+ SparseResBlock3d,
8
+ SparseConvNeXtBlock3d,
9
+
10
+ SparseResBlockDownsample3d,
11
+ SparseResBlockUpsample3d,
12
+ SparseResBlockS2C3d,
13
+ SparseResBlockC2S3d,
14
+ )
15
+ from .sparse_unet_vae import (
16
+ SparseUnetVaeEncoder,
17
+ SparseUnetVaeDecoder,
18
+ )
19
+ from ...representations import Mesh
20
+ from o_voxel.convert import flexible_dual_grid_to_mesh
21
+
22
+
23
+ class FlexiDualGridVaeEncoder(SparseUnetVaeEncoder):
24
+ def __init__(
25
+ self,
26
+ model_channels: List[int],
27
+ latent_channels: int,
28
+ num_blocks: List[int],
29
+ block_type: List[str],
30
+ down_block_type: List[str],
31
+ block_args: List[Dict[str, Any]],
32
+ use_fp16: bool = False,
33
+ ):
34
+ super().__init__(
35
+ 6,
36
+ model_channels,
37
+ latent_channels,
38
+ num_blocks,
39
+ block_type,
40
+ down_block_type,
41
+ block_args,
42
+ use_fp16,
43
+ )
44
+
45
+ def forward(self, vertices: sp.SparseTensor, intersected: sp.SparseTensor, sample_posterior=False, return_raw=False):
46
+ x = vertices.replace(torch.cat([
47
+ vertices.feats - 0.5,
48
+ intersected.feats.float() - 0.5,
49
+ ], dim=1))
50
+ return super().forward(x, sample_posterior, return_raw)
51
+
52
+
53
+ class FlexiDualGridVaeDecoder(SparseUnetVaeDecoder):
54
+ def __init__(
55
+ self,
56
+ resolution: int,
57
+ model_channels: List[int],
58
+ latent_channels: int,
59
+ num_blocks: List[int],
60
+ block_type: List[str],
61
+ up_block_type: List[str],
62
+ block_args: List[Dict[str, Any]],
63
+ voxel_margin: float = 0.5,
64
+ use_fp16: bool = False,
65
+ ):
66
+ self.resolution = resolution
67
+ self.voxel_margin = voxel_margin
68
+
69
+ super().__init__(
70
+ 7,
71
+ model_channels,
72
+ latent_channels,
73
+ num_blocks,
74
+ block_type,
75
+ up_block_type,
76
+ block_args,
77
+ use_fp16,
78
+ )
79
+
80
+ def set_resolution(self, resolution: int) -> None:
81
+ self.resolution = resolution
82
+
83
+ def forward(self, x: sp.SparseTensor, gt_intersected: sp.SparseTensor = None, **kwargs):
84
+ decoded = super().forward(x, **kwargs)
85
+ if self.training:
86
+ h, subs_gt, subs = decoded
87
+ vertices = h.replace((1 + 2 * self.voxel_margin) * F.sigmoid(h.feats[..., 0:3]) - self.voxel_margin)
88
+ intersected_logits = h.replace(h.feats[..., 3:6])
89
+ quad_lerp = h.replace(F.softplus(h.feats[..., 6:7]))
90
+ mesh = [Mesh(flexible_dual_grid_to_mesh(
91
+ h.coords[:, 1:], v.feats, i.feats, q.feats,
92
+ aabb=[[-0.5, -0.5, -0.5], [0.5, 0.5, 0.5]],
93
+ grid_size=self.resolution,
94
+ train=True
95
+ )) for v, i, q in zip(vertices, gt_intersected, quad_lerp)]
96
+ return mesh, vertices, intersected_logits, subs_gt, subs
97
+ else:
98
+ out_list = list(decoded) if isinstance(decoded, tuple) else [decoded]
99
+ h = out_list[0]
100
+ vertices = h.replace((1 + 2 * self.voxel_margin) * F.sigmoid(h.feats[..., 0:3]) - self.voxel_margin)
101
+ intersected = h.replace(h.feats[..., 3:6] > 0)
102
+ quad_lerp = h.replace(F.softplus(h.feats[..., 6:7]))
103
+ mesh = [Mesh(*flexible_dual_grid_to_mesh(
104
+ h.coords[:, 1:], v.feats, i.feats, q.feats,
105
+ aabb=[[-0.5, -0.5, -0.5], [0.5, 0.5, 0.5]],
106
+ grid_size=self.resolution,
107
+ train=False
108
+ )) for v, i, q in zip(vertices, intersected, quad_lerp)]
109
+ out_list[0] = mesh
110
+ return out_list[0] if len(out_list) == 1 else tuple(out_list)
TRELLIS.2/trellis2/models/sc_vaes/sparse_unet_vae.py ADDED
@@ -0,0 +1,522 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import *
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+ import torch.utils.checkpoint
6
+ from ...modules.utils import convert_module_to_f16, convert_module_to_f32, zero_module
7
+ from ...modules import sparse as sp
8
+ from ...modules.norm import LayerNorm32
9
+
10
+
11
+ class SparseResBlock3d(nn.Module):
12
+ def __init__(
13
+ self,
14
+ channels: int,
15
+ out_channels: Optional[int] = None,
16
+ downsample: bool = False,
17
+ upsample: bool = False,
18
+ resample_mode: Literal['nearest', 'spatial2channel'] = 'nearest',
19
+ use_checkpoint: bool = False,
20
+ ):
21
+ super().__init__()
22
+ self.channels = channels
23
+ self.out_channels = out_channels or channels
24
+ self.downsample = downsample
25
+ self.upsample = upsample
26
+ self.resample_mode = resample_mode
27
+ self.use_checkpoint = use_checkpoint
28
+
29
+ assert not (downsample and upsample), "Cannot downsample and upsample at the same time"
30
+
31
+ self.norm1 = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
32
+ self.norm2 = LayerNorm32(self.out_channels, elementwise_affine=False, eps=1e-6)
33
+ if resample_mode == 'nearest':
34
+ self.conv1 = sp.SparseConv3d(channels, self.out_channels, 3)
35
+ elif resample_mode =='spatial2channel' and not self.downsample:
36
+ self.conv1 = sp.SparseConv3d(channels, self.out_channels * 8, 3)
37
+ elif resample_mode =='spatial2channel' and self.downsample:
38
+ self.conv1 = sp.SparseConv3d(channels, self.out_channels // 8, 3)
39
+ self.conv2 = zero_module(sp.SparseConv3d(self.out_channels, self.out_channels, 3))
40
+ if resample_mode == 'nearest':
41
+ self.skip_connection = sp.SparseLinear(channels, self.out_channels) if channels != self.out_channels else nn.Identity()
42
+ elif resample_mode =='spatial2channel' and self.downsample:
43
+ self.skip_connection = lambda x: x.replace(x.feats.reshape(x.feats.shape[0], out_channels, channels * 8 // out_channels).mean(dim=-1))
44
+ elif resample_mode =='spatial2channel' and not self.downsample:
45
+ self.skip_connection = lambda x: x.replace(x.feats.repeat_interleave(out_channels // (channels // 8), dim=1))
46
+ self.updown = None
47
+ if self.downsample:
48
+ if resample_mode == 'nearest':
49
+ self.updown = sp.SparseDownsample(2)
50
+ elif resample_mode =='spatial2channel':
51
+ self.updown = sp.SparseSpatial2Channel(2)
52
+ elif self.upsample:
53
+ self.to_subdiv = sp.SparseLinear(channels, 8)
54
+ if resample_mode == 'nearest':
55
+ self.updown = sp.SparseUpsample(2)
56
+ elif resample_mode =='spatial2channel':
57
+ self.updown = sp.SparseChannel2Spatial(2)
58
+
59
+ def _updown(self, x: sp.SparseTensor, subdiv: sp.SparseTensor = None) -> sp.SparseTensor:
60
+ if self.downsample:
61
+ x = self.updown(x)
62
+ elif self.upsample:
63
+ x = self.updown(x, subdiv.replace(subdiv.feats > 0))
64
+ return x
65
+
66
+ def _forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
67
+ subdiv = None
68
+ if self.upsample:
69
+ subdiv = self.to_subdiv(x)
70
+ h = x.replace(self.norm1(x.feats))
71
+ h = h.replace(F.silu(h.feats))
72
+ if self.resample_mode == 'spatial2channel':
73
+ h = self.conv1(h)
74
+ h = self._updown(h, subdiv)
75
+ x = self._updown(x, subdiv)
76
+ if self.resample_mode == 'nearest':
77
+ h = self.conv1(h)
78
+ h = h.replace(self.norm2(h.feats))
79
+ h = h.replace(F.silu(h.feats))
80
+ h = self.conv2(h)
81
+ h = h + self.skip_connection(x)
82
+ if self.upsample:
83
+ return h, subdiv
84
+ return h
85
+
86
+ def forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
87
+ if self.use_checkpoint:
88
+ return torch.utils.checkpoint.checkpoint(self._forward, x, use_reentrant=False)
89
+ else:
90
+ return self._forward(x)
91
+
92
+
93
+ class SparseResBlockDownsample3d(nn.Module):
94
+ def __init__(
95
+ self,
96
+ channels: int,
97
+ out_channels: Optional[int] = None,
98
+ use_checkpoint: bool = False,
99
+ ):
100
+ super().__init__()
101
+ self.channels = channels
102
+ self.out_channels = out_channels or channels
103
+ self.use_checkpoint = use_checkpoint
104
+
105
+ self.norm1 = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
106
+ self.norm2 = LayerNorm32(self.out_channels, elementwise_affine=False, eps=1e-6)
107
+ self.conv1 = sp.SparseConv3d(channels, self.out_channels, 3)
108
+ self.conv2 = zero_module(sp.SparseConv3d(self.out_channels, self.out_channels, 3))
109
+ self.skip_connection = sp.SparseLinear(channels, self.out_channels) if channels != self.out_channels else nn.Identity()
110
+ self.updown = sp.SparseDownsample(2)
111
+
112
+ def _forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
113
+ h = x.replace(self.norm1(x.feats))
114
+ h = h.replace(F.silu(h.feats))
115
+ h = self.updown(h)
116
+ x = self.updown(x)
117
+ h = self.conv1(h)
118
+ h = h.replace(self.norm2(h.feats))
119
+ h = h.replace(F.silu(h.feats))
120
+ h = self.conv2(h)
121
+ h = h + self.skip_connection(x)
122
+ return h
123
+
124
+ def forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
125
+ if self.use_checkpoint:
126
+ return torch.utils.checkpoint.checkpoint(self._forward, x, use_reentrant=False)
127
+ else:
128
+ return self._forward(x)
129
+
130
+
131
+ class SparseResBlockUpsample3d(nn.Module):
132
+ def __init__(
133
+ self,
134
+ channels: int,
135
+ out_channels: Optional[int] = None,
136
+ use_checkpoint: bool = False,
137
+ pred_subdiv: bool = True,
138
+ ):
139
+ super().__init__()
140
+ self.channels = channels
141
+ self.out_channels = out_channels or channels
142
+ self.use_checkpoint = use_checkpoint
143
+ self.pred_subdiv = pred_subdiv
144
+
145
+ self.norm1 = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
146
+ self.norm2 = LayerNorm32(self.out_channels, elementwise_affine=False, eps=1e-6)
147
+ self.conv1 = sp.SparseConv3d(channels, self.out_channels, 3)
148
+ self.conv2 = zero_module(sp.SparseConv3d(self.out_channels, self.out_channels, 3))
149
+ self.skip_connection = sp.SparseLinear(channels, self.out_channels) if channels != self.out_channels else nn.Identity()
150
+ if self.pred_subdiv:
151
+ self.to_subdiv = sp.SparseLinear(channels, 8)
152
+ self.updown = sp.SparseUpsample(2)
153
+
154
+ def _forward(self, x: sp.SparseTensor, subdiv: sp.SparseTensor = None) -> sp.SparseTensor:
155
+ if self.pred_subdiv:
156
+ subdiv = self.to_subdiv(x)
157
+ h = x.replace(self.norm1(x.feats))
158
+ h = h.replace(F.silu(h.feats))
159
+ subdiv_binarized = subdiv.replace(subdiv.feats > 0) if subdiv is not None else None
160
+ h = self.updown(h, subdiv_binarized)
161
+ x = self.updown(x, subdiv_binarized)
162
+ h = self.conv1(h)
163
+ h = h.replace(self.norm2(h.feats))
164
+ h = h.replace(F.silu(h.feats))
165
+ h = self.conv2(h)
166
+ h = h + self.skip_connection(x)
167
+ if self.pred_subdiv:
168
+ return h, subdiv
169
+ else:
170
+ return h
171
+
172
+ def forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
173
+ if self.use_checkpoint:
174
+ return torch.utils.checkpoint.checkpoint(self._forward, x, use_reentrant=False)
175
+ else:
176
+ return self._forward(x)
177
+
178
+
179
+ class SparseResBlockS2C3d(nn.Module):
180
+ def __init__(
181
+ self,
182
+ channels: int,
183
+ out_channels: Optional[int] = None,
184
+ use_checkpoint: bool = False,
185
+ ):
186
+ super().__init__()
187
+ self.channels = channels
188
+ self.out_channels = out_channels or channels
189
+ self.use_checkpoint = use_checkpoint
190
+
191
+ self.norm1 = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
192
+ self.norm2 = LayerNorm32(self.out_channels, elementwise_affine=False, eps=1e-6)
193
+ self.conv1 = sp.SparseConv3d(channels, self.out_channels // 8, 3)
194
+ self.conv2 = zero_module(sp.SparseConv3d(self.out_channels, self.out_channels, 3))
195
+ self.skip_connection = lambda x: x.replace(x.feats.reshape(x.feats.shape[0], out_channels, channels * 8 // out_channels).mean(dim=-1))
196
+ self.updown = sp.SparseSpatial2Channel(2)
197
+
198
+ def _forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
199
+ h = x.replace(self.norm1(x.feats))
200
+ h = h.replace(F.silu(h.feats))
201
+ h = self.conv1(h)
202
+ h = self.updown(h)
203
+ x = self.updown(x)
204
+ h = h.replace(self.norm2(h.feats))
205
+ h = h.replace(F.silu(h.feats))
206
+ h = self.conv2(h)
207
+ h = h + self.skip_connection(x)
208
+ return h
209
+
210
+ def forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
211
+ if self.use_checkpoint:
212
+ return torch.utils.checkpoint.checkpoint(self._forward, x, use_reentrant=False)
213
+ else:
214
+ return self._forward(x)
215
+
216
+
217
+ class SparseResBlockC2S3d(nn.Module):
218
+ def __init__(
219
+ self,
220
+ channels: int,
221
+ out_channels: Optional[int] = None,
222
+ use_checkpoint: bool = False,
223
+ pred_subdiv: bool = True,
224
+ ):
225
+ super().__init__()
226
+ self.channels = channels
227
+ self.out_channels = out_channels or channels
228
+ self.use_checkpoint = use_checkpoint
229
+ self.pred_subdiv = pred_subdiv
230
+
231
+ self.norm1 = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
232
+ self.norm2 = LayerNorm32(self.out_channels, elementwise_affine=False, eps=1e-6)
233
+ self.conv1 = sp.SparseConv3d(channels, self.out_channels * 8, 3)
234
+ self.conv2 = zero_module(sp.SparseConv3d(self.out_channels, self.out_channels, 3))
235
+ self.skip_connection = lambda x: x.replace(x.feats.repeat_interleave(out_channels // (channels // 8), dim=1))
236
+ if pred_subdiv:
237
+ self.to_subdiv = sp.SparseLinear(channels, 8)
238
+ self.updown = sp.SparseChannel2Spatial(2)
239
+
240
+ def _forward(self, x: sp.SparseTensor, subdiv: sp.SparseTensor = None) -> sp.SparseTensor:
241
+ if self.pred_subdiv:
242
+ subdiv = self.to_subdiv(x)
243
+ h = x.replace(self.norm1(x.feats))
244
+ h = h.replace(F.silu(h.feats))
245
+ h = self.conv1(h)
246
+ subdiv_binarized = subdiv.replace(subdiv.feats > 0) if subdiv is not None else None
247
+ h = self.updown(h, subdiv_binarized)
248
+ x = self.updown(x, subdiv_binarized)
249
+ h = h.replace(self.norm2(h.feats))
250
+ h = h.replace(F.silu(h.feats))
251
+ h = self.conv2(h)
252
+ h = h + self.skip_connection(x)
253
+ if self.pred_subdiv:
254
+ return h, subdiv
255
+ else:
256
+ return h
257
+
258
+ def forward(self, x: sp.SparseTensor, subdiv: sp.SparseTensor = None) -> sp.SparseTensor:
259
+ if self.use_checkpoint:
260
+ return torch.utils.checkpoint.checkpoint(self._forward, x, subdiv, use_reentrant=False)
261
+ else:
262
+ return self._forward(x, subdiv)
263
+
264
+
265
+ class SparseConvNeXtBlock3d(nn.Module):
266
+ def __init__(
267
+ self,
268
+ channels: int,
269
+ mlp_ratio: float = 4.0,
270
+ use_checkpoint: bool = False,
271
+ ):
272
+ super().__init__()
273
+ self.channels = channels
274
+ self.use_checkpoint = use_checkpoint
275
+
276
+ self.norm = LayerNorm32(channels, elementwise_affine=True, eps=1e-6)
277
+ self.conv = sp.SparseConv3d(channels, channels, 3)
278
+ self.mlp = nn.Sequential(
279
+ nn.Linear(channels, int(channels * mlp_ratio)),
280
+ nn.SiLU(),
281
+ zero_module(nn.Linear(int(channels * mlp_ratio), channels)),
282
+ )
283
+
284
+ def _forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
285
+ h = self.conv(x)
286
+ h = h.replace(self.norm(h.feats))
287
+ h = h.replace(self.mlp(h.feats))
288
+ return h + x
289
+
290
+ def forward(self, x: sp.SparseTensor) -> sp.SparseTensor:
291
+ if self.use_checkpoint:
292
+ return torch.utils.checkpoint.checkpoint(self._forward, x, use_reentrant=False)
293
+ else:
294
+ return self._forward(x)
295
+
296
+
297
+ class SparseUnetVaeEncoder(nn.Module):
298
+ """
299
+ Sparse Swin Transformer Unet VAE model.
300
+ """
301
+ def __init__(
302
+ self,
303
+ in_channels: int,
304
+ model_channels: List[int],
305
+ latent_channels: int,
306
+ num_blocks: List[int],
307
+ block_type: List[str],
308
+ down_block_type: List[str],
309
+ block_args: List[Dict[str, Any]],
310
+ use_fp16: bool = False,
311
+ ):
312
+ super().__init__()
313
+ self.in_channels = in_channels
314
+ self.model_channels = model_channels
315
+ self.num_blocks = num_blocks
316
+ self.dtype = torch.float16 if use_fp16 else torch.float32
317
+ self.dtype = torch.float16 if use_fp16 else torch.float32
318
+
319
+ self.input_layer = sp.SparseLinear(in_channels, model_channels[0])
320
+ self.to_latent = sp.SparseLinear(model_channels[-1], 2 * latent_channels)
321
+
322
+ self.blocks = nn.ModuleList([])
323
+ for i in range(len(num_blocks)):
324
+ self.blocks.append(nn.ModuleList([]))
325
+ for j in range(num_blocks[i]):
326
+ self.blocks[-1].append(
327
+ globals()[block_type[i]](
328
+ model_channels[i],
329
+ **block_args[i],
330
+ )
331
+ )
332
+ if i < len(num_blocks) - 1:
333
+ self.blocks[-1].append(
334
+ globals()[down_block_type[i]](
335
+ model_channels[i],
336
+ model_channels[i+1],
337
+ **block_args[i],
338
+ )
339
+ )
340
+
341
+ self.initialize_weights()
342
+ if use_fp16:
343
+ self.convert_to_fp16()
344
+
345
+ @property
346
+ def device(self) -> torch.device:
347
+ """
348
+ Return the device of the model.
349
+ """
350
+ return next(self.parameters()).device
351
+
352
+ def convert_to_fp16(self) -> None:
353
+ """
354
+ Convert the torso of the model to float16.
355
+ """
356
+ self.blocks.apply(convert_module_to_f16)
357
+
358
+ def convert_to_fp32(self) -> None:
359
+ """
360
+ Convert the torso of the model to float32.
361
+ """
362
+ self.blocks.apply(convert_module_to_f32)
363
+
364
+ def initialize_weights(self) -> None:
365
+ # Initialize transformer layers:
366
+ def _basic_init(module):
367
+ if isinstance(module, nn.Linear):
368
+ torch.nn.init.xavier_uniform_(module.weight)
369
+ if module.bias is not None:
370
+ nn.init.constant_(module.bias, 0)
371
+ self.apply(_basic_init)
372
+
373
+ def forward(self, x: sp.SparseTensor, sample_posterior=False, return_raw=False):
374
+ h = self.input_layer(x)
375
+ h = h.type(self.dtype)
376
+ for i, res in enumerate(self.blocks):
377
+ for j, block in enumerate(res):
378
+ h = block(h)
379
+ h = h.type(x.dtype)
380
+ h = h.replace(F.layer_norm(h.feats, h.feats.shape[-1:]))
381
+ h = self.to_latent(h)
382
+
383
+ # Sample from the posterior distribution
384
+ mean, logvar = h.feats.chunk(2, dim=-1)
385
+ if sample_posterior:
386
+ std = torch.exp(0.5 * logvar)
387
+ z = mean + std * torch.randn_like(std)
388
+ else:
389
+ z = mean
390
+ z = h.replace(z)
391
+
392
+ if return_raw:
393
+ return z, mean, logvar
394
+ else:
395
+ return z
396
+
397
+
398
+ class SparseUnetVaeDecoder(nn.Module):
399
+ """
400
+ Sparse Swin Transformer Unet VAE model.
401
+ """
402
+ def __init__(
403
+ self,
404
+ out_channels: int,
405
+ model_channels: List[int],
406
+ latent_channels: int,
407
+ num_blocks: List[int],
408
+ block_type: List[str],
409
+ up_block_type: List[str],
410
+ block_args: List[Dict[str, Any]],
411
+ use_fp16: bool = False,
412
+ pred_subdiv: bool = True,
413
+ ):
414
+ super().__init__()
415
+ self.out_channels = out_channels
416
+ self.model_channels = model_channels
417
+ self.num_blocks = num_blocks
418
+ self.use_fp16 = use_fp16
419
+ self.pred_subdiv = pred_subdiv
420
+ self.dtype = torch.float16 if use_fp16 else torch.float32
421
+ self.low_vram = False
422
+
423
+ self.output_layer = sp.SparseLinear(model_channels[-1], out_channels)
424
+ self.from_latent = sp.SparseLinear(latent_channels, model_channels[0])
425
+
426
+ self.blocks = nn.ModuleList([])
427
+ for i in range(len(num_blocks)):
428
+ self.blocks.append(nn.ModuleList([]))
429
+ for j in range(num_blocks[i]):
430
+ self.blocks[-1].append(
431
+ globals()[block_type[i]](
432
+ model_channels[i],
433
+ **block_args[i],
434
+ )
435
+ )
436
+ if i < len(num_blocks) - 1:
437
+ self.blocks[-1].append(
438
+ globals()[up_block_type[i]](
439
+ model_channels[i],
440
+ model_channels[i+1],
441
+ pred_subdiv=pred_subdiv,
442
+ **block_args[i],
443
+ )
444
+ )
445
+
446
+ self.initialize_weights()
447
+ if use_fp16:
448
+ self.convert_to_fp16()
449
+
450
+ @property
451
+ def device(self) -> torch.device:
452
+ """
453
+ Return the device of the model.
454
+ """
455
+ return next(self.parameters()).device
456
+
457
+ def convert_to_fp16(self) -> None:
458
+ """
459
+ Convert the torso of the model to float16.
460
+ """
461
+ self.blocks.apply(convert_module_to_f16)
462
+
463
+ def convert_to_fp32(self) -> None:
464
+ """
465
+ Convert the torso of the model to float32.
466
+ """
467
+ self.blocks.apply(convert_module_to_f32)
468
+
469
+ def initialize_weights(self) -> None:
470
+ # Initialize transformer layers:
471
+ def _basic_init(module):
472
+ if isinstance(module, nn.Linear):
473
+ torch.nn.init.xavier_uniform_(module.weight)
474
+ if module.bias is not None:
475
+ nn.init.constant_(module.bias, 0)
476
+ self.apply(_basic_init)
477
+
478
+ def forward(self, x: sp.SparseTensor, guide_subs: Optional[List[sp.SparseTensor]] = None, return_subs: bool = False) -> sp.SparseTensor:
479
+ assert guide_subs is None or self.pred_subdiv == False, "Only decoders with pred_subdiv=False can be used with guide_subs"
480
+ assert return_subs == False or self.pred_subdiv == True, "Only decoders with pred_subdiv=True can be used with return_subs"
481
+
482
+ h = self.from_latent(x)
483
+ h = h.type(self.dtype)
484
+ subs_gt = []
485
+ subs = []
486
+ for i, res in enumerate(self.blocks):
487
+ for j, block in enumerate(res):
488
+ if i < len(self.blocks) - 1 and j == len(res) - 1:
489
+ if self.pred_subdiv:
490
+ if self.training:
491
+ subs_gt.append(h.get_spatial_cache('subdivision'))
492
+ h, sub = block(h)
493
+ subs.append(sub)
494
+ else:
495
+ h = block(h, subdiv=guide_subs[i] if guide_subs is not None else None)
496
+ else:
497
+ h = block(h)
498
+ h = h.type(x.dtype)
499
+ h = h.replace(F.layer_norm(h.feats, h.feats.shape[-1:]))
500
+ h = self.output_layer(h)
501
+ if self.training and self.pred_subdiv:
502
+ return h, subs_gt, subs
503
+ else:
504
+ if return_subs:
505
+ return h, subs
506
+ else:
507
+ return h
508
+
509
+ def upsample(self, x: sp.SparseTensor, upsample_times: int) -> torch.Tensor:
510
+ assert self.pred_subdiv == True, "Only decoders with pred_subdiv=True can be used with upsampling"
511
+
512
+ h = self.from_latent(x)
513
+ h = h.type(self.dtype)
514
+ for i, res in enumerate(self.blocks):
515
+ if i == upsample_times:
516
+ return h.coords
517
+ for j, block in enumerate(res):
518
+ if i < len(self.blocks) - 1 and j == len(res) - 1:
519
+ h, sub = block(h)
520
+ else:
521
+ h = block(h)
522
+
TRELLIS.2/trellis2/models/sparse_elastic_mixin.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from contextlib import contextmanager
2
+ from typing import *
3
+ import math
4
+ from ..modules import sparse as sp
5
+ from ..utils.elastic_utils import ElasticModuleMixin
6
+
7
+
8
+ class SparseTransformerElasticMixin(ElasticModuleMixin):
9
+ def _get_input_size(self, x: sp.SparseTensor, *args, **kwargs):
10
+ return x.feats.shape[0]
11
+
12
+ @contextmanager
13
+ def with_mem_ratio(self, mem_ratio=1.0):
14
+ if mem_ratio == 1.0:
15
+ yield 1.0
16
+ return
17
+ num_blocks = len(self.blocks)
18
+ num_checkpoint_blocks = min(math.ceil((1 - mem_ratio) * num_blocks) + 1, num_blocks)
19
+ exact_mem_ratio = 1 - (num_checkpoint_blocks - 1) / num_blocks
20
+ for i in range(num_blocks):
21
+ self.blocks[i].use_checkpoint = i < num_checkpoint_blocks
22
+ yield exact_mem_ratio
23
+ for i in range(num_blocks):
24
+ self.blocks[i].use_checkpoint = False
TRELLIS.2/trellis2/models/sparse_structure_flow.py ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import *
2
+ from functools import partial
3
+ import torch
4
+ import torch.nn as nn
5
+ import torch.nn.functional as F
6
+ import numpy as np
7
+ from ..modules.utils import convert_module_to, manual_cast, str_to_dtype
8
+ from ..modules.transformer import AbsolutePositionEmbedder, ModulatedTransformerCrossBlock
9
+ from ..modules.attention import RotaryPositionEmbedder
10
+
11
+
12
+ class TimestepEmbedder(nn.Module):
13
+ """
14
+ Embeds scalar timesteps into vector representations.
15
+ """
16
+ def __init__(self, hidden_size, frequency_embedding_size=256):
17
+ super().__init__()
18
+ self.mlp = nn.Sequential(
19
+ nn.Linear(frequency_embedding_size, hidden_size, bias=True),
20
+ nn.SiLU(),
21
+ nn.Linear(hidden_size, hidden_size, bias=True),
22
+ )
23
+ self.frequency_embedding_size = frequency_embedding_size
24
+
25
+ @staticmethod
26
+ def timestep_embedding(t, dim, max_period=10000):
27
+ """
28
+ Create sinusoidal timestep embeddings.
29
+
30
+ Args:
31
+ t: a 1-D Tensor of N indices, one per batch element.
32
+ These may be fractional.
33
+ dim: the dimension of the output.
34
+ max_period: controls the minimum frequency of the embeddings.
35
+
36
+ Returns:
37
+ an (N, D) Tensor of positional embeddings.
38
+ """
39
+ # https://github.com/openai/glide-text2im/blob/main/glide_text2im/nn.py
40
+ half = dim // 2
41
+ freqs = torch.exp(
42
+ -np.log(max_period) * torch.arange(start=0, end=half, dtype=torch.float32) / half
43
+ ).to(device=t.device)
44
+ args = t[:, None].float() * freqs[None]
45
+ embedding = torch.cat([torch.cos(args), torch.sin(args)], dim=-1)
46
+ if dim % 2:
47
+ embedding = torch.cat([embedding, torch.zeros_like(embedding[:, :1])], dim=-1)
48
+ return embedding
49
+
50
+ def forward(self, t):
51
+ t_freq = self.timestep_embedding(t, self.frequency_embedding_size)
52
+ t_emb = self.mlp(t_freq)
53
+ return t_emb
54
+
55
+
56
+ class SparseStructureFlowModel(nn.Module):
57
+ def __init__(
58
+ self,
59
+ resolution: int,
60
+ in_channels: int,
61
+ model_channels: int,
62
+ cond_channels: int,
63
+ out_channels: int,
64
+ num_blocks: int,
65
+ num_heads: Optional[int] = None,
66
+ num_head_channels: Optional[int] = 64,
67
+ mlp_ratio: float = 4,
68
+ pe_mode: Literal["ape", "rope"] = "ape",
69
+ rope_freq: Tuple[float, float] = (1.0, 10000.0),
70
+ dtype: str = 'float32',
71
+ use_checkpoint: bool = False,
72
+ share_mod: bool = False,
73
+ initialization: str = 'vanilla',
74
+ qk_rms_norm: bool = False,
75
+ qk_rms_norm_cross: bool = False,
76
+ **kwargs
77
+ ):
78
+ super().__init__()
79
+ self.resolution = resolution
80
+ self.in_channels = in_channels
81
+ self.model_channels = model_channels
82
+ self.cond_channels = cond_channels
83
+ self.out_channels = out_channels
84
+ self.num_blocks = num_blocks
85
+ self.num_heads = num_heads or model_channels // num_head_channels
86
+ self.mlp_ratio = mlp_ratio
87
+ self.pe_mode = pe_mode
88
+ self.use_checkpoint = use_checkpoint
89
+ self.share_mod = share_mod
90
+ self.initialization = initialization
91
+ self.qk_rms_norm = qk_rms_norm
92
+ self.qk_rms_norm_cross = qk_rms_norm_cross
93
+ self.dtype = str_to_dtype(dtype)
94
+
95
+ self.t_embedder = TimestepEmbedder(model_channels)
96
+ if share_mod:
97
+ self.adaLN_modulation = nn.Sequential(
98
+ nn.SiLU(),
99
+ nn.Linear(model_channels, 6 * model_channels, bias=True)
100
+ )
101
+
102
+ if pe_mode == "ape":
103
+ pos_embedder = AbsolutePositionEmbedder(model_channels, 3)
104
+ coords = torch.meshgrid(*[torch.arange(res, device=self.device) for res in [resolution] * 3], indexing='ij')
105
+ coords = torch.stack(coords, dim=-1).reshape(-1, 3)
106
+ pos_emb = pos_embedder(coords)
107
+ self.register_buffer("pos_emb", pos_emb)
108
+ elif pe_mode == "rope":
109
+ pos_embedder = RotaryPositionEmbedder(self.model_channels // self.num_heads, 3)
110
+ coords = torch.meshgrid(*[torch.arange(res, device=self.device) for res in [resolution] * 3], indexing='ij')
111
+ coords = torch.stack(coords, dim=-1).reshape(-1, 3)
112
+ rope_phases = pos_embedder(coords)
113
+ self.register_buffer("rope_phases", rope_phases)
114
+
115
+ if pe_mode != "rope":
116
+ self.rope_phases = None
117
+
118
+ self.input_layer = nn.Linear(in_channels, model_channels)
119
+
120
+ self.blocks = nn.ModuleList([
121
+ ModulatedTransformerCrossBlock(
122
+ model_channels,
123
+ cond_channels,
124
+ num_heads=self.num_heads,
125
+ mlp_ratio=self.mlp_ratio,
126
+ attn_mode='full',
127
+ use_checkpoint=self.use_checkpoint,
128
+ use_rope=(pe_mode == "rope"),
129
+ rope_freq=rope_freq,
130
+ share_mod=share_mod,
131
+ qk_rms_norm=self.qk_rms_norm,
132
+ qk_rms_norm_cross=self.qk_rms_norm_cross,
133
+ )
134
+ for _ in range(num_blocks)
135
+ ])
136
+
137
+ self.out_layer = nn.Linear(model_channels, out_channels)
138
+
139
+ self.initialize_weights()
140
+ self.convert_to(self.dtype)
141
+
142
+ @property
143
+ def device(self) -> torch.device:
144
+ """
145
+ Return the device of the model.
146
+ """
147
+ return next(self.parameters()).device
148
+
149
+ def convert_to(self, dtype: torch.dtype) -> None:
150
+ """
151
+ Convert the torso of the model to the specified dtype.
152
+ """
153
+ self.dtype = dtype
154
+ self.blocks.apply(partial(convert_module_to, dtype=dtype))
155
+
156
+ def initialize_weights(self) -> None:
157
+ if self.initialization == 'vanilla':
158
+ # Initialize transformer layers:
159
+ def _basic_init(module):
160
+ if isinstance(module, nn.Linear):
161
+ torch.nn.init.xavier_uniform_(module.weight)
162
+ if module.bias is not None:
163
+ nn.init.constant_(module.bias, 0)
164
+ self.apply(_basic_init)
165
+
166
+ # Initialize timestep embedding MLP:
167
+ nn.init.normal_(self.t_embedder.mlp[0].weight, std=0.02)
168
+ nn.init.normal_(self.t_embedder.mlp[2].weight, std=0.02)
169
+
170
+ # Zero-out adaLN modulation layers in DiT blocks:
171
+ if self.share_mod:
172
+ nn.init.constant_(self.adaLN_modulation[-1].weight, 0)
173
+ nn.init.constant_(self.adaLN_modulation[-1].bias, 0)
174
+ else:
175
+ for block in self.blocks:
176
+ nn.init.constant_(block.adaLN_modulation[-1].weight, 0)
177
+ nn.init.constant_(block.adaLN_modulation[-1].bias, 0)
178
+
179
+ # Zero-out output layers:
180
+ nn.init.constant_(self.out_layer.weight, 0)
181
+ nn.init.constant_(self.out_layer.bias, 0)
182
+
183
+ elif self.initialization == 'scaled':
184
+ # Initialize transformer layers:
185
+ def _basic_init(module):
186
+ if isinstance(module, nn.Linear):
187
+ torch.nn.init.normal_(module.weight, std=np.sqrt(2.0 / (5.0 * self.model_channels)))
188
+ if module.bias is not None:
189
+ nn.init.constant_(module.bias, 0)
190
+ self.apply(_basic_init)
191
+
192
+ # Scaled init for to_out and ffn2
193
+ def _scaled_init(module):
194
+ if isinstance(module, nn.Linear):
195
+ torch.nn.init.normal_(module.weight, std=1.0 / np.sqrt(5 * self.num_blocks * self.model_channels))
196
+ if module.bias is not None:
197
+ nn.init.constant_(module.bias, 0)
198
+ for block in self.blocks:
199
+ block.self_attn.to_out.apply(_scaled_init)
200
+ block.cross_attn.to_out.apply(_scaled_init)
201
+ block.mlp.mlp[2].apply(_scaled_init)
202
+
203
+ # Initialize input layer to make the initial representation have variance 1
204
+ nn.init.normal_(self.input_layer.weight, std=1.0 / np.sqrt(self.in_channels))
205
+ nn.init.zeros_(self.input_layer.bias)
206
+
207
+ # Initialize timestep embedding MLP:
208
+ nn.init.normal_(self.t_embedder.mlp[0].weight, std=0.02)
209
+ nn.init.normal_(self.t_embedder.mlp[2].weight, std=0.02)
210
+
211
+ # Zero-out adaLN modulation layers in DiT blocks:
212
+ if self.share_mod:
213
+ nn.init.constant_(self.adaLN_modulation[-1].weight, 0)
214
+ nn.init.constant_(self.adaLN_modulation[-1].bias, 0)
215
+ else:
216
+ for block in self.blocks:
217
+ nn.init.constant_(block.adaLN_modulation[-1].weight, 0)
218
+ nn.init.constant_(block.adaLN_modulation[-1].bias, 0)
219
+
220
+ # Zero-out output layers:
221
+ nn.init.constant_(self.out_layer.weight, 0)
222
+ nn.init.constant_(self.out_layer.bias, 0)
223
+
224
+ def forward(self, x: torch.Tensor, t: torch.Tensor, cond: torch.Tensor) -> torch.Tensor:
225
+ assert [*x.shape] == [x.shape[0], self.in_channels, *[self.resolution] * 3], \
226
+ f"Input shape mismatch, got {x.shape}, expected {[x.shape[0], self.in_channels, *[self.resolution] * 3]}"
227
+
228
+ h = x.view(*x.shape[:2], -1).permute(0, 2, 1).contiguous()
229
+
230
+ h = self.input_layer(h)
231
+ if self.pe_mode == "ape":
232
+ h = h + self.pos_emb[None]
233
+ t_emb = self.t_embedder(t)
234
+ if self.share_mod:
235
+ t_emb = self.adaLN_modulation(t_emb)
236
+ t_emb = manual_cast(t_emb, self.dtype)
237
+ h = manual_cast(h, self.dtype)
238
+ cond = manual_cast(cond, self.dtype)
239
+ for block in self.blocks:
240
+ h = block(h, t_emb, cond, self.rope_phases)
241
+ h = manual_cast(h, x.dtype)
242
+ h = F.layer_norm(h, h.shape[-1:])
243
+ h = self.out_layer(h)
244
+
245
+ h = h.permute(0, 2, 1).view(h.shape[0], h.shape[2], *[self.resolution] * 3).contiguous()
246
+
247
+ return h
TRELLIS.2/trellis2/models/sparse_structure_vae.py ADDED
@@ -0,0 +1,306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import *
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
+ from ..modules.norm import GroupNorm32, ChannelLayerNorm32
6
+ from ..modules.spatial import pixel_shuffle_3d
7
+ from ..modules.utils import zero_module, convert_module_to_f16, convert_module_to_f32
8
+
9
+
10
+ def norm_layer(norm_type: str, *args, **kwargs) -> nn.Module:
11
+ """
12
+ Return a normalization layer.
13
+ """
14
+ if norm_type == "group":
15
+ return GroupNorm32(32, *args, **kwargs)
16
+ elif norm_type == "layer":
17
+ return ChannelLayerNorm32(*args, **kwargs)
18
+ else:
19
+ raise ValueError(f"Invalid norm type {norm_type}")
20
+
21
+
22
+ class ResBlock3d(nn.Module):
23
+ def __init__(
24
+ self,
25
+ channels: int,
26
+ out_channels: Optional[int] = None,
27
+ norm_type: Literal["group", "layer"] = "layer",
28
+ ):
29
+ super().__init__()
30
+ self.channels = channels
31
+ self.out_channels = out_channels or channels
32
+
33
+ self.norm1 = norm_layer(norm_type, channels)
34
+ self.norm2 = norm_layer(norm_type, self.out_channels)
35
+ self.conv1 = nn.Conv3d(channels, self.out_channels, 3, padding=1)
36
+ self.conv2 = zero_module(nn.Conv3d(self.out_channels, self.out_channels, 3, padding=1))
37
+ self.skip_connection = nn.Conv3d(channels, self.out_channels, 1) if channels != self.out_channels else nn.Identity()
38
+
39
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
40
+ h = self.norm1(x)
41
+ h = F.silu(h)
42
+ h = self.conv1(h)
43
+ h = self.norm2(h)
44
+ h = F.silu(h)
45
+ h = self.conv2(h)
46
+ h = h + self.skip_connection(x)
47
+ return h
48
+
49
+
50
+ class DownsampleBlock3d(nn.Module):
51
+ def __init__(
52
+ self,
53
+ in_channels: int,
54
+ out_channels: int,
55
+ mode: Literal["conv", "avgpool"] = "conv",
56
+ ):
57
+ assert mode in ["conv", "avgpool"], f"Invalid mode {mode}"
58
+
59
+ super().__init__()
60
+ self.in_channels = in_channels
61
+ self.out_channels = out_channels
62
+
63
+ if mode == "conv":
64
+ self.conv = nn.Conv3d(in_channels, out_channels, 2, stride=2)
65
+ elif mode == "avgpool":
66
+ assert in_channels == out_channels, "Pooling mode requires in_channels to be equal to out_channels"
67
+
68
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
69
+ if hasattr(self, "conv"):
70
+ return self.conv(x)
71
+ else:
72
+ return F.avg_pool3d(x, 2)
73
+
74
+
75
+ class UpsampleBlock3d(nn.Module):
76
+ def __init__(
77
+ self,
78
+ in_channels: int,
79
+ out_channels: int,
80
+ mode: Literal["conv", "nearest"] = "conv",
81
+ ):
82
+ assert mode in ["conv", "nearest"], f"Invalid mode {mode}"
83
+
84
+ super().__init__()
85
+ self.in_channels = in_channels
86
+ self.out_channels = out_channels
87
+
88
+ if mode == "conv":
89
+ self.conv = nn.Conv3d(in_channels, out_channels*8, 3, padding=1)
90
+ elif mode == "nearest":
91
+ assert in_channels == out_channels, "Nearest mode requires in_channels to be equal to out_channels"
92
+
93
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
94
+ if hasattr(self, "conv"):
95
+ x = self.conv(x)
96
+ return pixel_shuffle_3d(x, 2)
97
+ else:
98
+ return F.interpolate(x, scale_factor=2, mode="nearest")
99
+
100
+
101
+ class SparseStructureEncoder(nn.Module):
102
+ """
103
+ Encoder for Sparse Structure (\mathcal{E}_S in the paper Sec. 3.3).
104
+
105
+ Args:
106
+ in_channels (int): Channels of the input.
107
+ latent_channels (int): Channels of the latent representation.
108
+ num_res_blocks (int): Number of residual blocks at each resolution.
109
+ channels (List[int]): Channels of the encoder blocks.
110
+ num_res_blocks_middle (int): Number of residual blocks in the middle.
111
+ norm_type (Literal["group", "layer"]): Type of normalization layer.
112
+ use_fp16 (bool): Whether to use FP16.
113
+ """
114
+ def __init__(
115
+ self,
116
+ in_channels: int,
117
+ latent_channels: int,
118
+ num_res_blocks: int,
119
+ channels: List[int],
120
+ num_res_blocks_middle: int = 2,
121
+ norm_type: Literal["group", "layer"] = "layer",
122
+ use_fp16: bool = False,
123
+ ):
124
+ super().__init__()
125
+ self.in_channels = in_channels
126
+ self.latent_channels = latent_channels
127
+ self.num_res_blocks = num_res_blocks
128
+ self.channels = channels
129
+ self.num_res_blocks_middle = num_res_blocks_middle
130
+ self.norm_type = norm_type
131
+ self.use_fp16 = use_fp16
132
+ self.dtype = torch.float16 if use_fp16 else torch.float32
133
+
134
+ self.input_layer = nn.Conv3d(in_channels, channels[0], 3, padding=1)
135
+
136
+ self.blocks = nn.ModuleList([])
137
+ for i, ch in enumerate(channels):
138
+ self.blocks.extend([
139
+ ResBlock3d(ch, ch)
140
+ for _ in range(num_res_blocks)
141
+ ])
142
+ if i < len(channels) - 1:
143
+ self.blocks.append(
144
+ DownsampleBlock3d(ch, channels[i+1])
145
+ )
146
+
147
+ self.middle_block = nn.Sequential(*[
148
+ ResBlock3d(channels[-1], channels[-1])
149
+ for _ in range(num_res_blocks_middle)
150
+ ])
151
+
152
+ self.out_layer = nn.Sequential(
153
+ norm_layer(norm_type, channels[-1]),
154
+ nn.SiLU(),
155
+ nn.Conv3d(channels[-1], latent_channels*2, 3, padding=1)
156
+ )
157
+
158
+ if use_fp16:
159
+ self.convert_to_fp16()
160
+
161
+ @property
162
+ def device(self) -> torch.device:
163
+ """
164
+ Return the device of the model.
165
+ """
166
+ return next(self.parameters()).device
167
+
168
+ def convert_to_fp16(self) -> None:
169
+ """
170
+ Convert the torso of the model to float16.
171
+ """
172
+ self.use_fp16 = True
173
+ self.dtype = torch.float16
174
+ self.blocks.apply(convert_module_to_f16)
175
+ self.middle_block.apply(convert_module_to_f16)
176
+
177
+ def convert_to_fp32(self) -> None:
178
+ """
179
+ Convert the torso of the model to float32.
180
+ """
181
+ self.use_fp16 = False
182
+ self.dtype = torch.float32
183
+ self.blocks.apply(convert_module_to_f32)
184
+ self.middle_block.apply(convert_module_to_f32)
185
+
186
+ def forward(self, x: torch.Tensor, sample_posterior: bool = False, return_raw: bool = False) -> torch.Tensor:
187
+ h = self.input_layer(x)
188
+ h = h.type(self.dtype)
189
+
190
+ for block in self.blocks:
191
+ h = block(h)
192
+ h = self.middle_block(h)
193
+
194
+ h = h.type(x.dtype)
195
+ h = self.out_layer(h)
196
+
197
+ mean, logvar = h.chunk(2, dim=1)
198
+
199
+ if sample_posterior:
200
+ std = torch.exp(0.5 * logvar)
201
+ z = mean + std * torch.randn_like(std)
202
+ else:
203
+ z = mean
204
+
205
+ if return_raw:
206
+ return z, mean, logvar
207
+ return z
208
+
209
+
210
+ class SparseStructureDecoder(nn.Module):
211
+ """
212
+ Decoder for Sparse Structure (\mathcal{D}_S in the paper Sec. 3.3).
213
+
214
+ Args:
215
+ out_channels (int): Channels of the output.
216
+ latent_channels (int): Channels of the latent representation.
217
+ num_res_blocks (int): Number of residual blocks at each resolution.
218
+ channels (List[int]): Channels of the decoder blocks.
219
+ num_res_blocks_middle (int): Number of residual blocks in the middle.
220
+ norm_type (Literal["group", "layer"]): Type of normalization layer.
221
+ use_fp16 (bool): Whether to use FP16.
222
+ """
223
+ def __init__(
224
+ self,
225
+ out_channels: int,
226
+ latent_channels: int,
227
+ num_res_blocks: int,
228
+ channels: List[int],
229
+ num_res_blocks_middle: int = 2,
230
+ norm_type: Literal["group", "layer"] = "layer",
231
+ use_fp16: bool = False,
232
+ ):
233
+ super().__init__()
234
+ self.out_channels = out_channels
235
+ self.latent_channels = latent_channels
236
+ self.num_res_blocks = num_res_blocks
237
+ self.channels = channels
238
+ self.num_res_blocks_middle = num_res_blocks_middle
239
+ self.norm_type = norm_type
240
+ self.use_fp16 = use_fp16
241
+ self.dtype = torch.float16 if use_fp16 else torch.float32
242
+
243
+ self.input_layer = nn.Conv3d(latent_channels, channels[0], 3, padding=1)
244
+
245
+ self.middle_block = nn.Sequential(*[
246
+ ResBlock3d(channels[0], channels[0])
247
+ for _ in range(num_res_blocks_middle)
248
+ ])
249
+
250
+ self.blocks = nn.ModuleList([])
251
+ for i, ch in enumerate(channels):
252
+ self.blocks.extend([
253
+ ResBlock3d(ch, ch)
254
+ for _ in range(num_res_blocks)
255
+ ])
256
+ if i < len(channels) - 1:
257
+ self.blocks.append(
258
+ UpsampleBlock3d(ch, channels[i+1])
259
+ )
260
+
261
+ self.out_layer = nn.Sequential(
262
+ norm_layer(norm_type, channels[-1]),
263
+ nn.SiLU(),
264
+ nn.Conv3d(channels[-1], out_channels, 3, padding=1)
265
+ )
266
+
267
+ if use_fp16:
268
+ self.convert_to_fp16()
269
+
270
+ @property
271
+ def device(self) -> torch.device:
272
+ """
273
+ Return the device of the model.
274
+ """
275
+ return next(self.parameters()).device
276
+
277
+ def convert_to_fp16(self) -> None:
278
+ """
279
+ Convert the torso of the model to float16.
280
+ """
281
+ self.use_fp16 = True
282
+ self.dtype = torch.float16
283
+ self.blocks.apply(convert_module_to_f16)
284
+ self.middle_block.apply(convert_module_to_f16)
285
+
286
+ def convert_to_fp32(self) -> None:
287
+ """
288
+ Convert the torso of the model to float32.
289
+ """
290
+ self.use_fp16 = False
291
+ self.dtype = torch.float32
292
+ self.blocks.apply(convert_module_to_f32)
293
+ self.middle_block.apply(convert_module_to_f32)
294
+
295
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
296
+ h = self.input_layer(x)
297
+
298
+ h = h.type(self.dtype)
299
+
300
+ h = self.middle_block(h)
301
+ for block in self.blocks:
302
+ h = block(h)
303
+
304
+ h = h.type(x.dtype)
305
+ h = self.out_layer(h)
306
+ return h
TRELLIS.2/trellis2/models/structured_latent_flow.py ADDED
@@ -0,0 +1,207 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import *
2
+ from functools import partial
3
+ import torch
4
+ import torch.nn as nn
5
+ import torch.nn.functional as F
6
+ import numpy as np
7
+ from ..modules.utils import convert_module_to, manual_cast, str_to_dtype
8
+ from ..modules.transformer import AbsolutePositionEmbedder
9
+ from ..modules import sparse as sp
10
+ from ..modules.sparse.transformer import ModulatedSparseTransformerCrossBlock
11
+ from .sparse_structure_flow import TimestepEmbedder
12
+ from .sparse_elastic_mixin import SparseTransformerElasticMixin
13
+
14
+
15
+ class SLatFlowModel(nn.Module):
16
+ def __init__(
17
+ self,
18
+ resolution: int,
19
+ in_channels: int,
20
+ model_channels: int,
21
+ cond_channels: int,
22
+ out_channels: int,
23
+ num_blocks: int,
24
+ num_heads: Optional[int] = None,
25
+ num_head_channels: Optional[int] = 64,
26
+ mlp_ratio: float = 4,
27
+ pe_mode: Literal["ape", "rope"] = "ape",
28
+ rope_freq: Tuple[float, float] = (1.0, 10000.0),
29
+ dtype: str = 'float32',
30
+ use_checkpoint: bool = False,
31
+ share_mod: bool = False,
32
+ initialization: str = 'vanilla',
33
+ qk_rms_norm: bool = False,
34
+ qk_rms_norm_cross: bool = False,
35
+ ):
36
+ super().__init__()
37
+ self.resolution = resolution
38
+ self.in_channels = in_channels
39
+ self.model_channels = model_channels
40
+ self.cond_channels = cond_channels
41
+ self.out_channels = out_channels
42
+ self.num_blocks = num_blocks
43
+ self.num_heads = num_heads or model_channels // num_head_channels
44
+ self.mlp_ratio = mlp_ratio
45
+ self.pe_mode = pe_mode
46
+ self.use_checkpoint = use_checkpoint
47
+ self.share_mod = share_mod
48
+ self.initialization = initialization
49
+ self.qk_rms_norm = qk_rms_norm
50
+ self.qk_rms_norm_cross = qk_rms_norm_cross
51
+ self.dtype = str_to_dtype(dtype)
52
+
53
+ self.t_embedder = TimestepEmbedder(model_channels)
54
+ if share_mod:
55
+ self.adaLN_modulation = nn.Sequential(
56
+ nn.SiLU(),
57
+ nn.Linear(model_channels, 6 * model_channels, bias=True)
58
+ )
59
+
60
+ if pe_mode == "ape":
61
+ self.pos_embedder = AbsolutePositionEmbedder(model_channels)
62
+
63
+ self.input_layer = sp.SparseLinear(in_channels, model_channels)
64
+
65
+ self.blocks = nn.ModuleList([
66
+ ModulatedSparseTransformerCrossBlock(
67
+ model_channels,
68
+ cond_channels,
69
+ num_heads=self.num_heads,
70
+ mlp_ratio=self.mlp_ratio,
71
+ attn_mode='full',
72
+ use_checkpoint=self.use_checkpoint,
73
+ use_rope=(pe_mode == "rope"),
74
+ rope_freq=rope_freq,
75
+ share_mod=self.share_mod,
76
+ qk_rms_norm=self.qk_rms_norm,
77
+ qk_rms_norm_cross=self.qk_rms_norm_cross,
78
+ )
79
+ for _ in range(num_blocks)
80
+ ])
81
+
82
+ self.out_layer = sp.SparseLinear(model_channels, out_channels)
83
+
84
+ self.initialize_weights()
85
+ self.convert_to(self.dtype)
86
+
87
+ @property
88
+ def device(self) -> torch.device:
89
+ """
90
+ Return the device of the model.
91
+ """
92
+ return next(self.parameters()).device
93
+
94
+ def convert_to(self, dtype: torch.dtype) -> None:
95
+ """
96
+ Convert the torso of the model to the specified dtype.
97
+ """
98
+ self.dtype = dtype
99
+ self.blocks.apply(partial(convert_module_to, dtype=dtype))
100
+
101
+ def initialize_weights(self) -> None:
102
+ if self.initialization == 'vanilla':
103
+ # Initialize transformer layers:
104
+ def _basic_init(module):
105
+ if isinstance(module, nn.Linear):
106
+ torch.nn.init.xavier_uniform_(module.weight)
107
+ if module.bias is not None:
108
+ nn.init.constant_(module.bias, 0)
109
+ self.apply(_basic_init)
110
+
111
+ # Initialize timestep embedding MLP:
112
+ nn.init.normal_(self.t_embedder.mlp[0].weight, std=0.02)
113
+ nn.init.normal_(self.t_embedder.mlp[2].weight, std=0.02)
114
+
115
+ # Zero-out adaLN modulation layers in DiT blocks:
116
+ if self.share_mod:
117
+ nn.init.constant_(self.adaLN_modulation[-1].weight, 0)
118
+ nn.init.constant_(self.adaLN_modulation[-1].bias, 0)
119
+ else:
120
+ for block in self.blocks:
121
+ nn.init.constant_(block.adaLN_modulation[-1].weight, 0)
122
+ nn.init.constant_(block.adaLN_modulation[-1].bias, 0)
123
+
124
+ # Zero-out output layers:
125
+ nn.init.constant_(self.out_layer.weight, 0)
126
+ nn.init.constant_(self.out_layer.bias, 0)
127
+
128
+ elif self.initialization == 'scaled':
129
+ # Initialize transformer layers:
130
+ def _basic_init(module):
131
+ if isinstance(module, nn.Linear):
132
+ torch.nn.init.normal_(module.weight, std=np.sqrt(2.0 / (5.0 * self.model_channels)))
133
+ if module.bias is not None:
134
+ nn.init.constant_(module.bias, 0)
135
+ self.apply(_basic_init)
136
+
137
+ # Scaled init for to_out and ffn2
138
+ def _scaled_init(module):
139
+ if isinstance(module, nn.Linear):
140
+ torch.nn.init.normal_(module.weight, std=1.0 / np.sqrt(5 * self.num_blocks * self.model_channels))
141
+ if module.bias is not None:
142
+ nn.init.constant_(module.bias, 0)
143
+ for block in self.blocks:
144
+ block.self_attn.to_out.apply(_scaled_init)
145
+ block.cross_attn.to_out.apply(_scaled_init)
146
+ block.mlp.mlp[2].apply(_scaled_init)
147
+
148
+ # Initialize input layer to make the initial representation have variance 1
149
+ nn.init.normal_(self.input_layer.weight, std=1.0 / np.sqrt(self.in_channels))
150
+ nn.init.zeros_(self.input_layer.bias)
151
+
152
+ # Initialize timestep embedding MLP:
153
+ nn.init.normal_(self.t_embedder.mlp[0].weight, std=0.02)
154
+ nn.init.normal_(self.t_embedder.mlp[2].weight, std=0.02)
155
+
156
+ # Zero-out adaLN modulation layers in DiT blocks:
157
+ if self.share_mod:
158
+ nn.init.constant_(self.adaLN_modulation[-1].weight, 0)
159
+ nn.init.constant_(self.adaLN_modulation[-1].bias, 0)
160
+ else:
161
+ for block in self.blocks:
162
+ nn.init.constant_(block.adaLN_modulation[-1].weight, 0)
163
+ nn.init.constant_(block.adaLN_modulation[-1].bias, 0)
164
+
165
+ # Zero-out output layers:
166
+ nn.init.constant_(self.out_layer.weight, 0)
167
+ nn.init.constant_(self.out_layer.bias, 0)
168
+
169
+ def forward(
170
+ self,
171
+ x: sp.SparseTensor,
172
+ t: torch.Tensor,
173
+ cond: Union[torch.Tensor, List[torch.Tensor]],
174
+ concat_cond: Optional[sp.SparseTensor] = None,
175
+ **kwargs
176
+ ) -> sp.SparseTensor:
177
+ if concat_cond is not None:
178
+ x = sp.sparse_cat([x, concat_cond], dim=-1)
179
+ if isinstance(cond, list):
180
+ cond = sp.VarLenTensor.from_tensor_list(cond)
181
+
182
+ h = self.input_layer(x)
183
+ h = manual_cast(h, self.dtype)
184
+ t_emb = self.t_embedder(t)
185
+ if self.share_mod:
186
+ t_emb = self.adaLN_modulation(t_emb)
187
+ t_emb = manual_cast(t_emb, self.dtype)
188
+ cond = manual_cast(cond, self.dtype)
189
+
190
+ if self.pe_mode == "ape":
191
+ pe = self.pos_embedder(h.coords[:, 1:])
192
+ h = h + manual_cast(pe, self.dtype)
193
+ for block in self.blocks:
194
+ h = block(h, t_emb, cond)
195
+
196
+ h = manual_cast(h, x.dtype)
197
+ h = h.replace(F.layer_norm(h.feats, h.feats.shape[-1:]))
198
+ h = self.out_layer(h)
199
+ return h
200
+
201
+
202
+ class ElasticSLatFlowModel(SparseTransformerElasticMixin, SLatFlowModel):
203
+ """
204
+ SLat Flow Model with elastic memory management.
205
+ Used for training with low VRAM.
206
+ """
207
+ pass