def recur(action,annot,env,pos=None): if isinstance(annot,ia.RenameAnnotation): save = dict() for x,y in annot.map.iteritems(): if x in env: save[x] = env[x] env[x] = env.get(y,y) recur(action,annot.arg,env,pos) env.update(save) return if isinstance(action,ia.Sequence): if pos is None: pos = len(action.args) if pos == 0: assert isinstance(annot,ia.EmptyAnnotation),annot return if not isinstance(annot,ia.ComposeAnnotation): iu.dbg('len(action.args)') iu.dbg('pos') iu.dbg('annot') assert isinstance(annot,ia.ComposeAnnotation) recur(action,annot.args[0],env,pos-1) recur(action.args[pos-1],annot.args[1],env) return if isinstance(action,ia.IfAction): assert isinstance(annot,ia.IteAnnotation),annot rncond = env.get(annot.cond,annot.cond) try: cond = handler.eval(rncond) except KeyError: print '{}skipping conditional'.format(action.lineno) iu.dbg('str_map(env)') iu.dbg('env.get(annot.cond,annot.cond)') return if cond: recur(action.args[1],annot.thenb,env) else: if len(action.args) > 2: recur(action.args[2],annot.elseb,env) return if isinstance(action,ia.ChoiceAction): assert isinstance(annot,ia.IteAnnotation) annots = unite_annot(annot) assert len(annots) == len(action.args) for act,(cond,ann) in reversed(zip(action.args,annots)): if handler.eval(cond): recur(act,ann,env) return assert False,'problem in match_annotation' if isinstance(action,ia.CallAction): callee = im.module.actions[action.args[0].rep] seq = ia.Sequence(*([ia.Sequence() for x in callee.formal_params] + [callee] + [ia.Sequence() for x in callee.formal_returns])) recur(seq,annot,env) return if isinstance(action,ia.LocalAction): recur(action.args[-1],annot,env) return handler.handle(action,env)
def check_concretely_sorted(term,no_error=False,unsorted_var_names=()): for x in chain(lu.used_variables(term),lu.used_constants(term)): if lg.contains_topsort(x.sort) or lg.is_polymorphic(x.sort): iu.dbg('unsorted_var_names') iu.dbg('x.name') if x.name not in unsorted_var_names: if no_error: raise lg.SortError raise IvyError(None,"cannot infer sort of {} in {}".format(x,term))
def get_model_clauses(clauses1): s = z3.Solver() z3c = clauses_to_z3(clauses1) s.add(z3c) iu.dbg('"before check"') res = s.check() iu.dbg('"after check"') if res == z3.unsat: return None m = get_model(s) return HerbrandModel(s, m, used_symbols_clauses(clauses1))
def get_model_clauses(clauses1): s = z3.Solver() z3c = clauses_to_z3(clauses1) s.add(z3c) iu.dbg('"before check"') res = s.check() iu.dbg('"after check"') if res == z3.unsat: return None m = get_model(s) return HerbrandModel(s,m,used_symbols_clauses(clauses1))
def check_conjectures(kind,msg,ag,state): failed = itp.undecided_conjectures(state) if failed: if diagnose.get(): print "{} failed.".format(kind) iu.dbg('ag.states[0].clauses') gui = ui.new_ui() agui = gui.add(ag) gui.tk.update_idletasks() # so that dialog is on top of main window agui.try_conjecture(state,msg="{}\nChoose one to see counterexample.".format(msg),bound=1) gui.tk.mainloop() exit(1) raise iu.IvyError(None,"{} failed.".format(kind))
def __call__(self,s): if s.startswith('$'): path = s[1:].split('.') num = int(path[0])-1 if num < 0 or num >= len(self.anchor.args): raise iu.IvyError(None,'event has no argument {}'.format(s)) res = self.anchor.args[num] for field in path[1:]: if not isinstance(res,DictValue) or field not in res: raise iu.IvyError(None,'value has no field {}'.format(field)) res = res[field] iu.dbg('res') return res return Symbol(s)
def compile_action_def(a,sig): sig = sig.copy() if not hasattr(a.args[1],'lineno'): print a assert hasattr(a.args[1],'lineno') with sig: with ASTContext(a.args[1]): params = a.args[0].args pformals = [v.to_const('prm:') for v in params] if params: subst = dict((x.rep,y) for x,y in zip(params,pformals)) a = ivy_ast.substitute_ast(a,subst) assert hasattr(a.args[1],'lineno') # a = ivy_ast.subst_prefix_atoms_ast(a,subst,None,None) # print "after: %s" % (a) # convert object paramaters to arguments (object-orientation!) formals = [compile_const(v,sig) for v in pformals + a.formal_params] returns = [compile_const(v,sig) for v in a.formal_returns] # print returns res = sortify(a.args[1]) assert hasattr(res,'lineno'), res for suba in res.iter_subactions(): if isinstance(suba,CallAction): if any(lu.used_variables_ast(a) for a in suba.args[0].args): iu.dbg('a.args[0]') iu.dbg('a.formal_params') iu.dbg('suba.lineno') iu.dbg('suba') raise iu.IvyError(suba,"call may not have free variables") res.formal_params = formals res.formal_returns = returns res.label = a.args[0].relname return res
def __call__(self, s): if s.startswith('$'): path = s[1:].split('.') num = int(path[0]) - 1 if num < 0 or num >= len(self.anchor.args): raise iu.IvyError(None, 'event has no argument {}'.format(s)) res = self.anchor.args[num] for field in path[1:]: if not isinstance(res, DictValue) or field not in res: raise iu.IvyError(None, 'value has no field {}'.format(field)) res = res[field] iu.dbg('res') return res return Symbol(s)
def check_conjectures(kind, msg, ag, state): failed = itp.undecided_conjectures(state) if failed: if diagnose.get(): print "{} failed.".format(kind) iu.dbg('ag.states[0].clauses') gui = ui.new_ui() agui = gui.add(ag) gui.tk.update_idletasks( ) # so that dialog is on top of main window agui.try_conjecture( state, msg="{}\nChoose one to see counterexample.".format(msg), bound=1) gui.tk.mainloop() exit(1) raise iu.IvyError(None, "{} failed.".format(kind))
def get_mixin_order(iso,mod): arcs = [(rdf.args[0].relname,rdf.args[1].relname) for rdf in mod.mixord] actions = mod.mixins.keys() for action in actions: mixins = mod.mixins[action] mixers = iu.topological_sort(list(set(m.mixer() for m in mixins)),arcs) iu.dbg('mixers') keymap = dict((x,y) for y,x in enumerate(mixers)) key = lambda m: keymap[m.mixer()] before = sorted([m for m in mixins if isinstance(m,ivy_ast.MixinBeforeDef)],key=key) after = sorted([m for m in mixins if isinstance(m,ivy_ast.MixinAfterDef)],key=key) # order = SortOrder(arcs) # before = sorted([m for m in mixins if isinstance(m,ivy_ast.MixinBeforeDef)],order) # after = sorted([m for m in mixins if isinstance(m,ivy_ast.MixinAfterDef)],order) before.reverse() # add the before mixins in reverse order mixins = before + after # print 'mixin order for action {}:' # for m in mixins: # print m.args[0] mod.mixins[action] = mixins
def try_conjecture(self,node,conj=None,msg=None,bound=None): if conj == None: udc = undecided_conjectures(node) udc_text = [repr(clauses_to_formula(conj)) for conj in udc] msg = msg or "Choose a conjecture to prove:" cmd = lambda idx: self.try_conjecture(node,udc[idx],bound=bound) self.ui_parent.listbox_dialog(msg,udc_text,command=cmd) else: if hasattr(conj,'lineno'): filename,lineno = conj.lineno self.ui_parent.browse(filename,lineno) dual = dual_clauses(conj) if self.mode.get() == "induction": iu.dbg('node.clauses') iu.dbg('dual') self.bmc(node,dual,bound=bound) else: sg = self.g.concept_graph(node) sg.current.add_constraints(dual.conjuncts) self.show_graph(sg)
def __call__(self,x,y): x = x.args[0].relname y = y.args[0].relname iu.dbg('x') iu.dbg('y') res = -1 if y in self.arcs[x] else 1 if x in self.arcs[y] else 0 iu.dbg('res') return res
def sort_infer_list(terms,sorts=None,no_error=False,unsorted_var_names=()): iu.dbg('[str(t) for t in terms]') iu.dbg('sorts') res = concretize_terms(terms,sorts) for term in res: check_concretely_sorted(term,no_error,unsorted_var_names) iu.dbg('res') return res
def rename_vars_no_clash(fmlas1,fmlas2): """ Rename the free variables in formula list fmlas1 so they occur nowhere in fmlas2, avoiding capture """ uvs = lu.used_variables(*fmlas2) uvs = lu.union(uvs,lu.bound_variables(*fmlas1)) iu.dbg('uvs') rn = iu.UniqueRenamer('',(v.name for v in uvs)) vs = lu.free_variables(*fmlas1) iu.dbg('vs') vmap = dict((v,Variable(rn(v.name),v.sort)) for v in vs) iu.dbg('vmap') return [lu.substitute(f,vmap) for f in fmlas1]
def compile_action_def(a, sig): sig = sig.copy() if not hasattr(a.args[1], 'lineno'): print a assert hasattr(a.args[1], 'lineno') with sig: with ASTContext(a.args[1]): params = a.args[0].args pformals = [v.to_const('prm:') for v in params] if params: subst = dict((x.rep, y) for x, y in zip(params, pformals)) a = ivy_ast.substitute_ast(a, subst) assert hasattr(a.args[1], 'lineno') # a = ivy_ast.subst_prefix_atoms_ast(a,subst,None,None) # print "after: %s" % (a) # convert object paramaters to arguments (object-orientation!) formals = [ compile_const(v, sig) for v in pformals + a.formal_params ] returns = [compile_const(v, sig) for v in a.formal_returns] # print returns res = sortify(a.args[1]) assert hasattr(res, 'lineno'), res for suba in res.iter_subactions(): if isinstance(suba, CallAction): if any( lu.used_variables_ast(a) for a in suba.args[0].args): iu.dbg('a.args[0]') iu.dbg('a.formal_params') iu.dbg('suba.lineno') iu.dbg('suba') raise iu.IvyError(suba, "call may not have free variables") res.formal_params = formals res.formal_returns = returns res.label = a.args[0].relname return res
def action_update(self, domain, pvars): lhs, rhs = self.args n = lhs.rep # Handle the hierarchical case if n in domain.hierarchy: asgns = [ postfix_atoms_ast(self, Atom(x, [])) for x in domain.hierarchy[n] ] res = unzip_append( [asgn.action_update(domain, pvars) for asgn in asgns]) return res # If the lhs application is partial, make it total by adding parameters xtra = len(lhs.rep.sort.dom) - len(lhs.args) if xtra < 0: raise IvyError(self, "too many parameters in assignment to " + lhs.rep) if xtra > 0: extend = sym_placeholders(lhs.rep)[-xtra:] extend = variables_distinct_list_ast(extend, self) # get unused variables lhs = add_parameters_ast(lhs, extend) # Assignment of individual to a boolean is a special case if is_individual_ast(rhs) and not is_individual_ast(lhs): rhs = eq_atom(extend[-1], add_parameters_ast(rhs, extend[0:-1])) else: rhs = add_parameters_ast(rhs, extend) lhs_vars = used_variables_ast(lhs) if any(v not in lhs_vars for v in used_variables_ast(rhs)): raise IvyError(self, "multiply assigned: {}".format(lhs.rep)) type_check(domain, rhs) if is_individual_ast(lhs) != is_individual_ast(rhs): # print type(lhs.rep) # print str(lhs.rep) # print type(lhs.rep.sort) # print "lhs: %s: %s" % (lhs,type(lhs)) # print "rhs: %s: %s" % (rhs,type(rhs)) raise IvyError(self, "sort mismatch in assignment to {}".format(lhs.rep)) # For a destructor assignment, we actually mutate the first argument if n.name in ivy_module.module.destructor_sorts: mut = lhs.args[0] rest = list(lhs.args[1:]) mut_n = mut.rep nondet = mut_n.suffix("_nd").skolem() new_clauses = mk_assign_clauses(mut_n, nondet(*sym_placeholders(mut_n))) fmlas = [] nondet_lhs = lhs.rep(*([nondet(*mut.args)] + rest)) fmlas.append(equiv_ast(nondet_lhs, rhs)) vs = sym_placeholders(n) dlhs = n(*([nondet(*mut.args)] + vs[1:])) drhs = n(*([mut] + vs[1:])) eqs = [ eq_atom(v, a) for (v, a) in zip(vs, lhs.args)[1:] if not isinstance(a, Variable) ] if eqs: fmlas.append(Or(And(*eqs), equiv_ast(dlhs, drhs))) new_clauses = and_clauses(new_clauses, Clauses(fmlas)) dbg('new_clauses') return ([mut_n], new_clauses, false_clauses()) new_clauses = mk_assign_clauses(lhs, rhs) # print "assign new_clauses = {}".format(new_clauses) return ([n], new_clauses, false_clauses())
def map(self, fun): iu.dbg('fun') return fun(self.name)
def map(self,fun): iu.dbg('fun') return fun(self.name)
def __init__(self,arcs): self.arcs = arcs iu.dbg('arcs')
usage() with im.Module(): isolate = ivy_compiler.isolate.get() ivy.source_file(sys.argv[1],ivy.open_read(sys.argv[1]),create_isolate=False) # If user specifies an isolate, check it. Else, if any isolates # are specificied in the file, check all, else check globally. if isolate != None: isolates = [isolate] else: isolates = sorted(list(im.module.isolates)) if len(isolates) == 0: isolates = [None] for isolate in isolates: if len(im.module.isolates[isolate].verified()) == 0: continue # skip if nothing to verify iu.dbg('isolate') with im.module.copy(): ivy_isolate.create_isolate(isolate) ag = ivy_art.AnalysisGraph(initializer=ivy_alpha.alpha) with utl.ErrorPrinter(): with ivy_interp.EvalContext(check=False): ag.execute_action('ext') cex = ag.check_bounded_safety(ag.states[-1]) if cex is not None: display_cex("safety failed",cex) print "OK"
def to_aiger(mod,ext_act): erf = il.Symbol('err_flag',il.find_sort('bool')) errconds = [] add_err_flag_mod(mod,erf,errconds) # we use a special state variable __init to indicate the initial state ext_acts = [mod.actions[x] for x in sorted(mod.public_actions)] ext_act = ia.EnvAction(*ext_acts) init_var = il.Symbol('__init',il.find_sort('bool')) init = add_err_flag(ia.Sequence(*([a for n,a in mod.initializers]+[ia.AssignAction(init_var,il.And())])),erf,errconds) action = ia.Sequence(ia.AssignAction(erf,il.Or()),ia.IfAction(init_var,ext_act,init)) # get the invariant to be proved, replacing free variables with # skolems. First, we apply any proof tactics. pc = ivy_proof.ProofChecker(mod.axioms,mod.definitions,mod.schemata) pmap = dict((lf.id,p) for lf,p in mod.proofs) conjs = [] for lf in mod.labeled_conjs: if lf.id in pmap: proof = pmap[lf.id] subgoals = pc.admit_proposition(lf,proof) conjs.extend(subgoals) else: conjs.append(lf) invariant = il.And(*[il.drop_universals(lf.formula) for lf in conjs]) # iu.dbg('invariant') skolemizer = lambda v: ilu.var_to_skolem('__',il.Variable(v.rep,v.sort)) vs = ilu.used_variables_in_order_ast(invariant) sksubs = dict((v.rep,skolemizer(v)) for v in vs) invariant = ilu.substitute_ast(invariant,sksubs) invar_syms = ilu.used_symbols_ast(invariant) # compute the transition relation stvars,trans,error = action.update(mod,None) # print 'action : {}'.format(action) # print 'annotation: {}'.format(trans.annot) annot = trans.annot # match_annotation(action,annot,MatchHandler()) indhyps = [il.close_formula(il.Implies(init_var,lf.formula)) for lf in mod.labeled_conjs] # trans = ilu.and_clauses(trans,indhyps) # save the original symbols for trace orig_syms = ilu.used_symbols_clauses(trans) orig_syms.update(ilu.used_symbols_ast(invariant)) # TODO: get the axioms (or maybe only the ground ones?) # axioms = mod.background_theory() # rn = dict((sym,tr.new(sym)) for sym in stvars) # next_axioms = ilu.rename_clauses(axioms,rn) # return ilu.and_clauses(axioms,next_axioms) funs = set() for df in trans.defs: funs.update(ilu.used_symbols_ast(df.args[1])) for fmla in trans.fmlas: funs.update(ilu.used_symbols_ast(fmla)) # funs = ilu.used_symbols_clauses(trans) funs.update(ilu.used_symbols_ast(invariant)) funs = set(sym for sym in funs if il.is_function_sort(sym.sort)) iu.dbg('[str(fun) for fun in funs]') # Propositionally abstract # step 1: get rid of definitions of non-finite symbols by turning # them into constraints new_defs = [] new_fmlas = [] for df in trans.defs: if len(df.args[0].args) == 0 and is_finite_sort(df.args[0].sort): new_defs.append(df) else: fmla = df.to_constraint() new_fmlas.append(fmla) trans = ilu.Clauses(new_fmlas+trans.fmlas,new_defs) # step 2: get rid of ite's over non-finite sorts, by introducing constraints cnsts = [] new_defs = [elim_ite(df,cnsts) for df in trans.defs] new_fmlas = [elim_ite(fmla,cnsts) for fmla in trans.fmlas] trans = ilu.Clauses(new_fmlas+cnsts,new_defs) # step 3: eliminate quantfiers using finite instantiations from_asserts = il.And(*[il.Equals(x,x) for x in ilu.used_symbols_ast(il.And(*errconds)) if tr.is_skolem(x) and not il.is_function_sort(x.sort)]) iu.dbg('from_asserts') invar_syms.update(ilu.used_symbols_ast(from_asserts)) sort_constants = mine_constants(mod,trans,il.And(invariant,from_asserts)) sort_constants2 = mine_constants2(mod,trans,invariant) print '\ninstantiations:' trans,invariant = Qelim(sort_constants,sort_constants2)(trans,invariant,indhyps) # print 'after qe:' # print 'trans: {}'.format(trans) # print 'invariant: {}'.format(invariant) # step 4: instantiate the axioms using patterns # We have to condition both the transition relation and the # invariant on the axioms, so we define a boolean symbol '__axioms' # to represent the axioms. axs = instantiate_axioms(mod,stvars,trans,invariant,sort_constants,funs) ax_conj = il.And(*axs) ax_var = il.Symbol('__axioms',ax_conj.sort) ax_def = il.Definition(ax_var,ax_conj) invariant = il.Implies(ax_var,invariant) trans = ilu.Clauses(trans.fmlas+[ax_var],trans.defs+[ax_def]) # step 5: eliminate all non-propositional atoms by replacing with fresh booleans # An atom with next-state symbols is converted to a next-state symbol if possible stvarset = set(stvars) prop_abs = dict() # map from atoms to proposition variables global prop_abs_ctr # sigh -- python lameness prop_abs_ctr = 0 # counter for fresh symbols new_stvars = [] # list of fresh symbols # get the propositional abstraction of an atom def new_prop(expr): res = prop_abs.get(expr,None) if res is None: prev = prev_expr(stvarset,expr,sort_constants) if prev is not None: # print 'stvar: old: {} new: {}'.format(prev,expr) pva = new_prop(prev) res = tr.new(pva) new_stvars.append(pva) prop_abs[expr] = res # prevent adding this again to new_stvars else: global prop_abs_ctr res = il.Symbol('__abs[{}]'.format(prop_abs_ctr),expr.sort) # print '{} = {}'.format(res,expr) prop_abs[expr] = res prop_abs_ctr += 1 return res # propositionally abstract an expression global mk_prop_fmlas mk_prop_fmlas = [] def mk_prop_abs(expr): if il.is_quantifier(expr) or len(expr.args) > 0 and any(not is_finite_sort(a.sort) for a in expr.args): return new_prop(expr) return expr.clone(map(mk_prop_abs,expr.args)) # apply propositional abstraction to the transition relation new_defs = map(mk_prop_abs,trans.defs) new_fmlas = [mk_prop_abs(il.close_formula(fmla)) for fmla in trans.fmlas] # find any immutable abstract variables, and give them a next definition def my_is_skolem(x): res = tr.is_skolem(x) and x not in invar_syms return res def is_immutable_expr(expr): res = not any(my_is_skolem(sym) or tr.is_new(sym) or sym in stvarset for sym in ilu.used_symbols_ast(expr)) return res for expr,v in prop_abs.iteritems(): if is_immutable_expr(expr): new_stvars.append(v) print 'new state: {}'.format(expr) new_defs.append(il.Definition(tr.new(v),v)) trans = ilu.Clauses(new_fmlas+mk_prop_fmlas,new_defs) # apply propositional abstraction to the invariant invariant = mk_prop_abs(invariant) # create next-state symbols for atoms in the invariant (is this needed?) rn = dict((sym,tr.new(sym)) for sym in stvars) mk_prop_abs(ilu.rename_ast(invariant,rn)) # this is to pick up state variables from invariant # update the state variables by removing the non-finite ones and adding the fresh state booleans stvars = [sym for sym in stvars if is_finite_sort(sym.sort)] + new_stvars # iu.dbg('trans') # iu.dbg('stvars') # iu.dbg('invariant') # exit(0) # For each state var, create a variable that corresponds to the input of its latch # Also, havoc all the state bits except the init flag at the initial time. This # is needed because in aiger, all latches start at 0! def fix(v): return v.prefix('nondet') def curval(v): return v.prefix('curval') def initchoice(v): return v.prefix('initchoice') stvars_fix_map = dict((tr.new(v),fix(v)) for v in stvars) stvars_fix_map.update((v,curval(v)) for v in stvars if v != init_var) trans = ilu.rename_clauses(trans,stvars_fix_map) # iu.dbg('trans') new_defs = trans.defs + [il.Definition(ilu.sym_inst(tr.new(v)),ilu.sym_inst(fix(v))) for v in stvars] new_defs.extend(il.Definition(curval(v),il.Ite(init_var,v,initchoice(v))) for v in stvars if v != init_var) trans = ilu.Clauses(trans.fmlas,new_defs) # Turn the transition constraint into a definition cnst_var = il.Symbol('__cnst',il.find_sort('bool')) new_defs = list(trans.defs) new_defs.append(il.Definition(tr.new(cnst_var),fix(cnst_var))) new_defs.append(il.Definition(fix(cnst_var),il.Or(cnst_var,il.Not(il.And(*trans.fmlas))))) stvars.append(cnst_var) trans = ilu.Clauses([],new_defs) # Input are all the non-defined symbols. Output indicates invariant is false. # iu.dbg('trans') def_set = set(df.defines() for df in trans.defs) def_set.update(stvars) # iu.dbg('def_set') used = ilu.used_symbols_clauses(trans) used.update(ilu.symbols_ast(invariant)) inputs = [sym for sym in used if sym not in def_set and not il.is_interpreted_symbol(sym)] fail = il.Symbol('__fail',il.find_sort('bool')) outputs = [fail] # iu.dbg('trans') # make an aiger aiger = Encoder(inputs,stvars,outputs) comb_defs = [df for df in trans.defs if not tr.is_new(df.defines())] invar_fail = il.Symbol('invar__fail',il.find_sort('bool')) # make a name for invariant fail cond comb_defs.append(il.Definition(invar_fail,il.Not(invariant))) aiger.deflist(comb_defs) for df in trans.defs: if tr.is_new(df.defines()): aiger.set(tr.new_of(df.defines()),aiger.eval(df.args[1])) miter = il.And(init_var,il.Not(cnst_var),il.Or(invar_fail,il.And(fix(erf),il.Not(fix(cnst_var))))) aiger.set(fail,aiger.eval(miter)) # aiger.sub.debug() # make a decoder for the abstract propositions decoder = dict((y,x) for x,y in prop_abs.iteritems()) for sym in aiger.inputs + aiger.latches: if sym not in decoder and sym in orig_syms: decoder[sym] = sym cnsts = set(sym for syms in sort_constants.values() for sym in syms) return aiger,decoder,annot,cnsts,action,stvarset