Files
rdkit/Code/GraphMol/GaussianShape/Wrap/rdGaussianShape.cpp
David Cosgrove 9f551aedbe Multi conf gaussian shape (#9265)
* First import of GaussianShape.

* Tidying.

* Custom features.

* Optimise.

* Optimise.

* Return 3 scores rather than 2 including combo score.

* Rename useFeatures to useColors.

* Python wrappers.

* Python tests.

* Take out big test.

* Add new start mode, as PubChem does it.

* Doh!

* Fix MolTransforms eigenvalue return.

* Two cycle optimisation, mostly working.

* Take out bestSoFar score from SCA.

* Take out DTYPE.

* Tidy out redundant variables.

* Optimisation in 2 parts.

* More fiddling in pursuit of speed.

* Update Python wrapper.

* Tweak.

* Atom subsets and different radii.

* Fix test.

* Revert pubchem_shape's test.cpp.

* Serialize ShapeInput.

* Trigger build

* Remove pointers to std::arrays in ShapeInput.

* ShapeInput virtual d'tor.

* Precondition - ShapeInput needs a molecule with at least 1 conformer.

* Rename ShapeInput::d_centroid to ShapeInput::d_canonTrans.

* Fix normalization bugs.

* Select start mode using moments of inertia rather than eigenvalues of canonical transformation.

* Include color features in moments of inertia.

* Smidge faster.

* Tversky similarity.

* Tidy tests.

* Tests working on Linux.

* Revert force of right handed axes in MolTransforms::computePrincipalAxesAndMomentsFromGyrationMatrix replacing with a comment in the code.

* Response to review.

* Sneaky allCarbon bug.

* add multithreaded test

* Response to review.

* Doh! Don't recalculate normalization after every transformation.

* Re-instate d_normalizationOK.

* Re-name functions for fetching canonical transformations.

* Separate alpha from coords.

* MultiConf works with single conf extraction.

* Extract all conformations.
Max and best similarities.

* Renames d_currConformer to d_activeShape.

* Update shapeToMol.

* Update shapeToMol.

* Changes from synthon shape searching.

* Fix normalization of multiple confs.

* Update Python wrappers.

* Fix shape merge.

* Improve bestSimilarity.

* Fix python wrapper.

* Pull in changes from SynthonShapeSearch:
make pruneShapes public.
function to negate Alpha values.

* clang-tidy suggestions.

* clang-tidy suggestions.

* Bug in quaternion gradients - we now have only 3 coordinates.

* Tidy tests.

* Mac result slightly different.

* Multi conformer molecule alignment.

* Optionally return raw overlap volumes in score functions.

* Python wrappers for raw overlap volumes.

* Update Python wrapper ShapeInputOptions.

* Tidy for PR.

* Extra include file.

* Extra library

* Tidy forward declarations.

* Don't prune if threshold < 0.0.

* Windows exporty thing.

* Check SMILES on merge of ShapeInputs.

* PRECONDITION of SMILES on merge of ShapeInputs.

* Response to review - rename some functions.

* change how overlapVols is passed
add a test for it

* API suggestions

* Response to review.

* Remove debugging writes.

* Fix Python wrappers.

---------

Co-authored-by: David Cosgrove <david@cozchemix.co.uk>
Co-authored-by: greg landrum <greg.landrum@gmail.com>
2026-06-03 06:09:09 +02:00

739 lines
31 KiB
C++

//
// Copyright (C) 2026 David Cosgrove 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.
//
// Original author: David Cosgrove (CozChemIx Limited)
//
#include <string>
#include <boost/python.hpp>
#include <Geometry/point.h>
#include <GraphMol/ROMol.h>
#include <GraphMol/RWMol.h>
#include <GraphMol/GaussianShape/GaussianShape.h>
#include <GraphMol/GaussianShape/ShapeInput.h>
#include <GraphMol/GaussianShape/ShapeOverlayOptions.h>
#include <RDBoost/Wrap.h>
namespace python = boost::python;
namespace RDKit {
namespace helpers {
void set_customFeatures(GaussianShape::ShapeInputOptions &shp,
const python::object &s) {
shp.customFeatures.clear();
auto numVecs = python::len(s);
shp.customFeatures.reserve(numVecs);
for (auto i = 0u; i < numVecs; ++i) {
const auto outVec = s[i];
auto numFeats = python::len(outVec);
std::vector<GaussianShape::CustomFeature> feats;
feats.reserve(numFeats);
for (auto j = 0u; j < numFeats; ++j) {
const auto feat = outVec[j];
unsigned int featType = python::extract<unsigned int>(feat[0]);
RDGeom::Point3D pos = python::extract<RDGeom::Point3D>(feat[1]);
double radius = python::extract<double>(feat[2]);
std::vector<unsigned int> atoms;
if (len(feat) == 4) {
for (unsigned int k = 0; k < len(feat[3]); ++k) {
atoms.push_back(python::extract<unsigned int>(feat[3][k]));
}
}
feats.emplace_back(featType, pos, radius, atoms);
}
shp.customFeatures.emplace_back(std::move(feats));
}
}
python::tuple get_customFeatures(const GaussianShape::ShapeInputOptions &shp) {
python::list allFeatLists;
for (const auto &feats : shp.customFeatures) {
python::list featList;
for (const auto &feat : feats) {
python::list elem;
elem.append(static_cast<int>(feat.type));
elem.append(feat.pos);
elem.append(feat.rad);
elem.append(feat.atoms);
featList.append(elem);
}
allFeatLists.append(featList);
}
return python::tuple(allFeatLists);
}
python::tuple alignMol1(const ROMol &ref, ROMol &fit,
const python::object &py_refOpts,
const python::object &py_fitOpts,
const python::object &py_overlayOpts, int refConfId,
int fitConfId) {
GaussianShape::ShapeInputOptions refOpts, fitOpts;
if (!py_refOpts.is_none()) {
refOpts = python::extract<GaussianShape::ShapeInputOptions>(py_refOpts);
}
if (!py_fitOpts.is_none()) {
fitOpts = python::extract<GaussianShape::ShapeInputOptions>(py_fitOpts);
}
GaussianShape::ShapeOverlayOptions overlayOpts;
if (!py_overlayOpts.is_none()) {
overlayOpts =
python::extract<GaussianShape::ShapeOverlayOptions>(py_overlayOpts);
}
auto results = GaussianShape::AlignMolecule(
ref, fit, refOpts, fitOpts, nullptr, overlayOpts, refConfId, fitConfId);
return python::make_tuple(results[0], results[1], results[2]);
}
python::tuple alignMol2(const GaussianShape::ShapeInput &refShape, ROMol &fit,
const python::object &py_fitOpts,
const python::object &py_overlayOpts, int fitConfId) {
GaussianShape::ShapeInputOptions fitOpts;
if (!py_fitOpts.is_none()) {
fitOpts = python::extract<GaussianShape::ShapeInputOptions>(py_fitOpts);
}
GaussianShape::ShapeOverlayOptions overlayOpts;
if (!py_overlayOpts.is_none()) {
overlayOpts =
python::extract<GaussianShape::ShapeOverlayOptions>(py_overlayOpts);
}
auto results = GaussianShape::AlignMolecule(refShape, fit, fitOpts, nullptr,
overlayOpts, fitConfId);
return python::make_tuple(results[0], results[1], results[2]);
}
python::tuple alignShapes(const GaussianShape::ShapeInput &refShape,
GaussianShape::ShapeInput &fitShape,
const python::object &py_overlayOpts) {
GaussianShape::ShapeOverlayOptions overlayOpts;
if (!py_overlayOpts.is_none()) {
overlayOpts =
python::extract<GaussianShape::ShapeOverlayOptions>(py_overlayOpts);
}
RDGeom::Transform3D xform;
auto results =
GaussianShape::AlignShape(refShape, fitShape, &xform, overlayOpts);
python::list pyMatrix;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
pyMatrix.append(xform.getValUnchecked(i, j));
}
}
return python::make_tuple(results[0], results[1], results[2], pyMatrix);
}
python::tuple scoreMol1(const ROMol &ref, const ROMol &fit,
const python::object &py_refOpts,
const python::object &py_fitOpts,
const python::object &py_overlayOpts, int refConfId,
int fitConfId) {
GaussianShape::ShapeInputOptions refOpts, fitOpts;
if (!py_refOpts.is_none()) {
refOpts = python::extract<GaussianShape::ShapeInputOptions>(py_refOpts);
}
if (!py_fitOpts.is_none()) {
fitOpts = python::extract<GaussianShape::ShapeInputOptions>(py_fitOpts);
}
GaussianShape::ShapeOverlayOptions overlayOpts;
if (!py_overlayOpts.is_none()) {
overlayOpts =
python::extract<GaussianShape::ShapeOverlayOptions>(py_overlayOpts);
}
std::pair<double, double> ovVols;
auto results = GaussianShape::ScoreMolecule(
ref, fit, refOpts, fitOpts, overlayOpts, refConfId, fitConfId, &ovVols);
return python::make_tuple(results[0], results[1], results[2], ovVols.first,
ovVols.second);
}
python::tuple scoreMol2(const GaussianShape::ShapeInput &refShape,
const ROMol &fit, const python::object &py_fitOpts,
const python::object &py_overlayOpts, int fitConfId) {
GaussianShape::ShapeInputOptions fitOpts;
if (!py_fitOpts.is_none()) {
fitOpts = python::extract<GaussianShape::ShapeInputOptions>(py_fitOpts);
}
GaussianShape::ShapeOverlayOptions overlayOpts;
if (!py_overlayOpts.is_none()) {
overlayOpts =
python::extract<GaussianShape::ShapeOverlayOptions>(py_overlayOpts);
}
std::pair<double, double> ovVols;
auto results = GaussianShape::ScoreMolecule(refShape, fit, fitOpts,
overlayOpts, fitConfId, &ovVols);
return python::make_tuple(results[0], results[1], results[2], ovVols.first,
ovVols.second);
}
python::tuple scoreShape(const GaussianShape::ShapeInput &refShape,
const GaussianShape::ShapeInput &fitShape,
const python::object &py_overlayOpts) {
GaussianShape::ShapeOverlayOptions overlayOpts;
if (!py_overlayOpts.is_none()) {
overlayOpts =
python::extract<GaussianShape::ShapeOverlayOptions>(py_overlayOpts);
}
std::pair<double, double> ovVols;
auto results =
GaussianShape::ScoreShape(refShape, fitShape, overlayOpts, &ovVols);
return python::make_tuple(results[0], results[1], results[2], ovVols.first,
ovVols.second);
}
void set_atomSubset(GaussianShape::ShapeInputOptions &opts,
const python::object &as) {
pythonObjectToVect<unsigned int>(as, opts.atomSubset);
}
python::tuple get_atomSubset(const GaussianShape::ShapeInputOptions &opts) {
python::list py_list;
for (const auto &val : opts.atomSubset) {
py_list.append(val);
}
return python::tuple(py_list);
}
void set_atomRadii(GaussianShape::ShapeInputOptions &opts,
const python::object &ar) {
int len = python::len(ar);
opts.atomRadii.resize(len);
for (int i = 0; i < len; i++) {
unsigned int atomIdx = python::extract<unsigned int>(ar[i][0]);
double radius = python::extract<double>(ar[i][1]);
opts.atomRadii[i] = std::make_pair(atomIdx, radius);
}
}
python::tuple get_atomRadii(const GaussianShape::ShapeInputOptions &opts) {
python::list py_list;
for (const auto &val : opts.atomRadii) {
py_list.append(python::make_tuple(static_cast<int>(val.first), val.second));
}
return python::tuple(py_list);
}
double getShapeVolume_helper(const GaussianShape::ShapeInput &shape) {
return shape.getShapeVolume();
}
double getColorVolume_helper(const GaussianShape::ShapeInput &shape) {
return shape.getColorVolume();
}
python::tuple bestSimilarity_helper(GaussianShape::ShapeInput &refShape,
const GaussianShape::ShapeInput &fitShape,
double threshold,
const python::object &py_overlayOpts) {
GaussianShape::ShapeOverlayOptions overlayOpts;
if (!py_overlayOpts.is_none()) {
overlayOpts =
python::extract<GaussianShape::ShapeOverlayOptions>(py_overlayOpts);
}
unsigned int bestFitShape, bestThisShape;
RDGeom::Transform3D bestXform;
auto bestSim = refShape.bestSimilarity(fitShape, bestThisShape, bestFitShape,
bestXform, threshold, overlayOpts);
python::list results;
results.append(python::make_tuple(bestSim[0], bestSim[1], bestSim[2]));
results.append(bestThisShape);
results.append(bestFitShape);
python::list pyMatrix;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
pyMatrix.append(bestXform.getValUnchecked(i, j));
}
}
results.append(pyMatrix);
return python::tuple(results);
}
double maxPossibleSimilarity_helper(GaussianShape::ShapeInput &refShape,
GaussianShape::ShapeInput &fitShape,
const python::object &py_overlayOpts) {
GaussianShape::ShapeOverlayOptions overlayOpts;
if (!py_overlayOpts.is_none()) {
overlayOpts =
python::extract<GaussianShape::ShapeOverlayOptions>(py_overlayOpts);
}
return refShape.maxPossibleSimilarity(fitShape, overlayOpts);
}
ROMol *shapeToMol_helper(GaussianShape::ShapeInput &shape, bool includeColors,
bool withBonds) {
auto mol = shape.shapeToMol(includeColors, withBonds);
return static_cast<ROMol *>(mol.release());
}
python::tuple scoreMolAllConfs_helper(const ROMol &ref, const ROMol &fit,
const python::object &py_refOpts,
const python::object &py_fitOpts,
const python::object &py_overlayOpts) {
python::list results;
GaussianShape::ShapeInputOptions refOpts, fitOpts;
if (!py_refOpts.is_none()) {
refOpts = python::extract<GaussianShape::ShapeInputOptions>(py_refOpts);
}
if (!py_fitOpts.is_none()) {
fitOpts = python::extract<GaussianShape::ShapeInputOptions>(py_fitOpts);
}
GaussianShape::ShapeOverlayOptions overlayOpts;
if (!py_overlayOpts.is_none()) {
overlayOpts =
python::extract<GaussianShape::ShapeOverlayOptions>(py_overlayOpts);
}
std::vector<std::vector<double>> combScores;
int bestRefConf, bestFitConf;
RDGeom::Transform3D bestXform;
GaussianShape::ScoreMoleculeAllConformers(ref, fit, bestRefConf, bestFitConf,
combScores, refOpts, fitOpts,
overlayOpts, &bestXform);
python::list pyScores;
for (const auto &scores : combScores) {
python::list s;
for (const auto &score : scores) {
s.append(score);
}
pyScores.append(s);
}
results.append(pyScores);
results.append(bestRefConf);
results.append(bestFitConf);
python::list pyMatrix;
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
pyMatrix.append(bestXform.getValUnchecked(i, j));
}
}
results.append(pyMatrix);
return python::tuple(results);
}
} // namespace helpers
void wrap_rdGaussianShape() {
python::scope().attr("__doc__") =
"Module containing implementation of Gaussian-based shape overlay and"
" scoring."
"NOTE: This functionality is experimental and the API"
" and/or results may change in future releases.";
python::enum_<RDKit::GaussianShape::StartMode>("StartMode")
.value("ROTATE_0", RDKit::GaussianShape::StartMode::ROTATE_0)
.value("ROTATE_180", RDKit::GaussianShape::StartMode::ROTATE_180)
.value("ROTATE_180_WIGGLE",
RDKit::GaussianShape::StartMode::ROTATE_180_WIGGLE)
.value("ROTATE_45", RDKit::GaussianShape::StartMode::ROTATE_45)
.value("ROTATE_0_FRAGMENT",
RDKit::GaussianShape::StartMode::ROTATE_0_FRAGMENT)
.value("ROTATE_180_FRAGMENT",
RDKit::GaussianShape::StartMode::ROTATE_180_FRAGMENT)
.value("ROTATE_45_FRAGMENT",
RDKit::GaussianShape::StartMode::ROTATE_45_FRAGMENT)
.value("A_LA_PUBCHEM", GaussianShape::StartMode::A_LA_PUBCHEM)
.export_values();
python::enum_<RDKit::GaussianShape::OptimMode>("OptimMode")
.value("SHAPE_ONLY", RDKit::GaussianShape::OptimMode::SHAPE_ONLY)
.value("SHAPE_PLUS_COLOR_SCORE",
RDKit::GaussianShape::OptimMode::SHAPE_PLUS_COLOR_SCORE)
.value("SHAPE_PLUS_COLOR",
RDKit::GaussianShape::OptimMode::SHAPE_PLUS_COLOR)
.export_values();
python::class_<GaussianShape::ShapeInputOptions, boost::noncopyable>(
"ShapeInputOptions",
"ShapeInputOptions - options for setting up ShapeInput objects.")
.def_readwrite("useColors", &GaussianShape::ShapeInputOptions::useColors,
"Whether to use color features in overlay. Default=True.")
.def_readwrite(
"allCarbonRadii", &GaussianShape::ShapeInputOptions::allCarbonRadii,
"Whether to use the same radius, appropriate for Carbon, for all atoms. There is a"
" slight accuracy penalty but significant speed gain if used. Default=True.")
.add_property(
"atomSubset", &helpers::get_atomSubset, &helpers::set_atomSubset,
"If not empty, use just these atoms in the molecule to form the ShapeInput object.")
.add_property(
"customFeatures", &helpers::get_customFeatures,
&helpers::set_customFeatures,
"Custom features for the shape. Requires a list of lists of tuples of"
" int (the feature type), Point3D (the coordinates), float (the radius)"
" and optionally a list of indices of the atoms that the feature was derived from.")
.add_property(
"atomRadii", &helpers::get_atomRadii, &helpers::set_atomRadii,
"Non-standard radii to use for the atoms specified by their indices"
" in the molecule. Not all atoms need have a radius specified."
" A list of tuples of [int, float].")
.def_readwrite(
"shapePruneThreshold",
&GaussianShape::ShapeInputOptions::shapePruneThreshold,
"If there is more than 1 conformer for the input molecule, prune the"
" shapes so that none of them are more similar to each other than the"
" threshold. Default -1.0 means no pruning.")
.def_readwrite(
"sortShapes", &GaussianShape::ShapeInputOptions::sortShapes,
"If True (the default), the shapes are sorted into descending order"
" of total volume.")
.def_readwrite(
"includeDummies", &GaussianShape::ShapeInputOptions::includeDummies,
"Whether to include dummy atoms in the shape or not. Default=True.")
.def("__setattr__", &safeSetattr);
python::class_<GaussianShape::ShapeOverlayOptions, boost::noncopyable>(
"ShapeOverlayOptions",
"ShapeOverlayOptions - options for controlling the shape overlay process.")
.def_readwrite(
"startMode", &RDKit::GaussianShape::ShapeOverlayOptions::startMode,
"Start modes for optimisation. Default is A_LA_PUBCHEM - as used by the"
" PubChem code - either ROTATE_180_WIGGLE or ROTATE_45 depending on the shape"
" of the two molecules. ROTATE_180_WIGGLE means 180 rotations about"
" the x, y and z axes, then a small"
" rotation about each axis from that point, using the best scoring one of"
" those. ROTATE_180 uses 180 degree rotations for 4 start points,"
" ROTATE_45 uses 45 degree rotations for 9 start points and ROTATE_0"
" leaves the relative orientations of the 2 molecules as passed in before"
" optimisation. There are also ROTATE_0_FRAGMENT, ROTATE_45_FRAGMENT"
" and ROTATE_180_FRAGMENT that as well as the above move the fit"
" molecule to the ends of each of the principal axes and then does"
" the appropriate rotations. This is useful when the fit molecule is"
" a lot smaller than the reference molecule, but requires a large number"
" of optimisations so is relatively slow.")
.def_readwrite(
"optimMode", &GaussianShape::ShapeOverlayOptions::optimMode,
"Optimisation mode, controlling what parameters are used"
" to drive the overlay. Default=SHAPE_PLUS_COLOR_SCORE which"
" optimises using just the overlap of shape, but uses the"
" color to decide which is the best overlay. Other options"
" are SHAPE_ONLY and SHAPE_AND_COLOR with the latter using"
" the overlap of color features as well. ")
.def_readwrite(
"simAlpha", &GaussianShape::ShapeOverlayOptions::simAlpha,
"When doing a Tversky similarity, the alpha value. If alpha and"
" beta are both the default 1.0, it's a Tanimoto similarity. A"
" high alpha and low beta emphasize the fit volume in the"
" similarity and vice versa. Tversky is O / (A * (R - O) + B * (F"
" - O) + O) where O is the overlap volume, R is the reference's"
" volume and F is the fit's volume. This is different from that"
" used by OpenEye (O / (A * R + B * F)).")
.def_readwrite("simBeta", &GaussianShape::ShapeOverlayOptions::simBeta,
"When doing a Tversky similarity, the beta value.")
.def_readwrite(
"optParam", &GaussianShape::ShapeOverlayOptions::optParam,
"If using colors, the relative weights of the shape and color scores,"
" as a fraction of 1. Default=0.5.")
.def_readwrite(
"nSteps", &GaussianShape::ShapeOverlayOptions::nSteps,
"Maximum number of steps for the shape overlay process. Default=100.")
.def_readwrite(
"normalize", &GaussianShape::ShapeOverlayOptions::normalize,
"Whether to normalize the shapes before overlay by putting them into their"
" canonical orientation (centred on the origin, aligned along its"
" principal axes. Default=True.")
.def_readwrite(
"useDistCutoff", &GaussianShape::ShapeOverlayOptions::useDistCutoff,
"Whether to use distance cutoff when calculating the shape volumes. If used,"
" there will be a small penalty in accuracy but a significant increase in speed."
" Default=True.")
.def_readwrite(
"distCutoff", &GaussianShape::ShapeOverlayOptions::distCutoff,
"If using a distance cutoff, this is the value used. Default=4.5 of whatever"
" units the coordinates are in.")
.def_readwrite(
"shapeConvergenceCriterion",
&GaussianShape::ShapeOverlayOptions::shapeConvergenceCriterion,
"Optimisation stops when the shape Tversky score changes by less"
" than this amount after an optimisation step. A larger number is"
" faster but gives less precise overlays. Default=0.001.")
.def("__setattr__", &safeSetattr);
std::string docString("ShapeInput object");
python::class_<GaussianShape::ShapeInput, boost::noncopyable>(
"ShapeInput", docString.c_str(),
python::init<const ROMol &, int, const GaussianShape::ShapeInputOptions &,
const GaussianShape::ShapeOverlayOptions &>(
python::args("self", "confId", "shapeOpt", "overlayOpts")))
.add_property(
"GetSmiles", &GaussianShape::ShapeInput::getSmiles,
"Get the SMILES string for the molecule that the shape relates to.")
.add_property(
"setActiveShape", &GaussianShape::ShapeInput::setActiveShape,
"Set the active shape, the one that will be used for overlays etc.")
.add_property("getActiveShape",
&GaussianShape::ShapeInput::getActiveShape,
"Return the number of the active shape.")
.add_property("NumAtoms", &GaussianShape::ShapeInput::getNumAtoms,
"Get the number of atoms defining the shape.")
.add_property("NumFeatures", &GaussianShape::ShapeInput::getNumFeatures,
"Get the number of features in the shape.")
.add_property(
"NumShapes", &GaussianShape::ShapeInput::getNumShapes,
"Get the number of shapes. There will be a shape for each conformation "
"of the input molecule, unless shape pruning was carried out in which case"
" there may be fewer.")
.add_property("ShapeVolume", &helpers::getShapeVolume_helper,
"Get the volume due to the atoms for the active shape.")
.add_property(
"ColorVolume", &helpers::getColorVolume_helper,
"Get the volume of the shape's color features for the active shap.")
.def(
"NormalizeCoords", &GaussianShape::ShapeInput::normalizeCoords,
"Align the principal axes to the cartesian axes and centre on the origin."
" Doesn't require that the shape was created from a molecule. Creates"
" the necessary transformation if not already done.")
.def(
"ShapeToMol", &helpers::shapeToMol_helper,
(python::arg("self"), python::arg("includeColors") = false,
python::arg("withBonds") = true),
"Return a molecule with coordinates of the current active shape."
" If includeColors is True, (default is False) the color features"
" will be added as xenon atoms. If withBonds is True (the default)"
" a molecule with bonds will be created, if not then just atoms at the"
" appropriate positions will be produced.",
python::return_value_policy<python::manage_new_object>())
.def(
"BestSimilarity", &helpers::bestSimilarity_helper,
(python::arg("self"), python::arg("fitShape"),
python::arg("threshold") = -1.0,
python::arg("overlayOpts") = python::object()),
"Find the best similarity score between all shapes in this shape and the"
" other one. Stops as soon as it gets something above the threshold."
" The score runs between 0.0 and 1.0, so the default threshold of -1.0"
" means no threshold. Fills in the shape numbers of the two that were"
" responsible if there is something above the threshold, and the"
" transformation that did it. Returns a tuple of the similarity scores"
" ((-1.0, -1.0, -1.0) if there was nothing above the threshold), the number of the"
" shape for this object and the shape number of the fitShape that gave"
" the best similarity and the transformation matrix (as a list of 16 floats)"
" that will reproduce the best overlay. The shapes won't necessarily"
" be left in the state that gave the best similarity. Note that the"
" shape numbers are not necessarily the same as the original molecule"
" conformation numbers.")
.def("MaxPossibleSimilarity", &helpers::maxPossibleSimilarity_helper,
(python::arg("self"), python::arg("fitShape"),
python::arg("overlayOpts") = python::object()),
"Get the maximum possible similarity score between all shapes in"
" this shape and all shapes in the fitShape. The maximum similarity"
" is when one shape is entirely inside the other. This returns"
" the similarity in that case, which is the upper bound on what"
" is achievable between these 2 shapes.")
.def("__setattr__", &safeSetattr);
python::def(
"AlignMol", &helpers::alignMol1,
(python::arg("ref"), python::arg("fit"),
python::arg("refOpts") = python::object(),
python::arg("fitOpts") = python::object(),
python::arg("overlayOpts") = python::object(),
python::arg("refConfId") = -1, python::arg("fitConfId") = -1),
R"DOC(Aligns a fit molecule onto a reference molecule. The fit is modified.
Parameters
----------
ref: RDKit.ROMol
Reference molecule
fit: RDKit.ROMol
Fit molecule that will be overlaid
refOpts: ShapeInputOptions, optional
Options for building the ref shape
fitOpts: ShapeInputOptions, optional
Options for building the fit shape
overlayOpts: ShapeOverlayOptions, optional
Options for controlling the overlay
refConfId : int, optional
Reference conformer ID (default is -1)
fitConfId : int, optional
fit conformer ID (default is -1)
Returns
-------
3-tuple of floats
The results are (combo_score, shape_score, color_score). The color_score is
0.0 if color features not used, in which case combo_score and shape_score will
be the same.
)DOC");
python::def(
"AlignMol", &helpers::alignMol2,
(python::arg("refShape"), python::arg("fit"),
python::arg("fitOpts") = python::object(),
python::arg("overlayOpts") = python::object(),
python::arg("fitConfId") = -1),
R"DOC(Aligns a fit molecule onto a reference shape. The fit is modified.
Parameters
----------
refShape: ShapeInput
Reference shape
fit: RDKit.ROMol
Fit molecule that will be overlaid
fitOpts: ShapeInputOptions, optional
Options for building the fit shape
overlayOpts: ShapeOverlayOptions, optional
Options for controlling the overlay
fitConfId : int, optional
Fit conformer ID (default is -1)
Returns
-------
3-tuple of floats
The results are (combo_score, shape_score, color_score). The color_score is
0.0 if color features not used, in which case combo_score and shape_score will
be the same.)DOC");
python::def(
"AlignShapes", &helpers::alignShapes,
(python::arg("refShape"), python::arg("fitShape"),
python::arg("overlayOpts") = python::object()),
R"DOC(Aligns a fit shape to a reference shape. The fit is modified.
Parameters
----------
refShape : ShapeInput
Reference shape
fitShape : ShapeInput
fit shape
overlayOpts: ShapeOverlayOptions, optional
Options for controlling the overlay
Returns
-------
4-tuple of float, float, list of floats
The results are (combo_score, shape_score, color_score, matrix)
The matrix is a 16-float list giving the transformation matrix that
overlays the fit onto the reference.)DOC");
python::def("ScoreMol", &helpers::scoreMol1,
(python::arg("ref"), python::arg("fit"),
python::arg("refOpts") = python::object(),
python::arg("fitOpts") = python::object(),
python::arg("overlayOpts") = python::object(),
python::arg("refConfId") = -1, python::arg("fitConfId") = -1),
R"DOC(Calculates the scores between a reference molecule and a fit
molecule without overlay.
Parameters
----------
ref: RDKit.ROMol
Reference molecule
fit: RDKit.ROMol
Fit molecule that will be scored
refOpts: ShapeInputOptions, optional
Options for building the ref shape
fitOpts: ShapeInputOptions, optional
Options for building the fit shape
overlayOpts: ShapeOverlayOptions, optional
Options for controlling the volume calculation
refConfId : int, optional
Reference conformer ID (default is -1)
fitConfId : int, optional
fit conformer ID (default is -1)
Returns
-------
3-tuple of floats
The results are (combo_score, shape_score, color_score). The color_score is
0.0 if color features not used, in which case combo_score and shape_score will
be the same.
)DOC");
python::def(
"ScoreMol", &helpers::scoreMol2,
(python::arg("refShape"), python::arg("fit"),
python::arg("fitOpts") = python::object(),
python::arg("overlayOpts") = python::object(),
python::arg("fitConfId") = -1),
R"DOC(Calculates the scores between a reference shape and a fit molecule
without overlay.
Parameters
----------
refShape: ShapeInput
Reference shape
fit: RDKit.ROMol
Fit molecule that will be scored
fitOpts: ShapeInputOptions, optional
Options for building the fit shape
overlayOpts: ShapeOverlayOptions, optional
Options for controlling the volume calculation
fitConfId : int, optional
fit conformer ID (default is -1)
Returns
-------
3-tuple of floats
The results are (combo_score, shape_score, color_score). The color_score is
0.0 if color features not used, in which case combo_score and shape_score will
be the same.
)DOC");
python::def(
"ScoreShape", &helpers::scoreShape,
(python::arg("refShape"), python::arg("fitShape"),
python::arg("overlayOpts") = python::object()),
R"DOC(Calculates the scores between a reference shape and a fit shape without
overlay.
Parameters
----------
refShape: ShapeInput
Reference shape
fitShape: ShapeInput
Fit shape
fitOpts: ShapeInputOptions, optional
Options for building the fit shape
overlayOpts: ShapeOverlayOptions, optional
Options for controlling the volume calculation
Returns
-------
3-tuple of floats
The results are (combo_score, shape_score, color_score). The color_score is
0.0 if color features not used, in which case combo_score and shape_score will
be the same.
)DOC");
python::def("ScoreMoleculeAllConformers", &helpers::scoreMolAllConfs_helper,
(python::arg("ref"), python::arg("fit"),
python::arg("refOpts") = python::object(),
python::arg("fitOpts") = python::object(),
python::arg("overlayOpts") = python::object()),
R"DOC(Calculate the scores for the alignment of all conformers
of the fit molecule onto the reference. The molecules themselves are not
altered.
Parameters
----------
ref: RDKit.ROMol
Reference molecule
fit: RDKit.ROMol
Fit molecule that will be scored
refOpts: ShapeInputOptions, optional
Options for building the ref shape
fitOpts: ShapeInputOptions, optional
Options for building the fit shape
overlayOpts: ShapeOverlayOptions, optional
Options for controlling the volume calculation
Returns
-------
A complex tuple containing:
A tuple of tuples containing the scores from aligning the fit conformations
onto the reference conformations. scores[0][1] is the score of aligning
fit conformation 1 onto ref conformation 0.
The ID of the ref conformer from the best-scoring alignment
The ID of the fit conformer from the best-scoring alignment
The transformation that gives the best-scoring alignment for those
conformers as a 16-float tuple.
)DOC");
}
BOOST_PYTHON_MODULE(rdGaussianShape) { wrap_rdGaussianShape(); }
} // namespace RDKit