By Matthew A. Turner and Paul E. Smaldino In: Complexity Volume 2018, Article ID 2740959, 17 pages https://doi.org/10.1155/2018/2740959
import networkx as nx
import matplotlib.pyplot as plt
import cmocean
plt.style.use('bmh')
G = nx.connected_caveman_graph(20, 5)
plt.figure(figsize=(6, 6))
pos = nx.kamada_kawai_layout(G)
nx.draw_networkx_nodes(G, pos, node_color=range(100), node_size=200, cmap=cmocean.cm.phase, alpha=0.5)
nx.draw_networkx_edges(G, pos, alpha=0.5)
<matplotlib.collections.LineCollection at 0x13d7c6e48>
def add_long_ties(G, n):
while n > 0:
a = b = np.random.choice(G.nodes())
while b == a:
b = np.random.choice(list(set(G.nodes()) - set(G.neighbors(a))))
G.add_edge(a, b)
n -= 1
add_long_ties(G, 20)
plt.figure(figsize=(6, 6))
nx.draw_networkx_nodes(G, pos, node_color=range(100), node_size=200,
cmap=cmocean.cm.phase, alpha=0.5)
nx.draw_networkx_edges(G, pos, alpha=0.5)
plt.savefig("network.png", dpi=300)
import tqdm
from scipy.spatial.distance import cityblock, cdist
def dist_fn(u, v):
return 1 - (abs(v - u).sum() / v.shape[0])
def update_weights(G, agent):
K = G.node[agent]['opinions'].shape[0]
for neighbor in G.neighbors(agent):
distance = dist_fn(
G.node[agent]['opinions'], G.node[neighbor]['opinions'])
G[agent][neighbor]['weight'] = distance
def update_opinions(G, agent, noise=0):
K = G.node[agent]['opinions'].shape[0]
noise = noise * np.random.normal(size=K)
neighbors = list(G.neighbors(agent))
delta_s = (1. / (2 * len(neighbors))) * np.array([
G[agent][neighbor]['weight'] *
(G.node[neighbor]['opinions'] - G.node[agent]['opinions'])
for neighbor in neighbors]).sum(0)
opinions = G.node[agent]['opinions']
idx = opinions > 0
opinions[idx] = opinions[idx] + ((noise[idx] + delta_s[idx]) * (1 - opinions[idx]))
opinions[~idx] = opinions[~idx] + ((noise[~idx] + delta_s[~idx]) * (1 + opinions[~idx]))
def polarization(G):
opinions = np.array([data['opinions'] for _, data in G.nodes(data=True)])
dm = (1.0 / opinions.shape[1]) * cdist(opinions, opinions, metric='cityblock')
return dm[np.triu_indices(opinions.shape[0], k=1)].var()
def simulate(G, n_iter=1000, long_range_ties=False):
history = []
for i in range(n_iter):
agents = list(G.nodes())
np.random.shuffle(agents)
for agent in agents:
update_weights(G, agent)
update_opinions(G, agent)
history.append(polarization(G))
if i == 500 and long_range_ties:
add_long_ties(G, 20)
return history
G = nx.connected_caveman_graph(20, 5)
S, K = 1, 1
# initialize opinion values for K opinions
for agent, data in G.nodes(data=True):
data['opinions'] = np.random.uniform(-S, S, K)
# initialize weight between agents
for agent in G.nodes():
update_weights(G, agent)
p = simulate(G, n_iter=1000)
plt.plot(p)
plt.title(f"P = {polarization(G):.2f}");
plt.ylabel("Polarization")
plt.xlabel("Time")
Text(0.5, 0, 'Time')
def experiment(S, K, runs=50, long_range_ties=False):
scores = []
for run in range(runs):
G = nx.connected_caveman_graph(20, 5)
# initialize opinion values for K opinions
for agent, data in G.nodes(data=True):
data['opinions'] = np.random.uniform(-S, S, K)
# initialize weight between agents
for agent in G.nodes():
update_weights(G, agent)
simulate(G, n_iter=1000, long_range_ties=long_range_ties)
scores.append(polarization(G))
return scores
results = []
for K in tqdm.tqdm((1, 2, 3, 5, 10)):
scores = experiment(1, K)
for score in scores:
results.append((K, score))
results = pd.DataFrame(results, columns=['K', 'P'])
0%| | 0/5 [00:00<?, ?it/s] 20%|██ | 1/5 [08:52<35:28, 532.14s/it] 40%|████ | 2/5 [17:46<26:38, 532.89s/it] 60%|██████ | 3/5 [26:47<17:50, 535.14s/it] 80%|████████ | 4/5 [35:46<08:56, 536.31s/it] 100%|██████████| 5/5 [44:50<00:00, 538.76s/it]
plt.plot(results['K'], results['P'], 's', alpha=0.5, color='#348ABD')
plt.plot(results.groupby('K')['P'].mean(), linewidth=1, color='#348ABD')
plt.ylabel("Average Polarization")
plt.xlabel("$K$");
results = []
for K in tqdm.tqdm((1, 2, 3, 5, 10)):
scores = experiment(1, K, long_range_ties=True)
for score in scores:
results.append((K, score))
results = pd.DataFrame(results, columns=['K', 'P'])
plt.plot(results['K'], results['P'], 's', alpha=0.5, color='#348ABD')
plt.plot(results.groupby('K')['P'].mean(), linewidth=1, color='#348ABD')
plt.ylabel("Average Polarization")
plt.xlabel("$K$");
0%| | 0/5 [00:00<?, ?it/s] 20%|██ | 1/5 [08:58<35:55, 538.79s/it] 40%|████ | 2/5 [17:58<26:57, 539.11s/it] 60%|██████ | 3/5 [27:00<17:59, 539.93s/it] 80%|████████ | 4/5 [36:20<09:06, 546.04s/it] 100%|██████████| 5/5 [45:30<00:00, 547.18s/it]
def plot_opinion_evolution(K, S, sigma, T, log):
G = nx.connected_caveman_graph(20, 5)
# initialize opinion values for K opinions
for agent, data in G.nodes(data=True):
data['opinions'] = np.random.uniform(-S, S, K)
# initialize weight between agents
for agent in G.nodes():
update_weights(G, agent)
history = []
for i in range(T + 1):
agents = list(G.nodes())
np.random.shuffle(agents)
for agent in agents:
update_weights(G, agent)
update_opinions(G, agent, noise=sigma)
if i in log:
history.append((i, np.array([data['opinions'] for _, data in G.nodes(data=True)])))
if i == 500:
add_long_ties(G, 20)
fig, axes = plt.subplots(ncols=4, figsize=(12, 3))
for i, (t, opinions) in enumerate(history):
axes[i].scatter(opinions[:, 0], opinions[:, 1], color='#348ABD')
axes[i].set_title(f'$t={t}$')
P = polarization(G)
fig.suptitle(rf'$S={S}, K={K}, \sigma={sigma}, P={P:.2f}$', y=1.1)
plt.tight_layout()
plot_opinion_evolution(2, 0.5, 0, T=1001, log=(0, 200, 500, 1000))
plot_opinion_evolution(2, 0.5, 0.08, T=1001, log=(0, 200, 500, 1000))
plot_opinion_evolution(2, 0.5, 0.2, T=1001, log=(0, 200, 500, 1000))