def test_4nodes(self):
        # Graph with 4 nodes, one cycle
        #
        #       x1---X3
        #        \  /
        #         x2---x4

        domain = ['a', 'b', 'c']
        x1 = Variable('x1', domain)
        x2 = Variable('x2', domain)
        x3 = Variable('x3', domain)
        x4 = Variable('x4', domain)
        variables = [x1, x2, x3, x4]

        def binary_func(x, y):
            return x + y

        r1 = NAryFunctionRelation(binary_func, [x1, x2], name='r1')
        r2 = NAryFunctionRelation(binary_func, [x1, x3], name='r2')
        r3 = NAryFunctionRelation(binary_func, [x2, x3], name='r3')
        r4 = NAryFunctionRelation(binary_func, [x2, x4], name='r4')
        relations = [r1, r2, r3, r4]

        root = _generate_dfs_tree(variables, relations, root=x1)
        _filter_relation_to_lowest_node(root)

        check_tree(root)

        self.assertEqual(root.variable, x1)
        self.assertEqual(root.parent, None)
        self.assertEqual(len(root.children), 1)
        self.assertEqual(len(root.relations), 0)
    def test_3nodes_tree_cycle(self):
        domain = ['a', 'b', 'c']
        x1 = Variable('x1', domain)
        x2 = Variable('x2', domain)
        x3 = Variable('x3', domain)
        variables = [x1, x2, x3]

        r1 = NAryFunctionRelation(lambda x, y: x + y, [x1, x2], name='r1')
        r2 = NAryFunctionRelation(lambda x, y: x + y, [x1, x3], name='r2')
        r3 = NAryFunctionRelation(lambda x, y: x + y, [x2, x3], name='r3')
        relations = [r1, r2, r3]

        dcop = DCOP('test', 'min')
        dcop.add_constraint(r1)
        dcop.add_constraint(r2)
        dcop.add_constraint(r3)

        cg = build_computation_graph(dcop)
        self.assertEqual(len(cg.nodes), len(variables))

        self.assertEqual(len(cg.roots), 1)
        # All variables have the same number of neighbors, they could all be
        #  root
        self.assertIn(cg.roots[0].variable, [x1, x2, x3])
        self.assertEqual(cg.roots[0].parent, None)

        root = _generate_dfs_tree(variables, relations, root=x1)
        self.assertEqual(root.variable, x1)
        self.assertEqual(root.parent, None)
    def test_3nodes_tree_cycle(self):
        domain = ['a', 'b', 'c']
        x1 = Variable('x1', domain)
        x2 = Variable('x2', domain)
        x3 = Variable('x3', domain)
        variables = [x1, x2, x3]

        r1 = NAryFunctionRelation(lambda x, y: x + y, [x1, x2], name='r1')
        r2 = NAryFunctionRelation(lambda x, y: x + y, [x1, x3], name='r2')
        r3 = NAryFunctionRelation(lambda x, y: x + y, [x2, x3], name='r3')
        relations = [r1, r2, r3]

        root = _generate_dfs_tree(variables, relations, root=x1)

        self.assertEqual(root.variable, x1)
        self.assertEqual(root.parent, None)
        self.assertEqual(len(root.children), 1)
        c1 = root.children[0]
        self.assertIn(c1.variable, [x2, x3])
        self.assertEqual(len(root.pseudo_children), 1)
        self.assertEqual(root.pseudo_parents, [])

        self.assertEqual(c1.parent, root)
        self.assertEqual(len(c1.children), 1)
        c2 = c1.children[0]
        self.assertEqual(c2.children, [])
        self.assertEqual(c2.pseudo_children, [])
        self.assertEqual(c2.pseudo_parents, [root])

        check_tree(root)
Esempio n. 4
0
    def test_diameter_simple(self):
        l1 = Variable('l1', [])
        l2 = Variable('l2', [])
        l3 = Variable('l3', [])
        r1 = NAryFunctionRelation(lambda x, y: 0, [l1, l2], name='r1')
        r2 = NAryFunctionRelation(lambda x, y: 0, [l2, l3], name='r2')

        d = graph_diameter([l1, l2, l3], [r1, r2])
        self.assertEqual(len(d), 1)
        self.assertEqual(d[0], 2)
