示例#1
0
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
示例#2
0
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
示例#3
0
    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
示例#4
0
    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)
示例#5
0
    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))])
示例#6
0
    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)
示例#7
0
    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)
示例#8
0
    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)
示例#9
0
    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()