├── ClassifyPhases.ipynb ├── LGTNumerics.ipynb ├── README.md ├── globalsu2shadowtomography.py ├── makekerneldata.py ├── slurm_createtoptrivstates.sh ├── slurm_globalsu2.sh ├── slurm_kerneldata.sh └── torictrivial_datageneration.cpp /ClassifyPhases.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "id": "377d0558", 7 | "metadata": {}, 8 | "outputs": [], 9 | "source": [ 10 | "#Katherine Van Kirk\n", 11 | "#kvankirk@g.harvard.edu\n", 12 | "\n", 13 | "import numpy as np\n", 14 | "import matplotlib.pyplot as plt\n", 15 | "import copy\n", 16 | "import math\n", 17 | "from math import e\n", 18 | "import random \n", 19 | "import ast\n", 20 | "import time\n", 21 | "import jax\n", 22 | "from neural_tangents import stax\n", 23 | "from sklearn.ensemble import RandomForestRegressor\n", 24 | "import seaborn as sns\n", 25 | "import copy\n", 26 | "\n", 27 | "# Kernel PCA from sklearn\n", 28 | "from sklearn.decomposition import PCA\n", 29 | "\n", 30 | "# Plotting tools\n", 31 | "import pandas as pd\n", 32 | "import seaborn as sns\n", 33 | "import matplotlib.pyplot as plt\n", 34 | "sns.set_style(\"ticks\")" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "id": "a0a38727", 40 | "metadata": {}, 41 | "source": [ 42 | "# Predicting Phases" 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": 2, 48 | "id": "f2937aa9", 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "# Constants\n", 53 | "L = 10 #side length of toric code\n", 54 | "PATCHES = (2*L**2)-(2*L)\n", 55 | "DEPTH = 5\n", 56 | "NSTATES = 100" 57 | ] 58 | }, 59 | { 60 | "cell_type": "markdown", 61 | "id": "41c9631c", 62 | "metadata": {}, 63 | "source": [ 64 | "### Read In Data" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": 3, 70 | "id": "ed23d422", 71 | "metadata": {}, 72 | "outputs": [ 73 | { 74 | "data": { 75 | "text/plain": [ 76 | "[array([[1.17703677, 1.13195697, 1.13019079, ..., 1.13093789, 1.12980571,\n", 77 | " 1.13197226],\n", 78 | " [1.13195697, 1.17650115, 1.13041536, ..., 1.13303876, 1.13289664,\n", 79 | " 1.13084495],\n", 80 | " [1.13019079, 1.13041536, 1.17531591, ..., 1.12813196, 1.12909276,\n", 81 | " 1.12971461],\n", 82 | " ...,\n", 83 | " [1.13093789, 1.13303876, 1.12813196, ..., 2.75748938, 2.63333216,\n", 84 | " 2.63759226],\n", 85 | " [1.12980571, 1.13289664, 1.12909276, ..., 2.63333216, 2.74145815,\n", 86 | " 2.6266508 ],\n", 87 | " [1.13197226, 1.13084495, 1.12971461, ..., 2.63759226, 2.6266508 ,\n", 88 | " 2.74978285]]),\n", 89 | " array([[1.5505255 , 1.38616441, 1.38948081, ..., 1.38877312, 1.38167968,\n", 90 | " 1.384296 ],\n", 91 | " [1.38616441, 1.554554 , 1.38998108, ..., 1.38823428, 1.38886688,\n", 92 | " 1.39002673],\n", 93 | " [1.38948081, 1.38998108, 1.55109015, ..., 1.38310596, 1.38773432,\n", 94 | " 1.3891178 ],\n", 95 | " ...,\n", 96 | " [1.38877312, 1.38823428, 1.38310596, ..., 3.94894263, 1.38585566,\n", 97 | " 1.41560379],\n", 98 | " [1.38167968, 1.38886688, 1.38773432, ..., 1.38585566, 4.5426581 ,\n", 99 | " 1.46962236],\n", 100 | " [1.384296 , 1.39002673, 1.3891178 , ..., 1.41560379, 1.46962236,\n", 101 | " 3.52960788]]),\n", 102 | " array([[1.54950789, 1.39353078, 1.39479712, ..., 1.39277554, 1.3907104 ,\n", 103 | " 1.39106496],\n", 104 | " [1.39353078, 1.55298062, 1.3921119 , ..., 1.39102593, 1.39245469,\n", 105 | " 1.38798776],\n", 106 | " [1.39479712, 1.3921119 , 1.55625108, ..., 1.39208411, 1.39453138,\n", 107 | " 1.39241371],\n", 108 | " ...,\n", 109 | " [1.39277554, 1.39102593, 1.39208411, ..., 2.66616432, 1.40530096,\n", 110 | " 1.37911012],\n", 111 | " [1.3907104 , 1.39245469, 1.39453138, ..., 1.40530096, 2.61468755,\n", 112 | " 1.39539556],\n", 113 | " [1.39106496, 1.38798776, 1.39241371, ..., 1.37911012, 1.39539556,\n", 114 | " 2.46416781]]),\n", 115 | " array([[1.55385421, 1.39540013, 1.39559046, ..., 1.39313885, 1.39798644,\n", 116 | " 1.39469629],\n", 117 | " [1.39540013, 1.55649383, 1.39485133, ..., 1.38907697, 1.39209654,\n", 118 | " 1.38804553],\n", 119 | " [1.39559046, 1.39485133, 1.56883849, ..., 1.3898638 , 1.39459182,\n", 120 | " 1.38902203],\n", 121 | " ...,\n", 122 | " [1.39313885, 1.38907697, 1.3898638 , ..., 1.91342184, 1.40209017,\n", 123 | " 1.3902411 ],\n", 124 | " [1.39798644, 1.39209654, 1.39459182, ..., 1.40209017, 2.16046463,\n", 125 | " 1.42070064],\n", 126 | " [1.39469629, 1.38804553, 1.38902203, ..., 1.3902411 , 1.42070064,\n", 127 | " 2.1318604 ]]),\n", 128 | " array([[1.5570426 , 1.39660129, 1.39251665, ..., 1.39259413, 1.3937969 ,\n", 129 | " 1.3963997 ],\n", 130 | " [1.39660129, 1.56364916, 1.39505964, ..., 1.40003181, 1.39008209,\n", 131 | " 1.39257362],\n", 132 | " [1.39251665, 1.39505964, 1.55693112, ..., 1.39519351, 1.39446689,\n", 133 | " 1.39337122],\n", 134 | " ...,\n", 135 | " [1.39259413, 1.40003181, 1.39519351, ..., 1.71787195, 1.393238 ,\n", 136 | " 1.39056292],\n", 137 | " [1.3937969 , 1.39008209, 1.39446689, ..., 1.393238 , 1.80108175,\n", 138 | " 1.39299887],\n", 139 | " [1.3963997 , 1.39257362, 1.39337122, ..., 1.39056292, 1.39299887,\n", 140 | " 1.83261024]])]" 141 | ] 142 | }, 143 | "execution_count": 3, 144 | "metadata": {}, 145 | "output_type": "execute_result" 146 | } 147 | ], 148 | "source": [ 149 | "gamma = 3 #hyperparameter you have to tune \n", 150 | "\n", 151 | "depthkernels = []\n", 152 | "for dep in range(DEPTH):\n", 153 | " # 1. Read in the expectation value data for all states\n", 154 | " with open('expvals_threesite_{}.npy'.format(dep), 'rb') as f:\n", 155 | " stateexpvals = np.load(f) #took another set of data to use against the diagonal\n", 156 | " \n", 157 | " # 2. Make matrix of inner products between the states \n", 158 | " storeinnerprod = []\n", 159 | " for s1 in range(NSTATES):\n", 160 | " stateinnerprods = []\n", 161 | " for s2 in range(NSTATES):\n", 162 | " if s1 == s2:\n", 163 | " stateinnerprods.append(np.sum(np.multiply(stateexpvals[s1],stateexpvals[s2]),1)) \n", 164 | " else: \n", 165 | " stateinnerprods.append(np.sum(np.multiply(stateexpvals[s1],stateexpvals[s2]),1)) \n", 166 | " storeinnerprod.append(stateinnerprods)\n", 167 | " \n", 168 | " # 3. Save matrix of (normalized) exponentiated inner products for this depth\n", 169 | " storeinnerprod = storeinnerprod/(gamma * np.average(storeinnerprod)) #normalize\n", 170 | " storeinnerprod = np.exp(storeinnerprod) #element-wise exponentiation\n", 171 | " kernelij = np.sum(storeinnerprod,2)/PATCHES #sum over patches\n", 172 | " depthkernels.append(kernelij)\n", 173 | " \n", 174 | "depthkernels" 175 | ] 176 | }, 177 | { 178 | "cell_type": "markdown", 179 | "id": "397f82f2", 180 | "metadata": {}, 181 | "source": [ 182 | "### Project data and predict phases with kernel PCA" 183 | ] 184 | }, 185 | { 186 | "cell_type": "markdown", 187 | "id": "b139e2b8", 188 | "metadata": {}, 189 | "source": [ 190 | "Here, we considered two different ways to project our data prior to considering the 1D representation of the state -- i.e. the first principal component. We projected the data onto the unit sphere (case 1), and we used the length of each data point as each state's signature feature. " 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "id": "f8d11ba6", 196 | "metadata": {}, 197 | "source": [ 198 | "##### 1. PROJECTION ONTO UNIT SPHERE" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": 4, 204 | "id": "fe18f29c", 205 | "metadata": {}, 206 | "outputs": [ 207 | { 208 | "data": { 209 | "image/png": "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6NH44osvkJWVFcfWC/fccw8uvfRS9O3bt9Z933zzDfr27YvMzMwmH1eSJOTm5rZEExtFVVV8/vnnmDZtGgCgT58+WLBgAf7zn//ErA2EkLMDDV8SojOcczz88MP44IMPMH78ePzzn//E+PHjYTCIz1AmkwlTp07FRx99hN69e4eFMgDo2bMnrFZrPJoeRgtPjLGw24uKijBv3jy4XK44taxplixZgg0bNoTdpv0sCCGkJVEoI0Rnli9fji+++AJWqxULFy6EyWSK+LiMjAwsWLCgVigDRE+bXoS2paysDHfddReKi4vj2KLG+7//+z+8+uqr8W4GIeQsQR/3CNGZ1157DQAwffp0pKen1/vYYcOGoXfv3gAAj8eDTZs24bPPPkNeXh5WrVoFQAy/bd++Hf/5z3/QqVMnTJ06Fb/97W9x4sQJLFmyBGPHjgUArFmzBqtWrYLVakVBQQESEhJw7733YtCgQTh58iTeeOMNfPjhh8jJycHatWsBABs3bsSKFSuwatUqjBo1Cu+++y4AoLKyEv/73//w2WefIScnB88++ywA4NVXX8WZM2cAAE888QQsFgtuvvnmQBvqsmLFCvz3v/9FZmYmfD4fRo8eHfFxHo8Hb7zxBnbu3InCwkJUVVVh2rRpeOCBB2C1WqEoCrZu3YrPPvsMVVVVePTRR/H8889j48aNMBgMuPzyy/G73/0OVqsV3333HVasWAHOOb799lvcc889yMjIwNNPPx32nJWVlVi4cCHWrFkDALj44ovx+OOPIyEhod7viRBCaqJQRoiO/PTTTzh9+jQAYMSIEY36Gi3wfP/99zhw4ABWrVqFnJycwP1r167FypUr8b///Q9Tp07FsmXLMHr0aOTl5aGwsBAA8Pzzz+PAgQN45ZVXYLFYYLfbccEFF2Dz5s347LPP0K1bNzz66KP48MMPw577ggsuQNeuXQMBULN582Zs3boV33zzDWbMmBG4/fe//z3y8vKwbds2PPXUU+jatWuD399f/vIXbN68GW+//TYSExMDt9WkqiruvvtuXHzxxXjzzTcBAO+88w6effZZHD16FEuXLsXOnTvx8ccfY9WqVejZsyeeeeYZXHTRRZg4cSLeeustfPjhhzhz5gxef/11nHfeefjVr36Fn//85xgzZgyee+65Ws9pt9sxf/58XHHFFfjZz36GpUuX4uOPP0bXrl1xzz33NPi9EUJIKBq+JERHTpw4Efj/jh07NulrR48ejTvuuKPW7VOmTMH1118PAMjLy8NTTz2F+fPnY/v27bjmmmvw1Vdf4e233w70XAFAYmIirr32WiiKApvNBgAwm80Rn9doNNa6bdq0aZg5c2aT2h/J1q1b8be//Q0PPfRQIJABwJ133lnrsf/6179QWlqKG264IXDb9ddfD4vFgnXr1iEvLw+jRo3CAw88ELj/pZdewqxZszBz5ky8++67SEtLw/r16/Htt982qn2VlZV4+umnMX36dAwfPhyPPvooAGDTpk3RfsuEkLMY9ZQRoiM+ny/w/9HMC6trArosywCAc889NxCitJDz/vvvIycnBz169Aj7mkceeQQPPvhgIKg1lfaczfH2229DluVaw5VJSUm1HrtixQpUVFTU6qHq0aMHPB4PiouL0a9fv0C7srKyws5Xamoqfvazn+HNN9/Eli1bMGbMmAbbl5ycHLaCVOuhbCtz5ggh+kKhjBAdCS1lUVRUFJPn3LNnT531vqINZC3lu+++Q3p6eqMC3oEDBzB58mQsWrQo6ucbMGAAALEgIRpaOxVFiboNhJCzFw1fEqIjQ4cODZSz2L59e0ye0+Fw4NSpUzF5rqaqrq5GRUUFVFVt8LFer7fZQTY1NTXsb0IIiSUKZYToiMlkwuzZswEAX3zxRUx6y7Kzs1FeXo6NGzfWuk9VVezevbvV21CXjIwMeL1eHDhwoMHHZmdn4/vvvw8sXgjFOceKFSsaPIY27Dh8+PAmt5UQQpqLQhkhOnPfffehf//+cLvdeOihh2C32+t8bEFBAebPn9+s4bJLLrkEAPDMM8+EBRpVVfH8888jOTk5cJvVakV5eXnYNk/alk6NbYNWdy1SfbWaJk2aBAB44403wm53u90AwufgXXjhhfB6vZg/f35YYVrOORYsWBBxHlpN69atQ7du3QLPqy1ucDgcDX4tIYQ0F4UyQnQmKSkJy5Ytw/jx47F9+3bMnDkTn3zyCUpLSwGI8HPkyBG88sor+PWvf4277rorMJepqqoq7G+NFlK0lZSh7r77bvTo0QPHjh3D1KlT8fDDD+Pxxx/H9OnTcc4556BXr16Bx44ePRoOhwO//e1v8eWXX+KVV17B2rVrYTQaceDAAaxevToQtqqrqyO2RTve8uXL8eOPP+Ltt9+u81zcd999yMzMxKeffoqnnnoK+/fvx65du/D73/8eBoMB+/btC9Q+mzt3Ljp27IgtW7Zg6tSpeOaZZ7BgwQJcddVVcLvdmDJlStix9+7di127dgX+vXHjRmzZsgUvvPBCYDFEt27dIMsytm3bhl27duHDDz/E4cOHA+dR+x41lZWVAFBvkCaEkLowzjmPdyMIIZFt2LABK1euxPfff4+SkhKYTCakpKRgwIABuOSSSzB9+vTACsINGzZg1apVWLlyJQDghhtuwOzZs3Ho0CG8+eab2LdvHywWC+6++25ceOGF6N+/f+B5SktLsWjRIqxduxZutxuDBg3CvffeW6uoa2FhIR5//HFs374daWlpuP7663HHHXdg9uzZuPjiizFp0iQMGDAAy5cvx0cffYQffvgBVqsVv/zlLzFr1ixkZGTg1KlTuPfee3HixAlceOGFePLJJ5GWllbnOTh58iQWLVqEzZs3w+PxYMSIEZg/fz4eeeQRXHjhhbjiiisCBXRPnToVeKzX60WvXr1www03YObMmZAkKfCYiy++GEOGDEH//v0DxWxNJhMefPDBWvtqvvLKK1i2bBkyMjLw61//Gv369cNrr72GTz/9FAAwe/Zs3H777XA4HHjttdfwxRdfAABuvvlm3HffffV+b4QQEopCGSHkrKKFstAdCAghRA9o+JIQQgghRAcolBFCCCGE6ACFMkLIWUVbSVlRURHfhhBCSA0UygghZ43169fj6aefBiB2AHj66aexbt26+DaKEEL8aKI/IYQQQogOUE8ZIYQQQogOUCgjhBBCCNEBCmWEEEIIITpAoYwQQgghRAcolBFCCCGE6ACFMkIIIYQQHaBQRgghhBCiAxTKCCGEEEJ0gEIZIYQQQogO/D/YoFtw4Ot5sQAAAABJRU5ErkJggg==\n", 210 | "text/plain": [ 211 | "
" 212 | ] 213 | }, 214 | "metadata": {}, 215 | "output_type": "display_data" 216 | } 217 | ], 218 | "source": [ 219 | "from matplotlib.lines import Line2D\n", 220 | "\n", 221 | "plt.rcParams['mathtext.fontset'] = 'stix'\n", 222 | "plt.rcParams['font.family'] = 'STIXGeneral'\n", 223 | "\n", 224 | "# Perform kernel PCA based on shadow kernel\n", 225 | "data = []\n", 226 | "for d in range(DEPTH):\n", 227 | " K = depthkernels[d]\n", 228 | " X = []\n", 229 | " for i in range(len(K)):\n", 230 | " single_X = []\n", 231 | " for j in range(len(K)):\n", 232 | " single_X.append(K[i][j] / ((K[i][i] * K[j][j]) ** 0.5))\n", 233 | " X.append(single_X)\n", 234 | " \n", 235 | " X = np.array(X)\n", 236 | " pca = PCA(n_components=1)\n", 237 | " F = pca.fit_transform(X)\n", 238 | " std = np.std(F)\n", 239 | " \n", 240 | " for z in range(len(X)):\n", 241 | " data.append((-F[z, 0] / std, d, \"Topological\" if z < 50 else \"Trivial\"))\n", 242 | "\n", 243 | "# Plot the 1D representation for different circuit depth\n", 244 | "plt.figure(figsize=(5.0, 2.5))\n", 245 | "\n", 246 | "df_toric = pd.DataFrame(data=data, columns = ['PC1', 'Depth', 'Phase'])\n", 247 | "ax = sns.stripplot(x=\"Depth\", y=\"PC1\", hue='Phase', data=df_toric[df_toric[\"Phase\"]==\"Topological\"], palette=['#EE857D'], orient=\"v\", edgecolor=\"black\", marker=\"o\", s=10, alpha=0.85, jitter=0.22)\n", 248 | "ax = sns.stripplot(x=\"Depth\", y=\"PC1\", hue='Phase', data=df_toric[df_toric[\"Phase\"]==\"Trivial\"], palette=['#6D95F8'], orient=\"v\", edgecolor=\"black\", marker=\"D\", s=10, alpha=0.55, jitter=0.22)\n", 249 | "\n", 250 | "# Update the legend oreder\n", 251 | "handles, labels = ax.get_legend_handles_labels()\n", 252 | "handles = [Line2D([], [], color=h.get_facecolor(), linestyle='', marker=\"D\" if l=='Trivial' else \"o\") for h, l in zip(handles, labels)]\n", 253 | "labels, handles = zip(*sorted(zip(labels, handles)))\n", 254 | "\n", 255 | "ax.legend( handles,labels,bbox_to_anchor=(1.0, 0.5),ncol = 1,loc=\"center left\")\n", 256 | "\n", 257 | "ax.set_xlabel(\"Circuit depth\", fontsize = 16)\n", 258 | "ax.set_ylabel(\"1st princ.\\n comp.\", fontsize = 16)\n", 259 | "ax.tick_params(labelsize=15)\n", 260 | "ax.set_ylim(-4.0, 4.0);\n", 261 | "\n", 262 | "plt.show()" 263 | ] 264 | }, 265 | { 266 | "cell_type": "markdown", 267 | "id": "c84c9e02", 268 | "metadata": {}, 269 | "source": [ 270 | "##### 2. USING ONLY LENGTH AS FEATURE" 271 | ] 272 | }, 273 | { 274 | "cell_type": "code", 275 | "execution_count": 5, 276 | "id": "7e05e25e", 277 | "metadata": {}, 278 | "outputs": [ 279 | { 280 | "data": { 281 | "image/png": "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hBBCSAdkiHYDYoHT6cSSJUvw+uuvh/29LpcLLpfL93V1dTUAwGaztVr7CCGExBbtGkCr+EkgCmXNsHjxYtxzzz2Ij48P+3uXLl2KJUuW1Lt9ypQprdE0QgghMcxutyMxMTHazSA60elC2YIFC/Dpp58267GyLOPdd99FUVERcnJyUFxc7LuvpqYGAHy3paWlQZbleseYN28e7rjjDt/XqqqisrISKSkpYIy15EdpVTabDVOmTMH69euRkJAQ7ebEDDpv4aNzFhk6b+HT8znjnMNutyMzMzPaTSE60ulC2Zw5czB27NhmPVaSJHz33Xf48ssv8eWXX4Z8zKRJkwAA3377LXr06FHvfpPJBJPJFHRbUlJSmK1uPwkJCbp784oFdN7CR+csMnTewqfXc0Y9ZKSuThfKxowZgzFjxjT78SdPnsR5551X7/Z//vOf2LJlC/7+978DADIyMlqtjYQQQgjpfDpdKAtX79690bt373q3r1q1CgAwefLk9m4SIYQQQjogKolBAIhh1gceeKDeUCtpHJ238NE5iwydt/DROSOxhnFajxsRbcFAXl5etJtCCCGEkA6AQhkhhBBCiA7Q8CUhhBBCiA5QKCOkBUpLS/Hss88G1bAjhOjLX//6V9xyyy3RbgYhTaLVlwQ2mw1//etf4XA4IEkSCgoKcN9992H06NHRbppu2e12LFu2DMuWLYPD4cDtt98e7SbFhA0bNuD111/H/v37YTQacf7552P+/Pno379/tJumaydPnsRLL72EnTt3QlVVjBkzBgsWLEDPnj2j3TTdO3DgAN58802MGjUq2k0hpEnUU9bJ2e123HzzzXA4HHj22Wfx9NNP4+c//zluu+027NixI9rN0628vDzceuutGD58eLSbEjO2bNmChx56CCkpKZg6dSosFgvWrl2LG2+8EQUFBdFunm7l5+fjgQcewODBg/H444/j6quvxrp163DXXXfB7XZHu3m65na7sXjxYl0WjiUkFOop6+ReeeUV5OXlYenSpb7bJk6ciJEjR+Kxxx7DqlWrYDQao9hCffrJT34CAOjevXuUWxIbOOdYtGgRPv30U/Tt2xcA4HA48OCDD2LTpk14++238fjjj0e5lfr0/vvv480330S3bt0AALNmzYKiKHj//fdx9OhR5ObmRrmF+rV06VLccMMNOHr0aLSbQkizUE9ZJ+Z0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YNy4cSEf53K58NZbb2HXrl04d+4cqqqqMHPmTDz00EOwWq1QFAXbtm3DF198gaqqKjz22GN48cUXsXHjRhgMBlx++eX43e9+B6vViv/9739YsWIFOOfYunUr7rvvPqSnp+OZZ54Jes7KykosXLgQq1evBgBcfPHFeOKJJ2CxWBr9mQghpC4KZYToyMGDB3HmzBkAwOjRo5v1PVrg+f7773Ho0CGsXLkS2dnZvvvXrFmDzz77DF9//TVmzJiBZcuWYdy4ccjLy8O5c+cAAC+++CIOHTqE1157DWazGXa7HRdeeCE2b96ML774Aj179sRjjz2Gjz76KOi5L7zwQvTo0cMXADWbN2/Gtm3b8N133+Haa6/13f773/8eeXl52L59O55++mn06NGjyZ/vL3/5CzZv3ox33nkH8fHxvtvqUlUV9957Ly6++GL84x//AAC8++67eO6553D8+HEsXboUu3btwieffIKVK1eiT58+ePbZZ3HRRRdh8uTJePvtt/HRRx/h7NmzePPNN/GTn/wEv/zlL3Hrrbdi/PjxeOGFF+o9p91ux4IFC3DFFVfgpz/9KZYuXYpPPvkEPXr0wH333dfkz0YIIYFo+JIQHTl16pTvv7t06RLW944bNw533XVXvdunT5+OG2+8EQCQl5eHp59+GgsWLMCOHTtwzTXX4Ntvv8U777zj67kCgPj4eFx33XVQFAU2mw0AEBcXF/J5jUZjvdtmzpyJ2bNnh9X+ULZt24a//e1v+PWvf+0LZABw991313vsv//9b5SWluKmm27y3XbjjTfCbDZj3bp1yMvLw9ixY/HQQw/57n/llVcwZ84czJ49G++99x5SUlKwfv16bN26tVntq6ysxDPPPINZs2Zh1KhReOyxxwAAmzZtivRHJoR0YtRTRoiOeDwe339HMi+soQnosiwDAM477zxfiNJCzgcffIDs7Gz07t076HseffRRPPzww76gFi7tOVvinXfegSzL9YYrExIS6j12xYoVqKioqNdD1bt3b7hcLhQXF2PQoEG+dmVmZgadr+TkZPz0pz/FP/7xD2zZsgXjx49vsn2JiYlBK0i1HspYmTNHCNEXCmWE6EhgKYuioqJ2ec69e/c2WO8r0kDWWv73v/8hLS2tWQHv0KFDmDZtGhYtWhTx8w0ePBiAWJAQCa2diqJE3AZCSOdFw5eE6MiIESN85Sx27NjRLs/pcDiQn5/fLs8VrurqalRUVEBV1SYf63a7Wxxkk5OTg/6fEELaE4UyQnTEZDLhhhtuAAB89dVX7dJblpWVhfLycmzcuLHefaqqYs+ePW3ehoakp6fD7Xbj0KFDTT42KysL33//vW/xQiDOOVasWNHkMbRhx1GjRoXdVkIIaSkKZYTozAMPPIDc3FzU1tbi17/+Nex2e4OPLSwsxIIFC1o0XHbJJZcAAJ599tmgQKOqKl588UUkJib6brNarSgvLw/a5knb0qm5bdDqroWqr1bXlClTAABvvfVW0O21tbUAgufgTZ06FW63GwsWLAgqTMs5x/PPPx9yHlpd69atQ8+ePX3Pqy1ucDgcTX4vIYS0FIUyQnQmISEBy5Ytw8SJE7Fjxw7Mnj0bn376KUpLSwGI8HPs2DG89tpr+NWvfoV77rnHN5epqqoq6P81WkjRVlIGuvfee9G7d2+cOHECM2bMwCOPPIInnngCs2bNwoABA9C3b1/fY8eNGweHw4Hf/va3+Oabb/Daa69hzZo1MBqNOHToEFatWuULW9XV1SHboh1v+fLl+PHHH/HOO+80eC4eeOABZGRk4PPPP8fTTz+NAwcOYPfu3fj9738Pg8GA/fv3+2qfzZs3D126dMGWLVswY8YMPPvss3j++edx1VVXoba2FtOnTw869r59+7B7927f1xs3bsSWLVvw0ksv+RZD9OzZE7IsY/v27di9ezc++ugjHD161HcetZ9RU1lZCQCNBmlCCGkI45zzaDeCEBLahg0b8Nlnn+H7779HSUkJTCYTkpKSMHjwYFxyySWYNWuWbwXhhg0bsHLlSnz22WcAgJtuugk33HADjhw5gn/84x/Yv38/zGYz7r33XkydOhW5ubm+5yktLcWiRYuwZs0a1NbWYujQobj//vvrFXU9d+4cnnjiCezYsQMpKSm48cYbcdddd+GGG27AxRdfjClTpmDw4MFYvnw5Pv74Y/zwww+wWq34xS9+gTlz5iA9PR35+fm4//77cerUKUydOhVPPfUUUlJSGjwHp0+fxqJFi7B582a4XC6MHj0aCxYswKOPPoqpU6fiiiuu8BXQzc/P9z3W7Xajb9++uOmmmzB79mxIkuR7zMUXX4zhw4cjNzfXV8zWZDLh4Ycfrrev5muvvYZly5YhPT0dv/rVrzBo0CC88cYb+PzzzwEAN9xwA+688044HA688cYb+OqrrwAAt9xyCx544IFGfzZCCAlEoYwQ0qlooSxwBwJCCNEDGr4khBBCCNEBCmWEEEIIITpAoYwQ0qloKykrKiqi2xBCCKmDQhkhpNNYv349nnnmGQBiB4BnnnkG69ati26jCCHEiyb6E0IIIYToAPWUEUIIIYToAIUyQgghhBAdoFBGCCGEEKIDFMoIIYQQQnSAQhkhhBBCiA5QKCOEEEII0QEKZYQQQgghOkChjBBCCCFEByiUEUIIIYTowP8HeRQYca1vdNIAAAAASUVORK5CYII=\n", 282 | "text/plain": [ 283 | "
" 284 | ] 285 | }, 286 | "metadata": {}, 287 | "output_type": "display_data" 288 | } 289 | ], 290 | "source": [ 291 | "# Perform kernel PCA based on shadow kernel\n", 292 | "data = []\n", 293 | "for d in range(DEPTH):\n", 294 | " K = depthkernels[d] \n", 295 | " X = np.array(np.diag(K).reshape(-1, 1)) #reshape cause 1d feature \n", 296 | " pca = PCA(n_components=1)\n", 297 | " F = pca.fit_transform(X)\n", 298 | " std = np.std(F)\n", 299 | " \n", 300 | " for z in range(len(X)):\n", 301 | " data.append((-F[z, 0] / std, d, \"Topological\" if z < 50 else \"Trivial\"))\n", 302 | "\n", 303 | " \n", 304 | "# Plot the 1D representation for different circuit depth\n", 305 | "plt.figure(figsize=(5.0, 2.5))\n", 306 | "\n", 307 | "df_toric = pd.DataFrame(data=data, columns = ['PC1', 'Depth', 'Phase'])\n", 308 | "ax = sns.stripplot(x=\"Depth\", y=\"PC1\", hue='Phase', data=df_toric[df_toric[\"Phase\"]==\"Topological\"], palette=['#EE857D'], orient=\"v\", edgecolor=\"black\", marker=\"o\", s=10, alpha=0.85, jitter=0.22)\n", 309 | "ax = sns.stripplot(x=\"Depth\", y=\"PC1\", hue='Phase', data=df_toric[df_toric[\"Phase\"]==\"Trivial\"], palette=['#6D95F8'], orient=\"v\", edgecolor=\"black\", marker=\"D\", s=10, alpha=0.55, jitter=0.22)\n", 310 | "\n", 311 | "# Update the legend oreder\n", 312 | "handles, labels = ax.get_legend_handles_labels()\n", 313 | "handles = [Line2D([], [], color=h.get_facecolor(), linestyle='', marker=\"D\" if l=='Trivial' else \"o\") for h, l in zip(handles, labels)]\n", 314 | "labels, handles = zip(*sorted(zip(labels, handles)))\n", 315 | "\n", 316 | "ax.legend( handles,labels,bbox_to_anchor=(1.0, 0.5),ncol = 1,loc=\"center left\")\n", 317 | "\n", 318 | "ax.set_xlabel(\"Circuit depth\", fontsize = 16)\n", 319 | "ax.set_ylabel(\"1st princ.\\n comp.\", fontsize = 16)\n", 320 | "ax.tick_params(labelsize=15)\n", 321 | "ax.set_ylim(-4.0, 4.0);\n", 322 | "\n", 323 | "plt.show()" 324 | ] 325 | }, 326 | { 327 | "cell_type": "code", 328 | "execution_count": null, 329 | "id": "76c3907f", 330 | "metadata": {}, 331 | "outputs": [], 332 | "source": [] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": null, 337 | "id": "a9abd646", 338 | "metadata": {}, 339 | "outputs": [], 340 | "source": [] 341 | } 342 | ], 343 | "metadata": { 344 | "kernelspec": { 345 | "display_name": "Python 3", 346 | "language": "python", 347 | "name": "python3" 348 | }, 349 | "language_info": { 350 | "codemirror_mode": { 351 | "name": "ipython", 352 | "version": 3 353 | }, 354 | "file_extension": ".py", 355 | "mimetype": "text/x-python", 356 | "name": "python", 357 | "nbconvert_exporter": "python", 358 | "pygments_lexer": "ipython3", 359 | "version": "3.9.5" 360 | } 361 | }, 362 | "nbformat": 4, 363 | "nbformat_minor": 5 364 | } 365 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hardware-efficient learning of quantum many-body states. 2 | This is an open source implementation for the paper "Hardware-efficient learning of quantum many-body states." 3 | 4 | We require g++ and python version 3. 5 | 6 | ### Quick Start 7 | Do the following in the terminal. 8 | 9 | 10 | ``` 11 | # 12 | # Python code "LGTNumerics.ipynb" is used for estimating the energy density of a lattice gauge theory, 13 | # and all remaining code is used for classifying topological phases. 14 | # 15 | 16 | ## APPLICATION I. ESTIMATING ENERGY DENSITY OF LATTICE GAUGE THEORY 17 | ## Open the LGT iPython notebook: LGTNumerics.ipynb. 18 | > jupyter notebook 19 | 20 | 21 | ## APPLICATION II. CLASSIFYING TOPOLOGICAL PHASES 22 | ## The data for this application was generated and manipulated on the Harvard cluster. Therefore, the 23 | ## numerics were performed in batches. The first three steps were data generation/manipulation (slurm 24 | ## files call the cpp and python code). For the last step, open the phases iPython notebook: 25 | ## ClassifyPhases.ipynb. Finally, while the code below is written for a cluster, this is not required. 26 | ## One can simply run the commands in the slurm files directly on the terminal and modify the .py 27 | ## codes to require fewer batches. 28 | 29 | > sbatch slurm_createtoptrivstates.sh # Step 1. Create topological & trivial states. 30 | > sbatch slurm_globalsu2.sh # Step 2. Perform global su2 shadow tomography. 31 | > sbatch slurm_kerneldata.sh # Step 3. Collect all global shadow data into shadow kernel. 32 | > jupyter notebook # Step 4. Kernel PCA to predict phases. 33 | 34 | ``` 35 | -------------------------------------------------------------------------------- /globalsu2shadowtomography.py: -------------------------------------------------------------------------------- 1 | #Katherine Van Kirk 2 | #kvankirk@g.harvard.edu 3 | 4 | # PACKAGES 5 | # -------- 6 | 7 | import numpy as np 8 | from numpy import linalg 9 | import matplotlib.pyplot as plt 10 | import copy 11 | import math 12 | from math import e 13 | import random 14 | import ast 15 | import time 16 | import scipy.sparse 17 | from scipy.sparse import csr_matrix, csc_matrix 18 | from scipy.sparse.linalg import svds, eigs 19 | from itertools import permutations 20 | from scipy.stats import unitary_group 21 | from numpy.random import choice 22 | from scipy.linalg import null_space, expm 23 | from scipy.linalg import norm 24 | from scipy.stats import unitary_group 25 | from sklearn.model_selection import train_test_split 26 | from sklearn import datasets 27 | from sklearn import svm 28 | import jax 29 | from neural_tangents import stax 30 | from sklearn.ensemble import RandomForestRegressor 31 | import seaborn as sns 32 | import copy 33 | import sys 34 | 35 | # A. Helper Functions for Creating Efficient Representations of 'Bi's 36 | # ------------------------------------------------------------------- 37 | # 'Efficient Representation' definition: For a Bi, take all the pauli strings in the Bi's sum and put them in 38 | # an array. For example, represent XY + YX by ['XY','YX'] 39 | 40 | # DESCRIPTION: Insert 'elem' in the string at site i 41 | def str_insert(i,elem,string): 42 | return string[:i] + elem + string[i:] 43 | 44 | # DESCRIPTION: Returns array of permutations of 'string.' When any of n_x, n_y, x_z, or n_I is greater than 1, 45 | # just permuting the string will yield duplicates-- the below removes duplicates. 46 | def string_permute(string): 47 | return list(set(["".join(perm) for perm in permutations(string)])) 48 | 49 | # DESCRIPTION: From a string of X,Y,Z,I paulis, create the corresponding B_i element. This element in the form 50 | # of an array-- each element in the array is a different pauli string A_k within the sum that makes up B_i. 51 | def string_permute_non_is(string): 52 | #permutes all non_Is by removing Is 53 | np_str = np.array(list(string)) 54 | I_mask = np_str == "I" 55 | other = np_str[~I_mask] 56 | perms = string_permute("".join(other)) 57 | 58 | #place Is back in and return 59 | new = [] 60 | for perm in perms: 61 | new_elem = "" 62 | j = 0 63 | for is_I in I_mask: 64 | if is_I: 65 | new_elem += "I" 66 | else: 67 | new_elem += perm[j] 68 | j += 1 69 | new.append(new_elem) 70 | return new 71 | 72 | # DESCRIPTION: Given a set of Bis for some system size n, but the set has duplicates. Here we remove the (unwanted) 73 | # duplicates. 74 | def de_duplicate(B): 75 | arr = [",".join(sorted(x)) for x in B] 76 | arr = list(set(arr)) 77 | return [elem.split(",") for elem in sorted(arr)] 78 | 79 | # DESCRIPTION: Counts size of visible space for a n-qubit system 80 | def visible_space_size(n): 81 | return int((2**(n-3)) * (n**2 + 7*n + 8)) 82 | 83 | # DESCRIPTION: Given a n-1 length string b, add an identity to every location. Of course, for bs like XXII this 84 | # creates redundancy. However, this is ok-- we will remove redundancy later. 85 | def add_identity(b,n): 86 | new = [] 87 | for i in range(n): 88 | x = copy.deepcopy(b) 89 | b2 = [str_insert(i,"I",x_comp) for x_comp in x] 90 | new.append(b2) 91 | return new 92 | 93 | # DESCRIPTION: Given a n-1 length string b, add matrix (X, Y or Z) to every location. Since this is non-I, we must 94 | # also permute the XYZ elements after adding a new one to the set. Then we return the corresponding set of new, 95 | # length-n bis. 96 | def add_non_identity(b,matrix,n): 97 | new = [] 98 | for i in range(n): 99 | x_first = copy.deepcopy(b[0]) 100 | new_first = str_insert(i,matrix,x_first) 101 | new.append(string_permute_non_is(new_first)) 102 | return new 103 | 104 | # DESCRIPTION: Creates the visible basis in the efficient, string format defined at the start of this section. The 105 | # only input is the system size "n" 106 | def visible_basis_strings(n): 107 | # a. Create the sets of pauli strings corresponding to each Bi. Build up to Bis for system size n by using Bis for 108 | # smaller system sizes. 109 | B = {1: ["I","X","Y","Z"]} 110 | for sz in range(2,n+1): 111 | Bsz = [] 112 | for bi in B[sz-1]: 113 | for matrix in ["I","X","Y","Z"]: 114 | if matrix == "I": 115 | #add it to every spot 116 | Bsz.extend(add_identity(bi,2)) 117 | else: 118 | Bsz.extend(add_non_identity(bi,matrix,2)) 119 | B[sz] = de_duplicate(Bsz) 120 | return B[n] 121 | 122 | 123 | # B. Helper Functions for Evaluating our 'Bi' Quantity of Interest 124 | # ---------------------------------------------------------------- 125 | 126 | # DESCRIPTION: Given a specific pauli string in some bi. Using this string (in the form of a string object), this 127 | # function constructs the corresponding matrix. 128 | def pauli_string_to_matrix(string): 129 | # Make a vector of the pauli matrices 130 | chars = list(string) 131 | flow = [] 132 | for i in chars: 133 | if i == "I": 134 | flow.append(I) 135 | if i == "X": 136 | flow.append(X) 137 | if i == "Y": 138 | flow.append(Y) 139 | if i == "Z": 140 | flow.append(Z) 141 | res = flow[0] 142 | 143 | # Tensor them together 144 | for j in range(1,len(flow)): 145 | res = np.kron(res,flow[j]) 146 | 147 | return res 148 | 149 | # DESCRIPTION: Given a specific bi in the form for example bi = [IIXXY, IIXYX, IIYXX]. For each internal string 150 | # in the bi sum, you construct the matrix (using pauli_string_to_matrix) and then add them together. 151 | def bi_to_matrix(bi): 152 | res = pauli_string_to_matrix(bi[0]) 153 | n = len(bi[0]) 154 | for i in range(1,len(bi)): 155 | res += pauli_string_to_matrix(bi[i]) 156 | 157 | return math.sqrt(1/(2**n * len(bi))) * res 158 | 159 | # DESCRIPTION: Return the visible basis for a system size n. The format of the returned data structure is a list 160 | # of sparse csr matricies. 161 | # B is dictionary of all possible bi's for a set of system sizes n=1,...,9 (n is the key) 162 | def visible_basis(n): 163 | # a. Create the sets of pauli strings corresponding to each Bi. Build up to Bis for system size n by using Bis for 164 | # smaller system sizes. 165 | B = {1: ["I","X","Y","Z"]} 166 | for sz in range(2,n+1): 167 | Bsz = [] 168 | for bi in B[sz-1]: 169 | for matrix in ["I","X","Y","Z"]: 170 | if matrix == "I": 171 | #add it to every spot 172 | Bsz.extend(add_identity(bi,2)) 173 | else: 174 | Bsz.extend(add_non_identity(bi,matrix,2)) 175 | B[sz] = de_duplicate(Bsz) 176 | 177 | # b. Generate the actual matrices for n and return. 178 | vis = [csr_matrix(bi_to_matrix(bi)) for bi in B[n]] 179 | return vis 180 | 181 | # DESCRIPTION: Given a density matrix rho and the systemsize, we calculate the visible space expectation values. 182 | # Note that the visible basis is in CSR sparse format, and rho should be passed in as such too. 183 | def visible_expvals(syssize, rho, vbasis): 184 | #print([x.todense() for x in vbasis]) 185 | return [np.real(np.trace((rho*bi).todense())) for bi in vbasis] 186 | 187 | 188 | # C. Implementing M Channel on Blocked Subspaces 189 | # ---------------------------------------------- 190 | # Condition for being in the same blocked subspace: 191 | # 1. all I must be on same sites 192 | # 2. n_i + n_j = even set 193 | # --> (n_{i,x} + n_{j,x}, n_{i,y} + n_{j,y}, n_{i,z} + n_{j,z}) = (even number, even number, even number) 194 | 195 | # DESCRIPTION: Given one of the terms within a Bi operator (e.g. given XYI for Bi = XYI+YXI), this function 196 | # returns a list of the indices where there is an identity. 197 | # example: find_identity('XYII') = [2,3] 198 | def find_identity(bi): 199 | return [i for i, ltr in enumerate(bi) if ltr == 'I'] 200 | 201 | # DESCRIPTION: Given two terms within two operators B1 and B2 (e.g. a term for Bi = XYI+YXI could be XYI), function 202 | # returns whether or not the corresponding B1 and B2 have I on the same sites. 203 | def identity_same_sites(b1, b2): 204 | return find_identity(b1) == find_identity(b2) 205 | 206 | # DESCRIPTION: Given a term within Bi (e.g. XYI for Bi = XYI+YXI), return tuple of parameters counting the 207 | # occurences of various paulis: (nI, nX, nY, nZ) 208 | def nvector(bi): 209 | nI = len([i for i, ltr in enumerate(bi) if ltr == 'I']) 210 | nX = len([i for i, ltr in enumerate(bi) if ltr == 'X']) 211 | nY = len([i for i, ltr in enumerate(bi) if ltr == 'Y']) 212 | nZ = len([i for i, ltr in enumerate(bi) if ltr == 'Z']) 213 | return (nI, nX, nY, nZ) 214 | 215 | # DESCRIPTION: Given two terms within two operators B1 and B2 (e.g. a term for Bi = XYI+YXI could be XYI), 216 | # function checks whether the n-tuples corresponding to B1 and B2 sum to an all-even tuple. 217 | def even_combined_components(b1, b2): 218 | (n1I, n1X, n1Y, n1Z) = nvector(b1) 219 | (n2I, n2X, n2Y, n2Z) = nvector(b2) 220 | if (n1X + n2X)%2 == 1: return False 221 | if (n1Y + n2Y)%2 == 1: return False 222 | if (n1Z + n2Z)%2 == 1: return False 223 | 224 | return True 225 | 226 | # DESCRIPTION: Given two terms within two operators B1 and B2 (e.g. a term for Bi = XYI+YXI could be XYI), 227 | # function checks whether the corresponding B1 and B2 live within the same block. 228 | def in_same_block(b1, b2): 229 | return identity_same_sites(b1, b2) and even_combined_components(b1, b2) 230 | 231 | # DESCRIPTION: Given system size n, return a list of blocks. The structure of this list will be as follows: 232 | # - each each element in the block list will be a list of the Bis living in the block 233 | # - each Bi is represented by a list of the pauli strings that make it up 234 | def create_blocks(n): 235 | #A. Make list of blocks 236 | blockdict = [] 237 | 238 | for Bi in visible_basis_strings(n): 239 | b1 = Bi[0] 240 | appended = False 241 | 242 | # i. check if current Bi lives in any of the existing blocks 243 | for x in range(len(blockdict)): 244 | block = blockdict[x] 245 | b2 = block[0][0] 246 | if in_same_block(b1,b2): 247 | block.append(Bi) 248 | blockdict[x] = block 249 | appended = True 250 | 251 | # ii. if current Bi does not live in any existing block, add to new block 252 | if appended == False: 253 | blockdict.append([Bi]) 254 | 255 | #B. Return list of blocks 256 | return blockdict 257 | 258 | # DESCRIPTION: Given two terms within two operators B1 and B2 (e.g. a term for Bi = XYI+YXI could be XYI), 259 | # calculate the c super-matrix element between the two Bi operators. 260 | def cmatrix(b1,b2): 261 | (n1I, n1X, n1Y, n1Z) = nvector(b1) 262 | (n2I, n2X, n2Y, n2Z) = nvector(b2) 263 | k = int((n1X+n1Y+n1Z+n2X+n2Y+n2Z)/2) 264 | cnumerator = 2*math.factorial(k)*math.factorial(k+1)*math.factorial(n1X+n2X)*math.factorial(n1Y+n2Y)*math.factorial(n1Z+n2Z) 265 | cdenominator = math.factorial(2*k+2) * \ 266 | math.factorial((n1X+n2X)//2) * \ 267 | math.factorial((n1Y+n2Y)//2) * \ 268 | math.factorial((n1Z+n2Z)//2) * \ 269 | math.sqrt(math.factorial(n1X) * \ 270 | math.factorial(n2X)*math.factorial(n1Y) * \ 271 | math.factorial(n2Y)*math.factorial(n1Z) * \ 272 | math.factorial(n2Z)) 273 | return cnumerator/cdenominator 274 | 275 | # DESCRIPTION: Given a block of the M superoperator, return a list of the eigenvalues on that block 276 | # note that all evals are real and positive 277 | def evals_of_block(block): 278 | #make the c matrix for the block 279 | cmat = np.zeros((len(block),len(block))) 280 | for r in range(len(block)): 281 | for c in range(len(block)): 282 | cmat[r][c] = cmatrix(block[r][0],block[c][0]) 283 | #diagonalize to get evals 284 | return np.real(linalg.eigvals(cmat)) 285 | 286 | # DESCRIPTION: Given a block of the M superoperator, return the eigendecompositon on that block in terms of 287 | # a tuple: (eigenvalues, eigenvectors) 288 | # note that all evals are real and positive 289 | def eigdecomp_of_block(block): 290 | #make the c matrix for the block 291 | cmat = np.zeros((len(block),len(block))) 292 | for r in range(len(block)): 293 | for c in range(len(block)): 294 | cmat[r][c] = cmatrix(block[r][0],block[c][0]) 295 | #diagonalize to get evals 296 | vals, vecs = linalg.eigh(cmat) # EIG DOES NOT RETURN ORTHONORMAL VECS UGHHHHH.e 297 | return (np.real(vals),vecs) 298 | 299 | # DESCRIPTION: Given system size n, return a list of all eigenvalues of the M superoperator. 300 | def evals_of_visible(n): 301 | blocks = create_blocks(n) 302 | evals = [] 303 | for block in blocks: 304 | evals.extend(evals_of_block(block)) # add evals from each block 305 | return np.sort(evals) 306 | 307 | # DESCRIPTION: Given system size n, you have some set of eigenvalues. Chop off the smallest percent p (e.g. p = 0.1) 308 | # corresponds to chopping off smallest 10%. Then return the smallest eigenvalues of the NEW set. 309 | def chop_smallest_evals(n, p): 310 | allevals = evals_of_visible(n) #these are returned sorted smallest-->largest 311 | sizeevals = visible_space_size(n) 312 | indexnewsmallest = int(np.floor(sizeevals*p)) 313 | return allevals[indexnewsmallest] 314 | 315 | # DESCRIPTION: Calculate the variance of each eigenvector of the M channel (i.e. 1/eval for the corresponding) 316 | # eigenvector. 317 | def average_variance_visible(n): 318 | evals = evals_of_visible(n) 319 | var = [1/ev for ev in evals] 320 | return sum(var)/len(var) 321 | 322 | # DESCRIPTION: Given a list of Bis and the corresponding weights of those Bis (i.e. the values in vec), contruct 323 | # the eigenvector operators of the M channel. 324 | # block = [B1, B2, ...] 325 | # vec = [amount of B1, amount of B2, ...] 326 | def make_evec(block, vec, n): 327 | evecOp = np.zeros((2**n,2**n)) 328 | for x in range(len(block)): 329 | bi = bi_to_matrix(block[x]) 330 | evecOp = np.add(evecOp, np.multiply(vec[x],bi)) 331 | return evecOp 332 | 333 | # DESCRIPTION: Gives the eigenvalus and eigenvectors of the M channel. This serves as setting up the M^-1 channel 334 | # because we can invert via multiplying evec components by 1/eval 335 | # NOTE: output looks like... [(eval1, evec1), (eval2, evec2), ... ] 336 | def setup_MChannel_EvalEvecs(n): 337 | blocks = create_blocks(n) 338 | mChann = [] 339 | for block in blocks: 340 | vals, vecs = eigdecomp_of_block(block) 341 | vecs = np.transpose(vecs) #new 342 | #print('\n',block) 343 | #print('eigenvalues: ', vals) 344 | #print('eigenvectors: ', vecs) 345 | for x in range(len(vals)): 346 | mChann.append((vals[x], np.matrix(make_evec(block, vecs[x],n)))) #tuple of val/vec 347 | return mChann 348 | 349 | # DESCRIPTION: Given a state \rho (de-densified), returns M^{-1}(\rho). You should pass in the (eval, evec) 350 | # form of the M channel, which was generated with the 'setup_MChannel_EvalEvecs' function. 351 | def implement_inverseMChannel(rho, mChann, n, val_threshold = 0): 352 | vals = np.array([entry[0] for entry in mChann if entry[0] > val_threshold]) 353 | vecs = np.stack([np.array(entry[1]) for entry in mChann if entry[0] > val_threshold]) 354 | rho = np.array(rho).reshape(1,2**n,2**n) 355 | coeffs = np.trace(rho @ vecs, axis1=1,axis2=2)/vals 356 | coeffs = coeffs.reshape(-1,1,1) 357 | 358 | rhoprime = np.sum(coeffs * vecs,axis = 0) 359 | return rhoprime 360 | 361 | # DESCRIPTION: Given a state \rho (de-densified), returns M(\rho). You should pass in the (eval, evec) 362 | # form of the M channel, which was generated with the 'setup_MChannel_EvalEvecs' function. 363 | def implement_MChannel(rho, mChann, n): 364 | rhoprime = np.zeros((2**n,2**n)) 365 | for (val, vec) in mChann: 366 | if val > 0.1: #NEW 11/9: for n=5, threshhold cuts off 25% smallest evals 367 | coeff = np.trace(np.matrix(rho)*np.matrix(vec)) 368 | coeff = coeff*val 369 | rhoprime = np.add(rhoprime, np.multiply(coeff, vec)) 370 | return rhoprime 371 | 372 | 373 | # D. Perform globalsu2 shadow tomography 374 | # -------------------------------------- 375 | 376 | 377 | # DESCRIPTION: Generates a random su2 unitary and tensors it n times for an n-qubit system 378 | def random_globalsu2(n): 379 | V = unitary_group.rvs(2) #U(2) 380 | Vtensorn = V 381 | for x in range(n-1): # Kronecker n times 382 | Vtensorn = np.kron(Vtensorn, V) 383 | return Vtensorn 384 | 385 | # DESCRIPTION: this function returns the chosen computational basis element found via measurement (It returns the 386 | # chosen vector in the form of the computational basis element's index). 387 | # GIVEN-- rho in the form of a array of rho's diagonal elements 388 | def measurement(n, rho): 389 | #bprobs = np.real(rho) #original 390 | positiverho = np.array(rho) 391 | positiverho[positiverho < 0] = 0 392 | bprobs = np.abs(positiverho)/np.sum(np.abs(positiverho)) #the classical shadow from random pauli data won't be perfect 393 | bvecs = range(2**n) 394 | draw = choice(bvecs, 1, p = bprobs) 395 | return draw[0] 396 | 397 | # DESCRIPTION: returns non-sparse density matrix for the b> return a vector of shadows where each shadow corresponds to a different patch 481 | def dataVecToShadowPatches(datavec): 482 | # PBC so edges are fine 483 | shadowPatches = [getShadow(datavec, pat) for pat in range(PATCHES)] 484 | return shadowPatches 485 | 486 | # DESCRIPTION: This function takes in an array containing many datavectors of a single state. This function adds 487 | # up the shadows made for each datavector (datavector = data from single measurement), where the shadows are added 488 | # up patch-wise. 489 | def makeNmmtShadowPatchesPerState(state): 490 | shadowsOfState = [] 491 | for ind, datavec in enumerate(state): 492 | if ind == 0: 493 | shadowsOfState = dataVecToShadowPatches(np.transpose(datavec)) 494 | else: 495 | shadowsOfState = np.add(shadowsOfState, dataVecToShadowPatches(np.transpose(datavec))) 496 | return np.multiply(1/len(state), shadowsOfState) 497 | 498 | 499 | 500 | 501 | # GLOBAL VARIABLES 502 | # ---------------- 503 | 504 | # Pauli Matrices 505 | I = np.array([[1,0],[0,1]]) 506 | X = np.array([[0,1],[1,0]]) 507 | Y = np.array([[0,-1j],[1j,0]],dtype=complex) 508 | Z = np.array([[1,0],[0,-1]]) 509 | 510 | # Constants 511 | L = 10 #side length of toric code 512 | PATCHES = (2*L**2)-(2*L) 513 | DEPTH = 5 514 | NSTATES = 100 515 | GSU2MMTS = 1000 516 | MMTCHANNEL = setup_MChannel_EvalEvecs(3) 517 | 518 | statestoiterate = 1 #10 519 | pauliShadows = makePauliShadows() #vector of shadows created from the possible single qubit measurements made 520 | 521 | 522 | def main(jobnumber): 523 | 524 | dep = int(jobnumber/NSTATES) 525 | stateind = jobnumber%NSTATES 526 | 527 | with open('data{}.txt'.format(jobnumber), 'r') as f: 528 | cppdata = ast.literal_eval(f.read()) 529 | 530 | allshadowsarray = makeNmmtShadowPatchesPerState(cppdata) 531 | print('near perfect shadows are done.') 532 | 533 | print('\nworking on patch...') 534 | stateexpvals = [] 535 | for p in range(PATCHES): 536 | print(p) 537 | stateexpvals.append(shadow_estimated_expvals(3, allshadowsarray[p], GSU2MMTS, MMTCHANNEL)) 538 | 539 | with open('saveallexpvals_threesite_{}_{}.npy'.format(dep,stateind), 'wb') as f: 540 | np.save(f, np.array(stateexpvals)) 541 | 542 | 543 | 544 | if __name__ == "__main__": 545 | start = time.time() 546 | 547 | jobnumber = int(sys.argv[1]) 548 | GSU2MMTS = int(sys.argv[2]) 549 | main(jobnumber) 550 | 551 | elapsed = time.time() - start 552 | print(f"Elapsed time {elapsed/60:.2f} min") 553 | 554 | 555 | 556 | # DESCRIPTION... This code takes in the data generated by torictrivial_datageneration.cpp and creates a set of 557 | # reduced density matrices on patches of the states -- saved in an np.array indexed by depth, state, and then patch. 558 | # ...per depth, per state, per patch... 559 | # a reduced density matrix of the patch 560 | # Then, we perform globalsu2 shadow tomography on each patch, and using the resulting shadow, 561 | 562 | # How to read in the final np array: 563 | # ---------------------------------- 564 | # with open('saveallexpvals_threesite_{}_{}.npy'.format(dep,stateind), 'rb') as f: 565 | # allexpvals = np.load(f) 566 | -------------------------------------------------------------------------------- /makekerneldata.py: -------------------------------------------------------------------------------- 1 | #Katherine Van Kirk 2 | #kvankirk@g.harvard.edu 3 | 4 | import numpy as np 5 | import matplotlib.pyplot as plt 6 | import copy 7 | import math 8 | from math import e 9 | import random 10 | import ast 11 | import time 12 | import jax 13 | from neural_tangents import stax 14 | from sklearn.ensemble import RandomForestRegressor 15 | import seaborn as sns 16 | import copy 17 | 18 | # Kernel PCA from sklearn 19 | from sklearn.decomposition import PCA 20 | 21 | # Plotting tools 22 | import pandas as pd 23 | import seaborn as sns 24 | import matplotlib.pyplot as plt 25 | sns.set_style("ticks") 26 | 27 | 28 | L = 10 29 | PATCHES = (2*L**2)-(2*L) 30 | DEPTH = 5 31 | NSTATES = 100 32 | 33 | 34 | def main(): 35 | 36 | depthkernels = [] 37 | for dep in range(DEPTH): 38 | # Read in the expectation value data for all states 39 | stateexpvals = [] 40 | for stateind in range(NSTATES): 41 | with open('saveallexpvals_threesite_{}_{}.npy'.format(dep,stateind), 'rb') as f: 42 | singlestateexpvals = np.load(f) 43 | stateexpvals.append(singlestateexpvals) #singlestateexpvals[0] 44 | # print(stateexpvals) 45 | with open('expvals_threesite_{}.npy'.format(dep), 'wb') as f: 46 | np.save(f, np.array(stateexpvals)) 47 | 48 | 49 | if __name__ == "__main__": 50 | start = time.time() 51 | main() 52 | elapsed = time.time() - start 53 | print(f"Elapsed time {elapsed/60:.2f} min") 54 | 55 | 56 | # DESCRIPTION: This code takes in the shadow data generated by globalsu2shadowtomography.py and aggregates it into numpy arrays for each circuit depth. 57 | 58 | # saveallexpvals_d_s.npy indices map: 59 | # saveallexpvals[0] -- list of patchdata in state (only one index cause only one state per .npy file) 60 | # saveallexpvals[0][0] -- for the first patch (ind 0), list of vis space expectation values 61 | -------------------------------------------------------------------------------- /slurm_createtoptrivstates.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | #SBATCH -p shared # partition name (shared, serial_requeue) 4 | #SBATCH -n 1 # number of CPU per node 5 | #SBATCH -N 1 # number of nodes 6 | #SBATCH --mem 100000 # total memory allowed in MegaBytes 180000 (1000 MB = 1 GB) 7 | #SBATCH -t 0-05:40 # day-hours:minutes 8 | #SBATCH --job-name="topological data generation" 9 | #SBATCH -o datatopological%a.out 10 | #SBATCH -e datatopological%a.err 11 | #SBATCH --array=0 # can also take the format array=1-10,22,34,39-45 for example. 1000 jobs: array=0-99 12 | #SBATCH --open-mode=append # append new output instead of overwriting to *.out and *.err when job restarts 13 | 14 | export TASK_ID=$SLURM_ARRAY_TASK_ID 15 | 16 | export JOB_ID=$SLURM_ARRAY_JOB_ID 17 | export JID=$SLURM_JOB_ID 18 | 19 | g++ -O3 -std=c++0x torictrivial_datageneration.cpp -o datageneration 20 | ./datageneration 10 10000 100 5 13131 1.0 > datatopological.txt 21 | -------------------------------------------------------------------------------- /slurm_globalsu2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | #SBATCH -p shared # partition name (shared, serial_requeue) 4 | #SBATCH -n 1 # number of CPU per node 5 | #SBATCH -N 1 # number of nodes 6 | #SBATCH --mem 10000 # total memory allowed in MegaBytes (1000 MB = 1 GB) 7 | #SBATCH -t 0-10:00 # day-hours:minutes 8 | #SBATCH --job-name="gsu21000s3" 9 | #SBATCH -o su2mmt3sv2_%a.out 10 | #SBATCH -e su2mmt3sv2_%a.err 11 | #SBATCH --array=0-499 # 0-499, 500 jobs 12 | #SBATCH --open-mode=append # append new output instead of overwriting to *.out and *.err when job restarts 13 | 14 | export TASK_ID=$SLURM_ARRAY_TASK_ID 15 | 16 | export JOB_ID=$SLURM_ARRAY_JOB_ID 17 | export JID=$SLURM_JOB_ID 18 | 19 | # the environment variable SLURM_ARRAY_TASK_ID contains 20 | # the index corresponding to the current job step 21 | module load Anaconda3/2020.11 22 | python3 -u globalsu2shadowtomography.py "$SLURM_ARRAY_TASK_ID" 1000 23 | -------------------------------------------------------------------------------- /slurm_kerneldata.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | #SBATCH -p shared # partition name (shared, serial_requeue) 4 | #SBATCH -n 1 # number of CPU per node 5 | #SBATCH -N 1 # number of nodes 6 | #SBATCH --mem 10000 # total memory allowed in MegaBytes (1000 MB = 1 GB) 7 | #SBATCH -t 0-10:00 # day-hours:minutes 8 | #SBATCH --job-name="3skernel" 9 | #SBATCH -o kerr3sv2_%a.out 10 | #SBATCH -e kerr3sv2_%a.err 11 | #SBATCH --array=0 # 0-499, can also take the format array=1-10,22,34,39-45 for example. 100 jobs: array=0-99 12 | #SBATCH --open-mode=append # append new output instead of overwriting to *.out and *.err when job restarts 13 | 14 | export TASK_ID=$SLURM_ARRAY_TASK_ID 15 | 16 | export JOB_ID=$SLURM_ARRAY_JOB_ID 17 | export JID=$SLURM_JOB_ID 18 | 19 | module load Anaconda3/2020.11 20 | python3 -u makekerneldata.py 21 | -------------------------------------------------------------------------------- /torictrivial_datageneration.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | using namespace std; 13 | 14 | default_random_engine generator; 15 | uniform_real_distribution distribution_float(0.0, 1.0); 16 | uniform_int_distribution distribution_int(0,1); 17 | ofstream myfile; 18 | 19 | class F2n_2{ 20 | public: 21 | int n; 22 | bool *v; // size = 2n 23 | 24 | F2n_2(){ 25 | n = 0; 26 | v = NULL; 27 | } 28 | 29 | F2n_2(int _n){ 30 | n = _n; 31 | v = (bool*)malloc((2 * n) * sizeof(bool)); 32 | memset(v, 0, (2 * n) * sizeof(bool)); 33 | } 34 | 35 | F2n_2(int _n, bool *_v){ 36 | n = _n; 37 | v = _v; 38 | } 39 | 40 | F2n_2(F2n_2 &u, bool copy){ 41 | n = u.n; 42 | 43 | if(copy){ 44 | v = (bool*)malloc((2 * n) * sizeof(bool)); 45 | for(int i = 0; i < 2 * n; i ++) 46 | v[i] = u.entry(i); 47 | } 48 | else{ 49 | v = u.v; 50 | } 51 | } 52 | 53 | void free_class(){ 54 | free(v); 55 | } 56 | 57 | bool operator ==(F2n_2 u){ 58 | if(u.n != n) return false; 59 | for(int i = 0; i < 2 * n; i ++){ 60 | if(u.v[i] != v[i]) return false; 61 | } 62 | return true; 63 | } 64 | 65 | void init(){ 66 | memset(v, 0, (2 * n) * sizeof(bool)); 67 | } 68 | 69 | void random(){ 70 | for(int i = 0; i < 2 * n; i ++){ 71 | v[i] = distribution_int(generator); 72 | } 73 | } 74 | 75 | void random_nonzero(){ 76 | bool all_zero = true; 77 | while(all_zero){ 78 | for(int i = 0; i < 2 * n; i ++){ 79 | v[i] = distribution_int(generator); 80 | if(v[i] != 0) 81 | all_zero = false; 82 | } 83 | } 84 | } 85 | 86 | bool& entry(int i){ 87 | assert(i < 2 * n && i >= 0); 88 | return v[i]; 89 | } 90 | 91 | void print(){ 92 | printf("Printing an element in F2n_2:\n"); 93 | for(int i = 0; i < 2 * n; i ++){ 94 | printf("%d ", v[i]); 95 | } 96 | printf("\n"); 97 | } 98 | }; 99 | 100 | bool inner(F2n_2 v, F2n_2 w){ // symplectic inner product 101 | assert(v.n == w.n); 102 | int t = 0; 103 | for(int i = 0; i < v.n; i ++){ 104 | t += v.entry(2 * i) * w.entry(2 * i + 1); 105 | t += w.entry(2 * i) * v.entry(2 * i + 1); 106 | } 107 | return t % 2; 108 | } 109 | 110 | F2n_2 transvection(F2n_2 k, F2n_2 v){ 111 | F2n_2 ret(v, true); 112 | 113 | bool inn = inner(k, v); 114 | if(inn == true){ 115 | for(int i = 0; i < 2 * ret.n; i++){ 116 | ret.entry(i) = (ret.entry(i) != k.entry(i)); 117 | } 118 | } 119 | return ret; 120 | } 121 | 122 | void findtransvection(F2n_2 x, F2n_2 y, F2n_2 &h1, F2n_2 &h2){ // finds h1,h2 such that y = Zh1 Zh2 x 123 | h1.init(); 124 | h2.init(); 125 | 126 | if(x == y){ 127 | return; 128 | } 129 | if(inner(x, y)){ 130 | for(int i = 0; i < 2 * h1.n; i ++) 131 | h1.entry(i) = (x.entry(i) != y.entry(i)); 132 | return; 133 | } 134 | 135 | F2n_2 z(x.n); 136 | for(int i = 0; i < x.n; i ++){ 137 | int ii = 2 * i; 138 | if((x.entry(ii) + x.entry(ii+1)) != 0 && (y.entry(ii) + y.entry(ii+1)) != 0){ 139 | z.entry(ii) = (x.entry(ii) != y.entry(ii)); 140 | z.entry(ii+1) = (x.entry(ii+1) != y.entry(ii+1)); 141 | 142 | if(z.entry(ii) + z.entry(ii+1) == 0){ 143 | z.entry(ii+1) = 1; 144 | if(x.entry(ii) != x.entry(ii+1)){ 145 | z.entry(ii) = 1; 146 | } 147 | } 148 | for(int i = 0; i < 2 * h1.n; i ++) 149 | h1.entry(i) = x.entry(i) != z.entry(i); 150 | for(int i = 0; i < 2 * h2.n; i ++) 151 | h2.entry(i) = y.entry(i) != z.entry(i); 152 | return; 153 | } 154 | } 155 | //didn’t find a pair 156 | 157 | // first y==00 and x doesn’t 158 | for(int i = 0; i < x.n; i ++){ 159 | int ii = 2 * i; 160 | if(((x.entry(ii) + x.entry(ii+1)) != 0) && ((y.entry(ii) + y.entry(ii+1)) == 0)){ 161 | if(x.entry(ii) == x.entry(ii+1)){ 162 | z.entry(ii+1) = 1; 163 | } 164 | else{ 165 | z.entry(ii+1) = x.entry(ii); 166 | z.entry(ii) = x.entry(ii+1); 167 | } 168 | break; 169 | } 170 | } 171 | // finally x==00 and y doesn’t 172 | for(int i = 0; i < x.n; i ++){ 173 | int ii = 2 * i; 174 | if(((x.entry(ii) + x.entry(ii+1)) == 0) && ((y.entry(ii) + y.entry(ii+1)) != 0)){ 175 | if(y.entry(ii) == y.entry(ii+1)){ 176 | z.entry(ii+1) = 1; 177 | } 178 | else{ 179 | z.entry(ii+1) = y.entry(ii); 180 | z.entry(ii) = y.entry(ii+1); 181 | } 182 | break; 183 | } 184 | } 185 | 186 | for(int i = 0; i < 2 * h1.n; i ++) 187 | h1.entry(i) = x.entry(i) != z.entry(i); 188 | for(int i = 0; i < 2 * h2.n; i ++) 189 | h2.entry(i) = y.entry(i) != z.entry(i); 190 | 191 | z.free_class(); 192 | return; 193 | } 194 | 195 | class symplectic_matrix{ 196 | public: 197 | int n; 198 | bool *S; // size = 2n x 2n 199 | 200 | symplectic_matrix(){ 201 | n = 0; 202 | S = NULL; 203 | } 204 | 205 | symplectic_matrix(int _n){ 206 | n = _n; 207 | S = (bool*)malloc((2 * n) * (2 * n) * sizeof(bool)); 208 | memset(S, 0, (2 * n) * (2 * n) * sizeof(bool)); 209 | } 210 | 211 | void free_class(){ 212 | free(S); 213 | } 214 | 215 | bool& entry(int i, int j){ 216 | assert(i < 2 * n && i >= 0); 217 | assert(j < 2 * n && j >= 0); 218 | return S[i * (2 * n) + j]; 219 | } 220 | 221 | F2n_2 row_slice(int i){ 222 | F2n_2 r(n); 223 | for(int j = 0; j < 2 * n; j++){ 224 | r.entry(j) = S[i * (2 * n) + j]; 225 | } 226 | return r; 227 | } 228 | 229 | bool verify_symplectic(){ 230 | for(int i = 0; i < 2 * n; i++){ 231 | for(int j = 0; j < 2 * n; j++){ 232 | int desired = 0; 233 | if(i / 2 == j / 2){ 234 | desired = (i % 2 != j % 2); 235 | } 236 | 237 | int sum = 0; 238 | for(int k = 0; k < n; k++){ 239 | sum += entry(i, 2*k) * entry(j, 2*k+1) + entry(i, 2*k+1) * entry(j, 2*k); 240 | } 241 | if(sum%2 != desired) return false; 242 | } 243 | } 244 | return true; 245 | } 246 | 247 | void print(){ 248 | printf("Printing a symplectic matrix:\n"); 249 | for(int i = 0; i < 2 * n; i++){ 250 | for(int j = 0; j < 2 * n; j++){ 251 | printf("%d ", entry(i, j)); 252 | } 253 | printf("\n"); 254 | } 255 | } 256 | }; 257 | 258 | // Destroys m1 and m2 after returning the output 259 | symplectic_matrix directsum(symplectic_matrix m1, symplectic_matrix m2){ 260 | symplectic_matrix out(m1.n + m2.n); 261 | 262 | for(int i = 0; i < 2 * m1.n; i ++){ 263 | for(int j = 0; j < 2 * m1.n; j ++){ 264 | out.entry(i, j) = m1.entry(i, j); 265 | } 266 | } 267 | 268 | for(int i = 0; i < 2 * m2.n; i ++){ 269 | for(int j = 0; j < 2 * m2.n; j ++){ 270 | out.entry(i + 2 * m1.n, j + 2 * m1.n) = m2.entry(i, j); 271 | } 272 | } 273 | m1.free_class(); 274 | m2.free_class(); 275 | 276 | return out; 277 | } 278 | 279 | symplectic_matrix symplectic(int n){ 280 | F2n_2 f1(n); 281 | f1.random_nonzero(); 282 | 283 | F2n_2 e1(n); 284 | e1.entry(0) = 1; 285 | 286 | F2n_2 T0(n); 287 | F2n_2 T1(n); 288 | findtransvection(e1, f1, T0, T1); 289 | 290 | F2n_2 eprime(n); 291 | eprime.random(); 292 | eprime.entry(0) = 1; 293 | eprime.entry(1) = 0; 294 | 295 | F2n_2 h0; 296 | h0 = transvection(T0, eprime); 297 | 298 | F2n_2 h0_new; 299 | h0_new = transvection(T1, h0); 300 | h0.free_class(); 301 | 302 | if(distribution_int(generator) == 1) 303 | f1.init(); 304 | 305 | symplectic_matrix id2(1); 306 | id2.entry(0, 0) = 1; 307 | id2.entry(1, 1) = 1; 308 | 309 | symplectic_matrix g; 310 | if(n != 1){ 311 | g = directsum(id2, symplectic(n-1)); 312 | } 313 | else{ 314 | g = id2; 315 | } 316 | for(int i = 0; i < 2 * n; i ++){ 317 | F2n_2 gi, gi_odd; 318 | gi = g.row_slice(i); 319 | gi_odd = transvection(T0, gi); 320 | gi.free_class(); 321 | gi = transvection(T1, gi_odd); 322 | gi_odd.free_class(); 323 | gi_odd = transvection(h0_new, gi); 324 | gi.free_class(); 325 | gi = transvection(f1, gi_odd); 326 | gi_odd.free_class(); 327 | for(int j = 0; j < 2 * n; j ++){ 328 | g.entry(i, j) = gi.entry(j); 329 | } 330 | gi.free_class(); 331 | } 332 | 333 | f1.free_class(); 334 | e1.free_class(); 335 | T0.free_class(); 336 | T1.free_class(); 337 | eprime.free_class(); 338 | h0_new.free_class(); 339 | 340 | return g; 341 | } 342 | 343 | class stabilizer_tableau{ 344 | public: 345 | int n; 346 | bool *x, *z, *r; 347 | 348 | stabilizer_tableau(int L){ // Toric code 349 | n = 2 * L * L; 350 | 351 | x = (bool*)malloc((2 * n + 1) * n * sizeof(bool)); 352 | memset(x, 0, (2 * n + 1) * n * sizeof(bool)); 353 | z = (bool*)malloc((2 * n + 1) * n * sizeof(bool)); 354 | memset(z, 0, (2 * n + 1) * n * sizeof(bool)); 355 | r = (bool*)malloc((2 * n + 1) * sizeof(bool)); 356 | memset(r, 0, (2 * n + 1) * sizeof(bool)); 357 | 358 | // First work out the stabilizers 359 | int cnt = n; 360 | 361 | // horizontal Z strings 362 | for(int i = 0; i < L; i++){ 363 | int idx = L * (2 * L - 2) + i; 364 | z[cnt * n + (idx)] = 1; 365 | } 366 | cnt ++; 367 | 368 | // vertical Z strings 369 | for(int i = 1; i < 2 * L; i += 2){ 370 | int idx = L * i + (L - 1); 371 | z[cnt * n + (idx)] = 1; 372 | } 373 | cnt ++; 374 | 375 | // Z plaquettes 376 | for(int _j = -1; _j < L-1; _j++){ 377 | int j = (_j + L) % L; 378 | for(int _i = -1; _i < L-1; _i++){ 379 | int i = (_i + L) % L; 380 | if(i == L-1 && j == L-1) continue; 381 | 382 | int idx1 = L * (2 * i) + j; 383 | z[cnt * n + (idx1)] = 1; 384 | int idx2 = L * (2 * i + 1) + j; 385 | z[cnt * n + (idx2)] = 1; 386 | int idx3 = L * (2 * i + 1) + ((j + 1) % L); 387 | z[cnt * n + (idx3)] = 1; 388 | int idx4 = L * ((2 * i + 2) % (2 * L)) + j; 389 | z[cnt * n + (idx4)] = 1; 390 | 391 | cnt ++; 392 | } 393 | } 394 | 395 | // X stars 396 | for(int j = L-1; j >= 0; j--){ 397 | for(int i = L-1; i >= 0; i--){ 398 | if(i == L-1 && j == L-1) continue; 399 | 400 | int idx1 = L * ((2 * i + 2 * L - 1) % (2 * L)) + j; 401 | x[cnt * n + (idx1)] = 1; 402 | int idx2 = L * (2 * i) + ((j + L - 1) % L); 403 | x[cnt * n + (idx2)] = 1; 404 | int idx3 = L * (2 * i) + j; 405 | x[cnt * n + (idx3)] = 1; 406 | int idx4 = L * ((2 * i + 1) % (2 * L)) + j; 407 | x[cnt * n + (idx4)] = 1; 408 | 409 | cnt ++; 410 | } 411 | } 412 | 413 | assert(cnt == 2 * n); 414 | 415 | // Now work out the destabilizers 416 | cnt = 0; 417 | 418 | // vertical X strings 419 | for(int i = 0; i < 2 * L; i += 2){ 420 | int idx = L * i + (L - 1); 421 | x[cnt * n + (idx)] = 1; 422 | } 423 | cnt ++; 424 | 425 | // horizontal X strings 426 | for(int i = 0; i < L; i++){ 427 | int idx = L * (2 * L - 1) + i; 428 | x[cnt * n + (idx)] = 1; 429 | } 430 | cnt ++; 431 | 432 | vector one_string; 433 | 434 | // strings of X errors 435 | for(int y = 0; y < L; y++){ 436 | one_string.clear(); 437 | 438 | for(int i = 0; i < y; i++){ 439 | int idx = L * (2 * L - 1) + i; 440 | one_string.push_back(idx); 441 | } 442 | for(int x = 0; x < 2 * L - 3; x += 2){ 443 | int idx = L * x + ((y + L - 1) % L); 444 | one_string.push_back(idx); 445 | } 446 | 447 | for(int i = max(y-1, 0); i < (int)one_string.size(); i++){ 448 | for(int j = 0; j <= i; j++){ 449 | x[cnt * n + one_string[j]] = 1; 450 | } 451 | cnt ++; 452 | } 453 | } 454 | 455 | // strings of Z errors 456 | for(int y = 0; y < L; y++){ 457 | one_string.clear(); 458 | 459 | for(int i = 1; i <= y; i++){ 460 | int idx = L * (2 * L - 2) + (L - 1 - i); 461 | one_string.push_back(idx); 462 | } 463 | for(int x = 2 * L - 3; x >= 0; x -= 2){ 464 | int idx = L * x + (L - 1 - y); 465 | one_string.push_back(idx); 466 | } 467 | 468 | for(int i = max(y-1, 0); i < (int)one_string.size(); i++){ 469 | for(int j = 0; j <= i; j++){ 470 | z[cnt * n + one_string[j]] = 1; 471 | } 472 | cnt ++; 473 | } 474 | } 475 | 476 | assert(cnt == n); 477 | } 478 | 479 | stabilizer_tableau(int _n, bool random){ 480 | n = _n; 481 | x = (bool*)malloc((2 * n + 1) * n * sizeof(bool)); 482 | memset(x, 0, (2 * n + 1) * n * sizeof(bool)); 483 | z = (bool*)malloc((2 * n + 1) * n * sizeof(bool)); 484 | memset(z, 0, (2 * n + 1) * n * sizeof(bool)); 485 | r = (bool*)malloc((2 * n + 1) * sizeof(bool)); 486 | memset(r, 0, (2 * n + 1) * sizeof(bool)); 487 | 488 | if(random == false){ 489 | for(int i = 0; i < n; i ++){ 490 | x[i * n + i] = 1; 491 | z[(n + i) * n + i] = 1; 492 | } 493 | } 494 | else{ 495 | symplectic_matrix random_g = symplectic(n); 496 | assert(random_g.verify_symplectic()); 497 | //printf("is symplectic? %d\n", random_g.verify_symplectic()); 498 | //random_g.print(); 499 | 500 | for(int i = 0; i < 2 * n; i ++){ 501 | if(i >= n) 502 | r[i] = distribution_int(generator); 503 | else 504 | r[i] = 0; 505 | for(int j = 0; j < n; j ++){ 506 | if(i < n){ 507 | x[i * n + j] = random_g.entry(2 * i + 1, 2 * j + 1); 508 | z[i * n + j] = random_g.entry(2 * i + 1, 2 * j); 509 | } 510 | else{ 511 | x[i * n + j] = random_g.entry(2 * (i - n), 2 * j + 1); 512 | z[i * n + j] = random_g.entry(2 * (i - n), 2 * j); 513 | } 514 | } 515 | } 516 | 517 | random_g.free_class(); 518 | } 519 | } 520 | 521 | stabilizer_tableau(stabilizer_tableau const &ST, bool copy){ 522 | if(copy){ 523 | n = ST.n; 524 | x = (bool*)malloc((2 * n + 1) * n * sizeof(bool)); 525 | memset(x, 0, (2 * n + 1) * n * sizeof(bool)); 526 | z = (bool*)malloc((2 * n + 1) * n * sizeof(bool)); 527 | memset(z, 0, (2 * n + 1) * n * sizeof(bool)); 528 | r = (bool*)malloc((2 * n + 1) * sizeof(bool)); 529 | memset(r, 0, (2 * n + 1) * sizeof(bool)); 530 | 531 | for(int i = 0; i < (2 * n + 1) * n; i++){ 532 | x[i] = ST.x[i]; 533 | z[i] = ST.z[i]; 534 | } 535 | for(int i = 0; i < 2 * n + 1; i++){ 536 | r[i] = ST.r[i]; 537 | } 538 | } 539 | else{ 540 | n = ST.n; 541 | x = ST.x; 542 | z = ST.z; 543 | r = ST.r; 544 | } 545 | } 546 | 547 | void free_class(){ 548 | free(x); 549 | free(z); 550 | free(r); 551 | } 552 | 553 | ~stabilizer_tableau(){ 554 | free_class(); 555 | } 556 | 557 | int g_func(int x1, int z1, int x2, int z2){ 558 | if(x1 == 0 && z1 == 0){ 559 | return 0; 560 | } 561 | if(x1 == 0 && z1 == 1){ 562 | return x2 * (1 - 2 * z2); 563 | } 564 | if(x1 == 1 && z1 == 0){ 565 | return z2 * (2 * x2 - 1); 566 | } 567 | if(x1 == 1 && z1 == 1){ 568 | return z2 - x2; 569 | } 570 | assert(true); 571 | return 0; 572 | } 573 | 574 | void rowsum(int h, int i){ 575 | if(h >= n){ 576 | int val = 0; 577 | val = 2 * r[h] + 2 * r[i]; 578 | for(int j = 0; j < n; j++){ 579 | val += g_func(x[i * n + j], z[i * n + j], x[h * n + j], z[h * n + j]); 580 | } 581 | //printf("%d, %d val: %d\n", h, i, val); 582 | val = (val % 4 + 4) % 4; 583 | 584 | assert(val == 2 || val == 0); 585 | r[h] = (val == 2); 586 | } 587 | else{ 588 | r[h] = 0; 589 | } 590 | 591 | for(int j = 0; j < n; j++){ 592 | x[h * n + j] = (x[i * n + j] != x[h * n + j]); 593 | z[h * n + j] = (z[i * n + j] != z[h * n + j]); 594 | } 595 | } 596 | 597 | void CNOT(int a, int b){ 598 | assert(a != b); 599 | for(int i = 0; i < 2 * n; i++){ 600 | bool xia = x[i * n + a]; 601 | bool xib = x[i * n + b]; 602 | bool zia = z[i * n + a]; 603 | bool zib = z[i * n + b]; 604 | 605 | r[i] = (r[i] != (xia * zib * (xib != (zia != 1)))); 606 | x[i * n + b] = (xib != xia); 607 | z[i * n + a] = (zia != zib); 608 | } 609 | } 610 | 611 | void Hadamard(int a){ 612 | for(int i = 0; i < 2 * n; i++){ 613 | bool xia = x[i * n + a]; 614 | bool zia = z[i * n + a]; 615 | 616 | r[i] = (r[i] != (xia * zia)); 617 | z[i * n + a] = xia; 618 | x[i * n + a] = zia; 619 | } 620 | } 621 | 622 | void Phase(int a){ 623 | for(int i = 0; i < 2 * n; i++){ 624 | bool xia = x[i * n + a]; 625 | bool zia = z[i * n + a]; 626 | 627 | r[i] = (r[i] != (xia * zia)); 628 | z[i * n + a] = (zia != xia); 629 | } 630 | } 631 | 632 | int measurement(int a, int forced_value = -999){ 633 | int p = -1; 634 | for(int i = n; i < 2 * n; i++){ 635 | if(x[i * n + a] == 1){ 636 | p = i; 637 | break; 638 | } 639 | } 640 | //printf("%d %d\n", a, p); 641 | 642 | if(p == -1){ 643 | for(int j = 0; j < n; j++){ 644 | x[(2 * n) * n + j] = 0; 645 | z[(2 * n) * n + j] = 0; 646 | } 647 | r[2 * n] = 0; 648 | 649 | for(int i = 0; i < n; i++){ 650 | if(x[i * n + a] == 1) 651 | rowsum(2 * n, i + n); 652 | } 653 | return 2 * 0 + r[2 * n]; // deterministic, with outcome r[2 * n] 654 | } 655 | else{ 656 | for(int i = 0; i < 2 * n; i++){ 657 | if(i != p && x[i * n + a]) 658 | rowsum(i, p); 659 | } 660 | for(int j = 0; j < n; j++){ 661 | x[(p - n) * n + j] = x[p * n + j]; 662 | z[(p - n) * n + j] = z[p * n + j]; 663 | } 664 | r[p - n] = r[p]; 665 | for(int j = 0; j < n; j++){ 666 | x[p * n + j] = 0; 667 | z[p * n + j] = 0; 668 | } 669 | z[p * n + a] = 1; 670 | 671 | if(forced_value != -999) 672 | r[p] = forced_value; 673 | else 674 | r[p] = distribution_int(generator); 675 | 676 | return 2 * 1 + r[p]; // random, with outcome r[p] 677 | } 678 | } 679 | 680 | void print(){ 681 | printf("Printing a stabilizer state:\n"); 682 | for(int i = 0; i < 2 * n; i ++){ 683 | if(i == n){ 684 | for(int j = 0; j < 2 * n + 3; j ++){ 685 | printf("--"); 686 | } 687 | printf("\n"); 688 | } 689 | 690 | for(int j = 0; j < n; j ++){ 691 | printf("%d ", x[i * n + j]); 692 | } 693 | printf("| "); 694 | for(int j = 0; j < n; j ++){ 695 | printf("%d ", z[i * n + j]); 696 | } 697 | printf("| "); 698 | printf("%d\n", r[i]); 699 | } 700 | } 701 | }; 702 | 703 | int L; 704 | 705 | vector seq_singleQ_Clifford[24] = {{}, {0}, {1}, {0, 1}, {1, 0}, {1, 1}, {0, 1, 0}, {0, 1, 1}, {1, 0, 1}, {1, 1, 0}, {1, 1, 1}, {0, 1, 0, 1}, {0, 1, 1, 0}, {0, 1, 1, 1}, {1, 0, 1, 1}, {1, 1, 0, 1}, {0, 1, 0, 1, 1}, {0, 1, 1, 0, 1}, {1, 0, 1, 1, 0}, {1, 0, 1, 1, 1}, {1, 1, 0, 1, 1}, {0, 1, 0, 1, 1, 0}, {0, 1, 0, 1, 1, 1}, {0, 1, 1, 0, 1, 1}}; 706 | 707 | void apply_const_depth_Clifford_circuit(stabilizer_tableau& ST, int depth){ 708 | for(int d = 0; d < depth; d++){ 709 | for(int i = 0; i < ST.n; i++){ 710 | int C = floor(distribution_float(generator) * 24); 711 | 712 | vector seq = seq_singleQ_Clifford[C]; 713 | reverse(seq.begin(), seq.end()); 714 | for(int x: seq){ 715 | if(x == 0) 716 | ST.Hadamard(i); 717 | else 718 | ST.Phase(i); 719 | } 720 | } 721 | 722 | if(d % 2 == 0){ 723 | for(int i = 0; i < 2 * L; i++){ 724 | for(int j = 0; j < L; j++){ 725 | if(j+1 >= L) continue; 726 | 727 | double p = distribution_float(generator); 728 | if(p < 1.0 / 3.0){ 729 | ST.CNOT(i*L+j, i*L+j+1); 730 | } 731 | else if(p < 2.0 / 3.0){ 732 | ST.CNOT(i*L+j+1, i*L+j); 733 | } 734 | } 735 | } 736 | } 737 | else{ 738 | for(int i = 0; i < 2 * L; i++){ 739 | for(int j = 0; j < L; j++){ 740 | if(i+1 >= 2 * L) continue; 741 | 742 | double p = distribution_float(generator); 743 | if(p < 1.0 / 3.0){ 744 | ST.CNOT(i*L+j, (i+1)*L+j); 745 | } 746 | else if(p < 2.0 / 3.0){ 747 | ST.CNOT((i+1)*L+j, i*L+j); 748 | } 749 | } 750 | } 751 | } 752 | // for(int i = (d % 2); i < ST.n; i+=2){ 753 | // if(i+1 >= ST.n) continue; 754 | // 755 | // double p = distribution_float(generator); 756 | // if(p < 1.0 / 3.0){ 757 | // ST.CNOT(i, i+1); 758 | // } 759 | // else if(p < 2.0 / 3.0){ 760 | // ST.CNOT(i+1, i); 761 | // } 762 | // } 763 | } 764 | } 765 | 766 | void classical_shadow(stabilizer_tableau& ST){ 767 | stabilizer_tableau ST_m(ST, true); 768 | 769 | for(int i = 0; i < ST.n; i++){ 770 | int outcome = 0; 771 | double p = distribution_float(generator); 772 | if(p < 1.0 / 3.0){ // X basis 773 | ST_m.Hadamard(i); 774 | outcome += 2; 775 | } 776 | else if(p < 2.0 / 3.0){ // Y basis 777 | ST_m.Phase(i); 778 | ST_m.Hadamard(i); 779 | outcome += 4; 780 | } 781 | outcome += (ST_m.measurement(i) % 2); 782 | 783 | myfile << std::to_string(outcome); 784 | if(i < ST.n - 1) myfile << ", "; 785 | } 786 | } 787 | 788 | 789 | 790 | // argc is number of arguments 791 | // argv is the arguments passed in 792 | int main(int argc, char *argv[]){ 793 | assert(argc == 7); 794 | 795 | L = stoi(argv[1]); 796 | int Nsample = stoi(argv[2]); 797 | int Ntrain = stoi(argv[3]); 798 | int depth = stoi(argv[4]); 799 | 800 | generator = default_random_engine(stoi(argv[5])); 801 | 802 | float gamma = stof(argv[6]); 803 | 804 | int n = 2 * L * L; //total number of qubits 805 | 806 | for(int dep = 0; dep < depth; dep++){ 807 | 808 | // Generates Ntrain/2 random states in the toric code phase (w given depth) 809 | stabilizer_tableau ToricCode(L); 810 | for(int t = 0; t < Ntrain / 2; t++){ 811 | stabilizer_tableau ToricPhase(ToricCode, true); 812 | apply_const_depth_Clifford_circuit(ToricPhase, dep); 813 | 814 | int filenumber = dep*Ntrain + t; 815 | myfile.open ("data" + std::to_string(filenumber) + ".txt"); 816 | 817 | //You want Nsample measurements 818 | myfile << "["; 819 | for(int s = 0; s < Nsample; s++){ 820 | myfile << "["; 821 | classical_shadow(ToricPhase); 822 | myfile << "],\n"; 823 | } 824 | myfile << "]\n"; 825 | myfile.close(); 826 | } 827 | 828 | // Generates Ntrain/2 random states in the toric code (w given depth) 829 | for(int t = Ntrain / 2; t < Ntrain; t++){ 830 | stabilizer_tableau TrivialPhase(n, false); 831 | apply_const_depth_Clifford_circuit(TrivialPhase, dep); 832 | 833 | int filenumber = dep*Ntrain + t; 834 | myfile.open ("data" + std::to_string(filenumber) + ".txt"); 835 | 836 | //You want Nsample measurements 837 | myfile << "["; 838 | for(int s = 0; s < Nsample; s++){ 839 | myfile << "["; 840 | classical_shadow(TrivialPhase); 841 | myfile << "],\n"; 842 | } 843 | myfile << "]\n"; 844 | myfile.close(); 845 | } 846 | } 847 | } 848 | 849 | /* 850 | # How to run the C++ code: 851 | # ./shadow_kernel_topological [length L in toric code (total number of qubits = 2 L x L)] 852 | # Nsample[number of randomized Pauli measurements] 853 | # Ntrain[number of random states (half trivial, half topological)] 854 | # [maximum depth for generating random states] 855 | # [random seed] 856 | # [gamma in shadow kernel (1.0 is usually a good choice)] 857 | */ 858 | 859 | 860 | 861 | // The above code was modified from code originally written by Hsin-Yuan Huang. I include his licence information below: 862 | 863 | /* 864 | Copyright (c) 2022 Hsin-Yuan Huang 865 | 866 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 867 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 868 | 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: 869 | 870 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 871 | */ --------------------------------------------------------------------------------