Example #1
0
    def on_new_cycle(self, messages, cycle_id) -> Optional[List]:

        assignment = {self.variable.name: self.current_value}
        for sender, (message, t) in messages.items():
            assignment[sender] = message.value

        self.logger.debug(
            f"Full neighbors assignment for cycle {self.cycle_count} : {assignment}"
        )

        current_cost = assignment_cost(assignment, self.constraints)
        # Compute best local cost, based on current neighbors values:
        arg_min, min_cost = find_optimal(self.variable, assignment,
                                         self.constraints, self.mode)

        self.logger.debug(
            f"Evaluate cycle {self.cycle_count}: current cost {current_cost} - best cost {min_cost}"
        )

        if current_cost - min_cost > 0 and 0.5 > random.random():
            self.value_selection(arg_min[0])
            self.logger.debug(
                f"Select new value {arg_min} for better cost {min_cost} ")
        else:
            self.logger.debug(f"Do not change value {self.current_value}")

        self.post_to_all_neighbors(DsaMessage(self.current_value))
Example #2
0
def get_next_assignment(
    variable: Variable,
    current_value: Optional[VarVal],
    constraints: List[Constraint],
    current_path: Path,
    upper_bound: Cost,
    mode: str,
):
    """
    Find the first next value in `variable`'s domain that respects `upper_bound`.

    Parameters
    ----------
    variable: variable
        The variable for which we want to select a value
    current_value: value or `None`
        The value currently assigned to `variable`
    constraints:
        The set of constraints `variable` is involved in
    current_path: Path
        a path assigning a value for each variable before `variable`
    upper_bound: float
        current bound for the path
    mode: str
        "min" or "max"

    Returns
    -------

    """
    candidates = get_value_candidates(variable, current_value)

    found = None
    for candidate in candidates:
        # Check if assigning candidate value to the variable would cause the global
        # cost to exceed the upper-bound.
        candidate_cost = 0
        if not current_path:
            return candidate, 0
        for var, val, elt_cost in current_path:
            var_constraints = constraints_for_variable(constraints, var)
            # This only works for binary constraints, we could extend it to n-ary constraints
            ass_cost = assignment_cost({
                var: val,
                variable.name: candidate
            }, var_constraints)
            candidate_cost += ass_cost
            if mode == "min" and (candidate_cost >= upper_bound
                                  or ass_cost + elt_cost >= upper_bound):
                break  # Try next value in domain.
            else:
                found = candidate, candidate_cost  # Check for next elt in path.
        if mode == "max" and candidate_cost > upper_bound:
            found = candidate, candidate_cost

        if found:
            return found

    return None
Example #3
0
    def compute_best_value(self) -> Tuple[Any, float]:

        arg_min, min_cost = None, float('inf')
        for value in self.variable.domain:
            self.current_cycle[self.variable.name] = value
            cost = assignment_cost(self.current_cycle, self.constraints)
            if cost < min_cost:
                min_cost, arg_min = cost, value
        return arg_min, min_cost
Example #4
0
    def _find_best_offer(
            self, all_offers: List[Tuple[str, Dict]]) -> Tuple[List, float]:
        """
        Find the offer that maximize the global gain of both partners in
        the given offers and for the given partner.

        Parameters
        ----------
        all_offers: list
            a list of couples (offerer_name, offer) where offer is a dictionary of
            offers {(partner_val, my_val): partner_gain} Mgm2OfferMessage

        Returns
        -------
        list:
            list of best offers (i.ee with the best gain)
        best_gain:
            gain for the best offers.
        """
        bests, best_gain = [], 0

        for partner, offers in all_offers:
            partial_asgt = self._neighbors_values.copy()
            current_partner = self._neighbor_var(partner)

            # Filter out the constraints linking those two variables to avoid
            # counting their cost twice.
            shared = find_dependent_relations(current_partner,
                                              self._constraints)
            concerned = [rel for rel in self._constraints if rel not in shared]

            for (val_p, my_offer_val), partner_local_gain in offers.items():
                partial_asgt.update({
                    partner: val_p,
                    self.variable.name: my_offer_val
                })

                # Then we evaluate the agent constraint's for the offer
                # and add the partner's local gain.
                cost = assignment_cost(partial_asgt, concerned)
                global_gain = self.current_cost - cost + partner_local_gain

                if (global_gain > best_gain
                        and self._mode == "min") or (global_gain < best_gain
                                                     and self._mode == "max"):
                    bests = [(val_p, my_offer_val, partner)]
                    best_gain = global_gain
                elif global_gain == best_gain:
                    bests.append((val_p, my_offer_val, partner))

        return bests, best_gain
