├── Image_classification_with_CNN.ipynb ├── Image_classification_with_QNN.ipynb ├── Image_classification_with_QNN_2.ipynb ├── README.md ├── quantum_circuit_examples.ipynb └── quantum_circuit_simulator.py /Image_classification_with_QNN_2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "id": "89319537", 6 | "metadata": {}, 7 | "source": [ 8 | "# Image classification \n", 9 | "\n", 10 | "Image classification is a computer vision task that involves categorizing images into predefined classes or categories. The goal is to develop algorithms or models that can accurately identify and assign labels to images based on their visual features and content. This task is commonly used in various applications, such as object recognition, facial recognition, medical imaging, and autonomous driving, to enable machines to understand and interpret visual information in a similar way to humans. The output of an image classification task is a prediction or probability distribution indicating the likelihood of each class for a given image." 11 | ] 12 | }, 13 | { 14 | "cell_type": "code", 15 | "execution_count": 1, 16 | "id": "349fb80c", 17 | "metadata": {}, 18 | "outputs": [], 19 | "source": [ 20 | "import numpy as np\n", 21 | "import matplotlib.pyplot as plt\n", 22 | "\n", 23 | "import time, copy\n", 24 | "\n", 25 | "import torch\n", 26 | "from torch import nn\n", 27 | "import torch.nn.functional as F\n", 28 | "from torch.utils.data import DataLoader\n", 29 | "from torchvision import datasets, transforms\n", 30 | "from torch.utils.data.dataset import random_split\n", 31 | "\n", 32 | "\n", 33 | "from quantum_circuit_simulator import quantum_circuit\n" 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "id": "dcbdabb7", 39 | "metadata": {}, 40 | "source": [ 41 | "## Load dataset and transform\n", 42 | "\n", 43 | "The \"Modified National Institute of Standards and Technology\" (__MNIST__) dataset consists of a large collection of 60,000 handwritten digits for training and an additional 10,000 handwritten digits for testing.\n", 44 | "It dataset has served as a benchmark for evaluating and comparing the performance of various machine learning algorithms, particularly in the field of image classification. It has played a crucial role in the development and advancement of deep learning models, especially convolutional neural networks (CNNs), and has been used extensively for educational purposes and as a baseline for assessing new algorithms and techniques in the field.\n", 45 | "\n", 46 | "\n", 47 | "_Image Format_: Each image in the MNIST dataset is a grayscale image with a resolution of 28x28 pixels. This results in a total of 784 pixels per image.\n", 48 | "\n", 49 | "_Digit Classes_: The dataset covers ten classes representing the digits from 0 to 9. Each image is labeled with the corresponding digit class, providing the ground truth for training and evaluation.\n", 50 | "\n", 51 | "_Data Distribution_: The dataset is balanced, meaning that it contains an equal number of samples for each digit class. This balance ensures that the model is exposed to an equal representation of each digit during training.\n", 52 | "\n", 53 | "\n", 54 | "-------------------\n", 55 | "\n", 56 | "__FashionMNIST__ is intended to be a more challenging dataset compared to MNIST, as it requires models to recognize and classify images of various clothing items accurately. FashionMNIST has the same Image Format, Data Distribution, and size of train- and test-set as of MNIST.\n", 57 | "\n", 58 | "_Clothing Categories_: The dataset covers ten different clothing categories, including T-shirts/tops, trousers, pullovers, dresses, coats, sandals, shirts, sneakers, bags, and ankle boots. Each image is labeled with the corresponding clothing category, providing the ground truth for training and evaluation." 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 2, 64 | "id": "0b35907a", 65 | "metadata": {}, 66 | "outputs": [ 67 | { 68 | "name": "stdout", 69 | "output_type": "stream", 70 | "text": [ 71 | "MNIST \n", 72 | "\n", 73 | "number of (train, test) examples = (60000, 10000)\n" 74 | ] 75 | } 76 | ], 77 | "source": [ 78 | "# Load the dataset and transform it into tensors and normalized between -1 and 1.\n", 79 | "\n", 80 | "size = 16\n", 81 | "\n", 82 | "transform = transforms.Compose([\n", 83 | " transforms.Resize((size, size)),\n", 84 | " transforms.ToTensor(),\n", 85 | " transforms.Normalize(mean=(0.5,), std=(0.5,)) \n", 86 | "])\n", 87 | "\n", 88 | "\n", 89 | "def load_dataset(name):\n", 90 | " print(name,'\\n')\n", 91 | " if name == \"FashionMNIST\":\n", 92 | " train_dataset = datasets.FashionMNIST(root=\"FashionMNIST\", train=True, download=True, transform=transform)\n", 93 | " test_dataset = datasets.FashionMNIST(root=\"FashionMNIST\", train=False, download=True, transform=transform)\n", 94 | " elif name == \"MNIST\":\n", 95 | " train_dataset = datasets.MNIST(root=\"MNIST\", train=True, download=True, transform=transform)\n", 96 | " test_dataset = datasets.MNIST(root=\"MNIST\", train=False, download=True, transform=transform)\n", 97 | " return train_dataset, test_dataset \n", 98 | "\n", 99 | "\n", 100 | "# Choose either \"MNIST\" or \"FashionMNIST\" \n", 101 | "train_dataset, test_dataset = load_dataset(\"MNIST\")\n", 102 | "\n", 103 | "\n", 104 | "\n", 105 | "print(f'number of (train, test) examples = {len(train_dataset), len(test_dataset)}')\n" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "id": "97f5b368", 111 | "metadata": {}, 112 | "source": [ 113 | "## Let us consider only two classes 0 and 1 for training and test" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 3, 119 | "id": "8cc59968", 120 | "metadata": {}, 121 | "outputs": [ 122 | { 123 | "name": "stdout", 124 | "output_type": "stream", 125 | "text": [ 126 | "number of (train, test) examples = (12665, 2115)\n" 127 | ] 128 | } 129 | ], 130 | "source": [ 131 | "def generate_subset(dataset):\n", 132 | " subset = []\n", 133 | " for i in range(len(dataset)):\n", 134 | " x, y = dataset[i]\n", 135 | " if y in [0, 1]:\n", 136 | " subset.append((x, torch.tensor(y, dtype=torch.float32)))\n", 137 | " return subset\n", 138 | "\n", 139 | "train_dataset = generate_subset(train_dataset)\n", 140 | "test_dataset = generate_subset(test_dataset)\n", 141 | "\n", 142 | "print(f'number of (train, test) examples = {len(train_dataset), len(test_dataset)}')" 143 | ] 144 | }, 145 | { 146 | "cell_type": "markdown", 147 | "id": "09367beb", 148 | "metadata": {}, 149 | "source": [ 150 | "### view a training example:" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": 4, 156 | "id": "0eaa7b82", 157 | "metadata": { 158 | "scrolled": true 159 | }, 160 | "outputs": [ 161 | { 162 | "name": "stdout", 163 | "output_type": "stream", 164 | "text": [ 165 | "x of torch.Size([1, 16, 16]) :\n" 166 | ] 167 | }, 168 | { 169 | "data": { 170 | "image/png": "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", 171 | "text/plain": [ 172 | "
" 173 | ] 174 | }, 175 | "metadata": { 176 | "needs_background": "light" 177 | }, 178 | "output_type": "display_data" 179 | }, 180 | { 181 | "name": "stdout", 182 | "output_type": "stream", 183 | "text": [ 184 | "true label = y = 1.0\n", 185 | "\n", 186 | "(x_min, x_max) = (-1.0, 0.961)\n" 187 | ] 188 | } 189 | ], 190 | "source": [ 191 | "idx = np.random.choice(len(train_dataset))\n", 192 | "\n", 193 | "x = train_dataset[idx][0]\n", 194 | "print(f'x of {x.shape} :')\n", 195 | "plt.imshow(x[0], cmap='gray')\n", 196 | "plt.show()\n", 197 | "\n", 198 | "print(f'true label = y = {train_dataset[idx][1]}\\n')\n", 199 | "\n", 200 | "print(f'(x_min, x_max) = {x.min().item(), round(x.max().item(),3)}')" 201 | ] 202 | }, 203 | { 204 | "attachments": { 205 | "QNN2.png": { 206 | "image/png": "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P8Xd3Z2cnBzuvPNODh48aHRZUobMzEzbZX9fNwMrEakcP3PR5geHDx82uBIRY2zduhUfHx/27Nljuy4xMdE2tkEci0I2EXFajzzyCAkJCaxbt46hQ4faro+Li6Nv374MHTqUTz/91MAKRUTqv4iICJYvX46LiwtHjx7l9ttvJzs72+iy5CIlAwr/86GFiDMoDoVLBsUiDUVMTAwRERGcOnUKgGuvvZbCwkJCQ0MNrkzKo5BNRJze4MGDWb16Nf/6178YN24c7u5FLx4++eQT7rnnHvr166cZHiIitWj48OG8/vrrAOzfv59bbrml1PxMMV7JgMJPnWziRPx8i/6uO3nypHYYlQblscce4//+7/9s/46KiiItLc3AisQeCtlEpN7o06cPixcvJjU1lRdeeIHWrVsDkJCQwKhRo+jVqxd//vOf+eGHHwyuVESk/pk8eTLR0dEA7N27lxtuuIEdO3YYXJUUy83NtV329HAxsBKRyvFsdOHrtXiuoEh9tm/fPvr378/ixYsBcHNzY+7cuSxbtszgysQeCtlEpN657rrreP3110lJSWHWrFlcc801AOzcuZOXX36ZLl26MGLECFasWMHZs2cNrlZEpP5YuHCh7V33o0ePcuutt/LRRx8ZXJWIiIhziI2N5fbbb+ef//wnAL169eKrr75i4sSJBlcm9lLIJiL1Vtu2bXn55ZdJSUnhrbfeIiIiAoCCggJWrVpFVFQUXbp04YUXXiAxMdHgakVE6oeZM2eycuVKPDw8yM3N5cEHHyy13EVERERK27p1K3fddRfjx4+3jVt44okn+M9//qP5a05GIZuI1Huenp48+eSTfPHFFyQlJfHCCy9w9dVXA/Djjz8ye/Zsbr75ZiIiIli4cCG//PKLwRWLiDi34cOH89VXX9G8eXOgaHDzgAED+Pnnnw2uTERExHFkZWXx/PPPExERwWeffQYU7dy9YsUK3n77bYOrk6pQyCYiDcpNN93E66+/zt69e/noo48YMWKEbaOErVu38swzz9ClSxfGjBnD+vXrDa5WRMR59e7dm9TUVK699loANm/ezDXXXMOf/vSnUvPBREREGqJ3332XPn36MG/ePAC8vLyYPn0627dv54EHHjC4OqkqhWwi0iB5eHhw//338/HHH7N3717+8pe/cOONNwJw4sQJli5dyuDBg7n++uuZPn063377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" 207 | } 208 | }, 209 | "cell_type": "markdown", 210 | "id": "519ac1b9", 211 | "metadata": {}, 212 | "source": [ 213 | "# Define model (QNN), training and test loops\n", 214 | "\n", 215 | "![QNN2.png](attachment:QNN2.png)\n", 216 | "\n", 217 | "### QNN = parameterized quantum circuit (PQC)\n", 218 | "\n", 219 | "\n", 220 | "\n", 221 | "A PQC is made of $L$ quantum layers. \n", 222 | "Here, each quantum layer $l$ has two parts: rotation $\\text{R}_y(\\theta^{[l]})$ in green and entangling $\\text{CXs}$ in blue color.\n", 223 | "\n", 224 | "-----------\n", 225 | "\n", 226 | "__The rotation part__ is made of the tensor-product \n", 227 | "$\\ \\text{R}_y(\\theta^{[l]}) = \\displaystyle\\bigotimes_{i} \\text{R}_y(\\theta^{[l]}_i)\\ $\n", 228 | "of single-qubit rotations (unitary gates) \n", 229 | "$\\ \\text{R}(\\theta) = \\cos(\\theta) I + \\texttt{i}\\sin(\\theta)\\,Y$ around $y$-axis.\n", 230 | "where $i\\in\\{1,\\cdots,n\\}$ is the qubit-index, the angles $\\theta^{[l]}=(\\theta^{[l]}_1,\\cdots,\\theta^{[l]}_n)$ of rotations are learnable parameters, and $n$ is the number of qubits.\n", 231 | "$I$ is the Identity and $X, Y$ are the Pauli operators.\n", 232 | "
\n", 233 | "\n", 234 | "\n", 235 | "__The entangling part__ is made of the tensor-product \n", 236 | "$\\text{CXs} = \\displaystyle\\bigotimes_{(i,j)} \\text{CX}_{(i,j)}$\n", 237 | "of two-qubit\n", 238 | "$\\text{CX}_{(i,j)} = |0\\rangle_i\\langle 0|\\otimes I_j +|1\\rangle_i\\langle 1|\\otimes X_j$ gates,\n", 239 | "where $(i,j)$ represents the pair of control and target qubits $i$ and $j$, respectively.\n", 240 | "This part will create entanglement between qubits and facilitate quantum information transfer between qubits. \n", 241 | "However, this part carries no learnable parameters.\n", 242 | "\n", 243 | "-------------\n", 244 | "\n", 245 | "__Input to the PQC__ is a $l_2$-normalized (quantum state) vector $|x\\rangle$ of $2^n$ components. The ket $|x\\rangle$ comes from the input vector $x$ after flattening a $\\text{size}\\times\\text{size}$ image from the data.\n", 246 | "Since $\\text{size}\\times\\text{size}=:2^n=\\text{dim}$ by definition and $\\text{size}=16$, $n=8$.\n", 247 | "\n", 248 | "\n", 249 | "\n", 250 | "__After the PQC__, we perform measurement on $|x,\\Theta\\rangle$ in the $z$-basis (computational-basis) and get a probability-vector $\\textbf{p}(x,\\Theta)=(p_0,\\cdots,p_{\\text{dim}})$. Then we get the marginal probability\n", 251 | "$\\sum_{i \\text{ is even}} p_i$. This whole procedure is equivalent to performing a $z$-measurement on the last qubit shown in red color and calculating the probability for the 0 outcome. With the marginal probability, we compute $loss(\\Theta|x)$. All the learnable parameters are made of $\\Theta=\\{\\theta^{[1]}, \\cdots, \\theta^{[L]}\\}$ associated with the quantum layer.\n", 252 | "\n", 253 | "\n", 254 | "\n", 255 | "For details on the parameterized quantum circuit, see https://iopscience.iop.org/article/10.1088/2058-9565/ab4eb5\n", 256 | " \n" 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": 5, 262 | "id": "077f1b6b", 263 | "metadata": { 264 | "scrolled": true 265 | }, 266 | "outputs": [ 267 | { 268 | "data": { 269 | "text/plain": [ 270 | "tensor([1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0.,\n", 271 | " 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0.,\n", 272 | " 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0.,\n", 273 | " 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0.,\n", 274 | " 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0.,\n", 275 | " 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0.,\n", 276 | " 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0.,\n", 277 | " 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0.,\n", 278 | " 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0.,\n", 279 | " 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0.,\n", 280 | " 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0.,\n", 281 | " 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0.,\n", 282 | " 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0.,\n", 283 | " 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0., 1., 0.,\n", 284 | " 1., 0., 1., 0.])" 285 | ] 286 | }, 287 | "execution_count": 5, 288 | "metadata": {}, 289 | "output_type": "execute_result" 290 | } 291 | ], 292 | "source": [ 293 | "\n", 294 | "n = int(2*np.log2(size))\n", 295 | "dim = 2**n # dimension of the n-qubit Hilbert space\n", 296 | "\n", 297 | "\n", 298 | "#--------------------------------------------------------------------------------------\n", 299 | "\n", 300 | "'''0 outcome projector on the last qubit'''\n", 301 | "\n", 302 | "last_qubit_proj = torch.tensor([(1 + (-1)**i)/2 for i in range(dim)], dtype=torch.float32)\n", 303 | "last_qubit_proj\n" 304 | ] 305 | }, 306 | { 307 | "cell_type": "code", 308 | "execution_count": 6, 309 | "id": "2905df86", 310 | "metadata": {}, 311 | "outputs": [ 312 | { 313 | "name": "stdout", 314 | "output_type": "stream", 315 | "text": [ 316 | "Using cpu device\n", 317 | "\n" 318 | ] 319 | } 320 | ], 321 | "source": [ 322 | "device = \"cuda\" if torch.cuda.is_available() else \"cpu\" # Get gpu or cpu device for training\n", 323 | "print(f\"Using {device} device\\n\")\n", 324 | "\n", 325 | "#=====================================================================================\n", 326 | "\n", 327 | "\n", 328 | "class QNN(torch.nn.Module): # Define model\n", 329 | " def __init__(self, n, L): # number of qubits = n, number of quantum layers = L\n", 330 | " super().__init__()\n", 331 | " \n", 332 | " self.flatten = nn.Flatten()\n", 333 | " \n", 334 | " angles = torch.empty((L, n), dtype=torch.float64)\n", 335 | " torch.nn.init.uniform_(angles, -0.01, 0.01)\n", 336 | " self.angles = torch.nn.Parameter(angles) # it makes angles learnable parameters\n", 337 | " \n", 338 | "\n", 339 | " def forward(self, x):\n", 340 | " x = self.flatten(x)\n", 341 | " x /= torch.linalg.norm(x.clone(), ord=2, dim=1, keepdim=True) # L2 normalization to change x --> |x⟩\n", 342 | " \n", 343 | " '''initializing parameterized quantum circuits (PQC)'''\n", 344 | " qc = quantum_circuit(num_qubits = n, state_vector = x.T) # each column is a feature-vector of an example\n", 345 | " for l in range(L):\n", 346 | " qc.Ry_layer(self.angles[l].to(torch.cfloat)) # rotation part of lth quantum layer\n", 347 | " qc.cx_linear_layer() # entangling part of lth quantum layer\n", 348 | "\n", 349 | " 'after passing through the PQC, measurement on the output-ket in the computational basis'\n", 350 | " x = torch.real(qc.probabilities()) # each column is a probabilities-vector for an example \n", 351 | " # x.shape = (dim, batch size)\n", 352 | " \n", 353 | " #print(torch.sum(x, dim=0)) # to see whether probabilities add up to 1 or not\n", 354 | "\n", 355 | " x = torch.matmul(x.T, last_qubit_proj) # probability of getting 0 outcome on the last qubit\n", 356 | " return x \n", 357 | "\n" 358 | ] 359 | }, 360 | { 361 | "cell_type": "code", 362 | "execution_count": 7, 363 | "id": "eec9caac", 364 | "metadata": {}, 365 | "outputs": [], 366 | "source": [ 367 | "\n", 368 | "def performance_estimate(dataset, model, loss_fn, train_or_test):\n", 369 | " '''this function computes accuracy and loss of a model on the training or test set'''\n", 370 | " data_size = len(dataset)\n", 371 | " \n", 372 | " dataloader = DataLoader(dataset=dataset, batch_size=batch_size, shuffle=True)\n", 373 | " num_batches = len(dataloader)\n", 374 | " \n", 375 | " model.eval()\n", 376 | " loss, accuracy = 0, 0\n", 377 | " with torch.no_grad():\n", 378 | " for X, y in dataloader:\n", 379 | " X, y = X.to(device), y.to(device)\n", 380 | " pred = model(X)\n", 381 | " loss += loss_fn(pred, y).item()\n", 382 | " \n", 383 | " pred = torch.stack([1-pred, pred]).T\n", 384 | " accuracy += (pred.argmax(1) == y).sum().item() \n", 385 | " accuracy /= data_size # accuracy lies in the interval [0, 1] \n", 386 | " loss /= num_batches\n", 387 | " print(f\"{train_or_test} accuracy: {round(accuracy, 3)}, {train_or_test} loss: {round(loss,3)}\")\n", 388 | " return accuracy, loss\n", 389 | "\n", 390 | "\n", 391 | "\n", 392 | "\n", 393 | "def one_epoch(model, loss_fn, optimizer, dataset, batch_size):\n", 394 | " \n", 395 | " A_train, L_train, A_test, L_test = [], [], [], []\n", 396 | "\n", 397 | " dataloader = DataLoader(dataset=dataset, batch_size=batch_size, shuffle=True)\n", 398 | " \n", 399 | " model.train()\n", 400 | " for batch, (X, y) in enumerate(dataloader):\n", 401 | " X, y = X.to(device), y.to(device)\n", 402 | "\n", 403 | " out = model(X) # Perform a single forward pass \n", 404 | " loss = loss_fn(out, y) \n", 405 | " \n", 406 | " optimizer.zero_grad() # Clear gradients\n", 407 | " loss.backward() # Derive gradients, backpropagation\n", 408 | " optimizer.step() # Update parameters based on gradients\n", 409 | " \n", 410 | " \n", 411 | " if batch % batch_size == 0: \n", 412 | " #As training progress, computing and appending loss and accuracy of the model on train and test set\n", 413 | " accuracy_train, loss_train = performance_estimate(train_dataset, model, loss_fn, 'train')\n", 414 | " accuracy_test, loss_test = performance_estimate(test_dataset, model, loss_fn, 'test ')\n", 415 | " print()\n", 416 | " \n", 417 | " A_train.append(accuracy_train) \n", 418 | " L_train.append(loss_train)\n", 419 | " A_test.append(accuracy_test)\n", 420 | " L_test.append(loss_test)\n", 421 | " \n", 422 | " #print(f\"train loss: {round(loss,3)}\")\n", 423 | " \n", 424 | " return A_train, L_train, A_test, L_test \n", 425 | "\n", 426 | "\n", 427 | "\n", 428 | "\n", 429 | "'''Binary Cross Entropy Loss''' \n", 430 | "\n", 431 | "def training(dataset, batch_size, n, L, lr_, weight_decay_, epochs):\n", 432 | " \n", 433 | " model = QNN(n=n, L=L).to(device)\n", 434 | " loss_fn = nn.BCELoss()\n", 435 | " optimizer = torch.optim.Adam(model.parameters(), lr=lr_, weight_decay=weight_decay_)\n", 436 | " \n", 437 | " A_Train, L_Train, A_Test, L_Test = [], [], [], []\n", 438 | " for t in range(epochs): \n", 439 | " print(f\"Epoch {t+1} ---------------------------------- \\n\")\n", 440 | " #As training progress, computing and appending loss and accuracy of the model on train and test set\n", 441 | " A_train, L_train, A_test, L_test = one_epoch(model, loss_fn, optimizer, dataset, batch_size)\n", 442 | " A_Train += A_train\n", 443 | " L_Train += L_train \n", 444 | " A_Test += A_test\n", 445 | " L_Test += L_test\n", 446 | " \n", 447 | " #accuracy, loss = performance_estimate(test_dataset, model, loss_fn, 'test ')\n", 448 | " \n", 449 | " model_state_dict = model.state_dict() # for saving or loading the trained model\n", 450 | " \n", 451 | " return A_Train, L_Train, A_Test, L_Test, model_state_dict\n", 452 | " " 453 | ] 454 | }, 455 | { 456 | "cell_type": "markdown", 457 | "id": "834b1b08", 458 | "metadata": {}, 459 | "source": [ 460 | "# training..." 461 | ] 462 | }, 463 | { 464 | "cell_type": "code", 465 | "execution_count": 8, 466 | "id": "fb62ba99", 467 | "metadata": { 468 | "scrolled": false 469 | }, 470 | "outputs": [ 471 | { 472 | "name": "stdout", 473 | "output_type": "stream", 474 | "text": [ 475 | "number of qubits = 8\n", 476 | "number of quantum layers = 3\n", 477 | "number of angles (learnable parameters of quantum circuit) = 24\n", 478 | " \n", 479 | "batch_size = 64\n", 480 | "\n", 481 | "Epoch 1 ---------------------------------- \n", 482 | "\n", 483 | "train accuracy: 0.629, train loss: 0.681\n", 484 | "test accuracy: 0.652, test loss: 0.679\n", 485 | "\n", 486 | "train accuracy: 0.826, train loss: 0.652\n", 487 | "test accuracy: 0.841, test loss: 0.649\n", 488 | "\n", 489 | "train accuracy: 0.82, train loss: 0.622\n", 490 | "test accuracy: 0.833, test loss: 0.618\n", 491 | "\n", 492 | "train accuracy: 0.861, train loss: 0.582\n", 493 | "test accuracy: 0.864, test loss: 0.577\n", 494 | "\n", 495 | "Epoch 2 ---------------------------------- \n", 496 | "\n", 497 | "train accuracy: 0.865, train loss: 0.579\n", 498 | "test accuracy: 0.871, test loss: 0.574\n", 499 | "\n", 500 | "train accuracy: 0.944, train loss: 0.543\n", 501 | "test accuracy: 0.948, test loss: 0.538\n", 502 | "\n", 503 | "train accuracy: 0.967, train loss: 0.521\n", 504 | "test accuracy: 0.967, test loss: 0.515\n", 505 | "\n", 506 | "train accuracy: 0.929, train loss: 0.509\n", 507 | "test accuracy: 0.93, test loss: 0.505\n", 508 | "\n", 509 | "Epoch 3 ---------------------------------- \n", 510 | "\n", 511 | "train accuracy: 0.928, train loss: 0.508\n", 512 | "test accuracy: 0.929, test loss: 0.497\n", 513 | "\n", 514 | "train accuracy: 0.921, train loss: 0.501\n", 515 | "test accuracy: 0.926, test loss: 0.496\n", 516 | "\n", 517 | "train accuracy: 0.913, train loss: 0.497\n", 518 | "test accuracy: 0.916, test loss: 0.495\n", 519 | "\n", 520 | "train accuracy: 0.914, train loss: 0.494\n", 521 | "test accuracy: 0.915, test loss: 0.491\n", 522 | "\n", 523 | " ~~~~~ training is done ~~~~~\n", 524 | "\n", 525 | "CPU times: user 7min 14s, sys: 183 ms, total: 7min 14s\n", 526 | "Wall time: 1min 48s\n" 527 | ] 528 | } 529 | ], 530 | "source": [ 531 | "%%time\n", 532 | "\n", 533 | "\n", 534 | "L = 3\n", 535 | "\n", 536 | "n_angs = n*L\n", 537 | "\n", 538 | "print(\"number of qubits = \", n)\n", 539 | "print(\"number of quantum layers = \", L)\n", 540 | "print(f\"number of angles (learnable parameters of quantum circuit) = {n_angs}\\n \")\n", 541 | "\n", 542 | "#--------------------------------------------------------------------------------------\n", 543 | "\n", 544 | "\n", 545 | "batch_size = 64\n", 546 | "print(f'batch_size = {batch_size}\\n')\n", 547 | "\n", 548 | "\n", 549 | "#----------------------------------------------------------------------------------\n", 550 | "\n", 551 | "\n", 552 | "A_Train, L_Train, A_Test, L_Test, model_state_dict = training(train_dataset, batch_size=batch_size, n=n, L=L,\n", 553 | " lr_=1e-3, weight_decay_=1e-8, epochs=3)\n", 554 | "\n", 555 | "\n", 556 | "print(f' ~~~~~ training is done ~~~~~\\n')" 557 | ] 558 | }, 559 | { 560 | "cell_type": "code", 561 | "execution_count": 9, 562 | "id": "9f9a6522", 563 | "metadata": {}, 564 | "outputs": [ 565 | { 566 | "data": { 567 | "image/png": "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", 568 | "text/plain": [ 569 | "
" 570 | ] 571 | }, 572 | "metadata": { 573 | "needs_background": "light" 574 | }, 575 | "output_type": "display_data" 576 | }, 577 | { 578 | "data": { 579 | "image/png": "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", 580 | "text/plain": [ 581 | "
" 582 | ] 583 | }, 584 | "metadata": { 585 | "needs_background": "light" 586 | }, 587 | "output_type": "display_data" 588 | } 589 | ], 590 | "source": [ 591 | "plt.plot(A_Train, label='train set')\n", 592 | "plt.plot(A_Test, label='test set')\n", 593 | "plt.ylabel('accuracy')\n", 594 | "plt.legend()\n", 595 | "plt.show()\n", 596 | "\n", 597 | "plt.plot(L_Train, label='train set')\n", 598 | "plt.plot(L_Test, label='test set')\n", 599 | "plt.ylabel('loss')\n", 600 | "plt.legend()\n", 601 | "plt.show()" 602 | ] 603 | }, 604 | { 605 | "cell_type": "markdown", 606 | "id": "416b30e5", 607 | "metadata": {}, 608 | "source": [ 609 | "## save model" 610 | ] 611 | }, 612 | { 613 | "cell_type": "code", 614 | "execution_count": 10, 615 | "id": "988bcfc3", 616 | "metadata": {}, 617 | "outputs": [], 618 | "source": [ 619 | "torch.save(model_state_dict, \"model_MNIST_QNN2.pth\")" 620 | ] 621 | }, 622 | { 623 | "cell_type": "markdown", 624 | "id": "bdf78969", 625 | "metadata": {}, 626 | "source": [ 627 | "## load model" 628 | ] 629 | }, 630 | { 631 | "cell_type": "code", 632 | "execution_count": 11, 633 | "id": "3eb4ede1", 634 | "metadata": {}, 635 | "outputs": [ 636 | { 637 | "data": { 638 | "text/plain": [ 639 | "" 640 | ] 641 | }, 642 | "execution_count": 11, 643 | "metadata": {}, 644 | "output_type": "execute_result" 645 | } 646 | ], 647 | "source": [ 648 | "model = QNN(n=n, L=L).to(device)\n", 649 | "model.load_state_dict(model_state_dict)\n", 650 | "\n", 651 | "#model.load_state_dict(torch.load(\"model_MNIST_QNN.pth\"))" 652 | ] 653 | }, 654 | { 655 | "cell_type": "markdown", 656 | "id": "52a30c42", 657 | "metadata": {}, 658 | "source": [ 659 | "## predict test examples" 660 | ] 661 | }, 662 | { 663 | "cell_type": "code", 664 | "execution_count": 12, 665 | "id": "b5be82d3", 666 | "metadata": { 667 | "scrolled": false 668 | }, 669 | "outputs": [ 670 | { 671 | "name": "stdout", 672 | "output_type": "stream", 673 | "text": [ 674 | "x of torch.Size([1, 16, 16]) :\n" 675 | ] 676 | }, 677 | { 678 | "data": { 679 | "image/png": "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", 680 | "text/plain": [ 681 | "
" 682 | ] 683 | }, 684 | "metadata": { 685 | "needs_background": "light" 686 | }, 687 | "output_type": "display_data" 688 | }, 689 | { 690 | "name": "stdout", 691 | "output_type": "stream", 692 | "text": [ 693 | "true label = y = 1.0\n", 694 | "\n", 695 | "predicted label = 1\n", 696 | "\n" 697 | ] 698 | }, 699 | { 700 | "data": { 701 | "image/png": "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", 702 | "text/plain": [ 703 | "
" 704 | ] 705 | }, 706 | "metadata": { 707 | "needs_background": "light" 708 | }, 709 | "output_type": "display_data" 710 | }, 711 | { 712 | "name": "stdout", 713 | "output_type": "stream", 714 | "text": [ 715 | "The sum of probability = 1.0\n" 716 | ] 717 | } 718 | ], 719 | "source": [ 720 | "idx = np.random.choice(len(test_dataset))\n", 721 | "\n", 722 | "x = test_dataset[idx][0]\n", 723 | "print(f'x of {x.shape} :')\n", 724 | "plt.imshow(x[0], cmap='gray')\n", 725 | "plt.show()\n", 726 | "\n", 727 | "print(f'true label = y = {test_dataset[idx][1]}\\n')\n", 728 | "\n", 729 | "\n", 730 | "out = model(x.view(1, 1, size, size)).detach().flatten()\n", 731 | "prob = torch.stack([1 - out, out])\n", 732 | "pred = prob.argmax().item()\n", 733 | "print(f'predicted label = {pred}\\n')\n", 734 | "\n", 735 | "plt.stem(np.arange(2), prob)\n", 736 | "plt.ylabel('probability')\n", 737 | "plt.xlabel('class labels')\n", 738 | "plt.show()\n", 739 | "\n", 740 | "print(f'The sum of probability = {torch.sum(prob).item()}')" 741 | ] 742 | }, 743 | { 744 | "cell_type": "markdown", 745 | "id": "8b298922", 746 | "metadata": {}, 747 | "source": [ 748 | "$ $\n", 749 | "\n", 750 | "$ $\n", 751 | "\n", 752 | "## simplified training loop" 753 | ] 754 | }, 755 | { 756 | "cell_type": "code", 757 | "execution_count": 13, 758 | "id": "b3dcb3aa", 759 | "metadata": {}, 760 | "outputs": [], 761 | "source": [ 762 | "def one_epoch_(model, loss_fn, optimizer, dataset, batch_size):\n", 763 | " \n", 764 | " dataloader = DataLoader(dataset=dataset, batch_size=batch_size, shuffle=True)\n", 765 | " \n", 766 | " model.train()\n", 767 | " for batch, (X, y) in enumerate(dataloader):\n", 768 | " X, y = X.to(device), y.to(device)\n", 769 | "\n", 770 | " out = model(X) # Perform a single forward pass\n", 771 | " loss = loss_fn(out, y) \n", 772 | " \n", 773 | " optimizer.zero_grad() # Clear gradients\n", 774 | " loss.backward() # Derive gradients, backpropagation\n", 775 | " optimizer.step() # Update parameters based on gradients\n", 776 | " \n", 777 | " if batch % batch_size == 0: \n", 778 | " print(f\"train loss: {round(loss.item(),3)}\")\n", 779 | "\n", 780 | "\n", 781 | " \n", 782 | " \n", 783 | "def training_(dataset, batch_size, n, L, lr_, weight_decay_, epochs):\n", 784 | " \n", 785 | " model = QNN(n=n, L=L).to(device)\n", 786 | " loss_fn = nn.BCELoss()\n", 787 | " optimizer = torch.optim.Adam(model.parameters(), lr=lr_, weight_decay=weight_decay_)\n", 788 | " \n", 789 | " for t in range(epochs): \n", 790 | " print(f\"Epoch {t+1} ---------------------------------- \\n \")\n", 791 | " one_epoch_(model, loss_fn, optimizer, dataset, batch_size)\n", 792 | " \n", 793 | " #accuracy_train, loss_train = performance_estimate(train_dataset, model, loss_fn, 'train')\n", 794 | " #accuracy_test, loss_test = performance_estimate(test_dataset, model, loss_fn, 'test ')\n", 795 | " #print()\n", 796 | "\n", 797 | " \n", 798 | " model_state_dict = model.state_dict()\n", 799 | " return model_state_dict\n", 800 | "\n", 801 | " " 802 | ] 803 | }, 804 | { 805 | "cell_type": "code", 806 | "execution_count": 14, 807 | "id": "7217eb93", 808 | "metadata": { 809 | "scrolled": false 810 | }, 811 | "outputs": [ 812 | { 813 | "name": "stdout", 814 | "output_type": "stream", 815 | "text": [ 816 | "batch_size = 64\n", 817 | "\n", 818 | "Epoch 1 ---------------------------------- \n", 819 | " \n", 820 | "train loss: 0.674\n", 821 | "train loss: 0.64\n", 822 | "train loss: 0.594\n", 823 | "train loss: 0.566\n", 824 | "Epoch 2 ---------------------------------- \n", 825 | " \n", 826 | "train loss: 0.566\n", 827 | "train loss: 0.575\n", 828 | "train loss: 0.516\n", 829 | "train loss: 0.5\n", 830 | "Epoch 3 ---------------------------------- \n", 831 | " \n", 832 | "train loss: 0.52\n", 833 | "train loss: 0.511\n", 834 | "train loss: 0.488\n", 835 | "train loss: 0.493\n", 836 | "\n", 837 | " ~~~~~ training is done ~~~~~\n", 838 | "\n", 839 | "CPU times: user 1min 33s, sys: 47.8 ms, total: 1min 33s\n", 840 | "Wall time: 23.3 s\n" 841 | ] 842 | } 843 | ], 844 | "source": [ 845 | "%%time\n", 846 | "\n", 847 | "\n", 848 | "batch_size = 64\n", 849 | "print(f'batch_size = {batch_size}\\n')\n", 850 | "\n", 851 | "#----------------------------------------------------------------------------------\n", 852 | "\n", 853 | "model_state_dict = training_(train_dataset, batch_size=batch_size, lr_=1e-3, n=n, L=L, \n", 854 | " weight_decay_=1e-8, epochs=3)\n", 855 | "\n", 856 | "print()\n", 857 | "print(f' ~~~~~ training is done ~~~~~\\n')" 858 | ] 859 | }, 860 | { 861 | "cell_type": "code", 862 | "execution_count": 15, 863 | "id": "6f5645b9", 864 | "metadata": {}, 865 | "outputs": [ 866 | { 867 | "name": "stdout", 868 | "output_type": "stream", 869 | "text": [ 870 | "train accuracy: 0.914, train loss: 0.493\n", 871 | "test accuracy: 0.916, test loss: 0.492\n", 872 | "\n", 873 | "CPU times: user 22.4 s, sys: 7.94 ms, total: 22.4 s\n", 874 | "Wall time: 5.62 s\n" 875 | ] 876 | } 877 | ], 878 | "source": [ 879 | "%%time\n", 880 | "\n", 881 | "\n", 882 | "model = QNN(n=n, L=L).to(device)\n", 883 | "model.load_state_dict(model_state_dict)\n", 884 | "loss_fn = nn.BCELoss()\n", 885 | "\n", 886 | "\n", 887 | "accuracy_train, loss_train = performance_estimate(train_dataset, model, loss_fn, 'train')\n", 888 | "accuracy_test, loss_test = performance_estimate(test_dataset, model, loss_fn, 'test ')\n", 889 | "print()\n", 890 | "\n" 891 | ] 892 | }, 893 | { 894 | "cell_type": "markdown", 895 | "id": "2c75a3b1", 896 | "metadata": {}, 897 | "source": [ 898 | "# " 899 | ] 900 | } 901 | ], 902 | "metadata": { 903 | "kernelspec": { 904 | "display_name": "Python 3 (ipykernel)", 905 | "language": "python", 906 | "name": "python3" 907 | }, 908 | "language_info": { 909 | "codemirror_mode": { 910 | "name": "ipython", 911 | "version": 3 912 | }, 913 | "file_extension": ".py", 914 | "mimetype": "text/x-python", 915 | "name": "python", 916 | "nbconvert_exporter": "python", 917 | "pygments_lexer": "ipython3", 918 | "version": "3.10.6" 919 | } 920 | }, 921 | "nbformat": 4, 922 | "nbformat_minor": 5 923 | } 924 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Image classification with CNN and QNN 2 | 3 | Image classification is a computer vision task, where a machine learning algorithm has to categorize images into predefined classes. Here, we have taken the "MNIST" and "FashionMNIST" datasets (for more details, see the attached notebooks). 4 | 5 | The "MNIST" dataset contains images of handwritten digits from 0 to 9. 6 | 7 | The "FashionMNIST" dataset covers ten different clothing categories, including T-shirts/tops, trousers, pullovers, dresses, coats, sandals, shirts, sneakers, bags, and ankle boots. Each image is labeled with the corresponding clothing category, providing the ground truth for training and testing. 8 | 9 | ![pred_CNN](https://github.com/ArunSehrawat/Image_classification_with_CNN/assets/99533657/169936fb-72c7-436c-b069-2e43f0ad49ae) 10 | 11 | ----- 12 | 13 | **(1)** In the attached Jupyter notebook __Image_classification_with_CNN__, I have presented the **Convolutional Neural Networks (CNNs)** for the task. 14 | 15 | 16 | ![LeNET5](https://github.com/ArunSehrawat/Image_classification_with_CNN_and_QNN/assets/99533657/21d097f2-d958-4100-bf90-0ffdb3360017) 17 | 18 | Image (LeNET-5) source: http://vision.stanford.edu/cs598_spring07/papers/Lecun98.pdf 19 | 20 | ----- 21 | 22 | **(2)** In the notebook __Image_classification_with_QNN__, I have presented the **Quantum Neural Network (QNN)** for the task. Here, the QNN consists of quantum layers followed by a classical layer at the end. 23 | 24 | ![QNN](https://github.com/ArunSehrawat/Image_classification_with_CNN_and_QNN/assets/99533657/febb497c-1dd7-4517-8ea4-4c1871c6540b) 25 | 26 | ----- 27 | 28 | **(3)** Unlike __Image_classification_with_QNN__, 29 | we are taking only two classes 0 and 1 and taking __only quantum layers__ in the notebook __Image_classification_with_QNN_2__. 30 | 31 | 32 | ![QNN2](https://github.com/ArunSehrawat/Image_classification_with_CNN_and_QNN/assets/99533657/8ee8b2fa-d71e-4850-9531-028c31ffb79e) 33 | 34 | 35 | ----- 36 | 37 | I have also uploaded: 38 | **(4) quantum circuit simulator** (used in QNN) 39 | 40 | **(5)** a notebook with **quantum circuit examples** to test the quantum circuit simulator 41 | 42 | ------- 43 | 44 | ![acc_loss_CNN](https://github.com/ArunSehrawat/Image_classification_with_CNN/assets/99533657/6c1f2025-db88-411f-b3e3-22e2779d40a4) 45 | -------------------------------------------------------------------------------- /quantum_circuit_examples.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "0aaf95a2", 7 | "metadata": {}, 8 | "outputs": [ 9 | { 10 | "name": "stdout", 11 | "output_type": "stream", 12 | "text": [ 13 | "\n" 14 | ] 15 | } 16 | ], 17 | "source": [ 18 | "import torch\n", 19 | "#import numpy as np\n", 20 | "\n", 21 | "from quantum_circuit_simulator import quantum_circuit\n", 22 | "\n", 23 | "\n", 24 | "'''\n", 25 | "state_vector can \n", 26 | "(1) either be a vector of shape (dim,)\n", 27 | "(2) either be a matrix of shape (dim, number of examples)\n", 28 | "\n", 29 | "n is the number of qubits\n", 30 | "dim = 2**n\n", 31 | "'''\n", 32 | "\n", 33 | "print()" 34 | ] 35 | }, 36 | { 37 | "cell_type": "markdown", 38 | "id": "2e53aba3", 39 | "metadata": {}, 40 | "source": [ 41 | "## X, Y, Z gates" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 2, 47 | "id": "60902d84", 48 | "metadata": {}, 49 | "outputs": [ 50 | { 51 | "name": "stdout", 52 | "output_type": "stream", 53 | "text": [ 54 | "tensor([1.+0.j, 2.+0.j]) = initial state vector\n", 55 | "\n", 56 | "1 = n\n", 57 | "2 = dim\n", 58 | "\n", 59 | "tensor([[1.+0.j, 0.+0.j],\n", 60 | " [0.+0.j, 1.+0.j]]) = I\n", 61 | "tensor([[0.+0.j, 1.+0.j],\n", 62 | " [1.+0.j, 0.+0.j]]) = X\n", 63 | "tensor([[0.+0.j, -0.-1.j],\n", 64 | " [0.+1.j, 0.+0.j]]) = Y\n", 65 | "tensor([[ 1.+0.j, 0.+0.j],\n", 66 | " [ 0.+0.j, -1.+0.j]]) = Z\n", 67 | "tensor([[ 0.7071+0.j, 0.7071+0.j],\n", 68 | " [ 0.7071+0.j, -0.7071+0.j]]) = H\n", 69 | "tensor([[1.+0.j, 0.+0.j],\n", 70 | " [0.+0.j, 0.+0.j]]) = proj_0\n", 71 | "tensor([[0.+0.j, 0.+0.j],\n", 72 | " [0.+0.j, 1.+0.j]]) = proj_1\n", 73 | "\n", 74 | "tensor([2.+0.j, 1.+0.j]) = final state vector\n", 75 | "\n" 76 | ] 77 | } 78 | ], 79 | "source": [ 80 | "num_qubits = 1\n", 81 | "dim = 2**num_qubits\n", 82 | "\n", 83 | "state_vector = torch.arange(dim) + 1 # state_vector must be normalized\n", 84 | "#state_vector = state_vector.reshape(-1,1)\n", 85 | "qc = quantum_circuit(num_qubits = num_qubits, state_vector = state_vector)\n", 86 | "print(qc.state_vector, '= initial state vector\\n')\n", 87 | "\n", 88 | "print(qc.n, '= n')\n", 89 | "print(qc.dim, '= dim\\n')\n", 90 | "print(qc.I, '= I')\n", 91 | "print(qc.x_matrix, '= X')\n", 92 | "print(qc.y_matrix, '= Y')\n", 93 | "print(qc.z_matrix, '= Z')\n", 94 | "\n", 95 | "print(qc.h_matrix, '= H')\n", 96 | "\n", 97 | "print(qc.proj_0, '= proj_0')\n", 98 | "print(qc.proj_1, '= proj_1\\n')\n", 99 | "\n", 100 | "\n", 101 | "qc.x(0)\n", 102 | "#qc.y(0)\n", 103 | "#qc.z(2)\n", 104 | "\n", 105 | "print(qc.state_vector, '= final state vector\\n')\n", 106 | "\n" 107 | ] 108 | }, 109 | { 110 | "cell_type": "markdown", 111 | "id": "b4fa3fc7", 112 | "metadata": {}, 113 | "source": [ 114 | "## H gate" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": 3, 120 | "id": "49b82f13", 121 | "metadata": {}, 122 | "outputs": [ 123 | { 124 | "name": "stdout", 125 | "output_type": "stream", 126 | "text": [ 127 | "tensor([[1.+0.j],\n", 128 | " [0.+0.j],\n", 129 | " [0.+0.j],\n", 130 | " [0.+0.j],\n", 131 | " [0.+0.j],\n", 132 | " [0.+0.j],\n", 133 | " [0.+0.j],\n", 134 | " [0.+0.j]]) = initial state vector\n", 135 | "\n", 136 | "tensor([[0.5000+0.j],\n", 137 | " [0.0000+0.j],\n", 138 | " [0.5000+0.j],\n", 139 | " [0.0000+0.j],\n", 140 | " [0.5000+0.j],\n", 141 | " [0.0000+0.j],\n", 142 | " [0.5000+0.j],\n", 143 | " [0.0000+0.j]]) = final state vector\n", 144 | "\n" 145 | ] 146 | } 147 | ], 148 | "source": [ 149 | "num_qubits = 3\n", 150 | "dim = 2**num_qubits\n", 151 | "\n", 152 | "qc = quantum_circuit(num_qubits = num_qubits)\n", 153 | "print(qc.state_vector, '= initial state vector\\n')\n", 154 | "\n", 155 | "qc.h(0)\n", 156 | "qc.h(1)\n", 157 | "print(qc.state_vector, '= final state vector\\n')" 158 | ] 159 | }, 160 | { 161 | "cell_type": "markdown", 162 | "id": "ac7da2a2", 163 | "metadata": {}, 164 | "source": [ 165 | "## Rx, Ry, Rz gates" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": 4, 171 | "id": "7aeac4d1", 172 | "metadata": {}, 173 | "outputs": [ 174 | { 175 | "name": "stdout", 176 | "output_type": "stream", 177 | "text": [ 178 | "tensor([[1.+0.j],\n", 179 | " [0.+0.j],\n", 180 | " [0.+0.j],\n", 181 | " [0.+0.j]]) = initial state vector\n", 182 | "\n", 183 | "[tensor(0.9394, grad_fn=), tensor(0.3429, grad_fn=)] \n", 184 | "\n", 185 | "[tensor(0.7649, grad_fn=), tensor(0.6442, grad_fn=)] \n", 186 | "\n", 187 | "tensor([[0.9394+0.j],\n", 188 | " [0.0000+0.j],\n", 189 | " [0.3429+0.j],\n", 190 | " [0.0000+0.j]], grad_fn=) = final state vector\n", 191 | "\n" 192 | ] 193 | } 194 | ], 195 | "source": [ 196 | "num_qubits = 2\n", 197 | "dim = 2**num_qubits\n", 198 | "\n", 199 | "\n", 200 | "qc = quantum_circuit(num_qubits = num_qubits)\n", 201 | "print(qc.state_vector, '= initial state vector\\n')\n", 202 | "\n", 203 | "\n", 204 | "\n", 205 | "ang_ = torch.rand(1, requires_grad=True)\n", 206 | "ang=ang_[0]\n", 207 | "\n", 208 | "print([torch.cos(ang/2), torch.sin(ang/2)], '\\n')\n", 209 | "print([torch.cos(ang), torch.sin(ang)], '\\n')\n", 210 | "\n", 211 | "\n", 212 | "\n", 213 | "#qc.Rx(0, ang)\n", 214 | "\n", 215 | "\n", 216 | "qc.Ry(0, ang)\n", 217 | "\n", 218 | "#qc.x(0)\n", 219 | "#qc.Rz(0, ang)\n", 220 | "\n", 221 | "\n", 222 | "print(qc.state_vector, '= final state vector\\n')" 223 | ] 224 | }, 225 | { 226 | "cell_type": "markdown", 227 | "id": "e8962768", 228 | "metadata": {}, 229 | "source": [ 230 | "## general rotation R" 231 | ] 232 | }, 233 | { 234 | "cell_type": "code", 235 | "execution_count": 5, 236 | "id": "18c0b6f6", 237 | "metadata": {}, 238 | "outputs": [ 239 | { 240 | "name": "stdout", 241 | "output_type": "stream", 242 | "text": [ 243 | "tensor([[1.+0.j],\n", 244 | " [0.+0.j]]) = initial state vector\n", 245 | "\n", 246 | "[tensor(0.9846, grad_fn=), tensor(0.1748, grad_fn=)] \n", 247 | "\n", 248 | "[tensor(0.9389, grad_fn=), tensor(0.3443, grad_fn=)] \n", 249 | "\n", 250 | "tensor([[0.0000+0.0000j],\n", 251 | " [0.9389+0.3443j]], grad_fn=) = final state vector\n", 252 | "\n" 253 | ] 254 | } 255 | ], 256 | "source": [ 257 | "num_qubits = 1\n", 258 | "dim = 2**num_qubits\n", 259 | "\n", 260 | "\n", 261 | "qc = quantum_circuit(num_qubits = num_qubits)\n", 262 | "print(qc.state_vector, '= initial state vector\\n')\n", 263 | "\n", 264 | "ang_ = torch.rand(1, requires_grad=True)\n", 265 | "ang=ang_[0]\n", 266 | "\n", 267 | "\n", 268 | "print([torch.cos(ang/2), torch.sin(ang/2)], '\\n')\n", 269 | "print([torch.cos(ang), torch.sin(ang)], '\\n')\n", 270 | "zero = torch.tensor(0.0)\n", 271 | "\n", 272 | "\n", 273 | "theta = zero #ang \n", 274 | "phi = zero #ang\n", 275 | "lamda = ang #zero\n", 276 | "\n", 277 | "\n", 278 | "\n", 279 | "qc.x(0)\n", 280 | "qc.R(0, theta=theta, phi=phi, lamda=lamda)\n", 281 | "print(qc.state_vector, '= final state vector\\n')\n", 282 | "\n", 283 | "\n" 284 | ] 285 | }, 286 | { 287 | "cell_type": "markdown", 288 | "id": "d18a9854", 289 | "metadata": {}, 290 | "source": [ 291 | "## CX, CZ gates" 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": 6, 297 | "id": "6cb1b102", 298 | "metadata": {}, 299 | "outputs": [ 300 | { 301 | "name": "stdout", 302 | "output_type": "stream", 303 | "text": [ 304 | "tensor([1.+0.j, 2.+0.j, 3.+0.j, 4.+0.j, 5.+0.j, 6.+0.j, 7.+0.j, 8.+0.j]) = initial state vector\n", 305 | "\n", 306 | "tensor([ 1.+0.j, 2.+0.j, 3.+0.j, 4.+0.j, 5.+0.j, 6.+0.j, -7.+0.j, -8.+0.j]) = final state vector\n", 307 | "\n" 308 | ] 309 | } 310 | ], 311 | "source": [ 312 | "num_qubits = 3\n", 313 | "dim = 2**num_qubits\n", 314 | "\n", 315 | "\n", 316 | "state_vector = torch.arange(dim) + 1 # state_vector must be normalized\n", 317 | "#state_vector = state_vector.reshape(-1,1)\n", 318 | "qc = quantum_circuit(num_qubits = num_qubits, state_vector = state_vector)\n", 319 | "print(qc.state_vector, '= initial state vector\\n')\n", 320 | "\n", 321 | "#qc.cx(control=0, target=1)\n", 322 | "qc.cz(control=0, target=1)\n", 323 | "\n", 324 | "print(qc.state_vector, '= final state vector\\n')" 325 | ] 326 | }, 327 | { 328 | "cell_type": "code", 329 | "execution_count": 7, 330 | "id": "4add728a", 331 | "metadata": {}, 332 | "outputs": [ 333 | { 334 | "name": "stdout", 335 | "output_type": "stream", 336 | "text": [ 337 | "tensor([[0.3959+0.j, 0.0264+0.j],\n", 338 | " [0.7602+0.j, 0.7937+0.j],\n", 339 | " [0.6448+0.j, 0.6465+0.j],\n", 340 | " [0.3658+0.j, 0.4472+0.j],\n", 341 | " [0.9422+0.j, 0.6814+0.j],\n", 342 | " [0.5886+0.j, 0.2620+0.j],\n", 343 | " [0.3211+0.j, 0.7520+0.j],\n", 344 | " [0.6750+0.j, 0.4294+0.j]]) = initial state vectors\n", 345 | "\n", 346 | "tensor([[ 0.3959+0.j, 0.0264+0.j],\n", 347 | " [ 0.7602+0.j, 0.7937+0.j],\n", 348 | " [ 0.6448+0.j, 0.6465+0.j],\n", 349 | " [ 0.3658+0.j, 0.4472+0.j],\n", 350 | " [ 0.9422+0.j, 0.6814+0.j],\n", 351 | " [ 0.5886+0.j, 0.2620+0.j],\n", 352 | " [-0.3211+0.j, -0.7520+0.j],\n", 353 | " [-0.6750+0.j, -0.4294+0.j]]) = final state vector\n", 354 | "\n" 355 | ] 356 | } 357 | ], 358 | "source": [ 359 | "num_qubits = 3\n", 360 | "dim = 2**num_qubits\n", 361 | "\n", 362 | "state_vector = torch.rand((dim, 2)) # state_vector must be normalized\n", 363 | "qc = quantum_circuit(num_qubits = num_qubits, state_vector = state_vector)\n", 364 | "print(qc.state_vector, '= initial state vectors\\n')\n", 365 | "\n", 366 | "\n", 367 | "#qc.cx(control=0, target=1)\n", 368 | "qc.cz(control=0, target=1)\n", 369 | "\n", 370 | "print(qc.state_vector, '= final state vector\\n')" 371 | ] 372 | }, 373 | { 374 | "cell_type": "markdown", 375 | "id": "885e524b", 376 | "metadata": {}, 377 | "source": [ 378 | "# cx_, cz_linear_layer" 379 | ] 380 | }, 381 | { 382 | "cell_type": "code", 383 | "execution_count": 8, 384 | "id": "b8534c5c", 385 | "metadata": {}, 386 | "outputs": [ 387 | { 388 | "name": "stdout", 389 | "output_type": "stream", 390 | "text": [ 391 | "3 4\n", 392 | "2 3\n", 393 | "1 2\n", 394 | "0 1\n" 395 | ] 396 | } 397 | ], 398 | "source": [ 399 | "nn = 5\n", 400 | "\n", 401 | "print(nn-2, nn-1)\n", 402 | "for i in range(nn - 3, -1, -1):\n", 403 | " print(i, i+1)" 404 | ] 405 | }, 406 | { 407 | "cell_type": "code", 408 | "execution_count": 9, 409 | "id": "88b94daf", 410 | "metadata": {}, 411 | "outputs": [ 412 | { 413 | "name": "stdout", 414 | "output_type": "stream", 415 | "text": [ 416 | "tensor([1.+0.j, 2.+0.j, 3.+0.j, 4.+0.j, 5.+0.j, 6.+0.j, 7.+0.j, 8.+0.j]) = initial state vector\n", 417 | "\n", 418 | "tensor([1.+0.j, 2.+0.j, 4.+0.j, 3.+0.j, 8.+0.j, 7.+0.j, 5.+0.j, 6.+0.j]) = final state vector\n", 419 | "\n" 420 | ] 421 | } 422 | ], 423 | "source": [ 424 | "num_qubits = 3\n", 425 | "dim = 2**num_qubits\n", 426 | "\n", 427 | "state_vector = torch.arange(dim) + 1 # state_vector must be normalized\n", 428 | "#state_vector = state_vector.reshape(-1,1)\n", 429 | "qc = quantum_circuit(num_qubits = num_qubits, state_vector = state_vector)\n", 430 | "print(qc.state_vector, '= initial state vector\\n')\n", 431 | "\n", 432 | "''' \n", 433 | "cx_linear_layer applies cx(n-1,n) ... cx(2,3) cx(1,2) cx(0,1) |state_vector>\n", 434 | "\n", 435 | "NOTE: First cx(0,1) will act on |state_vector>, then cx(1,2)\n", 436 | " And in the last cx(n-1,n) will act.\n", 437 | " order matter in case of cx\n", 438 | "'''\n", 439 | "\n", 440 | "qc.cx_linear_layer()\n", 441 | "\n", 442 | "#qc.cz_linear_layer()\n", 443 | "print(qc.state_vector, '= final state vector\\n')" 444 | ] 445 | }, 446 | { 447 | "cell_type": "markdown", 448 | "id": "48954fe1", 449 | "metadata": {}, 450 | "source": [ 451 | "# Ry_layer" 452 | ] 453 | }, 454 | { 455 | "cell_type": "code", 456 | "execution_count": 10, 457 | "id": "0ab3be16", 458 | "metadata": {}, 459 | "outputs": [ 460 | { 461 | "name": "stdout", 462 | "output_type": "stream", 463 | "text": [ 464 | "tensor([[1.+0.j],\n", 465 | " [0.+0.j],\n", 466 | " [0.+0.j],\n", 467 | " [0.+0.j]]) = initial state vector\n", 468 | "\n", 469 | "ang = tensor([0.8070, 0.8190], requires_grad=True)\n", 470 | "ang = tensor([0.8070+0.j, 0.8190+0.j], grad_fn=)\n" 471 | ] 472 | }, 473 | { 474 | "data": { 475 | "text/plain": [ 476 | "tensor([[0.4724+0.j],\n", 477 | " [0.5053+0.j],\n", 478 | " [0.4932+0.j],\n", 479 | " [0.5275+0.j]], grad_fn=)" 480 | ] 481 | }, 482 | "execution_count": 10, 483 | "metadata": {}, 484 | "output_type": "execute_result" 485 | } 486 | ], 487 | "source": [ 488 | "num_qubits = 2\n", 489 | "dim = 2**num_qubits\n", 490 | "\n", 491 | "\n", 492 | "qc = quantum_circuit(num_qubits = num_qubits)\n", 493 | "print(qc.state_vector, '= initial state vector\\n')\n", 494 | "\n", 495 | "\n", 496 | "\n", 497 | "ang = torch.rand(num_qubits, dtype=torch.float, requires_grad=True)\n", 498 | "print('ang =',ang)\n", 499 | "ang = ang.to(torch.cfloat) # = torch.complex(real=ang, imag=torch.zeros(num_qubits))\n", 500 | "print('ang =',ang)\n", 501 | "\n", 502 | "\n", 503 | "\n", 504 | "qc.Ry_layer(ang)" 505 | ] 506 | }, 507 | { 508 | "cell_type": "code", 509 | "execution_count": null, 510 | "id": "779636d0", 511 | "metadata": {}, 512 | "outputs": [], 513 | "source": [] 514 | }, 515 | { 516 | "cell_type": "code", 517 | "execution_count": null, 518 | "id": "df53882f", 519 | "metadata": {}, 520 | "outputs": [], 521 | "source": [] 522 | }, 523 | { 524 | "cell_type": "code", 525 | "execution_count": null, 526 | "id": "892b33be", 527 | "metadata": {}, 528 | "outputs": [], 529 | "source": [] 530 | } 531 | ], 532 | "metadata": { 533 | "kernelspec": { 534 | "display_name": "Python 3 (ipykernel)", 535 | "language": "python", 536 | "name": "python3" 537 | }, 538 | "language_info": { 539 | "codemirror_mode": { 540 | "name": "ipython", 541 | "version": 3 542 | }, 543 | "file_extension": ".py", 544 | "mimetype": "text/x-python", 545 | "name": "python", 546 | "nbconvert_exporter": "python", 547 | "pygments_lexer": "ipython3", 548 | "version": "3.10.6" 549 | } 550 | }, 551 | "nbformat": 4, 552 | "nbformat_minor": 5 553 | } 554 | -------------------------------------------------------------------------------- /quantum_circuit_simulator.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import math 3 | 4 | 5 | 6 | def get_device(gpu_no): 7 | if torch.cuda.is_available(): 8 | return torch.device('cuda', gpu_no) 9 | else: 10 | return torch.device('cpu') 11 | 12 | 13 | 14 | 15 | 16 | 17 | class quantum_circuit: 18 | 19 | def __init__(self, num_qubits : int, state_vector = None, device = 'cuda', gpu_no = 0): 20 | 21 | """ 22 | Defines a quantum circuit object that stores the full state-vector (evolved through 23 | the unitary operations of a quantum circuit) of `num_qubits` number of qubits. 24 | 25 | Args: 26 | num_qubits (int): Number of qubits in the circuit. 27 | 28 | state_vector (torch.Tensor, optional): The full state vector of the quantum circuit. 29 | Defaults to None. If None is provided then the state 30 | vector is automatically initialized to the ket |0000...0>. 31 | 32 | device (str, optional): Device on which the state vector should be stored (CPU / GPU). 33 | Defaults to 'cuda' i.e. GPU. 34 | 35 | gpu_no (int, optional): If there are multiple GPUs then this parameter defines which 36 | GPU to use. Defaults to 0 i.e. the first device. 37 | """ 38 | #---------------------------------------------------------------------------------------- 39 | 40 | if device != 'cuda': 41 | self.device = torch.device(device) 42 | else: 43 | self.device = get_device(gpu_no) 44 | 45 | 46 | #---------------------------------------------------------------------------------------- 47 | 48 | 49 | self.n = num_qubits # number of qubits 50 | self.dim = 2**self.n # dimention of the n-qubit hilbert space 51 | 52 | 53 | #---------------------------------------------------------------------------------------- 54 | 55 | ''' 56 | state_vector can 57 | (1) either be a vector of shape (dim,) 58 | (2) either be a matrix of shape (dim, number of examples) 59 | ''' 60 | 61 | if state_vector is None: 62 | ''' Initialize the state-vector to |0000...0> ''' 63 | state_vector = torch.zeros(self.dim, device=self.device, dtype=torch.cfloat) 64 | state_vector[0] = 1 65 | self.state_vector = state_vector.reshape(-1,1) 66 | else: 67 | if state_vector.shape[0] == self.dim: 68 | ''' state_vector must be normalized ''' 69 | self.state_vector = state_vector.to(torch.cfloat) 70 | else: 71 | print('The dimension 2**n does NOT match the shape of the state vector. n is the number of qubits.') 72 | 73 | 74 | #---------------------------------------------------------------------------------------- 75 | 76 | # single qubit Pauli gates (matrices) : 77 | self.I = torch.tensor([[1, 0], [0, 1]], device=self.device, dtype=torch.cfloat) 78 | self.x_matrix = torch.tensor([[0., 1], [1, 0]], device=self.device, dtype=torch.cfloat) 79 | self.y_matrix = torch.tensor([[0, -1j], [1j, 0]], device=self.device, dtype=torch.cfloat) 80 | self.z_matrix = torch.tensor([[1, 0], [0, -1]], device=self.device, dtype=torch.cfloat) 81 | 82 | self.h_matrix = (1 / math.sqrt(2)) * torch.tensor([[1, 1], [1, -1]], device=self.device, dtype=torch.cfloat) 83 | 84 | 85 | # single qubit projectors : 86 | self.proj_0 = torch.tensor([[1, 0], [0, 0]], device=self.device, dtype=torch.cfloat) 87 | self.proj_1 = torch.tensor([[0, 0], [0, 1]], device=self.device, dtype=torch.cfloat) 88 | 89 | 90 | 91 | #====================================================================================================== 92 | 93 | 94 | 95 | def single_qubit_gate(self, target : int, gate : torch.Tensor): 96 | """ 97 | Applies a single qubit gate = I ⊗ I ⊗ ... ⊗ gate ⊗ ... ⊗ I 98 | 99 | Args: 100 | target (int): The qubit index on which the gate will be applied 101 | gate (torch.Tensor): The matrix representation of a single qubit gate 102 | 103 | Returns: 104 | The state vector of the full quantum circuit after applying the single qubit gate. 105 | 106 | """ 107 | 108 | if target < 0 or self.n <= target: 109 | print('0 <= traget <= num_qubits - 1 is NOT satisfied!') 110 | 111 | else: 112 | single_q_gate = torch.tensor(1, device=self.device, dtype=torch.cfloat) # initialize 113 | 114 | for k in range(self.n): 115 | if k == target: 116 | single_q_gate = torch.kron(single_q_gate, gate) 117 | else: 118 | single_q_gate = torch.kron(single_q_gate, self.I) 119 | 120 | #------------------------------------------------------ 121 | 122 | self.state_vector = torch.matmul(single_q_gate, self.state_vector) 123 | return self.state_vector 124 | 125 | 126 | 127 | 128 | def controlled_gate(self, control: int, target: int, gate : torch.Tensor): 129 | """ 130 | Applies a two-qubit controlled gate between the 'control` and `target` qubits. 131 | 132 | control_gate_part_0 = I ⊗ |0><0| ⊗ ... ⊗ I ⊗ ... ⊗ I 133 | control_gate_part_1 = I ⊗ |1><1| ⊗ ... ⊗ gate ⊗ ... ⊗ I SEE: the control is set to 1 134 | 135 | control_gate = control_gate_part_0 + control_gate_part_1 136 | 137 | 138 | Args: 139 | control (int): Control qubit index 140 | target (int): Target qubit index 141 | gate (torch.Tensor): The matrix representation of a single qubit gate 142 | 143 | Returns: 144 | The state vector of the full quantum circuit after applying the two-qubit gate. 145 | """ 146 | 147 | if control < 0 or self.n <= control: 148 | print('0 <= control <= num_qubits - 1 is NOT satisfied!') 149 | elif target < 0 or self.n <= target: 150 | print('0 <= target <= num_qubits - 1 is NOT satisfied!') 151 | elif control == target: 152 | print('control and traget qubits must be different!') 153 | else: 154 | control_gate_part_0 = torch.tensor(1, device=self.device, dtype=torch.cfloat) # initialize 155 | control_gate_part_1 = torch.tensor(1, device=self.device, dtype=torch.cfloat) 156 | 157 | for k in range(self.n): 158 | if k == control: 159 | control_gate_part_0 = torch.kron(control_gate_part_0, self.proj_0) 160 | control_gate_part_1 = torch.kron(control_gate_part_1, self.proj_1) 161 | elif k == target: 162 | control_gate_part_0 = torch.kron(control_gate_part_0, self.I) 163 | control_gate_part_1 = torch.kron(control_gate_part_1, gate) 164 | else: 165 | control_gate_part_0 = torch.kron(control_gate_part_0, self.I) 166 | control_gate_part_1 = torch.kron(control_gate_part_1, self.I) 167 | 168 | control_gate = control_gate_part_0 + control_gate_part_1 169 | 170 | self.state_vector = torch.matmul(control_gate, self.state_vector) 171 | return self.state_vector 172 | 173 | 174 | #====================================================================================================== 175 | 176 | def x(self, target : int): # Applies X gate (matrix) on the target qubit 177 | 'NOTE: 0 <= target <= num_qubits - 1' 178 | self.single_qubit_gate(target, self.x_matrix) 179 | 180 | 181 | def y(self, target : int): 182 | self.single_qubit_gate(target, self.y_matrix) 183 | 184 | 185 | def z(self, target : int): 186 | self.single_qubit_gate(target, self.z_matrix) 187 | 188 | 189 | def h(self, target : int): # Applies Hadamard gate (matrix) on the target qubit 190 | self.single_qubit_gate(target, self.h_matrix) 191 | 192 | 193 | #====================================================================================================== 194 | 195 | 196 | def Rx(self, target : int, theta): 197 | 198 | """ 199 | Applies Rx gate (rotation around x axis) on the target qubit 200 | 201 | Args: 202 | theta (torch.Tensor): Angle by which the qubit should be rotated around X axis. 203 | Usually a tunable parameter is passed. 204 | 205 | target (int): Qubit index on which the Rx gate will be applied. 206 | NOTE: 0 <= target <= num_qubits - 1 207 | """ 208 | 209 | co = torch.cos(theta / 2) 210 | si = torch.sin(theta / 2) 211 | self.Rx_matrix = torch.stack([torch.stack([co, -1j*si]), torch.stack([-1j*si, co])]) 212 | 213 | self.single_qubit_gate(target, self.Rx_matrix) 214 | 215 | 216 | 217 | 218 | def Ry(self, target : int, theta): #like Rx, Ry gate applies (rotation around y axis) on the target qubit 219 | 220 | co = torch.cos(theta / 2) 221 | si = torch.sin(theta / 2) 222 | self.Ry_matrix = torch.stack([torch.stack([co, -si]), torch.stack([si, co])]) 223 | 224 | self.single_qubit_gate(target, self.Ry_matrix) 225 | 226 | 227 | 228 | 229 | 230 | def Rz(self, target : int, theta): #like Rx, Ry gate applies (rotation around z axis) on the target qubit 231 | 232 | exp_theta = torch.exp( 1j*theta ) 233 | zero = torch.tensor(0) 234 | one = torch.tensor(1) 235 | self.Rz_matrix = torch.stack([torch.stack([one, zero]), torch.stack([zero, exp_theta])]) 236 | 237 | self.single_qubit_gate(target, self.Rz_matrix) 238 | 239 | 240 | 241 | 242 | def R(self, target : int, theta, phi, lamda): 243 | """ 244 | Applies general rotation to the target qubit 245 | 246 | Args: 247 | theta, phi and lamda (torch.Tensor): The Euler angles which define a general rotation around Bloch sphere. 248 | 249 | target (int): Qubit index on which the gate will be applied. 250 | """ 251 | 252 | a = torch.cos(theta / 2) 253 | b = - torch.exp(1j * lamda) * torch.sin(theta / 2) 254 | c = torch.exp(1j * phi) * torch.sin(theta / 2) 255 | d = torch.exp(1j * (phi + lamda)) * torch.cos(theta / 2) 256 | self.R_matrix = torch.stack([torch.stack([a, b]), torch.stack([c, d])]) 257 | 258 | self.single_qubit_gate(target, self.R_matrix) 259 | 260 | 261 | #====================================================================================================== 262 | 263 | 264 | def Ry_layer(self, angs: torch.Tensor): 265 | ''' 266 | Applies tensor-product of single-qubit rotations around y-axis 267 | ''' 268 | 269 | cos, sin = torch.cos(angs[0]), torch.sin(angs[0]) 270 | ''' 271 | Use torch.stack otherwise computation graph will be broken (or will not begin). 272 | And, grad will be gone (will not be stored). 273 | ''' 274 | rot = torch.stack([torch.stack([cos, -sin]), torch.stack([sin, cos])]) 275 | 276 | for i in range(1, len(angs)): # one angles for each qubit 277 | cos, sin = torch.cos(angs[i]), torch.sin(angs[i]) 278 | rot = torch.kron(rot, torch.stack([torch.stack([cos, -sin]), torch.stack([sin, cos])])) 279 | 280 | #-------------------------------------------------------------------------- 281 | 282 | self.state_vector = torch.matmul(rot, self.state_vector) # rotated state vector 283 | return self.state_vector 284 | 285 | 286 | 287 | 288 | 289 | def Rz_layer(self, angs: torch.Tensor): #like Ry_layer, Rz_layer acts 290 | 291 | exp_ang = torch.exp( 1j*angs[0] ) 292 | zero = torch.tensor(0) 293 | one = torch.tensor(1) 294 | 295 | rot = torch.stack([torch.stack([one, zero]), torch.stack([zero, exp_ang])]) 296 | 297 | for i in range(1, len(angs)): # one angles for each qubit 298 | exp_ang = torch.exp( 1j*angs[i] ) 299 | rot = torch.kron(rot, torch.stack([torch.stack([one, zero]), torch.stack([zero, exp_ang])]) ) 300 | 301 | #-------------------------------------------------------------------------- 302 | 303 | self.state_vector = torch.matmul(rot, self.state_vector) # rotated state vector 304 | return self.state_vector 305 | 306 | 307 | 308 | #====================================================================================================== 309 | 310 | def cx(self, control: int, target: int): 311 | """ 312 | Applies controlled-X gate = I ⊗ |0><0| ⊗ ... ⊗ I ⊗ ... ⊗ I + 313 | I ⊗ |1><1| ⊗ ... ⊗ X ⊗ ... ⊗ I 314 | 315 | Args: 316 | control (int): Control qubit index 317 | target (int): Target qubit index 318 | """ 319 | self.controlled_gate(control, target, self.x_matrix) 320 | 321 | 322 | 323 | 324 | def cz(self, control: int, target: int): #like cx, cz gate acts 325 | self.controlled_gate(control, target, self.z_matrix) 326 | 327 | 328 | 329 | #====================================================================================================== 330 | 331 | 332 | def cx_linear_layer(self): 333 | ''' 334 | Applies cx(n-1,n) ... cx(2,3) cx(1,2) cx(0,1) |state_vector> 335 | 336 | NOTE: First cx(0,1) will act on |state_vector>, then cx(1,2) 337 | And in the last cx(n-1,n) will act. 338 | order matter in case of cx 339 | ''' 340 | 341 | self.controlled_gate(self.n - 2, self.n - 1, self.x_matrix) 342 | for i in range(self.n - 3, -1, -1): 343 | self.controlled_gate(i, i+1, self.x_matrix) 344 | 345 | 346 | 347 | def cz_linear_layer(self): #like cx_linear_layer, cz_linear_layer acts 348 | self.controlled_gate(self.n - 2, self.n - 1, self.z_matrix) 349 | for i in range(self.n - 3, -1, -1): 350 | self.controlled_gate(i, i+1, self.z_matrix) 351 | 352 | 353 | #====================================================================================================== 354 | 355 | def probabilities(self): 356 | """ 357 | probabilities obtained in the z-measurement (computational basis) on the state vector 358 | 359 | Returns: A torch.Tensor of the size same as the state vector 360 | """ 361 | 362 | return self.state_vector.conj() * self.state_vector 363 | 364 | 365 | 366 | 367 | 368 | --------------------------------------------------------------------------------