Skip to content

Commit

Permalink
#1319 Directional bonds not work with SMARTS
Browse files Browse the repository at this point in the history
Fix errors
Add UT
  • Loading branch information
AliaksandrDziarkach committed Oct 13, 2023
1 parent 5dddcc9 commit 2b6bde2
Show file tree
Hide file tree
Showing 10 changed files with 120 additions and 131 deletions.
4 changes: 4 additions & 0 deletions api/tests/integration/ref/formats/smarts.py.out
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ CC[C+5]CCCCC
[O;H] is ok. smarts_in==smarts_out
[!O;H] is ok. smarts_in==smarts_out
([#6]1-[#6]-[#6]-1.[#6]) is ok. smarts_in==smarts_out
[#9]/[#6]=[#6]/[#6]=[#6]/[#6] is ok. smarts_in==smarts_out
[#9]/[#6]=[#6]/[#6]=[#6]/[#6] is ok. smarts_in==smarts_out
[#9]/[#6]=[#6]/[#6]=[#6]/[#6] is ok. json_in==json_out
[#9]/[#6]=[#6]/[#6]=[#6]/[#6] is ok. expected string found in json
31 changes: 31 additions & 0 deletions api/tests/integration/tests/formats/smarts.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,32 @@ def test_smarts_load_save(smarts_in):
print("smarts_out=%s" % smarts_out)


def test_smarts_load_save_through_ket(smarts_in, expected_str):
m1 = indigo.loadSmarts(smarts_in)
json1 = m1.json()
m2 = indigo.loadQueryMolecule(json1)
smarts_out = m2.smarts()
m3 = indigo.loadSmarts(smarts_out)
json2 = m3.json()
if smarts_in == smarts_out:
print("%s is ok. smarts_in==smarts_out" % smarts_in)
else:
print("smarts_in!=smarts_out")
print(" smarts_in=%s" % smarts_in)
print("smarts_out=%s" % smarts_out)
if json1 == json2:
print("%s is ok. json_in==json_out" % smarts_in)
else:
print("json_in!=json_out")
print(" json_in=%s" % json1)
print("json_out=%s" % json2)
if expected_str in json1:
print("%s is ok. expected string found in json" % smarts_in)
else:
print("%s: expected string not found in json" % smarts_in)
print(json1)


molstr = """
Ketcher 11241617102D 1 1.00000 0.00000 0
Expand Down Expand Up @@ -116,3 +142,8 @@ def test_smarts_load_save(smarts_in):
test_smarts_load_save("[O;H]")
test_smarts_load_save("[!O;H]")
test_smarts_load_save("([#6]1-[#6]-[#6]-1.[#6])")
test_smarts_load_save("[#9]/[#6]=[#6]/[#6]=[#6]/[#6]")
expected_str = '"bonds":[{"type":1,"atoms":[0,1],"stereo":1},{"type":2,"atoms":[1,2]},{"type":1,"atoms":[2,3],"stereo":1},{"type":2,"atoms":[3,4]},{"type":1,"atoms":[4,5],"stereo":1}]}}'
test_smarts_load_save_through_ket(
"[#9]/[#6]=[#6]/[#6]=[#6]/[#6]", expected_str
)
9 changes: 1 addition & 8 deletions core/indigo-core/molecule/query_molecule.h
Original file line number Diff line number Diff line change
Expand Up @@ -329,13 +329,6 @@ namespace indigo
QUERY_ATOM_LIST,
QUERY_ATOM_NOTLIST
};
enum QUERY_BOND
{
QUERY_BOND_DOUBLE_OR_AROMATIC = 0,
QUERY_BOND_SINGLE_OR_AROMATIC,
QUERY_BOND_SINGLE_OR_DOUBLE,
QUERY_BOND_ANY
};

static bool isKnownAttr(QueryMolecule::Atom& qa);
static bool isNotAtom(QueryMolecule::Atom& qa, int elem);
Expand All @@ -349,7 +342,7 @@ namespace indigo
static bool isOrBond(Bond& qb, int type1, int type2);
static bool isSingleOrDouble(Bond& qb);
static int getQueryBondType(Bond& qb);
static int getQueryBondType(Bond& qb, int& direction);
static int getQueryBondType(Bond& qb, int& direction, bool& negative);
static int getAtomType(const char* label);
static void getQueryAtomLabel(int qa, Array<char>& result);
static QueryMolecule::Bond* createQueryMoleculeBond(int order, int topology, int direction);
Expand Down
8 changes: 4 additions & 4 deletions core/indigo-core/molecule/src/cml_saver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -473,13 +473,13 @@ void CmlSaver::_addMoleculeElement(XMLElement* elem, BaseMolecule& mol, bool que

int qb = QueryMolecule::getQueryBondType(qmol->getBond(i));

if (qb == QueryMolecule::QUERY_BOND_SINGLE_OR_DOUBLE)
if (qb == _BOND_SINGLE_OR_DOUBLE)
bond->SetAttribute("queryType", "SD");
else if (qb == QueryMolecule::QUERY_BOND_SINGLE_OR_AROMATIC)
else if (qb == _BOND_SINGLE_OR_AROMATIC)
bond->SetAttribute("queryType", "SA");
else if (qb == QueryMolecule::QUERY_BOND_DOUBLE_OR_AROMATIC)
else if (qb == _BOND_DOUBLE_OR_AROMATIC)
bond->SetAttribute("queryType", "DA");
else if (qb == QueryMolecule::QUERY_BOND_ANY)
else if (qb == _BOND_ANY)
bond->SetAttribute("queryType", "Any");

int indigo_topology = -1;
Expand Down
6 changes: 2 additions & 4 deletions core/indigo-core/molecule/src/molecule_json_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ void MoleculeJsonLoader::parseBonds(const rapidjson::Value& bonds, BaseMolecule&
if (stereo == 1)
direction = BOND_UP;
else if (stereo == 6)
direction == BOND_DOWN;
direction = BOND_DOWN;
}
bond_idx = _pmol ? _pmol->addBond_Silent(a1, a2, order) : addBondToMoleculeQuery(a1, a2, order, topology, direction);
if (stereo)
Expand All @@ -655,8 +655,6 @@ void MoleculeJsonLoader::parseBonds(const rapidjson::Value& bonds, BaseMolecule&
case 6:
mol.setBondDirection(bond_idx, BOND_DOWN);
break;
break;

default:
break;
}
Expand Down Expand Up @@ -1197,7 +1195,7 @@ void MoleculeJsonLoader::loadMolecule(BaseMolecule& mol, bool load_arrows)
{
if (mol.getBondDirection(i) > 0 && !sensible_bond_directions[i])
{
if (!stereochemistry_options.ignore_errors)
if (!stereochemistry_options.ignore_errors && !_pqmol)
throw Error("direction of bond #%d makes no sense", i);
}
}
Expand Down
48 changes: 21 additions & 27 deletions core/indigo-core/molecule/src/molecule_json_saver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,8 @@ void MoleculeJsonSaver::saveBonds(BaseMolecule& mol, JsonWriter& writer)
{
for (auto i : mol.edges())
{
int direction = 0;
bool negative = false;
writer.StartObject();
writer.Key("type");
if (_pmol)
Expand All @@ -431,25 +433,27 @@ void MoleculeJsonSaver::saveBonds(BaseMolecule& mol, JsonWriter& writer)
else if (_pqmol)
{
QueryMolecule::Bond& qbond = _pqmol->getBond(i);
int direction;
int bond_order = 0;
int qb = QueryMolecule::getQueryBondType(qbond, direction);
if (qb == QueryMolecule::QUERY_BOND_SINGLE_OR_DOUBLE)
bond_order = 5;
else if (qb == QueryMolecule::QUERY_BOND_SINGLE_OR_AROMATIC)
bond_order = 6;
else if (qb == QueryMolecule::QUERY_BOND_DOUBLE_OR_AROMATIC)
bond_order = 7;
else if (qb == QueryMolecule::QUERY_BOND_ANY)
bond_order = 8;
if (bond_order < 0)
int bond_order = QueryMolecule::getQueryBondType(qbond, direction, negative);
if (bond_order < 0 || negative)
{
// throw Error("Invalid query bond");

writer.Uint(BOND_SINGLE);
std::string customQuery = QueryMolecule::getSmartsBondStr(&qbond);
writer.Key("customQuery");
writer.String(customQuery.c_str());
}
else
{
writer.Uint(bond_order);
switch (direction)
{
case BOND_UP:
direction = 1;
break;
case BOND_DOWN:
direction = 6;
break;
}
}
}

int topology = -1;
Expand Down Expand Up @@ -481,17 +485,7 @@ void MoleculeJsonSaver::saveBonds(BaseMolecule& mol, JsonWriter& writer)
writer.EndArray();

int stereo = mol.getBondDirection(i);
bool stored_stereo = false;
if (_pqmol)
{
int direction = 0;
if (_pqmol->getBond(i).sureValue(QueryMolecule::BOND_DIRECTION, direction))
{
stereo = direction;
stored_stereo = true;
}
}
if (mol.cis_trans.isIgnored(i) && !stored_stereo)
if (mol.cis_trans.isIgnored(i))
stereo = 3;
else
switch (stereo)
Expand All @@ -511,10 +505,10 @@ void MoleculeJsonSaver::saveBonds(BaseMolecule& mol, JsonWriter& writer)
break;
}

if (stereo)
if (stereo || direction)
{
writer.Key("stereo");
writer.Uint(stereo);
writer.Uint(direction ? direction : stereo); // If have stored direction - override calculated
}

auto cip = mol.getBondCIP(i);
Expand Down
59 changes: 8 additions & 51 deletions core/indigo-core/molecule/src/molfile_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -586,33 +586,12 @@ void MolfileLoader::_readCtab2000()
}
else
{
std::unique_ptr<QueryMolecule::Bond> bond;

if (order == BOND_SINGLE || order == BOND_DOUBLE || order == BOND_TRIPLE || order == BOND_AROMATIC || order == _BOND_HYDROGEN ||
order == _BOND_COORDINATION)
bond = std::make_unique<QueryMolecule::Bond>(QueryMolecule::BOND_ORDER, order);
else if (order == _BOND_SINGLE_OR_DOUBLE)
bond.reset(QueryMolecule::Bond::und(QueryMolecule::Bond::nicht(new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC)),
QueryMolecule::Bond::oder(new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE),
new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_DOUBLE))));
else if (order == _BOND_SINGLE_OR_AROMATIC)
bond.reset(QueryMolecule::Bond::oder(new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE),
new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC)));
else if (order == _BOND_DOUBLE_OR_AROMATIC)
bond.reset(QueryMolecule::Bond::oder(new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_DOUBLE),
new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC)));
else if (order == _BOND_ANY)
bond = std::make_unique<QueryMolecule::Bond>();
else
throw Error("unknown bond type: %d", order);

if (topology != 0)
{
bond.reset(QueryMolecule::Bond::und(bond.release(),
new QueryMolecule::Bond(QueryMolecule::BOND_TOPOLOGY, topology == 1 ? TOPOLOGY_RING : TOPOLOGY_CHAIN)));
}

_qmol->addBond(beg - 1, end - 1, bond.release());
int direction = 0;
if (stereo == 1)
direction = BOND_UP;
else if (stereo == 6)
direction = BOND_DOWN;
_qmol->addBond(beg - 1, end - 1, QueryMolecule::createQueryMoleculeBond(order, topology, direction));
}

if (stereo == 1)
Expand Down Expand Up @@ -2076,7 +2055,7 @@ void MolfileLoader::_postLoad()
{
if (_bmol->getBondDirection(i) && !_sensible_bond_directions[i])
{
if (!stereochemistry_options.ignore_errors)
if (!stereochemistry_options.ignore_errors && !_qmol) // Don't check for query molecule
throw Error("direction of bond #%d makes no sense", i);
}
}
Expand Down Expand Up @@ -2830,29 +2809,7 @@ void MolfileLoader::_readCtab3000()
}
else
{
std::unique_ptr<QueryMolecule::Bond> bond;

if (order == BOND_SINGLE || order == BOND_DOUBLE || order == BOND_TRIPLE || order == BOND_AROMATIC || order == _BOND_COORDINATION ||
order == _BOND_HYDROGEN)
bond = std::make_unique<QueryMolecule::Bond>(QueryMolecule::BOND_ORDER, order);
else if (order == _BOND_SINGLE_OR_DOUBLE)
{
bond.reset(QueryMolecule::Bond::und(QueryMolecule::Bond::nicht(new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC)),
QueryMolecule::Bond::oder(new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE),
new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_DOUBLE))));
}
else if (order == _BOND_SINGLE_OR_AROMATIC)
bond.reset(QueryMolecule::Bond::oder(new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_SINGLE),
new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC)));
else if (order == _BOND_DOUBLE_OR_AROMATIC)
bond.reset(QueryMolecule::Bond::oder(new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_DOUBLE),
new QueryMolecule::Bond(QueryMolecule::BOND_ORDER, BOND_AROMATIC)));
else if (order == _BOND_ANY)
bond = std::make_unique<QueryMolecule::Bond>();
else
throw Error("unknown bond type: %d", order);

_qmol->addBond(beg, end, bond.release());
_qmol->addBond(beg, end, QueryMolecule::createQueryMoleculeBond(order, 0, 0));
}

while (true)
Expand Down
20 changes: 4 additions & 16 deletions core/indigo-core/molecule/src/molfile_saver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -629,14 +629,8 @@ void MolfileSaver::_writeCtab(Output& output, BaseMolecule& mol, bool query)
{
int qb = QueryMolecule::getQueryBondType(qmol->getBond(i));

if (qb == QueryMolecule::QUERY_BOND_SINGLE_OR_DOUBLE)
bond_order = 5;
else if (qb == QueryMolecule::QUERY_BOND_SINGLE_OR_AROMATIC)
bond_order = 6;
else if (qb == QueryMolecule::QUERY_BOND_DOUBLE_OR_AROMATIC)
bond_order = 7;
else if (qb == QueryMolecule::QUERY_BOND_ANY)
bond_order = 8;
if (qb == _BOND_SINGLE_OR_DOUBLE || qb == _BOND_SINGLE_OR_AROMATIC || qb == _BOND_DOUBLE_OR_AROMATIC || qb == _BOND_ANY)
bond_order = qb;
}

if (bond_order < 0)
Expand Down Expand Up @@ -1343,14 +1337,8 @@ void MolfileSaver::_writeCtab2000(Output& output, BaseMolecule& mol, bool query)
{
int qb = QueryMolecule::getQueryBondType(qmol->getBond(i));

if (qb == QueryMolecule::QUERY_BOND_SINGLE_OR_DOUBLE)
bond_order = 5;
else if (qb == QueryMolecule::QUERY_BOND_SINGLE_OR_AROMATIC)
bond_order = 6;
else if (qb == QueryMolecule::QUERY_BOND_DOUBLE_OR_AROMATIC)
bond_order = 7;
else if (qb == QueryMolecule::QUERY_BOND_ANY)
bond_order = 8;
if (qb == _BOND_SINGLE_OR_DOUBLE || qb == _BOND_SINGLE_OR_AROMATIC || qb == _BOND_DOUBLE_OR_AROMATIC || qb == _BOND_ANY)
bond_order = qb;
}

if (bond_order < 0)
Expand Down
Loading

0 comments on commit 2b6bde2

Please sign in to comment.