示例#1
0
def test_memory_footprint_from_classic_import():
    # use maxsum as is has a computation_memory function defined
    import pydcop.algorithms.amaxsum as maxsum_module
    from pydcop.computations_graph.factor_graph import (
        VariableComputationNode as FGVariableComputationNode,
    )

    v1 = Variable("v1", [1, 2])
    comp_def = ComputationDef(
        FGVariableComputationNode(v1, []),
        AlgorithmDef.build_with_default_param("amaxsum"),
    )
    comp = maxsum_module.MaxSumVariableComputation(comp_def=comp_def)

    # The variable has no neighbors : footprint is 0
    assert comp.footprint() == 0
    def test_current_local_cost_unary(self):
        x = Variable("x", list(range(5)))
        #        x2 = Variable('x2', list(range(5)))

        #        @AsNAryFunctionRelation(x, x2)
        #       def phi(x1_):
        #          return x1_
        phi = UnaryFunctionRelation("phi", x, lambda x_: 1
                                    if x_ in [0, 2, 3] else 0)
        computation = Mgm2Computation(x, [phi], comp_def=MagicMock())
        computation.__value__ = 0
        computation2 = Mgm2Computation(x, [phi], comp_def=MagicMock())
        computation2.__value__ = 1

        self.assertEqual(computation._current_local_cost(), 1)
        self.assertEqual(computation2._current_local_cost(), 0)
    def test_must_host_one(self):
        d1 = VariableDomain('d1', '', [1, 2, 3, 5])

        v1 = Variable('v1', d1)
        f1 = relation_from_str('f1', 'v1 * 0.5', [v1])
        cv1 = VariableComputationNode(v1, ['f1'])
        cf1 = FactorComputationNode(f1)
        cg = ComputationsFactorGraph([cv1], [cf1])

        hints = DistributionHints({'a1': ['v1']}, None)
        agents = [AgentDef('a1', capacity=100), AgentDef('a2', capacity=100)]
        agent_mapping = distribute(cg, agents, hints,
                                   computation_memory=lambda x: 10)

        self.assertIn('v1', agent_mapping.computations_hosted('a1'))
        self.assertTrue(is_all_hosted(cg, agent_mapping))
    def test_cost_for_1var(self):
        domain = list(range(10))
        x1 = Variable('x1', domain)

        @AsNAryFunctionRelation(x1)
        def cost(x1_):
            return x1_ * 2

        f = FactorAlgo(cost, comp_def=MagicMock())

        costs = f._costs_for_var(x1)

        # in the max-sum algorithm, for an unary factor the costs is simply
        # the result of the factor function
        self.assertEqual(costs[0], 0)
        self.assertEqual(costs[5], 10)
def test_no_neighbors():
    x1 = Variable("x1", list(range(10)))
    cost_x1 = constraint_from_str('cost_x1', 'x1 *2 ', [x1])

    computation = Mgm2Computation(x1, [cost_x1],
                                  mode='max',
                                  comp_def=MagicMock())

    computation.value_selection = MagicMock()
    computation.finished = MagicMock()
    vals, cost = computation._compute_best_value()
    assert cost == 18
    assert set(vals) == {9}

    computation.on_start()
    computation.value_selection.assert_called_once_with(9, 18)
    computation.finished.assert_called_once_with()
示例#6
0
def peav_variables_for_resource(
        resource: Resource, events: Dict[EVT, Event],
        slots_count: int) -> Dict[Tuple[RESOURCE, EVT], Variable]:
    variables: Dict[Tuple[RESOURCE, EVT], Variable] = {}
    for event in events.values():
        if resource.id in event.resources:
            name = f"v_{resource.id:02d}_{event.id:02d}"
            # The domain represents the start time (as slot) this event could start at.
            # Time slots start at 1, the value 0 represents a combination
            # (event, resource) that is not scheduled.
            domain = Domain(
                f"d_{name}",
                "time_slot",
                values=range(0, slots_count - event.length + 2),
            )
            variables[resource.id, event.id] = Variable(name, domain)
    return variables
示例#7
0
    def test_eff_cost_A_unary(self):
        domain = list(range(3))
        x1 = Variable('x1', domain)

        @AsNAryFunctionRelation(x1)
        def phi(x1_):
            return x1_

        g = GdbaComputation(x1, [phi], comp_def=MagicMock())
        c, _, _ = g.__constraints__[0]
        g.__value__ = 0
        asgt = frozenset({'x1': 0}.items())
        g.__constraints_modifiers__[c][asgt] = 5

        self.assertEqual(g._eff_cost(c, 0), 5)
        self.assertEqual(g._eff_cost(c, 1), 1)
        self.assertEqual(g._eff_cost(c, 2), 2)
def test_deploy_computation_request(orchestrated_agent):
    orchestrated_agent.start()
    orchestrated_agent.add_computation = MagicMock()

    mgt = orchestrated_agent._mgt_computation
    v1 = Variable('v1', [1, 2, 3])
    comp_node = VariableComputationNode(v1, [])
    comp_def = ComputationDef(comp_node, AlgoDef('dsa'))
    mgt.on_message('orchestrator', DeployMessage(comp_def), 0)

    # Check the computation is deployed, but not started, on the agent
    calls = orchestrated_agent.add_computation.mock_calls
    assert len(calls) == 1
    _, args, _ = calls[0]
    computation = args[0]
    assert isinstance(computation, MessagePassingComputation)
    assert not computation.is_running
    def test_unary_function_relation(self):
        x = Variable("x", list(range(5)))
        #        x2 = Variable('x2', list(range(5)))

        #        @AsNAryFunctionRelation(x, x2)
        #       def phi(x1_):
        #          return x1_
        phi = UnaryFunctionRelation("phi", x, lambda x_: 1
                                    if x_ in [0, 2, 3] else 0)
        computation = Mgm2Computation(
            ComputationDef(
                VariableComputationNode(x, [phi]),
                AlgorithmDef.build_with_default_param("mgm2"),
            ))
        computation.__value__ = 0

        self.assertEqual(computation._compute_cost(**{"x": 0}), 1)
