-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add cirq.PhasedXPowZPowGate #2566
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
7753528
68c6f43
575a7ba
9062921
aed306e
f395ad4
41b99a1
67a2db9
feea4d0
bf62609
a0d7417
e981df3
66ac75a
6bf259c
73decb6
12731b9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -234,6 +234,7 @@ | |
| PhaseGradientGate, | ||
| PhasedISwapPowGate, | ||
| PhasedXPowGate, | ||
| PhasedXZGate, | ||
| PhaseFlipChannel, | ||
| QFT, | ||
| Qid, | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,184 @@ | ||
| import numbers | ||
| from typing import Union, TYPE_CHECKING, Tuple, Optional, 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 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. | ||
| 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. | ||
| """ | ||
|
|
||
| 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: 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.PhasedXZGate': | ||
| x = self.x_exponent | ||
| z = self.z_exponent | ||
| a = self.axis_phase_exponent | ||
|
|
||
| # Canonicalize X exponent into (-1, +1]. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could also canonicalize x to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Acknowledged. The exact canonicalization is not too important; it's only really relevant for equality. The user doesn't see it. |
||
| 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 into (-1, +1]. | ||
| if isinstance(z, numbers.Real): | ||
| z %= 2 | ||
| if z > 1: | ||
| z -= 2 | ||
|
|
||
| # Canonicalize axis phase exponent into (-0.5, +0.5]. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned above, it would match our internal convention more closely if we instead canonicalize x to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Acknowledged. The exact canonicalization is not too important; it's only really relevant for equality. The user doesn't see it. |
||
| 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 PhasedXZGate(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.PhasedXZGate': | ||
| 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 PhasedXZGate(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._axis_phase_exponent + self._z_exponent) | ||
|
|
||
| def __pow__(self, exponent: Union[float, int]) -> 'PhasedXZGate': | ||
| if exponent == 1: | ||
| return self | ||
| if exponent == -1: | ||
| return PhasedXZGate( | ||
| 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.PhasedXZGate': | ||
| """See `cirq.SupportsParameterization`.""" | ||
| 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.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) | ||
|
|
||
| def _circuit_diagram_info_(self, | ||
| args: 'cirq.CircuitDiagramInfoArgs') -> str: | ||
| """See `cirq.SupportsCircuitDiagramInfo`.""" | ||
| return (f'PhXZ(' | ||
| 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)})') | ||
|
|
||
| def __str__(self): | ||
| return protocols.circuit_diagram_info(self).wire_symbols[0] | ||
|
|
||
| def __repr__(self): | ||
| 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)})') | ||
|
|
||
| def _json_dict_(self): | ||
| return protocols.obj_to_dict_helper( | ||
| self, ['axis_phase_exponent', 'x_exponent', 'z_exponent']) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This expression is matrix multiplication with time going to the left? Would be nice to make this clear so readers don't misinterpret this as a "circuit" with time going to the right. (This is implicit in the constructor docstring for
z_exponent, but would be good to be clear up front.)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarified.