def _compute_best_value(self): """ Compute the best evaluation the variable can have wrt the current values of neighbors. :return: (list of values the variable that lead to the best evaluation, best evaluation) """ reduced_cs = [] concerned_vars = set() for c in self.utilities: asgt = filter_assignment_dict(self._neighbors_values, c.dimensions) reduced_cs.append(c.slice(asgt)) concerned_vars.update(c.dimensions) var_val, rel_val = find_arg_optimal( self.variable, lambda x: functools.reduce(operator.add, [f(x) for f in reduced_cs]), self._mode, ) # Add the cost for each variable value if any for var in concerned_vars: if var.name == self.name: rel_val += var.cost_for_val(self.current_value) else: rel_val += var.cost_for_val(self._neighbors_values[var.name]) return var_val, rel_val
def on_start(self): if self.is_leaf and not self.is_root: # If we are a leaf in the DFS Tree we can immediately compute # our util and send it to our parent. # Note: as a leaf, our separator is the union of our parents and # pseudo-parents util = self._compute_utils_msg() self.logger.info( f"Leaf {self._variable.name} init message {self._variable.name} -> {self._parent} : {util}" ) msg = DpopMessage("UTIL", util) self.post_msg(self._parent, msg) elif self.is_leaf: # we are both root and leaf : means we are a isolated variable we # can select our own value alone: if self._constraints: for r in self._constraints: self._joined_utils = join(self._joined_utils, r) values, current_cost = find_arg_optimal( self._variable, self._joined_utils, self._mode) self.select_value_and_finish(values[0], float(current_cost)) else: # If the variable is not constrained, we can simply take a value at # random: value = choice(self._variable.domain) self.select_value_and_finish(value, 0.0)
def on_start(self): if self.is_leaf and not self.is_root: # If we are a leaf in the DFS Tree we can immediately compute # our util and send it to our parent. # Note: as a leaf, our separator is the union of our parents and # pseudo-parents util = self._compute_utils_msg() self.logger.info( f"Leaf {self._variable.name} init message {self._variable.name} -> {self._parent} : {util}" ) msg = DpopMessage("UTIL", util) self.post_msg(self._parent, msg) elif self.is_leaf: # we are both root and leaf : means we are a isolated variable we # can select our own value alone: if self._constraints: for r in self._constraints: self._joined_utils = join(self._joined_utils, r) values, current_cost = find_arg_optimal( self._variable, self._joined_utils, self._mode ) self.select_value_and_finish(values[0], float(current_cost)) elif hasattr(self._variable, "cost_for_val"): # The variable has no constraint with other variable but has a cost function, # (i.e a unary constraint) : select the value that optimize that constraint. self.logger.debug( f"Selecting value for {self._variable.name} based only on cost function" ) values, current_cost = find_arg_optimal( self._variable, self._joined_utils, self._mode ) self.select_value_and_finish(values[0], float(current_cost)) else: # If the variable is not constrained, we can simply take a value at # random: self.logger.debug( f"Selecting random value for {self._variable.name} (not constrained)" ) value = choice(self._variable.domain) self.select_value_and_finish(value, 0.0)
def _on_value_message(self, variable_name, recv_msg, t) -> None: """ Message handler for VALUE messages. Parameters ---------- variable_name: str name of the variable that sent the message recv_msg: DpopMessage received message t: int message timestamp """ self.logger.debug( f"{self.name}: on value message from {variable_name} : '{recv_msg}' at {t}" ) value = recv_msg.content # Value msg contains the optimal assignment for all variables in our # separator : sep_vars, sep_values = value value_dict = {k.name: v for k, v in zip(*value)} self.logger.debug(f"Slicing relation on {value_dict}") # as the value msg contains values for all variables in our # separator, slicing the util on these variables produces a relation # with a single dimension, our own variable. rel = self._joined_utils.slice(value_dict) self.logger.debug(f"Relation after slicing {rel}") values, current_cost = find_arg_optimal(self._variable, rel, self._mode) selected_value = values[0] for c in self._children: variables_msg = [self._variable] values_msg = [selected_value] # own_separator intersection child_separator union # self.current_value for v in self._children_separator[c]: try: values_msg.append(value_dict[v.name]) variables_msg.append(v) except KeyError: # we want an intersection, we can ignore the variable if # not in value_dict pass msg = DpopMessage("VALUE", (variables_msg, values_msg)) self.post_msg(c, msg) self.select_value_and_finish(selected_value, float(current_cost))
def _on_util_message(self, variable_name, recv_msg, t) -> None: """ Message handler for UTIL messages. Parameters ---------- variable_name: str name of the variable that sent the message recv_msg: DpopMessage received message t: int message timestamp """ self.logger.debug( f"UTIL from {variable_name} : {recv_msg.content} at {t}") utils = recv_msg.content # accumulate util messages until we got the UTIL from all our children self._joined_utils = join(self._joined_utils, utils) try: self._waited_children.remove(variable_name) except ValueError as e: self.logger.error( f"Unexpected UTIL message from {variable_name} on {self.name} : {recv_msg} " ) raise e # keep a reference of the separator of this children, we need it when # computing the value message self._children_separator[variable_name] = utils.dimensions if len(self._waited_children) == 0: if self.is_root: # We are the root of the DFS tree and have received all utils # we can select our own value and start the VALUE phase. # The root obviously has no parent nor pseudo parent, yet it # may have unary relations (with it-self!) for r in self._constraints: self._joined_utils = join(self._joined_utils, r) values, current_cost = find_arg_optimal( self._variable, self._joined_utils, self._mode) selected_value = values[0] self.logger.info( f"ROOT: On UTIL from {variable_name}, send VALUE to childrens {self._children} " ) for c in self._children: msg = DpopMessage("VALUE", ([self._variable], [selected_value])) self.post_msg(c, msg) self.select_value_and_finish(selected_value, float(current_cost)) else: # We have received the Utils msg from all our children, we can # now compute our own utils relation by joining the accumulated # util with the relations with our parent and pseudo_parents. util = self._compute_utils_msg() msg = DpopMessage("UTIL", util) self.logger.info( f"On UTIL from {variable_name}, send UTIL to parent {self._parent} " ) self.post_msg(self._parent, msg)