Package rdkit :: Package Chem :: Package FeatMaps :: Module FeatMapUtils
[hide private]
[frames] | no frames]

Source Code for Module rdkit.Chem.FeatMaps.FeatMapUtils

  1  # $Id$ 
  2  # 
  3  # Copyright (C) 2006 Greg Landrum 
  4  # 
  5  #   @@ All Rights Reserved @@ 
  6  #  This file is part of the RDKit. 
  7  #  The contents are covered by the terms of the BSD license 
  8  #  which is included in the file license.txt, found at the root 
  9  #  of the RDKit source tree. 
 10  # 
 11  import copy 
 12  from rdkit.Chem.FeatMaps import FeatMaps 
13 14 15 -class MergeMethod(object):
16 # Put the new point at the weighted average position of the two fused points 17 WeightedAverage = 0 18 # Put the new point at the un-weighted average position of the two fused points 19 Average = 1 20 # Put the new point at the position of the larger (by weight) of the two points 21 UseLarger = 2 22 23 @classmethod
24 - def valid(cls, mergeMethod):
25 """ Check that mergeMethod is valid """ 26 if mergeMethod not in (cls.WeightedAverage, cls.Average, cls.UseLarger): 27 raise ValueError('unrecognized mergeMethod')
28
29 30 -class MergeMetric(object):
31 # Do not merge points 32 NoMerge = 0 33 # merge two points if they come within a threshold distance 34 Distance = 1 35 # merge two points if their percent overlap exceeds a threshold 36 Overlap = 2 37 38 @classmethod
39 - def valid(cls, mergeMetric):
40 """ Check that mergeMetric is valid """ 41 if mergeMetric not in (cls.NoMerge, cls.Distance, cls.Overlap): 42 raise ValueError('unrecognized mergeMetric')
43
44 45 -class DirMergeMode(object):
46 # Do not merge directions (i.e. keep all direction vectors) 47 NoMerge = 0 48 # Sum direction vectors 49 Sum = 1 50 51 @classmethod
52 - def valid(cls, dirMergeMode):
53 """ Check that dirMergeMode is valid """ 54 if dirMergeMode not in (cls.NoMerge, cls.Sum): 55 raise ValueError('unrecognized dirMergeMode')
56
57 58 -def __copyAll(res, fm1, fm2):
59 """ no user-serviceable parts inside """ 60 for feat in fm1.GetFeatures(): 61 res.AddFeatPoint(copy.deepcopy(feat)) 62 for feat in fm2.GetFeatures(): 63 res.AddFeatPoint(copy.deepcopy(feat))
64
65 66 -def GetFeatFeatDistMatrix(fm, mergeMetric, mergeTol, dirMergeMode, compatFunc):
67 """ 68 69 NOTE that mergeTol is a max value for merging when using distance-based 70 merging and a min value when using score-based merging. 71 72 """ 73 MergeMetric.valid(mergeMetric) 74 75 dists = [[1e8] * fm.GetNumFeatures() for _ in range(fm.GetNumFeatures())] 76 if mergeMetric == MergeMetric.NoMerge: 77 return dists 78 elif mergeMetric == MergeMetric.Distance: 79 mergeTol2 = mergeTol * mergeTol 80 for i in range(fm.GetNumFeatures()): 81 ptI = fm.GetFeature(i) 82 for j in range(i + 1, fm.GetNumFeatures()): 83 ptJ = fm.GetFeature(j) 84 if compatFunc(ptI, ptJ): 85 dist2 = ptI.GetDist2(ptJ) 86 if dist2 < mergeTol2: 87 dists[i][j] = dist2 88 dists[j][i] = dist2 89 elif mergeMetric == MergeMetric.Overlap: 90 for i in range(fm.GetNumFeatures()): 91 ptI = fm.GetFeature(i) 92 for j in range(i + 1, fm.GetNumFeatures()): 93 ptJ = fm.GetFeature(j) 94 if compatFunc(ptI, ptJ): 95 score = fm.GetFeatFeatScore(ptI, ptJ, typeMatch=False) 96 score *= -1 * ptJ.weight 97 if score < mergeTol: 98 dists[i][j] = score 99 dists[j][i] = score 100 return dists
101
102 103 -def familiesMatch(f1, f2):
104 return f1.GetFamily() == f2.GetFamily()
105
106 107 -def feq(v1, v2, tol=1e-4):
108 return abs(v1 - v2) < tol
109
110 111 -def MergeFeatPoints(fm, mergeMetric=MergeMetric.NoMerge, mergeTol=1.5, 112 dirMergeMode=DirMergeMode.NoMerge, mergeMethod=MergeMethod.WeightedAverage, 113 compatFunc=familiesMatch):
114 """ 115 116 NOTE that mergeTol is a max value for merging when using distance-based 117 merging and a min value when using score-based merging. 118 119 returns whether or not any points were actually merged 120 121 """ 122 MergeMetric.valid(mergeMetric) 123 MergeMethod.valid(mergeMethod) 124 DirMergeMode.valid(dirMergeMode) 125 126 res = False 127 if mergeMetric == MergeMetric.NoMerge: 128 return res 129 dists = GetFeatFeatDistMatrix(fm, mergeMetric, mergeTol, dirMergeMode, compatFunc) 130 distOrders = [None] * len(dists) 131 for i in range(len(dists)): 132 distV = dists[i] 133 distOrders[i] = [] 134 for j, dist in enumerate(distV): 135 if dist < mergeTol: 136 distOrders[i].append((dist, j)) 137 distOrders[i].sort() 138 139 # print('distOrders:') 140 # print(distOrders) 141 142 # we now know the "distances" and have rank-ordered list of 143 # each point's neighbors. Work with that. 144 145 # progressively merge nearest neighbors until there 146 # are no more points left to merge 147 featsInPlay = list(range(fm.GetNumFeatures())) 148 featsToRemove = [] 149 # print '--------------------------------' 150 while featsInPlay: 151 # find two features who are mutual nearest neighbors: 152 fipCopy = featsInPlay[:] 153 for fi in fipCopy: 154 # print('>>>',fi,fipCopy,featsInPlay) 155 # print('\t',distOrders[fi]) 156 mergeThem = False 157 if not distOrders[fi]: 158 featsInPlay.remove(fi) 159 continue 160 dist, nbr = distOrders[fi][0] 161 if nbr not in featsInPlay: 162 continue 163 if distOrders[nbr][0][1] == fi: 164 # print 'direct:',fi,nbr 165 mergeThem = True 166 else: 167 # it may be that there are several points at about the same distance, 168 # check for that now 169 if (feq(distOrders[nbr][0][0], dist)): 170 for distJ, nbrJ in distOrders[nbr][1:]: 171 if feq(dist, distJ): 172 if nbrJ == fi: 173 # print 'indirect: ',fi,nbr 174 mergeThem = True 175 break 176 else: 177 break 178 # print ' bottom:',mergeThem 179 if mergeThem: 180 break 181 if mergeThem: 182 res = True 183 featI = fm.GetFeature(fi) 184 nbrFeat = fm.GetFeature(nbr) 185 186 if mergeMethod == MergeMethod.WeightedAverage: 187 newPos = featI.GetPos() * featI.weight + nbrFeat.GetPos() * nbrFeat.weight 188 newPos /= (featI.weight + nbrFeat.weight) 189 newWeight = (featI.weight + nbrFeat.weight) / 2 190 elif mergeMethod == MergeMethod.Average: 191 newPos = featI.GetPos() + nbrFeat.GetPos() 192 newPos /= 2 193 newWeight = (featI.weight + nbrFeat.weight) / 2 194 elif mergeMethod == MergeMethod.UseLarger: 195 if featI.weight > nbrFeat.weight: 196 newPos = featI.GetPos() 197 newWeight = featI.weight 198 else: 199 newPos = nbrFeat.GetPos() 200 newWeight = nbrFeat.weight 201 202 featI.SetPos(newPos) 203 featI.weight = newWeight 204 205 # nbr and fi are no longer valid targets: 206 # print 'nbr done:',nbr,featsToRemove,featsInPlay 207 featsToRemove.append(nbr) 208 featsInPlay.remove(fi) 209 featsInPlay.remove(nbr) 210 for nbrList in distOrders: 211 try: 212 nbrList.remove(fi) 213 except ValueError: 214 pass 215 try: 216 nbrList.remove(nbr) 217 except ValueError: 218 pass 219 else: 220 # print ">>>> Nothing found, abort" 221 break 222 featsToRemove.sort() 223 for i, fIdx in enumerate(featsToRemove): 224 fm.DropFeature(fIdx - i) 225 return res
226
227 228 -def CombineFeatMaps(fm1, fm2, mergeMetric=MergeMetric.NoMerge, mergeTol=1.5, 229 dirMergeMode=DirMergeMode.NoMerge):
230 """ 231 the parameters will be taken from fm1 232 """ 233 res = FeatMaps.FeatMap(params=fm1.params) 234 235 __copyAll(res, fm1, fm2) 236 if mergeMetric != MergeMetric.NoMerge: 237 MergeFeatPoints(res, mergeMetric=mergeMetric, mergeTol=mergeTol) 238 return res
239