Quantum teleportation and superdense coding
Kifumi Numata (26 Apr 2024)
Download the pdf of the original lecture. Note that some code snippets might become deprecated since these are static images.
Approximate QPU time to run this experiment is 10 seconds.
1. Introduction
To solve any utility-scale quantum problem, we will need to move information around on a quantum computer from one qubit to another. There are well-known protocols for doing this, but some of the most foundational were cast in the context of sending information between distant parties. Throughout this lesson, we will sometimes use language consistent with this context, such as "distant friends sending information". But keep in mind that these protocols have broader significance in quantum computing. In this lesson we consider the following quantum communication protocols:
- Quantum teleportation Using a shared entangled state (sometimes called an e-bit) to send an unknown quantum state to a distant friend, requiring supplemental classical communication.
- Quantum superdense coding How to send two bits of information by sending a single qubit to a distant friend (again using prior shared entangled qubits).
For more background relevant to these topics, we recommend lesson 4 in Basics of Quantum Information on Entanglement in action.
In the above description, an "unknown quantum state" simply refers to a state of the form described in the previous lesson:
where and are complex numbers such that . This allows us to write the quantum state as
Since we want to be able to transfer the information in any random quantum state, generating such a state is where we will begin this lesson.
2. Density matrices
We can also write the quantum state as its density matrix. This form is useful for denoting probabilistic mixture of pure quantum states. In the case of a single qubit, we can write
Note that the density matrix is a linear summation of Pauli matrices, as below,
Or, in general,
where .
And, the Bloch vector is .
Now, let's make an arbitrary quantum state using random numbers.
import numpy as np
# create a random 1-qubit state from a random (theta, varphi) to define r vector
np.random.seed(1) # fixing seed for repeatibility
theta = np.random.uniform(0.0, 1.0) * np.pi # from 0 to pi
varphi = np.random.uniform(0.0, 2.0) * np.pi # from 0 to 2*pi
def get_r_vec(theta, varphi):
rx = np.sin(theta) * np.cos(varphi)
ry = np.sin(theta) * np.sin(varphi)
rz = np.cos(theta)
return (rx, ry, rz)
# get r vector
rx, ry, rz = get_r_vec(theta, varphi)
print("theta=" + str(theta), ",varphi=" + str(varphi))
print("(rx, ry, rz) = (" + str(rx) + ", " + str(ry) + ", " + str(rz) + ")")
Output:
theta=1.3101132663588946 ,varphi=4.525932273597346
(rx, ry, rz) = (-0.1791150283307452, -0.9494670044331133, 0.2577405946274022)
We can show this Bloch vector on the Bloch sphere.
from qiskit.visualization import plot_bloch_vector
r = [rx, ry, rz]
plot_bloch_vector(r)
Output:

3. Quantum state tomography
If you only measure the quantum state in the computational basis ( and ), the phase information (the complex number information) will be lost. But if we have many copies of by repeating the preparation process (we can't clone states, but we can repeat preparation processes), we can estimate the value of by performing quantum state tomography for density matrix . Given the form:
it holds that
In case,
The last transformation of the equation is for . Therefore, we can obtain by probability of - Probability of .
Estimate value
In order to estimate , we create a quantum state and measure it. We then repeat the preparation and measurement many times. Finally we use the statistics of the measurement to estimate the probabilities above and thus estimate .
For creating the random quantum state, we will use the general unitary gate with the parameters . (Refer to U-gate for more information.)
from qiskit import QuantumCircuit
# create a 1-qubit quantum state psi from theta, varphi parameters
qc = QuantumCircuit(1, 1)
qc.u(theta, varphi, 0.0, 0)
# measure in computational basis
qc.measure(0, 0)
qc.draw(output="mpl")
Output:

Using the AerSimulator
, we will measure it in the computational basis to estimate .
# see if the expected value of measuring in the computational basis
# approaches the limit of rz
from qiskit_aer import AerSimulator
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Sampler
from qiskit.visualization import plot_histogram
# Define backend
backend = AerSimulator()
nshots = 1000 # or 10000
# nshots = 10000
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=nshots)
result = job.result()
# Extract counts data
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
Output:
{'1': 375, '0': 625}

