diffct 1.2.10__tar.gz → 1.3.0__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 (49) hide show
  1. diffct-1.3.0/CHANGELOG.md +295 -0
  2. {diffct-1.2.10 → diffct-1.3.0}/PKG-INFO +1 -1
  3. {diffct-1.2.10 → diffct-1.3.0}/diffct/differentiable.py +1494 -78
  4. {diffct-1.2.10 → diffct-1.3.0}/docs/source/api.rst +71 -0
  5. {diffct-1.2.10 → diffct-1.3.0}/examples/fbp_fan.py +86 -26
  6. {diffct-1.2.10 → diffct-1.3.0}/examples/fdk_cone.py +152 -32
  7. {diffct-1.2.10 → diffct-1.3.0}/examples/iterative_reco_cone.py +64 -19
  8. {diffct-1.2.10 → diffct-1.3.0}/examples/iterative_reco_fan.py +59 -19
  9. {diffct-1.2.10 → diffct-1.3.0}/pyproject.toml +1 -1
  10. diffct-1.3.0/pytest.ini +14 -0
  11. diffct-1.3.0/tests/benchmarks/README.md +60 -0
  12. diffct-1.3.0/tests/benchmarks/__init__.py +0 -0
  13. diffct-1.3.0/tests/benchmarks/_common.py +62 -0
  14. diffct-1.3.0/tests/benchmarks/test_bench_cone.py +145 -0
  15. diffct-1.3.0/tests/benchmarks/test_bench_fan.py +127 -0
  16. diffct-1.3.0/tests/benchmarks/test_bench_parallel.py +115 -0
  17. {diffct-1.2.10 → diffct-1.3.0}/tests/test_adjoint_inner_product.py +131 -0
  18. diffct-1.3.0/tests/test_gradcheck.py +209 -0
  19. diffct-1.3.0/tests/test_ramp_filter_windows.py +217 -0
  20. diffct-1.2.10/pytest.ini +0 -3
  21. {diffct-1.2.10 → diffct-1.3.0}/.github/workflows/docs.yml +0 -0
  22. {diffct-1.2.10 → diffct-1.3.0}/.github/workflows/release.yml +0 -0
  23. {diffct-1.2.10 → diffct-1.3.0}/.gitignore +0 -0
  24. {diffct-1.2.10 → diffct-1.3.0}/LICENSE +0 -0
  25. {diffct-1.2.10 → diffct-1.3.0}/README.md +0 -0
  26. {diffct-1.2.10 → diffct-1.3.0}/diffct/__init__.py +0 -0
  27. {diffct-1.2.10 → diffct-1.3.0}/docs/Makefile +0 -0
  28. {diffct-1.2.10 → diffct-1.3.0}/docs/source/_static/.gitkeep +0 -0
  29. {diffct-1.2.10 → diffct-1.3.0}/docs/source/conf.py +0 -0
  30. {diffct-1.2.10 → diffct-1.3.0}/docs/source/examples.rst +0 -0
  31. {diffct-1.2.10 → diffct-1.3.0}/docs/source/fbp_fan_example.rst +0 -0
  32. {diffct-1.2.10 → diffct-1.3.0}/docs/source/fbp_parallel_example.rst +0 -0
  33. {diffct-1.2.10 → diffct-1.3.0}/docs/source/fdk_cone_example.rst +0 -0
  34. {diffct-1.2.10 → diffct-1.3.0}/docs/source/getting_started.rst +0 -0
  35. {diffct-1.2.10 → diffct-1.3.0}/docs/source/index.rst +0 -0
  36. {diffct-1.2.10 → diffct-1.3.0}/docs/source/iterative_reco_cone_example.rst +0 -0
  37. {diffct-1.2.10 → diffct-1.3.0}/docs/source/iterative_reco_fan_example.rst +0 -0
  38. {diffct-1.2.10 → diffct-1.3.0}/docs/source/iterative_reco_parallel_example.rst +0 -0
  39. {diffct-1.2.10 → diffct-1.3.0}/examples/fbp_parallel.py +0 -0
  40. {diffct-1.2.10 → diffct-1.3.0}/examples/iterative_reco_parallel.py +0 -0
  41. {diffct-1.2.10 → diffct-1.3.0}/requirements.txt +0 -0
  42. {diffct-1.2.10 → diffct-1.3.0}/tests/test_cone_projector_autograd.py +0 -0
  43. {diffct-1.2.10 → diffct-1.3.0}/tests/test_cuda_smoke.py +0 -0
  44. {diffct-1.2.10 → diffct-1.3.0}/tests/test_fbp_fan_accuracy.py +0 -0
  45. {diffct-1.2.10 → diffct-1.3.0}/tests/test_fbp_fan_offsets.py +0 -0
  46. {diffct-1.2.10 → diffct-1.3.0}/tests/test_fbp_parallel_accuracy.py +0 -0
  47. {diffct-1.2.10 → diffct-1.3.0}/tests/test_fdk_cone_accuracy.py +0 -0
  48. {diffct-1.2.10 → diffct-1.3.0}/tests/test_fdk_cone_offsets.py +0 -0
  49. {diffct-1.2.10 → diffct-1.3.0}/tests/test_weights.py +0 -0
