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
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
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
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
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
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