Metadata-Version: 2.4
Name: vcti-multi-valued-array
Version: 1.6.0
Summary: Variable-length array for multivalued mappings — CSR-like storage with domain-codomain operations, reverse, cascade, and filtering
Author: Visual Collaboration Technologies Inc.
Requires-Python: <3.15,>=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.24
Requires-Dist: numba>=0.59
Requires-Dist: vcti-nputils>=1.0.0
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-cov; extra == "test"
Requires-Dist: hypothesis; extra == "test"
Provides-Extra: bench
Requires-Dist: pytest; extra == "bench"
Requires-Dist: pytest-benchmark; extra == "bench"
Provides-Extra: lint
Requires-Dist: ruff; extra == "lint"
Provides-Extra: typecheck
Requires-Dist: mypy; extra == "typecheck"
Dynamic: license-file

# Multi Valued Array

Variable-length array for multivalued mappings — CSR-like storage with domain-codomain operations, reverse, cascade, and filtering.

## Installation

```bash
pip install vcti-multi-valued-array>=1.6.0
```

### In `pyproject.toml` dependencies

```toml
dependencies = [
    "vcti-multi-valued-array>=1.6.0",
]
```

---

## Quick Start

```python
import numpy as np
from vcti.multivalued import MultiValuedArray, reverse, cascade

# Create: element 0 has values [10, 20], element 1 has [30], element 2 has [40, 50, 60]
mva = MultiValuedArray(counts=np.array([2, 1, 3]), values=np.array([10, 20, 30, 40, 50, 60]))

mva.counts()          # array([2, 1, 3])
mva.offsets()         # array([0, 2, 3, 6])
mva.values()          # array([10, 20, 30, 40, 50, 60])
mva.value_indices()   # array([0, 0, 1, 2, 2, 2])  — which element each value belongs to

# With domain and codomain metadata
faces = np.array([(1, 1), (2, 2)], dtype=[('id', 'u4'), ('type', 'u4')])
vertices = np.array([(1, 0., 0.), (2, 2., 0.), (3, 3., 1.), (4, 2., 2.), (5, 0., 2.)],
                     dtype=[('id', 'u4'), ('x', 'f4'), ('y', 'f4')])

connectivity = MultiValuedArray(
    counts=np.array([4, 3], dtype=np.uint32),
    values=np.array([0, 1, 3, 4, 1, 2, 3], dtype=np.uint32),
    domain=faces,
    codomain=vertices,
)

# Reverse: for each vertex, which faces contain it
node_faces = reverse(connectivity)
```

---

## Core API

### MultiValuedArray

| Method | Description |
|--------|-------------|
| `counts()` | Number of values per element (lazy from offsets) |
| `offsets()` | Cumulative positions into values (lazy from counts) |
| `values()` | Flat array of all values |
| `value_indices()` | Maps each value to its element index |
| `domain()` | Domain element identifiers |
| `codomain()` | Codomain element identifiers |
| `set_values(values)` | Set/replace values array |
| `len(mva)` | Number of elements (domain size) |
| `mva[i]` | Values for element at index *i* |
| `for v in mva` | Iterate over element value slices |
| `mva == other` | Structural equality check |

### Operations

| Function | Description |
|----------|-------------|
| `reverse(mva)` | Invert mapping (A->B becomes B->A) |
| `cascade(ab, bc)` | Compose mappings (A->B + B->C = A->C) |
| `concatenate(a, b)` | Vertically stack two arrays |
| `sort_and_unique(mva)` | Sort and deduplicate values per element |
| `non_negative_array(src)` | Extract non-negative values from 2D array |
| `from_joint_counts_and_values_array(arr)` | Parse interleaved format |
| `get_counts_values_array(mva)` | Convert to interleaved format |
| `get_chunk(mva, a, b)` | Extract elements [a:b] |

---

## Dependencies

- [numpy](https://numpy.org/) (>=1.24)
- [numba](https://numba.pydata.org/) (>=0.59) — JIT compilation for performance-critical loops
- [vcti-nputils](https://pypi.org/project/vcti-nputils/) (>=1.0.0) — position_array, as_ndarray
