diffct 2.0.1__tar.gz → 2.0.2__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 (56) hide show
  1. {diffct-2.0.1 → diffct-2.0.2}/CHANGELOG.md +22 -0
  2. {diffct-2.0.1 → diffct-2.0.2}/CITATION.cff +1 -1
  3. {diffct-2.0.1 → diffct-2.0.2}/PKG-INFO +32 -27
  4. {diffct-2.0.1 → diffct-2.0.2}/README.md +31 -26
  5. {diffct-2.0.1 → diffct-2.0.2}/diffct/__init__.py +1 -1
  6. {diffct-2.0.1 → diffct-2.0.2}/diffct/utils.py +12 -9
  7. diffct-2.0.2/docs/video/README.md +47 -0
  8. {diffct-2.0.1 → diffct-2.0.2}/examples/README.md +6 -4
  9. {diffct-2.0.1 → diffct-2.0.2}/pyproject.toml +1 -1
  10. diffct-2.0.2/tests/test_cuda_exit_deallocations.py +83 -0
  11. {diffct-2.0.1 → diffct-2.0.2}/tests/test_multi_gpu_exit.py +5 -0
  12. diffct-2.0.1/docs/video/README.md +0 -23
  13. {diffct-2.0.1 → diffct-2.0.2}/.gitignore +0 -0
  14. {diffct-2.0.1 → diffct-2.0.2}/LICENSE +0 -0
  15. {diffct-2.0.1 → diffct-2.0.2}/NOTICE +0 -0
  16. {diffct-2.0.1 → diffct-2.0.2}/diffct/analytical.py +0 -0
  17. {diffct-2.0.1 → diffct-2.0.2}/diffct/constants.py +0 -0
  18. {diffct-2.0.1 → diffct-2.0.2}/diffct/differentiable.py +0 -0
  19. {diffct-2.0.1 → diffct-2.0.2}/diffct/geometry.py +0 -0
  20. {diffct-2.0.1 → diffct-2.0.2}/diffct/kernels/__init__.py +0 -0
  21. {diffct-2.0.1 → diffct-2.0.2}/diffct/kernels/cone_beam.py +0 -0
  22. {diffct-2.0.1 → diffct-2.0.2}/diffct/kernels/fan_beam.py +0 -0
  23. {diffct-2.0.1 → diffct-2.0.2}/diffct/kernels/geometry_vjp.py +0 -0
  24. {diffct-2.0.1 → diffct-2.0.2}/diffct/kernels/parallel_beam.py +0 -0
  25. {diffct-2.0.1 → diffct-2.0.2}/diffct/operators.py +0 -0
  26. {diffct-2.0.1 → diffct-2.0.2}/diffct/projectors.py +0 -0
  27. {diffct-2.0.1 → diffct-2.0.2}/examples/data/NOTICE +0 -0
  28. {diffct-2.0.1 → diffct-2.0.2}/formal/README.md +0 -0
  29. {diffct-2.0.1 → diffct-2.0.2}/pytest.ini +0 -0
  30. {diffct-2.0.1 → diffct-2.0.2}/tests/__init__.py +0 -0
  31. {diffct-2.0.1 → diffct-2.0.2}/tests/_phantoms.py +0 -0
  32. {diffct-2.0.1 → diffct-2.0.2}/tests/benchmarks/README.md +0 -0
  33. {diffct-2.0.1 → diffct-2.0.2}/tests/benchmarks/__init__.py +0 -0
  34. {diffct-2.0.1 → diffct-2.0.2}/tests/benchmarks/_common.py +0 -0
  35. {diffct-2.0.1 → diffct-2.0.2}/tests/benchmarks/test_bench_cone.py +0 -0
  36. {diffct-2.0.1 → diffct-2.0.2}/tests/benchmarks/test_bench_fan.py +0 -0
  37. {diffct-2.0.1 → diffct-2.0.2}/tests/benchmarks/test_bench_parallel.py +0 -0
  38. {diffct-2.0.1 → diffct-2.0.2}/tests/distributed_projector_check.py +0 -0
  39. {diffct-2.0.1 → diffct-2.0.2}/tests/test_adjoint_inner_product.py +0 -0
  40. {diffct-2.0.1 → diffct-2.0.2}/tests/test_analytical_regressions.py +0 -0
  41. {diffct-2.0.1 → diffct-2.0.2}/tests/test_cone_projector_autograd.py +0 -0
  42. {diffct-2.0.1 → diffct-2.0.2}/tests/test_cuda_smoke.py +0 -0
  43. {diffct-2.0.1 → diffct-2.0.2}/tests/test_fbp_fan_accuracy.py +0 -0
  44. {diffct-2.0.1 → diffct-2.0.2}/tests/test_fbp_fan_offsets.py +0 -0
  45. {diffct-2.0.1 → diffct-2.0.2}/tests/test_fbp_parallel_accuracy.py +0 -0
  46. {diffct-2.0.1 → diffct-2.0.2}/tests/test_fdk_cone_accuracy.py +0 -0
  47. {diffct-2.0.1 → diffct-2.0.2}/tests/test_fdk_cone_offsets.py +0 -0
  48. {diffct-2.0.1 → diffct-2.0.2}/tests/test_geometry_autograd.py +0 -0
  49. {diffct-2.0.1 → diffct-2.0.2}/tests/test_geometry_gradients.py +0 -0
  50. {diffct-2.0.1 → diffct-2.0.2}/tests/test_gradcheck.py +0 -0
  51. {diffct-2.0.1 → diffct-2.0.2}/tests/test_projector_api.py +0 -0
  52. {diffct-2.0.1 → diffct-2.0.2}/tests/test_projector_degenerate_geometry.py +0 -0
  53. {diffct-2.0.1 → diffct-2.0.2}/tests/test_projector_geometry_devices.py +0 -0
  54. {diffct-2.0.1 → diffct-2.0.2}/tests/test_ramp_filter_windows.py +0 -0
  55. {diffct-2.0.1 → diffct-2.0.2}/tests/test_release_2_0_1.py +0 -0
  56. {diffct-2.0.1 → diffct-2.0.2}/tests/test_weights.py +0 -0