def test_build_computation_with_params():
    v1 = Variable('v1', [0, 1, 2, 3, 4])
    n1 = VariableComputationNode(v1, [])
    comp_def = ComputationDef(
        n1,
        AlgorithmDef.build_with_default_param('dsa',
                                              mode='max',
                                              params={
                                                  'variant': 'C',
                                                  'stop_cycle': 10,
                                                  'probability': 0.5
                                              }))
    c = DsaComputation(comp_def)
    assert c.mode == 'max'
    assert c.variant == 'C'
    assert c.stop_cycle == 10
    assert c.probability == 0.5
    def test_simple_fg(self):
        d1 = VariableDomain('d1', '', [1, 2, 3, 5])

        v1 = Variable('v1', d1)
        f1 = relation_from_str('f1', 'v1 * 0.5', [v1])
        cv1 = VariableComputationNode(v1, ['f1'])
        cf1 = FactorComputationNode(f1)
        cg = ComputationsFactorGraph([cv1], [cf1])

        agents= [AgentDef('a1'), AgentDef('a2')]
        distribution = distribute(cg, agents)

        self.assertEqual(len(distribution.agents), 2)
        self.assertEqual(len(distribution.computations), 2)
        self.assertEqual(len(distribution.computations_hosted('a1')), 1)
        self.assertEqual(len(distribution.computations_hosted('a2')), 1)
        self.assertNotEqual(distribution.computations_hosted('a2'),
                            distribution.computations_hosted('a1'))
def test_no_neighbors():
    x1 = Variable("x1", list(range(10)))
    cost_x1 = constraint_from_str("cost_x1", "x1 *2 ", [x1])

    computation = Mgm2Computation(
        ComputationDef(
            VariableComputationNode(x1, [cost_x1]),
            AlgorithmDef.build_with_default_param("mgm2", mode="max"),
        ))

    computation.value_selection = MagicMock()
    computation.finished = MagicMock()
    vals, cost = computation._compute_best_value()
    assert cost == 18
    assert set(vals) == {9}

    computation.on_start()
    computation.value_selection.assert_called_once_with(9, 18)
    computation.finished.assert_called_once_with()
示例#13
0
def test_cost_for_1var():
    domain = list(range(10))
    x1 = Variable("x1", domain)

    @AsNAryFunctionRelation(x1)
    def cost(x1_):
        return x1_ * 2

    comp_def = MagicMock()
    comp_def.algo.algo = "amaxsum"
    comp_def.algo.mode = "min"
    comp_def.node.factor = cost
    f = MaxSumFactorComputation(comp_def=comp_def)

    costs = factor_costs_for_var(cost, x1, f._costs, f.mode)
    # costs = f._costs_for_var(x1)

    # in the max-sum algorithm, for an unary factor the costs is simply
    # the result of the factor function
    assert costs[0] == 0
    assert costs[5] == 10
示例#14
0
def select_value(variable: Variable, costs: Dict[str, Dict],
                 mode: str) -> Tuple[Any, float]:
    """
    Select the value for `variable` with the best cost / reward (depending on `mode`)

    Parameters
    ----------
    variable: Variable
        the variable for which we need to select a value
    costs: Dict
        a dict { factorname : { value : costs}} representing the cost messages received from factors
    mode: str
        min or max
    Returns
    -------
    Tuple:
        a Tuple containing the selected value and the corresponding cost for
        this computation.
    """

    # Select a value from the domain, based on the variable cost and
    # the costs received from neighbor factors
    d_costs = {d: variable.cost_for_val(d) for d in variable.domain}
    for d in variable.domain:
        for f_costs in costs.values():
            d_costs[d] += f_costs[d]

    from operator import itemgetter

    # print(f" ### On selecting value for {variable.name} : {d_costs}")
    if mode == "min":
        optimal_d = min(d_costs.items(), key=itemgetter(1))
    else:
        optimal_d = max(d_costs.items(), key=itemgetter(1))

    return optimal_d[0], optimal_d[1]
def test_communication_load():
    v = Variable('v1', list(range(10)))
    var_node = VariableComputationNode(v, [])
    assert dsa.UNIT_SIZE + dsa.HEADER_SIZE == dsa.communication_load(
        var_node, 'f1')
def toy_pb():
    # A toy problem with 5 variables and 5 constraints.
    # The objective here is to have a problem that is simple enough to be solved
    # manually and used in test, but that is representative enough to be meaningful.
    # For example, it includes a loop to make sure we have pseudo parents
    v_a = Variable("A", ["R", "B"])
    v_b = Variable("B", ["R", "B"])
    v_c = Variable("C", ["R", "B"])
    v_d = Variable("D", ["R", "B"])
    v_e = Variable("E", ["R", "B"])
    c1 = constraint_from_str(
        "c1",
        "{('R', 'B'): 1, "
        " ('R', 'R'): 5, "
        " ('B', 'B'): 3, "
        " ('B', 'R'): 2 "
        "}[(A, B)]",
        [v_a, v_b],
    )
    c2 = constraint_from_str(
        "c2",
        "{('R', 'B'): 2, "
        " ('R', 'R'): 8, "
        " ('B', 'B'): 5, "
        " ('B', 'R'): 3 "
        "}[(A, C)]",
        [v_a, v_c],
    )
    c3 = constraint_from_str(
        "c3",
        "{('R', 'B'): 2, "
        " ('R', 'R'): 4, "
        " ('B', 'B'): 2, "
        " ('B', 'R'): 0 "
        "}[(A, D)]",
        [v_a, v_d],
    )
    c4 = constraint_from_str(
        "c4",
        "{('R', 'B'): 0, "
        " ('R', 'R'): 10, "
        " ('B', 'B'): 2, "
        " ('B', 'R'): 1 "
        "}[(B, D)]",
        [v_b, v_d],
    )
    c5 = constraint_from_str(
        "c5",
        "{('R', 'B'): 2, "
        " ('R', 'R'): 4, "
        " ('B', 'B'): 0, "
        " ('B', 'R'): 15 "
        "}[(D, E)]",
        [v_d, v_e],
    )

    # build the pseudo-tree for this problem
    g = build_computation_graph(None,
                                constraints=[c1, c2, c3, c4, c5],
                                variables=[v_a, v_b, v_c, v_d, v_e])
    return g
