In [0]:
%%capture
!pip install --quiet --upgrade pip
!pip install --quiet cirq==0.7
In this experiment, you are going to use Cirq to check that rotating a qubit by an increasing angle, and then measuring the qubit, produces Rabi oscillations. This requires you to do the following things:
Cirq emphasizes the details of implementing quantum algorithms on near term devices. For example, when you work on a qubit in Cirq you don't operate on an unspecified qubit that will later be mapped onto a device by a hidden step. Instead, you are always operating on specific qubits at specific locations that you specify.
Suppose you are working with a 54 qubit Sycamore chip.
This device is included in Cirq by default.
It is called cirq.google.Sycamore
, and you can see its layout by printing it.
In [2]:
import cirq
working_device = cirq.google.Sycamore
print(working_device)
For this experiment you only need one qubit and you can just pick whichever one you like.
In [0]:
my_qubit = cirq.GridQubit(5, 6)
Once you've chosen your qubit you can build circuits that use it.
In [4]:
from cirq.contrib.svg import SVGCircuit
# Create a circuit with X, Ry(pi/2) and H.
my_circuit = cirq.Circuit(
# Rotate the qubit pi/2 radians around the X axis.
cirq.rx(3.141 / 2).on(my_qubit),
# Measure the qubit.
cirq.measure(my_qubit, key='out')
)
SVGCircuit(my_circuit)
Out[4]:
Now you can simulate sampling from your circuit using cirq.Simulator
.
In [5]:
sim = cirq.Simulator()
samples = sim.sample(my_circuit, repetitions=10)
samples
Out[5]:
You can also get properties of the circuit, such as the density matrix of the circuit's output or the state vector just before the terminal measurement.
In [6]:
state_vector_before_measurement = sim.simulate(my_circuit[:-1])
sampled_state_vector_after_measurement = sim.simulate(my_circuit)
print(f'State before measurement:')
print(state_vector_before_measurement)
print(f'State after measurement:')
print(sampled_state_vector_after_measurement)
You can also examine the outputs from a noisy environment. For example, an environment where 10% depolarization is applied to each qubit after each operation in the circuit:
In [7]:
noisy_sim = cirq.DensityMatrixSimulator(noise=cirq.depolarize(0.1))
noisy_post_measurement_state = noisy_sim.simulate(my_circuit)
noisy_pre_measurement_state = noisy_sim.simulate(my_circuit[:-1])
print('Noisy state after measurement:' + str(noisy_post_measurement_state))
print('Noisy state before measurement:' + str(noisy_pre_measurement_state))
In [8]:
import sympy
theta = sympy.Symbol('theta')
parameterized_circuit = cirq.Circuit(
cirq.rx(theta).on(my_qubit),
cirq.measure(my_qubit, key='out')
)
SVGCircuit(parameterized_circuit)
Out[8]:
In the above block you saw that there is a sympy.Symbol
that you placed in the circuit. Cirq supports symbolic computation involving circuits. What this means is that when you construct cirq.Circuit
objects you can put placeholders in many of the classical control parameters of the circuit which you can fill with values later on.
Now if you wanted to use cirq.simulate
or cirq.sample
with the parameterized circuit you would also need to specify a value for theta
.
In [9]:
samples_at_theta_equals_2 = sim.sample(
parameterized_circuit,
params={theta: 2},
repetitions=10)
samples_at_theta_equals_2
Out[9]:
You can also specify multiple values of theta
, and get samples back for each value.
In [10]:
samples_at_multiple_theta = sim.sample(
parameterized_circuit,
params=[{theta: 0.5}, {theta: 3.141}],
repetitions=10)
samples_at_multiple_theta
Out[10]:
Cirq has shorthand notation you can use to sweep theta
over a range of values.
In [11]:
samples_at_swept_theta = sim.sample(
parameterized_circuit,
params=cirq.Linspace(theta, start=0, stop=3.14159, length=5),
repetitions=5)
samples_at_swept_theta
Out[11]:
The result value being returned by sim.sample
is a pandas.DataFrame
object.
Pandas is a common library for working with table data in python.
You can use standard pandas methods to analyze and summarize your results.
In [12]:
import pandas
big_results = sim.sample(
parameterized_circuit,
params=cirq.Linspace(theta, start=0, stop=3.14159, length=20),
repetitions=10_000)
# big_results is too big to look at. Plot cross tabulated data instead.
pandas.crosstab(big_results.theta, big_results.out).plot()
Out[12]:
Cirq comes with a pre-written Rabi oscillation experiment cirq.experiments.rabi_oscillations
.
This method takes a cirq.Sampler
, which could be a simulator or a network connection to real hardware.
The method takes a few more experimental parameters, and returns a result object
that can be plotted.
In [13]:
import datetime
result = cirq.experiments.rabi_oscillations(
sampler=noisy_sim,
qubit=my_qubit,
num_points=50,
repetitions=10000)
result.plot()
Out[13]:
Notice that you can tell from the plot that you used the noisy simulator you defined earlier. You can also tell that the amount of depolarization is roughly 10%.
As you have seen, you can use Cirq to perform a Rabi oscillation experiment. You can either make the experiment yourself out of the basic pieces made available by Cirq, or use the prebuilt experiment method.
Now you're going to put this knowledge to the test.
There is some amount of depolarizing noise on each qubit. Your goal is to characterize every qubit from the Sycamore chip using a Rabi oscillation experiment, and find the qubit with the lowest noise according to the secret noise model.
In [0]:
import hashlib
class SecretNoiseModel(cirq.NoiseModel):
def noisy_operation(self, op):
# Hey! No peeking!
q = op.qubits[0]
v = hashlib.sha256(str(q).encode()).digest()[0] / 256
yield cirq.depolarize(v).on(q)
yield op
secret_noise_sampler = cirq.DensityMatrixSimulator(noise=SecretNoiseModel())
In [15]:
q = cirq.google.Sycamore.qubits[3]
print('qubit', repr(q))
cirq.experiments.rabi_oscillations(
sampler=secret_noise_sampler,
qubit=q
).plot()
Out[15]:
In [0]: