Skip to main content

Generic hierarchical tree backed by NumPy arrays with typed node payloads

Project description

Array Tree

A generic hierarchical tree data structure backed by NumPy structured arrays, with typed node payloads.

ArrayTree stores tree topology (parent, child, and sibling pointers) in a compact NumPy structured array for cache-friendly memory layout and efficient bulk operations. Payloads are stored separately in a typed list, so the tree works with any Python object — strings, dicts, dataclasses, or the included DataNode container for data + metadata.

Designed for CAE (Computer-Aided Engineering) workflows where trees represent assembly structures, material hierarchies, and analysis results with thousands of nodes carrying large numerical arrays.

Key features

  • GenericArrayTree[T] accepts any payload type
  • NumPy-backed topology — structured array with uint32 IDs, ~29 bytes per node
  • Soft deletion — O(1) delete, explicit repack() for compaction
  • Lazy loadingLazyDataNode loads array data on demand, releases to free memory
  • Frozen mode — prevent mutations after construction
  • Zero hard dependencies beyond NumPy

Installation

pip install vcti-array-tree>=1.4.0

In pyproject.toml dependencies

dependencies = [
    "vcti-array-tree>=1.4.0",
]

Quick Start

from vcti.arraytree import ArrayTree, DataNode

# Create a tree with any payload type
tree: ArrayTree[str] = ArrayTree()

# Add nodes (root is always id=0)
models = tree.add_node(0, data="models")
results = tree.add_node(0, data="results")
stress = tree.add_node(results, data="stress")

# Navigate
children = tree.get_children_ids(0)        # [1, 2]
parent = tree.get_parent_id(stress)         # 2 (results)
all_nodes = tree.iter_nodes(0)              # [1, 2, 3] depth-first

# Access and update payloads
tree.get_node(stress)                       # "stress"
tree.set_node(stress, "von_mises_stress")   # update payload

# Ancestry and depth
tree.get_ancestor_ids(stress)               # [2, 0] (results, root)
tree.get_depth(stress)                      # 2

# Soft-delete and repack
tree.delete_node(models)
mapping = tree.repack()                     # compacts, remaps IDs

# Freeze for read-only access
tree.freeze()

With DataNode payloads

import numpy as np
from vcti.arraytree import ArrayTree, DataNode

tree: ArrayTree[DataNode] = ArrayTree()

tree.add_node(0, DataNode(
    data=np.array([1.0, 2.0, 3.0]),
    attributes={'units': 'mm', 'name': 'displacement'},
))

tree.add_node(0, DataNode(
    attributes={'type': 'group', 'solver': 'NASTRAN'},
))

Error handling

from vcti.arraytree import ArrayTree

tree: ArrayTree[str] = ArrayTree()
node = tree.add_node(0, "data")

# KeyError — node doesn't exist
try:
    tree.get_node(999)
except KeyError:
    pass

# ValueError — node is deleted
tree.delete_node(node)
try:
    tree.get_node(node)
except ValueError:
    pass

# RuntimeError — tree is frozen
tree.freeze()
try:
    tree.add_node(0, "blocked")
except RuntimeError:
    pass

Core API

ArrayTree[T]

Method Description
add_node(parent_id, data) Add child node, returns new ID
set_node(node_id, data) Update payload for existing node
delete_node(node_id, recursive) Soft-delete (marks inactive)
get_node(node_id) Get payload (O(1))
get_parent_id(node_id) Get parent ID
get_children_ids(node_id) List child IDs
get_ancestor_ids(node_id) List ancestor IDs (parent to root)
get_depth(node_id) Depth of node (root = 0)
iter_nodes(root_id, recursive) Depth-first traversal
get_all_node_ids(active_only) All node IDs
repack(new_capacity) Remove deleted nodes, compact arrays
freeze(payloads=False) Freeze structure; optionally freeze payloads too
is_payloads_frozen() True if payloads are also frozen
count_nodes(active_only) Count nodes
get_capacity() Current pre-allocated capacity
len(tree) Number of active nodes
node_id in tree Check if node is active
for nid in tree Iterate active node IDs
get_path(node_id) Path from root to node
tree.topology Read-only numpy view of tree structure
tree.payloads Read-only tuple of payloads

DataNode

Simple container for tree node payloads:

Attribute Type Description
data Any (ndarray, MaskedArray, etc.) or None Data payload
attributes dict[str, Any] Metadata dictionary

LazyDataNode

DataNode subclass with on-demand loading for large arrays:

from vcti.arraytree import ArrayTree, LazyDataNode

tree: ArrayTree[LazyDataNode] = ArrayTree()

node = LazyDataNode(
    loader=lambda: load_stress_data(path, offset),
    attributes={'units': 'MPa', 'name': 'stress'},
)
nid = tree.add_node(0, node)

# Attributes always available
tree.get_node(nid).attributes       # {'units': 'MPa', 'name': 'stress'}

# Array loaded on demand
tree.get_node(nid).is_loaded        # False
tree.get_node(nid).load()           # calls loader, caches result
tree.get_node(nid).is_loaded        # True
tree.get_node(nid).release()        # frees array, can reload later
Method/Property Description
load() Load array via callback (idempotent)
release() Free array data, attributes stay resident
is_loaded True if array is in memory

Configuration

The ID dtype and sentinel value are configurable per tree:

import numpy as np
from vcti.arraytree import ArrayTree

tree = ArrayTree(id_dtype=np.uint64)          # 64-bit node IDs
tree.id_dtype                                  # dtype('uint64')
tree.invalid_id                                # 18446744073709551615
tree.tree_dtype                                # structured dtype for topology

The make_tree_dtype(id_dtype) helper builds the structured dtype for external consumers (e.g., file loaders building topology arrays).

Exceptions

Exception Raised when
KeyError Node ID doesn't exist
ValueError Node is deleted, or invalid operation (e.g., delete root)
RuntimeError Tree structure is frozen, or payloads are frozen

Dependencies

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

vcti_array_tree-1.4.0.tar.gz (22.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

vcti_array_tree-1.4.0-py3-none-any.whl (14.2 kB view details)

Uploaded Python 3

File details

Details for the file vcti_array_tree-1.4.0.tar.gz.

File metadata

  • Download URL: vcti_array_tree-1.4.0.tar.gz
  • Upload date:
  • Size: 22.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for vcti_array_tree-1.4.0.tar.gz
Algorithm Hash digest
SHA256 0e0e91de0b10dc0b25f57331a2719a717d7661c246bc72aa770b2753f445e1d7
MD5 b2950cf3f019be9d1bbc25aca4315dda
BLAKE2b-256 28b7b079437841e07551eab623fdc8937097eb381d790bf2fe59397397ee14de

See more details on using hashes here.

Provenance

The following attestation bundles were made for vcti_array_tree-1.4.0.tar.gz:

Publisher: publish.yml on vcollab/vcti-python-array-tree

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file vcti_array_tree-1.4.0-py3-none-any.whl.

File metadata

File hashes

Hashes for vcti_array_tree-1.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e133e4d0a8cf6a2d014e65e866b3664d8862d63231c9fa65ba8a3f658f25f3be
MD5 8803777b8409faa6be2def46b86b2780
BLAKE2b-256 c4a662a636c725798b36bdd7712da471eb51cf4202c2c03679183cb998801423

See more details on using hashes here.

Provenance

The following attestation bundles were made for vcti_array_tree-1.4.0-py3-none-any.whl:

Publisher: publish.yml on vcollab/vcti-python-array-tree

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page