Ejemplo n.º 1
0
def get_constraint(idpool: IDPool, id2varmap,
                   constraint: tConstraint) -> CNFPlus:
    """ Generate formula for a given cardinality constraint"""
    validate_constraint(constraint)
    lits = []
    for ta in constraint.tas:
        t1 = tuple((constraint.course_name, ta))
        if t1 not in id2varmap.keys():
            id1 = idpool.id(t1)
            id2varmap[t1] = id1
        else:
            id1 = id2varmap[t1]
        lits.append(id1)

    if constraint.type == tCardType.GREATEROREQUALS:
        if (constraint.bound == 1):
            cnf = CNFPlus()
            cnf.append(lits)
        elif (constraint.bound > len(lits)):
            msg = "Num TAs available for constraint:" + constraint.con_str + "is more than the bound in the constraint. \
            Changing the bound to " + str(len(lits)) + ".\n"
            print(msg, file=sys.stderr)
            constraint.bound = len(lits)

        cnf = CardEnc.atleast(lits, vpool=idpool, bound=constraint.bound)
    elif constraint.type == tCardType.LESSOREQUALS:
        cnf = CardEnc.atmost(lits, vpool=idpool, bound=constraint.bound)
    return cnf
Ejemplo n.º 2
0
    def to_cnf(self, x_vars, cnf):
        d_vars = [[cnf.nv + 1 + j + (j * i) for j in range(self.out_dim)]
                  for i in range(self.out_dim)]
        cnf.nv += self.out_dim**2

        for j in range(self.out_dim):
            for i in range(self.out_dim):
                E_ij = np.ceil((self.b[i] - self.b[j] + np.sum(self.A[i]) -
                                np.sum(self.A[j])) / 2)

                lits = [
                    x if (ai > 0 and aj < 0) else -x
                    for (x, ai, aj) in zip(x_vars, self.A[i], self.A[j])
                    if not (np.sign(ai) == np.sign(aj))
                ]

                ale = CardEnc.atleast(lits, np.ceil(E_ij / 2.0), cnf.nv,
                                      EncType.seqcounter)

                r = ale.nv  # TODO: Check assumption that this is "r(n, D)" of AAAI paper

                cnf.extend(ale.clauses)
                cnf.append([-r, d_vars[i][j]])
                cnf.append([r, -d_vars[i][j]])

        return d_vars
Ejemplo n.º 3
0
 def uniqueness_clauses(self, row, col):
     clauses = []
     for turn in range(2, self.num_turns):
         lits = [self.vpool.id((turn, row, col, state)) for state in STATES]
         clauses.extend(
             CardEnc.equals(lits, bound=1, vpool=self.vpool).clauses)
     return clauses
Ejemplo n.º 4
0
Archivo: fm.py Proyecto: sschnug/pysat
    def relax_core(self):
        """
            Relax and bound the core.
        """

        if len(self.core) > 1:
            # relaxing
            rels = []

            for clid in self.core:
                self.topv += 1
                rels.append(self.topv)
                self.soft[clid].append(self.topv)

            # creating a new cardinality constraint
            am1 = CardEnc.atmost(lits=rels,
                                 top_id=self.topv,
                                 encoding=self.cenc)

            for cl in am1.clauses:
                self.hard.append(cl)

            # only if minicard
            # (for other solvers am1.atmosts should be empty)
            for am in am1.atmosts:
                self.atm1.append(am)

            self.topv = am1.nv

        elif len(self.core) == 1:  # unit core => simply negate the clause
            self.remove_unit_core()
Ejemplo n.º 5
0
    def dominating_subset(self, k=1):
        """
        Check if there exists a vertex cover of, at most, k-vertices.
        Accepts as params:
        - n_color: number of color to check
        - verbose: whether or not print the process
        """
        if not self.edges():
            return []

        logging.info('\nCodifying SAT Solver...')
        solver = Solver(name='cd')
        vpool = IDPool()
        vertices_ids = [vpool.id(vertex) for vertex in self.vertices()]

        logging.info(' -> Codifying: Every vertex must be accessible')
        for vertex in self.vertices():
            solver.add_clause([vpool.id(vertex)] + [
                vpool.id(adjacent_vertex) for adjacent_vertex in self[vertex]
            ])

        logging.info(' -> Codifying: At most', k,
                     'vertices should be selected')

        cnf = CardEnc.atmost(lits=vertices_ids, bound=k, vpool=vpool)
        solver.append_formula(cnf)

        logging.info('Running SAT Solver...')
        return solver.solve()
Ejemplo n.º 6
0
def prepare_hitman(pixels, inputs, intervals, htype):
    """
        Initialize a hitting set enumerator.
    """

    if not pixels:
        pixels = sorted(range(len(inputs)))

    # new Hitman object
    h = Hitman(htype=htype)

    # first variables should be related with the elements of the sets to hit
    # that is why we are adding soft clauses first
    for p in pixels:
        for v in range(intervals):
            var = h.idpool.id(tuple([inputs[p], v]))
            h.oracle.add_clause([-var], 1)

    # at most one value per pixel can be selected
    for p in pixels:
        lits = [h.idpool.id(tuple([inputs[p], v])) for v in range(intervals)]
        cnf = CardEnc.atmost(lits, encoding=EncType.pairwise)

        for cl in cnf.clauses:
            h.oracle.add_clause(cl)

    return h
Ejemplo n.º 7
0
    def generate_medic_clauses(self):
        clauses = []

        for turn in range(self.num_turns):
            lits = [
                self.vpool.id((turn, row, col, IMMUNE_RECENTLY))
                for row in range(self.height) for col in range(self.width)
            ]
            clauses.extend(
                CardEnc.atmost(lits, bound=self.num_medics,
                               vpool=self.vpool).clauses)
        # TODO check case of 0 medics
        if self.num_medics == 0:
            return clauses

        for turn in range(self.num_turns - 1):
            for num_healthy in range(self.width * self.height):
                for healthy_tiles in itertools.combinations(
                        self.tiles, num_healthy):
                    sick_tiles = [
                        tile for tile in self.tiles
                        if tile not in healthy_tiles
                    ]
                    clause = []

                    for row, col in healthy_tiles:
                        clause.append(-self.vpool.id((turn, row, col,
                                                      HEALTHY)))

                    for row, col in sick_tiles:
                        clause.append(self.vpool.id((turn, row, col, HEALTHY)))

                    lits = [
                        self.vpool.id((turn + 1, row, col, IMMUNE_RECENTLY))
                        for row, col in healthy_tiles
                    ]
                    equals_clauses = CardEnc.equals(lits,
                                                    bound=min(
                                                        self.num_medics,
                                                        num_healthy),
                                                    vpool=self.vpool).clauses
                    for sub_clause in equals_clauses:
                        temp_clause = deepcopy(clause)
                        temp_clause += sub_clause
                        clauses.append(temp_clause)

        return clauses
