Improve test coverage and some bug fixes (#4536)

* test getIdOfEntryWithBitId()

* remove unused functions

* improve bv tests in python wrapper

* more UniformGrid tests

* improve deprotect coverage

* improve abbreviations coverage

* add operator!= to DeprotectData

* more testing for adjustQueryProperties

* fix a copy-paste bug

* copy-paste bug

* more testing

* more testing

* more testing

* fix an edge case bug in getValenceContrib

* more bond tests

* add operator!= to StereoInfo
tests StereoInfo::operator==

* make some internal functions testable

* more testing

* minor code cleanup

* fix some bad caching behavior in getDistanceMat() and get3DDistanceMat()

testing

* test FixeMolSizeMolBundle() copy ctor

* deprecate BalabanJ

* more testing

* testing

* mods to get things working for windows DLL builds
(don't bother running some of the tests there)

* fix a typo
This commit is contained in:
Greg Landrum
2021-09-26 07:45:06 +02:00
committed by GitHub
parent 3d427e8153
commit df72c241c5
22 changed files with 1188 additions and 449 deletions

View File

@@ -7,351 +7,312 @@ import numpy
def feq(a, b, tol=1e-4):
return abs(a - b) < tol
return abs(a - b) < tol
class TestCase(unittest.TestCase):
def setUp(self):
pass
def setUp(self):
pass
def test0FromList(self):
bv1 = DataStructs.SparseBitVect(1000)
bv2 = DataStructs.SparseBitVect(1000)
obits = range(0, 1000, 3)
def test0FromList(self):
bv1 = DataStructs.SparseBitVect(1000)
bv2 = DataStructs.SparseBitVect(1000)
obits = range(0, 1000, 3)
for bit in obits:
bv1.SetBit(bit)
for bit in obits:
bv1.SetBit(bit)
bv2.SetBitsFromList(obits)
bv2.SetBitsFromList(obits)
for i in range(1000):
assert bv1.GetBit(i) == bv2.GetBit(i)
for i in range(1000):
assert bv1.GetBit(i) == bv2.GetBit(i)
self.assertTrue(bv1 == bv2)
bv2.SetBit(1)
self.assertTrue(bv1 != bv2)
bv2.UnSetBit(1)
self.assertTrue(bv1 == bv2)
self.assertTrue(bv1 == bv2)
bv2.SetBit(1)
self.assertTrue(bv1 != bv2)
bv2.UnSetBit(1)
self.assertTrue(bv1 == bv2)
bv2.UnSetBitsFromList(obits)
for i in range(1000):
assert bv2.GetBit(i) == 0
bv2.UnSetBitsFromList(obits)
for i in range(1000):
assert bv2.GetBit(i) == 0
bv1 = DataStructs.ExplicitBitVect(1000)
bv2 = DataStructs.ExplicitBitVect(1000)
obits = range(0, 1000, 3)
bv1 = DataStructs.ExplicitBitVect(1000)
bv2 = DataStructs.ExplicitBitVect(1000)
obits = range(0, 1000, 3)
for bit in obits:
bv1.SetBit(bit)
for bit in obits:
bv1.SetBit(bit)
bv2.SetBitsFromList(obits)
bv2.SetBitsFromList(obits)
for i in range(1000):
assert bv1.GetBit(i) == bv2.GetBit(i)
for i in range(1000):
assert bv1.GetBit(i) == bv2.GetBit(i)
bv2.UnSetBitsFromList(obits)
for i in range(1000):
assert bv2.GetBit(i) == 0
bv2.UnSetBitsFromList(obits)
for i in range(1000):
assert bv2.GetBit(i) == 0
def test01BVWithAllOnes(self):
bv1 = DataStructs.ExplicitBitVect(10, True)
for i in range(10):
assert bv1.GetBit(i) == 1
def test01BVWithAllOnes(self):
bv1 = DataStructs.ExplicitBitVect(10, True)
for i in range(10):
assert bv1.GetBit(i) == 1
def test1SparsePickle(self):
nbits = 10000
bv1 = DataStructs.SparseBitVect(nbits)
for i in range(1000):
x = random.randrange(0, nbits)
bv1.SetBit(x)
def test1SparsePickle(self):
nbits = 10000
bv1 = DataStructs.SparseBitVect(nbits)
for i in range(1000):
x = random.randrange(0, nbits)
bv1.SetBit(x)
pkl = pickle.dumps(bv1, 1)
bv2 = pickle.loads(pkl)
for i in range(nbits):
assert bv1[i] == bv2[i]
pkl = pickle.dumps(bv1, 1)
bv2 = pickle.loads(pkl)
for i in range(nbits):
assert bv1[i] == bv2[i]
def test2ExplicitPickle(self):
nbits = 10000
bv1 = DataStructs.ExplicitBitVect(nbits)
for i in range(1000):
x = random.randrange(0, nbits)
bv1.SetBit(x)
def test2ExplicitPickle(self):
nbits = 10000
bv1 = DataStructs.ExplicitBitVect(nbits)
for i in range(1000):
x = random.randrange(0, nbits)
bv1.SetBit(x)
pkl = pickle.dumps(bv1, 1)
bv2 = pickle.loads(pkl)
for i in range(nbits):
assert bv1[i] == bv2[i]
pkl = pickle.dumps(bv1, 1)
bv2 = pickle.loads(pkl)
for i in range(nbits):
assert bv1[i] == bv2[i]
def test3Bounds(self):
nbits = 10
bv1 = DataStructs.ExplicitBitVect(nbits)
bv1[0]
with self.assertRaisesRegex(IndexError, ""):
bv1[11]
def test3Bounds(self):
nbits = 10
bv1 = DataStructs.ExplicitBitVect(nbits)
bv1[0]
with self.assertRaisesRegex(IndexError, ""):
bv1[11]
def test4OnBitsInCommon(self):
sz = 100
bv1 = DataStructs.ExplicitBitVect(sz)
bv2 = DataStructs.ExplicitBitVect(sz)
for i in range(0, sz, 2):
bv1.SetBit(i)
if i < 3 * sz / 4:
bv2.SetBit(i)
self.assertTrue(DataStructs.AllProbeBitsMatch(bv1, bv1.ToBinary()))
self.assertTrue(DataStructs.AllProbeBitsMatch(bv2, bv1.ToBinary()))
self.assertFalse(DataStructs.AllProbeBitsMatch(bv1, bv2.ToBinary()))
self.assertTrue(DataStructs.AllProbeBitsMatch(bv2, bv2.ToBinary()))
def test4OnBitsInCommon(self):
sz = 100
bv1 = DataStructs.ExplicitBitVect(sz)
bv2 = DataStructs.ExplicitBitVect(sz)
for i in range(0, sz, 2):
bv1.SetBit(i)
if i < 3 * sz / 4:
bv2.SetBit(i)
self.assertTrue(DataStructs.AllProbeBitsMatch(bv1, bv1.ToBinary()))
self.assertTrue(DataStructs.AllProbeBitsMatch(bv2, bv1.ToBinary()))
self.assertFalse(DataStructs.AllProbeBitsMatch(bv1, bv2.ToBinary()))
self.assertTrue(DataStructs.AllProbeBitsMatch(bv2, bv2.ToBinary()))
def test5FromBitString(self):
s1 = '1010'
bv = DataStructs.CreateFromBitString(s1)
self.assertTrue(len(bv) == 4)
self.assertTrue(list(bv.GetOnBits()) == [0, 2])
def test5FromBitString(self):
s1 = '1010'
bv = DataStructs.CreateFromBitString(s1)
self.assertTrue(len(bv) == 4)
self.assertTrue(list(bv.GetOnBits()) == [0, 2])
def test6BulkOps(self):
nbits = 10000
bvs = []
for bvi in range(10):
bv = DataStructs.ExplicitBitVect(nbits)
for j in range(nbits):
x = random.randrange(0, nbits)
bv.SetBit(x)
bvs.append(bv)
sims = DataStructs.BulkTanimotoSimilarity(bvs[0], bvs)
for i in range(len(bvs)):
sim = DataStructs.TanimotoSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
def _bulkTest(self,bvs):
for metric in 'Tanimoto','Dice','AllBit','OnBit','RogotGoldberg':
bulk = getattr(DataStructs,f'Bulk{metric}Similarity')
single = getattr(DataStructs,f'{metric}Similarity')
sims = bulk(bvs[0],bvs)
for i in range(len(bvs)):
sim = single(bvs[0],bvs[i])
self.assertEqual(sim,sims[i])
self.assertEqual(sim, single(bvs[0],bvs[i].ToBinary()))
dists = bulk(bvs[0], bvs, returnDistance=True)
for i in range(len(bvs)):
dist = single(bvs[0], bvs[i], returnDistance=True)
self.assertEqual(dist, dists[i])
self.assertEqual(dist, single(bvs[0], bvs[i].ToBinary(), returnDistance=True))
sims = DataStructs.BulkDiceSimilarity(bvs[0], bvs)
for i in range(len(bvs)):
sim = DataStructs.DiceSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
sims = DataStructs.BulkTverskySimilarity(bvs[0], bvs, 1, 1)
for i in range(len(bvs)):
sim = DataStructs.TverskySimilarity(bvs[0], bvs[i], 1, 1)
self.assertEqual(sim, sims[i])
sim = DataStructs.TanimotoSimilarity(bvs[0], bvs[i])
self.assertEqual(sim, sims[i])
sims = DataStructs.BulkAllBitSimilarity(bvs[0], bvs)
for i in range(len(bvs)):
sim = DataStructs.AllBitSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
sims = DataStructs.BulkTverskySimilarity(bvs[0], bvs, 1, 1, returnDistance=True)
for i in range(len(bvs)):
sim = DataStructs.TverskySimilarity(bvs[0], bvs[i], 1, 1, returnDistance=True)
self.assertEqual(sim, sims[i])
sim = DataStructs.TanimotoSimilarity(bvs[0], bvs[i], returnDistance=True)
self.assertEqual(sim, sims[i])
sims = DataStructs.BulkOnBitSimilarity(bvs[0], bvs)
for i in range(len(bvs)):
sim = DataStructs.OnBitSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
def test6BulkOps(self):
nbits = 10000
bvs = []
for bvi in range(10):
bv = DataStructs.ExplicitBitVect(nbits)
for j in range(nbits):
x = random.randrange(0, nbits)
bv.SetBit(x)
bvs.append(bv)
self._bulkTest(bvs)
sims = DataStructs.BulkRogotGoldbergSimilarity(bvs[0], bvs)
for i in range(len(bvs)):
sim = DataStructs.RogotGoldbergSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
def test10BulkOps2(self):
nbits = 10000
bvs = []
for bvi in range(10):
bv = DataStructs.ExplicitBitVect(nbits)
for j in range(nbits):
x = random.randrange(0, nbits)
bv.SetBit(x)
bvs.append(bv)
bvs = tuple(bvs)
self._bulkTest(bvs)
sims = DataStructs.BulkTverskySimilarity(bvs[0], bvs, 1, 1)
for i in range(len(bvs)):
sim = DataStructs.TverskySimilarity(bvs[0], bvs[i], 1, 1)
self.assertTrue(feq(sim, sims[i]))
sim = DataStructs.TanimotoSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
sims = DataStructs.BulkTverskySimilarity(bvs[0], bvs, .5, .5)
for i in range(len(bvs)):
sim = DataStructs.TverskySimilarity(bvs[0], bvs[i], .5, .5)
self.assertTrue(feq(sim, sims[i]))
sim = DataStructs.DiceSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
def test7FPS(self):
bv = DataStructs.ExplicitBitVect(32)
bv.SetBit(0)
bv.SetBit(1)
bv.SetBit(17)
bv.SetBit(23)
bv.SetBit(31)
self.assertEqual(DataStructs.BitVectToFPSText(bv), "03008280")
bv2 = DataStructs.CreateFromFPSText("03008280")
self.assertEqual(bv, bv2)
self.assertRaises(ValueError, lambda: DataStructs.CreateFromFPSText("030082801"))
bv2 = DataStructs.CreateFromFPSText("")
self.assertEqual(bv2.GetNumBits(), 0)
def test8BinText(self):
bv = DataStructs.ExplicitBitVect(32)
bv.SetBit(0)
bv.SetBit(1)
bv.SetBit(17)
bv.SetBit(23)
bv.SetBit(31)
bv2 = DataStructs.CreateFromBinaryText(DataStructs.BitVectToBinaryText(bv))
self.assertEqual(bv, bv2)
bv2 = DataStructs.CreateFromBinaryText("")
self.assertEqual(bv2.GetNumBits(), 0)
def test9ToNumpy(self):
import numpy
for typ in (DataStructs.ExplicitBitVect,):
bv = typ(32)
bv.SetBit(0)
bv.SetBit(1)
bv.SetBit(17)
bv.SetBit(23)
bv.SetBit(31)
arr = numpy.zeros((32, ), 'i')
DataStructs.ConvertToNumpyArray(bv, arr)
for i in range(bv.GetNumBits()):
self.assertEqual(bv[i], arr[i])
for typ in (DataStructs.IntSparseIntVect,
DataStructs.LongSparseIntVect, DataStructs.UIntSparseIntVect,
DataStructs.ULongSparseIntVect):
iv = typ(32)
iv[0] = 1
iv[1] = 1
iv[17] = 1
iv[23] = 1
iv[31] = 1
arr = numpy.zeros((32, ), 'i')
DataStructs.ConvertToNumpyArray(iv, arr)
for i in range(iv.GetLength()):
self.assertEqual(iv[i], arr[i])
def test10BulkOps2(self):
nbits = 10000
bvs = []
for bvi in range(10):
bv = DataStructs.ExplicitBitVect(nbits)
for j in range(nbits):
x = random.randrange(0, nbits)
bv.SetBit(x)
bvs.append(bv)
bvs = tuple(bvs)
sims = DataStructs.BulkTanimotoSimilarity(bvs[0], bvs)
for i in range(len(bvs)):
sim = DataStructs.TanimotoSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
sims = DataStructs.BulkDiceSimilarity(bvs[0], bvs)
for i in range(len(bvs)):
sim = DataStructs.DiceSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
sims = DataStructs.BulkAllBitSimilarity(bvs[0], bvs)
for i in range(len(bvs)):
sim = DataStructs.AllBitSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
sims = DataStructs.BulkOnBitSimilarity(bvs[0], bvs)
for i in range(len(bvs)):
sim = DataStructs.OnBitSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
sims = DataStructs.BulkRogotGoldbergSimilarity(bvs[0], bvs)
for i in range(len(bvs)):
sim = DataStructs.RogotGoldbergSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
sims = DataStructs.BulkTverskySimilarity(bvs[0], bvs, 1, 1)
for i in range(len(bvs)):
sim = DataStructs.TverskySimilarity(bvs[0], bvs[i], 1, 1)
self.assertTrue(feq(sim, sims[i]))
sim = DataStructs.TanimotoSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
sims = DataStructs.BulkTverskySimilarity(bvs[0], bvs, .5, .5)
for i in range(len(bvs)):
sim = DataStructs.TverskySimilarity(bvs[0], bvs[i], .5, .5)
self.assertTrue(feq(sim, sims[i]))
sim = DataStructs.DiceSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
def test10BulkOps3(self):
nbits = 10000
bvs = numpy.empty((10, ), DataStructs.ExplicitBitVect)
for bvi in range(10):
bv = DataStructs.ExplicitBitVect(nbits)
for j in range(nbits):
x = random.randrange(0, nbits)
bv.SetBit(x)
bvs[bvi] = bv
sims = DataStructs.BulkTanimotoSimilarity(bvs[0], bvs)
for i in range(len(bvs)):
sim = DataStructs.TanimotoSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
sims = DataStructs.BulkDiceSimilarity(bvs[0], bvs)
for i in range(len(bvs)):
sim = DataStructs.DiceSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
sims = DataStructs.BulkAllBitSimilarity(bvs[0], bvs)
for i in range(len(bvs)):
sim = DataStructs.AllBitSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
sims = DataStructs.BulkOnBitSimilarity(bvs[0], bvs)
for i in range(len(bvs)):
sim = DataStructs.OnBitSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
sims = DataStructs.BulkRogotGoldbergSimilarity(bvs[0], bvs)
for i in range(len(bvs)):
sim = DataStructs.RogotGoldbergSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
sims = DataStructs.BulkTverskySimilarity(bvs[0], bvs, 1, 1)
for i in range(len(bvs)):
sim = DataStructs.TverskySimilarity(bvs[0], bvs[i], 1, 1)
self.assertTrue(feq(sim, sims[i]))
sim = DataStructs.TanimotoSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
sims = DataStructs.BulkTverskySimilarity(bvs[0], bvs, .5, .5)
for i in range(len(bvs)):
sim = DataStructs.TverskySimilarity(bvs[0], bvs[i], .5, .5)
self.assertTrue(feq(sim, sims[i]))
sim = DataStructs.DiceSimilarity(bvs[0], bvs[i])
self.assertTrue(feq(sim, sims[i]))
def test10BulkOps3(self):
nbits = 10000
bvs = numpy.empty((10, ), DataStructs.ExplicitBitVect)
for bvi in range(10):
bv = DataStructs.ExplicitBitVect(nbits)
for j in range(nbits):
x = random.randrange(0, nbits)
bv.SetBit(x)
bvs[bvi] = bv
self._bulkTest(bvs)
def test11BulkNeighbors(self):
nbits = 2048
bvs = []
for bvi in range(1000):
bv = DataStructs.ExplicitBitVect(nbits)
for j in range(nbits):
x = random.randrange(0, nbits)
bv.SetBit(x)
bvs.append(bv)
qs = bvs[:10]
db = bvs[10:]
for metric in ['Tanimoto','Cosine', 'Kulczynski', 'Dice', 'Sokal',
'McConnaughey', 'Asymmetric', 'BraunBlanquet', 'Russel',
'RogotGoldberg']:
bulkSim = getattr(DataStructs,f'Bulk{metric}Similarity')
nbrSim = getattr(DataStructs,f'{metric}SimilarityNeighbors')
tgts = []
for q in qs:
sims = bulkSim(q,db)
sim, idx = max((sim, -idx) for idx, sim in enumerate(sims))
tgts.append((-idx,sim))
nbrs = nbrSim(qs,db)
self.assertEqual(tgts,nbrs)
def test7FPS(self):
bv = DataStructs.ExplicitBitVect(32)
bv.SetBit(0)
bv.SetBit(1)
bv.SetBit(17)
bv.SetBit(23)
bv.SetBit(31)
self.assertEqual(DataStructs.BitVectToFPSText(bv), "03008280")
bv2 = DataStructs.CreateFromFPSText("03008280")
self.assertEqual(bv, bv2)
self.assertRaises(ValueError, lambda: DataStructs.CreateFromFPSText("030082801"))
bv2 = DataStructs.CreateFromFPSText("")
self.assertEqual(bv2.GetNumBits(), 0)
def test8BinText(self):
bv = DataStructs.ExplicitBitVect(32)
bv.SetBit(0)
bv.SetBit(1)
bv.SetBit(17)
bv.SetBit(23)
bv.SetBit(31)
bv2 = DataStructs.CreateFromBinaryText(DataStructs.BitVectToBinaryText(bv))
self.assertEqual(bv, bv2)
bv2 = DataStructs.CreateFromBinaryText("")
self.assertEqual(bv2.GetNumBits(), 0)
def test9ToNumpy(self):
import numpy
for typ in (DataStructs.ExplicitBitVect,):
bv = typ(32)
bv.SetBit(0)
bv.SetBit(1)
bv.SetBit(17)
bv.SetBit(23)
bv.SetBit(31)
arr = numpy.zeros((32, ), 'i')
DataStructs.ConvertToNumpyArray(bv, arr)
for i in range(bv.GetNumBits()):
self.assertEqual(bv[i], arr[i])
for typ in (DataStructs.IntSparseIntVect,
DataStructs.LongSparseIntVect, DataStructs.UIntSparseIntVect,
DataStructs.ULongSparseIntVect):
iv = typ(32)
iv[0] = 1
iv[1] = 1
iv[17] = 1
iv[23] = 1
iv[31] = 1
arr = numpy.zeros((32, ), 'i')
DataStructs.ConvertToNumpyArray(iv, arr)
for i in range(iv.GetLength()):
self.assertEqual(iv[i], arr[i])
def test11BulkNeighbors(self):
nbits = 2048
bvs = []
for bvi in range(1000):
bv = DataStructs.ExplicitBitVect(nbits)
for j in range(nbits):
x = random.randrange(0, nbits)
bv.SetBit(x)
bvs.append(bv)
qs = bvs[:10]
db = bvs[10:]
for metric in ['Tanimoto','Cosine', 'Kulczynski', 'Dice', 'Sokal',
'McConnaughey', 'Asymmetric', 'BraunBlanquet', 'Russel',
'RogotGoldberg']:
bulkSim = getattr(DataStructs,f'Bulk{metric}Similarity')
nbrSim = getattr(DataStructs,f'{metric}SimilarityNeighbors')
tgts = []
for q in qs:
sims = bulkSim(q,db)
sim, idx = max((sim, -idx) for idx, sim in enumerate(sims))
tgts.append((-idx,sim))
nbrs = nbrSim(qs,db)
self.assertEqual(tgts,nbrs)
def test12ToList(self):
nbits = 2048
for cls in [DataStructs.ExplicitBitVect, DataStructs.SparseBitVect]:
bv = cls(nbits)
l = [0]*2048
for j in range(nbits):
x = random.randrange(0, nbits)
l[x] = 1
bv.SetBit(x)
l2 = list(bv)
l3 = bv.ToList()
self.assertEqual(l, l2)
self.assertEqual(l, l3)
def test13Base64(self):
nbits = 2048
for cls in [DataStructs.ExplicitBitVect, DataStructs.SparseBitVect]:
bv = cls(nbits)
for j in range(nbits):
x = random.randrange(0, nbits)
bv.SetBit(x)
bv2 = cls(nbits)
bv2.FromBase64(bv.ToBase64())
self.assertEqual(bv,bv2)
def test14NegativeIndices(self):
nbits = 2048
for cls in [DataStructs.ExplicitBitVect, DataStructs.SparseBitVect]:
bv = cls(nbits)
bv2 = cls(nbits)
for j in range(nbits):
x = random.randrange(0, nbits)
bv.SetBit(x)
bv2[-(nbits-x)]=1
self.assertEqual(bv,bv2)
for j in range(nbits):
self.assertEqual(bv[j],bv[-(nbits-j)])
with self.assertRaises(IndexError):
bv[-(nbits+1)]
with self.assertRaises(IndexError):
bv2[-(nbits + 1)] = 1
def test15FoldFingerprint(self):
for cls in [DataStructs.ExplicitBitVect, DataStructs.SparseBitVect]:
fp = cls(8)
fp[0] = 1
fp[1] = 1
fp[6] = 1
ffp = DataStructs.FoldFingerprint(fp)
self.assertTrue(ffp[0])
self.assertTrue(ffp[1])
self.assertTrue(ffp[2])
self.assertFalse(ffp[3])
def test12ToList(self):
nbits = 2048
for cls in [DataStructs.ExplicitBitVect, DataStructs.SparseBitVect]:
bv = cls(nbits)
l = [0]*2048
for j in range(nbits):
x = random.randrange(0, nbits)
l[x] = 1
bv.SetBit(x)
l2 = list(bv)
l3 = bv.ToList()
self.assertEqual(l, l2)
self.assertEqual(l, l3)
if __name__ == '__main__':
unittest.main()
unittest.main()

View File

@@ -1,5 +1,5 @@
//
// Copyright (C) 2003-2020 greg Landrum and Rational Discovery LLC
// Copyright (C) 2003-2021 greg Landrum and other RDKit contributors
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
@@ -14,13 +14,6 @@
namespace python = boost::python;
SBV *ff1(const SBV &bv1, int factor = 2) {
return FoldFingerprint(bv1, factor);
}
EBV *ff2(const EBV &ev1, int factor = 2) {
return FoldFingerprint(ev1, factor);
}
namespace {
template <typename T>
python::object BVToBinaryText(const T &bv) {

View File

@@ -1,7 +1,17 @@
//
// Copyright (C) 2019-2021 Greg Landrum and other RDKit contributors
//
// @@ 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.
//
#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do
// this in one cpp file
#include "catch.hpp"
#include <Geometry/point.h>
#include <Geometry/UniformGrid3D.h>
TEST_CASE("construct Point2D from Point3D", "[point]") {
SECTION("basics") {
@@ -11,3 +21,127 @@ TEST_CASE("construct Point2D from Point3D", "[point]") {
CHECK(p2.y == p3.y);
}
}
TEST_CASE("UniformGrid getGridIndex") {
RDGeom::UniformGrid3D grd(6.0, 5.0, 4.0);
CHECK(grd.getSize() == 960);
CHECK(grd.getNumX() == 12);
CHECK(grd.getNumY() == 10);
CHECK(grd.getNumZ() == 8);
CHECK(grd.getGridIndex(12, 1, 1) == -1);
CHECK(grd.getGridIndex(1, 10, 1) == -1);
CHECK(grd.getGridIndex(1, 1, 8) == -1);
CHECK(grd.getGridIndex(100, 1, 1) == -1);
CHECK(grd.getGridIndex(1, 100, 1) == -1);
CHECK(grd.getGridIndex(1, 1, 100) == -1);
unsigned int x, y, z;
CHECK_THROWS_AS(grd.getGridIndices(960, x, y, z), IndexErrorException);
CHECK_THROWS_AS(grd.getGridPointLoc(960), IndexErrorException);
}
TEST_CASE("UniformGrid copying") {
RDGeom::UniformGrid3D grd(6.0, 5.0, 4.0);
grd.setSphereOccupancy(RDGeom::Point3D(0.0, 0.0, 0.0), 1.5, 0.25);
CHECK(grd.getOccupancyVect()->getTotalVal() == 523);
SECTION("operator=") {
RDGeom::UniformGrid3D grd2(3, 3, 3);
grd2 = grd;
CHECK(grd2.getSize() == grd.getSize());
CHECK(grd2.getOccupancyVect()->getTotalVal() ==
grd.getOccupancyVect()->getTotalVal());
}
SECTION("char * ctor") {
auto pkl = grd.toString();
RDGeom::UniformGrid3D grd2(pkl.c_str(), pkl.size());
CHECK(grd2.getSize() == grd.getSize());
CHECK(grd2.getOccupancyVect()->getTotalVal() ==
grd.getOccupancyVect()->getTotalVal());
}
}
TEST_CASE("UniformGrid get/setVal") {
RDGeom::UniformGrid3D grd(6.0, 5.0, 4.0);
SECTION("getVal()") {
{
RDGeom::Point3D pt(1, 0, 0);
CHECK(grd.getGridPointIndex(pt) >= 0);
CHECK(grd.getVal(pt) == 0);
}
{
RDGeom::Point3D pt(10, 0, 0);
CHECK(grd.getGridPointIndex(pt) == -1);
CHECK(grd.getVal(pt) == -1);
}
{
RDGeom::Point3D pt(0, 10, 0);
CHECK(grd.getGridPointIndex(pt) == -1);
CHECK(grd.getVal(pt) == -1);
}
{
RDGeom::Point3D pt(0, 0, 10);
CHECK(grd.getGridPointIndex(pt) == -1);
CHECK(grd.getVal(pt) == -1);
}
}
SECTION("setVal") {
CHECK(grd.getOccupancyVect()->getTotalVal() == 0);
grd.setVal(RDGeom::Point3D(1, 0, 0), 2);
CHECK(grd.getOccupancyVect()->getTotalVal() == 2);
// not on the grid, has no impact
grd.setVal(RDGeom::Point3D(10, 0, 0), 2);
CHECK(grd.getOccupancyVect()->getTotalVal() == 2);
grd.setVal(3, 1);
CHECK(grd.getOccupancyVect()->getTotalVal() == 3);
// not on the grid
CHECK_THROWS_AS(grd.setVal(grd.getSize() + 1, 1), IndexErrorException);
CHECK(grd.getOccupancyVect()->getTotalVal() == 3);
// value too large
CHECK_THROWS_AS(grd.setVal(5, 8), ValueErrorException);
CHECK(grd.getOccupancyVect()->getTotalVal() == 3);
}
SECTION("setSphereOccupancy out of range") {
grd.setSphereOccupancy(RDGeom::Point3D(10.0, 0.0, 0.0), 1.5, 0.25);
CHECK(grd.getOccupancyVect()->getTotalVal() == 0);
int maxLayers = -1;
bool ignoreOutOfBound = false;
CHECK_THROWS_AS(grd.setSphereOccupancy(RDGeom::Point3D(10.0, 0.0, 0.0), 1.5,
0.25, maxLayers, ignoreOutOfBound),
RDGeom::GridException);
}
}
TEST_CASE("compareParams") {
RDGeom::UniformGrid3D grd(6.0, 5.0, 4.0);
{
RDGeom::UniformGrid3D grd2(6.0, 5.0, 4.0);
CHECK(grd.compareParams(grd2));
CHECK(grd2.compareParams(grd));
}
{
RDGeom::UniformGrid3D grd2(7.0, 5.0, 4.0);
CHECK(!grd.compareParams(grd2));
CHECK(!grd2.compareParams(grd));
}
{
RDGeom::UniformGrid3D grd2(6.0, 6.0, 4.0);
CHECK(!grd.compareParams(grd2));
CHECK(!grd2.compareParams(grd));
}
{
RDGeom::UniformGrid3D grd2(6.0, 5.0, 5.0);
CHECK(!grd.compareParams(grd2));
CHECK(!grd2.compareParams(grd));
}
{
RDGeom::UniformGrid3D grd2(6.6, 5.5, 4.4, grd.getSpacing() + .05);
CHECK(!grd.compareParams(grd2));
CHECK(!grd2.compareParams(grd));
}
{
RDGeom::Point3D offset = grd.getOffset();
offset *= 1.5;
RDGeom::UniformGrid3D grd2(6.0, 5.0, 4.0, grd.getSpacing(),
RDKit::DiscreteValueVect::TWOBITVALUE, &offset);
CHECK(!grd.compareParams(grd2));
CHECK(!grd2.compareParams(grd));
}
}

View File

@@ -527,4 +527,12 @@ M END
m->clearConformers();
CHECK(MolToCXSmiles(*m) == "C*C |$;PEG2;$|");
}
}
TEST_CASE("comparison") {
auto abbrevs = Abbreviations::Utils::getDefaultAbbreviations();
Abbreviations::AbbreviationDefinition cp = abbrevs[0];
CHECK(cp == abbrevs[0]);
CHECK(cp != abbrevs[1]);
CHECK(abbrevs[1] == abbrevs[1]);
}

View File

@@ -322,7 +322,7 @@ void parseAdjustQueryParametersFromJSON(MolOps::AdjustQueryParameters &p,
}
which = boost::to_upper_copy<std::string>(pt.get("adjustRingChainFlags", ""));
if (!which.empty()) {
p.adjustRingCountFlags = parseWhichString(which);
p.adjustRingChainFlags = parseWhichString(which);
}
} // namespace MolOps
@@ -359,7 +359,7 @@ void adjustQueryProperties(RWMol &mol, const AdjustQueryParameters *inParams) {
!ringInfo->numAtomRings(i)) &&
!((params.makeAtomsGenericFlags & ADJUST_IGNORERINGS) &&
ringInfo->numAtomRings(i)) &&
!((params.adjustDegreeFlags & ADJUST_IGNOREMAPPED) &&
!((params.makeAtomsGenericFlags & ADJUST_IGNOREMAPPED) &&
isMapped(mol.getAtomWithIdx(i)))) {
auto *qa = new QueryAtom();
qa->setQuery(makeAtomNullQuery());

View File

@@ -183,6 +183,9 @@ double Bond::getBondTypeAsDouble() const {
}
double Bond::getValenceContrib(const Atom *atom) const {
if (atom != getBeginAtom() && atom != getEndAtom()) {
return 0.0;
}
double res;
if ((getBondType() == DATIVE || getBondType() == DATIVEONE) &&
atom->getIdx() != getEndAtomIdx()) {

View File

@@ -344,7 +344,7 @@ class RDKIT_GRAPHMOL_EXPORT Bond : public RDProps {
//! returns twice the \c bondType
//! (e.g. SINGLE->2, AROMATIC->3, etc.)
uint8_t getTwiceBondType(const RDKit::Bond &b);
RDKIT_GRAPHMOL_EXPORT extern uint8_t getTwiceBondType(const RDKit::Bond &b);
}; // namespace RDKit

View File

@@ -18,7 +18,7 @@
namespace RDKit {
namespace Canon {
namespace {
namespace details {
bool isUnsaturated(const Atom *atom, const ROMol &mol) {
for (const auto &bndItr :
boost::make_iterator_range(mol.getAtomBonds(atom))) {
@@ -38,14 +38,14 @@ bool hasSingleHQuery(const Atom::QUERYATOM_QUERY *q) {
std::string descr = q->getDescription();
if (descr == "AtomAnd") {
for (auto cIt = q->beginChildren(); cIt != q->endChildren(); ++cIt) {
std::string descr = (*cIt)->getDescription();
if (descr == "AtomHCount") {
auto cDescr = (*cIt)->getDescription();
if (cDescr == "AtomHCount") {
if (!(*cIt)->getNegation() &&
((ATOM_EQUALS_QUERY *)(*cIt).get())->getVal() == 1) {
return true;
}
return false;
} else if (descr == "AtomAnd") {
} else if (cDescr == "AtomAnd") {
res = hasSingleHQuery((*cIt).get());
if (res) {
return true;
@@ -68,7 +68,7 @@ bool atomHasFourthValence(const Atom *atom) {
}
return false;
}
} // end of anonymous namespace
} // namespace details
bool chiralAtomNeedsTagInversion(const RDKit::ROMol &mol,
const RDKit::Atom *atom, bool isAtomFirst,
@@ -76,8 +76,8 @@ bool chiralAtomNeedsTagInversion(const RDKit::ROMol &mol,
PRECONDITION(atom, "bad atom");
return atom->getDegree() == 3 &&
((isAtomFirst && atom->getNumExplicitHs() == 1) ||
(!atomHasFourthValence(atom) && numClosures == 1 &&
!isUnsaturated(atom, mol)));
(!details::atomHasFourthValence(atom) && numClosures == 1 &&
!details::isUnsaturated(atom, mol)));
}
struct _possibleCompare

View File

@@ -1,5 +1,5 @@
//
// Copyright (C) 2008-2020 Greg Landrum
// Copyright (C) 2008-2021 Greg Landrum and other RDKit contributors
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
@@ -92,6 +92,7 @@ struct RDKIT_GRAPHMOL_EXPORT StereoInfo {
centeredOn == other.centeredOn && descriptor == other.descriptor &&
controllingAtoms == other.controllingAtoms;
}
bool operator!=(const StereoInfo &other) const { return !(*this == other); }
};
//! identifies potential stereoatoms and stereobonds in a molecule

View File

@@ -56,6 +56,9 @@ struct RDKIT_DEPROTECT_EXPORT DeprotectData {
reaction_smarts == other.reaction_smarts &&
isValid() == other.isValid());
}
bool operator!=(const DeprotectData &other) const {
return !(*this == other);
}
//! Returns true if the deprotection is valid
bool isValid() const {

View File

@@ -20,6 +20,14 @@
using namespace RDKit;
using namespace RDKit::Deprotect;
TEST_CASE("Deprotection basics", "[deprotect]") {
const auto dps = getDeprotections();
DeprotectData cp = dps[0];
CHECK(cp == dps[0]);
CHECK(dps[0] == cp);
CHECK(dps[1] != dps[0]);
}
TEST_CASE("Standard deprotections", "[deprotect]") {
SECTION("simple deprotections") {
auto m = "N(C(=O)OC(C)(C)C)Cc1ccccc1NC(=O)OC(C)(C)C"_smiles;
@@ -31,7 +39,7 @@ TEST_CASE("Standard deprotections", "[deprotect]") {
CHECK(res->getProp<std::vector<std::string>>("DEPROTECTIONS") == expected);
}
SECTION("test deprotection examples") {
for(auto &data : getDeprotections()) {
for (auto &data : getDeprotections()) {
std::vector<DeprotectData> vect = {data};
std::vector<std::string> examples;
boost::split(examples, data.example, boost::is_any_of(">"));

View File

@@ -1,5 +1,5 @@
//
// Copyright (C) 2002-2018 Greg Landrum and Rational Discovery LLC
// Copyright (C) 2002-2021 Greg Landrum and other RDKit contributors
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
@@ -12,6 +12,7 @@
#include <GraphMol/RDKitBase.h>
#include <GraphMol/Canon.h>
#include <GraphMol/MonomerInfo.h>
#include <GraphMol/MolPickler.h>
#include "FileParsers.h"
#include "SequenceParsers.h"
#include "SequenceWriters.h"
@@ -3744,6 +3745,26 @@ void testPDBFile() {
TEST_ASSERT(feq(m->getConformer().getAtomPos(0).y, 14.099));
TEST_ASSERT(feq(m->getConformer().getAtomPos(0).z, 3.625));
std::string pkl;
MolPickler::pickleMol(*m, pkl);
RWMol m2(pkl);
for (const auto atom : m->atoms()) {
const auto atom2 = m2.getAtomWithIdx(atom->getIdx());
auto info1 = static_cast<AtomPDBResidueInfo *>(atom->getMonomerInfo());
auto info2 = static_cast<AtomPDBResidueInfo *>(atom2->getMonomerInfo());
// this is awkward because operator== isn't defined for AtomPDBResidueInfo
// yet
TEST_ASSERT(info1->getName() == info2->getName());
TEST_ASSERT(info1->getChainId() == info2->getChainId());
TEST_ASSERT(info1->getInsertionCode() == info2->getInsertionCode());
TEST_ASSERT(info1->getTempFactor() == info2->getTempFactor());
TEST_ASSERT(info1->getMonomerType() == info2->getMonomerType());
TEST_ASSERT(info1->getResidueName() == info2->getResidueName());
TEST_ASSERT(info1->getResidueNumber() == info2->getResidueNumber());
TEST_ASSERT(info1->getSerialNumber() == info2->getSerialNumber());
TEST_ASSERT(info1->getOccupancy() == info2->getOccupancy());
}
std::string mb = MolToPDBBlock(*m);
delete m;
m = PDBBlockToMol(mb);

View File

@@ -161,7 +161,7 @@ void FloydWarshall(int dim, T *adjMat, int *pathMat,
delete[] lastD;
delete[] lastP;
}
} // end of local utility namespace
} // namespace
namespace MolOps {
double *getDistanceMat(const ROMol &mol, bool useBO, bool useAtomWts,
@@ -178,6 +178,9 @@ double *getDistanceMat(const ROMol &mol, bool useBO, bool useAtomWts,
if (useBO) {
propName += "BO";
}
if (useAtomWts) {
propName += "AtomWts";
}
if (!force && mol.hasProp(propName)) {
mol.getProp(propName, sptr);
return sptr.get();
@@ -196,7 +199,7 @@ double *getDistanceMat(const ROMol &mol, bool useBO, bool useAtomWts,
ROMol::EDGE_ITER firstB, lastB;
boost::tie(firstB, lastB) = mol.getEdges();
while (firstB != lastB) {
const Bond* bond = mol[*firstB];
const Bond *bond = mol[*firstB];
i = bond->getBeginAtomIdx();
j = bond->getEndAtomIdx();
double contrib;
@@ -294,6 +297,9 @@ double *getAdjacencyMatrix(const ROMol &mol, bool useBO, int emptyVal,
propName = "";
}
propName += "AdjacencyMatrix";
if (useBO) {
propName += "BO";
}
if (!force && mol.hasProp(propName)) {
mol.getProp(propName, sptr);
return sptr.get();
@@ -395,6 +401,9 @@ double *get3DDistanceMat(const ROMol &mol, int confId, bool useAtomWts,
}
if (propName != "") {
propName += "3DDistanceMatrix_Conf" + std::to_string(conf.getId());
if (useAtomWts) {
propName += "_AtomWeights";
}
if (!force && mol.hasProp(propName)) {
mol.getProp(propName, sptr);
return sptr.get();

View File

@@ -1,6 +1,5 @@
// $Id$
//
// Copyright (C) 2006 Greg Landrum
// Copyright (C) 2006-2021 Greg Landrum
//
#include <RDGeneral/test.h>
#include <GraphMol/RDKitBase.h>
@@ -70,6 +69,10 @@ void test1() {
TEST_ASSERT(mcat->getNumEntries() == 3);
TEST_ASSERT(mcat->getFPLength() == 3);
TEST_ASSERT(
mcat->getEntryWithIdx(mcat->getIdOfEntryWithBitId(0))->getBitId() == 0);
TEST_ASSERT(
mcat->getEntryWithIdx(mcat->getIdOfEntryWithBitId(2))->getBitId() == 2);
TEST_ASSERT(mcat->getEntryWithBitId(0)->getMol()->getNumAtoms() == 10);
TEST_ASSERT(mcat->getEntryWithBitId(1)->getMol()->getNumAtoms() == 1);
TEST_ASSERT(mcat->getEntryWithBitId(2)->getMol()->getNumAtoms() == 3);

View File

@@ -137,7 +137,7 @@ getMolFragsWithQuery(const ROMol &mol, T (*query)(const ROMol &, const Atom *),
RDKIT_GRAPHMOL_EXPORT void findSpanningTree(const ROMol &mol,std::vector<int> &mst);
#endif
//! calculates Balaban's J index for the molecule
//! DEPRECATED calculates Balaban's J index for the molecule
/*!
\param mol the molecule of interest
\param useBO toggles inclusion of the bond order in the calculation
@@ -153,7 +153,7 @@ getMolFragsWithQuery(const ROMol &mol, T (*query)(const ROMol &, const Atom *),
RDKIT_GRAPHMOL_EXPORT double computeBalabanJ(
const ROMol &mol, bool useBO = true, bool force = false,
const std::vector<int> *bondPath = nullptr, bool cacheIt = true);
//! \overload
//! DEPRECATED \overload
RDKIT_GRAPHMOL_EXPORT double computeBalabanJ(double *distMat, int nb, int nAts);
//! \name Dealing with hydrogens

View File

@@ -1,6 +1,5 @@
//
//
// Copyright (C) 2020 Greg Landrum and T5 Informatics GmbH
// Copyright (C) 2020-2021 Greg Landrum and other RDKit contributors
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
@@ -334,6 +333,34 @@ TEST_CASE("adjustQueryParameters from JSON") {
MolOps::parseAdjustQueryParametersFromJSON(ps, json);
CHECK(ps.useStereoCareForBonds == false);
}
SECTION("adjustHeavyDegreeFlags") {
MolOps::AdjustQueryParameters ps;
CHECK(ps.adjustHeavyDegreeFlags != MolOps::ADJUST_IGNORENONE);
std::string json = R"JSON({"adjustHeavyDegreeFlags":"IGNORENONE"})JSON";
MolOps::parseAdjustQueryParametersFromJSON(ps, json);
CHECK(ps.adjustHeavyDegreeFlags == MolOps::ADJUST_IGNORENONE);
}
SECTION("adjustRingCountFlags") {
MolOps::AdjustQueryParameters ps;
CHECK(ps.adjustRingCountFlags != MolOps::ADJUST_IGNORERINGS);
std::string json = R"JSON({"adjustRingCountFlags":"IGNORERINGS"})JSON";
MolOps::parseAdjustQueryParametersFromJSON(ps, json);
CHECK(ps.adjustRingCountFlags == MolOps::ADJUST_IGNORERINGS);
}
SECTION("makeAtomsGenericFlags") {
MolOps::AdjustQueryParameters ps;
CHECK(ps.makeAtomsGenericFlags != MolOps::ADJUST_IGNOREALL);
std::string json = R"JSON({"makeAtomsGenericFlags":"IGNOREALL"})JSON";
MolOps::parseAdjustQueryParametersFromJSON(ps, json);
CHECK(ps.makeAtomsGenericFlags == MolOps::ADJUST_IGNOREALL);
}
SECTION("adjustRingChainFlags") {
MolOps::AdjustQueryParameters ps;
CHECK(ps.adjustRingChainFlags != MolOps::ADJUST_IGNORENONDUMMIES);
std::string json = R"JSON({"adjustRingChainFlags":"IGNORENONDUMMIES"})JSON";
MolOps::parseAdjustQueryParametersFromJSON(ps, json);
CHECK(ps.adjustRingChainFlags == MolOps::ADJUST_IGNORENONDUMMIES);
}
SECTION("bogus contents") {
MolOps::AdjustQueryParameters ps;
CHECK(ps.adjustDegree == true);
@@ -349,17 +376,18 @@ TEST_CASE("adjustQueryParameters from JSON") {
CHECK(ps.adjustDegree == true);
json = R"JSON({"adjustDegreeFlags":"IGNORENONE|bogus"})JSON";
// clang-format off
CHECK_THROWS_AS(MolOps::parseAdjustQueryParametersFromJSON(ps, json),ValueErrorException);
CHECK_THROWS_AS(MolOps::parseAdjustQueryParametersFromJSON(ps, json),
ValueErrorException);
}
}
TEST_CASE("MDL five-rings") {
MolOps::AdjustQueryParameters ps = MolOps::AdjustQueryParameters::noAdjustments();
MolOps::AdjustQueryParameters ps =
MolOps::AdjustQueryParameters::noAdjustments();
ps.setMDLFiveRingAromaticity = true;
SECTION("query details") {
using extuple=std::tuple<std::string,std::string,std::string>;
using extuple = std::tuple<std::string, std::string, std::string>;
// clang-format off
std::vector<extuple> examples = {
// no queries, no change
extuple{"adjustqueryprops_MDLfivering_1.mol","[#7H]1:[#6]:[#6]:[#6]:[#6]:1",""},
@@ -371,8 +399,9 @@ TEST_CASE("MDL five-rings") {
// aromatic then it won't match azulene in a normal RDKit molecule, which is certainly not the intent of this.
extuple{"adjustqueryprops_MDLfivering_4.mol","[#6]12:[#6]:[#6]:[#6]:[#6]-1:[#6]:[#6]:[#6]:[#6]:[#6]:2",""},
};
for( auto tpl : examples){
if(std::get<2>(tpl).empty()){
// clang-format on
for (auto tpl : examples) {
if (std::get<2>(tpl).empty()) {
std::get<2>(tpl) = std::get<1>(tpl);
}
auto fname = std::get<0>(tpl);
@@ -380,18 +409,77 @@ TEST_CASE("MDL five-rings") {
pathName += "/Code/GraphMol/test_data/";
std::unique_ptr<RWMol> qry(MolFileToMol(pathName + fname));
REQUIRE(qry);
CHECK(std::get<1>(tpl)==MolToSmarts(*qry));
MolOps::adjustQueryProperties(*qry,&ps);
CHECK(std::get<2>(tpl)==MolToSmarts(*qry));
}
{
RWMol cp(*qry);
CHECK(std::get<1>(tpl) == MolToSmarts(cp));
MolOps::adjustQueryProperties(cp, &ps);
CHECK(std::get<2>(tpl) == MolToSmarts(cp));
}
{ // make sure ring-finding happens
RWMol cp(*qry);
cp.getRingInfo()->reset();
CHECK(std::get<1>(tpl) == MolToSmarts(cp));
MolOps::adjustQueryProperties(cp, &ps);
CHECK(std::get<2>(tpl) == MolToSmarts(cp));
}
}
}
SECTION("edge cases") {
SmilesParserParams smiles_ps;
smiles_ps.sanitize = false;
{
std::unique_ptr<RWMol> qry{SmilesToMol("*1:c:c:c:c:1", smiles_ps)};
REQUIRE(qry);
QueryAtom *qat = new QueryAtom(0);
qat->setQuery(makeAAtomQuery());
qat->setIsAromatic(true);
qry->replaceAtom(0, qat);
MolOps::adjustQueryProperties(*qry, &ps);
CHECK(MolToSmarts(*qry) == "[!#1]1-,:[#6]=,:[#6]-,:[#6]=,:[#6]-,:1");
}
{ // ring not fully aromatic
std::unique_ptr<RWMol> qry{SmilesToMol("*1:c:C-c:c:1", smiles_ps)};
REQUIRE(qry);
QueryAtom *qat = new QueryAtom(0);
qat->setQuery(makeAAtomQuery());
qat->setIsAromatic(true);
qry->replaceAtom(0, qat);
MolOps::adjustQueryProperties(*qry, &ps);
CHECK(MolToSmarts(*qry) == "[!#1]1:[#6]:[#6]-[#6]:[#6]:1");
}
{ // ring has additional dummy
std::unique_ptr<RWMol> qry{SmilesToMol("*1:c:*:c:c:1", smiles_ps)};
REQUIRE(qry);
QueryAtom *qat = new QueryAtom(0);
qat->setQuery(makeAAtomQuery());
qat->setIsAromatic(true);
qry->replaceAtom(0, qat);
MolOps::adjustQueryProperties(*qry, &ps);
CHECK(MolToSmarts(*qry) == "[!#1]1:[#6]:[#0]:[#6]:[#6]:1");
}
{ // query bond in ring
std::unique_ptr<RWMol> qry{SmilesToMol("*1:c:c:c:c:1", smiles_ps)};
REQUIRE(qry);
QueryAtom *qat = new QueryAtom(0);
qat->setQuery(makeAAtomQuery());
qat->setIsAromatic(true);
qry->replaceAtom(0, qat);
QueryBond *qbnd = new QueryBond();
qbnd->setBondType(Bond::BondType::SINGLE);
qbnd->setQuery(makeBondOrderEqualsQuery(Bond::BondType::SINGLE));
qry->replaceBond(0, qbnd);
MolOps::adjustQueryProperties(*qry, &ps);
CHECK(MolToSmarts(*qry) == "[!#1]1-[#6]:[#6]:[#6]:[#6]:1");
}
}
}
TEST_CASE("conjugated five-rings") {
MolOps::AdjustQueryParameters ps = MolOps::AdjustQueryParameters::noAdjustments();
MolOps::AdjustQueryParameters ps =
MolOps::AdjustQueryParameters::noAdjustments();
ps.adjustConjugatedFiveRings = true;
SECTION("matching") {
// clang-format off
std::vector<matchCase> examples = {
// 1,3 cyclopentadiene
matchCase{"C1=CCC=C1","adjustqueryprops_fivering_1.mol",true,true},
@@ -429,24 +517,25 @@ TEST_CASE("conjugated five-rings") {
matchCase{"C1=COC=C1","adjustqueryprops_fivering_5.mol",false,false},
matchCase{"C1=COC=C1","adjustqueryprops_fivering_6.mol",false,false},
};
for( const auto &tpl : examples){
// clang-format on
for (const auto &tpl : examples) {
auto fname = std::get<1>(tpl);
std::string pathName = getenv("RDBASE");
pathName += "/Code/GraphMol/test_data/";
std::unique_ptr<RWMol> qry(MolFileToMol(pathName + fname));
REQUIRE(qry);
if(std::get<2>(tpl)){
CHECK_THAT(std::get<0>(tpl),IsSubstructOf(*qry,fname));
if (std::get<2>(tpl)) {
CHECK_THAT(std::get<0>(tpl), IsSubstructOf(*qry, fname));
} else {
CHECK_THAT(std::get<0>(tpl),!IsSubstructOf(*qry,fname));
CHECK_THAT(std::get<0>(tpl), !IsSubstructOf(*qry, fname));
}
MolOps::adjustQueryProperties(*qry,&ps);
if(std::get<3>(tpl)){
CHECK_THAT(std::get<0>(tpl),IsSubstructOf(*qry,fname));
MolOps::adjustQueryProperties(*qry, &ps);
if (std::get<3>(tpl)) {
CHECK_THAT(std::get<0>(tpl), IsSubstructOf(*qry, fname));
} else {
CHECK_THAT(std::get<0>(tpl),!IsSubstructOf(*qry,fname));
CHECK_THAT(std::get<0>(tpl), !IsSubstructOf(*qry, fname));
}
}
}
}
SECTION("query details") {
auto fname = "adjustqueryprops_fivering_2.mol";
@@ -455,49 +544,81 @@ TEST_CASE("conjugated five-rings") {
std::unique_ptr<RWMol> qry(MolFileToMol(pathName + fname));
REQUIRE(qry);
auto smarts = MolToSmarts(*qry);
CHECK(smarts=="[!#1]1:[#6]:[#6]:[#6]:[#6]:1");
MolOps::adjustQueryProperties(*qry,&ps);
CHECK(smarts == "[!#1]1:[#6]:[#6]:[#6]:[#6]:1");
MolOps::adjustQueryProperties(*qry, &ps);
smarts = MolToSmarts(*qry);
CHECK(smarts=="[!#1]1-,=,:[#6]-,=,:[#6]-,=,:[#6]-,=,:[#6]-,=,:1");
CHECK(smarts == "[!#1]1-,=,:[#6]-,=,:[#6]-,=,:[#6]-,=,:[#6]-,=,:1");
}
SECTION("some edge cases") {
{
auto qry="C1=COCC1"_smiles;
{
auto qry = "C1=COCC1"_smiles;
auto smarts = MolToSmarts(*qry);
CHECK(smarts == "[#6]1=[#6]-[#8]-[#6]-[#6]-1");
MolOps::adjustQueryProperties(*qry,&ps);
MolOps::adjustQueryProperties(*qry, &ps);
CHECK(MolToSmarts(*qry) == smarts);
}
{
auto qry="C1=CCC=C1"_smiles;
}
{
auto qry = "C1=CCC=C1"_smiles;
auto smarts = MolToSmarts(*qry);
CHECK(smarts == "[#6]1=[#6]-[#6]-[#6]=[#6]-1");
MolOps::adjustQueryProperties(*qry,&ps);
CHECK(MolToSmarts(*qry) == "[#6]1-,=,:[#6]-,=,:[#6]-,=,:[#6]-,=,:[#6]-,=,:1");
}
{
// conjugation (not bond order)
auto qry="C1=COOO1"_smiles;
MolOps::adjustQueryProperties(*qry, &ps);
CHECK(MolToSmarts(*qry) ==
"[#6]1-,=,:[#6]-,=,:[#6]-,=,:[#6]-,=,:[#6]-,=,:1");
}
{
// conjugation (not bond order)
auto qry = "C1=COOO1"_smiles;
auto smarts = MolToSmarts(*qry);
CHECK(smarts == "[#6]1=[#6]-[#8]-[#8]-[#8]-1");
MolOps::adjustQueryProperties(*qry,&ps);
CHECK(MolToSmarts(*qry) == "[#6]1-,=,:[#6]-,=,:[#8]-,=,:[#8]-,=,:[#8]-,=,:1");
}
{
// conjugation (not bond order)
auto qry="O=C1C(=O)C(=O)C(=O)C1=O"_smiles;
MolOps::adjustQueryProperties(*qry, &ps);
CHECK(MolToSmarts(*qry) ==
"[#6]1-,=,:[#6]-,=,:[#8]-,=,:[#8]-,=,:[#8]-,=,:1");
}
{
// conjugation (not bond order)
auto qry = "O=C1C(=O)C(=O)C(=O)C1=O"_smiles;
auto smarts = MolToSmarts(*qry);
CHECK(smarts == "[#8]=[#6]1-[#6](=[#8])-[#6](=[#8])-[#6](=[#8])-[#6]-1=[#8]");
MolOps::adjustQueryProperties(*qry,&ps);
CHECK(MolToSmarts(*qry) == "[#8]=[#6]1-,=,:[#6](=[#8])-,=,:[#6](=[#8])-,=,:[#6](=[#8])-,=,:[#6]-,=,:1=[#8]");
}
CHECK(smarts ==
"[#8]=[#6]1-[#6](=[#8])-[#6](=[#8])-[#6](=[#8])-[#6]-1=[#8]");
MolOps::adjustQueryProperties(*qry, &ps);
CHECK(MolToSmarts(*qry) ==
"[#8]=[#6]1-,=,:[#6](=[#8])-,=,:[#6](=[#8])-,=,:[#6](=[#8])-,=,:[#"
"6]-,=,:1=[#8]");
}
}
SECTION("edge cases: ring finding") {
// test that ring finding happens:
SmilesParserParams smiles_ps;
smiles_ps.sanitize = false;
std::unique_ptr<RWMol> qry{SmilesToMol("O1C=CC=C1", smiles_ps)};
REQUIRE(qry);
qry->updatePropertyCache();
MolOps::setConjugation(*qry);
qry->getRingInfo()->reset();
CHECK(!qry->getRingInfo()->isInitialized());
MolOps::adjustQueryProperties(*qry, &ps);
CHECK(qry->getRingInfo()->isInitialized());
CHECK(MolToSmarts(*qry) ==
"[#8]1-,=,:[#6]-,=,:[#6]-,=,:[#6]-,=,:[#6]-,=,:1");
}
SECTION("edge cases: ignore larger rings") {
SmilesParserParams smiles_ps;
smiles_ps.sanitize = false;
std::unique_ptr<RWMol> qry{SmilesToMol("N1=CC=CC=C1", smiles_ps)};
REQUIRE(qry);
qry->updatePropertyCache();
MolOps::setConjugation(*qry);
MolOps::adjustQueryProperties(*qry, &ps);
CHECK(MolToSmarts(*qry) == "[#7]1=[#6]-[#6]=[#6]-[#6]=[#6]-1");
}
}
TEST_CASE("single bonds to degree-one neighbors") {
MolOps::AdjustQueryParameters ps = MolOps::AdjustQueryParameters::noAdjustments();
MolOps::AdjustQueryParameters ps =
MolOps::AdjustQueryParameters::noAdjustments();
ps.adjustSingleBondsToDegreeOneNeighbors = true;
SECTION("matching") {
// clang-format off
std::vector<matchCase> examples = {
matchCase{"C2CCCc1c2nncc1","Cc1cnncc1",true,true},
matchCase{"C2CCCc1c2nncc1","CCc1cnncc1",true,true},
@@ -510,31 +631,46 @@ TEST_CASE("single bonds to degree-one neighbors") {
matchCase{"C2CCCc1[nH]ccc12","CCc1[nH]ccc1",true,true},
matchCase{"c2cccc1[nH]ccc12","Cc1[nH]ccc1",false,true},
matchCase{"c2cccc1[nH]ccc12","CCc1[nH]ccc1",false,false},
};
for( const auto &tpl : examples){
// clang-format on
for (const auto &tpl : examples) {
auto smi = std::get<1>(tpl);
std::unique_ptr<RWMol> qry(SmilesToMol(smi));
REQUIRE(qry);
if(std::get<2>(tpl)){
CHECK_THAT(std::get<0>(tpl),IsSubstructOf(*qry,smi));
if (std::get<2>(tpl)) {
CHECK_THAT(std::get<0>(tpl), IsSubstructOf(*qry, smi));
} else {
CHECK_THAT(std::get<0>(tpl),!IsSubstructOf(*qry,smi));
CHECK_THAT(std::get<0>(tpl), !IsSubstructOf(*qry, smi));
}
MolOps::adjustQueryProperties(*qry,&ps);
if(std::get<3>(tpl)){
CHECK_THAT(std::get<0>(tpl),IsSubstructOf(*qry,smi));
} else {
CHECK_THAT(std::get<0>(tpl),!IsSubstructOf(*qry,smi));
{
RWMol cp(*qry);
MolOps::adjustQueryProperties(cp, &ps);
if (std::get<3>(tpl)) {
CHECK_THAT(std::get<0>(tpl), IsSubstructOf(cp, smi));
} else {
CHECK_THAT(std::get<0>(tpl), !IsSubstructOf(cp, smi));
}
}
}
{ // make sure ring-finding happens
RWMol cp(*qry);
cp.getRingInfo()->reset();
MolOps::adjustQueryProperties(cp, &ps);
if (std::get<3>(tpl)) {
CHECK_THAT(std::get<0>(tpl), IsSubstructOf(cp, smi));
} else {
CHECK_THAT(std::get<0>(tpl), !IsSubstructOf(cp, smi));
}
}
}
}
}
TEST_CASE("single bonds to aromatic neighbors") {
MolOps::AdjustQueryParameters ps = MolOps::AdjustQueryParameters::noAdjustments();
MolOps::AdjustQueryParameters ps =
MolOps::AdjustQueryParameters::noAdjustments();
ps.adjustSingleBondsBetweenAromaticAtoms = true;
SECTION("matching") {
// clang-format off
std::vector<matchCase> examples = {
matchCase{"c1ncccc1-c1cnncc1","c1ncccc1-c1cnncc1",true,true},
matchCase{"C1=CC2=C(C=CC3=C2C=NN=C3)N=C1","c1ncccc1-c1cnncc1",false,true},
@@ -545,51 +681,107 @@ TEST_CASE("single bonds to aromatic neighbors") {
matchCase{"C1CC2=C(C=CC=N2)C2=C1C=NN=C2","C1CC2=C(C=CC=N2)C2=C1C=NN=C2",true,true},
matchCase{"C1CC2=C3C(C=CC4=NN=CC1=C34)=CC=N2","C1CC2=C(C=CC=N2)C2=C1C=NN=C2",false,true}, // was github #3325
};
for( const auto &tpl : examples){
// clang-format on
for (const auto &tpl : examples) {
auto smi = std::get<1>(tpl);
std::unique_ptr<RWMol> qry(SmilesToMol(smi));
REQUIRE(qry);
if(std::get<2>(tpl)){
CHECK_THAT(std::get<0>(tpl),IsSubstructOf(*qry,smi));
if (std::get<2>(tpl)) {
CHECK_THAT(std::get<0>(tpl), IsSubstructOf(*qry, smi));
} else {
CHECK_THAT(std::get<0>(tpl),!IsSubstructOf(*qry,smi));
CHECK_THAT(std::get<0>(tpl), !IsSubstructOf(*qry, smi));
}
MolOps::adjustQueryProperties(*qry,&ps);
if(std::get<3>(tpl)){
CHECK_THAT(std::get<0>(tpl),IsSubstructOf(*qry,smi));
MolOps::adjustQueryProperties(*qry, &ps);
if (std::get<3>(tpl)) {
CHECK_THAT(std::get<0>(tpl), IsSubstructOf(*qry, smi));
} else {
CHECK_THAT(std::get<0>(tpl),!IsSubstructOf(*qry,smi));
CHECK_THAT(std::get<0>(tpl), !IsSubstructOf(*qry, smi));
}
}
}
}
}
TEST_CASE("github #3388: Information about charges and isotopes lost when calling AdjustQueryProperties") {
MolOps::AdjustQueryParameters ps = MolOps::AdjustQueryParameters::noAdjustments();
TEST_CASE(
"github #3388: Information about charges and isotopes lost when calling "
"AdjustQueryProperties") {
MolOps::AdjustQueryParameters ps =
MolOps::AdjustQueryParameters::noAdjustments();
ps.adjustDegree = true;
ps.adjustDegreeFlags = MolOps::AdjustQueryWhichFlags::ADJUST_IGNORENONE;
SECTION("basics") {
auto mol = "[13CH3]C[O-]"_smiles;
REQUIRE(mol);
MolOps::adjustQueryProperties(*mol,&ps);
MolOps::adjustQueryProperties(*mol, &ps);
auto sma = MolToSmarts(*mol);
CHECK(sma=="[#6&13*&D1]-[#6&D2]-[#8&-&D1]");
CHECK(sma == "[#6&13*&D1]-[#6&D2]-[#8&-&D1]");
}
SECTION("root cause") {
auto mol = "[13CH2-]C[O-]"_smiles;
REQUIRE(mol);
QueryAtom atm(*mol->getAtomWithIdx(0));
auto sma = SmartsWrite::GetAtomSmarts(&atm);
CHECK(sma=="[#6&13*&-]");
CHECK(sma == "[#6&13*&-]");
}
SECTION("root cause2") {
// since we don't have a way to query for number of radical electrons in SMARTS,
// we need to check that a different way:
// since we don't have a way to query for number of radical electrons in
// SMARTS, we need to check that a different way:
auto mol = "[CH2]C[O-]"_smiles;
REQUIRE(mol);
QueryAtom atm(*mol->getAtomWithIdx(0));
auto descr = describeQuery(&atm);
CHECK(descr.find("AtomNumRadicalElectrons 1 = val")!=std::string::npos);
CHECK(descr.find("AtomNumRadicalElectrons 1 = val") != std::string::npos);
}
}
TEST_CASE("makeAtomsGeneric") {
SECTION("ignore mapped atoms") {
MolOps::AdjustQueryParameters ps =
MolOps::AdjustQueryParameters::noAdjustments();
ps.makeAtomsGeneric = true;
ps.makeAtomsGenericFlags =
MolOps::AdjustQueryWhichFlags::ADJUST_IGNOREMAPPED;
auto m = "C[CH3:1]"_smiles;
REQUIRE(m);
MolOps::adjustQueryProperties(*m, &ps);
CHECK(MolToSmarts(*m) == "*-[#6H3:1]");
}
}
TEST_CASE("other edges") {
SECTION("adjustHeavyDegree") {
MolOps::AdjustQueryParameters ps =
MolOps::AdjustQueryParameters::noAdjustments();
ps.adjustHeavyDegree = true;
ps.adjustHeavyDegreeFlags = MolOps::ADJUST_IGNOREMAPPED;
auto m = "C[CH3:1]"_smiles;
MolOps::adjustQueryProperties(*m, &ps);
CHECK(m->getAtomWithIdx(0)->hasQuery());
CHECK(describeQuery(m->getAtomWithIdx(0)).find("AtomHeavyAtomDegree") !=
std::string::npos);
CHECK(!m->getAtomWithIdx(1)->hasQuery());
}
SECTION("adjustRingCount") {
MolOps::AdjustQueryParameters ps =
MolOps::AdjustQueryParameters::noAdjustments();
ps.adjustRingCount = true;
ps.adjustRingCountFlags = MolOps::ADJUST_IGNOREMAPPED;
auto m = "C[CH3:1]"_smiles;
MolOps::adjustQueryProperties(*m, &ps);
CHECK(m->getAtomWithIdx(0)->hasQuery());
CHECK(describeQuery(m->getAtomWithIdx(0)).find("AtomInNRings") !=
std::string::npos);
CHECK(!m->getAtomWithIdx(1)->hasQuery());
}
SECTION("adjustRingCount") {
MolOps::AdjustQueryParameters ps =
MolOps::AdjustQueryParameters::noAdjustments();
ps.adjustRingChain = true;
ps.adjustRingChainFlags = MolOps::ADJUST_IGNOREMAPPED;
auto m = "C[CH3:1]"_smiles;
MolOps::adjustQueryProperties(*m, &ps);
CHECK(m->getAtomWithIdx(0)->hasQuery());
CHECK(describeQuery(m->getAtomWithIdx(0)).find("AtomInRing") !=
std::string::npos);
CHECK(!m->getAtomWithIdx(1)->hasQuery());
}
}

View File

@@ -1,5 +1,5 @@
//
// Copyright (C) 2020 Greg Landrum and other RDKit contributors
// Copyright (C) 2020-2021 Greg Landrum and other RDKit contributors
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
@@ -11,6 +11,7 @@
#include "catch.hpp"
#include <GraphMol/RDKitBase.h>
#include <GraphMol/StereoGroup.h>
#include <GraphMol/Chirality.h>
#include <GraphMol/MolOps.h>
@@ -1706,4 +1707,33 @@ TEST_CASE(
CHECK(m->getAtomWithIdx(1)->getChiralTag() != Atom::CHI_UNSPECIFIED);
CHECK(m->getAtomWithIdx(4)->getChiralTag() != Atom::CHI_UNSPECIFIED);
}
}
}
TEST_CASE("StereoInfo comparisons") {
Chirality::StereoInfo si1;
si1.centeredOn = 3;
si1.type == Chirality::StereoType::Atom_Tetrahedral;
Chirality::StereoInfo si2;
si2.centeredOn = 3;
si2.type == Chirality::StereoType::Atom_Tetrahedral;
CHECK(si1 == si2);
si2.descriptor = Chirality::StereoDescriptor::Tet_CCW;
CHECK(si1 != si2);
}
TEST_CASE("StereoGroup Testing") {
SECTION("basics") {
auto mol = "C[C@H](O)[C@@H](C)[C@H](F)Cl |o1:1,3,&2:5,r|"_smiles;
REQUIRE(mol);
CHECK(mol->getStereoGroups().size() == 2);
StereoGroup cp(mol->getStereoGroups()[0]);
CHECK(cp == mol->getStereoGroups()[0]);
CHECK(cp != mol->getStereoGroups()[1]);
std::vector<Atom *> toRemove{mol->getAtomWithIdx(1)};
std::vector<StereoGroup> &sgs =
const_cast<std::vector<StereoGroup> &>(mol->getStereoGroups());
removeGroupsWithAtoms(toRemove, sgs);
CHECK(mol->getStereoGroups().size() == 1);
}
}

View File

@@ -1905,7 +1905,8 @@ TEST_CASE("github #3912: cannot draw atom lists from SMARTS", "[query][bug]") {
}
}
TEST_CASE("github #4496: cannot draw aromatic atom lists from SMARTS", "[query][bug]") {
TEST_CASE("github #4496: cannot draw aromatic atom lists from SMARTS",
"[query][bug]") {
SECTION("original") {
auto m = "[c,n]1[c,n][c,n][c,n][c,n][c,n]1"_smarts;
REQUIRE(m);
@@ -2138,6 +2139,210 @@ TEST_CASE(
}
}
TEST_CASE("atom copy ctor") {
auto m = "CO"_smiles;
REQUIRE(m);
for (const auto atom : m->atoms()) {
Atom cp(*atom);
CHECK(cp.getAtomicNum() == atom->getAtomicNum());
CHECK(!cp.hasOwningMol());
}
}
TEST_CASE("bond copy ctor") {
auto m = "COC"_smiles;
REQUIRE(m);
for (const auto bond : m->bonds()) {
Bond cp(*bond);
CHECK(cp.getBondType() == bond->getBondType());
CHECK(!cp.hasOwningMol());
}
}
TEST_CASE("valence edge") {
{ // this is, of course, absurd:
auto m = "[H-2]"_smiles;
REQUIRE(m);
m->getAtomWithIdx(0)->setNoImplicit(false);
m->updatePropertyCache(false);
CHECK(m->getAtomWithIdx(0)->getFormalCharge() == -2);
CHECK(m->getAtomWithIdx(0)->getImplicitValence() == 0);
}
{
SmilesParserParams ps;
ps.sanitize = false;
std::unique_ptr<RWMol> m{SmilesToMol("CFC", ps)};
REQUIRE(m);
CHECK_THROWS_AS(m->getAtomWithIdx(1)->calcImplicitValence(true),
AtomValenceException);
}
}
TEST_CASE("SetQuery on normal atoms") {
auto m = "CC"_smiles;
REQUIRE(m);
auto qry = makeAtomAliphaticQuery();
CHECK_THROWS_AS(m->getAtomWithIdx(0)->setQuery(qry), std::runtime_error);
CHECK_THROWS_AS(m->getAtomWithIdx(0)->expandQuery(qry), std::runtime_error);
delete qry;
}
TEST_CASE("SetQuery on normal bonds") {
auto m = "CC"_smiles;
REQUIRE(m);
auto qry = makeBondOrderEqualsQuery(Bond::BondType::SINGLE);
CHECK_THROWS_AS(m->getBondWithIdx(0)->setQuery(qry), std::runtime_error);
CHECK_THROWS_AS(m->getBondWithIdx(0)->expandQuery(qry), std::runtime_error);
delete qry;
}
TEST_CASE("additional atom props") {
auto m = "CC"_smiles;
REQUIRE(m);
auto atom = m->getAtomWithIdx(0);
{
CHECK(!atom->hasProp(common_properties::_MolFileRLabel));
setAtomRLabel(atom, 1);
CHECK(atom->hasProp(common_properties::_MolFileRLabel));
setAtomRLabel(atom, 0);
CHECK(!atom->hasProp(common_properties::_MolFileRLabel));
}
{
CHECK(!atom->hasProp(common_properties::molFileAlias));
setAtomAlias(atom, "foo");
CHECK(atom->hasProp(common_properties::molFileAlias));
setAtomAlias(atom, "");
CHECK(!atom->hasProp(common_properties::molFileAlias));
}
{
CHECK(!atom->hasProp(common_properties::molFileValue));
setAtomValue(atom, "foo");
CHECK(atom->hasProp(common_properties::molFileValue));
setAtomValue(atom, "");
CHECK(!atom->hasProp(common_properties::molFileValue));
}
{
CHECK(!atom->hasProp(common_properties::_supplementalSmilesLabel));
setSupplementalSmilesLabel(atom, "foo");
CHECK(atom->hasProp(common_properties::_supplementalSmilesLabel));
setSupplementalSmilesLabel(atom, "");
CHECK(!atom->hasProp(common_properties::_supplementalSmilesLabel));
}
}
TEST_CASE("getBondTypeAsDouble()") {
SECTION("plain") {
std::vector<std::pair<Bond::BondType, double>> vals{
{Bond::BondType::IONIC, 0},
{Bond::BondType::ZERO, 0},
{Bond::BondType::SINGLE, 1},
{Bond::BondType::DOUBLE, 2},
{Bond::BondType::TRIPLE, 3},
{Bond::BondType::QUADRUPLE, 4},
{Bond::BondType::QUINTUPLE, 5},
{Bond::BondType::HEXTUPLE, 6},
{Bond::BondType::ONEANDAHALF, 1.5},
{Bond::BondType::TWOANDAHALF, 2.5},
{Bond::BondType::THREEANDAHALF, 3.5},
{Bond::BondType::FOURANDAHALF, 4.5},
{Bond::BondType::FIVEANDAHALF, 5.5},
{Bond::BondType::AROMATIC, 1.5},
{Bond::BondType::DATIVEONE, 1.0},
{Bond::BondType::DATIVE, 1.0},
{Bond::BondType::HYDROGEN, 0}
};
for (const auto &pr : vals) {
Bond bnd(pr.first);
CHECK(bnd.getBondType() == pr.first);
CHECK(bnd.getBondTypeAsDouble() == pr.second);
}
}
SECTION("twice") {
std::vector<std::pair<Bond::BondType, std::uint8_t>> vals{
{Bond::BondType::IONIC, 0}, {Bond::BondType::ZERO, 0},
{Bond::BondType::SINGLE, 2}, {Bond::BondType::DOUBLE, 4},
{Bond::BondType::TRIPLE, 6}, {Bond::BondType::QUADRUPLE, 8},
{Bond::BondType::QUINTUPLE, 10}, {Bond::BondType::HEXTUPLE, 12},
{Bond::BondType::ONEANDAHALF, 3}, {Bond::BondType::TWOANDAHALF, 5},
{Bond::BondType::THREEANDAHALF, 7}, {Bond::BondType::FOURANDAHALF, 9},
{Bond::BondType::FIVEANDAHALF, 11}, {Bond::BondType::AROMATIC, 3},
{Bond::BondType::DATIVEONE, 2}, {Bond::BondType::DATIVE, 2},
{Bond::BondType::HYDROGEN, 0}
};
for (const auto &pr : vals) {
Bond bnd(pr.first);
CHECK(bnd.getBondType() == pr.first);
CHECK(getTwiceBondType(bnd) == pr.second);
}
}
}
TEST_CASE("getValenceContrib()") {
const auto m = "CO->[Fe]"_smiles;
REQUIRE(m);
CHECK(m->getBondWithIdx(1)->getValenceContrib(m->getAtomWithIdx(0)) == 0);
CHECK(m->getBondWithIdx(1)->getValenceContrib(m->getAtomWithIdx(1)) == 0);
CHECK(m->getBondWithIdx(1)->getValenceContrib(m->getAtomWithIdx(2)) == 1);
}
TEST_CASE("conformer details") {
const auto m = "CC"_smiles;
REQUIRE(m);
Conformer *conf = new Conformer(m->getNumAtoms());
CHECK(!conf->hasOwningMol());
m->addConformer(conf);
CHECK(conf->hasOwningMol());
auto cid = conf->getId();
*conf = *conf;
CHECK(conf->hasOwningMol());
CHECK(conf->getId() == cid);
}
#if !defined(_MSC_VER) || !defined(RDKIT_DYN_LINK)
namespace RDKit {
namespace Canon {
namespace details {
bool atomHasFourthValence(const Atom *atom);
bool hasSingleHQuery(const Atom::QUERYATOM_QUERY *q);
} // namespace details
void switchBondDir(Bond *bond);
} // namespace Canon
} // namespace RDKit
TEST_CASE("canon details") {
SECTION("h queries") {
std::vector<std::pair<std::string, bool>> examples{
{"C[CHD3](F)Cl", true}, {"C[CD3H](F)Cl", true},
{"C[CH3D](F)Cl", false}, {"C[CDH3](F)Cl", false},
{"C[CDR4H](F)Cl", true},
};
for (const auto &pr : examples) {
std::unique_ptr<RWMol> m{SmartsToMol(pr.first)};
REQUIRE(m);
CHECK(RDKit::Canon::details::hasSingleHQuery(
m->getAtomWithIdx(1)->getQuery()) == pr.second);
CHECK(RDKit::Canon::details::atomHasFourthValence(m->getAtomWithIdx(1)) ==
pr.second);
// artificial, but causes atomHasFourthValence to always return true
m->getAtomWithIdx(1)->setNumExplicitHs(1);
CHECK(RDKit::Canon::details::atomHasFourthValence(m->getAtomWithIdx(1)));
}
}
}
TEST_CASE("switchBondDir") {
auto m = "C/C=C/C"_smiles;
REQUIRE(m);
auto bond = m->getBondWithIdx(0);
CHECK(bond->getBondDir() == Bond::BondDir::ENDUPRIGHT);
Canon::switchBondDir(bond);
CHECK(bond->getBondDir() == Bond::BondDir::ENDDOWNRIGHT);
bond->setBondDir(Bond::BondDir::UNKNOWN);
Canon::switchBondDir(bond);
CHECK(bond->getBondDir() == Bond::BondDir::UNKNOWN);
}
#endif
TEST_CASE("allow 5 valent N/P/As to kekulize", "[kekulization]") {
std::vector<std::pair<std::string, std::string>> tests = {
{"O=n1ccccc1", "O=N1=CC=CC=C1"},

View File

@@ -399,12 +399,7 @@ void test3() {
}
void test4() {
string smi;
Mol *m;
INT_VECT iv;
VECT_INT_VECT sssr;
smi = "CC";
m = SmilesToMol(smi);
auto m = "C=C"_smiles;
TEST_ASSERT(m);
double *adjMat = MolOps::getAdjacencyMatrix(*m);
TEST_ASSERT(adjMat);
@@ -418,7 +413,13 @@ void test4() {
TEST_ASSERT(adjMat[1] == 1);
TEST_ASSERT(adjMat[2] == 1);
TEST_ASSERT(adjMat[3] == 0);
delete m;
bool useBO = true;
adjMat = MolOps::getAdjacencyMatrix(*m, useBO);
TEST_ASSERT(adjMat);
TEST_ASSERT(adjMat[0] == 0);
TEST_ASSERT(adjMat[1] == 2.0);
TEST_ASSERT(adjMat[2] == 2.0);
TEST_ASSERT(adjMat[3] == 0);
}
void test5() {
@@ -790,14 +791,14 @@ void test9() {
BOOST_LOG(rdInfoLog)
<< "-----------------------\n Testing Distance Matrix Operations"
<< std::endl;
ROMol *m;
std::string smi = "CC=C";
m = SmilesToMol(smi);
auto m = "CC=O"_smiles;
TEST_ASSERT(m);
TEST_ASSERT(m->getNumAtoms() == 3);
bool useBO = false;
bool useAtomWts = false;
double *dMat;
dMat = MolOps::getDistanceMat(*m, false, false);
dMat = MolOps::getDistanceMat(*m, useBO, useAtomWts);
TEST_ASSERT(dMat);
TEST_ASSERT(dMat[0] == 0.0);
TEST_ASSERT(dMat[1] == 1.0);
@@ -809,7 +810,7 @@ void test9() {
TEST_ASSERT(dMat[7] == 1.0);
TEST_ASSERT(dMat[8] == 0.0);
dMat = MolOps::getDistanceMat(*m, false, false);
dMat = MolOps::getDistanceMat(*m, useBO, useAtomWts);
TEST_ASSERT(dMat);
TEST_ASSERT(dMat[0] == 0.0);
TEST_ASSERT(dMat[1] == 1.0);
@@ -822,7 +823,8 @@ void test9() {
TEST_ASSERT(dMat[8] == 0.0);
// test Issue328:
dMat = MolOps::getDistanceMat(*m, true, false);
useBO = true;
dMat = MolOps::getDistanceMat(*m, useBO, useAtomWts);
TEST_ASSERT(dMat);
TEST_ASSERT(dMat[0] == 0.0);
TEST_ASSERT(dMat[1] == 1.0);
@@ -834,7 +836,8 @@ void test9() {
TEST_ASSERT(dMat[7] == 0.5);
TEST_ASSERT(dMat[8] == 0.0);
dMat = MolOps::getDistanceMat(*m, false, false);
useBO = false;
dMat = MolOps::getDistanceMat(*m, useBO, useAtomWts);
TEST_ASSERT(dMat);
TEST_ASSERT(dMat[0] == 0.0);
TEST_ASSERT(dMat[1] == 1.0);
@@ -846,7 +849,76 @@ void test9() {
TEST_ASSERT(dMat[7] == 1.0);
TEST_ASSERT(dMat[8] == 0.0);
delete m;
useBO = false;
useAtomWts = true;
dMat = MolOps::getDistanceMat(*m, useBO, useAtomWts);
TEST_ASSERT(dMat);
for (auto i = 0; i < m->getNumAtoms(); ++i) {
for (auto j = 0; j < m->getNumAtoms(); ++j) {
std::cerr << dMat[i * m->getNumAtoms() + j] << " ";
}
std::cerr << std::endl;
}
TEST_ASSERT(dMat[0] == 1.0);
TEST_ASSERT(dMat[1] == 1.0);
TEST_ASSERT(dMat[2] == 2.0);
TEST_ASSERT(dMat[3] == 1.0);
TEST_ASSERT(dMat[4] == 1.0);
TEST_ASSERT(dMat[5] == 1.0);
TEST_ASSERT(dMat[6] == 2.0);
TEST_ASSERT(dMat[7] == 1.0);
TEST_ASSERT(dMat[8] == 6. / 8.);
useBO = true;
useAtomWts = true;
dMat = MolOps::getDistanceMat(*m, useBO, useAtomWts);
TEST_ASSERT(dMat);
TEST_ASSERT(dMat[0] == 1.0);
TEST_ASSERT(dMat[1] == 1.0);
TEST_ASSERT(dMat[2] == 1.5);
TEST_ASSERT(dMat[3] == 1.0);
TEST_ASSERT(dMat[4] == 1.0);
TEST_ASSERT(dMat[5] == 0.5);
TEST_ASSERT(dMat[6] == 1.5);
TEST_ASSERT(dMat[7] == 0.5);
TEST_ASSERT(dMat[8] == 6. / 8.);
useBO = false;
useAtomWts = false;
dMat = MolOps::getDistanceMat(*m, useBO, useAtomWts);
TEST_ASSERT(dMat);
TEST_ASSERT(dMat[0] == 0.0);
TEST_ASSERT(dMat[1] == 1.0);
TEST_ASSERT(dMat[2] == 2.0);
TEST_ASSERT(dMat[3] == 1.0);
TEST_ASSERT(dMat[4] == 0.0);
TEST_ASSERT(dMat[5] == 1.0);
TEST_ASSERT(dMat[6] == 2.0);
TEST_ASSERT(dMat[7] == 1.0);
TEST_ASSERT(dMat[8] == 0.0);
// limit participating atoms and bonds
std::vector<int> activeAtoms = {1, 2};
std::vector<const Bond *> activeBonds = {m->getBondWithIdx(1)};
useBO = false;
useAtomWts = false;
std::unique_ptr<double[]> dMat2{
MolOps::getDistanceMat(*m, activeAtoms, activeBonds, useBO, useAtomWts)};
TEST_ASSERT(dMat2);
TEST_ASSERT(dMat2[0] == 0.0);
TEST_ASSERT(dMat2[1] == 1.0);
TEST_ASSERT(dMat2[2] == 1.0);
TEST_ASSERT(dMat2[3] == 0.0);
useBO = true;
useAtomWts = true;
dMat2.reset(
MolOps::getDistanceMat(*m, activeAtoms, activeBonds, useBO, useAtomWts));
TEST_ASSERT(dMat2);
TEST_ASSERT(dMat2[0] == 1.0);
TEST_ASSERT(dMat2[1] == 0.5);
TEST_ASSERT(dMat2[2] == 0.5);
TEST_ASSERT(dMat2[3] == 6.0 / 8.0);
BOOST_LOG(rdInfoLog) << "Finished" << std::endl;
}
@@ -8105,6 +8177,53 @@ M END)CTAB"_ctab;
BOOST_LOG(rdInfoLog) << "\tdone" << std::endl;
}
void testGet3DDistanceMatrix() {
BOOST_LOG(rdInfoLog)
<< "-----------------------\n testing get3DDistanceMat(). " << std::endl;
auto mol = R"CTAB(bogus example
RDKit 3D
3 2 0 0 0 0 0 0 0 0999 V2000
0.0000 0.0000 0.1000 C 0 0 0 0 0 0 0 0 0 0 0 0
1.2000 0.0000 0.1000 C 0 0 0 0 0 0 0 0 0 0 0 0
2.5000 0.0000 0.1000 O 0 0 0 0 0 0 0 0 0 0 0 0
1 2 2 0
2 3 1 0
M END)CTAB"_ctab;
TEST_ASSERT(mol);
double *dm = MolOps::get3DDistanceMat(*mol);
TEST_ASSERT(dm);
TEST_ASSERT(dm[0] == 0.0);
TEST_ASSERT(dm[1] == 1.2);
TEST_ASSERT(dm[2] == 2.5);
TEST_ASSERT(dm[3] == 1.2);
TEST_ASSERT(dm[4] == 0.0);
TEST_ASSERT(dm[5] == 1.3);
TEST_ASSERT(dm[6] == 2.5);
TEST_ASSERT(dm[7] == 1.3);
TEST_ASSERT(dm[8] == 0.0);
// this will use a cached version:
double *dm2 = MolOps::get3DDistanceMat(*mol);
TEST_ASSERT(dm == dm2)
int confId = -1;
bool useAtomWts = true;
dm = MolOps::get3DDistanceMat(*mol, confId, useAtomWts);
TEST_ASSERT(dm);
TEST_ASSERT(dm[0] == 1.0);
TEST_ASSERT(dm[1] == 1.2);
TEST_ASSERT(dm[2] == 2.5);
TEST_ASSERT(dm[3] == 1.2);
TEST_ASSERT(dm[4] == 1.0);
TEST_ASSERT(dm[5] == 1.3);
TEST_ASSERT(dm[6] == 2.5);
TEST_ASSERT(dm[7] == 1.3);
TEST_ASSERT(dm[8] == 6.0 / 8.0);
BOOST_LOG(rdInfoLog) << "\tdone" << std::endl;
}
int main() {
RDLog::InitLogs();
// boost::logging::enable_logs("rdApp.debug");
@@ -8219,6 +8338,7 @@ int main() {
testRemoveAndTrackIsotopes();
testGithub3854();
testSetTerminalAtomCoords();
testGet3DDistanceMatrix();
return 0;
}

View File

@@ -27,18 +27,32 @@ void testBaseFunctionality() {
<< " testBaseFunctionality" << std::endl;
ROMOL_SPTR mol(SmilesToMol("CC[C@H](C)F"));
MolBundle bundle;
TEST_ASSERT(bundle.size() == 0);
TEST_ASSERT(bundle.addMol(mol) == 1);
TEST_ASSERT(bundle.size() == 1);
TEST_ASSERT(bundle.addMol(ROMOL_SPTR(SmilesToMol("CC[C@@H](C)F"))) == 2);
TEST_ASSERT(bundle.size() == 2);
{
MolBundle bundle;
TEST_ASSERT(bundle.size() == 0);
TEST_ASSERT(bundle.addMol(mol) == 1);
TEST_ASSERT(bundle.size() == 1);
TEST_ASSERT(bundle.addMol(ROMOL_SPTR(SmilesToMol("CC[C@@H](C)F"))) == 2);
TEST_ASSERT(bundle.size() == 2);
MolBundle bundle2(bundle);
TEST_ASSERT(bundle2.size() == 2);
MolBundle bundle2(bundle);
TEST_ASSERT(bundle2.size() == 2);
TEST_ASSERT(bundle.getMol(0)->getNumAtoms() == 5);
TEST_ASSERT(bundle[0]->getNumAtoms() == 5);
TEST_ASSERT(bundle.getMol(0)->getNumAtoms() == 5);
TEST_ASSERT(bundle[0]->getNumAtoms() == 5);
}
{
FixedMolSizeMolBundle bundle;
TEST_ASSERT(bundle.size() == 0);
TEST_ASSERT(bundle.addMol(mol) == 1);
TEST_ASSERT(bundle.size() == 1);
TEST_ASSERT(bundle.addMol(ROMOL_SPTR(SmilesToMol("CC[C@@H](C)F"))) == 2);
TEST_ASSERT(bundle.size() == 2);
FixedMolSizeMolBundle bundle2(bundle);
TEST_ASSERT(bundle2.size() == 2);
}
BOOST_LOG(rdInfoLog) << " Done." << std::endl;
}

View File

@@ -1,5 +1,5 @@
//
// Copyright (C) 2004-2018 Greg Landrum and Rational Discovery LLC
// Copyright (C) 2004-2021 Greg Landrum and other RDKit contributors
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
@@ -1725,6 +1725,36 @@ void testPropertyOptions() {
BOOST_LOG(rdErrorLog) << "\tdone" << std::endl;
}
void testAdditionalQueryPickling() {
BOOST_LOG(rdErrorLog) << "-------------------------------------" << std::endl;
BOOST_LOG(rdErrorLog) << "Testing property reading options" << std::endl;
auto m1 = "CCCC"_smarts;
m1->getAtomWithIdx(0)->expandQuery(
makeAtomNumQuery(8), Queries::CompositeQueryType::COMPOSITE_AND);
m1->getAtomWithIdx(1)->expandQuery(
makeAtomNumQuery<ATOM_GREATER_QUERY>(8, "greater_AtomAtomicNum"),
Queries::CompositeQueryType::COMPOSITE_XOR);
m1->getAtomWithIdx(2)->expandQuery(
makeAtomNumQuery<ATOM_LESS_QUERY>(8, "less_AtomAtomicNum"),
Queries::CompositeQueryType::COMPOSITE_OR);
ATOM_SET_QUERY *sq = new ATOM_SET_QUERY();
sq->setDataFunc(queryAtomNum);
sq->insert(6);
sq->insert(8);
sq->setDescription("AtomAtomicNum");
m1->getAtomWithIdx(3)->expandQuery(sq,
Queries::CompositeQueryType::COMPOSITE_OR);
std::string pkl;
MolPickler::pickleMol(*m1, pkl);
RWMol m2(pkl);
for (auto i = 0u; i < m2.getNumAtoms(); ++i) {
TEST_ASSERT(describeQuery(m1->getAtomWithIdx(i)) ==
describeQuery(m2.getAtomWithIdx(i)));
}
BOOST_LOG(rdErrorLog) << "\tdone" << std::endl;
}
int main(int argc, char *argv[]) {
RDLog::InitLogs();
bool doLong = false;
@@ -1768,4 +1798,5 @@ int main(int argc, char *argv[]) {
testHistoricalConfs();
testConformerOptions();
testPropertyOptions();
testAdditionalQueryPickling();
}

View File

@@ -23,6 +23,9 @@
- The function `mol_from_smarts()` in the PostgreSQL cartridge has been
deprecated and will be removed in the next release. Please use the
`qmol_from_smarts()` function instead.
- The `computeBalabanJ()` functions from the `MolOps` namespace have been
deprecated and will be removed in the next release. These have not been
exposed to Python, so this will not affect any Python code.