def option_printer(state: State, s: SortDecl, elt: str, args: List[str]) -> str: assert len(args) == 2 is_none_name, value_name = args is_none = get_relation(is_none_name) value = get_function(value_name) option_sort = UninterpretedSort(None, s.name) assert not is_none.mutable and not value.mutable and \ is_none.arity == [option_sort] and value.arity == [option_sort] elt_sort = syntax.get_decl_from_sort(value.sort) none: Optional[str] = None for tup, b in state.rel_interp[is_none]: if b: assert none is None and len(tup) == 1 none = tup[0] assert none is not None if elt == none: return 'None' else: the_value: Optional[str] = None for tup, res in state.func_interp[value]: assert len(tup) == 1 if tup[0] == elt: assert the_value is None the_value = res assert the_value is not None return 'Some(%s)' % (logic.print_element(state, elt_sort, the_value))
def option_printer(struct: FirstOrderStructure, s: SortDecl, elt: str, args: List[str]) -> str: assert len(args) == 2 is_none_name, value_name = args is_none = get_relation(is_none_name) value = get_function(value_name) option_sort = UninterpretedSort(s.name) assert not is_none.mutable and not value.mutable and \ is_none.arity == (option_sort,) and value.arity == (option_sort,) elt_sort = syntax.get_decl_from_sort(value.sort) none: Optional[str] = None for tup, b in struct.rel_interps[is_none].items(): if b: assert none is None and len(tup) == 1 none = tup[0] assert none is not None if elt == none: return 'None' else: the_value: Optional[str] = None for tup, res in struct.func_interps[value].items(): assert len(tup) == 1 if tup[0] == elt: assert the_value is None the_value = res assert the_value is not None return 'Some(%s)' % (print_element(struct, elt_sort, the_value))
def const_printer(state: State, s: SortDecl, elt: str, args: List[str]) -> str: prog = syntax.the_program for c in prog.constants(): if syntax.get_decl_from_sort(c.sort) == s and state.const_interp[c] == elt: return c.name return elt
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 print_element(struct: FirstOrderStructure, s: Union[SortDecl, syntax.Sort], elt: Element) -> str: if not isinstance(s, SortDecl): if isinstance(s, (syntax._BoolSort, syntax._IntSort)): return elt s = syntax.get_decl_from_sort(s) return try_printed_by(struct, s, elt) or elt
def arbitrary_interp_c(c: ConstantDecl) -> Element: if isinstance(c.sort, syntax._BoolSort): return 'false' elif isinstance(c.sort, syntax._IntSort): return '0' assert isinstance(c.sort, syntax.UninterpretedSort) sort = c.sort return get_univ(syntax.get_decl_from_sort(sort))[0]
def set_printer(state: State, s: SortDecl, elt: str, args: List[str]) -> str: assert len(args) == 1 member_name = args[0] member = get_relation(member_name) assert len(member.arity) == 2 and not member.mutable set_sort = UninterpretedSort(None, s.name) assert member.arity[1] == set_sort item_sort = member.arity[0] item_sort_decl = syntax.get_decl_from_sort(item_sort) items: Set[str] = set() for tup, b in state.rel_interp[member]: assert len(tup) == 2 item, set_id = tup if b and set_id == elt: items.add(item) return '{%s}' % (','.join(sorted(logic.print_element(state, item_sort_decl, x) for x in items)),)
def set_printer(struct: FirstOrderStructure, s: SortDecl, elt: str, args: List[str]) -> str: assert len(args) == 1 member_name = args[0] member = get_relation(member_name) assert len(member.arity) == 2 and not member.mutable set_sort = UninterpretedSort(s.name) assert member.arity[1] == set_sort item_sort = member.arity[0] item_sort_decl = syntax.get_decl_from_sort(item_sort) items: Set[str] = set() for tup, b in struct.rel_interps[member].items(): assert len(tup) == 2 item, set_id = tup if b and set_id == elt: items.add(item) return '{' + ','.join( sorted(print_element(struct, item_sort_decl, x) for x in items)) + '}'
def log_printer(state: State, s: SortDecl, elt: str, args: List[str]) -> str: # args should at least hold an index total order and used relation assert len(args) > 1 n_values = len(args) - 2 index_le = get_relation(args[0]) assert len(index_le.arity) == 2 assert index_le.arity[0] == index_le.arity[1] and not index_le.mutable index_sort = syntax.get_decl_from_sort(index_le.arity[0]) index_used = get_relation(args[1]) def default_values() -> List[List[str]]: return [[] for x in range(n_values)] def assert_valid_rel_or_func(rel_or_func: Union[RelationDecl, FunctionDecl]) -> None: assert len(rel_or_func.arity) >= 2 assert isinstance(rel_or_func.arity[0], UninterpretedSort) assert isinstance(rel_or_func.arity[1], UninterpretedSort) assert rel_or_func.arity[0].decl == s assert rel_or_func.arity[1].decl == index_sort entries: Dict[str, LogEntry] = {} for tup, b in state.rel_interp[index_used]: if not b: continue log, index = tup if log == elt and index not in entries: entries[index] = LogEntry(index, values=default_values()) value_sorts: List[SortDecl] = [] for idx, name in enumerate(args[2:]): if is_relation(name): val_rel = get_relation(name) assert_valid_rel_or_func(val_rel) value_sorts.append(syntax.get_decl_from_sort(val_rel.arity[2])) for tup, b in state.rel_interp[val_rel]: if not b: continue log, index, value = tup if log == elt: if index not in entries: entries[index] = LogEntry(index, default_values()) entries[index].values[idx].append(value) else: val_func = get_function(name) assert_valid_rel_or_func(val_func) value_sorts.append(syntax.get_decl_from_sort(val_func.sort)) for tup, res in state.func_interp[val_func]: log, index = tup if log == elt: if index not in entries: entries[index] = LogEntry(index, default_values()) entries[index].values[idx].append(res) # The printed value of an index is consistent with index_le, so the log # should be ordered in the same way. sorted_entries = sorted(entries.values(), key=lambda e: get_ordinal(state, index_le, e.index)) # A log is well-formed if it is empty or # all entries have a single element for each value and the set of indexes # has no gaps starting from zero. well_formed = ((not sorted_entries) or (all(all(len(x) == 1 for x in e.values) for e in sorted_entries) and get_ordinal(state, index_le, sorted_entries[-1].index) == len(sorted_entries) - 1)) def value_to_str(vs: List[str], sort: SortDecl) -> str: return '%s |-> %s' % ( sort.name, logic.print_element(state, sort, vs[0]) if len(vs) == 1 else '[%s]' % (', '.join(logic.print_element(state, sort, v) for v in vs))) def entry_to_str(e: LogEntry, wf: bool) -> str: entry_strs = [value_to_str(e.values[idx], sort) for idx, sort in enumerate(value_sorts)] if not wf: entry_strs.insert(0, 'index |-> %s' % logic.print_element(state, index_sort, e.index)) return '[%s]' % (', '.join(entry_strs)) return '<<%s>>' % (', '.join(entry_to_str(entry, well_formed) for entry in sorted_entries))
def arbitrary_interp_f(f: FunctionDecl) -> FunctionInterp: doms = [get_univ(syntax.get_decl_from_sort(s)) for s in f.arity] image = get_univ(syntax.get_decl_from_sort(f.sort))[0] return dict.fromkeys(product(*doms), image)
def arbitrary_interp_r(r: RelationDecl) -> RelationInterp: doms = [get_univ(syntax.get_decl_from_sort(s)) for s in r.arity] return dict.fromkeys(product(*doms), False)
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 go(expr: Expr, index: Optional[int]) -> Union[Element, bool]: assert index is None or 0 <= index < self.num_states def get_rel(d: RelationDecl) -> RelationInterp: if d.mutable: assert index is not None return self.rel_interps[index][d] else: return self.immut_rel_interps[d] def get_const(d: ConstantDecl) -> Element: if d.mutable: assert index is not None return self.const_interps[index][d] else: return self.immut_const_interps[d] def get_func(d: FunctionDecl) -> FunctionInterp: if d.mutable: assert index is not None return self.func_interps[index][d] else: return self.immut_func_interps[d] # TODO: rewrite to use the new resolved expr without traversing with scope scope: syntax.Scope[Union[Element, bool]] = cast( syntax.Scope[Union[Element, bool]], syntax.the_program.scope) if isinstance(expr, syntax.Bool): return expr.val elif isinstance(expr, syntax.UnaryExpr): if expr.op == 'NEW': assert index is not None with scope.next_state_index(): return go(expr.arg, index=index + 1) elif expr.op == 'NOT': return not go(expr.arg, index) else: assert False, f'eval unknown operation {expr.op}' elif isinstance(expr, syntax.BinaryExpr): if expr.op == 'IMPLIES': return not go(expr.arg1, index) or go(expr.arg2, index) elif expr.op in ['IFF', 'EQUAL']: return go(expr.arg1, index) == go(expr.arg2, index) elif expr.op == 'NOTEQ': return go(expr.arg1, index) != go(expr.arg2, index) else: assert False, expr elif isinstance(expr, syntax.NaryExpr): assert expr.op in ['AND', 'OR', 'DISTINCT'] if expr.op in ['AND', 'OR']: p = all if expr.op == 'AND' else any return p(go(arg, index) for arg in expr.args) else: assert expr.op == 'DISTINCT' return len(set(go(arg, index) for arg in expr.args)) == len(expr.args) elif isinstance(expr, syntax.AppExpr): args = tuple( cast(Element, go(arg, index)) for arg in expr.args) assert all(isinstance(a, Element) for a in args) d = scope.get(expr.callee) if isinstance(d, RelationDecl): return get_rel(d)[args] elif isinstance(d, FunctionDecl): return get_func(d)[args] else: assert False, f'{d}\n{expr}' elif isinstance(expr, syntax.QuantifierExpr): assert expr.quant in ['FORALL', 'EXISTS'] p = all if expr.quant == 'FORALL' else any doms = [ self.univs[syntax.get_decl_from_sort(sv.sort)] for sv in expr.binder.vs ] def one(q: syntax.QuantifierExpr, tup: Tuple[str, ...]) -> bool: with scope.in_scope(q.binder, list(tup)): ans = go(q.body, index) assert isinstance(ans, bool) return ans return p(one(expr, t) for t in product(*doms)) elif isinstance(expr, syntax.Id): a = scope.get(expr.name) # definitions are not supported assert (not isinstance(a, syntax.DefinitionDecl) and not isinstance(a, syntax.FunctionDecl) and a is not None) if isinstance(a, syntax.RelationDecl): return get_rel(a)[()] elif isinstance(a, syntax.ConstantDecl): return get_const(a) elif isinstance(a, tuple): # bound variable introduced to scope (bound_elem, ) = a return bound_elem else: assert isinstance(a, str) or isinstance(a, bool) return a elif isinstance(expr, syntax.IfThenElse): branch = go(expr.branch, index) assert isinstance(branch, bool) return go(expr.then, index) if branch else go(expr.els, index) elif isinstance(expr, syntax.Let): val = go(expr.val, index) with scope.in_scope(expr.binder, [val]): return go(expr.body, index) else: assert False, expr
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))