Chem.FeatMaps.FeatMapUtils

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

# Source Code for Module Chem.FeatMaps.FeatMapUtils

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