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,363 @@
1
+ """
2
+ Experiment Workspace Management
3
+ ===============================
4
+
5
+ This module provides utilities for managing DeepSDF experiment workspaces,
6
+ including directory structures, file naming conventions, and model loading/saving.
7
+
8
+ Constants
9
+ ---------
10
+ The module defines standard subdirectory and file names for organizing
11
+ experiment artifacts:
12
+ - Model parameters and checkpoints
13
+ - Optimizer states
14
+ - Latent code vectors
15
+ - Training logs and plots
16
+ - Reconstructions and evaluations
17
+ - Dataset samples and normalization parameters
18
+
19
+ Architecture Registry
20
+ --------------------
21
+ ARCHITECTURES: dict
22
+ Maps architecture names to decoder classes, enabling dynamic model
23
+ instantiation from configuration files.
24
+
25
+ Functions
26
+ ---------
27
+
28
+ load_experiment_specifications
29
+ Load experiment configuration from specs.json file.
30
+
31
+ load_trained_model
32
+ Load a trained decoder network from checkpoint.
33
+
34
+ load_latent_vectors
35
+ Load learned latent codes from checkpoint.
36
+
37
+ create_experiment_directory
38
+ Initialize directory structure for a new experiment.
39
+
40
+ The workspace utilities ensure consistent organization across experiments
41
+ and simplify model loading for inference and continued training.
42
+ """
43
+
44
+ #!/usr/bin/env python3
45
+ # Copyright 2004-present Facebook. All Rights Reserved.
46
+
47
+ import json
48
+ import os
49
+ import pathlib
50
+ import torch
51
+ from typing import TypedDict
52
+
53
+ from .networks.analytic_round_cross import RoundCrossDecoder
54
+ from .networks.deep_sdf_decoder import DeepSDFDecoder
55
+ from .networks.hierarchical_deep_sdf_decoder import HierachicalDeepSDFDecoder
56
+ from .networks.resnet_positional_sdf_decoder import ResNetPositionalDeepSDFDecoder
57
+ from .networks.hierarchical_positional_sdf_decoder import (
58
+ HierachicalPositionalDeepSDFDecoder,
59
+ )
60
+
61
+ screenshots_subdir = "Screenshots"
62
+ model_params_subdir = "ModelParameters"
63
+ optimizer_params_subdir = "OptimizerParameters"
64
+ latent_codes_subdir = "LatentCodes"
65
+ latent_code_data_map_filename = "latent_code_data_map.json"
66
+ logs_filename = "Logs.pth"
67
+ logplot_filename = "Logs.png"
68
+ reconstructions_subdir = "Reconstructions"
69
+ reconstruction_meshes_subdir = "Meshes"
70
+ reconstruction_codes_subdir = "Codes"
71
+ specifications_filename = "specs.json"
72
+ data_source_map_filename = ".datasources.json"
73
+ evaluation_subdir = "Evaluation"
74
+ sdf_samples_subdir = "SdfSamples"
75
+ surface_samples_subdir = "SurfaceSamples"
76
+ normalization_param_subdir = "NormalizationParameters"
77
+ training_meshes_subdir = "TrainingMeshes"
78
+ experiment_summary_name = "training_summary.json"
79
+
80
+ # Map architecture name to Decoder class
81
+ ARCHITECTURES = {
82
+ "analytic_round_cross": RoundCrossDecoder,
83
+ "deep_sdf_decoder": DeepSDFDecoder,
84
+ "hierarchical_deep_sdf_decoder": HierachicalDeepSDFDecoder,
85
+ "resnet_positional_deep_sdf_decoder": ResNetPositionalDeepSDFDecoder,
86
+ "hierarchical_positional_deep_sdf_decoder": HierachicalPositionalDeepSDFDecoder,
87
+ }
88
+
89
+
90
+ def load_experiment_specifications(experiment_directory):
91
+
92
+ filename = os.path.join(experiment_directory, specifications_filename)
93
+
94
+ if not os.path.isfile(filename):
95
+ raise Exception(
96
+ f"The experiment directory ({experiment_directory}) does not include specifications file "
97
+ + '"specs.json"'
98
+ )
99
+ with open(filename, "r", encoding="utf-8") as f:
100
+ return json.load(f)
101
+
102
+
103
+ def load_latent_vectors(experiment_directory, checkpoint, device):
104
+
105
+ filename = os.path.join(
106
+ experiment_directory, latent_codes_subdir, checkpoint + ".pth"
107
+ )
108
+
109
+ if not os.path.isfile(filename):
110
+ raise Exception(
111
+ f"The experiment directory ({experiment_directory}) does not include a latent code file"
112
+ + f" for checkpoint '{checkpoint}'"
113
+ )
114
+
115
+ data = torch.load(filename, map_location=device, weights_only=True)
116
+
117
+ if isinstance(data["latent_codes"], torch.Tensor):
118
+
119
+ num_vecs = data["latent_codes"].size()[0]
120
+
121
+ lat_vecs = []
122
+ for i in range(num_vecs):
123
+ lat_vecs.append(data["latent_codes"][i])
124
+
125
+ return lat_vecs
126
+
127
+ else:
128
+
129
+ num_embeddings, embedding_dim = data["latent_codes"]["weight"].shape
130
+
131
+ lat_vecs = torch.nn.Embedding(num_embeddings, embedding_dim, device=device)
132
+
133
+ lat_vecs.load_state_dict(data["latent_codes"])
134
+
135
+ return lat_vecs.weight.data.detach()
136
+
137
+
138
+ def load_model_parameters(
139
+ experiment_directory, checkpoint, decoder: torch.nn.Module, device
140
+ ):
141
+
142
+ filename = os.path.join(
143
+ experiment_directory, model_params_subdir, checkpoint + ".pth"
144
+ )
145
+
146
+ if not os.path.isfile(filename):
147
+ raise Exception('model state dict "{}" does not exist'.format(filename))
148
+
149
+ data = torch.load(filename, map_location=device, weights_only=True)
150
+
151
+ decoder.load_state_dict(data["model_state_dict"], strict=False)
152
+
153
+ return data["epoch"]
154
+
155
+
156
+ def load_optimizer(
157
+ experiment_directory, checkpoint, optimizer: torch.nn.Module, device
158
+ ):
159
+
160
+ filename = os.path.join(
161
+ experiment_directory, optimizer_params_subdir, checkpoint + ".pth"
162
+ )
163
+
164
+ if not os.path.isfile(filename):
165
+ raise Exception(f'optimizer state dict "{filename}" does not exist')
166
+
167
+ data = torch.load(filename, map_location=device, weights_only=True)
168
+
169
+ optimizer.load_state_dict(data["optimizer_state_dict"])
170
+
171
+ return data["epoch"]
172
+
173
+
174
+ def get_data_source_map_filename(data_dir):
175
+ return os.path.join(data_dir, data_source_map_filename)
176
+
177
+
178
+ def get_reconstructed_mesh_filename(
179
+ experiment_dir,
180
+ epoch,
181
+ dataset,
182
+ class_name,
183
+ instance_name,
184
+ create_dir=True,
185
+ filetype="ply",
186
+ ):
187
+ fname_raw = os.path.join(
188
+ experiment_dir,
189
+ reconstructions_subdir,
190
+ str(epoch),
191
+ reconstruction_meshes_subdir,
192
+ dataset,
193
+ class_name,
194
+ instance_name + "." + filetype,
195
+ )
196
+ fname = pathlib.Path(fname_raw)
197
+ if not os.path.isdir(fname.parent) and create_dir:
198
+ os.makedirs(fname.parent)
199
+ return fname
200
+
201
+
202
+ def get_reconstructed_code_filename(
203
+ experiment_dir, epoch, dataset, class_name, instance_name
204
+ ):
205
+
206
+ return os.path.join(
207
+ experiment_dir,
208
+ reconstructions_subdir,
209
+ str(epoch),
210
+ reconstruction_codes_subdir,
211
+ dataset,
212
+ class_name,
213
+ instance_name + ".pth",
214
+ )
215
+
216
+
217
+ def get_evaluation_dir(experiment_dir, checkpoint, create_if_nonexistent=False):
218
+
219
+ dir = os.path.join(experiment_dir, evaluation_subdir, checkpoint)
220
+
221
+ if create_if_nonexistent and not os.path.isdir(dir):
222
+ os.makedirs(dir)
223
+
224
+ return dir
225
+
226
+
227
+ def get_model_params_dir(experiment_dir, create_if_nonexistent=False):
228
+
229
+ dir = os.path.join(experiment_dir, model_params_subdir)
230
+
231
+ if create_if_nonexistent and not os.path.isdir(dir):
232
+ os.makedirs(dir)
233
+
234
+ return dir
235
+
236
+
237
+ def get_screenshots_dir(experiment_dir, create_if_nonexistent=True):
238
+
239
+ dir = os.path.join(experiment_dir, screenshots_subdir)
240
+
241
+ if create_if_nonexistent and not os.path.isdir(dir):
242
+ os.makedirs(dir)
243
+
244
+ return dir
245
+
246
+
247
+ def get_optimizer_params_dir(experiment_dir, create_if_nonexistent=False):
248
+
249
+ dir = os.path.join(experiment_dir, optimizer_params_subdir)
250
+
251
+ if create_if_nonexistent and not os.path.isdir(dir):
252
+ os.makedirs(dir)
253
+
254
+ return dir
255
+
256
+
257
+ def get_latent_codes_dir(experiment_dir, create_if_nonexistent=False):
258
+
259
+ dir = os.path.join(experiment_dir, latent_codes_subdir)
260
+
261
+ if create_if_nonexistent and not os.path.isdir(dir):
262
+ os.makedirs(dir)
263
+
264
+ return dir
265
+
266
+
267
+ def get_latent_code_data_map_filename(experiment_dir):
268
+ """Return absolute path for the latent-to-data mapping JSON file."""
269
+ return os.path.join(
270
+ get_latent_codes_dir(experiment_dir, create_if_nonexistent=True),
271
+ latent_code_data_map_filename,
272
+ )
273
+
274
+
275
+ def get_normalization_params_filename(
276
+ data_dir, dataset_name, class_name, instance_name
277
+ ):
278
+ return os.path.join(
279
+ data_dir,
280
+ normalization_param_subdir,
281
+ dataset_name,
282
+ class_name,
283
+ instance_name + ".npz",
284
+ )
285
+
286
+
287
+ def init_decoder(experiment_specs, device, data_parallel):
288
+ arch_name = experiment_specs["NetworkArch"]
289
+ if arch_name not in ARCHITECTURES:
290
+ raise ValueError(f"Unknown architecture: {arch_name}")
291
+
292
+ latent_size = experiment_specs["CodeLength"]
293
+ DecoderClass = ARCHITECTURES[arch_name]
294
+
295
+ decoder = DecoderClass(latent_size, **experiment_specs["NetworkSpecs"]).to(device)
296
+ if data_parallel:
297
+ decoder = torch.nn.DataParallel(decoder)
298
+ return decoder
299
+
300
+
301
+ def load_trained_model(
302
+ experiment_directory: str, checkpoint: str, device=None, data_parallel=False
303
+ ):
304
+ specs_filename = os.path.join(experiment_directory, "specs.json")
305
+ with open(specs_filename, "r", encoding="utf-8") as f:
306
+ experiment_specs = json.load(f)
307
+ if device is None:
308
+ device = get_default_device(device)
309
+
310
+ filename = os.path.join(
311
+ experiment_directory, model_params_subdir, checkpoint + ".pth"
312
+ )
313
+
314
+ if not os.path.isfile(filename):
315
+ raise Exception('model state dict "{}" does not exist'.format(filename))
316
+
317
+ data = torch.load(filename, map_location=device)
318
+ decoder = init_decoder(experiment_specs, device, data_parallel)
319
+
320
+ state_dict = data["model_state_dict"]
321
+ if any(k.startswith("module.") for k in state_dict.keys()):
322
+ stripped_dict = {}
323
+ for k, v in state_dict.items():
324
+ new_key = k.replace("module.", "", 1)
325
+ stripped_dict[new_key] = v
326
+ state_dict = stripped_dict
327
+
328
+ decoder.load_state_dict(state_dict, strict=False)
329
+ decoder = decoder.to(device)
330
+ return decoder
331
+
332
+
333
+ def get_default_device():
334
+
335
+ if torch.cuda.is_available():
336
+ device = torch.device("cuda")
337
+ else:
338
+ device = torch.device("cpu")
339
+ return device
340
+
341
+
342
+ def print_model_specifications(experiment_directory: str):
343
+ specs = load_experiment_specifications(experiment_directory)
344
+ print("Model Specifications:")
345
+ for key in specs:
346
+ print(f" {key}: {specs[key]}")
347
+ print("\n")
348
+
349
+
350
+ class ExperimentSummary(TypedDict):
351
+ loss: float
352
+ num_epochs: int
353
+ timestamp: str
354
+ host_name: str
355
+ device: str
356
+ training_duration: str
357
+ data_dir: str
358
+ version: str
359
+
360
+
361
+ def save_experiment_summary(experiment_directory: str, summary: ExperimentSummary):
362
+ with open(os.path.join(experiment_directory, experiment_summary_name), "w") as f:
363
+ json.dump(summary, f, indent=4)
@@ -0,0 +1,235 @@
1
+ """
2
+ Design of Experiments (DOE) for DeepSDF Training
3
+ ================================================
4
+
5
+ This module provides tools for conducting systematic design of experiments
6
+ when training DeepSDF neural network models. It integrates with MLflow for
7
+ experiment tracking and supports automated hyperparameter sweeps.
8
+
9
+ Key Features
10
+ ------------
11
+
12
+ ExperimentSpecifications
13
+ A flexible dictionary-like class for managing experiment configurations
14
+ with support for:
15
+ - Loading specifications from JSON files
16
+ - Recursive updates for nested parameter structures
17
+ - Deep copying to prevent unintended modifications
18
+ - Flattening nested configurations for logging
19
+ - Saving configurations for reproducibility
20
+
21
+ MLflow Integration
22
+ Automatic tracking of:
23
+ - Training metrics (loss curves, validation scores)
24
+ - Model checkpoints
25
+ - Hyperparameters and configurations
26
+ - Dataset information
27
+ - Experiment metadata
28
+
29
+ The module simplifies the process of running large-scale hyperparameter
30
+ searches and ablation studies for DeepSDF model training, with built-in
31
+ versioning and reproducibility features.
32
+
33
+ Examples
34
+ --------
35
+ Run an experiment with custom specifications::
36
+
37
+ from DeepSDFStruct.design_of_experiments import ExperimentSpecifications
38
+
39
+ # Load base configuration
40
+ specs = ExperimentSpecifications('config.json')
41
+
42
+ # Update hyperparameters
43
+ specs.update({
44
+ 'learning_rate': 0.001,
45
+ 'batch_size': 32,
46
+ 'num_epochs': 100
47
+ })
48
+
49
+ # Run training with tracking
50
+ # train_with_specs(specs)
51
+
52
+ Create and modify experiment configurations::
53
+
54
+ specs = ExperimentSpecifications({
55
+ 'model': {
56
+ 'layers': [512, 512, 512],
57
+ 'activation': 'relu'
58
+ },
59
+ 'training': {
60
+ 'lr': 0.0005
61
+ }
62
+ })
63
+
64
+ # Flatten for logging
65
+ flat_params = specs.flatten()
66
+ print(flat_params) # {'model.layers': [...], 'model.activation': 'relu', ...}
67
+ """
68
+
69
+ import os
70
+ import pathlib
71
+ import json
72
+ import mlflow
73
+ from urllib.parse import urlparse
74
+ from DeepSDFStruct.deep_sdf.training import train_deep_sdf
75
+ import copy
76
+ from typing import Dict, Any, Union
77
+
78
+ package_path = pathlib.Path(__file__).parent
79
+
80
+
81
+ class ExperimentSpecifications(dict):
82
+ """
83
+ A dictionary-like class to hold and update experiment specifications.
84
+ Supports recursive updates for nested dictionaries.
85
+ Can be initialized from a dictionary or loaded from a file (JSON/YAML).
86
+ """
87
+
88
+ def __init__(self, specs: Union[Dict[str, Any], str, None] = None):
89
+ """
90
+ Initialize the ExperimentSpecifications.
91
+
92
+ Args:
93
+ specs: A dictionary of specifications, a file path (JSON/YAML),
94
+ or None for an empty specification set.
95
+ """
96
+ if isinstance(specs, str):
97
+ loaded_specs = self._load_from_file(specs)
98
+ super().__init__(copy.deepcopy(loaded_specs))
99
+ else:
100
+ super().__init__(copy.deepcopy(specs) if specs else {})
101
+
102
+ @staticmethod
103
+ def _load_from_file(filename: str) -> Dict[str, Any]:
104
+ """
105
+ Load specifications from a JSON or YAML file.
106
+ """
107
+ with open(filename, "r") as f:
108
+ if filename.endswith(".json"):
109
+ return json.load(f)
110
+ else:
111
+ raise ValueError("Unsupported file format. Use .json or .yaml/.yml")
112
+
113
+ def save(self, filename: str) -> None:
114
+ """
115
+ Save current specifications to a JSON or YAML file.
116
+ """
117
+ with open(filename, "w") as f:
118
+ if filename.endswith(".json"):
119
+ json.dump(self, f, indent=4)
120
+
121
+ def update(self, updates: Dict[str, Any]) -> None:
122
+ """
123
+ Recursively update the experiment specifications with new values.
124
+ """
125
+
126
+ def _recursive_update(d, u):
127
+ for k, v in u.items():
128
+ if isinstance(v, dict) and isinstance(d.get(k), dict):
129
+ _recursive_update(d[k], v)
130
+ else:
131
+ d[k] = v
132
+
133
+ _recursive_update(self, updates)
134
+
135
+ def copy(self):
136
+ """Return a deep copy of the specifications."""
137
+ return ExperimentSpecifications(copy.deepcopy(self))
138
+
139
+ def flatten(self, parent_key: str = "", sep: str = ".") -> Dict[str, Any]:
140
+ """
141
+ Flatten nested dictionary (including lists of dicts) into a single-level dict
142
+ with dot-separated keys.
143
+ """
144
+ items = {}
145
+ for k, v in self.items():
146
+ new_key = f"{parent_key}{sep}{k}" if parent_key else k
147
+
148
+ if isinstance(v, dict):
149
+ items.update(
150
+ ExperimentSpecifications(v).flatten(parent_key=new_key, sep=sep)
151
+ )
152
+
153
+ elif isinstance(v, list):
154
+ # Check if the list contains dictionaries
155
+ if all(isinstance(i, dict) for i in v):
156
+ for idx, elem in enumerate(v):
157
+ items.update(
158
+ ExperimentSpecifications(elem).flatten(
159
+ parent_key=f"{new_key}.{idx}", sep=sep
160
+ )
161
+ )
162
+ else:
163
+ # Just convert the list/tuple to a string for MLflow
164
+ items[new_key] = str(v)
165
+
166
+ else:
167
+ items[new_key] = v
168
+
169
+ return items
170
+
171
+ def __repr__(self):
172
+ return f"ExperimentSpecifications({dict(self)})"
173
+
174
+
175
+ def create_experiment(exp_dir, specs):
176
+ """
177
+ Create a new experiment directory and specs.json file, copying defaults and applying overrides.
178
+
179
+ Args:
180
+ exp_dir (str): Path to new experiment directory.
181
+ specs (dict): Dictionary containing the experiment specifications.
182
+ """
183
+
184
+ os.makedirs(exp_dir, exist_ok=True)
185
+
186
+ # Write new specs.json
187
+ specs_path = os.path.join(exp_dir, "specs.json")
188
+ with open(specs_path, "w") as f:
189
+ json.dump(specs, f, indent=4)
190
+
191
+ return specs_path
192
+
193
+
194
+ def run_experiment(
195
+ exp_name,
196
+ data_dir,
197
+ specs: ExperimentSpecifications,
198
+ batch_split=1,
199
+ run_name=None,
200
+ device="cpu",
201
+ tracking_uri="sqlite:///mlruns.db",
202
+ ):
203
+ """
204
+ Creates an experiment with overrides, trains the model, and logs everything to MLflow.
205
+
206
+ Args:
207
+ exp_name (str): Name of the experiment folder.
208
+ data_dir (str): Path to the dataset.
209
+ specs (dict): Experiment specifications
210
+ device (str): Training device ('cpu' or 'cuda').
211
+ mlflow_experiment_name (str): Name of the MLflow experiment.
212
+ register_model (bool): If True, register the final model in MLflow Model Registry.
213
+ """
214
+ mlflow.set_tracking_uri(tracking_uri)
215
+ # Start MLflow run
216
+ mlflow.set_experiment(exp_name)
217
+ with mlflow.start_run(run_name=run_name):
218
+ parsed = urlparse(mlflow.get_artifact_uri())
219
+ if parsed.path == "":
220
+ raise NotImplementedError("Remote Tracking URI not implemented yet.")
221
+ else:
222
+ exp_dir = parsed.path
223
+ create_experiment(exp_dir, specs=specs)
224
+ specs_path = os.path.join(exp_dir, "specs.json")
225
+ mlflow.log_artifact(specs_path)
226
+
227
+ mlflow.log_params(specs.flatten())
228
+
229
+ summary = train_deep_sdf(
230
+ exp_dir, data_dir, device=device, batch_split=batch_split
231
+ )
232
+ mlflow.set_tags(dict(summary))
233
+
234
+ mlflow.log_metric("train_loss", summary["loss"])
235
+ return summary["loss"]
@@ -0,0 +1,15 @@
1
+ """
2
+ FlexiCubes - Differentiable 3D Mesh Extraction
3
+ ==============================================
4
+
5
+ This module implements FlexiCubes, a differentiable 3D variant of the
6
+ Dual Marching Cubes algorithm for extracting meshes from scalar fields.
7
+
8
+ FlexiCubes enables gradient-based optimization of mesh representations
9
+ by making the mesh extraction process fully differentiable. This is
10
+ essential for inverse problems and optimization tasks where the geometry
11
+ must be optimized to satisfy certain constraints or objectives.
12
+
13
+ The module uses precomputed lookup tables to handle all 256 possible
14
+ Marching Cubes configurations efficiently.
15
+ """