# Single-qubit and multi-qubit states, quantum teleportation (My Lab)

--

Cahyati Supriyati Sangaji (My Note)

You will learn how to write `Qiskit`

code and investigate single-qubit and multi-qubit states using the `qpshere`

visualization. If you have not used Jupyter notebooks before, take a look at the following video to quickly get started.

Remember, to run a cell in Jupyter notebooks, you press `Shift`

+ `Return/Enter`

on your keyboard.

# Installing necessary packages

Before we begin, you will need to install some prerequisites into your environment. Run the cell below to complete these installations. At the end, the cell outputs will be cleared.

!pip install -U seaborn==0.10

!pip install -U qiskit==0.19

!pip install -U ipywidgets==7.5.1from IPython.display import clear_output

clear_output()

# Single-qubit states

Single qubit states can be written down generally as

Here, 𝑝 is the probability that a measurement of the state in the computational basis {|0⟩,|1⟩} will have the outcome 1, and 𝜙 is the phase between the two computational basis states.

Single-qubit gates can then be used to manipulate this quantum state by changing either 𝑝, 𝜙, or both.

Let’s begin by creating a single-qubit quantum circuit. We can do this in `Qiskit`

using the following:

from qiskit import QuantumCircuitmycircuit = QuantumCircuit(1)

mycircuit.draw('mpl')

The above quantum circuit does not contain any gates. Therefore, if you start in any state, say |0⟩, applying this circuit to your state doesn’t change the state.

To see this clearly, let’s create the statevector |0⟩. In `Qiskit`

, you can do this using the following:

from qiskit.quantum_info import Statevectorsv = Statevector.from_label('0')

You can see what’s contained in the object `sv`

:

`sv`

Out:

`Statevector([1.+0.j, 0.+0.j],`

dims=(2,))

The vector itself can be found by writing

`sv.data`

Out:

`array([1.+0.j, 0.+0.j])`

Recall this

We can now apply the quantum circuit `mycircuit`

to this state by using the following:

`new_sv = sv.evolve(mycircuit)`

Once again, you can look at the new statevector by writing

`new_sv`

Out:

`Statevector([1.+0.j, 0.+0.j],`

dims=(2,))

As you can see, the statevector hasn’t changed. Recall the concept of state projection that you learned. You can compute the projection of `new_sv`

onto `sv`

by writing

from qiskit.quantum_info import state_fidelitystate_fidelity(sv, new_sv)

Out:

`1.0`

As you can see, the projection of `new_sv`

onto `sv`

is 1, indicating that the two states are identical. You can visualize this state using the `qsphere`

by writing

from qiskit.visualization import plot_state_qsphereplot_state_qsphere(sv.data)

Applying an 𝑋 gate flips the qubit from the state |0⟩ to the state |1⟩. To see this clearly, we will first create a single-qubit quantum circuit with the 𝑋 gate.

mycircuit = QuantumCircuit(1)

mycircuit.x(0)mycircuit.draw('mpl')

Now, we can apply this circuit onto our state by writing

`sv = Statevector.from_label('0')`

new_sv = sv.evolve(mycircuit)

new_sv

Out:

`Statevector([0.+0.j, 1.+0.j],`

dims=(2,))

As you can see, the statevector now corresponds to that of the state |1⟩. Recall that

Now, the projection of `new_sv`

onto `sv`

is

`state_fidelity(new_sv, sv)`

Out:

`0.0`

This is not surprising. Recall that the states |0⟩ and |1⟩ are orthogonal. Therefore, ⟨0|1⟩=0. The state can be shown on the `qsphere`

by writing

`plot_state_qsphere(new_sv.data)`

Similarly, we can create the state

by applying a Hadamard gate as you learned. Here is how we can create the state and visualize it in `Qiskit`

:

`sv = Statevector.from_label('0')`

mycircuit = QuantumCircuit(1)

mycircuit.h(0)

mycircuit.draw('mpl')

`new_sv = sv.evolve(mycircuit)`

print(new_sv)

plot_state_qsphere(new_sv.data)

As you can see above, the state has equal components of |0⟩ and |1⟩. The size of the circle is proportional to the probability of measuring each basis state in the statevector. As a result, you can see that the size of the circles is half of the size of the circles in our previous visualizations.

Recall that we can also create other superpositions with different phase. Let’s create

which can be done by applying the Hadamard gate on the state |1⟩.

sv = Statevector.from_label('1')

mycircuit = QuantumCircuit(1)

mycircuit.h(0)new_sv = sv.evolve(mycircuit)

print(new_sv)

plot_state_qsphere(new_sv.data)

This time, the bottom circle, corresponding to the basis state |1⟩ has a different color corresponding to the phase of 𝜙=𝜋. This is because the coefficient of |1⟩ in the state

