Ejemplo n.º 1
0
def create_formula_groundings(formula, unsatfailure=True):
    checkmem()
    results = []
    if global_bpll_grounding.mrf.mln.logic.islitconj(formula):
        for res in global_bpll_grounding.itergroundings_fast(formula):
            checkmem()
            results.append(res)
    else:
        for gf in formula.itergroundings(global_bpll_grounding.mrf,
                                         simplify=False):
            checkmem()
            stat = []
            for gndatom in gf.gndatoms():
                world = list(global_bpll_grounding.mrf.evidence)
                var = global_bpll_grounding.mrf.variable(gndatom)
                for validx, value in var.itervalues():
                    var.setval(value, world)
                    truth = gf(world)
                    if truth != 0:
                        stat.append((var.idx, validx, truth))
                    elif unsatfailure and gf.weight == HARD and gf(
                            global_bpll_grounding.mrf.evidence) != 1:
                        print
                        gf.print_structure(global_bpll_grounding.mrf.evidence)
                        raise SatisfiabilityException(
                            'MLN is unsatisfiable due to hard constraint violation {} (see above)'
                            .format(
                                global_bpll_grounding.mrf.formulas[gf.idx]))
            results.append((gf.idx, stat))
    return results
Ejemplo n.º 2
0
 def _pl(self, varidx, w):
     '''
     Computes the pseudo-likelihoods for the given variable under weights w. 
     '''
     var = self.mrf.variable(varidx)
     values = var.valuecount()
     gfs = self._varidx2fidx.get(varidx)
     if gfs is None:  # no list was saved, so the truth of all formulas is unaffected by the variable's value
         # uniform distribution applies
         p = 1.0 / values
         return [p] * values
     sums = [0] * values  #numpy.zeros(values)
     for fidx in gfs:
         for validx, n in enumerate(self._stat[fidx][varidx]):
             if w[fidx] == HARD:
                 # set the prob mass of every value violating a hard constraint to None
                 # to indicate a globally inadmissible value. We will set those ones to 0 afterwards.
                 if n == 0: sums[validx] = None
             elif sums[validx] is not None:
                 # don't set it if this value has already been assigned marked as inadmissible.
                 sums[validx] += n * w[fidx]
     expsums = [numpy.exp(s) if s is not None else 0
                for s in sums]  #numpy.exp(numpy.array(sums))
     z = sum(expsums)
     if z == 0:
         raise SatisfiabilityException(
             'MLN is unsatisfiable: all probability masses of variable %s are zero.'
             % str(var))
     return map(lambda w_: w_ / z, expsums)
Ejemplo n.º 3
0
    def convert(self):
        '''
        Performs a conversion from an MLN into a WCSP.
        '''
        # mln to be restored after inference
        self._weights = list(self.mrf.mln.weights)
        mln = self.mrf.mln
        logic = mln.logic
        # preprocess the formulas
        formulas = []
        for f in self.mrf.formulas:
            if f.weight == 0: 
                continue
            if f.weight < 0:
                f = logic.negate(f)
                f.weight = -f.weight
            formulas.append(f.nnf())
        # preprocess the ground formulas
#         grounder = DefaultGroundingFactory(self.mrf, formulas=formulas, simplify=True, unsatfailure=True, multicore=self.multicore, verbose=self.verbose)
        grounder = FastConjunctionGrounding(self.mrf, simplify=True, unsatfailure=True, formulas=formulas, multicore=self.multicore, verbose=self.verbose, cache=0)
        for gf in grounder.itergroundings():
            if isinstance(gf, Logic.TrueFalse):
                if gf.weight == HARD and gf.truth() == 0:
                    raise SatisfiabilityException('MLN is unsatisfiable: hard constraint %s violated' % self.mrf.mln.formulas[gf.idx])
                else:# formula is rendered true/false by the evidence -> equal in every possible world 
                    continue
            self.generate_constraint(gf)
        self.mrf.mln.weights = self._weights
        return self.wcsp
