Exemplo n.º 1
0
def sizeof(e, cardinalities):
    terms = [ONE]
    if is_collection(e.type):
        terms.append(cardinality(e, cardinalities))
    elif isinstance(e.type, TMap):
        ks = EMapKeys(e).with_type(TBag(e.type.k))
        terms.append(cardinality(ks, cardinalities))
        if is_collection(e.type.v):
            vals = EFlatMap(ks, mk_lambda(e.type.k, lambda k: EMapGet(e, k).with_type(e.type.v))).with_type(e.type.v)
            terms.append(cardinality(vals, cardinalities))
    return SymbolicCost(ESum(terms), cardinalities)
Exemplo n.º 2
0
def fingerprint_is_subset(fp1, fp2):
    """Are all cases of fp1 a subset of fp2?"""
    assert is_collection(fp1[0])
    assert is_collection(fp2[0])
    x = EVar("x").with_type(fp1[0])
    y = EVar("y").with_type(fp2[0])
    is_subset = EIsSubset(x, y)
    return all(
        eval(is_subset, {
            "x": a,
            "y": b
        }) for (a, b) in zip(fp1[1:], fp2[1:]))
Exemplo n.º 3
0
 def visit_EBinOp(self, e):
     c1 = self.visit(e.e1)
     c2 = self.visit(e.e2)
     costs = [ONE, c1, c2]
     if e.op == BOp.In:
         costs.append(self.cardinality(e.e2))
     elif e.op == "==" and is_collection(e.e1.type):
         costs.append(self.cardinality(e.e1))
         costs.append(self.cardinality(e.e2))
     elif e.op == "-" and is_collection(e.type):
         costs.append(self.cardinality(e.e1))
         costs.append(self.cardinality(e.e2))
     return self.combine(costs)
Exemplo n.º 4
0
 def visit(self, e):
     if hasattr(e, "_nosimpl"): return e
     if isinstance(e, Exp) and not isinstance(e, ELambda): t = e.type
     new = super().visit(e)
     if isinstance(e, Exp) and not isinstance(e, ELambda):
         assert new.type == e.type or (is_collection(new.type)
                                       and is_collection(e.type)), repr(e)
     if self.debug and isinstance(e, Exp) and not isinstance(e, ELambda):
         model = satisfy(ENot(EBinOp(e, "===", new).with_type(BOOL)))
         if model is not None:
             raise Exception(
                 "bad simplification: {} ---> {} (under model {!r}, got {!r} and {!r})"
                 .format(pprint(e), pprint(new), model, eval(e, model),
                         eval(new, model)))
     return new
Exemplo n.º 5
0
def reachable_handles_by_type(root : Exp) -> {THandle:Exp}:
    """
    Compute a mapping from handle types to bags of all handle objects of that
    type reachable from the given root.

    Note that the bags may contain duplicate handles.  This can happen in two
    ways:
     - there is a bag of handles reachable from the root that contains
       duplicate handles, or
     - the same handle is reachable from the root via two different paths
    """
    if isinstance(root.type, THandle):
        return _merge(
            { root.type : ESingleton(root).with_type(TBag(root.type)) },
            reachable_handles_by_type(EGetField(root, "val").with_type(root.type.value_type)))
    elif is_collection(root.type):
        v = fresh_var(root.type.elem_type)
        res = reachable_handles_by_type(v)
        for k, bag in list(res.items()):
            res[k] = EFlatMap(root, ELambda(v, bag)).with_type(bag.type)
        return res
    elif isinstance(root.type, TTuple):
        res = OrderedDict()
        for i, t in enumerate(root.type.ts):
            res = _merge(res, reachable_handles_by_type(ETupleGet(root, i).with_type(t)))
        return res
    elif isinstance(root.type, TRecord):
        res = OrderedDict()
        for f, t in root.type.fields:
            res = _merge(res, reachable_handles_by_type(EGetField(root, f).with_type(t)))
        return res
    elif isinstance(root.type, TMap):
        raise NotImplementedError()
    else:
        return OrderedDict()
Exemplo n.º 6
0
def worst_case_cardinality(e: Exp) -> Polynomial:
    assert is_collection(e.type)
    while isinstance(e, EFilter) or isinstance(e, EMap) or isinstance(
            e, EFlatMap) or isinstance(e, EMakeMap2) or isinstance(
                e, EStateVar) or (isinstance(e, EUnaryOp)
                                  and e.op == UOp.Distinct) or isinstance(
                                      e, EListSlice):
        e = e.e
    if isinstance(e, EBinOp) and e.op == "-":
        return worst_case_cardinality(e.e1)
    if isinstance(e, EBinOp) and e.op == "+":
        return worst_case_cardinality(e.e1) + worst_case_cardinality(e.e2)
    if isinstance(e, EFlatMap):
        return worst_case_cardinality(e.e) * worst_case_cardinality(
            e.transform_function.body)
    if isinstance(e, ECond):
        return max(worst_case_cardinality(e.then_branch),
                   worst_case_cardinality(e.else_branch))
    if isinstance(e, EEmptyList):
        return Polynomial.ZERO
    if isinstance(e, ESingleton):
        return Polynomial.ONE
    if isinstance(e, EMapGet):
        try:
            return worst_case_cardinality(map_value_func(e.map).body)
        except NotImplementedError:
            print("WARNING: unable to peer inside map {}".format(pprint(
                e.map)))
            return Polynomial.N
    return Polynomial.N
Exemplo n.º 7
0
Arquivo: cxx.py Projeto: uwplse/cozy
 def visit_Query(self, q):
     if q.visibility != Visibility.Public:
         return ""
     ret_exp = q.ret
     ret_type = ret_exp.type
     if is_collection(ret_type):
         x = self.fv(ret_type.elem_type, "x")
         if q.docstring:
             self.write(indent_lines(q.docstring, self.get_indent()), "\n")
         self.begin_statement()
         self.write("template <class F>")
         self.end_statement()
         self.begin_statement()
         self.write("inline void ", q.name, "(")
         self.visit_args(itertools.chain(q.args, [("_callback", TNative("const F&"))]))
         self.write(") ")
         with self.block():
             self.visit(simplify_and_optimize(SForEach(x, ret_exp, SEscape("{indent}_callback({x});\n", ["x"], [x]))))
         self.end_statement()
     else:
         if q.docstring:
             self.write(indent_lines(q.docstring, self.get_indent()), "\n")
         self.begin_statement()
         self.write("inline ", self.visit(ret_type, ""), " ", q.name, "(")
         self.visit_args(q.args)
         self.write(") ")
         with self.block():
             self.visit(simplify_and_optimize(SReturn(ret_exp)))
         self.end_statement()
Exemplo n.º 8
0
def reachable_values_of_type(root: Exp, t: Type) -> Exp:
    """
    Find all values of the given type reachable from the given root.
    """
    if root.type == t:
        return ESingleton(root).with_type(TBag(t))
    elif is_collection(root.type):
        v = fresh_var(root.type.t)
        res = reachable_values_of_type(v, t)
        return MkFlatMap(root, ELambda(v, res))
    elif isinstance(root.type, THandle):
        return reachable_values_of_type(
            EGetField(root, "val").with_type(root.type.value_type), t)
    elif isinstance(root.type, TTuple):
        sub = [
            reachable_values_of_type(ETupleGet(root, i).with_type(tt), t)
            for (i, tt) in enumerate(root.type.ts)
        ]
        return EUnion(sub, t)
    elif isinstance(root.type, TRecord):
        sub = [
            reachable_values_of_type(EGetField(root, f).with_type(ft), t)
            for (f, ft) in root.type.fields
        ]
        return EUnion(sub, t)
    elif isinstance(root.type, TMap):
        raise NotImplementedError()
    else:
        return EEmptyList().with_type(TBag(t))
Exemplo n.º 9
0
Arquivo: core.py Projeto: uwplse/cozy
def should_consider_replacement(
        target         : Exp,
        target_context : Context,
        subexp         : Exp,
        subexp_context : Context,
        subexp_pool    : Pool,
        subexp_fp      : Fingerprint,
        replacement    : Exp,
        replacement_fp : Fingerprint) -> bool:
    """Heuristic that controls "blind" replacements.

    Besides replacing subexpressions with improved versions, Cozy also attempts
    "blind" replacements where the subexpression and the replacement do not
    behave exactly the same.  In some cases this can actually make a huge
    difference, for instance by replacing a collection with a singleton.

    However, not all blind replacements are worth trying.  This function
    controls which ones Cozy actually attempts.

    Preconditions:
     - subexp and replacement are both legal in (subexp_context, subexp_pool)
     - subexp and replacement have the same type
    """

    if not is_collection(subexp.type):
        return No("only collections matter")

    if not replacement_fp.subset_of(subexp_fp):
        return No("not a subset")

    return True
Exemplo n.º 10
0
def _uneval(t, value):
    if is_numeric(t):
        return ENum(value).with_type(t)
    elif t == BOOL:
        return EBool(value).with_type(t)
    elif is_collection(t):
        e = EEmptyList().with_type(t)
        for x in value:
            e = EBinOp(e, "+",
                       ESingleton(uneval(t.t, x)).with_type(t)).with_type(t)
        return e
    elif isinstance(t, TString):
        return EStr(value).with_type(t)
    elif isinstance(t, TTuple):
        return ETuple(tuple(uneval(tt, x)
                            for (tt, x) in zip(t.ts, value))).with_type(t)
    elif isinstance(t, TRecord):
        return EMakeRecord(
            tuple((f, uneval(tt, value[f]))
                  for (f, tt) in t.fields)).with_type(t)
    elif isinstance(t, TEnum):
        return EEnumEntry(value).with_type(t)
    elif isinstance(t, THandle):
        return EHandle(
            ENum(value.address).with_type(INT),
            uneval(t.value_type, value.value)).with_type(t)
    elif isinstance(t, TNative):
        return ENative(ENum(value[1]).with_type(INT)).with_type(t)
    else:
        raise NotImplementedError(pprint(t))
Exemplo n.º 11
0
 def visit_Query(self, q):
     if q.visibility != Visibility.Public:
         return ""
     ret_type = q.ret.type
     if is_collection(ret_type):
         x = EVar(self.fn("x")).with_type(ret_type.elem_type)
         def body(x):
             return SEscape("{indent}_callback.accept({x});\n", ["x"], [x])
         if q.docstring:
             self.write(indent_lines(q.docstring, self.get_indent()), "\n")
         self.begin_statement()
         self.write("public ", self.visit(TNative("void"), q.name), "(")
         self.visit_args(itertools.chain(q.args, [("_callback", TNative("java.util.function.Consumer<{t}>".format(t=self.visit(ret_type.elem_type, ""))))]))
         self.write(") ")
         with self.block():
             self.visit(simplify_and_optimize(SForEach(x, q.ret, SEscape("{indent}_callback.accept({x});\n", ["x"], [x]))))
     else:
         if q.docstring:
             self.write(indent_lines(q.docstring, self.get_indent()), "\n")
         self.begin_statement()
         self.write("public ", self.visit(ret_type, q.name), "(")
         self.visit_args(q.args)
         self.write(") ")
         with self.block():
             self.visit(simplify_and_optimize(SReturn(q.ret)))
     self.end_statement()
