diff --git a/.gitignore b/.gitignore index dadee358..3c43393a 100644 --- a/.gitignore +++ b/.gitignore @@ -336,3 +336,5 @@ es_runs/ .vscode-upload.json mnist_data/ +.cursor/ +.github/ \ No newline at end of file diff --git a/examples/ICCAD22_tutorial/sec1_basic.ipynb b/examples/ICCAD22_tutorial/sec1_basic.ipynb index 6ac74155..0cd2c2be 100644 --- a/examples/ICCAD22_tutorial/sec1_basic.ipynb +++ b/examples/ICCAD22_tutorial/sec1_basic.ipynb @@ -1,45 +1,20 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "provenance": [], - "collapsed_sections": [], - "toc_visible": true - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "language_info": { - "name": "python" - }, - "accelerator": "GPU" - }, "cells": [ { "cell_type": "markdown", - "source": [ - "# **Setup**" - ], "metadata": { "id": "MX5Sdk7L9pfN", "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "# **Setup**" + ] }, { "cell_type": "code", - "source": [ - "print('Installing torchquantum...')\n", - "!git clone https://github.com/mit-han-lab/torchquantum.git\n", - "%cd /content/torchquantum\n", - "!pip install --editable . 1>/dev/null\n", - "!pip install matplotlib==3.1.3 1>/dev/null\n", - "%matplotlib inline\n", - "print('All required packages have been successfully installed!')" - ], + "execution_count": 1, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -50,107 +25,105 @@ "name": "#%%\n" } }, - "execution_count": 1, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Installing torchquantum...\n", "Cloning into 'torchquantum'...\n", - "remote: Enumerating objects: 11836, done.\u001B[K\n", - "remote: Counting objects: 100% (726/726), done.\u001B[K\n", - "remote: Compressing objects: 100% (306/306), done.\u001B[K\n", - "remote: Total 11836 (delta 435), reused 685 (delta 405), pack-reused 11110\u001B[K\n", + "remote: Enumerating objects: 11836, done.\u001b[K\n", + "remote: Counting objects: 100% (726/726), done.\u001b[K\n", + "remote: Compressing objects: 100% (306/306), done.\u001b[K\n", + "remote: Total 11836 (delta 435), reused 685 (delta 405), pack-reused 11110\u001b[K\n", "Receiving objects: 100% (11836/11836), 33.59 MiB | 25.33 MiB/s, done.\n", "Resolving deltas: 100% (6593/6593), done.\n", "/content/torchquantum\n", - "\u001B[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", - "torchquantum 0.1.2 requires matplotlib>=3.3.2, but you have matplotlib 3.1.3 which is incompatible.\u001B[0m\n", + "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", + "torchquantum 0.1.2 requires matplotlib>=3.3.2, but you have matplotlib 3.1.3 which is incompatible.\u001b[0m\n", "All required packages have been successfully installed!\n" ] } + ], + "source": [ + "print('Installing torchquantum...')\n", + "!git clone https://github.com/mit-han-lab/torchquantum.git\n", + "%cd /content/torchquantum\n", + "!pip install --editable . 1>/dev/null\n", + "!pip install matplotlib==3.1.3 1>/dev/null\n", + "%matplotlib inline\n", + "print('All required packages have been successfully installed!')" ] }, { "cell_type": "code", - "source": [ - "import torchquantum as tq\n", - "import torchquantum.functional as tqf\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "import torch" - ], + "execution_count": 1, "metadata": { "id": "10RsI2oaDXEI", "pycharm": { "name": "#%%\n" } }, - "execution_count": 2, - "outputs": [] + "outputs": [], + "source": [ + "import torchquantum as tq\n", + "import torchquantum.functional as tqf\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "import torch" + ] }, { "cell_type": "markdown", - "source": [ - "# **1. TorchQuantum basic operations**" - ], "metadata": { "id": "I3Vi2I17jo86", "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "# **1. TorchQuantum basic operations**" + ] }, { "cell_type": "markdown", - "source": [ - "## 1.2 TorchQuantum Operations" - ], "metadata": { "id": "Fu9gqh2XNeqM", "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "## 1.2 TorchQuantum Operations" + ] }, { "cell_type": "markdown", - "source": [ - "tq.QuantumDevice Usage" - ], "metadata": { "id": "abV1dwlE0Ksq", "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "tq.QuantumDevice Usage" + ] }, { "cell_type": "markdown", - "source": [ - "Method 1 of using quantum gates through torchquantum.functional" - ], "metadata": { "id": "DQHkBqqW0d4C", "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "Method 1 of using quantum gates through torchquantum.functional" + ] }, { "cell_type": "code", - "source": [ - "q_dev = tq.QuantumDevice(n_wires=1)\n", - "q_dev.reset_states(bsz=1)\n", - "print(f\"all zero state: {q_dev}\")\n", - "tqf.h(q_dev, wires=0)\n", - "print(f\"after h gate: {q_dev}\")\n", - "\n", - "tqf.rx(q_dev, wires=0, params=[0.3])\n", - "\n", - "print(f\"after rx gate: {q_dev}\")" - ], + "execution_count": 16, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -161,40 +134,32 @@ "name": "#%%\n" } }, - "execution_count": 16, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "all zero state: QuantumDevice 1 wires with states: tensor([[1.+0.j, 0.+0.j]])\n", "after h gate: QuantumDevice 1 wires with states: tensor([[0.7071+0.j, 0.7071+0.j]])\n", "after rx gate: QuantumDevice 1 wires with states: tensor([[0.6992-0.1057j, 0.6992-0.1057j]])\n" ] } - ] - }, - { - "cell_type": "code", + ], "source": [ - "# method 2 of using tq.Operator\n", + "q_dev = tq.QuantumDevice(n_wires=1)\n", "q_dev.reset_states(bsz=1)\n", "print(f\"all zero state: {q_dev}\")\n", - "\n", - "h_gate = tq.H()\n", - "h_gate(q_dev, wires=0)\n", - "\n", + "tqf.h(q_dev, wires=0)\n", "print(f\"after h gate: {q_dev}\")\n", "\n", - "rx_gate = tq.RX(has_params=True, init_params=[0.3])\n", - "\n", - "rx_gate(q_dev, wires=0)\n", - "\n", - "print(f\"after rx gate: {q_dev}\")\n", - "bitstring = tq.measure(q_dev, n_shots=1024, draw_id=0)\n", + "tqf.rx(q_dev, wires=0, params=[0.3])\n", "\n", - "print(bitstring)" - ], + "print(f\"after rx gate: {q_dev}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 19, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -206,11 +171,10 @@ "name": "#%%\n" } }, - "execution_count": 19, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "all zero state: QuantumDevice 1 wires with states: tensor([[1.+0.j, 0.+0.j]])\n", "after h gate: QuantumDevice 1 wires with states: tensor([[0.7071+0.j, 0.7071+0.j]])\n", @@ -218,39 +182,48 @@ ] }, { - "output_type": "display_data", "data": { + "image/png": "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", "text/plain": [ "
" - ], - "image/png": "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\n" + ] }, "metadata": { "needs_background": "light" - } + }, + "output_type": "display_data" }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "[OrderedDict([('0', 503), ('1', 521)])]\n" ] } + ], + "source": [ + "# method 2 of using tq.Operator\n", + "q_dev.reset_states(bsz=1)\n", + "print(f\"all zero state: {q_dev}\")\n", + "\n", + "h_gate = tq.H()\n", + "h_gate(q_dev, wires=0)\n", + "\n", + "print(f\"after h gate: {q_dev}\")\n", + "\n", + "rx_gate = tq.RX(has_params=True, init_params=[0.3])\n", + "\n", + "rx_gate(q_dev, wires=0)\n", + "\n", + "print(f\"after rx gate: {q_dev}\")\n", + "bitstring = tq.measure(q_dev, n_shots=1024, draw_id=0)\n", + "\n", + "print(bitstring)" ] }, { "cell_type": "code", - "source": [ - "# tq.QuantumState to prepare a EPR pair\n", - "\n", - "q_state = tq.QuantumState(n_wires=2)\n", - "q_state.h(wires=0)\n", - "q_state.cnot(wires=[0, 1])\n", - "\n", - "print(q_state)\n", - "bitstring = tq.measure(q_state, n_shots=1024, draw_id=0)\n", - "print(bitstring)\n" - ], + "execution_count": 20, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -262,57 +235,50 @@ "name": "#%%\n" } }, - "execution_count": 20, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "QuantumState 2 wires \n", " state: tensor([[0.7071+0.j, 0.0000+0.j, 0.0000+0.j, 0.7071+0.j]])\n" ] }, { - "output_type": "display_data", "data": { + "image/png": "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", "text/plain": [ "
" - ], - "image/png": "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\n" + ] }, "metadata": { "needs_background": "light" - } + }, + "output_type": "display_data" }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "[OrderedDict([('00', 492), ('01', 0), ('10', 0), ('11', 532)])]\n" ] } - ] - }, - { - "cell_type": "code", + ], "source": [ - "# tq.QuantumState\n", - "q_state = tq.QuantumState(n_wires=3)\n", - "q_state.x(wires=1)\n", - "q_state.rx(wires=2, params=0.6 * np.pi)\n", - "print(q_state)\n", - "\n", - "q_state.ry(wires=0, params=0.3 * np.pi)\n", - "\n", - "q_state.qubitunitary(wires=1, params=[[0, 1j], [-1j, 0]])\n", + "# tq.QuantumState to prepare a EPR pair\n", "\n", + "q_state = tq.QuantumState(n_wires=2)\n", + "q_state.h(wires=0)\n", "q_state.cnot(wires=[0, 1])\n", "\n", "print(q_state)\n", "bitstring = tq.measure(q_state, n_shots=1024, draw_id=0)\n", - "\n", - "print(bitstring)" - ], + "print(bitstring)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 21, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -324,11 +290,10 @@ "name": "#%%\n" } }, - "execution_count": 21, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "QuantumState 3 wires \n", " state: tensor([[0.0000+0.0000j, 0.0000+0.0000j, 0.5878+0.0000j, 0.0000-0.8090j,\n", @@ -339,48 +304,59 @@ ] }, { - "output_type": "display_data", "data": { + "image/png": "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", "text/plain": [ "
" - ], - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAXcAAAEfCAYAAAC6Z4bJAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nO3de7wdZX3v8c9XiIBghUCMkASCEFGsx4gRwUsPghdAK9QDGrQIFBvb4qmIt6DtAVtoY9Ui1qqNgkRBLgWVFNCCXETUAAFCJFwkSGISLtkC4VIESfieP+bZZGVl7b3Xvq6d2d/367Vee+Z5npn5zVpr/2bWMzfZJiIi6uV5nQ4gIiKGXpJ7REQNJblHRNRQkntERA0luUdE1FCSe0REDSW5j0KSzpJ0Shl+s6S7hnDeP5J0VBk+WtJ1QzjvD0i6fKjm14/lvlHS3ZKekHToSC9/NJC0TNJbe6h77vvUQ/0Tkl46jLE9952LkbN5pwOI3tn+GbBHX+0knQzsbvvP+5jfQUMRl6SpwL3AONtry7zPAc4Zivn30z8AX7V9egeWvcmzvU33sKSzgJW2/66v6Ub6Oxf9kz33MUKVun7euwBLOh3EUJFUi52umn/nRr288aOApNdIulnS45LOB7ZsqNtP0sqG8U9LWlXa3iXpAEkHAp8B3ld+Yt9a2l4j6VRJPweeBF5ayj604eL1VUmPSrpT0gENFRv81Jd0sqSzy+i15e+assx9m7t5JL1B0o1l3jdKekND3TWS/lHSz8u6XC5ph17eo7+UtFTSw5LmS9qplN8DvBT4rxLHFi2mXSbpk5IWS/ofSWdImli6Cx6X9BNJ2zW030fSLyStkXSrpP0a6o6RdEeZ7jeSPtxQt4OkS8p0D0v6WXdyk2RJuze0bex620/SyvLZPgB8W9LzJM2WdI+khyRdIGl8w/RHSlpe6j7b0/vWYAdJV5S4fyppl4Z5WdLukmYBHwA+Vd7L/yr1Q/ad6/6OSPqipEck3SvpoIZYdpV0bcPn8u/d3zlJW0o6u6zzmvKdmtjGuo9NtvPq4At4PrAc+BgwDjgMeAY4pdTvR/UzGarumRXATmV8KrBbGT4ZOLtp3tcAvwVeSdUFN66UfajUHw2sbVj2+4BHgfGlfhnw1ob5PbeMsmwDmzfUHw1cV4bHA48AR5ZlH1HGt2+I7R7gZcBWZXxOD+/R/sDvgL2ALYB/A65tqN8gzhbTLwMWABOBScBq4GbgNVQb0quAk0rbScBDwMFUOz9vK+MTSv07gd0AAf+bKoHtVer+GfhGeS/HAW8GVOpM1YXRHdNZTZ/xWuDzZf22Aj5aYp5cyv4DOLe03xN4AviTUvevZfqW70FZ1uMN7U/v/pyaY2uMaxi/c88AfwlsBvw1cF/D+/RL4ItU/xdvAh5j/Xfuw8B/AS8o074W+KNO/w+P1lf23DtvH6p/gC/bfsb2hcCNPbRdR/XPuaekcbaX2b6nj/mfZXuJ7bW2n2lRv7ph2ecDd1ElsMF6J3C37e+WZZ8L3An8aUObb9v+te3fAxcA03uY1weAM23fbPtp4ERgX1X9/u36N9sP2l4F/Ay43vYttp8CfkCV6AH+HLjM9mW2n7V9BbCQKtlj+1Lb97jyU+ByqiQOVdLaEdilvJ8/c8lKbXiWagPzdHk//gr4rO2VZZ1PBg5T1WVzGHCJ7WtL3d+X6XtzaUP7z1K9f1PaiGs4vnPLbX/T9jpgHtV7NlHSzsDrgP9n+w+2rwPmN0z3DLA91YZone2bbD/WxjqMSUnunbcTsKopCSxv1dD2UuB4qn/01ZLO6+6e6MWKPupbLbuvebZjJzZej+VUe8bdHmgYfhLYhtY2mJftJ6j2pif10L6VBxuGf99ivHvZuwCHl5/9ayStodqD3BFA0kGSFpRulzVUSb+7O+kLwFLg8tJlM7sf8XWVDU23XYAfNMRwB1WinUj1fjz3udr+H6r3ozeN7Z8AHqaNz3mYvnPPfe62nyyD25R4Hm4oa57Xd4H/Bs6TdJ+kf5E0rq91GKuS3DvvfmCSJDWU7dxTY9vfs/0mqn9+U/2Upwy3nKSP5bda9n1l+H+ofgJ3e0k/5ntfibHRzsCqPqbrc16StqbagxvIvPqyAviu7W0bXlvbnlP68y+i6jaYaHtb4DKqLhpsP27747ZfCrwbOEHrj2E8Sc/vJWz8fq4ADmqKY8vyy+N+4Lm9bkkvoHo/etPYfhuqbrP7WrTb6HMdhu9cT+4Hxpf16fZc3OXX0Ods7wm8AXgX8MEBLqv2ktw775dU/aV/K2mcpPcAe7dqKGkPSfuXJPMU1R5n98/xB4Gp6v/ZCS9uWPbhwCuoEhbAImBmqZtB1R3Qrassu6fzoy8DXibp/ZI2l/Q+qr7iS/oZH8C5wDGSppd1/yeqbpVlA5hXX84G/lTSOyRtVg7i7SdpMlU/8BZU6762HAh8e/eEkt5VDkyK6tjFOtZ/PouA95d5HkjVX9+bbwCndh/4lDRB0iGl7kLgXZLeJOn5VKeC9vW5H9zQ/h+BBbZb7WE/SMNnOkzfuZZsL6fqAjtZ0vMl7UtDN56kt0h6laTNqPrin6Hv7qgxK8m9w2z/AXgP1YGmh6kOan6/h+ZbAHOoDi4+QJWYTyx1/1n+PiTp5n6EcD0wrczzVOAw290/8f+e6uDhI8DngO81xP1kaf/z0nWwT9N6PUS1Z/Vxqi6DTwHvsv27fsTWPa+flFguotq72w2Y2d/5tLmsFcAhVGeCdFHtQX8SeJ7tx4G/pTo+8AjwfjbsE54G/ITqYOcvga/ZvrrUfZQqUa2hOobwwz5COb3M+3JJj1MdXH19iXEJcBzV53F/iWVlD/Pp9j3gJKrv2Gupji20cgZV//oaST9keL5zvfkAsC/Vd+YU4Hzg6VL3EqoN22NU3VQ/peqqiRa6j1BHRIw6qk4NvtP2SZ2OZVOTPfeIGDUkvU7SbqrO8z+Q6ldUX79yooVaXAkXEbXxEqpuye2pupr+2vYtnQ1p05RumYiIGkq3TEREDY2KbpkddtjBU6dO7XQYERGblJtuuul3tie0qhsVyX3q1KksXLiw02FERGxSJLW8mh3SLRMRUUtJ7hERNZTkHhFRQ0nuERE1lOQeEVFDSe4RETWU5B4RUUNJ7hERNZTkHhFRQ6PiCtW6mjr70o4uf9mcoXjOdURsirLnHhFRQ0nuERE1lOQeEVFDbSf38tT2WyRdUsZ3lXS9pKWSzi9PVUfSFmV8aamfOjyhR0RET/qz5/5RqieOd/s8cJrt3amevn5sKT8WeKSUn1baRUTECGoruUuaDLwT+FYZF7A/cGFpMg84tAwfUsYp9QeU9hERMULa3XP/MvAp4Nkyvj2wxvbaMr4SmFSGJwErAEr9o6X9BiTNkrRQ0sKurq4Bhh8REa30mdwlvQtYbfumoVyw7bm2Z9ieMWFCy6dERUTEALVzEdMbgXdLOhjYEvgj4HRgW0mbl73zycCq0n4VMAVYKWlz4EXAQ0MeeURE9KjPPXfbJ9qebHsqMBO4yvYHgKuBw0qzo4CLy/D8Mk6pv8q2hzTqiIjo1WDOc/80cIKkpVR96meU8jOA7Uv5CcDswYUYERH91a97y9i+BrimDP8G2LtFm6eAw4cgtoiIGKBcoRoRUUNJ7hERNZTkHhFRQ0nuERE1lOQeEVFDSe4RETWU5B4RUUNJ7hERNZTkHhFRQ0nuERE1lOQeEVFDSe4RETWU5B4RUUNJ7hERNZTkHhFRQ0nuERE11M4DsreUdIOkWyUtkfS5Un6WpHslLSqv6aVckr4iaamkxZL2Gu6ViIiIDbXzJKangf1tPyFpHHCdpB+Vuk/avrCp/UHAtPJ6PfD18jciIkZIOw/Itu0nyui48urtgdeHAN8p0y0AtpW04+BDjYiIdrXV5y5pM0mLgNXAFbavL1Wnlq6X0yRtUcomASsaJl9ZyprnOUvSQkkLu7q6BrEKERHRrK3kbnud7enAZGBvSX8MnAi8HHgdMB74dH8WbHuu7Rm2Z0yYMKGfYUdERG/6dbaM7TXA1cCBtu8vXS9PA98G9i7NVgFTGiabXMoiImKEtHO2zARJ25bhrYC3AXd296NLEnAocFuZZD7wwXLWzD7Ao7bvH5boIyKipXbOltkRmCdpM6qNwQW2L5F0laQJgIBFwF+V9pcBBwNLgSeBY4Y+7IiI6E2fyd32YuA1Lcr376G9geMGH1pERAxUrlCNiKihJPeIiBpKco+IqKEk94iIGkpyj4iooST3iIgaSnKPiKihJPeIiBpKco+IqKEk94iIGkpyj4iooST3iIgaSnKPiKihJPeIiBpKco+IqKEk94iIGmrnMXtbSrpB0q2Slkj6XCnfVdL1kpZKOl/S80v5FmV8aamfOryrEBERzdrZc38a2N/2q4HpwIHl2aifB06zvTvwCHBsaX8s8EgpP620i4iIEdRncnfliTI6rrwM7A9cWMrnUT0kG+CQMk6pP6A8RDsiIkZIW33ukjaTtAhYDVwB3AOssb22NFkJTCrDk4AVAKX+UWD7FvOcJWmhpIVdXV2DW4uIiNhAW8nd9jrb04HJwN7Aywe7YNtzbc+wPWPChAmDnV1ERDTo19kyttcAVwP7AttK2rxUTQZWleFVwBSAUv8i4KEhiTYiItrSztkyEyRtW4a3At4G3EGV5A8rzY4CLi7D88s4pf4q2x7KoCMioneb992EHYF5kjaj2hhcYPsSSbcD50k6BbgFOKO0PwP4rqSlwMPAzGGIOyIietFncre9GHhNi/LfUPW/N5c/BRw+JNFFRMSA5ArViIgaSnKPiKihJPeIiBpKco+IqKEk94iIGkpyj4iooST3iIgaauciplFt6uxLO7r8ZXPe2dHlR0S0kj33iIgaSnKPiKihJPeIiBpKco+IqKEk94iIGkpyj4iooST3iIgaSnKPiKihdh6zN0XS1ZJul7RE0kdL+cmSVklaVF4HN0xzoqSlku6S9I7hXIGIiNhYO1eorgU+bvtmSS8EbpJ0Rak7zfYXGxtL2pPq0XqvBHYCfiLpZbbXDWXgERHRsz733G3fb/vmMvw41cOxJ/UyySHAebaftn0vsJQWj+OLiIjh068+d0lTqZ6nen0p+oikxZLOlLRdKZsErGiYbCUtNgaSZklaKGlhV1dXvwOPiIietZ3cJW0DXAQcb/sx4OvAbsB04H7gS/1ZsO25tmfYnjFhwoT+TBoREX1oK7lLGkeV2M+x/X0A2w/aXmf7WeCbrO96WQVMaZh8cimLiIgR0s7ZMgLOAO6w/a8N5Ts2NPsz4LYyPB+YKWkLSbsC04Abhi7kiIjoSztny7wROBL4laRFpewzwBGSpgMGlgEfBrC9RNIFwO1UZ9oclzNlIiJGVp/J3fZ1gFpUXdbLNKcCpw4iroiIGIRcoRoRUUNJ7hERNZTkHhFRQ0nuERE1lOQeEVFDSe4RETWU5B4RUUNJ7hERNZTkHhFRQ0nuERE1lOQeEVFDSe4RETWU5B4RUUNJ7hERNZTkHhFRQ0nuERE11M5j9qZIulrS7ZKWSPpoKR8v6QpJd5e/25VySfqKpKWSFkvaa7hXIiIiNtTOnvta4OO29wT2AY6TtCcwG7jS9jTgyjIOcBDVc1OnAbOArw951BER0as+k7vt+23fXIYfB+4AJgGHAPNKs3nAoWX4EOA7riwAtm16mHZERAyzfvW5S5oKvAa4Hpho+/5S9QAwsQxPAlY0TLaylDXPa5akhZIWdnV19TPsiIjoTZ8PyO4maRvgIuB4249J65+ZbduS3J8F254LzAWYMWNGv6aNiBhKU2df2rFlL5vzzmGZb1t77pLGUSX2c2x/vxQ/2N3dUv6uLuWrgCkNk08uZRERMULaOVtGwBnAHbb/taFqPnBUGT4KuLih/IPlrJl9gEcbum8iImIEtNMt80bgSOBXkhaVss8Ac4ALJB0LLAfeW+ouAw4GlgJPAscMacQREdGnPpO77esA9VB9QIv2Bo4bZFwRETEIuUI1IqKGktwjImooyT0iooaS3CMiaijJPSKihpLcIyJqKMk9IqKGktwjImooyT0iooaS3CMiaijJPSKihpLcIyJqKMk9IqKGktwjImooyT0iooaS3CMiaqidx+ydKWm1pNsayk6WtErSovI6uKHuRElLJd0l6R3DFXhERPSsnT33s4ADW5SfZnt6eV0GIGlPYCbwyjLN1yRtNlTBRkREe/pM7ravBR5uc36HAOfZftr2vVTPUd17EPFFRMQADKbP/SOSFpdum+1K2SRgRUOblaVsI5JmSVooaWFXV9cgwoiIiGYDTe5fB3YDpgP3A1/q7wxsz7U9w/aMCRMmDDCMiIhoZUDJ3faDttfZfhb4Juu7XlYBUxqaTi5lERExggaU3CXt2DD6Z0D3mTTzgZmStpC0KzANuGFwIUZERH9t3lcDSecC+wE7SFoJnATsJ2k6YGAZ8GEA20skXQDcDqwFjrO9bnhCj4iInvSZ3G0f0aL4jF7anwqcOpigIiJicPpM7hERQ2Hq7Es7tuxlc97ZsWV3Sm4/EBFRQ0nuERE1lOQeEVFDSe4RETWU5B4RUUNJ7hERNZTkHhFRQ0nuERE1lOQeEVFDSe4RETWU5B4RUUNJ7hERNZTkHhFRQ0nuERE1lOQeEVFDfSZ3SWdKWi3ptoay8ZKukHR3+btdKZekr0haKmmxpL2GM/iIiGitnT33s4ADm8pmA1fangZcWcYBDqJ6buo0YBbw9aEJMyIi+qPP5G77WuDhpuJDgHlleB5waEP5d1xZAGzb9DDtiIgYAQPtc59o+/4y/AAwsQxPAlY0tFtZyjYiaZakhZIWdnV1DTCMiIhoZdAHVG0b8ACmm2t7hu0ZEyZMGGwYERHRYKDJ/cHu7pbyd3UpXwVMaWg3uZRFRMQIGmhynw8cVYaPAi5uKP9gOWtmH+DRhu6biIgYIZv31UDSucB+wA6SVgInAXOACyQdCywH3luaXwYcDCwFngSOGYaYIyKiD30md9tH9FB1QIu2Bo4bbFARETE4uUI1IqKGktwjImooyT0iooaS3CMiaijJPSKihpLcIyJqKMk9IqKGktwjImooyT0iooaS3CMiaijJPSKihpLcIyJqKMk9IqKGktwjImooyT0iooaS3CMiaqjPh3X0RtIy4HFgHbDW9gxJ44HzganAMuC9th8ZXJgREdEfQ7Hn/hbb023PKOOzgSttTwOuLOMRETGChqNb5hBgXhmeBxw6DMuIiIheDDa5G7hc0k2SZpWyibbvL8MPABNbTShplqSFkhZ2dXUNMoyIiGg0qD534E22V0l6MXCFpDsbK21bkltNaHsuMBdgxowZLdtERMTADGrP3faq8nc18ANgb+BBSTsClL+rBxtkRET0z4CTu6StJb2wexh4O3AbMB84qjQ7Crh4sEFGRET/DKZbZiLwA0nd8/me7R9LuhG4QNKxwHLgvYMPMyIi+mPAyd32b4BXtyh/CDhgMEFFRMTg5ArViIgaSnKPiKihJPeIiBpKco+IqKEk94iIGkpyj4iooST3iIgaSnKPiKihJPeIiBpKco+IqKEk94iIGkpyj4iooST3iIgaSnKPiKihJPeIiBpKco+IqKFhS+6SDpR0l6SlkmYP13IiImJjw5LcJW0G/DtwELAncISkPYdjWRERsbHh2nPfG1hq+ze2/wCcBxwyTMuKiIgmsj30M5UOAw60/aEyfiTwetsfaWgzC5hVRvcA7hryQNqzA/C7Di27L4ltYBLbwCS2gelkbLvYntCqYsAPyB4s23OBuZ1afjdJC23P6HQcrSS2gUlsA5PYBma0xjZc3TKrgCkN45NLWUREjIDhSu43AtMk7Srp+cBMYP4wLSsiIpoMS7eM7bWSPgL8N7AZcKbtJcOxrCHQ8a6hXiS2gUlsA5PYBmZUxjYsB1QjIqKzcoVqREQNJblHRNRQkntERA2N2eQuabyk8Z2OIyJiOIyp5C5pZ0nnSeoCrgdukLS6lE3tbHSjn6SJkvYqr4mdjqcvkrbpdAwRnTKmzpaR9Evgy8CFtteVss2Aw4Hjbe/Tyfh6IulXtl/VweVPB74BvIj1F6NNBtYAf2P75k7F1htJv7W98yiIYyIwqYyusv1gJ+Ppi6RtbD/R4RhEdY+q59434AaP4oQl6eW27+x0HN3GWnK/2/a0/taNBEnv6akK+EZP948YCZIWAR+2fX1T+T7Af9h+dWciA0kn9FQFfNZ2x7reslEc8PLfDnwNuJsN37fdqd63yzsVW286/b4169i9ZTrkJklfA+YBK0rZFOAo4JaORVU5HzgHaLW13XKEY2m2dXNiB7C9QNLWnQiowT8BXwDWtqjrdLfjWfS8Ufw2MFo3ip3uzjodeKvtZY2FknYFLgNe0YmgSgxf6akK2HYkY+nLWEvuHwSOBT7Hhj/35gNndCqoYjHwRdu3NVdIemsH4mn0I0mXAt9hw43iB4Efdyyqys3AD23f1Fwh6UMdiKdRNooDszmwskX5KmDcCMfS7Bjg48DTLeqOGOFYejWmumVGM0lvBpbb/m2Luhm2F3YgrMYYDqK6J/8GG0Xbl3UuKpC0B/CQ7Y1uuSppYif7t8te3m603ije23gL7A7E9gvg//awUVxhe0qLyUaEpBOB91I9B6LxfZsJXGD7nzsY21XA39n+RYu6e23v2oGwWhpTyV3S5lR77oeyYZK6GDjD9jOdii3qaZRvFB+23dWirqMbxRLDK2j9vt3euaiqU6iBp2w/2ck42jHWkvu5VAez5rH+Z99kqj738bbf18HYujc8fwbsVIpH/YZH0lzbs/puOfJGc2wRw22sJfdf235Zf+tGwijf8PR0xomAW21PHsl4NghgdMf2IuBEqj3QiVQHy1dTbbDn2F4zCmI7FHjxaIqtN5J+ZPugTsfRymiLbawdUH1Y0uHARbafBZD0PKrz3B/paGTw2hYbl5XAAkm/7kRADbqA5VQJs5vL+Is7EtF6ozm2C4CrgLfYfgBA0kuAo0vd2zsX2nOx7dcU21Gdjk3SXj1VAdNHMpaNAhjFsTUba3vuU4HPA2+h2kuG6vSlq4HZtu/tTGQgaQHwJVpveE6w/foOxnY3cEAPB3s7ffBtNMd2l+09+ls3EkZ5bOuAn7LhBrvbPra3GuGQnjOaY2s2pvbcbS+TdDLVOe0bHFDtZGIvZlJteP5dUvOGZ2bHoqp8GdgO2CiBAv8ywrE0G82xLZf0KWBe9wHKcrXq0aw/C6RTRnNsd1BdH3B3c4WkxNamsbbn/mmqRHkeG175NhM4z/acTsUGPZ4hcLHtOzoXVUXSy2l99kJi64Gk7YDZVLF1dxE9SHVdxRzbHesKHOWxHQb8yvZdLeoOtf3DDoTVvfxRG1uzsZbcfw28svnMk/Kc1yUdvv3AqN3wlD2895fYGg/2JrYBknSM7W93Oo5WEtvAjLbYxlpyvxN4h+3lTeW7AJd3uJ9xNG94EtsQG233IWmU2AZmtMU2pvrcgeOBK8tBuO7+sZ2pbkjUsasFi2epzm9f3lS+Y6nrpMQ2AJIW91RFdWpkxyS2gRnNsTUbU8nd9o8lvYyNbyV6Y/ctgDtoNG94EtvATATewcan2QrY6PL1EZbYBmY0x7aBMZXcAcpphgs6HUez0bzhSWwDdgmwje1FzRWSrhn5cDaQ2AZmNMe2gTHV5x4RMVZ0+taeERExDJLcIyJqKMl9EyZpqqSNHu5R6r4lac8y/Jk25nW8pBf0Uv/c/AajxPx7VY/uG+g8jpb01R7qftGwnPc31Z0oaamkuyS9Y6DL7yWuAX0eks5U9aD2ltOOBpKukTSjRfm7Jc0uw4c2fkcknSVplaQtyvgOkpaV4d0kLZLU0We11lmSe03Z/lDDva/7TO5UZ520TO6SNmua32DdY3tYbrJk+w1lcCrVxU0AlKQzE3glcCDwNVUPRx8RfXweZ5WYNjm25zdcKHYo0LwDsA74ixbTDdt3ICpJ7pu+zSWdI+kOSRd2731372lJmgNsVfaSzpG0taRLJd0q6TZJ75P0t1Tnil8t6eoy/ROSviTpVmDfxj23UndqmceCck+S7r2xBZJ+JemUdvfKJH1W0q8lXSfpXEmfaFyHMvzcXl8xpdTfLemkhnl1L3MO8Oay3h+jusz+PNtPl/sILaU6w6a3uA6UdKekmyV9RdIlpfzk7hjL+G2qbkrX788DwPa1wMPtvFdDQdL2ki6XtKT8olhe3t8NfnlI+oSqezF1O7LEfZukvUuboyV9VdIbgHcDXyhtdivTfBn4mKrnFcQISnLf9O0BfM32K4DHgL9prLQ9G/i97em2P0C1h3if7Vfb/mPgx7a/AtxHdWvat5RJtwauL+2ua1rm1sAC268GrgX+spSfDpxu+1W0fgbmRiS9lmqPejpwMPC6Ntd7b+D/AP8LOLxFl8Fs4GdlvU+jOk2y8cZOK1l/6mSruLYEvgn8KfBa4CVtxtXfz6MTTgKus/1K4AdU1wW04wVlb/tvgDMbK1w9dm4+8MmybveUqt8C1wFHDknk0bYk903fCts/L8NnA2/qo/2vgLdJ+rykN9t+tId264CLeqj7A9X5vgA3UXWBAOwL/GcZ/l5fgRdvBn5g+0nbj1EliHZcYfsh278Hvk/f691fL6d6zundrs4XPrvN6fr7eXTCn1DWx/altP8sg3PLNNcCfyRp2zan+2fgkyTfjKi82Zu+5gsVer1wwfavgb2okvwpkv5fD02f6uUioGe8/gKJdQzfxXBrWf8d3bKprl/rTXVhU+O93Sez/gZtg4kLNoytv3GNJr2tFwxw3crtcRdRPfQ6RkiS+6ZvZ0n7luH3U/0EbvaMpHEAknYCnrR9NvAFqkQP8DjwwkHGsoCqqwTavwf9tcChkraS9EKqbpBuy6i6RAAOa5rubZLGS9qK6kDez5vqm9dnPjBT0haSdgWmATcASLpSUnMXzZ3A1Ia+4yOa4tqrTLsX0PjE+359Hr2R9BFJA76FQi/TX1tiQ9UDvLcr5Q8CLy598lsA72qa7n1lmjcBj7b41dfbd+hU4BM91MUwSHLf9N0FHCfpDqp/0q+3aDMXWFwO4L0KuEHVqYgnAac0tPlx9wHVAToeOEHVzZV2B/FBG+kAAAFoSURBVHrq8nmO7ZuB84FbgR8BNzZUfxH4a0m3ADs0TXoDVbfRYqqnVy1sql8MrCsHfT9mewnV4+NuB34MHGd7naqnXe1O0wFN208Bs4BLJd1M9XzRbhcB4yUtobp/TeNjEPv7eaDq+bm/BPaQtFLSsaXdy4GHmicuB2a/1TC+qGH4Ww3HH1pOD3wO+JMS/3soDzopd9b8B6r39gqqDVyjp8pn8Q2qh7k3Ow/4pKRbGjaKlHkvAW5uMU0Mk9x+IIZMOTPk97YtaSZwhO1DmtpMBS4pB3NbzeNk4AnbXxzmcLuX98fAX9g+oY92+wGfsN28Nztsytk577H9h+GcvpyFNMP27waynMGQ9ITtbUZ6uWNBTk+KofRa4KuSRPWM2o3Ob6bqo3+RpEWj4Txn27cBvSb2ThnshmQkN0T9VfbsL6LqCophkD33iIgaSp97REQNJblHRNRQkntERA0luUdE1FCSe0REDf1/t7CDW216o8EAAAAASUVORK5CYII=\n" + ] }, "metadata": { "needs_background": "light" - } + }, + "output_type": "display_data" }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "[OrderedDict([('000', 273), ('001', 415), ('010', 0), ('011', 0), ('100', 0), ('101', 0), ('110', 138), ('111', 198)])]\n" ] } + ], + "source": [ + "# tq.QuantumState\n", + "q_state = tq.QuantumState(n_wires=3)\n", + "q_state.x(wires=1)\n", + "q_state.rx(wires=2, params=0.6 * np.pi)\n", + "print(q_state)\n", + "\n", + "q_state.ry(wires=0, params=0.3 * np.pi)\n", + "\n", + "q_state.qubitunitary(wires=1, params=[[0, 1j], [-1j, 0]])\n", + "\n", + "q_state.cnot(wires=[0, 1])\n", + "\n", + "print(q_state)\n", + "bitstring = tq.measure(q_state, n_shots=1024, draw_id=0)\n", + "\n", + "print(bitstring)" ] }, { "cell_type": "markdown", - "source": [ - "Batch mode process different states" - ], "metadata": { "id": "rYQ1mg1XCt5P", "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "Batch mode process different states" + ] }, { "cell_type": "code", - "source": [ - "# batch mode processing\n", - "\n", - "q_state = tq.QuantumState(n_wires=3, bsz=64)\n", - "q_state.x(wires=1)\n", - "q_state.rx(wires=2, params=0.6 * np.pi)\n", - "print(q_state)\n" - ], + "execution_count": 22, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -391,11 +367,10 @@ "name": "#%%\n" } }, - "execution_count": 22, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "QuantumState 3 wires \n", " state: tensor([[0.0000+0.0000j, 0.0000+0.0000j, 0.5878+0.0000j, 0.0000-0.8090j,\n", @@ -528,19 +503,19 @@ " 0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j, 0.0000+0.0000j]])\n" ] } + ], + "source": [ + "# batch mode processing\n", + "\n", + "q_state = tq.QuantumState(n_wires=3, bsz=64)\n", + "q_state.x(wires=1)\n", + "q_state.rx(wires=2, params=0.6 * np.pi)\n", + "print(q_state)\n" ] }, { "cell_type": "code", - "source": [ - "q_state = tq.QuantumState(n_wires=2)\n", - "print(q_state)\n", - "q_state.set_states(torch.tensor([[0, 0, 1, 0], [0, 1, 0, 0]]))\n", - "print(q_state)\n", - "\n", - "q_state.x(wires=0)\n", - "print(q_state)" - ], + "execution_count": 23, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -551,11 +526,10 @@ "name": "#%%\n" } }, - "execution_count": 23, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "QuantumState 2 wires \n", " state: tensor([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]])\n", @@ -568,17 +542,34 @@ ] }, { - "output_type": "stream", "name": "stderr", + "output_type": "stream", "text": [ "/content/torchquantum/torchquantum/states.py:47: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n", " states = torch.tensor(states, dtype=C_DTYPE).to(self.state.device)\n" ] } + ], + "source": [ + "q_state = tq.QuantumState(n_wires=2)\n", + "print(q_state)\n", + "q_state.set_states(torch.tensor([[0, 0, 1, 0], [0, 1, 0, 0]]))\n", + "print(q_state)\n", + "\n", + "q_state.x(wires=0)\n", + "print(q_state)" ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "FCD00B-f1R14", + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], "source": [ "# demonstrate the GPU processing\n", "\n", @@ -608,18 +599,35 @@ "\n", "print(f\"Use GPU: {use_gpu}, avg runtime for circuit with {n_qubits} qubits, {2*n_qubits} gates, {bsz} batch size is {start.elapsed_time(end) / run_iters / 1000:.2f} second\")\n", "\n" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 3, "metadata": { - "id": "FCD00B-f1R14", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "FrmkOuSw1lOI", + "outputId": "063d3d28-9a16-435c-ecf7-b16baaae2880", "pycharm": { "name": "#%%\n" } }, - "execution_count": null, - "outputs": [] - }, - { - "cell_type": "code", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "QuantumState 2 wires \n", + " state: tensor([[0.0000+0.0000j, 0.0000+0.0000j, 0.5878+0.0000j, 0.0000-0.8090j]],\n", + " grad_fn=)\n", + "tensor(0.1910, grad_fn=)\n", + "tensor([[[-0.8090+0.0000j, 0.0000+0.5878j],\n", + " [ 0.0000+0.0000j, 0.0000+0.0000j]]])\n" + ] + } + ], "source": [ "# automatic gradient computation\n", "q_state = tq.QuantumState(n_wires=2)\n", @@ -636,35 +644,36 @@ "loss.backward()\n", "\n", "print(q_state._states.grad)\n" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 4, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, - "id": "FrmkOuSw1lOI", - "outputId": "063d3d28-9a16-435c-ecf7-b16baaae2880", + "id": "11F-rQRN1q1g", + "outputId": "6568e55e-408c-44d0-fee6-9cd544b62f17", "pycharm": { "name": "#%%\n" } }, - "execution_count": 3, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ - "QuantumState 2 wires \n", - " state: tensor([[0.0000+0.0000j, 0.0000+0.0000j, 0.5878+0.0000j, 0.0000-0.8090j]],\n", - " grad_fn=)\n", - "tensor(0.1910, grad_fn=)\n", - "tensor([[[-0.8090+0.0000j, 0.0000+0.5878j],\n", - " [ 0.0000+0.0000j, 0.0000+0.0000j]]])\n" + "QuantumDevice 2 wires with states: tensor([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", + " [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", + " [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]])\n", + "QuantumDevice 2 wires with states: tensor([[ 0.1543-0.2309j, 0.2192-0.5838j, -0.4387-0.5519j, -0.0495+0.1859j],\n", + " [ 0.1543-0.2309j, 0.2192-0.5838j, -0.4387-0.5519j, -0.0495+0.1859j],\n", + " [ 0.1543-0.2309j, 0.2192-0.5838j, -0.4387-0.5519j, -0.0495+0.1859j]],\n", + " grad_fn=)\n" ] } - ] - }, - { - "cell_type": "code", + ], "source": [ "# build a circuit\n", "\n", @@ -698,43 +707,11 @@ "model = QModel()\n", "model(q_dev)\n", "print(q_dev)" - ], - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "11F-rQRN1q1g", - "outputId": "6568e55e-408c-44d0-fee6-9cd544b62f17", - "pycharm": { - "name": "#%%\n" - } - }, - "execution_count": 4, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "QuantumDevice 2 wires with states: tensor([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", - " [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]])\n", - "QuantumDevice 2 wires with states: tensor([[ 0.1543-0.2309j, 0.2192-0.5838j, -0.4387-0.5519j, -0.0495+0.1859j],\n", - " [ 0.1543-0.2309j, 0.2192-0.5838j, -0.4387-0.5519j, -0.0495+0.1859j],\n", - " [ 0.1543-0.2309j, 0.2192-0.5838j, -0.4387-0.5519j, -0.0495+0.1859j]],\n", - " grad_fn=)\n" - ] - } ] }, { "cell_type": "code", - "source": [ - "# easy conversion to qiskit\n", - "from torchquantum.plugin.qiskit_plugin import tq2qiskit\n", - "\n", - "circ = tq2qiskit(q_dev, model)\n", - "circ.draw('mpl')" - ], + "execution_count": 5, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -746,40 +723,44 @@ "name": "#%%\n" } }, - "execution_count": 5, "outputs": [ { - "output_type": "execute_result", "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA4EAAAB7CAYAAADKS4UuAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjMsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+AADFEAAAgAElEQVR4nO3dd3gU1frA8e/upockJAQIBAgt9CK9S1VA5AIqSBFFuYiAKLb7u17kihfFBqgXERuCAsJVVEAFkZYAUiQgvQQILZCEEhJISNvd/P4YElK2Jezu7LLv53nyQKaceWdydnbemTnnaPLz8/MRQgghhBBCCOERtGoHIIQQQgghhBDCeSQJFEIIIYQQQggPIkmgEEIIIYQQQngQSQKFEEIIIYQQwoNIEiiEEEIIIYQQHkSSQCGEEEIIIYTwIJIECiGEEEIIIYQHkSRQCCGEEEIIITyIJIFCCCGEEEII4UEkCRRCCCGEEEIIDyJJoBBCCCGEEEJ4EEkChRBCCCGEEMKDSBIohBBCCCGEEB5EkkAhhBBCCCGE8CCSBAohhBBCCCGEB5EkUAghhBBCCCE8iCSBQgghhBBCCOFBJAkUQgghhBBCCA8iSaAQQgghhBBCeBBJAoUQQgghhBDCg0gSKIQQQgghhBAeRJJAIYQQQgghhPAgkgQKIYQQQgghhAeRJFAIIYQQQgghPIgkgUIIIYQQQgjhQSQJFEIIIYQQQggP4qV2AK7CsGoP+RfT1A7jrqWpXhHdoDZqh+FxpF47li31+vgmuHHJSQG5uaAq0LBX+ddX61jfadxCCCGEs0kSeEv+xTTyE+RKTdxdpF6r78YlSEtUOwrPIMdaCCGEsI28DiqEEEIIIYQQHkSSQCGEEEIIIYTwIPI6qBBCCCGEEELYWU4eJKdDrh68dFAlGAJ91Y5KIUmgEEIIIYQQQtjBjWzYdQriTkNKOuSXmB8WCC1rQZdoCA9SJURAXgd1edGfP8fSI9tsni6EO5B67TyPzazNhj1LbJ5+t3tpfg8e+KcvA6dWYNC0EMbPuYfY/d+rHZYQQgg3ZzTCpiPwxkr4ZZ/yBLBkAgiQmgmbj8Jbq+GH3ZCjd3qogDwJFEII4WFG9ZnGqD6vYTDoWbX9Y97+diT1I1sRGV5f7dCEEEK4oZu5sCAWTpWhQ/Z8YGs8HE2C8T2hspOfCsqTQCGEEB5Jp/Oif4dxGIx6Tl3cp3Y4Qggh3FB2HszfWLYEsKgrN2DueriaYd+4rJEkUAghhEfK0+fyy/b5ANQIb6ByNEIIIdzRT3vgfKrlZT4cpfyYcz0Lvt4GBqN9Y7NEXgd1cymZ6QxdNQcfrRdZ+lxmdHuUXlHN1A7rrmHQgz4HvHxA5612NJ5D6rXzXLuRwvSvh+Cl8yE3L4sn+8+kdXRvtcNyqG83vsX3sbPIyrmBTufNi0O/pG71FgCs/XMBG/YsLlw2KTWB5nW68erIpWqFa5IxH27mgEYDAT7Kv0IUlZOn9Ejo76P0SigcJzsP8gzKZ1HnJo9XDEblHOLjBb5yfVNux5KUTmDs4dxViD0GvZrYpzxrXDoJNBqNzJkzh88++4zz58/TsGFD/vvf//L000/TvXt3Pv/8c7VDdDhvrQ69oXSL0TyjAW+tjnD/IDYPfx2dVktCWgqjfp7LjtFvqhDp3SXzKpz5E5KPQb5BucCqHA2120NwhNrRuT+p186j03mjN+aVmq435OGl8yY4MJw5E7ei0+pIuprAm0sepfXzu1WI1HlG9p7KqD6vcePmNWZ/P5b9JzfTv/1YAPq3H1v4/9Trybz8WU+e7PeWmuEWYzAqbUi2Hr/96lDlIOjWUOlpzl0uQIXjxCcrnVMcS1J+9/GCDnWVC8vQQHVju9scPK908JFwWfk9wAc61odejaGCn7qxmZN2U6kfu07d7pCkYYRSPxpWUzc2d7T+kH3L23QU7m3onBs3Lv11MXbsWGbMmMH48eNZu3Ytw4YNY8SIESQkJNCmTRu1w3OKqJDKnExLKTYtIzeb5Mw06oZUQafVotMqf8b0nJs0r1xLjTDvKmkXYNcSSDqiJIAA+flw6QTs/hYu2+mOjyeTeu08EaG1uXjlZLFpWTkZXLuRTLVKddFpdei0yrdNRlYadau1UCNMVQQFhPLi0C/ZdexXth9aVWye0Wjk7WWjGNv/bSLCaqsTYAl6A3wRAyv3QGqRtiNXbsCPcbBwi3NfJRKuZ8dJ+GQjHE++PS1Xr9w4mL0WUq6rF9vd5veDsGALnL5ye9rNXCXBmvMbpN9ULzZzLt9Q6sGW48V7pIxPhvmbYFu8erG5o+T08rcDNCcjGw6ct2+Z5rhsErhs2TIWLVrE6tWrefnll+nZsydTp06lU6dO6PV6WrdurXaITjG66b0sOLCJbYnHMBiNXMvO4MVN39AsvBb3VK0NwOm0S/RYNp0BK95hUHRbdQN2c0YDHFgFRj2l+/XNh3wjHPwZ8rLUiO7uIfXaee5vO4Y1uz7nYMJWDEYDN25e45NVz1M7ojn1q7cCICn1NFPmdeXVL/vSpdkQlSN2ruCAMB7u9iJf/fYvjMbbGdTi9W9QJ6I5XZoNVjG64jYUebpT9PRU8P9DF5RXiYRnunQdvtul/D/fRL/0mbmwaKvpeaJsEi7BmgPK/00dz2uZsHyXc2OyxTfbICOn9PSCXVixG5LSnBqSW4tPtr6MK5Vbksu+Djpz5kz69etH9+7di02vX78+3t7etGih3K0+c+YMTzzxBElJSfj6+vLJJ5/QrVs3NUJ2iJFNupKlz+W5DQs5d/0KFXz86FajMT8NeRmvW3fv61SsQsyI6SSkpdD3u7cYUM8zEmRHuHQCcq3cvTPq4eJhiJK8pNykXjtP79ajyMm7ydyfJpGSdhZ/nwq0qNudGU/9jE6nfAVUC6vDh5O2kXQ1gVc+60XHJg+qHLVzDen2PD9u/YD1e76hb7sx7D2xkT3xvzN7QqzaoRUyGG27S7/1OPRoBFqXvcUrHOWPE6bHJCuQn69c4J++DHWrOC2su9LWeNBg/njnA0cvKk/p1RwMvKhzV613XqIB/oiHR9o7JSS3Z+14ulq5JblkEpiYmMihQ4d44YUXSs07d+4cTZs2xdfXF4Dx48fz6KOPMnHiRLZv387QoUM5ffo0Pj4+FrehKdGKfv2jr9G9ppNaYpbR2Ba9GNuil8l5Ofo8fL2UFr3BPv4EervmS+gxsTHcN7GP2mFY9fzDn9K//Vh0WvMfDYPRwDfzfuXfiwY5MbLykXrtWLbU61nPbKZlvR7OCciMBzqM44EO40zOy9Xn4OOlnE8D/ILx96ngzNCKiY2Nod2InuVe35ZjPXtCTKlpgX7B/Pgf5Vs39XoyH698lplj1+LtZfl7pMCdxm2LsOqNGf3eEavLXbsJoRF1uH75jEPjEa5n1Mz9hNey/jr38HFT2b16phMiuns9Pf8y/kHhVpfr/uBTHNmy0AkRWdd6wMt0G/G+xWXygZ+3HGdoh0bOCcrNDfnnemo1K34NYKkHUEvzpxTpe+zEmUtoNFXLHVe+jY/7XTYJBIiIKN4DR1ZWFrGxsfTv3x+AK1eusG3bNlavXg1A586dqV69Ops3b6Zv377ODVolccmnmLbtO3QaLXlGPXN6Pa52SG5Np7HeElcDaLXS1ZojSb12nvjzu/lq7b/QanXoDXlMGPSR2iGpasmGGWRmp/P+/8YUTqtZuSFTHvlMvaAATRnOOWVZVtw9NDrb/u5SP+6crcfQlY61rdctrhSzqyv5QMmOBTum3JKbybc1XXSikydPEh0dzQcffMCUKVMKp7/xxhtMnz6defPmMXHiRPbu3csjjzxCQkJC4TLDhg2jT58+PP3002Xapn7+RvIT7Ny6UxTS1K2C1wTX73Y+cR8c22B9uTodoV5Xx8dzp6ReO5Yt9TpuOaQlOikgN1exBrQdXv711TrWdxq3LXL1MO2H4p05mBLgA/95SIYE8ETf7oDdCZZfCQUY1wOaRjojorvX/I1Kuy1rx/ql/lAzzCkhWXU8Sen8xRIN0CoKHneD6xtX8PU2+OusbcsWPAGcYsNoQ1WD4dWB5Y/LVi75JLBu3bq0aNGCmTNnEhYWRmRkJCtWrGDNmjUAHtMzqHC+iCZwIhYMpXvUv00DkZ7TgaIQwgX4eEGHekqvfpZ0qi8JoKfqEg1/JpifrwFCAqCxDANwx7o2KN4Da0kaDdQMdZ0EECA6AipVUHoWttSWsWsDZ0bl3mqG2Z4ElrVcZ3DJpuNarZbvv/+epk2bMmHCBJ588knCw8OZNGkSOp2usFOYWrVqkZKSQk7O7a6OTp8+TVRUlFqhCzfn5QNN+t36xczT+AY9wS/YaSEJIQQA/Zord4jNiQyF+5o5Lx7hWqLCzQ8yrdEonQWN7iydBtlDsxrQro7peRrA1wuGd3RqSFZpNfBYZ9DpzF7e0L0R1Kns1LDcmqM6WHJWx00u+SQQoEGDBmzevLnYtNGjR9OkSRP8/f0BCA8Pp0uXLixYsKCwY5gLFy7Qs6djG+iLu1vVhuDlC6f+gOtJt6cHhkPdTsp8IYRwtgBfeP5++GUf7D4NebfGMfXWKU8JH7wH/LzVjVGoa+A9ytOejYchNfP29OiqMKClkiiKO6fRwIhOEBECMcfgRvat6UDTGspnMSJE1RBNqlNZOYf8uu/2cDMAoQHKDYSuDZzWHO2uEFUJqleEi3YcVsPXC1rXtl95lrhsEmhKXFwcHTsWv7Xy6aefMmbMGD788EN8fHxYtmyZ1Z5B78SfSSd5efNitBoNbSPqMavnaKvz153ez/u7lM5r4q8lMbfPkwyKblfmbb+8eTF7khNoVbU2c3o9UTjdXPk383IYsfojMvNyCPYNYNnA5/D18mbx4S0sObwVg9HI1wMmERlU+rnzxYxrDP7xfY5evcC1578q7LYfMFuupXXcTaXayk9mKuz4SpnW8Ym79+Ro7W+nNxp44td5XLp5nTYRdXmn+0irn4WizNVdgDPpl+m6dBqNwiLx0XmxZuirJtcxFYM999Pc/tjr8wtwJf0i0xY+yNmUI/z8ZkbhEA0Au4/9xvLN7wCQePk4zz00n4Y125td3tZyrc3/YcsHbD34Ax9O2mbX/TE3Pzv3JjMWDyU7N5NAvxBeG/1dYe+kjmYp3nmrnufUxX3k5WUzfuAcmtXpwtKNb7F6+zz6tXuKJ/u96ZQYrQnwhWEdYGArePV7ZdqMhyX5EwqNRnkttFN9ePFbZdq0QUpiKOxLq4HeTaFHY3hpmTJt+hDllVtXVjMMnumljGX4xkpl2rTByv6IstFooGdjWLrDfmV2jnbe+dxtXgrIyMggPj6+1CDxdevWZcuWLcTHx3Po0KFS4wraW63gcH4fNpWYEdO5dDOdg5fPWZ3ft05LNgyfxobh06gZVIneUc3LvN2/Uk6TkZvN5hGvk2vQE5d0qnCeufLXnd5Pu2r12TB8Gu2q1WPdmf1cuJHK1vPHWDdsKhuGTzOZAAKE+QWybti/6FCtfql5psq1to67CixyeO7WBBCs/+1WnthNiypRrH/0NbL1uey/dNbqZ6GApbpboHdUczYMn1aYAJpax1QM9txPc/tjj89vgeCAMN57eiONa5V+T6hdo37MnhDD7AkxVKlYi9bRfSwub2u5lubn6nM4dXGfQ/bH3Pzdx3+jUa0OzJ4QQ8Na7Yk79lu5t19WluId/+As5kyI5bXR37Fsk9J9/gPt/86rI2xoxa8C/yL3OiUBFCUVvaCXBNCxdEWupF09ASwqNPD2/yUBLL+2daCRndrZhleAfk7sc8JtksAKFSpgMBiYPHmyqnFEBFbE79bYUd5aL3Qarc3zE9JSqBIYQgWfso95tuviycKLz15RzdmZdKLUMiXLr1uxKpl5SnvJ9OxMKvlVYP2ZAxjyjfT97i2mbFyEwWg0uT0/Lx9C/Ux/c5gq19o6wrVZ+9udTrtE8/BaALSsEsXOi/FWPwsFbKm7seeP0HPZG3wUt8bsOqZisOd+WtufO/n8FvDx9iMoINTiMklXE6gYVBV/3wo2LW9Luebm//bnAu5r+4SJNWxTnu1Wr1SP7FzlPbXMrDSCAyuVe/tlZSleL52SSWXlZFC3eksAQoOqOq4LcCGEEG5Po4ERHZVXai2ZstRyz6A+XjC6i/I6qLO4TRLoag5cPseVm9dpEl7D5vkrT+xmcP225dpeWk4mwb5KW8gQX3/Ssm+WWqZk+dGhEexKOkHLha+wJ+U0nSIbkHIznVyDnnXDpuLv5cvqk3FljsVUueLu1iCsGlsSjwIQc+4IaTm365+1z4K1ulstsCKHn5rN+kdfY9PZQxy4fM7kOpZisCdz+3Mnn9+y2HbwR7o0G+Lw7egNeew/FUOr+r0cvq2iIsOjOXp2B3+f1ZT4xDiaRHV26vYtmb5oCP/84n5aR/exvrAQQgiB8gR4Up/yP3X384bxPZ3fZtet2gQ6U3JmGo/9PLfYtKqBISwd+BypWRlM2biIbwc+Z3Jdc/N/PbWX7wa9UK5thvgGcD0nC4DrOVlU9Ct9y6Fk+YsPb2FA3Va81H4gc3b/wtIj2wjxCeDemo0B6FmrCXtSTls4CqaZKnd003vLXI5wPkt1zJIH67Vh87nD9P3uLaKCK1M1QGnxbu2zAFitu75e3viiPIV5oF4rDl85b3IdczHYax+t7Y+1z6+97Dj6M9Mf/9Hh29mwZzG9WllvV5l6PZm3lhYfBC8sKIKpjy0v13bXx31NxyYDGdbjFb6PmcXGvUu4r+3j5SrL3qaP+YnLaYn8Z/EjzJ28U+1whBBCuInwIHi5P6zaCztLt3oxq2GE0pNs0ddznUWSQDMiAiuyYfi0UtP1RgNj1szj3e4jiQisaPP85Mw0fHReVPIPKlzualYGVQNvX8ia2yZAx+rRfLF/I0MbdWTT2UM83qx40lWyfID8fAjzV25LhPsHcT0niy41GvLVAaXX1f2XzlI7pLLJWCwxVa5wD5bqmCU6rZYPe48BYMLvX3Bf7RYm67qpumSt7t7IzSLIR3nqt/1CPJNa98Vbqyu1jrkYyvI5ssTSZ9vU58sRUq8n463zsfiKpMGg5/rNq4QGVb2jbZ2/fJxTF/fxy45POZtymJXb5jKw04RSZYcFRzB7QswdbauofPIJClAa2wYHhpOZnW63su9Erj4HHy9f/H0r4OejwrexEEIIt+bvoyR0naNhWzzsPQt6Q+nlNBpoUl3pxKlxdfX6nJDXQctoxfFdxCUn8GrsMvosn8HOi/EkZ6bx9s6VZucD/HxyDwPr3R7k/kz6ZV7f9p3N221VtQ5+Xt70XPYGOq2WdtXqF9tuyfIBhjfuzIrjO+mzfAbLjv7BiMZduKdKbfy9fOizfAZxyQk83KCDyVjyDHr6ffcWBy6fZcCKd/gz6WTh9kyVa24d4R4s/b0BLtxIpc/yGdz/vzfpVL0BkUFhJuu6qbpkre5uSzxGh8X/4t5vX6d6hTDaV6tvch1TMZT1c2RpP819dsH056s89IY8/vFZHxKS9vPPL/ty9NwuUq8ns3TjWwBsP7yKTk0HWVw++doZFv72WpnKNTV/3IB3eWfcOt4e9xtRVZsyuOtkk2Xfyf6Ymt+r1Uhi93/HS/N7sOmvpfRqPepOD2u5491/KrYw1reWPMpL83swbeFAnrj/DQDW/rmAz35+iU17l/LfHyc5LU4hhBDuq1YlGNkJ3hmqPB0c2en2vOfvh3eGwbge0CRS3U4HNfn5+fnqbd516OdvJD/hktO292P8n4T6BdKzVlOnbVPNWDR1q+A1obfDynekDbOUf/u8rG4c5XG312u1P0e21Ou45ZCWaL9tbj3wAxUCQh3Sls+RZduiYg1oO9z6cubY+1jb6k7jLq+CTgY+dF4eLdyI1A/ncddj7a5xuyNXPNbyOqhKHmrQXu0QCrlSLMK9ObsueWLd7dbiYbcsWwghhBCuQ14HFUIIIYQQQggPIk8Cb9FUL93Ji7AfOb7qkOPuWLYc36AqTgikjApemaxoelQP1dzpsVLrWLvi31gIIYSwRJLAW3SD7rzTByFcjdRr9TVUp3mdRQXtXNVox+ZIrnishRBCCFckr4MKIYQQQgghhAeRJFAIIYQQQgghPIgkgUIIIYQQQgjhQSQJFEIIIYQQQggPIkmgEEIIIYQQQngQSQKFEEIIIYQQwoNIEiiEEEIIIYQQHkSSQCGEEEIIIYTwIJIECiGEEEIIIYQHkSRQCCGEEEIIITyIJIFCCCGEEEII4UEkCRRCCCGEEEIIDyJJoBBCCCGEEEJ4EC+1A3AXhlV7yL+YpnYYLk1TvSK6QW3KvN7xTXDjkgMCsrO45WpHYFpQFWjYy7ZlpR7fmfLWcSHuNmqdt8tyvitJze+aO4nbHcmxFpZI/XANkgTaKP9iGvkJbpCpuKEblyAtUe0orHOHGK2ReiyEsAd3OW8X5Y4xuys51sISqR+uQV4HFUIIIYQQQggPIkmgEEIIIYQQQngQeR1UCCGEcAPGfDiZovwkpt6e/kUM1AiD6KpQrwpoNKqFKFR25QYcugCJV29Pm7seqodCVCVoXgN8vdWL726SnA5HLsD5Ip/FeRsgMhRqV4amkeCtUy8+U3L0cOg8nL0KF67dnr74D6gZBk1rQOUg9eITziVJoIuL/vw5pncdxqgmXW2aLsrvsZm1GdP3Tfq0ecym6aL8pF4LYTtjPuw6BZuOwOUbpecfvqD8rDsIVYOhd1NoV0f9ZPCl+T04enYHOp03Wq2OiNA6jOw9le4th6obmAXuGDMoNwXW7IejFyG/xLxTl5SfrYCfN3SoB32bQ4CPGpHe5q7H+lQKrD2o3Iwp6USK8sMxCPSFLtHQpyn4qHy1nZ2nnB92nFT+X9KeM8rPyr3QqBo80BJqVXJ2lKW5ax1xF5IECiGEEC4q7SYs3X7rwtIGKdfh2x2w9wyM7ATB/g4Nz6pRfaYxqs9rGAx6Vm3/mLe/HUn9yFZEhtdXNzAL3ClmoxHWHYL1h5SbBdZk50HsMdh3FkZ0Ui741eROx1pvgFV/wdbjti2fmQO/H4K/zsKozlA73LHxmROfrJwT0m7atvyxJGWd3k2gXwvQqdxwzJ3qiLuRNoFCCCGEC7pyAz5cZ3sCWNSxJPjod7iWaf+4ykOn86J/h3EYjHpOXdyndjg2cfWYjUZYsl15wmNLAlhUehZ8vll5+uMKXP1Y5xmU165tTQCLunwDPt4Ax5PsHpZV+8/Bp5tsTwALGPNh/WH45g8wGB0TW1m5eh1xR5IECiGEEC4mO698F29FXc1QysjV2y+u8srT5/LL9vkA1AhvoHI0tnH1mFf/BXvPln99Y77ylPlUOW4y2JurH+tlO+B4cvnX1xvgy1hIcuIwvacvwzfbyn6DoKj95+CnPfaL6U64eh1xR/I6qJtLyUxn6Ko5+Gi9yNLnMqPbo/SKaqZ2WHelazdSmP71ELx0PuTmZfFk/5m0ju6tdlh3JanXwtOt/guuZFhe5sNRyr9TlppfJuU6/LofhrSxX2xl8e3Gt/g+dhZZOTfQ6bx5ceiX1K3eAoC1fy5gw57FhcsmpSbQvE43Xh1pYYecwFLMF66c5K0lj/LRszvw9vLhu5j3uZlzgzF9/+PUGE+mQMwxy8vYUj+M+fDtTvjHAPBV4YrQHY71X2etJ9u2HOs8g/Ja5pS+jn/FMlevbMtgJQG0Je5t8dCiJjSIsF98ZWGpjsxcOpJerUbSscmDALy+aDADO02kbcP71QnWzbj0k0Cj0cisWbOIjo7Gz8+Pli1bEhsbS8OGDXn66afVDs8pvLU69IbSt3HzjAa8tTrC/YPYPPx1NgyfxuIHn2XqluUqRHl30Om80RtLt5jWG/Lw0nkTHBjOnIlbmT0hhn+NWsaCNf9UIcq7g9RrdeTnQ+q527+f3Ao3r5lfXqjj4jXYfsJ+5W05Bpev26+8shjZeyorZ6SxYvoV2jd6gP0nNxfO699+LLMnxDB7QgxTRy3HzyeQJ/u9pU6gRViKOTK8Pl2bP8zyTW+TlHqamH3LGdl7qlPjy8+HH+PsV97VDIg5ar/yysLVj7XBaN8nYedT4c8E+5VnztbjpjuRKq8fdiv1Tg2W6siEQR+yaN00snIy2HrwRwL9QlwuAUxJh5//uv376cvqHcuSXDoJHDt2LDNmzGD8+PGsXbuWYcOGMWLECBISEmjTRqXbmk4WFVKZk2nF39XIyM0mOTONuiFV0Gm16LTKnzE95ybNK9dSI8y7QkRobS5eOVlsWlZOBtduJFOtUl10Wh06rdLfc0ZWGnWrtVAjzLuC1Gvny8mEP5fA3u9uTzuzC7YvgGMbIN9F2n0I+MOOCSAovUXau8yyCgoI5cWhX7Lr2K9sP7Sq2Dyj0cjby0Yxtv/bRITVVidAE8zFPKzHK+w8+gszl45gwt8+xMfL16lxnb4MF+38WuH2E+q2/XLVY33wPFzPsm+Z2+IdmwQYjfb/vKdcN90bqjOZqiOhFaowpOvzzFv1HN9ufJNn/vaBukEWob/15PftX2DjkdvTP/pdGbYlM0e92Aq4bBK4bNkyFi1axOrVq3n55Zfp2bMnU6dOpVOnTuj1elq3bq12iE4xuum9LDiwiW2JxzAYjVzLzuDFTd/QLLwW91StDcDptEv0WDadASveYVB0W3UDdmP3tx3Dml2fczBhKwajgRs3r/HJquepHdGc+tVbAZCUepop87ry6pd96dJsiMoRuy+p185l1CvJ341Lpucn7oMTsc6NSZhmzL+zdl7m7Dmj/t3n4IAwHu72Il/99i+MxtsZx+L1b1Anojldmg1WMTrTTMXspfOmed17yci6RrM6zh/OxhGduaRnKcNIqMlTjvWFa0pS5ShnrkCqAzqEcoVOhEzVkb7txpB4OZ7BXZ4jOCBM5QhvW7Hb/FPf05fhs81Kwq4ml00CZ86cSb9+/ejevXux6fXr18fb25sWLZSnMP/+979p0KABWq2WFStWqBGqQ41s0pUZ3R7luQ0LqfrxOFot+j+y9Ln8NORlvG49lapTsQoxI6azbdR/mLJxkboBu7HerUfxVP+ZzP1pEg+9Hsa42c3IyctixlM/o9MpjSWqhdXhw0nbmDt5Fx+vfFbliN2X1A/WUsoAABcvSURBVGvnunQCMq9SegCxIs7/pTwtFOq6egOycu1f7o1s5UJfbUO6PU/q9STW7/kGgL0nNrIn/nfGDXhP5cjMKxnzmeTDHD7zB63q92HNri+cHs+5q9aXKY/zDiq3LORY37lzqdaXcaVyy6pkHQGoXqm+Sw0ZcTUDdp4yPz8fpW4dvei0kExyyY5hEhMTOXToEC+88EKpeefOnaNp06b4+iqvBPTr148xY8bw1FNPOTtMpxnbohdjW/QyOS9Hn4evlzcAwT7+BHr7OTO0u84DHcbxQIdxJufl6nMKX0UJ8AvG36eCM0O760i9dp6LhwANFpPAfCOkHIdanvGShctKSndg2WlQMcBx5Zc0e0JMqWmBfsH8+B/lajL1ejIfr3yWmWPX4u2l8sjlt1iL2Wg08tGPzzB5yDxqhDfg+Xmd6dx0EKFBVZ0WY7KD6ogj654prn6ss3Idd+PEkcc62UE9kCanKW8TaDSOKd8Ua3XEVcWdtr6MBth9GprWcHg4ZrlsEggQEVG8K6KsrCxiY2Pp379/4bTOnTuXaxuaMtbi9Y++RveaTcq1LUeKSz7FtG3fodNoyTPqmdPrcdViiYmN4b6Jfcq83qxnNtOyXg/7B2Rn8ed389Xaf6HV6tAb8pgw6CO1QwIgNjaGdiN62rSsq9bjklypXhdV3jquts9e3G+1DWt+fj5vTH2br35zbscLoriGnUbQb9K3xaYV9OBnjrn5JXv8GzhoCKfiVt5BdLfZ47y9ZMMMMrPTef9/Ywqn1azckCmPfGZ2nbKc70qyR8w/75hPdGQbGtRQ+iUY03cGn6yewtRRyyyudydxl/T8kuJ3c+xVP5Yt/57RXYbdQWS33Q3HOiCkKuPmFR8Xwl7Hes4H/2XQ4ufvIDrz+k5YQqMuxQOxR9zGfPDy9sFoKN2BXlmped1nz8+iOT0en0vz3s+g1ZlPs/KBX3/fwpP3dje7THnl2/juv0smgeHh4QDEx8fzwAMPFE5/7733SEpK8phOYWzRpUYjNg3/t9pheIRmdboyZ+IWtcPwCFKv7Ss94xIGo6GwYyNTNBoN6TevODEqYYo+L9txZee6wPugRTz30Dyee2ie2mGUyaAuk4r93qXZYKe3ZdTnZuHl42//ch1Y98pD7WPt0M+iA8s2OKhso9FglwTQEf4xfJHaIRSTlXEFjYXvW1COZ9YNdb9zNfm2potOZDQaadWqFUlJScyaNYvIyEhWrFjBmjVrOHfuHDt37qRDhw7F1unRowfPPvssjzzyiENi0s/fSH6Cyq2mXZymbhW8JpR93Ly45ZCW6ICAPETFGtB2uG3LSj2+M+Wt42q7eBCOrLOykAa6Pg1+QU4JSZiRkq70JmcLW8b4KurfgyDMTm+xq3XeLsv5riQ1v2vuJO6SZq2BRBuGdilr/RjQEu6z03Csd8ux/vcPcN2GnKqsx3pER+hQr/xxWbL5KKzaa9uyZYm7Wgj834Plj6uou6V+mHPpOsz82fpyT3aDlip2fu6SHcNotVq+//57mjZtyoQJE3jyyScJDw9n0qRJ6HS6wk5hhBBCWFe1EfhXRGmEYEZkC0kAXUHlYMcM2h3oC6GB9i9XOF/NSo4pt5aDynVn7nisazqog0xHHYu7UZVgaB1lfr4GqFYRmqnYHhBcNAkEaNCgAZs3byYzM5Nz584xY8YMDh48SJMmTfD3t/9rEEIIcbfSeUProRAQemuChmIJYURjaGi6jx7hZFoN3GPh4qG8WkU5t0MH4TitHFA/KvhBvSr2L9fdOeJYR4QoP45SpzKEOOAy2RHH4m42vCM0v5XkFXzlFpyDq4fCM71Ap3IW5pJtAs2Ji4ujY8eOxaZNmzaNhQsXcvnyZQ4ePMiUKVOIjY2lXj0HPWe/5eXNi9mTnECrqrWZ0+uJYvNSszKYtH4BV7Nu0DOqGa92VN5hz8rLpcEXz7NowER6RzU3u1xJg398n/Scm/jovFjQ/xlqBBW/HbP48BaWHN6KwWjk6wGTyDMa6Lp0Go3CIvHRebFm6Ksmy72YcY3BP77P0asXuPb8V4Vd8xc4dPk8z25YQH4+zL3vKVpUrsXE37/k8JVENBr4bx9lmhDC9fmHQMcxcCUBLh0Hfa7y5K96cwh2XseGwgZdo2GXhe7Fy6NLtH3LuxsdPbeLT1e/gEajpWHNdkwoMvD0yQv7mPvTJLRaLU/1m0nzut04m3KEOd8rvUm3qt+LMf1mOCXO6KrKk4ZLdhxrrlM98LLchMkjtawFK/dAhh0H9u4S7dgbMjotdIqG3w7Yr8zwCtCwmv3K8wQ+XvDUvcpQELsSIP0m+PsoTwgbVVdu+KnNbZLAjIwM4uPjmThxYrHpM2bMYMYM55x4C/yVcpqM3Gw2j3idZ9cvIC7pFG2r3U4639zxA693eYRGlSKLrffVwc00q1zT6nIlfdDrCepUrMKGMwf5b9xa3uv5WOG8CzdS2Xr+GOuG3e7R70z6ZXpHNefrAZNMFVcozC+QdcP+xdCVH5icP/2P71k8YDJajYbJGxby45CXeKX936hTsQonriUxdctyvhtUehgPR5m/+gXiE+OoH9maSSV65szOvcmMxUPJzs0k0C+E10Z/h9FoKDWtYIiHspS9+9hvLN/8DgCJl4/z3EPz6dJssNl1ftjyAVsP/sCHk7aZ3JalC40C6+O+4fc9X2M0Gnh15FIupydaXaesDl0+z8T1X6LTaKlXsSpf9BtfrNfckjc6zqRftunmgql1i1p3ej/v71oNQPy1JOb2eZJB0e0A+ChuDT+d+JOYEdO5mZfDiNUfkZmXQ7BvAMsGPlc4bIS99tFUrJbic3daLVSpr/wI11WzErSro3Qfbg+d6yuvHjnblfSLTFv4IGdTjvDzmxmF460WOJ18iA9XPI1Wq6N6pfq8POwrNBpNqfNfeIjl70h7qVoxivfHb8LH24+3vx3F6aSD1KnWHICvf/83rz32P4ICwnjj64d4u+5v/LLjU8Y+8DYt6t7L/31+HxlZaVTwd/yB1mhgSBtlsGl7qBgAvVTqNLq8dQSsf9fag7cOBraCZTvtU161EOjkhPNvj0bKjaRrdhr7dUgbdZIWa9dLyalnmDy3A7WqNMZL58O7T/+OwaDnnWWPcS0jhYY12jHuQfXGINVoICpc+XFFLvs6aEkVKlTAYDAwefJktUNh18WT9I5Svhh6RTVnZ9KJYvMPX0nk3V2ruO9/b7LzYjwAuQY9u5JO0Kl6A4vLmVKnovKOhrdWh1Zb/E+2/swBDPlG+n73FlM2LsJgNAIQe/4IPZe9wUdxa8yW6+flQ6if+V4C0rIzqRlcicigMNJvjSJ9OxYvdBrnVZ8TiXvJysngg4lb0etzOX5+d7H5u4//RqNaHZg9IYaGtdoTd+w3k9PKU3a7Rv2YPSGG2RNiqFKxFq2j+5hdJ1efw6mL+yzuS8GFxoeTtpGWcYnTSQeLzb+SfoEDCbG8P34jsyfEEB4SaXWd8mgYVo0tI99g84jXAdiTnFA4r+iNjlyDnrgk5bFE76jmbBg+zWICaG7dAn3rtGTD8GlsGD6NmkGVCj9LOfo89l86W7jcutP7aVetPhuGT6NdtXqsO7PfrvtoLlZz8QnhTEPaWB/Tb8pS6x06VKoAf1Np7MfggDDee3ojjWt1NDm/ZuWGfPTsdj6YuBWA+MQ4k+c/ZwkLjsDn1pikOq032iJvx2RkXaNyxRr4+QSQnZdJTl4WNSo3JDM7HYPRAIC3mZuMjtC4uvVkwpb6oQEe7aA8oVBDeeoI2PZday/t61pvu2XLsdZpYWQn5zxx9fNWOp+xlrfZEnf7uuqNZWfLtU+b6PuYPSGGd5/+HYBth36ibvWWzHpmMzn6LE5dLPu1g6dwmyTQlaTlZBLsq7xwHeLrT1r2zWLzd1yM5x8dBrHkwcn8M1YZ7+mbQ7GMbNzV6nLmGIxG3t75E+NaFu+ZMOVmOrkGPeuGTcXfy5fVJ+OoFliRw0/NZv2jr7Hp7CEOXD5Xrv00FhlZumQXsq9tXc6zrfuWq9zyOHpuJ20a3AdA6+g+HDm7o9j86pXqkZ2rJKqZWWkEB1YyOa08ZRdIuppAxaCq+PtWMLvOb38u4L62T5hcv4ClCw2AuOPrMBgNvPJZbz5eORmD0WB1nfLwLnLH1dfLmxrBt4+PuRsdttxcsHaTpEBCWgpVAkOo4KPs18KDMYxudm/h/LoVq5KZp7yDk56dSSULNyzKs4/WYi0ZnxDOFOCrtBkJuoPqF+IPz/RULgjV4OPtR1BhQ9TSvHS3A/P28qVySE2T5z9nS7h4gPTMy0RVvf14LCSwMqeTD5GWcZkzyYfIyEqjTYP7+GTlczz1XkMaR3XC19u5/RU83Baa3mGOPKyDklCqpTx1BGz7rrUXjQZGd4Y6d/A0R6eBJ7o6t3OVBhEwopP1RNCSRtVgWHu7hVRmtlz77Du1mRc+6cYPW5SnhEmpCYXj4tarfg9Hzmx3XsBuRpLAcgjxDeB6jjLe0vWcLCr6Fb9dGx1ajcaVIqkaGIJWo0VvNPD7mQP0q3uPxeUs+UfMEkY17Ua9isUb74T4BHBvzcYA9KzVhGOpF/H18ibQxw8vrY4H6rXi8JXz5drPoicObZHf/rtnLY0rRdKlRqNylVseGVlpBPgGAxDoF0JGVlqx+ZHh0Rw9u4O/z2pKfGIcTaI6m5xWnrILbDv4I12aDTG7jt6Qx/5TMbSqb1sPG6YuNACuZaSgN+Ty/viN+HoHsP3wKqvrlNfPJ/dwz8J/kJKZXizJMnWjw9abC9ZukhRYeWI3g+u3BSDPoFcSzFpNC+dHh0awK+kELRe+wp6U03SKbGCynPLuo7VYi8YnhBoiQuC5+8vXk2Cdysq6lYPtH5c9bT+8mnGzmpF2I4XgwEoWz3/OcP1mKh+vfJaXhi4oNv3vD7zDp6tf5KMfnqFOtRaEBIazaN00Xhv9HQv/Ec+ZpIMkp55xaqxeOqXNUfdGZb/QD/RRuqd3xquJd6pkHSnrd609+HrDM72V17TLqmIAjO8FLWpaX9be2teFsd2hQjkeUndrAH/v7hptRc1d+4QFV2Ph/8Uza/xm9p7YQMLFA9Ss3JADp2IB2H9yMxnZpq/phBu1CXQlHatH88X+jQxt1JFNZw/xeJGnF6BcvCZlXCPYxx+90UBKZjrnr1/lwRXvcCothbUJ+2g9tE6p5QAuZaYT6hdY7AnGwoOb0Wg0jG5afDsAHSOj+eqA0jBg/6Wz1A6pzI3cLIJuDSS7/UI8k249sbtwI5XIINv7Dg71q0DijatoNVqCbl0orz9zgB0X4vl24HNlOGK2S72ezFtLiw/gEhYUQbM63biZo7SCz8y5Xqrdxfq4r+nYZCDDerzC9zGz2Lh3Cdm5maWm3df28VLbDPQLsVh2gR1Hf2b64z+aXWfDnsX0ajXSpv0suNB47bHvTMbTom53AO6p36vw9RdL65TXwPptGFi/DVM2LuLXhL8YfKvtm6kbHb5e3vii3JUtuLlgqmMgazdJCvx6am9hm9KlR7YxvHHxJH3x4S0MqNuKl9oPZM7uX1h6ZJvJz0ByZhqP/Ty32LSqgSEsvVVHze2jtViLxieEWioHwfP3w5bjEHMU0q2M917QvqtrtNIG1BnMnbenPrbc6rqdm/6Nzk3/xscrJ7PzyC9mz3/OUNCW6OkHZxEWHFFsXo3KDXj36d9Jz7zC/NUv4KXzJj8/nyD/MLRaLQF+IWTl3HBarAV0WuXV4RY14dd9kHDZ8vJeWmhdGwbeA0FOfHBpzzqSkXXN5u9ae/L1glGdld571+63Plajr5cyFmD/Fuq9bgvKq6z/fBB+2QdxZ0Bv5eF67XAYcI/SAZGzWKoflq59lH4elAy3Y+MHOZNyiO4tH+Wvkxt55bPeRITWJrSC9HxmjiSB5dCqah38vLzpuewNWlaJol21+iRnprHwYAyvdhzMvzs/wuhfPiZLn8trnR8iMiiMHaPfBOA/f6ygS42GhPpVKLUcwCsxS5h574hiydrkDQtpF1GPPstn0K1mY17v8gjv7VrNqCZduadKbfy9fOizfAaV/IN4vu0DbDx7kOl/fI+vzpsukY1oX60+eqOBv//2KWuH/quw3DyDnoE/vMuBy2cZsOIdZnR7lFrB4bf3o8sjjLp1cf1RnzEATNn4NcE+/tz3vzdpEFaNT+7/u12PbVhwBLMnxJSafiJxL7/u/IzuLYfx14kN3N92TLH5+eQTFKAcs+DAcDKz09FqdaWmGQx6rt+8SmjQ7ZNCk6hOFssG5QTlrfMpfKXU1DpbD/7AqYv7+GXHp5xNOczKbXMZ2GlCqe1ZutBQyu7Mml1fAHDq4j6qhdWxuk555OjzCjtaCfLxx9/r9reUqRsdpm4u6I0GrmZlUDUwxOK6JSVnpuGj86KSvzIw3fHUJA5cPsMX+zdy5Eoi8/auQ6fREuavPLkL9w/iek6Wye1FBFZkw/BpZd5HS7GWjE8INem00LMx3NsQDl+AkymQmKoMYq1BeWW0ZiXloq1xded3O27uvG1Nrj6nsLOuAN9gfL39TZ7/nCX2wPfEn9/NF7/+A4Cx/d9m075veXbwXNb+uYCNe5fg4+3P5CHzAHi05//x7vLRaLU6alVpXNiJjBrqVVGe/F68BgcSlfpx6ToYjMrrwNVDIaqS0s1/oPOaLhayZx05em5nqe/awV2d119E00hoUh3OXoUjF+B8KlzNAKNRSfYiQ5VE6p4o9V7FLqmCnzJkwcBWsO+sEvvFa5CVp5wvKgcp4ws2qwE1HDTOoCXm6oe1a5+b2TcIuDXA7eEzfzCo62R0Wh3PDlauXT9Y8TRtGzqv6ZK70eTn55ds7iVM0M/fSH7CJYdvZ/L6r5h731N2L3dvcgIHLp9jTPMedi+7gKZuFbwm9La+YAlxyyEt0fpy81Y9z8kLe6lX/R6eHTyX1OvJrN29gFG9p5KRlcabSx4lT5+Dl86bqY/9D61GW2rajZup/G/zu7w49Aubywb4Zcdn6I15DO7yrNl1ipoyrysfTtrGhSsnS21v01/L+GTVc0RVVV59HNv/bSLC6hTb3mc/v0x8YhwhgeG8OvJbth78odQ6TWp3AqBiDWhb/AaaWUXr8eqTcYVt++qHRjD//r9z6eb1wpsAL276mr9SztCyShQf9h7D2oS/it1ceLv7CE5eS2bWnz/zad9xxbZTct2iN0kAvti/kTyDnokm2pX2WDadmBHTScvOZNQv/yVHr8dbp2Ppg8+Rmp1hcnvmWNtHU7Faiq+8dVyIu40t5229IY9/fdmfExf2UD+yNU/1n0nVilGF57rth1axYuscQHml/4WHP0er1ZY6/3kXuXlTlvNdeWJ2lDuJ2x3ZeqzLW0cKFHzXFuVpx9od2Vo/rF0v7Tq6hq/XTcPby5dmdboxbsC7XEm/wNvfjkKr0dKnzeP0bTemWJlSP26TJNBGzkoC3Zmjk0B72HrgByoEhDqtLYEztlfeJNAefoz/k1C/wGJt+RzJ2dsrSZJAIRRqJVSSBLoHOdbCEqkfrkFeBxUepVuLh+/q7TnbQw2c222Ys7cnhBBCCHE3kiTQRprqKoy062bKe4yCqtg5EA9TluMn9fjOyPETQqHWeftOtqvmd42nfc/JsRaWSP1wDfI6qBBCCCGEEEJ4EBknUAghhBBCCCE8iCSBQgghhBBCCOFBJAkUQgghhBBCCA8iSaAQQgghhBBCeBBJAoUQQgghhBDCg0gSKIQQQgghhBAeRJJAIYQQQgghhPAgkgQKIYQQQgghhAeRJFAIIYQQQgghPIgkgUIIIYQQQgjhQSQJFEIIIYQQQggPIkmgEEIIIYQQQngQSQKFEEIIIYQQwoNIEiiEEEIIIYQQHkSSQCGEEEIIIYTwIJIECiGEEEIIIYQHkSRQCCGEEEIIITyIJIFCCCGEEEII4UH+H/Tv5YufplJTAAAAAElFTkSuQmCC", "text/plain": [ "
" - ], - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA4EAAAB7CAYAAADKS4UuAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjMsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+AADFEAAAgAElEQVR4nO3dd3gU1frA8e/upockJAQIBAgt9CK9S1VA5AIqSBFFuYiAKLb7u17kihfFBqgXERuCAsJVVEAFkZYAUiQgvQQILZCEEhJISNvd/P4YElK2Jezu7LLv53nyQKaceWdydnbemTnnaPLz8/MRQgghhBBCCOERtGoHIIQQQgghhBDCeSQJFEIIIYQQQggPIkmgEEIIIYQQQngQSQKFEEIIIYQQwoNIEiiEEEIIIYQQHkSSQCGEEEIIIYTwIJIECiGEEEIIIYQHkSRQCCGEEEIIITyIJIFCCCGEEEII4UEkCRRCCCGEEEIIDyJJoBBCCCGEEEJ4EEkChRBCCCGEEMKDSBIohBBCCCGEEB5EkkAhhBBCCCGE8CCSBAohhBBCCCGEB5EkUAghhBBCCCE8iCSBQgghhBBCCOFBJAkUQgghhBBCCA8iSaAQQgghhBBCeBBJAoUQQgghhBDCg0gSKIQQQgghhBAeRJJAIYQQQgghhPAgkgQKIYQQQgghhAeRJFAIIYQQQgghPIgkgUIIIYQQQgjhQSQJFEIIIYQQQggP4qV2AK7CsGoP+RfT1A7jrqWpXhHdoDZqh+FxpF47li31+vgmuHHJSQG5uaAq0LBX+ddX61jfadxCCCGEs0kSeEv+xTTyE+RKTdxdpF6r78YlSEtUOwrPIMdaCCGEsI28DiqEEEIIIYQQHkSSQCGEEEIIIYTwIPI6qBBCCCGEEELYWU4eJKdDrh68dFAlGAJ91Y5KIUmgEEIIIYQQQtjBjWzYdQriTkNKOuSXmB8WCC1rQZdoCA9SJURAXgd1edGfP8fSI9tsni6EO5B67TyPzazNhj1LbJ5+t3tpfg8e+KcvA6dWYNC0EMbPuYfY/d+rHZYQQgg3ZzTCpiPwxkr4ZZ/yBLBkAgiQmgmbj8Jbq+GH3ZCjd3qogDwJFEII4WFG9ZnGqD6vYTDoWbX9Y97+diT1I1sRGV5f7dCEEEK4oZu5sCAWTpWhQ/Z8YGs8HE2C8T2hspOfCsqTQCGEEB5Jp/Oif4dxGIx6Tl3cp3Y4Qggh3FB2HszfWLYEsKgrN2DueriaYd+4rJEkUAghhEfK0+fyy/b5ANQIb6ByNEIIIdzRT3vgfKrlZT4cpfyYcz0Lvt4GBqN9Y7NEXgd1cymZ6QxdNQcfrRdZ+lxmdHuUXlHN1A7rrmHQgz4HvHxA5612NJ5D6rXzXLuRwvSvh+Cl8yE3L4sn+8+kdXRvtcNyqG83vsX3sbPIyrmBTufNi0O/pG71FgCs/XMBG/YsLlw2KTWB5nW68erIpWqFa5IxH27mgEYDAT7Kv0IUlZOn9Ejo76P0SigcJzsP8gzKZ1HnJo9XDEblHOLjBb5yfVNux5KUTmDs4dxViD0GvZrYpzxrXDoJNBqNzJkzh88++4zz58/TsGFD/vvf//L000/TvXt3Pv/8c7VDdDhvrQ69oXSL0TyjAW+tjnD/IDYPfx2dVktCWgqjfp7LjtFvqhDp3SXzKpz5E5KPQb5BucCqHA2120NwhNrRuT+p186j03mjN+aVmq435OGl8yY4MJw5E7ei0+pIuprAm0sepfXzu1WI1HlG9p7KqD6vcePmNWZ/P5b9JzfTv/1YAPq3H1v4/9Trybz8WU+e7PeWmuEWYzAqbUi2Hr/96lDlIOjWUOlpzl0uQIXjxCcrnVMcS1J+9/GCDnWVC8vQQHVju9scPK908JFwWfk9wAc61odejaGCn7qxmZN2U6kfu07d7pCkYYRSPxpWUzc2d7T+kH3L23QU7m3onBs3Lv11MXbsWGbMmMH48eNZu3Ytw4YNY8SIESQkJNCmTRu1w3OKqJDKnExLKTYtIzeb5Mw06oZUQafVotMqf8b0nJs0r1xLjTDvKmkXYNcSSDqiJIAA+flw6QTs/hYu2+mOjyeTeu08EaG1uXjlZLFpWTkZXLuRTLVKddFpdei0yrdNRlYadau1UCNMVQQFhPLi0C/ZdexXth9aVWye0Wjk7WWjGNv/bSLCaqsTYAl6A3wRAyv3QGqRtiNXbsCPcbBwi3NfJRKuZ8dJ+GQjHE++PS1Xr9w4mL0WUq6rF9vd5veDsGALnL5ye9rNXCXBmvMbpN9ULzZzLt9Q6sGW48V7pIxPhvmbYFu8erG5o+T08rcDNCcjGw6ct2+Z5rhsErhs2TIWLVrE6tWrefnll+nZsydTp06lU6dO6PV6WrdurXaITjG66b0sOLCJbYnHMBiNXMvO4MVN39AsvBb3VK0NwOm0S/RYNp0BK95hUHRbdQN2c0YDHFgFRj2l+/XNh3wjHPwZ8rLUiO7uIfXaee5vO4Y1uz7nYMJWDEYDN25e45NVz1M7ojn1q7cCICn1NFPmdeXVL/vSpdkQlSN2ruCAMB7u9iJf/fYvjMbbGdTi9W9QJ6I5XZoNVjG64jYUebpT9PRU8P9DF5RXiYRnunQdvtul/D/fRL/0mbmwaKvpeaJsEi7BmgPK/00dz2uZsHyXc2OyxTfbICOn9PSCXVixG5LSnBqSW4tPtr6MK5Vbksu+Djpz5kz69etH9+7di02vX78+3t7etGih3K0+c+YMTzzxBElJSfj6+vLJJ5/QrVs3NUJ2iJFNupKlz+W5DQs5d/0KFXz86FajMT8NeRmvW3fv61SsQsyI6SSkpdD3u7cYUM8zEmRHuHQCcq3cvTPq4eJhiJK8pNykXjtP79ajyMm7ydyfJpGSdhZ/nwq0qNudGU/9jE6nfAVUC6vDh5O2kXQ1gVc+60XHJg+qHLVzDen2PD9u/YD1e76hb7sx7D2xkT3xvzN7QqzaoRUyGG27S7/1OPRoBFqXvcUrHOWPE6bHJCuQn69c4J++DHWrOC2su9LWeNBg/njnA0cvKk/p1RwMvKhzV613XqIB/oiHR9o7JSS3Z+14ulq5JblkEpiYmMihQ4d44YUXSs07d+4cTZs2xdfXF4Dx48fz6KOPMnHiRLZv387QoUM5ffo0Pj4+FrehKdGKfv2jr9G9ppNaYpbR2Ba9GNuil8l5Ofo8fL2UFr3BPv4EervmS+gxsTHcN7GP2mFY9fzDn9K//Vh0WvMfDYPRwDfzfuXfiwY5MbLykXrtWLbU61nPbKZlvR7OCciMBzqM44EO40zOy9Xn4OOlnE8D/ILx96ngzNCKiY2Nod2InuVe35ZjPXtCTKlpgX7B/Pgf5Vs39XoyH698lplj1+LtZfl7pMCdxm2LsOqNGf3eEavLXbsJoRF1uH75jEPjEa5n1Mz9hNey/jr38HFT2b16phMiuns9Pf8y/kHhVpfr/uBTHNmy0AkRWdd6wMt0G/G+xWXygZ+3HGdoh0bOCcrNDfnnemo1K34NYKkHUEvzpxTpe+zEmUtoNFXLHVe+jY/7XTYJBIiIKN4DR1ZWFrGxsfTv3x+AK1eusG3bNlavXg1A586dqV69Ops3b6Zv377ODVolccmnmLbtO3QaLXlGPXN6Pa52SG5Np7HeElcDaLXS1ZojSb12nvjzu/lq7b/QanXoDXlMGPSR2iGpasmGGWRmp/P+/8YUTqtZuSFTHvlMvaAATRnOOWVZVtw9NDrb/u5SP+6crcfQlY61rdctrhSzqyv5QMmOBTum3JKbybc1XXSikydPEh0dzQcffMCUKVMKp7/xxhtMnz6defPmMXHiRPbu3csjjzxCQkJC4TLDhg2jT58+PP3002Xapn7+RvIT7Ny6UxTS1K2C1wTX73Y+cR8c22B9uTodoV5Xx8dzp6ReO5Yt9TpuOaQlOikgN1exBrQdXv711TrWdxq3LXL1MO2H4p05mBLgA/95SIYE8ETf7oDdCZZfCQUY1wOaRjojorvX/I1Kuy1rx/ql/lAzzCkhWXU8Sen8xRIN0CoKHneD6xtX8PU2+OusbcsWPAGcYsNoQ1WD4dWB5Y/LVi75JLBu3bq0aNGCmTNnEhYWRmRkJCtWrGDNmjUAHtMzqHC+iCZwIhYMpXvUv00DkZ7TgaIQwgX4eEGHekqvfpZ0qi8JoKfqEg1/JpifrwFCAqCxDANwx7o2KN4Da0kaDdQMdZ0EECA6AipVUHoWttSWsWsDZ0bl3mqG2Z4ElrVcZ3DJpuNarZbvv/+epk2bMmHCBJ588knCw8OZNGkSOp2usFOYWrVqkZKSQk7O7a6OTp8+TVRUlFqhCzfn5QNN+t36xczT+AY9wS/YaSEJIQQA/Zord4jNiQyF+5o5Lx7hWqLCzQ8yrdEonQWN7iydBtlDsxrQro7peRrA1wuGd3RqSFZpNfBYZ9DpzF7e0L0R1Kns1LDcmqM6WHJWx00u+SQQoEGDBmzevLnYtNGjR9OkSRP8/f0BCA8Pp0uXLixYsKCwY5gLFy7Qs6djG+iLu1vVhuDlC6f+gOtJt6cHhkPdTsp8IYRwtgBfeP5++GUf7D4NebfGMfXWKU8JH7wH/LzVjVGoa+A9ytOejYchNfP29OiqMKClkiiKO6fRwIhOEBECMcfgRvat6UDTGspnMSJE1RBNqlNZOYf8uu/2cDMAoQHKDYSuDZzWHO2uEFUJqleEi3YcVsPXC1rXtl95lrhsEmhKXFwcHTsWv7Xy6aefMmbMGD788EN8fHxYtmyZ1Z5B78SfSSd5efNitBoNbSPqMavnaKvz153ez/u7lM5r4q8lMbfPkwyKblfmbb+8eTF7khNoVbU2c3o9UTjdXPk383IYsfojMvNyCPYNYNnA5/D18mbx4S0sObwVg9HI1wMmERlU+rnzxYxrDP7xfY5evcC1578q7LYfMFuupXXcTaXayk9mKuz4SpnW8Ym79+Ro7W+nNxp44td5XLp5nTYRdXmn+0irn4WizNVdgDPpl+m6dBqNwiLx0XmxZuirJtcxFYM999Pc/tjr8wtwJf0i0xY+yNmUI/z8ZkbhEA0Au4/9xvLN7wCQePk4zz00n4Y125td3tZyrc3/YcsHbD34Ax9O2mbX/TE3Pzv3JjMWDyU7N5NAvxBeG/1dYe+kjmYp3nmrnufUxX3k5WUzfuAcmtXpwtKNb7F6+zz6tXuKJ/u96ZQYrQnwhWEdYGArePV7ZdqMhyX5EwqNRnkttFN9ePFbZdq0QUpiKOxLq4HeTaFHY3hpmTJt+hDllVtXVjMMnumljGX4xkpl2rTByv6IstFooGdjWLrDfmV2jnbe+dxtXgrIyMggPj6+1CDxdevWZcuWLcTHx3Po0KFS4wraW63gcH4fNpWYEdO5dDOdg5fPWZ3ft05LNgyfxobh06gZVIneUc3LvN2/Uk6TkZvN5hGvk2vQE5d0qnCeufLXnd5Pu2r12TB8Gu2q1WPdmf1cuJHK1vPHWDdsKhuGTzOZAAKE+QWybti/6FCtfql5psq1to67CixyeO7WBBCs/+1WnthNiypRrH/0NbL1uey/dNbqZ6GApbpboHdUczYMn1aYAJpax1QM9txPc/tjj89vgeCAMN57eiONa5V+T6hdo37MnhDD7AkxVKlYi9bRfSwub2u5lubn6nM4dXGfQ/bH3Pzdx3+jUa0OzJ4QQ8Na7Yk79lu5t19WluId/+As5kyI5bXR37Fsk9J9/gPt/86rI2xoxa8C/yL3OiUBFCUVvaCXBNCxdEWupF09ASwqNPD2/yUBLL+2daCRndrZhleAfk7sc8JtksAKFSpgMBiYPHmyqnFEBFbE79bYUd5aL3Qarc3zE9JSqBIYQgWfso95tuviycKLz15RzdmZdKLUMiXLr1uxKpl5SnvJ9OxMKvlVYP2ZAxjyjfT97i2mbFyEwWg0uT0/Lx9C/Ux/c5gq19o6wrVZ+9udTrtE8/BaALSsEsXOi/FWPwsFbKm7seeP0HPZG3wUt8bsOqZisOd+WtufO/n8FvDx9iMoINTiMklXE6gYVBV/3wo2LW9Luebm//bnAu5r+4SJNWxTnu1Wr1SP7FzlPbXMrDSCAyuVe/tlZSleL52SSWXlZFC3eksAQoOqOq4LcCGEEG5Po4ERHZVXai2ZstRyz6A+XjC6i/I6qLO4TRLoag5cPseVm9dpEl7D5vkrT+xmcP225dpeWk4mwb5KW8gQX3/Ssm+WWqZk+dGhEexKOkHLha+wJ+U0nSIbkHIznVyDnnXDpuLv5cvqk3FljsVUueLu1iCsGlsSjwIQc+4IaTm365+1z4K1ulstsCKHn5rN+kdfY9PZQxy4fM7kOpZisCdz+3Mnn9+y2HbwR7o0G+Lw7egNeew/FUOr+r0cvq2iIsOjOXp2B3+f1ZT4xDiaRHV26vYtmb5oCP/84n5aR/exvrAQQgiB8gR4Up/yP3X384bxPZ3fZtet2gQ6U3JmGo/9PLfYtKqBISwd+BypWRlM2biIbwc+Z3Jdc/N/PbWX7wa9UK5thvgGcD0nC4DrOVlU9Ct9y6Fk+YsPb2FA3Va81H4gc3b/wtIj2wjxCeDemo0B6FmrCXtSTls4CqaZKnd003vLXI5wPkt1zJIH67Vh87nD9P3uLaKCK1M1QGnxbu2zAFitu75e3viiPIV5oF4rDl85b3IdczHYax+t7Y+1z6+97Dj6M9Mf/9Hh29mwZzG9WllvV5l6PZm3lhYfBC8sKIKpjy0v13bXx31NxyYDGdbjFb6PmcXGvUu4r+3j5SrL3qaP+YnLaYn8Z/EjzJ28U+1whBBCuInwIHi5P6zaCztLt3oxq2GE0pNs0ddznUWSQDMiAiuyYfi0UtP1RgNj1szj3e4jiQisaPP85Mw0fHReVPIPKlzualYGVQNvX8ia2yZAx+rRfLF/I0MbdWTT2UM83qx40lWyfID8fAjzV25LhPsHcT0niy41GvLVAaXX1f2XzlI7pLLJWCwxVa5wD5bqmCU6rZYPe48BYMLvX3Bf7RYm67qpumSt7t7IzSLIR3nqt/1CPJNa98Vbqyu1jrkYyvI5ssTSZ9vU58sRUq8n463zsfiKpMGg5/rNq4QGVb2jbZ2/fJxTF/fxy45POZtymJXb5jKw04RSZYcFRzB7QswdbauofPIJClAa2wYHhpOZnW63su9Erj4HHy9f/H0r4OejwrexEEIIt+bvoyR0naNhWzzsPQt6Q+nlNBpoUl3pxKlxdfX6nJDXQctoxfFdxCUn8GrsMvosn8HOi/EkZ6bx9s6VZucD/HxyDwPr3R7k/kz6ZV7f9p3N221VtQ5+Xt70XPYGOq2WdtXqF9tuyfIBhjfuzIrjO+mzfAbLjv7BiMZduKdKbfy9fOizfAZxyQk83KCDyVjyDHr6ffcWBy6fZcCKd/gz6WTh9kyVa24d4R4s/b0BLtxIpc/yGdz/vzfpVL0BkUFhJuu6qbpkre5uSzxGh8X/4t5vX6d6hTDaV6tvch1TMZT1c2RpP819dsH056s89IY8/vFZHxKS9vPPL/ty9NwuUq8ns3TjWwBsP7yKTk0HWVw++doZFv72WpnKNTV/3IB3eWfcOt4e9xtRVZsyuOtkk2Xfyf6Ymt+r1Uhi93/HS/N7sOmvpfRqPepOD2u5491/KrYw1reWPMpL83swbeFAnrj/DQDW/rmAz35+iU17l/LfHyc5LU4hhBDuq1YlGNkJ3hmqPB0c2en2vOfvh3eGwbge0CRS3U4HNfn5+fnqbd516OdvJD/hktO292P8n4T6BdKzVlOnbVPNWDR1q+A1obfDynekDbOUf/u8rG4c5XG312u1P0e21Ou45ZCWaL9tbj3wAxUCQh3Sls+RZduiYg1oO9z6cubY+1jb6k7jLq+CTgY+dF4eLdyI1A/ncddj7a5xuyNXPNbyOqhKHmrQXu0QCrlSLMK9ObsueWLd7dbiYbcsWwghhBCuQ14HFUIIIYQQQggPIk8Cb9FUL93Ji7AfOb7qkOPuWLYc36AqTgikjApemaxoelQP1dzpsVLrWLvi31gIIYSwRJLAW3SD7rzTByFcjdRr9TVUp3mdRQXtXNVox+ZIrnishRBCCFckr4MKIYQQQgghhAeRJFAIIYQQQgghPIgkgUIIIYQQQgjhQSQJFEIIIYQQQggPIkmgEEIIIYQQQngQSQKFEEIIIYQQwoNIEiiEEEIIIYQQHkSSQCGEEEIIIYTwIJIECiGEEEIIIYQHkSRQCCGEEEIIITyIJIFCCCGEEEII4UEkCRRCCCGEEEIIDyJJoBBCCCGEEEJ4EC+1A3AXhlV7yL+YpnYYLk1TvSK6QW3KvN7xTXDjkgMCsrO45WpHYFpQFWjYy7ZlpR7fmfLWcSHuNmqdt8tyvitJze+aO4nbHcmxFpZI/XANkgTaKP9iGvkJbpCpuKEblyAtUe0orHOHGK2ReiyEsAd3OW8X5Y4xuys51sISqR+uQV4HFUIIIYQQQggPIkmgEEIIIYQQQngQeR1UCCGEcAPGfDiZovwkpt6e/kUM1AiD6KpQrwpoNKqFKFR25QYcugCJV29Pm7seqodCVCVoXgN8vdWL726SnA5HLsD5Ip/FeRsgMhRqV4amkeCtUy8+U3L0cOg8nL0KF67dnr74D6gZBk1rQOUg9eITziVJoIuL/vw5pncdxqgmXW2aLsrvsZm1GdP3Tfq0ecym6aL8pF4LYTtjPuw6BZuOwOUbpecfvqD8rDsIVYOhd1NoV0f9ZPCl+T04enYHOp03Wq2OiNA6jOw9le4th6obmAXuGDMoNwXW7IejFyG/xLxTl5SfrYCfN3SoB32bQ4CPGpHe5q7H+lQKrD2o3Iwp6USK8sMxCPSFLtHQpyn4qHy1nZ2nnB92nFT+X9KeM8rPyr3QqBo80BJqVXJ2lKW5ax1xF5IECiGEEC4q7SYs3X7rwtIGKdfh2x2w9wyM7ATB/g4Nz6pRfaYxqs9rGAx6Vm3/mLe/HUn9yFZEhtdXNzAL3ClmoxHWHYL1h5SbBdZk50HsMdh3FkZ0Ui741eROx1pvgFV/wdbjti2fmQO/H4K/zsKozlA73LHxmROfrJwT0m7atvyxJGWd3k2gXwvQqdxwzJ3qiLuRNoFCCCGEC7pyAz5cZ3sCWNSxJPjod7iWaf+4ykOn86J/h3EYjHpOXdyndjg2cfWYjUZYsl15wmNLAlhUehZ8vll5+uMKXP1Y5xmU165tTQCLunwDPt4Ax5PsHpZV+8/Bp5tsTwALGPNh/WH45g8wGB0TW1m5eh1xR5IECiGEEC4mO698F29FXc1QysjV2y+u8srT5/LL9vkA1AhvoHI0tnH1mFf/BXvPln99Y77ylPlUOW4y2JurH+tlO+B4cvnX1xvgy1hIcuIwvacvwzfbyn6DoKj95+CnPfaL6U64eh1xR/I6qJtLyUxn6Ko5+Gi9yNLnMqPbo/SKaqZ2WHelazdSmP71ELx0PuTmZfFk/5m0ju6tdlh3JanXwtOt/guuZFhe5sNRyr9TlppfJuU6/LofhrSxX2xl8e3Gt/g+dhZZOTfQ6bx5ceiX1K3eAoC1fy5gw57FhcsmpSbQvE43Xh1pYYecwFLMF66c5K0lj/LRszvw9vLhu5j3uZlzgzF9/+PUGE+mQMwxy8vYUj+M+fDtTvjHAPBV4YrQHY71X2etJ9u2HOs8g/Ja5pS+jn/FMlevbMtgJQG0Je5t8dCiJjSIsF98ZWGpjsxcOpJerUbSscmDALy+aDADO02kbcP71QnWzbj0k0Cj0cisWbOIjo7Gz8+Pli1bEhsbS8OGDXn66afVDs8pvLU69IbSt3HzjAa8tTrC/YPYPPx1NgyfxuIHn2XqluUqRHl30Om80RtLt5jWG/Lw0nkTHBjOnIlbmT0hhn+NWsaCNf9UIcq7g9RrdeTnQ+q527+f3Ao3r5lfXqjj4jXYfsJ+5W05Bpev26+8shjZeyorZ6SxYvoV2jd6gP0nNxfO699+LLMnxDB7QgxTRy3HzyeQJ/u9pU6gRViKOTK8Pl2bP8zyTW+TlHqamH3LGdl7qlPjy8+HH+PsV97VDIg5ar/yysLVj7XBaN8nYedT4c8E+5VnztbjpjuRKq8fdiv1Tg2W6siEQR+yaN00snIy2HrwRwL9QlwuAUxJh5//uv376cvqHcuSXDoJHDt2LDNmzGD8+PGsXbuWYcOGMWLECBISEmjTRqXbmk4WFVKZk2nF39XIyM0mOTONuiFV0Gm16LTKnzE95ybNK9dSI8y7QkRobS5eOVlsWlZOBtduJFOtUl10Wh06rdLfc0ZWGnWrtVAjzLuC1Gvny8mEP5fA3u9uTzuzC7YvgGMbIN9F2n0I+MOOCSAovUXau8yyCgoI5cWhX7Lr2K9sP7Sq2Dyj0cjby0Yxtv/bRITVVidAE8zFPKzHK+w8+gszl45gwt8+xMfL16lxnb4MF+38WuH2E+q2/XLVY33wPFzPsm+Z2+IdmwQYjfb/vKdcN90bqjOZqiOhFaowpOvzzFv1HN9ufJNn/vaBukEWob/15PftX2DjkdvTP/pdGbYlM0e92Aq4bBK4bNkyFi1axOrVq3n55Zfp2bMnU6dOpVOnTuj1elq3bq12iE4xuum9LDiwiW2JxzAYjVzLzuDFTd/QLLwW91StDcDptEv0WDadASveYVB0W3UDdmP3tx3Dml2fczBhKwajgRs3r/HJquepHdGc+tVbAZCUepop87ry6pd96dJsiMoRuy+p185l1CvJ341Lpucn7oMTsc6NSZhmzL+zdl7m7Dmj/t3n4IAwHu72Il/99i+MxtsZx+L1b1Anojldmg1WMTrTTMXspfOmed17yci6RrM6zh/OxhGduaRnKcNIqMlTjvWFa0pS5ShnrkCqAzqEcoVOhEzVkb7txpB4OZ7BXZ4jOCBM5QhvW7Hb/FPf05fhs81Kwq4ml00CZ86cSb9+/ejevXux6fXr18fb25sWLZSnMP/+979p0KABWq2WFStWqBGqQ41s0pUZ3R7luQ0LqfrxOFot+j+y9Ln8NORlvG49lapTsQoxI6azbdR/mLJxkboBu7HerUfxVP+ZzP1pEg+9Hsa42c3IyctixlM/o9MpjSWqhdXhw0nbmDt5Fx+vfFbliN2X1A/WUsoAABcvSURBVGvnunQCMq9SegCxIs7/pTwtFOq6egOycu1f7o1s5UJfbUO6PU/q9STW7/kGgL0nNrIn/nfGDXhP5cjMKxnzmeTDHD7zB63q92HNri+cHs+5q9aXKY/zDiq3LORY37lzqdaXcaVyy6pkHQGoXqm+Sw0ZcTUDdp4yPz8fpW4dvei0kExyyY5hEhMTOXToEC+88EKpeefOnaNp06b4+iqvBPTr148xY8bw1FNPOTtMpxnbohdjW/QyOS9Hn4evlzcAwT7+BHr7OTO0u84DHcbxQIdxJufl6nMKX0UJ8AvG36eCM0O760i9dp6LhwANFpPAfCOkHIdanvGShctKSndg2WlQMcBx5Zc0e0JMqWmBfsH8+B/lajL1ejIfr3yWmWPX4u2l8sjlt1iL2Wg08tGPzzB5yDxqhDfg+Xmd6dx0EKFBVZ0WY7KD6ogj654prn6ss3Idd+PEkcc62UE9kCanKW8TaDSOKd8Ua3XEVcWdtr6MBth9GprWcHg4ZrlsEggQEVG8K6KsrCxiY2Pp379/4bTOnTuXaxuaMtbi9Y++RveaTcq1LUeKSz7FtG3fodNoyTPqmdPrcdViiYmN4b6Jfcq83qxnNtOyXg/7B2Rn8ed389Xaf6HV6tAb8pgw6CO1QwIgNjaGdiN62rSsq9bjklypXhdV3jquts9e3G+1DWt+fj5vTH2br35zbscLoriGnUbQb9K3xaYV9OBnjrn5JXv8GzhoCKfiVt5BdLfZ47y9ZMMMMrPTef9/Ywqn1azckCmPfGZ2nbKc70qyR8w/75hPdGQbGtRQ+iUY03cGn6yewtRRyyyudydxl/T8kuJ3c+xVP5Yt/57RXYbdQWS33Q3HOiCkKuPmFR8Xwl7Hes4H/2XQ4ufvIDrz+k5YQqMuxQOxR9zGfPDy9sFoKN2BXlmped1nz8+iOT0en0vz3s+g1ZlPs/KBX3/fwpP3dje7THnl2/juv0smgeHh4QDEx8fzwAMPFE5/7733SEpK8phOYWzRpUYjNg3/t9pheIRmdboyZ+IWtcPwCFKv7Ss94xIGo6GwYyNTNBoN6TevODEqYYo+L9txZee6wPugRTz30Dyee2ie2mGUyaAuk4r93qXZYKe3ZdTnZuHl42//ch1Y98pD7WPt0M+iA8s2OKhso9FglwTQEf4xfJHaIRSTlXEFjYXvW1COZ9YNdb9zNfm2potOZDQaadWqFUlJScyaNYvIyEhWrFjBmjVrOHfuHDt37qRDhw7F1unRowfPPvssjzzyiENi0s/fSH6Cyq2mXZymbhW8JpR93Ly45ZCW6ICAPETFGtB2uG3LSj2+M+Wt42q7eBCOrLOykAa6Pg1+QU4JSZiRkq70JmcLW8b4KurfgyDMTm+xq3XeLsv5riQ1v2vuJO6SZq2BRBuGdilr/RjQEu6z03Csd8ux/vcPcN2GnKqsx3pER+hQr/xxWbL5KKzaa9uyZYm7Wgj834Plj6uou6V+mHPpOsz82fpyT3aDlip2fu6SHcNotVq+//57mjZtyoQJE3jyyScJDw9n0qRJ6HS6wk5hhBBCWFe1EfhXRGmEYEZkC0kAXUHlYMcM2h3oC6GB9i9XOF/NSo4pt5aDynVn7nisazqog0xHHYu7UZVgaB1lfr4GqFYRmqnYHhBcNAkEaNCgAZs3byYzM5Nz584xY8YMDh48SJMmTfD3t/9rEEIIcbfSeUProRAQemuChmIJYURjaGi6jx7hZFoN3GPh4qG8WkU5t0MH4TitHFA/KvhBvSr2L9fdOeJYR4QoP45SpzKEOOAy2RHH4m42vCM0v5XkFXzlFpyDq4fCM71Ap3IW5pJtAs2Ji4ujY8eOxaZNmzaNhQsXcvnyZQ4ePMiUKVOIjY2lXj0HPWe/5eXNi9mTnECrqrWZ0+uJYvNSszKYtH4BV7Nu0DOqGa92VN5hz8rLpcEXz7NowER6RzU3u1xJg398n/Scm/jovFjQ/xlqBBW/HbP48BaWHN6KwWjk6wGTyDMa6Lp0Go3CIvHRebFm6Ksmy72YcY3BP77P0asXuPb8V4Vd8xc4dPk8z25YQH4+zL3vKVpUrsXE37/k8JVENBr4bx9lmhDC9fmHQMcxcCUBLh0Hfa7y5K96cwh2XseGwgZdo2GXhe7Fy6NLtH3LuxsdPbeLT1e/gEajpWHNdkwoMvD0yQv7mPvTJLRaLU/1m0nzut04m3KEOd8rvUm3qt+LMf1mOCXO6KrKk4ZLdhxrrlM98LLchMkjtawFK/dAhh0H9u4S7dgbMjotdIqG3w7Yr8zwCtCwmv3K8wQ+XvDUvcpQELsSIP0m+PsoTwgbVVdu+KnNbZLAjIwM4uPjmThxYrHpM2bMYMYM55x4C/yVcpqM3Gw2j3idZ9cvIC7pFG2r3U4639zxA693eYRGlSKLrffVwc00q1zT6nIlfdDrCepUrMKGMwf5b9xa3uv5WOG8CzdS2Xr+GOuG3e7R70z6ZXpHNefrAZNMFVcozC+QdcP+xdCVH5icP/2P71k8YDJajYbJGxby45CXeKX936hTsQonriUxdctyvhtUehgPR5m/+gXiE+OoH9maSSV65szOvcmMxUPJzs0k0C+E10Z/h9FoKDWtYIiHspS9+9hvLN/8DgCJl4/z3EPz6dJssNl1ftjyAVsP/sCHk7aZ3JalC40C6+O+4fc9X2M0Gnh15FIupydaXaesDl0+z8T1X6LTaKlXsSpf9BtfrNfckjc6zqRftunmgql1i1p3ej/v71oNQPy1JOb2eZJB0e0A+ChuDT+d+JOYEdO5mZfDiNUfkZmXQ7BvAMsGPlc4bIS99tFUrJbic3daLVSpr/wI11WzErSro3Qfbg+d6yuvHjnblfSLTFv4IGdTjvDzmxmF460WOJ18iA9XPI1Wq6N6pfq8POwrNBpNqfNfeIjl70h7qVoxivfHb8LH24+3vx3F6aSD1KnWHICvf/83rz32P4ICwnjj64d4u+5v/LLjU8Y+8DYt6t7L/31+HxlZaVTwd/yB1mhgSBtlsGl7qBgAvVTqNLq8dQSsf9fag7cOBraCZTvtU161EOjkhPNvj0bKjaRrdhr7dUgbdZIWa9dLyalnmDy3A7WqNMZL58O7T/+OwaDnnWWPcS0jhYY12jHuQfXGINVoICpc+XFFLvs6aEkVKlTAYDAwefJktUNh18WT9I5Svhh6RTVnZ9KJYvMPX0nk3V2ruO9/b7LzYjwAuQY9u5JO0Kl6A4vLmVKnovKOhrdWh1Zb/E+2/swBDPlG+n73FlM2LsJgNAIQe/4IPZe9wUdxa8yW6+flQ6if+V4C0rIzqRlcicigMNJvjSJ9OxYvdBrnVZ8TiXvJysngg4lb0etzOX5+d7H5u4//RqNaHZg9IYaGtdoTd+w3k9PKU3a7Rv2YPSGG2RNiqFKxFq2j+5hdJ1efw6mL+yzuS8GFxoeTtpGWcYnTSQeLzb+SfoEDCbG8P34jsyfEEB4SaXWd8mgYVo0tI99g84jXAdiTnFA4r+iNjlyDnrgk5bFE76jmbBg+zWICaG7dAn3rtGTD8GlsGD6NmkGVCj9LOfo89l86W7jcutP7aVetPhuGT6NdtXqsO7PfrvtoLlZz8QnhTEPaWB/Tb8pS6x06VKoAf1Np7MfggDDee3ojjWt1NDm/ZuWGfPTsdj6YuBWA+MQ4k+c/ZwkLjsDn1pikOq032iJvx2RkXaNyxRr4+QSQnZdJTl4WNSo3JDM7HYPRAIC3mZuMjtC4uvVkwpb6oQEe7aA8oVBDeeoI2PZday/t61pvu2XLsdZpYWQn5zxx9fNWOp+xlrfZEnf7uuqNZWfLtU+b6PuYPSGGd5/+HYBth36ibvWWzHpmMzn6LE5dLPu1g6dwmyTQlaTlZBLsq7xwHeLrT1r2zWLzd1yM5x8dBrHkwcn8M1YZ7+mbQ7GMbNzV6nLmGIxG3t75E+NaFu+ZMOVmOrkGPeuGTcXfy5fVJ+OoFliRw0/NZv2jr7Hp7CEOXD5Xrv00FhlZumQXsq9tXc6zrfuWq9zyOHpuJ20a3AdA6+g+HDm7o9j86pXqkZ2rJKqZWWkEB1YyOa08ZRdIuppAxaCq+PtWMLvOb38u4L62T5hcv4ClCw2AuOPrMBgNvPJZbz5eORmD0WB1nfLwLnLH1dfLmxrBt4+PuRsdttxcsHaTpEBCWgpVAkOo4KPs18KDMYxudm/h/LoVq5KZp7yDk56dSSULNyzKs4/WYi0ZnxDOFOCrtBkJuoPqF+IPz/RULgjV4OPtR1BhQ9TSvHS3A/P28qVySE2T5z9nS7h4gPTMy0RVvf14LCSwMqeTD5GWcZkzyYfIyEqjTYP7+GTlczz1XkMaR3XC19u5/RU83Baa3mGOPKyDklCqpTx1BGz7rrUXjQZGd4Y6d/A0R6eBJ7o6t3OVBhEwopP1RNCSRtVgWHu7hVRmtlz77Du1mRc+6cYPW5SnhEmpCYXj4tarfg9Hzmx3XsBuRpLAcgjxDeB6jjLe0vWcLCr6Fb9dGx1ajcaVIqkaGIJWo0VvNPD7mQP0q3uPxeUs+UfMEkY17Ua9isUb74T4BHBvzcYA9KzVhGOpF/H18ibQxw8vrY4H6rXi8JXz5drPoicObZHf/rtnLY0rRdKlRqNylVseGVlpBPgGAxDoF0JGVlqx+ZHh0Rw9u4O/z2pKfGIcTaI6m5xWnrILbDv4I12aDTG7jt6Qx/5TMbSqb1sPG6YuNACuZaSgN+Ty/viN+HoHsP3wKqvrlNfPJ/dwz8J/kJKZXizJMnWjw9abC9ZukhRYeWI3g+u3BSDPoFcSzFpNC+dHh0awK+kELRe+wp6U03SKbGCynPLuo7VYi8YnhBoiQuC5+8vXk2Cdysq6lYPtH5c9bT+8mnGzmpF2I4XgwEoWz3/OcP1mKh+vfJaXhi4oNv3vD7zDp6tf5KMfnqFOtRaEBIazaN00Xhv9HQv/Ec+ZpIMkp55xaqxeOqXNUfdGZb/QD/RRuqd3xquJd6pkHSnrd609+HrDM72V17TLqmIAjO8FLWpaX9be2teFsd2hQjkeUndrAH/v7hptRc1d+4QFV2Ph/8Uza/xm9p7YQMLFA9Ss3JADp2IB2H9yMxnZpq/phBu1CXQlHatH88X+jQxt1JFNZw/xeJGnF6BcvCZlXCPYxx+90UBKZjrnr1/lwRXvcCothbUJ+2g9tE6p5QAuZaYT6hdY7AnGwoOb0Wg0jG5afDsAHSOj+eqA0jBg/6Wz1A6pzI3cLIJuDSS7/UI8k249sbtwI5XIINv7Dg71q0DijatoNVqCbl0orz9zgB0X4vl24HNlOGK2S72ezFtLiw/gEhYUQbM63biZo7SCz8y5Xqrdxfq4r+nYZCDDerzC9zGz2Lh3Cdm5maWm3df28VLbDPQLsVh2gR1Hf2b64z+aXWfDnsX0ajXSpv0suNB47bHvTMbTom53AO6p36vw9RdL65TXwPptGFi/DVM2LuLXhL8YfKvtm6kbHb5e3vii3JUtuLlgqmMgazdJCvx6am9hm9KlR7YxvHHxJH3x4S0MqNuKl9oPZM7uX1h6ZJvJz0ByZhqP/Ty32LSqgSEsvVVHze2jtViLxieEWioHwfP3w5bjEHMU0q2M917QvqtrtNIG1BnMnbenPrbc6rqdm/6Nzk3/xscrJ7PzyC9mz3/OUNCW6OkHZxEWHFFsXo3KDXj36d9Jz7zC/NUv4KXzJj8/nyD/MLRaLQF+IWTl3HBarAV0WuXV4RY14dd9kHDZ8vJeWmhdGwbeA0FOfHBpzzqSkXXN5u9ae/L1glGdld571+63Plajr5cyFmD/Fuq9bgvKq6z/fBB+2QdxZ0Bv5eF67XAYcI/SAZGzWKoflq59lH4elAy3Y+MHOZNyiO4tH+Wvkxt55bPeRITWJrSC9HxmjiSB5dCqah38vLzpuewNWlaJol21+iRnprHwYAyvdhzMvzs/wuhfPiZLn8trnR8iMiiMHaPfBOA/f6ygS42GhPpVKLUcwCsxS5h574hiydrkDQtpF1GPPstn0K1mY17v8gjv7VrNqCZduadKbfy9fOizfAaV/IN4vu0DbDx7kOl/fI+vzpsukY1oX60+eqOBv//2KWuH/quw3DyDnoE/vMuBy2cZsOIdZnR7lFrB4bf3o8sjjLp1cf1RnzEATNn4NcE+/tz3vzdpEFaNT+7/u12PbVhwBLMnxJSafiJxL7/u/IzuLYfx14kN3N92TLH5+eQTFKAcs+DAcDKz09FqdaWmGQx6rt+8SmjQ7ZNCk6hOFssG5QTlrfMpfKXU1DpbD/7AqYv7+GXHp5xNOczKbXMZ2GlCqe1ZutBQyu7Mml1fAHDq4j6qhdWxuk555OjzCjtaCfLxx9/r9reUqRsdpm4u6I0GrmZlUDUwxOK6JSVnpuGj86KSvzIw3fHUJA5cPsMX+zdy5Eoi8/auQ6fREuavPLkL9w/iek6Wye1FBFZkw/BpZd5HS7GWjE8INem00LMx3NsQDl+AkymQmKoMYq1BeWW0ZiXloq1xded3O27uvG1Nrj6nsLOuAN9gfL39TZ7/nCX2wPfEn9/NF7/+A4Cx/d9m075veXbwXNb+uYCNe5fg4+3P5CHzAHi05//x7vLRaLU6alVpXNiJjBrqVVGe/F68BgcSlfpx6ToYjMrrwNVDIaqS0s1/oPOaLhayZx05em5nqe/awV2d119E00hoUh3OXoUjF+B8KlzNAKNRSfYiQ5VE6p4o9V7FLqmCnzJkwcBWsO+sEvvFa5CVp5wvKgcp4ws2qwE1HDTOoCXm6oe1a5+b2TcIuDXA7eEzfzCo62R0Wh3PDlauXT9Y8TRtGzqv6ZK70eTn55ds7iVM0M/fSH7CJYdvZ/L6r5h731N2L3dvcgIHLp9jTPMedi+7gKZuFbwm9La+YAlxyyEt0fpy81Y9z8kLe6lX/R6eHTyX1OvJrN29gFG9p5KRlcabSx4lT5+Dl86bqY/9D61GW2rajZup/G/zu7w49Aubywb4Zcdn6I15DO7yrNl1ipoyrysfTtrGhSsnS21v01/L+GTVc0RVVV59HNv/bSLC6hTb3mc/v0x8YhwhgeG8OvJbth78odQ6TWp3AqBiDWhb/AaaWUXr8eqTcYVt++qHRjD//r9z6eb1wpsAL276mr9SztCyShQf9h7D2oS/it1ceLv7CE5eS2bWnz/zad9xxbZTct2iN0kAvti/kTyDnokm2pX2WDadmBHTScvOZNQv/yVHr8dbp2Ppg8+Rmp1hcnvmWNtHU7Faiq+8dVyIu40t5229IY9/fdmfExf2UD+yNU/1n0nVilGF57rth1axYuscQHml/4WHP0er1ZY6/3kXuXlTlvNdeWJ2lDuJ2x3ZeqzLW0cKFHzXFuVpx9od2Vo/rF0v7Tq6hq/XTcPby5dmdboxbsC7XEm/wNvfjkKr0dKnzeP0bTemWJlSP26TJNBGzkoC3Zmjk0B72HrgByoEhDqtLYEztlfeJNAefoz/k1C/wGJt+RzJ2dsrSZJAIRRqJVSSBLoHOdbCEqkfrkFeBxUepVuLh+/q7TnbQw2c222Ys7cnhBBCCHE3kiTQRprqKoy062bKe4yCqtg5EA9TluMn9fjOyPETQqHWeftOtqvmd42nfc/JsRaWSP1wDfI6qBBCCCGEEEJ4EBknUAghhBBCCCE8iCSBQgghhBBCCOFBJAkUQgghhBBCCA8iSaAQQgghhBBCeBBJAoUQQgghhBDCg0gSKIQQQgghhBAeRJJAIYQQQgghhPAgkgQKIYQQQgghhAeRJFAIIYQQQgghPIgkgUIIIYQQQgjhQSQJFEIIIYQQQggPIkmgEEIIIYQQQngQSQKFEEIIIYQQwoNIEiiEEEIIIYQQHkSSQCGEEEIIIYTwIJIECiGEEEIIIYQHkSRQCCGEEEIIITyIJIFCCCGEEEII4UH+H/Tv5YufplJTAAAAAElFTkSuQmCC\n" + ] }, + "execution_count": 5, "metadata": {}, - "execution_count": 5 + "output_type": "execute_result" } + ], + "source": [ + "# easy conversion to qiskit\n", + "from torchquantum.plugin.qiskit_plugin import tq2qiskit\n", + "\n", + "circ = tq2qiskit(q_dev, model)\n", + "circ.draw('mpl')" ] }, { "cell_type": "code", - "source": [ - "#" - ], + "execution_count": null, "metadata": { "id": "qXO5aA1p27_L", "pycharm": { "name": "#%%\n" } }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "#" + ] }, { "cell_type": "code", - "source": [ - "! pip install pennylane" - ], + "execution_count": 3, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -790,17 +771,16 @@ "name": "#%%\n" } }, - "execution_count": 3, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Looking in indexes: https://pypi.org/simple, https://us-python.pkg.dev/colab-wheels/public/simple/\n", "Collecting pennylane\n", " Downloading PennyLane-0.25.1-py3-none-any.whl (1.0 MB)\n", - "\u001B[K |████████████████████████████████| 1.0 MB 35.4 MB/s \n", - "\u001B[?25hRequirement already satisfied: appdirs in /usr/local/lib/python3.7/dist-packages (from pennylane) (1.4.4)\n", + "\u001b[K |████████████████████████████████| 1.0 MB 35.4 MB/s \n", + "\u001b[?25hRequirement already satisfied: appdirs in /usr/local/lib/python3.7/dist-packages (from pennylane) (1.4.4)\n", "Requirement already satisfied: autograd in /usr/local/lib/python3.7/dist-packages (from pennylane) (1.4)\n", "Requirement already satisfied: scipy in /usr/local/lib/python3.7/dist-packages (from pennylane) (1.7.3)\n", "Requirement already satisfied: cachetools in /usr/local/lib/python3.7/dist-packages (from pennylane) (4.2.4)\n", @@ -808,8 +788,8 @@ "Requirement already satisfied: networkx in /usr/local/lib/python3.7/dist-packages (from pennylane) (2.6.3)\n", "Collecting pennylane-lightning>=0.25\n", " Downloading PennyLane_Lightning-0.25.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (13.6 MB)\n", - "\u001B[K |████████████████████████████████| 13.6 MB 29.3 MB/s \n", - "\u001B[?25hRequirement already satisfied: numpy in /usr/local/lib/python3.7/dist-packages (from pennylane) (1.21.6)\n", + "\u001b[K |████████████████████████████████| 13.6 MB 29.3 MB/s \n", + "\u001b[?25hRequirement already satisfied: numpy in /usr/local/lib/python3.7/dist-packages (from pennylane) (1.21.6)\n", "Collecting semantic-version>=2.7\n", " Downloading semantic_version-2.10.0-py2.py3-none-any.whl (15 kB)\n", "Collecting autoray>=0.3.1\n", @@ -817,16 +797,27 @@ "Requirement already satisfied: retworkx in /usr/local/lib/python3.7/dist-packages (from pennylane) (0.11.0)\n", "Collecting ninja\n", " Downloading ninja-1.10.2.3-py2.py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.whl (108 kB)\n", - "\u001B[K |████████████████████████████████| 108 kB 68.7 MB/s \n", - "\u001B[?25hRequirement already satisfied: future>=0.15.2 in /usr/local/lib/python3.7/dist-packages (from autograd->pennylane) (0.16.0)\n", + "\u001b[K |████████████████████████████████| 108 kB 68.7 MB/s \n", + "\u001b[?25hRequirement already satisfied: future>=0.15.2 in /usr/local/lib/python3.7/dist-packages (from autograd->pennylane) (0.16.0)\n", "Installing collected packages: ninja, semantic-version, pennylane-lightning, autoray, pennylane\n", "Successfully installed autoray-0.3.2 ninja-1.10.2.3 pennylane-0.25.1 pennylane-lightning-0.25.1 semantic-version-2.10.0\n" ] } + ], + "source": [ + "! pip install pennylane" ] }, { "cell_type": "code", + "execution_count": 12, + "metadata": { + "id": "iAsj8ImRQ2e4", + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], "source": [ "# Speed comparison with pennylane\n", "\n", @@ -834,34 +825,46 @@ "from pennylane import numpy as np\n", "import random\n", "import time \n" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 18, "metadata": { - "id": "iAsj8ImRQ2e4", + "id": "DCr7hQ_MROPU", "pycharm": { "name": "#%%\n" } }, - "execution_count": 12, - "outputs": [] - }, - { - "cell_type": "code", + "outputs": [], "source": [ "n_wires = 10\n", "bsz = 32\n", "use_gpu=False" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 19, "metadata": { - "id": "DCr7hQ_MROPU", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "C0Vf_Kte29Xt", + "outputId": "d989a826-c7cc-4860-dc8f-19a730135be7", "pycharm": { "name": "#%%\n" } }, - "execution_count": 18, - "outputs": [] - }, - { - "cell_type": "code", + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Pennylane inference time: 0.3734148144721985\n" + ] + } + ], "source": [ "dev=qml.device(\"default.qubit\",wires=n_wires)\n", "\n", @@ -893,30 +896,30 @@ "end = time.time()\n", "pennylane_time = (end-start)/reps\n", "print(f\"Pennylane inference time: {pennylane_time}\")\n" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 20, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, - "id": "C0Vf_Kte29Xt", - "outputId": "d989a826-c7cc-4860-dc8f-19a730135be7", + "id": "-bH438r0Q5gV", + "outputId": "00b1edc2-9dd9-4c65-e16e-e12ade91f6a6", "pycharm": { "name": "#%%\n" } }, - "execution_count": 19, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ - "Pennylane inference time: 0.3734148144721985\n" - ] - } - ] - }, - { - "cell_type": "code", + "TorchQuantum inference time 0.004048892259597778; is 92.22641417218001 X faster\n" + ] + } + ], "source": [ "reps = 1000\n", "'''\n", @@ -955,36 +958,11 @@ "tq_time = (end-start)/reps\n", "\n", "print(f\"TorchQuantum inference time {tq_time}; is {pennylane_time/tq_time} X faster\")" - ], - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "-bH438r0Q5gV", - "outputId": "00b1edc2-9dd9-4c65-e16e-e12ade91f6a6", - "pycharm": { - "name": "#%%\n" - } - }, - "execution_count": 20, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "TorchQuantum inference time 0.004048892259597778; is 92.22641417218001 X faster\n" - ] - } ] }, { "cell_type": "code", - "source": [ - "# basic pulse\n", - "pulse = tq.QuantumPulseDirect(n_steps=4,\n", - " hamil=[[0, 1], [1, 0]])\n", - "pulse.get_unitary()\n" - ], + "execution_count": 26, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -995,30 +973,29 @@ "name": "#%%\n" } }, - "execution_count": 26, "outputs": [ { - "output_type": "execute_result", "data": { "text/plain": [ "tensor([[-0.6536+0.0000j, 0.0000+0.7568j],\n", " [ 0.0000+0.7568j, -0.6536+0.0000j]], grad_fn=)" ] }, + "execution_count": 26, "metadata": {}, - "execution_count": 26 + "output_type": "execute_result" } + ], + "source": [ + "# basic pulse\n", + "pulse = tq.QuantumPulseDirect(n_steps=4,\n", + " hamil=[[0, 1], [1, 0]])\n", + "pulse.get_unitary()\n" ] }, { "cell_type": "code", - "source": [ - "theta = 0.6 * np.pi\n", - "target_unitary = torch.tensor([[np.cos(theta/2), -1j*np.sin(theta/2)], [-1j*np.sin(theta/2), np.cos(theta/2)]], dtype=torch.complex64)\n", - "loss = 1 - (torch.trace(pulse.get_unitary() @ target_unitary) / target_unitary.shape[0]).abs() ** 2\n", - "loss.backward()\n", - "print(pulse.pulse_shape.grad)\n" - ], + "execution_count": 28, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -1029,31 +1006,45 @@ "name": "#%%\n" } }, - "execution_count": 28, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "tensor([-0.4441, -0.4441, -0.4441, -0.4441])\n" ] } + ], + "source": [ + "theta = 0.6 * np.pi\n", + "target_unitary = torch.tensor([[np.cos(theta/2), -1j*np.sin(theta/2)], [-1j*np.sin(theta/2), np.cos(theta/2)]], dtype=torch.complex64)\n", + "loss = 1 - (torch.trace(pulse.get_unitary() @ target_unitary) / target_unitary.shape[0]).abs() ** 2\n", + "loss.backward()\n", + "print(pulse.pulse_shape.grad)\n" ] }, { "cell_type": "markdown", - "source": [ - "## 1.3 TorchQuantum for state preparation circuit" - ], "metadata": { "id": "ElNAsYJLj8J9", "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "## 1.3 TorchQuantum for state preparation circuit" + ] }, { "cell_type": "code", + "execution_count": 7, + "metadata": { + "id": "8ngaSqT-iItk", + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], "source": [ "import torch\n", "import torch.optim as optim\n", @@ -1064,18 +1055,18 @@ "\n", "import random\n", "import numpy as np" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 8, "metadata": { - "id": "8ngaSqT-iItk", + "id": "kJ64ckPTiZtM", "pycharm": { "name": "#%%\n" } }, - "execution_count": 7, - "outputs": [] - }, - { - "cell_type": "code", + "outputs": [], "source": [ "\n", "class QModel(tq.QuantumModule):\n", @@ -1111,18 +1102,18 @@ " print(f\"infidelity (loss): {loss.item()}, \\n target state : \"\n", " f\"{target_state.detach().cpu().numpy()}, \\n \"\n", " f\"result state : {result_state.detach().cpu().numpy()}\\n\")" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 35, "metadata": { - "id": "kJ64ckPTiZtM", + "id": "85BzTkY0io0o", "pycharm": { "name": "#%%\n" } }, - "execution_count": 8, - "outputs": [] - }, - { - "cell_type": "code", + "outputs": [], "source": [ "def main(n_epochs=3000):\n", " seed = 42\n", @@ -1145,47 +1136,37 @@ " print(f\"Epoch {epoch}, LR: {optimizer.param_groups[0]['lr']}\")\n", " train(target_state, q_device, model, optimizer)\n", " scheduler.step()" - ], - "metadata": { - "id": "85BzTkY0io0o", - "pycharm": { - "name": "#%%\n" - } - }, - "execution_count": 35, - "outputs": [] + ] }, { "cell_type": "code", - "source": [ - "main(n_epochs=3000)" - ], + "execution_count": null, "metadata": { "id": "NyMvW0pai_lO", "pycharm": { "name": "#%%\n" } }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "main(n_epochs=3000)" + ] }, { "cell_type": "markdown", - "source": [ - "## 1.4 TorchQuantum for VQE circuit " - ], "metadata": { "id": "6QeYK4OjA9qB", "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "## 1.4 TorchQuantum for VQE circuit " + ] }, { "cell_type": "code", - "source": [ - "! wget https://www.dropbox.com/s/1rtttfxoo02s09e/h2_new.txt" - ], + "execution_count": 1, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -1196,38 +1177,43 @@ "name": "#%%\n" } }, - "execution_count": 10, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ - "--2022-09-19 15:25:09-- https://www.dropbox.com/s/1rtttfxoo02s09e/h2_new.txt\n", - "Resolving www.dropbox.com (www.dropbox.com)... 162.125.65.18, 2620:100:6017:18::a27d:212\n", - "Connecting to www.dropbox.com (www.dropbox.com)|162.125.65.18|:443... connected.\n", + "--2025-08-19 09:30:48-- https://www.dropbox.com/s/1rtttfxoo02s09e/h2_new.txt\n", + "Resolving www.dropbox.com (www.dropbox.com)... 162.125.4.18, 2620:100:6019:18::a27d:412\n", + "Connecting to www.dropbox.com (www.dropbox.com)|162.125.4.18|:443... connected.\n", "HTTP request sent, awaiting response... 302 Found\n", - "Location: /s/raw/1rtttfxoo02s09e/h2_new.txt [following]\n", - "--2022-09-19 15:25:10-- https://www.dropbox.com/s/raw/1rtttfxoo02s09e/h2_new.txt\n", + "Location: https://www.dropbox.com/scl/fi/5hfv3opi7nb0toxzhohaz/h2_new.txt?rlkey=2r8t0enh6s8zsev5uj15qsy32 [following]\n", + "--2025-08-19 09:30:48-- https://www.dropbox.com/scl/fi/5hfv3opi7nb0toxzhohaz/h2_new.txt?rlkey=2r8t0enh6s8zsev5uj15qsy32\n", "Reusing existing connection to www.dropbox.com:443.\n", - "HTTP request sent, awaiting response... 302 Found\n", - "Location: https://ucfcd04121af2228bb42634017f1.dl.dropboxusercontent.com/cd/0/inline/BtNQ0j4Qw_P3NDqdfHMScfqMtF5UMizmFhmybBzezDMfQxVT-6XxJ8L4v68idx990zBZGgjFv_daTOhOCPhY7HqN47VGL7WU3mzIkkumskCkzELS-C8msPgRwrGbBLvst8KeznexC4Dk4dfyqQyM9YOjytB_H_HBaSmwsn9xn-VSGg/file# [following]\n", - "--2022-09-19 15:25:10-- https://ucfcd04121af2228bb42634017f1.dl.dropboxusercontent.com/cd/0/inline/BtNQ0j4Qw_P3NDqdfHMScfqMtF5UMizmFhmybBzezDMfQxVT-6XxJ8L4v68idx990zBZGgjFv_daTOhOCPhY7HqN47VGL7WU3mzIkkumskCkzELS-C8msPgRwrGbBLvst8KeznexC4Dk4dfyqQyM9YOjytB_H_HBaSmwsn9xn-VSGg/file\n", - "Resolving ucfcd04121af2228bb42634017f1.dl.dropboxusercontent.com (ucfcd04121af2228bb42634017f1.dl.dropboxusercontent.com)... 162.125.3.15, 2620:100:6017:15::a27d:20f\n", - "Connecting to ucfcd04121af2228bb42634017f1.dl.dropboxusercontent.com (ucfcd04121af2228bb42634017f1.dl.dropboxusercontent.com)|162.125.3.15|:443... connected.\n", "HTTP request sent, awaiting response... 200 OK\n", - "Length: 139 [text/plain]\n", + "Length: unspecified [text/html]\n", "Saving to: ‘h2_new.txt’\n", "\n", - "h2_new.txt 100%[===================>] 139 --.-KB/s in 0s \n", + "h2_new.txt [ <=> ] 171.70K --.-KB/s in 0.1s \n", "\n", - "2022-09-19 15:25:11 (26.7 MB/s) - ‘h2_new.txt’ saved [139/139]\n", + "2025-08-19 09:30:49 (1.57 MB/s) - ‘h2_new.txt’ saved [175819]\n", "\n" ] } + ], + "source": [ + "! wget https://www.dropbox.com/s/1rtttfxoo02s09e/h2_new.txt" ] }, { "cell_type": "code", + "execution_count": 4, + "metadata": { + "id": "-plW3t-BBDKG", + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], "source": [ "import torchquantum as tq\n", "import torch\n", @@ -1241,18 +1227,18 @@ "\n", "from torch.optim.lr_scheduler import CosineAnnealingLR, ConstantLR\n", "\n" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 11, "metadata": { - "id": "-plW3t-BBDKG", + "id": "Psb0lOq3BSbQ", "pycharm": { "name": "#%%\n" } }, - "execution_count": 4, - "outputs": [] - }, - { - "cell_type": "code", + "outputs": [], "source": [ "class QVQEModel(tq.QuantumModule):\n", " def __init__(self, arch, hamil_info):\n", @@ -1324,18 +1310,18 @@ " loss = outputs.mean()\n", "\n", " print(f\"Expectation of energy: {loss}\")\n" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 14, "metadata": { - "id": "Psb0lOq3BSbQ", + "id": "UTTikHR1BZnV", "pycharm": { "name": "#%%\n" } }, - "execution_count": 11, - "outputs": [] - }, - { - "cell_type": "code", + "outputs": [], "source": [ "class Args(object):\n", " def __init__(self):\n", @@ -1413,21 +1399,11 @@ "\n", " # final valid\n", " valid_test(dataflow, q_device, 'valid', model, device)" - ], - "metadata": { - "id": "UTTikHR1BZnV", - "pycharm": { - "name": "#%%\n" - } - }, - "execution_count": 14, - "outputs": [] + ] }, { "cell_type": "code", - "source": [ - "main()" - ], + "execution_count": 15, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -1439,11 +1415,10 @@ "name": "#%%\n" } }, - "execution_count": 15, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Epoch 1, LR: 0.005\n", "Expectation of energy: -0.308297323072801\n", @@ -1724,36 +1699,47 @@ ] }, { - "output_type": "error", "ename": "KeyboardInterrupt", "evalue": "ignored", + "output_type": "error", "traceback": [ - "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[0;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "\u001B[0;32m\u001B[0m in \u001B[0;36m\u001B[0;34m\u001B[0m\n\u001B[0;32m----> 1\u001B[0;31m \u001B[0mmain\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m", - "\u001B[0;32m\u001B[0m in \u001B[0;36mmain\u001B[0;34m()\u001B[0m\n\u001B[1;32m 67\u001B[0m \u001B[0;31m# train\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 68\u001B[0m \u001B[0mprint\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34mf\"Epoch {epoch}, LR: {optimizer.param_groups[0]['lr']}\"\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 69\u001B[0;31m \u001B[0mtrain\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mdataflow\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mq_device\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mmodel\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mdevice\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0moptimizer\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 70\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 71\u001B[0m \u001B[0;31m# valid\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m\u001B[0m in \u001B[0;36mtrain\u001B[0;34m(dataflow, q_device, model, device, optimizer)\u001B[0m\n\u001B[1;32m 57\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 58\u001B[0m \u001B[0moptimizer\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mzero_grad\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 59\u001B[0;31m \u001B[0mloss\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mbackward\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 60\u001B[0m \u001B[0moptimizer\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mstep\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 61\u001B[0m \u001B[0mprint\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34mf\"Expectation of energy: {loss.item()}\"\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m/usr/local/lib/python3.7/dist-packages/torch/_tensor.py\u001B[0m in \u001B[0;36mbackward\u001B[0;34m(self, gradient, retain_graph, create_graph, inputs)\u001B[0m\n\u001B[1;32m 394\u001B[0m \u001B[0mcreate_graph\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mcreate_graph\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 395\u001B[0m inputs=inputs)\n\u001B[0;32m--> 396\u001B[0;31m \u001B[0mtorch\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mautograd\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mbackward\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mself\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mgradient\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mretain_graph\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mcreate_graph\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0minputs\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0minputs\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 397\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 398\u001B[0m \u001B[0;32mdef\u001B[0m \u001B[0mregister_hook\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mself\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mhook\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m/usr/local/lib/python3.7/dist-packages/torch/autograd/__init__.py\u001B[0m in \u001B[0;36mbackward\u001B[0;34m(tensors, grad_tensors, retain_graph, create_graph, grad_variables, inputs)\u001B[0m\n\u001B[1;32m 173\u001B[0m Variable._execution_engine.run_backward( # Calls into the C++ engine to run the backward pass\n\u001B[1;32m 174\u001B[0m \u001B[0mtensors\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mgrad_tensors_\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mretain_graph\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mcreate_graph\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0minputs\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 175\u001B[0;31m allow_unreachable=True, accumulate_grad=True) # Calls into the C++ engine to run the backward pass\n\u001B[0m\u001B[1;32m 176\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 177\u001B[0m def grad(\n", - "\u001B[0;31mKeyboardInterrupt\u001B[0m: " + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mmain\u001b[0;34m()\u001b[0m\n\u001b[1;32m 67\u001b[0m \u001b[0;31m# train\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 68\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"Epoch {epoch}, LR: {optimizer.param_groups[0]['lr']}\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 69\u001b[0;31m \u001b[0mtrain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdataflow\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mq_device\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdevice\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moptimizer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 70\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 71\u001b[0m \u001b[0;31m# valid\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mtrain\u001b[0;34m(dataflow, q_device, model, device, optimizer)\u001b[0m\n\u001b[1;32m 57\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 58\u001b[0m \u001b[0moptimizer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mzero_grad\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 59\u001b[0;31m \u001b[0mloss\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 60\u001b[0m \u001b[0moptimizer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstep\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 61\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"Expectation of energy: {loss.item()}\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/torch/_tensor.py\u001b[0m in \u001b[0;36mbackward\u001b[0;34m(self, gradient, retain_graph, create_graph, inputs)\u001b[0m\n\u001b[1;32m 394\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mcreate_graph\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 395\u001b[0m inputs=inputs)\n\u001b[0;32m--> 396\u001b[0;31m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mautograd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mbackward\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgradient\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mretain_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minputs\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0minputs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 397\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 398\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mregister_hook\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mhook\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/torch/autograd/__init__.py\u001b[0m in \u001b[0;36mbackward\u001b[0;34m(tensors, grad_tensors, retain_graph, create_graph, grad_variables, inputs)\u001b[0m\n\u001b[1;32m 173\u001b[0m Variable._execution_engine.run_backward( # Calls into the C++ engine to run the backward pass\n\u001b[1;32m 174\u001b[0m \u001b[0mtensors\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mgrad_tensors_\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mretain_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mcreate_graph\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0minputs\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 175\u001b[0;31m allow_unreachable=True, accumulate_grad=True) # Calls into the C++ engine to run the backward pass\n\u001b[0m\u001b[1;32m 176\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 177\u001b[0m def grad(\n", + "\u001b[0;31mKeyboardInterrupt\u001b[0m: " ] } + ], + "source": [ + "main()" ] }, { "cell_type": "markdown", - "source": [ - "## 1.5 TorchQuantum for QNN circuit" - ], "metadata": { "id": "4k_7FrcQBCtl", "pycharm": { "name": "#%% md\n" } - } + }, + "source": [ + "## 1.5 TorchQuantum for QNN circuit" + ] }, { "cell_type": "code", + "execution_count": 47, + "metadata": { + "id": "n1U42zhEA6w3", + "pycharm": { + "name": "#%%\n" + } + }, + "outputs": [], "source": [ "import torch\n", "import torch.nn.functional as F\n", @@ -1773,18 +1759,18 @@ "\n", "import random\n", "import numpy as np" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 49, "metadata": { - "id": "n1U42zhEA6w3", + "id": "srvo_I_sDWv5", "pycharm": { "name": "#%%\n" } }, - "execution_count": 47, - "outputs": [] - }, - { - "cell_type": "code", + "outputs": [], "source": [ "class QFCModel(tq.QuantumModule):\n", " class QLayer(tq.QuantumModule):\n", @@ -1910,18 +1896,18 @@ "\n", " print(f\"{split} set accuracy: {accuracy}\")\n", " print(f\"{split} set loss: {loss}\")\n" - ], + ] + }, + { + "cell_type": "code", + "execution_count": 52, "metadata": { - "id": "srvo_I_sDWv5", + "id": "oBmCC02LDl25", "pycharm": { "name": "#%%\n" } }, - "execution_count": 49, - "outputs": [] - }, - { - "cell_type": "code", + "outputs": [], "source": [ "\n", "def main():\n", @@ -2013,21 +1999,11 @@ " \"save the account token according to the instruction at \"\n", " \"'https://github.com/Qiskit/qiskit-ibmq-provider', \"\n", " \"then try again.\")" - ], - "metadata": { - "id": "oBmCC02LDl25", - "pycharm": { - "name": "#%%\n" - } - }, - "execution_count": 52, - "outputs": [] + ] }, { "cell_type": "code", - "source": [ - "main()" - ], + "execution_count": 53, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -2039,18 +2015,17 @@ "name": "#%%\n" } }, - "execution_count": 53, "outputs": [ { - "output_type": "stream", "name": "stderr", + "output_type": "stream", "text": [ "[2022-09-18 05:29:24.683] Only use the front 75 images as TEST set.\n" ] }, { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Epoch 1:\n", "0.005\n", @@ -2060,39 +2035,69 @@ ] }, { - "output_type": "error", "ename": "KeyboardInterrupt", "evalue": "ignored", + "output_type": "error", "traceback": [ - "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", - "\u001B[0;31mKeyboardInterrupt\u001B[0m Traceback (most recent call last)", - "\u001B[0;32m\u001B[0m in \u001B[0;36m\u001B[0;34m\u001B[0m\n\u001B[0;32m----> 1\u001B[0;31m \u001B[0mmain\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m", - "\u001B[0;32m\u001B[0m in \u001B[0;36mmain\u001B[0;34m()\u001B[0m\n\u001B[1;32m 49\u001B[0m \u001B[0;31m# train\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 50\u001B[0m \u001B[0mprint\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34mf\"Epoch {epoch}:\"\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 51\u001B[0;31m \u001B[0mtrain\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mdataflow\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mmodel\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mdevice\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0moptimizer\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 52\u001B[0m \u001B[0mprint\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0moptimizer\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mparam_groups\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;36m0\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;34m'lr'\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 53\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m\u001B[0m in \u001B[0;36mtrain\u001B[0;34m(dataflow, model, device, optimizer)\u001B[0m\n\u001B[1;32m 91\u001B[0m \u001B[0mtargets\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mfeed_dict\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;34m'digit'\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mto\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mdevice\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 92\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 93\u001B[0;31m \u001B[0moutputs\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mmodel\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0minputs\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 94\u001B[0m \u001B[0mloss\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mF\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mnll_loss\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0moutputs\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mtargets\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 95\u001B[0m \u001B[0moptimizer\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mzero_grad\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m/usr/local/lib/python3.7/dist-packages/torch/nn/modules/module.py\u001B[0m in \u001B[0;36m_call_impl\u001B[0;34m(self, *input, **kwargs)\u001B[0m\n\u001B[1;32m 1128\u001B[0m if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks\n\u001B[1;32m 1129\u001B[0m or _global_forward_hooks or _global_forward_pre_hooks):\n\u001B[0;32m-> 1130\u001B[0;31m \u001B[0;32mreturn\u001B[0m \u001B[0mforward_call\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m*\u001B[0m\u001B[0minput\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 1131\u001B[0m \u001B[0;31m# Do not call functions when jit is used\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 1132\u001B[0m \u001B[0mfull_backward_hooks\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mnon_full_backward_hooks\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0;34m[\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m[\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m\u001B[0m in \u001B[0;36mforward\u001B[0;34m(self, x, use_qiskit)\u001B[0m\n\u001B[1;32m 76\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 77\u001B[0m \u001B[0;32melse\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 78\u001B[0;31m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mencoder\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mq_device\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mx\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 79\u001B[0m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mq_layer\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mq_device\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 80\u001B[0m \u001B[0mx\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mmeasure\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mq_device\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m/usr/local/lib/python3.7/dist-packages/torch/nn/modules/module.py\u001B[0m in \u001B[0;36m_call_impl\u001B[0;34m(self, *input, **kwargs)\u001B[0m\n\u001B[1;32m 1128\u001B[0m if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks\n\u001B[1;32m 1129\u001B[0m or _global_forward_hooks or _global_forward_pre_hooks):\n\u001B[0;32m-> 1130\u001B[0;31m \u001B[0;32mreturn\u001B[0m \u001B[0mforward_call\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m*\u001B[0m\u001B[0minput\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 1131\u001B[0m \u001B[0;31m# Do not call functions when jit is used\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 1132\u001B[0m \u001B[0mfull_backward_hooks\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mnon_full_backward_hooks\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0;34m[\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m[\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m/content/torchquantum/torchquantum/graph.py\u001B[0m in \u001B[0;36mforward_register_graph\u001B[0;34m(*args, **kwargs)\u001B[0m\n\u001B[1;32m 23\u001B[0m \u001B[0;32mif\u001B[0m \u001B[0margs\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;36m0\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mstatic_mode\u001B[0m \u001B[0;32mand\u001B[0m \u001B[0margs\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;36m0\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mparent_graph\u001B[0m \u001B[0;32mis\u001B[0m \u001B[0;32mnot\u001B[0m \u001B[0;32mNone\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 24\u001B[0m \u001B[0margs\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;36m0\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mparent_graph\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0madd_op\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0margs\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;36m0\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 25\u001B[0;31m \u001B[0mres\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mf\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m*\u001B[0m\u001B[0margs\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m**\u001B[0m\u001B[0mkwargs\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 26\u001B[0m \u001B[0;32mif\u001B[0m \u001B[0margs\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;36m0\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mstatic_mode\u001B[0m \u001B[0;32mand\u001B[0m \u001B[0margs\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0;36m0\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mis_graph_top\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 27\u001B[0m \u001B[0;31m# finish build graph, set flag\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m/content/torchquantum/torchquantum/encoding.py\u001B[0m in \u001B[0;36mforward\u001B[0;34m(self, q_device, x)\u001B[0m\n\u001B[1;32m 69\u001B[0m \u001B[0mparams\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mparams\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 70\u001B[0m \u001B[0mstatic\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mstatic_mode\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m---> 71\u001B[0;31m \u001B[0mparent_graph\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mgraph\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 72\u001B[0m )\n\u001B[1;32m 73\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m/content/torchquantum/torchquantum/functional.py\u001B[0m in \u001B[0;36mry\u001B[0;34m(q_device, wires, params, n_wires, static, parent_graph, inverse, comp_method)\u001B[0m\n\u001B[1;32m 1685\u001B[0m \u001B[0mstatic\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mstatic\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 1686\u001B[0m \u001B[0mparent_graph\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0mparent_graph\u001B[0m\u001B[0;34m,\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m-> 1687\u001B[0;31m \u001B[0minverse\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0minverse\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 1688\u001B[0m )\n\u001B[1;32m 1689\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m/content/torchquantum/torchquantum/functional.py\u001B[0m in \u001B[0;36mgate_wrapper\u001B[0;34m(name, mat, method, q_device, wires, params, n_wires, static, parent_graph, inverse)\u001B[0m\n\u001B[1;32m 260\u001B[0m name in ['qubitunitary', 'qubitunitaryfast',\n\u001B[1;32m 261\u001B[0m 'qubitunitarystrict']:\n\u001B[0;32m--> 262\u001B[0;31m \u001B[0mmatrix\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mmat\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mparams\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 263\u001B[0m \u001B[0;32melif\u001B[0m \u001B[0mname\u001B[0m \u001B[0;32min\u001B[0m \u001B[0;34m[\u001B[0m\u001B[0;34m'multicnot'\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0;34m'multixcnot'\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 264\u001B[0m \u001B[0;31m# this is for gates that can be applied to arbitrary numbers of\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;32m/content/torchquantum/torchquantum/functional.py\u001B[0m in \u001B[0;36mry_matrix\u001B[0;34m(params)\u001B[0m\n\u001B[1;32m 354\u001B[0m \u001B[0mtheta\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mparams\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mtype\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mC_DTYPE\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 355\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 356\u001B[0;31m \u001B[0mco\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mtorch\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mcos\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mtheta\u001B[0m \u001B[0;34m/\u001B[0m \u001B[0;36m2\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 357\u001B[0m \u001B[0msi\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mtorch\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0msin\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mtheta\u001B[0m \u001B[0;34m/\u001B[0m \u001B[0;36m2\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 358\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n", - "\u001B[0;31mKeyboardInterrupt\u001B[0m: " + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mmain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", + "\u001b[0;32m\u001b[0m in \u001b[0;36mmain\u001b[0;34m()\u001b[0m\n\u001b[1;32m 49\u001b[0m \u001b[0;31m# train\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 50\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34mf\"Epoch {epoch}:\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 51\u001b[0;31m \u001b[0mtrain\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdataflow\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdevice\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0moptimizer\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 52\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moptimizer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparam_groups\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'lr'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 53\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mtrain\u001b[0;34m(dataflow, model, device, optimizer)\u001b[0m\n\u001b[1;32m 91\u001b[0m \u001b[0mtargets\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfeed_dict\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;34m'digit'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mdevice\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 92\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 93\u001b[0;31m \u001b[0moutputs\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmodel\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0minputs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 94\u001b[0m \u001b[0mloss\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mF\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnll_loss\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0moutputs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtargets\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 95\u001b[0m \u001b[0moptimizer\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mzero_grad\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/torch/nn/modules/module.py\u001b[0m in \u001b[0;36m_call_impl\u001b[0;34m(self, *input, **kwargs)\u001b[0m\n\u001b[1;32m 1128\u001b[0m if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks\n\u001b[1;32m 1129\u001b[0m or _global_forward_hooks or _global_forward_pre_hooks):\n\u001b[0;32m-> 1130\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mforward_call\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0minput\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1131\u001b[0m \u001b[0;31m# Do not call functions when jit is used\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1132\u001b[0m \u001b[0mfull_backward_hooks\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnon_full_backward_hooks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m\u001b[0m in \u001b[0;36mforward\u001b[0;34m(self, x, use_qiskit)\u001b[0m\n\u001b[1;32m 76\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 77\u001b[0m \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 78\u001b[0;31m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencoder\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mq_device\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 79\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mq_layer\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mq_device\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 80\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmeasure\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mq_device\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/usr/local/lib/python3.7/dist-packages/torch/nn/modules/module.py\u001b[0m in \u001b[0;36m_call_impl\u001b[0;34m(self, *input, **kwargs)\u001b[0m\n\u001b[1;32m 1128\u001b[0m if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks\n\u001b[1;32m 1129\u001b[0m or _global_forward_hooks or _global_forward_pre_hooks):\n\u001b[0;32m-> 1130\u001b[0;31m \u001b[0;32mreturn\u001b[0m \u001b[0mforward_call\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0minput\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1131\u001b[0m \u001b[0;31m# Do not call functions when jit is used\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1132\u001b[0m \u001b[0mfull_backward_hooks\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnon_full_backward_hooks\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/content/torchquantum/torchquantum/graph.py\u001b[0m in \u001b[0;36mforward_register_graph\u001b[0;34m(*args, **kwargs)\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstatic_mode\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparent_graph\u001b[0m \u001b[0;32mis\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 24\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mparent_graph\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_op\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 25\u001b[0;31m \u001b[0mres\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mf\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m*\u001b[0m\u001b[0margs\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwargs\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 26\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstatic_mode\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0margs\u001b[0m\u001b[0;34m[\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mis_graph_top\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 27\u001b[0m \u001b[0;31m# finish build graph, set flag\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/content/torchquantum/torchquantum/encoding.py\u001b[0m in \u001b[0;36mforward\u001b[0;34m(self, q_device, x)\u001b[0m\n\u001b[1;32m 69\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mparams\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 70\u001b[0m \u001b[0mstatic\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mstatic_mode\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 71\u001b[0;31m \u001b[0mparent_graph\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mgraph\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 72\u001b[0m )\n\u001b[1;32m 73\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/content/torchquantum/torchquantum/functional.py\u001b[0m in \u001b[0;36mry\u001b[0;34m(q_device, wires, params, n_wires, static, parent_graph, inverse, comp_method)\u001b[0m\n\u001b[1;32m 1685\u001b[0m \u001b[0mstatic\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mstatic\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 1686\u001b[0m \u001b[0mparent_graph\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mparent_graph\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1687\u001b[0;31m \u001b[0minverse\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0minverse\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 1688\u001b[0m )\n\u001b[1;32m 1689\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/content/torchquantum/torchquantum/functional.py\u001b[0m in \u001b[0;36mgate_wrapper\u001b[0;34m(name, mat, method, q_device, wires, params, n_wires, static, parent_graph, inverse)\u001b[0m\n\u001b[1;32m 260\u001b[0m name in ['qubitunitary', 'qubitunitaryfast',\n\u001b[1;32m 261\u001b[0m 'qubitunitarystrict']:\n\u001b[0;32m--> 262\u001b[0;31m \u001b[0mmatrix\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mmat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mparams\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 263\u001b[0m \u001b[0;32melif\u001b[0m \u001b[0mname\u001b[0m \u001b[0;32min\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m'multicnot'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'multixcnot'\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 264\u001b[0m \u001b[0;31m# this is for gates that can be applied to arbitrary numbers of\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m/content/torchquantum/torchquantum/functional.py\u001b[0m in \u001b[0;36mry_matrix\u001b[0;34m(params)\u001b[0m\n\u001b[1;32m 354\u001b[0m \u001b[0mtheta\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mparams\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mtype\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mC_DTYPE\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 355\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 356\u001b[0;31m \u001b[0mco\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcos\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtheta\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 357\u001b[0m \u001b[0msi\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msin\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtheta\u001b[0m \u001b[0;34m/\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 358\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mKeyboardInterrupt\u001b[0m: " ] } + ], + "source": [ + "main()" ] }, { "cell_type": "code", - "source": [], + "execution_count": null, "metadata": { "id": "Oi0O1RF2Eksg", "pycharm": { "name": "#%%\n" } }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "collapsed_sections": [], + "provenance": [], + "toc_visible": true + }, + "kernelspec": { + "display_name": "tqcuquantum", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" } - ] + }, + "nbformat": 4, + "nbformat_minor": 0 } diff --git a/examples/cuquantum/h2_new.txt b/examples/cuquantum/h2_new.txt new file mode 100644 index 00000000..964330e1 --- /dev/null +++ b/examples/cuquantum/h2_new.txt @@ -0,0 +1,6 @@ +h2 bk 2 +0.1790005760614067 X0 X1 +-1.0439125217729273 I0 I1 +-0.42045567978280385 Z0 I1 +0.42045567978280385 I0 Z1 +-0.011507402176827025 Z0 Z1 diff --git a/examples/cuquantum/qaoa.py b/examples/cuquantum/qaoa.py new file mode 100644 index 00000000..22ba88d3 --- /dev/null +++ b/examples/cuquantum/qaoa.py @@ -0,0 +1,122 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: MIT + +import math +import argparse + +import torch +from torch import nn +from torchquantum.plugin.cuquantum import * +from torchquantum.operator.standard_gates import * + + + +class MAXCUT(nn.Module): + def __init__(self, n_wires, input_graph, n_layers): + super().__init__() + self.n_wires = n_wires + self.input_graph = input_graph + self.n_layers = n_layers + + self.circuit = ParameterizedQuantumCircuit(n_wires=n_wires, n_input_params=0, n_trainable_params=2 * n_layers) + self.circuit.set_trainable_params(torch.randn(2 * n_layers)) + + for wire in range(self.n_wires): + self.circuit.append_gate(Hadamard, wires=wire) + + for l in range(self.n_layers): + # mixer layer + for i in range(self.n_wires): + self.circuit.append_gate(RX, wires=i, trainable_idx=l) + + # entangler layer + for edge in self.input_graph: + self.circuit.append_gate(CNOT, wires=[edge[0], edge[1]]) + self.circuit.append_gate(RZ, wires=edge[1], trainable_idx=n_layers + l) + self.circuit.append_gate(CNOT, wires=[edge[0], edge[1]]) + + + hamiltonian = {} + for edge in self.input_graph: + pauli_string = "" + for wire in range(self.n_wires): + if wire in edge: + pauli_string += "Z" + else: + pauli_string += "I" + hamiltonian[pauli_string] = 0.5 + + backend = CuTensorNetworkBackend(TNConfig(num_hyper_samples=10)) + self.energy = QuantumExpectation(self.circuit, backend, [hamiltonian]) + self.sampling = QuantumSampling(self.circuit, backend, 100) + + def forward(self): + start_time = torch.cuda.Event(enable_timing=True) + end_time = torch.cuda.Event(enable_timing=True) + + start_time.record() + output = self.energy() - len(self.input_graph) / 2 + end_time.record() + + torch.cuda.synchronize() + elapsed_time = start_time.elapsed_time(end_time) + print(f"Forward pass took {elapsed_time:.2f} ms") + + return output + + +def optimize(model, n_steps=100, lr=0.1): + optimizer = torch.optim.Adam(model.parameters(), lr=lr) + print(f"The initial parameters are:\n{next(model.parameters()).data.tolist()}") + print("") + for step in range(n_steps): + optimizer.zero_grad() + loss = model() + start_time = torch.cuda.Event(enable_timing=True) + end_time = torch.cuda.Event(enable_timing=True) + + start_time.record() + loss.backward() + end_time.record() + + torch.cuda.synchronize() + elapsed_time = start_time.elapsed_time(end_time) + print(f"Backward pass took {elapsed_time:.2f} ms") + + optimizer.step() + + print(f"Step: {step}, Cost Objective: {loss.item()}") + + print("") + print(f"The optimal parameters are:\n{next(model.parameters()).data.tolist()}") + print("") + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--n_wires", type=int, default=4, help="number of wires") + parser.add_argument("--n_layers", type=int, default=4, help="number of layers") + parser.add_argument("--steps", type=int, default=100, help="number of steps") + parser.add_argument("--lr", type=float, default=0.01, help="learning rate") + parser.add_argument("--seed", type=int, default=0, help="random seed") + args = parser.parse_args() + + torch.manual_seed(args.seed) + + # create a fully connected graph + input_graph = [] + for i in range(args.n_wires): + for j in range(i): + input_graph.append((i, j)) + + print(f"Cost Objective Minimum (Analytic Reference Result): {math.floor(args.n_wires**2 // 4)}") + + model = MAXCUT(n_wires=args.n_wires, input_graph=input_graph, n_layers=args.n_layers) + optimize(model, n_steps=args.steps, lr=args.lr) + samples = model.sampling() + + print(f"Sampling Results: {samples}") + + +if __name__ == "__main__": + main() diff --git a/examples/cuquantum/sec1.ipynb b/examples/cuquantum/sec1.ipynb new file mode 100644 index 00000000..5588c53e --- /dev/null +++ b/examples/cuquantum/sec1.ipynb @@ -0,0 +1,1815 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "c4d58435", + "metadata": {}, + "source": [ + "## 1. Operations" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "6774f699", + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from torchquantum.plugin.cuquantum import *\n", + "from torchquantum.operator.standard_gates import *" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c160bd21", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "all zero state: class: QuantumDevice \n", + " device name: default \n", + " number of qubits: 1 \n", + " batch size: 1 \n", + " current computing device: cpu \n", + " recording op history: False \n", + " current states: array([[1.+0.j, 0.+0.j]], dtype=complex64)\n", + "after h gate: class: QuantumDevice \n", + " device name: default \n", + " number of qubits: 1 \n", + " batch size: 1 \n", + " current computing device: cpu \n", + " recording op history: False \n", + " current states: array([[0.70710677+0.j, 0.70710677+0.j]], dtype=complex64)\n", + "after rx gate: class: QuantumDevice \n", + " device name: default \n", + " number of qubits: 1 \n", + " batch size: 1 \n", + " current computing device: cpu \n", + " recording op history: False \n", + " current states: array([[0.6991667-0.10566872j, 0.6991667-0.10566872j]], dtype=complex64)\n" + ] + } + ], + "source": [ + "import torchquantum as tq\n", + "import torchquantum.functional as tqf\n", + "q_dev = tq.QuantumDevice(n_wires=1)\n", + "q_dev.reset_states(bsz=1)\n", + "print(f\"all zero state: {q_dev}\")\n", + "tqf.h(q_dev, wires=0)\n", + "print(f\"after h gate: {q_dev}\")\n", + "\n", + "tqf.rx(q_dev, wires=0, params=[0.3])\n", + "\n", + "print(f\"after rx gate: {q_dev}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "b65dd003", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[{'0': np.int64(54), '1': np.int64(46)}]\n" + ] + } + ], + "source": [ + "# Method 1: Using cuquantum ParameterizedQuantumCircuit\n", + "n_wires = 1\n", + "\n", + "# Create a parameterized quantum circuit with cuquantum backend\n", + "circuit = ParameterizedQuantumCircuit(n_wires=n_wires, n_input_params=0)\n", + "\n", + "\n", + "# Add gates to the circuit\n", + "circuit.append_gate(Hadamard, wires=0)\n", + "circuit.append_gate(RX, wires=0, fixed_params=[0.3])\n", + "\n", + "backend = CuTensorNetworkBackend(TNConfig(num_hyper_samples=1))\n", + "sampling = QuantumSampling(circuit, backend, 100)\n", + "print(sampling())" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "fc02b756", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Traditional TorchQuantum sampling:\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAh8AAAHCCAYAAABPFau9AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAANTtJREFUeJzt3Xl8VPW9//H3ZA9ZJgRCApewyBYQARsEwi5EIpdLQXKlaFsIhQI2gUJEK60SoCwWRbACohaDG1fLtYC2CMrqxhrABSVGZPsJCWBNIlsC5Pv7w0fOZUhYEsI3Cb6ej8c8Hjnf853v+cyZMzPvnGXGZYwxAgAAsMSrsgsAAAA/LYQPAABgFeEDAABYRfgAAABWET4AAIBVhA8AAGAV4QMAAFhF+AAAAFYRPgAAgFWED1S4KVOmyOVyebQ1atRISUlJN3zZBw4ckMvl0pIlS5y2pKQkBQcH3/BlF3O5XJoyZYq15ZXH9u3b1blzZwUFBcnlcmn37t2VXdJNp2fPnurZs+dV+7lcLqWkpFy135IlS+RyuXTgwIHrL66cSnt9AeVB+ECVtWrVqir7IV6Va7uac+fO6d5779W///1vzZ07V6+88ooaNmxY2WWhHBYuXFjuIFCdt2FUfz6VXQB+GjIzM+XlVbasu2rVKi1YsKBMb5ANGzbUmTNn5OvrW8YKy+ZKtZ05c0Y+PlX3pbVv3z4dPHhQL7zwgkaOHFnZ5eAa/frXv9aQIUPk7+/vtC1cuFC1a9cu117Fqvz6ws2v6r5D4qZy8RvmjXD+/HkVFRXJz89PAQEBN3RZV1PZy7+aY8eOSZLCwsIqt5Aq6tSpUwoKCqrsMkrw9vaWt7d3pSy7Kr2+cHPgsAuuy4cffqg77rhDAQEBatKkiZ577rlS+116zse5c+c0depUNWvWTAEBAapVq5a6du2q9957T9KP52ksWLBA0o/HxItv0v8dd37yySc1b948NWnSRP7+/vriiy+ueEz6m2++UUJCgoKCglSvXj1NmzZNF/+o88aNG+VyubRx40aP+1065pVqK2679L/JXbt2qW/fvgoNDVVwcLB69+6tLVu2ePQpPqb/0UcfKTU1VREREQoKCtI999yj48ePl/4EXGL9+vXq1q2bgoKCFBYWpgEDBujLL7905iclJalHjx6SpHvvvVcul+uK5yUU1/Thhx9q3LhxioiIUFhYmEaPHq3CwkLl5uZq6NChqlmzpmrWrKmHH35Yl/5QdlFRkebNm6dbb71VAQEBioyM1OjRo/X999979Fu5cqX69eunevXqyd/fX02aNNGf//xnXbhwwaNfVlaWEhMTFRUVpYCAANWvX19DhgxRXl6epCufl3Dpc1N8ftIXX3yh+++/XzVr1lTXrl2d+a+++qpiY2MVGBio8PBwDRkyRIcPHy4x7vPPP68mTZooMDBQHTp00AcffHDZdXo5r732mlq0aKGAgADFxsbq/fff95h/6TkfjRo10p49e7Rp0yZnGyx+Lm2+vorPqfr22281cOBABQcHKyIiQhMnTizx3H333Xf69a9/rdDQUIWFhWnYsGH65JNPSoyZnZ2t4cOHq379+vL391fdunU1YMCASj3fBRWLPR8ot88++0x9+vRRRESEpkyZovPnzystLU2RkZFXve+UKVM0a9YsjRw5Uh06dFB+fr527NihnTt36q677tLo0aN15MgRvffee3rllVdKHSM9PV1nz57VqFGj5O/vr/DwcBUVFZXa98KFC7r77rvVqVMnzZ49W6tXr1ZaWprOnz+vadOmlelxX0ttF9uzZ4+6deum0NBQPfzww/L19dVzzz2nnj17atOmTerYsaNH/7Fjx6pmzZpKS0vTgQMHNG/ePKWkpOiNN9644nLWrl2rvn376pZbbtGUKVN05swZPfPMM+rSpYt27typRo0aafTo0fqP//gPzZw5U+PGjdMdd9xxTc/X2LFjFRUVpalTp2rLli16/vnnFRYWpo8//lgNGjTQzJkztWrVKj3xxBNq3bq1hg4d6rG+lixZouHDh2vcuHHav3+/5s+fr127dumjjz5yduEvWbJEwcHBSk1NVXBwsNavX6/JkycrPz9fTzzxhCSpsLBQCQkJKigocGr69ttv9c9//lO5ublyu91XfSyluffee9WsWTPNnDnTCU8zZszQY489psGDB2vkyJE6fvy4nnnmGXXv3l27du1y9hwtXrxYo0ePVufOnTV+/Hh98803+vnPf67w8HBFR0df0/I3bdqkN954Q+PGjZO/v78WLlyou+++W9u2bVPr1q1Lvc+8efM0duxYBQcH609/+pMkOc9lZby+EhIS1LFjRz355JNau3at5syZoyZNmuiBBx6Q9GMI7d+/v7Zt26YHHnhAMTExWrlypYYNG1Zi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GjRooM2bN1/PogAAwE2iTHs+LlZUVKTx48erS5cuat26tSQpOztbfn5+CgsL8+gbGRmp7OzsUscpKChQQUGBM52fn1/ekgAAQDVQ7j0fycnJ+vzzz/X6669fVwGzZs2S2+12btHR0dc1HgAAqNrKFT5SUlL0z3/+Uxs2bFD9+vWd9qioKBUWFio3N9ejf05OjqKiokoda9KkScrLy3Nuhw8fLk9JAACgmihT+DDGKCUlRcuXL9f69evVuHFjj/mxsbHy9fXVunXrnLbMzEwdOnRIcXFxpY7p7++v0NBQjxsAALh5lemcj+TkZC1dulQrV65USEiIcx6H2+1WYGCg3G63RowYodTUVIWHhys0NFRjx45VXFwcV7oAAABJZQwfzz77rCSpZ8+eHu3p6elKSkqSJM2dO1deXl5KTExUQUGBEhIStHDhwgopFgAAVH9lCh/GmKv2CQgI0IIFC7RgwYJyFwUAAG5e/LYLAACwivABAACsInwAAACrCB8AAMAqwgcAALCK8AEAAKwifAAAAKsIHwAAwCrCBwAAsIrwAQAArCJ8AAAAqwgfAADAKsIHAACwivABAACsInwAAACrCB8AAMAqwgcAALCK8AEAAKwifAAAAKsIHwAAwCrCBwAAsIrwAQAArCJ8AAAAqwgfAADAKsIHAACwivABAACsInwAAACrCB8AAMAqwgcAALCK8AEAAKwifAAAAKsIHwAAwCrCBwAAsIrwAQAArCJ8AAAAqwgfAADAKsIHAACwivABAACsInwAAACrCB8AAMAqwgcAALCK8AEAAKwifAAAAKsIHwAAwCrCBwAAsIrwAQAArCJ8AAAAqwgfAADAKsIHAACwivABAACsInwAAACrCB8AAMAqwgcAALCK8AEAAKwifAAAAKsIHwAAwCrCBwAAsIrwAQAArCpz+Hj//ffVv39/1atXTy6XSytWrPCYb4zR5MmTVbduXQUGBio+Pl5ZWVkVVS8AAKjmyhw+Tp06pbZt22rBggWlzp89e7b++te/atGiRdq6dauCgoKUkJCgs2fPXnexAACg+vMp6x369u2rvn37ljrPGKN58+bp0Ucf1YABAyRJL7/8siIjI7VixQoNGTLk+qoFAADVXoWe87F//35lZ2crPj7eaXO73erYsaM2b95ckYsCAADVVJn3fFxJdna2JCkyMtKjPTIy0pl3qYKC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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Traditional result: [OrderedDict([('0', 49), ('1', 51)])]\n" + ] + } + ], + "source": [ + "# For comparison: Traditional TorchQuantum sampling approach\n", + "print(\"Traditional TorchQuantum sampling:\")\n", + "q_dev_compare = tq.QuantumDevice(n_wires=1)\n", + "q_dev_compare.reset_states(bsz=1)\n", + "\n", + "# Apply the same gates\n", + "tqf.h(q_dev_compare, wires=0)\n", + "tqf.rx(q_dev_compare, wires=0, params=[0.3])\n", + "\n", + "# Sample using TorchQuantum's measure function\n", + "traditional_samples = tq.measure(q_dev_compare, n_shots=100, draw_id=0)\n", + "print(f\"Traditional result: {traditional_samples}\")\n", + "\n", + "# The results should be statistically similar (both are sampling from the same quantum state)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "220c94df", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "=== Gradient-Based Optimization with cuQuantum ===\n", + "Parameters shape: torch.Size([16])\n", + "Parameters type: \n", + "Starting optimization to reach |00000000⟩ state...\n", + "Initial parameters (first few): [-1.1306666 -0.09106943 -0.9094527 2.2768314 0.99367696]\n", + "Step 0: amplitude=0.086376, loss=0.913624\n", + " gradients norm: 0.214764\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/home/zkysfls/torchquantum/torchquantum/plugin/cuquantum/cutn/gradient.py:48: UserWarning: Casting complex values to real discards the imaginary part (Triggered internally at /pytorch/aten/src/ATen/native/Copy.cpp:307.)\n", + " grads[arg_idx][var_idx] = grad_output * (val_plus - val_minus) / delta\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Step 5: amplitude=0.105141, loss=0.894859\n", + " gradients norm: 0.217477\n", + "Step 10: amplitude=0.127305, loss=0.872695\n", + " gradients norm: 0.238538\n", + "Step 10: amplitude=0.127305, loss=0.872695\n", + " gradients norm: 0.238538\n", + "Step 15: amplitude=0.152420, loss=0.847580\n", + " gradients norm: 0.259759\n", + "Step 15: amplitude=0.152420, loss=0.847580\n", + " gradients norm: 0.259759\n", + "Step 20: amplitude=0.180303, loss=0.819697\n", + " gradients norm: 0.278466\n", + "Step 20: amplitude=0.180303, loss=0.819697\n", + " gradients norm: 0.278466\n", + "Step 25: amplitude=0.210954, loss=0.789046\n", + " gradients norm: 0.295565\n", + "Step 25: amplitude=0.210954, loss=0.789046\n", + " gradients norm: 0.295565\n", + "Step 30: amplitude=0.244110, loss=0.755890\n", + " gradients norm: 0.307360\n", + "Step 30: amplitude=0.244110, loss=0.755890\n", + " gradients norm: 0.307360\n", + "Step 35: amplitude=0.278897, loss=0.721103\n", + " gradients norm: 0.313127\n", + "Step 35: amplitude=0.278897, loss=0.721103\n", + " gradients norm: 0.313127\n", + "Step 40: amplitude=0.314296, loss=0.685704\n", + " gradients norm: 0.314309\n", + "Step 40: amplitude=0.314296, loss=0.685704\n", + " gradients norm: 0.314309\n", + "Step 45: amplitude=0.348771, loss=0.651229\n", + " gradients norm: 0.307081\n", + "\n", + "Final amplitude for |00000000⟩: 0.374671\n", + "Step 45: amplitude=0.348771, loss=0.651229\n", + " gradients norm: 0.307081\n", + "\n", + "Final amplitude for |00000000⟩: 0.374671\n" + ] + } + ], + "source": [ + "# Gradient-based optimization with cuQuantum\n", + "print(\"=== Gradient-Based Optimization with cuQuantum ===\")\n", + "\n", + "# Reset and create a new circuit for optimization\n", + "n_wires = 8\n", + "circuit_opt = ParameterizedQuantumCircuit(n_wires=n_wires, n_input_params=0, n_trainable_params=16)\n", + "for layer in range(4):\n", + " for wire in range(n_wires):\n", + " circuit_opt.append_gate(Hadamard, wires=wire)\n", + " for wire in range(n_wires-1):\n", + " circuit_opt.append_gate(CNOT, wires=[wire, wire+1])\n", + " for wire in range(n_wires):\n", + " circuit_opt.append_gate(RX, wires=wire, trainable_idx=layer + wire)\n", + "\n", + "# Initialize parameter randomly\n", + "circuit_opt.set_trainable_params(torch.randn(16))\n", + "\n", + "# Create optimizer - FIX: Wrap trainable_params in a list!\n", + "print(\"Parameters shape:\", circuit_opt.trainable_params.shape)\n", + "print(\"Parameters type:\", type(circuit_opt.trainable_params))\n", + "optimizer = torch.optim.Adam([circuit_opt.trainable_params], lr=0.01) # Note the []\n", + "\n", + "# Target: maximize amplitude of specific state\n", + "target_bitstring = \"00000000\" # All zeros state for 8 qubits\n", + "backend_opt = CuTensorNetworkBackend(TNConfig(num_hyper_samples=1))\n", + "amplitude_module_opt = QuantumAmplitude(circuit_opt, backend_opt, [target_bitstring])\n", + "\n", + "print(f\"Starting optimization to reach |{target_bitstring}⟩ state...\")\n", + "print(\"Initial parameters (first few):\", circuit_opt.trainable_params[:5].detach().numpy())\n", + "\n", + "# Optimization loop\n", + "for step in range(50):\n", + " optimizer.zero_grad()\n", + " \n", + " # Compute amplitude and loss\n", + " amplitude = amplitude_module_opt()\n", + " loss = 1 - amplitude.abs() # Minimize this to maximize amplitude\n", + " \n", + " # Backpropagation\n", + " loss.backward()\n", + " optimizer.step()\n", + " \n", + " if step % 5 == 0:\n", + " print(f\"Step {step:2d}: amplitude={amplitude.abs().item():.6f}, loss={loss.item():.6f}\")\n", + " print(f\" gradients norm: {circuit_opt.trainable_params.grad.norm().item():.6f}\")\n", + "\n", + "print(f\"\\nFinal amplitude for |{target_bitstring}⟩: {amplitude.abs().item():.6f}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "c8284e62", + "metadata": {}, + "source": [ + "## 2. Model class" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b2185b9b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " class: QuantumDevice \n", + " device name: default \n", + " number of qubits: 2 \n", + " batch size: 1 \n", + " current computing device: cpu \n", + " recording op history: False \n", + " current states: array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", + " [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n", + " [1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j]], dtype=complex64)\n", + " class: QuantumDevice \n", + " device name: default \n", + " number of qubits: 2 \n", + " batch size: 1 \n", + " current computing device: cpu \n", + " recording op history: False \n", + " current states: array([[ 0.75102925+0.0200534j , -0.11806175-0.33148786j,\n", + " 0.3145066 +0.4528266j , 0.02394111-0.08479779j],\n", + " [ 0.75102925+0.0200534j , -0.11806175-0.33148786j,\n", + " 0.3145066 +0.4528266j , 0.02394111-0.08479779j],\n", + " [ 0.75102925+0.0200534j , -0.11806175-0.33148786j,\n", + " 0.3145066 +0.4528266j , 0.02394111-0.08479779j]],\n", + " dtype=complex64)\n" + ] + } + ], + "source": [ + "class QModel(tq.QuantumModule):\n", + " def __init__(self):\n", + " super().__init__()\n", + " self.n_wires = 2\n", + " self.u3_0 = tq.U3(has_params=True, trainable=True)\n", + " self.u3_1 = tq.U3(has_params=True, trainable=True)\n", + " self.cu3_0 = tq.CU3(has_params=True, trainable=True)\n", + " self.cu3_1 = tq.CU3(has_params=True, trainable=True)\n", + " self.u3_2 = tq.U3(has_params=True, trainable=True)\n", + " self.u3_3 = tq.U3(has_params=True, trainable=True)\n", + " #self.random_layer = tq.RandomLayer(n_ops=10,\n", + " # wires=list(range(self.n_wires)))\n", + "\n", + " def forward(self, q_device: tq.QuantumDevice):\n", + " self.u3_0(q_device, wires=0)\n", + " self.u3_1(q_device, wires=1)\n", + " self.cu3_0(q_device, wires=[0, 1])\n", + " self.u3_2(q_device, wires=0)\n", + " self.u3_3(q_device, wires=1)\n", + " self.cu3_1(q_device, wires=[1, 0])\n", + " #self.random_layer(q_device)\n", + "\n", + "\n", + "q_dev = tq.QuantumDevice(n_wires=2)\n", + "q_dev.reset_states(bsz=3)\n", + "print(q_dev)\n", + "\n", + "model = QModel()\n", + "model(q_dev)\n", + "print(q_dev)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "cc213356", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Testing corrected cuQuantum implementation:\n", + "Expectation value: 0.882402\n", + "Parameters shape: torch.Size([18])\n" + ] + } + ], + "source": [ + "\n", + "\n", + "# Corrected cuQuantum version that matches the TorchQuantum structure\n", + "class QModelCuQuantumCorrected(torch.nn.Module):\n", + " def __init__(self):\n", + " super().__init__()\n", + " self.n_wires = 2\n", + " \n", + " # Create circuit with same structure as TorchQuantum version\n", + " # 6 gates × 3 params each = 18 total parameters\n", + " self.circuit = ParameterizedQuantumCircuit(\n", + " n_wires=self.n_wires, \n", + " n_input_params=0, \n", + " n_trainable_params=18\n", + " )\n", + " \n", + " # Initialize parameters (make them trainable for optimization)\n", + " initial_params = torch.randn(18, requires_grad=True)\n", + " self.circuit.set_trainable_params(initial_params)\n", + " \n", + " # Add gates in the same order as TorchQuantum version\n", + " self.circuit.append_gate(U3, wires=0, trainable_idx=[0, 1, 2]) # u3_0\n", + " self.circuit.append_gate(U3, wires=1, trainable_idx=[3, 4, 5]) # u3_1 \n", + " self.circuit.append_gate(CU3, wires=[0, 1], trainable_idx=[6, 7, 8]) # cu3_0\n", + " self.circuit.append_gate(U3, wires=0, trainable_idx=[9, 10, 11]) # u3_2\n", + " self.circuit.append_gate(U3, wires=1, trainable_idx=[12, 13, 14]) # u3_3\n", + " self.circuit.append_gate(CU3, wires=[1, 0], trainable_idx=[15, 16, 17]) # cu3_1 \n", + " \n", + "\n", + " \n", + " # Create backend and expectation measurement \n", + " backend = CuTensorNetworkBackend(TNConfig(num_hyper_samples=1))\n", + " self.energy = QuantumExpectation(self.circuit, backend, [{\"IZ\": 1.0}]) # Note: dict format\n", + "\n", + " def forward(self):\n", + " output = self.energy()\n", + " return output\n", + "\n", + "\n", + "# Test the corrected version\n", + "print(\"\\nTesting corrected cuQuantum implementation:\")\n", + "model_corrected = QModelCuQuantumCorrected()\n", + "result = model_corrected()\n", + "print(f\"Expectation value: {result.item():.6f}\")\n", + "print(f\"Parameters shape: {model_corrected.circuit.trainable_params.shape}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "c885f466", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The initial parameters are:\n", + "[0.7232231497764587, -0.8105312585830688, -0.29326990246772766, 0.14994484186172485, -0.5617180466651917, 0.22708554565906525, -0.31306278705596924, 0.6680203676223755, -0.44283345341682434, 0.39733049273490906, -1.415021538734436, -0.4423304796218872, -0.7390486001968384, 1.4334053993225098, -0.9185096025466919, 0.02780008688569069, -1.6929177045822144, 2.3480868339538574]\n", + "\n", + "Backward pass took 56.73 ms\n", + "Step: 0, Cost Objective: 0.7153363227844238\n", + "Backward pass took 58.19 ms\n", + "Step: 1, Cost Objective: 0.7043275237083435\n", + "Backward pass took 50.80 ms\n", + "Step: 2, Cost Objective: 0.6928059458732605\n", + "Backward pass took 50.38 ms\n", + "Step: 3, Cost Objective: 0.6807578206062317\n", + "Backward pass took 42.66 ms\n", + "Step: 4, Cost Objective: 0.6681620478630066\n", + "Backward pass took 43.81 ms\n", + "Step: 5, Cost Objective: 0.6550008654594421\n", + "Backward pass took 41.17 ms\n", + "Step: 6, Cost Objective: 0.641255795955658\n", + "Backward pass took 29.15 ms\n", + "Step: 7, Cost Objective: 0.6269086003303528\n", + "Backward pass took 31.95 ms\n", + "Step: 8, Cost Objective: 0.6119385957717896\n", + "Backward pass took 50.38 ms\n", + "Step: 3, Cost Objective: 0.6807578206062317\n", + "Backward pass took 42.66 ms\n", + "Step: 4, Cost Objective: 0.6681620478630066\n", + "Backward pass took 43.81 ms\n", + "Step: 5, Cost Objective: 0.6550008654594421\n", + "Backward pass took 41.17 ms\n", + "Step: 6, Cost Objective: 0.641255795955658\n", + "Backward pass took 29.15 ms\n", + "Step: 7, Cost Objective: 0.6269086003303528\n", + "Backward pass took 31.95 ms\n", + "Step: 8, Cost Objective: 0.6119385957717896\n", + "Backward pass took 32.74 ms\n", + "Step: 9, Cost Objective: 0.5963292121887207\n", + "\n", + "The optimal parameters are:\n", + "[0.8244640231132507, -0.8526199460029602, -0.29326990246772766, 0.24756324291229248, -0.6631395220756531, 0.22708554565906525, -0.41386252641677856, 0.5670632719993591, -0.5444058179855347, 0.39389318227767944, -1.3558292388916016, -0.4637945294380188, -0.8394381999969482, 1.491828441619873, -1.0198345184326172, -0.02046274207532406, -1.6929177045822144, 2.411752462387085]\n", + "\n", + "Expectation value: 0.580064\n", + "Backward pass took 32.74 ms\n", + "Step: 9, Cost Objective: 0.5963292121887207\n", + "\n", + "The optimal parameters are:\n", + "[0.8244640231132507, -0.8526199460029602, -0.29326990246772766, 0.24756324291229248, -0.6631395220756531, 0.22708554565906525, -0.41386252641677856, 0.5670632719993591, -0.5444058179855347, 0.39389318227767944, -1.3558292388916016, -0.4637945294380188, -0.8394381999969482, 1.491828441619873, -1.0198345184326172, -0.02046274207532406, -1.6929177045822144, 2.411752462387085]\n", + "\n", + "Expectation value: 0.580064\n" + ] + } + ], + "source": [ + "def optimize(model, n_steps=100, lr=0.1):\n", + " optimizer = torch.optim.Adam(model.parameters(), lr=lr)\n", + " print(f\"The initial parameters are:\\n{next(model.parameters()).data.tolist()}\")\n", + " print(\"\")\n", + " for step in range(n_steps):\n", + " optimizer.zero_grad()\n", + " loss = model()\n", + " start_time = torch.cuda.Event(enable_timing=True)\n", + " end_time = torch.cuda.Event(enable_timing=True)\n", + "\n", + " start_time.record()\n", + " loss.backward()\n", + " end_time.record()\n", + "\n", + " torch.cuda.synchronize()\n", + " elapsed_time = start_time.elapsed_time(end_time)\n", + " print(f\"Backward pass took {elapsed_time:.2f} ms\")\n", + "\n", + " optimizer.step()\n", + "\n", + " print(f\"Step: {step}, Cost Objective: {loss.item()}\")\n", + "\n", + " print(\"\")\n", + " print(f\"The optimal parameters are:\\n{next(model.parameters()).data.tolist()}\")\n", + " print(\"\")\n", + "\n", + "model_corrected = QModelCuQuantumCorrected()\n", + "optimize(model_corrected, n_steps=10, lr=0.01)\n", + "result = model_corrected()\n", + "print(f\"Expectation value: {result.item():.6f}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "404e42b6", + "metadata": {}, + "source": [ + "# 3. VQE" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "cc34b527", + "metadata": {}, + "outputs": [], + "source": [ + "import torchquantum as tq\n", + "import torch\n", + "import torch.nn.functional as F\n", + "from torchquantum.util.vqe_utils import parse_hamiltonian_file\n", + "from torchquantum.dataset import VQE\n", + "import random\n", + "import numpy as np\n", + "import argparse\n", + "import torch.optim as optim\n", + "\n", + "from torch.optim.lr_scheduler import CosineAnnealingLR, ConstantLR\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "e0bc51dd", + "metadata": {}, + "outputs": [], + "source": [ + "class QVQEModel(tq.QuantumModule):\n", + " def __init__(self, arch, hamil_info):\n", + " super().__init__()\n", + " self.arch = arch\n", + " self.hamil_info = hamil_info\n", + " self.n_wires = hamil_info['n_wires']\n", + " self.n_blocks = arch['n_blocks']\n", + " self.u3_layers = tq.QuantumModuleList()\n", + " self.cu3_layers = tq.QuantumModuleList()\n", + " for _ in range(self.n_blocks):\n", + " self.u3_layers.append(tq.Op1QAllLayer(op=tq.U3,\n", + " n_wires=self.n_wires,\n", + " has_params=True,\n", + " trainable=True,\n", + " ))\n", + " self.cu3_layers.append(tq.Op2QAllLayer(op=tq.CU3,\n", + " n_wires=self.n_wires,\n", + " has_params=True,\n", + " trainable=True,\n", + " circular=True\n", + " ))\n", + " self.measure = tq.MeasureMultipleTimes(\n", + " obs_list=hamil_info['hamil_list'])\n", + "\n", + " def forward(self, q_device):\n", + " q_device.reset_states(bsz=1)\n", + " for k in range(self.n_blocks):\n", + " self.u3_layers[k](q_device)\n", + " self.cu3_layers[k](q_device)\n", + " x = self.measure(q_device)\n", + "\n", + " hamil_coefficients = torch.tensor([hamil['coefficient'] for hamil in\n", + " self.hamil_info['hamil_list']],\n", + " device=x.device).double()\n", + "\n", + " for k, hamil in enumerate(self.hamil_info['hamil_list']):\n", + " for wire, observable in zip(hamil['wires'], hamil['observables']):\n", + " if observable == 'i':\n", + " x[k][wire] = 1\n", + " for wire in range(q_device.n_wires):\n", + " if wire not in hamil['wires']:\n", + " x[k][wire] = 1\n", + "\n", + " x = torch.cumprod(x, dim=-1)[:, -1].double()\n", + " x = torch.dot(x, hamil_coefficients)\n", + "\n", + " if x.dim() == 0:\n", + " x = x.unsqueeze(0)\n", + "\n", + " return x\n", + "\n", + "\n", + "def train(dataflow, q_device, model, device, optimizer):\n", + " for _ in dataflow['train']:\n", + " outputs = model(q_device)\n", + " loss = outputs.mean()\n", + "\n", + " optimizer.zero_grad()\n", + " loss.backward()\n", + " optimizer.step()\n", + " print(f\"Expectation of energy: {loss.item()}\")\n", + "\n", + "\n", + "def valid_test(dataflow, q_device, split, model, device):\n", + " with torch.no_grad():\n", + " for _ in dataflow[split]:\n", + " outputs = model(q_device)\n", + " loss = outputs.mean()\n", + "\n", + " print(f\"Expectation of energy: {loss}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "942a070b", + "metadata": {}, + "outputs": [], + "source": [ + "class Args(object):\n", + " def __init__(self):\n", + " pass\n", + "\n", + "def main():\n", + " # parser = argparse.ArgumentParser()\n", + " # parser.add_argument('--pdb', action='store_true', help='debug with pdb')\n", + " # parser.add_argument('--n_blocks', type=int, default=2,\n", + " # help='number of blocks, each contain one layer of '\n", + " # 'U3 gates and one layer of CU3 with '\n", + " # 'ring connections')\n", + " # parser.add_argument('--steps_per_epoch', type=int, default=10,\n", + " # help='number of training epochs')\n", + " # parser.add_argument('--epochs', type=int, default=100,\n", + " # help='number of training epochs')\n", + " # parser.add_argument('--hamil_filename', type=str, default='./h2_new.txt',\n", + " # help='number of training epochs')\n", + "\n", + " args = Args()\n", + " args.n_blocks = 2\n", + " args.steps_per_epoch=100\n", + " args.epochs=100\n", + " args.hamil_filename = 'h2_new.txt'\n", + "\n", + " # if args.pdb:\n", + " # import pdb\n", + " # pdb.set_trace()\n", + "\n", + " seed = 0\n", + " random.seed(seed)\n", + " np.random.seed(seed)\n", + " torch.manual_seed(seed)\n", + "\n", + " dataset = VQE(steps_per_epoch=args.steps_per_epoch)\n", + "\n", + " dataflow = dict()\n", + "\n", + " for split in dataset:\n", + " if split == 'train':\n", + " sampler = torch.utils.data.RandomSampler(dataset[split])\n", + " else:\n", + " sampler = torch.utils.data.SequentialSampler(dataset[split])\n", + " dataflow[split] = torch.utils.data.DataLoader(\n", + " dataset[split],\n", + " batch_size=1,\n", + " sampler=sampler,\n", + " num_workers=1,\n", + " pin_memory=True)\n", + "\n", + " hamil_info = parse_hamiltonian_file(args.hamil_filename)\n", + "\n", + " use_cuda = torch.cuda.is_available()\n", + " device = torch.device(\"cuda\" if use_cuda else \"cpu\")\n", + " model = QVQEModel(arch={\"n_blocks\": args.n_blocks},\n", + " hamil_info=hamil_info)\n", + "\n", + " model.to(device)\n", + "\n", + " n_epochs = args.epochs\n", + " optimizer = optim.Adam(model.parameters(), lr=5e-3, weight_decay=1e-4)\n", + " scheduler = CosineAnnealingLR(optimizer, T_max=n_epochs)\n", + "\n", + " q_device = tq.QuantumDevice(n_wires=hamil_info['n_wires'])\n", + " q_device.reset_states(bsz=1)\n", + "\n", + " for epoch in range(1, n_epochs + 1):\n", + " # train\n", + " print(f\"Epoch {epoch}, LR: {optimizer.param_groups[0]['lr']}\")\n", + " train(dataflow, q_device, model, device, optimizer)\n", + "\n", + " # valid\n", + " valid_test(dataflow, q_device, 'valid', model, device)\n", + " scheduler.step()\n", + "\n", + " # final valid\n", + " valid_test(dataflow, q_device, 'valid', model, device)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "e047085a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Epoch 1, LR: 0.005\n", + "Expectation of energy: -0.3082973230728011\n", + "Expectation of energy: -0.3150707638089269\n", + "Expectation of energy: -0.32225806677983054\n", + "Expectation of energy: -0.32985389654800246\n", + "Expectation of energy: -0.3378548957310795\n", + "Expectation of energy: -0.3462579786169898\n", + "Expectation of energy: -0.3550608077597749\n", + "Expectation of energy: -0.3642612311998784\n", + "Expectation of energy: -0.37385944329722354\n", + "Expectation of energy: -0.3838581392187024\n", + "Expectation of energy: -0.3942625904653395\n", + "Expectation of energy: -0.4050825282943114\n", + "Expectation of energy: -0.3082973230728011\n", + "Expectation of energy: -0.3150707638089269\n", + "Expectation of energy: -0.32225806677983054\n", + "Expectation of energy: -0.32985389654800246\n", + "Expectation of energy: -0.3378548957310795\n", + "Expectation of energy: -0.3462579786169898\n", + "Expectation of energy: -0.3550608077597749\n", + "Expectation of energy: -0.3642612311998784\n", + "Expectation of energy: -0.37385944329722354\n", + "Expectation of energy: -0.3838581392187024\n", + "Expectation of energy: -0.3942625904653395\n", + "Expectation of energy: -0.4050825282943114\n", + "Expectation of energy: -0.41632978112054325\n", + "Expectation of energy: -0.42802158732468754\n", + "Expectation of energy: -0.44017898427901825\n", + "Expectation of energy: -0.45282500048685337\n", + "Expectation of energy: -0.4659814111187496\n", + "Expectation of energy: -0.47966704588196407\n", + "Expectation of energy: -0.49389359348281064\n", + "Expectation of energy: -0.5086673941322084\n", + "Expectation of energy: -0.5239895214774657\n", + "Expectation of energy: -0.5398576807415613\n", + "Expectation of energy: -0.5562654748515534\n", + "Expectation of energy: -0.5732026949285625\n", + "Expectation of energy: -0.41632978112054325\n", + "Expectation of energy: -0.42802158732468754\n", + "Expectation of energy: -0.44017898427901825\n", + "Expectation of energy: -0.45282500048685337\n", + "Expectation of energy: -0.4659814111187496\n", + "Expectation of energy: -0.47966704588196407\n", + "Expectation of energy: -0.49389359348281064\n", + "Expectation of energy: -0.5086673941322084\n", + "Expectation of energy: -0.5239895214774657\n", + "Expectation of energy: -0.5398576807415613\n", + "Expectation of energy: -0.5562654748515534\n", + "Expectation of energy: -0.5732026949285625\n", + "Expectation of energy: -0.590658420120113\n", + "Expectation of energy: -0.608620330128034\n", + "Expectation of energy: -0.6270758557711233\n", + "Expectation of energy: -0.6460132000997498\n", + "Expectation of energy: -0.6654196954810685\n", + "Expectation of energy: -0.685281903674411\n", + "Expectation of energy: -0.7055834474923162\n", + "Expectation of energy: -0.726305219584213\n", + "Expectation of energy: -0.7474252436428308\n", + "Expectation of energy: -0.7689183280480366\n", + "Expectation of energy: -0.7907568680503463\n", + "Expectation of energy: -0.81291209259283\n", + "Expectation of energy: -0.8353534281831065\n", + "Expectation of energy: -0.85804704188856\n", + "Expectation of energy: -0.880957847373533\n", + "Expectation of energy: -0.590658420120113\n", + "Expectation of energy: -0.608620330128034\n", + "Expectation of energy: -0.6270758557711233\n", + "Expectation of energy: -0.6460132000997498\n", + "Expectation of energy: -0.6654196954810685\n", + "Expectation of energy: -0.685281903674411\n", + "Expectation of energy: -0.7055834474923162\n", + "Expectation of energy: -0.726305219584213\n", + "Expectation of energy: -0.7474252436428308\n", + "Expectation of energy: -0.7689183280480366\n", + "Expectation of energy: -0.7907568680503463\n", + "Expectation of energy: -0.81291209259283\n", + "Expectation of energy: -0.8353534281831065\n", + "Expectation of energy: -0.85804704188856\n", + "Expectation of energy: -0.880957847373533\n", + "Expectation of energy: -0.9040495616514254\n", + "Expectation of energy: -0.9272829746508199\n", + "Expectation of energy: -0.9506196728202654\n", + "Expectation of energy: -0.9740197977914173\n", + "Expectation of energy: -0.9974416073147603\n", + "Expectation of energy: -1.0208441236378332\n", + "Expectation of energy: -1.0441846852648067\n", + "Expectation of energy: -1.0674217070922487\n", + "Expectation of energy: -1.0905137907957678\n", + "Expectation of energy: -1.1134213110665665\n", + "Expectation of energy: -1.1361057072349103\n", + "Expectation of energy: -1.1585313333150968\n", + "Expectation of energy: -0.9040495616514254\n", + "Expectation of energy: -0.9272829746508199\n", + "Expectation of energy: -0.9506196728202654\n", + "Expectation of energy: -0.9740197977914173\n", + "Expectation of energy: -0.9974416073147603\n", + "Expectation of energy: -1.0208441236378332\n", + "Expectation of energy: -1.0441846852648067\n", + "Expectation of energy: -1.0674217070922487\n", + "Expectation of energy: -1.0905137907957678\n", + "Expectation of energy: -1.1134213110665665\n", + "Expectation of energy: -1.1361057072349103\n", + "Expectation of energy: -1.1585313333150968\n", + "Expectation of energy: -1.180663582104024\n", + "Expectation of energy: -1.202471254116194\n", + "Expectation of energy: -1.2239270010409584\n", + "Expectation of energy: -1.2450051637873145\n", + "Expectation of energy: -1.2656835199066165\n", + "Expectation of energy: -1.2859427383915436\n", + "Expectation of energy: -1.3057651667030923\n", + "Expectation of energy: -1.3251348949225135\n", + "Expectation of energy: -1.3440388635554033\n", + "Expectation of energy: -1.362463844595287\n", + "Expectation of energy: -1.3803986113820435\n", + "Expectation of energy: -1.3978334377382622\n", + "Expectation of energy: -1.414759023212874\n", + "Expectation of energy: -1.4311687675460558\n", + "Expectation of energy: -1.4470567325681336\n", + "Expectation of energy: -1.180663582104024\n", + "Expectation of energy: -1.202471254116194\n", + "Expectation of energy: -1.2239270010409584\n", + "Expectation of energy: -1.2450051637873145\n", + "Expectation of energy: -1.2656835199066165\n", + "Expectation of energy: -1.2859427383915436\n", + "Expectation of energy: -1.3057651667030923\n", + "Expectation of energy: -1.3251348949225135\n", + "Expectation of energy: -1.3440388635554033\n", + "Expectation of energy: -1.362463844595287\n", + "Expectation of energy: -1.3803986113820435\n", + "Expectation of energy: -1.3978334377382622\n", + "Expectation of energy: -1.414759023212874\n", + "Expectation of energy: -1.4311687675460558\n", + "Expectation of energy: -1.4470567325681336\n", + "Expectation of energy: -1.4624196984805913\n", + "Expectation of energy: -1.4772561875299686\n", + "Expectation of energy: -1.4915666692216936\n", + "Expectation of energy: -1.5053542159521105\n", + "Expectation of energy: -1.5186237713800586\n", + "Expectation of energy: -1.5313820709801094\n", + "Expectation of energy: -1.5436378722801665\n", + "Expectation of energy: -1.5554013613852429\n", + "Expectation of energy: -1.5666839520069775\n", + "Expectation of energy: -1.5774984613201606\n", + "Expectation of energy: -1.587857075252658\n", + "Expectation of energy: -1.4624196984805913\n", + "Expectation of energy: -1.4772561875299686\n", + "Expectation of energy: -1.4915666692216936\n", + "Expectation of energy: -1.5053542159521105\n", + "Expectation of energy: -1.5186237713800586\n", + "Expectation of energy: -1.5313820709801094\n", + "Expectation of energy: -1.5436378722801665\n", + "Expectation of energy: -1.5554013613852429\n", + "Expectation of energy: -1.5666839520069775\n", + "Expectation of energy: -1.5774984613201606\n", + "Expectation of energy: -1.587857075252658\n", + "Expectation of energy: -1.5977739343081627\n", + "Expectation of energy: -1.6072635921352785\n", + "Expectation of energy: -1.6163389896972773\n", + "Expectation of energy: -1.62501606615993\n", + "Expectation of energy: -1.6333086159091856\n", + "Expectation of energy: -1.6412322610196552\n", + "Expectation of energy: -1.648801459848264\n", + "Expectation of energy: -1.6560320199591207\n", + "Expectation of energy: -1.6629383232418498\n", + "Expectation of energy: -1.6695364826198704\n", + "Expectation of energy: -1.6758401981134088\n", + "Expectation of energy: -1.6818654123544787\n", + "Expectation of energy: -1.6876254625710223\n", + "Expectation of energy: -1.6931351582182728\n", + "Expectation of energy: -1.5977739343081627\n", + "Expectation of energy: -1.6072635921352785\n", + "Expectation of energy: -1.6163389896972773\n", + "Expectation of energy: -1.62501606615993\n", + "Expectation of energy: -1.6333086159091856\n", + "Expectation of energy: -1.6412322610196552\n", + "Expectation of energy: -1.648801459848264\n", + "Expectation of energy: -1.6560320199591207\n", + "Expectation of energy: -1.6629383232418498\n", + "Expectation of energy: -1.6695364826198704\n", + "Expectation of energy: -1.6758401981134088\n", + "Expectation of energy: -1.6818654123544787\n", + "Expectation of energy: -1.6876254625710223\n", + "Expectation of energy: -1.6931351582182728\n", + "Expectation of energy: -1.6984067356422816\n", + "Expectation of energy: -1.7034542185236323\n", + "Expectation of energy: -1.7082892089668185\n", + "Expectation of energy: -1.7129236609278466\n", + "Expectation of energy: -1.7173691251398269\n", + "Expectation of energy: -1.721636294993666\n", + "Expectation of energy: -1.7257354466109298\n", + "Expectation of energy: -1.7296766908088\n", + "Expectation of energy: -1.7334683856039912\n", + "Expectation of energy: -1.6984067356422816\n", + "Expectation of energy: -1.7034542185236323\n", + "Expectation of energy: -1.7082892089668185\n", + "Expectation of energy: -1.7129236609278466\n", + "Expectation of energy: -1.7173691251398269\n", + "Expectation of energy: -1.721636294993666\n", + "Expectation of energy: -1.7257354466109298\n", + "Expectation of energy: -1.7296766908088\n", + "Expectation of energy: -1.7334683856039912\n", + "Expectation of energy: -1.7371202538957469\n", + "Epoch 2, LR: 0.0049987664009143295\n", + "Expectation of energy: -1.7371202538957469\n", + "Epoch 2, LR: 0.0049987664009143295\n", + "Expectation of energy: -1.7371202538957469\n", + "Expectation of energy: -1.7406388713359573\n", + "Expectation of energy: -1.7371202538957469\n", + "Expectation of energy: -1.7406388713359573\n", + "Expectation of energy: -1.7440338125895694\n", + "Expectation of energy: -1.7473112331370142\n", + "Expectation of energy: -1.7504784293738638\n", + "Expectation of energy: -1.753541674206239\n", + "Expectation of energy: -1.7565059543746655\n", + "Expectation of energy: -1.759377227185007\n", + "Expectation of energy: -1.7621603943167734\n", + "Expectation of energy: -1.7648596844048552\n", + "Expectation of energy: -1.7674792639099408\n", + "Expectation of energy: -1.770023437969753\n", + "Expectation of energy: -1.772495295945525\n", + "Expectation of energy: -1.7440338125895694\n", + "Expectation of energy: -1.7473112331370142\n", + "Expectation of energy: -1.7504784293738638\n", + "Expectation of energy: -1.753541674206239\n", + "Expectation of energy: -1.7565059543746655\n", + "Expectation of energy: -1.759377227185007\n", + "Expectation of energy: -1.7621603943167734\n", + "Expectation of energy: -1.7648596844048552\n", + "Expectation of energy: -1.7674792639099408\n", + "Expectation of energy: -1.770023437969753\n", + "Expectation of energy: -1.772495295945525\n", + "Expectation of energy: -1.7748981826991643\n", + "Expectation of energy: -1.7748981826991643\n", + "Expectation of energy: -1.777235585884982\n", + "Expectation of energy: -1.777235585884982\n", + "Expectation of energy: -1.7795100541143127\n", + "Expectation of energy: -1.7817233663791585\n", + "Expectation of energy: -1.7838789134322206\n", + "Expectation of energy: -1.7859782954261967\n", + "Expectation of energy: -1.7880238573362344\n", + "Expectation of energy: -1.790017182414406\n", + "Expectation of energy: -1.7919601299075572\n", + "Expectation of energy: -1.7938539914678886\n", + "Expectation of energy: -1.7957005365851084\n", + "Expectation of energy: -1.7795100541143127\n", + "Expectation of energy: -1.7817233663791585\n", + "Expectation of energy: -1.7838789134322206\n", + "Expectation of energy: -1.7859782954261967\n", + "Expectation of energy: -1.7880238573362344\n", + "Expectation of energy: -1.790017182414406\n", + "Expectation of energy: -1.7919601299075572\n", + "Expectation of energy: -1.7938539914678886\n", + "Expectation of energy: -1.7957005365851084\n", + "Expectation of energy: -1.797501363676768\n", + "Expectation of energy: -1.7992574832825081\n", + "Expectation of energy: -1.797501363676768\n", + "Expectation of energy: -1.7992574832825081\n", + "Expectation of energy: -1.8009702597400112\n", + "Expectation of energy: -1.80264074378608\n", + "Expectation of energy: -1.8009702597400112\n", + "Expectation of energy: -1.80264074378608\n", + "Expectation of energy: -1.8042699221185077\n", + "Expectation of energy: -1.8058597116028041\n", + "Expectation of energy: -1.8074102777885241\n", + "Expectation of energy: -1.8089228608213626\n", + "Expectation of energy: -1.810398627141158\n", + "Expectation of energy: -1.8118382800586137\n", + "Expectation of energy: -1.81324285278232\n", + "Expectation of energy: -1.8146133469352874\n", + "Expectation of energy: -1.8159508125215915\n", + "Expectation of energy: -1.8042699221185077\n", + "Expectation of energy: -1.8058597116028041\n", + "Expectation of energy: -1.8074102777885241\n", + "Expectation of energy: -1.8089228608213626\n", + "Expectation of energy: -1.810398627141158\n", + "Expectation of energy: -1.8118382800586137\n", + "Expectation of energy: -1.81324285278232\n", + "Expectation of energy: -1.8146133469352874\n", + "Expectation of energy: -1.8159508125215915\n", + "Expectation of energy: -1.8172553183052413\n", + "Expectation of energy: -1.8185284429125226\n", + "Expectation of energy: -1.8172553183052413\n", + "Expectation of energy: -1.8185284429125226\n", + "Expectation of energy: -1.8197708419066743\n", + "Expectation of energy: -1.8197708419066743\n", + "Expectation of energy: -1.8209831980280393\n", + "Expectation of energy: -1.8221666116671487\n", + "Expectation of energy: -1.8233216917527972\n", + "Expectation of energy: -1.8244490293341529\n", + "Expectation of energy: -1.825549587296592\n", + "Expectation of energy: -1.8266240050175007\n", + "Expectation of energy: -1.8276729232226387\n", + "Expectation of energy: -1.8286971577163584\n", + "Expectation of energy: -1.8296973277420652\n", + "Expectation of energy: -1.8306740291322348\n", + "Expectation of energy: -1.8316279971204035\n", + "Expectation of energy: -1.8209831980280393\n", + "Expectation of energy: -1.8221666116671487\n", + "Expectation of energy: -1.8233216917527972\n", + "Expectation of energy: -1.8244490293341529\n", + "Expectation of energy: -1.825549587296592\n", + "Expectation of energy: -1.8266240050175007\n", + "Expectation of energy: -1.8276729232226387\n", + "Expectation of energy: -1.8286971577163584\n", + "Expectation of energy: -1.8296973277420652\n", + "Expectation of energy: -1.8306740291322348\n", + "Expectation of energy: -1.8316279971204035\n", + "Expectation of energy: -1.8325602686818883\n", + "Expectation of energy: -1.8334704494336083\n", + "Expectation of energy: -1.8343599649678588\n", + "Expectation of energy: -1.8325602686818883\n", + "Expectation of energy: -1.8334704494336083\n", + "Expectation of energy: -1.8343599649678588\n", + "Expectation of energy: -1.8352291235035036\n", + "Expectation of energy: -1.836078429076151\n", + "Expectation of energy: -1.8369082967411425\n", + "Expectation of energy: -1.8377195957929808\n", + "Expectation of energy: -1.8385126233683013\n", + "Expectation of energy: -1.8392876235265518\n", + "Expectation of energy: -1.8400453568500272\n", + "Expectation of energy: -1.8407863561727447\n", + "Expectation of energy: -1.8415111449021362\n", + "Expectation of energy: -1.8422194070336828\n", + "Expectation of energy: -1.8429122597588616\n", + "Expectation of energy: -1.8435900274823893\n", + "Expectation of energy: -1.8352291235035036\n", + "Expectation of energy: -1.836078429076151\n", + "Expectation of energy: -1.8369082967411425\n", + "Expectation of energy: -1.8377195957929808\n", + "Expectation of energy: -1.8385126233683013\n", + "Expectation of energy: -1.8392876235265518\n", + "Expectation of energy: -1.8400453568500272\n", + "Expectation of energy: -1.8407863561727447\n", + "Expectation of energy: -1.8415111449021362\n", + "Expectation of energy: -1.8422194070336828\n", + "Expectation of energy: -1.8429122597588616\n", + "Expectation of energy: -1.8435900274823893\n", + "Expectation of energy: -1.8442528570504892\n", + "Expectation of energy: -1.8449012482341167\n", + "Expectation of energy: -1.8455354991341948\n", + "Expectation of energy: -1.8442528570504892\n", + "Expectation of energy: -1.8449012482341167\n", + "Expectation of energy: -1.8455354991341948\n", + "Expectation of energy: -1.8461557628430216\n", + "Expectation of energy: -1.8467629167627408\n", + "Expectation of energy: -1.8473568583743738\n", + "Expectation of energy: -1.847937777796123\n", + "Expectation of energy: -1.8485060900778687\n", + "Expectation of energy: -1.8490624245139484\n", + "Expectation of energy: -1.8496067567144294\n", + "Expectation of energy: -1.8501394581842807\n", + "Expectation of energy: -1.850660268481533\n", + "Expectation of energy: -1.8511704130587767\n", + "Expectation of energy: -1.8516694343999023\n", + "Expectation of energy: -1.8521579355335849\n", + "Expectation of energy: -1.8461557628430216\n", + "Expectation of energy: -1.8467629167627408\n", + "Expectation of energy: -1.8473568583743738\n", + "Expectation of energy: -1.847937777796123\n", + "Expectation of energy: -1.8485060900778687\n", + "Expectation of energy: -1.8490624245139484\n", + "Expectation of energy: -1.8496067567144294\n", + "Expectation of energy: -1.8501394581842807\n", + "Expectation of energy: -1.850660268481533\n", + "Expectation of energy: -1.8511704130587767\n", + "Expectation of energy: -1.8516694343999023\n", + "Expectation of energy: -1.8521579355335849\n", + "Expectation of energy: -1.8526356971209963\n", + "Expectation of energy: -1.8531036915416585\n", + "Expectation of energy: -1.8535612102112302\n", + "Expectation of energy: -1.8540092719617227\n", + "Expectation of energy: -1.8526356971209963\n", + "Expectation of energy: -1.8531036915416585\n", + "Expectation of energy: -1.8535612102112302\n", + "Expectation of energy: -1.8540092719617227\n", + "Expectation of energy: -1.8544478587887503\n", + "Expectation of energy: -1.8548767156421726\n", + "Expectation of energy: -1.8552967627978656\n", + "Expectation of energy: -1.855707565891321\n", + "Expectation of energy: -1.8561096923118137\n", + "Expectation of energy: -1.8565031795118154\n", + "Expectation of energy: -1.856888523347934\n", + "Expectation of energy: -1.8572654017177492\n", + "Expectation of energy: -1.8576343021637456\n", + "Expectation of energy: -1.8579951856933121\n", + "Expectation of energy: -1.858348341513046\n", + "Expectation of energy: -1.8586941857956536\n", + "Expectation of energy: -1.8544478587887503\n", + "Expectation of energy: -1.8548767156421726\n", + "Expectation of energy: -1.8552967627978656\n", + "Expectation of energy: -1.855707565891321\n", + "Expectation of energy: -1.8561096923118137\n", + "Expectation of energy: -1.8565031795118154\n", + "Expectation of energy: -1.856888523347934\n", + "Expectation of energy: -1.8572654017177492\n", + "Expectation of energy: -1.8576343021637456\n", + "Expectation of energy: -1.8579951856933121\n", + "Expectation of energy: -1.858348341513046\n", + "Expectation of energy: -1.8586941857956536\n", + "Expectation of energy: -1.8590325292433967\n", + "Expectation of energy: -1.8593637278896054\n", + "Expectation of energy: -1.8596875654468534\n", + "Expectation of energy: -1.8590325292433967\n", + "Expectation of energy: -1.8593637278896054\n", + "Expectation of energy: -1.8596875654468534\n", + "Expectation of energy: -1.860004442031103\n", + "Epoch 3, LR: 0.00499506682107068\n", + "Expectation of energy: -1.860004442031103\n", + "Epoch 3, LR: 0.00499506682107068\n", + "Expectation of energy: -1.860004442031103\n", + "Expectation of energy: -1.8603141892609298\n", + "Expectation of energy: -1.8606179837524155\n", + "Expectation of energy: -1.86091479750083\n", + "Expectation of energy: -1.8612049999289317\n", + "Expectation of energy: -1.8614892899542153\n", + "Expectation of energy: -1.8617670272965534\n", + "Expectation of energy: -1.8620391262303595\n", + "Expectation of energy: -1.8623053367764482\n", + "Expectation of energy: -1.860004442031103\n", + "Expectation of energy: -1.8603141892609298\n", + "Expectation of energy: -1.8606179837524155\n", + "Expectation of energy: -1.86091479750083\n", + "Expectation of energy: -1.8612049999289317\n", + "Expectation of energy: -1.8614892899542153\n", + "Expectation of energy: -1.8617670272965534\n", + "Expectation of energy: -1.8620391262303595\n", + "Expectation of energy: -1.8623053367764482\n", + "Expectation of energy: -1.8625657611754054\n", + "Expectation of energy: -1.8628203270784496\n", + "Expectation of energy: -1.863069407871183\n", + "Expectation of energy: -1.8633134450658166\n", + "Expectation of energy: -1.8625657611754054\n", + "Expectation of energy: -1.8628203270784496\n", + "Expectation of energy: -1.863069407871183\n", + "Expectation of energy: -1.8633134450658166\n", + "Expectation of energy: -1.8635520267275951\n", + "Expectation of energy: -1.8635520267275951\n", + "Expectation of energy: -1.8637852614298054\n", + "Expectation of energy: -1.8640134531211967\n", + "Expectation of energy: -1.86423665530881\n", + "Expectation of energy: -1.8644550787939789\n", + "Expectation of energy: -1.864668903977941\n", + "Expectation of energy: -1.864877783255882\n", + "Expectation of energy: -1.8650819044742026\n", + "Expectation of energy: -1.8652815935319909\n", + "Expectation of energy: -1.8654770365557285\n", + "Expectation of energy: -1.8637852614298054\n", + "Expectation of energy: -1.8640134531211967\n", + "Expectation of energy: -1.86423665530881\n", + "Expectation of energy: -1.8644550787939789\n", + "Expectation of energy: -1.864668903977941\n", + "Expectation of energy: -1.864877783255882\n", + "Expectation of energy: -1.8650819044742026\n", + "Expectation of energy: -1.8652815935319909\n", + "Expectation of energy: -1.8654770365557285\n", + "Expectation of energy: -1.865668398520782\n", + "Expectation of energy: -1.8658550410756\n", + "Expectation of energy: -1.865668398520782\n", + "Expectation of energy: -1.8658550410756\n", + "Expectation of energy: -1.8660378712144976\n", + "Expectation of energy: -1.8662161710872625\n", + "Expectation of energy: -1.8663913278405526\n", + "Expectation of energy: -1.8660378712144976\n", + "Expectation of energy: -1.8662161710872625\n", + "Expectation of energy: -1.8663913278405526\n", + "Expectation of energy: -1.8665618653250922\n", + "Expectation of energy: -1.866728731562075\n", + "Expectation of energy: -1.8668918775945844\n", + "Expectation of energy: -1.867051497369097\n", + "Expectation of energy: -1.8672076458172888\n", + "Expectation of energy: -1.8673599009300559\n", + "Expectation of energy: -1.8675090664205771\n", + "Expectation of energy: -1.8676546919754684\n", + "Expectation of energy: -1.8677970000252653\n", + "Expectation of energy: -1.8665618653250922\n", + "Expectation of energy: -1.866728731562075\n", + "Expectation of energy: -1.8668918775945844\n", + "Expectation of energy: -1.867051497369097\n", + "Expectation of energy: -1.8672076458172888\n", + "Expectation of energy: -1.8673599009300559\n", + "Expectation of energy: -1.8675090664205771\n", + "Expectation of energy: -1.8676546919754684\n", + "Expectation of energy: -1.8677970000252653\n", + "Expectation of energy: -1.8679357842511974\n", + "Expectation of energy: -1.8680718560900003\n", + "Expectation of energy: -1.8679357842511974\n", + "Expectation of energy: -1.8680718560900003\n", + "Expectation of energy: -1.8682047515223363\n", + "Expectation of energy: -1.8683345146286623\n", + "Expectation of energy: -1.8684611076045585\n", + "Expectation of energy: -1.8685851298449208\n", + "Expectation of energy: -1.8682047515223363\n", + "Expectation of energy: -1.8683345146286623\n", + "Expectation of energy: -1.8684611076045585\n", + "Expectation of energy: -1.8685851298449208\n", + "Expectation of energy: -1.8687062495042202\n", + "Expectation of energy: -1.868824442428968\n", + "Expectation of energy: -1.8689399372178694\n", + "Expectation of energy: -1.8690525462783898\n", + "Expectation of energy: -1.8691628219389527\n", + "Expectation of energy: -1.8692703742151409\n", + "Expectation of energy: -1.869375473473455\n", + "Expectation of energy: -1.8694780865391474\n", + "Expectation of energy: -1.8695782927610618\n", + "Expectation of energy: -1.8687062495042202\n", + "Expectation of energy: -1.868824442428968\n", + "Expectation of energy: -1.8689399372178694\n", + "Expectation of energy: -1.8690525462783898\n", + "Expectation of energy: -1.8691628219389527\n", + "Expectation of energy: -1.8692703742151409\n", + "Expectation of energy: -1.869375473473455\n", + "Expectation of energy: -1.8694780865391474\n", + "Expectation of energy: -1.8695782927610618\n", + "Expectation of energy: -1.8696760240676098\n", + "Expectation of energy: -1.8696760240676098\n", + "Expectation of energy: -1.8697716090420857\n", + "Expectation of energy: -1.869864817526579\n", + "Expectation of energy: -1.8699558281855186\n", + "Expectation of energy: -1.8700447034921823\n", + "Expectation of energy: -1.8697716090420857\n", + "Expectation of energy: -1.869864817526579\n", + "Expectation of energy: -1.8699558281855186\n", + "Expectation of energy: -1.8700447034921823\n", + "Expectation of energy: -1.8701313688565075\n", + "Expectation of energy: -1.870215815147725\n", + "Expectation of energy: -1.870298470817683\n", + "Expectation of energy: -1.870378980926447\n", + "Expectation of energy: -1.870457915178128\n", + "Expectation of energy: -1.8705345313494717\n", + "Expectation of energy: -1.8706093969245483\n", + "Expectation of energy: -1.8701313688565075\n", + "Expectation of energy: -1.870215815147725\n", + "Expectation of energy: -1.870298470817683\n", + "Expectation of energy: -1.870378980926447\n", + "Expectation of energy: -1.870457915178128\n", + "Expectation of energy: -1.8705345313494717\n", + "Expectation of energy: -1.8706093969245483\n", + "Expectation of energy: -1.8706823943954392\n", + "Expectation of energy: -1.8707536535284957\n", + "Expectation of energy: -1.8708232351130327\n", + "Expectation of energy: -1.8708908226956122\n", + "Expectation of energy: -1.8706823943954392\n", + "Expectation of energy: -1.8707536535284957\n", + "Expectation of energy: -1.8708232351130327\n", + "Expectation of energy: -1.8708908226956122\n", + "Expectation of energy: -1.8709570104312931\n", + "Expectation of energy: -1.8710214942672634\n", + "Expectation of energy: -1.8710843534690131\n", + "Expectation of energy: -1.8711457159952705\n", + "Expectation of energy: -1.8712054680650492\n", + "Expectation of energy: -1.8712638293674817\n", + "Expectation of energy: -1.8713205628927692\n", + "Expectation of energy: -1.871375871180436\n", + "Expectation of energy: -1.87142998045933\n", + "Expectation of energy: -1.8714826811408232\n", + "Expectation of energy: -1.8709570104312931\n", + "Expectation of energy: -1.8710214942672634\n", + "Expectation of energy: -1.8710843534690131\n", + "Expectation of energy: -1.8711457159952705\n", + "Expectation of energy: -1.8712054680650492\n", + "Expectation of energy: -1.8712638293674817\n", + "Expectation of energy: -1.8713205628927692\n", + "Expectation of energy: -1.871375871180436\n", + "Expectation of energy: -1.87142998045933\n", + "Expectation of energy: -1.8714826811408232\n", + "Expectation of energy: -1.8715339696957307\n", + "Expectation of energy: -1.8715839597481307\n", + "Expectation of energy: -1.8716325356069943\n", + "Expectation of energy: -1.871679881207095\n", + "Expectation of energy: -1.8717261163766357\n", + "Expectation of energy: -1.8715339696957307\n", + "Expectation of energy: -1.8715839597481307\n", + "Expectation of energy: -1.8716325356069943\n", + "Expectation of energy: -1.871679881207095\n", + "Expectation of energy: -1.8717261163766357\n", + "Expectation of energy: -1.8717713235937672\n", + "Expectation of energy: -1.8718153464571055\n", + "Expectation of energy: -1.8718577746058147\n", + "Expectation of energy: -1.8718993998547446\n", + "Expectation of energy: -1.8719400460760316\n", + "Expectation of energy: -1.8719792662163495\n", + "Expectation of energy: -1.8720178623049142\n", + "Expectation of energy: -1.8720552594773132\n", + "Expectation of energy: -1.8717713235937672\n", + "Expectation of energy: -1.8718153464571055\n", + "Expectation of energy: -1.8718577746058147\n", + "Expectation of energy: -1.8718993998547446\n", + "Expectation of energy: -1.8719400460760316\n", + "Expectation of energy: -1.8719792662163495\n", + "Expectation of energy: -1.8720178623049142\n", + "Expectation of energy: -1.8720552594773132\n", + "Expectation of energy: -1.8720919089847887\n", + "Expectation of energy: -1.8721269381482948\n", + "Expectation of energy: -1.8721618716681527\n", + "Expectation of energy: -1.8720919089847887\n", + "Expectation of energy: -1.8721269381482948\n", + "Expectation of energy: -1.8721618716681527\n", + "Expectation of energy: -1.872195185440861\n", + "Expectation of energy: -1.8722278677261794\n", + "Expectation of energy: -1.8722597092462838\n", + "Expectation of energy: -1.8722906975624165\n", + "Expectation of energy: -1.8723208288784416\n", + "Expectation of energy: -1.8723501040031085\n", + "Expectation of energy: -1.872195185440861\n", + "Expectation of energy: -1.8722278677261794\n", + "Expectation of energy: -1.8722597092462838\n", + "Expectation of energy: -1.8722906975624165\n", + "Expectation of energy: -1.8723208288784416\n", + "Expectation of energy: -1.8723501040031085\n", + "Expectation of energy: -1.872378663917379\n", + "Epoch 4, LR: 0.004988904911507701\n", + "Expectation of energy: -1.872378663917379\n", + "Epoch 4, LR: 0.004988904911507701\n", + "Expectation of energy: -1.872378663917379\n", + "Expectation of energy: -1.8724061803904382\n", + "Expectation of energy: -1.872378663917379\n", + "Expectation of energy: -1.8724061803904382\n", + "Expectation of energy: -1.8724333405013893\n", + "Expectation of energy: -1.872459487453013\n", + "Expectation of energy: -1.8724848674923742\n", + "Expectation of energy: -1.8725097794152847\n", + "Expectation of energy: -1.8725341451951172\n", + "Expectation of energy: -1.872557436408779\n", + "Expectation of energy: -1.8725799223484396\n", + "Expectation of energy: -1.8726022865168925\n", + "Expectation of energy: -1.8726238771576382\n", + "Expectation of energy: -1.8724333405013893\n", + "Expectation of energy: -1.872459487453013\n", + "Expectation of energy: -1.8724848674923742\n", + "Expectation of energy: -1.8725097794152847\n", + "Expectation of energy: -1.8725341451951172\n", + "Expectation of energy: -1.872557436408779\n", + "Expectation of energy: -1.8725799223484396\n", + "Expectation of energy: -1.8726022865168925\n", + "Expectation of energy: -1.8726238771576382\n", + "Expectation of energy: -1.8726446667054095\n", + "Expectation of energy: -1.8726446667054095\n", + "Expectation of energy: -1.8726650539934537\n", + "Expectation of energy: -1.872684799395599\n", + "Expectation of energy: -1.8726650539934537\n", + "Expectation of energy: -1.872684799395599\n", + "Expectation of energy: -1.8727041754095848\n", + "Expectation of energy: -1.8727226433008515\n", + "Expectation of energy: -1.8727408464258444\n", + "Expectation of energy: -1.8727584913858997\n", + "Expectation of energy: -1.8727756200964547\n", + "Expectation of energy: -1.872792365972355\n", + "Expectation of energy: -1.8728083799896003\n", + "Expectation of energy: -1.8728238550417058\n", + "Expectation of energy: -1.8728395143225112\n", + "Expectation of energy: -1.8728541761329929\n", + "Expectation of energy: -1.872868595059433\n", + "Expectation of energy: -1.8727041754095848\n", + "Expectation of energy: -1.8727226433008515\n", + "Expectation of energy: -1.8727408464258444\n", + "Expectation of energy: -1.8727584913858997\n", + "Expectation of energy: -1.8727756200964547\n", + "Expectation of energy: -1.872792365972355\n", + "Expectation of energy: -1.8728083799896003\n", + "Expectation of energy: -1.8728238550417058\n", + "Expectation of energy: -1.8728395143225112\n", + "Expectation of energy: -1.8728541761329929\n", + "Expectation of energy: -1.872868595059433\n", + "Expectation of energy: -1.8728824459755373\n", + "Expectation of energy: -1.8728960629623947\n", + "Expectation of energy: -1.8728824459755373\n", + "Expectation of energy: -1.8728960629623947\n", + "Expectation of energy: -1.8729093151452703\n", + "Expectation of energy: -1.8729220083940894\n", + "Expectation of energy: -1.8729345686090177\n", + "Expectation of energy: -1.8729093151452703\n", + "Expectation of energy: -1.8729220083940894\n", + "Expectation of energy: -1.8729345686090177\n", + "Expectation of energy: -1.8729463340940375\n", + "Expectation of energy: -1.8729580351661774\n", + "Expectation of energy: -1.8729694383027446\n", + "Expectation of energy: -1.872980255750511\n", + "Expectation of energy: -1.8729909084031056\n", + "Expectation of energy: -1.8730014272033906\n", + "Expectation of energy: -1.8730111694021598\n", + "Expectation of energy: -1.8730210770995745\n", + "Expectation of energy: -1.8730305890803793\n", + "Expectation of energy: -1.8729463340940375\n", + "Expectation of energy: -1.8729580351661774\n", + "Expectation of energy: -1.8729694383027446\n", + "Expectation of energy: -1.872980255750511\n", + "Expectation of energy: -1.8729909084031056\n", + "Expectation of energy: -1.8730014272033906\n", + "Expectation of energy: -1.8730111694021598\n", + "Expectation of energy: -1.8730210770995745\n", + "Expectation of energy: -1.8730305890803793\n", + "Expectation of energy: -1.8730398010881455\n", + "Expectation of energy: -1.8730398010881455\n", + "Expectation of energy: -1.8730485311272242\n", + "Expectation of energy: -1.8730570200845107\n", + "Expectation of energy: -1.873065414857302\n", + "Expectation of energy: -1.8730485311272242\n", + "Expectation of energy: -1.8730570200845107\n", + "Expectation of energy: -1.873065414857302\n", + "Expectation of energy: -1.8730734857414342\n", + "Expectation of energy: -1.8730817190257598\n", + "Expectation of energy: -1.8730890645319818\n", + "Expectation of energy: -1.8730965363398013\n", + "Expectation of energy: -1.873103551350709\n", + "Expectation of energy: -1.8731105197653037\n", + "Expectation of energy: -1.8731169951090372\n", + "Expectation of energy: -1.8731236863765475\n", + "Expectation of energy: -1.8731298527604396\n", + "Expectation of energy: -1.8731359611181515\n", + "Expectation of energy: -1.8730734857414342\n", + "Expectation of energy: -1.8730817190257598\n", + "Expectation of energy: -1.8730890645319818\n", + "Expectation of energy: -1.8730965363398013\n", + "Expectation of energy: -1.873103551350709\n", + "Expectation of energy: -1.8731105197653037\n", + "Expectation of energy: -1.8731169951090372\n", + "Expectation of energy: -1.8731236863765475\n", + "Expectation of energy: -1.8731298527604396\n", + "Expectation of energy: -1.8731359611181515\n", + "Expectation of energy: -1.8731420765421298\n", + "Expectation of energy: -1.8731420765421298\n", + "Expectation of energy: -1.8731475838067586\n", + "Expectation of energy: -1.8731530469908013\n", + "Expectation of energy: -1.8731475838067586\n", + "Expectation of energy: -1.8731530469908013\n", + "Expectation of energy: -1.8731584871457492\n", + "Expectation of energy: -1.87316357956895\n", + "Expectation of energy: -1.8731688839839395\n", + "Expectation of energy: -1.8731738877297062\n", + "Expectation of energy: -1.873178685014652\n", + "Expectation of energy: -1.8731826132588991\n", + "Expectation of energy: -1.873187598891294\n", + "Expectation of energy: -1.8731917589404703\n", + "Expectation of energy: -1.873195896053813\n", + "Expectation of energy: -1.8732000105386892\n", + "Expectation of energy: -1.8731584871457492\n", + "Expectation of energy: -1.87316357956895\n", + "Expectation of energy: -1.8731688839839395\n", + "Expectation of energy: -1.8731738877297062\n", + "Expectation of energy: -1.873178685014652\n", + "Expectation of energy: -1.8731826132588991\n", + "Expectation of energy: -1.873187598891294\n", + "Expectation of energy: -1.8731917589404703\n", + "Expectation of energy: -1.873195896053813\n", + "Expectation of energy: -1.8732000105386892\n", + "Expectation of energy: -1.873203708680606\n", + "Expectation of energy: -1.873203708680606\n", + "Expectation of energy: -1.8732075993582198\n", + "Expectation of energy: -1.8732112366370075\n", + "Expectation of energy: -1.8732148172691372\n", + "Expectation of energy: -1.8732181937217403\n", + "Expectation of energy: -1.8732075993582198\n", + "Expectation of energy: -1.8732112366370075\n", + "Expectation of energy: -1.8732148172691372\n", + "Expectation of energy: -1.8732181937217403\n", + "Expectation of energy: -1.8732214116872157\n", + "Expectation of energy: -1.8732247368652832\n", + "Expectation of energy: -1.8732275567914165\n", + "Expectation of energy: -1.8732309229132826\n", + "Expectation of energy: -1.8732334623730307\n", + "Expectation of energy: -1.873236493164608\n", + "Expectation of energy: -1.8732391841472202\n", + "Expectation of energy: -1.8732415825292608\n", + "Expectation of energy: -1.8732442465260855\n", + "Expectation of energy: -1.8732467126855632\n", + "Expectation of energy: -1.8732214116872157\n", + "Expectation of energy: -1.8732247368652832\n", + "Expectation of energy: -1.8732275567914165\n", + "Expectation of energy: -1.8732309229132826\n", + "Expectation of energy: -1.8732334623730307\n", + "Expectation of energy: -1.873236493164608\n", + "Expectation of energy: -1.8732391841472202\n", + "Expectation of energy: -1.8732415825292608\n", + "Expectation of energy: -1.8732442465260855\n", + "Expectation of energy: -1.8732467126855632\n", + "Expectation of energy: -1.8732488592826837\n", + "Expectation of energy: -1.873251437580944\n", + "Expectation of energy: -1.873253422734999\n", + "Expectation of energy: -1.8732488592826837\n", + "Expectation of energy: -1.873251437580944\n", + "Expectation of energy: -1.873253422734999\n", + "Expectation of energy: -1.873255594079776\n", + "Expectation of energy: -1.8732575606349917\n", + "Expectation of energy: -1.8732597362062904\n", + "Expectation of energy: -1.873261655059735\n", + "Expectation of energy: -1.8732633399217042\n", + "Expectation of energy: -1.873265084784756\n", + "Expectation of energy: -1.873266740396854\n", + "Expectation of energy: -1.8732687681705698\n", + "Expectation of energy: -1.8732704616022866\n", + "Expectation of energy: -1.8732721124079672\n", + "Expectation of energy: -1.8732732553616105\n", + "Expectation of energy: -1.873255594079776\n", + "Expectation of energy: -1.8732575606349917\n", + "Expectation of energy: -1.8732597362062904\n", + "Expectation of energy: -1.873261655059735\n", + "Expectation of energy: -1.8732633399217042\n", + "Expectation of energy: -1.873265084784756\n", + "Expectation of energy: -1.873266740396854\n", + "Expectation of energy: -1.8732687681705698\n", + "Expectation of energy: -1.8732704616022866\n", + "Expectation of energy: -1.8732721124079672\n", + "Expectation of energy: -1.8732732553616105\n", + "Expectation of energy: -1.8732749121841021\n", + "Expectation of energy: -1.8732764030509896\n", + "Expectation of energy: -1.8732749121841021\n", + "Expectation of energy: -1.8732764030509896\n", + "Expectation of energy: -1.8732777720696507\n", + "Expectation of energy: -1.8732787045515908\n", + "Expectation of energy: -1.8732801969047492\n", + "Expectation of energy: -1.8732777720696507\n", + "Expectation of energy: -1.8732787045515908\n", + "Expectation of energy: -1.8732801969047492\n", + "Expectation of energy: -1.873281420813239\n", + "Epoch 5, LR: 0.004980286753286196\n", + "Expectation of energy: -1.873281420813239\n", + "Epoch 5, LR: 0.004980286753286196\n", + "Expectation of energy: -1.873281420813239\n", + "Expectation of energy: -1.873282863513119\n", + "Expectation of energy: -1.8732838161199843\n", + "Expectation of energy: -1.8732850088614692\n", + "Expectation of energy: -1.8732859786253688\n", + "Expectation of energy: -1.873281420813239\n", + "Expectation of energy: -1.873282863513119\n", + "Expectation of energy: -1.8732838161199843\n", + "Expectation of energy: -1.8732850088614692\n", + "Expectation of energy: -1.8732859786253688\n", + "Expectation of energy: -1.8732870168098594\n", + "Expectation of energy: -1.8732870168098594\n", + "Expectation of energy: -1.8732882682401188\n", + "Expectation of energy: -1.8732890278568926\n", + "Expectation of energy: -1.8732899995003878\n", + "Expectation of energy: -1.873290892018582\n", + "Expectation of energy: -1.8732882682401188\n", + "Expectation of energy: -1.8732890278568926\n", + "Expectation of energy: -1.8732899995003878\n", + "Expectation of energy: -1.873290892018582\n", + "Expectation of energy: -1.873291532139776\n", + "Expectation of energy: -1.8732924807726898\n", + "Expectation of energy: -1.873291532139776\n", + "Expectation of energy: -1.8732924807726898\n", + "Expectation of energy: -1.8732933229955222\n", + "Expectation of energy: -1.8732942781578814\n", + "Expectation of energy: -1.873295052604531\n", + "Expectation of energy: -1.8732956711678317\n", + "Expectation of energy: -1.8732963754060659\n", + "Expectation of energy: -1.8732933229955222\n", + "Expectation of energy: -1.8732942781578814\n", + "Expectation of energy: -1.873295052604531\n", + "Expectation of energy: -1.8732956711678317\n", + "Expectation of energy: -1.8732963754060659\n", + "Expectation of energy: -1.8732967278358073\n", + "Expectation of energy: -1.8732976531511447\n", + "Expectation of energy: -1.8732967278358073\n", + "Expectation of energy: -1.8732976531511447\n", + "Expectation of energy: -1.873298274303338\n", + "Expectation of energy: -1.87329856453052\n", + "Expectation of energy: -1.8732995252535707\n", + "Expectation of energy: -1.8733000337856964\n", + "Expectation of energy: -1.8733006019107423\n", + "Expectation of energy: -1.873298274303338\n", + "Expectation of energy: -1.87329856453052\n", + "Expectation of energy: -1.8732995252535707\n", + "Expectation of energy: -1.8733000337856964\n", + "Expectation of energy: -1.8733006019107423\n", + "Expectation of energy: -1.8733010332991697\n", + "Expectation of energy: -1.8733015725966018\n", + "Expectation of energy: -1.8733010332991697\n", + "Expectation of energy: -1.8733015725966018\n", + "Expectation of energy: -1.8733022673774062\n", + "Expectation of energy: -1.8733026300148041\n", + "Expectation of energy: -1.8733030749825712\n", + "Expectation of energy: -1.8733022673774062\n", + "Expectation of energy: -1.8733026300148041\n", + "Expectation of energy: -1.8733030749825712\n" + ] + }, + { + "ename": "KeyboardInterrupt", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mKeyboardInterrupt\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[12]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mmain\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[11]\u001b[39m\u001b[32m, line 69\u001b[39m, in \u001b[36mmain\u001b[39m\u001b[34m()\u001b[39m\n\u001b[32m 66\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m epoch \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mrange\u001b[39m(\u001b[32m1\u001b[39m, n_epochs + \u001b[32m1\u001b[39m):\n\u001b[32m 67\u001b[39m \u001b[38;5;66;03m# train\u001b[39;00m\n\u001b[32m 68\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mEpoch \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mepoch\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m, LR: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00moptimizer.param_groups[\u001b[32m0\u001b[39m][\u001b[33m'\u001b[39m\u001b[33mlr\u001b[39m\u001b[33m'\u001b[39m]\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n\u001b[32m---> \u001b[39m\u001b[32m69\u001b[39m \u001b[43mtrain\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdataflow\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mq_device\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mmodel\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mdevice\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43moptimizer\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 71\u001b[39m \u001b[38;5;66;03m# valid\u001b[39;00m\n\u001b[32m 72\u001b[39m valid_test(dataflow, q_device, \u001b[33m'\u001b[39m\u001b[33mvalid\u001b[39m\u001b[33m'\u001b[39m, model, device)\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[10]\u001b[39m\u001b[32m, line 60\u001b[39m, in \u001b[36mtrain\u001b[39m\u001b[34m(dataflow, q_device, model, device, optimizer)\u001b[39m\n\u001b[32m 58\u001b[39m optimizer.zero_grad()\n\u001b[32m 59\u001b[39m loss.backward()\n\u001b[32m---> \u001b[39m\u001b[32m60\u001b[39m \u001b[43moptimizer\u001b[49m\u001b[43m.\u001b[49m\u001b[43mstep\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 61\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[33mExpectation of energy: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mloss.item()\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m\"\u001b[39m)\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/tqcuquantum/lib/python3.11/site-packages/torch/optim/lr_scheduler.py:124\u001b[39m, in \u001b[36mLRScheduler.__init__..patch_track_step_called..wrap_step..wrapper\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m 122\u001b[39m opt = opt_ref()\n\u001b[32m 123\u001b[39m opt._opt_called = \u001b[38;5;28;01mTrue\u001b[39;00m \u001b[38;5;66;03m# type: ignore[union-attr]\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m124\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m.\u001b[49m\u001b[34;43m__get__\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mopt\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mopt\u001b[49m\u001b[43m.\u001b[49m\u001b[34;43m__class__\u001b[39;49m\u001b[43m)\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/tqcuquantum/lib/python3.11/site-packages/torch/optim/optimizer.py:485\u001b[39m, in \u001b[36mOptimizer.profile_hook_step..wrapper\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m 480\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 481\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m(\n\u001b[32m 482\u001b[39m \u001b[33mf\u001b[39m\u001b[33m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mfunc\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m must return None or a tuple of (new_args, new_kwargs), but got \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mresult\u001b[38;5;132;01m}\u001b[39;00m\u001b[33m.\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 483\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m485\u001b[39m out = \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 486\u001b[39m \u001b[38;5;28mself\u001b[39m._optimizer_step_code()\n\u001b[32m 488\u001b[39m \u001b[38;5;66;03m# call optimizer step post hooks\u001b[39;00m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/tqcuquantum/lib/python3.11/site-packages/torch/optim/optimizer.py:79\u001b[39m, in \u001b[36m_use_grad_for_differentiable.._use_grad\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 77\u001b[39m torch.set_grad_enabled(\u001b[38;5;28mself\u001b[39m.defaults[\u001b[33m\"\u001b[39m\u001b[33mdifferentiable\u001b[39m\u001b[33m\"\u001b[39m])\n\u001b[32m 78\u001b[39m torch._dynamo.graph_break()\n\u001b[32m---> \u001b[39m\u001b[32m79\u001b[39m ret = \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 80\u001b[39m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[32m 81\u001b[39m torch._dynamo.graph_break()\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/tqcuquantum/lib/python3.11/site-packages/torch/optim/adam.py:246\u001b[39m, in \u001b[36mAdam.step\u001b[39m\u001b[34m(self, closure)\u001b[39m\n\u001b[32m 234\u001b[39m beta1, beta2 = group[\u001b[33m\"\u001b[39m\u001b[33mbetas\u001b[39m\u001b[33m\"\u001b[39m]\n\u001b[32m 236\u001b[39m has_complex = \u001b[38;5;28mself\u001b[39m._init_group(\n\u001b[32m 237\u001b[39m group,\n\u001b[32m 238\u001b[39m params_with_grad,\n\u001b[32m (...)\u001b[39m\u001b[32m 243\u001b[39m state_steps,\n\u001b[32m 244\u001b[39m )\n\u001b[32m--> \u001b[39m\u001b[32m246\u001b[39m \u001b[43madam\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 247\u001b[39m \u001b[43m \u001b[49m\u001b[43mparams_with_grad\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 248\u001b[39m \u001b[43m \u001b[49m\u001b[43mgrads\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 249\u001b[39m \u001b[43m \u001b[49m\u001b[43mexp_avgs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 250\u001b[39m \u001b[43m \u001b[49m\u001b[43mexp_avg_sqs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 251\u001b[39m \u001b[43m \u001b[49m\u001b[43mmax_exp_avg_sqs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 252\u001b[39m \u001b[43m \u001b[49m\u001b[43mstate_steps\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 253\u001b[39m \u001b[43m \u001b[49m\u001b[43mamsgrad\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mamsgrad\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 254\u001b[39m \u001b[43m \u001b[49m\u001b[43mhas_complex\u001b[49m\u001b[43m=\u001b[49m\u001b[43mhas_complex\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 255\u001b[39m \u001b[43m \u001b[49m\u001b[43mbeta1\u001b[49m\u001b[43m=\u001b[49m\u001b[43mbeta1\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 256\u001b[39m \u001b[43m \u001b[49m\u001b[43mbeta2\u001b[49m\u001b[43m=\u001b[49m\u001b[43mbeta2\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 257\u001b[39m \u001b[43m \u001b[49m\u001b[43mlr\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mlr\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 258\u001b[39m \u001b[43m \u001b[49m\u001b[43mweight_decay\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mweight_decay\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 259\u001b[39m \u001b[43m \u001b[49m\u001b[43meps\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43meps\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 260\u001b[39m \u001b[43m \u001b[49m\u001b[43mmaximize\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmaximize\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 261\u001b[39m \u001b[43m \u001b[49m\u001b[43mforeach\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mforeach\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 262\u001b[39m \u001b[43m \u001b[49m\u001b[43mcapturable\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mcapturable\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 263\u001b[39m \u001b[43m \u001b[49m\u001b[43mdifferentiable\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mdifferentiable\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 264\u001b[39m \u001b[43m \u001b[49m\u001b[43mfused\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mfused\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 265\u001b[39m \u001b[43m \u001b[49m\u001b[43mgrad_scale\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mgetattr\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mgrad_scale\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 266\u001b[39m \u001b[43m \u001b[49m\u001b[43mfound_inf\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43mgetattr\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mfound_inf\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 267\u001b[39m \u001b[43m \u001b[49m\u001b[43mdecoupled_weight_decay\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgroup\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mdecoupled_weight_decay\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 268\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 270\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m loss\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/tqcuquantum/lib/python3.11/site-packages/torch/optim/optimizer.py:147\u001b[39m, in \u001b[36m_disable_dynamo_if_unsupported..wrapper..maybe_fallback\u001b[39m\u001b[34m(*args, **kwargs)\u001b[39m\n\u001b[32m 145\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m disabled_func(*args, **kwargs)\n\u001b[32m 146\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m147\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43m*\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/tqcuquantum/lib/python3.11/site-packages/torch/optim/adam.py:933\u001b[39m, in \u001b[36madam\u001b[39m\u001b[34m(params, grads, exp_avgs, exp_avg_sqs, max_exp_avg_sqs, state_steps, foreach, capturable, differentiable, fused, grad_scale, found_inf, has_complex, decoupled_weight_decay, amsgrad, beta1, beta2, lr, weight_decay, eps, maximize)\u001b[39m\n\u001b[32m 930\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 931\u001b[39m func = _single_tensor_adam\n\u001b[32m--> \u001b[39m\u001b[32m933\u001b[39m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 934\u001b[39m \u001b[43m \u001b[49m\u001b[43mparams\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 935\u001b[39m \u001b[43m \u001b[49m\u001b[43mgrads\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 936\u001b[39m \u001b[43m \u001b[49m\u001b[43mexp_avgs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 937\u001b[39m \u001b[43m \u001b[49m\u001b[43mexp_avg_sqs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 938\u001b[39m \u001b[43m \u001b[49m\u001b[43mmax_exp_avg_sqs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 939\u001b[39m \u001b[43m \u001b[49m\u001b[43mstate_steps\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 940\u001b[39m \u001b[43m \u001b[49m\u001b[43mamsgrad\u001b[49m\u001b[43m=\u001b[49m\u001b[43mamsgrad\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 941\u001b[39m \u001b[43m \u001b[49m\u001b[43mhas_complex\u001b[49m\u001b[43m=\u001b[49m\u001b[43mhas_complex\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 942\u001b[39m \u001b[43m \u001b[49m\u001b[43mbeta1\u001b[49m\u001b[43m=\u001b[49m\u001b[43mbeta1\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 943\u001b[39m \u001b[43m \u001b[49m\u001b[43mbeta2\u001b[49m\u001b[43m=\u001b[49m\u001b[43mbeta2\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 944\u001b[39m \u001b[43m \u001b[49m\u001b[43mlr\u001b[49m\u001b[43m=\u001b[49m\u001b[43mlr\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 945\u001b[39m \u001b[43m \u001b[49m\u001b[43mweight_decay\u001b[49m\u001b[43m=\u001b[49m\u001b[43mweight_decay\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 946\u001b[39m \u001b[43m \u001b[49m\u001b[43meps\u001b[49m\u001b[43m=\u001b[49m\u001b[43meps\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 947\u001b[39m \u001b[43m \u001b[49m\u001b[43mmaximize\u001b[49m\u001b[43m=\u001b[49m\u001b[43mmaximize\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 948\u001b[39m \u001b[43m \u001b[49m\u001b[43mcapturable\u001b[49m\u001b[43m=\u001b[49m\u001b[43mcapturable\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 949\u001b[39m \u001b[43m \u001b[49m\u001b[43mdifferentiable\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdifferentiable\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 950\u001b[39m \u001b[43m \u001b[49m\u001b[43mgrad_scale\u001b[49m\u001b[43m=\u001b[49m\u001b[43mgrad_scale\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 951\u001b[39m \u001b[43m \u001b[49m\u001b[43mfound_inf\u001b[49m\u001b[43m=\u001b[49m\u001b[43mfound_inf\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 952\u001b[39m \u001b[43m \u001b[49m\u001b[43mdecoupled_weight_decay\u001b[49m\u001b[43m=\u001b[49m\u001b[43mdecoupled_weight_decay\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 953\u001b[39m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/tqcuquantum/lib/python3.11/site-packages/torch/optim/adam.py:738\u001b[39m, in \u001b[36m_multi_tensor_adam\u001b[39m\u001b[34m(params, grads, exp_avgs, exp_avg_sqs, max_exp_avg_sqs, state_steps, grad_scale, found_inf, amsgrad, has_complex, beta1, beta2, lr, weight_decay, eps, maximize, capturable, differentiable, decoupled_weight_decay)\u001b[39m\n\u001b[32m 736\u001b[39m torch._foreach_addcdiv_(device_params, device_exp_avgs, exp_avg_sq_sqrt)\n\u001b[32m 737\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m738\u001b[39m bias_correction1 = \u001b[43m[\u001b[49m\n\u001b[32m 739\u001b[39m \u001b[43m \u001b[49m\u001b[32;43m1\u001b[39;49m\u001b[43m \u001b[49m\u001b[43m-\u001b[49m\u001b[43m \u001b[49m\u001b[43mbeta1\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[43m \u001b[49m\u001b[43m_get_value\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstep\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mfor\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mstep\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;129;43;01min\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mdevice_state_steps\u001b[49m\n\u001b[32m 740\u001b[39m \u001b[43m \u001b[49m\u001b[43m]\u001b[49m\n\u001b[32m 741\u001b[39m bias_correction2 = [\n\u001b[32m 742\u001b[39m \u001b[32m1\u001b[39m - beta2 ** _get_value(step) \u001b[38;5;28;01mfor\u001b[39;00m step \u001b[38;5;129;01min\u001b[39;00m device_state_steps\n\u001b[32m 743\u001b[39m ]\n\u001b[32m 745\u001b[39m step_size = _stack_if_compiling([(lr / bc) * -\u001b[32m1\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m bc \u001b[38;5;129;01min\u001b[39;00m bias_correction1])\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/miniconda3/envs/tqcuquantum/lib/python3.11/site-packages/torch/optim/adam.py:738\u001b[39m, in \u001b[36m\u001b[39m\u001b[34m(.0)\u001b[39m\n\u001b[32m 736\u001b[39m torch._foreach_addcdiv_(device_params, device_exp_avgs, exp_avg_sq_sqrt)\n\u001b[32m 737\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m738\u001b[39m bias_correction1 = [\n\u001b[32m 739\u001b[39m \u001b[32m1\u001b[39m - beta1 ** _get_value(step) \u001b[38;5;28;01mfor\u001b[39;00m step \u001b[38;5;129;01min\u001b[39;00m device_state_steps\n\u001b[32m 740\u001b[39m ]\n\u001b[32m 741\u001b[39m bias_correction2 = [\n\u001b[32m 742\u001b[39m \u001b[32m1\u001b[39m - beta2 ** _get_value(step) \u001b[38;5;28;01mfor\u001b[39;00m step \u001b[38;5;129;01min\u001b[39;00m device_state_steps\n\u001b[32m 743\u001b[39m ]\n\u001b[32m 745\u001b[39m step_size = _stack_if_compiling([(lr / bc) * -\u001b[32m1\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m bc \u001b[38;5;129;01min\u001b[39;00m bias_correction1])\n", + "\u001b[31mKeyboardInterrupt\u001b[39m: " + ] + } + ], + "source": [ + "main()" + ] + }, + { + "cell_type": "markdown", + "id": "f5348088", + "metadata": {}, + "source": [ + "## 4. QNN circuit" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a4b5e88a", + "metadata": {}, + "outputs": [], + "source": [ + "import torch\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "from torchquantum.plugin.cuquantum import *\n", + "from torchquantum.operator.standard_gates import *\n", + "import torchquantum as tq\n", + "import torchquantum.functional as tqf\n", + "import math, random\n", + "import torch\n", + "from torchquantum.plugin.cuquantum import ParameterizedQuantumCircuit, CuTensorNetworkBackend, TNConfig, QuantumSampling\n", + "from torchquantum.operator.standard_gates import RX, RY, RZ, Hadamard, CNOT" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "763f79c0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Step 000 Expect=+0.719480 Loss=0.517651\n", + "Step 005 Expect=+0.639888 Loss=0.409456\n", + "Step 010 Expect=+0.549973 Loss=0.302470\n", + "Step 015 Expect=+0.450998 Loss=0.203400\n", + "Step 010 Expect=+0.549973 Loss=0.302470\n", + "Step 015 Expect=+0.450998 Loss=0.203400\n", + "Step 020 Expect=+0.346365 Loss=0.119968\n", + "Step 025 Expect=+0.241620 Loss=0.058380\n", + "Validation average loss: 0.020756\n", + "Step 020 Expect=+0.346365 Loss=0.119968\n", + "Step 025 Expect=+0.241620 Loss=0.058380\n", + "Validation average loss: 0.020756\n" + ] + }, + { + "data": { + "text/plain": [ + "0.02075638808310032" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "class QFCModel(tq.QuantumModule):\n", + " class QLayer(tq.QuantumModule):\n", + " def __init__(self):\n", + " super().__init__()\n", + " self.n_wires = 4\n", + "\n", + " # Define circuit and trainable params\n", + " self.circuit = ParameterizedQuantumCircuit(\n", + " n_wires=self.n_wires,\n", + " n_input_params=0,\n", + " n_trainable_params=18,\n", + " )\n", + " initial_params = torch.randn(18, requires_grad=True)\n", + " self.circuit.set_trainable_params(initial_params)\n", + "\n", + " # Circuit structure (example)\n", + " self.circuit.append_gate(U3, wires=0, trainable_idx=[0, 1, 2]) # u3_0\n", + " self.circuit.append_gate(U3, wires=1, trainable_idx=[3, 4, 5]) # u3_1\n", + " self.circuit.append_gate(CU3, wires=[1, 2], trainable_idx=[6, 7, 8]) # cu3_0\n", + " self.circuit.append_gate(U3, wires=1, trainable_idx=[9, 10, 11]) # u3_2\n", + " self.circuit.append_gate(U3, wires=2, trainable_idx=[12, 13, 14]) # u3_3\n", + " self.circuit.append_gate(CU3, wires=[2, 3], trainable_idx=[15, 16, 17]) # cu3_1\n", + "\n", + " # Backend + expectation: measure Z on last qubit (wire 3) => 'IIIZ' (string length = n_wires)\n", + " self.backend = CuTensorNetworkBackend(TNConfig(num_hyper_samples=1))\n", + " # Observable dictionary: each term is a dict mapping Pauli string to coefficient\n", + " self.expect = QuantumExpectation(self.circuit, self.backend, [{\"IIIZ\": 1.0}])\n", + "\n", + " def forward(self, x=None):\n", + " \"\"\"Forward pass.\n", + " x (optional): ignored for now; placeholder for future data encoding.\n", + " Returns a scalar expectation value tensor (shape [1]).\n", + " \"\"\"\n", + " out = self.expect() # tensor of shape [1]\n", + " return out\n", + "\n", + " def __init__(self):\n", + " super().__init__()\n", + " self.layer = self.QLayer()\n", + "\n", + " def forward(self, x=None):\n", + " return self.layer(x)\n", + "\n", + "\n", + "def train(dataflow, model, device, optimizer, steps=100, log_every=10):\n", + " \"\"\"Minimal training loop.\n", + " If dataflow is None, uses dummy iterations optimizing expectation -> 0.\n", + " Loss: mean(out^2) so we try to drive expectation toward 0.\n", + " \"\"\"\n", + " model.train()\n", + " it = 0\n", + " if dataflow is None:\n", + " while it < steps:\n", + " optimizer.zero_grad()\n", + " out = model() # shape [1]\n", + " loss = (out ** 2).mean()\n", + " loss.backward()\n", + " optimizer.step()\n", + " if it % log_every == 0:\n", + " print(f\"Step {it:03d} Expect={out.item():+.6f} Loss={loss.item():.6f}\")\n", + " it += 1\n", + " else:\n", + " for batch in dataflow:\n", + " if it >= steps:\n", + " break\n", + " optimizer.zero_grad()\n", + " # Expect batch can be (x,y) or just x; handle flexibly\n", + " if isinstance(batch, (list, tuple)) and len(batch) > 1:\n", + " x, y = batch[0], batch[1]\n", + " out = model(x.to(device))\n", + " # If y is scalar/1-dim target: MSE; otherwise reduce\n", + " y = y.to(device).view_as(out)\n", + " loss = ((out - y) ** 2).mean()\n", + " else:\n", + " x = batch[0] if isinstance(batch, (list, tuple)) else batch\n", + " out = model(x.to(device) if torch.is_tensor(x) else None)\n", + " loss = (out ** 2).mean()\n", + " loss.backward()\n", + " optimizer.step()\n", + " if it % log_every == 0:\n", + " print(f\"Step {it:03d} Expect={out.item():+.6f} Loss={loss.item():.6f}\")\n", + " it += 1\n", + " return model\n", + "\n", + "\n", + "def valid_test(dataflow, model, device):\n", + " model.eval()\n", + " losses = []\n", + " with torch.no_grad():\n", + " if dataflow is None:\n", + " out = model()\n", + " loss = (out ** 2).mean()\n", + " losses.append(loss.item())\n", + " else:\n", + " for batch in dataflow:\n", + " if isinstance(batch, (list, tuple)) and len(batch) > 1:\n", + " x, y = batch[0], batch[1]\n", + " out = model(x.to(device))\n", + " y = y.to(device).view_as(out)\n", + " loss = ((out - y) ** 2).mean()\n", + " else:\n", + " x = batch[0] if isinstance(batch, (list, tuple)) else batch\n", + " out = model(x.to(device) if torch.is_tensor(x) else None)\n", + " loss = (out ** 2).mean()\n", + " losses.append(loss.item())\n", + " avg = sum(losses) / max(1, len(losses))\n", + " print(f\"Validation average loss: {avg:.6f}\")\n", + " return avg\n", + "\n", + "# --- Quick demo (dummy training) ---\n", + "model = QFCModel()\n", + "optimizer = torch.optim.Adam([model.layer.circuit.trainable_params], lr=0.02)\n", + "train(None, model, device=torch.device('cpu'), optimizer=optimizer, steps=30, log_every=5)\n", + "valid_test(None, model, device=torch.device('cpu'))" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "tqcuquantum", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/torchquantum/__init__.py b/torchquantum/__init__.py index 28220c8a..159ca31f 100644 --- a/torchquantum/__init__.py +++ b/torchquantum/__init__.py @@ -35,30 +35,36 @@ from .layer import * from .encoding import * from .util import * -from .noise_model import * +# Note: noise_model requires qiskit and is not imported by default +# from .noise_model import * from .algorithm import * from .dataset import * from .pulse import * # here we check whether the Qiskit parameterization bug is fixed, if not, a # warning message will be printed -import qiskit -import os +# This is only done if qiskit is available +try: + import qiskit + import os -path = os.path.abspath(qiskit.__file__) -# print(path) -# path for aer provider -path_provider = path.replace("__init__.py", "providers/aer/backends/aerbackend.py") -# print(path_provider) + path = os.path.abspath(qiskit.__file__) + # print(path) + # path for aer provider + path_provider = path.replace("__init__.py", "providers/aer/backends/aerbackend.py") + # print(path_provider) -# with open(path_provider, 'r') as fid: -# for line in fid.readlines(): -# if 'FIXED' in line: -# # print('The qiskit parameterization bug is already fixed!') -# break -# else: -# print(f'\n\n WARNING: The qiskit parameterization bug is not ' -# f'fixed!\n\n' -# f'run python fix_qiskit_parameterization.py to fix it!' -# ) -# break + # with open(path_provider, 'r') as fid: + # for line in fid.readlines(): + # if 'FIXED' in line: + # # print('The qiskit parameterization bug is already fixed!') + # break + # else: + # print(f'\n\n WARNING: The qiskit parameterization bug is not ' + # f'fixed!\n\n' + # f'run python fix_qiskit_parameterization.py to fix it!' + # ) + # break +except ImportError: + # qiskit not available, skip the check + pass diff --git a/torchquantum/layer/entanglement/entanglement.py b/torchquantum/layer/entanglement/entanglement.py index 9f1a6cfd..b616af31 100644 --- a/torchquantum/layer/entanglement/entanglement.py +++ b/torchquantum/layer/entanglement/entanglement.py @@ -35,7 +35,7 @@ from .op2_layer import Op2QAllLayer from typing import Iterable -from torchquantum.plugin.qiskit import QISKIT_INCOMPATIBLE_FUNC_NAMES +from torchquantum.util.constants import QISKIT_INCOMPATIBLE_FUNC_NAMES from torchpack.utils.logging import logger __all__ = [ diff --git a/torchquantum/layer/entanglement/op2_layer.py b/torchquantum/layer/entanglement/op2_layer.py index edf120dc..802d2f52 100644 --- a/torchquantum/layer/entanglement/op2_layer.py +++ b/torchquantum/layer/entanglement/op2_layer.py @@ -30,7 +30,7 @@ from typing import Iterable -from torchquantum.plugin.qiskit import QISKIT_INCOMPATIBLE_FUNC_NAMES +from torchquantum.util.constants import QISKIT_INCOMPATIBLE_FUNC_NAMES from torchpack.utils.logging import logger __all__ = [ diff --git a/torchquantum/layer/layers/cx_layer.py b/torchquantum/layer/layers/cx_layer.py index 320d600d..0cf767df 100644 --- a/torchquantum/layer/layers/cx_layer.py +++ b/torchquantum/layer/layers/cx_layer.py @@ -29,7 +29,7 @@ import numpy as np from typing import Iterable -from torchquantum.plugin.qiskit import QISKIT_INCOMPATIBLE_FUNC_NAMES +from torchquantum.util.constants import QISKIT_INCOMPATIBLE_FUNC_NAMES from torchpack.utils.logging import logger diff --git a/torchquantum/layer/layers/layers.py b/torchquantum/layer/layers/layers.py index 024a5316..d2ccce6f 100644 --- a/torchquantum/layer/layers/layers.py +++ b/torchquantum/layer/layers/layers.py @@ -29,7 +29,7 @@ import numpy as np from typing import Iterable -from torchquantum.plugin.qiskit import QISKIT_INCOMPATIBLE_FUNC_NAMES +from torchquantum.util.constants import QISKIT_INCOMPATIBLE_FUNC_NAMES from torchpack.utils.logging import logger from torchquantum.layer.entanglement.op2_layer import Op2QAllLayer diff --git a/torchquantum/layer/layers/module_from_ops.py b/torchquantum/layer/layers/module_from_ops.py index f5aea5e0..9acb4152 100644 --- a/torchquantum/layer/layers/module_from_ops.py +++ b/torchquantum/layer/layers/module_from_ops.py @@ -30,7 +30,7 @@ from typing import Iterable -from torchquantum.plugin.qiskit import QISKIT_INCOMPATIBLE_FUNC_NAMES +from torchquantum.util.constants import QISKIT_INCOMPATIBLE_FUNC_NAMES from torchpack.utils.logging import logger __all__ = [ diff --git a/torchquantum/layer/layers/op_all.py b/torchquantum/layer/layers/op_all.py index 33478781..6a612357 100644 --- a/torchquantum/layer/layers/op_all.py +++ b/torchquantum/layer/layers/op_all.py @@ -30,7 +30,7 @@ from typing import Iterable -from torchquantum.plugin.qiskit import QISKIT_INCOMPATIBLE_FUNC_NAMES +from torchquantum.util.constants import QISKIT_INCOMPATIBLE_FUNC_NAMES from torchpack.utils.logging import logger __all__ = [ diff --git a/torchquantum/layer/layers/qft_layer.py b/torchquantum/layer/layers/qft_layer.py index 6ab1539e..681ebddc 100644 --- a/torchquantum/layer/layers/qft_layer.py +++ b/torchquantum/layer/layers/qft_layer.py @@ -5,7 +5,7 @@ import numpy as np from typing import Iterable -from torchquantum.plugin.qiskit import QISKIT_INCOMPATIBLE_FUNC_NAMES +from torchquantum.util.constants import QISKIT_INCOMPATIBLE_FUNC_NAMES from torchpack.utils.logging import logger class QFTLayer(tq.QuantumModule): diff --git a/torchquantum/layer/layers/random_layers.py b/torchquantum/layer/layers/random_layers.py index 1ca55414..3999bad6 100644 --- a/torchquantum/layer/layers/random_layers.py +++ b/torchquantum/layer/layers/random_layers.py @@ -29,7 +29,7 @@ import numpy as np from typing import Iterable -from torchquantum.plugin.qiskit import QISKIT_INCOMPATIBLE_FUNC_NAMES +from torchquantum.util.constants import QISKIT_INCOMPATIBLE_FUNC_NAMES from torchpack.utils.logging import logger __all__ = [ diff --git a/torchquantum/layer/layers/ry_layer.py b/torchquantum/layer/layers/ry_layer.py index 198d3dc5..e8fd0cc1 100644 --- a/torchquantum/layer/layers/ry_layer.py +++ b/torchquantum/layer/layers/ry_layer.py @@ -5,7 +5,7 @@ import numpy as np from typing import Iterable -from torchquantum.plugin.qiskit import QISKIT_INCOMPATIBLE_FUNC_NAMES +from torchquantum.util.constants import QISKIT_INCOMPATIBLE_FUNC_NAMES from torchpack.utils.logging import logger from .layers import LayerTemplate0 diff --git a/torchquantum/layer/layers/seth_layer.py b/torchquantum/layer/layers/seth_layer.py index 82865172..80169bde 100644 --- a/torchquantum/layer/layers/seth_layer.py +++ b/torchquantum/layer/layers/seth_layer.py @@ -29,7 +29,7 @@ import numpy as np from typing import Iterable -from torchquantum.plugin.qiskit import QISKIT_INCOMPATIBLE_FUNC_NAMES +from torchquantum.util.constants import QISKIT_INCOMPATIBLE_FUNC_NAMES from torchpack.utils.logging import logger from .layers import LayerTemplate0, Op1QAllLayer diff --git a/torchquantum/layer/layers/swap_layer.py b/torchquantum/layer/layers/swap_layer.py index 65adf15f..ddcef649 100644 --- a/torchquantum/layer/layers/swap_layer.py +++ b/torchquantum/layer/layers/swap_layer.py @@ -29,7 +29,7 @@ import numpy as np from typing import Iterable -from torchquantum.plugin.qiskit import QISKIT_INCOMPATIBLE_FUNC_NAMES +from torchquantum.util.constants import QISKIT_INCOMPATIBLE_FUNC_NAMES from torchpack.utils.logging import logger from .layers import LayerTemplate0 diff --git a/torchquantum/layer/layers/u3_layer.py b/torchquantum/layer/layers/u3_layer.py index f0339ed4..a95eab98 100644 --- a/torchquantum/layer/layers/u3_layer.py +++ b/torchquantum/layer/layers/u3_layer.py @@ -29,7 +29,7 @@ import numpy as np from typing import Iterable -from torchquantum.plugin.qiskit import QISKIT_INCOMPATIBLE_FUNC_NAMES +from torchquantum.util.constants import QISKIT_INCOMPATIBLE_FUNC_NAMES from torchpack.utils.logging import logger from .layers import LayerTemplate0, Op1QAllLayer diff --git a/torchquantum/noise_model/noise_models.py b/torchquantum/noise_model/noise_models.py index 571314e9..8fed93be 100644 --- a/torchquantum/noise_model/noise_models.py +++ b/torchquantum/noise_model/noise_models.py @@ -28,7 +28,7 @@ from torchpack.utils.logging import logger from qiskit.providers.aer.noise import NoiseModel -from torchquantum.util import get_provider +from torchquantum.util.qiskit_utils import get_provider __all__ = [ diff --git a/torchquantum/operator/op_types.py b/torchquantum/operator/op_types.py index bdf35337..869e062e 100644 --- a/torchquantum/operator/op_types.py +++ b/torchquantum/operator/op_types.py @@ -239,13 +239,28 @@ def forward( else: self.func(q_device, self.wires, n_wires=self.n_wires, inverse=self.inverse) # type: ignore else: - if isinstance(self.noise_model_tq, tq.NoiseModelTQPhase): + # Avoid hard dependency on tq.noise_model export; fall back gracefully + try: + is_phase_noise = isinstance( + self.noise_model_tq, tq.noise_model.NoiseModelTQPhase + ) + except AttributeError: + # tq.noise_model not exported; treat as no phase noise + is_phase_noise = False + + if is_phase_noise: params = self.noise_model_tq.add_noise(self.params) else: params = self.params if self.clifford_quantization: - params = CliffordQuantizer.quantize_sse(params) + try: + # Local import to avoid circular dependency and undefined name at import time + from torchquantum.util.quantization import CliffordQuantizer + params = CliffordQuantizer.quantize_sse(params) + except Exception: + # If quantizer is unavailable, skip quantization + pass if self.n_wires is None: self.func(q_device, self.wires, params=params, inverse=self.inverse) else: diff --git a/torchquantum/plugin/__init__.py b/torchquantum/plugin/__init__.py index 45756475..61b66e55 100644 --- a/torchquantum/plugin/__init__.py +++ b/torchquantum/plugin/__init__.py @@ -22,4 +22,9 @@ SOFTWARE. """ -from .qiskit import * +# Only import qiskit plugin if qiskit is available +try: + from .qiskit import * +except ImportError: + # qiskit not available, skip importing qiskit plugin + pass diff --git a/torchquantum/plugin/cuquantum/LICENSE b/torchquantum/plugin/cuquantum/LICENSE new file mode 100644 index 00000000..7532a9b3 --- /dev/null +++ b/torchquantum/plugin/cuquantum/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/torchquantum/plugin/cuquantum/__init__.py b/torchquantum/plugin/cuquantum/__init__.py new file mode 100644 index 00000000..ddba3d0d --- /dev/null +++ b/torchquantum/plugin/cuquantum/__init__.py @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: MIT + +from .circuit import ParameterizedQuantumCircuit +from .cutn import CuTensorNetworkBackend, TNConfig, MPSConfig +from .expectation import QuantumExpectation +from .sampling import QuantumSampling +from .amplitude import QuantumAmplitude + +__all__ = [ + "ParameterizedQuantumCircuit", + "CuTensorNetworkBackend", + "TNConfig", + "MPSConfig", + "QuantumExpectation", + "QuantumSampling", + "QuantumAmplitude", +] diff --git a/torchquantum/plugin/cuquantum/amplitude.py b/torchquantum/plugin/cuquantum/amplitude.py new file mode 100644 index 00000000..632e2d57 --- /dev/null +++ b/torchquantum/plugin/cuquantum/amplitude.py @@ -0,0 +1,59 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: MIT + +from typing import List + +import torch.nn as nn + +from .utils import check_input_params +from .backend import QuantumBackend +from .circuit import ParameterizedQuantumCircuit + + +class QuantumAmplitude(nn.Module): + """A PyTorch module for computing quantum state amplitudes. + + This module computes the amplitudes of specified bitstrings in the quantum state prepared by a given quantum circuit. + + Args: + circuit: The quantum circuit that prepares the state. + backend: The quantum backend to use for computation. + bitstrings: List of bitstrings whose amplitudes to compute. + """ + + def __init__(self, circuit: ParameterizedQuantumCircuit, backend: QuantumBackend, bitstrings: List[str]): + super().__init__() + self._circuit = circuit.copy() + self._bitstrings = bitstrings.copy() + self._backend = backend + self._amplitude_module = self.backend._create_amplitude_module(circuit, bitstrings) + + def forward(self, input_params=None): + """Compute the amplitudes for the bitstrings specified in the constructor. + + Args: + input_params: 2D Tensor of input parameters for the circuit. Shape should be (batch_size, n_input_params). If + only one batch is being processed, the tensor can be instead a 1D tensor with shape (n_input_params,). If + the circuit has no input parameters, this argument can be omitted (i.e. None). + + Returns: + 2D Tensor of amplitudes for each bitstring in each batch. The shape is (batch_size, len(bitstrings)). + """ + input_params = check_input_params(input_params, self._circuit.n_input_params) + return self._amplitude_module(input_params) + + @property + def bitstrings(self): + """Get the list of bitstrings whose amplitudes are being computed.""" + return self._bitstrings.copy() + + @property + def circuit(self): + """Get the quantum circuit used for state preparation.""" + return self._circuit.copy() + + @property + def backend(self): + """Get the quantum backend being used for computation.""" + return self._backend diff --git a/torchquantum/plugin/cuquantum/backend.py b/torchquantum/plugin/cuquantum/backend.py new file mode 100644 index 00000000..f3675d8c --- /dev/null +++ b/torchquantum/plugin/cuquantum/backend.py @@ -0,0 +1,68 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: MIT + +from abc import ABC, abstractmethod +from typing import List, Union, Dict, Optional + +import torch.nn as nn + +from .circuit import ParameterizedQuantumCircuit + + +class QuantumBackend(ABC): + """Abstract base class for quantum backends. + + This class defines the interface that all quantum backends must implement. Each backend must provide methods for + creating PyTorch modules that compute: + - Expectation values of Pauli operators. + - State amplitudes for given bitstrings. + - Sampling from the quantum state. + """ + + @abstractmethod + def _create_expectation_module( + self, circuit: ParameterizedQuantumCircuit, pauli_ops: Union[List[str], Dict[str, float]] + ) -> nn.Module: + """Create a module for computing expectation values of Pauli operators. + + Args: + circuit: The quantum circuit that prepares the state + pauli_ops: List of Pauli operators to compute expectations for. Each Pauli operator can be either: + - A single Pauli string specifying the pauli operator for each qubit ("I", "X", "Y", or "Z"). + - A linear combination of Pauli strings specified as a dictionary mapping each single Pauli string to its + corresponding coefficient. + + Returns: + A PyTorch module that computes the expectation values. + """ + pass + + @abstractmethod + def _create_amplitude_module(self, circuit: ParameterizedQuantumCircuit, bitstrings: List[str]) -> nn.Module: + """Create a module for computing state amplitudes. + + Args: + circuit: The quantum circuit that prepares the state. + bitstrings: List of bitstrings whose amplitudes to compute. + + Returns: + A PyTorch module that computes the amplitudes. + """ + pass + + @abstractmethod + def _create_sampling_module( + self, circuit: ParameterizedQuantumCircuit, n_samples: int, wires: Optional[List[int]] = None + ) -> nn.Module: + """Create a module for sampling from the quantum state. + + Args: + circuit: The quantum circuit that prepares the state. + n_samples: Number of samples to generate. + wires: Optional list of wires/qubits to sample from. If not provided, all wires/qubits are sampled from. + + Returns: + A PyTorch module that generates samples from the quantum state. + """ + pass diff --git a/torchquantum/plugin/cuquantum/circuit.py b/torchquantum/plugin/cuquantum/circuit.py new file mode 100644 index 00000000..db794b71 --- /dev/null +++ b/torchquantum/plugin/cuquantum/circuit.py @@ -0,0 +1,212 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: MIT + +from collections import namedtuple +from typing import List, Optional + +import torch +import torch.nn as nn +from torchquantum.operator import Operator +from torchquantum.operator.op_types import AnyNParams, AnyWires +from torchquantum.operator.standard_gates import all_variables +from torchquantum.operator.standard_gates.reset import Reset + + +class _ParameterizedQuantumGate: + """A named tuple representing a parameterized quantum gate in a circuit. + + This class holds the information needed to represent a quantum gate with parameters + that can be either trainable, input parameters, or fixed values. + + Attributes: + matrix_generator: Function that generates the gate's unitary matrix given parameters as an argument. + wires: List of qubit indices the gate acts on + params: Current parameter values for the gate + trainable_idx: Indices of parameters that are trainable + input_idx: Indices of parameters that are input parameters + inverse: Whether the gate should be applied in inverse + """ + + +_ParameterizedQuantumGate = namedtuple( + "Gate", ["matrix_generator", "wires", "params", "trainable_idx", "input_idx", "inverse"] +) + + +class ParameterizedQuantumCircuit: + """A class representing a parameterized quantum circuit. + + This class allows building quantum circuits with both trainable and input parameters. + Gates can be added to the circuit with parameters that are either trainable, + input parameters, or fixed values. + + Args: + n_wires: Number of qubits in the circuit + n_input_params: Number of input parameters the circuit accepts + n_trainable_params: Number of trainable parameters in the circuit + """ + + def __init__(self, n_wires: int, n_input_params: int = 0, n_trainable_params: int = 0): + super().__init__() + self._n_wires = n_wires + self._n_input_params = n_input_params + self._n_trainable_params = n_trainable_params + self._gates = [] + self._trainable_params = nn.Parameter(torch.zeros(n_trainable_params)) + + @property + def n_wires(self): + """Get the number of qubits in the circuit.""" + return self._n_wires + + @property + def n_input_params(self): + """Get the number of input parameters the circuit accepts.""" + return self._n_input_params + + @property + def n_trainable_params(self): + """Get the number of trainable parameters in the circuit.""" + return self._n_trainable_params + + @property + def gates(self): + """Get the list of gates in the circuit.""" + return self._gates + + @property + def trainable_params(self): + """Get the trainable parameters of the circuit.""" + return self._trainable_params + + def copy(self): + """Creates a shallow copy of the circuit. + + The parameters are shared, but appending new gates will not affect the original circuit. + + Returns: + A new ParameterizedQuantumCircuit instance with the same gates and parameters + """ + circuit = ParameterizedQuantumCircuit(self._n_wires, self._n_input_params, self._n_trainable_params) + circuit._trainable_params = self._trainable_params + circuit._gates = self._gates[:] + return circuit + + def append_gate( + self, + op: Operator, + wires: List[int], + fixed_params: Optional[List[float]] = None, + trainable_idx: Optional[List[int]] = None, + input_idx: Optional[List[int]] = None, + inverse: bool = False, + ): + """Add a gate to the circuit. + + Args: + op: The quantum operator to apply. It can be any of the TorchQuantum operators defined in + :py:mod:`torchquantum.operator.standard_gates` with a fixed number of parameters except for + :py:class:`Reset `. Note that + wires: List of qubit(s) to apply the gate to. + fixed_params: List of numbers defining the values of the fixed parameters for the gate. The length of this + list must be the same as the number of parameters for the gate. Gate parameters that are not fixed + should be set to None in this list. If the gate has no fixed parameters, this argument can be omitted + (i.e. None). + trainable_idx: List of indices linking the gate parameters to the circuit's trainable parameters. The length + of this list must be the same as the number of parameters for the gate. Gate parameters that are not + trainable should be set to None in this list. If the gate has no trainable parameters, this argument can + be omitted (i.e. None). + input_idx: List of indices linking the gate parameters to the circuit's input parameters. The length of this + list must be the same as the number of parameters for the gate. Gate parameters that are not input + parameters should be set to None in this list. If the gate has no input parameters, this argument can be + omitted (i.e. None). + inverse: Whether to apply the inverse of the operator + + Raises: + ValueError: If the operator is invalid, wires are out of bounds, or parameter indices are invalid. + """ + if op not in all_variables: + raise ValueError(f"{op} is not a valid operator") + + if isinstance(op, Reset): + raise ValueError(f"{op} is not supported") + + if op.num_params == AnyNParams: + raise ValueError(f"{op} has a variable number of parameters. This is not supported yet.") + + name = op.__name__ + if isinstance(wires, int): + wires = [wires] + if op.num_wires != AnyWires and len(wires) != op.num_wires: + raise ValueError(f"Number of wires for {name} must be {op.num_wires}") + for wire in wires: + if wire < 0 or wire >= self._n_wires: + raise ValueError(f"Wire {wire} is out of bounds") + + n_params = op.num_params + + if fixed_params is None: + fixed_params = [None] * n_params + if isinstance(fixed_params, float): + fixed_params = [fixed_params] + if not isinstance(fixed_params, list) or len(fixed_params) != n_params: + raise ValueError(f"Fixed params must be a list of floats/None of length {n_params}") + + + if trainable_idx is None: + trainable_idx = [None] * n_params + if isinstance(trainable_idx, int): + trainable_idx = [trainable_idx] + if not isinstance(trainable_idx, list) or len(trainable_idx) != n_params: + raise ValueError(f"Trainable index must be an integer or a list of integers/None of length {n_params}") + for idx in trainable_idx: + if idx is not None and (idx < 0 or idx >= self._n_trainable_params): + raise ValueError(f"Trainable index {idx} is out of bounds") + + if input_idx is None: + input_idx = [None] * n_params + if isinstance(input_idx, int): + input_idx = [input_idx] + if not isinstance(input_idx, list) or len(input_idx) != n_params: + raise ValueError(f"Input index must be an integer or a list of integers/None of length {n_params}") + for idx in input_idx: + if idx is not None and (idx < 0 or idx >= self._n_input_params): + raise ValueError(f"Input index {idx} is out of bounds") + + params = torch.empty(op.num_params) + for p in range(n_params): + if fixed_params[p] is not None: + if(trainable_idx[p] is not None): + raise ValueError(f"Parameter {p} cannot be both fixed and trainable") + if(input_idx[p] is not None): + raise ValueError(f"Parameter {p} cannot be both fixed and an input") + params[p] = fixed_params[p] + else: + if trainable_idx[p] is not None and input_idx[p] is not None: + raise ValueError(f"Parameter {p} cannot be both trainable and an input") + if trainable_idx[p] is None and input_idx[p] is None: + raise ValueError(f"Parameter {p} must be either fixed, trainable, or an input") + + matrix_generator = _maxtrix_generator_from_operator(op, len(wires)) + + self._gates.append( + _ParameterizedQuantumGate(matrix_generator, wires, params, trainable_idx, input_idx, inverse) + ) + + def set_trainable_params(self, trainable_params: torch.Tensor): + """Set the trainable parameters of the circuit. + + Args: + trainable_params: A tensor of trainable parameters + """ + with torch.no_grad(): + for i in range(self._n_trainable_params): + self._trainable_params[i] = trainable_params[i] + + +def _maxtrix_generator_from_operator(op, n_wires): + if op.num_wires == AnyWires: # This is necessary for operators that act on any number of wires, e.g. QFT, MultiCNOT, MultiRZ, etc. + return lambda params: op._matrix(params.unsqueeze(0), n_wires).reshape((2,) * (2 * n_wires)) + else: + return lambda params: op._matrix(params.unsqueeze(0)).reshape((2,) * (2 * n_wires)) diff --git a/torchquantum/plugin/cuquantum/cutn/__init__.py b/torchquantum/plugin/cuquantum/cutn/__init__.py new file mode 100644 index 00000000..b028736b --- /dev/null +++ b/torchquantum/plugin/cuquantum/cutn/__init__.py @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: MIT + +from .backend import CuTensorNetworkBackend, TNConfig, MPSConfig + +__all__ = ["CuTensorNetworkBackend", "TNConfig", "MPSConfig"] diff --git a/torchquantum/plugin/cuquantum/cutn/amplitude.py b/torchquantum/plugin/cuquantum/cutn/amplitude.py new file mode 100644 index 00000000..5a54f09d --- /dev/null +++ b/torchquantum/plugin/cuquantum/cutn/amplitude.py @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: MIT + +import torch +from torch import nn + +from .state import ParameterizedNetworkState +from .gradient import CuTNFiniteDifference + + +class CuTNAmplitudeFD(nn.Module): + def __init__(self, state, bitstrings, circuit_params, delta): + super().__init__() + + self.n_amplitudes = len(bitstrings) + self.state = state + self.bitstrings = bitstrings + if state.dtype == "float64" or state.dtype == "complex128": + self.output_dtype = torch.complex128 + elif state.dtype == "float32" or state.dtype == "complex64": + self.output_dtype = torch.complex64 + else: + raise ValueError(f"Unkown state dtype: {state.dtype}") + self.delta = delta + self.circuit_params = circuit_params + + def forward(self, input_params): + amplitudes = torch.zeros(input_params.shape[0], self.n_amplitudes, dtype=self.output_dtype) + for batch_idx in range(input_params.shape[0]): + for amplitude_idx in range(self.n_amplitudes): + amplitudes[batch_idx, amplitude_idx] = CuTNFiniteDifference.apply( + self.state, + _amplitude_wrapper, + self.bitstrings[amplitude_idx], + self.delta, + self.circuit_params, + input_params[batch_idx], + ) + return amplitudes + + +def _amplitude_wrapper(state: ParameterizedNetworkState, bitstring: str): + return state.compute_amplitude(bitstring) diff --git a/torchquantum/plugin/cuquantum/cutn/backend.py b/torchquantum/plugin/cuquantum/cutn/backend.py new file mode 100644 index 00000000..0c40ad4e --- /dev/null +++ b/torchquantum/plugin/cuquantum/cutn/backend.py @@ -0,0 +1,80 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: MIT + +from typing import List, Union, Dict, Optional + +from torch import nn +from cuquantum.tensornet.experimental import TNConfig, MPSConfig + +from ..backend import QuantumBackend +from ..circuit import ParameterizedQuantumCircuit +from .state import ParameterizedNetworkState +from .expectation import CuTNExpectationFD +from .amplitude import CuTNAmplitudeFD +from .sampling import CuTNSampling + + +class CuTensorNetworkBackend(QuantumBackend): + """A backend implementation using cuQuantum's Tensor Network library for quantum circuit simulations. + + This backend provides functionality for computing expectation values, amplitudes, and sampling from quantum circuits using + tensor network methods. It supports both general tensor networks and Matrix Product States (MPS). + + Args: + config: Optional configuration for the tensor network simulation. Can be either a + :py:class:`TNConfig ` or + :py:class:`MPSConfig ` object. + allow_multiple_states: If False, the backend uses a single network state for each quantum PyTorch module. + If True, the backend may create separate network states to utilize caching when necessary. + This is e.g. useful when the same quantum circuit is used to compute expectation values of different Pauli + operators. This can speed up the computation at the cost of slightly increased memory usage (one network state + per Pauli operator). Default is True. + grad_method: Method for computing gradients. Currently only supports "finite_difference". + fd_delta: Step size for finite difference gradient computation. + """ + + def __init__( + self, + config=Optional[Union[TNConfig, MPSConfig]], + allow_multiple_states: bool = True, + grad_method: str = "finite_difference", + fd_delta: float = 1e-4, + ): + self._allow_multiple_states = allow_multiple_states + self._config = config + self._grad_method = grad_method + self._fd_delta = fd_delta + if not self._grad_method in ["finite_difference"]: + raise NotImplementedError(f"Unkown gradient method") + + def _create_expectation_module( + self, circuit: ParameterizedQuantumCircuit, pauli_ops: Union[List[str], Dict[str, float]] + ) -> nn.Module: + if self._allow_multiple_states: + # In order to utilize caching feature of the network states, we need to create a seperate network state for each Pauli operator. + # Otherwise, the network state cache will be overwritten when pauli_op changes. + states = [ + ParameterizedNetworkState.from_parameterized_circuit(circuit, self._config) + for _ in range(len(pauli_ops)) + ] + else: + states = [ParameterizedNetworkState.from_parameterized_circuit(circuit, self._config)] * len(pauli_ops) + + if self._grad_method == "finite_difference": + return CuTNExpectationFD(states, pauli_ops, circuit.trainable_params, self._fd_delta) + else: + raise NotImplementedError(f"Gradient method {self._grad_method} not supported for this backend") + + def _create_amplitude_module(self, circuit: ParameterizedQuantumCircuit, bitstrings: List[str]) -> nn.Module: + state = ParameterizedNetworkState.from_parameterized_circuit(circuit, self._config) + if self._grad_method == "finite_difference": + return CuTNAmplitudeFD(state, bitstrings, circuit.trainable_params, self._fd_delta) + else: + raise NotImplementedError(f"Gradient method {self._grad_method} not supported for this backend") + + def _create_sampling_module( + self, circuit: ParameterizedQuantumCircuit, n_samples: int, wires: Optional[List[int]] = None + ): + state = ParameterizedNetworkState.from_parameterized_circuit(circuit, self._config) + return CuTNSampling(state, n_samples, wires, circuit.trainable_params) diff --git a/torchquantum/plugin/cuquantum/cutn/expectation.py b/torchquantum/plugin/cuquantum/cutn/expectation.py new file mode 100644 index 00000000..6a6ea131 --- /dev/null +++ b/torchquantum/plugin/cuquantum/cutn/expectation.py @@ -0,0 +1,63 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: MIT + +import torch +from torch import nn +from cuquantum.tensornet.experimental import NetworkOperator + +from .gradient import CuTNFiniteDifference + + +class CuTNExpectationFD(nn.Module): + def __init__(self, states, pauli_ops, circuit_params, delta): + super().__init__() + if len(states) != len(pauli_ops): + raise ValueError(f"Expected as many states as Pauli operators, got {len(states)} and {len(pauli_ops)}") + if len(states) == 0: + raise ValueError(f"Expected at least one state") + + self.n_exp_vals = len(pauli_ops) + self.states = states + self.pauli_ops = [] + self.output_dtype = torch.float32 + for i in range(self.n_exp_vals): + self.pauli_ops.append(NetworkOperator.from_pauli_strings(pauli_ops[i], dtype=states[i].dtype)) + if states[i].dtype == "float64" or states[i].dtype == "complex128": + self.output_dtype = torch.float64 + elif states[i].dtype == "float32" or states[i].dtype == "complex64": + pass + else: + raise ValueError(f"Unkown state dtype: {states[i].dtype}") + + self.delta = delta + self.circuit_params = circuit_params + + def forward(self, input_params): + exp_vals = torch.zeros(input_params.shape[0], self.n_exp_vals, dtype=self.output_dtype) + for batch_idx in range(input_params.shape[0]): + for exp_val_idx in range(self.n_exp_vals): + exp_vals[batch_idx, exp_val_idx] = CuTNFiniteDifference.apply( + self.states[exp_val_idx], + _expectation_wrapper, + self.pauli_ops[exp_val_idx], + self.delta, + self.circuit_params, + input_params[batch_idx], + ) + return exp_vals + + +def _expectation_wrapper(state, operator): + value = state.compute_expectation(operator) + + if state.dtype == "float32" or state.dtype == "complex64": + if abs(value.imag) > 1e-6: + raise RuntimeWarning(f"Something is wrong. Expectation value is not real. Value: {value}") + elif state.dtype == "float64" or state.dtype == "complex128": + if abs(value.imag) > 1e-15: + raise RuntimeWarning(f"Something is wrong. Expectation value is not real. Value: {value}") + else: + raise ValueError(f"Unknown dtype: {state.dtype}") + + return value.real diff --git a/torchquantum/plugin/cuquantum/cutn/gradient.py b/torchquantum/plugin/cuquantum/cutn/gradient.py new file mode 100644 index 00000000..a77d09c3 --- /dev/null +++ b/torchquantum/plugin/cuquantum/cutn/gradient.py @@ -0,0 +1,53 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: MIT + +import torch + + +class CuTNFiniteDifference(torch.autograd.Function): + @staticmethod + def forward(ctx, state, operation, operation_argument, delta: float, *args): + ctx.save_for_backward(*[arg.detach().clone() for arg in args]) # Save tensors for backward + ctx.state = state + ctx.operation = operation + ctx.operation_argument = operation_argument + ctx.delta = delta + + state.update_all_parameters(*args) + + return torch.tensor(operation(state, operation_argument)) + + @staticmethod + def backward(ctx, grad_output): + """Backward pass: compute gradients""" + args = ctx.saved_tensors + state = ctx.state + operation = ctx.operation + operation_argument = ctx.operation_argument + delta = ctx.delta + + # restore all original parameters + state.update_all_parameters(*args) + + grads = [None] * len(args) + + for arg_idx, arg in enumerate(args): + if ctx.needs_input_grad[4 + arg_idx]: + grads[arg_idx] = torch.zeros_like(arg) + for var_idx in range(grads[arg_idx].shape[0]): + original_arg_val = arg[var_idx].item() + arg[var_idx] = original_arg_val - delta / 2 + state.update_parameter(arg_idx, var_idx, *args) + val_minus = operation(state, operation_argument) + + arg[var_idx] = original_arg_val + delta / 2 + state.update_parameter(arg_idx, var_idx, *args) + val_plus = operation(state, operation_argument) + + grads[arg_idx][var_idx] = grad_output * (val_plus - val_minus) / delta + + arg[var_idx] = original_arg_val + state.update_parameter(arg_idx, var_idx, *args) + + return None, None, None, None, *grads diff --git a/torchquantum/plugin/cuquantum/cutn/sampling.py b/torchquantum/plugin/cuquantum/cutn/sampling.py new file mode 100644 index 00000000..76853b91 --- /dev/null +++ b/torchquantum/plugin/cuquantum/cutn/sampling.py @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: MIT + +import torch.nn as nn + + +class CuTNSampling(nn.Module): + def __init__(self, state, n_samples, wires, circuit_params): + super().__init__() + self.state = state + self.n_samples = n_samples + self.wires = wires + self.circuit_params = circuit_params + + def forward(self, input_params): + samples = [] + for batch_idx in range(input_params.shape[0]): + self.state.update_all_parameters(self.circuit_params, input_params[batch_idx]) + samples.append(self.state.compute_sampling(self.n_samples, modes=self.wires)) + + return samples diff --git a/torchquantum/plugin/cuquantum/cutn/state.py b/torchquantum/plugin/cuquantum/cutn/state.py new file mode 100644 index 00000000..82bcfebf --- /dev/null +++ b/torchquantum/plugin/cuquantum/cutn/state.py @@ -0,0 +1,99 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: MIT + +from collections import defaultdict + +import torch +from torchquantum.macro import C_DTYPE +from cuquantum.tensornet.experimental import NetworkState + + +class ParameterizedTensorOperator: + def __init__(self, modes, tensor_generator, params, parameters_map, unitary, adjoint): + self.modes = modes + self.tensor_generator = tensor_generator + self.params = params + self.parameters_map = parameters_map + self.unitary = unitary + self.adjoint = adjoint + + @classmethod + def from_gate(cls, gate, trainable_args_idx=0, input_args_idx=1): + parameters_map = {} + + for param_idx in range(len(gate.params)): + if gate.trainable_idx[param_idx] is not None: + parameters_map[param_idx] = (trainable_args_idx, gate.trainable_idx[param_idx]) + if gate.input_idx[param_idx] is not None: + parameters_map[param_idx] = (input_args_idx, gate.input_idx[param_idx]) + + return cls(gate.wires, gate.matrix_generator, gate.params, parameters_map, True, gate.inverse) + + def update(self, network_state, tensor_id, *args): + for param_idx, (arg_idx, val_idx) in self.parameters_map.items(): + self.params[param_idx] = args[arg_idx][val_idx] + + tensor = self.tensor_generator(self.params) + network_state.update_tensor_operator(tensor_id, tensor, unitary=self.unitary) + + +class ParameterizedNetworkState(NetworkState): + """ + A NetworkState that can be parameterized. + """ + + def __init__(self, param_args_shapes, *args, **kwargs): + super().__init__(*args, **kwargs) + self.param_args_shapes = param_args_shapes + self.mutable_operators = {} # tensor_id -> operator + self.reverse_params_map = defaultdict(set) # (arg_idx, val_idx) -> set of tensor_ids + + def apply_parameterized_tensor_operator(self, operator: ParameterizedTensorOperator): + operand = operator.tensor_generator(operator.params) + immutable = not operator.parameters_map + tensor_id = super().apply_tensor_operator( + operator.modes, operand, immutable=immutable, unitary=operator.unitary, adjoint=operator.adjoint + ) + if not immutable: + self.mutable_operators[tensor_id] = operator + for arg_idx, val_idx in operator.parameters_map.values(): + self.reverse_params_map[(arg_idx, val_idx)].add(tensor_id) + return tensor_id + + def update_all_parameters(self, *args): + if len(args) != len(self.param_args_shapes): + raise ValueError(f"Expected {len(self.param_args_shapes)} arguments, got {len(args)}") + for arg_idx, arg_shape in enumerate(self.param_args_shapes): + if args[arg_idx].ndim != 1: + raise ValueError(f"Expected argument {arg_idx} to be a 1D tensor, got {args[arg_idx].ndim}D tensor") + if args[arg_idx].size(0) != arg_shape: + raise ValueError(f"Expected argument {arg_idx} to have shape {arg_shape}, got {args[arg_idx].size(0)}") + + for tensor_id, operator in self.mutable_operators.items(): + operator.update(self, tensor_id, *args) + + def update_parameter(self, arg_idx, val_idx, *args): + for tensor_id in self.reverse_params_map[(arg_idx, val_idx)]: + self.mutable_operators[tensor_id].update(self, tensor_id, *args) + + @classmethod + def from_parameterized_circuit(cls, circuit, config): + if C_DTYPE == torch.complex64: + dtype = "complex64" + elif C_DTYPE == torch.complex128: + dtype = "complex128" + else: + raise ValueError(f"Unsupported dtype: {dtype}") + + state = cls( + param_args_shapes=[circuit.n_trainable_params, circuit.n_input_params], + state_mode_extents=(2,) * circuit.n_wires, + dtype=dtype, + config=config, + ) + for gate in circuit._gates: + operator = ParameterizedTensorOperator.from_gate(gate, 0, 1) + state.apply_parameterized_tensor_operator(operator) + + return state diff --git a/torchquantum/plugin/cuquantum/expectation.py b/torchquantum/plugin/cuquantum/expectation.py new file mode 100644 index 00000000..8a3dc478 --- /dev/null +++ b/torchquantum/plugin/cuquantum/expectation.py @@ -0,0 +1,68 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: MIT + +from typing import List, Dict, Union + +import torch.nn as nn + +from .utils import check_input_params +from .backend import QuantumBackend +from .circuit import ParameterizedQuantumCircuit + + +class QuantumExpectation(nn.Module): + """A PyTorch module for computing expectation values of Pauli operators. + + This module computes the expectation values of specified Pauli operators + in the quantum state prepared by a given quantum circuit. + + Args: + circuit: The quantum circuit that prepares the state. + backend: The quantum backend to use for computation. + pauli_ops: List of Pauli operators to compute expectations for. Each Pauli operator can be either: + - A single Pauli string specifying the Pauli operator for each qubit ("I", "X", "Y", or "Z"). + - A linear combination of Pauli strings specified as a dictionary mapping each single Pauli string to + its corresponding coefficient. + """ + + def __init__( + self, + circuit: ParameterizedQuantumCircuit, + backend: QuantumBackend, + pauli_ops: Union[List[str], Dict[str, float]], + ): + super().__init__() + self._circuit = circuit.copy() + self._pauli_ops = pauli_ops.copy() + self._backend = backend + self._expectation_module = self.backend._create_expectation_module(circuit, pauli_ops) + + def forward(self, input_params=None): + """Compute the expectation values for the Pauli operators specified in the constructor. + + Args: + input_params: 2D Tensor of input parameters for the circuit. Shape should be (batch_size, n_input_params). If + only one batch is being processed, the tensor can be instead a 1D tensor with shape (n_input_params,). If + the circuit has no input parameters, this argument can be omitted (i.e. None). + + Returns: + 2D Tensor of expectation values for each Pauli operator in each batch. The shape is (batch_size, len(pauli_ops)). + """ + input_params = check_input_params(input_params, self._circuit.n_input_params) + return self._expectation_module(input_params) + + @property + def pauli_ops(self): + """Get the list of Pauli operators being measured.""" + return self._pauli_ops.copy() + + @property + def circuit(self): + """Get the quantum circuit used for state preparation.""" + return self._circuit.copy() + + @property + def backend(self): + """Get the quantum backend being used for computation.""" + return self._backend diff --git a/torchquantum/plugin/cuquantum/sampling.py b/torchquantum/plugin/cuquantum/sampling.py new file mode 100644 index 00000000..77107d49 --- /dev/null +++ b/torchquantum/plugin/cuquantum/sampling.py @@ -0,0 +1,54 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: MIT + +from typing import List, Optional + +import torch.nn as nn + +from .utils import check_input_params +from .backend import QuantumBackend +from .circuit import ParameterizedQuantumCircuit + + +class QuantumSampling(nn.Module): + """A PyTorch module for sampling from quantum states. + + This module generates samples from the quantum state prepared by a given quantum circuit. It can sample from all + qubits or a specified subset of qubits. + + Args: + circuit: The quantum circuit that prepares the state. + backend: The quantum backend to use for computation. + n_samples: Number of samples to generate per batch. + wires: Optional list of wires/qubits to sample from. If not provided, all wires/qubits are sampled from. + """ + + def __init__( + self, + circuit: ParameterizedQuantumCircuit, + backend: QuantumBackend, + n_samples: int, + wires: Optional[List[int]] = None, + ): + super().__init__() + self.circuit = circuit + self.n_samples = n_samples + self.wires = wires + self.backend = backend + self.sampling_module = self.backend._create_sampling_module(circuit, n_samples, wires) + + def forward(self, input_params=None): + """Generate samples from the quantum state. + + Args: + input_params: 2D Tensor of input parameters for the circuit. Shape should be (batch_size, n_input_params). If + only one batch is being processed, the tensor can be instead a 1D tensor with shape (n_input_params,). If + the circuit has no input parameters, this argument can be omitted (i.e. None). + + Returns: + List of samples with length batch_size. Each sample is a dictionary mapping the bitstring to the corresponding + count. + """ + input_params = check_input_params(input_params, self.circuit.n_input_params) + return self.sampling_module(input_params) diff --git a/torchquantum/plugin/cuquantum/utils.py b/torchquantum/plugin/cuquantum/utils.py new file mode 100644 index 00000000..a326cade --- /dev/null +++ b/torchquantum/plugin/cuquantum/utils.py @@ -0,0 +1,33 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: MIT + +import torch + +def check_input_params(input_params, n_params): + """Validate and format input parameters for quantum circuits. + + This function ensures that input parameters are properly formatted as a 2D tensor with the correct number of parameters + per batch. + + Args: + input_params: Input parameters tensor. Can be None, 1D, or 2D. + n_params: Expected number of parameters per batch. + + Returns: + A 2D tensor of shape (batch_size, n_params) containing the input parameters. + + Raises: + ValueError: If input_params is not a 1D or 2D tensor, or if it has the wrong number of parameters per batch. + """ + if(input_params is None): + input_params = torch.zeros(0, dtype=torch.float32) + if(input_params.ndim == 1): # no batching, make it a batch of size 1 + input_params = input_params.unsqueeze(0) + if(input_params.ndim != 2): + raise ValueError(f"Input must be a 1D or 2D tensor") + + if(input_params.shape[1] != n_params): + raise ValueError(f"Input must have {n_params} parameters per batch") + + return input_params \ No newline at end of file diff --git a/torchquantum/plugin/qiskit/qiskit_processor.py b/torchquantum/plugin/qiskit/qiskit_processor.py index 2d91e7c3..be306110 100644 --- a/torchquantum/plugin/qiskit/qiskit_processor.py +++ b/torchquantum/plugin/qiskit/qiskit_processor.py @@ -38,9 +38,11 @@ ) from torchquantum.util import ( get_expectations_from_counts, + get_circ_stats, +) +from torchquantum.util.qiskit_utils import ( get_provider, get_provider_hub_group_project, - get_circ_stats, ) from .qiskit_macros import IBMQ_NAMES from tqdm import tqdm diff --git a/torchquantum/util/__init__.py b/torchquantum/util/__init__.py index 6c43455a..d3645216 100644 --- a/torchquantum/util/__init__.py +++ b/torchquantum/util/__init__.py @@ -24,3 +24,4 @@ from .utils import * from .vqe_utils import * +from .constants import * diff --git a/torchquantum/util/constants.py b/torchquantum/util/constants.py new file mode 100644 index 00000000..84f06c9b --- /dev/null +++ b/torchquantum/util/constants.py @@ -0,0 +1,32 @@ +""" +MIT License + +Copyright (c) 2020-present TorchQuantum Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +# Non-qiskit dependent constants that can be used throughout torchquantum + +# Function names that are incompatible with qiskit +QISKIT_INCOMPATIBLE_FUNC_NAMES = [ + "rot", + "multirz", + "crot", +] diff --git a/torchquantum/util/qiskit_utils.py b/torchquantum/util/qiskit_utils.py new file mode 100644 index 00000000..23320aa1 --- /dev/null +++ b/torchquantum/util/qiskit_utils.py @@ -0,0 +1,152 @@ +""" +MIT License + +Copyright (c) 2020-present TorchQuantum Authors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +""" + +# Qiskit-dependent utilities +# This module isolates all qiskit dependencies to avoid import issues for non-qiskit users + +from torchpack.utils.logging import logger + +# Optional qiskit imports - only imported when functions are called +def _import_qiskit_runtime(): + try: + from qiskit_ibm_runtime import QiskitRuntimeService + return QiskitRuntimeService + except ImportError: + raise ImportError("qiskit_ibm_runtime is required for this functionality. Install with: pip install qiskit-ibm-runtime") + +def _import_qiskit_error(): + try: + from qiskit.exceptions import QiskitError + return QiskitError + except ImportError: + raise ImportError("qiskit is required for this functionality. Install with: pip install qiskit") + +def _import_gate_error_values(): + try: + from qiskit.providers.aer.noise.device.parameters import gate_error_values + return gate_error_values + except ImportError: + raise ImportError("qiskit-aer is required for this functionality. Install with: pip install qiskit-aer") + + +def get_success_rate(properties, transpiled_circ): + """ + Estimate the success rate of a transpiled quantum circuit. + + Args: + properties (list): List of gate error properties. + transpiled_circ (QuantumCircuit): The transpiled quantum circuit. + + Returns: + float: The estimated success rate. + """ + # estimate the success rate according to the error rates of single and + # two-qubit gates in transpiled circuits + + gate_error_values = _import_gate_error_values() + gate_errors = gate_error_values(properties) + # construct the error dict + gate_error_dict = {} + for gate_error in gate_errors: + if gate_error[0] not in gate_error_dict.keys(): + gate_error_dict[gate_error[0]] = {tuple(gate_error[1]): gate_error[2]} + else: + gate_error_dict[gate_error[0]][tuple(gate_error[1])] = gate_error[2] + + success_rate = 1 + + for instruction, qubits, cbit in transpiled_circ.data: + if instruction.name in gate_error_dict.keys(): + if tuple(qubits) in gate_error_dict[instruction.name].keys(): + gate_err = gate_error_dict[instruction.name][tuple(qubits)] + success_rate *= 1 - gate_err + else: + logger.warning( + f"no error rate found for gate {instruction.name} on qubits {qubits}" + ) + + return success_rate + + +def get_provider(backend_name, hub=None): + """ + Get the provider object for a specific backend from IBM Quantum. + + Args: + backend_name (str): Name of the backend. + hub (str): Optional hub name. + + Returns: + IBMQProvider: The provider object. + """ + QiskitRuntimeService = _import_qiskit_runtime() + QiskitError = _import_qiskit_error() + + # mass-inst-tech-1 or MIT-1 + if backend_name in ["ibmq_casablanca", "ibmq_rome", "ibmq_bogota", "ibmq_jakarta"]: + if hub == "mass" or hub is None: + provider = QiskitRuntimeService(channel = "ibm_quantum", instance = "ibm-q-research/mass-inst-tech-1/main") + elif hub == "mit": + provider = QiskitRuntimeService(channel = "ibm_quantum", instance = "ibm-q-research/MIT-1/main") + else: + raise ValueError(f"not supported backend {backend_name} in hub " f"{hub}") + elif backend_name in [ + "ibmq_paris", + "ibmq_toronto", + "ibmq_manhattan", + "ibmq_guadalupe", + "ibmq_montreal", + ]: + provider = QiskitRuntimeService(channel = "ibm_quantum", instance = "ibm-q-ornl/anl/csc428") + else: + if hub == "mass" or hub is None: + try: + provider = QiskitRuntimeService(channel = "ibm_quantum", instance = "ibm-q-research/mass-inst-tech-1/main") + except QiskitError: + # logger.warning(f"Cannot use MIT backend, roll back to open") + logger.warning(f"Use the open backend") + provider = QiskitRuntimeService(channel = "ibm_quantum", instance = "ibm-q/open/main") + elif hub == "mit": + provider = QiskitRuntimeService(channel = "ibm_quantum", instance = "ibm-q-research/MIT-1/main") + else: + provider = QiskitRuntimeService(channel = "ibm_quantum", instance = "ibm-q/open/main") + + return provider + + +def get_provider_hub_group_project(hub="ibm-q", group="open", project="main"): + """ + Get provider by specifying hub, group, and project. + + Args: + hub (str): Hub name. + group (str): Group name. + project (str): Project name. + + Returns: + IBMQProvider: The provider object. + """ + QiskitRuntimeService = _import_qiskit_runtime() + provider = QiskitRuntimeService(channel = "ibm_quantum", instance = f"{hub}/{group}/{project}") + return provider diff --git a/torchquantum/util/utils.py b/torchquantum/util/utils.py index caeee471..f42bbec0 100644 --- a/torchquantum/util/utils.py +++ b/torchquantum/util/utils.py @@ -30,9 +30,6 @@ import torch.nn as nn import torch.nn.functional as F from opt_einsum import contract -from qiskit_ibm_runtime import QiskitRuntimeService -from qiskit.exceptions import QiskitError -from qiskit.providers.aer.noise.device.parameters import gate_error_values from torchpack.utils.config import Config from torchpack.utils.logging import logger @@ -65,9 +62,6 @@ "get_p_c_reg_mapping", "get_v_c_reg_mapping", "get_cared_configs", - "get_success_rate", - "get_provider", - "get_provider_hub_group_project", "normalize_statevector", "get_circ_stats", "partial_trace", @@ -704,88 +698,6 @@ def get_cared_configs(conf, mode) -> Config: return conf -def get_success_rate(properties, transpiled_circ): - """ - Estimate the success rate of a transpiled quantum circuit. - - Args: - properties (list): List of gate error properties. - transpiled_circ (QuantumCircuit): The transpiled quantum circuit. - - Returns: - float: The estimated success rate. - """ - # estimate the success rate according to the error rates of single and - # two-qubit gates in transpiled circuits - - gate_errors = gate_error_values(properties) - # construct the error dict - gate_error_dict = {} - for gate_error in gate_errors: - if gate_error[0] not in gate_error_dict.keys(): - gate_error_dict[gate_error[0]] = {tuple(gate_error[1]): gate_error[2]} - else: - gate_error_dict[gate_error[0]][tuple(gate_error[1])] = gate_error[2] - - success_rate = 1 - for gate in transpiled_circ.data: - gate_success_rate = ( - 1 - gate_error_dict[gate[0].name][tuple(map(lambda x: x.index, gate[1]))] - ) - if gate_success_rate == 0: - gate_success_rate = 1e-5 - success_rate *= gate_success_rate - - return success_rate - -def get_provider(backend_name, hub=None): - """ - Get the provider object for a specific backend from IBM Quantum. - - Args: - backend_name (str): Name of the backend. - hub (str): Optional hub name. - - Returns: - IBMQProvider: The provider object. - """ - # mass-inst-tech-1 or MIT-1 - if backend_name in ["ibmq_casablanca", "ibmq_rome", "ibmq_bogota", "ibmq_jakarta"]: - if hub == "mass" or hub is None: - provider = QiskitRuntimeService(channel = "ibm_quantum", instance = "ibm-q-research/mass-inst-tech-1/main") - elif hub == "mit": - provider = QiskitRuntimeService(channel = "ibm_quantum", instance = "ibm-q-research/MIT-1/main") - else: - raise ValueError(f"not supported backend {backend_name} in hub " f"{hub}") - elif backend_name in [ - "ibmq_paris", - "ibmq_toronto", - "ibmq_manhattan", - "ibmq_guadalupe", - "ibmq_montreal", - ]: - provider = QiskitRuntimeService(channel = "ibm_quantum", instance = "ibm-q-ornl/anl/csc428") - else: - if hub == "mass" or hub is None: - try: - provider = QiskitRuntimeService(channel = "ibm_quantum", instance = "ibm-q-research/mass-inst-tech-1/main") - except QiskitError: - # logger.warning(f"Cannot use MIT backend, roll back to open") - logger.warning(f"Use the open backend") - provider = QiskitRuntimeService(channel = "ibm_quantum", instance = "ibm-q/open/main") - elif hub == "mit": - provider = QiskitRuntimeService(channel = "ibm_quantum", instance = "ibm-q-research/MIT-1/main") - else: - provider = QiskitRuntimeService(channel = "ibm_quantum", instance = "ibm-q/open/main") - - return provider - - -def get_provider_hub_group_project(hub="ibm-q", group="open", project="main"): - provider = QiskitRuntimeService(channel = "ibm_quantum", instance = f"{hub}/{group}/{project}") - return provider - - def normalize_statevector(states): """ Normalize a statevector to ensure the square magnitude of the statevector sums to 1.