Example #1
0
def find_min(constraints, expr, default_min=0):
    if type(expr) == int:
        return expr

    constraint_strs = [f'{c}' for c in constraints]

    min_optimize = Optimize()
    min_optimize.set('timeout', 10000)

    min_optimize.assert_exprs(*constraints)
    min_optimize.minimize(expr)
    status = min_optimize.check()
    if status != sat:
        print(f'Unable to find min ({status}) for:\n' +
              '\n'.join(constraint_strs))
        return None

    min_val = min_optimize.model().eval(expr).as_long()

    # Make sure it's actually the min, since z3 has a bug
    #   https://github.com/Z3Prover/z3/issues/4670
    solver = Solver()
    solver.set('timeout', 10000)
    solver.add(constraints + [expr < min_val])
    status = solver.check()

    if status != unsat:
        print(
            f'Z3 bug\nFind min ({expr}) => {min_val} with status ({status}):\n'
            + '\n'.join(constraint_strs))
        return None
    return min_val
Example #2
0
def find_max(constraints, expr, l = None):
    if l is None:
        l = logger

    if type(expr) == int:
        return expr

    constraint_strs = [f'{c}' for c in constraints]

    max_optimize = Optimize()
    max_optimize.set('timeout', 10000)
    max_optimize.assert_exprs(*constraints)
    max_optimize.maximize(expr)
    status = max_optimize.check()
    if status != sat:
        l.warning(f'Unable to find max ({status}) for:\n' + '\n'.join(constraint_strs))
        return None

    max_val = max_optimize.model().eval(expr).as_long()

    # Make sure it's actually the max, since z3 has a bug
    #   https://github.com/Z3Prover/z3/issues/4670
    solver = Solver()
    solver.set('timeout', 10000)
    solver.add(constraints + [expr > max_val])
    status = solver.check()

    if status != unsat:
        l.error(f'Z3 bug\nFind max ({expr}) => {max_val} with status ({status}):\n' + '\n'.join(constraint_strs))
        return None
    return max_val
Example #3
0
def get_model(constraints, minimize=(), maximize=()):
    s = Optimize()
    s.set("timeout", 100000)

    for constraint in constraints:
        if type(constraint) == bool and not constraint:
            raise UnsatError

    constraints = [
        constraint for constraint in constraints if type(constraint) != bool
    ]

    for constraint in constraints:
        s.add(constraint)
    for e in minimize:
        s.minimize(e)
    for e in maximize:
        s.maximize(e)

    result = s.check()
    if result == sat:
        return s.model()
    elif result == unknown:
        logging.debug("Timeout encountered while solving expression using z3")
    raise UnsatError
Example #4
0
def solve_pkgver_z3(pkg_dict,
                    constrain_version_dict,
                    optim_target="approximate"):
    # solver = Solver()
    solver = Optimize()
    int_dict = build_int_dict(pkg_dict)
    order_dict = build_order_dict(pkg_dict)
    solver = add_dependency_constrains(solver, pkg_dict, order_dict, int_dict)
    solver = add_int_constrains(solver, pkg_dict, int_dict)
    solver = add_version_constrains(solver, constrain_version_dict, order_dict,
                                    int_dict)
    if optim_target == "approximate":
        solver = add_optimize_targets(solver, int_dict)
    if optim_target == "exact":
        solver = add_optimize_targets2(solver, int_dict, order_dict)
        solver.set(timeout=60000)
    try:
        if solver.check():
            pkgvers = parse_z3_model(solver.model(), int_dict, order_dict)
            return pkgvers
        else:
            return None
    except:
        return None
