Skip to content

Commit

Permalink
Add single qubit readout calibration (#2532)
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinsung authored and CirqBot committed Nov 15, 2019
1 parent bb0afb3 commit dac7814
Show file tree
Hide file tree
Showing 8 changed files with 239 additions and 0 deletions.
1 change: 1 addition & 0 deletions cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
)

from cirq.experiments import (
estimate_single_qubit_readout_errors,
hog_score_xeb_fidelity_from_probabilities,
linear_xeb_fidelity,
linear_xeb_fidelity_from_probabilities,
Expand Down
5 changes: 5 additions & 0 deletions cirq/experiments/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@
xeb_fidelity,
)

from cirq.experiments.single_qubit_readout_calibration import (
estimate_single_qubit_readout_errors,
SingleQubitReadoutCalibrationResult,
)

from cirq.experiments.t1_decay_experiment import (
t1_decay,
T1DecayResult,
Expand Down
116 changes: 116 additions & 0 deletions cirq/experiments/single_qubit_readout_calibration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Copyright 2019 The Cirq Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import Dict, Iterable, TYPE_CHECKING

import dataclasses
import time

import numpy as np

from cirq import circuits, ops

if TYPE_CHECKING:
import cirq


@dataclasses.dataclass(frozen=True)
class SingleQubitReadoutCalibrationResult:
"""Result of estimating single qubit readout error.
Attributes:
zero_state_errors: A dictionary from qubit to probability of measuring
a 1 when the qubit is initialized to |0⟩.
one_state_errors: A dictionary from qubit to probability of measuring
a 0 when the qubit is initialized to |1⟩.
repetitions: The number of repetitions that were used to estimate the
probabilities.
timestamp: The time the data was taken, in seconds since the epoch.
"""
zero_state_errors: Dict['cirq.Qid', float]
one_state_errors: Dict['cirq.Qid', float]
repetitions: int
timestamp: float

def _json_dict_(self):
return {
'cirq_type': self.__class__.__name__,
'zero_state_errors': list(self.zero_state_errors.items()),
'one_state_errors': list(self.one_state_errors.items()),
'repetitions': self.repetitions,
'timestamp': self.timestamp
}

@classmethod
def _from_json_dict_(cls, zero_state_errors, one_state_errors, repetitions,
timestamp, **kwargs):
return cls(zero_state_errors=dict(zero_state_errors),
one_state_errors=dict(one_state_errors),
repetitions=repetitions,
timestamp=timestamp)

def __repr__(self):
return ('cirq.experiments.SingleQubitReadoutCalibrationResult('
f'zero_state_errors={self.zero_state_errors!r}, '
f'one_state_errors={self.one_state_errors!r}, '
f'repetitions={self.repetitions!r}, '
f'timestamp={self.timestamp!r})')


def estimate_single_qubit_readout_errors(
sampler: 'cirq.Sampler',
*,
qubits: Iterable['cirq.Qid'],
repetitions: int = 1000) -> SingleQubitReadoutCalibrationResult:
"""Estimate single-qubit readout error.
For each qubit, prepare the |0⟩ state and measure. Calculate how often a 1
is measured. Also, prepare the |1⟩ state and calculate how often a 0 is
measured. The state preparations and measurements are done in parallel,
i.e., for the first experiment, we actually prepare every qubit in the |0⟩
state and measure them simultaneously.
Args:
sampler: The quantum engine or simulator to run the circuits.
qubits: The qubits being tested.
repetitions: The number of measurement repetitions to perform.
Returns:
A SingleQubitReadoutCalibrationResult storing the readout error
probabilities as well as the number of repetitions used to estimate
the probabilties. Also stores a timestamp indicating the time when
data was finished being collected from the sampler.
"""
qubits = list(qubits)

zeros_circuit = circuits.Circuit(ops.measure_each(*qubits, key_func=repr))
ones_circuit = circuits.Circuit(ops.X.on_each(*qubits),
ops.measure_each(*qubits, key_func=repr))

zeros_result = sampler.run(zeros_circuit, repetitions=repetitions)
ones_result = sampler.run(ones_circuit, repetitions=repetitions)
timestamp = time.time()

zero_state_errors = {
q: np.mean(zeros_result.measurements[repr(q)]) for q in qubits
}
one_state_errors = {
q: 1 - np.mean(ones_result.measurements[repr(q)]) for q in qubits
}

return SingleQubitReadoutCalibrationResult(
zero_state_errors=zero_state_errors,
one_state_errors=one_state_errors,
repetitions=repetitions,
timestamp=timestamp)
89 changes: 89 additions & 0 deletions cirq/experiments/single_qubit_readout_calibration_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Copyright 2019 The Cirq Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from typing import List, Union

import cirq


class NoisySingleQubitReadoutSampler(cirq.Sampler):

def __init__(self,
p0: float,
p1: float,
seed: cirq.value.RANDOM_STATE_LIKE = None):
"""Sampler that flips some bits upon readout.
Args:
p0: Probability of flipping a 0 to a 1.
p1: Probability of flipping a 1 to a 0.
seed: A seed for the pseudorandom number generator.
"""
self.p0 = p0
self.p1 = p1
self.prng = cirq.value.parse_random_state(seed)
self.simulator = cirq.Simulator(seed=self.prng)

def run_sweep(
self,
program: Union[cirq.Circuit, cirq.Schedule],
params: cirq.Sweepable,
repetitions: int = 1,
) -> List[cirq.TrialResult]:
results = self.simulator.run_sweep(program, params, repetitions)
for result in results:
for bits in result.measurements.values():
for i in range(bits.shape[0]):
if bits[i, 0] == 0 and self.prng.uniform() < self.p0:
bits[i, 0] = 1
elif self.prng.uniform() < self.p1:
bits[i, 0] = 0
return results


def test_estimate_single_qubit_readout_errors_no_noise():
qubits = cirq.LineQubit.range(10)
sampler = cirq.Simulator()
repetitions = 1000
result = cirq.estimate_single_qubit_readout_errors(sampler,
qubits=qubits,
repetitions=repetitions)
assert result.zero_state_errors == {q: 0 for q in qubits}
assert result.one_state_errors == {q: 0 for q in qubits}
assert result.repetitions == repetitions
assert isinstance(result.timestamp, float)


def test_estimate_single_qubit_readout_errors_with_noise():
qubits = cirq.LineQubit.range(5)
sampler = NoisySingleQubitReadoutSampler(p0=0.1, p1=0.2, seed=1234)
repetitions = 1000
result = cirq.estimate_single_qubit_readout_errors(sampler,
qubits=qubits,
repetitions=repetitions)
for error in result.zero_state_errors.values():
assert 0.08 < error < 0.12
for error in result.one_state_errors.values():
assert 0.18 < error < 0.22
assert result.repetitions == repetitions
assert isinstance(result.timestamp, float)


def test_single_qubit_readout_calibration_result_repr():
result = cirq.experiments.SingleQubitReadoutCalibrationResult(
zero_state_errors={cirq.LineQubit(0): 0.1},
one_state_errors={cirq.LineQubit(0): 0.2},
repetitions=1000,
timestamp=0.3)
cirq.testing.assert_equivalent_repr(result)
2 changes: 2 additions & 0 deletions cirq/protocols/json.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ def _identity_operation_from_dict(qubits, **kwargs):
'SingleQubitMatrixGate': cirq.SingleQubitMatrixGate,
'SingleQubitPauliStringGateOperation':
cirq.SingleQubitPauliStringGateOperation,
'SingleQubitReadoutCalibrationResult':
cirq.experiments.SingleQubitReadoutCalibrationResult,
'SwapPowGate': cirq.SwapPowGate,
'SycamoreGate': cirq.google.SycamoreGate,
'TwoQubitMatrixGate': cirq.TwoQubitMatrixGate,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"cirq_type": "SingleQubitReadoutCalibrationResult",
"zero_state_errors": [
[
{
"cirq_type": "LineQubit",
"x": 0
},
0.1
]
],
"one_state_errors": [
[
{
"cirq_type": "LineQubit",
"x": 0
},
0.2
]
],
"repetitions": 1000,
"timestamp": 0.3
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cirq.experiments.SingleQubitReadoutCalibrationResult(zero_state_errors={cirq.LineQubit(0): 0.1}, one_state_errors={cirq.LineQubit(0): 0.2}, repetitions=1000, timestamp=0.3)
2 changes: 2 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ run experiments.
.. autosummary::
:toctree: generated/

cirq.estimate_single_qubit_readout_errors
cirq.generate_boixo_2018_supremacy_circuits_v2
cirq.generate_boixo_2018_supremacy_circuits_v2_bristlecone
cirq.generate_boixo_2018_supremacy_circuits_v2_grid
Expand All @@ -430,6 +431,7 @@ run experiments.
cirq.experiments.CrossEntropyResult
cirq.experiments.RabiResult
cirq.experiments.RandomizedBenchMarkResult
cirq.experiments.SingleQubitReadoutCalibrationResult
cirq.experiments.T1DecayResult
cirq.experiments.TomographyResult

Expand Down

0 comments on commit dac7814

Please sign in to comment.