{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "d082b5c4-2440-4c96-a6db-831590087fe1",
   "metadata": {},
   "source": [
    "# Introduction\n",
    "In this lab you will be exploring applications of entanglement, how to create your own custom gates, analyzing a Qiskit circuit, and adapting a circuit to a device specification.\n",
    "\n",
    "# Some helpful programming hints:\n",
    "Some helpful programming hints:\n",
    "\n",
    "- The line circuit.draw(), where circuit is your Qiskit circuit, will draw out the circuit so you can visualize it. This must be the final call in a cell in order for the circuit to be rendered, alternatively, you can call ```print(circuit)``` at any point to see an ascii representation of the circuit\n",
    "- op = qiskit.quantum_info.Operator(circuit) will create an operator object, and op.data will let you look at the overall matrix for a circuit.\n",
    "- Keep in mind that Qiskit has a different relationship between the drawing and mathematical representation than we have. Specifically, they place the left-most bit at the bottom rather than at the top. You can [**watch this video**](https://youtu.be/Gf7XFFKS9jE) for more information. This has several implications.\n",
    "- If you look at a circuit the way we do, then the state vector ends up being stored as \\[00, 10, 01, 11\\] rather than \\[00, 01, 10, 11\\] (where the qubit on top is still the left-most qubit).\n",
    "- In reality, though, Qiskit also considers the qubit order to be swapped (little endian), where the top qubit is the least significant (right-most) bit. That is for qubits from top to bottom q0, q1, q2, the bitstring is q2, q1, q0. So the state vector is still \\[00, 01, 10, 11\\] from this perspective. We can see this in the CX gate.\n",
    "\n",
    "```\n",
    "q0_0: ──■──  \n",
    "      ┌─┴─┐  \n",
    "q0_1: ┤ X ├  \n",
    "      └───┘  \n",
    "```\n",
    "   \n",
    "This ordering also affects the matrix, resulting in the following for CX:  \n",
    "```\n",
    "[[1, 0, 0, 0],  \n",
    " [0, 0, 0, 1],  \n",
    " [0, 0, 1, 0],  \n",
    " [0, 1, 0, 0]]  \n",
    "```\n",
    "\n",
    "Which will take \\[00: w, 01: x, 10: y, 11: z\\] to \\[00: w, 01: z, 10: y, 11: x\\] in little endian form, and \\[00: w, 01: y, 10: z, 11: x\\] in big endian form (most significant bit first).\n",
    "\n",
    "**You are allowed to use Numpy and Networkx in addition to the python standard library**\n",
    "\n",
    "# Grading:  \n",
    "- The output matrix of your circuit will be compared against a baseline circuit, your circuit will be compared against this matrix.\n",
    "- If they do not match, we will test the circuit with different inputs and compare against the expected values.\n",
    "- You will receive feedback for whether the circuit runs. If it does not, you will receive an error message. If it runs with no message, it means that your circuit runs, but not necessarily that the answer is correct.\n",
    "- **Do not change any function names or return types**\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c6306750-3009-4377-82eb-8b9f6f6d6aa0",
   "metadata": {},
   "source": [
    "# Exercise 1: Teleportation\n",
    "\n",
    "You are given a circuit with two qubits qubit_pair, represented as a tuple of two qubits, in a Bell state. The entangled pair can be in any possible Bell Pair (i.e., starting in |00>, |01>, |10>, or |11> before being entangled). The circuit also has a third qubit, outside_qubit. Write a function that transfers the state from the outside qubit to the second qubit in the Bell pair.\n",
    "\n",
    "circuit: initialized qiskit circuit, add your gates here\n",
    "outside_qubit: the qubit whose value you will be teleporting\n",
    "qubit_pair: tuple containing the indices of the two entangled qubits\n",
    "bell_pair_start: the starting state of the qubit pair before they were put through the entanglement circuit to create a bell pair represented as a two character string: (`'00'`,`'01'`,`'10'`, or `'11'`)\n",
    "\n",
    "\n",
    "Specifically, you will be implementing the `teleportation circuit` portion of the following diagram:\n",
    "\n",
    "![](https://www.classes.cs.uchicago.edu/archive/2026/winter/22880-1/assigns/week7/teleportation_circuit.png)\n",
    "\n",
    "The diagram assumes a `bell_pair_start` of `00`, how would you modify the circuit to handle different starting states?\n",
    "\n",
    "\n",
    "For ease of grading, **please do not add measurement gates to your circuit**. It is not explicitly necessary to demonstrate the transfer of state.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b3547fb9-1733-43a7-86d0-fb146bf0ded6",
   "metadata": {},
   "outputs": [],
   "source": [
    "import qiskit\n",
    "\n",
    "def hw4_1_response(circuit, outside_qubit, qubit_pair, bell_pair_start):\n",
    "    # Put your code here (spaces for indentation)\n",
    "    # End Code\n",
    "    return circuit\n",
    "      "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5d7ca0ec-f348-4bb9-a0d5-fdb72182447b",
   "metadata": {},
   "source": [
    "# Exercise 2: Making Gates\n",
    "Create a function that, given a list of n-bit codes and the length of the code, creates a gate that acts on n+1 qubits, and implements the Archimedes Oracle. Then add it to an n-qubit circuit, and return the circuit from the function.\n",
    "\n",
    "\n",
    "\n",
    "Remember that a Qiskit uses a different ordering of states, where the top qubit is the least significant qubit when creating bitstrings.\n",
    "\n",
    "In our convention, the top qubit is the most significant bit, and a Qiskit matrix acting on a three qubit state vector will act on the state vector as if it was \\[000, 100, 010, 110, 001, 101, 011, 111\\].\n",
    "\n",
    "This means that the response bit will be 'at the top' of the circuit, if the code `001` is included then your matrix should map\n",
    "$$ |0100\\rangle$$\n",
    "to\n",
    " $$ |1100\\rangle$$\n",
    "\n",
    "There is documentation on creating your own, custom gate [**here**](https://quantum.cloud.ibm.com/docs/en/api/qiskit/qiskit.circuit.library.UnitaryGate)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f11eca24-c641-4ff6-ae2c-4deef5c6e0b8",
   "metadata": {},
   "outputs": [],
   "source": [
    "from qiskit.circuit.library import UnitaryGate\n",
    "import numpy as np\n",
    "def hw4_2_response(circuit, n, codes):\n",
    "    # Put your code to find the entangled qubits here\n",
    "\n",
    "    # Put your code here (spaces for indentation)\n",
    "    # End Code\n",
    "\n",
    "    return circuit\n",
    "      "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "96ecd534-4719-4515-9553-e8cd2350b1d7",
   "metadata": {},
   "source": [
    "# Exercise 3: Analyzing Circuits\n",
    "\n",
    "Write a function that given an n qubit circuit, returns a length n bitstring presenting the code for the Bernstein Vazarani Oracle embedded in the circuit, and the target of the oracle. Your bitstring should use an \"x\" to represent the location of the target, and 0s and 1s to represent the rest of the code and treat Qubit 0 as the most signficant bit.\n",
    "\n",
    "The only CX gates included in this circuit are involved in the oracle. You should do this without simulating the circuit, only analyzing the different gates in the circuit.\n",
    "\n",
    "You can examine the different operations in a circuit with a for loop over the circuit:\n",
    "```python\n",
    "for gate in circuit:\n",
    "    print(gate)\n",
    "```"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f929240c-69cf-464a-88db-37ca1efdb087",
   "metadata": {},
   "outputs": [],
   "source": [
    "def hw4_3_response(circuit):\n",
    "\n",
    "    # Put your code to find the entangled qubits here\n",
    "\n",
    "    # Put your code here (spaces for indentation)\n",
    "    # End Code\n",
    "\n",
    "    return bitstring"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "be419955-6efc-4ec8-bea9-117bc77dc244",
   "metadata": {
    "tags": []
   },
   "source": [
    "# Submission\n",
    "Congratulations on completing the lab!  \n",
    "Make sure you:\n",
    "1. Download your lab as a python script (File-> Save and Export Notebook As...->Executable Script\n",
    "2. Rename the downloaded file to **LabAnswers.py**\n",
    "3. Upload the **LabAnswers.py** file to gradescope\n",
    "4. Ensure the autograder runs successfully "
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.10.13"
  },
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "state": {},
    "version_major": 2,
    "version_minor": 0
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
