mirror of
https://github.com/rdkit/rdkit.git
synced 2026-06-03 21:44:30 +08:00
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:
@@ -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()
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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(">"));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user