Example #5
0
    def tick(self):
        if self.is_paused:
            return
        # Check if we have a value for all our neighbors
        if len(self.current_assignment) == len(self.neighbors):

            if self.logger.isEnabledFor(logging.DEBUG):
                self.logger.debug(
                    "Full neighbors assignment on periodic action %s : %s ",
                    self.cycle_count,
                    self.current_assignment,
                )

            assignment = self.current_assignment.copy()
            args_best, best_cost = self.find_best_values(assignment)

            # if self.current_value is not None:
            assignment[self.variable.name] = self.current_value
            current_cost = assignment_cost(assignment, self.constraints)
            delta = abs(current_cost - best_cost)
            if self.logger.isEnabledFor(logging.DEBUG):
                self.logger.debug(
                    f"Current value {self.current_value}, cost {current_cost}, "
                    f"best cost {best_cost} "
                    f"delta {delta}"
                )
            if self.variant == "A":
                self.variant_a(delta, best_cost, args_best)
            elif self.variant == "B":
                self.variant_b(delta, best_cost, args_best)
            elif self.variant == "C":
                self.variant_c(delta, best_cost, args_best)

        else:
            n = len(self.neighbors)
            c = len(self.current_assignment)

            print(f" {self.name} Still waiting for neighbors values {n-c} out of {n} ")
            if self.logger.isEnabledFor(logging.DEBUG):
                self.logger.debug(
                    f"Still waiting for neighbors values {n-c} out of {n} "
                )

        # In order to be more resilient to message loss, we send our value even
        # if it did not change.
        self.post_to_all_neighbors(ADsaMessage(self.current_value))
Example #6
0
    def find_best_values(
            self, assignment: Dict[Any, float]) -> Tuple[List[Any], float]:
        """
        Find the best values for our variable, given the current assignment.

        Find the values from the domain of our variable that yield the best
        cost (min or max depending of mode) given the assignment known for our
        neighbors.

        Parameters
        ----------
        assignment:
            The current assignment

        Returns
        -------
        List[Any]
            A list of values from the domain of our variable
        float
            The cost achieved with these values.
        """
        assignment = assignment.copy()

        arg_best, best_cost = None, float("inf")
        if self.mode == "max":
            arg_best, best_cost = None, -float("inf")

        for value in self.variable.domain:
            assignment[self.variable.name] = value
            cost = assignment_cost(assignment, self.constraints)

            # Take into account variable cost, if any
            cost += self.variable.cost_for_val(value)

            if cost == best_cost:
                arg_best.append(value)
            elif (self.mode == "min"
                  and cost < best_cost) or (self.mode == "max"
                                            and cost > best_cost):
                best_cost, arg_best = cost, [value]

        return arg_best, best_cost
Example #7
0
    def evaluate_cycle(self):

        self.logger.debug('Full neighbors assignment for cycle %s : %s ',
                          self.cycle_count, self.current_cycle)

        arg_min, min_cost = self.compute_best_value()
        self.current_cycle[self.variable.name] = self.current_value
        current_cost = assignment_cost(self.current_cycle, self.constraints)

        self.logger.debug("Evaluate cycle %s: current cost %s - best cost %s",
                          self.cycle_count, current_cost, min_cost)

        if current_cost - min_cost > 0 and 0.5 > random.random():
            self.value_selection(arg_min)
            self.logger.debug("Select new value %s for better cost %s ",
                              self.cycle_count, min_cost)
        else:
            self.logger.debug("Do not change value %s ", self.current_value)

        self.new_cycle()
        self.current_cycle, self.next_cycle = self.next_cycle, {}
        self.post_to_all_neighbors(DsaMessage(self.current_value))
Example #8
0
    def evaluate_cycle(self):

        if len(self.current_cycle) == len(self.neighbors):

            self.logger.debug(
                "Full neighbors assignment for cycle %s : %s ",
                self.cycle_count,
                self.current_cycle,
            )

            self.current_cycle[self.variable.name] = self.current_value
            assignment = self.current_cycle.copy()
            args_best, best_cost = find_optimal(self.variable, assignment,
                                                self.constraints, self.mode)
            current_cost = assignment_cost(self.current_cycle,
                                           self.constraints)
            delta = abs(current_cost - best_cost)
            self.logger.debug(
                f"Current cost {current_cost}, best cost {best_cost} "
                f"delta {delta}")

            if self.variant == "A":
                self.variant_a(delta, best_cost, args_best)
            elif self.variant == "B":
                self.variant_b(delta, best_cost, args_best)
            elif self.variant == "C":
                self.variant_c(delta, best_cost, args_best)

            self.new_cycle()
            self.current_cycle, self.next_cycle = self.next_cycle, {}

            # Check if this was the last cycle
            if self.stop_cycle and self.cycle_count >= self.stop_cycle:
                self.finished()
                self.stop()
                return

            self.post_to_all_neighbors(DsaMessage(self.current_value))
Example #9
0
 def _compute_cost(self, **kwargs):
     return assignment_cost(kwargs, self._constraints)