Example #5
0
    def smt_solution(self, timeout, optimize=False):
        if optimize:
            solver = Optimize()
        else:
            solver = Solver()
            solver.set("zero_accuracy",10)
        solver.set("timeout", timeout)


        non_trivial = False
        diff_sol = False
        smt_keep_sol = True # a.k.a A1
        smt_is_sol = True # a.k.a A2

        some_consume = False
        some_produce = False
        variables = set()
        variables_positive = set()
        for p_id, place in enumerate(self.facets):
            ti = place.offset
            smt_ti = Int("b%s" % p_id)

            if ti:
                solver.add(min(0,ti) <= smt_ti, smt_ti <= max(0, ti))
            else:
                solver.add(smt_ti == 0)


            facet_non_trivial = False
            facet_diff_sol = False
            smt_facet_eval = ti
            smt_facet_sol = ti

            # If halfspace's coefficients add to 1 it's
            # as simple as it gets
            simple = sum(abs(x) for x in place.normal) <= 1
            # do_cons_prod indicates if the place has incoming and outgoing transitions
            do_cons_prod = False
            consume = produce = 0
            for coeff in place.normal:
                if coeff > 0:
                    produce = 1
                elif coeff < 0:
                    consume = 1
                if consume * produce:
                    do_cons_prod = True
                    break

            do_cons_prod = reduce(lambda x,y:x*y, [x + 1 for x in place.normal]) < 1

            for t_id, coeff in enumerate(place.normal):
                # SMT coefficient
                smt_coeff = Int("a%s,%s" % (p_id, t_id))
                if optimize:
                    # Try to minimize the coefficient
                    solver.minimize(smt_coeff)
                # SMT variable
                smt_var = Int("x%s" % t_id)

                # Add SMT varible to the set of variables
                variables.add(smt_var)
                # Add SMT varible basic constraint to the set
                variables_positive.add(smt_var >= 0)
                if coeff:
                    # At least one SMT coefficient should be non zero
                    facet_non_trivial = Or(facet_non_trivial, smt_coeff != 0)
                    # At least one SMT coefficient should be different
                    facet_diff_sol = Or(facet_diff_sol, smt_coeff != coeff)
                    # Original solution with SMT variable
                    smt_facet_eval += coeff * smt_var
                    # SMT solution with SMT variable
                    smt_facet_sol += smt_coeff * smt_var
                    # Keep SMT coefficient between original bundaries
                    solver.add(min(0,coeff) <= smt_coeff, smt_coeff <= max(0, coeff))

                    if not self.neg_points and not simple and do_cons_prod:
                        some_produce = Or(some_produce, smt_coeff > 0)
                        some_consume = Or(some_consume, smt_coeff < 0)
                else:
                    # Keep zero coefficients unchanged
                    solver.add(smt_coeff == 0)
            if not self.neg_points:
                # Avoid trivial solution (i.e. all zeros as coeff)
                non_trivial = Or(non_trivial, facet_non_trivial)
            # Solution of smt must be different
            diff_sol = Or(diff_sol, facet_diff_sol)
            # If point is in old-solution should be in smt-solution
            smt_keep_sol = And(smt_keep_sol, smt_facet_eval <= 0)
            # Solutions shoul be a solution
            smt_is_sol = And(smt_is_sol, smt_facet_sol <= 0)

            if not self.neg_points and not simple and do_cons_prod:
                solver.add(simplify(some_consume))
                solver.add(simplify(some_produce))

            if optimize:
                # Try to minimize the independent term
                solver.minimize(smt_ti)

    # This is what we want:
        if not self.neg_points:
            # If there is not negative info, avoid trivial solution (i.e. Not all zero)
            solver.add(simplify(non_trivial))
        # A different solution (i.e. "better")
        solver.add(simplify(diff_sol))
        # If it was a solution before, keep it that way
        solver.add(simplify(ForAll(list(variables), Implies(And(Or(list(variables_positive)), smt_keep_sol), smt_is_sol))))

        # Negative point shouldn't be a solution
        for np in self.neg_points:
            smt_np = False
            for p_id, place in enumerate(self.facets):
                place_at_np = Int("b%s" % p_id)
                for t_id, coeff in enumerate(place.normal):
                    # If coefficient was zero, it will remain zero
                    if coeff and np[t_id]:
                        smt_coeff = Int("a%s,%s" % (p_id, t_id))
                        place_at_np = place_at_np + smt_coeff * np[t_id]

                smt_np = Or(smt_np, place_at_np > 0)
            # Keep out (NOTE halfspaces are Ax + b <= 0)
            solver.add(simplify(smt_np))

        sol = solver.check()
        if sol == unsat:
            ret = False
            logger.info('Z3 returns UNSAT: Cannot reduce without adding neg info')
        elif sol == unknown:
            ret = False
            logger.info('Z3 returns UNKNOWN: Cannot reduce in less than %s miliseconds', timeout)
        else:
            ret = solver.model()
        return ret
