diffct 2.4.3__tar.gz → 2.4.4__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.
- {diffct-2.4.3 → diffct-2.4.4}/.gitignore +5 -0
- {diffct-2.4.3 → diffct-2.4.4}/CHANGELOG.md +22 -1
- {diffct-2.4.3 → diffct-2.4.4}/CITATION.cff +2 -2
- {diffct-2.4.3 → diffct-2.4.4}/NOTICE +2 -0
- {diffct-2.4.3 → diffct-2.4.4}/PKG-INFO +7 -4
- {diffct-2.4.3 → diffct-2.4.4}/README.md +6 -3
- {diffct-2.4.3 → diffct-2.4.4}/diffct/__init__.py +1 -1
- {diffct-2.4.3 → diffct-2.4.4}/diffct/analytical.py +12 -9
- {diffct-2.4.3 → diffct-2.4.4}/diffct/analytical_execution.py +7 -3
- {diffct-2.4.3 → diffct-2.4.4}/diffct/backends.py +505 -110
- {diffct-2.4.3 → diffct-2.4.4}/diffct/chunking.py +3 -2
- {diffct-2.4.3 → diffct-2.4.4}/diffct/constants.py +12 -0
- {diffct-2.4.3 → diffct-2.4.4}/diffct/kernels/analytical_transpose.py +40 -16
- {diffct-2.4.3 → diffct-2.4.4}/diffct/kernels/cone_beam.py +117 -63
- {diffct-2.4.3 → diffct-2.4.4}/diffct/kernels/fan_beam.py +49 -33
- {diffct-2.4.3 → diffct-2.4.4}/diffct/kernels/geometry_vjp.py +153 -51
- {diffct-2.4.3 → diffct-2.4.4}/diffct/kernels/parallel_beam.py +45 -15
- diffct-2.4.4/diffct/kernels/spacing.py +79 -0
- {diffct-2.4.3 → diffct-2.4.4}/diffct/kernels/surface_geometry.py +15 -4
- {diffct-2.4.3 → diffct-2.4.4}/diffct/operators.py +67 -11
- {diffct-2.4.3 → diffct-2.4.4}/diffct/projectors.py +13 -6
- {diffct-2.4.3 → diffct-2.4.4}/diffct/reconstruction.py +7 -4
- {diffct-2.4.3 → diffct-2.4.4}/diffct/surfaces.py +7 -2
- diffct-2.4.4/docs/assets/gallery/NOTICE +29 -0
- diffct-2.4.4/docs/search/tests/.gitignore +2 -0
- diffct-2.4.4/docs/search/tests/fixtures/make_tiny_model.py +89 -0
- diffct-2.4.4/docs/search/tests/fixtures/tiny-model/config.json +15 -0
- diffct-2.4.4/docs/search/tests/fixtures/tiny-model/onnx/model_quantized.onnx +0 -0
- diffct-2.4.4/docs/search/tests/fixtures/tiny-model/onnx/model_quantized.onnx_data +0 -0
- diffct-2.4.4/docs/search/tests/fixtures/tiny-model/tokenizer.json +140 -0
- diffct-2.4.4/docs/search/tests/fixtures/tiny-model/tokenizer_config.json +6 -0
- diffct-2.4.4/docs/search/tests/full-model.spec.mjs +59 -0
- diffct-2.4.4/docs/search/tests/harness.mjs +473 -0
- diffct-2.4.4/docs/search/tests/item1-ordering.spec.mjs +106 -0
- diffct-2.4.4/docs/search/tests/item2-single-load.spec.mjs +71 -0
- diffct-2.4.4/docs/search/tests/item3-cancel.spec.mjs +93 -0
- diffct-2.4.4/docs/search/tests/item4-leaks.spec.mjs +123 -0
- diffct-2.4.4/docs/search/tests/item5-errors.spec.mjs +115 -0
- diffct-2.4.4/docs/search/tests/item6-a11y.spec.mjs +186 -0
- diffct-2.4.4/docs/search/tests/item7-security.spec.mjs +101 -0
- diffct-2.4.4/docs/search/tests/playwright.config.mjs +17 -0
- {diffct-2.4.3 → diffct-2.4.4}/formal/README.md +24 -12
- {diffct-2.4.3 → diffct-2.4.4}/pyproject.toml +1 -1
- diffct-2.4.4/tests/_native_per_axis_spacing.py +261 -0
- diffct-2.4.4/tests/fixtures/native_scalar_spacing_main.npz +0 -0
- diffct-2.4.4/tests/test_native_per_axis_admission.py +99 -0
- diffct-2.4.4/tests/test_native_per_axis_extremes.py +139 -0
- diffct-2.4.4/tests/test_native_per_axis_helical.py +93 -0
- diffct-2.4.4/tests/test_native_per_axis_spacing.py +505 -0
- diffct-2.4.4/tests/test_native_per_axis_spacing_gpu.py +237 -0
- diffct-2.4.4/tests/test_portable_bounded_autograd.py +945 -0
- diffct-2.4.4/tests/test_portable_cuda_budget.py +996 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_reference_backend.py +12 -4
- {diffct-2.4.3 → diffct-2.4.4}/LICENSE +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/diffct/_solver_state.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/diffct/_solver_tv.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/diffct/differentiable.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/diffct/distribution.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/diffct/geometry.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/diffct/kernels/__init__.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/diffct/pipeline.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/diffct/solvers.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/diffct/storage.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/diffct/streaming.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/diffct/surface_gradients.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/diffct/utils.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/docs/video/README.md +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/examples/README.md +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/examples/_block_cgls.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/examples/_common.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/examples/data/NOTICE +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/examples/data/preprocess_walnut.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/examples/walnut_reconstruction.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/pytest.ini +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/__init__.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/_multiprocessing.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/_phantoms.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/benchmarks/README.md +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/benchmarks/__init__.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/benchmarks/_common.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/benchmarks/test_bench_cone.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/benchmarks/test_bench_fan.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/benchmarks/test_bench_parallel.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/distributed_projector_check.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_adjoint_inner_product.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_allocator_capacity.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_analytical_autograd.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_analytical_distribution.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_analytical_regressions.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_analytical_weight_distribution.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_angular_low_precision.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_axis_aligned_chords.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_batch_execution_edges.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_batched_surfaces.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_block_cgls.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_block_storage.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_call_trajectory.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_cgls_state_admission.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_checkpoint_metadata_validation.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_chunked_projector.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_completed_transfer_reuse.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_cone_backprojection_transpose.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_cone_projector_autograd.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_cpu_install.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_cuda_exit_deallocations.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_cuda_sink_lifetimes.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_cuda_smoke.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_detector_surface_dtypes.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_detector_surface_mapping.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_detector_surface_units.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_detector_surfaces.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_distributed_training_transforms.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_example_psnr.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_execution_buffers.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_execution_features.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_execution_schedules.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_fbp_fan_accuracy.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_fbp_fan_offsets.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_fbp_parallel_accuracy.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_fdk_cone_accuracy.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_fdk_cone_offsets.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_geometry_autograd.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_geometry_conversions.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_geometry_gradients.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_gpu_performance_contracts.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_gradcheck.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_lazy_native_observers.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_live_trajectory_validation.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_multi_gpu_exit.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_multiprocessing.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_native_endpoint_precision.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_native_surface_transforms.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_native_volume_layout.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_native_volume_layout_distributed.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_output_pipeline_overlap.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_parallel3d.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_parallel3d_surface_oracle.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_partition_lifecycle.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_plan_memory_bounds.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_portable_distributed_geometry.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_portable_endpoint_precision.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_portable_execution_bounds.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_portable_numeric_regressions.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_portable_sampler_snapshot.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_portable_surface_admission.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_pr42_review_regressions.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_projector_api.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_projector_degenerate_geometry.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_projector_geometry_devices.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_ramp_filter_windows.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_reconstruction_api.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_reconstruction_work_reuse.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_release_2_0_1.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_review_230.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_review_regressions.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_solver_admission_regressions.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_solver_api.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_solver_checkpoint_edges.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_solver_collective_failures.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_solver_device_scalars.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_spatial_partition.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_surface_collective_failures.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_surface_composition_kernel.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_surface_compute_shards.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_surface_geometry_cudasim.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_surface_gpu_execution.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_surface_gradient_replay.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_surface_host_sampling_budget.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_surface_native_regressions.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_surface_parameter_updates.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_surface_pipeline_contracts.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_surface_saved_snapshot_budget.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_surface_snapshot_admission.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_surface_trajectory_transform.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_training_transforms.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_trajectory_admission_cost.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_trajectory_surface_integration.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_transfer_pipeline.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_transfer_retirement_lifetimes.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_tv_zero_dependency.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_two_far_endpoints.py +0 -0
- {diffct-2.4.3 → diffct-2.4.4}/tests/test_weights.py +0 -0
|
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.4.4] - 2026-10-25
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Native per-axis voxel spacing for projection, matched backprojection, geometry gradients and analytical FBP/FDK. Tensor axis order and physical length units match the PyTorch backend. Native pitches must be in `[1e-6, 1e6]`, with a maximum/minimum ratio of at most `1e4`. Outside these bounds, `numba` raises and `auto` selects PyTorch. On NVIDIA CUDA, the fallback warning names the reason. Scalar native arithmetic and admission are unchanged.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Portable (`backend="torch"`) autograd no longer records every ray batch and volume tile. Data gradients apply the matched adjoint, and geometry gradients recompute one ray batch and tile at a time. Saved autograd memory no longer grows with the number of batches, also inside `torch.vmap` and `torch.func.jvp` with differentiable inputs or tangents. Geometry backward splits large leading batches like the forward call and accepts empty leading batches. First-order values are unchanged.
|
|
19
|
+
- Second derivatives with respect to the geometry now raise an error on the single-process portable path, as documented. Before, they returned a value.
|
|
20
|
+
- Forward-mode derivatives (`torch.func.jvp`, `jacfwd`) with respect to `Projector` trajectory tensors now raise an error. Before, they returned zero tangents.
|
|
21
|
+
- Distributed operators reject trajectory dtype differences between ranks before gradient reduction, including per-call trajectory overrides.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- The portable PyTorch backend sizes its automatic volume tiles, view batches and pixel batches from CUDA memory when the compute devices are CUDA or ROCm devices. One call uses at most a quarter of the memory that the PyTorch allocator can still use when the call starts. This quarter covers work arrays and reduction buffers; geometry backward recomputes each pair with half of that limit. Geometry backward may also keep one cotangent-sized buffer outside that quarter, for a contiguous copy or a distributed sum. With replicated distributed outputs, geometry backward may hold up to three cotangent-sized buffers outside that quarter during the cotangent reduction. CPU inputs, outputs and host stores with CUDA compute devices stage at most the smaller of a quarter of the free host memory and the CUDA limit, but never less than 8 MiB. One work array stays below 2^31 elements. CPU calls keep the 8 MiB ceiling and their previous plans. Distributed ranks agree on the smallest limits. Results change only in floating-point summation order.
|
|
26
|
+
- Portable projection sets up the rays of a view/pixel batch once and reuses them for every volume tile. Projections, backprojections and their gradients are bitwise unchanged for the same chunks. Distributed backward through a backprojection keeps the previous tile order and holds one summed cotangent tile at a time.
|
|
27
|
+
- Portable outputs accumulate in place, so a call no longer holds a second full-size output. Execution statistics report the loop order that ran.
|
|
28
|
+
- `Projector(backend="auto")` emits one `UserWarning` when it selects the portable backend on an NVIDIA CUDA machine. The warning names the reasons. `backend="torch"` silences it.
|
|
29
|
+
- `backend="auto"` selects native NVIDIA execution for per-axis spacing in fast mode with views/space ownership. Strict precision modes and hybrid ownership continue to use PyTorch.
|
|
30
|
+
|
|
10
31
|
## [2.4.3] - 2026-10-22
|
|
11
32
|
|
|
12
33
|
### Changed
|
|
@@ -254,7 +275,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
254
275
|
|
|
255
276
|
Version 2.0.0 replaces the circular-orbit API of 1.x with arbitrary
|
|
256
277
|
per-view trajectories. It breaks the 1.x interface; see
|
|
257
|
-
[docs/MIGRATION.md](docs/MIGRATION.md).
|
|
278
|
+
[docs/MIGRATION.md](https://github.com/sypsyp97/diffct/blob/main/docs/MIGRATION.md).
|
|
258
279
|
|
|
259
280
|
### Breaking
|
|
260
281
|
|
|
@@ -13,8 +13,8 @@ authors:
|
|
|
13
13
|
given-names: Yipeng
|
|
14
14
|
email: yipeng.sun@fau.de
|
|
15
15
|
alias: sypsyp97
|
|
16
|
-
version: 2.4.
|
|
17
|
-
date-released: "2026-10-
|
|
16
|
+
version: 2.4.4
|
|
17
|
+
date-released: "2026-10-25"
|
|
18
18
|
license: Apache-2.0
|
|
19
19
|
repository-code: "https://github.com/sypsyp97/diffct"
|
|
20
20
|
url: "https://sypsyp97.github.io/diffct/"
|
|
@@ -16,3 +16,5 @@ https://creativecommons.org/licenses/by/4.0/). The walnut images in
|
|
|
16
16
|
docs/assets/ (walnut_measured.png, walnut_helical.png, diffct_intro.gif,
|
|
17
17
|
diffct_intro.mp4) are reconstructions from these data. Credit the dataset as
|
|
18
18
|
above when you reuse them. examples/data/NOTICE describes the preprocessing.
|
|
19
|
+
|
|
20
|
+
The documentation figures use third-party CT data; see docs/assets/gallery/NOTICE.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: diffct
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.4
|
|
4
4
|
Summary: Differentiable CT projection and reconstruction: precision modes, streaming, distributed execution and geometry gradients
|
|
5
5
|
Project-URL: Homepage, https://github.com/sypsyp97/diffct
|
|
6
6
|
Project-URL: Documentation, https://sypsyp97.github.io/diffct/
|
|
@@ -90,6 +90,7 @@ Capabilities, limits and isocenter rules: [docs/REFERENCE.md](https://github.com
|
|
|
90
90
|
For the native path, install NVIDIA CUDA PyTorch first, then diffct with the matching CUDA major extra:
|
|
91
91
|
|
|
92
92
|
```bash
|
|
93
|
+
python -c "import torch; print(torch.version.cuda)" # 12.x or 13.x
|
|
93
94
|
pip install "diffct[cu12]" # PyTorch built for CUDA 12
|
|
94
95
|
pip install "diffct[cu13]" # PyTorch built for CUDA 13
|
|
95
96
|
```
|
|
@@ -142,6 +143,8 @@ loss.backward() # x.grad = A^T (A x - y)
|
|
|
142
143
|
|
|
143
144
|
**Geometry gradients.** Reuse one projector with `A.project(volume, trajectory=current)` or `A.backproject(sinogram, trajectory=current)`. Circular helpers accept learnable angle tensors, and `transform_trajectory` applies differentiable rigid transforms. Omitting the override keeps the constructor trajectory. See [REFERENCE](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#geometry-gradients).
|
|
144
145
|
|
|
146
|
+
**Per-axis voxel spacing.** Set `voxel_spacing=(dy, dx)` or `(dz, dy, dx)` in tensor axis order. Both backends support these pitches for projection, projection geometry gradients and FBP/FDK with fixed geometry.
|
|
147
|
+
|
|
145
148
|
**3D parallel beams.** Use `circular_trajectory_3d_parallel(...)` and `beam="parallel3d"`. The PyTorch backend provides matched projection and backprojection on CPU and CUDA.
|
|
146
149
|
|
|
147
150
|
**Native volume layout.** For unbatched, flat-detector cone operations, set `native_volume_layout=True` to avoid volume layout copies. This applies to the native full-CUDA path on one compute device. Shapes stay `(D, H, W)`; the returned strides follow the kernel layout. The default is `False`. See [chunked execution](docs/REFERENCE.md#chunked-execution) for limits.
|
|
@@ -177,8 +180,8 @@ volume slabs use `--partition space` under torchrun. Run
|
|
|
177
180
|
|
|
178
181
|
<p align="center">
|
|
179
182
|
<picture>
|
|
180
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/scaling_dark.png
|
|
181
|
-
<img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/scaling_light.png
|
|
183
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/scaling_dark.png">
|
|
184
|
+
<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, 512^3 volume">
|
|
182
185
|
</picture>
|
|
183
186
|
</p>
|
|
184
187
|
|
|
@@ -188,7 +191,7 @@ volume slabs use `--partition space` under torchrun. Run
|
|
|
188
191
|
| Adjoint (backprojection) | 1060.77 ms | 279.37 ms (3.8×) | 224.04 ms (4.7×) |
|
|
189
192
|
| CGLS iteration | 1753.73 ms | 468.09 ms (3.7×) | 337.39 ms (5.2×) |
|
|
190
193
|
|
|
191
|
-
|
|
194
|
+
FP32, A100 64 GB GPUs; circular cone beam, 360 views, 1024×768 detector, pitch 1.25. Default memory settings, without an imposed allocator cap.
|
|
192
195
|
|
|
193
196
|
Projection times are medians of seven samples after three warmups. CGLS uses three ten-iteration solves after a two-iteration warmup; the median includes initialization and is divided by ten. Timings use synchronized wall time and the slowest rank. Raw samples and configuration: [scaling.json](https://github.com/sypsyp97/diffct/blob/main/docs/assets/scaling.json).
|
|
194
197
|
|
|
@@ -59,6 +59,7 @@ Capabilities, limits and isocenter rules: [docs/REFERENCE.md](https://github.com
|
|
|
59
59
|
For the native path, install NVIDIA CUDA PyTorch first, then diffct with the matching CUDA major extra:
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
|
+
python -c "import torch; print(torch.version.cuda)" # 12.x or 13.x
|
|
62
63
|
pip install "diffct[cu12]" # PyTorch built for CUDA 12
|
|
63
64
|
pip install "diffct[cu13]" # PyTorch built for CUDA 13
|
|
64
65
|
```
|
|
@@ -111,6 +112,8 @@ loss.backward() # x.grad = A^T (A x - y)
|
|
|
111
112
|
|
|
112
113
|
**Geometry gradients.** Reuse one projector with `A.project(volume, trajectory=current)` or `A.backproject(sinogram, trajectory=current)`. Circular helpers accept learnable angle tensors, and `transform_trajectory` applies differentiable rigid transforms. Omitting the override keeps the constructor trajectory. See [REFERENCE](https://github.com/sypsyp97/diffct/blob/main/docs/REFERENCE.md#geometry-gradients).
|
|
113
114
|
|
|
115
|
+
**Per-axis voxel spacing.** Set `voxel_spacing=(dy, dx)` or `(dz, dy, dx)` in tensor axis order. Both backends support these pitches for projection, projection geometry gradients and FBP/FDK with fixed geometry.
|
|
116
|
+
|
|
114
117
|
**3D parallel beams.** Use `circular_trajectory_3d_parallel(...)` and `beam="parallel3d"`. The PyTorch backend provides matched projection and backprojection on CPU and CUDA.
|
|
115
118
|
|
|
116
119
|
**Native volume layout.** For unbatched, flat-detector cone operations, set `native_volume_layout=True` to avoid volume layout copies. This applies to the native full-CUDA path on one compute device. Shapes stay `(D, H, W)`; the returned strides follow the kernel layout. The default is `False`. See [chunked execution](docs/REFERENCE.md#chunked-execution) for limits.
|
|
@@ -146,8 +149,8 @@ volume slabs use `--partition space` under torchrun. Run
|
|
|
146
149
|
|
|
147
150
|
<p align="center">
|
|
148
151
|
<picture>
|
|
149
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/scaling_dark.png
|
|
150
|
-
<img src="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/scaling_light.png
|
|
152
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/sypsyp97/diffct/main/docs/assets/scaling_dark.png">
|
|
153
|
+
<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, 512^3 volume">
|
|
151
154
|
</picture>
|
|
152
155
|
</p>
|
|
153
156
|
|
|
@@ -157,7 +160,7 @@ volume slabs use `--partition space` under torchrun. Run
|
|
|
157
160
|
| Adjoint (backprojection) | 1060.77 ms | 279.37 ms (3.8×) | 224.04 ms (4.7×) |
|
|
158
161
|
| CGLS iteration | 1753.73 ms | 468.09 ms (3.7×) | 337.39 ms (5.2×) |
|
|
159
162
|
|
|
160
|
-
|
|
163
|
+
FP32, A100 64 GB GPUs; circular cone beam, 360 views, 1024×768 detector, pitch 1.25. Default memory settings, without an imposed allocator cap.
|
|
161
164
|
|
|
162
165
|
Projection times are medians of seven samples after three warmups. CGLS uses three ten-iteration solves after a two-iteration warmup; the median includes initialization and is divided by ten. Timings use synchronized wall time and the slowest rank. Raw samples and configuration: [scaling.json](https://github.com/sypsyp97/diffct/blob/main/docs/assets/scaling.json).
|
|
163
166
|
|
|
@@ -27,7 +27,7 @@ import math
|
|
|
27
27
|
|
|
28
28
|
import torch
|
|
29
29
|
|
|
30
|
-
from .constants import _DTYPE
|
|
30
|
+
from .constants import _DTYPE, _native_spacing
|
|
31
31
|
from .utils import (
|
|
32
32
|
DeviceManager,
|
|
33
33
|
TorchCUDABridge,
|
|
@@ -431,7 +431,7 @@ def _parallel_weighted_backproject_raw(sinogram, ray_dir, det_origin, det_u_vec,
|
|
|
431
431
|
_resolve_kernel('_parallel_2d_fbp_backproject_kernel')[grid, tpb, numba_stream](
|
|
432
432
|
d_sino, n_views, n_det, d_reco, Nx, Ny,
|
|
433
433
|
_DTYPE(detector_spacing), d_ray_dir, d_det_origin, d_det_u_vec,
|
|
434
|
-
cx, cy,
|
|
434
|
+
cx, cy, _native_spacing(voxel_spacing),
|
|
435
435
|
)
|
|
436
436
|
|
|
437
437
|
return reco * (1.0 / (2.0 * math.pi))
|
|
@@ -522,8 +522,9 @@ def _fan_weighted_backproject_raw(sinogram, src_pos, det_center, det_u_vec,
|
|
|
522
522
|
_resolve_kernel('_fan_2d_fbp_backproject_kernel')[grid, tpb, numba_stream](
|
|
523
523
|
d_sino, n_views, n_det, d_reco, Nx, Ny,
|
|
524
524
|
_DTYPE(detector_spacing), d_src_pos, d_det_center, d_det_u_vec,
|
|
525
|
-
cx, cy,
|
|
526
|
-
*(_DTYPE(value
|
|
525
|
+
cx, cy, _native_spacing(voxel_spacing),
|
|
526
|
+
*(_DTYPE(value if isinstance(voxel_spacing, (tuple, list)) else value / voxel_spacing)
|
|
527
|
+
for value in isocenter.tolist()),
|
|
527
528
|
)
|
|
528
529
|
|
|
529
530
|
return reco
|
|
@@ -584,8 +585,9 @@ def _cone_weighted_backproject_raw(sinogram, src_pos, det_center, det_u_vec, det
|
|
|
584
585
|
d_sino, n_views, n_u, n_v, d_reco, Nx, Ny, Nz,
|
|
585
586
|
_DTYPE(du), _DTYPE(dv),
|
|
586
587
|
d_src_pos, d_det_center, d_det_u_vec, d_det_v_vec,
|
|
587
|
-
cx, cy, cz,
|
|
588
|
-
*(_DTYPE(value
|
|
588
|
+
cx, cy, cz, _native_spacing(voxel_spacing),
|
|
589
|
+
*(_DTYPE(value if isinstance(voxel_spacing, (tuple, list)) else value / voxel_spacing)
|
|
590
|
+
for value in isocenter.tolist()),
|
|
589
591
|
)
|
|
590
592
|
|
|
591
593
|
# Convert back to (D, H, W) layout for the user.
|
|
@@ -637,7 +639,8 @@ def _analytical_transpose_raw(image, beam, geometry, parameters, isocenter, sino
|
|
|
637
639
|
scale = (sdd_n / (2.0 * math.pi * sid_n)).view(
|
|
638
640
|
(-1,) + (1,) * (len(sinogram_shape) - 1)
|
|
639
641
|
)
|
|
640
|
-
iso = tuple(_DTYPE(value
|
|
642
|
+
iso = tuple(_DTYPE(value if isinstance(parameters[-1], (tuple, list)) else value / parameters[-1])
|
|
643
|
+
for value in isocenter.tolist())
|
|
641
644
|
|
|
642
645
|
if beam == "cone":
|
|
643
646
|
D, H, W, du, dv, spacing = parameters
|
|
@@ -647,7 +650,7 @@ def _analytical_transpose_raw(image, beam, geometry, parameters, isocenter, sino
|
|
|
647
650
|
_resolve_kernel('_cone_3d_fdk_transpose_kernel')[grid, tpb, stream](
|
|
648
651
|
d_sino, *sinogram_shape, d_image, W, H, D, _DTYPE(du), _DTYPE(dv),
|
|
649
652
|
*d_geometry, _DTYPE(W * 0.5), _DTYPE(H * 0.5), _DTYPE(D * 0.5),
|
|
650
|
-
|
|
653
|
+
_native_spacing(spacing), *iso,
|
|
651
654
|
)
|
|
652
655
|
else:
|
|
653
656
|
pitch, H, W, spacing = parameters
|
|
@@ -657,7 +660,7 @@ def _analytical_transpose_raw(image, beam, geometry, parameters, isocenter, sino
|
|
|
657
660
|
else '_fan_2d_fbp_transpose_kernel')
|
|
658
661
|
kernel[grid, tpb, stream](
|
|
659
662
|
d_sino, *sinogram_shape, d_image, W, H, _DTYPE(pitch), *d_geometry,
|
|
660
|
-
_DTYPE(W * 0.5), _DTYPE(H * 0.5),
|
|
663
|
+
_DTYPE(W * 0.5), _DTYPE(H * 0.5), _native_spacing(spacing), *iso,
|
|
661
664
|
)
|
|
662
665
|
return sinogram * scale
|
|
663
666
|
|
|
@@ -16,6 +16,7 @@ from .analytical import (
|
|
|
16
16
|
fan_weighted_backproject, parallel_weighted_backproject,
|
|
17
17
|
)
|
|
18
18
|
from .backends import _zero_dependency, select_backend, voxel_spacing_tuple
|
|
19
|
+
from .constants import _native_spacing
|
|
19
20
|
from .chunking import _memory_budget, _pixel_tiles
|
|
20
21
|
from .distribution import Partition, preflight
|
|
21
22
|
from .operators import _detector_spacing, _normalize_devices, _positive_int, _shape, _validate_trajectory
|
|
@@ -143,6 +144,9 @@ class AnalyticalExecution:
|
|
|
143
144
|
self.rank = 3 if beam == "cone" else 2
|
|
144
145
|
self.global_volume_shape = _shape(volume_shape, self.rank, "volume_shape")
|
|
145
146
|
self.spacing = voxel_spacing_tuple(voxel_spacing, self.rank)
|
|
147
|
+
self.voxel_spacing = self.spacing if isinstance(voxel_spacing, (tuple, list)) else self.spacing[0]
|
|
148
|
+
if self.backend == "numba" and isinstance(self.voxel_spacing, tuple):
|
|
149
|
+
_native_spacing(self.voxel_spacing)
|
|
146
150
|
self.pitch = _detector_spacing(detector_spacing, beam)
|
|
147
151
|
self.devices = _normalize_devices(devices)
|
|
148
152
|
if not isinstance(source, torch.Tensor):
|
|
@@ -336,14 +340,14 @@ class AnalyticalExecution:
|
|
|
336
340
|
device=data.device, dtype=self.dtype)
|
|
337
341
|
if self.beam == "parallel":
|
|
338
342
|
geometry = (geometry[0], geometry[1] - centre, geometry[2])
|
|
339
|
-
return parallel_weighted_backproject(data, *geometry, self.pitch[0], *shape, self.
|
|
343
|
+
return parallel_weighted_backproject(data, *geometry, self.pitch[0], *shape, self.voxel_spacing)
|
|
340
344
|
geometry = (geometry[0] - centre, geometry[1] - centre, *geometry[2:])
|
|
341
345
|
isocenter = self.isocenter.to(data.device) - centre
|
|
342
346
|
if self.beam == "fan":
|
|
343
347
|
return fan_weighted_backproject(data, *geometry, self.pitch[0], *shape,
|
|
344
|
-
self.
|
|
348
|
+
self.voxel_spacing, isocenter=isocenter)
|
|
345
349
|
return cone_weighted_backproject(data, *geometry, *shape, *self.pitch,
|
|
346
|
-
self.
|
|
350
|
+
self.voxel_spacing, isocenter=isocenter)
|
|
347
351
|
|
|
348
352
|
def _tile(self, spatial):
|
|
349
353
|
shape = tuple(part.stop - part.start for part in spatial)
|