RDKit
Open-source cheminformatics and machine learning.
Loading...
Searching...
No Matches
ShapeInput.h
Go to the documentation of this file.
1//
2// Copyright (C) 2026 David Cosgrove and other RDKit contributors
3//
4// @@ All Rights Reserved @@
5// This file is part of the RDKit.
6// The contents are covered by the terms of the BSD license
7// which is included in the file license.txt, found at the root
8// of the RDKit source tree.
9//
10// Original author: David Cosgrove (CozChemIx Limited)
11//
12
13#ifndef RDKIT_SHAPEINPUT_GUARD
14#define RDKIT_SHAPEINPUT_GUARD
15
16#include <array>
17#include <vector>
18
19#include <GraphMol/RWMol.h>
20#include <RDGeneral/export.h>
22
24#include <boost/dynamic_bitset.hpp>
25#ifdef RDK_USE_BOOST_SERIALIZATION
26#include <boost/archive/text_oarchive.hpp>
27#include <boost/archive/text_iarchive.hpp>
28#include <boost/serialization/vector.hpp>
29#include <boost/serialization/array.hpp>
30#include <boost/serialization/unique_ptr.hpp>
31#endif
33
35
36// The code below was provided by Claude (Sonnet 4.6).
37// If first tried to get me to use boost/serialization/dynamic_bitset.hpp
38// and then admitted that it had made that up.
39namespace boost {
40namespace serialization {
41
42template <class Archive, typename Block, typename Allocator>
43void serialize(Archive &ar, dynamic_bitset<Block, Allocator> &bs,
44 const unsigned int /*version*/) {
45 size_t num_bits = bs.size();
46 ar & num_bits;
47
48 std::vector<Block> blocks;
49
50 if (Archive::is_saving::value) {
51 to_block_range(bs, std::back_inserter(blocks));
52 }
53
54 ar & blocks;
55
56 if (Archive::is_loading::value) {
57 bs.resize(num_bits);
58 from_block_range(blocks.begin(), blocks.end(), bs);
59 bs.resize(num_bits); // trim any excess bits
60 }
61}
62
63} // namespace serialization
64} // namespace boost
65
66namespace RDKit {
67class ROMol;
68class Conformer;
69namespace GaussianShape {
70
71constexpr double CARBON_RAD = 1.70;
72constexpr double DUMMY_RAD = 2.16; // same as Xe
73// From Grant et al.
74constexpr double P = 2.7;
75constexpr double KAPPA = 2.41798793102;
76
79 const unsigned int t, const RDGeom::Point3D &p, const double r,
80 const std::vector<unsigned int> &a = std::vector<unsigned int>())
81 : type(t), pos(p), rad(r), atoms(a) {}
82 unsigned int type;
84 double rad;
85 std::vector<unsigned int>
86 atoms; // That the feature was derived from. May be left empty.
87};
88
90 ShapeInputOptions() = default;
95
96 ~ShapeInputOptions() = default;
97
99 true}; //! Whether to build the color features. By default, it will
100 //! create features using the RDKit pharmacophore definitions.
101
102 std::vector<std::vector<CustomFeature>>
103 customFeatures; //! Custom color features used verbatim. One outer
104 //! vector for each conformation in the molecule.
105 std::vector<unsigned int>
106 atomSubset; //! If not empty, use just these atoms in the molecule to
107 //! form the ShapeInput object.
108 std::vector<std::pair<unsigned int, double>>
109 atomRadii; //! Use these non-standard radii for these atoms. The int is
110 //! for the atom index in the molecule, not the atomic
111 //! number. Not all atoms need be specified; some radii
112 //! can be over-ridden, with the rest left as standard.
114 true}; //! Whether to use carbon radii for all atoms (which is quicker
115 //! but less accurate) or vdw radii appropriate for the elements.
116 double shapePruneThreshold{-1.0}; //! If there is more than 1 conformer for
117 //! the input molecule, prune the shapes so
118 //! that none of them are more similar to
119 //! each other than the threshold. Default
120 //! -1.0 means no pruning.
121 bool sortShapes{true}; //! If true, the shapes are sorted in descending order
122 //! of total volume.
123 bool includeDummies{true}; //! Whether to include dummy atoms in the shape
124 //! or not.
125};
126
127// Data for shape alignment code
129 public:
130 //! Create the ShapeInput object.
131 //! @param mol: The molecule of interest
132 //! @param confId: The conformer to use. If -1, uses all conformers.
133 //! @param opts: Options for setting up the shape
134 //! @param overlayOpts: Options for controlling overlays. The distance cutoff
135 //! elements are used in the self-overlap calculations.
136 explicit ShapeInput(
137 const ROMol &mol, int confId = -1,
138 const ShapeInputOptions &opts = ShapeInputOptions(),
139 const ShapeOverlayOptions &overlayOpts = ShapeOverlayOptions());
140 //! Create a ShapeInput object with a single shape copied from
141 //! other.
142 //! @param other: the ShapeInput that supplies the shape
143 //! @param shapeNum: the number of the shape of interest.
144 ShapeInput(const ShapeInput &other, unsigned int shapeNum);
145 explicit ShapeInput(const std::string &str) {
146#ifndef RDK_USE_BOOST_SERIALIZATION
147 PRECONDITION(0, "Boost SERIALIZATION is not enabled")
148#else
149 std::stringstream ss(str);
150 boost::archive::text_iarchive ia(ss);
151 ia &*this;
152#endif
153 }
154 ShapeInput(const ShapeInput &other);
155 ShapeInput(ShapeInput &&other) = default;
157 ShapeInput &operator=(ShapeInput &&other) = default;
158 ~ShapeInput() = default;
159
160 //! Merge the other ShapeInput, assuming it has the correct number
161 //! of atoms etc. Empties other, unless they can't be merged in which case
162 //! it returns unscathed. The merge can only be done if other has the same
163 // number of coordinates per conformer, and the feature types of the two
164 // match.
165 void merge(ShapeInput &&other);
166
167 std::string toString() const {
168#ifndef RDK_USE_BOOST_SERIALIZATION
169 PRECONDITION(0, "Boost SERIALIZATION is not enabled")
170#else
171 std::stringstream ss;
172 boost::archive::text_oarchive oa(ss);
173 oa &*this;
174 return ss.str();
175#endif
176 }
177
178 const std::string getSmiles() const { return d_smiles; }
179 unsigned int getActiveShape() const { return d_activeShape; }
180 //! Set the currently active conformation to the new value.
181 //! @param newShape: the number of the conformation to be used
182 //! for future calculations. Counts from 0,
183 //! obviously. If invalid, throws a runtime
184 //! error.
185 void setActiveShape(unsigned int newShape);
186 //! Return the coordinates of the currently active shape.
187 //! Note that the coords are returned as a vector size 3*getNumAtoms()
188 const std::vector<double> &getCoords() const {
189 return d_coords[d_activeShape];
190 }
191 //! Get the alpha values for the atoms and color features in the shape.
192 const std::vector<double> &getAlphas() const { return d_alphas; }
193 //! Multiply the alpha value for the given atom/feature by -1.0
194 //! which will toggle whether the atom/feature is used in the volume
195 //! calculation or not. For temporarily "turning off" an atom or feature.
196 void negateAlpha(unsigned int alphaNum);
197
198 //! Fetch the coordinates of the atoms and optionally features.
199 std::vector<RDGeom::Point3D> getAtomPoints(bool includeColors = false) const;
200 //! Return whether the coordinates for the current active shape are
201 //! normalized.
202 bool getIsNormalized() const { return d_normalizeds[d_activeShape]; }
203 //! Return the feature types of all atoms/features in the shape. Atoms
204 //! have type 0.
205 const std::vector<int> &getFeatureTypes() const { return d_types; }
206 //! Get the number of atoms in the shape.
207 unsigned int getNumAtoms() const { return d_numAtoms; }
208 //! Get the number of color features in the shape.
209 unsigned int getNumFeatures() const { return d_numFeats; }
210 //! Get the number of shapes/conformations in the shape object. This may
211 //! be smaller than the number of conformations in the input molecule if
212 //! shape pruning was performed.
213 unsigned int getNumShapes() const { return d_coords.size(); }
214 //! Get the volume of the atoms in the current active shape.
215 double getShapeVolume() const {
216 return d_selfOverlapShapeVols[d_activeShape];
217 }
218 //! Get the volume for the atoms for the given shape number.
219 double getShapeVolume(unsigned int shapeNum) const;
220 //! Get the volume of the color features in the current active shape.
221 double getColorVolume() const {
222 return d_selfOverlapColorVols[d_activeShape];
223 }
224 //! Get the volume of the color features for the given shape number.
225 double getColorVolume(unsigned int shapeNum) const;
226 //! Get the flags for which atoms have a carbon radius.
227 const boost::dynamic_bitset<> *getCarbonRadii() const {
228 return d_carbonRadii.get();
229 }
230 // These functions use cached values if available.
231 //! Get the canonical rotation for the current active shape.
232 const std::array<double, 9> &calcCanonicalRotation();
233 //! Get the canonical translation for the current active shape.
234 const std::array<double, 3> &calcCanonicalTranslation();
235 //! Get the eigen values for the coordinates matrix.
236 const std::array<double, 3> &calcEigenValues();
237 //! Get the numbers of the points at the extremes of x, y and z for the
238 //! current active shape. In the order minimum x, minimum y, minimum z,
239 //! then the maxima.
240 const std::array<size_t, 6> &calcExtremes();
241 //! Return the principal moments of inertia, if Eigen3 is available, and the
242 //! eigenvalues of the canonical transformation if not, for the current
243 //! active shape.
244 std::array<double, 3> calcMomentsOfInertia(bool includeColors = false) const;
245
246 //! Align the principal axes to the cartesian axes and centre on the origin
247 //! for the current active shape.
248 //! Doesn't require that the shape was created from a molecule. Creates
249 //! the necessary transformation if not already done.
251
252 //! Applies the given transformation to the current active shape.
254
255 //! Make a molecule from the current active shape. If required, features
256 //! are added as xenon atoms. If withBonds is false, just makes a molecule
257 //! from the atoms, otherwise builds a full molecule.
258 std::unique_ptr<RWMol> shapeToMol(bool includeColors = false,
259 bool withBonds = true) const;
260
261 //! Find the best similarity score between all shapes in this shape and the
262 //! other one. Stops as soon as it gets something above the threshold.
263 //! The score runs between 0.0 and 1.0, so the default threshold of -1.0
264 //! means no threshold. Fills in the shape numbers of the two that were
265 //! responsible if there is something above the threshold, and the
266 //! transformation that did it. Returns -1.0 for the similarity if there was
267 //! nothing above the threshold. Note that the shape numbers are not
268 //! necessarily the same as the original molecule conformation numbers.
269 std::array<double, 3> bestSimilarity(
270 const ShapeInput &fitShape, unsigned int &bestThisShape,
271 unsigned int &bestFitShape, RDGeom::Transform3D &bestXform,
272 double threshold = -1.0,
273 const ShapeOverlayOptions &overlayOpts = ShapeOverlayOptions());
274
275 //! Return the maximum similarity achievable between the 2 shapes. The
276 //! maximum similarity is when one shape is entirely inside the other. This
277 //! returns the similarity in that case, which is the upper bound on what
278 //! is achievable between these 2 shapes.
280 const ShapeInput &fitShape,
281 const ShapeOverlayOptions &overlayOpts = ShapeOverlayOptions()) const;
282
283 //! Prune the shapes so none a more similar to each other than
284 //! the threshold.
285 void pruneShapes(double simThreshold);
286
287#ifdef RDK_USE_BOOST_SERIALIZATION
288 template <class Archive>
289 void serialize(Archive &ar, unsigned int);
290#endif
291
292 private:
293 void extractAtoms(const Conformer &conf, const ShapeInputOptions &shapeOpts,
294 bool fillAlphas);
295 // Extract the features for the color scores, using RDKit pphore features
296 // for now. Other options to be added later.
297 void extractFeatures(const Conformer &conf, unsigned int confNum,
298 const ShapeInputOptions &shapeOpts, bool fillAlphas);
299 // Calculate the rotation and translation that will align the principal axes
300 // to the cartesian axes and centre on the origin.
301 void calcNormalization();
302
303 void calculateExtremes();
304
305 unsigned int d_activeShape;
306
307 std::vector<std::vector<double>>
308 d_coords; // The coordinates for the atoms and features,
309 // packed as 3 floats per item - x, y, z
310 std::vector<double> d_alphas; // The alpha values for the atoms and features.
311 // alpha is KAPPA / (r * r) where r is the radius
312 // of the atom. This is not used if using all_atoms_carbon mode.
313 std::vector<int> d_types; // The feature types. The size is the same
314 // as the number of atoms and features, padded with 0 for the atoms.
315 unsigned int d_numAtoms; // The number of atoms
316 unsigned int d_numFeats; // The number of features
317 std::vector<double> d_selfOverlapShapeVols; // Shape volume
318 std::vector<double> d_selfOverlapColorVols; // Color volume
319 // These are the points at the extremes of the x, y and z axes.
320 // they are min_x, min_y, min_z and max_x, max_y, max_z.
321 std::vector<std::array<size_t, 6>> d_extremePointss;
322 std::unique_ptr<boost::dynamic_bitset<>>
323 d_carbonRadii; // Flags those atoms with a carbon radius, for faster
324 // calculation later.
325 std::string d_smiles; // The SMILES string of the input molecule
326
327 // These are the rotation and translation matrices to align the principal
328 // axes of the shape with cartesian axes. If d_normalized is true, it has
329 // been applied to the coordinates.
330 boost::dynamic_bitset<> d_normalizeds;
331 // If the shape is moved, the normalization matrices are no longer valid.
332 // This flags that so it is re-computed as required.
333 boost::dynamic_bitset<> d_normalizationOKs;
334
335 std::vector<std::array<double, 9>> d_canonRots;
336 std::vector<std::array<double, 3>> d_canonTranss;
337 // The sorted eigenvalues of the principal axes.
338 std::vector<std::array<double, 3>> d_eigenValuess;
339
340 void selectConformations(const std::vector<int> &picks);
341 void calculateSelfOverlaps(const ShapeOverlayOptions &overlayOpts);
342 // Sort the shapes in descending order of the sum of the shape
343 // and color volumes.
344 void sortShapesByVolumes();
345};
346
347#ifdef RDK_USE_BOOST_SERIALIZATION
348template <class Archive>
349void ShapeInput::serialize(Archive &ar, const unsigned int) {
350 ar & d_activeShape;
351 ar & d_coords;
352 ar & d_alphas;
353 ar & d_types;
354 ar & d_numAtoms;
355 ar & d_numFeats;
356 ar & d_selfOverlapShapeVols;
357 ar & d_selfOverlapColorVols;
358 ar & d_extremePointss;
359 ar & d_carbonRadii;
360 ar & d_smiles;
361 ar & d_normalizeds;
362 ar & d_normalizationOKs;
363 ar & d_canonRots;
364 ar & d_canonTranss;
365 ar & d_eigenValuess;
366}
367#endif
368
369// Extract the features from the molecule, optionally just for the subset
370// of atoms.
372 const Conformer &conf, std::vector<CustomFeature> &features,
373 const std::optional<std::vector<unsigned int>> &atomSubset = std::nullopt);
374
375// Calculate the mean position of the given atoms.
377 const Conformer &conf, const std::vector<unsigned int> &ats);
378
380 const double *quat, const double *trans);
381
382// Apply the transformation to the coordinates assumed to be in
383// ShapeInput.d_coords form.
385 std::vector<double> &shape, const RDGeom::Transform3D &xform);
387 const double *inShape, double *outShape, size_t numPoints,
388 const RDGeom::Transform3D &xform);
390 std::vector<double> &shape, const RDGeom::Point3D &translation);
392 const double *inShape, double *outShape, size_t numPoints,
393 const RDGeom::Point3D &translation);
394
395// Maximum possible score of the 2 shape (v[12]) and color (c[12]) volumes
397 double v1, double v2, double c1, double c2,
398 const ShapeOverlayOptions &overlayOpts);
399
400} // namespace GaussianShape
401} // namespace RDKit
402
403#endif // RDKIT_SHAPEINPUT_GUARD
#define PRECONDITION(expr, mess)
Definition Invariant.h:108
Defines the editable molecule class RWMol.
The class for representing 2D or 3D conformation of a molecule.
Definition Conformer.h:46
ShapeInput(const std::string &str)
Definition ShapeInput.h:145
ShapeInput(const ROMol &mol, int confId=-1, const ShapeInputOptions &opts=ShapeInputOptions(), const ShapeOverlayOptions &overlayOpts=ShapeOverlayOptions())
void negateAlpha(unsigned int alphaNum)
std::array< double, 3 > bestSimilarity(const ShapeInput &fitShape, unsigned int &bestThisShape, unsigned int &bestFitShape, RDGeom::Transform3D &bestXform, double threshold=-1.0, const ShapeOverlayOptions &overlayOpts=ShapeOverlayOptions())
const std::array< size_t, 6 > & calcExtremes()
const std::string getSmiles() const
Definition ShapeInput.h:178
const std::array< double, 3 > & calcEigenValues()
Get the eigen values for the coordinates matrix.
std::vector< RDGeom::Point3D > getAtomPoints(bool includeColors=false) const
Fetch the coordinates of the atoms and optionally features.
void setActiveShape(unsigned int newShape)
ShapeInput & operator=(const ShapeInput &other)
unsigned int getNumAtoms() const
Get the number of atoms in the shape.
Definition ShapeInput.h:207
void merge(ShapeInput &&other)
const std::vector< double > & getAlphas() const
Get the alpha values for the atoms and color features in the shape.
Definition ShapeInput.h:192
unsigned int getActiveShape() const
Definition ShapeInput.h:179
void pruneShapes(double simThreshold)
ShapeInput(const ShapeInput &other)
ShapeInput(const ShapeInput &other, unsigned int shapeNum)
double getShapeVolume(unsigned int shapeNum) const
Get the volume for the atoms for the given shape number.
std::string toString() const
Definition ShapeInput.h:167
double getColorVolume() const
Get the volume of the color features in the current active shape.
Definition ShapeInput.h:221
unsigned int getNumShapes() const
Definition ShapeInput.h:213
ShapeInput & operator=(ShapeInput &&other)=default
ShapeInput(ShapeInput &&other)=default
double getColorVolume(unsigned int shapeNum) const
Get the volume of the color features for the given shape number.
unsigned int getNumFeatures() const
Get the number of color features in the shape.
Definition ShapeInput.h:209
std::unique_ptr< RWMol > shapeToMol(bool includeColors=false, bool withBonds=true) const
const std::array< double, 3 > & calcCanonicalTranslation()
Get the canonical translation for the current active shape.
const std::vector< double > & getCoords() const
Definition ShapeInput.h:188
void transformCoords(RDGeom::Transform3D &xform)
Applies the given transformation to the current active shape.
const std::array< double, 9 > & calcCanonicalRotation()
Get the canonical rotation for the current active shape.
double getShapeVolume() const
Get the volume of the atoms in the current active shape.
Definition ShapeInput.h:215
double maxPossibleSimilarity(const ShapeInput &fitShape, const ShapeOverlayOptions &overlayOpts=ShapeOverlayOptions()) const
const boost::dynamic_bitset * getCarbonRadii() const
Get the flags for which atoms have a carbon radius.
Definition ShapeInput.h:227
const std::vector< int > & getFeatureTypes() const
Definition ShapeInput.h:205
std::array< double, 3 > calcMomentsOfInertia(bool includeColors=false) const
#define RDKIT_GAUSSIANSHAPE_EXPORT
Definition export.h:233
constexpr double P
Definition ShapeInput.h:74
RDKIT_GAUSSIANSHAPE_EXPORT double maxScore(double v1, double v2, double c1, double c2, const ShapeOverlayOptions &overlayOpts)
RDKIT_GAUSSIANSHAPE_EXPORT void translateShape(std::vector< double > &shape, const RDGeom::Point3D &translation)
constexpr double CARBON_RAD
Definition ShapeInput.h:71
RDKIT_GAUSSIANSHAPE_EXPORT void applyTransformToShape(std::vector< double > &shape, const RDGeom::Transform3D &xform)
RDKIT_GAUSSIANSHAPE_EXPORT void findFeatures(const Conformer &conf, std::vector< CustomFeature > &features, const std::optional< std::vector< unsigned int > > &atomSubset=std::nullopt)
RDKIT_GAUSSIANSHAPE_EXPORT RDGeom::Transform3D quatTransToTransform(const double *quat, const double *trans)
constexpr double KAPPA
Definition ShapeInput.h:75
RDKIT_GAUSSIANSHAPE_EXPORT RDGeom::Point3D computeFeaturePos(const Conformer &conf, const std::vector< unsigned int > &ats)
constexpr double DUMMY_RAD
Definition ShapeInput.h:72
Std stuff.
void serialize(Archive &ar, dynamic_bitset< Block, Allocator > &bs, const unsigned int)
Definition ShapeInput.h:43
CustomFeature(const unsigned int t, const RDGeom::Point3D &p, const double r, const std::vector< unsigned int > &a=std::vector< unsigned int >())
Definition ShapeInput.h:78
std::vector< unsigned int > atoms
Definition ShapeInput.h:86
ShapeInputOptions & operator=(const ShapeInputOptions &)=default
std::vector< unsigned int > atomSubset
Definition ShapeInput.h:106
std::vector< std::pair< unsigned int, double > > atomRadii
Definition ShapeInput.h:109
std::vector< std::vector< CustomFeature > > customFeatures
Definition ShapeInput.h:103
ShapeInputOptions(ShapeInputOptions &&)=default
ShapeInputOptions & operator=(ShapeInputOptions &&)=default
ShapeInputOptions(const ShapeInputOptions &)=default