# How to Implement ML Interpolation for Estimating Device Parameters Between Simulated Designs in SQuADDS

> Implement ML interpolation in SQuADDS to estimate device parameters between simulations. Use Interpolator classes to fit surrogate models and predict missing variables, saving time and computation.

- Repository: [Levenson-Falk Lab/squadds](https://github.com/lfl-lab/squadds)
- Tags: how-to-guide
- Published: 2026-03-06

---

**You can estimate device parameters between simulated designs in SQuADDS by using the `Interpolator` abstract class with concrete implementations like `PhysicsInterpolator` or `GPInterpolator`, which fit surrogate models on existing simulation data to predict missing design variables without running new expensive simulations.**

SQuADDS (Superconducting Qubit Automated Design & Discovery System) provides a machine learning framework for interpolating between electromagnetic simulation results. When you need device parameters that fall between existing simulated points, you can leverage **ML interpolation for estimating device parameters between simulated designs in SQuADDS** to avoid costly re-simulation and accelerate your design workflow.

## Understanding the SQuADDS Interpolation Architecture

### The Analyzer Class

The `Analyzer` class, exposed via [`squadds/__init__.py`](https://github.com/lfl-lab/squadds/blob/main/squadds/__init__.py), serves as the data backbone for all interpolation operations. It wraps simulation results into a **pandas DataFrame** (`self.df`) and provides query methods to filter the database. When initializing any interpolator, you must pass an `Analyzer` instance to give the model access to the underlying training data.

### The Interpolator Abstract Base Class

All interpolation logic in SQuADDS inherits from the abstract `Interpolator` class defined in [`squadds/interpolations/interpolator.py`](https://github.com/lfl-lab/squadds/blob/main/squadds/interpolations/interpolator.py). The base constructor (lines 8‑14) stores references to the analyzer and target parameters:

```python
def __init__(self, analyzer, target_params):
    self.analyzer = analyzer
    self.target_params = target_params
    self.df = self.analyzer.df  # Direct access to simulation data

```

Subclasses must implement the `get_design(self)` method, which returns a `pd.DataFrame` containing the predicted device parameters.

### Concrete Interpolator Implementations

SQuADDS provides several concrete implementations tailored to different physics regimes:

- **`PhysicsInterpolator`** ([`squadds/interpolations/physics.py`](https://github.com/lfl-lab/squadds/blob/main/squadds/interpolations/physics.py)): Leverages domain‑specific electromagnetic scaling laws and feature engineering via `apply_physics_rules()` to ensure predictions remain physically plausible (e.g., converting geometry to capacitance using known formulas).
- **`GPInterpolator`** (exposed via [`squadds/interpolations/utils.py`](https://github.com/lfl-lab/squadds/blob/main/squadds/interpolations/utils.py)): Wraps `sklearn.GaussianProcessRegressor` for non‑linear, probabilistic interpolation between design points, making it suitable for sparse datasets.

## Step-by-Step Implementation Guide

### Loading the Simulation Database

First, instantiate the `Analyzer` with your existing simulation results:

```python
from squadds import Analyzer

analyzer = Analyzer.from_database("simulations.db")

```

This loads the electromagnetic simulation data into `analyzer.df`, making it available for model training.

### Defining Target Parameters

Create a dictionary specifying the device specifications you need. These are the values you want to hit through interpolation:

```python
target = {
    "freq_target_GHz": 5.2,
    "zeta_target_MHz": 3.0,
    "junction_cap_fF": 2.5,
}

```

### Selecting and Configuring an Interpolator

Choose the interpolation strategy that best fits your physics. For physics‑aware estimation:

```python
from squadds.interpolations.physics import PhysicsInterpolator

interp = PhysicsInterpolator(analyzer, target)

```

For a purely data‑driven Gaussian process approach:

```python
from squadds.interpolations.utils import GPInterpolator

gp_interp = GPInterpolator(analyzer, target, kernel="RBF")

```

### Generating the Interpolated Design

Call `get_design()` to fit the surrogate model and predict the missing geometric or electrical parameters:

```python
design_df = interp.get_design()
print(design_df)

```

The returned `pd.DataFrame` contains the interpolated device parameters that fall between your simulated design points.

## Code Examples for ML Interpolation

### Physics-Based Interpolation

The `PhysicsInterpolator` leverages domain knowledge from [`squadds/interpolations/physics.py`](https://github.com/lfl-lab/squadds/blob/main/squadds/interpolations/physics.py) to ensure predictions respect electromagnetic scaling laws:

```python
from squadds import Analyzer
from squadds.interpolations.physics import PhysicsInterpolator

# Load simulation database

analyzer = Analyzer.from_database("simulations.db")

# Define target specifications

target = {
    "freq_target_GHz": 5.2,
    "zeta_target_MHz": 3.0,
    "junction_cap_fF": 2.5,
}

# Initialize physics-based interpolator

interp = PhysicsInterpolator(analyzer, target)

# Generate interpolated design

design_df = interp.get_design()
print(design_df)

```

*Source reference:* The abstract `Interpolator` constructor is defined in [[`interpolator.py`](https://github.com/lfl-lab/squadds/blob/main/interpolator.py)](https://github.com/lfl-lab/squadds/blob/master/squadds/interpolations/interpolator.py#L8-L14).

### Gaussian Process Interpolation

For non‑linear interpolation between sparse simulation points, use the `GPInterpolator` exposed via [`squadds/interpolations/utils.py`](https://github.com/lfl-lab/squadds/blob/main/squadds/interpolations/utils.py):

```python
from squadds import Analyzer
from squadds.interpolations.utils import GPInterpolator

analyzer = Analyzer.from_database("simulations.db")
target = {"freq_target_GHz": 4.8, "chi_target_MHz": 1.8}

gp_interp = GPInterpolator(analyzer, target, kernel="RBF")
predicted = gp_interp.get_design()

```

*Source reference:* Helper functions and ML wrappers live in [[`utils.py`](https://github.com/lfl-lab/squadds/blob/main/utils.py)](https://github.com/lfl-lab/squadds/blob/master/squadds/interpolations/utils.py); the GP class inherits from `Interpolator`.

### Integrating with Layout Generation

The `get_design()` method returns a pandas DataFrame where each row represents a complete device design with geometric and electrical parameters. You can pass these parameters directly to layout generators in `squadds/components/` (such as `AirbridgeGenerator` in [`squadds/components/airbridge/airbridge_generator.py`](https://github.com/lfl-lab/squadds/blob/main/squadds/components/airbridge/airbridge_generator.py)) by converting the DataFrame row to a dictionary:

```python
from squadds.components.airbridge.airbridge_generator import AirbridgeGenerator
from squadds.interpolations.physics import PhysicsInterpolator

analyzer = Analyzer.from_database("simulations.db")
target = {"freq_target_GHz": 5.0}
interp = PhysicsInterpolator(analyzer, target)

design = interp.get_design()
airbridge = AirbridgeGenerator(**design.iloc[0].to_dict())
gds = airbridge.generate()
gds.write("interpolated_airbridge.gds")

```

This demonstrates a **seamless hand‑off** from ML interpolation to GDS file generation.

## Key Source Files and Implementation Details

| File | Purpose | Link |
|------|---------|------|
| [`squadds/interpolations/interpolator.py`](https://github.com/lfl-lab/squadds/blob/main/squadds/interpolations/interpolator.py) | Abstract base class that all interpolators derive from. | <https://github.com/lfl-lab/squadds/blob/master/squadds/interpolations/interpolator.py> |
| [`squadds/interpolations/physics.py`](https://github.com/lfl-lab/squadds/blob/main/squadds/interpolations/physics.py) | Physics‑guided interpolator implementation (features domain‑specific transformations). | <https://github.com/lfl-lab/squadds/blob/master/squadds/interpolations/physics.py> |
| [`squadds/interpolations/utils.py`](https://github.com/lfl-lab/squadds/blob/main/squadds/interpolations/utils.py) | Utility functions for preprocessing, scaling, and ML model wrappers. | <https://github.com/lfl-lab/squadds/blob/master/squadds/interpolations/utils.py> |
| [`squadds/__init__.py`](https://github.com/lfl-lab/squadds/blob/main/squadds/__init__.py) (exposes `Analyzer`) | Central object that loads simulation results into a pandas DataFrame. | <https://github.com/lfl-lab/squadds/blob/master/squadds/__init__.py> |
| `squadds/components/...` (e.g., [`airbridge_generator.py`](https://github.com/lfl-lab/squadds/blob/main/airbridge_generator.py)) | Layout generators that accept the interpolated design output. | <https://github.com/lfl-lab/squadds/tree/master/squadds/components> |

These files together form the backbone of ML‑enabled interpolation in SQuADDS, allowing rapid estimation of device parameters without additional costly simulations.

## Summary

- **SQuADDS** stores electromagnetic simulation results in a pandas DataFrame accessible via the `Analyzer` class.
- The **`Interpolator`** abstract base class in [`squadds/interpolations/interpolator.py`](https://github.com/lfl-lab/squadds/blob/main/squadds/interpolations/interpolator.py) defines the interface for all ML interpolation methods, storing `self.analyzer` and `self.target_params`.
- Concrete implementations like **`PhysicsInterpolator`** (physics‑aware) and **`GPInterpolator`** (Gaussian process) fit surrogate models on existing data to predict design variables between simulated points.
- The **`get_design()`** method returns a `pd.DataFrame` that integrates directly with layout generators for seamless GDS creation.
- This architecture eliminates the need for expensive re‑simulation when targeting parameter sets that fall between existing design points.

## Frequently Asked Questions

### What is the difference between PhysicsInterpolator and GPInterpolator in SQuADDS?

The **`PhysicsInterpolator`** leverages domain‑specific electromagnetic scaling laws and feature engineering from [`squadds/interpolations/physics.py`](https://github.com/lfl-lab/squadds/blob/main/squadds/interpolations/physics.py) to ensure predictions remain physically plausible (e.g., converting geometry to capacitance using known formulas). In contrast, the **`GPInterpolator`** uses a pure data‑driven Gaussian Process regressor from `sklearn` to model non‑linear relationships between design parameters without explicit physics constraints, making it more flexible but potentially less constrained by physical laws.

### How does the Interpolator class handle missing or sparse simulation data?

The concrete interpolator implementations rely on preprocessing utilities in [`squadds/interpolations/utils.py`](https://github.com/lfl-lab/squadds/blob/main/squadds/interpolations/utils.py) (specifically `prepare_dataframe()`) to align columns, normalize numeric fields, and drop NaN values before model fitting. For sparse datasets, the **`GPInterpolator`** is particularly robust because Gaussian Process regression naturally handles uncertainty and can interpolate effectively with limited training points, while the **`PhysicsInterpolator`** uses analytical scaling laws to constrain the search space even when simulation data is sparse.

### Can I use custom ML models with the SQuADDS interpolation framework?

Yes, you can extend the abstract **`Interpolator`** class defined in [`squadds/interpolations/interpolator.py`](https://github.com/lfl-lab/squadds/blob/main/squadds/interpolations/interpolator.py) to implement custom ML models. You must override the `__init__(self, analyzer, target_params)` method (which stores `self.analyzer` and `self.target_params`) and implement the `get_design(self)` method to return a `pd.DataFrame` with your predicted parameters. This allows integration of custom regressors (e.g., neural networks, random forests) while maintaining compatibility with the SQuADDS `Analyzer` and layout generation pipeline.

### How do I integrate interpolated designs with physical layout generation in SQuADDS?

The `get_design()` method returns a pandas DataFrame where each row represents a complete device design with geometric and electrical parameters. You can pass these parameters directly to layout generators in `squadds/components/` (such as `AirbridgeGenerator` in [`squadds/components/airbridge/airbridge_generator.py`](https://github.com/lfl-lab/squadds/blob/main/squadds/components/airbridge/airbridge_generator.py)) by converting the DataFrame row to a dictionary using `design.iloc[0].to_dict()`. This creates a seamless workflow from ML interpolation to GDS file generation without manual parameter mapping.