Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions api/c/bingo-nosql/src/bingo_matcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,8 @@ bool MoleculeSubMatcher::_tryCurrent() // const

matcher.arom_options = indigo.arom_options;
matcher.setRulesList(&indigo.tautomer_rules);
matcher.setRules(_tautomer_params.conditions, _tautomer_params.force_hydrogens, _tautomer_params.ring_chain, _tautomer_params.method);
matcher.setRules(_tautomer_params.conditions, _tautomer_params.force_hydrogens, _tautomer_params.ring_chain, _tautomer_params.method,
_tautomer_params.strict);
matcher.setQuery(query_mol);
return matcher.find();
}
Expand Down Expand Up @@ -1411,7 +1412,8 @@ bool MolExactMatcher::_tryCurrent() /* const */

matcher.arom_options = indigo.arom_options;
matcher.setRulesList(&indigo.tautomer_rules);
matcher.setRules(_tautomer_params.conditions, _tautomer_params.force_hydrogens, _tautomer_params.ring_chain, _tautomer_params.method);
matcher.setRules(_tautomer_params.conditions, _tautomer_params.force_hydrogens, _tautomer_params.ring_chain, _tautomer_params.method,
_tautomer_params.strict);
matcher.setQuery(query_mol);
return matcher.find();
}
Expand Down
6 changes: 3 additions & 3 deletions api/c/indigo/src/indigo_match.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ DLLEXPORT bool _indigoParseTautomerFlags(const char* flags, IndigoTautomerParams
if (strcasecmp(word.ptr(), "TAU") != 0)
return false;

MoleculeTautomerMatcher::parseConditions(flags, params.conditions, params.force_hydrogens, params.ring_chain, params.method);
MoleculeTautomerMatcher::parseConditions(flags, params.conditions, params.force_hydrogens, params.ring_chain, params.method, params.strict);

return true;
}
Expand Down Expand Up @@ -266,7 +266,7 @@ CEXPORT int indigoExactMatch(int handler1, int handler2, const char* flags)

matcher.arom_options = self.arom_options;
matcher.setRulesList(&self.tautomer_rules);
matcher.setRules(params.conditions, params.force_hydrogens, params.ring_chain, params.method);
matcher.setRules(params.conditions, params.force_hydrogens, params.ring_chain, params.method, params.strict);
matcher.setQuery(mol1);

if (!matcher.find())
Expand Down Expand Up @@ -709,7 +709,7 @@ bool IndigoMoleculeSubstructureMatcher::findTautomerMatch(QueryMolecule& query,
}

tau_matcher->setRulesList(&tautomer_rules);
tau_matcher->setRules(tau_params.conditions, tau_params.force_hydrogens, tau_params.ring_chain, tau_params.method);
tau_matcher->setRules(tau_params.conditions, tau_params.force_hydrogens, tau_params.ring_chain, tau_params.method, tau_params.strict);
tau_matcher->setQuery(query);
tau_matcher->arom_options = indigo.arom_options;
if (!tau_matcher->find())
Expand Down
1 change: 1 addition & 0 deletions api/c/indigo/src/indigo_match.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ struct IndigoTautomerParams
int conditions;
bool force_hydrogens;
bool ring_chain;
bool strict;
TautomerMethod method;
};

Expand Down
147 changes: 147 additions & 0 deletions api/c/tests/unit/tests/tautomers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#include <gtest/gtest.h>

#include "common.h"
#include <indigo.h>
#include <indigo_internal.h>

using namespace indigo;

class IndigoApiTautomersTest : public IndigoApiTest
{
};

TEST_F(IndigoApiTautomersTest, testExactTau)
{
try
{
const char* flags = "TAU STRICT";
// 2-Penten-2-ol
int mol1 = indigoLoadMoleculeFromString("CC(O)=CCC");

// 2-Pentanone
int mol2 = indigoLoadMoleculeFromString("CC(CCC)=O");

int match = indigoExactMatch(mol1, mol2, flags);
ASSERT_NE(0, match) << "Expected match between 2-Penten-2-ol and 2-Pentanone";
if (match)
indigoFree(match);

// 3-Penten-2-ol
int mol3 = indigoLoadMoleculeFromString("CC(O)/C=C/C");
match = indigoExactMatch(mol1, mol3, flags);
ASSERT_EQ(0, match) << "Expected NO match between 2-Penten-2-ol and 3-Penten-2-ol with restricted rules";
int mol4 = indigoLoadMoleculeFromString("CC(/C=C(\\O)/C)O");
match = indigoExactMatch(mol1, mol4, flags);
ASSERT_EQ(0, match) << "Expected NO match between 2-Penten-2-ol and 3-Penten-2-ol with restricted rules";
if (match)
indigoFree(match);

indigoFree(mol1);
indigoFree(mol2);
indigoFree(mol3);
}
catch (Exception& e)
{
ASSERT_STREQ("", e.message());
}
}

