# BSD-3-Clause License # # Copyright 2017 Orange # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # 3. Neither the name of the copyright holder nor the names of its contributors # may be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. import unittest from subprocess import check_output, STDOUT, CalledProcessError import yaml from tests.dcop_cli.utils import instance_path class GraphColoring1(unittest.TestCase): def test_oneagent_pseudotree(self): result = run_distribute('graph_coloring1.yaml', 'oneagent', 'pseudotree') dist = result['distribution'] # With oneagent distribution, each agent host at most one computation for a in dist: self.assertLessEqual(len(dist[a]), 1) self.assertTrue(is_hosted(dist, 'v1')) self.assertTrue(is_hosted(dist, 'v2')) self.assertTrue(is_hosted(dist, 'v3')) def test_oneagent_factorgraph(self): result = run_distribute('graph_coloring1.yaml', 'oneagent', 'factor_graph') dist = result['distribution'] # With oneagent distribution, each agent host at most one computation for a in dist: self.assertLessEqual(len(dist[a]), 1) self.assertTrue(is_hosted(dist, 'v1')) self.assertTrue(is_hosted(dist, 'v2')) self.assertTrue(is_hosted(dist, 'v3')) def test_oneagent_constraints_hypergraph(self): result = run_distribute('graph_coloring1.yaml', 'oneagent', 'constraints_hypergraph') dist = result['distribution'] # With oneagent distribution, each agent host at most one computation for a in dist: self.assertLessEqual(len(dist[a]), 1) self.assertTrue(is_hosted(dist, 'v1')) self.assertTrue(is_hosted(dist, 'v2')) self.assertTrue(is_hosted(dist, 'v3')) @unittest.skip('dpop does not define computation size') def test_adhoc_pseudotree(self): result = run_distribute('graph_coloring1.yaml', 'adhoc', 'pseudotree', algo='dpop') dist = result['distribution'] self.assertTrue(is_hosted(dist, 'v1')) self.assertTrue(is_hosted(dist, 'v2')) self.assertTrue(is_hosted(dist, 'v3')) def test_adhoc_factorgraph(self): result = run_distribute('graph_coloring1.yaml', 'adhoc', 'factor_graph', algo='maxsum') dist = result['distribution'] self.assertTrue(is_hosted(dist, 'v1')) self.assertTrue(is_hosted(dist, 'v2')) self.assertTrue(is_hosted(dist, 'v3')) def test_adhoc_constraints_hypergraph(self): result = run_distribute('graph_coloring1.yaml', 'adhoc', 'constraints_hypergraph', algo='dsa') dist = result['distribution'] self.assertTrue(is_hosted(dist, 'v1')) self.assertTrue(is_hosted(dist, 'v2')) self.assertTrue(is_hosted(dist, 'v3')) def test_ilp_fgdp_factorgraph(self): # When using ilp-fgdp, we must also provide the algorithm result = run_distribute('graph_coloring1.yaml', 'ilp_fgdp', 'factor_graph', algo='maxsum') dist = result['distribution'] # ILP-FGDP requires that each agent hosts at least one computation # and we have eaxctly 5 computations and 5 agents here: for a in dist: self.assertEqual(len(dist[a]), 1) self.assertTrue(is_hosted(dist, 'v1')) self.assertTrue(is_hosted(dist, 'v2')) self.assertTrue(is_hosted(dist, 'v3')) def test_ilp_fgdp_pseudotree(self): # ilp-fgdp does not work with pseudo-tree graph model self.assertRaises(CalledProcessError, run_distribute, 'graph_coloring1.yaml', 'ilp_fgdp', 'pseudotree', algo='maxsum') def test_ilp_fgdp_constraints_hypergraph(self): # ilp-fgdp does not work with pseudo-tree graph model self.assertRaises(CalledProcessError, run_distribute, 'graph_coloring1.yaml', 'ilp_fgdp', 'constraints_hypergraph', algo='maxsum') def test_ilp_compref_factorgraph(self): # When using ilp-compref, we must also provide the algorithm result = run_distribute('graph_coloring1.yaml', 'ilp_compref', 'factor_graph', algo='maxsum') # lame: we do not check the result, we just ensure we do not crash def test_ilp_compref_constraints_hypergraph(self): result = run_distribute('graph_coloring1.yaml', 'ilp_compref', 'constraints_hypergraph', algo='dsa') # lame: we do not check the result, we just ensure we do not crash class DistAlgoOpionCompatibility(unittest.TestCase): def test_dist_with_only_algo_only(self): run_distribute('graph_coloring1.yaml', 'oneagent', algo='maxsum') def test_dist_with_graph_only(self): run_distribute('graph_coloring1.yaml', 'oneagent', graph='factor_graph') def test_incompatible_graph_algo_must_fail(self): self.assertRaises(CalledProcessError, run_distribute, 'graph_coloring1.yaml', 'oneagent', algo='dsa', graph='factor_graph') def is_hosted(mapping, computation: str): for a in mapping: if computation in mapping[a]: return True return False def run_distribute(filename, distribution, graph=None, algo=None): """ Run the distribute cli command with the given parameters """ filename = instance_path(filename) algo_opt = '' if algo is None else '-a ' + algo graph_opt = '' if graph is None else '-g ' + graph cmd = 'pydcop distribute -d {distribution} {graph_opt} ' \ '{algo_opt} {file}'.format(distribution=distribution, graph_opt=graph_opt, algo_opt=algo_opt, file=filename) output = check_output(cmd, stderr=STDOUT, timeout=10, shell=True) return yaml.load(output.decode(encoding='utf-8'), Loader=yaml.FullLoader)