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 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 relax_actives_action_chunk(scope: syntax.Scope, actives: Dict[syntax.SortDecl, syntax.RelationDecl]) \ -> Tuple[Tuple[syntax.ModifiesClause, ...], List[Expr]]: new_mods = [] new_conjs = [] for sort, active_rel in actives.items(): name = scope.fresh(sort.name[0].upper()) ap = syntax.Apply(active_rel.name, (syntax.Id(name), )) expr = syntax.Forall((syntax.SortedVar(name, None), ), syntax.Implies(syntax.New(ap), ap)) new_conjs.append(expr) new_mods.append(syntax.ModifiesClause(actives[sort].name)) return tuple(new_mods), new_conjs
def sandbox(s: Solver) -> None: #################################################################################### # SANDBOX for playing with relaxed traces import pickle trns: logic.Trace = pickle.load(open("paxos_trace.p", "rb")) diff_conjunctions = relaxed_traces.derived_rels_candidates_from_trace( trns, [], 1, 3) print("num candidate relations:", len(diff_conjunctions)) for diffing_conjunction in diff_conjunctions: # print("relation:") # for conj in diffing_conjunction: # print("\t %s" % str(conj)) print(diffing_conjunction[1]) derrel_name = syntax.the_program.scope.fresh("nder") (free_vars, def_expr) = diff_conjunctions[0] def_axiom = syntax.Forall( free_vars, syntax.Iff( syntax.Apply(derrel_name, [syntax.Id(None, v.name) for v in free_vars]), # TODO: extract pattern def_expr)) derrel = syntax.RelationDecl( tok=None, name=derrel_name, arity=[syntax.safe_cast_sort(var.sort) for var in free_vars], mutable=True, derived=def_axiom, annotations=[]) # TODO: this irreversibly adds the relation to the context, wrap derrel.resolve(syntax.the_program.scope) syntax.the_program.decls.append( derrel ) # TODO: hack! because RelationDecl.resolve only adds to prog.scope s.mutable_axioms.extend([ def_axiom ]) # TODO: hack! currently we register these axioms only on solver init print("Trying derived relation:", derrel) # the new decrease_domain action incorporates restrictions that derived relations remain the same on active tuples new_decrease_domain = relaxed_traces.relaxation_action_def( syntax.the_program, fresh=False) new_prog = relaxed_traces.replace_relaxation_action( syntax.the_program, new_decrease_domain) new_prog.resolve() print(new_prog) syntax.the_program = new_prog trace_decl = next(syntax.the_program.traces()) trns2_o = bmc_trace(new_prog, trace_decl, s, lambda s, ks: logic.check_solver(s, ks, minimize=True)) assert trns2_o is not None trns2 = cast(logic.Trace, trns2_o) print(trns2) print() print( trns2.as_state(relaxed_traces.first_relax_step_idx(trns2) + 1).rel_interp[derrel]) assert not relaxed_traces.is_rel_blocking_relax( trns2, relaxed_traces.first_relax_step_idx(trns2), ([(v, str(syntax.safe_cast_sort(v.sort))) for v in free_vars], def_expr)) diff_conjunctions = list( filter( lambda candidate: relaxed_traces.is_rel_blocking_relax( trns2, relaxed_traces.first_relax_step_idx(trns2), ([(v, str(syntax.safe_cast_sort(v.sort))) for v in free_vars], def_expr)), diff_conjunctions)) print("num candidate relations:", len(diff_conjunctions)) for diffing_conjunction in diff_conjunctions: # print("relation:") # for conj in diffing_conjunction: # print("\t %s" % str(conj)) print(diffing_conjunction) print() assert False
def sandbox(s: Solver) -> None: #################################################################################### # SANDBOX for playing with relaxed traces import pickle trns: logic.Trace = pickle.load(open("paxos_trace.p", "rb")) diff_conjunctions = relaxed_traces.derived_rels_candidates_from_trace( trns, [], 2, 3) print("num candidate relations:", len(diff_conjunctions)) for diffing_conjunction in diff_conjunctions: # print("relation:") # for conj in diffing_conjunction: # print("\t %s" % str(conj)) print(diffing_conjunction[1]) derrel_name = syntax.the_program.scope.fresh("nder") (free_vars, def_expr) = diff_conjunctions[0] def_axiom = syntax.Forall( tuple(free_vars), syntax.Iff( syntax.Apply(derrel_name, tuple(syntax.Id(v.name) for v in free_vars)), # TODO: extract pattern def_expr)) derrel = syntax.RelationDecl( name=derrel_name, arity=tuple(syntax.safe_cast_sort(var.sort) for var in free_vars), mutable=True, derived=def_axiom) # TODO: this irreversibly adds the relation to the context, wrap typechecker.typecheck_statedecl(syntax.the_program.scope, derrel) syntax.the_program.decls.append( derrel ) # TODO: hack! because typecheck_statedecl only adds to prog.scope s.mutable_axioms.extend([ def_axiom ]) # TODO: hack! currently we register these axioms only on solver init print("Trying derived relation:", derrel) # the new decrease_domain action incorporates restrictions that derived relations remain the same on active tuples new_decrease_domain = relaxed_traces.relaxation_action_def( syntax.the_program, fresh=False) new_prog = relaxed_traces.replace_relaxation_action( syntax.the_program, new_decrease_domain) typechecker.typecheck_program(new_prog) print(new_prog) syntax.the_program = new_prog # TODO: recover this, making sure the candidate blocks the trace # trace_decl = next(syntax.the_program.traces()) # trns2_o = bmc_trace(new_prog, trace_decl, s, lambda s, ks: logic.check_solver(s, ks, minimize=True)) # assert trns2_o is None # migrated_trace = load_relaxed_trace_from_updr_cex(syntax.the_program, s) import pickle trns2_o = pickle.load(open("migrated_trace.p", "rb")) trns2 = cast(logic.Trace, trns2_o) print(trns2) print() assert not relaxed_traces.is_rel_blocking_relax( trns2, ([(v, str(syntax.safe_cast_sort(v.sort))) for v in free_vars], def_expr)) # for candidate in diff_conjunctions: # print("start checking") # print() # if str(candidate[1]) == ('exists v0:node. member(v0, v1) & left_round(v0, v2) ' # '& !vote(v0, v2, v3) & active_node(v0)'): # print(candidate) # assert False # resush = relaxed_traces.is_rel_blocking_relax_step( # trns2, 11, # ([(v, str(syntax.safe_cast_sort(v.sort))) for v in candidate[0]], # candidate[1])) # # res2 = trns2.as_state(0).eval(syntax.And(*[i.expr for i in syntax.the_program.inits()])) # # # resush = trns2.as_state(7).eval(syntax.And(*[i.expr for i in syntax.the_program.inits()])) # print(resush) # assert False # assert False diff_conjunctions = list( filter( lambda candidate: relaxed_traces.is_rel_blocking_relax( trns2, ([(v, str(syntax.safe_cast_sort(v.sort))) for v in candidate[0]], candidate[1])), diff_conjunctions)) print("num candidate relations:", len(diff_conjunctions)) for diffing_conjunction in diff_conjunctions: # print("relation:") # for conj in diffing_conjunction: # print("\t %s" % str(conj)) print(diffing_conjunction[1]) print() assert False
def active_var(name: str, sort_name: str) -> syntax.Expr: return syntax.Apply('active_%s' % sort_name, [syntax.Id(None, name)])
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 as_expr(self, els_trans: Callable[[str],str]) -> Expr: fact_free_vars = syntax.Apply(self._rel.name, [syntax.Id(None, els_trans(e)) for e in self._els]) if not self._is_positive(): fact_free_vars = syntax.Not(fact_free_vars) return fact_free_vars
def derived_rels_candidates_from_trace(trns: Trace, more_traces: List[Trace], max_conj_size: int, max_free_vars: int) -> List[Tuple[List[syntax.SortedVar],Expr]]: first_relax_idx = first_relax_step_idx(trns) pre_relax_state = trns.as_state(first_relax_idx) post_relax_state = trns.as_state(first_relax_idx + 1) assert pre_relax_state.univs == post_relax_state.univs # relaxed elements relaxed_elements = [] for sort, univ in pre_relax_state.univs.items(): active_rel_name = 'active_' + sort.name # TODO: de-duplicate pre_active_interp = dict_val_from_rel_name(active_rel_name, pre_relax_state.rel_interp) post_active_interp = dict_val_from_rel_name(active_rel_name, post_relax_state.rel_interp) pre_active_elements = [tup[0] for (tup, b) in pre_active_interp if b] post_active_elements = [tup[0] for (tup, b) in post_active_interp if b] assert set(post_active_elements).issubset(set(pre_active_elements)) for relaxed_elem in utils.OrderedSet(pre_active_elements) - set(post_active_elements): relaxed_elements.append((sort, relaxed_elem)) # pre-relaxation step facts concerning at least one relaxed element (other to be found by UPDR) relevant_facts: List[Union[RelationFact,FunctionFact,InequalityFact]] = [] for rel, rintp in pre_relax_state.rel_interp.items(): for rfact in rintp: (elms, polarity) = rfact relation_fact = RelationFact(rel, elms, polarity) if set(relation_fact.involved_elms()) & set(ename for (_, ename) in relaxed_elements): relevant_facts.append(relation_fact) for func, fintp in pre_relax_state.func_interp.items(): for ffact in fintp: (els_params, els_res) = ffact function_fact = FunctionFact(func, els_params, els_res) if set(function_fact.involved_elms()) & set(ename for (_, ename) in relaxed_elements): relevant_facts.append(function_fact) for sort, elm in relaxed_elements: # other inequalities presumably handled by UPDR for other_elm in pre_relax_state.univs[sort]: if other_elm == elm: continue relevant_facts.append(InequalityFact(elm, other_elm)) # facts blocking this specific relaxation step diff_conjunctions = [] candidates_cache: Set[str] = set() for fact_lst in itertools.combinations(relevant_facts, max_conj_size): elements = utils.OrderedSet(itertools.chain.from_iterable(fact.involved_elms() for fact in fact_lst)) relaxed_elements_relevant = [elm for (_, elm) in relaxed_elements if elm in elements] vars_from_elm = dict((elm, syntax.SortedVar(None, syntax.the_program.scope.fresh("v%d" % i), None)) for (i, elm) in enumerate(elements)) parameter_elements = elements - set(relaxed_elements_relevant) if len(parameter_elements) > max_free_vars: continue conjuncts = [fact.as_expr(lambda elm: vars_from_elm[elm].name) for fact in fact_lst] # for elm, var in vars_from_elm.items(): # TODO: make the two loops similar for elm in relaxed_elements_relevant: var = vars_from_elm[elm] sort = pre_relax_state.element_sort(elm) active_element_conj = syntax.Apply('active_%s' % sort.name, [syntax.Id(None, var.name)]) conjuncts.append(active_element_conj) derived_relation_formula = syntax.Exists([vars_from_elm[elm] for (_, elm) in relaxed_elements if elm in vars_from_elm], syntax.And(*conjuncts)) if str(derived_relation_formula) in candidates_cache: continue candidates_cache.add(str(derived_relation_formula)) if closing_qa_cycle(syntax.the_program, [pre_relax_state.element_sort(elm) for elm in parameter_elements], [pre_relax_state.element_sort(elm) for elm in relaxed_elements_relevant]): # adding the derived relation would close a quantifier alternation cycle, discard the candidate continue # if trns.eval_double_vocab(diffing_formula, first_relax_idx): if is_rel_blocking_relax(trns, first_relax_idx, ([(vars_from_elm[elm], pre_relax_state.element_sort(elm).name) for elm in parameter_elements], derived_relation_formula)): # if all(trs.eval_double_vocab(diffing_formula, first_relax_step_idx(trs)) for trs in more_traces): diff_conjunctions.append(([vars_from_elm[elm] for elm in parameter_elements], derived_relation_formula)) return diff_conjunctions
def active_var(name: str, sort_name: str) -> syntax.Expr: return syntax.Apply('active_%s' % sort_name, (syntax.Id(name), ))
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))