Skip to content

Commit

Permalink
Creates fake grid device for testing qubit connectivity in routing (#…
Browse files Browse the repository at this point in the history
…5830)

* added grid testing device

* formatting

* addressed comments and added ring device

* changed RoutingTestingDevice interface; need to change is_isomorphic tests

* added hard-coded isomorphism tests

* fixed type issue

* addressed comments

* removed unused import

* fixed nits
  • Loading branch information
ammareltigani authored Aug 20, 2022
1 parent 9e8c056 commit 79d7250
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 0 deletions.
6 changes: 6 additions & 0 deletions cirq-core/cirq/testing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,10 @@
FakePrinter,
)

from cirq.testing.routing_devices import (
construct_grid_device,
construct_ring_device,
RoutingTestingDevice,
)

from cirq.testing.sample_circuits import nonoptimal_toffoli_circuit
67 changes: 67 additions & 0 deletions cirq-core/cirq/testing/routing_devices.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Copyright 2021 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.
"""Provides test devices that can validate circuits during a routing procedure."""

from typing import TYPE_CHECKING

import networkx as nx

from cirq import devices, ops

if TYPE_CHECKING:
import cirq


class RoutingTestingDevice(devices.Device):
"""Testing device to be used for testing qubit connectivity in routing procedures."""

def __init__(self, nx_graph: nx.Graph) -> None:
relabeling_map = {
old: ops.q(old) if isinstance(old, (int, str)) else ops.q(*old) for old in nx_graph
}
# Relabel nodes in-place.
nx.relabel_nodes(nx_graph, relabeling_map, copy=False)

self._metadata = devices.DeviceMetadata(relabeling_map.values(), nx_graph)

@property
def metadata(self) -> devices.DeviceMetadata:
return self._metadata

def validate_operation(self, operation: 'cirq.Operation') -> None:
if not self._metadata.qubit_set.issuperset(operation.qubits):
raise ValueError(f'Qubits not on device: {operation.qubits!r}.')

if len(operation.qubits) > 1:
if len(operation.qubits) == 2:
if operation.qubits not in self._metadata.nx_graph.edges:
raise ValueError(
f'Qubit pair is not a valid edge on device: {operation.qubits!r}.'
)
return

if not isinstance(operation.gate, ops.MeasurementGate):
raise ValueError(
f'Unsupported operation: {operation}. '
f'Routing device only supports 1 / 2 qubit operations.'
)


def construct_grid_device(m: int, n: int) -> RoutingTestingDevice:
return RoutingTestingDevice(nx.grid_2d_graph(m, n))


def construct_ring_device(l: int, directed: bool = False) -> RoutingTestingDevice:
nx_graph = nx.cycle_graph(l, create_using=nx.DiGraph if directed else nx.Graph)
return RoutingTestingDevice(nx_graph)
116 changes: 116 additions & 0 deletions cirq-core/cirq/testing/routing_devices_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Copyright 2021 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 networkx as nx
import cirq


def test_grid_device():
rect_device = cirq.testing.construct_grid_device(5, 7)
rect_device_graph = rect_device.metadata.nx_graph
isomorphism_class = nx.Graph()
row_edges = [
(cirq.GridQubit(i, j), cirq.GridQubit(i, j + 1)) for i in range(5) for j in range(6)
]
col_edges = [
(cirq.GridQubit(i, j), cirq.GridQubit(i + 1, j)) for j in range(7) for i in range(4)
]
isomorphism_class.add_edges_from(row_edges)
isomorphism_class.add_edges_from(col_edges)
assert all(q in rect_device_graph.nodes for q in cirq.GridQubit.rect(5, 7))
assert nx.is_isomorphic(isomorphism_class, rect_device_graph)


def test_grid_op_validation():
device = cirq.testing.construct_grid_device(5, 7)

with pytest.raises(ValueError, match="Qubits not on device"):
device.validate_operation(cirq.X(cirq.NamedQubit("a")))
with pytest.raises(ValueError, match="Qubits not on device"):
device.validate_operation(cirq.CNOT(cirq.NamedQubit("a"), cirq.GridQubit(0, 0)))
with pytest.raises(ValueError, match="Qubits not on device"):
device.validate_operation(cirq.CNOT(cirq.GridQubit(5, 4), cirq.GridQubit(4, 4)))
with pytest.raises(ValueError, match="Qubits not on device"):
device.validate_operation(cirq.CNOT(cirq.GridQubit(4, 7), cirq.GridQubit(4, 6)))

