Models

Core data models for the subcircuit and functional block recognizers.

All structures are plain dataclasses — they carry no logic and can be freely inspected or passed between pipeline stages. They mirror the 3-layer pipeline described in the design doc (docs/plans/2026_06_15_subcircuit_and_functional_block_recognizer.md):

PatternDevice, PatternDef, and HookMatch describe the SR pattern-library schema used by circuitgenome.recognizer.subcircuit_recognizer.

class circuitgenome.recognizer.models.ParsedNetlist(name, external_ports, devices, internal_nets)[source]

Bases: object

A flat SPICE subcircuit, parsed into devices and net names.

Output of parse() (Layer 0) – the structural inverse of to_flat_spice().

Parameters:
  • name (str) – The .subckt name.

  • external_ports (list[str]) – Ordered list of port names from the .subckt header line.

  • devices (list[Device]) – Every device line in the subcircuit, as Device instances.

  • internal_nets (set[str]) – Net names referenced by devices that are not in external_ports.

name: str
external_ports: list[str]
devices: list[Device]
internal_nets: set[str]
class circuitgenome.recognizer.models.RecognizedStructure(name, category, circuit_block, index, tech_type, pins, devices, children=<factory>)[source]

Bases: object

A single recognized structure – one SR pattern match.

Produced by recognize() (Layer 1). SubcircuitRecognitionResult.structures may contain multiple overlapping RecognizedStructure instances covering the same devices (see SubcircuitRecognitionResult); FBR (Layer 2) is responsible for picking, per topology slot, the one whose pins line up with that slot’s expected connectivity.

Parameters:
  • name (str) – The matching PatternDef’s name, e.g. "differential_pair_nmos". For composite patterns (see PatternDef) this is also the corresponding opamp_modules.yaml variant name.

  • category (str | None) – The matching pattern’s category, e.g. "input_pair" (one of the opamp_modules.yaml module categories). None for level-0 primitives and structural composites that have no FBR counterpart.

  • circuit_block (str | None) – High-level functional block, e.g. "gain_stage_1", "gain_stage_2", "bias", "compensation", or "cmfb". None for primitives and structural composites. Used by group_by_category() to group structures without a topology template.

  • index (int) – 0-based instance number, distinguishing repeated matches of the same pattern (e.g. a second differential_pair_nmos candidate elsewhere in the netlist).

  • tech_type (str | None) – "n", "p", or None – the technology of the pattern’s PatternDef.tech_type_from template device, or None if the pattern doesn’t declare one.

  • pins (dict[str, str]) – Maps pin names (from PatternDef.pins, plus any extra_pins contributed by a HookMatch) to the actual net names they resolve to in this netlist.

  • devices (list[Device]) – The actual Device instances matched by this structure (the base template assignment, plus any extra_devices from a HookMatch).

  • children (list[RecognizedStructure]) – Sub-structures from multi-level composition. Empty for level-0 primitives and MVP composite-only patterns; populated for level-1+ patterns.

name: str
category: str | None
circuit_block: str | None
index: int
tech_type: str | None
pins: dict[str, str]
devices: list[Device]
children: list[RecognizedStructure]
class circuitgenome.recognizer.models.SubcircuitRecognitionResult(structures, unrecognized_devices=<factory>)[source]

Bases: object

Output of Layer 1, the Subcircuit Recognizer (SR).

Parameters:
  • structures (list[RecognizedStructure]) – Every RecognizedStructure matched by any pattern in the library. May contain multiple overlapping candidates for the same device-set – e.g. two patterns with structurally identical templates but different categories/names, or a composite pattern’s reference device that also satisfies a smaller, unrelated pattern. SR reports every candidate; it does not pick a winner. Layer 2 (FBR) resolves overlaps using topology context.

  • unrecognized_devices (list[Device]) – Devices matched by no pattern. For a netlist produced by to_flat_spice() from a known SynthesizedCircuit, this should be empty – a non-empty list indicates a pattern-library gap.

structures: list[RecognizedStructure]
unrecognized_devices: list[Device]
class circuitgenome.recognizer.models.PatternDevice(ref, type)[source]

Bases: object

One template device within a PatternDef.

Parameters:
  • ref (str) – Template-local reference used by PatternDef.same_net and PatternDef.pins to refer to this device, e.g. "m1". Scoped to the enclosing pattern – unrelated to the actual netlist’s device refs.

  • type (str) – The device type this template slot matches: "nmos", "pmos", "resistor", or "capacitor". Matches type.

ref: str
type: str
class circuitgenome.recognizer.models.ChildDef(pattern, devices)[source]

Bases: object

One required child in a multi-level composite pattern.

Parameters:
  • pattern (str) – Name of the expected child PatternDef, e.g. "diode_connected_nmos".

  • devices (list[str]) – Template device refs (from the enclosing PatternDef) that together form this child. E.g. ["m_ref"] for a single-device child or ["m_dr", "m_dl"] for a two-device child.

pattern: str
devices: list[str]
class circuitgenome.recognizer.models.PatternDef(name, category, circuit_block, devices, same_net, pins, tech_type_from=None, hook=None, children=<factory>, exclusive=False, priority=0)[source]

Bases: object

A single SR pattern definition, loaded from config/primitives.yaml, config/structural_patterns.yaml, or config/opamp_patterns.yaml.

