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 Katharina Buchthal 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#include <vector>
11#include "RDGeneral/Invariant.h"
12#include <ranges>
13#include <optional>
14#include <algorithm>
15
16#ifndef RD_BOUNDS_MATRIX_BUILDER_DETAILS_H
17#define RD_BOUNDS_MATRIX_BUILDER_DETAILS_H
18
19namespace RDKit {
20namespace DGeomHelpers {
21
22struct Bounds {
23 double lower{1.0}, upper{-1.0}; // we start invalid
24 unsigned int aid1{0}, aid4{0};
25
26 inline bool valid() const { return lower <= upper; }
27
28 bool operator==(const Bounds &) const = default;
29
30 friend std::ostream &operator<<(std::ostream &os, const Bounds &b) {
31 return os << "Bounds{"
32 << "lower=" << b.lower << ", upper=" << b.upper
33 << ", aid1=" << b.aid1 << ", aid4=" << b.aid4 << '}';
34 }
35};
36
37inline Bounds merge(std::vector<Bounds> bounds) {
38 PRECONDITION(bounds.size(), "Cannot merge empty list of bounds");
39
40 std::ranges::sort(bounds, {}, &Bounds::lower);
41
42 Bounds current = bounds.front();
43 double componentUpper = current.upper;
44 std::optional<double> resultLower;
45
46 // What we are doing here:
47 // U {i'=intersection(i_j,..,i_k) | {i_j, ..., i_k}\subset(I) ^ i` !=
48 // \emptyset ^ !\exists(i_l): intersection(i`, i_l) != \emptyset}
49 // or in other words:
50 // we aim to find the union of all intersections that are maximal in a sense
51 // that adding another arbitrary bounds to it, would lead into an empty set
52
53 // we solve this by traversing the sorted bounds in a sweep manner while
54 // keeping track on the current/active non-empty intersection
55 // (currentIntersection), the largest upperBound that was reached so far
56 // (this is needed since the currentIntersection.upper can be smaller than
57 // that, losing track of potenial overlaps/intersections).
58 // To avoid storing all maximal non-overlapping intersections (only the
59 // first and last one is relevant), we store the lower bound of the first
60 // maximal intersection in resultLower
61
62 for (const auto &_bound : bounds | std::views::drop(1)) {
63 if (_bound.lower <= current.upper) {
64 // Case 1: _bounds intersects with currentIntersection => add to current
65 // intersection
66 // we know that bounds are sorted by lower bounds =>
67 // _bound.lower is always greater/equal currentIntersection.lower
68 current.lower = _bound.lower;
69 current.upper = std::min(current.upper, _bound.upper);
70 } else {
71 // Case 2: _bound is not overlapping with the current intersection => we
72 // know that currentIntersection is maximal
73
74 if (!resultLower) {
75 resultLower = current.lower;
76 }
77
78 current.lower = _bound.lower;
79 current.upper =
80 _bound.lower <= componentUpper
81 ? std::min(componentUpper,
82 _bound.upper) // there is this at least former
83 // bounds that is overlapping and
84 // needs to be considered
85 : _bound.upper;
86 }
87
88 componentUpper = std::max(componentUpper, _bound.upper);
89 }
90 return Bounds{.lower = resultLower.value_or(current.lower),
91 .upper = current.upper,
92 .aid1 = bounds.front().aid1,
93 .aid4 = bounds.front().aid4};
94}
95
96} // namespace DGeomHelpers
97} // namespace RDKit
98#endif
#define PRECONDITION(expr, mess)
Definition Invariant.h:108
Bounds merge(std::vector< Bounds > bounds)
Std stuff.
bool operator==(const Bounds &) const =default
friend std::ostream & operator<<(std::ostream &os, const Bounds &b)