From ffabd04f22ab375273d06b183b0510816fd0f686 Mon Sep 17 00:00:00 2001 From: Mikhail Kviatkovskii Date: Fri, 8 Sep 2023 16:07:03 +0400 Subject: [PATCH] #1260: fix SMARTS saver for non-query entities with atom mappings (#1261) --- api/cpp/src/IndigoBaseReaction.cpp | 14 ++++++- api/cpp/src/IndigoBaseReaction.h | 10 +++++ api/cpp/src/IndigoChemicalStructure.cpp | 6 +++ api/cpp/src/IndigoChemicalStructure.h | 2 + api/cpp/src/IndigoQueryReaction.cpp | 29 +++++++++++++ api/cpp/src/IndigoQueryReaction.h | 35 ++++++++++++++++ api/cpp/src/IndigoSession.cpp | 6 +++ api/cpp/src/IndigoSession.h | 5 ++- api/cpp/tests/basic/reaction.cpp | 41 +++++++++++++++++++ .../indigo-core/molecule/src/smiles_saver.cpp | 10 +++++ 10 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 api/cpp/src/IndigoQueryReaction.cpp create mode 100644 api/cpp/src/IndigoQueryReaction.h create mode 100644 api/cpp/tests/basic/reaction.cpp diff --git a/api/cpp/src/IndigoBaseReaction.cpp b/api/cpp/src/IndigoBaseReaction.cpp index 485158ee93..de4acefb00 100644 --- a/api/cpp/src/IndigoBaseReaction.cpp +++ b/api/cpp/src/IndigoBaseReaction.cpp @@ -17,12 +17,18 @@ ***************************************************************************/ #include "IndigoBaseReaction.h" -#include "IndigoSession.h" + +#include #include using namespace indigo_cpp; +namespace +{ + const std::array AUTOMAP_MODES{"discard", "keep", "alter", "clear"}; +} + IndigoBaseReaction::IndigoBaseReaction(const int id, IndigoSessionPtr session) : IndigoChemicalStructure(id, std::move(session)) { } @@ -41,3 +47,9 @@ std::string IndigoBaseReaction::ctfile() const { return rxnfile(); } + +void IndigoBaseReaction::automap(const IndigoAutomapMode& mode) +{ + session()->setSessionId(); + session()->_checkResult(indigoAutomap(id(), AUTOMAP_MODES.at(static_cast(mode)))); +} diff --git a/api/cpp/src/IndigoBaseReaction.h b/api/cpp/src/IndigoBaseReaction.h index 511baaf09c..62e5522c0a 100644 --- a/api/cpp/src/IndigoBaseReaction.h +++ b/api/cpp/src/IndigoBaseReaction.h @@ -22,6 +22,14 @@ namespace indigo_cpp { + enum class IndigoAutomapMode + { + DISCARD = 0, + KEEP, + ALTER, + CLEAR + }; + class IndigoBaseReaction : public IndigoChemicalStructure { protected: @@ -35,5 +43,7 @@ namespace indigo_cpp public: std::string rxnfile() const; std::string ctfile() const override; + + void automap(const IndigoAutomapMode& mode); }; } diff --git a/api/cpp/src/IndigoChemicalStructure.cpp b/api/cpp/src/IndigoChemicalStructure.cpp index 986b15cd9e..86604c2114 100644 --- a/api/cpp/src/IndigoChemicalStructure.cpp +++ b/api/cpp/src/IndigoChemicalStructure.cpp @@ -61,6 +61,12 @@ std::string IndigoChemicalStructure::smiles() const return session()->_checkResultString(indigoSmiles(id())); } +std::string IndigoChemicalStructure::smarts() const +{ + session()->setSessionId(); + return session()->_checkResultString(indigoSmarts(id())); +} + std::string IndigoChemicalStructure::canonicalSmiles() const { session()->setSessionId(); diff --git a/api/cpp/src/IndigoChemicalStructure.h b/api/cpp/src/IndigoChemicalStructure.h index a90dc546bf..2878a4ae4c 100644 --- a/api/cpp/src/IndigoChemicalStructure.h +++ b/api/cpp/src/IndigoChemicalStructure.h @@ -40,6 +40,8 @@ namespace indigo_cpp std::string smiles() const; + std::string smarts() const; + std::string cml() const; std::string inchi() const; diff --git a/api/cpp/src/IndigoQueryReaction.cpp b/api/cpp/src/IndigoQueryReaction.cpp new file mode 100644 index 0000000000..412cdc8eeb --- /dev/null +++ b/api/cpp/src/IndigoQueryReaction.cpp @@ -0,0 +1,29 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +#include "IndigoQueryReaction.h" + +using namespace indigo_cpp; + +IndigoQueryReaction::IndigoQueryReaction(const int id, IndigoSessionPtr session) : IndigoBaseReaction(id, std::move(session)) +{ +} + +IndigoQueryReaction::IndigoQueryReaction(const IndigoQueryReaction& other) : IndigoBaseReaction(other) +{ +} diff --git a/api/cpp/src/IndigoQueryReaction.h b/api/cpp/src/IndigoQueryReaction.h new file mode 100644 index 0000000000..4be87c3bad --- /dev/null +++ b/api/cpp/src/IndigoQueryReaction.h @@ -0,0 +1,35 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +#pragma once + +#include "IndigoBaseReaction.h" + +namespace indigo_cpp +{ + class IndigoQueryReaction final : public IndigoBaseReaction + { + public: + IndigoQueryReaction(int id, IndigoSessionPtr session); + IndigoQueryReaction(IndigoQueryReaction&&) = default; + IndigoQueryReaction& operator=(IndigoQueryReaction&&) = default; + IndigoQueryReaction(const IndigoQueryReaction&); + IndigoQueryReaction& operator=(const IndigoQueryReaction&) = default; + ~IndigoQueryReaction() final = default; + }; +} diff --git a/api/cpp/src/IndigoSession.cpp b/api/cpp/src/IndigoSession.cpp index 46ab912f70..2ce45af95a 100644 --- a/api/cpp/src/IndigoSession.cpp +++ b/api/cpp/src/IndigoSession.cpp @@ -206,3 +206,9 @@ IndigoReaction IndigoSession::loadReaction(const std::string& data) setSessionId(); return {_checkResult(indigoLoadReactionFromString(data.c_str())), shared_from_this()}; } + +IndigoReaction IndigoSession::loadQueryReaction(const std::string& data) +{ + setSessionId(); + return {_checkResult(indigoLoadQueryReactionFromString(data.c_str())), shared_from_this()}; +} diff --git a/api/cpp/src/IndigoSession.h b/api/cpp/src/IndigoSession.h index 25b58a5bdc..40e9f27955 100644 --- a/api/cpp/src/IndigoSession.h +++ b/api/cpp/src/IndigoSession.h @@ -64,9 +64,12 @@ namespace indigo_cpp IndigoMolecule loadMolecule(const std::string& data); IndigoMolecule loadMoleculeFromFile(const std::string& path); + IndigoQueryMolecule loadQueryMolecule(const std::string& data); + IndigoReaction loadReaction(const std::string& data); - IndigoQueryMolecule loadQueryMolecule(const std::string& data); + IndigoReaction loadQueryReaction(const std::string& data); + IndigoWriteBuffer writeBuffer(); IndigoIterator iterateRDFile(const std::string& path); IndigoIterator iterateSDFile(const std::string& path); diff --git a/api/cpp/tests/basic/reaction.cpp b/api/cpp/tests/basic/reaction.cpp new file mode 100644 index 0000000000..d046228d65 --- /dev/null +++ b/api/cpp/tests/basic/reaction.cpp @@ -0,0 +1,41 @@ +/**************************************************************************** + * Copyright (C) from 2009 to Present EPAM Systems. + * + * This file is part of Indigo toolkit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +#include + +#include + +#include "common.h" + +using namespace indigo_cpp; + +TEST(Reaction, automapReactionSmarts) +{ + const auto& session = IndigoSession::create(); + auto reaction = session->loadReaction("[C:1]=[C:2][C:3].[C:4]=[C:5][C:6]>>[C:3][C:2]=[C:5][C:6]"); + reaction.automap(IndigoAutomapMode::CLEAR); + ASSERT_STREQ("[#6]=[#6]-[#6].[#6]=[#6]-[#6]>>[#6]-[#6]=[#6]-[#6] |^3:0,3,^1:1,4,7,8|", reaction.smarts().c_str()); +} + +TEST(Reaction, automapQueryReactionSmarts) +{ + const auto& session = IndigoSession::create(); + auto reaction = session->loadQueryReaction("[C:1]=[C:2][C:3].[C:4]=[C:5][C:6]>>[C:3][C:2]=[C:5][C:6] |$;;R1;;;R2;R1;;;R2$|"); + reaction.automap(IndigoAutomapMode::CLEAR); + ASSERT_STREQ("[#6]=[#6]-[#6].[#6]=[#6]-[#6]>>[#6]-[#6]=[#6]-[#6] |$;;R1;;;R2;R1;;;R2$|", reaction.smarts().c_str()); +} diff --git a/core/indigo-core/molecule/src/smiles_saver.cpp b/core/indigo-core/molecule/src/smiles_saver.cpp index a0c744da48..1bc2397e7f 100644 --- a/core/indigo-core/molecule/src/smiles_saver.cpp +++ b/core/indigo-core/molecule/src/smiles_saver.cpp @@ -878,6 +878,11 @@ void SmilesSaver::_writeSmartsAtom(int idx, QueryMolecule::Atom* atom, int chira case QueryMolecule::OP_AND: { for (i = 0; i < atom->children.size(); i++) { + if (atom->children[i]->type == QueryMolecule::ATOM_RADICAL || atom->children[i]->type == QueryMolecule::ATOM_VALENCE) + { + continue; + } + if (i > 0) _output.writeChar(has_or_parent ? '&' : ';'); _writeSmartsAtom(idx, (QueryMolecule::Atom*)atom->children[i], chirality, depth + 1, has_or_parent, has_not_parent); @@ -887,6 +892,11 @@ void SmilesSaver::_writeSmartsAtom(int idx, QueryMolecule::Atom* atom, int chira case QueryMolecule::OP_OR: { for (i = 0; i < atom->children.size(); i++) { + if (atom->children[i]->type == QueryMolecule::ATOM_RADICAL || atom->children[i]->type == QueryMolecule::ATOM_VALENCE) + { + continue; + } + if (i > 0) _output.printf(has_not_parent ? "!" : ","); _writeSmartsAtom(idx, (QueryMolecule::Atom*)atom->children[i], chirality, depth + 1, true, has_not_parent);