Ejemplo n.º 8
0
 def second_turn_clauses(self, row, col):
     lits = [
         self.vpool.id((1, row, col, state)) for state in SECOND_TURN_STATES
     ]
     clauses = CardEnc.equals(lits, bound=1, vpool=self.vpool).clauses
     for state in STATES:
         if state not in SECOND_TURN_STATES:
             clauses.append([-self.vpool.id((0, row, col, state))])
     return clauses
Ejemplo n.º 9
0
    def hadar_dynamics(self):
        clauses = []

        for t in range(self.num_observations):
            clauses.extend(
                CardEnc.atmost(self.__get_I0_predicates(t),
                               bound=self.medics,
                               vpool=self.pool).clauses)

        if self.medics == 0:
            return clauses

        for t in range(self.num_observations - 1):
            for num_healthy in range(self.cols * self.rows):
                for healthy_tiles in itertools.combinations(
                        self.tiles, num_healthy):
                    sick_tiles = [
                        tile for tile in self.tiles
                        if tile not in healthy_tiles
                    ]
                    clause = []

                    for i, j in healthy_tiles:
                        clause.append(-self.obj2id[f"H_{i}_{j}^{t}"])

                    for i, j in sick_tiles:
                        clause.append(self.obj2id[f"H_{i}_{j}^{t}"])

                    lits = [
                        self.obj2id[f"I0_{i}_{j}^{t + 1}"]
                        for i, j in healthy_tiles
                    ]
                    equals_clauses = CardEnc.equals(lits,
                                                    bound=min(
                                                        self.medics,
                                                        num_healthy),
                                                    vpool=self.pool).clauses
                    for sub_clause in equals_clauses:
                        temp_clause = copy.deepcopy(clause)
                        temp_clause += sub_clause
                        clauses.append(temp_clause)

        return CNF(from_clauses=clauses)
Ejemplo n.º 10
0
    def first_step(self, first_field=None):
        self.solver.append_formula(
            CardEnc.equals(lits=list(range(1, self.game.board_dim**2 + 1)),
                           bound=self.game.num_mines,
                           encoding=EncType.native))

        if first_field:
            row, column = first_field
            self.newly_opened = self.open(self.game.board[row][column])
        else:
            self.newly_opened = self.open(self.get_random_field())
Ejemplo n.º 11
0
 def init_literal_meaning(self):
     counter = 1
     states = ["S2", "S1", "S0", "Q1", "Q0", "U", "I0", "I1", "H"]
     for turn, turn_state in enumerate(self.map):
         self.literal_meanings[turn] = {}
         for i, row in enumerate(turn_state):
             for j, item in enumerate(row):
                 for num in range(len(states)):
                     self.rev_literal_meanings[counter +
                                               num] = (turn, (i, j),
                                                       states[num])
                 self.literal_meanings[turn][(i, j)] = {
                     state: counter + num
                     for num, state in enumerate(states)
                 }
                 cnf = CardEnc.equals(lits=list(
                     self.literal_meanings[turn][(i, j)].values()),
                                      bound=1,
                                      encoding=EncType.pairwise)
                 for clause in cnf.clauses:
                     self.solver.add_clause(clause)
                 counter += len(states)
                 self.literal_meanings[turn][(i, j)]["D"] = counter
                 self.rev_literal_meanings[counter] = (turn, (i, j), "D")
                 counter += 1
                 self.literal_meanings[turn][(i, j)]["P"] = counter
                 self.rev_literal_meanings[counter] = (turn, (i, j), "P")
                 counter += 1
                 self.literal_meanings[turn][(i, j)]["Sick"] = counter
                 self.rev_literal_meanings[counter] = (turn, (i, j), "Sick")
                 counter += 1
                 not_s = ["Q1", "Q0", "U", "I0", "I1", "H", "Sick"]
                 literals = [
                     self.literal_meanings[turn][(i, j)][state]
                     for state in not_s
                 ]
                 cnf = CardEnc.equals(lits=literals,
                                      bound=1,
                                      encoding=EncType.pairwise)
                 for clause in cnf.clauses:
                     self.solver.add_clause(clause)
Ejemplo n.º 12
0
def test_atmostk():
    for l in range(5, 10):
        for k in range(2, l):
            for e in encs:
                cnf = CardEnc.atmost(lits=list(range(1, l + 1)), bound=k, encoding=getattr(EncType, e))

                # enumerating all models
                with MinisatGH(bootstrap_with=cnf) as solver:
                    for num, model in enumerate(solver.enum_models(), 1):
                        solver.add_clause([-l for l in model[:l]])

                assert num == sum([bincoeff(l, o + 1) for o in range(k)]) + 1, 'wrong number of models for AtMost-{0}-of-{1} ({2})'.format(k, l, e)
