Skip to content

Commit

Permalink
Change Pasqal to use transformers (#5377)
Browse files Browse the repository at this point in the history
- Add PasqalGateset that encapsulates gatesets
used by the PasqalDevice classes.
- Deprecate PasqalConverter and use cirq.convert_to_target_gateset
instead.

Fixes: #5130
  • Loading branch information
dstrain115 authored May 23, 2022
1 parent 352a976 commit 289f9ac
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 27 deletions.
2 changes: 2 additions & 0 deletions cirq-pasqal/cirq_pasqal/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

from cirq_pasqal.pasqal_qubits import ThreeDQubit, TwoDQubit

from cirq_pasqal.pasqal_gateset import PasqalGateset

from cirq_pasqal.pasqal_device import PasqalDevice, PasqalVirtualDevice, PasqalConverter

from cirq_pasqal.pasqal_noise_model import PasqalNoiseModel
Expand Down
1 change: 1 addition & 0 deletions cirq-pasqal/cirq_pasqal/json_resolver_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
def _class_resolver_dictionary() -> Dict[str, ObjectFactory]:
return {
'PasqalDevice': cirq_pasqal.PasqalDevice,
'PasqalGateset': cirq_pasqal.PasqalGateset,
'PasqalVirtualDevice': cirq_pasqal.PasqalVirtualDevice,
'ThreeDQubit': cirq_pasqal.ThreeDQubit,
'TwoDQubit': cirq_pasqal.TwoDQubit,
Expand Down
4 changes: 4 additions & 0 deletions cirq-pasqal/cirq_pasqal/json_test_data/PasqalGateset.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"cirq_type": "PasqalGateset",
"include_additional_controlled_ops": false
}
1 change: 1 addition & 0 deletions cirq-pasqal/cirq_pasqal/json_test_data/PasqalGateset.repr
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cirq_pasqal.PasqalGateset(include_additional_controlled_ops=False)
34 changes: 7 additions & 27 deletions cirq-pasqal/cirq_pasqal/pasqal_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import cirq
from cirq import _compat, GridQubit, LineQubit
from cirq.ops import NamedQubit
from cirq_pasqal import ThreeDQubit, TwoDQubit
from cirq_pasqal import ThreeDQubit, TwoDQubit, PasqalGateset


@cirq.value.value_equality
Expand Down Expand Up @@ -63,20 +63,7 @@ def __init__(self, qubits: Sequence[cirq.Qid]) -> None:
'qubits.'.format(type(self), self.maximum_qubit_number)
)