Ejemplo n.º 4
0
 def _l(self, w):
     '''
     computes the likelihoods of all possible worlds under weights w
     '''
     if self._lastw is None or list(w) != self._lastw:
         self._lastw = list(w)
         expsums = []
         for fvalues in self._stat:
             s = 0
             hc_violation = False
             for fidx, val in fvalues.iteritems():
                 if self.mrf.mln.formulas[fidx].weight == HARD:
                     if val == 0:
                         hc_violation = True
                         break
                 else:
                     s += val * w[fidx]
             if hc_violation:
                 expsums.append(0)
             else:
                 expsums.append(exp(s))
         z = sum(expsums)
         if z == 0:
             raise SatisfiabilityException(
                 'MLN is unsatisfiable: probability masses of all possible worlds are zero.'
             )
         self._ls = map(lambda v: v / z, expsums)
     return self._ls
Ejemplo n.º 5
0
    def _compute_probs(self, w):
        probs = {}  #numpy.zeros(len(self.partitions))
        for pidx in range(len(self.partitions)):
            expsums = [0] * self.valuecounts[pidx]
            for fidx in self.partition2formulas[pidx]:
                for i, v in enumerate(self._stat[fidx][pidx]):
                    if w[fidx] == HARD:
                        if v == 0: expsums[i] = None
                    elif expsums[i] is not None:
                        #                         out('adding', v, '*', w[fidx], 'to', i)
                        expsums[i] += v * w[fidx]


#                         stop(expsums)
#             sum_max = numpy.max(sums)
#             sums -= sum_max
#             expsums = numpy.sum(numpy.exp(sums))
#             s = numpy.log(expsums)
#             probs[pidx] = numpy.exp(sums - s)
#             out(w)
            expsum = numpy.array([
                numpy.exp(s) if s is not None else 0 for s in expsums
            ])  # leave out the inadmissible values
            z = fsum(expsum)
            if z == 0:
                raise SatisfiabilityException(
                    'MLN is unsatisfiable: all probability masses of partition %s are zero.'
                    % str(self.partitions[pidx]))
            probs[pidx] = expsum / z
            self.probs[pidx] = expsum
        self.probs = probs
        return probs
Ejemplo n.º 6
0
 def _itergroundings(self, simplify=False, unsatfailure=False):
     if self.verbose:
         bar = ProgressBar(width=100, color='green')
     for i, formula in enumerate(self.formulas):
         if self.verbose: bar.update((i + 1) / float(len(self.formulas)))
         for gndformula in formula.itergroundings(self.mrf,
                                                  simplify=simplify):
             if unsatfailure and gndformula.weight == HARD and gndformula(
                     self.mrf.evidence) != 1:
                 print
                 gndformula.print_structure(self.mrf.evidence)
                 raise SatisfiabilityException(
                     'MLN is unsatisfiable due to hard constraint violation %s (see above)'
                     % self.mrf.formulas[gndformula.idx])
             yield gndformula
Ejemplo n.º 7
0
 def _itergroundings_fast(self,
                          formula,
                          constituents,
                          cidx,
                          pivotfct,
                          truthpivot,
                          assignment,
                          level=0):
     if truthpivot == 0 and (isinstance(formula, Logic.Conjunction)
                             or self.mrf.mln.logic.islit(formula)):
         if formula.weight == HARD:
             raise SatisfiabilityException(
                 'MLN is unsatisfiable given evidence due to hard constraint violation: %s'
                 % str(formula))
         return
     if truthpivot == 1 and (isinstance(formula, Logic.Disjunction)
                             or self.mrf.mln.logic.islit(formula)):
         return
     if cidx == len(
             constituents
     ):  # we have reached the end of the formula constituents
         gf = formula.ground(self.mrf, assignment, simplify=True)
         if isinstance(gf, Logic.TrueFalse):
             return
         yield gf
         return
     c = constituents[cidx]
     for varass in c.itervargroundings(self.mrf, partial=assignment):
         newass = dict_union(assignment, varass)
         ga = c.ground(self.mrf, newass)
         truth = ga.truth(self.mrf.evidence)
         if truth is None:
             truthpivot_ = truthpivot
         elif truthpivot is None:
             truthpivot_ = truth
         else:
             truthpivot_ = pivotfct(truthpivot, truth)
         for gf in self._itergroundings_fast(formula, constituents,
                                             cidx + 1, pivotfct,
                                             truthpivot_, newass,
                                             level + 1):
             yield gf