Ejemplo n.º 13
0
    def to_cnf(self, x_vars, cnf):

        y_vars = [cnf.nv + i + 1 for i in range(self.out_dim)]
        cnf.nv = max(y_vars)

        for i in range(self.out_dim):
            C_i = -(self.sigma[i] /
                    self.alpha[i]) * self.gamma[i] + self.mu[i] - self.b[i]
            if self.alpha[i] > 0:
                C_i = np.ceil(C_i)
            elif self.alpha[i] < 0:
                C_i = np.floor(C_i)

            print(self.sigma[i], self.alpha[i], self.gamma[i], self.mu[i],
                  self.b[i])
            print(C_i)
            print(np.sum(self.A[i]))

            C_ = np.ceil(C_i / 2 + np.sum(self.A[i]) / 2)

            n_nega = sum(self.A[i] > 0)

            print(C_, n_nega)

            D = int(C_ + n_nega)

            print(D)

            lits = [x if a > 0 else -x for (x, a) in zip(x_vars, self.A[i])]

            amo = CardEnc.atmost(lits, D - 1, cnf.nv, EncType.seqcounter)

            r = amo.nv  # TODO: Check assumption that this is "r(n, D)" of AAAI paper

            print("Adding", len(amo.clauses), "AMO clauses")

            cnf.extend(amo.clauses)
            cnf.append([-r, y_vars[i]])
            cnf.append([r, -y_vars[i]])

            # ale = CardEnc.atleast(lits, D, cnf.nv, EncType.seqcounter)

            # r = ale.nv # TODO: Check assumption that this is "r(n, D)" of AAAI paper

            # print("Adding", len(ale.clauses), "ALE clauses")

            print(len(cnf.clauses))

            # cnf.extend(ale.clauses)
            # cnf.append([-r,  y_vars[i]])
            # cnf.append([ r, -y_vars[i]])
        return y_vars
Ejemplo n.º 14
0
 def atmost(self, lits, bound=1, encoding=EncType.seqcounter):
     """
         **Added in SugarRush**\n
         Uses :meth:`pysat.card.CardEnc.atmost`.
         Adds automatic bookkeeping of literals.
     """
     cnf = CardEnc.atmost(lits=lits,
                          bound=bound,
                          encoding=encoding,
                          top_id=self._top_id())
     clauses = cnf.clauses
     self._add_lits_from(clauses)
     return clauses
Ejemplo n.º 15
0
    def naveh_dynamics(self):
        clauses = []

        for t in range(1, self.num_observations):
            clauses.extend(
                CardEnc.atmost(self.__get_Q0_predicates(t),
                               bound=self.police,
                               vpool=self.pool).clauses)

        if self.police == 0:
            return clauses

        for t in range(self.num_observations - 1):
            for num_sick in range(self.cols * self.rows):
                for sick_tiles in itertools.combinations(self.tiles, num_sick):
                    healthy_tiles = [
                        tile for tile in self.tiles if tile not in sick_tiles
                    ]
                    for sick_state_perm in itertools.combinations_with_replacement(
                            self.possible_sick_states(t), num_sick):
                        clause = []

                        for (i, j), state in zip(sick_tiles, sick_state_perm):
                            clause.append(-self.obj2id[f"{state}_{i}_{j}^{t}"])
                        for i, j in healthy_tiles:
                            for state in self.possible_sick_states(t):
                                clause.append(
                                    self.obj2id[f"{state}_{i}_{j}^{t}"])

                        equals_clauses = CardEnc.equals(
                            lits=self.__get_Q0_predicates(t + 1),
                            bound=min(self.police, num_sick),
                            vpool=self.pool).clauses
                        for sub_clause in equals_clauses:
                            temp_clause = copy.deepcopy(clause)
                            temp_clause += sub_clause
                            clauses.append(temp_clause)

        return CNF(from_clauses=clauses)
Ejemplo n.º 16
0
    def relax_core(self):
        """
            Relax and bound the core.

            After unsatisfiable core splitting, this method is called. If the
            core contains only one clause, i.e. this clause cannot be satisfied
            together with the hard clauses of the formula, the formula gets
            augmented with the negation of the clause (see
            :func:`remove_unit_core`).

            Otherwise (if the core contains more than one clause), every clause
            :math:`c` of the core is *relaxed*. This means a new *relaxation
            literal* is added to the clause, i.e. :math:`c\gets c\\vee r`,
            where :math:`r` is a fresh (unused) relaxation variable. After the
            clauses get relaxed, a new cardinality encoding is added to the
            formula enforcing the sum of the new relaxation variables to be not
            greater than 1, :math:`\sum_{c\in\phi}{r\leq 1}`, where
            :math:`\phi` denotes the unsatisfiable core.
        """

        if len(self.core) > 1:
            # relaxing
            rels = []

            for clid in self.core:
                self.topv += 1
                rels.append(self.topv)
                self.soft[clid].append(self.topv)

            # creating a new cardinality constraint
            am1 = CardEnc.atmost(lits=rels,
                                 top_id=self.topv,
                                 encoding=self.cenc)

            for cl in am1.clauses:
                self.hard.append(cl)

            # only if minicard
            # (for other solvers am1.atmosts should be empty)
            for am in am1.atmosts:
                self.atm1.append(am)

            self.topv = am1.nv

        elif len(self.core) == 1:  # unit core => simply negate the clause
            self.remove_unit_core()
Ejemplo n.º 17
0
def test_atmost1():
    encs = list(
        filter(lambda name: not name.startswith('__') and name != 'native',
               dir(EncType)))
    for l in range(10, 20):
        for e in encs:
            cnf = CardEnc.atmost(lits=list(range(1, l + 1)),
                                 bound=1,
                                 encoding=getattr(EncType, e))

            # enumerating all models
            with MinisatGH(bootstrap_with=cnf) as solver:
                for num, model in enumerate(solver.enum_models(), 1):
                    solver.add_clause([-l for l in model[:l]])

            assert num == l + 1, 'wrong number of models for AtMost-1-of-{0} ({1})'.format(
                l, e)
Ejemplo n.º 18
0
def test_atmost():
    vp = IDPool()
    n = 20
    b = 50
    assert n <= b

    lits = [vp.id(v) for v in range(1, n + 1)]
    top = vp.top

    G = CardEnc.atmost(lits, b, vpool=vp)

    assert len(G.clauses) == 0

    try:
        assert vp.top >= top
    except AssertionError as e:
        print(f"\nvp.top = {vp.top} (expected >= {top})\n")
        raise e
