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 weaken_facts(facts, fact): assert fact in facts if fact.is_rel(): without = set_without(facts, fact) yield without if fact.name == 'EQUAL': lhs, rhs = fact.args yield set_with(without, LESS(lhs, rhs)) yield set_with(without, LESS(rhs, lhs))
def get_contrapositives(seq): if len(seq.succedents) == 1: seq_succedent = iter(seq.succedents).next() result = set() for antecedent in seq.antecedents: if antecedent.name != 'OPTIONALLY': antecedents = set_without(seq.antecedents, antecedent) succedents = get_negated(antecedent) for disjunct in get_negated(seq_succedent): if get_negated(disjunct) & antecedents: pass # contradiction else: result.add(Sequent( set_with(antecedents, disjunct), succedents)) return result elif len(seq.succedents) > 1: TODO('allow multiple succedents') else: TODO('allow empty succedents')
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 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) try: neg_remaining = map(try_get_negated, remaining) except NotNegatable: continue for negated in itertools.product(*neg_remaining): try: antecedents = try_simplify_antecedents( set(negated) | seq.antecedents) except Inconsistent: continue result.add(Sequent(antecedents, set([succedent]))) else: raise ValueError('get_contrapositives never returns empty succedents') return result
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