Skip to content

Commit

Permalink
Expose replaceAtomWithQueryAtom to Python (#7380)
Browse files Browse the repository at this point in the history
* Expose QueryOpts::replaceAtomWithQueryAtom.

* Indentation error.

* Response to review.

* Update docstrings in response to review.

---------

Co-authored-by: David Cosgrove <david@cozchemix.co.uk>
  • Loading branch information
DavidACosgrove and David Cosgrove committed May 7, 2024
1 parent 9386481 commit f48c874
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 14 deletions.
6 changes: 5 additions & 1 deletion Code/GraphMol/QueryOps.h
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,8 @@ RDKIT_GRAPHMOL_EXPORT ATOM_OR_QUERY *makeMHAtomQuery();
// CXSMILES
const std::vector<std::string> complexQueries = {"A", "AH", "Q", "QH",
"X", "XH", "M", "MH"};
RDKIT_GRAPHMOL_EXPORT void convertComplexNameToQuery(Atom *query, std::string_view symb);
RDKIT_GRAPHMOL_EXPORT void convertComplexNameToQuery(Atom *query,
std::string_view symb);

//! returns a Query for matching atoms that have ring bonds
template <class T>
Expand Down Expand Up @@ -1099,6 +1100,9 @@ inline bool isAtomDummy(const Atom *a) {
namespace QueryOps {
RDKIT_GRAPHMOL_EXPORT void completeMolQueries(
RWMol *mol, unsigned int magicVal = 0xDEADBEEF);
// Replaces the given atom in the molecule with a QueryAtom that is otherwise
// a copy of the given atom. Returns a pointer to that atom.
// if the atom already has a query, nothing will be changed
RDKIT_GRAPHMOL_EXPORT Atom *replaceAtomWithQueryAtom(RWMol *mol, Atom *atom);

RDKIT_GRAPHMOL_EXPORT void finalizeQueryFromDescription(
Expand Down
42 changes: 29 additions & 13 deletions Code/GraphMol/Wrap/Queries.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ Ret *PropQueryWithTol(const std::string &propname, const ExplicitBitVect &v,
return res;
}

namespace {
Atom *replaceAtomWithQueryAtomHelper(ROMol &mol, Atom &atom) {
return QueryOps::replaceAtomWithQueryAtom(static_cast<RWMol *>(&mol), &atom);
}
} // namespace

struct queries_wrapper {
static void wrap() {
#define QADEF1(_funcname_) \
Expand Down Expand Up @@ -210,29 +216,29 @@ struct queries_wrapper {

python::def("HasPropQueryAtom", HasPropQueryAtom,
(python::arg("propname"), python::arg("negate") = false),
"Returns a QueryAtom that matches when the propery 'propname' "
"Returns a QueryAtom that matches when the property 'propname' "
"exists in the atom.",
python::return_value_policy<python::manage_new_object>());

python::def("HasPropQueryBond", HasPropQueryBond,
(python::arg("propname"), python::arg("negate") = false),
"Returns a QueryBond that matches when the propery 'propname' "
"Returns a QueryBond that matches when the property 'propname' "
"exists in the bond.",
python::return_value_policy<python::manage_new_object>());

python::def("HasIntPropWithValueQueryAtom",
PropQueryWithTol<Atom, QueryAtom, int>,
(python::arg("propname"), python::arg("val"),
python::arg("negate") = false, python::arg("tolerance") = 0),
"Returns a QueryAtom that matches when the propery 'propname' "
"Returns a QueryAtom that matches when the property 'propname' "
"has the specified int value.",
python::return_value_policy<python::manage_new_object>());

python::def("HasBoolPropWithValueQueryAtom",
PropQuery<Atom, QueryAtom, bool>,
(python::arg("propname"), python::arg("val"),
python::arg("negate") = false),
"Returns a QueryAtom that matches when the propery 'propname' "
"Returns a QueryAtom that matches when the property 'propname' "
"has the specified boolean"
" value.",
python::return_value_policy<python::manage_new_object>());
Expand All @@ -241,7 +247,7 @@ struct queries_wrapper {
PropQuery<Atom, QueryAtom, std::string>,
(python::arg("propname"), python::arg("val"),
python::arg("negate") = false),
"Returns a QueryAtom that matches when the propery 'propname' "
"Returns a QueryAtom that matches when the property 'propname' "
"has the specified string "
"value.",
python::return_value_policy<python::manage_new_object>());
Expand All @@ -250,7 +256,7 @@ struct queries_wrapper {
PropQueryWithTol<Atom, QueryAtom, double>,
(python::arg("propname"), python::arg("val"),
python::arg("negate") = false, python::arg("tolerance") = 0.0),
"Returns a QueryAtom that matches when the propery 'propname' "
"Returns a QueryAtom that matches when the property 'propname' "
"has the specified "
"value +- tolerance",
python::return_value_policy<python::manage_new_object>());
Expand All @@ -259,7 +265,7 @@ struct queries_wrapper {
PropQueryWithTol<Atom, QueryAtom>,
(python::arg("propname"), python::arg("val"),
python::arg("negate") = false, python::arg("tolerance") = 0),
"Returns a QueryAtom that matches when the propery 'propname' "
"Returns a QueryAtom that matches when the property 'propname' "
"has the specified explicit bit vector"
" value. The Tolerance is the allowed Tanimoto difference",
python::return_value_policy<python::manage_new_object>());
Expand All @@ -268,29 +274,29 @@ struct queries_wrapper {
// Bond Queries
python::def("HasPropQueryBond", HasPropQueryBond,
(python::arg("propname"), python::arg("negate") = false),
"Returns a QueryBond that matches when the propery 'propname' "
"Returns a QueryBond that matches when the property 'propname' "
"exists in the bond.",
python::return_value_policy<python::manage_new_object>());

python::def("HasPropQueryBond", HasPropQueryBond,
(python::arg("propname"), python::arg("negate") = false),
"Returns a QueryBond that matches when the propery 'propname' "
"Returns a QueryBond that matches when the property 'propname' "
"exists in the bond.",
python::return_value_policy<python::manage_new_object>());

python::def("HasIntPropWithValueQueryBond",
PropQueryWithTol<Bond, QueryBond, int>,
(python::arg("propname"), python::arg("val"),
python::arg("negate") = false, python::arg("tolerance") = 0),
"Returns a QueryBond that matches when the propery 'propname' "
"Returns a QueryBond that matches when the property 'propname' "
"has the specified int value.",
python::return_value_policy<python::manage_new_object>());

python::def("HasBoolPropWithValueQueryBond",
PropQuery<Bond, QueryBond, bool>,
(python::arg("propname"), python::arg("val"),
python::arg("negate") = false),
"Returns a QueryBond that matches when the propery 'propname' "
"Returns a QueryBond that matches when the property 'propname' "
"has the specified boolean"
" value.",
python::return_value_policy<python::manage_new_object>());
Expand All @@ -299,7 +305,7 @@ struct queries_wrapper {
PropQuery<Bond, QueryBond, std::string>,
(python::arg("propname"), python::arg("val"),
python::arg("negate") = false),
"Returns a QueryBond that matches when the propery 'propname' "
"Returns a QueryBond that matches when the property 'propname' "
"has the specified string "
"value.",
python::return_value_policy<python::manage_new_object>());
Expand All @@ -308,10 +314,20 @@ struct queries_wrapper {
PropQueryWithTol<Bond, QueryBond, double>,
(python::arg("propname"), python::arg("val"),
python::arg("negate") = false, python::arg("tolerance") = 0.0),
"Returns a QueryBond that matches when the propery 'propname' "
"Returns a QueryBond that matches when the property 'propname' "
"has the specified "
"value +- tolerance",
python::return_value_policy<python::manage_new_object>());

std::string docString = R"DOC(Changes the given atom in the molecule to
a query atom and returns the atom which can then be modified, for example
with additional query constraints added. The new atom is otherwise a copy
of the old.
If the atom already has a query, nothing will be changed.)DOC";
python::def(
"ReplaceAtomWithQueryAtom", replaceAtomWithQueryAtomHelper,
(python::arg("mol"), python::arg("atom")), docString.c_str(),
python::return_value_policy<python::reference_existing_object>());
};
};
} // namespace RDKit
Expand Down
13 changes: 13 additions & 0 deletions Code/GraphMol/Wrap/rough_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3920,6 +3920,19 @@ def testAdjustQueryPropertiesgithubIssue1474(self):
self.assertEqual(ap.GetAtomWithIdx(0).GetPropsAsDict()["foo"], "bar")
self.assertEqual(ap.GetAtomWithIdx(1).GetPropsAsDict()["foo"], "bar")

def testReplaceAtomWithQueryAtom(self):
mol = Chem.MolFromSmiles("CC(C)C")
qmol = Chem.MolFromSmiles("C")
matches = mol.GetSubstructMatches(qmol)
self.assertEqual(((0,), (1,), (2,), (3,)), matches)

atom = qmol.GetAtomWithIdx(0)
natom = rdqueries.ReplaceAtomWithQueryAtom(qmol, atom)
qa = rdqueries.ExplicitDegreeEqualsQueryAtom(3)
natom.ExpandQuery(qa, Chem.CompositeQueryType.COMPOSITE_AND)
matches = mol.GetSubstructMatches(qmol)
self.assertEqual(((1,),), matches)

def testGithubIssue579(self):
fileN = os.path.join(RDConfig.RDBaseDir, 'Code', 'GraphMol', 'FileParsers', 'test_data',
'NCI_aids_few.sdf.gz')
Expand Down

0 comments on commit f48c874

Please sign in to comment.