rz_approx = (counts["0"] - counts["1"]) / nshots
print("rz = ", rz, " and approx of rz = ", rz_approx)
Output:
rz = 0.2577405946274022 and approx of rz = 0.25
Using the quantum state tomography method, we estimated the value. In this case, since we chose a parameter for the "random" state, we know the value of and can check our work. But by its very nature, utility-scale work is not always so trivial to check. We will discuss more about checking quantum results later in this course. For now, simply note that our estimation was reasonably accurate.
Exercise 1: Estimate value
Recall that IBM® quantum computers measure along the -axis (sometimes stated "in the basis" or "in the computational basis"). However, by using rotations before the measurement, we can measure the quantum state's projection on the x-axis, also. To be more precise, if we rotate our system such that things that did point along now point along , then we can keep the same measurement hardware along , but learn about the state that was just along a moment ago. This is how most quantum computers (and all IBM quantum computers) perform measurements along multiple axes.
With this understanding, try writing code to estimate the value of using quantum state tomography.
Solution:
# create a 1-qubit quantum state psi from theta, varphi parameters
qc = QuantumCircuit(1, 1)
qc.u(theta, varphi, 0.0, 0)
qc.h(0)
qc.measure(0, 0)
qc.draw(output="mpl")
Output:

# Define backend
backend = AerSimulator()
nshots = 10000
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=nshots)
result = job.result()
# Extract counts data
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
Output:
{'1': 5925, '0': 4075}

rx_approx = (counts["0"] - counts["1"]) / nshots
print("rx = ", rx, " and approx of rx = ", rx_approx)
Output:
rx = -0.1791150283307452 and approx of rx = -0.185
Exercise 2: Estimate value
Using the same logical arguments as before, we can rotate the system prior to measurement to learn about the .
Try writing code yourself to estimate the value of using the quantum state tomography. You could start with the previous example, but make different rotations. (For more information about the various gates used, including sdg
, please refer to the API reference.
Solution:
# create a 1-qubit quantum state psi from theta, varphi parameters
qc = QuantumCircuit(1, 1)
qc.u(theta, varphi, 0.0, 0)
qc.sdg(0)
qc.h(0)
qc.measure(0, 0)
qc.draw(output="mpl")
Output:

# Define backend
backend = AerSimulator()
nshots = 10000
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=nshots)
result = job.result()
# Extract counts data
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
Output:
{'1': 9759, '0': 241}

