Kernel Methods with MerLin

This notebook illustrates how to build photonic feature maps and quantum kernels using MerLin. We cover the quickstart factory, custom circuits, and how to plug the resulting Gram matrices into classical machine-learning pipelines built around the Iris dataset.

Setup

We standardise the Iris features, split train/test partitions, and rely on scikit-learn to train classical models on the precomputed kernel matrices.

[1]:
import numpy as np
import perceval as pcvl
import torch
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC

from merlin.algorithms.kernels import FeatureMap, FidelityKernel, KernelCircuitBuilder

torch.manual_seed(0)
np.random.seed(0)

iris = load_iris()
X = iris.data.astype(np.float32)
y = iris.target

X = StandardScaler().fit_transform(X)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, stratify=y, random_state=12
)

X_train = torch.tensor(X_train)
X_test = torch.tensor(X_test)

Fidelity kernel in a few lines

FidelityKernel.simple constructs a kernel-ready photonic circuit and feature map. It encodes real inputs into a multi-mode Fock space, evaluates their overlaps, and returns a positive-semidefinite Gram matrix.

[2]:
kernel = FidelityKernel.simple(
    input_size=4,
    n_modes=6,
    shots=0,  # exact probabilities
    no_bunching=False,
    dtype=torch.float32,
    device=torch.device("cpu"),
)

K_train = kernel(X_train)
K_test = kernel(X_test, X_train)

print("Train Gram shape:", K_train.shape)
print("Test Gram shape:", K_test.shape)
Train Gram shape: torch.Size([105, 105])
Test Gram shape: torch.Size([45, 105])

Use with scikit-learn

We can train any estimator that accepts precomputed kernels. Below we use an SVM to distinguish the three Iris species from the quantum kernel features.

[3]:
svc = SVC(kernel="precomputed")
svc.fit(K_train.detach().numpy(), y_train)
test_accuracy = svc.score(K_test.detach().numpy(), y_test)
print(f"SVM accuracy (precomputed kernel): {test_accuracy:.3f}")
SVM accuracy (precomputed kernel): 1.000

Custom feature maps and experiments

For full control over the optical layout, detectors, and noise, build a perceval.Circuit and wrap it with FeatureMap. Fidelity kernels then evaluate overlaps between the resulting states.

[4]:
circuit = pcvl.Circuit(4)
circuit.add((0, 1), pcvl.BS())
circuit.add(0, pcvl.PS(pcvl.P("phi0")))
circuit.add(1, pcvl.PS(pcvl.P("phi1")))
circuit.add(2, pcvl.PS(pcvl.P("phi2")))
circuit.add(3, pcvl.PS(pcvl.P("phi3")))
circuit.add((2, 3), pcvl.BS())

experiment = pcvl.Experiment(circuit)
experiment.noise = pcvl.NoiseModel(brightness=0.93)

feature_map = FeatureMap(
    experiment=experiment,
    input_size=4,
    input_parameters="phi",
    dtype=torch.float32,
)

custom_kernel = FidelityKernel(
    feature_map=feature_map,
    input_state=[1, 0, 1, 0],
    no_bunching=True,
)

K_custom = custom_kernel(X_train[:20])
print("Custom kernel Gram shape:", K_custom.shape)

Custom kernel Gram shape: torch.Size([20, 20])

Declarative kernel circuits

KernelCircuitBuilder offers a fluent API to assemble reusable feature maps programmatically.

[5]:
builder = (
    KernelCircuitBuilder()
    .input_size(4)
    .n_modes(6)
    .n_photons(2)
    .angle_encoding(scale=0.7)
    .trainable(enabled=True, prefix="theta")
)

builder_kernel = builder.build_fidelity_kernel(
    input_state=[1, 1, 0, 0, 0, 0],
    shots=512,
    no_bunching=False,
)

K_builder = builder_kernel(X_train[:15])
print("Builder kernel Gram shape:", K_builder.shape)

Builder kernel Gram shape: torch.Size([15, 15])

Conclusion

  • Use FidelityKernel.simple for quick experiments where a default photonic layout suffices.

  • Wrap custom perceval.Experiment objects with FeatureMap to reflect hardware noise and detector choices.

  • KernelCircuitBuilder helps script repeatable feature maps with entangling blocks and measurement strategies.

  • The resulting Gram matrices integrate with classical ML libraries by choosing the precomputed kernel option.