Exemplo n.º 12
0
def construct_value(t: Type) -> Exp:
    """
    Construct an arbitrary expression e of the given type.
    eval(construct_value(t), {}) == mkval(t)
    """
    if is_numeric(t):
        e = ENum(0)
    elif t == BOOL:
        e = F
    elif t == STRING:
        e = EStr("")
    elif is_collection(t):
        e = EEmptyList()
    elif isinstance(t, TTuple):
        e = ETuple(tuple(construct_value(tt) for tt in t.ts))
    elif isinstance(t, TRecord):
        e = EMakeRecord(tuple(
            (f, construct_value(tt)) for (f, tt) in t.fields))
    elif isinstance(t, TEnum):
        e = EEnumEntry(t.cases[0])
    elif isinstance(t, THandle):
        e = EHandle(construct_value(INT), construct_value(t.value_type))
    elif isinstance(t, TNative):
        e = ENative(construct_value(INT))
    elif isinstance(t, TMap):
        e = EMakeMap2(EEmptyList().with_type(TBag(t.k)),
                      ELambda(EVar("x").with_type(t.k), construct_value(t.v)))
    else:
        h = extension_handler(type(t))
        if h is not None:
            return h.default_value(t, construct_value)
        raise NotImplementedError(pprint(t))
    return e.with_type(t)
Exemplo n.º 13
0
def should_consider_replacement(target: Exp, target_context: Context,
                                subexp: Exp, subexp_context: Context,
                                subexp_pool: Pool, subexp_fp: Fingerprint,
                                replacement: Exp,
                                replacement_fp: Fingerprint) -> bool:
    """Heuristic that controls "blind" replacements.

    Besides replacing subexpressions with improved versions, Cozy also attempts
    "blind" replacements where the subexpression and the replacement do not
    behave exactly the same.  In some cases this can actually make a huge
    difference, for instance by replacing a collection with a singleton.

    However, not all blind replacements are worth trying.  This function
    controls which ones Cozy actually attempts.

    Preconditions:
     - subexp and replacement are both legal in (subexp_context, subexp_pool)
     - subexp and replacement have the same type
    """

    if not is_collection(subexp.type):
        return No("only collections matter")

    if not replacement_fp.subset_of(subexp_fp):
        return No("not a subset")

    return True
Exemplo n.º 14
0
def reachable_handles_by_type(root : Exp) -> {THandle:Exp}:
    """
    Compute a mapping from handle types to bags of all handle objects of that
    type reachable from the given root.

    Note that the bags may contain duplicate handles.  This can happen in two
    ways:
     - there is a bag of handles reachable from the root that contains
       duplicate handles, or
     - the same handle is reachable from the root via two different paths
    """
    if isinstance(root.type, THandle):
        return _merge(
            { root.type : ESingleton(root).with_type(TBag(root.type)) },
            reachable_handles_by_type(EGetField(root, "val").with_type(root.type.value_type)))
    elif is_collection(root.type):
        v = fresh_var(root.type.t)
        res = reachable_handles_by_type(v)
        for k, bag in list(res.items()):
            res[k] = EFlatMap(root, ELambda(v, bag)).with_type(bag.type)
        return res
    elif isinstance(root.type, TTuple):
        res = OrderedDict()
        for i, t in enumerate(root.type.ts):
            res = _merge(res, reachable_handles_by_type(ETupleGet(root, i).with_type(t)))
        return res
    elif isinstance(root.type, TRecord):
        res = OrderedDict()
        for f, t in root.type.fields:
            res = _merge(res, reachable_handles_by_type(EGetField(root, f).with_type(t)))
        return res
    elif isinstance(root.type, TMap):
        raise NotImplementedError()
    else:
        return OrderedDict()
Exemplo n.º 15
0
 def visit_Query(self, q):
     if q.visibility != Visibility.Public:
         return ""
     ret_exp = q.ret
     ret_type = ret_exp.type
     if is_collection(ret_type):
         x = self.fv(ret_type.elem_type, "x")
         if q.docstring:
             self.write(indent_lines(q.docstring, self.get_indent()), "\n")
         self.begin_statement()
         self.write("template <class F>")
         self.end_statement()
         self.begin_statement()
         self.write("inline void ", q.name, "(")
         self.visit_args(
             itertools.chain(q.args, [("_callback", TNative("const F&"))]))
         self.write(") ")
         with self.block():
             self.visit(
                 simplify_and_optimize(
                     SForEach(
                         x, ret_exp,
                         SEscape("{indent}_callback({x});\n", ["x"], [x]))))
         self.end_statement()
     else:
         if q.docstring:
             self.write(indent_lines(q.docstring, self.get_indent()), "\n")
         self.begin_statement()
         self.write("inline ", self.visit(ret_type, ""), " ", q.name, "(")
         self.visit_args(q.args)
         self.write(") ")
         with self.block():
             self.visit(simplify_and_optimize(SReturn(ret_exp)))
         self.end_statement()
Exemplo n.º 16
0
def construct_value(t : Type) -> Exp:
    """
    Construct an arbitrary expression e of the given type.
    eval(construct_value(t), {}) == mkval(t)
    """
    if is_numeric(t):
        e = ENum(0)
    elif t == BOOL:
        e = EFALSE
    elif t == STRING:
        e = EStr("")
    elif is_collection(t):
        e = EEmptyList()
    elif isinstance(t, TTuple):
        e = ETuple(tuple(construct_value(tt) for tt in t.ts))
    elif isinstance(t, TRecord):
        e = EMakeRecord(tuple((f, construct_value(tt)) for (f, tt) in t.fields))
    elif isinstance(t, TEnum):
        e = EEnumEntry(t.cases[0])
    elif isinstance(t, THandle):
        e = EHandle(construct_value(INT), construct_value(t.value_type))
    elif isinstance(t, TNative):
        e = ENative(construct_value(INT))
    elif isinstance(t, TMap):
        e = EMakeMap2(
            EEmptyList().with_type(TBag(t.k)),
            ELambda(EVar("x").with_type(t.k), construct_value(t.v)))
    else:
        h = extension_handler(type(t))
        if h is not None:
            return h.default_value(t, construct_value)
        raise NotImplementedError(pprint(t))
    return e.with_type(t)
Exemplo n.º 17
0
Arquivo: cxx.py Projeto: sanidhya/cozy
 def visit_Query(self, q, indent=""):
     if q.visibility != Visibility.Public:
         return ""
     ret_type = q.ret.type
     if is_collection(ret_type):
         x = EVar(self.fn("x")).with_type(ret_type.t)
         s = "{docstring}{indent}template <class F>\n".format(
             docstring=indent_lines(q.docstring, indent) +
             "\n" if q.docstring else "",
             indent=indent)
         s += "{indent}inline void {name} ({args}const F& _callback) const {{\n{body}  }}\n\n".format(
             indent=indent,
             name=q.name,
             args="".join("{}, ".format(self.visit(t, name))
                          for name, t in q.args),
             body=self.visit(SForEach(
                 x, q.ret, SEscape("{indent}_callback({x});\n", ["x"],
                                   [x])),
                             indent=indent + INDENT))
         return s
     else:
         body, out = self.visit(q.ret, indent + INDENT)
         return "{docstring}{indent}inline {type} {name} ({args}) const {{\n{body}    return {out};\n  }}\n\n".format(
             docstring=indent_lines(q.docstring, indent) +
             "\n" if q.docstring else "",
             indent=indent,
             type=self.visit(ret_type, ""),
             name=q.name,
             args=", ".join(self.visit(t, name) for name, t in q.args),
             out=out,
             body=body)
Exemplo n.º 18
0
def debug_comparison(cm : CostModel, e1 : Exp, e2 : Exp, context : Context):
    """Print information about the cost relationship of two expressions.

    This procedure gives a lot of insight into the relationship between e1 and
    e2 under the given cost model.
    """

    print("-" * 20)
    print("Comparing")
    print("  e1 = {}".format(pprint(e1)))
    print("  e2 = {}".format(pprint(e2)))
    print("  res = {}".format(cm.compare(e1, e2, context=context, pool=RUNTIME_POOL)))
    if is_collection(e1.type):
        print("worst_case_cardinality(e1) = {}".format(worst_case_cardinality(e1)))
    if is_collection(e2.type):
        print("worst_case_cardinality(e2) = {}".format(worst_case_cardinality(e2)))
    print("-" * 20 + " {} freebies...".format(len(cm.freebies)))
    for freebie in cm.freebies:
        print("  * {}".format(pprint(freebie)))
    print("-" * 20 + " {} ops...".format(len(cm.ops)))
    for o in cm.ops:
        print(pprint(o))
        for ename, e in [("e1", e1), ("e2", e2)]:
            print("maintenance_cost({e}) = {res}".format(e=ename, res=pprint(maintenance_cost(e, o))))

    print("-" * 20)
    for f in asymptotic_runtime, polynomial_runtime, max_storage_size, rt:
        for ename, e in [("e1", e1), ("e2", e2)]:
            res = f(e)
            print("{f}({e}) = {res}".format(f=f.__name__, e=ename, res=(pprint(res) if isinstance(res, Exp) else res)))

    print("-" * 20 + " {} examples...".format(len(cm.examples)))
    for x in cm.examples:
        print(x)

        for op in cm.ops:
            print(pprint(op))
            print("maintcost(e1) = {}".format(eval_bulk(maintenance_cost(e1, op), [x], use_default_values_for_undefined_vars=True)[0]))
            print("maintcost(e2) = {}".format(eval_bulk(maintenance_cost(e2, op), [x], use_default_values_for_undefined_vars=True)[0]))

        print("storage(e1) = {}".format(eval_bulk(max_storage_size(e1), [x], use_default_values_for_undefined_vars=True)[0]))
        print("storage(e2) = {}".format(eval_bulk(max_storage_size(e2), [x], use_default_values_for_undefined_vars=True)[0]))
        print("runtime(e1) = {}".format(eval_bulk(rt(e1), [x], use_default_values_for_undefined_vars=True)[0]))
        print("runtime(e2) = {}".format(eval_bulk(rt(e2), [x], use_default_values_for_undefined_vars=True)[0]))
        print("-" * 20)
Exemplo n.º 19
0
def polynomial_runtime(e: Exp) -> Polynomial:
    res = Polynomial.ZERO
    stk = [e]
    while stk:
        e = stk.pop()
        if isinstance(e, tuple) or isinstance(e, list):
            stk.extend(e)
            continue
        if not isinstance(e, Exp):
            continue
        if isinstance(e, ELambda):
            e = e.body
        if isinstance(e, EFilter):
            stk.append(e.e)
            res += worst_case_cardinality(e.e) * polynomial_runtime(
                e.predicate)
            continue
        if isinstance(e, EArgMin) or isinstance(e, EArgMax):
            stk.append(e.e)
            res += worst_case_cardinality(e.e) * polynomial_runtime(
                e.key_function)
            continue
        if isinstance(e, ESorted):
            stk.append(e.e)
            n = worst_case_cardinality(e.e)
            res += n * n
            continue
        if isinstance(e, EMap) or isinstance(e, EFlatMap):
            stk.append(e.e)
            res += worst_case_cardinality(e.e) * polynomial_runtime(
                e.transform_function)
            continue
        if isinstance(e, ELet):
            stk.append(e.e)
            stk.append(e.body_function.body)
            continue
        res += Polynomial.ONE
        if isinstance(e, EMakeMap2):
            res += worst_case_cardinality(e.e) * polynomial_runtime(
                e.value_function)
        if isinstance(e, EBinOp) and e.op == BOp.In:
            res += worst_case_cardinality(e.e2)
        if isinstance(e, EBinOp) and e.op == "-" and is_collection(e.type):
            res += worst_case_cardinality(e.e1) + worst_case_cardinality(
                e.e2) + worst_case_cardinality(e.e1) * worst_case_cardinality(
                    e.e2)
        if isinstance(e, EUnaryOp) and e.op in LINEAR_TIME_UOPS:
            res += worst_case_cardinality(e.e)
        if isinstance(e, ECond):
            res += max(polynomial_runtime(e.then_branch),
                       polynomial_runtime(e.else_branch))
            stk.append(e.cond)
            continue
        if isinstance(e, EStateVar):
            continue
        stk.extend(e.children())
    return res
Exemplo n.º 20
0
    def enumerate(self, context, size, pool, enumerate_subexps, enumerate_lambdas):
        from cozy.typecheck import is_collection

        if pool == STATE_POOL:
            for (sz1, sz2) in pick_to_sum(2, size-1):
                for e in enumerate_subexps(context, sz1, pool):
                    if is_collection(e.type):
                        elem_type = e.type.elem_type
                        yield EMakeMaxTreeMultiset(e).with_type(TMaxTreeMultiset(elem_type))
                        yield EMakeMinTreeMultiset(e).with_type(TMinTreeMultiset(elem_type))
Exemplo n.º 21
0
def storage_size(e, freebies: [Exp] = []):
    h = extension_handler(type(e.type))
    if h is not None:
        return h.storage_size(e, storage_size=storage_size)

    if e in freebies:
        return ZERO
    elif e.type == BOOL:
        return ONE
    elif is_numeric(e.type) or isinstance(e.type, THandle):
        return FOUR
    elif isinstance(e.type, TEnum):
        return TWO
    elif isinstance(e.type, TNative):
        return FOUR
    elif isinstance(e.type, TString):
        return TWENTY
    elif isinstance(e.type, TTuple):
        return ESum([
            storage_size(ETupleGet(e, n).with_type(t))
            for (n, t) in enumerate(e.type.ts)
        ])
    elif isinstance(e.type, TRecord):
        return ESum([
            storage_size(EGetField(e, f).with_type(t))
            for (f, t) in e.type.fields
        ])
    elif is_collection(e.type):
        v = fresh_var(e.type.t, omit=free_vars(e))
        return ESum([
            FOUR,
            EUnaryOp(UOp.Sum,
                     EMap(e, ELambda(
                         v,
                         storage_size(v))).with_type(INT_BAG)).with_type(INT)
        ])
    elif isinstance(e.type, TMap):
        k = fresh_var(e.type.k, omit=free_vars(e))
        return ESum([
            FOUR,
            EUnaryOp(
                UOp.Sum,
                EMap(
                    EMapKeys(e).with_type(TBag(e.type.k)),
                    ELambda(
                        k,
                        ESum([
                            storage_size(k),
                            storage_size(EMapGet(e, k).with_type(e.type.v))
                        ]))).with_type(INT_BAG)).with_type(INT)
        ])
    else:
        raise NotImplementedError(e.type)
Exemplo n.º 22
0
    def subset_of(self, other) -> bool:
        """Test for subset inclusion.

        If this returns True, then it could be the case that an expression with
        this fingerprint always returns a strict subset of the elements that
        would be returned by an expression with the other fingerprint.
        """
        if not is_collection(self.type):
            raise ValueError(
                "this fingerprint is not for a collection-type expression")
        if not is_collection(other.type):
            raise ValueError(
                "other fingerprint is not for a collection-type expression")
        self._require_comparable_to(other)
        x = EVar("x").with_type(self.type)
        y = EVar("y").with_type(other.type)
        is_subset = EIsSubset(x, y)
        return all(
            eval_bulk(is_subset, [{
                x.id: a,
                y.id: b
            } for (a, b) in zip(self.outputs, other.outputs)]))
Exemplo n.º 23
0
    def enumerate(self, context, size, pool, enumerate_subexps,
                  enumerate_lambdas):
        from cozy.typecheck import is_collection

        if pool == STATE_POOL:
            for (sz1, sz2) in pick_to_sum(2, size - 1):
                for e in enumerate_subexps(context, sz1, pool):
                    if is_collection(e.type):
                        elem_type = e.type.elem_type
                        yield EMakeMaxTreeMultiset(e).with_type(
                            TMaxTreeMultiset(elem_type))
                        yield EMakeMinTreeMultiset(e).with_type(
                            TMinTreeMultiset(elem_type))
Exemplo n.º 24
0
def break_plus_minus(e):
    for (_, x, r, _) in enumerate_fragments(e):
        if isinstance(x, EBinOp) and x.op in ("+", "-"):
            # print("accel --> {}".format(pprint(r(x.e1))))
            yield from break_plus_minus(r(x.e1))
            # print("accel --> {}".format(pprint(r(x.e2))))
            yield from break_plus_minus(r(x.e2))
            if e.type == INT or is_collection(e.type):
                ee = EBinOp(r(x.e1), x.op, r(x.e2)).with_type(e.type)
                if e.type == INT and x.op == "-":
                    ee.op = "+"
                    ee.e2 = EUnaryOp("-", ee.e2).with_type(ee.e2.type)
                yield ee
            return
    yield e
Exemplo n.º 25
0
 def visit_EBinOp(self, e):
     op = e.op
     if op == "+" and (isinstance(e.e1.type, TBag)
                       or isinstance(e.e1.type, TSet)):
         raise NotImplementedError("adding collections: {}".format(e))
     elif op == "==":
         return self._eq(e.e1, e.e2)
     elif op == "===":
         # rewrite deep-equality test into regular equality
         op = "=="
     elif op == "!=":
         return self.visit(ENot(EEq(e.e1, e.e2)))
     elif op == BOp.Or:
         return self.visit(ECond(e.e1, T, e.e2).with_type(BOOL))
     elif op == BOp.And:
         return self.visit(ECond(e.e1, e.e2, F).with_type(BOOL))
     elif op == "-" and is_collection(e.type):
         t = e.type
         v = self.fv(t, "v")
         x = self.fv(t.t, "x")
         self.declare(v, e.e1)
         self.visit(SForEach(x, e.e2, SCall(v, "remove", [x])))
         return v.id
     elif op == BOp.In:
         if isinstance(e.e2.type, TSet):
             return self.test_set_containment_native(e.e2, e.e1)
         else:
             t = BOOL
             res = self.fv(t, "found")
             x = self.fv(e.e1.type, "x")
             label = fresh_name("label")
             self.visit(
                 seq([
                     SDecl(res.id, F),
                     SEscapableBlock(
                         label,
                         SForEach(
                             x, e.e2,
                             SIf(
                                 EBinOp(x, "==", e.e1).with_type(BOOL),
                                 seq([SAssign(res, T),
                                      SEscapeBlock(label)]), SNoOp())))
                 ]))
             return res.id
     return "({e1} {op} {e2})".format(e1=self.visit(e.e1),
                                      op=op,
                                      e2=self.visit(e.e2))
Exemplo n.º 26
0
 def visit_EBinOp(self, e):
     if e.op in ("+", "-") and is_collection(e.type):
         return self.visit_iterable(e)
     elif e.op == BOp.In and not isinstance(e.e2.type, TSet):
         t = BOOL
         res = fresh_var(t, "found")
         x = fresh_var(e.e1.type, "x")
         label = fresh_name("label")
         self.stms.append(simplify_and_optimize(seq([
             SDecl(res, EFALSE),
             SEscapableBlock(label,
                 SForEach(x, e.e2, SIf(
                     EBinOp(x, "==", e.e1).with_type(BOOL),
                     seq([SAssign(res, ETRUE), SEscapeBlock(label)]),
                     SNoOp())))])))
         return res
     return self.visit_Exp(e)
Exemplo n.º 27
0
def asymptotic_runtime(e: Exp) -> DominantTerm:
    res = DominantTerm.ZERO
    stk = [e]
    while stk:
        e = stk.pop()
        if isinstance(e, tuple) or isinstance(e, list):
            stk.extend(e)
            continue
        if not isinstance(e, Exp):
            continue
        if isinstance(e, ELambda):
            e = e.body
        if isinstance(e, EFilter):
            res += worst_case_cardinality(e.e) * asymptotic_runtime(
                e.p) + asymptotic_runtime(e.e)
            continue
        if isinstance(e, EMap) or isinstance(e, EFlatMap) or isinstance(
                e, EArgMin) or isinstance(e, EArgMax):
            res += worst_case_cardinality(e.e) * asymptotic_runtime(
                e.f) + asymptotic_runtime(e.e)
            continue
        res += DominantTerm.ONE
        if isinstance(e, EMakeMap2):
            res += worst_case_cardinality(e.e) * asymptotic_runtime(e.value)
        if isinstance(e, EBinOp) and e.op == BOp.In:
            res += worst_case_cardinality(e.e2)
        if isinstance(e, EBinOp) and e.op == "-" and is_collection(e.type):
            res += worst_case_cardinality(e.e1) + worst_case_cardinality(
                e.e2) + worst_case_cardinality(e.e1) * worst_case_cardinality(
                    e.e2)
        if isinstance(e, EUnaryOp) and e.op in LINEAR_TIME_UOPS:
            res += worst_case_cardinality(e.e)
        if isinstance(e, ECond):
            res += max(asymptotic_runtime(e.then_branch),
                       asymptotic_runtime(e.else_branch))
            stk.append(e.cond)
            continue
        if isinstance(e, EStateVar):
            continue
        stk.extend(e.children())
    if res.exponent == 0:
        return DominantTerm.ONE
    return res
Exemplo n.º 28
0
def asymptotic_runtime(e: Exp) -> int:
    res = 0
    stk = [e]
    while stk:
        e = stk.pop()
        if isinstance(e, tuple) or isinstance(e, list):
            stk.extend(e)
            continue
        if not isinstance(e, Exp):
            continue
        if isinstance(e, ELambda):
            e = e.body
        if isinstance(e, EFilter):
            res += max(
                wc_card(e.e) * asymptotic_runtime(e.p),
                asymptotic_runtime(e.e))
            continue
        if isinstance(e, EMap) or isinstance(e, EFlatMap) or isinstance(
                e, EArgMin) or isinstance(e, EArgMax):
            res += max(
                wc_card(e.e) * asymptotic_runtime(e.f),
                asymptotic_runtime(e.e))
            continue
        if isinstance(e, EMakeMap2):
            res += wc_card(e.e) * asymptotic_runtime(e.value)
        if isinstance(e, EBinOp) and e.op == BOp.In:
            res += wc_card(e.e2)
        if isinstance(e, EBinOp) and e.op == "-" and is_collection(e.type):
            res += wc_card(e.e1) + wc_card(
                e.e2) + wc_card(e.e1) * wc_card(e.e2)
        if isinstance(e, EUnaryOp) and e.op in LINEAR_TIME_UOPS:
            res += wc_card(e.e)
        if isinstance(e, ECond):
            res += max(asymptotic_runtime(e.then_branch),
                       asymptotic_runtime(e.else_branch))
            stk.append(e.cond)
            continue
        if isinstance(e, EStateVar):
            continue
        stk.extend(e.children())
    return max(res, 1)
Exemplo n.º 29
0
def wc_card(e):
    assert is_collection(e.type)
    while isinstance(e, EFilter) or isinstance(e, EMap) or isinstance(
            e, EFlatMap) or isinstance(e, EArgMin) or isinstance(
                e, EArgMax) or isinstance(e, EMakeMap2) or isinstance(
                    e, EStateVar) or (isinstance(e, EUnaryOp)
                                      and e.op == UOp.Distinct):
        e = e.e
    if isinstance(e, EBinOp) and e.op == "-":
        return wc_card(e.e1)
    if isinstance(e, EBinOp) and e.op == "+":
        return max(wc_card(e.e1), wc_card(e.e2))
    if isinstance(e, EFlatMap):
        return wc_card(e.e) * wc_card(e.f.body)
    if isinstance(e, ECond):
        return max(wc_card(e.then_branch), wc_card(e.else_branch))
    if isinstance(e, EEmptyList):
        return 0
    if isinstance(e, ESingleton):
        return 1
    return EXTREME_COST