self.gateset = cirq.Gateset(
cirq.ParallelGateFamily(cirq.H),
cirq.ParallelGateFamily(cirq.PhasedXPowGate),
cirq.ParallelGateFamily(cirq.XPowGate),
cirq.ParallelGateFamily(cirq.YPowGate),
cirq.ParallelGateFamily(cirq.ZPowGate),
cirq.AnyIntegerPowerGateFamily(cirq.CNotPowGate),
cirq.AnyIntegerPowerGateFamily(cirq.CCNotPowGate),
cirq.AnyIntegerPowerGateFamily(cirq.CZPowGate),
cirq.AnyIntegerPowerGateFamily(cirq.CCZPowGate),
cirq.IdentityGate,
cirq.MeasurementGate,
unroll_circuit_op=False,
)
self.gateset = PasqalGateset()
self.qubits = qubits
self._metadata = cirq.DeviceMetadata(
qubits, nx.from_edgelist([(a, b) for a in qubits for b in qubits if a != b])
Expand Down Expand Up @@ -211,24 +198,14 @@ def __init__(
'Control_radius cannot be larger than 3 times'
' the minimal distance between qubits.'
)

self.control_radius = control_radius
self.exclude_gateset = cirq.Gateset(
cirq.AnyIntegerPowerGateFamily(cirq.CNotPowGate),
cirq.AnyIntegerPowerGateFamily(cirq.CCNotPowGate),
cirq.AnyIntegerPowerGateFamily(cirq.CCZPowGate),
)
self.controlled_gateset = cirq.Gateset(
*self.exclude_gateset.gates, cirq.AnyIntegerPowerGateFamily(cirq.CZPowGate)
)
self.gateset = PasqalGateset(include_additional_controlled_ops=False)
self.controlled_gateset = cirq.Gateset(cirq.AnyIntegerPowerGateFamily(cirq.CZPowGate))

@property
def supported_qubit_type(self):
return (ThreeDQubit, TwoDQubit, GridQubit, LineQubit)

def is_pasqal_device_op(self, op: cirq.Operation) -> bool:
return super().is_pasqal_device_op(op) and op not in self.exclude_gateset

def validate_operation(self, operation: cirq.Operation):
"""Raises an error if the given operation is invalid on this device.
Expand Down Expand Up @@ -315,6 +292,9 @@ def _json_dict_(self) -> Dict[str, Any]:
return cirq.protocols.obj_to_dict_helper(self, ['control_radius', 'qubits'])


@_compat.deprecated_class(
deadline='v0.16', fix='Use cirq.optimize_for_target_gateset(circuit, gateset=PasqalGateset()).'
)
class PasqalConverter(cirq.neutral_atoms.ConvertToNeutralAtomGates):
"""A gate converter for compatibility with Pasqal processors.
Expand Down
69 changes: 69 additions & 0 deletions cirq-pasqal/cirq_pasqal/pasqal_gateset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Copyright 2022 The Cirq Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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 Any, Dict


import cirq


class PasqalGateset(cirq.neutral_atoms.NeutralAtomGateset):
"""A Compilation target intended for Pasqal neutral atom devices.
This gateset supports single qubit gates that can be used
in a parallel fashion as well as CZ.
This gateset can optionally include CNOT, CCNOT (TOFFOLI) gates, and
CCZ as well.
Args:
include_additional_controlled_ops: Whether to include CCZ, CCNOT, and CNOT
gates (defaults to True).
"""

def __init__(self, include_additional_controlled_ops: bool = True):
gate_families = [
cirq.ParallelGateFamily(cirq.H),
cirq.ParallelGateFamily(cirq.PhasedXPowGate),
cirq.ParallelGateFamily(cirq.XPowGate),
cirq.ParallelGateFamily(cirq.YPowGate),
cirq.ParallelGateFamily(cirq.ZPowGate),
cirq.AnyIntegerPowerGateFamily(cirq.CZPowGate),
cirq.IdentityGate,
cirq.MeasurementGate,
]
self.include_additional_controlled_ops = include_additional_controlled_ops
if self.include_additional_controlled_ops:
gate_families.append(cirq.AnyIntegerPowerGateFamily(cirq.CNotPowGate))
gate_families.append(cirq.AnyIntegerPowerGateFamily(cirq.CCNotPowGate))
gate_families.append(cirq.AnyIntegerPowerGateFamily(cirq.CCZPowGate))

# Call cirq.Gateset __init__ which is our grand-father inherited class
# pylint doesn't like this so disable checks on this.
# pylint: disable=bad-super-call
super(cirq.neutral_atoms.NeutralAtomGateset, self).__init__(
*gate_families, unroll_circuit_op=False
)

def __repr__(self):
return (
f'cirq_pasqal.PasqalGateset(include_additional_controlled_ops='
f'{self.include_additional_controlled_ops})'
)

@classmethod
def _from_json_dict_(cls, include_additional_controlled_ops, **kwargs):
return cls(include_additional_controlled_ops=include_additional_controlled_ops)

def _json_dict_(self) -> Dict[str, Any]:
return cirq.protocols.obj_to_dict_helper(self, ['include_additional_controlled_ops'])
105 changes: 105 additions & 0 deletions cirq-pasqal/cirq_pasqal/pasqal_gateset_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Copyright 2022 The Cirq Developers
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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.
#
import pytest
import cirq
import cirq_pasqal

Q, Q2, Q3 = cirq.LineQubit.range(3)


@pytest.mark.parametrize(
"op,expected",
[
(cirq.H(Q), True),
(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):
gs = cirq_pasqal.PasqalGateset()
assert gs.validate(op) == expected
assert gs.validate(cirq.Circuit(op)) == expected


@pytest.mark.parametrize(
"op,expected",
[
(cirq.H(Q), True),
(cirq.HPowGate(exponent=0.5)(Q), False),
(cirq.PhasedXPowGate(exponent=0.25, phase_exponent=0.125)(Q), True),
(cirq.ParallelGate(cirq.X, num_copies=3)(Q, Q2, Q3), True),
(cirq.CZPowGate(exponent=0.5)(Q, Q2), False),
(cirq.CZ(Q, Q2), True),
(cirq.CNOT(Q, Q2), False),
(cirq.CCNOT(Q, Q2, Q3), False),
(cirq.CCZ(Q, Q2, Q3), False),
(cirq.Z(Q).controlled_by(Q2), True),
(cirq.X(Q).controlled_by(Q2, Q3), False),
(cirq.Z(Q).controlled_by(Q2, Q3), False),
(cirq.ZPowGate(exponent=0.5)(Q).controlled_by(Q2, Q3), False),
],
)
def test_control_gates_not_included(op: cirq.Operation, expected: bool):
gs = cirq_pasqal.PasqalGateset(include_additional_controlled_ops=False)
assert gs.validate(op) == expected
assert gs.validate(cirq.Circuit(op)) == expected


@pytest.mark.parametrize(
"op",
[
cirq.X(Q),
cirq.SWAP(Q, Q2),
cirq.ISWAP(Q, Q2),
cirq.CCNOT(Q, Q2, Q3),
cirq.CCZ(Q, Q2, Q3),
cirq.ParallelGate(cirq.X, num_copies=3)(Q, Q2, Q3),
cirq.SWAP(Q, Q2).controlled_by(Q3),
],
)
def test_decomposition(op: cirq.Operation):
circuit = cirq.Circuit(op)
gs = cirq_pasqal.PasqalGateset()
gs2 = cirq_pasqal.PasqalGateset(include_additional_controlled_ops=False)
for gateset in [gs, gs2]:
decomposed_circuit = cirq.optimize_for_target_gateset(circuit, gateset=gateset)
for new_op in decomposed_circuit.all_operations():
assert gs.validate(new_op)


def test_repr():
cirq.testing.assert_equivalent_repr(
cirq_pasqal.PasqalGateset(), setup_code='import cirq_pasqal'
)
cirq.testing.assert_equivalent_repr(
cirq_pasqal.PasqalGateset(include_additional_controlled_ops=False),
setup_code='import cirq_pasqal',
)

0 comments on commit 289f9ac

Please sign in to comment.