This example walks through the steps of running the quantum volume algorithm on square matrices. It is intended to mirror Algorithm 1 of https://arxiv.org/pdf/1811.12926.pdf. In general, we will generate a model circuit, classically compute its Heavy Output Group, then run various samplers (currently, ideal and noisy simulators) to evaluate how often they generate Heavy results.
In [ ]:
# This cell sets up the parameters for the quantum volume algorithm.
# Feel free to mess with these!
import cirq
num_repetitions = 10 # This is supposed to be >= 100.
depths = range(2, 5) # The depths and number of qubits
optimize = lambda circuit: cirq.google.optimized_for_xmon(
circuit=circuit,
new_device=cirq.google.Bristlecone)
device = cirq.google.Bristlecone
# Here is the important set-up: the samplers and their plot configurations.
# These are what will be run on the generated circuit, and then evaluated.
samplers = [{
'label': 'Ideal simulation',
'sampler': cirq.Simulator(),
'marker': '+',
'color': 'tab:green',
}, {
'label': 'Noisy simulation',
'sampler':
cirq.DensityMatrixSimulator(noise=cirq.ConstantQubitNoiseModel(
qubit_noise_gate=cirq.DepolarizingChannel(p=0.005))),
'color': 'tab:red',
'marker': 'o',
}]
print(f"Configuration: depths from {depths[0]} to {depths[-1]} with "
f"{num_repetitions} runs of {len(samplers)} samplers")
In [ ]:
# This cell contains the business logic that actually runs the quantum volume algorithm with
# parameters specified in the previous cell.
from cirq.contrib import quantum_volume
from collections import defaultdict
import numpy as np
for sampler in samplers:
sampler['probabilities'] = defaultdict(int)
sampler['routed-probabilities'] = defaultdict(int)
sampler['compiled-probabilities'] = defaultdict(int)
for depth in depths:
num_qubits = depth # Square matrix.
print(f"Running simulation with {num_qubits} qubits and a depth of {depth}")
for i in range(num_repetitions):
print(f" Repetition {i + 1} of {num_repetitions}")
# Generate a model circuit and compute its heavy set.
model_circuit = quantum_volume.generate_model_circuit(
num_qubits, depth, random_state=np.random.RandomState())
heavy_set = quantum_volume.compute_heavy_set(model_circuit)
print(f" Heavy Set: {heavy_set}")
# Route and compile the model circuit.
[routed_circuit,
mapping] = quantum_volume.compile_circuit(model_circuit, device=device, routing_attempts=1)
compiled_circuit = optimize(routed_circuit)
# Run the given samplers over the model, compiled, and optimized circuits.
for sampler in samplers:
probability = quantum_volume.sample_heavy_set(
model_circuit, heavy_set, sampler=sampler['sampler'])
sampler['probabilities'][depth] += probability
print(f" {sampler['label']} HOG probability: {probability}")
routed_probability = quantum_volume.sample_heavy_set(
routed_circuit,
heavy_set,
sampler=sampler['sampler'],
mapping=mapping)
sampler['routed-probabilities'][depth] += routed_probability
print(
f" {sampler['label']} HOG routed probability: {routed_probability}"
)
compiled_probability = quantum_volume.sample_heavy_set(
compiled_circuit,
heavy_set,
sampler=sampler['sampler'],
mapping=mapping)
sampler['compiled-probabilities'][depth] += compiled_probability
print(
f" {sampler['label']} HOG compiled probability: {compiled_probability}"
)
# Compute the average performance over the total number of runs.
for sampler in samplers:
sampler['probabilities'][depth] /= num_repetitions
sampler['routed-probabilities'][depth] /= num_repetitions
sampler['compiled-probabilities'][depth] /= num_repetitions
print(f" Average {sampler['label']} HOG probability: "
f"{sampler['probabilities'][depth]}")
print(f" Average {sampler['label']} optimized HOG probability: "
f"{sampler['routed-probabilities'][depth]}")
print(f" Average {sampler['label']} compiled HOG probability: "
f"{sampler['compiled-probabilities'][depth]}")
In [ ]:
# Create a chart that is designed to look as similar as possible to
# Figure 2 in https://arxiv.org/pdf/1811.12926.pdf.
from matplotlib import pyplot as plt
fig, axs = plt.subplots()
for idx, sampler in enumerate(samplers):
axs.scatter([d + idx / 10 for d in depths],
sampler['probabilities'].values(),
marker='+',
c=f"{sampler['color']}",
label=f"{sampler['label']}")
axs.scatter([d + idx / 10 for d in depths],
sampler['routed-probabilities'].values(),
marker="o",
c=f"{sampler['color']}",
label=f"{sampler['label']} Routed")
axs.scatter([d + idx / 10 for d in depths],
sampler['compiled-probabilities'].values(),
marker="s",
c=f"{sampler['color']}",
label=f"{sampler['label']} Compiled")
# Line markers for asymptotic ideal heavy output probability and the ideal
# Heavy Output Generation threshold.
axs.axhline((1 + np.log(2)) / 2,
color='tab:green',
label='Asymptotic ideal',
linestyle='dashed')
axs.axhline(2 / 3, label='HOG threshold', color='k', linestyle='dotted')
# Making the plot look consistent.
axs.set_ybound(0.0, 1)
axs.xaxis.set_major_locator(plt.MultipleLocator(1))
axs.set_xlabel("width/depth of model circuit m=d")
axs.set_ylabel("est. heavy output probability h(d)")
fig.suptitle('Experimental data for square quantum volume circuits')
axs.legend(loc='best')