diff --git a/.gitignore b/.gitignore index b6e4761..8b4de1a 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,6 @@ dmypy.json # Pyre type checker .pyre/ + + +.idea/ \ No newline at end of file diff --git a/3qubit Swap-test.ipynb b/3qubit Swap-test.ipynb new file mode 100644 index 0000000..36b6dd0 --- /dev/null +++ b/3qubit Swap-test.ipynb @@ -0,0 +1,741 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from qiskit import *\n", + "from qiskit.result.result import Result, ExperimentResult\n", + "from qiskit.providers.aer.noise import *\n", + "from qiskit.providers.aer.noise.device import basic_device_noise_model\n", + "\n", + "from qiskit.test.mock import FakeAthens\n", + "\n", + "from qiskit.transpiler import PassManager\n", + "from qiskit.transpiler.passes import Unroller\n", + "\n", + "import numpy as np\n", + "\n", + "import sys\n", + "sys.path.append('../')\n", + "\n", + "from qiskit_utilities.utilities import *\n", + "\n", + "from zero_noise_extrapolation_cnot import ZeroNoiseExtrapolation\n", + "from zero_noise_extrapolation import mitigate, Richardson_extrapolate\n", + "\n", + "import pickle\n", + "from typing import List\n", + "\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "\n", + "sim_backend = Aer.get_backend(\"qasm_simulator\")\n", + "mock_backend = FakeAthens()" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def add_swaptest_gate(qc, probe, q1, q2, barrier=False):\n", + " qc.toffoli(probe, q1, q2)\n", + " \n", + " if barrier:\n", + " qc.barrier()\n", + " \n", + " qc.toffoli(probe, q2, q1)\n", + " \n", + " if barrier:\n", + " qc.barrier()\n", + " \n", + " qc.toffoli(probe, q1, q2)\n", + " \n", + "def create_3qswaptest_circuit(barrier=False):\n", + " qc = QuantumCircuit(3,1)\n", + " \n", + " qc.h(0)\n", + " \n", + " qc.h(1)\n", + " \n", + " if barrier:\n", + " qc.barrier()\n", + " \n", + " add_swaptest_gate(qc, 0, 1, 2, barrier=barrier)\n", + " \n", + " if barrier:\n", + " qc.barrier()\n", + " \n", + " qc.h(0)\n", + " \n", + " qc.measure(0,0)\n", + " \n", + " return qc" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The SWAP-test circuit on two 1-qubit states is constructed as shown belown, using 3 Toffoli-gates (https://arxiv.org/abs/1712.09271).\n", + "\n", + "We prepare the states q_1 = (|0> + |1>)/sqrt(2) and q_2 = |0>, while q_0 is called the probe qubit. The Z-measurement on the probe qubits measures the overlap between the state on q_1 and q_2. The true expectation value in this case is 0.5." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qc = create_3qswaptest_circuit()\n", + "\n", + "qc.draw(output=\"mpl\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Each Toffoli-gate can be decomposed in the following way. Note that one Toffoli gate has a CNOT complexity of 6. A SWAP-test circuit on two n-qubit states, thus 2n + 1 qubits in total, requires 3n Toffoli gates." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "pass_manager = PassManager(Unroller([\"u1\",\"u2\",\"u3\",\"cx\"]))\n", + "\n", + "qc_swap = create_3qswaptest_circuit(barrier=True)\n", + "\n", + "qc_unrolled = pass_manager.run(qc_swap)\n", + "\n", + "qc_unrolled.draw(output=\"mpl\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We specify the expectation value function that computes the estimated expectation value from a set of quantum circuit experiment results.\n", + "\n", + "The repeating CNOTs implementation requires the expectation value function to return both the expectation value and the variances. The random Pauli-sampling implementation requires only the expectation value. To reuse code, we specify a filter to be passed to the function that specifies the method used." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def swaptest_exp_val_func(results: List[ExperimentResult], myfilter=None):\n", + " exp_vals = np.zeros(len(results))\n", + " variances = np.zeros(len(results))\n", + " for i,experiment_result in enumerate(results):\n", + " shots = experiment_result.shots\n", + " counts = experiment_result.data.counts\n", + " eigenval = 0\n", + " for key in counts.keys():\n", + " if key == \"0x0\":\n", + " eigenval = +1\n", + " elif key == \"0x1\":\n", + " eigenval = -1\n", + " exp_vals[i] += eigenval*counts[key] / shots\n", + " variances[i] = 1 - exp_vals[i]**2\n", + " if myfilter is not None and myfilter[\"repeatingcnots\"]:\n", + " return exp_vals, variances\n", + " else:\n", + " return exp_vals" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Zero noise extrapolation, mitigating noise on CNOT-gates, on the SWAP-test circuit. Executing the circuits on a simulator with a stochastic Pauli noise model. We choose the noise probability on CNOT-gates to be an order of magnitude larger than on the u1/u2/u3 single-qubit gates, which is realistic for modern quantum processors.\n", + "\n", + "Noise amplification factors = [1,3,5,7,9]" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Amplification factor = 1.\n", + "Noise amplified result successfully read from disk.\n", + "Noise amplified exp val = 0.39123917, variance = 0.84683265, total shots executed = 8388608.\n", + "Amplification factor = 3.\n", + "Noise amplified result successfully read from disk.\n", + "Noise amplified exp val = 0.27788734, variance = 0.92266582, total shots executed = 8388608.\n", + "Amplification factor = 5.\n", + "Noise amplified result successfully read from disk.\n", + "Noise amplified exp val = 0.19675660, variance = 0.96117035, total shots executed = 8388608.\n", + "Amplification factor = 7.\n", + "Noise amplified result successfully read from disk.\n", + "Noise amplified exp val = 0.13966966, variance = 0.98036979, total shots executed = 8388608.\n", + "Amplification factor = 9.\n", + "Noise amplified result successfully read from disk.\n", + "Noise amplified exp val = 0.09886336, variance = 0.99010501, total shots executed = 8388608.\n", + "Amplification factor = 11.\n", + "Noise amplified result successfully read from disk.\n", + "Noise amplified exp val = 0.07036972, variance = 0.99492316, total shots executed = 8388608.\n", + "Amplification factor = 13.\n", + "Noise amplified result successfully read from disk.\n", + "Noise amplified exp val = 0.05051708, variance = 0.99733688, total shots executed = 8388608.\n", + "-----\n", + "ERROR MITIGATION DONE\n", + "Bare circuit expectation value: 0.39123917\n", + "Noise amplified expectation values: [0.39123917 0.27788734 0.1967566 0.13966966 0.09886336 0.07036972\n", + " 0.05051708]\n", + "Circuit depths: [ 35 71 107 143 179 215 251]\n", + "-----\n", + "Mitigated expectation value: 0.46028259\n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "0.4602825874462453" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def create_depolarizing_error_model(p_cnot, p_u, p_meas):\n", + " X = np.asarray([[0, 1], [1, 0]])\n", + " Y = np.asarray([[0, -1j], [1j, 0]])\n", + " Z = np.asarray([[1, 0], [0, -1]])\n", + " I = np.asarray([[1, 0], [0, 1]])\n", + "\n", + " pauli_dict = {\"X\": X, \"Y\": Y, \"Z\": Z, \"I\": I}\n", + "\n", + " # Two-qubit depolarizing error channel with error rate p_cnot on CNOT-gates\n", + " kraus_operators_cnot = [np.sqrt(1-p_cnot)*np.kron(I,I)]\n", + "\n", + " for a in [\"I\",\"X\",\"Y\",\"Z\"]:\n", + " for b in [\"I\",\"X\",\"Y\",\"Z\"]:\n", + " if not ((a==\"I\") and (b==\"I\")):\n", + " op = np.kron(pauli_dict[a], pauli_dict[b])\n", + " kraus_operators_cnot.append(np.sqrt(p_cnot/15)*op)\n", + "\n", + " cnot_error = QuantumError(noise_ops=kraus_operators_cnot, number_of_qubits=2)\n", + "\n", + " # One-qubit depolarizing error channel with error rate p_u on all single-qubit gates\n", + " kraus_operators_u = [np.sqrt(1-p_u)*I, np.sqrt(p_u/3) * X, np.sqrt(p_u/3) * Y, np.sqrt(p_u/3)*Z]\n", + " u_error = QuantumError(noise_ops=kraus_operators_u, number_of_qubits=1)\n", + "\n", + " # One-qubit depolarizing error channel with error rate p_meas on all measurement gates\n", + " kraus_operators_meas = [np.sqrt(1-p_meas)*I, np.sqrt(p_meas/3) * X, np.sqrt(p_meas/3) * Y, np.sqrt(p_meas/3)*Z]\n", + " meas_error = QuantumError(noise_ops=kraus_operators_meas, number_of_qubits=1)\n", + " \n", + " noise_model = NoiseModel()\n", + " noise_model.add_all_qubit_quantum_error(cnot_error, [\"cx\"])\n", + " noise_model.add_all_qubit_quantum_error(u_error, [\"u2\",\"u3\"])\n", + " noise_model.add_all_qubit_quantum_error(meas_error, [\"measure\"])\n", + " \n", + " return noise_model\n", + "\n", + "p_cnot = 0.01\n", + "p_u = 0.001\n", + "p_meas = 0.05\n", + " \n", + "noise_model = create_depolarizing_error_model(p_cnot, p_u, p_meas)\n", + "\n", + "N_AMP_FACTORS = 7 # -> amplification_factors = [1,3,5,7,9,11,13]\n", + "SHOTS = 1024*8192\n", + "\n", + "qem = ZeroNoiseExtrapolation(qc=qc, exp_val_func=swaptest_exp_val_func, backend=sim_backend,\n", + " exp_val_filter={\"repeatingcnots\": True}, noise_model=noise_model,\n", + " n_amp_factors=N_AMP_FACTORS, shots=SHOTS,\n", + " save_results=True, experiment_name=\"3qswaptest_paulinoisemodel\")\n", + "\n", + "qem.mitigate(verbose=True)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To examine if this method of noise amplification works as expected, we construct a set of noise models where the noise on CNOT-gates is manually ajusted to be amplified by the same set of noise amplification factors. This is done by setting the error rate on the CNOT-gate to p_r = r * p_0, where r is the amplification factor and p_0 the bare error rate. Noise on single-qubit gates and on measurements is kept the same.\n", + "\n", + "Note that this does not represent an actual feasible method of noise amplification as we \"amplify\" the noise in the very definition of the specific different noise models. But this shall serve as a sanity check for the noise amplification scheme by CNOT repetition. If the scheme amplifies noise as expected we should find the noise amplified expectation value for each amplification factor to align closely with the expectation value found with the corresponding amplified noise model." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Amplified exp vals read from file.\n", + "[0.39198685 0.2758348 0.19391012 0.13463688 0.09251571 0.06230164\n", + " 0.04276776]\n" + ] + } + ], + "source": [ + "filename = \"results\" + \"/\" + \"3qswaptest_paulinoisemodel\" + \\\n", + " \"_noisemodelamplification_ampfactors{:}_shots{:}\".format(N_AMP_FACTORS, qem.shots)\n", + "\n", + "data_loaded = False\n", + "\n", + "if os.path.isfile(filename):\n", + " file = open(filename, \"rb\")\n", + " noisemodels_amplified_exp_vals = pickle.load(file)\n", + " file.close()\n", + " \n", + " if noisemodels_amplified_exp_vals is not None:\n", + " print(\"Amplified exp vals read from file.\")\n", + " data_loaded = True\n", + "\n", + "if not data_loaded:\n", + " amplified_noise_models = []\n", + "\n", + " for amplification_factor in qem.noise_amplification_factors:\n", + " p_cnot_amplified = amplification_factor * p_cnot\n", + "\n", + " amplified_noise_models.append(create_depolarizing_error_model(p_cnot_amplified, p_u, p_meas))\n", + "\n", + " noisemodels_amplified_exp_vals = np.zeros(np.shape(amplified_noise_models)[0])\n", + "\n", + " shots, repeats = qem.partition_shots(qem.shots)\n", + "\n", + " for i, nm in enumerate(amplified_noise_models):\n", + " job = execute([qem.qc for i in range(repeats)], sim_backend, shots=shots, noise_model=nm)\n", + " exp_vals_temp,_ = swaptest_exp_val_func(job.result().results)\n", + " noisemodels_amplified_exp_vals[i] = np.average(exp_vals_temp)\n", + " \n", + " file = open(filename, \"wb\")\n", + " pickle.dump(noisemodels_amplified_exp_vals, file)\n", + " file.close()\n", + " print(\"Amplified exp vals written to file.\")\n", + "\n", + "print(noisemodels_amplified_exp_vals)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plotting the noise amplified expectation values against each other, we find exactly what we expected. The noise amplification scheme by CNOT repetition seems to amplify the noise present in CNOT-gates very closely to the desired amplification factor." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "x = qem.noise_amplification_factors\n", + "y = qem.noise_amplified_exp_vals\n", + "\n", + "plt.xlabel(r\"$r$, noise amplification factor\")\n", + "plt.ylabel(r\"$E_r$, expectation value\")\n", + "\n", + "plt.xticks(x)\n", + "\n", + "plt.plot(x, noisemodels_amplified_exp_vals,'--o', label=\"Noise amplification by adjusted noise model\")\n", + "plt.plot(x, y,'--o', label=\"Noise amplification by CNOT repetition\")\n", + "\n", + "plt.legend()\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We plot the mitigated expectation value, as a function of the number of noise amplification factors used, and we can see a noticable improvement.\n", + "\n", + "From the plot above we observe that the noise amplified expectation values are being slightly over-estimated for the last amplification factors for the repeating CNOTs-method. Correspondingly, we observe that the mitigated expectation value flattens off and fails to improve when including these amplification factors." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "n_amp_factors = qem.n_amp_factors\n", + "noise_amplified_exp_vals = qem.noise_amplified_exp_vals\n", + "noise_amplification_factors = qem.noise_amplification_factors\n", + "\n", + "mitigated_exp_vals = np.zeros(n_amp_factors)\n", + "mitigated_exp_vals[0] = noise_amplified_exp_vals[0]\n", + "\n", + "for n in range(1, n_amp_factors):\n", + " mitigated_exp_vals[n] = Richardson_extrapolate(noise_amplified_exp_vals[0:n+1], noise_amplification_factors[0:n+1])[0]\n", + " \n", + "x = [i+1 for i in range(n_amp_factors)]\n", + "ideal_exp_val = [0.5 for i in range(n_amp_factors)]\n", + "\n", + "plt.plot(x, ideal_exp_val, '--g')\n", + "plt.plot(x, mitigated_exp_vals, '-o')\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Zero noise extrapolation, mitigating noise on CNOT-gates, on the SWAP-test circuit. Now on the mock backend FakeAthens that emulates the IBMQ Athens quantum device. This mock backend has the same configurations as the real device, and an noise model that aims to approximate the physical device as closely as possible.\n", + "\n", + "Noise amplification factors = [1,3,5,7,9]" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\ander\\.conda\\envs\\qcomp\\lib\\site-packages\\qiskit\\providers\\ibmq\\ibmqfactory.py:192: UserWarning: Timestamps in IBMQ backend properties, jobs, and job results are all now in local time instead of UTC.\n", + " warnings.warn('Timestamps in IBMQ backend properties, jobs, and job results '\n" + ] + } + ], + "source": [ + "#load IBMQ account\n", + "#IBMQ.save_account('Your IBMQ token')\n", + "IBMQ.load_account()\n", + "provider = IBMQ.get_provider(hub=\"ibm-q\")\n", + "\n", + "athens_backend = provider.get_backend(\"ibmq_athens\")\n", + "\n", + "athens_noise_model = NoiseModel.from_backend(athens_backend)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Amplification factor = 1.\n", + "Noise amplified result successfully read from disk.\n", + "Noise amplified exp val = 0.29174495, variance = 0.91477763, total shots executed = 16777216.\n", + "Amplification factor = 3.\n", + "Noise amplified result successfully read from disk.\n", + "Noise amplified exp val = 0.10535479, variance = 0.98877137, total shots executed = 16777216.\n", + "Amplification factor = 5.\n", + "Noise amplified result successfully read from disk.\n", + "Noise amplified exp val = 0.04850841, variance = 0.99752100, total shots executed = 16777216.\n", + "Amplification factor = 7.\n", + "Noise amplified result successfully read from disk.\n", + "Noise amplified exp val = 0.03171182, variance = 0.99887338, total shots executed = 16777216.\n", + "Amplification factor = 9.\n", + "Noise amplified result successfully read from disk.\n", + "Noise amplified exp val = 0.02537715, variance = 0.99923966, total shots executed = 16777216.\n", + "Amplification factor = 11.\n", + "Noise amplified result successfully read from disk.\n", + "Noise amplified exp val = 0.02293003, variance = 0.99935403, total shots executed = 16777216.\n", + "Amplification factor = 13.\n", + "Noise amplified result successfully read from disk.\n", + "Noise amplified exp val = 0.02211404, variance = 0.99939353, total shots executed = 16777216.\n", + "-----\n", + "ERROR MITIGATION DONE\n", + "Bare circuit expectation value: 0.29174495\n", + "Noise amplified expectation values: [0.29174495 0.10535479 0.04850841 0.03171182 0.02537715 0.02293003\n", + " 0.02211404]\n", + "Circuit depths: [ 84 170 256 342 428 514 600]\n", + "-----\n", + "Mitigated expectation value: 0.49105048\n", + "\n" + ] + }, + { + "data": { + "text/plain": [ + "0.4910504779547751" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "N_AMP_FACTORS = 7 # -> amplification_factors = [1,3,5,7,9,11,13]\n", + "SHOTS = 2048*8192\n", + "\n", + "qc_transpiled = transpile(qc, athens_backend, optimization_level=3)\n", + "\n", + "qem = ZeroNoiseExtrapolation(qc=qc_transpiled, exp_val_func=swaptest_exp_val_func, backend=sim_backend, \n", + " noise_model = athens_noise_model,\n", + " n_amp_factors=N_AMP_FACTORS, shots=SHOTS,\n", + " save_results=True, experiment_name=\"3qswaptest_ibmqathensnoisemodel\")\n", + "\n", + "qem.mitigate(verbose=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For comparisons we run also the zero-noise extrapolation implementation with noise amplification by random Pauli gate-sampling.\n", + "\n", + "While the repeating CNOT-method requires odd noise amplificaiton factors (1, 3, 5, ..., 2n-1), the random Pauli-method seems to work best with powers of two (1, 2, 4, ..., 2^(n-1))." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sanity checks passed\n", + "Result for job ' 3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r1 ' successfully read from disk\n", + "Result for job ' 3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r2 ' successfully read from disk\n", + "Result for job ' 3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r4 ' successfully read from disk\n", + "Result for job ' 3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r8 ' successfully read from disk\n", + "Result for job ' 3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r16 ' successfully read from disk\n", + "Result for job ' 3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r32 ' successfully read from disk\n", + "Result for job ' 3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r64 ' successfully read from disk\n", + "[0.46603722]\n" + ] + } + ], + "source": [ + "two_qubit_error_map = get_cx_error_map(athens_backend)\n", + "athens_basis_gates = athens_noise_model.basis_gates\n", + "\n", + "# we take the same transpiled circuit as used before, because there is some randomness involved in the transpilation\n", + "qc_transpiled = qem.qc\n", + "\n", + "n_amp_factors = qem.n_amp_factors\n", + "\n", + "# amplification factors as multiples of 2 seems to give better results for this method\n", + "amplification_factors_powersoftwo = [2**i for i in range(n_amp_factors)]\n", + "\n", + "R,E_dict,E_av_dict,\\\n", + "max_depth_dict,mean_depth_dict,\\\n", + "max_depth_transpiled_dict,mean_depth_transpiled_dict,\\\n", + "bn= mitigate(qc_transpiled, amplification_factors_powersoftwo,\\\n", + " swaptest_exp_val_func,\\\n", + " sim_backend, \\\n", + " \"3qswaptest_ibmqathens\", two_qubit_error_map,\\\n", + " 8192, 2048,\\\n", + " athens_backend,\\\n", + " athens_noise_model,\\\n", + " athens_basis_gates,\\\n", + " paulitwirling=False\n", + " )\n", + "\n", + "print(R[-1])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We plot the mitigated expectation values as a function of the number of amplification factors used for both methods. We observe from the below plot that the repeating CNOTs-method converges a lot closer to the ideal expectation value than the random Pauli-method.\n", + "\n", + "The random Pauli-method relies on approximating the quantum noise as a two-qubit depolarizing noise model. Optionally, doing a pauli-twirling before applying this assumption. The repeating CNOTs-method have no such assumptions about the character of the quantum noise. Thus, we might expected the latter to work better for general noise models, and exp" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CNOT repetition, mitigated exp vals: [0.29174495 0.38494003 0.43351895 0.46148582 0.4778664 0.48694545\n", + " 0.49105048]\n", + "Random pauli gates, mitigated exp vals: [0.29193199 0.40144479 0.44075068 0.45646464 0.4634236 0.46669605\n", + " 0.46603722]\n" + ] + }, + { + "data": { + "image/png": "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buD0tvXo3658NGstzCVvLshz+3BBPADP/bTOkxyypWZkMe5bZxSW/YfT8+zrj6CgIG688UZeeumlPOvi4+M588wziY2NZcCAAWzbtg2AcePG8fzzzwMwceJEoqOjiY2N5dJLnWHWDx8+zLXXXkvPnj3p0qUL33zzTZ5jp6SkMGDAALp27UpMTIxnm/j4eNq3b8+oUaNo27YtI0eO5JdffqFPnz60adOGhQsXemK48sor6dWrF23atOGtt/IOVzZ79myGDh3Knj17uOKKK1i0aBFxcXFs2rSJfv36sXjxYgB++uknunbtSufOnRkwYAAACxcupFevXnTp0oXevXuzfv160tPTeeyxx/j00089VyLvvfcet912W4Gv16hRo7jjjjvo3bs3LVu2zPcXeGZmJiNHjqRDhw5cdNFFHDlyhF9//ZXhw4d7tpkxYwbnn39+jv2OHDnCW2+9xauvvkqVKlUAaNCgASNGjPBsc++99/Lkk0/mec7nn3+ehx56yNPiJioqirFjxzJhwgSfMfoyatQoRo8ezSmnnML999/Ppk2bGDJkCN26daNv376sW7cux3bdu3enbdu2fPfddwBkZWUxZswYevToQWxsLG+++SYADz74IHPnziUuLo6XXnrJ837Gx8czadIkXnrpJeLi4pg7d26Oz+SyZcs49dRTiY2N5fzzz+fAgQMA9OvXjwceeICePXvStm1b5s6d6/c5lraCft6udf9dDCzxcTPZMtJABOq2ISQogMt7NufRczpUioreXcm++0okHck47mPfeuutfPzxxyQnJ+dYfvvtt3P11VezYsUKRo4c6bm89/bMM8/w119/sWLFCiZNmgTAk08+yZlnnsnChQuZNWsWY8aM4fDhwzn2Cw0N5euvv2bp0qXMmjWLe++9F1VnMOONGzdy7733sm7dOtatW8eUKVOYN28ezz//PE899ZTnGCtWrODXX39lwYIFjB8/np07d/o8v/r16/Pf//6Xvn37smzZMlq1auVZl5iYyA033MCXX37J8uXL+fzzzwFo3749c+fO5a+//mL8+PE89NBDhISEMH78eC655BKWLVuW5yqkoNdr165dzJs3j++++44HH3zQZ5zr16/nlltuYe3atdSsWZPXX3+d/v37s27dOk+rmHfffZdrr702x34bN26kefPm1KyZ/8CUI0aMYOnSpWzcuDHH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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "# Process results from ZNE with noise amplification by CNOT repetition:\n", + "n_amp_factors = qem.n_amp_factors\n", + "noise_amplified_exp_vals = qem.noise_amplified_exp_vals\n", + "noise_amplification_factors = qem.noise_amplification_factors\n", + "\n", + "mitigated_exp_vals = np.zeros(n_amp_factors)\n", + "mitigated_exp_vals[0] = noise_amplified_exp_vals[0]\n", + "\n", + "for n in range(1, n_amp_factors):\n", + " mitigated_exp_vals[n] = Richardson_extrapolate(noise_amplified_exp_vals[0:n+1], noise_amplification_factors[0:n+1])[0]\n", + " \n", + "print(\"CNOT repetition, mitigated exp vals:\", mitigated_exp_vals)\n", + "\n", + "# Process results from ZNE with noise amplification by random pauli gates:\n", + "noise_amplified_exp_vals_randompauliamplification = np.zeros(N_AMP_FACTORS)\n", + "mitigated_exp_vals_randompauliamplification = np.zeros(N_AMP_FACTORS)\n", + "for i,r in enumerate(amplification_factors_powersoftwo):\n", + " noise_amplified_exp_vals_randompauliamplification[i] = E_av_dict[bn + \"_r{:}\".format(r)][-1]\n", + "\n", + "mitigated_exp_vals_randompauliamplification[0] = noise_amplified_exp_vals_randompauliamplification[0]\n", + "\n", + "for i in range(1, N_AMP_FACTORS):\n", + " mitigated_exp_vals_randompauliamplification[i] = Richardson_extrapolate(noise_amplified_exp_vals_randompauliamplification[0:i+1],\n", + " np.asarray(amplification_factors_powersoftwo[0:i+1]))[0]\n", + "\n", + "\n", + "print(\"Random pauli gates, mitigated exp vals:\", mitigated_exp_vals_randompauliamplification)\n", + " \n", + "# Plotting\n", + "x = [i+1 for i in range(N_AMP_FACTORS)]\n", + "\n", + "plt.xlabel(\"Number of amplification factors included\")\n", + "plt.ylabel(\"Mitigated expectation values\")\n", + "\n", + "plt.plot(x, mitigated_exp_vals, \"--o\", label=\"Noise amplification by CNOT repetition\")\n", + "plt.plot(x, mitigated_exp_vals_randompauliamplification, \"--o\", label=\"Noise amplification by random pauli gates\")\n", + "plt.plot(x, [0.5 for i in range(len(x))], \"--\", label=\"Ideal\")\n", + "\n", + "plt.legend()\n", + "\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Qiskit version:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'qiskit-terra': '0.16.0',\n", + " 'qiskit-aer': '0.6.1',\n", + " 'qiskit-ignis': '0.4.0',\n", + " 'qiskit-ibmq-provider': '0.8.0',\n", + " 'qiskit-aqua': '0.7.5',\n", + " 'qiskit': '0.23.0'}" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "qiskit.__qiskit_version__" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "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.8.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/5qubit GHZ state.ipynb b/5qubit GHZ state.ipynb index 89745ef..45553b0 100644 --- a/5qubit GHZ state.ipynb +++ b/5qubit GHZ state.ipynb @@ -435,7 +435,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.4" + "version": "3.8.3" } }, "nbformat": 4, diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r1.max_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r1.max_circuit_depth new file mode 100644 index 0000000..3ca9062 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r1.max_circuit_depth @@ -0,0 +1 @@ +84 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r1.max_transpiled_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r1.max_transpiled_circuit_depth new file mode 100644 index 0000000..c954f9c --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r1.max_transpiled_circuit_depth @@ -0,0 +1 @@ +394 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r1.mean_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r1.mean_circuit_depth new file mode 100644 index 0000000..219dd62 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r1.mean_circuit_depth @@ -0,0 +1 @@ +84.0 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r1.mean_transpiled_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r1.mean_transpiled_circuit_depth new file mode 100644 index 0000000..7ba3f31 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r1.mean_transpiled_circuit_depth @@ -0,0 +1 @@ +394.0 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r1.result b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r1.result new file mode 100644 index 0000000..569c24b Binary files /dev/null and b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r1.result differ diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r16.max_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r16.max_circuit_depth new file mode 100644 index 0000000..105d7d9 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r16.max_circuit_depth @@ -0,0 +1 @@ +100 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r16.max_transpiled_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r16.max_transpiled_circuit_depth new file mode 100644 index 0000000..4754f24 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r16.max_transpiled_circuit_depth @@ -0,0 +1 @@ +461 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r16.mean_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r16.mean_circuit_depth new file mode 100644 index 0000000..1b1beb1 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r16.mean_circuit_depth @@ -0,0 +1 @@ +90.49951171875 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r16.mean_transpiled_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r16.mean_transpiled_circuit_depth new file mode 100644 index 0000000..9eeb823 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r16.mean_transpiled_circuit_depth @@ -0,0 +1 @@ +418.64501953125 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r16.result b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r16.result new file mode 100644 index 0000000..60c4cf2 Binary files /dev/null and b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r16.result differ diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r2.max_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r2.max_circuit_depth new file mode 100644 index 0000000..9f72858 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r2.max_circuit_depth @@ -0,0 +1 @@ +88 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r2.max_transpiled_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r2.max_transpiled_circuit_depth new file mode 100644 index 0000000..3d41066 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r2.max_transpiled_circuit_depth @@ -0,0 +1 @@ +410 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r2.mean_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r2.mean_circuit_depth new file mode 100644 index 0000000..97caa14 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r2.mean_circuit_depth @@ -0,0 +1 @@ +84.43408203125 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r2.mean_transpiled_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r2.mean_transpiled_circuit_depth new file mode 100644 index 0000000..577b6a8 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r2.mean_transpiled_circuit_depth @@ -0,0 +1 @@ +395.65673828125 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r2.result b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r2.result new file mode 100644 index 0000000..0d4e8d0 Binary files /dev/null and b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r2.result differ diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r32.max_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r32.max_circuit_depth new file mode 100644 index 0000000..e3b5acb --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r32.max_circuit_depth @@ -0,0 +1 @@ +107 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r32.max_transpiled_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r32.max_transpiled_circuit_depth new file mode 100644 index 0000000..34c3a20 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r32.max_transpiled_circuit_depth @@ -0,0 +1 @@ +487 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r32.mean_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r32.mean_circuit_depth new file mode 100644 index 0000000..c3d216e --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r32.mean_circuit_depth @@ -0,0 +1 @@ +97.3681640625 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r32.mean_transpiled_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r32.mean_transpiled_circuit_depth new file mode 100644 index 0000000..616391b --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r32.mean_transpiled_circuit_depth @@ -0,0 +1 @@ +444.54833984375 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r32.result b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r32.result new file mode 100644 index 0000000..da0e483 Binary files /dev/null and b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r32.result differ diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r4.max_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r4.max_circuit_depth new file mode 100644 index 0000000..a46c9d2 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r4.max_circuit_depth @@ -0,0 +1 @@ +91 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r4.max_transpiled_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r4.max_transpiled_circuit_depth new file mode 100644 index 0000000..00c22e5 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r4.max_transpiled_circuit_depth @@ -0,0 +1 @@ +429 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r4.mean_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r4.mean_circuit_depth new file mode 100644 index 0000000..1b61715 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r4.mean_circuit_depth @@ -0,0 +1 @@ +85.3076171875 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r4.mean_transpiled_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r4.mean_transpiled_circuit_depth new file mode 100644 index 0000000..c96b197 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r4.mean_transpiled_circuit_depth @@ -0,0 +1 @@ +398.951171875 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r4.result b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r4.result new file mode 100644 index 0000000..05f2229 Binary files /dev/null and b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r4.result differ diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r64.max_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r64.max_circuit_depth new file mode 100644 index 0000000..3fdc173 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r64.max_circuit_depth @@ -0,0 +1 @@ +122 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r64.max_transpiled_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r64.max_transpiled_circuit_depth new file mode 100644 index 0000000..ea5ca36 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r64.max_transpiled_circuit_depth @@ -0,0 +1 @@ +547 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r64.mean_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r64.mean_circuit_depth new file mode 100644 index 0000000..b223445 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r64.mean_circuit_depth @@ -0,0 +1 @@ +111.03125 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r64.mean_transpiled_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r64.mean_transpiled_circuit_depth new file mode 100644 index 0000000..a6326f6 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r64.mean_transpiled_circuit_depth @@ -0,0 +1 @@ +495.94775390625 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r64.result b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r64.result new file mode 100644 index 0000000..ed1a41a Binary files /dev/null and b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r64.result differ diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r8.max_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r8.max_circuit_depth new file mode 100644 index 0000000..90be1cd --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r8.max_circuit_depth @@ -0,0 +1 @@ +95 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r8.max_transpiled_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r8.max_transpiled_circuit_depth new file mode 100644 index 0000000..2ae9f6c --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r8.max_transpiled_circuit_depth @@ -0,0 +1 @@ +441 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r8.mean_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r8.mean_circuit_depth new file mode 100644 index 0000000..0fc1736 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r8.mean_circuit_depth @@ -0,0 +1 @@ +87.0048828125 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r8.mean_transpiled_circuit_depth b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r8.mean_transpiled_circuit_depth new file mode 100644 index 0000000..7567a63 --- /dev/null +++ b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r8.mean_transpiled_circuit_depth @@ -0,0 +1 @@ +405.3291015625 \ No newline at end of file diff --git a/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r8.result b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r8.result new file mode 100644 index 0000000..d4a7a62 Binary files /dev/null and b/results/3qswaptest_ibmqathens_backendqasm_simulator_noisemodelibmq_athens_shots8192_experiments2048_paulitwirlingFalse_r8.result differ diff --git a/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse.circuit b/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse.circuit new file mode 100644 index 0000000..e64c0d7 Binary files /dev/null and b/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse.circuit differ diff --git a/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r1.result b/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r1.result new file mode 100644 index 0000000..86625ea Binary files /dev/null and b/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r1.result differ diff --git a/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r11.result b/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r11.result new file mode 100644 index 0000000..c9099e2 Binary files /dev/null and b/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r11.result differ diff --git a/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r13.result b/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r13.result new file mode 100644 index 0000000..790e62f Binary files /dev/null and b/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r13.result differ diff --git a/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r3.result b/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r3.result new file mode 100644 index 0000000..1265096 Binary files /dev/null and b/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r3.result differ diff --git a/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r5.result b/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r5.result new file mode 100644 index 0000000..02380a1 Binary files /dev/null and b/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r5.result differ diff --git a/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r7.result b/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r7.result new file mode 100644 index 0000000..9edda48 Binary files /dev/null and b/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r7.result differ diff --git a/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r9.result b/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r9.result new file mode 100644 index 0000000..4bcda74 Binary files /dev/null and b/results/3qswaptest_ibmqathensnoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots16777216_paulitwirlFalse_r9.result differ diff --git a/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse.circuit b/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse.circuit new file mode 100644 index 0000000..ce24925 Binary files /dev/null and b/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse.circuit differ diff --git a/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r1.result b/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r1.result new file mode 100644 index 0000000..f9808a3 Binary files /dev/null and b/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r1.result differ diff --git a/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r11.result b/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r11.result new file mode 100644 index 0000000..0344603 Binary files /dev/null and b/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r11.result differ diff --git a/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r13.result b/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r13.result new file mode 100644 index 0000000..ad65b3a Binary files /dev/null and b/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r13.result differ diff --git a/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r3.result b/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r3.result new file mode 100644 index 0000000..52f0263 Binary files /dev/null and b/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r3.result differ diff --git a/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r5.result b/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r5.result new file mode 100644 index 0000000..f9ffd67 Binary files /dev/null and b/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r5.result differ diff --git a/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r7.result b/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r7.result new file mode 100644 index 0000000..1913cb1 Binary files /dev/null and b/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r7.result differ diff --git a/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r9.result b/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r9.result new file mode 100644 index 0000000..2e5e26c Binary files /dev/null and b/results/3qswaptest_paulinoisemodel__ZNE_CNOT_REP__backendqasm_simulator_errorctrldFalse_shots8388608_paulitwirlFalse_r9.result differ diff --git a/results/3qswaptest_paulinoisemodel_noisemodelamplification_ampfactors7_shots8388608 b/results/3qswaptest_paulinoisemodel_noisemodelamplification_ampfactors7_shots8388608 new file mode 100644 index 0000000..6ad6de2 Binary files /dev/null and b/results/3qswaptest_paulinoisemodel_noisemodelamplification_ampfactors7_shots8388608 differ diff --git a/zero_noise_extrapolation.py b/zero_noise_extrapolation.py index 9f9df45..c4b3689 100644 --- a/zero_noise_extrapolation.py +++ b/zero_noise_extrapolation.py @@ -1,6 +1,12 @@ import re #because we need regular expressions import numpy as np #because we need the random number generator +from numpy.linalg import solve from qiskit import * + +from qiskit.transpiler import PassManager, PassManagerConfig, CouplingMap +from qiskit.transpiler.passes import Unroller, Optimize1qGates +from qiskit.transpiler.preset_passmanagers.level3 import level_3_pass_manager + from qiskit.tools.monitor import job_monitor import sys @@ -201,7 +207,7 @@ def Richardson_extrapolate(E, c): for k in range(1,n): A[k,:] = c**k x=np.linalg.solve(A,b) - return np.dot(np.transpose(E),x) + return np.dot(np.transpose(E),x), x def mitigate(circuit, amplification_factors,\ expectationvalue_fun,\ @@ -354,13 +360,11 @@ def mitigate(circuit, amplification_factors,\ else: raise ValueError("not yet implemented, coming soon") - R=Richardson_extrapolate(E_av.reshape(len(amplification_factors),num_experiments),\ + R,_=Richardson_extrapolate(E_av.reshape(len(amplification_factors),num_experiments),\ np.array(amplification_factors)) return R, E_dict, E_av_dict,\ max_depth_dict,mean_depth_dict,\ max_depth_transpiled_dict,mean_depth_transpiled_dict,\ - experimentname - - + experimentname \ No newline at end of file diff --git a/zero_noise_extrapolation_cnot.py b/zero_noise_extrapolation_cnot.py new file mode 100644 index 0000000..b9958f6 --- /dev/null +++ b/zero_noise_extrapolation_cnot.py @@ -0,0 +1,1072 @@ +from qiskit import QuantumCircuit, execute, Aer, transpile + +from qiskit.result.result import Result +from qiskit.providers.aer.noise import NoiseModel + +from qiskit.transpiler import PassManager, PassManagerConfig, CouplingMap +from qiskit.transpiler.passes import Unroller, Optimize1qGates +from qiskit.transpiler.preset_passmanagers.level3 import level_3_pass_manager + +from numpy import asarray, ndarray, shape, zeros, empty, average, transpose, dot, sqrt +from numpy.linalg import solve + +import random, os, pickle, sys, errno +from dataclasses import dataclass + +abs_path = os.path.dirname(__file__) +sys.path.append(abs_path) +sys.path.append(os.path.dirname(abs_path)) + +from typing import Callable, Union + +""" +-- ZERO NOISE EXTRAPOLATION for CNOT-gates -- + +This is an implementation of the zero-noise extrapolation technique for quantum error mitigation. The goal is to +mitigate noise present in a quantum device when evaluating some expectation value that is computed by a quantum circuit +with subsequent measurements. The main idea of zero-noise extrapolation is to amplify the noise by a set of known +noise amplification factors, such as to obtain a set of noise amplified expectation values. Richardsson extrapolation +is then used to extrapolate the expectation value to the zero-noise limit. + +The noise that is here amplified and mitigated is specifically general noise in CNOT-gates. Note that in modern quantum +devices the noise in the multi-qubit CNOT-gates tend to be an order of magnitude larger than in single-qubit gates. + +To amplify the noise we use that the CNOT-gate is its own inverse, i.e., CNOT*CNOT = Id, where Id is the identity gate. +Thus an odd number of CNOT-gates in a series will in the noise-less case have the same action as a single CNOT-gate. +The noise is amplified by replacing each CNOT-gate in the original bare circuit with a series of (2*i + 1) CNOT's, +using noise amplification factors c=1, 3, 5, ..., 2*n - 1, for n being the total number of amplification factors. +For c=3, each CNOT is thus replaced by the sequence CNOT*CNOT*CNOT, and while this has the same action in the noise-less +case, in the noisy case the noise operation associated with the noisy CNOT-gate will be applied thrice instead of once. + +""" + + +# Dataclasses, containing partial (noise amplified) and final results + +@dataclass(frozen=True) +class NoiseAmplifiedResult: + amp_factor: int + shots: int + qc: QuantumCircuit + depth: int + exp_val: float + variance: float + + +@dataclass(frozen=True) +class ZeroNoiseExtrapolationResult: + qc: QuantumCircuit + noise_amplified_results: ndarray + noise_amplification_factors: ndarray + gamma_coefficients: ndarray + exp_val: float + + @property + def bare_exp_val(self) -> float: + return self.noise_amplified_results[0].exp_val + + @property + def noise_amplified_exp_vals(self) -> ndarray: + return asarray([result.exp_val for result in self.noise_amplified_results]) + + @property + def noise_amplified_variances(self) -> ndarray: + return asarray([result.variance for result in self.noise_amplified_results]) + + @property + def shots(self) -> ndarray: + return asarray([result.shots for result in self.noise_amplified_results]) + + @property + def total_shots(self) -> float: + return sum(result.shots for result in self.noise_amplified_results) + + @property + def depths(self) -> ndarray: + return asarray([result.qc.depth() for result in self.noise_amplified_results]) + + +# Zero-noise extrapolation class: +class ZeroNoiseExtrapolation: + + def __init__(self, qc: QuantumCircuit, exp_val_func: Callable, backend=None, exp_val_filter=None, + noise_model: Union[NoiseModel, dict] = None, n_amp_factors: int = 3, shots: int = 8192, + pauli_twirl: bool = False, pass_manager: PassManager = None, + save_results: bool = False, experiment_name: str = "", option: dict = None, + error_controlled_sampling: bool = False, max_shots: int = 2048*8192, error_tol: float = 0.01): + """ + CONSTRUCTOR + + Parameters + ---------- + qc : qiskit.QuantumCircuit + A complete quantum circuit, with measurements, that we want to perform quantum error mitigation on. + When run on a quantum backend, the circuit should output a set of measurements results from which the + desired expectation value can be estimated. + + exp_val_func : Callable + A function that computes the desired expectation value, and its variance, based on the measurement results + outputted by an execution of a quantum circuit. + The function should take two arguments: a list of qiskit.result.result.ExperimentResult objects as its first + argument, and a possible filter as its second. + The function should return: A numpy.ndarray of expectation values corresponding to each ExperimentResult, + and a numpy.ndarray of variances in similar fashion. + + backend : A valid qiskit backend, IBMQ device or simulator + A qiskit backend, either an IBMQ quantum backend or a simulator backend, for circuit executions. + If none is passed, the qasm_simulator will be used. + + exp_val_filter : any, (optional) + Optional filter that is passed to the exp_val_func expectation value function. + + noise_model : qiskit.providers.aer.noise.NoiseModel or dict, (optional) + Custom noise model for circuit executions with the qasm simulator backend. + + n_amp_factors : int, (default set to 3) + The number of noise amplification factors to be used. For n number of amplification factors, the specific + noise amplification factors will be [1, 3, 5, ..., 2*n - 1]. Larger amounts of noise amplification factors + tend to give better results, but slower convergence thus requiring large amounts of shots. + Higher noise amplification also increases circuit depth, scaling linearly with the amplification factor c_i, + and at some point the circuit depth and the consecutive decoherence will eliminate any further advantage. + + shots: int, (default set to 8192) + The number of "shots" of each experiment to be executed, where one experiment is a single execution of a + quantum circuit. To obtain an error mitigated expectation value, a total of shots*n_amp_factors experiments + is performed. + + pauli_twirl : bool, (optional) + Perform Pauli twirling of each noise amplified circuit, True / False. + + pass_manager: qiskit.transpiler.PassManager, (optional) + Optional custom pass_manager for circuit transpiling. If none is passed, the circuit will be transpiled + using the qiskit optimization_level=3 preset, which is the heaviest optimization preset. + + save_results: bool, (optional) + If True, will attempt to read transpiled circuit and experiment results for each noise amplified from disk, + and if this fails, the transpiled circuit and/or experiment measurement results will be saved to disk. + + experiment_name: string, (optional) + The experiment name used when reading and writing transpiled circuits and measurement results to disk. + The experiment name will form the base for the full filename for each written/read file. + This argument is required if save_result = True. + + option: dict, (optional) + Options for the writing/reading of transpiled circuits and measurement results. + option["directory"] gives the directory in which files will be written to/attempted to be read from. + If no option is passed, the default directory used will be option["directory"] = "results". + + """ + + # Set backend for circuit execution. If none is passed, use the qasm_simulator backend. + if backend is None: + self.backend = Aer.get_backend("qasm_simulator") + self.is_simulator = True + else: + self.backend = backend + self.is_simulator = backend.configuration().simulator + + self.exp_val_func = exp_val_func + self.exp_val_filter = exp_val_filter + + self.noise_model = noise_model + + self.n_amp_factors = n_amp_factors + self.gamma_coefficients = self.compute_extrapolation_coefficients(n_amp_factors=self.n_amp_factors) + self.noise_amplification_factors = asarray([(2*i + 1) for i in range(0, self.n_amp_factors)]) + + self.pauli_twirl = pauli_twirl + + # Total number of shots + self.shots = shots + + # variables involved in error controlled sampling: + self.error_controlled_sampling = error_controlled_sampling + self.max_shots = max_shots + self.error_tol = error_tol + + # Variables involved in writing and reading results to/from disk + self.save_results, self.option = save_results, option + if self.option is None: + self.option = {} + self.experiment_name = "" + if self.save_results: + self.set_experiment_name(experiment_name) + self.create_directory() + + # Initial transpiling of the quantum circuit. If no custom pass manager is passed, the optimization_level=3 + # qiskit preset (the heaviest optimization preset) will be used. If save_results=True, will attempt to read + # the transpiled circuit from disk. + circuit_read_from_file = False + if self.save_results: + qc_from_file = self.read_from_file(self.experiment_name + ".circuit") + if not (qc_from_file is None): + circuit_read_from_file = True + self.qc = qc_from_file + if not circuit_read_from_file: + self.qc = self.transpile_circuit(qc, custom_pass_manager=pass_manager) + + """ + --- Initialization of other variables for later use: + """ + + self.noise_amplified_results = empty((self.n_amp_factors,), dtype=NoiseAmplifiedResult) + + self.result = None + + @staticmethod + def partition_shots(tot_shots: int) -> (int, int): + """ + IBMQ devices limits circuit executions to a max of 8192 shots per experiment. To perform more than 8192 shots, + the experiment has to be partitioned into a set of circuit executions, each with less than 8192 shots. + Therefore, if shots > 8192, we partition the execution into several repeats of less than 8192 shots each. + + Parameters + ---------- + tot_shots : int + The total number of circuit execution shots. + + Returns + ------- + shots, repeats: (int, int) + Shots per repeat, number of repeats + """ + if tot_shots <= 8192: + return tot_shots, 1 + else: + if tot_shots % 8192 == 0: + repeats = (tot_shots // 8192) + else: + repeats = (tot_shots // 8192) + 1 + return int(tot_shots / repeats), repeats + + # We use the @property decorator for certain useful data that we might want to retrieve that are stored within the + # noise amplified results + + @property + def bare_exp_val(self) -> float: + return self.noise_amplified_results[0].exp_val + + @property + def noise_amplified_exp_vals(self) -> ndarray: + return asarray([result.exp_val for result in self.noise_amplified_results]) + + @property + def noise_amplified_variances(self) -> ndarray: + return asarray([result.variance for result in self.noise_amplified_results]) + + @property + def depths(self) -> ndarray: + return asarray([result.qc.depth() for result in self.noise_amplified_results]) + + @property + def mitigated_exp_val(self) -> float: + if self.result is None: + return None + return self.result.exp_val + + # Functions for computing the Richardson extrapolation + + def extrapolate(self, noise_amplified_exp_vals: ndarray): + """ + The Richardson extrapolation reads + + E^* = \sum_i gamma_i * E(\lambda_i), + + where the gamma_i-coefficients are computed as a function of the amplification factors by solving a system + of linear equations, this is done in self.compute_extrapolation_coefficients(), and E(\lambda_i) is the + i-th noise amplified expectation value, corresponding to the amplification factor \lambda_i. + + Ref: https://doi.org/10.1098/rsta.1911.0009 + + Parameters + ---------- + noise_amplified_exp_vals: numpy.ndarray + The noise amplified expectation value, in order corresponding to amplification factors 1, 3, 5, ... , 2n-1. + + Returns + ------- + mitigated_exp_val: float + The mitigated expectation value, which is the exp val extrapolated to the zero-noise case. + """ + if self.n_amp_factors == 1: + return noise_amplified_exp_vals[0] + if not shape(noise_amplified_exp_vals)[0] == shape(self.gamma_coefficients)[0]: + raise Exception("Shape mismatch between noise_amplified_exp_vals and gamma_coefficients." + + " length={:}".format(shape(noise_amplified_exp_vals)[0]) + + " does not match length={:}".format(shape(self.gamma_coefficients)[0])) + return dot(transpose(noise_amplified_exp_vals), self.gamma_coefficients)[0] + + def compute_extrapolation_coefficients(self, n_amp_factors: int = None) -> ndarray: + """ + Compute the gamma_i-coefficients used in the Richardson extrapolation. We assume the specific noise + amplification factors to be 1, 3, 5, ..., 2n-1, where n=n_amp_factors is the number of noise amplification + factors. + + Parameters + ---------- + n_amp_factors: int + Number of amplification factors. + + Returns + ---------- + gamma_coefficients: numpy.ndarray + The set of coefficients to be used in the Richardson extrapolation. + """ + if n_amp_factors is None: + n_amp_factors = self.n_amp_factors + if n_amp_factors == 1: + return asarray([1]) + + amplification_factors = asarray([2*i + 1 for i in range(n_amp_factors)]) + + A, b = zeros((n_amp_factors, n_amp_factors)), zeros((n_amp_factors, 1)) + + A[0, :], b[0] = 1, 1 + + for k in range(1, n_amp_factors): + A[k, :] = amplification_factors**k + + gamma_coefficients = solve(A, b) + + return gamma_coefficients + + # Functions involved in saving and loading results from disk + + def set_experiment_name(self, experiment_name): + """ + Construct the experiment name that will form the base for the filenames that will be read from / written to + when save_results=True. The full experiment name will contain information about the backend, number of shots, + and pauli twirling, to ensure that different experiments using different parameters don't read from the same + data. + + Parameters + ---------- + experiment_name : str + The base for the experiment name that will be used for filenames + + """ + if self.save_results and experiment_name == "": + raise Exception("experiment_name cannot be empty when writing/reading results from disk is activated.") + self.experiment_name = experiment_name + self.experiment_name += "__ZNE_CNOT_REP_" + self.experiment_name += "_backend" + self.backend.name() + self.experiment_name += "_errorctrld" + str(self.error_controlled_sampling) + if self.error_controlled_sampling: + self.experiment_name += "_tol" + str(self.error_tol) + self.experiment_name += "_maxshots" + str(self.max_shots) + else: + self.experiment_name += "_shots" + str(self.shots) + self.experiment_name += "_paulitwirl" + str(self.pauli_twirl) + + def create_directory(self): + """ + Attempt to create the directory in which to read from/write to files. The case whereby the directory already + exists is handled by expection handling. + + The directory is given by self.option["directory"], with the default being "results". + + """ + if not self.save_results: + return + directory = self.option.get("directory", "results") + try: + os.makedirs(directory) + except OSError as e: + if e.errno != errno.EEXIST: + raise e + + def read_from_file(self, filename: str): + """ + Attempts to read data for a given filename, looking in the directory given by self.option["directory"]. + + Parameters + ---------- + filename : str + The full filename for the file in question + + Returns + ------- + data : any + The data read from said file. None if the file wasn't found + """ + directory = self.option.get("directory", "results") + if os.path.isfile(directory + "/" + filename): + file = open(directory + "/" + filename, "rb") + data = pickle.load(file) + file.close() + return data + else: + return None + + def write_to_file(self, filename: str, data): + """ + Writes data to file with given filename, located in the directory given by self.option["directory"]. + + Parameters + ---------- + filename : str + The full filename of the file to be written to. + data : any + The data to be stored. + + """ + directory = self.option.get("directory", "results") + file = open(directory + "/" + filename, "wb") + pickle.dump(data, file) + file.close() + + # Functions for processing and executing the quantum circuits + + def noise_amplify_and_pauli_twirl_cnots(self, qc: QuantumCircuit, amp_factor: int, + pauli_twirl: bool = False) -> QuantumCircuit: + """ + Amplify CNOT-noise by extending each CNOT-gate as CNOT^amp_factor and possibly Pauli-twirl all CNOT-gates + + Using CNOT*CNOT = I, the identity, and an amp_factor = (2*n + 1) for an integer n, then the + extended CNOT will have the same action as a single CNOT, but with the noise amplified by + a factor amp_factor. This thus method allows for circuit-level noise amplification. + + For efficiency, this function does both noise amplification and pauli twirling (optionally) at the same time. + Separate functions for noise amplification and for pauli twirling are included at the end of this file, for + completeness. + + :param qc: Quantum circuit for which to Pauli twirl all CNOT gates and amplify CNOT-noise + :param amp_factor: The noise amplification factor, must be (2n + 1) for n = 0,1,2,3,... + :param pauli_twirl: Add pauli twirling True / False + :return: Noise-amplified and possibly Pauli-twirled Quantum Circuit + """ + + if (amp_factor - 1) % 2 != 0: + raise Exception("Invalid amplification factors", amp_factor) + + # The circuit may be expressed in terms of various types of gates. + # The 'Unroller' transpiler pass 'unrolls' (decomposes) the circuit gates to be expressed in terms of the + # physical gate set [u1,u2,u3,cx] + + # The cz, cy (controlled-Z and -Y) gates can be constructed from a single cx-gate and single-qubit gates. + # For backends with native gate sets consisting of some set of single-qubit gates and either the cx, cz or cy, + # unrolling the circuit to the ["u3", "cx"] basis, amplifying the cx-gates, then unrolling back to the native + # gate set and doing a single-qubit optimization transpiler pass, is thus still general. + + unroller_ugatesandcx = Unroller(["u1", "u2", "u3", "cx"]) + pm = PassManager(unroller_ugatesandcx) + + unrolled_qc = pm.run(qc) + + circuit_qasm = unrolled_qc.qasm() + new_circuit_qasm_str = "" + + qreg_name = find_qreg_name(circuit_qasm) + + for i, line in enumerate(circuit_qasm.splitlines()): + if line[0:2] == "cx": + for j in range(amp_factor): + if pauli_twirl: + new_circuit_qasm_str += pauli_twirl_cnot_gate(qreg_name, line) + else: + new_circuit_qasm_str += (line + "\n") + else: + new_circuit_qasm_str += line + "\n" + + new_qc = QuantumCircuit.from_qasm_str(new_circuit_qasm_str) + + # The "Optimize1qGates" transpiler pass optimizes adjacent single-qubit gates, for a native gate set with the + # u3 gates it collapses any chain of adjacent single-qubit gates into a single, equivalent u3-gate. + # We want to collapse unnecessary single-qubit gates to minimize circuit depth, but not CNOT-gates + # as these give us the noise amplification. + unroller_backendspecific = Unroller(self.backend.configuration().basis_gates) + optimize1qates = Optimize1qGates() + + pm = PassManager([unroller_backendspecific, optimize1qates]) + + return pm.run(new_qc) + + def transpile_circuit(self, qc: QuantumCircuit, custom_pass_manager: PassManager = None) -> QuantumCircuit: + """ + Transpile and optimize the input circuit, optionally by using a custom pass manager. + If no custom pass manager is given, the optimization_level = 3 preset for the qiskit transpiler, + the heaviest optimization preset, will be used. + + As we want to add additional CNOTs for noise amplification and possibly additional single qubit gates + for Pauli twirling, we need to transpile the circuit before both the noise amplification is applied and + before circuit execution. This is to avoid the additional CNOT-gates beinng removed by the transpiler. + + The Optimize1qGates transpiler pass will be used later to optimize single qubit gates added during + the Pauli-twirling, as well as the Unroller pass which merely decomposes the given circuit gates into + the given set of basis gates. + + Parameters + ---------- + qc : qiskit.QuantumCircuit + The original bare quantum circuit. + custom_pass_manager : qiskit.transpiler.PassManager, (optional) + A custom pass manager to be used in transpiling. + + Returns + ------- + transpiled_circuit : qiskit.QuantumCircuit + The transpiled quantum circuit. + """ + + if custom_pass_manager is None: + pass_manager_config = PassManagerConfig(basis_gates=["id", "u1", "u2", "u3", "cx"], + backend_properties=self.backend.properties()) + if not self.is_simulator: + pass_manager_config.coupling_map = CouplingMap(self.backend.configuration().coupling_map) + + pass_manager = level_3_pass_manager(pass_manager_config) + else: + pass_manager = custom_pass_manager + + self.passes = pass_manager.passes() # Saves the list of passes used for transpiling + + transpiled_circuit = pass_manager.run(qc) + + # As there is some randomness involved in the qiskit transpiling we might want to save + # the specific transpiled circuit that is used in order to access it later. + if self.save_results: + filename = self.experiment_name + ".circuit" + self.write_to_file(filename, transpiled_circuit) + + return transpiled_circuit + + def execute_circuit(self, qc: QuantumCircuit, shots=None) -> Result: + """ + Execute a single experiment consisting of the execution of a quantum circuit over a specified number of shots. + One experiment may need to be partitioned into a set of several identical circuit executions. This is due to the + IBMQ quantum devices limiting circuit executions to a maximum of 8192 shots per. + + Parameters + ---------- + qc : qiskit.QuantumCircuit + The specific quantum circuit to be executed. + shots : int, (optional) + The number of shots of the circuit execution. If none is passed, self.shots is used. + Returns + ------- + circuit_measurement_results : qiskit.result.result.Result + A Result object containing the data and measurement results for the circuit executions. + """ + + if shots is None: + shots, repeats = self.partition_shots(self.shots) + else: + shots, repeats = self.partition_shots(shots) + + # The max number of shots on a single execution on the IBMQ devices is 8192. + # If shots > 8192, we have to partition the execution into several sub-executions. + # Note that several circuits can be entered into the IBMQ queue at once by passing them in a list. + execution_circuits = [qc.copy() for i in range(repeats)] + + # non-simulator backends throws "unexpected argument" exception when passing noise_model argument to them + if self.is_simulator: + job = execute(execution_circuits, backend=self.backend, noise_model=self.noise_model, + pass_manager=PassManager(), shots=shots) + else: + job = execute(execution_circuits, backend=self.backend, + pass_manager=PassManager(), shots=shots) + + circuit_measurement_results = job.result() + + return circuit_measurement_results + + # Functions involved in computing the noise amplified and mitigated expectation values and related measures. + + def compute_exp_val(self, result: Result) -> (float, float, ndarray): + """ + Compute the expectation value and variance for a set of circuit executions. We assume that all separate circuit + execution was run with the same number of shots. + + Parameters + ---------- + result : qiskit.result.result.Result + A qiskit Result object containing all measurement results from a set of quantum circuit executions. + + Returns + ------- + averaged_experiment_exp_vals, averaged_experiment_variances experiment_exp_vals : Tuple[float, numpy.ndarray] + The final estimated experiment expectation value and variance, averaged over all circuit sub-executions, + and a numpy array containing the expectation values for each circuit sub-execution. + """ + + experiment_results = result.results + + experiment_exp_vals, experiment_variances = self.exp_val_func(experiment_results, self.exp_val_filter) + + return average(experiment_exp_vals), average(experiment_variances), asarray(experiment_exp_vals) + + def compute_error_controlled_exp_val(self, noise_amplified_qc: QuantumCircuit, gamma_coeff: float, shots: int = None, + conf_index: int = 2, verbose: bool = False) -> (float, float, float): + """ + Handles optional error controlled sampling of the quantum circuit. Returns the resulting estimated expectation + value and variance, and the total number of shots used. + + Parameters + ---------- + noise_amplified_qc: qiskit.QuantumCircuit + gamma_coeff: float + shots: int + conf_index: int + verbose: bool + + Returns + ------- + exp_val, variance, shots: float, float, int + + """ + + if shots is None: + shots = self.shots + + result = self.execute_circuit(qc=noise_amplified_qc, shots=shots) + + exp_val, variance, _ = self.compute_exp_val(result) + + if not self.error_controlled_sampling: + return exp_val, variance, shots + + if verbose: + print("Error controlled sampling, " + + "min_shots={:}, max_shots={:}, error_tol={:}, conf_index={:}".format(shots, self.max_shots, + self.error_tol, conf_index)) + + total_shots = int(self.n_amp_factors * (gamma_coeff**2) * (conf_index**2) * (variance / self.error_tol**2)) + + if verbose: + print("Variance={:.4f}, gamma_coeff={:} need a total of {:} shots for convergence.".format(variance, + gamma_coeff, + total_shots)) + + if total_shots <= shots: + return exp_val, variance, shots + elif total_shots > self.max_shots: + total_shots = self.max_shots + + new_shots = int(total_shots - shots) + + if verbose: + print("Executing {:} additional shots.".format(new_shots)) + + result = self.execute_circuit(qc=noise_amplified_qc, shots=new_shots) + + new_exp_val, new_variance, _ = self.compute_exp_val(result) + + error_controlled_exp_val = (shots/total_shots) * exp_val + (new_shots/total_shots) * new_exp_val + error_controlled_variance = (shots/total_shots) * variance + (new_shots/total_shots) * new_variance + + return error_controlled_exp_val, error_controlled_variance, total_shots + + def compute_noise_amplified_exp_val(self, amp_factor, gamma_coeff, verbose: bool = False): + """ + Creates the noise amplified circuit for a given amplification factors and computes the corresponding estimated + expectation value and variance. + + Parameters + ---------- + amp_factor: int + gamma_coeff: float + verbose: bool + + Returns + ------- + noise_amplified_result: NoiseAmplifiedResult + + """ + noise_amplified_qc = self.noise_amplify_and_pauli_twirl_cnots(qc=self.qc, amp_factor=amp_factor, + pauli_twirl=self.pauli_twirl) + + if verbose: + print("Circuit created. Depth = {:}. Executing.".format(noise_amplified_qc.depth())) + + exp_val, variance, total_shots = self.compute_error_controlled_exp_val(noise_amplified_qc, + gamma_coeff=gamma_coeff, + shots=self.shots, verbose=verbose) + + noise_amplified_result = NoiseAmplifiedResult(amp_factor=amp_factor, shots=total_shots, + qc=noise_amplified_qc, depth=noise_amplified_qc.depth(), + exp_val=exp_val, variance=variance) + return noise_amplified_result + + def estimate_error(self) -> float: + """ + Estimate the error in the mitigated expectation value based on the variance found in the noise amplified circuit + executions. + + Returns + ------- + error: float + Estimated error in the mitigated expectation value. + """ + error = 0 + for i, amp_res in enumerate(self.noise_amplified_results): + variance, shots = amp_res.variance, amp_res.shots + gamma_coeff = self.gamma_coefficients[i] + + error += gamma_coeff**2 * (variance / shots) + + error = sqrt(error) + + return error + + def mitigate(self, verbose: bool = False) -> float: + """ + Perform the full quantum error mitigation procedure. + + Parameters + ---------- + verbose : bool + Do prints throughout the computation. + + Returns + ------- + result : float + The mitigated expectation value. + """ + + for i, amp_factor in enumerate(self.noise_amplification_factors): + + if verbose: + print("Amplification factor = {:}.".format(amp_factor)) + + noise_amplified_result, result_read_from_file = None, False + + # If self.save_results=True, attempt to read noise amplified result from disk + if self.save_results: + temp = self.read_from_file(filename=self.experiment_name + "_r{:}.result".format(amp_factor)) + if (temp is not None) and (type(temp) is NoiseAmplifiedResult): + noise_amplified_result = temp + result_read_from_file = True + if verbose: + print("Noise amplified result successfully read from disk.") + else: + if verbose: + print("Tried to read results from disk, but results were not found.") + + gamma_coeff = self.gamma_coefficients[i] + + if not result_read_from_file: + noise_amplified_result = self.compute_noise_amplified_exp_val(amp_factor=amp_factor, + gamma_coeff=gamma_coeff, + verbose=verbose) + + if self.save_results: + self.write_to_file(filename=self.experiment_name + "_r{:}.result".format(amp_factor), + data=noise_amplified_result) + if verbose: + "Noise amplified result successfully written to disk." + + self.noise_amplified_results[i] = noise_amplified_result + + if verbose: + print("Noise amplified exp val = {:.8f}, ".format(noise_amplified_result.exp_val) + + "variance = {:.8f}, ".format(noise_amplified_result.variance) + + "total shots executed = {:}.".format(noise_amplified_result.shots)) + + mitigated_exp_val = self.extrapolate(self.noise_amplified_exp_vals) + + self.result = ZeroNoiseExtrapolationResult(qc=self.qc, + noise_amplified_results=self.noise_amplified_results, + noise_amplification_factors=self.noise_amplification_factors, + gamma_coefficients=self.gamma_coefficients, + exp_val=mitigated_exp_val + ) + + if verbose: + print("-----\nERROR MITIGATION DONE\n" + + "Bare circuit expectation value: {:.8f}\n".format(self.result.bare_exp_val) + + "Noise amplified expectation values: {:}\n".format(self.result.noise_amplified_exp_vals) + + "Circuit depths: {:}\n".format(self.result.depths) + + "-----\n" + + "Mitigated expectation value: {:.8f}\n".format(self.result.exp_val)) + + return mitigated_exp_val + +""" +--- PAULI TWIRLING AND NOISE AMPLIFICATION HELP FUNCTIONS +""" + +# Conversion from pauli x/y/z-gates to physical u1/u3-gates in correct OpenQASM-format +PHYSICAL_GATE_CONVERSION = {"X": "u3(pi,0,pi)", "Z": "u1(pi)", "Y": "u3(pi,pi/2,pi/2)"} + + +def find_qreg_name(circuit_qasm: str) -> str: + """ + Finds the name of the quantum register in the circuit. Assumes a single quantum register. + + Parameters + ---------- + circuit_qasm : str + The OpenQASM-string for the circuit + + Returns + ------- + qreg_name :str + The name of the quantum register + """ + for line in circuit_qasm.splitlines(): + if line[0:5] == "qreg ": + qreg_name = "" + for i in range(5, len(line)): + if line[i] == "[" or line[i] == ";": + break + elif line[i] != " ": + qreg_name += line[i] + return qreg_name + + +def find_cnot_control_and_target(qasm_line: str) -> (int, int): + """ + Find the indices of the control and target qubits for a specific CNOT-gate. + + Parameters + ---------- + qasm_line : str + The line containing the CNOT-gate in question taken from the OpenQASM-format string of the quantum circuit. + + Returns + ------- + control, target : Tuple[int, int] + qubit indices for control and target qubits + """ + qubits = [] + for i, c in enumerate(qasm_line): + if c == "[": + qubit_nr = "" + for j in range(i + 1, len(qasm_line)): + if qasm_line[j] == "]": + break + qubit_nr += qasm_line[j] + qubits.append(int(qubit_nr)) + return qubits[0], qubits[1] + + +def propagate(control_in: str, target_in: str): + """ + Finds the c,d gates such that (a x b) CNOT (c x d) = CNOT for an ideal CNOT-gate, based on the a (control_in) + and b (target_in) pauli gates by "propagating" the a,b gates over a CNOT-gate by the following identities: + + (X x I) CNOT = CNOT (X x X) + (I x X) CNOT = CNOT (I x X) + (Z x I) CNOT = CNOT (I x Z) + (I x Z) CNOT = XNOT (Z x Z) + + Note that instead of Pauli-twirling with [X,Z,Y] we use [X,Z,XZ] where XZ = -i*Y. + The inverse of XZ is ZX = -XZ = i*Y. The factors of plus minus i are global phase factors which can be ignored. + + Parameters + ---------- + control_in : str + The Pauli operator on control qubit before the CNOT, i.e., a + target_in : str + The Pauli operator on target qubit before the CNOT, i.e., b + Returns + ------- + control_out, target_out : Tuple[str, str] + The operators c and d such that (a x b) CNOT (c x d) = CNOT + """ + + control_out, target_out = '', '' + if 'X' in control_in: + control_out += 'X' + target_out += 'X' + if 'X' in target_in: + target_out += 'X' + if 'Z' in control_in: + control_out += 'Z' + if 'Z' in target_in: + control_out += 'Z' + target_out += 'Z' + + # Pauli gates square to the identity, i.e. XX = I, ZZ = I + # Remove all such occurences from the control & target out Pauli gate strings + if 'ZZ' in control_out: + control_out = control_out[:-2] + if 'ZZ' in target_out: + target_out = target_out[:-2] + if 'XX' in control_out: + control_out = control_out[2:] + if 'XX' in target_out: + target_out = target_out[2:] + + # If no Pauli gates remain then we have the identity gate I + if control_out == '': + control_out = 'I' + if target_out == '': + target_out = 'I' + + # The inverse of XZ is ZX, therefore we reverse the gate order to obtain the correct pauli gates c,d + # such that (a x b) CNOT (c x d) = CNOT (c^-1 x d^-1) (c x d) = CNOT + return control_out[::-1], target_out[::-1] + + +def apply_qasm_pauli_gate(qreg_name: str, qubit: int, pauli_gates: str): + """ + Construct an OpenQASM-string line with the given Pauli-gates applid to the given qubit. + + Parameters + ---------- + qreg_name :str + The name of the qiskit.QuantumRegister containing the qubit. + qubit : int + The index of the qubit. + pauli_gates : str + A string determining the Pauli-gates to be applied. Must be a sequence the characters I, X, Y and/or Z. + Returns + ------- + new_qasm_line : str + An OpenQASM string with the Pauli-gates applied to the qubit. + """ + new_qasm_line = '' + for gate in pauli_gates: + if gate != 'I': + if gate not in PHYSICAL_GATE_CONVERSION.keys(): + raise Exception("Invalid Pauli-gate used in Pauli-twirl: {:}".format(gate)) + u_gate = PHYSICAL_GATE_CONVERSION[gate] + new_qasm_line += u_gate + ' ' + qreg_name + '[' + str(qubit) + '];' + '\n' + return new_qasm_line + + +def pauli_twirl_cnot_gate(qreg_name: str, qasm_line_cnot: str) -> str: + """ + Pauli-twirl a specific CNOT-gate. This involves drawing two random Pauli-gates a and b, picked from the single-qubit + Pauli set {Id, X, Y, Z}, then determining the corresponding two Pauli-gates c and d such that + (a x b) * CNOT * (c x d) = CNOT, for an ideal CNOT. + + The original CNOT gates is then replaced by the gate ((a x b) * CNOT * (c x d)). This transforms the noise in the + CNOT-gate into stochastic Pauli-type noise. An underlying assumption is that the noise in the single-qubit Pauli + gates is negligible to the noise in the CNOT-gates. + + Parameters + ---------- + qreg_name : str + The name of the qiskit.QuantumRegister for the qubits in question. + qasm_line_cnot : str + The OpenQASM-string line containing the CNOT-gate. + + Returns + ------- + new_qasm_line : str + A new OpenQASM-string section to replace the aforementioned OpenQASM line containing the CNOT-gate, where not + the CNOT-gate has been Pauli-twirled. + """ + + control, target = find_cnot_control_and_target(qasm_line_cnot) + + # Note: XZ = -i*Y, with inverse (XZ)^-1 = ZX = i*Y. This simplifies the propagation of gates a,b over the CNOT + pauli_gates = ["I", "X", "Z", "XZ"] + + a = random.choice(pauli_gates) + b = random.choice(pauli_gates) + + # Find gates such that: + # (a x b) CNOT (c x d) = CNOT for an ideal CNOT-gate, + # by propagating the Pauli gates through the CNOT + + c, d = propagate(a, b) + + new_qasm_line = apply_qasm_pauli_gate(qreg_name, control, a) + new_qasm_line += apply_qasm_pauli_gate(qreg_name, target, b) + new_qasm_line += qasm_line_cnot + '\n' + new_qasm_line += apply_qasm_pauli_gate(qreg_name, target, d) + new_qasm_line += apply_qasm_pauli_gate(qreg_name, control, c) + + return new_qasm_line + + +def pauli_twirl_cnots(qc: QuantumCircuit) -> QuantumCircuit: + """ + Pauli-twirl all CNOT-gates in a general quantum circuit. This function is included here for completeness. + + Parameters + ---------- + qc : qiskit.QuantumCircuit + The original quantum circuit. + + Returns + ------- + pauli_twirled_qc : qiskit.QuantumCircuit + The quantum circuit where all CNOT-gates have been Pauli-twirled. + """ + + # The circuit may be expressed in terms of various types of gates. + # The 'Unroller' transpiler pass 'unrolls' the circuit to be expressed in terms of the + # physical gate set [u1,u2,u3,cx] + unroller = Unroller(["u1", "u2", "u3", "cx"]) + pm = PassManager(unroller) + + unrolled_qc = pm.run(qc) + + circuit_qasm = unrolled_qc.qasm() + new_circuit_qasm_str = "" + + qreg_name = find_qreg_name(circuit_qasm) + + for i, line in enumerate(circuit_qasm.splitlines()): + if line[0:2] == "cx": + new_circuit_qasm_str += pauli_twirl_cnot_gate(qreg_name, line) + else: + new_circuit_qasm_str += line + "\n" + + new_qc = QuantumCircuit.from_qasm_str(new_circuit_qasm_str) + + # The "Optimize1qGates" transpiler pass optimizes chains of single-qubit gates by collapsing them into + # a single, equivalent u3-gate + + # We want to avoid that the transpiler optimizes CNOT-gates, as the ancillary CNOT-gates must be kept + # to keep the noise amplification + + optimize1qates = Optimize1qGates() + pm = PassManager(optimize1qates) + + return pm.run(new_qc) + +def noise_amplify_cnots(qc: QuantumCircuit, amp_factor: int): + """ + Noise amplify all CNOT-gates in the given QuantumCircuit by expanding each CNOT-gate as CNOT -> CNOT^a, where a is + the noise amplification factor, a = 2*n - 1. Included here for completeness. + + Parameters + ---------- + qc: qiskit.QuantumCircuit + Quantum circuit to be noise amplified + amp_factor: + The noise amplification factor. Must be odd + Returns + ------- + noise_amplified_qc: qiskit.QuantumCircuit + The noise amplified circuit + """ + + if (amp_factor - 1) % 2 != 0: + raise Exception("Invalid amplification factors", amp_factor) + + # The circuit may be expressed in terms of various types of gates. + # The 'Unroller' transpiler pass 'unrolls' (decomposes) the circuit gates to be expressed in terms of the + # physical gate set [u1,u2,u3,cx] + + # The cz, cy (controlled-Z and -Y) gates can be constructed from a single cx-gate and single-qubit gates. + # For backends with native gate sets consisting of some set of single-qubit gates and either the cx, cz or cy, + # unrolling the circuit to the ["u3", "cx"] basis, amplifying the cx-gates, then unrolling back to the native + # gate set and doing a single-qubit optimization transpiler pass, is thus still general. + + unroller_ugatesandcx = Unroller(["u", "cx"]) + pm = PassManager(unroller_ugatesandcx) + + unrolled_qc = pm.run(qc) + + circuit_qasm = unrolled_qc.qasm() + new_circuit_qasm_str = "" + + # qreg_name = find_qreg_name(circuit_qasm) + + for i, line in enumerate(circuit_qasm.splitlines()): + if line[0:2] == "cx": + for j in range(amp_factor): + new_circuit_qasm_str += line + "\n" + else: + new_circuit_qasm_str += line + "\n" + + new_qc = QuantumCircuit.from_qasm_str(new_circuit_qasm_str) + + return new_qc +