def bmc_trace(prog: syntax.Program, trace: syntax.TraceDecl, s: Solver, sat_checker: Callable[[Solver, List[str]], Optional[logic.Trace]], log: bool = False) -> Optional[logic.Trace]: n_states = len(list(trace.transitions())) + 1 if log: print('%s states' % (n_states, )) keys = ['state%2d' % i for i in range(n_states)] for k in keys: s.get_translator( k) # initialize all the keys before pushing a solver stack frame with s: lator = s.get_translator(keys[0]) if len(trace.components) > 0 and not isinstance( trace.components[0], syntax.AssertDecl): for init in prog.inits(): s.add(lator.translate_expr(init.expr)) i = 0 for c in trace.components: if isinstance(c, syntax.AssertDecl): if c.expr is None: if i != 0: utils.print_error_and_exit( c.tok, 'assert init is only allowed in the first state') for init in prog.inits(): s.add( s.get_translator(keys[i]).translate_expr( init.expr)) else: s.add(s.get_translator(keys[i]).translate_expr(c.expr)) else: te: syntax.TransitionExpr = c.transition if isinstance(te, syntax.AnyTransition): logic.assert_any_transition(s, str(i), keys[i + 1], keys[i], allow_stutter=True) else: l = [] for call in te.calls: tid = z3.Bool( logic.get_transition_indicator( str(i), call.target)) l.append(tid) s.add(tid == translate_transition_call( s, keys[i + 1], keys[i], call)) s.add(z3.Or(*l)) i += 1 return sat_checker(s, keys)
def relaxed_program(prog: syntax.Program) -> syntax.Program: new_decls: List[syntax.Decl] = [d for d in prog.sorts()] actives: Dict[syntax.SortDecl, syntax.RelationDecl] = {} for sort in prog.sorts(): name = prog.scope.fresh('active_' + sort.name) r = syntax.RelationDecl(name, arity=[syntax.UninterpretedSort(sort.name)], mutable=True, derived=None, annotations=[]) actives[sort] = r new_decls.append(r) # active relations initial conditions: always true for sort in prog.sorts(): name = prog.scope.fresh(sort.name[0].upper()) expr = syntax.Forall([syntax.SortedVar(name, None)], syntax.Apply(actives[sort].name, [syntax.Id(name)])) new_decls.append(syntax.InitDecl(name=None, expr=expr)) for d in prog.decls: if isinstance(d, syntax.SortDecl): pass # already included above elif isinstance(d, syntax.RelationDecl): if d.derived_axiom is not None: expr = syntax.relativize_quantifiers(actives, d.derived_axiom) new_decls.append(syntax.RelationDecl(d.name, d.arity, d.mutable, expr, d.annotations)) else: new_decls.append(d) elif isinstance(d, syntax.ConstantDecl): new_decls.append(d) elif isinstance(d, syntax.FunctionDecl): new_decls.append(d) elif isinstance(d, syntax.AxiomDecl): new_decls.append(d) elif isinstance(d, syntax.InitDecl): new_decls.append(d) elif isinstance(d, syntax.DefinitionDecl): assert not isinstance(d.body, syntax.BlockStatement), \ "relax does not support transitions written in imperative syntax" mods, expr = d.body expr = syntax.relativize_quantifiers(actives, expr) if d.is_public_transition: guard = syntax.relativization_guard_for_binder(actives, d.binder) expr = syntax.And(guard, expr) new_decls.append(syntax.DefinitionDecl(d.is_public_transition, d.num_states, d.name, params=d.binder.vs, body=(mods, expr))) elif isinstance(d, syntax.InvariantDecl): expr = syntax.relativize_quantifiers(actives, d.expr) new_decls.append(syntax.InvariantDecl(d.name, expr=expr, is_safety=d.is_safety, is_sketch=d.is_sketch)) else: assert False, d new_decls.append(relaxation_action_def(prog, actives=actives, fresh=True)) res = syntax.Program(new_decls) res.resolve() # #sorrynotsorry return res
def typecheck_program(prog: syntax.Program) -> None: typecheck_program_vocab(prog) for d in prog.decls_containing_exprs(): typecheck_declcontainingexpr(prog.scope, d) for tr in prog.traces(): typecheck_tracedecl(prog.scope, tr) assert len(prog.scope.stack) == 0
def typecheck_program_vocab(prog: syntax.Program) -> None: prog.scope = scope = syntax.Scope[InferenceSort]() for s in prog.sorts(): typecheck_sortdecl(scope, s) for rcf in prog.relations_constants_and_functions(): typecheck_statedecl(scope, rcf) for d in prog.definitions(): scope.add_definition(d)
def bmc_trace(prog: syntax.Program, trace: syntax.TraceDecl, s: Solver, sat_checker: Callable[[Solver, int], Optional[logic.Trace]], log: bool = False) -> Optional[logic.Trace]: n_states = len(list(trace.transitions())) + 1 if log: print('%s states' % (n_states, )) lator = s.get_translator(n_states) with s.new_frame(): if len(trace.components) > 0 and not isinstance( trace.components[0], syntax.AssertDecl): for init in prog.inits(): s.add(lator.translate_expr(init.expr)) i = 0 for c in trace.components: if isinstance(c, syntax.AssertDecl): if c.expr is None: if i != 0: utils.print_error_and_exit( c.span, 'assert init is only allowed in the first state') for init in prog.inits(): assert i == 0 s.add(lator.translate_expr(init.expr)) else: s.add(lator.translate_expr(New(c.expr, i))) else: te: syntax.TransitionExpr = c.transition if isinstance(te, syntax.AnyTransition): logic.assert_any_transition(s, lator, i, allow_stutter=True) else: l = [] for call in te.calls: tid = z3.Bool( logic.get_transition_indicator( str(i), call.target)) l.append(tid) s.add( z3.Implies( tid, translate_transition_call(s, lator, i, call))) s.add(z3.Or(*l)) i += 1 return sat_checker(s, n_states)
def decls_quantifier_alternation_graph(prog: Program, additional: List[Expr]) -> DiGraph: res = quantifier_alternation_graph( prog, [axiom.expr for axiom in prog.axioms()] + [cast(Expr, rel.derived_axiom) for rel in prog.derived_relations()] + additional) for f in prog.functions(): for asort in f.arity: esort = f.sort res.add_edge( prog.scope.get_sort_checked(str(asort)).name, prog.scope.get_sort_checked(str(esort)).name) return res
def functions_total_axioms(prog: syntax.Program) -> List[Expr]: res = [] for func in prog.functions(): # TODO: generation of part of the formula duplicated from relaxation_action_def. # TODO: would be best to beef up the expression-generation library names: List[str] = [] params = [] for arg_sort in func.arity: arg_sort_decl = syntax.get_decl_from_sort(arg_sort) name = prog.scope.fresh(arg_sort_decl.name[0].upper(), also_avoid=names) names.append(name) params.append(syntax.SortedVar(name, arg_sort)) ap_func = syntax.Apply(func.name, tuple(syntax.Id(v.name) for v in params)) name = prog.scope.fresh('y', also_avoid=names) ax = syntax.Forall( tuple(params), syntax.Exists((syntax.SortedVar(name, func.sort), ), syntax.Eq(syntax.Id(name), ap_func))) with prog.scope.n_states(1): typechecker.typecheck_expr(prog.scope, ax, syntax.BoolSort) res.append(ax) return res
def closing_qa_cycle(prog: syntax.Program, free_vars_sorts: List[syntax.SortDecl], existentially_quantified_sorts: List[syntax.SortDecl]) -> bool: qa_graph = prog.decls_quantifier_alternation_graph([]) assert networkx.is_directed_acyclic_graph(qa_graph) for asort in free_vars_sorts: for esort in existentially_quantified_sorts: qa_graph.add_edge(asort.name, esort.name) return not networkx.is_directed_acyclic_graph(qa_graph)
def consts_exist_axioms(prog: syntax.Program) -> List[Expr]: res = [] for c in prog.constants(): name = prog.scope.fresh('e_%s' % c.name) ax = syntax.Exists((syntax.SortedVar(name, c.sort), ), syntax.Eq(syntax.Id(c.name), syntax.Id(name))) with prog.scope.n_states(1): typechecker.typecheck_expr(prog.scope, ax, syntax.BoolSort) res.append(ax) return res
def _init_axioms(self, prog: syntax.Program, include_program: bool, reassert_axioms: bool, additional_mutable_axioms: List[Expr]) -> None: axioms = [] mutable_axioms = [] if include_program: if not reassert_axioms: axioms += [a.expr for a in prog.axioms()] else: mutable_axioms += [a.expr for a in prog.axioms()] mutable_axioms += [ r.derived_axiom for r in prog.derived_relations() if r.derived_axiom is not None ] mutable_axioms += additional_mutable_axioms t = self.get_translator(0) for aexpr in axioms: self.add(t.translate_expr(aexpr)) self.register_mutable_axioms(mutable_axioms)
def load_relaxed_trace_from_updr_cex(prog: Program, s: Solver) -> logic.Trace: import xml.dom.minidom # type: ignore collection = xml.dom.minidom.parse( "paxos_derived_trace.xml").documentElement components: List[syntax.TraceComponent] = [] xml_decls = reversed(collection.childNodes) seen_first = False for elm in xml_decls: if isinstance(elm, xml.dom.minidom.Text): # type: ignore continue if elm.tagName == 'state': diagram = parser.parse_expr(elm.childNodes[0].data) typechecker.typecheck_expr(prog.scope, diagram, syntax.BoolSort) assert isinstance( diagram, syntax.QuantifierExpr) and diagram.quant == 'EXISTS' active_clauses = [ relaxed_traces.active_var(v.name, str(v.sort)) for v in diagram.get_vs() ] if not seen_first: # restrict the domain to be subdomain of the diagram's existentials seen_first = True import itertools # type: ignore for sort, vars in itertools.groupby( diagram.get_vs(), lambda v: v.sort): # TODO; need to sort first free_var = syntax.SortedVar( syntax.the_program.scope.fresh("v_%s" % str(sort)), None) # TODO: diagram simplification omits them from the exists somewhere consts = list( filter(lambda c: c.sort == sort, prog.constants())) els: Sequence[Union[syntax.SortedVar, syntax.ConstantDecl]] els = list(vars) els += consts restrict_domain = syntax.Forall( (free_var, ), syntax.Or(*(syntax.Eq(syntax.Id(free_var.name), syntax.Id(v.name)) for v in els))) active_clauses += [restrict_domain] diagram_active = syntax.Exists( diagram.get_vs(), syntax.And(diagram.body, *active_clauses)) typechecker.typecheck_expr(prog.scope, diagram_active, syntax.BoolSort) components.append(syntax.AssertDecl(expr=diagram_active)) elif elm.tagName == 'action': action_name = elm.childNodes[0].data.split()[0] tcall = syntax.TransitionCalls( calls=[syntax.TransitionCall(target=action_name, args=None)]) components.append(syntax.TraceTransitionDecl(transition=tcall)) else: assert False, "unknown xml tagName" trace_decl = syntax.TraceDecl(components=components, sat=True) migrated_trace = bmc_trace( prog, trace_decl, s, lambda s, ks: logic.check_solver(s, ks, minimize=True), log=False) assert migrated_trace is not None import pickle pickle.dump(migrated_trace, open("migrated_trace.p", "wb")) return migrated_trace
def active_rel_by_sort(prog: syntax.Program) -> Dict[syntax.SortDecl, syntax.RelationDecl]: return dict((sort, active_rel(sort)) for sort in prog.sorts())
def relaxation_action_def(prog: syntax.Program, actives: Optional[Dict[syntax.SortDecl, syntax.RelationDecl]]=None, fresh: bool=True) \ -> syntax.DefinitionDecl: decrease_name = (prog.scope.fresh('decrease_domain') if fresh else 'decrease_domain') mods = [] conjs: List[Expr] = [] if actives is None: actives = active_rel_by_sort(prog) # a conjunct allowing each domain to decrease for sort in prog.sorts(): name = prog.scope.fresh(sort.name[0].upper()) ap = syntax.Apply(actives[sort].name, [syntax.Id(None, name)]) expr = syntax.Forall([syntax.SortedVar(None, name, None)], syntax.Implies(ap, syntax.Old(ap))) conjs.append(expr) mods.append(syntax.ModifiesClause(None, actives[sort].name)) # constants are active for const in prog.constants(): conjs.append(syntax.Apply(actives[syntax.get_decl_from_sort(const.sort)].name, [syntax.Id(None, const.name)])) # functions map active to active for func in prog.functions(): names: List[str] = [] func_conjs = [] for arg_sort in func.arity: arg_sort_decl = syntax.get_decl_from_sort(arg_sort) name = prog.scope.fresh(arg_sort_decl.name[0].upper(), also_avoid=names) names.append(name) func_conjs.append(syntax.Apply(actives[arg_sort_decl].name, [syntax.Id(None, name)])) ap_func = syntax.Old(syntax.Apply(func.name, [syntax.Id(None, name) for name in names])) active_func = syntax.Apply(actives[syntax.get_decl_from_sort(func.sort)].name, [ap_func]) conjs.append(syntax.Forall([syntax.SortedVar(None, name, None) for name in names], syntax.Implies(syntax.And(*func_conjs), active_func))) # (relativized) axioms hold after relaxation for axiom in prog.axioms(): if not syntax.is_universal(axiom.expr): conjs.append(syntax.relativize_quantifiers(actives, axiom.expr)) # derived relations have the same interpretation on the active domain for rel in prog.derived_relations(): names = [] rel_conjs = [] for arg_sort in rel.arity: arg_sort_decl = syntax.get_decl_from_sort(arg_sort) name = prog.scope.fresh(arg_sort_decl.name[0].upper(), also_avoid=names) names.append(name) rel_conjs.append(syntax.Apply(actives[arg_sort_decl].name, [syntax.Id(None, name)])) ap_rel = syntax.Apply(rel.name, [syntax.Id(None, name) for name in names]) conjs.append(syntax.Forall([syntax.SortedVar(None, name, None) for name in names], syntax.Implies(syntax.And(*rel_conjs), syntax.Iff(ap_rel, syntax.Old(ap_rel))))) return syntax.DefinitionDecl(None, public=True, twostate=True, name=decrease_name, params=[], body=(mods, syntax.And(*conjs)))
def relaxed_program(prog: syntax.Program) -> syntax.Program: new_decls: List[syntax.Decl] = [d for d in prog.sorts()] actives: Dict[syntax.SortDecl, syntax.RelationDecl] = {} for sort in prog.sorts(): name = prog.scope.fresh('active_' + sort.name) r = syntax.RelationDecl(name, arity=(syntax.UninterpretedSort(sort.name), ), mutable=True) actives[sort] = r new_decls.append(r) # active relations initial conditions: always true for sort in prog.sorts(): name = prog.scope.fresh(sort.name[0].upper()) expr = syntax.Forall((syntax.SortedVar(name, None), ), syntax.Apply(actives[sort].name, (syntax.Id(name), ))) new_decls.append(syntax.InitDecl(name=None, expr=expr)) for d in prog.decls: if isinstance(d, syntax.SortDecl): pass # already included above elif isinstance(d, syntax.RelationDecl): if d.derived_axiom is not None: expr = syntax.relativize_quantifiers(actives, d.derived_axiom) new_decls.append( syntax.RelationDecl(d.name, d.arity, d.mutable, expr, annotations=d.annotations)) else: new_decls.append(d) elif isinstance(d, syntax.ConstantDecl): new_decls.append(d) elif isinstance(d, syntax.FunctionDecl): new_decls.append(d) elif isinstance(d, syntax.AxiomDecl): new_decls.append(d) elif isinstance(d, syntax.InitDecl): new_decls.append(d) elif isinstance(d, syntax.DefinitionDecl): relativized_def = relativize_decl(d, actives, prog.scope, inline_relax_actives=False) new_decls.append(relativized_def) elif isinstance(d, syntax.InvariantDecl): expr = syntax.relativize_quantifiers(actives, d.expr) new_decls.append( syntax.InvariantDecl(d.name, expr=expr, is_safety=d.is_safety, is_sketch=d.is_sketch)) else: assert False, d new_decls.append(relaxation_action_def(prog, actives=actives, fresh=True)) res = syntax.Program(new_decls) typechecker.typecheck_program(res) # #sorrynotsorry return res
def relaxation_action_def(prog: syntax.Program, actives: Optional[Dict[syntax.SortDecl, syntax.RelationDecl]] = None, fresh: bool = True) -> syntax.DefinitionDecl: decrease_name = (prog.scope.fresh('decrease_domain') if fresh else 'decrease_domain') mods: Tuple[syntax.ModifiesClause, ...] = () conjs: List[Expr] = [] if actives is None: actives = active_rel_by_sort(prog) # a conjunct allowing each domain to decrease new_mods, new_conjs = relax_actives_action_chunk(prog.scope, actives) mods += new_mods conjs += new_conjs # constants are active for const in prog.constants(): conjs.append( syntax.New( syntax.Apply( actives[syntax.get_decl_from_sort(const.sort)].name, (syntax.Id(const.name), )))) # functions map active to active for func in prog.functions(): names: List[str] = [] func_conjs = [] for arg_sort in func.arity: arg_sort_decl = syntax.get_decl_from_sort(arg_sort) name = prog.scope.fresh(arg_sort_decl.name[0].upper(), also_avoid=names) names.append(name) func_conjs.append( syntax.New( syntax.Apply(actives[arg_sort_decl].name, (syntax.Id(name), )))) ap_func = syntax.Apply(func.name, tuple(syntax.Id(name) for name in names)) name = prog.scope.fresh('y', also_avoid=names) active_func = syntax.Let( syntax.SortedVar(name, func.sort), ap_func, syntax.New( syntax.Apply( actives[syntax.get_decl_from_sort(func.sort)].name, (syntax.Id(name), )))) conjs.append( syntax.Forall( tuple(syntax.SortedVar(name, None) for name in names), syntax.Implies(syntax.And(*func_conjs), active_func))) # (relativized) axioms hold after relaxation for axiom in prog.axioms(): if not syntax.is_universal(axiom.expr): conjs.append(syntax.relativize_quantifiers(actives, axiom.expr)) # derived relations have the same interpretation on the active domain for rel in prog.derived_relations(): names = [] rel_conjs = [] for arg_sort in rel.arity: arg_sort_decl = syntax.get_decl_from_sort(arg_sort) name = prog.scope.fresh(arg_sort_decl.name[0].upper(), also_avoid=names) names.append(name) rel_conjs.append( syntax.Apply(actives[arg_sort_decl].name, (syntax.Id(name), ))) ap_rel = syntax.Apply(rel.name, tuple(syntax.Id(name) for name in names)) conjs.append( syntax.Forall( tuple(syntax.SortedVar(name, None) for name in names), syntax.Implies(syntax.And(*rel_conjs), syntax.Iff(syntax.New(ap_rel), ap_rel)))) return syntax.DefinitionDecl(is_public_transition=True, num_states=2, name=decrease_name, params=(), mods=mods, expr=syntax.And(*conjs))