From d29027a95a280726ee5d97c5fd3fe184076d68e2 Mon Sep 17 00:00:00 2001 From: riesben Date: Wed, 17 Apr 2024 21:39:28 +0200 Subject: [PATCH 01/12] Adding a generic atom mapping scorer --- gufe/mapping/atom_mapping_scorer.py | 42 +++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 gufe/mapping/atom_mapping_scorer.py diff --git a/gufe/mapping/atom_mapping_scorer.py b/gufe/mapping/atom_mapping_scorer.py new file mode 100644 index 00000000..7eb116a8 --- /dev/null +++ b/gufe/mapping/atom_mapping_scorer.py @@ -0,0 +1,42 @@ +# This code is part of kartograf and is licensed under the MIT license. +# For details, see https://github.com/OpenFreeEnergy/gufe + +import abc + +from . import AtomMapping + + +class AtomMappingScorer(abc.ABC): + """A generic class for scoring Atom mappings. + this class can be used for example to build graph algorithm based networks. + + Implementations of this class can require an arbitrary and non-standardised + number of input arguments to create. + + Implementations of this class provide the :meth:`.get_score` method + + """ + + def __call__(self, mapping: AtomMapping, *args, **kwargs) -> float: + return self.get_score(mapping) + + @abc.abstractmethod + def get_score(self, mapping: AtomMapping, *args, **kwargs) -> float: + """ calculate the score for an :class:`.AtomMapping` + the scoring function returns a value between 0 and 1. + a value close to 1.0 indicates a small distance, a score close to zero indicates a large cost/error. + + Parameters + ---------- + mapping: AtomMapping + the mapping to be scored + args + kwargs + + Returns + ------- + float + a value between [0,1] where one is a very bad score and 0 a very good one. + + """ + pass From 4240960acecd7c4f64612f431b483664a431bbd8 Mon Sep 17 00:00:00 2001 From: riesben Date: Wed, 17 Apr 2024 21:51:33 +0200 Subject: [PATCH 02/12] This is a start for a generic ligand network planner. --- gufe/ligand_network_planner.py | 48 ++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 gufe/ligand_network_planner.py diff --git a/gufe/ligand_network_planner.py b/gufe/ligand_network_planner.py new file mode 100644 index 00000000..f167d5b9 --- /dev/null +++ b/gufe/ligand_network_planner.py @@ -0,0 +1,48 @@ +import abc +from typing import Iterable + +from . import SmallMoleculeComponent, LigandNetwork +from . import AtomMapper +from .mapping.atom_mapping_scorer import AtomMappingScorer + +class LigandNetworkPlanner(abc.ABC): + """A generic class for calculating :class:`.LigandNetworks`. + + Implementations of this class can require an arbitrary and non-standardised + number of input arguments to create. + + Implementations of this class provide the :meth:`.get_score` method + + """ + + def __init__(self, mapper: AtomMapper, scorer:AtomMappingScorer, *args, *kwargs): + """ Generate a Ligand Network Planner. This class in general needs a mapper and a scorer. + + Parameters + ---------- + mapper: AtomMapper + scorer: AtomMappingScorer + args + kwargs + """ + self.mapper = mapper + self.scorer = scorer + + + def __call__(self, *args, **kwargs)-> LigandNetwork: + return self.generate_ligand_network(*args, **kwargs) + + @abc.abstractmethod + def generate_ligand_network(self, ligands: Iterable[SmallMoleculeComponent])->LigandNetwork: + """Plan a Network which connects all ligands with minimal cost + + Parameters + ---------- + ligands : Iterable[SmallMoleculeComponent] + the ligands to include in the Network + + Returns + ------- + LigandNetwork + A Network, that connects all ligands with each other. + """ From c6d6508d4d9710bde54c65952eb6ea710fce563a Mon Sep 17 00:00:00 2001 From: Benjamin Ries Date: Thu, 18 Apr 2024 22:28:59 +0200 Subject: [PATCH 03/12] Update atom_mapping_scorer.py Absolutley agree with @richardjgowers --- gufe/mapping/atom_mapping_scorer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gufe/mapping/atom_mapping_scorer.py b/gufe/mapping/atom_mapping_scorer.py index 7eb116a8..a85f33e3 100644 --- a/gufe/mapping/atom_mapping_scorer.py +++ b/gufe/mapping/atom_mapping_scorer.py @@ -17,11 +17,11 @@ class AtomMappingScorer(abc.ABC): """ - def __call__(self, mapping: AtomMapping, *args, **kwargs) -> float: + def __call__(self, mapping: AtomMapping) -> float: return self.get_score(mapping) @abc.abstractmethod - def get_score(self, mapping: AtomMapping, *args, **kwargs) -> float: + def get_score(self, mapping: AtomMapping) -> float: """ calculate the score for an :class:`.AtomMapping` the scoring function returns a value between 0 and 1. a value close to 1.0 indicates a small distance, a score close to zero indicates a large cost/error. From 6813c0a7b8394122b1734e040df0a6e8564e1fce Mon Sep 17 00:00:00 2001 From: Benjamin Ries Date: Thu, 18 Apr 2024 22:30:56 +0200 Subject: [PATCH 04/12] Update ligand_network_planner.py adjusting to comments. --- gufe/ligand_network_planner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gufe/ligand_network_planner.py b/gufe/ligand_network_planner.py index f167d5b9..e9f16310 100644 --- a/gufe/ligand_network_planner.py +++ b/gufe/ligand_network_planner.py @@ -29,7 +29,7 @@ def __init__(self, mapper: AtomMapper, scorer:AtomMappingScorer, *args, *kwargs) self.scorer = scorer - def __call__(self, *args, **kwargs)-> LigandNetwork: + def __call__(self, ligands: Iterable[SmallMoleculeComponent])-> LigandNetwork: return self.generate_ligand_network(*args, **kwargs) @abc.abstractmethod From 1e6658d15d0cc7325f441a4bfbc8c516ff9a255d Mon Sep 17 00:00:00 2001 From: Benjamin Ries Date: Tue, 11 Jun 2024 06:50:28 +0200 Subject: [PATCH 05/12] Update atom_mapping_scorer.py --- gufe/mapping/atom_mapping_scorer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gufe/mapping/atom_mapping_scorer.py b/gufe/mapping/atom_mapping_scorer.py index a85f33e3..e8401158 100644 --- a/gufe/mapping/atom_mapping_scorer.py +++ b/gufe/mapping/atom_mapping_scorer.py @@ -36,7 +36,7 @@ def get_score(self, mapping: AtomMapping) -> float: Returns ------- float - a value between [0,1] where one is a very bad score and 0 a very good one. + a value between [0,1] where zero is a very bad score and one a very good one. """ pass From e4d422363c9372aeddff8abe0e3f5828af06b9f5 Mon Sep 17 00:00:00 2001 From: riesben Date: Tue, 11 Jun 2024 07:08:59 +0200 Subject: [PATCH 06/12] finalizing the setup interface for atom_mapping_scorer --- gufe/__init__.py | 2 +- gufe/mapping/__init__.py | 2 ++ gufe/mapping/atom_mapping_scorer.py | 3 ++- gufe/tests/test_setup_interfaces.py | 14 ++++++++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 gufe/tests/test_setup_interfaces.py diff --git a/gufe/__init__.py b/gufe/__init__.py index 10e12d57..8dc3677e 100644 --- a/gufe/__init__.py +++ b/gufe/__init__.py @@ -18,7 +18,7 @@ from .mapping import ( ComponentMapping, # how individual Components relate - AtomMapping, AtomMapper, # more specific to atom based components + AtomMapping, AtomMapper, AtomMappingScorer, # more specific to atom based components LigandAtomMapping, ) diff --git a/gufe/mapping/__init__.py b/gufe/mapping/__init__.py index bd6be51b..bb80180c 100644 --- a/gufe/mapping/__init__.py +++ b/gufe/mapping/__init__.py @@ -4,4 +4,6 @@ from .componentmapping import ComponentMapping from .atom_mapping import AtomMapping from .atom_mapper import AtomMapper +from .atom_mapping_scorer import AtomMappingScorer from .ligandatommapping import LigandAtomMapping + diff --git a/gufe/mapping/atom_mapping_scorer.py b/gufe/mapping/atom_mapping_scorer.py index e8401158..7b4f7d60 100644 --- a/gufe/mapping/atom_mapping_scorer.py +++ b/gufe/mapping/atom_mapping_scorer.py @@ -2,11 +2,12 @@ # For details, see https://github.com/OpenFreeEnergy/gufe import abc +from ..tokenization import GufeTokenizable from . import AtomMapping -class AtomMappingScorer(abc.ABC): +class AtomMappingScorer(GufeTokenizable): """A generic class for scoring Atom mappings. this class can be used for example to build graph algorithm based networks. diff --git a/gufe/tests/test_setup_interfaces.py b/gufe/tests/test_setup_interfaces.py new file mode 100644 index 00000000..ee355a55 --- /dev/null +++ b/gufe/tests/test_setup_interfaces.py @@ -0,0 +1,14 @@ +# This code is part of OpenFE and is licensed under the MIT license. +# For details, see https://github.com/OpenFreeEnergy/gufe + +import pytest +from gufe import AtomMappingScorer, AtomMapper + + +def test_atom_mapping_scorer(): + with pytest.raises(TypeError, match="Can't instantiate abstract class AtomMappingScorer without an implementation for abstract methods '_defaults', '_from_dict', '_to_dict', 'get_score'"): + scorer = AtomMappingScorer() + +def test_atom_mapper(): + with pytest.raises(TypeError, match="Can't instantiate abstract class AtomMapper without an implementation for abstract methods '_defaults', '_from_dict', '_to_dict', 'suggest_mappings'"): + mapper = AtomMapper() \ No newline at end of file From 6242eb18b88bf77ce053e5047cf8647fa4f33d64 Mon Sep 17 00:00:00 2001 From: riesben Date: Tue, 11 Jun 2024 07:22:22 +0200 Subject: [PATCH 07/12] adjusting catched error. --- gufe/tests/test_setup_interfaces.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gufe/tests/test_setup_interfaces.py b/gufe/tests/test_setup_interfaces.py index ee355a55..61f185f2 100644 --- a/gufe/tests/test_setup_interfaces.py +++ b/gufe/tests/test_setup_interfaces.py @@ -6,9 +6,9 @@ def test_atom_mapping_scorer(): - with pytest.raises(TypeError, match="Can't instantiate abstract class AtomMappingScorer without an implementation for abstract methods '_defaults', '_from_dict', '_to_dict', 'get_score'"): + with pytest.raises(TypeError, match="Can't instantiate abstract class AtomMapper with abstract methods"): scorer = AtomMappingScorer() def test_atom_mapper(): - with pytest.raises(TypeError, match="Can't instantiate abstract class AtomMapper without an implementation for abstract methods '_defaults', '_from_dict', '_to_dict', 'suggest_mappings'"): + with pytest.raises(TypeError, match="Can't instantiate abstract class AtomMapper without an implementation for abstract method"): mapper = AtomMapper() \ No newline at end of file From 4af765650727b7313d02b4fb1c38ab8cccd06a68 Mon Sep 17 00:00:00 2001 From: riesben Date: Tue, 11 Jun 2024 07:34:54 +0200 Subject: [PATCH 08/12] pep formatting g --- gufe/mapping/__init__.py | 1 - gufe/tests/test_setup_interfaces.py | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gufe/mapping/__init__.py b/gufe/mapping/__init__.py index bb80180c..3a9ceddb 100644 --- a/gufe/mapping/__init__.py +++ b/gufe/mapping/__init__.py @@ -6,4 +6,3 @@ from .atom_mapper import AtomMapper from .atom_mapping_scorer import AtomMappingScorer from .ligandatommapping import LigandAtomMapping - diff --git a/gufe/tests/test_setup_interfaces.py b/gufe/tests/test_setup_interfaces.py index 61f185f2..8eab85e1 100644 --- a/gufe/tests/test_setup_interfaces.py +++ b/gufe/tests/test_setup_interfaces.py @@ -9,6 +9,7 @@ def test_atom_mapping_scorer(): with pytest.raises(TypeError, match="Can't instantiate abstract class AtomMapper with abstract methods"): scorer = AtomMappingScorer() + def test_atom_mapper(): with pytest.raises(TypeError, match="Can't instantiate abstract class AtomMapper without an implementation for abstract method"): - mapper = AtomMapper() \ No newline at end of file + mapper = AtomMapper() From a02469145c0e45e4f9d4ac5d0f119f65fc72f01b Mon Sep 17 00:00:00 2001 From: riesben Date: Tue, 11 Jun 2024 07:44:39 +0200 Subject: [PATCH 09/12] fix test --- gufe/tests/test_setup_interfaces.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gufe/tests/test_setup_interfaces.py b/gufe/tests/test_setup_interfaces.py index 8eab85e1..cdb832a3 100644 --- a/gufe/tests/test_setup_interfaces.py +++ b/gufe/tests/test_setup_interfaces.py @@ -6,10 +6,10 @@ def test_atom_mapping_scorer(): - with pytest.raises(TypeError, match="Can't instantiate abstract class AtomMapper with abstract methods"): + with pytest.raises(TypeError, match=""Can't instantiate abstract class AtomMappingScorer"): scorer = AtomMappingScorer() def test_atom_mapper(): - with pytest.raises(TypeError, match="Can't instantiate abstract class AtomMapper without an implementation for abstract method"): + with pytest.raises(TypeError, match=""Can't instantiate abstract class AtomMapper"): mapper = AtomMapper() From be4b7d2a40a8c193aacfa2e95caf050a93d015dc Mon Sep 17 00:00:00 2001 From: Benjamin Ries Date: Tue, 11 Jun 2024 07:45:35 +0200 Subject: [PATCH 10/12] Update __init__.py for formatting pep --- gufe/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gufe/__init__.py b/gufe/__init__.py index 8dc3677e..37eb6e7b 100644 --- a/gufe/__init__.py +++ b/gufe/__init__.py @@ -18,7 +18,7 @@ from .mapping import ( ComponentMapping, # how individual Components relate - AtomMapping, AtomMapper, AtomMappingScorer, # more specific to atom based components + AtomMapping, AtomMapper, AtomMappingScorer, # more specific to atom based components LigandAtomMapping, ) From b3442bb898894b0313aea111d3caad7f3b3e5640 Mon Sep 17 00:00:00 2001 From: riesben Date: Tue, 11 Jun 2024 07:58:17 +0200 Subject: [PATCH 11/12] fix test --- gufe/tests/test_setup_interfaces.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gufe/tests/test_setup_interfaces.py b/gufe/tests/test_setup_interfaces.py index cdb832a3..ab8a8ba0 100644 --- a/gufe/tests/test_setup_interfaces.py +++ b/gufe/tests/test_setup_interfaces.py @@ -6,10 +6,10 @@ def test_atom_mapping_scorer(): - with pytest.raises(TypeError, match=""Can't instantiate abstract class AtomMappingScorer"): + with pytest.raises(TypeError, match="Can't instantiate abstract class AtomMappingScorer"): scorer = AtomMappingScorer() def test_atom_mapper(): - with pytest.raises(TypeError, match=""Can't instantiate abstract class AtomMapper"): + with pytest.raises(TypeError, match="Can't instantiate abstract class AtomMapper"): mapper = AtomMapper() From 24a1081caf7ec5d5468a55b551d8e758aedad0ff Mon Sep 17 00:00:00 2001 From: Benjamin Ries Date: Tue, 27 Aug 2024 17:30:50 +0200 Subject: [PATCH 12/12] Update atom_mapping_scorer.py --- gufe/mapping/atom_mapping_scorer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gufe/mapping/atom_mapping_scorer.py b/gufe/mapping/atom_mapping_scorer.py index 7b4f7d60..cf9d0611 100644 --- a/gufe/mapping/atom_mapping_scorer.py +++ b/gufe/mapping/atom_mapping_scorer.py @@ -25,7 +25,7 @@ def __call__(self, mapping: AtomMapping) -> float: def get_score(self, mapping: AtomMapping) -> float: """ calculate the score for an :class:`.AtomMapping` the scoring function returns a value between 0 and 1. - a value close to 1.0 indicates a small distance, a score close to zero indicates a large cost/error. + a value close to 1.0 indicates a small change, a score close to zero indicates a large cost/change. Parameters ----------