def single_variable_pb():
    x1 = Variable("x1", ["R", "B"])
    # build the pseudo-tree for this problem
    g = build_computation_graph(None, constraints=[], variables=[x1])
    return g
示例#18
0
    def test_variable_memory_two_neighbor(self):
        d1 = VariableDomain("d1", "", [1, 2, 3, 5])
        v1 = Variable("v1", d1)
        cv1 = VariableComputationNode(v1, ["f1", "f2"])

        self.assertEqual(computation_memory(cv1), VARIABLE_UNIT_SIZE * 4 * 2)
示例#19
0
def generate_mixed_problem(args):
    logger.debug('generate_mixed_problem %s ', args)
    variable_count = args.variable_count
    constraint_count = args.constraint_count
    density = args.density
    real_density = density
    domain_range = args.range
    arity = args.arity
    auto_agents = args.agents is None
    capacity = args.capacity
    agents_count = variable_count if auto_agents else args.agents
    nb_max_edges = constraint_count * min(arity, variable_count)
    edges_count = int(nb_max_edges * density)
    hard_count = int(args.hard_constraint * edges_count)
    logger.info(
        'Generating random DCOP graph with %s variables, whose domain '
        'are [0;%s], %s edges, %s agents, %s hard '
        'constraints and %s soft constraints', variable_count,
        domain_range - 1, edges_count, agents_count, hard_count,
        constraint_count - hard_count)

    if arity > variable_count:
        raise ValueError(
            "The arity of a constraint must be at most the "
            "number of variable. Arity: {}, Nb variables: {}".format(
                arity, variable_count))

    if hard_count < 0:
        raise ValueError(
            "The argument '-h' (or '--hard_count') must be "
            "between 0 and 1. Currently set to: {}".format(hard_count))
    # Create sets for the bipartite graph
    if constraint_count <= 0:
        raise ValueError(
            "The argument '-c' (or '--constraint_count') must be "
            "strictly positive. Currently set to: {}".format(constraint_count))
    if variable_count < 0:
        raise ValueError(
            "The argument '-v' (or '--variable_count') must be "
            "at least 1. Currently set to: {}".format(variable_count))
    if arity <= 0:
        raise ValueError("The argument '-a' (or '--arity') must be "
                         "at least 1. Currently set to: {}".format(arity))

    d = VariableDomain('levels', 'level', range(domain_range))
    variables = {}
    agents = {}
    constraints = {}

    if arity == 1:
        if constraint_count != variable_count:
            raise ValueError("For max arity 1 you need the same number of "
                             "variables, constraints and edges. You asked "
                             "for {} variables and {} constraints.".format(
                                 variable_count, constraint_count))
        nodes = [i + 1 for i in range(variable_count)]
        constraints_list = [("c{}".format(i + 1),
                             'hard') if i < hard_count else
                            ("c{}".format(i + 1), 'soft')
                            for i in range(constraint_count)]
        variables = {}
        constraints = {}
        while len(nodes) != 0:
            n = nodes.pop()
            c = constraints_list.pop(
                random.randint(0,
                               len(constraints_list) - 1))
            w = choose_weight()
            hard = c[1] == 'hard'
            objective = find_objective([w], domain_range - 1, hard)
            if hard:
                expression = "float('inf') if " + str(w) + "*v" + str(n) +\
                             " != " + str(objective) + " else 0"
            else:
                expression = str(w) + "*v" + str(n) + " - " + str(objective)

            v = Variable("v" + str(n), d)
            variables["v" + str(n)] = v
            constraints[c[0]] = relation_from_str(c[0], expression, [v])

            if auto_agents:
                a_name = 'a' + str(n)
                agents[a_name] = AgentDef(a_name, capacity)

        if not auto_agents:
            for i in range(agents_count):
                a_name = 'a' + str(i)
                agents[a_name] = AgentDef(a_name, capacity)

    elif arity == 2:
        edges_count = int(variable_count * (variable_count - 1) * density / 2)
        if constraint_count != edges_count:
            logger.warning("edges count is different of constraint count ({} "
                           "!= {}) but for arity 2, constraints are the deges"
                           "of the graph. We use the density ({}) to determine"
                           " the number of edges".format(
                               edges_count, constraint_count, density))
        is_connected = False
        while not is_connected:
            graph = nx.gnp_random_graph(variable_count, density)
            is_connected = nx.is_connected(graph)

        # Compute nb of hard constraints regarding the true density
        real_density = nx.density(graph)
        hard_count = args.hard_constraint * real_density * args.variable_count\
                     * (args.variable_count + 1) / 2

        for i, node in enumerate(graph.nodes_iter()):
            logger.debug('node %s - %s', node, i)
            name = 'v' + str(i)
            variables[name] = Variable(name, d)
            if auto_agents:
                a_name = 'a' + str(i)
                agents[a_name] = AgentDef(a_name, capacity)

        if not auto_agents:
            for i in range(agents_count):
                a_name = 'a' + str(i)
                agents[a_name] = AgentDef(a_name, capacity)

        constraints = {}
        for i, edge in enumerate(graph.edges_iter()):
            logger.debug('edge %s - %s', edge, i)
            name = 'c' + str(i)
            u, v = edge
            weights = [round(choose_weight(), 2), round(choose_weight(), 2)]

            # Create hard_constraints
            if i < hard_count:
                objective = round(find_objective(weights, domain_range, True),
                                  2)
                expression = "0 if v{} != v{} else float(" \
                             "'inf')".format(u, v)
            else:
                max_val = (weights[0] + weights[1]) * domain_range
                expression = 'abs(v{} + v{} - {})'\
                    .format(u, v, round(random.uniform(0, max_val), 2))

            constraints[name] = relation_from_str(name, expression,
                                                  variables.values())
            logger.debug(repr(constraints[name]))

    else:
        if edges_count < constraint_count and arity != 1:
            raise ValueError(
                "The number of edges must be greater or equal to the "
                "number of constraints. Otherwise you have unused "
                "constraints. Edges: {}, Constraints: {}".format(
                    edges_count, constraint_count))
        nodes = [i for i in list(range(variable_count))]
        constraints = [("c{}".format(i), "hard") if i < hard_count else
                       ("c{}".format(i), "soft")
                       for i in list(range(constraint_count))]
        # Randomly add edges
        edges = defaultdict(lambda: [])  # final set of edges

        # Available edges at a given run
        available_edges = {n: constraints.copy() for n in nodes}
        # First, make sure each variable has one constraint
        for n in nodes:
            if constraints:
                node = n
                c = constraints.pop(random.randint(0, len(constraints) - 1))
            else:
                node, c = choose_in_available_edges(available_edges, n)
            add_edge(node, c, available_edges, edges, arity)
            logger.debug('Add edge (%s, %s)', n, c)
        edges_count -= variable_count

        # Second, make sure each constraint is used
        for c in constraints:
            n = random.choice(nodes)
            add_edge(n, c, available_edges, edges, arity)
            edges_count -= 1
            logger.debug('Add edge (%s, %s)', n, c)

        # Third, randomly add remaining constraints
        while edges_count != 0:
            n, c = choose_in_available_edges(available_edges)
            if (n, c) == (None, None):
                # If more edges than possible are asked, returns just the maximum
                # edges (regarding nodes number and constraints arity)
                logger.warning(
                    '%s edges were not added because you asked for too'
                    ' many edges regarding the number of constraints ('
                    '%s) and their maximum arity (%s)',
                    edges_count - len(edges), constraint_count, args.arity)
                break
            else:
                add_edge(n, c, available_edges, edges, arity)
                edges_count -= 1

        # Now create a DCOP from the graph
        for i in nodes:
            name = 'v' + str(i)
            variables[name] = Variable(name, d)
            if auto_agents:
                a_name = 'a' + str(i)
                agents[a_name] = AgentDef(a_name, capacity)

        if not auto_agents:
            for i in range(agents_count):
                a_name = 'a' + str(i)
                agents[a_name] = AgentDef(a_name, capacity)

        constraints = {}
        for c, neighbors in edges.items():
            logger.debug('Constraint %s, variables: %s', c, neighbors)
            name, is_hard = c[0], c[1] == 'hard'
            c_variables = [variables["v" + str(name)] for name in neighbors]
            addition_string = ""
            first = True
            weights = []
            max_val = 0
            for i in neighbors:
                # Add random weights in constraints
                m = round(choose_weight(), 2)
                weights.append(m)
                max_val += m * (domain_range - 1)
                if not first:
                    addition_string += ' + '
                else:
                    first = False
                if m != 1:
                    addition_string += str(m) + '*'
                addition_string += 'v' + str(i) + ' '

            # To ensure that our hard constraints are achievable, we use
            # the value obtained for a set of random values (in the domain)
            # as the objective.

            objective = round(find_objective(weights, domain_range, is_hard),
                              2)

            if is_hard:
                expression = "0 if " + addition_string + " == " + \
                             str(objective) + " else float('inf')"
            else:
                const_function = "abs(" + addition_string
                if objective:
                    expression = const_function + " - " + str(objective) + ")"
                else:
                    expression = addition_string

            constraints[name] = relation_from_str(name, expression,
                                                  c_variables)
            logger.debug(repr(constraints[name]))

    dcop = DCOP('mixed constraints problem',
                'min',
                domains={'levels': d},
                variables=variables,
                constraints=constraints,
                agents=agents)

    if args.output is not None:
        outputfile = args.output[0]
        if args.correct_density:
            outputfile = correct_density(outputfile, real_density)
        write_in_file(outputfile, dcop_yaml(dcop))
    else:
        print(dcop_yaml(dcop))
