Array
The Array classes provide multidimensional arrays for storing data with support for numpy memory aliasing.
Overview
Arrays are available from 1D to 5D with two memory management variants:
Owned: Allocates and manages its own memory
Alias: References external memory (e.g., memory owned by numpy arrays)
Supported Types
Array{ND}Float{Owned/Alias}- Single precision floatArray{ND}Double{Owned/Alias}- Double precision floatArray{ND}Int{Owned/Alias}- Signed integerArray{ND}Bool{Owned/Alias}- Boolean
Replace {ND} with 1, 2, 3, 4, or 5 for the dimension.
Replace {Owned/Alias} by either Owned for memory managed by YRT-PET or
Alias for memory managed by NumPy.
Memory management
import pyyrtpet as yrt
import numpy as np
# ============================================================================
# Owned array - allocates its own memory
# ============================================================================
# Create a 3D owned array (single precision float)
arr_3d = yrt.Array3DFloatOwned()
arr_3d.allocate([10, 20, 30])
# Verify memory is allocated
assert arr_3d.isMemoryValid(), "Memory should be allocated"
# Get dimensions
dims = arr_3d.getDims()
assert dims[0] == 10 and dims[1] == 20 and dims[2] == 30
# Get total size
total_size = arr_3d.getSizeTotal()
assert total_size == 10*20*30, f"Expected 6000, got {total_size}"
# ============================================================================
# Alias array - binds to numpy array
# ============================================================================
# Create a numpy array
arr_np = np.random.random((5, 10, 20, 30)).astype(np.float32)
# Bind to a NumPy array
arr_alias = yrt.Array4DFloatAlias()
arr_alias.bind(arr_np)
# Change value in numpy array
arr_np[1, 2, 3, 4] = np.float32(-1.2)
# Or Bind from another array
arr_alias2 = yrt.Array4DFloatAlias()
arr_alias2.bind(arr_alias)
# Verify memory is valid
assert arr_alias.isMemoryValid(), "Memory should be valid after binding"
assert arr_alias2.isMemoryValid()
assert arr_alias[1,2,3,4] == arr_np[1,2,3,4], "Memory aliasing failed"
assert arr_alias2[0,2,15,26] == arr_alias[0,2,15,26], "Memory aliasing failed"
Data Access
# Create a 3D array for demonstration
arr = yrt.Array3DFloatOwned()
arr.allocate([3, 4, 5])
# Multi-dimensional access (3D)
arr[0, 0, 0] = 1.0
arr[1, 2, 3] = 2.5
value = arr[1, 2, 3]
assert value == 2.5
# Flat access (linear index from 0 to total_size-1)
arr.setFlat(10, 10.0)
value = arr.getFlat(10)
assert value == 10.0
# Increment flat index (useful for parallel operations)
arr.incrementFlat(5, 1.0) # Adds 1.0 to position 5
# Get dimensions
dims = arr.getDims()
assert len(dims) == 3
assert dims[0] == 3 and dims[1] == 4 and dims[2] == 5
# Get size of a specific dimension
assert arr.getSize(2) == 5 # Size of dim 2, should be 5
# Get strides (step size for each dimension)
strides = arr.getStrides()
# Strides represent how many elements to skip to move to next index in each dim
# Convert between flat and multi-dimensional indices
flat_idx = arr.getFlatIdx([1, 2, 3])
multi_idx = arr.unravelIdx(flat_idx)
assert multi_idx[0] == 1 and multi_idx[1] == 2 and multi_idx[2] == 3
Operations
# Create and fill array
arr = yrt.Array2DIntOwned()
arr.allocate([10, 10])
arr.fill(0)
# Set some values
arr[5, 5] = 100
# Sum all elements
total_sum = arr.sum()
assert total_sum == 100.0
# Get maximum value
max_val = arr.getMaxValue()
assert max_val == 100.0
# Fill with a new value
arr.fill(5)
assert arr.sum() == 500 # 10*10*5
# Arithmetic operations (in-place)
arr2 = yrt.Array2DIntOwned()
arr2.allocate([10, 10])
arr2.fill(2)
# arr += arr2
arr += arr2
assert arr.getMaxValue() == 7 # 5 + 2
# arr -= scalar
arr -= 1
assert arr.getMaxValue() == 6 # 5 + 2 - 1
# arr *= scalar
arr *= 2
assert arr.getMaxValue() == 12 # (5 + 2 - 1) * 2
# arr /= scalar
arr /= 3
assert arr.getMaxValue() == 4 # ((5 + 2 - 1) * 2) / 3
# Invert array (1/x for each element)
arr = yrt.Array2DDoubleOwned()
arr.allocate([10, 10])
arr.fill(2.0)
arr.invert()
assert abs(arr.getMaxValue() - 0.5) < 0.001
# Copy data from one array to another
arr_src = yrt.Array3DFloatOwned()
arr_src.allocate([5, 5, 5])
arr_src.fill(10.0)
arr_dst = yrt.Array3DFloatOwned()
arr_dst.allocate([5, 5, 5])
arr_dst.copy(arr_src)
assert arr_dst.sum() == 1250.0 # 5*5*5*10
File I/O
import os
# For this documentation's purposes only, we use a tempfile
import tempfile
with tempfile.NamedTemporaryFile(suffix='.rawd', delete=False) as f:
my_file = f.name
# Write array to file
arr = yrt.Array3DFloatOwned()
arr.allocate([10, 20, 30])
arr.fill(42.0)
arr.writeToFile(my_file)
# Read array from file
arr2 = yrt.Array3DFloatOwned()
arr2.readFromFile(my_file)
# Verify data
assert arr2.getMaxValue() == 42.0
dims = arr2.getDims()
assert dims[0] == 10 and dims[1] == 20 and dims[2] == 30
# Clean up
os.remove(my_file)
NumPy Interoperability
The Array classes implement the Python buffer protocol, allowing direct numpy conversion:
# Create owned array
arr_owned = yrt.Array3DFloatOwned()
arr_owned.allocate([10, 20, 30])
# Create a numpy array with zero-copy
arr_np = np.array(arr_owned, copy=False)
# Modify through numpy
arr_np[:] = 5.0
# arr_owned now contains 5.0
assert arr_owned.getMaxValue() == 5.0
# Alias array works similarly
arr_np_external = np.ones((5, 10, 20), dtype=np.float32)
arr_alias = yrt.Array3DFloatAlias()
arr_alias.bind(arr_np_external)
# You can even bind a numpy array to a YRT-PET array, which is itself bound to
# a NumPy array.
arr_from_alias = np.array(arr_alias, copy=False)
arr_from_alias[0, 0, 0] = 99.0
assert arr_np_external[0, 0, 0] == 99.0
Boolean Array example
# Boolean array
arr_bool = yrt.Array3DBoolOwned()
arr_bool.allocate([5, 5, 5])
arr_bool.fill(False)
arr_bool[0, 0, 0] = True
assert arr_bool[0, 0, 0] == True