def discrete_outcomes(self, n_max: int = None, astype: Type['Outcome'] = dict) -> List['Outcome']: """ A discrete set of outcomes that spans the outcome space Args: n_max: The maximum number of outcomes to return. If None, all outcomes will be returned for discrete issues and *100* if any of the issues was continuous astype: A type to cast the resulting outcomes to. Returns: List[Outcome]: List of `n` or less outcomes """ if self.outcomes is not None: return self.outcomes if self.__discrete_outcomes is None: if all(issue.is_discrete() for issue in self.issues): self.__discrete_outcomes = Issue.sample( issues=self.issues, n_outcomes=n_max, astype=astype, with_replacement=False, fail_if_not_enough=False) else: self.__discrete_outcomes = Issue.sample( issues=self.issues, n_outcomes=n_max if n_max is not None else 100, astype=astype, with_replacement=False, fail_if_not_enough=False) return self.__discrete_outcomes
def random( cls, issues: List["Issue"], reserved_value=(0.0, 1.0), normalized=True, max_n_outcomes: int = 10000, ): outcomes = (Issue.enumerate(issues) if Issue.num_outcomes(issues) <= max_n_outcomes else Issue.sample(issues, max_n_outcomes, with_replacement=False, fail_if_not_enough=False)) return UtilityFunction.generate_random(1, outcomes)[0]
def random_outcomes( self, n: int = 1, astype: Type[Outcome] = None ) -> List["Outcome"]: """Returns random offers. Args: n: Number of outcomes to generate astype: The type to use for the generated outcomes Returns: A list of outcomes (in the type specified using `astype`) of at most n outcomes. Remarks: - If the number of outcomes `n` cannot be satisfied, a smaller number will be returned - Sampling is done without replacement (i.e. returned outcomes are unique). """ if astype is None: astype = self.ami.outcome_type if self.ami.issues is None or len(self.ami.issues) == 0: raise ValueError("I do not have any issues to generate offers from") return Issue.sample( issues=self.issues, n_outcomes=n, astype=astype, with_replacement=False, fail_if_not_enough=False, )
def equiprobable_thresholds(cls, n: int, ufun: "UtilityFunction", issues: List[Issue], n_samples: int = 1000) -> List[float]: """ Generates thresholds for the n given levels where levels are equally likely approximately Args: n: Number of scale levels (one side) ufun: The utility function to use issues: The issues to generate the thresholds for n_samples: The number of samples to use during the process """ samples = list( Issue.sample(issues, n_samples, with_replacement=False, fail_if_not_enough=False)) n_samples = len(samples) diffs = [] for i, first in enumerate(samples): n_diffs = min(10, n_samples - i - 1) for second in sample(samples[i + 1:], k=n_diffs): diffs.append(abs(ufun.compare_real(first, second))) diffs = np.array(diffs) hist, edges = np.histogram(diffs, bins=n + 1) return edges[1:-1].tolist()
def random(cls, issues: List["Issue"], reserved_value=(0.0, 1.0), normalized=True, **kwargs): from negmas.utilities.ops import normalize reserved_value = make_range(reserved_value) n_issues = len(issues) r = reserved_value if reserved_value is not None else random.random() s = 0.0 weights = [2 * (random.random() - 0.5) for _ in range(n_issues)] biases = [2 * (random.random() - 0.5) for _ in range(n_issues)] ufun = cls( weights=weights, biases=biases, reserved_value=random.random() * (reserved_value[1] - reserved_value[0]) + reserved_value[0], ) if normalized: return normalize( ufun, outcomes=Issue.discretize_and_enumerate(issues, n_discretization=10, max_n_outcomes=10000), ) return ufun
def test_imap_issues(): issues = [Issue(5, f"i{i}") for i in range(5)] m = MyMechanism(issues=issues) assert len(m.ami.imap) == 10 for i in range(5): assert m.ami.imap[i] == f"i{i}" assert m.ami.imap[f"i{i}"] == i
def test_ufun_range_linear(n_issues): issues = [Issue(values=(0.0, 1.0), name=f"i{i}") for i in range(n_issues)] rs = [(i + 1.0) * random.random() for i in range(n_issues)] ufun = LinearUtilityFunction(weights=rs, reserved_value=0.0) assert ufun([0.0] * n_issues) == 0.0 assert ufun([1.0] * n_issues) == sum(rs) rng = utility_range(ufun, issues=issues) assert rng[0] >= 0.0 assert rng[1] <= sum(rs)
def _unorm(self, u: UtilityFunction, mn, mx): if not isinstance(u, LinearUtilityAggregationFunction) and not isinstance( u, LinearUtilityFunction ): return normalize(u, outcomes=Issue.enumerate(issues, max_n_outcomes=1000)) # _, mx = self._urange(u, issues) if mx < 0: return None u.weights = {k: _ / mx for k, _ in u.weights.items()} return u
def random_outcomes(self, n: int = 1, astype: Type[Outcome] = dict) -> List['Outcome']: """Returns random offers""" if self.info.issues is None or len(self.info.issues) == 0: raise ValueError( 'I do not have any issues to generate offers from') return Issue.sample(issues=self.issues, n_outcomes=n, astype=astype, with_replacement=False, fail_if_not_enough=False)
def test_ufun_range_general(n_issues): issues = [Issue(values=(0.0, 1.0), name=f"i{i}") for i in range(n_issues)] rs = [(i + 1.0) * random.random() for i in range(n_issues)] ufun = MappingUtilityFunction( mapping=lambda x: sum(r * v for r, v in zip(rs, outcome_as_tuple(x))), outcome_type=tuple, ) assert ufun([0.0] * n_issues) == 0.0 assert ufun([1.0] * n_issues) == sum(rs) rng = utility_range(ufun, issues=issues) assert rng[0] >= 0.0 assert rng[1] <= sum(rs)
def test_reading_writing_linear_ufun(tmp_path): from negmas.utilities import LinearUtilityAggregationFunction, UtilityFunction from negmas.outcomes import Issue base_folder = pkg_resources.resource_filename( "negmas", resource_name="tests/data/Laptop") _, agent_info, issues = load_genius_domain_from_folder( base_folder, keep_issue_names=True, keep_value_names=True, ) ufuns = [_["ufun"] for _ in agent_info] for ufun in ufuns: assert isinstance(ufun, LinearUtilityAggregationFunction) dst = tmp_path / "tmp.xml" print(dst) UtilityFunction.to_genius(ufun, issues=issues, file_name=dst) ufun2, _ = UtilityFunction.from_genius(dst) assert isinstance(ufun2, LinearUtilityAggregationFunction) for outcome in Issue.enumerate(issues): assert abs(ufun2(outcome) - ufun(outcome)) < 1e-3
def pareto_frontier( ufuns: Iterable[UtilityFunction], outcomes: Iterable[Outcome] = None, issues: Iterable[Issue] = None, n_discretization: Optional[int] = 10, sort_by_welfare=False, ) -> Tuple[List[Tuple[float]], List[int]]: """Finds all pareto-optimal outcomes in the list Args: ufuns: The utility functions outcomes: the outcomes to be checked. If None then all possible outcomes from the issues will be checked issues: The set of issues (only used when outcomes is None) n_discretization: The number of items to discretize each real-dimension into sort_by_welfare: If True, the resutls are sorted descendingly by total welfare Returns: Two lists of the same length. First list gives the utilities at pareto frontier points and second list gives their indices """ ufuns = list(ufuns) if issues: issues = list(issues) if outcomes: outcomes = list(outcomes) # calculate all candidate outcomes if outcomes is None: if issues is None: return [], [] outcomes = Issue.discretize_and_enumerate(issues, n_discretization) # outcomes = itertools.product( # *[issue.alli(n=n_discretization) for issue in issues] # ) points = [[ufun(outcome) for ufun in ufuns] for outcome in outcomes] return _pareto_frontier(points, sort_by_welfare=sort_by_welfare)
def test_inverse_genius_domain(normalize): issues, _ = Issue.from_xml_str( open( pkg_resources.resource_filename( "negmas", resource_name="tests/data/Laptop/Laptop-C-domain.xml" ), "r", ).read(), ) u, _ = UtilityFunction.from_xml_str( open( pkg_resources.resource_filename( "negmas", resource_name="tests/data/Laptop/Laptop-C-prof1.xml" ), "r", ).read(), force_single_issue=False, normalize_utility=normalize, ) u.init_inverse(issues=issues) for i in range(100): v = u(u.inverse(i / 100.0, eps=(0.001, 0.1), assume_normalized=normalize)) assert v-1e-3 <= v <= v+0.1
def random(cls, issues: List["Issue"], reserved_value=(0.0, 1.0), normalized=True, **kwargs): from negmas.utilities.ops import normalize reserved_value = make_range(reserved_value) n_issues = len(issues) # r = reserved_value if reserved_value is not None else random.random() s = 0.0 weights = dict( zip([_.name for _ in issues], [random.random() for _ in range(n_issues)])) s = sum(_ for _ in weights.values()) if s: for k, v in weights.items(): weights[k] = v / s issue_utilities = dict( zip([_.name for _ in issues], [random_mapping(issue) for issue in issues])) ufun = cls( weights=weights, issue_utilities=issue_utilities, reserved_value=random.random() * (reserved_value[1] - reserved_value[0]) + reserved_value[0], **kwargs, ) if normalized: return normalize( ufun, outcomes=Issue.discretize_and_enumerate(issues, max_n_outcomes=5000), )
def __init__( self, issues: List['Issue'] = None, outcomes: Union[int, List['Outcome']] = None, n_steps: int = None, time_limit: float = None, max_n_agents: int = None, dynamic_entry=False, cache_outcomes=True, max_n_outcomes: int = 1000000, keep_issue_names=True, annotation: Optional[Dict[str, Any]] = None, state_factory=MechanismState, name=None, ): """ Args: issues: List of issues to use (optional as you can pass `outcomes`) outcomes: List of outcomes (optional as you can pass `issues`). If an int then it is the number of outcomes n_steps: Number of rounds allowed (None means infinity) time_limit: Number of real seconds allowed (None means infinity) max_n_agents: Maximum allowed number of agents dynamic_entry: Allow agents to enter/leave negotiations between rounds cache_outcomes: If true, a list of all possible outcomes will be cached max_n_outcomes: The maximum allowed number of outcomes in the cached set keep_issue_names: If True, dicts with issue names will be used for outcomes otherwise tuples annotation: Arbitrary annotation state_factory: A callable that receives an arbitrary set of key-value pairs and return a MechanismState descendant object name: Name of the mechanism session. Should be unique. If not given, it will be generated. """ super().__init__(name=name) # parameters fixed for all runs if issues is None: if outcomes is None: raise ValueError( 'Not issues or outcomes are given to this mechanism. Cannot be constructed' ) else: if isinstance(outcomes, int): outcomes = [(_, ) for _ in range(outcomes)] else: outcomes = list(outcomes) n_issues = len(outcomes[0]) issues = [] issue_names = ikeys(outcomes[0]) for issue in range(n_issues): vals = list(set([_[issue] for _ in outcomes])) issues.append(vals) __issues = [ Issue(_, name=name_) for _, name_ in zip(issues, issue_names) ] else: __issues = list(issues) if outcomes is None and cache_outcomes: try: if len(__issues) == 0: outcomes = [] else: n_outcomes = 1 for issue in __issues: if issue.is_continuous(): break n_outcomes *= issue.cardinality() if n_outcomes > max_n_outcomes: break else: outcomes = enumerate_outcomes( __issues, keep_issue_names=keep_issue_names) except ValueError: pass elif outcomes is not None: issue_names = [_.name for _ in issues] assert (keep_issue_names and isinstance(outcomes[0], dict)) or \ (not keep_issue_names and isinstance(outcomes[0], tuple)), f"Either you request to keep issue" \ f" names but use tuple outcomes or " \ f"vice versa (names={keep_issue_names}" \ f", type={type(outcomes[0])})" if keep_issue_names: for i, outcome in enumerate(outcomes): assert list(outcome.keys()) == issue_names else: for i, outcome in enumerate(outcomes): assert len(outcome) == len(issue_names) __outcomes = outcomes # we have now __issues is a List[Issue] and __outcomes is Optional[List[Outcome]] # create a couple of ways to access outcomes by indices effeciently self.outcome_index = lambda x: None self.outcome_indices = [] if __outcomes is not None and cache_outcomes: self.outcome_indices = range(len(__outcomes)) try: _outcome_index = dict(zip(__outcomes, self.outcome_indices)) self.outcome_index = lambda x: _outcome_index[x] except: self.outcome_index = lambda x: __outcomes.index(x) self.id = str(uuid.uuid4()) self.info = MechanismInfo( id=self.id, n_outcomes=None if outcomes is None else len(outcomes), issues=__issues, outcomes=__outcomes, time_limit=time_limit, n_steps=n_steps, dynamic_entry=dynamic_entry, max_n_agents=max_n_agents, annotation=annotation) self._history = [] # if self.info.issues is not None: # self.info.issues = tuple(self.info.issues) # if self.info.outcomes is not None: # self.info.outcomes = tuple(self.info.outcomes) self._state_factory = state_factory Mechanism.all[self.id] = self self._requirements = {} self._agents = [] self._roles = [] self._start_time = None self._started = False self._step = 0 self._n_accepting_agents = 0 self._broken = False self._agreement = None self._timedout = False self._running = False self._error = False self._error_details = '' self.__discrete_outcomes: List[Outcome] = None self.agents_of_role = defaultdict(list) self.role_of_agent = {}
def __init__( self, issues: List["Issue"] = None, outcomes: Union[int, List["Outcome"]] = None, n_steps: int = None, time_limit: float = None, step_time_limit: float = None, negotiator_time_limit: float = None, max_n_agents: int = None, dynamic_entry=False, cache_outcomes=True, max_n_outcomes: int = 1000000, keep_issue_names=None, annotation: Optional[Dict[str, Any]] = None, state_factory=MechanismState, enable_callbacks=False, checkpoint_every: int = 1, checkpoint_folder: Optional[Union[str, Path]] = None, checkpoint_filename: str = None, extra_checkpoint_info: Dict[str, Any] = None, single_checkpoint: bool = True, exist_ok: bool = True, name=None, outcome_type=tuple, genius_port: int = DEFAULT_JAVA_PORT, id: str = None, ): super().__init__(name, id=id) CheckpointMixin.checkpoint_init( self, step_attrib="_step", every=checkpoint_every, folder=checkpoint_folder, filename=checkpoint_filename, info=extra_checkpoint_info, exist_ok=exist_ok, single=single_checkpoint, ) time_limit = time_limit if time_limit is not None else float("inf") step_time_limit = (step_time_limit if step_time_limit is not None else float("inf")) negotiator_time_limit = (negotiator_time_limit if negotiator_time_limit is not None else float("inf")) if keep_issue_names is not None: warnings.warn( "keep_issue_names is depricated. Use outcome_type instead.\n" "keep_issue_names=True <--> outcome_type=dict\n" "keep_issue_names=False <--> outcome_type=tuple\n", DeprecationWarning, ) outcome_type = dict if keep_issue_names else tuple keep_issue_names = not issubclass(outcome_type, tuple) # parameters fixed for all runs if issues is None: if outcomes is None: __issues = [] outcomes = [] else: if isinstance(outcomes, int): outcomes = [(_, ) for _ in range(outcomes)] else: outcomes = list(outcomes) n_issues = len(outcomes[0]) issues = [] issue_names = ikeys(outcomes[0]) for issue in range(n_issues): vals = list(set([_[issue] for _ in outcomes])) issues.append(vals) __issues = [ Issue(_, name=name_) for _, name_ in zip(issues, issue_names) ] else: __issues = list(issues) if outcomes is None and cache_outcomes: try: if len(__issues) == 0: outcomes = [] else: n_outcomes = 1 for issue in __issues: if issue.is_uncountable(): break n_outcomes *= issue.cardinality if n_outcomes > max_n_outcomes: break else: outcomes = enumerate_outcomes(__issues, astype=outcome_type) except ValueError: pass elif outcomes is not None: issue_names = [_.name for _ in issues] assert (not keep_issue_names and isinstance(outcomes[0], tuple)) or ( keep_issue_names and not isinstance(outcomes[0], tuple)), ( f"Either you request to keep issue" f" names but use tuple outcomes or " f"vice versa (names={keep_issue_names}" f", type={type(outcomes[0])})") if keep_issue_names: for outcome in outcomes: assert list(outcome.keys()) == issue_names else: for i, outcome in enumerate(outcomes): assert len(outcome) == len(issue_names) self.__outcomes = outcomes # we have now __issues is a List[Issue] and __outcomes is Optional[List[Outcome]] # create a couple of ways to access outcomes by indices effeciently self.outcome_indices = [] self.__outcome_index = None if self.__outcomes is not None and cache_outcomes: self.outcome_indices = range(len(self.__outcomes)) self.__outcome_index = dict( zip( (outcome_as_tuple(o) for o in self.__outcomes), range(len(self.__outcomes)), )) self.id = str(uuid.uuid4()) _imap = dict(zip((_.name for _ in __issues), range(len(__issues)))) _imap.update( dict(zip(range(len(__issues)), (_.name for _ in __issues)))) self.ami = AgentMechanismInterface( id=self.id, n_outcomes=None if outcomes is None else len(outcomes), issues=__issues, outcomes=self.__outcomes, time_limit=time_limit, n_steps=n_steps, step_time_limit=step_time_limit, negotiator_time_limit=negotiator_time_limit, dynamic_entry=dynamic_entry, max_n_agents=max_n_agents, annotation=annotation, outcome_type=dict if keep_issue_names else tuple, imap=_imap, ) self.ami._mechanism = self self._history: List[MechanismState] = [] self._stats: Dict[str, Any] = dict() self._stats["round_times"] = list() self._stats["times"] = defaultdict(float) self._stats["exceptions"] = defaultdict(list) # if self.ami.issues is not None: # self.ami.issues = tuple(self.ami.issues) # if self.ami.outcomes is not None: # self.ami.outcomes = tuple(self.ami.outcomes) self._state_factory = state_factory self._requirements = {} self._negotiators = [] self._negotiator_map: Dict[str, "SAONegotiator"] = dict() self._roles = [] self._start_time = None self._started = False self._step = 0 self._n_accepting_agents = 0 self._broken = False self._agreement = None self._timedout = False self._running = False self._error = False self._error_details = "" self._waiting = False self.__discrete_outcomes: List[Outcome] = None self._enable_callbacks = enable_callbacks self.agents_of_role = defaultdict(list) self.role_of_agent = {} # mechanisms do not differentiate between RANDOM_JAVA_PORT and ANY_JAVA_PORT. # if either is given as the genius_port, it will fix a port and all negotiators # that are not explicitly assigned to a port (by passing port>0 to them) will just # use that port. self.genius_port = genius_port if genius_port > 0 else get_free_tcp_port( ) self.params = dict( dynamic_entry=dynamic_entry, genius_port=genius_port, cache_outcomes=cache_outcomes, annotation=annotation, )
def hamlet(): return Issue(['val {}'.format(_) for _ in range(5)], name='THE problem')
def uissue(): return Issue(['val {}'.format(_) for _ in range(5)])
def dissue(): return Issue(10, name='d')
def uissue(): return Issue(["val {}".format(_) for _ in range(5)])
def sissue(): return Issue(['val {}'.format(_) for _ in range(5)], name='s')
def bissue(): return Issue(['be', 'not b'], name='binary')
def bissue(): return Issue(["be", "not b"], name="binary")
def int_issues(): return Issue.generate([5], [10])
def sissue(): return Issue(["val {}".format(_) for _ in range(5)], name="s")
def cissue(): return Issue((0.0, 1.0), name="c")
def __init__( self, issues: List["Issue"] = None, outcomes: Union[int, List["Outcome"]] = None, n_steps: int = None, time_limit: float = None, step_time_limit: float = None, max_n_agents: int = None, dynamic_entry=False, cache_outcomes=True, max_n_outcomes: int = 1000000, keep_issue_names=None, annotation: Optional[Dict[str, Any]] = None, state_factory=MechanismState, enable_callbacks=False, checkpoint_every: int = 1, checkpoint_folder: Optional[Union[str, Path]] = None, checkpoint_filename: str = None, extra_checkpoint_info: Dict[str, Any] = None, single_checkpoint: bool = True, exist_ok: bool = True, name=None, outcome_type=tuple, ): """ Args: issues: List of issues to use (optional as you can pass `outcomes`) outcomes: List of outcomes (optional as you can pass `issues`). If an int then it is the number of outcomes n_steps: Number of rounds allowed (None means infinity) time_limit: Number of real seconds allowed (None means infinity) max_n_agents: Maximum allowed number of agents dynamic_entry: Allow agents to enter/leave negotiations between rounds cache_outcomes: If true, a list of all possible outcomes will be cached max_n_outcomes: The maximum allowed number of outcomes in the cached set keep_issue_names: DEPRICATED. Use `outcome_type` instead. If True, dicts with issue names will be used for outcomes otherwise tuples annotation: Arbitrary annotation state_factory: A callable that receives an arbitrary set of key-value pairs and return a MechanismState descendant object checkpoint_every: The number of steps to checkpoint after. Set to <= 0 to disable checkpoint_folder: The folder to save checkpoints into. Set to None to disable checkpoint_filename: The base filename to use for checkpoints (multiple checkpoints will be prefixed with step number). single_checkpoint: If true, only the most recent checkpoint will be saved. extra_checkpoint_info: Any extra information to save with the checkpoint in the corresponding json file as a dictionary with string keys exist_ok: IF true, checkpoints override existing checkpoints with the same filename. name: Name of the mechanism session. Should be unique. If not given, it will be generated. outcome_type: The type used for representing outcomes. Can be tuple, dict or any `OutcomeType` """ super().__init__(name=name) CheckpointMixin.checkpoint_init( self, step_attrib="_step", every=checkpoint_every, folder=checkpoint_folder, filename=checkpoint_filename, info=extra_checkpoint_info, exist_ok=exist_ok, single=single_checkpoint, ) time_limit = time_limit if time_limit is not None else float("inf") step_time_limit = ( step_time_limit if step_time_limit is not None else float("inf") ) if keep_issue_names is not None: warnings.warn( "keep_issue_names is depricated. Use outcome_type instead.\n" "keep_issue_names=True <--> outcome_type=dict\n" "keep_issue_names=False <--> outcome_type=tuple\n", DeprecationWarning, ) outcome_type = dict if keep_issue_names else tuple keep_issue_names = not issubclass(outcome_type, tuple) # parameters fixed for all runs if issues is None: if outcomes is None: __issues = [] outcomes = [] else: if isinstance(outcomes, int): outcomes = [(_,) for _ in range(outcomes)] else: outcomes = list(outcomes) n_issues = len(outcomes[0]) issues = [] issue_names = ikeys(outcomes[0]) for issue in range(n_issues): vals = list(set([_[issue] for _ in outcomes])) issues.append(vals) __issues = [ Issue(_, name=name_) for _, name_ in zip(issues, issue_names) ] else: __issues = list(issues) if outcomes is None and cache_outcomes: try: if len(__issues) == 0: outcomes = [] else: n_outcomes = 1 for issue in __issues: if issue.is_uncountable(): break n_outcomes *= issue.cardinality if n_outcomes > max_n_outcomes: break else: outcomes = enumerate_outcomes(__issues, astype=outcome_type) except ValueError: pass elif outcomes is not None: issue_names = [_.name for _ in issues] assert (not keep_issue_names and isinstance(outcomes[0], tuple)) or ( keep_issue_names and not isinstance(outcomes[0], tuple) ), ( f"Either you request to keep issue" f" names but use tuple outcomes or " f"vice versa (names={keep_issue_names}" f", type={type(outcomes[0])})" ) if keep_issue_names: for i, outcome in enumerate(outcomes): assert list(outcome.keys()) == issue_names else: for i, outcome in enumerate(outcomes): assert len(outcome) == len(issue_names) self.__outcomes = outcomes # we have now __issues is a List[Issue] and __outcomes is Optional[List[Outcome]] # create a couple of ways to access outcomes by indices effeciently self.outcome_indices = [] self.__outcome_index = None if self.__outcomes is not None and cache_outcomes: self.outcome_indices = range(len(self.__outcomes)) self.__outcome_index = dict( zip( (outcome_as_tuple(o) for o in self.__outcomes), range(len(self.__outcomes)), ) ) self.id = str(uuid.uuid4()) self.ami = AgentMechanismInterface( id=self.id, n_outcomes=None if outcomes is None else len(outcomes), issues=__issues, outcomes=self.__outcomes, time_limit=time_limit, n_steps=n_steps, step_time_limit=step_time_limit, dynamic_entry=dynamic_entry, max_n_agents=max_n_agents, annotation=annotation, outcome_type=dict if keep_issue_names else tuple, ) self.ami._mechanism = self self._history = [] # if self.ami.issues is not None: # self.ami.issues = tuple(self.ami.issues) # if self.ami.outcomes is not None: # self.ami.outcomes = tuple(self.ami.outcomes) self._state_factory = state_factory self._requirements = {} self._negotiators = [] self._negotiator_map: Dict[str, "SAONegotiator"] = dict() self._roles = [] self._start_time = None self._started = False self._step = 0 self._n_accepting_agents = 0 self._broken = False self._agreement = None self._timedout = False self._running = False self._error = False self._error_details = "" self._waiting = False self.__discrete_outcomes: List[Outcome] = None self._enable_callbacks = enable_callbacks self.agents_of_role = defaultdict(list) self.role_of_agent = {}
def dissue(): return Issue(10, name="d")
def hamlet(): return Issue(["val {}".format(_) for _ in range(5)], name="THE problem")