Hello,
I am new to differential equations and I am trying to implement as simple differential equation model describe here. The basic model is described in equation 4. I am able to reproduce the expected behavior of case 3 in Figure 1 when using the parameters. r = .20, δ = 0.00, α = .10, β = 0.20
. However, I am not able to match the behavior when using different parameters that satisfy case 3, as shown in the Julia code. I expected the curves to have the same qualitative properties.
Julia Code
using ComponentArrays
import DifferentialEquations as DE
using Plots
using Test
function ode_model!(
du::AbstractVector{<:R},
u::AbstractVector{<:R},
θ::ComponentArray{<:R},
_
) where {R <: Real}
(; δ, r, α, β) = θ
p1 = (α * (1 - r) * (2 * u[1] - 1) - β * r * (2 * u[2] - 1)) > δ
p2 = (α * (1 - r) * (2 * u[1] - 1) - β * r * (2 * u[2] - 1)) < -δ
p3 = (α * (2 * u[2] - 1) - β * (1 - r) * (2 * u[1] - 1)) > δ
p4 = (α * (2 * u[2] - 1) - β * (1 - r) * (2 * u[1] - 1)) < -δ
# change in behavior for blue node
du[1] = (1 - u[1]) * p1 - u[1] * p2
# change in behavior for red node
du[2] = (1 - u[2]) * p3 - u[2] * p4
return nothing
end
r = .37
δ = 0.00
α = .74
β = 0.92
# ensure parameters satisfy conditions for case 3
@test (r / (1 - r)) < (β / α)
@test (r / (1 - r)) < (α / β)
θ = ComponentArray(; r, δ, α, β)
u0 = [0.80, 0.80]
tspan = (0.0, 10.0)
prob = DE.ODEProblem(ode_model!, u0, tspan, θ)
sol = DE.solve(prob)
plot(sol; idxs = 1, ylims = (0, 1))
plot!(sol; idxs = 2, linestyle = :dash)
My Julia example does not match the results of the code Python associated with the paper. I extracted the necessary components below, and disabled the parameter rho because its not relevant for the basic model.
Python Code
def Simulate_AP_model_SBM_Graph(Initial_H1_prob_blue, Initial_H1_prob_red, Alpha_b, Beta_b, Rho_b, Intertia_b, Alpha_r, Beta_r, Rho_r, Intertia_r, R, n = 5000000, T = 10, delta = 0.001):
theta_t_blue = [Initial_H1_prob_blue]
theta_t_red = [Initial_H1_prob_red]
T = 10
time = np.arange(0,T,delta)
for t in time[:-1]:
(theta_t_blue_dot, theta_t_red_dot) = AP_model_SBM_Graph(theta_t_blue[-1],theta_t_red[-1], t, Alpha_b, Beta_b, Rho_b, Inertia_b, Alpha_r, Beta_r, Rho_r, Inertia_r, R, n)
theta_t_blue.append(theta_t_blue[-1] + delta*theta_t_blue_dot)
theta_t_red.append(theta_t_red[-1] + delta*theta_t_red_dot)
return time, theta_t_blue, theta_t_red
def AP_model_SBM_Graph(theta_t_b,theta_t_r, t, Alpha_b, Beta_b, Rho_b, Inertia_b, Alpha_r, Beta_r, Rho_r, Inertia_r, R, n):
b_in_link_prob = (1-R)#*Rho_b
b_out_link_prob = R#*(1-Rho_b)
mu_b = np.array([b_in_link_prob*theta_t_b, b_in_link_prob*(1-theta_t_b), b_out_link_prob*theta_t_r, b_out_link_prob*(1-theta_t_r)]).reshape(4,1)
Sigma_b = np.zeros((4,4))
for i in [0,1,2,3]:
Sigma_b[i,i] = mu_b[i,0]*(1-mu_b[i,0])/n
r_in_link_prob = R#*Rho_r
r_out_link_prob = (1-R)#*(1-Rho_r)
mu_r = np.array([r_in_link_prob*theta_t_r, r_in_link_prob*(1-theta_t_r), r_out_link_prob*theta_t_b, r_out_link_prob*(1-theta_t_b)]).reshape(4,1)
Sigma_r = np.zeros((4,4))
for i in [0,1,2,3]:
Sigma_r[i,i] = mu_r[i,0]*(1-mu_r[i,0])/n
coeffs_blue = np.array([Alpha_b, -Alpha_b, -Beta_b, Beta_b]).reshape(4,1)
coeffs_red = np.array([Alpha_r, -Alpha_r, -Beta_r, Beta_r]).reshape(4,1)
p_b_01 = 1-NormalDist(mu=np.matmul(coeffs_blue.transpose(),mu_b)-Inertia_b, sigma=np.sqrt(np.matmul(np.matmul(coeffs_blue.transpose(),Sigma_b),coeffs_blue))).cdf(0)
p_b_10 = NormalDist(mu=np.matmul(coeffs_blue.transpose(),mu_b)+Inertia_b, sigma=np.sqrt(np.matmul(np.matmul(coeffs_blue.transpose(),Sigma_b),coeffs_blue))).cdf(0)
p_r_01 = 1-NormalDist(mu=np.matmul(coeffs_red.transpose(),mu_r)-Inertia_r, sigma=np.sqrt(np.matmul(np.matmul(coeffs_red.transpose(),Sigma_r),coeffs_red))).cdf(0)
p_r_10 = NormalDist(mu=np.matmul(coeffs_red.transpose(),mu_r)+Inertia_r, sigma=np.sqrt(np.matmul(np.matmul(coeffs_red.transpose(),Sigma_r),coeffs_red))).cdf(0)
theta_t_blue_dot = (1-theta_t_b)*p_b_01 - theta_t_b*p_b_10
theta_t_red_dot = (1-theta_t_r)*p_r_01 - theta_t_r*p_r_10
return [theta_t_blue_dot, theta_t_red_dot]
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from scipy.stats import bernoulli
from scipy.stats import pareto
from statistics import NormalDist
import networkx as nx
import matplotlib.pyplot as plt
import random
import math
# Set initial conditions
Initial_H1_prob_blue = 0.8
Initial_H1_prob_red = 0.8
# Set parameters
R = 0.37
Alpha = 0.74
Beta = 0.92
Rho = .5
Inertia = 0.0
Alpha_b = Alpha
Beta_b = Beta
Rho_b = Rho
Inertia_b = Inertia
Alpha_r = Alpha
Beta_r = Beta
Rho_r = Rho
Inertia_r = Inertia
time, theta_t_blue, theta_t_red = Simulate_AP_model_SBM_Graph(Initial_H1_prob_blue, Initial_H1_prob_red, Alpha_b, Beta_b, Rho_b, Inertia_b, Alpha_r, Beta_r, Rho_r, Inertia_r, R, n = 5000000, T = 10, delta = 0.001)
plt.plot(time, theta_t_blue)
plt.plot(time, theta_t_red)
I noticed two main differences. For some reason the equations include a normal cdf, which is not described in paper (see for example p_b_01
in AP_model_SBM_Graph
), and the model in Python is less sensitive to changes in parameter values (perhaps due to the use of the normal cdf).
I have no idea what this normal cdf is. Why is it there? Did I set up the code incorrectly, or am I using the wrong type of solver? Any help would be appreciated.