1
2
3
4
5
6
7
8
9
10
11 import copy
12 from rdkit.Chem.FeatMaps import FeatMaps
28
31
32 NoMerge = 0
33
34 Distance = 1
35
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
46
47 NoMerge = 0
48
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
64
101
104 return f1.GetFamily() == f2.GetFamily()
105
106
107 -def feq(v1, v2, tol=1e-4):
108 return abs(v1 - v2) < tol
109
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
140
141
142
143
144
145
146
147 featsInPlay = list(range(fm.GetNumFeatures()))
148 featsToRemove = []
149
150 while featsInPlay:
151
152 fipCopy = featsInPlay[:]
153 for fi in fipCopy:
154
155
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
165 mergeThem = True
166 else:
167
168
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
174 mergeThem = True
175 break
176 else:
177 break
178
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
206
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
221 break
222 featsToRemove.sort()
223 for i, fIdx in enumerate(featsToRemove):
224 fm.DropFeature(fIdx - i)
225 return res
226
239