Ejemplo n.º 19
0
    def coloring(self, n_color):
        """
        Returns whether or not there exists a vertex coloring
        of, at most, n_color colors.

        Accepts one param:
        - n_color: number of color to check

        Might raise ValueError exception.
        """
        if n_color < 0:
            raise ValueError('Number of colors must be positive integer')

        if n_color == 0:
            return not bool(self.vertices())

        logging.info('\nCodifying SAT Solver...')
        solver = Solver(name='cd')
        vpool = IDPool()

        logging.info(
            ' -> Codifying: Every vertex must have a color, and only one')
        for vertex in self.vertices():
            cnf = CardEnc.equals(lits=[
                vpool.id('{}color{}'.format(vertex, color))
                for color in range(n_color)
            ],
                                 vpool=vpool,
                                 encoding=0)

            solver.append_formula(cnf)

        logging.info(
            ' -> Codifying: No two neighbours can have the same color')
        for vertex in self.vertices():
            for neighbour in self[vertex]:
                for color in range(n_color):
                    solver.add_clause([
                        -vpool.id('{}color{}'.format(vertex, color)),
                        -vpool.id('{}color{}'.format(neighbour, color))
                    ])

        logging.info('Running SAT Solver...')
        return solver.solve()
Ejemplo n.º 20
0
 def unique_tile_dynamics(self):
     clauses = []
     for i in range(self.rows):
         for j in range(self.cols):
             for t in range(self.t_max + 1):  # including all
                 legal_states = [
                     self.pool.obj2id[f"U_{i}_{j}^{t}"],
                     self.pool.obj2id[f"I0_{i}_{j}^{t}"],
                     self.pool.obj2id[f"I_{i}_{j}^{t}"],
                     self.pool.obj2id[f"S0_{i}_{j}^{t}"],
                     self.pool.obj2id[f"S1_{i}_{j}^{t}"],
                     self.pool.obj2id[f"S2_{i}_{j}^{t}"],
                     self.pool.obj2id[f"Q0_{i}_{j}^{t}"],
                     self.pool.obj2id[f"Q1_{i}_{j}^{t}"],
                     self.pool.obj2id[f"H_{i}_{j}^{t}"],
                 ]
                 clauses.extend(
                     CardEnc.equals(legal_states, 1, vpool=self.pool))
     return CNF(from_clauses=clauses)
Ejemplo n.º 21
0
 def __call__(self, k, vars_):
     assert max(map(abs, vars_)) < self.s1, 'max vars exceeded nvars_total'
     assert len(vars_) == len(set(vars_)), 'vars_ element must be unique'
     assert all(x > 0 for x in vars_), 'vars_ must be greater than 0'
     n = len(vars_)
     cnf = CardEnc.equals(range(1, n + 1), k)
     C = cnf.clauses
     # ns: number of auxiliary varibales in C
     ns = len(set(map(abs, itertools.chain.from_iterable(C)))) - n
     assert ns >= 0, (ns, n, C, k, vars_)
     sdiff = self.s1 - (n + 1)
     assert sdiff >= 0, (self.s1, n, sdiff, k, vars_)
     if sdiff:
         C = [[(x + sdiff) if x > n else x for x in r] for r in C]
         C = [[(x - sdiff) if x < -n else x for x in r] for r in C]
     tr = dict(zip(range(1, n + 1), vars_))
     tr.update(dict(zip(range(-1, -n - 2, -1), [-x for x in vars_])))
     C = [[tr.get(x, x) for x in r] for r in C]
     self.s1 += ns
     self.logger.debug('Updated self.s1 from %d to %d', self.s1 - ns,
                       self.s1)
     C = [[int(x) for x in r] for r in C]
     return C
Ejemplo n.º 22
0
    def make_constraint(self, row_index, col_index):
        """
        Compute equations for field (row_index, col_index)
        """

        adjacent_fields = self.game.get_adjacent_fields(row_index, col_index)
        adjacent_mines = self.game.get_adjacent_mines(row_index, col_index)

        assert adjacent_mines
        for field in adjacent_fields:
            if field not in self.current_adjacent_fields \
                    and self.game.board[field.row][field.column].covered \
                    and field not in self.mines:
                self.current_adjacent_fields.append(field)

        literals = []
        for field in adjacent_fields:
            if field.covered:
                literals.append(field.id)

        return CardEnc.equals(lits=literals,
                              bound=adjacent_mines,
                              encoding=EncType.native)
Ejemplo n.º 23
0
    def find_hamiltonian_path(self, check_cycle=False):
        """
        should it exists, find a Hamiltonian on
        current self. Otherwise return empty list.
        """
        if not self.edges():
            return []

        logging.info('Codifying SAT Solver...')
        length = len(self.vertices())
        solver = Solver(name='cd')
        names = {}
        vpool = IDPool()

        for integer, vertex in enumerate(self.vertices()):
            names[integer + 1] = vertex
        names[0] = names[length]

        for position_in_path in range(length):
            for vertex in range(1, length + 1):
                vpool.id(xvar(vertex, position_in_path))

        logging.info(' -> Codifying: All Positions occupied')
        for position_in_path in range(length):
            var_list = [
                vpool.id(xvar(vertex, position_in_path))
                for vertex in range(1, length + 1)
            ]

            cnf = CardEnc.equals(lits=var_list, vpool=vpool)
            solver.append_formula(cnf)

        logging.info(' -> Codifying: All vertex visited')
        for vertex in range(1, length + 1):
            var_list = [
                vpool.id(xvar(vertex, position_in_path))
                for position_in_path in range(length)
            ]

            cnf = CardEnc.equals(lits=var_list, vpool=vpool)
            solver.append_formula(cnf)

        logging.info(' -> Codifying: Adjacency Matrix')
        edges = self.edges()
        for vertex_a in range(1, length + 1):
            for vertex_b in range(vertex_a + 1, length + 1):
                if (names[vertex_a], names[vertex_b]) not in edges:
                    for position_in_path in range(length - 1):
                        solver.add_clause([
                            -vpool.id(xvar(vertex_a, position_in_path)),
                            -vpool.id(xvar(vertex_b, position_in_path + 1)),
                        ])
                        solver.add_clause([
                            -vpool.id(xvar(vertex_b, position_in_path)),
                            -vpool.id(xvar(vertex_a, position_in_path + 1)),
                        ])

                    if check_cycle:
                        solver.add_clause([
                            vpool.id(xvar(vertex_b, length - 1)),
                            vpool.id(xvar(vertex_a, 0))
                        ])
                        solver.add_clause([
                            vpool.id(xvar(vertex_b, 0)),
                            vpool.id(xvar(vertex_a, length - 1))
                        ])

        logging.info('Running SAT Solver...')
        solution = []
        if solver.solve():
            for index, variable in enumerate(solver.get_model()):
                if variable > 0 and index < length**2:
                    solution.append(names[variable % length])

        return solution
