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

Source Code for Module Chem.FeatMaps.FeatMapUtils

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