示例#20
0
class TestsConstraintViolation(unittest.TestCase):
    domain = list(range(2))
    x1 = Variable('x1', domain)
    x2 = Variable('x2', domain)
    x3 = Variable('x3', domain)

    phi = UnaryFunctionRelation('phi', Variable('x1', domain), lambda x: x)

    phi_n_ary = NAryFunctionRelation(
        lambda x1_, x2_, x3_: 2 if x1_ == x2_ else (1 if x1_ == x3_ else 0),
        [x1, x2, x3])

    def NZ_violation_unary(self):
        g = GdbaComputation(self.x1, [self.phi], comp_def=MagicMock())
        g._neighbors_values['x2'] = 1
        g._neighbors_values['x3'] = 2
        c = g.__constraints__[0]
        self.assertEqual(g._is_violated(c, 0), False)
        self.assertEqual(g._is_violated(c, 1), True)
        self.assertEqual(g._is_violated(c, 2), True)

    def NZ_violation_n_ary(self):
        g = GdbaComputation(self.x1, [self.phi_n_ary], comp_def=MagicMock())
        g._neighbors_values['x2'] = 1
        g._neighbors_values['x3'] = 2
        c = g.__constraints__[0]
        self.assertEqual(g._is_violated(c, 0), False)
        self.assertEqual(g._is_violated(c, 1), True)
        self.assertEqual(g._is_violated(c, 2), True)

    def NM_violation_unary(self):
        g = GdbaComputation(self.x1, [self.phi], comp_def=MagicMock())
        g._neighbors_values['x2'] = 1
        g._neighbors_values['x3'] = 2
        g._violation_mode = 'NM'
        c = g.__constraints__[0]
        self.assertEqual(g._is_violated(c, 0), False)
        self.assertEqual(g._is_violated(c, 1), True)
        self.assertEqual(g._is_violated(c, 2), True)

    def NM_violation_n_ary(self):
        g = GdbaComputation(self.x1, [self.phi_n_ary], comp_def=MagicMock())
        g._neighbors_values['x2'] = 1
        g._neighbors_values['x3'] = 2
        g._violation_mode = 'NM'
        c = g.__constraints__[0]
        self.assertEqual(g._is_violated(c, 0), False)
        self.assertEqual(g._is_violated(c, 1), True)
        self.assertEqual(g._is_violated(c, 2), True)

    def MX_violation_unary(self):
        g = GdbaComputation(self.x1, [self.phi], comp_def=MagicMock())
        g._neighbors_values['x2'] = 1
        g._neighbors_values['x3'] = 2
        g._violation_mode = 'MX'
        c = g.__constraints__[0]
        self.assertEqual(g._is_violated(c, 0), False)
        self.assertEqual(g._is_violated(c, 1), False)
        self.assertEqual(g._is_violated(c, 2), True)

    def MX_violation_n_ary(self):
        g = GdbaComputation(self.x1, [self.phi_n_ary], comp_def=MagicMock())
        g._neighbors_values['x2'] = 1
        g._neighbors_values['x3'] = 2
        g._violation_mode = 'MX'
        c = g.__constraints__[0]
        self.assertEqual(g._is_violated(c, 0), False)
        self.assertEqual(g._is_violated(c, 1), True)
        self.assertEqual(g._is_violated(c, 2), False)
