Utilities¶
Spatial helper functions and GPU timing. The spatial functions use array_api_compat in Python and native MATLAB operations, so they work with any supported array backend (CuPy, NumPy, PyTorch).
cdist¶
Pairwise Euclidean distance between two sets of points.
ffdas.cdist(a, b)
# a: (...A, D), b: (...B, D) -> (*a.shape[:-1], *b.shape[:-1])
d = ffdas.utils.cdist(a, b)
% a: (D, ...A), b: (D, ...B) -> (...A, ...B)
The coordinate dimension is last in Python and first in MATLAB, matching the library-wide convention. Broadcasting applies to all other dimensions.
Example¶
sources = cp.array([[0.0, 0.0, -0.01]], dtype=cp.float32) # (1, 3)
targets = cp.stack([xx, yy, zz], axis=-1) # (nz, ny, nx, 3)
dist = ffdas.cdist(sources, targets) # (1, nz, ny, nx)
sources = gpuArray(single([0; 0; -0.01])); % (3, 1)
targets = permute(cat(4, xx, yy, zz), [4 3 1 2]); % (3, nz, ny, nx)
dist = ffdas.utils.cdist(sources, targets); % (1, nz, ny, nx)
rect_dist¶
Minimum Euclidean distance from 3D points to an axis-aligned rectangle centered at the origin in the \(z = 0\) plane. Typically used to compute the reference distance for diverging-wave transmit delay calculations (the point on the aperture closest to the virtual source).
ffdas.rect_dist(points, size)
# points: (..., 3), size: (2,) -> (...)
d = ffdas.utils.rect_dist(points, size)
% points: (3, ...), size: [width; height] -> (...)
size is the full width and height of the rectangle.
angle¶
Angle in radians between vectors a and b.
ffdas.angle(a, b, eps=1e-7)
# a: (..., D), b: (..., D) -> (...)
theta = ffdas.utils.angle(a, b)
theta = ffdas.utils.angle(a, b, eps)
% a: (D, ...), b: (D, ...) -> (...)
Broadcasting applies to all dimensions except the coordinate dimension. The eps parameter prevents division by zero for near-zero-length vectors.
Example¶
Computing the off-axis angle for apodization:
direction = cp.array([0.0, 0.0, 1.0], dtype=cp.float32)
theta = ffdas.angle(targets - source, direction) # angle from +z
direction = gpuArray(single([0; 0; 1]));
theta = ffdas.utils.angle(targets - source, direction); % angle from +z
Timer¶
Class for timing GPU operations using CUDA events. Records events on the library's internal CUDA stream before and after the wrapped block, then synchronizes and reports elapsed GPU time.
t = ffdas.utils.Timer()
t.start()
output = ffdas.das(rf, channel_pos, voxel_pos, offsets, weights)
t.stop()
print(f"{t.elapsed_ms():.1f} ms")
t = ffdas.utils.Timer();
t.start();
output = ffdas.das(rf, channel_pos, voxel_pos, offsets, weights);
t.stop();
fprintf('%.1f ms\n', t.elapsed_ms());
In Python, the timer can also be used as a context manager:
with ffdas.utils.Timer() as t:
output = ffdas.das(rf, channel_pos, voxel_pos, offsets, weights)
print(f"{t.elapsed_ms():.1f} ms")
MATLAB-only Utilities¶
take¶
Take the first \(k\) entries along a dimension.
y = ffdas.utils.take(x, k, axis)
take_along_axis¶
Gather elements from x using per-element indices along a dimension, equivalent to NumPy's take_along_axis.
y = ffdas.utils.take_along_axis(x, indices, axis)
indices are 1-based and must have the same shape as x except along the gather axis.