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

Retain ordering information in conversion. #4384

Merged
merged 25 commits into from
Jan 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f682b91
Retain ordering information in conversion.
Cynocracy Aug 4, 2021
1ea9a0b
Merge branch 'master' into patch-2
Cynocracy Sep 9, 2021
ec2b83b
Merge branch 'master' into patch-2
Cynocracy Sep 13, 2021
b61cccd
Merge branch 'master' into patch-2
Cynocracy Sep 14, 2021
9916850
Merge branch 'master' into patch-2
Cynocracy Sep 16, 2021
a0aa2e9
Merge branch 'master' into patch-2
Cynocracy Sep 23, 2021
0c9049c
Merge branch 'master' into patch-2
Cynocracy Sep 30, 2021
57cebd0
Merge branch 'master' into patch-2
Cynocracy Oct 15, 2021
41f3d7b
Merge branch 'master' into patch-2
Cynocracy Nov 22, 2021
6a02e5a
fmt
Cynocracy Dec 1, 2021
1609045
Merge branch 'master' into patch-2
Cynocracy Dec 1, 2021
d189649
Merge branch 'master' into patch-2
Cynocracy Dec 2, 2021
8fc436c
Merge branch 'master' into patch-2
Cynocracy Dec 7, 2021
4e9889b
Merge branch 'master' into patch-2
Cynocracy Dec 8, 2021
7693689
Merge branch 'master' into patch-2
Cynocracy Dec 13, 2021
3010dac
Merge branch 'master' into patch-2
Cynocracy Dec 13, 2021
61fa14a
Merge branch 'master' into patch-2
Cynocracy Dec 14, 2021
4e620f1
Merge branch 'master' into patch-2
Cynocracy Dec 14, 2021
79547a3
Appease test coverage check
Cynocracy Dec 14, 2021
eee9431
Appease format check
Cynocracy Dec 14, 2021
5fcd2ce
Merge branch 'master' into patch-2
Cynocracy Dec 14, 2021
7d7bee7
Merge branch 'master' into patch-2
Cynocracy Dec 20, 2021
6287569
Merge branch 'master' into patch-2
Cynocracy Jan 4, 2022
e2b977d
Merge branch 'master' into patch-2
Cynocracy Jan 4, 2022
9f231c4
Merge branch 'master' into patch-2
Cynocracy Jan 6, 2022
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
2 changes: 1 addition & 1 deletion cirq-google/cirq_google/engine/abstract_local_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,5 +202,5 @@ def get_circuit(self, program_num: Optional[int] = None) -> cirq.Circuit:
return self._circuits[0]

def batch_size(self) -> int:
"""Returns the number of programs in a batch program. """
"""Returns the number of programs in a batch program."""
return len(self._circuits)
49 changes: 39 additions & 10 deletions cirq-ionq/cirq_ionq/results.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"""Result types for the IonQ API."""

import collections
from typing import Dict, Counter, Optional, Sequence
from typing import Dict, Counter, List, Optional, Sequence

import numpy as np

Expand All @@ -27,7 +27,9 @@ class QPUResult:
def __init__(
self, counts: Dict[int, int], num_qubits: int, measurement_dict: Dict[str, Sequence[int]]
):
self._counts = counts
# We require a consistent ordering, and here we use bitvector as such.
# OrderedDict can be removed in python 3.7, where it is part of the contract.
self._counts = collections.OrderedDict(sorted(counts.items()))
self._num_qubits = num_qubits
self._measurement_dict = measurement_dict
self._repetitions = sum(self._counts.values())
Expand All @@ -40,8 +42,38 @@ def repetitions(self) -> int:
"""Returns the number of times the circuit was run."""
return self._repetitions

def ordered_results(self, key: Optional[str] = None) -> List[int]:
"""Returns a list of arbitrarily but consistently ordered results as big endian ints.

If a key parameter is supplied, these are the counts for the measurement results for
the qubits measured by the measurement gate with that key. If no key is given, these
are the measurement results from measuring all qubits in the circuit.

The value in the returned list is the computational basis state measured for the
qubits that have been measured. This is expressed in big-endian form. For example, if
no measurement key is supplied and all qubits are measured, each entry in this returned dict
has a bit string where the `cirq.LineQubit`s are expressed in the order:
(cirq.LineQubit(0), cirq.LineQubit(1), ..., cirq.LineQubit(n-1))
In the case where only `r` qubits are measured corresponding to targets t_0, t_1,...t_{r-1},
the bit string corresponds to the order
(cirq.LineQubit(t_0), cirq.LineQubit(t_1), ... cirq.LineQubit(t_{r-1}))
"""

