Skip to content

Commit

Permalink
Replace awful enum reflection macros with Better Enums (rdkit#7913)
Browse files Browse the repository at this point in the history
* - moved SMILES and RGroupDecomp JSON parsers to their own translation units
- added missing DLL export decorators that had been previously forgotten
- changed the signatures of MolToCXSmiles and updateCXSmilesFieldsFromJSON
to replace enum parameters with the underlying types
- updated ReleaseNotes.md

* make sure cxSmilesFields is only updated if the JSON string contains keys
belonging to the CXSmilesFields enum

* added missing copyright notices

---------

Co-authored-by: ptosco <[email protected]>
  • Loading branch information
ptosco and ptosco authored Oct 25, 2024
1 parent ccfb1fa commit f66ad7e
Show file tree
Hide file tree
Showing 18 changed files with 374 additions and 281 deletions.
12 changes: 12 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,18 @@ if(NOT Catch2_FOUND)
FetchContent_MakeAvailable(Catch2)
endif()

#include better-enums
find_package(better_enums 0 QUIET)
if(NOT better_enums)
Include(FetchContent)

FetchContent_Declare(
better_enums
GIT_REPOSITORY https://github.com/aantron/better-enums.git
GIT_TAG c35576bed0295689540b39873126129adfa0b4c8 # 0.11.3
)
endif()

if(RDK_INSTALL_INTREE)
set(RDKit_BinDir "${CMAKE_SOURCE_DIR}/bin")
set(RDKit_LibDir "${CMAKE_SOURCE_DIR}/lib")
Expand Down
32 changes: 17 additions & 15 deletions Code/GraphMol/RGroupDecomposition/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,33 +1,35 @@

rdkit_library(RGroupDecomposition RGroupDecomp.cpp RGroupDecompData.cpp RGroupData.cpp RGroupUtils.cpp RGroupCore.cpp
RGroupDecompParams.cpp RGroupScore.cpp RGroupFingerprintScore.cpp RGroupGa.cpp
rdkit_library(RGroupDecomposition RGroupDecomp.cpp RGroupDecompData.cpp
RGroupData.cpp RGroupUtils.cpp RGroupCore.cpp
RGroupDecompParams.cpp RGroupScore.cpp RGroupFingerprintScore.cpp
RGroupGa.cpp RGroupDecompJSONParsers.cpp
LINK_LIBRARIES
FMCS ChemTransforms SubstructMatch SmilesParse Fingerprints
GraphMol RDGeneral ga MolEnumerator TautomerQuery )
GraphMol RDGeneral ga MolEnumerator TautomerQuery)
target_compile_definitions(RGroupDecomposition PRIVATE RDKIT_RGROUPDECOMPOSITION_BUILD)

rdkit_headers(
RGroupDecomp.h RGroupDecompParams.h
DEST GraphMol/RGroupDecomposition)
RGroupDecomp.h RGroupDecompParams.h RGroupDecompJSONParsers.h
DEST GraphMol/RGroupDecomposition)

if(RDK_BUILD_PYTHON_WRAPPERS)
add_subdirectory(Wrap)
add_subdirectory(Wrap)
endif()

rdkit_test(testRGroupDecomp testRGroupDecomp.cpp
LINK_LIBRARIES RGroupDecomposition )
LINK_LIBRARIES RGroupDecomposition)

rdkit_test(testRGroupDecompInternals testRGroupInternals.cpp
LINK_LIBRARIES RGroupDecomposition )
LINK_LIBRARIES RGroupDecomposition)

rdkit_catch_test(rgroupCatchTests catch_rgd.cpp
LINK_LIBRARIES RGroupDecomposition )
LINK_LIBRARIES RGroupDecomposition)

find_package(Boost ${RDK_BOOST_VERSION} COMPONENTS program_options CONFIG)
if(RDK_BUILD_CPP_TESTS AND Boost_FOUND)
add_executable(gaExample GaExample.cpp)
if(NOT Boost_USE_STATIC_LIBS)
target_compile_definitions(gaExample PUBLIC -DBOOST_PROGRAM_OPTIONS_DYN_LINK)
endif()
target_link_libraries(gaExample RGroupDecomposition ${Boost_PROGRAM_OPTIONS_LIBRARY})
endif()
add_executable(gaExample GaExample.cpp)
if(NOT Boost_USE_STATIC_LIBS)
target_compile_definitions(gaExample PUBLIC -DBOOST_PROGRAM_OPTIONS_DYN_LINK)
endif()
target_link_libraries(gaExample RGroupDecomposition ${Boost_PROGRAM_OPTIONS_LIBRARY})
endif()
89 changes: 89 additions & 0 deletions Code/GraphMol/RGroupDecomposition/RGroupDecompJSONParsers.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//
// 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 "RGroupDecompJSONParsers.h"
#include <RDGeneral/BoostStartInclude.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <RDGeneral/BoostEndInclude.h>