Ejemplo n.º 8
0
 def _run(self):
     """
     verbose: whether to print results (or anything at all, in fact)
     details: (given that verbose is true) whether to output additional
              status information
     debug:   (given that verbose is true) if true, outputs debug
              information, in particular the distribution over possible
              worlds
     debugLevel: level of detail for debug mode
     """
     # check consistency with hard constraints:
     self._watch.tag('check hard constraints', verbose=self.verbose)
     hcgrounder = FastConjunctionGrounding(self.mrf, simplify=False, unsatfailure=True, 
                                           formulas=[f for f in self.mrf.formulas if f.weight == HARD], 
                                           **(self._params + {'multicore': False, 'verbose': False}))
     for gf in hcgrounder.itergroundings():
         if isinstance(gf, Logic.TrueFalse) and gf.truth() == .0:
             raise SatisfiabilityException('MLN is unsatisfiable due to hard constraint violation by evidence: {} ({})'.format(str(gf), str(self.mln.formula(gf.idx))))
     self._watch.finish('check hard constraints')
     # compute number of possible worlds
     worlds = 1
     for variable in self.mrf.variables:
         values = variable.valuecount(self.mrf.evidence)
         worlds *= values
     numerators = [0.0 for i in range(len(self.queries))]
     denominator = 0.
     # start summing
     logger.debug("Summing over %d possible worlds..." % worlds)
     if worlds > 500000 and self.verbose:
         print colorize('!!! %d WORLDS WILL BE ENUMERATED !!!' % worlds, (None, 'red', True), True)
     k = 0
     self._watch.tag('enumerating worlds', verbose=self.verbose)
     global global_enumAsk
     global_enumAsk = self
     bar = None
     if self.verbose:
         bar = ProgressBar(width=100, steps=worlds, color='green')
     if self.multicore:
         pool = Pool()
         logger.debug('Using multiprocessing on {} core(s)...'.format(pool._processes))
         try:
             for num, denum in pool.imap(with_tracing(eval_queries), self.mrf.worlds()):
                 denominator += denum
                 k += 1
                 for i, v in enumerate(num):
                     numerators[i] += v
                 if self.verbose: bar.inc()
         except Exception as e:
             logger.error('Error in child process. Terminating pool...')
             pool.close()
             raise e
         finally:
             pool.terminate()
             pool.join()
     else:  # do it single core
         for world in self.mrf.worlds():
             # compute exp. sum of weights for this world
             num, denom = eval_queries(world)
             denominator += denom
             for i, _ in enumerate(self.queries):
                 numerators[i] += num[i]
             k += 1
             if self.verbose:
                 bar.update(float(k) / worlds)
     logger.debug("%d worlds enumerated" % k)
     self._watch.finish('enumerating worlds')
     if 'grounding' in self.grounder.watch.tags:
         self._watch.tags['grounding'] = self.grounder.watch['grounding']
     if denominator == 0:
         raise SatisfiabilityException(
             'MLN is unsatisfiable. All probability masses returned 0.')
     # normalize answers
     dist = map(lambda x: float(x) / denominator, numerators)
     result = {}
     for q, p in zip(self.queries, dist):
         result[str(q)] = p
     return result
