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