File: scorer.py

package info (click to toggle)
mgltools-pyautodock 1.5.7-3
  • links: PTS, VCS
  • area: non-free
  • in suites: bullseye, buster, sid
  • size: 45,148 kB
  • sloc: python: 4,540; sh: 78; makefile: 13
file content (308 lines) | stat: -rw-r--r-- 9,357 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
############################################################################
#
# Authors: William Lindstrom, Ruth Huey
#
# Copyright: A. Olson TSRI 2004
#
#############################################################################

#
# $Id: scorer.py,v 1.12.2.1 2016/02/12 08:01:40 annao Exp $
#


import math
from math import sqrt
import numpy

from PyAutoDock.MolecularSystem import MolecularSystem


class ScoringStrategy:
    """Abstract class
    """
    def __init__(self, ms=None):
        pass
    
    def set_molecular_system(self, ms):
        """Abstract method
        """
        raise NotImplementedError, "abstract method"

    def get_score(self):
        """Abstract method
        """
        raise NotImplementedError, "abstract method"
    
# ScoringStrategy


class PairwiseScorer(ScoringStrategy):
    def __init__(self, ms=None):
        ScoringStrategy.__init__(self, None)
        if ms is not None:
            self.set_molecular_system(ms)
            
        self.required_attr_dictA = {}
        self.required_attr_dictB = {}


    def set_molecular_system(self, ms):
        """
ms, a MolecularSystem, manages which of its entity_sets is 'receptor'
and which 'ligand' via its configuration tuple and 
maintains the corresponding pairwise distance matrix. 

'set_molecular_system' checks that the currently designated entity_sets have
attributes required by this scorer class (required_attr_dicts
manage only checking a required attr once per entity_sets.)

@@FIX THIS: if change ms configuration, reset required_attr_dicts

"""
#        print "bases of ms", ms.__class__.__bases__
#        assert isinstance(ms, MolecularSystem)

        # check the required attribute for both subsets before going on
        listA = []
        for k,v in self.required_attr_dictA.items():
            if not v:
                listA.append(k)
                self.required_attr_dictA[k] = True
        ms.check_required_attributes( ms.configuration[0], listA)
        listB = []
        for k,v in self.required_attr_dictB.items():
            if not v:
                listB.append(k)
                self.required_attr_dictB[k] = True
        ms.check_required_attributes( ms.configuration[1], listB)

        # now set the molecular system
        self.ms = ms

    def _f(self, at_a, at_b):
        pass


    def get_score(self):
        if self.ms is None:
            raise RuntimeError("no molecular system available in scorer")
        array = self.get_score_array()
        return numpy.add.reduce(numpy.add.reduce(array))


    def get_score_array(self):
        """return pairwise score
        """
        # construct 2D array
        array = []
        for at_a in self.ms.get_entities(self.ms.configuration[0]):
            array.append([]) # a new row for every entity in first set
            row = array[-1]
            for at_b in  self.ms.get_entities(self.ms.configuration[1]):
                atom_score = self._f(at_a, at_b)
                row.append(atom_score)
        self.array = numpy.array(array)
        self.post_process()
        return self.array

    def post_process(self):
        pass
# PairwiseScorer



class DistDepPairwiseScorer(PairwiseScorer):
    non_bonded_cutoff = 8.0 # Angstroms
        
    def __init__(self, ms=None):
        """constructor
        """
        PairwiseScorer.__init__(self)
        # add the required attributes of this subclass to the dict
        self.required_attr_dictA.setdefault('coords', False)
        self.required_attr_dictB.setdefault('coords', False)
        if ms is not None:
            self.set_molecular_system(ms)

        self.use_non_bond_cutoff = True


    def _f(self, at_a, at_b, dist):
        """distDepPairwise function stub
        """
        return 1.0


    def get_score_array(self):
        """return DistDepPairwise score
        """
        entity_ix = self.ms.configuration[0]
        entity_jx = self.ms.configuration[1]
        distance = self.ms.get_dist_mat(entity_ix, entity_jx)
        
        # construct 2D array
        array = []
        cut = self.non_bonded_cutoff
        if self.use_non_bond_cutoff==True:
            for ax, at_a in  enumerate(self.ms.get_entities(entity_ix)):
                array.append([]) # a new row for every entity in first set
                row = array[ax]  # the 
                for bx, at_b in  enumerate(self.ms.get_entities(entity_jx)):
                    d = distance[ax][bx]
                    atom_score = 0.0
                    # Obey non-bonded distance cutoff
                    if (d < cut):
                        atom_score = self._f(at_a, at_b, d)
                    row.append(atom_score)
        else: # no cut_off
            for ax, at_a in  enumerate(self.ms.get_entities(entity_ix)):
                array.append([]) # a new row for every entity in first set
                row = array[ax]  # the
                for bx, at_b in  enumerate(self.ms.get_entities(entity_jx)):
                    atom_score = self._f(at_a, at_b, distance[ax][bx] )
                    row.append(atom_score)
        self.array = numpy.array(array)
        self.post_process()
        return self.array