Esempio n. 5
0
    def test_diameter_simple2(self):
        l1 = Variable('l1', [])
        l2 = Variable('l2', [])
        l3 = Variable('l3', [])
        r1 = NAryFunctionRelation(lambda x, y: 0, [l1, l2], name='r1')
        r2 = NAryFunctionRelation(lambda x, y: 0, [l2, l3], name='r2')
        r3 = NAryFunctionRelation(lambda x, y: 0, [l1, l3], name='r3')

        d = graph_diameter([l1, l2, l3], [r1, r2, r3])
        self.assertListEqual(d, [1])
Esempio n. 6
0
    def test_count_cycle_none(self):
        domain = list(range(10))

        l1 = Variable('l1', domain)
        l2 = Variable('l2', domain)
        l3 = Variable('l3', domain)
        r1 = NAryFunctionRelation(lambda x, y: 0, [l1, l2], name='r1')
        r2 = NAryFunctionRelation(lambda x, y: 0, [l2, l3], name='r2')

        n = cycles_count([l1, l2, l3], [r1, r2])

        self.assertEqual(n, 0)
Esempio n. 7
0
    def test_convert_graph_simple(self):
        domain = list(range(10))
        l1 = Variable('l1', domain)
        l2 = Variable('l2', domain)
        l3 = Variable('l3', domain)
        r1 = NAryFunctionRelation(lambda x, y: 0, [l1, l2], name='r1')
        r2 = NAryFunctionRelation(lambda x, y: 0, [l2, l3], name='r2')
        r3 = NAryFunctionRelation(lambda x, y: 0, [l1, l3], name='r3')

        graph = as_networkx_graph([l1, l2, l3], [r1, r2, r3])

        print(graph.edges())
        print(graph.nodes())

        self.assertEqual(len(graph.nodes()), 3)
        self.assertEqual(len(graph.edges()), 3)
Esempio n. 8
0
def create_computation_hosted_constraint(computation_name: str,
                                         bin_vars: Dict[Tuple, BinaryVariable])\
        -> Constraint:
    """
    Create a constraints that the computation names `computation_name` is
    hosted exactly once on a set of n candidate agents.
    The constraints is an hard constraint that return an 'high enough' (10
    000) value when it is not satisfied.

    :param computation_name: the name of the computation
    :param bin_vars:  a dictionary { (comp_name, agt_name) -> BinaryVariable }
     containing the n binary variables x_i^m, one for each of the n
    candidate agents a_m the computation x_i could be hosted on

    :return: a constraint object
    """
    def hosted(**kwargs):
        # kwargs will be a map {bin_var_name -> value} giving the binary
        # value for each of the n candidate agent the computation could be
        # hosted on
        s = sum([v for v in kwargs.values()])
        return 0 if s == 1 else 10000

    constraint = NAryFunctionRelation(
        hosted,
        list(bin_vars.values()),
        name='{}_hosted'.format(computation_name))

    return constraint
    def test_2nodes_tree(self):
        domain = ['a', 'b', 'c']
        x1 = Variable('x1', domain)
        x2 = Variable('x2', domain)
        variables = [x1, x2]

        r1 = NAryFunctionRelation(lambda x, y: x + y, [x1, x2], name='r1')
        relations = [r1]

        root = _generate_dfs_tree(variables, relations, root=x1)

        self.assertEqual(root.variable, x1)
        self.assertEqual(root.parent, None)
        self.assertEqual(len(root.children), 1)
        self.assertEqual(len(root.relations), 1)
        node = root.children[0]
        self.assertEqual(node.variable, x2)
        self.assertEqual(root.pseudo_children, [])
        self.assertEqual(root.pseudo_parents, [])

        self.assertEqual(node.parent, root)
        self.assertEqual(node.children, [])
        self.assertEqual(node.pseudo_children, [])
        self.assertEqual(node.pseudo_parents, [])
        self.assertEqual(node.relations, [r1])

        check_tree(root)
    def test_root(self):
        domain = ['a', 'b', 'c']
        x1 = Variable('x1', domain)
        x2 = Variable('x2', domain)
        x3 = Variable('x3', domain)
        variables = [x1, x2, x3]

        r1 = NAryFunctionRelation(lambda x, y: x + y, [x1, x2], name='r1')
        r2 = NAryFunctionRelation(lambda x, y: x - y, [x2, x3], name='r2')
        relations = [r1, r2]

        tree_root = _generate_dfs_tree(variables, relations, x1)
        self.assertEqual(tree_root.variable, x1)

        tree_root = _generate_dfs_tree(variables, relations)
        self.assertIn(tree_root.variable, variables)
    def test_3nodes_tree_cycle_3ary_rel_bottom(self):
        # A graph with 3 variables and a single 3-ary relation.

        domain = ['a', 'b', 'c']
        x1 = Variable('x1', domain)
        x2 = Variable('x2', domain)
        x3 = Variable('x3', domain)
        variables = [x1, x2, x3]
        r1 = NAryFunctionRelation(lambda x, y, z: x + y + z, [x1, x2, x3],
                                  name='r1')
        relations = [r1]

        root = _generate_dfs_tree(variables, relations, root=x1)
        _filter_relation_to_lowest_node(root)

        self.assertEqual(root.variable, x1)
        self.assertEqual(root.parent, None)
        self.assertEqual(len(root.children), 1)
        self.assertEqual(len(root.relations), 0)
        c1 = root.children[0]
        self.assertIn(c1.variable, [x2, x3])
        self.assertEqual(len(root.pseudo_children), 1)
        self.assertEqual(root.pseudo_parents, [])

        self.assertEqual(c1.parent, root)
        self.assertEqual(len(c1.children), 1)
        c2 = c1.children[0]
        self.assertEqual(c2.children, [])
        self.assertEqual(c2.pseudo_children, [])
        self.assertEqual(c2.pseudo_parents, [root])

        self.assertEqual(len(c1.relations) + len(c2.relations), 1)

        check_tree(root)
Esempio n. 12
0
    def test_diameter_simple3(self):
        l1 = Variable('l1', [])
        l2 = Variable('l2', [])
        l3 = Variable('l3', [])
        r1 = NAryFunctionRelation(lambda x, y: 0, [l1, l2], name='r1')

        g = as_networkx_graph([l1, l2, l3], [r1])

        d = graph_diameter([l1, l2, l3], [r1])
        self.assertListEqual(sorted(d), [0, 1])
Esempio n. 13
0
    def test_count_cycle_clique(self):
        domain = list(range(10))

        l1 = Variable('l1', domain)
        l2 = Variable('l2', domain)
        l3 = Variable('l3', domain)
        l4 = Variable('l4', domain)
        r1 = NAryFunctionRelation(lambda x, y, z, w: 0, [l1, l2, l3, l4],
                                  name='r1')

        n = cycles_count([l1, l2, l3, l4], [r1])

        self.assertEqual(n, 3)
    def test_visit_tree(self):
        domain = ['a', 'b', 'c']
        x1 = Variable('x1', domain)
        x2 = Variable('x2', domain)
        x3 = Variable('x3', domain)
        x4 = Variable('x4', domain)
        variables = [x1, x2, x3, x4]

        binary_func = lambda x, y: x + y
        r1 = NAryFunctionRelation(binary_func, [x1, x2], name='r1')
        r2 = NAryFunctionRelation(binary_func, [x1, x3], name='r2')
        r3 = NAryFunctionRelation(binary_func, [x2, x3], name='r3')
        r4 = NAryFunctionRelation(binary_func, [x2, x4], name='r4')
        relations = [r1, r2, r3, r4]

        root = _generate_dfs_tree(variables, relations, root=x1)

        for node in _visit_tree(root):
            self.assertIn(node.variable, variables)
            variables.remove(node.variable)

        self.assertEqual(len(variables), 0)
Esempio n. 15
0
    def test_convert_graph(self):
        domain = list(range(10))
        l1 = Variable('l1', domain)
        l2 = Variable('l2', domain)
        l3 = Variable('l3', domain)
        l4 = Variable('l4', domain)

        # 4-ary relation : iot defines a clique with l1, l2, l3, l4
        r1 = NAryFunctionRelation(lambda x, y: 0, [l1, l2, l3, l4], name='r1')

        graph = as_networkx_graph([l1, l2, l3], [r1])

        print(graph.edges())
        print(graph.nodes())

        self.assertEqual(len(graph.nodes()), 4)
        self.assertEqual(len(graph.edges()), 6)
Esempio n. 16
0
def create_agent_capacity_constraint(agt_name: str, remaining_capacity,
                                     footprint_func: Callable[[str], float],
                                     bin_vars: Dict[Tuple, BinaryVariable]) \
        -> Constraint:
    """
    Create a constraints that ensure that an agent a_m does not exceeds its
    capacity when hosting some candidates computations x_i \in X_c.

    The constraints is an hard constraint that return an 'high enough'
    (10 000) value when it is not satisfied.

    Parameters
    ----------
    agt_name: str
        the name of the agent a_m for which we are creating the constraint
    remaining_capacity
        the remaining capacity of a_m before hosting any candidate computation
        from X_c.
    footprint_func: function
        a function that gives the footprint for a computation, given its name.
    bin_vars: a dictionary { (comp_name, agt_name) -> BinaryVariable }
        containing the k binary variable x_i^m, one for each of the k
        candidate computations x_i that could be hosted on the agent a_m.

    Returns
    -------
    a Constraint object
    """
    # Reversed lookup table: which (agt, computation) a variable is about:
    var_lookup = {v.name: k for k, v in bin_vars.items()}

    def capacity(**kwargs):
        # kwargs will be a name {v_name : value}
        # where v_name is x_{}_{}
        orphaned_footprint = 0
        for v_name in kwargs:
            comp, _ = var_lookup[v_name]
            orphaned_footprint += kwargs[v_name] * footprint_func(comp)

        repair_capa = remaining_capacity - orphaned_footprint
        return 0 if repair_capa >= 0 else 10000

    constraint = NAryFunctionRelation(capacity,
                                      list(bin_vars.values()),
                                      name=agt_name + '_capacity')
    return constraint
    def test_find_neighbors_relations(self):
        domain = ['a', 'b', 'c']

        n1 = _BuildingNode(Variable('x1', domain))
        n2 = _BuildingNode(Variable('x2', domain))
        n3 = _BuildingNode(Variable('x3', domain))
        nodes = [n1, n2, n3]

        r1 = NAryFunctionRelation(lambda x, y: x + y,
                                  [n1.variable, n2.variable],
                                  name='r1')
        relations = [r1]

        self.assertEqual(_find_neighbors_relations(n1, relations, nodes),
                         ([n2], [r1]))
        self.assertEqual(_find_neighbors_relations(n2, relations, nodes),
                         ([n1], [r1]))
        self.assertEqual(_find_neighbors_relations(n3, relations, nodes),
                         ([], []))
Esempio n. 18
0
    def test_3var_1rel(self):
        domain = list(range(10))
        l1 = Variable('l1', domain)
        l2 = Variable('l2', domain)
        l3 = Variable('l3', domain)
        rel = NAryFunctionRelation(lambda x, y, z: 0, [l1, l2, l3], name='rel')

        nodes = as_bipartite_graph([l1, l2, l3], [rel])

        self.assertEqual(len(nodes), 4)
        var_nodes = [n for n in nodes if n.type == 'VARIABLE']
        rel_nodes = [n for n in nodes if n.type == 'CONSTRAINT']

        self.assertEqual(len(var_nodes), 3)
        self.assertEqual(len(rel_nodes), 1)

        self.assertEqual(len(rel_nodes[0].neighbors), 3)
        self.assertIn(var_nodes[0], rel_nodes[0].neighbors)
        self.assertIn(var_nodes[1], rel_nodes[0].neighbors)
        self.assertIn(var_nodes[2], rel_nodes[0].neighbors)
Esempio n. 19
0
def create_agent_hosting_constraint(agt_name: str,
                                    hosting_func: Callable[[str], float],
                                    bin_vars: Dict[Tuple, BinaryVariable]) \
        -> Constraint:
    """
    Create a constraints that returns the hosting costs for agent a_m
    `agt_name` when hosting some candidates computations x_i \in X_c.

    The constraints is an soft constraint that should be minimized.

    Parameters
    ----------
    agt_name: str
        the name of the agent a_m.
    hosting_func: Callable
        a function that gives the hosting costs for a computation x_i on a_m,
        given the name of x_i.
    bin_vars: dictionary { (comp_name, agt_name) -> BinaryVariable }
        containing the k binary variable x_i^m, one for each of the k
        candidate computations x_i that could be hosted on the agent a_m.

    Returns
    -------
    a Constraint object
    """

    # Reversed lookup table: which (agt, computation) a variable is about:
    var_lookup = {v.name: k for k, v in bin_vars.items()}

    def hosting_cost(**kwargs):
        cost = 0
        for v_name in kwargs:
            comp, _ = var_lookup[v_name]
            cost += kwargs[v_name] * hosting_func(comp)
        return cost

    constraint = NAryFunctionRelation(hosting_cost,
                                      list(bin_vars.values()),
                                      name=agt_name + '_hosting')
    return constraint
def dmaxsum_external_variable():

    domain = VariableDomain('colors', 'color', ['R', 'G', 'B'])
    # RW Variables
    v1 = VariableNoisyCostFunc('v1', domain, prefer_color('R'))
    v2 = VariableNoisyCostFunc('v2', domain, prefer_color('B'))
    v3 = VariableNoisyCostFunc('v3', domain, prefer_color('B'))
    v4 = VariableNoisyCostFunc('v4', domain, prefer_color('R'))

    # External Variable
    domain_e = VariableDomain('boolean', 'abstract', [False, True])
    e1 = ExternalVariable('e1', domain_e, False)

    def r1(v1_, v2_, v3_):
        if v1_ != v2_ and v2_ != v3_ and v1_ != v3_:
            return 0
        return 100

    condition = NAryFunctionRelation(lambda x: x, [e1], name='r1_cond')
    relation_if_true = NAryFunctionRelation(r1, [v1, v2, v3], name='r1')
    r1 = ConditionalRelation(condition, relation_if_true)

    def r2(v2_, v4_):
        if v2_ != v4_:
            return 0
        return 100

    r2 = NAryFunctionRelation(r2, [v2, v4], name='r2')

    def r3(v3_, v4_):
        if v3_ != v4_:
            return 0
        return 100

    r3 = NAryFunctionRelation(r3, [v3, v4], name='r3')

    r1_computation = DynamicFactorComputation(r1, name='r1')
    r2_computation = DynamicFactorComputation(r2)
    r3_computation = DynamicFactorComputation(r3)

    e1_computation = ExternalVariableComputation(e1)

    # MUST only consider current relation when building computation objects !!
    # When a relation uses external variable, these must be sliced out.
    current_r1 = r1.slice({e1.name: e1.value})
    relations = [current_r1, r2, r3]
    v1_computation = \
        DynamicFactorVariableComputation(
            v1,
            [r.name for r in find_dependent_relations(v1, relations)])
    v2_computation = \
        DynamicFactorVariableComputation(
            v2,
            [r.name for r in find_dependent_relations(v2, relations)])
    v3_computation = \
        DynamicFactorVariableComputation(
            v3,
            [r.name for r in find_dependent_relations(v3, relations)])
    v4_computation = \
        DynamicFactorVariableComputation(
            v4,
            [r.name for r in find_dependent_relations(v4, relations)])

    # Prepare the agents
    comm = InProcessCommunicationLayer()
    a1 = Agent('a1', comm)
    a1.add_computation(v1_computation)
    a1.add_computation(r1_computation)

    a2 = Agent('a2', comm)
    a2.add_computation(v2_computation)
    a1.add_computation(r2_computation)

    a3 = Agent('a3', comm)
    a3.add_computation(v3_computation)
    a3.add_computation(v4_computation)
    a3.add_computation(r3_computation)

    a4 = Agent('a4', comm)
    a4.add_computation(e1_computation)

    agents = [a1, a2, a3, a4]
    runner = AgentsRunner(agents)
    runner.run_agents()

    # Now change a factor function every two seconds
    fail = False
    for i in range(5):
        time.sleep(2)
        current_value = e1_computation.current_value
        print('###  Iteration {} - function {}'.format(i, current_value))
        print(runner.status_string())
        results = runner.variable_values()
        if current_value:
            c = r1(filter_assignment_dict(results, r1.dimensions)) + \
                r2(filter_assignment_dict(results, r2.dimensions)) + \
                r3(filter_assignment_dict(results, r3.dimensions))
            if c != 0:
                print('Error on results for {} : \nGot {}  !'.format(
                    current_value, results))
                fail = True
                break
        else:
            c = r2(filter_assignment_dict(results, r2.dimensions)) + \
                r3(filter_assignment_dict(results, r3.dimensions))
        if c != 0:
            print('Error on results for {} : \nGot {} !'.format(
                current_value, results))
            fail = True
            break

        new_val = not current_value
        print('## Changing e1 value to {}'.format(new_val))
        e1_computation.change_value(new_val)

    print('Finished, stopping agents')
    runner.request_stop_agents(wait_for_stop=True)

    if fail:
        print('Failed !')
        return 1
    else:
        print('Success !')
        return 0
