RDKit
Open-source cheminformatics and machine learning.
Loading...
Searching...
No Matches
ZMatrixUtils.h
Go to the documentation of this file.
1//
2// Copyright (C) 2026 ETH Zurich
3// Created by: Katharina Buchthal
4//
5// @@ All Rights Reserved @@
6// This file is part of the RDKit.
7// The contents are covered by the terms of the BSD license
8// which is included in the file license.txt, found at the root
9// of the RDKit source tree.
10//
11
12#ifndef RD_Z_MATRIX_UTILS_H
13#define RD_Z_MATRIX_UTILS_H
14
15#include <RDGeneral/Invariant.h>
17#include <variant>
18#include <unordered_set>
19#include <cmath>
20
21#ifndef M_PI
22#define M_PI 3.14159265358979323846
23#endif
24
25namespace DistGeom {
26
27// helper type for the visitor #4 (From
28// https://en.cppreference.com/cpp/utility/variant/visit2)
29template <class... Ts>
30struct overloaded : Ts... {
31 using Ts::operator()...;
32};
33
34namespace {
35constexpr double TORSION_PRECISION =
36 1.e9; // assuming torsions are between M_PI and -M_PI
37
38inline double dequantize(int64_t val) {
39 return static_cast<double>(val) / TORSION_PRECISION;
40}
41
42} // namespace
43
44inline int64_t quantize(double val, double precision = TORSION_PRECISION) {
45 val = std::fmod(val + M_PI, 2.0 * M_PI);
46 if (val < 0.0) {
47 val += 2.0 * M_PI;
48 }
49 val -= M_PI;
50
51 return static_cast<int64_t>(val * precision);
52}
53
55 double lower, upper;
56
57 TorsionRange(double lower, double upper) : lower(lower), upper(upper) {
58 PRECONDITION(lower <= upper, "Invalid range");
59 d_qLower = quantize(lower);
60 d_qUpper = quantize(upper);
61 if (d_qLower == d_qUpper) {
62 // full range
63 d_qUpper = static_cast<int64_t>(M_PI * TORSION_PRECISION);
64 d_qLower = -static_cast<int64_t>(M_PI * TORSION_PRECISION);
65 }
66 }
67
68 TorsionRange(int64_t qLower, int64_t qUpper)
69 : d_qLower(qLower), d_qUpper(qUpper) {
70 lower = dequantize(qLower);
71 upper = dequantize(qUpper);
72 }
73
74 bool qContains(const int64_t value) const {
75 if (d_qLower <= d_qUpper) {
76 return value >= d_qLower && value <= d_qUpper;
77 }
78 // range across M_PI and -M_PI
79 return (value < d_qLower) != (value > d_qUpper);
80 }
81
82 bool contains(const double value) const { return qContains(quantize(value)); }
83
84 double width() const {
85 auto res = upper - lower;
86 if (res < 0) {
87 // range across M_PI - -M_PI
88 return res + 2.0 * M_PI;
89 }
90 return res;
91 }
92
93 bool operator<(const TorsionRange &other) const {
94 return this->width() < other.width();
95 }
96
97 bool operator==(const TorsionRange &other) const {
98 return this->d_qLower == other.d_qLower && this->d_qUpper == other.d_qUpper;
99 }
100
102 return lower + (upper - lower) * (rng());
103 }
104
105 private:
106 int64_t d_qLower, d_qUpper;
107};
108
110 using value_type = int64_t;
111 using iterator = std::unordered_set<value_type>::iterator;
112 using const_iterator = std::unordered_set<value_type>::const_iterator;
113
114 TorsionValues() = default;
115
116 TorsionValues(std::initializer_list<double> values) {
117 content.reserve(values.size());
118 // make sure values are within -M_PI and M_PI
119 for (auto val : values) {
120 content.emplace(quantize(val));
121 }
122 }
123
124 bool empty() const { return content.empty(); }
125 std::size_t size() const { return content.size(); }
126 const_iterator begin() const { return content.begin(); }
127 const_iterator end() const { return content.end(); }
128 const_iterator cbegin() const { return content.cbegin(); }
129 const_iterator cend() const { return content.cend(); }
130
131 friend TorsionValues merge(TorsionValues lhs, // copying on purpose!
132 TorsionValues rhs) {
133 rhs.content.merge(lhs.content); // lhs <- lhs n rhs; rhs <- union
134 return lhs;
135 }
136
138 const auto idxT =
139 static_cast<std::size_t>(rng() * static_cast<double>(this->size()));
140
141 auto it = this->cbegin();
142 std::advance(it, idxT);
143 // in TorsionValues, the quantizied torsion (int_64)
144 // is stored -> we need to convert it to double
145 // [-M_PI, M_PI] again
146 return dequantize(*it);
147 }
148 bool operator==(const TorsionValues &other) const {
149 return content == other.content;
150 }
151 double firstValue() const { return dequantize(*content.begin()); }
152
153 std::pair<iterator, bool> insert(const double val) {
154 return content.insert(quantize(val));
155 }
156
157 std::pair<iterator, bool> insertQ(const int64_t val) {
158 return content.insert(val);
159 }
160
161 bool contains(const double value) const {
162 const std::int64_t qVal = quantize(value);
163 return content.contains(qVal);
164 }
165
166 private:
167 std::unordered_set<int64_t> content;
168};
169
170using TorsionCandidates = std::variant<TorsionRange, TorsionValues>;
171
172namespace {
173
174inline TorsionCandidates merge(const TorsionRange &lhs,
175 const TorsionRange &rhs) {
176 if (std::min(lhs.upper, rhs.upper) >= std::max(lhs.lower, rhs.lower)) {
177 return TorsionRange{std::max(lhs.lower, rhs.lower),
178 std::min(lhs.upper, rhs.upper)};
179 }
180 return TorsionRange{
181 std::min(lhs.lower, rhs.lower),
182 std::max(lhs.upper, rhs.upper)}; // we can adapt the same logic
183 // as for 1-4 ranges again but
184 // it could get a bit tidious
185}
186
187inline TorsionCandidates merge(const TorsionRange &range,
188 const TorsionValues &values) {
189 // we select only those fixed values that are within the range, if there is
190 // non, we take the min/max from the range and the values
191 TorsionValues mergedValues;
192
193 for (const auto &val : values) {
194 if (range.qContains(val)) {
195 mergedValues.insertQ(val);
196 }
197 }
198
199 if (!mergedValues.empty()) {
200 return mergedValues;
201 }
202
203 const auto &[minIt, maxIt] = std::ranges::minmax_element(values);
204 const TorsionRange minMaxValues{*minIt, *maxIt};
205
206 return merge(range, minMaxValues);
207}
208
209inline TorsionCandidates merge(const TorsionValues &values,
210 const TorsionRange &range) {
211 return merge(range, values);
212}
213
214inline bool less(const TorsionRange &lhs, const TorsionRange &rhs) {
215 return lhs < rhs;
216}
217
218inline bool less(const TorsionRange &, const TorsionValues &) {
219 return false; // distinct values are more constraint than ranges
220}
221
222inline bool less(const TorsionValues &, const TorsionRange &) { return true; }
223
224inline bool less(const TorsionValues &lhs, const TorsionValues &rhs) {
225 return lhs.size() < rhs.size();
226}
227
228inline bool equal(const TorsionRange &lhs, const TorsionRange &rhs) {
229 return lhs == rhs;
230}
231
232inline bool equal(const TorsionRange &, const TorsionValues &) {
233 return false; // distinct values are more constraint than ranges
234}
235
236inline bool equal(const TorsionValues &, const TorsionRange &) { return false; }
237
238inline bool equal(const TorsionValues &lhs, const TorsionValues &rhs) {
239 return lhs == rhs;
240}
241
242}; // namespace
243
245 const TorsionCandidates &rhs) {
246 // for mixture of torsion range and fixed values
247
248 return std::visit(
249 [](const auto &l, const auto &r) -> TorsionCandidates {
250 return merge(l, r);
251 },
252 lhs, rhs);
253}
254
255inline double sample(const TorsionCandidates &torsionCand,
257 return std::visit(
258 [&rng](const auto &torsionCand) -> double {
259 return torsionCand.sample(rng);
260 },
261 torsionCand);
262}
263
264inline bool less(const TorsionCandidates &lhs, const TorsionCandidates &rhs) {
265 // lhs < rhs => lhs is more constraint than rhs
266 return std::visit(
267 [](const auto &l, const auto &r) -> bool { return less(l, r); }, lhs,
268 rhs);
269}
270inline bool equal(const TorsionCandidates &lhs, const TorsionCandidates &rhs) {
271 // lhs < rhs => lhs is more constraint than rhs
272 return std::visit(
273 [](const auto &l, const auto &r) -> bool { return equal(l, r); }, lhs,
274 rhs);
275}
276
277inline bool contains(const TorsionCandidates &torsionCand, const double value) {
278 // lhs < rhs => lhs is more constraint than rhs
279 return std::visit(
280 [value](const auto &x) -> bool { return x.contains(value); },
281 torsionCand);
282}
283
284} // namespace DistGeom
285
286#endif
#define PRECONDITION(expr, mess)
Definition Invariant.h:108
bool less(const TorsionCandidates &lhs, const TorsionCandidates &rhs)
bool equal(const TorsionCandidates &lhs, const TorsionCandidates &rhs)
std::variant< TorsionRange, TorsionValues > TorsionCandidates
TorsionCandidates merge(const TorsionCandidates &lhs, const TorsionCandidates &rhs)
bool contains(const TorsionCandidates &torsionCand, const double value)
double sample(const TorsionCandidates &torsionCand, RDKit::double_source_type &rng)
int64_t quantize(double val, double precision=TORSION_PRECISION)
boost::variate_generator< rng_type &, uniform_double > double_source_type
Definition utils.h:49
bool operator==(const TorsionRange &other) const
bool contains(const double value) const
TorsionRange(double lower, double upper)
TorsionRange(int64_t qLower, int64_t qUpper)
double width() const
bool qContains(const int64_t value) const
bool operator<(const TorsionRange &other) const
double sample(RDKit::double_source_type &rng) const
double firstValue() const
bool operator==(const TorsionValues &other) const
const_iterator cend() const
friend TorsionValues merge(TorsionValues lhs, TorsionValues rhs)
bool contains(const double value) const
const_iterator begin() const
TorsionValues(std::initializer_list< double > values)
const_iterator end() const
std::pair< iterator, bool > insert(const double val)
std::unordered_set< value_type >::iterator iterator
double sample(RDKit::double_source_type &rng) const
std::size_t size() const
std::pair< iterator, bool > insertQ(const int64_t val)
std::unordered_set< value_type >::const_iterator const_iterator
const_iterator cbegin() const