Skip to content

Commit

Permalink
Remove deprecated netural_atoms classes (quantumlib#5695)
Browse files Browse the repository at this point in the history
- Removes NeutralAtomDevice and ConvertToNeutralAtomGates
  • Loading branch information
dstrain115 authored and rht committed May 1, 2023
1 parent 689dc69 commit acbf204
Show file tree
Hide file tree
Showing 7 changed files with 34 additions and 752 deletions.
7 changes: 1 addition & 6 deletions cirq-core/cirq/__init__.py
Expand Up @@ -654,12 +654,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
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
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
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

0 comments on commit acbf204

Please sign in to comment.