circuit ¤
High-level callable circuit interface.
Classes:
| Name | Description |
|---|---|
Circuit | A compiled, callable circuit with SAX-like parameter API. |
Functions:
| Name | Description |
|---|---|
compile_circuit | Compile a netlist into a callable :class: |
Circuit ¤
Circuit(
solver: CircuitLinearSolver,
groups: dict,
sys_size: int,
port_map: dict[str, int],
rtol: float = 1e-06,
atol: float = 1e-06,
max_steps: int = 100,
_source_netlist: dict | Netlist | None = None,
_source_models: dict | None = None,
_is_pure_sax: bool = False,
)
A compiled, callable circuit with SAX-like parameter API.
Wraps the output of :func:compile_netlist and :func:analyze_circuit into a single callable. Global parameters (e.g. wavelength_nm) are forwarded to every component group that declares them. When array-valued parameters are passed, :meth:__call__ applies jax.vmap over the batch dimension automatically.
Attributes:
| Name | Type | Description |
|---|---|---|
solver | The configured linear solver strategy. | |
groups | Compiled component groups dict. | |
sys_size | Number of scalar unknowns in the real-valued system. | |
port_map | Maps |
Methods:
| Name | Description |
|---|---|
ac | Run a small-signal S-parameter sweep. |
ac_response | Return physical small-signal response to independent source phasors. |
check_sax_compatibility | Return True if this circuit can be represented as a pure SAX S-parameter network. |
dc | Solve the DC operating point for the given parameters. |
dc_checked | Return the DC solution and the solver's explicit convergence flag. |
get_port_field | Extract the (possibly complex) field at a named port. |
hb | Run harmonic balance to find the periodic steady state. |
port | Alias for :meth: |
prepare_analysis | Prepare immutable native registrations without solving the circuit. |
set_simparams | Return an updated circuit with numeric Verilog-A simulator settings. |
sp | Run a small-signal S-parameter sweep. |
sp_wavelength | Characterize declared optical ports with normalized low-power DC waves. |
to_sax_circuit | Convert this circuit into a native SAX model function. |
transient | Run transient (time-domain) analysis. |
with_groups | Return a new Circuit with replaced component groups. |
Source code in circulax/circuit.py
source_models property ¤
source_models: dict | None
The leaf models used to compile this circuit, if available.
source_netlist property ¤
source_netlist: dict | Netlist | None
The original netlist used to compile this circuit, if available.
ac ¤
ac(
*,
ports: str | Sequence[str],
freqs: Array,
z0: float | Array = 50.0,
y_dc: Array | None = None,
holomorphic: bool | str = "auto",
params: dict[str, Any] | None = None,
**param_updates: Any
) -> Array
Run a small-signal S-parameter sweep.
.. deprecated:: 0.2 Use :meth:sp instead. Will be removed in 0.3.
Source code in circulax/circuit.py
ac_response ¤
ac_response(
*,
freqs: Array,
sources: Mapping[str, complex],
y_dc: Array | None = None,
params: dict[str, Any] | None = None,
holomorphic: bool | str = "auto",
**param_updates: Any
) -> dict[str, Any]
Return physical small-signal response to independent source phasors.
sources maps source instance names to complex peak amplitudes in their physical source units. Unspecified sources have zero AC drive; DC bias and schematic waveforms remain unchanged. No matching loads or scattering normalization are added, and no external ports are needed. solution has shape (len(freqs), sys_size) with complex physical unknowns; port_map retains node and state aliases. Parameter batching is available through external JAX vmap, as for scattering analysis.
Non-holomorphic optical response requires a quadrature/sideband result contract and is explicitly unsupported by this interface.
holomorphic can only tighten, never relax, that check. "auto" and True both rely on the component holomorphic flags: a complex-mode circuit with any non-holomorphic component is refused, because the Wirtinger solve would otherwise return silently wrong answers and there is no cheap runtime guard. False declares the response non-holomorphic and refuses every complex-mode circuit. Real circuits ignore the argument. Other values raise ValueError.
Source code in circulax/circuit.py
check_sax_compatibility ¤
check_sax_compatibility() -> bool
Return True if this circuit can be represented as a pure SAX S-parameter network.
A circuit qualifies when every component group is a SAX-wrapped model (:func:sax_component) and none is a source — i.e. a passive photonic (or RF) mesh with no reactive (dQ/dt) terms and no driving term. Such a circuit's DC operating point is provably all-zero, and :meth:to_sax_circuit can extract its S-parameters directly.
Source code in circulax/circuit.py
dc ¤
dc(
y_guess: Array | None = None,
*,
params: dict[str, Any] | None = None,
rtol: float | None = None,
atol: float | None = None,
max_steps: int | None = None,
**param_updates: Any
) -> Array
Solve the DC operating point for the given parameters.
Scalar params produce a single solve returning shape (n,). Array params trigger jax.vmap over the leading dimension, returning shape (batch, n). All array params must share the same leading dimension size.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y_guess | Array | None | Initial guess for the Newton solver. Defaults to zeros. | None |
rtol | float | None | Relative tolerance override. Defaults to value from :func: | None |
atol | float | None | Absolute tolerance override. Defaults to value from :func: | None |
max_steps | int | None | Max Newton iterations override. Defaults to value from :func: | None |
params | dict[str, Any] | None | Optional mapping of parameter updates. Keys without a dot are broadcast to every component group declaring that parameter. Keys like | None |
**param_updates | Any | Convenience form for global parameter values, e.g. | {} |
Returns:
| Type | Description |
|---|---|
Array | Flat solution vector of shape |
Raises:
| Type | Description |
|---|---|
ValueError | If multiple array params have different leading dims. |
Source code in circulax/circuit.py
dc_checked ¤
dc_checked(
y_guess: Array | None = None,
*,
params: dict[str, Any] | None = None,
rtol: float | None = None,
atol: float | None = None,
max_steps: int | None = None,
**param_updates: Any
) -> tuple[Array, Array]
Return the DC solution and the solver's explicit convergence flag.
Scalar parameters return a scalar boolean; array parameters use the same shared native registrations and vmap batching as dc, returning one boolean per leading batch point. Failed estimates can be finite; callers must use this flag rather than infer success from sample values. Both results remain JAX arrays and can be consumed inside jit.
Source code in circulax/circuit.py
get_port_field ¤
Extract the (possibly complex) field at a named port.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
y | Array | Solution array of shape | required |
port | str | Port key as | required |
Returns:
| Type | Description |
|---|---|
Array | Real or complex scalar/array. For complex circuits, reconstructs |
Array | the field from the unrolled block format. |
Source code in circulax/circuit.py
hb ¤
hb(
*,
freq: float,
harmonics: int = 5,
y0: Array | None = None,
params: dict[str, Any] | None = None,
y_flat_init: Array | None = None,
rtol: float | None = None,
atol: float | None = None,
max_steps: int | None = None,
osc_node: int | str | None = None,
amplitude_tries: Array | None = None,
g_leak: float | None = None,
**param_updates: Any
) -> tuple[Array, Array]
Run harmonic balance to find the periodic steady state.
Native OSDI devices without ABI state slots use transient-mode F/Q evaluation. Differentiating through the converged native solve is not yet supported.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
freq | float | Fundamental frequency in Hz. | required |
harmonics | int | Number of harmonics (K = 2*harmonics + 1 time samples). | 5 |
y0 | Array | None | DC operating point. If | None |
params | dict[str, Any] | None | Parameter updates (same format as :meth: | None |
y_flat_init | Array | None | Flat initial waveform, shape | None |
rtol | float | None | Relative tolerance override. | None |
atol | float | None | Absolute tolerance override. | None |
max_steps | int | None | Max Newton iterations override. | None |
osc_node | int | str | None | Port name or state index for oscillator multi-start. | None |
amplitude_tries | Array | None | Amplitudes to try when | None |
g_leak | float | None | Override the solver's regularisation conductance. | None |
**param_updates | Any | Global parameter overrides. | {} |
Returns:
| Type | Description |
|---|---|
Array |
|
Array | Fourier coefficients |
Source code in circulax/circuit.py
port ¤
prepare_analysis ¤
prepare_analysis(
analysis: str, *, ports: Sequence[str] | None = None, z0: float = 1.0
) -> Circuit
Prepare immutable native registrations without solving the circuit.
Return the selected DC, AC or transient registration variant. Builtin circuits return themselves. The original circuit caches required variants so callers may retain it for later execution. DC bias setup is also prepared for AC, transient and HB, without calculating that bias.
Supported names are op, dc, sp_wavelength, ac_response, sp_frequency, tran and hb. Native HB state-history and non-holomorphic optical driven-AC limitations are rejected before simulation execution.
sp_wavelength runs on a separately compiled matched-boundary bench. Pass ports (and z0, default 1.0) to build and cache that bench and its DC registrations now; later :meth:sp_wavelength calls with the same ports and z0 reuse it. The returned circuit is still the selected DC variant of this one.
Source code in circulax/circuit.py
set_simparams ¤
Return an updated circuit with numeric Verilog-A simulator settings.
Accept a mapping, keyword arguments, or both (keywords take precedence). Updates merge into the existing settings of every native descriptor. Native setup and analysis variants are rebuilt; existing registrations are reused for matching settings. The current circuit is unchanged so JIT callables already compiled for it remain valid.
Call outside JIT, then rerun DC, AC or transient on the returned circuit. This updates model $simparam queries, not the host solver's options.
Source code in circulax/circuit.py
sp ¤
sp(
*,
ports: str | Sequence[str],
freqs: Array,
z0: float | Array = 50.0,
y_dc: Array | None = None,
holomorphic: bool | str = "auto",
params: dict[str, Any] | None = None,
**param_updates: Any
) -> Array
Run a small-signal S-parameter sweep.
Linearises at the DC operating point and sweeps over freqs, returning S-parameters for the given ports.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ports | str | Sequence[str] | Port name(s) to probe (e.g. | required |
freqs | Array | Frequency array in Hz. | required |
z0 | float | Array | Reference impedance in ohms. Scalar (uniform) or array of shape | 50.0 |
y_dc | Array | None | DC operating point. If | None |
holomorphic | bool | str | If | 'auto' |
params | dict[str, Any] | None | Parameter updates (same format as :meth: | None |
**param_updates | Any | Global parameter overrides. | {} |
Returns:
| Type | Description |
|---|---|
Array | Complex S-parameter array of shape |
Source code in circulax/circuit.py
sp_wavelength ¤
sp_wavelength(
*,
ports: Sequence[str],
wavelengths: Array,
amplitude_unit: str,
amplitude: float = 0.0001,
params: Mapping[str, Any] | None = None,
z0: float = 1.0,
wl_param: str = "wl"
) -> dict[str, Any]
Characterize declared optical ports with normalized low-power DC waves.
The caller declares optical discipline and field power normalization; z0=1 is the SAX optical reference. Temporary matching and excitation preserve physical DUT sources, loads and electrical bias. Independent input columns and their zero-incident baseline use scan continuation over wavelength. s has shape (wavelength, output_port, input_port).
converged has shape (wavelength, 1 + len(ports)): the baseline and each excitation. Concrete results raise if any solve failed to converge or is not finite, and if a quadrature probe at the first wavelength shows the response is not complex-linear (a non-holomorphic model, or an amplitude outside the linear regime). Under jit these checks are skipped; inspect converged instead. The bench inherits this circuit's g_leak. It is compiled once per (ports, z0) and cached on this circuit; :meth:prepare_analysis can build it ahead.
Source code in circulax/circuit.py
to_sax_circuit ¤
Convert this circuit into a native SAX model function.
Only valid when :meth:check_sax_compatibility is True. Returns a callable model(**params) -> sax.SDict matching the calling convention of :func:sax.circuit's own returned model: scalar params produce one SDict; array-valued params trigger jax.vmap and return an SDict whose values are stacked arrays, e.g. model(wl=jnp.linspace(1500, 1600, 101)).
Raises:
| Type | Description |
|---|---|
ValueError | If this circuit is not all-SAX/source-free, or has no known external ports (e.g. built via :meth: |
Source code in circulax/circuit.py
transient ¤
transient(
*,
t0: float,
t1: float,
dt0: float,
y0: Array | None = None,
saveat: Any = None,
params: dict[str, Any] | None = None,
transient_solver: Any = None,
tstep: float | None = None,
**kwargs: Any
) -> Any
Run transient (time-domain) analysis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
t0 | float | Start time. | required |
t1 | float | End time. | required |
dt0 | float | Initial time step. | required |
y0 | Array | None | Initial state vector. If | None |
tstep | float | None | SPICE source-default time step; defaults to | None |
saveat | Any | Times at which to save the solution. Accepts an array of timestamps or a | None |
params | dict[str, Any] | None | Parameter updates (same format as :meth: | None |
transient_solver | Any | Override the Diffrax solver (default BDF2). | None |
**kwargs | Any | Forwarded to | {} |
Returns:
| Type | Description |
|---|---|
Any | A |
Source code in circulax/circuit.py
with_groups ¤
Return a new Circuit with replaced component groups.
Use together with :func:~circulax.utils.update_params_dict for instance-specific parameter changes before solving.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
groups | dict | New groups dict. | required |
Returns:
| Type | Description |
|---|---|
Circuit | A new :class: |
Source code in circulax/circuit.py
compile_circuit ¤
compile_circuit(
net_dict: dict | Netlist,
models_map: dict,
*,
backend: str = "default",
is_complex: bool | str = "auto",
g_leak: float = 1e-09,
rtol: float = 1e-06,
atol: float = 1e-06,
max_steps: int = 100,
params_map: dict[str, dict[str, str]] | None = None,
simparams: Mapping[str, float] | None = None
) -> Circuit
Compile a netlist into a callable :class:Circuit.
@tags circulax-simulation
Accepts a kfnetlist.Netlist, a SAX-format dict, or a RecursiveNetlist (dict[str, Netlist]). When a recursive netlist is given, subcircuit instances are flattened before compilation.
A compiled :class:Circuit may also appear as a value in models_map; its stored source netlist is inlined as a subcircuit automatically. A netlist_io.LibraryModel binds instance settings to card parameters and leaf topology before the same kfnetlist flattening step.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
net_dict | dict | Netlist | Netlist (kfnetlist.Netlist, SAX-format dict, or RecursiveNetlist). | required |
models_map | dict | Mapping from component type name strings to component classes, SAX model functions, or compiled :class: | required |
backend | str | Linear solver backend ( | 'default' |
is_complex | bool | str | If | 'auto' |
g_leak | float | Leakage conductance for regularisation. | 1e-09 |
rtol | float | Relative tolerance for the Newton solver. | 1e-06 |
atol | float | Absolute tolerance for the Newton solver. | 1e-06 |
max_steps | int | Max Newton iterations. | 100 |
simparams | Mapping[str, float] | None | Numeric settings queried by native Verilog-A $simparam calls. Applied to every OSDI descriptor, overriding its simulator defaults; retained across DC/AC/transient and device parameter updates. These are static model settings, separate from solver tolerances and device parameters. Use Circuit.set_simparams to update them. | None |
params_map | dict[str, dict[str, str]] | None | Optional mapping from component type names to dicts that rename netlist setting keys to model field names, e.g. | None |
Returns:
| Name | Type | Description |
|---|---|---|
A | Circuit | class: |
Source code in circulax/circuit.py
968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 | |