示例#21
0
def generate_small_world(args):
    logger.debug("generate small world problem %s ", args)

    # Erdős-Rényi graph aka binomial graph.
    graph = nx.barabasi_albert_graph(args.num, 2)

    # import matplotlib.pyplot as plt
    # plt.subplot(121)
    # nx.draw(graph)  # default spring_layout
    # plt.show()

    domain = Domain("d", "d", range(args.domain))
    variables = {}
    agents = {}
    for n in graph.nodes:
        v = Variable(var_name(n), domain)
        variables[v.name] = v
        logger.debug("Create var for node %s : %s", n, v)

    constraints = {}
    for i, (n1, n2) in enumerate(graph.edges):
        v1 = variables[var_name(n1)]
        v2 = variables[var_name(n2)]
        values = random_assignment_matrix([v1, v2], range(args.range))
        c = NAryMatrixRelation([v1, v2], values, name=c_name(n1, n2))
        logger.debug("Create constraints for edge (%s, %s) : %s", v1, v2, c)
        constraints[c.name] = c

    dcop = DCOP(
        "graph coloring",
        "min",
        domains={"d": domain},
        variables=variables,
        agents={},
        constraints=constraints,
    )
    graph_module = import_module("pydcop.computations_graph.factor_graph")
    cg = graph_module.build_computation_graph(dcop)
    algo_module = load_algorithm_module("maxsum")

    footprints = {n.name: algo_module.computation_memory(n) for n in cg.nodes}
    f_vals = footprints.values()
    logger.info(
        "%s computations, footprint: \n  sum: %s, avg: %s max: %s, "
        "min: %s",
        len(footprints),
        sum(f_vals),
        sum(f_vals) / len(footprints),
        max(f_vals),
        min(f_vals),
    )

    default_hosting_cost = 2000
    small_agents = [agt_name(i) for i in range(75)]
    small_capa, avg_capa, big_capa = 40, 200, 1000
    avg_agents = [agt_name(i) for i in range(75, 95)]
    big_agents = [agt_name(i) for i in range(95, 100)]
    hosting_factor = 10

    for a in small_agents:
        # communication costs with all other agents
        comm_costs = {other: 6 for other in small_agents if other != a}
        comm_costs.update({other: 8 for other in avg_agents})
        comm_costs.update({other: 10 for other in big_agents})
        # hosting cost for all computations
        hosting_costs = {}
        for n in cg.nodes:
            # hosting_costs[n.name] = hosting_factor * \
            #                         abs(small_capa -footprints[n.name])
            hosting_costs[n.name] = footprints[n.name] / small_capa

        agt = AgentDef(
            a,
            default_hosting_cost=default_hosting_cost,
            hosting_costs=hosting_costs,
            default_route=10,
            routes=comm_costs,
            capacity=small_capa,
        )
        agents[agt.name] = agt
        logger.debug("Create small agt : %s", agt)

    for a in avg_agents:
        # communication costs with all other agents
        comm_costs = {other: 8 for other in small_agents}
        comm_costs.update({other: 2 for other in avg_agents if other != a})
        comm_costs.update({other: 4 for other in big_agents})
        # hosting cost for all computations
        hosting_costs = {}
        for n in cg.nodes:
            # hosting_costs[n.name] = hosting_factor * \
            #                         abs(avg_capa - footprints[n.name])
            hosting_costs[n.name] = footprints[n.name] / avg_capa

        agt = AgentDef(
            a,
            default_hosting_cost=default_hosting_cost,
            hosting_costs=hosting_costs,
            default_route=10,
            routes=comm_costs,
            capacity=avg_capa,
        )
        agents[agt.name] = agt
        logger.debug("Create avg agt : %s", agt)

    for a in big_agents:
        # communication costs with all other agents
        comm_costs = {other: 10 for other in small_agents}
        comm_costs.update({other: 4 for other in avg_agents})
        comm_costs.update({other: 1 for other in big_agents if other != a})
        # hosting cost for all computations
        hosting_costs = {}
        for n in cg.nodes:
            hosting_costs[n.name] = footprints[n.name] / big_capa

        agt = AgentDef(
            a,
            default_hosting_cost=default_hosting_cost,
            hosting_costs=hosting_costs,
            default_route=10,
            routes=comm_costs,
            capacity=big_capa,
        )
        agents[agt.name] = agt
        logger.debug("Create big agt : %s", agt)

    dcop = DCOP(
        "graph coloring",
        "min",
        domains={"d": domain},
        variables=variables,
        agents=agents,
        constraints=constraints,
    )

    if args.output:
        outputfile = args.output[0]
        write_in_file(outputfile, dcop_yaml(dcop))
    else:
        print(dcop_yaml(dcop))
    def test_offer_already_has_partner(self):
        x1 = Variable("x1", list(range(2)))
        x2 = Variable("x2", list(range(2)))
        x3 = Variable("x3", list(range(2)))

        @AsNAryFunctionRelation(x1, x2, x3)
        def phi(x1_, x2_, x3_):
            return x1_ + x2_ + x3_

        # Receives a fake offer
        computation = Mgm2Computation(
            ComputationDef(
                VariableComputationNode(x1, [phi]),
                AlgorithmDef.build_with_default_param("mgm2"),
            ))
        computation.message_sender = DummySender()
        computation._state = "offer"
        computation._is_offerer = True
        computation.on_offer_msg("x2", Mgm2OfferMessage(), 1)
        self.assertEqual(computation._state, "offer")
        # self.assertEqual(computation._offers, [])
        # Received only fake offers
        computation2 = Mgm2Computation(
            ComputationDef(
                VariableComputationNode(x1, [phi]),
                AlgorithmDef.build_with_default_param("mgm2"),
            ))
        computation2.message_sender = DummySender()
        computation2._state = "offer"
        computation2.__nb_received_offers__ = 1
        computation2.on_offer_msg("x2", Mgm2OfferMessage(), 1)
        self.assertEqual(computation2._state, "gain")
        self.assertEqual(computation2._offers, [("x2", Mgm2OfferMessage())])
        # receives a real offer
        computation3 = Mgm2Computation(
            ComputationDef(
                VariableComputationNode(x1, [phi]),
                AlgorithmDef.build_with_default_param("mgm2"),
            ))
        computation3.message_sender = DummySender()
        computation3._state = "offer"
        computation3._is_offerer = True
        computation3.__cost__ = 15
        computation3.on_offer_msg(
            "x2", Mgm2OfferMessage({(1, 1): 8}, is_offering=True), 1)
        self.assertEqual(computation3._state, "offer")
        self.assertEqual(2, len(computation3._offers))
        self.assertEqual(computation3._potential_gain, 0)
        self.assertIsNone(computation3._potential_value)
        # Receives a real offer which is the last expected one
        computation4 = Mgm2Computation(
            ComputationDef(
                VariableComputationNode(x1, [phi]),
                AlgorithmDef.build_with_default_param("mgm2"),
            ))
        computation4.message_sender = DummySender()
        computation4._state = "offer"
        computation4._is_offerer = True
        computation4.__nb_received_offers__ = 1
        computation4.on_offer_msg(
            "x2", Mgm2OfferMessage({(1, 1): 8}, is_offering=True), 1)
        self.assertEqual(len(computation4), 3)
        self.assertEqual(computation4._state, "answer?")
        self.assertEqual(computation4._potential_gain, 0)
        self.assertIsNone(computation4._potential_value)
    def test_offer_has_no_partner_yet(self):
        x1 = Variable("x1", list(range(2)))
        x2 = Variable("x2", list(range(2)))
        x3 = Variable("x3", list(range(2)))

        @AsNAryFunctionRelation(x1, x2, x3)
        def phi(x1_, x2_, x3_):
            return x1_ + x2_ + x3_

        # Receives a fake offer
        computation = Mgm2Computation(
            ComputationDef(
                VariableComputationNode(x1, [phi]),
                AlgorithmDef.build_with_default_param("mgm2"),
            ))
        computation.message_sender = DummySender()
        computation._state = "offer"
        computation.on_offer_msg("x2", Mgm2OfferMessage(), 1)
        self.assertEqual(computation._state, "offer")
        self.assertEqual(computation._offers, [])
        # Received only fake offers
        computation2 = Mgm2Computation(
            ComputationDef(
                VariableComputationNode(x1, [phi]),
                AlgorithmDef.build_with_default_param("mgm2"),
            ))
        computation2.message_sender = DummySender()
        computation2._state = "offer"
        computation2.__nb_received_offers__ = 1
        computation2.on_offer_msg("x2", Mgm2OfferMessage(), 1)
        self.assertEqual(computation2._state, "gain")
        self.assertEqual(computation2._offers, [])

        # Receives a real offer (but still expects other OfferMessages)
        computation3 = Mgm2Computation(
            ComputationDef(
                VariableComputationNode(x1, [phi]),
                AlgorithmDef.build_with_default_param("mgm2"),
            ))
        computation3.message_sender = DummySender()
        computation3._state = "offer"
        computation3.on_offer_msg(
            "x2", Mgm2OfferMessage({(1, 1): 8}, is_offering=True), 1)
        self.assertEqual(computation3._state, "offer")
        self.assertEqual(computation3._offers, [("x2", {(1, 1): 8})])
        # Receives a real offer and is the last expected OfferMessage
        computation4 = Mgm2Computation(
            ComputationDef(
                VariableComputationNode(x1, [phi]),
                AlgorithmDef.build_with_default_param("mgm2"),
            ))
        computation4.message_sender = DummySender()
        computation4._state = "offer"
        computation4._neighbors_values = {"x2": 0, "x3": 1}
        computation4.__value__ = 0
        computation4.__cost__ = 1
        computation4.__nb_received_offers__ = 1
        computation4.on_offer_msg(
            "x2", Mgm2OfferMessage({(1, 1): 8}, is_offering=True), 1)
        self.assertEqual(computation4._offers, [("x2", {(1, 1): 8})])
        self.assertEqual(computation4._state, "gain")
        self.assertEqual(computation4._potential_gain, 9)
        self.assertEqual(computation4._potential_value, 1)
from pydcop.algorithms import amaxsum as ms
from pydcop.algorithms.amaxsum import communication_load, computation_memory
from pydcop.algorithms.maxsum import VARIABLE_UNIT_SIZE
from pydcop.computations_graph.factor_graph import ComputationsFactorGraph, \
    VariableComputationNode, FactorComputationNode, FactorGraphLink
from pydcop.dcop.objects import Variable, VariableDomain, AgentDef
from pydcop.dcop.relations import relation_from_str
from pydcop.distribution.ilp_fgdp import distribute, _build_alphaijk_binvars, \
    _objective_function, _computation_memory_in_cg
from pydcop.distribution.objects import ImpossibleDistributionException

Agent = namedtuple('Agent', ['name'])

d1 = VariableDomain('d1', '', [1, 2, 3, 5])
v1 = Variable('v1', d1)
v2 = Variable('v2', d1)
v3 = Variable('v3', d1)
v4 = Variable('v4', d1)
v5 = Variable('v5', d1)

a1 = AgentDef('a1', capacity=200)
a2 = AgentDef('a2', capacity=200)


def is_all_hosted(cg, dist):
    if len(cg.nodes) == len(dist.computations):
        for c in cg.nodes:
            if c.name not in dist.computations:
                return False
        return True
示例#25
0
def generate_graph_coloring(args):
    logger.debug('generate_graph_coloring %s ', args)
    node_count = args.node_count
    density = args.density
    color_count = args.color_count
    auto_agents = args.agents is None
    agents_count = node_count if auto_agents else args.agents
    capacity = args.capacity
    logger.info(
        'Generating random graph coloring with %s variables, '
        '%s colors, target density %s and %s agents', node_count, color_count,
        args.density, agents_count)

    # First a random graph
    is_connected = False
    if not args.allow_subgraph:
        while not is_connected:
            graph = nx.gnp_random_graph(args.node_count, density)
            is_connected = nx.is_connected(graph)
    else:
        graph = nx.gnp_random_graph(args.node_count, density)
        is_connected = nx.is_connected(graph)

    real_density = nx.density(graph)
    logger.info(nx.info(graph))
    logger.info('Connected : %s', nx.is_connected(graph))
    logger.info('Density %s', nx.density(graph))

    # Now create a DCOP from the graph
    d = VariableDomain('colors', 'color', range(color_count))
    variables = {}
    agents = {}
    for i, node in enumerate(graph.nodes_iter()):
        logger.debug('node %s - %s', node, i)
        name = 'v' + str(i)
        variables[name] = Variable(name, d)
        if auto_agents:
            a_name = 'a' + str(i)
            agents[a_name] = AgentDef(a_name, capacity)

    if not auto_agents:
        for i in range(agents_count):
            a_name = 'a' + str(i)
            agents[a_name] = AgentDef(a_name, capacity)

    constraints = {}
    for i, edge in enumerate(graph.edges_iter()):
        logger.debug('edge %s - %s', edge, i)
        name = 'c' + str(i)
        u, v = edge
        expression = '1000 if v{} == v{} else 0'.format(u, v)
        constraints[name] = relation_from_str(name, expression,
                                              variables.values())
        logger.debug(repr(constraints[name]))

    dcop = DCOP('graph coloring',
                'min',
                domains={'colors': d},
                variables=variables,
                agents=agents,
                constraints=constraints)

    if args.output:
        outputfile = args.output[0]
        if args.correct_density:
            outputfile = correct_density(outputfile, real_density)
        write_in_file(outputfile, dcop_yaml(dcop))
    else:
        print(dcop_yaml(dcop))
