mirror of https://github.com/Qiskit/qiskit.git
Add fast-path construction to `DAGCircuit` methods (#10753)
* Add fast-path construction to `DAGCircuit` methods This optimises the constructor functions `DAGCircuit.apply_operation_back`, `apply_operation_front` and `compose` by giving callers a way to skip the validity checking when passing known-good values. In most cases when we're operating on the DAG, we are certain that we're already upholding the invariants of the data structure by nature of having a `copy_empty_like` or similar, and the user should not pay a runtime price for what we should catch during testing. * Check for possibility of bits before iterating --------- Co-authored-by: John Lapeyre <jlapeyre@users.noreply.github.com>
This commit is contained in:
parent
c5c6b11324
commit
3059193b2d
|
@ -236,11 +236,11 @@ class AstInterpreter:
|
|||
cx_gate = std.CXGate()
|
||||
cx_gate.condition = self.condition
|
||||
if len(id0) > 1 and len(id1) > 1:
|
||||
self.dag.apply_operation_back(cx_gate, [id0[idx], id1[idx]], [])
|
||||
self.dag.apply_operation_back(cx_gate, [id0[idx], id1[idx]], [], check=False)
|
||||
elif len(id0) > 1:
|
||||
self.dag.apply_operation_back(cx_gate, [id0[idx], id1[0]], [])
|
||||
self.dag.apply_operation_back(cx_gate, [id0[idx], id1[0]], [], check=False)
|
||||
else:
|
||||
self.dag.apply_operation_back(cx_gate, [id0[0], id1[idx]], [])
|
||||
self.dag.apply_operation_back(cx_gate, [id0[0], id1[idx]], [], check=False)
|
||||
|
||||
def _process_measure(self, node):
|
||||
"""Process a measurement node."""
|
||||
|
@ -253,7 +253,7 @@ class AstInterpreter:
|
|||
for idx, idy in zip(id0, id1):
|
||||
meas_gate = Measure()
|
||||
meas_gate.condition = self.condition
|
||||
self.dag.apply_operation_back(meas_gate, [idx], [idy])
|
||||
self.dag.apply_operation_back(meas_gate, [idx], [idy], check=False)
|
||||
|
||||
def _process_if(self, node):
|
||||
"""Process an if node."""
|
||||
|
@ -335,14 +335,14 @@ class AstInterpreter:
|
|||
for qubit in ids:
|
||||
for j, _ in enumerate(qubit):
|
||||
qubits.append(qubit[j])
|
||||
self.dag.apply_operation_back(Barrier(len(qubits)), qubits, [])
|
||||
self.dag.apply_operation_back(Barrier(len(qubits)), qubits, [], check=False)
|
||||
|
||||
elif node.type == "reset":
|
||||
id0 = self._process_bit_id(node.children[0])
|
||||
for i, _ in enumerate(id0):
|
||||
reset = Reset()
|
||||
reset.condition = self.condition
|
||||
self.dag.apply_operation_back(reset, [id0[i]], [])
|
||||
self.dag.apply_operation_back(reset, [id0[i]], [], check=False)
|
||||
|
||||
elif node.type == "if":
|
||||
self._process_if(node)
|
||||
|
@ -399,7 +399,7 @@ class AstInterpreter:
|
|||
"""
|
||||
op = self._create_op(name, params)
|
||||
op.condition = self.condition
|
||||
self.dag.apply_operation_back(op, qargs, [])
|
||||
self.dag.apply_operation_back(op, qargs, [], check=False)
|
||||
|
||||
def _create_op(self, name, params):
|
||||
if name in self.standard_extension:
|
||||
|
|
|
@ -89,7 +89,7 @@ def circuit_to_dag(circuit, copy_operations=True, *, qubit_order=None, clbit_ord
|
|||
op = instruction.operation
|
||||
if copy_operations:
|
||||
op = copy.deepcopy(op)
|
||||
dagcircuit.apply_operation_back(op, instruction.qubits, instruction.clbits)
|
||||
dagcircuit.apply_operation_back(op, instruction.qubits, instruction.clbits, check=False)
|
||||
|
||||
dagcircuit.duration = circuit.duration
|
||||
dagcircuit.unit = circuit.unit
|
||||
|
|
|
@ -22,7 +22,6 @@ directly from the graph.
|
|||
"""
|
||||
from collections import OrderedDict, defaultdict, deque, namedtuple
|
||||
import copy
|
||||
import itertools
|
||||
import math
|
||||
from typing import Dict, Generator, Any, List
|
||||
|
||||
|
@ -569,6 +568,7 @@ class DAGCircuit:
|
|||
Returns:
|
||||
Iterable[Clbit]: the :class:`.Clbit`\\ s involved.
|
||||
"""
|
||||
# If updating this, also update the fast-path checker `DAGCirucit._operation_may_have_bits`.
|
||||
if (condition := getattr(operation, "condition", None)) is not None:
|
||||
yield from condition_resources(condition).clbits
|
||||
if isinstance(operation, SwitchCaseOp):
|
||||
|
@ -580,6 +580,22 @@ class DAGCircuit:
|
|||
else:
|
||||
yield from node_resources(target).clbits
|
||||
|
||||
@staticmethod
|
||||
def _operation_may_have_bits(operation) -> bool:
|
||||
"""Return whether a given :class:`.Operation` may contain any :class:`.Clbit` instances
|
||||
in itself (e.g. a control-flow operation).
|
||||
|
||||
Args:
|
||||
operation (qiskit.circuit.Operation): the operation to check.
|
||||
"""
|
||||
# This is separate to `_bits_in_operation` because most of the time there won't be any bits,
|
||||
# so we want a fast path to be able to skip creating and testing a generator for emptiness.
|
||||
#
|
||||
# If updating this, also update `DAGCirucit._bits_in_operation`.
|
||||
return getattr(operation, "condition", None) is not None or isinstance(
|
||||
operation, SwitchCaseOp
|
||||
)
|
||||
|
||||
def _increment_op(self, op):
|
||||
if op.name in self._op_names:
|
||||
self._op_names[op.name] += 1
|
||||
|
@ -592,23 +608,6 @@ class DAGCircuit:
|
|||
else:
|
||||
self._op_names[op.name] -= 1
|
||||
|
||||
def _add_op_node(self, op, qargs, cargs):
|
||||
"""Add a new operation node to the graph and assign properties.
|
||||
|
||||
Args:
|
||||
op (qiskit.circuit.Operation): the operation associated with the DAG node
|
||||
qargs (list[Qubit]): list of quantum wires to attach to.
|
||||
cargs (list[Clbit]): list of classical wires to attach to.
|
||||
Returns:
|
||||
int: The integer node index for the new op node on the DAG
|
||||
"""
|
||||
# Add a new operation node to the graph
|
||||
new_node = DAGOpNode(op=op, qargs=qargs, cargs=cargs, dag=self)
|
||||
node_index = self._multi_graph.add_node(new_node)
|
||||
new_node._node_id = node_index
|
||||
self._increment_op(op)
|
||||
return node_index
|
||||
|
||||
@deprecate_func(
|
||||
additional_msg="Instead, use :meth:`~copy_empty_like()`, which acts identically.",
|
||||
since="0.20.0",
|
||||
|
@ -647,13 +646,18 @@ class DAGCircuit:
|
|||
|
||||
return target_dag
|
||||
|
||||
def apply_operation_back(self, op, qargs=(), cargs=()):
|
||||
def apply_operation_back(self, op, qargs=(), cargs=(), *, check=True):
|
||||
"""Apply an operation to the output of the circuit.
|
||||
|
||||
Args:
|
||||
op (qiskit.circuit.Operation): the operation associated with the DAG node
|
||||
qargs (tuple[Qubit]): qubits that op will be applied to
|
||||
cargs (tuple[Clbit]): cbits that op will be applied to
|
||||
check (bool): If ``True`` (default), this function will enforce that the
|
||||
:class:`.DAGCircuit` data-structure invariants are maintained (all ``qargs`` are
|
||||
:class:`.Qubit`\\ s, all are in the DAG, etc). If ``False``, the caller *must*
|
||||
uphold these invariants itself, but the cost of several checks will be skipped.
|
||||
This is most useful when building a new DAG from a source of known-good nodes.
|
||||
Returns:
|
||||
DAGOpNode: the node for the op that was added to the dag
|
||||
|
||||
|
@ -664,52 +668,74 @@ class DAGCircuit:
|
|||
qargs = tuple(qargs) if qargs is not None else ()
|
||||
cargs = tuple(cargs) if cargs is not None else ()
|
||||
|
||||
all_cbits = set(self._bits_in_operation(op)).union(cargs)
|
||||
if self._operation_may_have_bits(op):
|
||||
# This is the slow path; most of the time, this won't happen.
|
||||
all_cbits = set(self._bits_in_operation(op)).union(cargs)
|
||||
else:
|
||||
all_cbits = cargs
|
||||
|
||||
self._check_condition(op.name, getattr(op, "condition", None))
|
||||
self._check_bits(qargs, self.output_map)
|
||||
self._check_bits(all_cbits, self.output_map)
|
||||
if check:
|
||||
self._check_condition(op.name, getattr(op, "condition", None))
|
||||
self._check_bits(qargs, self.output_map)
|
||||
self._check_bits(all_cbits, self.output_map)
|
||||
|
||||
node_index = self._add_op_node(op, qargs, cargs)
|
||||
node = DAGOpNode(op=op, qargs=qargs, cargs=cargs, dag=self)
|
||||
node._node_id = self._multi_graph.add_node(node)
|
||||
self._increment_op(op)
|
||||
|
||||
# Add new in-edges from predecessors of the output nodes to the
|
||||
# operation node while deleting the old in-edges of the output nodes
|
||||
# and adding new edges from the operation node to each output node
|
||||
|
||||
al = [qargs, all_cbits]
|
||||
self._multi_graph.insert_node_on_in_edges_multiple(
|
||||
node_index, [self.output_map[q]._node_id for q in itertools.chain(*al)]
|
||||
node._node_id,
|
||||
[self.output_map[bit]._node_id for bits in (qargs, all_cbits) for bit in bits],
|
||||
)
|
||||
return self._multi_graph[node_index]
|
||||
return node
|
||||
|
||||
def apply_operation_front(self, op, qargs=(), cargs=()):
|
||||
def apply_operation_front(self, op, qargs=(), cargs=(), *, check=True):
|
||||
"""Apply an operation to the input of the circuit.
|
||||
|
||||
Args:
|
||||
op (qiskit.circuit.Operation): the operation associated with the DAG node
|
||||
qargs (tuple[Qubit]): qubits that op will be applied to
|
||||
cargs (tuple[Clbit]): cbits that op will be applied to
|
||||
check (bool): If ``True`` (default), this function will enforce that the
|
||||
:class:`.DAGCircuit` data-structure invariants are maintained (all ``qargs`` are
|
||||
:class:`.Qubit`\\ s, all are in the DAG, etc). If ``False``, the caller *must*
|
||||
uphold these invariants itself, but the cost of several checks will be skipped.
|
||||
This is most useful when building a new DAG from a source of known-good nodes.
|
||||
Returns:
|
||||
DAGOpNode: the node for the op that was added to the dag
|
||||
|
||||
Raises:
|
||||
DAGCircuitError: if initial nodes connected to multiple out edges
|
||||
"""
|
||||
all_cbits = set(self._bits_in_operation(op)).union(cargs)
|
||||
qargs = tuple(qargs) if qargs is not None else ()
|
||||
cargs = tuple(cargs) if cargs is not None else ()
|
||||
|
||||
self._check_condition(op.name, getattr(op, "condition", None))
|
||||
self._check_bits(qargs, self.input_map)
|
||||
self._check_bits(all_cbits, self.input_map)
|
||||
node_index = self._add_op_node(op, qargs, cargs)
|
||||
if self._operation_may_have_bits(op):
|
||||
# This is the slow path; most of the time, this won't happen.
|
||||
all_cbits = set(self._bits_in_operation(op)).union(cargs)
|
||||
else:
|
||||
all_cbits = cargs
|
||||
|
||||
if check:
|
||||
self._check_condition(op.name, getattr(op, "condition", None))
|
||||
self._check_bits(qargs, self.input_map)
|
||||
self._check_bits(all_cbits, self.input_map)
|
||||
|
||||
node = DAGOpNode(op=op, qargs=qargs, cargs=cargs, dag=self)
|
||||
node._node_id = self._multi_graph.add_node(node)
|
||||
self._increment_op(op)
|
||||
|
||||
# Add new out-edges to successors of the input nodes from the
|
||||
# operation node while deleting the old out-edges of the input nodes
|
||||
# and adding new edges to the operation node from each input node
|
||||
al = [qargs, all_cbits]
|
||||
self._multi_graph.insert_node_on_out_edges_multiple(
|
||||
node_index, [self.input_map[q]._node_id for q in itertools.chain(*al)]
|
||||
node._node_id,
|
||||
[self.input_map[bit]._node_id for bits in (qargs, all_cbits) for bit in bits],
|
||||
)
|
||||
return self._multi_graph[node_index]
|
||||
return node
|
||||
|
||||
def compose(self, other, qubits=None, clbits=None, front=False, inplace=True):
|
||||
"""Compose the ``other`` circuit onto the output of this circuit.
|
||||
|
@ -819,7 +845,7 @@ class DAGCircuit:
|
|||
op.condition = variable_mapper.map_condition(condition, allow_reorder=True)
|
||||
elif isinstance(op, SwitchCaseOp):
|
||||
op.target = variable_mapper.map_target(op.target)
|
||||
dag.apply_operation_back(op, m_qargs, m_cargs)
|
||||
dag.apply_operation_back(op, m_qargs, m_cargs, check=False)
|
||||
else:
|
||||
raise DAGCircuitError("bad node type %s" % type(nd))
|
||||
|
||||
|
@ -1191,7 +1217,7 @@ class DAGCircuit:
|
|||
node_wire_order = list(node.qargs) + list(node.cargs)
|
||||
# If we're not propagating it, the number of wires in the input DAG should include the
|
||||
# condition as well.
|
||||
if not propagate_condition:
|
||||
if not propagate_condition and self._operation_may_have_bits(node.op):
|
||||
node_wire_order += [
|
||||
bit for bit in self._bits_in_operation(node.op) if bit not in node_cargs
|
||||
]
|
||||
|
@ -1260,7 +1286,7 @@ class DAGCircuit:
|
|||
)
|
||||
new_op = copy.copy(in_node.op)
|
||||
new_op.condition = new_condition
|
||||
in_dag.apply_operation_back(new_op, in_node.qargs, in_node.cargs)
|
||||
in_dag.apply_operation_back(new_op, in_node.qargs, in_node.cargs, check=False)
|
||||
else:
|
||||
in_dag = input_dag
|
||||
|
||||
|
@ -1483,7 +1509,7 @@ class DAGCircuit:
|
|||
subgraph_is_classical = False
|
||||
if not isinstance(node, DAGOpNode):
|
||||
continue
|
||||
new_dag.apply_operation_back(node.op, node.qargs, node.cargs)
|
||||
new_dag.apply_operation_back(node.op, node.qargs, node.cargs, check=False)
|
||||
|
||||
# Ignore DAGs created for empty clbits
|
||||
if not subgraph_is_classical:
|
||||
|
@ -1801,7 +1827,7 @@ class DAGCircuit:
|
|||
|
||||
for node in op_nodes:
|
||||
# this creates new DAGOpNodes in the new_layer
|
||||
new_layer.apply_operation_back(node.op, node.qargs, node.cargs)
|
||||
new_layer.apply_operation_back(node.op, node.qargs, node.cargs, check=False)
|
||||
|
||||
# The quantum registers that have an operation in this layer.
|
||||
support_list = [
|
||||
|
@ -1829,7 +1855,7 @@ class DAGCircuit:
|
|||
cargs = copy.copy(next_node.cargs)
|
||||
|
||||
# Add node to new_layer
|
||||
new_layer.apply_operation_back(op, qargs, cargs)
|
||||
new_layer.apply_operation_back(op, qargs, cargs, check=False)
|
||||
# Add operation to partition
|
||||
if not getattr(next_node.op, "_directive", False):
|
||||
support_list.append(list(qargs))
|
||||
|
|
|
@ -167,7 +167,7 @@ class OneQubitEulerDecomposer:
|
|||
else:
|
||||
gate = gate_entry
|
||||
|
||||
dag.apply_operation_back(gate, [qr[0]])
|
||||
dag.apply_operation_back(gate, (qr[0],), check=False)
|
||||
return dag
|
||||
else:
|
||||
circuit = QuantumCircuit(qr, global_phase=global_phase)
|
||||
|
|
|
@ -114,18 +114,18 @@ class GateSequence:
|
|||
"""
|
||||
from qiskit.dagcircuit import DAGCircuit
|
||||
|
||||
qreg = [Qubit()]
|
||||
qreg = (Qubit(),)
|
||||
dag = DAGCircuit()
|
||||
dag.add_qubits(qreg)
|
||||
|
||||
if len(self.gates) == 0 and not np.allclose(self.product, np.identity(3)):
|
||||
su2 = _convert_so3_to_su2(self.product)
|
||||
dag.apply_operation_back(UnitaryGate(su2), qreg)
|
||||
dag.apply_operation_back(UnitaryGate(su2), qreg, check=False)
|
||||
return dag
|
||||
|
||||
dag.global_phase = self.global_phase
|
||||
for gate in self.gates:
|
||||
dag.apply_operation_back(gate, qreg)
|
||||
dag.apply_operation_back(gate, qreg, check=False)
|
||||
|
||||
return dag
|
||||
|
||||
|
|
|
@ -573,7 +573,7 @@ def _compose_transforms(basis_transforms, source_basis, source_dag):
|
|||
dag = DAGCircuit()
|
||||
qr = QuantumRegister(gate_num_qubits)
|
||||
dag.add_qreg(qr)
|
||||
dag.apply_operation_back(placeholder_gate, qr[:], [])
|
||||
dag.apply_operation_back(placeholder_gate, qr, (), check=False)
|
||||
mapped_instrs[gate_name, gate_num_qubits] = placeholder_params, dag
|
||||
|
||||
for gate_name, gate_num_qubits, equiv_params, equiv in basis_transforms:
|
||||
|
|
|
@ -172,6 +172,6 @@ def _instruction_to_dag(op: Instruction) -> DAGCircuit:
|
|||
dag = DAGCircuit()
|
||||
dag.add_qubits([Qubit() for _ in range(op.num_qubits)])
|
||||
dag.add_qubits([Clbit() for _ in range(op.num_clbits)])
|
||||
dag.apply_operation_back(op, dag.qubits, dag.clbits)
|
||||
dag.apply_operation_back(op, dag.qubits, dag.clbits, check=False)
|
||||
|
||||
return dag
|
||||
|
|
|
@ -74,7 +74,7 @@ class ApplyLayout(TransformationPass):
|
|||
virtual_phsyical_map = layout.get_virtual_bits()
|
||||
for node in dag.topological_op_nodes():
|
||||
qargs = [q[virtual_phsyical_map[qarg]] for qarg in node.qargs]
|
||||
new_dag.apply_operation_back(node.op, qargs, node.cargs)
|
||||
new_dag.apply_operation_back(node.op, qargs, node.cargs, check=False)
|
||||
else:
|
||||
# First build a new layout object going from:
|
||||
# old virtual -> old phsyical -> new virtual -> new physical
|
||||
|
@ -94,7 +94,7 @@ class ApplyLayout(TransformationPass):
|
|||
# Apply new layout to the circuit
|
||||
for node in dag.topological_op_nodes():
|
||||
qargs = [q[new_virtual_to_physical[qarg]] for qarg in node.qargs]
|
||||
new_dag.apply_operation_back(node.op, qargs, node.cargs)
|
||||
new_dag.apply_operation_back(node.op, qargs, node.cargs, check=False)
|
||||
self.property_set["layout"] = full_layout
|
||||
if (final_layout := self.property_set["final_layout"]) is not None:
|
||||
final_layout_mapping = {
|
||||
|
|
|
@ -112,7 +112,9 @@ def split_barriers(dag: DAGCircuit):
|
|||
split_dag.add_qubits([Qubit() for _ in range(num_qubits)])
|
||||
for i in range(num_qubits):
|
||||
split_dag.apply_operation_back(
|
||||
Barrier(1, label=barrier_uuid), qargs=[split_dag.qubits[i]]
|
||||
Barrier(1, label=barrier_uuid),
|
||||
qargs=(split_dag.qubits[i],),
|
||||
check=False,
|
||||
)
|
||||
dag.substitute_node_with_dag(node, split_dag)
|
||||
|
||||
|
@ -191,7 +193,7 @@ def separate_dag(dag: DAGCircuit) -> List[DAGCircuit]:
|
|||
new_dag.global_phase = 0
|
||||
for node in dag.topological_op_nodes():
|
||||
if dag_qubits.issuperset(node.qargs):
|
||||
new_dag.apply_operation_back(node.op, node.qargs, node.cargs)
|
||||
new_dag.apply_operation_back(node.op, node.qargs, node.cargs, check=False)
|
||||
idle_clbits = []
|
||||
for bit, node in new_dag.input_map.items():
|
||||
succ_node = next(new_dag.successors(node))
|
||||
|
|
|
@ -140,13 +140,13 @@ class Optimize1qGatesDecomposition(TransformationPass):
|
|||
return best_synth_circuit
|
||||
|
||||
def _gate_sequence_to_dag(self, best_synth_circuit):
|
||||
qubits = [Qubit()]
|
||||
qubits = (Qubit(),)
|
||||
out_dag = DAGCircuit()
|
||||
out_dag.add_qubits(qubits)
|
||||
out_dag.global_phase = best_synth_circuit.global_phase
|
||||
|
||||
for gate_name, angles in best_synth_circuit:
|
||||
out_dag.apply_operation_back(NAME_MAP[gate_name](*angles), qubits)
|
||||
out_dag.apply_operation_back(NAME_MAP[gate_name](*angles), qubits, check=False)
|
||||
return out_dag
|
||||
|
||||
def _substitution_checks(self, dag, old_run, new_circ, basis, qubit):
|
||||
|
|
|
@ -64,7 +64,7 @@ class OptimizeSwapBeforeMeasure(TransformationPass):
|
|||
old_measure_qarg = successor.qargs[0]
|
||||
new_measure_qarg = swap_qargs[swap_qargs.index(old_measure_qarg) - 1]
|
||||
measure_layer.apply_operation_back(
|
||||
Measure(), [new_measure_qarg], [successor.cargs[0]]
|
||||
Measure(), (new_measure_qarg,), (successor.cargs[0],), check=False
|
||||
)
|
||||
dag.compose(measure_layer)
|
||||
dag.remove_op_node(swap)
|
||||
|
|
|
@ -98,6 +98,6 @@ def permutation_circuit(swaps: Iterable[list[Swap[_V]]]) -> PermutationCircuit:
|
|||
# Apply swaps to the circuit.
|
||||
for swap_step in swap_list:
|
||||
for swap0, swap1 in swap_step:
|
||||
dag.apply_operation_back(SwapGate(), [inputmap[swap0], inputmap[swap1]])
|
||||
dag.apply_operation_back(SwapGate(), (inputmap[swap0], inputmap[swap1]), check=False)
|
||||
|
||||
return PermutationCircuit(dag, inputmap)
|
||||
|
|
|
@ -100,7 +100,7 @@ class BasicSwap(TransformationPass):
|
|||
|
||||
# create the swap operation
|
||||
swap_layer.apply_operation_back(
|
||||
SwapGate(), qargs=[qubit_1, qubit_2], cargs=[]
|
||||
SwapGate(), (qubit_1, qubit_2), cargs=(), check=False
|
||||
)
|
||||
|
||||
# layer insertion
|
||||
|
|
|
@ -176,7 +176,7 @@ class LookaheadSwap(TransformationPass):
|
|||
# Preserve input DAG's name, regs, wire_map, etc. but replace the graph.
|
||||
mapped_dag = dag.copy_empty_like()
|
||||
for node in mapped_gates:
|
||||
mapped_dag.apply_operation_back(op=node.op, qargs=node.qargs, cargs=node.cargs)
|
||||
mapped_dag.apply_operation_back(node.op, node.qargs, node.cargs, check=False)
|
||||
|
||||
return mapped_dag
|
||||
|
||||
|
|
|
@ -364,7 +364,7 @@ def _apply_sabre_result(
|
|||
physical_qubits[layout.virtual_to_physical(b)],
|
||||
)
|
||||
layout.swap_virtual(a, b)
|
||||
dest_dag.apply_operation_back(SwapGate(), qubits, ())
|
||||
dest_dag.apply_operation_back(SwapGate(), qubits, (), check=False)
|
||||
|
||||
def recurse(dest_dag, source_dag, result, root_logical_map, layout):
|
||||
"""The main recursive worker. Mutates ``dest_dag`` and ``layout`` and returns them.
|
||||
|
@ -384,6 +384,7 @@ def _apply_sabre_result(
|
|||
for q in node.qargs
|
||||
],
|
||||
node.cargs,
|
||||
check=False,
|
||||
)
|
||||
continue
|
||||
|
||||
|
@ -419,7 +420,7 @@ def _apply_sabre_result(
|
|||
# Apply the control flow gate to the dag.
|
||||
mapped_node = node.op.replace_blocks(mapped_blocks)
|
||||
mapped_node_qargs = mapped_blocks[0].qubits if mapped_blocks else ()
|
||||
dest_dag.apply_operation_back(mapped_node, mapped_node_qargs, node.cargs)
|
||||
dest_dag.apply_operation_back(mapped_node, mapped_node_qargs, node.cargs, check=False)
|
||||
return dest_dag, layout
|
||||
|
||||
root_logical_map = {bit: index for index, bit in enumerate(in_dag.qubits)}
|
||||
|
|
|
@ -240,7 +240,7 @@ class StochasticSwap(TransformationPass):
|
|||
for idx in range(len(edges) // 2):
|
||||
swap_src = self._int_to_qubit[edges[2 * idx]]
|
||||
swap_tgt = self._int_to_qubit[edges[2 * idx + 1]]
|
||||
trial_circuit.apply_operation_back(SwapGate(), [swap_src, swap_tgt], [])
|
||||
trial_circuit.apply_operation_back(SwapGate(), (swap_src, swap_tgt), (), check=False)
|
||||
best_circuit = trial_circuit
|
||||
|
||||
# Otherwise, we return our result for this layer
|
||||
|
@ -445,7 +445,7 @@ class StochasticSwap(TransformationPass):
|
|||
|
||||
new_op = node.op.replace_blocks(block_circuits)
|
||||
new_qargs = block_circuits[0].qubits
|
||||
dagcircuit_output.apply_operation_back(new_op, new_qargs, node.cargs)
|
||||
dagcircuit_output.apply_operation_back(new_op, new_qargs, node.cargs, check=False)
|
||||
return final_layout
|
||||
|
||||
def _new_seed(self):
|
||||
|
@ -502,7 +502,10 @@ def _dag_from_block(block, node, root_dag):
|
|||
# mapping when required, so we use that with a dummy block.
|
||||
out.add_clbits(node.cargs)
|
||||
dummy = out.apply_operation_back(
|
||||
Instruction("dummy", len(node.qargs), len(node.cargs), []), node.qargs, node.cargs
|
||||
Instruction("dummy", len(node.qargs), len(node.cargs), []),
|
||||
node.qargs,
|
||||
node.cargs,
|
||||
check=False,
|
||||
)
|
||||
wire_map = dict(itertools.chain(zip(block.qubits, node.qargs), zip(block.clbits, node.cargs)))
|
||||
out.substitute_node_with_dag(dummy, circuit_to_dag(block), wires=wire_map)
|
||||
|
|
|
@ -131,10 +131,10 @@ class ALAPSchedule(BaseSchedulerTransform):
|
|||
for bit in node.qargs:
|
||||
delta = t0 - idle_before[bit]
|
||||
if delta > 0 and self._delay_supported(dag.find_bit(bit).index):
|
||||
new_dag.apply_operation_front(Delay(delta, time_unit), [bit], [])
|
||||
new_dag.apply_operation_front(Delay(delta, time_unit), [bit], [], check=False)
|
||||
idle_before[bit] = t1
|
||||
|
||||
new_dag.apply_operation_front(node.op, node.qargs, node.cargs)
|
||||
new_dag.apply_operation_front(node.op, node.qargs, node.cargs, check=False)
|
||||
|
||||
circuit_duration = max(idle_before.values())
|
||||
for bit, before in idle_before.items():
|
||||
|
@ -142,7 +142,7 @@ class ALAPSchedule(BaseSchedulerTransform):
|
|||
if not (delta > 0 and isinstance(bit, Qubit)):
|
||||
continue
|
||||
if self._delay_supported(dag.find_bit(bit).index):
|
||||
new_dag.apply_operation_front(Delay(delta, time_unit), [bit], [])
|
||||
new_dag.apply_operation_front(Delay(delta, time_unit), [bit], [], check=False)
|
||||
|
||||
new_dag.name = dag.name
|
||||
new_dag.metadata = dag.metadata
|
||||
|
|
|
@ -156,7 +156,7 @@ class AlignMeasures(TransformationPass):
|
|||
for q in qubits:
|
||||
if qubit_stop_times[q] < until:
|
||||
idle_duration = until - qubit_stop_times[q]
|
||||
new_dag.apply_operation_back(Delay(idle_duration, unit), [q])
|
||||
new_dag.apply_operation_back(Delay(idle_duration, unit), (q,), check=False)
|
||||
|
||||
for node in dag.topological_op_nodes():
|
||||
# choose appropriate clbit available time depending on op
|
||||
|
@ -181,7 +181,7 @@ class AlignMeasures(TransformationPass):
|
|||
|
||||
if not isinstance(node.op, Delay): # exclude delays for combining consecutive delays
|
||||
pad_with_delays(node.qargs, until=start_time, unit=time_unit)
|
||||
new_dag.apply_operation_back(node.op, node.qargs, node.cargs)
|
||||
new_dag.apply_operation_back(node.op, node.qargs, node.cargs, check=False)
|
||||
|
||||
stop_time = start_time + node.op.duration
|
||||
# update time table
|
||||
|
|
|
@ -201,26 +201,26 @@ class DynamicalDecoupling(TransformationPass):
|
|||
|
||||
for nd in dag.topological_op_nodes():
|
||||
if not isinstance(nd.op, Delay):
|
||||
new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)
|
||||
new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs, check=False)
|
||||
continue
|
||||
|
||||
dag_qubit = nd.qargs[0]
|
||||
physical_qubit = dag.find_bit(dag_qubit).index
|
||||
if physical_qubit not in self._qubits: # skip unwanted qubits
|
||||
new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)
|
||||
new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs, check=False)
|
||||
continue
|
||||
|
||||
pred = next(dag.predecessors(nd))
|
||||
succ = next(dag.successors(nd))
|
||||
if self._skip_reset_qubits: # discount initial delays
|
||||
if isinstance(pred, DAGInNode) or isinstance(pred.op, Reset):
|
||||
new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)
|
||||
new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs, check=False)
|
||||
continue
|
||||
|
||||
dd_sequence_duration = index_sequence_duration_map[physical_qubit]
|
||||
slack = nd.op.duration - dd_sequence_duration
|
||||
if slack <= 0: # dd doesn't fit
|
||||
new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)
|
||||
new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs, check=False)
|
||||
continue
|
||||
|
||||
if num_pulses == 1: # special case of using a single gate for DD
|
||||
|
@ -242,7 +242,7 @@ class DynamicalDecoupling(TransformationPass):
|
|||
sequence_gphase += phase
|
||||
# don't do anything if there's no single-qubit gate to absorb the inverse
|
||||
else:
|
||||
new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)
|
||||
new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs, check=False)
|
||||
continue
|
||||
|
||||
# insert the actual DD sequence
|
||||
|
@ -253,9 +253,9 @@ class DynamicalDecoupling(TransformationPass):
|
|||
|
||||
for tau, gate in itertools.zip_longest(taus, self._dd_sequence):
|
||||
if tau > 0:
|
||||
new_dag.apply_operation_back(Delay(tau), [dag_qubit])
|
||||
new_dag.apply_operation_back(Delay(tau), [dag_qubit], check=False)
|
||||
if gate is not None:
|
||||
new_dag.apply_operation_back(gate, [dag_qubit])
|
||||
new_dag.apply_operation_back(gate, [dag_qubit], check=False)
|
||||
|
||||
new_dag.global_phase = _mod_2pi(new_dag.global_phase + sequence_gphase)
|
||||
|
||||
|
|
|
@ -216,7 +216,7 @@ class BasePadding(TransformationPass):
|
|||
if isinstance(clbits, Clbit):
|
||||
clbits = [clbits]
|
||||
|
||||
new_node = dag.apply_operation_back(oper, qargs=qubits, cargs=clbits)
|
||||
new_node = dag.apply_operation_back(oper, qargs=qubits, cargs=clbits, check=False)
|
||||
self.property_set["node_start_time"][new_node] = t_start
|
||||
|
||||
def _pad(
|
||||
|
|
|
@ -63,7 +63,9 @@ class BarrierBeforeFinalMeasurements(TransformationPass):
|
|||
# from an unmeasured qubit after a measure.
|
||||
final_qubits = dag.qubits
|
||||
|
||||
barrier_layer.apply_operation_back(Barrier(len(final_qubits)), list(final_qubits), [])
|
||||
barrier_layer.apply_operation_back(
|
||||
Barrier(len(final_qubits)), final_qubits, (), check=False
|
||||
)
|
||||
|
||||
# Preserve order of final ops collected earlier from the original DAG.
|
||||
ordered_final_nodes = [
|
||||
|
@ -72,7 +74,9 @@ class BarrierBeforeFinalMeasurements(TransformationPass):
|
|||
|
||||
# Move final ops to the new layer and append the new layer to the DAG.
|
||||
for final_node in ordered_final_nodes:
|
||||
barrier_layer.apply_operation_back(final_node.op, final_node.qargs, final_node.cargs)
|
||||
barrier_layer.apply_operation_back(
|
||||
final_node.op, final_node.qargs, final_node.cargs, check=False
|
||||
)
|
||||
|
||||
for final_op in final_ops:
|
||||
dag.remove_op_node(final_op)
|
||||
|
|
|
@ -74,7 +74,10 @@ class ConvertConditionsToIfOps(TransformationPass):
|
|||
if isinstance(target, ClassicalRegister):
|
||||
replacement.add_creg(target)
|
||||
replacement.apply_operation_back(
|
||||
IfElseOp((target, value), block_body), block_body.qubits, block_body.clbits
|
||||
IfElseOp((target, value), block_body),
|
||||
block_body.qubits,
|
||||
block_body.clbits,
|
||||
check=False,
|
||||
)
|
||||
wire_map = {bit: bit for bit in block_body.qubits + block_body.clbits}
|
||||
dag.substitute_node_with_dag(node, replacement, wire_map, propagate_condition=False)
|
||||
|
|
|
@ -79,11 +79,11 @@ class MergeAdjacentBarriers(TransformationPass):
|
|||
qubits = node_to_barrier_qubits[node]
|
||||
# qubits are stored as a set, need to convert to a list
|
||||
new_dag.apply_operation_back(
|
||||
Barrier(len(qubits)), qargs=sorted(qubits, key=indices.get)
|
||||
Barrier(len(qubits)), qargs=sorted(qubits, key=indices.get), check=False
|
||||
)
|
||||
else:
|
||||
# copy the condition over too
|
||||
new_dag.apply_operation_back(node.op, qargs=node.qargs, cargs=node.cargs)
|
||||
new_dag.apply_operation_back(node.op, node.qargs, node.cargs, check=False)
|
||||
return new_dag
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
The :class:`.DAGCircuit` methods :meth:`~.DAGCircuit.apply_operation_back` and
|
||||
:meth:`~.DAGCircuit.apply_operation_front` have gained a ``check`` keyword argument that can be
|
||||
set ``False`` to skip validation that the inputs uphold the :class:`.DAGCircuit` data-structure
|
||||
invariants. This is useful as a performance optimisation when the DAG is being built from
|
||||
known-good data, such as during transpiler passes.
|
Loading…
Reference in New Issue