{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Applying noise to custom unitary gates"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction\n",
    "\n",
    "This notebook shows how to add custom unitary gates to a quantum circuit, and use them for noise simulations in Qiskit Aer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:09:18.154548Z",
     "start_time": "2019-08-19T17:09:15.909907Z"
    }
   },
   "outputs": [],
   "source": [
    "from qiskit import transpile, QuantumCircuit\n",
    "import qiskit.quantum_info as qi\n",
    "\n",
    "from qiskit_aer import AerSimulator\n",
    "from qiskit_aer.noise import NoiseModel, amplitude_damping_error\n",
    "\n",
    "from qiskit.visualization import plot_histogram"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating matrix operators\n",
    "\n",
    "We can use the `Operator` class in `qiskit.quantum_info` to represent arbitrary matrix operators. If the operator is unitary it can then be added to a quantum circuit and used for simulation on Qiskit Aer.\n",
    "\n",
    "Lets create two operators below for a CNOT gate and an iSWAP gate:\n",
    "\n",
    "$$\\mbox{CNOT} = \\left(\\begin{array} \n",
    "& 1 & 0 & 0 & 0 \\\\ \n",
    "0 & 0 & 0 & 1 \\\\ \n",
    "0 & 0 & 1 & 0 \\\\ \n",
    "0 & 1 & 0 & 0\n",
    "\\end{array}\\right), \\quad\n",
    "\\mbox{iSWAP} = \\left(\\begin{array} \n",
    "& 1 & 0 & 0 & 0 \\\\ \n",
    "0 & 0 & i & 0 \\\\ \n",
    "0 & i & 0 & 0 \\\\ \n",
    "0 & 0 & 0 & 1\n",
    "\\end{array}\\right)$$\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:09:18.170527Z",
     "start_time": "2019-08-19T17:09:18.166252Z"
    }
   },
   "outputs": [],
   "source": [
    "# CNOT matrix operator with qubit-0 as control and qubit-1 as target\n",
    "cx_op = qi.Operator([[1, 0, 0, 0],\n",
    "                     [0, 0, 0, 1],\n",
    "                     [0, 0, 1, 0],\n",
    "                     [0, 1, 0, 0]])\n",
    "\n",
    "# iSWAP matrix operator\n",
    "iswap_op = qi.Operator([[1, 0, 0, 0],\n",
    "                        [0, 0, 1j, 0],\n",
    "                        [0, 1j, 0, 0],\n",
    "                        [0, 0, 0, 1]])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Note:** The matrix is specified with respect to the tensor product $U_{b}\\otimes U_{a}$ for qubits specified by list `[a, b]`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Using operators in circuits\n",
    "\n",
    "Let us demonstrate how these can be used in a circuit. We will consider an example of implementing a CNOT gate decomposed in terms of single-qubit gates and the iSWAP gate as follows."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:09:55.343221Z",
     "start_time": "2019-08-19T17:09:55.332156Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "     ┌─────┐     ┌────────┐┌─────┐┌───┐┌─────┐┌────────┐     \n",
      "q_0: ┤ SDG ├─────┤0       ├┤ SDG ├┤ H ├┤ SDG ├┤0       ├─────\n",
      "     ├─────┤┌───┐│  iswap │└─────┘└───┘└─────┘│  iswap │┌───┐\n",
      "q_1: ┤ SDG ├┤ H ├┤1       ├───────────────────┤1       ├┤ S ├\n",
      "     └─────┘└───┘└────────┘                   └────────┘└───┘\n"
     ]
    }
   ],
   "source": [
    "# CNOT in terms of iSWAP and single-qubit gates\n",
    "cx_circ = QuantumCircuit(2, name='cx<iSWAP>')\n",
    "\n",
    "# Add gates\n",
    "cx_circ.sdg(1)\n",
    "cx_circ.h(1)\n",
    "cx_circ.sdg(0)\n",
    "cx_circ.unitary(iswap_op, [0, 1], label='iswap')\n",
    "cx_circ.sdg(0)\n",
    "cx_circ.h(0)\n",
    "cx_circ.sdg(0)\n",
    "cx_circ.unitary(iswap_op, [0, 1], label='iswap')\n",
    "cx_circ.s(1)\n",
    "\n",
    "print(cx_circ)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that we have assigned an optional *label* of `\"iswap\"` to the unitary when it is inserted. This allows us to identify this unitary in a Qiskit Aer `NoiseModel` so that we can add errors to these custom unitary gates in noisy circuit simulations."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can confirm this circuit returns the correct output using the `Operator` class as a simulator for the circuit:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:09:58.954826Z",
     "start_time": "2019-08-19T17:09:58.948275Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Operator([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],\n",
      "          [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],\n",
      "          [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],\n",
      "          [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]],\n",
      "         input_dims=(2, 2), output_dims=(2, 2))\n"
     ]
    }
   ],
   "source": [
    "# Simulate the unitary for the circuit using Operator:\n",
    "unitary = qi.Operator(cx_circ)\n",
    "print(unitary)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And to confirm the output is correct we can compute the average gate fidelity:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:10:01.198369Z",
     "start_time": "2019-08-19T17:10:01.184222Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Average Gate Fidelity: F = 1.000000\n"
     ]
    }
   ],
   "source": [
    "f_ave = qi.average_gate_fidelity(cx_op, unitary)\n",
    "print(\"Average Gate Fidelity: F = {:f}\".format(f_ave))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Creating a custom unitary in a noise model\n",
    "\n",
    "The Qiskit Aer `AerSimulator` supports simulation of arbitrary unitary operators directly as specified by the `\"unitary\"` in the basis gates."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:10:03.174651Z",
     "start_time": "2019-08-19T17:10:03.168643Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "'unitary' in AerSimulator().configuration().basis_gates"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This allows us to add noise models to arbitrary unitaries in our simulation when we identify them using the optional `label` argument of `QuantumCircuit.unitary`.\n",
    "\n",
    "We will now do this by creating a `NoiseModel` that includes a quantum error channel on our custom iSWAP gate. For our example we will create a 2-qubit error consisting of two single-qubit amplitude damping channels with different damping parameters. For now we will assume all the other circuit instructions are ideal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:10:05.585654Z",
     "start_time": "2019-08-19T17:10:05.574669Z"
    }
   },
   "outputs": [],
   "source": [
    "# Error parameters\n",
    "param_q0 = 0.05  # damping parameter for qubit-0\n",
    "param_q1 = 0.1   # damping parameter for qubit-1\n",
    "\n",
    "# Construct the error\n",
    "qerror_q0 = amplitude_damping_error(param_q0)\n",
    "qerror_q1 = amplitude_damping_error(param_q1)\n",
    "iswap_error = qerror_q1.tensor(qerror_q0)\n",
    "\n",
    "# Build the noise model by adding the error to the \"iswap\" gate\n",
    "noise_model = NoiseModel()\n",
    "noise_model.add_all_qubit_quantum_error(iswap_error, 'iswap')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that when we add an error to a custom label such as `\"iswap\"` the `NoiseModel` does not know what gate this label is supposed to apply to, so we must manually add the desired gate string to the noise model `basis_gates`. This ensures that the compiler will unroll to the correct basis gates for the noise model simulation. This can done using the `NoiseModel.add_basis_gates` function:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:10:06.301854Z",
     "start_time": "2019-08-19T17:10:06.298595Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['cx', 'id', 'u3', 'unitary']\n"
     ]
    }
   ],
   "source": [
    "noise_model.add_basis_gates(['unitary'])\n",
    "print(noise_model.basis_gates)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By default the basis gates of a noise model are `['cx','id','u3']` plus any standard `AerSimulator` basis gates that are added to the noise model."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Simulating a custom unitary noise model"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let us first take our previous CX circuit and add an initial Hadamard gate and final measurement to create a Bell-state preparation circuit that we may simulator on the `AerSimulator` both for the ideal and noisy case:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:10:26.234163Z",
     "start_time": "2019-08-19T17:10:26.224218Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "     ┌───┐┌────────────┐┌─┐   \n",
      "q_0: ┤ H ├┤0           ├┤M├───\n",
      "     └───┘│  cx<iSWAP> │└╥┘┌─┐\n",
      "q_1: ─────┤1           ├─╫─┤M├\n",
      "          └────────────┘ ║ └╥┘\n",
      "c: 2/════════════════════╩══╩═\n",
      "                         0  1 \n"
     ]
    }
   ],
   "source": [
    "# Bell state circuit where iSWAPS should be inserted at barrier locations\n",
    "bell_circ = QuantumCircuit(2, 2, name='bell')\n",
    "bell_circ.h(0)\n",
    "bell_circ.append(cx_circ, [0, 1])\n",
    "bell_circ.measure([0,1], [0,1])\n",
    "print(bell_circ)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ideal output\n",
    "\n",
    "Let's first see the ideal output. Since this generates a Bell-state we expect two peaks for 00 and 11."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:10:28.605669Z",
     "start_time": "2019-08-19T17:10:28.467516Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 15164 characters]\n",
      "text/plain": [
       "<Figure size 504x360 with 1 Axes>"
      ]
     },
     "execution_count": 10,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Create ideal simulator backend and transpile circuit\n",
    "sim_ideal = AerSimulator()\n",
    "tbell_circ = transpile(bell_circ, sim_ideal)\n",
    "\n",
    "ideal_result = sim_ideal.run(tbell_circ).result()\n",
    "ideal_counts = ideal_result.get_counts(0)\n",
    "plot_histogram(ideal_counts,\n",
    "               title='Ideal output for iSWAP bell-state preparation')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Noisy circuit execution\n",
    "\n",
    "Finally, let's now simulate it with our custom noise model. Since there is a small amplitude damping error on the two-qubit gates we expect small additional peaks for the 01 and 10 outcome probabilities."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:10:31.078094Z",
     "start_time": "2019-08-19T17:10:30.946144Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 17824 characters]\n",
      "text/plain": [
       "<Figure size 504x360 with 1 Axes>"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Create noisy simulator and transpile circuit\n",
    "sim_noise = AerSimulator(noise_model=noise_model)\n",
    "tbell_circ_noise = transpile(bell_circ, sim_noise)\n",
    "\n",
    "# Run on the simulator without noise\n",
    "noise_result = sim_noise.run(tbell_circ_noise).result()\n",
    "noise_counts = noise_result.get_counts(bell_circ)\n",
    "plot_histogram(noise_counts,\n",
    "               title='Noisy output for iSWAP bell-state preparation')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-08-19T17:10:53.298595Z",
     "start_time": "2019-08-19T17:10:53.290949Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<h3>Version Information</h3><table><tr><th>Qiskit Software</th><th>Version</th></tr><tr><td>Qiskit</td><td>0.25.0</td></tr><tr><td>Terra</td><td>0.17.0</td></tr><tr><td>Aer</td><td>0.8.0</td></tr><tr><td>Ignis</td><td>0.6.0</td></tr><tr><td>Aqua</td><td>0.9.0</td></tr><tr><td>IBM Q Provider</td><td>0.12.2</td></tr><tr><th>System information</th></tr><tr><td>Python</td><td>3.7.7 (default, May  6 2020, 04:59:01) \n",
       "[Clang 4.0.1 (tags/RELEASE_401/final)]</td></tr><tr><td>OS</td><td>Darwin</td></tr><tr><td>CPUs</td><td>6</td></tr><tr><td>Memory (Gb)</td><td>32.0</td></tr><tr><td colspan='2'>Fri Apr 02 12:12:41 2021 EDT</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, 2021.</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"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.7.5"
  },
  "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
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
