Beispiel #1
0
    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
Beispiel #2
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))
            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)
Beispiel #3
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)
Beispiel #4
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))
Beispiel #5
0
    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)