def show_used_relations(self, clauses, both=False): self.current_concept_graph.clear_edges() rels = self.current_concept_graph.g.relations used = set( il.normalize_symbol(s) for s in lu.used_constants(clauses.to_formula())) for rel in rels: fmla = rel.formula if any(c in used and not c.name.startswith('@') for c in lu.used_constants(fmla)): self.current_concept_graph.show_relation(rel, '+', update=False) if both and not il.is_enumerated(fmla): self.current_concept_graph.show_relation(rel, '-', update=False) need_update_relations = False for app in ilu.apps_clauses(clauses): if len(app.args) == 3 and il.is_numeral(app.args[0]): fmla = app.rep(app.args[0], il.Variable('X', app.args[1].sort), il.Variable('Y', app.args[2].sort)) concept = self.current_concept_graph.g.formula_to_concept(fmla) self.current_concept_graph.g.new_relation(concept) need_update_relations = True self.current_concept_graph.show_relation(concept, '+', update=False) if both: self.current_concept_graph.show_relation(concept, '-', update=False) if need_update_relations: self.current_concept_graph.update_relations() self.current_concept_graph.update()
def show_used_relations(self,clauses,both=False): rels = self.current_concept_graph.g.relations used = lu.used_constants(clauses.to_formula()) for rel in rels: if any(c in used and not c.name.startswith('@') for c in lu.used_constants(rel.formula)): self.current_concept_graph.show_relation(rel,'+',update=False) if both: self.current_concept_graph.show_relation(rel,'-',update=False) self.current_concept_graph.update()
def show_used_relations(self, clauses, both=False): rels = self.current_concept_graph.g.relations used = lu.used_constants(clauses.to_formula()) for rel in rels: if any(c in used and not c.name.startswith('@') for c in lu.used_constants(rel.formula)): self.current_concept_graph.show_relation(rel, '+', update=False) if both: self.current_concept_graph.show_relation(rel, '-', update=False) self.current_concept_graph.update()
def sort_infer(term): res = concretize_sorts(term) for x in chain(lu.used_variables(res),lu.used_constants(res)): if lg.contains_topsort(x.sort) or lg.is_polymorphic(x.sort): raise IvyError(None,"cannot infer sort of {}".format(x)) # print "sort_infer: res = {!r}".format(res) return res
def check_concretely_sorted(term,no_error=False,unsorted_var_names=()): for x in chain(lu.used_variables(term),lu.used_constants(term)): if lg.contains_topsort(x.sort) or lg.is_polymorphic(x.sort): if x.name not in unsorted_var_names: if no_error: raise lg.SortError raise IvyError(None,"cannot infer sort of {} in {}".format(x,repr(term)))
def sort_infer(term): res = concretize_sorts(term) for x in chain(lu.used_variables(res),lu.used_constants(res)): if lg.contains_topsort(x.sort) or lg.is_polymorphic(x.sort): raise IvyError(None,"cannot infer sort of {} in {}".format(x,term)) # print "sort_infer: res = {!r}".format(res) return res
def standardize_action(self, f, nexvars, name): nexSet = set() for n in nexvars: nexSet.add(n) self.updated.add(n) cons = lgu.used_constants(f) subs = dict() evars = [] for c in cons: if c in self.nex: if c not in nexSet: subs[c] = self.nex2pre[c] # elif False and (c not in self.pre): elif c not in self.pre: if (not c.sort.dom) and (c.sort != lg.Boolean): vname = "Vtmp" + c.name # vname = vname.replace(":", "") qv = lg.Var(vname, c.sort) subs[c] = qv evars.append(qv) action = f if len(subs) != 0: # for k, v in subs.iteritems(): # print("\treplacing %s -> %s in %s" % (k, v, name)) action = lgu.substitute(f, subs) if len(evars) != 0: action = lg.Exists(evars, action) return action
def _get_witnesses(self, concept_name): # TODO: maybe this function should be in ConceptDomain? or Concept? """ Return a list of constant that are witnesses for the given unary constant, or [] if none are found A witness is a constant c s.t. concept(x) implies x=c. Note that this does not necessarily mean that concept(c) holds. """ concept = self.domain.concepts[concept_name] assert concept.arity == 1 sort = concept.variables[0].sort assert sort != TopSort() constants = used_constants(concept.formula) x = Const(self._fresh_const_name(constants), sort) f = concept(x) def is_witness(c): try: return z3_implies(f, Eq(x, c)) except SortError: return False return [c for c in constants if is_witness(c)]
def _get_witnesses(self, concept_name): # TODO: maybe this function should be in ConceptDomain? or Concept? """ Return a list of constant that are witnesses for the given unary constant, or [] if none are found A witness is a constant c s.t. concept(x) implies x=c. Note that this does not necessarily mean that concept(c) holds. """ concept = self.domain.concepts[concept_name] assert concept.arity == 1 sort = concept.variables[0].sort assert sort != TopSort() if sort.name == 'unit': return [Const('0',sort)] constants = used_constants(concept.formula) x = Const(self._fresh_const_name(constants), sort) f = concept(x) def is_witness(c): try: return z3_implies(f, Eq(x, c)) except SortError: return False return [c for c in constants if is_witness(c)]
def add_new_constants(self, f): cons = lgu.used_constants(f) for c in cons: if c not in self.allvars: self.add_constant(c, False) self.vars.add(c) self.allvars.add(c)
def _fresh_const_name(self, extra=frozenset()): contents = ([self._to_formula()] + [ c.formula for n, c in self.domain.concepts.iteritems() if isinstance(c, Concept) ]) used = frozenset(c.name for c in (used_constants(*contents) | extra)) return next(name for name in constant_name_generator() if name not in used)
def _fresh_const_name(self, extra=frozenset()): contents = ([self._to_formula()] + [c.formula for n,c in self.domain.concepts.iteritems() if isinstance(c,Concept)]) used = frozenset(c.name for c in ( used_constants(*contents) | extra )) return next(name for name in constant_name_generator() if name not in used)
def get_diagram_concept_domain(sig, diagram): """ sig is an ivy_logic.Sig object diagram is a formula """ concepts = OrderedDict() concepts['nodes'] = [] concepts['node_labels'] = [] concepts['edges'] = [] # add equality concept X = Var('X', TopSort()) Y = Var('Y', TopSort()) concepts['='] = Concept([X, Y], Eq(X, Y)) # add concepts from relations and constants in the signature and # in the diagram if sig is not None: sig_symbols = frozenset(sig.symbols.values()) else: sig_symbols = frozenset() for c in sorted(sig_symbols | used_constants(diagram)): assert type(c) is Const if first_order_sort(c.sort): # first order constant, add unary equality concept X = Var('X', c.sort) name = '{}:{}'.format(c.name, c.sort) concepts[name] = Concept([X], Eq(X,c)) concepts['nodes'].append(name) elif type(c.sort) is FunctionSort and c.sort.arity == 1: # add unary concept and label X = Var('X', c.sort.domain[0]) name = '{}'.format(c.name) concepts[name] = Concept([X], c(X)) elif type(c.sort) is FunctionSort and c.sort.arity == 2: # add binary concept and edge X = Var('X', c.sort.domain[0]) Y = Var('Y', c.sort.domain[1]) name = '{}'.format(c.name) concepts[name] = Concept([X, Y], c(X, Y)) elif type(c.sort) is FunctionSort and c.sort.arity == 3: # add ternary concept X = Var('X', c.sort.domain[0]) Y = Var('Y', c.sort.domain[1]) Z = Var('Z', c.sort.domain[2]) name = '{}'.format(c.name) concepts[name] = Concept([X, Y, Z], c(X, Y, Z)) else: # skip other symbols pass return ConceptDomain(concepts, get_standard_combiners(), get_standard_combinations())
def check_concretely_sorted(term, no_error=False, unsorted_var_names=()): for x in chain(lu.used_variables(term), lu.used_constants(term)): if lg.contains_topsort(x.sort) or lg.is_polymorphic(x.sort): if x.name not in unsorted_var_names: if no_error: raise lg.SortError raise IvyError( None, "cannot infer sort of {} in {}".format(x, repr(term)))
def show_used_relations(self,clauses,both=False): self.current_concept_graph.clear_edges() rels = self.current_concept_graph.g.relations used = set(il.normalize_symbol(s) for s in lu.used_constants(clauses.to_formula())) for rel in rels: if any(c in used and not c.name.startswith('@') for c in lu.used_constants(rel.formula)): self.current_concept_graph.show_relation(rel,'+',update=False) if both: self.current_concept_graph.show_relation(rel,'-',update=False) need_update_relations = False for app in ilu.apps_clauses(clauses): if len(app.args) == 3 and il.is_numeral(app.args[0]): fmla = app.rep(app.args[0],il.Variable('X',app.args[1].sort),il.Variable('Y',app.args[2].sort)) concept = self.current_concept_graph.g.formula_to_concept(fmla) self.current_concept_graph.g.new_relation(concept) need_update_relations = True self.current_concept_graph.show_relation(concept,'+',update=False) if both: self.current_concept_graph.show_relation(concept,'-',update=False) if need_update_relations: self.current_concept_graph.update_relations() self.current_concept_graph.update()
def is_in_logic(term,logic,unstrat = False): global reason_text assert logic in logics if logic == "epr": # ok = (is_prenex_universal(term) # if lu.free_variables(term) else is_ea(term)) # if not ok: # reason_text = "of quantifier alternation" # return False try: check_essentially_uninterpreted(term) except NotEssentiallyUninterpreted: reason_text = "a variable occurs under an interpreted function symbol" return False cs = lu.used_constants(term) for s in cs: if s.name in sig.interp: reason_text = "'{}' is iterpreted".format(s) return False if unstrat: reason_text = "functions are not stratified" return False if not is_segregated(term): reason_text = "formula is unsegregated" return False return True elif logic == "qf": reason_text = "a formula contains a quantifier" return is_qf(term) elif logic == "fo": cs = lu.used_constants(term) for s in cs: if s.name in sig.interp: reason_text = "'{}' is iterpreted".format(s) return False return True
def is_in_logic(term, logic, unstrat=False): global reason_text assert logic in logics if logic == "epr": # ok = (is_prenex_universal(term) # if lu.free_variables(term) else is_ea(term)) # if not ok: # reason_text = "of quantifier alternation" # return False try: check_essentially_uninterpreted(term) except NotEssentiallyUninterpreted: reason_text = "a variable occurs under an interpreted function symbol" return False cs = lu.used_constants(term) for s in cs: if s.name in sig.interp: reason_text = "'{}' is iterpreted".format(s) return False if unstrat: reason_text = "functions are not stratified" return False if not is_segregated(term): reason_text = "formula is unsegregated" return False return True elif logic == "qf": reason_text = "a formula contains a quantifier" return is_qf(term) elif logic == "fo": cs = lu.used_constants(term) for s in cs: if s.name in sig.interp: reason_text = "'{}' is iterpreted".format(s) return False return True
def minimize_conjecture(self, button=None, bound=None): import ivy_transrel import ivy_solver from proof import ProofGoal from ivy_logic_utils import Clauses, and_clauses, dual_clauses, used_symbols_clauses, negate from ivy_solver import unsat_core from logic_util import free_variables, substitute if self.bmc_conjecture(bound=bound): # found a BMC counter-example return with self.ui_parent.run_context(): step_action = im.module.actions['ext'] n_steps = self.current_bound ag = self.parent.new_ag() with ag.context as ac: post = ac.new_state(ag.init_cond) if 'initialize' in im.module.actions: init_action = im.module.actions['initialize'] post = ag.execute(init_action, None, None, 'initialize') for n in range(n_steps): post = ag.execute(step_action, None, None, 'ext') axioms = im.module.background_theory() post_clauses = and_clauses(post.clauses, axioms) used_names = ( frozenset(x.name for x in il.sig.symbols.values()) | frozenset(x.name for x in used_symbols_clauses(post_clauses)) ) facts = self.get_active_facts() assert not any( c.is_skolem() and c.name in used_names for c in lu.used_constants(*facts) ) core = unsat_core(Clauses(facts), post_clauses) if core is None: core = Clauses([]) ## can happen if we are proving true # assert core is not None, "bmc_conjecture returned False but unsat core is None" core_formulas = frozenset(core.fmlas) self.set_facts([fact for fact in facts if fact in core_formulas]) self.highlight_selected_facts() self.ui_parent.text_dialog("BMC found the following possible conjecture:", str(self.get_selected_conjecture()))
def minimize_conjecture(self, button=None, bound=None): import ivy_transrel import ivy_solver from proof import ProofGoal from ivy_logic_utils import Clauses, and_clauses, dual_clauses, used_symbols_clauses, negate from ivy_solver import unsat_core from logic_util import free_variables, substitute if self.bmc_conjecture(bound=bound): # found a BMC counter-example return with self.ui_parent.run_context(): step_action = im.module.actions['ext'] n_steps = self.current_bound ag = self.parent.new_ag() with ag.context as ac: post = ac.new_state(ag.init_cond) if 'initialize' in im.module.actions: init_action = im.module.actions['initialize'] post = ag.execute(init_action, None, None, 'initialize') for n in range(n_steps): post = ag.execute(step_action, None, None, 'ext') axioms = im.module.background_theory() post_clauses = and_clauses(post.clauses, axioms) used_names = (frozenset(x.name for x in il.sig.symbols.values()) | frozenset( x.name for x in used_symbols_clauses(post_clauses))) facts = self.get_active_facts() assert not any(c.is_skolem() and c.name in used_names for c in lu.used_constants(*facts)) core = unsat_core(Clauses(facts), post_clauses) if core is None: core = Clauses([]) ## can happen if we are proving true # assert core is not None, "bmc_conjecture returned False but unsat core is None" core_formulas = frozenset(core.fmlas) self.set_facts([fact for fact in facts if fact in core_formulas]) self.highlight_selected_facts() self.ui_parent.text_dialog( "BMC found the following possible conjecture:", str(self.get_selected_conjecture()))
def get_selected_conjecture(self): """ Return a positive universal conjecture based on the selected facts. The result is a Clauses object """ from logic_util import used_constants, free_variables, substitute from ivy_logic_utils import negate, Clauses, simplify_clauses facts = self.get_active_facts() assert len(free_variables( * facts)) == 0, "conjecture would contain existential quantifiers..." sig_symbols = frozenset(il.sig.symbols.values()) facts_consts = used_constants(*facts) subs = {} rn = iu.VariableGenerator() for c in sorted(facts_consts, key=lambda c: c.name): if c.is_numeral() and il.is_uninterpreted_sort(c.sort): # prefix = str(c.sort)[:2].upper() + c.name subs[c] = lg.Var(rn(c.sort.name), c.sort) literals = [negate(substitute(f, subs)) for f in facts] result = Clauses([lg.Or(*literals)]) result = simplify_clauses(result) # now rename again to get a pretty clause, since some # variables have been eliminated by simplify_clauses # assert len(result.fmlas) == 1 # clause = result.fmlas[0] # subs = {} # count = defaultdict(int) # for c in free_variables(clause): # prefix = str(c.sort)[0].upper() # count[prefix] += 1 # subs[c] = lg.Var(prefix + str(count[prefix]), c.sort) # result = Clauses([substitute(clause, subs)]) # change to negation of conjunction rather than disjunction assert len(result.fmlas) == 1 if type(result.fmlas[0]) is lg.Or: result = Clauses( [lg.Not(lg.And(*(negate(lit) for lit in result.fmlas[0])))]) return result
def concretize_terms(terms, sorts=None): """ Concretize the sorts in a list of terms. Free variables must have the same sort in all terms. The list 'sorts' gives a sort upper bound for each term (use TopS to leave the sort unspecified). """ # give sort names to all the free variables and constants: names = free_variables(*terms, by_name=True).union(x.name for x in used_constants(*terms)) env = dict((name, SortVar()) for name in names) pairs = [infer_sorts(term, env) for term in terms] if sorts is not None: for (s, tt), sort in zip(pairs, sorts): unify(s, sort) return [tt() for s, tt in pairs]
def concretize_terms(terms,sorts=None): """ Concretize the sorts in a list of terms. Free variables must have the same sort in all terms. The list 'sorts' gives a sort upper bound for each term (use TopS to leave the sort unspecified). """ # give sort names to all the free variables and constants: names = free_variables(*terms, by_name=True).union( x.name for x in used_constants(*terms) ) env = dict((name, SortVar()) for name in names) pairs = [infer_sorts(term,env) for term in terms] if sorts is not None: for (s,tt),sort in zip(pairs,sorts): unify(s,sort) return [tt() for s,tt in pairs]
def standardize_action(self, f, nexvars, name): nexSet = set() for n in nexvars: nexSet.add(n) self.updated.add(n) cons = lgu.used_constants(f) subs = dict() for c in cons: if c in self.nex: if c not in nexSet: subs[c] = self.nex2pre[c] if len(subs) == 0: return f else: # for k, v in subs.iteritems(): # print("\treplacing %s -> %s in %s" % (k, v, name)) return lgu.substitute(f, subs)
def get_selected_conjecture(self): """ Return a positive universal conjecture based on the selected facts. The result is a Clauses object """ from logic_util import used_constants, free_variables, substitute from ivy_logic_utils import negate, Clauses, simplify_clauses facts = self.get_active_facts() assert len(free_variables(*facts)) == 0, "conjecture would contain existential quantifiers..." sig_symbols = frozenset(il.sig.symbols.values()) facts_consts = used_constants(*facts) subs = {} rn = iu.UniqueRenamer() for c in sorted(facts_consts, key=lambda c: c.name): if c.is_numeral() and il.is_uninterpreted_sort(c.sort): prefix = str(c.sort)[:2].upper() + str(c) subs[c] = lg.Var(rn(prefix), c.sort) literals = [negate(substitute(f, subs)) for f in facts] result = Clauses([lg.Or(*literals)]) result = simplify_clauses(result) # now rename again to get a pretty clause, since some # variables have been eliminated by simplify_clauses # assert len(result.fmlas) == 1 # clause = result.fmlas[0] # subs = {} # count = defaultdict(int) # for c in free_variables(clause): # prefix = str(c.sort)[0].upper() # count[prefix] += 1 # subs[c] = lg.Var(prefix + str(count[prefix]), c.sort) # result = Clauses([substitute(clause, subs)]) # change to negation of conjunction rather than disjunction assert len(result.fmlas) == 1 if type(result.fmlas[0]) is lg.Or: result = Clauses([lg.Not(lg.And(*(negate(lit) for lit in result.fmlas[0])))]) return result
def add_new_constants(self, f, origin=''): cons = lgu.used_constants(f) for c in cons: if c not in self.allvars and c.name not in {'<=', '>=', '>'}: # print >>sys.stderr, c, c.sort, origin if origin in {'prop', 'axiom'}: sym = c psym = sym.prefix('__') nsym = sym self.pre.add(psym) self.nex.add(nsym) self.pre2nex[psym] = nsym self.nex2pre[nsym] = psym self.allvars.add(psym) self.allvars.add(nsym) self.add_constant(sym, True) else: self.add_constant(c, False) self.vars.add(c) self.allvars.add(c)
def is_in_logic(term,logic,unstrat = False): global reason_text assert logic in logics if logic == "epr": # ok = (is_prenex_universal(term) # if lu.free_variables(term) else is_ea(term)) # if not ok: # reason_text = "of quantifier alternation" # return False cs = lu.used_constants(term) for s in cs: if s.name in sig.interp: reason_text = "'{}' is iterpreted".format(s) return False if unstrat: if not is_segregated(term): reason_text = "formula is unsegregated" return False return True elif logic == "qf": return is_qf(term)
def get_structure_concept_domain(state, sig=None): """ state is an ivy_interp.State with a .universe sig is an ivy_logic.Sig object """ concepts = OrderedDict() concepts['nodes'] = [] concepts['node_labels'] = [] # add equality concept X = Var('X', TopSort()) Y = Var('Y', TopSort()) concepts['='] = Concept([X, Y], Eq(X, Y)) # add nodes for universe elements elements = [uc for s in state.universe for uc in state.universe[s]] for uc in sorted(elements): # add unary equality concept X = Var('X', uc.sort) name = uc.name if str(uc.sort) not in name: name += ':{}'.format(uc.sort) concepts[name] = Concept([X], Eq(X,uc)) concepts['nodes'].append(name) # # find which symbols are equal to which universe constant # equals = dict( # (uc, [c for c in symbols # if c != uc and # c.sort == s and # z3_implies(state_formula, Eq(c, uc))]) # for s in state.universe # for uc in state.universe[s] # ) # add concepts for relations and constants state_formula = state.clauses.to_formula() symbols = used_constants(state_formula) if sig is not None: symbols = symbols | frozenset(sig.symbols.values()) symbols = symbols - frozenset(elements) symbols = sorted(symbols) for c in symbols: assert type(c) is Const if first_order_sort(c.sort): # first order constant, add unary equality concept X = Var('X', c.sort) name = '={}'.format(c.name) concepts[name] = Concept([X], Eq(X,c)) elif type(c.sort) is FunctionSort and c.sort.arity == 1: # add unary concept and label X = Var('X', c.sort.domain[0]) name = '{}'.format(c.name) concepts[name] = Concept([X], c(X)) elif type(c.sort) is FunctionSort and c.sort.arity == 2: # add binary concept and edge X = Var('X', c.sort.domain[0]) Y = Var('Y', c.sort.domain[1]) name = '{}'.format(c.name) concepts[name] = Concept([X, Y], c(X, Y)) elif type(c.sort) is FunctionSort and c.sort.arity == 3: # add ternary concept X = Var('X', c.sort.domain[0]) Y = Var('Y', c.sort.domain[1]) Z = Var('Z', c.sort.domain[2]) name = '{}'.format(c.name) concepts[name] = Concept([X, Y, Z], c(X, Y, Z)) else: # skip other symbols pass return ConceptDomain(concepts, get_standard_combiners(), get_standard_combinations())
def _fresh_const_name(self, extra=frozenset()): used = frozenset(c.name for c in ( used_constants(self._to_formula()) | extra )) return next(name for name in constant_name_generator() if name not in used)
def infer_sorts(t, env=None): """ Infer the sort of term t in environment env. env maps symbol names to sort variables. The result is a pair: (s, tt) where s is a sort or sort variable with the sort of t in env, and tt is a closure that, when called, will concretize t according to inferred sort information at its call time. If env is not None, it must contain all the free variables and constants used in t. """ if env is None: names = free_variables(t, by_name=True).union( x.name for x in used_constants(t) ) env = dict((name, SortVar()) for name in names) if type(t) in (Var, Const): if is_polymorphic(t): # each instance can have different sort s = insert_sortvars(t.sort,{}) else: s = env[t.name] unify(s, t.sort) return s, lambda: type(t)(t.name, convert_from_sortvars(s)) elif type(t) is Apply: func_s, func_t = infer_sorts(t.func, env) xys = [infer_sorts(tt, env) for tt in t.terms] terms_s = [x for x, y in xys] terms_t = [y for x, y in xys] sorts = terms_s + [SortVar()] unify(func_s, FunctionSort(*sorts)) return sorts[-1], lambda: Apply(func_t(), *( x() for x in terms_t )) elif type(t) is Eq: s1, t1 = infer_sorts(t.t1, env) s2, t2 = infer_sorts(t.t2, env) unify(s1, s2) return Boolean, lambda: Eq(t1(), t2()) elif type(t) is Ite: s_cond, t_cond = infer_sorts(t.cond, env) s_then, t_then = infer_sorts(t.t_then, env) s_else, t_else = infer_sorts(t.t_else, env) unify(s_cond, Boolean) unify(s_then, s_else) return s_then, lambda: Ite(t_cond(), t_then(), t_else()) elif type(t) in (Not, And, Or, Implies, Iff): xys = [infer_sorts(tt, env) for tt in t] terms_s = [x for x, y in xys] terms_t = [y for x, y in xys] for s in terms_s: unify(s, Boolean) return Boolean, lambda: type(t)(*[ x() for x in terms_t ]) elif type(t) in (ForAll, Exists): # create a copy of the environment and shadow that quantified # variables env = env.copy() env.update((v.name, SortVar()) for v in t.variables) vars_t = [infer_sorts(v, env)[1] for v in t.variables] body_s, body_t = infer_sorts(t.body, env) unify(body_s, Boolean) return Boolean, lambda: type(t)( [x() for x in vars_t], body_t(), ) elif hasattr(t,'clone'): xys = [infer_sorts(tt, env) for tt in t.args] terms_t = [y for x, y in xys] return TopSort(), lambda: t.clone([ x() for x in terms_t ]) else: assert False, type(t)
def infer_sorts(t, env=None): """ Infer the sort of term t in environment env. env maps symbol names to sort variables. The result is a pair: (s, tt) where s is a sort or sort variable with the sort of t in env, and tt is a closure that, when called, will concretize t according to inferred sort information at its call time. If env is not None, it must contain all the free variables and constants used in t. """ if env is None: names = free_variables(t, by_name=True).union(x.name for x in used_constants(t)) env = dict((name, SortVar()) for name in names) if type(t) in (Var, Const): if is_polymorphic(t): # each instance can have different sort s = insert_sortvars(t.sort, {}) else: s = env[t.name] unify(s, t.sort) return s, lambda: type(t)(t.name, convert_from_sortvars(s)) elif type(t) is Apply: func_s, func_t = infer_sorts(t.func, env) xys = [infer_sorts(tt, env) for tt in t.terms] terms_s = [x for x, y in xys] terms_t = [y for x, y in xys] sorts = terms_s + [SortVar()] unify(func_s, FunctionSort(*sorts)) return sorts[-1], lambda: Apply(func_t(), *(x() for x in terms_t)) elif type(t) is Eq: s1, t1 = infer_sorts(t.t1, env) s2, t2 = infer_sorts(t.t2, env) unify(s1, s2) return Boolean, lambda: Eq(t1(), t2()) elif type(t) is Ite: s_cond, t_cond = infer_sorts(t.cond, env) s_then, t_then = infer_sorts(t.t_then, env) s_else, t_else = infer_sorts(t.t_else, env) unify(s_cond, Boolean) unify(s_then, s_else) return s_then, lambda: Ite(t_cond(), t_then(), t_else()) elif type(t) in (Not, And, Or, Implies, Iff): xys = [infer_sorts(tt, env) for tt in t] terms_s = [x for x, y in xys] terms_t = [y for x, y in xys] for s in terms_s: unify(s, Boolean) return Boolean, lambda: type(t)(*[x() for x in terms_t]) elif type(t) in (ForAll, Exists): # create a copy of the environment and shadow that quantified # variables env = env.copy() env.update((v.name, SortVar()) for v in t.variables) vars_t = [infer_sorts(v, env)[1] for v in t.variables] body_s, body_t = infer_sorts(t.body, env) unify(body_s, Boolean) return Boolean, lambda: type(t)( [x() for x in vars_t], body_t(), ) elif hasattr(t, 'clone'): xys = [infer_sorts(tt, env) for tt in t.args] terms_t = [y for x, y in xys] return TopSort(), lambda: t.clone([x() for x in terms_t]) else: assert False, type(t)