def test_mapget_of_makemap1(self): t = THandle("T", INT) xs = EVar("xs").with_type(TBag(t)) x = EVar("x").with_type(t) y = EVar("y").with_type(t) mt = TTuple((INT, INT)) e1 = EMapGet( EMakeMap2( xs, ELambda( x, ETuple( (EGetField(x, "val").with_type(INT), EGetField( y, "val").with_type(INT))).with_type(mt))).with_type( TMap(t, mt)), y).with_type(mt) e2 = EUnaryOp( UOp.The, EMap( EFilter( e1.map.e, mk_lambda(e1.map.value.arg.type, lambda foo: EEq(foo, e1.key))).with_type( e1.map.e.type), e1.map.value).with_type(e1.map.e.type)).with_type( e1.map.e.type.t) assert retypecheck(e1) assert retypecheck(e2) self.assert_same(e1, e2)
def test_distinct_mapkeys(self): xs = EVar("xs").with_type(INT_BAG) x = EVar("x").with_type(INT) e1 = EUnaryOp(UOp.Distinct, xs) e2 = EMapKeys(EMakeMap2(xs, ELambda(x, T))) assert retypecheck(e1) assert retypecheck(e2) self.assert_same(e1, e2)
def test_let_discovery(self): x = EVar("x").with_type(INT) spec = ESum([x, x, x, x]) assert retypecheck(spec) y = EVar("y").with_type(INT) goal = ELet(ESum([x, x]), ELambda(y, ESum([y, y]))) assert retypecheck(goal) assert check_discovery(spec=spec, args=[x], expected=goal)
def test_let_discovery(self): x = EVar("x").with_type(INT) spec = ESum([x, x, x, x]) assert retypecheck(spec) y = EVar("y").with_type(INT) goal = ELet(ESum([x, x]), ELambda(y, ESum([y, y]))) assert retypecheck(goal) assert check_discovery(spec=spec, args=[x], expected=goal)
def test_distinct_mapkeys(self): xs = EVar("xs").with_type(INT_BAG) x = EVar("x").with_type(INT) e1 = EUnaryOp(UOp.Distinct, xs) e2 = EMapKeys(EMakeMap2(xs, ELambda(x, ETRUE))) assert retypecheck(e1) assert retypecheck(e2) self.assert_same(e1, e2)
def test_distribute_filter_over_subtract(self): xs = EVar("xs").with_type(INT_BAG) ys = EVar("ys").with_type(INT_BAG) x = EVar("x").with_type(INT) e1 = EFilter(EBinOp(xs, "-", ys), ELambda(x, ECall("f", (x,)).with_type(BOOL))) assert retypecheck(e1) e2 = EBinOp(EFilter(xs, e1.p), "-", EFilter(ys, e1.p)) assert retypecheck(e2) self.assert_same(e1, e2)
def desugar(spec: Spec) -> Spec: # rewrite enums repl = { name: EEnumEntry(name).with_type(t) for t in all_types(spec) if isinstance(t, TEnum) for name in t.cases } spec = subst(spec, repl) # convert all collection types to bags spec = Spec(spec.name, list(spec.types), list(spec.extern_funcs), list(spec.statevars), list(spec.assumptions), list(spec.methods), spec.header, spec.footer, spec.docstring) for i in range(len(spec.statevars)): v, t = spec.statevars[i] if isinstance(t, TSet): # Sets become bags w/ implicit unique assumptions. t = TBag(t.t) spec.statevars[i] = (v, t) v = EVar(v).with_type(t) spec.assumptions.append(EUnaryOp(UOp.AreUnique, v).with_type(BOOL)) assert retypecheck(spec, env={}) # organize queries by name queries = {q.name: q for q in spec.methods if isinstance(q, Query)} class V(BottomUpRewriter): def visit_ECall(self, e): q = queries.get(e.func) if q is not None: return self.visit( subst( q.ret, { arg_name: arg for ((arg_name, ty), arg) in zip(q.args, e.args) })) else: return ECall(e.func, tuple(self.visit(a) for a in e.args)).with_type(e.type) spec = V().visit(spec) spec.methods = [ m for m in spec.methods if not (isinstance(m, Query) and m.visibility == Visibility.Private) ] class V(BottomUpRewriter): def visit_Exp(self, e): return desugar_list_comprehensions(e) spec = V().visit(spec) assert retypecheck(spec, env={}) return spec
def test_distribute_filter_over_subtract(self): xs = EVar("xs").with_type(INT_BAG) ys = EVar("ys").with_type(INT_BAG) x = EVar("x").with_type(INT) e1 = EFilter(EBinOp(xs, "-", ys), ELambda(x, ECall("f", (x,)).with_type(BOOL))) assert retypecheck(e1) e2 = EBinOp(EFilter(xs, e1.predicate), "-", EFilter(ys, e1.predicate)) assert retypecheck(e2) self.assert_same(e1, e2)
def test_easy_synth(self): res = None x = EVar("x").with_type(BOOL) xs = EVar("xs").with_type(TBag(BOOL)) target = EFilter(EStateVar(xs), ELambda(x, x)) assumptions = EUnaryOp(UOp.All, xs) assert retypecheck(target) assert retypecheck(assumptions) assert check_discovery(target, EStateVar(EVar("xs")), args=[x], state_vars=[xs], assumptions=assumptions)
def test_distribute_the_over_map(self): xs = EVar("xs").with_type(INT_BAG) x = EVar("x").with_type(INT) e1 = EUnaryOp(UOp.The, EMap(xs, ELambda(x, ECall("f", (x,)).with_type(INT)))) assert retypecheck(e1) e2 = ECond( EUnaryOp(UOp.Exists, xs), e1.e.transform_function.apply_to(EUnaryOp(UOp.The, xs)), EUnaryOp(UOp.The, EEmptyList().with_type(xs.type))) assert retypecheck(e2) self.assert_same(e1, e2)
def test_slice_of_slice(self): xs = EVar("xs").with_type(TList(INT)) st1 = EVar("st1").with_type(INT) ed1 = EVar("ed1").with_type(INT) st2 = EVar("st2").with_type(INT) ed2 = EVar("ed2").with_type(INT) e1 = EListSlice(EListSlice(xs, st1, ed1), st2, ed2) assert retypecheck(e1) e2 = EListSlice(xs, EBinOp(max_of(st2, ZERO), "+", max_of(st1, ZERO)), min_of(ed1, ESum([max_of(st1, ZERO), ed2]))) assert retypecheck(e2) self.assert_same(e1, e2, assumptions=EUnaryOp(UOp.AreUnique, xs).with_type(BOOL))
def test_distribute_the_over_map(self): xs = EVar("xs").with_type(INT_BAG) x = EVar("x").with_type(INT) e1 = EUnaryOp(UOp.The, EMap(xs, ELambda(x, ECall("f", (x,)).with_type(INT)))) assert retypecheck(e1) e2 = ECond( EUnaryOp(UOp.Exists, xs), e1.e.f.apply_to(EUnaryOp(UOp.The, xs)), EUnaryOp(UOp.The, EEmptyList().with_type(xs.type))) assert retypecheck(e2) self.assert_same(e1, e2)
def test_heap_enumeration(self): xs = EVar("xs").with_type(INT_BAG) context = RootCtx(state_vars=[xs]) cost_model = CostModel() def not_min_or_max(e, *args, **kwargs): # forbid min/max to ensure that heap operations get cached if isinstance(e, EArgMin) or isinstance(e, EArgMax): return False return True enumerator = Enumerator(examples=[{ "xs": Bag(()) }, { "xs": Bag((1, 2)) }, { "xs": Bag((1, 1)) }], cost_model=cost_model, check_wf=not_min_or_max) with save_property(accelerate, "value"): accelerate.value = False print("-" * 20 + " Looking for xs...") found_xs = False for e in enumerator.enumerate(context, 0, STATE_POOL): print(pprint(e)) if e == xs: assert retypecheck(deep_copy(e)) found_xs = True print("^^^ FOUND") assert found_xs print("-" * 20 + " Looking for heap construction...") found_make_heap = False for e in enumerator.enumerate(context, 1, STATE_POOL): print(pprint(e)) if isinstance(e, EMakeMinHeap) or isinstance(e, EMakeMaxHeap): assert retypecheck(deep_copy(e)) found_make_heap = True print("^^^ FOUND") assert found_make_heap print("-" * 20 + " Looking for heap usage...") found_heap_peek = False for e in enumerator.enumerate(context, 2, RUNTIME_POOL): print(pprint(e)) if isinstance(e, EHeapPeek) or isinstance(e, EHeapPeek2): assert retypecheck(deep_copy(e)) found_heap_peek = True print("^^^ FOUND") assert found_heap_peek
def test_easy_synth(self): res = None x = EVar("x").with_type(BOOL) xs = EVar("xs").with_type(TBag(BOOL)) target = EFilter(EStateVar(xs), ELambda(x, x)) assumptions = EUnaryOp(UOp.All, xs) assert retypecheck(target) assert retypecheck(assumptions) assert check_discovery(target, EStateVar(EVar("xs")), args=[x], state_vars=[xs], assumptions=assumptions)
def test_argmin(self): xs = EVar("xs").with_type(INT_BAG) ys = EVar("ys").with_type(INT_BAG) id = mk_lambda(INT, lambda x: x) e1 = EArgMin(EBinOp(xs, "+", ys), id) e2 = ECond(EUnaryOp(UOp.Empty, xs), EArgMin(ys, id), ECond(EUnaryOp(UOp.Empty, ys), EArgMin(xs, id), EArgMin(EBinOp( ESingleton(EArgMin(xs, id)), "+", ESingleton(EArgMin(ys, id))), id))) assert retypecheck(e1) assert retypecheck(e2) self.assert_same(e1, e2)
def test_argmin(self): xs = EVar("xs").with_type(INT_BAG) ys = EVar("ys").with_type(INT_BAG) id = mk_lambda(INT, lambda x: x) e1 = EArgMin(EBinOp(xs, "+", ys), id) e2 = ECond(EUnaryOp(UOp.Empty, xs), EArgMin(ys, id), ECond(EUnaryOp(UOp.Empty, ys), EArgMin(xs, id), EArgMin(EBinOp( ESingleton(EArgMin(xs, id)), "+", ESingleton(EArgMin(ys, id))), id))) assert retypecheck(e1) assert retypecheck(e2) self.assert_same(e1, e2)
def test_map_eq(self): k = TNative("V") v = TBag(THandle("H", k)) t = TMap(k, v) m1 = EVar("m1").with_type(t) m2 = EVar("m1").with_type(t) e = EImplies(EEq(m1, m2), EEq(EMapKeys(m1), EMapKeys(m2))) assert retypecheck(e) assert valid(e, collection_depth=3) k = EVar("k").with_type(t.k) e = EImplies(EEq(m1, m2), EEq(EMapGet(m1, k), EMapGet(m2, k))) assert retypecheck(e) assert valid(e, collection_depth=3)
def test_easy_synth(self): # FIXME: resolve ETreeMultisetElems problem (#107) and support allow_random_assignment_heuristic in this case with save_property(allow_random_assignment_heuristic, "value"): allow_random_assignment_heuristic.value = False x = EVar("x").with_type(BOOL) xs = EVar("xs").with_type(TBag(BOOL)) target = EFilter(EStateVar(xs), ELambda(x, x)) assumptions = EUnaryOp(UOp.All, xs) assert retypecheck(target) assert retypecheck(assumptions) assert check_discovery(target, EStateVar(EVar("xs")), args=[x], state_vars=[xs], assumptions=assumptions)
def test_map_eq(self): k = TNative("V") v = TBag(THandle("H", k)) t = TMap(k, v) m1 = EVar("m1").with_type(t) m2 = EVar("m1").with_type(t) e = EImplies(EEq(m1, m2), EEq(EMapKeys(m1), EMapKeys(m2))) assert retypecheck(e) assert valid(e, collection_depth=3) k = EVar("k").with_type(t.k) e = EImplies(EEq(m1, m2), EEq(EMapGet(m1, k), EMapGet(m2, k))) assert retypecheck(e) assert valid(e, collection_depth=3)
def test_slice_of_slice(self): xs = EVar("xs").with_type(TList(INT)) st1 = EVar("st1").with_type(INT) ed1 = EVar("ed1").with_type(INT) st2 = EVar("st2").with_type(INT) ed2 = EVar("ed2").with_type(INT) e1 = EListSlice(EListSlice(xs, st1, ed1), st2, ed2) assert retypecheck(e1) e2 = EListSlice(xs, EBinOp(max_of(st2, ZERO), "+", max_of(st1, ZERO)), min_of(ed1, ESum([max_of(st1, ZERO), ed2]))) assert retypecheck(e2) self.assert_same(e1, e2, assumptions=EUnaryOp(UOp.AreUnique, xs).with_type(BOOL))
def test_heap_enumeration(self): xs = EVar("xs").with_type(INT_BAG) context = RootCtx(state_vars=[xs]) cost_model = CostModel() def not_min_or_max(e, *args, **kwargs): # forbid min/max to ensure that heap operations get cached if isinstance(e, EArgMin) or isinstance(e, EArgMax): return False return True enumerator = Enumerator( examples=[{"xs": Bag(())}, {"xs": Bag((1,2))}, {"xs": Bag((1,1))}], cost_model=cost_model, check_wf=not_min_or_max) with save_property(accelerate, "value"): accelerate.value = False print("-" * 20 + " Looking for xs...") found_xs = False for e in enumerator.enumerate(context, 0, STATE_POOL): print(pprint(e)) if e == xs: assert retypecheck(deep_copy(e)) found_xs = True print("^^^ FOUND") assert found_xs print("-" * 20 + " Looking for heap construction...") found_make_heap = False for e in enumerator.enumerate(context, 1, STATE_POOL): print(pprint(e)) if isinstance(e, EMakeMinHeap) or isinstance(e, EMakeMaxHeap): assert retypecheck(deep_copy(e)) found_make_heap = True print("^^^ FOUND") assert found_make_heap print("-" * 20 + " Looking for heap usage...") found_heap_peek = False for e in enumerator.enumerate(context, 2, RUNTIME_POOL): print(pprint(e)) if isinstance(e, EHeapPeek) or isinstance(e, EHeapPeek2): assert retypecheck(deep_copy(e)) found_heap_peek = True print("^^^ FOUND") assert found_heap_peek
def test_set_sub(self): t = TSet(INT) s1 = Bag((0, 1)) s2 = Bag((1, 0)) e = EEq(EBinOp(EVar("s1").with_type(t), "-", EVar("s2").with_type(t)), EEmptyList().with_type(t)) assert retypecheck(e) assert eval(e, {"s1": s1, "s2": s2}) is True
def convert_sets_to_bags(spec : Spec) -> Spec: """Convert set-type state variables to bag-type state variables. This function also adds invariants stating that all bags which used to be sets have distinct elements. """ spec = Spec( spec.name, list(spec.types), list(spec.extern_funcs), list(spec.statevars), list(spec.assumptions), list(spec.methods), spec.header, spec.footer, spec.docstring) for i in range(len(spec.statevars)): v, t = spec.statevars[i] if isinstance(t, TSet): t = TBag(t.elem_type) spec.statevars[i] = (v, t) v = EVar(v).with_type(t) spec.assumptions.append(EUnaryOp(UOp.AreUnique, v).with_type(BOOL)) if not retypecheck(spec, env={}): raise Exception("Set->Bag conversion failed") return spec
def test_set_sub(self): t = TSet(INT) s1 = Bag((0, 1)) s2 = Bag((1, 0)) e = EEq(EBinOp(EVar("s1").with_type(t), "-", EVar("s2").with_type(t)), EEmptyList().with_type(t)) assert retypecheck(e) assert eval(e, {"s1": s1, "s2": s2}) is True
def test_enumerate_fragments_estatevar(self): b = EVar("b").with_type(BOOL) e = ELet(ZERO, mk_lambda(INT, lambda x: EStateVar(b))) assert retypecheck(e) for (a, e, r, bound) in enumerate_fragments(e): if e == b: assert not bound, "EStateVar should reset bound variables, but got {}".format(bound)
def test_intersect(self): a = EVar("a").with_type(INT_BAG) b = EVar("b").with_type(INT_BAG) e = EBinOp( EIntersect(a, b), "+", EBinOp(a, "-", b)) assert retypecheck(e) self.assert_same(a, e, op="==")
def test_heap_wf(self): e = EHeapPeek2(EStateVar(EMakeMinHeap(EVar('xs'), ELambda(EVar('_var21501'), EVar('_var21501'))))) assert retypecheck(e, env={ "xs": INT_BAG, "_var21501": INT}) state_vars = OrderedSet([EVar('xs').with_type(TBag(TInt()))]) args = OrderedSet([EVar('x').with_type(TInt())]) pool = RUNTIME_POOL assert exp_wf(e, context=RootCtx(args=args, state_vars=state_vars), pool=pool)
def test_optimized_in1(self): xs = EVar("xs").with_type(INT_BAG) i = EVar("i").with_type(INT) j = EVar("j").with_type(INT) e1 = EIn(i, EBinOp(EStateVar(xs), "-", ESingleton(j))) assert retypecheck(e1) e2 = optimized_in(i, e1.e2) assert not alpha_equivalent(e1, e2) self.assert_same(e1, e2)
def test_enumerate_fragments_bound(self): b = EVar("b").with_type(BOOL) e = ELet(ZERO, mk_lambda(INT, lambda x: b)) assert retypecheck(e) for (a, x, r, bound) in enumerate_fragments(e): if x == b: assert bound == { e.f.arg }, "got {}".format(bound) elif x == ZERO: assert bound == set(), "got {}".format(bound)
def test_optimized_in2(self): xs = EVar("xs").with_type(INT_BAG) ys = EVar("ys").with_type(INT_BAG) i = EVar("i").with_type(INT) e1 = EIn(i, EBinOp(xs, "-", ys)) assert retypecheck(e1) e2 = optimized_in(i, e1.e2) assert not alpha_equivalent(e1, e2) self.assert_same(e1, e2)
def test_optimized_in1(self): xs = EVar("xs").with_type(INT_BAG) i = EVar("i").with_type(INT) j = EVar("j").with_type(INT) e1 = EIn(i, EBinOp(EStateVar(xs), "-", ESingleton(j))) assert retypecheck(e1) e2 = optimized_in(i, e1.e2) assert not alpha_equivalent(e1, e2) self.assert_same(e1, e2)
def test_subsub(self): xs = EVar("xs").with_type(INT_BAG) i = EVar("i").with_type(INT) e1 = EBinOp(EUnaryOp(UOp.Distinct, xs), "-", EBinOp(xs, "-", ESingleton(i))) assert retypecheck(e1) m = EMakeMap2( e1.e1, mk_lambda( INT, lambda x: EUnaryOp( UOp.Length, EFilter(xs, mk_lambda(INT, lambda y: EEq( x, y)))).with_type(INT))).with_type(TMap(INT, INT)) count = EMapGet(m, i).with_type(INT) e2 = ECond(EEq(count, ONE), ESingleton(i).with_type(INT_BAG), EEmptyList().with_type(INT_BAG)).with_type(INT_BAG) assert retypecheck(e2) self.assert_same(e1, e2)
def test_sub_self(self): xs = EVar("xs").with_type(TBag(INT)) removed = EVar("removed").with_type(TBag(INT)) added = EVar("added").with_type(TBag(INT)) e = EBinOp(EBinOp(EBinOp(xs, "-", removed), "+", added), "-", xs) assert retypecheck(e) self.assert_same(e, added, assumptions=EAll([ # EIsSubset(removed, xs), EDisjoint(added, removed)]))
def test_optimized_in2(self): xs = EVar("xs").with_type(INT_BAG) ys = EVar("ys").with_type(INT_BAG) i = EVar("i").with_type(INT) e1 = EIn(i, EBinOp(xs, "-", ys)) assert retypecheck(e1) e2 = optimized_in(i, e1.e2) assert not alpha_equivalent(e1, e2) self.assert_same(e1, e2)
def test_no_argument_conflict_lambda(self): x = EVar("x").with_type(TInt()) y = EVar("y").with_type(TInt()) f = ELambda(x, EBinOp(y, "+", ENum(1).with_type(INT))) assert retypecheck(f) g = subst(f, { y.id : x }) a = EVar("a").with_type(TInt()) b = EVar("b").with_type(TInt()) assert valid(EEq(g.apply_to(a), g.apply_to(b)))
def test_mutate_preserves_statevar(self): x = EVar("x").with_type(INT) e = EBinOp(EStateVar(x), "+", ONE) assert retypecheck(e) s = SAssign(x, EBinOp(x, "+", ONE).with_type(INT)) e2 = inc.mutate(e, s) e2 = inc.repair_EStateVar(e2, [x]) print(pprint(e)) print(pprint(e2)) assert e2 == EBinOp(EBinOp(EStateVar(x), "+", ONE), "+", ONE)
def test_mapget_of_makemap1(self): t = THandle("elem_type", INT) xs = EVar("xs").with_type(TBag(t)) x = EVar("x").with_type(t) y = EVar("y").with_type(t) mt = TTuple((INT, INT)) e1 = EMapGet( EMakeMap2(xs, ELambda(x, ETuple((EGetField(x, "val").with_type(INT), EGetField(y, "val").with_type(INT))).with_type(mt) )).with_type(TMap(t, mt)), y).with_type(mt) e2 = EUnaryOp(UOp.The, EMap( EFilter(e1.map.e, mk_lambda(e1.map.value_function.arg.type, lambda foo: EEq(foo, e1.key))).with_type(e1.map.e.type), e1.map.value_function).with_type(e1.map.e.type)).with_type(e1.map.e.type.elem_type) assert retypecheck(e1) assert retypecheck(e2) self.assert_same(e1, e2)
def test_mutate_preserves_statevar(self): x = EVar("x").with_type(INT) e = EBinOp(EStateVar(x), "+", ONE) assert retypecheck(e) s = SAssign(x, EBinOp(x, "+", ONE).with_type(INT)) e2 = strip_EStateVar(inc.mutate(e, s)) e2 = repair_well_formedness(e2, context=RootCtx(state_vars=[x], args=[])) print(pprint(e)) print(pprint(e2)) assert e2 == EBinOp(EBinOp(EStateVar(x), "+", ONE), "+", ONE)
def test_no_argument_conflict_lambda(self): x = EVar("x").with_type(TInt()) y = EVar("y").with_type(TInt()) f = ELambda(x, EBinOp(y, "+", ENum(1).with_type(INT))) assert retypecheck(f) g = subst(f, {y.id: x}) a = EVar("a").with_type(TInt()) b = EVar("b").with_type(TInt()) assert valid(equal(g.apply_to(a), g.apply_to(b)))
def test_mutate_preserves_statevar(self): x = EVar("x").with_type(INT) e = EBinOp(EStateVar(x), "+", ONE) assert retypecheck(e) s = SAssign(x, EBinOp(x, "+", ONE).with_type(INT)) e2 = strip_EStateVar(inc.mutate(e, s)) e2 = repair_well_formedness(e2, context=RootCtx(state_vars=[x], args=[])) print(pprint(e)) print(pprint(e2)) assert e2 == EBinOp(EBinOp(EStateVar(x), "+", ONE), "+", ONE)
def test_map_discovery(self): xs = EVar("xs").with_type(INT_BAG) y = EVar("y").with_type(INT) spec = EFilter(EStateVar(xs), mk_lambda(INT, lambda x: EEq(x, y))) assert retypecheck(spec) assert check_discovery( spec=spec, expected=lambda e: isinstance(e, EMapGet) and isinstance( e.map, EStateVar) and valid(EEq(e, spec)), args=[y], state_vars=[xs])
def test_let(self): e1 = ELet(ZERO, ELambda(x, x)) root_ctx = RootCtx(args=(), state_vars=()) assert retypecheck(e1) n = 0 for ee, ctx, pool in shred(e1, root_ctx, RUNTIME_POOL): if ee == x: e2 = replace(e1, root_ctx, RUNTIME_POOL, x, ctx, pool, ZERO) assert e2 == ELet(ZERO, ELambda(x, ZERO)) n += 1 assert n == 1
def test_incomplete_binders_list_2(self): res = None x = EVar("x").with_type(BOOL) xs = EVar("xs").with_type(TBag(BOOL)) target = EFilter(EStateVar(xs), ELambda(x, T)) assumptions = EUnaryOp(UOp.All, xs) assert retypecheck(target) assert retypecheck(assumptions) def should_stop(): return res == EStateVar(EVar("xs")) for r in improve(target, assumptions, [], [xs], [], CompositeCostModel(), BinderBuilder([], [xs], []), stop_callback=should_stop): print(pprint(r)) res = r assert should_stop()
def test_bag_plus_minus(self): t = THandle("H", INT) x = EVar("x").with_type(t) xs = EVar("xs").with_type(TBag(t)) spec = EBinOp(EBinOp(xs, "+", ESingleton(x)), "-", ESingleton(x)) expected = xs assert retypecheck(spec) assert valid(EEq(spec, expected)) ex = satisfy(ENot(EBinOp(spec, "===", expected).with_type(BOOL))) assert ex is not None assert check_discovery(spec=spec, expected=expected, args=[x, xs], examples=[ex])
def test_bind_callback(self): xs = EVar("xs").with_type(TBag(INT)) x = EVar("x").with_type(INT) e = EFilter(xs, ELambda(x, equal(x, ENum(1).with_type(INT)))) assert retypecheck(e) numbers = [0, 1, 1, 2, 3, 4] binds = [] m = eval(e, env={xs.id: Bag(numbers)}, bind_callback=lambda arg, val: binds.append((arg, val))) assert m == Bag([1, 1]), "m={}".format(m) assert binds == [(x, i) for i in numbers], "binds={}".format(binds)
def test_enumerate_fragments_strange_binder_behavior(self): xs = EVar("xs").with_type(TBag(INT)) x = EVar("x").with_type(INT) xs_eq_zero = EFilter(xs, ELambda(x, equal(x, ZERO))) e = EFilter(xs_eq_zero, ELambda(x, equal( EFilter(xs, ELambda(x, T)), EEmptyList().with_type(xs.type)))) assert retypecheck(e) for (a, e, r, bound) in enumerate_fragments(e): if e == T: assert not valid(implies(EAll(a), equal(x, ZERO)), validate_model=True), "assumptions at {}: {}".format(pprint(e), "; ".join(pprint(aa) for aa in a))
def test_var_under_estatevar(self): # wow, very tricky! # EStateVar(...) needs to be "separable" from the parent, so bound vars # get cleared. Thus, if EStateVar(x) appears somewhere, then `x` is # is free, even if it appears in e.g. \x -> EStateVar(x). x = EVar("x").with_type(INT) e = EUnaryOp(UOp.Exists, EFilter(ESingleton(ONE), ELambda(x, EStateVar(EEq(x, ZERO))))) print(pprint(e)) assert retypecheck(e) assert x in free_vars(e), free_vars(e) sub = subst(e, {"x":ZERO}) assert sub == EUnaryOp(UOp.Exists, EFilter(ESingleton(ONE), ELambda(x, EStateVar(EEq(ZERO, ZERO))))), pprint(sub)
def test_subsub(self): xs = EVar("xs").with_type(INT_BAG) i = EVar("i").with_type(INT) e1 = EBinOp( EUnaryOp(UOp.Distinct, xs), "-", EBinOp( xs, "-", ESingleton(i))) assert retypecheck(e1) m = EMakeMap2(e1.e1, mk_lambda(INT, lambda x: EUnaryOp(UOp.Length, EFilter(xs, mk_lambda(INT, lambda y: EEq(x, y)))).with_type(INT))).with_type(TMap(INT, INT)) count = EMapGet(m, i).with_type(INT) e2 = ECond( EEq(count, ONE), ESingleton(i).with_type(INT_BAG), EEmptyList().with_type(INT_BAG)).with_type(INT_BAG) assert retypecheck(e2) self.assert_same(e1, e2)
def test_map_discovery2(self): xs = EVar("xs").with_type(INT_BAG) y = EVar("y").with_type(INT) spec = EIn(y, EStateVar(xs)) assert retypecheck(spec) assert check_discovery( spec=spec, expected=lambda e: (isinstance(e, EMapGet) or isinstance(e, EHasKey)) and isinstance( e.map, EStateVar) and valid(EEq(e, spec)), args=[y], state_vars=[xs])
def test_let(self): e1 = ELet(ZERO, ELambda(x, x)) root_ctx = RootCtx(args=(), state_vars=()) assert retypecheck(e1) n = 0 for ee, ctx, pool in all_subexpressions_with_context_information(e1, root_ctx, RUNTIME_POOL): if ee == x: e2 = replace( e1, root_ctx, RUNTIME_POOL, x, ctx, pool, ZERO) assert e2 == ELet(ZERO, ELambda(x, ZERO)) n += 1 assert n == 1
def test_estatevar_ctx(self): xs = EVar("xs").with_type(INT_BAG) x = EVar("x").with_type(INT) y = EVar("y").with_type(BOOL) e = EMap(xs, ELambda(x, EStateVar(y))) ctx = RootCtx(args=(xs,), state_vars=(y,)) assert retypecheck(e) for ee, ctx, pool in all_subexpressions_with_context_information(e, ctx): if ee == y: assert isinstance(ctx, RootCtx) e = replace( e, ctx, RUNTIME_POOL, y, ctx, STATE_POOL, ETRUE) assert e == EMap(xs, ELambda(x, EStateVar(ETRUE))), pprint(e)
def test_pool_affects_alpha_equivalence(self): e = EMap(EEmptyList().with_type(INT_BAG), ELambda(x, ONE)) root_ctx = RootCtx(args=(), state_vars=()) assert retypecheck(e) c1 = [] for ee, ctx, pool in all_subexpressions_with_context_information(e, root_ctx, RUNTIME_POOL): if ee == ONE: c1.append(ctx) assert len(c1) == 1 c1 = c1[0] c2 = [] for ee, ctx, pool in all_subexpressions_with_context_information(e, root_ctx, STATE_POOL): if ee == ONE: c2.append(ctx) assert len(c2) == 1 c2 = c2[0] assert c1 != c2 assert not c1.alpha_equivalent(c2)
def test_ECond_3(self): x = ENum(1).with_type(INT) y = EBool(False) assert not retypecheck(ECond(EBool(True), y, x))
def _simple_filter(xs : Exp, p : ELambda, args : {EVar}): """Assumes the body of p is already in negation normal form""" if p.body == ETRUE: yield xs return if p.body == EFALSE: yield EEmptyList().with_type(xs.type) return if isinstance(xs, EEmptyList): yield xs return yielded = False if isinstance(xs, ESingleton): yielded = True yield optimized_cond(p.apply_to(xs.e), xs, EEmptyList().with_type(xs.type)) if isinstance(p.body, EBinOp) and p.body.op == BOp.Or: for e1, e2 in itertools.permutations([p.body.e1, p.body.e2]): for r1 in _simple_filter(xs, ELambda(p.arg, e1), args): for r2 in _simple_filter(xs, ELambda(p.arg, EAll([e2, ENot(e1)])), args): yielded = True yield EBinOp(r1, "+", r2).with_type(xs.type) if isinstance(p.body, EBinOp) and p.body.op == BOp.And: for e1, e2 in itertools.permutations([p.body.e1, p.body.e2]): for r1 in _simple_filter(xs, ELambda(p.arg, e1), args): yielded = True yield from _simple_filter(r1, ELambda(p.arg, e2), args) if isinstance(xs, EStateVar) and not any(v in args for v in free_vars(p)): yielded = True yield EStateVar(EFilter(xs.e, strip_EStateVar(p)).with_type(xs.type)).with_type(xs.type) if isinstance(xs, EMapGet) and isinstance(xs.map, EStateVar) and not any(v in args for v in free_vars(p)): for m in map_values_multi(xs.map.e, lambda ys: _simple_filter(ys, p, args)): yielded = True yield EMapGet(EStateVar(m).with_type(m.type), xs.key).with_type(xs.type) if isinstance(xs, EBinOp) and xs.op in ("+", "-"): for e1 in _simple_filter(xs.e1, p, args): for e2 in _simple_filter(xs.e2, p, args): yielded = True yield EBinOp(e1, xs.op, e2).with_type(xs.type) if isinstance(p.body, EBinOp) and p.body.op == "==": e1 = p.body.e1 e2 = p.body.e2 fvs2 = free_vars(e2) fvs1 = free_vars(e1) for (e1, fvs1), (e2, fvs2) in itertools.permutations([(e1, fvs1), (e2, fvs2)]): if p.arg in fvs1 and not any(a in fvs1 for a in args) and p.arg not in fvs2 and isinstance(xs, EStateVar): if e1 == p.arg: yield optimized_cond( optimized_in(e2, xs), ESingleton(e2).with_type(xs.type), EEmptyList().with_type(xs.type)) k = fresh_var(e1.type) e = EMapGet( EStateVar( EMakeMap2( EMap(xs.e, ELambda(p.arg, e1)), ELambda(k, EFilter(xs.e, ELambda(p.arg, EEq(e1, k)))))), e2) res = retypecheck(e) assert res yielded = True yield e if not yielded: yield EFilter(xs, p).with_type(xs.type)
def test_ECond_4(self): x = ENum(1).with_type(INT) assert not retypecheck(ECond(x, x, x))
def test_lambda_arg_inference(self): s = ESingleton(ETRUE) x = EVar("x") assert retypecheck(EFilter(s, ELambda(x, x))) assert retypecheck(EMap(s, ELambda(x, x))) assert retypecheck(EMakeMap2(s, ELambda(x, x)))
def test_heaps(self): e = ECond(EBinOp(EBinOp(EMapGet(EStateVar(EMakeMap2(EVar('xs'), ELambda(EVar('_var39381'), EUnaryOp('len', EFilter(EVar('xs'), ELambda(EVar('_var39382'), EBinOp(EVar('_var39381'), '==', EVar('_var39382')))))))), ENum(0).with_type(INT)), '==', ENum(1).with_type(INT)), 'and', EBinOp(ENum(0).with_type(INT), '==', EStateVar(EArgMin(EVar('xs'), ELambda(EVar('_var21501'), EVar('_var21501')))))), EHeapPeek2(EStateVar(EMakeMinHeap(EVar('xs'), ELambda(EVar('_var21501'), EVar('_var21501'))))), EStateVar(EArgMin(EVar('xs'), ELambda(EVar('_var21501'), EVar('_var21501'))))) assert retypecheck(e, env={ "xs": INT_BAG, "_var21501": INT})
def test_map_discovery(self): xs = EVar("xs").with_type(INT_BAG) y = EVar("y").with_type(INT) spec = EFilter(EStateVar(xs), mk_lambda(INT, lambda x: EEq(x, y))) assert retypecheck(spec) assert check_discovery(spec=spec, expected=lambda e: isinstance(e, EMapGet) and isinstance(e.map, EStateVar) and valid(EEq(e, spec)), args=[y], state_vars=[xs])
def test_map_discovery2(self): xs = EVar("xs").with_type(INT_BAG) y = EVar("y").with_type(INT) spec = EIn(y, EStateVar(xs)) assert retypecheck(spec) assert check_discovery(spec=spec, expected=lambda e: (isinstance(e, EMapGet) or isinstance(e, EHasKey)) and isinstance(e.map, EStateVar) and valid(EEq(e, spec)), args=[y], state_vars=[xs])