DenseLayout and TrivialLayout pass (#1499)

* adding an embedder pass

* improve error messages in dagcircuit

* get_cnot_nodes -> get_2q_nodes and get_named_nodes return node not node_id

* TranspilerError is fine grained enough

* rebase

* add test

* transpiler only calls DenseLayout if TrivialLayout does not work

* add tests
This commit is contained in:
Ali Javadi-Abhari 2018-12-23 22:14:50 -05:00 committed by Luciano
parent 11e9a31f1c
commit 285128ecdc
18 changed files with 441 additions and 192 deletions

View File

@ -304,7 +304,7 @@ class DAGCircuit:
# Check for each wire
for wire in args:
if wire not in amap:
raise DAGCircuitError("(qu)bit %s not found" % (wire,))
raise DAGCircuitError("(qu)bit %s[%d] not found" % (wire[0].name, wire[1]))
def _bits_in_condition(self, cond):
"""Return a list of bits in the given condition.
@ -624,10 +624,10 @@ class DAGCircuit:
m_wire = edge_map.get(nd["wire"], nd["wire"])
# the mapped wire should already exist
if m_wire not in self.output_map:
raise DAGCircuitError("wire (%s,%d) not in self" % (m_wire[0].name, m_wire[1]))
raise DAGCircuitError("wire %s[%d] not in self" % (m_wire[0].name, m_wire[1]))
if nd["wire"] not in input_circuit.wires:
raise DAGCircuitError("inconsistent wire type for (%s,%d) in input_circuit"
raise DAGCircuitError("inconsistent wire type for %s[%d] in input_circuit"
% (nd["wire"][0].name, nd["wire"][1]))
elif nd["type"] == "out":
@ -692,11 +692,11 @@ class DAGCircuit:
m_name = wire_map.get(nd["wire"], nd["wire"])
# the mapped wire should already exist
if m_name not in self.input_map:
raise DAGCircuitError("wire (%s,%d) not in self" % (m_name[0].name, m_name[1]))
raise DAGCircuitError("wire %s[%d] not in self" % (m_name[0].name, m_name[1]))
if nd["wire"] not in input_circuit.wires:
raise DAGCircuitError(
"inconsistent wire for (%s,%d) in input_circuit"
"inconsistent wire for %s[%d] in input_circuit"
% (nd["wire"][0].name, nd["wire"][1]))
elif nd["type"] == "in":
@ -957,11 +957,8 @@ class DAGCircuit:
full_pred_map[w] = self.multi_graph.predecessors(
self.output_map[w])[0]
if len(list(self.multi_graph.predecessors(self.output_map[w]))) != 1:
raise DAGCircuitError(
"too many predecessors for (%s,%d) output node" % (w[0], w[1])
)
raise DAGCircuitError("too many predecessors for %s[%d] "
"output node" % (w[0], w[1]))
return full_pred_map, full_succ_map
@staticmethod
@ -1252,26 +1249,21 @@ class DAGCircuit:
nodes = [n[0] for n in nodes]
return nodes
def get_named_nodes(self, name):
"""Get the set of "op" node ids with the given name."""
if name not in self.basis:
raise DAGCircuitError("%s is not in the list of basis operations"
% name)
def get_named_nodes(self, *names):
"""Get the set of "op" nodes with the given name."""
named_nodes = []
for node_id, node_data in self.multi_graph.nodes(data=True):
if node_data['type'] == 'op' and node_data['op'].name in names:
named_nodes.append(node_id)
return named_nodes
# We need to instantiate the full list now because the underlying multi_graph
# may change when users iterate over the named nodes.
return {node_id for node_id, data in self.multi_graph.nodes(data=True)
if data["type"] == "op" and data["op"].name == name}
def get_cnot_nodes(self):
"""Get the set of Cnot."""
cx_names = ['cx', 'CX']
cxs_nodes = []
for cx_name in cx_names:
if cx_name in self.basis:
for cx_id in self.get_named_nodes(cx_name):
cxs_nodes.append(self.multi_graph.node[cx_id])
return cxs_nodes
def get_2q_nodes(self):
"""Get the set of 2-qubit nodes."""
two_q_nodes = []
for node_id, node_data in self.multi_graph.nodes(data=True):
if node_data['type'] == 'op' and len(node_data['qargs']) == 2:
two_q_nodes.append(self.multi_graph.node[node_id])
return two_q_nodes
def successors(self, node):
"""Returns the successors of a node."""

View File

@ -363,7 +363,7 @@ def swap_mapper(circuit_graph, coupling_graph,
Args:
circuit_graph (DAGCircuit): input DAG circuit
coupling_graph (CouplingGraph): coupling graph to map onto
initial_layout (dict): dict {(str, int): (str, int)}
initial_layout (Layout): dict {(str, int): (str, int)}
from qubits of circuit_graph to qubits of coupling_graph (optional)
trials (int): number of trials.
seed (int): initial seed.

View File

@ -110,13 +110,13 @@ The control argument `do_while` will run these passes until the callable returns
The pass manager developer can avoid one or more passes by making them conditional (on a property in the property set):
```
pm.append(LayoutMapper(coupling_map))
pm.append(CheckIfMapped(coupling_map))
pm.append(BasicLayout(coupling_map))
pm.append(CheckMap(coupling_map))
pm.append(BasicSwap(coupling_map),
condition=lambda property_set: not property_set['is_mapped'])
condition=lambda property_set: not property_set['is_swap_mapped'])
```
The `CheckIfMapped` is an analysis pass that updates the property `is_mapped`. If `LayoutMapper` could map the circuit to the coupling map, the `SwapMapper` is unnecessary.
The `CheckMap` is an analysis pass that updates the property `is_swap_mapped`. If `LayoutMapper` could map the circuit to the coupling map, the `SwapMapper` is unnecessary.
### Idempotent passes

View File

@ -9,7 +9,7 @@
import os
from ._passmanager import PassManager, FlowController
from ._propertyset import PropertySet
from ._transpilererror import TranspilerError, TranspilerAccessError, MapperError
from ._transpilererror import TranspilerError, TranspilerAccessError
from ._fencedobjs import FencedDAGCircuit, FencedPropertySet
from ._basepasses import AnalysisPass, TransformationPass
from ._transpiler import transpile, transpile_dag

View File

@ -8,19 +8,15 @@
"""Tools for compiling a batch of quantum circuits."""
import logging
import warnings
import numpy as np
import scipy.sparse as sp
import scipy.sparse.csgraph as cs
from qiskit.circuit import QuantumCircuit
from qiskit.circuit import QuantumRegister
from qiskit.mapper import CouplingMap, swap_mapper
from qiskit.tools.parallel import parallel_map
from qiskit.converters import circuit_to_dag
from qiskit.converters import dag_to_circuit
from qiskit.extensions.standard import SwapGate
from .passes import (Unroller, CXDirection, CXCancellation,
Decompose, Optimize1qGates, BarrierBeforeFinalMeasurements)
from .passes import (Unroller, CXDirection, CXCancellation, DenseLayout, TrivialLayout,
CheckMap, Decompose, Optimize1qGates, BarrierBeforeFinalMeasurements)
from ._transpilererror import TranspilerError
logger = logging.getLogger(__name__)
@ -59,8 +55,7 @@ def transpile(circuits, backend=None, basis_gates=None, coupling_map=None,
raise TranspilerError('no basis_gates or backend to compile to')
circuits = parallel_map(_transpilation, circuits,
task_kwargs={'backend': backend,
'basis_gates': basis_gates,
task_kwargs={'basis_gates': basis_gates,
'coupling_map': coupling_map,
'initial_layout': initial_layout,
'seed_mapper': seed_mapper,
@ -70,14 +65,13 @@ def transpile(circuits, backend=None, basis_gates=None, coupling_map=None,
return circuits
def _transpilation(circuit, backend=None, basis_gates=None, coupling_map=None,
def _transpilation(circuit, basis_gates=None, coupling_map=None,
initial_layout=None, seed_mapper=None,
pass_manager=None):
"""Perform transpilation of a single circuit.
Args:
circuit (QuantumCircuit): A circuit to transpile.
backend (BaseBackend): a backend to compile for
basis_gates (str): comma-separated basis gate set to compile to
coupling_map (list): coupling map (perhaps custom) to target in mapping
initial_layout (list): initial layout of qubits in mapping
@ -94,12 +88,26 @@ def _transpilation(circuit, backend=None, basis_gates=None, coupling_map=None,
return circuit
dag = circuit_to_dag(circuit)
if not backend and not initial_layout:
raise TranspilerError('initial layout not supplied, and cannot '
'be inferred from backend.')
if (initial_layout is None and not backend.configuration().simulator
and not _matches_coupling_map(dag, coupling_map)):
initial_layout = _pick_best_layout(dag, backend)
# pick a trivial layout if the circuit already satisfies the coupling constraints
# else layout on the most densely connected physical qubit subset
# FIXME: this should be simplified once it is ported to a PassManager
if coupling_map:
check_map = CheckMap(CouplingMap(coupling_map))
check_map.run(dag)
if check_map.property_set['is_direction_mapped']:
trivial_layout = TrivialLayout(CouplingMap(coupling_map))
trivial_layout.run(dag)
initial_layout = trivial_layout.property_set['layout']
else:
dense_layout = DenseLayout(CouplingMap(coupling_map))
dense_layout.run(dag)
initial_layout = dense_layout.property_set['layout']
# temporarily build old-style layout dict
# (FIXME: remove after transition to StochasticSwap pass)
layout = initial_layout.copy()
virtual_qubits = layout.get_virtual_bits()
initial_layout = {(v[0].name, v[1]): ('q', layout[v]) for v in virtual_qubits}
final_dag = transpile_dag(dag, basis_gates=basis_gates,
coupling_map=coupling_map,
@ -154,7 +162,6 @@ def transpile_dag(dag, basis_gates='u1,u2,u3,cx,id', coupling_map=None,
Returns:
DAGCircuit: transformed dag
DAGCircuit, dict: transformed dag along with the final layout on backend qubits
"""
# TODO: `basis_gates` will be removed after we have the unroller pass.
# TODO: `coupling_map`, `initial_layout`, `seed_mapper` removed after mapper pass.
@ -181,7 +188,7 @@ def transpile_dag(dag, basis_gates='u1,u2,u3,cx,id', coupling_map=None,
logger.info("pre-mapping properties: %s",
dag.properties())
# Insert swap gates
coupling = CouplingMap(couplinglist=coupling_map)
coupling = CouplingMap(coupling_map)
logger.info("initial layout: %s", initial_layout)
dag = BarrierBeforeFinalMeasurements().run(dag)
dag, final_layout = swap_mapper(
@ -207,102 +214,3 @@ def transpile_dag(dag, basis_gates='u1,u2,u3,cx,id', coupling_map=None,
DeprecationWarning)
return dag
def _best_subset(backend, n_qubits):
"""Computes the qubit mapping with the best
connectivity.
Parameters:
backend (BaseBackend): A Qiskit backend instance.
n_qubits (int): Number of subset qubits to consider.
Returns:
ndarray: Array of qubits to use for best
connectivity mapping.
Raises:
TranspilerError: Wrong number of qubits given.
"""
if n_qubits == 1:
return np.array([0])
elif n_qubits <= 0:
raise TranspilerError('Number of qubits <= 0.')
device_qubits = backend.configuration().n_qubits
if n_qubits > device_qubits:
raise TranspilerError('Number of qubits greater than device.')
cmap = np.asarray(getattr(backend.configuration(), 'coupling_map', None))
data = np.ones_like(cmap[:, 0])
sp_cmap = sp.coo_matrix((data, (cmap[:, 0], cmap[:, 1])),
shape=(device_qubits, device_qubits)).tocsr()
best = 0
best_map = None
# do bfs with each node as starting point
for k in range(sp_cmap.shape[0]):
bfs = cs.breadth_first_order(sp_cmap, i_start=k, directed=False,
return_predecessors=False)
connection_count = 0
for i in range(n_qubits):
node_idx = bfs[i]
for j in range(sp_cmap.indptr[node_idx],
sp_cmap.indptr[node_idx + 1]):
node = sp_cmap.indices[j]
for counter in range(n_qubits):
if node == bfs[counter]:
connection_count += 1
break
if connection_count > best:
best = connection_count
best_map = bfs[0:n_qubits]
return best_map
def _matches_coupling_map(dag, coupling_map):
"""Iterate over circuit gates to check if all multi-qubit couplings
match the qubit coupling graph in the backend.
Parameters:
dag (DAGCircuit): DAG representation of circuit.
coupling_map (list): Backend coupling map, represented as an adjacency list.
Returns:
bool: True if all gates readily fit the backend coupling graph.
False if there's at least one gate that uses multiple qubits
which does not match the backend couplings.
"""
match = True
for _, data in dag.multi_graph.nodes(data=True):
if data['type'] == 'op':
gate_map = [qr[1] for qr in data['qargs']]
if len(gate_map) > 1:
if gate_map not in coupling_map:
match = False
break
return match
def _pick_best_layout(dag, backend):
"""Pick a convenient layout depending on the best matching qubit connectivity
Parameters:
dag (DAGCircuit): DAG representation of circuit.
backend (BaseBackend) : The backend with the coupling_map for searching
Returns:
dict: A special ordered initial_layout
"""
num_qubits = sum([qreg.size for qreg in dag.qregs.values()])
best_sub = _best_subset(backend, num_qubits)
layout = {}
map_iter = 0
device_qubits = backend.configuration().n_qubits
q = QuantumRegister(device_qubits, 'q')
for qreg in dag.qregs.values():
for i in range(qreg.size):
layout[(qreg.name, i)] = (q, int(best_sub[map_iter]))
map_iter += 1
return layout

View File

@ -16,8 +16,4 @@ class TranspilerError(QiskitError):
class TranspilerAccessError(QiskitError):
""" Exception of access error in the transpiler passes. """
class MapperError(QiskitError):
""" Exception for cases where a mapper pass cannot map. """
"""Exception of access error in the transpiler passes."""

View File

@ -17,6 +17,8 @@ from .mapping.barrier_before_final_measurements import BarrierBeforeFinalMeasure
from .mapping.check_map import CheckMap
from .mapping.cx_direction import CXDirection
from .mapping.unroller import Unroller
from .mapping.trivial_layout import TrivialLayout
from .mapping.dense_layout import DenseLayout
from .mapping.basic_swap import BasicSwap
from .mapping.lookahead_swap import LookaheadSwap
from .mapping.stochastic_swap import StochasticSwap

View File

@ -66,9 +66,9 @@ class BasicSwap(TransformationPass):
for layer in dag.serial_layers():
subdag = layer['graph']
for a_cx in subdag.get_cnot_nodes():
physical_q0 = current_layout[a_cx['qargs'][0]]
physical_q1 = current_layout[a_cx['qargs'][1]]
for gate in subdag.get_2q_nodes():
physical_q0 = current_layout[gate['qargs'][0]]
physical_q1 = current_layout[gate['qargs'][1]]
if self.coupling_map.distance(physical_q0, physical_q1) != 1:
# Insert a new layer with the SWAP(s).
swap_layer = DAGCircuit()

View File

@ -7,6 +7,8 @@
"""
This pass checks if a DAG is mapped to a coupling map.
It checks that all 2-qubit interactions are laid out to be physically close.
"""
from qiskit.transpiler._basepasses import AnalysisPass
@ -16,6 +18,8 @@ from qiskit.mapper import Layout
class CheckMap(AnalysisPass):
"""
Checks if a DAGCircuit is mapped to `coupling_map`.
It checks that all 2-qubit interactions are laid out to be physically close.
"""
def __init__(self, coupling_map, initial_layout=None):
@ -31,8 +35,8 @@ class CheckMap(AnalysisPass):
def run(self, dag):
"""
If `dag` is mapped to `coupling_map`, the property `is_mapped` is
set to True (or to False otherwise).
If `dag` is mapped to `coupling_map`, the property
`is_swap_mapped` is set to True (or to False otherwise).
If `dag` is mapped and the direction is correct the property
`is_direction_mapped` is set to True (or to False otherwise).
@ -44,17 +48,17 @@ class CheckMap(AnalysisPass):
for qreg in dag.qregs.values():
self.layout.add_register(qreg)
self.property_set['is_mapped'] = True
self.property_set['is_swap_mapped'] = True
self.property_set['is_direction_mapped'] = True
for layer in dag.serial_layers():
subdag = layer['graph']
for cnot in subdag.get_cnot_nodes():
physical_q0 = self.layout[cnot['qargs'][0]]
physical_q1 = self.layout[cnot['qargs'][1]]
for gate in subdag.get_2q_nodes():
physical_q0 = self.layout[gate['qargs'][0]]
physical_q1 = self.layout[gate['qargs'][1]]
if self.coupling_map.distance(physical_q0, physical_q1) != 1:
self.property_set['is_mapped'] = False
self.property_set['is_swap_mapped'] = False
self.property_set['is_direction_mapped'] = False
return
else:

View File

@ -11,7 +11,7 @@ compatible with the coupling_map.
"""
from qiskit.transpiler._basepasses import TransformationPass
from qiskit.transpiler import MapperError
from qiskit.transpiler import TranspilerError
from qiskit.dagcircuit import DAGCircuit
from qiskit.mapper import Layout
from qiskit.extensions.standard import HGate
@ -49,8 +49,8 @@ class CXDirection(TransformationPass):
DAGCircuit: The rearranged dag for the coupling map
Raises:
MapperError: If the circuit cannot be mapped just by flipping the
cx nodes.
TranspilerError: If the circuit cannot be mapped just by flipping the
cx nodes.
"""
new_dag = DAGCircuit()
@ -66,16 +66,16 @@ class CXDirection(TransformationPass):
for layer in dag.serial_layers():
subdag = layer['graph']
for cnot in subdag.get_cnot_nodes():
control = cnot['op'].qargs[0]
target = cnot['op'].qargs[1]
for cnot_id in subdag.get_named_nodes('cx', 'CX'):
cnot_node = subdag.multi_graph.nodes[cnot_id]
control = cnot_node['op'].qargs[0]
target = cnot_node['op'].qargs[1]
physical_q0 = self.layout[control]
physical_q1 = self.layout[target]
if self.coupling_map.distance(physical_q0, physical_q1) != 1:
raise MapperError('The circuit requires a connectiontion between the phsycial '
'qubits %s and %s' % (physical_q0, physical_q1))
raise TranspilerError('The circuit requires a connection between physical '
'qubits %s and %s' % (physical_q0, physical_q1))
if (physical_q0, physical_q1) not in self.coupling_map.get_edges():
# A flip needs to be done
@ -94,7 +94,7 @@ class CXDirection(TransformationPass):
subdag.apply_operation_front(HGate(control))
# Flips the CX
cnot['op'].qargs[0], cnot['op'].qargs[1] = target, control
cnot_node['op'].qargs[0], cnot_node['op'].qargs[1] = target, control
new_dag.extend_back(subdag)

View File

@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
# Copyright 2018, IBM.
#
# This source code is licensed under the Apache License, Version 2.0 found in
# the LICENSE.txt file in the root directory of this source tree.
"""A pass for choosing a Layout of a circuit onto a Coupling graph.
This pass associates a physical qubit (int) to each virtual qubit
of the circuit (tuple(QuantumRegister, int)).
Note: even though a 'layout' is not strictly a property of the DAG,
in the transpiler architecture it is best passed around between passes by
being set in `property_set`.
"""
import numpy as np
import scipy.sparse as sp
import scipy.sparse.csgraph as cs
from qiskit.mapper import Layout
from qiskit.transpiler._basepasses import AnalysisPass
from qiskit.transpiler import TranspilerError
class DenseLayout(AnalysisPass):
"""
Chooses a Layout by finding the most connected subset of qubits.
"""
def __init__(self, coupling_map):
"""
Chooses a DenseLayout
Args:
coupling_map (Coupling): directed graph representing a coupling map.
Raises:
TranspilerError: if invalid options
"""
super().__init__()
self.coupling_map = coupling_map
def run(self, dag):
"""
Pick a convenient layout depending on the best matching
qubit connectivity, and set the property `layout`.
Args:
dag (DAGCircuit): DAG to find layout for.
Raises:
TranspilerError: if dag wider than self.coupling_map
"""
num_dag_qubits = sum([qreg.size for qreg in dag.qregs.values()])
if num_dag_qubits > self.coupling_map.size():
raise TranspilerError('Number of qubits greater than device.')
best_sub = self._best_subset(num_dag_qubits)
layout = Layout()
map_iter = 0
for qreg in dag.qregs.values():
for i in range(qreg.size):
layout[(qreg, i)] = int(best_sub[map_iter])
map_iter += 1
self.property_set['layout'] = layout
def _best_subset(self, n_qubits):
"""Computes the qubit mapping with the best connectivity.
Args:
n_qubits (int): Number of subset qubits to consider.
Returns:
ndarray: Array of qubits to use for best connectivity mapping.
"""
if n_qubits == 1:
return np.array([0])
device_qubits = self.coupling_map.size()
cmap = np.asarray(self.coupling_map.get_edges())
data = np.ones_like(cmap[:, 0])
sp_cmap = sp.coo_matrix((data, (cmap[:, 0], cmap[:, 1])),
shape=(device_qubits, device_qubits)).tocsr()
best = 0
best_map = None
# do bfs with each node as starting point
for k in range(sp_cmap.shape[0]):
bfs = cs.breadth_first_order(sp_cmap, i_start=k, directed=False,
return_predecessors=False)
connection_count = 0
for i in range(n_qubits):
node_idx = bfs[i]
for j in range(sp_cmap.indptr[node_idx],
sp_cmap.indptr[node_idx + 1]):
node = sp_cmap.indices[j]
for counter in range(n_qubits):
if node == bfs[counter]:
connection_count += 1
break
if connection_count > best:
best = connection_count
best_map = bfs[0:n_qubits]
return best_map

View File

@ -46,8 +46,9 @@ from qiskit import QuantumRegister
from qiskit.dagcircuit import DAGCircuit
from qiskit.extensions.standard import SwapGate
from qiskit.transpiler._basepasses import TransformationPass
from qiskit.mapper import Layout, MapperError
from qiskit.transpiler.passes import BarrierBeforeFinalMeasurements
from qiskit.transpiler import TranspilerError
from qiskit.mapper import Layout
SEARCH_DEPTH = 4
SEARCH_WIDTH = 4
@ -74,18 +75,18 @@ class LookaheadSwap(TransformationPass):
dag (DAGCircuit): the directed acyclic graph to be mapped
Returns:
DAGCircuit: A dag mapped to be compatible with the coupling_map in
the property_set.
the property_set.
Raises:
MapperError: If the provided DAG has more qubits than are available
in the coupling map.
TranspilerError: If the provided DAG has more qubits than are
available in the coupling map.
"""
coupling_map = self._coupling_map
ordered_virtual_gates = list(dag.serial_layers())
if len(dag.get_qubits()) > len(coupling_map.physical_qubits):
raise MapperError('DAG contains more qubits than are present in the coupling map.')
raise TranspilerError('DAG contains more qubits than are '
'present in the coupling map.')
dag_qubits = dag.get_qubits()
coupling_qubits = coupling_map.physical_qubits

View File

@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
# Copyright 2018, IBM.
#
# This source code is licensed under the Apache License, Version 2.0 found in
# the LICENSE.txt file in the root directory of this source tree.
"""A pass for choosing a Layout of a circuit onto a Coupling graph, using a simple
round-robin order.
This pass associates a physical qubit (int) to each virtual qubit
of the circuit (tuple(QuantumRegister, int)) in increasing order.
"""
from qiskit.mapper import Layout
from qiskit.transpiler._basepasses import AnalysisPass
from qiskit.transpiler import TranspilerError
class TrivialLayout(AnalysisPass):
"""
Chooses a Layout by assigning n circuit qubits to device qubits 0, .., n-1.
Does not assume any ancilla.
"""
def __init__(self, coupling_map):
"""
Choose a TrivialLayout.
Args:
coupling_map (Coupling): directed graph representing a coupling map.
Raises:
TranspilerError: if invalid options
"""
super().__init__()
self.coupling_map = coupling_map
def run(self, dag):
"""
Pick a convenient layout depending on the best matching
qubit connectivity, and set the property `layout`.
Args:
dag (DAGCircuit): DAG to find layout for.
Raises:
TranspilerError: if dag wider than self.coupling_map
"""
num_dag_qubits = sum([qreg.size for qreg in dag.qregs.values()])
if num_dag_qubits > self.coupling_map.size():
raise TranspilerError('Number of qubits greater than device.')
layout = Layout()
for qreg in dag.qregs.values():
layout.add_register(qreg)
self.property_set['layout'] = layout

View File

@ -16,7 +16,7 @@ import nbformat
from nbconvert.preprocessors import ExecutePreprocessor
from qiskit.tools.visualization._matplotlib import HAS_MATPLOTLIB
from ...common import (Path, QiskitTestCase, requires_qe_access,
requires_cpp_simulator)
requires_cpp_simulator, slow_test)
# Timeout (in seconds) for a single notebook.
@ -62,6 +62,7 @@ class TestJupyter(QiskitTestCase):
@unittest.skipIf(not HAS_MATPLOTLIB, 'matplotlib not available.')
@requires_qe_access
@slow_test
def test_backend_tools(self, qe_token, qe_url):
"Test Jupyter backend tools."
self._execute_notebook(self._get_resource_path(

View File

@ -36,7 +36,7 @@ class TestCheckMap(QiskitTestCase):
dag = circuit_to_dag(circuit)
pass_ = CheckMap(coupling)
pass_.run(dag)
self.assertTrue(pass_.property_set['is_mapped'])
self.assertTrue(pass_.property_set['is_swap_mapped'])
self.assertTrue(pass_.property_set['is_direction_mapped'])
def test_true_map(self):
@ -60,7 +60,7 @@ class TestCheckMap(QiskitTestCase):
pass_ = CheckMap(coupling)
pass_.run(dag)
self.assertTrue(pass_.property_set['is_mapped'])
self.assertTrue(pass_.property_set['is_swap_mapped'])
self.assertTrue(pass_.property_set['is_direction_mapped'])
def test_true_map_in_same_layer(self):
@ -85,7 +85,7 @@ class TestCheckMap(QiskitTestCase):
pass_ = CheckMap(coupling)
pass_.run(dag)
self.assertTrue(pass_.property_set['is_mapped'])
self.assertTrue(pass_.property_set['is_swap_mapped'])
self.assertTrue(pass_.property_set['is_direction_mapped'])
def test_false_map(self):
@ -105,7 +105,7 @@ class TestCheckMap(QiskitTestCase):
pass_ = CheckMap(coupling)
pass_.run(dag)
self.assertFalse(pass_.property_set['is_mapped'])
self.assertFalse(pass_.property_set['is_swap_mapped'])
self.assertFalse(pass_.property_set['is_direction_mapped'])
def test_true_map_undirected(self):
@ -129,7 +129,7 @@ class TestCheckMap(QiskitTestCase):
pass_ = CheckMap(coupling)
pass_.run(dag)
self.assertTrue(pass_.property_set['is_mapped'])
self.assertTrue(pass_.property_set['is_swap_mapped'])
self.assertFalse(pass_.property_set['is_direction_mapped'])
def test_true_map_in_same_layer_undirected(self):
@ -154,7 +154,7 @@ class TestCheckMap(QiskitTestCase):
pass_ = CheckMap(coupling)
pass_.run(dag)
self.assertTrue(pass_.property_set['is_mapped'])
self.assertTrue(pass_.property_set['is_swap_mapped'])
self.assertFalse(pass_.property_set['is_direction_mapped'])

View File

@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-
# Copyright 2018, IBM.
#
# This source code is licensed under the Apache License, Version 2.0 found in
# the LICENSE.txt file in the root directory of this source tree.
"""Test the DenseLayout pass"""
import unittest
from qiskit import QuantumRegister, QuantumCircuit
from qiskit.mapper import CouplingMap
from qiskit.transpiler.passes import DenseLayout
from qiskit.converters import circuit_to_dag
from ..common import QiskitTestCase
class TestDenseLayout(QiskitTestCase):
"""Tests the DenseLayout pass"""
def setUp(self):
"""
0 = 1 = 2 = 3 4
|| || || || X ||
5 = 6 = 7 = 8 = 9
|| X || || X ||
10 = 11 = 12 = 13 = 14
|| || X || X ||
15 = 16 = 17 18 19
"""
self.cmap20 = [[0, 1], [0, 5], [1, 0], [1, 2], [1, 6], [2, 1],
[2, 3], [2, 6], [3, 2], [3, 8], [3, 9], [4, 8], [4, 9],
[5, 0], [5, 6], [5, 10], [5, 11], [6, 1], [6, 2], [6, 5],
[6, 7], [6, 10], [6, 11], [7, 1], [7, 6], [7, 8], [7, 12],
[7, 13], [8, 3], [8, 4], [8, 7], [8, 9], [8, 12], [8, 13],
[9, 3], [9, 4], [9, 8], [10, 5], [10, 6], [10, 11], [10, 15],
[11, 5], [11, 6], [11, 10], [11, 12], [11, 16], [11, 17],
[12, 7], [12, 8], [12, 11], [12, 13], [12, 16], [13, 7],
[13, 8], [13, 12], [13, 14], [13, 18], [13, 19], [14, 13],
[14, 18], [14, 19], [15, 10], [15, 16], [16, 11], [16, 12],
[16, 15], [16, 17], [17, 11], [17, 16], [18, 13], [18, 14],
[19, 13], [19, 14]]
def test_5q_circuit_20q_coupling(self):
"""Test finds dense 5q corner in 20q coupling map.
"""
qr = QuantumRegister(5, 'q')
circuit = QuantumCircuit(qr)
circuit.cx(qr[0], qr[3])
circuit.cx(qr[3], qr[4])
circuit.cx(qr[3], qr[1])
circuit.cx(qr[0], qr[2])
dag = circuit_to_dag(circuit)
pass_ = DenseLayout(CouplingMap(self.cmap20))
pass_.run(dag)
layout = pass_.property_set['layout']
self.assertEqual(layout[qr[0]], 5)
self.assertEqual(layout[qr[1]], 0)
self.assertEqual(layout[qr[2]], 6)
self.assertEqual(layout[qr[3]], 10)
self.assertEqual(layout[qr[4]], 11)
def test_6q_circuit_20q_coupling(self):
"""Test finds dense 5q corner in 20q coupling map.
"""
qr0 = QuantumRegister(3, 'q0')
qr1 = QuantumRegister(3, 'q1')
circuit = QuantumCircuit(qr0, qr1)
circuit.cx(qr0[0], qr1[2])
circuit.cx(qr1[1], qr0[2])
dag = circuit_to_dag(circuit)
pass_ = DenseLayout(CouplingMap(self.cmap20))
pass_.run(dag)
layout = pass_.property_set['layout']
self.assertEqual(layout[qr0[0]], 5)
self.assertEqual(layout[qr0[1]], 0)
self.assertEqual(layout[qr0[2]], 6)
self.assertEqual(layout[qr1[0]], 10)
self.assertEqual(layout[qr1[1]], 11)
self.assertEqual(layout[qr1[2]], 1)
if __name__ == '__main__':
unittest.main()

View File

@ -10,7 +10,7 @@
import unittest
from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit
from qiskit.transpiler import MapperError
from qiskit.transpiler import TranspilerError
from qiskit.mapper import CouplingMap
from qiskit.transpiler.passes import CXDirection
from qiskit.converters import circuit_to_dag
@ -59,7 +59,7 @@ class TestCXDirection(QiskitTestCase):
pass_ = CXDirection(coupling)
with self.assertRaises(MapperError):
with self.assertRaises(TranspilerError):
pass_.run(dag)
def test_direction_correct(self):

View File

@ -0,0 +1,86 @@
# -*- coding: utf-8 -*-
# Copyright 2018, IBM.
#
# This source code is licensed under the Apache License, Version 2.0 found in
# the LICENSE.txt file in the root directory of this source tree.
"""Test the TrivialLayout pass"""
import unittest
from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit
from qiskit.mapper import CouplingMap
from qiskit.transpiler.passes import TrivialLayout
from qiskit.transpiler import TranspilerError
from qiskit.converters import circuit_to_dag
from ..common import QiskitTestCase
class TestDenseLayout(QiskitTestCase):
"""Tests the TrivialLayout pass"""
def setUp(self):
self.cmap5 = CouplingMap([[1, 0], [2, 0], [2, 1], [3, 2], [3, 4], [4, 2]])
self.cmap16 = CouplingMap([[1, 0], [1, 2], [2, 3], [3, 4], [3, 14], [5, 4],
[6, 5], [6, 7], [6, 11], [7, 10], [8, 7], [9, 8],
[9, 10], [11, 10], [12, 5], [12, 11], [12, 13],
[13, 4], [13, 14], [15, 0], [15, 2], [15, 14]])
def test_3q_circuit_5q_coupling(self):
"""Test finds trivial layout for 3q circuit on 5q device.
"""
qr = QuantumRegister(3, 'q')
circuit = QuantumCircuit(qr)
circuit.cx(qr[1], qr[0])
circuit.cx(qr[0], qr[2])
circuit.cx(qr[1], qr[2])
dag = circuit_to_dag(circuit)
pass_ = TrivialLayout(self.cmap5)
pass_.run(dag)
layout = pass_.property_set['layout']
for i in range(3):
self.assertEqual(layout[qr[i]], i)
def test_9q_circuit_16q_coupling(self):
"""Test finds trivial layout for 9q circuit with 2 registers on 16q device.
"""
qr0 = QuantumRegister(4, 'q0')
qr1 = QuantumRegister(5, 'q1')
cr = ClassicalRegister(2, 'c')
circuit = QuantumCircuit(qr0, qr1, cr)
circuit.cx(qr0[1], qr0[2])
circuit.cx(qr0[0], qr1[3])
circuit.cx(qr1[4], qr0[2])
circuit.measure(qr1[1], cr[0])
circuit.measure(qr0[2], cr[1])
dag = circuit_to_dag(circuit)
pass_ = TrivialLayout(self.cmap16)
pass_.run(dag)
layout = pass_.property_set['layout']
for i in range(4):
self.assertEqual(layout[qr0[i]], i)
for i in range(5):
self.assertEqual(layout[qr1[i]], i+4)
def test_raises_wider_circuit(self):
"""Test error is raised if the circuit is wider than coupling map.
"""
qr0 = QuantumRegister(3, 'q0')
qr1 = QuantumRegister(3, 'q1')
circuit = QuantumCircuit(qr0, qr1)
circuit.cx(qr0, qr1)
dag = circuit_to_dag(circuit)
with self.assertRaises(TranspilerError):
pass_ = TrivialLayout(self.cmap5)
pass_.run(dag)
if __name__ == '__main__':
unittest.main()