{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Environment Check\n", "\n", "Check that environment containing quantum libaries is being used.\n", "qc jupyter kernel is running using libraries in the conda environent" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "jupyter": { "is_executing": true } }, "outputs": [], "source": "! conda env list" }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import sys\n", "print(sys.executable)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "! jupyter kernelspec list" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Homework 2: Basic Quantum Programs\n", "\n", "First, we will begin by importing Qiskit, IBM's quantum computing software that is written in Python! To create an experiment and run it, we will need to use Qiskit's Circuits, Registers, and Compilers. Programming using qiskit allows us to programmatically extend QASM code to use for loops and if statements to design circuits faster. To execute the circuit we also will need to import the Aer simulator, which is often used to mimic the behavior of evolving state and perform measurements to calculate the expectation." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Import libraries and simulator\n", "Aes is a backend simulator for the IBM quantum computers (other backends are run on real QC). Numpy is good with arrays and matrices. pyplot is for visualization of probability distributions." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import qiskit\n", "from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister\n", "from qiskit_aer import Aer\n", "import matplotlib.pyplot as plt\n", "from qiskit import qasm3\n", "\n", "print('qiskit vers.= %s'%qiskit.__version__)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Setting __AER backends(simulators)__:\\\n", "the `qasm_simulator` simulates a true backend and gets the probabilistic results using shots;\\\n", "the `statevector_simulator` returns an actual ideal vector of probabilities." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#settings and backends\n", "shots = 1024\n", "simulator = Aer.get_backend('qasm_simulator')\n", "state_vector_sim = Aer.get_backend('statevector_simulator')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Demo: Superposition\n", "\n", "First, we will demonstrate __quantum superposition__. This will be accomplished using a one-qubit quantum circuit that has a single gate: the __Hadamard operation__, $\\mathbf{H}$. The qubit is initialized to the computational __basis vector__ $|0\\rangle$ and is then applied to the Hadamard gate. Whenever the resulting __state vector__ for the evolved wavefunction is examined, it is observed that the __probability amplitudes__ for both $|0\\rangle$ and $|1\\rangle$ are equal to $\\frac{1}{\\sqrt{2}}$, indicating __maximal superposition__ of the qubit. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`QuantumCircuit` stores data registers \\\n", "`QuantumRegister` are where the qubit values are initialized and stored.\\\n", "`ClassicalRegister` are used to store the results of quantum registers when measured.\\\n", "`qc.measure(qr,cr)` is used to measure and put quantum result in register\\\n", "Commands `qc.h` are the Hadamard gate." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qr = QuantumRegister(1, 'q_reg')\n", "cr = ClassicalRegister(1, 'c_reg')\n", "qc = QuantumCircuit(qr, cr)\n", "\n", "qc.h(qr[0])\n", "\n", "# Must find state vector for wavefunction before you add measurement operators!\n", "# (Measurement operators cause wavefunction collapse)\n", "state_vector = state_vector_sim.run(qc).result()\n", "vector = state_vector.get_statevector(qc)\n", "\n", "print('\\nSTATEVECTOR: ', vector)\n", "\n", "qc.measure(qr, cr)\n", "\n", "print('\\nQUANTUM CIRCUIT DIAGRAM:')\n", "qc.draw(output=\"mpl\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see the opensource QASM assembly code specification of the circuit." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print('\\nQASM SPECIFICATION:')\n", "print(qasm3.dumps(qc))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A __measurement operation__ placed after the Hadamard gate causes it to collapse into one of the computational basis states. We are measuring in the __Pauli-Z__ measurement basis. We will simulate this circuit 1,024 times (this is called the number of __\"shots\"__ in the IBMQ environment). The measurement operator \"observes\" the quantum information and places the measurement output into the classical register. Once the circuit is complete with registers, a state transformation, and a measurement operation, experiments will be run using the Qiskit Aer simulator. Note how after each run, the output distribution varies slightly, but is close to a 50/50 split between the basis states $|0\\rangle$ and $|1\\rangle$ whenever the input quantum state is in a basis state." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print('\\nSIMULATION RESULTS:')\n", "for i in range(0,3):\n", " plt.figure()\n", " result = simulator.run(qc, shots = shots).result()\n", " counts = result.get_counts(qc)\n", " print('Simulation distribution %d:'%i, counts)\n", " plt.bar(counts.keys(),counts.values())\n", " plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Rotations X and Z\n", "\n", "We will experiment with Pauli-__X__ and Pauli-__Z__; known as the bit flip and phase flip operators. Pauli __Y__ gate can be considered as a bit and phase flip gate " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qr = QuantumRegister(1, 'q_reg')\n", "cr = ClassicalRegister(1, 'c_reg')\n", "qc = QuantumCircuit(qr, cr)\n", "\n", "qc.x(qr[0])\n", "\n", "# Must find state vector for wavefunction before you add measurement operators!\n", "# (Measurement operators cause wavefunction collapse)\n", "state_vector = state_vector_sim.run(qc).result()\n", "vector = state_vector.get_statevector(qc)\n", "print('\\nSTATEVECTOR: ', vector)\n", "\n", "qc.measure(qr, cr)\n", "\n", "print('\\nQUANTUM CIRCUIT DIAGRAM:')\n", "qc.draw(output=\"mpl\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can see the value is flipped to $\\ket{1}$" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print('\\nSIMULATION RESULTS:')\n", "for i in range(0,1):\n", " result = simulator.run(qc, shots = shots).result()\n", " counts = result.get_counts(qc)\n", " print('Simulation distribution %d:'%i, counts)\n", " plt.bar(counts.keys(),counts.values())\n", " plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.1 Phase flip" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qr = QuantumRegister(1, 'q_reg')\n", "cr = ClassicalRegister(1, 'c_reg')\n", "qc = QuantumCircuit(qr, cr)\n", "\n", "qc.z(qr[0])" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Must find state vector for wavefunction before you add measurement operators!\n", "# (Measurement operators cause wavefunction collapse)\n", "state_vector = state_vector_sim.run(qc).result()\n", "vector = state_vector.get_statevector(qc)\n", "print('\\nSTATEVECTOR: ', vector)\n", "\n", "qc.measure(qr, cr)\n", "\n", "print('\\nQUANTUM CIRCUIT DIAGRAM:')\n", "qc.draw(output=\"mpl\")\n", "print('\\nSIMULATION RESULTS:')\n", "for i in range(0,1):\n", " result = simulator.run(qc, shots = shots).result()\n", " counts = result.get_counts(qc)\n", " print('Simulation distribution %d:'%i, counts)\n", " plt.bar(counts.keys(),counts.values())\n", " plt.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.2 Question 1: Why is the phase flip not changing the probability? Implement a Pauli-X using Hadamard and Pauli-Z gates only and test measurement." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Insert the answer to Question 1 HERE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.3 Parameterized rotations\n", "Qiskit has parameterized rotations `rx, ry, rz`.\n", "The most generalized qubit is the `U3` operator in Qiskit" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from math import pi" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qc = QuantumCircuit(1)\n", "qc.u(pi/2, pi/2, pi/2, 0)\n", "qc.rx(pi/2,0)\n", "qc.ry(pi/2,0)\n", "qc.rz(pi/2,0)\n", "qc.draw(output=\"mpl\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.4 Question 2: Create a Hadamard gate using only the rx,ry,rz functions." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Insert the answer to Question 2 HERE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Multiqubit Systems and Specified Rotations Visualized\n", "We now experiment with multiqubit systems." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qr2 = QuantumRegister(3, 'q_reg')\n", "cr2 = ClassicalRegister(3, 'c_reg')\n", "qc2 = QuantumCircuit(qr2, cr2)\n", "qc2.h(qr2[0])\n", "qc2.x(qr2[0])\n", "\n", "qc2.h(qr2[1])\n", "qc2.y(qr2[1])\n", "\n", "qc2.h(qr2[2])\n", "qc2.z(qr2[2])\n", "\n", "state_vector2 = state_vector_sim.run(qc2).result()\n", "vector2 = state_vector2.get_statevector(qc2)\n", "print('\\nSTATEVECTOR: ', vector2)\n", "\n", "qc2.measure(qr2, cr2)\n", "\n", "print('\\nQUANTUM CIRCUIT DIAGRAM:')\n", "qc2.draw(output=\"mpl\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3.1 Question 3: Notice the statevector size is increased. What calculation results in the increased size? What is the growth rate relationship between statevector size and number of qubits, and what is one implication of this?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Insert the Answer to Question 3 HERE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. Run Hadamard on a Real Quantum Computer\n", "\n", "To use this notebook, you must copy your API token from the \"My Account\" page on the IBMQ Experience.\n", "\n", "Create an IBM Quantum Cloud account. https://quantum.cloud.ibm.com/docs/en/guides/cloud-setup\n", "\n", "More information about the tokens is available in the instructions document for this homework. Also, be aware that the\n", "commented out \"QiskitRuntimeService.save_account(token=token)\" should be uncommented the first time you run this code since your token will be\n", "saved to your local disk drive. You can comment out this line afterward, since your token will have already been saved.\n", "\n", "Also note that, running this code more than once in the same session will cause a warning to be issued when the \n", "\"service = QiskitRuntimeService()\" is re-executed since your account will have already been loaded the first time.\n", "You can ignore this warning.\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# import IBM runtime service package\n", "from qiskit_ibm_runtime import QiskitRuntimeService\n", "\n", "# Paste your token from the IBM Quantum Cloud here\n", "token = \"INSERT YOUR IBMQ TOKEN (FROM YOUR ACCOUNT) HERE\"\n", "\n", "# The following statement 'save.account' only needs to be run once, since your token will be saved to disk. When adding new account we need to set overwrite=True\n", "QiskitRuntimeService.save_account(token=token, overwrite=True)\n", "\n", "try:\n", " service = QiskitRuntimeService()\n", " backend = service.backends()\n", "except:\n", " print(\n", " \"\"\"WARNING: No valid IBMQ credentials found on disk.\n", " You must store your credentials using IBMQ.save_account(token, url).\n", " For now, there's only access to local simulator backends...\"\"\"\n", " )\n", " exit(0)\n", " pass\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This shows the types of ibm backend cloud devices. Note that each backend has a specific qubit architecture." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# See a list of available remote backends\n", "ibmq_backends = service.backends()\n", "print(\"Remote backends: \", ibmq_backends)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will select the least busy backend with at least 2 qubits" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Allocate the least busy device with at least 2 qubits\n", "try:\n", " least_busy_device = service.least_busy(\n", " min_num_qubits = 2, simulator= False\n", " )\n", "except:\n", " print(\"All devices are currently unavailable.\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Output selected device based on least queue/load\n", "print(\"Running on current least busy device: \", least_busy_device)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Question 4: Describe the specific IBM backend you will use above and the transmon qubit properties." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Insert the answer to Question 4 HERE" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "qr = QuantumRegister(1, 'q_reg')\n", "cr = ClassicalRegister(1, 'c_reg')\n", "qc = QuantumCircuit(qr, cr)\n", "\n", "qc.h(qr[0])\n", "\n", "# Must find state vector for wavefunction before you add measurement operators!\n", "# (Measurement operators cause wavefunction collapse)\n", "state_vector = state_vector_sim.run(qc).result()\n", "vector = state_vector.get_statevector(qc)\n", "print('\\nSTATEVECTOR: ', vector)\n", "\n", "qc.measure(qr, cr)\n", "\n", "print('\\nQUANTUM CIRCUIT DIAGRAM:')\n", "qc.draw(output=\"mpl\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# import sampler for running actual circuit\n", "from qiskit.transpiler import generate_preset_pass_manager\n", "from qiskit_ibm_runtime import SamplerV2 as Sampler\n", "\n", "# output the specified quantum circuit\n", "print('\\nQUANTUM CIRCUIT DIAGRAM:')\n", "print(qc.draw())\n", "print('\\nQASM CIRCUIT SPECIFICATION:')\n", "print(qasm3.dumps(qc))\n", " \n", "# Execute the quantum circuit and output the results of the execution\n", "print('\\nACTUAL EXECUTION RESULTS:')\n", "for i in range(0,3):\n", " # Build an Instruction Set Architecture (ISA) circuit\n", " pm = generate_preset_pass_manager(optimization_level=1, backend=least_busy_device)\n", " isa_circuit = pm.run(qc)\n", " print('Circuit ops (ISA): %d:', isa_circuit.count_ops())\n", "\n", " sampler = Sampler(mode=least_busy_device)\n", " job = sampler.run([isa_circuit], shots=shots)\n", " print(f\">>> Job ID: {job.job_id()}\")\n", " print(f\">>> Job Status: {job.status()}\")\n", " result = job.result()\n", " counts = result[0].data['c_reg'].get_counts()\n", " print('Actual execution distribution %d:'%i, counts)\n", " plt.bar(counts.keys(),counts.values())\n", " plt.show()\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Question 5: Describe any systematic/random errors in the histogram distributions between the ideal simulator and the quantum computer. What can be causing these differences (or the lack thereof)?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Insert the answer to Question 5 HERE" ] }, { "cell_type": "markdown", "metadata": {}, "source": "## 5. Export this Jupyter notebook to html and submit it by emailing to yayum@smu.edu, and mitch@smu.edu." }, { "metadata": {}, "cell_type": "code", "outputs": [], "execution_count": null, "source": "" } ], "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.18" } }, "nbformat": 4, "nbformat_minor": 4 }