Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace DAGNode class with OpNode, InNode, and OutNode classes #6567

Merged
merged 55 commits into from
Aug 3, 2021
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
d41727e
First dagcircuit dagnode changes
enavarro51 Jun 5, 2021
35b6e30
Stage 2 OpNode, etc
enavarro51 Jun 5, 2021
fc8bf1c
Update node ops and node types
enavarro51 Jun 6, 2021
9d58862
More predecessor testing
enavarro51 Jun 6, 2021
8661860
Further testing
enavarro51 Jun 7, 2021
69ae524
Put op in OpNode and further testing of dagcircuit
enavarro51 Jun 11, 2021
be6738e
Passing dagcircuit tests
enavarro51 Jun 11, 2021
4cdc91b
Merge branch 'main' into dag_node_op_io
enavarro51 Jun 11, 2021
30a02db
Final dagcircuit cleanup
enavarro51 Jun 11, 2021
318c0b5
Transpiler mods for node.type
enavarro51 Jun 11, 2021
7d5558a
Final transpiler tests
enavarro51 Jun 12, 2021
559e9e8
Merge branch 'main' into dag_node_op_io
enavarro51 Jun 12, 2021
f8cbd63
Fix transpiler tests, change to DAGNodeP
enavarro51 Jun 12, 2021
5d28436
Fix minimal InNode and OutNode
enavarro51 Jun 13, 2021
5853cde
Doc changes
enavarro51 Jun 13, 2021
6cd9bae
Merge branch 'main' into dag_node_op_io
enavarro51 Jun 13, 2021
803eaf2
More docs
enavarro51 Jun 13, 2021
2991e4d
Opflow bug
enavarro51 Jun 14, 2021
17d7745
Finish doc changes and testing
enavarro51 Jun 14, 2021
c1f25b8
Deprecations
enavarro51 Jun 15, 2021
8937e3f
Merge branch 'main' into dag_node_op_io
enavarro51 Jun 15, 2021
c9124f5
Fix merge conflict
enavarro51 Jun 15, 2021
09dfa0a
Make DAGNode parent also
enavarro51 Jun 16, 2021
5324544
Fix _op
enavarro51 Jun 16, 2021
e4d5a9f
Final deprecations for DAGNode
enavarro51 Jun 17, 2021
de56e10
Fix semantic_eq
enavarro51 Jun 17, 2021
88a4875
Update semantic_eq op check
enavarro51 Jun 17, 2021
b99faa8
Add semantic_eq test
enavarro51 Jun 17, 2021
d555f41
Merge branch 'main' into dag_node_op_io
enavarro51 Jun 17, 2021
f6a97f3
Lint
enavarro51 Jun 17, 2021
736357f
Merge branch 'dag_node_op_io' of github.com:enavarro51/qiskit-terra i…
enavarro51 Jun 17, 2021
c985848
Merge branch 'main' into dag_node_op_io
enavarro51 Jun 17, 2021
95f3b47
Fix node.condition and reno
enavarro51 Jun 18, 2021
ac0a881
Merge branch 'dag_node_op_io' of github.com:enavarro51/qiskit-terra i…
enavarro51 Jun 18, 2021
8f87581
Cleanup doc and reno
enavarro51 Jun 20, 2021
bd94850
Merge branch 'main' into dag_node_op_io
enavarro51 Jun 20, 2021
205ac26
Put DAG in class names, cleanup isinstances, add type to new classes-…
enavarro51 Jun 24, 2021
fe1ab48
Lint
enavarro51 Jun 24, 2021
a990ded
Remove condition from apply_operation_back and front
enavarro51 Jun 24, 2021
d19e3f4
Merge branch 'main' into dag_node_op_io
enavarro51 Jun 26, 2021
72fd91f
Fix merge conflicts from 6534
enavarro51 Jun 28, 2021
acdd6c1
Fix latex conflict
enavarro51 Jun 28, 2021
be2002f
Unused import
enavarro51 Jun 28, 2021
92c3693
Fix sort_key, qargs, and cargs and add deprecation testing
enavarro51 Jun 29, 2021
20a0fd6
Merge branch 'main' into dag_node_op_io
enavarro51 Jun 29, 2021
7947061
Move deprecations to 0.19.0 and remove DAGOpNode condition deprecation
enavarro51 Jul 7, 2021
0f6b4b1
Merge branch 'dag_node_op_io' of github.com:enavarro51/qiskit-terra i…
enavarro51 Jul 7, 2021
4bef5d6
Merge branch 'main' into dag_node_op_io
enavarro51 Jul 21, 2021
d763557
Convert type to DAGOpNode and DAGInNode in bip_mapping and dynamical_…
enavarro51 Jul 21, 2021
ddbc425
Several dagcircuit changes and more release note
enavarro51 Aug 2, 2021
054c487
Simplify idle_wires
enavarro51 Aug 2, 2021
82059d2
Merge branch 'main' into dag_node_op_io
enavarro51 Aug 2, 2021
8d5a430
Fix meth
enavarro51 Aug 3, 2021
7d58994
Merge branch 'dag_node_op_io' of github.com:enavarro51/qiskit-terra i…
enavarro51 Aug 3, 2021
1a2150b
Merge branch 'main' into dag_node_op_io
enavarro51 Aug 3, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions qiskit/dagcircuit/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017.
# (C) Copyright IBM 2017, 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
Expand All @@ -24,7 +24,9 @@
:toctree: ../stubs/

