Source code for circuitgenome.synthesizer.models

"""
Core data models for the topology synthesizer.

All structures are plain dataclasses — they carry no logic and can be freely
inspected, serialized, or passed between pipeline stages.
"""
from __future__ import annotations
from dataclasses import dataclass, field


[docs] @dataclass class Device: """A single primitive device (MOSFET, resistor, or capacitor). :param ref: Reference designator within the module (e.g. ``m1``, ``r2``). :param type: Device type: ``nmos``, ``pmos``, ``resistor``, or ``capacitor``. :param terminals: Maps terminal names to local net names. MOSFETs use ``d/g/s/b``; resistors use ``t1/t2``; capacitors use ``p/m``. """ ref: str type: str terminals: dict[str, str]
[docs] @dataclass class PortDef: """A port on a module variant. :param name: Port name (must match the canonical interface for its category). :param role: One of ``input``, ``output``, ``supply``, ``supply_in``, or ``optional``. Optional ports are skipped when not wired in a topology. :param alias_of: If set, this port is electrically the same node as the named port on the same variant (e.g. a non-cascode load's ``out1`` aliases its ``in1``). Used to recover the global net for ports that no device terminal references directly. """ name: str role: str alias_of: str | None = None
[docs] @dataclass class ModuleVariant: """One concrete implementation of a module category. Every variant in the same category exposes the same canonical port signature; only the internal devices differ. :param name: Unique snake_case identifier (e.g. ``differential_pair_pmos``). :param category: Module category (e.g. ``input_pair``, ``load``). :param display_name: Human-readable name shown in listings. :param ports: Ordered list of port definitions. :param devices: Ordered list of primitive devices. :param polarity: Electrical compatibility tag, either ``"pmos_input"``, ``"nmos_input"``, or ``None``. Variants that share a current-flow direction with a given ``input_pair`` polarity declare the matching tag; ``None`` means the variant is compatible with either polarity. Used by :func:`~circuitgenome.synthesizer.compatibility.is_combination_valid` to filter out combinations with no DC current path. :param output_cardinality: ``load``-only tag, either ``"single"``, ``"differential"``, or ``None``. Declares which topology ``output_type`` the variant is structurally usable with: for the cascode loads, which ``output_type`` defines a net for the variant's mandatory output port(s) (``out`` for ``"single"``, ``out1``/``out2`` for ``"differential"``); for ``current_source_load_*``, ``"differential"`` because their ``bias_cmfb``-gated branch devices need the CMFB loop that only ``fully_differential`` topologies wire (issue #112). ``None`` means the variant imposes no constraint and is compatible with either output type. Used by :func:`~circuitgenome.synthesizer.compatibility.output.is_output_type_compatible`. :param unsupported: ``None`` for enumerable variants. A non-``None`` reason string parks the variant: it stays loadable (recognizer patterns and hand-built variant maps keep working) but :func:`~circuitgenome.synthesizer.synthesizer.enumerate_circuits` skips it unless ``config={"include_unsupported": True}``. :param bias_infeasible: ``None`` for variants whose bias/headroom is expected to close on typical (low-voltage) specs. A non-``None`` reason string marks a variant whose wiring is *functionally correct* but whose DC bias is infeasible under normal supply/Vcm headroom (e.g. the stacked-diode cascode tails, which need ``|Vgs|+Vdsat`` of tail compliance — issue #111). Unlike ``unsupported`` the circuit is fully buildable and would size into a complete netlist; it is simply predicted to fail the DC bias gate (the ``"bias_infeasible"`` designer outcome) on the default spec class. :func:`~circuitgenome.synthesizer.synthesizer.enumerate_circuits` skips it unless ``config={"include_infeasible": True}``, which design-space exploration sets to keep these correct-but-infeasible circuits as mutation seeds. """ name: str category: str display_name: str ports: list[PortDef] devices: list[Device] polarity: str | None = None output_cardinality: str | None = None unsupported: str | None = None bias_infeasible: str | None = None
[docs] def port_names(self) -> set[str]: """Return the set of all port names on this variant.""" return {p.name for p in self.ports}
[docs] @dataclass class BiasLegLibrary: """The typed leg templates that demand-driven bias construction assembles. Loaded from ``config/bias_legs.yaml`` by :func:`~circuitgenome.synthesizer.loader.load_bias_legs`; consumed by :func:`~circuitgenome.synthesizer.bias_construction.construct_bias_generation`. Device terminals use the template-local net names ``ibias``, ``pref``, ``out``, ``vdd``, ``gnd`` (see the YAML header for the contract). :param reference: The master reference devices (always emitted). :param pref_branch: Devices deriving the PMOS-side reference gate (emitted only when an instantiated leg references ``pref``). :param legs: Maps each rail *kind* (``gate_vdd``, ``gate_gnd``, ``current_source``, ``current_sink``, ``tunable``) to its leg's device templates. """ reference: list[Device] pref_branch: list[Device] legs: dict[str, list[Device]]
[docs] @dataclass class Slot: """A named placeholder for one module category in a topology template. :param name: Slot identifier used in connection rules (e.g. ``input_pair``). :param category: Module category that fills this slot. """ name: str category: str
[docs] @dataclass class Connection: """Wires one port of a module slot to a global net in the assembled circuit. :param slot: Slot name this connection applies to. :param port: Port name on the module in that slot. :param net: Global net name in the assembled circuit. """ slot: str port: str net: str
[docs] @dataclass class TopologyTemplate: """A wiring blueprint for a complete op-amp topology. Defines which module slots are required and how their ports connect to global nets and to each other. :param name: Unique identifier (e.g. ``two_stage_opamp_single_ended``). :param config: Metadata dict — ``stages`` (int) and ``output_type`` (str). :param external_ports: Ordered list of top-level subcircuit port names. :param slots: All module slots in this topology. :param connections: Complete set of port-to-net wiring rules. """ name: str config: dict external_ports: list[str] slots: list[Slot] connections: list[Connection]
[docs] def slot_connections(self, slot_name: str) -> dict[str, str]: """Return ``{port: global_net}`` for *slot_name*.""" return {c.port: c.net for c in self.connections if c.slot == slot_name}
[docs] @dataclass class SynthesizedCircuit: """A fully instantiated circuit produced by the synthesizer. :param name: Auto-generated name encoding the topology and variant combo. :param topology: Name of the :class:`TopologyTemplate` used. :param variant_map: Maps each slot name to the chosen :class:`ModuleVariant`. :param external_ports: Top-level subcircuit ports (inherited from the template). :param devices: Flat list of ``(global_ref, Device)`` pairs after net substitution. Internal nets are prefixed with the slot name to avoid collisions. """ name: str topology: str variant_map: dict[str, ModuleVariant] external_ports: list[str] devices: list[tuple[str, Device]] = field(default_factory=list)