@@ -0,0 +1,295 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.3.0] - 2026-04-14
11
+
12
+ ### Added
13
+ - **Separable-footprint (SF) projector backends** for 2D fan and 3D cone
14
+ beam geometries, following Long-Fessler-Balter (IEEE TMI 2010).
15
+ `FanProjectorFunction` / `FanBackprojectorFunction` now accept
16
+ `backend="siddon"` (default, unchanged) or `backend="sf"`.
17
+ `ConeProjectorFunction` / `ConeBackprojectorFunction` accept
18
+ `backend="siddon"` (default), `"sf_tr"` (trapezoid in transaxial,
19
+ rectangle in axial) or `"sf_tt"` (trapezoid in both directions with
20
+ axial rise/fall from `U_near`/`U_far` across the voxel). Each SF
21
+ backend is implemented as a matched scatter/gather pair of CUDA
22
+ kernels so both forward and adjoint are available — autograd works
23
+ end-to-end for every backend, and the standalone Backprojector
24
+ Function can also be driven in SF mode for iterative reconstruction.
25
+ - **Adjoint inner-product tests** for every new SF backend in
26
+ `tests/test_adjoint_inner_product.py`, plus `torch.autograd.gradcheck`
27
+ coverage in `tests/test_gradcheck.py`. Test count grew from 57 to 66.
28
+
29
+ ### Changed
30
+ - **Internal grid-point convention** is now uniformly `x_v = ix - cx`
31
+ (matching `_cone_3d_fdk_backproject_kernel`). The SF kernels use the
32
+ same convention so voxel centers align with where the Siddon/FDK
33
+ paths interpret `d_vol[ix, iy, iz]`.
34
+ - **`_TPB_SF_3D = (4, 4, 4)`** introduced for the 3D SF kernels. SF-TT
35
+ in particular exceeds the 128-register-per-thread budget allowed by
36
+ the default `(8, 8, 8)` launch; the smaller block gives each thread
37
+ ~1024 registers and avoids `CUDA_ERROR_LAUNCH_OUT_OF_RESOURCES`.
38
+
39
+ ### Notes
40
+ - **Raw forward accuracy vs analytical Radon**: on both 2D fan
41
+ (512² Shepp-Logan) and 3D cone (96³ and 128³ 3D Shepp-Logan) phantoms,
42
+ measured against the analytical ellipse / ellipsoid Radon transform,
43
+ the existing Siddon projector with bilinear / trilinear interpolation
44
+ is slightly more accurate (~3% better RMSE, ~15% better max error)
45
+ than SF-TR / SF-TT at the thin-ray sampling level. The classic SF
46
+ advantage reported in Long et al. is over the piecewise-constant DD
47
+ projector; diffct's Siddon uses smooth interpolation, which covers
48
+ most of the gap SF was designed to close.
49
+ - **Full analytical reconstruction (forward + FBP/FDK)**: despite the
50
+ raw-forward result above, in a complete analytical pipeline
51
+ (`FanProjectorFunction` / `ConeProjectorFunction` -> `ramp_filter_1d`
52
+ -> `fan_weighted_backproject` / `cone_weighted_backproject`) SF
53
+ **measurably improves reconstruction quality**. On the 256²
54
+ Shepp-Logan `fbp_fan.py` example the raw MSE drops from
55
+ `0.00216` (Siddon) to `0.00178` (SF-TR), a ~17% improvement.
56
+ On the 128³ `fdk_cone.py` example it drops from `0.00325` (Siddon)
57
+ to `0.00271` (SF-TR), again ~17%. The mechanism is that SF's
58
+ mass-conserving cell-integrated forward pairs more naturally with
59
+ the voxel-driven FBP/FDK gather backprojector than Siddon's
60
+ thin-ray sampling, which feeds extra high-frequency ringing into
61
+ the ramp filter.
62
+ - **Performance**: SF-TR is ~2× and SF-TT ~2.5–4× slower than Siddon for
63
+ cone beam, and ~3× slower than Siddon for fan beam. The extra cost
64
+ comes from voxel-driven scatter with atomic adds (forward) versus
65
+ ray-driven parallelism in Siddon.
66
+ - **Recommendation**: `backend="siddon"` remains the default for
67
+ backward compatibility and raw speed. For **analytical FBP/FDK
68
+ reconstruction** where reconstruction MSE matters more than forward
69
+ runtime, `"sf"` / `"sf_tr"` is a quantifiably better choice — at
70
+ 2–3× forward cost you get ~17% lower reconstruction MSE on standard
71
+ phantoms. For **iterative reconstruction with autograd**, either
72
+ backend is valid; SF has the theoretical advantage of exact voxel-
73
+ scale mass conservation on both forward and adjoint, but in practice
74
+ Siddon's smoothing usually helps optimizer convergence. SF-TT's
75
+ axial-trapezoid refinement over SF-TR improves RMSE only by ~0.001
76
+ and is not worth the extra cost for typical cone angles; use SF-TR
77
+ by default and reserve SF-TT for extreme cone-angle research.
78
+
79
+ ## [1.2.11] - 2026-04-14
80
+
81
+ ### Added
82
+ - **Parker short-scan demos** in `examples/fdk_cone.py` and `examples/fbp_fan.py`.
83
+ Both examples now expose an `apply_parker` switch that not only multiplies
84
+ by `parker_weights` but also samples angles on the minimal short-scan range
85
+ `pi + 2*gamma_max`, where `gamma_max` is computed from the detector half-width
86
+ and `sdd`. (Before this change the `apply_parker` flag in `fbp_fan.py` was
87
+ silently a no-op because the angular range stayed at `2*pi`.)
88
+ - **`tests/test_gradcheck.py`** — `torch.autograd.gradcheck` coverage for
89
+ `ParallelProjectorFunction`, `FanProjectorFunction`, and `ConeProjectorFunction`.
90
+ Compares the analytical backward Jacobian to a finite-difference numerical
91
+ Jacobian over small inputs with float32-calibrated tolerances. Strongest
92
+ autograd-correctness guard beyond the existing inner-product adjoint tests.
93
+ - **`tests/test_ramp_filter_windows.py`** — 27 parametrised tests covering every
94
+ `ramp_filter_1d` window option (`None`/`"ram-lak"`, `"hann"`, `"hanning"`,
95
+ `"hamming"`, `"cosine"`, `"shepp-logan"`) at three layers: direct `_ramp_window`
96
+ helper (DC gain, Nyquist value, monotonicity), full `ramp_filter_1d` end-to-end
97
+ (shape, DC annihilation, pad correctness, rfft vs complex-fft parity,
98
+ `sample_spacing` scaling), and a high-frequency attenuation sanity check on a
99
+ step input.
100
+ - **`CHANGELOG.md`** — new Keep-a-Changelog file at the repo root.
101
+ - **`tests/benchmarks/`** — opt-in `pytest-benchmark` suite covering every CUDA
102
+ kernel in the library (forward projector, pure-adjoint backprojector, and
103
+ analytical FBP/FDK pipeline) across three sizes for each of the three
104
+ geometries. 27 benchmarks total. Excluded from the default `pytest tests/`
105
+ run via `--ignore=tests/benchmarks` in `pytest.ini` so the normal test suite
106
+ stays fast; run explicitly with `pytest tests/benchmarks/ --benchmark-only`.
107
+ Enables before/after perf comparisons via `--benchmark-save=` /
108
+ `--benchmark-compare=`.
109
+
110
+ ### Changed
111
+ - **`_cone_3d_fdk_backproject_kernel`** memory-access layout. The kernel now
112
+ unpacks `cuda.grid(3)` as `(iz, iy, ix)` instead of `(ix, iy, iz)`, and
113
+ `cone_weighted_backproject` launches the grid as `_grid_3d(D, H, W)` instead
114
+ of `_grid_3d(W, H, D)`. This makes `iz` warp-adjacent, which matches the
115
+ innermost stride-1 axis of the WHD output buffer, so the final `d_vol` write
116
+ becomes coalesced. Measured speedup on a 64^3–160^3 sweep: 1.0x–1.14x,
117
+ within run-to-run noise (the kernel is read-bound on the sinogram bilinear
118
+ samples, not write-bound, so L2 absorbed the old uncoalesced writes). The
119
+ fix is kept as the right default and future-proofs the kernel against
120
+ edits that shift work toward the write path. Matching coalescing comments
121
+ were added to the 2D `_parallel_2d_fbp_backproject_kernel` and
122
+ `_fan_2d_fbp_backproject_kernel` kernels, which were already coalesced
123
+ (their first grid index is also warp-adjacent and matches the `Nx` innermost
124
+ axis of their `(Ny, Nx)` output buffers).
125
+
126
+ ### Notes
127
+ - No public-API changes. Pure-adjoint kernels and autograd Function classes
128
+ are untouched, so the autograd and iterative-reconstruction paths are
129
+ byte-for-byte equivalent to 1.2.10.
130
+
131
+ ## [1.2.10] - 2026-04-14
132
+
133
+ This release is a correctness overhaul of every analytical reconstruction path
134
+ (parallel / fan / cone) **and** of the autograd adjoint path shared by the
135
+ iterative examples. It also ships a unified architecture where each geometry
136
+ has its own dedicated voxel-driven FBP/FDK gather kernel, separate from the
137
+ pure Siddon adjoint used by autograd.
138
+
139
+ ### Fixed
140
+
141
+ #### Analytical FBP / FDK amplitude bugs
142
+
143
+ Prior to this release, all three analytical reconstruction examples produced
144
+ results that were off by large constant factors:
145
+
146
+ | Example | Pre-fix reco range | Post-fix reco range | MSE improvement |
147
+ |---|---|---|---|
148
+ | `fbp_parallel.py` | [0, 6.52] | [−0.02, 1.01] | ~580× |
149
+ | `fbp_fan.py` | [0, 6.33] | [−0.08, 1.01] | ~900× |
150
+ | `fdk_cone.py` | [0, 10.08] | [−0.08, 1.00] | ~600× |
151
+
152
+ Root causes:
153
+ - Cone and fan used `(sdd/U)^2` with `U = sdd + x·sin − y·cos` instead of
154
+ the correct FDK weight `(sid/U)^2` with `U = sid + x·sin − y·cos`.
155
+ - All three geometries were missing the `1/(2π)` Fourier-convention
156
+ constant from the FBP/FDK reconstruction formula.
157
+ - Cone and fan used a Siddon ray-driven scatter for the analytical path,
158
+ which is neither a true adjoint nor a classical FBP/FDK gather.
159
+
160
+ #### Autograd adjoint bug (cone + fan)
161
+
162
+ `ConeProjectorFunction.backward`, `ConeBackprojectorFunction.forward`,
163
+ `FanProjectorFunction.backward` and `FanBackprojectorFunction.forward`
164
+ were all passing `distance_weight=1.0` to the shared Siddon backward
165
+ kernel. This meant the autograd backward was **not the true adjoint** `P^T`
166
+ of the forward projector — it had a `(sdd/U)^2` per-voxel factor baked in,
167
+ biasing the gradient by ~2–3× depending on voxel position. Any iterative
168
+ reconstruction that relied on autograd (including `iterative_reco_cone.py`
169
+ and `iterative_reco_fan.py`) was running on a biased gradient flow.
170
+
171
+ Parallel beam was unaffected (its backward kernel had no `distance_weight`
172
+ parameter to begin with).
173
+
174
+ This release removes the `distance_weight` parameter from both
175
+ `_cone_3d_backward_kernel` and `_fan_2d_backward_kernel` entirely, so the
176
+ dead code path cannot be accidentally reintroduced, and fixes the four
177
+ autograd call sites to use the pure adjoint.
178
+
179
+ ### Added
180
+
181
+ #### Voxel-driven gather kernels for every geometry
182
+
183
+ Three new CUDA kernels, all under a dedicated `fastmath=False` decorator
184
+ for FDK-grade accuracy:
185
+
186
+ - `_parallel_2d_fbp_backproject_kernel` — no distance weighting (no source).
187
+ - `_fan_2d_fbp_backproject_kernel` — `(sid/U)^2` + linear detector interp.
188
+ - `_cone_3d_fdk_backproject_kernel` — `(sid/U)^2` + bilinear detector interp.
189
+
190
+ Each kernel is voxel-driven: one thread per output pixel/voxel, loops over
191
+ views, computes the projected detector coordinate, interpolates the filtered
192
+ sinogram, weights and accumulates.
193
+
194
+ #### `parallel_weighted_backproject` (new public helper)
195
+
196
+ Mirrors `fan_weighted_backproject` and `cone_weighted_backproject`. Applies
197
+ the `1/(2π)` Fourier-convention constant so a unit-density disk reconstructs
198
+ to amplitude 1. Exported from `diffct`.
199
+
200
+ #### `ramp_filter_1d` — new backward-compatible kwargs
201
+
202
+ - `sample_spacing` (default `1.0`): physical detector cell pitch. Output is
203
+ rescaled by `1/sample_spacing` for physical-unit correctness.
204
+ - `pad_factor` (default `1`): zero-pad to `pad_factor * N` before the FFT to
205
+ suppress circular-convolution wrap-around. `2` is recommended for FBP/FDK.
206
+ - `window` (default `None`): frequency-domain apodization. Options: `None` /
207
+ `"ram-lak"`, `"hann"`, `"hamming"`, `"cosine"`, `"shepp-logan"`.
208
+ - `use_rfft` (default `True`): faster real-FFT path for real-valued inputs.
209
+
210
+ Existing `ramp_filter_1d(sino, dim=1)` calls keep working unchanged.
211
+
212
+ #### Analytical FBP / FDK scale factors
213
+
214
+ Each analytical helper now applies the correct analytical constant
215
+ automatically so reconstructions are already amplitude-calibrated:
216
+
217
+ - `parallel_weighted_backproject`: `1 / (2π)`.
218
+ - `fan_weighted_backproject`: `sdd / (2π · sid)`.
219
+ - `cone_weighted_backproject`: `sdd / (2π · sid)`.
220
+
221
+ ### Tests
222
+
223
+ Test suite expanded from 6 to **27 tests**:
224
+
225
+ - `tests/test_adjoint_inner_product.py` — the definitive check. For random `x`
226
+ and `y`, asserts `⟨A x, y⟩ = ⟨x, A^T y⟩` for parallel, fan and cone autograd
227
+ pairs. Permanently guards against any regression that would reintroduce the
228
+ `distance_weight=1.0` bug.
229
+ - `tests/test_fdk_cone_accuracy.py` — 128³ Shepp-Logan RMSE / amplitude bounds.
230
+ - `tests/test_fdk_cone_offsets.py` — detector, center and combined offsets with
231
+ amplitude assertions.
232
+ - `tests/test_cone_projector_autograd.py` — gradient finiteness and non-zero
233
+ sanity.
234
+ - `tests/test_fbp_fan_accuracy.py` — 256×256 Shepp-Logan RMSE.
235
+ - `tests/test_fbp_fan_offsets.py` — three offset configurations.
236
+ - `tests/test_fbp_parallel_accuracy.py` — parallel FBP RMSE + offset.
237
+
238
+ ### Examples
239
+
240
+ `examples/fdk_cone.py`, `examples/fbp_fan.py` and `examples/fbp_parallel.py`
241
+ have been rewritten with a consistent 8-step layout and detailed inline
242
+ comments documenting every geometry variable (what it is, units, typical
243
+ values, available options). Each example prints raw MSE, clamped MSE, and the
244
+ reconstruction / phantom data ranges.
245
+
246
+ The iterative reconstruction examples (`iterative_reco_parallel.py`,
247
+ `iterative_reco_fan.py`, `iterative_reco_cone.py`) are unchanged — they
248
+ silently benefit from the fixed autograd adjoint.
249
+
250
+ ### Documentation
251
+
252
+ - `docs/source/api.rst`: adds autofunction entries for
253
+ `parallel_weighted_backproject`, documents the new `ramp_filter_1d`
254
+ options, and adds an "Analytical FBP / FDK architecture" section
255
+ explaining the scale-factor derivation.
256
+ - `docs/source/fdk_cone_example.rst`: formula updated to reflect the
257
+ voxel-gather kernel and `(sid/U)^2` weight.
258
+ - `docs/source/fbp_fan_example.rst` and `fbp_parallel_example.rst`: completely
259
+ rewritten to reference the new analytical helpers and fix a sign-convention
260
+ inconsistency in the math section.
261
+
262
+ ### Compatibility
263
+
264
+ This release is source-compatible with 1.2.9 for every public entry point. The
265
+ `distance_weight` parameter was internal and is removed from the private
266
+ backward kernels; no public API touched that argument.
267
+
268
+ ## [1.2.9] - 2026-02-14
269
+
270
+ Restored `main` to the state of 1.2.9 by reverting an accidental merge.
271
+ See [GitHub release v1.2.9](https://github.com/sypsyp97/diffct/releases/tag/v1.2.9).
272
+
273
+ ## [1.2.8] - 2026-02-14
274
+
275
+ See [GitHub release v1.2.8](https://github.com/sypsyp97/diffct/releases/tag/v1.2.8)
276
+ and the commit history for details.
277
+
278
+ ## [1.2.7] - 2025-08-05
279
+
280
+ See [GitHub release v1.2.7](https://github.com/sypsyp97/diffct/releases/tag/v1.2.7).
281
+
282
+ ## Earlier versions
283
+
284
+ Releases 1.2.0 through 1.2.6 and the 1.1.x / 1.0.x lines are tracked on
285
+ [GitHub releases](https://github.com/sypsyp97/diffct/releases). This CHANGELOG
286
+ was introduced in 1.2.10 and does not back-fill detailed notes for earlier
287
+ versions beyond pointers to the GitHub release pages.
288
+
289
+ [Unreleased]: https://github.com/sypsyp97/diffct/compare/v1.3.0...HEAD
290
+ [1.3.0]: https://github.com/sypsyp97/diffct/releases/tag/v1.3.0
291
+ [1.2.11]: https://github.com/sypsyp97/diffct/releases/tag/v1.2.11
292
+ [1.2.10]: https://github.com/sypsyp97/diffct/releases/tag/v1.2.10
293
+ [1.2.9]: https://github.com/sypsyp97/diffct/releases/tag/v1.2.9
294
+ [1.2.8]: https://github.com/sypsyp97/diffct/releases/tag/v1.2.8
295
+ [1.2.7]: https://github.com/sypsyp97/diffct/releases/tag/v1.2.7
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: diffct
3
- Version: 1.2.10
3
+ Version: 1.3.0
4
4
  Summary: A CUDA-based library for computed tomography (CT) projection and reconstruction with differentiable operators
5
5
  Project-URL: Homepage, https://github.com/sypsyp97/diffct
6
6
  Author-email: Yipeng Sun <yipeng.sun@fau.de>