-
Notifications
You must be signed in to change notification settings - Fork 0
/
Boolean_regulatory_networks.py
190 lines (157 loc) · 6.84 KB
/
Boolean_regulatory_networks.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
"""
This module contains the code for the running example. Add further Boolean functions
in the folder "Examples" to try other examples.
"""
from PyBoolNet import InteractionGraphs as IGs
from PyBoolNet import QuineMcCluskey as QMC
from PyBoolNet import StateTransitionGraphs as STGs
import networkx as nx
# Replace this example if you like to choose another Boolean function as an example.
from Examples import runningExample as example
from construction_of_QDE_graph import construct_qde_graph
from sign_algebra import *
example_filename = "./Examples/"+example.return_name()
def phi(x, f):
y = f(x)
ret = [y[i] - x[i] for i in range(len(x))]
rret = []
for i in range(len(ret)):
if ret[i] != 0:
rret += [ phi.value_to_sign[ret[i]] ]
else:
temp = (-1)**(1-x[i])
rret += [ phi.value_to_sign[temp] ]
return rret
phi.value_to_sign = {1: p, -1: m}
def binary_string_to_components(binary_string):
"""
Converts smth. like '10011' into [1,0,0,1,1]
:param binary_string:
:return: list of elements in {0,1}
"""
return [int(x) for x in binary_string]
def function_is_monotonous(interaction_graph):
"""
Returns true if the interaction graph stems from a monontonous function,
:param interaction_graph:
:return:
"""
for edge in interaction_graph.edges(data=True): # data=True gives us also the signs of the edges
if len(edge[2]['sign']) > 1:
return False
return True
def graph_to_adj_matrix(interaction_graph):
"""
Converts the interaction graph into a sign matrix with elements from the sign algebra
:param interaction_graph:
:return: adjacency matrix of the interaction graph, nodes, nodes_to_number
"""
nodes = interaction_graph.nodes() # list number to node
nodes.sort()
nodes_to_number = dict([(nodes[i], i) for i in range(len(nodes))]) # dictionary node -> number
sign_dict = {1: p, 0: n, -1: m} # signs
# initialize adjacency matrix with zeros everywhere
sign_matrix = [[n] * len(nodes) for i in range(len(nodes))]
for edge in interaction_graph.edges(data=True):
#print(edge)
v1 = nodes_to_number[edge[0]]
v2 = nodes_to_number[edge[1]]
sign = edge[2]['sign']
sign_matrix[v2][v1] = sign_dict[list(sign)[0]] # the set sign has only one element
return sign_matrix, nodes, nodes_to_number
def interaction_graph_has_self_loops(interaction_graph):
sign_matrix, nodes, nodes_to_number = graph_to_adj_matrix(interaction_graph)
for i in range(len(nodes)):
if sign_matrix[i][i] != n:
return True
return False
def invert_dict(d):
return dict([(frozenset(d[k]), k) for k in d.keys()])
def computeQuotientGraph_fromFunction(graph, function):
"""
Computes the quotient graph: graph/function
:param graph: usual graph
:param function: a function from the nodes somewhere
:return: The quotient graph. The labels are the values of f
"""
# Create a partition for the nodes based on f
partition = {}
for node in graph.nodes():
node_converted = [int(bit) for bit in node]
key = str(tuple(function(node_converted)))
if key not in partition:
partition[key] = []
partition[key] += [node]
def relation(s,t):
s = [int(bit) for bit in s]
t = [int(bit) for bit in t]
for fs,ft in zip(function(s), function(t)):
if fs != ft:
return False
return True
quotient_graph = nx.quotient_graph(graph, relation)
return nx.relabel_nodes(quotient_graph, invert_dict(partition))
def convert_list_to_sting(list_of_elements):
string_repr = "{"
i = 1
for elem in list_of_elements:
string_repr += str(elem)+", "
if i % 2 == 0:
string_repr += "\n"
i += 1
if i % 2:
return string_repr[:-3]+"}"
return string_repr[:-2]+"}"
def computeQuotientGraph_fromPartition(graph, partition):
"""
Computes the quotient graph: graph/function
:param graph: usual graph
:param function: a function from the nodes somewhere
:return: The quotient graph. The labels are the values of f
"""
# Create a partition for the nodes based on f
partition_as_list = list(partition)
condensation = nx.condensation(graph, partition_as_list)
partition_as_dict = {}
for node in condensation.nodes():
partition_as_dict[node] = convert_list_to_sting(partition_as_list[node]) #str(partition_as_list[node])
result = nx.relabel_nodes(condensation, partition_as_dict)
return result
def spit_out_graphs(variable_to_boolean_function, prefix_of_filename):
"""
Plots the
1) interaction graph
2) asyncronous state transition graph
3) The quotient graph with respect to the partition induced by the Boolean function.
4) Qualitate and Boolean graph
5) SCCs of these graphs
"""
prime_implicants = QMC.functions2primes(variable_to_boolean_function)
# Create the interaction graph
igraph = IGs.primes2igraph(prime_implicants)
if not function_is_monotonous(interaction_graph=igraph):
raise ValueError("Error: This function is not monotonous.")
# Only consider interaction graphs wihtout self loops
if interaction_graph_has_self_loops(igraph):
raise ValueError("This graph has a self loop.")
# Construct the state transition graph
state_transition_graph = STGs.primes2stg(prime_implicants, "asynchronous")
# Get the quotient graph and plot it
quotientGraph = computeQuotientGraph_fromFunction(state_transition_graph, example.f)
STGs.stg2image(quotientGraph, prefix_of_filename+"_quotient_graph.pdf", LayoutEngine="dot")
# Convert the igraph into an adajacency matrix
sign_matrix, number_to_nodes, nodes_to_number = graph_to_adj_matrix(igraph)
# Get the QDE graph and the discrete QDE graph. Both are constructed in exactley the same way but have
# different node sets.
qde_graph = construct_qde_graph(sign_matrix, zero_one_representation=False)
boolean_qde_graph = construct_qde_graph(sign_matrix, zero_one_representation=True)
STGs.stg2image(qde_graph, prefix_of_filename + "_complete_qde_graph.pdf", LayoutEngine="dot")
STGs.stg2image(boolean_qde_graph, prefix_of_filename + "_complete_boolean_qde_graph.pdf", LayoutEngine="dot")
# Get the scc-graph of the QDE-graph
sccs = nx.strongly_connected_components(boolean_qde_graph)
scc_qde_graph = computeQuotientGraph_fromPartition(boolean_qde_graph, sccs)
STGs.stg2image(scc_qde_graph, prefix_of_filename + "_complete_scc_qde_graph.pdf")
STGs.stg2image(state_transition_graph, prefix_of_filename + "_stg.pdf", LayoutEngine="dot")
IGs.create_image(prime_implicants, prefix_of_filename + "_igraph.pdf")
if __name__ == "__main__":
spit_out_graphs(variable_to_boolean_function=example.funcs, prefix_of_filename=example_filename)