Expose propertyFlags to CFFI and MinimalLib (#7960)

* - added property support to CFFI library
- added propertyFlags JSON parser
- added support for setting propertyFlags to MinimalLib

* added missing files

* fix SWIG builds

---------

Co-authored-by: ptosco <paolo.tosco@novartis.com>
This commit is contained in:
Paolo Tosco
2024-11-07 06:08:27 +01:00
committed by GitHub
parent 5b943e3a55
commit b1d322555b
13 changed files with 294 additions and 32 deletions

View File

@@ -30,6 +30,7 @@
#include <ios>
#endif
#include <cstdint>
#include <RDGeneral/BetterEnums.h>
namespace RDKit {
class ROMol;
@@ -48,20 +49,19 @@ class RDKIT_GRAPHMOL_EXPORT MolPicklerException : public std::exception {
};
namespace PicklerOps {
typedef enum {
NoProps = 0, // no data pickled (default pickling, single-precision coords)
MolProps = 0x1, // only public non computed properties
AtomProps = 0x2,
BondProps = 0x4,
QueryAtomData =
0x2, // n.b. DEPRECATED and set to AtomProps (does the same work)
PrivateProps = 0x10,
ComputedProps = 0x20,
AllProps = 0x0000FFFF, // all data pickled
CoordsAsDouble = 0x00010000, // save coordinates in double precision
NoConformers =
0x00020000 // do not include conformers or associated properties
} PropertyPickleOptions;
BETTER_ENUM(
PropertyPickleOptions, unsigned int,
NoProps = 0, // no data pickled (default pickling, single-precision coords)
MolProps = 0x1, // only public non computed properties
AtomProps = 0x2, BondProps = 0x4,
QueryAtomData =
0x2, // n.b. DEPRECATED and set to AtomProps (does the same work)
PrivateProps = 0x10, ComputedProps = 0x20,
AllProps = 0x0000FFFF, // all data pickled
CoordsAsDouble = 0x00010000, // save coordinates in double precision
NoConformers =
0x00020000 // do not include conformers or associated properties
);
} // namespace PicklerOps
//! handles pickling (serializing) molecules
@@ -188,10 +188,12 @@ class RDKIT_GRAPHMOL_EXPORT MolPickler {
MolPickler::molFromPickle(pickle, &mol, propertyFlags);
}
static void molFromPickle(const std::string &pickle, ROMol *mol) {
MolPickler::molFromPickle(pickle, mol, PicklerOps::AllProps);
MolPickler::molFromPickle(pickle, mol,
PicklerOps::PropertyPickleOptions::AllProps);
}
static void molFromPickle(const std::string &pickle, ROMol &mol) {
MolPickler::molFromPickle(pickle, &mol, PicklerOps::AllProps);
MolPickler::molFromPickle(pickle, &mol,
PicklerOps::PropertyPickleOptions::AllProps);
}
//! constructs a molecule from a pickle stored in a stream
@@ -202,10 +204,12 @@ class RDKIT_GRAPHMOL_EXPORT MolPickler {
MolPickler::molFromPickle(ss, &mol, propertyFlags);
}
static void molFromPickle(std::istream &ss, ROMol *mol) {
MolPickler::molFromPickle(ss, mol, PicklerOps::AllProps);
MolPickler::molFromPickle(ss, mol,
PicklerOps::PropertyPickleOptions::AllProps);
}
static void molFromPickle(std::istream &ss, ROMol &mol) {
MolPickler::molFromPickle(ss, &mol, PicklerOps::AllProps);
MolPickler::molFromPickle(ss, &mol,
PicklerOps::PropertyPickleOptions::AllProps);
}
private:

View File

@@ -192,6 +192,7 @@
%include "enums.swg"
%javaconst(1);
#endif
%include <RDGeneral/BetterEnums.h>
%include <GraphMol/MolPickler.h>
#ifdef SWIGJAVA
%javaconst(0);

View File

@@ -34,7 +34,7 @@ if(RDK_BUILD_MINIMAL_LIB)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${USE_FLAGS}")
endif()
endif()
add_executable(RDKit_minimal jswrapper.cpp minilib.cpp)
add_executable(RDKit_minimal jswrapper.cpp minilib.cpp JSONParsers.cpp)
target_link_libraries(RDKit_minimal ${MINIMAL_LIB_LIBRARIES})
set_target_properties(RDKit_minimal PROPERTIES LINK_FLAGS "--bind")
@@ -67,7 +67,7 @@ if(RDK_BUILD_CFFI_LIB)
set(LIBS_TO_USE ${tmpLibs})
endif()
add_library(rdkitcffi SHARED cffiwrapper.cpp)
add_library(rdkitcffi SHARED cffiwrapper.cpp JSONParsers.cpp)
target_link_libraries(rdkitcffi PUBLIC rdkit_base)
target_link_libraries(rdkitcffi PUBLIC ${LIBS_TO_USE})
INSTALL(TARGETS rdkitcffi EXPORT rdkit-targets

View File

@@ -0,0 +1,48 @@
//
// Copyright (C) 2024 Novartis Biomedical Research 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.
//
#define USE_BETTER_ENUMS
#include "JSONParsers.h"
#include <GraphMol/MolPickler.h>
#include <GraphMol/FileParsers/PNGParser.h>
#include <GraphMol/SmilesParse/SmilesJSONParsers.h>
#include <RDGeneral/BoostStartInclude.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <RDGeneral/BoostEndInclude.h>
namespace RDKit {
namespace MinimalLib {
void updatePropertyPickleOptionsFromJSON(unsigned int &propFlags,
const char *details_json) {
if (details_json && strlen(details_json)) {
std::istringstream ss;
boost::property_tree::ptree pt;
ss.str(details_json);
boost::property_tree::read_json(ss, pt);
const auto nodeIt = pt.find("propertyFlags");
if (nodeIt != pt.not_found()) {
auto propertyFlagsFromJson =
(+PicklerOps::PropertyPickleOptions::NoProps)._to_integral();
for (const auto *key : PicklerOps::PropertyPickleOptions::_names()) {
if (nodeIt->second.get(key, false)) {
propertyFlagsFromJson |=
PicklerOps::PropertyPickleOptions::_from_string(key)
._to_integral();
}
}
propFlags = propertyFlagsFromJson;
}
}
}
} // end namespace MinimalLib
} // end namespace RDKit

View File

@@ -0,0 +1,22 @@
//
// Copyright (C) 2024 Novartis Biomedical Research 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.
//
#pragma once
#include <GraphMol/FileParsers/PNGParser.h>
namespace RDKit {
namespace MinimalLib {
void updatePropertyPickleOptionsFromJSON(unsigned int &propFlags,
const char *details_json);
} // end namespace MinimalLib
} // end namespace RDKit

View File

@@ -2765,6 +2765,75 @@ void test_custom_palette() {
free(mpkl);
}
void test_props() {
printf("--------------------------\n");
printf(" mol props\n");
const char *smi = "c1ccccn1";
const char *cxsmi_in = "c1ccccn1 |atomProp:0.a.1|";
const char *details = "{\"NoProps\":true}";
char *mpkl;
size_t mpkl_size;
char **prop_list;
char *prop;
char *cxsmi_out;
mpkl = get_mol(smi, &mpkl_size, "");
assert(mpkl && mpkl_size);
prop_list = get_prop_list(mpkl, mpkl_size, 0, 0);
assert(prop_list && !prop_list[0]);
free(prop_list);
set_prop(&mpkl, &mpkl_size, "a", "1", 0);
set_prop(&mpkl, &mpkl_size, "b", "2", 0);
prop_list = get_prop_list(mpkl, mpkl_size, 0, 0);
assert(prop_list && prop_list[0] && prop_list[1] && !prop_list[2]);
assert(!strcmp(prop_list[0], "a"));
free(prop_list[0]);
assert(!strcmp(prop_list[1], "b"));
free(prop_list[1]);
free(prop_list);
assert(has_prop(mpkl, mpkl_size, "a"));
assert(has_prop(mpkl, mpkl_size, "b"));
assert(!has_prop(mpkl, mpkl_size, "c"));
prop = get_prop(mpkl, mpkl_size, "a");
assert(prop);
assert(!strcmp(prop, "1"));
free(prop);
prop = get_prop(mpkl, mpkl_size, "b");
assert(prop);
assert(!strcmp(prop, "2"));
free(prop);
assert(clear_prop(&mpkl, &mpkl_size, "a"));
assert(!clear_prop(&mpkl, &mpkl_size, "c"));
prop_list = get_prop_list(mpkl, mpkl_size, 0, 0);
assert(prop_list && prop_list[0] && !prop_list[1]);
assert(!strcmp(prop_list[0], "b"));
free(prop_list[0]);
free(prop_list);
prop = get_prop(mpkl, mpkl_size, "a");
assert(!prop);
prop = get_prop(mpkl, mpkl_size, "b");
assert(prop);
assert(!strcmp(prop, "2"));
free(prop);
keep_props(&mpkl, &mpkl_size, "{\"propertyFlags\":{\"NoProps\":true}}");
prop_list = get_prop_list(mpkl, mpkl_size, 0, 0);
assert(prop_list && !prop_list[0]);
free(prop_list);
prop = get_prop(mpkl, mpkl_size, "b");
assert(!prop);
free(mpkl);
mpkl = get_mol(cxsmi_in, &mpkl_size, "");
cxsmi_out = get_cxsmiles(mpkl, mpkl_size, "");
assert(!strcmp(cxsmi_out, "c1ccncc1 |atomProp:2.a.1|"));
free(cxsmi_out);
free(mpkl);
mpkl =
get_mol(cxsmi_in, &mpkl_size, "{\"propertyFlags\":{\"NoProps\":true}}");
cxsmi_out = get_cxsmiles(mpkl, mpkl_size, "");
assert(!strcmp(cxsmi_out, "c1ccncc1"));
free(cxsmi_out);
free(mpkl);
}
int main() {
enable_logging();
char *vers = version();
@@ -2802,5 +2871,6 @@ int main() {
test_multi_highlights();
test_bw_palette();
test_custom_palette();
test_props();
return 0;
}

View File

@@ -85,9 +85,10 @@ char *str_to_c(const char *str) {
}
} // namespace
void mol_to_pkl(const ROMol &mol, char **mol_pkl, size_t *mol_pkl_sz) {
unsigned int propFlags = PicklerOps::PropertyPickleOptions::AllProps ^
PicklerOps::PropertyPickleOptions::ComputedProps;
void mol_to_pkl(
const ROMol &mol, char **mol_pkl, size_t *mol_pkl_sz,
unsigned int propFlags = PicklerOps::PropertyPickleOptions::AllProps ^
PicklerOps::PropertyPickleOptions::ComputedProps) {
std::string pkl;
MolPickler::pickleMol(mol, pkl, propFlags);
free(*mol_pkl);
@@ -269,9 +270,9 @@ extern "C" char *get_mol(const char *input, size_t *pkl_sz,
*pkl_sz = 0;
return nullptr;
}
static const unsigned int propFlags =
PicklerOps::PropertyPickleOptions::AllProps ^
PicklerOps::PropertyPickleOptions::ComputedProps;
unsigned int propFlags = PicklerOps::PropertyPickleOptions::AllProps ^
PicklerOps::PropertyPickleOptions::ComputedProps;
MinimalLib::updatePropertyPickleOptionsFromJSON(propFlags, details_json);
std::string pkl;
MolPickler::pickleMol(*mol, pkl, propFlags);
return str_to_c(pkl, pkl_sz);
@@ -873,6 +874,77 @@ extern "C" bool clear_log_buffer(void *log_handle) {
return 0;
}
extern "C" short has_prop(const char *mol_pkl, size_t mol_pkl_sz,
const char *key) {
auto mol = mol_from_pkl(mol_pkl, mol_pkl_sz);
return mol.hasProp(key);
}
extern "C" char **get_prop_list(const char *mol_pkl, size_t mol_pkl_sz,
short includePrivate, short includeComputed) {
auto mol = mol_from_pkl(mol_pkl, mol_pkl_sz);
auto propList = mol.getPropList(includePrivate, includeComputed);
std::string propNames;
for (const auto &prop : propList) {
propNames += prop + ",";
}
auto resLen = sizeof(char *) * (propList.size() + 1);
char **res = (char **)malloc(resLen);
if (!res) {
return nullptr;
}
memset(res, 0, resLen);
for (size_t i = 0; i < propList.size(); ++i) {
res[i] = strdup(propList.at(i).c_str());
if (!res[i]) {
while (i--) {
free(res[i]);
}
return nullptr;
}
}
return res;
}
extern "C" void set_prop(char **mol_pkl, size_t *mol_pkl_sz, const char *key,
const char *val, short computed) {
auto mol = mol_from_pkl(*mol_pkl, *mol_pkl_sz);
std::string valAsString(val);
mol.setProp(key, valAsString, computed);
mol_to_pkl(mol, mol_pkl, mol_pkl_sz);
}
extern "C" char *get_prop(const char *mol_pkl, size_t mol_pkl_sz,
const char *key) {
auto mol = mol_from_pkl(mol_pkl, mol_pkl_sz);
if (!mol.hasProp(key)) {
return nullptr;
}
std::string val;
mol.getProp(key, val);
return strdup(val.c_str());
}
extern "C" short clear_prop(char **mol_pkl, size_t *mol_pkl_sz,
const char *key) {
auto mol = mol_from_pkl(*mol_pkl, *mol_pkl_sz);
short res = mol.hasProp(key);
if (res) {
mol.clearProp(key);
mol_to_pkl(mol, mol_pkl, mol_pkl_sz);
}
return res;
}
extern "C" void keep_props(char **mol_pkl, size_t *mol_pkl_sz,
const char *details_json) {
auto mol = mol_from_pkl(*mol_pkl, *mol_pkl_sz);
unsigned int propFlags = PicklerOps::PropertyPickleOptions::AllProps ^
PicklerOps::PropertyPickleOptions::ComputedProps;
MinimalLib::updatePropertyPickleOptionsFromJSON(propFlags, details_json);
mol_to_pkl(mol, mol_pkl, mol_pkl_sz, propFlags);
}
#if (defined(__GNUC__) || defined(__GNUG__))
#pragma GCC diagnostic pop
#endif

View File

@@ -164,6 +164,23 @@ RDKIT_RDKITCFFI_EXPORT short destroy_log_handle(void **log_handle);
RDKIT_RDKITCFFI_EXPORT char *get_log_buffer(void *log_handle);
RDKIT_RDKITCFFI_EXPORT short clear_log_buffer(void *log_handle);
// props
RDKIT_RDKITCFFI_EXPORT short has_prop(const char *mol_pkl, size_t mol_pkl_sz,
const char *key);
RDKIT_RDKITCFFI_EXPORT char **get_prop_list(const char *mol_pkl,
size_t mol_pkl_sz,
short includePrivate,
short includeComputed);
RDKIT_RDKITCFFI_EXPORT void set_prop(char **mol_pkl, size_t *mol_pkl_sz,
const char *key, const char *val,
short computed);
RDKIT_RDKITCFFI_EXPORT char *get_prop(const char *mol_pkl, size_t mol_pkl_sz,
const char *key);
RDKIT_RDKITCFFI_EXPORT short clear_prop(char **mol_pkl, size_t *mol_pkl_sz,
const char *key);
RDKIT_RDKITCFFI_EXPORT void keep_props(char **mol_pkl, size_t *mol_pkl_sz,
const char *details_json);
#ifdef __cplusplus
}
#endif

View File

@@ -54,7 +54,7 @@
#include <GraphMol/RGroupDecomposition/RGroupUtils.h>
#include <RDGeneral/RDLog.h>
#include "common_defs.h"
#include "JSONParsers.h"
#include <sstream>
#include <RDGeneral/BoostStartInclude.h>
#include <boost/property_tree/ptree.hpp>

View File

@@ -153,7 +153,12 @@ emscripten::val uint_vector_to_uint32array(
return res;
}
emscripten::val get_as_uint8array(const JSMolBase &self) {
emscripten::val get_as_uint8array(const JSMolBase &self,
const std::string &details) {
return binary_string_to_uint8array(self.get_pickle(details));
}
emscripten::val get_as_uint8array_no_details(const JSMolBase &self) {
return binary_string_to_uint8array(self.get_pickle());
}
@@ -428,6 +433,7 @@ EMSCRIPTEN_BINDINGS(RDKit_minimal) {
select_overload<std::string(const std::string &) const>(
&JSMolBase::get_v3Kmolblock))
.function("get_as_uint8array", &get_as_uint8array)
.function("get_as_uint8array", &get_as_uint8array_no_details)
#ifdef RDK_BUILD_INCHI_SUPPORT
.function("get_inchi",
select_overload<std::string(const std::string &) const>(

View File

@@ -143,10 +143,11 @@ std::string JSMolBase::get_json() const {
return MolInterchange::MolToJSONData(get());
}
std::string JSMolBase::get_pickle() const {
std::string JSMolBase::get_pickle(const std::string &details) const {
unsigned int propFlags = PicklerOps::AllProps ^ PicklerOps::ComputedProps;
MinimalLib::updatePropertyPickleOptionsFromJSON(propFlags, details.c_str());
std::string pickle;
MolPickler::pickleMol(get(), pickle,
PicklerOps::AllProps ^ PicklerOps::ComputedProps);
MolPickler::pickleMol(get(), pickle, propFlags);
return pickle;
}

View File

@@ -42,7 +42,8 @@ class JSMolBase {
std::string get_molblock() const { return get_molblock("{}"); }
std::string get_v3Kmolblock(const std::string &details) const;
std::string get_v3Kmolblock() const { return get_v3Kmolblock("{}"); }
std::string get_pickle() const;
std::string get_pickle(const std::string &details) const;
std::string get_pickle() const { return get_pickle(""); };
#ifdef RDK_BUILD_INCHI_SUPPORT
std::string get_inchi(const std::string &options) const;
std::string get_inchi() const { return get_inchi(""); }

View File

@@ -3402,6 +3402,25 @@ function test_custom_palette() {
mol.delete();
}
function test_pickle() {
const mol = RDKitModule.get_mol('c1ccccn1');
assert(mol);
mol.set_prop('a', '1');
assert(mol.get_prop('a') === '1');
const pklWithProps = mol.get_as_uint8array();
assert(pklWithProps);
let molFromPkl = RDKitModule.get_mol_from_uint8array(pklWithProps);
assert(molFromPkl);
assert(molFromPkl.has_prop('a'));
assert(molFromPkl.get_prop('a') === '1');
molFromPkl.delete();
const pklWithoutProps = mol.get_as_uint8array(JSON.stringify({propertyFlags: { NoProps: true } }));
molFromPkl = RDKitModule.get_mol_from_uint8array(pklWithoutProps);
assert(molFromPkl);
assert(!molFromPkl.has_prop('a'));
molFromPkl.delete();
}
initRDKitModule().then(function(instance) {
var done = {};
const waitAllTestsFinished = () => {
@@ -3490,6 +3509,7 @@ initRDKitModule().then(function(instance) {
}
test_bw_palette();
test_custom_palette();
test_pickle();
waitAllTestsFinished().then(() =>
console.log("Tests finished successfully")