Coordinate-free circuit schematic engine: port-graph IR, deterministic geometry solver and router, lcapy renderer
Project description
lmschem
A coordinate-free circuit schematic engine. Authors describe a circuit as a port graph — components, electrical nets, relational placement — and a deterministic geometry engine places bodies, routes every wire, enforces hard drawing rules, and renders a PNG through lcapy/circuitikz. The same IR always yields the same drawing.
The complete authoring language reference is lmschem/rules.md — it
is the only document an author needs, and it ships as package data.
Architecture (four layers)
PortGraph IR ──► geometry engine ──► renderer adapter ──► PNG
(portgraph/) (geometry/solve, (geometry/adapters/
router, report) lcapy.py)
- Port-graph IR (
portgraph/ir.py) — Pydantic models: components, nets, relationalplaceanchors, rails/snaps, route hints, buses, decorations, blocks. No coordinates, ever. - Geometry engine (
geometry/) — deterministic placement (trunk-and-branch routing, channel assignment, facing-aware alignment), hard-rule checks with structured, fix-hinted issues, an ASCII occupancy grid, and solver-placed text (net labels, bus captions, decoration labels) backed by measured ink metrics. - Renderer adapter (
geometry/adapters/lcapy.py) — lowers the solved layout to an lcapy netlist with injected node positions (lcapy's placer is bypassed entirely) and verifies a real PNG came back. Emission is inventory-driven: bipoles, amps, chips/shapes, and the generic node-grammar path (node_pinnamesslot order) cover every drawable class. - Incremental edits (
edit.py) — targeted IR edit operations with a flow-style YAML emitter that keeps files in the shape authors write.
Entry point:
from lmschem.author import author_circuit
result = author_circuit(ir_yaml_string, "out_dir")
# result: ok / report / grid / issues / image
The inventory contract
lmschem/inventory/*.yaml is the sole runtime authority for
everything lcapy-specific: legal pin tokens, drawn subsets, pin aliases,
netlist-node slot order (node_pinnames), measured body extents and pin
coordinates, text metrics, decoration glyph ink, options, and the derived
semantic vocabulary. Runtime never introspects lcapy.
The inventory is generated, never hand-edited (except components.yaml,
schemcpts.yaml, options.yaml, placer.yaml, which are curated).
Regenerate after any lcapy upgrade:
python -m lmschem.gen_inventory
This re-measures glyphs, rewrites the generated YAMLs, re-derives the
semantic vocabulary (semantic_types.yaml), and re-splices the generated
component reference into rules.md between its markers. lcapy is pinned
exactly in pyproject.toml because the measurements are against that
version.
Coverage contract: every drawable inventory class either solves and
emits (guarded by tests/test_vocabulary_coverage.py, one param per
class, no rendering) or sits on gen_inventory.EXCLUDED_CLASSES with a
written reason (K, A, W, MISC, sV, sI). A new lcapy class becomes a
failing test param until supported or excluded.
Tests
python -m pytest tests -q
Most tests render real PNGs through pdflatex + ghostscript — a TeX distribution and ghostscript must be on PATH. The solve+emit coverage guard and the geometry tests run without rendering.
lcapy quirks this engine works around (do not "simplify" these away)
LatexRunner.runnever checks the pdflatex result andto_png_ghostscriptnever checks its output — the adapter verifies the PNG file itself and raises a structured error.- The opts parser splits on commas, so every label/value rides braced
(
l={...}); unbraced LaTeX with commas kills the compile silently. - A netlist with zero numeric nodes (all-unwired chips) crashes lcapy's canvas sizing — the adapter raises "nothing in the drawing is wired" first.
- Uisoamp's class declares a phantom
ocmpin its own draw rejects;gen_inventory._pins_forreads property-pin dicts from the class's own__dict__only. - lcapy injection scales by
node_spacing(2.0); gutters never move terminal segments.
Relationship to coursesmith
lmschem is standalone and speaks bare IR. Coursesmith integrates it through a thin adapter layer on its side (generator spec/executor, MCP edit tools, discovery): the generator-spec envelope, analysis requests, and example specs are coursesmith concepts and live there, not here.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lmschem-0.1.0.tar.gz.
File metadata
- Download URL: lmschem-0.1.0.tar.gz
- Upload date:
- Size: 224.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
01896eb3c896e92745f18f64d1e72fb7d954a8911a794cfc1aff76b4abf38558
|
|
| MD5 |
628d1cde1137035a13ac0e66181261c4
|
|
| BLAKE2b-256 |
22ca25dc1400d5ea401b57eb728f0859899bf2a6190f8e1606079ab7c9d63e45
|
File details
Details for the file lmschem-0.1.0-py3-none-any.whl.
File metadata
- Download URL: lmschem-0.1.0-py3-none-any.whl
- Upload date:
- Size: 184.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
65ed4fbd2f536aa4013d35815a15bbe6a69d00b64281b760c06b5eb06ba85c2b
|
|
| MD5 |
8edbd401c0adee50510ffa00ceee75ea
|
|
| BLAKE2b-256 |
ecf43890c43c5c73d79c600e265cc769fa712743ce1617ee8eb2c7795b25510e
|