def compile_app(self): args = [a.compile() for a in self.args] # handle action calls in rhs of assignment if expr_context and top_context and self.rep in top_context.actions: returns = top_context.actions[self.rep] if len(returns) != 1: raise IvyError(self, "wrong number of return values") # TODO: right now we can't do anything with multiple returns sorts = [find_sort(r.sort) for r in returns] ress = [] for sort in sorts: res = ivy_logic.Symbol( 'loc:' + str(len(expr_context.local_syms)), sort) expr_context.local_syms.append(res) ress.append(res()) expr_context.code.append( CallAction(*([ivy_ast.Atom(self.rep, args)] + ress))) return ivy_ast.Tuple(*ress) sort = find_sort(returns[0].sort) res = ivy_logic.Symbol('loc:' + str(len(expr_context.local_syms)), sort) expr_context.local_syms.append(res) expr_context.code.append(CallAction(ivy_ast.Atom(self.rep, args), res)) return res() return (ivy_logic.Equals if self.rep == '=' else ivy_logic.find_polymorphic_symbol(self.rep))(*args)
def compile_defn(df): has_consts = any(not isinstance(p, ivy_ast.Variable) for p in df.args[0].args) sig = ivy_logic.sig.copy() if has_consts else ivy_logic.sig with ivy_ast.ASTContext(df): with sig: for p in df.args[0].args: if not isinstance(p, ivy_ast.Variable): compile_const(p, sig) if isinstance(df.args[1], ivy_ast.SomeExpr): ifval = df.args[1].if_value() or df.args[1].params()[0] elseval = df.args[1].else_value() or ifval eqn = ivy_ast.Forall( df.args[1].params(), ivy_ast.Atom( '=', (df.args[0], ivy_ast.Ite(df.args[1].fmla(), ifval, elseval)))) fmla = sortify_with_inference(eqn) args = [list(fmla.variables)[0], fmla.body.args[1].args[0]] if df.args[1].if_value(): args.append(fmla.body.args[1].args[1]) if df.args[1].else_value(): args.append(fmla.body.args[1].args[2]) df = ivy_logic.Definition(fmla.body.args[0], ivy_logic.Some(*args)) else: eqn = ivy_ast.Atom('=', (df.args[0], df.args[1])) eqn = sortify_with_inference(eqn) df = ivy_logic.Definition(eqn.args[0], eqn.args[1]) return df
def compile_inline_call(self, args): params, returns = top_context.actions[self.rep] if len(returns) != 1: raise IvyError(self, "wrong number of return values") # TODO: right now we can't do anything with multiple returns sorts = [cmpl_sort(r.sort) for r in returns] ress = [] for sort in sorts: res = ivy_logic.Symbol('loc:' + str(len(expr_context.local_syms)), sort) expr_context.local_syms.append(res) ress.append(res()) expr_context.code.append( CallAction(*([ivy_ast.Atom(self.rep, args)] + ress))) return ivy_ast.Tuple(*ress) sort = cmpl_sort(returns[0].sort) res = ivy_logic.Symbol('loc:' + str(len(expr_context.local_syms)), sort) expr_context.local_syms.append(res) with ASTContext(self): if len(params) != len(args): raise iu.IvyError( self, "wrong number of input parameters (got {}, expecting {})". format(len(args), len(params))) args = [ sort_infer_contravariant(a, cmpl_sort(p.sort)) for a, p in zip(args, params) ] expr_context.code.append(CallAction(ivy_ast.Atom(self.rep, args), res)) return res()
def compile_app(self): args = [a.compile() for a in self.args] # handle action calls in rhs of assignment if expr_context and top_context and self.rep in top_context.actions: params, returns = top_context.actions[self.rep] if len(returns) != 1: raise IvyError(self, "wrong number of return values") # TODO: right now we can't do anything with multiple returns sorts = [cmpl_sort(r.sort) for r in returns] ress = [] for sort in sorts: res = ivy_logic.Symbol( 'loc:' + str(len(expr_context.local_syms)), sort) expr_context.local_syms.append(res) ress.append(res()) expr_context.code.append( CallAction(*([ivy_ast.Atom(self.rep, args)] + ress))) return ivy_ast.Tuple(*ress) sort = cmpl_sort(returns[0].sort) res = ivy_logic.Symbol('loc:' + str(len(expr_context.local_syms)), sort) expr_context.local_syms.append(res) with ASTContext(self): if len(params) != len(args): raise iu.IvyError( self, "wrong number of input parameters (got {}, expecting {})". format(len(args), len(params))) args = [ sort_infer(a, cmpl_sort(p.sort)) for a, p in zip(args, params) ] expr_context.code.append(CallAction(ivy_ast.Atom(self.rep, args), res)) return res() return (ivy_logic.Equals if self.rep == '=' else ivy_logic.find_polymorphic_symbol(self.rep))(*args)
def learnInv(mod): print "\n" * 2 print "<plearn> Directed to learning Algorithm" global silent, module, numVarFS, featureset silent = True updatenvi(mod) module = mod while True: print "Checking Inductiveness of the Invariant" maxunv, isInvInd = icheck.isInvInductive(mod) print "Invariant is{} Inductive\n".format('' if isInvInd else ' NOT') if isInvInd: # its Inductive so nothing to do silent = False checkInitialCond(mod) break # testfunc(mod) for actname in sorted(mod.public_actions): print "learning Invariant for action {}".format(actname) clf = Classifier() # new classifier newInv = learnWeekestInv(mod, clf, actname) print "\n" * 2 print "<plearn> new Invariant:", newInv a = raw_input('new Invariant learned, press enter to continue') print "\n" * 2 numVarFS = {} # resetting numVarFS featureset = [] # resetting featuerset if newInv != true_clauses(): lf = ivy_ast.LabeledFormula(*[ ivy_ast.Atom('learnedInv' + actname), logic.And(*newInv.fmlas) ]) mod.labeled_conjs.append( lf) # modifying mod and module, obj are copied by refr. updatenvi(mod) # takes care of numVarInv
def compile_call(self): assert top_context ctx = ExprContext(lineno=self.lineno) name = self.args[0].rep if name not in top_context.actions: raise iu.IvyError(self, "call to unknown action: {}".format(name)) with ctx: args = [a.cmpl() for a in self.args[0].args] params, returns = top_context.actions[name] if len(returns) != len(self.args) - 1: raise iu.IvyError( self, "wrong number of output parameters (got {}, expecting {})".format( len(self.args) - 1, len(returns))) if len(params) != len(args): raise iu.IvyError( self, "wrong number of input parameters (got {}, expecting {})".format( len(args), len(params))) with ASTContext(self): mas = [sort_infer(a, cmpl_sort(p.sort)) for a, p in zip(args, params)] # print self.args res = CallAction(*([ivy_ast.Atom(name, mas)] + [a.cmpl() for a in self.args[1:]])) res.lineno = self.lineno ctx.code.append(res) res = ctx.extract() # print "compiled call action: {}".format(res) return res
def cmpl(self): mas = [a.cmpl() for a in self.args[0].args] n = self.args[0].rep # print self.args res = CallAction(*([ivy_ast.Atom(n, mas)] + [a.cmpl() for a in self.args[1:]])) res.lineno = self.lineno # print "compiled call action: {}".format(res) return res
def compile_call(self): ctx = ExprContext(lineno=self.lineno) with ctx: mas = [a.cmpl() for a in self.args[0].args] n = self.args[0].rep # print self.args res = CallAction(*([ivy_ast.Atom(n, mas)] + [a.cmpl() for a in self.args[1:]])) res.lineno = self.lineno ctx.code.append(res) res = ctx.extract() # print "compiled call action: {}".format(res) return res
def expand_schemata(mod,sort_constants,funs): match = Match() res = [] for s in mod.sig.sorts.values(): if not il.is_function_sort(s): match.add(s,s) for name,lf in mod.schemata.iteritems(): schema = lf.formula if any(name.startswith(pref) for pref in ['rec[','lep[','ind[']): continue conc = schema.args[-1] for m in match_schema_prems(list(schema.args[:-1]),sort_constants,funs,match): # iu.dbg('str_map(m)') # iu.dbg('conc') inst = apply_match(m,conc) res.append(ivy_ast.LabeledFormula(ivy_ast.Atom(name),inst)) return res
def cmpl(self): return self mas = [a.cmpl() for a in self.args[0].args] n = self.args[0].rep return InstantiateAction(ivy_ast.Atom(n,mas))
def fresh_label(goals): rn = iu.UniqueRenamer(used=[x.name for x in goals]) return ia.Atom(rn(), [])
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 change(x, y): x = conditionalexp(ivy_ast.Atom('=', x.args[0].args[0], y.args[0].args[0]), x.args[0], y.args[1], x.args[1]) return instantiate(x)
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 field_reference_action(actname, args, top): nformals = len(top_context.actions[actname][0]) return compile_inline_call(ivy_ast.Atom(actname, []), pull_args(args, nformals, actname, top))
def action_app(action, arg): return ia.Atom(action, [arg])
def create_isolate(iso, mod=None, **kwargs): mod = mod or im.module # treat initializers as exports after_inits = mod.mixins["init"] del mod.mixins["init"] mod.exports.extend( ivy_ast.ExportDef(ivy_ast.Atom(a.mixer()), ivy_ast.Atom('')) for a in after_inits) # check all mixin declarations for name, mixins in mod.mixins.iteritems(): for mixin in mixins: with ASTContext(mixins): action1, action2 = (lookup_action(mixin, mod, a.relname) for a in mixin.args) # check all the delagate declarations for dl in mod.delegates: lookup_action(dl.args[0], mod, dl.delegated()) if dl.delegee() and dl.delegee() not in mod.hierarchy: raise iu.IvyError(dl.args[1], "{} is not a module instance".format(name)) # check all the export declarations for exp in mod.exports: expname = exp.args[0].rep if expname not in mod.actions: raise iu.IvyError(exp, "undefined action: {}".format(expname)) # create the import actions, if requested extra_with = [] extra_strip = {} if create_imports.get(): newimps = [] for imp in mod.imports: if imp.args[1].rep == '': impname = imp.args[0].rep if impname not in mod.actions: raise iu.IvyError(imp, "undefined action: {}".format(impname)) action = mod.actions[impname] if not (type(action) == ia.Sequence and not action.args): raise iu.IvyError( imp, "cannot import implemented action: {}".format(impname)) extname = 'imp__' + impname call = ia.CallAction( *([ivy_ast.Atom(extname, action.formal_params)] + action.formal_returns)) call.formal_params = action.formal_params call.formal_returns = action.formal_returns call.lineno = action.lineno mod.actions[impname] = call mod.actions[extname] = action newimps.append( ivy_ast.ImportDef(ivy_ast.Atom(extname), imp.args[1])) extra_with.append(ivy_ast.Atom(impname)) # extra_with.append(ivy_ast.Atom(extname)) if iso and iso in mod.isolates: ps = mod.isolates[iso].params() extra_strip[impname] = [a.rep for a in ps] extra_strip[extname] = [a.rep for a in ps] else: newimps.append(imp) mod.imports = newimps mixers = set() for ms in mod.mixins.values(): for m in ms: mixers.add(m.mixer()) # Determine the mixin order (as a side effect on module.mixins) get_mixin_order(iso, mod) # Construct an isolate if iso: isolate_component(mod, iso, extra_with=extra_with, extra_strip=extra_strip) else: if mod.isolates and cone_of_influence.get(): raise iu.IvyError(None, 'no isolate specified on command line') # apply all the mixins in no particular order for name, mixins in mod.mixins.iteritems(): for mixin in mixins: action1, action2 = (lookup_action(mixin, mod, a.relname) for a in mixin.args) mixed = ia.apply_mixin(mixin, action1, action2) mod.actions[mixin.args[1].relname] = mixed # find the globally exported actions (all if none specified, for compat) if mod.exports: mod.public_actions.clear() for e in mod.exports: if not e.scope(): # global export mod.public_actions.add(e.exported()) else: for a in mod.actions: mod.public_actions.add(a) # Create one big external action if requested for name in mod.public_actions: mod.actions[name].label = name ext = kwargs['ext'] if 'ext' in kwargs else ext_action.get() if ext is not None: ext_acts = [mod.actions[x] for x in sorted(mod.public_actions)] ext_act = ia.EnvAction(*ext_acts) mod.public_actions.add(ext) mod.actions[ext] = ext_act # Check native interpretations of symbols slv.check_compat() # Make concept spaces from the conjecture for i, cax in enumerate(mod.labeled_conjs): fmla = cax.formula csname = 'conjecture:' + str(i) variables = list(lu.used_variables_ast(fmla)) sort = ivy_logic.RelationSort([v.sort for v in variables]) sym = ivy_logic.Symbol(csname, sort) space = ics.NamedSpace(ivy_logic.Literal(0, fmla)) mod.concept_spaces.append((sym(*variables), space)) ith.check_theory() # get rid of useless actions cone = get_mod_cone(mod) if cone_of_influence.get(): for a in list(mod.actions): if a not in cone: del mod.actions[a] else: for a in list(mod.actions): if a not in cone and not a.startswith('ext:') and a not in mixers: ea = 'ext:' + a if ea in mod.actions and ea not in cone: if ia.has_code(mod.actions[a]): iu.warn(mod.actions[a], "action {} is never called".format(a)) fix_initializers(mod, after_inits) # show the compiled code if requested if show_compiled.get(): ivy_printer.print_module(mod)
def l2s(mod, temporal_goal): # modify mod in place # module pass helper funciton def mod_pass(transform): mod.labeled_conjs = [transform(x) for x in mod.labeled_conjs] # TODO: what about axioms and properties? for a in mod.public_actions: action = mod.actions[a] new_action = transform(action) new_action.lineno = action.lineno new_action.formal_params = action.formal_params new_action.formal_returns = action.formal_returns mod.actions[a] = new_action mod.initializers = [(x, transform(y)) for x, y in mod.initializers] l2s_waiting = lg.Const('l2s_waiting', lg.Boolean) l2s_frozen = lg.Const('l2s_frozen', lg.Boolean) l2s_saved = lg.Const('l2s_saved', lg.Boolean) l2s_error = lg.Const('l2s_error', lg.Boolean) l2s_d = lambda sort: lg.Const('l2s_d', lg.FunctionSort(sort, lg.Boolean)) l2s_a = lambda sort: lg.Const('l2s_a', lg.FunctionSort(sort, lg.Boolean)) l2s_w = lambda vs, t: lg.NamedBinder('l2s_w', vs, t) l2s_s = lambda vs, t: lg.NamedBinder('l2s_s', vs, t) l2s_g = lambda vs, t: lg.NamedBinder('l2s_g', vs, t) old_l2s_g = lambda vs, t: lg.NamedBinder('old_l2s_g', vs, t) # add conjectures about monitor state conjs = [ lg.Or(l2s_waiting, l2s_frozen, l2s_saved), lg.Or(lg.Not(l2s_waiting), lg.Not(l2s_frozen)), lg.Or(lg.Not(l2s_waiting), lg.Not(l2s_saved)), lg.Or(lg.Not(l2s_frozen), lg.Not(l2s_saved)), ] for f in conjs: c = ast.LabeledFormula(ast.Atom('l2s_internal'), f) c.lineno = temporal_goal.lineno mod.labeled_conjs.append(c) # add conjecture that we are not in the error state (this is # instead of using an assertion. see below) c = ast.LabeledFormula(ast.Atom('not_l2s_error'), lg.Not(l2s_error)) c.lineno = temporal_goal.lineno mod.labeled_conjs.append(c) #print ilu.used_symbols_asts(mod.labeled_conjs) #print '='*40 #print list(ilu.named_binders_asts(mod.labeled_conjs)) # some normalization # We first convert all temporal operators to named binders, so # it's possible to normalize them. Otherwise we won't have the # connection betweel (globally p(X)) and (globally p(Y)). Note # that we replace them even inside named binders. l2s_gs = set() def _l2s_g(vs, t): vs = tuple(vs) res = l2s_g(vs, t) l2s_gs.add((vs, t)) return res replace_temporals_by_l2s_g = lambda ast: ilu.replace_temporals_by_named_binder_g_ast( ast, _l2s_g) mod_pass(replace_temporals_by_l2s_g) not_temporal_goal = replace_temporals_by_l2s_g( lg.Not(temporal_goal.formula)) if debug.get(): print "=" * 80 + "\nafter replace_temporals_by_named_binder_g_ast" + "\n" * 3 print "=" * 80 + "\nl2s_gs:" for vs, t in sorted(l2s_gs): print vs, t print "=" * 80 + "\n" * 3 print_module(mod) print "=" * 80 + "\n" * 3 # now we normalize all named binders mod_pass(ilu.normalize_named_binders) if debug.get(): print "=" * 80 + "\nafter normalize_named_binders" + "\n" * 3 print_module(mod) print "=" * 80 + "\n" * 3 # TODO: what about normalizing temporal_goal? - temporal_goal # should not contain any named binders except for temporal # properties, so it is normalized by construction # construct the monitor related building blocks uninterpreted_sorts = [ s for s in mod.sig.sorts.values() if type(s) is lg.UninterpretedSort ] reset_a = [ AssignAction(l2s_a(s)(v), l2s_d(s)(v)) for s in uninterpreted_sorts for v in [lg.Var('X', s)] ] add_consts_to_d = [ AssignAction(l2s_d(s)(c), lg.true) for s in uninterpreted_sorts for c in mod.sig.symbols.values() if c.sort == s ] # TODO: maybe add all ground terms, not just consts (if stratified) # TODO: add conjectures that constants are in d and a # figure out which l2s_w and l2s_s are used in conjectures named_binders_conjs = defaultdict( list) # dict mapping names to lists of (vars, body) for b in ilu.named_binders_asts(mod.labeled_conjs): named_binders_conjs[b.name].append((b.variables, b.body)) named_binders_conjs = defaultdict( list, ((k, sorted(list(set(v)))) for k, v in named_binders_conjs.iteritems())) to_wait = [ ] # list of (variables, term) corresponding to l2s_w in conjectures to_wait += named_binders_conjs['l2s_w'] to_save = [ ] # list of (variables, term) corresponding to l2s_s in conjectures to_save += named_binders_conjs['l2s_s'] if debug.get(): print "=" * 40 + "\nto_wait:\n" for vs, t in to_wait: print vs, t # print list(ilu.variables_ast(t)) == list(vs) # print print "=" * 40 save_state = [AssignAction(l2s_s(vs, t)(*vs), t) for vs, t in to_save] done_waiting = [forall(vs, lg.Not(l2s_w(vs, t)(*vs))) for vs, t in to_wait] reset_w = [ AssignAction( l2s_w(vs, t)(*vs), lg.And(*(l2s_d(v.sort)(v) for v in vs))) for vs, t in to_wait ] update_w = [ AssignAction( l2s_w(vs, t)(*vs), lg.And( l2s_w(vs, t)(*vs), lg.Not(t), replace_temporals_by_l2s_g(lg.Not(lg.Globally(ilu.negate(t))))) # ($l2s_w. phi) waits until ( phi | globally ~phi), but # ($l2s_w. ~phi) waits until (~phi | globally phi) (i.e., we avoid "globally ~~phi" here) # note this adds to l2s_gs ) for vs, t in to_wait ] if debug.get(): print "=" * 40 + "\nupdate_w:\n" for x in update_w: print x print print "=" * 40 fair_cycle = [l2s_saved] fair_cycle += done_waiting # projection of relations fair_cycle += [ lg.ForAll( vs, lg.Implies(lg.And(*(l2s_a(v.sort)(v) for v in vs)), lg.Iff(l2s_s(vs, t)(*vs), t))) if len(vs) > 0 else lg.Iff(l2s_s(vs, t), t) for vs, t in to_save if (t.sort == lg.Boolean or isinstance(t.sort, lg.FunctionSort) and t.sort.range == lg.Boolean) ] # projection of functions and constants fair_cycle += [ forall( vs, lg.Implies( lg.And(*([l2s_a(v.sort)(v) for v in vs] + [ lg.Or(l2s_a(t.sort)(l2s_s(vs, t)(*vs)), l2s_a(t.sort)(t)) ])), lg.Eq(l2s_s(vs, t)(*vs), t))) for vs, t in to_save if (isinstance(t.sort, lg.UninterpretedSort) or isinstance(t.sort, lg.FunctionSort) and isinstance(t.sort.range, lg.UninterpretedSort)) ] if debug.get(): print "=" * 40 + "\nfair_cycle:\n" for x in fair_cycle: print x print print "=" * 40 # TODO: figure out why AssertAction doesn't work properly def assert_no_fair_cycle(a): # comment and uncomment the following lines to debug: # res = AssertAction(lg.Not(lg.And(*fair_cycle))) # res = AssertAction(lg.false) # res.lineno = temporal_goal.lineno # res.lineno = a.lineno res = AssignAction(l2s_error, lg.And(*fair_cycle)) return res monitor_edge = lambda s1, s2: [ AssumeAction(s1), AssignAction(s1, lg.false), AssignAction(s2, lg.true), ] change_monitor_state = [ ChoiceAction( # waiting -> frozen Sequence(*(monitor_edge(l2s_waiting, l2s_frozen) + [AssumeAction(x) for x in done_waiting] + reset_a)), # frozen -> saved Sequence(*(monitor_edge(l2s_frozen, l2s_saved) + save_state + reset_w)), # stay in same state (self edge) Sequence(), ) ] # tableau construction to_g = [] # list of (variables, formula) to_g += sorted(list(l2s_gs)) if debug.get(): print '=' * 40 + "\nto_g:\n" for vs, t in to_g: print vs, t, '\n' print '=' * 40 assume_g_axioms = [ AssumeAction(forall(vs, lg.Implies(l2s_g(vs, t)(*vs), t))) for vs, t in to_g ] update_g = [ a for vs, t in to_g for a in [ HavocAction(l2s_g(vs, t)(*vs)), AssumeAction( forall(vs, lg.Implies( old_l2s_g(vs, t)(*vs), l2s_g(vs, t)(*vs)))), AssumeAction( forall( vs, lg.Implies(lg.And(lg.Not(old_l2s_g(vs, t)( *vs)), t), lg.Not(l2s_g(vs, t)(*vs))))), ] ] # now patch the module actions with monitor and tableau if debug.get(): print "public_actions:", mod.public_actions for a in mod.public_actions: action = mod.actions[a] add_params_to_d = [ AssignAction(l2s_d(p.sort)(p), lg.true) for p in action.formal_params ] new_action = concat_actions(*( # TODO: check this with Sharon assume_g_axioms + change_monitor_state + add_params_to_d + update_g + [action] + assume_g_axioms + add_consts_to_d + update_w + [assert_no_fair_cycle(action)])) new_action.lineno = action.lineno new_action.formal_params = action.formal_params new_action.formal_returns = action.formal_returns mod.actions[a] = new_action l2s_init = [ AssignAction(l2s_waiting, lg.true), AssignAction(l2s_frozen, lg.false), AssignAction(l2s_saved, lg.false), AssignAction(l2s_error, lg.false), ] l2s_init += add_consts_to_d l2s_init += reset_w l2s_init += assume_g_axioms l2s_init += [AssumeAction(not_temporal_goal)] mod.initializers.append(('l2s_init', Sequence(*l2s_init))) if debug.get(): print "=" * 80 + "\nafter patching actions" + "\n" * 3 print_module(mod) print "=" * 80 + "\n" * 3 # now replace all named binders by fresh relations named_binders = defaultdict( list) # dict mapping names to lists of (vars, body) for b in ilu.named_binders_asts( chain( mod.labeled_conjs, mod.actions.values(), (y for x, y in mod.initializers), )): named_binders[b.name].append(b) # sort named binders according to a consistent order named_binders = defaultdict( list, ((k, list( sorted( set(v), key=lambda x: (len(x.variables), str(x.variables), str(x.body)), ))) for k, v in named_binders.iteritems())) # make sure old_l2s_g is consistent with l2s_g, so that # old_l2s_g_X is really old l2s_g_X after the substitution assert len(named_binders['l2s_g']) == len(named_binders['old_l2s_g']) assert named_binders['old_l2s_g'] == [ lg.NamedBinder('old_l2s_g', b.variables, b.body) for b in named_binders['l2s_g'] ] subs = dict((b, lg.Const('{}_{}'.format(k, i), b.sort)) for k, v in named_binders.iteritems() for i, b in enumerate(v)) if debug.get(): print "=" * 80 + "\nsubs:" + "\n" * 3 for k in sorted(named_binders.keys()): v = named_binders[k] for i, b in enumerate(v): print '{}_{}'.format(k, i), ' : ', b print "=" * 80 + "\n" * 3 mod_pass(lambda ast: ilu.replace_named_binders_ast(ast, subs)) if debug.get(): print "=" * 80 + "\nafter replace_named_binders" + "\n" * 3 print_module(mod) print "=" * 80 + "\n" * 3