Exploring uncertainty
For this Qiskit in Classrooms module, students must have a working Python environment with the following packages installed:
qiskit
v1.3 or newerqiskit-ibm-runtime
v0.29.0 or newerqiskit-aer
v15.0 or newerqiskit.visualization
To set up and install the packages above, see the Install Qiskit guide. In order to run jobs on real quantum computers, students will need to set up an account with IBM Quantum® by following the steps in the Set up your IBM Cloud account guide.
Click the image below to watch the module walkthrough by Dr. Katie McCormick.
(opens in a new tab)
Introduction
You have probably heard of the uncertainty principle, even outside of your physics courses. A common colloquial restatement of uncertainty is "By looking at something, you influence it." That is certainly true. But a more physical way of describing uncertainty is that there are certain physical observables that have an incompatibility that prevents them both from being simultaneously known to arbitrary accuracy. Many students first encounter the pair of incompatible variables and , meaning the position along one axis called the -axis, and the linear momentum along that direction, respectively. For those variables the constraint on uncertainty is written Here, is called the "uncertainty in ", which has the same definition as standard deviation in statistics, and can be defined as is defined in the same way. Here, we will not derive this uncertainty relation; we will point out that it is consistent with our understanding of classical waves. That is, a wave with truly one perfect frequency and wavelength would go on and on forever as a perfect sinusoid. Quantum mechanically, this would correspond to knowing the momentum perfectly according to de Broglie's hypothesis: . But in order to know a wave-like particle is located, the wave describing it must become more sharply peaked in space, like a very narrow Gaussian, for example. We know that we can express any continuous function, including such sharply-peaked wave functions, as a Fourier series of sinusoidal functions with different wavelengths. But as the wave function becomes more sharply peaked (and the position is better known), we will require more terms in the Fourier series, meaning a mixture of more wavelengths (and thus, quantum mechanically, more values of momentum).
Stated more simply: a state with a well-defined momentum (a perfect sinusoid in space) has very uncertain position. A state with a well-defined position (like a Dirac delta distribution) has a very uncertain momentum.
There are other variables that exhibit such incompatibility. For example, a particle's spin may have a well-defined projection along one axis, but then we know nothing about the project on an orthogonal axis. For example the state (for a qubit or spin-1/2 particle) has a definite projection along the axis (of 1 in the context of a qubit, and of in the context of a spin-1/2 particle). But this state can be written as a superposition of two states each of which has a well-defined projection onto the axis: or equivalently has a well-defined projection onto , as does . So if we specify the projection of a state along the axis, we do not know the projection along the axis. And if we specify the projection on the axis, we don't know the projection along . There are minor differences when discussing this in the context of spin and in qubits. But generally speaking, eigenstates of the Pauli matrices have an interesting relationship that we can explore. Throughout this lesson, we will be experimentally checking our intuition for the uncertainty in these incompatible variables, and verifying that uncertainty relations hold on IBM quantum computers.
Simple check of intuition
In this first experiment and throughout the module, we will use a framework for quantum computing known as "Qiskit patterns", which breaks workflows into the following steps:
- Step 1: Map classical inputs to a quantum problem
- Step 2: Optimize problem for quantum execution
- Step 3: Execute using Qiskit Runtime Primitives
- Step 4: Post-processing and classical analysis
We will generally follow these steps, though we may not always explicitly label them.
Let's start by loading some necessary packages including Runtime primitives. We will also select the least-busy quantum computer available to us.
from numpy import pi
# Load the Qiskit Runtime service
from qiskit_ibm_runtime import QiskitRuntimeService
# Syntax for first saving your token
# QiskitRuntimeService.save_account(channel="ibm_quantum", token="<MY_IBM_QUANTUM_TOKEN>", overwrite=True, set_as_default=True)
# service = QiskitRuntimeService(channel='ibm_quantum')
# Load saved credentials
service = QiskitRuntimeService()
# Load the Runtime primitive and session
from qiskit_ibm_runtime import (
Batch,
SamplerV2 as Sampler,
EstimatorV2 as Estimator,
)
# Use the least busy backend
backend = service.least_busy(min_num_qubits=127)
print(backend.name)
Output:
ibm_brisbane
If a student exhausts their available quantum computing time during the lesson, the lines below can be un-commented and used to set up a simulator that partly mimics the noise behavior of the quantum computer selected above.
# Import an estimator, this time from qiskit (we will import from Runtime for real hardware)
from qiskit.primitives import BackendSamplerV2
from qiskit.primitives import BackendEstimatorV2
sampler_sim = BackendSamplerV2(backend=backend)
estimator_sim = BackendEstimatorV2(backend=backend)
You might recall that an eigenstate of one operator, Z, is not an eigenstate of another operator X. We will observe that now, experimentally by making measurements along the and axes. For the measurement along , we simply use qc.measure()
, because IBM quantum computers are structured to measure along . But to measure along , we must rotate the system to effectively move the axis up to the orientation along which we measure. This is accomplished with a Hadamard gate. There is a similar step required for measurements along . The necessary steps are collected here for convenience:
- To measure along :
qc.measure()
- To measure along :
qc.h()
thenqc.measure()
- To measure along :
qc.sdg()
,qc.h()
,qc.s
thenqc.measure()
Step 1: Map classical inputs to a quantum problem
In this case, the mapping step is simply expressing the measurements and rotations descrive above in a quantum circuit:
# Step 1: Map
# Import some general packages
from qiskit import ClassicalRegister, QuantumCircuit, QuantumRegister
# Define registers
qr = QuantumRegister(1, "q")
cr = ClassicalRegister(2, "c")
qc = QuantumCircuit(qr, cr)
# Add a first measurement
qc.measure(qr, cr[0])
qc.barrier()
# Change basis so that measurements made on quantum computer which normally tell us about z, now tell us about x.
qc.h(qr)
# Add a second measurement
qc.measure(qr, cr[1])
qc.draw("mpl")
Output:

