From e1daf32fc81465e711b0e38f9fa51baff0ab7dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20M=C3=BCller?= <147368808+philip-paul-mueller@users.noreply.github.com> Date: Wed, 4 Sep 2024 22:20:30 +0200 Subject: [PATCH] Specified behaviour of `Subset.covers()` for different dimensionality (#1637) Before all cover functions ignored the case if the subsets have different lengths. The effect was that the subsets `A = Range.from_string("i, k, 0:N, 1")` and `B = Range.from_string("i, k")` would cover each other. This case was considered undefined behaviour. This commit changes the behaviour of these function such that for covering it is required that the subsets must have the same dimensions. --------- Co-authored-by: Tal Ben-Nun --- dace/subsets.py | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/dace/subsets.py b/dace/subsets.py index 0d6037e682..e7b6869678 100644 --- a/dace/subsets.py +++ b/dace/subsets.py @@ -21,10 +21,22 @@ def nng(expr): return expr def bounding_box_cover_exact(subset_a, subset_b) -> bool: + min_elements_a = subset_a.min_element() + max_elements_a = subset_a.max_element() + min_elements_b = subset_b.min_element() + max_elements_b = subset_b.max_element() + + # Covering only make sense if the two subsets have the same number of dimensions. + if len(min_elements_a) != len(min_elements_b): + return ValueError( + f"A bounding box of dimensionality {len(min_elements_a)} cannot" + f" test covering a bounding box of dimensionality {len(min_elements_b)}." + ) + return all([(symbolic.simplify_ext(nng(rb)) <= symbolic.simplify_ext(nng(orb))) == True and (symbolic.simplify_ext(nng(re)) >= symbolic.simplify_ext(nng(ore))) == True - for rb, re, orb, ore in zip(subset_a.min_element(), subset_a.max_element(), - subset_b.min_element(), subset_b.max_element())]) + for rb, re, orb, ore in zip(min_elements_a, max_elements_a, + min_elements_b, max_elements_b)]) def bounding_box_symbolic_positive(subset_a, subset_b, approximation = False)-> bool: min_elements_a = subset_a.min_element_approx() if approximation else subset_a.min_element() @@ -32,6 +44,13 @@ def bounding_box_symbolic_positive(subset_a, subset_b, approximation = False)-> min_elements_b = subset_b.min_element_approx() if approximation else subset_b.min_element() max_elements_b = subset_b.max_element_approx() if approximation else subset_b.max_element() + # Covering only make sense if the two subsets have the same number of dimensions. + if len(min_elements_a) != len(min_elements_b): + return ValueError( + f"A bounding box of dimensionality {len(min_elements_a)} cannot" + f" test covering a bounding box of dimensionality {len(min_elements_b)}." + ) + for rb, re, orb, ore in zip(min_elements_a, max_elements_a, min_elements_b, max_elements_b): # NOTE: We first test for equality, which always returns True or False. If the equality test returns @@ -53,12 +72,18 @@ def bounding_box_symbolic_positive(subset_a, subset_b, approximation = False)-> class Subset(object): """ Defines a subset of a data descriptor. """ + def covers(self, other): """ Returns True if this subset covers (using a bounding box) another subset. """ - symbolic_positive = Config.get('optimizer', 'symbolic_positive') - if not symbolic_positive: + # Subsets of different dimensionality can never cover each other. + if self.dims() != other.dims(): + return ValueError( + f"A subset of dimensionality {self.dim()} cannot test covering a subset of dimensionality {other.dims()}" + ) + + if not Config.get('optimizer', 'symbolic_positive'): try: return all([(symbolic.simplify_ext(nng(rb)) <= symbolic.simplify_ext(nng(orb))) == True and (symbolic.simplify_ext(nng(re)) >= symbolic.simplify_ext(nng(ore))) == True @@ -66,7 +91,6 @@ def covers(self, other): other.min_element_approx(), other.max_element_approx())]) except TypeError: return False - else: try: if not bounding_box_symbolic_positive(self, other, True): @@ -79,6 +103,12 @@ def covers(self, other): def covers_precise(self, other): """ Returns True if self contains all the elements in other. """ + # Subsets of different dimensionality can never cover each other. + if self.dims() != other.dims(): + return ValueError( + f"A subset of dimensionality {self.dim()} cannot test covering a subset of dimensionality {other.dims()}" + ) + # If self does not cover other with a bounding box union, return false. symbolic_positive = Config.get('optimizer', 'symbolic_positive') try: