def from_ufun( cls, u: MappingUtilityFunction, range: Tuple[float, float] = (0.0, 1.0), uncertainty: float = 0.5, variability: float = 0.0, ) -> "IPUtilityFunction": """ Generates a distribution from which `u` may have been sampled Args: u: range: range of the utility_function values uncertainty: uncertainty level Examples: - No uncertainty >>> u = MappingUtilityFunction(mapping=dict(zip([('o1',), ('o2',)], [0.3, 0.7]))) >>> p = IPUtilityFunction.from_ufun(u, uncertainty=0.0) >>> print(p) {('o1',): U(0.3, 0.3), ('o2',): U(0.7, 0.7)} - Full uncertainty >>> u = MappingUtilityFunction(mapping=dict(zip([('o1',), ('o2',)], [0.3, 0.7]))) >>> p = IPUtilityFunction.from_ufun(u, uncertainty=1.0) >>> print(p) {('o1',): U(0.0, 1.0), ('o2',): U(0.0, 1.0)} - some uncertainty >>> u = MappingUtilityFunction(mapping=dict(zip([('o1',), ('o2',)], [0.3, 0.7]))) >>> p = IPUtilityFunction.from_ufun(u, uncertainty=0.1) >>> print([_.scale for _ in p.distributions.values()]) [0.1, 0.1] >>> for k, v in p.distributions.items(): ... assert v.loc <= u(k) Returns: a new IPUtilityFunction """ if isinstance(u.mapping, dict): return cls.from_mapping( u.mapping, range=range, uncertainty=uncertainty, variability=variability, reserved_value=u.reserved_value, ) return cls.from_mapping( dict(zip(ikeys(u.mapping), ivalues(u.mapping))), range=range, uncertainty=uncertainty, variability=variability, reserved_value=u.reserved_value, )
def eval(self, offer: "Outcome") -> UtilityValue: """Calculate the utility_function value for a given outcome. Args: offer: The offer to be evaluated. Remarks: - You cannot return None from overriden apply() functions but raise an exception (ValueError) if it was not possible to calculate the UtilityValue. - Return A UtilityValue not a float for real-valued utilities for the benefit of inspection code. Returns: UtilityValue: The utility_function value which may be a distribution. If `None` it means the utility_function value cannot be calculated. """ if offer is None: return self.reserved_value if self.tupelized and not isinstance(offer, tuple): offer = tuple(ivalues(offer)) return self.distributions[offer]
def xml(self, issues: List[Issue]) -> str: """Represents the function as XML Args: issues: Examples: >>> f = HyperRectangleUtilityFunction(outcome_ranges=[ ... {0: (1.0, 2.0), 1: (1.0, 2.0)}, ... {0: (1.4, 2.0), 2: (2.0, 3.0)}] ... , utilities= [2.0, 9.0 + 4.0]) >>> print(f.xml([Issue((0.0, 4.0), name='0'), Issue((0.0, 9.0), name='1') ... , Issue((0.0, 9.0), name='2')]).strip()) <issue index="1" name="0" vtype="real" type="real" etype="real"> <range lowerbound="0.0" upperbound="4.0"></range> </issue><issue index="2" name="1" vtype="real" type="real" etype="real"> <range lowerbound="0.0" upperbound="9.0"></range> </issue><issue index="3" name="2" vtype="real" type="real" etype="real"> <range lowerbound="0.0" upperbound="9.0"></range> </issue><utility_function maxutility="-1.0"> <ufun type="PlainUfun" weight="1" aggregation="sum"> <hyperRectangle utility_function="2.0"> <INCLUDES index="0" min="1.0" max="2.0" /> <INCLUDES index="1" min="1.0" max="2.0" /> </hyperRectangle> <hyperRectangle utility_function="13.0"> <INCLUDES index="0" min="1.4" max="2.0" /> <INCLUDES index="2" min="2.0" max="3.0" /> </hyperRectangle> </ufun> </utility_function> """ output = "" for i, issue in enumerate(ivalues(issues)): name = issue.name if isinstance(issue.values, tuple): output += ( f'<issue index="{i+1}" name="{name}" vtype="real" type="real" etype="real">\n' f' <range lowerbound="{issue.values[0]}" upperbound="{issue.values[1]}"></range>\n' f"</issue>") elif isinstance(issue.values, int): output += ( f'<issue index="{i+1}" name="{name}" vtype="integer" type="integer" etype="integer" ' f'lowerbound="0" upperbound="{issue.values - 1}"/>\n') else: output += ( f'<issue index="{i+1}" name="{name}" vtype="integer" type="integer" etype="integer" ' f'lowerbound="{min(issue.values)}" upperbound="{max(issue.values)}"/>\n' ) # todo find the real maxutility output += '<utility_function maxutility="-1.0">\n <ufun type="PlainUfun" weight="1" aggregation="sum">\n' for rect, u, w in zip(self.outcome_ranges, self.mappings, self.weights): output += f' <hyperRectangle utility_function="{u * w}">\n' for indx in ikeys(rect): values = iget(rect, indx, None) if values is None: continue if isinstance(values, float) or isinstance(values, int): mn, mx = values, values elif isinstance(values, tuple): mn, mx = values else: mn, mx = min(values), max(values) output += ( f' <INCLUDES index="{indx}" min="{mn}" max="{mx}" />\n' ) output += f" </hyperRectangle>\n" output += " </ufun>\n</utility_function>" return output
def load_genius_domain(domain_file_name: str , utility_file_names: Optional[List[str]] = None , agent_factories: Optional[Union[Callable[[], Negotiator], List[Callable[[], Negotiator]]]] = None , force_single_issue=False , cache_and_discretize_outcomes=False , max_n_outcomes: int = 1e6 , n_discretization: Optional[int] = None , keep_issue_names=True , keep_value_names=True , normalize_utilities=True , n_steps=None , time_limit=3 * 60 # GENIUS uses 3min time limit by default , max_n_agents=None , dynamic_entry=True , safe_parsing=False , ignore_reserved=False , ignore_discount=False ) \ -> Tuple[Optional[SAOMechanism], List[dict], Union[Dict[str, Issue], List[Issue]]]: """ Loads a genius domain, creates appropriate negotiators if necessary Args: domain_file_name: utility_file_names: agent_factories: force_single_issue: cache_and_discretize_outcomes: max_n_outcomes: n_discretization: keep_issue_names: keep_value_names: normalize_utilities: n_steps: time_limit: max_n_agents: dynamic_entry: safe_parsing: ignore_reserved: ignore_discount: Returns: - mechanism (SAOMechanism): A mechanism for the given issues - agent_info (List[Dict]): All Negotiator functions from the given file - issues Union[Issue, Dict[str, Issue], List[Issue]]] : The issues """ issues, issues_details, mechanism = None, None, None if domain_file_name is not None: domain_file_name = str(domain_file_name) issues_details, _ = Issue.from_genius( domain_file_name, force_single_issue=False, keep_issue_names=True, keep_value_names=True, safe_parsing=safe_parsing, n_discretization=n_discretization) if force_single_issue: issues, _ = Issue.from_genius( domain_file_name, force_single_issue=force_single_issue, keep_issue_names=keep_issue_names, keep_value_names=keep_value_names, max_n_outcomes=max_n_outcomes, n_discretization=n_discretization) if issues is None: return None, [], [] else: issues, _ = Issue.from_genius( domain_file_name, force_single_issue=force_single_issue, keep_issue_names=keep_issue_names, keep_value_names=keep_value_names, safe_parsing=safe_parsing, n_discretization=n_discretization) agent_info = [] if utility_file_names is None: utility_file_names = [] utility_file_names = [str(_) for _ in utility_file_names] for ufname in utility_file_names: utility, discount_factor = UtilityFunction.from_genius( file_name=ufname, force_single_issue=force_single_issue, keep_issue_names=keep_issue_names, keep_value_names=keep_value_names, normalize_utility=normalize_utilities, domain_issues=issues_details, safe_parsing=safe_parsing, max_n_outcomes=max_n_outcomes, ignore_discount=ignore_discount, ignore_reserved=ignore_reserved) agent_info.append({ 'ufun': utility, 'reserved_value_func': utility.reserved_value if utility is not None else 0.0, 'discount_factor': discount_factor }) outcomes = None try: if force_single_issue or cache_and_discretize_outcomes or len( issues) == 1: n_outcomes: float = functools.reduce( operator.mul, (float(_.cardinality()) if not _.is_continuous() else float(n_discretization) if n_discretization is not None else np.inf for _ in ivalues(issues)), 1.0) if n_outcomes < max_n_outcomes: outcomes = enumerate_outcomes( issues, keep_issue_names=keep_issue_names) except ValueError: pass if domain_file_name is not None: mechanism_name = domain_file_name.split('/')[-1][:-4].replace( '-domain', '').replace('_domain', '').replace('domain', '') mechanism = SAOMechanism(issues=issues, outcomes=outcomes, n_steps=n_steps, time_limit=time_limit, max_n_agents=max_n_agents, dynamic_entry=dynamic_entry, name=mechanism_name, keep_issue_names=keep_issue_names) if agent_info is not None and len(agent_info) > 0: for info in agent_info: info['ufun'] = info['ufun'] if info['discount_factor'] is None or info['discount_factor'] == 1.0 else \ make_discounted_ufun(ufun=info['ufun'], info=mechanism.info , discount_per_round=info['discount_factor'], power_per_round=1.0) if agent_factories is not None and agent_info is not None and len( agent_info) > 0: if not isinstance(agent_factories, Iterable): agent_factories = [agent_factories] * len(agent_info) agents = [factory() for factory in agent_factories[0:len(agent_info)]] for a, info in zip(agents, agent_info): mechanism.add(a, ufun=info['ufun']) return mechanism, agent_info, (issues if not force_single_issue else [issues])