Ejemplo n.º 24
0
def make_formula(n_police, n_medics, n_rows, n_cols, n_time):
    states = {'U', 'H', 'S', 'I', 'Q'}
    variables = {}
    formula = CNF()
    var_pool = IDPool()
    for t in range(n_time):
        for r in range(n_rows):
            for c in range(n_cols):
                for s in states:
                    variables[(r, c), t,
                              s] = var_pool.id(f'({r}, {c}), {t}, {s}')
                variables[(r, c), t, 'P'] = var_pool.id(
                    f'({r}, {c}), {t}, P')  # Police were used
                variables[(r, c), t, 'M'] = var_pool.id(
                    f'({r}, {c}), {t}, M')  # Medics were used
                variables[(r, c), t, 'SS'] = var_pool.id(
                    f'({r}, {c}), {t}, SS')  # Stayed sick from last time
    for t in range(n_time):
        formula.extend(
            CardEnc.atmost([
                variables[(r, c), t, 'P'] for r in range(n_rows)
                for c in range(n_cols)
            ],
                           bound=n_police,
                           vpool=var_pool))
        formula.extend(
            CardEnc.atmost([
                variables[(r, c), t, 'M'] for r in range(n_rows)
                for c in range(n_cols)
            ],
                           bound=n_medics,
                           vpool=var_pool))
        for r in range(n_rows):
            for c in range(n_cols):
                formula.extend(
                    CardEnc.equals([variables[(r, c), t, s] for s in states],
                                   vpool=var_pool))
                if t > 0:
                    formula.extend(
                        req_equiv([
                            -variables[(r, c), t - 1, 'Q'], variables[(r, c),
                                                                      t, 'Q']
                        ], [variables[(r, c), t, 'P']]))
                    formula.extend(
                        req_equiv([
                            -variables[(r, c), t - 1, 'I'], variables[(r, c),
                                                                      t, 'I']
                        ], [variables[(r, c), t, 'M']]))
                    formula.extend(
                        req_equiv([
                            variables[(r, c), t - 1, 'S'], variables[(r, c), t,
                                                                     'S']
                        ], [variables[(r, c), t, 'SS']]))
                    nearby_sick_condition = []
                    for r_, c_ in nearby(r, c, n_rows, n_cols):
                        nearby_sick_condition.append(variables[(r_, c_), t,
                                                               'SS'])
                        formula.extend(
                            req_imply([
                                variables[(r, c), t, 'SS'],
                                variables[(r_, c_), t - 1, 'H']
                            ], [
                                variables[(r_, c_), t, 'S'],
                                variables[(r_, c_), t, 'I']
                            ]))
                        # formula.extend(req_imply([variables[(r, c), t, 'SS']], [-variables[(r_, c_), t, 'H']]))
                    formula.extend(
                        req_imply([
                            variables[(r, c), t - 1, 'H'], variables[(r, c), t,
                                                                     'S']
                        ], nearby_sick_condition))
                if t + 1 < n_time:
                    formula.extend(
                        req_equiv([variables[(r, c), t, 'U']],
                                  [variables[(r, c), t + 1, 'U']]))
                    formula.extend(
                        req_imply([variables[(r, c), t, 'I']],
                                  [variables[(r, c), t + 1, 'I']]))
                    formula.extend(
                        req_imply([variables[(r, c), t + 1, 'S']], [
                            variables[(r, c), t, 'S'], variables[(r, c), t,
                                                                 'H']
                        ]))
                    formula.extend(
                        req_imply([variables[(r, c), t + 1, 'Q']], [
                            variables[(r, c), t, 'Q'], variables[(r, c), t,
                                                                 'S']
                        ]))
                if t == 0:
                    formula.append([-variables[(r, c), t, 'Q']])
                    formula.append([-variables[(r, c), t, 'I']])
                    if t + 1 < n_time:
                        formula.extend(
                            req_imply([variables[(r, c), t, 'S']], [
                                variables[(r, c), t + 1, 'S'],
                                variables[(r, c), t + 1, 'Q']
                            ]))
                        formula.extend(
                            req_imply([variables[(r, c), t, 'Q']],
                                      [variables[(r, c), t + 1, 'Q']]))
                    if t + 2 < n_time:
                        formula.extend(
                            req_imply([
                                variables[(r, c), t, 'S'],
                                variables[(r, c), t + 1, 'S']
                            ], [
                                variables[(r, c), t + 2, 'S'],
                                variables[(r, c), t + 2, 'Q']
                            ]))
                        formula.extend(
                            req_imply([
                                variables[(r, c), t, 'S'],
                                variables[(r, c), t + 1, 'Q']
                            ], [variables[(r, c), t + 2, 'Q']]))
                        formula.extend(
                            req_imply([variables[(r, c), t, 'Q']],
                                      [variables[(r, c), t + 2, 'H']]))
                    if t + 3 < n_time:
                        formula.extend(
                            req_imply([
                                variables[(r, c), t,
                                          'S'], variables[(r, c), t + 1, 'S'],
                                variables[(r, c), t + 2, 'S']
                            ], [variables[(r, c), t + 3, 'H']]))
                if 0 < t and t + 1 < n_time:
                    formula.extend(
                        req_imply([
                            -variables[(r, c), t - 1, 'S'], variables[(r, c),
                                                                      t, 'S']
                        ], [
                            variables[(r, c), t + 1, 'S'],
                            variables[(r, c), t + 1, 'Q']
                        ]))
                    formula.extend(
                        req_imply([
                            -variables[(r, c), t - 1, 'Q'], variables[(r, c),
                                                                      t, 'Q']
                        ], [variables[(r, c), t + 1, 'Q']]))
                if 0 < t and t + 2 < n_time:
                    formula.extend(
                        req_imply([
                            -variables[(r, c), t - 1, 'S'],
                            variables[(r, c), t, 'S'], variables[(r, c), t + 1,
                                                                 'S']
                        ], [
                            variables[(r, c), t + 2, 'S'],
                            variables[(r, c), t + 2, 'Q']
                        ]))
                    formula.extend(
                        req_imply([
                            -variables[(r, c), t - 1, 'S'],
                            variables[(r, c), t, 'S'], variables[(r, c), t + 1,
                                                                 'Q']
                        ], [variables[(r, c), t + 2, 'Q']]))
                    formula.extend(
                        req_imply([
                            -variables[(r, c), t - 1, 'Q'], variables[(r, c),
                                                                      t, 'Q']
                        ], [variables[(r, c), t + 2, 'H']]))
                if 0 < t and t + 3 < n_time:
                    formula.extend(
                        req_imply([
                            -variables[(r, c), t - 1, 'S'], variables[(r, c),
                                                                      t, 'S'],
                            variables[(r, c), t + 1,
                                      'S'], variables[(r, c), t + 2, 'S']
                        ], [variables[(r, c), t + 3, 'H']]))
    return var_pool, formula
