def process_conj(self): fmlas = [] helpers = [] for lf in self.mod.labeled_conjs: label = str(lf.label) if label.startswith("help_"): helpers.append(lf) else: fmlas.append(lf.formula) cl = lut.Clauses(fmlas) f = self.get_formula(cl) pref = lgu.substitute(f, self.nex2pre) self.add_new_constants(pref) res = (pref, "prop", "invar-property", "0") self.vmt["$prop"] = res for lf in helpers: label = str(lf.label) self.helpers[label] = lf.formula cl = lut.Clauses([lf.formula]) f = self.get_formula(cl) pref = lgu.substitute(f, self.nex2pre) self.add_new_constants(pref) res = (pref, label, "help", label) self.vmt[label] = res
def process_axiom(self): fmlas = [lf.formula for lf in self.mod.labeled_axioms] cl = lut.Clauses(fmlas) f = self.get_formula(cl) self.add_new_constants(f) res = (f, "axiom", "axiom", "true") self.vmt["$axiom"] = res
def update_theory(self): theory = list(self.get_axioms()) # axioms of the derived relations TODO: used only the # referenced ones, but we need to know abstract domain for # this for ldf in self.definitions: cnst = ldf.formula.to_constraint() if all( isinstance(p, il.Variable) for p in ldf.formula.args[0].args): if not isinstance(ldf.formula, il.DefinitionSchema): # theory.append(ldf.formula) # TODO: make this a def? ax = ldf.formula ax = ax.to_constraint() if isinstance(ax.rhs(), il.Some) else ax if ldf.formula.args[0].args: ax = il.ForAll(ldf.formula.args[0].args, ax) theory.append(ax) # TODO: make this a def? # extensionality axioms for structs for sort in sorted(self.sort_destructors): destrs = self.sort_destructors[sort] if any(d.name in self.sig.symbols for d in destrs): ea = il.extensionality(destrs) if il.is_epr(ea): theory.append(ea) # exclusivity axioms for variants for sort in sorted(self.variants): sort_variants = self.variants[sort] if any(v.name in self.sig.sorts for v in sort_variants): ea = il.exclusivity(self.sig.sorts[sort], sort_variants) theory.append(ea) # these are always in EPR self.theory = lu.Clauses(theory)
def emit_action_gen(header, impl, name, action, classname): global indent_level caname = varname(name) upd = action.update(im.module, None) pre = tr.reverse_image(ilu.true_clauses(), ilu.true_clauses(), upd) pre_clauses = ilu.trim_clauses(pre) pre_clauses = ilu.and_clauses( pre_clauses, ilu.Clauses([df.to_constraint() for df in im.module.concepts])) pre = pre_clauses.to_formula() syms = [ x for x in ilu.used_symbols_ast(pre) if x.name not in il.sig.symbols ] header.append("class " + caname + "_gen : public gen {\n public:\n") for sym in syms: if not sym.name.startswith('__ts') and sym not in pre_clauses.defidx: declare_symbol(header, sym) header.append(" {}_gen();\n".format(caname)) impl.append(caname + "_gen::" + caname + "_gen(){\n") indent_level += 1 emit_sig(impl) for sym in syms: emit_decl(impl, sym) indent(impl) impl.append('add("(assert {})");\n'.format( slv.formula_to_z3(pre).sexpr().replace('\n', '\\\n'))) indent_level -= 1 impl.append("}\n") header.append(" bool generate(" + classname + "&);\n};\n") impl.append("bool " + caname + "_gen::generate(" + classname + "& obj) {\n push();\n") indent_level += 1 pre_used = ilu.used_symbols_ast(pre) for sym in all_state_symbols(): if sym in pre_used and sym not in pre_clauses.defidx: # skip symbols not used in constraint if slv.solver_name(sym) != None: # skip interpreted symbols global is_derived if sym not in is_derived: emit_set(impl, sym) for sym in syms: if not sym.name.startswith('__ts') and sym not in pre_clauses.defidx: emit_randomize(impl, sym) impl.append(""" bool res = solve(); if (res) { """) indent_level += 1 for sym in syms: if not sym.name.startswith('__ts') and sym not in pre_clauses.defidx: emit_eval(impl, sym) indent_level -= 2 impl.append(""" } pop(); obj.___ivy_gen = this; return res; } """)
def process_axiom(self): fmlas = [lf.formula for lf in self.mod.labeled_axioms] cl = lut.Clauses(fmlas) self.axioms.append(self.get_formula(cl)) f = lut.and_clauses(*self.axioms) self.add_new_constants(f, 'axiom') res = (f, "axiom", "axiom", "true") self.vmt["$axiom"] = res
def background_theory(self, symbols=None): """ Return a set of clauses which represent the background theory restricted to the given symbols (should be like the result of used_symbols). """ theory = list(self.get_axioms()) # axioms of the derived relations TODO: used only the # referenced ones, but we need to know abstract domain for # this for df in self.concepts: theory.append(df.to_constraint()) # TODO: make this a def? return lu.Clauses(theory)
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 make_vc(action, precond=[], postcond=[], check_asserts=True): ag = art.AnalysisGraph() pre = itp.State() pre.clauses = lut.Clauses([lf.formula for lf in precond]) pre.clauses.annot = act.EmptyAnnotation() with itp.EvalContext(check=False): # don't check safety post = ag.execute(action, pre) post.clauses = lut.true_clauses() fail = itp.State(expr=itp.fail_expr(post.expr)) history = ag.get_history(post) axioms = im.module.background_theory() clauses = history.post #Tricky: fix the annotation so it matches the original action stack = [] while isinstance(clauses.annot, act.RenameAnnotation): stack.append(clauses.annot.map) clauses.annot = clauses.annot.arg clauses.annot = clauses.annot.args[1] while stack: clauses.annot = act.RenameAnnotation(clauses.annot, stack.pop()) clauses = lut.and_clauses(clauses, axioms) fc = lut.Clauses([lf.formula for lf in postcond]) fc.annot = act.EmptyAnnotation() used_names = frozenset(x.name for x in lg.sig.symbols.values()) def witness(v): c = lg.Symbol('@' + v.name, v.sort) assert c.name not in used_names return c fcc = lut.dual_clauses(fc, witness) clauses = lut.and_clauses(clauses, fcc) return clauses
def instantiate_non_epr(non_epr,ground_terms): theory = [] if ground_terms != None: matched = set() for term in ground_terms: if term.rep in non_epr and term not in matched: ldf,cnst = non_epr[term.rep] subst = dict((v,t) for v,t in zip(ldf.formula.args[0].args,term.args) if not isinstance(v,il.Variable)) inst = lu.substitute_constants_ast(cnst,subst) theory.append(inst) # iu.dbg('inst') matched.add(term) return lu.Clauses(theory)
def add_state(self, eqns): clauses = lut.Clauses(eqns) state = self.domain.new_state(clauses) univs = self.get_universes() if univs is not None: state.universe = univs if self.last_action is not None: expr = itp.action_app(self.last_action, self.states[-1]) if self.returned is not None: expr.subgraph = self.returned self.returned = None self.last_action = None self.add(state, expr) else: self.add(state)
def update_theory(self): theory = list(self.get_axioms()) # axioms of the derived relations TODO: used only the # referenced ones, but we need to know abstract domain for # this for ldf in self.definitions: cnst = ldf.formula.to_constraint() if all(isinstance(p,il.Variable) for p in ldf.formula.args[0].args): theory.append(cnst) # TODO: make this a def? # extensionality axioms for structs for sort in sorted(self.sort_destructors): destrs = self.sort_destructors[sort] if any(d.name in self.sig.symbols for d in destrs): ea = il.extensionality(destrs) if il.is_epr(ea): theory.append(ea) self.theory = lu.Clauses(theory)
def new_state_pairs(self, sym_pairs, env): eqns = [] for sym, renamed_sym in sym_pairs: rmap = {renamed_sym: sym} # TODO: what if the renamed symbol is not in the model? for fmla in self.eqs[renamed_sym]: rfmla = lut.rename_ast(fmla, rmap) eqns.append(rfmla) clauses = lut.Clauses(eqns) state = self.domain.new_state(clauses) state.universe = self.model.universes(numerals=True) if self.last_action is not None: expr = itp.action_app(self.last_action, self.states[-1]) if self.returned is not None: expr.subgraph = self.returned self.returned = None self.last_action = None self.add(state, expr) else: self.add(state)
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
def background_theory(self, symbols=None): if hasattr(self, "theory"): return self.theory return lu.Clauses([])
def get_conjs(mod): fmlas = [lf.formula for lf in mod.labeled_conjs if not lf.explicit] return lut.Clauses(fmlas, annot=act.EmptyAnnotation())
def constraints(self): return ilu.Clauses(self.concept_session.suppose_constraints)
def abstractor(state): state.clauses = ilu.Clauses(stvals) state.universes = dict() # indicates this is a singleton state
def add_state(self,stvals,action): # iu.dbg('stvals') self.add(itp.State(value=ilu.Clauses(stvals),expr=itp.action_app(action,self.states[-1]),label='ext'))