def rename_vars(pcs, global_states): ret_pcs = [] vars_mapping = {} for expr in pcs: list_vars = get_vars(expr) for var in list_vars: if var in vars_mapping: expr = substitute(expr, (var, vars_mapping[var])) continue var_name = var.decl().name() # check if a var is global if var_name.startswith("Ia_store_"): position = var_name.split('Ia_store_')[1] # if it is not modified then keep the previous name if position not in global_states: continue # otherwise, change the name of the variable new_var_name = var_name + '_old' new_var = BitVec(new_var_name, 256) vars_mapping[var] = new_var expr = substitute(expr, (var, vars_mapping[var])) ret_pcs.append(expr) ret_gs = {} # replace variable in storage expression for storage_addr in global_states: expr = global_states[storage_addr] # z3 4.1 makes me add this line if is_expr(expr): list_vars = get_vars(expr) for var in list_vars: if var in vars_mapping: expr = substitute(expr, (var, vars_mapping[var])) continue var_name = var.decl().name() # check if a var is global if var_name.startswith("Ia_store_"): position = int( var_name.split('_')[len(var_name.split('_')) - 1]) # if it is not modified if position not in global_states: continue # otherwise, change the name of the variable new_var_name = var_name + '_old' new_var = BitVec(new_var_name, 256) vars_mapping[var] = new_var expr = substitute(expr, (var, vars_mapping[var])) ret_gs[storage_addr] = expr return ret_pcs, ret_gs
def slice_defs(prop, defs, assumes_state, assumes_trans): """ Return a new (potentially empty) def dictionary from the old one consisting of only necessary variable definitions to prove property """ if __debug__: assert is_dict(defs), defs assert is_list(assumes_state), assumes_state assert is_list(assumes_trans), assumes_trans fs = [prop] + assumes_state + assumes_trans fs = [f for f in fs if is_expr(f)] vs = [get_vars(f) for f in fs] vs = [cur(v_) if is_pre(v_) else v_ for v_ in vflatten(vs)] vs = vset(vs, fhash) vs_ = [Prog.get_influence_vs(v, defs, []) for v in vs] vs = vset(vflatten(vs + vs_), fhash) a_defs = OrderedDict() for v in vs: k = fhash(v) if k in defs: a_defs[k] = defs[k] return a_defs
def cur_f(f): """ Convert a formula f with pre vars to a formula with cur vars >>> from z3 import * >>> x,y,z = Ints('x y z') >>> cur_f(pre(z) == 9) z == 9 >>> cur_f(And(pre(y) == 4, pre(z) == 9) ) And(y == 4, z == 9) >>> cur_f(pre(y) == z) Traceback (most recent call last): ... AssertionError: y_pre == z """ if __debug__: assert f is None or is_pre_f(f), f if f is None: return None if is_expr_var(f): return cur(f) else: vss = [(v, cur(v)) for v in get_vars(f)] return substitute(f, *vss)
def substitute_f(f, i, s=None): """ Replaces all variables v in f with v_(i*_) s = a symbol string, e.g. n (so we will have the variable x_n_1 instead of x_1 Examples: >>> from z3 import * >>> x,x_pre,y,y_pre = Ints('x x_pre y y_pre') >>> substitute_f(Implies(x==10,y==5),i=0) Or(Not(x_0 == 10), y_0 == 5) >>> substitute_f(Implies(x==10,y==5),i=1) Or(Not(x_1 == 10), y_1 == 5) >>> substitute_f(Implies(x==10,pre(y)==5),i=1) Or(Not(x_1 == 10), y_0 == 5) >>> substitute_f(Implies(x==10,pre(y)==5),1,s='m') Or(Not(x_m_1 == 10), y_m_0 == 5) >>> substitute_f(Implies(x==10,pre(y)==5),10,s='m') Or(Not(x_m_10 == 10), y_m_9 == 5) >>> substitute_f(BoolVal(False),i=10) False >>> substitute_f(IntVal(100),i=10) 100 """ if __debug__: assert is_expr(f) assert i >= 0 assert i != 0 or is_state(f) # v_pre => i!=0 assert not s or isinstance(s, str) and len(s) >= 1 vs = get_vars(f) if not vs: #e.g. True, 5, etc has no variables so return as is return f else: vss = gen_vars(vs, i, s) f_ = f for vs in vss: f_ = substitute(f_, vs) return f_
def atT(c, when_c=None): """ Return And(!pre(c),c,when_c) When seeing the SCR syntax such as `@T(x) when c1`, the parser calls atT(x,c1') where c1' is c1 with all variables v => pre(v). In other words, the when_c only contains 'pre' variables. IMPORTANT: SCR syntax allows syntax such as `@T(x) when c1 OR @T(x) when c2` But I require explicit parentheses around these `(@T(x) when c1) OR (@T(x) when c2)`. The reason is to avoid ambiguity: 'c1 OR @T(x) when c2' could be treated as an expression c3, i.e. @T(x) when c3. EXAMPLES: >>> from z3 import * >>> x,y = Ints('x y') >>> atT(x==10) And(Not(x_pre == 10), x == 10) >>> atT(x==10, pre(x)!= pre(y)) And(Not(x_pre == 10), x == 10, x_pre != y_pre) >>> atT(Or(x==10,y==3),pre(x)!= pre(y)) And(Not(Or(x_pre == 10, y_pre == 3)), Or(x == 10, y == 3), x_pre != y_pre) """ if __debug__: assert is_state(c), c assert not when_c or is_pre_f(when_c),\ "'{}' does not have the right WHEN format. "\ "Perhaps missing parenthesis, e.g."\ "(@T(x) when c1) OR (@T(y) when c2). "\ "See document in function for details.".format(when_c) vss_c_cur = get_vars(c) vss_c_pre = map(pre, vss_c_cur) vss_c = zip(vss_c_cur, vss_c_pre) not_c = Not(substitute(c, *vss_c)) return myAnd(not_c, c, when_c)
def is_pre_f(f): """ Check if all variables in f are pre variables Examples: >>> from z3 import * >>> x,y = Ints('x y') >>> is_pre_f(x) False >>> is_pre_f(pre(x)) True >>> is_pre_f(Or(pre(x)==10, y==3)) False >>> is_pre_f(Or(pre(x)==10, pre(y)==3)) True """ if __debug__: assert is_expr(f), f return all(is_pre(v) for v in get_vars(f))
def pre_f(f): """ Convert a formula f with cur vars to a formula with pre vars >>> from z3 import * >>> x,y,z = Ints('x y z') >>> pre_f(z == 9) z_pre == 9 >>> pre_f(And(y == 4, z == 9) ) And(y_pre == 4, z_pre == 9) """ if __debug__: assert f is None or is_cur_f(f), f if f is None: return None elif is_expr_var(f): return pre(f) else: vss = [(v, pre(v)) for v in get_vars(f)] return substitute(f, *vss)
def get_state_vars(f, exclude_vars): """ f: a formula, e.g. the transition formula exclude_vars: variables that are not considered as state vars, e.g. independent variables From Verifying Safety Property of Lustre Programs: Temporal Induction Let L be a Lustre program, S be the tuple of L's state variables, non-input variables that occur within a pre. Then only non-input variables that occur within a pre are considered program states node test( X: bool ) returns ( P : bool ); var A, B, C : bool; let A = X -> pre A; B = not (not X -> pre(C)); C = not B; P = A = B; #property to be proved, not important, not part of input >>> from z3 import Bools, Implies, Ints, If, Or #states variables = [A,C] >>> A,A_pre,B,B_pre,C,C_pre,X = Bools('A A_pre B B_pre C C_pre X') >>> af = A == A_pre >>> bf = B == Not(Not(Implies(X,C_pre))) >>> cf = C == Not(B) >>> trans = And(af,bf,cf) >>> exclude_vars = [X] >>> KIP.get_state_vars(trans, exclude_vars) [A, C] >>> af = A == Implies(X,A_pre) >>> bf = B == Not(Implies(Not(X),C_pre)) >>> cf = C == Not(B) >>> trans = And(af,bf,cf) >>> exclude_vars = [X] >>> KIP.get_state_vars(trans, exclude_vars) [A, C] #state variables = c , R is not included because R_pre is not used >>> R,c_pre,c = Ints('R c_pre c') >>> trans = If(Or(R == 100, c_pre == 2), c == 0, c == c_pre + 1) >>> KIP.get_state_vars(trans, exclude_vars=[]) [c] #this algorithm would return [], instead of [C] #this is by design because it doesn't make sense to have #C_pre without C in trans >>> KIP.get_state_vars(A == C_pre,[]) [] """ if __debug__: assert f is None or is_expr(f), f assert is_list(exclude_vars), exclude_vars if f is None: return [] #start with state_vars being all vars in trans state_vars = get_vars(f) #keep those that not being excluded state_vars = [ v for v in state_vars if not expr_member(v, exclude_vars) ] #keep those v that are not pre but v's pre is part of the f state_vars = [ v for v in state_vars if not is_pre(v) and expr_member(pre(v), state_vars) ] if __debug__: assert all(not is_pre(v) for v in state_vars) return state_vars
def get_influence_vs(v, defs, rs): """ Return a list of variables that influences v (i.e. the definition of v depends on these variables) >>> from z3 import Bools, BoolVal >>> from scr_miscs import mk_OIA >>> s,t = Bools('s t') >>> x,y,z = Bools('x y z') >>> vs = [x,y,z] >>> o = mk_OIA(vs) >>> vv = [o,o,And(o,s)] >>> vs2 = [t] >>> vv2 = [BoolVal(True)] >>> vs_k = map(fhash,vs + vs2) >>> vs_v =vv + vv2 >>> defs = OrderedDict(zip(vs_k,vs_v)) >>> print Prog.get_influence_vs(x,defs,rs=[]) [s, y, z] #>>> print Prog.get_influence_vs(x,defs,assumes=[x==s],rs=[]) #[s, y, z] #>>> print Prog.get_influence_vs(x,defs,assumes=[y==t],rs=[]) #[s, y, z, t] """ if __debug__: assert is_expr_var(v), v assert is_dict(defs), defs assert is_list(rs), rs if is_pre(v): v = cur(v) #return if already in the result set if expr_member(v, rs): return rs try: vs = get_vars(defs[fhash(v)]) #print vs except KeyError: return rs rs = rs + [v] #convert v_pre to v vs = [cur(v_) if is_pre(v_) else v_ for v_ in vs] vs = vset(vs, fhash) for v_ in vs: rs_ = Prog.get_influence_vs(v_, defs, rs) rs = rs + rs_ rs = rs + vs rs = vset(rs, fhash) #remove myself v_idx = map(fhash, rs).index(fhash(v)) rs = rs[:v_idx] + rs[v_idx + 1:] return sorted(rs, key=str)
def get_all_vars(list_of_storage_exprs): ret_vars = [] for expr in list_of_storage_exprs: ret_vars += get_vars(list_of_storage_exprs[expr]) return ret_vars
def has_storage_vars(expr, storage_vars): list_vars = get_vars(expr) for var in list_vars: if var in storage_vars: return True return False
def is_in_expr(var, expr): list_vars = get_vars(expr) set_vars = set(i.decl().name() for i in list_vars) return var in set_vars