Beispiel #1
0
 def _increase_cost(self, constraint: NAryMatrixRelation):
     """
     Increase the cost(s) of a constraint according to the given
     increase_mode
     :param constraint: a constraint as NAryMatrixRelation
     :return:
     """
     asgt = self._neighbors_values.copy()
     asgt[self.name] = self.current_value
     self.logger.debug('%s increases cost for %s', self.name, constraint)
     if self._increase_mode == 'E':
         self._increase_modifier(constraint, asgt)
     elif self._increase_mode == 'R':
         for val in self.variable.domain:
             asgt[self.name] = val
             self._increase_modifier(constraint, asgt)
     elif self._increase_mode == 'C':
         # Creates all the assignments for the constraints, with the
         # agent variable set to its current value
         asgts = generate_assignment_as_dict(list(self._neighbors))
         for ass in asgts:
             ass[self.name] = self.current_value
             self._increase_modifier(constraint, ass)
     elif self._increase_mode == 'T':
         # Creates all the assignments for the constraints
         asgts = generate_assignment_as_dict(constraint.dimensions)
         for ass in asgts:
             self._increase_modifier(constraint, ass)
Beispiel #2
0
def _yaml_constraints(constraints: Iterable[RelationProtocol]):
    constraints_dict = {}
    for r in constraints:
        if hasattr(r, 'expression'):

            constraints_dict[r.name] = {
                'type': 'intention',
                'function': r.expression
            }
        else:
            # fallback to extensional constraint
            variables = [v.name for v in r.dimensions]
            values = defaultdict(lambda: [])

            for assignment in generate_assignment_as_dict(r.dimensions):
                val = r(**assignment)
                ass_str = ' '.join([str(assignment[var]) for var in variables])
                values[val].append(ass_str)

            for val in values:
                values[val] = ' | '.join(values[val])
            values = dict(values)
            constraints_dict[r.name] = {
                'type': 'extensional',
                'variables': variables,
                'values': values
            }

    return yaml.dump({'constraints': constraints_dict},
                     default_flow_style=False)
Beispiel #3
0
    def _compute_offers_to_send(self):
        """
        Computes all the coordinated moves with the partner (if exists).
        It also set the attribute best_unilateral_move, which corresponds to
        the best eval the agent can achieve if it moves alone and the list of
        values to achieve this eval

        :return: a dictionary which keys are couples (my_value,
        my_partner_value) and which values are the gain realized by the
        offerer thanks to this coordinated change.

        """
        partial_asgt = self._neighbors_values.copy()
        offers = dict()

        for limited_asgt in generate_assignment_as_dict(
            [self.variable, self._partner]):
            partial_asgt.update(limited_asgt)
            cost = self._compute_cost(partial_asgt)

            if (self.current_cost > cost and self._mode == 'min') or \
                    (self.current_cost < cost and self._mode == 'max'):
                offers[(limited_asgt[self.name],
                        limited_asgt[self._partner.name])] = self.current_cost\
                                                             - cost
        return offers
    def test_generate_1var(self):
        x1 = Variable('x1', ['a', 'b', 'c'])

        ass = list(algorithms.generate_assignment_as_dict([x1]))

        self.assertEqual(len(ass), len(x1.domain))
        self.assertIn({'x1': 'a'}, ass)
        self.assertIn({'x1': 'b'}, ass)
        self.assertIn({'x1': 'c'}, ass)
Beispiel #5
0
    def from_func_relation(rel: RelationProtocol) -> 'NAryMatrixRelation':
        variables = rel.dimensions
        cost_matrix = NAryMatrixRelation(variables)
        # We also compute the min and max value of the constraint as it is to
        # be needed in gdba
        mini = None
        maxi = None

        for asgt in generate_assignment_as_dict(variables):
            value = rel(asgt)
            cost_matrix = cost_matrix.set_value_for_assignment(asgt, value)

        return cost_matrix
Beispiel #6
0
 def _compute_boundary(self, constraints):
     constraints_list = list()
     optimum = 0
     for c in constraints:
         variables = [v for v in c.dimensions if v != self._variable]
         boundary = None
         for asgt in generate_assignment_as_dict(variables):
             rel = c.slice(filter_assignment_dict(asgt, c.dimensions))
             for val in self._variable.domain:
                 rel_val = rel(val)
                 if boundary is None:
                     boundary = rel_val
                 elif self.mode == 'max' and rel_val > boundary:
                     boundary = rel_val
                 elif self.mode == 'min' and rel_val < boundary:
                     boundary = rel_val
         constraints_list.append(c)
         optimum += boundary
     return constraints_list, optimum
