Skip to content

Commit

Permalink
Specified behaviour of Subset.covers() for different dimensionality (
Browse files Browse the repository at this point in the history
…#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 <[email protected]>
  • Loading branch information
philip-paul-mueller and tbennun authored Sep 4, 2024
1 parent 8521f40 commit e1daf32
Showing 1 changed file with 35 additions and 5 deletions.
40 changes: 35 additions & 5 deletions dace/subsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,36 @@ 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()
max_elements_a = subset_a.max_element_approx() if approximation else subset_a.max_element()
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
Expand All @@ -53,20 +72,25 @@ 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
for rb, re, orb, ore in zip(self.min_element_approx(), self.max_element_approx(),
other.min_element_approx(), other.max_element_approx())])
except TypeError:
return False

else:
try:
if not bounding_box_symbolic_positive(self, other, True):
Expand All @@ -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:
Expand Down

0 comments on commit e1daf32

Please sign in to comment.