Optimization Workflow¶
Optimization sits at the end of the Monata workflow. It should reuse the same circuit authoring, simulation, and measurement functions you already use for manual design checks.
design variables -> simulation task -> metric/spec evaluation -> optimizer
When to Optimize¶
Use optimization after you can already run a deterministic simulation and extract stable metrics. If a single simulation is not understood, an optimizer will only make failures harder to diagnose.
Design Variables¶
Design variables describe tunable parameters such as transistor width, load capacitance, bias current, or compensation capacitance.
from monata.optim import DesignVariable
variables = [
DesignVariable("w_p", min=0.5e-6, max=8e-6),
DesignVariable("w_n", min=0.5e-6, max=4e-6),
]
Objectives and Constraints¶
Objectives and constraints should be built from the same metric functions used in measurement specs.
from monata.optim import Objective, Constraint
objectives = [
Objective("delay", direction="minimize"),
]
constraints = [
Constraint("voh", min=1.1),
]
Choosing an Optimizer¶
Optimizer |
Best for |
|---|---|
Bayesian optimization |
Expensive simulations with a small number of variables |
NSGA-II |
Multi-objective tradeoffs and broader design-space exploration |
Circuit Optimization Loop¶
The optimizer should not know about backend details. It should receive a callable that applies parameter values, runs a simulation, and returns metrics.
def evaluate(params):
task = SimTask(
circuit=circuit,
analysis_spec=tran,
param_overrides=params,
simulator="ngspice-subprocess",
)
result = executor.submit(task).result()
if result.status != "ok":
raise RuntimeError(result.error_message)
return {
"delay": propagation_delay(result),
"voh": max(result.waveforms["out"]),
}
Keep this loop deterministic before enabling parallel execution or larger search spaces.