{ "cells": [ { "cell_type": "markdown", "id": "2f585ef9", "metadata": {}, "source": [ "# First Quantum Layers: Classifying Iris with MerLin\n", "\n", "This notebook walks through three complementary ways to instantiate `QuantumLayer` objects and trains each on the classic Iris classification task." ] }, { "cell_type": "markdown", "id": "134fcc8e", "metadata": {}, "source": [ "We will reuse a common data pipeline and optimisation loop while switching between the following APIs:\n", "\n", "1. `QuantumLayer.simple` quickstart factory.\n", "2. Declarative `CircuitBuilder` pipeline.\n", "3. A fully manual `perceval.Circuit`.\n", "\n", "You can run the cells top-to-bottom to reproduce the reported metrics !" ] }, { "cell_type": "code", "execution_count": 20, "id": "a5016e4d", "metadata": { "ExecuteTime": { "end_time": "2025-11-10T09:13:09.887064600Z", "start_time": "2025-11-10T09:13:09.840373500Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Train size: 112 samples\n", "Test size: 38 samples\n" ] } ], "source": [ "import matplotlib.pyplot as plt\n", "import numpy as np\n", "import perceval as pcvl\n", "import torch\n", "import torch.nn as nn\n", "import torch.nn.functional as F\n", "from sklearn.datasets import load_iris\n", "from sklearn.model_selection import train_test_split\n", "\n", "from merlin import LexGrouping, QuantumLayer\n", "from merlin.builder import CircuitBuilder\n", "\n", "torch.manual_seed(0)\n", "np.random.seed(0)\n", "\n", "iris = load_iris()\n", "X = iris.data.astype(\"float32\")\n", "y = iris.target.astype(\"int64\")\n", "\n", "X_train, X_test, y_train, y_test = train_test_split(\n", " X,\n", " y,\n", " test_size=0.25,\n", " stratify=y,\n", " random_state=42,\n", ")\n", "\n", "X_train = torch.tensor(X_train, dtype=torch.float32)\n", "X_test = torch.tensor(X_test, dtype=torch.float32)\n", "y_train = torch.tensor(y_train, dtype=torch.long)\n", "y_test = torch.tensor(y_test, dtype=torch.long)\n", "\n", "mean = X_train.mean(dim=0, keepdim=True)\n", "std = X_train.std(dim=0, keepdim=True).clamp_min(1e-6)\n", "X_train = (X_train - mean) / std\n", "X_test = (X_test - mean) / std\n", "\n", "print(f\"Train size: {X_train.shape[0]} samples\")\n", "print(f\"Test size: {X_test.shape[0]} samples\")" ] }, { "cell_type": "code", "execution_count": 21, "id": "ea1972d2", "metadata": { "ExecuteTime": { "end_time": "2025-11-10T09:13:09.887064600Z", "start_time": "2025-11-10T09:13:09.864622100Z" } }, "outputs": [], "source": [ "# here is a function to run an experiment : train and evaluate a QuantumLayer\n", "\n", "\n", "def run_experiment(layer: torch.nn.Module, epochs: int = 60, lr: float = 0.05):\n", " optimizer = torch.optim.Adam(layer.parameters(), lr=lr)\n", " losses = []\n", " for _ in range(epochs):\n", " layer.train()\n", " optimizer.zero_grad()\n", " logits = layer(X_train)\n", " loss = F.cross_entropy(logits, y_train)\n", " loss.backward()\n", " optimizer.step()\n", " losses.append(loss.item())\n", "\n", " layer.eval()\n", " with torch.no_grad():\n", " train_preds = layer(X_train).argmax(dim=1)\n", " test_preds = layer(X_test).argmax(dim=1)\n", " train_acc = (train_preds == y_train).float().mean().item()\n", " test_acc = (test_preds == y_test).float().mean().item()\n", " return losses, train_acc, test_acc\n", "\n", "\n", "def describe(name: str, losses, train_acc: float, test_acc: float):\n", " print(name)\n", " print(f\" epochs: {len(losses)}\")\n", " print(f\" final loss: {losses[-1]:.4f}\")\n", " print(f\" train accuracy: {train_acc:.3f}\")\n", " print(f\" test accuracy: {test_acc:.3f}\")" ] }, { "cell_type": "markdown", "id": "a7b72ea1", "metadata": {}, "source": [ "## 1. Quickstart factory: `QuantumLayer.simple`\n", "\n", "The quickstart helper allocates a ready-to-train 10-mode, 5-photon circuit, exposing a configurable number of trainable rotations." ] }, { "cell_type": "code", "execution_count": 22, "id": "8f402df2", "metadata": { "ExecuteTime": { "end_time": "2025-11-10T09:13:15.346136600Z", "start_time": "2025-11-10T09:13:09.866644400Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "QuantumLayer.simple\n", " epochs: 80\n", " final loss: 0.7972\n", " train accuracy: 0.884\n", " test accuracy: 0.842\n", " trainable parameters: 100\n" ] } ], "source": [ "base_simple = QuantumLayer.simple(\n", " input_size=X_train.shape[1],\n", " n_params=100,\n", " dtype=X_train.dtype,\n", ")\n", "\n", "simple_layer = nn.Sequential(\n", " base_simple,\n", " LexGrouping(base_simple.output_size, 3),\n", ")\n", "\n", "losses, train_acc, test_acc = run_experiment(simple_layer, epochs=80, lr=0.01)\n", "trainable = sum(p.numel() for p in simple_layer.parameters() if p.requires_grad)\n", "describe(\"QuantumLayer.simple\", losses, train_acc, test_acc)\n", "print(\n", " f\" trainable parameters: {trainable}\"\n", ") # this will also print the number of trainable parameters in the last Linear layer\n", "\n", "# this circuit does not work well on this dataset, let us try another circuit !" ] }, { "cell_type": "code", "execution_count": 23, "id": "da3a7fad", "metadata": { "ExecuteTime": { "end_time": "2025-11-10T09:13:15.378085500Z", "start_time": "2025-11-10T09:13:15.346136600Z" } }, "outputs": [ { "data": { "text/plain": "", "image/svg+xml": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nCPLX\n\n\nΦ=input1\n\n\nΦ=input2\n\n\nΦ=input3\n\n\nΦ=input4\n\n\n\nCPLX\n\n\n\n\nCPLX\n\n\n\n\nCPLX\n\n\n\n\nCPLX\n\n\n\n\nCPLX\n\n\n\n\n\n\n\n\n\n\nRx\n\n\n\n\n\n\n\n\n\nRx\n\n\n\n\n\n\n\n\n\nRx\n\n\n\n\n\n\n\n\n\nRx\n\n\n\n\n\n\n\n\n\n\nRx\n\n\n\n\n\n\n\n\n\n\nRx\n\n\n\n\n\n\n\n\n\n\nRx\n\n\n\n\n\n\n\n\n\n\nRx\n\n\n\n\n\n\n\n\n\n\nRx\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n" }, "execution_count": 23, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# you can visualize the circuit generated by QuantumLayer.simple\n", "pcvl.pdisplay(base_simple.circuit)" ] }, { "cell_type": "code", "execution_count": 24, "id": "5a04f401", "metadata": { "ExecuteTime": { "end_time": "2025-11-10T09:13:31.430651400Z", "start_time": "2025-11-10T09:13:15.366028900Z" } }, "outputs": [ { "data": { "text/plain": "
", "image/png": "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" }, "metadata": {}, "output_type": "display_data" } ], "source": [ "params = [90, 100, 110]\n", "test_accs, train_accs = [], []\n", "for n_params in params:\n", " base_layer = QuantumLayer.simple(\n", " input_size=X_train.shape[1],\n", " n_params=n_params,\n", " dtype=X_train.dtype,\n", " )\n", " simple_layer = nn.Sequential(\n", " base_layer,\n", " LexGrouping(base_layer.output_size, 3),\n", " )\n", " losses, train_acc, test_acc = run_experiment(simple_layer, epochs=80, lr=0.01)\n", " test_accs.append(test_acc)\n", " train_accs.append(train_acc)\n", "plt.plot(params, train_accs, label=\"train\")\n", "plt.plot(params, test_accs, label=\"test\")\n", "plt.xlabel(\"Number of trainable parameters\")\n", "plt.xticks(ticks=params, labels=[str(p) for p in params])\n", "plt.ylabel(\"Accuracy\")\n", "plt.legend()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "41fd459a", "metadata": {}, "source": [ "## 2. Declarative builder API\n", "\n", "`CircuitBuilder` offers a fluent interface to assemble interferometers, encoders, and trainable blocks before handing the result to `QuantumLayer`." ] }, { "cell_type": "code", "execution_count": 25, "id": "01c9efa8", "metadata": { "ExecuteTime": { "end_time": "2025-11-10T09:13:33.155301100Z", "start_time": "2025-11-10T09:13:31.430651400Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CircuitBuilder pipeline\n", " epochs: 80\n", " final loss: 0.8375\n", " train accuracy: 0.670\n", " test accuracy: 0.605\n", " trainable parameters: 36\n" ] } ], "source": [ "builder = CircuitBuilder(n_modes=6)\n", "builder.add_entangling_layer(trainable=True, name=\"U1\")\n", "builder.add_angle_encoding(modes=list(range(X_train.shape[1])), name=\"input\")\n", "builder.add_rotations(trainable=True, name=\"theta\")\n", "builder.add_superpositions(depth=1)\n", "builder_core = QuantumLayer(\n", " input_size=X_train.shape[1],\n", " builder=builder,\n", " n_photons=3, # equivalent to input_state = [1,1,1,0,0,0]\n", " dtype=X_train.dtype,\n", ")\n", "builder_layer = nn.Sequential(\n", " builder_core,\n", " LexGrouping(builder_core.output_size, 3),\n", ")\n", "losses, train_acc, test_acc = run_experiment(builder_layer, epochs=80, lr=0.05)\n", "trainable = sum(p.numel() for p in builder_layer.parameters() if p.requires_grad)\n", "describe(\"CircuitBuilder pipeline\", losses, train_acc, test_acc)\n", "print(f\" trainable parameters: {trainable}\")" ] }, { "cell_type": "code", "execution_count": 26, "id": "e6a99bbd", "metadata": { "ExecuteTime": { "end_time": "2025-11-10T09:13:33.207579Z", "start_time": "2025-11-10T09:13:33.155301100Z" } }, "outputs": [ { "data": { "text/plain": "", "image/svg+xml": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nCPLX\n\n\nΦ=input1\n\n\nΦ=input2\n\n\nΦ=input3\n\n\nΦ=input4\n\n\nΦ=theta_0\n\n\nΦ=theta_1\n\n\nΦ=theta_2\n\n\nΦ=theta_3\n\n\nΦ=theta_4\n\n\nΦ=theta_5\n\n\n\n\n\n\n\n\n\nRx\n\n\n\n\n\n\n\n\n\n\nRx\n\n\n\n\n\n\n\n\n\n\nRx\n\n\n\n\n\n\n\n\n\n\nRx\n\n\n\n\n\n\n\n\n\n\nRx\n\n\n\n\n\n\n\n\n\n\n0\n1\n2\n3\n4\n5\n0\n1\n2\n3\n4\n5\n" }, "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# you can observe your circuit\n", "pcvl.pdisplay(builder_core.circuit)" ] }, { "cell_type": "markdown", "id": "ba326ccd", "metadata": {}, "source": [ "## 3. Hand-crafted Perceval circuit\n", "\n", "When full control is required, build a `perceval.Circuit` manually and pass it to `QuantumLayer` alongside the parameter prefixes to train and encode." ] }, { "cell_type": "code", "execution_count": 27, "id": "680842d0", "metadata": { "ExecuteTime": { "end_time": "2025-11-10T09:13:35.653789100Z", "start_time": "2025-11-10T09:13:33.175465800Z" } }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Manual Perceval circuit\n", " epochs: 120\n", " final loss: 0.8179\n", " train accuracy: 0.670\n", " test accuracy: 0.605\n", " trainable parameters: 60\n" ] } ], "source": [ "modes = 6\n", "\n", "wl = pcvl.GenericInterferometer(\n", " modes,\n", " lambda i: pcvl.BS()\n", " // pcvl.PS(pcvl.P(f\"theta_li{i}\"))\n", " // pcvl.BS()\n", " // pcvl.PS(pcvl.P(f\"theta_lo{i}\")),\n", " shape=pcvl.InterferometerShape.RECTANGLE,\n", ")\n", "circuit = pcvl.Circuit(modes)\n", "circuit.add(0, wl)\n", "for mode in range(len(iris.feature_names)):\n", " circuit.add(mode, pcvl.PS(pcvl.P(f\"input{mode}\")))\n", "wr = pcvl.GenericInterferometer(\n", " modes,\n", " lambda i: pcvl.BS()\n", " // pcvl.PS(pcvl.P(f\"theta_ri{i}\"))\n", " // pcvl.BS()\n", " // pcvl.PS(pcvl.P(f\"theta_ro{i}\")),\n", " shape=pcvl.InterferometerShape.RECTANGLE,\n", ")\n", "circuit.add(0, wr)\n", "\n", "manual_core = QuantumLayer(\n", " input_size=X_train.shape[1],\n", " circuit=circuit,\n", " input_state=[\n", " 1,\n", " 0,\n", " 1,\n", " 0,\n", " 1,\n", " 0,\n", " ], # here, you can just precise the n_photons -> input_state = [1,1,1,0,0,0]\n", " trainable_parameters=[\"theta\"],\n", " input_parameters=[\"input\"],\n", " dtype=X_train.dtype,\n", ")\n", "\n", "manual_layer = nn.Sequential(\n", " manual_core,\n", " LexGrouping(manual_core.output_size, 3),\n", ")\n", "\n", "losses, train_acc, test_acc = run_experiment(manual_layer, epochs=120, lr=0.05)\n", "trainable = sum(p.numel() for p in manual_layer.parameters() if p.requires_grad)\n", "describe(\"Manual Perceval circuit\", losses, train_acc, test_acc)\n", "print(f\" trainable parameters: {trainable}\")" ] }, { "cell_type": "code", "execution_count": 28, "id": "d43797b2", "metadata": { "ExecuteTime": { "end_time": "2025-11-10T09:13:35.708099500Z", "start_time": "2025-11-10T09:13:35.653789100Z" } }, "outputs": [ { "data": { "text/plain": "", "image/svg+xml": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\nCPLX\n\n\nΦ=input0\n\n\nΦ=input1\n\n\nΦ=input2\n\n\nΦ=input3\n\n\n\n\n\n\n\n\n\nCPLX\n\n\n\n\n\n\n0\n1\n2\n3\n4\n5\n0\n1\n2\n3\n4\n5\n" }, "execution_count": 28, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# you can visualize the circuit\n", "pcvl.pdisplay(manual_core.circuit)" ] } ], "metadata": { "kernelspec": { "display_name": "merlin-venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.7" } }, "nbformat": 4, "nbformat_minor": 5 }