def eval_lambda(f, d, is_formula=True): """ Evaluates lambda function f Examples: sage: assert InvMPP.eval_lambda('lambda x,y: max(x - 13,-3) >= y', {'x':11,'y':100}) == False sage: assert InvMPP.eval_lambda('lambda x,y: x+y == 5', {'x':2,'y':3,'d':7}) sage: assert InvMPP.eval_lambda('lambda x,y: x+y == 6', {'x':2,'y':3,'d':7}) == False sage: assert InvMPP.eval_lambda('lambda x,y: x+y', {'x':2,'y':3,'d':7}, is_formula=False) == 5 sage: assert InvMPP.eval_lambda('lambda x,y: x+y == 10 or x + y == 5', {'x':2,'y':3,'d':7}) sage: assert InvMPP.eval_lambda('lambda x,y: x+y == 1 or x + y == 2', {'x':2,'y':3,'d':7}) == False """ if __debug__: assert is_str(f) and 'lambda' in f, f assert is_dict(d), d assert all(is_str(k) for k in d), d.keys() f = sage_eval(f) vs = f.func_code.co_varnames assert set(vs) <= set(d.keys()), (vs, d.keys()) #if d has more keys than variables in f then remove those extra keys d = dict([(k, d[k]) for k in vs]) rs = f(**d) return bool(rs) if is_formula else rs
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 eval_lambda(f, d, is_formula=True): """ Evaluates lambda function f Examples: sage: assert InvMPP.eval_lambda('lambda x,y: max(x - 13,-3) >= y', {'x':11,'y':100}) == False sage: assert InvMPP.eval_lambda('lambda x,y: x+y == 5', {'x':2,'y':3,'d':7}) sage: assert InvMPP.eval_lambda('lambda x,y: x+y == 6', {'x':2,'y':3,'d':7}) == False sage: assert InvMPP.eval_lambda('lambda x,y: x+y', {'x':2,'y':3,'d':7}, is_formula=False) == 5 sage: assert InvMPP.eval_lambda('lambda x,y: x+y == 10 or x + y == 5', {'x':2,'y':3,'d':7}) sage: assert InvMPP.eval_lambda('lambda x,y: x+y == 1 or x + y == 2', {'x':2,'y':3,'d':7}) == False """ if __debug__: assert is_str(f) and 'lambda' in f, f assert is_dict(d), d assert all(is_str(k) for k in d), d.keys() f = sage_eval(f) vs = f.func_code.co_varnames assert set(vs) <= set(d.keys()), (vs,d.keys()) #if d has more keys than variables in f then remove those extra keys d=dict([(k,d[k]) for k in vs]) rs = f(**d) return bool(rs) if is_formula else rs
def __init__(self, init_conds, defs, input_vars, assumes): """ This class models a program using 1. initial condition 2. transition (definitions of updated variables) 3. assumptions Input variables: - init_cond: list of initial conditions e.g. [Block == Off,Reset == On,WaterPres == wp_init_val, Overridden == False,SafetyInjection == On,Pressure == TooLow] - defs: a dictionary consisting variables being updated by the transition - input_vars: variables that are INDEPDENT. SCR programs don't have these, because input (monitored) vars are dependent due to OIA - assumes: list of assumptions Two types of assumptions: (1) state assumes: those for each *state* e.g. And(0 <= WaterPres,WaterPres < 2000): WaterPres is in range 0,2000 at any state (2) trans assumes: those for each *transition* e.g. One Input Assumption asserts only 1 var can changed at a time or And(pre(WaterPres) - 10 <= WaterPres, WaterPres <= pre(WaterPres) + 10) """ if __debug__: assert is_list(init_conds) and \ all(is_state(c) for c in init_conds), init_conds assert is_dict(defs) and \ all(is_expr(v) for v in defs.values()), defs assert is_list(input_vars) and \ all(is_expr_var(v) for v in input_vars), input_vars assert is_list(assumes) and \ all(is_expr(a) for a in assumes), assumes self.defs = defs self.init_conds = init_conds self.input_vars = input_vars self.assumes_state = [] self.assumes_trans = [] for a in assumes: Prog.append_f(a, self.assumes_state, self.assumes_trans) #Known invariants (lemmas). Use add_inv() to add an inv as lemma self.invs_state = [] self.invs_trans = []
def __init__(self, p): """ Ex: ('lambda A, B, A0: (A[A0]) + (-7*B[2*A0]) + (-3*A0) == 0', [{'A0': 2}, {'A0': 0}, {'A0': 1}]) """ if __debug__: assert (is_tuple(p) and len(p) == 2 and is_str(p[0]) and all(s in p[0] for s in ['lambda', '[', ']']) and is_list(p[1]) and all(is_dict(v) for v in p[1])), p super(InvFlatArray, self).__init__(p[0].strip()) self.idx_info = p[1]
def __init__(self, p): """ Ex: ('lambda A, B, A0: (A[A0]) + (-7*B[2*A0]) + (-3*A0) == 0', [{'A0': 2}, {'A0': 0}, {'A0': 1}]) """ if __debug__: assert (is_tuple(p) and len(p)==2 and is_str(p[0]) and all(s in p[0] for s in ['lambda','[' ,']']) and is_list(p[1]) and all(is_dict(v) for v in p[1])), p super(InvFlatArray,self).__init__(p[0].strip()) self.idx_info = p[1]
def __init__(self, terms, tcs, xinfo): assert is_dict(xinfo), xinfo logger.info('*** {} ***'.format(self.__class__.__name__)) self.terms = terms self.tcs = tcs self.xinfo = xinfo self.do_refine = True self.tcs_extra = [] self._sols = [] logger.debug('|terms|={}, |tcs|={}'.format(len(self.terms), len(self.tcs)))
def preprocess(self, xinfo): """ Preprocess input data 1) transforms external functions to special arrays 1) change arr repr to value->index repr to speed up arr idx lookup 2) generate nodes """ if __debug__: assert is_dict(xinfo), xinfo evs = ExtFun.gen_extvars(xinfo=xinfo) #arrays only evs = [OrderedDict([(k,v) for k,v in ev.iteritems() if is_list(v)]) for ev in evs] evs = Miscs.keys_to_str(evs) if not is_empty(evs): #add to traces self.tcs = [merge_dict(evs + [tc]) for tc in self.tcs] self.tcs_extra = [merge_dict(evs + [tc]) for tc in self.tcs_extra] mytcs = [] for tc in self.tcs: #arrs reprent ext funs (already in new format) efs = ExtFun.gen_extfuns(tc=tc,xinfo=xinfo) #convert normal arr format to new format arrs = [(k, Miscs.getListIdxs(v)) for k,v in tc.items()] arrs = OrderedDict(arrs) d = merge_dict(efs + [arrs]) mytcs.append(d) self.tcs = mytcs self.trees = [Tree({'root':k, 'children': [None] * len(c.values()[0][0]), 'commute': ExtFun(k).is_commute()}) for k,c in self.tcs[0].items()] self.xinfo = xinfo
def eval_lambda(f, idx_info, tc): """ Evaluate array expression p, e.g. p: A[i,j,k]=B[2i+3j+k] if idx_info is specified then only test p on the idxs from idx_info Assumes: the first array in lambda is the pivot lambda A,B,i,j,k: ... => i,j,k belong to A inv = 'lambda B,C,D,i,j: B[i][j]=C[D[2i+3]]' returns true if inv is true on tc Examples: sage: var('a,b,c,i,j') (a, b, c, i, j) sage: InvArray.eval_lambda('lambda a,b,c,i,j: a[i][j]==2*b[i]+c[j]', None, {'a':[[4,-5],[20,11]],'b':[1,9],'c':[2,-7]}) True sage: InvArray.eval_lambda('lambda c,a,b,xor,i: c[i] == xor(a[i],b[i])', None, {'a': [147, 156, 184, 76], 'b': [51, 26, 247, 189], 'c': [160, 334, 79, 281]}) False sage: InvArray.eval_lambda('lambda c,a,b,xor,i1: c[i1] == xor(a[i1],b[i1])', None, {'a': [147, 156, 184, 76], 'b': [51, 26, 247, 189], 'c': [160, 134, 79, 241]}) True sage: InvArray.eval_lambda('lambda rvu, t, rvu1, rvu0: (rvu[rvu0][rvu1]) + (-t[4*rvu0 + rvu1]) == 0', None, {'t': [28, 131, 11, 85, 133, 46, 179, 20, 227, 148, 225, 197, 38, 221, 221, 126], 'rvu': [[28, 131, 11, 85], [133, 46, 179, 20], [227, 148, 225, 197], [38, 221, 221, 126]]}) True #The following illustrate the use of idxVals, #i.e. p is only true under certain array rages sage: InvArray.eval_lambda('lambda st, rvu, st0, st1: (-st[st0][st1]) + (rvu[4*st0 + st1]) == 0', None, tc = {'rvu': [28, 131, 11, 85, 193, 124, 103, 215, 66, 26, 68, 54, 176, 102, 15, 237], 'st': [[28, 131, 11, 85, 133, 46, 179, 20, 227, 148, 225, 197, 38, 221, 221, 126], [193, 124, 103, 215, 106, 229, 162, 168, 166, 78, 144, 234, 199, 254, 152, 250], [66, 26, 68, 54, 206, 16, 155, 248, 231, 198, 240, 43, 208, 205, 213, 26], [176, 102, 15, 237, 49, 141, 213, 97, 137, 155, 50, 243, 112, 51, 124, 107]]}) False sage: InvArray.eval_lambda('lambda st, rvu, st0, st1: (-st[st0][st1]) + (rvu[4*st0 + st1]) == 0', idx_info = [{'st0': 0, 'st1': 0}, {'st0': 0, 'st1': 1}, {'st0': 2, 'st1': 2}, {'st0': 2, 'st1': 3}, {'st0': 3, 'st1': 0}, {'st0': 3, 'st1': 1}, {'st0': 3, 'st1': 2}, {'st0': 3, 'st1': 3}, {'st0': 0, 'st1': 2}, {'st0': 0, 'st1': 3}, {'st0': 1, 'st1': 0}, {'st0': 1, 'st1': 1}, {'st0': 1, 'st1': 2}, {'st0': 1, 'st1': 3}, {'st0': 2, 'st1': 0}, {'st0': 2, 'st1': 1}], tc = {'rvu': [28, 131, 11, 85, 193, 124, 103, 215, 66, 26, 68, 54, 176, 102, 15, 237], 'st': [[28, 131, 11, 85, 133, 46, 179, 20, 227, 148, 225, 197, 38, 221, 221, 126], [193, 124, 103, 215, 106, 229, 162, 168, 166, 78, 144, 234, 199, 254, 152, 250], [66, 26, 68, 54, 206, 16, 155, 248, 231, 198, 240, 43, 208, 205, 213, 26], [176, 102, 15, 237, 49, 141, 213, 97, 137, 155, 50, 243, 112, 51, 124, 107]]}) True """ """ Note: sage_eval vs eval sage_eval works on str of the format 'lambda x,y: 2*x+y' whereas eval works on str of the format 2*x+y directly (no lambda) Also, the result of sage_eval can apply on dicts whose keys are str e.g. f(**{'x':2,'y':3}) whereas the result of eval applies on dict whose keys are variables e.g. f(**{x:2,y:3}) """ if __debug__: assert is_str(f) and 'lambda' in f, f assert (idx_info is None or is_list(idx_info) and all(is_dict(v) for v in idx_info)), indx_info assert is_dict(tc), tc assert all(is_str(k) for k in tc), tc.keys() f = sage_eval(f) vs = f.func_code.co_varnames arrs = [v for v in vs if v in tc] #A,B extfuns = [v for v in vs if v in ExtFun.efdict] idxStr = [v for v in vs if v not in arrs + extfuns] #i,j,k d_tc = dict([(v, tc[v]) for v in arrs]) d_extfun = dict([(v, ExtFun(v).get_fun()) for v in extfuns]) d_ = merge_dict([d_tc, d_extfun]) if idx_info is None: #obtain idxsVals from the pivot array pivotContents = tc[arrs[0]] idxVals = [idx for idx, _ in Miscs.travel(pivotContents)] idx_info = [dict(zip(idxStr, idxV)) for idxV in idxVals] ds = [merge_dict([d_, idx_info_]) for idx_info_ in idx_info] try: return all(f(**d) for d in ds) except IndexError: return False except TypeError: return False except NameError as msg: logger.warn(msg) return False
def eval_lambda(f, idx_info, tc): """ Evaluate array expression p, e.g. p: A[i,j,k]=B[2i+3j+k] if idx_info is specified then only test p on the idxs from idx_info Assumes: the first array in lambda is the pivot lambda A,B,i,j,k: ... => i,j,k belong to A inv = 'lambda B,C,D,i,j: B[i][j]=C[D[2i+3]]' returns true if inv is true on tc Examples: sage: var('a,b,c,i,j') (a, b, c, i, j) sage: InvArray.eval_lambda('lambda a,b,c,i,j: a[i][j]==2*b[i]+c[j]', None, {'a':[[4,-5],[20,11]],'b':[1,9],'c':[2,-7]}) True sage: InvArray.eval_lambda('lambda c,a,b,xor,i: c[i] == xor(a[i],b[i])', None, {'a': [147, 156, 184, 76], 'b': [51, 26, 247, 189], 'c': [160, 334, 79, 281]}) False sage: InvArray.eval_lambda('lambda c,a,b,xor,i1: c[i1] == xor(a[i1],b[i1])', None, {'a': [147, 156, 184, 76], 'b': [51, 26, 247, 189], 'c': [160, 134, 79, 241]}) True sage: InvArray.eval_lambda('lambda rvu, t, rvu1, rvu0: (rvu[rvu0][rvu1]) + (-t[4*rvu0 + rvu1]) == 0', None, {'t': [28, 131, 11, 85, 133, 46, 179, 20, 227, 148, 225, 197, 38, 221, 221, 126], 'rvu': [[28, 131, 11, 85], [133, 46, 179, 20], [227, 148, 225, 197], [38, 221, 221, 126]]}) True #The following illustrate the use of idxVals, #i.e. p is only true under certain array rages sage: InvArray.eval_lambda('lambda st, rvu, st0, st1: (-st[st0][st1]) + (rvu[4*st0 + st1]) == 0', None, tc = {'rvu': [28, 131, 11, 85, 193, 124, 103, 215, 66, 26, 68, 54, 176, 102, 15, 237], 'st': [[28, 131, 11, 85, 133, 46, 179, 20, 227, 148, 225, 197, 38, 221, 221, 126], [193, 124, 103, 215, 106, 229, 162, 168, 166, 78, 144, 234, 199, 254, 152, 250], [66, 26, 68, 54, 206, 16, 155, 248, 231, 198, 240, 43, 208, 205, 213, 26], [176, 102, 15, 237, 49, 141, 213, 97, 137, 155, 50, 243, 112, 51, 124, 107]]}) False sage: InvArray.eval_lambda('lambda st, rvu, st0, st1: (-st[st0][st1]) + (rvu[4*st0 + st1]) == 0', idx_info = [{'st0': 0, 'st1': 0}, {'st0': 0, 'st1': 1}, {'st0': 2, 'st1': 2}, {'st0': 2, 'st1': 3}, {'st0': 3, 'st1': 0}, {'st0': 3, 'st1': 1}, {'st0': 3, 'st1': 2}, {'st0': 3, 'st1': 3}, {'st0': 0, 'st1': 2}, {'st0': 0, 'st1': 3}, {'st0': 1, 'st1': 0}, {'st0': 1, 'st1': 1}, {'st0': 1, 'st1': 2}, {'st0': 1, 'st1': 3}, {'st0': 2, 'st1': 0}, {'st0': 2, 'st1': 1}], tc = {'rvu': [28, 131, 11, 85, 193, 124, 103, 215, 66, 26, 68, 54, 176, 102, 15, 237], 'st': [[28, 131, 11, 85, 133, 46, 179, 20, 227, 148, 225, 197, 38, 221, 221, 126], [193, 124, 103, 215, 106, 229, 162, 168, 166, 78, 144, 234, 199, 254, 152, 250], [66, 26, 68, 54, 206, 16, 155, 248, 231, 198, 240, 43, 208, 205, 213, 26], [176, 102, 15, 237, 49, 141, 213, 97, 137, 155, 50, 243, 112, 51, 124, 107]]}) True """ """ Note: sage_eval vs eval sage_eval works on str of the format 'lambda x,y: 2*x+y' whereas eval works on str of the format 2*x+y directly (no lambda) Also, the result of sage_eval can apply on dicts whose keys are str e.g. f(**{'x':2,'y':3}) whereas the result of eval applies on dict whose keys are variables e.g. f(**{x:2,y:3}) """ if __debug__: assert is_str(f) and 'lambda' in f, f assert (idx_info is None or is_list(idx_info) and all(is_dict(v) for v in idx_info)), indx_info assert is_dict(tc), tc assert all(is_str(k) for k in tc), tc.keys() f = sage_eval(f) vs = f.func_code.co_varnames arrs = [v for v in vs if v in tc] #A,B extfuns = [v for v in vs if v in ExtFun.efdict] idxStr = [v for v in vs if v not in arrs+extfuns] #i,j,k d_tc = dict([(v,tc[v]) for v in arrs]) d_extfun= dict([(v,ExtFun(v).get_fun()) for v in extfuns]) d_ = merge_dict([d_tc,d_extfun]) if idx_info is None: #obtain idxsVals from the pivot array pivotContents = tc[arrs[0]] idxVals = [idx for idx,_ in Miscs.travel(pivotContents)] idx_info = [dict(zip(idxStr,idxV)) for idxV in idxVals] ds = [merge_dict([d_, idx_info_]) for idx_info_ in idx_info] try: return all(f(**d) for d in ds) except IndexError: return False except TypeError: return False except NameError as msg: logger.warn(msg) return False
def compute_new_trace(d, ainfo, tsinfo): """ Convert array elements into new variables and generate traces from these. Examples: sage: var('A B') (A, B) sage: ainfo = {} sage: tc = FlatArray.compute_new_trace({A: [0,1,2], B: [[0, 1], [2, 3]]}, ainfo, {}) sage: sorted(tc.items(),key= lambda (x,_) : str(x)) [(A_0, 0), (A_1, 1), (A_2, 2), (B_0_0, 0), (B_0_1, 1), (B_1_0, 2), (B_1_1, 3)] sage: sorted(ainfo.items(), key=lambda (x,_): str(x)) [(A_0, {'idx_': [(A0, 0)], 'name': 'A'}), (A_1, {'idx_': [(A0, 1)], 'name': 'A'}), (A_2, {'idx_': [(A0, 2)], 'name': 'A'}), (B_0_0, {'idx_': [(B0, 0), (B1, 0)], 'name': 'B'}), (B_0_1, {'idx_': [(B0, 0), (B1, 1)], 'name': 'B'}), (B_1_0, {'idx_': [(B0, 1), (B1, 0)], 'name': 'B'}), (B_1_1, {'idx_': [(B0, 1), (B1, 1)], 'name': 'B'})] sage: ainfo = {} sage: tc = FlatArray.compute_new_trace({'A':[['a','b'],['c','d'],['e','f',['z','w']]], \ 'B':[1,2,[7,8]],'C':[100]}, ainfo, {}) sage: sorted(tc.items(),key= lambda (x,_) : str(x)) [(A_0_0, 'a'), (A_0_1, 'b'), (A_1_0, 'c'), (A_1_1, 'd'), (A_2_0, 'e'), (A_2_1, 'f'), (A_2_2_0, 'z'), (A_2_2_1, 'w'), (B_0, 1), (B_1, 2), (B_2_0, 7), (B_2_1, 8), (C_0, 100)] sage: sorted(ainfo.items(), key=lambda (x,_): str(x)) [(A_0_0, {'idx_': [(A0, 0), (A1, 0)], 'name': 'A'}), (A_0_1, {'idx_': [(A0, 0), (A1, 1)], 'name': 'A'}), (A_1_0, {'idx_': [(A0, 1), (A1, 0)], 'name': 'A'}), (A_1_1, {'idx_': [(A0, 1), (A1, 1)], 'name': 'A'}), (A_2_0, {'idx_': [(A0, 2), (A1, 0)], 'name': 'A'}), (A_2_1, {'idx_': [(A0, 2), (A1, 1)], 'name': 'A'}), (A_2_2_0, {'idx_': [(A0, 2), (A1, 2), (A2, 0)], 'name': 'A'}), (A_2_2_1, {'idx_': [(A0, 2), (A1, 2), (A2, 1)], 'name': 'A'}), (B_0, {'idx_': [(B0, 0)], 'name': 'B'}), (B_1, {'idx_': [(B0, 1)], 'name': 'B'}), (B_2_0, {'idx_': [(B0, 2), (B1, 0)], 'name': 'B'}), (B_2_1, {'idx_': [(B0, 2), (B1, 1)], 'name': 'B'}), (C_0, {'idx_': [(C0, 0)], 'name': 'C'})] """ def compute_traces(aname, acontents, ainfo, tsinfo): vi = Miscs.travel(acontents) vals = Miscs.getVals(vi) idxs = Miscs.getIdxs(vi) aname = str(aname) newvars = [var(aname + '_' + list_str(idx, '_')) for idx in idxs] if aname not in tsinfo: tsinfo[aname] = newvars else: assert tsinfo[aname] == newvars dVals = dict(zip(newvars,vals)) #{A_0_0_1:'w'} for nv,idx in zip(newvars,idxs): if nv not in ainfo: idx_ = zip([var('{}{}'.format(aname,li)) for li in srange(len(idx))],idx) ainfo[nv]={'name':aname, 'idx_':idx_} return dVals if __debug__: assert is_dict(d), d assert is_dict(ainfo), d tcs = [compute_traces(k,v,ainfo,tsinfo) for k,v in d.iteritems()] tcs = merge_dict(tcs) return tcs
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)