Skip to main content

arraybridge

ArrayBridge converts arrays between NumPy, CuPy, PyTorch, TensorFlow, JAX and pyclesperanto, and gives code one way to ask each library about devices, memory and array operations.

Core dependencies are NumPy and metaclass-registry. The other libraries are optional and are imported only when used.

Version 0.5.0 removed names without aliases. CHANGELOG.md lists each removed name and what to call instead.

Quick start

import numpy as np

from arraybridge import HOST, convert_memory, detect_memory_type

value = np.arange(6).reshape(2, 3)
assert detect_memory_type(value) == "numpy"

result = convert_memory(value, source_type="numpy", target_type="numpy", device=HOST)
assert result is value

convert_memory(data, source_type, target_type, device) takes all four arguments. device is a device of the target library: HOST for main memory, AcceleratorDevice(0) for the library's first accelerator. A conversion to a device the target library does not have fails; nothing is placed somewhere else instead.

Devices

A device is a value: HOST (main memory), AcceleratorDevice(index) (an accelerator as its library numbers it) or NamedDevice(name) (a device a library reports that is not one of its numbered accelerators, such as pyclesperanto on a CPU OpenCL device). Indices belong to one library; the same index in two libraries need not be the same hardware.

Target library Devices it accepts
NumPy HOST
CuPy, pyclesperanto AcceleratorDevice(i)
PyTorch, JAX, TensorFlow HOST or AcceleratorDevice(i)

Each library answers what it has on this machine:

import numpy as np

from arraybridge import HOST, AcceleratorDevice, ArrayLibrary

numpy_library = ArrayLibrary.named("numpy")
assert numpy_library.host_devices() == (HOST,)
assert numpy_library.accelerator_devices() == ()
assert numpy_library.available_devices() == (HOST,)
assert numpy_library.device_of(np.zeros(3)) == HOST

assert AcceleratorDevice(0).label == "accelerator:0"

Decorators

from arraybridge import numpy

@numpy
def normalize(image):
    return image / max(float(image.max()), 1.0)

assert normalize.input_memory_type == "numpy"

The decorator named after a library sets input_memory_type, output_memory_type and execution_memory_type on the function, adds the slice_by_slice and dtype_config keyword arguments, and wraps the call with what that library's devices need (per-thread CUDA streams for CuPy and PyTorch, out-of-memory retries for libraries with accelerators). It does not convert inputs or outputs between libraries and does not choose a device. The caller converts with convert_memory.

Stack utilities

import numpy as np

from arraybridge import HOST, stack_slices, unstack_slices

slices = [np.zeros((8, 8)), np.ones((8, 8))]
stack = stack_slices(slices, memory_type="numpy", device=HOST)
restored = unstack_slices(stack, memory_type="numpy", device=HOST)
assert stack.shape == (2, 8, 8) and len(restored) == 2

stack_slices takes a non-empty list of 2D arrays. unstack_slices takes a 3D array. Both raise ValueError for a wrong shape or a device the target library does not have.

Which route a conversion takes

A conversion tries five mechanisms in order: "stays in place" (same library and device: nothing is converted), "device move" (same library, other device), "dlpack" (the target wraps the source's buffer), "shared host buffer" (both arrays are in main memory and the result is the source's own buffer) and "host copy" (the values are copied through NumPy). ConversionRoute.planned tells which one will be tried first; the record of a conversion names the one that was used. It reads only the library classes and the device values, so it works without the libraries or the hardware:

from arraybridge import HOST, AcceleratorDevice, ArrayLibrary, ConversionRoute

torch = ArrayLibrary.named("torch")
cupy = ArrayLibrary.named("cupy")
gpu = AcceleratorDevice(0)

assert ConversionRoute.planned(torch, gpu, cupy, gpu).mechanism == "dlpack"
assert ConversionRoute.planned(torch, HOST, cupy, gpu).mechanism == "host copy"
assert ConversionRoute.planned(torch, HOST, torch, gpu).mechanism == "device move"
assert ConversionRoute.planned(torch, gpu, torch, gpu).mechanism == "stays in place"

