Skip to content
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

Remove deprecated netural_atoms classes #5695

Merged
merged 9 commits into from
Jul 11, 2022
7 changes: 1 addition & 6 deletions cirq-core/cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -656,12 +656,7 @@
)

from cirq.ion import ConvertToIonGates, IonDevice
from cirq.neutral_atoms import (
ConvertToNeutralAtomGates,
is_native_neutral_atom_gate,
is_native_neutral_atom_op,
NeutralAtomDevice,
)
from cirq.neutral_atoms import is_native_neutral_atom_gate, is_native_neutral_atom_op

from cirq.vis import (
Heatmap,
Expand Down
3 changes: 0 additions & 3 deletions cirq-core/cirq/neutral_atoms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@

"""Neutral atom devices and gates."""

from cirq.neutral_atoms.neutral_atom_devices import NeutralAtomDevice

from cirq.neutral_atoms.convert_to_neutral_atom_gates import (
ConvertToNeutralAtomGates,
is_native_neutral_atom_gate,
is_native_neutral_atom_op,
)
80 changes: 1 addition & 79 deletions cirq-core/cirq/neutral_atoms/convert_to_neutral_atom_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,88 +11,10 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import List, Optional, TYPE_CHECKING

from cirq import ops, protocols, transformers
from cirq._compat import deprecated_class
from cirq.circuits.optimization_pass import PointOptimizationSummary, PointOptimizer
from cirq import ops
from cirq.neutral_atoms import neutral_atom_devices

if TYPE_CHECKING:
import cirq


@deprecated_class(
deadline='v0.16',
fix='Use cirq.optimize_for_target_gateset(circuit, gateset=cirq_pasqal.PasqalGateset()).',
)
class ConvertToNeutralAtomGates(PointOptimizer):
"""Attempts to convert gates into native Atom gates.

First, checks if the given operation is already a native neutral atom
operation.

Second, checks if the operation has a known unitary. If so, and the gate
is a 1-qubit or 2-qubit gate, then performs circuit synthesis of the
operation. The 2-qubit gates are decomposed using CZ gates because
CZ gates are the highest fidelity 2-qubit gates for neutral atoms.

Third, attempts to `cirq.decompose` to the operation.

Fourth, if ignore_failures is set, gives up and returns the gate unchanged.
Otherwise raises a TypeError.
"""

def __init__(self, ignore_failures=False) -> None:
"""Inits ConvertToNeutralAtomGates.

Args:
ignore_failures: If set, gates that fail to convert are forwarded
unchanged. If not set, conversion failures raise a TypeError.
"""
super().__init__()
self.ignore_failures = ignore_failures
self.gateset = neutral_atom_devices.neutral_atom_gateset()

def _convert_one(self, op: ops.Operation) -> ops.OP_TREE:
# Known matrix?
mat = protocols.unitary(op, None) if len(op.qubits) <= 2 else None
if mat is not None and len(op.qubits) == 1:
gates = transformers.single_qubit_matrix_to_phased_x_z(mat)
return [g.on(op.qubits[0]) for g in gates]
if mat is not None and len(op.qubits) == 2:
return transformers.two_qubit_matrix_to_cz_operations(
op.qubits[0], op.qubits[1], mat, allow_partial_czs=False, clean_operations=True
)

return NotImplemented

def convert(self, op: ops.Operation) -> List[ops.Operation]:
def on_stuck_raise(bad):
return TypeError(
"Don't know how to work with {!r}. "
"It isn't a native atom operation, "
"a 1 or 2 qubit gate with a known unitary, "
"or composite.".format(bad)
)

return protocols.decompose(
op,
keep=self.gateset._validate_operation,
intercepting_decomposer=self._convert_one,
on_stuck_raise=None if self.ignore_failures else on_stuck_raise,
)

def optimization_at(
self, circuit: 'cirq.Circuit', index: int, op: 'cirq.Operation'
) -> Optional['cirq.PointOptimizationSummary']:
converted = self.convert(op)
if len(converted) == 1 and converted[0] is op:
return None
return PointOptimizationSummary(
clear_span=1, new_operations=converted, clear_qubits=op.qubits
)


def is_native_neutral_atom_op(operation: ops.Operation) -> bool:
"""Returns true if the operation is in the default neutral atom gateset."""
Expand Down
104 changes: 31 additions & 73 deletions cirq-core/cirq/neutral_atoms/convert_to_neutral_atom_gates_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,81 +12,39 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import numpy as np
import pytest

import cirq


Q = cirq.LineQubit.range(3)


def test_coverage():
with cirq.testing.assert_deprecated(
"Use cirq.optimize_for_target_gateset", deadline='v0.16', count=3
):
q = cirq.LineQubit.range(3)
g = cirq.testing.ThreeQubitGate()

class FakeOperation(cirq.Operation):
def __init__(self, gate, qubits):
self._gate = gate
self._qubits = qubits

@property
def qubits(self):
return self._qubits

def with_qubits(self, *new_qubits):
return FakeOperation(self._gate, new_qubits)

op = FakeOperation(g, q).with_qubits(*q)
circuit_ops = [cirq.Y(q[0]), cirq.ParallelGate(cirq.X, 3).on(*q)]
c = cirq.Circuit(circuit_ops)
cirq.neutral_atoms.ConvertToNeutralAtomGates().optimize_circuit(c)
assert c == cirq.Circuit(circuit_ops)
assert cirq.neutral_atoms.ConvertToNeutralAtomGates().convert(cirq.X.on(q[0])) == [
cirq.X.on(q[0])
]
with pytest.raises(TypeError, match="Don't know how to work with"):
cirq.neutral_atoms.ConvertToNeutralAtomGates().convert(op)
assert not cirq.neutral_atoms.is_native_neutral_atom_op(op)
assert not cirq.neutral_atoms.is_native_neutral_atom_gate(g)


def test_avoids_decompose_fallback_when_matrix_available_single_qubit():
class OtherX(cirq.testing.SingleQubitGate):
def _unitary_(self) -> np.ndarray:
return np.array([[0, 1], [1, 0]])

class OtherOtherX(cirq.testing.SingleQubitGate):
def _decompose_(self, qubits):
return OtherX().on(*qubits)

q = cirq.GridQubit(0, 0)
c = cirq.Circuit(OtherX().on(q), OtherOtherX().on(q))
with cirq.testing.assert_deprecated("Use cirq.optimize_for_target_gateset", deadline='v0.16'):
cirq.neutral_atoms.ConvertToNeutralAtomGates().optimize_circuit(c)
cirq.testing.assert_has_diagram(c, '(0, 0): ───PhX(1)───PhX(1)───')


def test_avoids_decompose_fallback_when_matrix_available_two_qubit():
class OtherCZ(cirq.testing.TwoQubitGate):
def _unitary_(self) -> np.ndarray:
return np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, -1]])

