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:
Jake Lishman 2023-09-07 19:29:28 +01:00 committed by GitHub
parent c5c6b11324
commit 3059193b2d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 137 additions and 90 deletions

View File

@ -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:

View File

@ -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

View File

@ -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))

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -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 = {

View File

@ -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))

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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(

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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.