Skip to content

Deffuant bounded confidence

The Deffuant bounded confidence model, whose opinions settle into about 1 / (2 * confidence) clusters.

  • Name: deffuant
  • Field: social/opinion_dynamics
  • Formalism: agent-based model
  • References: Deffuant et al. 2000; Castellano, Fortunato, and Loreto 2009; Gros 2015

Factory

@register_model(
    field='social/opinion_dynamics',
    tags=('opinion', 'agents', 'bounded confidence', 'clusters'),
    references=('Deffuant et al. 2000', 'Castellano, Fortunato, and Loreto 2009', 'Gros 2015'),
    defaults={'until': 50},
)
def deffuant(
    n_agents: int = 200,
    *,
    confidence: float = 0.2,
    convergence: float = 0.5,
    random_state: int | np.random.Generator | None = 0,
) -> System:
    """Return the Deffuant bounded confidence model, whose opinions settle into about `1 / (2 * confidence)` clusters.

    Args:
        n_agents:
            The number of agents.

        confidence:
            The largest opinion difference at which two agents compromise.

        convergence:
            The fraction of the difference by which each agent of a compromising pair moves.

        random_state:
            The seed or generator of the uniform initial opinions.

    Returns:
        system:
            The model, with the group `people`.

    Examples:
        >>> from skcomplex.models.social.opinion_dynamics import deffuant
        >>> deffuant(20, random_state=0).elements[0].check().prepare().size_
        20
    """
    opinions = np.random.default_rng(random_state).random(n_agents)
    visual = VisualSpec('histograms', {'color_by': 'opinion', 'color_range': (0.0, 1.0), 'histogram_first': True})
    people = GroupElement(
        'people',
        element=DiscussantElement,
        size=n_agents,
        values={'opinion': opinions, 'confidence': confidence, 'convergence': convergence},
        visual=visual,
    )
    return System(elements=[people], interactions=[EncountersInteraction('meetings', target='people')])

Components

class DiscussantElement(Element):
    """Agent of the Deffuant model, with a continuous opinion and a confidence bound.

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

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

        opinion:
            The initial opinion, between 0 and 1.

        confidence:
            The largest opinion difference at which two agents still compromise.

        convergence:
            The fraction of the opinion difference by which each agent of a compromising pair moves.

    Examples:
        >>> from skcomplex.core.elements import GroupElement
        >>> from skcomplex.models.social.opinion_dynamics import DiscussantElement
        >>> group = GroupElement('people', element=DiscussantElement, size=10, values={'opinion': 0.5})
        >>> group.label, group.element.__name__, group.size
        ('people', 'DiscussantElement', 10)
    """

    def __init__(
        self,
        label: str,
        visual: VisualSpec | None = VisualSpec('agents', {'color_by': 'opinion', 'color_range': (0.0, 1.0)}),
        *,
        opinion: Annotated[float, StateSpec(domain=RealSet.closed_open(0, oo))] = 0.5,
        confidence: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.2,
        convergence: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.5,
    ) -> None:
        super().__init__(label, visual=visual)
        self.opinion = opinion
        self.confidence = confidence
        self.convergence = convergence

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

class EncountersInteraction(RandomPairsInteraction):
    """Random binary encounters of the Deffuant model, as many per step as there are agents.

    In every encounter two random agents whose opinions differ by less than the confidence bound move towards each
    other by the convergence fraction of their difference.

    Args:
        label:
            The label of the interaction.

        target:
            The path of the group of discussants.

        phase:
            The phase of the step the encounters run in, or `None` for the default phase.

    Examples:
        >>> from skcomplex.models.social.opinion_dynamics import EncountersInteraction
        >>> EncountersInteraction('meetings', target='people').target
        'people'
    """

    def act_on(self, members: list[AgentViewProtocol], model: ModelViewProtocol) -> None:
        """Move the opinions of a pair towards each other when they differ by less than the confidence bound.

        Args:
            members:
                The two discussants of the encounter.

            model:
                The model view of the rule.
        """
        first, second = [member for member in members if isinstance(member, DiscussantView)]
        difference = second.opinion - first.opinion
        if abs(difference) < first.confidence:
            first.opinion += first.convergence * difference
            second.opinion -= first.convergence * difference