Source code for qiskit.dagcircuit.dagnode
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2019, 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.
# pylint: disable=redefined-builtin
"""Objects to represent the information at a node in the DAGCircuit."""
import warnings
from typing import Iterable
from qiskit.circuit import Qubit, Clbit
def _condition_as_indices(operation, bit_indices):
cond = getattr(operation, "condition", None)
if cond is None:
return None
bits, value = cond
indices = [bit_indices[bits]] if isinstance(bits, Clbit) else [bit_indices[x] for x in bits]
return indices, value
[docs]class DAGNode:
"""Parent class for DAGOpNode, DAGInNode, and DAGOutNode."""
__slots__ = ["_node_id"]
def __init__(self, nid=-1):
"""Create a node"""
self._node_id = nid
def __lt__(self, other):
return self._node_id < other._node_id
def __gt__(self, other):
return self._node_id > other._node_id
def __str__(self):
# TODO is this used anywhere other than in DAG drawing?
# needs to be unique as it is what pydot uses to distinguish nodes
return str(id(self))
[docs] @staticmethod
def semantic_eq(node1, node2, bit_indices1=None, bit_indices2=None):
"""
Check if DAG nodes are considered equivalent, e.g., as a node_match for nx.is_isomorphic.
Args:
node1 (DAGOpNode, DAGInNode, DAGOutNode): A node to compare.
node2 (DAGOpNode, DAGInNode, DAGOutNode): The other node to compare.
bit_indices1 (dict): Dictionary mapping Bit instances to their index
within the circuit containing node1
bit_indices2 (dict): Dictionary mapping Bit instances to their index
within the circuit containing node2
Return:
Bool: If node1 == node2
"""
if bit_indices1 is None or bit_indices2 is None:
warnings.warn(
"DAGNode.semantic_eq now expects two bit-to-circuit index "
"mappings as arguments. To ease the transition, these will be "
"pre-populated based on the values found in Bit.index and "
"Bit.register. However, this behavior is deprecated and a future "
"release will require the mappings to be provided as arguments.",
DeprecationWarning,
)
bit_indices1 = {arg: arg for arg in node1.qargs + node1.cargs}
bit_indices2 = {arg: arg for arg in node2.qargs + node2.cargs}
if isinstance(node1, DAGOpNode) and isinstance(node2, DAGOpNode):
node1_qargs = [bit_indices1[qarg] for qarg in node1.qargs]
node1_cargs = [bit_indices1[carg] for carg in node1.cargs]
node2_qargs = [bit_indices2[qarg] for qarg in node2.qargs]
node2_cargs = [bit_indices2[carg] for carg in node2.cargs]
# For barriers, qarg order is not significant so compare as sets
if node1.op.name == node2.op.name and node1.name in {"barrier", "swap"}:
return set(node1_qargs) == set(node2_qargs)
return (
node1_qargs == node2_qargs
and node1_cargs == node2_cargs
and (
_condition_as_indices(node1.op, bit_indices1)
== _condition_as_indices(node2.op, bit_indices2)
)
and node1.op == node2.op
)
if (isinstance(node1, DAGInNode) and isinstance(node2, DAGInNode)) or (
isinstance(node1, DAGOutNode) and isinstance(node2, DAGOutNode)
):
return bit_indices1.get(node1.wire, None) == bit_indices2.get(node2.wire, None)
return False
[docs]class DAGOpNode(DAGNode):
"""Object to represent an Instruction at a node in the DAGCircuit."""
__slots__ = ["op", "qargs", "cargs", "sort_key"]
def __init__(self, op, qargs: Iterable[Qubit] = (), cargs: Iterable[Clbit] = ()):
"""Create an Instruction node"""
super().__init__()
self.op = op
self.qargs = tuple(qargs)
self.cargs = tuple(cargs)
self.sort_key = str(self.qargs)
@property
def name(self):
"""Returns the Instruction name corresponding to the op for this node"""
return self.op.name
@name.setter
def name(self, new_name):
"""Sets the Instruction name corresponding to the op for this node"""
self.op.name = new_name
def __repr__(self):
"""Returns a representation of the DAGOpNode"""
return f"DAGOpNode(op={self.op}, qargs={self.qargs}, cargs={self.cargs})"
[docs]class DAGInNode(DAGNode):
"""Object to represent an incoming wire node in the DAGCircuit."""
__slots__ = ["wire", "sort_key"]
def __init__(self, wire):
"""Create an incoming node"""
super().__init__()
self.wire = wire
# TODO sort_key which is used in dagcircuit.topological_nodes
# only works as str([]) for DAGInNodes. Need to figure out why.
self.sort_key = str([])
def __repr__(self):
"""Returns a representation of the DAGInNode"""
return f"DAGInNode(wire={self.wire})"
[docs]class DAGOutNode(DAGNode):
"""Object to represent an outgoing wire node in the DAGCircuit."""
__slots__ = ["wire", "sort_key"]
def __init__(self, wire):
"""Create an outgoing node"""
super().__init__()
self.wire = wire
# TODO sort_key which is used in dagcircuit.topological_nodes
# only works as str([]) for DAGOutNodes. Need to figure out why.
self.sort_key = str([])
def __repr__(self):
"""Returns a representation of the DAGOutNode"""
return f"DAGOutNode(wire={self.wire})"