From 630390b4b7032e45889fe7ba8d1f409d0cb222af Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Thu, 24 Oct 2024 15:54:12 +0100 Subject: [PATCH 1/5] add annotations to graphml edges --- gufe/ligandnetwork.py | 13 ++++++++----- gufe/tests/test_ligand_network.py | 6 +++--- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/gufe/ligandnetwork.py b/gufe/ligandnetwork.py index 177aa61e..54ab4a0a 100644 --- a/gufe/ligandnetwork.py +++ b/gufe/ligandnetwork.py @@ -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), ) for edge in self.edges ]) @@ -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 @@ -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')) # 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()) diff --git a/gufe/tests/test_ligand_network.py b/gufe/tests/test_ligand_network.py index a81a0888..b5e21742 100644 --- a/gufe/tests/test_ligand_network.py +++ b/gufe/tests/test_ligand_network.py @@ -47,9 +47,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}) + edge23 = LigandAtomMapping(mol2, mol3, {0: 0}, {"score": 1.0}) + edge13 = LigandAtomMapping(mol1, mol3, {0: 0, 2: 1}, {"score": 0.5}) return edge12, edge23, edge13 From 824a1f213a600ba2e83d1e90c613a89f96385505 Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Thu, 31 Oct 2024 11:55:41 +0000 Subject: [PATCH 2/5] add support for all types in the JSON codec --- gufe/ligandnetwork.py | 6 +++--- gufe/mapping/ligandatommapping.py | 2 +- gufe/tests/data/ligand_network.graphml | 4 ++++ gufe/tests/test_ligand_network.py | 4 +++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/gufe/ligandnetwork.py b/gufe/ligandnetwork.py index 54ab4a0a..e92fa812 100644 --- a/gufe/ligandnetwork.py +++ b/gufe/ligandnetwork.py @@ -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): @@ -98,7 +98,7 @@ 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(edge.annotations), + json.dumps(edge.annotations, cls=JSON_HANDLER.encoder), ) for edge in self.edges ]) @@ -128,7 +128,7 @@ def _from_serializable_graph(cls, graph: nx.Graph): LigandAtomMapping(componentA=label_to_mol[node1], componentB=label_to_mol[node2], componentA_to_componentB=dict(json.loads(edge_data["mapping"])), - annotations=json.loads(edge_data.get("annotations", 'null')) # work around old graphml files with missing edge annotations + 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) ] diff --git a/gufe/mapping/ligandatommapping.py b/gufe/mapping/ligandatommapping.py index 9a5b198b..cc8ada71 100644 --- a/gufe/mapping/ligandatommapping.py +++ b/gufe/mapping/ligandatommapping.py @@ -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""" diff --git a/gufe/tests/data/ligand_network.graphml b/gufe/tests/data/ligand_network.graphml index 07adb79d..604ae96c 100644 --- a/gufe/tests/data/ligand_network.graphml +++ b/gufe/tests/data/ligand_network.graphml @@ -1,4 +1,5 @@ + @@ -13,12 +14,15 @@ [[0, 0]] + {"score": 1.0} [[0, 0], [1, 1]] + {"score": 0.0, "length": {"magnitude": 1.0, "unit": "angstrom", ":is_custom:": true, "pint_unit_registry": "openff_units"}} [[0, 0], [2, 1]] + {"score": 0.5} \ No newline at end of file diff --git a/gufe/tests/test_ligand_network.py b/gufe/tests/test_ligand_network.py index b5e21742..763b3b35 100644 --- a/gufe/tests/test_ligand_network.py +++ b/gufe/tests/test_ligand_network.py @@ -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 @@ -47,7 +49,7 @@ def mols(): @pytest.fixture def std_edges(mols): mol1, mol2, mol3 = mols - edge12 = LigandAtomMapping(mol1, mol2, {0: 0, 1: 1}, {"score": 0.0}) + 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}) return edge12, edge23, edge13 From 252215e9fa430c7002e394ab37490bd3646ef618 Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Thu, 31 Oct 2024 12:59:04 +0000 Subject: [PATCH 3/5] fix mixing tests for ligand network --- gufe/tests/test_ligand_network.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gufe/tests/test_ligand_network.py b/gufe/tests/test_ligand_network.py index 763b3b35..b4791cd2 100644 --- a/gufe/tests/test_ligand_network.py +++ b/gufe/tests/test_ligand_network.py @@ -51,7 +51,7 @@ def std_edges(mols): mol1, mol2, mol3 = mols 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}) + edge13 = LigandAtomMapping(mol1, mol3, {0: 0, 2: 1}, {"score": 0.5, "time": 2.0 * unit.second}) return edge12, edge23, edge13 @@ -131,8 +131,8 @@ def network_container( class TestLigandNetwork(GufeTokenizableTestsMixin): cls = LigandNetwork - key = "LigandNetwork-c597016564f85a3c42445bd1dabd91b3" - repr = "" + key = "LigandNetwork-c858dd17c715a588c7bcd2a19ca8dcbd" + repr = "" @pytest.fixture def instance(self, simple_network): @@ -253,7 +253,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 From d4e2a213ea9158e549f0e8bcf64da08dd2b900ec Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Thu, 31 Oct 2024 13:01:42 +0000 Subject: [PATCH 4/5] update ligand network test file --- gufe/tests/data/ligand_network.graphml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gufe/tests/data/ligand_network.graphml b/gufe/tests/data/ligand_network.graphml index 604ae96c..31fe9e56 100644 --- a/gufe/tests/data/ligand_network.graphml +++ b/gufe/tests/data/ligand_network.graphml @@ -22,7 +22,7 @@ [[0, 0], [2, 1]] - {"score": 0.5} + {"score": 0.5, "time": {"magnitude": 2.0, "unit": "second", ":is_custom:": true, "pint_unit_registry": "openff_units"}} \ No newline at end of file From 0cea6f04ff8b2e59abbfa1b73e11aeff9fcc8906 Mon Sep 17 00:00:00 2001 From: Josh Horton Date: Fri, 1 Nov 2024 15:33:28 +0000 Subject: [PATCH 5/5] add news entry --- news/graphml_annotations.rst | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 news/graphml_annotations.rst diff --git a/news/graphml_annotations.rst b/news/graphml_annotations.rst new file mode 100644 index 00000000..b142b305 --- /dev/null +++ b/news/graphml_annotations.rst @@ -0,0 +1,23 @@ +**Added:** + +* + +**Changed:** + +* + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* Fixed a bug where edge annotations were lost when converting a ``LigandNetwork`` to graphml, all JSON codec types are now supported. + +**Security:** + +*