RDKit
Open-source cheminformatics and machine learning.
Loading...
Searching...
No Matches
BoundsMatrixBuilderDetails.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//
12#include "RDGeneral/Invariant.h"
13#include <vector>
14#include <optional>
15#include <vector>
16#include <ranges>
17#include <algorithm>
18#include "RDGeneral/Invariant.h"
19
20#ifndef M_PI
21#define M_PI 3.14159265358979323846
22#endif
23
24#ifndef RD_BOUNDS_MATRIX_BUILDER_DETAILS_H
25#define RD_BOUNDS_MATRIX_BUILDER_DETAILS_H
26
27namespace RDKit {
28namespace DGeomHelpers {
36
46
49 std::optional<double> value = {};
50 std::optional<double> extraDist = {};
51 bool isForced = false;
52};
53
54inline DistGeom::TorsionRange ringTorsion(const std::size_t rSize) {
55 double torsion = M_PI;
56 switch (rSize) {
57 case 4u:
58 [[fallthrough]];
59 case 5u:
60 torsion = M_PI * 45.0 / 180.0;
61 break;
62 case 6u:
63 torsion = M_PI * 60.0 / 180.0;
64 break;
65 case 7u:
66 torsion = M_PI * 90.0 / 180.0;
67 break;
68 case 8u:
69 torsion = M_PI * 100.0 / 180.0;
70 break;
71 }
72 return {-torsion, torsion};
73}
74
75//! A structure used to store planar 14 paths - cis/trans
77 unsigned int bid1, bid2, bid3;
78 unsigned int aid1, aid2, aid3, aid4;
81 std::size_t rSize = 0;
82
84 switch (value.type) {
86 return DistGeom::TorsionValues{0.0};
90 return ringTorsion(rSize);
92 return DistGeom::TorsionValues{0.0, M_PI};
94 return DistGeom::TorsionValues{*value.value};
95 default:
96 break;
97 }
98 return ringTorsion(0);
99 }
100};
101
102using PATH14_VECT = std::vector<Path14Configuration>;
103
104inline std::size_t getUnifiedId(const unsigned int id1, const unsigned int id2,
105 const unsigned int n) {
106 // returns an id for (id1, id2) independent of order within range (0, 2*n - 1)
107 // assuming id1 < n and id2 < n
108 return id1 < id2 ? (static_cast<std::size_t>(id1) * n + id2)
109 : (static_cast<std::size_t>(id2) * n + id1);
110}
111
112inline std::size_t getUnifiedId(const unsigned int id1, const unsigned int id2,
113 const unsigned int id3, const unsigned int n) {
114 // returns an id for (id1, id2, id3) independent of order of id1, id3 within
115 // range (0, 3*(n) - 1) assuming id1 < n, id2 < n and id3 < n
116 return id1 < id3 ? (static_cast<std::size_t>(id1) * n * n + id2 * n + id3)
117 : (static_cast<std::size_t>(id3) * n * n + id2 * n + id1);
118}
119
120template <unsigned int numBondIds>
121auto unifiedIdToBondIds(std::size_t id, const unsigned int n) {
122 // converts an id (created with getUnifiedId) back into the original bond ids
123 std::array<unsigned int, numBondIds> bondIds;
124
125 for (auto i : std::views::iota(0u, numBondIds) | std::views::reverse) {
126 bondIds[i] = static_cast<unsigned int>(id % n);
127 id /= n;
128 }
129
130 return bondIds;
131};
132
133struct Bounds {
134 double lower{1.0}, upper{-1.0}; // we start invalid
135 unsigned int aid1{0}, aid4{0};
136
137 inline bool valid() const { return lower <= upper; }
138
139 bool operator==(const Bounds &) const = default;
140
141 friend std::ostream &operator<<(std::ostream &os, const Bounds &b) {
142 return os << "Bounds{"
143 << "lower=" << b.lower << ", upper=" << b.upper
144 << ", aid1=" << b.aid1 << ", aid4=" << b.aid4 << '}';
145 }
146};
147
148inline Bounds merge(std::vector<Bounds> bounds) {
149 PRECONDITION(bounds.size(), "Cannot merge empty list of bounds");
150
151 std::ranges::sort(bounds, {}, &Bounds::lower);
152
153 Bounds current = bounds.front();
154 double componentUpper = current.upper;
155 std::optional<double> resultLower;
156
157 // What we are doing here:
158 // U {i'=intersection(i_j,..,i_k) | {i_j, ..., i_k}\subset(I) ^ i` !=
159 // \emptyset ^ !\exists(i_l): intersection(i`, i_l) != \emptyset}
160 // or in other words:
161 // we aim to find the union of all intersections that are maximal in a sense
162 // that adding another arbitrary bounds to it, would lead into an empty set
163
164 // we solve this by traversing the sorted bounds in a sweep manner while
165 // keeping track on the current/active non-empty intersection
166 // (currentIntersection), the largest upperBound that was reached so far
167 // (this is needed since the currentIntersection.upper can be smaller than
168 // that, losing track of potenial overlaps/intersections).
169 // To avoid storing all maximal non-overlapping intersections (only the
170 // first and last one is relevant), we store the lower bound of the first
171 // maximal intersection in resultLower
172
173 for (const auto &_bound : bounds | std::views::drop(1)) {
174 if (_bound.lower <= current.upper) {
175 // Case 1: _bounds intersects with currentIntersection => add to current
176 // intersection
177 // we know that bounds are sorted by lower bounds =>
178 // _bound.lower is always greater/equal currentIntersection.lower
179 current.lower = _bound.lower;
180 current.upper = std::min(current.upper, _bound.upper);
181 } else {
182 // Case 2: _bound is not overlapping with the current intersection => we
183 // know that currentIntersection is maximal
184
185 if (!resultLower) {
186 resultLower = current.lower;
187 }
188
189 current.lower = _bound.lower;
190 current.upper =
191 _bound.lower <= componentUpper
192 ? std::min(componentUpper,
193 _bound.upper) // there is this at least former
194 // bounds that is overlapping and
195 // needs to be considered
196 : _bound.upper;
197 }
198
199 componentUpper = std::max(componentUpper, _bound.upper);
200 }
201 return Bounds{.lower = resultLower.value_or(current.lower),
202 .upper = current.upper,
203 .aid1 = bounds.front().aid1,
204 .aid4 = bounds.front().aid4};
205}
206
207} // namespace DGeomHelpers
208} // namespace RDKit
209#endif
#define PRECONDITION(expr, mess)
Definition Invariant.h:108
std::variant< TorsionRange, TorsionValues > TorsionCandidates
auto unifiedIdToBondIds(std::size_t id, const unsigned int n)
std::size_t getUnifiedId(const unsigned int id1, const unsigned int id2, const unsigned int n)
std::vector< Path14Configuration > PATH14_VECT
DistGeom::TorsionRange ringTorsion(const std::size_t rSize)
Bounds merge(std::vector< Bounds > bounds)
Std stuff.
bool operator==(const Bounds &) const =default
friend std::ostream & operator<<(std::ostream &os, const Bounds &b)
A structure used to store planar 14 paths - cis/trans.
DistGeom::TorsionCandidates toTorsionRange() const