Model Optimization

ParamRF allows you to easily optimize model parameters to meet a given design goal. In this example, we design a simple low-pass filter using SciPy’s L-BFGS-B algorithm.

Defining the Model

Instead of passing unconstrained floats, we can pass Bounded parameters to specify interval constraints on our values:

import pmrf as prf
from pmrf.models import ShuntCapacitor, Inductor

C1 = ShuntCapacitor(C=prf.Bounded(1.0, 100.0, scale=1e-12))
L1 = Inductor(L=prf.Bounded(1.0, 100.0, scale=1e-9))
C2 = ShuntCapacitor(C=prf.Bounded(1.0, 100.0, scale=1e-12))

lpf = C1 ** L1 ** C2

It is best practice to apply a scaling factor to our values in order to keep the optimization numerically stable. To create fixed parameters and apply more complicated constraints, parameters re-exported from pmrf.parameters can be used e.g. pmrf.Fixed() and pmrf.Constrained().

Running the Optimizer

Next, we define our design goals. In this case, we want to ensure good matching (low reflection) across our passband. We can use the Goal evaluator and pass it to the minimize() function alongside our frequency range:

from pmrf.evaluators import Goal
from pmrf.optimize import minimize, ScipyMinimize

match_goal = Goal('s11_db', '<', -20)
passband = prf.Frequency(100, 500, 101, 'MHz')
result = minimize(match_goal, lpf, passband, solver=ScipyMinimize(method='L-BFGS-B'))

The minimize() function returns an OptimizeResult object containing the optimized model and solver metrics. We can extract this newly fitted model to verify our results and print its parameters:

import matplotlib.pyplot as plt
import numpy as np

optimized_lpf = result.model

print("Optimized Parameters:")
print(optimized_lpf.named_params())

plt.plot(passband.f, -20.0 * np.ones_like(passband.f), color='black', linestyle='--', label='target')
lpf.plot_s_db(passband, m=0, n=0, label='initial')
optimized_lpf.plot_s_db(passband, m=0, n=0, label='optimized')
plt.title('Initial vs. Optimized S11')
../_images/model_optimization-3.png

For more complex designs, the minimize() function can accept a list of multiple goals, and you can apply masks to evaluate different features across different frequency bands. Custom loss functions can also be specified in losses.

For even more complicated designs, AbstractEvaluator can be overridden directly.

Note that ParamRF also provides convenience functions for fitting models directly to data in fitting(). See the tutorial for a detailed guide.