Exemplo n.º 30
0
def break_bag(e):
    assert is_collection(e.type)
    if isinstance(e, EBinOp):
        if e.op == "+":
            yield from break_bag(e.e1)
            yield from break_bag(e.e2)
        else:
            assert e.op == "-"
            yield from break_bag(e.e1)
            for pos, x in break_bag(e.e2):
                yield (not pos, x)
    elif isinstance(e, EMap):
        for pos, x in break_bag(e.e):
            yield pos, EMap(x, e.f).with_type(e.type)
    elif isinstance(e, EFilter):
        for pos, x in break_bag(e.e):
            yield pos, EFilter(x, e.p).with_type(e.type)
    # elif isinstance(e, EStateVar):
    #     yield from break_bag(e.e)
    else:
        yield True, e
Exemplo n.º 31
0
def cardinality(e : Exp, cache : { Exp : EVar }, plus_one=False) -> Exp:
    assert is_collection(e.type)
    # if plus_one:
    #     return ESum((self.cardinality(e, plus_one=False), ONE))
    if isinstance(e, EEmptyList):
        return ZERO
    if isinstance(e, ESingleton):
        return ONE
    if isinstance(e, EBinOp) and e.op == "+":
        return ESum((cardinality(e.e1, cache), cardinality(e.e2, cache)))
    if isinstance(e, EMap):
        return cardinality(e.e, cache)
    if isinstance(e, EStateVar):
        return cardinality(e.e, cache)
    prev = cache.get(e)
    if prev is not None:
        return prev
    else:
        v = fresh_var(INT)
        cache[e] = v
        # if isinstance(e, EFilter):
        #     cc = self.cardinality(e.e)
        #     self.assumptions.append(EBinOp(v, "<=", cc).with_type(BOOL))
        #     # heuristic: (xs) large implies (filter_p xs) large
        #     self.assumptions.append(EBinOp(
        #         EBinOp(v,  "*", ENum(5).with_type(INT)).with_type(INT), ">=",
        #         EBinOp(cc, "*", ENum(4).with_type(INT)).with_type(INT)).with_type(BOOL))
        # if isinstance(e, EUnaryOp) and e.op == UOp.Distinct:
        #     cc = self.cardinality(e.e)
        #     self.assumptions.append(EBinOp(v, "<=", cc).with_type(BOOL))
        #     # self.assumptions.append(EImplies(EGt(cc, ZERO), EGt(v, ZERO)))
        #     # heuristic: (xs) large implies (distinct xs) large
        #     self.assumptions.append(EBinOp(
        #         EBinOp(v,  "*", ENum(5).with_type(INT)).with_type(INT), ">=",
        #         EBinOp(cc, "*", ENum(4).with_type(INT)).with_type(INT)).with_type(BOOL))
        # if isinstance(e, EBinOp) and e.op == "-":
        #     self.assumptions.append(EBinOp(v, "<=", self.cardinality(e.e1)).with_type(BOOL))
        # if isinstance(e, ECond):
        #     self.assumptions.append(EAny([EEq(v, self.cardinality(e.then_branch)), EEq(v, self.cardinality(e.else_branch))]))
        return v
Exemplo n.º 32
0
    def enumerate(self, context, size, pool, enumerate_subexps, enumerate_lambdas):
        from cozy.typecheck import is_ordered, is_collection

        if pool == STATE_POOL:
            for (sz1, sz2) in pick_to_sum(2, size-1):
                for e in enumerate_subexps(context, sz1, pool):
                    if is_collection(e.type):
                        elem_type = e.type.elem_type
                        for keyfunc in enumerate_lambdas(e, pool, sz2):
                            key_type = keyfunc.body.type
                            if is_ordered(key_type):
                                yield EMakeMinHeap(e, keyfunc).with_type(TMinHeap(elem_type, key_type))
                                yield EMakeMaxHeap(e, keyfunc).with_type(TMaxHeap(elem_type, key_type))

        elif pool == RUNTIME_POOL:
            for e in enumerate_subexps(context.root(), size-1, STATE_POOL):
                t = e.type
                if isinstance(t, TMinHeap) or isinstance(t, TMaxHeap):
                    elem_type = t.elem_type
                    # yielding EHeapElems would be redundant
                    yield EHeapPeek (EStateVar(e).with_type(e.type)).with_type(elem_type)
                    yield EHeapPeek2(EStateVar(e).with_type(e.type)).with_type(elem_type)
Exemplo n.º 33
0
def _uneval(t, value):
    if is_numeric(t):
        return ENum(value).with_type(t)
    elif t == BOOL:
        return EBool(value).with_type(t)
    elif is_collection(t):
        e = EEmptyList().with_type(t)
        for x in value:
            e = EBinOp(e, "+", ESingleton(uneval(t.elem_type, x)).with_type(t)).with_type(t)
        return e
    elif isinstance(t, TString):
        return EStr(value).with_type(t)
    elif isinstance(t, TTuple):
        return ETuple(tuple(uneval(tt, x) for (tt, x) in zip(t.ts, value))).with_type(t)
    elif isinstance(t, TRecord):
        return EMakeRecord(tuple((f, uneval(tt, value[f])) for (f, tt) in t.fields)).with_type(t)
    elif isinstance(t, TEnum):
        return EEnumEntry(value).with_type(t)
    elif isinstance(t, THandle):
        return EHandle(ENum(value.address).with_type(INT), uneval(t.value_type, value.value)).with_type(t)
    elif isinstance(t, TNative):
        return ENative(ENum(value[1]).with_type(INT)).with_type(t)
    else:
        raise NotImplementedError(pprint(t))
Exemplo n.º 34
0
def worst_case_cardinality(e: Exp) -> DominantTerm:
    assert is_collection(e.type)
    while isinstance(e, EFilter) or isinstance(e, EMap) or isinstance(
            e, EFlatMap) or isinstance(e, EMakeMap2) or isinstance(
                e, EStateVar) or (isinstance(e, EUnaryOp)
                                  and e.op == UOp.Distinct) or isinstance(
                                      e, EListSlice):
        e = e.e
    if isinstance(e, EBinOp) and e.op == "-":
        return worst_case_cardinality(e.e1)
    if isinstance(e, EBinOp) and e.op == "+":
        return worst_case_cardinality(e.e1) + worst_case_cardinality(e.e2)
    if isinstance(e, EFlatMap):
        return worst_case_cardinality(e.e) * worst_case_cardinality(e.f.body)
    if isinstance(e, ECond):
        return max(worst_case_cardinality(e.then_branch),
                   worst_case_cardinality(e.else_branch))
    if isinstance(e, EEmptyList):
        return DominantTerm.ZERO
    if isinstance(e, ESingleton):
        return DominantTerm.ONE
    if isinstance(e, EMapGet):
        return worst_case_cardinality(map_value_func(e.map).body)
    return DominantTerm.N
Exemplo n.º 35
0
Arquivo: core.py Projeto: uwplse/cozy
def possibly_useful_nonrecursive(solver, e : Exp, context : Context, pool = RUNTIME_POOL, assumptions : Exp = ETRUE, ops : [Op] = ()) -> bool:
    """Heuristic filter to ignore expressions that are almost certainly useless."""

    state_vars  = OrderedSet(v for v, p in context.vars() if p == STATE_POOL)
    args        = OrderedSet(v for v, p in context.vars() if p == RUNTIME_POOL)
    assumptions = EAll([assumptions, context.path_condition()])
    at_runtime  = pool == RUNTIME_POOL

    h = extension_handler(type(e))
    if h is not None:
        res = h.possibly_useful(e, context, pool, assumptions, ops, solver)
        if not res:
            return res

    if isinstance(e, EStateVar) and not free_vars(e.e):
        return No("constant value in state position")
    if (isinstance(e, EDropFront) or isinstance(e, EDropBack)) and not at_runtime:
        return No("EDrop* in state position")
    if not allow_big_sets.value and isinstance(e, EFlatMap) and not at_runtime:
        return No("EFlatMap in state position")
    if not allow_int_arithmetic_state.value and not at_runtime and isinstance(e, EBinOp) and e.type == INT:
        return No("integer arithmetic in state position")
    if is_collection(e.type) and not is_scalar(e.type.elem_type):
        return No("collection of nonscalar: e {}\n elem_type: {}\n".format(e, e.type.elem_type))
    if isinstance(e.type, TMap) and not is_scalar(e.type.k):
        return No("bad key type {}".format(pprint(e.type.k)))
    if isinstance(e.type, TMap) and isinstance(e.type.v, TMap):
        return No("map to map")
    # This check is probably a bad idea: whether `the` is legal may depend on
    # the contex that the expression is embedded within, so we can't skip it
    # during synthesis just because it looks invalid now.
    # if isinstance(e, EUnaryOp) and e.op == UOp.The:
    #     len = EUnaryOp(UOp.Length, e.e).with_type(INT)
    #     if not valid(EImplies(assumptions, EBinOp(len, "<=", ENum(1).with_type(INT)).with_type(BOOL))):
    #         return No("illegal application of 'the': could have >1 elems")
    if not at_runtime and isinstance(e, EBinOp) and e.op == "-" and is_collection(e.type):
        return No("collection subtraction in state position")
    # if not at_runtime and isinstance(e, ESingleton):
    #     return No("singleton in state position")
    if not allow_nonzero_state_constants.value and not at_runtime and isinstance(e, ENum) and e.val != 0:
        return No("nonzero integer constant in state position")
    if not allow_binop_state.value and at_runtime and isinstance(e, EStateVar) and isinstance(e.e, EBinOp) and is_scalar(e.e.e1.type) and is_scalar(e.e.e2.type):
        return No("constant-time binary operator {!r} in state position".format(e.e.op))
    if not allow_conditional_state.value and not at_runtime and isinstance(e, ECond):
        return No("conditional in state position")
    if isinstance(e, EMakeMap2) and isinstance(e.e, EEmptyList):
        return No("trivially empty map")
    if isinstance(e, EMakeMap2) and isinstance(e.e, ESingleton):
        return No("really tiny map")
    if not at_runtime and (isinstance(e, EArgMin) or isinstance(e, EArgMax)):
        # Cozy has no way to efficiently implement mins/maxes when more than
        # one element may leave the collection.
        from cozy.state_maintenance import mutate
        for op in ops:
            elems = e.e
            elems_prime = mutate(elems, op.body)
            formula = EAll([assumptions] + list(op.assumptions) + [EGt(ELen(EBinOp(elems, "-", elems_prime).with_type(elems.type)), ONE)])
            if solver.satisfiable(formula):
                return No("more than one element might be removed during {}".format(op.name))
    if not allow_peels.value and not at_runtime and isinstance(e, EFilter):
        # catch "peels": removal of zero or one elements
        if solver.valid(EImplies(assumptions, ELe(ELen(EFilter(e.e, ELambda(e.predicate.arg, ENot(e.predicate.body))).with_type(e.type)), ONE))):
            return No("filter is a peel")
    if not allow_big_maps.value and not at_runtime and isinstance(e, EMakeMap2) and is_collection(e.type.v):
        all_collections = [sv for sv in state_vars if is_collection(sv.type)]
        total_size = ENum(0).with_type(INT)
        for c in all_collections:
            total_size = EBinOp(total_size, "+", EUnaryOp(UOp.Length, c).with_type(INT)).with_type(INT)
        my_size = EUnaryOp(UOp.Length, EFlatMap(EUnaryOp(UOp.Distinct, e.e).with_type(e.e.type), e.value_function).with_type(e.type.v)).with_type(INT)
        s = EImplies(
            assumptions,
            EBinOp(total_size, ">=", my_size).with_type(BOOL))
        if not solver.valid(s):
            return No("non-polynomial-sized map")

    return True
