Skip to content

Commit

Permalink
Add cirq.PhasedXPowZPowGate (#2566)
Browse files Browse the repository at this point in the history
This gives a canonical single qubit operation to optimize into when doing microwave rotations with virtual Zs
  • Loading branch information
Strilanc authored and CirqBot committed Nov 21, 2019
1 parent 35c64e4 commit ebd0b36
Show file tree
Hide file tree
Showing 8 changed files with 469 additions and 0 deletions.
1 change: 1 addition & 0 deletions cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@
PhaseGradientGate,
PhasedISwapPowGate,
PhasedXPowGate,
PhasedXZGate,
PhaseFlipChannel,
QFT,
Qid,
Expand Down
3 changes: 3 additions & 0 deletions cirq/ops/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@
from cirq.ops.phased_x_gate import (
PhasedXPowGate,)

from cirq.ops.phased_x_z_gate import (
PhasedXZGate,)

from cirq.ops.raw_types import (
Gate,
Operation,
Expand Down
184 changes: 184 additions & 0 deletions cirq/ops/phased_x_z_gate.py
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].
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].
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'])

0 comments on commit ebd0b36

Please sign in to comment.