Hegselmann-Krause

The HK model [1] describes continuous opinion dynamics. Each node holds an initial opinion \(h \in [-1,1]\) and updates it in discrete rounds. At each step: 1) \(\epsilon\) neighbor identification: find the set \(\Gamma_\epsilon\) of each node, \(d_{i,j}=\left|h_i-h_j\right|\le\epsilon\), \(j\in\Gamma_\epsilon\); 2) update the opinion value: The opinion of node \(i\) at the next time step is updated as follows:

\[h_i^{\left(k\right)}=\frac{\sum_{j\in\Gamma_\epsilon}{h_j^{\left(k-1\right)}}}{\#\Gamma_\epsilon}\]

where \(\#\Gamma_\epsilon\) denotes the number of \(\epsilon\) neighbors.

Implementation

Unlike the SIR model with three discrete states, the HK model uses a continuous opinion space. To avoid infinitely many states, we consider only two cases (Figure ref{fig:HK}): if $#Gamma_epsilon=0$, the opinion remains unchanged; otherwise, it is updated to the $epsilon$-neighbors’ average.

HK model diagram
  1. For each neighbor \(j \in N(i)\), generate two message terms:

\[\begin{split}\begin{aligned} m_{ji\_c}^{(k)} &=\mathbf{1}\!\left( |h_i^{(k-1)} - h_j^{(k-1)}| < \varepsilon \right) \cdot h_j^{(k-1)}, \\[6pt] m_{ji\_v}^{(k)} &= \mathbf{1}\!\left( |h_i^{(k-1)} - h_j^{(k-1)}| < \varepsilon \right). \end{aligned}\end{split}\]

where \(\mathbf{1}(\cdot)\) is the indicator function.

  1. Node \(i\) aggregates received messages by averaging opinions of \(\varepsilon\)-neighbors:

\[\begin{split}\begin{aligned} m_i^{(k)} &= \begin{cases} \dfrac{\sum\limits_{j \in N(i)} m_{ji\_c}^{(k)}}{\sum\limits_{j \in N(i)} m_{ji\_v}^{(k)}}, & \text{if } \sum\limits_{j \in N(i)} m_{ji\_v}^{(k)} > 0, \\[8pt] \mathrm{NaN}, & \text{otherwise}. \end{cases} \\[10pt] \end{aligned}\end{split}\]
  1. Finally, the opinion of node \(i\) is updated as

\[\begin{split} \begin{aligned} h_i^{(k)} &= \begin{cases} m_i^{(k)}, & \text{if } m_i^{(k)} \neq \mathrm{NaN}, \\[6pt] h_i^{(k-1)}, & \text{otherwise}. \end{cases} \end{aligned}\end{split}\]

Status

During the simulation, a node holds a continuous opinion value:

Status

Range

Opinion

Float in [-1,1]

HKModel

class fs_gplib.Opinions.HKModel.HKModel(data, seeds, epsilon, device='cpu', rand_seed=None)[source]

Bases: DiffusionModel

Hegselmann-Krause (HK) bounded-confidence opinion dynamics on static graphs.

Each node carries a continuous opinion \(h \in (-1, 1)\) (initialised from seeds or drawn uniformly at random when seeds is None). At each step, for every node \(i\) the set of confidence neighbors consists of graph neighbors \(j\) with \(|h_i^{(k-1)} - h_j^{(k-1)}| < \varepsilon\). If that set is non-empty, \(h_i^{(k)}\) becomes the average of their opinions; if it is empty, \(h_i\) stays unchanged.

Self-loops are removed from the edge index.

Parameters:
  • data (torch_geometric.data.Data) -- PyTorch Geometric Data representing \(G=(V,E)\). Must provide edge_index and num_nodes.

  • seeds (list[float] | None) -- Initial opinion per node, length num_nodes, each strictly between -1 and 1; or None to sample each component independently from \(\mathrm{Uniform}(-1, 1)\) (using rand_seed for the RNG).

  • epsilon (float) -- Confidence bound \(\varepsilon\) in [0, 1]; only neighbors with opinion separation below this threshold contribute to the update.

  • device (str | int) -- (optional) 'cpu' or a CUDA device index. Defaults to 'cpu'.

  • rand_seed (int | None) -- (optional) Seed for the random number generator when seeds is None. Defaults to None.

run_iteration()[source]

Execute a single opinion-update step.

The internal node_status is updated so that subsequent calls continue from the latest opinion configuration.

Returns:

Node opinions after one step, shape (1, N).

Return type:

torch.Tensor

run_iterations(times)[source]

Execute times opinion-update steps sequentially.

The internal node_status is updated in-place so that subsequent calls continue from the latest opinion configuration.

Parameters:

times (int) -- Number of steps to run.

Returns:

Node opinions at final step, shape (1, N).

Return type:

torch.Tensor

run_epoch(iterations_times)[source]

Run a single Monte-Carlo epoch (one independent realisation).

Node opinions are re-initialised before the epoch starts.

Parameters:

iterations_times (int) -- Number of opinion-update steps per epoch.

Returns:

Node opinions at final step of the epoch, shape (1, N).

Return type:

torch.Tensor

run_epochs(epochs, iterations_times, batch_size=200)[source]

Run multiple independent Monte-Carlo epochs in batches.

Node opinions are re-initialised before the run.

Parameters:
  • epochs (int) -- Total number of independent realisations.

  • iterations_times (int) -- Number of opinion-update steps per epoch.

  • batch_size (int) -- (optional) Number of epochs processed in parallel per batch. Defaults to 200.

Returns:

Node opinions at final step of all epochs, shape (epochs, N).

Return type:

torch.Tensor

References