namespace RDKit {

void updateRGroupDecompositionParametersFromJSON(
RGroupDecompositionParameters &params, const std::string &details_json) {
updateRGroupDecompositionParametersFromJSON(params, details_json.c_str());
}

void updateRGroupDecompositionParametersFromJSON(
RGroupDecompositionParameters &params, 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);

std::string labels;
labels = pt.get<std::string>("labels", labels);
if (RGroupLabels::_is_valid(labels.c_str())) {
params.labels = RGroupLabels::_from_string(labels.c_str());
}

std::string matchingStrategy;
matchingStrategy =
pt.get<std::string>("matchingStrategy", matchingStrategy);
if (RGroupMatching::_is_valid(matchingStrategy.c_str())) {
params.matchingStrategy =
RGroupMatching::_from_string(matchingStrategy.c_str());
}

std::string scoreMethod;
scoreMethod = pt.get<std::string>("scoreMethod", scoreMethod);
if (RGroupScore::_is_valid(scoreMethod.c_str())) {
params.scoreMethod = RGroupScore::_from_string(scoreMethod.c_str());
}

std::string rgroupLabelling;
rgroupLabelling = pt.get<std::string>("rgroupLabelling", rgroupLabelling);
if (RGroupLabelling::_is_valid(rgroupLabelling.c_str())) {
params.rgroupLabelling =
RGroupLabelling::_from_string(rgroupLabelling.c_str());
}

std::string alignment;
alignment = pt.get<std::string>("alignment", alignment);
if (RGroupCoreAlignment::_is_valid(alignment.c_str())) {
params.alignment = RGroupCoreAlignment::_from_string(alignment.c_str());
}

params.chunkSize = pt.get<unsigned int>("chunkSize", params.chunkSize);
params.onlyMatchAtRGroups =
pt.get<bool>("onlyMatchAtRGroups", params.onlyMatchAtRGroups);
params.removeAllHydrogenRGroups = pt.get<bool>(
"removeAllHydrogenRGroups", params.removeAllHydrogenRGroups);
params.removeAllHydrogenRGroupsAndLabels =
pt.get<bool>("removeAllHydrogenRGroupsAndLabels",
params.removeAllHydrogenRGroupsAndLabels);
params.removeHydrogensPostMatch = pt.get<bool>(
"removeHydrogensPostMatch", params.removeHydrogensPostMatch);
params.allowNonTerminalRGroups =
pt.get<bool>("allowNonTerminalRGroups", params.allowNonTerminalRGroups);
params.allowMultipleRGroupsOnUnlabelled =
pt.get<bool>("allowMultipleRGroupsOnUnlabelled",
params.allowMultipleRGroupsOnUnlabelled);
params.doTautomers = pt.get<bool>("doTautomers", params.doTautomers);
params.doEnumeration = pt.get<bool>("doEnumeration", params.doEnumeration);
params.includeTargetMolInResults = pt.get<bool>(
"includeTargetMolInResults", params.includeTargetMolInResults);
params.timeout = pt.get<double>("timeout", params.timeout);
}
}

} // end namespace RDKit
24 changes: 24 additions & 0 deletions Code/GraphMol/RGroupDecomposition/RGroupDecompJSONParsers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//
// 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 "RGroupDecompParams.h"

namespace RDKit {

RDKIT_RGROUPDECOMPOSITION_EXPORT void
updateRGroupDecompositionParametersFromJSON(
RGroupDecompositionParameters &params, const std::string &details_json);
RDKIT_RGROUPDECOMPOSITION_EXPORT void
updateRGroupDecompositionParametersFromJSON(
RGroupDecompositionParameters &params, const char *details_json);

} // end namespace RDKit
86 changes: 0 additions & 86 deletions Code/GraphMol/RGroupDecomposition/RGroupDecompParams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@
#include <GraphMol/FMCS/FMCS.h>
#include <GraphMol/QueryBond.h>
#include <set>
#include <RDGeneral/BoostStartInclude.h>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <RDGeneral/BoostEndInclude.h>

