Source code for negmas.outcomes.contiguous_issue

from __future__ import annotations
import math
import numbers
import random
from typing import Generator, Iterable

import numpy as np

from negmas.helpers.numeric import sample
from negmas.outcomes.base_issue import DiscreteIssue, Issue
from negmas.outcomes.range_issue import RangeIssue

__all__ = ["ContiguousIssue"]


[docs] class ContiguousIssue(RangeIssue, DiscreteIssue): """ A `RangeIssue` (also a `DiscreteIssue`) representing a contiguous range of integers. """ def __init__(self, values: int | tuple[int, int], name: str | None = None) -> None: vt: tuple[int, int] vt = values # type: ignore if isinstance(vt, numbers.Integral): vt = (0, int(vt) - 1) if isinstance(vt, Iterable): vt = tuple(vt) if len(vt) != 2: raise ValueError( f"{self.__class__.__name__} should receive one or two values for " f"the minimum and maximum limits but received {values=}" ) if not isinstance(vt[0], numbers.Integral) or not isinstance( vt[1], numbers.Integral ): raise ValueError( f"{self.__class__.__name__} should receive one or two integers for" f" the minimum and maximum limits but received {values=}" ) vt = tuple(vt) super().__init__(vt, name) self._n_values = vt[1] - vt[0] + 1 # type: ignore def _to_xml_str(self, indx): # return ( # f' <issue etype="integer" index="{indx + 1}" name="{self.name}" type="integer" vtype="integer"' # f' lowerbound="{self._values[0]}" upperbound="{self._values[1]}" />\n' # ) output = f' <issue etype="discrete" index="{indx + 1}" name="{self.name}" type="discrete" vtype="integer">\n' for i, v in enumerate(range(self._values[0], self._values[1] + 1)): output += f' <item index="{i + 1}" value="{v}" cost="0" description="{v}">\n </item>\n' output += " </issue>\n" return output @property def all(self) -> Generator[int, None, None]: yield from range(self._values[0], self._values[1] + 1) @property def cardinality(self) -> int: return self.max_value - self.min_value + 1
[docs] def ordered_value_generator( self, n: int | float | None = None, grid=True, compact=False, endpoints=True ) -> Generator[int, None, None]: m = self.cardinality n = m if n is None or not math.isfinite(n) else int(n) for i in range(n): yield self._values[0] + (i % m)
[docs] def value_generator( self, n: int | float | None = 10, grid=True, compact=False, endpoints=True ) -> Generator[int, None, None]: yield from ( _ + self._values[0] for _ in sample( self.cardinality, n, grid=grid, compact=compact, endpoints=endpoints ) )
[docs] def to_discrete( self, n: int | None, grid=True, compact=False, endpoints=True ) -> DiscreteIssue: if n is None or self.cardinality < n: return self if not compact: return super().to_discrete( n, grid=grid, compact=compact, endpoints=endpoints ) beg = (self.cardinality - n) // 2 return ContiguousIssue((int(beg), int(beg + n)), name=self.name + f"{n}")
[docs] def rand(self) -> int: """Picks a random valid value.""" return random.randint(*self._values)
[docs] def rand_outcomes( self, n: int, with_replacement=False, fail_if_not_enough=False ) -> list[int]: """Picks a random valid value.""" if n > self._n_values and not with_replacement: if fail_if_not_enough: raise ValueError( f"Cannot sample {n} outcomes out of {self._values} without replacement" ) return [_ for _ in range(self._values[0], self._values[1] + 1)] if with_replacement: return np.random.randint( low=self._values[0], high=self._values[1] + 1, size=n ).tolist() vals = [_ for _ in range(self._values[0], self._values[1] + 1)] random.shuffle(vals) return vals[:n]
[docs] def rand_invalid(self): """Pick a random *invalid* value""" return random.randint(self.max_value + 1, 2 * self.max_value)
[docs] def is_continuous(self) -> bool: return False
[docs] def value_at(self, index: int): if index < 0 or index > self.cardinality - 1: raise IndexError(index) return self.min_value + index
[docs] def contains(self, issue: Issue) -> bool: """Checks weather this issue contains the input issue (i.e. every value in the input issue is in this issue)""" return ( issubclass(issue.value_type, numbers.Integral) and issue.min_value >= self.min_value and issue.max_value <= self.max_value )