def solver(self): solver = z3.Solver() # Cheat a bit to randomize possible solution by adding constraints in a random order undetermined = list(self.tiles(determined=False)) revealed_boundary = list(self.tiles(revealed=True, on_boundary=True)) shuffle(undetermined) shuffle(revealed_boundary) # The sum of all undetermined tiles that are a mine must equal the number of unplaced mines solver.add( z3.PbEq([(t.var, 1) for t in undetermined], self.num_undetermined_mines) ) # The sum of all undetermined tiles touching a revealed number must equal that number for t in revealed_boundary: known_mine_neighbors = sum( 1 for n in t.neighbors if n.determined and n.mine ) solver.add( z3.PbEq( [(n.var, 1) for n in t.neighbors if n.undetermined], t.num_adjacent_mines - known_mine_neighbors, ) ) return solver
def cons_rowcol(self): '''Return basic structural constraints. The returned list of constraints will require every row and column of every grid to have exactly one 1. ''' for grid in self.all_grids: for i in range(self.num_items): yield z3.PbEq([(g, 1) for g in grid[i, :]], 1) yield z3.PbEq([(g, 1) for g in grid[:, i]], 1)
def solve_vortorb_flip(col_constraints, row_constraints, cell_constraints): ''' col_constraints: [Constraint], left-to-right row_constraints: [Constraint], top-to-bottom cell_constraints: {(row, col): val} returns num_solutions, 2d-array[row][col]{val: probability} ''' cel_val_upper_bound = max(c.sum_ for c in col_constraints) cel_val_upper_bound = max(cel_val_upper_bound, max(c.sum_ for c in row_constraints)) cel_val_upper_bound = min(cel_val_upper_bound, 3) s = z3.Solver() n = len(col_constraints) xss = [[z3.Int('x{}{}'.format(i, j)) for j in range(n)] for i in range(n)] # cell constraints for i in range(n): for j in range(n): val = cell_constraints.get((i, j)) if val is not None: s.add(xss[i][j] == val) else: s.add(0 <= xss[i][j], xss[i][j] <= cel_val_upper_bound) # row constraints for i in range(n): s.add(z3.Sum(xss[i]) == row_constraints[i].sum_) s.add(z3.PbEq([(xss[i][j]==0, 1) for j in range(n)], row_constraints[i].zeros)) # col constraints for j in range(n): s.add(z3.Sum([xss[i][j] for i in range(n)]) == col_constraints[j].sum_) s.add(z3.PbEq([(xss[i][j]==0, 1) for i in range(n)], col_constraints[j].zeros)) num_soln = 0 soln = [[collections.defaultdict(int) for j in range(n)] for i in range(n)] while s.check() == z3.sat: num_soln += 1 m = s.model() for i in range(n): for j in range(n): soln[i][j][m[xss[i][j]].as_long()] += 1 s.add(z3.Or([xss[i][j] != m[xss[i][j]] for i in range(n) for j in range(n)])) for i in range(n): for j in range(n): for k, v in list(soln[i][j].items()): soln[i][j][k] = v/num_soln return num_soln, soln
def visitBFactorOneOnly(self, ctx): formulas = [] for i in range(2, ctx.getChildCount() - 1, 2): formulas.append(ctx.getChild(i).accept(self)) if not formulas: return z3.BoolVal(False) elif len(formulas) == 1: return formulas[0] return z3.PbEq([(i, 1) for i in formulas], 1)
def to_z3_(self): return z3.PbEq(*[a.to_z3_() for a in self.children], 1)
def run_feature_analysis_forall(features, features_as_boolean, contexts, attributes, constraints, optional_features, non_incremental_solver, out_stream, time_context=""): """ Performs the feature analysis task. A quantifier formula is solved to detect the anomalies """ data = {"dead_features": {}, "false_optionals": {}} solver = z3.Solver() #solver.set("smt.relevancy", 0) if non_incremental_solver: solver.set("combined_solver.solver2_timeout", 1) # if time variable is not defined, create a fictional one time_context = get_time_context(time_context, optional_features) # add it in context if not present if time_context not in contexts: contexts[time_context] = {} contexts[time_context]['min'] = 0 contexts[time_context]['max'] = 0 # these constraints will also be added in the forall formula and are redundant # hopefully they will help the SMT solver to solve the forall formula solver.add(contexts[time_context]["min"] <= z3.Int(time_context)) solver.add(z3.Int(time_context) <= contexts[time_context]["max"]) log.info("Building the FM formula") # will repeat the constraints about the bounds on the environment but that is OK formulas = get_basic_formula_list(features, attributes, contexts, constraints, features_as_boolean) if not non_incremental_solver: log.debug("Preliminary check") solver.check() log.info( "Computing dead or false optional features considering {} optional features" .format(len(optional_features))) opt_features_ls = optional_features.keys() # fresh variables representing the selected features namefresh_var fresh_var = "_" + uuid.uuid4().hex if not opt_features_ls: log.warning("Nothing to check") json.dump(data, out_stream) out_stream.write("\n") return elif len( opt_features_ls ) == 1: # zip problem raised by smt if list of one element is used for z3.PbEq solver.add(z3.Bool(opt_features_ls[0] + fresh_var)) else: # only one selected solver.add( z3.PbEq([(z3.Bool(i + fresh_var), 1) for i in opt_features_ls], 1)) log.info( "Adding constraint limiting time context based on the optional features to check" ) for i in opt_features_ls: ls = [] for k in optional_features[i]: ls.append(z3.Int(time_context) >= k[0]) ls.append(z3.Int(time_context) <= k[1]) solver.add(z3.Implies(z3.Bool(i + fresh_var), z3.And(ls))) solver.push() if features_as_boolean: z3_features = [z3.Bool(j) for j in features] else: z3_features = [z3.Int(j) for j in features] log.info("Search for dead features") solver.add( z3.ForAll( z3_features + [z3.Int(j) for j in attributes.keys()] + [z3.Int(j) for j in contexts if j != time_context], z3.Implies( z3.And([ z3.Implies( z3.Bool(i + fresh_var), z3.Bool(i) if features_as_boolean else z3.Int(i).__eq__(z3.IntVal(1))) for i in opt_features_ls ]), z3.Not(z3.And(formulas))))) log.debug(unicode(solver)) while True: log.info("Computing") result = solver.check() if result == z3.sat: model = solver.model() for i in opt_features_ls: if model[z3.Bool(i + fresh_var)] == z3.BoolVal(True): found_feature = i break assert found_feature found_context = model[z3.Int(time_context)].as_long() # remove check for false optional log.debug("Dead feature for time {}: {}".format( found_context, found_feature)) if found_feature in data["dead_features"]: data["dead_features"][found_feature].append(found_context) else: data["dead_features"][found_feature] = [found_context] # add constraint for next iteration solver.add( z3.Not( z3.And( z3.Bool(found_feature + fresh_var), z3.Int(time_context).__eq__( z3.IntVal(found_context))))) elif result == z3.unsat: log.debug("Formula found unsat. No more dead features.") break else: log.critical( "SMT solver can not solve the forall formula (result unknown)." ) log.critical("Exiting") sys.exit(1) solver.pop() log.info("Search for false positive features") solver.add( z3.ForAll( z3_features + [z3.Int(j) for j in attributes.keys()] + [z3.Int(j) for j in contexts if j != time_context], z3.Implies( z3.And([ z3.Implies( z3.Bool(i + fresh_var), z3.Not(z3.Bool(i)) if features_as_boolean else z3.Int(i).__eq__(z3.IntVal(0))) for i in opt_features_ls ]), z3.Not(z3.And(formulas))))) log.info("Remove also the checks for the {} features found dead".format( len(data["dead_features"]))) for i in data["dead_features"]: for j in data["dead_features"][i]: solver.add( z3.Not( z3.And(z3.Bool(i + fresh_var), z3.Int(time_context).__eq__(z3.IntVal(j))))) #log.debug(unicode(solver)) while True: log.info("Computing") result = solver.check() if result == z3.sat: model = solver.model() for i in opt_features_ls: if model[z3.Bool(i + fresh_var)] == z3.BoolVal(True): found_feature = i break assert found_feature found_context = model[z3.Int(time_context)].as_long() log.debug("False positive feature for time {}: {}".format( found_context, found_feature)) if found_feature in data["false_optionals"]: data["false_optionals"][found_feature].append(found_context) else: data["false_optionals"][found_feature] = [found_context] # add constraint for next iteration solver.add( z3.Not( z3.And( z3.Bool(found_feature + fresh_var), z3.Int(time_context).__eq__( z3.IntVal(found_context))))) elif result == z3.unsat: log.debug("Formula found unsat. No more false positives.") break else: log.critical( "SMT solver can not solve the forall formula (result unknown)." ) log.critical("Exiting") sys.exit(1) log.info("Printing output") json.dump(data, out_stream) out_stream.write("\n")
def exactlyOne(problem, varList): # constrains at exactly one of the vars in the list to be true problem.add(z3.PbEq([(v, 1) for v in varList], 1))
def to_z3(self, ctx): args = list(map(lambda v: (ctx.z3var(v), 1), self._vars)) return z3.PbEq(args, self._n)
def solve(puzzle: Puzzle, forced_rects: Set[Rect] = frozenset(), clue_constraints: str = 'hard', cover_constraints: str = 'exact', corner_constraints: str = 'hard', reflex_three_corners: bool = False) -> Iterator[List[Rect]]: rect_to_var: Dict[Rect, z3.Bool] = {} # Each cell is covered by exactly one rect. cell_to_rects: Dict[Point, z3.Bool] = {c: list() for c in puzzle.cells} # Each clue is satisfied by exactly one rect (of the right shape). clue_to_rects: Dict[Point, z3.Bool] = {c: list() for c in puzzle.clues.keys()} # Maps cells to rects having a there, for tracking four-corner violations. # The values may be empty (but most won't be). ul_to_rects: Dict[Point, z3.Bool] = {c: list() for c in puzzle.cells} ur_to_rects: Dict[Point, z3.Bool] = {c: list() for c in puzzle.cells} ll_to_rects: Dict[Point, z3.Bool] = {c: list() for c in puzzle.cells} lr_to_rects: Dict[Point, z3.Bool] = {c: list() for c in puzzle.cells} for r1 in range(puzzle.rmax): for c1 in range(puzzle.cmax): for r2 in range(r1, puzzle.rmax): for c2 in range(c1, puzzle.cmax): width, height = c2 - c1 + 1, r2 - r1 + 1 # inclusive rect = Rect(r1, c1, height, width) rect_cells = { Point(r, c) for r in range(r1, r2 + 1) for c in range(c1, c2 + 1) } # Depending on where the violation was, we may be able to # break the r2 loop early for both of these checks. if not rect_cells <= puzzle.cells: if rect in forced_rects: raise RuntimeError( 'forced rect {} contains holes {}'.format( rect, rect_cells - puzzle.cells)) break rect_clues = rect_cells & puzzle.clues.keys() if len(rect_clues) > 1: if rect in forced_rects: raise RuntimeError( 'forced rect {} contains multiple clues at {}'. format(rect, rect_clues)) break if len(rect_clues) == 0: if rect in forced_rects: raise RuntimeError( 'forced rect {} contains no clues'.format( rect)) continue # expanding the rect may add a clue clue_cell = rect_clues.pop() clue = puzzle.clues[clue_cell] expected_clue = Clue.PLUS if width == height else Clue.VERT if width < height else Clue.HORIZ if clue != expected_clue and clue_constraints == 'hard': if rect in forced_rects: raise RuntimeError( 'forced rect {} contains a {}, but is shaped for a {}' .format(rect, clue, expected_clue)) continue var = z3.Bool('{},{},{},{}'.format(r1, c1, height, width)) rect_to_var[rect] = var for c in rect_cells: cell_to_rects[c].append(var) clue_to_rects[clue_cell].append(var) ul_to_rects[Point(r1, c1)].append(var) ur_to_rects[Point(r1, c2)].append(var) ll_to_rects[Point(r2, c1)].append(var) lr_to_rects[Point(r2, c2)].append(var) missing_forced_rects = forced_rects - rect_to_var.keys() if missing_forced_rects: # We could add a command-line flag to disable pruning to get a # better error message here. raise RuntimeError( 'forced rects {} were pruned'.format(missing_forced_rects)) for cell, rects in cell_to_rects.items(): if not rects: print('cell', cell, 'has no covering rects') for clue_cell, rects in clue_to_rects.items(): if not rects: print('clue', puzzle.clues[clue_cell], 'in', clue_cell, 'has no candidate rects') solver = z3.Optimize() for c in puzzle.cells: if c in clue_to_rects and clue_constraints == 'hard': solver.add(z3.PbEq([(r, 1) for r in clue_to_rects[c]], 1)) elif cover_constraints == 'exact': solver.add(z3.PbEq([(r, 1) for r in cell_to_rects[c]], 1)) elif cover_constraints == 'subset': solver.add(z3.PbLe([(r, 1) for r in cell_to_rects[c]], 1)) solver.add_soft(z3.PbGe([(r, 1) for r in cell_to_rects[c]], 1), 1, 'cover') elif cover_constraints == 'superset': solver.add_soft(z3.PbLe([(r, 1) for r in cell_to_rects[c]], 1), 1, 'cover') solver.add(z3.PbGe([(r, 1) for r in cell_to_rects[c]], 1)) elif cover_constraints == 'incomparable': solver.add_soft(z3.PbEq([(r, 1) for r in cell_to_rects[c]], 1), 1, 'cover') else: raise AssertionError('bad cover_constraints: ' + cover_constraints) if corner_constraints != 'ignore': # skip the loop if we wouldn't add anyway for c in puzzle.cells: for r1 in lr_to_rects.get(c, []): for r2 in ll_to_rects.get(Point(c.r, c.c + 1), []): for r3 in ur_to_rects.get(Point(c.r + 1, c.c), []): for r4 in ul_to_rects.get(Point(c.r + 1, c.c + 1), []): if corner_constraints == 'hard': solver.add( z3.PbLe([(r1, 1), (r2, 1), (r3, 1), (r4, 1)], 3)) elif corner_constraints == 'soft': solver.add_soft( z3.PbLe([(r1, 1), (r2, 1), (r3, 1), (r4, 1)], 3), 1, 'corner') else: raise AssertionError( 'bad corner_constraints: ' + corner_constraints) if reflex_three_corners: holes = [ Point(r, c) for r in range(puzzle.rmax) for c in range(puzzle.cmax) if Point(r, c) not in puzzle.cells ] for c in holes: for r2 in ll_to_rects.get(Point(c.r, c.c + 1), []): for r3 in ur_to_rects.get(Point(c.r + 1, c.c), []): for r4 in ul_to_rects.get(Point(c.r + 1, c.c + 1), []): if corner_constraints == 'hard': solver.add( z3.PbLe([(r2, 1), (r3, 1), (r4, 1)], 2)) elif corner_constraints == 'soft': solver.add_soft( z3.PbLe([(r2, 1), (r3, 1), (r4, 1)], 2), 1, 'corner') else: raise AssertionError( 'bad corner_constraints: ' + corner_constraints) for r2 in lr_to_rects.get(Point(c.r, c.c - 1), []): for r3 in ur_to_rects.get(Point(c.r + 1, c.c - 1), []): for r4 in ul_to_rects.get(Point(c.r + 1, c.c), []): if corner_constraints == 'hard': solver.add( z3.PbLe([(r2, 1), (r3, 1), (r4, 1)], 2)) elif corner_constraints == 'soft': solver.add_soft( z3.PbLe([(r2, 1), (r3, 1), (r4, 1)], 2), 1, 'corner') else: raise AssertionError( 'bad corner_constraints: ' + corner_constraints) for r2 in lr_to_rects.get(Point(c.r - 1, c.c), []): for r3 in ll_to_rects.get(Point(c.r - 1, c.c + 1), []): for r4 in ul_to_rects.get(Point(c.r, c.c + 1), []): if corner_constraints == 'hard': solver.add( z3.PbLe([(r2, 1), (r3, 1), (r4, 1)], 2)) elif corner_constraints == 'soft': solver.add_soft( z3.PbLe([(r2, 1), (r3, 1), (r4, 1)], 2), 1, 'corner') else: raise AssertionError( 'bad corner_constraints: ' + corner_constraints) for r2 in lr_to_rects.get(Point(c.r - 1, c.c - 1), []): for r3 in ll_to_rects.get(Point(c.r - 1, c.c), []): for r4 in ur_to_rects.get(Point(c.r, c.c - 1), []): if corner_constraints == 'hard': solver.add( z3.PbLe([(r2, 1), (r3, 1), (r4, 1)], 2)) elif corner_constraints == 'soft': solver.add_soft( z3.PbLe([(r2, 1), (r3, 1), (r4, 1)], 2), 1, 'corner') else: raise AssertionError( 'bad corner_constraints: ' + corner_constraints) if forced_rects: solver.add(z3.And(list(rect_to_var[r] for r in forced_rects))) while solver.check() == z3.sat: model = solver.model() soln: List[Rect] = [] blockers = [] for d in model.decls(): if z3.is_true(model[d]): soln.append(str_to_rect(d.name())) # Build a constraint that prevents this model from being returned again. # https://stackoverflow.com/a/11869410/3614835 blockers.append(d() != model[d]) soln.sort() yield soln solver.add(z3.Or(blockers))
def _loop_cons(self): # Every corner must have either no edges or exactly two edges for point, c in self.iter_corners(): vars = tuple([(v, 1) for v in c.values()]) yield z3.PbEq(vars, 0) | z3.PbEq(vars, 2)
def __init__(self, bool_expressions: Sequence[expr.BooleanExpression], weights: Sequence[float], value: float): super().__init__(expr.WeightedSum(bool_expressions, weights), expr.NumericValue(value)) self.z3_expr = z3.PbEq([(e.get_z3(), w) for (e, w) in zip(bool_expressions, weights)], value)