// // Copyright (C) 2006-2020 Greg Landrum // // @@ 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. // #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace RDKit; void testDeleteSubstruct() { ROMol *mol1 = nullptr, *mol2 = nullptr, *matcher1 = nullptr, *matcher2 = nullptr, *matcher3 = nullptr; std::string smi, sma; BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing deleteSubstruct" << std::endl; // a lot of the seemingly repetitive stuff is here for Issue96 smi = "CCC(=O).C=O"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); TEST_ASSERT(mol1->getNumAtoms() == 6) sma = "C=O"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); mol2 = deleteSubstructs(*mol1, *matcher1, 0); delete mol1; TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 2) mol1 = deleteSubstructs(*mol2, *matcher1, 0); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 2) delete matcher1; delete mol1; delete mol2; sma = "[Cl;H1&X1,-]"; matcher1 = SmartsToMol(sma); sma = "[Na+]"; matcher2 = SmartsToMol(sma); sma = "[O;H2,H1&-,X0&-2]"; matcher3 = SmartsToMol(sma); mol1 = SmilesToMol("CCO.Cl"); TEST_ASSERT(mol1); TEST_ASSERT(mol1->getNumAtoms() == 4); mol2 = deleteSubstructs(*mol1, *matcher1, true); delete mol1; TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 3); mol1 = deleteSubstructs(*mol2, *matcher2, true); delete mol1; TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 3); mol1 = deleteSubstructs(*mol2, *matcher3, true); delete mol1; TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 3); delete mol2; delete matcher3; mol1 = SmilesToMol("CC(=O)[O-].[Na+]"); TEST_ASSERT(mol1); TEST_ASSERT(mol1->getNumAtoms() == 5); delete matcher1; matcher1 = SmartsToMol("[Cl;H1&X1,-]"); delete matcher2; matcher2 = SmartsToMol("[Na+]"); mol2 = deleteSubstructs(*mol1, *matcher1, true); delete mol1; TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 5); mol1 = deleteSubstructs(*mol2, *matcher2, true); delete mol2; TEST_ASSERT(mol1); TEST_ASSERT(mol1->getNumAtoms() == 4); mol2 = deleteSubstructs(*mol1, *matcher1, true); TEST_ASSERT(mol1); TEST_ASSERT(mol1->getNumAtoms() == 4); delete mol2; delete mol1; delete matcher1; delete matcher2; // test chirality smi = "CCO[C@H](N)(P)"; mol1 = SmilesToMol(smi); matcher1 = SmartsToMol("O[C@H](N)(P)"); mol2 = deleteSubstructs(*mol1, *matcher1, false, true); std::string smi1 = MolToSmiles(*mol2, true); std::cerr << "1 smi: " << smi1 << std::endl; TEST_ASSERT(smi1 == "CC"); delete mol2; // still matches with non-chiral mol2 = deleteSubstructs(*mol1, *matcher1, false, false); smi1 = MolToSmiles(*mol2, true); std::cerr << "1 smi: " << smi1 << std::endl; TEST_ASSERT(smi1 == "CC"); delete mol2; delete matcher1; matcher1 = SmartsToMol("O[C@@H](N)(P)"); mol2 = deleteSubstructs(*mol1, *matcher1, false, true); smi1 = MolToSmiles(*mol2, true); std::cerr << "2 smi: " << smi1 << std::endl; TEST_ASSERT(smi1 == "CCO[C@H](N)P"); delete mol2; // still matches with non-chiral mol2 = deleteSubstructs(*mol1, *matcher1, false, false); smi1 = MolToSmiles(*mol2, true); std::cerr << "2 smi: " << smi1 << std::endl; TEST_ASSERT(smi1 == "CC"); delete mol2; delete mol1; delete matcher1; BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testReplaceSubstructs() { ROMol *mol1 = nullptr, *matcher1 = nullptr, *frag = nullptr; std::string smi, sma; std::vector vect; BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing replaceSubstruct" << std::endl; smi = "CCCC"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); sma = "C(=O)O"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); smi = "N"; frag = SmilesToMol(smi); TEST_ASSERT(frag); vect = replaceSubstructs(*mol1, *matcher1, *frag); TEST_ASSERT(vect.size() == 1); TEST_ASSERT(mol1->getNumAtoms() == 4); TEST_ASSERT(vect[0]->getNumAtoms() == 4); delete mol1; smi = "CCCC(=O)O"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); vect = replaceSubstructs(*mol1, *matcher1, *frag); TEST_ASSERT(vect.size() == 1); TEST_ASSERT(mol1->getNumAtoms() == 6); TEST_ASSERT(vect[0]->getNumAtoms() == 4); vect = replaceSubstructs(*mol1, *matcher1, *frag, true); TEST_ASSERT(vect.size() == 1); TEST_ASSERT(mol1->getNumAtoms() == 6); TEST_ASSERT(vect[0]->getNumAtoms() == 4); delete mol1; smi = "OC(=O)CCCC(=O)O"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); vect = replaceSubstructs(*mol1, *matcher1, *frag); TEST_ASSERT(vect.size() == 2); TEST_ASSERT(mol1->getNumAtoms() == 9); TEST_ASSERT(vect[0]->getNumAtoms() == 7); TEST_ASSERT(vect[1]->getNumAtoms() == 7); // use replaceAll when a single replacement is available: vect = replaceSubstructs(*mol1, *matcher1, *frag, true); TEST_ASSERT(vect.size() == 1); TEST_ASSERT(mol1->getNumAtoms() == 9); TEST_ASSERT(vect[0]->getNumAtoms() == 5); delete matcher1; sma = "C=O"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); delete mol1; smi = "CC(=O)C"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); vect = replaceSubstructs(*mol1, *matcher1, *frag); TEST_ASSERT(vect.size() == 1); TEST_ASSERT(mol1->getNumAtoms() == 4); TEST_ASSERT(vect[0]->getNumAtoms() == 3); delete mol1; smi = "C1C(=O)C1"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); vect = replaceSubstructs(*mol1, *matcher1, *frag); TEST_ASSERT(vect.size() == 1); TEST_ASSERT(mol1->getNumAtoms() == 4); TEST_ASSERT(vect[0]->getNumAtoms() == 3); TEST_ASSERT(vect[0]->getNumBonds() == 3); // test chirality delete mol1; delete matcher1; sma = "P([C@H](N)O)"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); // test stereo matching smi = "CCP([C@H](N)O)CC"; mol1 = SmilesToMol(smi); vect = replaceSubstructs(*mol1, *matcher1, *frag, false, 0, true); TEST_ASSERT(vect.size() == 1); std::string smi1 = MolToSmiles(*vect[0], true); TEST_ASSERT(smi1 == "CCNCC"); // should also match when matching non-chiral vect = replaceSubstructs(*mol1, *matcher1, *frag, false, 0, false); TEST_ASSERT(vect.size() == 1); smi1 = MolToSmiles(*vect[0], true); TEST_ASSERT(smi1 == "CCNCC"); delete mol1; smi = "CCP([C@@H](N)O)CC"; mol1 = SmilesToMol(smi); vect = replaceSubstructs(*mol1, *matcher1, *frag, false, 0, true); TEST_ASSERT(vect.size() == 1); smi1 = MolToSmiles(*vect[0], true); std::cerr << "replaceSub smi1:" << smi1 << std::endl; ; // no change TEST_ASSERT(smi1 == "CCP(CC)[C@@H](N)O"); // should also match when matching non-chiral vect = replaceSubstructs(*mol1, *matcher1, *frag, false, 0, false); TEST_ASSERT(vect.size() == 1); smi1 = MolToSmiles(*vect[0], true); std::cerr << "replaceSub smi1:" << smi1 << std::endl; delete mol1; delete frag; delete matcher1; // no change TEST_ASSERT(smi1 == "CCNCC"); BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testReplaceSubstructs2() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "More testing of replaceSubstruct" << std::endl; { std::string smi = "CCC(=O)O"; ROMol *mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); std::string sma = "[$(O-C(=O))]"; ROMol *matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); std::string repl = "NC"; ROMol *frag = SmilesToMol(repl); TEST_ASSERT(frag); std::vector vect = replaceSubstructs(*mol1, *matcher1, *frag); TEST_ASSERT(vect.size() == 1); TEST_ASSERT(mol1->getNumAtoms() == 5); TEST_ASSERT(vect[0]->getNumAtoms() == 6); std::string csmi1 = MolToSmiles(*vect[0], true); repl = "CN"; delete frag; frag = SmilesToMol(repl); TEST_ASSERT(frag); vect = replaceSubstructs(*mol1, *matcher1, *frag, true, 1); TEST_ASSERT(vect.size() == 1); TEST_ASSERT(mol1->getNumAtoms() == 5); TEST_ASSERT(vect[0]->getNumAtoms() == 6); std::string csmi2 = MolToSmiles(*vect[0], true); TEST_ASSERT(csmi2 == csmi1); delete mol1; delete frag; delete matcher1; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testReplaceSidechains() { ROMol *mol1 = nullptr, *mol2 = nullptr, *matcher1 = nullptr; std::string smi, sma; BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing replaceSidechains" << std::endl; smi = "ClC1CC(F)C1"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); sma = "C1CCC1"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); mol2 = replaceSidechains(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 6); delete mol2; delete mol1; smi = "ClC1C(F)C1"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); mol2 = replaceSidechains(*mol1, *matcher1); TEST_ASSERT(!mol2); delete mol1; delete mol2; delete matcher1; sma = "C=O"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); smi = "CC=O"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); mol2 = replaceSidechains(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 3); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[1*]C=O"); delete mol1; delete mol2; smi = "CC(C)=O"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); mol2 = replaceSidechains(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 4); smi = MolToSmiles(*mol2, true); // there's no way to guarantee the order here: TEST_ASSERT(smi == "[1*]C([2*])=O" || smi == "[2*]C([1*])=O"); delete mol1; delete mol2; delete matcher1; sma = "P([C@H](N)O)"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); smi = "CCP([C@H](N)O)CC"; mol1 = SmilesToMol(smi); mol2 = replaceSidechains(*mol1, *matcher1, true); TEST_ASSERT(mol2); smi = MolToSmiles(*mol2, true); std::cerr << "sidechains chiral=true smi1;;; " << smi << std::endl; delete mol2; mol2 = replaceSidechains(*mol1, *matcher1, true); TEST_ASSERT(mol2); smi = MolToSmiles(*mol2, true); std::cerr << "sidechains chiral=false smi1;;; " << smi << std::endl; delete mol1; delete mol2; delete matcher1; BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testReplaceCore() { ROMol *mol1 = nullptr, *mol2 = nullptr, *matcher1 = nullptr; std::string smi, sma; BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing replaceCore" << std::endl; smi = "ClC1CC(F)C1"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); sma = "C1CC1"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(!mol2); delete mol1; delete mol2; delete matcher1; smi = "ClC1CC(F)C1"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); sma = "C1CCC1"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 4); smi = MolToSmiles(*mol2, true); // there's no way to guarantee the order here: TEST_ASSERT(smi == "[1*]Cl.[2*]F" || smi == "[2*]Cl.[1*]F"); delete mol1; delete mol2; delete matcher1; smi = "CCC=O"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); sma = "C=O"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 3); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[1*]CC"); delete mol1; delete mol2; delete matcher1; smi = "C1C(=O)CC1"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); sma = "C=O"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 5); smi = MolToSmiles(*mol2, true); // there's no way to guarantee the order here: TEST_ASSERT(smi == "[1*]CCC[2*]" || smi == "[1*]CCC[2*]"); delete mol1; delete mol2; delete matcher1; smi = "CNC"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); sma = "N"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 4); smi = MolToSmiles(*mol2, true); // there's no way to guarantee the order here: TEST_ASSERT(smi == "[1*]C.[2*]C" || smi == "[2*]C.[1*]C"); delete mol1; delete mol2; delete matcher1; smi = "OC1CCC1"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); sma = "[CH2][CH2][CH2]"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); smi = MolToSmiles(*mol2, true); TEST_ASSERT(mol2->getNumAtoms() == 4); smi = MolToSmiles(*mol2, true); // there's no way to guarantee the order here: TEST_ASSERT(smi == "[2*]C([1*])O" || smi == "[1*]C([2*])O"); delete mol1; delete mol2; delete matcher1; smi = "C/C=C/CN/C=C/O"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); sma = "N"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 9); smi = MolToSmiles(*mol2, true); // there's no way to guarantee the order here: TEST_ASSERT(smi == "[1*]C/C=C/C.[2*]/C=C/O" || smi == "[1*]/C=C/O.[2*]C/C=C/C"); delete mol1; delete mol2; delete matcher1; smi = "C[C@](F)(Cl)N"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); sma = "N"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 5); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[1*][C@@](C)(F)Cl"); delete mol1; delete mol2; smi = "C[C@](F)(N)Cl"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 5); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[1*][C@](C)(F)Cl"); delete mol1; delete mol2; smi = "N[C@](C)(F)Cl"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 5); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[1*][C@](C)(F)Cl"); delete mol1; delete mol2; delete matcher1; smi = "C[C@@](F)(Cl)N"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); sma = "N"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 5); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[1*][C@](C)(F)Cl"); delete mol1; delete mol2; smi = "C[C@@](F)(N)Cl"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 5); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[1*][C@@](C)(F)Cl"); smi = "C[C@@](N)(Cl)F"; delete mol1; delete mol2; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 5); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[1*][C@@](C)(F)Cl"); delete mol1; delete mol2; delete matcher1; smi = "C[C@H](F)N"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); sma = "N"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 4); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[1*][C@H](C)F"); delete mol1; delete mol2; smi = "C[C@H](N)F"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 4); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[1*][C@@H](C)F"); delete mol1; delete mol2; smi = "N[C@H](C)F"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 4); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[1*][C@H](C)F"); delete mol1; delete mol2; smi = "F[C@H](C)N"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 4); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[1*][C@@H](C)F"); delete mol1; delete mol2; smi = "N[C@H]1CCCO1"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 6); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[1*][C@H]1CCCO1"); delete mol1; delete mol2; delete matcher1; smi = "ClC1CC(F)C1"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); sma = "*C1CC(*)C1"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); mol2 = replaceCore(*mol1, *matcher1, false); TEST_ASSERT(mol2); smi = MolToSmiles(*mol2, true); TEST_ASSERT(mol2->getNumAtoms() == 4); smi = MolToSmiles(*mol2, true); // there's no way to guarantee the order here: TEST_ASSERT(smi == "[1*]F.[2*]Cl" || smi == "[1*]Cl.[2*]F"); delete mol1; delete mol2; delete matcher1; BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } struct CoreTest { const char *smiles; const char *smarts; bool replaceDummies; bool labelByIndex; bool requireDummyMatch; bool useChirality; const char *expected; }; void testReplaceCore2() { const CoreTest tests[] = { {"C1O[C@@]1(OC)NC", "C1O[C@]1(*)*", false, false, false, false, "[1*]OC.[2*]NC"}, {"C1O[C@@]1(OC)NC", "C1O[C@]1(*)*", false, false, false, true, "[1*]NC.[2*]OC"}, {"C1O[C@@]1(OC)NC", "C1O[C@]1(*)*", false, true, false, false, "[3*]OC.[4*]NC"}, {"C1O[C@@]1(OC)NC", "C1O[C@]1(*)*", false, true, false, true, "[3*]NC.[4*]OC"}, {"C1O[C@@]1(OC)NC", "C1O[C@]1(*)*", true, false, false, false, "[1*]C.[2*]C"}, {"C1O[C@@]1(OC)NC", "C1O[C@]1(*)*", true, false, false, true, "[1*]C.[2*]C"}, {"C1O[C@@]1(OC)NC", "C1O[C@]1(*)*", true, true, false, false, "[3*]C.[4*]C"}, {"C1O[C@@]1(OC)NC", "C1O[C@]1(*)*", true, true, false, true, "[3*]C.[4*]C"}, {"C1O[C@]1(OC)NC", "C1O[C@]1(*)*", false, false, false, false, "[1*]OC.[2*]NC"}, {"C1O[C@]1(OC)NC", "C1O[C@]1(*)*", false, false, false, true, "[1*]OC.[2*]NC"}, {"C1O[C@]1(OC)NC", "C1O[C@]1(*)*", false, true, false, false, "[3*]OC.[4*]NC"}, {"C1O[C@]1(OC)NC", "C1O[C@]1(*)*", false, true, false, true, "[3*]OC.[4*]NC"}, {"C1O[C@]1(OC)NC", "C1O[C@]1(*)*", true, false, false, false, "[1*]C.[2*]C"}, {"C1O[C@]1(OC)NC", "C1O[C@]1(*)*", true, false, false, true, "[1*]C.[2*]C"}, {"C1O[C@]1(OC)NC", "C1O[C@]1(*)*", true, true, false, false, "[3*]C.[4*]C"}, {"C1O[C@]1(OC)NC", "C1O[C@]1(*)*", true, true, false, true, "[3*]C.[4*]C"}, {"C1O[C@@]1(OC)NCC", "C1O[C@]1(*)*", true, false, false, false, "[1*]C.[2*]CC"}, {"C1O[C@@]1(OC)NCC", "C1O[C@]1(*)*", true, false, false, true, "[1*]CC.[2*]C"}, {"C1O[C@@]1(OC)NCC", "C1O[C@]1(*)*", true, true, false, false, "[3*]C.[4*]CC"}, {"C1O[C@@]1(OC)NCC", "C1O[C@]1(*)*", true, true, false, true, "[3*]CC.[4*]C"}, {"C1O[C@]1(OC)NCC", "C1O[C@]1(*)*", true, false, false, false, "[1*]C.[2*]CC"}, {"C1O[C@]1(OC)NCC", "C1O[C@]1(*)*", true, false, false, true, "[1*]C.[2*]CC"}, {"C1O[C@]1(OC)NCC", "C1O[C@]1(*)*", true, true, false, false, "[3*]C.[4*]CC"}, {"C1O[C@]1(OC)NCC", "C1O[C@]1(*)*", true, true, false, true, "[3*]C.[4*]CC"}, {"CNOC", "CONC", false, true, false, false, ""}, {"PCNOCS", "CONC", false, true, false, false, "*S.[3*]P"}, {"PCNOCS", "CONC", false, false, false, false, "[1*]S.[2*]P"}, {"PCONCS", "CONC", false, true, false, false, "*P.[3*]S"}, {"PCONCS", "CONC", false, false, false, false, "[1*]P.[2*]S"} }; size_t num_tests = sizeof(tests) / sizeof(CoreTest); for (size_t i = 0; i < num_tests; ++i) { ROMOL_SPTR mol(SmilesToMol(tests[i].smiles)); ROMOL_SPTR smarts(SmartsToMol(tests[i].smarts)); ROMOL_SPTR res(replaceCore(*mol.get(), *smarts.get(), tests[i].replaceDummies, tests[i].labelByIndex, tests[i].requireDummyMatch, tests[i].useChirality)); if (tests[i].expected) { TEST_ASSERT(res.get()); std::string smi = MolToSmiles(*res.get(), true); if (smi != tests[i].expected) { std::cerr << i << " " << tests[i].smiles << " " << tests[i].smarts << " " << (int)tests[i].replaceDummies << " " << (int)tests[i].labelByIndex << " " << (int)tests[i].requireDummyMatch << " " << (int)tests[i].useChirality << " expected:" << tests[i].expected << " got => " << smi << std::endl; } TEST_ASSERT(smi == tests[i].expected); } else { TEST_ASSERT(!res.get()); } MatchVectType matchV; // do the substructure matching and get the atoms that match the query const bool recursionPossible = true; bool matchFound = SubstructMatch(*mol.get(), *smarts.get(), matchV, recursionPossible, tests[i].useChirality); TEST_ASSERT(matchFound); res = ROMOL_SPTR(replaceCore(*mol.get(), *smarts.get(), matchV, tests[i].replaceDummies, tests[i].labelByIndex, tests[i].requireDummyMatch)); if (tests[i].expected) { TEST_ASSERT(res.get()); std::string smi = MolToSmiles(*res.get(), true); if (smi != tests[i].expected) { std::cerr << i << " " << tests[i].smiles << " " << tests[i].smarts << " " << (int)tests[i].replaceDummies << " " << (int)tests[i].labelByIndex << " " << (int)tests[i].requireDummyMatch << " " << (int)tests[i].useChirality << " expected:" << tests[i].expected << " got => " << smi << std::endl; } TEST_ASSERT(smi == tests[i].expected); } else { TEST_ASSERT(!res.get()); } } } void testReplaceCoreLabels() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing replaceCore with labels" << std::endl; { std::string sma = "n1cocc1"; ROMol *matcher = SmartsToMol(sma); TEST_ASSERT(matcher); std::string smi = "n1c(CC)oc(C)c1"; ROMol *mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); ROMol *mol2 = replaceCore(*mol1, *matcher, true, true); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 5); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[1*]CC.[3*]C"); delete mol1; delete mol2; delete matcher; } { std::string sma = "n1cocc1"; ROMol *matcher = SmartsToMol(sma); TEST_ASSERT(matcher); std::string smi = "n1c(C)oc(CC)c1"; ROMol *mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); ROMol *mol2 = replaceCore(*mol1, *matcher, true, true); TEST_ASSERT(mol2); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[1*]C.[3*]CC"); delete mol1; delete mol2; delete matcher; } { std::string sma = "n1cocc1"; ROMol *matcher = SmartsToMol(sma); TEST_ASSERT(matcher); std::string smi = "CCC1=C(OC)N=C(C)O1"; ROMol *mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); ROMol *mol2 = replaceCore(*mol1, *matcher, true, true); TEST_ASSERT(mol2); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[1*]C.[3*]CC.[4*]OC"); delete mol1; delete mol2; delete matcher; } { std::string sma = "n1cocc1"; ROMol *matcher = SmartsToMol(sma); TEST_ASSERT(matcher); std::string smi = "C1=C(OC)N=CO1"; ROMol *mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); ROMol *mol2 = replaceCore(*mol1, *matcher, true, true); TEST_ASSERT(mol2); smi = MolToSmiles(*mol2, true); TEST_ASSERT(smi == "[4*]OC"); delete mol1; delete mol2; delete matcher; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testReplaceCoreCrash() { ROMol *mol1 = nullptr, *mol2 = nullptr, *matcher1 = nullptr; std::string smi, sma; BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing a former crash in replaceCore" << std::endl; std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/ChemTransforms/testData/oldcrash.mol"; mol1 = MolFileToMol(pathName); TEST_ASSERT(mol1); sma = "N"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 6); smi = MolToSmiles(*mol2, true); delete mol1; delete mol2; delete matcher1; // there's no way to guarantee the order here: TEST_ASSERT(smi == "[1*]CC.[2*]CC" || smi == "[2*]CC.[1*]CC"); BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testReplaceCorePositions() { ROMol *mol1 = nullptr, *mol2 = nullptr, *matcher1 = nullptr; std::string smi, sma; BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing that atom positions are correctly copied by replaceCore" << std::endl; std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/ChemTransforms/testData/core_location.mol"; mol1 = MolFileToMol(pathName); TEST_ASSERT(mol1); TEST_ASSERT(mol1->getNumAtoms() == 8); sma = "c1ccccc1"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); mol2 = replaceCore(*mol1, *matcher1); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 4); RDGeom::Point3D op, np; op = mol1->getConformer().getAtomPos(6); np = mol2->getConformer().getAtomPos(0); TEST_ASSERT(feq(op.x, np.x)); TEST_ASSERT(feq(op.y, np.y)); TEST_ASSERT(feq(op.z, np.z)); op = mol1->getConformer().getAtomPos(7); np = mol2->getConformer().getAtomPos(1); TEST_ASSERT(feq(op.x, np.x)); TEST_ASSERT(feq(op.y, np.y)); TEST_ASSERT(feq(op.z, np.z)); op = mol1->getConformer().getAtomPos(0); np = mol2->getConformer().getAtomPos(2); TEST_ASSERT(feq(op.x, np.x)); TEST_ASSERT(feq(op.y, np.y)); TEST_ASSERT(feq(op.z, np.z)); op = mol1->getConformer().getAtomPos(4); np = mol2->getConformer().getAtomPos(3); TEST_ASSERT(feq(op.x, np.x)); TEST_ASSERT(feq(op.y, np.y)); TEST_ASSERT(feq(op.z, np.z)); delete mol1; delete mol2; delete matcher1; BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testReplaceCoreMatchVectMultipleMappingToCore() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing replaceCore with a matchvect with multiple " "dummy target atoms mapping to single core atom" << std::endl; std::unique_ptr core, mol; // 2 dummies in core map to single N in molecule core = "C1([*:1])C([*:2])CC1"_smiles; mol = "C1CC2NC12"_smiles; MatchVectType mapping{{0, 4}, {1, 3}, {2, 2}, {3, 3}, {4, 1}, {5, 0}}; ROMOL_SPTR result(replaceCore(*mol.get(), *core.get(), mapping, false)); std::string smi = MolToSmiles(*result.get(), true); TEST_ASSERT("[1*]N[2*]" == smi); // 3 dummies in core map to single N in molecule core = "C1([*:1])C([*:2])C([*:3])C1"_smiles; mol = "C1C2C3N2C13"_smiles; mapping.assign({{0, 4}, {1, 3}, {2, 2}, {3, 3}, {4, 1}, {5, 3}, {6, 0}}); result.reset(replaceCore(*mol.get(), *core.get(), mapping, false)); smi = MolToSmiles(*result.get(), true); TEST_ASSERT("[1*]N([2*])[3*]" == smi); } void testReplaceCoreMatchVect() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing replaceCore with a matchvect" << std::endl; std::map expected; expected["[1*]N.[2*]O.[3*]P"] = 1; expected["[1*]N.[2*]P.[3*]O"] = 1; expected["[1*]O.[2*]N.[3*]P"] = 1; expected["[1*]O.[2*]P.[3*]N"] = 1; expected["[1*]P.[2*]N.[3*]O"] = 1; expected["[1*]P.[2*]O.[3*]N"] = 1; const char *smiles = "NC1C(O)C1P"; const char *smarts = "*C1C(*)C1*"; ROMOL_SPTR mol(SmilesToMol(smiles)); ROMOL_SPTR query(SmartsToMol(smarts)); std::vector matches; const bool uniquify = false; unsigned int matchCount = SubstructMatch(*mol.get(), *query.get(), matches, uniquify); TEST_ASSERT(matchCount); std::map results; const bool replaceDummies = false; for (unsigned int i = 0; i < matchCount; ++i) { ROMOL_SPTR res( replaceCore(*mol.get(), *query.get(), matches[i], replaceDummies)); std::string smi = MolToSmiles(*res.get(), true); results[smi] = results[smi] + 1; } TEST_ASSERT(expected == results); { MatchVectType fake; fake.push_back(std::make_pair(1, 100)); try { ROMOL_SPTR res( replaceCore(*mol.get(), *query.get(), fake, replaceDummies)); TEST_ASSERT(0); } catch (...) { BOOST_LOG(rdInfoLog) << "-- caught error"; } } { MatchVectType fake; fake.push_back(std::make_pair(100, 1)); try { ROMOL_SPTR res( replaceCore(*mol.get(), *query.get(), fake, replaceDummies)); TEST_ASSERT(0); } catch (...) { BOOST_LOG(rdInfoLog) << "-- caught error"; } } { MatchVectType fake; fake.push_back(std::make_pair(1, -10)); try { ROMOL_SPTR res( replaceCore(*mol.get(), *query.get(), fake, replaceDummies)); TEST_ASSERT(0); } catch (...) { BOOST_LOG(rdInfoLog) << "-- caught error"; } } { MatchVectType fake; fake.push_back(std::make_pair(-10, 1)); try { ROMOL_SPTR res( replaceCore(*mol.get(), *query.get(), fake, replaceDummies)); TEST_ASSERT(0); } catch (...) { BOOST_LOG(rdInfoLog) << "-- caught error"; } } } void testMurckoDecomp() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing murcko decomposition" << std::endl; const char *testMolecules[][2] = { {"C1CC1CCC1CC(C)C1", "C1CC1CCC1CCC1"}, {"CC1CCC1", "C1CCC1"}, {"NCNCC2CC2C1CC1O", "C1CC1C1CC1"}, {"OC2C(C)C21C(N)C1C", "C2CC12CC1"}, // Spiro {"C1CC1C(=O)OC", "C1CC1"}, // Carbonyl outside scaffold {"C1CC1C=C", "C1CC1"}, // Double bond outside scaffold {"C1CC1C=CC1CC1C=CNNCO", "C1CC1C=CC1CC1"}, // Double bond in scaffold {"CC1CC1C(N)C1C(N)C1", "C1CC1CC1CC1"}, {"C1CC1S(=O)C1CC1C=CNNCO", "C1CC1S(=O)C1CC1"}, // S=O group in scaffold {"O=SCNC1CC1S(=O)C1CC1C=CNNCO", "C1CC1S(=O)C1CC1"}, // S=O group outside scaffold {"C1CC1S(=O)(=O)C1CC1C=CNNCO", "C1CC1S(=O)(=O)C1CC1"}, // SO2 group in scaffold {"O=S(CNCNC)(=O)CNC1CC1S(=O)(=O)C1CC1C=CNNCO", "C1CC1S(=O)(=O)C1CC1"}, // SO2 group outside scaffold {"C1CC1C=NO", "C1CC1"}, // Hydroxamide {"C1CC1C(C(C)C)=NC1CC1", "C1CC1C=NC1CC1"}, // Hydroxamide {"C1CC1C#N", "C1CC1"}, // Cyano group {"C1CC1C#CNC", "C1CC1"}, // Acetylene group {"O=C1N(C)C(=O)N1C#CNC", "O=C1NC(=O)N1"}, // Acetylene group {"[O-][N+](=O)c1cc(ccc1Cl)NS(=O)(=O)Cc2ccccc2", "c1ccccc1NS(=O)(=O)Cc2ccccc2"}, {"Cn1cccc1", "c1ccc[nH]1"}, {"C1CC1[CH](C)C1CC1", "C1CC1CC1CC1"}, {"CC(C)c1c(Cl)cc(F)c(Nc2ccc(C)cc2CC(=O)O)c1F", "c1ccc(Nc2ccccc2)cc1"}, {"C1CC1C[C@](Cl)(F)C1CCC1", "C1CC1CCC1CCC1"}, {"C1CC1.C1CC1", "C1CC1.C1CC1"}, // this was a crash at one point {"CCC", ""}, {"EOS", "EOS"}}; unsigned int i = 0; while (1) { std::string smi = testMolecules[i][0]; std::string tgt = testMolecules[i][1]; ++i; if (smi == "EOS") { break; } ROMol *mol = SmilesToMol(smi); ROMol *nMol = MurckoDecompose(*mol); TEST_ASSERT(nMol); delete mol; if (tgt != "") { TEST_ASSERT(nMol->getNumAtoms()); MolOps::sanitizeMol(static_cast(*nMol)); smi = MolToSmiles(*nMol, true); mol = SmilesToMol(tgt); TEST_ASSERT(mol); tgt = MolToSmiles(*mol, true); delete mol; TEST_ASSERT(smi == tgt); } else { TEST_ASSERT(nMol->getNumAtoms() == 0); } delete nMol; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testReplaceCoreRequireDummies() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing replaceCore requiring dummy atoms" << std::endl; { std::string sma = "n1c(*)oc(*)c1"; ROMol *matcher = SmartsToMol(sma); TEST_ASSERT(matcher); std::string smi = "n1c(CC)oc(C)c1"; ROMol *mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); bool replaceDummies = false; bool labelByIndex = true; bool requireDummyMatch = false; ROMol *mol2 = replaceCore(*mol1, *matcher, replaceDummies, labelByIndex, requireDummyMatch); TEST_ASSERT(mol2); TEST_ASSERT(mol2->getNumAtoms() == 5); smi = MolToSmiles(*mol2, true); // If dummies existed and were replaced // use THEIR atom indices TEST_ASSERT(smi == "[2*]CC.[5*]C"); delete mol1; delete mol2; smi = "n1c(CC)oc(C)c1C"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); replaceDummies = false; labelByIndex = true; requireDummyMatch = true; mol2 = replaceCore(*mol1, *matcher, replaceDummies, labelByIndex, requireDummyMatch); TEST_ASSERT(!mol2); delete mol1; smi = "n1c(CC)occ1C"; mol1 = SmilesToMol(smi); TEST_ASSERT(mol1); replaceDummies = false; labelByIndex = true; requireDummyMatch = true; mol2 = replaceCore(*mol1, *matcher, replaceDummies, labelByIndex, requireDummyMatch); TEST_ASSERT(!mol2); delete mol1; delete matcher; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testIssue3453144() { ROMol *mol1 = nullptr, *matcher1 = nullptr, *replacement = nullptr; std::string smi, sma; BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing that atom positions are correctly copied by replaceSubstructs" << std::endl; std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/ChemTransforms/testData/ethanol.mol"; mol1 = MolFileToMol(pathName); TEST_ASSERT(mol1); TEST_ASSERT(mol1->getNumAtoms() == 3); sma = "O"; matcher1 = SmartsToMol(sma); TEST_ASSERT(matcher1); smi = "N"; replacement = SmilesToMol(smi); TEST_ASSERT(replacement); std::vector rs; rs = replaceSubstructs(*mol1, *matcher1, *replacement); TEST_ASSERT(rs.size() == 1); TEST_ASSERT(rs[0]->getNumAtoms() == 3); TEST_ASSERT(rs[0]->getAtomWithIdx(2)->getAtomicNum() == 7); TEST_ASSERT(rs[0]->getNumConformers() == mol1->getNumConformers()); RDGeom::Point3D op, np; op = mol1->getConformer().getAtomPos(0); np = rs[0]->getConformer().getAtomPos(0); TEST_ASSERT(feq(op.x, np.x)); TEST_ASSERT(feq(op.y, np.y)); TEST_ASSERT(feq(op.z, np.z)); op = mol1->getConformer().getAtomPos(1); np = rs[0]->getConformer().getAtomPos(1); TEST_ASSERT(feq(op.x, np.x)); TEST_ASSERT(feq(op.y, np.y)); TEST_ASSERT(feq(op.z, np.z)); delete mol1; delete matcher1; delete replacement; BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testIssue3537675() { std::string smi, sma; BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing handling of substituted non-carbon aromatic atoms" << std::endl; { std::string smi = "c1cccp1C"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); ROMol *nMol = MurckoDecompose(*mol); TEST_ASSERT(nMol->getAtomWithIdx(4)->getNumExplicitHs()); delete mol; delete nMol; } { std::string smi = "c1cccn1C"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); ROMol *nMol = MurckoDecompose(*mol); TEST_ASSERT(nMol->getAtomWithIdx(4)->getNumExplicitHs()); delete mol; delete nMol; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testCombineMols() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing combination of molecules" << std::endl; { std::string smi1 = "C1CCC1"; std::string smi2 = "C1CC1"; ROMol *mol1 = SmilesToMol(smi1); ROMol *mol2 = SmilesToMol(smi2); ROMol *mol3 = combineMols(*mol1, *mol2); TEST_ASSERT(mol3->getNumAtoms() == (mol1->getNumAtoms() + mol2->getNumAtoms())); MolOps::findSSSR(*mol3); TEST_ASSERT(mol3->getRingInfo()->numRings() == 2); delete mol1; delete mol2; delete mol3; } { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/ChemTransforms/testData/ethanol.mol"; ROMol *mol1 = MolFileToMol(pathName); TEST_ASSERT(mol1); ROMol *mol2 = MolFileToMol(pathName); TEST_ASSERT(mol2); TEST_ASSERT(mol1->getNumAtoms() == 3); TEST_ASSERT(mol2->getNumAtoms() == 3); ROMol *mol3 = combineMols(*mol1, *mol2); TEST_ASSERT(mol3->getNumAtoms() == (mol1->getNumAtoms() + mol2->getNumAtoms())); TEST_ASSERT(mol3->getNumConformers() == 1); TEST_ASSERT(feq(mol3->getConformer().getAtomPos(0).x, mol3->getConformer().getAtomPos(3).x)); TEST_ASSERT(feq(mol3->getConformer().getAtomPos(0).y, mol3->getConformer().getAtomPos(3).y)); TEST_ASSERT(feq(mol3->getConformer().getAtomPos(0).z, mol3->getConformer().getAtomPos(3).z)); delete mol3; mol3 = combineMols(*mol1, *mol2, RDGeom::Point3D(1, 0, 0)); TEST_ASSERT(mol3->getNumAtoms() == (mol1->getNumAtoms() + mol2->getNumAtoms())); TEST_ASSERT(mol3->getNumConformers() == 1); TEST_ASSERT(!feq(mol3->getConformer().getAtomPos(0).x, mol3->getConformer().getAtomPos(3).x)); TEST_ASSERT(feq(mol3->getConformer().getAtomPos(0).y, mol3->getConformer().getAtomPos(3).y)); TEST_ASSERT(feq(mol3->getConformer().getAtomPos(0).z, mol3->getConformer().getAtomPos(3).z)); delete mol1; delete mol2; delete mol3; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testAddRecursiveQueries() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing adding recursive queries to a molecule" << std::endl; { std::string smi1 = "CC"; ROMol *mol1 = SmilesToMol(smi1); std::string smi2 = "CO"; ROMOL_SPTR q1(SmilesToMol(smi2)); std::map mp; mp["foo"] = q1; TEST_ASSERT(!mol1->getAtomWithIdx(0)->hasQuery()); addRecursiveQueries(*mol1, mp, "replaceme"); TEST_ASSERT(!mol1->getAtomWithIdx(0)->hasQuery()); mol1->getAtomWithIdx(0)->setProp("replaceme", "foo"); addRecursiveQueries(*mol1, mp, "replaceme"); TEST_ASSERT(mol1->getAtomWithIdx(0)->hasQuery()); TEST_ASSERT(mol1->getAtomWithIdx(0)->getQuery()->getDescription() == "AtomAnd"); TEST_ASSERT(!mol1->getAtomWithIdx(1)->hasQuery()); mol1->getAtomWithIdx(0)->setProp("replaceme", "bar"); bool ok = false; try { addRecursiveQueries(*mol1, mp, "replaceme"); } catch (KeyErrorException &) { ok = true; } TEST_ASSERT(ok); delete mol1; } { std::string smi1 = "CC"; ROMol *mol1 = SmilesToMol(smi1); std::string smi2 = "CO"; ROMOL_SPTR q1(SmilesToMol(smi2)); std::map mp; mp["foo"] = q1; std::vector> labels; mol1->getAtomWithIdx(0)->setProp("replaceme", "foo"); addRecursiveQueries(*mol1, mp, "replaceme", &labels); TEST_ASSERT(mol1->getAtomWithIdx(0)->hasQuery()); TEST_ASSERT(labels.size() == 1); delete mol1; } { std::string smi1 = "CC"; ROMol *mol1 = SmilesToMol(smi1); std::string smi2 = "CO"; ROMOL_SPTR q1(SmilesToMol(smi2)); std::map mp; mp["foo"] = q1; smi2 = "OC"; ROMOL_SPTR q2(SmilesToMol(smi2)); mp["bar"] = q2; std::vector> labels; mol1->getAtomWithIdx(0)->setProp("replaceme", "foo"); mol1->getAtomWithIdx(1)->setProp("replaceme", "bar"); addRecursiveQueries(*mol1, mp, "replaceme", &labels); TEST_ASSERT(mol1->getAtomWithIdx(0)->hasQuery()); TEST_ASSERT(mol1->getAtomWithIdx(1)->hasQuery()); TEST_ASSERT(mol1->getAtomWithIdx(0)->getQuery()->getDescription() == "AtomAnd"); TEST_ASSERT(mol1->getAtomWithIdx(1)->getQuery()->getDescription() == "AtomAnd"); TEST_ASSERT(labels.size() == 2); delete mol1; } { std::string smi1 = "CC"; ROMol *mol1 = SmilesToMol(smi1); std::string smi2 = "CO"; ROMOL_SPTR q1(SmilesToMol(smi2)); std::map mp; mp["foo"] = q1; smi2 = "CN"; ROMOL_SPTR q2(SmilesToMol(smi2)); mp["bar"] = q2; mol1->getAtomWithIdx(0)->setProp("replaceme", "foo,bar"); addRecursiveQueries(*mol1, mp, "replaceme"); TEST_ASSERT(mol1->getAtomWithIdx(0)->hasQuery()); TEST_ASSERT(!mol1->getAtomWithIdx(1)->hasQuery()); TEST_ASSERT(mol1->getAtomWithIdx(0)->getQuery()->getDescription() == "AtomAnd"); MatchVectType mv; std::string msmi = "CCC"; ROMol *mmol = SmilesToMol(msmi); TEST_ASSERT(mmol); TEST_ASSERT(!SubstructMatch(*mmol, *mol1, mv)); delete mmol; msmi = "CCO"; mmol = SmilesToMol(msmi); TEST_ASSERT(mmol); TEST_ASSERT(SubstructMatch(*mmol, *mol1, mv)); delete mmol; msmi = "CCN"; mmol = SmilesToMol(msmi); TEST_ASSERT(mmol); TEST_ASSERT(SubstructMatch(*mmol, *mol1, mv)); delete mmol; delete mol1; } // Tests smarts as props { std::string smi1 = "CC"; ROMol *mol1 = SmilesToMol(smi1); std::map mp; mol1->getAtomWithIdx(0)->setProp("replaceme", "CO"); addRecursiveQueries(*mol1, mp, "replaceme"); TEST_ASSERT(mol1->getAtomWithIdx(0)->hasQuery()); TEST_ASSERT(!mol1->getAtomWithIdx(1)->hasQuery()); TEST_ASSERT(mol1->getAtomWithIdx(0)->getQuery()->getDescription() == "AtomAnd"); MatchVectType mv; std::string msmi = "CCC"; ROMol *mmol = SmilesToMol(msmi); TEST_ASSERT(mmol); TEST_ASSERT(!SubstructMatch(*mmol, *mol1, mv)); delete mmol; msmi = "CCO"; mmol = SmilesToMol(msmi); TEST_ASSERT(mmol); TEST_ASSERT(SubstructMatch(*mmol, *mol1, mv)); delete mmol; delete mol1; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testParseQueryDefFile() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing parsing query definition file" << std::endl; { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/ChemTransforms/testData/query_file1.txt"; std::map qdefs; parseQueryDefFile(pathName, qdefs, false); TEST_ASSERT(!qdefs.empty()); TEST_ASSERT(qdefs.size() == 7); TEST_ASSERT(qdefs.find("AcidChloride") != qdefs.end()); TEST_ASSERT(qdefs.find("AcideChloride") == qdefs.end()); TEST_ASSERT(qdefs.find("AcidChloride.Aliphatic") != qdefs.end()); TEST_ASSERT(qdefs.find("CarboxylicAcid.AlphaAmino") != qdefs.end()); std::string msmi = "CCC(=O)Cl"; ROMol *mmol = SmilesToMol(msmi); TEST_ASSERT(mmol); MatchVectType mv; TEST_ASSERT(SubstructMatch(*mmol, *(qdefs["AcidChloride"]), mv)); delete mmol; } { std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/ChemTransforms/testData/query_file2.txt"; std::map qdefs; parseQueryDefFile(pathName, qdefs); TEST_ASSERT(qdefs.empty()); } /*{ std::string pathName = getenv("RDBASE"); pathName += "/Code/GraphMol/ChemTransforms/testData/query_file1.txt"; std::map qdefs; parseQueryDefFile(pathName, qdefs); TEST_ASSERT(!qdefs.empty()); TEST_ASSERT(qdefs.size()==7); TEST_ASSERT(qdefs.find("acidchloride")!=qdefs.end()); TEST_ASSERT(qdefs.find("AcidChloride")==qdefs.end()); }*/ BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testIssue275() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing sf.net issue 275: Murcko decomposition with chiral atoms" << std::endl; { std::string smi = "CCCCC[C@H]1CC[C@H](C(=O)O)CC1"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); ROMol *nMol = MurckoDecompose(*mol); smi = MolToSmiles(*nMol, true); TEST_ASSERT(smi == "C1CCCCC1"); delete mol; delete nMol; } { std::string smi = "CCCCC[C@H]1CC[C@H](C(=O)O)CC1"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); ROMol *nMol = MurckoDecompose(*mol); smi = MolToSmiles(*nMol, false); TEST_ASSERT(smi == "C1CCCCC1"); delete mol; delete nMol; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testFragmentOnBonds() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing fragmentOnBonds" << std::endl; { std::string smi = "OCCCN"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 5); unsigned int indices[] = {0, 3}; std::vector bindices( indices, indices + (sizeof(indices) / sizeof(indices[0]))); ROMol *nmol = MolFragmenter::fragmentOnBonds(*mol, bindices, false); TEST_ASSERT(nmol); TEST_ASSERT(nmol->getNumAtoms() == 5); delete mol; delete nmol; } { std::string smi = "OCCCN"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 5); unsigned int indices[] = {0, 1}; std::vector bindices( indices, indices + (sizeof(indices) / sizeof(indices[0]))); std::vector cutsPerAtom(mol->getNumAtoms()); ROMol *nmol = MolFragmenter::fragmentOnBonds(*mol, bindices, false, nullptr, nullptr, &cutsPerAtom); TEST_ASSERT(nmol); TEST_ASSERT(nmol->getNumAtoms() == 5); TEST_ASSERT(cutsPerAtom[0] == 1); TEST_ASSERT(cutsPerAtom[1] == 2); TEST_ASSERT(cutsPerAtom[2] == 1); TEST_ASSERT(cutsPerAtom[3] == 0); delete mol; delete nmol; } { std::string smi = "OCCCN"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 5); TEST_ASSERT(mol->getBondBetweenAtoms(0, 1)); TEST_ASSERT(mol->getBondBetweenAtoms(3, 4)); unsigned int indices[] = {0, 3}; std::vector bindices( indices, indices + (sizeof(indices) / sizeof(indices[0]))); ROMol *nmol = MolFragmenter::fragmentOnBonds(*mol, bindices); TEST_ASSERT(nmol); TEST_ASSERT(nmol->getNumAtoms() == 9); TEST_ASSERT(!nmol->getBondBetweenAtoms(0, 1)); TEST_ASSERT(!nmol->getBondBetweenAtoms(3, 4)); TEST_ASSERT(nmol->getAtomWithIdx(5)->getAtomicNum() == 0); TEST_ASSERT(nmol->getAtomWithIdx(5)->getIsotope() == 0); TEST_ASSERT(nmol->getBondBetweenAtoms(1, 5)); TEST_ASSERT(nmol->getAtomWithIdx(6)->getAtomicNum() == 0); TEST_ASSERT(nmol->getAtomWithIdx(6)->getIsotope() == 1); TEST_ASSERT(nmol->getBondBetweenAtoms(0, 6)); TEST_ASSERT(nmol->getAtomWithIdx(7)->getAtomicNum() == 0); TEST_ASSERT(nmol->getAtomWithIdx(7)->getIsotope() == 3); TEST_ASSERT(nmol->getBondBetweenAtoms(4, 7)); TEST_ASSERT(nmol->getAtomWithIdx(8)->getAtomicNum() == 0); TEST_ASSERT(nmol->getAtomWithIdx(8)->getIsotope() == 4); TEST_ASSERT(nmol->getBondBetweenAtoms(3, 8)); delete mol; delete nmol; } { std::string smi = "OCCCN"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 5); TEST_ASSERT(mol->getBondBetweenAtoms(0, 1)); TEST_ASSERT(mol->getBondBetweenAtoms(3, 4)); unsigned int indices[] = {0, 3}; std::vector bindices( indices, indices + (sizeof(indices) / sizeof(indices[0]))); std::vector> dummyLabels(2); dummyLabels[0] = std::make_pair(10, 11); dummyLabels[1] = std::make_pair(100, 110); ROMol *nmol = MolFragmenter::fragmentOnBonds(*mol, bindices, true, &dummyLabels); TEST_ASSERT(nmol); TEST_ASSERT(nmol->getNumAtoms() == 9); TEST_ASSERT(!nmol->getBondBetweenAtoms(0, 1)); TEST_ASSERT(!nmol->getBondBetweenAtoms(3, 4)); TEST_ASSERT(nmol->getAtomWithIdx(5)->getAtomicNum() == 0); TEST_ASSERT(nmol->getAtomWithIdx(5)->getIsotope() == 10); TEST_ASSERT(nmol->getBondBetweenAtoms(1, 5)); TEST_ASSERT(nmol->getAtomWithIdx(6)->getAtomicNum() == 0); TEST_ASSERT(nmol->getAtomWithIdx(6)->getIsotope() == 11); TEST_ASSERT(nmol->getBondBetweenAtoms(0, 6)); TEST_ASSERT(nmol->getAtomWithIdx(7)->getAtomicNum() == 0); TEST_ASSERT(nmol->getAtomWithIdx(7)->getIsotope() == 100); TEST_ASSERT(nmol->getBondBetweenAtoms(4, 7)); TEST_ASSERT(nmol->getAtomWithIdx(8)->getAtomicNum() == 0); TEST_ASSERT(nmol->getAtomWithIdx(8)->getIsotope() == 110); TEST_ASSERT(nmol->getBondBetweenAtoms(3, 8)); delete mol; delete nmol; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testFragmentOnBRICSBonds() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing fragmentOnBRICSBonds" << std::endl; { std::string smi = "c1ccccc1OC"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 8); TEST_ASSERT(mol->getBondBetweenAtoms(5, 6)); TEST_ASSERT(mol->getBondBetweenAtoms(6, 7)); std::vector fbts; MolFragmenter::constructBRICSBondTypes(fbts); ROMol *nmol = MolFragmenter::fragmentOnBonds(*mol, fbts); TEST_ASSERT(nmol); TEST_ASSERT(nmol->getNumAtoms() == 10); TEST_ASSERT(!nmol->getBondBetweenAtoms(5, 6)); TEST_ASSERT(nmol->getBondBetweenAtoms(6, 7)); smi = MolToSmiles(*nmol, true); TEST_ASSERT(smi == "[16*]c1ccccc1.[3*]OC"); TEST_ASSERT(nmol->getAtomWithIdx(8)->getAtomicNum() == 0); TEST_ASSERT(nmol->getAtomWithIdx(8)->getIsotope() == 3); TEST_ASSERT(nmol->getBondBetweenAtoms(6, 8)); TEST_ASSERT(nmol->getAtomWithIdx(9)->getAtomicNum() == 0); TEST_ASSERT(nmol->getAtomWithIdx(9)->getIsotope() == 16); TEST_ASSERT(nmol->getBondBetweenAtoms(5, 9)); delete mol; delete nmol; } { std::string smi = "c1ccccc1"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 6); std::vector fbts; MolFragmenter::constructBRICSBondTypes(fbts); ROMol *nmol = MolFragmenter::fragmentOnBonds(*mol, fbts); TEST_ASSERT(nmol); TEST_ASSERT(nmol->getNumAtoms() == 6); smi = MolToSmiles(*nmol, true); TEST_ASSERT(smi == "c1ccccc1"); delete mol; delete nmol; } { std::string smi = "OC(C)=CC"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 5); std::vector fbts; MolFragmenter::constructBRICSBondTypes(fbts); ROMol *nmol = MolFragmenter::fragmentOnBonds(*mol, fbts); TEST_ASSERT(nmol); TEST_ASSERT(nmol->getNumAtoms() == 7); smi = MolToSmiles(*nmol, true); TEST_ASSERT(smi == "[7*]=C(C)O.[7*]=CC"); delete mol; delete nmol; } { std::string smi = "c1ccccc1OC"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 8); ROMol *nmol = MolFragmenter::fragmentOnBRICSBonds(*mol); TEST_ASSERT(nmol); TEST_ASSERT(nmol->getNumAtoms() == 10); smi = MolToSmiles(*nmol, true); TEST_ASSERT(smi == "[16*]c1ccccc1.[3*]OC"); delete mol; delete nmol; } { std::string smi = "OC(C)=CC"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 5); ROMol *nmol = MolFragmenter::fragmentOnBRICSBonds(*mol); TEST_ASSERT(nmol); TEST_ASSERT(nmol->getNumAtoms() == 7); smi = MolToSmiles(*nmol, true); TEST_ASSERT(smi == "[7*]=C(C)O.[7*]=CC"); delete mol; delete nmol; } { std::string smi = "CCCOCCC(=O)c1ccccc1"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 14); ROMol *nmol = MolFragmenter::fragmentOnBRICSBonds(*mol); TEST_ASSERT(nmol); TEST_ASSERT(nmol->getNumAtoms() == 20); smi = MolToSmiles(*nmol, true); TEST_ASSERT(smi == "[16*]c1ccccc1.[3*]O[3*].[4*]CCC.[4*]CCC([6*])=O"); MolOps::sanitizeMol(static_cast(*nmol)); smi = MolToSmiles(*nmol, true); TEST_ASSERT(smi == "[16*]c1ccccc1.[3*]O[3*].[4*]CCC.[4*]CCC([6*])=O"); delete mol; delete nmol; } { std::string smi = "Cl.CC(=O)O[C@]1(c2ccccc2)CCN(C)[C@H]2CCCC[C@@H]21"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 22); ROMol *nmol = MolFragmenter::fragmentOnBRICSBonds(*mol); TEST_ASSERT(nmol); TEST_ASSERT(nmol->getNumAtoms() == 28); smi = MolToSmiles(*nmol, true); // std::cerr<(*nmol)); smi = MolToSmiles(*nmol, true); TEST_ASSERT(smi == "Cl.[1*]C(C)=O.[15*]C1([15*])CCN(C)[C@H]2CCCC[C@@H]21.[16*]" "c1ccccc1.[3*]O[3*]"); delete mol; delete nmol; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void benchFragmentOnBRICSBonds() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing fragmentOnBRICSBonds" << std::endl; { std::string pathName = getenv("RDBASE"); pathName += "/Regress/Data/mols.1000.sdf"; SDMolSupplier suppl(pathName); while (!suppl.atEnd()) { ROMol *m = suppl.next(); ROMol *nmol = MolFragmenter::fragmentOnBRICSBonds(*m); delete m; delete nmol; } } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testFragmentOnSomeBonds() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing fragmentOnSomeBonds" << std::endl; { std::string smi = "OCCCCN"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms()); unsigned int indices[] = {0, 2, 4}; std::vector bindices( indices, indices + (sizeof(indices) / sizeof(indices[0]))); std::vector frags; MolFragmenter::fragmentOnSomeBonds(*mol, bindices, frags, 2); TEST_ASSERT(frags.size() == 3); std::vector> fragMap; TEST_ASSERT(MolOps::getMolFrags(*frags[0], fragMap) == 3); TEST_ASSERT(fragMap.size() == 3); TEST_ASSERT(fragMap[0].size() == 2); TEST_ASSERT(fragMap[1].size() == 4); TEST_ASSERT(fragMap[2].size() == 4); TEST_ASSERT(MolOps::getMolFrags(*frags[1], fragMap) == 3); TEST_ASSERT(fragMap.size() == 3); TEST_ASSERT(fragMap[0].size() == 2); TEST_ASSERT(fragMap[1].size() == 6); TEST_ASSERT(fragMap[2].size() == 2); TEST_ASSERT(MolOps::getMolFrags(*frags[2], fragMap) == 3); TEST_ASSERT(fragMap.size() == 3); TEST_ASSERT(fragMap[0].size() == 4); TEST_ASSERT(fragMap[1].size() == 4); TEST_ASSERT(fragMap[2].size() == 2); delete mol; } { std::string smi = "OCCCCN"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms()); unsigned int indices[] = {0, 2, 4}; std::vector bindices( indices, indices + (sizeof(indices) / sizeof(indices[0]))); std::vector frags; std::vector> cpa; MolFragmenter::fragmentOnSomeBonds(*mol, bindices, frags, 2, false, nullptr, nullptr, &cpa); TEST_ASSERT(frags.size() == 3); TEST_ASSERT(cpa.size() == 3); TEST_ASSERT(cpa[0].size() == mol->getNumAtoms()); TEST_ASSERT(cpa[1].size() == mol->getNumAtoms()); TEST_ASSERT(cpa[2].size() == mol->getNumAtoms()); delete mol; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testGithubIssue429() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing github issue 429: fragmentOnSomeBonds() " "should update implicit H count on aromatic " "heteroatoms when addDummies is False" << std::endl; { std::string smi = "c1cccn1CC1CC1"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms()); unsigned int indices[] = {4, 5}; std::vector bindices( indices, indices + (sizeof(indices) / sizeof(indices[0]))); std::vector frags; MolFragmenter::fragmentOnSomeBonds(*mol, bindices, frags, 1, false); TEST_ASSERT(frags.size() == 2); std::vector> fragMap; for (auto romol : frags) { auto *rwmol = (RWMol *)(romol.get()); MolOps::sanitizeMol(*rwmol); } // we actually changed fragmentOnBonds(), check that too: auto *nmol = (RWMol *)MolFragmenter::fragmentOnBonds(*mol, bindices, false); MolOps::sanitizeMol(*nmol); delete nmol; delete mol; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testGithubIssue430() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing github issue 430: fragmentOnSomeBonds() " "crashes if bond list is empty" << std::endl; { std::string smi = "c1cccn1CC1CC1"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms()); std::vector bindices; std::vector frags; MolFragmenter::fragmentOnSomeBonds(*mol, bindices, frags, 1, false); TEST_ASSERT(frags.size() == 0); delete mol; } { std::string smi = "c1cccn1CC1CC1"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms()); unsigned int indices[] = {4, 5}; std::vector bindices( indices, indices + (sizeof(indices) / sizeof(indices[0]))); std::vector frags; MolFragmenter::fragmentOnSomeBonds(*mol, bindices, frags, 0, false); TEST_ASSERT(frags.size() == 0); delete mol; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testGithubIssue511() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing github issue 511: FragmentOnBonds() " "producing incorrect chirality" << std::endl; { // start with an example with no ring bonds... just to dodge that // complication std::string smi = "CO[C@](CC)(C)N"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 7) std::vector bindices; bindices.push_back(1); ROMol *frags = MolFragmenter::fragmentOnBonds(*mol, bindices, true); TEST_ASSERT(frags->getNumAtoms() == 9); std::string csmi1 = MolToSmiles(*mol, true); // std::cerr << csmi1 << std::endl; TEST_ASSERT(csmi1 == "CC[C@@](C)(N)OC"); std::string csmi2 = MolToSmiles(*frags, true); // std::cerr << csmi2 << std::endl; TEST_ASSERT(csmi2 == "[1*][C@](C)(N)CC.[2*]OC"); delete mol; delete frags; } { // Steve's original bug report std::string smi = "CO[C@]1(C)NCC1"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 7) std::vector bindices; bindices.push_back(1); ROMol *frags = MolFragmenter::fragmentOnBonds(*mol, bindices, true); TEST_ASSERT(frags->getNumAtoms() == 9); std::string csmi1 = MolToSmiles(*mol, true); TEST_ASSERT(csmi1 == "CO[C@@]1(C)CCN1"); std::string csmi2 = MolToSmiles(*frags, true); // std::cerr << csmi2 << std::endl; TEST_ASSERT(csmi2 == "[1*][C@@]1(C)CCN1.[2*]OC"); delete mol; delete frags; } { // further complicated by adding stereochem on both sides std::string smi = "C[C@](O)(F)[C@@]1(C)CCN1"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 9) std::vector bindices; bindices.push_back(3); ROMol *frags = MolFragmenter::fragmentOnBonds(*mol, bindices, true); TEST_ASSERT(frags->getNumAtoms() == 11); std::string csmi1 = MolToSmiles(*mol, true); // std::cerr << csmi1 << std::endl; TEST_ASSERT(csmi1 == "C[C@](O)(F)[C@@]1(C)CCN1"); std::string csmi2 = MolToSmiles(*frags, true); // std::cerr << csmi2 << std::endl; TEST_ASSERT(csmi2 == "[1*][C@@]1(C)CCN1.[4*][C@@](C)(O)F"); delete mol; delete frags; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testGithub1734() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing github issue 1734: BreakBRICSBonds() not " "preserving stereochemistry" << std::endl; { // no implicit H on chiral center std::string smi = "c1ccccc1[C@]([I])(C)NC"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 11) ROMol *nmol = MolFragmenter::fragmentOnBRICSBonds(*mol); TEST_ASSERT(nmol); TEST_ASSERT(nmol->getNumAtoms() == 15); smi = MolToSmiles(*nmol, true); TEST_ASSERT(smi == "[16*]c1ccccc1.[4*][C@]([8*])(C)I.[5*]NC"); delete mol; delete nmol; } { // The original example std::string smi = "c1ccccc1[C@H](C)NC"; RWMol *mol = SmilesToMol(smi); TEST_ASSERT(mol); TEST_ASSERT(mol->getNumAtoms() == 10) ROMol *nmol = MolFragmenter::fragmentOnBRICSBonds(*mol); TEST_ASSERT(nmol); TEST_ASSERT(nmol->getNumAtoms() == 14); smi = MolToSmiles(*nmol, true); TEST_ASSERT(smi == "[16*]c1ccccc1.[4*][C@H]([8*])C.[5*]NC"); delete mol; delete nmol; } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testGithub3206() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing GitHub #3206: Queries generated from " "PreprocessReaction cannot be translated to SMARTS" << std::endl; { auto mol1 = "CC"_smiles; std::map mp; mp["foo"] = ROMOL_SPTR(SmilesToMol("CO")); mp["bar"] = ROMOL_SPTR(SmilesToMol("CN")); mp["baz"] = ROMOL_SPTR(SmilesToMol("CF")); TEST_ASSERT(!mol1->getAtomWithIdx(0)->hasQuery()); addRecursiveQueries(*mol1, mp, "replaceme"); TEST_ASSERT(!mol1->getAtomWithIdx(0)->hasQuery()); mol1->getAtomWithIdx(0)->setProp("replaceme", "foo,bar,baz"); addRecursiveQueries(*mol1, mp, "replaceme"); TEST_ASSERT(mol1->getAtomWithIdx(0)->hasQuery()); TEST_ASSERT(mol1->getAtomWithIdx(0)->getQuery()->getDescription() == "AtomAnd"); TEST_ASSERT(!mol1->getAtomWithIdx(1)->hasQuery()); auto sma = MolToSmarts(*mol1); TEST_ASSERT(sma == "[#6;$([#6]-[#8]),$([#6]-[#7]),$([#6]-[#9])]-[#6]"); } BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } void testGithub4019() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing GitHub #4019: dummy atoms should not be marked " << "as aromatic, not have explicit Hs, not " << "be bonded through an aromatic bond and " << "not be bonded with each other" << std::endl; { auto mol = "c1ncccc1n1ccc2ccccc12"_smiles; ROMOL_SPTR core(SmartsToMol("n1ccc2ccccc12")); ROMOL_SPTR molNoSidechain(replaceSidechains(*mol, *core)); bool hasDummy = false; for (auto a : molNoSidechain->atoms()) { if (a->getAtomicNum() == 0) { hasDummy = true; TEST_ASSERT(!a->getIsAromatic()); a->setAtomicNum(1); a->setIsotope(0); } } TEST_ASSERT(hasDummy); molNoSidechain.reset(MolOps::removeHs(*molNoSidechain)); TEST_ASSERT(MolToSmiles(*molNoSidechain) == "c1ccc2[nH]ccc2c1"); } { auto mol = "c1ccc2[nH]ccc2c1"_smiles; ROMOL_SPTR core(SmartsToMol("c1ccccc1")); ROMOL_SPTR molNoSidechain(replaceSidechains(*mol, *core)); unsigned int nDummies = 0; for (auto a : molNoSidechain->atoms()) { if (a->getAtomicNum() == 0) { ++nDummies; TEST_ASSERT(!a->getIsAromatic()); TEST_ASSERT(!a->getNumExplicitHs()); } } TEST_ASSERT(nDummies == 2); for (auto b : molNoSidechain->bonds()) { if (b->getBeginAtom()->getAtomicNum() == 0 || b->getEndAtom()->getAtomicNum() == 0) { TEST_ASSERT(!b->getIsAromatic()); TEST_ASSERT(b->getBondType() == Bond::SINGLE); } } } { auto mol = "c1ccccc1C1CC1"_smiles; ROMOL_SPTR core(SmartsToMol("c1ccccc1C")); ROMOL_SPTR molNoSidechain(replaceSidechains(*mol, *core)); std::vector dummies; for (auto a : molNoSidechain->atoms()) { if (a->getAtomicNum() == 0) { dummies.push_back(a->getIdx()); TEST_ASSERT(!a->getIsAromatic()); TEST_ASSERT(!a->getNumExplicitHs()); } } TEST_ASSERT(dummies.size() == 2); TEST_ASSERT( !molNoSidechain->getBondBetweenAtoms(dummies.front(), dummies.back())); } } int main() { RDLog::InitLogs(); BOOST_LOG(rdInfoLog) << "********************************************************\n"; BOOST_LOG(rdInfoLog) << "Testing Chemical Transforms \n"; #if 1 testDeleteSubstruct(); testReplaceSubstructs(); testReplaceSubstructs2(); testReplaceSidechains(); testReplaceCore(); testReplaceCoreLabels(); testReplaceCoreCrash(); testReplaceCorePositions(); testReplaceCoreMatchVect(); testReplaceCoreMatchVectMultipleMappingToCore(); testMurckoDecomp(); testReplaceCoreRequireDummies(); testIssue3453144(); testIssue3537675(); testCombineMols(); testAddRecursiveQueries(); testParseQueryDefFile(); testIssue275(); testFragmentOnBonds(); testFragmentOnBRICSBonds(); testFragmentOnSomeBonds(); // benchFragmentOnBRICSBonds(); testGithubIssue429(); testGithubIssue430(); testGithubIssue511(); testReplaceCore2(); #endif testGithub1734(); testGithub3206(); testGithub4019(); BOOST_LOG(rdInfoLog) << "*******************************************************\n"; return (0); }