def dpop_nonbinaryrelation():

    x0 = Variable('x0', list(range(10)))
    x1 = Variable('x1', list(range(10)))
    x2 = Variable('x2', list(range(10)))

    @relations.AsNAryFunctionRelation(x0)
    def x0_prefs(x):
        if x > 5:
            return 0
        return 10

    @relations.AsNAryFunctionRelation(x1)
    def x1_prefs(x):
        if 2 < x < 7:
            return 0
        return 10

    @relations.AsNAryFunctionRelation(x2)
    def x2_prefs(x):
        if x < 5:
            return 0
        return 10

    @relations.AsNAryFunctionRelation(x0, x1, x2)
    def three_ary_relation(x, y, z):
        return abs(10 - (x+y+z))

    comm = InProcessCommunicationLayer()

    al0 = DpopAlgo(x0, mode='min')
    al1 = DpopAlgo(x1, mode='min')
    al2 = DpopAlgo(x2, mode='min')

    # unary relation for preferences
    al0.add_relation(x0_prefs)
    al1.add_relation(x1_prefs)
    al2.add_relation(x2_prefs)

    al0.add_child(x1)
    al1.add_child(x2)
    al1.set_parent(x0)
    al2.set_parent(x1)
    al2.add_relation(three_ary_relation)

    a0 = Agent('a0', comm)
    a1 = Agent('a1', comm)
    a2 = Agent('a2', comm)

    a0.add_computation(al0)
    a1.add_computation(al1)
    a2.add_computation(al2)

    results, _, _ = synchronous_single_run([a0, a1, a2])

    if results == {'x0': 6, 'x1': 3, 'x2': 1} or \
       results == {'x0': 7, 'x1': 3, 'x2': 0}:
        logging.info('SUCCESS !! ' + str(results))
        return 0
    else:
        logging.info('invalid result found, needs some debugging ...' + str(
            results))
        return 1
def maxsum_smartlights_multiplecomputationagent_costvariable():
    """

    This sample use variable with integrated cost.


     * 3 lights l1, l2 & l3
       each light can have a luminosity level in the  0-9 range
       The energy cost is a linear function of the luminosity level, with l1
       more efficient than l2 and l3

     * one scene action y1, the room luminosity level
       y1 = (l1 + l2 + l3 )/3
       y1 domain is also between 0-9

     * one rule :
       l3 must be off AND y1 Must be 5


    """
    # building the Factor Graph
    # Lights :

    l1 = VariableWithCostFunc('l1', list(range(10)), lambda x: x * 0.5)
    l2 = VariableWithCostFunc('l2', list(range(10)), lambda x: x)
    l3 = VariableWithCostFunc('l3', list(range(10)), lambda x: x)

    # Scene action
    y1 = Variable('y1', list(range(10)))

    @relations.AsNAryFunctionRelation(l1, l2, l3, y1)
    def scene_rel(l1, l2, l3, y1):
        if y1 == round(l1 / 3.0 + l2 / 3.0 + l3 / 3.0):
            return 0
        return INFNT

    # Rule
    @relations.AsNAryFunctionRelation(l3, y1)
    def rule_rel(l3, y1):
        """
        This rule means : target luminosity if 5, and x3 is off.

        :param x3:
        :param y1:
        :return:
        """
        return 10 * (abs(y1 - 5) + l3)

    # Create computation for factors and variables
    # Light 1
    algo_l1 = amaxsum.VariableAlgo(l1, [scene_rel.name])

    # Light 2
    algo_l2 = amaxsum.VariableAlgo(l2, [scene_rel.name])

    # Light 3
    algo_l3 = amaxsum.VariableAlgo(l3, [scene_rel.name, rule_rel.name])

    # Scene
    algo_y1 = amaxsum.VariableAlgo(y1, [rule_rel.name, scene_rel.name])
    algo_scene = amaxsum.FactorAlgo(scene_rel)

    # Rule
    algo_rule = amaxsum.FactorAlgo(rule_rel)

    # Distribution of the computation on the three physical light-bulb nodes.
    # We have 9 computations to distribute on 3 agents, mapping the 3 light
    # bulbs.
    comm = infrastructure.communication.InProcessCommunicationLayer()

    a1 = infrastructure.Agent('Bulb1', comm)
    a1.add_computation(algo_l1)
    a1.add_computation(algo_scene)
    a1.add_computation(algo_y1)

    a2 = infrastructure.Agent('Bulb2', comm)
    a2.add_computation(algo_l2)

    a3 = infrastructure.Agent('Bulb3', comm)
    a3.add_computation(algo_l3)
    a3.add_computation(algo_rule)

    dcop_agents = [a1, a2, a3]

    results, _, _ = infrastructure.synchronous_single_run(dcop_agents, 5)

    print(results)

    if results == {'l1': 9, 'y1': 5, 'l3': 0, 'l2': 5}:
        logging.info('SUCCESS !! ')
        return 0
    else:
        logging.info('invalid result found, needs some debugging ...' +
                     str(results))
        return 1
