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

cahyati sangaji (cahya)
10 min readDec 7, 2020

--

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.1
from 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:

  1. Initializing the state to be teleported. We will do this on Alice’s qubit q0.
  2. Creating entanglement between two qubits. We will use qubits q1 and q2 for this. Recall that Alice owns q1, and Bob owns q2.
  3. Applying a Bell measurement on Alice’s qubits q0 and q1.
  4. 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 - END
return 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

References:

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

--

--