Chem.FeatMaps.FeatMapUtils

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