# `Monty.Frameworks.Models.EvidenceMatching.Hypotheses`
[🔗](https://github.com/d-led/ex.tbp.monty/blob/v0.1.0/packages/ex_tbp_monty_core/lib/monty/frameworks/models/evidence_matching/hypotheses.ex#L1)

A set of hypotheses: their evidence, the location each implies, the pose each
implies, and whether each can still be possible.

Mirrors `tbp/monty/frameworks/models/evidence_matching/hypotheses.py`. The
four fields are parallel — an index is one hypothesis in all of them — which
is why Monty keeps them as separate arrays rather than a list of records: a
learning module selects, masks and weights whole columns of them with one
operation.

In Elixir `possible` is a list, one flag per hypothesis, and `evidence`,
`locations` and `poses` are the same rows as **bytes**: eight little-endian
f64 of evidence, then three of location and nine of rotation each, in the
order the hypothesis index names them. That is the form a step needs them in —
it displaces every hypothesis, blends what each earned into a column, then
asks the k-d index about every location and a kernel for the evidence at each
— so holding them this way stops a step from rebuilding a list, and eight or
three or nine boxed floats a hypothesis, once per pass. `evidence/1`,
`evidence_at/2`, `locations/1`, `location/2`, `poses/1` and `pose/2` read them
back, one hypothesis at a time where that is all a caller wants.

`empty/0` is the identity for `concatenate/1`, which is what lets a learning
module build a step's hypotheses from however many sources voted.

# `location`

```elixir
@type location() :: [float()]
```

# `pose`

```elixir
@type pose() :: [[float()]]
```

# `t`

```elixir
@type t() :: %Monty.Frameworks.Models.EvidenceMatching.Hypotheses{
  evidence: binary(),
  locations: binary(),
  poses: binary(),
  possible: [boolean()]
}
```

`evidence` is `hypotheses` f64, `locations` `hypotheses * 3` f64 and `poses`
`hypotheses * 9` f64, all in the hypothesis's own order; `possible` has one
entry each.

# `argmax_evidence`

```elixir
@spec argmax_evidence(t()) :: non_neg_integer()
```

The index of the most likely hypothesis — the first of several equal maxima.

Asked far more often than any other question of the column, so it is asked of
the bytes: a kernel when they are loaded, and the same walk in Elixir otherwise.

# `concatenate`

```elixir
@spec concatenate([t()]) :: t()
```

One set of hypotheses from several, in order.

Monty concatenates along the hypothesis axis, so votes from two learning
modules produce their votes first and the receiving module's hypotheses
second, in the order the caller passes them.

# `count`

```elixir
@spec count(t()) :: non_neg_integer()
```

How many hypotheses the set holds.

# `empty`

```elixir
@spec empty() :: t()
```

The hypotheses of nothing: the identity for `concatenate/1`.

# `evidence`

```elixir
@spec evidence(t()) :: [float() | nil]
```

Every hypothesis' evidence, as the numbers they are.

# `evidence_at`

```elixir
@spec evidence_at(t(), non_neg_integer()) :: float() | nil
```

One hypothesis' evidence.

# `evidence_bytes`

```elixir
@spec evidence_bytes(t()) :: binary()
```

The evidence column as a step holds it: one f64 per hypothesis.

What a kernel reads, where `evidence/1` is what a reporter or an argmax wants.

# `index_bytes`

```elixir
@spec index_bytes([non_neg_integer()]) :: binary()
```

Indices as the u32 bytes a kernel reads them from.

# `index_list`

```elixir
@spec index_list(binary()) :: [non_neg_integer()]
```

Indices as Elixir values, read out of the u32 bytes a kernel answers with.

# `indices_within`

```elixir
@spec indices_within(t(), float(), number()) :: [non_neg_integer()]
```

The indices of the hypotheses within `percentage` per cent of the best one.

Monty's `get_possible_hypothesis_ids`: `np.where(evidence > max - max / 100 *
x_percent_threshold)`, once the best clears `floor`. A learning module asks
once per object per step, so it is answered from the bytes.

Two edges are numpy's rather than Elixir's, and they are the reason the answer
is stated here: a column with a NaN in it has no best at all — `np.max`
propagates the NaN, every comparison against one is false — so nothing is
possible; and the band is strict, so a hypothesis exactly at the threshold is
out.

# `location`

```elixir
@spec location(t(), non_neg_integer()) :: location()
```

One hypothesis' location.

# `location_bytes`

```elixir
@spec location_bytes(location()) :: binary()
```

The bytes of one location, as a step holds it.

# `locations`

```elixir
@spec locations(t()) :: [location()]
```

Every hypothesis' location, as the three values each one is.

# `locations_of`

```elixir
@spec locations_of(binary()) :: [location()]
```

The locations in `bytes`, as values — the reference's way of reading them.

# `max_evidence`

```elixir
@spec max_evidence(t()) :: float() | nil
```

The highest evidence any hypothesis holds.

# `min_evidence`

```elixir
@spec min_evidence(t()) :: float() | nil
```

The lowest evidence any hypothesis holds — the threshold that updates them all.

`evidence_threshold_config: "all"` asks this once per graph per step, and the
answer is one number: asked of the bytes, a minimum of six thousand of them no
longer costs six thousand decoded floats.

# `new`

```elixir
@spec new(keyword()) :: t()
```

One set of hypotheses from the rows of each, in order.

The caller speaks in the shapes it has — evidence as a number, a location as
`[x, y, z]`, a pose as three rows — and this is the one place those become the
bytes a step reads.

# `per_pose`

```elixir
@spec per_pose(keyword()) :: t()
```

One hypothesis per node per pose — the shape a burst samples in.

`locations` and `evidence` name the chosen nodes once; `poses` holds one
matrix per hypothesis in that order, pose-major, which is what aligning each
sensed direction against the nodes produces. The node block is encoded once
and copied, where `new/1` would encode the same few hundred nodes once per
sensed direction: a burst samples eight poses at each of a few hundred nodes,
ten graphs at a time, so that is tens of thousands of rows of the same values
every step.

# `pose`

```elixir
@spec pose(t(), non_neg_integer()) :: pose()
```

One hypothesis' rotation.

# `pose_bytes`

```elixir
@spec pose_bytes(pose()) :: binary()
```

The bytes of one rotation, row by row, as a step holds it.

# `poses`

```elixir
@spec poses(t()) :: [pose()]
```

Every hypothesis' rotation, as the matrix each one is.

# `poses_of`

```elixir
@spec poses_of(binary()) :: [pose()]
```

The rotations in `bytes`, as values — the reference's way of reading them.

# `put_evidence`

```elixir
@spec put_evidence(t(), [float()]) :: t()
```

The same hypotheses with a new evidence column, given as numbers.

# `put_evidence_bytes`

```elixir
@spec put_evidence_bytes(t(), binary()) :: t()
```

The same hypotheses with a new evidence column a kernel has written.

# `rows_at`

```elixir
@spec rows_at(t(), [non_neg_integer()]) :: {binary(), binary()}
```

The rows of the hypotheses at `indices`, as the bytes a kernel reads.

What a step needs when it asks about a subset — the hypotheses worth testing
this observation — without decoding them into values first.

# `second_argmax_evidence`

```elixir
@spec second_argmax_evidence(t()) :: non_neg_integer() | nil
```

The index of the runner-up hypothesis, or `nil` when there is no second one.

Monty reads it as `argsort(evidence)[-2]` to weigh a vote between the two best
hypotheses, and a step asks once per object. Asked of the bytes: the sort it
stands for needs every value decoded and a tuple for each.

# `take`

```elixir
@spec take(t(), [non_neg_integer()]) :: t()
```

The hypotheses at the given indices, in the order asked for.

Used by a learning module to keep the hypotheses it still considers possible
without rebuilding four columns by hand. A kept row is copied out of the row
it was already in, so this does not go back through the values themselves.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
