Пример #1
0
    def optimize(self, formula, model):
        """
            Try to optimize the solution with a MaxSAT solver.
        """

        MaxSAT = RC2Stratified if self.options.weighted else RC2

        formula_new = WCNF()
        formula_new.extend(formula.hard)

        # hardening the soft clauses based on the model
        for j in range(1, self.nof_terms + 1):
            formula_new.append([model[self.unused(j)]])
        for lb in self.labels:
            for q in self.samps[lb]:
                formula_new.append([model[self.miss(q + 1)]])

        for j in range(1, self.nof_terms + 1):
            for r in range(1, self.nof_feats + 1):
                formula_new.append([-self.dvar1(j, r)], weight=1)
                formula_new.append([-self.dvar0(j, r)], weight=1)

        with MaxSAT(
                formula_new,
                solver=self.options.solver,
                adapt=self.options.am1,
                exhaust=self.options.exhaust,
                minz=self.options.minz,
                trim=self.options.trim,
        ) as rc2:
            model = rc2.compute()

        return model
Пример #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 MUSExtraction(self, C):
     wcnf = WCNF()
     wcnf.extend(self.cnf.clauses)
     wcnf.extend([[l] for l in C], [1] * len(C))
     with MUSX(wcnf, verbosity=0) as musx:
         mus = musx.compute()
         # gives back positions of the clauses !!
         return set(C[i - 1] for i in mus)
    def __createWncf(self, initialisationFormulas, distanceFormula, artefactForMinimization):
        '''
        This method creates the wncf formulas with the weighted variables depending on the distance and artefact.
        :param initialisationFormulas: @see __artefactsInitialisation
        :param distanceFormula: @see __compute_distance
        :param artefactForMinimization: MULTI_ALIGNMENT or ANTI_ALIGNMENT or EXACT_ALIGNMENT
        :return:
        '''
        formulas = initialisationFormulas + distanceFormula + self.__sup_to_minimize(artefactForMinimization)
        full_formula = And([], [], formulas)
        cnf = full_formula.operatorToCnf(self.__vars.iterator)
        wcnf = WCNF()
        wcnf.extend(cnf)
        wcnf = self.__createWeights(wcnf,artefactForMinimization)

        self.__formula_time = time.time()
        return wcnf
Пример #5
0
def get_MUS(KB, e, q):
    # Compute minimal unsatisfiable set
    wcnf2 = WCNF()
    for k in e:
        if any(isinstance(el, list) for el in k):
            for ks in k:
                wcnf2.append(ks, weight=1)
        else:
            wcnf2.append(k, weight=1)

    if KB:
        for c in KB.clauses:
            wcnf2.append(c, weight=1)
    wcnf2.extend((q.negate().clauses))
    mmusx = MUSX(wcnf2, verbosity=0)
    mus = mmusx.compute()
    return [list(wcnf2.soft[m - 1]) for m in mus]
Пример #6
0
    def grow_maxsat(self, f, A, HS):
        remaining, weights = None, None
        wcnf = WCNF()

        # HARD clauses
        wcnf.extend(self.cnf.clauses)
        wcnf.extend([[l] for l in HS])

        # SOFT clauses to grow
        if self.params.interpretation is Interpretation.INITIAL:
            remaining = list(self.I0 - HS)
        elif self.params.interpretation is Interpretation.ACTUAL:
            remaining = list(self.I - HS)
        elif self.params.interpretation is Interpretation.FULL:
            remaining = list(self.Iend - HS)
        elif self.params.interpretation is Interpretation.FINAL:
            remaining = list(A - HS)

        remaining_clauses = [[l] for l in remaining]

        if self.params.maxsat_weighing is Weighing.POSITIVE:
            weights = [f(l) for l in remaining]
        elif self.params.maxsat_weighing is Weighing.INVERSE:
            max_weight = max(f(l) for l in remaining) + 1
            weights = [max_weight - f(l) for l in remaining]
        elif self.params.maxsat_weighing is Weighing.UNIFORM:
            weights = [1] * len(remaining)

        # cost is associated for assigning a truth value to literal not in
        # contrary to A.
        wcnf.extend(clauses=remaining_clauses, weights=weights)

        # solve the MAXSAT problem
        with RC2(wcnf) as s:
            if self.params.maxsat_polarity and hasattr(s, 'oracle'):
                s.oracle.set_phases(literals=list(self.Iend))

            t_model = s.compute()

            return set(t_model)
