def replace_quants(self, ex, domain): """ Apply the closed domain assumption to the expression - Domain = union([e.free()|e.constants() for e in all_expressions]) - translate "exists x.P" to "(z=d1 | z=d2 | ... ) & P.replace(x,z)" OR "P.replace(x, d1) | P.replace(x, d2) | ..." - translate "all x.P" to "P.replace(x, d1) & P.replace(x, d2) & ..." :param ex: ``Expression`` :param domain: set of {Variable}s :return: ``Expression`` """ if isinstance(ex, AllExpression): conjuncts = [ ex.term.replace(ex.variable, VariableExpression(d)) for d in domain ] conjuncts = [self.replace_quants(c, domain) for c in conjuncts] return reduce(lambda x, y: x & y, conjuncts) elif isinstance(ex, BooleanExpression): return ex.__class__( self.replace_quants(ex.first, domain), self.replace_quants(ex.second, domain), ) elif isinstance(ex, NegatedExpression): return -self.replace_quants(ex.term, domain) elif isinstance(ex, ExistsExpression): disjuncts = [ ex.term.replace(ex.variable, VariableExpression(d)) for d in domain ] disjuncts = [self.replace_quants(d, domain) for d in disjuncts] return reduce(lambda x, y: x | y, disjuncts) else: return ex
def assumptions(self): """ - Domain = union([e.free()|e.constants() for e in all_expressions]) - if "d1 = d2" cannot be proven from the premises, then add "d1 != d2" """ assumptions = self._command.assumptions() domain = list(get_domain(self._command.goal(), assumptions)) # build a dictionary of obvious equalities eq_sets = SetHolder() for a in assumptions: if isinstance(a, EqualityExpression): av = a.first.variable bv = a.second.variable # put 'a' and 'b' in the same set eq_sets[av].add(bv) new_assumptions = [] for i, a in enumerate(domain): for b in domain[i + 1:]: # if a and b are not already in the same equality set if b not in eq_sets[a]: newEqEx = EqualityExpression(VariableExpression(a), VariableExpression(b)) if Prover9().prove(newEqEx, assumptions): # we can prove that the names are the same entity. # remember that they are equal so we don't re-check. eq_sets[a].add(b) else: # we can't prove it, so assume unique names new_assumptions.append(-newEqEx) return assumptions + new_assumptions
def _attempt_proof_all(self, current, context, agenda, accessible_vars, atoms, debug): try: current._used_vars except AttributeError: current._used_vars = set() #if there are accessible_vars on the path if accessible_vars: # get the set of bound variables that have not be used by this AllExpression bv_available = accessible_vars - current._used_vars if bv_available: variable_to_use = list(bv_available)[0] debug.line('--> Using \'%s\'' % variable_to_use, 2) current._used_vars |= set([variable_to_use]) agenda.put(current.term.replace(current.variable, variable_to_use), context) agenda[Categories.ALL].add((current,context)) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) else: #no more available variables to substitute debug.line('--> Variables Exhausted', 2) current._exhausted = True agenda[Categories.ALL].add((current,context)) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) else: new_unique_variable = VariableExpression(unique_variable()) debug.line('--> Using \'%s\'' % new_unique_variable, 2) current._used_vars |= set([new_unique_variable]) agenda.put(current.term.replace(current.variable, new_unique_variable), context) agenda[Categories.ALL].add((current,context)) agenda.mark_alls_fresh() return self._attempt_proof(agenda, accessible_vars|set([new_unique_variable]), atoms, debug+1)
def _attempt_proof_some(self, current, context, agenda, accessible_vars, atoms, debug): new_unique_variable = VariableExpression(unique_variable()) agenda.put(current.term.replace(current.variable, new_unique_variable), context) agenda.mark_alls_fresh() return self._attempt_proof( agenda, accessible_vars | set([new_unique_variable]), atoms, debug + 1)
def _make_antecedent(self, predicate, signature): """ Return an application expression with 'predicate' as the predicate and 'signature' as the list of arguments. """ antecedent = predicate for v in signature: antecedent = antecedent(VariableExpression(v)) return antecedent
def clausify(expression): """ Skolemize, clausify, and standardize the variables apart. """ clause_list = [] for clause in _clausify(skolemize(expression)): for free in clause.free(): if is_indvar(free.name): newvar = VariableExpression(unique_variable()) clause = clause.replace(free, newvar) clause_list.append(clause) return clause_list
def find_answers(self, verbose=False): self.prove(verbose) answers = set() answer_ex = VariableExpression(Variable(ResolutionProver.ANSWER_KEY)) for clause in self._clauses: for term in clause: if isinstance(term, ApplicationExpression) and\ term.function == answer_ex and\ not isinstance(term.argument, IndividualVariableExpression): answers.add(term.argument) return answers
def _attempt_proof_app(self, current, context, agenda, accessible_vars, atoms, debug): f, args = current.uncurry() for i, arg in enumerate(args): if not TableauProver.is_atom(arg): ctx = f nv = Variable('X%s' % _counter.get()) for j,a in enumerate(args): ctx = (ctx(VariableExpression(nv)) if i == j else ctx(a)) if context: ctx = context(ctx).simplify() ctx = LambdaExpression(nv, ctx) agenda.put(arg, ctx) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) raise Exception('If this method is called, there must be a non-atomic argument')
def assumptions(self): assumptions = self._command.assumptions() predicates = self._make_predicate_dict(assumptions) new_assumptions = [] for p in predicates: predHolder = predicates[p] new_sig = self._make_unique_signature(predHolder) new_sig_exs = [VariableExpression(v) for v in new_sig] disjuncts = [] # Turn the signatures into disjuncts for sig in predHolder.signatures: equality_exs = [] for v1, v2 in zip(new_sig_exs, sig): equality_exs.append(EqualityExpression(v1, v2)) disjuncts.append(reduce(lambda x, y: x & y, equality_exs)) # Turn the properties into disjuncts for prop in predHolder.properties: # replace variables from the signature with new sig variables bindings = {} for v1, v2 in zip(new_sig_exs, prop[0]): bindings[v2] = v1 disjuncts.append(prop[1].substitute_bindings(bindings)) # make the assumption if disjuncts: # disjuncts exist, so make an implication antecedent = self._make_antecedent(p, new_sig) consequent = reduce(lambda x, y: x | y, disjuncts) accum = ImpExpression(antecedent, consequent) else: # nothing has property 'p' accum = NegatedExpression(self._make_antecedent(p, new_sig)) # quantify the implication for new_sig_var in new_sig[::-1]: accum = AllExpression(new_sig_var, accum) new_assumptions.append(accum) return assumptions + new_assumptions
def __setitem__(self, variable, binding): """ A binding is consistent with the dict if its variable is not already bound, OR if its variable is already bound to its argument. :param variable: ``Variable`` The variable to bind :param binding: ``Expression`` The atomic to which 'variable' should be bound :raise BindingException: If the variable cannot be bound in this dictionary """ assert isinstance(variable, Variable) assert isinstance(binding, Expression) try: existing = self[variable] except KeyError: existing = None if not existing or binding == existing: self.d[variable] = binding elif isinstance(binding, IndividualVariableExpression): # Since variable is already bound, try to bind binding to variable try: existing = self[binding.variable] except KeyError: existing = None binding2 = VariableExpression(variable) if not existing or binding2 == existing: self.d[binding.variable] = binding2 else: raise BindingException( "Variable %s already bound to another " "value" % (variable) ) else: raise BindingException( "Variable %s already bound to another " "value" % (variable) )
def make_VariableExpression(self, name): return VariableExpression(name)
def skolemize(expression, univ_scope=None, used_variables=None): """ Skolemize the expression and convert to conjunctive normal form (CNF) """ if univ_scope is None: univ_scope = set() if used_variables is None: used_variables = set() if isinstance(expression, AllExpression): term = skolemize(expression.term, univ_scope | set([expression.variable]), used_variables | set([expression.variable])) return term.replace( expression.variable, VariableExpression(unique_variable(ignore=used_variables))) elif isinstance(expression, AndExpression): return skolemize(expression.first, univ_scope, used_variables) &\ skolemize(expression.second, univ_scope, used_variables) elif isinstance(expression, OrExpression): return to_cnf(skolemize(expression.first, univ_scope, used_variables), skolemize(expression.second, univ_scope, used_variables)) elif isinstance(expression, ImpExpression): return to_cnf(skolemize(-expression.first, univ_scope, used_variables), skolemize(expression.second, univ_scope, used_variables)) elif isinstance(expression, IffExpression): return to_cnf(skolemize(-expression.first, univ_scope, used_variables), skolemize(expression.second, univ_scope, used_variables)) &\ to_cnf(skolemize(expression.first, univ_scope, used_variables), skolemize(-expression.second, univ_scope, used_variables)) elif isinstance(expression, EqualityExpression): return expression elif isinstance(expression, NegatedExpression): negated = expression.term if isinstance(negated, AllExpression): term = skolemize(-negated.term, univ_scope, used_variables | set([negated.variable])) if univ_scope: return term.replace(negated.variable, skolem_function(univ_scope)) else: skolem_constant = VariableExpression( unique_variable(ignore=used_variables)) return term.replace(negated.variable, skolem_constant) elif isinstance(negated, AndExpression): return to_cnf( skolemize(-negated.first, univ_scope, used_variables), skolemize(-negated.second, univ_scope, used_variables)) elif isinstance(negated, OrExpression): return skolemize(-negated.first, univ_scope, used_variables) &\ skolemize(-negated.second, univ_scope, used_variables) elif isinstance(negated, ImpExpression): return skolemize(negated.first, univ_scope, used_variables) &\ skolemize(-negated.second, univ_scope, used_variables) elif isinstance(negated, IffExpression): return to_cnf(skolemize(-negated.first, univ_scope, used_variables), skolemize(-negated.second, univ_scope, used_variables)) &\ to_cnf(skolemize(negated.first, univ_scope, used_variables), skolemize(negated.second, univ_scope, used_variables)) elif isinstance(negated, EqualityExpression): return expression elif isinstance(negated, NegatedExpression): return skolemize(negated.term, univ_scope, used_variables) elif isinstance(negated, ExistsExpression): term = skolemize(-negated.term, univ_scope | set([negated.variable]), used_variables | set([negated.variable])) return term.replace( negated.variable, VariableExpression(unique_variable(ignore=used_variables))) elif isinstance(negated, ApplicationExpression): return expression else: raise Exception('\'%s\' cannot be skolemized' % expression) elif isinstance(expression, ExistsExpression): term = skolemize(expression.term, univ_scope, used_variables | set([expression.variable])) if univ_scope: return term.replace(expression.variable, skolem_function(univ_scope)) else: skolem_constant = VariableExpression( unique_variable(ignore=used_variables)) return term.replace(expression.variable, skolem_constant) elif isinstance(expression, ApplicationExpression): return expression else: raise Exception('\'%s\' cannot be skolemized' % expression)
def _attempt_proof_n_app(self, current, context, agenda, accessible_vars, atoms, debug): f, args = current.term.uncurry() for i, arg in enumerate(args): if not TableauProver.is_atom(arg): ctx = f nv = Variable('X%s' % _counter.get()) for j,a in enumerate(args): if i==j: ctx = (ctx(VariableExpression(nv)) if i == j else ctx(a)) if context: #combine new context with existing ctx = context(ctx).simplify() ctx = LambdaExpression(nv, -ctx) agenda.put(-arg, ctx) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) raise Exception('If this method is called, there must be a non-atomic argument') def _attempt_proof_n_eq(self, current, context, agenda, accessible_vars, atoms, debug): ########################################################################### # Since 'current' is of type '~(a=b)', the path is closed if 'a' == 'b' ########################################################################### if current.term.first == current.term.second: debug.line('CLOSED', 1) return True agenda[Categories.N_EQ].add((current,context)) current._exhausted = True return self._attempt_proof(agenda, accessible_vars|set([current.term.first, current.term.second]), atoms, debug+1) def _attempt_proof_d_neg(self, current, context, agenda, accessible_vars, atoms, debug): agenda.put(current.term.term, context) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) def _attempt_proof_n_all(self, current, context, agenda, accessible_vars, atoms, debug): agenda[Categories.EXISTS].add((ExistsExpression(current.term.variable, -current.term.term), context)) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) def _attempt_proof_n_some(self, current, context, agenda, accessible_vars, atoms, debug): agenda[Categories.ALL].add((AllExpression(current.term.variable, -current.term.term), context)) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) def _attempt_proof_and(self, current, context, agenda, accessible_vars, atoms, debug): agenda.put(current.first, context) agenda.put(current.second, context) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) def _attempt_proof_n_or(self, current, context, agenda, accessible_vars, atoms, debug): agenda.put(-current.term.first, context) agenda.put(-current.term.second, context) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) def _attempt_proof_n_imp(self, current, context, agenda, accessible_vars, atoms, debug): agenda.put(current.term.first, context) agenda.put(-current.term.second, context) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) def _attempt_proof_or(self, current, context, agenda, accessible_vars, atoms, debug): new_agenda = agenda.clone() agenda.put(current.first, context) new_agenda.put(current.second, context) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) and \ self._attempt_proof(new_agenda, accessible_vars, atoms, debug+1) def _attempt_proof_imp(self, current, context, agenda, accessible_vars, atoms, debug): new_agenda = agenda.clone() agenda.put(-current.first, context) new_agenda.put(current.second, context) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) and \ self._attempt_proof(new_agenda, accessible_vars, atoms, debug+1) def _attempt_proof_n_and(self, current, context, agenda, accessible_vars, atoms, debug): new_agenda = agenda.clone() agenda.put(-current.term.first, context) new_agenda.put(-current.term.second, context) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) and \ self._attempt_proof(new_agenda, accessible_vars, atoms, debug+1) def _attempt_proof_iff(self, current, context, agenda, accessible_vars, atoms, debug): new_agenda = agenda.clone() agenda.put(current.first, context) agenda.put(current.second, context) new_agenda.put(-current.first, context) new_agenda.put(-current.second, context) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) and \ self._attempt_proof(new_agenda, accessible_vars, atoms, debug+1) def _attempt_proof_n_iff(self, current, context, agenda, accessible_vars, atoms, debug): new_agenda = agenda.clone() agenda.put(current.term.first, context) agenda.put(-current.term.second, context) new_agenda.put(-current.term.first, context) new_agenda.put(current.term.second, context) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) and \ self._attempt_proof(new_agenda, accessible_vars, atoms, debug+1) def _attempt_proof_eq(self, current, context, agenda, accessible_vars, atoms, debug): ######################################################################### # Since 'current' is of the form '(a = b)', replace ALL free instances # of 'a' with 'b' ######################################################################### agenda.put_atoms(atoms) agenda.replace_all(current.first, current.second) accessible_vars.discard(current.first) agenda.mark_neqs_fresh(); return self._attempt_proof(agenda, accessible_vars, set(), debug+1) def _attempt_proof_some(self, current, context, agenda, accessible_vars, atoms, debug): new_unique_variable = VariableExpression(unique_variable()) agenda.put(current.term.replace(current.variable, new_unique_variable), context) agenda.mark_alls_fresh() return self._attempt_proof(agenda, accessible_vars|set([new_unique_variable]), atoms, debug+1) def _attempt_proof_all(self, current, context, agenda, accessible_vars, atoms, debug): try: current._used_vars except AttributeError: current._used_vars = set() #if there are accessible_vars on the path if accessible_vars: # get the set of bound variables that have not be used by this AllExpression bv_available = accessible_vars - current._used_vars if bv_available: variable_to_use = list(bv_available)[0] debug.line('--> Using \'%s\'' % variable_to_use, 2) current._used_vars |= set([variable_to_use]) agenda.put(current.term.replace(current.variable, variable_to_use), context) agenda[Categories.ALL].add((current,context)) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) else: #no more available variables to substitute debug.line('--> Variables Exhausted', 2) current._exhausted = True agenda[Categories.ALL].add((current,context)) return self._attempt_proof(agenda, accessible_vars, atoms, debug+1) else: new_unique_variable = VariableExpression(unique_variable()) debug.line('--> Using \'%s\'' % new_unique_variable, 2) current._used_vars |= set([new_unique_variable]) agenda.put(current.term.replace(current.variable, new_unique_variable), context) agenda[Categories.ALL].add((current,context)) agenda.mark_alls_fresh() return self._attempt_proof(agenda, accessible_vars|set([new_unique_variable]), atoms, debug+1) @staticmethod def is_atom(e): if isinstance(e, NegatedExpression): e = e.term if isinstance(e, ApplicationExpression): for arg in e.args: if not TableauProver.is_atom(arg): return False return True elif isinstance(e, AbstractVariableExpression) or \ isinstance(e, LambdaExpression): return True else: return False