mbirtorch 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. mbirtorch-0.0.1/LICENSE +29 -0
  2. mbirtorch-0.0.1/PKG-INFO +121 -0
  3. mbirtorch-0.0.1/README.md +52 -0
  4. mbirtorch-0.0.1/mbirtorch/__init__.py +130 -0
  5. mbirtorch-0.0.1/mbirtorch/_memory_ledger.py +1282 -0
  6. mbirtorch-0.0.1/mbirtorch/_sharding.py +624 -0
  7. mbirtorch-0.0.1/mbirtorch/_utils.py +84 -0
  8. mbirtorch-0.0.1/mbirtorch/_widening_floors.py +525 -0
  9. mbirtorch-0.0.1/mbirtorch/autograd.py +111 -0
  10. mbirtorch-0.0.1/mbirtorch/bn256.py +261 -0
  11. mbirtorch-0.0.1/mbirtorch/cone_beam.py +1121 -0
  12. mbirtorch-0.0.1/mbirtorch/denoising.py +546 -0
  13. mbirtorch-0.0.1/mbirtorch/horizontal_fan.py +157 -0
  14. mbirtorch-0.0.1/mbirtorch/hsnt.py +664 -0
  15. mbirtorch-0.0.1/mbirtorch/kernel_availability.py +469 -0
  16. mbirtorch-0.0.1/mbirtorch/memory_stats.py +90 -0
  17. mbirtorch-0.0.1/mbirtorch/multiaxis_parallel.py +418 -0
  18. mbirtorch-0.0.1/mbirtorch/parallel_beam.py +386 -0
  19. mbirtorch-0.0.1/mbirtorch/parameter_handler.py +331 -0
  20. mbirtorch-0.0.1/mbirtorch/preprocess/__init__.py +8 -0
  21. mbirtorch-0.0.1/mbirtorch/preprocess/_xradia_ole.py +348 -0
  22. mbirtorch-0.0.1/mbirtorch/preprocess/mar.py +986 -0
  23. mbirtorch-0.0.1/mbirtorch/preprocess/nsi.py +705 -0
  24. mbirtorch-0.0.1/mbirtorch/preprocess/pipeline.py +121 -0
  25. mbirtorch-0.0.1/mbirtorch/preprocess/pymbir.py +235 -0
  26. mbirtorch-0.0.1/mbirtorch/preprocess/segmentation.py +432 -0
  27. mbirtorch-0.0.1/mbirtorch/preprocess/stripe.py +407 -0
  28. mbirtorch-0.0.1/mbirtorch/preprocess/utilities.py +1654 -0
  29. mbirtorch-0.0.1/mbirtorch/preprocess/zeiss.py +703 -0
  30. mbirtorch-0.0.1/mbirtorch/preprocess/zeiss_tct.py +622 -0
  31. mbirtorch-0.0.1/mbirtorch/projectors.py +628 -0
  32. mbirtorch-0.0.1/mbirtorch/qggmrf.py +249 -0
  33. mbirtorch-0.0.1/mbirtorch/tomography_model.py +3721 -0
  34. mbirtorch-0.0.1/mbirtorch/tomography_utils.py +77 -0
  35. mbirtorch-0.0.1/mbirtorch/translation_model.py +466 -0
  36. mbirtorch-0.0.1/mbirtorch/triton_cone.py +697 -0
  37. mbirtorch-0.0.1/mbirtorch/triton_parallel.py +488 -0
  38. mbirtorch-0.0.1/mbirtorch/utilities.py +1783 -0
  39. mbirtorch-0.0.1/mbirtorch/vcd_utils.py +357 -0
  40. mbirtorch-0.0.1/mbirtorch/vcls.py +614 -0
  41. mbirtorch-0.0.1/mbirtorch/view_utils.py +136 -0
  42. mbirtorch-0.0.1/mbirtorch/viewer.py +2416 -0
  43. mbirtorch-0.0.1/mbirtorch.egg-info/PKG-INFO +121 -0
  44. mbirtorch-0.0.1/mbirtorch.egg-info/SOURCES.txt +80 -0
  45. mbirtorch-0.0.1/mbirtorch.egg-info/dependency_links.txt +1 -0
  46. mbirtorch-0.0.1/mbirtorch.egg-info/requires.txt +25 -0
  47. mbirtorch-0.0.1/mbirtorch.egg-info/top_level.txt +1 -0
  48. mbirtorch-0.0.1/pyproject.toml +95 -0
  49. mbirtorch-0.0.1/setup.cfg +4 -0
  50. mbirtorch-0.0.1/tests/test_adjoint.py +70 -0
  51. mbirtorch-0.0.1/tests/test_cone.py +305 -0
  52. mbirtorch-0.0.1/tests/test_demo_data.py +167 -0
  53. mbirtorch-0.0.1/tests/test_denoiser.py +101 -0
  54. mbirtorch-0.0.1/tests/test_device_policy.py +889 -0
  55. mbirtorch-0.0.1/tests/test_hdf5_family.py +156 -0
  56. mbirtorch-0.0.1/tests/test_hsnt_vcls.py +108 -0
  57. mbirtorch-0.0.1/tests/test_kernels_sharded.py +317 -0
  58. mbirtorch-0.0.1/tests/test_logging.py +304 -0
  59. mbirtorch-0.0.1/tests/test_memory_ledger.py +1348 -0
  60. mbirtorch-0.0.1/tests/test_multiaxis.py +219 -0
  61. mbirtorch-0.0.1/tests/test_params_and_paths.py +505 -0
  62. mbirtorch-0.0.1/tests/test_phantom.py +36 -0
  63. mbirtorch-0.0.1/tests/test_preprocess_loaders.py +195 -0
  64. mbirtorch-0.0.1/tests/test_preprocess_mar.py +199 -0
  65. mbirtorch-0.0.1/tests/test_preprocess_stripe_segmentation.py +92 -0
  66. mbirtorch-0.0.1/tests/test_preprocess_utilities.py +271 -0
  67. mbirtorch-0.0.1/tests/test_prox_map.py +44 -0
  68. mbirtorch-0.0.1/tests/test_recon_smoke.py +30 -0
  69. mbirtorch-0.0.1/tests/test_sharded_pipeline.py +72 -0
  70. mbirtorch-0.0.1/tests/test_sharded_segmentation.py +358 -0
  71. mbirtorch-0.0.1/tests/test_sharding.py +1659 -0
  72. mbirtorch-0.0.1/tests/test_smoke.py +26 -0
  73. mbirtorch-0.0.1/tests/test_split_sino.py +117 -0
  74. mbirtorch-0.0.1/tests/test_translation.py +164 -0
  75. mbirtorch-0.0.1/tests/test_triton_cone.py +730 -0
  76. mbirtorch-0.0.1/tests/test_triton_parallel.py +756 -0
  77. mbirtorch-0.0.1/tests/test_view_batching.py +275 -0
  78. mbirtorch-0.0.1/tests/test_viewer_controller.py +1121 -0
  79. mbirtorch-0.0.1/tests/test_viewer_model.py +470 -0
  80. mbirtorch-0.0.1/tests/test_viewer_wrapper.py +161 -0
  81. mbirtorch-0.0.1/tests/test_vs_goldens.py +130 -0
  82. mbirtorch-0.0.1/tests/test_widening_floors.py +458 -0
@@ -0,0 +1,29 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2024, Charles A. Bouman and Gregery T. Buzzard
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ 1. Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ 2. Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ 3. Neither the name of the copyright holder nor the names of its
17
+ contributors may be used to endorse or promote products derived from
18
+ this software without specific prior written permission.
19
+
20
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,121 @@
1
+ Metadata-Version: 2.4
2
+ Name: mbirtorch
3
+ Version: 0.0.1
4
+ Summary: High-performance tomographic reconstruction using PyTorch
5
+ Author-email: MBIRTORCH development team <buzzard@purdue.edu>
6
+ License: BSD 3-Clause License
7
+
8
+ Copyright (c) 2024, Charles A. Bouman and Gregery T. Buzzard
9
+ All rights reserved.
10
+
11
+ Redistribution and use in source and binary forms, with or without
12
+ modification, are permitted provided that the following conditions are met:
13
+
14
+ 1. Redistributions of source code must retain the above copyright notice, this
15
+ list of conditions and the following disclaimer.
16
+
17
+ 2. Redistributions in binary form must reproduce the above copyright notice,
18
+ this list of conditions and the following disclaimer in the documentation
19
+ and/or other materials provided with the distribution.
20
+
21
+ 3. Neither the name of the copyright holder nor the names of its
22
+ contributors may be used to endorse or promote products derived from
23
+ this software without specific prior written permission.
24
+
25
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
26
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
29
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
33
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
34
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35
+
36
+ Project-URL: source, https://github.com/cabouman/mbirtorch
37
+ Keywords: tomography,tomographic reconstruction,computed tomography
38
+ Classifier: Programming Language :: Python :: 3
39
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
40
+ Classifier: License :: OSI Approved :: BSD License
41
+ Classifier: Operating System :: OS Independent
42
+ Requires-Python: >=3.11
43
+ Description-Content-Type: text/markdown
44
+ License-File: LICENSE
45
+ Requires-Dist: torch>=2.13
46
+ Requires-Dist: numpy
47
+ Requires-Dist: matplotlib
48
+ Requires-Dist: h5py
49
+ Requires-Dist: scipy
50
+ Requires-Dist: tifffile
51
+ Requires-Dist: tqdm
52
+ Requires-Dist: pywavelets
53
+ Requires-Dist: osqp
54
+ Requires-Dist: opencv-python
55
+ Requires-Dist: olefile
56
+ Requires-Dist: gdown
57
+ Requires-Dist: scikit-learn
58
+ Requires-Dist: psutil
59
+ Provides-Extra: test
60
+ Requires-Dist: pytest; extra == "test"
61
+ Requires-Dist: pytest-xdist; extra == "test"
62
+ Provides-Extra: docs
63
+ Requires-Dist: sphinx; extra == "docs"
64
+ Requires-Dist: sphinxcontrib-bibtex; extra == "docs"
65
+ Requires-Dist: sphinx-book-theme>=1.0.1; extra == "docs"
66
+ Requires-Dist: sphinx-design; extra == "docs"
67
+ Requires-Dist: sphinx-copybutton; extra == "docs"
68
+ Dynamic: license-file
69
+
70
+ # mbirtorch
71
+
72
+ [![CI](https://github.com/cabouman/mbirtorch/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/cabouman/mbirtorch/actions/workflows/ci.yml)
73
+ [![Documentation Status](https://readthedocs.org/projects/mbirtorch/badge/?version=latest)](https://mbirtorch.readthedocs.io/en/latest/)
74
+
75
+ A PyTorch port of [mbirjax](https://github.com/cabouman/mbirjax): high-performance
76
+ tomographic reconstruction with multi-GPU support.
77
+
78
+ This repo is the home for the port evaluated in
79
+ `mbirjax_plans/plans/torch_port/port_plan.md` (checked out parallel to this
80
+ repo). The plan defines the motivation, the parity gates against mbirjax,
81
+ the incremental phase plan, and a progress record; its findings pages and the
82
+ supporting scripts live alongside it under `mbirjax_plans/plans/`.
83
+
84
+ ## Setup
85
+
86
+ Create the conda environment and install the package in editable mode:
87
+
88
+ ```bash
89
+ conda env create -f environment.yml
90
+ conda activate mbirtorch
91
+ pip install -e ".[test]"
92
+ ```
93
+
94
+ ## Tests
95
+
96
+ ```bash
97
+ dev_scripts/run_tests.sh
98
+ ```
99
+
100
+ ## Caches
101
+
102
+ mbirtorch keeps one on-disk cache: compiled `torch.compile` artifacts, under
103
+ `~/.mbirtorch/torch_cache`. It exists to make cold starts fast -- with it, a
104
+ fresh process reuses prior compilations instead of recompiling (roughly 14 s
105
+ down to 2 s for a first small reconstruction). It grows with the number of
106
+ distinct compiled shapes and typically stays in the tens of megabytes; it is
107
+ never cleaned automatically. To remove it:
108
+
109
+ ```python
110
+ import mbirtorch
111
+ mbirtorch.clear_cache() # deletes ~/.mbirtorch entirely (recreated empty)
112
+ ```
113
+
114
+ The location can be redirected by setting the `TORCHINDUCTOR_CACHE_DIR`
115
+ environment variable before the first compile (e.g. to node-local or scratch
116
+ storage on a cluster, where home quotas are tight); `clear_cache()` does not
117
+ touch a redirected location.
118
+
119
+ Everything else the package caches is in-memory only and is freed with the
120
+ objects that hold it (e.g. the per-model pixel-index cache); nothing besides
121
+ `~/.mbirtorch` is written to disk.
@@ -0,0 +1,52 @@
1
+ # mbirtorch
2
+
3
+ [![CI](https://github.com/cabouman/mbirtorch/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/cabouman/mbirtorch/actions/workflows/ci.yml)
4
+ [![Documentation Status](https://readthedocs.org/projects/mbirtorch/badge/?version=latest)](https://mbirtorch.readthedocs.io/en/latest/)
5
+
6
+ A PyTorch port of [mbirjax](https://github.com/cabouman/mbirjax): high-performance
7
+ tomographic reconstruction with multi-GPU support.
8
+
9
+ This repo is the home for the port evaluated in
10
+ `mbirjax_plans/plans/torch_port/port_plan.md` (checked out parallel to this
11
+ repo). The plan defines the motivation, the parity gates against mbirjax,
12
+ the incremental phase plan, and a progress record; its findings pages and the
13
+ supporting scripts live alongside it under `mbirjax_plans/plans/`.
14
+
15
+ ## Setup
16
+
17
+ Create the conda environment and install the package in editable mode:
18
+
19
+ ```bash
20
+ conda env create -f environment.yml
21
+ conda activate mbirtorch
22
+ pip install -e ".[test]"
23
+ ```
24
+
25
+ ## Tests
26
+
27
+ ```bash
28
+ dev_scripts/run_tests.sh
29
+ ```
30
+
31
+ ## Caches
32
+
33
+ mbirtorch keeps one on-disk cache: compiled `torch.compile` artifacts, under
34
+ `~/.mbirtorch/torch_cache`. It exists to make cold starts fast -- with it, a
35
+ fresh process reuses prior compilations instead of recompiling (roughly 14 s
36
+ down to 2 s for a first small reconstruction). It grows with the number of
37
+ distinct compiled shapes and typically stays in the tens of megabytes; it is
38
+ never cleaned automatically. To remove it:
39
+
40
+ ```python
41
+ import mbirtorch
42
+ mbirtorch.clear_cache() # deletes ~/.mbirtorch entirely (recreated empty)
43
+ ```
44
+
45
+ The location can be redirected by setting the `TORCHINDUCTOR_CACHE_DIR`
46
+ environment variable before the first compile (e.g. to node-local or scratch
47
+ storage on a cluster, where home quotas are tight); `clear_cache()` does not
48
+ touch a redirected location.
49
+
50
+ Everything else the package caches is in-memory only and is freed with the
51
+ objects that hold it (e.g. the per-model pixel-index cache); nothing besides
52
+ `~/.mbirtorch` is written to disk.
@@ -0,0 +1,130 @@
1
+ """mbirtorch: a PyTorch port of mbirjax (parallel-beam geometry).
2
+
3
+ The public API mirrors mbirjax where implemented: numpy in, numpy out by
4
+ default, with device tensors available via ``output_sharded=True`` (the name
5
+ kept for API compatibility; here it means "return the device tensor").
6
+ """
7
+
8
+ __version__ = "0.0.1"
9
+
10
+ # ── persistent torch.compile cache ────────────────────────────────────────────
11
+ # The inductor cache directory defaults to /tmp/torchinductor_<user>, which the
12
+ # OS may clean; pin it to a stable per-user location so compiled artifacts
13
+ # survive across processes and reboots (the mbirjax ~/.mbirjax/jax_cache
14
+ # analog). The FX-graph cache is what makes a FRESH PROCESS reuse prior
15
+ # compilations; enable it explicitly for torch versions where it is not the
16
+ # default. setdefault keeps both overridable per-run via the environment, and
17
+ # -- like mbirjax's TF_CPP_MIN_LOG_LEVEL -- this is effective only if mbirtorch
18
+ # is imported before torch triggers its first compile, which any
19
+ # import-mbirtorch-first program satisfies. Dynamo TRACING still runs per
20
+ # process (the cache skips inductor codegen, not tracing), so a cold process
21
+ # keeps a small residual warmup. ``mbirtorch.clear_cache()`` removes the
22
+ # whole ~/.mbirtorch directory (see utilities.py).
23
+ import os as _os
24
+
25
+ _os.environ.setdefault("TORCHINDUCTOR_CACHE_DIR",
26
+ _os.path.expanduser("~/.mbirtorch/torch_cache"))
27
+ _os.environ.setdefault("TORCHINDUCTOR_FX_GRAPH_CACHE", "1")
28
+
29
+ from .parallel_beam import ParallelBeamModel
30
+ from .cone_beam import ConeBeamModel
31
+ from .translation_model import TranslationModel
32
+ from .multiaxis_parallel import MultiAxisParallelModel, MultiAxisParallelBeamModel
33
+ from .denoising import QGGMRFDenoiser
34
+ from .tomography_model import TomographyModel
35
+ from .autograd import (TorchProjector, forward_project_differentiable,
36
+ back_project_differentiable)
37
+ from .vcd_utils import (gen_weights, gen_weights_mar, gen_full_indices,
38
+ gen_pixel_partition, gen_set_of_pixel_partitions,
39
+ gen_partition_sequence, get_2d_ror_mask)
40
+ from .denoising import median_filter3d
41
+ from .qggmrf import (qggmrf_gradient_and_hessian_at_indices, get_b_from_nbr_wts,
42
+ b_tilde_by_definition, qggmrf_loss)
43
+ from .utilities import (generate_3d_shepp_logan_low_dynamic_range, clear_cache,
44
+ makedirs, load_data_hdf5, save_data_hdf5,
45
+ export_recon_hdf5, import_recon_hdf5,
46
+ build_model, download_and_extract,
47
+ copy_ct_model, stitch_arrays,
48
+ get_ct_model, generate_demo_data,
49
+ generate_3d_shepp_logan_reference, gen_cube_phantom,
50
+ gen_translation_vectors, gen_translation_phantom,
51
+ get_helical_half_rotation_slice_range,
52
+ merge_log_files)
53
+ from .memory_stats import get_memory_stats
54
+
55
+ # __all__ is the DECLARED public surface, and autodoc honors it: every name here is
56
+ # documented by ``automodule:: mbirtorch :members:``. It is deliberately narrower than
57
+ # the import list above -- the VCD and qGGMRF helpers stay importable as attributes
58
+ # (mbirtorch.gen_full_indices still works, and the tests use that spelling) but are not
59
+ # promised as public API, matching the surface mbirjax documents. The same goes for
60
+ # gen_cube_phantom and get_helical_half_rotation_slice_range: mbirjax's star imports put
61
+ # them on the package, and its tests call them there, but its docs do not carry them.
62
+ __all__ = [
63
+ "ParallelBeamModel", "ConeBeamModel", "TranslationModel",
64
+ "MultiAxisParallelModel", "TomographyModel", "QGGMRFDenoiser",
65
+ "TorchProjector", "forward_project_differentiable",
66
+ "back_project_differentiable", "gen_weights", "gen_weights_mar",
67
+ "median_filter3d", "download_and_extract", "build_model",
68
+ "save_data_hdf5", "load_data_hdf5", "export_recon_hdf5",
69
+ "import_recon_hdf5",
70
+ "generate_3d_shepp_logan_low_dynamic_range", "clear_cache",
71
+ "get_memory_stats", "SliceViewer", "VolumeStack", "slice_viewer",
72
+ "stitch_arrays", "get_ct_model", "copy_ct_model",
73
+ "generate_demo_data", "generate_3d_shepp_logan_reference",
74
+ # Documented hsnt and vcls names; these resolve lazily through __getattr__.
75
+ "hyper_denoise", "dehydrate", "rehydrate", "import_hsnt_data_hdf5",
76
+ "create_hsnt_metadata", "export_hsnt_data_hdf5", "generate_hyper_data",
77
+ "get_opt_views", "show_image_with_projection_rays",
78
+ ]
79
+
80
+ # ── lazy exports (PEP 562) ───────────────────────────────────────────────────
81
+ # The viewer names resolve on first attribute access so that a headless
82
+ # `import mbirtorch` never imports matplotlib; most mbirtorch runs (batch
83
+ # recons, tests) never open a viewer. The preprocess, hsnt, and vcls
84
+ # modules resolve the same way, so `import mbirtorch` never pays for their
85
+ # dependency stacks (preprocess: osqp pulls scipy.sparse, plus cv2 and
86
+ # tifffile; hsnt: scikit-learn, scipy, matplotlib; vcls: the model layer and
87
+ # tqdm). Both spellings keep working -- `mbirtorch.hsnt` resolves here, and
88
+ # `import mbirtorch.hsnt` is an ordinary submodule import -- and the
89
+ # star-exported FUNCTION names (mbirtorch.dehydrate, mbirtorch.get_opt_views,
90
+ # ...) resolve through _LAZY_NAMES, so the public surface matches mbirjax's
91
+ # eager star imports exactly; only WHEN each module loads changes.
92
+ _VIEWER_EXPORTS = ("SliceViewer", "VolumeStack", "slice_viewer")
93
+
94
+ _LAZY_MODULES = ("preprocess", "hsnt", "vcls")
95
+
96
+ # The names mbirjax exposes at package level via `from .hsnt import *` and
97
+ # `from .vcls import *`, mapped to their owning module (neither module
98
+ # declares __all__, so this is their full public def list; a new public
99
+ # function in either module gets a line here).
100
+ _LAZY_NAMES = {
101
+ 'hyper_denoise': 'hsnt', 'dehydrate': 'hsnt', 'rehydrate': 'hsnt',
102
+ 'import_hsnt_data_hdf5': 'hsnt', 'create_hsnt_metadata': 'hsnt',
103
+ 'export_hsnt_data_hdf5': 'hsnt', 'generate_hyper_data': 'hsnt',
104
+ 'subsample_R_gamma': 'vcls', 'max_abs_neighbor_diff': 'vcls',
105
+ 'get_opt_views': 'vcls', 'compute_view_basis_functions': 'vcls',
106
+ 'compute_cov_matrix': 'vcls', 'compute_vcl': 'vcls',
107
+ 'compute_opt_angle_subset': 'vcls', 'get_2d_subsampling_indices': 'vcls',
108
+ 'show_image_with_projection_rays': 'vcls', 'reorder_by_priority': 'vcls',
109
+ # The blue-noise pattern (a 382 KB array literal), loaded on first use.
110
+ 'bn256': 'bn256',
111
+ }
112
+
113
+
114
+ def __getattr__(name):
115
+ import importlib
116
+ if name in _VIEWER_EXPORTS:
117
+ from . import view_utils
118
+ value = getattr(view_utils, name)
119
+ globals()[name] = value # cache: later accesses skip this hook
120
+ return value
121
+ if name in _LAZY_MODULES:
122
+ value = importlib.import_module('.' + name, __name__)
123
+ globals()[name] = value
124
+ return value
125
+ if name in _LAZY_NAMES:
126
+ module = importlib.import_module('.' + _LAZY_NAMES[name], __name__)
127
+ value = getattr(module, name)
128
+ globals()[name] = value
129
+ return value
130
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")