The notion of spreading activation is a prevalent metaphor in the
cognitive sciences; however, the tools to implement spreading activation
in a computational simulation are not as readily available. This
vignette introduces the spreadr R package (pronunced ‘SPREAD-er’), which
can implement spreading activation within a specified network structure.
The algorithmic method implemented in the spreadr
function
follows the approach described in Vitevitch,
Ercal, and Adagarla (2011), who viewed activation as a fixed
cognitive resource that could “spread” among connected nodes in a
network.
You can choose to install the stable version from CRAN, or the development build from GitHub.
If you encounter any bugs or issues, please try the development build first. The bug or issue may have already been fixed on GitHub, but not yet propagated onto CRAN.
In this example, we will simulate spreading activation in a small sample portion of a phonological network (Chan and Vitevitch 2009) which is automatically loaded with spreadr. This phonological network is unweighted and undirected, but spreadr supports weighted and directed graphs as well. This makes it is possible to simulate spreading activation in a weighted network where more activation is passed between nodes that have “stronger” edges, or in a directed (asymmetric) network where activation can pass from node i to node j but not necessarily from node j to node i.
You may substitute any network in place of this example one.
The network for spreading activation must be either an
igraph
object or an adjacency matrix.
pnet
, an igraph
object with named vertices
representing our sample phonological network is automatically loaded
with the spreadr library.
## This graph was created by an old(er) igraph version.
## ℹ Call `igraph::upgrade_graph()` on it to use with the current igraph version.
## For now we convert it on the fly...
Don’t worry if your plot does not look exactly as above. The layout of the nodes is determined stochastically.
pnetm
, an named adjacency matrix representing our sample
phonological network is automatically loaded with the spreadr
library.
library(spreadr)
library(igraph)
set.seed(1)
data("pnetm")
pnetm[1:5, 1:5] # inspect the first few entries
## spike speak spoke speck spook
## spike 0 1 1 1 1
## speak 1 0 1 1 1
## spoke 1 1 0 1 1
## speck 1 1 1 0 1
## spook 1 1 1 1 0
For those following along: don’t worry if your plot does not look exactly as above. The layout of the nodes is determined stochastically.
For simplicity, the rest of this example will use pnet
,
an igraph
representation of pnetm
. This
difference is trivial — the spreadr function accepts both igraph and
adjacency matrix.
A simulation of spreading activation is uninteresting without some activation to spread. In the simplest case, you may specify the initial activation state of each node, from which the simulation will proceed. Alternatively, you can also specify the addition of activation at any node, at any time point within the simulation.
Initial activation is specified by a data.frame
(or
data.frame
-like object, such as a tibble
) with
columns node
and activation
. Each row
represents the addition of activation
amount of activation
to the node with name node
, at the initial pre-spreading
activation step of the simulation.
For example, if we wanted the nodes "beach"
and
"speck"
to have 20
and 10
activation initially, respectively:
The adding of activation at specified time points is specified by a
data.frame
(or data.frame
-like object, such as
a tibble
) with columns node
,
activation
, and time
. Each row represents the
addition of activation
amount of activation to the node
with name node
at time point time
. The initial
state before any spreading activation occurs is
time = 0
.
Therefore, if we wanted the node "beach"
to have
20
activation initially, then "speck"
to have
10
activation at time = 5
:
For more details about the meaning of time
in the
spreading activation simulation, please read the spreadr
function documentation. To keep things simple, the rest of this example
will use the version of start_run
without the
time
column (see the leftmost tab).
We are now ready to run the simulation through the
spreadr
function. This function takes a number of
parameters, some of which are listed here. Required parameters are
written in bold, optional parameters are written with
their default values (in brackets):
igraph
object representing the network in which to simulate spreading
activationdata.frame
describing the addition of activation into the network.0
): Proportion of activation lost at
each time step (range from 0 to 1)0.5
): Proportion of activation
retained in the originator node (range from 0 to 1)0
): Nodes with activation values
lower than this value will have their activations forced to
0
(typically this will be a very small value e.g. <
0.001)10
): Number of iterations in the
simulationFALSE
): If the initial state
activations should be included in the result
data.frame
For more details on all the possible function parameters, please read
the spreadr
function documentation. For more details on the
spreading activation algorithm, see Siew
(2019) and Vitevitch, Ercal, and Adagarla
(2011).
The result is presented as a data.frame
with columns
node
, activation
, and time
. Each
row contains the activation value of a node at its specified time
step.
## node activation time
## 1 spike 0 0
## 2 speak 0 0
## 3 spoke 0 0
## 4 speck 10 0
## 5 spook 0 0
## 6 sped 0 0
## node activation time
## 369 patch 0.5139104 10
## 370 pooch 0.5139104 10
## 371 poach 0.5139104 10
## 372 preach 0.5493873 10
## 373 reach 2.0688303 10
## 374 perch 0.5139104 10
As a data.frame
, the result
can be easily
saved as a CSV file for sharing.
You can also easily visualise the result
using your
favourite graphical libraries. Here, we use ggplot2
:
Here, we address some common “how do I do X?” questions by example.
Simply pass a weighted network (igraph
object or
adjacency matrix) to the spreadr
function.
Let’s take a simple three-node network as example:
weighted_network <- matrix(
c(0, 1, 9,
1, 0, 0,
9, 0, 0), nrow=3, byrow=TRUE)
colnames(weighted_network) <- c("a", "b", "c")
rownames(weighted_network) <- c("a", "b", "c")
# To visualise the network only --- this is not necessary for spreadr
weighted_igraph <- graph_from_adjacency_matrix(
weighted_network, mode="undirected", weighted=TRUE)
plot(weighted_igraph, edge.width=E(weighted_igraph)$weight)
If we let the node "a"
have initial
activation = 10
, we should expect to see 9 units of
activation go to "c"
, and 1 unit to "b"
, for
retention = 0
.
spreadr(
weighted_network, data.frame(node="a", activation=10),
time=1, retention=0, include_t0=TRUE)
## node activation time
## 1 a 10 0
## 2 b 0 0
## 3 c 0 0
## 4 a 0 1
## 5 b 1 1
## 6 c 9 1
Simply pass a directed network (igraph
object or
adjacency matrix) to the spreadr
function.
Let’s take a simple three-node network as example:
directed_network <- matrix(
c(0, 1, 0,
0, 0, 1,
0, 0, 0), nrow=3, byrow=TRUE)
colnames(directed_network) <- c("a", "b", "c")
rownames(directed_network) <- c("a", "b", "c")
# To visualise the network only --- this is not necessary for spreadr
directed_igraph <- graph_from_adjacency_matrix(
directed_network, mode="directed")
plot(directed_igraph, edge.width=E(directed_igraph)$weight)
If we let the node "b"
have initial
activation = 10
, we should expect to see all 10 units of
activation go to "c"
, and none go to "a"
, for
retention = 0
.
spreadr(
directed_network, data.frame(node="b", activation=10),
time=1, retention=0, include_t0=TRUE)
## node activation time
## 1 a 0 0
## 2 b 10 0
## 3 c 0 0
## 4 a 0 1
## 5 b 0 1
## 6 c 10 1
There are a few ways to do this, but here we will showcase a
relatively simple method using data.frame
s.
Suppose we want to simulate spreading activation across four
different parameter sets of (retention, decay): (0, 0), (0.5,
0), (0, 0.5), and (0.5, 0.5). We would first record down all those
parameters which will differ into a data.frame
:
Then, we prepare the common parameters that will not differ.
network <- matrix(
c(0, 1,
0, 0), nrow=2, byrow=TRUE)
start_run <- data.frame(node=1, activation=10)
Now, to run the simulate once for each set of different parameters,
we simply apply
over the rows of params
.
apply(params, 1, function(row)
spreadr(
network, start_run,
time=2, include_t0=TRUE,
retention=row[1], decay=row[2]))
## [[1]]
## node activation time
## 1 1 10 0
## 2 2 0 0
## 3 1 0 1
## 4 2 10 1
## 5 1 0 2
## 6 2 10 2
##
## [[2]]
## node activation time
## 1 1 10.0 0
## 2 2 0.0 0
## 3 1 5.0 1
## 4 2 5.0 1
## 5 1 2.5 2
## 6 2 7.5 2
##
## [[3]]
## node activation time
## 1 1 10.0 0
## 2 2 0.0 0
## 3 1 0.0 1
## 4 2 5.0 1
## 5 1 0.0 2
## 6 2 2.5 2
##
## [[4]]
## node activation time
## 1 1 10.000 0
## 2 2 0.000 0
## 3 1 2.500 1
## 4 2 2.500 1
## 5 1 0.625 2
## 6 2 1.875 2
First, please check that you are on the development build. This is because the development build contains a more up-to-date version of spreadr, which may include various bug fixes, as compared to the stable CRAN build.
If your problems persist, please report them on our Github repository.
## R version 4.4.3 (2025-02-28)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.2 LTS
##
## Matrix products: default
## BLAS: /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so; LAPACK version 3.12.0
##
## locale:
## [1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
## [3] LC_TIME=en_US.UTF-8 LC_COLLATE=C
## [5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
## [7] LC_PAPER=en_US.UTF-8 LC_NAME=C
## [9] LC_ADDRESS=C LC_TELEPHONE=C
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
##
## time zone: Etc/UTC
## tzcode source: system (glibc)
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] ggplot2_3.5.1 igraph_2.1.4 spreadr_0.2.0 Rcpp_1.0.14
##
## loaded via a namespace (and not attached):
## [1] Matrix_1.7-2 gtable_0.3.6 jsonlite_1.9.1 dplyr_1.1.4
## [5] compiler_4.4.3 tidyselect_1.2.1 assertthat_0.2.1 jquerylib_0.1.4
## [9] scales_1.3.0 yaml_2.3.10 fastmap_1.2.0 lattice_0.22-6
## [13] R6_2.6.1 labeling_0.4.3 generics_0.1.3 knitr_1.49
## [17] tibble_3.2.1 maketools_1.3.2 munsell_0.5.1 bslib_0.9.0
## [21] pillar_1.10.1 rlang_1.1.5 cachem_1.1.0 xfun_0.51
## [25] sass_0.4.9 sys_3.4.3 cli_3.6.4 withr_3.0.2
## [29] magrittr_2.0.3 digest_0.6.37 grid_4.4.3 lifecycle_1.0.4
## [33] vctrs_0.6.5 evaluate_1.0.3 glue_1.8.0 farver_2.1.2
## [37] buildtools_1.0.0 colorspace_2.1-1 rmarkdown_2.29 tools_4.4.3
## [41] pkgconfig_2.0.3 htmltools_0.5.8.1