Skip to content

Random

Create a random generator from an ActorsList

Source code in abses/random.py
def __init__(self, model: MainModel[Any, Any], actors: ActorsList) -> None:
    self.model = model
    self.actors = actors
    self.seed = model.random.random() * 100
    self.generator = np.random.default_rng(seed=int(self.seed))

clean_p

clean_p(prob)

Clean the probabilities. Any negative values, NaN values, or zeros will be recognized as in-valid probabilities. For all valid probabilities, normalize them into a prob-array (the sum is equal to 1.0).

Parameters:

Name Type Description Default
prob Union[ndarray, str]

An array-like numbers of probabilities.

required

Returns:

Type Description
ndarray

The probabilities after cleaned.

Example:

>>> clean_p([0, 0])
>>> [0.5, 0.5]

>>> clean_p([-1, np.nan])
>>> [0.5, 0.5]

>>> clean_p([3, 2])
>>> [0.6, 0.4]

Source code in abses/random.py
def clean_p(self, prob: Union[np.ndarray, str]) -> np.ndarray:
    """Clean the probabilities.
    Any negative values, NaN values, or zeros will be recognized as in-valid probabilities.
    For all valid probabilities, normalize them into a prob-array (the sum is equal to 1.0).

    Parameters:
        prob:
            An array-like numbers of probabilities.

    Returns:
        The probabilities after cleaned.

    Example:
    ```
    >>> clean_p([0, 0])
    >>> [0.5, 0.5]

    >>> clean_p([-1, np.nan])
    >>> [0.5, 0.5]

    >>> clean_p([3, 2])
    >>> [0.6, 0.4]
    ```
    """
    if isinstance(prob, str):
        prob = self.actors.array(attr=prob)
    else:
        prob = np.array(make_list(prob))
    length = len(prob)
    prob = np.nan_to_num(prob)
    prob[prob < 0] = 0.0
    total = prob.sum()
    prob = prob / total if total else np.repeat(1 / length, length)
    return prob

choice

choice(size=1, prob=None, replace=False, as_list=False, when_empty='raise exception', double_check=False)

Randomly choose one or more actors from the current self object.

Parameters:

Name Type Description Default
size int

The number of actors to choose. Defaults to 1.

1
prob ndarray | None | str

A list of probabilities for each actor to be chosen. If None, all actors have equal probability. If is a string, will use the value of this attribute as the prob. Defaults to None.

None
replace bool

Whether to sample with replacement. Defaults to True.

False
as_list bool

Whether to return the result as a list of actors. Defaults to False.

False

Returns:

Type Description
Optional[Actor | ActorsList[Actor]]

An Actor or an ActorList of multiple actors.

Notes

Given the parameter set size=1 and as_list=False, a single Actor object is returned. Given the parameter set size>1 and as_list=False, a Self (ActorsList) object is returned.

Raises:

Type Description
ValueError

If size is not a positive integer.

ABSESpyError

Not enough actors to choose in this ActorsList.

Source code in abses/random.py
def choice(
    self,
    size: int = 1,
    prob: np.ndarray | None | str = None,
    replace: bool = False,
    as_list: bool = False,
    when_empty: WHEN_EMPTY = "raise exception",
    double_check: bool = False,
) -> Optional[Actor | ActorsList[Actor]]:
    """Randomly choose one or more actors from the current self object.

    Parameters:
        size:
            The number of actors to choose. Defaults to 1.
        prob:
            A list of probabilities for each actor to be chosen.
            If None, all actors have equal probability.
            If is a string, will use the value of this attribute as the prob.
            Defaults to None.
        replace:
            Whether to sample with replacement. Defaults to True.
        as_list:
            Whether to return the result as a list of actors. Defaults to False.

    Returns:
        An Actor or an ActorList of multiple actors.

    Notes:
        Given the parameter set size=1 and as_list=False, a single Actor object is returned.
        Given the parameter set size>1 and as_list=False, a Self (ActorsList) object is returned.

    Raises:
        ValueError:
            If size is not a positive integer.
        ABSESpyError:
            Not enough actors to choose in this `ActorsList`.
    """
    instances_num = len(self.actors)
    if instances_num == 0:
        self._when_empty(when_empty=when_empty)
        return None
    if not isinstance(size, int):
        raise ValueError(f"{size} isn't an integer size.")
    if instances_num < size and not replace:
        raise ABSESpyError(
            f"Trying to choose {size} actors from {self.actors}."
        )
    # 有概率的时候,先清理概率
    if prob is not None:
        prob = self.clean_p(prob=prob)
        valid_prob = prob.astype(bool)
        # 特别处理有概率的主体数量不足预期的情况
        if valid_prob.sum() < size and not replace:
            return self._when_p_not_enough(double_check, valid_prob, size)
    # 其他情况就正常随机选择
    chosen = self.generator.choice(
        self.actors, size=size, replace=replace, p=prob
    )
    return (
        chosen[0]
        if size == 1 and not as_list
        else self._to_actors_list(chosen)
    )

new

new(actor_cls, actor_attrs=None, **kwargs)

Randomly creating new agents for a given actor type.

Source code in abses/random.py
def new(
    self,
    actor_cls: Type[Actor],
    actor_attrs: Optional[Dict[str, Any]] = None,
    **kwargs,
) -> ActorsList[Actor]:
    """Randomly creating new agents for a given actor type."""
    if actor_attrs is None:
        actor_attrs = {}
    cells = self.choice(as_list=True, **kwargs)
    objs = cells.apply(
        lambda c: c.agents.new(
            breed_cls=actor_cls, singleton=True, **actor_attrs
        )
    )
    return self._to_actors_list(objs)
link(link, p=1.0, mutual=True)

Random build links between actors.

Parameters:

Name Type Description Default
link str

Name of the link.

required
p float

Probability to generate a link.

1.0

Returns:

Type Description
List[Tuple[Actor, Actor]]

A list of tuple, in each tuple, there are two actors who got linked.

Example
# generate three actors
actors = model.agents.new(Actor, 3)
# with `probability=1`, all possible actor-actor links would be generated.
>>> actors.random.link('test', p=1)
>>> a1, a2, a3 = actors
>>> assert a1.link.get('test) == [a2, a3]
>>> assert a2.link.get('test) == [a1, a3]
>>> assert a3.link.get('test) == [a1, a2]
Source code in abses/random.py
def link(
    self, link: str, p: float = 1.0, mutual: bool = True
) -> List[Tuple[Actor, Actor]]:
    """Random build links between actors.

    Parameters:
        link:
            Name of the link.
        p:
            Probability to generate a link.

    Returns:
        A list of tuple, in each tuple, there are two actors who got linked.

    Example:
        ```
        # generate three actors
        actors = model.agents.new(Actor, 3)
        # with `probability=1`, all possible actor-actor links would be generated.
        >>> actors.random.link('test', p=1)
        >>> a1, a2, a3 = actors
        >>> assert a1.link.get('test) == [a2, a3]
        >>> assert a2.link.get('test) == [a1, a3]
        >>> assert a3.link.get('test) == [a1, a2]
        ```
    """
    linked_combs = []
    for source, target in list(combinations(self.actors, 2)):
        if np.random.random() < p:
            source.link.to(target, link_name=link, mutual=mutual)
            linked_combs.append((source, target))
    return linked_combs

assign

assign(value, attr, when_empty='raise exception')

Randomly assign a value to each actor.

Source code in abses/random.py
def assign(
    self,
    value: float | int,
    attr: str,
    when_empty: WHEN_EMPTY = "raise exception",
) -> np.ndarray:
    """Randomly assign a value to each actor."""
    num = len(self.actors)
    if num == 0:
        self._when_empty(when_empty=when_empty, operation="assign")
        return np.array([])
    if num == 1:
        values = np.array([value])
    else:
        # 生成 n-1 个随机切割点
        cuts = np.sort(self.generator.uniform(0, value, num - 1))
        # 将 0 和总面积 X 添加到切割点数组中,方便计算每段区间长度
        full_range = np.append(np.append(0, cuts), value)
        # 计算每个区间的长度,即为每个对象的分配面积
        values = np.diff(full_range)
    # 将分配的值赋予每个对象
    self.actors.update(attr, values)
    return values