Projector#
- class diffct.Projector(trajectory, volume_shape, detector_shape, *, beam='cone', detector_spacing=1.0, voxel_spacing=1.0, devices=None, distributed=False, process_group=None, detector_surface=None, volume_chunk_shape=None, view_chunk_size=None, detector_chunk_shape=None, schedule='auto', partition='views', backend='auto', precision='fast', partition_grid=None, native_volume_layout=False)[source]#
Project floating tensors along parallel, fan, cone, or parallel3d trajectories.
A parallel trajectory is
(ray_dir, det_origin, det_u)and a fan trajectory is(src_pos, det_center, det_u); each component has shape(views, 2). A cone trajectory is(src_pos, det_center, det_u, det_v)with components shaped(views, 3). A parallel3d trajectory is(ray_dir, det_center, det_u, det_v)with the same 3D component shapes; its infinite ray lines pass through detector pixels. Direction axes are unit vectors; parallelray_diranddet_uand 3D detectordet_uanddet_vare orthogonal. Parallel3d rays can be oblique but must not be tangent to the detector plane. If no trajectory tensor requires gradients, the projector clones the geometry. Otherwise it keeps references, reads the current values at every call, and returns gradients for those tensors. Live default frames and overrides are checked on each call.project,backproject,__call__and their*_intovariants accept a keyword-onlytrajectoryreplacing all frames for that call. It must retain the constructor’s beam and view count; it is validated and snapshotted independently without replacing the default frames. Rebuild computed trajectories from trainable leaves on each optimization step. Second derivatives are available for volumes and sinograms, not for the geometry.detector_surface(u, v)optionally supplies local(u, v, n)offsets for every pixel. It receives centred physical float64 grids on CPU and returns a floating-point tensor shaped(*detector_shape, 3)or(views, *detector_shape, 3). 3D normals arecross(det_u, det_v); 2D normals are(det_u_y, -det_u_x)with zero middle offsets. Offsets can be on CPU or CUDA in full-volume and streamed execution. World points expand on each compute device after view sharding. Native execution preserves the composition’s dtype promotion before converting complete coordinates to float32. The PyTorch backend uses the selected precision. The callback is sampled and validated on every call, with first-order gradients for captured PyTorch parameters. Omitting it preserves the flat detector.ParameterizedSurface(sampler, parameters)instead samples only the selected global view IDs and detector rectangle. Its explicit parameters and trajectory values are snapshotted once per call, with batch-local first-order parameter gradients.detector_chunk_shapebounds pixel rectangles; their physical origins remain those of the full detector.Volumes use
(H, W)for parallel and fan beams and(D, H, W)for cone and parallel3d beams. Sinograms use(views, detectors)or(views, U, V). Any leading batch/channel dimensions are preserved.voxel_spacingis a positive scalar or a tuple in volume tensor order:(y, x)or(z, y, x). Physical coordinates retain the centred global volume frame. Results stay on the input device.precision="fast"and"float32"return float32;"float64"computes geometry, ray lengths and accumulations in float64. The selectedbackend,precisionanddtypeare public metadata.backend="auto"selects the native"numba"backend for NVIDIA CUDA and fast precision with scalar or per-axis voxel spacing, except for hybrid partitions. Other configurations use portable"torch"execution on the input’s CPU or supported PyTorch accelerator. Explicit"numba"requires a compatible native configuration. Parallel3d usestorch; explicitly selectingnumbafor it raises an error. Datatorch.functransforms use portable operations while preserving selected metadata.Native CPU inputs stream spatial tiles and view batches through CUDA. Native CUDA inputs retain the full-volume path when its additional working set fits every device; otherwise they stream too.
volume_chunk_shapesets explicit spatial limits in tensor order;view_chunk_sizesets an explicit view limit. Either forces native streaming. Native automatic sizing uses live free memory, reusable allocator cache and the observable per-process fraction ceiling, retaining 25% headroom. Automatic sizing compares fitting first-axis slabs and Cartesian blocks; view batches are at most 32 and shrink if needed. Older PyTorch without a public fraction getter cannot account for that ceiling. Concurrent external allocations can still cause an allocation error.For the native backend,
schedule="auto"selects spatial, view or bounded two-tile window order using estimated full-workload time from short native pilots, with estimated copy bytes breaking ties. The same orders can be requested with"spatial","views"or"window"; capacity may require a serial spatial fallback. Streaming uses distinct upload, compute and download streams and two reusable host slots capped at 8 MiB each. Store reads and completed output drains are fragmented independently of the resident GPU tile size.last_execution_statsreports actual copy payloads, native launches/layouts, planned capacity and pilot metadata; it is None before execution, after errors, and on the uninstrumented native full-CUDA path. Portable execution reports actual ray/tile pairs, store I/O, transfers and collective payloads, with conservative automatic chunks targeting 8 MiB work arrays. Explicit chunk hints take priority. Reports and selection caches retain metadata.native_volume_layout=Truemakes native full-CUDA cone backprojections of unbatched inputs on a flat detector return(D, H, W)tensors with the kernels’(W, H, D)-contiguous strides instead of contiguous copies; native projections accept such tensors without a copy. An iterative solver that keeps its volumes in this layout then avoids two whole-volume copies per normal-operator application on one device. The layout does not change the operator. Results from several local devices, curved detectors, batched inputs, streamed execution and the PyTorch backend still involve copies.Full host volumes/sinograms stay on CPU; streamed native CUDA buffers are bounded by the chosen tile and view batch. Caller-owned CUDA data/results and CUDA work inside a supplied callback are outside this memory bound. Native local devices launch their view batches concurrently, but each device receives every owned volume tile, so host transfers limit the speedup. Local devices split views in list order.
partition="views"returns rank-local projections and replicated backprojections.partition="space"assigns balanced first-axis slabs and returns replicated projections and owned backprojections.partition="hybrid"combines both withpartition_grid=(view_ranks, space_ranks)and parent-group rankview_rank * space_world_size + space_rank.global_volume_shapeis the constructor shape;volume_sliceidentifies the owned global slab, andvolume_shapeandlocal_volume_shapegive its required tensor shape. Empty slabs are valid. Physical coordinates retain the global volume frame. For replicated outputs, count the loss once globally or divide each rank’s projection loss byspace_world_sizeor backprojection loss byview_world_size. Geometry gradients already SUM over all parent ranks; do not apply an additional DDP reduction to them. Space ranks participate in backward even when their local input and geometry require no gradients.- backproject(sinogram, *, trajectory=None)[source]#
Apply the matched adjoint to a tensor ending in
projection_shape.Cone sinograms use
(views, U, V)order after any leading batch/channel dimensions. The result preserves those dimensions, uses selected precision and stays onsinogram.device. View ranks SUM bounded volume contributions; spatial ranks return only the ownedvolume_slice. Divide replicated losses byview_world_size.
- backproject_into(sinogram, output, *, trajectory=None)[source]#
Write the numerical adjoint into block output matching
self.dtype.Accepts tensors or stores with read/write/shape/dtype/flush metadata. Returns
outputafter bounded view/pixel contributions complete.
- property process_group#
Process group of a distributed operator, or None for the default group.
Pass it as
group=to collectives that combine per-rank results of this operator, so they use the same ranks as the operator.
- project(volume, *, trajectory=None)[source]#
Project a floating tensor ending in the owned
volume_shape.Leading batch/channel dimensions are preserved ahead of
projection_shape. The result has the selected precision and stays onvolume.device. View partitions own contiguous view slices; spatial ranks SUM their ray contributions and replicate the result. Portable execution supports CPU and PyTorch accelerator tensors; native execution uses NVIDIA CUDA and streams CPU data when needed.
- project_into(volume, output, *, trajectory=None)[source]#
Numerically project into supplied output matching
self.dtype.Returns
output; no complete result is allocated. Direct tensors requiring autograd must be detached or used undertorch.no_grad(). Known overlapping/read-only outputs are rejected before execution.