Example #6
0
    def smt_solution(self, timeout, neg_points=None, optimize=False):
        # If halfspace's coefficients add to 1 it's
        # as simple as it gets
        normal_sum = sum(abs(x) for x in self.normal)
        if normal_sum == 0:
            return False
        elif normal_sum <= 1:
            simple = True
        else:
            simple = False

        neg_points = neg_points or []

        if optimize:
            solver = Optimize()
        else:
            solver = Solver()
            solver.set("zero_accuracy",10)
        solver.set("timeout", timeout)

        ti = self.offset
        smt_ti = Int("b")
        if ti:
            solver.add(min(0,ti) <= smt_ti, smt_ti <= max(0,ti))
            if optimize:
                # Try to minimize the independent term
                solver.minimize(smt_ti)
        else:
            solver.add(smt_ti == 0)

        variables = set()
        variables_positive = set()

        positive_x = True
        non_trivial = False
        some_consume = False
        some_produce = False

        diff_sol = smt_ti != ti
        smt_keep_sol = ti
        smt_is_sol = smt_ti

        for t_id, coeff in enumerate(self.normal):
            # SMT coefficient
            smt_coeff = Int("a%s" % t_id)
            if optimize:
                # Try to minimize the coefficient
                solver.minimize(smt_coeff)
            # SMT variable
            smt_var = Int("x%s"%t_id)

            # Add SMT varible to the set of variables
            variables.add(smt_var)
            # Add SMT varible basic constraint to the set
            variables_positive.add(smt_var >= 0)

            if coeff:
                # At least one SMT coefficient should be non zero
                non_trivial = Or(non_trivial, smt_coeff != 0)
                # At least one SMT coefficient should be different
                diff_sol = Or(diff_sol, smt_coeff != coeff)
                # Original solution with SMT variable
                smt_keep_sol += coeff * smt_var
                # SMT solution with SMT variable
                smt_is_sol += smt_coeff * smt_var
                # Keep SMT coefficient between original bundaries
                solver.add(min(0,coeff) <= smt_coeff, smt_coeff <= max(0, coeff))

                if not neg_points and not simple:
                    some_produce = Or(some_produce, smt_coeff > 0)
                    some_consume = Or(some_consume, smt_coeff < 0)
            else:
                solver.add(smt_coeff == 0)

    #This is what we want:
        if not neg_points:
            # If there is not negative info, avoid trivial solution (i.e. Not all zero)
            solver.add(simplify(non_trivial))
            if not simple:
                solver.add(simplify(some_consume))
                solver.add(simplify(some_produce))
        # A different solution (i.e. "better")
        solver.add(simplify(diff_sol))
        # If it was a solution before, keep it that way
        solver.add(simplify(ForAll(list(variables), Implies(And(Or(list(variables_positive)), smt_keep_sol <= 0), smt_is_sol <= 0))))

        # New solution shouldn't add a negative point
        for np in neg_points:
            smt_ineq_np = smt_ti
            ineq_np = ti
            for t_id, coeff in enumerate(self.normal):
                ineq_np += coeff * np[t_id]
                smt_coeff = Int("a%s"%(t_id))
                smt_ineq_np += smt_coeff * np[t_id]
            # If neg point was out of this halfspace, keep it out!
            if ineq_np > 0:
                logger.info('Adding HS SMT-NEG restriction')
                solver.add(simplify(smt_ineq_np > 0))

        sol = solver.check()
        if sol == unsat:
            ret = False
            logger.info('Z3 returns UNSAT: Cannot simplify HS without adding neg info')
        elif sol == unknown:
            ret = False
            logger.info('Z3 returns UNKNOWN: Cannot simplify HS in less than %s miliseconds', timeout)
        else:
            ret = solver.model()
        return ret