Package rdkit :: Package Chem :: Package FeatMaps :: Module FeatMapUtils

[hide private]
[frames] | no frames]

# Source Code for Module rdkit.Chem.FeatMaps.FeatMapUtils

      1  ## Automatically adapted for numpy.oldnumeric Jun 27, 2008 by -c
      2
      3  # $Id$
      4  #
      5  # Copyright (C) 2006 Greg Landrum
      6  #
      7  #   @@ All Rights Reserved @@
      8  #  This file is part of the RDKit.
      9  #  The contents are covered by the terms of the BSD license
     10  #  which is included in the file license.txt, found at the root
     11  #  of the RDKit source tree.
     12  #
     13  import copy
     14  from rdkit.six.moves import range
     15  from rdkit.Chem.FeatMaps import FeatMaps
     16
     17 -class MergeMethod(object):
     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
     33 -class MergeMetric(object):
     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
     43 -class DirMergeMode(object):
     44    NoMerge=0
     45    """ Do not merge directions (i.e. keep all direction vectors) """
     46
     47    Sum=1
     48    """ Sum direction vectors """
     49
     50 -def __copyAll(res,fm1,fm2):
     51    """ no user-serviceable parts inside """
     52    for feat in fm1.GetFeatures():
     53      res.AddFeatPoint(copy.deepcopy(feat))
     54    for feat in fm2.GetFeatures():
     55      res.AddFeatPoint(copy.deepcopy(feat))
     56
     57
     58 -def GetFeatFeatDistMatrix(fm,mergeMetric,mergeTol,dirMergeMode,compatFunc):
     59    """
     60
     61      NOTE that mergeTol is a max value for merging when using distance-based
     62      merging and a min value when using score-based merging.
     63
     64    """
     65    dists = [[1e8]*fm.GetNumFeatures() for x in range(fm.GetNumFeatures())]
     66    if mergeMetric==MergeMetric.NoMerge:
     67      return dists
     68    elif mergeMetric==MergeMetric.Distance:
     69      mergeTol2 = mergeTol*mergeTol
     70      for i in range(fm.GetNumFeatures()):
     71        ptI = fm.GetFeature(i)
     72        for j in range(i+1,fm.GetNumFeatures()):
     73          ptJ = fm.GetFeature(j)
     74          if compatFunc(ptI,ptJ):
     75            dist2 = ptI.GetDist2(ptJ)
     76            if dist2<mergeTol2:
     77              dists[i][j]=dist2
     78              dists[j][i]=dist2
     79    elif mergeMetric==MergeMetric.Overlap:
     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            score = fm.GetFeatFeatScore(ptI,ptJ,typeMatch=False)
     86            score *= -1*ptJ.weight
     87            if score<mergeTol:
     88              dists[i][j]=score
     89              dists[j][i]=score
     90    else:
     91      raise ValueError('unrecognized mergeMetric')
     92
     93    return dists
     94
     95 -def familiesMatch(f1,f2):
     96    return f1.GetFamily()==f2.GetFamily()
     97
     98 -def feq(v1,v2,tol=1e-4):
     99    return abs(v1-v2)<tol
    100
    101 -def MergeFeatPoints(fm,mergeMetric=MergeMetric.NoMerge,mergeTol=1.5,
    102                      dirMergeMode=DirMergeMode.NoMerge,
    103                      mergeMethod=MergeMethod.WeightedAverage,
    104                      compatFunc=familiesMatch):
    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    #print 'distOrders:'
    127    #print distOrders
    128
    129    # we now know the "distances" and have rank-ordered list of
    130    # each point's neighbors. Work with that.
    131
    132    # progressively merge nearest neighbors until there
    133    # are no more points left to merge
    134    featsInPlay=list(range(fm.GetNumFeatures()))
    135    featsToRemove = []
    136    #print '--------------------------------'
    137    while featsInPlay:
    138      # find two features who are mutual nearest neighbors:
    139      fipCopy=featsInPlay[:]
    140      for fi in fipCopy:
    141        #print '>>>',fi,fipCopy,featsInPlay
    142        #print '\t',distOrders[fi]
    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          #print 'direct:',fi,nbr
    152          mergeThem=True
    153        else:
    154          # it may be that there are several points at about the same distance,
    155          # check for that now
    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                  #print 'indirect: ',fi,nbr
    161                  mergeThem=True
    162                  break
    163              else:
    164                break
    165        #print '    bottom:',mergeThem
    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        # nbr and fi are no longer valid targets:
    194        #print 'nbr done:',nbr,featsToRemove,featsInPlay
    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        #print ">>>> Nothing found, abort"
    209        break
    210    featsToRemove.sort()
    211    for i,fIdx in enumerate(featsToRemove):
    212      fm.DropFeature(fIdx-i)
    213    return res
    214
    215 -def CombineFeatMaps(fm1,fm2,mergeMetric=MergeMetric.NoMerge,mergeTol=1.5,
    216                      dirMergeMode=DirMergeMode.NoMerge):
    217    """
    218       the parameters will be taken from fm1
    219    """
    220    res = FeatMaps.FeatMap(params=fm1.params)
    221
    222    __copyAll(res,fm1,fm2)
    223    if mergeMetric!=MergeMetric.NoMerge:
    224      MergeFeatPoints(res,mergeMetric=mergeMetric,mergeTol=mergeTol)
    225    return res
    226
    227  #------------------------------------
    228  #
    229  #  doctest boilerplate
    230  #
    231 -def _test():
    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