diff --git a/dace/memlet.py b/dace/memlet.py index e7f0699eb8..d50c6c77f7 100644 --- a/dace/memlet.py +++ b/dace/memlet.py @@ -7,6 +7,7 @@ import warnings import dace +from dace.sdfg.graph import generate_element_id import dace.serialize from dace import subsets, dtypes, symbolic from dace.frontend.operations import detect_reduction_type @@ -54,6 +55,8 @@ class Memlet(object): '(non-atomic) writes in resulting code') allow_oob = Property(dtype=bool, default=False, desc='Bypass out-of-bounds validation') + guid = Property(dtype=str, allow_none=False) + def __init__(self, expr: Optional[str] = None, data: Optional[str] = None, @@ -137,6 +140,9 @@ def __init__(self, self.debuginfo = debuginfo self.allow_oob = allow_oob + self.guid = generate_element_id(self) + + @staticmethod def from_memlet(memlet: 'Memlet') -> 'Memlet': sbs = subsets.Range(memlet.subset.ndrange()) if memlet.subset is not None else None @@ -207,6 +213,8 @@ def __deepcopy__(self, memo): node._allow_oob = self._allow_oob node._is_data_src = self._is_data_src + node._guid = generate_element_id(node) + # Nullify graph references node._sdfg = None node._state = None diff --git a/dace/sdfg/graph.py b/dace/sdfg/graph.py index 567e5e84d2..778027f663 100644 --- a/dace/sdfg/graph.py +++ b/dace/sdfg/graph.py @@ -3,6 +3,7 @@ from collections import deque, OrderedDict import itertools +import uuid import networkx as nx from dace.dtypes import deduplicate import dace.serialize @@ -825,3 +826,7 @@ def edges_between(self, source: NodeT, destination: NodeT) -> List[MultiConnecto def is_multigraph(self) -> bool: return True + + +def generate_element_id(element) -> str: + return str(uuid.uuid4()) diff --git a/dace/sdfg/nodes.py b/dace/sdfg/nodes.py index 94d7640976..25030b595d 100644 --- a/dace/sdfg/nodes.py +++ b/dace/sdfg/nodes.py @@ -35,6 +35,7 @@ class Node(object): out_connectors = DictProperty(key_type=str, value_type=dtypes.typeclass, desc="A set of output connectors for this node.") + guid = Property(dtype=str, allow_none=False) def __init__(self, in_connectors=None, out_connectors=None): # Convert connectors to typed connectors with autodetect type @@ -46,6 +47,8 @@ def __init__(self, in_connectors=None, out_connectors=None): self.in_connectors = in_connectors or {} self.out_connectors = out_connectors or {} + self.guid = graph.generate_element_id(self) + def __str__(self): if hasattr(self, 'label'): return self.label @@ -253,6 +256,9 @@ def __deepcopy__(self, memo): node._in_connectors = dcpy(self._in_connectors, memo=memo) node._out_connectors = dcpy(self._out_connectors, memo=memo) node._debuginfo = dcpy(self._debuginfo, memo=memo) + + node._guid = graph.generate_element_id(node) + return node @property @@ -574,6 +580,9 @@ def __deepcopy__(self, memo): result = cls.__new__(cls) memo[id(self)] = result for k, v in self.__dict__.items(): + # Skip GUID. + if k in ('guid',): + continue setattr(result, k, dcpy(v, memo)) if result._sdfg is not None: result._sdfg.parent_nsdfg_node = result diff --git a/dace/sdfg/sdfg.py b/dace/sdfg/sdfg.py index 3e5f58a413..84d7189ebd 100644 --- a/dace/sdfg/sdfg.py +++ b/dace/sdfg/sdfg.py @@ -15,6 +15,7 @@ import warnings import dace +from dace.sdfg.graph import generate_element_id import dace.serialize from dace import (data as dt, hooks, memlet as mm, subsets as sbs, dtypes, symbolic) from dace.sdfg.replace import replace_properties_dict @@ -173,6 +174,7 @@ class InterstateEdge(object): assignments = Property(dtype=dict, desc="Assignments to perform upon transition (e.g., 'x=x+1; y = 0')") condition = CodeProperty(desc="Transition condition", default=CodeBlock("1")) + guid = Property(dtype=str, allow_none=False) def __init__(self, condition: Optional[Union[CodeBlock, str, ast.AST, list]] = None, @@ -195,6 +197,8 @@ def __init__(self, self._cond_sympy = None self._uncond = None + self.guid = generate_element_id(self) + def __setattr__(self, name: str, value: Any) -> None: if name == 'condition' or name == '_condition': super().__setattr__('_cond_sympy', None) @@ -512,9 +516,9 @@ def __deepcopy__(self, memo): result = cls.__new__(cls) memo[id(self)] = result for k, v in self.__dict__.items(): - # Skip derivative attributes + # Skip derivative attributes and GUID if k in ('_cached_start_block', '_edges', '_nodes', '_parent', '_parent_sdfg', '_parent_nsdfg_node', - '_cfg_list', '_transformation_hist'): + '_cfg_list', '_transformation_hist', 'guid'): continue setattr(result, k, copy.deepcopy(v, memo)) # Copy edges and nodes @@ -638,7 +642,7 @@ def keyword_remover(json_obj: Any, last_keyword=""): for key, value in json_obj.items(): if (isinstance(key, str) and (key.startswith('_meta_') - or key in ['name', 'hash', 'orig_sdfg', 'transformation_hist', 'instrument'])): + or key in ['name', 'hash', 'orig_sdfg', 'transformation_hist', 'instrument', 'guid'])): keys_to_delete.append(key) else: kv_to_recurse.append((key, value)) diff --git a/dace/sdfg/state.py b/dace/sdfg/state.py index ca0d077d66..c0a283a346 100644 --- a/dace/sdfg/state.py +++ b/dace/sdfg/state.py @@ -22,7 +22,8 @@ from dace.properties import (CodeBlock, DebugInfoProperty, DictProperty, EnumProperty, Property, SubsetProperty, SymbolicProperty, CodeProperty, make_properties) from dace.sdfg import nodes as nd -from dace.sdfg.graph import MultiConnectorEdge, OrderedMultiDiConnectorGraph, SubgraphView, OrderedDiGraph, Edge +from dace.sdfg.graph import (MultiConnectorEdge, OrderedMultiDiConnectorGraph, SubgraphView, OrderedDiGraph, Edge, + generate_element_id) from dace.sdfg.propagation import propagate_memlet from dace.sdfg.validation import validate_state from dace.subsets import Range, Subset @@ -1099,6 +1100,8 @@ def replace_dict(self, @make_properties class ControlFlowBlock(BlockGraphView, abc.ABC): + guid = Property(dtype=str, allow_none=False) + is_collapsed = Property(dtype=bool, desc='Show this block as collapsed', default=False) pre_conditions = DictProperty(key_type=str, value_type=list, desc='Pre-conditions for this block') @@ -1122,6 +1125,8 @@ def __init__(self, label: str = '', sdfg: Optional['SDFG'] = None, parent: Optio self.post_conditions = {} self.invariant_conditions = {} + self.guid = generate_element_id(self) + def nodes(self): return [] @@ -1169,7 +1174,7 @@ def __deepcopy__(self, memo): result = cls.__new__(cls) memo[id(self)] = result for k, v in self.__dict__.items(): - if k in ('_parent_graph', '_sdfg'): # Skip derivative attributes + if k in ('_parent_graph', '_sdfg', 'guid'): # Skip derivative attributes and GUID continue setattr(result, k, copy.deepcopy(v, memo))