<filename>rdkit/Chem/UnitTestGraphDescriptors.2.py
# $Id$
#
#  Copyright (C) 2003-2006  Rational Discovery LLC
#
#   @@ All Rights Reserved @@
#  This file is part of the RDKit.
#  The contents are covered by the terms of the BSD license
#  which is included in the file license.txt, found at the root
#  of the RDKit source tree.
#
"""unit testing code for graph-theoretical descriptors

"""
from __future__ import print_function
from rdkit import RDConfig
import unittest, os.path
from rdkit import Chem
from rdkit.Chem import GraphDescriptors, MolSurf, Lipinski, Crippen


def feq(n1, n2, tol=1e-4):
  return abs(n1 - n2) <= tol


class TestCase(unittest.TestCase):

  def setUp(self):
    if doLong:
      print('\n%s: ' % self.shortDescription(), end='')

  def testBertzCTShort(self):
    """ test calculation of Bertz 'C(T)' index

   """
    data = [('C=CC=C', 21.01955), ('O=CC=O', 25.01955), ('FCC(=O)CF', 46.7548875),
            ('O=C1C=CC(=O)C=C1', 148.705216), ('C12C(F)=C(O)C(F)C1C(F)=C(O)C(F)2', 315.250442),
            ('C12CC=CCC1C(=O)C3CC=CCC3C(=O)2', 321.539522)]

    for smi, CT in data:
      m = Chem.MolFromSmiles(smi)
      newCT = GraphDescriptors.BertzCT(m, forceDMat=1)
      assert feq(newCT, CT, 1e-3), 'mol %s (CT calc = %f) should have CT = %f' % (smi, newCT, CT)

  def _testBertzCTLong(self):
    """ test calculation of Bertz 'C(T)' index

    NOTE: this is a backwards compatibility test, because of the changes
      w.r.t. the treatment of aromatic atoms in the new version, we need
      to ignore molecules with aromatic rings...

     
   """
    col = 1
    with open(os.path.join(RDConfig.RDCodeDir, 'Chem', 'test_data', 'PP_descrs_regress.2.csv'),
              'r') as inF:
      lineNum = 0
      for line in inF:
        lineNum += 1
        if line[0] != '#':
          splitL = line.split(',')
          smi = splitL[0]
          m = Chem.MolFromSmiles(smi)

          assert m, 'line %d, smiles: %s' % (lineNum, smi)
          useIt = 1
          for atom in m.GetAtoms():
            if atom.GetIsAromatic():
              useIt = 0
              break
          if useIt:
            tgtVal = float(splitL[col])
            try:
              val = GraphDescriptors.BertzCT(m)
            except Exception:
              val = 666
            assert feq(val, tgtVal, 1e-4), 'line %d, mol %s (CT calc = %f) should have CT = %f' % (
              lineNum, smi, val, tgtVal)

  def __testDesc(self, fileN, col, func):
    with open(os.path.join(RDConfig.RDCodeDir, 'Chem', 'test_data', fileN), 'r') as inF:
      lineNum = 0
      for line in inF:
        lineNum += 1
        if line[0] != '#':
          splitL = line.split(',')
          smi = splitL[0]
          m = Chem.MolFromSmiles(smi)
          assert m, 'line %d, smiles: %s' % (lineNum, smi)
          useIt = 1
          if useIt:
            tgtVal = float(splitL[col])
            if not feq(tgtVal, 666.0):
              try:
                val = func(m)
              except Exception:
                val = 666
              assert feq(val, tgtVal, 1e-4), 'line %d, mol %s (calc = %f) should have val = %f' % (
                lineNum, smi, val, tgtVal)

  def testChi0Long(self):
    """ test calculation of Chi0
     
   """
    col = 2
    self.__testDesc('PP_descrs_regress.csv', col, GraphDescriptors.Chi0)

  def _testChi0Long2(self):
    """ test calculation of Chi0
     
   """
    col = 2
    self.__testDesc('PP_descrs_regress.2.csv', col, GraphDescriptors.Chi0)

  def testHallKierAlphaLong(self):
    """ test calculation of the Hall-Kier Alpha value
     
   """
    col = 3
    self.__testDesc('PP_descrs_regress.csv', col, GraphDescriptors.HallKierAlpha)

  def _testHallKierAlphaLong2(self):
    """ test calculation of the Hall-Kier Alpha value
     
   """
    col = 3
    self.__testDesc('PP_descrs_regress.2.csv', col, GraphDescriptors.HallKierAlpha)

  def testIpc(self):
    """ test calculation of Ipc.

   """
    data = [('CCCCC', 1.40564, 11.24511), ('CCC(C)C', 1.37878, 9.65148),
            ('CC(C)(C)C', 0.72193, 3.60964), ('CN(CC)CCC', 1.67982, 31.91664),
            ('C1CCCCC1', 1.71997, 34.39946), ('CC1CCCCC1', 1.68562, 47.19725),
            ('Cc1ccccc1', 1.68562, 47.19725), ('CC(C)=C(C)C', 1.36096, 13.60964),
            ('C#N', 1.00000, 2.00000), ('OC#N', 0.91830, 2.75489)]
    for smi, res1, res2 in data:
      m = Chem.MolFromSmiles(smi)
      Ipc = GraphDescriptors.Ipc(m, forceDMat=1)
      Ipc_avg = GraphDescriptors.Ipc(m, avg=1, forceDMat=1)
      assert feq(Ipc_avg, res1, 1e-3), 'mol %s (Ipc_avg=%f) should have Ipc_avg=%f' % (smi, Ipc_avg,
                                                                                       res1)
      assert feq(Ipc, res2, 1e-3), 'mol %s (Ipc=%f) should have Ipc=%f' % (smi, Ipc, res2)
      Ipc = GraphDescriptors.Ipc(m)
      Ipc_avg = GraphDescriptors.Ipc(m, avg=1)
      assert feq(Ipc_avg, res1, 1e-3), '2nd pass: mol %s (Ipc_avg=%f) should have Ipc_avg=%f' % (
        smi, Ipc_avg, res1)
      assert feq(Ipc, res2, 1e-3), '2nd pass: mol %s (Ipc=%f) should have Ipc=%f' % (smi, Ipc, res2)

  def _testIpcLong(self):
    """ test calculation of Ipc
     
   """
    col = 4
    self.__testDesc('PP_descrs_regress.csv', col, GraphDescriptors.Ipc)

  def _testIpcLong2(self):
    """ test calculation of Ipc
     
   """
    col = 4
    self.__testDesc('PP_descrs_regress.2.csv', col, GraphDescriptors.Ipc)

  def testKappa1(self):
    """ test calculation of the Hall-Kier kappa1 value

     corrected data from Tables 3 and 6 of Rev. Comp. Chem. vol 2, 367-422, (1991)

    """
    data = [('C12CC2C3CC13', 2.344), ('C1CCC12CC2', 3.061), ('C1CCCCC1', 4.167), ('CCCCCC', 6.000),
            ('CCC(C)C1CCC(C)CC1', 9.091), ('CC(C)CC1CCC(C)CC1', 9.091),
            ('CC(C)C1CCC(C)CCC1', 9.091)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      kappa = GraphDescriptors.Kappa1(m)
      assert feq(kappa, res, 1e-3), 'mol %s (kappa1=%f) should have kappa1=%f' % (smi, kappa, res)

  def testKappa2(self):
    """ test calculation of the Hall-Kier kappa2 value

     corrected data from Tables 5 and 6 of Rev. Comp. Chem. vol 2, 367-422, (1991)

    """
    data = [
      ('[C+2](C)(C)(C)(C)(C)C', 0.667), ('[C+](C)(C)(C)(C)(CC)', 1.240),
      ('C(C)(C)(C)(CCC)', 2.3444), ('CC(C)CCCC', 4.167), ('CCCCCCC', 6.000), ('CCCCCC', 5.000),
      ('CCCCCCC', 6.000), ('C1CCCC1', 1.440), ('C1CCCC1C', 1.633), ('C1CCCCC1', 2.222),
      ('C1CCCCCC1', 3.061), ('CCCCC', 4.00), ('CC=CCCC', 4.740), ('C1=CN=CN1', 0.884),
      ('c1ccccc1', 1.606), ('c1cnccc1', 1.552), ('n1ccncc1', 1.500), ('CCCCF', 3.930),
      ('CCCCCl', 4.290), ('CCCCBr', 4.480), ('CCC(C)C1CCC(C)CC1', 4.133),
      ('CC(C)CC1CCC(C)CC1', 4.133), ('CC(C)C1CCC(C)CCC1', 4.133)
    ]
    for smi, res in data:
      #print smi
      m = Chem.MolFromSmiles(smi)
      kappa = GraphDescriptors.Kappa2(m)
      assert feq(kappa, res, 1e-3), 'mol %s (kappa2=%f) should have kappa2=%f' % (smi, kappa, res)

  def testKappa3(self):
    """ test calculation of the Hall-Kier kappa3 value

     corrected data from Tables 3 and 6 of Rev. Comp. Chem. vol 2, 367-422, (1991)

    """
    data = [
      ('C[C+](C)(C)(C)C(C)(C)C', 2.000), ('CCC(C)C(C)(C)(CC)', 2.380), ('CCC(C)CC(C)CC', 4.500),
      ('CC(C)CCC(C)CC', 5.878), ('CC(C)CCCC(C)C', 8.000), ('CCC(C)C1CCC(C)CC1', 2.500),
      ('CC(C)CC1CCC(C)CC1', 3.265), ('CC(C)C1CCC(C)CCC1', 2.844)
    ]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      kappa = GraphDescriptors.Kappa3(m)
      assert feq(kappa, res, 1e-3), 'mol %s (kappa3=%f) should have kappa3=%f' % (smi, kappa, res)

  def testKappa3Long(self):
    """ test calculation of kappa3
     
   """
    col = 5
    self.__testDesc('PP_descrs_regress.csv', col, GraphDescriptors.Kappa3)

  def _testKappa3Long2(self):
    """ test calculation of kappa3
     
   """
    col = 5
    self.__testDesc('PP_descrs_regress.2.csv', col, GraphDescriptors.Kappa3)

  def _testLabuteASALong(self):
    """ test calculation of Labute's ASA value
     
   """
    col = 6
    self.__testDesc('PP_descrs_regress.csv', col, lambda x: MolSurf.LabuteASA(x, includeHs=1))

  def _testLabuteASALong2(self):
    """ test calculation of Labute's ASA value
     
   """
    col = 6
    self.__testDesc('PP_descrs_regress.2.csv', col, lambda x: MolSurf.LabuteASA(x, includeHs=1))

  def _testTPSAShortNCI(self):
    " Short TPSA test "
    inName = RDConfig.RDDataDir + '/NCI/first_200.tpsa.csv'
    with open(inName, 'r') as inF:
      lines = inF.readlines()
    for line in lines:
      if line[0] != '#':
        line.strip()
        smi, ans = line.split(',')
        ans = float(ans)

        mol = Chem.MolFromSmiles(smi)

        calc = MolSurf.TPSA(mol)
        assert feq(calc, ans), 'bad TPSA for SMILES %s (%.2f != %.2f)' % (smi, calc, ans)

  def _testTPSALongNCI(self):
    " Long TPSA test "
    fileN = 'tpsa_regr.csv'
    with open(os.path.join(RDConfig.RDCodeDir, 'Chem', 'test_data', fileN), 'r') as inF:
      lines = inF.readlines()
    lineNo = 0
    for line in lines:
      lineNo += 1
      if line[0] != '#':
        line.strip()
        smi, ans = line.split(',')
        ans = float(ans)

        mol = Chem.MolFromSmiles(smi)
        assert mol, "line %d, failed for smiles: %s" % (lineNo, smi)

        calc = MolSurf.TPSA(mol)
        assert feq(calc, ans), 'line %d: bad TPSA for SMILES %s (%.2f != %.2f)' % (lineNo, smi,
                                                                                   calc, ans)

  def testTPSALong(self):
    """ test calculation of TPSA
     
   """
    col = 28
    self.__testDesc('PP_descrs_regress.csv', col, MolSurf.TPSA)

  def _testTPSALong2(self):
    """ test calculation of TPSA
     
   """
    col = 28
    self.__testDesc('PP_descrs_regress.2.csv', col, MolSurf.TPSA)

  def _testLipinskiLong(self):
    """ test calculation of Lipinski params
     
   """
    fName = 'PP_descrs_regress.csv'
    # we can't do H Acceptors for these pyridine-containing molecules
    #  because the values will be wrong for EVERY one.
    #col = 29
    #self.__testDesc(fName,col,Lipinski.NumHAcceptors)

    col = 30
    self.__testDesc(fName, col, Lipinski.NumHDonors)

    col = 31
    self.__testDesc(fName, col, Lipinski.NumHeteroatoms)

    col = 32
    self.__testDesc(fName, col, Lipinski.NumRotatableBonds)

  def _testHAcceptorsLong(self):
    """ test calculation of Lipinski params
     
   """
    fName = 'Block_regress.Lip.csv'
    col = 1
    self.__testDesc(fName, col, Lipinski.NumHAcceptors)

  def _testHDonorsLong(self):
    """ test calculation of Lipinski params
     
   """
    fName = 'Block_regress.Lip.csv'
    col = 2
    self.__testDesc(fName, col, Lipinski.NumHDonors)

  def _testHeterosLong(self):
    """ test calculation of Lipinski params
     
   """
    fName = 'Block_regress.Lip.csv'
    col = 3
    self.__testDesc(fName, col, Lipinski.NumHeteroatoms)

  def _testRotBondsLong(self):
    """ test calculation of Lipinski params
     
   """
    fName = 'Block_regress.Lip.csv'
    col = 4
    self.__testDesc(fName, col, Lipinski.NumRotatableBonds)

  def _testLogPLong(self):
    """ test calculation of Lipinski params
     
   """
    fName = 'PP_descrs_regress.csv'
    col = 33
    self.__testDesc(fName, col, lambda x: Crippen.MolLogP(x, includeHs=1))

  def _testLogPLong2(self):
    """ test calculation of Lipinski params
     
   """
    fName = 'PP_descrs_regress.2.csv'
    col = 33
    self.__testDesc(fName, col, lambda x: Crippen.MolLogP(x, includeHs=1))

  def _testMOELong(self):
    """ test calculation of MOE-type descriptors
     
   """
    fName = 'PP_descrs_regress.VSA.csv'
    col = 1
    self.__testDesc(fName, col, MolSurf.SMR_VSA1)
    col = 2
    self.__testDesc(fName, col, MolSurf.SMR_VSA10)
    col = 3
    self.__testDesc(fName, col, MolSurf.SMR_VSA2)
    col = 4
    self.__testDesc(fName, col, MolSurf.SMR_VSA3)
    col = 5
    self.__testDesc(fName, col, MolSurf.SMR_VSA4)
    col = 6
    self.__testDesc(fName, col, MolSurf.SMR_VSA5)
    col = 7
    self.__testDesc(fName, col, MolSurf.SMR_VSA6)
    col = 8
    self.__testDesc(fName, col, MolSurf.SMR_VSA7)
    col = 9
    self.__testDesc(fName, col, MolSurf.SMR_VSA8)
    col = 10
    self.__testDesc(fName, col, MolSurf.SMR_VSA9)
    col = 11
    self.__testDesc(fName, col, MolSurf.SlogP_VSA1)
    col = 12
    self.__testDesc(fName, col, MolSurf.SlogP_VSA10)
    col = 13
    self.__testDesc(fName, col, MolSurf.SlogP_VSA11)
    col = 14
    self.__testDesc(fName, col, MolSurf.SlogP_VSA12)

  def _testMOELong2(self):
    """ test calculation of MOE-type descriptors
     
   """
    fName = 'PP_descrs_regress.VSA.2.csv'
    col = 1
    self.__testDesc(fName, col, MolSurf.SMR_VSA1)
    col = 2
    self.__testDesc(fName, col, MolSurf.SMR_VSA10)
    col = 11
    self.__testDesc(fName, col, MolSurf.SlogP_VSA1)
    col = 12
    self.__testDesc(fName, col, MolSurf.SlogP_VSA10)
    col = 13
    self.__testDesc(fName, col, MolSurf.SlogP_VSA11)
    col = 14
    self.__testDesc(fName, col, MolSurf.SlogP_VSA12)

  def testBalabanJ(self):
    """ test calculation of the Balaban J value 

      J values are from Balaban's paper and have had roundoff
      errors and typos corrected.
    """
    data = [  # alkanes
      ('CC', 1.0),
      ('CCC', 1.6330),
      ('CCCC', 1.9747),
      ('CC(C)C', 2.3238),
      ('CCCCC', 2.1906),
      ('CC(C)CC', 2.5396),
      ('CC(C)(C)C', 3.0237),
      ('CCCCCC', 2.3391),
      ('CC(C)CCC', 2.6272),
      ('CCC(C)CC', 2.7542),
      ('CC(C)(C)CC', 3.1685),
      ('CC(C)C(C)C', 2.9935),

      # cycloalkanes
      ('C1CCCCC1', 2.0000),
      ('C1C(C)CCCC1', 2.1229),
      ('C1C(CC)CCCC1', 2.1250),
      ('C1C(C)C(C)CCC1', 2.2794),
      ('C1C(C)CC(C)CC1', 2.2307),
      ('C1C(C)CCC(C)C1', 2.1924),
      ('C1C(CCC)CCCC1', 2.0779),
      ('C1C(C(C)C)CCCC1', 2.2284),
      ('C1C(CC)C(C)CCC1', 2.2973),
      ('C1C(CC)CC(C)CC1', 2.2317),
      ('C1C(CC)CCC(C)C1', 2.1804),
      ('C1C(C)C(C)C(C)CC1', 2.4133),
      ('C1C(C)C(C)CC(C)C1', 2.3462),
      ('C1C(C)CC(C)CC1(C)', 2.3409),
      # aromatics
      ('c1ccccc1', 3.0000),
      ('c1c(C)cccc1', 3.0215),
      ('c1c(CC)cccc1', 2.8321),
      ('c1c(C)c(C)ccc1', 3.1349),
      ('c1c(C)cc(C)cc1', 3.0777),
      ('c1c(C)ccc(C)c1', 3.0325),
      ('c1c(CCC)cccc1', 2.6149),
      ('c1c(C(C)C)cccc1', 2.8483),
      ('c1c(CC)c(C)ccc1', 3.0065),
      ('c1c(CC)cc(C)cc1', 2.9369),
      ('c1c(CC)ccc(C)c1', 2.8816),
      ('c1c(C)c(C)c(C)cc1', 3.2478),
      ('c1c(C)c(C)cc(C)c1', 3.1717),
      ('c1c(C)cc(C)cc1(C)', 3.1657)
    ]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      j = GraphDescriptors.BalabanJ(m, forceDMat=1)
      assert feq(j, res), 'mol %s (J=%f) should have J=%f' % (smi, j, res)
      j = GraphDescriptors.BalabanJ(m)
      assert feq(j, res), 'second pass: mol %s (J=%f) should have J=%f' % (smi, j, res)

  def _testBalabanJLong(self):
    """ test calculation of the balaban j value
     
   """
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 1
    self.__testDesc(fName, col, GraphDescriptors.BalabanJ)

  def _testKappa1Long(self):
    """ test calculation of kappa1
     
   """
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 31
    self.__testDesc(fName, col, GraphDescriptors.Kappa1)

  def _testKappa2Long(self):
    """ test calculation of kappa2
     
   """
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 32
    self.__testDesc(fName, col, GraphDescriptors.Kappa2)

  def _testChi0Long(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 5
    self.__testDesc(fName, col, GraphDescriptors.Chi0)

  def _testChi1Long(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 8
    self.__testDesc(fName, col, GraphDescriptors.Chi1)

  def _testChi0v(self):
    """ test calculation of Chi0v

    """
    data = [('CCCCCC', 4.828), ('CCC(C)CC', 4.992), ('CC(C)CCC', 4.992), ('CC(C)C(C)C', 5.155),
            ('CC(C)(C)CC', 5.207), ('CCCCCO', 4.276), ('CCC(O)CC', 4.439), ('CC(O)(C)CC', 4.654),
            ('c1ccccc1O', 3.834), ('CCCl', 2.841), ('CCBr', 3.671), ('CCI', 4.242)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi0v(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi0v=%f) should have Chi0V=%f' % (smi, chi, res)

  def _testChi0vLong(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 7
    self.__testDesc(fName, col, GraphDescriptors.Chi0v)

  def testChi1v(self):
    """ test calculation of Chi1v

    """
    data = [('CCCCCC', 2.914), ('CCC(C)CC', 2.808), ('CC(C)CCC', 2.770), ('CC(C)C(C)C', 2.643),
            ('CC(C)(C)CC', 2.561), ('CCCCCO', 2.523), ('CCC(O)CC', 2.489), ('CC(O)(C)CC', 2.284),
            ('c1ccccc1O', 2.134)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi1v(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi1v=%f) should have Chi1V=%f' % (smi, chi, res)

  def _testChi1vLong(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 10
    self.__testDesc(fName, col, GraphDescriptors.Chi1v)

  def testPathCounts(self):
    """ FIX: this should be in some other file

    """
    data = [('CCCCCC', (6, 5, 4, 3, 2, 1)),
            ('CCC(C)CC', (6, 5, 5, 4, 1, 0)),
            ('CC(C)CCC', (6, 5, 5, 3, 2, 0)),
            ('CC(C)C(C)C', (6, 5, 6, 4, 0, 0)),
            ('CC(C)(C)CC', (6, 5, 7, 3, 0, 0)),
            ('CCCCCO', (6, 5, 4, 3, 2, 1)),
            ('CCC(O)CC', (6, 5, 5, 4, 1, 0)),
            ('CC(O)(C)CC', (6, 5, 7, 3, 0, 0)),
            ('c1ccccc1O', (7, 7, 8, 8, 8, 8)), ]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      for i in range(1, 6):
        cnt = len(Chem.FindAllPathsOfLengthN(m, i, useBonds=1))
        assert cnt == res[i], (smi, i, cnt, res[i], Chem.FindAllPathsOfLengthN(m, i, useBonds=1))
        cnt = len(Chem.FindAllPathsOfLengthN(m, i + 1, useBonds=0))
        assert cnt == res[i], (smi, i, cnt, res[i], Chem.FindAllPathsOfLengthN(m, i + 1,
                                                                               useBonds=1))

  def testChi2v(self):
    """ test calculation of Chi2v

    """
    data = [('CCCCCC', 1.707),
            ('CCC(C)CC', 1.922),
            ('CC(C)CCC', 2.183),
            ('CC(C)C(C)C', 2.488),
            ('CC(C)(C)CC', 2.914),
            ('CCCCCO', 1.431),
            ('CCC(O)CC', 1.470),
            ('CC(O)(C)CC', 2.166),
            ('c1ccccc1O', 1.336), ]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi2v(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi2v=%f) should have Chi2V=%f' % (smi, chi, res)

  def _testChi2vLong(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 12
    self.__testDesc(fName, col, GraphDescriptors.Chi2v)

  def testChi3v(self):
    """ test calculation of Chi3v

    """
    data = [('CCCCCC', 0.957), ('CCC(C)CC', 1.394), ('CC(C)CCC', 0.866), ('CC(C)C(C)C', 1.333),
            ('CC(C)(C)CC', 1.061), ('CCCCCO', 0.762), ('CCC(O)CC', 0.943), ('CC(O)(C)CC', 0.865),
            ('c1ccccc1O', 0.756)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi3v(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi3v=%f) should have Chi3V=%f' % (smi, chi, res)

  def _testChi3vLong(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 14
    self.__testDesc(fName, col, GraphDescriptors.Chi3v)

  def testChi4v(self):
    """ test calculation of Chi4v

    """
    data = [('CCCCCC', 0.500), ('CCC(C)CC', 0.289), ('CC(C)CCC', 0.577), ('CC(C)C(C)C', 0.000),
            ('CC(C)(C)CC', 0.000), ('CCCCCO', 0.362), ('CCC(O)CC', 0.289), ('CC(O)(C)CC', 0.000),
            ('c1ccccc1O', 0.428)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi4v(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi4v=%f) should have Chi4V=%f' % (smi, chi, res)

  def testChi5v(self):
    """ test calculation of Chi5v

    """
    data = [('CCCCCC', 0.250), ('CCC(C)CC', 0.000), ('CC(C)CCC', 0.000), ('CC(C)C(C)C', 0.000),
            ('CC(C)(C)CC', 0.000), ('CCCCCO', 0.112), ('CCC(O)CC', 0.000), ('CC(O)(C)CC', 0.000),
            ('c1ccccc1O', 0.242)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.ChiNv_(m, 5)
      assert feq(chi, res, 1e-3), 'mol %s (Chi5v=%f) should have Chi5V=%f' % (smi, chi, res)

  def testChi0n(self):
    """ test calculation of Chi0n

    """
    data = [('CCCCCC', 4.828),
            ('CCC(C)CC', 4.992),
            ('CC(C)CCC', 4.992),
            ('CC(C)C(C)C', 5.155),
            ('CC(C)(C)CC', 5.207),
            ('CCCCCO', 4.276),
            ('CCC(O)CC', 4.439),
            ('CC(O)(C)CC', 4.654),
            ('c1ccccc1O', 3.834),
            ('CCCl', 2.085),
            ('CCBr', 2.085),
            ('CCI', 2.085), ]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi0n(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi0n=%f) should have Chi0n=%f' % (smi, chi, res)

  def _testChi0nLong(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 6
    self.__testDesc(fName, col, GraphDescriptors.Chi0n)

  def testChi1n(self):
    """ test calculation of Chi1n

    """
    data = [('CCCCCC', 2.914), ('CCC(C)CC', 2.808), ('CC(C)CCC', 2.770), ('CC(C)C(C)C', 2.643),
            ('CC(C)(C)CC', 2.561), ('CCCCCO', 2.523), ('CCC(O)CC', 2.489), ('CC(O)(C)CC', 2.284),
            ('c1ccccc1O', 2.134)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi1n(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi1n=%f) should have Chi1N=%f' % (smi, chi, res)

  def _testChi1nLong(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 9
    self.__testDesc(fName, col, GraphDescriptors.Chi1n)

  def testChi2n(self):
    """ test calculation of Chi2n

    """
    data = [('CCCCCC', 1.707), ('CCC(C)CC', 1.922), ('CC(C)CCC', 2.183), ('CC(C)C(C)C', 2.488),
            ('CC(C)(C)CC', 2.914), ('CCCCCO', 1.431), ('CCC(O)CC', 1.470), ('CC(O)(C)CC', 2.166),
            ('c1ccccc1O', 1.336)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi2n(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi2n=%f) should have Chi2N=%f' % (smi, chi, res)

  def _testChi2nLong(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 11
    self.__testDesc(fName, col, GraphDescriptors.Chi2n)

  def testChi3n(self):
    """ test calculation of Chi3n

    """
    data = [('CCCCCC', 0.957), ('CCC(C)CC', 1.394), ('CC(C)CCC', 0.866), ('CC(C)C(C)C', 1.333),
            ('CC(C)(C)CC', 1.061), ('CCCCCO', 0.762), ('CCC(O)CC', 0.943), ('CC(O)(C)CC', 0.865),
            ('c1ccccc1O', 0.756)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi3n(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi3n=%f) should have Chi3N=%f' % (smi, chi, res)

  def _testChi3nLong(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 13
    self.__testDesc(fName, col, GraphDescriptors.Chi3n)

  def testChi4n(self):
    """ test calculation of Chi4n

    """
    data = [('CCCCCC', 0.500), ('CCC(C)CC', 0.289), ('CC(C)CCC', 0.577), ('CC(C)C(C)C', 0.000),
            ('CC(C)(C)CC', 0.000), ('CCCCCO', 0.362), ('CCC(O)CC', 0.289), ('CC(O)(C)CC', 0.000),
            ('c1ccccc1O', 0.428)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi4n(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi4n=%f) should have Chi4N=%f' % (smi, chi, res)

  def testIssue125(self):
    """ test an issue with calculating BalabanJ
    """
    smi = 'O=C(OC)C1=C(C)NC(C)=C(C(OC)=O)C1C2=CC=CC=C2[N+]([O-])=O'
    m1 = Chem.MolFromSmiles(smi)
    m2 = Chem.MolFromSmiles(smi)
    Chem.MolToSmiles(m1)
    j1 = GraphDescriptors.BalabanJ(m1)
    j2 = GraphDescriptors.BalabanJ(m2)
    assert feq(j1, j2)

  def testOrderDepend(self):
    """ test order dependence of some descriptors:
    """
    data = [('C=CC=C', 21.01955, 2.73205), ('O=CC=O', 25.01955, 2.73205),
            ('FCC(=O)CF', 46.7548875, 2.98816), ('O=C1C=CC(=O)C=C1', 148.705216, 2.8265),
            ('C12C(F)=C(O)C(F)C1C(F)=C(O)C(F)2', 315.250442, 2.4509),
            ('C12CC=CCC1C(=O)C3CC=CCC3C(=O)2', 321.539522, 1.95986)]

    for smi, CT, bal in data:
      m = Chem.MolFromSmiles(smi)
      newBal = GraphDescriptors.BalabanJ(m, forceDMat=1)
      assert feq(newBal, bal, 1e-4), 'mol %s %f!=%f' % (smi, newBal, bal)
      m = Chem.MolFromSmiles(smi)
      newCT = GraphDescriptors.BertzCT(m, forceDMat=1)
      assert feq(newCT, CT, 1e-4), 'mol %s (CT calc = %f) should have CT = %f' % (smi, newCT, CT)
      m = Chem.MolFromSmiles(smi)
      newCT = GraphDescriptors.BertzCT(m, forceDMat=1)
      assert feq(newCT, CT, 1e-4), 'mol %s (CT calc = %f) should have CT = %f' % (smi, newCT, CT)
      newBal = GraphDescriptors.BalabanJ(m, forceDMat=1)
      assert feq(newBal, bal, 1e-4), 'mol %s %f!=%f' % (smi, newBal, bal)

      m = Chem.MolFromSmiles(smi)
      newBal = GraphDescriptors.BalabanJ(m, forceDMat=1)
      assert feq(newBal, bal, 1e-4), 'mol %s %f!=%f' % (smi, newBal, bal)
      newCT = GraphDescriptors.BertzCT(m, forceDMat=1)
      assert feq(newCT, CT, 1e-4), 'mol %s (CT calc = %f) should have CT = %f' % (smi, newCT, CT)


if __name__ == '__main__':
  import sys, getopt, re
  doLong = 0
  if len(sys.argv) > 1:
    args, extras = getopt.getopt(sys.argv[1:], 'l')
    for arg, val in args:
      if arg == '-l':
        doLong = 1
      sys.argv.remove('-l')
  if doLong:
    for methName in dir(TestCase):
      if re.match('_test', methName):
        newName = re.sub('_test', 'test', methName)
        exec('TestCase.%s = TestCase.%s' % (newName, methName))

  unittest.main()

<filename>rdkit/Chem/UnitTestGraphDescriptors.2.py
# $Id$
#
#  Copyright (C) 2003-2006  Rational Discovery LLC
#
#   @@ All Rights Reserved @@
#  This file is part of the RDKit.
#  The contents are covered by the terms of the BSD license
#  which is included in the file license.txt, found at the root
#  of the RDKit source tree.
#
"""unit testing code for graph-theoretical descriptors

"""
from __future__ import print_function
from rdkit import RDConfig
import unittest, os.path
from rdkit import Chem
from rdkit.Chem import GraphDescriptors, MolSurf, Lipinski, Crippen


def feq(n1, n2, tol=1e-4):
  return abs(n1 - n2) <= tol


class TestCase(unittest.TestCase):

  def setUp(self):
    if doLong:
      print('\n%s: ' % self.shortDescription(), end='')

  def testBertzCTShort(self):
    """ test calculation of Bertz 'C(T)' index

   """
    data = [('C=CC=C', 21.01955), ('O=CC=O', 25.01955), ('FCC(=O)CF', 46.7548875),
            ('O=C1C=CC(=O)C=C1', 148.705216), ('C12C(F)=C(O)C(F)C1C(F)=C(O)C(F)2', 315.250442),
            ('C12CC=CCC1C(=O)C3CC=CCC3C(=O)2', 321.539522)]

    for smi, CT in data:
      m = Chem.MolFromSmiles(smi)
      newCT = GraphDescriptors.BertzCT(m, forceDMat=1)
      assert feq(newCT, CT, 1e-3), 'mol %s (CT calc = %f) should have CT = %f' % (smi, newCT, CT)

  def _testBertzCTLong(self):
    """ test calculation of Bertz 'C(T)' index

    NOTE: this is a backwards compatibility test, because of the changes
      w.r.t. the treatment of aromatic atoms in the new version, we need
      to ignore molecules with aromatic rings...

     
   """
    col = 1
    with open(os.path.join(RDConfig.RDCodeDir, 'Chem', 'test_data', 'PP_descrs_regress.2.csv'),
              'r') as inF:
      lineNum = 0
      for line in inF:
        lineNum += 1
        if line[0] != '#':
          splitL = line.split(',')
          smi = splitL[0]
          m = Chem.MolFromSmiles(smi)

          assert m, 'line %d, smiles: %s' % (lineNum, smi)
          useIt = 1
          for atom in m.GetAtoms():
            if atom.GetIsAromatic():
              useIt = 0
              break
          if useIt:
            tgtVal = float(splitL[col])
            try:
              val = GraphDescriptors.BertzCT(m)
            except Exception:
              val = 666
            assert feq(val, tgtVal, 1e-4), 'line %d, mol %s (CT calc = %f) should have CT = %f' % (
              lineNum, smi, val, tgtVal)

  def __testDesc(self, fileN, col, func):
    with open(os.path.join(RDConfig.RDCodeDir, 'Chem', 'test_data', fileN), 'r') as inF:
      lineNum = 0
      for line in inF:
        lineNum += 1
        if line[0] != '#':
          splitL = line.split(',')
          smi = splitL[0]
          m = Chem.MolFromSmiles(smi)
          assert m, 'line %d, smiles: %s' % (lineNum, smi)
          useIt = 1
          if useIt:
            tgtVal = float(splitL[col])
            if not feq(tgtVal, 666.0):
              try:
                val = func(m)
              except Exception:
                val = 666
              assert feq(val, tgtVal, 1e-4), 'line %d, mol %s (calc = %f) should have val = %f' % (
                lineNum, smi, val, tgtVal)

  def testChi0Long(self):
    """ test calculation of Chi0
     
   """
    col = 2
    self.__testDesc('PP_descrs_regress.csv', col, GraphDescriptors.Chi0)

  def _testChi0Long2(self):
    """ test calculation of Chi0
     
   """
    col = 2
    self.__testDesc('PP_descrs_regress.2.csv', col, GraphDescriptors.Chi0)

  def testHallKierAlphaLong(self):
    """ test calculation of the Hall-Kier Alpha value
     
   """
    col = 3
    self.__testDesc('PP_descrs_regress.csv', col, GraphDescriptors.HallKierAlpha)

  def _testHallKierAlphaLong2(self):
    """ test calculation of the Hall-Kier Alpha value
     
   """
    col = 3
    self.__testDesc('PP_descrs_regress.2.csv', col, GraphDescriptors.HallKierAlpha)

  def testIpc(self):
    """ test calculation of Ipc.

   """
    data = [('CCCCC', 1.40564, 11.24511), ('CCC(C)C', 1.37878, 9.65148),
            ('CC(C)(C)C', 0.72193, 3.60964), ('CN(CC)CCC', 1.67982, 31.91664),
            ('C1CCCCC1', 1.71997, 34.39946), ('CC1CCCCC1', 1.68562, 47.19725),
            ('Cc1ccccc1', 1.68562, 47.19725), ('CC(C)=C(C)C', 1.36096, 13.60964),
            ('C#N', 1.00000, 2.00000), ('OC#N', 0.91830, 2.75489)]
    for smi, res1, res2 in data:
      m = Chem.MolFromSmiles(smi)
      Ipc = GraphDescriptors.Ipc(m, forceDMat=1)
      Ipc_avg = GraphDescriptors.Ipc(m, avg=1, forceDMat=1)
      assert feq(Ipc_avg, res1, 1e-3), 'mol %s (Ipc_avg=%f) should have Ipc_avg=%f' % (smi, Ipc_avg,
                                                                                       res1)
      assert feq(Ipc, res2, 1e-3), 'mol %s (Ipc=%f) should have Ipc=%f' % (smi, Ipc, res2)
      Ipc = GraphDescriptors.Ipc(m)
      Ipc_avg = GraphDescriptors.Ipc(m, avg=1)
      assert feq(Ipc_avg, res1, 1e-3), '2nd pass: mol %s (Ipc_avg=%f) should have Ipc_avg=%f' % (
        smi, Ipc_avg, res1)
      assert feq(Ipc, res2, 1e-3), '2nd pass: mol %s (Ipc=%f) should have Ipc=%f' % (smi, Ipc, res2)

  def _testIpcLong(self):
    """ test calculation of Ipc
     
   """
    col = 4
    self.__testDesc('PP_descrs_regress.csv', col, GraphDescriptors.Ipc)

  def _testIpcLong2(self):
    """ test calculation of Ipc
     
   """
    col = 4
    self.__testDesc('PP_descrs_regress.2.csv', col, GraphDescriptors.Ipc)

  def testKappa1(self):
    """ test calculation of the Hall-Kier kappa1 value

     corrected data from Tables 3 and 6 of Rev. Comp. Chem. vol 2, 367-422, (1991)

    """
    data = [('C12CC2C3CC13', 2.344), ('C1CCC12CC2', 3.061), ('C1CCCCC1', 4.167), ('CCCCCC', 6.000),
            ('CCC(C)C1CCC(C)CC1', 9.091), ('CC(C)CC1CCC(C)CC1', 9.091),
            ('CC(C)C1CCC(C)CCC1', 9.091)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      kappa = GraphDescriptors.Kappa1(m)
      assert feq(kappa, res, 1e-3), 'mol %s (kappa1=%f) should have kappa1=%f' % (smi, kappa, res)

  def testKappa2(self):
    """ test calculation of the Hall-Kier kappa2 value

     corrected data from Tables 5 and 6 of Rev. Comp. Chem. vol 2, 367-422, (1991)

    """
    data = [
      ('[C+2](C)(C)(C)(C)(C)C', 0.667), ('[C+](C)(C)(C)(C)(CC)', 1.240),
      ('C(C)(C)(C)(CCC)', 2.3444), ('CC(C)CCCC', 4.167), ('CCCCCCC', 6.000), ('CCCCCC', 5.000),
      ('CCCCCCC', 6.000), ('C1CCCC1', 1.440), ('C1CCCC1C', 1.633), ('C1CCCCC1', 2.222),
      ('C1CCCCCC1', 3.061), ('CCCCC', 4.00), ('CC=CCCC', 4.740), ('C1=CN=CN1', 0.884),
      ('c1ccccc1', 1.606), ('c1cnccc1', 1.552), ('n1ccncc1', 1.500), ('CCCCF', 3.930),
      ('CCCCCl', 4.290), ('CCCCBr', 4.480), ('CCC(C)C1CCC(C)CC1', 4.133),
      ('CC(C)CC1CCC(C)CC1', 4.133), ('CC(C)C1CCC(C)CCC1', 4.133)
    ]
    for smi, res in data:
      #print smi
      m = Chem.MolFromSmiles(smi)
      kappa = GraphDescriptors.Kappa2(m)
      assert feq(kappa, res, 1e-3), 'mol %s (kappa2=%f) should have kappa2=%f' % (smi, kappa, res)

  def testKappa3(self):
    """ test calculation of the Hall-Kier kappa3 value

     corrected data from Tables 3 and 6 of Rev. Comp. Chem. vol 2, 367-422, (1991)

    """
    data = [
      ('C[C+](C)(C)(C)C(C)(C)C', 2.000), ('CCC(C)C(C)(C)(CC)', 2.380), ('CCC(C)CC(C)CC', 4.500),
      ('CC(C)CCC(C)CC', 5.878), ('CC(C)CCCC(C)C', 8.000), ('CCC(C)C1CCC(C)CC1', 2.500),
      ('CC(C)CC1CCC(C)CC1', 3.265), ('CC(C)C1CCC(C)CCC1', 2.844)
    ]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      kappa = GraphDescriptors.Kappa3(m)
      assert feq(kappa, res, 1e-3), 'mol %s (kappa3=%f) should have kappa3=%f' % (smi, kappa, res)

  def testKappa3Long(self):
    """ test calculation of kappa3
     
   """
    col = 5
    self.__testDesc('PP_descrs_regress.csv', col, GraphDescriptors.Kappa3)

  def _testKappa3Long2(self):
    """ test calculation of kappa3
     
   """
    col = 5
    self.__testDesc('PP_descrs_regress.2.csv', col, GraphDescriptors.Kappa3)

  def _testLabuteASALong(self):
    """ test calculation of Labute's ASA value
     
   """
    col = 6
    self.__testDesc('PP_descrs_regress.csv', col, lambda x: MolSurf.LabuteASA(x, includeHs=1))

  def _testLabuteASALong2(self):
    """ test calculation of Labute's ASA value
     
   """
    col = 6
    self.__testDesc('PP_descrs_regress.2.csv', col, lambda x: MolSurf.LabuteASA(x, includeHs=1))

  def _testTPSAShortNCI(self):
    " Short TPSA test "
    inName = RDConfig.RDDataDir + '/NCI/first_200.tpsa.csv'
    with open(inName, 'r') as inF:
      lines = inF.readlines()
    for line in lines:
      if line[0] != '#':
        line.strip()
        smi, ans = line.split(',')
        ans = float(ans)

        mol = Chem.MolFromSmiles(smi)

        calc = MolSurf.TPSA(mol)
        assert feq(calc, ans), 'bad TPSA for SMILES %s (%.2f != %.2f)' % (smi, calc, ans)

  def _testTPSALongNCI(self):
    " Long TPSA test "
    fileN = 'tpsa_regr.csv'
    with open(os.path.join(RDConfig.RDCodeDir, 'Chem', 'test_data', fileN), 'r') as inF:
      lines = inF.readlines()
    lineNo = 0
    for line in lines:
      lineNo += 1
      if line[0] != '#':
        line.strip()
        smi, ans = line.split(',')
        ans = float(ans)

        mol = Chem.MolFromSmiles(smi)
        assert mol, "line %d, failed for smiles: %s" % (lineNo, smi)

        calc = MolSurf.TPSA(mol)
        assert feq(calc, ans), 'line %d: bad TPSA for SMILES %s (%.2f != %.2f)' % (lineNo, smi,
                                                                                   calc, ans)

  def testTPSALong(self):
    """ test calculation of TPSA
     
   """
    col = 28
    self.__testDesc('PP_descrs_regress.csv', col, MolSurf.TPSA)

  def _testTPSALong2(self):
    """ test calculation of TPSA
     
   """
    col = 28
    self.__testDesc('PP_descrs_regress.2.csv', col, MolSurf.TPSA)

  def _testLipinskiLong(self):
    """ test calculation of Lipinski params
     
   """
    fName = 'PP_descrs_regress.csv'
    # we can't do H Acceptors for these pyridine-containing molecules
    #  because the values will be wrong for EVERY one.
    #col = 29
    #self.__testDesc(fName,col,Lipinski.NumHAcceptors)

    col = 30
    self.__testDesc(fName, col, Lipinski.NumHDonors)

    col = 31
    self.__testDesc(fName, col, Lipinski.NumHeteroatoms)

    col = 32
    self.__testDesc(fName, col, Lipinski.NumRotatableBonds)

  def _testHAcceptorsLong(self):
    """ test calculation of Lipinski params
     
   """
    fName = 'Block_regress.Lip.csv'
    col = 1
    self.__testDesc(fName, col, Lipinski.NumHAcceptors)

  def _testHDonorsLong(self):
    """ test calculation of Lipinski params
     
   """
    fName = 'Block_regress.Lip.csv'
    col = 2
    self.__testDesc(fName, col, Lipinski.NumHDonors)

  def _testHeterosLong(self):
    """ test calculation of Lipinski params
     
   """
    fName = 'Block_regress.Lip.csv'
    col = 3
    self.__testDesc(fName, col, Lipinski.NumHeteroatoms)

  def _testRotBondsLong(self):
    """ test calculation of Lipinski params
     
   """
    fName = 'Block_regress.Lip.csv'
    col = 4
    self.__testDesc(fName, col, Lipinski.NumRotatableBonds)

  def _testLogPLong(self):
    """ test calculation of Lipinski params
     
   """
    fName = 'PP_descrs_regress.csv'
    col = 33
    self.__testDesc(fName, col, lambda x: Crippen.MolLogP(x, includeHs=1))

  def _testLogPLong2(self):
    """ test calculation of Lipinski params
     
   """
    fName = 'PP_descrs_regress.2.csv'
    col = 33
    self.__testDesc(fName, col, lambda x: Crippen.MolLogP(x, includeHs=1))

  def _testMOELong(self):
    """ test calculation of MOE-type descriptors
     
   """
    fName = 'PP_descrs_regress.VSA.csv'
    col = 1
    self.__testDesc(fName, col, MolSurf.SMR_VSA1)
    col = 2
    self.__testDesc(fName, col, MolSurf.SMR_VSA10)
    col = 3
    self.__testDesc(fName, col, MolSurf.SMR_VSA2)
    col = 4
    self.__testDesc(fName, col, MolSurf.SMR_VSA3)
    col = 5
    self.__testDesc(fName, col, MolSurf.SMR_VSA4)
    col = 6
    self.__testDesc(fName, col, MolSurf.SMR_VSA5)
    col = 7
    self.__testDesc(fName, col, MolSurf.SMR_VSA6)
    col = 8
    self.__testDesc(fName, col, MolSurf.SMR_VSA7)
    col = 9
    self.__testDesc(fName, col, MolSurf.SMR_VSA8)
    col = 10
    self.__testDesc(fName, col, MolSurf.SMR_VSA9)
    col = 11
    self.__testDesc(fName, col, MolSurf.SlogP_VSA1)
    col = 12
    self.__testDesc(fName, col, MolSurf.SlogP_VSA10)
    col = 13
    self.__testDesc(fName, col, MolSurf.SlogP_VSA11)
    col = 14
    self.__testDesc(fName, col, MolSurf.SlogP_VSA12)

  def _testMOELong2(self):
    """ test calculation of MOE-type descriptors
     
   """
    fName = 'PP_descrs_regress.VSA.2.csv'
    col = 1
    self.__testDesc(fName, col, MolSurf.SMR_VSA1)
    col = 2
    self.__testDesc(fName, col, MolSurf.SMR_VSA10)
    col = 11
    self.__testDesc(fName, col, MolSurf.SlogP_VSA1)
    col = 12
    self.__testDesc(fName, col, MolSurf.SlogP_VSA10)
    col = 13
    self.__testDesc(fName, col, MolSurf.SlogP_VSA11)
    col = 14
    self.__testDesc(fName, col, MolSurf.SlogP_VSA12)

  def testBalabanJ(self):
    """ test calculation of the Balaban J value 

      J values are from Balaban's paper and have had roundoff
      errors and typos corrected.
    """
    data = [  # alkanes
      ('CC', 1.0),
      ('CCC', 1.6330),
      ('CCCC', 1.9747),
      ('CC(C)C', 2.3238),
      ('CCCCC', 2.1906),
      ('CC(C)CC', 2.5396),
      ('CC(C)(C)C', 3.0237),
      ('CCCCCC', 2.3391),
      ('CC(C)CCC', 2.6272),
      ('CCC(C)CC', 2.7542),
      ('CC(C)(C)CC', 3.1685),
      ('CC(C)C(C)C', 2.9935),

      # cycloalkanes
      ('C1CCCCC1', 2.0000),
      ('C1C(C)CCCC1', 2.1229),
      ('C1C(CC)CCCC1', 2.1250),
      ('C1C(C)C(C)CCC1', 2.2794),
      ('C1C(C)CC(C)CC1', 2.2307),
      ('C1C(C)CCC(C)C1', 2.1924),
      ('C1C(CCC)CCCC1', 2.0779),
      ('C1C(C(C)C)CCCC1', 2.2284),
      ('C1C(CC)C(C)CCC1', 2.2973),
      ('C1C(CC)CC(C)CC1', 2.2317),
      ('C1C(CC)CCC(C)C1', 2.1804),
      ('C1C(C)C(C)C(C)CC1', 2.4133),
      ('C1C(C)C(C)CC(C)C1', 2.3462),
      ('C1C(C)CC(C)CC1(C)', 2.3409),
      # aromatics
      ('c1ccccc1', 3.0000),
      ('c1c(C)cccc1', 3.0215),
      ('c1c(CC)cccc1', 2.8321),
      ('c1c(C)c(C)ccc1', 3.1349),
      ('c1c(C)cc(C)cc1', 3.0777),
      ('c1c(C)ccc(C)c1', 3.0325),
      ('c1c(CCC)cccc1', 2.6149),
      ('c1c(C(C)C)cccc1', 2.8483),
      ('c1c(CC)c(C)ccc1', 3.0065),
      ('c1c(CC)cc(C)cc1', 2.9369),
      ('c1c(CC)ccc(C)c1', 2.8816),
      ('c1c(C)c(C)c(C)cc1', 3.2478),
      ('c1c(C)c(C)cc(C)c1', 3.1717),
      ('c1c(C)cc(C)cc1(C)', 3.1657)
    ]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      j = GraphDescriptors.BalabanJ(m, forceDMat=1)
      assert feq(j, res), 'mol %s (J=%f) should have J=%f' % (smi, j, res)
      j = GraphDescriptors.BalabanJ(m)
      assert feq(j, res), 'second pass: mol %s (J=%f) should have J=%f' % (smi, j, res)

  def _testBalabanJLong(self):
    """ test calculation of the balaban j value
     
   """
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 1
    self.__testDesc(fName, col, GraphDescriptors.BalabanJ)

  def _testKappa1Long(self):
    """ test calculation of kappa1
     
   """
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 31
    self.__testDesc(fName, col, GraphDescriptors.Kappa1)

  def _testKappa2Long(self):
    """ test calculation of kappa2
     
   """
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 32
    self.__testDesc(fName, col, GraphDescriptors.Kappa2)

  def _testChi0Long(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 5
    self.__testDesc(fName, col, GraphDescriptors.Chi0)

  def _testChi1Long(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 8
    self.__testDesc(fName, col, GraphDescriptors.Chi1)

  def _testChi0v(self):
    """ test calculation of Chi0v

    """
    data = [('CCCCCC', 4.828), ('CCC(C)CC', 4.992), ('CC(C)CCC', 4.992), ('CC(C)C(C)C', 5.155),
            ('CC(C)(C)CC', 5.207), ('CCCCCO', 4.276), ('CCC(O)CC', 4.439), ('CC(O)(C)CC', 4.654),
            ('c1ccccc1O', 3.834), ('CCCl', 2.841), ('CCBr', 3.671), ('CCI', 4.242)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi0v(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi0v=%f) should have Chi0V=%f' % (smi, chi, res)

  def _testChi0vLong(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 7
    self.__testDesc(fName, col, GraphDescriptors.Chi0v)

  def testChi1v(self):
    """ test calculation of Chi1v

    """
    data = [('CCCCCC', 2.914), ('CCC(C)CC', 2.808), ('CC(C)CCC', 2.770), ('CC(C)C(C)C', 2.643),
            ('CC(C)(C)CC', 2.561), ('CCCCCO', 2.523), ('CCC(O)CC', 2.489), ('CC(O)(C)CC', 2.284),
            ('c1ccccc1O', 2.134)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi1v(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi1v=%f) should have Chi1V=%f' % (smi, chi, res)

  def _testChi1vLong(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 10
    self.__testDesc(fName, col, GraphDescriptors.Chi1v)

  def testPathCounts(self):
    """ FIX: this should be in some other file

    """
    data = [('CCCCCC', (6, 5, 4, 3, 2, 1)),
            ('CCC(C)CC', (6, 5, 5, 4, 1, 0)),
            ('CC(C)CCC', (6, 5, 5, 3, 2, 0)),
            ('CC(C)C(C)C', (6, 5, 6, 4, 0, 0)),
            ('CC(C)(C)CC', (6, 5, 7, 3, 0, 0)),
            ('CCCCCO', (6, 5, 4, 3, 2, 1)),
            ('CCC(O)CC', (6, 5, 5, 4, 1, 0)),
            ('CC(O)(C)CC', (6, 5, 7, 3, 0, 0)),
            ('c1ccccc1O', (7, 7, 8, 8, 8, 8)), ]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      for i in range(1, 6):
        cnt = len(Chem.FindAllPathsOfLengthN(m, i, useBonds=1))
        assert cnt == res[i], (smi, i, cnt, res[i], Chem.FindAllPathsOfLengthN(m, i, useBonds=1))
        cnt = len(Chem.FindAllPathsOfLengthN(m, i + 1, useBonds=0))
        assert cnt == res[i], (smi, i, cnt, res[i], Chem.FindAllPathsOfLengthN(m, i + 1,
                                                                               useBonds=1))

  def testChi2v(self):
    """ test calculation of Chi2v

    """
    data = [('CCCCCC', 1.707),
            ('CCC(C)CC', 1.922),
            ('CC(C)CCC', 2.183),
            ('CC(C)C(C)C', 2.488),
            ('CC(C)(C)CC', 2.914),
            ('CCCCCO', 1.431),
            ('CCC(O)CC', 1.470),
            ('CC(O)(C)CC', 2.166),
            ('c1ccccc1O', 1.336), ]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi2v(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi2v=%f) should have Chi2V=%f' % (smi, chi, res)

  def _testChi2vLong(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 12
    self.__testDesc(fName, col, GraphDescriptors.Chi2v)

  def testChi3v(self):
    """ test calculation of Chi3v

    """
    data = [('CCCCCC', 0.957), ('CCC(C)CC', 1.394), ('CC(C)CCC', 0.866), ('CC(C)C(C)C', 1.333),
            ('CC(C)(C)CC', 1.061), ('CCCCCO', 0.762), ('CCC(O)CC', 0.943), ('CC(O)(C)CC', 0.865),
            ('c1ccccc1O', 0.756)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi3v(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi3v=%f) should have Chi3V=%f' % (smi, chi, res)

  def _testChi3vLong(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 14
    self.__testDesc(fName, col, GraphDescriptors.Chi3v)

  def testChi4v(self):
    """ test calculation of Chi4v

    """
    data = [('CCCCCC', 0.500), ('CCC(C)CC', 0.289), ('CC(C)CCC', 0.577), ('CC(C)C(C)C', 0.000),
            ('CC(C)(C)CC', 0.000), ('CCCCCO', 0.362), ('CCC(O)CC', 0.289), ('CC(O)(C)CC', 0.000),
            ('c1ccccc1O', 0.428)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi4v(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi4v=%f) should have Chi4V=%f' % (smi, chi, res)

  def testChi5v(self):
    """ test calculation of Chi5v

    """
    data = [('CCCCCC', 0.250), ('CCC(C)CC', 0.000), ('CC(C)CCC', 0.000), ('CC(C)C(C)C', 0.000),
            ('CC(C)(C)CC', 0.000), ('CCCCCO', 0.112), ('CCC(O)CC', 0.000), ('CC(O)(C)CC', 0.000),
            ('c1ccccc1O', 0.242)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.ChiNv_(m, 5)
      assert feq(chi, res, 1e-3), 'mol %s (Chi5v=%f) should have Chi5V=%f' % (smi, chi, res)

  def testChi0n(self):
    """ test calculation of Chi0n

    """
    data = [('CCCCCC', 4.828),
            ('CCC(C)CC', 4.992),
            ('CC(C)CCC', 4.992),
            ('CC(C)C(C)C', 5.155),
            ('CC(C)(C)CC', 5.207),
            ('CCCCCO', 4.276),
            ('CCC(O)CC', 4.439),
            ('CC(O)(C)CC', 4.654),
            ('c1ccccc1O', 3.834),
            ('CCCl', 2.085),
            ('CCBr', 2.085),
            ('CCI', 2.085), ]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi0n(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi0n=%f) should have Chi0n=%f' % (smi, chi, res)

  def _testChi0nLong(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 6
    self.__testDesc(fName, col, GraphDescriptors.Chi0n)

  def testChi1n(self):
    """ test calculation of Chi1n

    """
    data = [('CCCCCC', 2.914), ('CCC(C)CC', 2.808), ('CC(C)CCC', 2.770), ('CC(C)C(C)C', 2.643),
            ('CC(C)(C)CC', 2.561), ('CCCCCO', 2.523), ('CCC(O)CC', 2.489), ('CC(O)(C)CC', 2.284),
            ('c1ccccc1O', 2.134)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi1n(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi1n=%f) should have Chi1N=%f' % (smi, chi, res)

  def _testChi1nLong(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 9
    self.__testDesc(fName, col, GraphDescriptors.Chi1n)

  def testChi2n(self):
    """ test calculation of Chi2n

    """
    data = [('CCCCCC', 1.707), ('CCC(C)CC', 1.922), ('CC(C)CCC', 2.183), ('CC(C)C(C)C', 2.488),
            ('CC(C)(C)CC', 2.914), ('CCCCCO', 1.431), ('CCC(O)CC', 1.470), ('CC(O)(C)CC', 2.166),
            ('c1ccccc1O', 1.336)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi2n(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi2n=%f) should have Chi2N=%f' % (smi, chi, res)

  def _testChi2nLong(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 11
    self.__testDesc(fName, col, GraphDescriptors.Chi2n)

  def testChi3n(self):
    """ test calculation of Chi3n

    """
    data = [('CCCCCC', 0.957), ('CCC(C)CC', 1.394), ('CC(C)CCC', 0.866), ('CC(C)C(C)C', 1.333),
            ('CC(C)(C)CC', 1.061), ('CCCCCO', 0.762), ('CCC(O)CC', 0.943), ('CC(O)(C)CC', 0.865),
            ('c1ccccc1O', 0.756)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi3n(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi3n=%f) should have Chi3N=%f' % (smi, chi, res)

  def _testChi3nLong(self):
    fName = 'PP_descrs_regress.rest.2.csv'
    col = 13
    self.__testDesc(fName, col, GraphDescriptors.Chi3n)

  def testChi4n(self):
    """ test calculation of Chi4n

    """
    data = [('CCCCCC', 0.500), ('CCC(C)CC', 0.289), ('CC(C)CCC', 0.577), ('CC(C)C(C)C', 0.000),
            ('CC(C)(C)CC', 0.000), ('CCCCCO', 0.362), ('CCC(O)CC', 0.289), ('CC(O)(C)CC', 0.000),
            ('c1ccccc1O', 0.428)]
    for smi, res in data:
      m = Chem.MolFromSmiles(smi)
      chi = GraphDescriptors.Chi4n(m)
      assert feq(chi, res, 1e-3), 'mol %s (Chi4n=%f) should have Chi4N=%f' % (smi, chi, res)

  def testIssue125(self):
    """ test an issue with calculating BalabanJ
    """
    smi = 'O=C(OC)C1=C(C)NC(C)=C(C(OC)=O)C1C2=CC=CC=C2[N+]([O-])=O'
    m1 = Chem.MolFromSmiles(smi)
    m2 = Chem.MolFromSmiles(smi)
    Chem.MolToSmiles(m1)
    j1 = GraphDescriptors.BalabanJ(m1)
    j2 = GraphDescriptors.BalabanJ(m2)
    assert feq(j1, j2)

  def testOrderDepend(self):
    """ test order dependence of some descriptors:
    """
    data = [('C=CC=C', 21.01955, 2.73205), ('O=CC=O', 25.01955, 2.73205),
            ('FCC(=O)CF', 46.7548875, 2.98816), ('O=C1C=CC(=O)C=C1', 148.705216, 2.8265),
            ('C12C(F)=C(O)C(F)C1C(F)=C(O)C(F)2', 315.250442, 2.4509),
            ('C12CC=CCC1C(=O)C3CC=CCC3C(=O)2', 321.539522, 1.95986)]

    for smi, CT, bal in data:
      m = Chem.MolFromSmiles(smi)
      newBal = GraphDescriptors.BalabanJ(m, forceDMat=1)
      assert feq(newBal, bal, 1e-4), 'mol %s %f!=%f' % (smi, newBal, bal)
      m = Chem.MolFromSmiles(smi)
      newCT = GraphDescriptors.BertzCT(m, forceDMat=1)
      assert feq(newCT, CT, 1e-4), 'mol %s (CT calc = %f) should have CT = %f' % (smi, newCT, CT)
      m = Chem.MolFromSmiles(smi)
      newCT = GraphDescriptors.BertzCT(m, forceDMat=1)
      assert feq(newCT, CT, 1e-4), 'mol %s (CT calc = %f) should have CT = %f' % (smi, newCT, CT)
      newBal = GraphDescriptors.BalabanJ(m, forceDMat=1)
      assert feq(newBal, bal, 1e-4), 'mol %s %f!=%f' % (smi, newBal, bal)

      m = Chem.MolFromSmiles(smi)
      newBal = GraphDescriptors.BalabanJ(m, forceDMat=1)
      assert feq(newBal, bal, 1e-4), 'mol %s %f!=%f' % (smi, newBal, bal)
      newCT = GraphDescriptors.BertzCT(m, forceDMat=1)
      assert feq(newCT, CT, 1e-4), 'mol %s (CT calc = %f) should have CT = %f' % (smi, newCT, CT)


if __name__ == '__main__':
  import sys, getopt, re
  doLong = 0
  if len(sys.argv) > 1:
    args, extras = getopt.getopt(sys.argv[1:], 'l')
    for arg, val in args:
      if arg == '-l':
        doLong = 1
      sys.argv.remove('-l')
  if doLong:
    for methName in dir(TestCase):
      if re.match('_test', methName):
        newName = re.sub('_test', 'test', methName)
        exec('TestCase.%s = TestCase.%s' % (newName, methName))

  unittest.main()

# $Id$ # #  Copyright (C) 2003-2006  Rational Discovery LLC # #   @@ All Rights Reserved @@ #  This file is part of the RDKit. #  The contents are covered by the terms of the BSD license #  which is included in the file license.txt, found at the root #  of the RDKit source tree. # unit testing code for graph-theoretical descriptors test calculation of Bertz 'C(T)' index test calculation of Bertz 'C(T)' index      NOTE: this is a backwards compatibility test, because of the changes       w.r.t. the treatment of aromatic atoms in the new version, we need       to ignore molecules with aromatic rings... test calculation of Chi0 test calculation of Chi0 test calculation of the Hall-Kier Alpha value test calculation of the Hall-Kier Alpha value test calculation of Ipc. #N', 1.00000, 2.00000), ('OC#N', 0.91830, 2.75489)] test calculation of Ipc test calculation of Ipc test calculation of the Hall-Kier kappa1 value       corrected data from Tables 3 and 6 of Rev. Comp. Chem. vol 2, 367-422, (1991) test calculation of the Hall-Kier kappa2 value       corrected data from Tables 5 and 6 of Rev. Comp. Chem. vol 2, 367-422, (1991) #print smi test calculation of the Hall-Kier kappa3 value       corrected data from Tables 3 and 6 of Rev. Comp. Chem. vol 2, 367-422, (1991) test calculation of kappa3 test calculation of kappa3 test calculation of Labute's ASA value test calculation of Labute's ASA value test calculation of TPSA test calculation of TPSA test calculation of Lipinski params # we can't do H Acceptors for these pyridine-containing molecules #  because the values will be wrong for EVERY one. #col = 29 #self.__testDesc(fName,col,Lipinski.NumHAcceptors) test calculation of Lipinski params test calculation of Lipinski params test calculation of Lipinski params test calculation of Lipinski params test calculation of Lipinski params test calculation of Lipinski params test calculation of MOE-type descriptors test calculation of MOE-type descriptors test calculation of the Balaban J value         J values are from Balaban's paper and have had roundoff       errors and typos corrected. # alkanes # cycloalkanes # aromatics test calculation of the balaban j value test calculation of kappa1 test calculation of kappa2 test calculation of Chi0v test calculation of Chi1v FIX: this should be in some other file test calculation of Chi2v test calculation of Chi3v test calculation of Chi4v test calculation of Chi5v test calculation of Chi0n test calculation of Chi1n test calculation of Chi2n test calculation of Chi3n test calculation of Chi4n test an issue with calculating BalabanJ test order dependence of some descriptors: