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.