ry_approx = (counts["0"] - counts["1"]) / nshots
print("ry = ", ry, " and approx of ry = ", ry_approx)
Output:
ry = -0.9494670044331133 and approx of ry = -0.9518
We have now estimated all components of and can write out the full vector.
print("Estimated vector is (", rx_approx, ",", ry_approx, ",", rz_approx, ").")
print("Original random vector was (" + str(rx) + ", " + str(ry) + ", " + str(rz) + ").")
Output:
Estimated vector is ( -0.185 , -0.9518 , 0.25 ).
Original random vector was (-0.1791150283307452, -0.9494670044331133, 0.2577405946274022).
You obtained the estimation of the original random vector fairly accurately using this quantum state tomography method.
4. Quantum teleportation
Let us consider the situation when a character Alice wants to send an unknown quantum state to her friend Bob, who is far away. Assume they can only communicate with classical communication (like using email or a phone). Alice cannot copy the quantum state (due to the no-cloning theorem). If she repeated the same preparation process many times, she could build up statistics as we just did. But what if there is only a single unknown state? This state might have emerged from a physical process you want to study. Or it could be part of a larger quantum computation. In that case, how could Alice send the state to Bob? She can, if she and Bob share a valuable quantum resource: a shared entangled state, like the Bell state introduced in the previous lesson: You might sometimes also see this referred to as an "EPR pair" or an "e-bit" (a fundamental unit of entanglement). If Alice shares such an entangled state with Bob, she can teleport the unknown quantum state to Bob by performing a series of quantum operations and sending him two bits of classical information.
4.1 The protocol of Quantum teleportation
Assumption: Alice has an unknown quantum state to be sent to Bob. Alice and Bob shares a 2-qubit entangled state, or e-bit, each having one of the qubits physically at their location.
Here we outline the procedure without explanation. These will be implemented in detail below.
- Alice entangles with her part of the e-bit using the CNOT gate.
- Alice applies a Hadamard gate to , and measures both her qubits in the computational basis.
- Alice sends Bob her measurement results (either “00”, “01”, “10”, or “11”)
- Bob performs a correction operator based on Alice’s two-bit of information on his part of the e-bit pair.
- If “00”, Bob does nothing
- If “01”, Bob applies X gate
- If “10”, Bob applies Z gate
- If “11”, Bob applies iY = ZX gate
- Bob's part of the e-bit becomes .
This is also worked out in more detail in Basics of Quantum Information. But the situation will become more clear as we instantiate this in Qiskit.
4.2 Quantum circuit simulating the quantum teleportation
As always, we will apply the Qiskit patterns framework. This subsection will focus on mapping only.
Step 1: Map problem to quantum circuits and operators
To describe the scenario above, we need a circuit with three qubits: two for the entangled pair shared by Alice and Bob, and one for the unknown quantum state .
from qiskit import QuantumCircuit
import numpy as np
# create 3-qubits circuit
qc = QuantumCircuit(3, 3)
qc.draw(output="mpl")
Output:

At the start, Alice has an unknown quantum state We will create this using the gate.
# Create the unknown quantum state using the u-gate. Alice has this.
qc.u(theta, varphi, 0.0, 0)
qc.barrier() # for visual separation
qc.draw(output="mpl")
Output:

We can visualize the state we've created, but only because we know what parameters were used in the gate. If this state had emerged from a complicated quantum process, the state would not be knowable without running the process to create the state many times, and collecting statistics as in tomography.
# show the quantum state on bloch sphere
from qiskit.quantum_info import Statevector
from qiskit.visualization import plot_bloch_multivector
out_vector = Statevector(qc)
plot_bloch_multivector(out_vector)
Output:

Before this protocol even begins, we assume Alice and Bob have a shared entangled pair. If Alice and Bob are truly in different locations, they might have set up the shared state before the unknown state was ever created. Because those things are happening on different qubits, there order here won't matter, and this order is convenient for visualization.
# Alice and Bob are together in the same place and set up an entangled pair.
qc.h(1)
qc.cx(1, 2)
qc.barrier() # for visual separation.
# We can consider that Alice and Bob might move their qubits to different physical locations, now.
qc.draw(output="mpl")
Output:

Next, Alice entangles with her part of the shared e-bit, using the gate & gate, and measures them in the computational basis.
# Alice entangles the unknown state with her part of the e-bit, using the CNOT gate & H gate.
qc.cx(0, 1)
qc.h(0)
qc.barrier()
# Alice measures the two qubits.
qc.measure(0, 0)
qc.measure(1, 1)
qc.draw(output="mpl")
Output:

Alice sends Bob her measurement results (either “00”, “01”, “10”, or “11”), and Bob performs a correction operator based on Alice’s two bits of information on his part of the shared e-bit. Then, Bob's becomes .
# Alice sent the results to Bob. Bob applies correction
with qc.if_test((0, 1)):
qc.z(2)
with qc.if_test((1, 1)):
qc.x(2)
qc.barrier()
qc.draw(output="mpl")
Output:

You have completed a quantum teleportation circuit! Let's see the output state of this circuit using the statevector simulator.
from qiskit_aer import StatevectorSimulator
backend = StatevectorSimulator()
out_vector = backend.run(qc, shots=1).result().get_statevector() # set shots = 1
plot_bloch_multivector(out_vector)
Output:

You can see that the quantum state created by the -gate of qubit 0 (the qubit originally holding the secret state) has been transferred to qubit 2 (Bob's qubit).
You can run above cell a few times to make sure. You might notice that the qubits 0 & 1 change states, but qubit 2 is always in the state .
4.3 Execute it and confirm the result by applying U inverse
Above, we checked visually that the teleported state looked correct. Another way to check if the quantum state has been teleported correctly, is to apply the inverse of the gate on Bob's qubit so that we can measure '0'. That is, since is the identity, if Bob's qubit is in the state created from then applying the inverse should yield
# Apply the inverse of u-gate to measure |0>
qc.u(theta, varphi, 0.0, 2).inverse() # inverse of u(theta,varphi,0.0)
qc.measure(2, 2) # add measurement gate
qc.draw(output="mpl")
Output:

We will execute the circuit first using the AerSimulator, before moving on to a real quantum comptuer.
from qiskit_aer import AerSimulator
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Sampler
from qiskit.visualization import plot_histogram
# Define backend
backend = AerSimulator()
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job = sampler.run([isa_qc], shots=nshots)
result = job.result()
# Extract counts data
counts = result[0].data.c.get_counts()
print(counts)
# Plot the counts in a histogram
plot_histogram(counts)
Output:
{'011': 2510, '010': 2417, '000': 2635, '001': 2438}

Recall that in little endian notation, qubit 2 is the left-most (or bottom-most, in the column labels) qubit. Note that the left- and bottom-most qubit in the column labels is a 0 for all possible outcomes. This shows we have a 100% chance of measuring in the state . This is the expected result, and indicates the teleportation protocol has worked properly.
4.4 Teleportation on a real quantum computer
Next, we will perform teleportation on a real quantum computer. Using the dynamic circuit function, we can operate mid-circuit using measurement outcomes, implementing in real-time the conditionals operations in the teleportation circuit. For solving problems with real quantum computers, we will follow the four steps of Qiskit patterns.
- Map problem to quantum circuits and operators
- Optimize for target hardware
- Execute on target hardware
- Post-process the results
Exercise 3: Build the teleportation circuit
Try building the whole teleportation circuit from scratch to test your understanding. Scroll back up if you need a reminder.
Solution:
# Step 1: Map problem to quantum circuits and operators
# Create the circuit with 3-qubits and 1-bit
qc = QuantumCircuit(3, 3)
# Alice creates an unknown quantum state using the u-gate.
qc.u(theta, varphi, 0.0, 0)
qc.barrier() # for visual separation
# Eve creates EPR pair and sends q1 to Alice and q2 to Bob
##your code goes here##
qc.h(1)
qc.cx(1, 2)
qc.barrier()
# Alice entangles the unknown state with her EPR part, using the CNOT gate & H gate.
##your code goes here##
qc.cx(0, 1)
qc.h(0)
qc.barrier()
# Alice measures the two qubits.
##your code goes here##
qc.measure(0, 0)
qc.measure(1, 1)
# Alice sent the results to Bob. Now, Bob applies correction
##your code goes here##
with qc.if_test((0, 1)):
qc.z(2)
with qc.if_test((1, 1)):
qc.x(2)
qc.barrier()
# Apply the inverse of u-gate to measure |0>
qc.u(theta, varphi, 0.0, 2).inverse()
qc.measure(2, 2)
qc.draw(output="mpl")
Output:

As a reminder, applying the inverse of the gate is just so we can verify the expected behavior. It isn't part of sending the state to Bob, and we would not use that inverse gate if the only goal was to transfer quantum information.
Step 2: Optimize for target hardware
To run on hardware, import QiskitRuntimeService
and load your saved credentials. Select the backend with the fewest number of jobs in the queue.
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
service.backends()
Output:
[<IBMBackend('ibm_brisbane')>,
<IBMBackend('ibm_sherbrooke')>,
<IBMBackend('ibm_torino')>]
# You can also identify the least busy device
backend = service.least_busy(operational=True)
print("The least busy device is ", backend)
Output:
The least busy device is <IBMBackend('ibm_sherbrooke')>
# You can specify the device
# backend = service.backend('ibm_sherbrooke')
Let's see the coupling map of the device that you selected.
from qiskit.visualization import plot_gate_map
plot_gate_map(backend)
Output:

Different devices might have different coupling maps, and each device has some qubits and couplers that are more performant than others. Finally, different quantum computers might have different native gates (gates the hardware can execute). Transpiling the circuit rewrites the abstract quantum circuit using gates the target quantum computer can execute, and selects the optimal mapping to physical qubits (among other things). Transpilation is a rich and complicated topic. For more on transpilation, see the API reference.
# Step 2: Optimize for target hardware
# Transpile the circuit into basis gates executable on the hardware
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
qc_compiled = pm.run(qc)
qc_compiled.draw("mpl", idle_wires=False, fold=-1)
Output:

Step 3: Execute the circuit.
Using the Sampler
Runtime primitive, we will execute the target circuit.
# Step 3: Execute the target circuit
sampler = Sampler(backend)
job = sampler.run([qc_compiled])
job_id = job.job_id()
print("job id:", job_id)
Output:
job id: d13nkhpn2txg008jt0d0
# Check the job status
job.status()
Output:
'DONE'
You can also check the job status from your IBM Quantum dashboard.
# If the Notebook session got disconnected you can also check your job status by running the following code
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
job_real = service.job(job.job_id()) # Input your job-id between the quotations
job_real.status()
Output:
'DONE'
If you see 'DONE'
is displayed, you can get the result by executing below cell.
# Execute after 'DONE' is displayed
result_real = job_real.result()
print(result_real[0].data.c.get_counts())
Output:
{'001': 992, '110': 430, '011': 579, '010': 605, '111': 402, '000': 925, '100': 57, '101': 106}
Step 4: Post-process the results
# Step 4: Post-process the results
from qiskit.visualization import plot_histogram
plot_histogram(result_real[0].data.c.get_counts())
Output:

You can interpret the results above directly. Or, using marginal_count
, you can trace out Bob's results on qubit 2.
# trace out Bob's results on qubit 2
from qiskit.result import marginal_counts
bobs_qubit = 2
real_counts = result_real[0].data.c.get_counts()
bobs_counts = marginal_counts(real_counts, [bobs_qubit])
plot_histogram(bobs_counts)
Output:

As we see here, there are a few results in which we measured . These are due to noise and errors. In particular, dynamic circuits tend to have a higher error rate because of the time-consuming measurement in the middle of the circuit.
4.5 Key takeaways on quantum teleportation
We can transport a quantum state to a distant friend by sharing a pair of entangled qubits (an e-bit).
-
Can quantum teleportation send the quantum state faster than light? No, because Alice has to tell Bob the measurement results in a classical way.
-
Would quantum teleportation break the "no cloning theorem", which forbids copying of a quantum state? No, because the original quantum state given to Alice on one of her qubits was lost in measurement. It collapsed to a or .
5. Superdense coding
Almost the same setup can be used for a different purpose. Suppose Alice wants to send Bob two bits of classical information, but she has no means of classical communication with Bob. She does, however, share an entangled pair with Bob and she is allowed to send her qubit to Bob's location. Notice the contrast with the quantum teleportation protocol. In teleportation, classical communication was available to the friends, and the goal was to send a quantum state. Here, classical communication is not accessible and they use the transfer of a qubit to share two bits of classical information.
5.1 The protocol of superdense coding
Assumption: Alice has two bits of information, say, . Alice and Bob share an entangled pair (e-bit), but they cannot communicate classically.
- Alice performs one of the following operations on her part of e-bit.
- If , she does nothing
- If , she applies Z gate
- If , she applies X gate
- If , she applies Z gate and X gate.
- Alice sends her part of the e-bit to Bob's location.
- Bob applies a CNOT gate with the qubit from Alice as control and his qubit as target, then applies H gate to the qubit from Alice, and measures the two qubits. The possible starting states and results of Bob's operations are:
Please note that a negative sign of is global phase, so it is not measurable.
5.2 Quantum circuit simulating the superdense coding
Based on the protocol of superdense coding, you can build the superdense coding circuit as below. Please try to change the message, msg
, which Alice wants to transform to Bob.
from qiskit import QuantumCircuit
Qiskit pattern steps are identified in the code comments.
# Step 1: Map problem to quantum circuits and operators
# Create 2-qubits circuit
qc = QuantumCircuit(2, 2)
# Eve creates EPR pair and send q0 to Alice and q1 to Bob
qc.h(0)
qc.cx(0, 1)
qc.barrier()
# set message which Alice wants to transform to Bob
msg = "11" # You can change the message
if msg == "00":
pass
elif msg == "10":
qc.x(0)
elif msg == "01":
qc.z(0)
elif msg == "11":
qc.z(0)
qc.x(0)
qc.barrier()
# Bob receives EPR qubit from Alice and performs unitary operations
qc.cx(0, 1)
qc.h(0)
qc.barrier()
# Bob measures q0 and q1
qc.measure(0, 0)
qc.measure(1, 1)
qc.draw(output="mpl")
Output:

# We will execute on a simulator first
from qiskit_aer import AerSimulator
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import Sampler
# Define backend
backend = AerSimulator()
shots = 1000
# Transpile to backend
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_qc = pm.run(qc)
# Run the job
sampler = Sampler(mode=backend)
job_sim = sampler.run([isa_qc], shots=shots)
result_sim = job_sim.result()
# Extract counts data
counts = result_sim[0].data.c.get_counts()
print(counts)
Output:
{'11': 1000}
# Visualize the results
from qiskit.visualization import plot_histogram
plot_histogram(counts)
Output:

You can see that Bob received the message that Alice wanted to send to him.
Next, let's try it with a real quantum computer.
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
backend = service.least_busy(operational=True)
print("The least busy device is ", backend)
Output:
The least busy device is <IBMBackend('ibm_sherbrooke')>
# Step 1 was already completed before the simulator job above.
# Step 2: Optimize for target hardware
# Transpile the circuit into basis gates executable on the hardware
pm = generate_preset_pass_manager(backend=backend, optimization_level=2)
qc_compiled = pm.run(qc)
qc_compiled.draw("mpl", idle_wires=False)
Output:

# Step 3:Execute the target circuit
sampler = Sampler(backend)
job = sampler.run([qc_compiled])
job_id = job.job_id()
print("job id:", job_id)
Output:
job id: d13nnyq3grvg008j0zag
# Check the job status
job.status()
Output:
'DONE'
# If the Notebook session got disconnected you can also check your job status by running the following code
# from qiskit_ibm_runtime import QiskitRuntimeService
# service = QiskitRuntimeService()
job = service.job(job_id) # Input your job-id between the quotations
job.status()
Output:
'DONE'
# Execute after job has successfully run
real_result = job.result()
print(real_result[0].data.c.get_counts())
Output:
{'11': 3942, '01': 107, '10': 41, '00': 6}
# Step 4: Postprocess the results
from qiskit.visualization import plot_histogram
plot_histogram(real_result[0].data.c.get_counts())
Output:

The result is what we expected. Note that superdense coding on a real quantum computer showed fewer errors than in the case of quantum teleportation on a real quantum computer. One reason for this might be that quantum teleportation uses dynamic circuits, and superdense coding does not. We will learn more about errors in quantum circuits in later lessons.
6. Summary
In this session, we have implemented two quantum protocols. Although the scenarios for both involving distant friends are somewhat removed from quantum computing on a single QPU, they have applications in quantum computing, and help us understand the transfer of quantum information better.
- Quantum teleportation: Although we cannot copy quantum states, we can teleport unknown quantum states by having shared entanglement.
- Quantum superdense coding: A shared entangled pair, and transfer of one qubit, enable the communication of two bits of classical information.
# See the version of Qiskit
import qiskit
qiskit.__version__
Output:
'2.0.2'