示例#28
0
    def test_1_unary_constraint_means_no_neighbors(self):
        variable = Variable('a', [0, 1, 2, 3, 4])
        c1 = UnaryFunctionRelation('c1', variable, lambda x: abs(x - 2))

        computation = DsaComputation(variable, [c1], comp_def=MagicMock())
        self.assertEqual(len(computation._neighbors), 0)
示例#29
0
文件: maxsum.py 项目: khoihd/pyDcop
def costs_for_factor(variable: Variable, factor: FactorName,
                     factors: List[Constraint],
                     costs: Dict) -> Dict[VarVal, Cost]:
    """
    Produce the message that must be sent to factor f.

    The content if this message is a d -> cost table, where
    * d is a value from the domain
    * cost is the sum of the costs received from all other factors except f
    for this value d for the domain.

    Parameters
    ----------
    variable: Variable
        the variable sending the message
    factor: str
        the name of the factor the message will be sent to
    factors: list of Constraints
        the constraints this variables depends on
    costs: dict
        the accumulated costs received by the variable from all factors

    Returns
    -------
    Dict:
        a dict containing a cost for each value in the domain of the variable
    """
    # If our variable has integrated costs, add them
    msg_costs = {d: variable.cost_for_val(d) for d in variable.domain}

    sum_cost = 0
    for d in variable.domain:
        for f in [f for f in factors if f != factor and f in costs]:
            f_costs = costs[f]
            if d not in f_costs:
                msg_costs[d] = INFINITY
                break
            c = f_costs[d]
            sum_cost += c
            msg_costs[d] += c

    # Experimentally, when we do not normalize costs the algorithm takes
    # more cycles to stabilize
    # return {d: c for d, c in msg_costs.items() if c != INFINITY}

    # Normalize costs with the average cost, to avoid exploding costs
    avg_cost = sum_cost / len(msg_costs)
    normalized_msg_costs = {
        d: c - avg_cost
        for d, c in msg_costs.items() if c != INFINITY
    }
    msg_costs = normalized_msg_costs

    # FIXME: restore damping support
    # prev_costs, count = self._prev_messages[factor]
    # damped_costs = {}
    # if prev_costs is not None:
    #     for d, c in msg_costs.items():
    #         damped_costs[d] = self.damping * prev_costs[d] + (1 - self.damping) * c
    #     self.logger.warning("damping : replace %s with %s", msg_costs, damped_costs)
    #     msg_costs = damped_costs

    return msg_costs
    def test_gain_all_received(self):
        x1 = Variable("x1", list(range(3)))
        x2 = Variable("x2", list(range(2)))
        x3 = Variable("x3", list(range(2)))

        @AsNAryFunctionRelation(x1, x2)
        def phi(x1_, x2_):
            if x1_ == x2_:
                return 1
            return 0

        @AsNAryFunctionRelation(x1, x3)
        def psi(x1_, x3_):
            if x1_ == x3_:
                return 8
            return 0

        computation = Mgm2Computation(
            ComputationDef(
                VariableComputationNode(x1, [phi, psi]),
                AlgorithmDef.build_with_default_param("mgm2"),
            ))
        computation.message_sender = DummySender()

        computation._neighbors_values = {"x2": 1, "x3": 0}

        # If potential gain is 0
        computation.__value__ = 1
        computation.__cost__ = 1
        computation._potential_value = 0
        computation._potential_gain = 0
        computation._state = "gain"
        computation._neighbors_gains["x3"] = 2
        computation.on_gain_msg("x2", Mgm2GainMessage(5), 1)
        self.assertEqual(computation.current_value, 1)
        self.assertEqual(computation._state, "value")
        self.assertEqual(computation.current_cost, 1)
        # If commited and has best gain
        computation._state = "gain"
        computation.__value__ = 1
        computation.__cost__ = 1
        computation._committed = True
        computation._partner = x3
        computation._potential_gain = 10
        computation._neighbors_gains["x3"] = 10
        computation._potential_value = 0
        computation.on_gain_msg("x2", Mgm2GainMessage(5), 1)
        self.assertEqual(computation.current_value, 1)
        self.assertEqual(computation.current_cost, 1)
        self.assertTrue(computation._can_move)
        self.assertEqual(computation._state, "go?")
        # If commited and has not best gain
        computation._state = "gain"
        computation.__value__ = 1
        computation.__cost__ = 1
        computation._committed = True
        computation._partner = x3
        computation._potential_gain = 1
        computation._neighbors_gains["x3"] = 1
        computation._potential_value = 0
        computation.on_gain_msg("x2", Mgm2GainMessage(5), 1)
        self.assertEqual(computation.current_value, 1)
        self.assertEqual(computation.current_cost, 1)
        self.assertFalse(computation._can_move)
        self.assertEqual(computation._state, "go?")
        self.test_clear_agent()

        # If not committed and has best gain not alone: no test as it could (in
        # the future) be randomly chosen

        # If not committed and not best gain
        computation._state = "gain"
        computation.__value__ = 1
        computation.__cost__ = 1
        computation._committed = False
        computation._partner = None
        computation._potential_gain = 2
        computation._neighbors_gains["x3"] = 2
        computation._potential_value = 0
        computation.on_gain_msg("x2", Mgm2GainMessage(5), 1)
        self.assertEqual(computation.current_value, 1)
        self.assertEqual(computation.current_cost, 1)
        self.assertEqual(computation._state, "value")
        self.test_clear_agent()