To see what conversions actually ran, install a ConversionCounters observer. It keeps a count, bytes and seconds per source, target and mechanism. This example needs TensorFlow:

import numpy as np

from arraybridge import HOST, ConversionCounters, convert_memory

counters = ConversionCounters()
with counters.installed():
    convert_memory(np.zeros((4, 4), dtype=np.float32), "numpy", "tensorflow", HOST)

row = counters.rows()[0]
assert (row["source_library"], row["target_library"]) == ("numpy", "tensorflow")
assert (row["mechanism"], row["count"], row["nbytes"]) == ("host copy", 1, 64)
print(counters.format_table())

A conversion that stays in place is not counted. With no observer installed nothing is timed. An observer belongs to the thread or asyncio task that installed it; a thread you start is observed when you run it in a copy of your context (contextvars.copy_context().run).

Array operations in the array's own library

ArrayLibrary.named("torch"), ArrayLibrary.of(array) and MemoryType.TORCH.library all return the library object. Its methods run in that library, on the device of their input: max, min, mean, sum, astype, reshape, broadcast_to, ones_like, ones, prepend_axis, linspace, assign, outer, floor, logical_and, stack_arrays, normalize_planes, scale_dtype, to_numpy, from_numpy. linspace, ones, outer and floor exist only on libraries that can make an array without a reference array (ArrayMath: every built-in library but pyclesperanto).

import numpy as np

from arraybridge import ArrayLibrary


def max_projection(stack):
    library = ArrayLibrary.of(stack)
    return library.prepend_axis(library.max(stack, 0))


assert max_projection(np.ones((4, 8, 8))).shape == (1, 8, 8)

library.module is the library's module, imported on first use. Each library keeps its own numeric rules: PyTorch, JAX and TensorFlow take means in float32, and pyclesperanto reduces only along the leading axis.

reshape, broadcast_to and ones_like do not download pixels to NumPy. pyclesperanto cannot change shape or broadcast natively, so those calls raise NotImplementedError unless the shape is unchanged; its ones_like supports one to three dimensions and its float32 and integer dtypes, not bool.

Adding a library

A library is one class. See ADDING_NEW_FRAMEWORKS.md.

Installation

pip install arraybridge
pip install "arraybridge[torch]"
pip install "arraybridge[cupy]"

Documentation: https://arraybridge.readthedocs.io

Metadata

Release files for arraybridge 0.5.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for arraybridge 0.5.2
File Size Uploaded
arraybridge-0.5.2.tar.gz 136.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for arraybridge 0.5.2
File Interpreter ABI Platform
arraybridge-0.5.2-py3-none-any.whl Python 3 none any Details

Total release size: 179.8 kB

Release files / arraybridge-0.5.2.tar.gz

Download URL arraybridge-0.5.2.tar.gz
Size 136.3 kB
Tags Source
SHA-256 checksum
How to use checksums
7e2bcbab06c92f6fd0e1b9a50eeb91f8473533439e2b2c277122174ea285b6e7
BLAKE2b-256 checksum
How to use checksums
d35a92fdff49c158aa2c6a10f1f3602055b344be148f5ba88716d81927f02f29
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release files / arraybridge-0.5.2-py3-none-any.whl

Download URL arraybridge-0.5.2-py3-none-any.whl
Size 43.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d20af040a4d31a1350cddafb339f1743932c30d65f8659b12200250c1b4d2da3
BLAKE2b-256 checksum
How to use checksums
bef9b0a46ed80fba4f3f90ab669a3bf1530c8ac00f62e3d84e74eae9420c12ed
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.5.2 This release

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.3.8

2 release files

0.3.7

2 release files

0.3.6

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.11

2 release files

0.2.10

2 release files

0.2.9

2 release files

0.2.8

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page