def eval_queries(world): """ Evaluates the queries given a possible world. """ numerators = [0] * len(global_enumAsk.queries) denominator = 0 expsum = 0 for gf in global_enumAsk.grounder.itergroundings(): if global_enumAsk.soft_evidence_formula(gf): expsum += gf.noisyor(world) * gf.weight else: truth = gf(world) if gf.weight == HARD: if truth in Interval(']0,1['): raise Exception('No real-valued degrees of truth are allowed in hard constraints.') if truth == 1: continue else: return numerators, 0 expsum += gf(world) * gf.weight expsum = exp(expsum) # update numerators for i, query in enumerate(global_enumAsk.queries): if query(world): numerators[i] += expsum denominator += expsum return numerators, denominator
def consistent(self, world, strict=False): ''' Checks for this variable if its assignment in the assignment `evidence` is consistent. :param evidence: the assignment to be checked. :param strict: if True, no unknown assignments are allowed, i.e. there must not be any ground atoms in the variable that do not have a truth value assigned. ''' total = 0 evstr = ','.join([ifNone(world[atom.idx], '?', str) for atom in self.gndatoms]) for gnatom in self.gndatoms: val = world[gnatom.idx] if strict and val is None: raise MRFValueException('Not all values have truth assignments: %s: %s' % (repr(self), evstr)) total += ifNone(val, 0) if not (total == 1 if strict else total in Interval('[0,1]')): raise MRFValueException('Invalid value of variable %s: %s' % (repr(self), evstr)) return True
def soft_evidence_formula(self, gf): return isinstance(self.mrf.mln.logic, FirstOrderLogic) and any(map(lambda a: a.truth(self.mrf.evidence) in Interval('(0,1)'), gf.gndatoms()))
def set_evidence(self, atomvalues, erase=False, cw=False): ''' Sets the evidence of variables in this MRF. If erase is `True`, for every ground atom appearing in atomvalues, the truth values of all ground ground atom in the respective MRF variable are erased before the evidences are set. All other ground atoms stay untouched. :param atomvalues: a dict mapping ground atom strings/objects/indices to their truth values. :param erase: specifies whether or not variables shall be erased before asserting the evidences. :param cw: applies the closed-world assumption for all non evidence atoms. ''' # check validity of evidence values atomvalues_ = {} for key, value in dict(atomvalues).iteritems(): # convert boolean to numeric values if value in (True, False): atomvalues[key] = {True: 1, False: 0}[value] value = atomvalues[key] gndatom = self.gndatom(key) if gndatom is None: self.print_gndatoms() raise MRFValueException('"%s" is not among the ground atoms.' % key) atomvalues_[str(gndatom)] = value var = self.variable(gndatom) if isinstance(self.mln.logic, FuzzyLogic): if (isinstance(var, MutexVariable) or isinstance(var, SoftMutexVariable) or isinstance(var, BinaryVariable)) and value is not None and value in Interval(']0,1['): raise MRFValueException('Illegal value for the (soft-) mutex or binary variable "%s": %s' % (str(var), value)) atomvalues = atomvalues_ if erase: # erase all variable assignments appearing in atomvalues for key, _ in atomvalues.iteritems(): var = self.variable(self.gndatom(key)) # unset all atoms in this variable for atom in var.gndatoms: self._evidence[atom.idx] = None for key, value in atomvalues.iteritems(): gndatom = self.gndatom(key) var = self.variable(gndatom) # create a template with admissible truth values for all # ground atoms in this variable values = [-1] * len(var.gndatoms) if isinstance(var, FuzzyVariable): self._evidence[gndatom.idx] = value continue elif isinstance(var, BinaryVariable): self._evidence[gndatom.idx] = value continue for _, val in var.itervalues(evidence={gndatom.idx: value}): for i, (v, v_) in enumerate(zip(values, val)): if v == -1: values[i] = v_ elif v is not None and v != v_: values[i] = None for atom, val in zip(var.gndatoms, values): curval = self._evidence[atom.idx] if curval is not None and val is not None and curval != val: raise MRFValueException('Contradictory evidence in variable %s: %s = %s vs. %s' % (var.name, str(gndatom), curval, val)) elif curval is None and val is not None: self._evidence[atom.idx] = val if cw: self.apply_cw()
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