Monty learns an object by sensing it, and its pretrained memories come from episodes. A CAD part has already been explored: its surface is exact, and the recognition names every face, so a node's label is a fact rather than a conclusion. This package is the join between the two — the part on one side, Monty's own percept and its library format on the other.
{:ok, part} = Quiddity.open("bracket.step")
# What a sensor module perceives here:
{:ok, camera} = Quiddity.Camera.patch([15.0, 0.0, 13.0], [0.0, 0.0, 1.0], distance: 5.0)
{:ok, frame} = Quiddity.frame(part, camera)
{:ok, message} = MontyCad.Percept.message(frame)
MontyCad.Percept.node_features(message)
#=> %{"pose_vectors" => [...], "principal_curvatures_log" => [0.0, 0.0], ...}
# The library a run loads:
{:ok, written} = MontyCad.Training.write(part, "bracket", "bracket.jsonl.gz", spacing: 2.0)
written.nodes
#=> 186The percept is Monty's own code — Monty.Frameworks.Models.SensorModules.ObservationProcessor
over the frame — not a re-implementation, so a node trained from a part is the
percept a run would have made. docs/bridge.md says exactly what is faithful and
what is not.
The three modules
| Module | What it answers |
|---|---|
MontyCad.Percept | A Quiddity.Frame as the observation a sensor module reads: semantic_3d, shaded rgba, cam_to_world, and the message Monty builds from them |
MontyCad.Training | Visits over the part's surface, one percept each, with exact labels from the recognition |
MontyCad.Library | The file: gzipped JSON lines, one record per node, in the shape GraphLM reads |
MontyCad.Percept.node_features/1 merges the morphological and non-morphological
features the way GraphLM.select_features_to_use/2 does — those are the columns
a record stores.
From a shell
mix monty_cad.train bracket.step --object bracket --out bracket.jsonl.gz--spacing sets the millimetres between visits, --limit the most visits to
make. The answer names the file, the nodes, the features held, and how many nodes
each feature family contributed.
Writing a plugin: the generators
A run is assembled from modules — a motor policy that decides where to sense next, a sensor transform that turns one observation into a percept. Each has a contract in Monty's own behaviour, and a cluster that will call it wants a description of it first. Two generators write both halves, so what you fill in is the decision and not the wiring:
mix monty_cad.gen.policy follow_chamfers
mix monty_cad.gen.transform surface_smootherEach writes a module and a test:
follow_chamfers | |
|---|---|
| the module | lib/<your_app>/plugins/follow_chamfers.ex — Monty.Frameworks.Models.MotorPolicies.MotorPolicy's callbacks and describe/0 |
| the test | test/<your_app>/plugins/follow_chamfers_test.exs — it describes itself, it is a motor policy, it forgets between episodes |
call/6 and call/3 raise with a sentence saying what to answer, on purpose: a
generated body that looks like an answer is a decision nobody made. The policy is
:owner_only (it reads a B-rep, and a B-rep handle cannot leave its node); change
that in describe/0 if yours does not, and the test will follow the description.
The description is MontyCad.Plugin's contract — kinds, placements, needs — and
MontyCad.Plugin.conforms?/1 says what is wrong with one that a cluster cannot
use. A cluster asks over the protocol; this package asks the same question in a
test.
Install
def deps do
[
{:ex_monty_cad, "~> 0.2"}
]
endThis package is Elixir only, but ex_quiddity builds a Rust engine when it is
compiled, so building wants a Rust toolchain and running wants none.
Limits, said out loud
- A CAD frame is perfect: no sensor noise, no occlusion from a real probe. A
library trained from one is a library trained from a drawing;
docs/bridge.mdsays what that costs. - A library holds the part, densely and evenly. It does not hold an exploration policy — that is what a training episode is, and this package does not replace one.
- The labels are exact because the recognition named the faces, not because
anything was inferred. Where quiddity could not prove a feature,
Quiddity.Recognition.unexplained_faces/1is where that is visible. - A node whose patch saw too little surface is skipped and counted, not stored: an empty percept is not a node.
Licence
MPL-2.0 (LICENSE). The dependencies keep their own: ex_quiddity is MPL-2.0,
ex_tbp_monty_core is MIT (the Thousand Brains Project's Monty, reimplemented on
the BEAM), and the engine under ex_quiddity — quiddity and haecceity — is
Apache-2.0. NOTICE says what came from where.