Ejemplo n.º 9
0
 def _itergroundings_fast(self,
                          formula,
                          constituents,
                          cidx,
                          assignment,
                          variables,
                          falsevar=None,
                          level=0):
     if cidx == len(constituents):
         # no remaining literals to ground. return the ground formula
         # and statistics
         stat = [(varidx, validx, count)
                 for (varidx, validx, count) in variables]
         yield formula.idx, stat
         return
     c = constituents[cidx]
     # go through all remaining groundings of the current constituent
     for varass in c.itervargroundings(self.mrf, partial=assignment):
         gnd = c.ground(self.mrf, dict_union(varass, assignment))
         # check if it violates a hard constraint
         if formula.weight == HARD and gnd(self.mrf.evidence) < 1:
             raise SatisfiabilityException(
                 'MLN is unsatisfiable by evidence due to hard constraint violation {} (see above)'
                 .format(global_bpll_grounding.mrf.formulas[formula.idx]))
         if isinstance(gnd, Logic.Equality):
             # if an equality grounding is false in a conjunction, we can
             # stop since the  conjunction cannot be rendered true in any
             # grounding that follows
             if gnd.truth(None) == 0: continue
             for gf in self._itergroundings_fast(
                     formula, constituents, cidx + 1,
                     dict_union(assignment, varass), variables, falsevar,
                     level + 1):
                 yield gf
         else:
             var = self.mrf.variable(gnd.gndatom)
             world_ = list(self.mrf.evidence)
             stat = []
             skip = False
             falsevar_ = falsevar
             vars_ = list(variables)
             for validx, value in var.itervalues():
                 var.setval(value, world_)
                 truth = gnd(world_)
                 if truth == 0 and value == var.evidence_value():
                     # if the evidence value renders the current
                     # consituent false and there was already a false
                     # literal in the grounding path, we can prune the
                     # tree since no grounding will be true
                     if falsevar is not None and falsevar != var.idx:
                         skip = True
                         break
                     else:
                         # if there was no literal false so far, we
                         # collect statistics only for the current literal
                         # and only if all future literals will be true
                         # by evidence
                         vars_ = []
                         falsevar_ = var.idx
                 if truth > 0 and falsevar is None:
                     stat.append((var.idx, validx, truth))
             if falsevar is not None and falsevar == var.idx:
                 # in case of non-mutual exclusive values take only the
                 # values that render all literals true
                 # example: soft-functional constraint with
                 # !foo(?x) ^ foo(?y), x={X,Y,Z} where the evidence
                 # foo(Z) is true
                 # here the grounding !foo(X) ^ foo(Y) is false:
                 # !foo(X) is true for foo(Z) and foo(Y) and
                 # (!foo(Z) ^ !foox(X) ^ !foo(Y))
                 #       foo(Y) is true for foo(Y)
                 #   both are only true for foo(Y)
                 stat = set(variables).intersection(stat)
                 skip = not bool(stat)  # skip if no values remain
             if skip: continue
             for gf in self._itergroundings_fast(formula,
                                                 constituents,
                                                 cidx + 1,
                                                 dict_union(
                                                     assignment, varass),
                                                 vars_ + stat,
                                                 falsevar=falsevar_,
                                                 level=level + 1):
                 yield gf
