def __init__(self, name, *args): assert isinstance(name, str) assert re.match('[a-zA-Z][a-zA-Z_]*$', name),\ 'invalid name: {0}'.format(name) args = list(args) arity = signature.get_arity(name) assert len(args) == signature.get_nargs(arity) for arg in args: assert isinstance(arg, Expression), arg self._name = name self._args = args self._arity = arity self._polish = ' '.join([name] + [arg._polish for arg in args]) self._hash = hash(self._polish) if arity == 'Variable': self._var = self self._vars = set([self]) elif arity == 'NullaryFunction': self._var = Expression(name + '_') self._vars = set() elif arity in [ 'InjectiveFunction', 'BinaryFunction', 'SymmetricFunction', ]: var = re.sub('[ _]+', '_', self.polish).rstrip('_') self._var = Expression(var) self._vars = union(arg.vars for arg in args) else: self._var = None self._vars = union(arg.vars for arg in args)
def vars(self): if self._vars is None: if self._arity == 'Variable': self._vars = set([self]) elif self._arity == 'NullaryFunction': self._vars = set() elif self._arity in signature.FUNCTION_ARITIES: self._vars = union(a.vars for a in self._args) else: self._vars = union(a.vars for a in self._args) self._vars = sortedset(self._vars) return self._vars
def get_inverses(sequent): ''' Given a sequent A |- B, return set of sequents ~A |- ~B, dealing with multiple antecedents and succedents. For example A, B |- C, D yields the set A, ~B |- ~C, ~D ~A, B |- ~C, ~D ''' result = set() neg_succedents = union(try_get_negated(s) for s in sequent.succedents) pos_antecedents = set(sequent.antecedents) for pos_antecedent in pos_antecedents: try: negated = try_get_negated(pos_antecedent) except NotNegatable: negated = set() for neg_antecedent in negated: neg_antecedents = set_without(pos_antecedents, pos_antecedent) neg_antecedents = set_with(neg_antecedents, neg_antecedent) result.add(Sequent(neg_antecedents, neg_succedents)) return result
def terms(self): if self._terms is None: self._terms = union(a.terms for a in self._args) if self.is_term(): self._terms.add(self) self._terms = sortedset(self._terms) return self._terms
def consts(self): if self._consts is None: if self.is_fun() and not self._args: self._consts = sortedset([self]) else: self._consts = sortedset(union(a.consts for a in self._args)) return self._consts
def get_events(seq): events = set() if seq.optional: logger('skipped optional rule {}', seq) return events free_vars = seq.vars for sequent in normalize(seq): for antecedent in sequent.antecedents: if antecedent.name == 'EQUAL': lhs, rhs = antecedent.args assert lhs.is_var() and rhs.is_var(), antecedent # HACK ignore equation antecedents else: events.add(antecedent) # HACK to deal with Equation args succedent = iter(sequent.succedents).next() for arg in succedent.args: if not arg.is_var(): events.add(arg) antecedent_vars = union(a.vars for a in sequent.antecedents) for var in succedent.vars & free_vars - antecedent_vars: compound_count = sum(1 for arg in succedent.args if arg.args) in_count = sum(1 for arg in succedent.args if var in arg.vars) # if the var is in a compound in both succedent.args, # then succedent.args are sufficient events. if in_count < 2 or compound_count < 2: events.add(var) return events
def get_pointed(seq): ''' Return a set of sequents each with a single succedent. ''' result = set() if len(seq.succedents) == 1: for succedent in seq.succedents: if succedent.name != 'OPTIONALLY': result.add(seq) elif len(seq.succedents) > 1: for succedent in seq.succedents: remaining = set_without(seq.succedents, succedent) negated = union(map(get_negated, remaining)) # FIXME get_negated is a disjunction; do not union it neg_neg = union(map(get_negated, seq.antecedents)) if not (negated & neg_neg): antecedents = seq.antecedents | negated result.add(Sequent(antecedents, set([succedent]))) else: TODO('allow empty succedents') return result
def simplify_step(completed): terms = union(p.terms for p in completed) def step(fact_sets): result = fact_sets.copy() for facts in fact_sets: for fact in facts: for simple in weaken_facts(facts, fact): frozen = frozenset(simple) if frozen not in result: if complete(simple, terms) == completed: result.add(frozen) return result return step
def complete(facts, terms=set()): terms = terms | union(p.terms for p in facts) | set([BOT, TOP]) relevant_facts = set(p for p in basic_facts if p.terms <= terms) return close_under(facts, complete_step(terms, relevant_facts))
def vars(self): return union(s.vars for s in self.antecedents | self.succedents)
def consts(self): return union(s.consts for s in self.antecedents | self.succedents)
def optimize_plan(antecedents, succedent, bound): """Iterate through the space of plans, narrowing heuristically.""" assert isinstance(antecedents, sortedset) assert isinstance(succedent.consts, sortedset) # ensure if not antecedents and succedent.vars <= bound: POMAGMA_DEBUG('ensure {}', succedent) return Ensure.make(succedent) # conditionals # HEURISTIC test eagerly in arbitrary order for a in antecedents: if a.is_rel(): if a.vars <= bound: antecedents_a = sortedset(set_without(antecedents, a)) POMAGMA_DEBUG('test relation {}', a) body = optimize_plan(antecedents_a, succedent, bound) return Test.make(a, body) else: assert a.is_fun(), a if a.vars <= bound and a.var in bound: antecedents_a = sortedset(set_without(antecedents, a)) POMAGMA_DEBUG('test function {}', a) body = optimize_plan(antecedents_a, succedent, bound) return Test.make(a, body) # find & bind variable # HEURISTIC bind eagerly in arbitrary order for a in antecedents: if a.is_fun(): if a.vars <= bound: assert a.var not in bound antecedents_a = sortedset(set_without(antecedents, a)) bound_a = set_with(bound, a.var) POMAGMA_DEBUG('let {}', a) body = optimize_plan(antecedents_a, succedent, bound_a) return Let.make(a, body) else: # TODO find inverse if injective function pass results = [] # iterate unknown if succedent.is_rel() and succedent.name != 'EQUAL': # TODO handle EQUAL s_free = succedent.vars - bound if len(succedent.vars) == len(succedent.args) and len(s_free) == 1: v = iter(s_free).next() bound_v = set_with(bound, v) POMAGMA_DEBUG('iterate unknown {}', v) body = optimize_plan(antecedents, succedent, bound_v) results.append(Iter.make(v, Test.make(UNKNOWN(succedent), body))) # iterate forward forward_vars = set() for a in antecedents: a_free = a.vars - bound if len(a_free) == 1: forward_vars |= a_free for v in forward_vars: bound_v = set_with(bound, v) POMAGMA_DEBUG('iterate forward {}', v) body = optimize_plan(antecedents, succedent, bound_v) results.append(Iter.make(v, body)) # iterate backward for a in antecedents: if a.is_fun() and a.args and a.var in bound and not (a.vars <= bound): nargs = len(a.args) a_free = a.vars - bound bound_v = bound | a_free antecedents_a = sortedset(set_without(antecedents, a)) assert len(a_free) in [0, 1, 2] assert nargs in [0, 1, 2] POMAGMA_DEBUG('iterate backward {}', a) if nargs == 1 and len(a_free) == 1: # TODO injective function inverse need not be iterated body = optimize_plan(antecedents_a, succedent, bound_v) results.append(IterInvInjective.make(a, body)) elif nargs == 2 and len(a_free) == 1 and len(a.vars) == 2: (fixed,) = list(a.vars - a_free) body = optimize_plan(antecedents_a, succedent, bound_v) results.append(IterInvBinaryRange.make(a, fixed, body)) elif nargs == 2 and len(a_free) == 2: body = optimize_plan(antecedents_a, succedent, bound_v) results.append(IterInvBinary.make(a, body)) # HEURISTIC iterate locally eagerly if results: return min(results) # iterate anything for v in union(a.vars for a in antecedents) | succedent.vars - bound: bound_v = set_with(bound, v) POMAGMA_DEBUG('iterate non-locally') body = optimize_plan(antecedents, succedent, bound_v) results.append(Iter.make(v, body)) return min(results)
def get_compiled(antecedents, succedent, bound): ''' Iterate through the space of strategies, narrowing heuristically. ''' POMAGMA_DEBUG('{} | {} |- {}', list(bound), list(antecedents), succedent) if not antecedents and succedent.vars <= bound: POMAGMA_DEBUG('ensure') return [Ensure(succedent)] results = [] # bind succedent constants for c in succedent.consts: if c.var not in bound: bound_c = set_with(bound, c.var) POMAGMA_DEBUG('bind succedent constant') for s in get_compiled(antecedents, succedent, bound_c): results.append(Let(c, s)) return results # HEURISTIC bind eagerly in arbitrary order # bind antecedent constants for a in antecedents: if not a.args and a.var not in bound: assert a.is_fun(), a antecedents_a = set_without(antecedents, a) bound_a = set_with(bound, a.var) POMAGMA_DEBUG('bind antecedent constant') for s in get_compiled(antecedents_a, succedent, bound_a): results.append(Let(a, s)) return results # HEURISTIC bind eagerly in arbitrary order # conditionals for a in antecedents: # if a.name in ['LESS', 'NLESS']: if a.is_rel(): if a.vars <= bound: antecedents_a = set_without(antecedents, a) POMAGMA_DEBUG('conditional') for s in get_compiled(antecedents_a, succedent, bound): results.append(Test(a, s)) else: assert a.is_fun(), a if a.vars <= bound and a.var in bound: antecedents_a = set_without(antecedents, a) POMAGMA_DEBUG('conditional') for s in get_compiled(antecedents_a, succedent, bound): results.append(Test(a, s)) if results: return results # HEURISTIC test eagerly in arbitrary order # find & bind variable for a in antecedents: if a.is_fun(): if a.vars <= bound: assert a.var not in bound antecedents_a = set_without(antecedents, a) bound_a = set_with(bound, a.var) POMAGMA_DEBUG('bind variable') for s in get_compiled(antecedents_a, succedent, bound_a): results.append(Let(a, s)) else: # TODO find inverse if injective function pass if results: return results # HEURISTIC bind eagerly in arbitrary order # iterate forward for a in antecedents: # works for both Relation and Function antecedents if a.vars & bound: for v in a.vars - bound: bound_v = set_with(bound, v) POMAGMA_DEBUG('iterate forward') for s in get_compiled(antecedents, succedent, bound_v): results.append(Iter(v, s)) # iterate backward for a in antecedents: if a.is_fun() and a.var in bound: a_vars = a.vars a_free = a_vars - bound assert len(a_free) in [0, 1, 2] nargs = len(a.args) assert nargs in [0, 1, 2] if nargs and a_free: bound_v = bound | a_free antecedents_a = set(antecedents) antecedents_a.remove(a) POMAGMA_DEBUG('iterate backward') if nargs == 1 and len(a_free) == 1: # TODO injective function inverse need not be iterated for s in get_compiled(antecedents_a, succedent, bound_v): results.append(IterInvInjective(a, s)) elif nargs == 2 and len(a_free) == 1 and len(a_vars) == 2: for s in get_compiled(antecedents_a, succedent, bound_v): (fixed,) = list(a.vars - a_free) results.append(IterInvBinaryRange(a, fixed, s)) elif nargs == 2 and len(a_free) == 2: for s in get_compiled(antecedents_a, succedent, bound_v): results.append(IterInvBinary(a, s)) if results: return results # HEURISTIC iterate locally eagerly # iterate anything free = (union([a.vars for a in antecedents]) | succedent.vars - bound) for v in free: bound_v = set_with(bound, v) POMAGMA_DEBUG('iterate non-locally') for s in get_compiled(antecedents, succedent, bound_v): results.append(Iter(v, s)) assert results return results
def consts(self): if self.is_fun() and not self.args: return set([self]) else: return union([arg.consts for arg in self.args])