1
2
3
4
5
6
7
8
9
10
11
12
13 import copy
14 from rdkit.six.moves import range
15 from rdkit.Chem.FeatMaps import FeatMaps
16
18 WeightedAverage=0
19 """ Put the new point at the weighted average position of the two
20 fused points
21 """
22
23 Average=1
24 """ Put the new point at the un-weighted average position of the two
25 fused points
26 """
27
28 UseLarger=2
29 """ Put the new point at the position of the larger (by weight)
30 of the two points
31 """
32
34 NoMerge=0
35 """ Do not merge points """
36
37 Distance=1
38 """ merge two points if they come within a threshold distance """
39
40 Overlap=2
41 """ merge two points if their percent overlap exceeds a threshold """
42
44 NoMerge=0
45 """ Do not merge directions (i.e. keep all direction vectors) """
46
47 Sum=1
48 """ Sum direction vectors """
49
56
57
94
96 return f1.GetFamily()==f2.GetFamily()
97
98 -def feq(v1,v2,tol=1e-4):
100
105 """
106
107 NOTE that mergeTol is a max value for merging when using distance-based
108 merging and a min value when using score-based merging.
109
110 returns whether or not any points were actually merged
111
112 """
113 res=False
114 if mergeMetric==MergeMetric.NoMerge:
115 return res
116 dists = GetFeatFeatDistMatrix(fm,mergeMetric,mergeTol,dirMergeMode,compatFunc)
117 distOrders = [None]*len(dists)
118 for i in range(len(dists)):
119 distV = dists[i]
120 distOrders[i] = []
121 for j,dist in enumerate(distV):
122 if dist<mergeTol:
123 distOrders[i].append((dist,j))
124 distOrders[i].sort()
125
126
127
128
129
130
131
132
133
134 featsInPlay=list(range(fm.GetNumFeatures()))
135 featsToRemove = []
136
137 while featsInPlay:
138
139 fipCopy=featsInPlay[:]
140 for fi in fipCopy:
141
142
143 mergeThem=False
144 if not distOrders[fi]:
145 featsInPlay.remove(fi)
146 continue
147 dist,nbr = distOrders[fi][0]
148 if nbr not in featsInPlay:
149 continue
150 if distOrders[nbr][0][1]==fi:
151
152 mergeThem=True
153 else:
154
155
156 if(feq(distOrders[nbr][0][0],dist)):
157 for distJ,nbrJ in distOrders[nbr][1:]:
158 if feq(dist,distJ):
159 if nbrJ==fi:
160
161 mergeThem=True
162 break
163 else:
164 break
165
166 if mergeThem: break
167 if mergeThem:
168 res=True
169 featI = fm.GetFeature(fi)
170 nbrFeat = fm.GetFeature(nbr)
171
172 if mergeMethod==MergeMethod.WeightedAverage:
173 newPos = featI.GetPos()*featI.weight+nbrFeat.GetPos()*nbrFeat.weight
174 newPos /= (featI.weight+nbrFeat.weight)
175 newWeight = (featI.weight+nbrFeat.weight)/2
176 elif mergeMethod==MergeMethod.Average:
177 newPos = featI.GetPos()+nbrFeat.GetPos()
178 newPos /= 2
179 newWeight = (featI.weight+nbrFeat.weight)/2
180 elif mergeMethod==MergeMethod.UseLarger:
181 if featI.weight>nbrFeat.weight:
182 newPos=featI.GetPos()
183 newWeight = featI.weight
184 else:
185 newPos=nbrFeat.GetPos()
186 newWeight = nbrFeat.weight
187 else:
188 raise ValueError("bad mergeMethod")
189
190 featI.SetPos(newPos)
191 featI.weight = newWeight
192
193
194
195 featsToRemove.append(nbr)
196 featsInPlay.remove(fi)
197 featsInPlay.remove(nbr)
198 for nbrList in distOrders:
199 try:
200 nbrList.remove(fi)
201 except ValueError:
202 pass
203 try:
204 nbrList.remove(nbr)
205 except ValueError:
206 pass
207 else:
208
209 break
210 featsToRemove.sort()
211 for i,fIdx in enumerate(featsToRemove):
212 fm.DropFeature(fIdx-i)
213 return res
214
226
227
228
229
230
232 import doctest,sys
233 return doctest.testmod(sys.modules["__main__"])
234
235 if __name__ == '__main__':
236 import sys
237 failed,tried = _test()
238 sys.exit(failed)
239