def get_formula(observations, decision_variables_p, p_to_atom, decision_variables_a, a_to_atom, no_police, no_medics, indices_list, state_codes, no_rounds): formula = CNF() initial_state_clauses = get_initial_state_clauses(observations, decision_variables_p, p_to_atom, state_codes, indices_list) action_precondition_clauses = get_action_precondition_clauses( decision_variables_a, p_to_atom, a_to_atom, no_police, no_medics, indices_list) action_interference_clauses = get_action_interference_clauses( decision_variables_a, a_to_atom, state_codes, no_rounds) fact_achievement_clauses = get_fact_achievement_clauses( decision_variables_p, p_to_atom, decision_variables_a, a_to_atom) permanent_clauses = get_permanent_clauses(p_to_atom, decision_variables_a, a_to_atom, no_police, no_medics, indices_list, state_codes) clauses = initial_state_clauses + action_precondition_clauses + action_interference_clauses +\ fact_achievement_clauses + permanent_clauses formula.extend(clauses) return formula
def get_clause(t, c): p = CNF() vpool = IDPool() # add numbers that are prefilled and add all boolean variables to the pool of used variables for i in range(n): for j in range(n): for z in range(1, n + 1): vpool.id('v{0}'.format(s(i, j, z))) if t[i][j] != "_": p.extend( PBEnc.equals(lits=[s(i, j, t[i][j])], bound=1, vpool=vpool).clauses) # ensure there is at least one value per square for x in range(n): for y in range(n): lits = list(map(lambda z: s(x, y, z), range(1, n + 1))) p.extend(PBEnc.atleast(lits=lits, bound=1, vpool=vpool).clauses) # ensure there exists only 1 of each value in each row and column for z in range(1, n + 1): for a in range(n): lits_row = list(map(lambda b: s(a, b, z), range(n))) lits_col = list(map(lambda b: s(b, a, z), range(n))) p.extend(PBEnc.equals(lits=lits_row, bound=1, vpool=vpool).clauses) p.extend(PBEnc.equals(lits=lits_col, bound=1, vpool=vpool).clauses) # ensure inequalities hold for x in c: (a, b) = x (i1, j1) = a (i2, j2) = b lits = list(map(lambda z: s(i1, j1, z), range(1, n + 1))) + \ list(map(lambda z: s(i2, j2, z), range(1, n + 1))) weights = list(range(1, n + 1)) + list(range(-1, -n - 1, -1)) p.extend( PBEnc.atleast(lits=lits, weights=weights, bound=1, vpool=vpool).clauses) return p
def encode(self, label, nof_terms): """ Encode the problem of computing a DS of size nof_terms. """ self.nof_terms = nof_terms self.nof_samps = len(self.samps) enc = CNF() # constraint 2 for j in range(1, self.nof_terms + 1): for r in range(1, self.nof_feats + 1): enc.append([self.pvar(j, r), self.nvar(j, r)]) # constraint 3 if len(self.labels) == 1: # distinguish one class from all the others other_labels = set(self.samps.keys()) else: # distinguish the classes under question only other_labels = set(self.labels) other_labels.remove(label) other_labels = sorted(other_labels) for j in range(1, self.nof_terms + 1): for lb in other_labels: for q in self.samps[lb]: cl = [] shift = 0 for r in range(1, self.nof_feats + 1): if r - 1 in self.data.vmiss[q]: # this feature is missing in q'th sample cl.append(-self.pvar(j, r)) cl.append(-self.nvar(j, r)) shift += 1 else: cl.append(-self.slvar(j, r, q, shift)) enc.append(cl) # constraint 4 for j in range(1, self.nof_terms + 1): for q in self.samps[label]: shift = 0 for r in range(1, self.nof_feats + 1): if r - 1 in self.data.vmiss[q]: # this feature is missing in q'th sample enc.append([self.pvar(j, r), -self.crvar(j, q + 1)]) enc.append([self.nvar(j, r), -self.crvar(j, q + 1)]) shift += 1 else: enc.append([ self.slvar(j, r, q, shift), -self.crvar(j, q + 1) ]) # symmetry breaking constraints if self.options.bsymm: self.add_bsymm(enc) # constraint 5 if self.options.accuracy == 100.0: for q in self.samps[label]: enc.append([ self.crvar(j, q + 1) for j in range(1, self.nof_terms + 1) ]) else: for q in self.samps[label]: cv = self.cvvar(q + 1) enc.append([-cv] + [ self.crvar(j, q + 1) for j in range(1, self.nof_terms + 1) ]) for j in range(1, self.nof_terms + 1): enc.append([-self.crvar(j, q + 1), cv]) cnum = int(self.options.accuracy * len(self.samps[label]) / 100.0) al = CardEnc.atleast( [self.cvvar(q + 1) for q in self.samps[label]], bound=cnum, top_id=enc.nv, encoding=self.options.enc) if al: enc.extend(al.clauses) # at most one value can be chosen for a feature for feats in six.itervalues(self.ffmap.dir): if len(feats) > 2: for j in range(1, self.nof_terms + 1): lits = [-self.pvar(j, r + 1) for r in feats] # atmost1 can be true onev = CardEnc.atmost(lits, top_id=enc.nv, encoding=self.options.enc) enc.extend(onev.clauses) # saving comments for j in range(1, self.nof_terms + 1): for r in range(1, self.nof_feats + 1): enc.comments.append('c p({0}, {1}) => v{2}'.format( j, r, self.pvar(j, r))) enc.comments.append('c n({0}, {1}) => v{2}'.format( j, r, self.nvar(j, r))) for q in range(len(self.data.samps)): enc.comments.append('c cr({0}, {1}) => v{2}'.format( j, q + 1, self.crvar(j, q + 1))) for n, f in zip(self.data.names[:-1], self.data.feats[:-1]): for v in f: if self.data.fvmap.dir[(n, v)] > 0: enc.comments.append('c {0} = {1} => positive'.format(n, v)) else: enc.comments.append('c {0} = {1} => negative'.format(n, v)) return enc
def encode(self, label, nof_lits=1): """ Encode the problem of computing a DS of size nof_lits. """ self.nof_lits = nof_lits self.nof_samps = len(self.data.samps) self.nof_labls = len(self.labels) if len(self.labels) == 1: # distinguish one class from all the others other_labels = set(self.samps.keys()) else: # distinguish the classes under question only other_labels = set(self.labels) other_labels.remove(label) other_labels = sorted(other_labels) for j in range(1, self.nof_lits + 1): for r in range(1, self.nof_feats + 2): self.feat(j, r) for j in range(1, self.nof_lits + 1): self.sign(j) for j in range(1, self.nof_lits + 1): self.leaf(j) enc = CNF() # exactly one feature per node (including class/label) for j in range(1, self.nof_lits + 1): feats = [self.feat(j, r) for r in range(1, self.nof_feats + 2)] one = CardEnc.equals(lits=feats, vpool=self.idpool, encoding=self.options.enc) enc.extend(one) # at most one class/label per node for j in range(1, self.nof_lits + 1): labels = [self.label(j, z) for z in self.labels] am1 = CardEnc.atmost(lits=labels, vpool=self.idpool, encoding=self.options.enc) enc.extend(am1) # the model is split, # i.e. we currently target only rules for this concrete class enc.append([self.label(j, label)]) # leaf constraints enc.append([-self.leaf(1)]) # first node can't be a leaf enc.append([self.leaf(self.nof_lits)]) # last node should be a leaf # everything is reachable at node 1 for lb in self.labels: for i in self.samps[lb]: enc.append([self.reached(1, i + 1)]) # reachability propagation for j in range(1, self.nof_lits): for lb in self.labels: for i in self.samps[lb]: aij = self.agree(j, i + 1) cl, shift = [], 0 for r in range(1, self.nof_feats + 1): if r - 1 in self.data.vmiss[i]: # this feature is missing in i'th sample shift += 1 else: # a = self.agree(j, i + 1, r) # node j agrees with sample i on feature r f = self.feat(j, r) # feature r decided in node j s = self.sign(j) # polarity of node j if self.data.samps[i][r - 1 - shift] > 0: a = self.sets1(j, r) if a > enc.nv: enc.extend([[-a, f], [-a, s], [a, -f, -s]]) else: a = self.sets0(j, r) if a > enc.nv: enc.extend([[-a, f], [-a, -s], [a, -f, s]]) cl.append(a) enc.append([-aij] + cl) for l in cl: enc.append([aij, -l]) cur = self.reached(j, i + 1) # node j is reachable for sample i new = self.reached(j + 1, i + 1) # node j + 1 reachable for sample i enc.append([-new, self.leaf(j), cur]) enc.append([-new, self.leaf(j), aij]) enc.append([new, -self.leaf(j)]) enc.append([new, -cur, -aij]) # correctness of leafs for j in range(1, self.nof_lits + 1): for lb in self.labels: for i in self.samps[lb]: enc.append([ -self.leaf(j), -self.reached(j, i + 1), self.label(j, lb) ]) # coverage constraints for i in self.samps[label]: cl = [] for j in range(1, self.nof_lits + 1): cvar = self.covered(j, i + 1) enc.append([-cvar, self.leaf(j)]) enc.append([-cvar, self.reached(j, i + 1)]) enc.append([cvar, -self.leaf(j), -self.reached(j, i + 1)]) cl.append(cvar) enc.append(cl) # symmetry breaking if self.options.bsymm: self.add_bsymm(enc) for v, o in self.idpool.id2obj.items(): enc.comments.append('c {0} <=> {1}'.format(o, v)) return enc
def world_dynamics(self): # single tile dynamics dynamics = CNF() dynamics.extend(self.U_tile_dynamics()) dynamics.extend(self.I_tile_dynamics()) dynamics.extend(self.S_tile_dynamics()) dynamics.extend(self.Q_tile_dynamics()) dynamics.extend(self.H_tile_dynamics()) # exactly one state for each tile dynamics.extend(self.first_turn_rules()) dynamics.extend(self.unique_tile_dynamics()) # use all teams # dynamics.extend(self.use_all_medics_dynamics()) dynamics.extend(self.hadar_dynamics()) dynamics.extend(self.naveh_dynamics()) return dynamics
max_problem_var = nv enc = EncType.ladder # Encode constraints for each column for i in range(N**2): for v in range(N**2): # Atleast-1 clause col = [V[i][j][v] for j in range(N**2)] F.append(col) # Atmost-1 encoding card = pysat.card.CardEnc.atmost(lits=col, top_id=nv, bound=1, encoding=enc) F.extend(card.clauses) nv = card.nv # Encode constraints for each row for j in range(N**2): for v in range(N**2): # Atleast-1 clause row = [V[i][j][v] for i in range(N**2)] F.append(row) # Atmost-1 encoding card = pysat.card.CardEnc.atmost(lits=row, top_id=nv, bound=1, encoding=enc) F.extend(card.clauses) nv = card.nv
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 solve_problem2(input): num_rounds = len(input["observations"]) observations = input["observations"] num_police = input["police"] num_medics = input["medics"] rows = len(observations[0]) columns = len(observations[0][0]) c = CNF() # init unpopulated = set() for round in range(num_rounds): for i in range(rows): for j in range(columns): condition = observations[round][i][j] if condition == "?": # one condition only c.extend([[-get_k("H", round, i, j), -get_k("I", round, i, j)], [-get_k("H", round, i, j), -get_k("U", round, i, j)], [-get_k("H", round, i, j), -get_k("S", round, i, j, 1)], [-get_k("H", round, i, j), -get_k("S", round, i, j, 2)], [-get_k("H", round, i, j), -get_k("S", round, i, j, 3)], [-get_k("H", round, i, j), -get_k("Q", round, i, j, 1)], [-get_k("H", round, i, j), -get_k("Q", round, i, j, 2)], [-get_k("I", round, i, j), -get_k("U", round, i, j)], [-get_k("I", round, i, j), -get_k("S", round, i, j, 1)], [-get_k("I", round, i, j), -get_k("S", round, i, j, 2)], [-get_k("I", round, i, j), -get_k("S", round, i, j, 3)], [-get_k("I", round, i, j), -get_k("Q", round, i, j, 1)], [-get_k("I", round, i, j), -get_k("Q", round, i, j, 2)], [-get_k("U", round, i, j), -get_k("S", round, i, j, 1)], [-get_k("U", round, i, j), -get_k("S", round, i, j, 2)], [-get_k("U", round, i, j), -get_k("S", round, i, j, 3)], [-get_k("U", round, i, j), -get_k("Q", round, i, j, 1)], [-get_k("U", round, i, j), -get_k("Q", round, i, j, 2)], [-get_k("Q", round, i, j, 1), -get_k("S", round, i, j, 1)], [-get_k("Q", round, i, j, 1), -get_k("S", round, i, j, 2)], [-get_k("Q", round, i, j, 1), -get_k("S", round, i, j, 3)], [-get_k("Q", round, i, j, 1), -get_k("Q", round, i, j, 2)], [-get_k("Q", round, i, j, 2), -get_k("S", round, i, j, 1)], [-get_k("Q", round, i, j, 2), -get_k("S", round, i, j, 2)], [-get_k("Q", round, i, j, 2), -get_k("S", round, i, j, 3)], [-get_k("S", round, i, j, 1), -get_k("S", round, i, j, 2)], [-get_k("S", round, i, j, 1), -get_k("S", round, i, j, 3)], [-get_k("S", round, i, j, 2), -get_k("S", round, i, j, 3)], [get_k("H", round, i, j), get_k("I", round, i, j), get_k("U", round, i, j), get_k("Q", round, i, j, 1), get_k("Q", round, i, j, 2), get_k("S", round, i, j, 1), get_k("S", round, i, j, 2), get_k("S", round, i, j, 3)]]) if condition == "S": c.extend([[-get_k("H", round, i, j)], [-get_k("I", round, i, j)], [-get_k("U", round, i, j)], [-get_k("Q", round, i, j, 1)], [-get_k("Q", round, i, j, 2)], [get_k("S", round, i, j, 1), get_k("S", round, i, j, 2), get_k("S", round, i, j, 3)]]) if condition == "H": c.extend([[get_k("H", round, i, j)], [-get_k("I", round, i, j)], [-get_k("U", round, i, j)], [-get_k("Q", round, i, j, 1)], [-get_k("Q", round, i, j, 2)], [-get_k("S", round, i, j, 1)], [-get_k("S", round, i, j, 2)], [-get_k("S", round, i, j, 3)]]) if condition == "Q": c.extend([[-get_k("H", round, i, j)], [-get_k("I", round, i, j)], [-get_k("U", round, i, j)], [get_k("Q", round, i, j, 1), get_k("Q", round, i, j, 2)], [-get_k("S", round, i, j, 1)], [-get_k("S", round, i, j, 2)], [-get_k("S", round, i, j, 3)]]) if condition == "I": c.extend([[-get_k("H", round, i, j)], [get_k("I", round, i, j)], [-get_k("U", round, i, j)], [-get_k("Q", round, i, j, 1)], [-get_k("Q", round, i, j, 2)], [-get_k("S", round, i, j, 1)], [-get_k("S", round, i, j, 2)], [-get_k("S", round, i, j, 3)]]) if condition == "U": unpopulated.add((i, j)) if round == 0: if condition == '?': c.extend( [[get_k("S", 0, i, j, 3), get_k("H", 0, i, j), get_k("U", 0, i, j)], [-get_k("S", 0, i, j, 2)] , [-get_k("S", 0, i, j, 1)], [-get_k("Q", 0, i, j, 1)], [-get_k("Q", 0, i, j, 2)], [-get_k("I", 0, i, j)]]) if condition == "S": # if S, stay S for 3 rounds c.append([get_k("S", 0, i, j, 3)]) # all unpopulated are unpopulated in all rounds for pair in unpopulated: for round in range(num_rounds): c.extend([[-get_k("H", round, pair[0], pair[1])], [-get_k("I", round, pair[0], pair[1])], [get_k("U", round, pair[0], pair[1])], [-get_k("Q", round, pair[0], pair[1], 1)], [-get_k("Q", round, pair[0], pair[1], 2)], [-get_k("S", round, pair[0], pair[1], 1)], [-get_k("S", round, pair[0], pair[1], 2)], [-get_k("S", round, pair[0], pair[1], 3)]]) index = 1 k = v_id.vpool.id(1) # current action for round in range(num_rounds - 1): round_actions = [] for a in all_actions(observations[round], num_police, num_medics): affected = [] cur_map = [list(r) for r in observations[round]] stop = False l = [] # consists of preconditions and effects of current action num_Q = 0 num_I = 0 for pair in a: if observations[round + 1][pair[1][0]][pair[1][1]] != '?' and pair[0] != \ observations[round + 1][pair[1][0]][pair[1][1]]: stop = True break affected.append(pair[1]) cur_map[pair[1][0]][pair[1][1]] = pair[0] # updated map if action occurs, used for infection if pair[0] == 'Q': # precondition + effects l.append( [-k, get_k("S", round, pair[1][0], pair[1][1], 1), get_k("S", round, pair[1][0], pair[1][1], 2), get_k("S", round, pair[1][0], pair[1][1], 3)]) l.append([-k, get_k("Q", round + 1, pair[1][0], pair[1][1], 2)]) num_Q += 1 if pair[0] == 'I': l.append([-k, get_k("H", round, pair[1][0], pair[1][1])]) l.append([-k, get_k("I", round + 1, pair[1][0], pair[1][1])]) num_I += 1 if stop == True: index += 1 k = v_id.vpool.id(index) continue round_actions.append(k) c.extend(l) # if not all police or medics were used, then ? cant be H or S respectively if num_I < num_medics: for i in range(rows): for j in range(columns): if cur_map[i][j] == '?': c.append([-k, -get_k("H", round, i, j)]) if num_Q < num_police: for i in range(rows): for j in range(columns): if cur_map[i][j] == '?': c.extend([[-k, -get_k("S", round, i, j, 1)], [-k, -get_k("S", round, i, j, 2)], [-k, -get_k("S", round, i, j, 3)]]) for i in range(rows): for j in range(columns): if (i, j) in affected: continue else: condition = cur_map[i][j] # update S (depends on the chosen action) if condition == 'S': c.extend([[-k, -get_k("S", round, i, j, 3), get_k("S", round + 1, i, j, 2)], [-k, -get_k("S", round, i, j, 2), get_k("S", round + 1, i, j, 1)], [-k, -get_k("S", round, i, j, 1), get_k("H", round + 1, i, j)]]) continue if condition == '?': c.extend([[-k, -get_k("S", round, i, j, 3), get_k("S", round + 1, i, j, 2)], [-k, -get_k("S", round, i, j, 2), get_k("S", round + 1, i, j, 1)], [-k, -get_k("S", round, i, j, 1), get_k("H", round + 1, i, j)]]) # conditional infection neighbors_unknown = [] if condition == "H": # infect if there's an S if ((i - 1 >= 0 and cur_map[i - 1][j] == 'S') or (i + 1 < rows and cur_map[i + 1][j] == 'S') or (j - 1 >= 0 and cur_map[i][j - 1] == 'S') or (j + 1 < columns and cur_map[i][j + 1] == 'S')): c.extend([[-k, get_k("S", round + 1, i, j, 3)]]) continue # conditional infect if there's a ? (if ? is S) else: if i - 1 >= 0 and cur_map[i - 1][j] == '?': c.extend([[-k, -get_k("S", round, i - 1, j, 1), get_k("S", round + 1, i, j, 3)], [-k, -get_k("S", round, i - 1, j, 2), get_k("S", round + 1, i, j, 3)], [-k, -get_k("S", round, i - 1, j, 3), get_k("S", round + 1, i, j, 3)]]) neighbors_unknown.append((i - 1, j)) if i + 1 < rows and cur_map[i + 1][j] == '?': c.extend([[-k, -get_k("S", round, i + 1, j, 1), get_k("S", round + 1, i, j, 3)], [-k, -get_k("S", round, i + 1, j, 2), get_k("S", round + 1, i, j, 3)], [-k, -get_k("S", round, i + 1, j, 3), get_k("S", round + 1, i, j, 3)]]) neighbors_unknown.append((i + 1, j)) if j - 1 >= 0 and cur_map[i][j - 1] == '?': c.extend([[-k, -get_k("S", round, i, j - 1, 1), get_k("S", round + 1, i, j, 3)], [-k, -get_k("S", round, i, j - 1, 2), get_k("S", round + 1, i, j, 3)], [-k, -get_k("S", round, i, j - 1, 3), get_k("S", round + 1, i, j, 3)]]) neighbors_unknown.append((i, j - 1)) if j + 1 < columns and cur_map[i][j + 1] == '?': c.extend([[-k, -get_k("S", round, i, j + 1, 1), get_k("S", round + 1, i, j, 3)], [-k, -get_k("S", round, i, j + 1, 2), get_k("S", round + 1, i, j, 3)], [-k, -get_k("S", round, i, j + 1, 3), get_k("S", round + 1, i, j, 3)]]) neighbors_unknown.append((i, j + 1)) # if no one infected, stay H lst = [get_k("S", round, n[0], n[1], 1) for n in neighbors_unknown] + \ [get_k("S", round, n[0], n[1], 2) for n in neighbors_unknown] + \ [get_k("S", round, n[0], n[1], 3) for n in neighbors_unknown] lst.append(get_k("H", round + 1, i, j)) lst.append(-k) c.append(lst) elif condition == '?': # conditional infect if there's an S (if current ? is H) if ((i - 1 >= 0 and cur_map[i - 1][j] == 'S') or (i + 1 < rows and cur_map[i + 1][j] == 'S') or (j - 1 >= 0 and cur_map[i][j - 1] == 'S') or (j + 1 < columns and cur_map[i][j + 1] == 'S')): c.extend([[-k, -get_k("H", round, i, j), get_k("S", round + 1, i, j, 3)]]) continue # conditional infect of there's a ? (if current ? is H and other ? is S) else: if i - 1 >= 0 and cur_map[i - 1][j] == '?': c.extend([[-k, -get_k("S", round, i - 1, j, 1), -get_k("H", round, i, j), get_k("S", round + 1, i, j, 3)], [-k, -get_k("S", round, i - 1, j, 2), -get_k("H", round, i, j), get_k("S", round + 1, i, j, 3)], [-k, -get_k("S", round, i - 1, j, 3), -get_k("H", round, i, j), get_k("S", round + 1, i, j, 3)]]) neighbors_unknown.append((i - 1, j)) if i + 1 < rows and cur_map[i + 1][j] == '?': c.extend([[-k, -get_k("S", round, i + 1, j, 1), -get_k("H", round, i, j), get_k("S", round + 1, i, j, 3)], [-k, -get_k("S", round, i + 1, j, 2), -get_k("H", round, i, j), get_k("S", round + 1, i, j, 3)], [-k, -get_k("S", round, i + 1, j, 3), -get_k("H", round, i, j), get_k("S", round + 1, i, j, 3)]]) neighbors_unknown.append((i + 1, j)) if j - 1 >= 0 and cur_map[i][j - 1] == '?': c.extend([[-k, -get_k("S", round, i, j - 1, 1), -get_k("H", round, i, j), get_k("S", round + 1, i, j, 3)], [-k, -get_k("S", round, i, j - 1, 2), -get_k("H", round, i, j), get_k("S", round + 1, i, j, 3)], [-k, -get_k("S", round, i, j - 1, 3), -get_k("H", round, i, j), get_k("S", round + 1, i, j, 3)]]) neighbors_unknown.append((i, j - 1)) if j + 1 < columns and cur_map[i][j + 1] == '?': c.extend([[-k, -get_k("S", round, i, j + 1, 1), -get_k("H", round, i, j), get_k("S", round + 1, i, j, 3)], [-k, -get_k("S", round, i, j + 1, 2), -get_k("H", round, i, j), get_k("S", round + 1, i, j, 3)], [-k, -get_k("S", round, i, j + 1, 3), -get_k("H", round, i, j), get_k("S", round + 1, i, j, 3)]]) neighbors_unknown.append((i, j + 1)) # if no one infected, stay H (if ? is H) lst = [get_k("S", round, n[0], n[1], 1) for n in neighbors_unknown] + \ [get_k("S", round, n[0], n[1], 2) for n in neighbors_unknown] + \ [get_k("S", round, n[0], n[1], 3) for n in neighbors_unknown] lst.append(get_k("H", round + 1, i, j)) lst.append(-get_k("H", round, i, j)) lst.append(-k) c.append(lst) index += 1 k = v_id.vpool.id(index) lst = [] for r in round_actions: lst.append(r) for t in round_actions: if r > t: c.append([-r, -t]) c.append(lst) for i in range(rows): for j in range(columns): condition = observations[round][i][j] if condition == 'Q': c.extend([[-get_k("Q", round, i, j, 1), get_k("H", round + 1, i, j)], [-get_k("Q", round, i, j, 2), get_k("Q", round + 1, i, j, 1)]]) if condition == 'I': c.append([get_k("I", round + 1, i, j)]) if condition == '?': c.extend([[-get_k("I", round, i, j), get_k("I", round + 1, i, j)], [-get_k("Q", round, i, j, 1), get_k("H", round + 1, i, j)], [-get_k("Q", round, i, j, 2), get_k("Q", round + 1, i, j, 1)], [-get_k("U", round, i, j), get_k("U", round + 1, i, j)], [-get_k("U", round + 1, i, j), get_k("U", round, i, j)]]) return c
def solve_problem1(input): num_rounds = len(input["observations"]) observations = input["observations"] rows = len(observations[0]) columns = len(observations[0][0]) c = CNF() # init unpopulated = set() for round in range(num_rounds): for i in range(rows): for j in range(columns): condition = observations[round][i][j] if condition == "?": # one condition only c.extend([[-get_n("S", round, i, j), -get_n("H", round, i, j)], [-get_n("S", round, i, j), -get_n("U", round, i, j)], [-get_n("U", round, i, j), -get_n("H", round, i, j)], [get_n("S", round, i, j), get_n("H", round, i, j), get_n("U", round, i, j)]]) if condition == "S": c.extend([[get_n("S", round, i, j)], [-get_n("H", round, i, j)], [-get_n("U", round, i, j)]]) if condition == "H": c.extend([[-get_n("S", round, i, j)], [get_n("H", round, i, j)], [-get_n("U", round, i, j)]]) if condition == "U": unpopulated.add((i, j)) if round == 0: if condition == "S": # if S, stay S for 3 rounds c.append([get_n("S", 1, i, j)]) c.append([get_n("S", 2, i, j)]) c.append([get_n("H", 3, i, j)]) elif condition == "?": c.extend([[-get_n("S", 0, i, j), get_n("S", 1, i, j)], [-get_n("S", 0, i, j), get_n("S", 2, i, j)], [-get_n("S", 0, i, j), get_n("H", 3, i, j)]]) # all unpopulated are unpopulated in all rounds for pair in unpopulated: for round in range(num_rounds): c.extend([[-get_n("S", round, pair[0], pair[1])], [-get_n("H", round, pair[0], pair[1])], [get_n("U", round, pair[0], pair[1])]]) # infect each round for round in range(num_rounds - 1): for i in range(rows): for j in range(columns): condition = observations[round][i][j] neighbors_unknown = [] if condition == "H": # infect if there's an S if ((i - 1 >= 0 and observations[round][i - 1][j] == 'S') or (i + 1 < rows and observations[round][i + 1][j] == 'S') or (j - 1 >= 0 and observations[round][i][j - 1] == 'S') or (j + 1 < columns and observations[round][i][j + 1] == 'S')): c.extend([[get_n("S", round + 1, i, j)], [get_n("S", round + 2, i, j)], [get_n("S", round + 3, i, j)], [get_n("H", round + 4, i, j)]]) continue # conditional infect if there's a ? (if ? is S) else: if i - 1 >= 0 and observations[round][i - 1][j] == '?': c.extend([[-get_n("S", round, i - 1, j), get_n("S", round + 1, i, j)], [-get_n("S", round, i - 1, j), get_n("S", round + 2, i, j)], [-get_n("S", round, i - 1, j), get_n("S", round + 3, i, j)], [-get_n("S", round, i - 1, j), get_n("H", round + 4, i, j)]]) neighbors_unknown.append((i - 1, j)) if i + 1 < rows and observations[round][i + 1][j] == '?': c.extend([[-get_n("S", round, i + 1, j), get_n("S", round + 1, i, j)], [-get_n("S", round, i + 1, j), get_n("S", round + 2, i, j)], [-get_n("S", round, i + 1, j), get_n("S", round + 3, i, j)], [-get_n("S", round, i + 1, j), get_n("H", round + 4, i, j)]]) neighbors_unknown.append((i + 1, j)) if j - 1 >= 0 and observations[round][i][j - 1] == '?': c.extend([[-get_n("S", round, i, j - 1), get_n("S", round + 1, i, j)], [-get_n("S", round, i, j - 1), get_n("S", round + 2, i, j)], [-get_n("S", round, i, j - 1), get_n("S", round + 3, i, j)], [-get_n("S", round, i, j - 1), get_n("H", round + 4, i, j)]]) neighbors_unknown.append((i, j - 1)) if j + 1 < columns and observations[round][i][j + 1] == '?': c.extend([[-get_n("S", round, i, j + 1), get_n("S", round + 1, i, j)], [-get_n("S", round, i, j + 1), get_n("S", round + 2, i, j)], [-get_n("S", round, i, j + 1), get_n("S", round + 3, i, j)], [-get_n("S", round, i, j + 1), get_n("H", round + 4, i, j)]]) neighbors_unknown.append((i, j + 1)) # if no one infected, stay H l = [get_n("S", round, pair[0], pair[1]) for pair in neighbors_unknown] l.append(get_n("H", round + 1, i, j)) c.append(l) elif condition == '?': # conditional infect if there's an S (if current ? is H) if ((i - 1 >= 0 and observations[round][i - 1][j] == 'S') or (i + 1 < rows and observations[round][i + 1][j] == 'S') or (j - 1 >= 0 and observations[round][i][j - 1] == 'S') or (j + 1 < columns and observations[round][i][j + 1] == 'S')): c.extend([[-get_n("H", round, i, j), get_n("S", round + 1, i, j)], [-get_n("H", round, i, j), get_n("S", round + 2, i, j)], [-get_n("H", round, i, j), get_n("S", round + 3, i, j)], [-get_n("H", round, i, j), get_n("H", round + 4, i, j)]]) continue # conditional infect of there's a ? (if current ? is H and other ? is S) else: if i - 1 >= 0 and observations[round][i - 1][j] == '?': c.extend( [[-get_n("H", round, i, j), -get_n("S", round, i - 1, j), get_n("S", round + 1, i, j)], [-get_n("H", round, i, j), -get_n("S", round, i - 1, j), get_n("S", round + 2, i, j)], [-get_n("H", round, i, j), -get_n("S", round, i - 1, j), get_n("S", round + 3, i, j)], [-get_n("H", round, i, j), -get_n("S", round, i - 1, j), get_n("H", round + 4, i, j)]]) neighbors_unknown.append((i - 1, j)) if i + 1 < rows and observations[round][i + 1][j] == '?': c.extend( [[-get_n("H", round, i, j), -get_n("S", round, i + 1, j), get_n("S", round + 1, i, j)], [-get_n("H", round, i, j), -get_n("S", round, i + 1, j), get_n("S", round + 2, i, j)], [-get_n("H", round, i, j), -get_n("S", round, i + 1, j), get_n("S", round + 3, i, j)], [-get_n("H", round, i, j), -get_n("S", round, i + 1, j), get_n("H", round + 4, i, j)]]) neighbors_unknown.append((i + 1, j)) if j - 1 >= 0 and observations[round][i][j - 1] == '?': c.extend( [[-get_n("H", round, i, j), -get_n("S", round, i, j - 1), get_n("S", round + 1, i, j)], [-get_n("H", round, i, j), -get_n("S", round, i, j - 1), get_n("S", round + 2, i, j)], [-get_n("H", round, i, j), -get_n("S", round, i, j - 1), get_n("S", round + 3, i, j)], [-get_n("H", round, i, j), -get_n("S", round, i, j - 1), get_n("H", round + 4, i, j)]]) neighbors_unknown.append((i, j - 1)) if j + 1 < columns and observations[round][i][j + 1] == '?': c.extend( [[-get_n("H", round, i, j), -get_n("S", round, i, j + 1), get_n("S", round + 1, i, j)], [-get_n("H", round, i, j), -get_n("S", round, i, j + 1), get_n("S", round + 2, i, j)], [-get_n("H", round, i, j), -get_n("S", round, i, j + 1), get_n("S", round + 3, i, j)], [-get_n("H", round, i, j), -get_n("S", round, i, j + 1), get_n("H", round + 4, i, j)]]) neighbors_unknown.append((i, j + 1)) # if no one infected, stay H (if ? is H) l = [get_n("S", round, pair[0], pair[1]) for pair in neighbors_unknown] l.append(-get_n("H", round, i, j)) l.append(get_n("H", round + 1, i, j)) c.append(l) for i in range(rows): for j in range(columns): condition = observations[round][i][j] if condition == '?': c.extend([[-get_n("U", round, i, j), get_n("U", round + 1, i, j)], [-get_n("U", round + 1, i, j), get_n("U", round, i, j)]]) return c
def encode(self, nof_terms=1): """ Encode the problem of computing a DS of size nof_terms. """ self.nof_terms = nof_terms self.nof_samps = len(self.samps) self.nof_labls = len(self.labels) enc = CNF() # constraint 11 for j in range(1, self.nof_terms + 1): enc.append([-self.svar(j, r) for r in range(1, self.nof_feats + 1)]) # constraint 12 for j in range(1, self.nof_terms + 1): for r in range(1, self.nof_feats + 1): d0 = self.dvar0(j, r) p0 = [-self.svar(j, r), self.lvar(j, r)] enc.append([d0, -p0[0], -p0[1]]) enc.append([-d0, p0[0]]) enc.append([-d0, p0[1]]) d1 = self.dvar1(j, r) p1 = [-self.svar(j, r), -self.lvar(j, r)] enc.append([d1, -p1[0], -p1[1]]) enc.append([-d1, p1[0]]) enc.append([-d1, p1[1]]) # constraint 13 for j in range(1, self.nof_terms + 1): for z in range(1, self.nof_labls + 1): for lb in self.labels: # skip current class label if lb == self.labels[z - 1]: continue for q in self.samps[lb]: cl = [-self.cvar(j, z)] shift = 0 for r in range(1, self.nof_feats + 1): if r - 1 in self.data.vmiss[q]: # this feature is missing in q'th sample cl.append(-self.svar(j, r)) shift += 1 elif self.data.samps[q][r - 1 - shift] > 0: cl.append(self.dvar1(j, r)) else: cl.append(self.dvar0(j, r)) enc.append(cl) # constraint 14 for j in range(1, self.nof_terms + 1): for z, lb in enumerate(self.labels, 1): for q in self.samps[lb]: cr = self.crvar(j, q + 1) cl = [-self.cvar(j, z)] shift = 0 for r in range(1, self.nof_feats + 1): if r - 1 in self.data.vmiss[q]: # this feature is missing in q'th sample cl.append(-self.svar(j, r)) shift += 1 elif self.data.samps[q][r - 1 - shift] > 0: cl.append(self.dvar1(j, r)) else: cl.append(self.dvar0(j, r)) enc.append([cr] + cl) for l in cl: enc.append([-cr, -l]) # constraint 16 for i in range(1, self.nof_terms + 1): for j in range(i + 1, self.nof_terms + 1): if self.nof_labls == 2: enc.append([-self.tlvar(i, j, 1), -self.cvar(i, 1), self.cvar(j, 1)]) enc.append([-self.tlvar(i, j, 1), self.cvar(i, 1), -self.cvar(j, 1)]) enc.append([self.tlvar(i, j, 1), -self.cvar(i, 1), -self.cvar(j, 1)]) enc.append([self.tlvar(i, j, 1), self.cvar(i, 1), self.cvar(j, 1)]) lhs = -self.tlvar(i, j, 1) else: lits = [] for z in range(1, self.nof_labls + 1): enc.append([-self.tlvar(i, j, z), -self.cvar(i, z), self.cvar(j, z)]) enc.append([-self.tlvar(i, j, z), self.cvar(i, z), -self.cvar(j, z)]) enc.append([self.tlvar(i, j, z), -self.cvar(i, z), -self.cvar(j, z)]) enc.append([self.tlvar(i, j, z), self.cvar(i, z), self.cvar(j, z)]) enc.append([-self.tlvar(i, j, self.nof_labls + 1), self.tlvar(i, j, z)]) lits.append(-self.tlvar(i, j, z)) enc.append([self.tlvar(i, j, self.nof_labls + 1)] + lits) lhs = -self.tlvar(i, j, self.nof_labls + 1) terms = [] for r in range(1, self.nof_feats + 1): # equality between l variables enc.append([-self.trvar0(i, j, r), -self.lvar(i, r), self.lvar(j, r)]) enc.append([-self.trvar0(i, j, r), self.lvar(i, r), -self.lvar(j, r)]) enc.append([self.trvar0(i, j, r), -self.lvar(i, r), -self.lvar(j, r)]) enc.append([self.trvar0(i, j, r), self.lvar(i, r), self.lvar(j, r)]) # r-th term t = self.trvar1(i, j, r) enc.append([-t, -self.svar(i, r)]) enc.append([-t, -self.svar(j, r)]) enc.append([-t, -self.trvar0(i, j, r)]) enc.append([t, self.svar(i, r), self.svar(j, r), self.trvar0(i, j, r)]) terms.append(t) enc.append([-lhs] + terms) # symmetry breaking constraints if self.options.bsymm: self.add_bsymm(enc) # constraint 15 if self.options.accuracy == 100.0: for label in self.labels: for q in self.samps[label]: enc.append([self.crvar(j, q + 1) for j in range(1, self.nof_terms + 1)]) else: allcv = [] for label in self.labels: for q in self.samps[label]: cv = self.cvvar(q + 1) enc.append([-cv] + [self.crvar(j, q + 1) for j in range(1, self.nof_terms + 1)]) for j in range(1, self.nof_terms + 1): enc.append([-self.crvar(j, q + 1), cv]) allcv.append(cv) cnum = int(math.ceil(self.options.accuracy * len(allcv) / 100.0)) al = CardEnc.atleast(allcv, bound=cnum, top_id=enc.nv, encoding=self.options.enc) if al: enc.extend(al.clauses) # at most one value can be chosen for a feature for feats in six.itervalues(self.ffmap.dir): if len(feats) > 2: for j in range(1, self.nof_terms + 1): lits = [self.dvar0(j, r + 1) for r in feats] # atmost1 can be true onev = CardEnc.atmost(lits, top_id=enc.nv, encoding=self.options.enc) enc.extend(onev.clauses) # at most one class per rule can be used if self.nof_labls > 2: for j in range(1, self.nof_terms + 1): onelb = CardEnc.equals([self.cvar(j, z) for z in range(1, self.nof_labls + 1)], top_id=enc.nv, encoding=self.options.enc) enc.extend(onelb.clauses) # saving comments for j in range(1, self.nof_terms + 1): if self.nof_labls > 2: for z in range(1, self.nof_labls + 1): enc.comments.append('c c({0}, {1}) => v{2}'.format(j, z, self.cvar(j, z))) for r in range(1, self.nof_feats + 1): enc.comments.append('c s({0}, {1}) => v{2}'.format(j, r, self.svar(j, r))) enc.comments.append('c l({0}, {1}) => v{2}'.format(j, r, self.lvar(j, r))) enc.comments.append('c d0({0}, {1}) => v{2}'.format(j, r, self.dvar0(j, r))) enc.comments.append('c d1({0}, {1}) => v{2}'.format(j, r, self.dvar1(j, r))) for q in range(len(self.data.samps)): enc.comments.append('c cr({0}, {1}) => v{2}'.format(j, q + 1, self.crvar(j, q + 1))) for n, f in zip(self.data.names[:-1], self.data.feats[:-1]): for v in f: if self.data.fvmap.dir[(n, v)] > 0: enc.comments.append('c {0} = {1} => positive'.format(n, v)) else: enc.comments.append('c {0} = {1} => negative'.format(n, v)) return enc
def create_clauses(epeg): class_to_var, var_to_class, \ class_pair_to_var, var_to_class_pair = create_vars_mappings(epeg) cnf = CNF(from_clauses=[]) top_id = max(class_pair_to_var.values()) # constraint (1) - at most one node from each class for class_name, class_nodes in epeg.classes.items(): vars = [class_to_var[class_name]] + list( set([node.id for node in class_nodes])) weights = [1] + [ -1 for _ in list(set([node.id for node in class_nodes])) ] constraint_formula = PBEnc.equals(lits=vars, weights=weights, bound=0, top_id=top_id) top_id = max([top_id] + [ abs(var) for clause in constraint_formula.clauses for var in clause ]) cnf.extend(constraint_formula.clauses) # constraint (2) - return class ret_class_var = class_to_var[epeg.root_class] return_constraint = PBEnc.equals(lits=[ret_class_var], weights=[1], bound=1, top_id=top_id) top_id = max( [top_id] + [abs(var) for clause in return_constraint.clauses for var in clause]) cnf.extend(return_constraint.clauses) # constraint (3) - taking node implies taking each of its children eq-classes for _id, node in epeg.nodes.items(): # node => child_eq_class ---> ~node \/ child_eq_class cnf.extend([[-_id, class_to_var[child.eq_class]] for child in node.children]) # constraint (4) - taking node implies taking pair of its class and each child class # (except for theta nodes' second child) for _id, node in epeg.nodes.items(): for i in range(len(node.children)): if not (isinstance(node, THETANode) and i == 1): node_child_pair_var = class_pair_to_var[( node.eq_class, node.children[i].eq_class)] clause = [-_id, node_child_pair_var] cnf.append(clause) # constraint (5) - if there is pair of same class, there must be at least one theta node from that class taken for class_name, nodes in epeg.classes.items(): pair_var = class_pair_to_var[(class_name, class_name)] clause = [-pair_var] + [ node.id for node in nodes if isinstance(node, THETANode) ] cnf.append(clause) # constraint (6) - transitivity for class_1 in epeg.classes.keys(): for class_2 in epeg.classes.keys(): for class_3 in epeg.classes.keys(): if len(set([class_1, class_2, class_3])) == 3: pair_1 = class_pair_to_var[(class_1, class_2)] pair_2 = class_pair_to_var[(class_2, class_3)] pair_3 = class_pair_to_var[(class_1, class_3)] clause = [-pair_1, -pair_2, pair_3] cnf.append(clause) return cnf, top_id