From 775352859d9b1b88251501801e4e1680d4b98a40 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Mon, 18 Nov 2019 15:57:45 -0800 Subject: [PATCH 01/11] Add cirq.PhasedXZPowGate - Add cirq.CircuitDiagramInfoArgs.format_real helper method This gives a canonical single qubit operation to optimize into when doing microwave rotations with virtual Zs --- cirq/__init__.py | 1 + cirq/ops/__init__.py | 3 + cirq/ops/phased_x_z_gate.py | 182 +++++++++++ cirq/ops/phased_x_z_gate_test.py | 291 ++++++++++++++++++ .../circuit_diagram_info_protocol.py | 10 + .../circuit_diagram_info_protocol_test.py | 19 ++ .../json_test_data/PhasedXZPowGate.json | 8 + .../json_test_data/PhasedXZPowGate.repr | 3 + docs/api.rst | 1 + 9 files changed, 518 insertions(+) create mode 100644 cirq/ops/phased_x_z_gate.py create mode 100644 cirq/ops/phased_x_z_gate_test.py create mode 100644 cirq/protocols/json_test_data/PhasedXZPowGate.json create mode 100644 cirq/protocols/json_test_data/PhasedXZPowGate.repr diff --git a/cirq/__init__.py b/cirq/__init__.py index 3d902180d15..fce990a19e3 100644 --- a/cirq/__init__.py +++ b/cirq/__init__.py @@ -234,6 +234,7 @@ PhaseGradientGate, PhasedISwapPowGate, PhasedXPowGate, + PhasedXZPowGate, PhaseFlipChannel, QFT, Qid, diff --git a/cirq/ops/__init__.py b/cirq/ops/__init__.py index 2427b94a49b..4c689071f4f 100644 --- a/cirq/ops/__init__.py +++ b/cirq/ops/__init__.py @@ -193,6 +193,9 @@ from cirq.ops.phased_x_gate import ( PhasedXPowGate,) +from cirq.ops.phased_x_z_gate import ( + PhasedXZPowGate,) + from cirq.ops.raw_types import ( Gate, Operation, diff --git a/cirq/ops/phased_x_z_gate.py b/cirq/ops/phased_x_z_gate.py new file mode 100644 index 00000000000..8309a488872 --- /dev/null +++ b/cirq/ops/phased_x_z_gate.py @@ -0,0 +1,182 @@ +import numbers +from typing import Union, TYPE_CHECKING, Tuple, Optional, cast, Sequence + +import numpy as np +import sympy + +from cirq import value, ops, protocols, linalg +from cirq.ops import gate_features +from cirq._compat import proper_repr + +if TYPE_CHECKING: + import cirq + + +@value.value_equality(approximate=True) +class PhasedXZPowGate(gate_features.SingleQubitGate): + """A single qubit operation expressed as $Z^z Z^a X^x Z^{-a}$. + + The axis phase exponent (a) decides which axis in the XY plane to rotate + around. The amount of rotation around that axis is decided by the x + exponent (x). Then the z exponent (z) decides how much to phase the qubit. + """ + + def __init__(self, + *, + x_exponent: Union[numbers.Real, sympy.Basic], + z_exponent: Union[numbers.Real, sympy.Basic], + axis_phase_exponent: Union[numbers.Real, sympy.Basic] + ) -> None: + """ + Args: + x_exponent: The $x$ in $Z^z Z^a X^x Z^{-a}$. + z_exponent: The $z$ in $Z^z Z^a X^x Z^{-a}$. Note that the $Z^z$ + operation happens last. + axis_phase_exponent: The $a$ $Z^z Z^a X^x Z^{-a}$. + """ + self._x_exponent = x_exponent + self._z_exponent = z_exponent + self._axis_phase_exponent = axis_phase_exponent + + def _canonical(self) -> 'cirq.PhasedXZPowGate': + x = self.x_exponent + z = self.z_exponent + a = self.axis_phase_exponent + + # Canonicalize X exponent (-1, +1]. + if isinstance(x, numbers.Real): + x %= 2 + if x > 1: + x -= 2 + # Axis phase exponent is irrelevant if there is no X exponent. + if x == 0: + a = 0 + # For 180 degree X rotations, the axis phase and z exponent overlap. + if x == 1 and z != 0: + a -= z / 2 + z = 0 + + # Canonicalize Z exponent (-1, +1]. + if isinstance(z, numbers.Real): + z %= 2 + if z > 1: + z -= 2 + + # Canonicalize axis phase exponent into (-0.5, +0.5]. + if isinstance(a, numbers.Real): + a %= 2 + if a > 1: + a -= 2 + if a <= -0.5: + a += 1 + x = -x + elif a > 0.5: + a -= 1 + x = -x + + return PhasedXZPowGate(x_exponent=x, z_exponent=z, axis_phase_exponent=a) + + @property + def x_exponent(self) -> Union[numbers.Real, sympy.Basic]: + return self._x_exponent + + @property + def z_exponent(self) -> Union[numbers.Real, sympy.Basic]: + return self._z_exponent + + @property + def axis_phase_exponent(self) -> Union[numbers.Real, sympy.Basic]: + return self._axis_phase_exponent + + def _value_equality_values_(self): + c = self._canonical() + return ( + value.PeriodicValue(c._x_exponent, 2), + value.PeriodicValue(c._z_exponent, 2), + value.PeriodicValue(c._axis_phase_exponent, 2), + ) + + @staticmethod + def from_matrix(mat: np.array) -> 'cirq.PhasedXZPowGate': + pre_phase, rotation, post_phase = ( + linalg.deconstruct_single_qubit_matrix_into_angles(mat)) + pre_phase /= np.pi + post_phase /= np.pi + rotation /= np.pi + pre_phase -= 0.5 + post_phase += 0.5 + return PhasedXZPowGate( + x_exponent=rotation, + axis_phase_exponent=-pre_phase, + z_exponent=post_phase + pre_phase + )._canonical() + + def _qasm_(self, args: 'cirq.QasmArgs', + qubits: Tuple['cirq.Qid', ...]) -> Optional[str]: + from cirq.circuits import qasm_output + qasm_gate = qasm_output.QasmUGate( + lmda=0.5 - self._axis_phase_exponent, + theta=self._x_exponent, + phi=self._z_exponent + self._axis_phase_exponent - 0.5) + return protocols.qasm(qasm_gate, args=args, qubits=qubits) + + def _decompose_(self, qubits: Sequence['cirq.Qid']) -> 'cirq.OP_TREE': + q = qubits[0] + yield ops.Z(q)**-self._axis_phase_exponent + yield ops.X(q)**self._x_exponent + yield ops.Z(q)**(self._z_exponent + self._axis_phase_exponent) + + def __pow__(self, exponent: Union[float, int]) -> 'PhasedXZPowGate': + if exponent == 1: + return self + if exponent == -1: + return PhasedXZPowGate( + x_exponent=-self._x_exponent, + z_exponent=-self._z_exponent, + axis_phase_exponent=self._z_exponent + self.axis_phase_exponent, + ) + return NotImplemented + + def _is_parameterized_(self) -> bool: + """See `cirq.SupportsParameterization`.""" + return (protocols.is_parameterized(self._x_exponent) or + protocols.is_parameterized(self._z_exponent) or + protocols.is_parameterized(self._axis_phase_exponent)) + + def _resolve_parameters_(self, param_resolver) -> 'cirq.PhasedXZPowGate': + """See `cirq.SupportsParameterization`.""" + return PhasedXZPowGate( + z_exponent=param_resolver.value_of(self._z_exponent), + x_exponent=param_resolver.value_of(self._x_exponent), + axis_phase_exponent=param_resolver.value_of( + self._axis_phase_exponent), + ) + + def _phase_by_(self, phase_turns, qubit_index) -> 'cirq.PhasedXZPowGate': + """See `cirq.SupportsPhase`.""" + assert qubit_index == 0 + return PhasedXZPowGate( + x_exponent=self._x_exponent, + z_exponent=self._z_exponent, + axis_phase_exponent=self._axis_phase_exponent + phase_turns * 2) + + def _circuit_diagram_info_(self, args: 'cirq.CircuitDiagramInfoArgs' + ) -> str: + """See `cirq.SupportsCircuitDiagramInfo`.""" + return (f'PhXZ(' + f'p={args.format_real(self._axis_phase_exponent)},' + f'x={args.format_real(self._x_exponent)},' + f'z={args.format_real(self._z_exponent)})') + + def __str__(self): + return protocols.circuit_diagram_info(self).wire_symbols[0] + + def __repr__(self): + return (f'cirq.PhasedXZPowGate(' + f'axis_phase_exponent={proper_repr(self._axis_phase_exponent)},' + f' x_exponent={proper_repr(self._x_exponent)}, ' + f'z_exponent={proper_repr(self._z_exponent)})') + + def _json_dict_(self): + return protocols.obj_to_dict_helper( + self, ['axis_phase_exponent', 'x_exponent', 'z_exponent']) diff --git a/cirq/ops/phased_x_z_gate_test.py b/cirq/ops/phased_x_z_gate_test.py new file mode 100644 index 00000000000..ce971a63e87 --- /dev/null +++ b/cirq/ops/phased_x_z_gate_test.py @@ -0,0 +1,291 @@ +import numbers +import random +from typing import Union, TYPE_CHECKING, Tuple, Optional, cast, Sequence + +import numpy as np +import sympy + +import cirq + + +def test_init_properties(): + g = cirq.PhasedXZPowGate( + x_exponent=0.125, + z_exponent=0.25, + axis_phase_exponent=0.375) + assert g.x_exponent == 0.125 + assert g.z_exponent == 0.25 + assert g.axis_phase_exponent == 0.375 + + +def test_eq(): + eq = cirq.testing.EqualsTester() + eq.make_equality_group(lambda: cirq.PhasedXZPowGate( + x_exponent=0.25, + z_exponent=0.5, + axis_phase_exponent=0.75)) + + # Sensitive to each parameter. + eq.add_equality_group(cirq.PhasedXZPowGate( + x_exponent=0, + z_exponent=0.5, + axis_phase_exponent=0.75)) + eq.add_equality_group(cirq.PhasedXZPowGate( + x_exponent=0.25, + z_exponent=0, + axis_phase_exponent=0.75)) + eq.add_equality_group(cirq.PhasedXZPowGate( + x_exponent=0.25, + z_exponent=0.5, + axis_phase_exponent=0)) + + # Different from other gates. + eq.add_equality_group(cirq.PhasedXPowGate( + exponent=0.25, + phase_exponent=0.75)) + eq.add_equality_group(cirq.X) + eq.add_equality_group(cirq.PhasedXZPowGate( + x_exponent=1, + z_exponent=0, + axis_phase_exponent=0)) + + +def test_canonicalization(): + def f(x, z, a): + return cirq.PhasedXZPowGate(x_exponent=x, z_exponent=z, axis_phase_exponent=a) + + # Canonicalizations are equivalent. + eq = cirq.testing.EqualsTester() + eq.add_equality_group( + f(-1, 0, 0), + f(-3, 0, 0), + f(1, 1, 0.5)) + + """ + # Canonicalize X exponent (-1, +1]. + if isinstance(x, numbers.Real): + x %= 2 + if x > 1: + x -= 2 + # Axis phase exponent is irrelevant if there is no X exponent. + # Canonicalize Z exponent (-1, +1]. + if isinstance(z, numbers.Real): + z %= 2 + if z > 1: + z -= 2 + + # Canonicalize axis phase exponent into (-0.5, +0.5]. + if isinstance(a, numbers.Real): + a %= 2 + if a > 1: + a -= 2 + if a <= -0.5: + a += 1 + x = -x + elif a > 0.5: + a -= 1 + x = -x + """ + + # X rotation gets canonicalized. + t = f(3, 0, 0)._canonical() + assert t.x_exponent == 1 + assert t.z_exponent == 0 + assert t.axis_phase_exponent == 0 + t = f(1.5, 0, 0)._canonical() + assert t.x_exponent == -0.5 + assert t.z_exponent == 0 + assert t.axis_phase_exponent == 0 + + # Z rotation gets canonicalized. + t = f(0, 3, 0)._canonical() + assert t.x_exponent == 0 + assert t.z_exponent == 1 + assert t.axis_phase_exponent == 0 + t = f(0, 1.5, 0)._canonical() + assert t.x_exponent == 0 + assert t.z_exponent == -0.5 + assert t.axis_phase_exponent == 0 + + # Axis phase gets canonicalized. + t = f(0.5, 0, 2.25)._canonical() + assert t.x_exponent == 0.5 + assert t.z_exponent == 0 + assert t.axis_phase_exponent == 0.25 + t = f(0.5, 0, 1.25)._canonical() + assert t.x_exponent == -0.5 + assert t.z_exponent == 0 + assert t.axis_phase_exponent == 0.25 + t = f(0.5, 0, 0.75)._canonical() + assert t.x_exponent == -0.5 + assert t.z_exponent == 0 + assert t.axis_phase_exponent == -0.25 + + # 180 degree rotations don't need a virtual Z. + t = f(1, 1, 0.5)._canonical() + assert t.x_exponent == 1 + assert t.z_exponent == 0 + assert t.axis_phase_exponent == 0 + t = f(1, 0.25, 0.5)._canonical() + assert t.x_exponent == 1 + assert t.z_exponent == 0 + assert t.axis_phase_exponent == 0.375 + + # Axis phase is irrelevant when not rotating. + t = f(0, 0.25, 0.5)._canonical() + assert t.x_exponent == 0 + assert t.z_exponent == 0.25 + assert t.axis_phase_exponent == 0 + + +def test_from_matrix(): + # Axis rotations. + assert cirq.approx_eq( + cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.X**0.1)), + cirq.PhasedXZPowGate( + x_exponent=0.1, + z_exponent=0, + axis_phase_exponent=0), + atol=1e-8) + assert cirq.approx_eq( + cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.X**-0.1)), + cirq.PhasedXZPowGate( + x_exponent=-0.1, + z_exponent=0, + axis_phase_exponent=0), + atol=1e-8) + assert cirq.approx_eq( + cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.Y**0.1)), + cirq.PhasedXZPowGate( + x_exponent=0.1, + z_exponent=0, + axis_phase_exponent=0.5), + atol=1e-8) + assert cirq.approx_eq( + cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.Y**-0.1)), + cirq.PhasedXZPowGate( + x_exponent=-0.1, + z_exponent=0, + axis_phase_exponent=0.5), + atol=1e-8) + assert cirq.approx_eq( + cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.Z**-0.1)), + cirq.PhasedXZPowGate( + x_exponent=0, + z_exponent=-0.1, + axis_phase_exponent=0), + atol=1e-8) + assert cirq.approx_eq( + cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.Z**0.1)), + cirq.PhasedXZPowGate( + x_exponent=0, + z_exponent=0.1, + axis_phase_exponent=0), + atol=1e-8) + + # Pauli matrices. + assert cirq.approx_eq( + cirq.PhasedXZPowGate.from_matrix(np.eye(2)), + cirq.PhasedXZPowGate( + x_exponent=0, + z_exponent=0, + axis_phase_exponent=0), + atol=1e-8) + assert cirq.approx_eq( + cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.X)), + cirq.PhasedXZPowGate( + x_exponent=1, + z_exponent=0, + axis_phase_exponent=0), + atol=1e-8) + assert cirq.approx_eq( + cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.Y)), + cirq.PhasedXZPowGate( + x_exponent=1, + z_exponent=0, + axis_phase_exponent=0.5), + atol=1e-8) + assert cirq.approx_eq( + cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.Z)), + cirq.PhasedXZPowGate( + x_exponent=0, + z_exponent=1, + axis_phase_exponent=0), + atol=1e-8) + + # Round trips. + a = random.random() + b = random.random() + c = random.random() + g = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=b, axis_phase_exponent=c) + assert cirq.approx_eq( + cirq.PhasedXZPowGate.from_matrix(cirq.unitary(g)), + g, + atol=1e-8) + u = cirq.testing.random_unitary(2) + cirq.testing.assert_allclose_up_to_global_phase( + cirq.unitary(cirq.PhasedXZPowGate.from_matrix(u)), + u, + atol=1e-8) + + +def test_protocols(): + a = random.random() + b = random.random() + c = random.random() + g = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=b, axis_phase_exponent=c) + cirq.testing.assert_implements_consistent_protocols(g) + + # Symbolic. + t = sympy.Symbol('t') + g = cirq.PhasedXZPowGate(x_exponent=t, z_exponent=b, axis_phase_exponent=c) + cirq.testing.assert_implements_consistent_protocols(g) + g = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=t, axis_phase_exponent=c) + cirq.testing.assert_implements_consistent_protocols(g) + g = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=b, axis_phase_exponent=t) + cirq.testing.assert_implements_consistent_protocols(g) + + +def test_inverse(): + a = random.random() + b = random.random() + c = random.random() + q = cirq.LineQubit(0) + g = cirq.PhasedXZPowGate( + x_exponent=a, z_exponent=b, axis_phase_exponent=c).on(q) + + cirq.testing.assert_allclose_up_to_global_phase( + cirq.unitary(g**-1), + np.transpose(np.conjugate(cirq.unitary(g))), + atol=1e-8) + + +def test_parameterized(): + a = random.random() + b = random.random() + c = random.random() + g = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=b, axis_phase_exponent=c) + assert not cirq.is_parameterized(g) + + t = sympy.Symbol('t') + gt = cirq.PhasedXZPowGate(x_exponent=t, z_exponent=b, axis_phase_exponent=c) + assert cirq.is_parameterized(gt) + assert cirq.resolve_parameters(gt, {'t': a}) == g + gt = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=t, axis_phase_exponent=c) + assert cirq.is_parameterized(gt) + assert cirq.resolve_parameters(gt, {'t': b}) == g + gt = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=b, axis_phase_exponent=t) + assert cirq.is_parameterized(gt) + assert cirq.resolve_parameters(gt, {'t': c}) == g + + +def test_str_diagram(): + g = cirq.PhasedXZPowGate(x_exponent=0.5, z_exponent=0.25, axis_phase_exponent=0.125) + + assert str(g) == "PhXZ(p=0.125,x=0.5,z=0.25)" + + cirq.testing.assert_has_diagram(cirq.Circuit( + g.on(cirq.LineQubit(0)) + ), """ +0: ───PhXZ(p=0.125,x=0.5,z=0.25)─── + """) diff --git a/cirq/protocols/circuit_diagram_info_protocol.py b/cirq/protocols/circuit_diagram_info_protocol.py index 3326dded566..dc1c6337ca6 100644 --- a/cirq/protocols/circuit_diagram_info_protocol.py +++ b/cirq/protocols/circuit_diagram_info_protocol.py @@ -15,6 +15,7 @@ from typing import (Any, TYPE_CHECKING, Optional, Union, TypeVar, Dict, overload, Iterable) +import sympy from typing_extensions import Protocol from cirq import value @@ -133,6 +134,15 @@ def __repr__(self): self.use_unicode_characters, self.precision, self.qubit_map)) + def format_real(self, val: Union[sympy.Basic, int, float]): + if isinstance(val, sympy.Basic): + return str(val) + if val == int(val): + return str(int(val)) + if self.precision is None: + return str(val) + return '{{:.{}}}'.format(self.precision).format(float(val)) + def copy(self): return self.__class__( known_qubits=self.known_qubits, diff --git a/cirq/protocols/circuit_diagram_info_protocol_test.py b/cirq/protocols/circuit_diagram_info_protocol_test.py index f4bf36b4e78..bdec2962b2d 100644 --- a/cirq/protocols/circuit_diagram_info_protocol_test.py +++ b/cirq/protocols/circuit_diagram_info_protocol_test.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import pytest +import sympy import cirq @@ -170,3 +171,21 @@ def test_circuit_diagram_info_args_repr(): cirq.LineQubit(0): 5, cirq.LineQubit(1): 7 })) + + +def test_formal_real(): + args = cirq.CircuitDiagramInfoArgs.UNINFORMED_DEFAULT + assert args.format_real(1) == '1' + assert args.format_real(1.1) == '1.1' + assert args.format_real(1.234567) == '1.23' + assert args.format_real(1 / 7) == '0.143' + assert args.format_real(sympy.Symbol('t')) == 't' + assert args.format_real(sympy.Symbol('t') * 2 + 1) == '2*t + 1' + + args.precision = None + assert args.format_real(1) == '1' + assert args.format_real(1.1) == '1.1' + assert args.format_real(1.234567) == '1.234567' + assert args.format_real(1 / 7) == repr(1 / 7) + assert args.format_real(sympy.Symbol('t')) == 't' + assert args.format_real(sympy.Symbol('t') * 2 + 1) == '2*t + 1' diff --git a/cirq/protocols/json_test_data/PhasedXZPowGate.json b/cirq/protocols/json_test_data/PhasedXZPowGate.json new file mode 100644 index 00000000000..9717cc9bec7 --- /dev/null +++ b/cirq/protocols/json_test_data/PhasedXZPowGate.json @@ -0,0 +1,8 @@ +[ + { + "cirq_type": "PhasedXZPowGate", + "axis_phase_exponent": 0.75, + "x_exponent": 0.5, + "z_exponent": 0.25 + } +] \ No newline at end of file diff --git a/cirq/protocols/json_test_data/PhasedXZPowGate.repr b/cirq/protocols/json_test_data/PhasedXZPowGate.repr new file mode 100644 index 00000000000..8099b575d47 --- /dev/null +++ b/cirq/protocols/json_test_data/PhasedXZPowGate.repr @@ -0,0 +1,3 @@ +[ +cirq.PhasedXZPowGate(axis_phase_exponent=0.75, x_exponent=0.5, z_exponent=0.25), +] \ No newline at end of file diff --git a/docs/api.rst b/docs/api.rst index fa6d8444f70..c1b9ee72ae5 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -90,6 +90,7 @@ Unitary effects that can be applied to one or more qubits. cirq.PhaseGradientGate cirq.PhasedISwapPowGate cirq.PhasedXPowGate + cirq.PhasedXZPowGate cirq.QuantumFourierTransformGate cirq.SingleQubitGate cirq.SingleQubitMatrixGate From 68c6f43d3523a4ffc213a97171f0444160a3d68f Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Mon, 18 Nov 2019 16:24:05 -0800 Subject: [PATCH 02/11] format, lint --- cirq/ops/phased_x_z_gate.py | 41 +++--- cirq/ops/phased_x_z_gate_test.py | 216 ++++++++++++++----------------- 2 files changed, 117 insertions(+), 140 deletions(-) diff --git a/cirq/ops/phased_x_z_gate.py b/cirq/ops/phased_x_z_gate.py index 8309a488872..e837990120a 100644 --- a/cirq/ops/phased_x_z_gate.py +++ b/cirq/ops/phased_x_z_gate.py @@ -1,5 +1,5 @@ import numbers -from typing import Union, TYPE_CHECKING, Tuple, Optional, cast, Sequence +from typing import Union, TYPE_CHECKING, Tuple, Optional, Sequence import numpy as np import sympy @@ -21,12 +21,9 @@ class PhasedXZPowGate(gate_features.SingleQubitGate): exponent (x). Then the z exponent (z) decides how much to phase the qubit. """ - def __init__(self, - *, - x_exponent: Union[numbers.Real, sympy.Basic], + def __init__(self, *, x_exponent: Union[numbers.Real, sympy.Basic], z_exponent: Union[numbers.Real, sympy.Basic], - axis_phase_exponent: Union[numbers.Real, sympy.Basic] - ) -> None: + axis_phase_exponent: Union[numbers.Real, sympy.Basic]) -> None: """ Args: x_exponent: The $x$ in $Z^z Z^a X^x Z^{-a}$. @@ -74,7 +71,9 @@ def _canonical(self) -> 'cirq.PhasedXZPowGate': a -= 1 x = -x - return PhasedXZPowGate(x_exponent=x, z_exponent=z, axis_phase_exponent=a) + return PhasedXZPowGate(x_exponent=x, + z_exponent=z, + axis_phase_exponent=a) @property def x_exponent(self) -> Union[numbers.Real, sympy.Basic]: @@ -105,19 +104,17 @@ def from_matrix(mat: np.array) -> 'cirq.PhasedXZPowGate': rotation /= np.pi pre_phase -= 0.5 post_phase += 0.5 - return PhasedXZPowGate( - x_exponent=rotation, - axis_phase_exponent=-pre_phase, - z_exponent=post_phase + pre_phase - )._canonical() + return PhasedXZPowGate(x_exponent=rotation, + axis_phase_exponent=-pre_phase, + z_exponent=post_phase + pre_phase)._canonical() def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]: from cirq.circuits import qasm_output - qasm_gate = qasm_output.QasmUGate( - lmda=0.5 - self._axis_phase_exponent, - theta=self._x_exponent, - phi=self._z_exponent + self._axis_phase_exponent - 0.5) + qasm_gate = qasm_output.QasmUGate(lmda=0.5 - self._axis_phase_exponent, + theta=self._x_exponent, + phi=self._z_exponent + + self._axis_phase_exponent - 0.5) return protocols.qasm(qasm_gate, args=args, qubits=qubits) def _decompose_(self, qubits: Sequence['cirq.Qid']) -> 'cirq.OP_TREE': @@ -155,13 +152,13 @@ def _resolve_parameters_(self, param_resolver) -> 'cirq.PhasedXZPowGate': def _phase_by_(self, phase_turns, qubit_index) -> 'cirq.PhasedXZPowGate': """See `cirq.SupportsPhase`.""" assert qubit_index == 0 - return PhasedXZPowGate( - x_exponent=self._x_exponent, - z_exponent=self._z_exponent, - axis_phase_exponent=self._axis_phase_exponent + phase_turns * 2) + return PhasedXZPowGate(x_exponent=self._x_exponent, + z_exponent=self._z_exponent, + axis_phase_exponent=self._axis_phase_exponent + + phase_turns * 2) - def _circuit_diagram_info_(self, args: 'cirq.CircuitDiagramInfoArgs' - ) -> str: + def _circuit_diagram_info_(self, + args: 'cirq.CircuitDiagramInfoArgs') -> str: """See `cirq.SupportsCircuitDiagramInfo`.""" return (f'PhXZ(' f'p={args.format_real(self._axis_phase_exponent)},' diff --git a/cirq/ops/phased_x_z_gate_test.py b/cirq/ops/phased_x_z_gate_test.py index ce971a63e87..b538e43b570 100644 --- a/cirq/ops/phased_x_z_gate_test.py +++ b/cirq/ops/phased_x_z_gate_test.py @@ -1,6 +1,4 @@ -import numbers import random -from typing import Union, TYPE_CHECKING, Tuple, Optional, cast, Sequence import numpy as np import sympy @@ -9,10 +7,9 @@ def test_init_properties(): - g = cirq.PhasedXZPowGate( - x_exponent=0.125, - z_exponent=0.25, - axis_phase_exponent=0.375) + g = cirq.PhasedXZPowGate(x_exponent=0.125, + z_exponent=0.25, + axis_phase_exponent=0.375) assert g.x_exponent == 0.125 assert g.z_exponent == 0.25 assert g.axis_phase_exponent == 0.375 @@ -21,46 +18,40 @@ def test_init_properties(): def test_eq(): eq = cirq.testing.EqualsTester() eq.make_equality_group(lambda: cirq.PhasedXZPowGate( - x_exponent=0.25, - z_exponent=0.5, - axis_phase_exponent=0.75)) + x_exponent=0.25, z_exponent=0.5, axis_phase_exponent=0.75)) # Sensitive to each parameter. - eq.add_equality_group(cirq.PhasedXZPowGate( - x_exponent=0, - z_exponent=0.5, - axis_phase_exponent=0.75)) - eq.add_equality_group(cirq.PhasedXZPowGate( - x_exponent=0.25, - z_exponent=0, - axis_phase_exponent=0.75)) - eq.add_equality_group(cirq.PhasedXZPowGate( - x_exponent=0.25, - z_exponent=0.5, - axis_phase_exponent=0)) + eq.add_equality_group( + cirq.PhasedXZPowGate(x_exponent=0, + z_exponent=0.5, + axis_phase_exponent=0.75)) + eq.add_equality_group( + cirq.PhasedXZPowGate(x_exponent=0.25, + z_exponent=0, + axis_phase_exponent=0.75)) + eq.add_equality_group( + cirq.PhasedXZPowGate(x_exponent=0.25, + z_exponent=0.5, + axis_phase_exponent=0)) # Different from other gates. - eq.add_equality_group(cirq.PhasedXPowGate( - exponent=0.25, - phase_exponent=0.75)) + eq.add_equality_group( + cirq.PhasedXPowGate(exponent=0.25, phase_exponent=0.75)) eq.add_equality_group(cirq.X) - eq.add_equality_group(cirq.PhasedXZPowGate( - x_exponent=1, - z_exponent=0, - axis_phase_exponent=0)) + eq.add_equality_group( + cirq.PhasedXZPowGate(x_exponent=1, z_exponent=0, axis_phase_exponent=0)) def test_canonicalization(): + def f(x, z, a): - return cirq.PhasedXZPowGate(x_exponent=x, z_exponent=z, axis_phase_exponent=a) + return cirq.PhasedXZPowGate(x_exponent=x, + z_exponent=z, + axis_phase_exponent=a) # Canonicalizations are equivalent. eq = cirq.testing.EqualsTester() - eq.add_equality_group( - f(-1, 0, 0), - f(-3, 0, 0), - f(1, 1, 0.5)) - + eq.add_equality_group(f(-1, 0, 0), f(-3, 0, 0), f(1, 1, 0.5)) """ # Canonicalize X exponent (-1, +1]. if isinstance(x, numbers.Real): @@ -140,93 +131,81 @@ def f(x, z, a): def test_from_matrix(): # Axis rotations. - assert cirq.approx_eq( - cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.X**0.1)), - cirq.PhasedXZPowGate( - x_exponent=0.1, - z_exponent=0, - axis_phase_exponent=0), - atol=1e-8) - assert cirq.approx_eq( - cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.X**-0.1)), - cirq.PhasedXZPowGate( - x_exponent=-0.1, - z_exponent=0, - axis_phase_exponent=0), - atol=1e-8) - assert cirq.approx_eq( - cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.Y**0.1)), - cirq.PhasedXZPowGate( - x_exponent=0.1, - z_exponent=0, - axis_phase_exponent=0.5), - atol=1e-8) - assert cirq.approx_eq( - cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.Y**-0.1)), - cirq.PhasedXZPowGate( - x_exponent=-0.1, - z_exponent=0, - axis_phase_exponent=0.5), - atol=1e-8) - assert cirq.approx_eq( - cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.Z**-0.1)), - cirq.PhasedXZPowGate( - x_exponent=0, - z_exponent=-0.1, - axis_phase_exponent=0), - atol=1e-8) - assert cirq.approx_eq( - cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.Z**0.1)), - cirq.PhasedXZPowGate( - x_exponent=0, - z_exponent=0.1, - axis_phase_exponent=0), - atol=1e-8) + assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix( + cirq.unitary(cirq.X**0.1)), + cirq.PhasedXZPowGate(x_exponent=0.1, + z_exponent=0, + axis_phase_exponent=0), + atol=1e-8) + assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix( + cirq.unitary(cirq.X**-0.1)), + cirq.PhasedXZPowGate(x_exponent=-0.1, + z_exponent=0, + axis_phase_exponent=0), + atol=1e-8) + assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix( + cirq.unitary(cirq.Y**0.1)), + cirq.PhasedXZPowGate(x_exponent=0.1, + z_exponent=0, + axis_phase_exponent=0.5), + atol=1e-8) + assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix( + cirq.unitary(cirq.Y**-0.1)), + cirq.PhasedXZPowGate(x_exponent=-0.1, + z_exponent=0, + axis_phase_exponent=0.5), + atol=1e-8) + assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix( + cirq.unitary(cirq.Z**-0.1)), + cirq.PhasedXZPowGate(x_exponent=0, + z_exponent=-0.1, + axis_phase_exponent=0), + atol=1e-8) + assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix( + cirq.unitary(cirq.Z**0.1)), + cirq.PhasedXZPowGate(x_exponent=0, + z_exponent=0.1, + axis_phase_exponent=0), + atol=1e-8) # Pauli matrices. - assert cirq.approx_eq( - cirq.PhasedXZPowGate.from_matrix(np.eye(2)), - cirq.PhasedXZPowGate( - x_exponent=0, - z_exponent=0, - axis_phase_exponent=0), - atol=1e-8) - assert cirq.approx_eq( - cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.X)), - cirq.PhasedXZPowGate( - x_exponent=1, - z_exponent=0, - axis_phase_exponent=0), - atol=1e-8) - assert cirq.approx_eq( - cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.Y)), - cirq.PhasedXZPowGate( - x_exponent=1, - z_exponent=0, - axis_phase_exponent=0.5), - atol=1e-8) - assert cirq.approx_eq( - cirq.PhasedXZPowGate.from_matrix(cirq.unitary(cirq.Z)), - cirq.PhasedXZPowGate( - x_exponent=0, - z_exponent=1, - axis_phase_exponent=0), - atol=1e-8) + assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix(np.eye(2)), + cirq.PhasedXZPowGate(x_exponent=0, + z_exponent=0, + axis_phase_exponent=0), + atol=1e-8) + assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix(cirq.unitary( + cirq.X)), + cirq.PhasedXZPowGate(x_exponent=1, + z_exponent=0, + axis_phase_exponent=0), + atol=1e-8) + assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix(cirq.unitary( + cirq.Y)), + cirq.PhasedXZPowGate(x_exponent=1, + z_exponent=0, + axis_phase_exponent=0.5), + atol=1e-8) + assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix(cirq.unitary( + cirq.Z)), + cirq.PhasedXZPowGate(x_exponent=0, + z_exponent=1, + axis_phase_exponent=0), + atol=1e-8) # Round trips. a = random.random() b = random.random() c = random.random() g = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=b, axis_phase_exponent=c) - assert cirq.approx_eq( - cirq.PhasedXZPowGate.from_matrix(cirq.unitary(g)), - g, - atol=1e-8) + assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix(cirq.unitary(g)), + g, + atol=1e-8) u = cirq.testing.random_unitary(2) - cirq.testing.assert_allclose_up_to_global_phase( - cirq.unitary(cirq.PhasedXZPowGate.from_matrix(u)), - u, - atol=1e-8) + cirq.testing.assert_allclose_up_to_global_phase(cirq.unitary( + cirq.PhasedXZPowGate.from_matrix(u)), + u, + atol=1e-8) def test_protocols(): @@ -251,8 +230,8 @@ def test_inverse(): b = random.random() c = random.random() q = cirq.LineQubit(0) - g = cirq.PhasedXZPowGate( - x_exponent=a, z_exponent=b, axis_phase_exponent=c).on(q) + g = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=b, + axis_phase_exponent=c).on(q) cirq.testing.assert_allclose_up_to_global_phase( cirq.unitary(g**-1), @@ -280,12 +259,13 @@ def test_parameterized(): def test_str_diagram(): - g = cirq.PhasedXZPowGate(x_exponent=0.5, z_exponent=0.25, axis_phase_exponent=0.125) + g = cirq.PhasedXZPowGate(x_exponent=0.5, + z_exponent=0.25, + axis_phase_exponent=0.125) assert str(g) == "PhXZ(p=0.125,x=0.5,z=0.25)" - cirq.testing.assert_has_diagram(cirq.Circuit( - g.on(cirq.LineQubit(0)) - ), """ + cirq.testing.assert_has_diagram( + cirq.Circuit(g.on(cirq.LineQubit(0))), """ 0: ───PhXZ(p=0.125,x=0.5,z=0.25)─── """) From 575a7ba25a395931ce368fa8817e08dc4da5a712 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Mon, 18 Nov 2019 16:55:24 -0800 Subject: [PATCH 03/11] missing entry --- cirq/protocols/json.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cirq/protocols/json.py b/cirq/protocols/json.py index 51bdc9c4126..517af624d42 100644 --- a/cirq/protocols/json.py +++ b/cirq/protocols/json.py @@ -105,6 +105,7 @@ def _identity_operation_from_dict(qubits, **kwargs): 'PhaseGradientGate': cirq.PhaseGradientGate, 'PhasedISwapPowGate': cirq.PhasedISwapPowGate, 'PhasedXPowGate': cirq.PhasedXPowGate, + 'PhasedXZPowGate': cirq.PhasedXZPowGate, 'QuantumFourierTransformGate': cirq.QuantumFourierTransformGate, 'ResetChannel': cirq.ResetChannel, 'SingleQubitMatrixGate': cirq.SingleQubitMatrixGate, From aed306e4697c4f48afd1e32c222e05a8448f43fd Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Mon, 18 Nov 2019 21:13:43 -0800 Subject: [PATCH 04/11] polish --- cirq/ops/phased_x_z_gate.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cirq/ops/phased_x_z_gate.py b/cirq/ops/phased_x_z_gate.py index e837990120a..c94d602bdcf 100644 --- a/cirq/ops/phased_x_z_gate.py +++ b/cirq/ops/phased_x_z_gate.py @@ -29,7 +29,7 @@ def __init__(self, *, x_exponent: Union[numbers.Real, sympy.Basic], x_exponent: The $x$ in $Z^z Z^a X^x Z^{-a}$. z_exponent: The $z$ in $Z^z Z^a X^x Z^{-a}$. Note that the $Z^z$ operation happens last. - axis_phase_exponent: The $a$ $Z^z Z^a X^x Z^{-a}$. + axis_phase_exponent: The $a$ in $Z^z Z^a X^x Z^{-a}$. """ self._x_exponent = x_exponent self._z_exponent = z_exponent @@ -40,7 +40,7 @@ def _canonical(self) -> 'cirq.PhasedXZPowGate': z = self.z_exponent a = self.axis_phase_exponent - # Canonicalize X exponent (-1, +1]. + # Canonicalize X exponent into (-1, +1]. if isinstance(x, numbers.Real): x %= 2 if x > 1: @@ -53,7 +53,7 @@ def _canonical(self) -> 'cirq.PhasedXZPowGate': a -= z / 2 z = 0 - # Canonicalize Z exponent (-1, +1]. + # Canonicalize Z exponent into (-1, +1]. if isinstance(z, numbers.Real): z %= 2 if z > 1: From f395ad439ed42cf5475e5e85c8068f55d5d7c062 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Mon, 18 Nov 2019 23:25:34 -0800 Subject: [PATCH 05/11] Implement decompose_two_qubit_interaction_into_four_fsim_gates_via_bdecompose - This code is not very performant, but it does the job --- cirq/__init__.py | 1 + cirq/linalg/decompositions.py | 22 ++- cirq/linalg/decompositions_test.py | 10 ++ cirq/ops/fsim_gate.py | 4 +- cirq/optimizers/__init__.py | 3 + cirq/optimizers/two_qubit_to_fsim.py | 209 ++++++++++++++++++++++ cirq/optimizers/two_qubit_to_fsim_test.py | 43 +++++ 7 files changed, 284 insertions(+), 8 deletions(-) create mode 100644 cirq/optimizers/two_qubit_to_fsim.py create mode 100644 cirq/optimizers/two_qubit_to_fsim_test.py diff --git a/cirq/__init__.py b/cirq/__init__.py index fce990a19e3..3a4eb5d5928 100644 --- a/cirq/__init__.py +++ b/cirq/__init__.py @@ -278,6 +278,7 @@ from cirq.optimizers import ( ConvertToCzAndSingleGates, + decompose_two_qubit_interaction_into_four_fsim_gates_via_b, DropEmptyMoments, DropNegligible, EjectPhasedPaulis, diff --git a/cirq/linalg/decompositions.py b/cirq/linalg/decompositions.py index 9413920d5d1..f442212ad6a 100644 --- a/cirq/linalg/decompositions.py +++ b/cirq/linalg/decompositions.py @@ -430,10 +430,12 @@ class KakDecomposition: def __init__(self, *, - global_phase: complex, - single_qubit_operations_before: Tuple[np.ndarray, np.ndarray], + global_phase: complex = complex(1), + single_qubit_operations_before: Optional[ + Tuple[np.ndarray, np.ndarray]] = None, interaction_coefficients: Tuple[float, float, float], - single_qubit_operations_after: Tuple[np.ndarray, np.ndarray]): + single_qubit_operations_after: Optional[ + Tuple[np.ndarray, np.ndarray]] = None): """Initializes a decomposition for a two-qubit operation U. U = g · (a1 ⊗ a0) · exp(i·(x·XX + y·YY + z·ZZ)) · (b1 ⊗ b0) @@ -444,10 +446,18 @@ def __init__(self, interaction_coefficients: x, y, z from the above equation. single_qubit_operations_after: a0, a1 from the above equation. """ - self.global_phase = global_phase - self.single_qubit_operations_before = single_qubit_operations_before + self.global_phase: complex = global_phase + self.single_qubit_operations_before: Tuple[np.ndarray, np.ndarray] = ( + single_qubit_operations_before or ( + np.eye(2, dtype=np.complex64), + np.eye(2, dtype=np.complex64), + )) self.interaction_coefficients = interaction_coefficients - self.single_qubit_operations_after = single_qubit_operations_after + self.single_qubit_operations_after: Tuple[np.ndarray, np.ndarray] = ( + single_qubit_operations_after or ( + np.eye(2, dtype=np.complex64), + np.eye(2, dtype=np.complex64), + )) def _value_equality_values_(self): def flatten(x): diff --git a/cirq/linalg/decompositions_test.py b/cirq/linalg/decompositions_test.py index 9edbb4bb00f..0dc8e340f44 100644 --- a/cirq/linalg/decompositions_test.py +++ b/cirq/linalg/decompositions_test.py @@ -293,6 +293,16 @@ def test_kak_decomposition_eq(): single_qubit_operations_after=(np.eye(2), cirq.unitary(cirq.Z)), )) + eq.add_equality_group( + cirq.KakDecomposition( + global_phase=1, + single_qubit_operations_before=(np.eye(2), np.eye(2)), + interaction_coefficients=(0.3, 0.2, 0.1), + single_qubit_operations_after=(np.eye(2), np.eye(2)), + ), + cirq.KakDecomposition(interaction_coefficients=(0.3, 0.2, 0.1)), + ) + eq.make_equality_group(lambda: cirq.KakDecomposition( global_phase=1, single_qubit_operations_before=(cirq.unitary(cirq.X), diff --git a/cirq/ops/fsim_gate.py b/cirq/ops/fsim_gate.py index e2e9e1aecc9..510c3177838 100644 --- a/cirq/ops/fsim_gate.py +++ b/cirq/ops/fsim_gate.py @@ -66,10 +66,10 @@ def __init__(self, theta: float, phi: float): theta: Swap angle on the ``|01⟩`` ``|10⟩`` subspace, in radians. Determined by the strength and duration of the XX+YY interaction. Note: uses opposite sign convention to the - iSWAP gate. + iSWAP gate. Maximum strength (full iswap) is at pi/2. phi: Controlled phase angle, in radians. Determines how much the ``|11⟩`` state is phased. Note: uses opposite sign convention to - the CZPowGate. + the CZPowGate. Maximum strength (full cz) is at pi/2. """ self.theta = theta self.phi = phi diff --git a/cirq/optimizers/__init__.py b/cirq/optimizers/__init__.py index c971ecc1666..7033aea67dc 100644 --- a/cirq/optimizers/__init__.py +++ b/cirq/optimizers/__init__.py @@ -53,3 +53,6 @@ from cirq.optimizers.two_qubit_decompositions import ( two_qubit_matrix_to_operations,) + +from cirq.optimizers.two_qubit_to_fsim import ( + decompose_two_qubit_interaction_into_four_fsim_gates_via_b,) diff --git a/cirq/optimizers/two_qubit_to_fsim.py b/cirq/optimizers/two_qubit_to_fsim.py new file mode 100644 index 00000000000..8453130a9a2 --- /dev/null +++ b/cirq/optimizers/two_qubit_to_fsim.py @@ -0,0 +1,209 @@ +from typing import Sequence, Union, Any, List, Iterator, TYPE_CHECKING, Iterable + +import numpy as np + +from cirq import ops, linalg, circuits, devices +from cirq.optimizers import merge_single_qubit_gates, drop_empty_moments + +if TYPE_CHECKING: + import cirq + + +def decompose_two_qubit_interaction_into_four_fsim_gates_via_b( + interaction: Union['cirq.Operation', 'cirq.Gate', np.ndarray, Any], + *, + fsim_gate: 'cirq.FSimGate', + qubits: Sequence['cirq.Qid'] = None) -> 'cirq.Circuit': + """Decomposes operations into an FSimGate near theta=pi/2, phi=0. + + This decomposition is guaranteed to use exactly four of the given FSim + gates. It works by decomposing into two B gates and then decomposing each + B gate into two of the given FSim gate. + + TODO: describe the feasible angles. + + Args: + interaction: The two qubit operation to synthesize. This can either be + a cirq object (such as a gate, operation, or circuit) or a raw numpy + array specifying the 4x4 unitary matrix. + fsim_gate: The only two qubit gate that is permitted to appear in the + output. + qubits: The qubits that the resulting operations should apply the + desired interaction to. If not set then defaults to either the + qubits of the given interaction (if it is a `cirq.Operation`) or + else to `cirq.LineQubit.range(2)`. + + Returns: + A list of operations implementing the desired two qubit unitary. The + list will include four operations of the given fsim gate, various single + qubit operations, and a global phase operation. + """ + if qubits is None: + if isinstance(interaction, ops.Operation): + qubits = interaction.qubits + else: + qubits = devices.LineQubit.range(2) + if len(qubits) != 2: + raise ValueError(f'Expected a pair of qubits, but got {qubits!r}.') + kak = linalg.kak_decomposition(interaction) + + result_using_b_gates = _decompose_two_qubit_interaction_into_two_b_gates( + kak, qubits=qubits) + + b_decomposition = _decompose_b_gate_into_two_fsims(fsim_gate=fsim_gate, + qubits=qubits) + result = [] + for op in result_using_b_gates: + if isinstance(op.gate, _BGate): + result.extend(b_decomposition) + else: + result.append(op) + + circuit = circuits.Circuit(result) + merge_single_qubit_gates.MergeSingleQubitGates().optimize_circuit(circuit) + drop_empty_moments.DropEmptyMoments().optimize_circuit(circuit) + return circuit + + +def _decompose_xx_yy_into_two_fsims_ignoring_single_qubit_ops( + *, qubits: Sequence['cirq.Qid'], fsim_gate: 'cirq.FSimGate', + canonical_x_kak_coefficient: float, + canonical_y_kak_coefficient: float) -> List['cirq.Operation']: + x = canonical_x_kak_coefficient + y = canonical_y_kak_coefficient + assert 0 <= y <= x <= np.pi / 4 + + eta = np.sin(x)**2 * np.cos(y)**2 + np.cos(x)**2 * np.sin(y)**2 + xi = abs(np.sin(2 * x) * np.sin(2 * y)) + + t = fsim_gate.phi / 2 + kappa = np.sin(fsim_gate.theta)**2 - np.sin(t)**2 + s_sum = (eta - np.sin(t)**2) / kappa + s_dif = 0.5 * xi / kappa + + x_dif = np.arcsin(np.sqrt(s_sum + s_dif)) + x_sum = np.arcsin(np.sqrt(s_sum - s_dif)) + + x_a = x_sum + x_dif + x_b = x_dif - x_sum + if np.isnan(x_b): + raise ValueError( + f'Failed to synthesize XX^{x/np.pi}·YY^{y/np.pi} from two ' + f'{fsim_gate!r} separated by single qubit operations.') + + a, b = qubits + return [ + fsim_gate(a, b), + ops.Rz(t + np.pi).on(a), + ops.Rz(t).on(b), + ops.Rx(x_a).on(a), + ops.Rx(x_b).on(b), + fsim_gate(a, b), + ] + + +class _BGate(ops.Gate): + """Single qubit gates and two of these can achieve any kak coefficients. + + References: + Minimum construction of two-qubit quantum operations + https://arxiv.org/abs/quant-ph/0312193 + """ + + def num_qubits(self) -> int: + return 2 + + def _decompose_(self, qubits): + a, b = qubits + return [ + ops.XX(a, b)**0.5, + ops.YY(a, b)**0.25, + ] + + def __str__(self): + return 'B' + + +def _decompose_two_qubit_interaction_into_two_b_gates( + interaction: Union['cirq.Operation', 'cirq.Gate', np.ndarray, Any], + *, + qubits: Sequence['cirq.Qid'] = None) -> List['cirq.Operation']: + if qubits is None: + if isinstance(interaction, ops.Operation): + qubits = interaction.qubits + else: + qubits = devices.LineQubit.range(2) + if len(qubits) != 2: + raise ValueError(f'Expected a pair of qubits, but got {qubits!r}.') + kak = linalg.kak_decomposition(interaction) + + result = _decompose_interaction_into_two_b_gates_ignoring_single_qubit_ops( + qubits, kak.interaction_coefficients) + + return list( + _fix_single_qubit_gates_around_kak_interaction(desired=kak, + qubits=qubits, + operations=result)) + + +def _decompose_b_gate_into_two_fsims(*, fsim_gate: 'cirq.FSimGate', + qubits: Sequence['cirq.Qid'] + ) -> List['cirq.Operation']: + kak = linalg.kak_decomposition(_BGate()) + + result = _decompose_xx_yy_into_two_fsims_ignoring_single_qubit_ops( + qubits=qubits, + fsim_gate=fsim_gate, + canonical_x_kak_coefficient=kak.interaction_coefficients[0], + canonical_y_kak_coefficient=kak.interaction_coefficients[1]) + + return list( + _fix_single_qubit_gates_around_kak_interaction(desired=kak, + qubits=qubits, + operations=result)) + + +def _decompose_interaction_into_two_b_gates_ignoring_single_qubit_ops( + qubits: Sequence['cirq.Qid'], + kak_interaction_coefficients: Iterable[float]) -> List['cirq.Operation']: + """ + References: + Minimum construction of two-qubit quantum operations + https://arxiv.org/abs/quant-ph/0312193 + """ + a, b = qubits + x, y, z = kak_interaction_coefficients + B = _BGate() + r = (np.sin(y) * np.cos(z))**2 + b1 = np.arccos(1 - 4 * r) + b2 = np.arcsin(np.sqrt(np.cos(y * 2) * np.cos(z * 2) / (1 - 2 * r))) + s = 1 if z < 0 else -1 + return [ + B(a, b), + ops.Ry(s * 2 * x).on(a), + ops.Rz(b2).on(b), + ops.Ry(b1).on(b), + ops.Rz(b2).on(b), + B(a, b), + ] + + +def _fix_single_qubit_gates_around_kak_interaction( + desired: 'cirq.KakDecomposition', qubits: Sequence['cirq.Qid'], + operations: List['cirq.Operation']) -> Iterator['cirq.Operation']: + actual = linalg.kak_decomposition(circuits.Circuit(operations)) + + def dag(a: np.ndarray) -> np.ndarray: + return np.transpose(np.conjugate(a)) + + for k in range(2): + g = ops.MatrixGate( + dag(actual.single_qubit_operations_before[k]) + @ desired.single_qubit_operations_before[k]) + yield g(qubits[k]) + yield from operations + for k in range(2): + g = ops.MatrixGate(desired.single_qubit_operations_after[k] @ dag( + actual.single_qubit_operations_after[k])) + yield g(qubits[k]) + yield ops.GlobalPhaseOperation(desired.global_phase / actual.global_phase) diff --git a/cirq/optimizers/two_qubit_to_fsim_test.py b/cirq/optimizers/two_qubit_to_fsim_test.py new file mode 100644 index 00000000000..d3b8c919798 --- /dev/null +++ b/cirq/optimizers/two_qubit_to_fsim_test.py @@ -0,0 +1,43 @@ +import random + +import numpy as np +import pytest + +import cirq +from cirq.optimizers.two_qubit_to_fsim import ( + _decompose_two_qubit_interaction_into_two_b_gates,) + + +@pytest.mark.parametrize('obj', [ + cirq.IdentityGate(2), + cirq.XX**0.25, + cirq.CNOT, + cirq.ISWAP, + cirq.SWAP, + cirq.FSimGate(theta=np.pi / 6, phi=np.pi / 6), +] + [cirq.testing.random_unitary(4) for _ in range(10)]) +def test_decompose_two_qubit_interaction_into_two_b_gates(obj): + circuit = cirq.Circuit( + _decompose_two_qubit_interaction_into_two_b_gates(obj)) + desired_unitary = obj if isinstance(obj, np.ndarray) else cirq.unitary(obj) + assert cirq.approx_eq(cirq.unitary(circuit), desired_unitary, atol=1e-6) + + +@pytest.mark.parametrize('obj', [ + cirq.IdentityGate(2), + cirq.XX**0.25, + cirq.CNOT, + cirq.ISWAP, + cirq.SWAP, + cirq.FSimGate(theta=np.pi / 6, phi=np.pi / 6), +] + [cirq.testing.random_unitary(4) for _ in range(10)]) +def test_decompose_two_qubit_interaction_into_fsim_gate(obj): + gate = cirq.FSimGate( + theta=np.pi / 2 + (random.random() * 2 - 1) * 0.1, + phi=(random.random() * 2 - 1) * 0.1) + circuit = cirq.Circuit( + cirq.decompose_two_qubit_interaction_into_four_fsim_gates_via_b( + obj, fsim_gate=gate)) + desired_unitary = obj if isinstance(obj, np.ndarray) else cirq.unitary(obj) + assert len(circuit) <= 4 + 5 + assert cirq.approx_eq(cirq.unitary(circuit), desired_unitary, atol=1e-6) From 67a2db94f95aff8c92b998ca8427ad6765caee30 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 19 Nov 2019 16:44:44 -0800 Subject: [PATCH 06/11] comments --- cirq/__init__.py | 2 +- cirq/ops/__init__.py | 2 +- cirq/ops/phased_x_z_gate.py | 58 +++--- cirq/ops/phased_x_z_gate_test.py | 173 ++++++++++-------- cirq/optimizers/two_qubit_to_fsim.py | 3 +- cirq/optimizers/two_qubit_to_fsim_test.py | 5 +- cirq/protocols/json.py | 2 +- ...XZPowGate.json => PhasedXPowZPowGate.json} | 2 +- .../json_test_data/PhasedXPowZPowGate.repr | 3 + .../json_test_data/PhasedXZPowGate.repr | 3 - docs/api.rst | 2 +- 11 files changed, 142 insertions(+), 113 deletions(-) rename cirq/protocols/json_test_data/{PhasedXZPowGate.json => PhasedXPowZPowGate.json} (69%) create mode 100644 cirq/protocols/json_test_data/PhasedXPowZPowGate.repr delete mode 100644 cirq/protocols/json_test_data/PhasedXZPowGate.repr diff --git a/cirq/__init__.py b/cirq/__init__.py index 3a4eb5d5928..8ae72182c32 100644 --- a/cirq/__init__.py +++ b/cirq/__init__.py @@ -234,7 +234,7 @@ PhaseGradientGate, PhasedISwapPowGate, PhasedXPowGate, - PhasedXZPowGate, + PhasedXPowZPowGate, PhaseFlipChannel, QFT, Qid, diff --git a/cirq/ops/__init__.py b/cirq/ops/__init__.py index 4c689071f4f..3ebcb69a923 100644 --- a/cirq/ops/__init__.py +++ b/cirq/ops/__init__.py @@ -194,7 +194,7 @@ PhasedXPowGate,) from cirq.ops.phased_x_z_gate import ( - PhasedXZPowGate,) + PhasedXPowZPowGate,) from cirq.ops.raw_types import ( Gate, diff --git a/cirq/ops/phased_x_z_gate.py b/cirq/ops/phased_x_z_gate.py index c94d602bdcf..7b64f13fb63 100644 --- a/cirq/ops/phased_x_z_gate.py +++ b/cirq/ops/phased_x_z_gate.py @@ -13,9 +13,14 @@ @value.value_equality(approximate=True) -class PhasedXZPowGate(gate_features.SingleQubitGate): +class PhasedXPowZPowGate(gate_features.SingleQubitGate): """A single qubit operation expressed as $Z^z Z^a X^x Z^{-a}$. + The above expression is a matrix multiplication with time going to the left. + In quantum circuit notation, this operation decomposes into this circuit: + + ───Z^(-a)──X^x──Z^a────Z^z───$ + The axis phase exponent (a) decides which axis in the XY plane to rotate around. The amount of rotation around that axis is decided by the x exponent (x). Then the z exponent (z) decides how much to phase the qubit. @@ -26,16 +31,18 @@ def __init__(self, *, x_exponent: Union[numbers.Real, sympy.Basic], axis_phase_exponent: Union[numbers.Real, sympy.Basic]) -> None: """ Args: - x_exponent: The $x$ in $Z^z Z^a X^x Z^{-a}$. - z_exponent: The $z$ in $Z^z Z^a X^x Z^{-a}$. Note that the $Z^z$ - operation happens last. - axis_phase_exponent: The $a$ in $Z^z Z^a X^x Z^{-a}$. + x_exponent: Determines how much to rotate during the + axis-in-XY-plane rotation. The $x$ in $Z^z Z^a X^x Z^{-a}$. + z_exponent: The amount of phasing to apply after the + axis-in-XY-plane rotation. The $z$ in $Z^z Z^a X^x Z^{-a}$. + axis_phase_exponent: Determines which axis to rotate around during + the axis-in-XY-plane rotation. The $a$ in $Z^z Z^a X^x Z^{-a}$. """ self._x_exponent = x_exponent self._z_exponent = z_exponent self._axis_phase_exponent = axis_phase_exponent - def _canonical(self) -> 'cirq.PhasedXZPowGate': + def _canonical(self) -> 'cirq.PhasedXPowZPowGate': x = self.x_exponent z = self.z_exponent a = self.axis_phase_exponent @@ -71,9 +78,9 @@ def _canonical(self) -> 'cirq.PhasedXZPowGate': a -= 1 x = -x - return PhasedXZPowGate(x_exponent=x, - z_exponent=z, - axis_phase_exponent=a) + return PhasedXPowZPowGate(x_exponent=x, + z_exponent=z, + axis_phase_exponent=a) @property def x_exponent(self) -> Union[numbers.Real, sympy.Basic]: @@ -96,7 +103,7 @@ def _value_equality_values_(self): ) @staticmethod - def from_matrix(mat: np.array) -> 'cirq.PhasedXZPowGate': + def from_matrix(mat: np.array) -> 'cirq.PhasedXPowZPowGate': pre_phase, rotation, post_phase = ( linalg.deconstruct_single_qubit_matrix_into_angles(mat)) pre_phase /= np.pi @@ -104,9 +111,10 @@ def from_matrix(mat: np.array) -> 'cirq.PhasedXZPowGate': rotation /= np.pi pre_phase -= 0.5 post_phase += 0.5 - return PhasedXZPowGate(x_exponent=rotation, - axis_phase_exponent=-pre_phase, - z_exponent=post_phase + pre_phase)._canonical() + return PhasedXPowZPowGate(x_exponent=rotation, + axis_phase_exponent=-pre_phase, + z_exponent=post_phase + + pre_phase)._canonical() def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]: @@ -121,13 +129,13 @@ def _decompose_(self, qubits: Sequence['cirq.Qid']) -> 'cirq.OP_TREE': q = qubits[0] yield ops.Z(q)**-self._axis_phase_exponent yield ops.X(q)**self._x_exponent - yield ops.Z(q)**(self._z_exponent + self._axis_phase_exponent) + yield ops.Z(q)**(self._axis_phase_exponent + self._z_exponent) - def __pow__(self, exponent: Union[float, int]) -> 'PhasedXZPowGate': + def __pow__(self, exponent: Union[float, int]) -> 'PhasedXPowZPowGate': if exponent == 1: return self if exponent == -1: - return PhasedXZPowGate( + return PhasedXPowZPowGate( x_exponent=-self._x_exponent, z_exponent=-self._z_exponent, axis_phase_exponent=self._z_exponent + self.axis_phase_exponent, @@ -140,28 +148,28 @@ def _is_parameterized_(self) -> bool: protocols.is_parameterized(self._z_exponent) or protocols.is_parameterized(self._axis_phase_exponent)) - def _resolve_parameters_(self, param_resolver) -> 'cirq.PhasedXZPowGate': + def _resolve_parameters_(self, param_resolver) -> 'cirq.PhasedXPowZPowGate': """See `cirq.SupportsParameterization`.""" - return PhasedXZPowGate( + return PhasedXPowZPowGate( z_exponent=param_resolver.value_of(self._z_exponent), x_exponent=param_resolver.value_of(self._x_exponent), axis_phase_exponent=param_resolver.value_of( self._axis_phase_exponent), ) - def _phase_by_(self, phase_turns, qubit_index) -> 'cirq.PhasedXZPowGate': + def _phase_by_(self, phase_turns, qubit_index) -> 'cirq.PhasedXPowZPowGate': """See `cirq.SupportsPhase`.""" assert qubit_index == 0 - return PhasedXZPowGate(x_exponent=self._x_exponent, - z_exponent=self._z_exponent, - axis_phase_exponent=self._axis_phase_exponent + - phase_turns * 2) + return PhasedXPowZPowGate( + x_exponent=self._x_exponent, + z_exponent=self._z_exponent, + axis_phase_exponent=self._axis_phase_exponent + phase_turns * 2) def _circuit_diagram_info_(self, args: 'cirq.CircuitDiagramInfoArgs') -> str: """See `cirq.SupportsCircuitDiagramInfo`.""" return (f'PhXZ(' - f'p={args.format_real(self._axis_phase_exponent)},' + f'a={args.format_real(self._axis_phase_exponent)},' f'x={args.format_real(self._x_exponent)},' f'z={args.format_real(self._z_exponent)})') @@ -169,7 +177,7 @@ def __str__(self): return protocols.circuit_diagram_info(self).wire_symbols[0] def __repr__(self): - return (f'cirq.PhasedXZPowGate(' + return (f'cirq.PhasedXPowZPowGate(' f'axis_phase_exponent={proper_repr(self._axis_phase_exponent)},' f' x_exponent={proper_repr(self._x_exponent)}, ' f'z_exponent={proper_repr(self._z_exponent)})') diff --git a/cirq/ops/phased_x_z_gate_test.py b/cirq/ops/phased_x_z_gate_test.py index b538e43b570..1e5202e0a28 100644 --- a/cirq/ops/phased_x_z_gate_test.py +++ b/cirq/ops/phased_x_z_gate_test.py @@ -7,9 +7,9 @@ def test_init_properties(): - g = cirq.PhasedXZPowGate(x_exponent=0.125, - z_exponent=0.25, - axis_phase_exponent=0.375) + g = cirq.PhasedXPowZPowGate(x_exponent=0.125, + z_exponent=0.25, + axis_phase_exponent=0.375) assert g.x_exponent == 0.125 assert g.z_exponent == 0.25 assert g.axis_phase_exponent == 0.375 @@ -17,37 +17,39 @@ def test_init_properties(): def test_eq(): eq = cirq.testing.EqualsTester() - eq.make_equality_group(lambda: cirq.PhasedXZPowGate( + eq.make_equality_group(lambda: cirq.PhasedXPowZPowGate( x_exponent=0.25, z_exponent=0.5, axis_phase_exponent=0.75)) # Sensitive to each parameter. eq.add_equality_group( - cirq.PhasedXZPowGate(x_exponent=0, - z_exponent=0.5, - axis_phase_exponent=0.75)) + cirq.PhasedXPowZPowGate(x_exponent=0, + z_exponent=0.5, + axis_phase_exponent=0.75)) eq.add_equality_group( - cirq.PhasedXZPowGate(x_exponent=0.25, - z_exponent=0, - axis_phase_exponent=0.75)) + cirq.PhasedXPowZPowGate(x_exponent=0.25, + z_exponent=0, + axis_phase_exponent=0.75)) eq.add_equality_group( - cirq.PhasedXZPowGate(x_exponent=0.25, - z_exponent=0.5, - axis_phase_exponent=0)) + cirq.PhasedXPowZPowGate(x_exponent=0.25, + z_exponent=0.5, + axis_phase_exponent=0)) # Different from other gates. eq.add_equality_group( cirq.PhasedXPowGate(exponent=0.25, phase_exponent=0.75)) eq.add_equality_group(cirq.X) eq.add_equality_group( - cirq.PhasedXZPowGate(x_exponent=1, z_exponent=0, axis_phase_exponent=0)) + cirq.PhasedXPowZPowGate(x_exponent=1, + z_exponent=0, + axis_phase_exponent=0)) def test_canonicalization(): def f(x, z, a): - return cirq.PhasedXZPowGate(x_exponent=x, - z_exponent=z, - axis_phase_exponent=a) + return cirq.PhasedXPowZPowGate(x_exponent=x, + z_exponent=z, + axis_phase_exponent=a) # Canonicalizations are equivalent. eq = cirq.testing.EqualsTester() @@ -131,79 +133,81 @@ def f(x, z, a): def test_from_matrix(): # Axis rotations. - assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix( + assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( cirq.unitary(cirq.X**0.1)), - cirq.PhasedXZPowGate(x_exponent=0.1, - z_exponent=0, - axis_phase_exponent=0), + cirq.PhasedXPowZPowGate(x_exponent=0.1, + z_exponent=0, + axis_phase_exponent=0), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix( + assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( cirq.unitary(cirq.X**-0.1)), - cirq.PhasedXZPowGate(x_exponent=-0.1, - z_exponent=0, - axis_phase_exponent=0), + cirq.PhasedXPowZPowGate(x_exponent=-0.1, + z_exponent=0, + axis_phase_exponent=0), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix( + assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( cirq.unitary(cirq.Y**0.1)), - cirq.PhasedXZPowGate(x_exponent=0.1, - z_exponent=0, - axis_phase_exponent=0.5), + cirq.PhasedXPowZPowGate(x_exponent=0.1, + z_exponent=0, + axis_phase_exponent=0.5), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix( + assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( cirq.unitary(cirq.Y**-0.1)), - cirq.PhasedXZPowGate(x_exponent=-0.1, - z_exponent=0, - axis_phase_exponent=0.5), + cirq.PhasedXPowZPowGate(x_exponent=-0.1, + z_exponent=0, + axis_phase_exponent=0.5), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix( + assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( cirq.unitary(cirq.Z**-0.1)), - cirq.PhasedXZPowGate(x_exponent=0, - z_exponent=-0.1, - axis_phase_exponent=0), + cirq.PhasedXPowZPowGate(x_exponent=0, + z_exponent=-0.1, + axis_phase_exponent=0), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix( + assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( cirq.unitary(cirq.Z**0.1)), - cirq.PhasedXZPowGate(x_exponent=0, - z_exponent=0.1, - axis_phase_exponent=0), + cirq.PhasedXPowZPowGate(x_exponent=0, + z_exponent=0.1, + axis_phase_exponent=0), atol=1e-8) # Pauli matrices. - assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix(np.eye(2)), - cirq.PhasedXZPowGate(x_exponent=0, - z_exponent=0, - axis_phase_exponent=0), + assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix(np.eye(2)), + cirq.PhasedXPowZPowGate(x_exponent=0, + z_exponent=0, + axis_phase_exponent=0), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix(cirq.unitary( - cirq.X)), - cirq.PhasedXZPowGate(x_exponent=1, - z_exponent=0, - axis_phase_exponent=0), + assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( + cirq.unitary(cirq.X)), + cirq.PhasedXPowZPowGate(x_exponent=1, + z_exponent=0, + axis_phase_exponent=0), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix(cirq.unitary( - cirq.Y)), - cirq.PhasedXZPowGate(x_exponent=1, - z_exponent=0, - axis_phase_exponent=0.5), + assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( + cirq.unitary(cirq.Y)), + cirq.PhasedXPowZPowGate(x_exponent=1, + z_exponent=0, + axis_phase_exponent=0.5), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix(cirq.unitary( - cirq.Z)), - cirq.PhasedXZPowGate(x_exponent=0, - z_exponent=1, - axis_phase_exponent=0), + assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( + cirq.unitary(cirq.Z)), + cirq.PhasedXPowZPowGate(x_exponent=0, + z_exponent=1, + axis_phase_exponent=0), atol=1e-8) # Round trips. a = random.random() b = random.random() c = random.random() - g = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=b, axis_phase_exponent=c) - assert cirq.approx_eq(cirq.PhasedXZPowGate.from_matrix(cirq.unitary(g)), + g = cirq.PhasedXPowZPowGate(x_exponent=a, + z_exponent=b, + axis_phase_exponent=c) + assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix(cirq.unitary(g)), g, atol=1e-8) u = cirq.testing.random_unitary(2) cirq.testing.assert_allclose_up_to_global_phase(cirq.unitary( - cirq.PhasedXZPowGate.from_matrix(u)), + cirq.PhasedXPowZPowGate.from_matrix(u)), u, atol=1e-8) @@ -212,16 +216,24 @@ def test_protocols(): a = random.random() b = random.random() c = random.random() - g = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=b, axis_phase_exponent=c) + g = cirq.PhasedXPowZPowGate(x_exponent=a, + z_exponent=b, + axis_phase_exponent=c) cirq.testing.assert_implements_consistent_protocols(g) # Symbolic. t = sympy.Symbol('t') - g = cirq.PhasedXZPowGate(x_exponent=t, z_exponent=b, axis_phase_exponent=c) + g = cirq.PhasedXPowZPowGate(x_exponent=t, + z_exponent=b, + axis_phase_exponent=c) cirq.testing.assert_implements_consistent_protocols(g) - g = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=t, axis_phase_exponent=c) + g = cirq.PhasedXPowZPowGate(x_exponent=a, + z_exponent=t, + axis_phase_exponent=c) cirq.testing.assert_implements_consistent_protocols(g) - g = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=b, axis_phase_exponent=t) + g = cirq.PhasedXPowZPowGate(x_exponent=a, + z_exponent=b, + axis_phase_exponent=t) cirq.testing.assert_implements_consistent_protocols(g) @@ -230,8 +242,9 @@ def test_inverse(): b = random.random() c = random.random() q = cirq.LineQubit(0) - g = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=b, - axis_phase_exponent=c).on(q) + g = cirq.PhasedXPowZPowGate(x_exponent=a, + z_exponent=b, + axis_phase_exponent=c).on(q) cirq.testing.assert_allclose_up_to_global_phase( cirq.unitary(g**-1), @@ -243,25 +256,33 @@ def test_parameterized(): a = random.random() b = random.random() c = random.random() - g = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=b, axis_phase_exponent=c) + g = cirq.PhasedXPowZPowGate(x_exponent=a, + z_exponent=b, + axis_phase_exponent=c) assert not cirq.is_parameterized(g) t = sympy.Symbol('t') - gt = cirq.PhasedXZPowGate(x_exponent=t, z_exponent=b, axis_phase_exponent=c) + gt = cirq.PhasedXPowZPowGate(x_exponent=t, + z_exponent=b, + axis_phase_exponent=c) assert cirq.is_parameterized(gt) assert cirq.resolve_parameters(gt, {'t': a}) == g - gt = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=t, axis_phase_exponent=c) + gt = cirq.PhasedXPowZPowGate(x_exponent=a, + z_exponent=t, + axis_phase_exponent=c) assert cirq.is_parameterized(gt) assert cirq.resolve_parameters(gt, {'t': b}) == g - gt = cirq.PhasedXZPowGate(x_exponent=a, z_exponent=b, axis_phase_exponent=t) + gt = cirq.PhasedXPowZPowGate(x_exponent=a, + z_exponent=b, + axis_phase_exponent=t) assert cirq.is_parameterized(gt) assert cirq.resolve_parameters(gt, {'t': c}) == g def test_str_diagram(): - g = cirq.PhasedXZPowGate(x_exponent=0.5, - z_exponent=0.25, - axis_phase_exponent=0.125) + g = cirq.PhasedXPowZPowGate(x_exponent=0.5, + z_exponent=0.25, + axis_phase_exponent=0.125) assert str(g) == "PhXZ(p=0.125,x=0.5,z=0.25)" diff --git a/cirq/optimizers/two_qubit_to_fsim.py b/cirq/optimizers/two_qubit_to_fsim.py index 8453130a9a2..1f7b509c452 100644 --- a/cirq/optimizers/two_qubit_to_fsim.py +++ b/cirq/optimizers/two_qubit_to_fsim.py @@ -165,7 +165,8 @@ def _decompose_b_gate_into_two_fsims(*, fsim_gate: 'cirq.FSimGate', def _decompose_interaction_into_two_b_gates_ignoring_single_qubit_ops( qubits: Sequence['cirq.Qid'], - kak_interaction_coefficients: Iterable[float]) -> List['cirq.Operation']: + kak_interaction_coefficients: Iterable[float] +) -> List['cirq.Operation']: """ References: Minimum construction of two-qubit quantum operations diff --git a/cirq/optimizers/two_qubit_to_fsim_test.py b/cirq/optimizers/two_qubit_to_fsim_test.py index d3b8c919798..22e7899d2db 100644 --- a/cirq/optimizers/two_qubit_to_fsim_test.py +++ b/cirq/optimizers/two_qubit_to_fsim_test.py @@ -32,9 +32,8 @@ def test_decompose_two_qubit_interaction_into_two_b_gates(obj): cirq.FSimGate(theta=np.pi / 6, phi=np.pi / 6), ] + [cirq.testing.random_unitary(4) for _ in range(10)]) def test_decompose_two_qubit_interaction_into_fsim_gate(obj): - gate = cirq.FSimGate( - theta=np.pi / 2 + (random.random() * 2 - 1) * 0.1, - phi=(random.random() * 2 - 1) * 0.1) + gate = cirq.FSimGate(theta=np.pi / 2 + (random.random() * 2 - 1) * 0.1, + phi=(random.random() * 2 - 1) * 0.1) circuit = cirq.Circuit( cirq.decompose_two_qubit_interaction_into_four_fsim_gates_via_b( obj, fsim_gate=gate)) diff --git a/cirq/protocols/json.py b/cirq/protocols/json.py index 517af624d42..73306a28b13 100644 --- a/cirq/protocols/json.py +++ b/cirq/protocols/json.py @@ -105,7 +105,7 @@ def _identity_operation_from_dict(qubits, **kwargs): 'PhaseGradientGate': cirq.PhaseGradientGate, 'PhasedISwapPowGate': cirq.PhasedISwapPowGate, 'PhasedXPowGate': cirq.PhasedXPowGate, - 'PhasedXZPowGate': cirq.PhasedXZPowGate, + 'PhasedXPowZPowGate': cirq.PhasedXPowZPowGate, 'QuantumFourierTransformGate': cirq.QuantumFourierTransformGate, 'ResetChannel': cirq.ResetChannel, 'SingleQubitMatrixGate': cirq.SingleQubitMatrixGate, diff --git a/cirq/protocols/json_test_data/PhasedXZPowGate.json b/cirq/protocols/json_test_data/PhasedXPowZPowGate.json similarity index 69% rename from cirq/protocols/json_test_data/PhasedXZPowGate.json rename to cirq/protocols/json_test_data/PhasedXPowZPowGate.json index 9717cc9bec7..283d78e28a5 100644 --- a/cirq/protocols/json_test_data/PhasedXZPowGate.json +++ b/cirq/protocols/json_test_data/PhasedXPowZPowGate.json @@ -1,6 +1,6 @@ [ { - "cirq_type": "PhasedXZPowGate", + "cirq_type": "PhasedXPowZPowGate", "axis_phase_exponent": 0.75, "x_exponent": 0.5, "z_exponent": 0.25 diff --git a/cirq/protocols/json_test_data/PhasedXPowZPowGate.repr b/cirq/protocols/json_test_data/PhasedXPowZPowGate.repr new file mode 100644 index 00000000000..7815302e84f --- /dev/null +++ b/cirq/protocols/json_test_data/PhasedXPowZPowGate.repr @@ -0,0 +1,3 @@ +[ +cirq.PhasedXPowZPowGate(axis_phase_exponent=0.75, x_exponent=0.5, z_exponent=0.25), +] \ No newline at end of file diff --git a/cirq/protocols/json_test_data/PhasedXZPowGate.repr b/cirq/protocols/json_test_data/PhasedXZPowGate.repr deleted file mode 100644 index 8099b575d47..00000000000 --- a/cirq/protocols/json_test_data/PhasedXZPowGate.repr +++ /dev/null @@ -1,3 +0,0 @@ -[ -cirq.PhasedXZPowGate(axis_phase_exponent=0.75, x_exponent=0.5, z_exponent=0.25), -] \ No newline at end of file diff --git a/docs/api.rst b/docs/api.rst index c1b9ee72ae5..9c24f671b07 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -90,7 +90,7 @@ Unitary effects that can be applied to one or more qubits. cirq.PhaseGradientGate cirq.PhasedISwapPowGate cirq.PhasedXPowGate - cirq.PhasedXZPowGate + cirq.PhasedXPowZPowGate cirq.QuantumFourierTransformGate cirq.SingleQubitGate cirq.SingleQubitMatrixGate From feea4d07aff74f46ec11be0db79323327ecad564 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 19 Nov 2019 17:02:27 -0800 Subject: [PATCH 07/11] fix tests --- cirq/ops/phased_x_z_gate_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cirq/ops/phased_x_z_gate_test.py b/cirq/ops/phased_x_z_gate_test.py index 1e5202e0a28..0b5ef2105bc 100644 --- a/cirq/ops/phased_x_z_gate_test.py +++ b/cirq/ops/phased_x_z_gate_test.py @@ -284,9 +284,9 @@ def test_str_diagram(): z_exponent=0.25, axis_phase_exponent=0.125) - assert str(g) == "PhXZ(p=0.125,x=0.5,z=0.25)" + assert str(g) == "PhXZ(a=0.125,x=0.5,z=0.25)" cirq.testing.assert_has_diagram( cirq.Circuit(g.on(cirq.LineQubit(0))), """ -0: ───PhXZ(p=0.125,x=0.5,z=0.25)─── +0: ───PhXZ(a=0.125,x=0.5,z=0.25)─── """) From bf626099ff4cb58fe969d31f6f280cf53d15d962 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 19 Nov 2019 17:08:23 -0800 Subject: [PATCH 08/11] drop unintended changes --- cirq/__init__.py | 1 - cirq/linalg/decompositions.py | 20 +-- cirq/linalg/decompositions_test.py | 10 -- cirq/optimizers/__init__.py | 3 - cirq/optimizers/two_qubit_to_fsim.py | 210 ---------------------- cirq/optimizers/two_qubit_to_fsim_test.py | 42 ----- 6 files changed, 5 insertions(+), 281 deletions(-) delete mode 100644 cirq/optimizers/two_qubit_to_fsim.py delete mode 100644 cirq/optimizers/two_qubit_to_fsim_test.py diff --git a/cirq/__init__.py b/cirq/__init__.py index 8ae72182c32..fb2f4b2b5c8 100644 --- a/cirq/__init__.py +++ b/cirq/__init__.py @@ -278,7 +278,6 @@ from cirq.optimizers import ( ConvertToCzAndSingleGates, - decompose_two_qubit_interaction_into_four_fsim_gates_via_b, DropEmptyMoments, DropNegligible, EjectPhasedPaulis, diff --git a/cirq/linalg/decompositions.py b/cirq/linalg/decompositions.py index f442212ad6a..47274faaca1 100644 --- a/cirq/linalg/decompositions.py +++ b/cirq/linalg/decompositions.py @@ -430,12 +430,10 @@ class KakDecomposition: def __init__(self, *, - global_phase: complex = complex(1), - single_qubit_operations_before: Optional[ - Tuple[np.ndarray, np.ndarray]] = None, + global_phase: complex, + single_qubit_operations_before: Tuple[np.ndarray, np.ndarray], interaction_coefficients: Tuple[float, float, float], - single_qubit_operations_after: Optional[ - Tuple[np.ndarray, np.ndarray]] = None): + single_qubit_operations_after: Tuple[np.ndarray, np.ndarray]): """Initializes a decomposition for a two-qubit operation U. U = g · (a1 ⊗ a0) · exp(i·(x·XX + y·YY + z·ZZ)) · (b1 ⊗ b0) @@ -447,17 +445,9 @@ def __init__(self, single_qubit_operations_after: a0, a1 from the above equation. """ self.global_phase: complex = global_phase - self.single_qubit_operations_before: Tuple[np.ndarray, np.ndarray] = ( - single_qubit_operations_before or ( - np.eye(2, dtype=np.complex64), - np.eye(2, dtype=np.complex64), - )) + self.single_qubit_operations_before = single_qubit_operations_before self.interaction_coefficients = interaction_coefficients - self.single_qubit_operations_after: Tuple[np.ndarray, np.ndarray] = ( - single_qubit_operations_after or ( - np.eye(2, dtype=np.complex64), - np.eye(2, dtype=np.complex64), - )) + self.single_qubit_operations_after = single_qubit_operations_after def _value_equality_values_(self): def flatten(x): diff --git a/cirq/linalg/decompositions_test.py b/cirq/linalg/decompositions_test.py index 0dc8e340f44..9edbb4bb00f 100644 --- a/cirq/linalg/decompositions_test.py +++ b/cirq/linalg/decompositions_test.py @@ -293,16 +293,6 @@ def test_kak_decomposition_eq(): single_qubit_operations_after=(np.eye(2), cirq.unitary(cirq.Z)), )) - eq.add_equality_group( - cirq.KakDecomposition( - global_phase=1, - single_qubit_operations_before=(np.eye(2), np.eye(2)), - interaction_coefficients=(0.3, 0.2, 0.1), - single_qubit_operations_after=(np.eye(2), np.eye(2)), - ), - cirq.KakDecomposition(interaction_coefficients=(0.3, 0.2, 0.1)), - ) - eq.make_equality_group(lambda: cirq.KakDecomposition( global_phase=1, single_qubit_operations_before=(cirq.unitary(cirq.X), diff --git a/cirq/optimizers/__init__.py b/cirq/optimizers/__init__.py index 7033aea67dc..c971ecc1666 100644 --- a/cirq/optimizers/__init__.py +++ b/cirq/optimizers/__init__.py @@ -53,6 +53,3 @@ from cirq.optimizers.two_qubit_decompositions import ( two_qubit_matrix_to_operations,) - -from cirq.optimizers.two_qubit_to_fsim import ( - decompose_two_qubit_interaction_into_four_fsim_gates_via_b,) diff --git a/cirq/optimizers/two_qubit_to_fsim.py b/cirq/optimizers/two_qubit_to_fsim.py deleted file mode 100644 index 1f7b509c452..00000000000 --- a/cirq/optimizers/two_qubit_to_fsim.py +++ /dev/null @@ -1,210 +0,0 @@ -from typing import Sequence, Union, Any, List, Iterator, TYPE_CHECKING, Iterable - -import numpy as np - -from cirq import ops, linalg, circuits, devices -from cirq.optimizers import merge_single_qubit_gates, drop_empty_moments - -if TYPE_CHECKING: - import cirq - - -def decompose_two_qubit_interaction_into_four_fsim_gates_via_b( - interaction: Union['cirq.Operation', 'cirq.Gate', np.ndarray, Any], - *, - fsim_gate: 'cirq.FSimGate', - qubits: Sequence['cirq.Qid'] = None) -> 'cirq.Circuit': - """Decomposes operations into an FSimGate near theta=pi/2, phi=0. - - This decomposition is guaranteed to use exactly four of the given FSim - gates. It works by decomposing into two B gates and then decomposing each - B gate into two of the given FSim gate. - - TODO: describe the feasible angles. - - Args: - interaction: The two qubit operation to synthesize. This can either be - a cirq object (such as a gate, operation, or circuit) or a raw numpy - array specifying the 4x4 unitary matrix. - fsim_gate: The only two qubit gate that is permitted to appear in the - output. - qubits: The qubits that the resulting operations should apply the - desired interaction to. If not set then defaults to either the - qubits of the given interaction (if it is a `cirq.Operation`) or - else to `cirq.LineQubit.range(2)`. - - Returns: - A list of operations implementing the desired two qubit unitary. The - list will include four operations of the given fsim gate, various single - qubit operations, and a global phase operation. - """ - if qubits is None: - if isinstance(interaction, ops.Operation): - qubits = interaction.qubits - else: - qubits = devices.LineQubit.range(2) - if len(qubits) != 2: - raise ValueError(f'Expected a pair of qubits, but got {qubits!r}.') - kak = linalg.kak_decomposition(interaction) - - result_using_b_gates = _decompose_two_qubit_interaction_into_two_b_gates( - kak, qubits=qubits) - - b_decomposition = _decompose_b_gate_into_two_fsims(fsim_gate=fsim_gate, - qubits=qubits) - result = [] - for op in result_using_b_gates: - if isinstance(op.gate, _BGate): - result.extend(b_decomposition) - else: - result.append(op) - - circuit = circuits.Circuit(result) - merge_single_qubit_gates.MergeSingleQubitGates().optimize_circuit(circuit) - drop_empty_moments.DropEmptyMoments().optimize_circuit(circuit) - return circuit - - -def _decompose_xx_yy_into_two_fsims_ignoring_single_qubit_ops( - *, qubits: Sequence['cirq.Qid'], fsim_gate: 'cirq.FSimGate', - canonical_x_kak_coefficient: float, - canonical_y_kak_coefficient: float) -> List['cirq.Operation']: - x = canonical_x_kak_coefficient - y = canonical_y_kak_coefficient - assert 0 <= y <= x <= np.pi / 4 - - eta = np.sin(x)**2 * np.cos(y)**2 + np.cos(x)**2 * np.sin(y)**2 - xi = abs(np.sin(2 * x) * np.sin(2 * y)) - - t = fsim_gate.phi / 2 - kappa = np.sin(fsim_gate.theta)**2 - np.sin(t)**2 - s_sum = (eta - np.sin(t)**2) / kappa - s_dif = 0.5 * xi / kappa - - x_dif = np.arcsin(np.sqrt(s_sum + s_dif)) - x_sum = np.arcsin(np.sqrt(s_sum - s_dif)) - - x_a = x_sum + x_dif - x_b = x_dif - x_sum - if np.isnan(x_b): - raise ValueError( - f'Failed to synthesize XX^{x/np.pi}·YY^{y/np.pi} from two ' - f'{fsim_gate!r} separated by single qubit operations.') - - a, b = qubits - return [ - fsim_gate(a, b), - ops.Rz(t + np.pi).on(a), - ops.Rz(t).on(b), - ops.Rx(x_a).on(a), - ops.Rx(x_b).on(b), - fsim_gate(a, b), - ] - - -class _BGate(ops.Gate): - """Single qubit gates and two of these can achieve any kak coefficients. - - References: - Minimum construction of two-qubit quantum operations - https://arxiv.org/abs/quant-ph/0312193 - """ - - def num_qubits(self) -> int: - return 2 - - def _decompose_(self, qubits): - a, b = qubits - return [ - ops.XX(a, b)**0.5, - ops.YY(a, b)**0.25, - ] - - def __str__(self): - return 'B' - - -def _decompose_two_qubit_interaction_into_two_b_gates( - interaction: Union['cirq.Operation', 'cirq.Gate', np.ndarray, Any], - *, - qubits: Sequence['cirq.Qid'] = None) -> List['cirq.Operation']: - if qubits is None: - if isinstance(interaction, ops.Operation): - qubits = interaction.qubits - else: - qubits = devices.LineQubit.range(2) - if len(qubits) != 2: - raise ValueError(f'Expected a pair of qubits, but got {qubits!r}.') - kak = linalg.kak_decomposition(interaction) - - result = _decompose_interaction_into_two_b_gates_ignoring_single_qubit_ops( - qubits, kak.interaction_coefficients) - - return list( - _fix_single_qubit_gates_around_kak_interaction(desired=kak, - qubits=qubits, - operations=result)) - - -def _decompose_b_gate_into_two_fsims(*, fsim_gate: 'cirq.FSimGate', - qubits: Sequence['cirq.Qid'] - ) -> List['cirq.Operation']: - kak = linalg.kak_decomposition(_BGate()) - - result = _decompose_xx_yy_into_two_fsims_ignoring_single_qubit_ops( - qubits=qubits, - fsim_gate=fsim_gate, - canonical_x_kak_coefficient=kak.interaction_coefficients[0], - canonical_y_kak_coefficient=kak.interaction_coefficients[1]) - - return list( - _fix_single_qubit_gates_around_kak_interaction(desired=kak, - qubits=qubits, - operations=result)) - - -def _decompose_interaction_into_two_b_gates_ignoring_single_qubit_ops( - qubits: Sequence['cirq.Qid'], - kak_interaction_coefficients: Iterable[float] -) -> List['cirq.Operation']: - """ - References: - Minimum construction of two-qubit quantum operations - https://arxiv.org/abs/quant-ph/0312193 - """ - a, b = qubits - x, y, z = kak_interaction_coefficients - B = _BGate() - r = (np.sin(y) * np.cos(z))**2 - b1 = np.arccos(1 - 4 * r) - b2 = np.arcsin(np.sqrt(np.cos(y * 2) * np.cos(z * 2) / (1 - 2 * r))) - s = 1 if z < 0 else -1 - return [ - B(a, b), - ops.Ry(s * 2 * x).on(a), - ops.Rz(b2).on(b), - ops.Ry(b1).on(b), - ops.Rz(b2).on(b), - B(a, b), - ] - - -def _fix_single_qubit_gates_around_kak_interaction( - desired: 'cirq.KakDecomposition', qubits: Sequence['cirq.Qid'], - operations: List['cirq.Operation']) -> Iterator['cirq.Operation']: - actual = linalg.kak_decomposition(circuits.Circuit(operations)) - - def dag(a: np.ndarray) -> np.ndarray: - return np.transpose(np.conjugate(a)) - - for k in range(2): - g = ops.MatrixGate( - dag(actual.single_qubit_operations_before[k]) - @ desired.single_qubit_operations_before[k]) - yield g(qubits[k]) - yield from operations - for k in range(2): - g = ops.MatrixGate(desired.single_qubit_operations_after[k] @ dag( - actual.single_qubit_operations_after[k])) - yield g(qubits[k]) - yield ops.GlobalPhaseOperation(desired.global_phase / actual.global_phase) diff --git a/cirq/optimizers/two_qubit_to_fsim_test.py b/cirq/optimizers/two_qubit_to_fsim_test.py deleted file mode 100644 index 22e7899d2db..00000000000 --- a/cirq/optimizers/two_qubit_to_fsim_test.py +++ /dev/null @@ -1,42 +0,0 @@ -import random - -import numpy as np -import pytest - -import cirq -from cirq.optimizers.two_qubit_to_fsim import ( - _decompose_two_qubit_interaction_into_two_b_gates,) - - -@pytest.mark.parametrize('obj', [ - cirq.IdentityGate(2), - cirq.XX**0.25, - cirq.CNOT, - cirq.ISWAP, - cirq.SWAP, - cirq.FSimGate(theta=np.pi / 6, phi=np.pi / 6), -] + [cirq.testing.random_unitary(4) for _ in range(10)]) -def test_decompose_two_qubit_interaction_into_two_b_gates(obj): - circuit = cirq.Circuit( - _decompose_two_qubit_interaction_into_two_b_gates(obj)) - desired_unitary = obj if isinstance(obj, np.ndarray) else cirq.unitary(obj) - assert cirq.approx_eq(cirq.unitary(circuit), desired_unitary, atol=1e-6) - - -@pytest.mark.parametrize('obj', [ - cirq.IdentityGate(2), - cirq.XX**0.25, - cirq.CNOT, - cirq.ISWAP, - cirq.SWAP, - cirq.FSimGate(theta=np.pi / 6, phi=np.pi / 6), -] + [cirq.testing.random_unitary(4) for _ in range(10)]) -def test_decompose_two_qubit_interaction_into_fsim_gate(obj): - gate = cirq.FSimGate(theta=np.pi / 2 + (random.random() * 2 - 1) * 0.1, - phi=(random.random() * 2 - 1) * 0.1) - circuit = cirq.Circuit( - cirq.decompose_two_qubit_interaction_into_four_fsim_gates_via_b( - obj, fsim_gate=gate)) - desired_unitary = obj if isinstance(obj, np.ndarray) else cirq.unitary(obj) - assert len(circuit) <= 4 + 5 - assert cirq.approx_eq(cirq.unitary(circuit), desired_unitary, atol=1e-6) From a0d741749a4b98f9aee8d93ada9300d7f3c6e0a3 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Tue, 19 Nov 2019 17:09:05 -0800 Subject: [PATCH 09/11] drop unintended changes --- cirq/linalg/decompositions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq/linalg/decompositions.py b/cirq/linalg/decompositions.py index 47274faaca1..9413920d5d1 100644 --- a/cirq/linalg/decompositions.py +++ b/cirq/linalg/decompositions.py @@ -444,7 +444,7 @@ def __init__(self, interaction_coefficients: x, y, z from the above equation. single_qubit_operations_after: a0, a1 from the above equation. """ - self.global_phase: complex = global_phase + self.global_phase = global_phase self.single_qubit_operations_before = single_qubit_operations_before self.interaction_coefficients = interaction_coefficients self.single_qubit_operations_after = single_qubit_operations_after From 66ac75a3c5a75f9d45bcd5bf005f63ea45237f30 Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Thu, 21 Nov 2019 13:36:18 -0800 Subject: [PATCH 10/11] Rename to PhasedXZGate --- cirq/__init__.py | 2 +- cirq/ops/__init__.py | 2 +- cirq/ops/phased_x_z_gate.py | 24 +++--- cirq/ops/phased_x_z_gate_test.py | 80 +++++++++---------- cirq/protocols/json.py | 2 +- .../json_test_data/PhasedXPowZPowGate.repr | 3 - ...sedXPowZPowGate.json => PhasedXZGate.json} | 2 +- .../json_test_data/PhasedXZGate.repr | 3 + docs/api.rst | 2 +- 9 files changed, 60 insertions(+), 60 deletions(-) delete mode 100644 cirq/protocols/json_test_data/PhasedXPowZPowGate.repr rename cirq/protocols/json_test_data/{PhasedXPowZPowGate.json => PhasedXZGate.json} (69%) create mode 100644 cirq/protocols/json_test_data/PhasedXZGate.repr diff --git a/cirq/__init__.py b/cirq/__init__.py index 5536d5fb934..6a797f1bfab 100644 --- a/cirq/__init__.py +++ b/cirq/__init__.py @@ -234,7 +234,7 @@ PhaseGradientGate, PhasedISwapPowGate, PhasedXPowGate, - PhasedXPowZPowGate, + PhasedXZGate, PhaseFlipChannel, QFT, Qid, diff --git a/cirq/ops/__init__.py b/cirq/ops/__init__.py index 1ab0ff4e602..be15594e73f 100644 --- a/cirq/ops/__init__.py +++ b/cirq/ops/__init__.py @@ -197,7 +197,7 @@ PhasedXPowGate,) from cirq.ops.phased_x_z_gate import ( - PhasedXPowZPowGate,) + PhasedXZGate,) from cirq.ops.raw_types import ( Gate, diff --git a/cirq/ops/phased_x_z_gate.py b/cirq/ops/phased_x_z_gate.py index 7b64f13fb63..19f25d3de4c 100644 --- a/cirq/ops/phased_x_z_gate.py +++ b/cirq/ops/phased_x_z_gate.py @@ -13,7 +13,7 @@ @value.value_equality(approximate=True) -class PhasedXPowZPowGate(gate_features.SingleQubitGate): +class PhasedXZGate(gate_features.SingleQubitGate): """A single qubit operation expressed as $Z^z Z^a X^x Z^{-a}$. The above expression is a matrix multiplication with time going to the left. @@ -42,7 +42,7 @@ def __init__(self, *, x_exponent: Union[numbers.Real, sympy.Basic], self._z_exponent = z_exponent self._axis_phase_exponent = axis_phase_exponent - def _canonical(self) -> 'cirq.PhasedXPowZPowGate': + def _canonical(self) -> 'cirq.PhasedXZGate': x = self.x_exponent z = self.z_exponent a = self.axis_phase_exponent @@ -78,7 +78,7 @@ def _canonical(self) -> 'cirq.PhasedXPowZPowGate': a -= 1 x = -x - return PhasedXPowZPowGate(x_exponent=x, + return PhasedXZGate(x_exponent=x, z_exponent=z, axis_phase_exponent=a) @@ -103,7 +103,7 @@ def _value_equality_values_(self): ) @staticmethod - def from_matrix(mat: np.array) -> 'cirq.PhasedXPowZPowGate': + def from_matrix(mat: np.array) -> 'cirq.PhasedXZGate': pre_phase, rotation, post_phase = ( linalg.deconstruct_single_qubit_matrix_into_angles(mat)) pre_phase /= np.pi @@ -111,7 +111,7 @@ def from_matrix(mat: np.array) -> 'cirq.PhasedXPowZPowGate': rotation /= np.pi pre_phase -= 0.5 post_phase += 0.5 - return PhasedXPowZPowGate(x_exponent=rotation, + return PhasedXZGate(x_exponent=rotation, axis_phase_exponent=-pre_phase, z_exponent=post_phase + pre_phase)._canonical() @@ -131,11 +131,11 @@ def _decompose_(self, qubits: Sequence['cirq.Qid']) -> 'cirq.OP_TREE': yield ops.X(q)**self._x_exponent yield ops.Z(q)**(self._axis_phase_exponent + self._z_exponent) - def __pow__(self, exponent: Union[float, int]) -> 'PhasedXPowZPowGate': + def __pow__(self, exponent: Union[float, int]) -> 'PhasedXZGate': if exponent == 1: return self if exponent == -1: - return PhasedXPowZPowGate( + return PhasedXZGate( x_exponent=-self._x_exponent, z_exponent=-self._z_exponent, axis_phase_exponent=self._z_exponent + self.axis_phase_exponent, @@ -148,19 +148,19 @@ def _is_parameterized_(self) -> bool: protocols.is_parameterized(self._z_exponent) or protocols.is_parameterized(self._axis_phase_exponent)) - def _resolve_parameters_(self, param_resolver) -> 'cirq.PhasedXPowZPowGate': + def _resolve_parameters_(self, param_resolver) -> 'cirq.PhasedXZGate': """See `cirq.SupportsParameterization`.""" - return PhasedXPowZPowGate( + return PhasedXZGate( z_exponent=param_resolver.value_of(self._z_exponent), x_exponent=param_resolver.value_of(self._x_exponent), axis_phase_exponent=param_resolver.value_of( self._axis_phase_exponent), ) - def _phase_by_(self, phase_turns, qubit_index) -> 'cirq.PhasedXPowZPowGate': + def _phase_by_(self, phase_turns, qubit_index) -> 'cirq.PhasedXZGate': """See `cirq.SupportsPhase`.""" assert qubit_index == 0 - return PhasedXPowZPowGate( + return PhasedXZGate( x_exponent=self._x_exponent, z_exponent=self._z_exponent, axis_phase_exponent=self._axis_phase_exponent + phase_turns * 2) @@ -177,7 +177,7 @@ def __str__(self): return protocols.circuit_diagram_info(self).wire_symbols[0] def __repr__(self): - return (f'cirq.PhasedXPowZPowGate(' + return (f'cirq.PhasedXZGate(' f'axis_phase_exponent={proper_repr(self._axis_phase_exponent)},' f' x_exponent={proper_repr(self._x_exponent)}, ' f'z_exponent={proper_repr(self._z_exponent)})') diff --git a/cirq/ops/phased_x_z_gate_test.py b/cirq/ops/phased_x_z_gate_test.py index 0b5ef2105bc..4744dd3221a 100644 --- a/cirq/ops/phased_x_z_gate_test.py +++ b/cirq/ops/phased_x_z_gate_test.py @@ -7,7 +7,7 @@ def test_init_properties(): - g = cirq.PhasedXPowZPowGate(x_exponent=0.125, + g = cirq.PhasedXZGate(x_exponent=0.125, z_exponent=0.25, axis_phase_exponent=0.375) assert g.x_exponent == 0.125 @@ -17,20 +17,20 @@ def test_init_properties(): def test_eq(): eq = cirq.testing.EqualsTester() - eq.make_equality_group(lambda: cirq.PhasedXPowZPowGate( + eq.make_equality_group(lambda: cirq.PhasedXZGate( x_exponent=0.25, z_exponent=0.5, axis_phase_exponent=0.75)) # Sensitive to each parameter. eq.add_equality_group( - cirq.PhasedXPowZPowGate(x_exponent=0, + cirq.PhasedXZGate(x_exponent=0, z_exponent=0.5, axis_phase_exponent=0.75)) eq.add_equality_group( - cirq.PhasedXPowZPowGate(x_exponent=0.25, + cirq.PhasedXZGate(x_exponent=0.25, z_exponent=0, axis_phase_exponent=0.75)) eq.add_equality_group( - cirq.PhasedXPowZPowGate(x_exponent=0.25, + cirq.PhasedXZGate(x_exponent=0.25, z_exponent=0.5, axis_phase_exponent=0)) @@ -39,7 +39,7 @@ def test_eq(): cirq.PhasedXPowGate(exponent=0.25, phase_exponent=0.75)) eq.add_equality_group(cirq.X) eq.add_equality_group( - cirq.PhasedXPowZPowGate(x_exponent=1, + cirq.PhasedXZGate(x_exponent=1, z_exponent=0, axis_phase_exponent=0)) @@ -47,7 +47,7 @@ def test_eq(): def test_canonicalization(): def f(x, z, a): - return cirq.PhasedXPowZPowGate(x_exponent=x, + return cirq.PhasedXZGate(x_exponent=x, z_exponent=z, axis_phase_exponent=a) @@ -133,64 +133,64 @@ def f(x, z, a): def test_from_matrix(): # Axis rotations. - assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( + assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( cirq.unitary(cirq.X**0.1)), - cirq.PhasedXPowZPowGate(x_exponent=0.1, + cirq.PhasedXZGate(x_exponent=0.1, z_exponent=0, axis_phase_exponent=0), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( + assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( cirq.unitary(cirq.X**-0.1)), - cirq.PhasedXPowZPowGate(x_exponent=-0.1, + cirq.PhasedXZGate(x_exponent=-0.1, z_exponent=0, axis_phase_exponent=0), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( + assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( cirq.unitary(cirq.Y**0.1)), - cirq.PhasedXPowZPowGate(x_exponent=0.1, + cirq.PhasedXZGate(x_exponent=0.1, z_exponent=0, axis_phase_exponent=0.5), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( + assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( cirq.unitary(cirq.Y**-0.1)), - cirq.PhasedXPowZPowGate(x_exponent=-0.1, + cirq.PhasedXZGate(x_exponent=-0.1, z_exponent=0, axis_phase_exponent=0.5), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( + assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( cirq.unitary(cirq.Z**-0.1)), - cirq.PhasedXPowZPowGate(x_exponent=0, + cirq.PhasedXZGate(x_exponent=0, z_exponent=-0.1, axis_phase_exponent=0), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( + assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( cirq.unitary(cirq.Z**0.1)), - cirq.PhasedXPowZPowGate(x_exponent=0, + cirq.PhasedXZGate(x_exponent=0, z_exponent=0.1, axis_phase_exponent=0), atol=1e-8) # Pauli matrices. - assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix(np.eye(2)), - cirq.PhasedXPowZPowGate(x_exponent=0, + assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix(np.eye(2)), + cirq.PhasedXZGate(x_exponent=0, z_exponent=0, axis_phase_exponent=0), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( + assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( cirq.unitary(cirq.X)), - cirq.PhasedXPowZPowGate(x_exponent=1, + cirq.PhasedXZGate(x_exponent=1, z_exponent=0, axis_phase_exponent=0), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( + assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( cirq.unitary(cirq.Y)), - cirq.PhasedXPowZPowGate(x_exponent=1, + cirq.PhasedXZGate(x_exponent=1, z_exponent=0, axis_phase_exponent=0.5), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix( + assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( cirq.unitary(cirq.Z)), - cirq.PhasedXPowZPowGate(x_exponent=0, + cirq.PhasedXZGate(x_exponent=0, z_exponent=1, axis_phase_exponent=0), atol=1e-8) @@ -199,15 +199,15 @@ def test_from_matrix(): a = random.random() b = random.random() c = random.random() - g = cirq.PhasedXPowZPowGate(x_exponent=a, + g = cirq.PhasedXZGate(x_exponent=a, z_exponent=b, axis_phase_exponent=c) - assert cirq.approx_eq(cirq.PhasedXPowZPowGate.from_matrix(cirq.unitary(g)), + assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix(cirq.unitary(g)), g, atol=1e-8) u = cirq.testing.random_unitary(2) cirq.testing.assert_allclose_up_to_global_phase(cirq.unitary( - cirq.PhasedXPowZPowGate.from_matrix(u)), + cirq.PhasedXZGate.from_matrix(u)), u, atol=1e-8) @@ -216,22 +216,22 @@ def test_protocols(): a = random.random() b = random.random() c = random.random() - g = cirq.PhasedXPowZPowGate(x_exponent=a, + g = cirq.PhasedXZGate(x_exponent=a, z_exponent=b, axis_phase_exponent=c) cirq.testing.assert_implements_consistent_protocols(g) # Symbolic. t = sympy.Symbol('t') - g = cirq.PhasedXPowZPowGate(x_exponent=t, + g = cirq.PhasedXZGate(x_exponent=t, z_exponent=b, axis_phase_exponent=c) cirq.testing.assert_implements_consistent_protocols(g) - g = cirq.PhasedXPowZPowGate(x_exponent=a, + g = cirq.PhasedXZGate(x_exponent=a, z_exponent=t, axis_phase_exponent=c) cirq.testing.assert_implements_consistent_protocols(g) - g = cirq.PhasedXPowZPowGate(x_exponent=a, + g = cirq.PhasedXZGate(x_exponent=a, z_exponent=b, axis_phase_exponent=t) cirq.testing.assert_implements_consistent_protocols(g) @@ -242,7 +242,7 @@ def test_inverse(): b = random.random() c = random.random() q = cirq.LineQubit(0) - g = cirq.PhasedXPowZPowGate(x_exponent=a, + g = cirq.PhasedXZGate(x_exponent=a, z_exponent=b, axis_phase_exponent=c).on(q) @@ -256,23 +256,23 @@ def test_parameterized(): a = random.random() b = random.random() c = random.random() - g = cirq.PhasedXPowZPowGate(x_exponent=a, + g = cirq.PhasedXZGate(x_exponent=a, z_exponent=b, axis_phase_exponent=c) assert not cirq.is_parameterized(g) t = sympy.Symbol('t') - gt = cirq.PhasedXPowZPowGate(x_exponent=t, + gt = cirq.PhasedXZGate(x_exponent=t, z_exponent=b, axis_phase_exponent=c) assert cirq.is_parameterized(gt) assert cirq.resolve_parameters(gt, {'t': a}) == g - gt = cirq.PhasedXPowZPowGate(x_exponent=a, + gt = cirq.PhasedXZGate(x_exponent=a, z_exponent=t, axis_phase_exponent=c) assert cirq.is_parameterized(gt) assert cirq.resolve_parameters(gt, {'t': b}) == g - gt = cirq.PhasedXPowZPowGate(x_exponent=a, + gt = cirq.PhasedXZGate(x_exponent=a, z_exponent=b, axis_phase_exponent=t) assert cirq.is_parameterized(gt) @@ -280,7 +280,7 @@ def test_parameterized(): def test_str_diagram(): - g = cirq.PhasedXPowZPowGate(x_exponent=0.5, + g = cirq.PhasedXZGate(x_exponent=0.5, z_exponent=0.25, axis_phase_exponent=0.125) diff --git a/cirq/protocols/json.py b/cirq/protocols/json.py index 73306a28b13..be7b236c57c 100644 --- a/cirq/protocols/json.py +++ b/cirq/protocols/json.py @@ -105,7 +105,7 @@ def _identity_operation_from_dict(qubits, **kwargs): 'PhaseGradientGate': cirq.PhaseGradientGate, 'PhasedISwapPowGate': cirq.PhasedISwapPowGate, 'PhasedXPowGate': cirq.PhasedXPowGate, - 'PhasedXPowZPowGate': cirq.PhasedXPowZPowGate, + 'PhasedXZGate': cirq.PhasedXZGate, 'QuantumFourierTransformGate': cirq.QuantumFourierTransformGate, 'ResetChannel': cirq.ResetChannel, 'SingleQubitMatrixGate': cirq.SingleQubitMatrixGate, diff --git a/cirq/protocols/json_test_data/PhasedXPowZPowGate.repr b/cirq/protocols/json_test_data/PhasedXPowZPowGate.repr deleted file mode 100644 index 7815302e84f..00000000000 --- a/cirq/protocols/json_test_data/PhasedXPowZPowGate.repr +++ /dev/null @@ -1,3 +0,0 @@ -[ -cirq.PhasedXPowZPowGate(axis_phase_exponent=0.75, x_exponent=0.5, z_exponent=0.25), -] \ No newline at end of file diff --git a/cirq/protocols/json_test_data/PhasedXPowZPowGate.json b/cirq/protocols/json_test_data/PhasedXZGate.json similarity index 69% rename from cirq/protocols/json_test_data/PhasedXPowZPowGate.json rename to cirq/protocols/json_test_data/PhasedXZGate.json index 283d78e28a5..ae2e3d64b94 100644 --- a/cirq/protocols/json_test_data/PhasedXPowZPowGate.json +++ b/cirq/protocols/json_test_data/PhasedXZGate.json @@ -1,6 +1,6 @@ [ { - "cirq_type": "PhasedXPowZPowGate", + "cirq_type": "PhasedXZGate", "axis_phase_exponent": 0.75, "x_exponent": 0.5, "z_exponent": 0.25 diff --git a/cirq/protocols/json_test_data/PhasedXZGate.repr b/cirq/protocols/json_test_data/PhasedXZGate.repr new file mode 100644 index 00000000000..bc742d2433d --- /dev/null +++ b/cirq/protocols/json_test_data/PhasedXZGate.repr @@ -0,0 +1,3 @@ +[ +cirq.PhasedXZGate(axis_phase_exponent=0.75, x_exponent=0.5, z_exponent=0.25), +] \ No newline at end of file diff --git a/docs/api.rst b/docs/api.rst index 189bcc9e60e..b576d45393c 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -93,7 +93,7 @@ Unitary effects that can be applied to one or more qubits. cirq.PhaseGradientGate cirq.PhasedISwapPowGate cirq.PhasedXPowGate - cirq.PhasedXPowZPowGate + cirq.PhasedXZGate cirq.QuantumFourierTransformGate cirq.SingleQubitGate cirq.SingleQubitMatrixGate From 73decb6a44014b24b030998ca1912e2a60ebcb8e Mon Sep 17 00:00:00 2001 From: Craig Gidney Date: Thu, 21 Nov 2019 14:19:02 -0800 Subject: [PATCH 11/11] format --- cirq/ops/phased_x_z_gate.py | 17 ++--- cirq/ops/phased_x_z_gate_test.py | 118 ++++++++++++------------------- 2 files changed, 54 insertions(+), 81 deletions(-) diff --git a/cirq/ops/phased_x_z_gate.py b/cirq/ops/phased_x_z_gate.py index 19f25d3de4c..4c4d510c2fb 100644 --- a/cirq/ops/phased_x_z_gate.py +++ b/cirq/ops/phased_x_z_gate.py @@ -78,9 +78,7 @@ def _canonical(self) -> 'cirq.PhasedXZGate': a -= 1 x = -x - return PhasedXZGate(x_exponent=x, - z_exponent=z, - axis_phase_exponent=a) + return PhasedXZGate(x_exponent=x, z_exponent=z, axis_phase_exponent=a) @property def x_exponent(self) -> Union[numbers.Real, sympy.Basic]: @@ -112,9 +110,8 @@ def from_matrix(mat: np.array) -> 'cirq.PhasedXZGate': pre_phase -= 0.5 post_phase += 0.5 return PhasedXZGate(x_exponent=rotation, - axis_phase_exponent=-pre_phase, - z_exponent=post_phase + - pre_phase)._canonical() + axis_phase_exponent=-pre_phase, + z_exponent=post_phase + pre_phase)._canonical() def _qasm_(self, args: 'cirq.QasmArgs', qubits: Tuple['cirq.Qid', ...]) -> Optional[str]: @@ -160,10 +157,10 @@ def _resolve_parameters_(self, param_resolver) -> 'cirq.PhasedXZGate': def _phase_by_(self, phase_turns, qubit_index) -> 'cirq.PhasedXZGate': """See `cirq.SupportsPhase`.""" assert qubit_index == 0 - return PhasedXZGate( - x_exponent=self._x_exponent, - z_exponent=self._z_exponent, - axis_phase_exponent=self._axis_phase_exponent + phase_turns * 2) + return PhasedXZGate(x_exponent=self._x_exponent, + z_exponent=self._z_exponent, + axis_phase_exponent=self._axis_phase_exponent + + phase_turns * 2) def _circuit_diagram_info_(self, args: 'cirq.CircuitDiagramInfoArgs') -> str: diff --git a/cirq/ops/phased_x_z_gate_test.py b/cirq/ops/phased_x_z_gate_test.py index 4744dd3221a..2c10aca5f31 100644 --- a/cirq/ops/phased_x_z_gate_test.py +++ b/cirq/ops/phased_x_z_gate_test.py @@ -8,8 +8,8 @@ def test_init_properties(): g = cirq.PhasedXZGate(x_exponent=0.125, - z_exponent=0.25, - axis_phase_exponent=0.375) + z_exponent=0.25, + axis_phase_exponent=0.375) assert g.x_exponent == 0.125 assert g.z_exponent == 0.25 assert g.axis_phase_exponent == 0.375 @@ -23,33 +23,31 @@ def test_eq(): # Sensitive to each parameter. eq.add_equality_group( cirq.PhasedXZGate(x_exponent=0, - z_exponent=0.5, - axis_phase_exponent=0.75)) + z_exponent=0.5, + axis_phase_exponent=0.75)) eq.add_equality_group( cirq.PhasedXZGate(x_exponent=0.25, - z_exponent=0, - axis_phase_exponent=0.75)) + z_exponent=0, + axis_phase_exponent=0.75)) eq.add_equality_group( cirq.PhasedXZGate(x_exponent=0.25, - z_exponent=0.5, - axis_phase_exponent=0)) + z_exponent=0.5, + axis_phase_exponent=0)) # Different from other gates. eq.add_equality_group( cirq.PhasedXPowGate(exponent=0.25, phase_exponent=0.75)) eq.add_equality_group(cirq.X) eq.add_equality_group( - cirq.PhasedXZGate(x_exponent=1, - z_exponent=0, - axis_phase_exponent=0)) + cirq.PhasedXZGate(x_exponent=1, z_exponent=0, axis_phase_exponent=0)) def test_canonicalization(): def f(x, z, a): return cirq.PhasedXZGate(x_exponent=x, - z_exponent=z, - axis_phase_exponent=a) + z_exponent=z, + axis_phase_exponent=a) # Canonicalizations are equivalent. eq = cirq.testing.EqualsTester() @@ -136,72 +134,67 @@ def test_from_matrix(): assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( cirq.unitary(cirq.X**0.1)), cirq.PhasedXZGate(x_exponent=0.1, - z_exponent=0, - axis_phase_exponent=0), + z_exponent=0, + axis_phase_exponent=0), atol=1e-8) assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( cirq.unitary(cirq.X**-0.1)), cirq.PhasedXZGate(x_exponent=-0.1, - z_exponent=0, - axis_phase_exponent=0), + z_exponent=0, + axis_phase_exponent=0), atol=1e-8) assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( cirq.unitary(cirq.Y**0.1)), cirq.PhasedXZGate(x_exponent=0.1, - z_exponent=0, - axis_phase_exponent=0.5), + z_exponent=0, + axis_phase_exponent=0.5), atol=1e-8) assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( cirq.unitary(cirq.Y**-0.1)), cirq.PhasedXZGate(x_exponent=-0.1, - z_exponent=0, - axis_phase_exponent=0.5), + z_exponent=0, + axis_phase_exponent=0.5), atol=1e-8) assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( cirq.unitary(cirq.Z**-0.1)), cirq.PhasedXZGate(x_exponent=0, - z_exponent=-0.1, - axis_phase_exponent=0), + z_exponent=-0.1, + axis_phase_exponent=0), atol=1e-8) assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( cirq.unitary(cirq.Z**0.1)), cirq.PhasedXZGate(x_exponent=0, - z_exponent=0.1, - axis_phase_exponent=0), + z_exponent=0.1, + axis_phase_exponent=0), atol=1e-8) # Pauli matrices. assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix(np.eye(2)), cirq.PhasedXZGate(x_exponent=0, - z_exponent=0, - axis_phase_exponent=0), + z_exponent=0, + axis_phase_exponent=0), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( - cirq.unitary(cirq.X)), + assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix(cirq.unitary(cirq.X)), cirq.PhasedXZGate(x_exponent=1, - z_exponent=0, - axis_phase_exponent=0), + z_exponent=0, + axis_phase_exponent=0), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( - cirq.unitary(cirq.Y)), + assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix(cirq.unitary(cirq.Y)), cirq.PhasedXZGate(x_exponent=1, - z_exponent=0, - axis_phase_exponent=0.5), + z_exponent=0, + axis_phase_exponent=0.5), atol=1e-8) - assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix( - cirq.unitary(cirq.Z)), + assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix(cirq.unitary(cirq.Z)), cirq.PhasedXZGate(x_exponent=0, - z_exponent=1, - axis_phase_exponent=0), + z_exponent=1, + axis_phase_exponent=0), atol=1e-8) # Round trips. a = random.random() b = random.random() c = random.random() - g = cirq.PhasedXZGate(x_exponent=a, - z_exponent=b, - axis_phase_exponent=c) + g = cirq.PhasedXZGate(x_exponent=a, z_exponent=b, axis_phase_exponent=c) assert cirq.approx_eq(cirq.PhasedXZGate.from_matrix(cirq.unitary(g)), g, atol=1e-8) @@ -216,24 +209,16 @@ def test_protocols(): a = random.random() b = random.random() c = random.random() - g = cirq.PhasedXZGate(x_exponent=a, - z_exponent=b, - axis_phase_exponent=c) + g = cirq.PhasedXZGate(x_exponent=a, z_exponent=b, axis_phase_exponent=c) cirq.testing.assert_implements_consistent_protocols(g) # Symbolic. t = sympy.Symbol('t') - g = cirq.PhasedXZGate(x_exponent=t, - z_exponent=b, - axis_phase_exponent=c) + g = cirq.PhasedXZGate(x_exponent=t, z_exponent=b, axis_phase_exponent=c) cirq.testing.assert_implements_consistent_protocols(g) - g = cirq.PhasedXZGate(x_exponent=a, - z_exponent=t, - axis_phase_exponent=c) + g = cirq.PhasedXZGate(x_exponent=a, z_exponent=t, axis_phase_exponent=c) cirq.testing.assert_implements_consistent_protocols(g) - g = cirq.PhasedXZGate(x_exponent=a, - z_exponent=b, - axis_phase_exponent=t) + g = cirq.PhasedXZGate(x_exponent=a, z_exponent=b, axis_phase_exponent=t) cirq.testing.assert_implements_consistent_protocols(g) @@ -242,9 +227,8 @@ def test_inverse(): b = random.random() c = random.random() q = cirq.LineQubit(0) - g = cirq.PhasedXZGate(x_exponent=a, - z_exponent=b, - axis_phase_exponent=c).on(q) + g = cirq.PhasedXZGate(x_exponent=a, z_exponent=b, + axis_phase_exponent=c).on(q) cirq.testing.assert_allclose_up_to_global_phase( cirq.unitary(g**-1), @@ -256,33 +240,25 @@ def test_parameterized(): a = random.random() b = random.random() c = random.random() - g = cirq.PhasedXZGate(x_exponent=a, - z_exponent=b, - axis_phase_exponent=c) + g = cirq.PhasedXZGate(x_exponent=a, z_exponent=b, axis_phase_exponent=c) assert not cirq.is_parameterized(g) t = sympy.Symbol('t') - gt = cirq.PhasedXZGate(x_exponent=t, - z_exponent=b, - axis_phase_exponent=c) + gt = cirq.PhasedXZGate(x_exponent=t, z_exponent=b, axis_phase_exponent=c) assert cirq.is_parameterized(gt) assert cirq.resolve_parameters(gt, {'t': a}) == g - gt = cirq.PhasedXZGate(x_exponent=a, - z_exponent=t, - axis_phase_exponent=c) + gt = cirq.PhasedXZGate(x_exponent=a, z_exponent=t, axis_phase_exponent=c) assert cirq.is_parameterized(gt) assert cirq.resolve_parameters(gt, {'t': b}) == g - gt = cirq.PhasedXZGate(x_exponent=a, - z_exponent=b, - axis_phase_exponent=t) + gt = cirq.PhasedXZGate(x_exponent=a, z_exponent=b, axis_phase_exponent=t) assert cirq.is_parameterized(gt) assert cirq.resolve_parameters(gt, {'t': c}) == g def test_str_diagram(): g = cirq.PhasedXZGate(x_exponent=0.5, - z_exponent=0.25, - axis_phase_exponent=0.125) + z_exponent=0.25, + axis_phase_exponent=0.125) assert str(g) == "PhXZ(a=0.125,x=0.5,z=0.25)"