def dmaxsum_graphcoloring():

    v1 = VariableNoisyCostFunc('v1', d, prefer_color('R'))
    v2 = VariableNoisyCostFunc('v2', d, prefer_color('G'))
    v3 = VariableNoisyCostFunc('v3', d, prefer_color('B'))
    v4 = VariableNoisyCostFunc('v4', d, prefer_color('R'))

    def r1(v1_, v2_, v3_):
        if v1_ != v2_ and v2_ != v3_ and v1_ != v3_:
            return 0
        return 100

    r1 = NAryFunctionRelation(r1, [v1, v2, v3], name='r1')

    def r1_2(v1_, v2_, v4_):
        if v1_ != v2_ and v2_ != v4_ and v1_ != v4_:
            return 0
        return 100

    r1_2 = NAryFunctionRelation(r1_2, [v1, v2, v4], name='r1_2')

    def r2(v2_, v4_):
        if v2_ != v4_:
            return 0
        return 100

    r2 = NAryFunctionRelation(r2, [v2, v4], name='r2')

    def r3(v3_, v4_):
        if v3_ != v4_:
            return 0
        return 100

    r3 = NAryFunctionRelation(r3, [v3, v4], name='r3')

    relations = [r1, r2, r3]
    r1_computation = DynamicFactorComputation(r1, name='r1')
    r2_computation = DynamicFactorComputation(r2)
    r3_computation = DynamicFactorComputation(r3)

    v1_computation = \
        DynamicFactorVariableComputation(
            v1,
            [r.name for r in find_dependent_relations(v1, relations)])
    v2_computation = \
        DynamicFactorVariableComputation(
            v2,
            [r.name for r in find_dependent_relations(v2, relations)])
    v3_computation = \
        DynamicFactorVariableComputation(
            v3,
            [r.name for r in find_dependent_relations(v3, relations)])
    v4_computation = \
        DynamicFactorVariableComputation(
            v4,
            [r.name for r in find_dependent_relations(v4, relations)])

    # Prepare the agents
    comm = InProcessCommunicationLayer()
    a1 = Agent('a1', comm)
    a1.add_computation(v1_computation)
    a1.add_computation(r1_computation)

    a2 = Agent('a2', comm)
    a2.add_computation(v2_computation)
    a1.add_computation(r2_computation)

    a3 = Agent('a3', comm)
    a3.add_computation(v3_computation)
    a3.add_computation(v4_computation)
    a3.add_computation(r3_computation)

    # Expected results to check for success
    expected_results = {
        'r1': {
            'v1': 'R',
            'v2': 'G',
            'v3': 'B',
            'v4': 'R'
        },
        'r1_2': {
            'v1': 'B',
            'v2': 'G',
            'v3': 'B',
            'v4': 'R'
        }
    }
    r1_fcts = [r1, r1_2]

    agents = [a1, a2, a3]

    for a in agents:
        a.start()

    # Now change a factor function every two seconds
    r1_fct, iteration, fail = 0, 0, False
    for _ in range(5):
        iteration += 1
        time.sleep(2)
        print('###  Iteration {} - function {}'.format(iteration,
                                                       r1_fcts[r1_fct].name))
        print(runner.status_string())
        results = runner.variable_values()
        if not results == expected_results[r1_fcts[r1_fct].name]:
            print('Error on results for {} : \nGot {} instead of {}  !'.format(
                r1_fcts[r1_fct].name, results,
                expected_results[r1_fcts[r1_fct].name]))
            fail = True
            break
        r1_fct = (r1_fct + 1) % 2
        print('## Changing to function {}'.format(r1_fcts[r1_fct].name))
        r1_computation.change_factor_function(r1_fcts[r1_fct])

    print('Finished, stopping agents')
    runner.request_stop_agents(wait_for_stop=True)

    if fail:
        print('Failed !')
        return 1
    else:
        print('Success !')
        return 0
