Skip to content

Commit

Permalink
Merge pull request #369 from OpenFreeEnergy/graphml_annotations
Browse files Browse the repository at this point in the history
Save edge annotations in `LigandNetwork.to_graphml`
  • Loading branch information
IAlibay authored Nov 5, 2024
2 parents 7142e80 + 78d557d commit f7eb1a7
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 11 deletions.
15 changes: 9 additions & 6 deletions gufe/ligandnetwork.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

from gufe import SmallMoleculeComponent
from .mapping import LigandAtomMapping
from .tokenization import GufeTokenizable
from .tokenization import GufeTokenizable, JSON_HANDLER


class LigandNetwork(GufeTokenizable):
Expand Down Expand Up @@ -97,7 +97,8 @@ def _serializable_graph(self) -> nx.Graph:
(
mol_to_label[edge.componentA],
mol_to_label[edge.componentB],
json.dumps(list(edge.componentA_to_componentB.items()))
json.dumps(list(edge.componentA_to_componentB.items())),
json.dumps(edge.annotations, cls=JSON_HANDLER.encoder),
)
for edge in self.edges
])
Expand All @@ -109,8 +110,8 @@ def _serializable_graph(self) -> nx.Graph:
moldict=json.dumps(mol.to_dict(),
sort_keys=True))

for molA, molB, mapping in edge_data:
serializable_graph.add_edge(molA, molB, mapping=mapping)
for molA, molB, mapping, annotation in edge_data:
serializable_graph.add_edge(molA, molB, mapping=mapping, annotations=annotation)

return serializable_graph

Expand All @@ -126,8 +127,10 @@ def _from_serializable_graph(cls, graph: nx.Graph):
edges = [
LigandAtomMapping(componentA=label_to_mol[node1],
componentB=label_to_mol[node2],
componentA_to_componentB=dict(json.loads(mapping)))
for node1, node2, mapping in graph.edges(data='mapping')
componentA_to_componentB=dict(json.loads(edge_data["mapping"])),
annotations=json.loads(edge_data.get("annotations", 'null'), cls=JSON_HANDLER.decoder) # work around old graphml files with missing edge annotations
)
for node1, node2, edge_data in graph.edges(data=True)
]

return cls(edges=edges, nodes=label_to_mol.values())
Expand Down
2 changes: 1 addition & 1 deletion gufe/mapping/ligandatommapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def componentB_unique(self):
def annotations(self):
"""Any extra metadata, for example the score of a mapping"""
# return a copy (including copy of nested)
return json.loads(json.dumps(self._annotations))
return json.loads(json.dumps(self._annotations, cls=JSON_HANDLER.encoder), cls=JSON_HANDLER.decoder)

def _to_dict(self):
"""Serialize to dict"""
Expand Down
4 changes: 4 additions & 0 deletions gufe/tests/data/ligand_network.graphml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<key id="d2" for="edge" attr.name="annotations" attr.type="string" />
<key id="d1" for="edge" attr.name="mapping" attr.type="string" />
<key id="d0" for="node" attr.name="moldict" attr.type="string" />
<graph edgedefault="directed">
Expand All @@ -13,12 +14,15 @@
</node>
<edge source="mol0" target="mol2" id="0">
<data key="d1">[[0, 0]]</data>
<data key="d2">{"score": 1.0}</data>
</edge>
<edge source="mol1" target="mol0" id="0">
<data key="d1">[[0, 0], [1, 1]]</data>
<data key="d2">{"score": 0.0, "length": {"magnitude": 1.0, "unit": "angstrom", ":is_custom:": true, "pint_unit_registry": "openff_units"}}</data>
</edge>
<edge source="mol1" target="mol2" id="0">
<data key="d1">[[0, 0], [2, 1]]</data>
<data key="d2">{"score": 0.5, "time": {"magnitude": 2.0, "unit": "second", ":is_custom:": true, "pint_unit_registry": "openff_units"}}</data>
</edge>
</graph>
</graphml>
10 changes: 6 additions & 4 deletions gufe/tests/test_ligand_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from gufe.tests.test_protocol import DummyProtocol
from gufe import SmallMoleculeComponent, LigandNetwork, LigandAtomMapping

from openff.units import unit

from rdkit import Chem

from networkx import NetworkXError
Expand Down Expand Up @@ -47,9 +49,9 @@ def mols():
@pytest.fixture
def std_edges(mols):
mol1, mol2, mol3 = mols
edge12 = LigandAtomMapping(mol1, mol2, {0: 0, 1: 1})
edge23 = LigandAtomMapping(mol2, mol3, {0: 0})
edge13 = LigandAtomMapping(mol1, mol3, {0: 0, 2: 1})
edge12 = LigandAtomMapping(mol1, mol2, {0: 0, 1: 1}, {"score": 0.0, "length": 1.0 * unit.angstrom})
edge23 = LigandAtomMapping(mol2, mol3, {0: 0}, {"score": 1.0})
edge13 = LigandAtomMapping(mol1, mol3, {0: 0, 2: 1}, {"score": 0.5, "time": 2.0 * unit.second})
return edge12, edge23, edge13


Expand Down Expand Up @@ -250,7 +252,7 @@ def test_enlarge_graph_add_duplicate_edge(self, mols, simple_network):
# Adding a duplicate of an existing edge should create a new network
# with the same edges and nodes as the previous one.
mol1, _, mol3 = mols
duplicate = LigandAtomMapping(mol1, mol3, {0: 0, 2: 1})
duplicate = LigandAtomMapping(mol1, mol3, {0: 0, 2: 1}, {"score": 0.5, "time": 2.0 * unit.second})
network = simple_network.network

existing = network.edges
Expand Down
23 changes: 23 additions & 0 deletions news/graphml_annotations.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
**Added:**

* <news item>

**Changed:**

* <news item>

**Deprecated:**

* <news item>

**Removed:**

* <news item>

**Fixed:**

* Fixed a bug where edge annotations were lost when converting a ``LigandNetwork`` to graphml, all JSON codec types are now supported.

**Security:**

* <news item>

0 comments on commit f7eb1a7

Please sign in to comment.