Ejemplo n.º 25
0
def buildClausesAndEffects(b, nRows, nCols, nPolice, nMedics, actionToIndex,
                           atomsToIndex):
    actionClauses = []
    mapIndicesList = buildMapIndices(nRows, nCols)
    DeseaseSpread = AllPossibleInfectionIndices(nRows, nCols)
    actionEffectsAtPreviousT = {}
    for roundT in range(b - 1):
        actionEffectsAtT = {}
        for idx in mapIndicesList:
            for p in range(nPolice):
                actionIndex = actionToIndex[("Q", p, roundT, idx)]
                curClauses, curPre, curAdd, curDel = modelAgentQAction(
                    b, actionIndex, roundT, idx, atomsToIndex)
                actionEffectsAtT[actionIndex] = (curPre, curAdd, curDel)
                actionClauses += curClauses
            for m in range(nMedics):
                actionIndex = actionToIndex[("V", m, roundT, idx)]
                curClauses, curPre, curAdd, curDel = modelAgentVAction(
                    b, actionIndex, roundT, idx, atomsToIndex)
                actionEffectsAtT[actionIndex] = (curPre, curAdd, curDel)
                actionClauses += curClauses
        for idx in mapIndicesList:  # at most 1 quarantine and at most 1 vaccination in each place in the map
            qvars = [
                actionToIndex[("Q", p, roundT, idx)] for p in range(nPolice)
            ]
            ivars = [
                actionToIndex[("V", m, roundT, idx)] for m in range(nMedics)
            ]
            actionClauses += CardEnc.atmost(lits=qvars,
                                            encoding=EncType.pairwise,
                                            bound=1).clauses
            actionClauses += CardEnc.atmost(lits=ivars,
                                            encoding=EncType.pairwise,
                                            bound=1).clauses
        for p in range(
                nPolice):  # each police can be used at most once each turn
            qvars = [
                actionToIndex[("Q", p, roundT, idx)] for idx in mapIndicesList
            ]
            actionClauses += CardEnc.atmost(lits=qvars,
                                            encoding=EncType.pairwise,
                                            bound=1).clauses
        for m in range(
                nMedics):  # each medic can be used at most once each turn
            ivars = [
                actionToIndex[("V", m, roundT, idx)] for idx in mapIndicesList
            ]
            actionClauses += CardEnc.atmost(lits=ivars,
                                            encoding=EncType.pairwise,
                                            bound=1).clauses
        for p in range(nPolice):
            actionClauses += [[
                -actionToIndex[("Q", p, roundT, idx)],
                atomsToIndex[("Q", p, roundT)]
            ] for idx in mapIndicesList]
            actionClauses += [[-atomsToIndex[("Q", p, roundT)]] + [
                actionToIndex[("Q", p, roundT, idx)] for idx in mapIndicesList
            ]]
        for m in range(nMedics):
            actionClauses += [[
                -actionToIndex[("V", m, roundT, idx)],
                atomsToIndex[("V", m, roundT)]
            ] for idx in mapIndicesList]
            actionClauses += [[-atomsToIndex[("V", m, roundT)]] + [
                actionToIndex[("V", m, roundT, idx)] for idx in mapIndicesList
            ]]

        for pair in DeseaseSpread:
            actionIndex = actionToIndex[(roundT, pair)]
            curClauses, curPre, curAdd, curDel = ModelInfection(
                b, roundT, pair, atomsToIndex, actionIndex)
            actionEffectsAtT[actionIndex] = (curPre, curAdd, curDel)
            actionClauses += curClauses

        for atom, atomIdx in atomsToIndex.items():
            if atom[0] == roundT:
                add = atomsToIndex[(atom[0] + 1, atom[1], atom[2])]
                cur_state = atom[2]
                actionIdx = actionToIndex[atomIdx]
                actionEffectsAtT[actionToIndex[atomIdx]] = ([atomIdx], [add],
                                                            [])
                actionClauses.append([-actionIdx, atomIdx])
                if cur_state == "H":
                    required = [
                        atomsToIndex[("V", m, roundT)] for m in range(nMedics)
                    ]
                    for req in required:
                        actionClauses.append([req, -actionIdx])
                    row, col = atom[1][0], atom[1][1]
                    for neighbor in [(row - 1, col), (row + 1, col),
                                     (row, col - 1), (row, col + 1)]:
                        if 0 <= neighbor[0] <= nRows - 1 and 0 <= neighbor[
                                1] <= nCols - 1:
                            actionClauses.append([
                                -actionIdx,
                                atomsToIndex[(roundT + 1, neighbor, "Q")],
                                -atomsToIndex[(roundT, neighbor, "S")]
                            ])
                if cur_state == "Q":
                    if roundT >= 1:
                        preC = [
                            atomsToIndex[(roundT, atom[1], "Q")],
                            atomsToIndex[(roundT - 1, atom[1], "Q")]
                        ]
                        clause = [-actionIdx]
                        for pre in preC:
                            clause.append(-pre)
                        actionClauses.append(clause)
                if cur_state == "S":
                    required = [
                        atomsToIndex[("Q", p, roundT)] for p in range(nPolice)
                    ]
                    for req in required:
                        actionClauses.append([req, -actionIdx])
                    if roundT >= 2:
                        preC = [
                            atomsToIndex[(roundT, atom[1], "S")],
                            atomsToIndex[(roundT - 1, atom[1], "S")],
                            atomsToIndex[(roundT - 2, atom[1], "S")]
                        ]
                        clause = [-actionIdx]
                        for pre in preC:
                            clause.append(-pre)
                        actionClauses.append(clause)

        if roundT >= 2:
            for idx in mapIndicesList:
                actionIndex = actionToIndex[("heal", roundT, idx)]
                curClauses, curPre, curAdd, curDel = ModelHealing(
                    roundT, idx, atomsToIndex, actionIndex)
                actionEffectsAtT[actionIndex] = (curPre, curAdd, curDel)
                actionClauses += curClauses

        if roundT >= 1:
            for idx in mapIndicesList:
                actionIndex = actionToIndex[("exitQ", roundT, idx)]
                curClauses, curPre, curAdd, curDel = ModelExitQ(
                    roundT, idx, atomsToIndex, actionIndex)
                actionEffectsAtT[actionIndex] = (curPre, curAdd, curDel)
                actionClauses += curClauses

        interferClauses = BuildInterferClauses(actionEffectsAtT)
        actionClauses += interferClauses
        if roundT >= 1:
            factAchieveClauses = BuildFactAchieveClauses(
                actionEffectsAtPreviousT, atomsToIndex, roundT)
            actionClauses += factAchieveClauses

        actionEffectsAtPreviousT = actionEffectsAtT

    if actionEffectsAtPreviousT != {}:
        factAchieveClauses = BuildFactAchieveClauses(actionEffectsAtPreviousT,
                                                     atomsToIndex, b - 1)
        actionClauses += factAchieveClauses

    return actionClauses
