{ "cells": [ { "cell_type": "markdown", "id": "7fb27b941602401d91542211134fc71a", "metadata": {}, "source": [ "# Kernel Methods with MerLin\n", "\n", "This notebook illustrates how to build photonic feature maps and quantum kernels using MerLin.\n", "We cover the quickstart factory, custom circuits, and how to plug the resulting Gram matrices\n", "into classical machine-learning pipelines built around the Iris dataset.\n" ] }, { "cell_type": "markdown", "id": "acae54e37e7d407bbb7b55eff062a284", "metadata": {}, "source": [ "## Setup\n", "\n", "We standardise the Iris features, split train/test partitions, and rely on scikit-learn to\n", "train classical models on the precomputed kernel matrices.\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "9a63283cbaf04dbcab1f6479b197f3a8", "metadata": { "ExecuteTime": { "end_time": "2025-11-10T09:12:58.373465300Z", "start_time": "2025-11-10T09:12:53.188283900Z" } }, "outputs": [], "source": [ "import numpy as np\n", "import perceval as pcvl\n", "import torch\n", "from sklearn.datasets import load_iris\n", "from sklearn.model_selection import train_test_split\n", "from sklearn.preprocessing import StandardScaler\n", "from sklearn.svm import SVC\n", "\n", "from merlin.algorithms.kernels import FeatureMap, FidelityKernel, KernelCircuitBuilder\n", "\n", "torch.manual_seed(0)\n", "np.random.seed(0)\n", "\n", "iris = load_iris()\n", "X = iris.data.astype(np.float32)\n", "y = iris.target\n", "\n", "X = StandardScaler().fit_transform(X)\n", "X_train, X_test, y_train, y_test = train_test_split(\n", " X, y, test_size=0.3, stratify=y, random_state=12\n", ")\n", "\n", "X_train = torch.tensor(X_train)\n", "X_test = torch.tensor(X_test)\n" ] }, { "cell_type": "markdown", "id": "8dd0d8092fe74a7c96281538738b07e2", "metadata": {}, "source": [ "## Fidelity kernel in a few lines\n", "\n", "`FidelityKernel.simple` constructs a kernel-ready photonic circuit and feature map.\n", "It encodes real inputs into a multi-mode Fock space, evaluates their overlaps, and\n", "returns a positive-semidefinite Gram matrix.\n" ] }, { "cell_type": "code", "execution_count": 2, "id": "72eea5119410473aa328ad9291626812", "metadata": { "ExecuteTime": { "end_time": "2025-11-10T09:12:58.735162800Z", "start_time": "2025-11-10T09:12:58.376533Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Train Gram shape: torch.Size([105, 105])\n", "Test Gram shape: torch.Size([45, 105])\n" ] } ], "source": [ "kernel = FidelityKernel.simple(\n", " input_size=4,\n", " n_modes=6,\n", " shots=0, # exact probabilities\n", " no_bunching=False,\n", " dtype=torch.float32,\n", " device=torch.device(\"cpu\"),\n", ")\n", "\n", "K_train = kernel(X_train)\n", "K_test = kernel(X_test, X_train)\n", "\n", "print(\"Train Gram shape:\", K_train.shape)\n", "print(\"Test Gram shape:\", K_test.shape)" ] }, { "cell_type": "markdown", "id": "8edb47106e1a46a883d545849b8ab81b", "metadata": {}, "source": [ "### Use with scikit-learn\n", "\n", "We can train any estimator that accepts precomputed kernels. Below we use an SVM\n", "to distinguish the three Iris species from the quantum kernel features.\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "10185d26023b46108eb7d9f57d49d2b3", "metadata": { "ExecuteTime": { "end_time": "2025-11-10T09:12:58.747107500Z", "start_time": "2025-11-10T09:12:58.735162800Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "SVM accuracy (precomputed kernel): 1.000\n" ] } ], "source": [ "svc = SVC(kernel=\"precomputed\")\n", "svc.fit(K_train.detach().numpy(), y_train)\n", "test_accuracy = svc.score(K_test.detach().numpy(), y_test)\n", "print(f\"SVM accuracy (precomputed kernel): {test_accuracy:.3f}\")" ] }, { "cell_type": "markdown", "id": "8763a12b2bbd4a93a75aff182afb95dc", "metadata": {}, "source": [ "## Custom feature maps and experiments\n", "\n", "For full control over the optical layout, detectors, and noise, build a `perceval.Circuit`\n", "and wrap it with `FeatureMap`. Fidelity kernels then evaluate overlaps between the resulting states.\n" ] }, { "cell_type": "code", "execution_count": 4, "id": "7623eae2785240b9bd12b16a66d81610", "metadata": { "ExecuteTime": { "end_time": "2025-11-10T09:12:58.807489700Z", "start_time": "2025-11-10T09:12:58.747107500Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Custom kernel Gram shape: torch.Size([20, 20])\n" ] } ], "source": [ "circuit = pcvl.Circuit(4)\n", "circuit.add((0, 1), pcvl.BS())\n", "circuit.add(0, pcvl.PS(pcvl.P(\"phi0\")))\n", "circuit.add(1, pcvl.PS(pcvl.P(\"phi1\")))\n", "circuit.add(2, pcvl.PS(pcvl.P(\"phi2\")))\n", "circuit.add(3, pcvl.PS(pcvl.P(\"phi3\")))\n", "circuit.add((2, 3), pcvl.BS())\n", "\n", "experiment = pcvl.Experiment(circuit)\n", "experiment.noise = pcvl.NoiseModel(brightness=0.93)\n", "\n", "feature_map = FeatureMap(\n", " experiment=experiment,\n", " input_size=4,\n", " input_parameters=\"phi\",\n", " dtype=torch.float32,\n", ")\n", "\n", "custom_kernel = FidelityKernel(\n", " feature_map=feature_map,\n", " input_state=[1, 0, 1, 0],\n", " no_bunching=True,\n", ")\n", "\n", "K_custom = custom_kernel(X_train[:20])\n", "print(\"Custom kernel Gram shape:\", K_custom.shape)\n" ] }, { "cell_type": "markdown", "id": "7cdc8c89c7104fffa095e18ddfef8986", "metadata": {}, "source": [ "## Declarative kernel circuits\n", "\n", "`KernelCircuitBuilder` offers a fluent API to assemble reusable feature maps programmatically.\n" ] }, { "cell_type": "code", "execution_count": 5, "id": "b118ea5561624da68c537baed56e602f", "metadata": { "ExecuteTime": { "end_time": "2025-11-10T09:12:59.286965200Z", "start_time": "2025-11-10T09:12:58.775409200Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Builder kernel Gram shape: torch.Size([15, 15])\n" ] } ], "source": [ "builder = (\n", " KernelCircuitBuilder()\n", " .input_size(4)\n", " .n_modes(6)\n", " .n_photons(2)\n", " .angle_encoding(scale=0.7)\n", " .trainable(enabled=True, prefix=\"theta\")\n", ")\n", "\n", "builder_kernel = builder.build_fidelity_kernel(\n", " input_state=[1, 1, 0, 0, 0, 0],\n", " shots=512,\n", " no_bunching=False,\n", ")\n", "\n", "K_builder = builder_kernel(X_train[:15])\n", "print(\"Builder kernel Gram shape:\", K_builder.shape)\n" ] }, { "cell_type": "markdown", "id": "938c804e27f84196a10c8828c723f798", "metadata": {}, "source": [ "## Conclusion\n", "\n", "- Use `FidelityKernel.simple` for quick experiments where a default photonic layout suffices.\n", "- Wrap custom `perceval.Experiment` objects with `FeatureMap` to reflect hardware noise and detector choices.\n", "- `KernelCircuitBuilder` helps script repeatable feature maps with entangling blocks and measurement strategies.\n", "- The resulting Gram matrices integrate with classical ML libraries by choosing the `precomputed` kernel option.\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python", "version": "3.12" } }, "nbformat": 4, "nbformat_minor": 5 }