From be41718fe751075366bef788b869e1df4bcc3fa4 Mon Sep 17 00:00:00 2001 From: "Kevin J. Sung" Date: Fri, 25 May 2018 13:18:51 -0700 Subject: [PATCH 1/3] parameterize xmon gates --- cirq/contrib/jobs/depolarizer_channel.py | 9 +- cirq/google/xmon_gates.py | 105 ++++------------------- cirq/google/xmon_gates_test.py | 11 +++ cirq/ops/__init__.py | 1 + cirq/ops/gate_features.py | 3 - cirq/ops/partial_reflection_gate.py | 2 - cirq/ops/partial_reflection_gate_test.py | 3 - docs/simulation.md | 2 +- 8 files changed, 37 insertions(+), 99 deletions(-) diff --git a/cirq/contrib/jobs/depolarizer_channel.py b/cirq/contrib/jobs/depolarizer_channel.py index a7d256ebd0d..19e942bd210 100644 --- a/cirq/contrib/jobs/depolarizer_channel.py +++ b/cirq/contrib/jobs/depolarizer_channel.py @@ -15,7 +15,6 @@ """Error simulator that adds randomly activated error gates after every moment. """ -import copy import numpy as np from cirq.circuits.circuit import Circuit @@ -108,8 +107,12 @@ def transform_job(self, job): errors = np.random.random(self.realizations) < self.p if any(errors): key = self._parameter_name + str(error_number) - new_error_gate = copy.deepcopy(gate) - new_error_gate.half_turns = Symbol(key) + if isinstance(gate, xmon_gates.ExpWGate): + new_error_gate = xmon_gates.ExpWGate( + half_turns=Symbol(key), + axis_half_turns=gate.axis_half_turns) + else: + new_error_gate = type(gate)(half_turns=Symbol(key)) error_gates.append(new_error_gate.on(q)) error_sweep += Points(key, list(errors * 1.0)) error_number += 1 diff --git a/cirq/google/xmon_gates.py b/cirq/google/xmon_gates.py index 6a1fddd1e11..be0ba21d7f3 100644 --- a/cirq/google/xmon_gates.py +++ b/cirq/google/xmon_gates.py @@ -108,10 +108,8 @@ def __repr__(self): class Exp11Gate(XmonGate, - ops.TextDiagrammableGate, - ops.InterchangeableQubitsGate, ops.PhaseableGate, - PotentialImplementation): + ops.Rot11Gate): """A two-qubit interaction that phases the amplitude of the 11 state. This gate is exp(i * pi * |11><11| * half_turn). @@ -124,11 +122,6 @@ class Exp11Gate(XmonGate, so the half_turn corresponds to half of a full rotation in U(4). """ - def __init__(self, *positional_args, - half_turns: Union[Symbol, float]=1) -> None: - assert not positional_args - self.half_turns = _canonicalize_half_turns(half_turns) - def phase_by(self, phase_turns, qubit_index): return self @@ -143,33 +136,6 @@ def to_proto(self, *qubits): self.parameterized_value_to_proto(self.half_turns, op.exp_11.half_turns) return op - def try_cast_to(self, desired_type): - if desired_type is ops.KnownMatrixGate and self.has_matrix(): - return self - return super().try_cast_to(desired_type) - - def has_matrix(self): - return not isinstance(self.half_turns, Symbol) - - def matrix(self): - if not self.has_matrix(): - raise ValueError("Don't have a known matrix.") - return ops.Rot11Gate(half_turns=self.half_turns).matrix() - - def text_diagram_wire_symbols(self, - qubit_count=None, - use_unicode_characters=True, - precision=3): - return 'Z', 'Z' - - def text_diagram_exponent(self): - return self.half_turns - - def __str__(self): - if self.half_turns == 1: - return 'CZ' - return self.__repr__() - def __repr__(self): return 'Exp11Gate(half_turns={})'.format( repr(self.half_turns)) @@ -179,11 +145,8 @@ def __eq__(self, other): return NotImplemented return self.half_turns == other.half_turns - def __ne__(self, other): - return not self == other - def __hash__(self): - return hash((ops.Rot11Gate, self.half_turns)) + return hash((type(self), self.half_turns)) class ExpWGate(XmonGate, @@ -191,6 +154,7 @@ class ExpWGate(XmonGate, ops.TextDiagrammableGate, ops.PhaseableGate, ops.BoundedEffectGate, + ops.ParameterizableGate, PotentialImplementation): """A rotation around an axis in the XY plane of the Bloch sphere. @@ -238,7 +202,7 @@ def to_proto(self, *qubits): return op def try_cast_to(self, desired_type): - if desired_type is ops.KnownMatrixGate and self.has_matrix(): + if desired_type is ops.KnownMatrixGate and not self.is_parameterized(): return self if desired_type is ops.ReversibleGate and self.has_inverse(): return self @@ -253,13 +217,9 @@ def inverse(self): return ExpWGate(half_turns=-self.half_turns, axis_half_turns=self.axis_half_turns) - def has_matrix(self): - return (not isinstance(self.half_turns, Symbol) and - not isinstance(self.axis_half_turns, Symbol)) - def matrix(self): - if not self.has_matrix(): - raise ValueError("Don't have a known matrix.") + if self.is_parameterized(): + raise ValueError("Parameterized. Don't have a known matrix.") phase = ops.RotZGate(half_turns=self.axis_half_turns).matrix() c = np.exp(1j * np.pi * self.half_turns) rot = np.array([[1 + c, 1 - c], [1 - c, 1 + c]]) / 2 @@ -292,6 +252,15 @@ def text_diagram_wire_symbols(self, def text_diagram_exponent(self): return self.half_turns + def is_parameterized(self) -> bool: + return (isinstance(self.half_turns, Symbol) or + isinstance(self.axis_half_turns, Symbol)) + + def with_parameters_resolved_by(self, param_resolver) -> 'ExpWGate': + return ExpWGate( + half_turns=param_resolver.value_of(self.half_turns), + axis_half_turns=param_resolver.value_of(self.axis_half_turns)) + def __str__(self): base = self.text_diagram_wire_symbols()[0] if self.half_turns == 1: @@ -327,9 +296,7 @@ def __hash__(self): class ExpZGate(XmonGate, - ops.SingleQubitGate, - ops.TextDiagrammableGate, - PotentialImplementation): + ops.RotZGate): """A rotation around the Z axis of the Bloch sphere. This gate is exp(-i * pi * Z * half_turns / 2) where Z is the Z matrix @@ -341,11 +308,6 @@ class ExpZGate(XmonGate, bloch sphere of 360 degrees. """ - def __init__(self, *positional_args, - half_turns: Union[Symbol, float]=1) -> None: - assert not positional_args - self.half_turns = _canonicalize_half_turns(half_turns) - def text_diagram_wire_symbols(self, qubit_count=None, use_unicode_characters=True, @@ -363,34 +325,6 @@ def text_diagram_exponent(self): return -1 return self.half_turns - def try_cast_to(self, desired_type): - if desired_type is ops.KnownMatrixGate and self.has_matrix(): - return self - if desired_type is ops.ReversibleGate and self.has_inverse(): - return self - return super().try_cast_to(desired_type) - - def has_inverse(self): - return not isinstance(self.half_turns, Symbol) - - def inverse(self): - if not self.has_inverse(): - raise ValueError("Don't have a known inverse.") - return ExpZGate(half_turns=-self.half_turns) - - def has_matrix(self): - return not isinstance(self.half_turns, Symbol) - - def matrix(self): - if not self.has_matrix(): - raise ValueError("Don't have a known matrix.") - return ops.RotZGate(half_turns=self.half_turns).matrix() - - def trace_distance_bound(self): - if isinstance(self.half_turns, Symbol): - return 1 - return abs(self.half_turns) * 3.5 - def to_proto(self, *qubits): if len(qubits) != 1: raise ValueError('Wrong number of qubits.') @@ -417,15 +351,12 @@ def __repr__(self): repr(self.half_turns)) def __eq__(self, other): - if not isinstance(other, type(self)): + if not isinstance(other, (ops.RotZGate, type(self))): return NotImplemented return self.half_turns == other.half_turns - def __ne__(self, other): - return not self == other - def __hash__(self): - return hash((ExpZGate, self.half_turns)) + return hash((type(self), self.half_turns)) def _canonicalize_half_turns( diff --git a/cirq/google/xmon_gates_test.py b/cirq/google/xmon_gates_test.py index 8f980f2a199..0ecc4245b2b 100644 --- a/cirq/google/xmon_gates_test.py +++ b/cirq/google/xmon_gates_test.py @@ -20,6 +20,7 @@ XmonGate, XmonQubit, XmonMeasurementGate, ExpZGate, Exp11Gate, ExpWGate, ) from cirq.ops import KnownMatrixGate, ReversibleGate +from cirq.study import ParamResolver from cirq.value import Symbol from cirq.testing import EqualsTester @@ -270,3 +271,13 @@ def test_w_potential_implementation(): ReversibleGate) assert ex.can_cast(ExpWGate(), KnownMatrixGate) assert ex.can_cast(ExpWGate(), ReversibleGate) + + +def test_w_parameterize(): + parameterized_gate = ExpWGate(half_turns=Symbol('a'), + axis_half_turns=Symbol('b')) + with pytest.raises(ValueError): + _ = parameterized_gate.matrix() + resolver = ParamResolver({'a': 0.1, 'b': 0.2}) + resolved_gate = parameterized_gate.with_parameters_resolved_by(resolver) + assert resolved_gate == ExpWGate(half_turns=0.1, axis_half_turns=0.2) diff --git a/cirq/ops/__init__.py b/cirq/ops/__init__.py index 2dae98326af..6b1134dae8b 100644 --- a/cirq/ops/__init__.py +++ b/cirq/ops/__init__.py @@ -42,6 +42,7 @@ CompositeGate, ExtrapolatableGate, KnownMatrixGate, + ParameterizableGate, PhaseableGate, ReversibleGate, SelfInverseGate, diff --git a/cirq/ops/gate_features.py b/cirq/ops/gate_features.py index 4ea65910014..eb733ae593e 100644 --- a/cirq/ops/gate_features.py +++ b/cirq/ops/gate_features.py @@ -258,7 +258,4 @@ def with_parameters_resolved_by(self, Returns a gate of the same type, but with all Symbols replaced with floats according to the given ParamResolver. - - Raises: - ValueError: The gate has no parameters to resolve. """ diff --git a/cirq/ops/partial_reflection_gate.py b/cirq/ops/partial_reflection_gate.py index 3a9377d1985..f6640c2ba83 100644 --- a/cirq/ops/partial_reflection_gate.py +++ b/cirq/ops/partial_reflection_gate.py @@ -138,7 +138,5 @@ def is_parameterized(self) -> bool: def with_parameters_resolved_by(self, param_resolver) -> 'PartialReflectionGate': - if not self.is_parameterized(): - raise ValueError("Gate does not have any parameters to resolve.") return self._with_half_turns( half_turns=param_resolver.value_of(self.half_turns)) diff --git a/cirq/ops/partial_reflection_gate_test.py b/cirq/ops/partial_reflection_gate_test.py index ca4ff51089c..e0721f8a9f8 100644 --- a/cirq/ops/partial_reflection_gate_test.py +++ b/cirq/ops/partial_reflection_gate_test.py @@ -15,7 +15,6 @@ from typing import Union import numpy as np -import pytest import cirq from cirq.ops.partial_reflection_gate import PartialReflectionGate @@ -84,5 +83,3 @@ def test_partial_reflection_gate_with_parameters_resolved_by(): resolver = ParamResolver({'a': 0.1}) resolved_gate = gate.with_parameters_resolved_by(resolver) assert resolved_gate.half_turns == 0.1 - with pytest.raises(ValueError): - _ = resolved_gate.with_parameters_resolved_by(resolver) diff --git a/docs/simulation.md b/docs/simulation.md index c0540e0cadd..6a1227a41a6 100644 --- a/docs/simulation.md +++ b/docs/simulation.md @@ -34,7 +34,7 @@ circuit.append(basic_circuit()) print(circuit) # prints -# (0, 0): ───X^0.5───Z───X^0.5───M─── +# (0, 0): ───X^0.5───@───X^0.5───M─── # │ # (1, 0): ───X^0.5───Z───X^0.5───M─── ``` From a38f79a41898b5a9f4514deeaf3f778f3cea971c Mon Sep 17 00:00:00 2001 From: "Kevin J. Sung" Date: Tue, 29 May 2018 13:59:27 -0700 Subject: [PATCH 2/3] mostly revert to old xmon gate code --- cirq/google/xmon_gates.py | 127 ++++++++++++++++++++++++++++----- cirq/google/xmon_gates_test.py | 33 +++++++++ 2 files changed, 143 insertions(+), 17 deletions(-) diff --git a/cirq/google/xmon_gates.py b/cirq/google/xmon_gates.py index be0ba21d7f3..677ffdc7834 100644 --- a/cirq/google/xmon_gates.py +++ b/cirq/google/xmon_gates.py @@ -108,8 +108,11 @@ def __repr__(self): class Exp11Gate(XmonGate, + ops.TextDiagrammableGate, + ops.InterchangeableQubitsGate, ops.PhaseableGate, - ops.Rot11Gate): + ops.ParameterizableGate, + PotentialImplementation): """A two-qubit interaction that phases the amplitude of the 11 state. This gate is exp(i * pi * |11><11| * half_turn). @@ -122,6 +125,11 @@ class Exp11Gate(XmonGate, so the half_turn corresponds to half of a full rotation in U(4). """ + def __init__(self, *positional_args, + half_turns: Union[Symbol, float]=1) -> None: + assert not positional_args + self.half_turns = _canonicalize_half_turns(half_turns) + def phase_by(self, phase_turns, qubit_index): return self @@ -136,6 +144,33 @@ def to_proto(self, *qubits): self.parameterized_value_to_proto(self.half_turns, op.exp_11.half_turns) return op + def try_cast_to(self, desired_type): + if desired_type is ops.KnownMatrixGate and self.has_matrix(): + return self + return super().try_cast_to(desired_type) + + def has_matrix(self): + return not isinstance(self.half_turns, Symbol) + + def matrix(self): + if not self.has_matrix(): + raise ValueError("Don't have a known matrix.") + return ops.Rot11Gate(half_turns=self.half_turns).matrix() + + def text_diagram_wire_symbols(self, + qubit_count=None, + use_unicode_characters=True, + precision=3): + return '@', 'Z' + + def text_diagram_exponent(self): + return self.half_turns + + def __str__(self): + if self.half_turns == 1: + return 'CZ' + return self.__repr__() + def __repr__(self): return 'Exp11Gate(half_turns={})'.format( repr(self.half_turns)) @@ -145,8 +180,17 @@ def __eq__(self, other): return NotImplemented return self.half_turns == other.half_turns + def __ne__(self, other): + return not self == other + def __hash__(self): - return hash((type(self), self.half_turns)) + return hash((Exp11Gate, self.half_turns)) + + def is_parameterized(self) -> bool: + return isinstance(self.half_turns, Symbol) + + def with_parameters_resolved_by(self, param_resolver) -> 'Exp11Gate': + return Exp11Gate(half_turns=param_resolver.value_of(self.half_turns)) class ExpWGate(XmonGate, @@ -202,7 +246,7 @@ def to_proto(self, *qubits): return op def try_cast_to(self, desired_type): - if desired_type is ops.KnownMatrixGate and not self.is_parameterized(): + if desired_type is ops.KnownMatrixGate and self.has_matrix(): return self if desired_type is ops.ReversibleGate and self.has_inverse(): return self @@ -217,9 +261,13 @@ def inverse(self): return ExpWGate(half_turns=-self.half_turns, axis_half_turns=self.axis_half_turns) + def has_matrix(self): + return (not isinstance(self.half_turns, Symbol) and + not isinstance(self.axis_half_turns, Symbol)) + def matrix(self): - if self.is_parameterized(): - raise ValueError("Parameterized. Don't have a known matrix.") + if not self.has_matrix(): + raise ValueError("Don't have a known matrix.") phase = ops.RotZGate(half_turns=self.axis_half_turns).matrix() c = np.exp(1j * np.pi * self.half_turns) rot = np.array([[1 + c, 1 - c], [1 - c, 1 + c]]) / 2 @@ -252,15 +300,6 @@ def text_diagram_wire_symbols(self, def text_diagram_exponent(self): return self.half_turns - def is_parameterized(self) -> bool: - return (isinstance(self.half_turns, Symbol) or - isinstance(self.axis_half_turns, Symbol)) - - def with_parameters_resolved_by(self, param_resolver) -> 'ExpWGate': - return ExpWGate( - half_turns=param_resolver.value_of(self.half_turns), - axis_half_turns=param_resolver.value_of(self.axis_half_turns)) - def __str__(self): base = self.text_diagram_wire_symbols()[0] if self.half_turns == 1: @@ -294,9 +333,21 @@ def __hash__(self): return hash((ops.RotYGate, self.half_turns)) return hash((ExpWGate, self.half_turns, self.axis_half_turns)) + def is_parameterized(self) -> bool: + return (isinstance(self.half_turns, Symbol) or + isinstance(self.axis_half_turns, Symbol)) + + def with_parameters_resolved_by(self, param_resolver) -> 'ExpWGate': + return ExpWGate( + half_turns=param_resolver.value_of(self.half_turns), + axis_half_turns=param_resolver.value_of(self.axis_half_turns)) + class ExpZGate(XmonGate, - ops.RotZGate): + ops.SingleQubitGate, + ops.TextDiagrammableGate, + ops.ParameterizableGate, + PotentialImplementation): """A rotation around the Z axis of the Bloch sphere. This gate is exp(-i * pi * Z * half_turns / 2) where Z is the Z matrix @@ -308,6 +359,11 @@ class ExpZGate(XmonGate, bloch sphere of 360 degrees. """ + def __init__(self, *positional_args, + half_turns: Union[Symbol, float]=1) -> None: + assert not positional_args + self.half_turns = _canonicalize_half_turns(half_turns) + def text_diagram_wire_symbols(self, qubit_count=None, use_unicode_characters=True, @@ -325,6 +381,34 @@ def text_diagram_exponent(self): return -1 return self.half_turns + def try_cast_to(self, desired_type): + if desired_type is ops.KnownMatrixGate and self.has_matrix(): + return self + if desired_type is ops.ReversibleGate and self.has_inverse(): + return self + return super().try_cast_to(desired_type) + + def has_inverse(self): + return not isinstance(self.half_turns, Symbol) + + def inverse(self): + if not self.has_inverse(): + raise ValueError("Don't have a known inverse.") + return ExpZGate(half_turns=-self.half_turns) + + def has_matrix(self): + return not isinstance(self.half_turns, Symbol) + + def matrix(self): + if not self.has_matrix(): + raise ValueError("Don't have a known matrix.") + return np.diag([(-1j)**self.half_turns, 1j**self.half_turns]) + + def trace_distance_bound(self): + if isinstance(self.half_turns, Symbol): + return 1 + return abs(self.half_turns) * 3.5 + def to_proto(self, *qubits): if len(qubits) != 1: raise ValueError('Wrong number of qubits.') @@ -351,12 +435,21 @@ def __repr__(self): repr(self.half_turns)) def __eq__(self, other): - if not isinstance(other, (ops.RotZGate, type(self))): + if not isinstance(other, type(self)): return NotImplemented return self.half_turns == other.half_turns + def __ne__(self, other): + return not self == other + def __hash__(self): - return hash((type(self), self.half_turns)) + return hash((ExpZGate, self.half_turns)) + + def is_parameterized(self) -> bool: + return isinstance(self.half_turns, Symbol) + + def with_parameters_resolved_by(self, param_resolver) -> 'ExpZGate': + return ExpZGate(half_turns=param_resolver.value_of(self.half_turns)) def _canonicalize_half_turns( diff --git a/cirq/google/xmon_gates_test.py b/cirq/google/xmon_gates_test.py index 0ecc4245b2b..8e60a95cb16 100644 --- a/cirq/google/xmon_gates_test.py +++ b/cirq/google/xmon_gates_test.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import pytest +import numpy as np from google.protobuf import message, text_format from cirq.api.google.v1 import operations_pb2 @@ -128,6 +129,27 @@ def test_z_to_proto(): """) +def test_z_matrix(): + assert np.allclose(ExpZGate(half_turns=1).matrix(), + np.array([[-1j, 0], [0, 1j]])) + assert np.allclose(ExpZGate(half_turns=0.5).matrix(), + np.array([[1 - 1j, 0], [0, 1 + 1j]]) / np.sqrt(2)) + assert np.allclose(ExpZGate(half_turns=0).matrix(), + np.array([[1, 0], [0, 1]])) + assert np.allclose(ExpZGate(half_turns=-0.5).matrix(), + np.array([[1 + 1j, 0], [0, 1 - 1j]]) / np.sqrt(2)) + + +def test_z_parameterize(): + parameterized_gate = ExpZGate(half_turns=Symbol('a')) + assert parameterized_gate.is_parameterized() + with pytest.raises(ValueError): + _ = parameterized_gate.matrix() + resolver = ParamResolver({'a': 0.1}) + resolved_gate = parameterized_gate.with_parameters_resolved_by(resolver) + assert resolved_gate == ExpZGate(half_turns=0.1) + + def test_cz_eq(): eq = EqualsTester() eq.make_equality_pair(lambda: Exp11Gate(half_turns=0)) @@ -180,6 +202,16 @@ def test_cz_to_proto(): """) +def test_cz_parameterize(): + parameterized_gate = Exp11Gate(half_turns=Symbol('a')) + assert parameterized_gate.is_parameterized() + with pytest.raises(ValueError): + _ = parameterized_gate.matrix() + resolver = ParamResolver({'a': 0.1}) + resolved_gate = parameterized_gate.with_parameters_resolved_by(resolver) + assert resolved_gate == Exp11Gate(half_turns=0.1) + + def test_w_eq(): eq = EqualsTester() eq.add_equality_group(ExpWGate(), @@ -276,6 +308,7 @@ def test_w_potential_implementation(): def test_w_parameterize(): parameterized_gate = ExpWGate(half_turns=Symbol('a'), axis_half_turns=Symbol('b')) + assert parameterized_gate.is_parameterized() with pytest.raises(ValueError): _ = parameterized_gate.matrix() resolver = ParamResolver({'a': 0.1, 'b': 0.2}) From a482399a41410ea06e8f0e09d4257210df3f7447 Mon Sep 17 00:00:00 2001 From: "Kevin J. Sung" Date: Tue, 29 May 2018 14:02:23 -0700 Subject: [PATCH 3/3] revert depolarizer channel change --- cirq/contrib/jobs/depolarizer_channel.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cirq/contrib/jobs/depolarizer_channel.py b/cirq/contrib/jobs/depolarizer_channel.py index 19e942bd210..a7d256ebd0d 100644 --- a/cirq/contrib/jobs/depolarizer_channel.py +++ b/cirq/contrib/jobs/depolarizer_channel.py @@ -15,6 +15,7 @@ """Error simulator that adds randomly activated error gates after every moment. """ +import copy import numpy as np from cirq.circuits.circuit import Circuit @@ -107,12 +108,8 @@ def transform_job(self, job): errors = np.random.random(self.realizations) < self.p if any(errors): key = self._parameter_name + str(error_number) - if isinstance(gate, xmon_gates.ExpWGate): - new_error_gate = xmon_gates.ExpWGate( - half_turns=Symbol(key), - axis_half_turns=gate.axis_half_turns) - else: - new_error_gate = type(gate)(half_turns=Symbol(key)) + new_error_gate = copy.deepcopy(gate) + new_error_gate.half_turns = Symbol(key) error_gates.append(new_error_gate.on(q)) error_sweep += Points(key, list(errors * 1.0)) error_number += 1