def _find_best_offer( self, all_offers: List[Tuple[str, Dict]]) -> Tuple[List, float]: """ Find the offer that maximize the global gain of both partners in the given offers and for the given partner. Parameters ---------- all_offers: list a list of couples (offerer_name, offer) where offer is a dictionary of offers {(partner_val, my_val): partner_gain} Mgm2OfferMessage Returns ------- list: list of best offers (i.ee with the best gain) best_gain: gain for the best offers. """ bests, best_gain = [], 0 for partner, offers in all_offers: partial_asgt = self._neighbors_values.copy() current_partner = self._neighbor_var(partner) # Filter out the constraints linking those two variables to avoid # counting their cost twice. shared = find_dependent_relations(current_partner, self._constraints) concerned = [rel for rel in self._constraints if rel not in shared] for (val_p, my_offer_val), partner_local_gain in offers.items(): partial_asgt.update({ partner: val_p, self.variable.name: my_offer_val }) # Then we evaluate the agent constraint's for the offer # and add the partner's local gain. cost = assignment_cost(partial_asgt, concerned) global_gain = self.current_cost - cost + partner_local_gain if (global_gain > best_gain and self._mode == "min") or (global_gain < best_gain and self._mode == "max"): bests = [(val_p, my_offer_val, partner)] best_gain = global_gain elif global_gain == best_gain: bests.append((val_p, my_offer_val, partner)) return bests, best_gain
def build_computation_graph( dcop: Optional[DCOP] = None, variables: Iterable[Variable] = None, constraints: Iterable[Constraint] = None, ) -> OrderedConstraintGraph: computations = [] if dcop is not None: if constraints or variables is not None: raise ValueError("Cannot use both dcop and constraints / " "variables parameters") for v in dcop.variables.values(): var_constraints = find_dependent_relations( v, dcop.constraints.values()) computations.append(VariableComputationNode(v, var_constraints)) else: if constraints is None or variables is None: raise ValueError( "Constraints AND variables parameters must be " "provided when not building the graph from a dcop") for v in variables: var_constraints = find_dependent_relations(v, constraints) computations.append(VariableComputationNode(v, var_constraints)) return OrderedConstraintGraph(computations)
def build_computation_graph(dcop: DCOP, variables: Iterable[Variable]=None, constraints: Iterable[Constraint]=None, )-> ComputationsFactorGraph: """Build a Factor graph computation graph for a DCOP. Parameters ---------- dcop: DCOP a DCOP objects containing variables and constraints variables: iterable of Variables objects The variables to build the computation graph from. When this parameter is used, the `constraints` parameter MUST also be given. constraints: iterable of Constraints objects The constraints to build the computation graph from. When this parameter is used, the `variables` parameter MUST also be given. """ # TODO : external variables computation ? if dcop is not None: if constraints or variables is not None: raise ValueError('Cannot use both dcop and constraints / ' 'variables parameters') variables = dcop.variables.values() constraints = dcop.constraints.values() elif constraints is None or variables is None: raise ValueError('Constraints AND variables parameters must be ' 'provided wgen not building the graph from a dcop') var_nodes = [] for v in variables: dep = find_dependent_relations(v, constraints) var_nodes.append(VariableComputationNode( v, constraints_names=[d.name for d in dep])) factor_nodes = [] for c in constraints: n = FactorComputationNode(c) factor_nodes.append(n) fg = ComputationsFactorGraph(var_nodes, factor_nodes) return fg
def _find_best_offer(self, all_offers): """ Find the offer that maximize the global gain of both partners in the given offers and for the given partner. :param all_offers: a list of couples (offerer_name, offer) where offer is a dictionary of offers {(partner_val, my_val): partner_gain} Mgm2OfferMessage :return: (list of best offers, global_gain) """ bests, best_gain = [], 0 for partner, offers in all_offers: partial_asgt = self._neighbors_values.copy() current_partner = self._neighbor_var(partner) # Filter out the constraints linking those two variables to avoid # counting their cost twice. shared = find_dependent_relations(current_partner, self._constraints) concerned = [rel for rel in self._constraints if rel not in shared] for (val_p, my_offer_val), partner_local_gain in offers.items(): partial_asgt.update({ partner: val_p, self.variable.name: my_offer_val }) # Then we evaluate the agent constraint's for the offer # and add the partner's local gain. cost = self._compute_cost(partial_asgt, concerned) global_gain = self.current_cost - cost + partner_local_gain if (global_gain > best_gain and self._mode == 'min') \ or (global_gain < best_gain and self._mode == 'max'): bests = [(val_p, my_offer_val, partner)] best_gain = global_gain elif global_gain == best_gain: bests.append((val_p, my_offer_val, partner)) return bests, best_gain
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
def build_computation_graph( dcop: DCOP = None, variables: Iterable[Variable] = None, constraints: Iterable[Constraint] = None, ) -> ComputationConstraintsHyperGraph: """ Build a computation hyper graph for the DCOP. A computation graph is generally built from a DCOP, however it is also possible to build a sub-graph of the computation graph by simply passing the variables and constraints. Parameters ---------- dcop: DCOP DCOP object to build the computation graph from.When this parameter is used, the `constraints` and `variables` parameters MUST NOT be used. variables: iterable of Variables objects The variables to build the computation graph from. When this parameter is used, the `constraints` parameter MUST also be given. constraints: iterable of Constraints objects The constraints to build the computation graph from. When this parameter is used, the `variables` parameter MUST also be given. Returns ------- ComputationConstraintsHyperGraph In hyper-graph for the variables and constraints Raises ------ ValueError If both `dcop` and one of the `variables` or `constraints` arguments have been used. """ computations = [] if dcop is not None: if constraints or variables is not None: raise ValueError( "Cannot use both dcop and constraints / " "variables parameters" ) for v in dcop.variables.values(): var_constraints = find_dependent_relations(v, dcop.constraints.values()) computations.append(VariableComputationNode(v, var_constraints)) else: if constraints is None or variables is None: raise ValueError( "Constraints AND variables parameters must be " "provided when not building the graph from a dcop" ) for v in variables: var_constraints = find_dependent_relations(v, constraints) computations.append(VariableComputationNode(v, var_constraints)) # links = [] # for r in dcop.constraints.values(): # in_scope = (v.name for v in r.dimensions) # links.append(ConstraintLink(r.name, in_scope)) return ComputationConstraintsHyperGraph(computations)