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