MontyCad.Training (ExMontyCad v0.2.0)

Copy Markdown View Source

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.

Summary

Types

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

Functions

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

Visits the part and takes a percept at each one.

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

Types

visit_node()

@type visit_node() :: %{
  location: [float()],
  features: %{optional(String.t()) => 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.

Functions

library(nodes, object_id, options \\ [])

@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(part, options \\ [])

@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(part, object_id, path, options \\ [])

@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.