// // Copyright (C) 2003-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. // #include "QueryOps.h" #include #include #include #include #include #include namespace RDKit { // common general queries int queryIsAtomBridgehead(Atom const *at) { // at least three ring bonds, at least one ring bond in a ring which shares at // least two bonds with another ring involving this atom // // We can't just go with "at least three ring bonds shared between multiple // rings" because of structures like CC12CCN(CC1)C2 where there are only two // SSSRs PRECONDITION(at, "no atom"); if (at->getDegree() < 3) { return 0; } const auto &mol = at->getOwningMol(); const auto ri = mol.getRingInfo(); if (!ri || !ri->isInitialized()) { return 0; } // track which bonds involve this atom boost::dynamic_bitset<> atomRingBonds(mol.getNumBonds()); for (const auto &nbri : boost::make_iterator_range(mol.getAtomBonds(at))) { const auto &bnd = mol[nbri]; if (ri->numBondRings(bnd->getIdx())) { atomRingBonds.set(bnd->getIdx()); } } if (atomRingBonds.count() < 3) { return 0; } boost::dynamic_bitset<> bondsInRing(mol.getNumBonds()); for (unsigned int i = 0; i < ri->bondRings().size(); ++i) { bondsInRing.reset(); bool atomInRingI = false; for (const auto bidx : ri->bondRings()[i]) { bondsInRing.set(bidx); if (atomRingBonds[bidx]) { atomInRingI = true; } } if (!atomInRingI) { continue; } for (unsigned int j = i + 1; j < ri->bondRings().size(); ++j) { unsigned int overlap = 0; bool atomInRingJ = false; for (const auto bidx : ri->bondRings()[j]) { if (atomRingBonds[bidx]) { atomInRingJ = true; } if (bondsInRing[bidx]) { ++overlap; } if (overlap >= 2 && atomInRingJ) { // we have two rings containing the atom which share at least two // bonds: return 1; } } } } return 0; } //! returns a Query for matching atoms with a particular number of ring bonds ATOM_EQUALS_QUERY *makeAtomRingBondCountQuery(int what) { ATOM_EQUALS_QUERY *res = new AtomRingQuery(what); res->setDescription("AtomRingBondCount"); res->setDataFunc(queryAtomRingBondCount); return res; }; ATOM_EQUALS_QUERY *makeAtomInRingOfSizeQuery(int tgt) { RANGE_CHECK(3, tgt, 20); auto *res = new ATOM_EQUALS_QUERY; res->setVal(tgt); switch (tgt) { case 3: res->setDataFunc(queryAtomIsInRingOfSize<3>); break; case 4: res->setDataFunc(queryAtomIsInRingOfSize<4>); break; case 5: res->setDataFunc(queryAtomIsInRingOfSize<5>); break; case 6: res->setDataFunc(queryAtomIsInRingOfSize<6>); break; case 7: res->setDataFunc(queryAtomIsInRingOfSize<7>); break; case 8: res->setDataFunc(queryAtomIsInRingOfSize<8>); break; case 9: res->setDataFunc(queryAtomIsInRingOfSize<9>); break; case 10: res->setDataFunc(queryAtomIsInRingOfSize<10>); break; case 11: res->setDataFunc(queryAtomIsInRingOfSize<11>); break; case 12: res->setDataFunc(queryAtomIsInRingOfSize<12>); break; case 13: res->setDataFunc(queryAtomIsInRingOfSize<13>); break; case 14: res->setDataFunc(queryAtomIsInRingOfSize<14>); break; case 15: res->setDataFunc(queryAtomIsInRingOfSize<15>); break; case 16: res->setDataFunc(queryAtomIsInRingOfSize<16>); break; case 17: res->setDataFunc(queryAtomIsInRingOfSize<17>); break; case 18: res->setDataFunc(queryAtomIsInRingOfSize<18>); break; case 19: res->setDataFunc(queryAtomIsInRingOfSize<19>); break; case 20: res->setDataFunc(queryAtomIsInRingOfSize<20>); break; } res->setDescription("AtomRingSize"); return res; } BOND_EQUALS_QUERY *makeBondInRingOfSizeQuery(int tgt) { RANGE_CHECK(3, tgt, 20); auto *res = new BOND_EQUALS_QUERY; res->setVal(tgt); switch (tgt) { case 3: res->setDataFunc(queryBondIsInRingOfSize<3>); break; case 4: res->setDataFunc(queryBondIsInRingOfSize<4>); break; case 5: res->setDataFunc(queryBondIsInRingOfSize<5>); break; case 6: res->setDataFunc(queryBondIsInRingOfSize<6>); break; case 7: res->setDataFunc(queryBondIsInRingOfSize<7>); break; case 8: res->setDataFunc(queryBondIsInRingOfSize<8>); break; case 9: res->setDataFunc(queryBondIsInRingOfSize<9>); break; case 10: res->setDataFunc(queryBondIsInRingOfSize<10>); break; case 11: res->setDataFunc(queryBondIsInRingOfSize<11>); break; case 12: res->setDataFunc(queryBondIsInRingOfSize<12>); break; case 13: res->setDataFunc(queryBondIsInRingOfSize<13>); break; case 14: res->setDataFunc(queryBondIsInRingOfSize<14>); break; case 15: res->setDataFunc(queryBondIsInRingOfSize<15>); break; case 16: res->setDataFunc(queryBondIsInRingOfSize<16>); break; case 17: res->setDataFunc(queryBondIsInRingOfSize<17>); break; case 18: res->setDataFunc(queryBondIsInRingOfSize<18>); break; case 19: res->setDataFunc(queryBondIsInRingOfSize<19>); break; case 20: res->setDataFunc(queryBondIsInRingOfSize<20>); break; } res->setDescription("BondRingSize"); return res; } ATOM_EQUALS_QUERY *makeAtomMinRingSizeQuery(int tgt) { auto *res = new ATOM_EQUALS_QUERY; res->setVal(tgt); res->setDataFunc(queryAtomMinRingSize); res->setDescription("AtomMinRingSize"); return res; } BOND_EQUALS_QUERY *makeBondMinRingSizeQuery(int tgt) { auto *res = new BOND_EQUALS_QUERY; res->setVal(tgt); res->setDataFunc(queryBondMinRingSize); res->setDescription("BondMinRingSize"); return res; } unsigned int queryAtomBondProduct(Atom const *at) { ROMol::OEDGE_ITER beg, end; boost::tie(beg, end) = at->getOwningMol().getAtomBonds(at); unsigned int prod = 1; while (beg != end) { prod *= static_cast( firstThousandPrimes[at->getOwningMol()[*beg]->getBondType()]); ++beg; } return prod; } unsigned int queryAtomAllBondProduct(Atom const *at) { ROMol::OEDGE_ITER beg, end; boost::tie(beg, end) = at->getOwningMol().getAtomBonds(at); unsigned int prod = 1; while (beg != end) { prod *= static_cast( firstThousandPrimes[at->getOwningMol()[*beg]->getBondType()]); ++beg; } for (unsigned int i = 0; i < at->getTotalNumHs(); i++) { prod *= static_cast(firstThousandPrimes[Bond::SINGLE]); } return prod; } ATOM_EQUALS_QUERY *makeAtomImplicitValenceQuery(int what) { auto *res = makeAtomSimpleQuery(what, queryAtomImplicitValence); res->setDescription("AtomImplicitValence"); return res; } ATOM_EQUALS_QUERY *makeAtomExplicitValenceQuery(int what) { auto *res = makeAtomSimpleQuery(what, queryAtomExplicitValence); res->setDescription("AtomExplicitValence"); return res; } ATOM_EQUALS_QUERY *makeAtomTotalValenceQuery(int what) { auto *res = makeAtomSimpleQuery(what, queryAtomTotalValence); res->setDescription("AtomTotalValence"); return res; } ATOM_EQUALS_QUERY *makeAtomNumQuery(int what) { return makeAtomSimpleQuery(what, queryAtomNum, "AtomAtomicNum"); } ATOM_EQUALS_QUERY *makeAtomTypeQuery(int num, int aromatic) { return makeAtomSimpleQuery(makeAtomType(num, aromatic), queryAtomType, "AtomType"); } ATOM_EQUALS_QUERY *makeAtomExplicitDegreeQuery(int what) { auto *res = makeAtomSimpleQuery(what, queryAtomExplicitDegree); res->setDescription("AtomExplicitDegree"); return res; } ATOM_EQUALS_QUERY *makeAtomTotalDegreeQuery(int what) { auto *res = makeAtomSimpleQuery(what, queryAtomTotalDegree); res->setDescription("AtomTotalDegree"); return res; } ATOM_EQUALS_QUERY *makeAtomHeavyAtomDegreeQuery(int what) { auto *res = makeAtomSimpleQuery(what, queryAtomHeavyAtomDegree); res->setDescription("AtomHeavyAtomDegree"); return res; } ATOM_EQUALS_QUERY *makeAtomHCountQuery(int what) { auto *res = makeAtomSimpleQuery(what, queryAtomHCount); res->setDescription("AtomHCount"); return res; } ATOM_EQUALS_QUERY *makeAtomImplicitHCountQuery(int what) { auto *res = makeAtomSimpleQuery(what, queryAtomImplicitHCount); res->setDescription("AtomImplicitHCount"); return res; } ATOM_EQUALS_QUERY *makeAtomHasImplicitHQuery() { auto *res = makeAtomSimpleQuery(true, queryAtomHasImplicitH); res->setDescription("AtomHasImplicitH"); return res; } ATOM_EQUALS_QUERY *makeAtomAromaticQuery() { auto *res = makeAtomSimpleQuery(true, queryAtomAromatic); res->setDescription("AtomIsAromatic"); return res; } ATOM_EQUALS_QUERY *makeAtomAliphaticQuery() { auto *res = makeAtomSimpleQuery(true, queryAtomAliphatic); res->setDescription("AtomIsAliphatic"); return res; } ATOM_EQUALS_QUERY *makeAtomUnsaturatedQuery() { auto *res = makeAtomSimpleQuery(true, queryAtomUnsaturated); res->setDescription("AtomUnsaturated"); return res; } ATOM_EQUALS_QUERY *makeAtomMassQuery(int what) { auto *res = makeAtomSimpleQuery( massIntegerConversionFactor * what, queryAtomMass); res->setDescription("AtomMass"); return res; } ATOM_EQUALS_QUERY *makeAtomIsotopeQuery(int what) { auto *res = makeAtomSimpleQuery(what, queryAtomIsotope); res->setDescription("AtomIsotope"); return res; } ATOM_EQUALS_QUERY *makeAtomFormalChargeQuery(int what) { auto *res = makeAtomSimpleQuery(what, queryAtomFormalCharge); res->setDescription("AtomFormalCharge"); return res; } ATOM_EQUALS_QUERY *makeAtomNegativeFormalChargeQuery(int what) { auto *res = makeAtomSimpleQuery( what, queryAtomNegativeFormalCharge); res->setDescription("AtomNegativeFormalCharge"); return res; } ATOM_EQUALS_QUERY *makeAtomHybridizationQuery(int what) { auto *res = makeAtomSimpleQuery(what, queryAtomHybridization); res->setDescription("AtomHybridization"); return res; } ATOM_EQUALS_QUERY *makeAtomNumRadicalElectronsQuery(int what) { auto *res = makeAtomSimpleQuery( what, queryAtomNumRadicalElectrons); res->setDescription("AtomNumRadicalElectrons"); return res; } ATOM_EQUALS_QUERY *makeAtomHasChiralTagQuery() { auto *res = makeAtomSimpleQuery(true, queryAtomHasChiralTag); res->setDescription("AtomHasChiralTag"); return res; } ATOM_EQUALS_QUERY *makeAtomMissingChiralTagQuery() { auto *res = makeAtomSimpleQuery(true, queryAtomMissingChiralTag); res->setDescription("AtomMissingChiralTag"); return res; } ATOM_EQUALS_QUERY *makeAtomInRingQuery() { auto *res = makeAtomSimpleQuery(true, queryIsAtomInRing); res->setDescription("AtomInRing"); return res; } ATOM_EQUALS_QUERY *makeAtomIsBridgeheadQuery() { auto *res = makeAtomSimpleQuery(true, queryIsAtomBridgehead); res->setDescription("AtomIsBridgehead"); return res; } ATOM_OR_QUERY *makeQAtomQuery() { auto *res = new ATOM_OR_QUERY; res->setDescription("AtomOr"); res->setTypeLabel("Q"); res->setNegation(true); res->addChild( Queries::Query::CHILD_TYPE(makeAtomNumQuery(6))); res->addChild( Queries::Query::CHILD_TYPE(makeAtomNumQuery(1))); return res; } ATOM_EQUALS_QUERY *makeQHAtomQuery() { ATOM_EQUALS_QUERY *res = makeAtomNumQuery(6); res->setNegation(true); res->setTypeLabel("QH"); return res; } ATOM_EQUALS_QUERY *makeAAtomQuery() { ATOM_EQUALS_QUERY *res = makeAtomNumQuery(1); res->setNegation(true); res->setTypeLabel("A"); return res; } ATOM_NULL_QUERY *makeAHAtomQuery() { auto *res = makeAtomNullQuery(); res->setTypeLabel("AH"); return res; } ATOM_OR_QUERY *makeXAtomQuery() { auto *res = new ATOM_OR_QUERY; res->setDescription("AtomOr"); res->addChild( Queries::Query::CHILD_TYPE(makeAtomNumQuery(9))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(17))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(35))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(53))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(85))); res->setTypeLabel("X"); return res; } ATOM_OR_QUERY *makeXHAtomQuery() { ATOM_OR_QUERY *res = makeXAtomQuery(); res->addChild( Queries::Query::CHILD_TYPE(makeAtomNumQuery(1))); res->setTypeLabel("XH"); return res; } ATOM_OR_QUERY *makeMAtomQuery() { // using the definition from Marvin Sketch, which produces the following // SMARTS: // !#1!#2!#5!#6!#7!#8!#9!#10!#14!#15!#16!#17!#18!#33!#34!#35!#36!#52!#53!#54!#85!#86 // it's easier to define what isn't a metal than what is. :-) ATOM_OR_QUERY *res = makeMHAtomQuery(); res->addChild( Queries::Query::CHILD_TYPE(makeAtomNumQuery(1))); res->setTypeLabel("M"); return res; } ATOM_OR_QUERY *makeMHAtomQuery() { // using the definition from Marvin Sketch, which produces the following // SMARTS: // !#2!#5!#6!#7!#8!#9!#10!#14!#15!#16!#17!#18!#33!#34!#35!#36!#52!#53!#54!#85!#86 // it's easier to define what isn't a metal than what is. :-) auto *res = new ATOM_OR_QUERY; res->setDescription("AtomOr"); res->setNegation(true); res->addChild( Queries::Query::CHILD_TYPE(makeAtomNumQuery(2))); res->addChild( Queries::Query::CHILD_TYPE(makeAtomNumQuery(5))); res->addChild( Queries::Query::CHILD_TYPE(makeAtomNumQuery(6))); res->addChild( Queries::Query::CHILD_TYPE(makeAtomNumQuery(7))); res->addChild( Queries::Query::CHILD_TYPE(makeAtomNumQuery(8))); res->addChild( Queries::Query::CHILD_TYPE(makeAtomNumQuery(9))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(10))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(14))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(15))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(16))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(17))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(18))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(33))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(34))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(35))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(36))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(52))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(53))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(54))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(85))); res->addChild(Queries::Query::CHILD_TYPE( makeAtomNumQuery(86))); res->setTypeLabel("MH"); return res; } ATOM_EQUALS_QUERY *makeAtomInNRingsQuery(int what) { ATOM_EQUALS_QUERY *res; res = makeAtomSimpleQuery(what, queryIsAtomInNRings); res->setDescription("AtomInNRings"); return res; } ATOM_EQUALS_QUERY *makeAtomHasRingBondQuery() { auto *res = makeAtomSimpleQuery(true, queryAtomHasRingBond); res->setDescription("AtomHasRingBond"); return res; } ATOM_EQUALS_QUERY *makeAtomNumHeteroatomNbrsQuery(int what) { auto *res = makeAtomSimpleQuery(what, queryAtomNumHeteroatomNbrs); res->setDescription("AtomNumHeteroatomNeighbors"); return res; } ATOM_EQUALS_QUERY *makeAtomHasHeteroatomNbrsQuery() { auto *res = makeAtomSimpleQuery(true, queryAtomHasHeteroatomNbrs); res->setDescription("AtomHasHeteroatomNeighbors"); return res; } ATOM_EQUALS_QUERY *makeAtomNumAliphaticHeteroatomNbrsQuery(int what) { auto *res = makeAtomSimpleQuery( what, queryAtomNumAliphaticHeteroatomNbrs); res->setDescription("AtomNumAliphaticHeteroatomNeighbors"); return res; } ATOM_EQUALS_QUERY *makeAtomHasAliphaticHeteroatomNbrsQuery() { auto *res = makeAtomSimpleQuery( true, queryAtomHasAliphaticHeteroatomNbrs); res->setDescription("AtomHasAliphaticHeteroatomNeighbors"); return res; } ATOM_EQUALS_QUERY *makeAtomNonHydrogenDegreeQuery(int what) { auto *res = makeAtomSimpleQuery(what, queryAtomNonHydrogenDegree); res->setDescription("AtomNonHydrogenDegree"); return res; } BOND_EQUALS_QUERY *makeBondOrderEqualsQuery(Bond::BondType what) { auto *res = new BOND_EQUALS_QUERY; res->setVal(what); res->setDataFunc(queryBondOrder); res->setDescription("BondOrder"); res->setTypeLabel("BondOrder"); return res; } RDKIT_GRAPHMOL_EXPORT BOND_EQUALS_QUERY *makeSingleOrAromaticBondQuery() { auto *res = new BOND_EQUALS_QUERY; res->setVal(true); res->setDataFunc(queryBondIsSingleOrAromatic); res->setDescription("SingleOrAromaticBond"); res->setTypeLabel("BondOrder"); return res; }; RDKIT_GRAPHMOL_EXPORT BOND_EQUALS_QUERY *makeDoubleOrAromaticBondQuery() { auto *res = new BOND_EQUALS_QUERY; res->setVal(true); res->setDataFunc(queryBondIsDoubleOrAromatic); res->setDescription("DoubleOrAromaticBond"); res->setTypeLabel("BondOrder"); return res; }; RDKIT_GRAPHMOL_EXPORT BOND_EQUALS_QUERY *makeSingleOrDoubleBondQuery() { auto *res = new BOND_EQUALS_QUERY; res->setVal(true); res->setDataFunc(queryBondIsSingleOrDouble); res->setDescription("SingleOrDoubleBond"); res->setTypeLabel("BondOrder"); return res; }; RDKIT_GRAPHMOL_EXPORT BOND_EQUALS_QUERY * makeSingleOrDoubleOrAromaticBondQuery() { auto *res = new BOND_EQUALS_QUERY; res->setVal(true); res->setDataFunc(queryBondIsSingleOrDoubleOrAromatic); res->setDescription("SingleOrDoubleOrAromaticBond"); res->setTypeLabel("BondOrder"); return res; }; namespace QueryOps { // we don't use these anymore but we need to keep them around for backwards // compatibility with pickled queries. There's no reason to update this list. const std::vector bondOrderQueryFunctions{ std::string("BondOrder"), std::string("SingleOrAromaticBond"), std::string("DoubleOrAromaticBond"), std::string("SingleOrDoubleBond"), std::string("SingleOrDoubleOrAromaticBond")}; RDKIT_GRAPHMOL_EXPORT bool hasBondTypeQuery( const Queries::Query &qry) { const auto df = qry.getDescription(); const auto dt = qry.getTypeLabel(); // is this a bond order query? if (dt == "BondOrder" || (dt.empty() && std::find(bondOrderQueryFunctions.begin(), bondOrderQueryFunctions.end(), df) != bondOrderQueryFunctions.end())) { return true; } for (const auto &child : boost::make_iterator_range(qry.beginChildren(), qry.endChildren())) { if (hasBondTypeQuery(*child)) { return true; } } return false; } namespace { bool hasComplexBondTypeQueryHelper( const Queries::Query &qry, bool seenBondOrder) { const auto df = qry.getDescription(); bool isBondOrder = (df == "BondOrder"); // is this a bond order query? if (std::find(bondOrderQueryFunctions.begin(), bondOrderQueryFunctions.end(), df) != bondOrderQueryFunctions.end()) { if (seenBondOrder || !isBondOrder || qry.getNegation()) { return true; } } for (const auto &child : boost::make_iterator_range(qry.beginChildren(), qry.endChildren())) { if (hasComplexBondTypeQueryHelper(*child, seenBondOrder | isBondOrder)) { return true; } if (child->getDescription() == "BondOrder") { seenBondOrder = true; } } return false; } } // namespace RDKIT_GRAPHMOL_EXPORT bool hasComplexBondTypeQuery( const Queries::Query &qry) { return hasComplexBondTypeQueryHelper(qry, false); } } // namespace QueryOps BOND_EQUALS_QUERY *makeBondDirEqualsQuery(Bond::BondDir what) { auto *res = new BOND_EQUALS_QUERY; res->setVal(what); res->setDataFunc(queryBondDir); res->setDescription("BondDir"); return res; } BOND_EQUALS_QUERY *makeBondHasStereoQuery() { auto *res = new BOND_EQUALS_QUERY; res->setVal(true); res->setDataFunc(queryBondHasStereo); res->setDescription("BondStereo"); return res; } BOND_EQUALS_QUERY *makeBondIsInRingQuery() { auto *res = new BOND_EQUALS_QUERY; res->setVal(true); res->setDataFunc(queryIsBondInRing); res->setDescription("BondInRing"); return res; } BOND_EQUALS_QUERY *makeBondInNRingsQuery(int what) { auto *res = new BOND_EQUALS_QUERY; res->setVal(what); res->setDataFunc(queryIsBondInNRings); res->setDescription("BondInNRings"); return res; } BOND_NULL_QUERY *makeBondNullQuery() { auto *res = new BOND_NULL_QUERY; res->setDataFunc(nullDataFun); res->setMatchFunc(nullQueryFun); res->setDescription("BondNull"); return res; } ATOM_NULL_QUERY *makeAtomNullQuery() { auto *res = new ATOM_NULL_QUERY; res->setDataFunc(nullDataFun); res->setMatchFunc(nullQueryFun); res->setDescription("AtomNull"); return res; } bool isComplexQuery(const Bond *b) { PRECONDITION(b, "bad bond"); if (!b->hasQuery()) { return false; } // negated things are always complex: if (b->getQuery()->getNegation()) { return true; } std::string descr = b->getQuery()->getDescription(); if (descr == "BondOrder" || descr == "SingleOrAromaticBond") { return false; } if (descr == "BondAnd" || descr == "BondXor") { return true; } if (descr == "BondOr") { // detect the types of queries that appear for unspecified bonds in // SMARTS: if (b->getQuery()->endChildren() - b->getQuery()->beginChildren() == 2) { for (auto child = b->getQuery()->beginChildren(); child != b->getQuery()->endChildren(); ++child) { if ((*child)->getDescription() != "BondOrder" || (*child)->getNegation()) { return true; } if (static_cast(child->get())->getVal() != Bond::SINGLE && static_cast(child->get())->getVal() != Bond::AROMATIC) { return true; } } return false; } } return true; } namespace { bool _complexQueryHelper(Atom::QUERYATOM_QUERY const *query, bool &hasAtNum) { if (!query) { return false; } if (query->getNegation()) { return true; } std::string descr = query->getDescription(); // std::cerr<<" |"<beginChildren(); while (childIt != query->endChildren()) { if (_complexQueryHelper(childIt->get(), hasAtNum)) { return true; } ++childIt; } } return false; } template bool _atomListQueryHelper(const T query) { PRECONDITION(query, "no query"); if (query->getNegation()) { return false; } if (query->getDescription() == "AtomAtomicNum" || query->getDescription() == "AtomType") { return true; } if (query->getDescription() == "AtomOr") { for (const auto &child : boost::make_iterator_range(query->beginChildren(), query->endChildren())) { if (!_atomListQueryHelper(child)) { return false; } } return true; } return false; } } // namespace bool isAtomListQuery(const Atom *a) { PRECONDITION(a, "bad atom"); if (!a->hasQuery()) { return false; } if (a->getQuery()->getDescription() == "AtomOr") { for (const auto &child : boost::make_iterator_range( a->getQuery()->beginChildren(), a->getQuery()->endChildren())) { if (!_atomListQueryHelper(child)) { return false; } } return true; } return false; } void getAtomListQueryVals(const Atom::QUERYATOM_QUERY *q, std::vector &vals) { // list queries are series of nested ors of AtomAtomicNum queries PRECONDITION(q, "bad query"); auto descr = q->getDescription(); PRECONDITION(descr == "AtomOr", "bad query"); if (descr == "AtomOr") { for (const auto &child : boost::make_iterator_range(q->beginChildren(), q->endChildren())) { auto descr = child->getDescription(); if (child->getNegation() || (descr != "AtomOr" && descr != "AtomAtomicNum" && descr != "AtomType")) { throw ValueErrorException("bad query type1"); } // we don't allow negation of any children of the query: if (descr == "AtomOr") { getAtomListQueryVals(child.get(), vals); } else if (descr == "AtomAtomicNum") { vals.push_back(static_cast(child.get())->getVal()); } else if (descr == "AtomType") { auto v = static_cast(child.get())->getVal(); // aromatic AtomType queries add 1000 to the atomic number; // correct for that: if (v >= 1000) { v -= 1000; } vals.push_back(v); } } } } bool isComplexQuery(const Atom *a) { PRECONDITION(a, "bad atom"); if (!a->hasQuery()) { return false; } // std::cerr<<"\n"<getIdx(); // negated things are always complex: if (a->getQuery()->getNegation()) { return true; } std::string descr = a->getQuery()->getDescription(); // std::cerr<<" "<getQuery(), hasAtNum)) { return true; } return !hasAtNum; } return true; } bool isAtomAromatic(const Atom *a) { PRECONDITION(a, "bad atom"); bool res = false; if (!a->hasQuery()) { res = isAromaticAtom(*a); } else { std::string descr = a->getQuery()->getDescription(); if (descr == "AtomAtomicNum") { res = a->getIsAromatic(); } else if (descr == "AtomIsAromatic") { res = true; if (a->getQuery()->getNegation()) { res = !res; } } else if (descr == "AtomIsAliphatic") { res = false; if (a->getQuery()->getNegation()) { res = !res; } } else if (descr == "AtomType") { res = getAtomTypeIsAromatic( static_cast(a->getQuery())->getVal()); if (a->getQuery()->getNegation()) { res = !res; } } else if (descr == "AtomAnd") { auto childIt = a->getQuery()->beginChildren(); if ((*childIt)->getDescription() == "AtomAtomicNum") { if (a->getQuery()->getNegation()) { res = false; } else if ((*(childIt + 1))->getDescription() == "AtomIsAliphatic") { res = false; } else if ((*(childIt + 1))->getDescription() == "AtomIsAromatic") { res = true; } } } } return res; } namespace QueryOps { namespace { void completeQueryAndChildren(Atom::QUERYATOM_QUERY *query, Atom *tgt, unsigned int magicVal) { PRECONDITION(query, "no query"); PRECONDITION(tgt, "no atom"); auto eqQuery = dynamic_cast(query); if (eqQuery) { if (static_cast(eqQuery->getVal()) == magicVal) { int tgtVal = eqQuery->getDataFunc()(tgt); eqQuery->setVal(tgtVal); } } for (auto childIt = query->beginChildren(); childIt != query->endChildren(); ++childIt) { completeQueryAndChildren(childIt->get(), tgt, magicVal); } } } // namespace void completeMolQueries(RWMol *mol, unsigned int magicVal) { PRECONDITION(mol, "bad molecule"); for (auto atom : mol->atoms()) { if (atom->hasQuery()) { completeQueryAndChildren(atom->getQuery(), atom, magicVal); } } } Atom *replaceAtomWithQueryAtom(RWMol *mol, Atom *atom) { PRECONDITION(mol, "bad molecule"); PRECONDITION(atom, "bad atom"); if (atom->hasQuery()) { return atom; } QueryAtom qa(*atom); unsigned int idx = atom->getIdx(); if (atom->hasProp(common_properties::_hasMassQuery)) { qa.expandQuery(makeAtomMassQuery(static_cast(atom->getMass()))); } mol->replaceAtom(idx, &qa); return mol->getAtomWithIdx(idx); } void finalizeQueryFromDescription( Queries::Query *query, Atom const *) { std::string descr = query->getDescription(); if (boost::starts_with(descr, "range_")) { descr = descr.substr(6); } else if (boost::starts_with(descr, "less_")) { descr = descr.substr(5); } else if (boost::starts_with(descr, "greater_")) { descr = descr.substr(8); } Queries::Query *tmpQuery; if (descr == "AtomRingBondCount") { query->setDataFunc(queryAtomRingBondCount); } else if (descr == "AtomHasRingBond") { query->setDataFunc(queryAtomHasRingBond); } else if (descr == "AtomRingSize") { tmpQuery = makeAtomInRingOfSizeQuery( static_cast(query)->getVal()); query->setDataFunc(tmpQuery->getDataFunc()); delete tmpQuery; } else if (descr == "AtomMinRingSize") { query->setDataFunc(queryAtomMinRingSize); } else if (descr == "AtomImplicitValence") { query->setDataFunc(queryAtomImplicitValence); } else if (descr == "AtomTotalValence") { query->setDataFunc(queryAtomTotalValence); } else if (descr == "AtomAtomicNum") { query->setDataFunc(queryAtomNum); } else if (descr == "AtomExplicitDegree") { query->setDataFunc(queryAtomExplicitDegree); } else if (descr == "AtomTotalDegree") { query->setDataFunc(queryAtomTotalDegree); } else if (descr == "AtomHeavyAtomDegree") { query->setDataFunc(queryAtomHeavyAtomDegree); } else if (descr == "AtomHCount") { query->setDataFunc(queryAtomHCount); } else if (descr == "AtomImplicitHCount") { query->setDataFunc(queryAtomImplicitHCount); } else if (descr == "AtomHasImplicitH") { query->setDataFunc(queryAtomHasImplicitH); } else if (descr == "AtomIsAromatic") { query->setDataFunc(queryAtomAromatic); } else if (descr == "AtomIsAliphatic") { query->setDataFunc(queryAtomAliphatic); } else if (descr == "AtomUnsaturated") { query->setDataFunc(queryAtomUnsaturated); } else if (descr == "AtomMass") { query->setDataFunc(queryAtomMass); } else if (descr == "AtomIsotope") { query->setDataFunc(queryAtomIsotope); } else if (descr == "AtomFormalCharge") { query->setDataFunc(queryAtomFormalCharge); } else if (descr == "AtomNegativeFormalCharge") { query->setDataFunc(queryAtomNegativeFormalCharge); } else if (descr == "AtomHybridization") { query->setDataFunc(queryAtomHybridization); } else if (descr == "AtomInRing") { query->setDataFunc(queryIsAtomInRing); } else if (descr == "AtomInNRings") { query->setDataFunc(queryIsAtomInNRings); } else if (descr == "AtomHasHeteroatomNeighbors") { query->setDataFunc(queryAtomHasHeteroatomNbrs); } else if (descr == "AtomNumHeteroatomNeighbors") { query->setDataFunc(queryAtomNumHeteroatomNbrs); } else if (descr == "AtomNonHydrogenDegree") { query->setDataFunc(queryAtomNonHydrogenDegree); } else if (descr == "AtomHasAliphaticHeteroatomNeighbors") { query->setDataFunc(queryAtomHasAliphaticHeteroatomNbrs); } else if (descr == "AtomNumAliphaticHeteroatomNeighbors") { query->setDataFunc(queryAtomNumAliphaticHeteroatomNbrs); } else if (descr == "AtomNull") { query->setDataFunc(nullDataFun); query->setMatchFunc(nullQueryFun); } else if (descr == "AtomType") { query->setDataFunc(queryAtomType); } else if (descr == "AtomInNRings" || descr == "RecursiveStructure") { // don't need to do anything here because the classes // automatically have everything set } else if (descr == "AtomAnd" || descr == "AtomOr" || descr == "AtomXor") { // don't need to do anything here because the classes // automatically have everything set } else { throw ValueErrorException("Do not know how to finalize query: '" + descr + "'"); } } void finalizeQueryFromDescription( Queries::Query *query, Bond const *) { std::string descr = query->getDescription(); Queries::Query *tmpQuery; if (descr == "BondRingSize") { tmpQuery = makeBondInRingOfSizeQuery( static_cast(query)->getVal()); query->setDataFunc(tmpQuery->getDataFunc()); delete tmpQuery; } else if (descr == "BondMinRingSize") { query->setDataFunc(queryBondMinRingSize); } else if (descr == "BondOrder") { query->setDataFunc(queryBondOrder); } else if (descr == "BondDir") { query->setDataFunc(queryBondDir); } else if (descr == "BondInRing") { query->setDataFunc(queryIsBondInRing); } else if (descr == "BondInNRings") { query->setDataFunc(queryIsBondInNRings); } else if (descr == "SingleOrAromaticBond") { query->setDataFunc(queryBondIsSingleOrAromatic); } else if (descr == "SingleOrDoubleBond") { query->setDataFunc(queryBondIsSingleOrDouble); } else if (descr == "DoubleOrAromaticBond") { query->setDataFunc(queryBondIsDoubleOrAromatic); } else if (descr == "SingleOrDoubleOrAromaticBond") { query->setDataFunc(queryBondIsSingleOrDoubleOrAromatic); } else if (descr == "BondNull") { query->setDataFunc(nullDataFun); query->setMatchFunc(nullQueryFun); } else if (descr == "BondAnd" || descr == "BondOr" || descr == "BondXor") { // don't need to do anything here because the classes // automatically have everything set } else { throw ValueErrorException("Do not know how to finalize query: '" + descr + "'"); } } } // namespace QueryOps }; // namespace RDKit