# `MontyCad.Training`
[🔗](https://github.com/d-led/ex.monty.cad/blob/main/lib/monty_cad/training.ex#L1)

A graph memory built from a CAD part — supervised training without an episode.

Monty learns an object by touching it: a surface agent explores, and every
patch it lands on becomes a node with the percept it made there. A CAD part
already *is* the explored object — its surface is known exactly — so the
exploration can be replaced by sampling: put a patch sensor on the surface,
spacing the visits as far apart as the detail needs, and take the percept each
visit makes.

What comes out is what a training episode comes out with: locations in the
part's own frame and the features the sensor modules measured there. The
difference is that the *labels* are exact — quiddity says which face each node
is on and which feature that face belongs to — which is what makes this
supervised: a node's feature family is read, never concluded.

## What this is not

It is not a replacement for a training episode's *action* policy. Monty's
episodes also learn where to look next, and a CAD-built memory does not hold
that. What it holds is the part, densely and evenly, which is the thing an
episode spends its first minutes doing badly.

## Cost

One patch is one frame — 4 096 rays — and a percept is Monty's own feature
extraction on top. A plate at 2 mm spacing is a few hundred patches; a large
part at 0.5 mm is thousands, so `:limit` caps the work and `:spacing` is the
honest dial.

# `visit_node`

```elixir
@type visit_node() :: %{
  location: [float()],
  features: %{optional(String.t()) =&gt; term()},
  face: non_neg_integer(),
  family: String.t() | nil
}
```

One node: where the sensor was, what it perceived, and what quiddity says it
is looking at.

# `library`

```elixir
@spec library([visit_node()], String.t(), keyword()) ::
  {:ok, MontyCad.Library.t()} | {:error, Quiddity.Error.t()}
```

The nodes a previous visit produced, as the library a run loads.

`object_id` is what the graph is called — the name a recognition would answer
— so a run matching this library says `"bracket"`, not `"new_object0"`.

# `nodes`

```elixir
@spec nodes(Quiddity.Part.t(), keyword()) ::
  {:ok, map()} | {:error, Quiddity.Error.t()}
```

Visits the part and takes a percept at each one.

Options:

  * `:spacing` — how far apart the visits are, in millimetres (default 2.0);
  * `:limit` — the most visits to make (default 2 000);
  * `:distance` — how far the sensor stands off the surface (default 5 mm,
    which keeps a patch's footprint smaller than a face on most parts);
  * `:min_coverage` — the share of a patch that must land on the part for the
    visit to count (default 0.6);
  * `:features` — the sensor's feature list, defaulting to a Monty patch's;
  * `:progress` — a one-argument function called after each percept, for a
    caller that wants to show a count rather than wait.

A visit whose patch does not see the surface — a corner sharper than the
patch's field of view, or a face the rays miss — is *skipped*, not invented:
the node count is what the sensor could perceive, and `:skipped` in the answer
says how many visits it was.

# `write`

```elixir
@spec write(Quiddity.Part.t(), String.t(), Path.t(), keyword()) ::
  {:ok, map()} | {:error, Quiddity.Error.t()}
```

Samples the part and writes the library a run loads, in one call.

Answers what was written and what it holds: the path, the node count, the
feature names, and how many nodes each feature family contributed.

---

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