Skip to content

Barabási priority queues

The Barabási priority queue, whose waiting times follow a power law as the choice becomes greedy.

  • Name: priority_queues
  • Field: social/organizations
  • Formalism: agent-based model
  • References: Barabási 2005; Castellano, Fortunato, and Loreto 2009

Factory

@register_model(
    field='social/organizations',
    tags=('queues', 'agents', 'bursts', 'power law'),
    references=('Barabási 2005', 'Castellano, Fortunato, and Loreto 2009'),
    defaults={'until': 200},
)
def priority_queues(
    n_tasks: int = 2,
    *,
    greed: float = 0.99999,
    random_state: int | np.random.Generator | None = 0,
) -> System:
    """Return the Barabási priority queue, whose waiting times follow a power law as the choice becomes greedy.

    Args:
        n_tasks:
            The length of the list of tasks.

        greed:
            The probability that the highest priority task is executed rather than a random one.

        random_state:
            The seed or generator of the initial priorities.

    Returns:
        system:
            The model, with the group `tasks` and the execution `execution`, which reports the waiting time of the
            task it executes at every step.

    Examples:
        >>> from skcomplex.models.social.organizations import priority_queues
        >>> priority_queues(3, random_state=0).elements[0].check().prepare().size_
        3
    """
    priorities = np.random.default_rng(random_state).random(n_tasks)
    tasks = GroupElement('tasks', element=TaskElement, size=n_tasks, values={'priority': priorities, 'greed': greed})
    return System(elements=[tasks], interactions=[ExecutionInteraction('execution', target='tasks')])

Components

class TaskElement(Element):
    """Task of the Barabási priority queue, with a priority, an age, and the waiting time when it was last executed.

    Args:
        label:
            The label of the agent or group.

        visual:
            How the agent is drawn, or `None` for `default_visual_`.

        priority:
            The priority, uniform on 0 to 1 for a new task.

        age:
            The number of steps the task has waited.

        wait:
            The waiting time of the last task executed from this slot of the list.

        greed:
            The probability that the highest priority task is executed rather than a random one.

    Examples:
        >>> from skcomplex.core.elements import GroupElement
        >>> from skcomplex.models.social.organizations import TaskElement
        >>> group = GroupElement('tasks', element=TaskElement, size=2, values={'priority': [0.2, 0.7]})
        >>> group.label, group.element.__name__, group.size
        ('tasks', 'TaskElement', 2)
    """

    def __init__(
        self,
        label: str,
        visual: VisualSpec | None = VisualSpec('histograms', {'color_by': 'age', 'shown': ('agents',)}),
        *,
        priority: Annotated[float, StateSpec(domain=RealSet.closed_open(0, oo))] = 0.5,
        age: Annotated[float, StateSpec(domain=NN)] = 0.0,
        wait: Annotated[float, StateSpec(domain=NN)] = 0.0,
        greed: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.99999,
    ) -> None:
        super().__init__(label, visual=visual)
        self.priority = priority
        self.age = age
        self.wait = wait
        self.greed = greed

    @classmethod
    def _get_view(cls) -> type[TaskView]:
        """Return the class of the views of the agents, whose attributes are the states and parameters."""
        return TaskView

class ExecutionInteraction(Interaction):
    """Execution of one task per step from the list of the Barabási model.

    The highest priority task is executed with probability `greed` and a random task otherwise. The executed task is
    replaced by a new task with a uniform random priority, and every other task ages by one step. The waiting time of
    the executed task is reported as `wait`, so the run records one waiting time per step.

    Args:
        label:
            The label of the interaction.

        target:
            The path of the group of tasks.

    Examples:
        >>> from skcomplex.models.social.organizations import ExecutionInteraction
        >>> ExecutionInteraction('execution', target='tasks').target
        'tasks'
    """

    reports: ClassVar[tuple[str, ...]] = ('wait',)

    def __init__(
        self,
        label: str,
        *,
        target: Annotated[str, RoleSpec()],
    ) -> None:
        super().__init__(label)
        self.target = target

    def _get_laws(self, s: SimpleNamespace) -> list[BaseLaw]:
        """Return the execution rule.

        Args:
            s:
                The namespace of the symbols the laws can use.

        Returns:
            laws:
                The laws of the component.
        """
        return [RuleLaw(self.execute)]

    def execute(self, model: ModelView) -> None:
        """Execute one task and replace it with a new one.

        Args:
            model:
                The model.
        """
        tasks = model.agents(self.target, TaskView)
        if not tasks:
            return
        priorities = [task.priority for task in tasks]
        greedy = model.rng.random() < tasks[0].greed
        chosen = int(np.argmax(priorities)) if greedy else int(model.rng.integers(len(tasks)))
        for index, task in enumerate(tasks):
            task.age += 1
            if index == chosen:
                task.wait, task.age, task.priority = task.age, 0.0, float(model.rng.random())
                model.report('wait', task.wait)