{
"cells": [
{
"cell_type": "markdown",
"id": "85be67db",
"metadata": {},
"source": [
"# Quantum circuits and simulation of noisy algorithms\n",
"\n",
"Welcome to this hands-on session. The session is build around interactive Notebooks, that include code, explanations and tasks. Please run the code cells on your computer and follow the instructions of the tasks to deepen your learning. \n",
"\n",
"Jami Rönkkö, IQM Quantum Computers, email: jami@meetiqm.com"
]
},
{
"cell_type": "markdown",
"id": "7fafd7c7",
"metadata": {},
"source": [
"# Basics of quantum circuits: measurement and two-qubit gates\n",
"\n",
"In this notebook you will learn:\n",
"- how to emulate measurement of qubits\n",
"- entangle qubits with two-qubit gates\n",
"\n",
"The statevector is a mathematical description of the quantum mechanical superposition state of the qubit(s). A fundamental axiom of quantum physics says that reading out or measuring a qubit will only yield classical outputs ∣0⟩ or ∣1⟩: **collapse of a wavefunction/statevector**.\n",
"\n",
"The measurement outcome of a qubit in a superposition state is fundamentally non-deterministic. One can only assign probabilities for different outcomes. The probability of observing specific computational state can be calculated by taking the square of the absolute value of the probability amplitude for that state. For example for the equal superposition state:\n",
"$$\n",
"\\frac{\\sqrt 2}{2}|0\\hspace{-0.1cm}> - \\frac{\\sqrt 2}{2}|1\\hspace{-0.1cm}> \\hspace{1cm} (\\textrm{this state is typically denoted as } |-\\hspace{-0.1cm}>)\n",
"$$\n",
"The probabilites for the two measurement outcomes are obtained as:\n",
"$$\n",
"|\\sqrt 2/2|^2=0.5 ~~\\textrm{ and }~~ |-\\sqrt 2/2|^2=0.5\n",
"$$\n",
"It is noteworthy that the phase of the state (here $e^{2\\pi i}=-1$) does not affect the probability.\n",
"\n",
"\n",
"To emulate measurement of qubits in qiskit we have to:\n",
"- add to the circuit measurement operations and classical bits to store the output\n",
"- use simulator dedicated for measurement simulation \n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4d1ce9e7",
"metadata": {},
"outputs": [],
"source": [
"# Import everything from qiskit \n",
"from qiskit import *"
]
},
{
"cell_type": "markdown",
"id": "c89253b9",
"metadata": {},
"source": [
"## TASK 1

\n",
"> Read and run the three code snippets below to learn how to emulate measurement of a quantum circuit.\n",
">\n",
"> Before running the last cell that executes the circuit, think what states you expect the measurements to register out of all possible *computational* two-qubit states: ∣00⟩, ∣01⟩, ∣10⟩ and ∣11⟩?"
]
},
{
"cell_type": "markdown",
"id": "b3ee4639",
"metadata": {},
"source": [
"# Emulating measurement"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1198d61b",
"metadata": {},
"outputs": [],
"source": [
"# Create a quantum circuit\n",
"circuit = QuantumCircuit(2,2) # First argument is number of qubits and second is number of bits\n",
"\n",
"# Add X gate to the first and H gate to the second qubit of the circuit\n",
"circuit.x(0)\n",
"circuit.h(1) \n",
"\n",
"# Visualise the circuit\n",
"circuit.draw(output='mpl')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ee4dc547",
"metadata": {},
"outputs": [],
"source": [
"circuit.measure([0,1],[0,1]) # Measure qubits 0 and 1 and store the result to classical bits 0 and 1, respectively\n",
"\n",
"# Visualise the circuit\n",
"circuit.draw(output='mpl')"
]
},
{
"cell_type": "markdown",
"id": "74bdc288",
"metadata": {},
"source": [
"Before running the cell below, think what outcome(s) you expect by looking at the above circuit. Run the cell to see if you were right."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "eb30678e",
"metadata": {},
"outputs": [],
"source": [
"simulator = Aer.get_backend(\"qasm_simulator\") # Use qasm_simulator for circuit with measurements\n",
"\n",
"result = execute(circuit, backend = simulator, shots = 11).result() # Define how many circuit executions + measurements we do \n",
"\n",
"# Extract the result (measurement counts) from the executed job and look at the outcome\n",
"measurements = result.get_counts()\n",
"\n",
"from qiskit.visualization import plot_histogram\n",
"\n",
"plot_histogram(result.get_counts())"
]
},
{
"cell_type": "markdown",
"id": "bdda714e",
"metadata": {},
"source": [
"\n",
"> Note: In Qiskit qubit indices in the statevector are read from right to left."
]
},
{
"cell_type": "markdown",
"id": "7b65c56b",
"metadata": {},
"source": [
"## TASK 2

\n",
"> Increase the `shots` argument in the `execute` method in the cell above. Do the statistics of the measurement outcomes agree better with what you would expect based on the gates of the circuit? \n",
">\n",
"> Run statevector simulation below to verify the state of the qubits."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0d797fd6",
"metadata": {},
"outputs": [],
"source": [
"from qiskit.visualization import plot_bloch_multivector\n",
"\n",
"circuit.remove_final_measurements() # For statevector simulation we have to remove measurements from the circuit\n",
"\n",
"result = execute(circuit, backend = Aer.get_backend(\"statevector_simulator\")).result()\n",
"\n",
"result.get_statevector().draw(\"latex\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "457f3e51",
"metadata": {},
"outputs": [],
"source": [
"plot_bloch_multivector(result.get_statevector())"
]
},
{
"cell_type": "markdown",
"id": "ce2ead4e",
"metadata": {},
"source": [
"From the state vector and Bloch spheres we see that first qubit is in state ∣1⟩ and second qubit is in the equal superposition of ∣0⟩ and ∣1⟩ with phase zero; typically called the ∣+⟩ state."
]
},
{
"cell_type": "markdown",
"id": "7cb44123",
"metadata": {},
"source": [
"# Two-qubit gates and entanglement\n",
"We will now introduce the architypical entangling two-qubit gate: *CNOT* aka *controlled-NOT* aka *controlled-X*. "
]
},
{
"cell_type": "markdown",
"id": "8f7c8618",
"metadata": {},
"source": [
"## TASK 3

\n",
"> Figure out the effect of `cx` (CNOT) gate by adding X gates to the beginning of the circuit below. Using the X gates, try the effect of `cx` to all possible computational two-qubit states: ∣00⟩, ∣01⟩, ∣01⟩ and ∣11⟩.\n",
">\n",
"> What does the CNOT gate do?"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0ad19375",
"metadata": {},
"outputs": [],
"source": [
"circuit = QuantumCircuit(2,2)\n",
"\n",
"# Try adding X gates to: 1) neither 2) one 3) the other 4) both qubits\n",
"\n",
"circuit.barrier() # Visual barrier that helps structuring circuits, it has no computational effect\n",
"\n",
"# Apply CNOT gate using the first qubit as control and second as target\n",
"circuit.cx(0,1)\n",
"\n",
"circuit.draw(output='mpl')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0efb79d9",
"metadata": {},
"outputs": [],
"source": [
"# Solve and print the statevector\n",
"result = execute(circuit, backend = Aer.get_backend(\"statevector_simulator\")).result()\n",
"result.get_statevector().draw(\"latex\")\n"
]
},
{
"cell_type": "markdown",
"id": "3d1bf4ed",
"metadata": {},
"source": [
"We can plot also the Bloch spheres of each qubit:"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f4e200e0",
"metadata": {},
"outputs": [],
"source": [
"plot_bloch_multivector(result.get_statevector())"
]
},
{
"cell_type": "markdown",
"id": "1da627fb",
"metadata": {},
"source": [
"We found out that CNOT applies X gate to the target qubit if control qubit is 1 and does nothing if target qubit is 0. It is equivalent to classical *reversible XOR gate*."
]
},
{
"cell_type": "markdown",
"id": "e0cec805",
"metadata": {},
"source": [
"## TASK 4

\n",
"> Go now back and add Hadamard gate to the control qubit. The bloch sphere plots seem to fail now, why is that?"
]
},
{
"cell_type": "markdown",
"id": "84f9c470",
"metadata": {},
"source": [
"**Answer:** \n",
"The Bloch sphere visualization assumes that we can express the state of each qubit separately. By using the CNOT gate with the control qubit in a superposition state, we have *entangled* the qubits. The definition of entanglement is: **entangled qubits cannot be described with separate individual states**.\n",
"\n",
"Two dimensional state i.e. state of a single qubit can be described by a Bloch sphere. Higher dimensional state vectors, that describe combined state of the qubits, would have to be visualised by higher dimensional hypersphere.\n",
"\n",
"As a concrete example, consider two equal superposition states:\n",
"$$\n",
"\\frac{\\sqrt 2}{2}|01\\hspace{-0.1cm}> + \\frac{\\sqrt 2}{2}|11\\hspace{-0.1cm}> ~~\\text{ and }~~ \\frac{\\sqrt 2}{2}|00\\hspace{-0.1cm}> + \\frac{\\sqrt 2}{2}|11\\hspace{-0.1cm}>\n",
"$$\n",
"The first two-qubit state can be described by saying that the first qubit is in state ∣1⟩ and second is in superposition of ∣0⟩ and ∣1⟩ i.e. the state is **separable**. Mathematically: \n",
"$$\n",
"\\frac{\\sqrt 2}{2}|01\\hspace{-0.1cm}> + \\frac{\\sqrt 2}{2}|11\\hspace{-0.1cm}> = \\left( \\frac{\\sqrt 2}{2}|0\\hspace{-0.1cm}> + \\frac{\\sqrt 2}{2}|1\\hspace{-0.1cm}>\\right) \\otimes |1\\hspace{-0.1cm}>\n",
"$$\n",
"\n",
"This is not possible for the second state. Instead we can say that first qubit is 1 iff the second qubit is 1 and 0 iff the second is 0. The qubits are entangled and need to be described by a shared statevector."
]
},
{
"cell_type": "markdown",
"id": "56854ca9",
"metadata": {},
"source": [
"#### Disclaimer: Two-qubit gates don't always cause entanglement: it depends on the state we act upon\n",
"Above we saw that CNOT creates entanglement when control (or target) qubit is in superposition. Indeed without superposition CNOT is just a classical XOR gate. However, it might not be intuitive that 'too much' superposition will also cause CNOT to have no effect. To see this:\n",
" 1) run the cell below without cx gate \n",
" 2) add cx gate and see if the final state changes"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8024feaf",
"metadata": {},
"outputs": [],
"source": [
"circuit = QuantumCircuit(2)\n",
"circuit.h([0,1])\n",
"#circuit.cx(0, 1)\n",
"\n",
"result = execute(circuit, backend = Aer.get_backend(\"statevector_simulator\")).result()\n",
"result.get_statevector().draw(\"latex\")"
]
},
{
"cell_type": "markdown",
"id": "25607670",
"metadata": {},
"source": [
"We see that the state is unaffected by the CNOT gate: in practice ∣01⟩ becomes ∣11⟩ and ∣11⟩ becomes ∣01⟩, so there is no actual change in the state.\n",
"\n",
"The fact that equal superposition states (states that include equal amount of all computational basis) are separable, even if we apply two-qubit gates to them, will be useful in the next Notebook, where we will visualize such states with Bloch Spheres."
]
},
{
"cell_type": "markdown",
"id": "b00868be",
"metadata": {},
"source": [
"## Takeaway\n",
"\n",
"- Real quantum computers cannot directly access the statevector of qubits \\\n",
" $\\rightarrow$ only resulting probability distribution can be sampled by measurements \n",
"\n",
" - Probability for finding qubit in some state ∣x⟩ in measurement is equal to the square of the absolute value of its probability amplitude: $P(x)=|a_x|^2$\n",
" \n",
"- CNOT gate entagles qubits by making them be conditioned to each other's values\n",
"\n",
"In the next notebook we consider compulsory step towards running circuits on real quantum computers: *transpilation*. "
]
}
],
"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.9.13"
},
"vscode": {
"interpreter": {
"hash": "36cf16204b8548560b1c020c4e8fb5b57f0e4c58016f52f2d4be01e192833930"
}
}
},
"nbformat": 4,
"nbformat_minor": 5
}