Garfield++ 4.0
A toolkit for the detailed simulation of particle detectors based on ionisation measurement in gases and semiconductors
Loading...
Searching...
No Matches
KDTree.cc
Go to the documentation of this file.
1//
2// (c) Matthew B. Kennel, Institute for Nonlinear Science, UCSD (2004)
3//
4// Licensed under the Academic Free License version 1.1 found in file LICENSE
5// with additional provisions in that same file.
6
7#include <algorithm>
8#include <limits>
9#include <iostream>
10
11#include "Garfield/KDTree.hh"
12
13namespace {
14
15double squared(const double x) { return x * x; }
16
17double dis_from_bnd(const double x, const double amin, const double amax) {
18 if (x > amax) {
19 return(x-amax);
20 } else if (x < amin)
21 return (amin-x);
22 else
23 return 0.0;
24}
25
26}
27
28namespace Garfield {
29
30inline bool operator<(const KDTreeResult& e1, const KDTreeResult& e2) {
31 return (e1.dis < e2.dis);
32}
33
34// Constructor
36 : m_data(data_in) {
37
38 const size_t n = data_in.size();
39 if (!data_in.empty()) {
40 m_dim = data_in[0].size();
41 }
42
43 m_ind.resize(n);
44 for (size_t i = 0; i < n; i++) m_ind[i] = i;
45 // Build the tree.
46 m_root = build_tree_for_range(0, n - 1, 0);
47}
48
49// Destructor
51 delete m_root;
52}
53
54KDTreeNode* KDTree::build_tree_for_range(int l, int u, KDTreeNode* parent) {
55
56 if (u < l) return nullptr;
57 KDTreeNode* node = new KDTreeNode(m_dim);
58 if ((u - l) <= bucketsize) {
59 // Create a terminal node. Always compute true bounding box.
60 for (size_t i = 0; i < m_dim; i++) {
61 node->box[i] = spread_in_coordinate(i, l, u);
62 }
63 node->cut_dim = 0;
64 node->cut_val = 0.0;
65 node->m_l = l;
66 node->m_u = u;
67 node->left = node->right = nullptr;
68 } else {
69 // Compute an APPROXIMATE bounding box for this node.
70 // If parent == nullptr, then this is the root node, and
71 // we compute for all dimensions.
72 // Otherwise, we copy the bounding box from the parent for
73 // all coordinates except for the parent's cut dimension.
74 // That, we recompute ourself.
75 int c = -1;
76 double maxspread = 0.0;
77 for (size_t i = 0; i < m_dim; i++) {
78 if (!parent || (parent->cut_dim == i)) {
79 node->box[i] = spread_in_coordinate(i, l, u);
80 } else {
81 node->box[i] = parent->box[i];
82 }
83 const double spread = node->box[i][1] - node->box[i][0];
84 if (spread > maxspread) {
85 maxspread = spread;
86 c = i;
87 }
88 }
89
90 // Now, c is the identity of which coordinate has the greatest spread.
91 double sum = 0.0;
92 for (int k = l; k <= u; k++) {
93 sum += m_data[m_ind[k]][c];
94 }
95 const double average = sum / static_cast<double>(u - l + 1);
96 int m = select_on_coordinate_value(c, average, l, u);
97
98 // Move the indices around to cut on dim 'c'.
99 node->cut_dim = c;
100 node->m_l = l;
101 node->m_u = u;
102 node->left = build_tree_for_range(l, m, node);
103 node->right = build_tree_for_range(m + 1, u, node);
104
105 if (!node->right) {
106 node->box = node->left->box;
107 node->cut_val = node->left->box[c][1];
108 node->cut_val_left = node->cut_val_right = node->cut_val;
109 } else if (!node->left) {
110 node->box = node->right->box;
111 node->cut_val = node->right->box[c][1];
112 node->cut_val_left = node->cut_val_right = node->cut_val;
113 } else {
114 node->cut_val_right = node->right->box[c][0];
115 node->cut_val_left = node->left->box[c][1];
116 node->cut_val = 0.5 * (node->cut_val_left + node->cut_val_right);
117
118 // Now recompute true bounding box as union of subtree boxes.
119 // This is now faster having built the tree, being logarithmic in
120 // N, not linear as would be from naive method.
121 for (size_t i = 0; i < m_dim; i++) {
122 node->box[i][1] = std::max(node->left->box[i][1],
123 node->right->box[i][1]);
124 node->box[i][0] = std::min(node->left->box[i][0],
125 node->right->box[i][0]);
126 }
127 }
128 }
129 return node;
130}
131
132std::array<double, 2> KDTree::spread_in_coordinate(const int c, const int l,
133 const int u) const {
134 // Return the minimum and maximum of the indexed data between l and u.
135
136 double smin = m_data[m_ind[l]][c];
137 double smax = smin;
138
139 // Process two at a time.
140 int i;
141 for (i = l + 2; i <= u; i += 2) {
142 double lmin = m_data[m_ind[i - 1]][c];
143 double lmax = m_data[m_ind[i]][c];
144 if (lmin > lmax) std::swap(lmin, lmax);
145 if (smin > lmin) smin = lmin;
146 if (smax < lmax) smax = lmax;
147 }
148 // Is there one more element?
149 if (i == u + 1) {
150 double last = m_data[m_ind[u]][c];
151 if (smin > last) smin = last;
152 if (smax < last) smax = last;
153 }
154 return {smin, smax};
155}
156
157int KDTree::select_on_coordinate_value(int c, double alpha, int l, int u) {
158 // Move indices in ind[l..u] so that the elements in [l .. return]
159 // are <= alpha, and hence are less than the [return + 1 .. u]
160 // elements, viewed across dimension 'c'.
161 int lb = l, ub = u;
162 while (lb < ub) {
163 if (m_data[m_ind[lb]][c] <= alpha) {
164 lb++; // good where it is.
165 } else {
166 std::swap(m_ind[lb], m_ind[ub]);
167 ub--;
168 }
169 }
170 // Here ub = lb.
171 return m_data[m_ind[lb]][c] <= alpha ? lb : lb - 1;
172}
173
174void KDTree::n_nearest(const std::vector<double>& qv,
175 const unsigned int nn,
176 std::vector<KDTreeResult>& result) const {
177 // Search for n nearest to a given query vector 'qv'.
178 std::priority_queue<KDTreeResult> res;
179 double r2 = std::numeric_limits<double>::max();
180 m_root->search_n(-1, 0, nn, r2, qv, *this, res);
181 result.clear();
182 while (!res.empty()) {
183 result.push_back(res.top());
184 res.pop();
185 }
186 if (sort_results) sort(result.begin(), result.end());
187}
188
189void KDTree::n_nearest_around_point(const unsigned int idx,
190 const unsigned int ndecorrel,
191 const unsigned int nn,
192 std::vector<KDTreeResult>& result) const {
193
194 std::priority_queue<KDTreeResult> res;
195 double r2 = std::numeric_limits<double>::max();
196 m_root->search_n(idx, ndecorrel, nn, r2, m_data[idx], *this, res);
197 result.clear();
198 while (!res.empty()) {
199 result.push_back(res.top());
200 res.pop();
201 }
202 if (sort_results) sort(result.begin(), result.end());
203}
204
205void KDTree::r_nearest(const std::vector<double>& qv, const double r2,
206 std::vector<KDTreeResult>& result) const {
207 // Search for all within a ball of a certain radius.
208 result.clear();
209 m_root->search_r(-1, 0, r2, qv, *this, result);
210 if (sort_results) sort(result.begin(), result.end());
211}
212
213void KDTree::r_nearest_around_point(const unsigned int idx,
214 const unsigned int ndecorrel,
215 const double r2,
216 std::vector<KDTreeResult>& result) const {
217
218 result.clear();
219 m_root->search_r(idx, ndecorrel, r2, m_data[idx], *this, result);
220 if (sort_results) sort(result.begin(), result.end());
221}
222
223// Constructor
224KDTreeNode::KDTreeNode(int dim) : box(dim) {}
225
226// Destructor
228 if (left) delete left;
229 if (right) delete right;
230}
231
232void KDTreeNode::search_n(const int idx0, const int nd,
233 const unsigned int nn, double& r2,
234 const std::vector<double>& qv, const KDTree& tree,
235 std::priority_queue<KDTreeResult>& res) const {
236
237 if (!left && !right) {
238 // We are on a terminal node.
239 process_terminal_node_n(idx0, nd, nn, r2, qv, tree, res);
240 return;
241 }
242 KDTreeNode *ncloser = nullptr;
243 KDTreeNode *nfarther = nullptr;
244
245 double extra;
246 double qval = qv[cut_dim];
247 // value of the wall boundary on the cut dimension.
248 if (qval < cut_val) {
249 ncloser = left;
250 nfarther = right;
251 extra = cut_val_right - qval;
252 } else {
253 ncloser = right;
254 nfarther = left;
255 extra = qval - cut_val_left;
256 }
257
258 if (ncloser) ncloser->search_n(idx0, nd, nn, r2, qv, tree, res);
259 if ((nfarther) && (squared(extra) < r2)) {
260 // first cut
261 if (nfarther->box_in_search_range(r2, qv)) {
262 nfarther->search_n(idx0, nd, nn, r2, qv, tree, res);
263 }
264 }
265}
266
267void KDTreeNode::search_r(const int idx0, const int nd, const double r2,
268 const std::vector<double>& qv, const KDTree& tree,
269 std::vector<KDTreeResult>& res) const {
270
271 if (!left && !right) {
272 // We are on a terminal node.
273 process_terminal_node_r(idx0, nd, r2, qv, tree, res);
274 return;
275 }
276 KDTreeNode *ncloser = nullptr;
277 KDTreeNode *nfarther = nullptr;
278
279 double extra;
280 double qval = qv[cut_dim];
281 // value of the wall boundary on the cut dimension.
282 if (qval < cut_val) {
283 ncloser = left;
284 nfarther = right;
285 extra = cut_val_right - qval;
286 } else {
287 ncloser = right;
288 nfarther = left;
289 extra = qval - cut_val_left;
290 }
291
292 if (ncloser) ncloser->search_r(idx0, nd, r2, qv, tree, res);
293 if ((nfarther) && (squared(extra) < r2)) {
294 // first cut
295 if (nfarther->box_in_search_range(r2, qv)) {
296 nfarther->search_r(idx0, nd, r2, qv, tree, res);
297 }
298 }
299}
300
301inline bool KDTreeNode::box_in_search_range(const double r2,
302 const std::vector<double>& qv) const {
303
304 // Does the bounding box have any point which is within 'r2' to 'qv'??
305
306 const size_t dim = qv.size();
307 double dis2 = 0.0;
308 for (size_t i = 0; i < dim; i++) {
309 dis2 += squared(dis_from_bnd(qv[i], box[i][0], box[i][1]));
310 if (dis2 > r2) return false;
311 }
312 return true;
313}
314
315void KDTreeNode::process_terminal_node_n(const int idx0, const int nd,
316 const unsigned int nn, double& r2, const std::vector<double>& qv,
317 const KDTree& tree, std::priority_queue<KDTreeResult>& res) const {
318
319 const size_t dim = tree.m_dim;
320 const auto& data = tree.m_data;
321
322 for (int i = m_l; i <= m_u; i++) {
323 const int idx = tree.m_ind[i];
324 bool early_exit = false;
325 double dis = 0.0;
326 for (size_t k = 0; k < dim; k++) {
327 dis += squared(data[idx][k] - qv[k]);
328 if (dis > r2) {
329 early_exit = true;
330 break;
331 }
332 }
333 if (early_exit) continue; // next iteration of mainloop
334
335 // Skip points within the decorrelation interval.
336 if (idx0 >= 0 && (abs(idx - idx0) < nd)) continue;
337
338 // Add the point to the list.
339 if (res.size() < nn) {
340 // The list so far is undersized.
341 KDTreeResult e;
342 e.idx = idx;
343 e.dis = dis;
344 res.push(e);
345 // Set the ball radius to the largest on the list (maximum priority).
346 if (res.size() == nn) r2 = res.top().dis;
347 } else {
348 // if we get here then the current node, has a squared
349 // distance smaller
350 // than the last on the list, and belongs on the list.
351 KDTreeResult e;
352 e.idx = idx;
353 e.dis = dis;
354 res.pop();
355 res.push(e);
356 r2 = res.top().dis;
357 }
358 } // main loop
359}
360
361void KDTreeNode::process_terminal_node_r(const int idx0, const int nd,
362 const double r2, const std::vector<double>& qv, const KDTree& tree,
363 std::vector<KDTreeResult>& res) const {
364
365 const size_t dim = tree.m_dim;
366 const auto& data = tree.m_data;
367
368 for (int i = m_l; i <= m_u; i++) {
369 const int idx = tree.m_ind[i];
370 bool early_exit = false;
371 double dis = 0.0;
372 for (size_t k = 0; k < dim; k++) {
373 dis += squared(data[idx][k] - qv[k]);
374 if (dis > r2) {
375 early_exit = true;
376 break;
377 }
378 }
379 if (early_exit) continue; // next iteration of mainloop
380
381 // Skip points within the decorrelation interval.
382 if (idx0 >= 0 && (abs(idx - idx0) < nd)) continue;
383
384 KDTreeResult e;
385 e.idx = idx;
386 e.dis = dis;
387 res.push_back(std::move(e));
388 }
389}
390
391}
A node in the k-d tree.
Definition: KDTree.hh:101
KDTreeNode(int dim)
Constructor.
Definition: KDTree.cc:224
~KDTreeNode()
Destructor.
Definition: KDTree.cc:227
bool sort_results
Definition: KDTree.hh:40
const KDTreeArray & m_data
Definition: KDTree.hh:37
void n_nearest_around_point(const unsigned int idx, const unsigned int ndecorrel, const unsigned int nn, std::vector< KDTreeResult > &result) const
Definition: KDTree.cc:189
friend class KDTreeNode
Definition: KDTree.hh:83
void r_nearest(const std::vector< double > &qv, const double r2, std::vector< KDTreeResult > &result) const
Definition: KDTree.cc:205
~KDTree()
Destructor.
Definition: KDTree.cc:50
size_t m_dim
Definition: KDTree.hh:39
void n_nearest(const std::vector< double > &qv, const unsigned int nn, std::vector< KDTreeResult > &result) const
Definition: KDTree.cc:174
void r_nearest_around_point(const unsigned int idx, const unsigned int ndecorrel, const double r2, std::vector< KDTreeResult > &result) const
Definition: KDTree.cc:213
bool operator<(const KDTreeResult &e1, const KDTreeResult &e2)
Definition: KDTree.cc:30
std::vector< std::vector< double > > KDTreeArray
Definition: KDTree.hh:20
Search result.
Definition: KDTree.hh:26