diff --git a/cirq/ops/common_gates.py b/cirq/ops/common_gates.py index c32e1afa39e..bbb7b630006 100644 --- a/cirq/ops/common_gates.py +++ b/cirq/ops/common_gates.py @@ -134,13 +134,16 @@ def controlled(self, This behavior only occurs when the last control qubit is a default-type control qubit. A default-type control qubit is one with shape of 2 (not a generic qudit) and where the control is satisfied by the qubit being - ON, as opposed to OFF. + ON, as opposed to OFF. Note also that this only transforms into a + CXPowGate (or controlled version of that gate) if the global shift on + the XPowGate is 0, otherwise it produces a normal ControlledGate. (Note that a CXPowGate is, by definition, a controlled-XPowGate.) """ result = super().controlled(num_controls, control_values, control_qid_shape) - if (isinstance(result, controlled_gate.ControlledGate) and + if (self._global_shift == 0 and + isinstance(result, controlled_gate.ControlledGate) and result.control_values[-1] == (1,) and result.control_qid_shape[-1] == 2): return cirq.CXPowGate(exponent=self._exponent, @@ -452,13 +455,16 @@ def controlled(self, This behavior only occurs when the last control qubit is a default-type control qubit. A default-type control qubit is one with shape of 2 (not a generic qudit) and where the control is satisfied by the qubit being - ON, as opposed to OFF. + ON, as opposed to OFF. Note also that this only transforms into a + CZPowGate (or controlled version of that gate) if the global shift of + the ZPowGate is 0, otherwise it produces a normal ControlledGate. (Note that a CZPowGate is, by definition, a controlled-ZPowGate.) """ result = super().controlled(num_controls, control_values, control_qid_shape) - if (isinstance(result, controlled_gate.ControlledGate) and + if (self._global_shift == 0 and + isinstance(result, controlled_gate.ControlledGate) and result.control_values[-1] == (1,) and result.control_qid_shape[-1] == 2): return cirq.CZPowGate(exponent=self._exponent, @@ -803,13 +809,16 @@ def controlled(self, This behavior only occurs when the last control qubit is a default-type control qubit. A default-type control qubit is one with shape of 2 (not a generic qudit) and where the control is satisfied by the qubit being - ON, as opposed to OFF. + ON, as opposed to OFF. Note also that this only transforms into a + CCZPowGate (or controlled version of that gate) if the global shift of + the CZPowGate is 0, otherwise it produces a normal ControlledGate. (Note that a CCZPowGate is, by definition, a controlled-CZPowGate.) """ result = super().controlled(num_controls, control_values, control_qid_shape) - if (isinstance(result, controlled_gate.ControlledGate) and + if (self._global_shift == 0 and + isinstance(result, controlled_gate.ControlledGate) and result.control_values[-1] == (1,) and result.control_qid_shape[-1] == 2): return cirq.CCZPowGate(exponent=self._exponent, @@ -969,13 +978,16 @@ def controlled(self, This behavior only occurs when the last control qubit is a default-type control qubit. A default-type control qubit is one with shape of 2 (not a generic qudit) and where the control is satisfied by the qubit being - ON, as opposed to OFF. + ON, as opposed to OFF. Note also that this only transforms into a + CCXPowGate (or controlled version of that gate) if the global shift of + the CXPowGate is 0, otherwise it produces a normal ControlledGate. (Note that a CCXPowGate is, by definition, a controlled-CXPowGate.) """ result = super().controlled(num_controls, control_values, control_qid_shape) - if (isinstance(result, controlled_gate.ControlledGate) and + if (self._global_shift == 0 and + isinstance(result, controlled_gate.ControlledGate) and result.control_values[-1] == (1,) and result.control_qid_shape[-1] == 2): return cirq.CCXPowGate(exponent=self._exponent, diff --git a/cirq/ops/common_gates_test.py b/cirq/ops/common_gates_test.py index 7a268572e00..c6610c09701 100644 --- a/cirq/ops/common_gates_test.py +++ b/cirq/ops/common_gates_test.py @@ -183,6 +183,35 @@ def test_specialized_control(input_gate, specialized_output): input_gate, num_controls=3, control_qid_shape=(3, 2, 4)) +@pytest.mark.parametrize( + 'gate, specialized_type', + [(cirq.ZPowGate(global_shift=-0.5, exponent=0.5), cirq.CZPowGate), + (cirq.CZPowGate(global_shift=-0.5, exponent=0.5), cirq.CCZPowGate), + (cirq.XPowGate(global_shift=-0.5, exponent=0.5), cirq.CXPowGate), + (cirq.CXPowGate(global_shift=-0.5, exponent=0.5), cirq.CCXPowGate)]) +def test_no_specialized_control_for_global_shift_non_zero( + gate, specialized_type): + assert not isinstance(gate.controlled(), specialized_type) + + +@pytest.mark.parametrize( + 'gate, matrix', + [(cirq.ZPowGate(global_shift=-0.5, exponent=1), np.diag([1, 1, -1j, 1j])), + (cirq.CZPowGate(global_shift=-0.5, + exponent=1), np.diag([1, 1, 1, 1, -1j, -1j, -1j, 1j])), + (cirq.XPowGate(global_shift=-0.5, exponent=1), + np.block([[np.eye(2), np.zeros( + (2, 2))], [np.zeros( + (2, 2)), np.array([[0, -1j], [-1j, 0]])]])), + (cirq.CXPowGate(global_shift=-0.5, exponent=1), + np.block([[np.diag([1, 1, 1, 1, -1j, -1j]), + np.zeros((6, 2))], + [np.zeros( + (2, 6)), np.array([[0, -1j], [-1j, 0]])]]))]) +def test_global_phase_controlled_gate(gate, matrix): + np.testing.assert_equal(cirq.unitary(gate.controlled()), matrix) + + def test_rot_gates_eq(): eq = cirq.testing.EqualsTester() gates = [