diff --git a/jupyter_notebooks/Examples/Propagatable error gens tutorial.ipynb b/jupyter_notebooks/Examples/Propagatable error gens tutorial.ipynb new file mode 100644 index 000000000..47f95861c --- /dev/null +++ b/jupyter_notebooks/Examples/Propagatable error gens tutorial.ipynb @@ -0,0 +1,275 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The autoreload extension is already loaded. To reload it, use:\n", + " %reload_ext autoreload\n" + ] + } + ], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2\n", + "from pygsti.extras.errorgenpropagation.propagatableerrorgen import *\n", + "from pygsti.extras.errorgenpropagation.errorpropagator import *\n", + "from pygsti.circuits import Circuit\n", + "import numpy as np\n", + "import pygsti.processors\n", + "import pygsti\n", + "import pygsti.tools.lindbladtools as _lt\n", + "import scipy\n", + "import matplotlib.pyplot as plt" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Introduction to the Propagatable Error Generators Code" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Defining a circuit and error generators" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Currently Error Propgagation works for any model that meets three criteria\n", + "\n", + " 1. The circuit is clifford\n", + " 2. The errors on each gate can be defined at a time t of interest in the small markovian errors basis\n", + " 3. The error error model is defined such that a gate G has some linear combination of error generators following it\n", + "\n", + "We can therefore, start a code by defining a circuit and an error model by simply following the common pyGSTi notation" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "errorModel={\n", + " 'Gxpi2' : {('H','Y'):.01}\n", + "\n", + "}\n", + "c=Circuit(10*[('Gxpi2',0)])" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we can take the above definitions and plug them into the errorpropagator function, to get out a list of post-circuit error generators out." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[[('H', ('Y',), 0.01)]]]\n", + "[[[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]]]\n", + "[[[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]]]\n", + "[[[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]]]\n", + "[[[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]]]\n", + "[[[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]]]\n", + "[[[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]]]\n", + "[[[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]]]\n", + "[[[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]]]\n", + "[[[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]], [[('H', ('Y',), 0.01)]]]\n" + ] + } + ], + "source": [ + "errors=ErrorPropagator(c,errorModel,BCHOrder=1,BCHLayerwise=False,NonMarkovian=False)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here BCH order determines the to what order the BCH order will be taken to (if applicable). BCHLayerwise will if false, propagatate all errors to the end before taking the BCH expansion, otherwise it will push the errorgens through a layer and combine with the the error generators for that layer by the rules given by the BCHOrder. Non-markovian prevents any simplification or BCH expansions being taken, instead allowing the output to be a list a lists, where the each sublist denotes the errorgenerators that were occuring at time t in the circuit." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Additionally, if you want to describe a gate with multiple associated error definitions you can define it as follows." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[('H', ('X',), (0.09999999999999999+0j))]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "MultiGateDict={'Gxpi22' : 'Gxpi2'}\n", + "errorModel={\n", + " 'Gxpi2' : {('H','Y'):.01},\n", + " 'Gxpi22' : {('H','X'):.01}\n", + "\n", + "}\n", + "c=Circuit(10*[('Gxpi2',0),('Gxpi22',0)])\n", + "\n", + "ErrorPropagator(c,errorModel,MultiGateDict=MultiGateDict, MultiGate=True)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Once the errors are propagated to the process matrix given by the end of circuit error generators is given by" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "expMat=np.zeros([4**len(c.line_labels),4**len(c.line_labels)],dtype=np.complex128)\n", + "for error in errors:\n", + " expMat +=error.toWeightedErrorBasisMatrix()\n", + "processMatrix = scipy.linalg.expm(expMat)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Non-Markovianity" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you want to use the non markovianity function you need to define an n x n correlation where n is the number of layers. Currently, we are capable of describing each layer to be governed by some stochastic process, that is correlated to the other layers. To using the code is relatively simple, see the below example" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 1.0, 'White noise dephasing')" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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+kB1zZs3j0aNGpEnTx7Onz9vf4SEhBAfH8+aNWvu+xkVK1akUaNG9tcFChSgXLlyDjUtXbqUunXrOnynXLly8cILL3D8+HH27duX5Gdnz54dT09PVq1axT///JPkMeHh4URHR9OlSxeH7+Du7k5QUFCSI+DutHTpUurVq0fdunUdvkPXrl1TfJ47pxG4Na3A9evX+fXXXx2O69SpE3ny5LG/vnU977yGd7Y4Xbt2jfPnz1OvXj0Ah9tVuXPnZtOmTZw+ffqe3zsp96vj9OnT7N69m+7du5MrVy77cU2aNKFKlSpOn0/ECrqlJZKGbDYbwcHBrFmzhoSEBNavX0/BggUpU6YMYAaeiRMnAtiDT1KBp3jx4om25cmT565h4H4OHz7Mrl27KFCgQJL7o6Ki7vsZyanpzz//JCgoKNFxFSpUsO+vXLlyov1eXl6MHj2aV199FX9/f+rVq8djjz1G9+7dKVSokP07ADRr1izJ+nx9fe9Z/91qK1eunMNrZ8/j5uZGqVKlHLaVLVsWIFH/o39fw1uh485reOHCBd5//31mz56d6M/l4sWL9udjxowhLCyMgIAAatWqRatWrejevXuiWpJyvzr+/PNPAPu/t3cqU6aMQ/ASyagUeETSWMOGDfnxxx/ZvXu3vf/OLcHBwbz++uucOnWKdevWUaRIkSR/oO42+sq4S0fY+0lISKBFixa88cYbSe6/9QN9L6ld07/179+fNm3asGjRIn7++WeGDBnCqFGjWLFiBTVq1LB3Lv7222/tIehOHh6p89dbWp4nOdfwySefZMOGDbz++utUr16dXLlykZCQwKOPPurQwfrJJ5+kUaNGLFy4kF9++YWxY8cyevRoFixYQMuWLVNch0hmp8AjksbunI9n/fr19O/f376vVq1aeHl5sWrVKjZt2kSrVq1S9dy3OrT+W+nSpbl8+TIhISGper5/K1GiRJLzvhw4cMC+/15Kly7Nq6++yquvvsrhw4epXr0648eP57vvvrPf4itYsOADfY8SJUrYW2/u9O96nT1PQkICR48edQiNhw4dAnB6NNM///xDREQE77//Pu+++659e1J1AxQuXJiXX36Zl19+maioKGrWrMmIESPuG3ju59af0x9//JFoX1LbRDIi9eERSWO1a9fG29ubmTNncurUKYcWHi8vL2rWrMmkSZO4cuXKA82/cy85c+YkOjo60fYnn3ySjRs38vPPPyfaFx0dzc2bN1Pl/K1atWLz5s1s3LjRvu3KlStMnTqVwMBAKlasmOT7YmNjuXbtmsO20qVL4+PjQ1xcHAChoaH4+voycuRIbty4kegzzp07d9/afvvtNzZv3uzwnpkzZzoc9yDnuXWbEsxWkokTJ5ItWzaaN29+z5r+7VbLy79bWv49ii4+Pt7h9haYAa1IkSL265USRYoUoXLlynzzzTdcvnzZvn316tXs3r07xZ8vkh7UwiOSxjw9PalTpw5r167Fy8uLWrVqOewPDg5m/PjxwINNOHgvtWrVYvLkyQwfPpwyZcpQsGBBmjVrxuuvv84PP/zAY489Zh9KfuXKFXbv3s38+fM5fvw4+fPnT/H5Bw0axPfff0/Lli155ZVXyJs3LzNmzODYsWP873//w80t6f/nOnToEM2bN+fJJ5+kYsWKeHh4sHDhQs6ePUvnzp0Bs+/M5MmT6datGzVr1qRz584UKFCAEydOsGTJEho0aOAQPP7tjTfe4Ntvv+XRRx+lX79+9mHpJUqUYNeuXfbjnD2Pt7c3y5cvJywsjKCgIJYtW8aSJUt466237tpn6m58fX1p3LgxY8aM4caNGxQtWpRffvmFY8eOORx36dIlihUrxhNPPEG1atXIlSsXv/76K1u2bLH/u5VSI0eOpG3btjRo0ICePXvyzz//MHHiRCpXruwQgkQyLCuHiIlkFYMHDzYAIzg4ONG+BQsWGIDh4+Nj3Lx5M9F+IMnhxiVKlDDCwsLsr5Malh4ZGWm0bt3a8PHxMQCHIeqXLl0yBg8ebJQpU8bw9PQ08ufPbwQHBxvjxo0zrl+/fs/vU6JECaN169aJtjdp0iTRMPgjR44YTzzxhJE7d27D29vbqFu3rvHTTz85HPPvYennz583evfubZQvX97ImTOn4efnZwQFBRlz585NdM6VK1caoaGhhp+fn+Ht7W2ULl3a6NGjh/H777/f8zsYhmHs2rXLaNKkieHt7W0ULVrU+OCDD4wvv/wy0XVM7nnCwsKMnDlzGkeOHDEeeeQRI0eOHIa/v78xdOhQ+xD8O79vUsPuAWPo0KH213/99ZfRvn17I3fu3Iafn5/RsWNH4/Tp0w7HxcXFGa+//rpRrVo1w8fHx8iZM6dRrVo147PPPnP47LsNS09OHYZhGLNnzzbKly9veHl5GZUrVzZ++OEHo0OHDkb58uXvc6VFrGczDPVKExFJDT169GD+/PlZqsWjevXqFChQgPDwcKtLEbkn9eEREZH7unHjRqK+XatWrWLnzp0OS5aIZFTqwyMiIvd16tQpQkJCePrppylSpAgHDhxgypQpFCpUiF69elldnsh9KfCIiMh95cmTh1q1avHFF19w7tw5cubMSevWrfnwww/vup6bSEaiPjwiIiLi8tSHR0RERFyeAo+IiIi4vCzXhychIYHTp0/j4+Nz12n3RUREJGMxDINLly5RpEiRu05aei9ZLvCcPn2agIAAq8sQERGRB3Dy5EmKFSvm9PuyXODx8fEBzAvm6+trcTUiIiKSHDExMQQEBNh/x52V5QLPrdtYvr6+CjwiIiKZzIN2R1GnZREREXF5CjwiIiLi8hR4RERExOUp8IiIiIjLU+ARERERl6fAIyIiIi5PgUdERERcngKPiIiIuDwFHhEREXF5CjwiIiLi8hR4RERExOUp8IiIiIjLU+ARERERl6fAIyIiIi5PgUdERERcngKPiIiIuDwFHhEREXF5CjwiIiLi8hR4RERExOUp8IiIiIjLU+ARERERl6fAIyIiIi5PgUdERERcngKPiIiIuDwFHhEREXF5CjwiIiLi8iwNPGvWrKFNmzYUKVIEm83GokWL7vueVatWUbNmTby8vChTpgxff/11mtcpIiIimZulgefKlStUq1aNSZMmJev4Y8eO0bp1ax5++GF27NhB//79ee655/j555/TuFIRERHJzDysPHnLli1p2bJlso+fMmUKJUuWZPz48QBUqFCBdevW8fHHHxMaGppWZYqIiEgml6n68GzcuJGQkBCHbaGhoWzcuNGiikRERCQzsLSFx1mRkZH4+/s7bPP39ycmJoarV6+SPXv2RO+Ji4sjLi7O/jomJibN6xQREZGMJVO18DyIUaNG4efnZ38EBARYXZKIiIiks0wVeAoVKsTZs2cdtp09exZfX98kW3cABg8ezMWLF+2PkydPpkepIiIikoFkqlta9evXZ+nSpQ7bwsPDqV+//l3f4+XlhZeXV1qXJiIiIhmYpS08ly9fZseOHezYsQMwh53v2LGDEydOAGbrTPfu3e3H9+rVi6NHj/LGG29w4MABPvvsM+bOncuAAQOsKF9EREQyCUsDz++//06NGjWoUaMGAAMHDqRGjRq8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7LPPsn37dtq1a0e7du3Ys2dPOlcuIiIimYXNMAzDygKCgoKoU6cOEydOBCAhIYGAgAD69u3LoEGDEh3fqVMnrly5wk8//WTfVq9ePapXr86UKVPue76YmBj8/Py4ePEivr6+qfdFREREJM2k9Pfb0hae69evs3XrVkJCQuzb3NzcCAkJYePGjUm+Z+PGjQ7HA4SGht71eBEREREPK09+/vx54uPj8ff3d9ju7+/PgQMHknxPZGRkksdHRkYmeXxcXBxxcXH21xcvXgTMpCgiIiKZw63f7Qe9MWVp4EkPo0aN4v3330+0PSAgwIJqREREJCUuXbqEn5+f0++zNPDkz58fd3d3zp4967D97NmzFCpUKMn3FCpUyKnjBw8ezMCBA+2vExISuHDhAvny5cNmsyWrzpiYGAICAjh58qT6/aQzXXvr6NpbR9feOrr21rnftTcMg0uXLlGkSJEH+nxLA4+npye1atUiIiKCdu3aAWYgiYiIoE+fPkm+p379+kRERNC/f3/7tvDwcOrXr5/k8V5eXnh5eTlsy5079wPV6+vrq/8ALKJrbx1de+vo2ltH194697r2D9Kyc4vlt7QGDhxIWFgYtWvXpm7dukyYMIErV67Qs2dPALp3707RokUZNWoUAP369aNJkyaMHz+e1q1bM3v2bH7//XemTp1q5dcQERGRDMzywNOpUyfOnTvHu+++S2RkJNWrV2f58uX2jsknTpzAze32YLLg4GBmzZrFO++8w1tvvcVDDz3EokWLqFy5slVfQURERDI4ywMPQJ8+fe56C2vVqlWJtnXs2JGOHTumcVW3eXl5MXTo0ES3xiTt6dpbR9feOrr21tG1t05aX3vLJx4UERERSWuWLy0hIiIiktYUeERERMTlKfCIiIiIy1PgEREREZenwJMMkyZNIjAwEG9vb4KCgti8ebPVJbmcUaNGUadOHXx8fChYsCDt2rXj4MGDDsdcu3aN3r17ky9fPnLlykWHDh0SzbotKfPhhx9is9kcJvbUdU87p06d4umnnyZfvnxkz56dKlWq8Pvvv9v3G4bBu+++S+HChcmePTshISEcPnzYwopdQ3x8PEOGDKFkyZJkz56d0qVL88EHHzis0aRrnzrWrFlDmzZtKFKkCDabjUWLFjnsT851vnDhAl27dsXX15fcuXPz7LPPcvnyZadrUeC5jzlz5jBw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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "x_coherence = []\n", + "Ls = range(2,100,5)\n", + "for L in Ls:\n", + " c=Circuit((L)*[('Gi' ,0)])\n", + " ErrorDict={'Gi' : {('H','Z'): 1}}\n", + " EndErrors = ErrorPropagator(c,ErrorDict,NonMarkovian=True)\n", + "\n", + " corr=np.eye(len(c))*.01\n", + " error = averaged_evolution(corr,EndErrors,1)\n", + "\n", + " x_coherence += [np.real(error[1,1])]\n", + "plt.plot(Ls,x_coherence, color='blue')\n", + "plt.ylim(0,1.1)\n", + "plt.xlabel('Circuit Length')\n", + "plt.ylabel('X Coherence Decay')\n", + "plt.title('White noise dephasing')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[[('H', ('X',), (1+0j))]], [[('H', ('X',), (1+0j))]], [[('H', ('X',), (1+0j))]], [[('H', ('X',), (1+0j))]], [[('H', ('X',), (1+0j))]], [[('H', ('X',), (1+0j))]], [[('H', ('X',), (1+0j))]], [[('H', ('X',), (1+0j))]], [[('H', ('X',), (1+0j))]], [[('H', ('X',), (1+0j))]]]\n" + ] + } + ], + "source": [ + "list=[propagatableerrorgen('H',['X'],1)]\n", + "errors=ErrorPropagator(c,list,NonMarkovian=True,ErrorLayerDef=True)\n", + "print(errors)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "PyGSTi_EOC", + "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.4" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/pygsti/circuits/circuit.py b/pygsti/circuits/circuit.py index 8e629031a..4cccaadc5 100644 --- a/pygsti/circuits/circuit.py +++ b/pygsti/circuits/circuit.py @@ -3707,6 +3707,60 @@ def _write_q_circuit_tex(self, filename): # TODO f.write("\\end{document}") f.close() + + def convert_to_stim_tableau_layers(self,gate_name_conversions=None): + """ + Converts this circuit to a list of stim tableau layers + + Parameters + ---------- + gate_name_conversions : Dict + A map from pygsti gatenames to standard stim tableaus. If set to None a standard set of gate names is used + + Returns + ------- + A layer by layer list of stim tabluaes + """ + try: + import stim + except ImportError: + raise ImportError("Stim is required for this operation, and it does not appear to be installed.") + if gate_name_conversions is None: + gate_name_conversions = _itgs.standard_gatenames_stim_conversions() + + qubits=len(self.line_labels) + stim_layers=[] + for j in range(self.depth): + layer = self.layer(j) + stim_layer=stim.Tableau(qubits) + for sub_lbl in layer: + temp = gate_name_conversions[sub_lbl.name] + + stim_layer.append(temp,sub_lbl.qubits) + stim_layers.append(stim_layer) + return stim_layers + + def convert_to_stim_tableau(self,gate_name_conversions=None): + """ + Converts this circuit to a stim tableu + + Parameters + ---------- + gate_name_conversions : Dict + A map from pygsti gatenames to standard stim tableaus. If set to None a standard set of gate names is used + + Returns + ------- + A single stim tableu representing the entire circuit + """ + layers=self.convert_to_stim_tableau_layers(gate_name_conversions) + tableu=layers.pop(0) + for layer in layers: + tableu=tableu*layer + return tableu + + + def convert_to_cirq(self, qubit_conversion, wait_duration=None, diff --git a/pygsti/extras/errorgenpropagation/error_propagator_obj.py b/pygsti/extras/errorgenpropagation/error_propagator_obj.py new file mode 100644 index 000000000..77ab5bdf1 --- /dev/null +++ b/pygsti/extras/errorgenpropagation/error_propagator_obj.py @@ -0,0 +1,307 @@ +import copy as _copy +import numpy as _np +from pygsti.extras.errorgenpropagation.localstimerrorgen import localstimerrorgen +import stim + +class error_propagator: + def __init__(self,circ,potential_errors,label_layers=False,labels=None,gate_lbled=False): + self.circ=circ + if not gate_lbled: + self.__prop_dict=self.__build_error_dict(potential_errors,label_layers,labels) + else: + self.__prop_dict=self.__build_gate_labeled_dict(potential_errors) + + def __build_gate_labeled_dict(self,errors): + #converts pygsti circuit to stim tableau + stim_layers=self.circ.convert_to_stim_tableau_layers() + stim_layers.pop(0) + + # list to hold the propagation + propagation_layers=[] + #go through the list of sub circuits until there are none left + while len(stim_layers)>0: + #get first stim layer + top_layer=stim_layers.pop(0) + #find stim tableu until the end + for layer in stim_layers: + top_layer = layer*top_layer + propagation_layers.append(top_layer) + + #create a bunch of error layers including every layer in our model + errorLayers=[] + for layer in self.circ: + error_layer=[] + for gate_lbl in layer: + for errs in errors[tuple(gate_lbl)]: + errType=errs[0] + paulis=[] + p1='I'*len(self.circ.line_labels) + + + qbt1=gate_lbl[1] + if gate_lbl[0] =='Gcphase': + qbt2=gate_lbl[2] + if len(errs[1]) != 2: + sub_pauli=errs[1].split(':')[0] + qbt1=int(errs[1].split(':')[1].split(',')[0]) + if len(errs[1])==6: + qbt2=int(errs[1].split(':')[1].split(',')[1]) + else: + qbt2=None + else: + sub_pauli=errs[1] + p1=p1[:qbt1]+sub_pauli[0]+p1[(qbt1+1):] + if qbt2 is not None: + p1=p1[:qbt2]+sub_pauli[1]+p1[(qbt2+1):] + + + paulis.append(stim.PauliString(p1)) + error_layer.append(localstimerrorgen(errType,paulis,label=tuple(gate_lbl))) + errorLayers.append(error_layer) + fully_propagated_layers=[] + #get a subcircuit starting at layer idx + for (idx,layer) in enumerate(errorLayers): + #create a dictionary + new_error_dict=dict() + if idx 0: + #get first stim layer + top_layer=stim_layers.pop(0) + #find stim tableu until the end + for layer in stim_layers: + top_layer = layer*top_layer + propagation_layers.append(top_layer) + + #create a bunch of error layers including every layer in our model + errorLayers=[[_copy.deepcopy(eg) for eg in error_model] for i in range(self.circ.depth)] + + fully_propagated_layers=[] + #get a subcircuit starting at layer idx + for (idx,layer) in enumerate(errorLayers): + #create a dictionary + new_error_dict=dict() + if idx 0, and 'IX' -> 1, and 'ZZ' -> 15. + + ps: str, list, or tuple. + + num_qubits: int + + Returns + int + """ + idx = 0 + p_to_i = {'I': 0, 'X': 1, 'Y': 2, 'Z': 3} + for i in range(num_qubits): + idx += p_to_i[ps[num_qubits - 1 - i]] * 4**i + return idx \ No newline at end of file diff --git a/pygsti/extras/errorgenpropagation/errordict_deprecated.py b/pygsti/extras/errorgenpropagation/errordict_deprecated.py new file mode 100644 index 000000000..95dadea0b --- /dev/null +++ b/pygsti/extras/errorgenpropagation/errordict_deprecated.py @@ -0,0 +1,10 @@ +from pygsti.extras.errorgenpropagation.propagatableerrorgen import propagatableerrorgen +from numpy import complex128 + +class errordict(dict): + + def __setitem__(self, __key: any, __value: any) -> None: + if __key in self : + super().__setitem__(__key,self[__key]+__value) + else: + super().__setitem__(__key,__value) \ No newline at end of file diff --git a/pygsti/extras/errorgenpropagation/errorpropagator.py b/pygsti/extras/errorgenpropagation/errorpropagator.py new file mode 100644 index 000000000..f58115d3e --- /dev/null +++ b/pygsti/extras/errorgenpropagation/errorpropagator.py @@ -0,0 +1,239 @@ +import stim +from pygsti.extras.errorgenpropagation.propagatableerrorgen import * +from pygsti.extras.errorgenpropagation.utilspygstistimtranslator import * +from numpy import abs,zeros, complex128 + +from numpy.linalg import multi_dot +from scipy.linalg import expm +from pygsti.tools.internalgates import standard_gatenames_stim_conversions + + +''' +takes a pygsti circuit where each gate has a defined error model and returns the errorgenerators necessary to create an +end of circuit error generator under a variety of scenarios + +circ: pygsti circuit +errorModel: Dictionary defined the small markovian error generators and their rates for each gate +BCHOrder: in cases where the BCH approximation is used, carries it out to the desired order (can currently only handle order 1 or 2) +BCHLayerWise: If true will push the errors through one layer of gatesand then combines them using the bch approximation at each layer +If false will simply push all errors to the end +NonMarkovian: Pushes the error generators to the end and then formats them to work with the cumulant expansion code +MultiGateDict: Containts the translation between a numbered gate Gxpi22 and the PyGSTi standard gate used when a singular gate has +multiple error iterations +MultiGate: lets the code know +returns: list of propagatableerrorgens +''' +def ErrorPropagator(circ,errorModel,MultiGateDict={},BCHOrder=1,BCHLayerwise=False,NonMarkovian=False,MultiGate=False,ErrorLayerDef=False): + stim_dict=standard_gatenames_stim_conversions() + if MultiGate: + for key in MultiGateDict: + stim_dict[key]=stim_dict[MultiGateDict[key]] + stim_layers=circ.convert_to_stim_tableau_layers(gate_name_conversions=stim_dict) + stim_layers.pop(0) #Immeditielty toss the first layer because it is not important, + + propagation_layers=[] + if not BCHLayerwise or NonMarkovian: + while len(stim_layers) != 0: + top_layer=stim_layers.pop(0) + for layer in stim_layers: + top_layer = layer*top_layer + propagation_layers.append(top_layer) + else: + propagation_layers = stim_layers + + if not ErrorLayerDef: + errorLayers=buildErrorlayers(circ,errorModel,len(circ.line_labels)) + else: + errorLayers=[[errorModel]]*circ.depth #this doesn't work + + + num_error_layers=len(errorLayers) + fully_propagated_layers=[] + for _ in range(0,num_error_layers-1): + err_layer=errorLayers.pop(0) + layer=propagation_layers.pop(0) + for err_order in err_layer: + for errorGenerator in err_order: + errorGenerator.propagate_error_gen_inplace_tableau(layer) + + if BCHLayerwise and not NonMarkovian: + following_layer = errorLayers.pop(0) + new_errors=BCH_Handler(err_layer,following_layer,BCHOrder) + errorLayers.insert(new_errors,0) + else: + fully_propagated_layers.append(err_layer) + + fully_propagated_layers.append(errorLayers.pop(0)) + if BCHLayerwise and not NonMarkovian: + for order in errorLayers: + for error in order: + if len(fully_propagated_layers)==0: + fully_propagated_layers.append(error) + elif error in fully_propagated_layers: + idy=fully_propagated_layers.index(error) + new_error=error+fully_propagated_layers[idy] + fully_propagated_layers.pop(idy) + fully_propagated_layers.append(new_error) + else: + fully_propagated_layers.append(error) + return fully_propagated_layers + + elif not BCHLayerwise and not NonMarkovian: + simplified_EOC_errors=[] + if BCHOrder == 1: + for layer in fully_propagated_layers: + for order in layer: + for error in order: + if len(simplified_EOC_errors)==0: + simplified_EOC_errors.append(error) + elif error in simplified_EOC_errors: + idy=simplified_EOC_errors.index(error) + new_error=error+simplified_EOC_errors[idy] + simplified_EOC_errors.pop(idy) + if not (abs(new_error.get_Error_Rate()) <.000001): + simplified_EOC_errors.append(new_error) + else: + if not (abs(error.get_Error_Rate())<.000001): + simplified_EOC_errors.append(error) + else: + Exception("Higher propagated through Errors are not Implemented Yet") + return simplified_EOC_errors + + else: + return fully_propagated_layers + + +''' +takes two error layers (list of propagatableerrorgens) and find the bch combination of the two +err_layer: list lists of propagatableerrorgens +following_layer: list of propagatableerrorgens +BCHOrder: Order to carry the bch expansion out to, can currently be set to one or two +returns list of lists of propagatableerrorgens. The outer list contains each of them individual list denote order +''' +def BCH_Handler(err_layer,following_layer,BCHOrder): + new_errors=[] + for curr_order in range(0,BCHOrder): + working_order=[] + if curr_order == 0: + used_indexes=[] + for error in err_layer[curr_order]: + try: + idy=following_layer[curr_order].index(error) + working_order.append(error+following_layer[curr_order][idy]) + used_indexes.append(idy) + except: + working_order.append(error) + for idy,error in enumerate(following_layer[curr_order]): + if idy in used_indexes: + continue + else: + working_order.append(error) + + new_errors.append(working_order) + elif curr_order ==1: + working_order=[] + for error1 in err_layer[curr_order-1]: + for error2 in following_layer[curr_order-1]: + errorlist = commute_errors(error1,error2,BCHweight=1/2) + for error3 in errorlist: + if len(working_order)==0: + working_order.append(error3) + elif error3 in working_order: + idy=working_order.index(error3) + new_error=error3+working_order[idy] + working_order.pop(idy) + working_order.append(new_error) + else: + working_order.append(error3) + if len(err_layer)==2: + for error3 in err_layer[1]: + if len(working_order)==0: + working_order.append(error3) + elif error3 in working_order: + idy=working_order.index(error3) + new_error=error3+working_order[idy] + working_order.pop(idy) + working_order.append(new_error) + else: + working_order.append(error3) + if len(following_layer)==2: + for error3 in following_layer[1]: + if len(working_order)==0: + working_order.append(error3) + elif error3 in working_order: + idy=working_order.index(error3) + new_error=error3+working_order[idy] + working_order.pop(idy) + if new_error.get_Error_Rate() != 0j: + working_order.append(new_error) + else: + working_order.append(error3) + new_errors.append(working_order) + + else: + Exception("Higher Orders are not Implemented Yet") + + +''' +takes a pygst circuit object and error Dictionary and creates error layers + +inputs +circ: pygsti circuit +errorDict: Dictionary defined the small markovian error generators and their rates for each gate +qubits: number of qubits in the circuit + +output +ErrorGens, a list of error gen layers (which are list of propagatable errorgens) + +''' +def buildErrorlayers(circ,errorDict,qubits): + ErrorGens=[] + #For the jth layer of each circuit + for j in range(circ.depth): + l = circ.layer(j) # get the layer + errorLayer=[] + for _, g in enumerate(l): # for gate in layer l + gErrorDict = errorDict[g.name] #get the errors for the gate + p1=qubits*'I' # make some paulis why? + p2=qubits*'I' + for errs in gErrorDict: #for an error in the accompanying error dictionary + errType=errs[0] + paulis=[] + for ind,el in enumerate(g): #enumerate the gate ind =0 is name ind = 1 is first qubit ind = 2 is second qubit + if ind !=0: #if the gate element of concern is not the name + p1=p1[:el] + errs[1][ind-1] +p1[(el+1):] + + paulis.append(p1) + if errType in "CA": + for ind,el in enumerate(g): + if ind !=0: + p2=p2[:el] + errs[2][ind-1] +p2[(el+1):] + paulis.append(p2) + errorLayer.append(propagatableerrorgen(errType,paulis,gErrorDict[errs])) + ErrorGens.append([errorLayer]) + + return ErrorGens + + + +# There's a factor of a half missing in here. +def nm_propagators(corr, Elist,qubits): + Kms = [] + for idm in range(len(Elist)): + Am=zeros([4**qubits,4**qubits],dtype=complex128) + for idmm in range(len(Elist[idm][0])): + Am += Elist[idm][0][idmm].toWeightedErrorBasisMatrix() + # This assumes that Elist is in reverse chronological order + partials = [] + for idn in range(idm, len(Elist)): + An=zeros([4**qubits,4**qubits],dtype=complex128) + for idnn in range(len(Elist[idn][0])): + An = Elist[idn][0][idnn].toWeightedErrorBasisMatrix() + partials += [corr[idm,idn] * Am @ An] + partials[0] = partials[0]/2 + Kms += [sum(partials,0)] + return Kms + +def averaged_evolution(corr, Elist,qubits): + Kms = nm_propagators(corr, Elist,qubits) + return multi_dot([expm(Km) for Km in Kms]) \ No newline at end of file diff --git a/pygsti/extras/errorgenpropagation/errorpropagator_dev.py b/pygsti/extras/errorgenpropagation/errorpropagator_dev.py new file mode 100644 index 000000000..69c9c931e --- /dev/null +++ b/pygsti/extras/errorgenpropagation/errorpropagator_dev.py @@ -0,0 +1,258 @@ +import stim +from pygsti.extras.errorgenpropagation.localstimerrorgen import * +from numpy import abs,zeros, complex128 +from numpy.linalg import multi_dot +from scipy.linalg import expm +from pygsti.tools.internalgates import standard_gatenames_stim_conversions +from pygsti.extras.errorgenpropagation.utilserrorgenpropagation import * +import copy as _copy + +def ErrorPropagatorAnalytic(circ,errorModel,ErrorLayerDef=False,startingErrors=None): + stim_layers=circ.convert_to_stim_tableau_layers() + + if startingErrors is None: + stim_layers.pop(0) + + propagation_layers=[] + while len(stim_layers)>0: + top_layer=stim_layers.pop(0) + for layer in stim_layers: + top_layer = layer*top_layer + propagation_layers.append(top_layer) + + if not ErrorLayerDef: + errorLayers=buildErrorlayers(circ,errorModel,len(circ.line_labels)) + else: + errorLayers=[[_copy.deepcopy(eg) for eg in errorModel] for i in range(circ.depth)] + + if not startingErrors is None: + errorLayers.insert(0,startingErrors) + + + fully_propagated_layers=[] + for (idx,layer) in enumerate(errorLayers): + new_error_dict=dict() + if idx Weight(self.basis_element_labels[1]): + return Weight(self.basis_element_labels[0]) + else: + return Weight(self.basis_element_labels[1]) + + + ''' + propagates a propagatableerrorgen object through a clifford layer, returns the created error gen + ''' + def propagate_error_gen_inplace(self, player): + slayer = pyGSTiLayer_to_stimLayer(player) + new_basis_labels = [] + weightmod = 1 + for pauli in self.basis_element_labels: + temp=pyGSTiPauli_2_stimPauli(pauli) + temp = slayer(temp) + weightmod=weightmod*temp.sign + new_basis_labels.append(stimPauli_2_pyGSTiPauli(temp)) + + if self.errorgen_type in 'HCA': + self.error_rate=self.error_rate*weightmod + self.basis_element_labels =tuple(new_basis_labels) + + ''' + using stim propagates the associated pauli labels through a stim tableu object, the object is modified inplace + ''' + def propagate_error_gen_inplace_tableau(self, slayer): + new_basis_labels = [] + weightmod = 1 + for pauli in self.basis_element_labels: + temp=pyGSTiPauli_2_stimPauli(pauli) + temp = slayer(temp) + weightmod=weightmod*temp.sign + new_basis_labels.append(stimPauli_2_pyGSTiPauli(temp)) + if self.errorgen_type in 'HCA': + self.error_rate=self.error_rate*weightmod + self.basis_element_labels =tuple(new_basis_labels) + + + + ''' + returns the strings representing the pauli labels in the pygsti representation of paulis as stim PauliStrings + ''' + def returnStimPaulis(self): + paulis_string=[] + for pauli in self.basis_element_labels: + paulis_string.append(stim.PauliString(pauli)) + return tuple(paulis_string) + + ''' + Returns the errorbasis matrix for the associated errorgenerator mulitplied by its error rate + + input: A pygsti defined matrix basis by default can be pauli-product, gellmann 'gm' or then pygsti standard basis 'std' + functions defaults to pauli product if not specified + ''' + def toWeightedErrorBasisMatrix(self,matrix_basis='pp'): + PauliDict={ + 'I' : array([[1.0,0.0],[0.0,1.0]]), + 'X' : array([[0.0j, 1.0+0.0j], [1.0+0.0j, 0.0j]]), + 'Y' : array([[0.0, -1.0j], [1.0j, 0.0]]), + 'Z' : array([[1.0, 0.0j], [0.0j, -1.0]]) + } + paulis=[] + for paulistring in self.basis_element_labels: + for idx,pauli in enumerate(paulistring): + if idx == 0: + pauliMat = PauliDict[pauli] + else: + pauliMat=kron(pauliMat,PauliDict[pauli]) + paulis.append(pauliMat) + if self.errorgen_type in 'HS': + return self.error_rate*change_basis(create_elementary_errorgen(self.errorgen_type,paulis[0]),'std',matrix_basis) + else: + return self.error_rate*change_basis(create_elementary_errorgen(self.errorgen_type,paulis[0],paulis[1]),'std',matrix_basis) + + + + + +''' +Returns the Commutator of two errors +''' +def commute_errors(ErG1,ErG2, weightFlip=1.0, BCHweight=1.0): + def com(p1,p2): + P1 = pyGSTiPauli_2_stimPauli(p1) + P2=pyGSTiPauli_2_stimPauli(p2) + P3=P1*P2-P2*P1 + return (P3.weight,stimPauli_2_pyGSTiPauli(P3)) + + def acom(p1,p2): + P1 = pyGSTiPauli_2_stimPauli(p1) + P2=pyGSTiPauli_2_stimPauli(p2) + P3=P1*P2+P2*P1 + return (P3.weight,stimPauli_2_pyGSTiPauli(P3)) + + def labelMultiply(p1,p2): + P1 = pyGSTiPauli_2_stimPauli(p1) + P2=pyGSTiPauli_2_stimPauli(p2) + P3=P1*P2 + return (P3.weight,stimPauli_2_pyGSTiPauli(P3)) + + errorGens=[] + + wT=ErG1.getWeight()*ErG2.getWeight()*weightFlip*BCHweight + + if ErG1.getType()=='H' and ErG2.getType()=='H': + pVec=com(ErG1.getP1() , ErG2.getP2()) + errorGens.append( propagatableerrorgen( 'H' , [pVec[1]] , -1j*wT *pVec[0] ) ) + + elif ErG1.getType()=='H' and ErG2.getType()=='S': + pVec=com(ErG2.getP1() , ErG1.getP1()) + errorGens.append( propagatableerrorgen( 'C' , [ErG2.getP1() , pVec[1]] , 1j*wT*pVec[0] ) ) + + elif ErG1.getType()=='S' and ErG2.getType()=='H': + pVec=com(ErG2.getP1() , ErG1.getP1()) + errorGens.append( propagatableerrorgen( 'C' , [ErG2.getP1() , pVec[1]] , -1j*wT *pVec[0] ) ) + + elif ErG1.getType()=='H' and ErG2.getType()=='C': + pVec1=com(ErG2.getP1() , ErG1.getP1()) + errorGens.append( propagatableerrorgen('C' , [pVec1[1], ErG2.getP2()] , 1j*wT*pVec1[0] ) ) + pVec2=com(ErG2.getP2() , ErG1.getP1()) + errorGens.append( propagatableerrorgen('C' , [pVec2[1] , ErG2.getP1()] , 1j*wT*pVec2[0] ) ) + + elif ErG1.getType()=='C' and ErG2.getType()=='H': + errorGens = commute_errors(ErG2,ErG1,weightFlip=-1.0,BCHweight=BCHweight) + + elif ErG1.getType()=='H' and ErG2.getType()=='A': + pVec1 = com(ErG1.getP1() , ErG2.getP1()) + errorGens.append( propagatableerrorgen('A' , [pVec1[1] , ErG2.getP2()] , -1j*wT*pVec1[0]) ) + pVec2 = com(ErG1.getP1() , ErG2.getP2()) + errorGens.append( propagatableerrorgen('A' , [ErG2.getP1(), pVec2[1]] , -1j*wT*pVec2[0] ) ) + + elif ErG1.getType()=='A' and ErG2.getType()=='H': + errorGens = commute_errors(ErG2,ErG1,weightFlip=-1.0,BCHweight=BCHweight) + + elif ErG1.getType()=='S' and ErG2.getType()=='S': + errorGens.append( propagatableerrorgen('H', ErG1.getP1(),0 )) + + elif ErG1.getType()=='S' and ErG2.getType()=='C': + pVec1=labelMultiply(ErG1.getP1() , ErG2.getP1()) + pVec2=labelMultiply(ErG2.getP2() , ErG1.getP1()) + errorGens.append( propagatableerrorgen( 'A' , [pVec1[1], pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0] )) + pVec1 = labelMultiply(ErG1.getP1() , ErG2.getP2()) + pVec2 = labelMultiply(ErG2.getP1() , ErG1.getP1()) + errorGens.append( propagatableerrorgen( 'A', [pVec1[1], pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0])) + pVec1 =acom(ErG2.getP1(), ErG2.getP2()) + pVec2 = labelMultiply(pVec1[1],ErG1.getP1()) + errorGens.append( propagatableerrorgen( 'A' ,[pVec2[1], ErG1.getP1()] , -1j*.5*wT*pVec1[0]*pVec2[0])) + pVec1=acom(ErG2.getP1(), ErG2.getP2()) + pVec2=labelMultiply(ErG1.getP1(),pVec1[1]) + errorGens.append( propagatableerrorgen( 'A', [ErG1.getP1() ,pVec2[1]],-1j*.5*wT*pVec1[0]*pVec2[0])) + + elif ErG1.getType() == 'C' and ErG2.getType() == 'S': + errorGens = commute_errors(ErG2,ErG1,weightFlip=-1.0,BCHweight=BCHweight) + + elif ErG1.getType() == 'S' and ErG2.getType() == 'A': + pVec1 =labelMultiply(ErG1.getP1() , ErG2.getP1()) + pVec2=labelMultiply(ErG2.getP2() , ErG1.getP1()) + errorGens.append( propagatableerrorgen( 'C', [pVec1[1], pVec2[1]] ,1j*wT*pVec1[0]*pVec2[0] )) + pVec1=labelMultiply(ErG1.getP1() , ErG2.getP2()) + pVec2=labelMultiply(ErG2.getP1() , ErG1.getP1()) + errorGens.append( propagatableerrorgen( 'C', [pVec1[1], pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0])) + pVec1 = com(ErG2.getP1() , ErG2.getP2()) + pVec2 = com(ErG1.getP1(),pVec1[1]) + errorGens.append( propagatableerrorgen( 'A', [ErG1.getP1(), pVec2[1]] ,-.5*wT*pVec1[0]*pVec2[0])) + + elif ErG1.getType() == 'A' and ErG1.getType() == 'S': + errorGens = commute_errors(ErG2,ErG1,weightFlip=-1.0,BCHweight=BCHweight) + + elif ErG1.getType() == 'C' and ErG2.getType() == 'C': + A=ErG1.getP1() + B=ErG1.getP2() + P=ErG2.getP1() + Q=ErG2.getP2() + pVec1 = labelMultiply(A,P) + pVec2 =labelMultiply(Q,B) + errorGens.append( propagatableerrorgen( 'A', [pVec1[1], pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0] )) + pVec1 = labelMultiply(A,Q) + pVec2 =labelMultiply(P,B) + errorGens.append( propagatableerrorgen( 'A' , [pVec1[1] , pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0] )) + pVec1 = labelMultiply(B,P) + pVec2 =labelMultiply(Q,A) + errorGens.append( propagatableerrorgen( 'A' , [pVec1[1] , pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0] )) + pVec1 = labelMultiply(B,Q) + pVec2 =labelMultiply(P,A) + errorGens.append( propagatableerrorgen( 'A' , [pVec1[1] , pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0])) + pVec1=acom(A,B) + pVec2=com(P,pVec1[1]) + errorGens.append( propagatableerrorgen( 'A' , [pVec2[1] , Q ], -.5*1j*wT*pVec1[0]*pVec2[0])) + pVec1=acom(A,B) + pVec2=com(Q,pVec1[1]) + errorGens.append( propagatableerrorgen( 'A' , [pVec2[1], P] , -.5*1j*wT*pVec1[0]*pVec2[0])) + pVec1=acom(P,Q) + pVec2=com(pVec1[1],A) + errorGens.append( propagatableerrorgen( 'A' , [pVec2[1] , B] , -.5*1j*wT*pVec1[0]*pVec2[0])) + pVec1=acom(P,Q) + pVec2=com(pVec1[1],B) + errorGens.append( propagatableerrorgen( 'A' , [pVec2[1] , A ] , -.5*1j*wT*pVec1[0]*pVec2[0])) + pVec1=acom(A,B) + pVec2=acom(P,Q) + pVec3=com(pVec1[1],pVec2[1]) + errorGens.append( propagatableerrorgen( 'H', [pVec3[1]] ,.25*1j*wT*pVec1[0]*pVec2[0]*pVec3[0])) + + elif ErG1.getType() == 'C' and ErG2.getType() == 'A': + A=ErG1.getP1() + B=ErG1.getP2() + P=ErG2.getP1() + Q=ErG2.getP2() + pVec1 = labelMultiply(A,P) + pVec2 =labelMultiply(Q,B) + errorGens.append( propagatableerrorgen('C' , [pVec1[1],pVec2[1]] , 1j*wT*pVec1[0]*pVec2[0])) + pVec1 = labelMultiply(A,Q) + pVec2 =labelMultiply(P,B) + errorGens.append( propagatableerrorgen('C' ,[pVec1[1],pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0])) + pVec1 = labelMultiply(B,P) + pVec2 =labelMultiply(Q,A) + errorGens.append( propagatableerrorgen('C' , [pVec1[1],pVec2[1]] , 1j*wT*pVec1[0]*pVec2[0])) + pVec1 = labelMultiply(P,A) + pVec2 =labelMultiply(B,Q) + errorGens.append( propagatableerrorgen('C' ,[pVec1[1],pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0])) + pVec1 = com(P,Q) + pVec2 =com(A,pVec1[1]) + errorGens.append( propagatableerrorgen('A' , [pVec2[1] , B] , .5*wT*pVec1[0]*pVec2[0] )) + pVec1 = com(P,Q) + pVec2 =com(B,pVec1[1]) + errorGens.append( propagatableerrorgen('A' , [pVec2[1], A ], .5*wT*pVec1[0]*pVec2[0] )) + pVec1 = acom(A,B) + pVec2 =com(P,pVec1[1]) + errorGens.append( propagatableerrorgen('C', [pVec2[1] , Q ], .5*1j*wT*pVec1[0]*pVec2[0] )) + pVec1 = acom(A,B) + pVec2 =com(Q,pVec1[1]) + errorGens.append( propagatableerrorgen('C',[pVec2[1],P ],-.5*1j*wT*pVec1[0]*pVec2[0] )) + pVec1 = com(P,Q) + pVec2 =acom(A,B) + pVec3=com(pVec1[1],pVec2[1]) + errorGens.append( propagatableerrorgen('H',[pVec3[1]],-.25*wT*pVec1[0]*pVec2[0]*pVec3[0])) + + elif ErG1.getType() == 'A' and ErG2.getType() == 'C': + errorGens = commute_errors(ErG2,ErG1,weightFlip=-1.0,BCHweight=BCHweight) + + elif ErG1.getType() == 'A' and ErG2.getType() == 'A': + A=ErG1.getP1() + B=ErG1.getP2() + P=ErG2.getP1() + Q=ErG2.getP2() + pVec1=labelMultiply(Q,B) + pVec2=labelMultiply(A,P) + errorGens.append(propagatableerrorgen('A',[pVec1[1],pVec2[1]] ,-1j*wT*pVec1[0]*pVec2[0])) + pVec1=labelMultiply(P,A) + pVec2=labelMultiply(B,Q) + errorGens.append(propagatableerrorgen('A',[pVec1[1],pVec2[1]],-1j*wT*pVec1[0]*pVec2[0])) + pVec1=labelMultiply(B,P) + pVec2=labelMultiply(Q,A) + errorGens.append(propagatableerrorgen('A',[pVec1[1],pVec2[1]],-1j*wT*pVec1[0]*pVec2[0])) + pVec1=labelMultiply(A,Q) + pVec2=labelMultiply(P,B) + errorGens.append(propagatableerrorgen('A',[pVec1[1],pVec2[1]],-1j*wT*pVec1[0]*pVec2[0])) + pVec1=com(P,Q) + pVec2=com(B,pVec1[1]) + errorGens.append(propagatableerrorgen('C',[pVec2[1],A],.5*wT*pVec1[0]*pVec2[0])) + pVec1=com(P,Q) + pVec2=com(A,pVec1[1]) + errorGens.append(propagatableerrorgen('C',[pVec2[1],B] ,-.5*wT*pVec1[0]*pVec2[0])) + pVec1=com(A,B) + pVec2=com(P,pVec1[1]) + errorGens.append(propagatableerrorgen('C', [pVec2[1],Q] ,.5*wT*pVec1[0]*pVec2[0])) + pVec1=com(A,B) + pVec2=com(Q,pVec1[1]) + errorGens.append(propagatableerrorgen('C', [pVec2[1],P] ,-.5*wT*pVec1[0]*pVec2[0])) + pVec1=com(P,Q) + pVec2=com(A,B) + pVec3=com(pVec1[1],pVec2[1]) + errorGens.append( propagatableerrorgen('H',[pVec3[1]] ,.25*wT*pVec1[0]*pVec2[0]*pVec3[0])) + + + return errorGens + + diff --git a/pygsti/extras/errorgenpropagation/utilserrorgenpropagation.py b/pygsti/extras/errorgenpropagation/utilserrorgenpropagation.py new file mode 100644 index 000000000..ca07bdb58 --- /dev/null +++ b/pygsti/extras/errorgenpropagation/utilserrorgenpropagation.py @@ -0,0 +1,195 @@ + +from pygsti.extras.errorgenpropagation.localstimerrorgen import localstimerrorgen +from numpy import conjugate + +''' +Returns the Commutator of two errors +''' +def commute_errors(ErG1,ErG2, weightFlip=1.0, BCHweight=1.0): + def com(P1,P2): + P3=P1*P2-P2*P1 + return (P3.weight,P3*conjugate(P3.weight)) + + def acom(P1,P2): + P3=P1*P2+P2*P1 + return (P3.weight,P3*conjugate(P3.weight)) + + def labelMultiply(P1,P2): + P3=P1*P2 + return (P3.weight,P3*conjugate(P3.weight)) + + errorGens=[] + + wT=weightFlip*BCHweight + + if ErG1.getType()=='H' and ErG2.getType()=='H': + pVec=com(ErG1.basis_element_labels[0] , ErG2.basis_element_labels[0]) + errorGens.append( localstimerrorgen( 'H' , [pVec[1]] , -1j*wT *pVec[0] ) ) + + elif ErG1.getType()=='H' and ErG2.getType()=='S': + pVec=com(ErG2.basis_element_labels[0] , ErG1.basis_element_labels[0]) + errorGens.append( localstimerrorgen( 'C' , [ErG2.basis_element_labels[0] , pVec[1]] , 1j*wT*pVec[0] ) ) + + elif ErG1.getType()=='S' and ErG2.getType()=='H': + pVec=com(ErG2.basis_element_labels[0] , ErG1.basis_element_labels[0]) + errorGens.append( localstimerrorgen( 'C' , [ErG2.basis_element_labels[0] , pVec[1]] , -1j*wT *pVec[0] ) ) + + elif ErG1.getType()=='H' and ErG2.getType()=='C': + pVec1=com(ErG2.basis_element_labels[0] , ErG1.basis_element_labels[0]) + errorGens.append( localstimerrorgen('C' , [pVec1[1], ErG2.basis_element_labels[1]] , 1j*wT*pVec1[0] ) ) + pVec2=com(ErG2.basis_element_labels[1] , ErG1.basis_element_labels[0]) + errorGens.append( localstimerrorgen('C' , [pVec2[1] , ErG2.basis_element_labels[0]] , 1j*wT*pVec2[0] ) ) + + elif ErG1.getType()=='C' and ErG2.getType()=='H': + errorGens = commute_errors(ErG2,ErG1,weightFlip=-1.0,BCHweight=BCHweight) + + elif ErG1.getType()=='H' and ErG2.getType()=='A': + pVec1 = com(ErG1.basis_element_labels[0] , ErG2.basis_element_labels[0]) + errorGens.append( localstimerrorgen('A' , [pVec1[1] , ErG2.basis_element_labels[1]] , -1j*wT*pVec1[0]) ) + pVec2 = com(ErG1.basis_element_labels[0] , ErG2.basis_element_labels[1]) + errorGens.append( localstimerrorgen('A' , [ErG2.basis_element_labels[0], pVec2[1]] , -1j*wT*pVec2[0] ) ) + + elif ErG1.getType()=='A' and ErG2.getType()=='H': + errorGens = commute_errors(ErG2,ErG1,weightFlip=-1.0,BCHweight=BCHweight) + + elif ErG1.getType()=='S' and ErG2.getType()=='S': + errorGens.append( localstimerrorgen('H', ErG1.basis_element_labels[0],0 )) + + elif ErG1.getType()=='S' and ErG2.getType()=='C': + pVec1=labelMultiply(ErG1.basis_element_labels[0] , ErG2.basis_element_labels[0]) + pVec2=labelMultiply(ErG2.basis_element_labels[1] , ErG1.basis_element_labels[0]) + errorGens.append( localstimerrorgen( 'A' , [pVec1[1], pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0] )) + pVec1 = labelMultiply(ErG1.basis_element_labels[0] , ErG2.basis_element_labels[1]) + pVec2 = labelMultiply(ErG2.basis_element_labels[0] , ErG1.basis_element_labels[0]) + errorGens.append( localstimerrorgen( 'A', [pVec1[1], pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0])) + pVec1 =acom(ErG2.basis_element_labels[0], ErG2.basis_element_labels[1]) + pVec2 = labelMultiply(pVec1[1],ErG1.basis_element_labels[0]) + errorGens.append( localstimerrorgen( 'A' ,[pVec2[1], ErG1.basis_element_labels[0]] , -1j*.5*wT*pVec1[0]*pVec2[0])) + pVec1=acom(ErG2.basis_element_labels[0], ErG2.basis_element_labels[1]) + pVec2=labelMultiply(ErG1.basis_element_labels[0],pVec1[1]) + errorGens.append( localstimerrorgen( 'A', [ErG1.basis_element_labels[0] ,pVec2[1]],-1j*.5*wT*pVec1[0]*pVec2[0])) + + elif ErG1.getType() == 'C' and ErG2.getType() == 'S': + errorGens = commute_errors(ErG2,ErG1,weightFlip=-1.0,BCHweight=BCHweight) + + elif ErG1.getType() == 'S' and ErG2.getType() == 'A': + pVec1 =labelMultiply(ErG1.basis_element_labels[0] , ErG2.basis_element_labels[0]) + pVec2=labelMultiply(ErG2.basis_element_labels[1] , ErG1.basis_element_labels[0]) + errorGens.append( localstimerrorgen( 'C', [pVec1[1], pVec2[1]] ,1j*wT*pVec1[0]*pVec2[0] )) + pVec1=labelMultiply(ErG1.basis_element_labels[0] , ErG2.basis_element_labels[1]) + pVec2=labelMultiply(ErG2.basis_element_labels[0] , ErG1.basis_element_labels[0]) + errorGens.append( localstimerrorgen( 'C', [pVec1[1], pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0])) + pVec1 = com(ErG2.basis_element_labels[0] , ErG2.basis_element_labels[1]) + pVec2 = com(ErG1.basis_element_labels[0],pVec1[1]) + errorGens.append( localstimerrorgen( 'A', [ErG1.basis_element_labels[0], pVec2[1]] ,-.5*wT*pVec1[0]*pVec2[0])) + + elif ErG1.getType() == 'A' and ErG1.getType() == 'S': + errorGens = commute_errors(ErG2,ErG1,weightFlip=-1.0,BCHweight=BCHweight) + + elif ErG1.getType() == 'C' and ErG2.getType() == 'C': + A=ErG1.basis_element_labels[0] + B=ErG1.basis_element_labels[1] + P=ErG2.basis_element_labels[0] + Q=ErG2.basis_element_labels[1] + pVec1 = labelMultiply(A,P) + pVec2 =labelMultiply(Q,B) + errorGens.append( localstimerrorgen( 'A', [pVec1[1], pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0] )) + pVec1 = labelMultiply(A,Q) + pVec2 =labelMultiply(P,B) + errorGens.append( localstimerrorgen( 'A' , [pVec1[1] , pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0] )) + pVec1 = labelMultiply(B,P) + pVec2 =labelMultiply(Q,A) + errorGens.append( localstimerrorgen( 'A' , [pVec1[1] , pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0] )) + pVec1 = labelMultiply(B,Q) + pVec2 =labelMultiply(P,A) + errorGens.append( localstimerrorgen( 'A' , [pVec1[1] , pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0])) + pVec1=acom(A,B) + pVec2=com(P,pVec1[1]) + errorGens.append( localstimerrorgen( 'A' , [pVec2[1] , Q ], -.5*1j*wT*pVec1[0]*pVec2[0])) + pVec1=acom(A,B) + pVec2=com(Q,pVec1[1]) + errorGens.append( localstimerrorgen( 'A' , [pVec2[1], P] , -.5*1j*wT*pVec1[0]*pVec2[0])) + pVec1=acom(P,Q) + pVec2=com(pVec1[1],A) + errorGens.append( localstimerrorgen( 'A' , [pVec2[1] , B] , -.5*1j*wT*pVec1[0]*pVec2[0])) + pVec1=acom(P,Q) + pVec2=com(pVec1[1],B) + errorGens.append( localstimerrorgen( 'A' , [pVec2[1] , A ] , -.5*1j*wT*pVec1[0]*pVec2[0])) + pVec1=acom(A,B) + pVec2=acom(P,Q) + pVec3=com(pVec1[1],pVec2[1]) + errorGens.append( localstimerrorgen( 'H', [pVec3[1]] ,.25*1j*wT*pVec1[0]*pVec2[0]*pVec3[0])) + + elif ErG1.getType() == 'C' and ErG2.getType() == 'A': + A=ErG1.basis_element_labels[0] + B=ErG1.basis_element_labels[1] + P=ErG2.basis_element_labels[0] + Q=ErG2.basis_element_labels[1] + pVec1 = labelMultiply(A,P) + pVec2 =labelMultiply(Q,B) + errorGens.append( localstimerrorgen('C' , [pVec1[1],pVec2[1]] , 1j*wT*pVec1[0]*pVec2[0])) + pVec1 = labelMultiply(A,Q) + pVec2 =labelMultiply(P,B) + errorGens.append( localstimerrorgen('C' ,[pVec1[1],pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0])) + pVec1 = labelMultiply(B,P) + pVec2 =labelMultiply(Q,A) + errorGens.append( localstimerrorgen('C' , [pVec1[1],pVec2[1]] , 1j*wT*pVec1[0]*pVec2[0])) + pVec1 = labelMultiply(P,A) + pVec2 =labelMultiply(B,Q) + errorGens.append( localstimerrorgen('C' ,[pVec1[1],pVec2[1]] , -1j*wT*pVec1[0]*pVec2[0])) + pVec1 = com(P,Q) + pVec2 =com(A,pVec1[1]) + errorGens.append( localstimerrorgen('A' , [pVec2[1] , B] , .5*wT*pVec1[0]*pVec2[0] )) + pVec1 = com(P,Q) + pVec2 =com(B,pVec1[1]) + errorGens.append( localstimerrorgen('A' , [pVec2[1], A ], .5*wT*pVec1[0]*pVec2[0] )) + pVec1 = acom(A,B) + pVec2 =com(P,pVec1[1]) + errorGens.append( localstimerrorgen('C', [pVec2[1] , Q ], .5*1j*wT*pVec1[0]*pVec2[0] )) + pVec1 = acom(A,B) + pVec2 =com(Q,pVec1[1]) + errorGens.append( localstimerrorgen('C',[pVec2[1],P ],-.5*1j*wT*pVec1[0]*pVec2[0] )) + pVec1 = com(P,Q) + pVec2 =acom(A,B) + pVec3=com(pVec1[1],pVec2[1]) + errorGens.append( localstimerrorgen('H',[pVec3[1]],-.25*wT*pVec1[0]*pVec2[0]*pVec3[0])) + + elif ErG1.getType() == 'A' and ErG2.getType() == 'C': + errorGens = commute_errors(ErG2,ErG1,weightFlip=-1.0,BCHweight=BCHweight) + + elif ErG1.getType() == 'A' and ErG2.getType() == 'A': + A=ErG1.basis_element_labels[0] + B=ErG1.basis_element_labels[1] + P=ErG2.basis_element_labels[0] + Q=ErG2.basis_element_labels[1] + pVec1=labelMultiply(Q,B) + pVec2=labelMultiply(A,P) + errorGens.append(localstimerrorgen('A',[pVec1[1],pVec2[1]] ,-1j*wT*pVec1[0]*pVec2[0])) + pVec1=labelMultiply(P,A) + pVec2=labelMultiply(B,Q) + errorGens.append(localstimerrorgen('A',[pVec1[1],pVec2[1]],-1j*wT*pVec1[0]*pVec2[0])) + pVec1=labelMultiply(B,P) + pVec2=labelMultiply(Q,A) + errorGens.append(localstimerrorgen('A',[pVec1[1],pVec2[1]],-1j*wT*pVec1[0]*pVec2[0])) + pVec1=labelMultiply(A,Q) + pVec2=labelMultiply(P,B) + errorGens.append(localstimerrorgen('A',[pVec1[1],pVec2[1]],-1j*wT*pVec1[0]*pVec2[0])) + pVec1=com(P,Q) + pVec2=com(B,pVec1[1]) + errorGens.append(localstimerrorgen('C',[pVec2[1],A],.5*wT*pVec1[0]*pVec2[0])) + pVec1=com(P,Q) + pVec2=com(A,pVec1[1]) + errorGens.append(localstimerrorgen('C',[pVec2[1],B] ,-.5*wT*pVec1[0]*pVec2[0])) + pVec1=com(A,B) + pVec2=com(P,pVec1[1]) + errorGens.append(localstimerrorgen('C', [pVec2[1],Q] ,.5*wT*pVec1[0]*pVec2[0])) + pVec1=com(A,B) + pVec2=com(Q,pVec1[1]) + errorGens.append(localstimerrorgen('C', [pVec2[1],P] ,-.5*wT*pVec1[0]*pVec2[0])) + pVec1=com(P,Q) + pVec2=com(A,B) + pVec3=com(pVec1[1],pVec2[1]) + errorGens.append( localstimerrorgen('H',[pVec3[1]] ,.25*wT*pVec1[0]*pVec2[0]*pVec3[0])) + + + return errorGens \ No newline at end of file diff --git a/pygsti/extras/errorgenpropagation/utilspygstistimtranslator.py b/pygsti/extras/errorgenpropagation/utilspygstistimtranslator.py new file mode 100644 index 000000000..b6473318f --- /dev/null +++ b/pygsti/extras/errorgenpropagation/utilspygstistimtranslator.py @@ -0,0 +1,68 @@ +import stim +from numpy import conjugate + + + +''' +returns a dictionary capable of translating pygsti standard gate labels to stim tablue representations of gates +''' +def Gate_Translate_Dict_p_2_s(): + pyGSTi_to_stim_GateDict={ + 'Gi' : stim.Tableau.from_named_gate('I'), + 'Gxpi' : stim.Tableau.from_named_gate('X'), + 'Gypi' : stim.Tableau.from_named_gate('Y'), + 'Gzpi' : stim.Tableau.from_named_gate('Z'), + 'Gxpi2' : stim.Tableau.from_named_gate('SQRT_X'), + 'Gypi2' : stim.Tableau.from_named_gate('SQRT_Y'), + 'Gzpi2' : stim.Tableau.from_named_gate('SQRT_Z'), + 'Gxmpi2': stim.Tableau.from_named_gate('SQRT_X_DAG'), + 'Gympi2': stim.Tableau.from_named_gate('SQRT_Y_DAG'), + 'Gzmpi2': stim.Tableau.from_named_gate('SQRT_Z_DAG'), + 'Gh' : stim.Tableau.from_named_gate('H'), + 'Gxx' : stim.Tableau.from_named_gate('SQRT_XX'), + 'Gzz' : stim.Tableau.from_named_gate('SQRT_ZZ'), + 'Gcnot' : stim.Tableau.from_named_gate('CNOT'), + 'Gswap' : stim.Tableau.from_named_gate('SWAP') + } + return pyGSTi_to_stim_GateDict + + +''' +returns a dict translating the stim tableu (gate) key to pyGSTi gate keys +TODO: change the stim tablues to tablues keys +''' +def Gate_Translate_Dict_s_2_p(): + dict = Gate_Translate_Dict_p_2_s() + return {v: k for k, v in dict.items()} + +''' +Takes a layer of pyGSTi gates and composes them into a single stim Tableu +''' +def pyGSTiLayer_to_stimLayer(player,qubits,MultiGateDict={},MultiGate=False): + slayer=stim.Tableau(qubits) + stimDict=Gate_Translate_Dict_p_2_s() + for sub_lbl in player: + if not MultiGate: + temp = stimDict[sub_lbl.name] + else: + temp = stimDict[MultiGateDict[sub_lbl.name]] + slayer.append(temp,sub_lbl.qubits) + return slayer + +''' +Takes the typical pygsti label for paulis and returns a stim PauliString object +''' +def pyGSTiPauli_2_stimPauli(pauli): + return stim.PauliString(pauli) + + +''' +Converts a stim paulistring to the string typically used in pysti to label paulis +warning: stim ofter stores a pauli phase in the string (i.e +1,-1,+i,-i) this is assumed positive +in this function, if the weight is needed please store paulistring::weight prior to applying this function +''' +def stimPauli_2_pyGSTiPauli(pauliString): + n=1 + if pauliString.sign==1j or pauliString.sign==-1j: + n=2 + return str(pauliString)[n:].replace('_',"I") \ No newline at end of file diff --git a/pygsti/tools/internalgates.py b/pygsti/tools/internalgates.py index ec4a92f0d..08a43da51 100644 --- a/pygsti/tools/internalgates.py +++ b/pygsti/tools/internalgates.py @@ -370,7 +370,43 @@ def unitary_to_standard_gatename(unitary, up_to_phase = False, return_phase = Fa return std_name return None +def standard_gatenames_stim_conversions(): + """ + A dictionary converting the gates with standard names to stim tableus for these gates. Currently is only capable of converting + clifford gates, no capability for T gates + + TODO: Add all standard clifford gate names in + Returns + ------- + A dict mapping string to tableu + """ + try: + import stim + except ImportError: + raise ImportError("Stim is required for this operation, and it does not appear to be installed.") + pyGSTi_to_stim_GateDict={ + 'Gi' : stim.Tableau.from_named_gate('I'), + 'Gxpi' : stim.Tableau.from_named_gate('X'), + 'Gypi' : stim.Tableau.from_named_gate('Y'), + 'Gzpi' : stim.Tableau.from_named_gate('Z'), + 'Gxpi2' : stim.Tableau.from_named_gate('SQRT_X'), + 'Gypi2' : stim.Tableau.from_named_gate('SQRT_Y'), + 'Gzpi2' : stim.Tableau.from_named_gate('SQRT_Z'), + 'Gxmpi2': stim.Tableau.from_named_gate('SQRT_X_DAG'), + 'Gympi2': stim.Tableau.from_named_gate('SQRT_Y_DAG'), + 'Gzmpi2': stim.Tableau.from_named_gate('SQRT_Z_DAG'), + 'Gp' : stim.Tableau.from_named_gate('S'), + 'Gsm' : stim.Tableau.from_named_gate('S_DAG'), + 'Gh' : stim.Tableau.from_named_gate('H'), + 'Gxx' : stim.Tableau.from_named_gate('SQRT_XX'), + 'Gzz' : stim.Tableau.from_named_gate('SQRT_ZZ'), + 'Gcnot' : stim.Tableau.from_named_gate('CNOT'), + 'Gswap' : stim.Tableau.from_named_gate('SWAP'), + 'Gcz' : stim.Tableau.from_named_gate('CZ'), + 'Gcphase' : stim.Tableau.from_named_gate('CZ') + } + return pyGSTi_to_stim_GateDict def standard_gatenames_cirq_conversions(): """