# Inputs and Outputs

> USearch for Python

### Arenas

Every output whose length only the call knows, such as matches, rows, products, monoliths and cursor or stream batches, lands in a `usearch.Arena` you pass, and the verb answers a view over it without copying.
`usearch.Arena(executor)` places it where that executor computes, decided once, and `usearch.Arena()` makes host pages for the calling thread.
Each call rewinds its arena, so the previous call's views die and a steady loop allocates once.
To keep a result past the next call, or to read one while making the next, pass that call another arena.
An arena serves one call at a time, so each deferred call needs its own.
A NumPy array or `memoryview` over an output pins its arena as a `memoryview` pins a `bytearray`, so the next call naming that arena raises `ContendedError` until the export is released.
A stale output raises `UninitializedError` from `memoryview`, from indexing a `Matches` and from `DenseProducts.query`, but `np.asarray` swallows that error into a 0-d object array, so read through those or copy with `np.array(...)` while the output is fresh.
An arena from a VRAM executor holds device memory, which host accessors refuse with `DeviceError`.
It serves only calls on that executor, any other or none raising `DeviceError`, and holds the executor until it is freed, so an executor closed first keeps its streams until then.
Its views export `__cuda_array_interface__` instead, for CuPy, PyTorch or Numba, and `arena.describe()` tells the two apart.
Every view also exports `__dlpack__` wherever it lives, so `torch.from_dlpack(vectors)` takes it in place.

```python
hits, arena = 0, usearch.Arena(pool)
for batch in batches:
    matches = index.on(pool).search(batch, 10, arena)  # rewinds: last batch's matches are gone
    hits += int(np.asarray(matches.counts).sum())  # an array kept past here would pin the arena
found, fetched = usearch.Arena(), usearch.Arena()  # two live results, two arenas
for key, _ in index.search(query, 10, found)[0]:
    rerank(key, index.get(key, fetched))
pools = [usearch.Executor.for_ram(threads=8) for _ in shards]  # one executor and one arena per submitted call
arenas = [usearch.Arena(own) for own in pools]
calls = [shard.deferred(own).search(queries, 10, arena) for shard, own, arena in zip(shards, pools, arenas)]
results = [call.wait() for call in calls]  # or, in a coroutine: await call
```

`search` answers a `usearch.Matches`, `get` and `attend` answer `usearch.DenseVectors`, a cursor's batches `usearch.DenseBatch`, `multiply` and `maxsim` answer `usearch.DenseProducts`, and `save_buffer` answers `usearch.Monolith`.
Their `len()` counts queries, entries or rows, or bytes of a monolith.

### Rows & Scalars

Inputs arrive through DLPack first, then PEP 3118 and `__array_interface__`, so NumPy is never required and a PyTorch, JAX or CuPy tensor needs no round-trip through it, on the host or on its GPU.
Only the row stride may vary; a strided column would force a gather the SIMD kernels have no form for.
A `dtype=` names the scalar type a buffer cannot spell, such as `Scalar.BF16` over raw bytes, and must be as wide as the buffer's items.
In `get` and `attend`, an `outputs_dtype=` names the scalar type the verb exports as, converted from storage.

### Search

A ranked verb answers a `Matches` that reads through to three outputs in the arena:

```python
arena = usearch.Arena(pool)
matches = index.on(pool).search(queries, 10, arena)
matches.keys  # shape (queries, columns, 16), format 'B', columns at most 10
matches.distances  # shape (queries, columns),     format 'd'
matches.counts  # shape (queries,)
matches[0]  # a tuple of `Match` struct sequences, one per hit for query zero
np.asarray(matches.distances)  # zero-copy; NumPy never entered the binding
```

A count bounds its own row, and the cells past it hold nothing a call wrote.

The breadth of the candidates is a per-call setting rather than a property of the handle, so a recall sweep runs against one immutable index with no mutation between points.
Traversal telemetry rides a `DenseTraversalStats` the call fully overwrites, so one reading of `visited_members`, `computed_distances` and `restarts` always belongs to one call and never to the sweep around it:

```python
for expansion_factor in (16, 32, 64, 128, 256):
    stats = usearch.DenseTraversalStats()
    index.on(pool).search(queries, 10, arena, expansion_factor=expansion_factor, stats=stats)
    print(expansion_factor, stats.computed_distances)
```

`traversal=usearch.Traversal.SCAN` skips the walk and scores every entry exactly, on RAM and NVMe handles, which gives such a sweep its ground truth.
`+=` adds one reading into another when the totals across a sweep are what is wanted:

```python
total = usearch.DenseTraversalStats()
for batch in batches:
    index.on(pool).search(batch, 10, arena, stats=stats)
    total += stats
```

`import usearch`

`from usearch import Arena, Match, Matches, …`

## Scalar

A stored or exchanged scalar type, widest dynamic range first.

```python
class Scalar(IntEnum)
```

- `F64` = `1`: IEEE-754 binary64: ±1.80e308, epsilon 2.22e-16, with infinities and NaNs.
- `F32` = `2`: IEEE-754 binary32: ±3.40e38, epsilon 1.19e-7, with infinities and NaNs.
- `BF16` = `3`: Truncated f32 with a 7-bit mantissa: f32's range and epsilon 7.81e-3.
- `F16` = `4`: IEEE-754 binary16: ±65504, epsilon 9.77e-4, with infinities and NaNs.
- `E5M2` = `5`: FP8 with a 5-bit exponent: ±57344, epsilon 0.25, with infinities and NaNs.
- `E4M3` = `6`: FP8 with a 4-bit exponent: ±448, epsilon 0.125, no infinities, two NaN codes.
- `E3M2` = `7`: FP6 with a 3-bit exponent in one byte: ±28, every one of its 64 codes a number.
- `E2M3` = `8`: FP6 with a 2-bit exponent in one byte: ±7.5, every one of its 64 codes a number.
- `I8` = `10`: 8-bit signed integer spanning -128 to 127.
- `I4X2` = `11`: Two signed nibbles per byte spanning -8 to 7, even dimensions in the low nibble.
- `I32` = `12`: 32-bit signed integer spanning ±2.15e9.
- `U8` = `20`: 8-bit unsigned integer spanning 0 to 255.
- `U4X2` = `21`: Two unsigned nibbles per byte spanning 0 to 15, even dimensions in the low nibble.
- `U1X8` = `22`: Eight single-bit dimensions per byte, the form Hamming and Jaccard score.

## Traversal

How a search reaches the entries it scores.

```python
class Traversal(IntEnum)
```

- `WALK` = `1`: Walks the graph, scoring the entries the expansion reaches; approximate, the default.
- `SCAN` = `2`: Scores every entry published when the scan starts; exact, the ground truth a walk approximates.

## Arena

The memory every output whose length scales with a call lands in, rewound by each call handed it.

```python
class Arena
```

## Arena

Makes one where `executor` computes, or host pages for the calling thread.

```python
Arena(executor: Executor | None = None, /)
```

## residency

The [`Residency`](https://www.unum.cloud/docs/usearch/python/residency.md#Residency) its outputs live in.

```python
residency: Residency
```

Python-only.

## Match

One scored entry: the identity found and its distance, unpacking as `key, distance = match`.

```python
class Match(structseq[Any], tuple[Key, float])
```

## key

The identity of the matched entry.

```python
key: Key
```

## distance

The distance to the query, in the index's own metric.

```python
distance: float
```

## Matches

A ranked run [`search`](https://www.unum.cloud/docs/usearch/python/metrics.md#search) wrote into an [`Arena`](#Arena), reading through to three outputs.

```python
class Matches
```

Exports Arrow in place as a LargeListView of `Struct<key, distance>`, or compacted into a LargeList
when `requested_schema` asks one.

## keys

(queries, columns, 16) identities, most significant byte first.

```python
keys: _ArenaBuffer
```

## distances

(queries, columns) distances in the index's own metric, columns at most the call's wanted.

```python
distances: _ArenaBuffer
```

## counts

(queries,) match counts, each bounding its own row.

```python
counts: _ArenaBuffer
```

## __arrow_c_array__

Exports host rows as an Arrow schema and array capsule.

```python
def __arrow_c_array__(
    requested_schema: _Capsule | None = None,
) -> tuple[_Capsule, _Capsule]
```

## __arrow_c_device_array__

Exports rows wherever they live as an Arrow schema and device array capsule.

```python
def __arrow_c_device_array__(
    requested_schema: _Capsule | None = None,
) -> tuple[_Capsule, _Capsule]
```

## __arrow_c_stream__

Exports host rows as an Arrow stream of one chunk.

```python
def __arrow_c_stream__(
    requested_schema: _Capsule | None = None,
) -> _Capsule
```

## DenseTraversalStats

Traversal telemetry, fully overwritten by every call it is passed to; `+=` sums a sweep.

```python
class DenseTraversalStats
```

## __iadd__

Python-only.

```python
def __iadd__(other: DenseTraversalStats, /) -> Self
```

## visited_members

Entries whose neighborhoods the walk expanded.

```python
visited_members: int
```

## computed_distances

Distance evaluations the walk paid for.

```python
computed_distances: int
```

## restarts

Times the walk fell back to a fresh entry point.

```python
restarts: int
```

