{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "d082b5c4-2440-4c96-a6db-831590087fe1",
      "metadata": {
        "id": "d082b5c4-2440-4c96-a6db-831590087fe1"
      },
      "source": [
        "# Introduction\n",
        "These labs will be an introduction to the Qiskit Framework, which is a Python package, developed by IBM, for construction, testing, optimizing, simulating and running quantum circuits on real quantum computers!\n",
        "\n",
        "Qiskit works by declaratively building up quantum circuits by creating Quantum and Classical Registers, creating a circuit object and then adding gates to the circuit that act on specific qubits.\n",
        "# This Lab\n",
        "In this particular lab assignment, these exercises build up to more complex circuits, and you will be creating circuits based on a given input and a given output.\n",
        "\n",
        "When considering the state of a circuit in this lab, consider the \"top\" qubit, qubit 0 in a bitstring representing the state of the circuit. The following circuit numbering is a representation of where each qubit should be considered to be in the bitstring.\n",
        "\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.\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",
        "# 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",
        "# Qiskit Example\n",
        "This is an example for how to build a circuit using Qiskit. It will go over how to create a circuit and apply basic gates to the qubits.\n",
        "\n",
        "# Circuit Creation\n",
        "\n",
        "We create a quantum circuit by first creating a quantum register with the number of qubits that will be included in the overall quantum circuit. We can also create a classical register that will hold the measurement outcomes if there any measurement operations in the circuit.\n",
        "``` python\n",
        "# Allocate a 3-Qubit Quantum Register\n",
        "qrex = qiskit.QuantumRegister(3)\n",
        "# Allocate a 3-Bit Classical Register\n",
        "crex = qiskit.ClassicalRegister(3)\n",
        "# Create a Quantum Circuit with a 3-Qubit Quantum Register and a 3-Bit Classical Register\n",
        "qcex = qiskit.QuantumCircuit(qrex, crex)\n",
        "```\n",
        "\n",
        "# Gate Addition\n",
        "For most of the circuits we will be using, gates are methods of the quantum circuit. For a single-qubit gate, indicate which qubit the gate is being applied to. For a two-qubit gate like the CNOT gate, the order of the qubits matters (control and target). For the two-qubit SWAP gate, the order doesn't matter.\n",
        "\n",
        "``` python\n",
        "qcex.x(qrex[0]) # Apply an X gate to the first qubit.\n",
        "qcex.z(qrex[1]) # Apply a Z gate to the second qubit.\n",
        "qcex.h(qrex[2]) # Apply an H gate to the third qubit.\n",
        "qcex.cx(qrex[0], qrex[1]) # Apply a CNOT gate where the control is the first qubit and the target is the second.\n",
        "qcex.cx(qrex[1], qrex[0]) # Apply a CNOT gate where the control is the second qubit and the target is the first.\n",
        "qcex.swap(qrex[1], qrex[2]) # Apply a SWAP gate between the second and third qubits.\n",
        "\n",
        "qcex.measure(qrex, crex) # Applies a measurement across all the qubits to the classical register\n",
        "\n",
        "# To look at the circuit diagram, use .draw()\n",
        "qcex.draw()\n",
        "```\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c6306750-3009-4377-82eb-8b9f6f6d6aa0",
      "metadata": {
        "id": "c6306750-3009-4377-82eb-8b9f6f6d6aa0"
      },
      "source": [
        "# Pre-Exercise\n",
        "```\n",
        "                ┌───┐\n",
        "q0_0: ──■───────┤ H ├──X───────\n",
        "      ┌─┴─┐┌───┐└───┘  |  ┌───┐\n",
        "q0_1: ┤ X ├┤ X ├───────|──┤ X ├\n",
        "      └───┘└─┬─┘┌───┐  |  └───┘\n",
        "q0_2: ───────■──┤ Z ├──X───────\n",
        "                └───┘\n",
        "    \n",
        "```\n",
        "Recreate the following circuit in Qiskit. Don't worry about spacing, just get the gates in the right order. The number of qubits in the register needs to match the number of qubits in the pictured circuit. You may need to adjust this in the code provided.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "1VwQh_kx50KL",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "1VwQh_kx50KL",
        "outputId": "e6089b3a-7780-48b8-ffb5-3676a4f033a5"
      },
      "outputs": [],
      "source": [
        "## RUN THIS CELL TO INSTALL QISKIT & OTHER RESOURCES\n",
        "## (Press Shift+Enter or click on )\n",
        "!pip install qiskit\n",
        "!pip install qiskit_ibm_runtime\n",
        "!pip install matplotlib\n",
        "!pip install pylatexenc\n",
        "!pip install qiskit-aer"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "b3547fb9-1733-43a7-86d0-fb146bf0ded6",
      "metadata": {
        "id": "b3547fb9-1733-43a7-86d0-fb146bf0ded6"
      },
      "outputs": [],
      "source": [
        "import qiskit\n",
        "\n",
        "def hw1_0_response():\n",
        "    qrpre = qiskit.QuantumRegister(3)\n",
        "    qcpre = qiskit.QuantumCircuit(qrpre)\n",
        "\n",
        "    # Put your code here (spaces for indentation)\n",
        "\n",
        "    # End Code\n",
        "\n",
        "    return qcpre"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "VuWoy9e76zFA",
      "metadata": {
        "id": "VuWoy9e76zFA"
      },
      "source": [
        "## Testing your code\n",
        "All of your solutions will be formatted as functions. This means that your code will not be run unless you explicitly call its function. **This makes it so that the Python interpreter will not catch any errors until you run your code!**  [Read more about Python's laziness.](https://en.wikipedia.org/wiki/Lazy_evaluation)\n",
        "\n",
        "In order to test your code you will have to call each function as follows:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "kXLKTyPr8dKW",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 125
        },
        "id": "kXLKTyPr8dKW",
        "outputId": "2e2ce7a4-8df8-4a37-8ef6-261e3a32eea7"
      },
      "outputs": [],
      "source": [
        "hw1_0_response()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "CrwHjuE9fPR3",
      "metadata": {
        "id": "CrwHjuE9fPR3"
      },
      "source": [
        "You might also want to save the result returned by your function and draw it with `.draw()`. Make sure to run all of your code before submitting your lab!"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "5d7ca0ec-f348-4bb9-a0d5-fdb72182447b",
      "metadata": {
        "id": "5d7ca0ec-f348-4bb9-a0d5-fdb72182447b"
      },
      "source": [
        "# Exercise 1: 1 Qubit Circuit\n",
        "Starting in state $|0\\rangle$ create a circuit that generates a $\\frac{1}{\\sqrt{2}} (|0\\rangle - |1\\rangle)$ state, or $|-\\rangle$.\n",
        "\n",
        "You may include helper functions if needed."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f11eca24-c641-4ff6-ae2c-4deef5c6e0b8",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 63
        },
        "id": "f11eca24-c641-4ff6-ae2c-4deef5c6e0b8",
        "outputId": "e8131e3f-3c84-4b02-b735-d875cd2a2dff"
      },
      "outputs": [],
      "source": [
        "import qiskit\n",
        "\n",
        "def hw1_1_response():\n",
        "    qr1 = qiskit.QuantumRegister(1)\n",
        "    qc1 = qiskit.QuantumCircuit(qr1)\n",
        "\n",
        "    # Put your code here (spaces for indentation)\n",
        "\n",
        "    # End Code\n",
        "\n",
        "    return qc1"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "96ecd534-4719-4515-9553-e8cd2350b1d7",
      "metadata": {
        "id": "96ecd534-4719-4515-9553-e8cd2350b1d7"
      },
      "source": [
        "# Exercise 2: 2 Qubit Circuit\n",
        "This time, we will not assume a known starting state. Starting in unknown state $\\alpha|00\\rangle + \\beta|11\\rangle$, create a circuit that transforms that state to the $\\frac{1}{\\sqrt{2}} (\\beta|00\\rangle - \\alpha|01\\rangle + \\beta|10\\rangle + \\alpha|11\\rangle)$ state.\n",
        "\n",
        "You may include helper functions if needed."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "f929240c-69cf-464a-88db-37ca1efdb087",
      "metadata": {
        "id": "f929240c-69cf-464a-88db-37ca1efdb087"
      },
      "outputs": [],
      "source": [
        "import qiskit\n",
        "\n",
        "def hw1_2_response():\n",
        "    qr2 = qiskit.QuantumRegister(2)\n",
        "    qc2 = qiskit.QuantumCircuit(qr2)\n",
        "\n",
        "    # Put your code here (spaces for indentation)\n",
        "\n",
        "    # End Code\n",
        "\n",
        "    return qc2\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "7f172699-1d66-4d57-bcc4-7df3f6636143",
      "metadata": {
        "id": "7f172699-1d66-4d57-bcc4-7df3f6636143"
      },
      "source": [
        "# Exercise 3: 2 Qubit Circuit\n",
        "Create a circuit that performs the following transformations:\n",
        "\n",
        "- starting in state $|00\\rangle$ generates a $\\frac{1}{\\sqrt{2}} (-|10\\rangle+|11\\rangle)$ state\n",
        "- starting in state $|11\\rangle$ generates a $\\frac{1}{\\sqrt{2}}  (|00\\rangle-|01\\rangle)$ state.\n",
        "\n",
        "**Hint:** You will need to use a CNOT Gate.\n",
        "\n",
        "You may include helper functions if needed."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "2b6b81d6-d994-40eb-9616-1cf6a8fe39d2",
      "metadata": {
        "id": "2b6b81d6-d994-40eb-9616-1cf6a8fe39d2"
      },
      "outputs": [],
      "source": [
        "import qiskit\n",
        "\n",
        "def hw1_3_response():\n",
        "    qr3 = qiskit.QuantumRegister(2)\n",
        "    qc3 = qiskit.QuantumCircuit(qr3)\n",
        "\n",
        "    # Put your code here (spaces for indentation)\n",
        "\n",
        "    # End Code\n",
        "\n",
        "    return qc3\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1e893b83-3ca3-4913-9657-9aa8503b2f9b",
      "metadata": {
        "id": "1e893b83-3ca3-4913-9657-9aa8503b2f9b",
        "tags": []
      },
      "source": [
        "# Exercise 4: 2 Qubit Circuit\n",
        "Create a circuit that:\n",
        "\n",
        "- starting in state $|00\\rangle$ transforms to a $\\frac{1}{2} (|00\\rangle - |01\\rangle - |10\\rangle + |11\\rangle)$ state,\n",
        "- starting in state $|10\\rangle$ transforms to a $\\frac{1}{2} (|00\\rangle + |01\\rangle - |10\\rangle - |11\\rangle)$ state,\n",
        "- starting in state $|01\\rangle$ transforms to a $\\frac{1}{2} (|00\\rangle - |01\\rangle + |10\\rangle - |11\\rangle)$ state\n",
        "- starting in state $|11\\rangle$ transforms to a $\\frac{1}{2} (|00\\rangle + |01\\rangle + |10\\rangle + |11\\rangle)$ state\n",
        "\n",
        "\n",
        "**Hint:** It may be helpful to consider using a SWAP gate.\n",
        "\n",
        "You may include helper functions if needed."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "id": "0c075ee2-bca3-42b9-8cc1-ac83222b97e7",
      "metadata": {
        "id": "0c075ee2-bca3-42b9-8cc1-ac83222b97e7"
      },
      "outputs": [],
      "source": [
        "import qiskit\n",
        "\n",
        "def hw1_4_response():\n",
        "    qr4 = qiskit.QuantumRegister(2)\n",
        "    qc4 = qiskit.QuantumCircuit(qr4)\n",
        "\n",
        "    # Put your code here (spaces for indentation)\n",
        "\n",
        "    # End Code\n",
        "\n",
        "    return qc4"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "be419955-6efc-4ec8-bea9-117bc77dc244",
      "metadata": {
        "id": "be419955-6efc-4ec8-bea9-117bc77dc244",
        "tags": []
      },
      "source": [
        "# Submission\n",
        "Congratulations on completing the lab! Make sure you:\n",
        "1. Test all of your functions by calling them at least once.\n",
        "2. Download your lab as a **Python** `.py` script (*not* an `.ipynb` file):\n",
        "  \n",
        "    ```File-> Download -> Download .py```\n",
        "\n",
        "3. Rename the downloaded file to **Lab3Answers.py**.\n",
        "4. Upload the **Lab3Answers.py** file to Gradescope.\n",
        "5. Ensure the autograder runs successfully."
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "display_name": "Qiskit v0.34.1 (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.8.12"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