is −1, which is equal to 𝑒𝑖𝜋.

Other phases can also be created by applying different gates. The 𝑇 and � gates apply phases of +𝜋/4 and +𝜋/2, respectively. The widget below helps you see different gates, and their actions on single-qubit quantum states.

`from resources.qiskit_textbook.widgets import gate_demo`

gate_demo(qsphere=True)

Out:

VBox(children=(HBox(children=(Button(description='I', layout=Layout(height='3em', width='3em'), style=ButtonSt…Image(value=b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x01\xf8\x00\x00\x01\xf8\x08\x06\x00\x00\x00\xa6(\xff…

A summary of the operations of the most common gates on single-qubit states is given by the handy image below, where the phases are shown in degrees.

# Multi-qubit states

Similar to the discussion above, you can also explore multi-qubit gates in `Qiskit`

. We will demonstrate below how to create the Bell state

from the state |00⟩. We’ll start by visualizing the state |00⟩ using the same procedure:

`sv = Statevector.from_label('00')`

plot_state_qsphere(sv.data)

Next, we use the Hadamard gate described above, along with a controlled-X gate, to create the Bell state.

`mycircuit = QuantumCircuit(2)`

mycircuit.h(0)

mycircuit.cx(0,1)

mycircuit.draw('mpl')

The result of this quantum circuit on the state |00⟩ is found by writing

`new_sv = sv.evolve(mycircuit)`

print(new_sv)

plot_state_qsphere(new_sv.data)

Note how this looks very similar to a single-qubit superposition with zero phase. Following entanglement, it is no longer possible to treat the two qubits individually, and they must be considered to be one system.

To see this clearly, we can see what would happen if we measured the Bell state above 1000 times.

counts = new_sv.sample_counts(shots=1000)from qiskit.visualization import plot_histogram

plot_histogram(counts)

As you can see above, all measurements give either the result `00`

or `11`

. In other words, if the measurement outcome for one of the qubits is known, then the outcome for the other is fully determined.

# Ungraded exercise 1

Can you create the state

using a similar procedure?

`### WRITE YOUR CODE`

sv = Statevector.from_label('01')

plot_state_qsphere(sv.data)

`mycircuit = QuantumCircuit(2)`

mycircuit.h(0)

mycircuit.cx(0,1)

mycircuit.draw('mpl')

`new_sv = sv.evolve(mycircuit)`

print(new_sv)

plot_state_qsphere(new_sv.data)

counts = new_sv.sample_counts(shots=1000)from qiskit.visualization import plot_histogram

plot_histogram(counts)

# Ungraded exercise 2

Can you create the state

using a similar procedure?

`### WRITE YOUR CODE`

sv = Statevector.from_label('10')

plot_state_qsphere(sv.data)

`mycircuit = QuantumCircuit(2)`

mycircuit.h(0)

mycircuit.cx(0,1)

mycircuit.draw('mpl')

`new_sv = sv.evolve(mycircuit)`

print(new_sv)

plot_state_qsphere(new_sv.data)

counts = new_sv.sample_counts(shots=1000)from qiskit.visualization import plot_histogram

plot_histogram(counts)

# Measurements

In the above example, we simulated the action of a measurement by sampling counts from the statevector. A measurement can explicitly be inserted into a quantum circuit as well. Here is an example that creates the same Bell state and applies a measurement.

`mycircuit = QuantumCircuit(2, 2)`

mycircuit.h(0)

mycircuit.cx(0,1)

mycircuit.measure([0,1], [0,1])

mycircuit.draw('mpl')

Two new features appeared in the circuit compared to our previous examples.

- First, note that we used a second argument in the
`QuantumCircuit(2,2)`

command. The second argument says that we will be creating a quantum circuit that contains two qubits (the first argument), and two classical bits (the second argument). - Second, note that the
`measure`

command takes two arguments. The first argument is the set of qubits that will be measured. The second is the set of classical bits onto which the outcomes from the measurements of the qubits will be stored.

Since the above quantum circuit contains non-unitaries (the measurement gates), we will use `Qiskit`

's built-in `Aer`

simulators to run the circuit. To get the measurement counts, we can use the following code:

`from qiskit import Aer, execute`

simulator = Aer.get_backend('qasm_simulator')

result = execute(mycircuit, simulator, shots=10000).result()

counts = result.get_counts(mycircuit)

plot_histogram(counts)

As you can see, the measurement outcomes are similar to when we sampled counts from the statevector itself.

# Graded exercise 1: Quantum teleportation

In this graded exercise, you will teleport the quantum state

from Alice’s qubit to Bob’s qubit. Recall that the teleportation algorithm consists of four major components:

- Initializing the state to be teleported. We will do this on Alice’s qubit
`q0`

. - Creating entanglement between two qubits. We will use qubits
`q1`

and`q2`

for this. Recall that Alice owns`q1`

, and Bob owns`q2`

. - Applying a Bell measurement on Alice’s qubits
`q0`

and`q1`

. - Applying classically controlled operations on Bob’s qubit
`q2`

depending on the outcomes of the Bell measurement on Alice's qubits.

This exercise guides you through each of these steps.

# Initializing the state to be teleported

First, create a quantum circuit that creates the state

You can do this by using `Qiskit`

's `initialize`

function.

`def initialize_qubit(given_circuit, qubit_index):`

import numpy as np

### WRITE YOUR CODE BETWEEN THESE LINES - START

desired_vector = [np.sqrt(0.7),np.sqrt(0.3)]

given_circuit.initialize(desired_vector, qubit_index)

### WRITE YOUR CODE BETWEEN THESE LINES - END

return given_circuit

Next, we need to create entanglement between Alice’s and Bob’s qubits.

`def entangle_qubits(given_circuit, qubit_Alice, qubit_Bob):`

### WRITE YOUR CODE BETWEEN THESE LINES - START

given_circuit.h(qubit_Alice)

given_circuit.cx(qubit_Alice, qubit_Bob)

### WRITE YOUR CODE BETWEEN THESE LINES - END

return given_circuit

Next, we need to do a Bell measurement of Alice’s qubits.

def bell_meas_Alice_qubits(given_circuit, qubit1_Alice, qubit2_Alice, clbit1_Alice, clbit2_Alice):

### WRITE YOUR CODE BETWEEN THESE LINES - START

given_circuit.cx(qubit1_Alice, qubit2_Alice)

given_circuit.h(qubit1_Alice)

given_circuit.barrier()

given_circuit.measure(qubit1_Alice,clbit1_Alice)

given_circuit.measure(qubit2_Alice,clbit2_Alice)

### WRITE YOUR CODE BETWEEN THESE LINES - ENDreturn given_circuit

Finally, we apply controlled operations on Bob’s qubit. Recall that the controlled operations are applied in this order:

- an 𝑋X gate is applied on Bob’s qubit if the measurement coutcome of Alice’s second qubit,
`clbit2_Alice`

, is`1`

. - a 𝑍Z gate is applied on Bob’s qubit if the measurement coutcome of Alice’s first qubit,
`clbit1_Alice`

, is`1`

.

`def controlled_ops_Bob_qubit(given_circuit, qubit_Bob, clbit1_Alice, clbit2_Alice):`

### WRITE YOUR CODE BETWEEN THESE LINES - START

given_circuit.x(qubit_Bob).c_if(clbit2_Alice, 1)

given_circuit.z(qubit_Bob).c_if(clbit1_Alice, 1)

### WRITE YOUR CODE BETWEEN THESE LINES - END

return given_circuit

The next lines of code put everything together.

### imports

from qiskit import QuantumRegister, ClassicalRegister### set up the qubits and classical bits

all_qubits_Alice = QuantumRegister(2)

all_qubits_Bob = QuantumRegister(1)

creg1_Alice = ClassicalRegister(1)

creg2_Alice = ClassicalRegister(1)### quantum teleportation circuit here

# Initialize

mycircuit = QuantumCircuit(all_qubits_Alice, all_qubits_Bob, creg1_Alice, creg2_Alice)

initialize_qubit(mycircuit, 0)

mycircuit.barrier()

# Entangle

entangle_qubits(mycircuit, 1, 2)

mycircuit.barrier()

# Do a Bell measurement

bell_meas_Alice_qubits(mycircuit, all_qubits_Alice[0], all_qubits_Alice[1], creg1_Alice, creg2_Alice)

mycircuit.barrier()

# Apply classically controlled quantum gates

controlled_ops_Bob_qubit(mycircuit, all_qubits_Bob[0], creg1_Alice, creg2_Alice)### Look at the complete circuit

print(mycircuit.draw(output='text'))### store the circuit as the submitted answer

answer = mycircuit

# Additional reading

- You can watch a video on building the quantum teleportation quantum circuit here: https://www.youtube.com/watch?v=mMwovHK2NrE&list=PLOFEBzvs-Vvp2xg9-POLJhQwtVktlYGbY&index=6&t=0s
- For additional details about the quantum teleportation algorithm, including the principle of deferred measurement, you can refer to the Qiskit Textbook’s section on the algorithm here: https://qiskit.org/textbook/ch-algorithms/teleportation.html
- The
`1 minute Qiskit`

episode entitled`What is the qsphere?`

succinctly describes the Qsphere visualization tool that we used in this lab. You can find it here: https://youtu.be/4SoK2h4a7us

*References:*

Qiskit (Global Summer School), Introduction to Quantum Computing and Quantum Hardware — Lab 1