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 validate(self, bound): assert self.value in bound if self.lhs_fixed: assert_in(self.var1, bound) assert_not_in(self.var2, bound) self.body.validate(set_with(bound, self.var2)) else: assert_in(self.var2, bound) assert_not_in(self.var1, bound) self.body.validate(set_with(bound, self.var1))
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 normalize_given(seq, atom, bound): for normal in normalize(seq): if atom in normal.antecedents or atom.is_var(): yield normal # HACK to deal with Equation args succedent = iter(normal.succedents).next() if succedent.name == 'EQUAL': lhs, rhs = succedent.args if lhs == atom: yield Sequent( set_with(normal.antecedents, lhs), set([Expression('EQUAL', lhs.var, rhs)])) elif rhs == atom: yield Sequent( set_with(normal.antecedents, rhs), set([Expression('EQUAL', lhs, rhs.var)]))
def validate(self, bound): assert_not_in(self.var, bound) bound = set_with(bound, self.var) for test in self.tests: assert_subset(test.vars, bound) for var, expr in self.lets.iteritems(): assert_subset(expr.vars, bound) assert_not_in(var, bound) self.body.validate(bound)
def compile_solver(expr, theory): ''' Produces a set of programs that finds values of term satisfying a theory. Inputs: expr - string, an expression with free variables theory - string representing a theory (in .theory format) Outputs: a program to be consumed by the virtual machine Example: expr = 's' theory = """ # 6 constraints = 4 facts + 2 rules LESS APP V s s NLESS x BOT NLESS x I LESS APP s BOT BOT -------------- ---------------- EQUAL APP s I I LESS I APP s x LESS TOP APP s x LESS TOP APP s TOP """ ''' assert isinstance(expr, basestring), expr assert isinstance(theory, basestring), theory expr = desugar_expr(parse_string_to_expr(expr)) assert expr.vars, expr theory = parse_theory_string(theory) facts = map(desugar_expr, theory['facts']) rules = map(desugar_sequent, theory['rules']) sequents = [] can_infer_necessary = not rules and all(f.vars <= expr.vars for f in facts) can_infer_possible = expr.is_var() # TODO generalize to injective exprs if can_infer_necessary: sequents.append(Sequent(facts, [NONEGATE(RETURN(expr))])) if can_infer_possible: fail = NONEGATE(NRETURN(expr)) sequents += [Sequent([], [f, fail]) for f in facts] sequents += [ Sequent(r.antecedents, set_with(r.succedents, fail)) for r in rules ] assert sequents, 'Cannot infer anything' programs = [] write_full_programs(programs, sequents, can_parallelize=False) program = '\n'.join(programs) assert not re.search('FOR_BLOCK', program), 'cannot parallelize' return program
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 validate(self, bound): assert_subset(self.expr.vars, bound) assert_not_in(self.var, bound) self.body.validate(set_with(bound, self.var))
def validate(self, bound): assert_in(self.value, bound) assert_not_in(self.var1, bound) assert_not_in(self.var2, bound) self.body.validate(set_with(bound, self.var1, self.var2))
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_bound(atom): if atom.is_fun(): return set_with(atom.vars, atom.var) else: return atom.vars
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