In [416]:
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
from itertools import combinations

In [417]:
n = 8
sign = ['+', '-']
nodes = np.arange(1,n+1,1)
triangles = list(combinations(nodes,3))

In [418]:
# add Graph
G = nx.Graph()

# add nodes
G.add_nodes_from(nodes)

In [419]:
for n1 in G.nodes():
    for n2 in G.nodes():
        if n1 != n2:
            s = np.random.choice(sign,p=[.5,.5])
            G.add_edge(n1,n2,sign=s)
    
Original = G.copy()

In [420]:
pos = nx.spring_layout(G)

In [421]:
def partial_draw_network(G,pos,node_color='#ADD8E6',width=3,include_signs=True):
    edge_labels = nx.get_edge_attributes(G,'sign')
    edge_colors = ['g' if e[2]['sign'] == '+' else 'r' for e in G.edges(data=True)]
    nx.draw(G, pos=pos, edge_color=edge_colors, width=width, node_color=node_color, with_labels=True)
    if include_signs:
        nx.draw_networkx_edge_labels(G, pos=pos, edge_labels=edge_labels, font_size=20)
    
def draw_network(G,pos,node_color='#ADD8E6',width=3,include_signs=True):
    partial_draw_network(G,pos,node_color,width,include_signs)
    
    plt.axis('off')
    plt.show()

In [429]:
# draw current state
plt.figure(figsize=[15,15])
draw_network(G,pos)



In [423]:
def is_stable(signs = []):
    count = signs.count('+')
    return ((count == 3) | (count == 1))

def get_stability(all_signs):
    stable = 0
    unstable = 0
    for signs in all_signs:
        if is_stable(signs):
            stable += 1
        else:
            unstable += 1
    return {'stable': stable, 'unstable': unstable}

def get_signs(G,triangle = []):
    assert len(triangle) == 3
    
    prevs = []
    signs = []
    for n1 in triangle:
        for n2 in triangle:
            if (n1 != n2) & (n2 not in prevs):
                value = G[n1][n2]['sign']
                signs.append(value)
        prevs.append(n1)
    
    return signs

def get_all_signs(G,all_triangles):
    return [get_signs(G,t) for t in all_triangles]

In [424]:
# get all the triangle signs
triangle_signs = get_all_signs(G,triangles)

# print out the network stability info
output = get_stability(triangle_signs)
print(output)


{'stable': 32, 'unstable': 24}

In [425]:
def make_triangle_stable(G,triangle):
    signs = get_signs(G,triangle)
    while(not is_stable(signs)):
        plus_count = signs.count('+')
        
        # randomly choose two nodes
        n1 = np.random.choice(triangle)
        n2 = np.random.choice(np.setdiff1d(triangle, [n1]))
        
        # flip the sign
        s = G[n1][n2]['sign']
        if s == '+':
            s = '-'
        else:
            s = '+'
        
        G[n1][n2]['sign'] = s
        
        # pull the latest.
        signs = get_signs(G,triangle)

In [426]:
while output['unstable'] > 0:
    # randomly choose an unstable triangle
    unstable_triangles = [t for t in triangles if not is_stable(get_signs(G,t))]
    unstable_triangle_index = np.random.choice(np.arange(0,len(unstable_triangles),1))
    unstable_triangle = unstable_triangles[unstable_triangle_index]
    
    # fix unstable_triangle
    make_triangle_stable(G,unstable_triangle)
    
    # pull latest and check for network stability
    triangle_signs = get_all_signs(G,triangles)
    output = get_stability(triangle_signs)
    
triangle_signs = get_all_signs(G,triangles)
output = get_stability(triangle_signs)
print(output)


{'stable': 56, 'unstable': 0}

In [428]:
# draw prior and current state
plt.figure(figsize=[10,10])
plt.subplot(1,2,1)
partial_draw_network(Original,pos,include_signs=False)

plt.subplot(1,2,2)
partial_draw_network(G,pos,include_signs=False)

plt.tight_layout()
plt.show()

print()
print('red = "negative" relationship')
print('green = "positive" relationship')


red = "negative" relationship
green = "positive" relationship

In [ ]: