def correct_KB(KB1, KB2): Diff = [ list(x) for x in set(map(tuple, KB1.clauses)).difference( set(map(tuple, KB2.clauses))) ] KB2_s = [ list(x) for x in set(map(tuple, KB2.clauses)).difference( set(map(tuple, KB1.clauses))) ] KB2_h = [ list(x) for x in set(map(tuple, KB2.clauses)).intersection( set(map(tuple, KB1.clauses))) ] wcnf = WCNF() for c in KB2_s: wcnf.append(c, weight=1) for c in KB2_h: wcnf.append(c) for c in Diff: wcnf.append(c) lbx = LBX(wcnf, use_cld=True, solver_name='g3') # Compute mcs and return the clauses indexes mcs = lbx.compute() temp_cl_lookup = create_clauses_lookup(wcnf.soft) clauses = get_clauses_from_index(mcs, temp_cl_lookup) return clauses
def get_MCS(KBa_s, KBa_h, q, seed, clauses_dict): # Compute minimal hitting set wcnf = WCNF() for c in KBa_s: if c not in seed: # don't add to the soft clauses those in the seed wcnf.append(c, weight=1) for c in KBa_h: wcnf.append(c) for c in seed: # add clauses in the seed as hard if any(isinstance(el, list) for el in c): for cs in c: wcnf.append(cs) else: wcnf.append(c) wcnf.extend(q.negate().clauses) lbx = LBX(wcnf, use_cld=True, solver_name='g3') # Compute mcs and return the clauses indexes mcs = lbx.compute() # Current mcs is computed w.r.t. the soft clauses excluding the seed. Below we find the corresponding indexes of these clauses in KBa_s temp_cl_lookup = create_clauses_lookup(wcnf.soft) clauses = get_clauses_from_index(mcs, temp_cl_lookup) mcs = get_index_from_clauses(clauses, clauses_dict) return mcs
def optimize(self, enc): """ Try to optimize the solution with a MaxSAT solver. """ # a dummy model (everything is deselected) model = [-v for v in range(enc.nv)] all_vars = set() # MaxSAT formula to work with formula = WCNF() # hard clauses for cl in enc.clauses: formula.append(cl) for j in range(1, self.nof_terms + 1): for r in range(1, self.nof_feats + 1): formula.append([-self.dvar1(j, r)], 1) formula.append([-self.dvar0(j, r)], 1) all_vars.add(self.dvar1(j, r)) all_vars.add(self.dvar0(j, r)) if self.options.approx: hitman = LBX(formula, use_cld=self.options.use_cld, solver_name=self.options.solver) hses = [] for i, hs in enumerate(hitman.enumerate()): hitman.block(hs) hses.append(hs) if i + 1 == self.options.approx: break hs = list( map(lambda v: -formula.soft[v - 1][0], min(hses, key=lambda x: len(x)))) hitman.delete() else: hitman = RC2(formula, solver=self.options.solver, adapt=True, exhaust=True, incr=False, minz=False, trim=self.options.trim) hs = list( filter(lambda v: v > 0 and v in all_vars, hitman.compute())) hitman.delete() # filling the model with the right values for e in hs: model[e - 1] = e return model
def init(self, bootstrap_with): """ This method serves for initializing the hitting set solver with a given list of sets to hit. Concretely, the hitting set problem is encoded into partial MaxSAT as outlined above, which is then fed either to a MaxSAT solver or an MCS enumerator. :param bootstrap_with: input set of sets to hit :type bootstrap_with: iterable(iterable(obj)) """ # formula encoding the sets to hit formula = WCNF() # hard clauses for to_hit in bootstrap_with: to_hit = list(map(lambda obj: self.idpool.id(obj), to_hit)) formula.append(to_hit) # soft clauses for obj_id in six.iterkeys(self.idpool.id2obj): formula.append([-obj_id], weight=1) if self.htype == 'rc2': # using the RC2-A options from MaxSAT evaluation 2018 self.oracle = RC2(formula, solver=self.solver, adapt=False, exhaust=True, trim=5) elif self.htype == 'lbx': self.oracle = LBX(formula, solver_name=self.solver, use_cld=True) else: self.oracle = MCSls(formula, solver_name=self.solver, use_cld=True)
def optimize(self, enc): """ Try to optimize the solution with a MaxSAT solver. """ # all d0 and d1 variables (for computing the complement --- MSS) all_vars = set([]) # MaxSAT formula to work with formula = WCNF() # hard clauses for cl in enc.clauses: formula.append(cl) for j in range(1, self.nof_terms + 1): for r in range(1, self.nof_feats + 1): formula.append([-self.dvar1(j, r)], 1) formula.append([-self.dvar0(j, r)], 1) all_vars.add(self.dvar1(j, r)) all_vars.add(self.dvar0(j, r)) if self.options.approx: hitman = LBX(formula, use_cld=self.options.use_cld, solver_name=self.options.solver) hses = [] for i, hs in enumerate(hitman.enumerate()): hitman.block(hs) hses.append(hs) if i + 1 == self.options.approx: break hs = list(map(lambda v: -formula.soft[v - 1][0], min(hses, key=lambda x: len(x)))) hitman.delete() else: hitman = RC2(formula, solver=self.options.solver, adapt=True, exhaust=True, incr=False, minz=False, trim=self.options.trim) hs = list(filter(lambda v: v > 0 and v in all_vars, hitman.compute())) hitman.delete() return sorted([-v for v in all_vars.difference(set(hs))])
def init(self, bootstrap_with, weights=None): """ This method serves for initializing the hitting set solver with a given list of sets to hit. Concretely, the hitting set problem is encoded into partial MaxSAT as outlined above, which is then fed either to a MaxSAT solver or an MCS enumerator. An additional optional parameter is ``weights``, which can be used to specify non-unit weights for the target objects in the sets to hit. This only works if ``'sorted'`` enumeration of hitting sets is applied. :param bootstrap_with: input set of sets to hit :param weights: weights of the objects in case the problem is weighted :type bootstrap_with: iterable(iterable(obj)) :type weights: dict(obj) """ # formula encoding the sets to hit formula = WCNF() # hard clauses for to_hit in bootstrap_with: to_hit = list(map(lambda obj: self.idpool.id(obj), to_hit)) formula.append(to_hit) # soft clauses for obj_id in six.iterkeys(self.idpool.id2obj): formula.append( [-obj_id], weight=1 if not weights else weights[self.idpool.obj(obj_id)]) if self.htype == 'rc2': if not weights or min(weights.values()) == max(weights.values()): self.oracle = RC2(formula, solver=self.solver, adapt=self.adapt, exhaust=self.exhaust, minz=self.minz, trim=self.trim) else: self.oracle = RC2Stratified(formula, solver=self.solver, adapt=self.adapt, exhaust=self.exhaust, minz=self.minz, nohard=True, trim=self.trim) elif self.htype == 'lbx': self.oracle = LBX(formula, solver_name=self.solver, use_cld=self.usecld) else: self.oracle = MCSls(formula, solver_name=self.solver, use_cld=self.usecld)
def init(self, bootstrap_with, costs): """ This method serves for initializing the hitting set solver with a given list of sets to hit. Concretely, the hitting set problem is encoded into partial MaxSAT as outlined above, which is then fed either to a MaxSAT solver or an MCS enumerator. :param bootstrap_with: input set of sets to hit :type bootstrap_with: iterable(iterable(obj)) """ # formula encoding the sets to hit formula = WCNF() # hard clauses for to_hit in bootstrap_with: to_hit = list(map(lambda obj: self.idpool.id(obj), to_hit)) formula.append(to_hit) # soft clauses for obj_id in six.iterkeys(self.idpool.id2obj): # this is saying that not including a clause is given a weight of x # maxSAT is MAXIMISING the sum of satisfied soft clauses, so to minimise sum, # we want to weight *not* including something (hence the -obj_id) # this means words such as <PAD> should be given a *higher* weight, so the # solver decides that NOT including <PAD> is more worth it than not including # a more "meaningful" word cost = costs[obj_id - 1] formula.append([-obj_id], weight=cost) if self.htype == 'rc2': # using the RC2-A options from MaxSAT evaluation 2018 self.oracle = RC2(formula, solver=self.solver, adapt=False, exhaust=True, trim=5) elif self.htype == 'lbx': self.oracle = LBX(formula, solver_name=self.solver, use_cld=True) else: self.oracle = MCSls(formula, solver_name=self.solver, use_cld=True)
def init(self, bootstrap_with, weights=None, subject_to=[]): """ This method serves for initializing the hitting set solver with a given list of sets to hit. Concretely, the hitting set problem is encoded into partial MaxSAT as outlined above, which is then fed either to a MaxSAT solver or an MCS enumerator. An additional optional parameter is ``weights``, which can be used to specify non-unit weights for the target objects in the sets to hit. This only works if ``'sorted'`` enumeration of hitting sets is applied. Another optional parameter is available, namely, ``subject_to``. It can be used to specify arbitrary hard constraints that must be respected when computing hitting sets of the given sets. Note that ``subject_to`` should be an iterable containing pure clauses and/or native AtMostK constraints. Finally, note that these hard constraints must be defined over the set of signed atomic objects, i.e. instances of class :class:`.Atom`. :param bootstrap_with: input set of sets to hit :param weights: weights of the objects in case the problem is weighted :param subject_to: hard constraints (either clauses or native AtMostK constraints) :type bootstrap_with: iterable(iterable(obj)) :type weights: dict(obj) :type subject_to: iterable(iterable(Atom)) """ # formula encoding the sets to hit formula = WCNFPlus() # hard clauses for to_hit in bootstrap_with: to_hit = list(map(lambda obj: self.idpool.id(obj), to_hit)) formula.append(to_hit) # additional hard constraints for cl in subject_to: if not len(cl) == 2 or not type(cl[0]) in (list, tuple, set): # this is a pure clause formula.append(list(map(lambda a: self.idpool.id(a.obj) * (2 * a.sign - 1), cl))) else: # this is a native AtMostK constraint formula.append([list(map(lambda a: self.idpool.id(a.obj) * (2 * a.sign - 1), cl[0])), cl[1]], is_atmost=True) # soft clauses for obj_id in six.iterkeys(self.idpool.id2obj): formula.append([-obj_id], weight=1 if not weights else weights[self.idpool.obj(obj_id)]) if self.htype == 'rc2': if not weights or min(weights.values()) == max(weights.values()): self.oracle = RC2(formula, solver=self.solver, adapt=self.adapt, exhaust=self.exhaust, minz=self.minz, trim=self.trim) else: self.oracle = RC2Stratified(formula, solver=self.solver, adapt=self.adapt, exhaust=self.exhaust, minz=self.minz, nohard=True, trim=self.trim) elif self.htype == 'lbx': self.oracle = LBX(formula, solver_name=self.solver, use_cld=self.usecld) else: self.oracle = MCSls(formula, solver_name=self.solver, use_cld=self.usecld)
def compute_mxsat(self): """ Cover samples for all labels using MaxSAT or MCS enumeration. """ if self.options.verb: print('c2 (using rc2)') # we model a set cover problem with MaxSAT formula = WCNFPlus() # hard part of the formula if self.options.accuracy == 100.0: for sid in self.cluster: # for every sample in the cluster to_hit = [] for rid, rule in enumerate(self.rules): if rule.issubset(self.samps[sid]): to_hit.append(rid + 1) formula.append(to_hit) else: topv = len(self.rules) allvars = [] # hard clauses first for sid in self.cluster: # for every sample in cluster to_hit = [] for rid, rule in enumerate(self.rules): if rule.issubset(self.samps[sid]): to_hit.append(rid + 1) topv += 1 allvars.append(topv) formula.append([-topv] + to_hit) for rid in to_hit: formula.append([topv, -rid]) # forcing at least the given percentage of samples to be covered cnum = int(math.ceil(self.options.accuracy * len(allvars) / 100.0)) al = CardEnc.atleast(allvars, bound=cnum, top_id=topv, encoding=self.options.enc) if al: for cl in al.clauses: formula.append(cl) # soft clauses for rid in range(len(self.rules)): formula.append([-rid - 1], weight=1) if self.options.weighted and not self.options.approx: # it is safe to add weights for all rules # because each rule covers at least one sample formula.wght = [len(rule) + 1 for rule in self.rules] if self.options.pdump: fname = 'cover{0}.{1}@{2}.wcnf'.format(self.target, os.getpid(), socket.gethostname()) formula.to_file(fname) # choosing the right solver if not self.options.approx: MaxSAT = RC2Stratified if self.options.blo else RC2 hitman = MaxSAT(formula, solver=self.options.solver, adapt=self.options.am1, exhaust=self.options.exhaust, trim=self.options.trim, minz=self.options.minz) else: hitman = LBX(formula, use_cld=self.options.use_cld, solver_name=self.options.solver, use_timer=False) # and the cover is... if not self.options.approx: self.cover = list( filter(lambda l: 0 < l <= len(self.rules) + 1, hitman.compute())) self.cost += hitman.cost if self.options.weighted: self.cost -= len(self.cover) else: # approximating by computing a number of MCSes covers = [] for i, cover in enumerate(hitman.enumerate()): hitman.block(cover) if self.options.weighted: cost = sum([len(self.rules[rid - 1]) for rid in cover]) else: cost = len(cover) covers.append([cover, cost]) if i + 1 == self.options.approx: break self.cover, cost = min(covers, key=lambda x: x[1]) self.cost += cost hitman.delete()