DAGCircuit
DAGNode
OpNode
InNode
OutNode
kdk marked this conversation as resolved.
Show resolved Hide resolved
DAGDepNode
DAGDependency

Expand All @@ -37,7 +39,7 @@
DAGCircuitError
"""
from .dagcircuit import DAGCircuit
from .dagnode import DAGNode
from .dagnode import OpNode, InNode, OutNode
from .dagdepnode import DAGDepNode
from .exceptions import DAGCircuitError
from .dagdependency import DAGDependency
161 changes: 82 additions & 79 deletions qiskit/dagcircuit/dagcircuit.py

Large diffs are not rendered by default.

139 changes: 134 additions & 5 deletions qiskit/dagcircuit/dagnode.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2019.
# (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
Expand All @@ -12,16 +12,146 @@

# pylint: disable=redefined-builtin

"""Object to represent the information at a node in the DAGCircuit."""
"""Objects to represent the information at a node in the DAGCircuit."""

import warnings

from qiskit.exceptions import QiskitError


class DAGNodeP:
"""Parent class for OpNode, InNode, and OutNode used by DAGCircuit."""
kdk marked this conversation as resolved.
Show resolved Hide resolved

__slots__ = ["_qargs", "cargs", "sort_key", "_node_id"]

def __init__(self, qargs=None, cargs=None, nid=-1):
"""Create a node"""
self._qargs = qargs if qargs is not None else []
self.cargs = cargs if cargs is not None else []
self.sort_key = str(self._qargs)
kdk marked this conversation as resolved.
Show resolved Hide resolved
self._node_id = nid

@property
def qargs(self):
"""Returns list of Qubit, else an empty list."""
return self._qargs

@qargs.setter
def qargs(self, new_qargs):
"""Sets qargs to be the new list of qargs and updates sort_key."""
self._qargs = new_qargs
self.sort_key = str(new_qargs)

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

@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 (OpNode, InNode, or OutNode): A node to compare.
node2 (OpNode, InNode, or OutNode): 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(
"DAGNodeP.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, OpNode) and isinstance(node2, OpNode):
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 "barrier" == node1.op.name == node2.op.name:
return set(node1_qargs) == set(node2_qargs)

if node1.op.name == node2.op.name:
if node1_qargs == node2_qargs:
if node1_cargs == node2_cargs:
if node1.op.condition == node2.op.condition:
return True
elif (isinstance(node1, InNode) and isinstance(node2, InNode)) or (
isinstance(node1, OutNode) and isinstance(node2, OutNode)
):
if bit_indices1.get(node1.wire, None) == bit_indices2.get(node2.wire, None):
return True
else:
return False


class OpNode(DAGNodeP):
"""Object to represent an Instruction at a node in the DAGCircuit."""

__slots__ = ["op"]

def __init__(self, op, qargs=None, cargs=None):
"""Create an Instruction node"""
self.op = op
super().__init__(qargs, cargs)

@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


class InNode(DAGNodeP):
"""Object to represent an incoming wire node in the DAGCircuit."""

__slots__ = ["wire"]

def __init__(self, wire):
"""Create an incoming node"""
self.wire = wire
super().__init__()


class OutNode(DAGNodeP):
"""Object to represent an outgoing wire node in the DAGCircuit."""

__slots__ = ["wire"]

def __init__(self, wire):
"""Create an outgoing node"""
self.wire = wire
super().__init__()


class DAGNode:
"""Object to represent the information at a node in the DAGCircuit.

It is used as the return value from `*_nodes()` functions and can
be supplied to functions that take a node.
"""
Expand Down Expand Up @@ -66,6 +196,7 @@ def name(self):

@name.setter
def name(self, name):
"""Sets the Instruction name corresponding to the op for this node"""
if self.type and self.type == "op":
self._op.name = name

Expand Down Expand Up @@ -138,15 +269,13 @@ def __str__(self):
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 (DAGNode): A node to compare.
node2 (DAGNode): 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
"""
Expand Down
2 changes: 1 addition & 1 deletion qiskit/transpiler/passes/analysis/dag_longest_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@


class DAGLongestPath(AnalysisPass):
"""Return the longest path in a DAGcircuit as a list of DAGNodes."""
"""Return the longest path in a DAGcircuit as a list of OpNodes, InNodes, and OutNodes."""

def run(self, dag):
"""Run the DAGLongestPath pass on `dag`."""
Expand Down
4 changes: 2 additions & 2 deletions qiskit/transpiler/passes/layout/apply_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"""Transform a circuit with virtual qubits into a circuit with physical qubits."""

from qiskit.circuit import QuantumRegister
from qiskit.dagcircuit import DAGCircuit
from qiskit.dagcircuit import DAGCircuit, OpNode
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler.exceptions import TranspilerError

Expand Down Expand Up @@ -59,7 +59,7 @@ def run(self, dag):
for creg in dag.cregs.values():
new_dag.add_creg(creg)
for node in dag.topological_op_nodes():
if node.type == "op":
if isinstance(node, OpNode):
kdk marked this conversation as resolved.
Show resolved Hide resolved
qargs = [q[layout[qarg]] for qarg in node.qargs]
new_dag.apply_operation_back(node.op, qargs, node.cargs)
new_dag._global_phase = dag._global_phase
Expand Down
53 changes: 27 additions & 26 deletions qiskit/transpiler/passes/optimization/collect_2q_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from collections import defaultdict

from qiskit.circuit import Gate
from qiskit.dagcircuit import OpNode
from qiskit.transpiler.basepasses import AnalysisPass


