Metadata-Version: 2.4
Name: vcti-nptree
Version: 2.0.0
Summary: Vectorized, numpy-backed tree: parent-id topology and user-defined fields in one growable structured array, with vectorized operations over node ids.
Author: Visual Collaboration Technologies Inc.
Requires-Python: <3.15,>=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: vcti-nputils>=1.6.0
Requires-Dist: vcti-multi-valued-array>=1.6.0
Requires-Dist: numpy>=1.26
Requires-Dist: numba>=0.59
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Requires-Dist: hypothesis; extra == "test"
Provides-Extra: lint
Requires-Dist: ruff; extra == "lint"
Provides-Extra: typecheck
Requires-Dist: mypy; extra == "typecheck"
Provides-Extra: bench
Requires-Dist: tabulate; extra == "bench"
Provides-Extra: tree
Requires-Dist: vcti-tree>=2.0.0; extra == "tree"
Dynamic: license-file

# vcti-nptree

Vectorized, numpy-backed tree: parent-index topology and user-defined fields in
one growable structured array, with vectorized operations over node indices.

## Overview

`vcti-nptree` provides `NpTree`, a single-rooted tree whose
structure and per-node data live together in one growable numpy structured
array (backed by `GrowableArray` from
[vcti-nputils](https://github.com/vcollab/vcti-python-nputils)):

- **One record per node**, addressed by its integer **row index**. The record
  carries `parent_index`, a `flags` column (active / locks), and any
  **fixed-width user fields** declared at construction.
- **`parent_index` is the structure.** Children and siblings are vectorized
  scans of that column (`parent_index == n`), ancestors are a short parent-chain
  walk, and per-node fields roll up with
  `np.bincount(parent_index, weights=field)`.
- **Soft deletion** clears the `ACTIVE` bit and **leaves the row in place** —
  nothing is moved or renumbered, so node indices stay stable for the life of the
  tree and every vectorized operation keeps working with deletions present.
  `delete(..., fill=...)` optionally writes a per-field "inactive" / "na" /
  zero / custom value so aggregations stay branchless without even consulting
  the mask.

It is built for **vectorized operations over sets of nodes** — aggregating,
masking, or rolling up fields across many nodes at once, rather than walking
the tree one node at a time. Nodes are addressed by integer indices and
index-arrays (not opaque handles), which is what lets those operations be plain
numpy.

> The design and rationale are documented in [docs/design.md](docs/design.md).

## Installation

```bash
pip install vcti-nptree
```

### In `requirements.txt`

```
vcti-nptree>=2.0.0
```

### In `pyproject.toml` dependencies

```toml
dependencies = [
    "vcti-nptree>=2.0.0",
]
```

---

## Quick Start

```python
import numpy as np
from vcti.tree.nptree import FieldSpec, NpTree

# Declare fixed-width user fields: a schema dtype plus the per-field default and
# inactive values (unspecified fields default to zero).
spec = FieldSpec.create(
    np.dtype([("mass", "f8")]), default={"mass": 0.0}, inactive={"mass": 0.0}
)

tree = NpTree(spec)                # the root is node 0
p1 = tree.create_child(tree.root)
p2 = tree.create_child(tree.root)

# Bulk-create a level of elements under the parts (one vectorized append).
tree.create_children(np.array([p1, p1, p2]), mass=np.array([3.0, 4.0, 10.0]))

# Vectorized roll-up: every node's subtree total in one pass.
totals = tree.subtree_sum("mass")
assert totals[tree.root] == 17.0           # whole tree

# Soft-delete a subtree; aggregations still work and node indices stay valid.
tree.delete(p1)
assert tree.subtree_sum("mass")[tree.root] == 10.0
```

---

## Dependencies

- [`vcti-nputils`](https://github.com/vcollab/vcti-python-nputils) `>=1.6.0`
  — `GrowableArray` storage backing.
- [`vcti-multi-valued-array`](https://github.com/vcollab/vcti-python-multi-valued-array)
  `>=1.5.0` — the CSR / ragged container for the parent→children index.
- `numpy>=1.26` — structured-array storage and vectorized operations.
- `numba>=0.59` — compiles the per-node kernels (DFS walk orders, ordered
  ancestor chains).