Ejemplo n.º 10
0
 def _gather_constraint_tuples(self, varindices, formula):
     '''
     Collects and evaluates all tuples that belong to the constraint
     given by a formula. In case of disjunctions and conjunctions,
     this is fairly efficient since not all combinations
     need to be evaluated. Returns a dictionary mapping the constraint
     costs to the list of respective variable assignments.
     '''
     logic = self.mrf.mln.logic
     # we can treat conjunctions and disjunctions fairly efficiently
     defaultProcedure = False
     conj = logic.islitconj(formula)
     disj = False
     if not conj:
         disj = logic.isclause(formula)
     if not varindices:
         return None
     if not conj and not disj:
         defaultProcedure = True
     if not defaultProcedure:
         assignment = [None] * len(varindices)
         children = list(formula.literals())
         for gndlit in children:
             # constants are handled in the maxtruth/mintruth calls below
             if isinstance(gndlit, Logic.TrueFalse): continue
             # get the value of the gndlit that renders the formula true (conj) or false (disj):
             # for a conjunction, the literal must be true,
             # for a disjunction, it must be false.
             (gndatom, val) = (gndlit.gndatom, not gndlit.negated)
             if disj: val = not val
             val = 1 if val else 0
             variable = self.variables[self.atom2var[gndatom.idx]]
             # in case there are multiple values of a variable that may render the formula true
             # we cannot apply this efficient implementation and have to fall back to the naive impl. 
             tmp_evidence = variable.value2dict(variable.evidence_value())
             evval = tmp_evidence.get(gndatom.idx)
             if evval is not None and evval != val:
                 # the supposed value of the variable and the evidence value mismatch,
                 # so the conjunction (disjunction) can never be rendered true (false)
                 return  
             tmp_evidence = dict_union(tmp_evidence, {gndatom.idx: val})
             if variable.valuecount(tmp_evidence) > 1:
                 defaultProcedure = True
                 break
             # there is only one value remaining
             for _, value in variable.itervalues(tmp_evidence):
                 varidx = self.atom2var[gndatom.idx] 
                 validx = self.val2idx[varidx][value]
             # if there are two different values needed to render the formula true...
             if assignment[varindices.index(varidx)] is not None and assignment[varindices.index(varidx)] != value:
                 if formula.weight == HARD:
                     if conj: # ...if it's a hard conjunction, the MLN is unsatisfiable -- e.g. foo(x) ^ !foo(x) 
                         raise SatisfiabilityException('Knowledge base is unsatisfiable due to hard constraint violation: %s' % formula)
                     elif disj: # ...if it's a hard disjunction, it's a tautology -- e.g. foo(x) v !foo(x)
                         continue
                 else: # for soft constraints, unsatisfiable formulas and tautologies  can be ignored
                     return None
             assignment[varindices.index(varidx)] = validx
         if not defaultProcedure:
             maxtruth = formula.maxtruth(self.mrf.evidence)
             mintruth = formula.mintruth(self.mrf.evidence)
             if formula.weight == HARD and (maxtruth in Interval(']0,1[') or mintruth in Interval(']0,1[')):
                 raise MRFValueException('No fuzzy truth values are allowed in hard constraints.')
             if conj:
                 if formula.weight == HARD:
                     cost = 0
                     defcost = self.wcsp.top
                 else:
                     cost = formula.weight * (1 - maxtruth)
                     defcost = formula.weight
             else:
                 if formula.weight == HARD:
                     cost = self.wcsp.top
                     defcost = 0
                 else:
                     defcost = 0
                     cost = formula.weight * (1 - mintruth)
             if len(assignment) != len(varindices):
                 raise MRFValueException('Illegal variable assignments. Variables: %s, Assignment: %s' % (varindices, assignment))
             return {cost: [tuple(assignment)], defcost: 'else'}
     if defaultProcedure: 
         # fallback: go through all combinations of truth assignments
         domains = [self.domains[v] for v in varindices]
         cost2assignments = defaultdict(list)
         # compute number of worlds to be examined and print a warning
         worlds = 1
         for d in domains: worlds *= len(d)
         if worlds > 1000000:
             logger.warning('!!! WARNING: %d POSSIBLE WORLDS ARE GOING TO BE EVALUATED. KEEP IN SIGHT YOUR MEMORY CONSUMPTION !!!' % worlds)
         for c in combinations(domains):
             world = [0] * len(self.mrf.gndatoms)
             assignment = []
             for varidx, value in zip(varindices, c):
                 world = self.variables[varidx].setval(value, world)
                 assignment.append(self.val2idx[varidx][value])
             # the MRF feature imposed by this formula 
             truth = formula(world)
             if truth is None:
                 print 'POSSIBLE WORLD:'
                 print '==============='
                 self.mrf.print_world_vars(world)
                 print 'GROUND FORMULA:'
                 print '==============='
                 formula.print_structure(world)
                 raise Exception('Something went wrong: Truth of ground formula cannot be evaluated (see above)')
             
             if truth in Interval(']0,1[') and formula.weight == HARD:
                 raise MRFValueException('No fuzzy truth values are allowed in hard constraints.')
             if formula.weight == HARD:
                 if truth == 1:
                     cost = 0
                 else:
                     cost = self.wcsp.top
             else:
                 cost = ((1 - truth) * formula.weight)
             cost2assignments[cost].append(tuple(assignment))
         return cost2assignments
     assert False # unreachable