# DistDepPairwiseScorer



class WeightedMultiTerm(DistDepPairwiseScorer):
    def __init__(self):
        DistDepPairwiseScorer.__init__(self)
        self.terms = []


    def set_molecular_system(self, ms):
        """
ms, a MolecularSystem, manages which of its entity_sets is 'receptor'
and which 'ligand' via its configuration tuple and 
maintains the corresponding pairwise distance matrix. 

'set_molecular_system' checks that the currently designated entity_sets have
attributes required by this scorer class. (required_attr_dicts
manage only checking a required attr once per entity_sets.)

@@FIX THIS: if change ms configuration, reset required_attr_dicts

"""
        # this call allows you to set the ms before any terms
        # are added. when terms are added (see add_term) set_molecular_system
        # will be called then.
        DistDepPairwiseScorer.set_molecular_system(self, ms)
        
        self.use_non_bond_cutoff = True
        for term in self.terms:
            term[0].set_molecular_system(self.ms)


    def add_term(self, term, weight=1.0):
        """add the term and weight as a tuple to the list of terms.
        """
        if hasattr(self, 'ms'):
            term.set_molecular_system(self.ms)
        self.terms.append( (term, weight) )


    def get_score(self):
        score = 0.0
        for term, weight in self.terms:
            score = score + weight*term.get_score()
        return score


    def get_score_per_term(self):
        scorelist = []
        for term, weight in self.terms:
            scorelist.append( weight*term.get_score() )
        return scorelist


    def get_score_array(self):
        #weightedMultiTerm
        t = self.terms[0]
        # do you really want the list of arrays ? or a list of number for each
        # scoring object?
        array = t[0].get_score_array() * t[1]
        if len(self.terms) > 1:
            for term, weight in self.terms[1:]:
                array = array + weight*term.get_score_array()
        self.array = array
        self.post_process()
        return self.array

        
# WeightedMultiTerm



class Distance(PairwiseScorer):
    """reference implementation of the Distance class
    """
    def __init__(self, ms=None):
        PairwiseScorer.__init__(self)
        # add the required attributes of this subclass to the dict
        self.required_attr_dictA.setdefault('coords', False)
        self.required_attr_dictB.setdefault('coords', False)
        if ms is not None:
            self.set_molecular_system(ms)


    #def _distance(self, a1, a2):
    def _f(self, at_a, at_b):
        a1 = at_a.coords
        a2 = at_b.coords
        return math.sqrt( (a2[0]-a1[0])**2 +
                          (a2[1]-a1[1])**2 +
                          (a2[2]-a1[2])**2)
# Distance



class HBondVDWSmoothing:
    def __init__(self, smooth_width=0.25):
        # Autodock smooth default is 0.5 which is then
        # divided by 2. before it's used
        self.set_smooth_width(smooth_width)


    def set_smooth_width(self, smooth_width):
        self.smooth_width = smooth_width # Angstoms


    def smooth_dist(self, dist, Rij,):
        """Adjust atomic distance to effectively broaden energy well

        dist is the distance between atoms
        Rij is the distance at the energy well minimum

        This implementation assumes that the energy function is
        monotonicly decreasing between 0 and Rij and monotonicly
        increasing for domain values greater than Rij
        """
        width = self.smooth_width # amount to broaden energy well

        # if dist is inside the "repulsive" side of the well
        if dist < (Rij-width):
            # move closer to energy well minimum
            new_dist = dist + width
            return new_dist
        
        # if dist is beyond the "attractive" side of the well
        if dist > (Rij + width):
            # move closer to energy well minimum
            new_dist = dist - width
            return new_dist

        # now dist is with-in width of Rij
        return Rij

    



if __name__ == '__main__':
    print "Tests are in Tests/test_scorer.py"
    # test_scorer.run_test()