Vectors4D
Represents a batch of 4D vectors.
Parameters
| Field | Type | Description |
|---|---|---|
data | np.ndarray | list | tuple | Array-like data of shape (N, 4), converted to a contiguous float32 array. |
Raises
| Exception | Condition |
|---|---|
TypeError | data can't be converted to a float32 array (e.g. non-numeric elements) |
ValueError | data's rank isn't 2, its last axis isn't length 4, or it contains a non-finite value (NaN/Inf) |
Attributes
| Attribute | Type | Description |
|---|---|---|
shape_spec | ClassVar[tuple[int | None, ...]] | Class-level shape spec, (None, 4) — None means the batch size is variable. |
data | np.ndarray | The wrapped batch, shape (N, 4). Reading it returns a copy, so mutating the result doesn't affect the Vectors4D; assigning a new value re-validates it the same way as construction. |
shape | tuple[int, ...] | (N, 4). |
ndim | int | Always 2. |
dtype | np.dtype | Always float32. |
size | int | 4 * N. |
Methods
| Method | Description |
|---|---|
to_numpy(copy=True) | Returns the batch as np.ndarray. Pass copy=False to get a reference to the internal array instead — faster for large data, but mutating it mutates the Vectors4D too. |
copy() | Returns a new Vectors4D with an independent copy of the data. |
Vectors4D.coerce(value) | Returns value unchanged if it's already a Vectors4D; otherwise wraps a shape-(N, 4) np.ndarray/list/tuple into one. Raises TypeError for any other input. |
Operators
| Operation | Behavior |
|---|---|
v1 == v2 | True only if other is exactly a Vectors4D (not a subclass, not another datatype) with element-equal data. False for anything else. |
len(v) | Batch size N. 0 for an empty batch (shape (0, 4)) — that's a valid, constructible batch. |
np.asarray(v) | Works directly — NumPy functions (e.g. np.reshape, np.sum) accept a Vectors4D in place of an np.ndarray. Always returns a copy; use to_numpy(copy=False) for a zero-copy view. |
hash(v) | Not supported — a Vectors4D can't be used as a dict key or set member. |
Visualization
datatypes.visualize(vectors, entity_path=...) logs the batch as text (rr.TextLog) — it shares its handler with Vector4D, since there's no native 4D arrow/spatial primitive in the renderer. No label handler is registered for Vectors4D, so passing label to visualize() has no effect for it.
Example
python
"""Demonstrates the Telekinesis Vectors4D datatype."""
import time
import numpy as np
from loguru import logger
import rerun as rr
from telekinesis import datatypes
def vectors4d_example():
"""Demonstrate creation, access, update, NumPy interop, serialization, and empty batches."""
# ======================= Create ============================================
vectors = [[1.0, 2.0, 3.0, 4.0], [5.0, 6.0, 7.0, 8.0]]
vectors4d = datatypes.Vectors4D(vectors)
logger.info(f"Created Vectors4D: {vectors4d}")
# ======================= Inspect ===========================================
data = vectors4d.data
shape = vectors4d.shape
size = vectors4d.size
dtype = vectors4d.dtype
ndim = vectors4d.ndim
numpy_array = vectors4d.to_numpy()
vectors4d_copy = vectors4d.copy()
logger.info(f"shape={shape}, size={size}, ndim={ndim}, dtype={dtype}")
logger.info(f"Vectors4D data: {data}")
logger.info(f"NumPy array: {numpy_array}")
logger.info(f"Copied Vectors4D: {vectors4d_copy}")
# ======================= Visualize =========================================
rr.init("vectors4d_example", spawn=True)
datatypes.visualize(vectors4d, entity_path="/Vectors4D")
# ======================= Update ============================================
new_data = [[9.0, 10.0, 11.0, 12.0], [13.0, 14.0, 15.0, 16.0]]
vectors4d.data = new_data
logger.info(f"Updated Vectors4D: {vectors4d}")
datatypes.visualize(vectors4d, entity_path="/Vectors4D/updated")
# ======================= NumPy Interop =====================================
sum_result = vectors4d + np.array([1.0, 1.0, 1.0, 1.0])
logger.info(f"Sum of Vectors4D with numpy array: {sum_result}")
# ======================= Serialize / Deserialize ===========================
start = time.perf_counter()
serialized = datatypes.serialize(vectors4d)
serialization_ms = (time.perf_counter() - start) * 1000
start = time.perf_counter()
deserialized = datatypes.deserialize(serialized)["param_0"]
deserialization_ms = (time.perf_counter() - start) * 1000
logger.info(f"Deserialized Vectors4D: {deserialized}")
logger.info(f"Round-trip successful: {deserialized.data == new_data}")
logger.info(f"Serialization time: {serialization_ms:.3f} ms")
logger.info(f"Deserialization time: {deserialization_ms:.3f} ms")
# ======================= Empty Batch =======================================
empty = datatypes.Vectors4D(np.empty((0, 4), dtype=np.float32))
logger.info(f"Empty Vectors4D: {empty}")
logger.info(f"Empty Vectors4D shape: {empty.shape}")
if __name__ == "__main__":
vectors4d_example()See also Vector4D for a single 4D vector.

