Source code for dockerasmus.score.function
# coding: utf-8
from __future__ import absolute_import
from __future__ import unicode_literals
import logging
from . import requirements
from .components.base import BaseComponent
[docs]class ScoringFunction(object):
"""The generalisation of a scoring function
The ScoringFunction will first fetch dynamically the
requirements that must be pre-computed before calling each
scoring component: for instance, since both `components.Coulomb`
and `components.LennardJones` require the atom-wise distance
matrix between the two proteins, this matrix will be
created only once. This *magic* behaviour is obtained by
matching the names of the arguments of each scoring component
with an actual function in the `requirements` module.
Attributes:
components (`list`): the list of individual components
that are used independently to compute the final
score.
requirements (`set`): a set of arguments that must be
preprocessed to compute the score, based on the
requirements of the individual scoring components.
Examples:
Non-bound terms of Cornell's scoring function:
>>> f = ScoringFunction(LennardJones, Coulomb)
>>> f(barnase, barstar)
-84.9...
Using weights on individual components:
>>> g = ScoringFunction(LennardJones, Fabiola, weights=[1, 3])
>>> g(barnase, barstar)
-118.54...
"""
def __init__(self, *components, **kwargs):
self.components = []
self.weights = kwargs.get('weights') or [1 for _ in range(len(components))]
for component in components:
if issubclass(component, BaseComponent):
logging.debug("Creating new {} instance...".format(component.__name__))
self.components.append(component())
elif isinstance(component, BaseComponent):
logging.debug("Registering {} instance...".format(component.__class__.__name__))
self.components.append(component)
else:
raise TypeError("Invalid component: {}".format(component))
self.requirements = {
req:getattr(requirements, req)
for c in components for req in c.args()
}
def __call__(self, protein1, protein2, **parameters):
requirements = self._compute_requirements(protein1, protein2)
score = 0
for weight, component in zip(self.weights, self.components):
args = self._filter_requirements(component, requirements)
kwargs = self._filter_parameters(component, parameters)
score += weight*component(*args, **kwargs)
return score
def _compute_requirements(self, protein1, protein2):
return {req: func(protein1, protein2) for req,func in self.requirements.items()}
@staticmethod
def _filter_requirements(component, requirements):
return [requirements[arg] for arg in component.args()]
@staticmethod
def _filter_parameters(component, parameters):
return {k:v for k,v in parameters.items() if k in component.kwargs()}