def emit_assume(self,header): code = [] indent(code) code.append('ivy_assume(') il.close_formula(self.args[0]).emit(header,code) code.append(', "{}");\n'.format(iu.lineno_str(self))) header.extend(code)
def emit_assume(self, header): code = [] indent(code) code.append('ivy_assume(') il.close_formula(self.args[0]).emit(header, code) code.append(', "{}");\n'.format(iu.lineno_str(self))) header.extend(code)
def emit_assume(self,header): code = [] indent(code) code.append('ivy_assume(') il.close_formula(self.args[0]).emit(header,code) code.append(');\n') header.extend(code)
def debug_clauses_list(cl): for clauses in cl: print "definitions:" for df in clauses.defs: print df print "fmlas:" for fmla in clauses.fmlas: print ivy_logic.close_formula(fmla)
def debug_clauses_list(cl): for clauses in cl: print "definitions:" for df in clauses.defs: print df print "fmlas:" for fmla in clauses.fmlas: print ivy_logic.close_formula(fmla)
def create_strat_map(assumes, asserts, macros): """ Given a list of assumptions, assertions and macros, compute the stratification graph. The difference between assumes and asserts is that the free variables in assumes are treated as universally quantified, while the asserts are treated as negated. Each argument is a list of pairs `(fmla,ast)` where `fmla` is the formula and `ast` an ast giving the origin of the formula. """ global universally_quantified_variables global strat_map global arcs # Gather all the formulas in the VC. all_fmlas = [(il.close_formula(x), y) for x, y in assumes] all_fmlas.extend((il.Not(x), y) for x, y in asserts) all_fmlas.extend(macros) # Get the universally quantified variables. The free variables of # asserts and macros won't count as universal. We keep track of the # line numbers of these variables for error messages. universally_quantified_variables = dict() for fmla, lf in all_fmlas: for v in il.universal_variables([fmla]): if (il.is_uninterpreted_sort(v.sort) or il.has_infinite_interpretation(v.sort)): universally_quantified_variables[v] = lf # print 'universally_quantified_variables : {}'.format([str(v) for v in universally_quantified_variables]) # Create an empty graph. strat_map = defaultdict(UFNode) arcs = [] # Handle macros, as described above. create_macro_maps(assumes, asserts, macros) # Simulate the Skolem functions that would be generated by AE alternations. for fmla, ast in all_fmlas: make_skolems(fmla, ast, True, []) # Construct the stratification graph by calling `map_fmla` on all # of the formulas in the VC. We don't include the macro definitions # here, because these are 'inlined' by `map_fmla`. for pair in assumes + asserts: map_fmla(pair[1].lineno, pair[0], 0)
def __call__(self,trans,invariant,indhyps): # apply to the transition relation new_defs = [self.qe(defn,self.sort_constants) for defn in trans.defs] new_fmlas = [self.qe(il.close_formula(fmla),self.sort_constants) for fmla in trans.fmlas] # apply to the invariant invariant = self.qe(invariant,self.sort_constants) # apply to inductive hyps indhyps = [self.qe(fmla,self.sort_constants2) for fmla in indhyps] # add the transition constraints to the new trans trans = ilu.Clauses(new_fmlas+indhyps+self.fmlas,new_defs) return trans,invariant
def create_strat_map(assumes,asserts,macros): global symbols_over_universals global universally_quantified_variables all_fmlas = [il.close_formula(pair[0]) for pair in assumes] all_fmlas.extend(il.Not(pair[0]) for pair in asserts) all_fmlas.extend(pair[0] for pair in macros) # for f in all_fmlas: # print f symbols_over_universals = il.symbols_over_universals(all_fmlas) universally_quantified_variables = il.universal_variables(all_fmlas) strat_map = defaultdict(UFNode) for pair in assumes+asserts+macros: map_fmla(pair[0],strat_map) # show_strat_map(strat_map) # print 'universally_quantified_variables:{}'.format(universally_quantified_variables) return strat_map
def create_strat_map(assumes,asserts,macros): global symbols_over_universals global universally_quantified_variables all_fmlas = [il.close_formula(pair[0]) for pair in assumes] all_fmlas.extend(il.Not(pair[0]) for pair in asserts) all_fmlas.extend(pair[0] for pair in macros) # for f in all_fmlas: # print f symbols_over_universals = il.symbols_over_universals(all_fmlas) universally_quantified_variables = il.universal_variables(all_fmlas) strat_map = defaultdict(UFNode) for pair in assumes+asserts+macros: map_fmla(pair[0],strat_map) # show_strat_map(strat_map) # print 'universally_quantified_variables:{}'.format(universally_quantified_variables) return strat_map
def check_can_assume(logic, fmla, ast): if not il.is_in_logic(il.close_formula(fmla), logic): report_error(logic, "", ast)
def check_can_assume(logic,fmla,ast): if not il.is_in_logic(il.close_formula(fmla),logic): report_error(logic,"",ast)
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