Skip to content

JSON Protocol #1880

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

Merged
merged 60 commits into from
Aug 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
24655a2
JSON Protocol
mpharrigan Jul 25, 2019
7d03b0a
Allow extensibility
mpharrigan Jul 26, 2019
f558974
Add to GridQubit
mpharrigan Jul 26, 2019
4c1d6e0
Explicit resolvers and improved docstrings
mpharrigan Jul 29, 2019
890155e
[json] percolate functions to top level
mpharrigan Jul 31, 2019
14c910f
Merge remote-tracking branch 'origin/master' into json-protocol
mpharrigan Aug 16, 2019
8dd3880
Automated testing
mpharrigan Aug 16, 2019
9363243
JSON support for Gate
mpharrigan Aug 16, 2019
cfb7df3
More JSON goodness
mpharrigan Aug 16, 2019
f16914f
rename to TEST_OBJECTS
mpharrigan Aug 16, 2019
c3985b3
Automatic testing of every class importable from cirq
mpharrigan Aug 19, 2019
537925f
Test singletons too
mpharrigan Aug 19, 2019
5ded473
Everything to support cirq.Circuit
mpharrigan Aug 19, 2019
6053846
Distinguish between things we dont want to serialize
mpharrigan Aug 19, 2019
01dfd8a
clean up
mpharrigan Aug 19, 2019
bd7713f
Merge remote-tracking branch 'origin/master' into json-protocol
mpharrigan Aug 19, 2019
ae84e61
CSWAP fix has been merged in
mpharrigan Aug 19, 2019
2e57f60
Implement serialization for NamedQubit
mpharrigan Aug 19, 2019
6e5f817
Implement serialization for PhasedXPowGate
mpharrigan Aug 19, 2019
33d446a
remove outdated note
mpharrigan Aug 19, 2019
5af0fde
give this assertion its own test
mpharrigan Aug 19, 2019
7230890
Format
mpharrigan Aug 20, 2019
aaaa514
Pylint
mpharrigan Aug 20, 2019
fbddd6c
Types
mpharrigan Aug 20, 2019
7450e01
Merge remote-tracking branch 'origin/master' into json-protocol
mpharrigan Aug 20, 2019
46ea3a1
There's been a new type
mpharrigan Aug 20, 2019
dc1d5e3
Imports for mypy
mpharrigan Aug 20, 2019
e40bed2
mypy hoops
mpharrigan Aug 20, 2019
0cf6e64
extreme hoops to get both formatter and mypy happy
mpharrigan Aug 20, 2019
9fb07fe
Full coverage
mpharrigan Aug 20, 2019
34a4869
Format
mpharrigan Aug 20, 2019
c24b5fb
Pylint
mpharrigan Aug 20, 2019
e78db5a
Mypy
mpharrigan Aug 20, 2019
86894f3
Test coverage for default ser/deser code paths
mpharrigan Aug 20, 2019
160bd99
coverage
mpharrigan Aug 20, 2019
9180b0e
Put extra imports behind `if TYPE_CHECKING`
mpharrigan Aug 22, 2019
3e55ac7
Merge remote-tracking branch 'origin/master' into json-protocol
mpharrigan Aug 22, 2019
8a05060
Keep up with new public classes being added
mpharrigan Aug 22, 2019
87e511f
Tack on support for sympy.Symbol
mpharrigan Aug 22, 2019
abb0cba
Namespace 3rd party objects
mpharrigan Aug 22, 2019
b64eb9e
self._num_qubits -> self.num_qubits()
mpharrigan Aug 22, 2019
b2e8daa
remove superflous comment
mpharrigan Aug 22, 2019
ce9e684
Document CirqEncoder
mpharrigan Aug 22, 2019
d804b58
Document DEFAULT_RESOLVERS
mpharrigan Aug 22, 2019
40a8c13
Format
mpharrigan Aug 22, 2019
9f36322
Merge remote-tracking branch 'origin/master' into json-protocol
mpharrigan Aug 22, 2019
23c79f6
Messed up the merge a bit
mpharrigan Aug 22, 2019
f8df124
More changes due to breaking change
mpharrigan Aug 22, 2019
9cce33d
Missed this one
mpharrigan Aug 22, 2019
66d05de
deser -> deserialization
mpharrigan Aug 23, 2019
fa94654
read_json -> cirq.read_json
mpharrigan Aug 23, 2019
155d063
invert control
mpharrigan Aug 23, 2019
b51de61
Help developers with an error message
mpharrigan Aug 23, 2019
6e83989
format
mpharrigan Aug 23, 2019
2ba7fbf
Merge remote-tracking branch 'origin/master' into json-protocol
mpharrigan Aug 23, 2019
67ad592
Document to_json
mpharrigan Aug 23, 2019
b98e78a
head explosion emoji
mpharrigan Aug 23, 2019
bbbaa95
manual format
mpharrigan Aug 23, 2019
1034435
skip coverage
mpharrigan Aug 23, 2019
264faf9
Merge branch 'master' into json-protocol
dabacon Aug 24, 2019
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
3 changes: 3 additions & 0 deletions cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@
qasm,
QasmArgs,
qid_shape,
read_json,
resolve_parameters,
SupportsApplyChannel,
SupportsConsistentApplyUnitary,
Expand All @@ -353,6 +354,8 @@
SupportsQasmWithArgsAndQubits,
SupportsTraceDistanceBound,
SupportsUnitary,
to_json,
to_json_dict,
trace_distance_bound,
unitary,
validate_mixture,
Expand Down
7 changes: 7 additions & 0 deletions cirq/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1578,6 +1578,13 @@ def save_qasm(self,
"""
self._to_qasm_output(header, precision, qubit_order).save(file_path)

@property
def moments(self):
return self._moments

def _json_dict_(self):
return protocols.to_json_dict(self, ['moments', 'device'])


def _resolve_operations(
operations: Iterable[ops.Operation],
Expand Down
17 changes: 17 additions & 0 deletions cirq/circuits/circuit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3180,3 +3180,20 @@ def test_deprecated_to_unitary_matrix():
def test_deprecated_apply_unitary_effect_to_state():
np.testing.assert_allclose(cirq.Circuit().apply_unitary_effect_to_state(),
cirq.Circuit().final_wavefunction())


def test_moments_property():
q = cirq.NamedQubit('q')
c = cirq.Circuit.from_ops(cirq.X(q), cirq.Y(q))
assert c.moments[0] == cirq.Moment([cirq.X(q)])
assert c.moments[1] == cirq.Moment([cirq.Y(q)])


def test_json_dict():
q0, q1 = cirq.LineQubit.range(2)
c = cirq.Circuit.from_ops(cirq.CNOT(q0, q1))
assert c._json_dict_() == {
'cirq_type': 'Circuit',
'moments': [cirq.Moment([cirq.CNOT(q0, q1)])],
'device': cirq.UNCONSTRAINED_DEVICE,
}
5 changes: 4 additions & 1 deletion cirq/devices/grid_qubit.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

from typing import Dict, List, Tuple

from cirq import ops
from cirq import ops, protocols


class GridQubit(ops.Qid):
Expand Down Expand Up @@ -139,6 +139,9 @@ def __repr__(self):
def __str__(self):
return '({}, {})'.format(self.row, self.col)

def _json_dict_(self):
return protocols.to_json_dict(self, ['row', 'col'])

def __add__(self, other: Tuple[int, int]) -> 'GridQubit':
if not (isinstance(other, tuple) and len(other) == 2 and
all(isinstance(x, int) for x in other)):
Expand Down
10 changes: 10 additions & 0 deletions cirq/devices/grid_qubit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,13 @@ def test_from_proto_bad_dict():
cirq.GridQubit.from_proto_dict({})
with pytest.raises(ValueError):
cirq.GridQubit.from_proto_dict({'nothing': 1})


def test_to_json():
q = cirq.GridQubit(5, 6)
d = q._json_dict_()
assert d == {
'cirq_type': 'GridQubit',
'row': 5,
'col': 6,
}
5 changes: 4 additions & 1 deletion cirq/devices/line_qubit.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import functools
from typing import List

from cirq import ops
from cirq import ops, protocols


@functools.total_ordering
Expand Down Expand Up @@ -85,3 +85,6 @@ def __rsub__(self, other: int) -> 'LineQubit':

def __neg__(self) -> 'LineQubit':
return LineQubit(-self.x)

def _json_dict_(self):
return protocols.to_json_dict(self, ['x'])
7 changes: 7 additions & 0 deletions cirq/devices/line_qubit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,10 @@ def test_addition_subtraction_type_error():

def test_neg():
assert -cirq.LineQubit(1) == cirq.LineQubit(-1)


def test_json_dict():
assert cirq.LineQubit(5)._json_dict_() == {
'cirq_type': 'LineQubit',
'x': 5,
}
9 changes: 8 additions & 1 deletion cirq/devices/unconstrained_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from cirq import value
from cirq import value, protocols
from cirq.devices import device


@value.value_equality()
class _UnconstrainedDevice(device.Device):
"""A device that allows everything, infinitely fast."""

Expand All @@ -37,5 +38,11 @@ def validate_schedule(self, schedule):
def __repr__(self):
return 'cirq.UNCONSTRAINED_DEVICE'

def _value_equality_values_(self):
return ()

def _json_dict_(self):
return protocols.to_json_dict(self, [])


UNCONSTRAINED_DEVICE: device.Device = _UnconstrainedDevice()
20 changes: 20 additions & 0 deletions cirq/ops/common_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,20 @@ def __repr__(self):
def _value_equality_values_(self):
return self.num_qubits(), self.key, self.invert_mask

def _json_dict_(self):
return {
'cirq_type': self.__class__.__name__,
'num_qubits': self.num_qubits(),
'key': self.key,
'invert_mask': self.invert_mask
}

@classmethod
def _from_json_dict_(cls, num_qubits, key, invert_mask, **kwargs):
return cls(num_qubits=num_qubits,
key=key,
invert_mask=tuple(invert_mask))


def _default_measurement_key(qubits: Iterable[raw_types.Qid]) -> str:
return ','.join(str(q) for q in qubits)
Expand Down Expand Up @@ -697,6 +711,12 @@ def _qasm_(self, args: protocols.QasmArgs,
def _value_equality_values_(self):
return self.num_qubits(),

def _json_dict_(self):
return {
'cirq_type': self.__class__.__name__,
'num_qubits': self.num_qubits(),
}


class HPowGate(eigen_gate.EigenGate, gate_features.SingleQubitGate):
"""A Gate that performs a rotation around the X+Z axis of the Bloch sphere.
Expand Down
7 changes: 7 additions & 0 deletions cirq/ops/eigen_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ def __init__(
def exponent(self) -> value.TParamVal:
return self._exponent

@property
def global_shift(self) -> float:
return self._global_shift

# virtual method
def _with_exponent(self: TSelf, exponent: value.TParamVal) -> TSelf:
"""Return the same kind of gate, but with a different exponent.
Expand Down Expand Up @@ -319,6 +323,9 @@ def _resolve_parameters_(self: TSelf, param_resolver) -> TSelf:
return self._with_exponent(
exponent=param_resolver.value_of(self._exponent))

def _json_dict_(self):
return protocols.to_json_dict(self, ['exponent', 'global_shift'])


def _lcm(vals: Iterable[int]) -> int:
t = 1
Expand Down
3 changes: 3 additions & 0 deletions cirq/ops/fsim_gate.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ def __repr__(self):
return 'cirq.FSimGate(theta={}, phi={})'.format(proper_repr(self.theta),
proper_repr(self.phi))

def _json_dict_(self):
return protocols.to_json_dict(self, ['theta', 'phi'])


def _format_rads(args: 'cirq.CircuitDiagramInfoArgs', radians: float) -> str:
if cirq.is_parameterized(radians):
Expand Down
8 changes: 8 additions & 0 deletions cirq/ops/fsim_gate_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,11 @@ def test_fsim_iswap_cphase(theta, phi):
iswap_cphase = cirq.Circuit.from_ops((iswap.on(q0, q1), cphase.on(q0, q1)))
fsim = cirq.FSimGate(theta=theta, phi=phi)
assert np.allclose(cirq.unitary(iswap_cphase), cirq.unitary(fsim))


def test_fsim_json_dict():
assert cirq.FSimGate(theta=0.123, phi=0.456)._json_dict_() == {
'cirq_type': 'FSimGate',
'theta': 0.123,
'phi': 0.456,
}
3 changes: 3 additions & 0 deletions cirq/ops/gate_operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ def __str__(self):
return '{}({})'.format(self.gate,
', '.join(str(e) for e in self.qubits))

def _json_dict_(self):
return protocols.to_json_dict(self, ['gate', 'qubits'])

def _group_interchangeable_qubits(self) -> Tuple[
Union[raw_types.Qid,
Tuple[int, FrozenSet[raw_types.Qid]]],
Expand Down
5 changes: 4 additions & 1 deletion cirq/ops/global_phase_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import numpy as np

from cirq import value
from cirq import value, protocols
from cirq.ops import raw_types


Expand Down Expand Up @@ -62,3 +62,6 @@ def __str__(self):

def __repr__(self):
return 'cirq.GlobalPhaseOperation({!r})'.format(self.coefficient)

def _json_dict_(self):
return protocols.to_json_dict(self, ['coefficient'])
7 changes: 7 additions & 0 deletions cirq/ops/global_phase_op_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,10 @@ def test_diagram():
]), """
global phase: -0.5π
""")


def test_global_phase_op_json_dict():
assert cirq.GlobalPhaseOperation(-1j)._json_dict_() == {
'cirq_type': 'GlobalPhaseOperation',
'coefficient': -1j,
}
4 changes: 4 additions & 0 deletions cirq/ops/moment.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

from typing import Any, Callable, Iterable, Sequence, TypeVar, Union

from cirq import protocols
from cirq.protocols import approx_eq
from cirq.ops import raw_types

Expand Down Expand Up @@ -147,6 +148,9 @@ def transform_qubits(self: TSelf_Moment,
return self.__class__(op.transform_qubits(func)
for op in self.operations)

def _json_dict_(self):
return protocols.to_json_dict(self, ['operations'])


def _list_repr_with_indented_item_lines(items: Sequence[Any]) -> str:
block = '\n'.join([repr(op) + ',' for op in items])
Expand Down
12 changes: 11 additions & 1 deletion cirq/ops/moment_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ def test_qubits():
a = cirq.NamedQubit('a')
b = cirq.NamedQubit('b')

assert Moment([cirq.X(a), cirq.X(b)]).qubits == {a , b}
assert Moment([cirq.X(a), cirq.X(b)]).qubits == {a, b}
assert Moment([cirq.X(a)]).qubits == {a}
assert Moment([cirq.CZ(a, b)]).qubits == {a, b}

Expand All @@ -225,3 +225,13 @@ def test_bool():
assert not Moment()
a = cirq.NamedQubit('a')
assert Moment([cirq.X(a)])


def test_json_dict():
a = cirq.NamedQubit('a')
b = cirq.NamedQubit('b')
mom = Moment([cirq.CZ(a, b)])
assert mom._json_dict_() == {
'cirq_type': 'Moment',
'operations': (cirq.CZ(a, b),)
}
5 changes: 4 additions & 1 deletion cirq/ops/named_qubit.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
# 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 cirq import protocols
from cirq.ops import raw_types


Expand Down Expand Up @@ -60,6 +60,9 @@ def range(*args, prefix: str):
"""
return [NamedQubit(prefix + str(i)) for i in range(*args)]

def _json_dict_(self):
return protocols.to_json_dict(self, ['name'])


def _pad_digits(text: str) -> str:
"""A str method with hacks to support better lexicographic ordering.
Expand Down
18 changes: 15 additions & 3 deletions cirq/ops/pauli_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ def __pow__(self: '_PauliX',
exponent: value.TParamVal) -> common_gates.XPowGate:
return common_gates.XPowGate(exponent=exponent)

@classmethod
def _from_json_dict_(cls, exponent, global_shift, **kwargs):
assert global_shift == 0
return cls(exponent=exponent)


class _PauliY(Pauli, common_gates.YPowGate):

Expand All @@ -107,6 +112,11 @@ def __pow__(self: '_PauliY',
exponent: value.TParamVal) -> common_gates.YPowGate:
return common_gates.YPowGate(exponent=exponent)

@classmethod
def _from_json_dict_(cls, exponent, global_shift, **kwargs):
assert global_shift == 0
return cls(exponent=exponent)


class _PauliZ(Pauli, common_gates.ZPowGate):

Expand All @@ -118,6 +128,11 @@ def __pow__(self: '_PauliZ',
exponent: value.TParamVal) -> common_gates.ZPowGate:
return common_gates.ZPowGate(exponent=exponent)

@classmethod
def _from_json_dict_(cls, exponent, global_shift, **kwargs):
assert global_shift == 0
return cls(exponent=exponent)


# The Pauli X gate.
#
Expand All @@ -127,7 +142,6 @@ def __pow__(self: '_PauliZ',
# [1, 0]]
X = _PauliX()


# The Pauli Y gate.
#
# Matrix:
Expand All @@ -136,7 +150,6 @@ def __pow__(self: '_PauliZ',
# [i, 0]]
Y = _PauliY()


# The Pauli Z gate.
#
# Matrix:
Expand All @@ -145,5 +158,4 @@ def __pow__(self: '_PauliZ',
# [0, -1]]
Z = _PauliZ()


Pauli._XYZ = (X, Y, Z)
Loading