namespace RDKit {

Expand Down Expand Up @@ -73,88 +69,6 @@ bool hasAttachedLabels(const ROMol &mol, const Atom *atom,

} // namespace

void updateRGroupDecompositionParametersFromJSON(
RGroupDecompositionParameters &params, const std::string &details_json) {
updateRGroupDecompositionParametersFromJSON(params, details_json.c_str());
}

void updateRGroupDecompositionParametersFromJSON(
RGroupDecompositionParameters &params, const char *details_json) {
static const std::map<std::string, RGroupLabels> rGroupLabelsMap{
RGROUPLABELS_ENUM_ITEMS};
static const std::map<std::string, RGroupMatching> rGroupMatchingMap{
RGROUPMATCHING_ENUM_ITEMS};
static const std::map<std::string, RGroupLabelling> rGroupLabellingMap{
RGROUPLABELLING_ENUM_ITEMS};
static const std::map<std::string, RGroupCoreAlignment>
rGroupCoreAlignmentMap{RGROUPCOREALIGNMENT_ENUM_ITEMS};
static const std::map<std::string, RGroupScore> rGroupScoreMap{
RGROUPSCORE_ENUM_ITEMS};
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);

std::string labels;
labels = pt.get<std::string>("labels", labels);
auto rGroupLabelsMapIt = rGroupLabelsMap.find(labels);
if (rGroupLabelsMapIt != rGroupLabelsMap.end()) {
params.labels = rGroupLabelsMapIt->second;
}

std::string matchingStrategy;
matchingStrategy =
pt.get<std::string>("matchingStrategy", matchingStrategy);
auto rGroupMatchingMapIt = rGroupMatchingMap.find(matchingStrategy);
if (rGroupMatchingMapIt != rGroupMatchingMap.end()) {
params.matchingStrategy = rGroupMatchingMapIt->second;
}

std::string scoreMethod;
scoreMethod = pt.get<std::string>("scoreMethod", scoreMethod);
auto rGroupScoreMapIt = rGroupScoreMap.find(scoreMethod);
if (rGroupScoreMapIt != rGroupScoreMap.end()) {
params.scoreMethod = rGroupScoreMapIt->second;
}

std::string rgroupLabelling;
rgroupLabelling = pt.get<std::string>("rgroupLabelling", rgroupLabelling);
auto rGroupLabellingMapIt = rGroupLabellingMap.find(rgroupLabelling);
if (rGroupLabellingMapIt != rGroupLabellingMap.end()) {
params.rgroupLabelling = rGroupLabellingMapIt->second;
}

std::string alignment;
alignment = pt.get<std::string>("alignment", alignment);
auto rGroupCoreAlignmentMapIt = rGroupCoreAlignmentMap.find(alignment);
if (rGroupCoreAlignmentMapIt != rGroupCoreAlignmentMap.end()) {
params.alignment = rGroupCoreAlignmentMapIt->second;
}

params.chunkSize = pt.get<unsigned int>("chunkSize", params.chunkSize);
params.onlyMatchAtRGroups =
pt.get<bool>("onlyMatchAtRGroups", params.onlyMatchAtRGroups);
params.removeAllHydrogenRGroups = pt.get<bool>(
"removeAllHydrogenRGroups", params.removeAllHydrogenRGroups);
params.removeAllHydrogenRGroupsAndLabels =
pt.get<bool>("removeAllHydrogenRGroupsAndLabels",
params.removeAllHydrogenRGroupsAndLabels);
params.removeHydrogensPostMatch = pt.get<bool>(
"removeHydrogensPostMatch", params.removeHydrogensPostMatch);
params.allowNonTerminalRGroups =
pt.get<bool>("allowNonTerminalRGroups", params.allowNonTerminalRGroups);
params.allowMultipleRGroupsOnUnlabelled =
pt.get<bool>("allowMultipleRGroupsOnUnlabelled",
params.allowMultipleRGroupsOnUnlabelled);
params.doTautomers = pt.get<bool>("doTautomers", params.doTautomers);
params.doEnumeration = pt.get<bool>("doEnumeration", params.doEnumeration);
params.includeTargetMolInResults = pt.get<bool>(
"includeTargetMolInResults", params.includeTargetMolInResults);
params.timeout = pt.get<double>("timeout", params.timeout);
}
}