class OtherOtherCZ(cirq.testing.TwoQubitGate):
def _decompose_(self, qubits):
return OtherCZ().on(*qubits)

q00 = cirq.GridQubit(0, 0)
q01 = cirq.GridQubit(0, 1)
c = cirq.Circuit(OtherCZ().on(q00, q01), OtherOtherCZ().on(q00, q01))
expected_diagram = """
(0, 0): ───@───@───
│ │
(0, 1): ───@───@───
"""
with cirq.testing.assert_deprecated("Use cirq.optimize_for_target_gateset", deadline='v0.16'):
cirq.neutral_atoms.ConvertToNeutralAtomGates().optimize_circuit(c)
cirq.testing.assert_has_diagram(c, expected_diagram)
Q, Q2, Q3 = cirq.LineQubit.range(3)


@pytest.mark.parametrize(
"op,expected",
[
(cirq.H(Q), False),
(cirq.HPowGate(exponent=0.5)(Q), False),
(cirq.PhasedXPowGate(exponent=0.25, phase_exponent=0.125)(Q), True),
(cirq.XPowGate(exponent=0.5)(Q), True),
(cirq.YPowGate(exponent=0.25)(Q), True),
(cirq.ZPowGate(exponent=0.125)(Q), True),
(cirq.CZPowGate(exponent=0.5)(Q, Q2), False),
(cirq.CZ(Q, Q2), True),
(cirq.CNOT(Q, Q2), True),
(cirq.SWAP(Q, Q2), False),
(cirq.ISWAP(Q, Q2), False),
(cirq.CCNOT(Q, Q2, Q3), True),
(cirq.CCZ(Q, Q2, Q3), True),
(cirq.ParallelGate(cirq.X, num_copies=3)(Q, Q2, Q3), True),
(cirq.ParallelGate(cirq.Y, num_copies=3)(Q, Q2, Q3), True),
(cirq.ParallelGate(cirq.Z, num_copies=3)(Q, Q2, Q3), True),
(cirq.X(Q).controlled_by(Q2, Q3), True),
(cirq.Z(Q).controlled_by(Q2, Q3), True),
(cirq.ZPowGate(exponent=0.5)(Q).controlled_by(Q2, Q3), False),
],
)
def test_gateset(op: cirq.Operation, expected: bool):
assert cirq.is_native_neutral_atom_op(op) == expected
if op.gate is not None:
assert cirq.is_native_neutral_atom_gate(op.gate) == expected