if key is not None and not key in self._measurement_dict:
raise ValueError(
f'Measurement key {key} is not a key for a measurement gate in the'
'circuit that produced these results.'
)
targets = self._measurement_dict[key] if key is not None else range(self.num_qubits())
result: List[int] = []
for value, count in self._counts.items():
bits = [(value >> (self.num_qubits() - target - 1)) & 1 for target in targets]
bit_value = sum(bit * (1 << i) for i, bit in enumerate(bits[::-1]))
result.extend([bit_value] * count)
return result

def counts(self, key: Optional[str] = None) -> Counter[int]:
"""Returns the raw counts of the measurement results.
"""Returns the processed counts of the measurement results.

If a key parameter is supplied, these are the counts for the measurement results for
the qubits measured by the measurement gate with that key. If no key is given, these
Expand All @@ -67,12 +99,8 @@ def counts(self, key: Optional[str] = None) -> Counter[int]:
f'Measurement key {key} is not a key for a measurement gate in the'
'circuit that produced these results.'
)
targets = self._measurement_dict[key]
result: Counter[int] = collections.Counter()
for value, count in self._counts.items():
bits = [(value >> (self.num_qubits() - target - 1)) & 1 for target in targets]
bit_value = sum(bit * (1 << i) for i, bit in enumerate(bits[::-1]))
result[bit_value] += count
result.update([bit_value for bit_value in self.ordered_results(key)])
return result

def measurement_dict(self) -> Dict[str, Sequence[int]]:
Expand All @@ -89,7 +117,8 @@ def to_cirq_result(
the IonQ API. Typically these results are also ordered by when they were run, though
that contract is implicit. Because the IonQ API does not retain that ordering information,
the order of these `cirq.Result` objects should *not* be interpetted as representing the
order in which the circuit was repeated.
order in which the circuit was repeated. Correlations between measurements keys are
preserved.

Args:
params: The `cirq.ParamResolver` used to generate these results.
Expand All @@ -108,7 +137,7 @@ def to_cirq_result(
)
measurements = {}
for key, targets in self.measurement_dict().items():
qpu_results = list(self.counts(key).elements())
qpu_results = self.ordered_results(key)
measurements[key] = np.array(
list(cirq.big_endian_int_to_bits(x, bit_count=len(targets)) for x in qpu_results)
)
Expand Down
15 changes: 15 additions & 0 deletions cirq-ionq/cirq_ionq/results_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,15 @@ def test_qpu_result_to_cirq_result():
# cirq.Result only compares pandas data frame, so possible to have supplied an list of
# list instead of a numpy multidimensional array. Check this here.
assert type(result.to_cirq_result().measurements['x']) == np.ndarray
# Results bitstreams need to be consistent betwween measurement keys
# Ordering is by bitvector, so 0b01 0b01 0b10 should be the ordering for all measurement dicts.
result = ionq.QPUResult(
{0b10: 1, 0b01: 2}, num_qubits=2, measurement_dict={'x': [0, 1], 'y': [0], 'z': [1]}
)
assert result.to_cirq_result() == cirq.Result(
params=cirq.ParamResolver({}),
measurements={'x': [[0, 1], [0, 1], [1, 0]], 'y': [[0], [0], [1]], 'z': [[1], [1], [0]]},
)


def test_qpu_result_to_cirq_result_multiple_keys():
Expand All @@ -138,6 +147,12 @@ def test_qpu_result_to_cirq_result_no_keys():
_ = result.to_cirq_result()


def test_ordered_results_invalid_key():
result = ionq.QPUResult({0b00: 1, 0b01: 2}, num_qubits=2, measurement_dict={'x': [1]})
with pytest.raises(ValueError, match='is not a key for'):
_ = result.ordered_results('y')


def test_simulator_result_fields():
result = ionq.SimulatorResult(
{0: 0.4, 1: 0.6},
Expand Down
2 changes: 1 addition & 1 deletion examples/deutsch.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def main():


def make_oracle(q0, q1, secret_function):
""" Gates implementing the secret function f(x)."""
"""Gates implementing the secret function f(x)."""

# coverage: ignore
if secret_function[0]:
Expand Down