Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

XEB fidelity estimator for large circuits #2114

Merged
merged 23 commits into from
Sep 23, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
)

from cirq.experiments import (
estimate_circuit_fidelity,
generate_supremacy_circuit_google_v2,
generate_supremacy_circuit_google_v2_bristlecone,
generate_supremacy_circuit_google_v2_grid,
Expand Down
3 changes: 3 additions & 0 deletions cirq/experiments/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,6 @@
build_entangling_layers,
cross_entropy_benchmarking,
)

from cirq.experiments.fidelity_estimation import (
estimate_circuit_fidelity,)
viathor marked this conversation as resolved.
Show resolved Hide resolved
51 changes: 51 additions & 0 deletions cirq/experiments/fidelity_estimation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Estimate fidelity of large random quantum circuit from observed bitstrings.

Fidelity estimator defined here is used in cross-entropy benchmarking and
works under the assumption that the evaluated circuit is sufficiently
random, see https://arxiv.org/abs/1608.00263.
"""

from typing import cast, List, Sequence, Tuple

import numpy as np

from cirq.circuits import Circuit
from cirq.ops import Qid
from cirq.sim import Simulator, WaveFunctionTrialResult


def estimate_circuit_fidelity(circuit: Circuit, qubit_order: Sequence[Qid],
bitstrings: Sequence[int]) -> float:
viathor marked this conversation as resolved.
Show resolved Hide resolved
"""Computes fidelity estimate from one circuit using linear XEB estimator.

Args:
circuit: Random quantum circuit which has been executed on quantum
viathor marked this conversation as resolved.
Show resolved Hide resolved
processor under test
qubit_order: Qubit order used to construct bitstrings from measurements
viathor marked this conversation as resolved.
Show resolved Hide resolved
bitstrings: Results of terminal all-qubit measurements performed after
viathor marked this conversation as resolved.
Show resolved Hide resolved
viathor marked this conversation as resolved.
Show resolved Hide resolved
each circuit execution
Returns:
Estimate of circuit fidelity.
viathor marked this conversation as resolved.
Show resolved Hide resolved
Raises:
ValueError: Circuit is inconsistent with qubit order or one of the
bitstrings is inconsistent with the number of qubits.
"""
dim = 2**len(qubit_order)
viathor marked this conversation as resolved.
Show resolved Hide resolved

if set(qubit_order) != circuit.all_qubits():
raise ValueError(
f'Inconsistent qubits: circuit has {circuit.all_qubits()}, '
f'qubit order is {qubit_order}')
for bitstring in bitstrings:
if bitstring < 0 or dim <= bitstring:
viathor marked this conversation as resolved.
Show resolved Hide resolved
raise ValueError(
f'Bitstring {bitstring} could not have been observed '
f'on {len(qubit_order)} qubits.')

simulator = Simulator()
result = cast(WaveFunctionTrialResult,
viathor marked this conversation as resolved.
Show resolved Hide resolved
simulator.simulate(circuit, qubit_order=qubit_order))
viathor marked this conversation as resolved.
Show resolved Hide resolved
output_probabilities = np.abs(result.final_state)**2
assert 1 - 1e-4 < np.sum(output_probabilities) < 1 + 1e-4
viathor marked this conversation as resolved.
Show resolved Hide resolved
fidelity_estimate = dim * np.mean(output_probabilities[bitstrings]) - 1
return fidelity_estimate
59 changes: 59 additions & 0 deletions cirq/experiments/fidelity_estimation_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from typing import Sequence

import numpy as np
import pytest

import cirq


def make_bitstrings(samples: np.ndarray) -> Sequence[int]:
assert samples.shape[1] == 2
return [2 * b1 + b0 for b1, b0 in samples]


def sample_noisy_bitstrings(circuit: cirq.Circuit, depolarization: float,
n_samples: int) -> Sequence[int]:
assert 0 <= depolarization <= 1
n_incoherent = int(depolarization * n_samples)
n_coherent = n_samples - n_incoherent
incoherent_samples = np.random.randint(2, size=(n_incoherent, 2))
if n_coherent > 0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this optimize belongs inside sim.run instead of outside of it. Also this is test code; don't worry about it.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed #2154.

sim = cirq.Simulator()
r = sim.run(circuit, repetitions=n_coherent)
coherent_samples = r.measurements['0,1']
viathor marked this conversation as resolved.
Show resolved Hide resolved
all_samples = np.concatenate((coherent_samples, incoherent_samples))
return make_bitstrings(all_samples)
return make_bitstrings(incoherent_samples)


@pytest.mark.parametrize('depolarization', (0, 0.25, 0.5, 0.75, 1.0))
def test_estimate_circuit_fidelity(depolarization):
prng_state = np.random.get_state()
np.random.seed(0)

q0, q1 = cirq.LineQubit.range(2)
circuit = cirq.Circuit.from_ops(cirq.H(q0), cirq.CNOT(q0, q1),
cirq.measure(q0, q1))
bitstrings = sample_noisy_bitstrings(circuit, depolarization, 10000)
f = cirq.estimate_circuit_fidelity(circuit, (q0, q1), bitstrings)
assert np.isclose(f, 1 - depolarization, atol=0.026)

np.random.set_state(prng_state)


def test_estimate_circuit_fidelity_invalid_qubits():
q0, q1, q2 = cirq.LineQubit.range(3)
circuit = cirq.Circuit.from_ops(cirq.H(q0), cirq.CNOT(q0, q1),
cirq.measure(q0, q1))
bitstrings = sample_noisy_bitstrings(circuit, 0.9, 10)
with pytest.raises(ValueError):
cirq.estimate_circuit_fidelity(circuit, (q0, q1, q2), bitstrings)


def test_estimate_circuit_fidelity_invalid_bitstrings():
q0, q1 = cirq.LineQubit.range(2)
circuit = cirq.Circuit.from_ops(cirq.H(q0), cirq.CNOT(q0, q1),
cirq.measure(q0, q1))
bitstrings = [0, 1, 2, 3, 4]
with pytest.raises(ValueError):
cirq.estimate_circuit_fidelity(circuit, (q0, q1), bitstrings)