Exemplo n.º 36
0
def _try_optimize(e : Exp, context : Context, pool : Pool):
    if not accelerate.value:
        return

    if pool != RUNTIME_POOL:
        return

    state_vars = [v for v, p in context.vars() if p == STATE_POOL]
    args = [v for v, p in context.vars() if p == RUNTIME_POOL]

    # ---------------------------------------------------------------------
    # "Rewrite schemes": these trigger on many different AST shapes
    # They are listed first because they are more powerful than the
    # specific rewrite rules below.

    if not free_vars(e) and not free_funcs(e):
        try:
            yield _check(uneval(e.type, eval(e, {})), context, RUNTIME_POOL)
        except NotImplementedError:
            print("Unable to evaluate {!r}".format(e))

    if all(v in state_vars for v in free_vars(e)):
        nsv = strip_EStateVar(e)
        sv = EStateVar(nsv).with_type(e.type)
        yield _check(sv, context, RUNTIME_POOL)

    for ee in fold_into_map(e, context):
        yield _check(ee, context, pool)

    # ---------------------------------------------------------------------
    # "Rewrites": these trigger on specific AST nodes

    if isinstance(e, EBinOp):

        if e.op == "-" and is_collection(e.type):
            ee = optimized_bag_difference(e.e1, e.e2)
            yield _check(ee, context, RUNTIME_POOL)

        if e.op == "===" and isinstance(e.e1.type, THandle):
            yield _check(EAll([
                optimized_eq(optimized_addr(e.e1), optimized_addr(e.e2)),
                optimized_eq(optimized_val(e.e1),  optimized_val(e.e2)).with_type(BOOL)]), context, RUNTIME_POOL)

        if e.op == BOp.In:
            ee = optimized_in(e.e1, e.e2)
            yield _check(ee, context, RUNTIME_POOL)

    if isinstance(e, ECond):
        yield _check(optimized_cond(e.cond, e.then_branch, e.else_branch), context, RUNTIME_POOL)

    if isinstance(e, EGetField):
        for ee in optimized_get_field(e.e, e.field_name, args):
            yield _check(ee, context, RUNTIME_POOL)

    if isinstance(e, EListGet) and e.index == ZERO:
        for res in optimized_the(e.e, args):
            yield _check(res, context, RUNTIME_POOL)

    if isinstance(e, EListGet) and isinstance(e.e, ECond):
        yield optimized_cond(e.e.cond,
                             EListGet(e.e.then_branch, e.index).with_type(e.type),
                             EListGet(e.e.else_branch, e.index).with_type(e.type))

    from cozy.structures.treemultiset import ETreeMultisetElems, ETreeMultisetPeek
    if isinstance(e, EListGet) and isinstance(e.e, ETreeMultisetElems):
        yield ETreeMultisetPeek(e.e.e, e.index).with_type(e.type)

    if isinstance(e, EMapGet):
        ee = inline_mapget(e, context)
        yield _check(ee, context, RUNTIME_POOL)

    if isinstance(e, EUnaryOp):

        if e.op == UOp.Sum:
            for ee in optimized_sum(e.e, args):
                yield _check(ee, context, RUNTIME_POOL)

        if e.op == UOp.Length:
            ee = optimized_len(e.e)
            yield _check(ee, context, RUNTIME_POOL)

        if e.op == UOp.Empty:
            ee = optimized_empty(e.e)
            yield _check(ee, context, RUNTIME_POOL)

        if e.op == UOp.Exists:
            ee = optimized_exists(e.e)
            yield _check(ee, context, RUNTIME_POOL)

        if e.op == UOp.Distinct:
            for ee in optimized_distinct(e.e, args):
                yield _check(ee, context, RUNTIME_POOL)

        if e.op == UOp.The:
            for ee in optimized_the(e.e, args):
                yield _check(ee, context, RUNTIME_POOL)

    if isinstance(e, EArgMin) or isinstance(e, EArgMax):
        for ee in optimized_best(e.e, e.key_function, "<" if isinstance(e, EArgMin) else ">", args=args):
            yield _check(ee, context, RUNTIME_POOL)

    if isinstance(e, EFilter):
        for ee in optimized_filter(e.e, e.predicate, args=args):
            yield _check(ee, context, RUNTIME_POOL)

    if isinstance(e, EMap):
        for ee in optimized_map(e.e, e.transform_function, args=args):
            yield _check(ee, context, RUNTIME_POOL)
    from cozy.syntax import ESorted
    from cozy.structures.treemultiset import EMakeMaxTreeMultiset, TMaxTreeMultiset, EMakeMinTreeMultiset, TMinTreeMultiset, ETreeMultisetElems
    target = e
    if isinstance(target, ESorted) and isinstance(target.e, EStateVar):
        e_max = EMakeMaxTreeMultiset(target.e.e).with_type(TMaxTreeMultiset(target.e.e.type.elem_type))
        e_min = EMakeMinTreeMultiset(target.e.e).with_type(TMinTreeMultiset(target.e.e.type.elem_type))
        ee = optimized_cond(target.asc,
                            ETreeMultisetElems(EStateVar(e_min).with_type(e_min.type)).with_type(target.type),
                            ETreeMultisetElems(EStateVar(e_max).with_type(e_max.type)).with_type(target.type))
        yield _check(ee, context, RUNTIME_POOL)
Exemplo n.º 37
0
def _compile(e, env: {str: int}, out):
    if isinstance(e, EVar):
        i = env[e.id]
        if isinstance(i, int):

            def load_var(stk):
                stk.append(stk[i])

            out.append(load_var)
        else:

            def load_bound(stk):
                stk.append(i())

            out.append(load_bound)
    elif isinstance(e, EBool):
        out.append(push_true if e.val else push_false)
    elif isinstance(e, ENum):
        s = e.val

        def push_num(stk):
            stk.append(s)

        out.append(push_num)
    elif isinstance(e, EStr):
        s = e.val

        def push_str(stk):
            stk.append(s)

        out.append(push_str)
    elif isinstance(e, EEnumEntry):
        s = e.name

        def push_enum(stk):
            stk.append(s)

        out.append(push_enum)
    elif isinstance(e, EEmptyList):

        def push_empty_list(stk):
            stk.append(_EMPTY_BAG)

        out.append(push_empty_list)
    elif isinstance(e, ESingleton):
        _compile(e.e, env, out)
        if isinstance(e.type, TList):
            out.append(make_singleton_list)
        else:
            out.append(make_singleton_bag)
    elif isinstance(e, EHandle):
        _compile(e.addr, env, out)
        _compile(e.value, env, out)
        out.append(make_handle)
    elif isinstance(e, ENull):
        out.append(push_null)
    elif isinstance(e, ECond):
        _compile(e.cond, env, out)
        then_code = []
        _compile(e.then_branch, env, then_code)
        else_code = []
        _compile(e.else_branch, env, else_code)

        def ite(stk):
            return then_code if stk.pop() else else_code

        out.append(ite)
    elif isinstance(e, EMakeRecord):
        for (f, ee) in e.fields:
            _compile(ee, env, out)

        def make_record(stk):
            stk.append(
                FrozenDict((f, stk.pop()) for (f, _) in reversed(e.fields)))

        out.append(make_record)
    elif isinstance(e, EGetField):
        _compile(e.e, env, out)
        if isinstance(e.e.type, THandle):
            assert e.f == "val"
            out.append(get_handle_value)
        else:
            assert isinstance(e.e.type, TRecord)
            f = e.f

            def get_field(stk):
                stk.append(stk.pop()[f])

            out.append(get_field)
    elif isinstance(e, ETuple):
        n = len(e.es)
        for ee in e.es:
            _compile(ee, env, out)

        def make_tuple(stk):
            entries = reversed([stk.pop() for i in range(n)])
            stk.append(tuple(entries))

        out.append(make_tuple)
    elif isinstance(e, ETupleGet):
        _compile(e.e, env, out)

        def tuple_get(stk):
            stk.append(stk.pop()[e.n])

        out.append(tuple_get)
    elif isinstance(e, EStateVar):
        _compile(e.e, env, out)
    elif isinstance(e, ENative):
        _compile(e.e, env, out)

        def make_native(stk):
            stk.append((e.type.name, stk.pop()))

        out.append(make_native)
    elif isinstance(e, EUnaryOp):
        _compile(e.e, env, out)
        if e.op == UOp.Not:
            out.append(unaryop_not)
        elif e.op == UOp.Sum:
            out.append(unaryop_sum)
        elif e.op == UOp.Exists:
            out.append(unaryop_exists)
        elif e.op == UOp.Empty:
            out.append(unaryop_empty)
        elif e.op == UOp.All:
            out.append(unaryop_all)
        elif e.op == UOp.Any:
            out.append(unaryop_any)
        elif e.op == UOp.Length:
            out.append(unaryop_len)
        elif e.op == UOp.AreUnique:
            out.append(unaryop_areunique(e.e.type.t))
        elif e.op == UOp.Distinct:
            out.append(unaryop_distinct(e.e.type.t))
        elif e.op == UOp.The:
            out.append(unaryop_the(default=mkval(e.type)))
        elif e.op == UOp.Reversed:
            out.append(unaryop_reversed)
        elif e.op == "-":
            out.append(unaryop_neg)
        else:
            raise NotImplementedError(e.op)
    elif isinstance(e, EBinOp):
        if e.op == BOp.And:
            return _compile(ECond(e.e1, e.e2, F).with_type(BOOL), env, out)
        elif e.op == BOp.Or:
            return _compile(ECond(e.e1, T, e.e2).with_type(BOOL), env, out)
        elif e.op == "=>":
            return _compile(ECond(e.e1, e.e2, T).with_type(BOOL), env, out)
        _compile(e.e1, env, out)
        _compile(e.e2, env, out)
        e1type = e.e1.type
        if e.op == "+":
            if is_collection(e.type):
                out.append(binaryop_add_collections)
            else:
                out.append(binaryop_add_numbers)
        elif e.op == "*":
            out.append(binaryop_mul)
        elif e.op == "-":
            if isinstance(e.type, TBag) or isinstance(e.type, TSet):
                out.append(binaryop_sub_bags(e.type.t))
            elif isinstance(e.type, TList):
                out.append(binaryop_sub_lists(e.type.t))
            else:
                out.append(binaryop_sub)
        elif e.op == "==":
            out.append(binaryop_eq(e1type))
        elif e.op == "===":
            out.append(binaryop_eq(e1type, deep=True))
        elif e.op == "<":
            out.append(binaryop_lt(e1type))
        elif e.op == ">":
            out.append(binaryop_gt(e1type))
        elif e.op == "<=":
            out.append(binaryop_le(e1type))
        elif e.op == ">=":
            out.append(binaryop_ge(e1type))
        elif e.op == "!=":
            out.append(binaryop_ne(e1type))
        elif e.op == BOp.In:
            out.append(binaryop_in(e1type))
        else:
            raise NotImplementedError(e.op)
    elif isinstance(e, EListGet):
        _compile(e.e, env, out)
        _compile(e.index, env, out)
        out.append(list_index(mkval(e.type)))
    elif isinstance(e, EListSlice):
        _compile(e.e, env, out)
        _compile(e.start, env, out)
        _compile(e.end, env, out)
        out.append(list_slice)
    elif isinstance(e, EDropFront):
        _compile(e.e, env, out)
        out.append(drop_front)
    elif isinstance(e, EDropBack):
        _compile(e.e, env, out)
        out.append(drop_back)
    elif isinstance(e, EFilter):
        _compile(e.e, env, out)
        box = [None]
        body = []
        with extend(env, e.p.arg.id, lambda: box[0]):
            _compile(e.p.body, env, body)

        def set_arg(v):
            def set_arg(stk):
                box[0] = v

            return set_arg

        def maybe_append_to_result(idx):
            return lambda stk: (stk[idx].append(box[0]) if stk.pop() else None)

        def do_filter(stk):
            bag = stk.pop()
            res_idx = len(stk)
            stk.append([])
            ops = []
            for (i, val) in enumerate(bag):
                ops.append(set_arg(val))
                ops.extend(body)
                ops.append(maybe_append_to_result(res_idx))
            return ops

        out.append(do_filter)
        out.append(iterable_to_bag)
    elif isinstance(e, EMap):
        _compile(e.e, env, out)
        box = [None]
        body = []
        with extend(env, e.f.arg.id, lambda: box[0]):
            _compile(e.f.body, env, body)

        def set_arg(v):
            def set_arg(stk):
                box[0] = v

            return set_arg

        def append_to_result(idx):
            return lambda stk: stk[idx].append(stk.pop())

        def do_map(stk):
            bag = stk.pop()
            res_idx = len(stk)
            stk.append([])
            ops = []
            for (i, val) in enumerate(bag):
                ops.append(set_arg(val))
                ops.extend(body)
                ops.append(append_to_result(res_idx))
            return ops

        out.append(do_map)
        out.append(iterable_to_bag)
    elif isinstance(e, EFlatMap):
        _compile(EMap(e.e, e.f).with_type(TBag(e.type)), env, out)
        out.append(do_concat)
    elif isinstance(e, EArgMin) or isinstance(e, EArgMax):
        # stack layout:
        #   len | f(best) | best | elem_0 | ... | elem_len

        # body is a seq. of opcodes that has the effect of pushing
        # f(top_of_stack) onto the stack, leaving the old top underneath
        box = [None]

        def set_arg(stk):
            box[0] = stk[-1]

        body = [set_arg]
        with extend(env, e.f.arg.id, lambda: box[0]):
            _compile(e.f.body, env, body)

        keytype = e.f.body.type

        def initialize(stk):
            bag = stk.pop()
            if bag:
                stk.extend(reversed(bag))
            else:
                stk.append(mkval(e.type))
            return body + [push(len(bag) - 1)]

        do_cmp = binaryop_lt(keytype) if isinstance(
            e, EArgMin) else binaryop_gt(keytype)

        def loop(stk):
            len = stk.pop()
            key = stk.pop()
            if len > 0:
                best = stk.pop()
                return body + [
                    dup,
                    push(key), do_cmp,
                    if_then_else(
                        [], [drop, drop, push(best),
                             push(key)]),
                    push(len - 1), loop
                ]

        _compile(e.e, env, out)
        out.append(initialize)
        out.append(loop)
    elif isinstance(e, EMakeMap2):
        _compile(
            EMap(
                e.e,
                ELambda(
                    e.value.arg,
                    ETuple((e.value.arg, e.value.body)).with_type(
                        TTuple((e.value.arg.type,
                                e.value.body.type))))).with_type(
                                    TBag(
                                        TTuple((e.value.arg.type,
                                                e.value.body.type)))), env,
            out)
        default = mkval(e.type.v)

        def make_map(stk):
            res = Map(e.type, default)
            for (k, v) in reversed(list(stk.pop())):
                res[k] = v
            stk.append(res)

        out.append(make_map)
    elif isinstance(e, EMapGet):
        _compile(e.map, env, out)
        _compile(e.key, env, out)
        out.append(read_map)
    elif isinstance(e, EHasKey):
        _compile(e.map, env, out)
        _compile(e.key, env, out)
        out.append(has_key(e.key.type))
    elif isinstance(e, EMapKeys):
        _compile(e.e, env, out)
        out.append(read_map_keys)
    elif isinstance(e, ECall):
        _compile(EVar(e.func), env, out)
        for a in e.args:
            _compile(a, env, out)
        n = len(e.args)

        def call(stk):
            args = reversed([stk.pop() for i in range(n)])
            f = stk.pop()
            stk.append(f(*args))

        out.append(call)
    elif isinstance(e, ELet):
        _compile(e.e, env, out)
        box = [None]

        def set_arg(v):
            def set_arg(stk):
                box[0] = v

            return set_arg

        def do_bind(stk):
            return [set_arg(stk.pop())]

        out.append(do_bind)
        with extend(env, e.f.arg.id, lambda: box[0]):
            _compile(e.f.body, env, out)
    else:
        h = extension_handler(type(e))
        if h is not None:
            _compile(h.encode(e), env, out)
        else:
            raise NotImplementedError(type(e))
    if hasattr(e, "type") and isinstance(e.type, TList):
        out.append(iterable_to_list)
Exemplo n.º 38
0
def stream(iterable : Exp, loop_var : EVar, body : Stm) -> Stm:
    """Convert an iterable expression to a streaming operation.

    Input:
      iterable - an expression with an iterable type (Bag, Set, or List), not
        yet optimized
      loop_var - a variable to use as the loop variable
      body - a statement to run on that variable, not yet optimized

    Output:
      A statement equivalent to
        for (loop_var in iterable) { body }
      that eliminates as many intermediate collections and objects as possible.

    NOTE: The output of function will not be correct if the body modifies any
    free variable in the iterable expression or writes to any pointers that
    are read by the iterable expression.

    Generating code for the expression

        Map {func} (Filter {predicate} big_collection)

    might create two new collections as large as `big_collection`: one to hold
    the result of the filter and one to hold the result of the map.  If all the
    code needs to do is to iterate over the result, then there is no reason to
    make the two new collections.

    This function is mutually recursive with `simplify_and_optimize`, so any
    transformations performed by that method are also applied to the output of
    this one.
    """

    if isinstance(iterable, EEmptyList):
        return SNoOp()
    elif isinstance(iterable, ESingleton):
        setup, value = simplify_and_optimize_expression(iterable.e)
        # SScoped because if the iterable is e.g. [x] + [y], then the body
        # might be appear in the same block twice.  If the body declares any
        # variables, that will cause problems in languages like Java or C++.
        return seq([setup, SScoped(re_use(value, loop_var, simplify_and_optimize(body)))])
    elif isinstance(iterable, ECond):
        cond_setup, cond = simplify_and_optimize_expression(iterable.cond)
        return seq([
            cond_setup,
            SIf(cond,
                stream(iterable.then_branch, loop_var, body),
                stream(iterable.else_branch, loop_var, body))])
    elif isinstance(iterable, EUnaryOp) and iterable.op == UOp.Distinct:
        tmp = fresh_var(TSet(iterable.type.elem_type), "distinct_elems")
        return seq([
            SDecl(tmp, EEmptyList().with_type(tmp.type)),
            stream(iterable.e, loop_var, SIf(
                ENot(EBinOp(loop_var, BOp.In, tmp).with_type(BOOL)),
                seq([body, SCall(tmp, "add", [loop_var])]),
                SNoOp()))])
    elif isinstance(iterable, EBinOp) and iterable.op == "+":
        return seq([
            stream(iterable.e1, loop_var, body),
            stream(iterable.e2, loop_var, body)])
    elif isinstance(iterable, EBinOp) and iterable.op == "-":
        if is_hashable(iterable.type.elem_type):
            h_setup, h = histogram(iterable.e2)
            val_ref = fresh_var(INT, "count")
            return seq([
                simplify_and_optimize(h_setup),
                stream(
                    iterable.e1,
                    loop_var,
                    SIf(EGt(EMapGet(h, loop_var).with_type(INT), ZERO),
                        SMapUpdate(h, loop_var, val_ref, SAssign(val_ref, EBinOp(val_ref, "-", ONE).with_type(INT))),
                        body))])
        else:
            rhs = fresh_var(iterable.e2.type, "bag_subtraction_right")
            return seq([
                simplify_and_optimize(SDecl(rhs, iterable.e2)),
                stream(
                    iterable.e1,
                    loop_var,
                    SIf(EIn(loop_var, rhs),
                        SCall(rhs, "remove", (loop_var,)),
                        body))])
    elif isinstance(iterable, EFilter):
        return stream(
            EFlatMap(iterable.e, ELambda(iterable.predicate.arg,
                ECond(iterable.predicate.body,
                    ESingleton(iterable.predicate.arg).with_type(iterable.type),
                    EEmptyList().with_type(iterable.type)).with_type(iterable.type))).with_type(iterable.type),
            loop_var,
            body)
    elif isinstance(iterable, EMap):
        return stream(
            EFlatMap(iterable.e, ELambda(iterable.transform_function.arg,
                ESingleton(iterable.transform_function.body).with_type(iterable.type))).with_type(iterable.type),
            loop_var,
            body)
    elif isinstance(iterable, EFlatMap):
        inner_loop_var = fresh_var(
            iterable.transform_function.arg.type,
            iterable.transform_function.arg.id)
        return stream(
            iterable.e,
            inner_loop_var,
            stream(iterable.transform_function.apply_to(inner_loop_var), loop_var, body))
    elif isinstance(iterable, EListSlice):
        elem_type = iterable.type.elem_type
        l = fresh_var(iterable.e.type, "list")
        s = fresh_var(INT, "start")
        e = fresh_var(INT, "end")
        return simplify_and_optimize(seq([
            SDecl(l, iterable.e),
            SDecl(s, max_of(iterable.start, ZERO)),
            SDecl(e, min_of(iterable.end, ELen(l))),
            SWhile(ELt(s, e), seq([
                SDecl(loop_var, EListGet(l, s).with_type(elem_type)),
                body,
                SAssign(s, EBinOp(s, "+", ONE).with_type(INT))]))]))
    elif isinstance(iterable, ELet):
        v = fresh_var(
            iterable.body_function.arg.type,
            iterable.body_function.arg.id)
        return seq([
            simplify_and_optimize(SDecl(v, iterable.e)),
            stream(iterable.body_function.apply_to(v), loop_var, body)])
    elif isinstance(iterable, EMove):
        return stream(iterable.e, loop_var, body)
    else:
        assert is_collection(iterable.type), repr(iterable)
        setup, e = simplify_and_optimize_expression(iterable)
        return seq([setup, SForEach(loop_var, e, simplify_and_optimize(body))])
Exemplo n.º 39
0
 def visit(self, e):
     if hasattr(e, "_nosimpl"): return e
     if isinstance(e, Exp) and not isinstance(e, ELambda): t = e.type
     new = super().visit(e)
     if isinstance(e, Exp) and not isinstance(e, ELambda): assert new.type == e.type or (is_collection(new.type) and is_collection(e.type)), repr(e)
     if self.debug and isinstance(e, Exp) and not isinstance(e, ELambda):
         model = satisfy(ENot(EBinOp(e, "===", new).with_type(BOOL)))
         if model is not None:
             raise Exception("bad simplification: {} ---> {} (under model {!r}, got {!r} and {!r})".format(pprint(e), pprint(new), model, eval(e, model), eval(new, model)))
     return new
Exemplo n.º 40
0
def _compile(e, env : {str:int}, out):
    if isinstance(e, EVar):
        i = env[e.id]
        if isinstance(i, int):
            def load_var(stk):
                stk.append(stk[i])
            out.append(load_var)
        else:
            def load_bound(stk):
                stk.append(i())
            out.append(load_bound)
    elif isinstance(e, EBool):
        out.append(push_true if e.val else push_false)
    elif isinstance(e, ENum):
        s = e.val
        if e.type == FLOAT:
            s = Fraction(str(s))
        def push_num(stk):
            stk.append(s)
        out.append(push_num)
    elif isinstance(e, EStr):
        s = e.val
        def push_str(stk):
            stk.append(s)
        out.append(push_str)
    elif isinstance(e, EEnumEntry):
        s = e.name
        def push_enum(stk):
            stk.append(s)
        out.append(push_enum)
    elif isinstance(e, EEmptyList):
        def push_empty_list(stk):
            stk.append(_EMPTY_BAG)
        out.append(push_empty_list)
    elif isinstance(e, ESingleton):
        _compile(e.e, env, out)
        if isinstance(e.type, TList):
            out.append(make_singleton_list)
        else:
            out.append(make_singleton_bag)
    elif isinstance(e, EHandle):
        _compile(e.addr, env, out)
        _compile(e.value, env, out)
        out.append(make_handle)
    elif isinstance(e, ENull):
        out.append(push_null)
    elif isinstance(e, ECond):
        _compile(e.cond, env, out)
        then_code = []; _compile(e.then_branch, env, then_code)
        else_code = []; _compile(e.else_branch, env, else_code)
        def ite(stk):
            return then_code if stk.pop() else else_code
        out.append(ite)
    elif isinstance(e, EMakeRecord):
        for (f, ee) in e.fields:
            _compile(ee, env, out)
        def make_record(stk):
            stk.append(FrozenDict((f, stk.pop()) for (f, _) in reversed(e.fields)))
        out.append(make_record)
    elif isinstance(e, EGetField):
        _compile(e.e, env, out)
        if isinstance(e.e.type, THandle):
            assert e.field_name == "val"
            out.append(get_handle_value)
        else:
            assert isinstance(e.e.type, TRecord)
            f = e.field_name
            def get_field(stk):
                stk.append(stk.pop()[f])
            out.append(get_field)
    elif isinstance(e, ETuple):
        n = len(e.es)
        for ee in e.es:
            _compile(ee, env, out)
        def make_tuple(stk):
            entries = reversed([stk.pop() for i in range(n)])
            stk.append(tuple(entries))
        out.append(make_tuple)
    elif isinstance(e, ETupleGet):
        _compile(e.e, env, out)
        def tuple_get(stk):
            stk.append(stk.pop()[e.index])
        out.append(tuple_get)
    elif isinstance(e, EStateVar):
        _compile(e.e, env, out)
    elif isinstance(e, ENative):
        _compile(e.e, env, out)
        def make_native(stk):
            stk.append((e.type.name, stk.pop()))
        out.append(make_native)
    elif isinstance(e, EUnaryOp):
        _compile(e.e, env, out)
        if e.op == UOp.Not:
            out.append(unaryop_not)
        elif e.op == UOp.Sum:
            out.append(unaryop_sum)
        elif e.op == UOp.Exists:
            out.append(unaryop_exists)
        elif e.op == UOp.Empty:
            out.append(unaryop_empty)
        elif e.op == UOp.All:
            out.append(unaryop_all)
        elif e.op == UOp.Any:
            out.append(unaryop_any)
        elif e.op == UOp.Length:
            out.append(unaryop_len)
        elif e.op == UOp.AreUnique:
            out.append(unaryop_areunique(e.e.type.elem_type))
        elif e.op == UOp.Distinct:
            out.append(unaryop_distinct(e.e.type.elem_type))
        elif e.op == UOp.The:
            out.append(unaryop_the(default=mkval(e.type)))
        elif e.op == UOp.Reversed:
            out.append(unaryop_reversed)
        elif e.op == "-":
            out.append(unaryop_neg)
        else:
            raise NotImplementedError(e.op)
    elif isinstance(e, EBinOp):
        if e.op == BOp.And:
            return _compile(ECond(e.e1, e.e2, EFALSE).with_type(BOOL), env, out)
        elif e.op == BOp.Or:
            return _compile(ECond(e.e1, ETRUE, e.e2).with_type(BOOL), env, out)
        elif e.op == "=>":
            return _compile(ECond(e.e1, e.e2, ETRUE).with_type(BOOL), env, out)
        _compile(e.e1, env, out)
        _compile(e.e2, env, out)
        e1type = e.e1.type
        if e.op == "+":
            if is_collection(e.type):
                out.append(binaryop_add_sets if isinstance(e.type, TSet) else binaryop_add_collections)
            else:
                out.append(binaryop_add_numbers)
        elif e.op == "*":
            out.append(binaryop_mul)
        elif e.op == "-":
            if isinstance(e.type, TBag) or isinstance(e.type, TSet):
                out.append(binaryop_sub_bags(e.type.elem_type))
            elif isinstance(e.type, TList):
                out.append(binaryop_sub_lists(e.type.elem_type))
            else:
                out.append(binaryop_sub)
        elif e.op == "==":
            out.append(binaryop_eq(e1type))
        elif e.op == "===":
            out.append(binaryop_eq(e1type, deep=True))
        elif e.op == "<":
            out.append(binaryop_lt(e1type))
        elif e.op == ">":
            out.append(binaryop_gt(e1type))
        elif e.op == "<=":
            out.append(binaryop_le(e1type))
        elif e.op == ">=":
            out.append(binaryop_ge(e1type))
        elif e.op == "!=":
            out.append(binaryop_ne(e1type))
        elif e.op == BOp.In:
            out.append(binaryop_in(e1type))
        else:
            raise NotImplementedError(e.op)
    elif isinstance(e, EListGet):
        _compile(e.e, env, out)
        _compile(e.index, env, out)
        out.append(list_index(mkval(e.type)))
    elif isinstance(e, EListSlice):
        _compile(e.e, env, out)
        _compile(e.start, env, out)
        _compile(e.end, env, out)
        out.append(list_slice)
    elif isinstance(e, EDropFront):
        _compile(e.e, env, out)
        out.append(drop_front)
    elif isinstance(e, EDropBack):
        _compile(e.e, env, out)
        out.append(drop_back)
    elif isinstance(e, ESorted):
        _compile(e.e, env, out)
        _compile(e.asc, env, out)
        def bag_sort(stk):
            asc = stk.pop()
            bag = stk.pop()
            bag = sorted(bag, reverse=not asc)
            assert isinstance(asc, bool)
            assert isinstance(bag, list)
            stk.append(bag)
        out.append(bag_sort)
    elif isinstance(e, EFilter):
        _compile(e.e, env, out)
        box = [None]
        body = []
        with extend(env, e.predicate.arg.id, lambda: box[0]):
            _compile(e.predicate.body, env, body)
        def set_arg(v):
            def set_arg(stk):
                box[0] = v
            return set_arg
        def maybe_append_to_result(idx):
            return lambda stk: (stk[idx].append(box[0]) if stk.pop() else None)
        def do_filter(stk):
            bag = stk.pop()
            res_idx = len(stk)
            stk.append([])
            ops = []
            for (i, val) in enumerate(bag):
                ops.append(set_arg(val))
                ops.extend(body)
                ops.append(maybe_append_to_result(res_idx))
            return ops
        out.append(do_filter)
        out.append(iterable_to_bag)
    elif isinstance(e, EMap):
        _compile(e.e, env, out)
        box = [None]
        body = []
        with extend(env, e.transform_function.arg.id, lambda: box[0]):
            _compile(e.transform_function.body, env, body)
        def set_arg(v):
            def set_arg(stk):
                box[0] = v
            return set_arg
        def append_to_result(idx):
            return lambda stk: stk[idx].append(stk.pop())
        def do_map(stk):
            bag = stk.pop()
            res_idx = len(stk)
            stk.append([])
            ops = []
            for (i, val) in enumerate(bag):
                ops.append(set_arg(val))
                ops.extend(body)
                ops.append(append_to_result(res_idx))
            return ops
        out.append(do_map)
        out.append(iterable_to_bag)
    elif isinstance(e, EFlatMap):
        _compile(EMap(e.e, e.transform_function).with_type(TBag(e.type)), env, out)
        out.append(do_concat)
    elif isinstance(e, EArgMin) or isinstance(e, EArgMax):
        # stack layout:
        #   len | f(best) | best | elem_0 | ... | elem_len

        # body is a seq. of opcodes that has the effect of pushing
        # f(top_of_stack) onto the stack, leaving the old top underneath
        box = [None]
        def set_arg(stk):
            box[0] = stk[-1]
        body = [set_arg]
        with extend(env, e.key_function.arg.id, lambda: box[0]):
            _compile(e.key_function.body, env, body)

        keytype = e.key_function.body.type

        def initialize(stk):
            bag = stk.pop()
            if bag:
                stk.extend(reversed(bag))
            else:
                stk.append(mkval(e.type))
            return body + [push(len(bag)-1)]

        do_cmp = binaryop_lt(keytype) if isinstance(e, EArgMin) else binaryop_gt(keytype)
        def loop(stk):
            len = stk.pop()
            key = stk.pop()
            if len > 0:
                best = stk.pop()
                return body + [dup, push(key), do_cmp, if_then_else(
                    [],
                    [drop, drop, push(best), push(key)]), push(len-1), loop]

        _compile(e.e, env, out)
        out.append(initialize)
        out.append(loop)
    elif isinstance(e, EMakeMap2):
        _compile(EMap(e.e, ELambda(e.value_function.arg, ETuple((e.value_function.arg, e.value_function.body)).with_type(TTuple((e.value_function.arg.type, e.value_function.body.type))))).with_type(TBag(TTuple((e.value_function.arg.type, e.value_function.body.type)))), env, out)
        default = mkval(e.type.v)
        def make_map(stk):
            res = Map(e.type, default)
            for (k, v) in list(stk.pop()):
                res[k] = v
            stk.append(res)
        out.append(make_map)
    elif isinstance(e, EMapGet):
        _compile(e.map, env, out)
        _compile(e.key, env, out)
        out.append(read_map)
    elif isinstance(e, EHasKey):
        _compile(e.map, env, out)
        _compile(e.key, env, out)
        out.append(has_key(e.key.type))
    elif isinstance(e, EMapKeys):
        _compile(e.e, env, out)
        out.append(read_map_keys)
    elif isinstance(e, ECall):
        _compile(EVar(e.func), env, out)
        for a in e.args:
            _compile(a, env, out)
        n = len(e.args)
        def call(stk):
            args = reversed([stk.pop() for i in range(n)])
            f = stk.pop()
            stk.append(f(*args))
        out.append(call)
    elif isinstance(e, ELet):
        _compile(e.e, env, out)
        box = [None]
        def set_arg(v):
            def set_arg(stk):
                box[0] = v
            return set_arg
        def do_bind(stk):
            return [set_arg(stk.pop())]
        out.append(do_bind)
        with extend(env, e.body_function.arg.id, lambda: box[0]):
            _compile(e.body_function.body, env, out)
    else:
        from cozy.structures.treemultiset import ETreeMultisetElems
        h = extension_handler(type(e))
        if h is not None:
            if isinstance(e, ETreeMultisetElems) and isinstance(e.e, EVar):
                _compile(e.e, env, out)
                # this is a no-op because its argument, the Treeset, will be sorted already
                def no_op(stk):
                    pass
                out.append(no_op)
            else:
                _compile(h.encode(e), env, out)
        else:
            raise NotImplementedError(type(e))
    if hasattr(e, "type") and isinstance(e.type, TList):
        out.append(iterable_to_list)
