137 lines
4.4 KiB
Python
137 lines
4.4 KiB
Python
import mod
|
|
import networkx as nx
|
|
import time
|
|
|
|
# from equilibrator_assets.generate_compound import create_compounds
|
|
|
|
# each vertex has attribute "label" which stores string label
|
|
# each edge has attribute "bond" which stores bond order (-, =, etc.)
|
|
|
|
class GraphObj:
|
|
def __init__(self, graph, verbose=False):
|
|
self._verbose = verbose
|
|
|
|
mod_lg_class = mod.libpymod.Rule.LeftGraph
|
|
mod_rg_class = mod.libpymod.Rule.RightGraph
|
|
mod_g_class = mod.libpymod.Graph
|
|
if isinstance(graph, mod_lg_class) or isinstance(graph, mod_rg_class) or isinstance(graph, mod_g_class):
|
|
self._nx_graph = self.mod_to_nx_graph(graph)
|
|
self._gml_string = self.nx_graph_to_GML_string(self._nx_graph)
|
|
elif isinstance(graph, nx.Graph):
|
|
self._nx_graph = graph
|
|
self._gml_string = self.nx_graph_to_GML_string(self._nx_graph)
|
|
elif isinstance(graph, str):
|
|
self._gml_string = graph
|
|
self._nx_graph = self.GML_to_nx_graph(self._gml_string)
|
|
else:
|
|
print(f"ERROR: Graph class cannot identify graph type in constructor: {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_mod(self._nx_graph)
|
|
|
|
# self._equ_compounds = self.equ_compounds()
|
|
|
|
def equ_compounds(self):
|
|
smiles = [c.mod_graph.smiles for c in self._components]
|
|
equ_comps = create_compounds(smiles, mol_format="smiles", bypass_chemaxon=True, save_empty_compounds=True)
|
|
return equ_comps
|
|
|
|
def mod_to_nx_graph(self, modGraph: mod.Graph):
|
|
g = nx.Graph()
|
|
|
|
for v in modGraph.vertices:
|
|
g.add_node(int(v.id), label=str(v.stringLabel), modID=int(v.id))
|
|
|
|
for e in modGraph.edges:
|
|
g.add_edge(int(e.source.id), int(e.target.id), bond=str(e.bondType))
|
|
|
|
return g
|
|
|
|
def nx_graph_to_mod(self, nxGraph):
|
|
try:
|
|
return mod.graphGMLString(self.nx_graph_to_GML_string(nxGraph))
|
|
except mod.libpymod.InputError: # graph is not connected probably
|
|
if self._verbose:
|
|
print("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":
|
|
(_,_,_, mid, _, l, _) = tokens
|
|
g.add_node(int(mid), label=l[1:-1], modID=int(mid))
|
|
elif tokens[0] == "edge":
|
|
(_, _, _, u, _, v, _, bondOrder, _ ) = tokens
|
|
g.add_edge(int(u), int(v), bond=bondOrder[1:-1])
|
|
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 mod.graphGMLString(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)
|