Esempio n. 22
0
def create_agent_comp_comm_constraint(
        agt_name: str, candidate_name: str,
        candidate_info: Tuple[List[str],
                              Dict[str, str],
                              Dict[str, List[str]]],
        comm: Callable[[str, str, str], float],
        bin_vars: Dict[Tuple, BinaryVariable]) \
        -> Constraint:
    """
    Create a constraints that returns the communication costs for agent a_m
    `agt_name` when hosting a candidate computations x_i \in X_c.
    The constraints is an soft constraint that should be minimized.

    Parameters
    ----------
    agt_name: str

    candidate_info:

    comm: Callable
        function (comp_name, neigh_comp, neigh_agt) -> float that returns the
        communication cost between the computation comp_name hosted on the
        current agt_name agent and it's neighbor computation neigh_comp hosted
        on neigh_agt.

    bin_vars: Dict[Tuple, BinaryVariable]
        a dict containing one binary variable for each par (x_i, a_m) where x_i
        is a candidate computation or a neighbor of a candidate computation and
        a_m is an agent that can host x_i.

    Returns
    -------
    A Constraint object
    """
    agts, fixed_neighbors, candidate_neighbors = candidate_info

    def host_cost(**kwargs):
        """

        Parameters
        ----------
        kwargs : dict
            assignment for the variables the comm constraints depends on,
            given as a dict { variable name -> variable value}

        Returns
        -------

        """
        candidate_cost = 0.0
        locally_hosted = bin_vars[(candidate_name, agt_name)].name
        for v in fixed_neighbors:
            v_agt = fixed_neighbors[v]
            candidate_cost += kwargs[locally_hosted] * \
                                comm(candidate_name, v, v_agt)
        for v in candidate_neighbors:
            cost_v = 0.0
            for v_agt in candidate_neighbors[v]:
                arg_name = bin_vars[(v, v_agt)].name
                cost_v += kwargs[arg_name] * comm(candidate_name, v, v_agt)
            candidate_cost += kwargs[locally_hosted] * cost_v

        return candidate_cost

    scope = [bin_vars[(candidate_name, agt_name)]]
    for v in candidate_neighbors:
        for v_agt in candidate_neighbors[v]:
            scope.append(bin_vars[(v, v_agt)])

    constraint = NAryFunctionRelation(host_cost,
                                      scope,
                                      name='comm_' + agt_name + '_' +
                                      candidate_name)
    return constraint
Esempio n. 23
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)