@@ -5,6 +5,25 @@ All notable changes to this project are documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.0.2] - 2026-10-09
9
+
10
+ ### Fixed
11
+
12
+ - The exit-time cleanup from 2.0.1 set numba-cuda's pending-deallocation
13
+ ratio to infinity. numba-cuda converts ``capacity * ratio`` to ``int``, so
14
+ each deallocation queued during shutdown raised ``ValueError`` or
15
+ ``OverflowError``. The exit code stayed 0, but stderr showed the error. The
16
+ exit hook now disables numba-cuda's pending-deallocation queues instead of
17
+ changing the limits, so no queue is flushed at exit and no error is raised.
18
+ The hook no longer imports ``numba.cuda.core.config``, which older
19
+ numba-cuda releases do not have.
20
+
21
+ ### Documentation
22
+
23
+ - The install instructions state that ``pip install diffct`` without a CUDA
24
+ extra needs a system CUDA Toolkit. Use ``diffct[cu12]`` or ``diffct[cu13]``.
25
+ - README images and links use absolute URLs, so they also work on PyPI.
26
+
8
27
  ## [2.0.1] - 2026-10-09
9
28
 
10
29
  ### Fixed
@@ -13,6 +32,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13
32
  weighted backprojections run in the CUDA context of their input. Before,
14
33
  inputs on another GPU than the current device could use the wrong context
15
34
  and stream.
35
+ - A process that ran diffct kernels on several GPUs no longer crashes at
36
+ interpreter exit: numba-cuda's flush of pending CUDA module unloads during
37
+ shutdown is skipped, and process exit releases the modules.
16
38
  - ``custom_trajectory_3d`` and ``custom_trajectory_2d_fan`` raise
17
39
  ``ValueError`` for a source at the isocenter or a non-finite source,
18
40
  instead of returning NaN geometry. The figure-8 example in the
@@ -13,7 +13,7 @@ authors:
13
13
  given-names: Yipeng
14
14
  email: yipeng.sun@fau.de
15
15
  alias: sypsyp97
16
- version: 2.0.1
16
+ version: 2.0.2
17
17
  date-released: "2026-10-09"
18
18
  license: Apache-2.0
19
19
  repository-code: "https://github.com/sypsyp97/diffct"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: diffct
3
- Version: 2.0.1
3
+ Version: 2.0.2
4
4
  Summary: Differentiable CUDA projectors for CT: arbitrary trajectories, multi-GPU and multi-node execution, geometry gradients
5
5
  Project-URL: Homepage, https://github.com/sypsyp97/diffct
6
6
  Project-URL: Documentation, https://sypsyp97.github.io/diffct/
@@ -31,7 +31,7 @@ Description-Content-Type: text/markdown
31
31
 
32
32
  <h1 align="center">diffct</h1>
33
33
 
34
- <p align="center">English · <a href="README.zh.md">简体中文</a></p>
34
+ <p align="center">English · <a href="https://github.com/sypsyp97/diffct/blob/main/README.zh.md">简体中文</a></p>
35
35
 
36
36
  <p align="center">
37
37
  Differentiable CUDA projectors for CT: arbitrary trajectories, multi-GPU, multi-node, geometry gradients.
@@ -43,15 +43,15 @@ Description-Content-Type: text/markdown
43
43
  <a href="https://pypi.org/project/diffct/"><img src="https://img.shields.io/pypi/v/diffct.svg?style=flat-square&logo=pypi&logoColor=white" alt="PyPI version"></a>
44
44
  <a href="https://sypsyp97.github.io/diffct/"><img src="https://img.shields.io/github/actions/workflow/status/sypsyp97/diffct/docs.yml?branch=main&label=docs&style=flat-square" alt="Documentation"></a>
45
45
  <a href="https://github.com/sypsyp97/diffct/actions"><img src="https://img.shields.io/github/actions/workflow/status/sypsyp97/diffct/ci.yml?branch=main&label=CI&style=flat-square" alt="CI/CD"></a>
46
- <a href="https://deepwiki.com/sypsyp97/diffct"><img src="docs/assets/deepwiki-badge.svg" alt="Ask DeepWiki"></a>
46
+ <a href="https://deepwiki.com/sypsyp97/diffct"><img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/deepwiki-badge.svg" alt="Ask DeepWiki"></a>
47
47
  </p>
48
48
 
49
49
  <p align="center">
50
- <img src="docs/assets/diffct_intro.gif" width="100%" alt="diffct intro: projection, sinogram, trajectories, multi-GPU split, reconstruction and geometry calibration">
50
+ <img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/diffct_intro.gif" width="100%" alt="diffct intro: projection, sinogram, trajectories, multi-GPU split, reconstruction and geometry calibration">
51
51
  </p>
52
52
 
53
53
  <p align="center">
54
- <a href="docs/assets/diffct_intro.mp4">Intro video (MP4)</a> ·
54
+ <a href="https://github.com/sypsyp97/diffct/blob/main/docs/assets/diffct_intro.mp4">Intro video (MP4)</a> ·
55
55
  <a href="https://www.preprints.org/manuscript/202605.1446/v1">Technical report</a> ·
56
56
  <a href="https://doi.org/10.20944/preprints202605.1446.v1">DOI</a>
57
57
  </p>
@@ -65,7 +65,9 @@ Description-Content-Type: text/markdown
65
65
  <a href="#citation">Citation</a>
66
66
  </p>
67
67
 
