def _covered(new_lb: Tensor, new_ub: Tensor, new_label: Tensor) -> bool: """ Returns True if the new LB/UB is already covered by some intersected piece. Assuming new_lb/new_ub is from X or Y. So there won't be intersection, thus just check subset? is sufficient. Assuming all params are not-batched. """ for i in range(len(shared_lbs)): shared_lb, shared_ub, shared_label = shared_lbs[i], shared_ubs[i], shared_labels[i] if valid_lb_ub(shared_lb, new_lb) and valid_lb_ub(new_ub, shared_ub): assert torch.equal(new_label | shared_label, shared_label), 'New intersected cube got more props?!' return True return False
def lbub_intersect(lb1: Tensor, ub1: Tensor, lb2: Tensor, ub2: Tensor) -> Tuple[Tensor, Tensor]: """ Return intersected [lb1, ub1] /\ [lb2, ub2], or raise ValueError when they do not overlap. :param lb1, ub1, lb2, ub2: not batched :return: not batched tensors """ assert lb1.size() == lb2.size() and ub1.size() == ub2.size() res_lb, _ = torch.max(torch.stack((lb1, lb2), dim=-1), dim=-1) res_ub, _ = torch.min(torch.stack((ub1, ub2), dim=-1), dim=-1) if not valid_lb_ub(res_lb, res_ub): raise ValueError('Intersection failed.') return res_lb, res_ub
def join_all(self, props: List[AbsProp]): """ Conjoin multiple properties altogether. Now that each property may have different input space and different safety / violation distance functions. This method will re-arrange and determine the boundaries of sub-regions and which properties they should satisfy. """ nprops = len(props) assert nprops > 0 # initialize for 1st prop orig_label = torch.eye(nprops).byte() # showing each input region which properties they should obey lbs, ubs = props[0].lbub() labels = orig_label[[0]].expand(len(lbs), nprops) for i, prop in enumerate(props): if i == 0: continue new_lbs, new_ubs = prop.lbub() assert valid_lb_ub(new_lbs, new_ubs) new_labels = orig_label[[i]].expand(len(new_lbs), nprops) lbs, ubs, labels = self._join(lbs, ubs, labels, new_lbs, new_ubs, new_labels) return lbs, ubs, labels
def gamma(self) -> Tuple[Tensor, Tensor]: """ Transform the abstract elements back into Lower Bounds and Upper Bounds. """ lb = self.lb() ub = self.ub() assert valid_lb_ub(lb, ub) return lb, ub
def __init__(self, boxes_lb: Tensor, boxes_ub: Tensor, boxes_extra: Tensor = None): assert valid_lb_ub(boxes_lb, boxes_ub) self.boxes_lb = boxes_lb self.boxes_ub = boxes_ub self.boxes_extra = boxes_extra return