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

Which hypotheses survive a step, and which new ones are worth trying.

Mirrors
`tbp/monty/frameworks/models/evidence_matching/burst_sampling.py::BurstSamplingHypothesesUpdater`,
the sampling strategy Monty's evidence matching actually runs.

The idea: when evidence stops moving, one more observation of the same
hypotheses will not settle anything. So when the largest evidence slope falls
to `burst_trigger_slope` or below — and no burst is already running — the
updater spends `burst_sampling_duration` steps sampling fresh hypotheses
around the best-matching nodes, and drops the ones whose evidence is falling
faster than `deletion_trigger_slope`. A burst therefore explores without
simply growing the hypothesis space.

## Two passes, both deterministic

  * **existing** hypotheses are filtered by slope, never sampled randomly, so
    two runs of an episode keep the same ones;
  * **new** hypotheses come from the top `count ÷ rotations_per_node` nodes by
    *this* observation's feature evidence, each tried with every rotation the
    sensed pose allows.

A new hypothesis starts with its feature evidence and `possible?: false`: it
has earned nothing yet, but it is the alternative the burst exists to try.

# `t`

```elixir
@type t() ::
  %Monty.Frameworks.Models.EvidenceMatching.BurstSampling.BurstSamplingHypothesesUpdater{
    burst_sampling_duration: term(),
    burst_trigger_slope: term(),
    deletion_trigger_slope: term(),
    displacer: term(),
    evidence_slope_trackers: term(),
    graph_memory: term(),
    initial_possible_poses: term(),
    max_slope: term(),
    sampling_burst_steps: term(),
    sampling_multiplier: term(),
    scorer: term(),
    umbilical_num_poses: term()
  }
```

# `begin_step`

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

Prepare the step: measure the evidence slope and start a burst if it stalled.

A burst starts when evidence has stopped moving, not while it is climbing —
while it climbs, another observation is the cheaper way to settle things.

# `displace_hypotheses`

```elixir
@spec displace_hypotheses(
  t(),
  Monty.Frameworks.Models.EvidenceMatching.Hypotheses.t(),
  [number()] | nil
) :: Monty.Frameworks.Models.EvidenceMatching.Hypotheses.t()
```

Move every hypothesis by `displacement`, as its own orientation sees it.

A no-op with no hypotheses or no movement — before the first step there is
nothing to move by.

# `end_step`

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

Count a burst step down once the step's update is done.

# `new`

```elixir
@spec new(Monty.Frameworks.Models.GraphMatching.GraphMemory.t(), keyword()) :: t()
```

An updater over `memory`.

`:sampling_multiplier` is how many hypotheses per node a burst draws.
`:initial_possible_poses` is `nil` for informed sampling — align each node's
stored pose with what was sensed — or a list of 3×3 pose matrices for fixed
poses.

# `reset`

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

Forget what an episode learned, keeping the configuration.

# `update_evidence`

```elixir
@spec update_evidence(
  t(),
  Monty.Frameworks.Models.EvidenceMatching.Hypotheses.t(),
  %{optional(String.t()) =&gt; map()},
  String.t(),
  float()
) :: {t(), Monty.Frameworks.Models.EvidenceMatching.Hypotheses.t() | nil, map()}
```

Rebuild the hypothesis space for this observation and update its evidence.

Returns `{updater, hypotheses, telemetry}`, with `hypotheses` and telemetry
empty when the observation carries no channel this graph stores — the caller
then has nothing to update rather than an empty space pretending to be one.

---

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