def test_comp_creation_with_factory_method(): d = Domain("d", "", ["R", "G"]) v1 = Variable("v1", d) v2 = Variable("v2", d) c1 = constraint_from_str("c1", "10 if v1 == v2 else 0", [v1, v2]) graph = build_computation_graph(None, constraints=[c1], variables=[v1, v2]) comp_node = graph.computation("c1") algo_def = AlgorithmDef.build_with_default_param("maxsum") comp_def = ComputationDef(comp_node, algo_def) comp = build_computation(comp_def) assert comp is not None assert comp.name == "c1" assert comp.factor == c1 comp_node = graph.computation("v1") algo_def = AlgorithmDef.build_with_default_param("maxsum") comp_def = ComputationDef(comp_node, algo_def) comp = build_computation(comp_def) assert comp is not None assert comp.name == "v1" assert comp.variable.name == "v1" assert comp.factors == ["c1"]
def test_current_local_cost_3_ary(self): x1 = Variable("x1", list(range(2))) x2 = Variable("x2", list(range(2))) x3 = Variable("x3", [1]) @AsNAryFunctionRelation(x1, x2, x3) def phi(x1_, x2_, x3_): return x1_ + x2_ + x3_ computation = Mgm2Computation( ComputationDef( VariableComputationNode(x1, [phi]), AlgorithmDef.build_with_default_param("mgm2"), )) computation.__value__ = 1 computation._neighbors_values["x2"] = 0 computation._neighbors_values["x3"] = 1 computation2 = Mgm2Computation( ComputationDef( VariableComputationNode(x1, [phi]), AlgorithmDef.build_with_default_param("mgm2"), )) computation2.__value__ = 0 computation2._neighbors_values["x2"] = 0 computation2._neighbors_values["x3"] = 1 self.assertEqual(computation._current_local_cost(), 2) self.assertEqual(computation2._current_local_cost(), 1)
def test_enter_offer_state(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_ computation = Mgm2Computation( ComputationDef( VariableComputationNode(x1, [phi]), AlgorithmDef.build_with_default_param("mgm2", mode="max"), )) computation.message_sender = DummySender() computation._postponed_msg["offer"] = [ ("x2", Mgm2OfferMessage({(1, 1): 5}, is_offering=True), 1) ] computation._enter_state("offer") self.assertEqual(computation._state, "offer") self.assertEqual(computation._postponed_msg["offer"], []) self.assertEqual(computation._offers, [("x2", Mgm2OfferMessage({(1, 1): 5}, True))])
def test_gain_not_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._state = "gain" computation.on_gain_msg("x2", Mgm2GainMessage(5), 1) self.assertEqual(computation._neighbors_gains, {"x2": 5})
def test_compute_offers_max_mode(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_): if x1_ == x3_: return 2 elif x1_ == x2_: return 1 return 0 computation = Mgm2Computation( ComputationDef( VariableComputationNode(x1, [phi]), AlgorithmDef.build_with_default_param("mgm2", mode="max"), )) computation._neighbors_values = {"x2": 0, "x3": 0} computation._partner = x2 computation.__value__ = 1 computation.__cost__ = 0 offers = computation._compute_offers_to_send() self.assertEqual(offers, {(0, 0): -2, (0, 1): -2, (1, 1): -1})
def test_go_reject_no_postponed_value_message(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": 1} computation.__value__ = 1 computation._potential_value = 0 computation._state = "go?" # from Response message or accepted offer computation._handle_go_message("x3", Mgm2GoMessage(False)) self.assertEqual(computation._state, "value") self.assertEqual(computation.__value__, 1) self.test_clear_agent()
def test_receive_one_neighbor_dcop_computation(): comp_def = ComputationDef(ComputationNode("test", neighbors=["bar"]), AlgorithmDef("fake", {})) c = SynchDcopC("test", comp_def) c.on_new_cycle = MagicMock() c.start() # First cycle, c receives a message from its single neighbor, # on_cycle_message must be called. msg1 = FooMsg(1) # This is automatically done when sending from a Synchronous Computation: msg1.cycle_id = 0 c.on_message("bar", msg1, 42) assert c.cycle_count == 1 c.on_new_cycle.assert_any_call({"bar": (msg1, 42)}, 0) # Second cycle, c receives a message from its single neighbor, # on_cycle_message must be called again. c.on_new_cycle.reset_mock() msg2 = FooMsg(2) msg2.cycle_id = 1 c.on_message("bar", msg2, 43) c.on_new_cycle.assert_any_call({"bar": (msg2, 43)}, 1) assert c.cycle_count == 2
def test_select_and_send_random_value_when_starting(): # When starting, a DSA computation select a random value and send it to # all neighbors v1 = Variable('v1', [0, 1, 2, 3, 4]) v2 = Variable('v2', [0, 1, 2, 3, 4]) v3 = Variable('v3', [0, 1, 2, 3, 4]) @AsNAryFunctionRelation(v1, v2, v3) def c1(v1_, v2_, v3_): return abs(v1_ - v2_ + v3_) node = VariableComputationNode(v1, [c1]) comp_def = ComputationDef(node, AlgorithmDef.build_with_default_param('dsa')) computation = DsaComputation(comp_def=comp_def) message_sender = MagicMock() computation.message_sender = message_sender computation.start() assert computation.current_value in v1.domain expected_message = DsaMessage(computation.current_value) print("message_sender.mock_calls", message_sender.mock_calls) message_sender.assert_has_calls([ call('v1', 'v2', expected_message, None, None), call('v1', 'v3', expected_message, None, None) ], any_order=True)
def test_build_computation_max_mode(): v1 = Variable('v1', [0, 1, 2, 3, 4]) n1 = VariableComputationNode(v1, []) comp_def = ComputationDef( n1, AlgorithmDef.build_with_default_param('dsa', mode='max')) c = DsaComputation(comp_def) assert c.mode == 'max'
def test_repr_dsa_class(): variable = Variable('a', [0, 1, 2, 3, 4]) c1 = UnaryFunctionRelation('c1', variable, lambda x: abs(x - 2)) computation = DsaComputation( ComputationDef(VariableComputationNode(variable, [c1]), AlgorithmDef.build_with_default_param('dsa'))) assert repr(computation) == "dsa.DsaComputation(a)"
def test_build_computation_default_params(): v1 = Variable('v1', [0, 1, 2, 3, 4]) n1 = VariableComputationNode(v1, []) comp_def = ComputationDef(n1, AlgorithmDef.build_with_default_param('dsa')) c = DsaComputation(comp_def) assert c.mode == 'min' assert c.variant == 'B' assert c.stop_cycle == 0 assert c.probability == 0.7
def test_footprint_on_computation_object(): v1 = Variable('v1', [0, 1, 2, 3, 4]) v2 = Variable('v2', [0, 1, 2, 3, 4]) c1 = relation_from_str('c1', '0 if v1 == v2 else 1', [v1, v2]) n1 = VariableComputationNode(v1, [c1]) n2 = VariableComputationNode(v2, [c1]) comp_def = ComputationDef(n1, AlgoDef('dsa', mode='min')) c = build_computation(comp_def) assert c.footprint() == 5
def test_1_unary_constraint_means_no_neighbors(): variable = Variable('a', [0, 1, 2, 3, 4]) c1 = UnaryFunctionRelation('c1', variable, lambda x: abs(x - 2)) node = VariableComputationNode(variable, [c1]) comp_def = ComputationDef(node, AlgorithmDef.build_with_default_param('dsa')) computation = DsaComputation(comp_def=comp_def) assert len(computation.neighbors) == 0
def test_go_accept_with_postponed_value_message(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": 1} computation.__value__ = 1 computation.__cost__ = 9 computation._state = "go?" computation._postponed_msg["value"] = [("x2", Mgm2ValueMessage(1), 1)] # from Response message or accepted offer computation._potential_gain = 10 computation._potential_value = 0 computation.on_go_msg("x3", Mgm2GoMessage(True), 1) # Common tests self.assertEqual(computation._state, "value") self.assertEqual(computation._potential_gain, 0) self.assertIsNone(computation._potential_value) self.assertEqual(computation._neighbors_values, {"x2": 1}) self.assertEqual(computation._neighbors_gains, dict()) self.assertEqual(computation._offers, []) self.assertIsNone(computation._partner) self.assertEqual(computation.__nb_received_offers__, 0) self.assertFalse(computation._committed) self.assertFalse(computation._is_offerer) self.assertFalse(computation._can_move) # If cannot move self.assertEqual(computation.current_value, 1) # If can move computation._can_move = True computation._state = "go?" computation._potential_value = 0 computation.on_go_msg("x3", Mgm2GoMessage(True), 1) self.assertEqual(computation.current_value, 0)
def _deploy_computation(self, agent_id: str): """Deploy computations hosted on agent `agent_id` """ for c in self.initial_dist.computations_hosted(agent_id): self._nb_computations += 1 self.logger.info('Deploying computation %s on %s', c, agent_id) comp_def = ComputationDef(self.graph.computation(c), self._algo) self._send_mgt_msg(agent_id, DeployMessage(comp_def)) self.discovery.subscribe_computation( comp_def.node.name, self._cb_computation_registration) self.discovery.subscribe_replica(comp_def.node.name, self._cb_replica_registration)
def get_computation_instance(graph, name): # Get the computation node for x1 comp_node = graph.computation(name) # Create the ComputationDef and computation instance algo_def = AlgorithmDef.build_with_default_param("ncbb") comp_def = ComputationDef(comp_node, algo_def) comp = NcbbAlgo(comp_def) comp._msg_sender = MagicMock() return comp
def test_fallback_memory_footprint_from_classic_import(): # use dsatuto as is has no computation_memory function defined import pydcop.algorithms.dsatuto as dsa_module v1 = Variable("v1", [1, 2]) comp_def = ComputationDef( VariableComputationNode(v1, []), AlgorithmDef.build_with_default_param("dsatuto"), ) comp = dsa_module.DsaTutoComputation(comp_def) assert comp.footprint() == 1
def test_clear_agent(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": 1} computation.__value__ = 1 computation.__nb_received_offers__ = 2 computation._partner = x3 computation._state = "go?" computation._potential_gain = 10 computation._potential_value = 10 computation._neighbors_values = {"x2": 1, "x3": 0} computation._neighbors_gains = {"x2": 5, "x3": 1} computation._offers = [(1, 1, "x2")] computation._committed = True computation._is_offerer = True computation._can_move = True computation._clear_agent() self.assertEqual(computation._potential_gain, 0) self.assertEqual(computation._neighbors_values, dict()) self.assertEqual(computation._neighbors_gains, dict()) self.assertEqual(computation._offers, []) self.assertIsNone(computation._partner) self.assertEqual(computation.__nb_received_offers__, 0) self.assertFalse(computation._committed) self.assertFalse(computation._is_offerer) self.assertFalse(computation._can_move) self.assertIsNone(computation._potential_value) self.assertIsNotNone(computation.current_value)
def test_3ary_constraint_2_neighbors(): v1 = Variable('v1', [0, 1, 2, 3, 4]) v2 = Variable('v2', [0, 1, 2, 3, 4]) v3 = Variable('v3', [0, 1, 2, 3, 4]) @AsNAryFunctionRelation(v1, v2, v3) def c1(v1_, v2_, v3_): return abs(v1_ - v2_ + v3_) node = VariableComputationNode(v1, [c1]) comp_def = ComputationDef(node, AlgorithmDef.build_with_default_param('dsa')) computation = DsaComputation(comp_def=comp_def) assert len(computation.neighbors) == 2
def test_cycle_id_is_added_when_using_post_to_all_neighbors_dcop_computation(): comp_def = ComputationDef( ComputationNode("test", neighbors=["bar", "yup"]), AlgorithmDef("fake", {})) c = SynchDcopC("test", comp_def) c.start() c.post_to_all_neighbors(FooMsg(1)) expected = FooMsg(1) expected.cycle_id = 0 c.message_sender.assert_any_call("test", "yup", expected, None, None) c.message_sender.assert_any_call("test", "bar", expected, None, None)
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_value_all_neighbors_received(self): x1 = Variable("x1", list(range(2))) x2 = Variable("x2", list(range(2))) @AsNAryFunctionRelation(x1, x2) def phi(x1_, x2_): return x1_ + x2_ computation = Mgm2Computation( ComputationDef( VariableComputationNode(x1, [phi]), AlgorithmDef.build_with_default_param("mgm2"), )) computation.message_sender = DummySender() computation._state = "value" computation.__value__ = 1 computation.on_value_msg("x2", Mgm2ValueMessage(0), 1) self.assertEqual(computation._state, "offer") self.assertEqual(computation._neighbors_values["x2"], 0) self.assertEqual(computation._potential_gain, 1) self.assertEqual(computation._potential_value, 0) computation2 = Mgm2Computation( ComputationDef( VariableComputationNode(x1, [phi]), AlgorithmDef.build_with_default_param("mgm2", mode="max"), )) computation2.message_sender = DummySender() computation2._state = "value" computation2.__value__ = 1 computation2.on_value_msg("x2", Mgm2ValueMessage(0), 1) self.assertEqual(computation2._state, "offer") self.assertEqual(computation2._neighbors_values["x2"], 0) self.assertEqual(computation2._potential_gain, 0) self.assertEqual(computation2._potential_value, 1)
def test_best_unary(self): x = Variable("x", list(range(5))) 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 bests, best = computation._compute_best_value() self.assertEqual(best, 0) self.assertEqual(bests, [1, 4])
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( ComputationDef( VariableComputationNode(x, [phi]), AlgorithmDef.build_with_default_param("mgm2"), )) computation.__value__ = 0 computation2 = Mgm2Computation( ComputationDef( VariableComputationNode(x, [phi]), AlgorithmDef.build_with_default_param("mgm2"), )) computation2.__value__ = 1 self.assertEqual(computation._current_local_cost(), 1) self.assertEqual(computation2._current_local_cost(), 0)
def test_find_best_offer_max_mode_one_offerer(self): x1 = Variable("x1", list(range(2))) x2 = Variable("x2", list(range(2))) x3 = Variable("x3", list(range(2))) x4 = Variable("x4", list(range(2))) @AsNAryFunctionRelation(x1, x2, x3) def phi(x1_, x2_, x3_): if x1_ == x3_: return 2 elif x1_ == x2_: return 1 return 0 @AsNAryFunctionRelation(x1, x4) def psi(x1_, x4_): if x1_ == x4_: return 1 return 0 computation = Mgm2Computation( ComputationDef( VariableComputationNode(x1, [phi, psi]), AlgorithmDef.build_with_default_param("mgm2", mode="max"), )) computation._neighbors_values = {"x2": 0, "x3": 1, "x4": 1} computation.__value__ = 0 computation.__cost__ = 1 bests, best_gain = computation._find_best_offer([("x2", { (0, 0): -1, (0, 1): -5, (1, 0): -3 })]) # global gain: -1 -5 -5 bests2, best_gain2 = computation._find_best_offer([("x2", { (0, 0): -1, (0, 1): -5, (1, 0): -6 })]) # global gain: -1 -5 -5 self.assertEqual(bests, [(0, 1, "x2")]) self.assertEqual(best_gain, -5) self.assertEqual(set(bests2), {(0, 1, "x2"), (1, 0, "x2")}) self.assertEqual(best_gain2, -5)
def test_footprint_on_computation_object(): v1 = Variable('v1', [0, 1, 2, 3, 4]) v2 = Variable('v2', [0, 1, 2, 3, 4]) c1 = relation_from_str('c1', '0 if v1 == v2 else 1', [v1, v2]) n1 = VariableComputationNode(v1, [c1]) n2 = VariableComputationNode(v2, [c1]) comp_def = ComputationDef( n1, AlgorithmDef.build_with_default_param('dsa', mode='min')) c = DsaComputation(comp_def) # Must fix unit size otherwise the tests fails when we change the default # value dsa.UNIT_SIZE = 1 footprint = c.footprint() assert footprint == 1
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_binary_func(self): x1 = Variable("x1", list(range(2))) x2 = Variable("x2", list(range(2))) @AsNAryFunctionRelation(x1, x2) def phi(x1_, x2_): return x1_ + x2_ computation = Mgm2Computation( ComputationDef( VariableComputationNode(x1, [phi]), AlgorithmDef.build_with_default_param("mgm2"), )) self.assertEqual(computation._compute_cost(**{"x1": 0, "x2": 0}), 0) self.assertEqual(computation._compute_cost(**{"x1": 0, "x2": 1}), 1) self.assertEqual(computation._compute_cost(**{"x1": 1, "x2": 0}), 1) self.assertEqual(computation._compute_cost(**{"x1": 1, "x2": 1}), 2)
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_find_best_offer_min_mode_2_offerers(self): x1 = Variable("x1", list(range(2))) x2 = Variable("x2", list(range(2))) x3 = Variable("x3", list(range(2))) x4 = Variable("x4", list(range(2))) @AsNAryFunctionRelation(x1, x2, x3) def phi(x1_, x2_, x3_): if x1_ == x3_: return 2 elif x1_ == x2_: return 1 return 0 @AsNAryFunctionRelation(x1, x4) def psi(x1_, x4_): if x1_ == x4_: return 1 return 0 computation = Mgm2Computation( ComputationDef( VariableComputationNode(x1, [phi, psi]), AlgorithmDef.build_with_default_param("mgm2"), )) computation._neighbors_values = {"x2": 0, "x3": 0, "x4": 0} computation.__value__ = 0 computation.__cost__ = 3 bests, best_gain = computation._find_best_offer([ ("x2", { (0, 0): 1, (0, 1): 5, (1, 0): 3 }), ("x4", { (1, 0): 7, (0, 1): 2, (1, 1): 3 }), ]) self.assertEqual(set(bests), {(0, 1, "x2"), (1, 0, "x4")}) self.assertEqual(best_gain, 8)