Step 2: Optimize problem for quantum execution
This step takes the operations we want to perform and expresses them in terms of the functionality of a specific quantum computer. It also maps our problem onto the layout of the quantum computer.
# Step 2: Transpile
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
target = backend.target
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
Step 3: Execute using Qiskit Runtime primitives
We can use sampler to collect statistics on the measurements. We will construct the Sampler primitive to run on a real quantum computer using mode = backend
. There are other modes for other workflows, and we will use one below. Sampler will be used by calling its run() method with a list of "pubs" (Primitive Unified Blocs). Each pub contains up to three values that, together, define a computation unit of work for the estimator to complete: circuits, observables, parameters. You can also provide a list of circuits, a list of observables, and a list of parameters. For more information, read the Overview of PUBs.
We want to run on a real quantum computer, so that we are carrying out a real quantum physics experiment. If you exhaust your allotted time on real quantum computers, you can comment out the code below for the quantum computer, and uncomment the code for running on a simulator.
# Step 3: Run the job on a real quantum computer
sampler = Sampler(mode=backend)
pubs = [qc_isa]
job = sampler.run(pubs)
res = job.result()
counts = res[0].data.c.get_counts()
# Run the job on the Aer simulator with noise model from real backend
# job = sampler_sim.run([qc_isa])
# counts=job.result()[0].data.c.get_counts()
Step 4: Post-processing
This is an especially simple case of post-processing, in which we simply visualize the counts.
Note that Qiskit orders qubits, measurements, and other things by listing the lowest-numbered item last / on the right, a convention referred to as "little-endian". This means that the column below labeled "10" refers to counts where the first measurement yielded a "0", and the second measurements yielded a "1".
# Step 4: Post-process
from qiskit.visualization import plot_histogram
plot_histogram(counts)
Output:

If this convention does not appeal to you, you can use marginal_counts
to visualize the results of each measurement separately:
from qiskit.result import marginal_counts
plot_histogram(
marginal_counts(counts, indices=[0]), title="Counts after first measurement"
)
Output:

plot_histogram(
marginal_counts(counts, indices=[1]), title="Counts after second measurement"
)
Output:

