Skip to content

Binder

The Fitz-Hugh Nagumo oscillator

In this notebook, the basic use of the implementation of the FitzHugh-Nagumo (fhn) model is presented. Usually, the fhn model is used to represent a single neuron (for example in Cakan et al. (2014), "Heterogeneous delays in neural networks"). This is due to the difference in timescales of the two equations that define the FHN model: The first equation is often referred to as the "fast variable" whereas the second one is the "slow variable". This makes it possible to create a model with a very fast spiking mechanism but with a slow refractory period.

In our case, we are using a parameterization of the fhn model that is not quite as usual. Inspired by the paper by Kostova et al. (2004) "FitzHugh–Nagumo revisited: Types of bifurcations, periodical forcing and stability regions by a Lyapunov functional.", the implementation in neurolib produces a slowly oscillating dynamics and has the advantage to incorporate an external input term that causes a Hopf bifurcation. This means, that the model roughly approximates the behaviour of the aln model: For low input values, there is a low-activity fixed point, for intermediate inputs, there is an oscillatory region, and for high input values, the system is in a high-activity fixed point. Thus, it offers a simple way of exploring the dynamics of a neural mass model with these properties, such as the aln model.

We want to start by producing a bifurcation diagram of a single node. With neurolib, this can be done with a couple of lines of code, as seen further below.

# change to the root directory of the project
import os
if os.getcwd().split("/")[-1] == "examples":
    os.chdir('..')

# This will reload all imports as soon as the code changes
%load_ext autoreload
%autoreload 2    
try:
    import matplotlib.pyplot as plt
except ImportError:
    import sys
    !{sys.executable} -m pip install matplotlib
    import matplotlib.pyplot as plt

import numpy as np

# Let's import the fhn model
from neurolib.models.fhn import FHNModel

# Some useful functions are provided here
import neurolib.utils.functions as func

# a nice color map
plt.rcParams['image.cmap'] = 'plasma'

Single node simulation

model = FHNModel()
model.params['duration'] = 2.0*1000

Let's draw a simple one-dimensional bifurcation diagram of this model to orient ourselves in the parameter space

max_x = []
min_x = []
# these are the different input values that we want to scan
x_inputs = np.linspace(0, 2, 50)
for x_ext in x_inputs:
    # Note: this has to be a vector since it is input for all nodes
    # (but we have only one node in this example)
    model.params['x_ext'] = [x_ext]
    model.run()
    # we add the maximum and the minimum of the last second of the 
    # simulation to a list
    max_x.append(np.max(model.x[0, -int(1000/model.params['dt']):]))
    min_x.append(np.min(model.x[0, -int(1000/model.params['dt']):]))
plt.plot(x_inputs, max_x, c='k', lw = 2)
plt.plot(x_inputs, min_x, c='k', lw = 2)
plt.title("Bifurcation diagram of the FHN oscillator")
plt.xlabel("Input to x")
plt.ylabel("Min / max x")
Text(0, 0.5, 'Min / max x')

In this model, there is a Hopf bifurcation happening at two input values. We can see the oscillatory region at input values from roughly 0.75 to 1.3.

Brain network

from neurolib.utils.loadData import Dataset

ds = Dataset("hcp")
model = FHNModel(Cmat = ds.Cmat, Dmat = ds.Dmat)
model.params['duration'] = 10 * 1000 
# add some noise
model.params['sigma_ou'] = .01
# set the global coupling strenght of the brain network
model.params['K_gl'] = 1.0
# let's put all nodes close to the limit cycle such that
# noise can kick them in and out of the oscillation
# all nodes get the same constant input
model.params['x_ext'] = [0.72] * model.params['N']

model.run(chunkwise=True, append_outputs=True)
plt.plot(model.t, model.x[::5, :].T, alpha=0.8);
plt.xlabel("t [ms]")
Text(0.5, 0, 't [ms]')
fig, axs = plt.subplots(1, 2, figsize=(8, 2))
axs[0].imshow(func.fc(model.x[:, -10000:]))
axs[1].plot(model.t, model.x[::5, :].T, alpha=0.8);
scores = [func.matrix_correlation(func.fc(model.x[:, -int(5000/model.params['dt']):]), fcemp) for fcemp in ds.FCs]
print("Correlation per subject:", [f"{s:.2}" for s in scores])
print("Mean FC/FC correlation: {:.2f}".format(np.mean(scores)))
Correlation per subject: ['0.41', '0.5', '0.5', '0.48', '0.49', '0.45', '0.54']
Mean FC/FC correlation: 0.48