mirror of https://github.com/Qiskit/qiskit.git
Fix transpile() for control flow operations with a Target/BackendV2 (#8852)
* Fix transpile() for control flow operations with a Target/BackendV2 This commit fixes an issue when compiling with circuits that have control flow operations and are running transpile() with a BackendV2 instance or a custom Target. In these cases the transpile() operation would fail to run because the Target didn't have a provision to recognize a global variable width operation as part of the target. Previously all operations in the target needed to have an instance of an Operation so that the parameters and number of qubits could be verified. Each of these operation instances would be assigned a unique name. But, for control flow operations they're defined over a variable number of qubits and all have the same name (this is a similar problem for gates like mcx too). Not being able to fully represent the control flow operations in a target was preventing running transpile() on a circuit with control flow. This commit fixes this by adding support to the target to represent globally defined operations by passing the class into Target.add_instruction instead of an instance of that class. When the class is received the Target class will treat that operation name and class as always being valid for the target for all qubits and parameters. This can then be used to represent control flow operations in the target and run transpile() with control flow operations. Fixes #8824 * Simplify test slightly * Add release note * Add coupling map target test * Raise if instruction class and properties are both set * Only return global gates if they exist * Change UserWarning to a comment * Revert change to instructions() getter * Add release note about coupling map api change * Fix lint and logic bug * Add back nested while_loop to transpile() test
This commit is contained in:
parent
b3cf64f49c
commit
ad07847081
|
@ -22,12 +22,14 @@ from collections import defaultdict
|
|||
import datetime
|
||||
import io
|
||||
import logging
|
||||
import inspect
|
||||
|
||||
import retworkx as rx
|
||||
|
||||
from qiskit.circuit.parameter import Parameter
|
||||
from qiskit.pulse.instruction_schedule_map import InstructionScheduleMap
|
||||
from qiskit.transpiler.coupling import CouplingMap
|
||||
from qiskit.transpiler.exceptions import TranspilerError
|
||||
from qiskit.transpiler.instruction_durations import InstructionDurations
|
||||
from qiskit.transpiler.timing_constraints import TimingConstraints
|
||||
|
||||
|
@ -295,7 +297,11 @@ class Target(Mapping):
|
|||
|
||||
Args:
|
||||
instruction (qiskit.circuit.Instruction): The operation object to add to the map. If it's
|
||||
paramerterized any value of the parameter can be set
|
||||
paramerterized any value of the parameter can be set. Optionally for variable width
|
||||
instructions (such as control flow operations such as :class:`~.ForLoop` or
|
||||
:class:`~MCXGate`) you can specify the class. If the class is specified than the
|
||||
``name`` argument must be specified. When a class is used the gate is treated as global
|
||||
and not having any properties set.
|
||||
properties (dict): A dictionary of qarg entries to an
|
||||
:class:`~qiskit.transpiler.InstructionProperties` object for that
|
||||
instruction implementation on the backend. Properties are optional
|
||||
|
@ -319,19 +325,37 @@ class Target(Mapping):
|
|||
documentation for the :class:`~qiskit.transpiler.Target` class).
|
||||
Raises:
|
||||
AttributeError: If gate is already in map
|
||||
TranspilerError: If an operation class is passed in for ``instruction`` and no name
|
||||
is specified or ``properties`` is set.
|
||||
"""
|
||||
is_class = inspect.isclass(instruction)
|
||||
if not is_class:
|
||||
instruction_name = name or instruction.name
|
||||
else:
|
||||
# Invalid to have class input without a name with characters set "" is not a valid name
|
||||
if not name:
|
||||
raise TranspilerError(
|
||||
"A name must be specified when defining a supported global operation by class"
|
||||
)
|
||||
if properties is not None:
|
||||
raise TranspilerError(
|
||||
"An instruction added globally by class can't have properties set."
|
||||
)
|
||||
instruction_name = name
|
||||
if properties is None:
|
||||
properties = {None: None}
|
||||
instruction_name = name or instruction.name
|
||||
if instruction_name in self._gate_map:
|
||||
raise AttributeError("Instruction %s is already in the target" % instruction_name)
|
||||
self._gate_name_map[instruction_name] = instruction
|
||||
qargs_val = {}
|
||||
for qarg in properties:
|
||||
if qarg is not None:
|
||||
self.num_qubits = max(self.num_qubits, max(qarg) + 1)
|
||||
qargs_val[qarg] = properties[qarg]
|
||||
self._qarg_gate_map[qarg].add(instruction_name)
|
||||
if is_class:
|
||||
qargs_val = {None: None}
|
||||
else:
|
||||
qargs_val = {}
|
||||
for qarg in properties:
|
||||
if qarg is not None:
|
||||
self.num_qubits = max(self.num_qubits, max(qarg) + 1)
|
||||
qargs_val[qarg] = properties[qarg]
|
||||
self._qarg_gate_map[qarg].add(instruction_name)
|
||||
self._gate_map[instruction_name] = qargs_val
|
||||
self._coupling_graph = None
|
||||
self._instruction_durations = None
|
||||
|
@ -494,7 +518,9 @@ class Target(Mapping):
|
|||
instruction (str): The instruction name to get the
|
||||
:class:`~qiskit.circuit.Instruction` instance for
|
||||
Returns:
|
||||
qiskit.circuit.Instruction: The Instruction instance corresponding to the name
|
||||
qiskit.circuit.Instruction: The Instruction instance corresponding to the
|
||||
name. This also can also be the class for globally defined variable with
|
||||
operations.
|
||||
"""
|
||||
return self._gate_name_map[instruction]
|
||||
|
||||
|
@ -507,14 +533,18 @@ class Target(Mapping):
|
|||
instructions that apply to qubit 0.
|
||||
Returns:
|
||||
list: The list of :class:`~qiskit.circuit.Instruction` instances
|
||||
that apply to the specified qarg.
|
||||
that apply to the specified qarg. This may also be a class if
|
||||
a variable width operation is globally defined.
|
||||
|
||||
Raises:
|
||||
KeyError: If qargs is not in target
|
||||
"""
|
||||
if qargs not in self._qarg_gate_map:
|
||||
raise KeyError(f"{qargs} not in target.")
|
||||
return [self._gate_name_map[x] for x in self._qarg_gate_map[qargs]]
|
||||
res = [self._gate_name_map[x] for x in self._qarg_gate_map[qargs]]
|
||||
if None in self._qarg_gate_map:
|
||||
res += [self._gate_name_map[x] for x in self._qarg_gate_map[None]]
|
||||
return res
|
||||
|
||||
def operation_names_for_qargs(self, qargs):
|
||||
"""Get the operation names for a specified qargs tuple
|
||||
|
@ -609,6 +639,20 @@ class Target(Mapping):
|
|||
qargs = tuple(qargs)
|
||||
if operation_class is not None:
|
||||
for op_name, obj in self._gate_name_map.items():
|
||||
if inspect.isclass(obj):
|
||||
if obj != operation_class:
|
||||
continue
|
||||
# If no qargs a operation class is supported
|
||||
if qargs is None:
|
||||
return True
|
||||
# If qargs set then validate no duplicates and all indices are valid on device
|
||||
elif all(qarg <= self.num_qubits for qarg in qargs) and len(set(qargs)) == len(
|
||||
qargs
|
||||
):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
if isinstance(obj, operation_class):
|
||||
if parameters is not None:
|
||||
if len(parameters) != len(obj.params):
|
||||
|
@ -627,6 +671,23 @@ class Target(Mapping):
|
|||
if operation_name in self._gate_map:
|
||||
if parameters is not None:
|
||||
obj = self._gate_name_map[operation_name]
|
||||
if inspect.isclass(obj):
|
||||
# The parameters argument was set and the operation_name specified is
|
||||
# defined as a globally supported class in the target. This means
|
||||
# there is no available validation (including whether the specified
|
||||
# operation supports parameters), the returned value will not factor
|
||||
# in the argument `parameters`,
|
||||
|
||||
# If no qargs a operation class is supported
|
||||
if qargs is None:
|
||||
return True
|
||||
# If qargs set then validate no duplicates and all indices are valid on device
|
||||
elif all(qarg <= self.num_qubits for qarg in qargs) and len(set(qargs)) == len(
|
||||
qargs
|
||||
):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
if len(parameters) != len(obj.params):
|
||||
return False
|
||||
for index, param in enumerate(parameters):
|
||||
|
@ -643,9 +704,21 @@ class Target(Mapping):
|
|||
if qargs in self._gate_map[operation_name]:
|
||||
return True
|
||||
if self._gate_map[operation_name] is None or None in self._gate_map[operation_name]:
|
||||
return self._gate_name_map[operation_name].num_qubits == len(qargs) and all(
|
||||
x < self.num_qubits for x in qargs
|
||||
)
|
||||
obj = self._gate_name_map[operation_name]
|
||||
if inspect.isclass(obj):
|
||||
if qargs is None:
|
||||
return True
|
||||
# If qargs set then validate no duplicates and all indices are valid on device
|
||||
elif all(qarg <= self.num_qubits for qarg in qargs) and len(set(qargs)) == len(
|
||||
qargs
|
||||
):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return self._gate_name_map[operation_name].num_qubits == len(qargs) and all(
|
||||
x < self.num_qubits for x in qargs
|
||||
)
|
||||
return False
|
||||
|
||||
@property
|
||||
|
@ -661,7 +734,12 @@ class Target(Mapping):
|
|||
@property
|
||||
def instructions(self):
|
||||
"""Get the list of tuples ``(:class:`~qiskit.circuit.Instruction`, (qargs))``
|
||||
for the target"""
|
||||
for the target
|
||||
|
||||
For globally defined variable width operations the tuple will be of the form
|
||||
``(class, None)`` where class is the actual operation class that
|
||||
is globally defined.
|
||||
"""
|
||||
return [
|
||||
(self._gate_name_map[op], qarg) for op in self._gate_map for qarg in self._gate_map[op]
|
||||
]
|
||||
|
@ -711,6 +789,8 @@ class Target(Mapping):
|
|||
self._coupling_graph.add_nodes_from([{} for _ in range(self.num_qubits)])
|
||||
for gate, qarg_map in self._gate_map.items():
|
||||
for qarg, properties in qarg_map.items():
|
||||
if qarg is None:
|
||||
continue
|
||||
if len(qarg) == 1:
|
||||
self._coupling_graph[qarg[0]] = properties
|
||||
elif len(qarg) == 2:
|
||||
|
@ -719,6 +799,8 @@ class Target(Mapping):
|
|||
edge_data[gate] = properties
|
||||
except rx.NoEdgeBetweenNodes:
|
||||
self._coupling_graph.add_edge(*qarg, {gate: properties})
|
||||
if self._coupling_graph.num_edges() == 0 and any(x is None for x in self._qarg_gate_map):
|
||||
self._coupling_graph = None
|
||||
|
||||
def build_coupling_map(self, two_q_gate=None):
|
||||
"""Get a :class:`~qiskit.transpiler.CouplingMap` from this target.
|
||||
|
@ -761,9 +843,14 @@ class Target(Mapping):
|
|||
|
||||
if self._coupling_graph is None:
|
||||
self._build_coupling_graph()
|
||||
cmap = CouplingMap()
|
||||
cmap.graph = self._coupling_graph
|
||||
return cmap
|
||||
# if there is no connectivity constraints in the coupling graph treat it as not
|
||||
# existing and return
|
||||
if self._coupling_graph is not None:
|
||||
cmap = CouplingMap()
|
||||
cmap.graph = self._coupling_graph
|
||||
return cmap
|
||||
else:
|
||||
return None
|
||||
|
||||
@property
|
||||
def physical_qubits(self):
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Add support for representing an operation that has a variable width
|
||||
to the :class:`~.Target` class. Previously, a :class:`~.Target` object
|
||||
needed to have an instance of :class:`~Operation` defined for each
|
||||
operation supported in the target. This was used for both validation
|
||||
of arguments and parameters of the operation. However, for operations
|
||||
that have a variable width this wasn't possible because each instance
|
||||
of an :class:`~Operation` class can only have a fixed number of qubits.
|
||||
For cases where a backend supports variable width operations the
|
||||
instruction can be added with the class of the operation instead of an
|
||||
instance. In such cases the operation will be treated as globally
|
||||
supported on all qubits. For example, if building a target like::
|
||||
|
||||
from qiskit.transpiler import Target
|
||||
|
||||
ibm_target = Target()
|
||||
i_props = {
|
||||
(0,): InstructionProperties(duration=35.5e-9, error=0.000413),
|
||||
(1,): InstructionProperties(duration=35.5e-9, error=0.000502),
|
||||
(2,): InstructionProperties(duration=35.5e-9, error=0.0004003),
|
||||
(3,): InstructionProperties(duration=35.5e-9, error=0.000614),
|
||||
(4,): InstructionProperties(duration=35.5e-9, error=0.006149),
|
||||
}
|
||||
ibm_target.add_instruction(IGate(), i_props)
|
||||
rz_props = {
|
||||
(0,): InstructionProperties(duration=0, error=0),
|
||||
(1,): InstructionProperties(duration=0, error=0),
|
||||
(2,): InstructionProperties(duration=0, error=0),
|
||||
(3,): InstructionProperties(duration=0, error=0),
|
||||
(4,): InstructionProperties(duration=0, error=0),
|
||||
}
|
||||
ibm_target.add_instruction(RZGate(theta), rz_props)
|
||||
sx_props = {
|
||||
(0,): InstructionProperties(duration=35.5e-9, error=0.000413),
|
||||
(1,): InstructionProperties(duration=35.5e-9, error=0.000502),
|
||||
(2,): InstructionProperties(duration=35.5e-9, error=0.0004003),
|
||||
(3,): InstructionProperties(duration=35.5e-9, error=0.000614),
|
||||
(4,): InstructionProperties(duration=35.5e-9, error=0.006149),
|
||||
}
|
||||
ibm_target.add_instruction(SXGate(), sx_props)
|
||||
x_props = {
|
||||
(0,): InstructionProperties(duration=35.5e-9, error=0.000413),
|
||||
(1,): InstructionProperties(duration=35.5e-9, error=0.000502),
|
||||
(2,): InstructionProperties(duration=35.5e-9, error=0.0004003),
|
||||
(3,): InstructionProperties(duration=35.5e-9, error=0.000614),
|
||||
(4,): InstructionProperties(duration=35.5e-9, error=0.006149),
|
||||
}
|
||||
ibm_target.add_instruction(XGate(), x_props)
|
||||
cx_props = {
|
||||
(3, 4): InstructionProperties(duration=270.22e-9, error=0.00713),
|
||||
(4, 3): InstructionProperties(duration=305.77e-9, error=0.00713),
|
||||
(3, 1): InstructionProperties(duration=462.22e-9, error=0.00929),
|
||||
(1, 3): InstructionProperties(duration=497.77e-9, error=0.00929),
|
||||
(1, 2): InstructionProperties(duration=227.55e-9, error=0.00659),
|
||||
(2, 1): InstructionProperties(duration=263.11e-9, error=0.00659),
|
||||
(0, 1): InstructionProperties(duration=519.11e-9, error=0.01201),
|
||||
(1, 0): InstructionProperties(duration=554.66e-9, error=0.01201),
|
||||
}
|
||||
ibm_target.add_instruction(CXGate(), cx_props)
|
||||
measure_props = {
|
||||
(0,): InstructionProperties(duration=5.813e-6, error=0.0751),
|
||||
(1,): InstructionProperties(duration=5.813e-6, error=0.0225),
|
||||
(2,): InstructionProperties(duration=5.813e-6, error=0.0146),
|
||||
(3,): InstructionProperties(duration=5.813e-6, error=0.0215),
|
||||
(4,): InstructionProperties(duration=5.813e-6, error=0.0333),
|
||||
}
|
||||
ibm_target.add_instruction(Measure(), measure_props)
|
||||
ibm_target.add_instruction(IfElseOp, name="if_else")
|
||||
ibm_target.add_instruction(ForLoopOp, name="for_loop")
|
||||
ibm_target.add_instruction(WhileLoopOp, name="while_loop")
|
||||
|
||||
The :class:`~.IfElseOp`, :class:`~.ForLoopOp`, and :class:`~.WhileLoopOp`
|
||||
operations are globally supported for any number of qubits. This is then
|
||||
reflected by other calls in the :class:`~.Target` API such as
|
||||
:meth:`~.Target.instruction_supported`::
|
||||
|
||||
ibm_target.instruction_supported(operation_class=WhileLoopOp, qargs=(0, 2, 3, 4))
|
||||
ibm_target.instruction_supported('if_else', qargs=(0, 1))
|
||||
|
||||
both return ``True``.
|
||||
upgrade:
|
||||
- |
|
||||
For :class:`~.Target` objects that only contain globally defined 2 qubit
|
||||
operations without any connectivity constaints the return from the
|
||||
:meth:`.Target.build_coupling_map` method will now return ``None`` instead
|
||||
of a :class:`~.CouplingMap` object that contains ``num_qubits`` nodes
|
||||
and no edges. This change was made to better reflect the actual
|
||||
connectivity constraints of the :class:`~.Target` because in this case
|
||||
there are no connectivity constraints on the backend being modeled by
|
||||
the :class:`~.Target`, not a lack of connecitvity. If you desire the
|
||||
previous behavior for any reason you can reproduce it by checking for a
|
||||
``None`` and manually building a coupling map, for example::
|
||||
|
||||
from qiskit.transpiler import Target
|
||||
from qiskit.circuit.library import CXGate
|
||||
|
||||
target = Target(num_qubits=3)
|
||||
target.add_instruction(CXGate())
|
||||
cmap = target.build_coupling_map()
|
||||
if cmap is None:
|
||||
cmap = CouplingMap()
|
||||
for i in range(target.num_qubits):
|
||||
cmap.add_physical_qubit(i)
|
|
@ -32,7 +32,18 @@ from qiskit.circuit import Parameter, Gate, Qubit, Clbit
|
|||
from qiskit.compiler import transpile
|
||||
from qiskit.dagcircuit import DAGOutNode
|
||||
from qiskit.converters import circuit_to_dag
|
||||
from qiskit.circuit.library import CXGate, U3Gate, U2Gate, U1Gate, RXGate, RYGate, RZGate, UGate
|
||||
from qiskit.circuit.library import (
|
||||
CXGate,
|
||||
U3Gate,
|
||||
U2Gate,
|
||||
U1Gate,
|
||||
RXGate,
|
||||
RYGate,
|
||||
RZGate,
|
||||
UGate,
|
||||
CZGate,
|
||||
)
|
||||
from qiskit.circuit import IfElseOp, WhileLoopOp, ForLoopOp, ControlFlowOp
|
||||
from qiskit.circuit.measure import Measure
|
||||
from qiskit.test import QiskitTestCase
|
||||
from qiskit.providers.fake_provider import (
|
||||
|
@ -1469,6 +1480,79 @@ class TestTranspile(QiskitTestCase):
|
|||
expected.measure(qubit_reg, clbit_reg)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
# TODO: Add optimization level 2 and 3 after they support control flow
|
||||
# compilation
|
||||
@data(0, 1)
|
||||
def test_transpile_with_custom_control_flow_target(self, opt_level):
|
||||
"""Test transpile() with a target and constrol flow ops."""
|
||||
target = FakeMumbaiV2().target
|
||||
target.add_instruction(ForLoopOp, name="for_loop")
|
||||
target.add_instruction(WhileLoopOp, name="while_loop")
|
||||
target.add_instruction(IfElseOp, name="if_else")
|
||||
|
||||
class CustomCX(Gate):
|
||||
"""Custom CX"""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("custom_cx", 2, [])
|
||||
|
||||
def _define(self):
|
||||
self._definition = QuantumCircuit(2)
|
||||
self._definition.cx(0, 1)
|
||||
|
||||
circuit = QuantumCircuit(6, 1)
|
||||
circuit.h(0)
|
||||
circuit.measure(0, 0)
|
||||
circuit.cx(0, 1)
|
||||
circuit.cz(0, 2)
|
||||
circuit.append(CustomCX(), [1, 2], [])
|
||||
with circuit.for_loop((1,)):
|
||||
circuit.cx(0, 1)
|
||||
circuit.cz(0, 2)
|
||||
circuit.append(CustomCX(), [1, 2], [])
|
||||
with circuit.if_test((circuit.clbits[0], True)) as else_:
|
||||
circuit.cx(0, 1)
|
||||
circuit.cz(0, 2)
|
||||
circuit.append(CustomCX(), [1, 2], [])
|
||||
with else_:
|
||||
circuit.cx(3, 4)
|
||||
circuit.cz(3, 5)
|
||||
circuit.append(CustomCX(), [4, 5], [])
|
||||
with circuit.while_loop((circuit.clbits[0], True)):
|
||||
circuit.cx(3, 4)
|
||||
circuit.cz(3, 5)
|
||||
circuit.append(CustomCX(), [4, 5], [])
|
||||
transpiled = transpile(
|
||||
circuit, optimization_level=opt_level, target=target, seed_transpiler=12434
|
||||
)
|
||||
# Tests of the complete validity of a circuit are mostly done at the indiviual pass level;
|
||||
# here we're just checking that various passes do appear to have run.
|
||||
self.assertIsInstance(transpiled, QuantumCircuit)
|
||||
# Assert layout ran.
|
||||
self.assertIsNot(getattr(transpiled, "_layout", None), None)
|
||||
|
||||
def _visit_block(circuit, qubit_mapping=None):
|
||||
for instruction in circuit:
|
||||
qargs = tuple(qubit_mapping[x] for x in instruction.qubits)
|
||||
self.assertTrue(target.instruction_supported(instruction.operation.name, qargs))
|
||||
if isinstance(instruction.operation, ControlFlowOp):
|
||||
for block in instruction.operation.blocks:
|
||||
new_mapping = {
|
||||
inner: qubit_mapping[outer]
|
||||
for outer, inner in zip(instruction.qubits, block.qubits)
|
||||
}
|
||||
_visit_block(block, new_mapping)
|
||||
# Assert unrolling ran.
|
||||
self.assertNotIsInstance(instruction.operation, CustomCX)
|
||||
# Assert translation ran.
|
||||
self.assertNotIsInstance(instruction.operation, CZGate)
|
||||
|
||||
# Assert routing ran.
|
||||
_visit_block(
|
||||
transpiled,
|
||||
qubit_mapping={qubit: index for index, qubit in enumerate(transpiled.qubits)},
|
||||
)
|
||||
|
||||
|
||||
class StreamHandlerRaiseException(StreamHandler):
|
||||
"""Handler class that will raise an exception on formatting errors."""
|
||||
|
|
|
@ -112,7 +112,7 @@ class TestDenseLayout(QiskitTestCase):
|
|||
dag = circuit_to_dag(circuit)
|
||||
instruction_props = {edge: None for edge in CouplingMap.from_heavy_hex(3).get_edges()}
|
||||
noiseless_target = Target()
|
||||
noiseless_target.add_instruction(CXGate, instruction_props)
|
||||
noiseless_target.add_instruction(CXGate(), instruction_props)
|
||||
pass_ = DenseLayout(target=noiseless_target)
|
||||
pass_.run(dag)
|
||||
layout = pass_.property_set["layout"]
|
||||
|
|
|
@ -30,6 +30,7 @@ from qiskit.circuit.library import (
|
|||
RZXGate,
|
||||
CZGate,
|
||||
)
|
||||
from qiskit.circuit import IfElseOp, ForLoopOp, WhileLoopOp
|
||||
from qiskit.circuit.measure import Measure
|
||||
from qiskit.circuit.parameter import Parameter
|
||||
from qiskit import pulse
|
||||
|
@ -1165,6 +1166,342 @@ Instructions:
|
|||
)
|
||||
|
||||
|
||||
class TestGlobalVariableWidthOperations(QiskitTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.theta = Parameter("theta")
|
||||
self.phi = Parameter("phi")
|
||||
self.lam = Parameter("lambda")
|
||||
self.target_global_gates_only = Target(num_qubits=5)
|
||||
self.target_global_gates_only.add_instruction(CXGate())
|
||||
self.target_global_gates_only.add_instruction(UGate(self.theta, self.phi, self.lam))
|
||||
self.target_global_gates_only.add_instruction(Measure())
|
||||
self.target_global_gates_only.add_instruction(IfElseOp, name="if_else")
|
||||
self.target_global_gates_only.add_instruction(ForLoopOp, name="for_loop")
|
||||
self.target_global_gates_only.add_instruction(WhileLoopOp, name="while_loop")
|
||||
self.ibm_target = Target()
|
||||
i_props = {
|
||||
(0,): InstructionProperties(duration=35.5e-9, error=0.000413),
|
||||
(1,): InstructionProperties(duration=35.5e-9, error=0.000502),
|
||||
(2,): InstructionProperties(duration=35.5e-9, error=0.0004003),
|
||||
(3,): InstructionProperties(duration=35.5e-9, error=0.000614),
|
||||
(4,): InstructionProperties(duration=35.5e-9, error=0.006149),
|
||||
}
|
||||
self.ibm_target.add_instruction(IGate(), i_props)
|
||||
rz_props = {
|
||||
(0,): InstructionProperties(duration=0, error=0),
|
||||
(1,): InstructionProperties(duration=0, error=0),
|
||||
(2,): InstructionProperties(duration=0, error=0),
|
||||
(3,): InstructionProperties(duration=0, error=0),
|
||||
(4,): InstructionProperties(duration=0, error=0),
|
||||
}
|
||||
self.ibm_target.add_instruction(RZGate(self.theta), rz_props)
|
||||
sx_props = {
|
||||
(0,): InstructionProperties(duration=35.5e-9, error=0.000413),
|
||||
(1,): InstructionProperties(duration=35.5e-9, error=0.000502),
|
||||
(2,): InstructionProperties(duration=35.5e-9, error=0.0004003),
|
||||
(3,): InstructionProperties(duration=35.5e-9, error=0.000614),
|
||||
(4,): InstructionProperties(duration=35.5e-9, error=0.006149),
|
||||
}
|
||||
self.ibm_target.add_instruction(SXGate(), sx_props)
|
||||
x_props = {
|
||||
(0,): InstructionProperties(duration=35.5e-9, error=0.000413),
|
||||
(1,): InstructionProperties(duration=35.5e-9, error=0.000502),
|
||||
(2,): InstructionProperties(duration=35.5e-9, error=0.0004003),
|
||||
(3,): InstructionProperties(duration=35.5e-9, error=0.000614),
|
||||
(4,): InstructionProperties(duration=35.5e-9, error=0.006149),
|
||||
}
|
||||
self.ibm_target.add_instruction(XGate(), x_props)
|
||||
cx_props = {
|
||||
(3, 4): InstructionProperties(duration=270.22e-9, error=0.00713),
|
||||
(4, 3): InstructionProperties(duration=305.77e-9, error=0.00713),
|
||||
(3, 1): InstructionProperties(duration=462.22e-9, error=0.00929),
|
||||
(1, 3): InstructionProperties(duration=497.77e-9, error=0.00929),
|
||||
(1, 2): InstructionProperties(duration=227.55e-9, error=0.00659),
|
||||
(2, 1): InstructionProperties(duration=263.11e-9, error=0.00659),
|
||||
(0, 1): InstructionProperties(duration=519.11e-9, error=0.01201),
|
||||
(1, 0): InstructionProperties(duration=554.66e-9, error=0.01201),
|
||||
}
|
||||
self.ibm_target.add_instruction(CXGate(), cx_props)
|
||||
measure_props = {
|
||||
(0,): InstructionProperties(duration=5.813e-6, error=0.0751),
|
||||
(1,): InstructionProperties(duration=5.813e-6, error=0.0225),
|
||||
(2,): InstructionProperties(duration=5.813e-6, error=0.0146),
|
||||
(3,): InstructionProperties(duration=5.813e-6, error=0.0215),
|
||||
(4,): InstructionProperties(duration=5.813e-6, error=0.0333),
|
||||
}
|
||||
self.ibm_target.add_instruction(Measure(), measure_props)
|
||||
self.ibm_target.add_instruction(IfElseOp, name="if_else")
|
||||
self.ibm_target.add_instruction(ForLoopOp, name="for_loop")
|
||||
self.ibm_target.add_instruction(WhileLoopOp, name="while_loop")
|
||||
self.aqt_target = Target(description="AQT Target")
|
||||
rx_props = {
|
||||
(0,): None,
|
||||
(1,): None,
|
||||
(2,): None,
|
||||
(3,): None,
|
||||
(4,): None,
|
||||
}
|
||||
self.aqt_target.add_instruction(RXGate(self.theta), rx_props)
|
||||
ry_props = {
|
||||
(0,): None,
|
||||
(1,): None,
|
||||
(2,): None,
|
||||
(3,): None,
|
||||
(4,): None,
|
||||
}
|
||||
self.aqt_target.add_instruction(RYGate(self.theta), ry_props)
|
||||
rz_props = {
|
||||
(0,): None,
|
||||
(1,): None,
|
||||
(2,): None,
|
||||
(3,): None,
|
||||
(4,): None,
|
||||
}
|
||||
self.aqt_target.add_instruction(RZGate(self.theta), rz_props)
|
||||
r_props = {
|
||||
(0,): None,
|
||||
(1,): None,
|
||||
(2,): None,
|
||||
(3,): None,
|
||||
(4,): None,
|
||||
}
|
||||
self.aqt_target.add_instruction(RGate(self.theta, self.phi), r_props)
|
||||
rxx_props = {
|
||||
(0, 1): None,
|
||||
(0, 2): None,
|
||||
(0, 3): None,
|
||||
(0, 4): None,
|
||||
(1, 0): None,
|
||||
(2, 0): None,
|
||||
(3, 0): None,
|
||||
(4, 0): None,
|
||||
(1, 2): None,
|
||||
(1, 3): None,
|
||||
(1, 4): None,
|
||||
(2, 1): None,
|
||||
(3, 1): None,
|
||||
(4, 1): None,
|
||||
(2, 3): None,
|
||||
(2, 4): None,
|
||||
(3, 2): None,
|
||||
(4, 2): None,
|
||||
(3, 4): None,
|
||||
(4, 3): None,
|
||||
}
|
||||
self.aqt_target.add_instruction(RXXGate(self.theta), rxx_props)
|
||||
measure_props = {
|
||||
(0,): None,
|
||||
(1,): None,
|
||||
(2,): None,
|
||||
(3,): None,
|
||||
(4,): None,
|
||||
}
|
||||
self.aqt_target.add_instruction(Measure(), measure_props)
|
||||
self.aqt_target.add_instruction(IfElseOp, name="if_else")
|
||||
self.aqt_target.add_instruction(ForLoopOp, name="for_loop")
|
||||
self.aqt_target.add_instruction(WhileLoopOp, name="while_loop")
|
||||
|
||||
def test_qargs(self):
|
||||
expected_ibm = {
|
||||
(0,),
|
||||
(1,),
|
||||
(2,),
|
||||
(3,),
|
||||
(4,),
|
||||
(3, 4),
|
||||
(4, 3),
|
||||
(3, 1),
|
||||
(1, 3),
|
||||
(1, 2),
|
||||
(2, 1),
|
||||
(0, 1),
|
||||
(1, 0),
|
||||
}
|
||||
self.assertEqual(expected_ibm, self.ibm_target.qargs)
|
||||
expected_aqt = {
|
||||
(0,),
|
||||
(1,),
|
||||
(2,),
|
||||
(3,),
|
||||
(4,),
|
||||
(0, 1),
|
||||
(0, 2),
|
||||
(0, 3),
|
||||
(0, 4),
|
||||
(1, 0),
|
||||
(2, 0),
|
||||
(3, 0),
|
||||
(4, 0),
|
||||
(1, 2),
|
||||
(1, 3),
|
||||
(1, 4),
|
||||
(2, 1),
|
||||
(3, 1),
|
||||
(4, 1),
|
||||
(2, 3),
|
||||
(2, 4),
|
||||
(3, 2),
|
||||
(4, 2),
|
||||
(3, 4),
|
||||
(4, 3),
|
||||
}
|
||||
self.assertEqual(expected_aqt, self.aqt_target.qargs)
|
||||
self.assertEqual(None, self.target_global_gates_only.qargs)
|
||||
|
||||
def test_qargs_for_operation_name(self):
|
||||
self.assertEqual(
|
||||
self.ibm_target.qargs_for_operation_name("rz"), {(0,), (1,), (2,), (3,), (4,)}
|
||||
)
|
||||
self.assertEqual(
|
||||
self.aqt_target.qargs_for_operation_name("rz"), {(0,), (1,), (2,), (3,), (4,)}
|
||||
)
|
||||
self.assertIsNone(self.target_global_gates_only.qargs_for_operation_name("cx"))
|
||||
self.assertIsNone(self.ibm_target.qargs_for_operation_name("if_else"))
|
||||
self.assertIsNone(self.aqt_target.qargs_for_operation_name("while_loop"))
|
||||
|
||||
def test_instruction_names(self):
|
||||
self.assertEqual(
|
||||
self.ibm_target.operation_names,
|
||||
{"rz", "id", "sx", "x", "cx", "measure", "if_else", "while_loop", "for_loop"},
|
||||
)
|
||||
self.assertEqual(
|
||||
self.aqt_target.operation_names,
|
||||
{"rz", "ry", "rx", "rxx", "r", "measure", "if_else", "while_loop", "for_loop"},
|
||||
)
|
||||
self.assertEqual(
|
||||
self.target_global_gates_only.operation_names,
|
||||
{"u", "cx", "measure", "if_else", "while_loop", "for_loop"},
|
||||
)
|
||||
|
||||
def test_operations(self):
|
||||
ibm_expected = [
|
||||
RZGate(self.theta),
|
||||
IGate(),
|
||||
SXGate(),
|
||||
XGate(),
|
||||
CXGate(),
|
||||
Measure(),
|
||||
WhileLoopOp,
|
||||
IfElseOp,
|
||||
ForLoopOp,
|
||||
]
|
||||
for gate in ibm_expected:
|
||||
self.assertIn(gate, self.ibm_target.operations)
|
||||
aqt_expected = [
|
||||
RZGate(self.theta),
|
||||
RXGate(self.theta),
|
||||
RYGate(self.theta),
|
||||
RGate(self.theta, self.phi),
|
||||
RXXGate(self.theta),
|
||||
ForLoopOp,
|
||||
IfElseOp,
|
||||
WhileLoopOp,
|
||||
]
|
||||
for gate in aqt_expected:
|
||||
self.assertIn(gate, self.aqt_target.operations)
|
||||
fake_expected = [
|
||||
UGate(self.theta, self.phi, self.lam),
|
||||
CXGate(),
|
||||
Measure(),
|
||||
ForLoopOp,
|
||||
WhileLoopOp,
|
||||
IfElseOp,
|
||||
]
|
||||
for gate in fake_expected:
|
||||
self.assertIn(gate, self.target_global_gates_only.operations)
|
||||
|
||||
def test_instructions(self):
|
||||
ibm_expected = [
|
||||
(IGate(), (0,)),
|
||||
(IGate(), (1,)),
|
||||
(IGate(), (2,)),
|
||||
(IGate(), (3,)),
|
||||
(IGate(), (4,)),
|
||||
(RZGate(self.theta), (0,)),
|
||||
(RZGate(self.theta), (1,)),
|
||||
(RZGate(self.theta), (2,)),
|
||||
(RZGate(self.theta), (3,)),
|
||||
(RZGate(self.theta), (4,)),
|
||||
(SXGate(), (0,)),
|
||||
(SXGate(), (1,)),
|
||||
(SXGate(), (2,)),
|
||||
(SXGate(), (3,)),
|
||||
(SXGate(), (4,)),
|
||||
(XGate(), (0,)),
|
||||
(XGate(), (1,)),
|
||||
(XGate(), (2,)),
|
||||
(XGate(), (3,)),
|
||||
(XGate(), (4,)),
|
||||
(CXGate(), (3, 4)),
|
||||
(CXGate(), (4, 3)),
|
||||
(CXGate(), (3, 1)),
|
||||
(CXGate(), (1, 3)),
|
||||
(CXGate(), (1, 2)),
|
||||
(CXGate(), (2, 1)),
|
||||
(CXGate(), (0, 1)),
|
||||
(CXGate(), (1, 0)),
|
||||
(Measure(), (0,)),
|
||||
(Measure(), (1,)),
|
||||
(Measure(), (2,)),
|
||||
(Measure(), (3,)),
|
||||
(Measure(), (4,)),
|
||||
(IfElseOp, None),
|
||||
(ForLoopOp, None),
|
||||
(WhileLoopOp, None),
|
||||
]
|
||||
self.assertEqual(ibm_expected, self.ibm_target.instructions)
|
||||
ideal_sim_expected = [
|
||||
(CXGate(), None),
|
||||
(UGate(self.theta, self.phi, self.lam), None),
|
||||
(Measure(), None),
|
||||
(IfElseOp, None),
|
||||
(ForLoopOp, None),
|
||||
(WhileLoopOp, None),
|
||||
]
|
||||
self.assertEqual(ideal_sim_expected, self.target_global_gates_only.instructions)
|
||||
|
||||
def test_instruction_supported(self):
|
||||
self.assertTrue(self.aqt_target.instruction_supported("r", (0,)))
|
||||
self.assertFalse(self.aqt_target.instruction_supported("cx", (0, 1)))
|
||||
self.assertTrue(self.target_global_gates_only.instruction_supported("cx", (0, 1)))
|
||||
self.assertFalse(self.target_global_gates_only.instruction_supported("cx", (0, 524)))
|
||||
self.assertFalse(self.target_global_gates_only.instruction_supported("cx", (0, 1, 2)))
|
||||
self.assertTrue(self.aqt_target.instruction_supported("while_loop", (0, 1, 2, 3)))
|
||||
self.assertTrue(
|
||||
self.aqt_target.instruction_supported(operation_class=WhileLoopOp, qargs=(0, 1, 2, 3))
|
||||
)
|
||||
self.assertFalse(
|
||||
self.ibm_target.instruction_supported(
|
||||
operation_class=IfElseOp, qargs=(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
|
||||
)
|
||||
)
|
||||
self.assertFalse(
|
||||
self.ibm_target.instruction_supported(operation_class=IfElseOp, qargs=(0, 425))
|
||||
)
|
||||
self.assertFalse(self.ibm_target.instruction_supported("for_loop", qargs=(0, 425)))
|
||||
|
||||
def test_coupling_map(self):
|
||||
self.assertIsNone(self.target_global_gates_only.build_coupling_map())
|
||||
self.assertEqual(
|
||||
set(CouplingMap.from_full(5).get_edges()),
|
||||
set(self.aqt_target.build_coupling_map().get_edges()),
|
||||
)
|
||||
self.assertEqual(
|
||||
{
|
||||
(3, 4),
|
||||
(4, 3),
|
||||
(3, 1),
|
||||
(1, 3),
|
||||
(1, 2),
|
||||
(2, 1),
|
||||
(0, 1),
|
||||
(1, 0),
|
||||
},
|
||||
set(self.ibm_target.build_coupling_map().get_edges()),
|
||||
)
|
||||
|
||||
|
||||
class TestInstructionProperties(QiskitTestCase):
|
||||
def test_empty_repr(self):
|
||||
properties = InstructionProperties()
|
||||
|
|
Loading…
Reference in New Issue