TEST_F(IndigoApiTautomersTest, testSpecificTautomers)
{
const char* flags = "TAU STRICT";
int mol1 = indigoLoadMoleculeFromString("C(/C)(\\O)=C\\C(CC)O");
int mol2 = indigoLoadMoleculeFromString("C(C)(O)/C=C(/CC)\\O");

int match = indigoExactMatch(mol1, mol2, flags);
ASSERT_NE(0, match) << "Expected match between specified tautomers";

if (match)
indigoFree(match);

indigoFree(mol1);
indigoFree(mol2);
}

TEST_F(IndigoApiTautomersTest, testExpandedTautomerRules)
{
const char* flags = "TAU STRICT";

// 1. NOT MATCHED: Allylic Isomerization (Simple Alkene Shift)
// C=CCCC (1-Pentene) vs CC=CCC (2-Pentene)
// Shift C1-C2 to C2-C3. Proton shift involved. Neither C1 nor C3 activated.
int mol1 = indigoLoadMoleculeFromString("C=CCCC");
int mol2 = indigoLoadMoleculeFromString("CC=CCC");
int match = indigoExactMatch(mol1, mol2, flags);
ASSERT_EQ(0, match) << "Allylic Isomerization should NOT match";
if (match)
indigoFree(match);
indigoFree(mol1);
indigoFree(mol2);

// 2. NOT MATCHED: Allylic Alcohol Isomerization
// CC(O)=CCC (Enol) vs CC(O)C=CC (Allylic Alcohol)
mol1 = indigoLoadMoleculeFromString("CC(O)=CCC");
mol2 = indigoLoadMoleculeFromString("CC(O)C=CC");
match = indigoExactMatch(mol1, mol2, flags);
ASSERT_EQ(0, match) << "Allylic Alcohol Isomerization should NOT match";
if (match)
indigoFree(match);
indigoFree(mol1);
indigoFree(mol2);

// 3. MATCHED: Keto-Enol Tautomerism
// CC(=O)C (Acetone) vs CC(O)=C (Propen-2-ol)
mol1 = indigoLoadMoleculeFromString("CC(=O)C");
mol2 = indigoLoadMoleculeFromString("CC(O)=C");
match = indigoExactMatch(mol1, mol2, flags);
ASSERT_NE(0, match) << "Keto-Enol should MATCH";
if (match)
indigoFree(match);
indigoFree(mol1);
indigoFree(mol2);

// 4. MATCHED: Imine-Enamine Tautomerism
// CC=N (Imine) vs C=CN (Enamine)
mol1 = indigoLoadMoleculeFromString("CC=N");
mol2 = indigoLoadMoleculeFromString("C=CN");
match = indigoExactMatch(mol1, mol2, flags);
ASSERT_NE(0, match) << "Imine-Enamine should MATCH";
if (match)
indigoFree(match);
indigoFree(mol1);
indigoFree(mol2);

// 5. MATCHED: Enediol Tautomerism
// O-C=C(O) (Enediol) vs O=C-C(O) (Hydroxyketone)
mol1 = indigoLoadMoleculeFromString("OC=C(O)");
mol2 = indigoLoadMoleculeFromString("O=CC(O)");
match = indigoExactMatch(mol1, mol2, flags);
ASSERT_NE(0, match) << "Enediol should MATCH";
if (match)
indigoFree(match);
indigoFree(mol1);
indigoFree(mol2);

// 6. MATCHED: 1,3-Dicarbonyl Tautomerism (Activated Methylene)
// CC(=O)CC(=O)C (Pentane-2,4-dione) vs CC(=O)C=C(O)C (Enol form)
mol1 = indigoLoadMoleculeFromString("CC(=O)CC(=O)C");
mol2 = indigoLoadMoleculeFromString("CC(=O)C=C(O)C");
match = indigoExactMatch(mol1, mol2, flags);
ASSERT_NE(0, match) << "1,3-Dicarbonyl should MATCH";
if (match)
indigoFree(match);
indigoFree(mol1);
indigoFree(mol2);

// 7. MATCHED: Nitro-Aci-Nitro Tautomerism
// [O-][N+](=O)C (Nitromethane) vs [O-][N+](=C)O (Aci-nitro zwitterion)
// Ensures valence compatibility (N+ in both).
mol1 = indigoLoadMoleculeFromString("[O-][N+](=O)C");
mol2 = indigoLoadMoleculeFromString("[O-][N+](=C)O");
match = indigoExactMatch(mol1, mol2, flags);
ASSERT_NE(0, match) << "Nitro-Aci-Nitro should MATCH";
if (match)
indigoFree(match);
indigoFree(mol1);
indigoFree(mol2);
}
4 changes: 3 additions & 1 deletion core/indigo-core/molecule/molecule_tautomer.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ namespace indigo