with pytest.raises(ValueError, match="Qubit pair is not a valid edge on device"):
device.validate_operation(cirq.CNOT(cirq.GridQubit(0, 0), cirq.GridQubit(0, 2)))
with pytest.raises(ValueError, match="Qubit pair is not a valid edge on device"):
device.validate_operation(cirq.CNOT(cirq.GridQubit(2, 0), cirq.GridQubit(0, 0)))

device.validate_operation(cirq.CNOT(cirq.GridQubit(0, 0), cirq.GridQubit(0, 1)))
device.validate_operation(cirq.CNOT(cirq.GridQubit(0, 1), cirq.GridQubit(0, 0)))
device.validate_operation(cirq.CNOT(cirq.GridQubit(1, 0), cirq.GridQubit(0, 0)))
device.validate_operation(cirq.CNOT(cirq.GridQubit(0, 0), cirq.GridQubit(1, 0)))


def test_ring_device():
undirected_device = cirq.testing.construct_ring_device(5)
undirected_device_graph = undirected_device.metadata.nx_graph
assert all(q in undirected_device_graph.nodes for q in cirq.LineQubit.range(5))
isomorphism_class = nx.Graph()
edges = [(cirq.LineQubit(i % 5), cirq.LineQubit((i + 1) % 5)) for i in range(5)]
isomorphism_class.add_edges_from(edges)
assert nx.is_isomorphic(isomorphism_class, undirected_device_graph)

directed_device = cirq.testing.construct_ring_device(5, directed=True)
directed_device_graph = directed_device.metadata.nx_graph
assert all(q in directed_device_graph.nodes for q in cirq.LineQubit.range(5))
isomorphism_class = nx.DiGraph()
edges = [(cirq.LineQubit(i % 5), cirq.LineQubit((i + 1) % 5)) for i in range(5)]
isomorphism_class.add_edges_from(edges)
assert nx.is_isomorphic(isomorphism_class, directed_device_graph)


def test_ring_op_validation():
directed_device = cirq.testing.construct_ring_device(5, directed=True)
undirected_device = cirq.testing.construct_ring_device(5, directed=False)

with pytest.raises(ValueError, match="Qubits not on device"):
directed_device.validate_operation(cirq.X(cirq.LineQubit(5)))
with pytest.raises(ValueError, match="Qubits not on device"):
undirected_device.validate_operation(cirq.X(cirq.LineQubit(5)))

with pytest.raises(ValueError, match="Qubit pair is not a valid edge on device"):
undirected_device.validate_operation(cirq.CNOT(cirq.LineQubit(0), cirq.LineQubit(2)))
with pytest.raises(ValueError, match="Qubit pair is not a valid edge on device"):
directed_device.validate_operation(cirq.CNOT(cirq.LineQubit(1), cirq.LineQubit(0)))

undirected_device.validate_operation(cirq.CNOT(cirq.LineQubit(0), cirq.LineQubit(1)))
undirected_device.validate_operation(cirq.CNOT(cirq.LineQubit(1), cirq.LineQubit(0)))
directed_device.validate_operation(cirq.CNOT(cirq.LineQubit(0), cirq.LineQubit(1)))


def test_allowed_multi_qubit_gates():
device = cirq.testing.construct_ring_device(5)

device.validate_operation(cirq.MeasurementGate(1).on(cirq.LineQubit(0)))
device.validate_operation(cirq.MeasurementGate(2).on(*cirq.LineQubit.range(2)))
device.validate_operation(cirq.MeasurementGate(3).on(*cirq.LineQubit.range(3)))

with pytest.raises(ValueError, match=f"Unsupported operation"):
device.validate_operation(cirq.CCNOT(*cirq.LineQubit.range(3)))

device.validate_operation(cirq.CNOT(*cirq.LineQubit.range(2)))


def test_namedqubit_device():
# 4-star graph
nx_graph = nx.Graph([("a", "b"), ("a", "c"), ("a", "d")])

device = cirq.testing.RoutingTestingDevice(nx_graph)
relabeled_graph = device.metadata.nx_graph
qubit_set = {cirq.NamedQubit(n) for n in "abcd"}
assert set(relabeled_graph.nodes) == qubit_set
assert nx.is_isomorphic(nx_graph, relabeled_graph)

0 comments on commit 79d7250

Please sign in to comment.