Note
Go to the end to download the full example code
A small neuron-astrocyte network¶
Run this example as a Jupyter notebook:
See our guide for more information and troubleshooting.
This script shows how to create a neuron-astrocyte network in NEST. The network
in this script includes 20 neurons and five astrocytes. The astrocytes are modeled
with astrocyte_lr_1994
, implemented according to [1], [2], and [3]. The
neurons are modeled with aeif_cond_alpha_astro
, an adaptive exponential
integrate-and-fire neuron supporting neuron-astrocyte interactions.
The network is created with the TripartiteConnect()
function and the
tripartite_bernoulli_with_pool
rule (see Tripartite Bernoulli with pool for
detailed descriptions). This rule creates a tripartite Bernoulli connectivity
with the following principles:
1. For each pair of neurons, a Bernoulli trial with a probability p_primary
determines if a tsodyks_synapse
will be created between them.
2. For each neuron-neuron connection created, a Bernoulli trial with a
probability p_third_if_primary
determines if it will be paired with one astrocyte.
The selection of this particular astrocyte is confined by pool_size
and
pool_type
(see below).
3. If a neuron-neuron connection is to be paired with an astrocyte, a
tsodyks_synapse
from the presynaptic (source) neuron to the astrocyte
is created, and a sic_connection
from the astrocyte to the postsynaptic
(target) neuron is created.
The available connectivity parameters are as follows:
conn_spec
parametersp_primary
: Connection probability between neurons.p_third_if_primary
: Probability of each created neuron-neuron connection to be paired with one astrocyte.pool_size
: The size of astrocyte pool for each target neuron. The astrocyte pool of each target neuron is determined before making connections. Each target neuron can only be connected to astrocytes in its pool.pool_type
: The way to determine the astrocyte pool for each target neuron. If"random"
, a number (pool_size
) of astrocytes are randomly chosen from all astrocytes (without replacement) and assigned as the pool. If"block"
, the astrocytes are evenly distributed to the neurons in blocks without overlapping, and the specifiedpool_size
has to be compatible with this arrangement. See Tripartite Bernoulli with pool for more details aboutpool_type
.
syn_specs
parametersprimary
:syn_spec
specifications for the connections between neurons.third_in
:syn_spec
specifications for the connections from neurons to astrocytes.third_out
:syn_spec
specifications for the connections from astrocytes to neurons.
In this script, the network is created with the pool_type
being "block"
.
p_primary
and p_third_if_primary
are both set to one to include as many
connections as possible. One of the created figures shows the connections between
neurons and astrocytes as a result (note that multiple connections may exist
between a pair of nodes; this is not obvious in the figure since connections
drawn later cover previous ones). It can be seen from the figure that "block"
results in astrocytes being connected to postsynaptic neurons in non-overlapping
blocks. The pool_size
should be compatible with this arrangement; in the case
here, a pool_size
of one is required. Users can try different parameters
(e.g. p_primary
= 0.5 and p_third_if_primary
= 0.5) to see changes in
connections.
With the created network, neuron-astrocyte interactions can be observed. The
presynaptic spikes induce the generation of IP3, which then changes the calcium
concentration in the astrocytes. This change in calcium then induces the slow
inward current (SIC) in the neurons through the sic_connection
. The changes
in membrane potential of the presynaptic and postsynaptic neurons are also
recorded. These data are shown in the created figures.
The pool_type
can be changed to “random” to see the results with random
astrocyte pools. In that case, the pool_size
can be any from one to the
total number of astrocytes.
See Tripartite Bernoulli with pool for more details about the
TripartiteConnect()
function and the tripartite_bernoulli_with_pool
rule.
References¶
See Also¶
Random balanced network with astrocytes
Import all necessary modules.
import matplotlib.pyplot as plt
import nest
import numpy as np
Initialize NEST kernel.
nest.ResetKernel()
Set network parameters.
n_neurons = 10 # number of source and target neurons
n_astrocytes = 5 # number of astrocytes
p_primary = 1.0 # connection probability between neurons
p_third_if_primary = 1.0 # probability of each created neuron-neuron connection to be paired with one astrocyte
pool_size = 1 # astrocyte pool size for each target neuron
pool_type = "block" # the way to determine the astrocyte pool for each target neuron
Set astrocyte parameters.
astrocyte_model = "astrocyte_lr_1994"
astrocyte_params = {
"IP3": 0.4, # IP3 initial value in µM
"delta_IP3": 2.0, # parameter determining the increase in astrocytic IP3 concentration induced by synaptic input
"tau_IP3": 10.0, # time constant of the exponential decay of astrocytic IP3
}
Set neuron parameters.
neuron_model = "aeif_cond_alpha_astro"
neuron_params = {
"tau_syn_ex": 2.0, # excitatory synaptic time constant in ms
"I_e": 1000.0, # external current input in pA
}
Functions for plotting.
def plot_connections(conn_n2n, conn_n2a, conn_a2n, pre_id_list, post_id_list, astro_id_list):
"""Plot all connections between neurons and astrocytes.
Parameters
---------
conn_n2n
Data of neuron-to-neuron connections.
conn_n2a
Data of neuron-to-astrocyte connections.
conn_a2n
Data of astrocyte-to-neuron connections.
pre_id_list
ID list of presynaptic neurons.
post_id_list
ID list of postsynaptic neurons.
astro_id_list
ID list of astrocytes.
"""
print("Plotting connections ...")
# helper function to create lists of connection positions
def get_conn_positions(dict_in, source_center, target_center):
source_list = np.array(dict_in["source"]) - source_center
target_list = np.array(dict_in["target"]) - target_center
return source_list.tolist(), target_list.tolist()
# prepare data (lists of node positions, list of connection positions)
pre_id_center = (pre_id_list[0] + pre_id_list[-1]) / 2
post_id_center = (post_id_list[0] + post_id_list[-1]) / 2
astro_id_center = (astro_id_list[0] + astro_id_list[-1]) / 2
slist_n2n, tlist_n2n = get_conn_positions(conns_n2n.get(), pre_id_center, post_id_center)
slist_n2a, tlist_n2a = get_conn_positions(conns_n2a.get(), pre_id_center, astro_id_center)
slist_a2n, tlist_a2n = get_conn_positions(conns_a2n.get(), astro_id_center, post_id_center)
# initialize figure
fig, axs = plt.subplots(1, 1, figsize=(10, 8))
# plot nodes and connections
# source neuron nodes
axs.scatter(
np.array(pre_id_list) - pre_id_center,
[2] * len(pre_id_list),
s=300,
color="gray",
marker="^",
label="pre_neurons",
zorder=3,
)
# neuron-to-neuron connections
for i, (sx, tx) in enumerate(zip(slist_n2n, tlist_n2n)):
label = "neuron-to-neuron" if i == 0 else None
axs.plot([sx, tx], [2, 0], linestyle=":", color="b", alpha=0.3, linewidth=2, label=label)
# target neuron nodes
axs.scatter(
np.array(post_id_list) - post_id_center,
[0] * len(post_id_list),
s=300,
color="k",
marker="^",
label="post_neurons",
zorder=3,
)
# neuron-to-astrocyte connections
for i, (sx, tx) in enumerate(zip(slist_n2a, tlist_n2a)):
label = "neuron-to-astrocyte" if i == 0 else None
axs.plot([sx, tx], [2, 1], linestyle="-", color="orange", alpha=0.5, linewidth=2, label=label)
# astrocyte nodes
axs.scatter(
np.array(astro_id_list) - astro_id_center,
[1] * len(astro_id_list),
s=300,
color="g",
marker="o",
label="astrocytes",
zorder=3,
)
# astrocyte-to-neuron connections
for i, (sx, tx) in enumerate(zip(slist_a2n, tlist_a2n)):
label = "astrocyte-to-neuron" if i == 0 else None
axs.plot([sx, tx], [1, 0], linestyle="-", color="g", linewidth=4, label=label)
# set legends
legend = axs.legend(bbox_to_anchor=(0.5, 1.15), loc="upper center", ncol=3, labelspacing=1.5)
legend.set_frame_on(False)
# set axes and frames invisible
axs.get_xaxis().set_visible(False)
axs.get_yaxis().set_visible(False)
axs.spines["top"].set_visible(False)
axs.spines["bottom"].set_visible(False)
axs.spines["left"].set_visible(False)
axs.spines["right"].set_visible(False)
def get_plot_data(data_in, variable):
"""Helper function to get times, means, and standard deviations of data for plotting.
Parameters
---------
data_in
Data containing the variable to be plotted.
Variable
Variable to be plotted.
Return values
-------------
Times, means, and standard deviations of the variable to be plotted.
"""
times_all = data_in["times"]
ts = list(set(data_in["times"]))
means = np.array([np.mean(data_in[variable][times_all == t]) for t in ts])
sds = np.array([np.std(data_in[variable][times_all == t]) for t in ts])
return ts, means, sds
def plot_vm(pre_data, post_data, start):
"""Plot membrane potentials of presynaptic and postsynaptic neurons.
Parameters
---------
pre_data
Data of the presynaptic neurons.
post_data
Data of the postsynaptic neurons.
start
Start time of the data to be plotted.
"""
print("Plotting V_m ...")
# get presynaptic data
pre_times, pre_vm_mean, pre_vm_sd = get_plot_data(pre_data, "V_m")
# get postsynaptic data
post_times, post_vm_mean, post_vm_sd = get_plot_data(post_data, "V_m")
# set plots
fig, axes = plt.subplots(2, 1, sharex=True)
color_pre = color_post = "tab:blue"
# plot presynaptic membrane potential
axes[0].set_title(f"membrane potential of presynaptic neurons (n={len(set(pre_data['senders']))})")
axes[0].set_ylabel(r"$V_{m}$ (mV)")
axes[0].fill_between(
pre_times, pre_vm_mean + pre_vm_sd, pre_vm_mean - pre_vm_sd, alpha=0.3, linewidth=0.0, color=color_pre
)
axes[0].plot(pre_times, pre_vm_mean, linewidth=2, color=color_pre)
# plot postsynaptic membrane potential
axes[1].set_title(f"membrane potential of postsynaptic neurons (n={len(set(post_data['senders']))})")
axes[1].set_ylabel(r"$V_{m}$ (mV)")
axes[1].set_xlabel("Time (ms)")
axes[1].fill_between(
post_times, post_vm_mean + post_vm_sd, post_vm_mean - post_vm_sd, alpha=0.3, linewidth=0.0, color=color_post
)
axes[1].plot(post_times, post_vm_mean, linewidth=2, color=color_post)
def plot_dynamics(astro_data, neuron_data, start):
"""Plot dynamics in astrocytes and SIC in neurons.
Parameters
---------
astro_data
Data of the astrocytes.
neuron_data
Data of the neurons.
start
Start time of the data to be plotted.
"""
print("Plotting dynamics ...")
# get astrocyte data
astro_times, astro_ip3_mean, astro_ip3_sd = get_plot_data(astro_data, "IP3")
astro_times, astro_ca_mean, astro_ca_sd = get_plot_data(astro_data, "Ca_astro")
# get neuron data
neuron_times, neuron_sic_mean, neuron_sic_sd = get_plot_data(neuron_data, "I_SIC")
# set plots
fig, axes = plt.subplots(2, 1, sharex=True)
color_ip3 = "tab:blue"
color_cal = "tab:green"
color_sic = "tab:purple"
# plot astrocyte data
n_astro = len(set(astro_data["senders"]))
axes[0].set_title(f"IP$_{{3}}$ and Ca$^{{2+}}$ in astrocytes (n={n_astro})")
axes[0].set_ylabel(r"IP$_{3}$ ($\mu$M)")
axes[0].tick_params(axis="y", labelcolor=color_ip3)
axes[0].fill_between(
astro_times,
astro_ip3_mean + astro_ip3_sd,
astro_ip3_mean - astro_ip3_sd,
alpha=0.3,
linewidth=0.0,
color=color_ip3,
)
axes[0].plot(astro_times, astro_ip3_mean, linewidth=2, color=color_ip3)
ax = axes[0].twinx()
ax.set_ylabel(r"Ca$^{2+}$ ($\mu$M)")
ax.tick_params(axis="y", labelcolor=color_cal)
ax.fill_between(
astro_times, astro_ca_mean + astro_ca_sd, astro_ca_mean - astro_ca_sd, alpha=0.3, linewidth=0.0, color=color_cal
)
ax.plot(astro_times, astro_ca_mean, linewidth=2, color=color_cal)
# plot neuron data
n_neuron = len(set(neuron_data["senders"]))
axes[1].set_title(f"SIC in postsynaptic neurons (n={n_neuron})")
axes[1].set_ylabel("SIC (pA)")
axes[1].set_xlabel("Time (ms)")
axes[1].fill_between(
neuron_times,
neuron_sic_mean + neuron_sic_sd,
neuron_sic_mean - neuron_sic_sd,
alpha=0.3,
linewidth=0.0,
color=color_sic,
)
axes[1].plot(neuron_times, neuron_sic_mean, linewidth=2, color=color_sic)
Create and connect populations and devices. The neurons and astrocytes are connected with multimeters to record their dynamics.
pre_neurons = nest.Create(neuron_model, n_neurons, params=neuron_params)
post_neurons = nest.Create(neuron_model, n_neurons, params=neuron_params)
astrocytes = nest.Create(astrocyte_model, n_astrocytes, params=astrocyte_params)
nest.TripartiteConnect(
pre_neurons,
post_neurons,
astrocytes,
conn_spec={
"rule": "tripartite_bernoulli_with_pool",
"p_primary": p_primary,
"p_third_if_primary": p_third_if_primary,
"pool_size": pool_size,
"pool_type": pool_type,
},
syn_specs={
"primary": {"synapse_model": "tsodyks_synapse"},
"third_in": {"synapse_model": "tsodyks_synapse"},
"third_out": {"synapse_model": "sic_connection"},
},
)
mm_pre_neurons = nest.Create("multimeter", params={"record_from": ["V_m"]})
mm_post_neurons = nest.Create("multimeter", params={"record_from": ["V_m", "I_SIC"]})
mm_astrocytes = nest.Create("multimeter", params={"record_from": ["IP3", "Ca_astro"]})
nest.Connect(mm_pre_neurons, pre_neurons)
nest.Connect(mm_post_neurons, post_neurons)
nest.Connect(mm_astrocytes, astrocytes)
Get connection data. The data are used to plot the network connectivity.
conns_a2n = nest.GetConnections(astrocytes, post_neurons)
conns_n2n = nest.GetConnections(pre_neurons, post_neurons)
conns_n2a = nest.GetConnections(pre_neurons, astrocytes)
Run simulation.
nest.Simulate(1000.0)
plot_connections(conns_n2n, conns_n2a, conns_a2n, pre_neurons.tolist(), post_neurons.tolist(), astrocytes.tolist())
plot_vm(mm_pre_neurons.events, mm_post_neurons.events, 0.0)
plot_dynamics(mm_astrocytes.events, mm_post_neurons.events, 0.0)
plt.show()