Skip to content

Maki-Thompson rumours

The Maki-Thompson rumour model started by one spreader, which leaves about 20% of people ignorant.

  • Name: maki_thompson
  • Field: social/spreading
  • Formalism: jump process
  • References: Maki and Thompson 1973; Pastor-Satorras et al. 2015

Factory

@register_model(
    field='social/spreading',
    tags=('rumours', 'jump process', 'stochastic'),
    references=('Maki and Thompson 1973', 'Pastor-Satorras et al. 2015'),
    defaults={'until': 20.0, 'dt': 0.5},
)
def maki_thompson(n_people: int = 1000, *, spread: float = 1.0, stifle: float = 1.0) -> System:
    """Return the Maki-Thompson rumour model started by one spreader, which leaves about 20% of people ignorant.

    Args:
        n_people:
            The size of the population.

        spread:
            The contact rate that spreads the rumour.

        stifle:
            The contact rate that stifles a spreader.

    Returns:
        system:
            The model, with the counts `ignorants.n`, `spreaders.n`, and `stiflers.n`.

    Examples:
        >>> from skcomplex.models.social.spreading import maki_thompson
        >>> [element.n for element in maki_thompson(100).elements]
        [99, 1, 0]
    """
    return System(
        elements=[
            ListenersElement('ignorants', n=n_people - 1),
            ListenersElement('spreaders', n=1),
            ListenersElement('stiflers', n=0),
        ],
        interactions=[
            RumourContactsInteraction(
                'contacts',
                ignorants='ignorants',
                spreaders='spreaders',
                stiflers='stiflers',
                spread=spread,
                stifle=stifle,
            ),
        ],
    )

Components

class ListenersElement(Element):
    """Number of people in one state of a rumour: ignorant, spreading, or stifling.

    Args:
        label:
            The label of the element.

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

        n:
            The initial number of people.

    Examples:
        >>> from skcomplex.models.social.spreading import ListenersElement
        >>> ListenersElement('spreaders', n=1).n
        1
    """

    def __init__(
        self,
        label: str,
        visual: VisualSpec | None = None,
        *,
        n: Annotated[float, StateSpec(domain=NN)] = 0,
    ) -> None:
        super().__init__(label, visual=visual)
        self.n = n

class RumourContactsInteraction(Interaction):
    """Contacts of the Maki-Thompson rumour model in a well-mixed population of size `N`.

    A spreader who meets an ignorant tells the rumour at rate `spread * X * Y / N`, and a spreader who meets another
    spreader or a stifler stops spreading at rate `stifle * Y * (Y - 1 + Z) / N`.

    Args:
        label:
            The label of the interaction.

        ignorants:
            The path of the ignorants `X`.

        spreaders:
            The path of the spreaders `Y`.

        stiflers:
            The path of the stiflers `Z`.

        spread:
            The contact rate that spreads the rumour.

        stifle:
            The contact rate that stifles a spreader.

    Examples:
        >>> from skcomplex.models.social.spreading import RumourContactsInteraction
        >>> RumourContactsInteraction('contacts', ignorants='X', spreaders='Y', stiflers='Z').spread
        1.0
    """

    def __init__(
        self,
        label: str,
        *,
        ignorants: Annotated[str, RoleSpec()],
        spreaders: Annotated[str, RoleSpec()],
        stiflers: Annotated[str, RoleSpec()],
        spread: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
        stifle: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 1.0,
    ) -> None:
        super().__init__(label)
        self.ignorants = ignorants
        self.spreaders = spreaders
        self.stiflers = stiflers
        self.spread = spread
        self.stifle = stifle

    def _get_laws(self, s: SimpleNamespace) -> list[BaseLaw]:
        """Return the telling and stifling jumps.

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

        Returns:
            laws:
                The laws of the component.
        """
        ignorants, spreaders, stiflers = s.ignorants.n, s.spreaders.n, s.stiflers.n
        total = ignorants + spreaders + stiflers
        return [
            JumpLaw(s.spread * ignorants * spreaders / total, {ignorants: -1, spreaders: 1}),
            JumpLaw(s.stifle * spreaders * (spreaders - 1 + stiflers) / total, {spreaders: -1, stiflers: 1}),
        ]