Exemplo n.º 41
0
    def build(self, cache, size):

        for e in cache.find(pool=RUNTIME_POOL, size=size - 1, type=INT):
            if not is_root(e):
                continue
            e2 = simplify_sum(e)
            if e != e2:
                yield self.check(e2, RUNTIME_POOL)

        # for e in cache.find(pool=RUNTIME_POOL, size=size-1):
        #     if isinstance(e, EMapGet) and isinstance(e.map, EMakeMap2):
        #         x = e.map.value.apply_to(e.key)
        #         x._tag = True
        #         yield self.check(x, RUNTIME_POOL)

        # [x] - ys
        for e in cache.find_collections(pool=RUNTIME_POOL, size=size - 1):
            if not is_root(e):
                continue
            if isinstance(e, EBinOp) and e.op == "-" and isinstance(
                    e.e1, ESingleton):
                x = e.e1.e
                y = e.e2
                x = ECond(
                    EBinOp(x, BOp.In, y).with_type(BOOL),
                    EEmptyList().with_type(e.type), e.e1).with_type(e.type)
                yield self.check(x, RUNTIME_POOL)
            elif isinstance(e, EUnaryOp) and e.op == UOp.Distinct:
                e = strip_EStateVar(e)
                m = EMakeMap2(e.e, mk_lambda(e.type.t, lambda x: T)).with_type(
                    TMap(e.type.t, BOOL))
                yield self.check(m, STATE_POOL)
                m = EStateVar(m).with_type(m.type)
                yield self.check(m, RUNTIME_POOL)
                x = EMapKeys(m).with_type(e.type)
                # x._tag = True
                yield self.check(x, RUNTIME_POOL)

        # # x in ys ----> (count x in ys) > 0
        # for e in cache.find(pool=RUNTIME_POOL, type=BOOL, size=size-1):
        #     if isinstance(e, EBinOp) and e.op == BOp.In:
        #         for b in self.binders:
        #             if b.type != e.e1.type:
        #                 continue
        #             x = EGt(
        #                 EUnaryOp(UOp.Length, EFilter(e.e2, ELambda(b, EEq(e.e1, b))).with_type(e.e2.type)).with_type(INT),
        #                 ZERO)
        #             x._tag = True
        #             yield self.check(x, RUNTIME_POOL)

        for e in cache.find(pool=RUNTIME_POOL, size=size - 1):
            if not is_root(e):
                continue
            if (isinstance(e, EArgMin) or isinstance(
                    e, EArgMax)) and isinstance(e.e, EBinOp) and e.e.op == "+":
                l = e.e.e1
                r = e.e.e2
                op = e.e.op
                f = lambda x: type(e)(x, e.f).with_type(e.type)
                ll = EStateVar(f(l.e)).with_type(e.type) if isinstance(
                    l, EStateVar) else f(l)
                rr = EStateVar(f(r.e)).with_type(e.type) if isinstance(
                    r, EStateVar) else f(r)
                x = ECond(
                    EUnaryOp(UOp.Exists, l).with_type(BOOL),
                    ECond(
                        EUnaryOp(UOp.Exists, r).with_type(BOOL),
                        f(
                            EBinOp(
                                ESingleton(ll).with_type(e.e.type), op,
                                ESingleton(rr).with_type(e.e.type)).with_type(
                                    e.e.type)), ll).with_type(e.type),
                    rr).with_type(e.type)
                # from cozy.solver import valid
                # assert valid(EEq(e, x), model_callback=print)
                x._tag = True
                yield self.check(x, RUNTIME_POOL)

        # is-last(x, l)
        for (sz1, sz2) in pick_to_sum(2, size - 1):
            for e1 in cache.find(pool=RUNTIME_POOL, size=sz1):
                if not is_root(e1):
                    continue
                for e2 in cache.find_collections(pool=STATE_POOL,
                                                 size=sz2,
                                                 of=e1.type):
                    if not is_root(e2):
                        continue
                    for b in self.binders:
                        if b.type != e1.type:
                            continue
                        m = EMakeMap2(
                            e2,
                            mk_lambda(
                                e2.type.t, lambda x: EUnaryOp(
                                    UOp.Length,
                                    EFilter(
                                        e2,
                                        mk_lambda(e2.type.t, lambda y: EEq(
                                            x, y))).with_type(e2.type)).
                                with_type(INT))).with_type(TMap(
                                    e2.type.t, INT))
                        # filt = EFilter(e2, ELambda(b, EEq(e1, b))).with_type(e2.type)
                        # x = EEq(
                        #     EUnaryOp(UOp.Length, filt).with_type(INT),
                        #     ONE)
                        x = EGt(
                            EMapGet(EStateVar(m).with_type(m.type),
                                    e1).with_type(INT), ONE)
                        # x._tag = True
                        yield self.check(x, RUNTIME_POOL)

        # histogram
        # for e in cache.find_collections(pool=STATE_POOL, size=size-1):
        #     m = EMakeMap2(e,
        #         mk_lambda(e.type.t, lambda x:
        #             EUnaryOp(UOp.Length, EFilter(e,
        #                 mk_lambda(e.type.t, lambda y: EEq(x, y))).with_type(e.type)).with_type(INT))).with_type(TMap(e.type.t, INT))
        #     m._tag = True
        #     yield self.check(m, STATE_POOL)

        # Fixup EFilter(\x -> ECond...)
        for e in cache.find_collections(pool=RUNTIME_POOL, size=size - 1):
            if not is_root(e):
                continue
            if isinstance(e, EFilter):
                for (_, x, r, _) in enumerate_fragments(e.p.body):
                    if isinstance(x, ECond):
                        lhs = EFilter(
                            e.e,
                            ELambda(e.p.arg, EAll([x.cond,
                                                   r(x.then_branch)
                                                   ]))).with_type(e.type)
                        rhs = EFilter(
                            e.e,
                            ELambda(e.p.arg,
                                    EAll([ENot(x.cond),
                                          r(x.else_branch)
                                          ]))).with_type(e.type)
                        union = EBinOp(lhs, "+", rhs).with_type(e.type)
                        # yield self.check(lhs.p.body, RUNTIME_POOL)
                        # yield self.check(rhs.p.body, RUNTIME_POOL)
                        yield self.check(lhs, RUNTIME_POOL)
                        yield self.check(rhs, RUNTIME_POOL)
                        yield self.check(union, RUNTIME_POOL)

        # Try instantiating bound expressions
        for pool in (STATE_POOL, RUNTIME_POOL):
            for (sz1, sz2) in pick_to_sum(2, size - 1):
                for e1 in cache.find(pool=pool, size=sz1):
                    if not is_root(e1):
                        continue
                    for v in free_vars(e1):
                        if pool == RUNTIME_POOL:
                            e1 = subst(
                                strip_EStateVar(e1), {
                                    sv.id: EStateVar(sv).with_type(sv.type)
                                    for sv in self.state_vars if sv != v
                                })
                        for e2 in cache.find(pool=pool, type=v.type, size=sz2):
                            yield self.check(subst(e1, {v.id: e2}), pool)

        for (sz1, sz2) in pick_to_sum(2, size - 1):
            for e in cache.find(pool=RUNTIME_POOL, size=sz1):
                if not is_root(e):
                    continue
                for x, pool in map_accelerate(e, self.state_vars, self.binders,
                                              self.args, cache, sz2):
                    yield self.check(x, pool)
                if isinstance(e, EFilter) and not any(v in self.binders
                                                      for v in free_vars(e)):
                    for x, pool in accelerate_filter(e.e, e.p, self.state_vars,
                                                     self.binders, self.args,
                                                     cache, sz2):
                        yield self.check(x, pool)

        for bag in cache.find_collections(pool=RUNTIME_POOL, size=size - 1):
            if not is_root(bag):
                continue
            for a in self.args:
                for v in self.state_vars:
                    if is_collection(v.type) and v.type == a.type:
                        v = EStateVar(v).with_type(v.type)
                        cond = EBinOp(a, BOp.In, v).with_type(BOOL)
                        yield self.check(
                            EFilter(bag, mk_lambda(bag.type.t,
                                                   lambda _: cond)).with_type(
                                                       bag.type), RUNTIME_POOL)
                        yield self.check(
                            EFilter(
                                bag,
                                mk_lambda(bag.type.t,
                                          lambda _: ENot(cond))).with_type(
                                              bag.type), RUNTIME_POOL)

            if isinstance(bag, EFilter):
                if any(v not in self.state_vars for v in free_vars(bag.e)):
                    continue

                # separate filter conds
                if isinstance(bag.p.body, EBinOp) and bag.p.body.op == BOp.And:
                    p1 = ELambda(bag.p.arg, bag.p.body.e1)
                    p2 = ELambda(bag.p.arg, bag.p.body.e2)
                    f1 = EFilter(bag.e, p1).with_type(bag.type)
                    f2 = EFilter(bag.e, p2).with_type(bag.type)
                    f3 = EFilter(f1, p2).with_type(bag.type)
                    f4 = EFilter(f2, p1).with_type(bag.type)
                    yield self.check(f1, RUNTIME_POOL)
                    yield self.check(f2, RUNTIME_POOL)
                    yield self.check(f3, RUNTIME_POOL)
                    yield self.check(f4, RUNTIME_POOL)

                # construct map lookups
                binder = bag.p.arg
                inf = infer_map_lookup(bag.p.body, binder,
                                       set(self.state_vars))
                if inf:
                    key_proj, key_lookup, remaining_filter = inf
                    bag_binder = find_one(
                        self.binders,
                        lambda b: b.type == key_proj.type and b != binder)
                    if bag_binder:
                        m = strip_EStateVar(
                            EMakeMap2(
                                EMap(bag.e,
                                     ELambda(binder, key_proj)).with_type(
                                         type(bag.type)(key_proj.type)),
                                ELambda(
                                    bag_binder,
                                    EFilter(
                                        bag.e,
                                        ELambda(binder,
                                                EEq(key_proj,
                                                    bag_binder))).with_type(
                                                        bag.type))).with_type(
                                                            TMap(
                                                                key_proj.type,
                                                                bag.type)))
                        assert not any(v in self.args for v in free_vars(m))
                        yield self.check(m, STATE_POOL)
                        m = EStateVar(m).with_type(m.type)
                        mg = EMapGet(m, key_lookup).with_type(bag.type)
                        yield self.check(mg, RUNTIME_POOL)
                        yield self.check(
                            EFilter(mg, ELambda(
                                binder, remaining_filter)).with_type(mg.type),
                            RUNTIME_POOL)

        # for e in cache.find(size=size-1):
        #     # F(xs +/- ys) ---> F(xs), F(ys)
        #     for z in break_plus_minus(e):
        #         if z != e:
        #             # print("broke {} --> {}".format(pprint(e), pprint(z)))
        #             yield z

        #     # try reordering operations
        #     for (_, e1, f) in enumerate_fragments(e):
        #         if e1.type == e.type and e1 != e:
        #             for (_, e2, g) in enumerate_fragments(e1):
        #                 if e2.type == e.type and e2 != e1:
        #                     # e == f(g(e2))
        #                     yield g(f(e2))

        yield from self.wrapped.build(cache, size)