Expand Down Expand Up @@ -57,12 +58,12 @@ def run(self, dag):
group = []
# Explore predecessors and successors of 2q gates
if ( # pylint: disable=too-many-boolean-expressions
nd.type == "op"
and isinstance(nd._op, Gate)
isinstance(nd, OpNode)
and isinstance(nd.op, Gate)
and len(nd._qargs) == 2
and not nodes_seen[nd]
and nd._op.condition is None
and not nd._op.is_parameterized()
and nd.op.condition is None
and not nd.op.is_parameterized()
):
these_qubits = set(nd._qargs)
# Explore predecessors of the 2q node
Expand All @@ -74,11 +75,11 @@ def run(self, dag):
if len(pred) == 1 and not nodes_seen[pred[0]]:
pnd = pred[0]
if (
pnd.type == "op"
and isinstance(pnd._op, Gate)
isinstance(pnd, OpNode)
and isinstance(pnd.op, Gate)
and len(pnd._qargs) <= 2
and pnd._op.condition is None
and not pnd._op.is_parameterized()
and pnd.op.condition is None
and not pnd.op.is_parameterized()
):
if (len(pnd._qargs) == 2 and set(pnd._qargs) == these_qubits) or len(
pnd._qargs
Expand Down Expand Up @@ -112,18 +113,18 @@ def run(self, dag):
# Examine each predecessor
for pnd in sorted_pred:
if (
pnd.type != "op"
or not isinstance(pnd._op, Gate)
not isinstance(pnd, OpNode)
or not isinstance(pnd.op, Gate)
or len(pnd._qargs) > 2
or pnd._op.condition is not None
or pnd._op.is_parameterized()
or pnd.op.condition is not None
or pnd.op.is_parameterized()
):
# remove any qubits that are interrupted by a gate
# e.g. a measure in the middle of the circuit
these_qubits = list(set(these_qubits) - set(pnd._qargs))
continue
# If a predecessor is a single qubit gate, add it
if len(pnd._qargs) == 1 and not pnd._op.is_parameterized():
if len(pnd._qargs) == 1 and not pnd.op.is_parameterized():
if not nodes_seen[pnd]:
group.append(pnd)
nodes_seen[pnd] = True
Expand All @@ -133,8 +134,8 @@ def run(self, dag):
pred_qubits = set(pnd._qargs)
if (
pred_qubits == these_qubits
and pnd._op.condition is None
and not pnd._op.is_parameterized()
and pnd.op.condition is None
and not pnd.op.is_parameterized()
):
# add if on same qubits
if not nodes_seen[pnd]:
Expand Down Expand Up @@ -164,11 +165,11 @@ def run(self, dag):
if len(succ) == 1 and not nodes_seen[succ[0]]:
snd = succ[0]
if (
snd.type == "op"
and isinstance(snd._op, Gate)
isinstance(snd, OpNode)
and isinstance(snd.op, Gate)
and len(snd._qargs) <= 2
and snd._op.condition is None
and not snd._op.is_parameterized()
and snd.op.condition is None
and not snd.op.is_parameterized()
):
if (len(snd._qargs) == 2 and set(snd._qargs) == these_qubits) or len(
snd._qargs
Expand Down Expand Up @@ -202,11 +203,11 @@ def run(self, dag):
# Examine each successor
for snd in sorted_succ:
if (
snd.type != "op"
or not isinstance(snd._op, Gate)
not isinstance(snd, OpNode)
or not isinstance(snd.op, Gate)
or len(snd._qargs) > 2
or snd._op.condition is not None
or snd._op.is_parameterized()
or snd.op.condition is not None
or snd.op.is_parameterized()
):
# remove qubits from consideration if interrupted
# by a gate e.g. a measure in the middle of the circuit
Expand All @@ -216,7 +217,7 @@ def run(self, dag):
# If a successor is a single qubit gate, add it
# NB as we have eliminated all gates with names not in
# good_names, this check guarantees they are single qubit
if len(snd._qargs) == 1 and not snd._op.is_parameterized():
if len(snd._qargs) == 1 and not snd.op.is_parameterized():
if not nodes_seen[snd]:
group.append(snd)
nodes_seen[snd] = True
Expand All @@ -226,8 +227,8 @@ def run(self, dag):
succ_qubits = set(snd._qargs)
if (
succ_qubits == these_qubits
and snd._op.condition is None
and not snd._op.is_parameterized()
and snd.op.condition is None
and not snd.op.is_parameterized()
):
# add if on same qubits
if not nodes_seen[snd]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.basepasses import AnalysisPass
from qiskit.quantum_info.operators import Operator
from qiskit.dagcircuit import OpNode

_CUTOFF_PRECISION = 1e-10

Expand Down Expand Up @@ -88,7 +89,7 @@ def run(self, dag):

def _commute(node1, node2, cache):

if node1.type != "op" or node2.type != "op":
if not isinstance(node1, OpNode) or not isinstance(node2, OpNode):
return False

for nd in [node1, node2]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from qiskit.transpiler.exceptions import TranspilerError
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler.passes.optimization.commutation_analysis import CommutationAnalysis
from qiskit.dagcircuit import DAGCircuit
from qiskit.dagcircuit import DAGCircuit, InNode, OutNode
from qiskit.circuit.library.standard_gates.u1 import U1Gate
from qiskit.circuit.library.standard_gates.rx import RXGate
from qiskit.circuit.library.standard_gates.p import PhaseGate
Expand Down Expand Up @@ -97,7 +97,7 @@ def run(self, dag):
wire_commutation_set = self.property_set["commutation_set"][wire]

for com_set_idx, com_set in enumerate(wire_commutation_set):
if com_set[0].type in ["in", "out"]:
if isinstance(com_set[0], (InNode, OutNode)):
continue
for node in com_set:
num_qargs = len(node.qargs)
Expand Down
Loading