qoqo 0.4.4

Quantum computing circuit toolkit. Python interface of roqoq
Documentation
{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Quantum Teleportation with qoqo & the use of conditional measurements\n",
    "\n",
    "This notebook is designed to demonstrate the use of conditional measurements, by following through an example of quantum state teleportation.\n",
    "\n",
    "In quantum teleportation there are two end users: The first user, Alice, wishes to send a particular quantum state to the second user, Bob. The protocol requires a total of three qubits, and the transmission of two classical bits. \n",
    "\n",
    "The sender Alice controls qubits 0 and 1, and the reciever Bob controls qubit 2. \n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "from qoqo_pyquest import PyQuestBackend\n",
    "from qoqo import Circuit\n",
    "from qoqo import operations as ops \n",
    "import numpy as np\n",
    "from math import sqrt, pi\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## State preparation\n",
    "\n",
    "The first step is to prepare the quantum state which Alice will send to Bob. As an example, the most general single qubit quantum state is given by:\n",
    "\\begin{equation}\n",
    "|\\psi \\rangle = cos(\\frac{\\theta}{2}) |0 \\rangle + e^{i \\phi} sin(\\frac{\\theta}{2}) |1 \\rangle.\n",
    "\\end{equation}\n",
    "This state can be prepared by a sequence of two single qubit rotations. In the code block below we first define a function that takes the angles $\\theta$ and $\\phi$ as input and prepares qubit 0 of a quantum register in the state $| \\psi \\rangle$.\n",
    "\n",
    "Next we use an instance of the function with the angles $\\theta=\\frac{\\pi}{2}$ and $\\phi=0$ to create a circuit which prepares the state: \n",
    "\\begin{equation}\n",
    "|\\psi \\rangle = \\frac{1}{\\sqrt{2}} \\big{(} |0 \\rangle + |1 \\rangle \\big{)} = | + \\rangle.\n",
    "\\end{equation}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def prep_psi(Theta: float, Phi: float) -> Circuit:\n",
    "    circuit = Circuit()\n",
    "    circuit += ops.RotateY(qubit=0, theta=Theta)\n",
    "    circuit += ops.RotateZ(qubit=0, theta=Phi)\n",
    "    return circuit\n",
    "\n",
    "init_circuit = prep_psi(pi/2, 0.0)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Preparing an entangled resource state\n",
    "\n",
    "Quantum teleportation requires that the end users initially share an entangled resource state, \n",
    "\\begin{equation}\n",
    "|\\Phi_{+} \\rangle = \\frac{1}{\\sqrt(2)} \\big{(} |00 \\rangle + |11 \\rangle \\big{)} .\n",
    "\\end{equation}\n",
    "\n",
    "The following circuit prepares the state $|\\Phi_{+} \\rangle$ between qubit 1, held by Alice, and qubit 2, held by Bob."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "entangling_circ = Circuit()\n",
    "entangling_circ += ops.Hadamard(qubit=1)\n",
    "entangling_circ += ops.CNOT(control=1, target=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Encoding the state to be sent in the entangled resource state\n",
    "\n",
    "The next step of the procedure is to encode the state of qubit 0, $\\psi$, into the entangled resource state. This is accomplished by way of the circuit defined below, which is similar to that used to prepare the entangled resource. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "encoding_circ = Circuit()\n",
    "encoding_circ += ops.CNOT(control=0, target=1)\n",
    "encoding_circ += ops.Hadamard(qubit=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## State transfer part 1: Measurement\n",
    "\n",
    "At this stage in the process both of Alice's qubits, 0 and 1, are measured. The measurement consumes the entangled resource and leaves the state of qubit 2,Bob's qubit, in a state that depends on the two measurement outcomes. \n",
    "\n",
    "Let us call the classical bit which results from measuring qubit 0 'M1' and the bit resulting from measuring qubit 1 'M2'. The circuit below defines the classical register named 'M1M2', performs the measurement of qubits 0 and 1, and stores the results in the register 'M1M2'. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "meas_circ = Circuit()\n",
    "meas_circ += ops.DefinitionBit(name='M1M2', length=2, is_output=True) #for classical bits corresponding to measurement outcomes\n",
    "meas_circ += ops.MeasureQubit(qubit=0,readout='M1M2',readout_index=0)\n",
    "meas_circ += ops.MeasureQubit(qubit=1,readout='M1M2',readout_index=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Defining the circuit for a conditional operation\n",
    "\n",
    "Conditional operations in qoqo have three inputs: the name of a classical register containing boolean values, the index of the register containing the value to be used to condition the operation, and the operation or sequence of operations to be performed if the boolean condition value is True. \n",
    "\n",
    "To prepare the third input, it is necessary to create circuit snippets corresponding to the operations to be completed if the condition is True. \n",
    "\n",
    "In the case of quantum teleportation, we need two conditional operations. The first is a Pauli Z acting on Bob's qubit, conditioned on the measurement result M1. The second is a Pauli X acting on Bob's qubit, conditioned on the measurement result M2. \n",
    "\n",
    "Hence we prepare circuit snippets correspponding to a Pauli Z and a Pauli X operation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "conditional_Z = Circuit()\n",
    "conditional_Z += ops.PauliZ(qubit=2)\n",
    "\n",
    "conditional_X = Circuit()\n",
    "conditional_X += ops.PauliX(qubit=2)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## State transfer part 2: conditional operations\n",
    "\n",
    "The final stage of the teleportation protocol is to perform corrections to the state of Bob's qubit 2, according to the measurement outcomes 'M1' and 'M2'.\n",
    "\n",
    "The below circuit makes use of the circuit snippets defined above to perform the conditional corrections to the state of qubit 2. 2. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "conditional_circ = Circuit()\n",
    "conditional_circ += ops.PragmaConditional(condition_register='M1M2',condition_index=1, circuit=conditional_X)\n",
    "conditional_circ += ops.PragmaConditional(condition_register='M1M2',condition_index=0, circuit=conditional_Z)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Putting it all together\n",
    "\n",
    "Combining each of the circuits we have defined yeilds the full teleportation protocol. We can verify that the protocol is successful by reading out the final state vector and comparing it to the state which was to be sent, $|\\psi \\rangle$. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[[True, False]]\n",
      "[array([0.        +0.j, 0.70710678+0.j, 0.        +0.j, 0.        +0.j,\n",
      "       0.        +0.j, 0.70710678+0.j, 0.        +0.j, 0.        +0.j])]\n"
     ]
    }
   ],
   "source": [
    "verification = Circuit()\n",
    "# Create register for state vector readout\n",
    "verification += ops.DefinitionComplex(name='psi', length=8, is_output=True) \n",
    "verification += ops.PragmaGetStateVector(readout='psi', circuit=Circuit())\n",
    "\n",
    "# Combine parts for full protocol\n",
    "teleportation_circuit = init_circuit + entangling_circ + encoding_circ + meas_circ + conditional_circ + verification\n",
    "\n",
    "# Run simulation and collect outputs\n",
    "backend = PyQuestBackend(number_qubits=3)\n",
    "(result_bit_registers, result_float_registers, result_complex_registers)=backend.run_circuit(teleportation_circuit)\n",
    "\n",
    "# View measurement outcomes and post-protocol state of qubits\n",
    "print(result_bit_registers['M1M2'])\n",
    "print(result_complex_registers['psi'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.9.1 64-bit ('base': conda)",
   "language": "python",
   "name": "python391jvsc74a57bd0b13e638a16295cae54382cf94de53aab2f36cab0dac81716224e1871a3c10c9a"
  },
  "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.9.1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}