Example #1
0
    def X(pred):
        """Returns a function that transforms predicates by casting accesses to
        't1' to 'x1' and accesses to 't2' to 'x2'.

        Example:
            Here is an example of casting an example predicate::

                # This predicate tests whether a bound's 't2' value is greater
                # than its 't1' value
                def example_pred(bounds):
                    return bounds['t2'] > bounds['t1']

                # t1 = 0, t2 = 1, x1 = 1, x2 = 0, y1 = 1, y2 = 0
                higher_t2_lower_x2 = Bounds3D(0, 1, 1, 0, 1, 0)

                example_pred(higher_t2_lower_x2) # this is True, since t2 > t1

                Bounds3D.X(example_pred)(higher_t2_lower_x2) # this is False, since x2 < x1

        Arg:
            pred: The predicate to cast.

        Returns:
            The same predicate as ``pred``, except accesses to 't1' are cast to
            'x1', and accesses to 't2' are cast to 'x2'.
        """
        return Bounds.cast({'t1': 'x1', 't2': 'x2'})(pred)
Example #2
0
    def T(pred):
        """Returns a function that transforms predicates by casting accesses to
        't1' to 't1' and accesses to 't2' to 't2'. This doesn't actually
        transform anything, but it's a nice helper function for readability.

        Arg:
            pred: The predicate to cast.

        Returns:
            The same predicate as ``pred``.
        """
        return Bounds.cast({'t1': 't1', 't2': 't2'})(pred)
Example #3
0
 def map_output(intrvl, overlapped):
     # Take only nontrivial overlaps
     to_subtract = sorted([
         i for i in overlapped
         if (i.size(axis) > 0 and Bounds.cast({
             't1': axis[0],
             't2': axis[1]
         })(overlaps())(intrvl, i))
     ],
                          key=lambda i: (i[axis[0]], i[axis[1]]))
     if len(to_subtract) == 0:
         return [intrvl.copy()]
     else:
         return compute_difference(intrvl, to_subtract)
Example #4
0
    def coalesce(self,
                 axis,
                 bounds_merge_op,
                 payload_merge_op=lambda p1, p2: p1,
                 predicate=None,
                 epsilon=0):
        """Recursively merge all intervals that are touching or overlapping
        along ``axis``. 

        Merge intervals in self if they meet, overlap, or are up to ``epsilon``
        apart along ``axis``. If a predicate is specified, intervals will be merged
        if they meet/overlap and satisfy the predicate. 
        Repeat the process until all such intervals are merged.

        Merges the bounds with ``bounds_merge_op`` and merges payloads with
        ``payload_merge_op``.

        Args:
            axis: The axis to coalesce on.
            bounds_merge_op: A function that takes two bounds and returns a
                merged version of both of them. Along ``axis``, this function
                should return a bound that spans the two bounds.
            payload_merge_op (optional): A function that takes in two payloads
                and merges them. Defaults to a function that returns the first
                of the two payloads.
            predicate (optional): A function that takes an interval that is 
                currently being coalesced and a new interval and returns 
                whether or not the two intervals should be merged. 
            epsilon (optional): The slack for judging if Intervals meet or
                overlap. Must be nonnegative. Defaults to 0 (no slack).

        Returns:
            A new IntervalSet of intervals that are disjoint along ``axis`` and
            are at least ``epsilon`` apart.
            
        """        
        if (len(self._intrvls) == 0):
            return self

        new_coalesced_intrvls = []

        #tracks all intervals that are currently experiencing merging
        current_intrvls = []

        sorted_intervals = self._intrvls.copy()
        sorted_intervals = sorted(sorted_intervals, key=lambda intrvl: (intrvl[axis[0]], intrvl[axis[1]]))

        for intrvl in sorted_intervals:
            new_current_intrvls = []
            for cur in current_intrvls:
                if Bounds.cast({
			        axis[0] : 't1',
			        axis[1] : 't2'
		        })(or_pred(overlaps(),
                    before(max_dist=epsilon)))(cur, intrvl):
                        #adds overlapping intervals to new_current_intrvls
                        new_current_intrvls.append(cur)            
                else:
                    #adds all non-overlapping intervals to new_coalesced_intrvls
                    new_coalesced_intrvls.append(cur)

            current_intrvls = new_current_intrvls
            matched_intrvl = None
            loc = len(current_intrvls) - 1

            #if current_intrvls is empty, we need to start constructing a new set of coalesced intervals
            if len(current_intrvls) == 0:
                current_intrvls.append(intrvl.copy())
                continue
            
            if predicate is None:
                matched_intrvl = current_intrvls[-1]
            else:
                for index, cur in enumerate(current_intrvls):
                    if predicate(cur, intrvl):
                        matched_intrvl = cur
                        loc = index

            #if no matching interval is found, this implies that intrvl should be the start of a new coalescing interval
            if matched_intrvl is None:
                current_intrvls.append(intrvl)
            else:
                current_intrvls[loc] = Interval(
                        bounds_merge_op(matched_intrvl['bounds'],
                                        intrvl['bounds']),
                        payload_merge_op(matched_intrvl['payload'],
                                        intrvl['payload'])
                    )

        for cur in current_intrvls:
            new_coalesced_intrvls.append(cur)
        
        return IntervalSet(new_coalesced_intrvls)
