DeepSDFStruct 1.7.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. DeepSDFStruct/SDF.py +1734 -0
  2. DeepSDFStruct/__init__.py +66 -0
  3. DeepSDFStruct/deep_sdf/__init__.py +69 -0
  4. DeepSDFStruct/deep_sdf/create_screenshots_from_plyfiles.py +65 -0
  5. DeepSDFStruct/deep_sdf/data.py +258 -0
  6. DeepSDFStruct/deep_sdf/metrics/__init__.py +14 -0
  7. DeepSDFStruct/deep_sdf/metrics/mesh_to_analytical.py +24 -0
  8. DeepSDFStruct/deep_sdf/models.py +197 -0
  9. DeepSDFStruct/deep_sdf/networks/__init__.py +8 -0
  10. DeepSDFStruct/deep_sdf/networks/analytic_round_cross.py +105 -0
  11. DeepSDFStruct/deep_sdf/networks/deep_sdf_decoder.py +155 -0
  12. DeepSDFStruct/deep_sdf/networks/hierarchical_deep_sdf_decoder.py +139 -0
  13. DeepSDFStruct/deep_sdf/networks/hierarchical_positional_sdf_decoder.py +190 -0
  14. DeepSDFStruct/deep_sdf/networks/resnet_positional_sdf_decoder.py +108 -0
  15. DeepSDFStruct/deep_sdf/nn_utils.py +70 -0
  16. DeepSDFStruct/deep_sdf/plotting.py +143 -0
  17. DeepSDFStruct/deep_sdf/reconstruction.py +165 -0
  18. DeepSDFStruct/deep_sdf/training.py +840 -0
  19. DeepSDFStruct/deep_sdf/workspace.py +363 -0
  20. DeepSDFStruct/design_of_experiments.py +235 -0
  21. DeepSDFStruct/flexicubes/__init__.py +15 -0
  22. DeepSDFStruct/flexicubes/flexicubes.py +1005 -0
  23. DeepSDFStruct/flexicubes/tables.py +2338 -0
  24. DeepSDFStruct/flexisquares/__init__.py +15 -0
  25. DeepSDFStruct/flexisquares/flexisquares.py +967 -0
  26. DeepSDFStruct/flexisquares/tables.py +63 -0
  27. DeepSDFStruct/lattice_structure.py +254 -0
  28. DeepSDFStruct/local_shapes.py +226 -0
  29. DeepSDFStruct/mesh.py +1184 -0
  30. DeepSDFStruct/optimization.py +357 -0
  31. DeepSDFStruct/parametrization.py +215 -0
  32. DeepSDFStruct/pretrained_models.py +128 -0
  33. DeepSDFStruct/sampling.py +670 -0
  34. DeepSDFStruct/sdf_operations.py +990 -0
  35. DeepSDFStruct/sdf_primitives.py +1416 -0
  36. DeepSDFStruct/splinepy_unitcells/__init__.py +35 -0
  37. DeepSDFStruct/splinepy_unitcells/chi_3D.py +1617 -0
  38. DeepSDFStruct/splinepy_unitcells/cross_lattice.py +165 -0
  39. DeepSDFStruct/splinepy_unitcells/double_lattice_extruded.py +298 -0
  40. DeepSDFStruct/splinepy_unitcells/snappy_3d.py +544 -0
  41. DeepSDFStruct/torch_spline.py +446 -0
  42. DeepSDFStruct/trained_models/analytic_round_cross/LatentCodes/latest.pth +0 -0
  43. DeepSDFStruct/trained_models/analytic_round_cross/ModelParameters/latest.pth +0 -0
  44. DeepSDFStruct/trained_models/analytic_round_cross/OptimizerParameters/latest.pth +0 -0
  45. DeepSDFStruct/trained_models/analytic_round_cross/specs.json +45 -0
  46. DeepSDFStruct/trained_models/chi_and_cross/LatentCodes/latest.pth +0 -0
  47. DeepSDFStruct/trained_models/chi_and_cross/Logs.pth +0 -0
  48. DeepSDFStruct/trained_models/chi_and_cross/ModelParameters/latest.pth +0 -0
  49. DeepSDFStruct/trained_models/chi_and_cross/OptimizerParameters/latest.pth +0 -0
  50. DeepSDFStruct/trained_models/chi_and_cross/specs.json +76 -0
  51. DeepSDFStruct/trained_models/primitives/LatentCodes/latest.pth +0 -0
  52. DeepSDFStruct/trained_models/primitives/ModelParameters/latest.pth +0 -0
  53. DeepSDFStruct/trained_models/primitives/OptimizerParameters/latest.pth +0 -0
  54. DeepSDFStruct/trained_models/primitives/specs.json +46 -0
  55. DeepSDFStruct/trained_models/primitives_2d/LatentCodes/latest.pth +0 -0
  56. DeepSDFStruct/trained_models/primitives_2d/ModelParameters/latest.pth +0 -0
  57. DeepSDFStruct/trained_models/primitives_2d/OptimizerParameters/latest.pth +0 -0
  58. DeepSDFStruct/trained_models/primitives_2d/specs.json +46 -0
  59. DeepSDFStruct/trained_models/round_cross/LatentCodes/latest.pth +0 -0
  60. DeepSDFStruct/trained_models/round_cross/Logs.pth +0 -0
  61. DeepSDFStruct/trained_models/round_cross/ModelParameters/latest.pth +0 -0
  62. DeepSDFStruct/trained_models/round_cross/OptimizerParameters/latest.pth +0 -0
  63. DeepSDFStruct/trained_models/round_cross/specs.json +45 -0
  64. DeepSDFStruct/trained_models/test_experiment/LatentCodes/latent_code_data_map.json +106 -0
  65. DeepSDFStruct/trained_models/test_experiment/specs.json +45 -0
  66. DeepSDFStruct/trained_models/test_experiment/training_summary.json +10 -0
  67. DeepSDFStruct/trained_models/test_experiment_hierarchical/LatentCodes/latent_code_data_map.json +106 -0
  68. DeepSDFStruct/trained_models/test_experiment_hierarchical/specs.json +45 -0
  69. DeepSDFStruct/trained_models/test_experiment_hierarchical/training_summary.json +10 -0
  70. DeepSDFStruct/trained_models/test_experiment_homogenization/LatentCodes/latent_code_data_map.json +5006 -0
  71. DeepSDFStruct/trained_models/test_experiment_homogenization/LatentCodes/latest.pth +0 -0
  72. DeepSDFStruct/trained_models/test_experiment_homogenization/Logs.png +0 -0
  73. DeepSDFStruct/trained_models/test_experiment_homogenization/ModelParameters/latest.pth +0 -0
  74. DeepSDFStruct/trained_models/test_experiment_homogenization/OptimizerParameters/latest.pth +0 -0
  75. DeepSDFStruct/trained_models/test_experiment_homogenization/specs.json +83 -0
  76. DeepSDFStruct/trained_models/test_experiment_homogenization/training_summary.json +10 -0
  77. DeepSDFStruct/utils.py +96 -0
  78. DeepSDFStruct/visualization/latent_viewer.py +211 -0
  79. DeepSDFStruct/visualization/latent_viewer_cli.py +8 -0
  80. benchmarks/generate_sdf_showcase.py +622 -0
  81. benchmarks/sdf_from_mesh_benchmark.py +52 -0
  82. benchmarks/sdf_from_mesh_comparison.md +33 -0
  83. benchmarks/sdf_showcase/operations/boolean/difference_sphere_box.png +0 -0
  84. benchmarks/sdf_showcase/operations/boolean/union_sphere_box.png +0 -0
  85. benchmarks/sdf_showcase/operations/transformations/circular_array_sphere.png +0 -0
  86. benchmarks/sdf_showcase/operations/transformations/dilate_sphere.png +0 -0
  87. benchmarks/sdf_showcase/operations/transformations/mirror_sphere.png +0 -0
  88. benchmarks/sdf_showcase/operations/transformations/repeat_sphere.png +0 -0
  89. benchmarks/sdf_showcase/operations/transformations/revolve_circle.png +0 -0
  90. benchmarks/sdf_showcase/operations/transformations/shell_sphere.png +0 -0
  91. benchmarks/sdf_showcase/operations/transformations/twist_torus.png +0 -0
  92. benchmarks/sdf_showcase/primitives/2D/circle.png +0 -0
  93. benchmarks/sdf_showcase/primitives/2D/equilateral_triangle.png +0 -0
  94. benchmarks/sdf_showcase/primitives/2D/hexagon.png +0 -0
  95. benchmarks/sdf_showcase/primitives/2D/polygon_pentagon.png +0 -0
  96. benchmarks/sdf_showcase/primitives/2D/rectangle.png +0 -0
  97. benchmarks/sdf_showcase/primitives/2D/rounded_rectangle.png +0 -0
  98. benchmarks/sdf_showcase/primitives/3D/box.png +0 -0
  99. benchmarks/sdf_showcase/primitives/3D/capped_cone.png +0 -0
  100. benchmarks/sdf_showcase/primitives/3D/capped_cylinder.png +0 -0
  101. benchmarks/sdf_showcase/primitives/3D/capsule.png +0 -0
  102. benchmarks/sdf_showcase/primitives/3D/cone.png +0 -0
  103. benchmarks/sdf_showcase/primitives/3D/corner_spheres.png +0 -0
  104. benchmarks/sdf_showcase/primitives/3D/cross_ms.png +0 -0
  105. benchmarks/sdf_showcase/primitives/3D/cylinder.png +0 -0
  106. benchmarks/sdf_showcase/primitives/3D/dodecahedron.png +0 -0
  107. benchmarks/sdf_showcase/primitives/3D/ellipsoid.png +0 -0
  108. benchmarks/sdf_showcase/primitives/3D/icosahedron.png +0 -0
  109. benchmarks/sdf_showcase/primitives/3D/octahedron.png +0 -0
  110. benchmarks/sdf_showcase/primitives/3D/pyramid.png +0 -0
  111. benchmarks/sdf_showcase/primitives/3D/rounded_box.png +0 -0
  112. benchmarks/sdf_showcase/primitives/3D/rounded_cone.png +0 -0
  113. benchmarks/sdf_showcase/primitives/3D/rounded_cylinder.png +0 -0
  114. benchmarks/sdf_showcase/primitives/3D/sphere.png +0 -0
  115. benchmarks/sdf_showcase/primitives/3D/tetrahedron.png +0 -0
  116. benchmarks/sdf_showcase/primitives/3D/torus.png +0 -0
  117. benchmarks/sdf_showcase/primitives/3D/wireframe_box.png +0 -0
  118. deepsdfstruct-1.7.2.dist-info/METADATA +44 -0
  119. deepsdfstruct-1.7.2.dist-info/RECORD +165 -0
  120. deepsdfstruct-1.7.2.dist-info/WHEEL +5 -0
  121. deepsdfstruct-1.7.2.dist-info/licenses/LICENSE +208 -0
  122. deepsdfstruct-1.7.2.dist-info/licenses/NOTICE +67 -0
  123. deepsdfstruct-1.7.2.dist-info/scm_file_list.json +178 -0
  124. deepsdfstruct-1.7.2.dist-info/scm_version.json +8 -0
  125. deepsdfstruct-1.7.2.dist-info/top_level.txt +4 -0
  126. docs/Makefile +20 -0
  127. docs/api_reference.rst +6 -0
  128. docs/conf.py +60 -0
  129. docs/index.rst +136 -0
  130. docs/make.bat +35 -0
  131. docs/qr_code_github.png +0 -0
  132. docs/qr_code_paper.png +0 -0
  133. docs/readme_images/example_output_01.png +0 -0
  134. docs/readme_images/example_output_02.png +0 -0
  135. docs/readme_images/example_output_03.png +0 -0
  136. docs/readme_images/example_output_04.png +0 -0
  137. tests/data/chairs/1005.obj +282 -0
  138. tests/data/chairs/1024.obj +222 -0
  139. tests/data/chairs/README.md +1 -0
  140. tests/data/circular_balls_test_case.stl +0 -0
  141. tests/data/cone.stl +1794 -0
  142. tests/data/example_disconnectd_mesh.inp +1784 -0
  143. tests/data/example_line_mesh.vtk +0 -0
  144. tests/data/stanford_bunny.stl +0 -0
  145. tests/data/sweep_test_case.stl +0 -0
  146. tests/test_DOE.py +49 -0
  147. tests/test_elongate_sdf.py +195 -0
  148. tests/test_flexisquares.py +187 -0
  149. tests/test_generate_dataset.py +76 -0
  150. tests/test_lattice_evaluation.py +137 -0
  151. tests/test_mesh_export.py +137 -0
  152. tests/test_mesh_functions.py +49 -0
  153. tests/test_networks.py +71 -0
  154. tests/test_pretrained_models.py +40 -0
  155. tests/test_reconstruction.py +101 -0
  156. tests/test_sdf_from_mesh.py +52 -0
  157. tests/test_sdf_functions.py +117 -0
  158. tests/test_sdf_primitives.py +668 -0
  159. tests/test_sdf_trimesh_comparison.py +482 -0
  160. tests/test_splinepy_unitcells.py +64 -0
  161. tests/test_structural_optimization.py +178 -0
  162. tests/test_torch_spline.py +216 -0
  163. tests/test_train_model.py +110 -0
  164. tests/test_training_data_ids.py +41 -0
  165. tests/tmp_outputs/temp_file.txt +0 -0