Beispiel #7
0
def find_optimum(rel: RelationProtocol, mode: str) -> float:
    """
    Compute the optimum of the relation given the mode
    :param rel:
    :param mode: 'min' or 'max'
    :return: The optimum value of the relation as a float

    """
    if mode != "min" and mode != "max":
        raise ValueError("mode must be 'min' or 'max'")
    variables = [v for v in rel.dimensions]
    optimum = None
    for asgt in generate_assignment_as_dict(variables):
        rel_val = rel(**filter_assignment_dict(asgt, rel.dimensions))
        if optimum is None:
            optimum = rel_val
        elif mode == 'max' and rel_val > optimum:
            optimum = rel_val
        elif mode == 'min' and rel_val < optimum:
            optimum = rel_val
    return optimum
Beispiel #8
0
def join_utils(u1: Constraint, u2: Constraint) -> Constraint:
    """
    Build a new relation by joining the two relations u1 and u2.

    The dimension of the new relation is the union of the dimensions of u1
    and u2. As order is important for most operation, variables for u1 are
    listed first, followed by variables from u2 that where already used by u1
    (in the order in which they appear in u2.dimension).

    For any complete assignment, the value of this new relation is the sum of
    the values from u1 and u2 for the subset of this assignment that apply to
    their respective dimension.

    For more details, see the definition of the join operator in Petcu Phd
    Thesis.

    :param u1: n-ary relation
    :param u2: n-ary relation
    :return: a new relation
    """
    #
    dims = u1.dimensions[:]
    for d2 in u2.dimensions:
        if d2 not in dims:
            dims.append(d2)

    u_j = NAryMatrixRelation(dims, name='joined_utils')
    for ass in generate_assignment_as_dict(dims):

        # FIXME use dict for assignement
        # for Get AND sett value

        u1_ass = filter_assignment_dict(ass, u1.dimensions)
        u2_ass = filter_assignment_dict(ass, u2.dimensions)
        s = u1(**u1_ass) + u2(**u2_ass)
        u_j = u_j.set_value_for_assignment(ass, s)

    return u_j
Beispiel #9
0
    def __init__(self, variable, constraints, variant='B', proba_hard=0.7,
                 proba_soft=0.7, mode='min', logger=None,
                 comp_def=None):
        """

        :param variable a variable object for which this computation is
        responsible
        :param constraints: the list of constraints involving this variable
        :param variant: the variant of the DSA algorithm : 'A' for DSA-A,
        etc.. possible values avec 'A', 'B' and 'C'
        :param proba_hard : the probability to change the value in case of
        the number of violated hard constraints can be decreased
        :param proba_soft : the probability to change the value in case the
        cost on hard constraints can't be improved, but the cost on soft
        constraints can
        :param mode: optimization mode, 'min' for minimization and 'max' for
        maximization. Defaults to 'min'.

        """
        super().__init__(variable, comp_def)
        self._msg_handlers['mixed_dsa_value'] = self._on_value_msg

        self.proba_hard = proba_hard
        self.proba_soft = proba_soft
        self.variant = variant
        self.mode = mode
        # some constraints might be unary, and our variable can have several
        # constraints involving the same variable
        self._neighbors = set([v.name for c in constraints
                               for v in c.dimensions if v != variable])
        self._neighbors_values = {}

        self.logger = logger if logger is not None else \
            logging.getLogger('pydcop.algo.mixed-dsa'+variable.name)

        self._postponed_messages = list()

        self.hard_constraints = list()
        self.soft_constraints = list()
        self._violated_hard_cons = list()
        self._curr_dcop_cost = None
        self.__optimum_dict__ = {}
        # We do not use pydcop.dcop.relations.find_optimum() to distinguish
        # hard and soft constraints
        for c in constraints:
            hard = False
            variables = [v for v in c.dimensions if v != self._variable]
            boundary = None
            for asgt in generate_assignment_as_dict(variables):
                rel = c.slice(filter_assignment_dict(asgt, c.dimensions))
                for val in self._variable.domain:
                    rel_val = rel(val)
                    if boundary is None:
                        boundary = rel_val
                    elif self.mode == 'max' and rel_val > boundary:
                        boundary = rel_val
                    elif self.mode == 'min' and rel_val < boundary:
                        boundary = rel_val
                    if rel_val == float("inf") or rel_val == -float("inf"):
                        hard = True
            self.__optimum_dict__[c.name] = boundary
            if hard:
                self.hard_constraints.append(c)
            else:
                self.soft_constraints.append(c)

        if not self.hard_constraints:
            global INFINITY
            INFINITY = float("inf")