API reference
Every function in fastfields.dlpack, with its arguments and options.
fastfields.dlpack
fastfields.dlpack: nanobind DLPack bindings to the fastfields-lib C++ lib.
All functions accept any array object exposing __dlpack__ (numpy, torch,
cupy, ...). Tensors marked in-place / as outputs are written through their
DLPack data pointers.
Argument checking
This is the raw layer and it stays sharp: it does not reshape, cast, allocate
or copy anything for you. It does reject structurally malformed arrays --
a null data pointer, a 0-d array, a vector (multi-lane) dtype, arrays of a
single call spread across different devices -- with a ValueError naming the
function and the argument, instead of passing them on to the kernels. Shape and
dtype contracts (matching ranks, matching batch shapes, ndim vs. rank)
are enforced one layer down, by fastfields-lib, and also surface as
ValueError.
CUDA lifetime invariant
Every array passed to a CUDA op must be kept alive by the caller until the stream it was submitted on has been synchronized.
These bindings are asynchronous on CUDA: a call enqueues work on stream and
returns immediately, and nothing in this package holds a reference to your
arrays -- the DLPack tensors handed to C++ are plain views over memory the
Python objects own.
If an input, output or temporary is dropped before the stream is synchronized, its memory goes back to the framework's allocator (CuPy's memory pool, PyTorch's caching allocator) while a kernel may still be reading or writing it. The allocator can then hand that same block to an unrelated allocation: silent corruption, not a crash. The frameworks differ in when such reuse is possible -- PyTorch's caching allocator records the stream a block was used on, CuPy's pool does not -- so code that happens to work under one is not portable to the other.
In practice:
.. code-block:: python
out = cupy.empty_like(inp)
ff.pull(out, inp, grid, stream=stream.ptr)
stream.synchronize() # only now may inp / grid / out be dropped
del inp, grid # (or let them go out of scope)
The friendly wrappers satisfy this for you, which is why it is easy to forget:
they submit on the framework's own current stream (fastfields.torch via
_util.stream_ptr, fastfields.cupy via _util.current_stream_ptr), so
the framework's ordering rules cover the memory they allocated, and they keep
the operands referenced for the whole call -- fastfields.torch beyond it,
through autograd's save_for_backward. Calling fastfields.dlpack
directly, or passing a stream of your own, means taking the invariant on
yourself. On CPU it holds trivially -- the calls are synchronous.
Bound
Spline
anchor_scale_shift
anchor_scale_shift(anchor: str, inshape: Sequence[int], outshape: Sequence[int], ndim: int) -> tuple[list[float], float]
Map a torch-interpol anchor to a per-dim scale and scalar shift.
The fastfields resize kernel samples input coordinate
scale[d] * loc + shift * (scale[d] - 1) for output index loc. The
four anchors of interpol.resize map onto (scale, shift) as:
========== ================= =======
anchor scale[d] shift
========== ================= =======
centers (in-1)/(out-1) 0.0
edges in/out 0.5
first in/out 0.0
last in/out 1.0
========== ================= =======
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
anchor
|
str
|
Anchor name or abbreviation ( |
required |
inshape
|
sequence of int
|
Input and output spatial sizes (length |
required |
outshape
|
sequence of int
|
Input and output spatial sizes (length |
required |
ndim
|
int
|
Number of spatial dimensions. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
scale |
list of float
|
Per-dim input-index step per output-index step. |
shift |
float
|
Scalar sampling shift shared across dimensions. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
as_bound
Normalise a boundary-condition argument to an int in 0..7.
Accepts an integer, a :class:Bound enum, or a friendly string alias
(e.g. "dct2", "wrap"). Raises ValueError for an unknown alias
or an out-of-range integer.
as_spline
Normalise a spline-order argument to an int in 0..7.
Accepts an integer, a :class:Spline enum, or a friendly string alias
(e.g. "cubic"). Raises ValueError for an unknown alias or an
out-of-range integer.
check_ndim
Validate that ndim is in 1..arr_ndim (else ValueError).
infer_ndim
infer_ndim(ndim: Optional[int], factor: float | Sequence[float] | None, shape: int | Sequence[int] | None) -> int
Infer the number of trailing spatial dimensions to resize.
An explicit ndim wins; otherwise a sequence shape or factor
implies its length; failing that, 1.
normalize_shape
Normalise a shape argument to a list of int of length ndim.
A scalar int is broadcast to ndim entries. Raises ValueError if
an explicit sequence does not have length ndim.
resolve_out_spatial
resolve_out_spatial(spatial_in: Sequence[int], ndim: int, factor: float | Sequence[float] | None, shape: int | Sequence[int] | None) -> tuple[int, ...]
Resolve the output spatial shape from factor or shape.
factor and shape are mutually exclusive; with neither, the output
keeps the input spatial shape (identity). A scalar factor is broadcast
to ndim entries; each output size is max(1, round(in * factor)).