Example #1
0
    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
Example #2
0
    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)
Example #3
0
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
Example #4
0
 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)
Example #5
0
 def to_z3_(self):
     return z3.PbEq(*[a.to_z3_() for a in self.children], 1)
Example #6
0
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")
Example #7
0
 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))
Example #8
0
 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))
Example #10
0
 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)
Example #11
0
 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)