From c89ae0f162e51d2a0622fb4f78ca652e0222d264 Mon Sep 17 00:00:00 2001 From: Aliaksandr Dziarkach <18146690+AliaksandrDziarkach@users.noreply.github.com> Date: Mon, 30 Oct 2023 17:13:38 +0300 Subject: [PATCH] #1372 Wildcard symbol ('any') with logical operators at custom query for bond is missing at SMARTS (#1386) --- .../integration/ref/formats/smarts.py.out | 4 +++ api/tests/integration/tests/formats/smarts.py | 4 +++ core/indigo-core/molecule/query_molecule.h | 3 ++ .../molecule/src/molecule_arom_match.cpp | 2 +- .../src/molecule_substructure_matcher.cpp | 2 ++ .../molecule/src/query_molecule.cpp | 32 +++++++++++++++++++ .../molecule/src/smiles_loader.cpp | 7 ++++ 7 files changed, 53 insertions(+), 1 deletion(-) diff --git a/api/tests/integration/ref/formats/smarts.py.out b/api/tests/integration/ref/formats/smarts.py.out index b9a44de9c8..345f825864 100644 --- a/api/tests/integration/ref/formats/smarts.py.out +++ b/api/tests/integration/ref/formats/smarts.py.out @@ -96,3 +96,7 @@ Smarts [C]!-[C] loaded as smarts - OK. Smarts [C]-,=[C] loaded as smarts - OK. Smarts [C]-;=[C] loaded as smarts - OK. Smarts [C]-&=[C] loaded as smarts - OK. +******* Any wildcard '~' in bond custom query ******* +[#7]~,-[#6] is ok. smarts_in==smarts_out +[#7]~,-[#6] is ok. json_in==json_out +[#7]~,-[#6] is ok. expected string found in json \ No newline at end of file diff --git a/api/tests/integration/tests/formats/smarts.py b/api/tests/integration/tests/formats/smarts.py index 6fe3361a59..458dbf70fd 100755 --- a/api/tests/integration/tests/formats/smarts.py +++ b/api/tests/integration/tests/formats/smarts.py @@ -244,3 +244,7 @@ def test_smarts_load_save_through_ket( print("Smarts %s loaded as smarts - OK." % smarts) else: print("Smarts %s loaded as %s - FAILED" % (smarts, format)) +print("******* Any wildcard '~' in bond custom query *******") +smarts = "[#7]~,-[#6]" +expected = '"bonds":[{"customQuery":"~,-","atoms":[0,1]}]}}' +test_smarts_load_save_through_ket(smarts, expected) diff --git a/core/indigo-core/molecule/query_molecule.h b/core/indigo-core/molecule/query_molecule.h index 1a61ca65d9..0d67255f6a 100644 --- a/core/indigo-core/molecule/query_molecule.h +++ b/core/indigo-core/molecule/query_molecule.h @@ -102,6 +102,7 @@ namespace indigo BOND_ORDER, BOND_TOPOLOGY, + BOND_ANY, HIGHLIGHTING }; @@ -147,6 +148,7 @@ namespace indigo // Check if there is no other constraint, except specified ones bool hasNoConstraintExcept(int what_type); bool hasNoConstraintExcept(int what_type1, int what_type2); + bool hasNoConstraintExcept(std::vector what_types); // Remove all constraints of the given type void removeConstraints(int what_type); @@ -248,6 +250,7 @@ namespace indigo { public: Bond(); + Bond(int type_); Bond(int type_, int value_); Bond(int type_, int value_, int direction_); ~Bond() override; diff --git a/core/indigo-core/molecule/src/molecule_arom_match.cpp b/core/indigo-core/molecule/src/molecule_arom_match.cpp index 0914145042..a323f78be0 100644 --- a/core/indigo-core/molecule/src/molecule_arom_match.cpp +++ b/core/indigo-core/molecule/src/molecule_arom_match.cpp @@ -297,7 +297,7 @@ bool AromaticityMatcher::match(int* core_sub, int* core_super) return false; } - bool has_other = !qbond.hasNoConstraintExcept(QueryMolecule::BOND_ORDER, QueryMolecule::BOND_TOPOLOGY); + bool has_other = !qbond.hasNoConstraintExcept({QueryMolecule::BOND_ORDER, QueryMolecule::BOND_TOPOLOGY, QueryMolecule::BOND_ANY}); if (has_other) throw Error("Only bond with order and topology constraints are supported"); diff --git a/core/indigo-core/molecule/src/molecule_substructure_matcher.cpp b/core/indigo-core/molecule/src/molecule_substructure_matcher.cpp index 22e9ed13c9..b4513768ce 100644 --- a/core/indigo-core/molecule/src/molecule_substructure_matcher.cpp +++ b/core/indigo-core/molecule/src/molecule_substructure_matcher.cpp @@ -541,6 +541,8 @@ bool MoleculeSubstructureMatcher::matchQueryBond(QueryMolecule::Bond* query, Bas case QueryMolecule::OP_NOT: return !matchQueryBond(query->child(0), target, sub_idx, super_idx, am, flags ^ MATCH_DISABLED_AS_TRUE); + case QueryMolecule::BOND_ANY: + return true; case QueryMolecule::BOND_ORDER: { if (flags & MATCH_BOND_TYPE) { diff --git a/core/indigo-core/molecule/src/query_molecule.cpp b/core/indigo-core/molecule/src/query_molecule.cpp index b33bc40efa..a7c3f622cc 100644 --- a/core/indigo-core/molecule/src/query_molecule.cpp +++ b/core/indigo-core/molecule/src/query_molecule.cpp @@ -399,6 +399,10 @@ void QueryMolecule::writeSmartsBond(Output& output, Bond* bond, bool has_or_pare output.writeString("!@"); break; } + case BOND_ANY: { + output.writeChar('~'); + break; + } default: throw Error("Unexpected bond type: %d", bond->type); } @@ -743,6 +747,9 @@ void QueryMolecule::_getBondDescription(Bond* bond, Output& out) case BOND_TOPOLOGY: out.printf("%s", bond->value == TOPOLOGY_RING ? "ring" : "chain"); return; + case BOND_ANY: + out.writeChar('~'); + return; default: out.printf("", bond->type); } @@ -931,6 +938,10 @@ QueryMolecule::Bond::Bond() : Node(OP_NONE), value(0), direction(0) { } +QueryMolecule::Bond::Bond(int type_) : Node(type_), value(0), direction(0) +{ +} + QueryMolecule::Bond::Bond(int type_, int value_) : Node(type_), value(value_), direction(0) { } @@ -1768,6 +1779,8 @@ bool QueryMolecule::Bond::_possibleValuePair(int what_type1, int what_value1, in return what_value1 == value; if (type == what_type2) return what_value2 == value; + if (type == BOND_ANY) + return true; return false; } @@ -1947,6 +1960,25 @@ bool QueryMolecule::Node::hasNoConstraintExcept(int what_type1, int what_type2) return type == what_type1 || type == what_type2; } +bool QueryMolecule::Node::hasNoConstraintExcept(std::vector what_types) +{ + if (type == OP_NONE) + return true; + + if (type == OP_AND || type == OP_OR || type == OP_NOT) + { + int i; + + for (i = 0; i < children.size(); i++) + if (!children[i]->hasNoConstraintExcept(what_types)) + return false; + + return true; + } + + return std::any_of(what_types.cbegin(), what_types.cend(), [this](int i) { return type == i; }); +} + void QueryMolecule::Node::removeConstraints(int what_type) { if (type == what_type) diff --git a/core/indigo-core/molecule/src/smiles_loader.cpp b/core/indigo-core/molecule/src/smiles_loader.cpp index c383f9ed85..4783750a01 100644 --- a/core/indigo-core/molecule/src/smiles_loader.cpp +++ b/core/indigo-core/molecule/src/smiles_loader.cpp @@ -2636,6 +2636,13 @@ void SmilesLoader::_readBondSub(Array& bond_str, _BondDesc& bond, std::uni else if (order == _ANY_BOND) { bond.type = order; + if (qbond.get() != 0) + { + if (subqbond.get() == 0) + subqbond = std::make_unique(QueryMolecule::BOND_ANY); + else + subqbond.reset(QueryMolecule::Bond::und(subqbond.release(), new QueryMolecule::Bond(QueryMolecule::BOND_ANY))); + } } if (topology > 0)