From 0f08366acc5571dbbcdedc484c3418aab39133b5 Mon Sep 17 00:00:00 2001 From: Tal Ben-Nun Date: Tue, 13 Jan 2026 10:59:49 -0800 Subject: [PATCH 1/7] Attempt to make properties faster --- dace/properties.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/dace/properties.py b/dace/properties.py index 5137abc621..8dbc22b341 100644 --- a/dace/properties.py +++ b/dace/properties.py @@ -142,11 +142,6 @@ def __get__(self, obj, objtype=None) -> T: # Called on the class rather than an instance, so return the # property object itself return self - # If a custom getter is specified, use it - if self.getter: - return self.getter(obj) - if not hasattr(self, "attr_name"): - raise RuntimeError("Attribute name not set") # Otherwise look for attribute prefixed by "_" return getattr(obj, "_" + self.attr_name) From 2ff9e437457bcbc15d09f09728d02343cabe2e7f Mon Sep 17 00:00:00 2001 From: Tal Ben-Nun Date: Tue, 13 Jan 2026 22:35:20 -0800 Subject: [PATCH 2/7] Change Property from being a descriptor to an object, for which containing classes override getattr and setattr --- dace/memlet.py | 28 +++--- dace/properties.py | 234 +++++++++++++++++++++++++++++---------------- dace/sdfg/nodes.py | 23 +++-- dace/sdfg/sdfg.py | 11 ++- dace/sdfg/state.py | 4 + dace/serialize.py | 2 +- 6 files changed, 193 insertions(+), 109 deletions(-) diff --git a/dace/memlet.py b/dace/memlet.py index c9b12f8bca..5abd0c1156 100644 --- a/dace/memlet.py +++ b/dace/memlet.py @@ -207,17 +207,17 @@ def __deepcopy__(self, memo): node = object.__new__(Memlet) # Immutable objects are: Python strings, integer, boolean but also SymPy expressions, i.e. symbols. - node._volume = self._volume - node._dynamic = self._dynamic - node._subset = dcpy(self._subset, memo=memo) - node._other_subset = dcpy(self._other_subset, memo=memo) - node._data = self._data - node._wcr = dcpy(self._wcr, memo=memo) - node._wcr_nonatomic = self._wcr_nonatomic - node._debuginfo = dcpy(self._debuginfo, memo=memo) - node._wcr_nonatomic = self._wcr_nonatomic - node._allow_oob = self._allow_oob - node._guid = generate_element_id(node) + node.volume = self.volume + node.dynamic = self.dynamic + node.subset = dcpy(self.subset, memo=memo) + node.other_subset = dcpy(self.other_subset, memo=memo) + node.data = self.data + node.wcr = dcpy(self.wcr, memo=memo) + node.wcr_nonatomic = self.wcr_nonatomic + node.debuginfo = dcpy(self.debuginfo, memo=memo) + node.wcr_nonatomic = self.wcr_nonatomic + node.allow_oob = self.allow_oob + node.guid = generate_element_id(node) # TODO: Since we set the `.sdfg` and friends to `None` we should probably also set this to `None`. node._is_data_src = self._is_data_src @@ -305,7 +305,7 @@ def simple(data, else: result.volume = num_accesses else: - result.volume = result._subset.num_elements() + result.volume = result.subset.num_elements() if wcr_str is not None: if isinstance(wcr_str, ast.AST): @@ -413,10 +413,10 @@ def try_initialize(self, sdfg: 'dace.sdfg.SDFG', state: 'dace.sdfg.SDFGState', is_data_src = False is_data_dst = False if isinstance(path[0].src, AccessNode): - if path[0].src.data == self._data: + if path[0].src.data == self.data: is_data_src = True if isinstance(path[-1].dst, AccessNode): - if path[-1].dst.data == self._data: + if path[-1].dst.data == self.data: is_data_dst = True if is_data_src and is_data_dst: # In case both point to the same array, diff --git a/dace/properties.py b/dace/properties.py index 8dbc22b341..e2f118c73b 100644 --- a/dace/properties.py +++ b/dace/properties.py @@ -37,9 +37,51 @@ class PropertyError(Exception): pass +def _validate_property_value(prop, val, obj_type_name): + """ + Validate and potentially convert a value for a property. + Returns the validated/converted value. + """ + # Fail on None unless explicitly allowed + if val is None and not prop.allow_none: + raise ValueError("None not allowed for property {} in class {}".format(prop.attr_name, obj_type_name)) + + # Accept all DaCe/numpy typeclasses as Python native types + if isinstance(val, np.number): + val = val.item() + + # Edge cases for integer and float types + if isinstance(val, int) and prop.dtype == float: + val = float(val) + if isinstance(val, float) and prop.dtype == int and val == int(val): + val = int(val) + + # Check if type matches before setting + if (prop.dtype is not None and not isinstance(val, prop.dtype) and not (val is None and prop.allow_none)): + if isinstance(val, str): + raise TypeError("Received str for property {} of type {}. Use " + "from_string method of the property.".format(prop.attr_name, prop.dtype)) + raise TypeError("Invalid type \"{}\" for property {}: expected {}".format( + type(val).__name__, prop.attr_name, prop.dtype.__name__)) + + # Check choices + if prop.choices is not None \ + and isinstance(prop.choices, (list, tuple, set)) \ + and (val is not None or not prop.allow_none): + if val not in prop.choices: + raise ValueError("Value {} not present in choices: {}".format(val, prop.choices)) + + return val + + class Property(Generic[T]): """ Class implementing properties of DaCe objects that conform to strong - typing, and allow conversion to and from strings to be edited. """ + typing, and allow conversion to and from strings to be edited. + + Performance note: This class is NOT a descriptor. Property access is handled + by __getattr__/__setattr__ on the owner class, added by @make_properties. + For properties without custom getters, reads go directly to the instance dict. + """ def __init__( self, @@ -137,49 +179,22 @@ def tmp_func(self): # Handle circular import case - defer docstring generation self.__doc__ = "Object property of type %s" % type(self).__name__ - def __get__(self, obj, objtype=None) -> T: - if obj is None: - # Called on the class rather than an instance, so return the - # property object itself - return self - # Otherwise look for attribute prefixed by "_" - return getattr(obj, "_" + self.attr_name) - - def __set__(self, obj, val): - # If custom setter is specified, use it - if self.setter: - return self.setter(obj, val) - if not hasattr(self, "attr_name"): - raise RuntimeError("Attribute name not set") - # Fail on None unless explicitly allowed - if val is None and not self.allow_none: - raise ValueError("None not allowed for property {} in class {}".format(self.attr_name, type(obj).__name__)) - - # Accept all DaCe/numpy typeclasses as Python native types - if isinstance(val, np.number): - val = val.item() - - # Edge cases for integer and float types - if isinstance(val, int) and self.dtype == float: - val = float(val) - if isinstance(val, float) and self.dtype == int and val == int(val): - val = int(val) - - # Check if type matches before setting - if (self.dtype is not None and not isinstance(val, self.dtype) and not (val is None and self.allow_none)): - if isinstance(val, str): - raise TypeError("Received str for property {} of type {}. Use " - "from_string method of the property.".format(self.attr_name, self.dtype)) - raise TypeError("Invalid type \"{}\" for property {}: expected {}".format( - type(val).__name__, self.attr_name, self.dtype.__name__)) - # If the value has not yet been set, we cannot pass it to the enum - # function. Fail silently if this happens - if self.choices is not None \ - and isinstance(self.choices,(list, tuple, set)) \ - and (val is not None or not self.allow_none): - if val not in self.choices: - raise ValueError("Value {} not present in choices: {}".format(val, self.choices)) - setattr(obj, "_" + self.attr_name, val) + def set_value(self, obj, val): + """ + Validate and store a value for this property on the given object. + Subclasses can override this to add custom validation/conversion. + + For properties with getters or setters, this stores with underscore prefix so + __getattr__ is still triggered on read. + """ + val = _validate_property_value(self, val, type(obj).__name__) + if self.getter is not None or self.setter is not None: + # Properties with getters/setters: store with underscore prefix + # so __getattr__ is triggered on read + obj.__dict__['_' + self.attr_name] = val + else: + # Properties without getters/setters: store directly for fast access + obj.__dict__[self.attr_name] = val # Python Properties of this Property class @@ -291,15 +306,27 @@ def get_property_element(object_with_properties, name): def _property_generator(instance): for name, prop in type(instance).__properties__.items(): - if hasattr(instance, "_" + name): - yield prop, getattr(instance, "_" + name) + # Properties with getters or setters store with underscore prefix + if prop.getter is not None or prop.setter is not None: + if '_' + name in instance.__dict__: + yield prop, instance.__dict__['_' + name] + else: + yield prop, getattr(instance, name) else: - yield prop, getattr(instance, name) + # Properties without getters/setters store directly + if name in instance.__dict__: + yield prop, instance.__dict__[name] + else: + yield prop, getattr(instance, name) def make_properties(cls): """ A decorator for objects that adds support and checks for strongly-typed properties (which use the Property class). + + This decorator adds __getattr__ and __setattr__ methods to handle property + access. For properties without custom getters, values are stored directly + in the instance dict, making reads fast (no method call overhead). """ # Extract all Property members of the class @@ -308,6 +335,10 @@ def make_properties(cls): for name, prop in properties.items(): prop.attr_name = name prop.owner = cls + # Remove Property object from class dict for properties with getters or setters + # This allows __getattr__ to be called for these attributes + if prop.getter is not None or prop.setter is not None: + delattr(cls, name) # Grab properties from baseclass(es) own_properties = copy.copy(properties) for base in cls.__bases__: @@ -322,6 +353,59 @@ def make_properties(cls): # Add an iterator to pairs of property names and their values cls.properties = _property_generator + # Create a lookup dict for properties with getters (for fast access in __getattr__) + properties_with_getters = {name: prop for name, prop in properties.items() if prop.getter is not None} + + # Properties with setters but no getters need __getattr__ to read from _ + properties_with_setters_only = { + name: prop + for name, prop in properties.items() if prop.setter is not None and prop.getter is None + } + + # Create a lookup dict for properties with setters or validation (for __setattr__) + # All properties need validation, but we can skip __setattr__ overhead for simple cases + all_property_names = frozenset(properties.keys()) + + # Add __getattr__ to handle properties with custom getters or setters-only + # __getattr__ is only called when the attribute is NOT found in instance dict + properties_needing_getattr = {**properties_with_getters, **properties_with_setters_only} + if properties_needing_getattr: + old_getattr = getattr(cls, '__getattr__', None) + + def property_getattr(self, name): + if name in properties_with_getters: + prop = properties_with_getters[name] + return prop.getter(self) + if name in properties_with_setters_only: + # Properties with setter but no getter: read from _ + return self.__dict__.get('_' + name) + if old_getattr is not None: + return old_getattr(self, name) + raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'") + + cls.__getattr__ = property_getattr + + # Add __setattr__ to handle property validation and custom setters + old_setattr = getattr(cls, '__setattr__', None) + + def property_setattr(self, name, val): + if name in all_property_names: + prop = properties[name] + # If custom setter is specified, use it + if prop.setter is not None: + prop.setter(self, val) + return + # Use the property's set_value method (allows subclass overrides) + prop.set_value(self, val) + else: + # Not a property, use default behavior + if old_setattr is not None: + old_setattr(self, name, val) + else: + self.__dict__[name] = val + + cls.__setattr__ = property_setattr + # Grab old init. This will be brought into the closure in the below init = cls.__init__ @@ -332,7 +416,9 @@ def initialize_properties(obj, *args, **kwargs): for name, prop in own_properties.items(): # Only assign our own properties, so we don't overwrite what's been # set by the base class - if hasattr(obj, '_' + name): + # Properties with getters/setters store with underscore prefix + attr_name = '_' + name if (prop.getter is not None or prop.setter is not None) else name + if attr_name in obj.__dict__: raise PropertyError("Property {} already assigned in {}".format(name, type(obj).__name__)) if not prop.indirected: if prop.allow_none or prop.default is not None: @@ -449,12 +535,12 @@ def __init__(self, element_type: T, *args, **kwargs): super().__init__(*args, **kwargs) self.element_type = element_type - def __set__(self, obj, val): + def set_value(self, obj, val): if isinstance(val, str): val = list(map(self.element_type, list(val))) elif isinstance(val, tuple): val = list(map(self.element_type, val)) - super(ListProperty, self).__set__(obj, val) + super(ListProperty, self).set_value(obj, val) @staticmethod def to_string(l): @@ -505,8 +591,8 @@ def __init__(self, *args, **kwargs): kwargs['dtype'] = list super().__init__(*args, **kwargs) - def __set__(self, obj, val): - super(TransformationHistProperty, self).__set__(obj, val) + def set_value(self, obj, val): + super(TransformationHistProperty, self).set_value(obj, val) def to_json(self, hist): if hist is None: @@ -557,7 +643,7 @@ def __init__(self, key_type, value_type, *args, **kwargs): else: self.is_value = lambda v: False - def __set__(self, obj, val): + def set_value(self, obj, val): if isinstance(val, str): val = ast.literal_eval(val) elif isinstance(val, (tuple, list)): @@ -567,7 +653,7 @@ def __set__(self, obj, val): (k if self.is_key(k) else self.key_type(k)): (v if self.is_value(v) else self.value_type(v)) for k, v in val.items() } - super(DictProperty, self).__set__(obj, val) + super(DictProperty, self).set_value(obj, val) @staticmethod def to_string(d): @@ -691,10 +777,10 @@ def from_json(self, obj, context=None): class RangeProperty(Property): """ Custom Property type for `dace.subsets.Range` members. """ - def __set__(self, obj, value): + def set_value(self, obj, value): if isinstance(value, list): value = dace.subsets.Range(value) - super(RangeProperty, self).__set__(obj, value) + super(RangeProperty, self).set_value(obj, value) @property def dtype(self): @@ -838,17 +924,9 @@ def from_json(self, l, sdfg=None): return None return frozenset(l) - def __get__(self, obj, objtype=None): - val = super(SetProperty, self).__get__(obj, objtype) - if val is None: - return val - - # `val` is a `frozenset` (see `__set__()`) thus it is safe to return it unprotected. - return val - - def __set__(self, obj, val): + def set_value(self, obj, val): if val is None: - return super(SetProperty, self).__set__(obj, val) + return super(SetProperty, self).set_value(obj, val) # Check for uniqueness if isinstance(val, (frozenset, set)): @@ -863,7 +941,7 @@ def __set__(self, obj, val): except (TypeError, ValueError): raise ValueError('Some elements could not be converted to %s' % (str(self._element_type))) - super(SetProperty, self).__set__(obj, new_set) + super(SetProperty, self).set_value(obj, new_set) class LambdaProperty(Property): @@ -894,7 +972,7 @@ def from_json(self, s, sdfg=None): if s is None: return None return LambdaProperty.from_string(s) - def __set__(self, obj, val): + def set_value(self, obj, val): if val is not None: if isinstance(val, str): self.from_string(val) # Check that from_string doesn't fail @@ -902,7 +980,7 @@ def __set__(self, obj, val): val = self.to_string(val) # Store as string internally else: raise TypeError("Lambda property must be either string or ast.Lambda") - super(LambdaProperty, self).__set__(obj, val) + super(LambdaProperty, self).set_value(obj, val) class CodeBlock(object): @@ -1098,13 +1176,13 @@ def dtype(self): def allow_none(self): return True - def __set__(self, obj, val): + def set_value(self, obj, val): if isinstance(val, str): val = self.from_string(val) if (val is not None and not isinstance(val, sbs.Range) and not isinstance(val, sbs.Indices) and not isinstance(val, sbs.SubsetUnion)): raise TypeError("Subset property must be either Range or Indices: got {}".format(type(val).__name__)) - super(SubsetProperty, self).__set__(obj, val) + super(SubsetProperty, self).set_value(obj, val) @staticmethod def from_string(s): @@ -1145,14 +1223,14 @@ class SymbolicProperty(Property): def dtype(self): return None - def __set__(self, obj, val): + def set_value(self, obj, val): if (val is not None and not isinstance(val, (sp.Expr, Number, np.bool_, str))): raise TypeError(f"Property {self.attr_name} must be a literal " f"or symbolic expression, got: {type(val)}") if isinstance(val, (Number, str)): val = SymbolicProperty.from_string(str(val)) - super(SymbolicProperty, self).__set__(obj, val) + super(SymbolicProperty, self).set_value(obj, val) @staticmethod def from_string(s): @@ -1267,12 +1345,12 @@ def from_json(self, d, sdfg=None): return None return tuple([dace.symbolic.pystr_to_symbolic(m) for m in d]) - def __set__(self, obj, val): + def set_value(self, obj, val): if isinstance(val, list): val = tuple(val) if isinstance(val, tuple): val = tuple(dace.symbolic.UndefinedSymbol() if v == '?' else v for v in val) - super(ShapeProperty, self).__set__(obj, val) + super(ShapeProperty, self).set_value(obj, val) class TypeProperty(Property): @@ -1306,9 +1384,6 @@ class TypeClassProperty(Property): """ Custom property type for memory as defined in dace.types, e.g. `dace.float32`. """ - def __get__(self, obj, objtype=None) -> typeclass: - return super().__get__(obj, objtype) - @property def dtype(self): return typeclass @@ -1347,9 +1422,6 @@ def from_json(obj, context=None): class NestedDataClassProperty(Property): """ Custom property type for nested data. """ - def __get__(self, obj, objtype=None) -> 'dData': - return super().__get__(obj, objtype) - @property def dtype(self): from dace import data as dt diff --git a/dace/sdfg/nodes.py b/dace/sdfg/nodes.py index c790f9411d..4d4d4326dc 100644 --- a/dace/sdfg/nodes.py +++ b/dace/sdfg/nodes.py @@ -65,6 +65,8 @@ def __deepcopy__(self, memo): if k == 'guid': # Skip ID continue setattr(result, k, dcpy(v, memo)) + # Generate new guid for the copy + result.guid = graph.generate_element_id(result) return result def validate(self, sdfg, state): @@ -303,15 +305,15 @@ def from_json(json_obj, context=None): def __deepcopy__(self, memo): node = object.__new__(AccessNode) - node._data = self._data - node._setzero = self._setzero - node._instrument = self._instrument - node._instrument_condition = dcpy(self._instrument_condition, memo=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.data = self.data + node.setzero = self.setzero + node.instrument = self.instrument + node.instrument_condition = dcpy(self.instrument_condition, memo=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) + node.guid = graph.generate_element_id(node) return node @@ -656,6 +658,8 @@ def __deepcopy__(self, memo): setattr(result, k, dcpy(v, memo)) if result._sdfg is not None: result._sdfg.parent_nsdfg_node = result + # Generate new guid for the copy + result.guid = graph.generate_element_id(result) return result @staticmethod @@ -1077,8 +1081,7 @@ def __init__(self, def __str__(self): return self.label + "[" + ", ".join( - ["{}={}".format(i, r) - for i, r in zip(self._params, [sbs.Range.dim_to_string(d) for d in self._range])]) + "]" + ["{}={}".format(i, r) for i, r in zip(self.params, [sbs.Range.dim_to_string(d) for d in self.range])]) + "]" def __repr__(self): return type(self).__name__ + ' (' + self.__str__() + ')' diff --git a/dace/sdfg/sdfg.py b/dace/sdfg/sdfg.py index 5c9061a3bb..692a643e64 100644 --- a/dace/sdfg/sdfg.py +++ b/dace/sdfg/sdfg.py @@ -220,6 +220,8 @@ def __deepcopy__(self, memo): if k == 'guid': # Skip ID continue setattr(result, k, copy.deepcopy(v, memo)) + # Generate new guid for the copy + result.guid = generate_element_id(result) return result @staticmethod @@ -571,7 +573,7 @@ def __deepcopy__(self, memo): for k, v in self.__dict__.items(): # Skip derivative attributes and GUID if k in ('_cached_start_block', '_edges', '_nodes', '_parent', '_parent_sdfg', '_parent_nsdfg_node', - '_cfg_list', '_transformation_hist', 'guid'): + '_cfg_list', 'transformation_hist', 'guid'): continue setattr(result, k, copy.deepcopy(v, memo)) # Copy edges and nodes @@ -585,8 +587,8 @@ def __deepcopy__(self, memo): else: setattr(result, k, None) # Copy SDFG list and transformation history - if hasattr(self, '_transformation_hist'): - setattr(result, '_transformation_hist', copy.deepcopy(self._transformation_hist, memo)) + if hasattr(self, 'transformation_hist'): + setattr(result, 'transformation_hist', copy.deepcopy(self.transformation_hist, memo)) result._cfg_list = [] if self._parent_sdfg is None: # Avoid import loops @@ -597,6 +599,9 @@ def __deepcopy__(self, memo): if fixed: warnings.warn(f'Fixed {fixed} nested SDFG parent references during deep copy.') + # Generate new guid for the copy + result.guid = generate_element_id(result) + return result @property diff --git a/dace/sdfg/state.py b/dace/sdfg/state.py index cf64642128..4828b26a4c 100644 --- a/dace/sdfg/state.py +++ b/dace/sdfg/state.py @@ -1313,6 +1313,10 @@ def __deepcopy__(self, memo): else: setattr(result, k, None) + # Generate new guid for the copy + from dace.sdfg import graph + result.guid = graph.generate_element_id(result) + return result @property diff --git a/dace/serialize.py b/dace/serialize.py index 496f2e8cf7..21e7ea24ed 100644 --- a/dace/serialize.py +++ b/dace/serialize.py @@ -98,7 +98,7 @@ def to_json(obj): # If the object knows how to convert itself, let it. By calling the # method directly on the type, this works for both static and # non-static implementations of to_json. - return type(obj).to_json(obj) + return obj.to_json() elif type(obj) in {bool, int, float, list, dict, str}: # Some types are natively understood by JSON return obj From 6d9f58dd3e0fff579428316729d7e675cc9be038 Mon Sep 17 00:00:00 2001 From: Tal Ben-Nun Date: Wed, 14 Jan 2026 08:21:24 -0800 Subject: [PATCH 3/7] One more modification --- dace/sdfg/nodes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dace/sdfg/nodes.py b/dace/sdfg/nodes.py index 4d4d4326dc..82073672c7 100644 --- a/dace/sdfg/nodes.py +++ b/dace/sdfg/nodes.py @@ -656,8 +656,8 @@ def __deepcopy__(self, memo): if k in ('guid', ): continue setattr(result, k, dcpy(v, memo)) - if result._sdfg is not None: - result._sdfg.parent_nsdfg_node = result + if result.sdfg is not None: + result.sdfg.parent_nsdfg_node = result # Generate new guid for the copy result.guid = graph.generate_element_id(result) return result From e4e5232d5fe1ce82f42f93059f3e16aba2f35089 Mon Sep 17 00:00:00 2001 From: Tal Ben-Nun Date: Wed, 14 Jan 2026 08:27:05 -0800 Subject: [PATCH 4/7] Minor update --- dace/transformation/interstate/multistate_inline.py | 2 +- dace/transformation/interstate/sdfg_nesting.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dace/transformation/interstate/multistate_inline.py b/dace/transformation/interstate/multistate_inline.py index ecba880865..a1c1504f30 100644 --- a/dace/transformation/interstate/multistate_inline.py +++ b/dace/transformation/interstate/multistate_inline.py @@ -161,7 +161,7 @@ def apply(self, outer_state: SDFGState, sdfg: SDFG): sdfg.append_exit_code(code.code, loc) # Callbacks and other types - sdfg._callback_mapping.update(nsdfg.callback_mapping) + sdfg.callback_mapping.update(nsdfg.callback_mapping) # Environments for nstate in nsdfg.states(): diff --git a/dace/transformation/interstate/sdfg_nesting.py b/dace/transformation/interstate/sdfg_nesting.py index b90d448c56..42e5d87692 100644 --- a/dace/transformation/interstate/sdfg_nesting.py +++ b/dace/transformation/interstate/sdfg_nesting.py @@ -263,7 +263,7 @@ def apply(self, state: SDFGState, sdfg: SDFG): sdfg.append_exit_code(code.code, loc) # Callbacks and other types - sdfg._callback_mapping.update(nsdfg.callback_mapping) + sdfg.callback_mapping.update(nsdfg.callback_mapping) # Environments for node in nstate.nodes(): From a29daab6e88ab0a7613a3d430ff578475fb96739 Mon Sep 17 00:00:00 2001 From: Tal Ben-Nun Date: Wed, 14 Jan 2026 23:11:53 -0800 Subject: [PATCH 5/7] Minor fixes --- dace/frontend/python/newast.py | 2 +- dace/sdfg/nodes.py | 6 +++--- dace/sdfg/validation.py | 2 +- dace/transformation/helpers.py | 4 ++-- dace/transformation/subgraph/expansion.py | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dace/frontend/python/newast.py b/dace/frontend/python/newast.py index 724ad16a45..cb718e9a05 100644 --- a/dace/frontend/python/newast.py +++ b/dace/frontend/python/newast.py @@ -4654,7 +4654,7 @@ def parse_target(t: Union[ast.Name, ast.Subscript]): # Avoid cast of output pointers to scalars in code generation for cname in outargs: if (cname in self.sdfg.arrays and tuple(self.sdfg.arrays[cname].shape) == (1, )): - tasklet._out_connectors[f'__out_{cname}'] = dtypes.pointer(self.sdfg.arrays[cname].dtype) + tasklet.out_connectors[f'__out_{cname}'] = dtypes.pointer(self.sdfg.arrays[cname].dtype) # Setup arguments in graph for arg in dtypes.deduplicate(args): diff --git a/dace/sdfg/nodes.py b/dace/sdfg/nodes.py index 82073672c7..977208a316 100644 --- a/dace/sdfg/nodes.py +++ b/dace/sdfg/nodes.py @@ -457,7 +457,7 @@ def from_json(json_obj, context=None): @property def name(self): - return self._label + return self.label def validate(self, sdfg: 'dace.sdfg.SDFG', state: 'dace.sdfg.SDFGState'): if not dtypes.validate_name(self.label): @@ -1295,9 +1295,9 @@ def __init__(self, label, pe_tuple, condition, schedule=dtypes.ScheduleType.Defa def __str__(self): if self.condition is not None: return ("%s [%s=0:%s], Condition: %s" % - (self._label, self.pe_index, self.num_pes, CodeProperty.to_string(self.condition))) + (self.label, self.pe_index, self.num_pes, CodeProperty.to_string(self.condition))) else: - return ("%s [%s=0:%s]" % (self._label, self.pe_index, self.num_pes)) + return ("%s [%s=0:%s]" % (self.label, self.pe_index, self.num_pes)) def validate(self, sdfg, state, node): if not dtypes.validate_name(self.label): diff --git a/dace/sdfg/validation.py b/dace/sdfg/validation.py index 9434794cd5..7a6efbfc41 100644 --- a/dace/sdfg/validation.py +++ b/dace/sdfg/validation.py @@ -436,7 +436,7 @@ def validate_state(state: 'dace.sdfg.SDFGState', 'rather than using multiple references to the same one', sdfg, state_id) references.add(id(state)) - if not dtypes.validate_name(state._label): + if not dtypes.validate_name(state.label): raise InvalidSDFGError("Invalid state name", sdfg, state_id) if state.sdfg != sdfg: diff --git a/dace/transformation/helpers.py b/dace/transformation/helpers.py index bb4bd85d3f..738e975ce0 100644 --- a/dace/transformation/helpers.py +++ b/dace/transformation/helpers.py @@ -755,8 +755,8 @@ def state_fission( for second_state_boundary_node in boundary_nodes: first_state_boundary_node = first_nodes_map[second_state_boundary_node] assert second_state.in_degree(second_state_boundary_node) == 0 - first_state_boundary_node._out_connectors.clear() - second_state_boundary_node._in_connectors.clear() + first_state_boundary_node.out_connectors.clear() + second_state_boundary_node.in_connectors.clear() # Remove isolated nodes. # TODO(phimuell): Is this the best approach, it might be a bit too far reaching. diff --git a/dace/transformation/subgraph/expansion.py b/dace/transformation/subgraph/expansion.py index 73445dba9a..39042a538f 100644 --- a/dace/transformation/subgraph/expansion.py +++ b/dace/transformation/subgraph/expansion.py @@ -308,7 +308,7 @@ def expand(self, sdfg, graph, map_entries, map_base_variables=None): for edge in dynamic_edges: # Remove old edge and connector graph.remove_edge(edge) - edge.dst._in_connectors.remove(edge.dst_conn) + edge.dst.in_connectors.remove(edge.dst_conn) # Propagate to each range it belongs to path = [] From f65ff2f5bb3bb4e17bfb27fcc7698ba1fa3bcc7e Mon Sep 17 00:00:00 2001 From: Tal Ben-Nun Date: Wed, 14 Jan 2026 23:26:30 -0800 Subject: [PATCH 6/7] Remove extraneous property indirection --- dace/frontend/python/newast.py | 11 +- dace/optimization/data_layout_tuner.py | 2 +- dace/sdfg/analysis/cutout.py | 4 +- dace/sdfg/sdfg.py | 136 +++++++++++-------------- dace/sdfg/state.py | 14 +-- dace/sdfg/validation.py | 4 +- dace/transformation/helpers.py | 4 +- 7 files changed, 76 insertions(+), 99 deletions(-) diff --git a/dace/frontend/python/newast.py b/dace/frontend/python/newast.py index cb718e9a05..e60d1227e5 100644 --- a/dace/frontend/python/newast.py +++ b/dace/frontend/python/newast.py @@ -3604,11 +3604,10 @@ def _visit_assign(self, node, node_target, op, dtype=None, is_return=False): new_data, rng = None, None dtype_keys = tuple(dtypes.dtype_to_typeclass().keys()) - if not ( - result in self.sdfg.symbols or symbolic.issymbolic(result) or isinstance(result, dtype_keys) or - (isinstance(result, str) and any( - result in x - for x in [self.sdfg.arrays, self.sdfg._pgrids, self.sdfg._subarrays, self.sdfg._rdistrarrays]))): + if not (result in self.sdfg.symbols or symbolic.issymbolic(result) or isinstance(result, dtype_keys) or + (isinstance(result, str) and any( + result in x + for x in [self.sdfg.arrays, self.sdfg.pgrids, self.sdfg.subarrays, self.sdfg.rdistrarrays]))): raise DaceSyntaxError( self, node, "In assignments, the rhs may only be " "data, numerical/boolean constants " @@ -3632,7 +3631,7 @@ def _visit_assign(self, node, node_target, op, dtype=None, is_return=False): true_name, new_data = self.sdfg.add_scalar(true_name, ttype, transient=True, find_new_name=True) self.variables[name] = true_name defined_vars[name] = true_name - if any(result in x for x in [self.sdfg._pgrids, self.sdfg._rdistrarrays, self.sdfg._subarrays]): + if any(result in x for x in [self.sdfg.pgrids, self.sdfg.rdistrarrays, self.sdfg.subarrays]): # NOTE: In previous versions some `pgrid` and subgrid related replacement function, # see `dace/frontend/common/distr.py`, created dummy variables with the same name # as the entities, such as process grids, they created. Thus the frontend was diff --git a/dace/optimization/data_layout_tuner.py b/dace/optimization/data_layout_tuner.py index f45325f313..d134fee752 100644 --- a/dace/optimization/data_layout_tuner.py +++ b/dace/optimization/data_layout_tuner.py @@ -143,7 +143,7 @@ def evaluate(self, config, cutout, arguments: Dict, measurements: int, **kwargs) modified_arrays, new_arrays = config # Modify data layout prior to calling - cutout._arrays = new_arrays + cutout.arrays = new_arrays for marray in modified_arrays: arguments[marray] = dt.make_array_from_descriptor(cutout.arrays[marray], arguments[marray]) diff --git a/dace/sdfg/analysis/cutout.py b/dace/sdfg/analysis/cutout.py index ea754a2cbc..2532c063a8 100644 --- a/dace/sdfg/analysis/cutout.py +++ b/dace/sdfg/analysis/cutout.py @@ -841,8 +841,8 @@ def _create_alibi_access_node_for_edge(target_sdfg: SDFG, target_state: SDFGStat original_edge.data access_size = original_edge.data.subset.size_exact() container_name = '__cutout_' + str(original_edge.data.data) - container_name = data.find_new_name(container_name, target_sdfg._arrays.keys()) - original_array = original_sdfg._arrays[original_edge.data.data] + container_name = data.find_new_name(container_name, target_sdfg.arrays.keys()) + original_array = original_sdfg.arrays[original_edge.data.data] memlet_str = '' if original_edge.data.subset.num_elements_exact() > 1: access_size = original_edge.data.subset.size_exact() diff --git a/dace/sdfg/sdfg.py b/dace/sdfg/sdfg.py index 692a643e64..1f7d7c8417 100644 --- a/dace/sdfg/sdfg.py +++ b/dace/sdfg/sdfg.py @@ -457,10 +457,10 @@ class SDFG(ControlFlowRegion): default={}, desc='Compile-time constants. The dictionary maps between a constant name to ' 'a tuple of its type and the actual constant data.') - _arrays = Property(dtype=NestedDict, - desc="Data descriptors for this SDFG", - to_json=_arrays_to_json, - from_json=_nested_arrays_from_json) + arrays = Property(dtype=NestedDict, + desc="Data descriptors for this SDFG", + to_json=_arrays_to_json, + from_json=_nested_arrays_from_json) symbols = DictProperty(str, dtypes.typeclass, desc="Global symbols for this SDFG") instrument = EnumProperty(dtype=dtypes.InstrumentationType, @@ -482,21 +482,21 @@ class SDFG(ControlFlowRegion): debuginfo = DebugInfoProperty(allow_none=True) - _pgrids = DictProperty(str, - ProcessGrid, - desc="Process-grid descriptors for this SDFG", - to_json=_arrays_to_json, - from_json=_arrays_from_json) - _subarrays = DictProperty(str, - SubArray, - desc="Sub-array descriptors for this SDFG", - to_json=_arrays_to_json, - from_json=_arrays_from_json) - _rdistrarrays = DictProperty(str, - RedistrArray, - desc="Sub-array redistribution descriptors for this SDFG", - to_json=_arrays_to_json, - from_json=_arrays_from_json) + pgrids = DictProperty(str, + ProcessGrid, + desc="Process-grid descriptors for this SDFG", + to_json=_arrays_to_json, + from_json=_arrays_from_json) + subarrays = DictProperty(str, + SubArray, + desc="Sub-array descriptors for this SDFG", + to_json=_arrays_to_json, + from_json=_arrays_from_json) + rdistrarrays = DictProperty(str, + RedistrArray, + desc="Sub-array redistribution descriptors for this SDFG", + to_json=_arrays_to_json, + from_json=_arrays_from_json) callback_mapping = DictProperty(str, str, @@ -539,7 +539,7 @@ def __init__(self, self.symbols = {} self._parent_sdfg = None self._parent_nsdfg_node = None - self._arrays = NestedDict() # type: Dict[str, dt.Array] + self.arrays = NestedDict() # type: Dict[str, dt.Array] self.arg_names = [] self._labels: Set[str] = set() self.global_code = {'frame': CodeBlock("", dtypes.Language.CPP)} @@ -556,9 +556,9 @@ def __init__(self, self._recompile = True # Grid-distribution-related fields - self._pgrids = {} - self._subarrays = {} - self._rdistrarrays = {} + self.pgrids = {} + self.subarrays = {} + self.rdistrarrays = {} # Counter to resolve name conflicts self._orig_name = name @@ -721,32 +721,10 @@ def keyword_remover(json_obj: Any, last_keyword=""): hsh = sha256(string_representation.encode('utf-8')) return hsh.hexdigest() - @property - def arrays(self): - """ Returns a dictionary of data descriptors (`Data` objects) used - in this SDFG, with an extra `None` entry for empty memlets. - """ - return self._arrays - - @property - def process_grids(self): - """ Returns a dictionary of process-grid descriptors (`ProcessGrid` objects) used in this SDFG. """ - return self._pgrids - - @property - def subarrays(self): - """ Returns a dictionary of sub-array descriptors (`SubArray` objects) used in this SDFG. """ - return self._subarrays - - @property - def rdistrarrays(self): - """ Returns a dictionary of sub-array redistribution descriptors (`RedistrArray` objects) used in this SDFG. """ - return self._rdistrarrays - def data(self, dataname: str): """ Looks up a data descriptor from its name, which can be an array, stream, or scalar symbol. """ - if dataname in self._arrays: - return self._arrays[dataname] + if dataname in self.arrays: + return self.arrays[dataname] if str(dataname) in self.symbols: return self.symbols[str(dataname)] if dataname in self.constants_prop: @@ -795,7 +773,7 @@ def replace_dict(self, repldict_filtered = {k: v for k, v in repldict.items() if '.' not in k} for name, new_name in repldict_filtered.items(): if validate_name(new_name): - _replace_dict_keys(self._arrays, name, new_name) + _replace_dict_keys(self.arrays, name, new_name) _replace_dict_keys(self.symbols, name, new_name) _replace_dict_keys(self.constants_prop, name, new_name) _replace_dict_keys(self.callback_mapping, name, new_name) @@ -823,11 +801,11 @@ def add_symbol(self, name, stype, find_new_name: bool = False): raise FileExistsError(f'Symbol "{name}" already exists in SDFG') if name in self.arrays: raise FileExistsError(f'Cannot create symbol "{name}", the name is used by a data descriptor.') - if name in self._subarrays: + if name in self.subarrays: raise FileExistsError(f'Cannot create symbol "{name}", the name is used by a subarray.') - if name in self._rdistrarrays: + if name in self.rdistrarrays: raise FileExistsError(f'Cannot create symbol "{name}", the name is used by a RedistrArray.') - if name in self._pgrids: + if name in self.pgrids: raise FileExistsError(f'Cannot create symbol "{name}", the name is used by a ProcessGrid.') if not isinstance(stype, dtypes.typeclass): stype = dtypes.dtype_to_typeclass(stype) @@ -1193,7 +1171,7 @@ def remove_data(self, name, validate=True): """ # Verify that the data descriptor exists - if name not in self._arrays: + if name not in self.arrays: return # Verify that there are no access nodes that use this data @@ -1205,7 +1183,7 @@ def remove_data(self, name, validate=True): f"{name}: it is accessed by node " f"{node} in state {state}.") - del self._arrays[name] + del self.arrays[name] def reset_sdfg_list(self): """ @@ -1280,12 +1258,12 @@ def add_constant(self, name: str, value: Any, dtype: dt.Data = None): :param value: The constant value. :param dtype: Optional data type of the symbol, or None to deduce automatically. """ - if name in self._subarrays: - raise FileExistsError(f'Can not create constant "{name}", the name is used by a subarray.') - if name in self._rdistrarrays: - raise FileExistsError(f'Can not create constant "{name}", the name is used by a RedistrArray.') - if name in self._pgrids: - raise FileExistsError(f'Can not create constant "{name}", the name is used by a ProcessGrid.') + if name in self.subarrays: + raise FileExistsError(f'Cannot create constant "{name}", the name is used by a subarray.') + if name in self.rdistrarrays: + raise FileExistsError(f'Cannot create constant "{name}", the name is used by a RedistrArray.') + if name in self.pgrids: + raise FileExistsError(f'Cannot create constant "{name}", the name is used by a ProcessGrid.') self.constants_prop[name] = (dtype or dt.create_datadescriptor(value), value) @property @@ -1739,23 +1717,23 @@ def from_file(filename: str) -> 'SDFG': def _find_new_name(self, name: str): """ Tries to find a new name by adding an underscore and a number. """ - names = (self._arrays.keys() | self.constants_prop.keys() | self._pgrids.keys() | self._subarrays.keys() - | self._rdistrarrays.keys() | self.symbols.keys()) + names = (self.arrays.keys() | self.constants_prop.keys() | self.pgrids.keys() | self.subarrays.keys() + | self.rdistrarrays.keys() | self.symbols.keys()) return dt.find_new_name(name, names) def is_name_used(self, name: str) -> bool: """ Checks if `name` is already used inside the SDFG.""" - if name in self._arrays: + if name in self.arrays: return True if name in self.symbols: return True if name in self.constants_prop: return True - if name in self._pgrids: + if name in self.pgrids: return True - if name in self._subarrays: + if name in self.subarrays: return True - if name in self._rdistrarrays: + if name in self.rdistrarrays: return True return False @@ -2106,11 +2084,11 @@ def add_datadesc(self, name: str, datadesc: dt.Data, find_new_name=False) -> str raise FileExistsError(f'Data descriptor "{name}" already exists in SDFG') if name in self.symbols: raise FileExistsError(f'Can not create data descriptor "{name}", the name is used by a symbol.') - if name in self._subarrays: + if name in self.subarrays: raise FileExistsError(f'Can not create data descriptor "{name}", the name is used by a subarray.') - if name in self._rdistrarrays: + if name in self.rdistrarrays: raise FileExistsError(f'Can not create data descriptor "{name}", the name is used by a RedistrArray.') - if name in self._pgrids: + if name in self.pgrids: raise FileExistsError(f'Can not create data descriptor "{name}", the name is used by a ProcessGrid.') def _add_symbols(sdfg: SDFG, desc: dt.Data): @@ -2123,7 +2101,7 @@ def _add_symbols(sdfg: SDFG, desc: dt.Data): sdfg.add_symbol(sym.name, sym.dtype) # Add the data descriptor to the SDFG and all symbols that are not yet known. - self._arrays[name] = datadesc + self.arrays[name] = datadesc _add_symbols(self, datadesc) return name @@ -2185,12 +2163,12 @@ def add_pgrid(self, grid_name = self._find_new_name('__pgrid') is_subgrid = (parent_grid is not None) if parent_grid and isinstance(parent_grid, str): - parent_grid = self._pgrids[parent_grid] + parent_grid = self.pgrids[parent_grid] - self._pgrids[grid_name] = ProcessGrid(grid_name, is_subgrid, shape, parent_grid, color, exact_grid, root) + self.pgrids[grid_name] = ProcessGrid(grid_name, is_subgrid, shape, parent_grid, color, exact_grid, root) - self.append_init_code(self._pgrids[grid_name].init_code()) - self.append_exit_code(self._pgrids[grid_name].exit_code()) + self.append_init_code(self.pgrids[grid_name].init_code()) + self.append_exit_code(self.pgrids[grid_name].exit_code()) return grid_name @@ -2232,9 +2210,9 @@ def add_subarray(self, # No need to ensure unique test. subarray_name = self._find_new_name('__subarray') - self._subarrays[subarray_name] = SubArray(subarray_name, dtype, shape, subshape, pgrid, correspondence) - self.append_init_code(self._subarrays[subarray_name].init_code()) - self.append_exit_code(self._subarrays[subarray_name].exit_code()) + self.subarrays[subarray_name] = SubArray(subarray_name, dtype, shape, subshape, pgrid, correspondence) + self.append_init_code(self.subarrays[subarray_name].init_code()) + self.append_exit_code(self.subarrays[subarray_name].exit_code()) return subarray_name @@ -2249,9 +2227,9 @@ def add_rdistrarray(self, array_a: str, array_b: str): # No need to ensure unique test. name = self._find_new_name('__rdistrarray') - self._rdistrarrays[name] = RedistrArray(name, array_a, array_b) - self.append_init_code(self._rdistrarrays[name].init_code(self)) - self.append_exit_code(self._rdistrarrays[name].exit_code(self)) + self.rdistrarrays[name] = RedistrArray(name, array_a, array_b) + self.append_init_code(self.rdistrarrays[name].init_code(self)) + self.append_exit_code(self.rdistrarrays[name].exit_code(self)) return name def add_loop( diff --git a/dace/sdfg/state.py b/dace/sdfg/state.py index 4828b26a4c..b6da1d82b6 100644 --- a/dace/sdfg/state.py +++ b/dace/sdfg/state.py @@ -1554,7 +1554,7 @@ def _repr_html_(self): from dace.sdfg import SDFG arrays = set(n.data for n in self.data_nodes()) sdfg = SDFG(self.label) - sdfg._arrays = dace.sdfg.NestedDict({k: self.sdfg.arrays[k] for k in arrays}) + sdfg.arrays = dace.sdfg.NestedDict({k: self.sdfg.arrays[k] for k in arrays}) sdfg.add_node(self) return sdfg._repr_html_() @@ -2367,8 +2367,8 @@ def add_array(self, 'The "SDFGState.add_array" API is deprecated, please ' 'use "SDFG.add_array" and "SDFGState.add_access"', DeprecationWarning) # Workaround to allow this legacy API - if name in self.sdfg._arrays: - del self.sdfg._arrays[name] + if name in self.sdfg.arrays: + del self.sdfg.arrays[name] self.sdfg.add_array(name, shape, dtype, @@ -2400,8 +2400,8 @@ def add_stream( 'The "SDFGState.add_stream" API is deprecated, please ' 'use "SDFG.add_stream" and "SDFGState.add_access"', DeprecationWarning) # Workaround to allow this legacy API - if name in self.sdfg._arrays: - del self.sdfg._arrays[name] + if name in self.sdfg.arrays: + del self.sdfg.arrays[name] self.sdfg.add_stream( name, dtype, @@ -2429,8 +2429,8 @@ def add_scalar( 'The "SDFGState.add_scalar" API is deprecated, please ' 'use "SDFG.add_scalar" and "SDFGState.add_access"', DeprecationWarning) # Workaround to allow this legacy API - if name in self.sdfg._arrays: - del self.sdfg._arrays[name] + if name in self.sdfg.arrays: + del self.sdfg.arrays[name] self.sdfg.add_scalar(name, dtype, storage, transient, lifetime, debuginfo) return self.add_access(name, debuginfo) diff --git a/dace/sdfg/validation.py b/dace/sdfg/validation.py index 7a6efbfc41..949b7d2e24 100644 --- a/dace/sdfg/validation.py +++ b/dace/sdfg/validation.py @@ -252,7 +252,7 @@ def validate_sdfg(sdfg: 'dace.sdfg.SDFG', references: Set[int] = None, **context # Check the names of data descriptors and co. seen_names: Set[str] = set() - for obj_names in [sdfg.arrays.keys(), sdfg.symbols.keys(), sdfg._rdistrarrays.keys(), sdfg._subarrays.keys()]: + for obj_names in [sdfg.arrays.keys(), sdfg.symbols.keys(), sdfg.rdistrarrays.keys(), sdfg.subarrays.keys()]: if not seen_names.isdisjoint(obj_names): raise InvalidSDFGError( f'Found duplicated names: "{seen_names.intersection(obj_names)}". Please ensure ' @@ -275,7 +275,7 @@ def validate_sdfg(sdfg: 'dace.sdfg.SDFG', references: Set[int] = None, **context warnings.warn(f'Found constant "{const_name}" that does not refer to an array or a symbol.') # Validate data descriptors - for name, desc in sdfg._arrays.items(): + for name, desc in sdfg.arrays.items(): if id(desc) in references: raise InvalidSDFGError( f'Duplicate data descriptor object detected: "{name}". Please copy objects ' diff --git a/dace/transformation/helpers.py b/dace/transformation/helpers.py index 738e975ce0..c39c881b49 100644 --- a/dace/transformation/helpers.py +++ b/dace/transformation/helpers.py @@ -1887,12 +1887,12 @@ def _change_sdfg_type(sdfg: SDFG, from_type: typeclass, to_type: typeclass, swap swaps_count += 1 # Swap structures and structure views - for array_desc in sdfg._arrays.values(): + for array_desc in sdfg.arrays.values(): if _is_structure(array_desc) or _is_structure_view(array_desc): swaps_count = _change_structure_type(array_desc, from_type, to_type, swaps_count) # Change dace.data.struct field types - for array_desc in sdfg._arrays.values(): + for array_desc in sdfg.arrays.values(): if _is_structure(array_desc): if _is_pointer(array_desc.dtype): if isinstance(array_desc.dtype.base_type, dtypes.struct): From 507ed9dd7593291dd977cee3f86422ada0345034 Mon Sep 17 00:00:00 2001 From: Tal Ben-Nun Date: Fri, 16 Jan 2026 22:32:59 -0800 Subject: [PATCH 7/7] process_grids -> pgrids --- dace/distr_types.py | 8 ++++---- dace/frontend/python/newast.py | 9 +++------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/dace/distr_types.py b/dace/distr_types.py index 4317343082..b34d0668ed 100644 --- a/dace/distr_types.py +++ b/dace/distr_types.py @@ -451,9 +451,9 @@ def init_code(self, sdfg): tmp += f""" if (__state->{array_b.pgrid}_valid) {{ """ - grid_a = sdfg.process_grids[array_a.pgrid] + grid_a = sdfg.pgrids[array_a.pgrid] if grid_a.is_subgrid: - pgrid_a = sdfg.process_grids[grid_a.parent_grid] + pgrid_a = sdfg.pgrids[grid_a.parent_grid] tmp += f""" int pgrid_exact_coords[{len(pgrid_a.shape)}]; dace::comm::cart_coords({grid_a.exact_grid}, {len(pgrid_a.shape)}, __state->{pgrid_a.name}_dims, pgrid_exact_coords); @@ -520,9 +520,9 @@ def init_code(self, sdfg): tmp += f""" if (__state->{array_a.pgrid}_valid) {{ """ - grid_b = sdfg.process_grids[array_b.pgrid] + grid_b = sdfg.pgrids[array_b.pgrid] if grid_b.is_subgrid: - pgrid_b = sdfg.process_grids[grid_b.parent_grid] + pgrid_b = sdfg.pgrids[grid_b.parent_grid] tmp += f""" int pgrid_exact_coords[{len(pgrid_b.shape)}]; dace::comm::cart_coords({grid_b.exact_grid}, {len(pgrid_b.shape)}, __state->{pgrid_b.name}_dims, pgrid_exact_coords); diff --git a/dace/frontend/python/newast.py b/dace/frontend/python/newast.py index e60d1227e5..c4f9f7555b 100644 --- a/dace/frontend/python/newast.py +++ b/dace/frontend/python/newast.py @@ -1369,10 +1369,7 @@ def defined(self): result.update(self.sdfg.arrays) # MPI-related stuff - result.update({ - v: self.sdfg.process_grids[v] - for k, v in self.variables.items() if v in self.sdfg.process_grids - }) + result.update({v: self.sdfg.pgrids[v] for k, v in self.variables.items() if v in self.sdfg.pgrids}) try: from mpi4py import MPI result.update({k: v for k, v in self.globals.items() if isinstance(v, MPI.Comm)}) @@ -5194,8 +5191,8 @@ def _gettype(self, opnode: ast.AST) -> List[Tuple[str, str]]: result = [] for operand in operands: - if isinstance(operand, str) and operand in self.sdfg.process_grids: - result.append((operand, type(self.sdfg.process_grids[operand]).__name__)) + if isinstance(operand, str) and operand in self.sdfg.pgrids: + result.append((operand, type(self.sdfg.pgrids[operand]).__name__)) elif isinstance(operand, str) and operand in self.sdfg.arrays: result.append((operand, type(self.sdfg.arrays[operand]))) elif isinstance(operand, str) and operand in self.scope_arrays: