{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# Building Noise Models"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction\n",
    "\n",
    "This notebook introduces how to use the Qiskit Aer `noise` module to build custom noise models for noisy simulations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:00:43.403378Z",
     "start_time": "2019-08-19T17:00:41.139269Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from qiskit import QuantumCircuit, transpile\n",
    "from qiskit.quantum_info import Kraus, SuperOp\n",
    "from qiskit_aer import AerSimulator\n",
    "from qiskit.visualization import plot_histogram\n",
    "\n",
    "# Import from Qiskit Aer noise module\n",
    "from qiskit_aer.noise import (NoiseModel, QuantumError, ReadoutError, \n",
    "    pauli_error, depolarizing_error, thermal_relaxation_error)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "## Qiskit Aer Noise Module\n",
    "\n",
    "The Qiskit Aer `noise` module contains Python classes to build customized noise models for simulation. There are three key classes:\n",
    "\n",
    "1. The `NoiseModel` class which stores a noise model used for noisy simulation.\n",
    "2. The `QuantumError` class which describes CPTP gate errors. These can be applied:\n",
    "    * After *gate* or *reset* instructions\n",
    "    * Before *measure* instructions.\n",
    "\n",
    "3. The `ReadoutError` class which describes classical readout errors."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quantum Errors\n",
    "\n",
    "Rather than deal with the `QuantumError` object directly, many helper functions exist to automatically generate a specific type of parameterized quantum error. These are contained in the `noise` module and include functions for many common errors types used in quantum computing research. The function names and the type of error they return are:\n",
    "\n",
    "| Standard error function | Details |\n",
    "| --- | --- |\n",
    "| `kraus_error` | a general n-qubit CPTP error channel given as a list of Kraus matrices $[K_0, ...]$. |\n",
    "| `mixed_unitary_error` | an n-qubit mixed unitary error given as a list of unitary matrices and probabilities $[(U_0, p_0),...]$. |\n",
    "| `coherent_unitary_error` | an n-qubit coherent unitary error given as a single unitary matrix $U$. |\n",
    "| `pauli_error` | an n-qubit Pauli error channel (mixed unitary) given as a list of Pauli's and probabilities $[(P_0, p_0),...]$ |\n",
    "| `depolarizing_error` | an n-qubit depolarizing error channel parameterized by a depolarization probability $p$. |\n",
    "| `reset_error` | a single-qubit reset error parameterized by a probabilities $p_0, p_1$ of resetting to the $|0\\rangle$, $|1\\rangle$ state.|\n",
    "| `thermal_relaxation_error` | a single qubit thermal relaxation channel parameterized by relaxation time constants $T_1$, $T_2$, gate time $t$, and excited state thermal population $p_1$. |\n",
    "| `phase_amplitude_damping_error` | A single-qubit generalized combined phase and amplitude damping error channel given by an amplitude damping parameter $\\lambda$, a phase damping parameter $\\gamma$, and an excited state thermal population $p_1$. |\n",
    "| `amplitude_damping_error` |  A single-qubit generalized amplitude damping error channel given by an amplitude damping parameter $\\lambda$, and an excited state thermal population $p_1$. |\n",
    "| `phase_damping_error` |  A single-qubit phase damping error channel given by a phase damping parameter $\\gamma$ |\n",
    "\n",
    "### Combining quantum errors\n",
    "\n",
    "`QuantumError` instances can be combined by using composition, tensor product, and tensor expansion (reversed order tensor product) to produce new `QuantumErrors` as:\n",
    "\n",
    " * Composition: $\\cal{E}(\\rho)=\\cal{E_2}(\\cal{E_1}(\\rho))$ as `error = error1.compose(error2)`\n",
    " * Tensor product: $\\cal{E}(\\rho) =(\\cal{E_1}\\otimes\\cal{E_2})(\\rho)$ as `error error1.tensor(error2)`\n",
    " * Expand product: $\\cal{E}(\\rho) =(\\cal{E_2}\\otimes\\cal{E_1})(\\rho)$ as `error error1.expand(error2)`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Example\n",
    "\n",
    "For example to construct a 5% single-qubit Bit-flip error:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:00:43.420358Z",
     "start_time": "2019-08-19T17:00:43.416062Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "QuantumError on 1 qubits. Noise circuits:\n",
      "  P(0) = 0.05, Circuit = \n",
      "   ┌───┐\n",
      "q: ┤ X ├\n",
      "   └───┘\n",
      "  P(1) = 0.95, Circuit = \n",
      "   ┌───┐\n",
      "q: ┤ I ├\n",
      "   └───┘\n",
      "QuantumError on 1 qubits. Noise circuits:\n",
      "  P(0) = 0.05, Circuit = \n",
      "   ┌───┐\n",
      "q: ┤ Z ├\n",
      "   └───┘\n",
      "  P(1) = 0.95, Circuit = \n",
      "   ┌───┐\n",
      "q: ┤ I ├\n",
      "   └───┘\n"
     ]
    }
   ],
   "source": [
    "# Construct a 1-qubit bit-flip and phase-flip errors\n",
    "p_error = 0.05\n",
    "bit_flip = pauli_error([('X', p_error), ('I', 1 - p_error)])\n",
    "phase_flip = pauli_error([('Z', p_error), ('I', 1 - p_error)])\n",
    "print(bit_flip)\n",
    "print(phase_flip)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:00:43.435843Z",
     "start_time": "2019-08-19T17:00:43.432211Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "QuantumError on 1 qubits. Noise circuits:\n",
      "  P(0) = 0.0025000000000000005, Circuit = \n",
      "   ┌───┐┌───┐\n",
      "q: ┤ X ├┤ Z ├\n",
      "   └───┘└───┘\n",
      "  P(1) = 0.0475, Circuit = \n",
      "   ┌───┐┌───┐\n",
      "q: ┤ X ├┤ I ├\n",
      "   └───┘└───┘\n",
      "  P(2) = 0.0475, Circuit = \n",
      "   ┌───┐┌───┐\n",
      "q: ┤ I ├┤ Z ├\n",
      "   └───┘└───┘\n",
      "  P(3) = 0.9025, Circuit = \n",
      "   ┌───┐┌───┐\n",
      "q: ┤ I ├┤ I ├\n",
      "   └───┘└───┘\n"
     ]
    }
   ],
   "source": [
    "# Compose two bit-flip and phase-flip errors\n",
    "bitphase_flip = bit_flip.compose(phase_flip)\n",
    "print(bitphase_flip)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:00:43.460191Z",
     "start_time": "2019-08-19T17:00:43.456782Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "QuantumError on 2 qubits. Noise circuits:\n",
      "  P(0) = 0.0025000000000000005, Circuit = \n",
      "     ┌───┐\n",
      "q_0: ┤ X ├\n",
      "     ├───┤\n",
      "q_1: ┤ Z ├\n",
      "     └───┘\n",
      "  P(1) = 0.0475, Circuit = \n",
      "     ┌───┐\n",
      "q_0: ┤ I ├\n",
      "     ├───┤\n",
      "q_1: ┤ Z ├\n",
      "     └───┘\n",
      "  P(2) = 0.0475, Circuit = \n",
      "     ┌───┐\n",
      "q_0: ┤ X ├\n",
      "     ├───┤\n",
      "q_1: ┤ I ├\n",
      "     └───┘\n",
      "  P(3) = 0.9025, Circuit = \n",
      "     ┌───┐\n",
      "q_0: ┤ I ├\n",
      "     ├───┤\n",
      "q_1: ┤ I ├\n",
      "     └───┘\n"
     ]
    }
   ],
   "source": [
    "# Tensor product two bit-flip and phase-flip errors with\n",
    "# bit-flip on qubit-0, phase-flip on qubit-1\n",
    "error2 = phase_flip.tensor(bit_flip)\n",
    "print(error2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Converting to and from QuantumChannel operators\n",
    " \n",
    "We can also convert back and forth between `QuantumError` objects in Qiskit Aer and `QuantumChannel` objects in Qiskit Terra."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:00:43.482424Z",
     "start_time": "2019-08-19T17:00:43.473779Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Kraus([[[ 9.74679434e-01+0.j,  0.00000000e+00+0.j],\n",
      "        [-1.20234617e-16+0.j,  9.74679434e-01+0.j]],\n",
      "\n",
      "       [[ 2.62045272e-16+0.j,  2.23606798e-01+0.j],\n",
      "        [ 2.23606798e-01+0.j, -2.84112242e-16+0.j]]],\n",
      "      input_dims=(2,), output_dims=(2,))\n"
     ]
    }
   ],
   "source": [
    "# Convert to Kraus operator\n",
    "bit_flip_kraus = Kraus(bit_flip)\n",
    "print(bit_flip_kraus)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:00:43.509521Z",
     "start_time": "2019-08-19T17:00:43.503976Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "SuperOp([[1. +0.j, 0. +0.j, 0. +0.j, 0. +0.j],\n",
      "         [0. +0.j, 0.9+0.j, 0. +0.j, 0. +0.j],\n",
      "         [0. +0.j, 0. +0.j, 0.9+0.j, 0. +0.j],\n",
      "         [0. +0.j, 0. +0.j, 0. +0.j, 1. +0.j]],\n",
      "        input_dims=(2,), output_dims=(2,))\n"
     ]
    }
   ],
   "source": [
    "# Convert to Superoperator\n",
    "phase_flip_sop = SuperOp(phase_flip)\n",
    "print(phase_flip_sop)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:00:43.794037Z",
     "start_time": "2019-08-19T17:00:43.778223Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "QuantumError on 1 qubits. Noise circuits:\n",
      "  P(0) = 1.0, Circuit = \n",
      "   ┌───────┐\n",
      "q: ┤ kraus ├\n",
      "   └───────┘\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Convert back to a quantum error\n",
    "print(QuantumError(bit_flip_kraus))\n",
    "\n",
    "# Check conversion is equivalent to original error\n",
    "QuantumError(bit_flip_kraus) == bit_flip"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Readout Error\n",
    "\n",
    "Classical readout errors are specified by a list of assignment probabilities vectors $P(A|B)$:\n",
    "\n",
    "  * $A$ is the *recorded* classical bit value\n",
    "  * $B$ is the *true* bit value returned from the measurement \n",
    " \n",
    "E.g. for 1 qubits: $ P(A|B) = [P(A|0), P(A|1)]$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:00:44.659598Z",
     "start_time": "2019-08-19T17:00:44.654818Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "ReadoutError([[0.95 0.05]\n",
       " [0.1  0.9 ]])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Measurement miss-assignement probabilities\n",
    "p0given1 = 0.1\n",
    "p1given0 = 0.05\n",
    "\n",
    "ReadoutError([[1 - p1given0, p1given0], [p0given1, 1 - p0given1]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Readout errors may also be combined using `compose`, `tensor` and `expand` like with quantum errors."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Adding errors to a Noise Model\n",
    "\n",
    "When adding a quantum error to a noise model we must specify the type of *instruction* that it acts on, and what qubits to apply it to. There are two cases for Quantum Errors:\n",
    " \n",
    " 1. All-qubit quantum error\n",
    " 2. Specific qubit quantum error\n",
    "  \n",
    "### 1. All-qubit quantum error\n",
    "\n",
    "This applies the same error to any occurrence of an instruction, regardless of which qubits it acts on.\n",
    "\n",
    "It is added as `noise_model.add_all_qubit_quantum_error(error, instructions)`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:00:45.882254Z",
     "start_time": "2019-08-19T17:00:45.877630Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NoiseModel:\n",
      "  Basis gates: ['cx', 'id', 'rz', 'sx', 'u1', 'u2', 'u3']\n",
      "  Instructions with noise: ['u3', 'u2', 'u1']\n",
      "  All-qubits errors: ['u1', 'u2', 'u3']\n"
     ]
    }
   ],
   "source": [
    "# Create an empty noise model\n",
    "noise_model = NoiseModel()\n",
    "\n",
    "# Add depolarizing error to all single qubit u1, u2, u3 gates\n",
    "error = depolarizing_error(0.05, 1)\n",
    "noise_model.add_all_qubit_quantum_error(error, ['u1', 'u2', 'u3'])\n",
    "\n",
    "# Print noise model info\n",
    "print(noise_model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Specific qubit quantum error\n",
    "\n",
    "This applies the error to any occurrence of an instruction acting on a specified list of qubits. Note that the order of the qubit matters: For a 2-qubit gate an error applied to qubits [0, 1] is different to one applied to qubits [1, 0] for example.\n",
    "\n",
    "It is added as `noise_model.add_quantum_error(error, instructions, qubits)`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:00:46.615959Z",
     "start_time": "2019-08-19T17:00:46.612055Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NoiseModel:\n",
      "  Basis gates: ['cx', 'id', 'rz', 'sx', 'u1', 'u2', 'u3']\n",
      "  Instructions with noise: ['u3', 'u2', 'u1']\n",
      "  Qubits with noise: [0]\n",
      "  Specific qubit errors: [('u1', (0,)), ('u2', (0,)), ('u3', (0,))]\n"
     ]
    }
   ],
   "source": [
    "# Create an empty noise model\n",
    "noise_model = NoiseModel()\n",
    "\n",
    "# Add depolarizing error to all single qubit u1, u2, u3 gates on qubit 0 only\n",
    "error = depolarizing_error(0.05, 1)\n",
    "noise_model.add_quantum_error(error, ['u1', 'u2', 'u3'], [0])\n",
    "\n",
    "# Print noise model info\n",
    "print(noise_model)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Note on non-local qubit quantum error\n",
    "\n",
    "`NoiseModel` does not support addition of non-local qubit quantum errors. They should be handled outside of `NoiseModel`. That suggests you should write your own transpiler pass (`TransformationPass`) and run the pass just before running the simulator if you need to insert your quantum errors into your circuit under your own conditions. See `noise.passes.LocalNoisePass` for example of how to implement such a transpiler pass."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Executing a noisy simulation with a noise model\n",
    "\n",
    "The command `AerSimulator(noise_model=noise_model)` returns a simulator configured to the given noise model. In addition to setting the simulator's noise model, it also overrides the simulator's basis gates, according to the gates of the noise model."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "## Noise Model Examples\n",
    "\n",
    "We will now give some examples of noise models. For our demonstrations we will use a simple test circuit generating a n-qubit GHZ state:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:00:48.817405Z",
     "start_time": "2019-08-19T17:00:48.806966Z"
    },
    "slideshow": {
     "slide_type": "fragment"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "        ┌───┐                ░ ┌─┐         \n",
      "   q_0: ┤ H ├──■─────────────░─┤M├─────────\n",
      "        └───┘┌─┴─┐           ░ └╥┘┌─┐      \n",
      "   q_1: ─────┤ X ├──■────────░──╫─┤M├──────\n",
      "             └───┘┌─┴─┐      ░  ║ └╥┘┌─┐   \n",
      "   q_2: ──────────┤ X ├──■───░──╫──╫─┤M├───\n",
      "                  └───┘┌─┴─┐ ░  ║  ║ └╥┘┌─┐\n",
      "   q_3: ───────────────┤ X ├─░──╫──╫──╫─┤M├\n",
      "                       └───┘ ░  ║  ║  ║ └╥┘\n",
      "meas: 4/════════════════════════╩══╩══╩══╩═\n",
      "                                0  1  2  3 \n"
     ]
    }
   ],
   "source": [
    "# System Specification\n",
    "n_qubits = 4\n",
    "circ = QuantumCircuit(n_qubits)\n",
    "\n",
    "# Test Circuit\n",
    "circ.h(0)\n",
    "for qubit in range(n_qubits - 1):\n",
    "    circ.cx(qubit, qubit + 1)\n",
    "circ.measure_all()\n",
    "print(circ)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ideal Simulation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:00:50.560988Z",
     "start_time": "2019-08-19T17:00:50.415545Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 10140 characters]\n",
      "text/plain": [
       "<Figure size 504x360 with 1 Axes>"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Ideal simulator and execution\n",
    "sim_ideal = AerSimulator()\n",
    "result_ideal = sim_ideal.run(circ).result()\n",
    "plot_histogram(result_ideal.get_counts(0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "## Noise Example 1: Basic bit-flip error noise model\n",
    "\n",
    "Lets consider a simple toy noise model example common in quantum information theory research:\n",
    "\n",
    "* When applying a single qubit gate, flip the state of the qubit with probability `p_gate1`.\n",
    "* When applying a 2-qubit gate apply single-qubit errors to each qubit.\n",
    "* When resetting a qubit reset to 1 instead of 0 with probability `p_reset`.\n",
    "* When measuring a qubit, flip the state of the qubit with probability `p_meas`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:00:51.543615Z",
     "start_time": "2019-08-19T17:00:51.536564Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NoiseModel:\n",
      "  Basis gates: ['cx', 'id', 'rz', 'sx', 'u1', 'u2', 'u3']\n",
      "  Instructions with noise: ['reset', 'measure', 'u3', 'u1', 'cx', 'u2']\n",
      "  All-qubits errors: ['reset', 'measure', 'u1', 'u2', 'u3', 'cx']\n"
     ]
    }
   ],
   "source": [
    "# Example error probabilities\n",
    "p_reset = 0.03\n",
    "p_meas = 0.1\n",
    "p_gate1 = 0.05\n",
    "\n",
    "# QuantumError objects\n",
    "error_reset = pauli_error([('X', p_reset), ('I', 1 - p_reset)])\n",
    "error_meas = pauli_error([('X',p_meas), ('I', 1 - p_meas)])\n",
    "error_gate1 = pauli_error([('X',p_gate1), ('I', 1 - p_gate1)])\n",
    "error_gate2 = error_gate1.tensor(error_gate1)\n",
    "\n",
    "# Add errors to noise model\n",
    "noise_bit_flip = NoiseModel()\n",
    "noise_bit_flip.add_all_qubit_quantum_error(error_reset, \"reset\")\n",
    "noise_bit_flip.add_all_qubit_quantum_error(error_meas, \"measure\")\n",
    "noise_bit_flip.add_all_qubit_quantum_error(error_gate1, [\"u1\", \"u2\", \"u3\"])\n",
    "noise_bit_flip.add_all_qubit_quantum_error(error_gate2, [\"cx\"])\n",
    "\n",
    "print(noise_bit_flip)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Executing the noisy simulation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:00:52.951874Z",
     "start_time": "2019-08-19T17:00:52.687440Z"
    },
    "slideshow": {
     "slide_type": "-"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 20408 characters]\n",
      "text/plain": [
       "<Figure size 504x360 with 1 Axes>"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Create noisy simulator backend\n",
    "sim_noise = AerSimulator(noise_model=noise_bit_flip)\n",
    "\n",
    "# Transpile circuit for noisy basis gates\n",
    "circ_tnoise = transpile(circ, sim_noise)\n",
    "\n",
    "# Run and get counts\n",
    "result_bit_flip = sim_noise.run(circ_tnoise).result()\n",
    "counts_bit_flip = result_bit_flip.get_counts(0)\n",
    "\n",
    "# Plot noisy output\n",
    "plot_histogram(counts_bit_flip)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example 2: T1/T2 thermal relaxation\n",
    "\n",
    "Now consider a more realistic error model based on thermal relaxation with the qubit environment:\n",
    "* Each qubit is parameterized by a thermal relaxation time constant $T_1$ and a dephasing time constant $T_2$.\n",
    "* Note that we must have $T_2 \\le 2 T_1$.\n",
    "* Error rates on instructions are determined by gate times and qubit $T_1$, $T_2$ values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:00:54.577456Z",
     "start_time": "2019-08-19T17:00:54.491018Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NoiseModel:\n",
      "  Basis gates: ['cx', 'id', 'rz', 'sx', 'u2', 'u3']\n",
      "  Instructions with noise: ['reset', 'measure', 'u3', 'cx', 'u2']\n",
      "  Qubits with noise: [0, 1, 2, 3]\n",
      "  Specific qubit errors: [('reset', (0,)), ('reset', (1,)), ('reset', (2,)), ('reset', (3,)), ('measure', (0,)), ('measure', (1,)), ('measure', (2,)), ('measure', (3,)), ('u2', (0,)), ('u2', (1,)), ('u2', (2,)), ('u2', (3,)), ('u3', (0,)), ('u3', (1,)), ('u3', (2,)), ('u3', (3,)), ('cx', (0, 0)), ('cx', (0, 1)), ('cx', (0, 2)), ('cx', (0, 3)), ('cx', (1, 0)), ('cx', (1, 1)), ('cx', (1, 2)), ('cx', (1, 3)), ('cx', (2, 0)), ('cx', (2, 1)), ('cx', (2, 2)), ('cx', (2, 3)), ('cx', (3, 0)), ('cx', (3, 1)), ('cx', (3, 2)), ('cx', (3, 3))]\n"
     ]
    }
   ],
   "source": [
    "# T1 and T2 values for qubits 0-3\n",
    "T1s = np.random.normal(50e3, 10e3, 4) # Sampled from normal distribution mean 50 microsec\n",
    "T2s = np.random.normal(70e3, 10e3, 4)  # Sampled from normal distribution mean 50 microsec\n",
    "\n",
    "# Truncate random T2s <= T1s\n",
    "T2s = np.array([min(T2s[j], 2 * T1s[j]) for j in range(4)])\n",
    "\n",
    "# Instruction times (in nanoseconds)\n",
    "time_u1 = 0   # virtual gate\n",
    "time_u2 = 50  # (single X90 pulse)\n",
    "time_u3 = 100 # (two X90 pulses)\n",
    "time_cx = 300\n",
    "time_reset = 1000  # 1 microsecond\n",
    "time_measure = 1000 # 1 microsecond\n",
    "\n",
    "# QuantumError objects\n",
    "errors_reset = [thermal_relaxation_error(t1, t2, time_reset)\n",
    "                for t1, t2 in zip(T1s, T2s)]\n",
    "errors_measure = [thermal_relaxation_error(t1, t2, time_measure)\n",
    "                  for t1, t2 in zip(T1s, T2s)]\n",
    "errors_u1  = [thermal_relaxation_error(t1, t2, time_u1)\n",
    "              for t1, t2 in zip(T1s, T2s)]\n",
    "errors_u2  = [thermal_relaxation_error(t1, t2, time_u2)\n",
    "              for t1, t2 in zip(T1s, T2s)]\n",
    "errors_u3  = [thermal_relaxation_error(t1, t2, time_u3)\n",
    "              for t1, t2 in zip(T1s, T2s)]\n",
    "errors_cx = [[thermal_relaxation_error(t1a, t2a, time_cx).expand(\n",
    "             thermal_relaxation_error(t1b, t2b, time_cx))\n",
    "              for t1a, t2a in zip(T1s, T2s)]\n",
    "               for t1b, t2b in zip(T1s, T2s)]\n",
    "\n",
    "# Add errors to noise model\n",
    "noise_thermal = NoiseModel()\n",
    "for j in range(4):\n",
    "    noise_thermal.add_quantum_error(errors_reset[j], \"reset\", [j])\n",
    "    noise_thermal.add_quantum_error(errors_measure[j], \"measure\", [j])\n",
    "    noise_thermal.add_quantum_error(errors_u1[j], \"u1\", [j])\n",
    "    noise_thermal.add_quantum_error(errors_u2[j], \"u2\", [j])\n",
    "    noise_thermal.add_quantum_error(errors_u3[j], \"u3\", [j])\n",
    "    for k in range(4):\n",
    "        noise_thermal.add_quantum_error(errors_cx[j][k], \"cx\", [j, k])\n",
    "\n",
    "print(noise_thermal)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Executing the noisy simulation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:00:55.689241Z",
     "start_time": "2019-08-19T17:00:55.515394Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 14796 characters]\n",
      "text/plain": [
       "<Figure size 504x360 with 1 Axes>"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Run the noisy simulation\n",
    "sim_thermal = AerSimulator(noise_model=noise_thermal)\n",
    "\n",
    "# Transpile circuit for noisy basis gates\n",
    "circ_tthermal = transpile(circ, sim_thermal)\n",
    "\n",
    "# Run and get counts\n",
    "result_thermal = sim_thermal.run(circ_tthermal).result()\n",
    "counts_thermal = result_thermal.get_counts(0)\n",
    "\n",
    "# Plot noisy output\n",
    "plot_histogram(counts_thermal)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:01:16.716582Z",
     "start_time": "2019-08-19T17:01:16.709462Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<h3>Version Information</h3><table><tr><th>Qiskit Software</th><th>Version</th></tr><tr><td><code>qiskit</code></td><td>0.24.0.dev0+dba2eff</td></tr><tr><td><code>qiskit-aer</code></td><td>0.11.2</td></tr><tr><td><code>qiskit-ibmq-provider</code></td><td>0.19.2</td></tr><tr><td><code>qiskit</code></td><td>0.40.0</td></tr><tr><th>System information</th></tr><tr><td>Python version</td><td>3.8.11</td></tr><tr><td>Python compiler</td><td>Clang 12.0.5 (clang-1205.0.22.11)</td></tr><tr><td>Python build</td><td>default, Jul 27 2021 10:46:38</td></tr><tr><td>OS</td><td>Darwin</td></tr><tr><td>CPUs</td><td>8</td></tr><tr><td>Memory (Gb)</td><td>64.0</td></tr><tr><td colspan='2'>Tue Feb 07 10:30:00 2023 JST</td></tr></table>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<div style='width: 100%; background-color:#d5d9e0;padding-left: 10px; padding-bottom: 10px; padding-right: 10px; padding-top: 5px'><h3>This code is a part of Qiskit</h3><p>&copy; Copyright IBM 2017, 2023.</p><p>This code is licensed under the Apache License, Version 2.0. You may<br>obtain a copy of this license in the LICENSE.txt file in the root directory<br> of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.<p>Any modifications or derivative works of this code must retain this<br>copyright notice, and modified files need to carry a notice indicating<br>that they have been altered from the originals.</p></div>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import qiskit\n",
    "qiskit.__version__\n"
   ]
  }
 ],
 "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.11"
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  },
  "vscode": {
   "interpreter": {
    "hash": "f8729fd834348017bca17aea688b306f536a675180840f7307eb909fff39c285"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
