Skip to content

Commit

Permalink
Rename Mølmer–Sørensen gate factory (#2518)
Browse files Browse the repository at this point in the history
Partially implements #2508 and #1681.
  • Loading branch information
viathor authored and CirqBot committed Nov 14, 2019
1 parent d6f65c2 commit 08df3b4
Show file tree
Hide file tree
Showing 11 changed files with 71 additions and 42 deletions.
1 change: 1 addition & 0 deletions cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@
ConvertToIonGates,
IonDevice,
MS,
ms,
two_qubit_matrix_to_ion_operations,
)
from cirq.neutral_atoms import (
Expand Down
4 changes: 3 additions & 1 deletion cirq/ion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
"""

from cirq.ion.ion_gates import (
MS,)
MS,
ms,
)

from cirq.ion.ion_decomposition import (
two_qubit_matrix_to_ion_operations,)
Expand Down
20 changes: 12 additions & 8 deletions cirq/ion/convert_to_ion_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import numpy as np

from cirq import ops, protocols, optimizers, circuits
from cirq.ion import MS, two_qubit_matrix_to_ion_operations
from cirq.ion import ms, two_qubit_matrix_to_ion_operations


class ConvertToIonGates:
Expand Down Expand Up @@ -50,15 +50,19 @@ def convert_one(self, op: ops.Operation) -> ops.OP_TREE:
return [op]
# one choice of known Hadamard gate decomposition
if isinstance(op.gate, ops.HPowGate) and op.gate.exponent == 1:
return [ops.Rx(np.pi).on(op.qubits[0]),
ops.Ry(-1 * np.pi/2).on(op.qubits[0])]
return [
ops.Rx(np.pi).on(op.qubits[0]),
ops.Ry(-1 * np.pi / 2).on(op.qubits[0])
]
# one choice of known CNOT gate decomposition
if isinstance(op.gate, ops.CNotPowGate) and op.gate.exponent == 1:
return [ops.Ry(np.pi/2).on(op.qubits[0]),
MS(np.pi/4).on(op.qubits[0], op.qubits[1]),
ops.Rx(-1*np.pi/2).on(op.qubits[0]),
ops.Rx(-1*np.pi/2).on(op.qubits[1]),
ops.Ry(-1*np.pi/2).on(op.qubits[0])]
return [
ops.Ry(np.pi / 2).on(op.qubits[0]),
ms(np.pi / 4).on(op.qubits[0], op.qubits[1]),
ops.Rx(-1 * np.pi / 2).on(op.qubits[0]),
ops.Rx(-1 * np.pi / 2).on(op.qubits[1]),
ops.Ry(-1 * np.pi / 2).on(op.qubits[0])
]
# Known matrix
mat = protocols.unitary(op, None) if len(
op.qubits) <= 2 else None
Expand Down
25 changes: 15 additions & 10 deletions cirq/ion/convert_to_ion_gates_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,20 @@ def test_convert_to_ion_gates():
assert rx == [
cirq.PhasedXPowGate(phase_exponent=1).on(cirq.GridQubit(0, 0))
]
assert rop == [cirq.Ry(np.pi/2).on(op.qubits[0]),
cirq.ion.MS(np.pi/4).on(op.qubits[0], op.qubits[1]),
cirq.ops.Rx(-1*np.pi/2).on(op.qubits[0]),
cirq.ops.Rx(-1*np.pi/2).on(op.qubits[1]),
cirq.ops.Ry(-1*np.pi/2).on(op.qubits[0])]
assert rop == [
cirq.Ry(np.pi / 2).on(op.qubits[0]),
cirq.ms(np.pi / 4).on(op.qubits[0], op.qubits[1]),
cirq.Rx(-1 * np.pi / 2).on(op.qubits[0]),
cirq.Rx(-1 * np.pi / 2).on(op.qubits[1]),
cirq.Ry(-1 * np.pi / 2).on(op.qubits[0])
]
assert rcnot == [
cirq.PhasedXPowGate(phase_exponent=-0.75,
exponent=0.5).on(cirq.GridQubit(0, 0)),
cirq.PhasedXPowGate(phase_exponent=1,
exponent=0.25).on(cirq.GridQubit(0, 1)),
cirq.T.on(cirq.GridQubit(0, 0)),
cirq.MS(-0.5 * np.pi / 2).on(cirq.GridQubit(0, 0), cirq.GridQubit(0,
cirq.ms(-0.5 * np.pi / 2).on(cirq.GridQubit(0, 0), cirq.GridQubit(0,
1)),
(cirq.Y**0.5).on(cirq.GridQubit(0, 0)),
cirq.PhasedXPowGate(phase_exponent=1,
Expand All @@ -85,17 +87,20 @@ def test_convert_to_ion_circuit():
ion_device = cirq.IonDevice(us, us, us, [q0, q1])

clifford_circuit_1 = cirq.Circuit()
clifford_circuit_1.append([cirq.X(q0), cirq.H(q1),
cirq.MS(np.pi/4).on(q0, q1)])
clifford_circuit_1.append(
[cirq.X(q0), cirq.H(q1),
cirq.ms(np.pi / 4).on(q0, q1)])
ion_circuit_1 = cirq.ion.ConvertToIonGates().convert_circuit(
clifford_circuit_1)

ion_device.validate_circuit(ion_circuit_1)
cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent(
clifford_circuit_1, ion_circuit_1, atol=1e-6)
clifford_circuit_2 = cirq.Circuit()
clifford_circuit_2.append([cirq.X(q0), cirq.CNOT(q1, q0), cirq.MS(
np.pi/4).on(q0, q1)])
clifford_circuit_2.append(
[cirq.X(q0),
cirq.CNOT(q1, q0),
cirq.ms(np.pi / 4).on(q0, q1)])
ion_circuit_2 = cirq.ion.ConvertToIonGates().convert_circuit(
clifford_circuit_2)
ion_device.validate_circuit(ion_circuit_2)
Expand Down
4 changes: 2 additions & 2 deletions cirq/ion/ion_decomposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import numpy as np

from cirq import ops, linalg, protocols, optimizers, circuits
from cirq.ion import MS
from cirq.ion import ms


def two_qubit_matrix_to_ion_operations(q0: ops.Qid,
Expand Down Expand Up @@ -103,7 +103,7 @@ def _parity_interaction(q0: ops.Qid,
g = cast(ops.Gate, gate)
yield g.on(q0), g.on(q1)

yield MS(-1 * rads).on(q0, q1)
yield ms(-1 * rads).on(q0, q1)

if gate is not None:
g = protocols.inverse(gate)
Expand Down
4 changes: 3 additions & 1 deletion cirq/ion/ion_decomposition_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def assert_ms_depth_below(operations, threshold):
assert total_ms <= threshold


# yapf: disable
@pytest.mark.parametrize('max_ms_depth,effect', [
(0, np.eye(4)),
(0, np.array([
Expand All @@ -79,7 +80,7 @@ def assert_ms_depth_below(operations, threshold):
[0, 1, 0, 0],
[1, 0, 0, 0j]
])),
(1, cirq.unitary(cirq.MS(np.pi/4))),
(1, cirq.unitary(cirq.ms(np.pi/4))),
(0, cirq.unitary(cirq.CZ ** 0.00000001)),
(0.5, cirq.unitary(cirq.CZ ** 0.5)),
Expand Down Expand Up @@ -124,6 +125,7 @@ def assert_ms_depth_below(operations, threshold):
] + [
(2, _random_double_MS_effect()) for _ in range(10)
])
# yapf: enable
def test_two_to_ops(
max_ms_depth: int,
effect: np.array):
Expand Down
8 changes: 7 additions & 1 deletion cirq/ion/ion_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
import numpy as np

from cirq import ops
from cirq._compat import deprecated


def MS(rads: float) -> ops.XXPowGate:
def ms(rads: float) -> ops.XXPowGate:
"""The Mølmer–Sørensen gate, a native two-qubit operation in ion traps.
A rotation around the XX axis in the two-qubit bloch sphere.
Expand All @@ -38,3 +39,8 @@ def MS(rads: float) -> ops.XXPowGate:
Mølmer–Sørensen gate rotating by the desired amount.
"""
return ops.XXPowGate(exponent=rads*2/np.pi, global_shift=-0.5)


@deprecated(deadline='v0.8.0', fix='Use cirq.ms, instead.')
def MS(rads: float) -> ops.XXPowGate:
return ms(rads)
36 changes: 22 additions & 14 deletions cirq/ion/ion_gates_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,47 +13,55 @@
# limitations under the License.

import numpy as np
import pytest

import cirq


def test_MS_arguments():
def test_ms_arguments():
eq_tester = cirq.testing.EqualsTester()
eq_tester.add_equality_group(cirq.MS(np.pi/2),
eq_tester.add_equality_group(cirq.ms(np.pi / 2),
cirq.XXPowGate(global_shift=-0.5))


def test_MS_str():
assert str(cirq.MS(np.pi/2)) == 'MS(π/2)'
assert str(cirq.MS(np.pi)) == 'MS(2.0π/2)'
def test_ms_str():
assert str(cirq.ms(np.pi / 2)) == 'MS(π/2)'
assert str(cirq.ms(np.pi)) == 'MS(2.0π/2)'


def test_MS_matrix():
def test_ms_matrix():
s = np.sqrt(0.5)
np.testing.assert_allclose(cirq.unitary(cirq.MS(np.pi/4)),
# yapf: disable
np.testing.assert_allclose(cirq.unitary(cirq.ms(np.pi/4)),
np.array([[s, 0, 0, -1j*s],
[0, s, -1j*s, 0],
[0, -1j*s, s, 0],
[-1j*s, 0, 0, s]]),
atol=1e-8)
np.testing.assert_allclose(cirq.unitary(cirq.MS(np.pi)),
# yapf: enable
np.testing.assert_allclose(cirq.unitary(cirq.ms(np.pi)),
np.diag([-1, -1, -1, -1]),
atol=1e-8)


def test_MS_repr():
assert repr(cirq.MS(np.pi/2)) == 'cirq.MS(np.pi/2)'
assert repr(cirq.MS(np.pi/4)) == 'cirq.MS(0.5*np.pi/2)'
cirq.testing.assert_equivalent_repr(cirq.MS(np.pi/4))
def test_ms_repr():
assert repr(cirq.ms(np.pi / 2)) == 'cirq.ms(np.pi/2)'
assert repr(cirq.ms(np.pi / 4)) == 'cirq.ms(0.5*np.pi/2)'
cirq.testing.assert_equivalent_repr(cirq.ms(np.pi / 4))


def test_MS_diagrams():
def test_ms_diagrams():
a = cirq.NamedQubit('a')
b = cirq.NamedQubit('b')
circuit = cirq.Circuit(cirq.SWAP(a, b), cirq.X(a), cirq.Y(a),
cirq.MS(np.pi).on(a, b))
cirq.ms(np.pi).on(a, b))
cirq.testing.assert_has_diagram(circuit, """
a: ───×───X───Y───MS(π)───
│ │
b: ───×───────────MS(π)───
""")


@pytest.mark.parametrize('rads', (-1, -0.1, 0.2, 1))
def test_deprecated_ms(rads):
assert np.all(cirq.unitary(cirq.ms(rads)) == cirq.unitary(cirq.MS(rads)))
4 changes: 2 additions & 2 deletions cirq/ops/parity_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ def __str__(self) -> str:
def __repr__(self) -> str:
if self._global_shift == -0.5 and not protocols.is_parameterized(self):
if self._exponent == 1:
return 'cirq.MS(np.pi/2)'
return 'cirq.MS({!r}*np.pi/2)'.format(self._exponent)
return 'cirq.ms(np.pi/2)'
return 'cirq.ms({!r}*np.pi/2)'.format(self._exponent)
if self._global_shift == 0:
if self._exponent == 1:
return 'cirq.XX'
Expand Down
6 changes: 3 additions & 3 deletions cirq/ops/parity_gates_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ def test_xx_repr():
assert repr(cirq.XXPowGate(exponent=0.5)) == '(cirq.XX**0.5)'

ms = cirq.XXPowGate(global_shift=-0.5)
assert (repr(ms) == 'cirq.MS(np.pi/2)')
assert (repr(ms**2) == 'cirq.MS(2.0*np.pi/2)')
assert (repr(ms**-0.5) == 'cirq.MS(-0.5*np.pi/2)')
assert (repr(ms) == 'cirq.ms(np.pi/2)')
assert (repr(ms**2) == 'cirq.ms(2.0*np.pi/2)')
assert (repr(ms**-0.5) == 'cirq.ms(-0.5*np.pi/2)')


def test_xx_matrix():
Expand Down
1 change: 1 addition & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,7 @@ Support for ion trap an neutral atom devices.
:toctree: generated/

cirq.MS
cirq.ms
cirq.is_native_neutral_atom_gate
cirq.is_native_neutral_atom_op
cirq.two_qubit_matrix_to_ion_operations
Expand Down

0 comments on commit 08df3b4

Please sign in to comment.