[404218]: / Code / All PennyLane QML Demos / 22 Quantum GANs 0.64 Loss kkawchak.ipynb

Download this file

991 lines (990 with data), 95.3 kB

{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": 14,
      "metadata": {
        "id": "Adv9qCFis2V1",
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 0
        },
        "outputId": "6031da25-97f1-4790-f416-e3e2d3225184"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Time in seconds since beginning of run: 1693264730.4222255\n",
            "Mon Aug 28 23:18:50 2023\n"
          ]
        }
      ],
      "source": [
        "# This cell is added by sphinx-gallery\n",
        "# It can be customized to whatever you like\n",
        "%matplotlib inline\n",
        "# !pip install pennylane\n",
        "# from google.colab import drive\n",
        "# drive.mount('/content/drive')\n",
        "import time\n",
        "seconds = time.time()\n",
        "print(\"Time in seconds since beginning of run:\", seconds)\n",
        "local_time = time.ctime(seconds)\n",
        "print(local_time)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "UK7teNKZs2V2"
      },
      "source": [
        "Quantum GANs {#quantum_gans}\n",
        "============\n",
        "\n",
        "::: {.meta}\n",
        ":property=\\\"og:description\\\": Explore quantum GANs to generate\n",
        "hand-written digits of zero :property=\\\"og:image\\\":\n",
        "<https://pennylane.ai/qml/_images/patch.jpeg>\n",
        ":::\n",
        "\n",
        "::: {.related}\n",
        "tutorial\\_QGAN Quantum generative adversarial networks with Cirq +\n",
        "TensorFlow\n",
        ":::\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "xjulm5j1s2V3"
      },
      "source": [
        "*Author: James Ellis --- Posted: 01 February 2022. Last updated: 27\n",
        "January 2022.*\n",
        "\n",
        "In this tutorial, we will explore quantum GANs to generate hand-written\n",
        "digits of zero. We will first cover the theory of the classical case,\n",
        "then extend to a quantum method recently proposed in the literature. If\n",
        "you have no experience with GANs, particularly in PyTorch, you might\n",
        "find [PyTorch\\'s\n",
        "tutorial](https://pytorch.org/tutorials/beginner/dcgan_faces_tutorial.html)\n",
        "useful since it serves as the foundation for what is to follow.\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "lEfsJh_fs2V3"
      },
      "source": [
        "Generative Adversarial Networks (GANs)\n",
        "======================================\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "EByLMGeNs2V3"
      },
      "source": [
        "The goal of generative adversarial networks (GANs) is to generate data\n",
        "that resembles the original data used in training. To achieve this, we\n",
        "train two neural networks simulatenously: a generator and a\n",
        "discriminator. The job of the generator is to create fake data which\n",
        "imitates the real training dataset. On the otherhand, the discriminator\n",
        "acts like a detective trying to discern real from fake data. During the\n",
        "training process, both players iteratively improve with one another. By\n",
        "the end, the generator should hopefully generate new data very similar\n",
        "to the training dataset.\n",
        "\n",
        "Specifically, the training dataset represents samples drawn from some\n",
        "unknown data distribution $P_{data}$, and the generator has the job of\n",
        "trying to capture this distribution. The generator, $G$, starts from\n",
        "some initial latent distribution, $P_z$, and maps it to $P_g = G(P_z)$.\n",
        "The best solution would be for $P_g = P_{data}$. However, this point is\n",
        "rarely achieved in practice apart from in the most simple tasks.\n",
        "\n",
        "Both the discriminator, $D$, and generator, $G$, play in a 2-player\n",
        "minimax game. The discriminator tries to maximise the probability of\n",
        "discerning real from fake data, while the generator tries to minimise\n",
        "the same probability. The value function for the game is summarised by,\n",
        "\n",
        "$$\\begin{aligned}\n",
        "\\begin{align}\n",
        "\\min_G \\max_D V(D,G) &= \\mathbb{E}_{\\boldsymbol{x}\\sim p_{data}}[\\log D(\\boldsymbol{x})] \\\\\n",
        "    & ~~ + \\mathbb{E}_{\\boldsymbol{z}\\sim p_{\\boldsymbol{z}}}[\\log(1 - D(G(\\boldsymbol{z}))]\n",
        "\\end{align}\n",
        "\\end{aligned}$$\n",
        "\n",
        "-   $\\boldsymbol{x}$: real data sample\n",
        "-   $\\boldsymbol{z}$: latent vector\n",
        "-   $D(\\boldsymbol{x})$: probability of the discriminator classifying\n",
        "    real data as real\n",
        "-   $G(\\boldsymbol{z})$: fake data\n",
        "-   $D(G(\\boldsymbol{z}))$: probability of discriminator classifying\n",
        "    fake data as real\n",
        "\n",
        "In practice, the two networks are trained iteratively, each with a\n",
        "separate loss function to be minimised,\n",
        "\n",
        "$$L_D = -[y \\cdot \\log(D(x)) + (1-y)\\cdot \\log(1-D(G(z)))]$$\n",
        "\n",
        "$$L_G = [(1-y) \\cdot \\log(1-D(G(z)))]$$\n",
        "\n",
        "where $y$ is a binary label for real ($y=1$) or fake ($y=0$) data. In\n",
        "practice, generator training is shown to be more stable when made to\n",
        "maximise $\\log(D(G(z)))$ instead of minimising $\\log(1-D(G(z)))$. Hence,\n",
        "the generator loss function to be minimised becomes,\n",
        "\n",
        "$$L_G = -[(1-y) \\cdot \\log(D(G(z)))]$$\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "EZP6T-7Is2V3"
      },
      "source": [
        "Quantum GANs: The Patch Method\n",
        "==============================\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PonC35zos2V3"
      },
      "source": [
        "In this tutorial, we re-create one of the quantum GAN methods presented\n",
        "by Huang et al.: the patch method. This method uses several quantum\n",
        "generators, with each sub-generator, $G^{(i)}$, responsible for\n",
        "constructing a small patch of the final image. The final image is\n",
        "contructed by concatenting all of the patches together as shown below.\n",
        "\n",
        "![](../demonstrations/quantum_gans/patch.jpeg){.align-center\n",
        "width=\"90.0%\"}\n",
        "\n",
        "The main advantage of this method is that it is particulary suited to\n",
        "situations where the number of available qubits are limited. The same\n",
        "quantum device can be used for each sub-generator in an iterative\n",
        "fashion, or execution of the generators can be parallelised across\n",
        "multiple devices.\n",
        "\n",
        "::: {.note}\n",
        "::: {.title}\n",
        "Note\n",
        ":::\n",
        "\n",
        "In this tutorial, parenthesised superscripts are used to denote\n",
        "individual objects as part of a collection.\n",
        ":::\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FoPuIvwJs2V3"
      },
      "source": [
        "Module Imports\n",
        "==============\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "metadata": {
        "id": "3HyrRTKws2V3"
      },
      "outputs": [],
      "source": [
        "# Library imports\n",
        "import math\n",
        "import random\n",
        "import numpy as np\n",
        "import pandas as pd\n",
        "import matplotlib.pyplot as plt\n",
        "import matplotlib.gridspec as gridspec\n",
        "import pennylane as qml\n",
        "\n",
        "# Pytorch imports\n",
        "import torch\n",
        "import torch.nn as nn\n",
        "import torch.optim as optim\n",
        "import torchvision\n",
        "import torchvision.transforms as transforms\n",
        "from torch.utils.data import Dataset, DataLoader\n",
        "\n",
        "# Set the random seed for reproducibility\n",
        "seed = 42\n",
        "torch.manual_seed(seed)\n",
        "np.random.seed(seed)\n",
        "random.seed(seed)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "PlFxROwFs2V3"
      },
      "source": [
        "Data\n",
        "====\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "0Xc1K-WFs2V4"
      },
      "source": [
        "As mentioned in the introduction, we will use a [small\n",
        "dataset](https://archive.ics.uci.edu/ml/datasets/optical+recognition+of+handwritten+digits)\n",
        "of handwritten zeros. First, we need to create a custom dataloader for\n",
        "this dataset.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "metadata": {
        "id": "bS-UnbQ8s2V4"
      },
      "outputs": [],
      "source": [
        "class DigitsDataset(Dataset):\n",
        "    \"\"\"Pytorch dataloader for the Optical Recognition of Handwritten Digits Data Set\"\"\"\n",
        "\n",
        "    def __init__(self, csv_file, label=0, transform=None):\n",
        "        \"\"\"\n",
        "        Args:\n",
        "            csv_file (string): Path to the csv file with annotations.\n",
        "            root_dir (string): Directory with all the images.\n",
        "            transform (callable, optional): Optional transform to be applied\n",
        "                on a sample.\n",
        "        \"\"\"\n",
        "        self.csv_file = csv_file\n",
        "        self.transform = transform\n",
        "        self.df = self.filter_by_label(label)\n",
        "\n",
        "    def filter_by_label(self, label):\n",
        "        # Use pandas to return a dataframe of only zeros\n",
        "        df = pd.read_csv(self.csv_file)\n",
        "        df = df.loc[df.iloc[:, -1] == label]\n",
        "        return df\n",
        "\n",
        "    def __len__(self):\n",
        "        return len(self.df)\n",
        "\n",
        "    def __getitem__(self, idx):\n",
        "        if torch.is_tensor(idx):\n",
        "            idx = idx.tolist()\n",
        "\n",
        "        image = self.df.iloc[idx, :-1] / 16\n",
        "        image = np.array(image)\n",
        "        image = image.astype(np.float32).reshape(8, 8)\n",
        "\n",
        "        if self.transform:\n",
        "            image = self.transform(image)\n",
        "\n",
        "        # Return image and label\n",
        "        return image, 0"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "za7LyZnNs2V4"
      },
      "source": [
        "Next we define some variables and create the dataloader instance.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 17,
      "metadata": {
        "id": "FejShZNxs2V4"
      },
      "outputs": [],
      "source": [
        "image_size = 8  # Height / width of the square images\n",
        "batch_size = 1\n",
        "\n",
        "transform = transforms.Compose([transforms.ToTensor()])\n",
        "dataset = DigitsDataset(csv_file=\"/content/drive/MyDrive/Colab Notebooks/data/optdigits.tra\", transform=transform)\n",
        "dataloader = torch.utils.data.DataLoader(\n",
        "    dataset, batch_size=batch_size, shuffle=True, drop_last=True\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "FEFii_G2s2V4"
      },
      "source": [
        "Let\\'s visualize some of the data.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 18,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 102
        },
        "id": "dzGFZQyRs2V4",
        "outputId": "f0e8aea0-63b3-4ede-ba57-1b5bebd60051"
      },
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 800x200 with 8 Axes>"
            ],
            "image/png": "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\n"
          },
          "metadata": {}
        }
      ],
      "source": [
        "plt.figure(figsize=(8,2))\n",
        "\n",
        "for i in range(8):\n",
        "    image = dataset[i][0].reshape(image_size,image_size)\n",
        "    plt.subplot(1,8,i+1)\n",
        "    plt.axis('off')\n",
        "    plt.imshow(image.numpy(), cmap='gray')\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "-NemlIWxs2V4"
      },
      "source": [
        "Implementing the Discriminator\n",
        "==============================\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "IOcvPprks2V4"
      },
      "source": [
        "For the discriminator, we use a fully connected neural network with two\n",
        "hidden layers. A single output is sufficient to represent the\n",
        "probability of an input being classified as real.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 19,
      "metadata": {
        "id": "hPJvAU4Ts2V4"
      },
      "outputs": [],
      "source": [
        "class Discriminator(nn.Module):\n",
        "    \"\"\"Fully connected classical discriminator\"\"\"\n",
        "\n",
        "    def __init__(self):\n",
        "        super().__init__()\n",
        "\n",
        "        self.model = nn.Sequential(\n",
        "            # Inputs to first hidden layer (num_input_features -> 64)\n",
        "            nn.Linear(image_size * image_size, 64),\n",
        "            nn.ReLU(),\n",
        "            # First hidden layer (64 -> 16)\n",
        "            nn.Linear(64, 16),\n",
        "            nn.ReLU(),\n",
        "            # Second hidden layer (16 -> output)\n",
        "            nn.Linear(16, 1),\n",
        "            nn.Sigmoid(),\n",
        "        )\n",
        "\n",
        "    def forward(self, x):\n",
        "        return self.model(x)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "1s1-1Hcus2V4"
      },
      "source": [
        "Implementing the Generator\n",
        "==========================\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "V1rbipYps2V4"
      },
      "source": [
        "Each sub-generator, $G^{(i)}$, shares the same circuit architecture as\n",
        "shown below. The overall quantum generator consists of $N_G$\n",
        "sub-generators, each consisting of $N$ qubits. The process from latent\n",
        "vector input to image output can be split into four distinct sections:\n",
        "state embedding, parameterisation, non-linear transformation, and\n",
        "post-processing. Each of the following sections below refer to a single\n",
        "iteration of the training process to simplify the discussion.\n",
        "\n",
        "![](../demonstrations/quantum_gans/qcircuit.jpeg){.align-center\n",
        "width=\"90.0%\"}\n",
        "\n",
        "**1) State Embedding**\n",
        "\n",
        "A latent vector, $\\boldsymbol{z}\\in\\mathbb{R}^N$, is sampled from a\n",
        "uniform distribution in the interval $[0,\\pi/2)$. All sub-generators\n",
        "receive the same latent vector which is then embedded using RY gates.\n",
        "\n",
        "**2) Parameterised Layers**\n",
        "\n",
        "The parameterised layer consists of parameterised RY gates followed by\n",
        "control Z gates. This layer is repeated $D$ times in total.\n",
        "\n",
        "**3) Non-Linear Transform**\n",
        "\n",
        "Quantum gates in the circuit model are unitary which, by definition,\n",
        "linearly transform the quantum state. A linear mapping between the\n",
        "latent and generator distribution would suffice for only the most simple\n",
        "generative tasks, hence we need non-linear transformations. We will use\n",
        "ancillary qubits to help.\n",
        "\n",
        "For a given sub-generator, the pre-measurement quantum state is given\n",
        "by,\n",
        "\n",
        "$$|\\Psi(z)\\rangle = U_{G}(\\theta)|\\boldsymbol{z}\\rangle$$\n",
        "\n",
        "where $U_{G}(\\theta)$ represents the overall unitary of the\n",
        "parameterised layers. Let us inspect the state when we take a partial\n",
        "measurment, $\\Pi$, and trace out the ancillary subsystem, $\\mathcal{A}$,\n",
        "\n",
        "$$\\rho(\\boldsymbol{z}) = \\frac{\\text{Tr}_{\\mathcal{A}}(\\Pi \\otimes \\mathbb{I} |\\Psi(z)\\rangle \\langle \\Psi(\\boldsymbol{z})|) }{\\text{Tr}(\\Pi \\otimes \\mathbb{I} |\\Psi(\\boldsymbol{z})\\rangle \\langle \\Psi(\\boldsymbol{z})|))} = \\frac{\\text{Tr}_{\\mathcal{A}}(\\Pi \\otimes \\mathbb{I} |\\Psi(\\boldsymbol{z})\\rangle \\langle \\Psi(\\boldsymbol{z})|) }{\\langle \\Psi(\\boldsymbol{z})| \\Pi \\otimes \\mathbb{I} |\\Psi(\\boldsymbol{z})\\rangle}$$\n",
        "\n",
        "The post-measurement state, $\\rho(\\boldsymbol{z})$, is dependent on\n",
        "$\\boldsymbol{z}$ in both the numerator and denominator. This means the\n",
        "state has been non-linearly transformed! For this tutorial,\n",
        "$\\Pi = (|0\\rangle \\langle0|)^{\\otimes N_A}$, where $N_A$ is the number\n",
        "of ancillary qubits in the system.\n",
        "\n",
        "With the remaining data qubits, we measure the probability of\n",
        "$\\rho(\\boldsymbol{z})$ in each computational basis state, $P(j)$, to\n",
        "obtain the sub-generator output, $\\boldsymbol{g}^{(i)}$,\n",
        "\n",
        "$$\\boldsymbol{g}^{(i)} = [P(0), P(1), ... ,P(2^{N-N_A} - 1)]$$\n",
        "\n",
        "**4) Post Processing**\n",
        "\n",
        "Due to the normalisation constraint of the measurment, all elements in\n",
        "$\\boldsymbol{g}^{(i)}$ must sum to one. This is a problem if we are to\n",
        "use $\\boldsymbol{g}^{(i)}$ as the pixel intensity values for our patch.\n",
        "For example, imagine a hypothetical situation where a patch of full\n",
        "intensity pixels was the target. The best patch a sub-generator could\n",
        "produce would be a patch of pixels all at a magnitude of\n",
        "$\\frac{1}{2^{N-N_A}}$. To alleviate this constraint, we apply a\n",
        "post-processing technique to each patch,\n",
        "\n",
        "$$\\boldsymbol{\\tilde{x}^{(i)}} = \\frac{\\boldsymbol{g}^{(i)}}{\\max_{k}\\boldsymbol{g}_k^{(i)}}$$\n",
        "\n",
        "Therefore, the final image, $\\boldsymbol{\\tilde{x}}$, is given by\n",
        "\n",
        "$$\\boldsymbol{\\tilde{x}} = [\\boldsymbol{\\tilde{x}^{(1)}}, ... ,\\boldsymbol{\\tilde{x}^{(N_G)}}]$$\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 20,
      "metadata": {
        "id": "ByOocBfSs2V5"
      },
      "outputs": [],
      "source": [
        "# Quantum variables\n",
        "n_qubits = 5  # Total number of qubits / N\n",
        "n_a_qubits = 1  # Number of ancillary qubits / N_A\n",
        "q_depth = 10  # Depth of the parameterised quantum circuit / D\n",
        "n_generators = 4  # Number of subgenerators for the patch method / N_G"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Ow6pzPrJs2V5"
      },
      "source": [
        "Now we define the quantum device we want to use, along with any\n",
        "available CUDA GPUs (if available).\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 21,
      "metadata": {
        "id": "hve9vkDEs2V5"
      },
      "outputs": [],
      "source": [
        "# Quantum simulator\n",
        "dev = qml.device(\"lightning.qubit\", wires=n_qubits)\n",
        "# Enable CUDA device if available\n",
        "device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Ish10FIUs2V5"
      },
      "source": [
        "Next, we define the quantum circuit and measurement process described\n",
        "above.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 22,
      "metadata": {
        "id": "LNy_ET6ns2V5"
      },
      "outputs": [],
      "source": [
        "@qml.qnode(dev, interface=\"torch\", diff_method=\"parameter-shift\")\n",
        "def quantum_circuit(noise, weights):\n",
        "\n",
        "    weights = weights.reshape(q_depth, n_qubits)\n",
        "\n",
        "    # Initialise latent vectors\n",
        "    for i in range(n_qubits):\n",
        "        qml.RY(noise[i], wires=i)\n",
        "\n",
        "    # Repeated layer\n",
        "    for i in range(q_depth):\n",
        "        # Parameterised layer\n",
        "        for y in range(n_qubits):\n",
        "            qml.RY(weights[i][y], wires=y)\n",
        "\n",
        "        # Control Z gates\n",
        "        for y in range(n_qubits - 1):\n",
        "            qml.CZ(wires=[y, y + 1])\n",
        "\n",
        "    return qml.probs(wires=list(range(n_qubits)))\n",
        "\n",
        "\n",
        "# For further info on how the non-linear transform is implemented in Pennylane\n",
        "# https://discuss.pennylane.ai/t/ancillary-subsystem-measurement-then-trace-out/1532\n",
        "def partial_measure(noise, weights):\n",
        "    # Non-linear Transform\n",
        "    probs = quantum_circuit(noise, weights)\n",
        "    probsgiven0 = probs[: (2 ** (n_qubits - n_a_qubits))]\n",
        "    probsgiven0 /= torch.sum(probs)\n",
        "\n",
        "    # Post-Processing\n",
        "    probsgiven = probsgiven0 / torch.max(probsgiven0)\n",
        "    return probsgiven"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "OG5gYWSvs2V5"
      },
      "source": [
        "Now we create a quantum generator class to use during training.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 23,
      "metadata": {
        "id": "n6DbP7d7s2V5"
      },
      "outputs": [],
      "source": [
        "class PatchQuantumGenerator(nn.Module):\n",
        "    \"\"\"Quantum generator class for the patch method\"\"\"\n",
        "\n",
        "    def __init__(self, n_generators, q_delta=1):\n",
        "        \"\"\"\n",
        "        Args:\n",
        "            n_generators (int): Number of sub-generators to be used in the patch method.\n",
        "            q_delta (float, optional): Spread of the random distribution for parameter initialisation.\n",
        "        \"\"\"\n",
        "\n",
        "        super().__init__()\n",
        "\n",
        "        self.q_params = nn.ParameterList(\n",
        "            [\n",
        "                nn.Parameter(q_delta * torch.rand(q_depth * n_qubits), requires_grad=True)\n",
        "                for _ in range(n_generators)\n",
        "            ]\n",
        "        )\n",
        "        self.n_generators = n_generators\n",
        "\n",
        "    def forward(self, x):\n",
        "        # Size of each sub-generator output\n",
        "        patch_size = 2 ** (n_qubits - n_a_qubits)\n",
        "\n",
        "        # Create a Tensor to 'catch' a batch of images from the for loop. x.size(0) is the batch size.\n",
        "        images = torch.Tensor(x.size(0), 0).to(device)\n",
        "\n",
        "        # Iterate over all sub-generators\n",
        "        for params in self.q_params:\n",
        "\n",
        "            # Create a Tensor to 'catch' a batch of the patches from a single sub-generator\n",
        "            patches = torch.Tensor(0, patch_size).to(device)\n",
        "            for elem in x:\n",
        "                q_out = partial_measure(elem, params).float().unsqueeze(0)\n",
        "                patches = torch.cat((patches, q_out))\n",
        "\n",
        "            # Each batch of patches is concatenated with each other to create a batch of images\n",
        "            images = torch.cat((images, patches), 1)\n",
        "\n",
        "        return images"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "hdzKXAD5s2V5"
      },
      "source": [
        "Training\n",
        "========\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BJbPX_TSs2V5"
      },
      "source": [
        "Let\\'s define learning rates and number of iterations for the training\n",
        "process.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 24,
      "metadata": {
        "id": "ROVGLoQ0s2V5"
      },
      "outputs": [],
      "source": [
        "lrG = 0.3  # Learning rate for the generator\n",
        "lrD = 0.01  # Learning rate for the discriminator\n",
        "num_iter = 500  # Number of training iterations"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "T-tO_03Ys2V5"
      },
      "source": [
        "Now putting everything together and executing the training process.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 25,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 0
        },
        "id": "XaowdvYgs2V5",
        "outputId": "9d40d53c-6cd8-4d8d-92e9-ceedfb78dcae"
      },
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Iteration: 10, Discriminator Loss: 1.374, Generator Loss: 0.582\n",
            "Iteration: 20, Discriminator Loss: 1.355, Generator Loss: 0.595\n",
            "Iteration: 30, Discriminator Loss: 1.365, Generator Loss: 0.580\n",
            "Iteration: 40, Discriminator Loss: 1.374, Generator Loss: 0.576\n",
            "Iteration: 50, Discriminator Loss: 1.352, Generator Loss: 0.589\n",
            "Iteration: 60, Discriminator Loss: 1.333, Generator Loss: 0.605\n",
            "Iteration: 70, Discriminator Loss: 1.348, Generator Loss: 0.600\n",
            "Iteration: 80, Discriminator Loss: 1.314, Generator Loss: 0.599\n",
            "Iteration: 90, Discriminator Loss: 1.333, Generator Loss: 0.601\n",
            "Iteration: 100, Discriminator Loss: 1.312, Generator Loss: 0.606\n",
            "Iteration: 110, Discriminator Loss: 1.279, Generator Loss: 0.629\n",
            "Iteration: 120, Discriminator Loss: 1.309, Generator Loss: 0.604\n",
            "Iteration: 130, Discriminator Loss: 1.253, Generator Loss: 0.624\n",
            "Iteration: 140, Discriminator Loss: 1.315, Generator Loss: 0.604\n",
            "Iteration: 150, Discriminator Loss: 1.227, Generator Loss: 0.660\n",
            "Iteration: 160, Discriminator Loss: 1.268, Generator Loss: 0.610\n",
            "Iteration: 170, Discriminator Loss: 1.306, Generator Loss: 0.599\n",
            "Iteration: 180, Discriminator Loss: 1.296, Generator Loss: 0.615\n",
            "Iteration: 190, Discriminator Loss: 1.264, Generator Loss: 0.604\n",
            "Iteration: 200, Discriminator Loss: 1.321, Generator Loss: 0.589\n",
            "Iteration: 210, Discriminator Loss: 1.208, Generator Loss: 0.667\n",
            "Iteration: 220, Discriminator Loss: 1.290, Generator Loss: 0.622\n",
            "Iteration: 230, Discriminator Loss: 1.236, Generator Loss: 0.628\n",
            "Iteration: 240, Discriminator Loss: 1.282, Generator Loss: 0.630\n",
            "Iteration: 250, Discriminator Loss: 1.190, Generator Loss: 0.660\n",
            "Iteration: 260, Discriminator Loss: 1.343, Generator Loss: 0.585\n",
            "Iteration: 270, Discriminator Loss: 1.303, Generator Loss: 0.624\n",
            "Iteration: 280, Discriminator Loss: 1.244, Generator Loss: 0.688\n",
            "Iteration: 290, Discriminator Loss: 1.269, Generator Loss: 0.641\n",
            "Iteration: 300, Discriminator Loss: 1.273, Generator Loss: 0.616\n",
            "Iteration: 310, Discriminator Loss: 1.247, Generator Loss: 0.759\n",
            "Iteration: 320, Discriminator Loss: 1.323, Generator Loss: 0.612\n",
            "Iteration: 330, Discriminator Loss: 1.281, Generator Loss: 0.647\n",
            "Iteration: 340, Discriminator Loss: 1.212, Generator Loss: 0.760\n",
            "Iteration: 350, Discriminator Loss: 1.322, Generator Loss: 0.636\n",
            "Iteration: 360, Discriminator Loss: 1.334, Generator Loss: 0.631\n",
            "Iteration: 370, Discriminator Loss: 1.283, Generator Loss: 0.721\n",
            "Iteration: 380, Discriminator Loss: 1.209, Generator Loss: 0.685\n",
            "Iteration: 390, Discriminator Loss: 1.332, Generator Loss: 0.629\n",
            "Iteration: 400, Discriminator Loss: 1.161, Generator Loss: 0.695\n",
            "Iteration: 410, Discriminator Loss: 1.340, Generator Loss: 0.630\n",
            "Iteration: 420, Discriminator Loss: 1.126, Generator Loss: 0.703\n",
            "Iteration: 430, Discriminator Loss: 1.204, Generator Loss: 0.753\n",
            "Iteration: 440, Discriminator Loss: 1.124, Generator Loss: 0.735\n",
            "Iteration: 450, Discriminator Loss: 1.136, Generator Loss: 0.743\n",
            "Iteration: 460, Discriminator Loss: 1.169, Generator Loss: 0.731\n",
            "Iteration: 470, Discriminator Loss: 1.054, Generator Loss: 0.785\n",
            "Iteration: 480, Discriminator Loss: 1.204, Generator Loss: 0.729\n",
            "Iteration: 490, Discriminator Loss: 1.075, Generator Loss: 0.778\n",
            "Iteration: 500, Discriminator Loss: 1.227, Generator Loss: 0.644\n"
          ]
        }
      ],
      "source": [
        "discriminator = Discriminator().to(device)\n",
        "generator = PatchQuantumGenerator(n_generators).to(device)\n",
        "\n",
        "# Binary cross entropy\n",
        "criterion = nn.BCELoss()\n",
        "\n",
        "# Optimisers\n",
        "optD = optim.SGD(discriminator.parameters(), lr=lrD)\n",
        "optG = optim.SGD(generator.parameters(), lr=lrG)\n",
        "\n",
        "real_labels = torch.full((batch_size,), 1.0, dtype=torch.float, device=device)\n",
        "fake_labels = torch.full((batch_size,), 0.0, dtype=torch.float, device=device)\n",
        "\n",
        "# Fixed noise allows us to visually track the generated images throughout training\n",
        "fixed_noise = torch.rand(8, n_qubits, device=device) * math.pi / 2\n",
        "\n",
        "# Iteration counter\n",
        "counter = 0\n",
        "\n",
        "# Collect images for plotting later\n",
        "results = []\n",
        "\n",
        "while True:\n",
        "    for i, (data, _) in enumerate(dataloader):\n",
        "\n",
        "        # Data for training the discriminator\n",
        "        data = data.reshape(-1, image_size * image_size)\n",
        "        real_data = data.to(device)\n",
        "\n",
        "        # Noise follwing a uniform distribution in range [0,pi/2)\n",
        "        noise = torch.rand(batch_size, n_qubits, device=device) * math.pi / 2\n",
        "        fake_data = generator(noise)\n",
        "\n",
        "        # Training the discriminator\n",
        "        discriminator.zero_grad()\n",
        "        outD_real = discriminator(real_data).view(-1)\n",
        "        outD_fake = discriminator(fake_data.detach()).view(-1)\n",
        "\n",
        "        errD_real = criterion(outD_real, real_labels)\n",
        "        errD_fake = criterion(outD_fake, fake_labels)\n",
        "        # Propagate gradients\n",
        "        errD_real.backward()\n",
        "        errD_fake.backward()\n",
        "\n",
        "        errD = errD_real + errD_fake\n",
        "        optD.step()\n",
        "\n",
        "        # Training the generator\n",
        "        generator.zero_grad()\n",
        "        outD_fake = discriminator(fake_data).view(-1)\n",
        "        errG = criterion(outD_fake, real_labels)\n",
        "        errG.backward()\n",
        "        optG.step()\n",
        "\n",
        "        counter += 1\n",
        "\n",
        "        # Show loss values\n",
        "        if counter % 10 == 0:\n",
        "            print(f'Iteration: {counter}, Discriminator Loss: {errD:0.3f}, Generator Loss: {errG:0.3f}')\n",
        "            test_images = generator(fixed_noise).view(8,1,image_size,image_size).cpu().detach()\n",
        "\n",
        "            # Save images every 50 iterations\n",
        "            if counter % 50 == 0:\n",
        "                results.append(test_images)\n",
        "\n",
        "        if counter == num_iter:\n",
        "            break\n",
        "    if counter == num_iter:\n",
        "        break"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "uDK5G66Bs2V5"
      },
      "source": [
        "::: {.note}\n",
        "::: {.title}\n",
        "Note\n",
        ":::\n",
        "\n",
        "You may have noticed `errG = criterion(outD_fake, real_labels)` and\n",
        "wondered why we don't use `fake_labels` instead of `real_labels`.\n",
        "However, this is simply a trick to be able to use the same `criterion`\n",
        "function for both the generator and discriminator. Using `real_labels`\n",
        "forces the generator loss function to use the $\\log(D(G(z))$ term\n",
        "instead of the $\\log(1 - D(G(z))$ term of the binary cross entropy loss\n",
        "function.\n",
        ":::\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Xsa1GwZus2V6"
      },
      "source": [
        "Finally, we plot how the generated images evolved throughout training.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 26,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 417
        },
        "id": "fvpigTx8s2V6",
        "outputId": "80be262d-635f-4269-f558-af22312ee315"
      },
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "text/plain": [
              "<Figure size 1000x500 with 80 Axes>"
            ],
            "image/png": "iVBORw0KGgoAAAANSUhEUgAAAxoAAAGQCAYAAAAp9BbFAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAACiLElEQVR4nO2deVwV1fvHH0A2WVQEzBVcUhQ0DbXFFNOUCre+qWlZamW4W6llWtlXzaVcy91SyqxcWlxyKQ3LzNzSslIzNUXclxAQBO49vz/q3t/MPA/cuTCE+P28Xy9fXj733LnPnDlzzpw78zmPh1JKEQAAAAAAAABYiGdJBwAAAAAAAAC4+cBEAwAAAAAAAGA5mGgAAAAAAAAALAcTDQAAAAAAAIDlYKIBAAAAAAAAsBxMNAAAAAAAAACWg4kGAAAAAAAAwHIw0QAAAAAAAABYDiYaAAAAAAAAAMu56ScaW7duJQ8PD9q6dWtJhwIAAOAmAOMKAACYw62JRlJSEnl4eNCePXuc2vr16+m1116zOi63mTt3LiUlJZV0GDr69OlDHh4e7F9UVBQra7fb6Y033qCaNWuSn58fNWrUiD766KMSiBoAAP49MK64x65du2jgwIEUGxtL3t7e5OHhUWD5d999l+rXr09+fn5066230ttvvy2WS01Npe7du1P58uUpODiYOnfuTMeOHSuOXQAA/A9RpqgbWL9+Pc2ZM6fEB4W5c+dSaGgo9enTR6e3atWKsrKyyMfHp0Ti8vX1pXfeeUenlStXjpUbM2YMTZ48mfr160fNmjWj1atX06OPPkoeHh7Uo0ePfytcAAAocTCu5M/69evpnXfeoUaNGlGtWrXo999/z7fsggULqH///vTwww/T888/T9u2baOhQ4fStWvX6MUXX3SWy8jIoHvvvZfS0tJo9OjR5O3tTTNmzKC4uDjav38/VaxY8d/YNQDATUiRJxrFgVKKsrOzyd/fv8jb8vT0JD8/PwuiKhxlypShXr16FVgmNTWVpk2bRoMGDaLZs2cTEdHTTz9NcXFxNHLkSOrWrRt5eXn9G+ECAMBNyc0yrgwYMIBefPFF8vf3p8GDB+c70cjKyqIxY8ZQQkICrVq1ioiI+vXrR3a7ncaPH0/PPPMMVahQgYj+nlAdOXKEdu3aRc2aNSMiogceeIBiYmJo2rRpNHHixH9n5wAANx1F8mj06dOH5syZQ0SkezTIgd1up5kzZ1J0dDT5+flRpUqVKDExka5cuaLbTmRkJHXo0IE2bdpETZs2JX9/f1qwYAERES1ZsoTatGlD4eHh5OvrSw0aNKB58+axz//666/0zTffOGNo3bo1EeX/LO3KlSspNjaW/P39KTQ0lHr16kWpqals/wIDAyk1NZW6dOlCgYGBFBYWRiNGjCCbzWa6nmw2G129ejXf91evXk25ubk0cOBAp+bh4UEDBgygU6dO0Y4dO0x/FwAAlGYwrhRMpUqVTE2WkpOT6dKlS7pxhYho0KBBlJmZSV988YVTW7VqFTVr1sw5ySAiioqKorZt29KKFStcfhcAAORHke5oJCYm0unTp+mrr76ipUuXiu8nJSVR3759aejQoXT8+HGaPXs27du3j7Zv307e3t7OsocPH6aePXtSYmIi9evXj+rVq0dERPPmzaPo6Gjq1KkTlSlThtauXUsDBw4ku91OgwYNIiKimTNn0pAhQygwMJDGjBlDRH93xvnhiKlZs2Y0adIkOnfuHM2aNYu2b99O+/bto/LlyzvL2mw2io+PpzvuuIOmTp1KmzdvpmnTplHt2rVpwIABLuvo2rVrFBwcTNeuXaMKFSpQz549acqUKRQYGOgss2/fPgoICKD69evrPtu8eXPn+/fcc4/L7wIAgNIOxhXX44oZ9u3bR0RETZs21emxsbHk6elJ+/bto169epHdbqeff/6ZnnzySbaN5s2b05dffknp6ekUFBRkSVwAgP8xlBssWbJEEZHavXu3Uxs0aJCSNrNt2zZFRGrZsmU6fePGjUyPiIhQRKQ2btzItnPt2jWmxcfHq1q1aum06OhoFRcXx8omJycrIlLJyclKKaVycnJUeHi4iomJUVlZWc5y69atU0SkXn31VafWu3dvRURq3Lhxum02adJExcbGsu8yMmrUKPXiiy+q5cuXq48++si5vRYtWqjc3FxnuYSEBLY/SimVmZmpiEiNGjXK5XcBAEBpBOPK35gdV7TkV0+O97y8vMT3wsLCVI8ePZRSSl24cEGMRyml5syZo4hIHTp0yK24AADAQbEtb7ty5UoqV64ctWvXji5evOj8FxsbS4GBgZScnKwrX7NmTYqPj2fb0d4iTktLo4sXL1JcXBwdO3aM0tLS3I5rz549dP78eRo4cKDuGduEhASKiorS3U52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\n"
          },
          "metadata": {}
        }
      ],
      "source": [
        "fig = plt.figure(figsize=(10, 5))\n",
        "outer = gridspec.GridSpec(5, 2, wspace=0.1)\n",
        "\n",
        "for i, images in enumerate(results):\n",
        "    inner = gridspec.GridSpecFromSubplotSpec(1, images.size(0),\n",
        "                    subplot_spec=outer[i])\n",
        "\n",
        "    images = torch.squeeze(images, dim=1)\n",
        "    for j, im in enumerate(images):\n",
        "\n",
        "        ax = plt.Subplot(fig, inner[j])\n",
        "        ax.imshow(im.numpy(), cmap=\"gray\")\n",
        "        ax.set_xticks([])\n",
        "        ax.set_yticks([])\n",
        "        if j==0:\n",
        "            ax.set_title(f'Iteration {50+i*50}', loc='left')\n",
        "        fig.add_subplot(ax)\n",
        "\n",
        "plt.show()"
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "seconds = time.time()\n",
        "print(\"Time in seconds since end of run:\", seconds)\n",
        "local_time = time.ctime(seconds)\n",
        "print(local_time)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 0
        },
        "id": "y4dVhN44beWS",
        "outputId": "e034b58b-afd9-4403-889c-47ad30b8a9be"
      },
      "execution_count": 27,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Time in seconds since end of run: 1693266593.7901826\n",
            "Mon Aug 28 23:49:53 2023\n"
          ]
        }
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "THvPX4IBs2V6"
      },
      "source": [
        "Acknowledgements\n",
        "================\n",
        "\n",
        "Many thanks to Karolis Špukas who I co-developed much of the code with.\n",
        "I also extend my thanks to Dr. Yuxuan Du for answering my questions\n",
        "regarding his paper. I am also indebited to the Pennylane community for\n",
        "their help over the past few years.\n",
        "\n",
        "References\n",
        "==========\n",
        "\n",
        "About the author\n",
        "================\n"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "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.9.17"
    },
    "colab": {
      "provenance": [],
      "gpuType": "V100"
    },
    "accelerator": "GPU"
  },
  "nbformat": 4,
  "nbformat_minor": 0
}