@@ -0,0 +1,8 @@
1
+ """
2
+ DeepSDF Neural Network Architectures
3
+ ====================================
4
+
5
+ This module provides neural network decoder architectures for DeepSDF models.
6
+ Each architecture implements a different approach to decoding latent codes
7
+ into SDF values.
8
+ """
@@ -0,0 +1,105 @@
1
+ """
2
+ Analytic Round Cross Decoder Network
3
+ ====================================
4
+
5
+ This module implements a specialized decoder architecture for round cross
6
+ lattice structures with analytical parametrization. The network incorporates
7
+ geometric priors specific to cross-shaped lattice unit cells.
8
+
9
+ The architecture is similar to the standard DeepSDF decoder but may include
10
+ specialized layers or constraints to better represent the round cross geometry
11
+ family, which is common in mechanical metamaterials.
12
+ """
13
+
14
+ #!/usr/bin/env python3
15
+ # Copyright 2004-present Facebook. All Rights Reserved.
16
+
17
+ import torch.nn as nn
18
+ import torch
19
+
20
+
21
+ class RoundCrossDecoder(nn.Module):
22
+ def __init__(
23
+ self,
24
+ latent_size,
25
+ dims,
26
+ geom_dimension,
27
+ dropout=None,
28
+ dropout_prob=0.0,
29
+ norm_layers=(),
30
+ latent_in=(),
31
+ weight_norm=False,
32
+ xyz_in_all=None,
33
+ use_tanh=False,
34
+ latent_dropout=False,
35
+ ):
36
+ super(RoundCrossDecoder, self).__init__()
37
+
38
+ def make_sequence():
39
+ return []
40
+
41
+ dims = [latent_size + geom_dimension] + dims + [1]
42
+
43
+ self.num_layers = len(dims)
44
+ self.geom_dimension = geom_dimension
45
+ self.norm_layers = norm_layers
46
+ self.latent_in = latent_in
47
+ self.latent_dropout = latent_dropout
48
+ if self.latent_dropout:
49
+ self.lat_dp = nn.Dropout(0.2)
50
+
51
+ self.xyz_in_all = xyz_in_all
52
+ self.weight_norm = weight_norm
53
+
54
+ for layer in range(0, self.num_layers - 1):
55
+ if layer + 1 in latent_in:
56
+ out_dim = dims[layer + 1] - dims[0]
57
+ else:
58
+ out_dim = dims[layer + 1]
59
+ if self.xyz_in_all and layer != self.num_layers - 2:
60
+ out_dim -= geom_dimension
61
+
62
+ if weight_norm and layer in self.norm_layers:
63
+ setattr(
64
+ self,
65
+ "lin" + str(layer),
66
+ nn.utils.parametrizations.weight_norm(
67
+ nn.Linear(dims[layer], out_dim)
68
+ ),
69
+ )
70
+ else:
71
+ setattr(self, "lin" + str(layer), nn.Linear(dims[layer], out_dim))
72
+
73
+ if (
74
+ (not weight_norm)
75
+ and self.norm_layers is not None
76
+ and layer in self.norm_layers
77
+ ):
78
+ setattr(self, "bn" + str(layer), nn.LayerNorm(out_dim))
79
+
80
+ self.use_tanh = use_tanh
81
+ if use_tanh:
82
+ self.tanh = nn.Tanh()
83
+ self.relu = nn.ReLU()
84
+
85
+ self.dropout_prob = dropout_prob
86
+ self.dropout = dropout
87
+ self.th = nn.Tanh()
88
+
89
+ # input: N x (L+3), or N x (L+geom_dimension)
90
+ def forward(self, input):
91
+ xyz = input[:, -self.geom_dimension :]
92
+ r = input[:, 0]
93
+ output = torch.linalg.norm(xyz, dim=1, ord=torch.inf)
94
+
95
+ # add x cylinder
96
+ cylinder = torch.sqrt(xyz[:, 1] ** 2 + xyz[:, 2] ** 2) - r
97
+ output = torch.minimum(output, cylinder)
98
+ # add y cylinder
99
+ cylinder = torch.sqrt(xyz[:, 0] ** 2 + xyz[:, 2] ** 2) - r
100
+ output = torch.minimum(output, cylinder)
101
+ # add z cylinder
102
+ cylinder = torch.sqrt(xyz[:, 0] ** 2 + xyz[:, 1] ** 2) - r
103
+ output = torch.minimum(output, cylinder)
104
+
105
+ return output.reshape(-1, 1)
@@ -0,0 +1,155 @@
1
+ """
2
+ Standard DeepSDF Decoder Network
3
+ ================================
4
+
5
+ This module implements the standard DeepSDF decoder architecture from
6
+ Park et al. (2019). The network takes a latent code and 3D coordinates
7
+ as input and outputs a signed distance value.
8
+
9
+ Architecture
10
+ ------------
11
+ The decoder is a multi-layer perceptron (MLP) with:
12
+ - Concatenation of latent code and spatial coordinates at input
13
+ - Optional skip connections for latent code injection
14
+ - Optional coordinate injection at multiple layers
15
+ - Batch normalization or weight normalization
16
+ - ReLU activations (or tanh for final layer)
17
+ - Dropout for regularization
18
+
19
+ The network learns to map from a latent space to implicit SDF representations,
20
+ enabling compact encoding of complex 3D geometries.
21
+ """
22
+
23
+ #!/usr/bin/env python3
24
+ # Copyright 2004-present Facebook. All Rights Reserved.
25
+
26
+ import torch.nn as nn
27
+ import torch
28
+ import torch.nn.functional as F
29
+
30
+
31
+ class DeepSDFDecoder(nn.Module):
32
+ def __init__(
33
+ self,
34
+ latent_size,
35
+ dims,
36
+ geom_dimension,
37
+ dropout=None,
38
+ dropout_prob=0.0,
39
+ norm_layers=(),
40
+ latent_in=(),
41
+ weight_norm=False,
42
+ xyz_in_all=None,
43
+ use_tanh=False,
44
+ latent_dropout=False,
45
+ homogen_predictor_dims=None,
46
+ ):
47
+ super(DeepSDFDecoder, self).__init__()
48
+
49
+ def make_sequence():
50
+ return []
51
+
52
+ dims = [latent_size + geom_dimension] + dims + [1]
53
+
54
+ self.num_layers = len(dims)
55
+ self.geom_dimension = geom_dimension
56
+ self.norm_layers = norm_layers
57
+ self.latent_in = latent_in
58
+ self.latent_dropout = latent_dropout
59
+ if self.latent_dropout:
60
+ self.lat_dp = nn.Dropout(0.2)
61
+
62
+ self.xyz_in_all = xyz_in_all
63
+ self.weight_norm = weight_norm
64
+
65
+ for layer in range(0, self.num_layers - 1):
66
+ if layer + 1 in latent_in:
67
+ out_dim = dims[layer + 1] - dims[0]
68
+ else:
69
+ out_dim = dims[layer + 1]
70
+ if self.xyz_in_all and layer != self.num_layers - 2:
71
+ out_dim -= geom_dimension
72
+
73
+ if weight_norm and layer in self.norm_layers:
74
+ setattr(
75
+ self,
76
+ "lin" + str(layer),
77
+ nn.utils.parametrizations.weight_norm(
78
+ nn.Linear(dims[layer], out_dim)
79
+ ),
80
+ )
81
+ else:
82
+ setattr(self, "lin" + str(layer), nn.Linear(dims[layer], out_dim))
83
+
84
+ if (
85
+ (not weight_norm)
86
+ and self.norm_layers is not None
87
+ and layer in self.norm_layers
88
+ ):
89
+ setattr(self, "bn" + str(layer), nn.LayerNorm(out_dim))
90
+
91
+ self.use_tanh = use_tanh
92
+ if use_tanh:
93
+ self.tanh = nn.Tanh()
94
+ self.relu = nn.ReLU()
95
+
96
+ self.dropout_prob = dropout_prob
97
+ self.dropout = dropout
98
+ self.th = nn.Tanh()
99
+ if homogen_predictor_dims is not None:
100
+ hp_dims = [latent_size] + homogen_predictor_dims
101
+ homogen_layers = []
102
+
103
+ for i in range(len(hp_dims) - 1):
104
+ homogen_layers.append(nn.Linear(hp_dims[i], hp_dims[i + 1]))
105
+
106
+ # No activation after final layer
107
+ if i < len(hp_dims) - 2:
108
+ homogen_layers.append(nn.ReLU())
109
+
110
+ self.homogen_network = nn.Sequential(*homogen_layers)
111
+ else:
112
+ self.homogen_network = None
113
+
114
+ # input: N x (L+3), or N x (L+geom_dimension)
115
+ def forward(self, input):
116
+ xyz = input[:, -self.geom_dimension :]
117
+
118
+ if input.shape[1] > self.geom_dimension and self.latent_dropout:
119
+ latent_vecs = input[:, : -self.geom_dimension]
120
+ latent_vecs = F.dropout(latent_vecs, p=0.2, training=self.training)
121
+ x = torch.cat([latent_vecs, xyz], 1)
122
+ else:
123
+ x = input
124
+
125
+ for layer in range(0, self.num_layers - 1):
126
+ lin = getattr(self, "lin" + str(layer))
127
+ if layer in self.latent_in:
128
+ x = torch.cat([x, input], 1)
129
+ elif layer != 0 and self.xyz_in_all:
130
+ x = torch.cat([x, xyz], 1)
131
+ x = lin(x)
132
+ # last layer Tanh
133
+ if layer == self.num_layers - 2 and self.use_tanh:
134
+ x = self.tanh(x)
135
+ if layer < self.num_layers - 2:
136
+ if (
137
+ self.norm_layers is not None
138
+ and layer in self.norm_layers
139
+ and not self.weight_norm
140
+ ):
141
+ bn = getattr(self, "bn" + str(layer))
142
+ x = bn(x)
143
+ x = self.relu(x)
144
+ if self.dropout is not None and layer in self.dropout:
145
+ x = F.dropout(x, p=self.dropout_prob, training=self.training)
146
+
147
+ return x
148
+
149
+ def predict_C_homogenized(self, latent_vec):
150
+ if self.homogen_network is not None:
151
+ return self.homogen_network(latent_vec)
152
+ else:
153
+ raise RuntimeError(
154
+ "Cannot predict homogenized elasticity tensor, because homogenization network is not defined"
155
+ )
@@ -0,0 +1,139 @@
1
+ """
2
+ Hierarchical DeepSDF Decoder Network
3
+ ====================================
4
+
5
+ This module implements a hierarchical decoder architecture that injects
6
+ latent codes at multiple layers rather than only at the input. This enables
7
+ multi-scale geometric control and better representation of complex features.
8
+
9
+ Architecture
10
+ ------------
11
+ Unlike the standard DeepSDF decoder, this architecture:
12
+ - Accepts multiple latent vectors of potentially different sizes
13
+ - Injects each latent at a specified layer depth
14
+ - Enables hierarchical control from coarse to fine details
15
+ - Supports various activation functions (ReLU, GELU, SiLU, etc.)
16
+
17
+ The hierarchical structure is particularly useful for:
18
+ - Multi-resolution geometric features
19
+ - Compositional shape generation
20
+ - Transfer learning across scales
21
+ - Fine-tuning specific detail levels
22
+
23
+ This architecture is inspired by progressive training and multi-scale
24
+ representations in generative models.
25
+ """
26
+
27
+ #!/usr/bin/env python3
28
+ # Copyright 2004-present Facebook. All Rights Reserved.
29
+
30
+ import torch.nn as nn
31
+ import torch
32
+ import torch.nn.functional as F
33
+ from DeepSDFStruct.deep_sdf.nn_utils import activations
34
+
35
+
36
+ class HierachicalDeepSDFDecoder(nn.Module):
37
+ def __init__(
38
+ self,
39
+ latent_size,
40
+ dims,
41
+ geom_dimension,
42
+ dropout=None,
43
+ dropout_prob=0.0,
44
+ latent_in=[],
45
+ weight_norm=False,
46
+ norm_layers=[],
47
+ xyz_in_all=None,
48
+ activation_fun="relu",
49
+ latent_dropout=False,
50
+ ):
51
+ super(HierachicalDeepSDFDecoder, self).__init__()
52
+
53
+ self.layer_dims = [geom_dimension] + dims + [1]
54
+ assert len(latent_size) == len(
55
+ latent_in
56
+ ), "latent shape must match latent in shape"
57
+ # add latent dimensions to inputs
58
+ self.latent_dims = {}
59
+ for lat_size, index in zip(latent_size, latent_in):
60
+ self.layer_dims[index] = self.layer_dims[index] + lat_size
61
+ self.latent_dims[index] = lat_size
62
+
63
+ self.num_layers = len(dims)
64
+ self.geom_dimension = geom_dimension
65
+ self.latent_size = latent_size
66
+ self.latent_in = latent_in
67
+ self.latent_dropout = latent_dropout
68
+ if self.latent_dropout:
69
+ self.lat_dp = nn.Dropout(0.2)
70
+
71
+ self.xyz_in_all = xyz_in_all
72
+ self.weight_norm = weight_norm
73
+ self.norm_layers = norm_layers
74
+
75
+ for layer in range(0, len(self.layer_dims) - 1):
76
+ if layer + 1 in latent_in:
77
+ out_dim = self.layer_dims[layer + 1] - self.latent_dims[layer + 1]
78
+ else:
79
+ out_dim = self.layer_dims[layer + 1]
80
+
81
+ if weight_norm and layer in self.norm_layers:
82
+ setattr(
83
+ self,
84
+ "lin" + str(layer),
85
+ nn.utils.parametrizations.weight_norm(
86
+ nn.Linear(self.layer_dims[layer], out_dim)
87
+ ),
88
+ )
89
+ else:
90
+ setattr(
91
+ self, "lin" + str(layer), nn.Linear(self.layer_dims[layer], out_dim)
92
+ )
93
+
94
+ if activation_fun not in activations:
95
+ raise ValueError(
96
+ f"Unsupported activation function '{activation_fun}'. "
97
+ f"Available options are: {list(activations.keys())}"
98
+ )
99
+
100
+ self.activation = activations[activation_fun]
101
+
102
+ self.dropout_prob = dropout_prob
103
+ self.dropout = dropout
104
+ self.th = nn.Tanh()
105
+
106
+ # input: N x (L+3), or N x (L+geom_dimension)
107
+ def forward(self, input):
108
+ xyz = input[:, -self.geom_dimension :]
109
+ latent_vecs = input[:, : -self.geom_dimension]
110
+ latent_chunks = torch.split(latent_vecs, self.latent_size, dim=1)
111
+
112
+ if self.latent_dropout:
113
+ latent_vecs = F.dropout(latent_vecs, p=0.2, training=self.training)
114
+ x = xyz
115
+
116
+ for layer in range(0, len(self.layer_dims) - 1):
117
+ lin = getattr(self, "lin" + str(layer))
118
+ if layer in self.latent_in:
119
+ latent_idx = self.latent_in.index(layer)
120
+ latent_chunk = latent_chunks[latent_idx]
121
+ x = torch.cat([x, latent_chunk], 1)
122
+ # print(f"Latent chunk in layer {layer}: {latent_chunk}")
123
+ x = lin(x)
124
+
125
+ if layer < self.num_layers - 2:
126
+ if (
127
+ self.norm_layers is not None
128
+ and layer in self.norm_layers
129
+ and not self.weight_norm
130
+ ):
131
+ bn = getattr(self, "bn" + str(layer))
132
+ x = bn(x)
133
+ if self.dropout is not None and layer in self.dropout:
134
+ x = F.dropout(x, p=self.dropout_prob, training=self.training)
135
+
136
+ if layer <= (self.num_layers - 2):
137
+ x = self.activation(x)
138
+
139
+ return x
@@ -0,0 +1,190 @@
1
+ import torch
2
+ import torch.nn as nn
3
+ import logging
4
+ import torch.nn.functional as F
5
+ from DeepSDFStruct.deep_sdf.nn_utils import activations
6
+
7
+ logger = logging.getLogger("DeepSDFStruct")
8
+
9
+
10
+ class HierachicalPositionalDeepSDFDecoder(nn.Module):
11
+ def __init__(
12
+ self,
13
+ latent_size,
14
+ dims,
15
+ geom_dimension,
16
+ dropout=None,
17
+ dropout_prob=0.0,
18
+ latent_in=[],
19
+ weight_norm=False,
20
+ norm_layers=[],
21
+ xyz_in_all=None,
22
+ positional_encoding=False,
23
+ pe_levels=6,
24
+ split_latent=False,
25
+ activation_fun="relu",
26
+ latent_dropout=False,
27
+ use_tanh=None,
28
+ ):
29
+ super(HierachicalPositionalDeepSDFDecoder, self).__init__()
30
+ if use_tanh is not None:
31
+ logger.warning("Use tanh is deprecated, use the activation_fun argument")
32
+
33
+ # -----------------------------------------------------
34
+ # Positional Encoding
35
+ # -----------------------------------------------------
36
+ self.use_pe = positional_encoding
37
+ self.pe_levels = pe_levels
38
+
39
+ if positional_encoding:
40
+ # PE increases dimensionality from D -> D * (2*L + 1)
41
+ self.xyz_dim = geom_dimension * (2 * pe_levels + 1)
42
+ else:
43
+ self.xyz_dim = geom_dimension
44
+
45
+ self.geom_dimension = geom_dimension
46
+
47
+ # -----------------------------------------------------
48
+ # Base MLP structure
49
+ # layer_dims[i] = input dimension of layer i
50
+ # -----------------------------------------------------
51
+ self.layer_dims = [
52
+ self.xyz_dim
53
+ ] + dims # final layer out_dim is handled separately
54
+ self.num_layers = len(dims)
55
+
56
+ # -----------------------------------------------------
57
+ # Latent injection logic
58
+ # -----------------------------------------------------
59
+ self.split_latent = split_latent
60
+ self.latent_in = latent_in
61
+ self.latent_size = latent_size
62
+ self.latent_dropout = latent_dropout
63
+
64
+ if self.split_latent:
65
+ assert (
66
+ latent_size % len(latent_in) == 0
67
+ ), "latent_size must be divisible by number of latent injection layers"
68
+ chunk_size = latent_size // len(latent_in)
69
+ latent_chunk_sizes = [chunk_size] * len(latent_in)
70
+ else:
71
+ latent_chunk_sizes = [latent_size] * len(latent_in)
72
+
73
+ self.latent_dims = {} # map layer → latent_chunk_dim
74
+ for size, layer_idx in zip(latent_chunk_sizes, latent_in):
75
+ self.layer_dims[layer_idx] += size
76
+ self.latent_dims[layer_idx] = size
77
+
78
+ # -----------------------------------------------------
79
+ # XYZ skip connection logic (xyz_in_all)
80
+ # xyz is added BEFORE the linear layer, after activation,
81
+ # but only for layers > 0
82
+ # -----------------------------------------------------
83
+ self.xyz_in_all = xyz_in_all
84
+ if xyz_in_all:
85
+ for layer in range(1, self.num_layers):
86
+ self.layer_dims[layer] += self.xyz_dim
87
+
88
+ # -----------------------------------------------------
89
+ # Linear layer construction
90
+ # -----------------------------------------------------
91
+ self.weight_norm = weight_norm
92
+ self.norm_layers = norm_layers
93
+
94
+ for layer in range(self.num_layers):
95
+ in_dim = self.layer_dims[layer]
96
+ out_dim = dims[layer]
97
+
98
+ linear = nn.Linear(in_dim, out_dim)
99
+ if weight_norm and layer in norm_layers:
100
+ linear = nn.utils.parametrizations.weight_norm(linear)
101
+
102
+ setattr(self, f"lin{layer}", linear)
103
+
104
+ # Output layer (final layer: size dims[-1] → 1)
105
+ self.output_layer = nn.Linear(dims[-1], 1)
106
+
107
+ # -----------------------------------------------------
108
+ # Normalization + Dropout
109
+ # -----------------------------------------------------
110
+ self.dropout_prob = dropout_prob
111
+ self.dropout = dropout
112
+ if dropout is not None:
113
+ self.dropout_layers = set(dropout)
114
+ else:
115
+ self.dropout_layers = set()
116
+
117
+ # BatchNorm for chosen layers
118
+ for layer in norm_layers:
119
+ setattr(self, f"bn{layer}", nn.BatchNorm1d(dims[layer]))
120
+
121
+ # Latent dropout
122
+ if latent_dropout:
123
+ self.lat_dp = nn.Dropout(0.2)
124
+
125
+ # -----------------------------------------------------
126
+ # Activation
127
+ # -----------------------------------------------------
128
+ if activation_fun not in activations:
129
+ raise ValueError(
130
+ f"Unsupported activation function {activation_fun}. "
131
+ f"Choose from: {list(activations.keys())}"
132
+ )
133
+ self.activation = activations[activation_fun]
134
+
135
+ # ----------- Positional Encoding -----------
136
+ def positional_encoding(self, xyz):
137
+ """
138
+ Applies sinusoidal positional encoding to each of the xyz dims.
139
+ Output shape: [B, 3*(2*L+1)]
140
+ """
141
+ enc = [xyz]
142
+ for i in range(self.pe_levels):
143
+ freq = 2**i
144
+ enc.append(torch.sin(freq * xyz))
145
+ enc.append(torch.cos(freq * xyz))
146
+ return torch.cat(enc, dim=-1)
147
+
148
+ # input: N x (L+3), or N x (L+geom_dimension)
149
+ def forward(self, input: torch.Tensor):
150
+ xyz = input[:, -self.geom_dimension :]
151
+ latent_vecs = input[:, : -self.geom_dimension]
152
+ if self.latent_dropout:
153
+ latent_vecs = F.dropout(latent_vecs, p=0.2, training=self.training)
154
+ if self.split_latent:
155
+ latent_chunks = torch.split(
156
+ latent_vecs, int(self.latent_size / len(self.latent_in)), dim=1
157
+ )
158
+ else:
159
+ latent_chunks = [latent_vecs for _ in self.latent_in]
160
+
161
+ if self.use_pe:
162
+ xyz = self.positional_encoding(xyz)
163
+ x = xyz
164
+
165
+ for layer in range(0, len(self.layer_dims) - 1):
166
+ lin = getattr(self, "lin" + str(layer))
167
+ if layer in self.latent_in:
168
+ latent_idx = self.latent_in.index(layer)
169
+ latent_chunk = latent_chunks[latent_idx]
170
+ x = torch.cat([x, latent_chunk], 1)
171
+
172
+ if layer != 0 and self.xyz_in_all:
173
+ x = torch.cat([x, xyz])
174
+ x = lin(x)
175
+
176
+ if layer < self.num_layers - 2:
177
+ if (
178
+ self.norm_layers is not None
179
+ and layer in self.norm_layers
180
+ and not self.weight_norm
181
+ ):
182
+ bn = getattr(self, "bn" + str(layer))
183
+ x = bn(x)
184
+ if self.dropout is not None and layer in self.dropout:
185
+ x = F.dropout(x, p=self.dropout_prob, training=self.training)
186
+
187
+ if layer <= (self.num_layers - 2):
188
+ x = self.activation(x)
189
+
190
+ return self.output_layer(x)
@@ -0,0 +1,108 @@
1
+ import torch
2
+ import torch.nn as nn
3
+ import logging
4
+
5
+ logger = logging.getLogger("DeepSDFStruct")
6
+
7
+
8
+ class ResNetPositionalDeepSDFDecoder(nn.Module):
9
+ def __init__(
10
+ self,
11
+ latent_size,
12
+ dims,
13
+ dropout=None,
14
+ dropout_prob=None,
15
+ norm_layers=None,
16
+ latent_in=None,
17
+ xyz_in_all=False,
18
+ latent_dropout=False,
19
+ weight_norm=True,
20
+ use_tanh=None,
21
+ geom_dimension=3,
22
+ activation_fun="tanh",
23
+ positional_encoding=False,
24
+ pe_levels=6, # number of frequency bands if PE enabled
25
+ resnet=False,
26
+ resnet_every=2, # insert residual connection every N layers
27
+ ):
28
+ super().__init__()
29
+ if use_tanh is not None:
30
+ logger.warning("Use tanh is deprecated, use the activation_fun argument")
31
+ self.geom_dimension = geom_dimension
32
+ self.use_pe = positional_encoding
33
+ self.pe_levels = pe_levels
34
+ self.use_resnet = resnet
35
+ self.resnet_every = resnet_every
36
+
37
+ # Optional positional encoding increases xyz dimension
38
+ input_geom_dim = geom_dimension
39
+ if positional_encoding:
40
+ input_geom_dim = geom_dimension * (2 * pe_levels + 1)
41
+
42
+ # MLP layer dims
43
+ dims = [latent_size + input_geom_dim] + dims + [1]
44
+ self.num_layers = len(dims) - 1
45
+
46
+ if activation_fun == "tanh":
47
+ self.act = nn.Tanh()
48
+ elif activation_fun == "relu":
49
+ self.act = nn.ReLU()
50
+ elif activation_fun == "leaky_relu":
51
+ self.act = nn.LeakyReLU(0.2)
52
+ elif activation_fun == "silu":
53
+ self.act = nn.SiLU()
54
+ elif activation_fun == "sigmoid":
55
+ self.act = nn.Sigmoid()
56
+ else:
57
+ raise ValueError(f"Unknown activation function: {activation_fun}")
58
+
59
+ # Construct layers
60
+ self.layers = nn.ModuleList()
61
+ for i in range(self.num_layers):
62
+ self.layers.append(nn.Linear(dims[i], dims[i + 1]))
63
+
64
+ # ----------- Positional Encoding -----------
65
+ def positional_encoding(self, xyz):
66
+ """
67
+ Applies sinusoidal positional encoding to each of the xyz dims.
68
+ Output shape: [B, 3*(2*L+1)]
69
+ """
70
+ enc = [xyz]
71
+ for i in range(self.pe_levels):
72
+ freq = 2**i
73
+ enc.append(torch.sin(freq * xyz))
74
+ enc.append(torch.cos(freq * xyz))
75
+ return torch.cat(enc, dim=-1)
76
+
77
+ # ---------------- Forward ------------------
78
+ def forward(self, input):
79
+ """
80
+ input: [B, latent + xyz]
81
+ """
82
+ xyz = input[:, -self.geom_dimension :]
83
+ latent = input[:, : -self.geom_dimension]
84
+
85
+ if self.use_pe:
86
+ xyz = self.positional_encoding(xyz)
87
+
88
+ x = torch.cat([latent, xyz], dim=-1)
89
+
90
+ residual = None
91
+ for i, layer in enumerate(self.layers):
92
+ x_in = x
93
+ x = layer(x)
94
+ if i < self.num_layers - 1:
95
+ x = self.act(x)
96
+
97
+ if self.use_resnet and i % self.resnet_every == 0:
98
+
99
+ if residual is None:
100
+ residual = x_in
101
+ else:
102
+
103
+ if i < self.num_layers - 1:
104
+ if residual.shape == x.shape:
105
+ x = x + residual
106
+ residual = x_in
107
+
108
+ return x