{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Using Quara's standard tomography features from QuTiP\n",
    "\n",
    "Quara supports wrappers for executing standard quantum tomography from QuTiP. Here we briefly explain how to use them with examples on an 1-qubit system and 2 types of estimators."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from math import sqrt\n",
    "\n",
    "from qutip import basis, ket2dm, identity, sigmax, sigmay, to_super\n",
    "from qutip.qip.operations.gates import hadamard_transform\n",
    "\n",
    "from quara.interface.qutip.api import (\n",
    "    estimate_standard_povmt_from_qutip,\n",
    "    estimate_standard_qpt_from_qutip,\n",
    "    estimate_standard_qst_from_qutip,\n",
    "    generate_empi_dists_from_qutip_gate,\n",
    "    generate_empi_dists_from_qutip_povm,\n",
    "    generate_empi_dists_from_qutip_state,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First, we define quantum objects in QuTiP. We will use these objects as true objects (target objects of estimation) and tester objects (objects that are used to inspect other quantum objects). States can be defined as either `ket` or `oper`. POVMs can only be defined as list of `oper`. Gates can only be defined as `super` operators.\n",
    "\n",
    "In this tutorial we will define true objects due to that we don't have experimental data. Although it is not needed to define true objects if you have empirical distribution of projective measurements from an experiment."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# states\n",
    "x0 = ket2dm((basis(2,0) + basis(2,1)).unit()) # (|0> + |1>)/sqrt(2)\n",
    "x1 = ket2dm((basis(2,0) - basis(2,1)).unit()) # (|0> - |1>)/sqrt(2)\n",
    "y0 = ket2dm((basis(2,0) + 1j * basis(2,1)).unit()) # (|0> + i|1>)/sqrt(2)\n",
    "y1 = ket2dm((basis(2,0) - 1j * basis(2,1)).unit()) # (|0> - i|1>)/sqrt(2)\n",
    "z0 = ket2dm(basis(2,0)) # |0>\n",
    "z1 = ket2dm(basis(2,1)) # |1>\n",
    "a = (identity(2) + sigmax()/sqrt(2) + sigmay()/sqrt(2))/2\n",
    "\n",
    "# POVMs\n",
    "povm_x = [x0, x1]\n",
    "povm_y = [y0, y1]\n",
    "povm_z = [z0, z1]\n",
    "\n",
    "# gate\n",
    "gate_identity = to_super(identity(2))\n",
    "gate_hadamard = to_super(hadamard_transform(1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quantum State Tomography (1-qubit)\n",
    "\n",
    "Here we consider quantum state tomography of an 1-qubit system. We need to define tester POVMs when we perform quantum state tomography using Quara."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\n",
      "Qobj data =\n",
      "[[0.5       +0.j         0.35355339-0.35355339j]\n",
      " [0.35355339+0.35355339j 0.5       +0.j        ]]\n"
     ]
    }
   ],
   "source": [
    "# Testers\n",
    "tester_povms = [povm_x, povm_y, povm_z]\n",
    "\n",
    "# True object\n",
    "true_state = a\n",
    "print(true_state)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We will genearate empirical distributions from the given true object. If you have your own list of empirical distributions make sure that the type matches to `List[Tuple[int, np.ndarray]]`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1000, array([0.864, 0.136]))\n",
      "(1000, array([0.844, 0.156]))\n",
      "(1000, array([0.49, 0.51]))\n"
     ]
    }
   ],
   "source": [
    "# define system\n",
    "mode = \"qubit\"\n",
    "num = 1\n",
    "\n",
    "# define requirement for empirical distribution\n",
    "num_data = 1000\n",
    "seed = 7896\n",
    "\n",
    "# calculate empirical distribution\n",
    "empi_dists = generate_empi_dists_from_qutip_state(mode_system=mode, num_system=num, true_state=true_state, tester_povms=tester_povms, num_sum=num_data, seed=seed, schedules=\"all\")\n",
    "for f in empi_dists:\n",
    "    print(f)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now, we execute quantum state tomography using linear estimation. You can estimate the quantum state just by calling `estimate_standard_qst_from_qutip()`. You can choose linear estimation by specifying `estimator_name=\"linear\"`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\n",
      "Qobj data =\n",
      "[[0.49 +0.j    0.364-0.344j]\n",
      " [0.364+0.344j 0.51 +0.j   ]]\n"
     ]
    }
   ],
   "source": [
    "# estimate the state using linear estimator\n",
    "estimate = estimate_standard_qst_from_qutip(mode_system=mode, num_system=num, tester_povms=tester_povms, empi_dists=empi_dists, estimator_name=\"linear\", schedules=\"all\")\n",
    "print(estimate)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can choose least squares estimation by specifying `estimator_name=\"least_squares\"`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\n",
      "Qobj data =\n",
      "[[0.49001861+0.j         0.36332341-0.34336059j]\n",
      " [0.36332341+0.34336059j 0.50998144+0.j        ]]\n"
     ]
    }
   ],
   "source": [
    "# estimate the state using least squares estimator\n",
    "estimate = estimate_standard_qst_from_qutip(mode_system=mode, num_system=num, tester_povms=tester_povms, empi_dists=empi_dists, estimator_name=\"least_squares\", schedules=\"all\")\n",
    "print(estimate)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## POVM tomography (1-qubit)\n",
    "\n",
    "Here we consider POVM tomography of a 1-qubit system. We need to define tester states when we perform POVM tomography using Quara.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\n",
      "Qobj data =\n",
      "[[1. 0.]\n",
      " [0. 0.]]\n",
      "Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\n",
      "Qobj data =\n",
      "[[0. 0.]\n",
      " [0. 1.]]\n"
     ]
    }
   ],
   "source": [
    "# Testers\n",
    "tester_states = [x0, y0, z0, z1]\n",
    "\n",
    "# True object\n",
    "true_povm = povm_z\n",
    "for q_obj in true_povm:\n",
    "    print(q_obj)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1000, array([0.496, 0.504]))\n",
      "(1000, array([0.502, 0.498]))\n",
      "(1000, array([1., 0.]))\n",
      "(1000, array([0., 1.]))\n"
     ]
    }
   ],
   "source": [
    "# define system\n",
    "mode = \"qubit\"\n",
    "num = 1\n",
    "\n",
    "# define requirement for empirical distribution\n",
    "num_data = 1000\n",
    "seed = 7896\n",
    "\n",
    "# calculate empirical distribution\n",
    "empi_dists = generate_empi_dists_from_qutip_povm(mode_system=mode, num_system=num, true_povm=true_povm, tester_states=tester_states, num_sum=num_data, seed=seed, schedules=\"all\")\n",
    "for f in empi_dists:\n",
    "    print(f)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When performing POVM tomography make sure you specify `num_outcomes`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\n",
      "Qobj data =\n",
      "[[ 1.00000000e+00+0.j    -4.00000000e-03-0.002j]\n",
      " [-4.00000000e-03+0.002j -1.11022302e-16+0.j   ]]\n",
      "Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\n",
      "Qobj data =\n",
      "[[1.11022302e-16+0.j    4.00000000e-03+0.002j]\n",
      " [4.00000000e-03-0.002j 1.00000000e+00+0.j   ]]\n"
     ]
    }
   ],
   "source": [
    "# estimate POVM using linear estimator\n",
    "estimate = estimate_standard_povmt_from_qutip(mode_system=mode, num_system=num, num_outcomes=2, tester_states=tester_states, empi_dists=empi_dists, estimator_name=\"linear\", schedules=\"all\")\n",
    "for q_obj in estimate:\n",
    "    print(q_obj)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\n",
      "Qobj data =\n",
      "[[ 9.99980029e-01+0.j         -3.99968437e-03-0.00199982j]\n",
      " [-3.99968437e-03+0.00199982j  1.99971568e-05+0.j        ]]\n",
      "Quantum object: dims = [[2], [2]], shape = (2, 2), type = oper, isherm = True\n",
      "Qobj data =\n",
      "[[1.99712481e-05+0.j         3.99968437e-03+0.00199982j]\n",
      " [3.99968437e-03-0.00199982j 9.99980003e-01+0.j        ]]\n"
     ]
    }
   ],
   "source": [
    "# estimate POVM using least squares estimator\n",
    "estimate = estimate_standard_povmt_from_qutip(mode_system=mode, num_system=num, num_outcomes=2, tester_states=tester_states, empi_dists=empi_dists, estimator_name=\"least_squares\", schedules=\"all\")\n",
    "for q_obj in estimate:\n",
    "    print(q_obj)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Quantum Process Tomography (1-qubit)\n",
    "\n",
    "Finally, we consider performing quantum process tomography of a 1-qubit system from QuTiP."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Quantum object: dims = [[[2], [2]], [[2], [2]]], shape = (4, 4), type = super, isherm = True\n",
      "Qobj data =\n",
      "[[ 0.5  0.5  0.5  0.5]\n",
      " [ 0.5 -0.5  0.5 -0.5]\n",
      " [ 0.5  0.5 -0.5 -0.5]\n",
      " [ 0.5 -0.5 -0.5  0.5]]\n"
     ]
    }
   ],
   "source": [
    "# Testers\n",
    "tester_states = [x0, y0, z0, z1]\n",
    "tester_povms = [povm_x, povm_y, povm_z]\n",
    "\n",
    "# True object\n",
    "true_gate = gate_hadamard\n",
    "print(true_gate)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1000, array([0.496, 0.504]))\n",
      "(1000, array([0.502, 0.498]))\n",
      "(1000, array([1., 0.]))\n",
      "(1000, array([0.488, 0.512]))\n",
      "(1000, array([0., 1.]))\n",
      "(1000, array([0.491, 0.509]))\n",
      "(1000, array([1., 0.]))\n",
      "(1000, array([0.507, 0.493]))\n",
      "(1000, array([0.513, 0.487]))\n",
      "(1000, array([0., 1.]))\n",
      "(1000, array([0.47, 0.53]))\n",
      "(1000, array([0.497, 0.503]))\n"
     ]
    }
   ],
   "source": [
    "# define system\n",
    "mode = \"qubit\"\n",
    "num = 1\n",
    "\n",
    "# define requirement for empirical distribution\n",
    "num_data = 1000\n",
    "seed = 7896\n",
    "\n",
    "# calculate empirical distribution\n",
    "empi_dists = generate_empi_dists_from_qutip_gate(mode_system=mode, num_system=num, true_gate=true_gate, tester_states=tester_states, tester_povms=tester_povms, num_sum=num_data, seed=seed, schedules=\"all\")\n",
    "for f in empi_dists:\n",
    "    print(f)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Quantum object: dims = [[[2], [2]], [[2], [2]]], shape = (4, 4), type = super, isherm = False\n",
      "Qobj data =\n",
      "[[ 0.513 +0.j      0.495 +0.014j   0.495 -0.014j   0.497 +0.j    ]\n",
      " [ 0.5   +0.007j  -0.4925+0.0255j  0.4845+0.0015j -0.5   -0.03j  ]\n",
      " [ 0.5   -0.007j   0.4845-0.0015j -0.4925-0.0255j -0.5   +0.03j  ]\n",
      " [ 0.487 +0.j     -0.495 -0.014j  -0.495 +0.014j   0.503 +0.j    ]]\n"
     ]
    }
   ],
   "source": [
    "# estimate the gate using linear estimator\n",
    "estimate = estimate_standard_qpt_from_qutip(mode_system=mode, num_system=num, tester_states=tester_states, tester_povms=tester_povms, empi_dists=empi_dists, estimator_name=\"linear\", schedules=\"all\")\n",
    "print(estimate)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Quantum object: dims = [[[2], [2]], [[2], [2]]], shape = (4, 4), type = super, isherm = False\n",
      "Qobj data =\n",
      "[[ 0.51438824+0.j          0.48853761+0.00725424j  0.48853761-0.00725424j\n",
      "   0.49664456+0.j        ]\n",
      " [ 0.49329968+0.00741707j -0.49249075-0.00293811j  0.48871849+0.0007454j\n",
      "  -0.49336835-0.01536569j]\n",
      " [ 0.49329968-0.00741707j  0.48871849-0.0007454j  -0.49249075+0.00293811j\n",
      "  -0.49336835+0.01536569j]\n",
      " [ 0.48561176+0.j         -0.48853761-0.00725424j -0.48853761+0.00725424j\n",
      "   0.50335544+0.j        ]]\n"
     ]
    }
   ],
   "source": [
    "# estimate the gate using least squares estimator\n",
    "estimate = estimate_standard_qpt_from_qutip(mode_system=mode, num_system=num, tester_states=tester_states, tester_povms=tester_povms, empi_dists=empi_dists, estimator_name=\"least_squares\", schedules=\"all\")\n",
    "print(estimate)"
   ]
  }
 ],
 "metadata": {
  "interpreter": {
   "hash": "cb92e15a8d637e9dd9c5ecfc8cd130dbba6859f1dbeed3d128cdd82d23a19cd6"
  },
  "kernelspec": {
   "display_name": "Python 3.8.7 64-bit ('venv': venv)",
   "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.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
