Optionally expose MCS to JS and extend optional compilation to JSReaction and JSSubstructLibrary (#6409)

* reverted all changes that did not really belong to this PR

* I had forgotten ChemReactions_static

* changes in response to review

---------

Co-authored-by: Tosco, Paolo <paolo.tosco@novartis.com>
This commit is contained in:
Paolo Tosco
2023-06-07 12:35:55 +02:00
committed by GitHub
parent f9f23a569c
commit 312fa9a703
9 changed files with 493 additions and 46 deletions

View File

@@ -66,6 +66,9 @@ option(RDK_BUILD_MINIMAL_LIB "build the minimal RDKit wrapper (for the JS bindin
option(RDK_MINIMAL_LIB_SUPPORT_LEGACY_BROWSERS "build the minimal RDKit JS wrapper such that it supports legacy browsers" OFF)
option(RDK_BUILD_CFFI_LIB "build the CFFI wrapper (for use in other programming languges)" OFF)
option(RDK_BUILD_FUZZ_TARGETS "build the fuzz targets" OFF)
option(RDK_BUILD_MINIMAL_LIB_RXN "build support for reactions into MinimalLib" ON )
option(RDK_BUILD_MINIMAL_LIB_SUBSTRUCTLIBRARY "build support for SubstructLibrary into MinimalLib" ON )
option(RDK_BUILD_MINIMAL_LIB_MCS "build support for MCS into MinimalLib" OFF )
set(RDK_BOOST_VERSION "1.58.0")

View File

@@ -24,6 +24,14 @@
namespace RDKit {
namespace {
struct cmpCStr {
bool operator()(const char *a, const char *b) const {
return std::strcmp(a, b) < 0;
}
};
} // namespace
void MCSParameters::setMCSAtomTyperFromEnum(AtomComparator atomComp) {
switch (atomComp) {
case AtomCompareAny:
@@ -45,7 +53,7 @@ void MCSParameters::setMCSAtomTyperFromEnum(AtomComparator atomComp) {
void MCSParameters::setMCSAtomTyperFromConstChar(const char* atomComp) {
PRECONDITION(atomComp, "atomComp must not be NULL");
static const std::map<const char*, AtomComparator> atomCompStringToEnum = {
static const std::map<const char*, AtomComparator, cmpCStr> atomCompStringToEnum = {
{"Any", AtomCompareAny},
{"Elements", AtomCompareElements},
{"Isotopes", AtomCompareIsotopes},
@@ -75,7 +83,7 @@ void MCSParameters::setMCSBondTyperFromEnum(BondComparator bondComp) {
void MCSParameters::setMCSBondTyperFromConstChar(const char* bondComp) {
PRECONDITION(bondComp, "bondComp must not be NULL");
static const std::map<const char*, BondComparator> bondCompStringToEnum = {
static const std::map<const char*, BondComparator, cmpCStr> bondCompStringToEnum = {
{"Any", BondCompareAny},
{"Order", BondCompareOrder},
{"OrderExact", BondCompareOrderExact}};

View File

@@ -1043,6 +1043,8 @@ void testJSONParameters() {
", \"MatchFusedRings\": true"
", \"MatchFusedRingsStrict\": true"
", \"InitialSeed\": \"CNC\""
", \"AtomCompare\": \"Isotopes\""
", \"BondCompare\": \"OrderExact\""
"}";
parseMCSParametersJSON(json, &pj);
TEST_ASSERT(!pj.MaximizeBonds && pj.Threshold == 0.7 && pj.Timeout == 3 &&
@@ -1055,6 +1057,8 @@ void testJSONParameters() {
pj.BondCompareParameters.CompleteRingsOnly &&
pj.BondCompareParameters.MatchFusedRings &&
pj.BondCompareParameters.MatchFusedRingsStrict &&
pj.AtomTyper == MCSAtomCompareIsotopes &&
pj.BondTyper == MCSBondCompareOrderExact &&
0 == strcmp(pj.InitialSeed.c_str(), "CNC"));
}
{

View File

@@ -2,6 +2,22 @@ include_directories(${RDKit_ExternalDir})
include_directories(${RDKit_ExternalDir}/rapidjson-1.1.0/include)
if(RDK_BUILD_MINIMAL_LIB)
set(MINIMAL_LIB_LIBRARIES "MolInterchange_static;Abbreviations_static;"
"CIPLabeler_static;MolDraw2D_static;Depictor_static;RDInchiLib_static;"
"SubstructMatch_static;FileParsers_static;"
"SmilesParse_static;GraphMol_static;RDGeometryLib_static;RDGeneral_static")
if(RDK_BUILD_MINIMAL_LIB_RXN)
add_definitions(-DRDK_BUILD_MINIMAL_LIB_RXN)
set(MINIMAL_LIB_LIBRARIES "${MINIMAL_LIB_LIBRARIES};ChemReactions_static")
endif()
if(RDK_BUILD_MINIMAL_LIB_SUBSTRUCTLIBRARY)
add_definitions(-DRDK_BUILD_MINIMAL_LIB_SUBSTRUCTLIBRARY)
set(MINIMAL_LIB_LIBRARIES "${MINIMAL_LIB_LIBRARIES};SubstructLibrary_static")
endif()
if(RDK_BUILD_MINIMAL_LIB_MCS)
add_definitions(-DRDK_BUILD_MINIMAL_LIB_MCS)
set(MINIMAL_LIB_LIBRARIES "${MINIMAL_LIB_LIBRARIES};FMCS_static")
endif()
if(RDK_BUILD_FREETYPE_SUPPORT)
if( ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten")
set(USE_FLAGS "-s USE_FREETYPE=1")
@@ -10,10 +26,7 @@ if(RDK_BUILD_MINIMAL_LIB)
endif()
endif()
add_executable(RDKit_minimal jswrapper.cpp minilib.cpp)
target_link_libraries(RDKit_minimal MolInterchange_static Abbreviations_static
CIPLabeler_static MolDraw2D_static Depictor_static RDInchiLib_static
SubstructMatch_static SubstructLibrary_static FileParsers_static
SmilesParse_static GraphMol_static RDGeometryLib_static RDGeneral_static)
target_link_libraries(RDKit_minimal ${MINIMAL_LIB_LIBRARIES})
set_target_properties(RDKit_minimal PROPERTIES LINK_FLAGS "--bind")
endif(RDK_BUILD_MINIMAL_LIB)

View File

@@ -107,6 +107,7 @@ std::string draw_to_canvas_with_highlights(JSMol &self, emscripten::val canvas,
return "";
}
#ifdef RDK_BUILD_MINIMAL_LIB_RXN
std::string draw_rxn_to_canvas_with_offset(JSReaction &self,
emscripten::val canvas, int offsetx,
int offsety, int width, int height) {
@@ -172,11 +173,22 @@ std::string draw_rxn_to_canvas_with_highlights(JSReaction &self,
: &highlightColorsReactants);
return "";
}
#endif
JSMol *get_mol_no_details(const std::string &input) {
return get_mol(input, std::string());
}
#ifdef RDK_BUILD_MINIMAL_LIB_MCS
JSMol *get_mcs_as_mol_no_details(const JSMolList &mols) {
return get_mcs_as_mol(mols, std::string());
}
std::string get_mcs_as_smarts_no_details(const JSMolList &mols) {
return get_mcs_as_smarts(mols, std::string());
}
#endif
emscripten::val binary_string_to_uint8array(const std::string &pkl) {
emscripten::val view(emscripten::typed_memory_view(
pkl.size(), reinterpret_cast<const unsigned char *>(pkl.c_str())));
@@ -204,9 +216,11 @@ JSMol *get_mol_from_uint8array(const emscripten::val &pklAsUInt8Array) {
return get_mol_from_pickle(pklAsUInt8Array.as<std::string>());
}
#ifdef RDK_BUILD_MINIMAL_LIB_RXN
JSReaction *get_rxn_no_details(const std::string &input) {
return get_rxn(input, std::string());
}
#endif
std::string generate_aligned_coords_helper(JSMol &self,
const JSMol &templateMol,
@@ -312,7 +326,7 @@ emscripten::val get_frags_helper(JSMol &self, const std::string &details) {
auto res = self.get_frags(details);
auto obj = emscripten::val::object();
auto molArray = emscripten::val::object();
obj.set("molIterator", res.first);
obj.set("molList", res.first);
obj.set("mappings", res.second);
return obj;
}
@@ -321,6 +335,7 @@ emscripten::val get_frags_helper(JSMol &self) {
return get_frags_helper(self, "{}");
}
#ifdef RDK_BUILD_MINIMAL_LIB_SUBSTRUCTLIBRARY
int add_trusted_smiles_and_pattern_fp_helper(
JSSubstructLibrary &self, const std::string &smi,
const emscripten::val &patternFpAsUInt8Array) {
@@ -355,6 +370,7 @@ emscripten::val get_matches_as_uint32array(const JSSubstructLibrary &self,
self.d_defaultNumThreads,
self.d_defaultMaxResults);
}
#endif
#ifdef RDK_BUILD_AVALON_SUPPORT
emscripten::val get_avalon_fp_as_uint8array(const JSMol &self,
@@ -555,14 +571,26 @@ EMSCRIPTEN_BINDINGS(RDKit_minimal) {
.function("straighten_depiction",
select_overload<void()>(&JSMol::straighten_depiction))
.function("straighten_depiction",
select_overload<void(bool)>(&JSMol::straighten_depiction));
select_overload<void(bool)>(&JSMol::straighten_depiction))
.function("get_num_atoms", select_overload<unsigned int(bool) const>(
&JSMol::get_num_atoms))
.function("get_num_atoms",
select_overload<unsigned int() const>(&JSMol::get_num_atoms))
.function("get_num_bonds", &JSMol::get_num_bonds)
;
class_<JSMolIterator>("MolIterator")
.function("next", &JSMolIterator::next, allow_raw_pointers())
.function("reset", &JSMolIterator::reset)
.function("at_end", &JSMolIterator::at_end)
.function("size", &JSMolIterator::size);
class_<JSMolList>("MolList")
.constructor<>()
.function("append", &JSMolList::append)
.function("insert", &JSMolList::insert)
.function("at", &JSMolList::at, allow_raw_pointers())
.function("pop", &JSMolList::pop, allow_raw_pointers())
.function("next", &JSMolList::next, allow_raw_pointers())
.function("reset", &JSMolList::reset)
.function("at_end", &JSMolList::at_end)
.function("size", &JSMolList::size);
#ifdef RDK_BUILD_MINIMAL_LIB_RXN
class_<JSReaction>("Reaction")
#ifdef __EMSCRIPTEN__
.function("draw_to_canvas_with_offset", &draw_rxn_to_canvas_with_offset)
@@ -577,7 +605,9 @@ EMSCRIPTEN_BINDINGS(RDKit_minimal) {
.function("get_svg_with_highlights",
&JSReaction::get_svg_with_highlights);
#endif
#ifdef RDK_BUILD_MINIMAL_LIB_SUBSTRUCTLIBRARY
class_<JSSubstructLibrary>("SubstructLibrary")
.constructor<>()
.constructor<unsigned int>()
@@ -627,7 +657,9 @@ EMSCRIPTEN_BINDINGS(RDKit_minimal) {
.function("count_matches",
select_overload<unsigned int(const JSMol &) const>(
&JSSubstructLibrary::count_matches))
.function("size", &JSSubstructLibrary::size);
.function("size", &JSSubstructLibrary::size)
#endif
;
function("version", &version);
function("prefer_coordgen", &prefer_coordgen);
@@ -640,6 +672,14 @@ EMSCRIPTEN_BINDINGS(RDKit_minimal) {
allow_raw_pointers());
function("get_mol_copy", &get_mol_copy, allow_raw_pointers());
function("get_qmol", &get_qmol, allow_raw_pointers());
#ifdef RDK_BUILD_MINIMAL_LIB_RXN
function("get_rxn", &get_rxn, allow_raw_pointers());
function("get_rxn", &get_rxn_no_details, allow_raw_pointers());
#endif
#ifdef RDK_BUILD_MINIMAL_LIB_MCS
function("get_mcs_as_mol", &get_mcs_as_mol, allow_raw_pointers());
function("get_mcs_as_mol", &get_mcs_as_mol_no_details, allow_raw_pointers());
function("get_mcs_as_smarts", &get_mcs_as_smarts);
function("get_mcs_as_smarts", &get_mcs_as_smarts_no_details);
#endif
}

View File

@@ -24,7 +24,12 @@
#include <GraphMol/MolDraw2D/MolDraw2DSVG.h>
#include <GraphMol/MolDraw2D/MolDraw2DUtils.h>
#include <GraphMol/Substruct/SubstructMatch.h>
#ifdef RDK_BUILD_MINIMAL_LIB_SUBSTRUCTLIBRARY
#include <GraphMol/SubstructLibrary/SubstructLibrary.h>
#endif
#ifdef RDK_BUILD_MINIMAL_LIB_MCS
#include <GraphMol/FMCS/FMCS.h>
#endif
#include <GraphMol/Descriptors/Property.h>
#include <GraphMol/Descriptors/MolDescriptors.h>
#include <GraphMol/MolInterchange/MolInterchange.h>
@@ -625,7 +630,7 @@ void JSMol::straighten_depiction(bool minimizeRotation) {
RDDepict::straightenDepiction(*d_mol, -1, minimizeRotation);
}
std::pair<JSMolIterator *, std::string> JSMol::get_frags(
std::pair<JSMolList *, std::string> JSMol::get_frags(
const std::string &details_json) {
if (!d_mol) {
return std::make_pair(nullptr, "");
@@ -639,10 +644,21 @@ std::pair<JSMolIterator *, std::string> JSMol::get_frags(
auto molFrags = MolOps::getMolFrags(*d_mol, sanitizeFrags, &frags,
&fragsMolAtomMapping, copyConformers);
return std::make_pair(
new JSMolIterator(molFrags),
new JSMolList(molFrags),
MinimalLib::get_mol_frags_mappings(frags, fragsMolAtomMapping));
}
unsigned int JSMol::get_num_atoms(bool heavyOnly) const {
assert(d_mol);
return heavyOnly ? d_mol->getNumHeavyAtoms() : d_mol->getNumAtoms();
}
unsigned int JSMol::get_num_bonds() const {
assert(d_mol);
return d_mol->getNumBonds();
}
#ifdef RDK_BUILD_MINIMAL_LIB_RXN
std::string JSReaction::get_svg(int w, int h) const {
if (!d_rxn) {
return "";
@@ -659,7 +675,46 @@ std::string JSReaction::get_svg_with_highlights(
int h = d_defaultHeight;
return MinimalLib::rxn_to_svg(*d_rxn, w, h, details);
}
#endif
JSMol *JSMolList::next() {
return (d_idx < d_mols.size()
? new JSMol(new RDKit::RWMol(*d_mols.at(d_idx++)))
: nullptr);
}
JSMol *JSMolList::at(size_t idx) const {
return (idx < d_mols.size() ? new JSMol(new RDKit::RWMol(*d_mols.at(idx)))
: nullptr);
}
JSMol *JSMolList::pop(size_t idx) {
JSMol *res = nullptr;
if (idx < d_mols.size()) {
res = new JSMol(new RDKit::RWMol(*d_mols.at(idx)));
d_mols.erase(d_mols.begin() + idx);
if (d_idx > idx) {
--d_idx;
}
}
return res;
}
size_t JSMolList::append(const JSMol &mol) {
d_mols.emplace_back(new ROMol(*mol.d_mol));
return d_mols.size();
}
size_t JSMolList::insert(size_t idx, const JSMol &mol) {
size_t res = 0;
if (idx < d_mols.size()) {
d_mols.emplace(d_mols.begin() + idx, new ROMol(*mol.d_mol));
res = d_mols.size();
}
return res;
}
#ifdef RDK_BUILD_MINIMAL_LIB_SUBSTRUCTLIBRARY
JSSubstructLibrary::JSSubstructLibrary(unsigned int num_bits) :
d_fpHolder(nullptr) {
boost::shared_ptr<CachedTrustedSmilesMolHolder> molHolderSptr(new CachedTrustedSmilesMolHolder());
@@ -771,6 +826,7 @@ unsigned int JSSubstructLibrary::count_matches(const JSMol &q,
false, numThreads)
: 0;
}
#endif
std::string get_inchikey_for_inchi(const std::string &input) {
return InchiToInchiKey(input);
@@ -805,10 +861,12 @@ JSMol *get_qmol(const std::string &input) {
return new JSMol(mol);
}
#ifdef RDK_BUILD_MINIMAL_LIB_RXN
JSReaction *get_rxn(const std::string &input, const std::string &details_json) {
auto rxn = MinimalLib::rxn_from_input(input, details_json);
return new JSReaction(rxn);
}
#endif
std::string version() { return std::string(rdkitVersion); }
@@ -829,3 +887,28 @@ bool allow_non_tetrahedral_chirality(bool value) {
Chirality::setAllowNontetrahedralChirality(value);
return was;
}
#ifdef RDK_BUILD_MINIMAL_LIB_MCS
namespace {
MCSResult getMcsResult(const JSMolList &molList,
const std::string &details_json) {
MCSParameters p;
if (!details_json.empty()) {
parseMCSParametersJSON(details_json.c_str(), &p);
}
return RDKit::findMCS(molList.mols(), &p);
}
} // namespace
std::string get_mcs_as_smarts(const JSMolList &molList,
const std::string &details_json) {
auto res = getMcsResult(molList, details_json);
return res.SmartsString;
}
JSMol *get_mcs_as_mol(const JSMolList &molList,
const std::string &details_json) {
auto res = getMcsResult(molList, details_json);
return new JSMol(new RWMol(*res.QueryMol));
}
#endif

View File

@@ -14,7 +14,7 @@
#include <GraphMol/ChemReactions/Reaction.h>
#include <GraphMol/ChemReactions/ReactionParser.h>
class JSMolIterator;
class JSMolList;
class JSMol {
public:
@@ -132,35 +132,41 @@ class JSMol {
double normalize_depiction() { return normalize_depiction(1, -1.); }
void straighten_depiction(bool minimizeRotation);
void straighten_depiction() { straighten_depiction(false); }
std::pair<JSMolIterator *, std::string> get_frags(
std::pair<JSMolList *, std::string> get_frags(
const std::string &details_json);
std::pair<JSMolIterator *, std::string> get_frags() {
std::pair<JSMolList *, std::string> get_frags() {
return get_frags("{}");
}
unsigned int get_num_atoms(bool heavyOnly) const;
unsigned int get_num_atoms() const { return get_num_atoms(false); };
unsigned int get_num_bonds() const;
std::unique_ptr<RDKit::RWMol> d_mol;
static constexpr int d_defaultWidth = 250;
static constexpr int d_defaultHeight = 200;
};
class JSMolIterator {
class JSMolList {
public:
JSMolIterator(const std::vector<RDKit::ROMOL_SPTR> &mols)
JSMolList(const std::vector<RDKit::ROMOL_SPTR> &mols)
: d_mols(mols), d_idx(0){};
JSMol *next() {
return (d_idx < d_mols.size()
? new JSMol(new RDKit::RWMol(*d_mols.at(d_idx++)))
: nullptr);
}
JSMolList() : d_idx(0){};
JSMol *next();
size_t append(const JSMol &mol);
size_t insert(size_t idx, const JSMol &mol);
JSMol *at(size_t idx) const;
JSMol *pop(size_t idx);
void reset() { d_idx = 0; }
bool at_end() { return d_idx == d_mols.size(); }
size_t size() { return d_mols.size(); }
bool at_end() const { return d_idx == d_mols.size(); }
size_t size() const { return d_mols.size(); }
const std::vector<RDKit::ROMOL_SPTR> &mols() const { return d_mols; }
private:
std::vector<RDKit::ROMOL_SPTR> d_mols;
size_t d_idx;
};
#ifdef RDK_BUILD_MINIMAL_LIB_RXN
class JSReaction {
public:
JSReaction() : d_rxn(nullptr) {}
@@ -177,7 +183,9 @@ class JSReaction {
static constexpr int d_defaultWidth = 800;
static constexpr int d_defaultHeight = 200;
};
#endif
#ifdef RDK_BUILD_MINIMAL_LIB_SUBSTRUCTLIBRARY
class JSSubstructLibrary {
public:
JSSubstructLibrary(unsigned int num_bits);
@@ -222,14 +230,21 @@ class JSSubstructLibrary {
private:
inline int add_mol_helper(const RDKit::ROMol &mol);
};
#endif
std::string get_inchikey_for_inchi(const std::string &input);
JSMol *get_mol(const std::string &input, const std::string &details_json);
JSMol *get_mol_from_pickle(const std::string &pkl);
JSMol *get_mol_copy(const JSMol &other);
JSMol *get_qmol(const std::string &input);
#ifdef RDK_BUILD_MINIMAL_LIB_RXN
JSReaction *get_rxn(const std::string &input, const std::string &details_json);
#endif
std::string version();
void prefer_coordgen(bool prefer);
bool use_legacy_stereo_perception(bool value);
bool allow_non_tetrahedral_chirality(bool value);
#ifdef RDK_BUILD_MINIMAL_LIB_MCS
std::string get_mcs_as_smarts(const JSMolList &mols, const std::string &details_json);
JSMol *get_mcs_as_mol(const JSMolList &mols, const std::string &details_json);
#endif

View File

@@ -1769,17 +1769,17 @@ function test_get_frags() {
frags: [0,0,0,0,0,0,1,1,1,1,2,2,2,2,2],
fragsMolAtomMapping: [[0,1,2,3,4,5],[6,7,8,9],[10,11,12,13,14]],
};
var { molIterator, mappings } = mol.get_frags();
assert(molIterator.size() === 3);
var { molList, mappings } = mol.get_frags();
assert(molList.size() === 3);
assert(JSON.stringify(JSON.parse(mappings)) === JSON.stringify(expectedMappings));
var i = 0;
while (!molIterator.at_end()) {
var mol = molIterator.next();
while (!molList.at_end()) {
var mol = molList.next();
assert(mol.get_smiles() === expectedFragSmiles[i++]);
mol.delete();
}
assert(!molIterator.next());
molIterator.delete();
assert(!molList.next());
molList.delete();
}
{
var mol = RDKitModule.get_mol("N(C)(C)(C)C.c1ccc1", JSON.stringify({sanitize: false}));
@@ -1790,16 +1790,16 @@ function test_get_frags() {
exceptionThrown = true;
}
assert(exceptionThrown);
var { molIterator, mappings } = mol.get_frags(JSON.stringify({sanitizeFrags: false}));
assert(molIterator.size() === 2);
var { molList, mappings } = mol.get_frags(JSON.stringify({sanitizeFrags: false}));
assert(molList.size() === 2);
var i = 0;
while (!molIterator.at_end()) {
var mol = molIterator.next();
while (!molList.at_end()) {
var mol = molList.next();
assert(mol.get_smiles() === expectedFragSmilesNonSanitized[i++]);
mol.delete();
}
assert(!molIterator.next());
molIterator.delete();
assert(!molList.next());
molList.delete();
}
}
@@ -2045,6 +2045,270 @@ M END`);
}
}
function molIteratorFromSmiArray(smiArray) {
const molList = new RDKitModule.MolList();
assert(molList);
smiArray.forEach((smiName) => {
const [smi, name] = smiName.split(' ');
let mol;
try {
mol = RDKitModule.get_mol(smi);
assert(mol);
molList.append(mol);
} finally {
if (mol) {
mol.delete();
}
}
});
return molList;
}
function test_mol_iterator() {
const smiArray = [ 'C1CC1', 'C1CCCC1' ];
let molList;
let mol;
try {
molList = molIteratorFromSmiArray(smiArray);
assert(molList);
assert.equal(molList.size(), 2);
assert(!molList.at_end());
try {
mol = molList.next();
assert(mol);
} finally {
if (mol) {
mol.delete();
}
}
try {
mol = molList.next();
assert(mol);
} finally {
if (mol) {
mol.delete();
}
}
mol = molList.next();
assert(!mol);
assert(molList.at_end());
try {
mol = molList.at(0);
assert.equal(mol.get_num_atoms(), 3);
} finally {
if (mol) {
mol.delete();
}
}
try {
mol = molList.at(1);
assert.equal(mol.get_num_atoms(), 5);
} finally {
if (mol) {
mol.delete();
}
}
assert(molList.at_end());
try {
mol = RDKitModule.get_mol('C1CCC1');
assert(mol);
molList.insert(1, mol);
assert.equal(molList.size(), 3);
assert(!molList.at_end());
} finally {
if (mol) {
mol.delete();
}
}
molList.reset();
assert(!molList.at_end());
try {
mol = molList.at(1);
assert.equal(mol.get_num_atoms(), 4);
} finally {
if (mol) {
mol.delete();
}
}
try {
mol = molList.pop(0);
assert.equal(mol.get_num_atoms(), 3);
} finally {
if (mol) {
mol.delete();
}
}
assert.equal(molList.size(), 2);
let i = 0;
while (!molList.at_end()) {
try {
mol = molList.next();
} finally {
if (mol) {
++i;
mol.delete();
}
}
}
assert.equal(i, 2);
assert(molList.at_end());
try {
mol = molList.pop(0);
assert.equal(mol.get_num_atoms(), 4);
} finally {
if (mol) {
mol.delete();
}
}
assert.equal(molList.size(), 1);
assert(molList.at_end());
try {
mol = molList.pop(0);
assert.equal(mol.get_num_atoms(), 5);
} finally {
if (mol) {
mol.delete();
}
}
assert.equal(molList.size(), 0);
assert(molList.at_end());
assert(!molList.pop(0));
try {
mol = RDKitModule.get_mol('C1CCCCC1');
assert(mol);
molList.append(mol);
assert.equal(molList.size(), 1);
assert(!molList.at_end());
} finally {
if (mol) {
mol.delete();
}
}
assert(!molList.at(1));
try {
mol = molList.at(0);
assert(mol);
assert(mol.get_num_atoms(), 6);
} finally {
if (mol) {
mol.delete();
}
}
} finally {
if (molList) {
molList.delete();
}
}
}
function test_mcs() {
{
const smiArray = [
"COc1cc2nc(-c3cc(NC(=O)CSc4ccc(Cl)cc4)ccc3)oc2cc1 CHEMBL1479679",
"COc1cc2nc(-c3cc(NC(=O)CSc4ccc(Cl)cc4)c(C)cc3)oc2cc1 CHEMBL1333382",
"Cc1cc2oc(-c3cc(NC(=O)CSc4ccc(Cl)cc4)ccc3)nc2cc1 CHEMBL1437584",
"COc1c(NC(=O)CSc2ccc(Cl)cc2)cc(-c2nc3ccccc3o2)cc1 CHEMBL1601350",
"Cc1cc2nc(-c3cccc(NC(=O)CSc4ccc(Cl)cc4)c3)oc2cc1C CHEMBL1398008",
"Cc1cc2oc(-c3cc(NC(=O)CSc4ccc(Cl)cc4)c(C)cc3)nc2cc1 CHEMBL1612903",
"COc1cc2nc(-c3cc(NC(=O)Cc4ccc(Cl)cc4)c(C)cc3)oc2cc1 CHEMBL1316483",
"Cc1c(NC(=O)CSc2ccc(Cl)cc2)cccc1-c1nc2cc(Cl)ccc2o1 CHEMBL1568754",
"COc1ccc2oc(-c3ccc(C)c(NC(=O)COc4cc(C)cc(C)c4)c3)nc2c1 CHEMBL1436972",
"Cc1ccc(SCC(=O)Nc2cc(-c3nc4cc(C)ccc4o3)c(O)cc2)cc1 CHEMBL1611932",
];
let molList;
let mcsSmarts;
try {
molList = molIteratorFromSmiArray(smiArray);
let mcsMol;
try {
mcsMol = RDKitModule.get_mcs_as_mol(molList);
assert(mcsMol);
assert.equal(mcsMol.get_smarts(), '[#6]1:[#6]:[#6]2:[#8]:[#6](:[#7]:[#6]:2:[#6]:[#6]:1)-[#6]1:[#6]:[#6](-[#7]-[#6](=[#8])-[#6]):[#6]:[#6]:[#6]:1');
mcsSmarts = RDKitModule.get_mcs_as_smarts(molList);
} finally {
if (mcsMol) {
mcsMol.delete();
}
}
} finally {
if (molList) {
molList.delete();
}
}
assert(mcsSmarts);
assert.equal(mcsSmarts, '[#6]1:[#6]:[#6]2:[#8]:[#6](:[#7]:[#6]:2:[#6]:[#6]:1)-[#6]1:[#6]:[#6](-[#7]-[#6](=[#8])-[#6]):[#6]:[#6]:[#6]:1');
}
{
const smiArray = ["C1CC1N2CC2", "C1CC1N"];
let molList;
try {
molList = molIteratorFromSmiArray(smiArray);
let mcsMol;
try {
mcsMol = RDKitModule.get_mcs_as_mol(molList);
assert(mcsMol);
assert.equal(mcsMol.get_num_atoms(), 4);
assert.equal(mcsMol.get_num_bonds(), 4);
} finally {
if (mcsMol) {
mcsMol.delete();
}
}
try {
mcsMol = RDKitModule.get_mcs_as_mol(molList, JSON.stringify({ RingMatchesRingOnly: true }));
assert(mcsMol);
assert.equal(mcsMol.get_num_atoms(), 3);
assert.equal(mcsMol.get_num_bonds(), 3);
} finally {
if (mcsMol) {
mcsMol.delete();
}
}
} finally {
if (molList) {
molList.delete();
}
}
}
{
const smiArray = ["NC1CCCCC1", "Cc1ccccc1", "Cc1cnccc1", "CC1CCCCN1"];
let molList;
const res = new Set();
try {
molList = molIteratorFromSmiArray(smiArray);
["Elements", "Any"].forEach((AtomCompare) => {
["Order", "OrderExact"].forEach((BondCompare) => {
const details = {
AtomCompare,
BondCompare
};
let mcsSmarts;
mcsSmarts = RDKitModule.get_mcs_as_smarts(molList, JSON.stringify(details));
assert(mcsSmarts);
res.add(mcsSmarts);
});
});
} finally {
if (molList) {
molList.delete();
}
}
assert(res.size === 4);
}
}
function test_get_num_atoms_bonds() {
var mol = RDKitModule.get_mol('CCCC');
var molH = RDKitModule.get_mol_copy(mol);
molH.add_hs_in_place();
assert.equal(mol.get_num_atoms(), molH.get_num_atoms(true));
assert.equal(mol.get_num_atoms(), 4);
assert.equal(molH.get_num_atoms(), 14);
assert.equal(molH.get_num_atoms(false), 14);
assert.equal(mol.get_num_bonds(), 3);
assert.equal(molH.get_num_bonds(), 13);
}
initRDKitModule().then(function(instance) {
var done = {};
const waitAllTestsFinished = () => {
@@ -2066,11 +2330,13 @@ initRDKitModule().then(function(instance) {
test_sketcher_services();
test_sketcher_services2();
test_abbreviations();
test_substruct_library(done);
test_substruct_library_merge_hs();
test_substruct_library_empty_mols();
test_substruct_library_empty_lib();
test_substruct_library_empty_query();
if (RDKitModule.SubstructLibrary) {
test_substruct_library(done);
test_substruct_library_merge_hs();
test_substruct_library_empty_mols();
test_substruct_library_empty_lib();
test_substruct_library_empty_query();
}
test_generate_aligned_coords();
test_isotope_labels();
test_generate_aligned_coords_allow_rgroups();
@@ -2086,7 +2352,9 @@ initRDKitModule().then(function(instance) {
test_normalize_depiction();
test_straighten_depiction();
test_flexicanvas();
test_rxn_drawing();
if (RDKitModule.get_rxn) {
test_rxn_drawing();
}
test_legacy_stereochem();
test_allow_non_tetrahedral_chirality();
test_prop();
@@ -2099,6 +2367,11 @@ initRDKitModule().then(function(instance) {
test_hs_in_place();
test_query_colour();
test_alignment_r_groups_aromatic_ring();
test_mol_iterator();
test_get_num_atoms_bonds();
if (RDKitModule.get_mcs_as_mol) {
test_mcs();
}
waitAllTestsFinished().then(() =>
console.log("Tests finished successfully")
);

View File

@@ -6,6 +6,14 @@
## Highlights
## Backwards incompatible changes
In JS MinimalLib `MolIterator` was renamed to `MolList`: since now it
includes `at()`, `append()`, `insert()` and `pop()` methods, `MolIterator`
felt inappropriate. This change should have minimal impact on existing
JS code since so far there was no constructor for this class.
The only place where JS code needs to be updated is when parsing the return
value of `JSMol::get_frags()`: the return value consists of an object with
two keys, `molIterator` and `mappings`. The `molIterator` key has now
been renamed to `molList`.
## Bug Fixes: