coordinate());
46 | pMax.maxCoords(items[i]->coordinate());
47 | }}
48 |
49 | inline void boundingBoxParallel() {
50 | // intT P = getWorkers()*8;
51 | static const intT P = 36 * 8;
52 | intT blockSize = (n+P-1)/P;
53 | pointT localMin[P];
54 | pointT localMax[P];
55 | for (intT i=0; icoordinate());
57 | localMax[i] = pointT(items[0]->coordinate());}
58 | parallel_for(0, P,
59 | [&](intT p) {
60 | intT s = p*blockSize;
61 | intT e = min((intT)(p+1)*blockSize,n);
62 | for (intT j=s; jcoordinate());
64 | localMax[p].maxCoords(items[j]->coordinate());}
65 | });
66 | pMin = pointT(items[0]->coordinate());
67 | pMax = pointT(items[0]->coordinate());
68 | for(intT p=0; pcoordinate(k)>=xM) {
80 | while (items[rPt]->coordinate(k)>=xM && lPt < rPt) {
81 | rPt--;
82 | }
83 | if (lPt < rPt) {
84 | swap(items[lPt], items[rPt]);
85 | rPt--; }
86 | else { break;}
87 | }
88 | lPt++;
89 | }
90 | if (items[lPt]->coordinate(k) < xM) lPt++;
91 | return lPt;
92 | }
93 |
94 | inline intT splitItemParallel(floatT xM, objT **scratch, intT* flags) {
95 | if (n < 2) {
96 | cout << "error, kdTree splitting singleton, abort" << endl;abort();}
97 | parallel_for(0, n,
98 | [&](intT i) {
99 | if (items[i]->coordinate(k)pMax2[i]) exclude = true;
122 | if (pMax1[i]pMin2[i]) include = false;
123 | }
124 | if (exclude) return boxExclude;
125 | else if (include) return boxInclude;
126 | else return boxOverlap;
127 | }
128 |
129 | inline bool itemInBox(pointT pMin1, pointT pMax1, objT* item) {
130 | for(int i=0; icoordinate(i) || pMin1[i]>item->coordinate(i)) return false;
132 | }
133 | return true;}
134 |
135 | intT findWidest() {
136 | floatT xM = -1;
137 | for (int kk=0; kkxM) {
139 | xM = pMax[kk]-pMin[kk];
140 | k = kk;}}
141 | return k;
142 | }
143 |
144 | void constructSerial(nodeT *space, intT leafSize) {
145 | boundingBoxSerial();
146 | sib = NULL;
147 | if (n <= leafSize) {
148 | left = NULL; right = NULL;
149 | } else {
150 | if (!space[0].isEmpty() || !space[1].isEmpty()) {
151 | cout << "error, kdNode overwrite, abort" << endl;abort();}
152 | intT k = findWidest();
153 | floatT xM = (pMax[k]+pMin[k])/2;
154 |
155 | intT median;
156 | if (spatialMedian) {
157 | median = splitItemSerial(xM);
158 | } else {
159 | auto splitK = [&](objT* a, objT* b) {
160 | return a->coordinate(k) < b->coordinate(k);};
161 | median = ceil(n/2.0);
162 | nth_element(items, items+median, items + n, splitK);
163 | }
164 |
165 | if (median == 0 || median == n) {median = ceil(n/2.0);}
166 | space[0] = nodeT(items, median, space+1, leafSize);
167 | space[2*median-1] = nodeT(items+median, n-median, space+2*median, leafSize);
168 | left = space;
169 | right = space+2*median-1;
170 | left->sib = right;
171 | right->sib = left;
172 | }
173 | }
174 |
175 | void constructParallel(nodeT *space, objT** scratch, intT* flags, intT leafSize) {
176 | boundingBoxParallel();
177 | sib = NULL;
178 | if (n <= leafSize) {
179 | left = NULL; right = NULL;
180 | } else {
181 | if (!space[0].isEmpty() || !space[1].isEmpty()) {
182 | cout << "error, kdNode overwrite, abort" << endl;abort();}
183 | intT k = findWidest();
184 | floatT xM = (pMax[k]+pMin[k])/2;
185 |
186 | intT median;
187 | if (spatialMedian) {
188 | median = splitItemParallel(xM, scratch, flags);
189 | } else {
190 | auto splitK = [&](objT* a, objT* b) {
191 | return a->coordinate(k) < b->coordinate(k);};
192 | median = ceil(n/2.0);
193 | nth_element(items, items+median, items + n, splitK);//todo
194 | }
195 |
196 | if (median == 0 || median == n) {median = (n/2.0);}
197 | par_do([&](){space[0] = nodeT(items, median, space+1, scratch, flags, leafSize);},
198 | [&](){space[2*median-1] = nodeT(items+median, n-median, space+2*median, scratch+median, flags+median, leafSize);});
199 | left = space;
200 | right = space+2*median-1;
201 | left->sib = right;
202 | right->sib = left;
203 | }
204 | }
205 |
206 | public:
207 | inline nodeT* L() {return left;}
208 | inline nodeT* R() {return right;}
209 | inline nodeT* siblin() {return sib;}//todo
210 | inline pair getBox() {return make_pair(pMin, pMax);}
211 | inline objT** getItems() {return items;}
212 | inline objT* getItem(intT i) {return items[i];}
213 | inline intT size() {return n;}
214 | inline objT* operator[](intT i) {return items[i];}
215 | inline pointT getMax() {return pMax;}
216 | inline pointT getMin() {return pMin;}
217 | inline floatT getMax(intT i) {return pMax[i];}
218 | inline floatT getMin(intT i) {return pMin[i];}
219 |
220 | kdNode(objT** itemss, intT nn, nodeT *space, objT** scratch, intT* flags, intT leafSize=16): items(itemss), n(nn) {
221 | if (n>2000) constructParallel(space, scratch, flags, leafSize);
222 | else constructSerial(space, leafSize);}
223 | kdNode(objT** itemss, intT nn, nodeT *space, intT leafSize=16): items(itemss), n(nn) {
224 | constructSerial(space, leafSize);}
225 |
226 | void setEmpty() {n=-1;}
227 | bool isEmpty() {return n<0;}
228 | bool isLeaf() {return !left;}//check
229 |
230 | floatT nodeDiag() {//todo change name
231 | floatT result = 0;
232 | for (int d = 0; d < dim; ++ d) {
233 | floatT tmp = pMax[d] - pMin[d];
234 | result += tmp * tmp;
235 | }
236 | return sqrt(result);
237 | }
238 |
239 | // return maximum span of node bounding box among all dimensions
240 | inline floatT lMax() {
241 | floatT myMax = 0;
242 | for (int d=0; d myMax) {
245 | myMax = thisMax;}
246 | }
247 | return myMax;
248 | }
249 |
250 | //return the bb distance with and n2
251 | inline floatT nodeDistance(nodeT* n2) {
252 | for (int d = 0; d < dim; ++ d) {
253 | if (pMin[d] > n2->pMax[d] || n2->pMin[d] > pMax[d]) {
254 | // disjoint at dim d, and intersect on dim < d
255 | floatT rsqr = 0;
256 | for (int dd = d; dd < dim; ++ dd) {
257 | floatT tmp = max(pMin[dd]-n2->pMax[dd], n2->pMin[dd]-pMax[dd]);
258 | tmp = max(tmp, (floatT)0);
259 | rsqr += tmp*tmp;
260 | }
261 | return sqrt(rsqr);
262 | }
263 | }
264 | return 0; // intersect
265 | }
266 |
267 | //return the far bb distance between n1 and n2
268 | inline floatT nodeFarDistance(nodeT* n2) {
269 | floatT result = 0;
270 | for (int d = 0; d < dim; ++ d) {
271 | floatT tmp = max(pMax[d],n2->pMax[d]) - min(pMin[d],n2->pMin[d]);
272 | result += tmp *tmp;
273 | }
274 | return sqrt(result);
275 | }
276 |
277 | //whether well separated with v
278 | inline bool wellSeparated(nodeT *v, floatT s=2) {
279 | floatT circleDiam_u = 0;
280 | floatT circleDiam_v = 0;
281 | floatT circleDistance = 0;
282 | for (int d = 0; d < dim; ++ d) {
283 | floatT uTmpDiff = pMax[d] - pMin[d];
284 | floatT vTmpDiff = v->pMax[d] - v->pMin[d];
285 | floatT uTmpAvg = (pMax[d] + pMin[d])/2;
286 | floatT vTmpAvg = (v->pMax[d] + v->pMin[d])/2;
287 | circleDistance += (uTmpAvg - vTmpAvg) * (uTmpAvg - vTmpAvg);
288 | circleDiam_u += uTmpDiff * uTmpDiff;
289 | circleDiam_v += vTmpDiff * vTmpDiff;
290 | }
291 | circleDiam_u = sqrt(circleDiam_u);
292 | circleDiam_v = sqrt(circleDiam_v);
293 | floatT myRadius = max(circleDiam_u, circleDiam_v)/2;
294 | circleDistance = sqrt(circleDistance) - circleDiam_u/2 - circleDiam_v/2;
295 | return circleDistance >= (s * myRadius);
296 | }
297 |
298 | //vecT need to be vector
299 | template
300 | void rangeNeighbor(pointT queryPt, floatT r, pointT pMin1, pointT pMax1, vecT* accum) {
301 | int relation = boxCompare(pMin1, pMax1, pMin, pMax);
302 | if (relation == boxInclude) {
303 | for(intT i=0; igetCoordObj()->dist(queryPt) <= r)
305 | accum->push_back(items[i]);
306 | }
307 | } else if (relation == boxOverlap) {
308 | if (isLeaf()) {
309 | for(intT i=0; igetCoordObj()->dist(queryPt) <= r &&
311 | itemInBox(pMin1, pMax1, items[i])) accum->push_back(items[i]);
312 | }
313 | } else {
314 | left->rangeNeighbor(queryPt, r, pMin1, pMax1, accum);
315 | right->rangeNeighbor(queryPt, r, pMin1, pMax1, accum);}
316 | }
317 | }
318 |
319 | template
320 | void rangeNeighbor(pointT queryPt, floatT r, pointT pMin1, pointT pMax1, func term, func2 doTerm) {
321 | if (term()) return;
322 | int relation = boxCompare(pMin1, pMax1, pMin, pMax);
323 | if (relation == boxInclude) {
324 | for(intT i=0; igetCoordObj()->dist(queryPt) <= r &&
326 | doTerm(items[i])) break;
327 | }
328 | } else if (relation == boxOverlap) {
329 | if (isLeaf()) {
330 | for(intT i=0; igetCoordObj()->dist(queryPt) <= r &&
332 | doTerm(items[i])) break;
333 | }
334 | } else {
335 | left->rangeNeighbor(queryPt, r, pMin1, pMax1, term, doTerm);
336 | right->rangeNeighbor(queryPt, r, pMin1, pMax1, term, doTerm);}
337 | }
338 | }
339 |
340 | struct bcp {
341 | objT* u;
342 | objT* v;
343 | floatT dist;
344 | bcp(objT* uu, objT* vv, floatT distt): u(uu), v(vv), dist(distt) {}
345 | bcp(): u(NULL), v(NULL), dist(floatMax()) {}
346 | void update(objT* uu, objT* vv) {
347 | auto distt = uu->dist(*vv);
348 | if (distt < dist) {
349 | u = uu; v = vv; dist = distt;}
350 | }
351 | void update(objT* uu, objT* vv, floatT distt) {
352 | if (distt < dist) {
353 | u = uu; v = vv; dist = distt;}
354 | }
355 | };
356 |
357 | inline void compBcpH(nodeT* n2, bcp* r) {
358 | if (nodeDistance(n2) > r->dist) return;
359 |
360 | if (isLeaf() && n2->isLeaf()) {//basecase
361 | for (intT i=0; isize(); ++j) {
363 | r->update(items[i], n2->items[j]);}
364 | }
365 | } else {//recursive, todo consider call order, might help
366 | if (isLeaf()) {
367 | if (nodeDistance(n2->left) < nodeDistance(n2->right)) {
368 | compBcpH(n2->left, r);
369 | compBcpH(n2->right, r);
370 | } else {
371 | compBcpH(n2->right, r);
372 | compBcpH(n2->left, r);
373 | }
374 | } else if (n2->isLeaf()) {
375 | if (n2->nodeDistance(left) < n2->nodeDistance(right)) {
376 | n2->compBcpH(left, r);
377 | n2->compBcpH(right, r);
378 | } else {
379 | n2->compBcpH(right, r);
380 | n2->compBcpH(left, r);
381 | }
382 | } else {
383 | pair ordering[4];
384 | ordering[0] = make_pair(n2->left, left);
385 | ordering[1] = make_pair(n2->right, left);
386 | ordering[2] = make_pair(n2->left, right);
387 | ordering[3] = make_pair(n2->right, right);
388 | auto bbd = [&](pair p1, pair p2) {
389 | return p1.first->nodeDistance(p1.second) < p2.first->nodeDistance(p2.second);};
390 | quickSortSerial(ordering, 4, bbd);
391 | for (intT o=0; o<4; ++o) {
392 | ordering[o].first->compBcpH(ordering[o].second, r);}
393 | }
394 | }
395 | }
396 |
397 | inline bcp compBcp(nodeT* n2) {
398 | auto r = bcp();
399 | compBcpH(n2, &r);
400 | // for (intT i=0; isize(); ++j) {
402 | // r.update(items[i], n2->items[j]);}
403 | // }
404 | return r;
405 | }
406 |
407 | };
408 |
--------------------------------------------------------------------------------
/include/dbscan/kdTree.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020 Yiqiu Wang
2 | //
3 | // Permission is hereby granted, free of charge, to any person obtaining a
4 | // copy of this software and associated documentation files (the
5 | // "Software"), to deal in the Software without restriction, including
6 | // without limitation the rights (to use, copy, modify, merge, publish,
7 | // distribute, sublicense, and/or sell copies of the Software, and to
8 | // permit persons to whom the Software is furnished to do so, subject to
9 | // the following conditions:
10 | //
11 | // The above copyright notice and this permission notice shall be included
12 | // in all copies or substantial portions of the Software.
13 | //
14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
22 | #pragma once
23 |
24 | #include
25 | #include
26 | #include "pbbs/parallel.h"
27 | #include "pbbs/utils.h"
28 | #include "pbbs/sequence.h"
29 | #include "pbbs/quickSort.h"
30 | #include "point.h"
31 | #include "kdNode.h"
32 |
33 | // *************************************************************
34 | // A generic parallel Euclidean kdTree
35 | // *************************************************************
36 | // objT needs to be templatized with int dim,
37 | // and supports coordinate() that returns floatT[dim],
38 | // and coordinate(i) that returns floatT* A[i]
39 | // and dist(objT p2) that computes euclidean distance with p2
40 | // and objT need an empty constructor for empty value
41 |
42 | template
43 | class kdTree {
44 | typedef double floatT;
45 | typedef point pointT;
46 | typedef kdNode nodeT;
47 | objT **items;
48 | nodeT *root;
49 | intT n;
50 |
51 | public:
52 | nodeT* rootNode() {return root;}
53 | intT size() {return n;}
54 |
55 | kdTree(objT* P, intT nn, bool parallel=true, bool noCoarsen=false): n(nn) {
56 | items = newA(objT*, n);
57 | parallel_for(0, n, [&](intT i) {
58 | items[i]=&P[i];
59 | });
60 | root = newA(nodeT, 2*n-1);
61 | parallel_for(0, 2*n-1, [&](intT i) {
62 | root[i].setEmpty();
63 | });
64 | if (parallel) {
65 | objT** scratch = newA(objT*, n);
66 | intT* flags = newA(intT, n);
67 | root[0] = nodeT(items, n, root+1, scratch, flags, noCoarsen ? 1 : 16);
68 | free(scratch);
69 | free(flags);
70 | } else {
71 | root[0] = nodeT(items, n, root+1, noCoarsen ? 1 : 16);}
72 | }
73 | ~kdTree() {
74 | free(items);
75 | free(root);}
76 |
77 | template
78 | vecT* rangeNeighbor(objT* query, floatT r, func term, func2 doTerm, bool cache=false, vecT* accum=NULL) {
79 | pointT pMin1 = pointT();
80 | pointT pMax1 = pointT();
81 | pointT queryPt = pointT();
82 | floatT* center = query->coordinate();
83 | for (int i=0; irangeNeighbor(queryPt, r, pMin1, pMax1, accum);
90 | for (auto accum_i : *accum) {
91 | if(doTerm(accum_i)) break;
92 | }
93 | return accum;
94 | } else {
95 | root->rangeNeighbor(queryPt, r, pMin1, pMax1, term, doTerm);
96 | return NULL;
97 | }
98 | }
99 |
100 | objT** kNN(objT* q, intT k, objT** R=NULL) {
101 | return rootNode()->kNN(q, k, R);
102 | }
103 |
104 | };
105 |
--------------------------------------------------------------------------------
/include/dbscan/pbbs/gettime.h:
--------------------------------------------------------------------------------
1 | #ifndef GETTIME_H
2 | #define GETTIME_H
3 |
4 | /*
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | struct timing {
11 | double totalTime;
12 | double lastTime;
13 | double totalWeight;
14 | bool on;
15 | struct timezone tzp;
16 | timing() {
17 | struct timezone tz = {0, 0};
18 | totalTime=0.0;
19 | totalWeight=0.0;
20 | on=0; tzp = tz;}
21 | void clear() { totalTime=0.0; totalWeight=0.0; on=0;}
22 | double getTime() {
23 | timeval now;
24 | gettimeofday(&now, &tzp);
25 | return ((double) now.tv_sec) + ((double) now.tv_usec)/1000000.;
26 | }
27 | void start () {
28 | on = 1;
29 | lastTime = getTime();
30 | }
31 | double stop () {
32 | on = 0;
33 | double d = (getTime()-lastTime);
34 | totalTime += d;
35 | return d;
36 | }
37 | double stop (double weight) {
38 | on = 0;
39 | totalWeight += weight;
40 | double d = (getTime()-lastTime);
41 | totalTime += weight*d;
42 | return d;
43 | }
44 |
45 | double total() {
46 | if (on) return totalTime + getTime() - lastTime;
47 | else return totalTime;
48 | }
49 |
50 | double next() {
51 | if (!on) return 0.0;
52 | double t = getTime();
53 | double td = t - lastTime;
54 | totalTime += td;
55 | lastTime = t;
56 | return td;
57 | }
58 |
59 | void reportT(double time) {
60 | std::cout << "PBBS-time: " << std::setprecision(3) << time << std::endl;;
61 | }
62 |
63 | void reportTime(double time) {
64 | reportT(time);
65 | }
66 |
67 | void reportStop(double weight, std::string str) {
68 | std::cout << str << " :" << weight << ": ";
69 | reportTime(stop(weight));
70 | }
71 |
72 | void reportTotal() {
73 | double to = (totalWeight > 0.0) ? total()/totalWeight : total();
74 | reportTime(to);
75 | totalTime = 0.0;
76 | totalWeight = 0.0;
77 | }
78 |
79 | void reportTotal(std::string str) {
80 | std::cout << str << " : ";
81 | reportTotal();}
82 |
83 | void reportNext() {reportTime(next());}
84 |
85 | void reportNext(std::string str) {std::cout << str << " : "; reportNext();}
86 | };
87 |
88 | // static timer _tm;
89 | // #define timeStatement(_A,_string) _tm.start(); _A; _tm.reportNext(_string);
90 | // #define startTime() _tm.start();
91 | // #define stopTime(_weight,_str) _tm.reportStop(_weight,_str);
92 | // #define reportTime(_str) _tm.reportTotal(_str);
93 | // #define nextTime(_string) _tm.reportNext(_string);
94 | // #define nextTimeN() _tm.reportT(_tm.next());
95 |
96 | */
97 |
98 | #endif
99 |
--------------------------------------------------------------------------------
/include/dbscan/pbbs/ndHash.h:
--------------------------------------------------------------------------------
1 | // This code is part of the Problem Based Benchmark Suite (PBBS)
2 | // Copyright (c) 2010 Guy Blelloch and the PBBS team
3 | //
4 | // Permission is hereby granted, free of charge, to any person obtaining a
5 | // copy of this software and associated documentation files (the
6 | // "Software"), to deal in the Software without restriction, including
7 | // without limitation the rights (to use, copy, modify, merge, publish,
8 | // distribute, sublicense, and/or sell copies of the Software, and to
9 | // permit persons to whom the Software is furnished to do so, subject to
10 | // the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included
13 | // in all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #ifndef ND_HASH_H
24 | #define ND_HASH_H
25 |
26 | #include "parallel.h"
27 | #include "utils.h"
28 | #include "sequence.h"
29 | #include
30 | using namespace std;
31 |
32 | extern intT g_dim;
33 |
34 | template
35 | class Table {
36 | public:
37 | // typedef typename std::atomic eType;
38 | typedef typename HASH::eType eType;
39 | typedef typename HASH::kType kType;
40 | intT m;
41 | intT mask;
42 | eType empty;
43 | HASH hashStruct;
44 | eType* TA;
45 | intT* compactL;
46 | float load;
47 |
48 | // needs to be in separate routine due to Cilk bugs
49 | static void clearA(eType* A, intT n, eType v) {
50 | parallel_for(0, n, [&](intT i) {A[i] = v;});
51 | }
52 |
53 | /*
54 | // needs to be in separate routine due to Cilk bugs
55 | void clear() {
56 | parallel_for(0, m, [&](intT i) {TA[i] = empty;});
57 | }
58 | */
59 |
60 | struct notEmptyF {
61 | eType e; notEmptyF(eType _e) : e(_e) {}
62 | int operator() (eType a) {return e != a;}};
63 |
64 | uintT hashToRange(intT h) {return h & mask;}
65 | intT firstIndex(kType v) {return hashToRange(hashStruct.hash(v));}
66 | intT incrementIndex(intT h) {return hashToRange(h+1);}
67 | intT decrementIndex(intT h) {return hashToRange(h-1);}
68 | bool lessIndex(intT a, intT b) {return 2 * hashToRange(a - b) > m;}
69 |
70 | /*
71 | // Size is the maximum number of values the hash table will hold.
72 | // Overfilling the table could put it into an infinite loop.
73 | Table(intT size, HASH hashF, float _load) :
74 | m((intT)1 << utils::log2Up(100+(intT)(_load*(float)size))),
75 | mask(m-1),
76 | empty(hashF.empty()),
77 | hashStruct(hashF),
78 | TA(newA(eType,m)),
79 | compactL(NULL),
80 | load(_load)
81 | { clearA(TA,m,empty);
82 | }
83 | */
84 |
85 | Table(intT size, HASH hashF) :
86 | m((intT)1 << utils::log2Up(100+(intT)(2.0*(float)size))),
87 | mask(m-1),
88 | empty(hashF.empty()),
89 | hashStruct(hashF),
90 | TA(newA(eType,m)),
91 | compactL(NULL),
92 | load(2.0)
93 | { clearA(TA,m,empty);
94 | }
95 |
96 | /*
97 | // Constructor that takes an array for the hash table space. The
98 | // passed size must be a power of 2 and will not be rounded. Make
99 | // sure to not call del() if you are passing a pointer to the middle
100 | // of an array.
101 | Table(intT size, eType* _TA, HASH hashF) :
102 | m(size),
103 | mask(m-1),
104 | empty(hashF.empty()),
105 | hashStruct(hashF),
106 | TA(newA(eType,m)),
107 | compactL(NULL),
108 | load(1.0)
109 | { clearA(TA,m,empty);
110 | }
111 |
112 | void setActive(intT mm) {
113 | m = (intT)1 << utils::log2Up(100+(intT)(load*(float)mm));
114 | mask = m-1;
115 | }
116 | */
117 |
118 | // Deletes the allocated arrays
119 | void del() {
120 | free(TA);
121 | if (compactL != NULL) free(compactL);
122 | }
123 |
124 | //for equal keys, first one to arrive at location wins, linear probing
125 | bool insert(eType v) {
126 | kType vkey = hashStruct.getKey(v);
127 | intT h = firstIndex(vkey);
128 | while (1) {
129 | eType c;
130 | c = TA[h];
131 | // intT cmp;
132 | // if(c==empty && utils::CAS(&TA[h],c,v)) return 1;
133 | if(c==empty && hashStruct.cas(&TA[h],c,v)) return 1;
134 | else if(0 == hashStruct.cmp(vkey,hashStruct.getKey(c))) {
135 | if(!hashStruct.replaceQ(v,c))
136 | return 0;
137 | // else if (utils::CAS(&TA[h],c,v))
138 | else if (hashStruct.cas(&TA[h],c,v))
139 | return 1;
140 | }
141 | // move to next bucket
142 | h = incrementIndex(h);
143 | }
144 | return 0; // should never get here
145 | }
146 |
147 | /*
148 | //for equal keys, first one to arrive at location wins, linear probing
149 | bool insertWithDuplicates(eType v) {
150 | kType vkey = hashStruct.getKey(v);
151 | intT h = firstIndex(vkey);
152 | while (1) {
153 | eType c;
154 | c = TA[h];
155 | if(c==empty && utils::CAS(&TA[h],c,v)) return 1;
156 | // move to next bucket
157 | h = incrementIndex(h);
158 | }
159 | return 0; // should never get here
160 | }
161 |
162 | // needs to be more thoroughly tested
163 | // currently always returns true
164 | bool deleteVal(kType v) {
165 | intT i = firstIndex(v);
166 | int cmp = 1;
167 |
168 | // find first element less than or equal to v in priority order
169 | intT j = i;
170 | eType c = TA[j];
171 |
172 | if (c == empty) return true;
173 |
174 | // find first location with priority less or equal to v's priority
175 | while(c != empty && (cmp = hashStruct.cmp(v,hashStruct.getKey(c))) != 0) {
176 | j = incrementIndex(j);
177 | c = TA[j];
178 | }
179 | cmp=(c==empty)?1:hashStruct.cmp(v,hashStruct.getKey(c));
180 | while (1) {
181 | // Invariants:
182 | // v is the key that needs to be deleted
183 | // j is our current index into TA
184 | // if v appears in TA, then at least one copy must appear at or before j
185 | // c = TA[j] at some previous time (could now be changed)
186 | // i = h(v)
187 | // cmp = compare v to key of c (1 if greater, 0 equal, -1 less)
188 |
189 | if (cmp != 0){//why doesn't the following work as the condition???
190 | //c==empty || hashStruct.cmp(v,hashStruct.getKey(c)) != 0) {
191 | // v does not match key of c, need to move down one and exit if
192 | // moving before h(v)
193 | if (j == i) return true;
194 | j = decrementIndex(j);
195 | c = TA[j];
196 | cmp = (c == empty) ? 1 : hashStruct.cmp(v, hashStruct.getKey(c));
197 | } else { // found v at location j (at least at some prior time)
198 |
199 | // Find next available element to fill location j.
200 | // This is a little tricky since we need to skip over elements for
201 | // which the hash index is greater than j, and need to account for
202 | // things being moved downwards by others as we search.
203 | // Makes use of the fact that values in a cell can only decrease
204 | // during a delete phase as elements are moved from the right to left.
205 | intT jj = incrementIndex(j);
206 | eType x = TA[jj];
207 | while (x != empty && lessIndex(j, firstIndex(hashStruct.getKey(x)))) {
208 | jj = incrementIndex(jj);
209 | x = TA[jj];
210 | }
211 | intT jjj = decrementIndex(jj);
212 | while (jjj != j) {
213 | eType y = TA[jjj];
214 | if (y == empty || !lessIndex(j, firstIndex(hashStruct.getKey(y)))) {
215 | x = y;
216 | jj = jjj;
217 | }
218 | jjj = decrementIndex(jjj);
219 | }
220 |
221 | // try to copy the the replacement element into j
222 | if (utils::CAS(&TA[j],c,x)) {
223 | // swap was successful
224 | // if the replacement element was empty, we are done
225 | if (x == empty) return true;
226 |
227 | // Otherwise there are now two copies of the replacement element x
228 | // delete one copy (probably the original) by starting to look at jj.
229 | // Note that others can come along in the meantime and delete
230 | // one or both of them, but that is fine.
231 | v = hashStruct.getKey(x);
232 | j = jj;
233 | i = firstIndex(v);
234 | }
235 | c = TA[j];
236 | cmp = (c == empty) ? 1 : hashStruct.cmp(v, hashStruct.getKey(c));
237 | }
238 | }
239 | }
240 | */
241 |
242 | // Returns the value if an equal value is found in the table
243 | // otherwise returns the "empty" element.
244 | // due to prioritization, can quit early if v is greater than cell
245 | eType find(kType v) {
246 | intT h = firstIndex(v);
247 | eType c = TA[h];
248 | while (1) {
249 | if (c == empty) return empty;
250 | else if (!hashStruct.cmp(v,hashStruct.getKey(c)))
251 | return c;
252 | h = incrementIndex(h);
253 | c = TA[h];
254 | }
255 | }
256 |
257 | /*
258 | eType findWithDuplicates(eType v) {
259 | kType vKey = hashStruct.getKey(v);
260 | intT h = firstIndex(vKey);
261 | eType c = TA[h];
262 | while (1) {
263 | if (c == empty) {
264 | return empty;
265 | } else if (!hashStruct.cmp(vKey,hashStruct.getKey(c))) {
266 | intT* vCoordinates = (intT *)v.first;
267 | intT* cCoordinates = (intT *)c.first;
268 | long i = 0;
269 | for(;i(TA,m,utils::addF(),notEmptyF(empty));
287 | }
288 |
289 | /*
290 | // returns all the current entries compacted into a sequence
291 | _seq entries() {
292 | bool *FL = newA(bool,m);
293 | parallel_for (0, m, [&](intT i) {
294 | FL[i] = (TA[i] != empty);});
295 | _seq R = sequence::pack(TA,FL,m);
296 | free(FL);
297 | return R;
298 | }
299 |
300 | // prints the current entries along with the index they are stored at
301 | void print() {
302 | cout << "vals = ";
303 | for (intT i=0; i < m; i++)
304 | if (TA[i] != empty)
305 | cout << i << ":" << TA[i] << ",";
306 | cout << endl;
307 | }
308 |
309 | */
310 | };
311 |
312 | /*
313 | template
314 | _seq removeDuplicates(_seq S, intT m, HASH hashF) {
315 | Table T(m,hashF,1.0);
316 | ET* A = S.A;
317 | parallel_for(0, S.n, [&](intT i) { T.insert(A[i]);});
318 | _seq R = T.entries();
319 | T.del();
320 | return R;
321 | }
322 |
323 | template
324 | _seq removeDuplicates(_seq S, HASH hashF) {
325 | return removeDuplicates(S, S.n, hashF);
326 | }
327 |
328 | template
329 | struct hashInt {
330 | typedef intT eType;
331 | typedef intT kType;
332 | eType empty() {return -1;}
333 | kType getKey(eType v) {return v;}
334 | intT hash(kType v) {return utils::hash(v);}
335 | int cmp(kType v, kType b) {return (v > b) ? 1 : ((v == b) ? 0 : -1);}
336 | bool replaceQ(eType v, eType b) {return 0;}
337 | };
338 |
339 | //typedef Table IntTable;
340 | //static IntTable makeIntTable(int m) {return IntTable(m,hashInt());}
341 | template
342 | static Table,intT > makeIntTable(intT m, float load) {
343 | return Table,intT >(m,hashInt(),load);}
344 |
345 | struct hashStr {
346 | typedef char* eType;
347 | typedef char* kType;
348 |
349 | eType empty() {return NULL;}
350 | kType getKey(eType v) {
351 | return v;}
352 |
353 | uintT hash(kType s) {
354 | uintT hash = 0;
355 | while (*s) hash = *s++ + (hash << 6) + (hash << 16) - hash;
356 | return hash;
357 | }
358 |
359 | int cmp(kType s, kType s2) {
360 | while (*s && *s==*s2) {s++; s2++;};
361 | return (*s > *s2) ? 1 : ((*s == *s2) ? 0 : -1);
362 | }
363 |
364 | bool replaceQ(eType s, eType s2) {return 0;}
365 | };
366 |
367 | template
368 | static Table makeStrTable(intT m, float load) {
369 | return Table(m,hashStr(),load);}
370 |
371 | template
372 | struct hashPair {
373 | KEYHASH keyHash;
374 | typedef typename KEYHASH::kType kType;
375 | typedef pair* eType;
376 | eType empty() {return NULL;}
377 |
378 | hashPair(KEYHASH _k) : keyHash(_k) {}
379 |
380 | kType getKey(eType v) { return v->first; }
381 |
382 | uintT hash(kType s) { return keyHash.hash(s);}
383 | int cmp(kType s, kType s2) { return keyHash.cmp(s, s2);}
384 |
385 | bool replaceQ(eType s, eType s2) {
386 | return 0;}//s->second > s2->second;}
387 | };
388 |
389 | static _seq*> removeDuplicates(_seq*> S) {
390 | return removeDuplicates(S,hashPair(hashStr()));}
391 |
392 | */
393 |
394 | template
395 | struct hashSimplePair {
396 | // typedef pair eType;
397 | typedef myPair eType;
398 | typedef intT kType;
399 | // eType empty() {return pair(-1,-1);}
400 | eType empty() {return myPair();}
401 | kType getKey(eType v) { return v.first; }
402 | uintT hash(intT s) { return utils::hash(s);}
403 | int cmp(intT v, intT b) {return (v > b) ? 1 : ((v == b) ? 0 : -1);}
404 | bool replaceQ(eType s, eType s2) {return 0;}//return s.second > s2.second;}
405 | bool cas(eType* p, eType o, eType n) {
406 | return std::atomic_compare_exchange_strong_explicit(
407 | reinterpret_cast*>(p), &o, n, std::memory_order_relaxed, std::memory_order_relaxed);
408 | }
409 | };
410 |
411 | /*
412 | // static _seq > removeDuplicates(_seq > A) {
413 | // return removeDuplicates(A,hashSimplePair());
414 | // }
415 |
416 | template
417 | static _seq removeDuplicates(_seq A) {
418 | return removeDuplicates(A,hashSimplePair());
419 | }
420 | */
421 |
422 | #endif
423 |
--------------------------------------------------------------------------------
/include/dbscan/pbbs/parallel.h:
--------------------------------------------------------------------------------
1 | #ifndef PARALLEL_H
2 | #define PARALLEL_H
3 |
4 | #include
5 | #include
6 | using namespace std;
7 |
8 | typedef int intT;
9 | typedef unsigned int uintT;
10 | typedef double floatT;
11 | static intT intMax() {return numeric_limits::max();}
12 | static floatT floatMax() {return numeric_limits::max();}
13 |
14 | #define HOMEGROWN
15 |
16 | #if defined(OPENCILK)
17 |
18 | #include
19 | #include
20 | #define parallel_main main
21 | #define par_for cilk_for
22 | #define par_for_1 _Pragma("cilk_grainsize = 1") cilk_for
23 | #define par_for_256 _Pragma("cilk_grainsize = 256") cilk_for
24 |
25 | extern "C" int __cilkrts_internal_worker_id(void);
26 |
27 | static int getWorkers() {
28 | return __cilkrts_get_nworkers();}
29 | static int getWorkerId() {return __cilkrts_internal_worker_id();}
30 | static void setWorkers(int n) { }
31 | static void printScheduler() {
32 | cout << "scheduler = OpenCilk" << endl;
33 | cout << "num-threads = " << getWorkers() << endl;
34 | }
35 |
36 | //new syntax:
37 |
38 | inline size_t num_workers() {
39 | return __cilkrts_get_nworkers();}
40 |
41 | inline size_t worker_id() {
42 | return __cilkrts_internal_worker_id();}
43 |
44 | template
45 | inline void parallel_for(size_t start, size_t end, F f,
46 | size_t granularity=0,
47 | bool conservative=false) {
48 | if (end > start) {
49 | if (granularity == 1) {
50 | _Pragma("cilk_grainsize = 1") cilk_for(size_t i=start; i
60 | inline void par_do(Lf left, Rf right, bool conservative=false) {
61 | cilk_spawn left();
62 | right();
63 | cilk_sync;
64 | }
65 |
66 | #elif defined(CILK)
67 |
68 | #include
69 | #include
70 | #define parallel_main main
71 | #define par_for cilk_for
72 | #define par_for_1 _Pragma("cilk_grainsize = 1") cilk_for
73 | #define par_for_256 _Pragma("cilk_grainsize = 256") cilk_for
74 |
75 | static int getWorkers() {
76 | return __cilkrts_get_nworkers();}
77 | static int getWorkerId() {return __cilkrts_get_worker_number();}
78 | static void setWorkers(int n) { }
79 | static void printScheduler() {
80 | cout << "scheduler = CilkPlus" << endl;
81 | cout << "num-threads = " << getWorkers() << endl;
82 | }
83 |
84 | //new syntax:
85 |
86 | inline size_t num_workers() {
87 | return __cilkrts_get_nworkers();}
88 |
89 | inline size_t worker_id() {
90 | return __cilkrts_get_worker_number();}
91 |
92 | template
93 | inline void parallel_for(size_t start, size_t end, F f,
94 | size_t granularity=0,
95 | bool conservative=false) {
96 | if (end > start) {
97 | if (granularity == 1) {
98 | _Pragma("cilk_grainsize = 1") cilk_for(size_t i=start; i
108 | inline void par_do(Lf left, Rf right, bool conservative=false) {
109 | cilk_spawn left();
110 | right();
111 | cilk_sync;
112 | }
113 |
114 | #elif defined(HOMEGROWN)
115 |
116 | #include "scheduler.h"
117 |
118 | namespace parlay {
119 | namespace internal {
120 | // Use a "Meyer singleton" to provide thread-safe
121 | // initialisation and destruction of the scheduler
122 | //
123 | // The declaration of get_default_scheduler must be
124 | // extern inline to ensure that there is only ever one
125 | // copy of the scheduler. This is guaranteed by the C++
126 | // standard: 7.1.2/4 A static local variable in an
127 | // extern inline function always refers to the same
128 | // object.
129 | extern inline fork_join_scheduler& get_default_scheduler() {
130 | static fork_join_scheduler fj;
131 | return fj;
132 | }
133 |
134 | extern inline void start_scheduler() {
135 | get_default_scheduler().start();
136 | }
137 |
138 | extern inline void stop_scheduler() {
139 | get_default_scheduler().stop();
140 | }
141 | }
142 |
143 | inline size_t num_workers() {
144 | return internal::get_default_scheduler().num_workers();
145 | }
146 |
147 | inline size_t worker_id() {
148 | return internal::get_default_scheduler().worker_id();
149 | }
150 |
151 | template
152 | inline void parallel_for(size_t start, size_t end, F f,
153 | size_t granularity=0,
154 | bool conservative=false) {
155 | if (end > start)
156 | internal::get_default_scheduler().parfor(start, end, f, granularity, conservative);
157 | }
158 |
159 | template
160 | inline void par_do(Lf left, Rf right, bool conservative=false) {
161 | return internal::get_default_scheduler().pardo(left, right, conservative);
162 | }
163 | }
164 |
165 | using namespace parlay;
166 |
167 | #define cilk_spawn
168 | #define cilk_sync
169 | #define parallel_main main
170 | #define par_for for
171 | #define par_for_1 for
172 | #define par_for_256 for
173 |
174 | static int getWorkers() {return (int)num_workers();}
175 | static int getWorkerId() {return (int)worker_id();}
176 | static void setWorkers(int n) { }
177 | static void printScheduler() {
178 | cout << "scheduler = Parlay-HomeGrown" << endl;
179 | cout << "num-threads = " << getWorkers() << endl;}
180 |
181 | #else
182 |
183 | #define cilk_spawn
184 | #define cilk_sync
185 | #define parallel_main main
186 | #define par_for for
187 | #define par_for_1 for
188 | #define par_for_256 for
189 |
190 | static void printScheduler() {
191 | cout << "scheduler = sequential" << endl;}
192 | static int getWorkers() {return 1;}
193 | static int getWorkerId() {return 0;}
194 | static void setWorkers(int n) { }
195 |
196 | //new syntax:
197 |
198 | inline size_t num_workers() {return 1;}
199 | inline size_t worker_id() {return 0;}
200 |
201 | template
202 | inline void parallel_for(size_t start, size_t end, F f,
203 | size_t granularity=0,
204 | bool conservative=false) {
205 | if (end > start) {
206 | for(size_t i=start; i
210 | inline void par_do(Lf left, Rf right, bool conservative=false) {
211 | left();
212 | right();
213 | }
214 |
215 | #endif
216 |
217 | #define nblocks(_n,_bsize) (1 + ((_n)-1)/(_bsize))
218 |
219 | template
220 | inline void blocked_for(intT _s, intT _e, intT _bsize, F f) {
221 | if (_e > _s) {
222 | intT _ss = _s;
223 | intT _ee = _e;
224 | intT _n = _ee-_ss;
225 | intT _l = nblocks(_n,_bsize);
226 | auto body = [&](intT _i) {
227 | intT _s = _ss + _i * (_bsize);
228 | intT _e = min(_s + (_bsize), _ee);
229 | f(_s, _e, _i);
230 | };
231 | parallel_for(0, _l, body);
232 | }
233 | }
234 |
235 | template
236 | inline void granular_for(intT _s, intT _e, intT _thresh, F f, intT granularity=0) {
237 | if (_e - _s > _thresh)
238 | parallel_for(_s, _e, f, granularity);
239 | else
240 | for(intT i=_s; i<_e; ++i) f(i);
241 | }
242 |
243 | #endif
244 |
--------------------------------------------------------------------------------
/include/dbscan/pbbs/parseCommandLine.h:
--------------------------------------------------------------------------------
1 | // This code is part of the Problem Based Benchmark Suite (PBBS)
2 | // Copyright (c) 2011 Guy Blelloch and the PBBS team
3 | //
4 | // Permission is hereby granted, free of charge, to any person obtaining a
5 | // copy of this software and associated documentation files (the
6 | // "Software"), to deal in the Software without restriction, including
7 | // without limitation the rights (to use, copy, modify, merge, publish,
8 | // distribute, sublicense, and/or sell copies of the Software, and to
9 | // permit persons to whom the Software is furnished to do so, subject to
10 | // the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included
13 | // in all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #ifndef _PARSE_COMMAND_LINE
24 | #define _PARSE_COMMAND_LINE
25 |
26 | #include
27 | #include
28 | #include
29 | #include
30 | using namespace std;
31 |
32 | struct commandLine {
33 | int argc;
34 | char** argv;
35 | string comLine;
36 | commandLine(int _c, char** _v, string _cl)
37 | : argc(_c), argv(_v), comLine(_cl) {}
38 |
39 | commandLine(int _c, char** _v)
40 | : argc(_c), argv(_v), comLine("bad arguments") {}
41 |
42 | void badArgument() {
43 | cout << "usage: " << argv[0] << " " << comLine << endl;
44 | abort();
45 | }
46 |
47 | // get an argument
48 | // i is indexed from the last argument = 0, second to last indexed 1, ..
49 | char* getArgument(int i) {
50 | if (argc < 2+i) badArgument();
51 | return argv[argc-1-i];
52 | }
53 |
54 | // looks for two filenames
55 | pair IOFileNames() {
56 | if (argc < 3) badArgument();
57 | return pair(argv[argc-2],argv[argc-1]);
58 | }
59 |
60 | pair sizeAndFileName() {
61 | if (argc < 3) badArgument();
62 | return pair(std::atoi(argv[argc-2]),(char*) argv[argc-1]);
63 | }
64 |
65 | bool getOption(string option) {
66 | for (int i = 1; i < argc; i++)
67 | if ((string) argv[i] == option) return true;
68 | return false;
69 | }
70 |
71 | char* getOptionValue(string option) {
72 | for (int i = 1; i < argc-1; i++)
73 | if ((string) argv[i] == option) return argv[i+1];
74 | return NULL;
75 | }
76 |
77 | string getOptionValue(string option, string defaultValue) {
78 | for (int i = 1; i < argc-1; i++)
79 | if ((string) argv[i] == option) return (string) argv[i+1];
80 | return defaultValue;
81 | }
82 |
83 | int getOptionIntValue(string option, int defaultValue) {
84 | for (int i = 1; i < argc-1; i++)
85 | if ((string) argv[i] == option) {
86 | int r = atoi(argv[i+1]);
87 | if (r < 1) badArgument();
88 | return r;
89 | }
90 | return defaultValue;
91 | }
92 |
93 | long getOptionLongValue(string option, long defaultValue) {
94 | for (int i = 1; i < argc-1; i++)
95 | if ((string) argv[i] == option) {
96 | long r = atol(argv[i+1]);
97 | if (r < 1) badArgument();
98 | return r;
99 | }
100 | return defaultValue;
101 | }
102 |
103 | double getOptionDoubleValue(string option, double defaultValue) {
104 | for (int i = 1; i < argc-1; i++)
105 | if ((string) argv[i] == option) {
106 | double val;
107 | if (sscanf(argv[i+1], "%lf", &val) == EOF) {
108 | badArgument();
109 | }
110 | return val;
111 | }
112 | return defaultValue;
113 | }
114 |
115 | };
116 |
117 | #endif // _PARSE_COMMAND_LINE
118 |
--------------------------------------------------------------------------------
/include/dbscan/pbbs/quickSort.h:
--------------------------------------------------------------------------------
1 | // This code is part of the Problem Based Benchmark Suite (PBBS)
2 | // Copyright (c) 2010 Guy Blelloch and the PBBS team
3 | //
4 | // Permission is hereby granted, free of charge, to any person obtaining a
5 | // copy of this software and associated documentation files (the
6 | // "Software"), to deal in the Software without restriction, including
7 | // without limitation the rights (to use, copy, modify, merge, publish,
8 | // distribute, sublicense, and/or sell copies of the Software, and to
9 | // permit persons to whom the Software is furnished to do so, subject to
10 | // the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included
13 | // in all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | #ifndef QUICK_SORT_H
24 | #define QUICK_SORT_H
25 | #include
26 | #include "parallel.h"
27 |
28 | template
29 | void insertionSort(E* A, intT n, BinPred f) {
30 | for (intT i=0; i < n; i++) {
31 | E v = A[i];
32 | E* B = A + i;
33 | while (--B >= A && f(v,*B)) *(B+1) = *B;
34 | *(B+1) = v;
35 | }
36 | }
37 |
38 | template
39 | E medianOfThree(E a, E b, E c, BinPred f) {
40 | return f(a,b) ? (f(b,c) ? b : (f(a,c) ? c : a))
41 | : (f(a,c) ? a : (f(b,c) ? c : b));
42 | }
43 |
44 |
45 | template
46 | std::pair split(E* A, intT n, BinPred f) {
47 | E p = medianOfThree(A[n/4],A[n/2],A[(3*n)/4],f);
48 | E* L = A; // below L are less than pivot
49 | E* M = A; // between L and M are equal to pivot
50 | E* R = A+n-1; // above R are greater than pivot
51 | while (1) {
52 | while (!f(p,*M)) {
53 | if (f(*M,p)) std::swap(*M,*(L++));
54 | if (M >= R) break;
55 | M++;
56 | }
57 | while (f(p,*R)) R--;
58 | if (M >= R) break;
59 | std::swap(*M,*R--);
60 | if (f(*M,p)) std::swap(*M,*(L++));
61 | M++;
62 | }
63 | return std::pair(L,M);
64 | }
65 | template
66 | void quickSortSerial(E* A, intT n, BinPred f) {
67 | while (n > 20) {
68 | std::pair X = split(A,n,f);
69 | quickSortSerial(X.second, A+n-X.second, f);
70 | n = X.first - A;
71 | }
72 | insertionSort(A,n,f);
73 | }
74 |
75 | template
76 | void quickSort(E* A, intT n, BinPred f) {
77 | if (n < (1 << 8)) quickSortSerial(A, n, f);
78 | else {
79 | std::pair X = split(A,n,f);
80 | par_do([&](){quickSort(A, X.first - A, f);},
81 | [&](){quickSort(X.second, A+n-X.second, f);});
82 | }
83 | }
84 |
85 | #define compSort(__A, __n, __f) (quickSort(__A, __n, __f))
86 |
87 | #endif // _A_QSORT_INCLUDED
88 |
--------------------------------------------------------------------------------
/include/dbscan/pbbs/sampleSort.h:
--------------------------------------------------------------------------------
1 | // This code is part of the Problem Based Benchmark Suite (PBBS)
2 | // Copyright (c) 2010 Guy Blelloch and Harsha Vardhan Simhadri and the PBBS team
3 | //
4 | // Permission is hereby granted, free of charge, to any person obtaining a
5 | // copy of this software and associated documentation files (the
6 | // "Software"), to deal in the Software without restriction, including
7 | // without limitation the rights (to use, copy, modify, merge, publish,
8 | // distribute, sublicense, and/or sell copies of the Software, and to
9 | // permit persons to whom the Software is furnished to do so, subject to
10 | // the following conditions:
11 | //
12 | // The above copyright notice and this permission notice shall be included
13 | // in all copies or substantial portions of the Software.
14 | //
15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 |
23 | // This file is basically the cache-oblivious sorting algorithm from:
24 | //
25 | // Low depth cache-oblivious algorithms.
26 | // Guy E. Blelloch, Phillip B. Gibbons and Harsha Vardhan Simhadri.
27 | // Proc. ACM symposium on Parallelism in algorithms and architectures (SPAA), 2010
28 |
29 | // intT is either "int" or "long" (needs to be "long" if n >= 2^31)
30 |
31 | #ifndef SAMPLE_SORT_H
32 | #define SAMPLE_SORT_H
33 |
34 | #include "parallel.h"
35 | #include "sequence.h"
36 | #include "math.h"
37 | #include "quickSort.h"
38 | #include "transpose.h"
39 | #include "utils.h"
40 |
41 | template
42 | void mergeSeq (E* sA, E* sB, intT* sC, long lA, long lB, BinPred f) {
43 | if (lA==0 || lB==0) return;
44 | E *eA = sA+lA;
45 | E *eB = sB+lB;
46 | for (long i=0; i <= lB; i++) sC[i] = 0;
47 | while(1) {
48 | while (f(*sA, *sB)) {(*sC)++; if (++sA == eA) return;}
49 | sB++; sC++;
50 | if (sB == eB) break;
51 | if (!(f(*(sB-1),*sB))) {
52 | while (!f(*sB, *sA)) {(*sC)++; if (++sA == eA) return;}
53 | sB++; sC++;
54 | if (sB == eB) break;
55 | }
56 | }
57 | *sC = eA-sA;
58 | }
59 |
60 | inline unsigned long hashVal(unsigned long a) {
61 | // 982.. is a largish prime
62 | return (((unsigned long) 982451653 * a) + (unsigned long) 12345);
63 | }
64 |
65 | // the following parameters can be tuned
66 | #define PBBS_QUICKSORT_THRESHOLD 1000
67 | #define PBBS_BLOCK_QUOTIENT 2
68 | #define PBBS_BUCKET_QUOTIENT 2
69 | #define PBBS_OVER_SAMPLE 10
70 |
71 | template
72 | void sampleSort (E* A, intT n, BinPred f) {
73 | if (n < PBBS_QUICKSORT_THRESHOLD) quickSort(A, n, f);
74 | else {
75 | long sqrt = (long) ceil(pow(n,0.5));
76 | long numBlocks = (long) (sqrt/PBBS_BLOCK_QUOTIENT) + 1;
77 | long blockSize = ((n-1)/numBlocks) + 1;
78 | int numBuckets = (int) ((sqrt/PBBS_BUCKET_QUOTIENT) + 1);
79 | long sampleSetSize = numBuckets * PBBS_OVER_SAMPLE;
80 |
81 | E* sampleSet = newA(E,sampleSetSize);
82 |
83 | // generate "random" samples with oversampling
84 | parallel_for(0, sampleSetSize,
85 | [&](intT j) {sampleSet[j] = A[hashVal(j)%n];});
86 |
87 | // sort the samples
88 | quickSort(sampleSet, sampleSetSize, f);
89 |
90 | // subselect samples at even stride
91 | E* pivots = newA(E,numBuckets-1);
92 | parallel_for(0, numBuckets-1,
93 | [&](intT k){pivots[k] = sampleSet[PBBS_OVER_SAMPLE*k];});
94 | free(sampleSet);
95 |
96 | // sort each block and merge with samples to get counts for each bucket
97 | intT *counts = newA(intT, numBlocks*numBuckets);
98 | parallel_for(0, numBlocks,
99 | [&](intT i) {
100 | long offset = i * blockSize;
101 | long size = (i < numBlocks - 1) ? blockSize : n - offset;
102 | quickSort(A+offset, size, f);
103 | mergeSeq(A + offset, pivots, counts + i*numBuckets, size, numBuckets-1, f);
104 | });
105 |
106 | E *B = newA(E, numBlocks*blockSize);
107 | intT *sourceOffsets = newA(intT, numBlocks*numBuckets);
108 | intT *destOffsets = newA(intT, numBlocks*numBuckets);
109 |
110 | // transpose from blocks-major to bucket-major
111 | sequence::scan(counts, sourceOffsets, numBlocks*numBuckets, plus(),(intT)0);
112 | transpose(counts, destOffsets).trans(numBlocks, numBuckets);
113 | sequence::scan(destOffsets, destOffsets,
114 | numBlocks*numBuckets, plus(),(intT)0);
115 | blockTrans(A, B, sourceOffsets,
116 | destOffsets, counts).trans(numBlocks, numBuckets);
117 | free(sourceOffsets);
118 | free(counts);
119 |
120 | // sort within each bucket
121 | parallel_for(0, numBuckets,
122 | [&](intT i) {
123 | long start = destOffsets[i*numBlocks];
124 | long end = (i < numBuckets -1) ? destOffsets[(i+1)*numBlocks] : n;
125 |
126 | // middle buckets need not be sorted if two consecutive pivots are equal
127 | if (i == 0 || i == numBuckets - 1 || f(pivots[i-1],pivots[i]))
128 | quickSort(B+start, end - start, f);
129 |
130 | // copy back to A
131 | for (long j = start; j < end; j++)
132 | A[j] = B[j];
133 | });
134 | free(pivots);
135 | free(destOffsets);
136 | free(B);
137 | }
138 | }
139 |
140 | #undef compSort
141 | #define compSort(__A, __n, __f) (sampleSort(__A, __n, __f))
142 |
143 | #endif
144 |
--------------------------------------------------------------------------------
/include/dbscan/pbbs/scheduler.h:
--------------------------------------------------------------------------------
1 | // Copyright (c) 2020-present Guy Blelloch and other contributors
2 |
3 | // Permission is hereby granted, free of charge, to any person obtaining a copy
4 | // of this software and associated documentation files (the "Software"), to deal
5 | // in the Software without restriction, including without limitation the rights
6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | // copies of the Software, and to permit persons to whom the Software is
8 | // furnished to do so, subject to the following conditions:
9 |
10 | // The above copyright notice and this permission notice shall be included in all
11 | // copies or substantial portions of the Software.
12 |
13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | // SOFTWARE.
20 |
21 | // EXAMPLE USE 1:
22 | //
23 | // fork_join_scheduler fj;
24 | //
25 | // long fib(long i) {
26 | // if (i <= 1) return 1;
27 | // long l,r;
28 | // fj.pardo([&] () { l = fib(i-1);},
29 | // [&] () { r = fib(i-2);});
30 | // return l + r;
31 | // }
32 | //
33 | // fib(40);
34 | //
35 | // EXAMPLE USE 2:
36 | //
37 | // void init(long* x, size_t n) {
38 | // parfor(0, n, [&] (int i) {a[i] = i;});
39 | // }
40 | //
41 |
42 | #ifndef PARLAY_SCHEDULER_H_
43 | #define PARLAY_SCHEDULER_H_
44 |
45 | #include
46 | #include
47 |
48 | #include
49 | #include
50 | #include
51 | #include
52 | #include
53 | #include
54 | #include
55 | #include
56 | #include
57 | #include // IWYU pragma: keep
58 | #include
59 |
60 | #include "work_stealing_job.h"
61 |
62 | namespace parlay {
63 |
64 | // Deque from Arora, Blumofe, and Plaxton (SPAA, 1998).
65 | template
66 | struct Deque {
67 | using qidx = unsigned int;
68 | using tag_t = unsigned int;
69 |
70 | // use std::atomic for atomic access.
71 | // Note: Explicit alignment specifier required
72 | // to ensure that Clang inlines atomic loads.
73 | struct alignas(int64_t) age_t {
74 | tag_t tag;
75 | qidx top;
76 | };
77 |
78 | // align to avoid false sharing
79 | struct alignas(64) padded_job {
80 | std::atomic job;
81 | };
82 |
83 | static constexpr int q_size = 200;
84 | std::atomic bot;
85 | std::atomic age;
86 | std::array deq;
87 |
88 | Deque() : bot(0), age(age_t{0, 0}) {}
89 |
90 | void push_bottom(Job* job) {
91 | auto local_bot = bot.load(std::memory_order_relaxed); // atomic load
92 | deq[local_bot].job.store(job, std::memory_order_relaxed); // shared store
93 | local_bot += 1;
94 | if (local_bot == q_size) {
95 | throw std::runtime_error("internal error: scheduler queue overflow");
96 | }
97 | bot.store(local_bot, std::memory_order_relaxed); // shared store
98 | std::atomic_thread_fence(std::memory_order_seq_cst);
99 | }
100 |
101 | Job* pop_top() {
102 | Job* result = nullptr;
103 | auto old_age = age.load(std::memory_order_relaxed); // atomic load
104 | auto local_bot = bot.load(std::memory_order_relaxed); // atomic load
105 | if (local_bot > old_age.top) {
106 | auto job =
107 | deq[old_age.top].job.load(std::memory_order_relaxed); // atomic load
108 | auto new_age = old_age;
109 | new_age.top = new_age.top + 1;
110 | if (age.compare_exchange_strong(old_age, new_age))
111 | result = job;
112 | else
113 | result = nullptr;
114 | }
115 | return result;
116 | }
117 |
118 | Job* pop_bottom() {
119 | Job* result = nullptr;
120 | auto local_bot = bot.load(std::memory_order_relaxed); // atomic load
121 | if (local_bot != 0) {
122 | local_bot--;
123 | bot.store(local_bot, std::memory_order_relaxed); // shared store
124 | std::atomic_thread_fence(std::memory_order_seq_cst);
125 | auto job =
126 | deq[local_bot].job.load(std::memory_order_relaxed); // atomic load
127 | auto old_age = age.load(std::memory_order_relaxed); // atomic load
128 | if (local_bot > old_age.top)
129 | result = job;
130 | else {
131 | bot.store(0, std::memory_order_relaxed); // shared store
132 | auto new_age = age_t{old_age.tag + 1, 0};
133 | if ((local_bot == old_age.top) &&
134 | age.compare_exchange_strong(old_age, new_age))
135 | result = job;
136 | else {
137 | age.store(new_age, std::memory_order_relaxed); // shared store
138 | result = nullptr;
139 | }
140 | std::atomic_thread_fence(std::memory_order_seq_cst);
141 | }
142 | }
143 | return result;
144 | }
145 | };
146 |
147 | template
148 | struct scheduler {
149 | public:
150 | // see comments under wait(..)
151 | static bool const conservative = false;
152 | unsigned int num_threads;
153 |
154 | static thread_local unsigned int thread_id;
155 |
156 | scheduler()
157 | : num_threads(init_num_workers()),
158 | num_deques(2 * num_threads),
159 | deques(num_deques),
160 | attempts(num_deques),
161 | spawned_threads(),
162 | finished_flag(false) {
163 | // Stopping condition
164 | auto finished = [this]() {
165 | return finished_flag.load(std::memory_order_relaxed);
166 | };
167 |
168 | // Spawn num_threads many threads on startup
169 | thread_id = 0; // thread-local write
170 | for (unsigned int i = 1; i < num_threads; i++) {
171 | spawned_threads.emplace_back([&, i, finished]() {
172 | thread_id = i; // thread-local write
173 | start(finished);
174 | });
175 | }
176 | }
177 |
178 | ~scheduler() {
179 | finished_flag.store(true, std::memory_order_relaxed);
180 | for (unsigned int i = 1; i < num_threads; i++) {
181 | spawned_threads[i - 1].join();
182 | }
183 | }
184 |
185 | // Push onto local stack.
186 | void spawn(Job* job) {
187 | int id = worker_id();
188 | deques[id].push_bottom(job);
189 | }
190 |
191 | // Wait for condition: finished().
192 | template
193 | void wait(F finished, bool conservative = false) {
194 | // Conservative avoids deadlock if scheduler is used in conjunction
195 | // with user locks enclosing a wait.
196 | if (conservative) {
197 | while (!finished()) std::this_thread::yield();
198 | }
199 | // If not conservative, schedule within the wait.
200 | // Can deadlock if a stolen job uses same lock as encloses the wait.
201 | else
202 | start(finished);
203 | }
204 |
205 | // All scheduler threads quit after this is called.
206 | void finish() { finished_flag.store(true, std::memory_order_relaxed); }
207 |
208 | // Pop from local stack.
209 | Job* try_pop() {
210 | auto id = worker_id();
211 | return deques[id].pop_bottom();
212 | }
213 |
214 | #ifdef _MSC_VER
215 | #pragma warning(push)
216 | #pragma warning(disable : 4996) // 'getenv': This function or variable may be unsafe.
217 | #endif
218 |
219 | // Determine the number of workers to spawn
220 | unsigned int init_num_workers() {
221 | if (const auto env_p = std::getenv("PARLAY_NUM_THREADS")) {
222 | return std::stoi(env_p);
223 | } else {
224 | return std::thread::hardware_concurrency();
225 | }
226 | }
227 |
228 | #ifdef _MSC_VER
229 | #pragma warning(pop)
230 | #endif
231 |
232 | unsigned int num_workers() { return num_threads; }
233 | unsigned int worker_id() { return thread_id; }
234 | void set_num_workers(unsigned int) {
235 | std::cout << "Unsupported" << std::endl;
236 | exit(-1);
237 | }
238 |
239 | private:
240 | // Align to avoid false sharing.
241 | struct alignas(128) attempt {
242 | size_t val;
243 | };
244 |
245 | int num_deques;
246 | std::vector> deques;
247 | std::vector attempts;
248 | std::vector spawned_threads;
249 | std::atomic finished_flag;
250 |
251 | // Start an individual scheduler task. Runs until finished().
252 | template
253 | void start(F finished) {
254 | while (true) {
255 | Job* job = get_job(finished);
256 | if (!job) return;
257 | (*job)();
258 | }
259 | }
260 |
261 | Job* try_steal(size_t id) {
262 | // use hashing to get "random" target
263 | size_t target = (hash(id) + hash(attempts[id].val)) % num_deques;
264 | attempts[id].val++;
265 | return deques[target].pop_top();
266 | }
267 |
268 | // Find a job, first trying local stack, then random steals.
269 | template
270 | Job* get_job(F finished) {
271 | if (finished()) return nullptr;
272 | Job* job = try_pop();
273 | if (job) return job;
274 | size_t id = worker_id();
275 | while (true) {
276 | // By coupon collector's problem, this should touch all.
277 | for (int i = 0; i <= num_deques * 100; i++) {
278 | if (finished()) return nullptr;
279 | job = try_steal(id);
280 | if (job) return job;
281 | }
282 | // If haven't found anything, take a breather.
283 | std::this_thread::sleep_for(std::chrono::nanoseconds(num_deques * 100));
284 | }
285 | }
286 |
287 | size_t hash(uint64_t x) {
288 | x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9ULL;
289 | x = (x ^ (x >> 27)) * 0x94d049bb133111ebULL;
290 | x = x ^ (x >> 31);
291 | return static_cast(x);
292 | }
293 | };
294 |
295 | template
296 | thread_local unsigned int scheduler::thread_id = 0;
297 |
298 | class fork_join_scheduler {
299 | using Job = WorkStealingJob;
300 |
301 | // Underlying scheduler object
302 | std::unique_ptr> sched;
303 |
304 | public:
305 | fork_join_scheduler() {}
306 |
307 | void start() {
308 | sched = std::make_unique