68
- > **Version 2.0** replaces the circular-orbit API of 1.x with per-view trajectories. Code written for 1.x needs changes; see the [migration notes](docs/MIGRATION.md).
68
+ > **Install** with the CUDA extra, `pip install "diffct[cu12]"` or `pip install "diffct[cu13]"`, after PyTorch. A plain `pip install diffct` needs a system CUDA Toolkit.
69
+ >
70
+ > **Version 2.0** replaces the circular-orbit API of 1.x with per-view trajectories. Code written for 1.x needs changes; see the [migration notes](https://github.com/sypsyp97/diffct/blob/main/docs/MIGRATION.md).
69
71
  > For Apple Silicon, use the [MLX port by Linda-Sophie Schneider](https://github.com/Linda-SophieSchneider/DiffCT-MLX).
70
72
 
71
73
  ## Why diffct
@@ -77,16 +79,19 @@ To our knowledge, diffct is the only open-source GPU CT library that combines ar
77
79
  - **Multi-GPU and multi-node.** Use `devices=[0, 1, 2, 3]` in one process, or one process per GPU with torchrun and NCCL. Views are partitioned and the volume is replicated. Speedup depends on workload and communication costs.
78
80
  - **Analytical helpers.** `diffct.analytical` provides ramp filters (ram-lak, shepp-logan, cosine, hamming, hann), fan, cone and Parker weights, and FBP and FDK backprojection.
79
81
 
80
- Capabilities, limits and isocenter rules: [docs/REFERENCE.md](docs/REFERENCE.md#capabilities-and-limits).
82
+ Capabilities, limits and isocenter rules: [docs/REFERENCE.md](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#capabilities-and-limits).
81
83
 
82
84
  ## Install
83
85
 
84
- You need a CUDA GPU and PyTorch. Install PyTorch for your CUDA version first.
86
+ You need an NVIDIA GPU. Install PyTorch for your CUDA version first. Then install diffct with the extra for the same CUDA major version:
85
87
 
86
88
  ```bash
87
- pip install "diffct[cu12]" # [cu13] for CUDA 13; install PyTorch for your CUDA first
89
+ pip install "diffct[cu12]" # PyTorch built for CUDA 12
90
+ pip install "diffct[cu13]" # PyTorch built for CUDA 13
88
91
  ```
89
92
 
93
+ The extra installs the CUDA compiler libraries that Numba CUDA needs to compile the kernels. A plain `pip install diffct` does not install them. Use it only if a CUDA Toolkit is already installed on the system.
94
+
90
95
  From source, with the examples:
91
96
 
92
97
  ```bash
@@ -129,11 +134,11 @@ loss = 0.5 * (A.project(x) - sinogram).square().sum()
129
134
  loss.backward() # x.grad = A^T (A x - y)
130
135
  ```
131
136
 
132
- **Custom or calibrated trajectories.** Pass calibrated tensors directly instead of a generator; no circular fit is needed. See [REFERENCE](docs/REFERENCE.md#custom-or-calibrated-trajectories).
137
+ **Custom or calibrated trajectories.** Pass calibrated tensors directly instead of a generator; no circular fit is needed. See [REFERENCE](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#custom-or-calibrated-trajectories).
133
138
 
134
- **Geometry gradients.** Set `requires_grad=True` on trajectory tensors before you build `Projector`. See [REFERENCE](docs/REFERENCE.md#geometry-gradients).
139
+ **Geometry gradients.** Set `requires_grad=True` on trajectory tensors before you build `Projector`. See [REFERENCE](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#geometry-gradients).
135
140
 
136
- **Several GPUs and nodes.** Use `devices=[0, 1, 2, 3]` in one process, or one process per GPU with torchrun for one or more nodes. See [REFERENCE](docs/REFERENCE.md#several-gpus-and-nodes) and [DISTRIBUTED.md](docs/DISTRIBUTED.md).
141
+ **Several GPUs and nodes.** Use `devices=[0, 1, 2, 3]` in one process, or one process per GPU with torchrun for one or more nodes. See [REFERENCE](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#several-gpus-and-nodes) and [DISTRIBUTED.md](https://github.com/sypsyp97/diffct/blob/main/docs/DISTRIBUTED.md).
137
142
 
138
143
  ```python
139
144
  A = Projector(trajectory, (128, 128, 128), (384, 256), detector_spacing=0.8, devices=[0, 1, 2, 3])
@@ -143,8 +148,8 @@ A = Projector(trajectory, (128, 128, 128), (384, 256), detector_spacing=0.8, dev
143
148
 
144
149
  <p align="center">
145
150
  <picture>
146
- <source media="(prefers-color-scheme: dark)" srcset="docs/assets/scaling_dark.png">
147
- <img src="docs/assets/scaling_light.png" width="100%" alt="Time per forward, adjoint and CGLS iteration on 1, 4 and 8 GPUs, 64^3 and 128^3">
151
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/scaling_dark.png">
152
+ <img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/scaling_light.png" width="100%" alt="Time per forward, adjoint and CGLS iteration on 1, 4 and 8 GPUs, 64^3 and 128^3">
148
153
  </picture>
149
154
  </p>
150
155
 
@@ -154,7 +159,7 @@ A = Projector(trajectory, (128, 128, 128), (384, 256), detector_spacing=0.8, dev
154
159
  | Adjoint (backprojection) | 20.66 ms | 5.89 ms (3.5×) | 4.62 ms (4.5×) |
155
160
  | CGLS iteration | 30.08 ms | 8.08 ms (3.7×) | 5.59 ms (5.4×) |
156
161
 
157
- Circular trajectory, 360 views, detector (2n, 1.5n) cells at pitch 1.25, A100 64 GB GPUs. Small volumes scale less: at 64³, one CGLS iteration takes 4.63 ms on 1 GPU, 1.57 ms on 4 and 1.56 ms on 8. Raw data: [docs/assets/scaling.json](docs/assets/scaling.json).
162
+ Circular trajectory, 360 views, detector (2n, 1.5n) cells at pitch 1.25, A100 64 GB GPUs. Small volumes scale less: at 64³, one CGLS iteration takes 4.63 ms on 1 GPU, 1.57 ms on 4 and 1.56 ms on 8. Raw data: [docs/assets/scaling.json](https://github.com/sypsyp97/diffct/blob/main/docs/assets/scaling.json).
158
163
 
159
164
  To measure one against several GPUs on your machine (default scan of the script, not the table setup):
160
165
 
@@ -167,7 +172,7 @@ python examples/benchmark_projector.py --devices 0 1
167
172
  **Measured walnut.** 240 measured views, circular cone beam, 256³: FDK (Hann window), SIRT (200 iterations), CGLS (20), TV (300, weight 0.3); axial and coronal centre slices.
168
173
 
169
174
  <p align="center">
170
- <img src="docs/assets/walnut_measured.png" width="100%" alt="Measured walnut: FDK, SIRT, CGLS and TV reconstructions">
175
+ <img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/walnut_measured.png" width="100%" alt="Measured walnut: FDK, SIRT, CGLS and TV reconstructions">
171
176
  </p>
172
177
 
173
178
  ```bash
@@ -177,7 +182,7 @@ python examples/walnut_reconstruction.py --figure out.png
177
182
  **Simulated helical scan of the walnut.** The FDK walnut volume as ground truth, 720 views, 1% noise, 256³. PSNR: FDK 26.67 dB, CGLS (30 iterations) 34.13 dB, SIRT (200) 34.52 dB, TV (200, weight 1.0) 37.77 dB.
178
183
 
179
184
  <p align="center">
180
- <img src="docs/assets/walnut_helical.png" width="100%" alt="Simulated helical scan of the walnut: FDK, CGLS, SIRT and TV reconstructions">
185
+ <img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/walnut_helical.png" width="100%" alt="Simulated helical scan of the walnut: FDK, CGLS, SIRT and TV reconstructions">
181
186
  </p>
182
187
 
183
188
  ```bash
@@ -185,22 +190,22 @@ python examples/walnut_reconstruction.py --algorithms --save-volume walnut256.np
185
190
  python examples/iterative_reconstruction.py --size 256 --views 720 --trajectory helical --noise 0.01 --phantom walnut256.npy --figure out.png
186
191
  ```
187
192
 
188
- Walnut data: Meaney 2022, Zenodo 6986012, CC BY 4.0; see [examples/data/NOTICE](examples/data/NOTICE).
193
+ Walnut data: Meaney 2022, Zenodo 6986012, CC BY 4.0; see [examples/data/NOTICE](https://github.com/sypsyp97/diffct/blob/main/examples/data/NOTICE).
189
194
 
190
195
  ## Examples
191
196
 
192
- `quickstart.py`, `analytical_reconstruction.py`, `iterative_reconstruction.py`, `walnut_reconstruction.py`, `geometry_calibration.py`, `benchmark_projector.py`, `plot_trajectory.py`, and the Slurm template `slurm/multi_node.sbatch`. Launch modes, distributed-loss rules and measured results are in [examples/README.md](examples/README.md).
197
+ `quickstart.py`, `analytical_reconstruction.py`, `iterative_reconstruction.py`, `walnut_reconstruction.py`, `geometry_calibration.py`, `benchmark_projector.py`, `plot_trajectory.py`, and the Slurm template `slurm/multi_node.sbatch`. Launch modes and distributed-loss rules are in [examples/README.md](https://github.com/sypsyp97/diffct/blob/main/examples/README.md).
193
198
 
194
199
  ## Documentation
195
200
 
196
201
  | Document | Contents |
197
202
  |---|---|
198
- | [Trajectory guide](docs/source/trajectories.rst) | Trajectory tuples and `Projector` usage |
199
- | [docs/REFERENCE.md](docs/REFERENCE.md) | Capabilities and limits, custom trajectories, geometry gradients, multi-GPU details, geometry and units |
200
- | [docs/DISTRIBUTED.md](docs/DISTRIBUTED.md) | Execution/memory choices, distributed loss rules, Slurm and cross-node checks |
201
- | [docs/MIGRATION.md](docs/MIGRATION.md) | Moving from the circular-only API |
202
- | [docs/video/README.md](docs/video/README.md) | How the intro video is rendered |
203
- | [CHANGELOG.md](CHANGELOG.md) | Release history |
203
+ | [Trajectory guide](https://github.com/sypsyp97/diffct/blob/main/docs/source/trajectories.rst) | Trajectory tuples and `Projector` usage |
204
+ | [docs/REFERENCE.md](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md) | Capabilities and limits, custom trajectories, geometry gradients, multi-GPU details, geometry and units |
205
+ | [docs/DISTRIBUTED.md](https://github.com/sypsyp97/diffct/blob/main/docs/DISTRIBUTED.md) | Execution/memory choices, distributed loss rules, Slurm and cross-node checks |
206
+ | [docs/MIGRATION.md](https://github.com/sypsyp97/diffct/blob/main/docs/MIGRATION.md) | Moving from the circular-only API |
207
+ | [docs/video/README.md](https://github.com/sypsyp97/diffct/blob/main/docs/video/README.md) | How the intro video is rendered |
208
+ | [CHANGELOG.md](https://github.com/sypsyp97/diffct/blob/main/CHANGELOG.md) | Release history |
204
209
 
205
210
  Run the test suite on a CUDA host with `python -m pytest tests/ -q`.
206
211
 
@@ -232,7 +237,7 @@ If you use this library in your research, please cite the software and the techn
232
237
 
233
238
  ## License and acknowledgements
234
239
 
235
- Apache 2.0, see [LICENSE](LICENSE). The project draws on
240
+ Apache 2.0, see [LICENSE](https://github.com/sypsyp97/diffct/blob/main/LICENSE). The project draws on
236
241
  [PYRO-NN](https://github.com/csyben/PYRO-NN) and
237
242
  [geometry_gradients_CT](https://github.com/mareikethies/geometry_gradients_CT).
238
- Issues and pull requests are welcome. The walnut data are CC BY 4.0 (Meaney 2022); see [NOTICE](NOTICE).
243
+ Issues and pull requests are welcome. The walnut data are CC BY 4.0 (Meaney 2022); see [NOTICE](https://github.com/sypsyp97/diffct/blob/main/NOTICE).
@@ -1,6 +1,6 @@
1
1
  <h1 align="center">diffct</h1>
2
2
 
3
- <p align="center">English · <a href="README.zh.md">简体中文</a></p>
3
+ <p align="center">English · <a href="https://github.com/sypsyp97/diffct/blob/main/README.zh.md">简体中文</a></p>
4
4
 
5
5
  <p align="center">
6
6
  Differentiable CUDA projectors for CT: arbitrary trajectories, multi-GPU, multi-node, geometry gradients.
@@ -12,15 +12,15 @@
12
12
  <a href="https://pypi.org/project/diffct/"><img src="https://img.shields.io/pypi/v/diffct.svg?style=flat-square&logo=pypi&logoColor=white" alt="PyPI version"></a>
13
13
  <a href="https://sypsyp97.github.io/diffct/"><img src="https://img.shields.io/github/actions/workflow/status/sypsyp97/diffct/docs.yml?branch=main&label=docs&style=flat-square" alt="Documentation"></a>
14
14
  <a href="https://github.com/sypsyp97/diffct/actions"><img src="https://img.shields.io/github/actions/workflow/status/sypsyp97/diffct/ci.yml?branch=main&label=CI&style=flat-square" alt="CI/CD"></a>
15
- <a href="https://deepwiki.com/sypsyp97/diffct"><img src="docs/assets/deepwiki-badge.svg" alt="Ask DeepWiki"></a>
15
+ <a href="https://deepwiki.com/sypsyp97/diffct"><img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/deepwiki-badge.svg" alt="Ask DeepWiki"></a>
16
16
  </p>
17
17
 
18
18
  <p align="center">
19
- <img src="docs/assets/diffct_intro.gif" width="100%" alt="diffct intro: projection, sinogram, trajectories, multi-GPU split, reconstruction and geometry calibration">
19
+ <img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/diffct_intro.gif" width="100%" alt="diffct intro: projection, sinogram, trajectories, multi-GPU split, reconstruction and geometry calibration">
20
20
  </p>
21
21
 
22
22
  <p align="center">
23
- <a href="docs/assets/diffct_intro.mp4">Intro video (MP4)</a> ·
23
+ <a href="https://github.com/sypsyp97/diffct/blob/main/docs/assets/diffct_intro.mp4">Intro video (MP4)</a> ·
24
24
  <a href="https://www.preprints.org/manuscript/202605.1446/v1">Technical report</a> ·
25
25
  <a href="https://doi.org/10.20944/preprints202605.1446.v1">DOI</a>
26
26
  </p>
@@ -34,7 +34,9 @@
34
34
  <a href="#citation">Citation</a>
35
35
  </p>
36
36
 
37
- > **Version 2.0** replaces the circular-orbit API of 1.x with per-view trajectories. Code written for 1.x needs changes; see the [migration notes](docs/MIGRATION.md).
37
+ > **Install** with the CUDA extra, `pip install "diffct[cu12]"` or `pip install "diffct[cu13]"`, after PyTorch. A plain `pip install diffct` needs a system CUDA Toolkit.
38
+ >
39
+ > **Version 2.0** replaces the circular-orbit API of 1.x with per-view trajectories. Code written for 1.x needs changes; see the [migration notes](https://github.com/sypsyp97/diffct/blob/main/docs/MIGRATION.md).
38
40
  > For Apple Silicon, use the [MLX port by Linda-Sophie Schneider](https://github.com/Linda-SophieSchneider/DiffCT-MLX).
39
41
 
40
42
  ## Why diffct
@@ -46,16 +48,19 @@ To our knowledge, diffct is the only open-source GPU CT library that combines ar
46
48
  - **Multi-GPU and multi-node.** Use `devices=[0, 1, 2, 3]` in one process, or one process per GPU with torchrun and NCCL. Views are partitioned and the volume is replicated. Speedup depends on workload and communication costs.
47
49
  - **Analytical helpers.** `diffct.analytical` provides ramp filters (ram-lak, shepp-logan, cosine, hamming, hann), fan, cone and Parker weights, and FBP and FDK backprojection.
48
50
 
49
- Capabilities, limits and isocenter rules: [docs/REFERENCE.md](docs/REFERENCE.md#capabilities-and-limits).
51
+ Capabilities, limits and isocenter rules: [docs/REFERENCE.md](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#capabilities-and-limits).
50
52
 
51
53
  ## Install
52
54
 
53
- You need a CUDA GPU and PyTorch. Install PyTorch for your CUDA version first.
55
+ You need an NVIDIA GPU. Install PyTorch for your CUDA version first. Then install diffct with the extra for the same CUDA major version:
54
56
 
55
57
  ```bash
56
- pip install "diffct[cu12]" # [cu13] for CUDA 13; install PyTorch for your CUDA first
58
+ pip install "diffct[cu12]" # PyTorch built for CUDA 12
59
+ pip install "diffct[cu13]" # PyTorch built for CUDA 13
57
60
  ```
58
61
 
62
+ The extra installs the CUDA compiler libraries that Numba CUDA needs to compile the kernels. A plain `pip install diffct` does not install them. Use it only if a CUDA Toolkit is already installed on the system.
63
+
59
64
  From source, with the examples:
60
65
 
61
66
  ```bash
@@ -98,11 +103,11 @@ loss = 0.5 * (A.project(x) - sinogram).square().sum()
98
103
  loss.backward() # x.grad = A^T (A x - y)
99
104
  ```
100
105
 
101
- **Custom or calibrated trajectories.** Pass calibrated tensors directly instead of a generator; no circular fit is needed. See [REFERENCE](docs/REFERENCE.md#custom-or-calibrated-trajectories).
106
+ **Custom or calibrated trajectories.** Pass calibrated tensors directly instead of a generator; no circular fit is needed. See [REFERENCE](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#custom-or-calibrated-trajectories).
102
107
 
103
- **Geometry gradients.** Set `requires_grad=True` on trajectory tensors before you build `Projector`. See [REFERENCE](docs/REFERENCE.md#geometry-gradients).
108
+ **Geometry gradients.** Set `requires_grad=True` on trajectory tensors before you build `Projector`. See [REFERENCE](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#geometry-gradients).
104
109
 
105
- **Several GPUs and nodes.** Use `devices=[0, 1, 2, 3]` in one process, or one process per GPU with torchrun for one or more nodes. See [REFERENCE](docs/REFERENCE.md#several-gpus-and-nodes) and [DISTRIBUTED.md](docs/DISTRIBUTED.md).
110
+ **Several GPUs and nodes.** Use `devices=[0, 1, 2, 3]` in one process, or one process per GPU with torchrun for one or more nodes. See [REFERENCE](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#several-gpus-and-nodes) and [DISTRIBUTED.md](https://github.com/sypsyp97/diffct/blob/main/docs/DISTRIBUTED.md).
106
111
 
107
112
  ```python
108
113
  A = Projector(trajectory, (128, 128, 128), (384, 256), detector_spacing=0.8, devices=[0, 1, 2, 3])
@@ -112,8 +117,8 @@ A = Projector(trajectory, (128, 128, 128), (384, 256), detector_spacing=0.8, dev
112
117
 
113
118
  <p align="center">
114
119
  <picture>
115
- <source media="(prefers-color-scheme: dark)" srcset="docs/assets/scaling_dark.png">
116
- <img src="docs/assets/scaling_light.png" width="100%" alt="Time per forward, adjoint and CGLS iteration on 1, 4 and 8 GPUs, 64^3 and 128^3">
120
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/scaling_dark.png">
121
+ <img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/scaling_light.png" width="100%" alt="Time per forward, adjoint and CGLS iteration on 1, 4 and 8 GPUs, 64^3 and 128^3">
117
122
  </picture>
118
123
  </p>
119
124
 
@@ -123,7 +128,7 @@ A = Projector(trajectory, (128, 128, 128), (384, 256), detector_spacing=0.8, dev
123
128
  | Adjoint (backprojection) | 20.66 ms | 5.89 ms (3.5×) | 4.62 ms (4.5×) |
124
129
  | CGLS iteration | 30.08 ms | 8.08 ms (3.7×) | 5.59 ms (5.4×) |
125
130
 
126
- Circular trajectory, 360 views, detector (2n, 1.5n) cells at pitch 1.25, A100 64 GB GPUs. Small volumes scale less: at 64³, one CGLS iteration takes 4.63 ms on 1 GPU, 1.57 ms on 4 and 1.56 ms on 8. Raw data: [docs/assets/scaling.json](docs/assets/scaling.json).
131
+ Circular trajectory, 360 views, detector (2n, 1.5n) cells at pitch 1.25, A100 64 GB GPUs. Small volumes scale less: at 64³, one CGLS iteration takes 4.63 ms on 1 GPU, 1.57 ms on 4 and 1.56 ms on 8. Raw data: [docs/assets/scaling.json](https://github.com/sypsyp97/diffct/blob/main/docs/assets/scaling.json).
127
132
 
128
133
  To measure one against several GPUs on your machine (default scan of the script, not the table setup):
129
134
 
@@ -136,7 +141,7 @@ python examples/benchmark_projector.py --devices 0 1
136
141
  **Measured walnut.** 240 measured views, circular cone beam, 256³: FDK (Hann window), SIRT (200 iterations), CGLS (20), TV (300, weight 0.3); axial and coronal centre slices.
137
142
 
138
143
  <p align="center">
139
- <img src="docs/assets/walnut_measured.png" width="100%" alt="Measured walnut: FDK, SIRT, CGLS and TV reconstructions">
144
+ <img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/walnut_measured.png" width="100%" alt="Measured walnut: FDK, SIRT, CGLS and TV reconstructions">
140
145
  </p>
141
146
 
142
147
  ```bash
@@ -146,7 +151,7 @@ python examples/walnut_reconstruction.py --figure out.png
146
151
  **Simulated helical scan of the walnut.** The FDK walnut volume as ground truth, 720 views, 1% noise, 256³. PSNR: FDK 26.67 dB, CGLS (30 iterations) 34.13 dB, SIRT (200) 34.52 dB, TV (200, weight 1.0) 37.77 dB.
147
152
 
148
153
  <p align="center">
149
- <img src="docs/assets/walnut_helical.png" width="100%" alt="Simulated helical scan of the walnut: FDK, CGLS, SIRT and TV reconstructions">
154
+ <img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/walnut_helical.png" width="100%" alt="Simulated helical scan of the walnut: FDK, CGLS, SIRT and TV reconstructions">
150
155
  </p>
151
156
 
152
157
  ```bash
@@ -154,22 +159,22 @@ python examples/walnut_reconstruction.py --algorithms --save-volume walnut256.np
154
159
  python examples/iterative_reconstruction.py --size 256 --views 720 --trajectory helical --noise 0.01 --phantom walnut256.npy --figure out.png
155
160
  ```
156
161
 
157
- Walnut data: Meaney 2022, Zenodo 6986012, CC BY 4.0; see [examples/data/NOTICE](examples/data/NOTICE).
162
+ Walnut data: Meaney 2022, Zenodo 6986012, CC BY 4.0; see [examples/data/NOTICE](https://github.com/sypsyp97/diffct/blob/main/examples/data/NOTICE).
158
163
 
159
164
  ## Examples
160
165
 
161
- `quickstart.py`, `analytical_reconstruction.py`, `iterative_reconstruction.py`, `walnut_reconstruction.py`, `geometry_calibration.py`, `benchmark_projector.py`, `plot_trajectory.py`, and the Slurm template `slurm/multi_node.sbatch`. Launch modes, distributed-loss rules and measured results are in [examples/README.md](examples/README.md).
166
+ `quickstart.py`, `analytical_reconstruction.py`, `iterative_reconstruction.py`, `walnut_reconstruction.py`, `geometry_calibration.py`, `benchmark_projector.py`, `plot_trajectory.py`, and the Slurm template `slurm/multi_node.sbatch`. Launch modes and distributed-loss rules are in [examples/README.md](https://github.com/sypsyp97/diffct/blob/main/examples/README.md).
162
167
 
163
168
  ## Documentation
164
169
 
165
170
  | Document | Contents |
166
171
  |---|---|
167
- | [Trajectory guide](docs/source/trajectories.rst) | Trajectory tuples and `Projector` usage |
168
- | [docs/REFERENCE.md](docs/REFERENCE.md) | Capabilities and limits, custom trajectories, geometry gradients, multi-GPU details, geometry and units |
169
- | [docs/DISTRIBUTED.md](docs/DISTRIBUTED.md) | Execution/memory choices, distributed loss rules, Slurm and cross-node checks |
170
- | [docs/MIGRATION.md](docs/MIGRATION.md) | Moving from the circular-only API |
171
- | [docs/video/README.md](docs/video/README.md) | How the intro video is rendered |
172
- | [CHANGELOG.md](CHANGELOG.md) | Release history |
172
+ | [Trajectory guide](https://github.com/sypsyp97/diffct/blob/main/docs/source/trajectories.rst) | Trajectory tuples and `Projector` usage |
173
+ | [docs/REFERENCE.md](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md) | Capabilities and limits, custom trajectories, geometry gradients, multi-GPU details, geometry and units |
174
+ | [docs/DISTRIBUTED.md](https://github.com/sypsyp97/diffct/blob/main/docs/DISTRIBUTED.md) | Execution/memory choices, distributed loss rules, Slurm and cross-node checks |
175
+ | [docs/MIGRATION.md](https://github.com/sypsyp97/diffct/blob/main/docs/MIGRATION.md) | Moving from the circular-only API |
176
+ | [docs/video/README.md](https://github.com/sypsyp97/diffct/blob/main/docs/video/README.md) | How the intro video is rendered |
177
+ | [CHANGELOG.md](https://github.com/sypsyp97/diffct/blob/main/CHANGELOG.md) | Release history |
173
178
 
174
179
  Run the test suite on a CUDA host with `python -m pytest tests/ -q`.
175
180
 
@@ -201,7 +206,7 @@ If you use this library in your research, please cite the software and the techn
201
206
 
202
207
  ## License and acknowledgements
203
208
 
204
- Apache 2.0, see [LICENSE](LICENSE). The project draws on
209
+ Apache 2.0, see [LICENSE](https://github.com/sypsyp97/diffct/blob/main/LICENSE). The project draws on
205
210
  [PYRO-NN](https://github.com/csyben/PYRO-NN) and
206
211
  [geometry_gradients_CT](https://github.com/mareikethies/geometry_gradients_CT).
207
- Issues and pull requests are welcome. The walnut data are CC BY 4.0 (Meaney 2022); see [NOTICE](NOTICE).
212
+ Issues and pull requests are welcome. The walnut data are CC BY 4.0 (Meaney 2022); see [NOTICE](https://github.com/sypsyp97/diffct/blob/main/NOTICE).
@@ -49,7 +49,7 @@ try:
49
49
  except ImportError:
50
50
  pass
51
51
 
52
- __version__ = '2.0.1'
52
+ __version__ = '2.0.2'
53
53
 
54
54
  __all__ = [
55
55
  'Projector',
@@ -10,7 +10,6 @@ import contextlib
10
10
  import functools
11
11
  import inspect
12
12
  import math
13
- import sys
14
13
  import weakref
15
14
  import numpy as np
16
15
  import torch
@@ -191,16 +190,20 @@ def _on_device_of(name):
191
190
  def _keep_cuda_modules_at_exit():
192
191
  """Stop numba-cuda from unloading CUDA modules while the interpreter exits.
193
192
 
194
- numba-cuda queues module unloads and flushes the queue once it holds more
195
- than ``CUDA_DEALLOCS_COUNT`` items. After kernels were loaded on several
196
- GPUs, a flush during interpreter shutdown can unload the same library twice
197
- and corrupt the heap, so the process crashes after all work is done. The
198
- process exit releases these resources anyway.
193
+ numba-cuda queues module unloads and other deallocations, and flushes a
194
+ queue once it passes a count or byte limit. After kernels were loaded on
195
+ several GPUs, a flush during interpreter shutdown can unload the same
196
+ library twice and corrupt the heap, so the process crashes after all work
197
+ is done. The process exit releases these resources anyway.
198
+
199
+ Raising the limits is not enough: the byte limit is
200
+ ``int(capacity * CUDA_DEALLOCS_RATIO)``, and queues with no capacity set
201
+ have a limit of 0 bytes for any finite ratio. numba-cuda skips the flush
202
+ while a queue is disabled, so disable every queue for the rest of the exit.
199
203
  """
200
- from numba.cuda.core import config as cuda_config
204
+ from numba.cuda.cudadrv.driver import _PendingDeallocs
201
205
 
202
- cuda_config.CUDA_DEALLOCS_COUNT = sys.maxsize
203
- cuda_config.CUDA_DEALLOCS_RATIO = float("inf")
206
+ _PendingDeallocs.is_disabled = property(lambda self: True)
204
207
 
205
208
 
206
209
  class _ExitOrderAnchor:
@@ -0,0 +1,47 @@
1
+ # Intro video
2
+
3
+ `diffct_intro.py` is the Manim Community (v0.21) source of `docs/assets/diffct_intro.gif`
4
+ and `docs/assets/diffct_intro.mp4`. Rendering needs Manim, LaTeX and FFmpeg.
5
+ The scenes read three files in `docs/video/`:
6
+
7
+ - `walnut_measured.npz`: measured projections and reconstruction slices.
8
+ - `data2d.npz`: a walnut slice and a display sinogram made with SciPy.
9
+ - `calib_history.json`: saved results from a separate geometry calibration run.
10
+
11
+ These inputs are included in the checkout. Rendering them does not need CUDA.
12
+ The scenes do not read `recon_slices.npz` or `recon_psnr.json`.
13
+
14
+ To rebuild the inputs, install SciPy in the environment that runs `make_inputs.py`.
15
+ SciPy is not a core diffct dependency.
16
+ Run these commands from the repository root:
17
+
18
+ ```bash
19
+ python -m pip install scipy
20
+ python docs/video/make_inputs.py --gpu
21
+ ```
22
+
23
+ `--gpu` needs a CUDA-enabled diffct installation. It writes `walnut_measured.npz`,
24
+ `recon_slices.npz`, `recon_psnr.json` and `data2d.npz`.
25
+ Without `--gpu`, the script writes only `data2d.npz` and reads the existing `walnut_measured.npz`.
26
+ Neither mode writes `calib_history.json`. The calibration example does not export this JSON file.
27
+
28
+ Render and join the scenes:
29
+
30
+ ```bash
31
+ cd docs/video
32
+ rm -f scenes.txt
33
+ for s in S00Intro S01Trajectory S02Autograd S03Geometry S04MultiGPU S05Measured S06End; do
34
+ manim -qh diffct_intro.py $s
35
+ echo "file 'media/videos/diffct_intro/1080p60/$s.mp4'" >> scenes.txt
36
+ done
37
+ ffmpeg -f concat -safe 0 -i scenes.txt -c:v libx264 -preset slow -crf 26 -pix_fmt yuv420p -r 30 \
38
+ -movflags +faststart -an ../assets/diffct_intro.mp4
39
+ ffmpeg -i ../assets/diffct_intro.mp4 -vf "fps=8,scale=800:-1:flags=lanczos,palettegen=max_colors=64:stats_mode=full" palette.png
40
+ ffmpeg -i ../assets/diffct_intro.mp4 -i palette.png \
41
+ -lavfi "fps=8,scale=800:-1:flags=lanczos[x];[x][1:v]paletteuse=dither=none" ../assets/diffct_intro.gif
42
+ ```
43
+
44
+ The reconstructions come from the measured walnut in `examples/data/walnut_cone.npz`
45
+ (Meaney 2022, CC BY 4.0; see `examples/data/NOTICE`). The timings in the multi-GPU scene
46
+ are measured diffct results on A100 64 GB GPUs; see `docs/assets/scaling.json`.
47
+ The scene stores these timings as constants. Rebuilding the inputs does not update them.
@@ -21,10 +21,12 @@ python examples/iterative_reconstruction.py --size 32 --views 32 \
21
21
  --trajectory helical --algorithms cgls --iterations 5
22
22
  ```
23
23
 
24
- This still computes an FDK baseline; five CGLS steps are a usage example, not
25
- the reconstruction-quality configuration in the results below. The default
26
- iterative run uses a 128³ volume, 360 views, and CGLS/SIRT/TV with 30/200/200
27
- iterations respectively.
24
+ This still computes an FDK baseline. Five CGLS steps show how to use the script.
25
+ The default iterative run uses a 128³ volume, 360 views, and CGLS/SIRT/TV with 30/200/200
26
+ iterations respectively. Its TV weight is 1.0.
27
+ The measured walnut script uses SIRT/CGLS/TV with 200/20/300 iterations and TV weight 0.3.
28
+ These weights are example settings. Choose the TV weight for your geometry,
29
+ voxel spacing, resolution and data scale.
28
30
 
29
31
  To inspect a helical trajectory without a GPU (PyTorch and matplotlib required):
30
32
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "diffct"
7
- version = "2.0.1"
7
+ version = "2.0.2"
8
8
  description = "Differentiable CUDA projectors for CT: arbitrary trajectories, multi-GPU and multi-node execution, geometry gradients"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -0,0 +1,83 @@
1
+ """CPU regression checks for CUDA deallocation queues during shutdown."""
2
+
3
+ import logging
4
+
5
+ import pytest
6
+
7
+
8
+ @pytest.mark.parametrize("capacity", [None, 80 * 1024**3], ids=["unset", "80-GiB"])
9
+ @pytest.mark.parametrize("with_size", [False, True], ids=["unsized", "sized"])
10
+ def test_exit_hook_keeps_cuda_deallocations_pending(monkeypatch, capacity, with_size):
11
+ try:
12
+ from numba.cuda.cudadrv import driver
13
+ from numba.cuda.cudadrv.driver import _PendingDeallocs
14
+ except ImportError as error:
15
+ pytest.skip(f"CUDA deallocation queue cannot be imported: {error}")
16
+
17
+ from numba.core import config
18
+ from diffct.utils import _keep_cuda_modules_at_exit
19
+
20
+ # Queue logging normally starts when a CUDA driver is initialized.
21
+ monkeypatch.setattr(driver, "_logger", logging.getLogger(__name__), raising=False)
22
+ # Capture all shared state before the hook can replace or add attributes.
23
+ class_attributes = dict(vars(_PendingDeallocs))
24
+ configurations = {id(configuration): configuration for configuration in (config, driver.config)}
25
+ configuration_attributes = {
26
+ identifier: {
27
+ name: value
28
+ for name, value in vars(configuration).items()
29
+ if name.startswith("CUDA_DEALLOCS_")
30
+ }
31
+ for identifier, configuration in configurations.items()
32
+ }
33
+
34
+ queue = _PendingDeallocs() if capacity is None else _PendingDeallocs(capacity)
35
+ destroyed = []
36
+
37
+ def destructor(handle):
38
+ destroyed.append(handle)
39
+
40
+ try:
41
+ _keep_cuda_modules_at_exit()
42
+
43
+ # Sizes are bookkeeping values; this test does not allocate CUDA memory.
44
+ size = (80 * 1024**3) // 32
45
+ for handle in range(64):
46
+ if with_size:
47
+ queue.add_item(destructor, handle, size)
48
+ else:
49
+ queue.add_item(destructor, handle)
50
+ assert destroyed == []
51
+ assert len(queue) == handle + 1
52
+ finally:
53
+ for name in set(vars(_PendingDeallocs)) - class_attributes.keys():
54
+ delattr(_PendingDeallocs, name)
55
+ for name, value in class_attributes.items():
56
+ if name not in vars(_PendingDeallocs) or vars(_PendingDeallocs)[name] is not value:
57
+ setattr(_PendingDeallocs, name, value)
58
+ for identifier, configuration in configurations.items():
59
+ saved = configuration_attributes[identifier]
60
+ for name in tuple(vars(configuration)):
61
+ if name.startswith("CUDA_DEALLOCS_") and name not in saved:
62
+ delattr(configuration, name)
63
+ for name, value in saved.items():
64
+ setattr(configuration, name, value)
65
+
66
+ assert dict(vars(_PendingDeallocs)) == class_attributes
67
+ for identifier, configuration in configurations.items():
68
+ assert {
69
+ name: value
70
+ for name, value in vars(configuration).items()
71
+ if name.startswith("CUDA_DEALLOCS_")
72
+ } == configuration_attributes[identifier]
73
+
74
+ # A fresh queue must regain the stock count-triggered flush after restoration.
75
+ stock_queue = _PendingDeallocs()
76
+ stock_destroyed = []
77
+ count_limit = driver.config.CUDA_DEALLOCS_COUNT
78
+ for handle in range(count_limit):
79
+ stock_queue.add_item(stock_destroyed.append, handle)
80
+ assert stock_destroyed == []
81
+ stock_queue.add_item(stock_destroyed.append, count_limit)
82
+ assert stock_destroyed == list(range(count_limit + 1))
83
+ assert len(stock_queue) == 0
@@ -81,3 +81,8 @@ def test_multi_gpu_exit():
81
81
  f"CUDA subprocess exited with return code {child.returncode}\n"
82
82
  f"Child stderr tail:\n{child.stderr[-8000:]}"
83
83
  )
84
+ for marker in ("Exception ignored", "Traceback", "ValueError", "OverflowError"):
85
+ assert marker not in child.stderr, (
86
+ f"CUDA subprocess reported {marker!r} during cleanup\n"
87
+ f"Child stderr tail:\n{child.stderr[-8000:]}"
88
+ )
@@ -1,23 +0,0 @@
1
- # Intro video
2
-
3
- `diffct_intro.py` is the Manim Community (v0.21) source of `docs/assets/diffct_intro.gif`
4
- and `docs/assets/diffct_intro.mp4`. Make the inputs on a CUDA GPU, then render and join the scenes:
5
-
6
- ```bash
7
- python docs/video/make_inputs.py --gpu # walnut reconstructions; without --gpu only data2d.npz
8
- cd docs/video
9
- rm -f scenes.txt
10
- for s in S00Intro S01Trajectory S02Autograd S03Geometry S04MultiGPU S05Measured S06End; do
11
- manim -qh diffct_intro.py $s
12
- echo "file 'media/videos/diffct_intro/1080p60/$s.mp4'" >> scenes.txt
13
- done
14
- ffmpeg -f concat -safe 0 -i scenes.txt -c:v libx264 -preset slow -crf 26 -pix_fmt yuv420p -r 30 \
15
- -movflags +faststart -an ../assets/diffct_intro.mp4
16
- ffmpeg -i ../assets/diffct_intro.mp4 -vf "fps=8,scale=800:-1:flags=lanczos,palettegen=max_colors=64:stats_mode=full" palette.png
17
- ffmpeg -i ../assets/diffct_intro.mp4 -i palette.png \
18
- -lavfi "fps=8,scale=800:-1:flags=lanczos[x];[x][1:v]paletteuse=dither=none" ../assets/diffct_intro.gif
19
- ```
20
-
21
- The reconstructions come from the measured walnut in `examples/data/walnut_cone.npz`
22
- (Meaney 2022, CC BY 4.0; see `examples/data/NOTICE`). The timings in the multi-GPU scene
23
- are measured diffct results on A100 64 GB GPUs; see `docs/assets/scaling.json`.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes