Analysis of the multiorder Laplacian matrixยถ

Here we show how to analyze the multiorder Laplacian matrix defined for synchronization in higher-order networks.

Source:

Lucas M., Cencetti G., Battiston F., Multiorder Laplacian for synchronization in higher-order networks, Physical Review Research 2, 033410 (2020)

Definition

Multiorder Laplacians encode coupling at each interaction order for stability analysis.

You will learn

Analyze multiorder Laplacians and relate spectra to synchronization stability.

Overviewยถ

  • Analyze multiorder Laplacian spectra and synchronization properties.

  • Inspect how interaction order shapes stability.

Setupยถ

[ ]:
import matplotlib as mpl

mpl.rcParams.update({
    "figure.figsize": (6, 4),
    "figure.dpi": 120,
    "savefig.dpi": 150,
})

[1]:
import sys

import numpy as np
import matplotlib.pyplot as plt

sys.path.append("..")
from hypergraphx.core.hypergraph import Hypergraph
from hypergraphx.linalg.linalg import compute_multiorder_laplacian, laplacian_matrices_all_orders
from hypergraphx.dynamics.synch import higher_order_MSF

from scipy.linalg import eigh

from itertools import combinations

np.random.seed(123)
[2]:
# definition of the higher-order network
edge_list = [[0,7],[1,2],[1,7],[2,7],[3,7],[4,7],[5,7],[6,7],[1,2,7],[4,5,6],[4,5,7],[4,6,7],[5,6,7],[4,5,6,7]]
hypergraph = Hypergraph(edge_list)

N = hypergraph.num_nodes()

# definition of the coupling coefficients, i.e., the strength of each order of interaction
sigmas = [1,1,1]
[3]:
# We can compute the Laplacian matrix for the different orders of interaction with
laplacians = laplacian_matrices_all_orders(hypergraph)

# for instance, the Laplacian matrix of order 2, i.e., the Laplacian matrix for three-body interactions, is given by
print(laplacians[2].todense())

[[ 0.  0.  0.  0.  0.  0.  0.  0.]
 [ 0.  2. -1.  0.  0.  0.  0. -1.]
 [ 0. -1.  2.  0.  0.  0.  0. -1.]
 [ 0.  0.  0.  0.  0.  0.  0.  0.]
 [ 0.  0.  0.  0.  6. -2. -2. -2.]
 [ 0.  0.  0.  0. -2.  6. -2. -2.]
 [ 0.  0.  0.  0. -2. -2.  6. -2.]
 [ 0. -1. -1.  0. -2. -2. -2.  8.]]
[4]:
# We can compute the multiorder Laplacian with
multiorder_laplacian = compute_multiorder_laplacian(hypergraph,sigmas)

print(multiorder_laplacian.toarray().round(3))
[[ 0.5    0.     0.     0.     0.     0.     0.    -0.5  ]
 [ 0.     2.067 -1.033  0.     0.     0.     0.    -1.033]
 [ 0.    -1.033  2.067  0.     0.     0.     0.    -1.033]
 [ 0.     0.     0.     0.5    0.     0.     0.    -0.5  ]
 [ 0.     0.     0.     0.     9.7   -3.067 -3.067 -3.567]
 [ 0.     0.     0.     0.    -3.067  9.7   -3.067 -3.567]
 [ 0.     0.     0.     0.    -3.067 -3.067  9.7   -3.567]
 [-0.5   -1.033 -1.033 -0.5   -3.567 -3.567 -3.567 13.767]]
[5]:
# The stability of a system of Kuramoto oscillators depends on the spectrum of the multiorder Laplacian
spectrum = eigh(multiorder_laplacian.toarray(), eigvals_only=True)

print(spectrum)
[-9.36750677e-16  5.00000000e-01  6.05839850e-01  1.44725527e+00
  3.10000000e+00  1.27666667e+01  1.27666667e+01  1.68135715e+01]

Master Stability Functionยถ

Here we show how to evaluate the Master Stability Function for synchronization in higher-order networks.

Source:

Gambuzza L.V., Di Patti F., Gallo L., Lepri S., Romance M., Criado R., Frasca M., Latora V., Boccaletti S., Stability of synchronization in simplicial complexes, Nature Communications 12(1), 1-13 (2021)

We study synchronization of coupled dynamical systems with higher-order interactions. The model follows a standard multiorder coupling form; the equations below define the dynamics used to compute the master stability function and related spectra.

[6]:
# To evaluate the MSF we need to define the coupling function F, its Jacobian matrix JF,
# as well as the Jacobian matrix of the coupling function, JH.
# In this tutorial, we will consider a system of Rossler oscillators, coupled through cubic functions
def rossler_system(t, X, a, b, c):
    x,y,z = X

    dxdt = - y - z
    dydt = x + a*y
    dzdt = b + z*(x - c)

    return np.array([dxdt,dydt,dzdt])

def jacobian_rossler(X, a, b, c):
    x,y,z = X

    dFxdX = [0, -1, -1]
    dFydX = [1, a, 0]
    dFzdX = [z, 0, x - c]

    return np.array([dFxdX, dFydX, dFzdX])

def jacobian_coupling(X):
    x,y,z = X

    dHxdX = [3*(x**2), 0, 0]
    dHydX = [0, 0, 0]
    dHzdX = [0, 0, 0]

    return np.array([dHxdX, dHydX, dHzdX])
[ ]:
# Setting of the isolated system
dim = 3
F = rossler_system
JF = jacobian_rossler
params = (0.2, 0.2, 9)

# Setting of the coupling
sigmas = [0.005, 0.0005, 0.00005]
JHs = [jacobian_coupling, jacobian_coupling, jacobian_coupling]

# Initial condition of the system
X0 = np.random.random(size=(3,))

# interval of values on which the MSF is evaluated
interval = np.logspace(-4,0.5,100)

MSF, hon_MSF, spectrum = higher_order_MSF(hypergraph, dim, F, JF, params, sigmas, JHs, X0, interval, integration_time=8000, integration_step=10**-3, verbose=False)
[ ]:
# We plot the results
fig, ax = plt.subplots(figsize=(8, 6))

# MSF
ax.plot(interval, MSF,
        lw=2, c=plt.cm.Blues(0.8), zorder=1,
        label="MSF, $\Lambda(\\alpha)$")

# Lyapunov exponents associated to the eigenvalues of the Laplacian matrix
ax.scatter(spectrum[1:], hon_MSF,
        color=plt.cm.Oranges(0.8), zorder=10,
        label="$\Lambda(\lambda_i)$")

ax.hlines(y=0, xmin=10**-4, xmax=10,
          colors=plt.cm.Greys(0.9), ls="--")

ax.semilogx()
ax.set_xlim(10**-4,10**0.5)
ax.tick_params(axis='both', which='major', labelsize=13)

ax.set_xlabel("$\\alpha$", fontsize=15)
ax.set_ylabel("Master Stability Function, $\Lambda(\\alpha)$", fontsize=15)
ax.legend(fontsize=15, frameon=False, loc=4)

plt.show()
../_images/tutorials_synchronization_15_0.png
[ ]:
# We now want to evaluate the MSF for the all-to-all case
def rossler_system(t, X, a, b, c):
    x,y,z = X

    dxdt = - y - z
    dydt = x + a*y
    dzdt = b + z*(x - c)

    return np.array([dxdt,dydt,dzdt])

def jacobian_rossler(X, a, b, c):
    x,y,z = X

    dFxdX = [0, -1, -1]
    dFydX = [1, a, 0]
    dFzdX = [z, 0, x - c]

    return np.array([dFxdX, dFydX, dFzdX])

def jacobian_coupling_order1(X):
    x,y,z = X

    dHxdX = [1, 0, 0]
    dHydX = [0, 0, 0]
    dHzdX = [0, 0, 0]

    return np.array([dHxdX, dHydX, dHzdX])

def jacobian_coupling_order2(X):
    x,y,z = X

    dHxdX = [3 * (x**2), 0, 0]
    dHydX = [0, 0, 0]
    dHzdX = [0, 0, 0]

    return np.array([dHxdX, dHydX, dHzdX])
[ ]:
# We build the all-to-all hypergraph with hyperedges of size two and three
N = 5
edge_list = list(combinations(range(N), 2)) + list(combinations(range(N), 3))
hypergraph = Hypergraph(edge_list)
[ ]:
# Setting of the isolated system
dim = 3
F = rossler_system
JF = jacobian_rossler
params = (0.2, 0.2, 9)

# Setting of the coupling
sigmas = [0.005, 0.0005]
JHs = [jacobian_coupling_order1, jacobian_coupling_order2]

# Initial condition of the system
X0 = np.random.random(size=(3,))

# interval of values on which the MSF is evaluated
interval = np.logspace(-4,0.5,100)

MSF, hon_MSF, spectrum = higher_order_MSF(hypergraph, dim, F, JF, params, sigmas, JHs, X0, interval, integration_time=6000, integration_step=10**-3, verbose=False)
[ ]:
# We plot the results
fig, ax = plt.subplots(figsize=(8, 6))

# MSF
ax.plot(interval, MSF,
        lw=2, c=plt.cm.Blues(0.8), zorder=1,
        label="MSF, $\Lambda(\\alpha)$")

# Lyapunov exponents associated to the eigenvalues of the Laplacian matrix
ax.scatter(spectrum, hon_MSF,
        color=plt.cm.Oranges(0.8), zorder=10,
        label="$\Lambda(\lambda_i)$")

ax.hlines(y=0, xmin=10**-4, xmax=10,
          colors=plt.cm.Greys(0.9), ls="--")

ax.semilogx()
ax.set_xlim(10**-4,10**0.5)
ax.tick_params(axis='both', which='major', labelsize=13)

ax.set_xlabel("$\\alpha$", fontsize=15)
ax.set_ylabel("Master Stability Function, $\Lambda(\\alpha)$", fontsize=15)
ax.legend(fontsize=15, frameon=False, loc=3)

plt.show()
../_images/tutorials_synchronization_19_0.png