PyTorch-style high-level API: Tensor / Image / ImageBatch + fluent fused pipelines - #9
Open
johnnynunez wants to merge 1 commit into
Conversation
…pelines
A PyTorch-flavoured layer over the existing machinery — an API layer, not a
framework. Everything zero-copy; every pipeline lowers to ONE fused kernel
through compose()'s JIT cache (types compile once, values ride in params[]).
- fkl.Tensor: thin zero-copy wrapper over torch CUDA tensors, CAI objects
(cupy/numba) and DeviceBuffer. .shape/.dtype/.device (scalar dtype,
channels live in shape), __cuda_array_interface__, __dlpack__/
__dlpack_device__, .torch() (zero-copy view, cached), .reshape()
(metadata-only over the C-contiguous memory), Tensor.empty(shape, dtype).
A vector dtype spec appends the channel dim (empty((4, 6), 'uint8x3') ==
empty((4, 6, 3), 'uint8')); bases without an FKL surface (float16/
bfloat16/int64 torch tensors) raise a clear TypeError at construction.
- fkl.Image: HWC semantics (width/height/channels, C 1..4, eager shape
validation with pointers to ImageBatch / to_planar). Input layout is
'HWC'; planar 'CHW' is an OUTPUT layout via the existing TensorSplit.
- fkl.ImageBatch: list of same-shape images (validated for same
W/H/C/dtype/device) OR one batched (N, H, W[, C]) tensor split into
zero-copy per-plane views — either way pipelines run as horizontal
fusion (BatchRead, N part of the kernel type).
- fkl.pipe(x).crop/resize/border/cvt_color/normalize/mul/add/sub/div/cast/
saturate_cast/apply(op)/to_planar('CHW').run(out=, stream=) -> fkl.Tensor.
Each method appends an existing symbolic op; normalize composes
Cast('float32') (only when needed) + Sub + Div; run() returns the
SEMANTIC shape ((C,H,W) / (N,C,H,W) after to_planar; 2-D sources keep a
height-1 dim so (1, W) round-trips). Geometry-after-compute, batch
multi-ROI, channel mismatches, post-to_planar appends AND out= size/
dtype/device mismatches (validated against the planned output BEFORE the
launch — a too-small out would otherwise be an out-of-bounds device
write) raise eagerly instead of surfacing as nvcc errors or memory
corruption. Auto-allocated outputs follow the SOURCE's framework (torch
in -> torch out), including batched-tensor plane views and reshaped
Tensors. .source() shows the single generated TU for the exact variant
.run() launches, including the effective ThreadFusion choice.
- fkl.F: eager one-op fused pipelines (resize/crop/cvt_color/normalize/
cast/saturate_cast/to_planar/mul/add/sub/div).
- _make_dlpack_capsule grows an optional dims override so reshaped /
planar-split DeviceBuffer exports carry the semantic shape (validated
against the buffer's element count), and refcounts multiple exports of
one buffer: the device memory is freed when the LAST consumer releases
its capsule (two Tensor views handed to torch no longer race).
- OUT of scope, documented: autograd, broadcasting, >4-channel images,
multi-input graphs.
tests/test_highlevel.py: 54 checks, dependency-free (wrapper semantics,
vector-spec empty, plane-view zero-copy, shared-buffer DLPack ownership,
pipelines vs pure-Python / compose() references, batch-tensor ==
batch-list, single-fused-kernel source check, TF source/run, (1, W)
round-trip, eager errors incl. out= validation and mixed-device batches).
tests/test_highlevel_torch.py: torch interop, skips cleanly without torch.
Example 12 + README/skill docs.
Verified on GB10 (sm_121), CUDA 13.0, nvcc backend: test_highlevel 54/54;
full pre-existing matrix re-run green except the pre-existing
Saturate failure (reproduces with the pristine package on this toolkit).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
compose()is op-chain-first: using it well means knowing the FKL op catalog (TensorRead,SaturateCast,TensorSplit, ...) and the read/write chain rules. DNN-preprocessing users coming from torch/torchvision think in tensors, images and batches. This adds that spelling as a thin layer over the existing machinery — every pipeline still lowers to ONE fused kernel throughcompose()'s JIT cache, and nothing about codegen, fusion or the C ABI changes.What's included
fkl.Tensor— zero-copy wrapper over torch CUDA tensors,__cuda_array_interface__objects (cupy/numba) andDeviceBuffer:.shape/.dtype/.device(scalar dtype; channels live in the shape),__cuda_array_interface__,__dlpack__/__dlpack_device__,.torch()(cached zero-copy view), metadata-only.reshape(),Tensor.empty(shape, dtype). A vector dtype spec appends the channel dim (empty((4, 6), 'uint8x3')≡empty((4, 6, 3), 'uint8')). Dtypes without an FKL kernel surface (float16/bfloat16/int64 torch tensors) raise a clearTypeErrorat construction instead of failing later.fkl.Image— HWC image semantics (width/height/channels, C 1..4) with eager shape validation;fkl.ImageBatch— list of same-shape images (validated for matching W/H/C/dtype/device) OR one batched(N, H, W[, C])tensor split into zero-copy per-plane views; both run as horizontal fusion through the existing list-input path (BatchRead, N part of the kernel type).fkl.pipe(x)— fluent builder:crop,resize,border,cvt_color,normalize,mul/add/sub/div,cast,saturate_cast,apply(op)(escape hatch for any symbolic op),to_planar('CHW'), then.run(out=None, stream=None) -> fkl.Tensor. Each method appends an existing symbolic op; the same rules ascompose()apply (types compile once, values ride inparams[]; the kernel handle is cached on the pipeline).run()returns semantic shapes —(C,H,W)/(N,C,H,W)afterto_planar, and 2-D sources keep a height-1 dim so(1, W)round-trips — and auto-allocated outputs follow the SOURCE's framework (torch in -> torch out, including batched-tensor plane views and reshaped Tensors)..source()shows the single generated TU for the exact variant.run()launches, including the effective ThreadFusion choice.to_planaron single-channel chains, ops afterto_planar, mixed-device batches — andout=is validated against the planned output (element count, dtype, device) BEFORE the launch, since a too-small or wrongly-typedoutwould otherwise mean an out-of-bounds device write or a silent reinterpretation.fkl.F— eager one-op fused pipelines (fkl.F.resize(img, (64, 64)),cvt_color,normalize,to_planar, ...)._make_dlpack_capsulegains an optionaldimsoverride (validated against the buffer's element count; default behaviour unchanged) so reshaped / planar-split outputs export the semantic shape over the same memory, and exports of one buffer are refcounted: the device memory is freed when the LAST consumer releases its capsule, so several Tensor views handed to torch cannot free memory out from under each other.Out of scope, documented (module docstring, README, skill): autograd, broadcasting, >4-channel images (FKL vector pixels are 1..4 channels), multi-input graphs (single-input/single-output ABI; batches are horizontal fusion).
Tests / docs
tests/test_highlevel.py: 54 dependency-free checks (DeviceBuffer-backed) — wrapper semantics (incl. vector-specemptyand shared-buffer DLPack ownership through the real capsule deleters), zero-copy plane views, pipelines vs pure-Python per-op references and vs equivalentcompose()chains, batched-tensor == list-of-images equality, single-fused-kernel source check, ThreadFusion source/run,(1, W)round-trip, out=/stream/kernel-reuse, and the eager error paths (ordering, batch/channel mismatches, mixed devices, out= size/dtype validated before launch with the rejected buffer verified untouched). All pass on sm_121 / CUDA 13.0 (nvcc backend).tests/test_highlevel_torch.py: torch interop (Tensor over torch tensors,.torch()both ways, torch-backed Image/ImageBatch pipelines, out= into torch tensors incl. rejection cases, fp16/bf16 rejection, torch-allocated outputs for plane-view/reshaped sources); skips cleanly when torch is absent.Saturatecase fails identically on unmodified main with this toolkit — pre-existing, unrelated).examples/12_torch_style_api.py(batch of HWC uint8 frames -> resized, normalized, NCHW float in one fused kernel), README section, examples index row, usage-skill section.🤖 Generated with Claude Code