Example #5
0
    def coalesce(self,
                 axis,
                 bounds_merge_op,
                 payload_merge_op=lambda p1, p2: p1,
                 predicate=None,
                 epsilon=0):
        """Recursively merge all intervals that are touching or overlapping
        along ``axis``.

        Merge intervals in self if they meet, overlap, or are up to ``epsilon``
        apart along ``axis``. Repeat the process until all such intervals are
        merged.

        Merges the bounds with ``bounds_merge_op`` and merges payloads with
        ``payload_merge_op``.

        Args:
            axis: The axis to coalesce on.
            bounds_merge_op: A function that takes two bounds and returns a
                merged version of both of them. Along ``axis``, this function
                should return a bound that spans the two bounds.
            payload_merge_op (optional): A function that takes in two payloads
                and merges them. Defaults to a function that returns the first
                of the two payloads.
            epsilon (optional): The slack for judging if Intervals meet or
                overlap. Must be nonnegative. Defaults to 0 (no slack).

        Returns:
            A new IntervalSet of intervals that are disjoint along ``axis`` and
            are at least ``epsilon`` apart.
            
        """
        if (len(self._intrvls) == 0):
            return self

        new_coalesced_intrvls = []
        current_intrvls = []

        for intrvl in self._intrvls:
            for cur in current_intrvls:
                #adds any intervals that occured before the start of intrvl
                if not Bounds.cast({
                        axis[0]: 't1',
                        axis[1]: 't2'
                })(or_pred(overlaps(), before(max_dist=epsilon)))(cur, intrvl):
                    new_coalesced_intrvls.append(cur)
            #re-update contents of current_intrvls ==> contains all intervals that overlap
            current_intrvls = [
                cur for cur in current_intrvls if Bounds.cast({
                    axis[0]: 't1',
                    axis[1]: 't2'
                })(or_pred(overlaps(), before(max_dist=epsilon)))(cur, intrvl)
            ]

            matched_intrvl = None
            loc = len(current_intrvls) - 1

            if len(current_intrvls) == 0:
                current_intrvls.append(intrvl.copy())
                continue

            if predicate is None:
                matched_intrvl = current_intrvls[-1]
            else:
                for index, cur in enumerate(current_intrvls):
                    if predicate(cur, intrvl):
                        matched_intrvl = cur
                        loc = index

            if matched_intrvl is None:
                current_intrvls.append(intrvl)
            else:
                current_intrvls[loc] = Interval(
                    bounds_merge_op(matched_intrvl['bounds'],
                                    intrvl['bounds']),
                    payload_merge_op(matched_intrvl['payload'],
                                     intrvl['payload']))

        for cur in current_intrvls:
            new_coalesced_intrvls.append(cur)

        return IntervalSet(new_coalesced_intrvls)
Example #6
0
    def execute(self):
        while True:
            if len(self.publishable_coalesced_intrvls) > 0 and \
                (len(self.pending_coalesced_intrvls) == 0 or \
                    self.publishable_coalesced_intrvls[0] < self.pending_coalesced_intrvls[0]):
                # the above condition ensures output t1 increases monotoically. 
                self.publish(self.publishable_coalesced_intrvls.pop(0))
                return True
            elif self.done:
                return False

            intrvl = self.instream.get()
            # logger.debug(f"got 1 input: {intrvl}")
            if intrvl is None:
                self.publishable_coalesced_intrvls = sorted(self.publishable_coalesced_intrvls +  self.pending_coalesced_intrvls)
                self.pending_coalesced_intrvls.clear()
                self.done = True
                continue

            # logger.debug(f"publishable: {self.publishable_coalesced_intrvls}")
            # logger.debug(f"pending: {self.pending_coalesced_intrvls}")

            bounds_merge_op = self.bounds_merge_op
            payload_merge_op = self.payload_merge_op
            interval_merge_op = self.interval_merge_op

            predicate = self.predicate
            epsilon = self.epsilon
            axis = self.axis
            distance = self.distance

            new_coalesced_intrvls = self.publishable_coalesced_intrvls
            current_intrvls = self.pending_coalesced_intrvls
            new_current_intrvls = []

            for cur in current_intrvls:
                if Bounds.cast({
			        axis[0] : 't1',
			        axis[1] : 't2'
		        })(or_pred(overlaps(),
                    before(max_dist=epsilon)))(cur, intrvl):
                        # add overlapping/near intervals to new_current_intrvls
                        new_current_intrvls.append(cur)            
                else:
                    # others intervals can be released, add to new_coalesced_intrvls
                    # logger.debug(f"Adding to publishable: {cur}")
                    new_coalesced_intrvls.append(cur)

            current_intrvls = new_current_intrvls

            #if current_intrvls is empty, we need to start constructing a new set of pending intervals
            if len(current_intrvls) == 0:
                current_intrvls.append(intrvl)
                self.publishable_coalesced_intrvls = sorted(new_coalesced_intrvls)
                self.pending_coalesced_intrvls = sorted(current_intrvls)
                continue
            
            matched_intrvl = None
            min_dist = None
            loc = None
            for index, cur in enumerate(current_intrvls):
                if predicate(cur, intrvl):
                    d = distance(cur, intrvl)
                    if min_dist is None or d < min_dist:
                        # update winner
                        matched_intrvl = cur
                        loc = index
                        min_dist = d 

            # if no matching interval is found, this implies that intrvl should be the start of a new coalescing interval
            if matched_intrvl is None:
                current_intrvls.append(intrvl)
            else:
                # finally, do the merge
                if interval_merge_op is not None:
                    current_intrvls[loc] = interval_merge_op(matched_intrvl, intrvl)
                else:
                    current_intrvls[loc] = Interval(
                            bounds_merge_op(matched_intrvl['bounds'],
                                            intrvl['bounds']),
                            payload_merge_op(matched_intrvl['payload'],
                                            intrvl['payload'])
                        )

            self.publishable_coalesced_intrvls = sorted(new_coalesced_intrvls)
            self.pending_coalesced_intrvls = sorted(current_intrvls)