bool force_hydrogens;
bool ring_chain;
bool strict;
int rules;
TautomerMethod method;
bool substructure;
Expand Down Expand Up @@ -123,11 +124,12 @@ namespace indigo
bool isFeasiblePair(int n1, int n2, int& h_diff);
void restore();

static bool matchAtomsTau(BaseMolecule& g1, BaseMolecule& g2, int n1, int n2);
static bool matchAtomsTau(BaseMolecule& g1, BaseMolecule& g2, int n1, int n2, bool strict);
static bool matchBondsTau(Graph& subgraph, Graph& supergraph, int sub_idx, int super_idx, void* userdata);
static bool matchBondsTauSub(Graph& subgraph, Graph& supergraph, int sub_idx, int super_idx, void* userdata);

static bool fixBondsNotInChains(TautomerSearchContext& context, const int* core1, const int* core2);
static bool isTautomerActivatedCarbon(BaseMolecule& mol, int atom_idx);

private:
bool _checkInterPathBonds();
Expand Down
5 changes: 3 additions & 2 deletions core/indigo-core/molecule/molecule_tautomer_matcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ namespace indigo
void setQuery(BaseMolecule& query);

void setRulesList(const PtrArray<TautomerRule>* rules_list);
void setRules(int rules_set, bool force_hydrogens, bool ring_chain, TautomerMethod method);
void setRules(int rules_set, bool force_hydrogens, bool ring_chain, TautomerMethod method, bool strict);

bool find();

const int* getQueryMapping();

static void parseConditions(const char* tautomer_text, int& rules, bool& force_hydrogens, bool& ring_chain, TautomerMethod& method);
static void parseConditions(const char* tautomer_text, int& rules, bool& force_hydrogens, bool& ring_chain, TautomerMethod& method, bool& strict);

static int countNonHydrogens(BaseMolecule& molecule);

Expand All @@ -66,6 +66,7 @@ namespace indigo
bool _substructure;
bool _force_hydrogens;
bool _ring_chain;
bool _strict;
TautomerMethod _method;
int _rules;

Expand Down
10 changes: 5 additions & 5 deletions core/indigo-core/molecule/src/molecule_tautomer_chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ bool TautomerChainFinder::nextPair(int& n1, int& n2, int& e1, int& e2, int prev_
if (_context.core_2[n2] != EmbeddingEnumerator::UNMAPPED)
continue;

if (TautomerMatcher::matchAtomsTau(_context.g1, _context.g2, n1, n2))
if (TautomerMatcher::matchAtomsTau(_context.g1, _context.g2, n1, n2, _context.strict))
return true;
}

Expand Down Expand Up @@ -144,7 +144,7 @@ bool TautomerChainFinder::nextPair(int& n1, int& n2, int& e1, int& e2, int prev_
if (_context.core_2[n2] != EmbeddingEnumerator::UNMAPPED)
continue;

if (TautomerMatcher::matchAtomsTau(_context.g1, _context.g2, n1, n2))
if (TautomerMatcher::matchAtomsTau(_context.g1, _context.g2, n1, n2, _context.strict))
return true;
}

Expand Down Expand Up @@ -175,7 +175,7 @@ bool TautomerChainFinder::nextPair(int& n1, int& n2, int& e1, int& e2, int prev_
if (_context.core_2[n2] != EmbeddingEnumerator::UNMAPPED)
continue;

if (TautomerMatcher::matchAtomsTau(_context.g1, _context.g2, n1, n2))
if (TautomerMatcher::matchAtomsTau(_context.g1, _context.g2, n1, n2, _context.strict))
return true;
}

Expand Down Expand Up @@ -490,7 +490,7 @@ bool TautomerChainChecker::isFeasibleStartingPair(int n1, int n2, int& h_diff)
return true;
}

if (!TautomerMatcher::matchAtomsTau(_context.g1, _context.g2, n1, n2))
if (!TautomerMatcher::matchAtomsTau(_context.g1, _context.g2, n1, n2, _context.strict))
return false;

int h_count_2 = _context.g2.getAtomTotalH(n2);
Expand Down Expand Up @@ -751,7 +751,7 @@ int TautomerChainChecker::isFeasiblePair(int n1, int n2, TautomerChainChecker& n

if (n1 != -1)
{
if (!TautomerMatcher::matchAtomsTau(_context.g1, _context.g2, n1, n2))
if (!TautomerMatcher::matchAtomsTau(_context.g1, _context.g2, n1, n2, _context.strict))
return 0;

int h_rep_1 = 0;
Expand Down
Loading
Loading