Ejemplo n.º 26
0
def closest_string(bitarray_list, distance=4):
    """
    Return if a bitarray exists of distance at most 'distance'.
    Use example:

    s1=bitarray('0010')
    s2=bitarray('0011')
    closest_string([s1,s2], distance=0)
    > False
    closest_string([s1,s2], distance=2)
    > True
    """
    if distance < 0:
        raise ValueError('Distance must be positive integer')

    logging.info('\nCodifying SAT Solver...')

    length = max(len(bit_arr) for bit_arr in bitarray_list)
    solver = Solver(name='mcm')
    vpool = IDPool()
    local_list = bitarray_list.copy()

    logging.info(' -> Codifying: normalizing strings')
    for index, bitarr in enumerate(bitarray_list):
        aux = (length - len(bitarr)) * bitarray('0')
        local_list[index] = bitarr + aux

    logging.info(' -> Codifying: imposing distance condition')
    for index, word in enumerate(local_list):
        for pos in range(length):
            vpool.id(ut.xvar(index, pos))

    for pos in range(length):
        vpool.id(ut.yvar(pos))

    for index, word in enumerate(local_list):
        for pos in range(length):
            vpool.id(ut.zvar(index, pos))

    for index, word in enumerate(local_list):
        for pos in range(length):
            for clause in ut.triple_equal(ut.xvar(index, pos),
                                          ut.yvar(pos),
                                          ut.zvar(index, pos),
                                          vpool=vpool):
                solver.add_clause(clause)
        cnf = CardEnc.atleast(
            lits=[vpool.id(ut.zvar(index, pos)) for pos in range(length)],
            bound=length - distance,
            vpool=vpool)
        solver.append_formula(cnf)

    logging.info(' -> Codifying: Words Value')
    assumptions = []
    for index, word in enumerate(local_list):
        for pos in range(length):
            assumptions += [
                vpool.id(ut.xvar(index, pos)) * (-1)**(not word[pos])
            ]

    logging.info('Running SAT Solver...')
    return solver.solve(assumptions=assumptions)
Ejemplo n.º 27
0
    def generate_police_clauses(self):
        clauses = []

        for turn in range(1, self.num_turns):
            lits = [
                self.vpool.id((turn, row, col, QUARANTINED_1))
                for row in range(self.height) for col in range(self.width)
            ]
            clauses.extend(
                CardEnc.atmost(lits, bound=self.num_police,
                               vpool=self.vpool).clauses)
        # TODO check case of 0 policemen
        if self.num_police == 0:
            return clauses

        for turn in range(self.num_turns - 1):
            for num_sick in range(self.width * self.height):
                for sick_tiles in itertools.combinations(self.tiles, num_sick):
                    healthy_tiles = [
                        tile for tile in self.tiles if tile not in sick_tiles
                    ]
                    # TODO don't iterate over all sick states
                    for sick_state_perm in \
                            itertools.combinations_with_replacement(self.possible_sick_states(turn), num_sick):
                        clause = []

                        for (row, col), state in zip(sick_tiles,
                                                     sick_state_perm):
                            clause.append(-self.vpool.id((turn, row, col,
                                                          state)))
                        for row, col in healthy_tiles:
                            for state in self.possible_sick_states(turn):
                                clause.append(
                                    self.vpool.id((turn, row, col, state)))

                        lits = [
                            self.vpool.id((turn + 1, row, col, QUARANTINED_1))
                            for row, col in sick_tiles
                        ]
                        equals_clauses = CardEnc.equals(
                            lits,
                            bound=min(self.num_police, num_sick),
                            vpool=self.vpool).clauses
                        for sub_clause in equals_clauses:
                            temp_clause = deepcopy(clause)
                            temp_clause += sub_clause
                            clauses.append(temp_clause)

                        # if num_sick <= self.num_police:
                        #     for (row, col) in sick_tiles:
                        #         temp_clause = deepcopy(clause)
                        #         temp_clause.append(self.vpool.id((turn+1, row, col, QUARANTINED_1)))
                        #         clauses.extend(temp_clause)
                        #
                        #     # for (row, col) in healthy_tiles:
                        #     #     temp_clause = deepcopy(clause)
                        #     #     temp_clause.append(-self.vpool.id((turn+1, row, col, QUARANTINED_1)))
                        #     #     clauses.extend(temp_clause)
                        #
                        # else:
                        #     lits = [self.vpool.id((turn+1, row, col, QUARANTINED_1))
                        #             for row in range(self.height)
                        #             for col in range(self.width)]
                        #     equals_clauses = CardEnc.equals(lits, bound=self.num_police, vpool=self.vpool)
                        #
                        #     for sub_clause in equals_clauses.clauses():
                        #         temp_clause = deepcopy(clause)
                        #         temp_clause += sub_clause
                        #         clauses.extend(temp_clause)
        return clauses
Ejemplo n.º 28
0
    def solve(self):
        n_orange = len([h for h in self.hexagons if h.tpe == HexagonTypes.ORANGE])
        use_total = False
        while True:
            with Solver() as s:

                if use_total:
                    assert self.total_n_blue is not None, 'Need total number of blue to solve'
                    is_orange = [h.var_id for h in self.hexagons if h.tpe == HexagonTypes.ORANGE]
                    s.append_formula(CardEnc.equals(is_orange, self.total_n_blue - self.n_blue, vpool=id_pool).clauses)
                    print('Total active')

                assumptions = []
                formulas = []
                for h in self.hexagons:
                    formulas.append((h.var_id, h.constraint))
                    if h.constraint is not None:
                        s.append_formula(h.constraint)
                    if h.tpe == HexagonTypes.DARK:
                        assumptions.append(-h.var_id)
                    elif h.tpe in [HexagonTypes.BLUE, HexagonTypes.BLUE_W_NUMBER]:
                        assumptions.append(h.var_id)
                    # s.solve(assumptions=assumptions)

                for l in self.lines:
                    formulas.append((l.detection, h.constraint))
                    if l.constraint is not None:
                        s.append_formula(l.constraint)
                        # assert s.solve(assumptions=assumptions)

                if not s.solve(assumptions=assumptions):
                    raise RuntimeError('The boolean formula is unsatisfiable. This usually occurs when a game element is detected wrongly.')

                hexagons_to_update = []

                for h in reversed(self.hexagons):
                    if h.tpe != HexagonTypes.ORANGE:
                        continue
                    if not s.solve(assumptions=assumptions + [h.var_id]):
                        print(-h.var_id)
                        pyautogui.rightClick(*tuple(h.center + self.window_rect[:2]))
                        hexagons_to_update.append(h)
                    elif not s.solve(assumptions=assumptions + [-h.var_id]):
                        print(h.var_id)
                        pyautogui.leftClick(*tuple(h.center + self.window_rect[:2]))
                        hexagons_to_update.append(h)
                        self.n_blue += 1

                n_orange -= len(hexagons_to_update)
                if n_orange == 0:
                    break
                if self.total_n_blue is not None:
                    print('REMAINING', self.total_n_blue - self.n_blue)

                if len(hexagons_to_update) == 0:
                    if use_total:
                        raise RuntimeError('Can\'t solve the puzzle! Solution is ambiguous. Maybe a game element was detected wrongly.')
                    use_total = True
                    continue

                sleep(.3)

                start_time = time()

                while len(hexagons_to_update) > 0:
                    img = self.take_screenshot()
                    failed = []
                    for h in hexagons_to_update:
                        print('updating', h.var_id)
                        failed.append(not h.update(img))
                    hexagons_to_update = list(compress(hexagons_to_update, failed))
                    if time() - start_time >= 3:
                        raise RuntimeError('Failed to update state of game elements after 3 seconds of trying')

                for x in self.hexagons + self.lines:
                    x.check_if_constraint_trivial()
Ejemplo n.º 29
0
def encode_constraint(
        number,  # (int) number of hexagons in influence which should be blue
        curly,  # (bool) is curly
        dashed,  # (bool) is dashed
        influence,  # (List[Hexagon]) list of hexagons which are affected by the constraint
        circle  # (bool) if influenced hexagons are in a circle
):
    assert not circle or len(influence) <= 6
    assert len(influence) >= number

    # extract boolean variables
    literals = [h.var_id for h in influence]

    # if just a number
    if not curly and not dashed:
        return CardEnc.equals(lits=literals, bound=number, vpool=id_pool)

    # if dashed or curly
    # encode {number} in dnf
    n_vars = len(influence)
    dnf = []

    # this case needs special considerations since for hexagons gaps to count as such
    if (dashed or curly) and circle and n_vars < 6:
        # first find partitions
        # find start
        # remember: influence is sorted
        start = None
        for i in range(n_vars):
            # if there is a gap between influence[i] and influence[i - 1]
            if influence[i - 1] not in influence[i].influence or influence[i - 1].distance(influence[i]) > 1:
                start = i
                break
        assert start is not None

        # rotate such that start is at the beginning of influence
        influence = influence[start:] + influence[:start]
        partitions = [[influence[0]]]
        for i in influence[1:]:
            # if there is no gap between i and partitions[-1][-1]
            if i in partitions[-1][-1].influence and partitions[-1][-1].distance(i) == 1:
                partitions[-1].append(i)
            else:
                partitions.append([i])

        # encode {number} in dnf
        all_not = [-h.var_id for h in influence]
        start = 0
        for p in partitions:
            if len(p) < number:
                start += len(p)
                continue
            for i in range((len(p) - number + 1)):
                formula = all_not.copy()
                for j in range(number):
                    formula[start + i + j] = -formula[start + i + j]
                dnf.append(formula)
            start += len(p)

    else:
        # encode {number} in dnf
        all_not = [-h.var_id for h in influence]
        for i in range(n_vars if circle else (n_vars - number + 1)):
            formula = all_not.copy()
            for j in range(number):
                formula[(i + j) % n_vars] = -formula[(i + j) % n_vars]
            dnf.append(formula)

    if dashed:
        # -number- == not {number} and number
        cnf = CardEnc.equals(lits=literals, bound=number, vpool=id_pool)
        cnf.extend([[-v for v in c] for c in dnf])  # not {number} with DeMorgan
        return cnf

    if curly:
        # dnf to cnf
        cnf = [[]]
        for c in dnf:
            aux_var = id_pool.id(id_pool.top + 1)
            cnf[0].append(aux_var)
            for v in c:
                cnf.append([-aux_var, v])
        return CNF(from_clauses=cnf)
Ejemplo n.º 30
0
from pysat.formula import IDPool
from pysat.card import CardEnc

vp = IDPool()
n = 20
b = 50
assert n <= b

lits = [vp.id(v) for v in range(1, n + 1)]
top = vp.top

G = CardEnc.atmost(lits, b, vpool=vp)

assert len(G.clauses) == 0

try:
    assert vp.top >= top
except AssertionError as e:
    print(f"\nvp.top = {vp.top} (expected >= {top})\n")
    raise e