A pattern is a small template graph: a handful of typed PatternDevice slots, same_net equality constraints between their terminals, and a pins map exporting named nets. See recognize() for the matching algorithm.

Level-0 primitive patterns describe single-device topological facts (e.g. diode_connected_nmos). Exclusive primitives claim a device for exactly one pattern (higher priority wins).

Level-1+ composite patterns declare children referencing lower-level patterns they compose; the SR verifies those children are present before accepting a match.

MVP composite patterns (no children, not exclusive) correspond 1:1 to an opamp_modules.yaml module variant and run in the same single-pass loop as before.

Parameters:
  • name (str) – Unique pattern name, e.g. "differential_pair_nmos". For composite patterns, also the opamp_modules.yaml variant name – this is the value that ends up in RecognizedStructure.name and SlotAssignment.pattern_name.

  • category (str | None) – The opamp_modules.yaml module category this pattern represents, e.g. "input_pair". None for primitives and structural composites without an FBR counterpart. Copied into RecognizedStructure.category.

  • circuit_block (str | None) – High-level functional block this pattern belongs to, e.g. "gain_stage_1", "gain_stage_2", "bias", "compensation", or "cmfb". Uses gain_stage_N naming (never second_stage) to avoid clashing with category values. None for primitives and structural composites. Copied into RecognizedStructure.circuit_block.

  • devices (list[PatternDevice]) – The pattern’s template devices. The matcher searches for an injective assignment from these to actual netlist devices of matching PatternDevice.type.

  • same_net (list[list[str]]) – Equality constraints on resolved terminals, each written as a list of "template_ref.terminal" strings, e.g. [["m1.s", "m2.s"]] means “m1’s source and m2’s source must be the same net”. These are the only required equalities – terminals not listed are unconstrained, so a more-connected real circuit can still match.

  • pins (dict[str, str]) – Maps an exported pin name to a "template_ref.terminal" string. Resolved through the matched assignment to produce RecognizedStructure.pins.

  • tech_type_from (str | None) – The template device ref (e.g. "m1") whose matched type (first character, "n" or "p") becomes RecognizedStructure.tech_type. None if the pattern declares no dominant technology.

  • hook (str | None) – Optional "module:function" path to an extra-check function (see HookMatch), resolved dynamically by recognize(). Most patterns have no hook.

  • children (list[ChildDef]) – Required sub-structures for multi-level composition. Empty for level-0 patterns and MVP composites. When non-empty, recognize verifies each child is present as a matched structure at the previous level before accepting this pattern’s match.

  • exclusive (bool) – If True, this pattern claims its matched device(s) exclusively in Pass 0 – each device is assigned to at most one exclusive pattern. Only level-0 primitives use this.

  • priority (int) – Tie-breaking priority among exclusive patterns that match the same device; higher wins. E.g. diode_connected_nmos (priority 10) beats nmos (priority 0).

name: str
category: str | None
circuit_block: str | None
devices: list[PatternDevice]
same_net: list[list[str]]
pins: dict[str, str]
tech_type_from: str | None = None
hook: str | None = None
children: list[ChildDef]
exclusive: bool = False
priority: int = 0
class circuitgenome.recognizer.models.HookMatch(extra_devices=<factory>, extra_pins=<factory>)[source]

Bases: object

Accept-and-extend result of a PatternDef.hook function.

A hook is called once per base-template match (recognize()’s (assignment, pins, netlist)) and returns either:

  • None – reject this match entirely (it is dropped, as if the pattern hadn’t matched).

  • a HookMatch – accept the match, merging extra_devices into RecognizedStructure.devices and extra_pins into RecognizedStructure.pins (in addition to the base template’s devices/pins).

The motivating example is diode_connected_mosfet_bias (diode_connected_mosfet_bias_legs()): its base template is just the 1-device diode-connected bias reference, and the hook discovers however many output “legs” (1-7, one per needed bias rail) are actually present in the netlist and appends their devices and legN_out pins.

Parameters:
extra_devices: list[Device]
extra_pins: dict[str, str]
class circuitgenome.recognizer.models.SlotAssignment(slot_name, pattern_name, structure)[source]

Bases: object

One FBR decision: a recognized structure assigned to a topology slot.

Parameters:
slot_name: str
pattern_name: str
structure: RecognizedStructure
class circuitgenome.recognizer.models.FunctionalBlockRecognitionResult(slot_assignments, unassigned_structures=<factory>, unrecognized_devices=<factory>)[source]

Bases: object

Output of Layer 2, the Functional Block Recognizer (FBR).

MVP taxonomy: the opamp_modules.yaml categories plus the matched TopologyTemplate (design doc section 6.2). Produced by assign_slots().

Parameters:
slot_assignments: dict[str, SlotAssignment]
unassigned_structures: list[RecognizedStructure]
unrecognized_devices: list[Device]
class circuitgenome.recognizer.models.CategoryGroupResult(groups, unrecognized_devices=<factory>)[source]

Bases: object

Layer 2 output when no TopologyTemplate is available.

Produced by group_by_category(). Structures are grouped first by circuit_block (e.g. "gain_stage_1", "bias") then by category (e.g. "input_pair", "load"). Within each category list, candidates are ranked by descending external-port adjacency — how many of the structure’s pins connect directly to a subcircuit external port.

Parameters:
groups: dict[str, dict[str, list[RecognizedStructure]]]
unrecognized_devices: list[Device]