def on_start(self): msg_count, msg_size = 0, 0 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 self.logger.info('Leaf %s prepares init message %s -> %s ', self._variable.name, self._variable.name, self._parent) util = self._compute_utils_msg() msg = DpopMessage('UTIL', util) self.post_msg(self._parent, msg) msg_count += 1 msg_size += msg.size elif self.is_leaf: # we are both root and leaf : means we are a isolated variable we # can select our own value alone: for r in self._constraints: self._joined_utils = join_utils(self._joined_utils, r) values, current_cost = find_arg_optimal(self._variable, self._joined_utils, self._mode) self.value_selection(values[0], float(current_cost)) self.logger.info('Value selected at %s : %s - %s', self.name, self.current_value, self.current_cost) return { 'num_msg_out': msg_count, 'size_msg_out': msg_size, 'current_value': self.current_value }
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 hasattr(var, 'cost_for_val'): 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 test_findargmin_several_values(self): v1 = Variable('v1', list(range(10))) f1 = UnaryFunctionRelation('f1', v1, lambda x: 2 if 3 < x < 6 else 10) values, c = algorithms.find_arg_optimal(v1, f1, mode='min') self.assertEqual(len(values), 2) self.assertIn(4, values) self.assertIn(5, values) self.assertEqual(c, 2)
def test_findargmin_fct(self): v1 = Variable('v1', list(range(10))) f1 = UnaryFunctionRelation('f1', v1, lambda x: abs(x-5)) m, c = algorithms.find_arg_optimal(v1, f1, mode='min') self.assertEqual(len(m), 1) self.assertEqual(m[0], 5) self.assertEqual(c, 0)
def test_findargmin(self): # u1 is a relation with a single variable : x1 = Variable('x1', ['a', 'b', 'c']) u1 = pydcop.dcop.relations.NAryMatrixRelation([x1], np.array([2, 4, 8], np.int8)) # take the projection of u1 along x1 m, c= algorithms.find_arg_optimal(x1, u1, mode='min') self.assertEqual(len(m), 1) self.assertEqual(m[0], 'a') self.assertEqual(c, 2)
def _on_value_message(self, variable_name, recv_msg, t): self.logger.debug('{}: on value message from {} : "{}"'.format( self.name, variable_name, recv_msg)) value = recv_msg.content msg_count, msg_size = 0, 0 # 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('Slicing relation on %s', 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('Relation after slicing %s', rel) values, current_cost = find_arg_optimal(self._variable, rel, self._mode) self.value_selection(values[0], float(current_cost)) self.logger.info('on VALUE msg from %s, %s select value %s cost=%s', variable_name, self.name, self.current_value, self.current_cost) for c in self._children: variables_msg = [self._variable] values_msg = [self.current_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)) msg_count += 1 msg_size += msg.size self.post_msg(c, msg) return { 'num_msg_out': msg_count, 'size_msg_out': msg_size, 'current_value': self.current_value }
def _compute_best_value(self): reduced_cs = [] concerned_vars = set() for c in self.constraints: 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) for var in concerned_vars: if hasattr(var, 'cost_for_val'): 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_util_message(self, variable_name, recv_msg, t): self.logger.debug('Util message from %s : %r ', variable_name, recv_msg.content) utils = recv_msg.content msg_count, msg_size = 0, 0 # accumulate util messages until we got the UTIL from all our children self._joined_utils = join_utils(self._joined_utils, utils) try: self._waited_children.remove(variable_name) except ValueError as e: self.logger.error('Unexpected UTIL message from %s on %s : %r ', variable_name, 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_utils(self._joined_utils, r) values, current_cost = find_arg_optimal( self._variable, self._joined_utils, self._mode) self.value_selection(values[0], float(current_cost)) self.logger.info('Value selected : %s - %s', self.current_value, self.current_cost) self.logger.info( 'ROOT: On UNTIL message from %s, send value ' 'msg to childrens %s ', variable_name, self._children) for c in self._children: msg = DpopMessage('VALUE', ([self._variable], [self.current_value])) self.post_msg(c, msg) msg_count += 1 msg_size += msg.size 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( 'On UTIL message from %s, send UTILS msg ' 'to parent %s ', variable_name, self._children) self.post_msg(self._parent, msg) msg_count += 1 msg_size += msg.size return { 'num_msg_out': msg_count, 'size_msg_out': msg_size, 'current_value': self.current_value }