Support using iterators with MolSuppliers (#9230)

* iterators for random-access MolSuppliers
add optional caching to SDMolSupplier

* add support to SmilesMolSupplier too
There is a lot of duplicate code between the random-access suppliers that would be worth trying to remove
but at the moment it looks like it would require multiple inheritance, and I think we want to avoid that

* add input iterators for ForwardSDMolSupplier()

* throw when calling begin() on a used supplier

* switch to use the spaceship operator

* init() should reset the mol cache

* Make SDMolSupplier and SmilesMolSupplier safe for multi-threaded reads

* add benchmarking

* add TDTMolSupplier support
improved testing
add benchmarks for parallel iteration
optional TBB support

* better const handling, add reverse iterators

doesn't look like const_iterator is possible since getting data from the underlyng supplier object is non-const

* improve docs
more usings
add reverse iterator to TDTMolSupplier

* tests only try execution::par when it is there

* fix typo

* more testing/demo

* remove accidentally added files

* review changes

* add default ctors

* disable a false-positive compiler warning
it is stupid to have to do this

---------

Co-authored-by: = <=>
This commit is contained in:
Greg Landrum
2026-05-05 13:36:15 +02:00
committed by GitHub
parent 232e4ffc84
commit 6d75052459
12 changed files with 54835 additions and 69 deletions

View File

@@ -130,6 +130,14 @@ rdkit_catch_test(v2FileParsersCatchTest v2_file_parsers_catch.cpp
rdkit_catch_test(atropisomersCatch atropisomers_catch.cpp
LINK_LIBRARIES FileParsers)
find_package(TBB )
if(TBB_FOUND)
rdkit_catch_test(fileParsersIteratorsTest fileParsersIterTest.cpp
LINK_LIBRARIES SmilesParse FileParsers SubstructMatch TBB::tbb)
else()
rdkit_catch_test(fileParsersIteratorsTest fileParsersIterTest.cpp
LINK_LIBRARIES SmilesParse FileParsers SubstructMatch)
endif()
if(RDK_TEST_MULTITHREADED AND RDK_BUILD_THREADSAFE_SSS)

View File

@@ -19,10 +19,14 @@
#include <memory>
#include <vector>
#include <fstream>
#include <iterator>
#include <GraphMol/ROMol.h>
#include <RDGeneral/BadFileException.h>
#include "FileParsers.h"
#include <GraphMol/SmilesParse/SmilesParse.h>
#ifdef RDK_BUILD_THREADSAFE_SSS
#include <mutex>
#endif
#ifdef RDK_BUILD_MAEPARSER_SUPPORT
namespace schrodinger {
@@ -38,27 +42,67 @@ RDKIT_FILEPARSERS_EXPORT std::string strip(const std::string &orig);
namespace v2 {
namespace FileParsers {
// clang-format off
/*!
//
// Here are a couple of ways one can interact with MolSuppliers:
//
// 1) Lazy (ForwardIterator):
// while(!supplier.atEnd()){
// ROMol *mol = supplier.next();
// if(mol){
// do something;
// }
// }
// 2) Random Access:
// for(int i=0;i<supplier.length();i++){
// ROMol *mol = supplier[i];
// if(mol){
// do something;
// }
// }
//
//
Here are some of the ways one can interact with MolSuppliers:
1) Lazy (works with forward and random access suppliers):
while(!supplier.atEnd()){
auto mol = supplier.next(); // mol is a std::unique_ptr<RWMol>
if(mol){
do something;
}
}
2) Range based for loops (works with forward and random access suppliers):
for(auto mol : supplier){
if(mol) {
do something; // mol is a shared_ptr<RWMol>
}
}
3) Random Access:
for(int i=0;i<supplier.length();i++){
auto mol = supplier[i]; // mol is a std::unique_ptr<RWMol>
if(mol){
do something;
}
}
4) Random access supplier also support caching:
supplier.setCaching(true);
for(auto mol : supplier){
if(mol) {
do something; // mol is a shared_ptr<RWMol>
}
}
Subsequent iterations will be much faster as the molecules are cached
after the first read.
It's also possible to access the cached molecules directly using the getShared method:
supplier.setCaching(true);
for(int i=0;i<supplier.length();i++){
auto mol = supplier.getShared(i); // mol is a std::shared_ptr<RWMol>;
if(mol){
do something;
}
}
5) Random access suppliers can also be used with parallel algorithms:
supplier.setCaching(true);
std::for_each(std::execution::par, supplier.begin(), supplier.end(),
[](auto mol) {
if (mol) {
do something;
}
});
Caching is not required here, but if you are planning on working with the
molecules multiple times, it can speed things up significantly.
*/
// clang-format on
class RDKIT_FILEPARSERS_EXPORT MolSupplier {
// this is an abstract base class to supply molecules one at a time
public:
@@ -118,6 +162,65 @@ class RDKIT_FILEPARSERS_EXPORT MolSupplier {
}
};
// g++ 13 generates some spurious warnings from here (they are gone in g++ 14).
// disable them
#if !defined(__clang__) && defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
// \brief an input iterator for suppliers that only support forward reading
template <typename Supplier>
struct ForwardSupplierIter {
using iterator_category = std::input_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = std::shared_ptr<RWMol>;
using const_value_type = const std::shared_ptr<RWMol>;
Supplier *supplier = nullptr;
std::optional<value_type> current;
ForwardSupplierIter() = default;
ForwardSupplierIter(const ForwardSupplierIter &) = default;
ForwardSupplierIter &operator=(const ForwardSupplierIter &) = default;
ForwardSupplierIter(ForwardSupplierIter &&) = default;
ForwardSupplierIter &operator=(ForwardSupplierIter &&) = default;
explicit ForwardSupplierIter(Supplier *supplier)
: supplier(supplier), current(supplier->nextShared()) {}
const_value_type operator*() const { return current.value(); }
ForwardSupplierIter &operator++() {
if (supplier->atEnd()) {
current.reset();
return *this;
}
current = supplier->nextShared();
// This is the special case where there's a trailing blank line in the SDF.
// we were actually at the logical end of the file coming in, but atEnd()
// hadn't yet been set.
// In this case we want to make sure to reset the iterator to the end state
// instead of returning a null molecule.
if (!current.value() && supplier->getEOFHitOnRead()) {
current.reset();
}
return *this;
}
ForwardSupplierIter operator++(int) {
if (supplier->atEnd()) {
current.reset();
return *this;
}
ForwardSupplierIter tmp = *this;
++(*this);
return tmp;
}
bool operator==(const ForwardSupplierIter &other) const {
return !current.has_value() && !other.current.has_value();
}
};
#if !defined(__clang__) && defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
// \brief a supplier from an SD file that only reads forward:
class RDKIT_FILEPARSERS_EXPORT ForwardSDMolSupplier : public MolSupplier {
/*************************************************************************
@@ -126,6 +229,7 @@ class RDKIT_FILEPARSERS_EXPORT ForwardSDMolSupplier : public MolSupplier {
*noted.
***********************************************************************************/
public:
using iterator = ForwardSupplierIter<ForwardSDMolSupplier>;
ForwardSDMolSupplier() { init(); }
explicit ForwardSDMolSupplier(
@@ -137,6 +241,9 @@ class RDKIT_FILEPARSERS_EXPORT ForwardSDMolSupplier : public MolSupplier {
void init() override;
void reset() override;
std::unique_ptr<RWMol> next() override;
std::shared_ptr<RWMol> nextShared() {
return std::shared_ptr<RWMol>(this->next());
};
bool atEnd() override;
void setProcessPropertyLists(bool val) { df_processPropertyLists = val; }
@@ -144,6 +251,16 @@ class RDKIT_FILEPARSERS_EXPORT ForwardSDMolSupplier : public MolSupplier {
bool getEOFHitOnRead() const { return df_eofHitOnRead; }
iterator begin() {
if (d_line) {
throw ValueErrorException(
"Cannot create an iterator for a ForwardSDMolSupplier that has already "
"been read from.");
}
return iterator(this);
}
iterator end() { return iterator(); }
protected:
virtual void checkForEnd();
std::unique_ptr<RWMol> _next();
@@ -154,21 +271,102 @@ class RDKIT_FILEPARSERS_EXPORT ForwardSDMolSupplier : public MolSupplier {
bool df_processPropertyLists = true;
bool df_eofHitOnRead = false;
};
// clang-format off
static_assert(
std::ranges::input_range<ForwardSDMolSupplier>
);
// clang-format on
// \brief a random access iterator for suppliers that support random access
template <typename Supplier>
struct RandomAccessSupplierIter {
using iterator_category = std::random_access_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = std::shared_ptr<RWMol>;
using const_value_type = const std::shared_ptr<RWMol>;
Supplier *supplier = nullptr;
size_t current_idx = 0;
RandomAccessSupplierIter() = default;
RandomAccessSupplierIter(const RandomAccessSupplierIter &) = default;
RandomAccessSupplierIter &operator=(const RandomAccessSupplierIter &) =
default;
RandomAccessSupplierIter(RandomAccessSupplierIter &&) = default;
RandomAccessSupplierIter &operator=(RandomAccessSupplierIter &&) = default;
explicit RandomAccessSupplierIter(Supplier *supplier)
: supplier(supplier), current_idx(0) {}
RandomAccessSupplierIter(Supplier *supplier, size_t idx)
: supplier(supplier), current_idx(idx) {}
const_value_type operator*() const {
return supplier->getShared(current_idx);
}
const_value_type operator[](size_t idx) const {
return supplier->getShared(idx);
}
RandomAccessSupplierIter &operator++() {
++current_idx;
return *this;
}
RandomAccessSupplierIter operator++(int) {
RandomAccessSupplierIter tmp(this->supplier, current_idx);
++(*this);
return tmp;
}
RandomAccessSupplierIter &operator--() {
--current_idx;
return *this;
}
RandomAccessSupplierIter operator--(int) {
RandomAccessSupplierIter tmp(this->supplier, current_idx);
--(*this);
return tmp;
}
RandomAccessSupplierIter &operator+=(difference_type n) {
current_idx += n;
return *this;
}
RandomAccessSupplierIter &operator-=(difference_type n) {
current_idx -= n;
return *this;
}
RandomAccessSupplierIter operator+(difference_type n) const {
return RandomAccessSupplierIter(this->supplier, current_idx + n);
}
RandomAccessSupplierIter operator-(difference_type n) const {
return RandomAccessSupplierIter(this->supplier, current_idx - n);
}
difference_type operator-(const RandomAccessSupplierIter &other) const {
return static_cast<difference_type>(current_idx) -
static_cast<difference_type>(other.current_idx);
}
friend RandomAccessSupplierIter operator+(
difference_type n, const RandomAccessSupplierIter &it) {
return RandomAccessSupplierIter(it.supplier, it.current_idx + n);
}
auto operator<=>(const RandomAccessSupplierIter &other) const {
return current_idx <=> other.current_idx;
}
bool operator==(const RandomAccessSupplierIter &other) const {
return this->supplier == other.supplier &&
this->current_idx == other.current_idx;
}
};
// \brief a lazy supplier from an SD file
class RDKIT_FILEPARSERS_EXPORT SDMolSupplier : public ForwardSDMolSupplier {
/*************************************************************************
* A lazy mol supplier from a SD file.
* - When new molecules are read using "next" their positions in the file are
*noted.
* - A call to the "length" will automatically parse the entire file and
*cache all the mol
* block positions
* - [] operator is used to access a molecule at "idx", calling next
*following this will result
* in the next molecule after "idx"
* - When new molecules are read using "next()" their positions in the file
* are stored.
* - A call to the "length()" will automatically parse the entire file and
* store all the mol block positions
* - [] operator is used to access a molecule at "idx", calling next()
* after this will result in the next molecule after "idx"
***********************************************************************************/
public:
using iterator = RandomAccessSupplierIter<SDMolSupplier>;
using reverse_iterator = std::reverse_iterator<iterator>;
SDMolSupplier() { init(); }
/*!
@@ -195,6 +393,8 @@ class RDKIT_FILEPARSERS_EXPORT SDMolSupplier : public ForwardSDMolSupplier {
bool atEnd() override;
void moveTo(unsigned int idx);
std::unique_ptr<RWMol> operator[](unsigned int idx);
///! \brief returns a shared pointer to the molecule at the given index.
std::shared_ptr<RWMol> getShared(unsigned int idx);
/*! \brief returns the text block for a particular item
*
* \param idx - which item to return
@@ -218,14 +418,33 @@ class RDKIT_FILEPARSERS_EXPORT SDMolSupplier : public ForwardSDMolSupplier {
*/
void setStreamIndices(const std::vector<std::streampos> &locs);
iterator begin() { return RandomAccessSupplierIter(this); }
iterator end() { return RandomAccessSupplierIter(this, length()); }
reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
void setCaching(bool val) { d_cacheMolecules = val; }
bool getCaching() const { return d_cacheMolecules; }
private:
void checkForEnd() override;
void peekCheckForEnd(char* bufPtr, char* bufEnd, std::streampos molStartPos);
void peekCheckForEnd(char *bufPtr, char *bufEnd, std::streampos molStartPos);
void buildIndexTo(unsigned int targetIdx);
int d_len = 0; // total number of mol blocks in the file (initialized to -1)
int d_last = 0; // the molecule we are ready to read
std::vector<std::streampos> d_molpos;
bool d_cacheMolecules = false;
std::vector<std::optional<std::shared_ptr<RWMol>>> d_molCache;
#ifdef RDK_BUILD_THREADSAFE_SSS
std::mutex d_readMutex;
std::mutex d_cacheMutex;
#endif
};
// clang-format off
static_assert(
std::ranges::random_access_range<SDMolSupplier>
);
// clang-format on
struct SmilesMolSupplierParams {
std::string delimiter = " \t";
@@ -249,34 +468,21 @@ class RDKIT_FILEPARSERS_EXPORT SmilesMolSupplier : public MolSupplier {
/**************************************************************************
* Lazy file parser for Smiles table file, similar to the lazy SD
* file parser above
* - As an when new molecules are read using "next" their
* positions in the file are noted.
* - A call to the "length" will automatically parse the entire
* file and cache all the mol block positions
* - When new molecules are read using "next()" their
* positions in the file are stored.
* - A call to "length()" will automatically parse the entire
* file and store all the mol block positions
* - [] operator is used to access a molecule at "idx", calling
* next following this will result in the next molecule after
* next() following this will result in the next molecule after
* "idx"
***************************************************************************/
public:
using iterator = RandomAccessSupplierIter<SmilesMolSupplier>;
using reverse_iterator = std::reverse_iterator<iterator>;
/*!
* \param fileName - the name of smiles table file
* \param delimiter - delimiting characters between records on a each
* line NOTE that this is not a string, the tokenizer looks for
* the individual characters in delimiter, not the full string
* itself. So the default delimiter: " \t", means " " or "\t".
* \param smilesColumn - column number for the SMILES string (defaults
* to the first column)
* \param nameColumn - column number for the molecule name (defaults to
* the second column) If set to -1 we assume that no name is
* available for the molecule and the name is defaulted to the
* smiles string
* \param titleLine - if true, the first line is assumed to list the
* names of properties in order separated by 'delimiter'. It is
* also assume that the 'SMILES' column and the 'name' column
* are not specified here if false - no title line is assumed
* and the properties are recorded as the "columnX" where "X" is
* the column number
* \param sanitize - if true sanitize the molecule before returning it
* \param params - SmilesMolSupplierParams object controlling how
* the file itself and the individual SMILES are parsed.
*/
explicit SmilesMolSupplier(
const std::string &fileName,
@@ -295,6 +501,7 @@ class RDKIT_FILEPARSERS_EXPORT SmilesMolSupplier : public MolSupplier {
bool atEnd() override;
void moveTo(unsigned int idx);
std::unique_ptr<RWMol> operator[](unsigned int idx);
std::shared_ptr<RWMol> getShared(unsigned int idx);
/*! \brief returns the text block for a particular item
*
* \param idx - which item to return
@@ -302,6 +509,14 @@ class RDKIT_FILEPARSERS_EXPORT SmilesMolSupplier : public MolSupplier {
std::string getItemText(unsigned int idx);
unsigned int length();
iterator begin() { return iterator(this); }
iterator end() { return iterator(this, length()); }
reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
void setCaching(bool val) { d_cacheMolecules = val; }
bool getCaching() const { return d_cacheMolecules; }
private:
std::unique_ptr<RWMol> processLine(std::string inLine);
void processTitleLine();
@@ -318,6 +533,12 @@ class RDKIT_FILEPARSERS_EXPORT SmilesMolSupplier : public MolSupplier {
d_molpos; // vector of positions in the file for molecules
std::vector<int> d_lineNums;
STR_VECT d_props; // vector of property names
bool d_cacheMolecules = false;
std::vector<std::optional<std::shared_ptr<RWMol>>> d_molCache;
#ifdef RDK_BUILD_THREADSAFE_SSS
std::mutex d_readMutex;
std::mutex d_cacheMutex;
#endif
};
struct TDTMolSupplierParams {
@@ -341,26 +562,22 @@ class RDKIT_FILEPARSERS_EXPORT TDTMolSupplier : public MolSupplier {
/**************************************************************************
* Lazy file parser for TDT files, similar to the lazy SD
* file parser above
* - As an when new molecules are read using "next" their
* - When new molecules are read using "next()" their
* positions in the file are noted.
* - A call to the "length" will automatically parse the entire
* file and cache all the mol block positions
* - A call to "length()" will automatically parse the entire
* file and store all the mol block positions
* - [] operator is used to access a molecule at "idx", calling
* next following this will result in the next molecule after
* next() following this will result in the next molecule after
* "idx"
***************************************************************************/
public:
using iterator = RandomAccessSupplierIter<TDTMolSupplier>;
using reverse_iterator = std::reverse_iterator<iterator>;
/*!
* \param fileName - the name of the TDT file
* \param nameRecord - property name for the molecule name.
* If empty (the default), the name defaults to be empty
* \param confId2D - if >=0 and 2D coordinates are provided, the 2D
* structure (depiction) in the input will be read into the
* corresponding conformer id.
* \param confId3D - if >=0 and 3D coordinates are provided, the 3D
* structure (depiction) in the input will be read into the
* corresponding conformer id.
* \param sanitize - if true sanitize the molecule before returning it
* \param params - TDTMolSupplierParams object controlling how the file
* itself and the individual records are parsed.
*/
explicit TDTMolSupplier(
const std::string &fileName,
@@ -378,6 +595,8 @@ class RDKIT_FILEPARSERS_EXPORT TDTMolSupplier : public MolSupplier {
bool atEnd() override;
void moveTo(unsigned int idx);
std::unique_ptr<RWMol> operator[](unsigned int idx);
std::shared_ptr<RWMol> getShared(unsigned int idx);
/*! \brief returns the text block for a particular item
*
* \param idx - which item to return
@@ -385,6 +604,14 @@ class RDKIT_FILEPARSERS_EXPORT TDTMolSupplier : public MolSupplier {
std::string getItemText(unsigned int idx);
unsigned int length();
iterator begin() { return iterator(this); }
iterator end() { return iterator(this, length()); }
reverse_iterator rbegin() { return reverse_iterator(end()); }
reverse_iterator rend() { return reverse_iterator(begin()); }
void setCaching(bool val) { d_cacheMolecules = val; }
bool getCaching() const { return d_cacheMolecules; }
private:
bool advanceToNextRecord();
void checkForEnd();
@@ -397,6 +624,12 @@ class RDKIT_FILEPARSERS_EXPORT TDTMolSupplier : public MolSupplier {
std::vector<std::streampos>
d_molpos; // vector of positions in the file for molecules
TDTMolSupplierParams d_params;
bool d_cacheMolecules = false;
std::vector<std::optional<std::shared_ptr<RWMol>>> d_molCache;
#ifdef RDK_BUILD_THREADSAFE_SSS
std::mutex d_readMutex;
std::mutex d_cacheMutex;
#endif
};
#ifdef RDK_BUILD_MAEPARSER_SUPPORT
@@ -435,6 +668,7 @@ class RDKIT_FILEPARSERS_EXPORT MaeMolSupplier : public MolSupplier {
bool atEnd() override;
void moveTo(unsigned int idx);
std::unique_ptr<RWMol> operator[](unsigned int idx);
unsigned int length();
void close() override { dp_sInStream.reset(); }

View File

@@ -62,6 +62,10 @@ void SDMolSupplier::init() {
ForwardSDMolSupplier::init();
d_len = -1;
d_last = 0;
#ifdef RDK_BUILD_THREADSAFE_SSS
const std::lock_guard<std::mutex> guard(d_cacheMutex);
#endif
d_molCache.clear();
}
void SDMolSupplier::setData(const std::string &text) {
@@ -195,6 +199,9 @@ std::unique_ptr<RWMol> SDMolSupplier::next() {
std::string SDMolSupplier::getItemText(unsigned int idx) {
PRECONDITION(dp_inStream, "no stream");
#ifdef RDK_BUILD_THREADSAFE_SSS
const std::lock_guard<std::mutex> guard(d_readMutex);
#endif
unsigned int holder = d_last;
moveTo(idx);
std::streampos begP = d_molpos[idx];
@@ -218,7 +225,6 @@ std::string SDMolSupplier::getItemText(unsigned int idx) {
void SDMolSupplier::moveTo(unsigned int idx) {
PRECONDITION(dp_inStream, "no stream");
// dp_inStream->seekg() is called for all idx values
// and earlier calls to next() may have put the stream into a bad state
dp_inStream->clear();
@@ -256,9 +262,43 @@ void SDMolSupplier::moveTo(unsigned int idx) {
std::unique_ptr<RWMol> SDMolSupplier::operator[](unsigned int idx) {
PRECONDITION(dp_inStream, "no stream");
#ifdef RDK_BUILD_THREADSAFE_SSS
const std::lock_guard<std::mutex> guard(d_readMutex);
#endif
// std::cerr << "get molecule with index " << idx << std::endl;
// get the molecule with index idx
moveTo(idx);
return next();
auto res = next();
return res;
}
std::shared_ptr<RWMol> SDMolSupplier::getShared(unsigned int idx) {
PRECONDITION(dp_inStream, "no stream");
if (d_cacheMolecules) {
#ifdef RDK_BUILD_THREADSAFE_SSS
const std::lock_guard<std::mutex> guard(d_cacheMutex);
#endif
if (d_molCache.size() > idx && d_molCache[idx]) {
return d_molCache[idx].value();
}
}
std::shared_ptr<RWMol> res;
{
#ifdef RDK_BUILD_THREADSAFE_SSS
const std::lock_guard<std::mutex> guard(d_readMutex);
#endif
// get the molecule with index idx
moveTo(idx);
res.reset(next().release());
}
if (d_cacheMolecules) {
if (d_molCache.size() <= idx) {
constexpr unsigned int molCacheAllocChunkSize = 1000;
d_molCache.resize(idx + molCacheAllocChunkSize);
}
d_molCache[idx] = res;
}
return res;
}
unsigned int SDMolSupplier::length() {

View File

@@ -66,6 +66,10 @@ void SmilesMolSupplier::init() {
d_line = -1;
d_molpos.clear();
d_lineNums.clear();
#ifdef RDK_BUILD_THREADSAFE_SSS
const std::lock_guard<std::mutex> guard(d_cacheMutex);
#endif
d_molCache.clear();
}
void SmilesMolSupplier::setData(const std::string &text,
@@ -175,7 +179,7 @@ std::unique_ptr<RWMol> SmilesMolSupplier::processLine(std::string inLine) {
if (d_props.size() > col) {
pname = d_props[col];
}
if(pname.empty()){
if (pname.empty()) {
pname = "Column_";
pname += std::to_string(col);
}
@@ -454,6 +458,9 @@ std::unique_ptr<RWMol> SmilesMolSupplier::next() {
std::unique_ptr<RWMol> SmilesMolSupplier::operator[](unsigned int idx) {
PRECONDITION(dp_inStream, "no stream");
#ifdef RDK_BUILD_THREADSAFE_SSS
const std::lock_guard<std::mutex> guard(d_readMutex);
#endif
// ---------
// move to the appropriate location in the file:
// ---------
@@ -466,6 +473,37 @@ std::unique_ptr<RWMol> SmilesMolSupplier::operator[](unsigned int idx) {
return res;
}
std::shared_ptr<RWMol> SmilesMolSupplier::getShared(unsigned int idx) {
PRECONDITION(dp_inStream, "no stream");
if (d_cacheMolecules) {
#ifdef RDK_BUILD_THREADSAFE_SSS
const std::lock_guard<std::mutex> guard(d_cacheMutex);
#endif
if (d_molCache.size() > idx && d_molCache[idx]) {
return d_molCache[idx].value();
}
}
// get the molecule with index idx
std::shared_ptr<RWMol> res;
{
#ifdef RDK_BUILD_THREADSAFE_SSS
const std::lock_guard<std::mutex> guard(d_readMutex);
#endif
moveTo(idx);
res.reset(next().release());
}
if (d_cacheMolecules) {
#ifdef RDK_BUILD_THREADSAFE_SSS
const std::lock_guard<std::mutex> guard(d_cacheMutex);
#endif
if (d_molCache.size() <= idx) {
constexpr unsigned int molCacheAllocChunkSize = 1000;
d_molCache.resize(idx + molCacheAllocChunkSize);
}
d_molCache[idx] = res;
}
return res;
}
// ----------------------------------------------------------------------
//

View File

@@ -104,6 +104,11 @@ void TDTMolSupplier::init() {
d_len = -1;
d_last = 0;
d_line = 0;
d_molpos.clear();
#ifdef RDK_BUILD_THREADSAFE_SSS
const std::lock_guard<std::mutex> guard(d_cacheMutex);
#endif
d_molCache.clear();
}
void TDTMolSupplier::setData(const std::string &text,
@@ -311,6 +316,9 @@ std::unique_ptr<RWMol> TDTMolSupplier::next() {
std::string TDTMolSupplier::getItemText(unsigned int idx) {
PRECONDITION(dp_inStream, "no stream");
#ifdef RDK_BUILD_THREADSAFE_SSS
const std::lock_guard<std::mutex> guard(d_readMutex);
#endif
unsigned int holder = d_last;
moveTo(idx);
std::streampos begP = d_molpos[idx];
@@ -372,11 +380,43 @@ void TDTMolSupplier::moveTo(unsigned int idx) {
std::unique_ptr<RWMol> TDTMolSupplier::operator[](unsigned int idx) {
PRECONDITION(dp_inStream, "no stream");
// get the molecule with index idx
#ifdef RDK_BUILD_THREADSAFE_SSS
const std::lock_guard<std::mutex> guard(d_readMutex);
#endif
moveTo(idx);
return next();
}
std::shared_ptr<RWMol> TDTMolSupplier::getShared(unsigned int idx) {
PRECONDITION(dp_inStream, "no stream");
if (d_cacheMolecules) {
#ifdef RDK_BUILD_THREADSAFE_SSS
const std::lock_guard<std::mutex> guard(d_cacheMutex);
#endif
if (d_molCache.size() > idx && d_molCache[idx]) {
return d_molCache[idx].value();
}
}
// get the molecule with index idx
std::shared_ptr<RWMol> res;
{
#ifdef RDK_BUILD_THREADSAFE_SSS
const std::lock_guard<std::mutex> guard(d_readMutex);
#endif
moveTo(idx);
res.reset(next().release());
}
if (d_cacheMolecules) {
#ifdef RDK_BUILD_THREADSAFE_SSS
const std::lock_guard<std::mutex> guard(d_cacheMutex);
#endif
if (d_molCache.size() <= idx) {
constexpr unsigned int molCacheAllocChunkSize = 1000;
d_molCache.resize(idx + molCacheAllocChunkSize);
}
d_molCache[idx] = res;
}
return res;
}
unsigned int TDTMolSupplier::length() {
PRECONDITION(dp_inStream, "no stream");
// return the number of mol blocks in the sdfile

View File

@@ -0,0 +1,506 @@
//
// Copyright (C) 2026 Greg Landrum and other RDKit contributors
//
// @@ All Rights Reserved @@
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
#include <catch2/catch_all.hpp>
#include <vector>
#include <algorithm>
#include <execution>
#include <ranges>
#include <iostream>
#include <filesystem>
#include <GraphMol/RDKitBase.h>
#include "MolSupplier.h"
#include <GraphMol/SmilesParse/SmilesWrite.h>
#include <GraphMol/SmilesParse/SmilesParse.h>
#include <GraphMol/Substruct/SubstructMatch.h>
static const std::string rdbase = getenv("RDBASE");
using namespace RDKit;
namespace {
unsigned int countAtoms(const std::shared_ptr<RWMol> &mol) {
REQUIRE(mol);
return mol->getNumAtoms();
}
size_t molPtr(const std::shared_ptr<RWMol> &mol) { return (size_t)mol.get(); }
} // namespace
template <typename Supplier>
void iterTest(Supplier &reader, size_t len) {
CHECK(reader.length() == len);
std::vector<unsigned int> expected;
expected.reserve(reader.length());
for (unsigned int i = 0; i < reader.length(); ++i) {
expected.push_back(reader[i]->getNumAtoms());
}
std::vector<unsigned int> actual;
std::ranges::transform(reader, std::back_inserter(actual), countAtoms);
CHECK(actual == expected);
}
template <typename Supplier>
void forwardIterTest(Supplier &reader, size_t len) {
std::vector<unsigned int> actual;
std::ranges::transform(reader, std::back_inserter(actual), countAtoms);
CHECK(actual.size() == len);
}
template <typename Supplier>
void cacheTest(Supplier &reader, size_t len) {
reader.setCaching(true);
CHECK(reader.length() == len);
std::vector<std::shared_ptr<RWMol>> mols(reader.length());
std::copy(reader.begin(), reader.end(), mols.begin());
REQUIRE(mols.size() == reader.length());
std::vector<size_t> expected;
std::ranges::transform(mols, std::back_inserter(expected), molPtr);
std::vector<size_t> actual;
std::ranges::transform(reader, std::back_inserter(actual), molPtr);
CHECK(actual == expected);
}
TEST_CASE("basic SDMolSupplier iteration") {
std::string infile =
rdbase + "/Code/GraphMol/FileParsers/test_data/NCI_aids_few.sdf";
v2::FileParsers::SDMolSupplier reader(infile);
SECTION("basics") { iterTest(reader, 16); }
SECTION("with caching") { cacheTest(reader, 16); }
SECTION("reverse iteration") {
std::vector<unsigned int> expected;
std::ranges::transform(std::begin(reader), std::end(reader),
std::back_inserter(expected), countAtoms);
std::vector<unsigned int> actual;
std::ranges::transform(std::rbegin(reader), std::rend(reader),
std::back_inserter(actual), countAtoms);
std::ranges::reverse(actual);
CHECK(actual == expected);
}
}
TEST_CASE("ForwardSDMolSupplier iteration") {
std::string infile =
rdbase + "/Code/GraphMol/FileParsers/test_data/NCI_aids_few.sdf";
std::ifstream strm(infile);
bool takeOwnership = false;
v2::FileParsers::ForwardSDMolSupplier reader(&strm, takeOwnership);
SECTION("basics") { forwardIterTest(reader, 16); }
SECTION("error handling") {
reader.next();
CHECK_THROWS_AS(reader.begin(), ValueErrorException);
}
SECTION("pre-increment") {
unsigned int i = 0;
auto it = reader.begin();
auto end = reader.end();
while (it != end) {
auto mol = *it;
REQUIRE(mol);
++it;
++i;
}
CHECK(i == 16);
}
SECTION("post-increment") {
unsigned int i = 0;
auto it = reader.begin();
auto end = reader.end();
while (it != end) {
auto mol = *it;
REQUIRE(mol);
it++;
++i;
}
CHECK(i == 16);
}
}
TEST_CASE("ForwardSDMolSupplier iteration with failing molecules") {
std::string infile =
rdbase + "/Code/GraphMol/FileParsers/test_data/good_bad_good_good.sdf";
std::ifstream strm(infile);
bool takeOwnership = false;
v2::FileParsers::ForwardSDMolSupplier reader(&strm, takeOwnership);
SECTION("basics") {
std::vector<unsigned int> expected{6, 0, 6, 6};
std::vector<unsigned int> actual;
std::ranges::transform(
reader, std::back_inserter(actual),
[](const auto &mol) { return mol ? mol->getNumAtoms() : 0; });
CHECK(actual == expected);
}
SECTION("pre-increment") {
unsigned int i = 0;
auto it = reader.begin();
auto end = reader.end();
while (it != end) {
auto mol = *it;
if (i == 1) {
CHECK(!mol);
} else {
CHECK(mol);
}
++it;
++i;
}
CHECK(i == 4);
}
SECTION("post-increment") {
unsigned int i = 0;
auto it = reader.begin();
auto end = reader.end();
while (it != end) {
auto mol = *it;
if (i == 1) {
CHECK(!mol);
} else {
CHECK(mol);
}
it++;
++i;
}
CHECK(i == 4);
}
}
TEST_CASE("ForwardSDMolSupplier iteration with failing molecule at end") {
std::string infile =
rdbase + "/Code/GraphMol/FileParsers/test_data/good_bad_good_bad.sdf";
std::ifstream strm(infile);
bool takeOwnership = false;
v2::FileParsers::ForwardSDMolSupplier reader(&strm, takeOwnership);
SECTION("basics") {
std::vector<unsigned int> expected{6, 0, 6, 0};
std::vector<unsigned int> actual;
std::ranges::transform(
reader, std::back_inserter(actual),
[](const auto &mol) { return mol ? mol->getNumAtoms() : 0; });
CHECK(actual == expected);
}
SECTION("pre-increment") {
unsigned int i = 0;
auto it = reader.begin();
auto end = reader.end();
while (it != end) {
auto mol = *it;
if (i % 2) {
CHECK(!mol);
} else {
CHECK(mol);
}
++it;
++i;
}
CHECK(i == 4);
}
SECTION("post-increment") {
unsigned int i = 0;
auto it = reader.begin();
auto end = reader.end();
while (it != end) {
auto mol = *it;
if (i % 2) {
CHECK(!mol);
} else {
CHECK(mol);
}
it++;
++i;
}
CHECK(i == 4);
}
}
TEST_CASE("cached SDMolSupplier error handling") {
std::string infile =
rdbase + "/Code/GraphMol/FileParsers/test_data/good_bad_good_bad.sdf";
SECTION("basics") {
v2::FileParsers::SDMolSupplier reader(infile);
reader.setCaching(true);
CHECK(reader.length() == 4);
std::vector<std::shared_ptr<RWMol>> mols(reader.length());
std::copy(reader.begin(), reader.end(), mols.begin());
REQUIRE(mols.size() == reader.length());
CHECK(mols[0]);
CHECK(!mols[1]);
CHECK(mols[2]);
CHECK(!mols[3]);
std::vector<size_t> expected;
std::ranges::transform(mols, std::back_inserter(expected), molPtr);
// now use the cached versions:
std::vector<std::shared_ptr<RWMol>> nmols(reader.length());
std::copy(reader.begin(), reader.end(), nmols.begin());
REQUIRE(nmols.size() == reader.length());
CHECK(nmols[0]);
CHECK(!nmols[1]);
CHECK(nmols[2]);
CHECK(!nmols[3]);
// confirm the caching
std::vector<size_t> actual;
std::ranges::transform(nmols, std::back_inserter(actual), molPtr);
CHECK(actual == expected);
}
}
TEST_CASE("basic SmilesMolSupplier iteration") {
std::string infile =
rdbase + "/Code/GraphMol/FileParsers/test_data/first_200.tpsa.csv";
v2::FileParsers::SmilesMolSupplierParams params;
params.delimiter = ',';
params.smilesColumn = 0;
params.nameColumn = -1;
v2::FileParsers::SmilesMolSupplier reader(infile, params);
SECTION("basics") { iterTest(reader, 200); }
SECTION("with caching") { cacheTest(reader, 200); }
SECTION("reverse iteration") {
std::vector<unsigned int> expected;
std::ranges::transform(std::begin(reader), std::end(reader),
std::back_inserter(expected), countAtoms);
std::vector<unsigned int> actual;
std::ranges::transform(std::rbegin(reader), std::rend(reader),
std::back_inserter(actual), countAtoms);
std::ranges::reverse(actual);
CHECK(actual == expected);
}
}
TEST_CASE("cached SmilesMolSupplier error handling") {
v2::FileParsers::SmilesMolSupplierParams params;
params.delimiter = ',';
params.smilesColumn = 0;
params.nameColumn = -1;
std::string data = R"SMI(smiles,is_valid
CCO,1
CFC,0
CCN,1
c1cc1,0
c1cc,0
)SMI";
SECTION("basics") {
v2::FileParsers::SmilesMolSupplier reader;
reader.setData(data, params);
reader.setCaching(true);
CHECK(reader.length() == 5);
std::vector<std::shared_ptr<RWMol>> mols(reader.length());
std::copy(reader.begin(), reader.end(), mols.begin());
REQUIRE(mols.size() == reader.length());
CHECK(mols[0]);
CHECK(!mols[1]);
CHECK(mols[2]);
CHECK(!mols[3]);
CHECK(!mols[4]);
std::vector<size_t> expected;
std::ranges::transform(mols, std::back_inserter(expected), molPtr);
// now use the cached versions:
std::vector<std::shared_ptr<RWMol>> nmols(reader.length());
std::copy(reader.begin(), reader.end(), nmols.begin());
REQUIRE(nmols.size() == reader.length());
CHECK(nmols[0]);
CHECK(!nmols[1]);
CHECK(nmols[2]);
CHECK(!nmols[3]);
CHECK(!nmols[4]);
// confirm the caching
std::vector<size_t> actual;
std::ranges::transform(nmols, std::back_inserter(actual), molPtr);
CHECK(actual == expected);
}
}
#if defined(RDK_BUILD_THREADSAFE_SSS) && defined(__cpp_lib_execution)
// NOTE: will only run in parallel on linux if TBB is installed
TEST_CASE("parallel reads") {
// there's likely no benefit from the parallelization here,
// but we want to be sure that the mutexes are working correctly
auto *rdbase = std::getenv("RDBASE");
REQUIRE(rdbase);
SECTION("sdf") {
auto path = std::filesystem::path(rdbase) /
"Code/GraphMol/FileParsers/test_data/zinc.leads.500.q.sdf";
REQUIRE(std::filesystem::exists(path));
v2::FileParsers::SDMolSupplier reader1(path.string());
std::vector<unsigned int> nAts1(reader1.length());
std::transform(reader1.begin(), reader1.end(), nAts1.begin(), countAtoms);
// std::sort(nAts1.begin(), nAts1.end());
auto start = std::chrono::high_resolution_clock::now();
constexpr unsigned int numIters = 20;
for (unsigned int iter = 0; iter < numIters; ++iter) {
v2::FileParsers::SDMolSupplier reader2(path.string());
reader2.setCaching(true);
std::vector<unsigned int> nAts2(reader1.length());
std::transform(std::execution::par, reader2.begin(), reader2.end(),
nAts2.begin(), countAtoms);
REQUIRE(nAts1.size() == nAts2.size());
REQUIRE(nAts1 == nAts2);
}
auto end = std::chrono::high_resolution_clock::now();
auto duration =
std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cerr << "Read of " << reader1.length() << "x" << numIters
<< " molecules took " << duration.count() << " ms" << std::endl;
}
SECTION("smiles") {
auto path = std::filesystem::path(rdbase) /
"Code/GraphMol/FileParsers/test_data/zinc.leads.500.q.smi";
REQUIRE(std::filesystem::exists(path));
v2::FileParsers::SmilesMolSupplierParams params;
params.delimiter = '\t';
params.smilesColumn = 0;
params.nameColumn = 1;
params.titleLine = false;
v2::FileParsers::SmilesMolSupplier reader1(path.string(), params);
std::vector<unsigned int> nAts1(reader1.length());
std::transform(reader1.begin(), reader1.end(), nAts1.begin(), countAtoms);
auto start = std::chrono::high_resolution_clock::now();
constexpr unsigned int numIters = 20;
for (unsigned int iter = 0; iter < numIters; ++iter) {
v2::FileParsers::SmilesMolSupplier reader2(path.string(), params);
reader2.setCaching(true);
std::vector<unsigned int> nAts2(reader1.length());
std::transform(std::execution::par, reader2.begin(), reader2.end(),
nAts2.begin(), countAtoms);
REQUIRE(nAts1 == nAts2);
}
auto end = std::chrono::high_resolution_clock::now();
auto duration =
std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cerr << "Read of " << reader1.length() << "x" << numIters
<< " molecules took " << duration.count() << " ms" << std::endl;
}
SECTION("TDT") {
auto path = std::filesystem::path(rdbase) /
"Code/GraphMol/FileParsers/test_data/zinc.leads.500.q.tdt";
REQUIRE(std::filesystem::exists(path));
v2::FileParsers::TDTMolSupplier reader1(path.string());
std::vector<unsigned int> nAts1(reader1.length());
std::transform(reader1.begin(), reader1.end(), nAts1.begin(), countAtoms);
auto start = std::chrono::high_resolution_clock::now();
constexpr unsigned int numIters = 20;
for (unsigned int iter = 0; iter < numIters; ++iter) {
v2::FileParsers::TDTMolSupplier reader2(path.string());
reader2.setCaching(true);
std::vector<unsigned int> nAts2(reader1.length());
std::transform(std::execution::par, reader2.begin(), reader2.end(),
nAts2.begin(), countAtoms);
REQUIRE(nAts1 == nAts2);
}
auto end = std::chrono::high_resolution_clock::now();
auto duration =
std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cerr << "Read of " << reader1.length() << "x" << numIters
<< " molecules took " << duration.count() << " ms" << std::endl;
}
}
TEST_CASE("benchmarking") {
auto *rdbase = std::getenv("RDBASE");
REQUIRE(rdbase);
auto path = std::filesystem::path(rdbase) /
"Code/GraphMol/FileParsers/test_data/zinc.leads.500.q.smi";
REQUIRE(std::filesystem::exists(path));
v2::FileParsers::SmilesMolSupplierParams params;
params.delimiter = '\t';
params.smilesColumn = 0;
params.nameColumn = 1;
params.titleLine = false;
v2::FileParsers::SmilesMolSupplier reader(path.string(), params);
reader.setCaching(true);
SECTION("transform") {
auto start = std::chrono::high_resolution_clock::now();
// prime the cache:
std::vector<unsigned int> nAts1;
std::transform(reader.begin(), reader.end(), std::back_inserter(nAts1),
countAtoms);
double accum = 0.0;
constexpr unsigned int numIters = 200;
for (unsigned int iter = 0; iter < numIters; ++iter) {
std::vector<unsigned int> nAts(reader.length());
std::transform(std::execution::seq, reader.begin(), reader.end(),
nAts.begin(),
[](const auto mol) { return MolToSmiles(*mol).size(); });
accum += nAts.size();
}
auto end = std::chrono::high_resolution_clock::now();
auto duration =
std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cerr << "Base transform of " << reader.length() << "x" << numIters
<< " molecules took " << duration.count() << " ms" << std::endl;
CHECK(accum > 0);
accum = 0.0;
start = std::chrono::high_resolution_clock::now();
for (unsigned int iter = 0; iter < numIters; ++iter) {
std::vector<unsigned int> nAts(reader.length());
std::transform(std::execution::par, reader.begin(), reader.end(),
nAts.begin(),
[](const auto mol) { return MolToSmiles(*mol).size(); });
accum += nAts.size();
}
end = std::chrono::high_resolution_clock::now();
duration =
std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
std::cerr << "Parallel transform of " << reader.length() << "x" << numIters
<< " molecules took " << duration.count() << " ms" << std::endl;
CHECK(accum > 0);
}
}
#endif
TEST_CASE("views and filtered reads") {
auto *rdbase = std::getenv("RDBASE");
REQUIRE(rdbase);
auto path = std::filesystem::path(rdbase) /
"Code/GraphMol/FileParsers/test_data/zinc.leads.500.q.sdf";
REQUIRE(std::filesystem::exists(path));
v2::FileParsers::SDMolSupplier reader1(path.string());
SECTION("filters") {
std::vector<unsigned int> nAts1(reader1.length());
std::transform(reader1.begin(), reader1.end(), nAts1.begin(), countAtoms);
constexpr unsigned int tgtSize = 15;
auto nSmall =
std::ranges::count_if(nAts1, [](const auto &v) { return v < tgtSize; });
REQUIRE(nSmall > 0);
auto filtered = reader1 | std::views::filter([](const auto &mol) {
return mol->getNumAtoms() < tgtSize;
});
CHECK(std::distance(std::begin(filtered), std::end(filtered)) == nSmall);
// only read until we have a set number of molecules matching our filter:
const unsigned int subsetSz = nSmall / 4;
auto firstN = reader1 | std::views::filter([](const auto &mol) {
return mol->getNumAtoms() < tgtSize;
}) |
std::views::take(subsetSz);
// this doen't work and I don't understand why:
// CHECK(std::distance(std::begin(firstN), std::end(firstN)) == subsetSz);
std::vector<std::shared_ptr<RWMol>> mols;
std::ranges::copy(firstN, std::back_inserter(mols));
CHECK(mols.size() == subsetSz);
}
SECTION("substructure filter") {
auto query = "c1ncncn1"_smiles;
SubstructMatchParameters params;
params.maxMatches = 1;
auto firstN = reader1 |
std::views::filter([&query, &params](const auto &mol) {
return SubstructMatch(*mol, *query, params).empty();
}) |
std::views::take(5);
std::vector<std::shared_ptr<RWMol>> mols;
std::ranges::copy(firstN, std::back_inserter(mols));
CHECK(mols.size() == 5);
}
}

View File

@@ -0,0 +1,80 @@
OK
csChFnd70/05230312262D
6 5 0 0 0 0 0 0 0 0999 V2000
1.2124 0.0000 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0
2.4249 0.7000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
3.6373 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
2.4249 2.1000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
0.0000 0.7000 0.0000 Y 0 0 0 0 0 0 0 0 0 0 0 0
4.8477 0.6988 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1 2 1 0 0 0 0
2 3 1 0 0 0 0
2 4 2 0 0 0 0
3 5 1 0 0 0 0
3 6 1 0 0 0 0
M END
> <ID>
good1
$$$$
Sanitization Error
csChFnd70/05230312262D
6 5 0 0 0 0 0 0 0 0999 V2000
1.2124 0.0000 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0
2.4249 0.7000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
3.6373 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
2.4249 2.1000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
0.0000 0.7000 0.0000 Y 0 0 0 0 0 0 0 0 0 0 0 0
4.8477 0.6988 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1 2 1 0 0 0 0
2 3 1 0 0 0 0
2 4 2 0 0 0 0
1 5 1 0 0 0 0
3 6 1 0 0 0 0
M END
> <ID>
bad1
$$$$
OK
csChFnd70/05230312262D
6 5 0 0 0 0 0 0 0 0999 V2000
1.2124 0.0000 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0
2.4249 0.7000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
3.6373 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
2.4249 2.1000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
0.0000 0.7000 0.0000 Y 0 0 0 0 0 0 0 0 0 0 0 0
4.8477 0.6988 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1 2 1 0 0 0 0
2 3 1 0 0 0 0
2 4 2 0 0 0 0
3 5 1 0 0 0 0
3 6 1 0 0 0 0
M END
> <ID>
good2
$$$$
too many bonds
csChFnd70/05230312262D
6 6 0 0 0 0 0 0 0 0999 V2000
1.2124 0.0000 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0
2.4249 0.7000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
3.6373 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
2.4249 2.1000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
0.0000 0.7000 0.0000 Y 0 0 0 0 0 0 0 0 0 0 0 0
4.8477 0.6988 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1 2 1 0 0 0 0
2 3 1 0 0 0 0
2 4 2 0 0 0 0
3 5 1 0 0 0 0
3 6 1 0 0 0 0
M END
> <ID> (4)
Lig2
$$$$

View File

@@ -0,0 +1,80 @@
OK
csChFnd70/05230312262D
6 5 0 0 0 0 0 0 0 0999 V2000
1.2124 0.0000 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0
2.4249 0.7000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
3.6373 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
2.4249 2.1000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
0.0000 0.7000 0.0000 Y 0 0 0 0 0 0 0 0 0 0 0 0
4.8477 0.6988 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1 2 1 0 0 0 0
2 3 1 0 0 0 0
2 4 2 0 0 0 0
3 5 1 0 0 0 0
3 6 1 0 0 0 0
M END
> <ID>
good1
$$$$
Sanitization Error
csChFnd70/05230312262D
6 5 0 0 0 0 0 0 0 0999 V2000
1.2124 0.0000 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0
2.4249 0.7000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
3.6373 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
2.4249 2.1000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
0.0000 0.7000 0.0000 Y 0 0 0 0 0 0 0 0 0 0 0 0
4.8477 0.6988 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1 2 1 0 0 0 0
2 3 1 0 0 0 0
2 4 2 0 0 0 0
1 5 1 0 0 0 0
3 6 1 0 0 0 0
M END
> <ID>
bad2
$$$$
OK
csChFnd70/05230312262D
6 5 0 0 0 0 0 0 0 0999 V2000
1.2124 0.0000 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0
2.4249 0.7000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
3.6373 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
2.4249 2.1000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
0.0000 0.7000 0.0000 Y 0 0 0 0 0 0 0 0 0 0 0 0
4.8477 0.6988 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1 2 1 0 0 0 0
2 3 1 0 0 0 0
2 4 2 0 0 0 0
3 5 1 0 0 0 0
3 6 1 0 0 0 0
M END
> <ID>
good3
$$$$
OK
csChFnd70/05230312262D
6 5 0 0 0 0 0 0 0 0999 V2000
1.2124 0.0000 0.0000 F 0 0 0 0 0 0 0 0 0 0 0 0
2.4249 0.7000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
3.6373 0.0000 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
2.4249 2.1000 0.0000 O 0 0 0 0 0 0 0 0 0 0 0 0
0.0000 0.7000 0.0000 Y 0 0 0 0 0 0 0 0 0 0 0 0
4.8477 0.6988 0.0000 C 0 0 0 0 0 0 0 0 0 0 0 0
1 2 1 0 0 0 0
2 3 1 0 0 0 0
2 4 2 0 0 0 0
3 5 1 0 0 0 0
3 6 1 0 0 0 0
M END
> <ID> (4)
good4
$$$$

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,500 @@
C1CCO[C@@H](C1)ON2C(=O)CCC2=O ZINC00403244
c1ccc(cc1)Cn2c(c(nn2)c3[nH]c4ccccc4n3)N ZINC00031867
C1C(=O)[C@@H]2[C@@H](C=CS2)S(=O)(=O)N1 ZINC30714392
C[C@@H]1Cn2c(c(c(=O)[nH]c2=O)c3ccccc3)O1 ZINC02028360
C[C@@H](c1nncn1C)NC(=O)Nc2cccc(c2)Cl ZINC32934674
C1C[C@@H]([NH2+]C1)n2[nH]c(=S)nn2 ZINC16952145
c1cc(ccc1C2=NCC=CCO2)[N+](=O)[O-] ZINC01717366
Cc1cc(nc(n1)SC)OCc2n[nH]c(=S)o2 ZINC05537581
CCN(CC)C1=Nc2ccccc2-n3ccnc3C1 ZINC13214820
C[C@@H]1CCCC[NH+]1Cc2nnnn2Nc3cccc(c3)[N+](=O)[O-] ZINC00804824
Cc1cc(nc(n1)n2c3c(cn2)C(=O)CCC3)C ZINC02351349
c1cc(oc1)C(=O)Nc2csnn2 ZINC01680566
Cn1c(n[nH]c1=S)Cc2c3ccccc3c(=O)n(n2)C ZINC15919724
CN(c1ccccc1)/C(=N/NC(=O)[C@H]2CCC(=O)N2)/S ZINC05549721
[H]/N=C/1\\CC2(CC[NH+](CC2)C)c3c(nc([nH]3)COC)O1 ZINC20459262
c1ccc(cc1)c2c(=S)n(c(=O)[nH]n2)c3ccc(c(c3)Cl)F ZINC03846921
c1ccc2c(c1)CN(C(=[NH2+])C23CCOCC3)N ZINC01751973
Cc1ccccc1[C@@H]2[C@@H]([C@@]23C(=NN=C3O)N)C#N ZINC06525698
c1cc(ccc1c2ccc(o2)C=O)n3cnnn3 ZINC00336354
CC(=O)SC ZINC02004049
C/C(=N/Oc1cccc(n1)F)/c2ccccc2 ZINC19880407
c1ccc(cc1)N/C=C(\\C2=NCCCCC2)/[N+](=O)[O-] ZINC12338436
Cc1c(c(n[nH]1)O)n2c3ccccc3c(=O)c4c2cccc4 ZINC05261103
C1Cc2c(onc2c3c4c(on3)CCC4)C1 ZINC01588238
COC(=O)[C@@H]1CN(C(=O)N1)C(=O)CCc2ccccc2 ZINC05537267
c1ccc2c(c1)-c3c4c(cccc4n[nH]3)C2=O ZINC04335977
CC1=C(C[S@@](=O)N(C1)c2nc(sn2)SC)C ZINC04290546
CCc1c(sc(=[N+](C)C)s1)S ZINC14628732
Cc1csc(n1)CCNc2c(cccn2)C#N ZINC40071566
CC1([C@@H]2[C@@H](C[NH+]1C)[C@@H](NC(=[NH2+])N2)[NH3+])C ZINC16911528
CC1=C[n+]2ccccc2Sc3c1cccc3 ZINC01648649
CCn1c(=O)n-2c(n1)COc3c2cccc3 ZINC13362299
CC1(CC(=O)/C(=C/[C@@H]2CCN=C2S)/C(=O)N1)C ZINC05734401
CC1=CNC(=O)CC(C1)(C)C ZINC01582100
Cc1c(nc2ccccc2n1)NCc3[nH]ncn3 ZINC32593071
c1ccc2c(c1)C(=O)N(C(=CO2)Cl)CCCCCl ZINC22012349
C1CS[S@@](=O)O1 ZINC04692300
Cc1c(n2c(=O)n(c(=O)n2c1=O)c3ccccc3)C ZINC01587443
c1ccc(cc1)CC(=O)NN2[C@@H]([C@@H](C2=O)Cl)c3ccccc3 ZINC05838349
CCc1nc(on1)Cn2c3ccccc3ncc2=O ZINC12985807
CC1=C([NH2+]Cc2c1cccc2)/N=C(/NC(=O)c3ccccc3)\\[S-] ZINC00031959
C1CSC[C@@]12C(=O)NC(=O)N2 ZINC03434821
C[NH+](C)C=C1C=CC=C1 ZINC00967188
COc1c(cc2c-3c1COc4c3c(cc(c4)O)CC2)O ZINC31168289
c1ccc2c(c1)c(=O)c3=c(c2=O)sc(c(s3)C#N)C#N ZINC01755317
Cc1cc([n+]2c(n1)CCN2c3ccccc3)C ZINC03111478
C1[C@@H]2C=CC(=O)[C@H](O1)O2 ZINC00078169
Cc1[nH]c2c3ccccc3sc2n1 ZINC00241929
Cc1cnc(c2[n+]1c(nn2C)[N-]c3ccccc3)C ZINC00387689
Cn1c(ncn1)CCc2ccccc2 ZINC01633222
CC1([C@@]([N+](=[N+]1[O-])[O-])(c2ccccc2)Br)C ZINC13777563
Cc1c2c(con2)c(nn1)c3ccccn3 ZINC01403003
c1ccc2c(c1)cc(c(=[NH2+])o2)c3[nH]c4ccccc4n3 ZINC04721566
Cc1ccc(cc1)/N=C\\2/CNC(=O)NC2=O ZINC00185656
COn1c2ccccc2[n+](=O)c(c1[O-])c3ccco3 ZINC20476957
CC1(O[C@@H]2CCC(=O)[C@@H]2O1)C ZINC22014840
c1ccn2c(c1)c(c(=S)[nH]c2=O)C#N ZINC08133017
c1cc2c(c3c1non3)nns2 ZINC01279110
COc1ccc(cc1OC)N2C=NN3C2=NNc4c3[nH]c5c4cccc5 ZINC15226578
c1cc2c(cc1N)COCO2 ZINC04272152
c1cn(nc1C(=O)N)[C@@H]2[C@H]([C@@H]([C@@H](O2)CO)O)O ZINC05208104
Cc1ccc(cc1)/C=C\\2/C(=N[C@@H](C(=O)N2)C)O ZINC02087495
Cc1c(c(on1)c2c(nns2)C)C(=O)[O-] ZINC00158954
CCCC1=NN([C@@H]2[C@@H]1N=C(NC2=O)Cc3ccc(cc3)N)C ZINC21983240
CCOC(=O)c1c([nH]c(n1)[C@@H]2[C@H]([C@@H]([C@@H](O2)CO)O)O)O ZINC17142118
CC[C@@](c1ccncc1Br)(C2=NC(CO2)(C)C)O ZINC05010471
c1ccc(c(c1)/C=N\\[C@@H]2CONC2=O)O ZINC16980124
c1ccc(cc1)C(=O)C[n+]2c(scn2)N ZINC03122070
c1ccc(c(c1)/C=N/NC(=O)COC2=CCCC2)Cl ZINC00222804
c1cc(c(c(c1)Cl)c2csc(nc2=O)N)Cl ZINC08782861
Cc1cc2cc(ccc2[nH]1)O ZINC02571370
CC1=[N+]([C@@]2(CCc3c(non3)[C@@]2(C1)O)[NH3+])[O-] ZINC19325434
Cn1cc(cn1)c2ccnc(n2)N(C)Cc3nc(on3)C4CCC4 ZINC23506850
c1ccc2c(c1)C[C@@H]3C[C@H]2OC(=O)N3 ZINC01589294
CC1=C(N=C(C1(C)C)C)C ZINC13281730
c1ccc2=[NH+][C@@H]3C(=O)C=CC=C3C=c2c1 ZINC04517670
c1cc(sc1)[C@@H]2CC(=O)CS2 ZINC00149349
Cc1cc(n2c(n1)ncn2)OS(=O)(=O)c3ccc(cc3)NC(=O)C ZINC02057760
c1cc(cc(c1)[N+](=O)[O-])N2CNC=[NH+]N=C2 ZINC28294495
CC/N=C(/C(C#N)C#N)\\SCc1ccncc1 ZINC06380277
Cc1ccc2c(c1)[C@@H]3[C@@H]4CC[C@@H](C4)[C@@H]3[S@@](=O)N2 ZINC04512174
c1cnoc1c2nnc(o2)N ZINC26423850
c1cc(c(cc1O)O)/C=N\\Nc2nccs2 ZINC04482795
c1ccc(c(c1)N2C(=C([C@@H](C(=C2O)C#N)O)C#N)N)Br ZINC00494677
CCOC(=O)Nc1csnn1 ZINC01718899
c1ccc(cc1)n2c(nnn2)Cn3nc(nn3)c4ccsc4 ZINC09504077
CCOP(=O)(CO[C@@H]1CCCCO1)OCC ZINC16125103
CCn1c(nnn1)NC(=O)NCc2[nH+]ccn2Cc3ccccc3 ZINC23158989
Cc1nnnn1c2cc3c(s2)CCCC3 ZINC04917484
C1[C@@H]([C@@H](CS1(=O)=O)SC(=[NH2+])N)O ZINC04336692
C(=O)([C@@]([N+](=O)[O-])(Cl)Br)N ZINC04595350
c1ccc(cc1)C2=CN3C=CS[C@@H]3N2CCO ZINC01578471
c1cc2c(nc1)sc(=O)o2 ZINC05331463
C1CC[NH+]2CSCCC[C@@H]2C1 ZINC01580316
C1[NH+]2CN3CN1CP(=S)(C2)C3 ZINC19321765
CC(C)[C@H]1C(=N[C@@H](C(=N1)OC)Cc2cc(ccc2F)F)OC ZINC21987106
Cc1nnc(o1)CNC(=O)Cc2ccc(cc2)SC ZINC19740639
c1c(c([nH]n1)n2cnnn2)C(=O)[O-] ZINC05642230
C1C(=C(N2C(=O)CSC2=C1C(=O)N)N)C#N ZINC00201398
c1ccc2c(c1)nnn2CN3CC[C@@H](O3)C#N ZINC05520574
Cc1ccc(cc1)c2ncc(o2)Sc3[nH]c(=O)cc(n3)C ZINC35681810
CN(C)C(F)F ZINC19615840
Cc1nc(nn1C(C)C)NC(=O)NCc2coc(n2)c3ccccc3 ZINC20999088
COc1cc2c(cc1OC)C[n+]3ccccc3C2 ZINC01596111
c1cc(sc1)CCn2cnnc2S ZINC12505079
CCOC(=O)c1c(nc(c(n1)C)c2c[nH]c3c2cccc3)NC(=O)C ZINC01424411
Cc1nn2cc(nc2s1)Cn3cnc4ccncc4c3=O ZINC32587233
CC(=O)N1CCO[C@@H]1c2c(c(c(s2)Cl)Cl)Cl ZINC00523301
COP(=O)([C@@H]1C[C@H]2C[C@@H]1C=C2)OC ZINC05051728
CC(=O)/N=C\\1/NC(=O)/C(=C/N2CCCCC2)/S1 ZINC30783294
CC1([C@@]2(NC(=O)[C@]1(C([C@@H](O2)c3ccccc3)(C#N)C#N)C#N)C)C ZINC35323783
c1ccc2c(c1)c(nnn2)OC3CCN(CC3)C(=O)CCC(=O)[O-] ZINC21176605
C/N=C/1\\c2ccccc2[C@]3([C@@H]1[NH+]3C)c4ccccc4 ZINC16920394
c1cc(cc(c1)c2c3ccoc3ccn2)CO ZINC32590156
c1ccc2c(c1)[nH]c(n2)NNC3=C4C=CC[C@@H]4C3 ZINC02646901
C[S@@](=O)CC1=NC(=N)N=C1C[S@@](=O)C ZINC05567373
C=NC1C=CCC=C1 ZINC01764765
CNC1=C[N+](=C(c2cc(ccc2N1)Cl)c3ccccc3)[O-] ZINC13813184
C1Cc2c(sc(n2)N)C(=O)NC1 ZINC00243040
c1ccc2c(c1)-c3ccccc3S(=O)(=O)N2 ZINC00096040
c1ccc(c(c1)[C@@H]2NC(=NO2)CC(=O)N3CCCC3)O ZINC00138607
c1ccc(cc1)Cn2c(=O)ccc3c2n[nH]n3 ZINC03852551
c1ccc(c(c1)c2nc(on2)Cn3c(nnn3)N)Cl ZINC19223392
CC1=C/C(=C/2\\NCC=[NH+]2)/C(=O)[NH+]=N1 ZINC38342386
C1N(COCN1[N+](=O)[O-])[N+](=O)[O-] ZINC05002672
COCc1cn(nn1)[C@H]2CO[C@H]3[C@@H]2OC[C@H]3O ZINC04260853
Cn1/c(=N/C(=S)Nc2ccccc2)/n(nn1)Cc3ccccc3 ZINC17104998
c1ccc(cc1)C2=NNC(=[NH2+])SC2 ZINC05773132
CN(C)C1C(=O)C(=[N+](C)C)C1=O ZINC16953003
C[C@@H]1C=C(C(=O)c2ccccc2N1S(=O)(=O)C)Br ZINC05022953
COC1(C=NN=C2N1C=NN2)OC ZINC17176662
C/C(=N\\Oc1c2ccccc2ncn1)/SC ZINC15224080
CC1=C(/C(=C\\2/C(=NNS2)C)/N=N1)C(=O)OC ZINC13125377
Cc1nnc2n1c(=O)/c(=C\\C(=O)OC)/[nH]2 ZINC17949010
Cc1c(c(n(n1)Cc2csc(n2)c3ccccc3)C)[N+](=O)[O-] ZINC05797520
c1ccc(cc1)n2c(=O)cc3n(c2=O)CCN3 ZINC00398488
CCOC(=O)n1c2c([nH][nH]n1C(=O)OCC)n(c(=O)n(c2=O)C)C ZINC01871901
C[NH+]1CCC2(CC1)NC(=O)C3CC[NH+](CC3=N2)C ZINC03888336
c1ccc2c(c1)cc3n(c2=O)CCCCC3 ZINC15829272
Cc1c(ncc(c1c2ccc(c(c2O)OC)OC)C#N)C ZINC13281575
CC(C)(C)C1=C([N+](=[NH2+])c2n[nH]c(=S)n2[N-]1)O ZINC26443358
CC(=CC[C@@H]1[C@]([C@]2([C@@H](C(=O)CC[C@@]2(CS1)O)OC)O)(C)O)C ZINC31164256
Cn1cc(c(c1[C@@H]2CCCO2)C(=O)[O-])c3ccccc3 ZINC15778916
c1ccc-2c(c1)C[n+]3c2cccc3 ZINC01870630
c1ccc(c(c1)N)SC[C@@H](COc2cccc(n2)C(F)(F)F)O ZINC00117316
[H]/N=C\\1/C(=O)C=C(NC1=O)ONCCO ZINC01668471
CCOC(=O)c1c([nH]c(=O)[nH]1)Cn2ccnc2 ZINC19925870
C1C[NH+]=CC(C=[NH+]1)[N+](=O)[O-] ZINC01734891
c1ccc(cc1)/N=c/2\\[nH]n(c(=S)n2c3ccccc3)CCC(=O)[O-] ZINC00234993
Cc1c(snn1)c2[nH]c3ccccc3n2 ZINC11535685
c1cn[nH]n1 ZINC04807252
CC1=C2C(=O)O[C@@H]([C@@]2(CCC1)C)c3ccoc3 ZINC00265495
c1cc(cnc1)n2ccnc2 ZINC04227998
CC1(CC(=O)C(=C/N=C(/c2ccccc2)\\S)C(=O)C1)C ZINC05734408
c1cc2c(nc1)CC(=O)N2 ZINC13283774
c1cc2cc(cc(c2nc1)NCn3cnc(n3)[N+](=O)[O-])Br ZINC00032833
CC(=O)O[C@@H]1[C@@H]2CC[C@@H](C2)[C@@H]1[N+](=O)[O-] ZINC05081834
CCOC(=O)n1cnnn1 ZINC21999832
c1ccc(cc1)c2cc(no2)c3ncon3 ZINC01245981
CCOC(=O)c1c(n(nn1)CC2(COC2)C)C(=O)Nc3ccccc3 ZINC15767578
CC[C@@]1(N=N/C(=N\\c2ccccc2)/O1)C ZINC05337261
Cc1c(non1)/[NH+]=C/c2ccccc2[O-] ZINC03088859
C1CC=C([C@@H](C1)S(=O)(=O)CC#N)[NH+]2CCOCC2 ZINC05282762
COc1ccc2c(c1)CC[C@@H]3[C@@H]2O[C@@H](O3)c4cccnc4 ZINC05487850
c1cc2cccc3c2c(c1)C(=O)N3 ZINC00162129
c1ccc2c(c1)NS(=O)(=O)N2 ZINC00009186
CCOC(=O)c1c([nH]cc1O)C ZINC13465680
c1ccc2c(c1)C[C@@H]([C@@H]2Sc3ncccn3)O ZINC05776525
c1c(nc([nH]c1=O)SC2=N[C@@H](C[C@@H](C2)O)N)N ZINC17193121
C1C=C(O[C@@H]2[C@@H]1OC(=O)N2)C#N ZINC04267562
C[C@@]12CCCC[NH+]1CCC3=C2CCC3 ZINC05559913
CC(C)(C)C\\1=NN=N/C1=C/Nc2ccc(cc2)F ZINC05293058
CN(C)c1ccc(cc1)c2ncnn2c3nc[nH]n3 ZINC22146391
C1CCN2[C@@H](C1)C=CC3=C2CCCC3=O ZINC22003183
c1(c(non1)Sc2c(non2)[N+](=O)[O-])[N+](=O)[O-] ZINC04343707
C=CCC[C@@H](c1ccccc1)/[NH+]=N/C(=O)N ZINC16999204
Cc1c(noc1c2ccc(cc2)F)Cn3cnc(n3)C#N ZINC14144321
c1ccc2c(c1)C3=C(S2(=O)=O)NCCS3 ZINC20476634
Cc1cc(=O)c(c(o1)Nc2nnns2)C(=O)C ZINC27027625
Cc1cnc(cn1)C(=O)OCc2nnc(o2)C ZINC28648709
CC1=CC(=O)N[C@@H](N1)NN ZINC19168956
CC1=C(OP(=O)(O1)n2ccnc2)C ZINC01566125
CC(C)Cn1cncc1CNc2c(nccn2)C#N ZINC32598442
CC(=O)[C@@H]1C[C@@]2(C(=O)c3ccccc3N2O1)c4ccccc4 ZINC05712628
COC(=O)c1c(c(n2c1oc3c2cccc3)Cl)C(=O)OC ZINC00313544
CC1=CNNc2c1cccc2 ZINC01683958
CC(=O)C1=CCC[C@@H]2CC[C@H]1[NH2+]2 ZINC04098885
Cc1nc(on1)COC(=O)C[C@@H]2CCS(=O)(=O)C2 ZINC36692474
c1nc2c(c(n1)N)[nH]nc2[C@@H]3C[C@@H]([C@H](O3)CO)O ZINC16953432
c1cn2ccnc2cn1 ZINC12356946
Cc1ccc(n1c2nc([nH]n2)[S-])C ZINC13110826
C[C@@]1(CSC(=[NH+]1)Nc2c([nH]cn2)C(=O)Nc3ccccc3)O ZINC04818365
C1CC[NH+](CC1)/C=C/C=C/C=[N+]2CCCCC2 ZINC01623366
C(C(=O)OCS(=O)(=O)[O-])Br ZINC05048631
C[C@@H]1Sc2c(=S)ssc2S1 ZINC05920112
c1ccc(cc1)c2c(cc3ccccc3[o+]2)O ZINC01686405
c1ccnc(c1)c2c3c(cc(cn3)C(F)(F)F)no2 ZINC03134964
CC1=C([C@@H]2CCO[C@@H]2O1)C(=O)OC ZINC01590962
CSc1c(c(c(s1)C#N)N)[n+]2ccccc2 ZINC05037757
CC1=Nc2ccccc2NC(=C1c3ccc(c(c3)OC)OC)N ZINC13637103
Cc1ccc(cc1)C2=N[N+]3=C(SC2)SCC3=O ZINC26442375
c1cnc2c(n1)C(=CC2=O)[O-] ZINC21982657
C1=C[C@@H](C(=C1)CCO)S ZINC21991846
c1ccc(cc1)SCCS(=O)(=O)OCC(F)(F)F ZINC05446740
CC1(OC[C@@H](O1)[C@@H]2CC23SCCCS3)C ZINC01635232
Cc1cn2c(nc(nc2n1)c3ccccc3)N ZINC13491993
c1ccc(cc1)NC(=O)/C(=N/c2ccc(cc2C(F)(F)F)F)/NN ZINC20523173
c1cc[n+](cc1)c2c(cn3c2nc4ccccc4c3[O-])N ZINC36384739
CCc1c(sc(c1C(=O)OC)NC(=S)n2cc(cn2)C)C ZINC02859215
CC(=O)c1c(c(c(s1)SC)C#N)c2ccco2 ZINC00149475
C1CN(CC[NH2+]1)CC/[NH+]=C(/C(=[NH+]/CCN2CCNCC2)/[S-])\\[S-] ZINC19864754
C1CCNC(=O)/C(=N/N=C/2\\CCCCNC2=O)/C1 ZINC05566896
C[C@@H](/N=N/c1ccccc1)/N=N/c2[n-]nnn2 ZINC17024942
CC[C@@H](c1ccoc1)[S@@](=O)c2ccccc2 ZINC05675384
C=C1C=C(N2[C@@H](N=C(N=C2N1)N3CCOCC3)c4ccccc4)[O-] ZINC36552956
Cc1cc(nc(n1)N2C(=O)C/C(=C/C(=O)OC)/N2)C ZINC02203018
Cc1cc(n(n1)C)C(=O)Nn2cnc3ccccc3c2=[NH2+] ZINC06527852
C1COC=N1 ZINC01846598
CC(=O)O[C@@]1(CC(N[N+]1=O)(C)C)C ZINC17058437
C[NH+]1CCc2c(ccs2)[C@@H](OCC1)c3ccccc3 ZINC01607889
Cc1csc2n1s/c(=N/[N+](=O)[O-])/[nH+]2 ZINC01608251
CCn1nc(nn1)NC2CSCCSC2 ZINC23582035
CCSc1nc(s[s+]1)N2CCCC2 ZINC05811622
c1ccc-2c(c1)CCc3c2oc(n3)C[NH3+] ZINC40539228
C1C[C@@H]2N=Cc3c(c(sc3N2C1)C(=O)N)N ZINC01272559
CCCCNC(=O)NCC1=[NH+][C@@H]2C=CC=C[C@@H]2N1 ZINC20030994
C1CN=C2[C@@H](N1)C(=O)NC2=O ZINC05386257
c1c[n+](cc(c1[N+](=O)[O-])/C=C/N2CCOCC2)[O-] ZINC19313191
CC1=NS(=O)(=O)N=C1O ZINC26441933
c1cc(ccc1N2C(=O)N3CCCN=C3N2)F ZINC00093512
[H]/N=C/1\\[C@@H](C(=C\\2C1=C(C(/C2=N\\[H])C#N)C)C)C#N ZINC05501428
CCOP1(OC(=O)[C@@H](S1)C)(OCC)SSC ZINC05618554
Cc1c(n2c(ccn2)nn1)C ZINC01705111
CC1=C([C@@H](C2=C(N1)c3ccccc3C2=O)c4ccco4)C#N ZINC01407040
c1cc(ccc1C=Nn2cnnc2)O ZINC06645782
c1ccc(cc1)n2cc(cn2)c3c4ccccc4on3 ZINC13362334
CO[P@@](=O)(NS(=O)(=O)C)[O-] ZINC06483334
c1ccc2c(c1)-c3ccc(c4c3c(ccc4)C2=O)[S@@](=O)CC(=O)[O-] ZINC04683087
Cc1c(nsn1)CSCC(=O)NC ZINC40754381
Cc1cc(nc(n1)Nc2[nH]c3ccc(cc3n2)[N+](=O)[O-])C ZINC01301402
c1cc(cnc1)C2=C(NON2)/N=C\\N=O ZINC16010710
c1ccc2c(c1)C3C[C@@]4(C2c5c3cccc5)C=CS4(=O)=O ZINC16952606
COc1ccc(cc1)c2c(c[nH]n2)C=NNC(=O)OC ZINC08721401
CCCCC1=N/C(=C\\C#N)/Sc2c1ccc(n2)C ZINC04996816
COCCn1cncc1CNc2c(cc(cn2)Cl)F ZINC32593155
C/C(=N/N=C(\\C)/c1nnn(n1)C)/c2nnn(n2)C ZINC16136723
COC(=O)CSC(=O)N/C=C/c1ccsc1 ZINC02149293
CN(C)c1nnc(o1)c2cn[nH]c2N ZINC01395784
c1ccc(cc1)N2C(=O)N[C@@H]3c4ccccc4[C@H]5CC[C@@H]3[C@@H]5NC2=O ZINC03852371
[H]/N=C(\\CC(=O)c1ccccc1)/c2c(c(c([nH]c2=O)N)C#N)SC ZINC13283266
C1C[C@@H](OC1)Cn2c(nnn2)S ZINC04218699
Cc1cc(n(n1)[C@H]2C(=C(N=N2)C)[NH3+])C ZINC19737403
c1c2c(cc3c1OCCO3)[nH]c(=O)[nH]2 ZINC04983305
Cc1ccc(cc1)n2c3c[nH]c(=O)nc3cn2 ZINC32616595
c1cc(oc1)[C@@H]2N(C(=O)CS2)c3nccs3 ZINC36649114
COC1=C(Cc2c3ccccc3[nH]c2C(=O)N1)C#N ZINC01395241
CC(C)(C)N1[C@H]2CS(=O)(=O)C[C@@H]2CO1 ZINC19878364
CCc1c(nnc(n1)SCC2=N[C@@H]3C=CC=CN3C(=O)C2)CC ZINC03911024
c1cc(ccc1NC(=O)c2cc(=O)[nH+]c(s2)N)F ZINC06058681
Cc1nnc(o1)c2ccccc2c3coc4ccccc4c3=O ZINC32537128
Cc1c(nnn1c2nc3ccccc3s2)C(=O)N4CCOCC4 ZINC01418428
Cc1cc(=O)oc2c1cc[nH+]c2N ZINC01577802
CP(C)(Cc1ccc(cc1)[N+](=O)[O-])c2ccccc2 ZINC06485149
c1c[n+](c(c[n+]1[O-])c2cnoc2)[O-] ZINC03165159
c1ccc2c(c1)nc3n2c(=O)cn[nH]3 ZINC05647467
COC(=O)/C=c/1\\c(=O)n2c(=NCC2)s1 ZINC00187658
C[C@@]12CCCCC1=[N+](CN2O)[O-] ZINC00370675
c1c(nns1)c2nc(no2)CCl ZINC02243855
c1cc(oc1)c2nnc3n[nH]c(=S)n3n2 ZINC17059703
C1(=C(S(=O)(=O)C(=C1Cl)Cl)Cl)Cl ZINC02570757
Cc1nnc(n1c2cccc3c2non3)S ZINC13731070
c1ccc(cc1)C2=CSCC(=O)O2 ZINC01692836
Cc1cc(n2c(n1)cc(n2)NC[C@@]3(CCOC3)c4ccc(cc4)F)C ZINC20868478
CC(C)CC1=N[C@@H](OC1=O)c2ccccc2 ZINC03850379
[H]/N=C\\1/C=C[C@@H](S1)C ZINC34925468
C[C@@H]1CCCCN1C2=N/C(=C/3\\C=CC=CC3=O)/NC(=C2)C ZINC13121767
C/C=C(\\C=c1c(=C)[nH+]c([nH]1)CCC(=O)[O-])/Cl ZINC12362733
Cc1ccc(c(c1)C)c2csc(n2)CN3CCOC3=O ZINC30448012
C1C/C(=N/O)/CSC1 ZINC00158065
Cc1ccc(o1)[C@@H]2Nc3ccccc3C(=N2)O ZINC04108187
C1=Nc2c(non2)NN=C1 ZINC03204866
c1ccc(cc1)c2[nH]c(=S)c3c(n2)csc3N ZINC06530209
c1ccc(cc1)c2nc3cccnc3s2 ZINC12940414
COCCCNC1=C(C(=O)N[C@@H](S1)[C@@H]2CCC=CC2)C#N ZINC20414032
Cc1ccccc1CNc2cncc(n2)SCC(=O)[O-] ZINC32577550
c1ccc(cc1)C2=NO[C@@H]3[C@@H]2C(=O)NC(=O)N3 ZINC05124822
Cc1cc(on1)c2cnc(nc2CCNC(=O)C3CC3)C ZINC19123322
c1ccc2c(c1)n3cnnc3s2 ZINC00173203
C1CC=COC1 ZINC04726938
c1ccc2c(c1)cccc2Nc3ncc(o3)N ZINC01637015
COc1ccccc1n2n(o2)c3ccccc3OC ZINC01670119
c1cc(oc1)Cn2ccnc2S ZINC00024727
c12c(non1)C(=O)c3c(non3)C2=O ZINC01017719
c1cc(ccc1C(=O)Nc2c(c[nH]c(=O)n2)F)[N+](=O)[O-] ZINC01601271
c1ccc2c(c1)C=C[C@@H]3[C@@H]2SC(=C3)C=O ZINC00335881
C1CN2C[NH+]3CC=CC[C@@H]3[C@@H]1O2 ZINC05282913
CC1=NC2=C(CN1C)CN3C=C(C=CC3=N2)Br ZINC15781046
c1ccc(cc1)[C@@H]2[NH2+][C@H]3[C@H](S2)COC3=O ZINC16974985
CN1CCC\\2=C1/C(=N\\O)/S/C2=N\\c3ccc(cc3)F ZINC32936437
CO/N=C(/c1cc(sc1)Nc2cc(on2)c3ccco3)\\C(=O)[O-] ZINC00097189
c1cc(ccc1Nc2nnc(o2)c3c(nc[nH]3)[N+](=O)[O-])Cl ZINC04344036
c1ccc(cc1)/N=N/N2CCC(=O)N2 ZINC01395505
c1ccc(cc1)n2c(nnn2)Sc3c4c(ccs4)ncn3 ZINC08727358
c1ccc2c(c1)CC3[C@@H]2NC3=O ZINC05338825
Cc1cc(nc(n1)N/C(=N/S(=O)(=O)N2CCCCCC2)/[O-])C ZINC00186775
C[C@@]1([C@@H](N(C(=O)NC1=O)[C@H]2C=C[C@@H](O2)CO)N=[N+]=[N-])Br ZINC17214544
c1ccc(cc1)CN2CCC(=N2)/C=C/c3ccco3 ZINC00172696
Cc1cs/c(=N\\c2c(nc[nH]2)C(=O)N)/n1c3ccccc3 ZINC08694932
C1CSC(=C(SC1)SCC(=O)N)SCC(=O)N ZINC03672036
c1cc(oc1)/C=C\\2/C(=O)NC(=N2)[S-] ZINC37866052
CC(C)(C)C1=CC=Nn2cnnc2S1 ZINC04753508
Cc1ccc(cc1)S(=O)(=O)N/N=C(/C)\\c2c(cc(oc2=O)C)O ZINC17176558
CC1([C@@H](N(S1(=O)=O)C(=O)OC)N2CCCC2=O)C ZINC05179691
c1cc2n(c1)CCC2 ZINC04914414
COC(=O)/C=c/1\\c(=O)n2cnc(c2s1)C(=O)N ZINC01415905
CN1C(=O)/C(=C/[n+]2ccccc2N)/SC1=S ZINC01690541
COC(=O)C1=CC(=O)/C(=C(/Nc2ccc(cc2Cl)Cl)\\O)/S1 ZINC00480524
CCOC(=O)NC(=O)[C@H](/C=N/[C@@H](CO)c1ccccc1)C#N ZINC12859992
CC1=C(/C(=C\\2/C(=NNS2)C)/N=N1)C(=O)OC ZINC13125377
c1csc2c1[nH]c(=O)cc2O ZINC00080290
c1cc(ccc1c2cc3c([n-]nn3)nc2)C(=O)N4CCCC4 ZINC32593203
c1ccc2c(c1)c3c(c(=O)o2)SC(=C(C#N)C#N)S3 ZINC00051671
COc1ccc(c(c1)O)c2c(cn[nH]2)c3cscn3 ZINC13126788
c1cc(ccc1C2=C[N+](=C([CH-]O2)O)c3ccc(cc3)Cl)[N+](=O)[O-] ZINC33384935
CCOP(=O)(Cc1cnc(s1)Cl)OCC ZINC04198745
c1ccc2c(c1)CCC3=C(C=C2)C(=O)OC3 ZINC19878394
COc1ccc2c(c1)[C@@H]3CCCC[C@@H]3SCC2=O ZINC01706546
c1ccc2c(c1)c(nc(n2)[O-])C(=O)N[C@@H]3CCSC3 ZINC20907159
Cc1cccc(c1)/C=N/NC2=NNC(=Cc3n2ncn3)[NH3+] ZINC06055095
CC1(c2ccccc2-[n+]3c1cc(cc3c4ccccc4)c5ccco5)C ZINC03848440
Cc1cccc2c1[C@H](C(=O)N2)n3cnc4c(c3=O)cccn4 ZINC32519397
CS(=O)(=O)SCc1c[nH]cn1 ZINC06187559
c1cc(cc(c1)[N+](=O)[O-])[C@@H]2[C@@](O2)(C#N)C(=C(C#N)C#N)N ZINC35324029
c1cc([nH]c1)C(=O)CSC(=S)N2CCCC2 ZINC16927016
CCn1c2c[n+](nnc2c(=O)n(c1=O)CC)[O-] ZINC01696288
Cc1[c-](n2nc(c([n+]2n1)[N+](=O)[O-])C)C(=O)NCc3ccco3 ZINC04594478
c1[nH]c(c(n1)/N=N/SC[C@@H](C(=O)[O-])[NH3+])C(=O)N ZINC05605512
C=C(C[NH3+])Br ZINC01684537
CC(=O)N[C@@H](C(=O)[O-])OCSC ZINC21986107
c1ccc(cc1)C2=NNC(=O)/C(=N/N=c/3\\c4ccccc4nc[nH]3)/C2 ZINC12810418
c1cc(cc(c1)[N+](=O)[O-])c2cc(=S)[nH+]c(s2)N ZINC23549979
c1ccc(cc1)C2=CCC(=O)O2 ZINC01723799
CCOC(=O)CC(=C)NNc1c(=O)[nH]c(=O)[nH]n1 ZINC03196592
C/N=C/1\\CC(=O)C1(F)F ZINC05103278
COc1ccc(cc1)C2=NC[C@@H](S2)Cn3cnc(n3)C#N ZINC14143714
CC1=C(C(=O)C(=C(C1[NH+]=C(C)C)C)C)C ZINC01692804
Cc1c2c(on1)[C@@H](C(=C([C@@H]2c3ccccc3)C#N)[NH3+])c4cccc(c4)O ZINC04639687
c1ccc(cc1)n2c(=O)n3n(c2=O)-c4cc5ccccc5cc4C3 ZINC01630777
CC1=C(CC(=O)N1Cc2ccco2)C(=O)OC ZINC00032157
c1cnn(c1)c2ccc(nn2)NN ZINC27704609
CN1[C@@H]2[C@@H](NNC(=O)N2)N(C1=O)C ZINC01437219
c1ccc(cc1)C/N=c\\2/c(=[NH2+])c(c2O)[O-] ZINC11536078
c1ccc(cc1)N2C(=O)[C@H]3[C@H](C2=O)N=NN3 ZINC01635478
COc1cc(cc2c1oc(=O)s2)/C=C(/C#N)\\c3[nH]c4ccccc4n3 ZINC15230985
CN(C)C=C(C=[N+](C)C)C(F)(F)F ZINC19795288
Cc1ccc(cc1)[C@@H]2/C(=N/CCOC)/C(=O)C2=O ZINC06749075
Cc1c2c(cn[nH]2)n[nH]1 ZINC13119669
c1ccc(cc1)c2cn3c4ccsc4cc3c(=S)n2N ZINC05001335
Cc1cc(=O)c(c([nH]1)SC)C#N ZINC12250592
CC1=[NH+][C@@H](C(=N[C@@H](C1)c2ccccc2)C#N)C#N ZINC12341202
Cc1ccc(o1)/C=N/N=C/2\\C[C@@H](N=N2)c3ccccc3 ZINC04615652
CNc1c(cnc2c1c(=[NH2+])nc3n2cccc3)[N+](=O)[O-] ZINC05073549
c1ccc2c(c1)c3ccccc3n2/N=C/c4cnc(nc4)N ZINC13957148
CCS[C@@H]1C[C@@H](NC(=S)N1)C ZINC00495630
COCc1[nH]c2c(n1)-c3c(nc(o3)N)[C@H]2c4ccc(cc4)Cl ZINC04809817
C1=C(S/C(=C(\\N)/N=O)/N1)[N+](=O)[O-] ZINC04394414
C1C#CCSCC#CCS1 ZINC01579050
CNC1=C(C(=O)[C@H](O1)c2ccccc2)c3ccc(cc3)C(F)(F)F ZINC01392875
C1CC[C@H]2[C@@H](C1)N[P@@](=O)(S2)C3CCC(CC3)[N+](=O)[O-] ZINC05188068
[C@@H]12[C@@H](NS(=O)(=O)N1)[N-]/C(=N\\[N+](=O)[O-])/N2 ZINC27499231
c1ccc(cc1)C(=O)/C(=C/2\\C=[N+](ON2)[O-])/N=O ZINC04722296
c1ccc(c(c1)/N=N/c2ccc[nH]2)O ZINC05583542
c1ccc2c(c1)cc(cn2)O ZINC00191173
CC(C)(C)C(=O)[C@@]1([C@@H](c2ccccc2O1)O)n3cncn3 ZINC02338624
CC(C)c1nc(on1)[C@@H]2CCCN(C2)c3cc(nc(n3)N)OC ZINC32580949
Cc1ccn(n1)CCc2nnc(n2C3CC3)S ZINC02788311
CCOC(=O)[C@@H](c1ccccc1)S[P@@](=S)(O)OC ZINC02436012
Cn1c2c(cn1)nc3c(c2Cl)CCCC3 ZINC12394906
CC(=O)c1cc(cs1)CSC2=NCCN2 ZINC00034674
CCS(=O)(=O)NN1CCOCC1 ZINC06048769
CC1(CC2=C(C(=O)C1)Sc3c(c(=O)[nH]cn3)N2)C ZINC08773260
c1ccc(cc1)c2c3c([nH]n2)nccn3 ZINC05478968
CCS(=O)(=O)[C@@]1(CC[C@@]1(C#N)S(=O)(=O)CC)C#N ZINC01626125
COc1ccc2c(c1)CCC3=C2CC(=O)N4N3CCCC4 ZINC01585422
c1ccc(cc1)CSc2c3c(ncn2)nsn3 ZINC01683976
Cc1nn(c(=S)s1)CN2CCCCC2 ZINC20427162
CCn\\1ccnc(/c1=N\\c2ccc(cc2)OC)N3CCOCC3 ZINC08654268
CCOC(=O)C1=NN[C@@H]2[C@H]1C(=O)NC2=O ZINC04484374
c1ccc2c(c1)CP(=O)(C2)CCl ZINC02575553
C[C@]12C=C3C(OC(=O)N3CCN1C(=O)OC2(C)C)(C)C ZINC00052076
c1ccc(cc1)SC2CN(N(C2)C=O)C=O ZINC01403861
CC1=C(NON1)c2c(c(n[nH]2)N=O)N=O ZINC13810301
c1ccc(cc1)[C@@H]2CCc3c(nc(nc3O2)S)O ZINC08742562
CCOC(=O)c1ccc(cc1)NC=C2C(=O)OC(OC2=O)(C)C ZINC09187082
Cc1cc2n(n1)C3(CCCC3)N(NC2=O)C ZINC33950295
c1cc(ccc1CSc2nc(n(n2)C(=O)C3CCC3)N)Cl ZINC14162494
Cc1c(non1)c2cc3ccccc3[nH]2 ZINC15083870
c1ccc2c(c1)ncc(n2)Oc3cnns3 ZINC06943106
c1ccc2c(c1)c3c(c(=O)o2)nsn3 ZINC12341338
COC(=O)Nc1cc(nc([n+]1[O-])N)N2CCC=CC2 ZINC01590448
[H]/N=C\\1/C(=N\\Nc2ccccc2)/c3ncc(n3O1)c4ccc(cc4)Cl ZINC04457953
CC1=C([C@@H](N(O1)C)c2ccccc2)S(=O)(=O)c3ccccc3 ZINC01612039
CCOC(=O)c1c(sc(n1)N/N=C(\\C#N)/C2=NC(=O)CS2)C ZINC05473952
c1ccc(cc1)n2c3c(ccc(=O)o3)cn2 ZINC05477675
Cc1cnc(cn1)CNC(=O)CCN2CCCCO2 ZINC20820454
CN(Cc1ccccc1)c2c(nsn2)Cl ZINC35232106
c1cc2c(cc1O)CCO2 ZINC18557846
C[NH+]1CCC(CC1)OC(=O)[C@@](c2ccccc2)(c3ccns3)O ZINC01641834
c1ccc(cc1)N2CC=CCO2 ZINC01721075
C[C@@H]1C(=O)N/C(=C/C(=S)N)/S1 ZINC08857271
C1(=C(SC(=C(S1)C(=O)N)C(=O)N)C(=O)N)C(=O)N ZINC01720125
c1cc2c(nc1)-c3c(cccn3)[C@@H]4[C@H]2O4 ZINC18120220
c1ccc(cc1)[C@@H]2C(=N[C@@H](C(=N2)C#N)C(=O)N)c3ccccc3 ZINC04377479
COC(=O)c1ccoc1CC2CC[NH2+]CC2 ZINC12506134
c1cc2c(cc1Nc3nnc(o3)c4ccn[nH]4)OCO2 ZINC04632419
C1[C@@H]2[C@@H](CS1)OC(=O)O2 ZINC05574446
CS(=O)(=O)OCC[C@@H](C#N)c1cccc(c1)Cl ZINC15442603
c1cc2ccc3c(c2nc1)N=C[C@H](C3=O)C(=O)[O-] ZINC18166085
c1c(nc(nc1O)N)/N=C/[C@@H](C=O)[N+](=O)[O-] ZINC16893188
CC1=C(OP(=O)(O1)[O-])C ZINC01559410
CO[C@@]1(CC(=O)N1c2ccccc2)n3c4ccccc4nn3 ZINC06145487
c1ccc(cc1)n2nc3ccc4c(c3n2)nc(cn4)N ZINC00189666
COC1=C[C@@H]2C[C@@H]([C@]2(C=C1)OC)C#N ZINC17174593
CCc1ccc(o1)CC/C(=N/c2nc(ns2)CC(=O)C)/[O-] ZINC09014289
CCOC(=O)C1=NNC(=CC(=O)c2cccs2)C(=O)N1 ZINC04301474
c1ccc(cc1)n2c(c[nH]c2=S)/C(=N/c3nncs3)/[O-] ZINC21215055
Cc1cc(nc2c1c(c(s2)c3c(cco3)C(=O)OC)N)C ZINC12422107
CONC(=O)/C=C/c1ccc(s1)c2ccccc2 ZINC32848452
COc1cc(ccc1O)C2=N[C@H](C(=O)Nc3c2cc(cc3)Cl)O ZINC00083397
c1ccc(cc1)c2nc3c(c(n2)[O-])nc(=O)n(c3N)c4ccccc4 ZINC13470910
Cc1nc2c(s1)CCCC2 ZINC39269804
c1cc(ccc1c2cc(nc(n2)S)C(=O)Nc3nccs3)N ZINC01617710
Cc1c(sc(n1)c2c(c[nH]n2)c3ccccc3F)CC[NH3+] ZINC19560940
Cc1c(sc2n1ncn2)c3csc(n3)C ZINC01401913
CO[C@@]1(C(=O)N(C(=N1)[O-])c2ccc(cc2)F)C(F)(F)F ZINC13362415
c1ccc(cc1)n2c3c(cn2)c(=[NH2+])[nH][nH]3 ZINC08627905
CCOC(=O)c1c(c2c(cccc2s1)F)Cn3cnc(n3)C#N ZINC31766246
CC(C)(C)/N=C/1\\CC(=O)OC1 ZINC05284069
COC(=O)c1ccccc1OC/C(=[NH+]\\Nc2cccc(n2)Cl)/N ZINC05210011
CCOC1=CN(NCC(=C1)[N+](=O)[O-])c2ccncn2 ZINC28294498
c1ccc(cc1)/C=C/2\\CCNC2=O ZINC01653824
C/C(=N/NC(=S)N)/c1cc(ccc1O)Cl ZINC05287757
CCCc1[nH]c(nn1)Sc2ncc(c(n2)NC3CC3)[N+](=O)[O-] ZINC12894303
CN(C)NC(=S)SNN(C)C ZINC21487034
CS(=O)(=O)CSCSSCS(=O)(=O)C ZINC01631876
c1cc(c(c(c1)Cl)[C@@H]2[C@@H]([C@@H](SCCS2)N)C#N)F ZINC20086628
CC1=NNC(=O)OC1(C)C ZINC04776593
Cn1c(=O)n(sc1=O)c2ccccc2 ZINC00102676
C1COC(C(O1)(Cl)Cl)(Cl)Cl ZINC04722373
c1cc(ccc1C(=O)NC2=[NH+]CCCCCN2)[N+](=O)[O-] ZINC00045438
Cc1cc([nH]c1c2nc(no2)C[C@@H]3CCCO3)C ZINC20598117
c1ccc2c(c1)C=CC23C(=O)c4ccccc4C3=O ZINC01611935
C([C@@H]1[C@@H]([C@@H]([C@@H](O1)OP(=O)([O-])[O-])O)O)O ZINC03870207
CC1=C([C@@H](C(=C(S1)N)C#N)c2ccco2)C#N ZINC20568713
c1ccc(cc1)n2nc3c([n+]2[O-])-c4ccccc4CC3 ZINC07787398
C[NH+]1CCC2=C(C1)C(=NC3(N2)CCCCC3)O ZINC19334663
Cc1c(snn1)c2[nH]nc(n2)[S-] ZINC40544346
Cc1c(non1)NS(=O)(=O)c2ccccc2 ZINC00134635
c1cc(sc1)n2c(=O)[nH]nn2 ZINC04218436
c1nc(c2c(n1)snc2Cl)O ZINC16977238
C1C[C@H]2C[C@@H]1C3=C2SC([C@@H]3F)(F)F ZINC05600475
Cc1nc(on1)c2cnc[nH]c2=O ZINC40351448
Cc1c(c(c(o1)C)C(=O)[O-])c2ccc3c(c2)[nH]c(n3)NC(=O)OC ZINC13216433
CSCSC ZINC01621620
c1nnnn1CC(=O)N[C@@H]2CCS(=O)(=O)C2 ZINC27977392
Cc1nn2cc(nc2s1)CNC(=O)Nc3nnc(s3)C4CCC4 ZINC23429667
Cc1cccc(c1)NC2=C(C(=O)NC3(S2)CCCC3)C#N ZINC00137148
c1ccc(cc1)C#CC(=O)NCCc2cnccn2 ZINC20860959
c1cc(ccc1c2nnc(n2C[C@H]3CCCO3)S)O ZINC04993921
Cc1cn2ccnc2s1 ZINC33359219
CC1(C=C2C(=NN(C(=C2C#N)N)c3ccccc3)CO1)C ZINC00173901
CCn\\1c(=O)/c(=C/Nc2ccccc2)/s/c1=C(\\C#N)/C(=O)OCC ZINC12546759
Cc1ccc(cc1N/C=C(\\C(=O)OC)/n2cncn2)F ZINC01407155
c1nncn1NC(=S)SCC(=O)[O-] ZINC16848052
Cc1ccc(cc1)OC/C(=N/OC(=O)c2cc(on2)C)/N ZINC08536224
Cc1c(=O)[nH]c2[n+](n1)[C@@H]([C@@H](CS2)Br)c3ccccc3 ZINC00202955
c1ccc2c(c1)C(=NCCO2)S ZINC06576028
CC(C)(C1c2ccccc2C(=NO)c3c1cccc3)[N+](=O)[O-] ZINC01601553
C[C@@]12C[C@@]3(C(NC(=S)N3O)(C)C)ON1C(=S)NC2(C)C ZINC00499273
CC1=NC(=C2[C@@H](C1C#N)c3ccccc3NC2=[NH2+])C ZINC08651825
c1ccc(cc1)C2=NN(CN(N2)c3ccccc3)c4[n-]nnn4 ZINC05682131
c1ccc(cc1)c2nnn(n2)Cc3nc4ccccc4s3 ZINC00416878
C1C(=NCC(=N1)S)S ZINC05566537
C(#N)S/C(=C(/[N+](=O)[O-])\\Cl)/C(=C(Cl)Cl)Cl ZINC17256958
c1ccc(cc1)[C@@H]2CC(=O)C=CO2 ZINC21999070
Cc1ccc(nc1)/[NH+]=c\\2/c(c(cc(o2)C)O)C(=O)C ZINC35682746
Cn1cccc1c2cc3n(n2)CCCN3 ZINC20864741
CCO[P@@]1(=O)CCCCN1Cc2ccccc2 ZINC01849833
Cc1nc(on1)c2ccc(cc2)O ZINC04015309
Cc1cs/c(=N\\C(C)C)/n1/N=C/c2ccco2 ZINC05833376
COC1=CCC=CC1 ZINC01840966
CCOC(=O)CNC1=[NH+]N=[S@](=O)(c2c1cccc2)O ZINC17223734
Cc1[n+](c2ccc3c(c2s1)C(C(=[N+]3C)C)(C)C)C ZINC27987876
Cc1c(c([nH]n1)C(F)(F)F)/N=N\\N(C)C ZINC16924215
c1ccc(cc1)c2nc(nc(n2)[n+]3cccc(c3)O)c4ccccc4 ZINC05516865
Cc1c(ccc2c1oc(c2[O-])C=[N+]3CCN(CC3)C)O ZINC23127906
c1cc2=[NH+]C(=O)CC=c2c(c1)O ZINC16952034
c1ccc(cc1)c2c(c3n(n2)-c4ccccc4[C@@H]3CO)O ZINC06007053
Cc1cc(nn2c1nnc2)n3c(nc(n3)N)N ZINC06413412
C1CCC2=CS(=O)(=O)[C@@H]2C1 ZINC01705812

File diff suppressed because it is too large Load Diff