diff --git a/Code/GraphMol/FMCS/FMCS.cpp b/Code/GraphMol/FMCS/FMCS.cpp index 2ea98e406..9b7654f71 100644 --- a/Code/GraphMol/FMCS/FMCS.cpp +++ b/Code/GraphMol/FMCS/FMCS.cpp @@ -57,6 +57,8 @@ void parseMCSParametersJSON(const char* json, MCSParameters* params) { p.AtomTyper = MCSAtomCompareElements; else if (0 == strcmp("Isotopes", s.c_str())) p.AtomTyper = MCSAtomCompareIsotopes; + else if (0 == strcmp("AnyHeavy", s.c_str())) + p.AtomTyper = MCSAtomCompareAnyHeavyAtom; s = pt.get("BondCompare", "def"); if (0 == strcmp("Any", s.c_str())) @@ -109,6 +111,9 @@ MCSResult findMCS(const std::vector& mols, bool maximizeBonds, case AtomCompareIsotopes: ps->AtomTyper = MCSAtomCompareIsotopes; break; + case AtomCompareAnyHeavyAtom: + ps->AtomTyper = MCSAtomCompareAnyHeavyAtom; + break; } ps->AtomCompareParameters.RingMatchesRingOnly = ringMatchesRingOnly; switch (bondComp) { @@ -221,6 +226,18 @@ bool MCSAtomCompareIsotopes(const MCSAtomCompareParameters& p, return true; } +bool MCSAtomCompareAnyHeavyAtom(const MCSAtomCompareParameters& p, + const ROMol& mol1, unsigned int atom1, + const ROMol& mol2, unsigned int atom2, void*) { + const Atom& a1 = *mol1.getAtomWithIdx(atom1); + const Atom& a2 = *mol2.getAtomWithIdx(atom2); + //Any atom, including H, matches another atom of the same type, according to the other flags + if (a1.getAtomicNum() == a2.getAtomicNum() || (a1.getAtomicNum() > 1 && a2.getAtomicNum() > 1)){ + return MCSAtomCompareAny(p,mol1,atom1,mol2,atom2,nullptr); + } + return false; +} + //=== BOND COMPARE ======================================================== class BondMatchOrderMatrix { diff --git a/Code/GraphMol/FMCS/FMCS.h b/Code/GraphMol/FMCS/FMCS.h index e57dace39..ae29b1c4d 100644 --- a/Code/GraphMol/FMCS/FMCS.h +++ b/Code/GraphMol/FMCS/FMCS.h @@ -49,6 +49,10 @@ RDKIT_FMCS_EXPORT bool MCSAtomCompareAny(const MCSAtomCompareParameters& p, const ROMol& mol1, unsigned int atom1, const ROMol& mol2, unsigned int atom2, void* userData); +RDKIT_FMCS_EXPORT bool MCSAtomCompareAnyHeavyAtom(const MCSAtomCompareParameters& p, + const ROMol& mol1, unsigned int atom1, + const ROMol& mol2, unsigned int atom2, + void* userData); RDKIT_FMCS_EXPORT bool MCSAtomCompareElements( const MCSAtomCompareParameters& p, const ROMol& mol1, unsigned int atom1, @@ -125,7 +129,8 @@ RDKIT_FMCS_EXPORT MCSResult findMCS_P(const std::vector& mols, typedef enum { AtomCompareAny, AtomCompareElements, - AtomCompareIsotopes + AtomCompareIsotopes, + AtomCompareAnyHeavyAtom } AtomComparator; typedef enum { BondCompareAny, diff --git a/Code/GraphMol/FMCS/Wrap/rdFMCS.cpp b/Code/GraphMol/FMCS/Wrap/rdFMCS.cpp index 3ee166dcf..321727e2e 100644 --- a/Code/GraphMol/FMCS/Wrap/rdFMCS.cpp +++ b/Code/GraphMol/FMCS/Wrap/rdFMCS.cpp @@ -27,6 +27,9 @@ void SetMCSAtomTyper(MCSParameters &p, AtomComparator atomComp) { case AtomCompareIsotopes: p.AtomTyper = MCSAtomCompareIsotopes; break; + case AtomCompareAnyHeavyAtom: + p.AtomTyper = MCSAtomCompareAnyHeavyAtom; + break; } } void SetMCSBondTyper(MCSParameters &p, BondComparator bondComp) { @@ -120,7 +123,8 @@ BOOST_PYTHON_MODULE(rdFMCS) { python::enum_("AtomCompare") .value("CompareAny", RDKit::AtomCompareAny) .value("CompareElements", RDKit::AtomCompareElements) - .value("CompareIsotopes", RDKit::AtomCompareIsotopes); + .value("CompareIsotopes", RDKit::AtomCompareIsotopes) + .value("CompareAnyHeavyAtom", RDKit::AtomCompareAnyHeavyAtom); python::enum_("BondCompare") .value("CompareAny", RDKit::BondCompareAny) .value("CompareOrder", RDKit::BondCompareOrder) diff --git a/Code/GraphMol/FMCS/Wrap/testFMCS.py b/Code/GraphMol/FMCS/Wrap/testFMCS.py index 64d6e5d71..5745d8c80 100644 --- a/Code/GraphMol/FMCS/Wrap/testFMCS.py +++ b/Code/GraphMol/FMCS/Wrap/testFMCS.py @@ -171,6 +171,30 @@ class TestCase(unittest.TestCase): for m in ms: self.assertTrue(m.HasSubstructMatch(qm)) + def testAtomCompareAnyHeavyAtom(self): + # H matches H, O matches C + smis = ('[H]c1ccccc1C', '[H]c1ccccc1O') + ms = [Chem.MolFromSmiles(x, sanitize=False) for x in smis] + mcs = rdFMCS.FindMCS(ms, atomCompare=rdFMCS.AtomCompare.CompareAnyHeavyAtom) + self.assertEqual(mcs.numBonds, 8) + self.assertEqual(mcs.numAtoms, 8) + qm = Chem.MolFromSmarts(mcs.smartsString) + + for m in ms: + self.assertTrue(m.HasSubstructMatch(qm)) + + def testAtomCompareAnyHeavyAtom1(self): + # O matches C, H does not match O + smis = ('[H]c1ccccc1C', 'Oc1ccccc1O') + ms = [Chem.MolFromSmiles(x, sanitize=False) for x in smis] + mcs = rdFMCS.FindMCS(ms, atomCompare=rdFMCS.AtomCompare.CompareAnyHeavyAtom) + self.assertEqual(mcs.numBonds, 7) + self.assertEqual(mcs.numAtoms, 7) + qm = Chem.MolFromSmarts(mcs.smartsString) + + for m in ms: + self.assertTrue(m.HasSubstructMatch(qm)) + def test6MatchValences(self): ms = (Chem.MolFromSmiles('NC1OC1'), Chem.MolFromSmiles('C1OC1[N+](=O)[O-]')) mcs = rdFMCS.FindMCS(ms) diff --git a/Code/GraphMol/FMCS/testFMCS_Unit.cpp b/Code/GraphMol/FMCS/testFMCS_Unit.cpp index b8089d205..98e03e862 100644 --- a/Code/GraphMol/FMCS/testFMCS_Unit.cpp +++ b/Code/GraphMol/FMCS/testFMCS_Unit.cpp @@ -585,6 +585,46 @@ void testAtomCompareAnyAtomBond() { BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; } +void testAtomCompareAnyHeavyAtom() { + BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; + BOOST_LOG(rdInfoLog) << "Testing FMCS testAtomCompareAnyAtom" << std::endl; + std::cout << "\ntestAtomCompareAnyAtom()\n"; + std::vector mols; + const char* smi[] = { + "[H]c1ccccc1C", "[H]c1ccccc1O", // H matches H, O matches C + }; + for (auto& i : smi) mols.push_back(ROMOL_SPTR(SmilesToMol(getSmilesOnly(i),0,false))); + MCSParameters p; + p.AtomTyper = MCSAtomCompareAnyHeavyAtom; + t0 = nanoClock(); + MCSResult res = findMCS(mols, &p); + std::cout << "MCS: " << res.SmartsString << " " << res.NumAtoms << " atoms, " + << res.NumBonds << " bonds\n"; + printTime(); + TEST_ASSERT(res.NumAtoms == 8 && res.NumBonds == 8); + BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; +} + +void testAtomCompareAnyHeavyAtom1() { + BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; + BOOST_LOG(rdInfoLog) << "Testing FMCS testAtomCompareAnyAtom" << std::endl; + std::cout << "\ntestAtomCompareAnyAtom()\n"; + std::vector mols; + const char* smi[] = { + "[H]c1ccccc1C", "Oc1ccccc1O", // O matches C, H does not match O + }; + for (auto& i : smi) mols.push_back(ROMOL_SPTR(SmilesToMol(getSmilesOnly(i),0,false))); + MCSParameters p; + p.AtomTyper = MCSAtomCompareAnyHeavyAtom; + t0 = nanoClock(); + MCSResult res = findMCS(mols, &p); + std::cout << "MCS: " << res.SmartsString << " " << res.NumAtoms << " atoms, " + << res.NumBonds << " bonds\n"; + printTime(); + TEST_ASSERT(res.NumAtoms == 7 && res.NumBonds == 7); + BOOST_LOG(rdInfoLog) << "\tdone" << std::endl; +} + void testSimple() { BOOST_LOG(rdInfoLog) << "-------------------------------------" << std::endl; BOOST_LOG(rdInfoLog) << "Testing FMCS testSimple" << std::endl; @@ -1376,6 +1416,8 @@ int main(int argc, const char* argv[]) { testAtomCompareIsotopes(); testAtomCompareAnyAtom(); testAtomCompareAnyAtomBond(); + testAtomCompareAnyHeavyAtom(); + testAtomCompareAnyHeavyAtom1(); test18(); test504(); diff --git a/Code/JavaWrappers/gmwrapper/src-test/org/RDKit/FMCSTests.java b/Code/JavaWrappers/gmwrapper/src-test/org/RDKit/FMCSTests.java index 1bbbca586..ebe8c0470 100644 --- a/Code/JavaWrappers/gmwrapper/src-test/org/RDKit/FMCSTests.java +++ b/Code/JavaWrappers/gmwrapper/src-test/org/RDKit/FMCSTests.java @@ -86,6 +86,37 @@ public class FMCSTests extends GraphMolTest { assertEquals(false,mcs.getCanceled()); } + + @Test + public void testAtomCompareAnyHeavyAtom() { + ROMol_Vect mols = new ROMol_Vect(); + mols.add(RWMol.MolFromSmiles("[H]c1ccccc1C",0, false)); + mols.add(RWMol.MolFromSmiles("[H]c1ccccc1O",0, false)); + // H matches H, O matches C + MCSResult mcs=RDKFuncs.findMCS(mols,true,1,60,false,false,false,false,false, + AtomComparator.AtomCompareAnyHeavyAtom, + BondComparator.BondCompareAny); + assertEquals(8,mcs.getNumAtoms()); + assertEquals(8,mcs.getNumBonds()); + //assertEquals("[#6]1:,-[#6]:,-[#6]:,-[#6]:,-[#6]:,-[#6]:,-1",mcs.getSmartsString()); + assertEquals(false,mcs.getCanceled()); +} + + @Test + public void testAtomCompareAnyHeavyAtom1() { + ROMol_Vect mols = new ROMol_Vect(); + mols.add(RWMol.MolFromSmiles("[H]c1ccccc1C",0, false)); + mols.add(RWMol.MolFromSmiles("Oc1ccccc1O",0, false)); + // O matches C, H does not match O + MCSResult mcs=RDKFuncs.findMCS(mols,true,1,60,false,false,false,false,false, + AtomComparator.AtomCompareAnyHeavyAtom, + BondComparator.BondCompareAny); + assertEquals(7,mcs.getNumAtoms()); + assertEquals(7,mcs.getNumBonds()); + //assertEquals("[#6]1:,-[#6]:,-[#6]:,-[#6]:,-[#6]:,-[#6]:,-1",mcs.getSmartsString()); + assertEquals(false,mcs.getCanceled()); + + } public static void main(String args[]) { org.junit.runner.JUnitCore.main("org.RDKit.FMCSTests");