By default, states in Qiskit are initialized to the state. So it is no surprise that almost all of the first measurements yeilded . Note however, that there was almost an even split in the second measurement (the one giving information about projections of the state onto ). It seems like this state that gives us a very predictable outcome of measurements along gives us a very unpredictable set of outcomes for measurements along . What happens if the make the measurements in the opposite order? We could start by using the Hadamard gate to obtain statistics on the probability of being measured in . Then for the second measurement, we will change back to the basis using a second Hadamard gate.
# Step 1:
# Define registers
qr = QuantumRegister(1, "q")
cr = ClassicalRegister(2, "c")
qc = QuantumCircuit(qr, cr)
# Change basis to measure along x.
qc.h(qr)
qc.measure(qr, cr[0])
qc.barrier()
# Change our basis back to z and make a second measurement
qc.h(qr)
qc.measure(qr, cr[1])
qc.draw("mpl")
Output:

# Step 2: Transpile the circuit for running on a quantum computer
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
# Step 3: Run the job on a real quantum computer
sampler = Sampler(mode=backend)
pubs = [qc_isa]
job = sampler.run(pubs)
res = job.result()
counts = res[0].data.c.get_counts()
# Run the job on the Aer simulator with noise model from real backend
# job = sampler_sim.run([qc])
# counts = job.result()[0].data.c.get_counts()
# Step 4: Post-process
from qiskit.visualization import plot_histogram
plot_histogram(counts)
Output:

Here, we seem to have even less predictability! Previously, we at least knew what the outcome would be of the first measurement, now we have a fairly even distribution across all possible states. It's not too hard to see why this happened. We started in , which is a 50-50 mixture of and , according to So clearly there should be equal probability of obtaining the + or - state (mapped to 0 and 1 in the chart) for the first measurement. The measurement along collapses the state into either an eigenstate or the eigenstate . Each one of those states is a 50-50 mixture of and , according to So once the system is in an eigenstate of , clearly, measurements along will yield both and , and will do so with roughly equal probability. So our first example showed us that some states will have very predictable outcomes of some measurements, but unpredictable outcomes for other measurements. The current example shows us that we can do worse than that. There are states that can give us unpredictable outcomes for both measurements, even if all we do is swap the order of the measurements. Let's investigate how certain or uncertain a quantity is for a given state.
We can quantify this using uncertainty, or variance. The "uncertainty" is often defined to be the square root of the "variance" of a distribution. That is, the uncertainty for some observable is denoted and is given by
For the case of Pauli matrices, for which , this becomes
Let's apply this to a concrete example. Let's start with the state and let's determine the uncertainty of the observable in that state.
We can create an arbitrary initial state using qc.initialize()
. Note that the syntax for the imaginary unit here is .
Check-in question:
Calculate the uncertainty of in the state , by hand.
Answer:
In the given state, this yields:
# Step 1: Map the problem into a quantum circuit
from qiskit.quantum_info import SparsePauliOp
import numpy as np
obs = SparsePauliOp("X")
# Define registers
qr = QuantumRegister(1, "q")
cr = ClassicalRegister(1, "c")
qc = QuantumCircuit(qr, cr)
# Initialize the state
qc.initialize([1, 1j] / np.sqrt(2))
# Step 2: Transpile the circuit
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
obs_isa = obs.apply_layout(layout=qc_isa.layout)
# Step 3: Run the circuit on a real quantum computer
estimator = Estimator(mode=backend)
pubs = [(qc_isa, obs_isa)]
job = estimator.run([[qc_isa, obs_isa]])
res = job.result()
# Run the job on the Aer simulator with noise model from real backend
# job = estimator_sim.run([[qc_isa,obs_isa]])
# res=job.result()
# Step 4: Return the result in classical form, and analyze.
print(res[0].data.evs)
Output:
-0.026236000247509435
According to our equation above, Let's stick with that same state, but find the expectation value of , now:
# Step 1: Map the problem into a quantum circuit
obs = SparsePauliOp("Z")
# Define registers
qr = QuantumRegister(1, "q")
cr = ClassicalRegister(1, "c")
qc = QuantumCircuit(qr, cr)
# Initialize the state to |+>_y
qc.initialize([1, 1j] / np.sqrt(2))
# Step 2: Transpile the circuit
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
obs_isa = obs.apply_layout(layout=qc_isa.layout)
# Step 3: Run the circuit on a real quantum computer
estimator = Estimator(mode=backend)
pubs = [(qc_isa, obs_isa)]
job = estimator.run(pubs)
res = job.result()
# Run the job on the Aer simulator with noise model from real backend
# job = estimator_sim.run([[qc_isa,obs_isa]])
# res=job.result()
# Step 4: Return the result in classical form, and analyze.
print(res[0].data.evs)
Output:
0.04402399060161998
We could do the same math as before, but we would see that the variance is again very close to 1.0. We could conclude that . Indeed this approximately correct for the state we chose. But can we do better? Or worse?
Recall that there is an uncertainty relation between position along one direction, and momentum along the same direction, For those variables, but most familiar form is probably If this is all we remember, we might be tempted to think that and could also have such a fundamental limit on uncertainty. Perhaps it is impossible for the product to reach zero? Let's try another state and see if this holds. This time, we'll use Let's see what happens. Note that in the code below, estimator can accept two sets of circuits and observables in the same job submission.
# Step 1: Map the problem into a quantum circuit
obs1 = SparsePauliOp("X")
obs2 = SparsePauliOp("Z")
# Define registers
qr = QuantumRegister(1, "q")
cr = ClassicalRegister(1, "c")
qc = QuantumCircuit(qr, cr)
# Initialize the state
qc.initialize([1, 1] / np.sqrt(2))
# Step 2: Transpile the circuit
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
obs1_isa = obs1.apply_layout(layout=qc_isa.layout)
obs2_isa = obs2.apply_layout(layout=qc_isa.layout)
# Step 3: Run the circuit on a real quantum computer
with Batch(backend=backend) as batch:
estimator = Estimator(mode=batch)
pubs = [(qc_isa, obs1_isa), (qc_isa, obs2_isa)]
job = estimator.run(pubs)
res = job.result()
batch.close()
# Run the job on the Aer simulator with noise model from real backend
# job = estimator_sim.run([[qc_isa,obs1_isa],[qc_isa,obs2_isa]])
# res=job.result()
# Step 4: Return the result in classical form, and analyze.
print("The expectation value of the first observable is: ", res[0].data.evs)
print("The expectation value of the first observable is: ", res[1].data.evs)
Output:
The expectation value of the first observable is: 1.0092810295755477
The expectation value of the first observable is: -0.016334612052963743
The expectation value of should be close to 1.0, but should not exceed 1.0. Don't worry if it exceeds 1.0 by a very small amount. This can be attributed to factors like noise and/or readout error. Although this is a very important topic, we can ignore it for now.
We obtained an expectation value of that is very close to 1.0 (corresponding to a very low variance for ). This makes the product of the two variances quite low: While this is not exactly zero, this value is getting small in comparison with the eigenvalues of the Pauli operators (). Well, you may recall that the uncertainty relation between linear position and momentum could be written differently, explicitly using the commutation relation between the operators and : where is the commutator of and . This is the form that that can be most easily extended to the Pauli operators. Generally, for two operators and , And in the case of the Pauli matrices and , we need in order to calculate We show this here, and leave similar calculations to the reader as an exercise: This is a perfectly acceptable answer, but with one more step, we see Our uncertainty relation thus becomes
Check-in question
Determine and . Use this to write down the uncertainty relations between & , and & .
Answer:
Combining with the general uncertainty relation, we have
Verify consistency
Before moving on, let us check that this was consistent with our previous finding. We used the state And we found that Now we know this product should be greater than or equal to So indeed,
Use the questions below to build some intuition for these findings:
Check-in questions:
Answer the following items together as a set:
(a) What states would you expect to have zero uncertainty in ?
(b) What states would you expect to have zero uncertainty in ?
(c) In what states would you obtain a zero expectation value ?
(d) Are the answers to the above questions consistent with the case ?
(e) Write code to explicitly check this using estimator.
Answers:
(a) We might expect eigenstates of the operator to yield zero uncertainty in . Indeed, using we have
(b) We might expect eigenstates of the operator to yield zero uncertainty in . Indeed, using we have
(c) We expect to find for any states that, when measured, yield a positive projection on the axis just as often as a negative projection. These include the eigenstates of and .
(d) Yes. One would expect a very small value for the product of uncertainties for eigenstates of or : This can hold because we would also expect for those same states. So the uncertainty relation could be satisfied.
(e) Code such as the following would verify this:
obs1 = SparsePauliOp.from_list(
[("X", 1.000)]
)
obs2 = SparsePauliOp.from_list(
[("Y", 1.000)]
)
obs3 = SparsePauliOp.from_list(
[("Z", 1.000)]
)
qc = QuantumCircuit(1,1)
qc.ry(pi/2,0)
job = estimator.run([(qc, [[obs1], [obs2], [obs3]])], precision=0.001)
res=job.result()
Where the results returns all expectation values. To retrieve all expectation values and calculate uncertainties, we could use:
xs=res[0].data.evs[0]
ys=abs(res[0].data.evs[1])
zs=res[0].data.evs[2]
import math
prodxz=((1-xs[i]*xs[i])**0.5)*(1-zs[i]*zs[i])**0.5
Check-in questions:
Answer the following items together as a set:
(a) Can you think of a state in which you would have a large expectation value ?
(b) Would you expect that same state to have large or small uncertainty in ?
(c) Would you expect that same state to have large or small uncertainty in ?
(d) Are the answers to the above questions consistent with the case ?
(e) Write code to explicitly check this using estimator.
Answers:
(a) We expect to find for the eigenstate of : .
(b) We might expect to have large uncertainty in the state since measuring in that state would yield a positive and negative result with equal frequency/probability.
(c) We might expect to have large uncertainty in the state since measuring in that state would yield a positive and negative result with equal frequency/probability.
(d) Yes. One would expect a large value for the product of uncertainties for eigenstates of and for specifically. We would also expect for that same state. So both and are both fairly large in this state, and it is plausible that the uncertainty relation could again be satisfied.
(e) Code such as the following would verify this:
obs1 = SparsePauliOp.from_list(
[("X", 1.000)]
)
obs2 = SparsePauliOp.from_list(
[("Y", 1.000)]
)
obs3 = SparsePauliOp.from_list(
[("Z", 1.000)]
)
qc = QuantumCircuit(1,1)
qc.rx(-pi/2,0)
job = estimator.run([(qc, [[obs1], [obs2], [obs3]])], precision=0.001)
res=job.result()
Where the results returns all expectation values. To retrieve all expectation values and calculate uncertainties, we could use:
xs=res[0].data.evs[0]
ys=abs(res[0].data.evs[1])
zs=res[0].data.evs[2]
import math
prodxz=((1-xs[i]*xs[i])**0.5)*(1-zs[i]*zs[i])**0.5
The test above only demonstrated the validity of the uncertainty relation for a single choice of state vector . To convince ourselves that this is generally consistent with experiment, we should carry out similar calculations using estimator for many choices of the state vector. Let's start by rotating our state vector away from the axis, using an RY
gate to produce different initial states using a parameter .
# Step 1: Map the problem into a quantum circuit
from qiskit.circuit import Parameter
import numpy as np
# Specify observables
obs1 = SparsePauliOp("X")
obs2 = SparsePauliOp("Y")
obs3 = SparsePauliOp("Z")
# Define registers
qr = QuantumRegister(1, "q")
cr = ClassicalRegister(1, "c")
qc = QuantumCircuit(qr, cr)
# Rotate away from |0>
theta = Parameter("θ")
qc.ry(theta, 0)
params = np.linspace(0, 2, num=21)
# Step 2: Transpile the circuit
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
obs1_isa = obs1.apply_layout(layout=qc_isa.layout)
obs2_isa = obs2.apply_layout(layout=qc_isa.layout)
obs3_isa = obs3.apply_layout(layout=qc_isa.layout)
# Step 3: Run the circuit on a real quantum computer
with Batch(backend=backend) as batch:
estimator = Estimator(mode=batch)
pubs = [(qc_isa, [[obs1_isa], [obs2_isa], [obs3_isa]], [params])]
job = estimator.run(pubs, precision=0.01)
res = job.result()
# batch.close()
# Run the job on the Aer simulator with noise model from real backend
# job = estimator_sim.run([(qc_isa, [[obs1_isa], [obs2_isa], [obs3_isa]], [params])], precision=0.01)
# res=job.result()
# Step 4: Post-processing and classical analysis.
xs = res[0].data.evs[0]
ys = abs(res[0].data.evs[1])
zs = res[0].data.evs[2]
# Calculate uncertainties
delx = []
delz = []
prodxz = []
for i in range(len(xs)):
delx.append(abs((1 - xs[i] * xs[i])) ** 0.5)
delz.append(abs((1 - zs[i] * zs[i])) ** 0.5)
prodxz.append(delx[i] * delz[i])
# Here we can plot the results from this simulation.
import matplotlib.pyplot as plt
plt.plot(params, delx, label=r"$\Delta$ X")
plt.plot(params, ys, label=r"$\langle$ Y $\rangle$")
plt.plot(params, delz, label=r"$\Delta$ Z")
plt.plot(params, prodxz, label=r"$\Delta$X $\Delta$Z")
plt.xlabel(r"$\theta$")
plt.ylabel("Expectation/Uncertainty Values")
plt.legend()
plt.show()
Output:

Note that the red curve is always greater than the orange curve Sometimes the uncertainty product dips and is somewhat close to the limit, and other times it rises and is farther from the limit, but it always obeys the uncertainty relation.
Of course, this might not be the best test of the uncertainty relation, since our limit is always very close to zero. Let's use a quantum state that has a larger projection on eigenstates of . Specifically, we will still rotate rotate down from the axis by varying angles, but now we will also rotate that resulting state around by some angle, perhaps , and see what happens.
from qiskit.circuit import Parameter
import numpy as np
# Step 1: Map the problem to a quantum circuit
# Specify observables
obs1 = SparsePauliOp("X")
obs2 = SparsePauliOp("Y")
obs3 = SparsePauliOp("Z")
# Define registers
qr = QuantumRegister(1, "q")
cr = ClassicalRegister(1, "c")
qc = QuantumCircuit(qr, cr)
# Rotate away from |0> along one plane, and then along a transverse direction.
theta = Parameter("θ")
qc.ry(theta, 0)
qc.rz(pi / 4, 0)
params = np.linspace(0, 2, num=21)
# Step 2: Transpile the circuit
pm = generate_preset_pass_manager(target=target, optimization_level=3)
qc_isa = pm.run(qc)
obs1_isa = obs1.apply_layout(layout=qc_isa.layout)
obs2_isa = obs2.apply_layout(layout=qc_isa.layout)
obs3_isa = obs3.apply_layout(layout=qc_isa.layout)
# Step 3: Run the circuit on a real quantum computer
with Batch(backend=backend) as batch:
estimator = Estimator(mode=batch)
pubs = [(qc_isa, [[obs1_isa], [obs2_isa], [obs3_isa]], [params])]
job = estimator.run(pubs, precision=0.01)
res = job.result()
batch.close()
# Run the job on the Aer simulator with noise model from real backend
# job = estimator_sim.run([(qc_isa, [[obs1_isa], [obs2_isa], [obs3_isa]], [params])], precision=0.01)
# res=job.result()
# Step 4: Post-processing and classical analysis.
xs = res[0].data.evs[0]
ys = abs(res[0].data.evs[1])
zs = res[0].data.evs[2]
# Calculate uncertainties
delx = []
delz = []
prodxz = []
for i in range(len(xs)):
delx.append(abs((1 - xs[i] * xs[i])) ** 0.5)
delz.append(abs((1 - zs[i] * zs[i])) ** 0.5)
prodxz.append(delx[i] * delz[i])
# Here we can plot the results from this simulation.
import matplotlib.pyplot as plt
plt.plot(params, delx, label=r"$\Delta$ X")
plt.plot(params, ys, label=r"$\langle$ Y $\rangle$")
plt.plot(params, delz, label=r"$\Delta$ Z")
plt.plot(params, prodxz, label=r"$\Delta$X $\Delta$Z")
plt.xlabel(r"$\theta$")
plt.ylabel("Expectation/Uncertainty Values")
plt.legend()
plt.show()
Output:

Now we see that the limit on the uncertainty is being put to the test! The red curve comes much closer to the orange curve than before. In fact, in the absence of noise, the uncertainty relation would be exactly saturated () one point. In the presence of noise and readout error, it should not be surprising if a run occassionally yields very slightly larger than This is not a true violation of uncertainty; it is just an artifact of non-zero error.
Check-in question:
Explain how you would push this to the absolute limit, making as large as possible?
Answer:
The code currently has lines that rotate the default initial state , down from the axis by some parametrized angle and then also around the axis by an angle which rotates the state vector part of the way over to the axis.
qc.ry(theta,0)
qc.rz(pi/4,0)
We could change the rotation about from to , rotating all the way to an eigenstate of :
qc.ry(theta,0)
qc.rz(pi/2,0)
No other changes would be required.
Check-in question:
Change the code or copy it over and implement that check of the uncertainty relation with the expectation value of Y maximized. Does the uncertainty relation hold?
We would use exactly the code from the example above, with
qc.rz(pi/2,0)
swapped in to replace
qc.rz(pi/4,0).
The resulting figure should look like that below, and yes, the uncertainty principle should still be valid.

Check-in question:
Modify the code above to make a similar picture, demonstrating that from measurements on the quantum computer the product behaves as it should. Choose any set of states you like.
We would use exactly the code from the example above, and in face we could use the same results from above, just using the expectation values to calculate different uncertainties. For example, we could use
xs=res[0].data.evs[0]
ys=res[0].data.evs[1]
zs=abs(res[0].data.evs[2])
import math
delx = []
dely = []
prodxy=[]
for i in range(len(xs)):
delx.append((1-xs[i]*xs[i])**0.5)
dely.append((1-ys[i]*ys[i])**0.5)
prodxy.append(((1-xs[i]*xs[i])**0.5)*(1-ys[i]*ys[i])**0.5)
and we could plot
import matplotlib.pyplot as plt
plt.plot(params, delx, label=r'$\Delta$ X')
plt.plot(params, dely, label=r'$\langle$ Y $\rangle$')
plt.plot(params, zs, label=r'$\Delta$ Z')
plt.plot(params, prodxy, label=r'$\Delta$X $\Delta$Z')
plt.xlabel(r'$\theta$')
plt.ylabel('Expectation/Uncertainty Values')
plt.legend()
plt.show()
Challenge: Write code to scan through many values of , just as we scanned through many values of , and make a 3-D plot showing that the uncertainty relation is never violated. Pick any observables you like.
Critical concepts:
- There are uncertainty relations between many sets of physical observables, including position & linear momentum, and components of spin.
- The Pauli matrices do not commute. This is a mathematical reflection of the fact that not all components of spin can be simultaneously known/determined.
- Quantum computing makes heavy use of the Pauli operators/matrices, so it is useful to know the uncertainty relation for Pauli operators, as well as the closely-related spin operators.
- A general formula for the uncertainty of two operators and is
- An eigenstate of some operator yields zero uncertainty in the physical observable associated with that operator. Even experimentally,
- An eigenstate of some operator will yield a larger uncertainty for an operator that does not commute with .
- Experimental results using a real quantum computer confirm the intuition we gain from matrix representations of physical operators.
Questions
T/F Questions:
- T/F One can simultaneously measure and , but not .
- T/F One can simultaneously measure and , but not .
- T/F Linear position and linear momentum operators do not commute.
- T/F IBM quantum computers measure along by default, so a rotation must be performed to measure along any other direction.
- T/F The circuit below effectively measures and then .

MC Questions:
-
The diagram below demonstrates which of the following uncertainty relations?
- a.
- b.
- c.
- d. None of the above

-
Which of the following is the standard sequence to perform a measurement along ?
- a. Only
qc.measure()
- b.
qc.h()
thenqc.measure()
- c.
qc.h()
,qc.h()
thenqc.measure()
- d.
qc.h()
,qc.s
,qc.h()
thenqc.measure()
- e.
qc.sdg()
,qc.h()
,qc.s
thenqc.measure()
- f.
qc.sdg()
,qc.h()
,qc.s
,qc.h()
thenqc.measure()
- a. Only
-
Which of the following states yields the largest expectation value ?
- a.
- b.
- c. also called
- d. also called
- e. also called
- f. also called
-
Which of the following states yields the largest uncertainty ?
- a.
- b. also called
- c. also called
- d. a and b are tied
- e. b and c are tied
- f. a, b, and c are tied
Discussion Questions:
-
Does this concept of uncertainty conflict in any way with the notion of spin as a vector arrow in Cartesian space? How about on the Bloch sphere?
-
Suppose you orient a measurement device along a direction half-way between the and axes. What happens? Can you make a measurement along this direction? How does this relate to uncertainty in and ?
-
What additional experiments would you like to do to convince yourself of the results obtained here?