unsigned int RGroupDecompositionParameters::autoGetLabels(const RWMol &core) {
unsigned int autoLabels = 0;
if (!onlyMatchAtRGroups) {
Expand Down
94 changes: 41 additions & 53 deletions Code/GraphMol/RGroupDecomposition/RGroupDecompParams.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,59 +14,52 @@

#include "../RDKitBase.h"
#include <GraphMol/Substruct/SubstructMatch.h>
#include <RDGeneral/BetterEnums.h>

namespace RDKit {

#define RGROUPLABELS_ENUM_ITEMS \
RGD_ENUM_ITEM(IsotopeLabels, 1 << 0) \
RGD_ENUM_ITEM(AtomMapLabels, 1 << 1) \
RGD_ENUM_ITEM(AtomIndexLabels, 1 << 2) \
RGD_ENUM_ITEM(RelabelDuplicateLabels, 1 << 3) \
RGD_ENUM_ITEM(MDLRGroupLabels, 1 << 4) \
RGD_ENUM_ITEM(DummyAtomLabels, \
1 << 5) /* These are rgroups but will get relabelled */ \
RGD_ENUM_ITEM(AutoDetect, 0xFF)

#define RGROUPMATCHING_ENUM_ITEMS \
RGD_ENUM_ITEM(Greedy, 1 << 0) \
RGD_ENUM_ITEM(GreedyChunks, 1 << 1) \
RGD_ENUM_ITEM(Exhaustive, 1 << 2) /* not really useful for large sets */ \
RGD_ENUM_ITEM(NoSymmetrization, 1 << 3) \
RGD_ENUM_ITEM(GA, 1 << 4)

#define RGROUPLABELLING_ENUM_ITEMS \
RGD_ENUM_ITEM(AtomMap, 1 << 0) \
RGD_ENUM_ITEM(Isotope, 1 << 1) \
RGD_ENUM_ITEM(MDLRGroup, 1 << 2)

#define RGROUPCOREALIGNMENT_ENUM_ITEMS \
RGD_ENUM_ITEM(NoAlignment, 0) \
RGD_ENUM_ITEM(MCS, 1 << 0)

#define RGROUPSCORE_ENUM_ITEMS \
RGD_ENUM_ITEM(Match, 1 << 0) \
RGD_ENUM_ITEM(FingerprintVariance, 1 << 2)

#define RGD_ENUM_ITEM(k, v) k = v,
typedef enum { RGROUPLABELS_ENUM_ITEMS } RGroupLabels;

typedef enum { RGROUPMATCHING_ENUM_ITEMS } RGroupMatching;

typedef enum { RGROUPLABELLING_ENUM_ITEMS } RGroupLabelling;

typedef enum { RGROUPCOREALIGNMENT_ENUM_ITEMS } RGroupCoreAlignment;

typedef enum { RGROUPSCORE_ENUM_ITEMS } RGroupScore;
#undef RGD_ENUM_ITEM
#define RGD_STD_MAP_ITEM(k) {#k, k},
#define RGD_ENUM_ITEM(k, v) RGD_STD_MAP_ITEM(k)
BETTER_ENUM(RGroupLabels, unsigned int,
IsotopeLabels = 0x01,
AtomMapLabels = 0x02,
AtomIndexLabels = 0x04,
RelabelDuplicateLabels = 0x08,
MDLRGroupLabels = 0x10,
DummyAtomLabels = 0x20, // These are rgroups but will get relabelled
AutoDetect = 0xFF
);

BETTER_ENUM(RGroupMatching, unsigned int,
Greedy = 0x01,
GreedyChunks = 0x02,
Exhaustive = 0x04, // not really useful for large sets
NoSymmetrization = 0x08,
GA = 0x10
);

BETTER_ENUM(
RGroupLabelling, unsigned int,
AtomMap = 0x01,
Isotope = 0x02,
MDLRGroup = 0x04
);

BETTER_ENUM(RGroupCoreAlignment, unsigned int,
NoAlignment = 0x0,
MCS = 0x01
);

BETTER_ENUM(RGroupScore, unsigned int,
Match = 0x1,
FingerprintVariance = 0x4
);

struct RDKIT_RGROUPDECOMPOSITION_EXPORT RGroupDecompositionParameters {
unsigned int labels = AutoDetect;
unsigned int matchingStrategy = GreedyChunks;
unsigned int scoreMethod = Match;
unsigned int rgroupLabelling = AtomMap | MDLRGroup;
unsigned int alignment = MCS;
unsigned int labels = RGroupLabels::AutoDetect;
unsigned int matchingStrategy = RGroupMatching::GreedyChunks;
unsigned int scoreMethod = RGroupScore::Match;
unsigned int rgroupLabelling =
RGroupLabelling::AtomMap | RGroupLabelling::MDLRGroup;
unsigned int alignment = RGroupCoreAlignment::MCS;

unsigned int chunkSize = 5;
//! only allow rgroup decomposition at the specified rgroups
Expand Down Expand Up @@ -133,11 +126,6 @@ struct RDKIT_RGROUPDECOMPOSITION_EXPORT RGroupDecompositionParameters {
void checkNonTerminal(const Atom &atom) const;
};

void updateRGroupDecompositionParametersFromJSON(
RGroupDecompositionParameters &params, const std::string &details_json);
void updateRGroupDecompositionParametersFromJSON(
RGroupDecompositionParameters &params, const char *details_json);

} // namespace RDKit

#endif // RDKIT_RGROUPDECOMPPARAMS_H
Loading

0 comments on commit f66ad7e

Please sign in to comment.