183 lines
5.9 KiB
Python
183 lines
5.9 KiB
Python
# import mod
|
|
import molfoundry as mf
|
|
import networkx as nx
|
|
import time
|
|
|
|
import re
|
|
|
|
# from equilibrator_assets.generate_compound import create_compounds
|
|
from prettify import red, warn
|
|
|
|
# each vertex has attribute "label" which stores string label
|
|
# each edge has attribute "bond" which stores bond order (-, =, etc.)
|
|
|
|
class Graph:
|
|
def __init__(self, graph, left=False, verbose=False):
|
|
self._verbose = verbose
|
|
if isinstance(graph, nx.Graph):
|
|
self._nx_graph = graph
|
|
self._gml_string = self.nx_graph_to_GML_string(graph)
|
|
elif isinstance(graph, str):
|
|
self._gml_string = graph
|
|
self._nx_graph = self.GML_to_nx_graph(self._gml_string)
|
|
elif isinstance(graph, mf.graph.Graph):
|
|
self._nx_graph = self.mf_to_nx_graph(graph)
|
|
self._gml_string = self.nx_graph_to_GML_string(self._nx_graph)
|
|
elif isinstance(graph, mf.rule.Rule):
|
|
if left:
|
|
self._nx_graph = self.rule_to_nx_graph(graph, left=True)
|
|
self._gml_string = self.nx_graph_to_GML_string(self._nx_graph)
|
|
else:
|
|
self._nx_graph = self.rule_to_nx_graph(graph, left=False)
|
|
self._gml_string = self.nx_graph_to_GML_string(self._nx_graph)
|
|
else:
|
|
print(red(f"Error: type not found for {type(graph)}"))
|
|
|
|
# up to the caller to check the number of components created
|
|
ccs = [self._nx_graph.subgraph(c).copy() for c in nx.connected_components(self._nx_graph)]
|
|
ccs = sorted(ccs, key=len, reverse=True)
|
|
self._num_components = len(ccs)
|
|
if self._num_components > 1:
|
|
self._components = [Graph(c) for c in ccs]
|
|
else:
|
|
self._components = [self]
|
|
|
|
if self._num_components == 1:
|
|
self._mod_graph = self.nx_graph_to_mf(self._nx_graph)
|
|
|
|
def rule_to_nx_graph(self, rule, left):
|
|
g = nx.Graph()
|
|
|
|
# vertices that don't change
|
|
for v in rule.vertices:
|
|
if v.isContext:
|
|
g.add_node(int(v.id), label=v.leftLabel, modID=int(v.id))
|
|
|
|
# bonds that don't change
|
|
for e in rule.edges:
|
|
if e.isContext:
|
|
g.add_edge(e.source.id, e.target.id, bond=e.leftLabel)
|
|
|
|
if left:
|
|
# vertices/edges which are destroyed
|
|
verts = [v for v in rule.vertices if v.isDeleted]
|
|
edges = [e for e in rule.edges if e.isDeleted]
|
|
|
|
else:
|
|
# vertices/edges which are created
|
|
verts = [v for v in rule.vertices if v.isCreated]
|
|
edges = [e for e in rule.edges if e.isCreated]
|
|
|
|
# vertices which change label
|
|
verts.extend([v for v in rule.vertices if v.isChanged])
|
|
# bonds which change label
|
|
edges.extend([e for e in rule.edges if e.isChanged])
|
|
|
|
for v in verts:
|
|
vLabel = v.leftLabel if left else v.rightLabel
|
|
g.add_node(v.id, label=vLabel, modID=v.id)
|
|
|
|
for e in edges:
|
|
eLabel = e.leftLabel if left else e.rightLabel
|
|
g.add_edge(e.source.id, e.target.id, bond=eLabel)
|
|
|
|
return g
|
|
|
|
def mf_to_nx_graph(self, mfGraph):
|
|
g = nx.Graph()
|
|
|
|
for v in mfGraph.vertices:
|
|
g.add_node(int(v.id), label=str(v.stringLabel), modID=int(v.id))
|
|
|
|
for e in mfGraph.edges:
|
|
g.add_edge(int(e.source.id), int(e.target.id), bond=e.stringLabel)
|
|
|
|
return g
|
|
|
|
def nx_graph_to_mf(self, nxGraph):
|
|
try:
|
|
return mf.Graph.fromGMLString(self.nx_graph_to_GML_string(nxGraph))
|
|
except: # graph is not connected probably
|
|
if self._verbose:
|
|
print(warn("Error converting nxGraph to mod graph. Likely graph is not connected. This will not affect rule generation."))
|
|
return None
|
|
|
|
def GML_to_nx_graph(self, gml_string):
|
|
g = nx.Graph()
|
|
lines = gml_string.split("\n")
|
|
for line in lines:
|
|
tokens = line.split()
|
|
if len(tokens) < 2:
|
|
continue
|
|
if tokens[0] == "node":
|
|
m = re.search(r"label\s+\"(.*)\"", line)
|
|
l = m.group(1)
|
|
m = re.search(r"id\s+(\d+)", line)
|
|
mid = m.group(1)
|
|
#(_,_,_, mid, _, l, _) = tokens
|
|
g.add_node(int(mid), label=l, modID=int(mid))
|
|
elif tokens[0] == "edge":
|
|
m = re.search(r"source\s+(\d+)\s+target\s+(\d+)\s+label\s+\"(.*)\"", line)
|
|
u = m.group(1)
|
|
v = m.group(2)
|
|
bondOrder = m.group(3)
|
|
#(_, _, _, u, _, v, _, bondOrder, _ ) = tokens
|
|
g.add_edge(int(u), int(v), bond=bondOrder)
|
|
return g
|
|
|
|
def nx_graph_to_GML_string(self, nxGraph):
|
|
out = []
|
|
out.append("graph [")
|
|
|
|
out.extend([f"\t\tnode [ id {nxGraph.nodes[node]['modID']} label \"{nxGraph.nodes[node]['label']}\" ]" for node in nxGraph.nodes])
|
|
out.extend([f"\t\tedge [ source {u} target {v} label \"{nxGraph[u][v]['bond']}\" ]"
|
|
for (u,v) in nxGraph.edges])
|
|
|
|
out.append("]")
|
|
|
|
return "\n".join(out)
|
|
|
|
@property
|
|
def nx_graph(self):
|
|
return self._nx_graph
|
|
|
|
@property
|
|
def mod_graph(self):
|
|
if self.num_components == 1:
|
|
return self._mod_graph
|
|
else:
|
|
return mf.Graph.fromGMLString(self.gml_string)
|
|
|
|
@property
|
|
def gml_string(self):
|
|
return self._gml_string
|
|
|
|
@property
|
|
def gml(self):
|
|
return self._gml_string
|
|
|
|
@property
|
|
def edges(self):
|
|
return self._nx_graph.edges
|
|
|
|
@property
|
|
def nodes(self):
|
|
return self._nx_graph.nodes
|
|
|
|
@property
|
|
def num_components(self):
|
|
return self._num_components
|
|
|
|
@property
|
|
def connected_components(self):
|
|
return self._components
|
|
|
|
def mod_print(self):
|
|
self._mod_graph.print()
|
|
|
|
def __str__(self) -> str:
|
|
return self.gml_string
|
|
|
|
def __hash__(self) -> int:
|
|
return hash(self.gml_string)
|