def assume(self, condition, manager: PyManager = None, bwd: bool = False) -> 'Box2State': if self.is_bottom(): return self if isinstance(condition, tuple): condition = list(condition) if isinstance(condition, list): for feature, (lower, upper) in condition: self.bounds[feature.name].meet(IntervalLattice(lower, upper)) return self elif isinstance(condition, BinaryBooleanOperation): if condition.operator == BinaryBooleanOperation.Operator.Or: right = deepcopy(self).assume(condition.right, bwd=bwd) return self.assume(condition.left, bwd=bwd).join(right) elif condition.operator == BinaryBooleanOperation.Operator.And: assert isinstance(condition.left, BinaryComparisonOperation) assert isinstance(condition.right, BinaryComparisonOperation) assert isinstance(condition.left.left, Literal) assert condition.left.operator == BinaryComparisonOperation.Operator.LtE assert isinstance(condition.left.right, VariableIdentifier) assert isinstance(condition.right.left, VariableIdentifier) assert condition.right.operator == BinaryComparisonOperation.Operator.LtE assert isinstance(condition.right.right, Literal) lower = eval(condition.left.left.val) upper = eval(condition.right.right.val) assert condition.left.right.name == condition.right.left.name self.bounds[condition.left.right.name].meet( IntervalLattice(lower, upper)) return self elif isinstance(condition, BinaryComparisonOperation): if condition.operator == BinaryComparisonOperation.Operator.Gt: assert isinstance(condition.left, VariableIdentifier) assert isinstance(condition.right, Literal) lower = eval(condition.right.val) upper = 1 self.bounds[condition.left.name].meet( IntervalLattice(lower, upper)) return self elif condition.operator == BinaryComparisonOperation.Operator.LtE: assert isinstance(condition.left, VariableIdentifier) assert isinstance(condition.right, Literal) lower = 0 upper = eval(condition.right.val) self.bounds[condition.left.name].meet( IntervalLattice(lower, upper)) return self # elif isinstance(condition, PyTcons1): # abstract1 = self.domain(manager, self.environment, array=PyTcons1Array([condition])) # self.state = self.state.meet(abstract1) # return self elif isinstance(condition, set): assert len(condition) == 1 self.assume(condition.pop(), bwd=bwd) return self raise NotImplementedError( f"Assumption of {condition.__class__.__name__} is unsupported!")
def __init__(self, inputs: Set[VariableIdentifier], precursory: State = None): super().__init__(precursory=precursory) self.inputs = {input.name for input in inputs} # PRE: all the inputs in [0, 1] self.bounds = {input: (IntervalLattice(0, 0), IntervalLattice(1, 1)) for input in self.inputs} # the free coeff is indicated by '_' in the dictionary self.poly self.poly = {input: ({'_': 0.0}, {'_': 1.0}) for input in self.inputs} self.flag = None
def affine(self, left: List[PyVar], right: List[PyTexpr1]) -> 'NeurifyState': if self.is_bottom(): return self for lhs, expr in zip(left, right): name = str(lhs) rhs = texpr_to_dict(expr) _inf, inf = deepcopy(rhs), deepcopy(rhs) _sup, sup = deepcopy(rhs), deepcopy(rhs) self.poly[name] = (_inf, _sup) # while there is a non-input-variable in inf while any(variable in inf and variable not in self.inputs for variable in self.poly): for variable in self.poly: if variable in inf and variable not in self.inputs: # should be replaced coeff = inf[variable] if coeff > 0: replacement = self.poly[variable][LOW] elif coeff < 0: replacement = self.poly[variable][UP] else: # coeff == 0 replacement = dict() replacement['_'] = 0.0 del inf[variable] for var, val in replacement.items(): if var in inf: inf[var] += coeff * val else: inf[var] = coeff * val # while there is a non-input-variable in sup while any(variable in sup and variable not in self.inputs for variable in self.poly): for variable in self.poly: if variable in sup and variable not in self.inputs: # should be replaced coeff = sup[variable] if coeff > 0: replacement = self.poly[variable][UP] elif coeff < 0: replacement = self.poly[variable][LOW] else: # coeff == 0 replacement = dict() replacement['_'] = 0.0 del sup[variable] for var, val in replacement.items(): if var in sup: sup[var] += coeff * val else: sup[var] = coeff * val ext_bounds = {k: IntervalLattice(l.lower, u.upper) for k, (l, u) in self.bounds.items()} lower = evaluate(inf, ext_bounds) upper = evaluate(sup, ext_bounds) self.bounds[name] = (IntervalLattice(lower.lower, lower.upper), IntervalLattice(upper.lower, upper.upper)) return self
def resize_bounds(self, var_name, new_bounds): new_low_lower = new_bounds.lower new_low_upper = self.bounds[var_name][LOW].upper new_up_lower = self.bounds[var_name][UP].lower new_up_upper = new_bounds.upper if new_low_lower > new_low_upper: new_low_upper = new_low_lower if new_up_upper < new_up_lower: new_up_lower = new_up_upper self.bounds[var_name] = ( IntervalLattice(new_low_lower, new_low_upper), IntervalLattice(new_up_lower, new_up_upper) )
def relu(self, stmt: PyVar, active: bool = False, inactive: bool = False) -> 'Symbolic3State': if self.is_bottom(): return self self.flag = None name = str(stmt) lattice: IntervalLattice = self.bounds[name] lower, upper = lattice.lower, lattice.upper if upper <= 0 or inactive: zero = dict() zero['_'] = 0.0 self.symbols[name] = (name, zero) self.bounds[name] = IntervalLattice(0, 0) self.flag = -1 elif 0 <= lower or active: if active and lower < 0: bounds = self.bounds[name] self.bounds[name] = bounds.meet(IntervalLattice(0, upper)) del self.symbols[name] self.flag = 1 else: _active, _inactive = deepcopy(self.bounds), deepcopy(self.bounds) _active[name] = _active[name].meet(IntervalLattice(0, upper)) _inactive[name] = _inactive[name].meet(IntervalLattice(0, 0)) if any(element.is_bottom() for element in _active.values()): zero = dict() zero['_'] = 0.0 self.symbols[name] = (name, zero) self.flag = -1 elif any(element.is_bottom() for element in _inactive.values()): self.flag = 1 else: del self.symbols[name] self.flag = None join = dict() for variable, itv in _active.items(): join[variable] = itv.join(_inactive[variable]) self.bounds[name] = join[name].meet(IntervalLattice(0, upper)) self.flag = None return self
def __init__(self, inputs: Set[VariableIdentifier], precursory: State = None): super().__init__(precursory=precursory) self.inputs = {input.name for input in inputs} self.bounds = dict() for input in self.inputs: self.bounds[input] = IntervalLattice(0, 1) self.flag = None
def evaluate(dictionary, bounds): result = IntervalLattice(0, 0) for var, val in dictionary.items(): coeff = IntervalLattice(val, val) if var != '_': result = result._add(coeff._mult(bounds[var])) else: result = result._add(coeff) return result
def affine(self, left: List[PyVar], right: List[PyTexpr1]) -> 'Box2State': if self.is_bottom(): return self for lhs, expr in zip(left, right): name = str(lhs) rhs = texpr_to_dict(expr) inf = deepcopy(rhs) sup = deepcopy(rhs) lower = evaluate(inf, self.bounds) upper = evaluate(sup, self.bounds) self.bounds[name] = IntervalLattice(lower.lower, upper.upper) return self
def _meet(self, other: 'NeurifyState') -> 'NeurifyState': for var in self.bounds: other_bound = IntervalLattice(other.bounds[var][LOW].lower, other.bounds[var][UP].upper) self_bound = IntervalLattice(self.bounds[var][LOW].lower, self.bounds[var][UP].upper) self_bound.meet(other_bound) self.bounds[var] = ( IntervalLattice(self_bound.lower, self_bound.lower), IntervalLattice(self_bound.upper, self_bound.upper)) self.poly[var] = ( {'_': self.bounds[var][LOW].lower}, {'_': self.bounds[var][UP].upper}) return self
def relu(self, stmt: PyVar, active: bool = False, inactive: bool = False) -> 'Box2State': if self.is_bottom(): return self name = str(stmt) lattice: IntervalLattice = self.bounds[name] lower, upper = lattice.lower, lattice.upper if upper <= 0 or inactive: # l_j = u_j = 0 self.bounds[name] = IntervalLattice(0, 0) # 0 <= x_j <= 0 self.flag = -1 elif 0 <= lower or active: if active and lower < 0: self.bounds[name] = IntervalLattice(0, upper) self.flag = 1 else: # case (c) in Fig. 4, equation (4) _active, _inactive = deepcopy(self.bounds), deepcopy(self.bounds) _active[name] = _active[name].meet(IntervalLattice(0, upper)) _inactive[name] = _inactive[name].meet(IntervalLattice(0, 0)) if any(element.is_bottom() for element in _active.values()): self.flag = -1 elif any(element.is_bottom() for element in _inactive.values()): self.flag = 1 else: self.flag = None join = dict() for variable, itv in _active.items(): join[variable] = itv.join(_inactive[variable]) self.bounds[name] = join[name].meet(IntervalLattice(0, upper)) self.flag = None return self
def affine(self, left: List[PyVar], right: List[PyTexpr1]) -> 'Symbolic3State': if self.is_bottom(): return self assignments = dict() for lhs, expr in zip(left, right): name = str(lhs) rhs = texpr_to_dict(expr) for sym, val in self.symbols.values(): rhs = substitute_in_dict(rhs, sym, val) assignments[name] = (name, rhs) bound = evaluate(rhs, self.bounds) self.bounds[name] = IntervalLattice(bound.lower, bound.upper) self.symbols = assignments return self
def relu(self, stmt: PyVar, active: bool = False, inactive: bool = False) -> 'NeurifyState': if self.is_bottom(): return self name = str(stmt) low_lattice, up_lattice = self.bounds[name] low_lower, low_upper = low_lattice.lower, low_lattice.upper up_lower, up_upper = up_lattice.lower, up_lattice.upper self.set_flag(low_lower, up_upper, active, inactive) if low_upper <= 0 or inactive: self.bounds[name] = (IntervalLattice(0, 0), self.bounds[name][UP]) self.poly[name] = ({'_': 0.0}, self.poly[name][UP]) elif 0 <= low_lower or active: if active and low_lower < 0: self.bounds[name] = (IntervalLattice(0, low_upper), self.bounds[name][UP]) self.poly[name] = ({'_': 0.0}, self.poly[name][UP]) else: m = low_upper / (low_upper - low_lower) sup = self.poly[name][LOW] # sup does side effects over self.poly[name][UP] for var, val in sup.items(): sup[var] = m * val self.bounds[name] = (IntervalLattice(low_lower*m, low_upper*m), self.bounds[name][UP]) if up_upper <= 0 or inactive: self.bounds[name] = (self.bounds[name][LOW], IntervalLattice(0, 0)) self.poly[name] = (self.poly[name][LOW], {'_': 0.0}) elif 0 <= up_lower or active: if active and up_lower < 0: self.bounds[name] = (self.bounds[name][LOW], IntervalLattice(0, up_upper)) else: m = up_upper / (up_upper - up_lower) q = up_upper * (-up_lower) / (up_upper - up_lower) sup = self.poly[name][UP] # sup does side effects over self.poly[name][UP] for var, val in sup.items(): sup[var] = m * val sup['_'] += q self.bounds[name] = (self.bounds[name][LOW], IntervalLattice(up_lower*m+q, up_upper*m+q)) return self
def resize_bounds(self, var_name, new_bounds): self.bounds[var_name] = IntervalLattice(new_bounds.lower, new_bounds.upper)
def get_bounds(self, var_name): low, up = self.bounds[var_name] return IntervalLattice(low.lower, up.upper)
def __assume_helper(self, feature, lower, upper): self.bounds[feature.name] = (IntervalLattice(lower, lower), IntervalLattice(upper, upper)) self.poly[feature.name] = ( {'_': self.bounds[feature.name][LOW].lower}, {'_': self.bounds[feature.name][UP].upper} )