Пример #7
0
    def encode(self, label, nof_terms=1):
        """
            Encode the problem of computing a DS of size nof_terms.
        """

        self.nof_terms = nof_terms

        enc = WCNF()

        # all the hard clauses
        #
        # constraint 6 (relaxed with the unused variable)
        for j in range(1, self.nof_terms + 1):
            for r in range(1, self.nof_feats + 1):
                enc.append([-self.unused(j), self.svar(j, r)])

            enc.append(
                [self.unused(j)] +
                [-self.svar(j, r) for r in range(1, self.nof_feats + 1)])

        # sort unused rules
        for j in range(1, self.nof_terms):
            enc.append([-self.unused(j), self.unused(j + 1)])

        # constraint 7
        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 8
        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 = [self.unused(j),
                          self.miss(q + 1)]  # the clause is relaxed

                    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 9
        for j in range(1, self.nof_terms + 1):
            for q in self.samps[label]:
                cr = self.crvar(j, q + 1)
                cl = [self.unused(j)]

                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 10
        for q in self.samps[label]:
            enc.append(
                [self.miss(q + 1)] +
                [self.crvar(j, q + 1) for j in range(1, self.nof_terms + 1)])

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

        # soft clauses
        # minimizing the number of literals used
        for j in range(1, self.nof_terms + 1):
            enc.append([self.unused(j)], weight=self.lambda_)
        # minimizing the number of missclassifications
        for lb in self.labels:
            for q in self.samps[lb]:
                enc.append([-self.miss(q + 1)], weight=self.data.wghts[q])

        # there should be at least one rule for this class
        enc.append([-self.unused(1)])

        # saving comments
        for j in range(1, self.nof_terms + 1):
            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 j in range(1, self.nof_terms + 1):
            enc.comments.append('c u({0}) => v{1}'.format(j, self.unused(j)))

        for lb in self.labels:
            for q in self.samps[lb]:
                enc.comments.append('c m({0}) => v{1}'.format(
                    q + 1, self.miss(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
Пример #8
0
    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 = WCNF()

        # all the hard clauses
        #
        # 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)] + [self.unused(j)]
            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)])

        # propagation of unused literals
        for j in range(1, self.nof_lits):
            enc.append([-self.unused(j), self.unused(j + 1)])

        # leaf constraints
        # this disallows empty (default) rules and thus is disabled
        # enc.append([-self.leaf(1)])  # first node can't be a leaf

        # last leaf
        for j in range(1, self.nof_lits):
            enc.append([-self.unused(j + 1), self.unused(j), self.leaf(j)])
        enc.append([self.leaf(self.nof_lits), self.unused(self.nof_lits)])

        # 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), self.miss(i + 1)])

        # coverage constraints
        for i in self.samps[label]:
            cl = [self.miss(i + 1)]  # here the clause is relaxed

            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)

        # soft clauses
        # minimizing the number of literals used
        for j in range(1, self.nof_lits + 1):
            # enc.append([self.unused(j)], weight=self.lambdas[label])
            enc.append([self.unused(j)], weight=self.lambda_)
        # minimizing the number of missclassifications
        for lb in self.labels:
            for i in self.samps[lb]:
                enc.append([-self.miss(i + 1)], weight=self.data.wghts[i])

        # there should be at least two literals in the decision set
        # since u_j variables are sorted, we know that they are u1 and u2
        # enc.extend([[-self.unused(1)], [-self.unused(2)]])

        # the previous constraints disallowed empty (default) rules
        # and so this one looks better
        enc.extend([[-self.unused(1)]])

        # 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