def bq(fmla): """ Bound the free variables in fmla of uninterpeted sort """ vs = list(sorted(used_variables_ast(fmla))) vs = [v for v in vs if not ivy_logic.is_interpreted_sort(v.sort)] cnsts = [bdv(v) for v in vs] bq_res = ivy_logic.Implies(ivy_logic.And(*cnsts),fmla) if cnsts else fmla return bq_res
def __init__(self, clauses, model, vocab, top_level=True): art.AnalysisGraph.__init__(self) self.clauses = clauses self.model = model self.vocab = vocab mod_clauses = islv.clauses_model_to_clauses(clauses, model=model, numerals=True) self.eqs = defaultdict(list) for fmla in mod_clauses.fmlas: if lg.is_eq(fmla): lhs, rhs = fmla.args if lg.is_app(lhs): self.eqs[lhs.rep].append(fmla) elif isinstance(fmla, lg.Not): app = fmla.args[0] if lg.is_app(app): self.eqs[app.rep].append(lg.Equals(app, lg.Or())) else: if lg.is_app(fmla): self.eqs[fmla.rep].append(lg.Equals(fmla, lg.And())) self.last_action = None self.sub = None self.returned = None self.top_level = top_level
def __init__(self, clauses, model, vocab): # iu.dbg('clauses') self.clauses = clauses self.model = model self.vocab = vocab self.current = dict() mod_clauses = islv.clauses_model_to_clauses(clauses, model=model, numerals=True) # iu.dbg('mod_clauses') self.eqs = defaultdict(list) for fmla in mod_clauses.fmlas: if lg.is_eq(fmla): lhs, rhs = fmla.args if lg.is_app(lhs): self.eqs[lhs.rep].append(fmla) elif isinstance(fmla, lg.Not): app = fmla.args[0] if lg.is_app(app): self.eqs[app.rep].append(lg.Equals(app, lg.Or())) else: if lg.is_app(fmla): self.eqs[fmla.rep].append(lg.Equals(fmla, lg.And())) # for sym in vocab: # if not itr.is_new(sym) and not itr.is_skolem(sym): # self.show_sym(sym,sym) self.started = False self.renaming = dict() print print 'Trace follows...' print 80 * '*'
def __init__(self, clauses, model, vocab, top_level=True): TraceBase.__init__(self) self.clauses = clauses self.model = model self.vocab = vocab self.top_level = top_level if clauses is not None: ignore = lambda s: islv.solver_name(s) == None mod_clauses = islv.clauses_model_to_clauses(clauses, model=model, numerals=True, ignore=ignore) self.eqs = defaultdict(list) for fmla in mod_clauses.fmlas: if lg.is_eq(fmla): lhs, rhs = fmla.args if lg.is_app(lhs): self.eqs[lhs.rep].append(fmla) elif isinstance(fmla, lg.Not): app = fmla.args[0] if lg.is_app(app): self.eqs[app.rep].append(lg.Equals(app, lg.Or())) else: if lg.is_app(fmla): self.eqs[fmla.rep].append(lg.Equals(fmla, lg.And()))
def let_tactic(self,decls,proof): cond = il.And(*[il.Equals(x,y) for x,y in proof.args]) subgoal = ia.LabeledFormula(decls[0].label,il.Implies(cond,decls[0].formula)) if not hasattr(decls[0],'lineno'): print 'has no line number: {}'.format(decls[0]) exit(1) subgoal.lineno = decls[0].lineno return attrib_goals(proof,[subgoal]) + decls[1:]
def sort_size_constraint(sort,size): if isinstance(sort,ivy_logic.UninterpretedSort): syms = [ivy_logic.Symbol('__'+sort.name+'$'+str(i),sort) for i in range(size)] v = ivy_logic.Variable('X'+sort.name,sort) res = ivy_logic.Or(*[ivy_logic.Equals(v,s) for s in syms]) # print "sort_size_constraint : {}".format(res) return res return ivy_logic.And()
def get_truth(digits,idx,syms): if (len(digits) != len(syms)): badwit() digit = digits[idx] if digit == '0': return il.Or() elif digit == '1': return il.And() elif digit != 'x': badwit() return None
def clone_normal(expr,args): if il.is_eq(expr): x,y = args if x == y: return il.And() to = term_ord(x,y) # print 'term_ord({},{}) = {}'.format(x,y,to) if to == 1: x,y = y,x return expr.clone([x,y]) return expr.clone(args)
def let_tactic(self, decls, proof): cond = il.And(*[il.Equals(x, y) for x, y in proof.args]) subgoal = ia.LabeledFormula(decls[0].label, il.Implies(cond, decls[0].formula)) subgoal.lineno = decls[0].lineno return attrib_goals(proof, [subgoal]) + decls[1:]
def constant_from_z3(sort, c): if z3.is_true(c): return ivy_logic.And() if z3.is_false(c): return ivy_logic.Or() return ivy_logic.Constant(ivy_logic.Symbol(repr(c), sort))
def scenario(self, scen): init_tokens = set(p.rep for p in scen.args[0].args) transs_by_action = defaultdict(list) for tr in scen.args[1:]: transs_by_action[tr.args[2].args[1].args[0].rep].append(tr) for (place_name, lineno) in scen.places(): sym = find_symbol(place_name) iname = place_name + '[init]' iact = AssignAction( sym, ivy_logic.And() if (place_name in init_tokens) else ivy_logic.Or()) iact.formal_params = [] iact.formal_returns = [] iact.lineno = scen.lineno self.mod.actions[iname] = iact self.mixin( ivy_ast.MixinAfterDef(ivy_ast.Atom(iname), ivy_ast.Atom('init'))) for actname, trs in transs_by_action.iteritems(): choices = [] params = None afters = [] for tr in trs: scmix = tr.args[2] is_after = isinstance(scmix, ivy_ast.ScenarioAfterMixin) df = scmix.args[1] body = compile_action_def(df, self.mod.sig) seq = [] if not is_after: for p in tr.args[0].args: seq.append(AssumeAction(find_symbol(p.rep))) for p in tr.args[0].args: seq.append( AssignAction(find_symbol(p.rep), ivy_logic.Or())) for p in tr.args[1].args: seq.append( AssignAction(find_symbol(p.rep), ivy_logic.And())) seq.append(body) seq = Sequence(*seq) else: for p in tr.args[0].args: seq.append( AssignAction(find_symbol(p.rep), ivy_logic.Or())) for p in tr.args[1].args: seq.append( AssignAction(find_symbol(p.rep), ivy_logic.And())) seq.append(body) seq = Sequence(*seq) seq = IfAction( And(*[find_symbol(p.rep) for p in tr.args[0].args]), seq) if params is None: params = body.formal_params returns = body.formal_returns mixer = scmix.args[0] mixee = scmix.args[1].args[0] else: aparams = df.formal_params + df.formal_returns subst = dict(zip(aparams, params + returns)) seq = substitute_constants_ast(seq, subst) seq.lineno = tr.lineno if not is_after: choices.append(seq) else: afters.append(seq) if choices: choice = BalancedChoice(choices) choice.lineno = choices[0].lineno choice.formal_params = params choice.formal_returns = returns self.mod.actions[mixer.rep] = choice self.mixin(ivy_ast.MixinBeforeDef(mixer, mixee)) if afters: choice = Sequence(*afters) choice.lineno = afters[0].lineno choice.formal_params = params choice.formal_returns = returns self.mod.actions[mixer.rep] = choice self.mixin(ivy_ast.MixinAfterDef(mixer, mixee))
def let_tactic(self,decls,proof): cond = il.And(*[il.Equals(x,y) for x,y in proof.args]) return [ia.LabeledFormula(decls[0].label, il.Implies(cond,decls[0].formula))] + decls[1:]
def isolate_component(mod, isolate_name, extra_with=[], extra_strip=None): if isolate_name not in mod.isolates: raise iu.IvyError(None, "undefined isolate: {}".format(isolate_name)) isolate = mod.isolates[isolate_name] verified = set(a.relname for a in (isolate.verified() + tuple(extra_with))) present = set(a.relname for a in isolate.present()) present.update(verified) if not interpret_all_sorts: for type_name in list(ivy_logic.sig.interp): if not (type_name in present or any( startswith_eq_some(itp.label.rep, present, mod) for itp in mod.interps[type_name] if itp.label)): del ivy_logic.sig.interp[type_name] delegates = set(s.delegated() for s in mod.delegates if not s.delegee()) delegated_to = dict( (s.delegated(), s.delegee()) for s in mod.delegates if s.delegee()) derived = set(df.args[0].func.name for df in mod.concepts) for name in present: if (name not in mod.hierarchy and name not in ivy_logic.sig.sorts and name not in derived and name not in ivy_logic.sig.interp and name not in mod.actions and name not in ivy_logic.sig.symbols): raise iu.IvyError( None, "{} is not an object, action, sort, definition, or interpreted function" .format(name)) impl_mixins = defaultdict(list) # delegate all the stub actions to their implementations global implementation_map implementation_map = {} for actname, ms in mod.mixins.iteritems(): implements = [ m for m in ms if isinstance(m, ivy_ast.MixinImplementDef) ] impl_mixins[actname].extend(implements) before_after = [ m for m in ms if not isinstance(m, ivy_ast.MixinImplementDef) ] del ms[:] ms.extend(before_after) for m in implements: for foo in (m.mixee(), m.mixer()): if foo not in mod.actions: raise IvyError(m, 'action {} not defined'.format(foo)) action = mod.actions[m.mixee()] if not (isinstance(action, ia.Sequence) and len(action.args) == 0): raise IvyError( m, 'multiple implementations of action {}'.format(m.mixee())) action = ia.apply_mixin(m, mod.actions[m.mixer()], action) mod.actions[m.mixee()] = action implementation_map[m.mixee()] = m.mixer() new_actions = {} use_mixin = lambda name: startswith_some(name, present, mod) mod_mixin = lambda m: m if startswith_some(name, verified, mod ) else m.prefix_calls('ext:') all_mixins = lambda m: True no_mixins = lambda m: False after_mixins = lambda m: isinstance(m, ivy_ast.MixinAfterDef) before_mixins = lambda m: isinstance(m, ivy_ast.MixinBeforeDef) delegated_to_verified = lambda n: n in delegated_to and startswith_eq_some( delegated_to[n], verified, mod) ext_assumes = lambda m: before_mixins(m) and not delegated_to_verified( m.mixer()) int_assumes = lambda m: after_mixins(m) and not delegated_to_verified( m.mixer()) ext_assumes_no_ver = lambda m: not delegated_to_verified(m.mixer()) summarized_actions = set() for actname, action in mod.actions.iteritems(): ver = startswith_eq_some(actname, verified, mod) pre = startswith_eq_some(actname, present, mod) if pre: if not ver: assert hasattr(action, 'lineno') assert hasattr(action, 'formal_params'), action ext_action = action.assert_to_assume().prefix_calls('ext:') assert hasattr(ext_action, 'lineno') assert hasattr(ext_action, 'formal_params'), ext_action if actname in delegates: int_action = action.prefix_calls('ext:') assert hasattr(int_action, 'lineno') assert hasattr(int_action, 'formal_params'), int_action else: int_action = ext_action assert hasattr(int_action, 'lineno') assert hasattr(int_action, 'formal_params'), int_action else: int_action = ext_action = action assert hasattr(int_action, 'lineno') assert hasattr(int_action, 'formal_params'), int_action # internal version of the action has mixins checked ea = no_mixins if ver else int_assumes new_actions[actname] = add_mixins(mod, actname, int_action, ea, use_mixin, lambda m: m) # external version of the action assumes mixins are ok, unless they # are delegated to a currently verified object ea = ext_assumes if ver else ext_assumes_no_ver new_action = add_mixins(mod, actname, ext_action, ea, use_mixin, mod_mixin) new_actions['ext:' + actname] = new_action # TODO: external version is public if action public *or* called from opaque # public_actions.add('ext:'+actname) else: # TODO: here must check that summarized action does not # have a call dependency on the isolated module summarized_actions.add(actname) action = summarize_action(action) new_actions[actname] = add_mixins(mod, actname, action, after_mixins, use_mixin, mod_mixin) new_actions['ext:' + actname] = add_mixins(mod, actname, action, all_mixins, use_mixin, mod_mixin) # figure out what is exported: exported = set() for e in mod.exports: if not e.scope() and startswith_eq_some(e.exported(), present, mod): # global scope exported.add('ext:' + e.exported()) for actname, action in mod.actions.iteritems(): if not startswith_some(actname, present, mod): for c in action.iter_calls(): if (startswith_some(c, present, mod) or any( startswith_some(m.mixer(), present, mod) for m in mod.mixins[c])): exported.add('ext:' + c) # print "exported: {}".format(exported) # We allow objects to reference any symbols in global scope, and # we keep axioms declared in global scope. Because of the way # thigs are named, this gives a different condition for keeping # symbols and axioms (in particular, axioms in global scope have # label None). Maybe this needs to be cleaned up. keep_sym = lambda name: (iu.ivy_compose_character not in name or startswith_eq_some(name, present)) keep_ax = lambda name: (name is None or startswith_eq_some( name.rep, present, mod)) check_pr = lambda name: (name is None or startswith_eq_some( name.rep, verified, mod)) prop_deps = get_prop_dependencies(mod) # filter the conjectures new_conjs = [c for c in mod.labeled_conjs if keep_ax(c.label)] del mod.labeled_conjs[:] mod.labeled_conjs.extend(new_conjs) # filter the inits new_inits = [c for c in mod.labeled_inits if keep_ax(c.label)] del mod.labeled_inits[:] mod.labeled_inits.extend(new_inits) # filter the axioms dropped_axioms = [a for a in mod.labeled_axioms if not keep_ax(a.label)] mod.labeled_axioms = [a for a in mod.labeled_axioms if keep_ax(a.label)] mod.labeled_props = [a for a in mod.labeled_props if keep_ax(a.label)] # convert the properties not being verified to axioms mod.labeled_axioms.extend( [a for a in mod.labeled_props if not check_pr(a.label)]) mod.labeled_props = [a for a in mod.labeled_props if check_pr(a.label)] # filter definitions mod.concepts = [ c for c in mod.concepts if startswith_eq_some(c.args[0].func.name, present, mod) ] # filter the signature # keep only the symbols referenced in the remaining # formulas asts = [] for x in [ mod.labeled_axioms, mod.labeled_props, mod.labeled_inits, mod.labeled_conjs ]: asts += [y.formula for y in x] asts += mod.concepts asts += [action for action in new_actions.values()] sym_names = set(x.name for x in lu.used_symbols_asts(asts)) if filter_symbols.get() or cone_of_influence.get(): old_syms = list(mod.sig.symbols) for sym in old_syms: if sym not in sym_names: del mod.sig.symbols[sym] # check that any dropped axioms do not refer to the isolate's signature # and any properties have dependencies present def pname(s): return s.label if s.label else "" if enforce_axioms.get(): for a in dropped_axioms: for x in lu.used_symbols_ast(a.formula): if x.name in sym_names: raise iu.IvyError( a, "relevant axiom {} not enforced".format(pname(a))) for actname, action in mod.actions.iteritems(): if startswith_eq_some(actname, present, mod): for c in action.iter_calls(): called = mod.actions[c] if not startswith_eq_some(c, present, mod): if not (type(called) == ia.Sequence and not called.args): raise iu.IvyError( None, "No implementation for action {}".format(c)) for p, ds in prop_deps: for d in ds: if not startswith_eq_some(d, present, mod): raise iu.IvyError( p, "property {} depends on abstracted object {}".format( pname(p), d)) # for x,y in new_actions.iteritems(): # print iu.pretty(ia.action_def_to_str(x,y)) # check for interference # iu.dbg('list(summarized_actions)') check_interference(mod, new_actions, summarized_actions) # After checking, we can put in place the new action definitions mod.public_actions.clear() mod.public_actions.update(exported) mod.actions.clear() mod.actions.update(new_actions) # TODO: need a better way to filter signature # new_syms = set(s for s in mod.sig.symbols if keep_sym(s)) # for s in list(mod.sig.symbols): # if s not in new_syms: # del mod.sig.symbols[s] # strip the isolate parameters strip_isolate(mod, isolate, impl_mixins, extra_strip) # collect the initial condition init_cond = ivy_logic.And(*(lf.formula for lf in mod.labeled_inits)) mod.init_cond = lu.formula_to_clauses(init_cond)
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 get_next_sym(self,v): enc = self.encoding[v] abits = self.sub.sym_next_vals(enc) bits = [il.And() if b == '1' else il.Or() for b in abits] return self.decode_val(bits,v)
def invariance_tactic(prover,goals,proof): goal = goals[0] # pick up the first proof goal conc = ipr.goal_conc(goal) # get its conclusion if not isinstance(conc,TemporalModels): raise iu.IvyError(proof,'proof goal is not temporal') model = conc.model fmla = conc.fmla if not isinstance(fmla,il.Globally): raise iu.IvyError(proof,'invariance tactic applies only to globally formulas') invar = fmla.args[0] if il.is_temporal(invar): raise iu.IvyError(proof,'invariance tactic applies only formulas "globally p"' + ' where p is non-temporal') # Collect the auxiliary invariants invars = [inv.compile() for inv in proof.tactic_decls] # Add the invariant phi to the list invars.append(ipr.clone_goal(goal,[],invar)) # Add the invariant list to the model model = model.clone([]) model.invars = model.invars + invars # Get all the implicit globally properties from the proof # environment. Each temporal operator has an 'environment'. The # operator applies to states *not* in actions labeled with this # environment. This has several consequences: # # 1) The operator's semantic constraint is an assumed invariant (i.e., # it holds outside of any action) # # 2) An 'event' for the temporal operator occurs when (a) we return # from an execution context inside its environment to one outside, # or (b) we are outside the environment of the operator and some symbol # occurring in it's body is mutated. # # 3) At any event for the operator, we update its truth value and # and re-establish its senatic constraint. # # # The following procedure instruments a statement with operator events for # both the property to be proved and the invariant assumptions (all G properties here). # This depends on the statement's environment, that is, current set of environment # labels. # # Currently, the environment labels of a statement have to be # statically determined, but this could change, i.e., the labels # could be represented by boolean variables. # # TODO: there is something a bit inelegant here, because when we return from # an exported action to the external environment, we need to update the operator # states, however, we do *not* want to do this when returning to in internal caller. # The best solution currently for this is to duplication the actions so there # is one version for internal callers and one for external callers. This issue doesn't # affect this simple invariance tactic, because it doesn't need to update the truth # values of the operators. # Get all the G properties from the prover environment as assumptions assumed_gprops = [x for x in prover.axioms if not x.explicit and x.temporal and il.is_gprop(x.formula)] gprops = [x.formula for x in assumed_gprops] gproplines = [x.lineno for x in assumed_gprops] # We represent the property G phi to be proved by its negation F ~phi. gprops.append(il.Eventually(fmla.environ,il.Not(invar))) gproplines.append(goal.lineno) # Make some memo tables envprops = defaultdict(list) symprops = defaultdict(list) for prop in gprops: envprops[prop.environ].append(prop) for sym in ilu.symbols_ast(prop): symprops[sym].append(prop) actions = dict((b.name,b.action) for b in model.bindings) lines = dict(zip(gprops,gproplines)) def instr_stmt(stmt,labels): # first, recur on the sub-statements args = [instr_stmt(a,labels) if isinstance(a,iact.Action) else a for a in stmt.args] res = stmt.clone(args) # now add any needed temporal events after this statement event_props = set() # first, if it is a call, we must consider any events associated with # the return if isinstance(stmt,iact.CallAction): callee = actions[stmt.callee()] # get the called action exiting = [l for l in callee.labels if l not in labels] # environments we exit on return for label in exiting: for prop in envprops[label]: event_props.add(prop) # Second, if a symbol is modified, we must add events for every property that # depends on the symbol, but only if we are not in the environment of that property. for sym in stmt.modifies(): for prop in symprops[sym]: if prop.environ not in labels: event_props.add(prop) # Now, for every property event, we update the property state (none in this case) # and also assert the property semantic constraint. events = [prop_event(prop,lines[prop]) for prop in event_props] res = iact.postfix_action(res,events) stmt.copy_formals(res) # HACK: This shouldn't be needed return res # Add property events to all of the actions: model.bindings = [b.clone([b.action.clone([instr_stmt(b.action.stmt,b.action.labels)])]) for b in model.bindings] # Add all the assumed invariants to the model model.asms.extend([p.clone([p.label,p.formula.args[0]]) for p in assumed_gprops]) # if len(gprops) > 0: # assumes = [gprop_to_assume(x) for x in gprops] # model.bindings = [b.clone([prefix_action(b.action,assumes)]) for b in model.bindings] # Change the conclusion formula to M |= true conc = TemporalModels(model,il.And()) # Build the new goal goal = ipr.clone_goal(goal,ipr.goal_prems(goal),conc) # Return the new goal stack goals = [goal] + goals[1:] return goals
def check_isolate(): mod = im.module if mod.isolate_proof is not None: pc = ivy_proof.ProofChecker( mod.labeled_axioms + mod.assumed_invariants, mod.definitions, mod.schemata) model = itmp.normal_program_from_module(im.module) prop = ivy_ast.LabeledFormula(ivy_ast.Atom('safety'), lg.And()) subgoal = ivy_ast.LabeledFormula(ivy_ast.Atom('safety'), itmp.TemporalModels(model, lg.And())) # print 'subgoal = {}'.format(subgoal) subgoals = [subgoal] subgoals = pc.admit_proposition(prop, mod.isolate_proof, subgoals) check_subgoals(subgoals) return ifc.check_fragment() with im.module.theory_context(): global check_lineno check_lineno = act.checked_assert.get() if check_lineno == "": check_lineno = None # print 'check_lineno: {}'.format(check_lineno) check = not opt_summary.get() subgoalmap = dict((x.id, y) for x, y in im.module.subgoals) axioms = [m for m in mod.labeled_axioms if m.id not in subgoalmap] schema_instances = [ m for m in mod.labeled_axioms if m.id in subgoalmap ] if axioms: print "\n The following properties are assumed as axioms:" for lf in axioms: print pretty_lf(lf) if mod.definitions: print "\n The following definitions are used:" for lf in mod.definitions: print pretty_lf(lf) if (mod.labeled_props or schema_instances) and not checked_action.get(): print "\n The following properties are to be checked:" if check: for lf in schema_instances: print pretty_lf(lf) + " [proved by axiom schema]" ag = ivy_art.AnalysisGraph() clauses1 = lut.true_clauses(annot=act.EmptyAnnotation()) pre = itp.State(value=clauses1) props = [x for x in im.module.labeled_props if not x.temporal] props = [ p for p in props if not (p.id in subgoalmap and p.explicit) ] fcs = ([(ConjAssumer if prop.assumed or prop.id in subgoalmap else ConjChecker)(prop) for prop in props]) check_fcs_in_state(mod, ag, pre, fcs) else: for lf in schema_instances + mod.labeled_props: print pretty_lf(lf) # after checking properties, make them axioms, except temporals im.module.labeled_axioms.extend(p for p in im.module.labeled_props if not p.temporal) im.module.update_theory() if mod.labeled_inits: print "\n The following properties are assumed initially:" for lf in mod.labeled_inits: print pretty_lf(lf) if mod.labeled_conjs: print "\n The inductive invariant consists of the following conjectures:" for lf in mod.labeled_conjs: print pretty_lf(lf) apply_conj_proofs(mod) if mod.isolate_info is not None and mod.isolate_info.implementations: print "\n The following action implementations are present:" for mixer, mixee, action in sorted( mod.isolate_info.implementations, key=lambda x: x[0]): print " {}implementation of {}".format( pretty_lineno(action), mixee) if mod.isolate_info is not None and mod.isolate_info.monitors: print "\n The following action monitors are present:" for mixer, mixee, action in sorted(mod.isolate_info.monitors, key=lambda x: x[0]): print " {}monitor of {}".format(pretty_lineno(action), mixee) # if mod.actions: # print "\n The following actions are present:" # for actname,action in sorted(mod.actions.iteritems()): # print " {}{}".format(pretty_lineno(action),actname) if mod.initializers: print "\n The following initializers are present:" for actname, action in sorted(mod.initializers, key=lambda x: x[0]): print " {}{}".format(pretty_lineno(action), actname) if mod.labeled_conjs and not checked_action.get(): print "\n Initialization must establish the invariant" if check: with itp.EvalContext(check=False): ag = ivy_art.AnalysisGraph(initializer=lambda x: None) check_conjs_in_state(mod, ag, ag.states[0]) else: print '' if mod.initializers: print "\n Any assertions in initializers must be checked", if check: ag = ivy_art.AnalysisGraph(initializer=lambda x: None) fail = itp.State(expr=itp.fail_expr(ag.states[0].expr)) check_safety_in_state(mod, ag, fail) checked_actions = get_checked_actions() if checked_actions and mod.labeled_conjs: print "\n The following set of external actions must preserve the invariant:" for actname in sorted(checked_actions): action = act.env_action(actname) print " {}{}".format(pretty_lineno(action), actname) if check: ag = ivy_art.AnalysisGraph() pre = itp.State() pre.clauses = get_conjs(mod) with itp.EvalContext(check=False): # don't check safety # post = ag.execute(action, pre, None, actname) post = ag.execute(action, pre) check_conjs_in_state(mod, ag, post, indent=12) else: print '' callgraph = defaultdict(list) for actname, action in mod.actions.iteritems(): for called_name in action.iter_calls(): callgraph[called_name].append(actname) some_assumps = False for actname, action in mod.actions.iteritems(): assumptions = [ sub for sub in action.iter_subactions() if isinstance(sub, act.AssumeAction) ] if assumptions: if not some_assumps: print "\n The following program assertions are treated as assumptions:" some_assumps = True callers = callgraph[actname] if actname in mod.public_actions: callers.append("the environment") prettyname = actname[4:] if actname.startswith( 'ext:') else actname prettycallers = [ c[4:] if c.startswith('ext:') else c for c in callers ] print " in action {} when called from {}:".format( prettyname, ','.join(prettycallers)) for sub in assumptions: print " {}assumption".format(pretty_lineno(sub)) tried = set() some_guarants = False for actname, action in mod.actions.iteritems(): guarantees = [ sub for sub in action.iter_subactions() if isinstance(sub, (act.AssertAction, act.Ranking)) ] if check_lineno is not None: guarantees = [ sub for sub in guarantees if sub.lineno == check_lineno ] if guarantees: if not some_guarants: print "\n The following program assertions are treated as guarantees:" some_guarants = True callers = callgraph[actname] if actname in mod.public_actions: callers.append("the environment") prettyname = actname[4:] if actname.startswith( 'ext:') else actname prettycallers = [ c[4:] if c.startswith('ext:') else c for c in callers ] print " in action {} when called from {}:".format( prettyname, ','.join(prettycallers)) roots = set(iu.reachable([actname], lambda x: callgraph[x])) for sub in guarantees: print " {}guarantee".format(pretty_lineno(sub)), if check and any( r in roots and (r, sub.lineno) not in tried for r in checked_actions): print_dots() old_checked_assert = act.checked_assert.get() act.checked_assert.value = sub.lineno some_failed = False for root in checked_actions: if root in roots: tried.add((root, sub.lineno)) action = act.env_action(root) ag = ivy_art.AnalysisGraph() pre = itp.State() pre.clauses = get_conjs(mod) with itp.EvalContext(check=False): post = ag.execute(action, prestate=pre) fail = itp.State(expr=itp.fail_expr(post.expr)) if not check_safety_in_state( mod, ag, fail, report_pass=False): some_failed = True break if not some_failed: print 'PASS' act.checked_assert.value = old_checked_assert else: print "" check_temporals()
def isolate_component(mod,isolate_name): if isolate_name not in mod.isolates: raise iu.IvyError(None,"undefined isolate: {}".format(isolate_name)) isolate = mod.isolates[isolate_name] verified = set(a.relname for a in isolate.verified()) present = set(a.relname for a in isolate.present()) present.update(verified) if not interpret_all_sorts: for type_name in list(ivy_logic.sig.interp): if not startswith_eq_some(type_name,present): del ivy_logic.sig.interp[type_name] delegates = set(s.delegated() for s in mod.delegates if not s.delegee()) delegated_to = dict((s.delegated(),s.delegee()) for s in mod.delegates if s.delegee()) derived = set(df.args[0].func.name for df in mod.concepts) for name in present: if (name not in mod.hierarchy and name not in ivy_logic.sig.sorts and name not in derived and name not in ivy_logic.sig.interp): raise iu.IvyError(None,"{} is not a module instance, sort, definition, or interpreted function".format(name)) new_actions = {} use_mixin = lambda name: startswith_some(name,present) mod_mixin = lambda m: m if startswith_some(name,verified) else m.prefix_calls('ext:') all_mixins = lambda m: True no_mixins = lambda m: False after_mixins = lambda m: isinstance(m,ivy_ast.MixinAfterDef) before_mixins = lambda m: isinstance(m,ivy_ast.MixinBeforeDef) delegated_to_verified = lambda n: n in delegated_to and startswith_eq_some(delegated_to[n],verified) ext_assumes = lambda m: before_mixins(m) and not delegated_to_verified(m.mixer()) for actname,action in mod.actions.iteritems(): ver = startswith_some(actname,verified) pre = startswith_some(actname,present) if pre: if not ver: assert hasattr(action,'lineno') assert hasattr(action,'formal_params'), action ext_action = action.assert_to_assume().prefix_calls('ext:') assert hasattr(ext_action,'lineno') assert hasattr(ext_action,'formal_params'), ext_action if actname in delegates: int_action = action.prefix_calls('ext:') assert hasattr(int_action,'lineno') assert hasattr(int_action,'formal_params'), int_action else: int_action = ext_action assert hasattr(int_action,'lineno') assert hasattr(int_action,'formal_params'), int_action else: int_action = ext_action = action assert hasattr(int_action,'lineno') assert hasattr(int_action,'formal_params'), int_action # internal version of the action has mixins checked new_actions[actname] = add_mixins(mod,actname,int_action,no_mixins,use_mixin,lambda m:m) # external version of the action assumes mixins are ok, unless they # are delegated to a currently verified object new_action = add_mixins(mod,actname,ext_action,ext_assumes,use_mixin,mod_mixin) new_actions['ext:'+actname] = new_action # TODO: external version is public if action public *or* called from opaque # public_actions.add('ext:'+actname) else: # TODO: here must check that summarized action does not # have a call dependency on the isolated module action = summarize_action(action) new_actions[actname] = add_mixins(mod,actname,action,after_mixins,use_mixin,mod_mixin) new_actions['ext:'+actname] = add_mixins(mod,actname,action,all_mixins,use_mixin,mod_mixin) # figure out what is exported: exported = set() for e in mod.exports: if not e.scope() and startswith_some(e.exported(),present): # global scope exported.add('ext:' + e.exported()) for actname,action in mod.actions.iteritems(): if not startswith_some(actname,present): for c in action.iter_calls(): if startswith_some(c,present): exported.add('ext:' + c) # print "exported: {}".format(exported) # We allow objects to reference any symbols in global scope, and # we keep axioms declared in global scope. Because of the way # thigs are named, this gives a different condition for keeping # symbols and axioms (in particular, axioms in global scope have # label None). Maybe this needs to be cleaned up. keep_sym = lambda name: (iu.ivy_compose_character not in name or startswith_eq_some(name,present)) keep_ax = lambda name: (name is None or startswith_eq_some(name.rep,present)) # filter the conjectures new_conjs = [c for c in mod.labeled_conjs if keep_ax(c.label)] del mod.labeled_conjs[:] mod.labeled_conjs.extend(new_conjs) # filter the signature # TODO: need a better way to filter signature # new_syms = set(s for s in mod.sig.symbols if keep_sym(s)) # for s in list(mod.sig.symbols): # if s not in new_syms: # del mod.sig.symbols[s] # filter the inits new_inits = [c for c in mod.labeled_inits if keep_ax(c.label)] del mod.labeled_inits[:] mod.labeled_inits.extend(new_inits) init_cond = ivy_logic.And(*(lf.formula for lf in new_inits)) im.module.init_cond = lu.formula_to_clauses(init_cond) # filter the axioms mod.labeled_axioms = [a for a in mod.labeled_axioms if keep_ax(a.label)] # filter definitions mod.concepts = [c for c in mod.concepts if startswith_eq_some(c.args[0].func.name,present)] mod.public_actions.clear() mod.public_actions.update(exported) mod.actions.clear() mod.actions.update(new_actions)