# Spreading Processes on Networks (SPoNet)
[![build](https://github.com/lueckem/SPoNet/actions/workflows/build.yml/badge.svg)](https://github.com/lueckem/SPoNet/actions/workflows/build.yml)
This package provides an efficient implementation of popular discrete-state spreading processes on networks of interacting *agents*.
They can be used to simulate how opinions about certain issues develop over time within a population, or how an infectious disease spreads.
The simulation loop is just-in-time compiled using `numba`, which makes performance comparable with compiled languages like C++.
Available models:
- continuous-time noisy voter model (CNVM)
- continuous-time noisy threshold model (CNTM)
## Installation
The package requires Python 3.9, 3.10, 3.11, or 3.12.
Install from the PyPI repository:
```
pip install sponet
```
or get the latest version directly from GitHub:
```
pip install git+https://github.com/lueckem/SPoNet
```
## About the CNVM
Let a network (undirected simple graph) of $N$ nodes be given. The nodes represent agents and the edges social interactions.
Each node is endowed with one of $M$ discrete opinions. Thus, the system state is given by a vector $x \in \{1,\dots,M\}^N$, where $x_i$ describes the opinion of node $i$.
Each node's opinion $x_i \in \{1,\dots,M\}$ changes over time according to a continuous-time Markov chain (Markov jump process).
Given the current system state $x$, the generator matrix $Q^i$ of the continuous-time Markov chain associated with node $i$ is defined as
$$ Q^i \in \mathbb{R}^{M \times M},\quad (Q^i)_ {m,n} := r_{m,n} \frac{d_ {i,n}(x)}{(d_i)^\alpha} + \tilde{r}_ {m,n},\ m\neq n, $$
where $d_{i,n}(x)$ denotes the number of neighbors of node $i$ with opinion $n$ and $d_i$ is the degree of node $i$. The matrices $r, \tilde{r} \in \mathbb{R}^{M \times M}$ and $\alpha \in \mathbb{R}$ are model parameters.
Thus, the transition rates $(Q^i)_ {m,n}$ consist of two components.
The first component $r_{m,n} d_{i,n}(x)/ (d_i)^\alpha$ describes at which rate node $i$ gets "infected" with opinion $n$ by nodes in its neighborhood.
The second part $\tilde{r}_{m,n}$ describes transitions that are independent from the neighborhood (e.g., noise).
The parameter $\alpha$ can be used to tune the type of interaction. For $\alpha=1$ the transition rates are normalized because $d_{i,n}(x)/d_i \in [0,1]$.
The setting $\alpha=0$ however yields a linear increase of the transition rates with the number of "infected" neighbors, and is often used in epidemic modeling, e.g., the contact process or SIS model.
The network itself is static, i.e., the nodes and edges do not change over time.
### Basic Usage
First define the model parameters:
```python
from sponet import CNVMParameters
import numpy as np
import networkx as nx
num_nodes = 100
r = np.array([[0, .8], [.2, 0]])
r_tilde = np.array([[0, .1], [.2, 0]])
network = nx.erdos_renyi_graph(n=num_nodes, p=0.1)
params = CNVMParameters(
num_opinions=2,
network=network,
r=r,
r_tilde=r_tilde,
alpha=1,
)
```
Then simulate the model, starting in state `x_init`:
```python
from sponet import CNVM
x_init = np.random.randint(0, 2, num_nodes)
model = CNVM(params)
t, x = model.simulate(t_max=50, x_init=x_init)
```
The output `t` contains the time points of state jumps and `x` the system states after each jump.
A more detailed overview of the package can be found in the jupyter notebook [*examples/tutorial_cnvm.ipynb*](examples/tutorial_cnvm.ipynb).
Moreover, the behavior of the CNVM in the mean-field limit is discussed in [*examples/mean_field.ipynb*](examples/mean_field.ipynb).
In the notebook [*examples/SIS-model.ipynb*](examples/SIS-model.ipynb) the existence of an epidemic threshold for the SIS model in epidemiology is demonstrated.
### Implementation details
After a node switches its opinion, the system state $x$ changes and hence all the generator matrices $Q^i$ may change as well.
We apply a Gillespie-like algorithm to generate statistically correct samples of the process.
We start a Poisson clock for each possible transition and as soon as the first transition occurs we modify the generator matrices and reset all the clocks.
To do this efficiently, it is advantageous to transform the rate matrices $r$ and $\tilde{r}$ into an equivalent format consisting of base rates $r_0, \tilde{r}_0 > 0$ and probability matrices $p, \tilde{p} \in [0, 1]^{M\times M}$ such that
$$ r_{m,n} = r_0 p_ {m,n}, \quad \tilde{r}_ {m,n} = \tilde{r}_ 0 \tilde{p}_ {m,n} / M. $$
Furthermore, we define the cumulative rates
$$ \lambda := \sum_{i=1}^N r_0 d_i^{(1-\alpha)},\quad \tilde{\lambda} := N \tilde{r}_0,\quad \hat{\lambda} := \lambda + \tilde{\lambda}. $$
Then the simulation loop is given by
1. Draw time of next jump event from exponential distribution $\exp(\hat{\lambda})$. Go to 2.
2. With probability $\lambda / \hat{\lambda}$ the event is due to infection, in which case go to 3.
Else it is due to noise, go to 4.
3. Draw agent $i$ from $\{1,\dots,N\}$ according to distribution $\mathbb{P}(i = j) = r_0 d_j^{(1-\alpha)} / \lambda$. Let $m$ denote the state of agent $i$.
Draw $n$ from $\{1,\dots,M\}$ according to $\mathbb{P}(n = k) = d_{i,k}(x) / d_i$.
With probability $p_{m,n}$ agent $i$ switches to state $n$. Go back to 1.
4. Draw $i$ from $\{1,\dots,N\}$ and $n$ from $\{1,\dots,M\}$ uniformly. Let $m$ denote the state of agent $i$.
With probability $\tilde{p}_{m,n}$ agent $i$ switches to state $n$. Go back to 1.
## About the CNTM
On a network (undirected simple graph) of $N$ nodes, each node $i$ has one of two opinions $x_i \in \{0, 1\}$.
At the rate $r \geq 0$, each node evaluates to change their opinion from its current
opinion $m\in \{0, 1\}$ to the other opinion $n=1-m$. It changes the opinion if the
percentage of neighbors of opinion n exceeds the threshold $b_{m,n}$.
Additionally, each node changes its state randomly at rate $\tilde{r} \geq 0$ (noise).
Hence, the rate at which node $i$ switches from opinion $m$ to opinion $n$ is
$$ r \ \delta_{\left( \frac{d_{i,n}(x)}{d_{i}} \geq b_{m,n} \right)} + \tilde{r} $$
where $d_{i,n}(x)$ denotes the number of neighbors of node $i$ with opinion $n$ and $d_i$ is the degree of node $i$.
Thus, in contrast to the CNVM, the CNTM assumes that a switch to a different opinion only occurs
if that opinion is already sufficiently established in the neighborhood.
### Basic Usage
First define the model parameters:
```python
from sponet import CNTMParameters
import numpy as np
import networkx as nx
num_nodes = 100
r = 1
r_tilde = 0.1
threshold_01 = 0.5
threshold_10 = 0.3
network = nx.erdos_renyi_graph(n=num_nodes, p=0.1)
params = CNTMParameters(
network=network,
r=r,
r_tilde=r_tilde,
threshold_01=threshold_01,
threshold_10=threshold_10,
)
```
Then simulate the model, starting in state `x_init`:
```python
from sponet import CNTM
x_init = np.random.randint(0, 2, num_nodes)
model = CNTM(params)
t, x = model.simulate(t_max=50, x_init=x_init)
```
The output `t` contains the time points of state jumps and `x` the system states after each jump.
Raw data
{
"_id": null,
"home_page": "https://github.com/lueckem/SPoNet",
"name": "sponet",
"maintainer": null,
"docs_url": null,
"requires_python": "<3.13,>=3.9",
"maintainer_email": null,
"keywords": "voter model, threshold model, social dynamics, opinion dynamics, statistical physics, agent-based model, epidemiology, interacting particle system",
"author": "Marvin L\u00fccke",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/bf/c6/b2ecae5ae23589c6cf261e84e1edfef5378d109e571685e8f7382b1cbd89/sponet-2.4.0.tar.gz",
"platform": null,
"description": "# Spreading Processes on Networks (SPoNet)\n\n[![build](https://github.com/lueckem/SPoNet/actions/workflows/build.yml/badge.svg)](https://github.com/lueckem/SPoNet/actions/workflows/build.yml)\n\nThis package provides an efficient implementation of popular discrete-state spreading processes on networks of interacting *agents*.\nThey can be used to simulate how opinions about certain issues develop over time within a population, or how an infectious disease spreads.\nThe simulation loop is just-in-time compiled using `numba`, which makes performance comparable with compiled languages like C++.\n\nAvailable models:\n- continuous-time noisy voter model (CNVM)\n- continuous-time noisy threshold model (CNTM)\n\n\n## Installation\nThe package requires Python 3.9, 3.10, 3.11, or 3.12.\nInstall from the PyPI repository:\n```\npip install sponet\n```\nor get the latest version directly from GitHub:\n```\npip install git+https://github.com/lueckem/SPoNet\n```\n\n## About the CNVM\nLet a network (undirected simple graph) of $N$ nodes be given. The nodes represent agents and the edges social interactions. \nEach node is endowed with one of $M$ discrete opinions. Thus, the system state is given by a vector $x \\in \\{1,\\dots,M\\}^N$, where $x_i$ describes the opinion of node $i$.\nEach node's opinion $x_i \\in \\{1,\\dots,M\\}$ changes over time according to a continuous-time Markov chain (Markov jump process).\nGiven the current system state $x$, the generator matrix $Q^i$ of the continuous-time Markov chain associated with node $i$ is defined as\n\n$$ Q^i \\in \\mathbb{R}^{M \\times M},\\quad (Q^i)_ {m,n} := r_{m,n} \\frac{d_ {i,n}(x)}{(d_i)^\\alpha} + \\tilde{r}_ {m,n},\\ m\\neq n, $$\n\nwhere $d_{i,n}(x)$ denotes the number of neighbors of node $i$ with opinion $n$ and $d_i$ is the degree of node $i$. The matrices $r, \\tilde{r} \\in \\mathbb{R}^{M \\times M}$ and $\\alpha \\in \\mathbb{R}$ are model parameters.\n\nThus, the transition rates $(Q^i)_ {m,n}$ consist of two components.\nThe first component $r_{m,n} d_{i,n}(x)/ (d_i)^\\alpha$ describes at which rate node $i$ gets \"infected\" with opinion $n$ by nodes in its neighborhood.\nThe second part $\\tilde{r}_{m,n}$ describes transitions that are independent from the neighborhood (e.g., noise).\n\nThe parameter $\\alpha$ can be used to tune the type of interaction. For $\\alpha=1$ the transition rates are normalized because $d_{i,n}(x)/d_i \\in [0,1]$.\nThe setting $\\alpha=0$ however yields a linear increase of the transition rates with the number of \"infected\" neighbors, and is often used in epidemic modeling, e.g., the contact process or SIS model.\n\nThe network itself is static, i.e., the nodes and edges do not change over time.\n\n### Basic Usage\nFirst define the model parameters:\n\n```python\nfrom sponet import CNVMParameters\nimport numpy as np\nimport networkx as nx\n\nnum_nodes = 100\nr = np.array([[0, .8], [.2, 0]])\nr_tilde = np.array([[0, .1], [.2, 0]])\nnetwork = nx.erdos_renyi_graph(n=num_nodes, p=0.1)\n\nparams = CNVMParameters(\n num_opinions=2,\n network=network,\n r=r,\n r_tilde=r_tilde,\n alpha=1,\n)\n```\nThen simulate the model, starting in state `x_init`:\n\n```python\nfrom sponet import CNVM\n\nx_init = np.random.randint(0, 2, num_nodes)\nmodel = CNVM(params)\nt, x = model.simulate(t_max=50, x_init=x_init)\n```\nThe output `t` contains the time points of state jumps and `x` the system states after each jump.\n\nA more detailed overview of the package can be found in the jupyter notebook [*examples/tutorial_cnvm.ipynb*](examples/tutorial_cnvm.ipynb).\nMoreover, the behavior of the CNVM in the mean-field limit is discussed in [*examples/mean_field.ipynb*](examples/mean_field.ipynb).\nIn the notebook [*examples/SIS-model.ipynb*](examples/SIS-model.ipynb) the existence of an epidemic threshold for the SIS model in epidemiology is demonstrated.\n\n### Implementation details\n\nAfter a node switches its opinion, the system state $x$ changes and hence all the generator matrices $Q^i$ may change as well.\nWe apply a Gillespie-like algorithm to generate statistically correct samples of the process.\nWe start a Poisson clock for each possible transition and as soon as the first transition occurs we modify the generator matrices and reset all the clocks.\nTo do this efficiently, it is advantageous to transform the rate matrices $r$ and $\\tilde{r}$ into an equivalent format consisting of base rates $r_0, \\tilde{r}_0 > 0$ and probability matrices $p, \\tilde{p} \\in [0, 1]^{M\\times M}$ such that\n\n$$ r_{m,n} = r_0 p_ {m,n}, \\quad \\tilde{r}_ {m,n} = \\tilde{r}_ 0 \\tilde{p}_ {m,n} / M. $$\n\nFurthermore, we define the cumulative rates\n\n$$ \\lambda := \\sum_{i=1}^N r_0 d_i^{(1-\\alpha)},\\quad \\tilde{\\lambda} := N \\tilde{r}_0,\\quad \\hat{\\lambda} := \\lambda + \\tilde{\\lambda}. $$\n\nThen the simulation loop is given by\n1. Draw time of next jump event from exponential distribution $\\exp(\\hat{\\lambda})$. Go to 2.\n2. With probability $\\lambda / \\hat{\\lambda}$ the event is due to infection, in which case go to 3.\nElse it is due to noise, go to 4.\n3. Draw agent $i$ from $\\{1,\\dots,N\\}$ according to distribution $\\mathbb{P}(i = j) = r_0 d_j^{(1-\\alpha)} / \\lambda$. Let $m$ denote the state of agent $i$.\nDraw $n$ from $\\{1,\\dots,M\\}$ according to $\\mathbb{P}(n = k) = d_{i,k}(x) / d_i$.\nWith probability $p_{m,n}$ agent $i$ switches to state $n$. Go back to 1.\n4. Draw $i$ from $\\{1,\\dots,N\\}$ and $n$ from $\\{1,\\dots,M\\}$ uniformly. Let $m$ denote the state of agent $i$.\nWith probability $\\tilde{p}_{m,n}$ agent $i$ switches to state $n$. Go back to 1.\n\n\n## About the CNTM\nOn a network (undirected simple graph) of $N$ nodes, each node $i$ has one of two opinions $x_i \\in \\{0, 1\\}$.\nAt the rate $r \\geq 0$, each node evaluates to change their opinion from its current\nopinion $m\\in \\{0, 1\\}$ to the other opinion $n=1-m$. It changes the opinion if the\npercentage of neighbors of opinion n exceeds the threshold $b_{m,n}$.\nAdditionally, each node changes its state randomly at rate $\\tilde{r} \\geq 0$ (noise).\nHence, the rate at which node $i$ switches from opinion $m$ to opinion $n$ is\n\n$$ r \\ \\delta_{\\left( \\frac{d_{i,n}(x)}{d_{i}} \\geq b_{m,n} \\right)} + \\tilde{r} $$\n\nwhere $d_{i,n}(x)$ denotes the number of neighbors of node $i$ with opinion $n$ and $d_i$ is the degree of node $i$.\nThus, in contrast to the CNVM, the CNTM assumes that a switch to a different opinion only occurs\nif that opinion is already sufficiently established in the neighborhood.\n\n### Basic Usage\nFirst define the model parameters:\n\n```python\nfrom sponet import CNTMParameters\nimport numpy as np\nimport networkx as nx\n\nnum_nodes = 100\nr = 1\nr_tilde = 0.1\nthreshold_01 = 0.5\nthreshold_10 = 0.3\nnetwork = nx.erdos_renyi_graph(n=num_nodes, p=0.1)\n\nparams = CNTMParameters(\n network=network,\n r=r,\n r_tilde=r_tilde,\n threshold_01=threshold_01,\n threshold_10=threshold_10,\n)\n```\nThen simulate the model, starting in state `x_init`:\n\n```python\nfrom sponet import CNTM\n\nx_init = np.random.randint(0, 2, num_nodes)\nmodel = CNTM(params)\nt, x = model.simulate(t_max=50, x_init=x_init)\n```\nThe output `t` contains the time points of state jumps and `x` the system states after each jump.\n",
"bugtrack_url": null,
"license": "GPL-3.0-or-later",
"summary": "Spreading Processes on Networks",
"version": "2.4.0",
"project_urls": {
"Homepage": "https://github.com/lueckem/SPoNet",
"Repository": "https://github.com/lueckem/SPoNet"
},
"split_keywords": [
"voter model",
" threshold model",
" social dynamics",
" opinion dynamics",
" statistical physics",
" agent-based model",
" epidemiology",
" interacting particle system"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "a13900930ee23d34b1ab945d91012a67cf07a70b5d13920ecdafd8ffcf1bc3a6",
"md5": "937b56df64566634f26366dd0228843d",
"sha256": "216373c7b1e419a473c29a3b5e2311b9427e504389a00e27c39553aeb906a484"
},
"downloads": -1,
"filename": "sponet-2.4.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "937b56df64566634f26366dd0228843d",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<3.13,>=3.9",
"size": 40636,
"upload_time": "2024-09-02T12:08:45",
"upload_time_iso_8601": "2024-09-02T12:08:45.333256Z",
"url": "https://files.pythonhosted.org/packages/a1/39/00930ee23d34b1ab945d91012a67cf07a70b5d13920ecdafd8ffcf1bc3a6/sponet-2.4.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "bfc6b2ecae5ae23589c6cf261e84e1edfef5378d109e571685e8f7382b1cbd89",
"md5": "7157cdcc085239558e481fd52e2d2d7e",
"sha256": "bf67e41935eaea3b221bb4a47fa3245c294d4deda38bbdbafdccff4ca52272fc"
},
"downloads": -1,
"filename": "sponet-2.4.0.tar.gz",
"has_sig": false,
"md5_digest": "7157cdcc085239558e481fd52e2d2d7e",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<3.13,>=3.9",
"size": 35636,
"upload_time": "2024-09-02T12:08:48",
"upload_time_iso_8601": "2024-09-02T12:08:48.306246Z",
"url": "https://files.pythonhosted.org/packages/bf/c6/b2ecae5ae23589c6cf261e84e1edfef5378d109e571685e8f7382b1cbd89/sponet-2.4.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-09-02 12:08:48",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "lueckem",
"github_project": "SPoNet",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "sponet"
}