Synthesizer¶
Topology synthesis engine.
Enumerates every valid combination of module variants for a given topology
template and assembles each combination into a
SynthesizedCircuit.
The main entry points are:
synthesize()— high-level API that loads YAML configs and returns a list of circuits.enumerate_circuits()— lower-level iterator over circuits for a single topology, useful when you want to stream results or supply custom configs.
- circuitgenome.synthesizer.synthesizer.build_circuit(topology, variant_map, bias_legs=None)[source]¶
Assemble a single
SynthesizedCircuitfrom variant_map, applying all cross-slot compatibility filters, pruning passes, and the bias-generation construction described inenumerate_circuits().Returns
Noneif variant_map is rejected byis_combination_valid(),is_second_stage_compatible(),is_compensation_compatible(),is_output_type_compatible(),is_load_branch_compatible(),is_cmfb_compatible(), oris_tail_current_compatible().- Parameters:
topology (TopologyTemplate) – The wiring template that defines slots and net connections.
variant_map (dict[str, ModuleVariant]) – One
ModuleVariantper slot, keyed by slot name. Anybias_generationslot’s entry may be omitted – it is constructed from the other slots’ demands (seeconstruct_bias_generation()) and overwrites whatever the caller supplied.bias_legs (BiasLegLibrary | None) – Leg library for the bias construction; the built-in
config/bias_legs.yamlwhen omitted.
- Return type:
SynthesizedCircuit | None
- circuitgenome.synthesizer.synthesizer.enumerate_circuits(topology, modules, config=None)[source]¶
Yield one
SynthesizedCircuitfor every valid combination of module variants in topology.Variants parked with an
unsupported:reason tag in the modules YAML are dropped from every slot’s candidate pool before the product is formed (currentlyinverter_based_input, issue #113, anddifferential_ota_second_stage, issue #114), unlessconfig={"include_unsupported": True}– see theconfigparameter.Variants tagged
bias_infeasible:– functionally-correct wiring whose DC bias does not close on the default (low-voltage) spec class (currentlystacked_cascode_current_mirror_tail_pmos/_nmos, issue #111) – are likewise dropped by default and kept only withconfig={"include_infeasible": True}. Unlikeunsupportedvariants these build into a complete, valid netlist; they are retained for design-space exploration as correct-but-infeasible mutation seeds.Combinations that mix incompatible
polaritytags (seeis_combination_valid()) are skipped – these would leave a shared node with no DC current path.Combinations where a
second_stageslot sensing the first stage’s output has a signal device of the same channel type as the input pair (seeis_second_stage_compatible()) are also skipped – the first stage’s reachable output window and the second stage’s required gate level are disjoint, so no sizing can bias the interface (thethird_stageslot senses a wide-swing node instead and is not constrained).Combinations where a
compensationslot wraps a non-inverting stage chain with gain – a positive even number of common-source inversions between the compensation’sinandoutnets (seeis_compensation_compatible()) – are also skipped: a Miller-family capacitor around such a chain is positive feedback, producing a right-half-plane AC response whose gain/GBW/PM cannot be measured (issue #114). Pure follower chains (zero inversions, no gain) are benign and stay allowed.Combinations where
load’soutput_cardinalitytag (if set) doesn’t match topology’soutput_type(seeis_output_type_compatible()) are also skipped – these would leave the load’s mandatory output port(s) (outfor"single",out1/out2for"differential") unconnected: onlysingle_endedtopologies define a net forload.out, and onlyfully_differentialtopologies definenet_loadout1/net_loadout2forload.out1/out2.Combinations where a
single_endedtopology’s untapped first-stage branch node (load.in1,net_diff1) is left high-impedance by the load – a plain rail-referenced current source with no diode, resistor, or cascode connection to define its DC voltage (seeis_load_branch_compatible()) – are also skipped: the node sits between two series current sources with no mechanism to absorb their mismatch, so no sizing can establish an operating point (issue #112).After each slot’s ports are wired, a net-merge pass (see
compute_alias_net_rename()/apply_net_rename()) collapsesloadports declaredalias_ofanotherloadport (out1/out2on the 6 resistor/active/current-source loads) onto their target port’s net, restoring the shared in/out node those variants’ devices assume.For topologies with a
cmfbslot, combinations whereload’soutput_cardinalityisn’t"differential"(i.e.cmfb.outwould drive nothing) are restricted to the canonicalcmfbvariant (seeis_cmfb_compatible()), and that variant is then pruned to an empty placeholder (seeprune_cmfb()) so it contributes no devices andcmfb.biasis not counted as a needed bias rail.Likewise, combinations where
input_pairdoesn’t reference itstailport (currently onlyinverter_based_input, which is self-biased and would otherwise leavenet_tailfloating) are restricted to the canonicaltail_currentvariant (seeis_tail_current_compatible()), and that variant is then pruned to an empty placeholder (seeprune_tail_current()) so it contributes no devices andtail_current.biasis not counted as a needed bias rail.The
bias_generationslot is not part of the enumeration product: its variant is constructed per combination from what the other slots actually consume on each bias rail (seeconstruct_bias_generation()) – one leg of the matching kind per consumed rail, so flavor mismatches, unused legs, and redundant rail-7 diodes cannot arise.out1..out4feedload’s bias inputs;out5/out6feedsecond_stage/third_stage(shared across_p/_ninstances in fully-differential topologies via the topology’s static wiring);out7feedstail_current(current-mirror / cascode-current-mirror variants only – resistor-tail variants declarebiasasoptionaland need no rail);out8feedstail_current.bias_casc(the cascode tails’ wide-swing cascode-gate level). Each role’s rail is independent of the others, soload/second_stage/third_stage/tail_currentnever share a bias voltage. Anybias_generationentries in modules are ignored.- Parameters:
topology (TopologyTemplate) – The wiring template that defines slots and net connections.
modules (dict[str, list[ModuleVariant]]) – Module variant pool, keyed by category name. Typically the return value of
load_modules().config (dict | None) – Optional per-enumeration filter dictionary. Supported keys:
include_unsupported(bool, defaultFalse) — whenTrue, variants parked with anunsupported:reason tag (currentlyinverter_based_input, issue #113, anddifferential_ota_second_stage, issue #114) are enumerated anyway. Intended for round-trip tests and future un-parking work, not for design runs.include_infeasible(bool, defaultFalse) — whenTrue, variants taggedbias_infeasible:(currently thestacked_cascode_current_mirror_tail_*, issue #111) are enumerated anyway. Intended for design-space exploration, which wants the full set of functionally- correct wirings including ones the default spec class cannot bias; not for acceptance-oriented design runs.
- Raises:
ValueError – If a required module category (other than
bias_generation) has no available variants (after droppingunsupported/bias_infeasible- tagged ones).- Return type:
Example:
from circuitgenome.synthesizer.loader import load_modules, load_topologies from circuitgenome.synthesizer.synthesizer import enumerate_circuits from circuitgenome.synthesizer.netlist import to_flat_spice modules = load_modules() topology = next(t for t in load_topologies() if t.name == "one_stage_opamp") for circuit in enumerate_circuits(topology, modules): print(to_flat_spice(circuit))
- circuitgenome.synthesizer.synthesizer.synthesize(config=None, modules_path=None, topologies_path=None)[source]¶
Generate all op-amp circuits matching the given configuration.
Loads YAML definitions, applies filters, and returns a flat list of
SynthesizedCircuitobjects.- Parameters:
config (dict | None) –
Optional filter dictionary. Supported keys:
topology(str) — exact topology name.stages(int) —1,2, or3.output_type(str) —"single_ended"or"fully_differential".compensation_scheme(str) — for 3-stage topologies,"nested_miller"or"reversed_nested_miller".
modules_path (str | Path | None) – Path to a custom modules YAML file. Uses the built-in definitions when omitted.
topologies_path (str | Path | None) – Path to a custom topologies YAML file. Uses the built-in definitions when omitted.
- Returns:
All synthesized circuits across every matching topology.
- Return type:
Example:
from circuitgenome import synthesize from circuitgenome.synthesizer import to_flat_spice circuits = synthesize({"stages": 2, "output_type": "single_ended"}) print(f"{len(circuits)} circuits generated") print(to_flat_spice(circuits[0]))