├── pybinding ├── pybind11 ├── CMakeLists.txt ├── hdbscanTest.py └── test.py ├── include ├── parlay ├── euclideanMst │ └── euclideanMst.h ├── delaunayTriangulation │ └── delaunay.h ├── spatialGraph │ └── spatialGraph.h ├── pargeo │ ├── algebra.h │ ├── edge.h │ ├── getTime.h │ ├── atomics.h │ ├── point.h │ ├── parseCommandLine.h │ ├── pointIO.h │ ├── bccp.h │ ├── speculativeFor.h │ ├── unionFind.h │ ├── parBuf.h │ ├── kdTreeRange.h │ ├── zorderSort.h │ ├── graphIO.h │ ├── kruskal.h │ ├── kdTreeKnn.h │ ├── wspd.h │ └── IO.h ├── dataset │ └── uniform.h └── closestPair │ └── divideConquer.h ├── src ├── delaunayTriangulation │ ├── incremental │ │ ├── CMakeLists.txt │ │ ├── neighbors.h │ │ ├── topology.h │ │ └── oct_tree.h │ ├── CMakeLists.txt │ └── README.md ├── euclideanMst │ ├── CMakeLists.txt │ ├── memoGfk │ │ ├── mark.h │ │ ├── emst.cpp │ │ └── wspdFilter.h │ └── wspd │ │ └── emst.cpp ├── CMakeLists.txt └── spatialGraph │ ├── CMakeLists.txt │ ├── knnGraph │ └── knnGraph.cpp │ ├── betaSkeleton │ └── betaSkeleton.cpp │ ├── spanner │ └── spanner.cpp │ ├── delaunayGraph │ └── delaunayGraph.cpp │ └── gabrielGraph │ └── gabrielGraph.cpp ├── .gitmodules ├── .gitignore ├── test ├── CMakeLists.txt.in ├── closestPair_test.cpp ├── dataset_test.cpp ├── kdTree_test.cpp ├── datasets │ ├── 3d_40.txt │ └── 2d_100.txt ├── CMakeLists.txt └── wspd_test.cpp ├── executable ├── delaunay2d.cpp ├── gabrielGraph.cpp ├── CMakeLists.txt ├── delaunayGraph.cpp ├── betaSkeleton.cpp ├── zorder.cpp ├── uniformData.cpp ├── knn.cpp ├── spanner.cpp ├── closestPair.cpp ├── knnGraph.cpp └── emst.cpp ├── LICENSE ├── benchmark ├── zorder_bench.cpp ├── wspd_bench.cpp ├── CMakeLists.txt ├── spatialGraph_bench.cpp └── spatialSearch_bench.cpp ├── CMakeLists.txt └── README.md /pybinding/pybind11: -------------------------------------------------------------------------------- 1 | ../external/pybind11 -------------------------------------------------------------------------------- /include/parlay: -------------------------------------------------------------------------------- 1 | ../external/parlaylib/include/parlay -------------------------------------------------------------------------------- /src/delaunayTriangulation/incremental/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(delaunay delaunayTime.cpp delaunay.cpp) 2 | -------------------------------------------------------------------------------- /src/euclideanMst/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(emstLib memoGfk/emst.cpp) 2 | 3 | target_link_libraries(emstLib pargeoLib) 4 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(delaunayTriangulation) 2 | add_subdirectory(spatialGraph) 3 | add_subdirectory(euclideanMst) 4 | -------------------------------------------------------------------------------- /src/delaunayTriangulation/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(delaunay2dLib incremental/delaunay.cpp) 2 | 3 | target_link_libraries(delaunay2dLib pargeoLib) 4 | -------------------------------------------------------------------------------- /src/delaunayTriangulation/README.md: -------------------------------------------------------------------------------- 1 | ### The credit of the delaunay triangulation code goes to the [Problem Based Benchmark Suite](https://github.com/cmuparlay/pbbsbench). 2 | -------------------------------------------------------------------------------- /pybinding/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | pybind11_add_module(pypargeo pypargeo.cpp) 2 | 3 | target_link_libraries(pypargeo PRIVATE 4 | pargeoLib 5 | spatialGraphLib 6 | delaunay2dLib 7 | emstLib) 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/parlaylib"] 2 | path = external/parlaylib 3 | url = https://github.com/cmuparlay/parlaylib.git 4 | [submodule "external/pybind11"] 5 | path = external/pybind11 6 | url = https://github.com/pybind/pybind11.git 7 | -------------------------------------------------------------------------------- /src/spatialGraph/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(spatialGraphLib 2 | delaunayGraph/delaunayGraph.cpp 3 | knnGraph/knnGraph.cpp 4 | gabrielGraph/gabrielGraph.cpp 5 | betaSkeleton/betaSkeleton.cpp 6 | spanner/spanner.cpp 7 | ) 8 | 9 | target_link_libraries(spatialGraphLib pargeoLib delaunay2dLib) 10 | -------------------------------------------------------------------------------- /include/euclideanMst/euclideanMst.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "parlay/sequence.h" 4 | #include "pargeo/edge.h" 5 | #include "pargeo/point.h" 6 | 7 | namespace pargeo { 8 | 9 | template 10 | parlay::sequence euclideanMst(parlay::sequence> &); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /include/delaunayTriangulation/delaunay.h: -------------------------------------------------------------------------------- 1 | // The inteface for delaunay triangulation 2 | #include "geometry.h" 3 | #include "parlay/primitives.h" 4 | 5 | namespace pbbsbench { 6 | 7 | using coord = double; 8 | using pointT = point2d; 9 | 10 | triangles delaunay(parlay::sequence& P); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Temporary files 35 | *.*~ 36 | 37 | # Build files 38 | build/ 39 | -------------------------------------------------------------------------------- /test/CMakeLists.txt.in: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7) 2 | 3 | project(googletest-download NONE) 4 | 5 | include(ExternalProject) 6 | ExternalProject_Add(googletest 7 | GIT_REPOSITORY https://github.com/google/googletest.git 8 | GIT_TAG master 9 | SOURCE_DIR "${CMAKE_BINARY_DIR}/googletest-src" 10 | BINARY_DIR "${CMAKE_BINARY_DIR}/googletest-build" 11 | CONFIGURE_COMMAND "" 12 | BUILD_COMMAND "" 13 | INSTALL_COMMAND "" 14 | TEST_COMMAND "" 15 | ) 16 | -------------------------------------------------------------------------------- /pybinding/hdbscanTest.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from pypargeo import loadPoints 3 | from pypargeo import HDBSCAN 4 | from scipy.cluster.hierarchy import dendrogram, linkage 5 | from matplotlib import pyplot as plt 6 | from sklearn.datasets import make_blobs 7 | 8 | points, y = make_blobs(n_samples=20, centers=3, n_features=2, random_state=0) 9 | 10 | dendro = HDBSCAN(points, 3) 11 | 12 | fig = plt.figure(figsize=(3,2.3)) 13 | 14 | dn = dendrogram(dendro, color_threshold=0, no_labels=True) 15 | 16 | plt.show() 17 | 18 | fig.savefig("hdbscan.pdf") 19 | -------------------------------------------------------------------------------- /include/spatialGraph/spatialGraph.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "parlay/sequence.h" 4 | #include "pargeo/edge.h" 5 | #include "pargeo/point.h" 6 | 7 | namespace pargeo { 8 | 9 | template 10 | parlay::sequence knnGraph(parlay::sequence> &S, size_t k); 11 | 12 | template 13 | parlay::sequence delaunayGraph(parlay::sequence> &S); 14 | 15 | template 16 | parlay::sequence gabrielGraph(parlay::sequence> &S); 17 | 18 | template 19 | parlay::sequence betaSkeleton(parlay::sequence> &S, double); 20 | 21 | template 22 | parlay::sequence spanner(parlay::sequence> &S, double); 23 | 24 | } 25 | -------------------------------------------------------------------------------- /executable/delaunay2d.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "pargeo/getTime.h" 4 | #include "pargeo/pointIO.h" 5 | #include "pargeo/parseCommandLine.h" 6 | #include "delaunayTriangulation/geometry.h" 7 | #include "delaunayTriangulation/delaunay.h" 8 | #include "parlay/primitives.h" 9 | 10 | using namespace std; 11 | using namespace pargeo; 12 | using namespace pargeo::pointIO; 13 | using namespace pbbsbench; 14 | 15 | int main(int argc, char* argv[]) { 16 | using pointT = pbbsbench::point2d; 17 | 18 | commandLine C(argc,argv,"./delaunay "); 19 | char* iFile = C.getArgument(0); 20 | 21 | parlay::sequence P = readPointsFromFile(iFile); 22 | triangles R; 23 | 24 | timer t; t.start(); 25 | R = delaunay(P); 26 | cout << "time = " << t.stop() << endl; 27 | } 28 | -------------------------------------------------------------------------------- /include/pargeo/algebra.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "point.h" 4 | 5 | namespace pargeo { 6 | // All inputs are row-major 7 | 8 | template 9 | inline typename pt::floatT determinant2by2(pt a, pt b) { 10 | return a[0]*b[1] - a[1]*b[0]; 11 | } 12 | 13 | template 14 | inline void inverse2by2(pt a, pt b) { 15 | typename pt::floatT c = 1/determinant2by2(a, b); 16 | typename pt::floatT m[4]; 17 | m[0] = b[1]*c; m[1] = -a[1]*c; 18 | m[2] = -b[0]*c; m[3] = a[0]*c; 19 | a[0] = m[0]; a[1] = m[1]; 20 | b[0] = m[2]; b[1] = m[3]; 21 | } 22 | 23 | template 24 | inline pt crossProduct3d(pt p, pt q) { 25 | pt r; 26 | r[0] = p[1]*q[2] - p[2]*q[1]; 27 | r[1] = p[2]*q[0] - p[0]*q[2]; 28 | r[2] = p[0]*q[1] - p[1]*q[0]; 29 | return r; 30 | } 31 | 32 | template 33 | inline typename pt::floatT determinant3by3(pt a, pt b, pt c) { 34 | return a[0] * (b[1]*c[2] - c[1]*b[2]) - 35 | a[1] * (b[0]*c[2] - b[2]*c[0]) + 36 | a[2] * (b[0]*c[1] - b[1]*c[0]); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Yiqiu Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /benchmark/zorder_bench.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "dataset/uniform.h" 4 | #include "pargeo/zorderSort.h" 5 | 6 | static parlay::sequence> data2d(size_t n) { 7 | auto P = pargeo::uniformInPolyPoints<2>(n, 1); 8 | return std::move(P); 9 | } 10 | 11 | static parlay::sequence> data3d(size_t n) { 12 | auto P = pargeo::uniformInPolyPoints<3>(n, 1); 13 | return std::move(P); 14 | } 15 | 16 | static void BM_zorder_2d(benchmark::State& state) { 17 | using namespace pargeo; 18 | auto points = data2d(state.range(0)); 19 | for (auto _ : state) { 20 | zorderSort2d(points); 21 | } 22 | } 23 | 24 | static void BM_zorder_3d(benchmark::State& state) { 25 | using namespace pargeo; 26 | auto points = data3d(state.range(0)); 27 | for (auto _ : state) { 28 | zorderSort3d(points); 29 | } 30 | } 31 | 32 | BENCHMARK(BM_zorder_2d) 33 | ->UseRealTime() 34 | ->Unit(benchmark::kMillisecond) 35 | ->Arg(100000)->Arg(1000000)->Arg(10000000); 36 | 37 | BENCHMARK(BM_zorder_3d) 38 | ->UseRealTime() 39 | ->Unit(benchmark::kMillisecond) 40 | ->Arg(100000)->Arg(1000000)->Arg(10000000); 41 | 42 | BENCHMARK_MAIN(); 43 | -------------------------------------------------------------------------------- /executable/gabrielGraph.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "parlay/parallel.h" 4 | #include "pargeo/point.h" 5 | #include "pargeo/getTime.h" 6 | #include "pargeo/pointIO.h" 7 | #include "pargeo/graphIO.h" 8 | #include "pargeo/parseCommandLine.h" 9 | #include "spatialGraph/spatialGraph.h" 10 | 11 | using namespace std; 12 | using namespace pargeo; 13 | using namespace pargeo::pointIO; 14 | using namespace pargeo::graphIO; 15 | 16 | template 17 | void timeGraph(parlay::sequence> &P, char const *outFile) { 18 | timer t; t.start(); 19 | auto I = gabrielGraph(P); 20 | cout << "time = " << t.stop() << endl; 21 | if (outFile != NULL) graphIO::writeEdgeSeqToFile(I, outFile); 22 | } 23 | 24 | int main(int argc, char* argv[]) { 25 | commandLine P(argc,argv,"[-o ] "); 26 | char* iFile = P.getArgument(0); 27 | char* oFile = P.getOptionValue("-o"); 28 | 29 | int dim = readHeader(iFile); 30 | 31 | if (dim == 2) { 32 | parlay::sequence> Points = readPointsFromFile>(iFile); 33 | timeGraph<2>(Points, oFile); 34 | } else { 35 | throw std::runtime_error("unsupported dimensionality"); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /executable/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(closestPair closestPair.cpp) 2 | target_link_libraries(closestPair pargeoLib) 3 | 4 | add_executable(delaunay2d delaunay2d.cpp) 5 | target_link_libraries(delaunay2d pargeoLib delaunay2dLib) 6 | 7 | add_executable(emst emst.cpp) 8 | target_link_libraries(emst pargeoLib emstLib) 9 | 10 | add_executable(betaSkeleton betaSkeleton.cpp) 11 | target_link_libraries(betaSkeleton pargeoLib delaunay2dLib 12 | spatialGraphLib) 13 | 14 | add_executable(delaunayGraph delaunayGraph.cpp) 15 | target_link_libraries(delaunayGraph pargeoLib delaunay2dLib 16 | spatialGraphLib) 17 | 18 | add_executable(gabrielGraph gabrielGraph.cpp) 19 | target_link_libraries(gabrielGraph pargeoLib delaunay2dLib 20 | spatialGraphLib) 21 | 22 | add_executable(knnGraph knnGraph.cpp) 23 | target_link_libraries(knnGraph pargeoLib spatialGraphLib) 24 | 25 | add_executable(spanner spanner.cpp) 26 | target_link_libraries(spanner pargeoLib spatialGraphLib) 27 | 28 | add_executable(knn knn.cpp) 29 | target_link_libraries(knn pargeoLib) 30 | 31 | add_executable(zorder zorder.cpp) 32 | target_link_libraries(zorder pargeoLib) 33 | 34 | add_executable(uniformData uniformData.cpp) 35 | target_link_libraries(uniformData pargeoLib) 36 | -------------------------------------------------------------------------------- /executable/delaunayGraph.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "parlay/parallel.h" 4 | #include "pargeo/point.h" 5 | #include "pargeo/getTime.h" 6 | #include "pargeo/pointIO.h" 7 | #include "pargeo/graphIO.h" 8 | #include "pargeo/parseCommandLine.h" 9 | #include "spatialGraph/spatialGraph.h" 10 | 11 | using namespace std; 12 | using namespace pargeo; 13 | using namespace pargeo::graphIO; 14 | using namespace pargeo::pointIO; 15 | 16 | template 17 | void timeGraph(parlay::sequence> &P, char const *outFile) { 18 | timer t; t.start(); 19 | 20 | auto I = delaunayGraph(P); 21 | cout << "time = " << t.get_next() << endl; 22 | 23 | if (outFile != NULL) graphIO::writeEdgeSeqToFile(I, outFile); 24 | 25 | t.stop(); 26 | } 27 | 28 | int main(int argc, char* argv[]) { 29 | commandLine P(argc,argv,"[-o ] "); 30 | char* iFile = P.getArgument(0); 31 | char* oFile = P.getOptionValue("-o"); 32 | 33 | int dim = readHeader(iFile); 34 | 35 | if (dim == 2) { 36 | parlay::sequence> Points = readPointsFromFile>(iFile); 37 | timeGraph<2>(Points, oFile); 38 | } else { 39 | throw std::runtime_error("only supports 2d data set"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /executable/betaSkeleton.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "parlay/parallel.h" 4 | #include "pargeo/point.h" 5 | #include "pargeo/getTime.h" 6 | #include "pargeo/pointIO.h" 7 | #include "pargeo/graphIO.h" 8 | #include "pargeo/parseCommandLine.h" 9 | #include "spatialGraph/spatialGraph.h" 10 | 11 | using namespace std; 12 | using namespace pargeo; 13 | using namespace pargeo::pointIO; 14 | using namespace pargeo::graphIO; 15 | 16 | template 17 | void timeGraph(parlay::sequence> &P, double b, char const *outFile) { 18 | timer t; t.start(); 19 | auto I = pargeo::betaSkeleton(P, b); 20 | cout << "time = " << t.stop() << endl; 21 | if (outFile != NULL) graphIO::writeEdgeSeqToFile(I, outFile); 22 | } 23 | 24 | int main(int argc, char* argv[]) { 25 | commandLine P(argc,argv,"[-b ] [-o ] "); 26 | char* iFile = P.getArgument(0); 27 | double b = P.getOptionDoubleValue("-b",1.0); 28 | char* oFile = P.getOptionValue("-o"); 29 | 30 | int dim = readHeader(iFile); 31 | 32 | if (dim == 2) { 33 | parlay::sequence> Points = readPointsFromFile>(iFile); 34 | timeGraph<2>(Points, b, oFile); 35 | } else { 36 | throw std::runtime_error("unsupported dimensionality"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/euclideanMst/memoGfk/mark.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "parlay/parallel.h" 4 | 5 | namespace pargeo { 6 | namespace emstInternal { 7 | 8 | template 9 | void markAll(nodeT *nd, size_t id){ 10 | if(!nd->isLeaf() && nd->getId() != id){ 11 | if (nd->size() > 2000) { 12 | parlay::par_do([&](){markAll(nd->L(), id);}, 13 | [&](){markAll(nd->R(), id);}); 14 | } else { 15 | markAll(nd->L(), id); 16 | markAll(nd->R(), id);} 17 | } 18 | nd->setId(id); 19 | } 20 | 21 | template 22 | void mark(nodeT *nd, UF *uf, pointT* s){ 23 | 24 | if(nd->hasId()) { 25 | markAll(nd, uf->find(nd->getItem(0)-s)); 26 | return; 27 | } 28 | 29 | nd->setId(uf->find(nd->getItem(0)-s)); 30 | 31 | if(nd->isLeaf()){ 32 | for (size_t i=1; i < nd->size(); ++i) { 33 | if (nd->getId() != uf->find(nd->getItem(i)-s)) { 34 | nd->resetId(); 35 | return;} 36 | } 37 | } else { 38 | if (nd->size() > 2000) { 39 | parlay::par_do([&](){mark(nd->L(), uf, s);}, 40 | [&](){mark(nd->R(), uf, s);}); 41 | } else { 42 | mark(nd->L(), uf, s); 43 | mark(nd->R(), uf, s); 44 | } 45 | if (nd->getId() != nd->L()->getId()) { 46 | nd->resetId(); 47 | return; 48 | } 49 | if (nd->getId() != nd->R()->getId()) { 50 | nd->resetId(); 51 | return; 52 | } 53 | } 54 | } 55 | 56 | } // End namespace emstInternal 57 | } // End namespace pargeo 58 | -------------------------------------------------------------------------------- /benchmark/wspd_bench.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "dataset/uniform.h" 4 | #include "pargeo/wspd.h" 5 | 6 | static parlay::sequence> data2d(size_t n) { 7 | auto P = pargeo::uniformInPolyPoints<2>(n, 1); 8 | return std::move(P); 9 | } 10 | 11 | static parlay::sequence> data3d(size_t n) { 12 | auto P = pargeo::uniformInPolyPoints<3>(n, 1); 13 | return std::move(P); 14 | } 15 | 16 | static void BM_wspd_2d_includeTree(benchmark::State& state) { 17 | using namespace pargeo; 18 | auto points = data2d(state.range(0)); 19 | for (auto _ : state) { 20 | kdNode<2, point<2>>* tree = 21 | buildKdt<2, point<2>>(points, true, true); 22 | auto pairs = wspdParallel(tree, state.range(1)); 23 | } 24 | } 25 | 26 | static void BM_wspd_3d_includeTree(benchmark::State& state) { 27 | using namespace pargeo; 28 | auto points = data3d(state.range(0)); 29 | for (auto _ : state) { 30 | kdNode<3, point<3>>* tree = 31 | buildKdt<3, point<3>>(points, true, true); 32 | auto pairs = wspdParallel(tree, state.range(1)); 33 | } 34 | } 35 | 36 | BENCHMARK(BM_wspd_2d_includeTree) 37 | ->UseRealTime() 38 | ->Unit(benchmark::kMillisecond) 39 | ->Args({100000, 2})->Args({100000, 3})->Args({100000, 5}) 40 | ->Args({1000000, 2})->Args({1000000, 3})->Args({1000000, 5}); 41 | 42 | BENCHMARK(BM_wspd_3d_includeTree) 43 | ->UseRealTime() 44 | ->Unit(benchmark::kMillisecond) 45 | ->Args({100000, 2})->Args({100000, 3})->Args({100000, 5}) 46 | ->Args({1000000, 2})->Args({1000000, 3})->Args({1000000, 5}); 47 | 48 | BENCHMARK_MAIN(); 49 | -------------------------------------------------------------------------------- /test/closestPair_test.cpp: -------------------------------------------------------------------------------- 1 | #include "pargeo/pointIO.h" 2 | #include "closestPair/divideConquer.h" 3 | #include "gtest/gtest.h" 4 | 5 | TEST(closestPair_divideConquer, test2d) { 6 | using namespace pargeo; 7 | using namespace pargeo::pointIO; 8 | 9 | auto filePath = "datasets/2d_100.txt"; 10 | int dim = readHeader(filePath); 11 | parlay::sequence> P = 12 | readPointsFromFile>(filePath); 13 | 14 | auto cp1 = closestPairDC<2>(P); 15 | auto cp2 = bruteForceParallel<2>(make_slice(P)); 16 | if (cp1.u != cp2.u) swap(cp1.u, cp1.v); 17 | EXPECT_EQ(cp1.dist, cp2.dist); 18 | EXPECT_EQ(cp1.u[0], cp2.u[0]); 19 | EXPECT_EQ(cp1.u[1], cp2.u[1]); 20 | EXPECT_EQ(cp1.v[0], cp2.v[0]); 21 | EXPECT_EQ(cp1.v[1], cp2.v[1]); 22 | } 23 | 24 | TEST(closestPair_divideConquer, test3d) { 25 | using namespace pargeo; 26 | using namespace pargeo::pointIO; 27 | 28 | auto filePath = "datasets/3d_40.txt"; 29 | int dim = readHeader(filePath); 30 | parlay::sequence> P = 31 | readPointsFromFile>(filePath); 32 | 33 | auto cp1 = closestPairDC<3>(P); 34 | auto cp2 = bruteForceParallel<3>(make_slice(P)); 35 | if (cp1.u != cp2.u) swap(cp1.u, cp1.v); 36 | EXPECT_EQ(cp1.dist, cp2.dist); 37 | EXPECT_EQ(cp1.u[0], cp2.u[0]); 38 | EXPECT_EQ(cp1.u[1], cp2.u[1]); 39 | EXPECT_EQ(cp1.u[2], cp2.u[2]); 40 | EXPECT_EQ(cp1.v[0], cp2.v[0]); 41 | EXPECT_EQ(cp1.v[1], cp2.v[1]); 42 | EXPECT_EQ(cp1.v[2], cp2.v[2]); 43 | } 44 | 45 | int main(int argc, char **argv) { 46 | ::testing::InitGoogleTest(&argc, argv); 47 | return RUN_ALL_TESTS(); 48 | } 49 | -------------------------------------------------------------------------------- /benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(pargeo_benchmark) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(CMAKE_CXX_STANDARD_REQUIRED True) 7 | 8 | # Benchmark should not run its own unit tests 9 | set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE) 10 | set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE BOOL "" FORCE) 11 | 12 | # Download Benchmark library 13 | include(FetchContent) 14 | FetchContent_Declare(benchmark 15 | GIT_REPOSITORY https://github.com/google/benchmark 16 | GIT_TAG master 17 | ) 18 | FetchContent_GetProperties(benchmark) 19 | if(NOT benchmark_POPULATED) 20 | message(STATUS "benchmarks: Configuring Google Benchmark") 21 | FetchContent_Populate(benchmark) 22 | set(CMAKE_SUPPRESS_DEVELOPER_WARNINGS 1 CACHE BOOL "") 23 | add_subdirectory(${benchmark_SOURCE_DIR} 24 | ${benchmark_BINARY_DIR} 25 | EXCLUDE_FROM_ALL) 26 | endif() 27 | 28 | # Include benchmark targets 29 | message(STATUS "benchmarks: Enabled") 30 | 31 | add_executable(spatialGraph_bench spatialGraph_bench.cpp) 32 | target_link_libraries(spatialGraph_bench PRIVATE 33 | pargeoLib 34 | spatialGraphLib 35 | emstLib 36 | benchmark_main) 37 | 38 | add_executable(wspd_bench wspd_bench.cpp) 39 | target_link_libraries(wspd_bench PRIVATE 40 | pargeoLib 41 | benchmark_main) 42 | 43 | add_executable(zorder_bench zorder_bench.cpp) 44 | target_link_libraries(zorder_bench PRIVATE 45 | pargeoLib 46 | benchmark_main) 47 | 48 | add_executable(spatialSearch_bench spatialSearch_bench.cpp) 49 | target_link_libraries(spatialSearch_bench PRIVATE 50 | pargeoLib 51 | benchmark_main) 52 | -------------------------------------------------------------------------------- /executable/zorder.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "parlay/parallel.h" 4 | #include "pargeo/point.h" 5 | #include "pargeo/getTime.h" 6 | #include "pargeo/pointIO.h" 7 | #include "pargeo/zorderSort.h" 8 | #include "pargeo/parseCommandLine.h" 9 | 10 | using namespace std; 11 | using namespace pargeo; 12 | using namespace pargeo::pointIO; 13 | 14 | void timeSort2d(parlay::sequence> &P, char const *outFile) { 15 | timer t; t.start(); 16 | auto sorted = pargeo::zorderSort2d>(P); 17 | cout << "time = " << t.stop() << endl; 18 | if (outFile != NULL) pargeo::pointIO::writePointsToFile(sorted, outFile); 19 | } 20 | 21 | void timeSort3d(parlay::sequence> &P, char const *outFile) { 22 | timer t; t.start(); 23 | auto sorted = pargeo::zorderSort3d>(P); 24 | cout << "time = " << t.stop() << endl; 25 | if (outFile != NULL) pargeo::pointIO::writePointsToFile(sorted, outFile); 26 | } 27 | 28 | int main(int argc, char* argv[]) { 29 | commandLine P(argc,argv,"[-o ] "); 30 | char* iFile = P.getArgument(0); 31 | char* oFile = P.getOptionValue("-o"); 32 | 33 | int dim = readHeader(iFile); 34 | 35 | if (dim == 2) { 36 | parlay::sequence> Points = readPointsFromFile>(iFile); 37 | timeSort2d(Points, oFile); 38 | } else if (dim == 3) { 39 | parlay::sequence> Points = readPointsFromFile>(iFile); 40 | timeSort3d(Points, oFile); 41 | } else { 42 | cout << "dim = " << dim << endl; 43 | throw std::runtime_error("dimension not supported"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /include/pargeo/edge.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace pargeo { 4 | 5 | template 6 | class _edge { 7 | public: 8 | static constexpr bool directed = _directed; 9 | idxT u,v; 10 | 11 | _edge(idxT _u, idxT _v): u(_u), v(_v) { 12 | if (!directed && u > v) std::swap(u,v); 13 | } 14 | 15 | _edge(): u(-1), v(-1) { } 16 | 17 | bool isEmpty() { return u == -1; } 18 | 19 | bool operator==(_edge e2) { 20 | return e2.u == u && e2.v == v; 21 | } 22 | 23 | bool operator!=(_edge e2) { 24 | return e2.u != u || e2.v != v; 25 | } 26 | }; 27 | 28 | using edge = pargeo::_edge; 29 | using dirEdge = pargeo::_edge; 30 | 31 | template 32 | class _weightedEdge { 33 | public: 34 | static constexpr bool directed = _directed; 35 | idxT u,v; 36 | weightT weight; 37 | 38 | _weightedEdge(idxT _u, idxT _v): u(_u), v(_v), weight(-1) { 39 | if (!directed && u > v) std::swap(u,v); 40 | } 41 | 42 | _weightedEdge(idxT _u, idxT _v, weightT _w): u(_u), v(_v), weight(_w) { 43 | if (!directed && u > v) std::swap(u,v); 44 | } 45 | 46 | _weightedEdge(): u(-1), v(-1), weight(-1) { } 47 | 48 | bool isEmpty() { return u == -1; } 49 | 50 | bool operator==(_weightedEdge e2) { 51 | return e2.u == u && e2.v == v; 52 | } 53 | 54 | bool operator!=(_weightedEdge e2) { 55 | return e2.u != u || e2.v != v; 56 | } 57 | }; 58 | 59 | using wghEdge = pargeo::_weightedEdge; 60 | using wghDirEdge = pargeo::_weightedEdge; 61 | 62 | } // END namespace pargeo 63 | -------------------------------------------------------------------------------- /src/spatialGraph/knnGraph/knnGraph.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "parlay/parallel.h" 3 | #include "parlay/primitives.h" 4 | #include "pargeo/point.h" 5 | #include "pargeo/kdTreeKnn.h" 6 | #include "spatialGraph/spatialGraph.h" 7 | 8 | #define SILENT 9 | 10 | using namespace std; 11 | 12 | template 13 | parlay::sequence pargeo::knnGraph(parlay::sequence> &P, size_t k) { 14 | using namespace parlay; 15 | 16 | if (k < 1 || k > P.size()-1) 17 | throw std::runtime_error("k must range from 1 to n-1"); 18 | 19 | // Call knn 20 | parlay::sequence nnIdx = kdTreeKnn>(P, k+1); 21 | 22 | // Convert to edge list 23 | auto edges = parlay::sequence(k * P.size()); 24 | parallel_for(0, P.size(), [&](size_t i) { 25 | size_t jj = 0; 26 | for(size_t j=0; j pargeo::knnGraph<2>(parlay::sequence> &, size_t); 38 | template parlay::sequence pargeo::knnGraph<3>(parlay::sequence> &, size_t); 39 | template parlay::sequence pargeo::knnGraph<4>(parlay::sequence> &, size_t); 40 | template parlay::sequence pargeo::knnGraph<5>(parlay::sequence> &, size_t); 41 | template parlay::sequence pargeo::knnGraph<6>(parlay::sequence> &, size_t); 42 | template parlay::sequence pargeo::knnGraph<7>(parlay::sequence> &, size_t); 43 | -------------------------------------------------------------------------------- /executable/uniformData.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pargeo/point.h" 3 | #include "pargeo/pointIO.h" 4 | #include "pargeo/parseCommandLine.h" 5 | 6 | #include "dataset/uniform.h" 7 | 8 | template 9 | void uniformGenerator(size_t n, size_t shape, double thickness, char* fName) { 10 | if (thickness < 0) { 11 | auto P = pargeo::uniformInPolyPoints>(n, shape, sqrt(double(n))); 12 | pargeo::pointIO::writePointsToFile(P, fName); 13 | } else { 14 | auto P = pargeo::uniformOnPolyPoints>(n, shape, thickness, double(n)); 15 | pargeo::pointIO::writePointsToFile(P, fName); 16 | } 17 | } 18 | 19 | int main(int argc, char* argv[]) { 20 | 21 | std::string text = "[-s] [-c] [-t {double}] [-d {2--9}] n "; 22 | text += "\n polygon type: -s sphere -c cube"; 23 | text += "\n generate point on surface: -t thickness"; 24 | text += "\n o.w. default to generate in polygon"; 25 | pargeo::commandLine P(argc, argv, text); 26 | 27 | std::pair in = P.sizeAndFileName(); 28 | size_t n = in.first; 29 | char* fName = in.second; 30 | 31 | int dim = P.getOptionIntValue("-d", 2); 32 | bool sphere = P.getOption("-s"); 33 | bool cube = P.getOption("-c"); 34 | double thickness = P.getOptionDoubleValue("-t", -1); 35 | 36 | size_t shape = sphere ? 0 : 1; 37 | 38 | if (dim == 2) uniformGenerator<2>(n, shape, thickness, fName); 39 | else if (dim == 3) uniformGenerator<3>(n, shape, thickness, fName); 40 | else if (dim == 4) uniformGenerator<4>(n, shape, thickness, fName); 41 | else if (dim == 5) uniformGenerator<5>(n, shape, thickness, fName); 42 | else if (dim == 6) uniformGenerator<6>(n, shape, thickness, fName); 43 | else if (dim == 7) uniformGenerator<7>(n, shape, thickness, fName); 44 | else if (dim == 8) uniformGenerator<8>(n, shape, thickness, fName); 45 | else if (dim == 9) uniformGenerator<9>(n, shape, thickness, fName); 46 | else throw std::runtime_error("dimensionality not yet supported"); 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /executable/knn.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "parlay/parallel.h" 4 | #include "pargeo/point.h" 5 | #include "pargeo/kdTreeKnn.h" 6 | #include "pargeo/getTime.h" 7 | #include "pargeo/pointIO.h" 8 | #include "pargeo/parseCommandLine.h" 9 | 10 | using namespace std; 11 | using namespace pargeo; 12 | using namespace pargeo::pointIO; 13 | using namespace pargeo::IO; 14 | 15 | template 16 | void timeKnn(parlay::sequence> &P, size_t k, char const *outFile) { 17 | timer t; t.start(); 18 | parlay::sequence I; 19 | I = kdTreeKnn>(P, k+1); 20 | cout << "time = " << t.stop() << endl; 21 | if (outFile != NULL) writeIntSeqToFile(I, outFile); 22 | } 23 | 24 | int main(int argc, char* argv[]) { 25 | commandLine P(argc,argv,"[-k ] [-o ] "); 26 | char* iFile = P.getArgument(0); 27 | size_t k = P.getOptionIntValue("-k",1); 28 | char* oFile = P.getOptionValue("-o"); 29 | 30 | int dim = readHeader(iFile); 31 | 32 | if (dim == 2) { 33 | parlay::sequence> Points = readPointsFromFile>(iFile); 34 | timeKnn<2>(Points, k, oFile); 35 | } else if (dim == 3) { 36 | parlay::sequence> Points = readPointsFromFile>(iFile); 37 | timeKnn<3>(Points, k, oFile); 38 | } else if (dim == 4) { 39 | parlay::sequence> Points = readPointsFromFile>(iFile); 40 | timeKnn<4>(Points, k, oFile); 41 | } else if (dim == 5) { 42 | parlay::sequence> Points = readPointsFromFile>(iFile); 43 | timeKnn<5>(Points, k, oFile); 44 | } else if (dim == 6) { 45 | parlay::sequence> Points = readPointsFromFile>(iFile); 46 | timeKnn<6>(Points, k, oFile); 47 | } else if (dim == 7) { 48 | parlay::sequence> Points = readPointsFromFile>(iFile); 49 | timeKnn<7>(Points, k, oFile); 50 | } else { 51 | cout << "dim = " << dim << endl; 52 | throw std::runtime_error("dimension not supported"); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(pargeo) 4 | 5 | set(CMAKE_CXX_STANDARD 17) 6 | set(CMAKE_CXX_STANDARD_REQUIRED True) 7 | 8 | option(USE_PARALLEL "Parallel scheduler" ON) 9 | option(USE_WARNINGS "Warning flags" OFF) 10 | option(USE_PYBIND "Pybinding" OFF) 11 | option(USE_BENCHMARK "Benchmark" OFF) 12 | option(USE_TEST "Test" OFF) 13 | 14 | if (USE_WARNINGS) 15 | set(EXTRA_FLAGS "-Wall -Wextra") 16 | endif() 17 | 18 | if(USE_PARALLEL) 19 | set(CMAKE_CXX_FLAGS "-DHOMEGROWN -pthread -mcx16 -O3 -DNDEBUG -fPIC ${EXTRA_FLAGS}") 20 | else() 21 | set(CMAKE_CXX_FLAGS "-DPARLAY_SEQUENTIAL -mcx16 -O3 -DNDEBUG -fPIC ${EXTRA_FLAGS}") 22 | endif() 23 | 24 | message(STATUS "--------------- General configuration -------------") 25 | message(STATUS "CMake Generator: ${CMAKE_GENERATOR}") 26 | message(STATUS "Compiler: ${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}") 27 | message(STATUS "Build type: ${CMAKE_BUILD_TYPE}") 28 | message(STATUS "CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}") 29 | message(STATUS "CMAKE_CXX_FLAGS_DEBUG: ${CMAKE_CXX_FLAGS_DEBUG}") 30 | message(STATUS "CMAKE_CXX_FLAGS_RELEASE: ${CMAKE_CXX_FLAGS_RELEASE}") 31 | message(STATUS "CMAKE_CXX_FLAGS_RELWITHDEBINFO: ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") 32 | message(STATUS "CMAKE_EXE_LINKER_FLAGS ${CMAKE_CXX_LINKER_FLAGS}") 33 | message(STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}" ) 34 | 35 | # ------------------------------------------------- 36 | # Add sub directories 37 | 38 | add_subdirectory(src) 39 | 40 | add_subdirectory(executable) 41 | 42 | if (USE_PYBIND) 43 | add_subdirectory(external/pybind11) 44 | add_subdirectory(pybinding) 45 | endif() 46 | 47 | if (USE_BENCHMARK) 48 | add_subdirectory(benchmark) 49 | endif() 50 | 51 | if (USE_TEST) 52 | enable_testing() 53 | add_subdirectory(test) 54 | endif() 55 | 56 | # ------------------------------------------------- 57 | # Expose pargeoLib 58 | 59 | project(pargeoLib) 60 | 61 | add_library(pargeoLib INTERFACE) 62 | 63 | target_include_directories(pargeoLib INTERFACE include) 64 | -------------------------------------------------------------------------------- /include/pargeo/getTime.h: -------------------------------------------------------------------------------- 1 | // This file is part of pbbsbench 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace pargeo { 12 | 13 | struct timer { 14 | double total_time; 15 | double last_time; 16 | bool on; 17 | std::string name; 18 | struct timezone tzp; 19 | 20 | timer(std::string name = "PBBS time", bool _start = true) 21 | : total_time(0.0), on(false), name(name), tzp({0,0}) { 22 | if (_start) start(); 23 | } 24 | 25 | double get_time() { 26 | timeval now; 27 | gettimeofday(&now, &tzp); 28 | return ((double) now.tv_sec) + ((double) now.tv_usec)/1000000.; 29 | } 30 | 31 | void start () { 32 | on = 1; 33 | last_time = get_time(); 34 | } 35 | 36 | double stop () { 37 | on = 0; 38 | double d = (get_time()-last_time); 39 | total_time += d; 40 | return d; 41 | } 42 | 43 | void reset() { 44 | total_time=0.0; 45 | on=0; 46 | } 47 | 48 | double get_total() { 49 | if (on) return total_time + get_time() - last_time; 50 | else return total_time; 51 | } 52 | 53 | double get_next() { 54 | if (!on) return 0.0; 55 | double t = get_time(); 56 | double td = t - last_time; 57 | total_time += td; 58 | last_time = t; 59 | return td; 60 | } 61 | 62 | void report(double time, std::string str) { 63 | std::ios::fmtflags cout_settings = std::cout.flags(); 64 | std::cout.precision(4); 65 | std::cout << std::fixed; 66 | std::cout << name << ": "; 67 | if (str.length() > 0) 68 | std::cout << str << ": "; 69 | std::cout << time << std::endl; 70 | std::cout.flags(cout_settings); 71 | } 72 | 73 | void total() { 74 | report(get_total(),"total"); 75 | total_time = 0.0; 76 | } 77 | 78 | void reportTotal(std::string str) { 79 | report(get_total(), str); 80 | } 81 | 82 | void next(std::string str) { 83 | if (on) report(get_next(), str); 84 | } 85 | }; 86 | 87 | static timer _tm; 88 | #define startTime() _tm.start(); 89 | #define nextTime(_string) _tm.next(_string); 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/spatialGraph/betaSkeleton/betaSkeleton.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "parlay/parallel.h" 4 | #include "parlay/primitives.h" 5 | #include "pargeo/point.h" 6 | #include "pargeo/getTime.h" 7 | #include "spatialGraph/spatialGraph.h" 8 | #include "kdt.h" 9 | 10 | #define SILENT 11 | 12 | template 13 | parlay::sequence pargeo::betaSkeleton(parlay::sequence> &P, double beta) { 14 | using namespace parlay; 15 | using namespace skeletonKdt; 16 | using namespace std; 17 | using pt = pargeo::point; 18 | 19 | if (dim != 2) 20 | throw std::runtime_error("Error, beta skeleton only supports 2 dimensional inputs."); 21 | 22 | if (beta < 0) 23 | throw std::runtime_error("Error, beta skeleton only takes beta >= 0."); 24 | 25 | #ifndef SILENT 26 | cout << "beta = " << beta << endl; 27 | timer t; t.start(); 28 | #endif 29 | sequence dedges = pargeo::delaunayGraph(P); 30 | #ifndef SILENT 31 | cout << "#delaunay-edges = " << dedges.size() << endl; 32 | cout << "delaunay-time = " << t.get_next() << endl; 33 | #endif 34 | // Take a subset of qualifying edges 35 | 36 | kdNode* tree = buildKdt(P, true); 37 | #ifndef SILENT 38 | cout << "build-tree-time = " << t.get_next() << endl; 39 | #endif 40 | auto skeleton = parlay::filter(dedges, 41 | [&](edge e) { 42 | if (beta < 1) { 43 | typename pt::floatT r = P[e.u].dist(P[e.v]) / (2*beta); 44 | return !tree->nonEmptyLune(P[e.u], r, P[e.v], r, &P[e.u], &P[e.v]); 45 | } else { 46 | typename pt::floatT r = beta * P[e.u].dist(P[e.v]) / 2; 47 | pt c1 = P[e.u]*(1-beta/2) + P[e.v]*(beta/2); 48 | pt c2 = P[e.u]*(beta/2) + P[e.v]*(1-beta/2); 49 | return !tree->nonEmptyLune(c1, r, c2, r, &P[e.u], &P[e.v]); 50 | } 51 | }); 52 | 53 | #ifndef SILENT 54 | cout << "edge-pruning-time = " << t.stop() << endl; 55 | cout << "#edges = " << skeleton.size() << endl; 56 | #endif 57 | return skeleton; 58 | } 59 | 60 | template parlay::sequence pargeo::betaSkeleton<2>(parlay::sequence> &, double); 61 | -------------------------------------------------------------------------------- /test/dataset_test.cpp: -------------------------------------------------------------------------------- 1 | #include "parlay/sequence.h" 2 | #include "pargeo/pointIO.h" 3 | #include "dataset/uniform.h" 4 | #include "gtest/gtest.h" 5 | 6 | template 7 | void unique(Seq& P) { 8 | for (size_t i = 0; i < 100; ++ i) { 9 | for (size_t j = i + 1; j < 100; ++ j) { 10 | EXPECT_TRUE(!(P[i] == P[j])); 11 | } 12 | } 13 | } 14 | 15 | TEST(dataset, onSphere0) { 16 | 17 | parlay::sequence> P = 18 | pargeo::uniformOnPolyPoints<3, pargeo::point<3>>(100, 0, 0); 19 | 20 | pargeo::point<3> zero; 21 | zero[0] = 0; zero[1] = 0; zero[2] = 0; 22 | 23 | for (auto p: P) EXPECT_DOUBLE_EQ(1, zero.dist(p)); 24 | 25 | unique(P); 26 | } 27 | 28 | TEST(dataset, onSphereFloat0) { 29 | 30 | parlay::sequence> P = 31 | pargeo::uniformOnPolyPoints<3, pargeo::fpoint<3>>(100, 0, 0); 32 | 33 | pargeo::fpoint<3> zero; 34 | zero[0] = 0; zero[1] = 0; zero[2] = 0; 35 | 36 | for (auto p: P) EXPECT_FLOAT_EQ(1, zero.dist(p)); 37 | 38 | unique(P); 39 | } 40 | 41 | TEST(dataset, onSphere1) { 42 | 43 | parlay::sequence> P = 44 | pargeo::uniformOnPolyPoints<3, pargeo::point<3>>(100, 0, 0.1); 45 | 46 | pargeo::point<3> zero; 47 | zero[0] = 0; zero[1] = 0; zero[2] = 0; 48 | 49 | for (auto p: P) EXPECT_TRUE(zero.dist(p) <= 1.1 && zero.dist(p) >= 0.9); 50 | 51 | unique(P); 52 | } 53 | 54 | TEST(dataset, inSphere0) { 55 | 56 | parlay::sequence> P = 57 | pargeo::uniformInPolyPoints<3, pargeo::point<3>>(100, 0); 58 | 59 | pargeo::point<3> zero; 60 | zero[0] = 0; zero[1] = 0; zero[2] = 0; 61 | 62 | for (auto p: P) EXPECT_TRUE(zero.dist(p) <= 1.0); 63 | 64 | unique(P); 65 | } 66 | 67 | TEST(dataset, inCube0) { 68 | 69 | parlay::sequence> P = 70 | pargeo::uniformInPolyPoints<3, pargeo::point<3>>(100, 1); 71 | 72 | for (auto p: P) { 73 | EXPECT_TRUE(abs(p[0]) <= 1); 74 | EXPECT_TRUE(abs(p[1]) <= 1); 75 | EXPECT_TRUE(abs(p[2]) <= 1); 76 | } 77 | 78 | unique(P); 79 | } 80 | 81 | int main(int argc, char **argv) { 82 | ::testing::InitGoogleTest(&argc, argv); 83 | return RUN_ALL_TESTS(); 84 | } 85 | -------------------------------------------------------------------------------- /benchmark/spatialGraph_bench.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "dataset/uniform.h" 4 | #include "spatialGraph/spatialGraph.h" 5 | #include "euclideanMst/euclideanMst.h" 6 | 7 | static parlay::sequence> P; 8 | 9 | void data() { 10 | if (P.size() == 0) 11 | P = pargeo::uniformInPolyPoints<2>(1000000, 1); 12 | } 13 | 14 | static void BM_knnGraph(benchmark::State& state) { 15 | data(); 16 | for (auto _ : state) 17 | parlay::sequence E = pargeo::knnGraph(P, state.range(0)); 18 | } 19 | 20 | static void BM_delaunayGraph(benchmark::State& state) { 21 | data(); 22 | for (auto _ : state) 23 | parlay::sequence E = pargeo::delaunayGraph(P); 24 | } 25 | 26 | static void BM_gabrielGraph(benchmark::State& state) { 27 | data(); 28 | for (auto _ : state) 29 | parlay::sequence E = pargeo::gabrielGraph(P); 30 | } 31 | 32 | static void BM_betaSkeleton(benchmark::State& state) { 33 | data(); 34 | for (auto _ : state) 35 | parlay::sequence E = pargeo::betaSkeleton(P, state.range(0)); 36 | } 37 | 38 | static void BM_spanner(benchmark::State& state) { 39 | data(); 40 | for (auto _ : state) 41 | parlay::sequence E = pargeo::spanner(P, state.range(0)); 42 | } 43 | 44 | static void BM_emst(benchmark::State& state) { 45 | data(); 46 | for (auto _ : state) 47 | parlay::sequence E = pargeo::euclideanMst(P); 48 | } 49 | 50 | BENCHMARK(BM_knnGraph) 51 | ->UseRealTime() 52 | ->Unit(benchmark::kMillisecond) 53 | ->Arg(1)->Arg(3)->Arg(10); 54 | 55 | BENCHMARK(BM_delaunayGraph) 56 | ->UseRealTime() 57 | ->Unit(benchmark::kMillisecond); 58 | 59 | BENCHMARK(BM_gabrielGraph) 60 | ->UseRealTime() 61 | ->Unit(benchmark::kMillisecond); 62 | 63 | BENCHMARK(BM_betaSkeleton) 64 | ->UseRealTime() 65 | ->Unit(benchmark::kMillisecond) 66 | ->Arg(1)->Arg(3)->Arg(10); 67 | 68 | BENCHMARK(BM_spanner) 69 | ->UseRealTime() 70 | ->Unit(benchmark::kMillisecond) 71 | ->Arg(10)->Arg(20)->Arg(30); 72 | 73 | BENCHMARK(BM_emst) 74 | ->UseRealTime() 75 | ->Unit(benchmark::kMillisecond) 76 | ->Arg(10)->Arg(20)->Arg(30); 77 | 78 | BENCHMARK_MAIN(); 79 | -------------------------------------------------------------------------------- /executable/spanner.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "parlay/parallel.h" 4 | #include "pargeo/point.h" 5 | #include "pargeo/getTime.h" 6 | #include "pargeo/pointIO.h" 7 | #include "pargeo/graphIO.h" 8 | #include "pargeo/parseCommandLine.h" 9 | #include "spatialGraph/spatialGraph.h" 10 | 11 | using namespace std; 12 | using namespace pargeo; 13 | using namespace pargeo::pointIO; 14 | using namespace pargeo::graphIO; 15 | 16 | template 17 | void timeGraph(parlay::sequence> &P, double k, char const *outFile) { 18 | timer t; t.start(); 19 | auto I = spanner(P, k); 20 | cout << "time = " << t.stop() << endl; 21 | if (outFile != NULL) graphIO::writeEdgeSeqToFile(I, outFile); 22 | } 23 | 24 | int main(int argc, char* argv[]) { 25 | commandLine P(argc,argv,"[-t ] [-o ] "); 26 | char* iFile = P.getArgument(0); 27 | double k = P.getOptionDoubleValue("-t",10); // spanner relaxation constant 28 | char* oFile = P.getOptionValue("-o"); 29 | 30 | int dim = readHeader(iFile); 31 | 32 | if (k <= 1) throw std::runtime_error("Bad relaxation constant t"); 33 | 34 | if (dim == 2) { 35 | parlay::sequence> Points = readPointsFromFile>(iFile); 36 | timeGraph<2>(Points, k, oFile); 37 | } else if (dim == 3) { 38 | parlay::sequence> Points = readPointsFromFile>(iFile); 39 | timeGraph<3>(Points, k, oFile); 40 | } else if (dim == 4) { 41 | parlay::sequence> Points = readPointsFromFile>(iFile); 42 | timeGraph<4>(Points, k, oFile); 43 | } else if (dim == 5) { 44 | parlay::sequence> Points = readPointsFromFile>(iFile); 45 | timeGraph<5>(Points, k, oFile); 46 | } else if (dim == 6) { 47 | parlay::sequence> Points = readPointsFromFile>(iFile); 48 | timeGraph<6>(Points, k, oFile); 49 | } else if (dim == 7) { 50 | parlay::sequence> Points = readPointsFromFile>(iFile); 51 | timeGraph<7>(Points, k, oFile); 52 | } else { 53 | throw std::runtime_error("unsupported dimensionality"); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/euclideanMst/wspd/emst.cpp: -------------------------------------------------------------------------------- 1 | #include "parlay/parallel.h" 2 | #include "parlay/sequence.h" 3 | #include "pargeo/getTime.h" 4 | #include "pargeo/point.h" 5 | #include "pargeo/wspd.h" 6 | #include "pargeo/kdTree.h" 7 | #include "pargeo/bccp.h" 8 | #include "pargeo/kruskal.h" 9 | #include "euclideanMst/euclideanMst.h" 10 | 11 | using namespace parlay; 12 | using namespace pargeo; 13 | 14 | template 15 | parlay::sequence pargeo::euclideanMst(parlay::sequence> &S) { 16 | using nodeT = kdNode>; 17 | using floatT = double; 18 | 19 | timer t; 20 | 21 | nodeT* tree = buildKdt>(S, true, true); 22 | 23 | cout << "build-time = " << t.get_next() << endl; 24 | 25 | auto pairs = wspdParallel(tree, 2); 26 | 27 | cout << "decomposition-time = " << t.get_next() << endl; 28 | 29 | cout << "pairs = " << pairs.size() << endl; 30 | 31 | struct wEdge { 32 | size_t u,v; 33 | floatT weight; 34 | }; 35 | 36 | auto base = S.data(); 37 | sequence edges = tabulate(pairs.size(), [&](size_t i) { 38 | auto bcp = bccp(pairs[i].u, pairs[i].v); 39 | wEdge e; 40 | e.u = get<0>(bcp) - base; 41 | e.v = get<1>(bcp) - base; 42 | e.weight = get<2>(bcp); 43 | return e; 44 | }); 45 | 46 | cout << "bccp-time = " << t.get_next() << endl; 47 | 48 | auto edgeIds = kruskal(edges, S.size()); 49 | 50 | cout << "kruskal-time = " << t.get_next() << endl; 51 | 52 | auto mstEdges = tabulate(edgeIds.size(), 53 | [&](size_t i){ 54 | auto e = edges[edgeIds[i]]; 55 | return pargeo::wghEdge(e.u, e.v, e.weight); 56 | }); 57 | 58 | cout << "edges = " << mstEdges.size() << endl; 59 | return mstEdges; 60 | } 61 | 62 | template sequence pargeo::euclideanMst<2>(sequence> &); 63 | template sequence pargeo::euclideanMst<3>(sequence> &); 64 | template sequence pargeo::euclideanMst<4>(sequence> &); 65 | template sequence pargeo::euclideanMst<5>(sequence> &); 66 | template sequence pargeo::euclideanMst<6>(sequence> &); 67 | template sequence pargeo::euclideanMst<7>(sequence> &); 68 | -------------------------------------------------------------------------------- /src/spatialGraph/spanner/spanner.cpp: -------------------------------------------------------------------------------- 1 | #include "parlay/parallel.h" 2 | #include "parlay/primitives.h" 3 | #include "pargeo/point.h" 4 | #include "pargeo/kdTree.h" 5 | #include "pargeo/wspd.h" 6 | #include "pargeo/getTime.h" 7 | #include "spatialGraph/spatialGraph.h" 8 | 9 | template 10 | parlay::sequence pargeo::spanner(parlay::sequence> &S, double t) { 11 | using namespace std; 12 | using namespace pargeo; 13 | using namespace parlay; 14 | 15 | using pointT = point; 16 | using nodeT = kdNode; 17 | using pairT = wsp; 18 | 19 | // cout << t << "-spanner of " << S.size() << ", dim " << dim << " points" << endl; 20 | if (S.size() < 2) abort(); 21 | 22 | double s = 4*((double)t+1)/((double)t-1); 23 | // cout << "separation-constant = " << s << endl; 24 | 25 | if (s > 10) std::cout << "WARNING: very large separation constant, try a smaller t if crashed\n"; 26 | 27 | timer t0; 28 | t0.start(); 29 | 30 | nodeT* tree = buildKdt>(S, true, true); 31 | // cout << "build-tree-time = " << t0.get_next() << endl; 32 | 33 | auto wspd = wspdParallel(tree, s); 34 | // cout << "decomposition-time = " << t0.get_next() << endl; 35 | 36 | auto edges = sequence(wspd.size()); 37 | pointT* base = S.data(); 38 | parallel_for(0, wspd.size(), [&](size_t i) { 39 | size_t pt1 = wspd.at(i).u->getItem(0) - base; 40 | size_t pt2 = wspd.at(i).v->getItem(0) - base; 41 | edges[i] = edge(pt1, pt2); 42 | }); 43 | 44 | // cout << "edge-count = " << edges.size() << endl; 45 | 46 | return edges; 47 | } 48 | 49 | template parlay::sequence pargeo::spanner<2>(parlay::sequence> &, double); 50 | template parlay::sequence pargeo::spanner<3>(parlay::sequence> &, double); 51 | template parlay::sequence pargeo::spanner<4>(parlay::sequence> &, double); 52 | template parlay::sequence pargeo::spanner<5>(parlay::sequence> &, double); 53 | template parlay::sequence pargeo::spanner<6>(parlay::sequence> &, double); 54 | template parlay::sequence pargeo::spanner<7>(parlay::sequence> &, double); 55 | -------------------------------------------------------------------------------- /test/kdTree_test.cpp: -------------------------------------------------------------------------------- 1 | #include "pargeo/pointIO.h" 2 | #include "pargeo/kdTree.h" 3 | #include "gtest/gtest.h" 4 | 5 | template 6 | inline void testKdTree(parlay::sequence>& P) { 7 | using namespace pargeo; 8 | using nodeT = kdNode>; 9 | 10 | nodeT* tree1 = buildKdt>(P, true, false); 11 | nodeT* tree2 = buildKdt>(P, false, false); 12 | nodeT* tree3 = buildKdt>(P, true, true); 13 | nodeT* tree4 = buildKdt>(P, false, true); 14 | 15 | std::function checkSum = 16 | [&](nodeT* node)->size_t { 17 | if (!node->isLeaf()) { 18 | size_t lSize = checkSum(node->L()); 19 | size_t rSize = checkSum(node->R()); 20 | 21 | // Check if node sizes are consistent 22 | EXPECT_EQ(lSize + rSize, node->size()); 23 | 24 | // Check if each point is in the bounding box 25 | for (size_t i = 0; i < node->size(); ++ i) { 26 | auto p = *(node->at(i)); 27 | for (size_t d = 0; d < node->dim; ++ d) { 28 | EXPECT_TRUE(p[d] <= node->getMax(d)); 29 | EXPECT_TRUE(p[d] >= node->getMin(d)); 30 | } 31 | } 32 | 33 | // Check if box sizes are consistent 34 | for (size_t d = 0; d < node->dim; ++ d) { 35 | EXPECT_FLOAT_EQ(std::max(node->L()->getMax(d), 36 | node->R()->getMax(d)), 37 | node->getMax(d)); 38 | } 39 | } 40 | return node->size(); 41 | }; 42 | 43 | checkSum(tree1); 44 | checkSum(tree2); 45 | checkSum(tree3); 46 | checkSum(tree4); 47 | } 48 | 49 | TEST(kdTree_structure, testSerial2d) { 50 | using namespace pargeo; 51 | using namespace pargeo::pointIO; 52 | 53 | auto filePath = "datasets/2d_100.txt"; 54 | int dim = readHeader(filePath); 55 | 56 | parlay::sequence> P = 57 | readPointsFromFile>(filePath); 58 | 59 | testKdTree<2>(P); 60 | } 61 | 62 | TEST(kdTree_structure, testSerial3d) { 63 | using namespace pargeo; 64 | using namespace pargeo::pointIO; 65 | 66 | auto filePath = "datasets/3d_40.txt"; 67 | int dim = readHeader(filePath); 68 | 69 | parlay::sequence> P = 70 | readPointsFromFile>(filePath); 71 | 72 | testKdTree<3>(P); 73 | } 74 | 75 | int main(int argc, char **argv) { 76 | ::testing::InitGoogleTest(&argc, argv); 77 | return RUN_ALL_TESTS(); 78 | } 79 | -------------------------------------------------------------------------------- /executable/closestPair.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "parlay/parallel.h" 5 | #include "pargeo/point.h" 6 | #include "pargeo/pointIO.h" 7 | #include "pargeo/getTime.h" 8 | #include "pargeo/parseCommandLine.h" 9 | 10 | #include "closestPair/divideConquer.h" 11 | 12 | using namespace std; 13 | using namespace pargeo; 14 | using namespace pargeo::pointIO; 15 | 16 | template 17 | void timeClosestPair(sequence>& P, char* outFile, int perturb) { 18 | if (perturb) { 19 | parallel_for(0, P.size(), [&](size_t i) { 20 | for (int j = 0; j < dim; ++ j) { 21 | double myRand = P[i][j] / 10000; 22 | P[i][j] += -myRand + 2*myRand*hash64(i)/numeric_limits::max(); 23 | }}); 24 | } 25 | 26 | timer t0; t0.start(); 27 | auto cpair = closestPairDC(P); 28 | cout << "closest pair = " << cpair.u << ", " << cpair.v << endl; 29 | cout << "time = " << t0.stop() << endl; 30 | } 31 | 32 | int main(int argc, char* argv[]) { 33 | commandLine P(argc, argv, "./closestPair "); 34 | char* iFile = P.getArgument(0); 35 | char* oFile = P.getOptionValue("-o"); 36 | int perturb = P.getOptionIntValue("-p",0); 37 | 38 | int dim = readHeader(iFile); 39 | 40 | if (dim == 2) { 41 | parlay::sequence> Points = readPointsFromFile>(iFile); 42 | timeClosestPair<2>(Points, oFile, perturb);} 43 | else if (dim == 3) { 44 | parlay::sequence> Points = readPointsFromFile>(iFile); 45 | timeClosestPair<3>(Points, oFile, perturb);} 46 | else if (dim == 4) { 47 | parlay::sequence> Points = readPointsFromFile>(iFile); 48 | timeClosestPair<4>(Points, oFile, perturb);} 49 | else if (dim == 5) { 50 | parlay::sequence> Points = readPointsFromFile>(iFile); 51 | timeClosestPair<5>(Points, oFile, perturb);} 52 | else if (dim == 6) { 53 | parlay::sequence> Points = readPointsFromFile>(iFile); 54 | timeClosestPair<6>(Points, oFile, perturb);} 55 | else if (dim == 7) { 56 | parlay::sequence> Points = readPointsFromFile>(iFile); 57 | timeClosestPair<7>(Points, oFile, perturb);} 58 | else { 59 | throw std::runtime_error("dimension not yet supported"); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /test/datasets/3d_40.txt: -------------------------------------------------------------------------------- 1 | 4.64358861061e+00 3.57238375333e+00 -1.11467509593e+00 2 | 5.84057255959e-01 2.98440569083e+00 -2.46964848402e+00 3 | 9.22158227945e-01 1.12606946635e-01 3.50699605829e-01 4 | 2.27870839345e+00 -1.19956651359e-01 -5.08890628241e+00 5 | 5.57709629031e+00 -7.75751551455e-01 -1.15129146111e+00 6 | -1.33695569845e+00 4.78896083230e-01 2.72509287493e+00 7 | -7.84699267768e-01 -2.72541126387e+00 5.17447212222e+00 8 | 1.97313378995e+00 1.82374204784e+00 2.00969580624e+00 9 | -2.47732250322e+00 -4.02566042405e+00 2.42239325508e-01 10 | -2.72051015113e+00 4.39128412694e+00 -1.86318667559e+00 11 | 4.92752014096e+00 -1.61843701111e+00 3.32073706377e+00 12 | 1.29259593339e+00 3.71582472529e+00 2.95018887582e+00 13 | -1.91910384595e+00 -1.48539948336e-01 3.30096404704e+00 14 | 1.35399573838e+00 -5.05795119578e+00 3.45420508866e+00 15 | 4.10459998187e+00 -3.85628334112e+00 -1.21347779645e+00 16 | -1.33260871817e+00 -4.79089750101e+00 6.21705278097e-01 17 | -6.83344820170e-01 -3.59590332090e+00 -1.67585726658e+00 18 | -3.15830743195e+00 1.34894872536e+00 -1.98958715996e+00 19 | -2.27060936089e+00 4.36058933419e+00 -2.46588902775e+00 20 | 1.50940651219e+00 3.17252973510e+00 4.96312365659e+00 21 | 2.63167008394e+00 -2.90224504702e+00 -3.95483391158e+00 22 | -2.33196681534e+00 -1.34628082351e+00 5.12677221254e+00 23 | -1.42836143716e+00 -1.35883052745e+00 -5.99614126148e+00 24 | 3.03348248805e-01 4.99018202759e+00 2.28326619581e+00 25 | 2.25073285333e+00 1.77112588760e+00 1.74229424796e+00 26 | 4.14181336606e+00 -3.96008997962e+00 1.45314857013e-02 27 | 3.62581642504e+00 -1.95734088232e+00 5.88999258474e-01 28 | -5.45416611812e+00 -2.00464651960e-01 -1.25976648460e+00 29 | 2.80603623797e+00 -1.43839616654e+00 -3.00449279664e+00 30 | -2.60058966560e+00 -2.79817634169e+00 -7.42767449542e-02 31 | -4.15496607935e-01 2.10423527336e-01 -4.74109827060e+00 32 | 5.40914461841e+00 1.10103882068e+00 -3.48200823438e-01 33 | 2.29259686945e+00 -5.17832658743e+00 8.93490877798e-01 34 | -2.17916302691e+00 -4.95377564220e+00 -2.95724174386e+00 35 | 1.07888570323e+00 7.58781363415e-01 3.54978934790e+00 36 | -5.62190336903e+00 2.06750434966e+00 -2.02126731857e+00 37 | 1.31245618989e+00 4.54833596382e+00 -1.13854114280e+00 38 | -4.42595562425e+00 -1.83816544246e+00 -1.23289962760e+00 39 | -2.24800415871e+00 1.85049250857e+00 3.65473744260e-01 40 | 7.14247077585e-01 2.10709719255e+00 -4.35807421238e+00 41 | -------------------------------------------------------------------------------- /executable/knnGraph.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "parlay/parallel.h" 4 | #include "pargeo/point.h" 5 | #include "pargeo/getTime.h" 6 | #include "pargeo/pointIO.h" 7 | #include "pargeo/graphIO.h" 8 | #include "pargeo/parseCommandLine.h" 9 | #include "spatialGraph/spatialGraph.h" 10 | 11 | using namespace std; 12 | using namespace pargeo; 13 | using namespace pargeo::pointIO; 14 | using namespace pargeo::graphIO; 15 | 16 | template 17 | void timeGraph(parlay::sequence> &P, size_t k, char const *outFile) { 18 | timer t; t.start(); 19 | 20 | /* Add a small perturbation to the input to deal with duplicates */ 21 | parlay::parallel_for(0, P.size(), [&](size_t i) { 22 | for (int j = 0; j < dim; ++ j) { 23 | double myRand = P[i][j] / 1000000; 24 | P[i][j] += - myRand + 2 * myRand * parlay::hash64(i) / ULONG_MAX; 25 | } 26 | }); 27 | 28 | auto I = knnGraph(P, k); 29 | cout << "time = " << t.stop() << endl; 30 | if (outFile != NULL) graphIO::writeEdgeSeqToFile(I, outFile); 31 | } 32 | 33 | int main(int argc, char* argv[]) { 34 | commandLine P(argc,argv,"[-k ] [-o ] "); 35 | char* iFile = P.getArgument(0); 36 | size_t k = P.getOptionIntValue("-k",1); 37 | char* oFile = P.getOptionValue("-o"); 38 | 39 | int dim = readHeader(iFile); 40 | 41 | if (dim == 2) { 42 | parlay::sequence> Points = readPointsFromFile>(iFile); 43 | timeGraph<2>(Points, k, oFile); 44 | } else if (dim == 3) { 45 | parlay::sequence> Points = readPointsFromFile>(iFile); 46 | timeGraph<3>(Points, k, oFile); 47 | } else if (dim == 4) { 48 | parlay::sequence> Points = readPointsFromFile>(iFile); 49 | timeGraph<4>(Points, k, oFile); 50 | } else if (dim == 5) { 51 | parlay::sequence> Points = readPointsFromFile>(iFile); 52 | timeGraph<5>(Points, k, oFile); 53 | } else if (dim == 6) { 54 | parlay::sequence> Points = readPointsFromFile>(iFile); 55 | timeGraph<6>(Points, k, oFile); 56 | } else if (dim == 7) { 57 | parlay::sequence> Points = readPointsFromFile>(iFile); 58 | timeGraph<7>(Points, k, oFile); 59 | } else { 60 | throw std::runtime_error("unsupported dimensionality"); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /executable/emst.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "parlay/utilities.h" 4 | #include "parlay/parallel.h" 5 | #include "pargeo/point.h" 6 | #include "pargeo/getTime.h" 7 | #include "pargeo/pointIO.h" 8 | #include "pargeo/graphIO.h" 9 | #include "pargeo/getTime.h" 10 | #include "pargeo/parseCommandLine.h" 11 | #include "euclideanMst/euclideanMst.h" 12 | 13 | using namespace std; 14 | using namespace parlay; 15 | using namespace pargeo; 16 | using namespace pargeo::pointIO; 17 | using namespace pargeo::graphIO; 18 | 19 | template 20 | void timeEmst(sequence>& P, char* outFile, int perturb) { 21 | if (perturb) { 22 | parallel_for(0, P.size(), [&](size_t i) { 23 | for (int j = 0; j < dim; ++ j) { 24 | double myRand = P[i][j] / 10000; 25 | P[i][j] += -myRand + 2*myRand*hash64(i)/numeric_limits::max(); 26 | }}); 27 | } 28 | 29 | pargeo::timer t0; t0.start(); 30 | auto I = euclideanMst(P); 31 | cout << "time = " << t0.stop() << endl; 32 | if (outFile != NULL) graphIO::writeEdgeSeqToFile(I, outFile); 33 | } 34 | 35 | int main(int argc, char* argv[]) { 36 | commandLine P(argc,argv,"[-o ] "); 37 | char* iFile = P.getArgument(0); 38 | char* oFile = P.getOptionValue("-o"); 39 | int perturb = P.getOptionIntValue("-p",0); 40 | 41 | int dim = readHeader(iFile); 42 | 43 | if (dim == 2) { 44 | parlay::sequence> Points = readPointsFromFile>(iFile); 45 | timeEmst<2>(Points, oFile, perturb);} 46 | else if (dim == 3) { 47 | parlay::sequence> Points = readPointsFromFile>(iFile); 48 | timeEmst<3>(Points, oFile, perturb);} 49 | else if (dim == 4) { 50 | parlay::sequence> Points = readPointsFromFile>(iFile); 51 | timeEmst<4>(Points, oFile, perturb);} 52 | else if (dim == 5) { 53 | parlay::sequence> Points = readPointsFromFile>(iFile); 54 | timeEmst<5>(Points, oFile, perturb);} 55 | else if (dim == 6) { 56 | parlay::sequence> Points = readPointsFromFile>(iFile); 57 | timeEmst<6>(Points, oFile, perturb);} 58 | else if (dim == 7) { 59 | parlay::sequence> Points = readPointsFromFile>(iFile); 60 | timeEmst<7>(Points, oFile, perturb);} 61 | else { 62 | throw std::runtime_error("dimension not yet supported"); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | set(CMAKE_CXX_STANDARD 17) 3 | 4 | # check whether googletest is locally installed, if not download and fetch 5 | message(STATUS "--------------- GoogleTest -------------") 6 | find_package(GTest CONFIG) 7 | if(NOT GTest_FOUND) 8 | # new way of including googletest 9 | # Download and unpack googletest at configure time 10 | configure_file(CMakeLists.txt.in googletest-download/CMakeLists.txt) 11 | execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" . 12 | RESULT_VARIABLE result 13 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test/googletest-download ) 14 | if(result) 15 | message(FATAL_ERROR "CMake step for googletest failed: ${result}") 16 | endif() 17 | execute_process(COMMAND ${CMAKE_COMMAND} --build . 18 | RESULT_VARIABLE result 19 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/test/googletest-download ) 20 | if(result) 21 | message(FATAL_ERROR "Build step for googletest failed: ${result}") 22 | endif() 23 | 24 | # Prevent overriding the parent project's compiler/linker 25 | # settings on Windows 26 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 27 | 28 | # Add googletest directly to our build. This defines 29 | # the gtest and gtest_main targets. 30 | add_subdirectory(${CMAKE_BINARY_DIR}/googletest-src 31 | ${CMAKE_BINARY_DIR}/googletest-build 32 | EXCLUDE_FROM_ALL) 33 | set(GTest_LIBRARIES "gtest") 34 | else() 35 | message(STATUS "using locally installed GoogleTest") 36 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 37 | set(GTest_LIBRARIES GTest::gtest) 38 | endif() 39 | 40 | include(CTest) 41 | 42 | add_executable(kdTree_test kdTree_test.cpp) 43 | target_link_libraries(kdTree_test PRIVATE 44 | pargeoLib ${GTest_LIBRARIES}) 45 | add_test(NAME kdTree_test COMMAND kdTree_test) 46 | 47 | add_executable(closestPair_test closestPair_test.cpp) 48 | target_link_libraries(closestPair_test PRIVATE 49 | pargeoLib ${GTest_LIBRARIES}) 50 | add_test(NAME cloestPair_test COMMAND closestPair_test) 51 | 52 | add_executable(wspd_test wspd_test.cpp) 53 | target_link_libraries(wspd_test PRIVATE 54 | pargeoLib ${GTest_LIBRARIES}) 55 | add_test(NAME wspd_test COMMAND wspd_test) 56 | 57 | add_executable(dataset_test dataset_test.cpp) 58 | target_link_libraries(dataset_test PRIVATE 59 | pargeoLib ${GTest_LIBRARIES}) 60 | add_test(NAME dataset_test COMMAND dataset_test) 61 | 62 | message(STATUS "CMAKE_BINARY_DIR: ${CMAKE_BINARY_DIR}") 63 | file(COPY datasets DESTINATION ${CMAKE_BINARY_DIR}/test) 64 | -------------------------------------------------------------------------------- /benchmark/spatialSearch_bench.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "dataset/uniform.h" 4 | #include "pargeo/kdTree.h" 5 | #include "pargeo/kdTreeKnn.h" 6 | #include "pargeo/kdTreeRange.h" 7 | 8 | static parlay::sequence> data2d(size_t n) { 9 | auto P = pargeo::uniformInPolyPoints<2>(n, 1); 10 | return std::move(P); 11 | } 12 | 13 | static parlay::sequence> data3d(size_t n) { 14 | auto P = pargeo::uniformInPolyPoints<3>(n, 1); 15 | return std::move(P); 16 | } 17 | 18 | static void BM_knn_2d_includeTree(benchmark::State& state) { 19 | using namespace pargeo; 20 | auto P = data2d(state.range(0)); 21 | for (auto _ : state) { 22 | kdNode<2, point<2>>* tree = 23 | buildKdt<2, point<2>>(P, true, true); 24 | auto nn = kdTreeKnn(P, state.range(1), tree, false); 25 | } 26 | } 27 | 28 | static void BM_range_2d_includeTree(benchmark::State& state) { 29 | using namespace pargeo; 30 | auto P = data2d(state.range(0)); 31 | for (auto _ : state) { 32 | kdNode<2, point<2>>* tree = 33 | buildKdt<2, point<2>>(P, true, true); 34 | parallel_for(0, P.size(), [&](size_t i) { 35 | auto I = kdTreeRange(P, tree, P[i], state.range(1)); 36 | }); 37 | } 38 | } 39 | 40 | static void BM_knn_3d_includeTree(benchmark::State& state) { 41 | using namespace pargeo; 42 | auto P = data3d(state.range(0)); 43 | for (auto _ : state) { 44 | kdNode<3, point<3>>* tree = 45 | buildKdt<3, point<3>>(P, true, true); 46 | auto nn = kdTreeKnn(P, state.range(1), tree, false); 47 | } 48 | } 49 | 50 | static void BM_range_3d_includeTree(benchmark::State& state) { 51 | using namespace pargeo; 52 | auto P = data3d(state.range(0)); 53 | for (auto _ : state) { 54 | kdNode<3, point<3>>* tree = 55 | buildKdt<3, point<3>>(P, true, true); 56 | parallel_for(0, P.size(), [&](size_t i) { 57 | auto I = kdTreeRange(P, tree, P[i], state.range(1)); 58 | }); 59 | } 60 | } 61 | 62 | BENCHMARK(BM_knn_2d_includeTree) 63 | ->UseRealTime() 64 | ->Unit(benchmark::kMillisecond) 65 | ->Args({100000, 1})->Args({100000, 2})->Args({100000, 10}) 66 | ->Args({1000000, 1})->Args({1000000, 2})->Args({1000000, 10}); 67 | 68 | BENCHMARK(BM_range_2d_includeTree) 69 | ->UseRealTime() 70 | ->Unit(benchmark::kMillisecond) 71 | ->Arg(100000)->Arg(1000000); 72 | 73 | BENCHMARK(BM_knn_3d_includeTree) 74 | ->UseRealTime() 75 | ->Unit(benchmark::kMillisecond) 76 | ->Args({100000, 1})->Args({100000, 2})->Args({100000, 10}) 77 | ->Args({1000000, 1})->Args({1000000, 2})->Args({1000000, 10}); 78 | 79 | BENCHMARK(BM_range_3d_includeTree) 80 | ->UseRealTime() 81 | ->Unit(benchmark::kMillisecond) 82 | ->Arg(100000)->Arg(1000000); 83 | 84 | BENCHMARK_MAIN(); 85 | -------------------------------------------------------------------------------- /pybinding/test.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from datetime import datetime 3 | 4 | from matplotlib import pyplot as plt 5 | from matplotlib.collections import LineCollection 6 | 7 | from pypargeo import loadPoints 8 | from pypargeo import DelaunayGraph 9 | from pypargeo import WghDelaunayGraph 10 | from pypargeo import GabrielGraph 11 | from pypargeo import WghGabrielGraph 12 | from pypargeo import KnnGraph 13 | from pypargeo import WghKnnGraph 14 | from pypargeo import BetaSkeleton 15 | from pypargeo import WghBetaSkeleton 16 | from pypargeo import WghEuclideanMst 17 | from pypargeo import Spanner 18 | 19 | start_time = None 20 | 21 | def test(points, name, graphGen, savePlot = False, k = -1, eps = -1): 22 | dim = 2 23 | print("\n" + name) 24 | 25 | start_time = datetime.now() 26 | if k < 0: 27 | edges = graphGen(points) 28 | else: 29 | if eps > 0: 30 | edges = graphGen(points, k, eps) 31 | else: 32 | edges = graphGen(points, k) 33 | end_time = datetime.now() 34 | print("graph-gen-time: {}".format(end_time - start_time)) 35 | print("#-edges =", len(edges)) 36 | 37 | if savePlot: 38 | intEdges = edges[:,:2].astype(np.int32) 39 | lc = LineCollection(points[intEdges]) 40 | fig = plt.figure() 41 | plt.gca().add_collection(lc) 42 | plt.xlim(points[:,0].min()-.1, points[:,0].max()+.1) 43 | plt.ylim(points[:,1].min()-.1, points[:,1].max()+.1) 44 | plt.plot(points[:,0], points[:,1], 'ro', markersize=2) 45 | # labels = [str(i) for i in range(points.shape[0])] 46 | # for p,label in enumerate(labels): 47 | # plt.text(points[p,0], points[p,1], label, size=12) 48 | plt.gca().set_aspect('equal', adjustable='box') 49 | fig.savefig(name+".png") 50 | 51 | points = np.random.random((100, 2)) 52 | test(points, "rand-delaunay-100", DelaunayGraph, True) 53 | test(points, "rand-wgh-delaunay-100", WghDelaunayGraph, True) 54 | test(points, "rand-gabriel-100", GabrielGraph, True) 55 | test(points, "rand-wgh-gabriel-100", WghGabrielGraph, True) 56 | test(points, "rand-knn1-100", KnnGraph, True, 1) 57 | test(points, "rand-knn2-100", KnnGraph, True, 2) 58 | test(points, "rand-knn2-100", KnnGraph, True, 2) 59 | test(points, "rand-knn10-100", KnnGraph, True, 10) 60 | test(points, "rand-wgh-knn10-100", WghKnnGraph, True, 10) 61 | test(points, "rand-wgh-eps-knn10-100", WghKnnGraph, True, 10, 0.2) 62 | test(points, "beta-skeleton0.5-100", BetaSkeleton, True, 0.5) 63 | test(points, "beta-skeleton1-100", BetaSkeleton, True, 1) 64 | test(points, "beta-wgh-skeleton1-100", WghBetaSkeleton, True, 1) 65 | test(points, "beta-skeleton2-100", BetaSkeleton, True, 2) 66 | test(points, "beta-skeleton5-100", BetaSkeleton, True, 5) 67 | test(points, "emst-100", WghEuclideanMst, True) 68 | test(points, "10-spanner-100", Spanner, True, 10) 69 | -------------------------------------------------------------------------------- /src/spatialGraph/delaunayGraph/delaunayGraph.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "parlay/parallel.h" 4 | #include "parlay/primitives.h" 5 | #include "pargeo/point.h" 6 | #include "pargeo/getTime.h" 7 | #include "spatialGraph/spatialGraph.h" 8 | #include "delaunayTriangulation/delaunay.h" 9 | #include "delaunayTriangulation/geometry.h" 10 | 11 | #define SILENT 12 | 13 | template 14 | parlay::sequence pargeo::delaunayGraph(parlay::sequence> &P) { 15 | using namespace pbbsbench; 16 | using namespace parlay; 17 | using namespace std; 18 | 19 | if (dim != 2) 20 | throw std::runtime_error("Error, delaunay graph only supports 2 dimensional inputs."); 21 | #ifndef SILENT 22 | timer t; t.start(); 23 | #endif 24 | // A pbbsbench point data structure 25 | sequence> P2d(P.size()); 26 | parallel_for(0, P.size(), [&](size_t i) { 27 | P2d[i].x = P[i][0]; 28 | P2d[i].y = P[i][1]; 29 | }); 30 | #ifndef SILENT 31 | cout << "data-convert-time = " << t.get_next() << endl; 32 | #endif 33 | triangles> Tri = delaunay(P2d); 34 | #ifndef SILENT 35 | cout << "triangulation-time = " << t.get_next() << endl; 36 | #endif 37 | using pt = pargeo::point; 38 | 39 | size_t n = P.size(); 40 | size_t nt = Tri.numTriangles(); 41 | sequence edges(nt*3+1); 42 | edges[nt*3] = edge(); 43 | 44 | // Process a triangle 45 | auto processTri = [&](tri &T, size_t idx) { 46 | for(int i=0; i<3; ++i) { 47 | size_t u = T[(i+1)%3]; 48 | size_t v = T[(i+2)%3]; 49 | 50 | // Keep non-boundary edges 51 | if (u < n && v < n) 52 | edges[idx*3 + i] = edge(u,v); 53 | else 54 | edges[idx*3 + i] = edge(); 55 | 56 | } 57 | }; 58 | 59 | // Process triangles 60 | parallel_for(0, nt, [&](size_t i) { 61 | processTri(Tri.T[i], i); 62 | }); 63 | 64 | // Group edges 65 | sort_inplace(edges, [&](edge e1, edge e2) { 66 | return e1.u == e2.u ? 67 | (e1.v < e2.v) : (e1.u < e2.u); 68 | }); 69 | 70 | // Only keep each edge once 71 | sequence flag(nt*3); 72 | parallel_for(0, edges.size(), [&](size_t i) { 73 | if ( edges[i] == edges[i+1] && !edges[i].isEmpty() ) 74 | flag[i] = 1; 75 | else 76 | flag[i] = 0; 77 | }); 78 | 79 | sequence edges2 = pack(edges, flag); 80 | 81 | #ifndef SILENT 82 | cout << "graph-gen-time = " << t.stop() << endl; 83 | cout << "#-triangles = " << nt << endl; 84 | cout << "#delaunay-edges = " << edges2.size() << endl; 85 | #endif 86 | return edges2; 87 | } 88 | 89 | template parlay::sequence pargeo::delaunayGraph<2>(parlay::sequence> &); 90 | template parlay::sequence pargeo::delaunayGraph<3>(parlay::sequence> &); 91 | -------------------------------------------------------------------------------- /include/pargeo/atomics.h: -------------------------------------------------------------------------------- 1 | // This file is part of pbbsbench 2 | 3 | #pragma once 4 | 5 | namespace pargeo { 6 | 7 | template 8 | inline bool atomic_compare_and_swap(ET* a, ET oldval, ET newval) { 9 | static_assert(sizeof(ET) <= 8, "Bad CAS length"); 10 | if (sizeof(ET) == 1) { 11 | uint8_t r_oval, r_nval; 12 | std::memcpy(&r_oval, &oldval, sizeof(ET)); 13 | std::memcpy(&r_nval, &newval, sizeof(ET)); 14 | return __sync_bool_compare_and_swap(reinterpret_cast(a), r_oval, r_nval); 15 | } else if (sizeof(ET) == 4) { 16 | uint32_t r_oval, r_nval; 17 | std::memcpy(&r_oval, &oldval, sizeof(ET)); 18 | std::memcpy(&r_nval, &newval, sizeof(ET)); 19 | return __sync_bool_compare_and_swap(reinterpret_cast(a), r_oval, r_nval); 20 | } else { // if (sizeof(ET) == 8) { 21 | uint64_t r_oval, r_nval; 22 | std::memcpy(&r_oval, &oldval, sizeof(ET)); 23 | std::memcpy(&r_nval, &newval, sizeof(ET)); 24 | return __sync_bool_compare_and_swap(reinterpret_cast(a), r_oval, r_nval); 25 | } 26 | } 27 | 28 | template 29 | inline E fetch_and_add(E *a, EV b) { 30 | volatile E newV, oldV; 31 | do {oldV = *a; newV = oldV + b;} 32 | while (!atomic_compare_and_swap(a, oldV, newV)); 33 | return oldV; 34 | } 35 | 36 | template 37 | inline void write_add(E *a, EV b) { 38 | //volatile E newV, oldV; 39 | E newV, oldV; 40 | do {oldV = *a; newV = oldV + b;} 41 | while (!atomic_compare_and_swap(a, oldV, newV)); 42 | } 43 | 44 | template 45 | inline void write_add(std::atomic *a, EV b) { 46 | //volatile E newV, oldV; 47 | E newV, oldV; 48 | do {oldV = a->load(); newV = oldV + b;} 49 | while (!std::atomic_compare_exchange_strong(a, &oldV, newV)); 50 | } 51 | 52 | template 53 | inline bool write_min(ET *a, ET b, F less) { 54 | ET c; bool r=0; 55 | do c = *a; 56 | while (less(b,c) && !(r=atomic_compare_and_swap(a,c,b))); 57 | return r; 58 | } 59 | 60 | template 61 | inline bool write_min(std::atomic *a, ET b, F less) { 62 | ET c; bool r=0; 63 | do c = a->load(); 64 | while (less(b,c) && !(r=std::atomic_compare_exchange_strong(a, &c, b))); 65 | return r; 66 | } 67 | 68 | template 69 | inline bool write_max(ET *a, ET b, F less) { 70 | ET c; bool r=0; 71 | do c = *a; 72 | while (less(c,b) && !(r=atomic_compare_and_swap(a,c,b))); 73 | return r; 74 | } 75 | 76 | template 77 | inline bool write_max(std::atomic *a, ET b, F less) { 78 | ET c; bool r=0; 79 | do c = a->load(); 80 | while (less(c,b) && !(r=std::atomic_compare_exchange_strong(a, &c, b))); 81 | return r; 82 | } 83 | 84 | } // End namespace pargeo 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## ParGeo is now being maintained at https://github.com/ParAlg/ParGeo 2 | ## Pargeo: A Library for Parallal Algorithms in Computational Geometry 3 | 4 | Pargeo is being developted at MIT. It hosts a growing set of multi-core implementations for parallel algorithms in computational geometry: 5 | 6 | * Graph Generators 7 | * Euclidean MST 8 | * K-Nearest Neighbor Graph 9 | * Planar Beta-Skeleton 10 | * Planar Delaunay Graph 11 | * Planar Gabriel Graph 12 | * T-Spanner 13 | * Spatial Query 14 | * K-Nearest Neighbor 15 | * Range Search 16 | * Closest Pair 17 | * 2D & 3D Z-Order Sort 18 | * Planar Delaunay Triangulation 19 | * Well-Separated Pair Decomposition 20 | 21 | ## Depdendencies 22 | 23 | Pargeo runs on any modern x86-based multicore machines. To compile, it requires g++ 5.4.0 or later. The build system is [CMake](https://cmake.org/install/). 24 | 25 | Pargeo uses the [parlaylib](https://github.com/cmuparlay/parlaylib), a wonderful library developed at CMU for multi-core parallel programming. It and other dependencies are included in the project as submodules - initialize them before compiling the code: 26 | 27 | ``` 28 | git submodule init 29 | git submodule update 30 | ``` 31 | 32 | ## Compilation 33 | 34 | Starting from the project root directory: 35 | 36 | ``` 37 | mkdir build 38 | cd build 39 | cmake .. 40 | make -j # this will take a while 41 | ``` 42 | 43 | ## Usage 44 | 45 | After the build completes, navigate to `/build/executable`, where the executables are generated. Running `./` in the terminal displays short usage instructions. As an example, to compute the Euclidean MST of a point data set ``, input the following, and the MST edges will be written to `edges.txt`. 46 | 47 | ``` 48 | ./emst -o edges.txt 49 | ``` 50 | 51 | Example data sets can be found [here](https://github.com/wangyiqiu/pargeo/tree/main/test/datasets). Pargeo automatically parses CSV-like point data files with or without header, as long as the delimiters (space, comma, etc) in between numbers are one character each in length. 52 | 53 | ## References 54 | 55 | [1] [Yiqiu Wang, Shangdi Yu, Laxman Dhulipala, Yan Gu, and Julian Shun. 2021. GeoGraph: A Framework for Graph Processing on Geometric Data. SIGOPS Operating Systems Review 55, 1 (July 2021), 38–46.](https://dl.acm.org/doi/abs/10.1145/3469379.3469384) 56 | 57 | [2] [Yiqiu Wang, Shangdi Yu, Yan Gu, and Julian Shun. 2021. Fast Parallel Algorithms for Euclidean Minimum Spanning Tree and Hierarchical Spatial Clustering. In Proceedings of the 2021 International Conference on Management of Data (SIGMOD/PODS '21). Association for Computing Machinery, New York, NY, USA, 1982–1995.](https://arxiv.org/abs/2104.01126) 58 | 59 | [3] [Yiqiu Wang, Shangdi Yu, Yan Gu, and Julian Shun. 2021. A Parallel Batch-Dynamic Data Structure for the Closest Pair Problem. 37th International Symposium on Computational Geometry (SoCG 2021).](https://arxiv.org/abs/2010.02379) 60 | -------------------------------------------------------------------------------- /src/euclideanMst/memoGfk/emst.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "parlay/parallel.h" 3 | #include "parlay/sequence.h" 4 | #include "pargeo/getTime.h" 5 | #include "pargeo/point.h" 6 | #include "pargeo/wspd.h" 7 | #include "pargeo/kdTree.h" 8 | #include "pargeo/bccp.h" 9 | #include "pargeo/kruskal.h" 10 | #include "euclideanMst/euclideanMst.h" 11 | #include "wspdFilter.h" 12 | #include "mark.h" 13 | 14 | using namespace std; 15 | using namespace parlay; 16 | using namespace pargeo; 17 | using namespace pargeo::emstInternal; 18 | 19 | template 20 | parlay::sequence pargeo::euclideanMst(parlay::sequence> &S) { 21 | using pointT = point; 22 | using nodeT = kdNode>; 23 | using floatT = typename pointT::floatT; 24 | using pairT = wsp; 25 | using bcpT = tuple; 26 | 27 | if (S.size() < 2) { 28 | throw std::runtime_error("need more than 2 points"); 29 | } 30 | 31 | // timer t0; 32 | // t0.start(); 33 | bool paraTree = true; 34 | 35 | nodeT* tree = buildKdt>(S, true, true); 36 | 37 | // cout << "build-tree-time = " << t0.get_next() << endl; 38 | 39 | floatT rhoLo = -0.1; 40 | floatT beta = 2; 41 | size_t numEdges = 0; 42 | 43 | floatT wspdTime = 0; 44 | floatT kruskalTime = 0; 45 | floatT markTime = 0; 46 | edgeUnionFind UF(S.size()); 47 | 48 | // t0.stop(); 49 | 50 | while (UF.numEdge() < S.size() - 1) { 51 | 52 | // t0.start(); 53 | 54 | floatT rhoHi; 55 | auto bccps = filterWspdParallel(beta, rhoLo, rhoHi, tree, &UF); 56 | 57 | // wspdTime += t0.get_next(); 58 | 59 | // cout << "---" << endl; 60 | // cout << " beta = " << beta << endl; 61 | // cout << " rho = " << rhoLo << " -- " << rhoHi << endl; 62 | 63 | numEdges += bccps.size(); 64 | 65 | if (bccps.size() <= 0) { 66 | beta *= 2; 67 | rhoLo = rhoHi; 68 | continue;} 69 | 70 | // cout << " edges = " << bccps.size() << endl; 71 | 72 | struct wEdge { 73 | size_t u,v; 74 | floatT weight; 75 | }; 76 | 77 | auto base = S.data(); 78 | sequence edges = tabulate(bccps.size(), [&](size_t i) { 79 | auto bcp = bccps[i]; 80 | wEdge e; 81 | e.u = get<0>(bcp) - base; 82 | e.v = get<1>(bcp) - base; 83 | e.weight = get<2>(bcp); 84 | return e; 85 | }); 86 | 87 | batchKruskal(edges, S.size(), UF); 88 | // cout << " mst-edges = " << UF.numEdge() << endl; 89 | // kruskalTime += t0.get_next(); 90 | 91 | mark>(tree, &UF, S.data()); 92 | // markTime += t0.stop(); 93 | 94 | beta *= 2; 95 | rhoLo = rhoHi; 96 | } 97 | 98 | // floatT sum = 0; 99 | // auto E = UF.getEdge(); 100 | // for (auto e: E) 101 | // sum += S[e.u].dist(S[e.v]); 102 | // cout << "edge-sum = " << sum << endl; 103 | 104 | // cout << "wspd-time = " << wspdTime << endl; 105 | // cout << "kruskal-time = " << kruskalTime << endl; 106 | // cout << "mark-time = " << markTime << endl; 107 | return UF.getEdge(); 108 | } 109 | 110 | template sequence pargeo::euclideanMst<2>(sequence> &); 111 | template sequence pargeo::euclideanMst<3>(sequence> &); 112 | template sequence pargeo::euclideanMst<4>(sequence> &); 113 | template sequence pargeo::euclideanMst<5>(sequence> &); 114 | template sequence pargeo::euclideanMst<6>(sequence> &); 115 | template sequence pargeo::euclideanMst<7>(sequence> &); 116 | -------------------------------------------------------------------------------- /include/dataset/uniform.h: -------------------------------------------------------------------------------- 1 | /* This data generator is partially adapted from the 2 | Problem Based Benchmark Suite 3 | https://github.com/cmuparlay/pbbsbench 4 | */ 5 | 6 | #pragma once 7 | 8 | #include 9 | #include "parlay/parallel.h" 10 | #include "parlay/utilities.h" 11 | #include "pargeo/point.h" 12 | 13 | namespace pargeo { 14 | namespace uniformDataGen { 15 | 16 | template 17 | floatT randFloat(size_t i) { 18 | return floatT(parlay::hash64(i)) / floatT(std::numeric_limits::max()); 19 | } 20 | 21 | template 22 | pointT randNd(size_t i) { 23 | size_t s[dim]; 24 | s[0] = i; 25 | for (int j=1; j(s[j]) - 1; 31 | } 32 | return pointT(ss); 33 | } 34 | 35 | template 36 | pointT randInUnitSphere(size_t i) { 37 | auto origin = pointT(); 38 | for(int j=0; j(o+i); 44 | } while (p.dist(origin) > 1.0); 45 | return p; 46 | } 47 | 48 | template 49 | pointT randOnUnitSphere(size_t i, 50 | typename pointT::floatT scale=1) { 51 | auto origin = pointT(); 52 | for(int j=0; j(i); 54 | return (v / v.dist(origin)) * scale; 55 | } 56 | 57 | } // End uniform data gen namespace 58 | 59 | template> 60 | parlay::sequence uniformInPolyPoints(size_t n, 61 | size_t shape, 62 | double scale = 1.0) { 63 | using namespace parlay; 64 | using namespace uniformDataGen; 65 | 66 | auto P = sequence(n); 67 | parallel_for (0, n, [&](size_t i) { 68 | if (shape == 0) P[i] = randInUnitSphere(i) * scale; 69 | else if (shape == 1) P[i] = randNd(i) * scale; 70 | else throw std::runtime_error("generator not implemented yet"); 71 | }); 72 | 73 | return P; // data should be already permuted 74 | } 75 | 76 | template> 77 | parlay::sequence uniformOnPolyPoints(size_t n, 78 | size_t shape, 79 | double thickness, 80 | double scale = 1.0) { 81 | using namespace parlay; 82 | using namespace uniformDataGen; 83 | using floatT = typename pointT::floatT; 84 | 85 | auto P = sequence(n); 86 | 87 | if (shape == 0) { 88 | floatT r1 = 1 + thickness; 89 | floatT r2 = 1 - thickness; 90 | floatT a1 = 1; for (int d = 0; d < dim - 1; ++ d) a1 *= r1; 91 | floatT a2 = 1; for (int d = 0; d < dim - 1; ++ d) a2 *= r2; 92 | size_t n1 = a1 * n / (a1 + a2); 93 | size_t n2 = n - n1; 94 | floatT t1 = 1 - 1 / r1; 95 | floatT t2 = 1 / r2 - 1; 96 | 97 | // Outer 98 | parallel_for (0, n1, [&](size_t i) { 99 | floatT s = 1 - t1 * randFloat(i); 100 | P[i] = randOnUnitSphere(i, r1) * s * scale; 101 | }); 102 | 103 | // Inner 104 | parallel_for (n1, n, [&](size_t i) { 105 | floatT s = t2 * randFloat(i) + 1; 106 | P[i] = randOnUnitSphere(i, r2) * s * scale; 107 | }); 108 | 109 | } else throw std::runtime_error("generator not implemented yet"); 110 | 111 | return P; // data should be already permuted 112 | } 113 | 114 | } // End namespace 115 | -------------------------------------------------------------------------------- /src/spatialGraph/gabrielGraph/gabrielGraph.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "parlay/parallel.h" 4 | #include "parlay/primitives.h" 5 | #include "pargeo/point.h" 6 | #include "pargeo/getTime.h" 7 | #include "spatialGraph/spatialGraph.h" 8 | #include "delaunayTriangulation/delaunay.h" 9 | 10 | #define SILENT 11 | 12 | template 13 | parlay::sequence pargeo::gabrielGraph(parlay::sequence> &P) { 14 | using namespace parlay; 15 | using namespace std; 16 | using namespace pbbsbench; 17 | 18 | if (dim != 2) 19 | throw std::runtime_error("Error, gabriel graph only supports 2 dimensional inputs."); 20 | #ifndef SILENT 21 | timer t; t.start(); 22 | #endif 23 | // A pbbsbench point data structure 24 | sequence> P2d(P.size()); 25 | parallel_for(0, P.size(), [&](size_t i) { 26 | P2d[i].x = P[i][0]; 27 | P2d[i].y = P[i][1]; 28 | }); 29 | #ifndef SILENT 30 | cout << "data-convert-time = " << t.get_next() << endl; 31 | #endif 32 | triangles> Tri = delaunay(P2d); 33 | #ifndef SILENT 34 | cout << "triangulation-time = " << t.get_next() << endl; 35 | #endif 36 | using pt = pargeo::point; 37 | using edgepair = tuple; 38 | 39 | size_t n = P.size(); 40 | size_t nt = Tri.numTriangles(); 41 | sequence edges(nt*3+1); 42 | edges[edges.size()-1] = make_tuple(edge(), false); 43 | 44 | // Process a triangle 45 | auto processTri = [&](tri &T, size_t idx) { 46 | for(int i=0; i<3; ++i) { 47 | // Testing if x violates u,v 48 | size_t x = T[i]; 49 | size_t u = T[(i+1)%3]; 50 | size_t v = T[(i+2)%3]; 51 | 52 | // Discard irrelevant edges of boundary triangle edges 53 | if (u >= n || v >= n) { 54 | edges[idx*3 + i] = make_tuple(edge(u,v), false); 55 | continue; 56 | } 57 | 58 | pt c = (P[u] + P[v])/2; 59 | typename pt::floatT rq = (P[u] - P[v]).length()/2; 60 | typename pt::floatT dis = (P[x] - c).length(); 61 | if (x < n && dis <= rq) { // not gabriel 62 | edges[idx*3 + i] = make_tuple(edge(u,v), false); 63 | } else { // ok 64 | edges[idx*3 + i] = make_tuple(edge(u,v), true); 65 | } 66 | } 67 | }; 68 | 69 | // Process triangles 70 | parallel_for(0, nt, [&](size_t i) { 71 | processTri(Tri.T[i], i); 72 | }); 73 | 74 | // Group edges 75 | sort_inplace(edges, [&](edgepair e1, edgepair e2) { 76 | return get<0>(e1).u == get<0>(e2).u ? 77 | (get<0>(e1).v < get<0>(e2).v): 78 | (get<0>(e1).u < get<0>(e2).u); 79 | }); 80 | 81 | // parallel_for(0, edges.size(), [&](size_t i) { 82 | // auto e = get<0>(edges[i]); 83 | // cout << "(" << e.u << "," << e.v << ")" << get<1>(edges[i]) << " "; 84 | // }); 85 | // cout << endl; 86 | 87 | // Keep valid edges 88 | // - appears twice && both are valid in their own triangles 89 | sequence flag(nt*3+1); 90 | parallel_for(0, edges.size(), [&](size_t i) { 91 | if ( get<0>(edges[i]) == get<0>(edges[i+1]) && 92 | get<1>(edges[i]) && 93 | get<1>(edges[i+1]) ) { 94 | flag[i] = 1; 95 | } else { 96 | flag[i] = 0; 97 | } 98 | }); 99 | 100 | size_t ne = scan_inplace(flag.cut(0,nt*3)); 101 | flag[nt*3] = ne; 102 | 103 | sequence edges2(ne); 104 | parallel_for(0, nt*3, [&](size_t i) { 105 | if (flag[i] != flag[i+1]) { 106 | edges2[flag[i]] = get<0>(edges[i]); 107 | } 108 | }); 109 | 110 | #ifndef SILENT 111 | cout << "graph-gen-time = " << t.stop() << endl; 112 | cout << "#-triangles = " << nt << endl; 113 | cout << "#gabriel-edges = " << ne << endl; 114 | #endif 115 | return edges2; 116 | } 117 | 118 | template parlay::sequence pargeo::gabrielGraph<2>(parlay::sequence> &); 119 | -------------------------------------------------------------------------------- /test/wspd_test.cpp: -------------------------------------------------------------------------------- 1 | #include "pargeo/pointIO.h" 2 | #include "pargeo/kdTree.h" 3 | #include "pargeo/wspd.h" 4 | #include "gtest/gtest.h" 5 | 6 | template 7 | inline void testWspd(parlay::sequence>& P, 8 | parlay::sequence>>> pairs, 9 | double constant) { 10 | using namespace std; 11 | using namespace pargeo; 12 | using pointT = point; 13 | using nodeT = kdNode>; 14 | 15 | // Check that each pair is well separated 16 | for (auto pair: pairs) { 17 | EXPECT_TRUE(geomWellSeparated(pair.u, pair.v, constant)); 18 | } 19 | 20 | // Check that each pair of point is in a pair 21 | auto find = [&](pointT p1, pointT p2){ 22 | for (auto pair: pairs) { 23 | 24 | for (size_t i = 0; i < pair.u->size(); ++ i) { 25 | for (size_t j = 0; j < pair.v->size(); ++ j) { 26 | 27 | if (p1 == *pair.u->at(i) && p2 == *pair.v->at(j)) 28 | return true; 29 | if (p2 == *pair.u->at(i) && p1 == *pair.v->at(j)) 30 | return true; 31 | } 32 | } 33 | 34 | } 35 | return false; 36 | abort(); 37 | }; 38 | 39 | for (size_t i = 0; i < P.size(); ++ i) { 40 | for (size_t j = i + 1; j < P.size(); ++ j) { 41 | EXPECT_TRUE(find(P[i], P[j])); 42 | } 43 | } 44 | 45 | } 46 | 47 | TEST(wspd_test, testSerial2d) { 48 | using namespace parlay; 49 | using namespace pargeo; 50 | using namespace pargeo::pointIO; 51 | 52 | auto filePath = "datasets/2d_100.txt"; 53 | int dim = readHeader(filePath); 54 | 55 | parlay::sequence> P = 56 | readPointsFromFile>(filePath); 57 | 58 | kdNode<2, point<2>>* tree = buildKdt<2, point<2>>(P, true, true); 59 | 60 | auto pairs1 = wspdSerial(tree, 2); 61 | 62 | testWspd<2>(P, pairs1, 2); 63 | 64 | auto pairs2 = wspdSerial(tree, 2.2); 65 | 66 | testWspd<2>(P, pairs2, 2.2); 67 | } 68 | 69 | TEST(wspd_test, testParallel2d) { 70 | using namespace parlay; 71 | using namespace pargeo; 72 | using namespace pargeo::pointIO; 73 | 74 | auto filePath = "datasets/2d_100.txt"; 75 | int dim = readHeader(filePath); 76 | 77 | parlay::sequence> P = 78 | readPointsFromFile>(filePath); 79 | 80 | kdNode<2, point<2>>* tree = buildKdt<2, point<2>>(P, true, true); 81 | 82 | auto pairs1 = wspdParallel(tree, 2); 83 | 84 | testWspd<2>(P, pairs1, 2); 85 | 86 | auto pairs2 = wspdParallel(tree, 3.9); 87 | 88 | testWspd<2>(P, pairs2, 3.9); 89 | } 90 | 91 | TEST(wspd_test, testSerial3d) { 92 | using namespace parlay; 93 | using namespace pargeo; 94 | using namespace pargeo::pointIO; 95 | 96 | auto filePath = "datasets/3d_40.txt"; 97 | int dim = readHeader(filePath); 98 | 99 | parlay::sequence> P = 100 | readPointsFromFile>(filePath); 101 | 102 | kdNode<3, point<3>>* tree = buildKdt<3, point<3>>(P, true, true); 103 | 104 | auto pairs1 = wspdSerial(tree, 2); 105 | 106 | testWspd<3>(P, pairs1, 2); 107 | 108 | auto pairs2 = wspdSerial(tree, 2.2); 109 | 110 | testWspd<3>(P, pairs2, 2.2); 111 | } 112 | 113 | TEST(wspd_test, testParallel3d) { 114 | using namespace parlay; 115 | using namespace pargeo; 116 | using namespace pargeo::pointIO; 117 | 118 | auto filePath = "datasets/3d_40.txt"; 119 | int dim = readHeader(filePath); 120 | 121 | parlay::sequence> P = 122 | readPointsFromFile>(filePath); 123 | 124 | kdNode<3, point<3>>* tree = buildKdt<3, point<3>>(P, true, true); 125 | 126 | auto pairs1 = wspdParallel(tree, 2); 127 | 128 | testWspd<3>(P, pairs1, 2); 129 | 130 | auto pairs2 = wspdParallel(tree, 3.9); 131 | 132 | testWspd<3>(P, pairs2, 3.9); 133 | } 134 | 135 | int main(int argc, char **argv) { 136 | ::testing::InitGoogleTest(&argc, argv); 137 | return RUN_ALL_TESTS(); 138 | } 139 | -------------------------------------------------------------------------------- /include/pargeo/point.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "parlay/parallel.h" 8 | #include "parlay/primitives.h" 9 | 10 | namespace pargeo { 11 | 12 | struct _empty { 13 | int arr[0]; // todo this produces a struct of size 0 but seems dangerous, need to check 14 | }; 15 | 16 | template class _point { 17 | 18 | static constexpr _tData empty = std::numeric_limits<_tData>::max(); 19 | 20 | public: 21 | 22 | static constexpr int dim = _dim; 23 | typedef _tFloat floatT; 24 | 25 | _tData x[_dim]; 26 | _tAtt attribute; 27 | 28 | _point() { for (int i=0; i<_dim; ++i) x[i]=empty; } 29 | 30 | _point(_tData* p) { for (int i=0; i<_dim; ++i) x[i]=p[i]; } 31 | 32 | _point(_point* p): attribute(p->attribute) { for (int i=0; i<_dim; ++i) x[i]=p->x[i]; } 33 | 34 | template 35 | _point(parlay::slice<_tIn*,_tIn*> p) { 36 | for(int i=0; i<_dim; ++i) x[i] = (_tData)p[i];} 37 | 38 | void setEmpty() {x[0]=empty;} 39 | 40 | bool isEmpty() {return x[0]==empty;} 41 | 42 | _point operator+(_point op2) { 43 | _tData xx[_dim]; 44 | for (int i=0; i<_dim; ++i) xx[i] = x[i]+op2.x[i]; 45 | return _point(xx);} 46 | 47 | _point operator-(_point op2) { 48 | _tData xx[_dim]; 49 | for (int i=0; i<_dim; ++i) xx[i] = x[i]-op2.x[i]; 50 | return _point(xx);} 51 | 52 | _point operator*(_tData dv) { 53 | _tData xx[_dim]; 54 | for (int i=0; i<_dim; ++i) xx[i] = x[i]*dv; 55 | return _point(xx);} 56 | 57 | _point operator/(_tData dv) { 58 | _tData xx[_dim]; 59 | for (int i=0; i<_dim; ++i) xx[i] = x[i]/dv; 60 | return _point(xx);} 61 | 62 | _tData& operator[](int i) {return x[i];} 63 | 64 | _tData& at(int i) {return x[i];} 65 | 66 | friend bool operator==(_point a, _point b) { 67 | for (int ii=0; ii 104 | using point = _point; 105 | 106 | template 107 | using fpoint = _point; 108 | 109 | template 110 | using lpoint = _point; 111 | 112 | template 113 | _B pointCast(_B p) { 114 | _B q; 115 | for (int i=0; i 121 | static std::ostream& operator<<(std::ostream& os, const pargeo::point v) { 122 | for (int i=0; i 128 | static std::ostream& operator<<(std::ostream& os, const pargeo::fpoint v) { 129 | for (int i=0; i 135 | static std::ostream& operator<<(std::ostream& os, const pargeo::lpoint v) { 136 | for (int i=0; i 26 | #include 27 | #include 28 | #include 29 | 30 | namespace pargeo { 31 | 32 | struct commandLine { 33 | int argc; 34 | char** argv; 35 | std::string comLine; 36 | commandLine(int _c, char** _v, std::string _cl) 37 | : argc(_c), argv(_v), comLine(_cl) { 38 | if (getOption("-h") || getOption("-help")) 39 | badArgument(); 40 | } 41 | 42 | commandLine(int _c, char** _v) 43 | : argc(_c), argv(_v), comLine("bad arguments") { } 44 | 45 | void badArgument() { 46 | std::cout << "usage: " << argv[0] << " " << comLine << std::endl; 47 | exit(0); 48 | } 49 | 50 | // get an argument 51 | // i is indexed from the last argument = 0, second to last indexed 1, .. 52 | char* getArgument(int i) { 53 | if (argc < 2+i) badArgument(); 54 | return argv[argc-1-i]; 55 | } 56 | 57 | // looks for two filenames 58 | std::pair IOFileNames() { 59 | if (argc < 3) badArgument(); 60 | return std::pair(argv[argc-2],argv[argc-1]); 61 | } 62 | 63 | std::pair sizeAndFileName() { 64 | if (argc < 3) badArgument(); 65 | return std::pair(std::atoi(argv[argc-2]),(char*) argv[argc-1]); 66 | } 67 | 68 | bool getOption(std::string option) { 69 | for (int i = 1; i < argc; i++) 70 | if ((std::string) argv[i] == option) return true; 71 | return false; 72 | } 73 | 74 | char* getOptionValue(std::string option) { 75 | for (int i = 1; i < argc-1; i++) 76 | if ((std::string) argv[i] == option) return argv[i+1]; 77 | return NULL; 78 | } 79 | 80 | std::string getOptionValue(std::string option, std::string defaultValue) { 81 | for (int i = 1; i < argc-1; i++) 82 | if ((std::string) argv[i] == option) return (std::string) argv[i+1]; 83 | return defaultValue; 84 | } 85 | 86 | long getOptionLongValue(std::string option, long defaultValue) { 87 | for (int i = 1; i < argc-1; i++) 88 | if ((std::string) argv[i] == option) { 89 | long r = atol(argv[i+1]); 90 | if (r < 0) badArgument(); 91 | return r; 92 | } 93 | return defaultValue; 94 | } 95 | 96 | int getOptionIntValue(std::string option, int defaultValue) { 97 | for (int i = 1; i < argc-1; i++) 98 | if ((std::string) argv[i] == option) { 99 | int r = atoi(argv[i+1]); 100 | if (r < 0) badArgument(); 101 | return r; 102 | } 103 | return defaultValue; 104 | } 105 | 106 | double getOptionDoubleValue(std::string option, double defaultValue) { 107 | for (int i = 1; i < argc-1; i++) 108 | if ((std::string) argv[i] == option) { 109 | double val; 110 | if (sscanf(argv[i+1], "%lf", &val) == EOF) { 111 | badArgument(); 112 | } 113 | return val; 114 | } 115 | return defaultValue; 116 | } 117 | 118 | }; 119 | 120 | } // End namespace pargeo 121 | -------------------------------------------------------------------------------- /include/pargeo/pointIO.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 | #pragma once 24 | #include "parlay/parallel.h" 25 | #include "parlay/primitives.h" 26 | #include "IO.h" 27 | 28 | namespace pargeo { 29 | 30 | namespace pointIO { 31 | 32 | std::string pbbsHeader(int dim) { 33 | if (dim < 2 || dim > 9) { 34 | throw std::runtime_error("Error, unsupported dimension"); 35 | } 36 | return "pbbs_sequencePoint" + std::to_string(dim) + "d"; 37 | } 38 | 39 | bool isGenericHeader(std::string line) { 40 | for (auto c: line) { 41 | if (!pargeo::IO::is_number(c) && !pargeo::IO::is_delim(c)) return true; 42 | } 43 | return false; 44 | } 45 | 46 | int countEntry(std::string line) { 47 | while (pargeo::IO::is_delim(line.back()) || 48 | pargeo::IO::is_space(line.back()) || 49 | pargeo::IO::is_newline(line.back())) { 50 | line.pop_back(); 51 | } 52 | 53 | int count = 0; 54 | for (auto c: line) { 55 | if (pargeo::IO::is_delim(c)) count ++; 56 | } 57 | return count + 1; 58 | } 59 | 60 | // returns dim 61 | int readHeader(const char* fileName) { 62 | std::ifstream file (fileName); 63 | if (!file.is_open()) 64 | throw std::runtime_error("Unable to open file"); 65 | 66 | std::string line1; std::getline(file, line1); 67 | if (isGenericHeader(line1)) { 68 | std::string line2; std::getline(file, line2); 69 | return countEntry(line2); 70 | } else { 71 | return countEntry(line1); 72 | } 73 | } 74 | 75 | // todo deprecate 76 | int readDimensionFromFile(char* const fileName) { 77 | std::cout << "warning: using deprecated function readDimensionFromFile\n"; 78 | return readHeader(fileName); 79 | } 80 | 81 | template 82 | int writePointsToFile(parlay::sequence const &P, char const *fname) { 83 | int r = pargeo::IO::writeSeqToFile("", P, fname); 84 | return r; 85 | } 86 | 87 | template 88 | int writePointsToFilePbbs(parlay::sequence const &P, char const *fname) { 89 | std::string Header = pbbsHeader(pointT::dim); 90 | int r = pargeo::IO::writeSeqToFile(Header, P, fname); 91 | return r; 92 | } 93 | 94 | template 95 | parlay::sequence parsePoints(Seq W) { 96 | using coord = double; 97 | int d = pointT::dim; 98 | size_t n = W.size()/d; 99 | auto a = parlay::tabulate(d * n, [&] (size_t i) -> coord { 100 | return atof(W[i]);}); 101 | auto points = parlay::tabulate(n, [&] (size_t i) -> pointT { 102 | return pointT(a.cut(d*i,d*(i + 1)));}); 103 | return points; 104 | } 105 | 106 | template 107 | parlay::sequence readPointsFromFile(char const *fname) { 108 | parlay::sequence S = pargeo::IO::readStringFromFile(fname); 109 | parlay::sequence W = pargeo::IO::stringToWords(S); 110 | int d = pointT::dim; 111 | if (W.size() == 0) 112 | throw std::runtime_error("readPointsFromFile empty file"); 113 | 114 | if (isGenericHeader(W[0])) 115 | return parsePoints(W.cut(1,W.size())); 116 | else 117 | return parsePoints(W.cut(0,W.size())); 118 | } 119 | 120 | } // End namespace pointIO 121 | } // End namespace pargeo 122 | -------------------------------------------------------------------------------- /include/pargeo/bccp.h: -------------------------------------------------------------------------------- 1 | // This code is part of the project "Fast Parallel Algorithms for Euclidean 2 | // Minimum Spanning Tree and Hierarchical Spatial Clustering" 3 | // Copyright (c) 2021 Yiqiu Wang, Shangdi Yu, Yan Gu, Julian Shun 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a 6 | // copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights (to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included 14 | // in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | #pragma once 25 | 26 | #include "limits.h" 27 | #include 28 | 29 | namespace pargeo { 30 | 31 | using namespace std; 32 | 33 | namespace bccpInternal { 34 | 35 | template 36 | struct bcp { 37 | using floatT = typename objT::floatT; 38 | 39 | objT* u; 40 | objT* v; 41 | floatT dist; 42 | 43 | bcp(): u(NULL), v(NULL), dist(std::numeric_limits::max()) {} 44 | 45 | void update(objT* _u, objT* _v, floatT _dist) { 46 | if (_dist < dist) { 47 | u = _u; v = _v; dist = _dist; 48 | } 49 | } 50 | }; 51 | 52 | template 53 | bcp bcpBruteforce(nodeT* n1, nodeT* n2) { 54 | bcp r; 55 | for (size_t i = 0; i < n1->size(); ++ i) { 56 | for (size_t j = 0; j < n2->size(); ++ j) { 57 | double tmp = n1->at(i)->dist( *(n2->at(j)) ); 58 | r.update(n1->at(i), n2->at(j), tmp); 59 | } 60 | } 61 | return r; 62 | } 63 | 64 | template 65 | inline void bcpHelper(nodeT* n1, nodeT* n2, bcp* r) { 66 | if (nodeDistance(n1, n2) > r->dist) return; 67 | 68 | if (n1->isLeaf() && n2->isLeaf()) { 69 | 70 | for (size_t i=0; isize(); ++i) { 71 | for (size_t j=0; jsize(); ++j) { 72 | r->update(n1->at(i), n2->at(j), 73 | n1->at(i)->dist(*n2->at(j))); 74 | } 75 | } 76 | 77 | } else { 78 | 79 | if (n1->isLeaf()) { 80 | 81 | if (nodeDistance(n1, n2->L()) < nodeDistance(n1, n2->R())) { 82 | bcpHelper(n1, n2->L(), r); bcpHelper(n1, n2->R(), r); 83 | } else { 84 | bcpHelper(n1, n2->R(), r); bcpHelper(n1, n2->L(), r); 85 | } 86 | 87 | } else if (n2->isLeaf()) { 88 | 89 | if (nodeDistance(n2, n1->L()) < nodeDistance(n2, n1->R())) { 90 | bcpHelper(n1->L(), n2, r); bcpHelper(n1->R(), n2, r); 91 | } else { 92 | bcpHelper(n1->R(), n2, r); bcpHelper(n1->L(), n2, r); 93 | } 94 | 95 | } else { 96 | 97 | pair ordering[4]; //todo change to tuple 98 | ordering[0] = make_pair(n1->L(), n2->L()); 99 | ordering[1] = make_pair(n1->L(), n2->R()); 100 | ordering[2] = make_pair(n1->R(), n2->L()); 101 | ordering[3] = make_pair(n1->R(), n2->R()); 102 | 103 | auto cmp = [&](pair p1, pair p2) { 104 | return nodeDistance(p1.first, p1.second) < nodeDistance(p2.first, p2.second);}; 105 | sort(ordering, ordering + 4, cmp); 106 | 107 | for (int o=0; o<4; ++o) { 108 | bcpHelper(ordering[o].first, ordering[o].second, r);} 109 | 110 | } 111 | 112 | } 113 | } 114 | 115 | } // End namespace bcpInternal 116 | 117 | template 118 | tuple bccp(nodeT* n1, nodeT* n2) { 121 | using namespace bccpInternal; 122 | using floatT = double; 123 | 124 | auto r = bcp(); 125 | 126 | bcpHelper(n1, n2, &r); 127 | 128 | // auto verify = bcpBruteforce(n1, n2); 129 | // if (r.u != verify.u || r.v != verify.v) { 130 | // throw std::runtime_error("bcp wrong"); 131 | // } 132 | return tuple(r.u, r.v, r.dist); 133 | } 134 | 135 | } // End namespace 136 | -------------------------------------------------------------------------------- /include/pargeo/speculativeFor.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 | #include "parlay/parallel.h" 24 | #include "parlay/primitives.h" 25 | #include 26 | 27 | namespace pargeo { 28 | 29 | // idxT should be able to represent the range of iterations 30 | // int OK for up to 2^31 iterations 31 | // unsigned OK if freeze not used 32 | template 33 | struct reservation { 34 | std::atomic r; 35 | static constexpr idxT max_idx = std::numeric_limits::max(); 36 | reservation() : r(max_idx) {} 37 | idxT get() const { return r.load();} 38 | bool reserve(idxT i) { return parlay::write_min(&r, i, std::less());} 39 | bool reserved() const { return (r.load() < max_idx);} 40 | void reset() {r = max_idx;} 41 | void freeze() {r = -1;} 42 | bool check(idxT i) const { return (r.load() == i);} 43 | bool checkReset(idxT i) { 44 | if (r==i) { r = max_idx; return 1;} 45 | else return 0; 46 | } 47 | }; 48 | 49 | template 50 | long speculative_for(S step, idxT s, idxT e, long granularity, 51 | bool hasState=1, long maxTries=-1) { 52 | if (maxTries < 0) maxTries = 100 + 200*granularity; 53 | long maxRoundSize = max((long) 4, (e-s)/granularity+1); 54 | long currentRoundSize = maxRoundSize/4; 55 | // integer types, do not need to be initialized 56 | auto I = parlay::sequence::uninitialized(maxRoundSize); 57 | auto keep = parlay::sequence::uninitialized(maxRoundSize); 58 | parlay::sequence Ihold; // initially empty 59 | parlay::sequence state; 60 | if (hasState) 61 | state = parlay::tabulate(maxRoundSize, [&] (size_t i) -> S {return step;}); 62 | 63 | long round = 0; 64 | long numberDone = s; // number of iterations done 65 | long numberKeep = 0; // number of iterations to carry to next round 66 | long totalProcessed = 0; // number done including wasteds tries 67 | 68 | while (numberDone < e) { 69 | if (round++ > maxTries) 70 | throw std::runtime_error("speculative_for: too many iterations, increase maxTries"); 71 | long size = std::min(currentRoundSize, e - numberDone); 72 | 73 | totalProcessed += size; 74 | size_t loop_granularity = 0; 75 | 76 | if (hasState) { 77 | parlay::parallel_for (0, size, [&] (size_t i) { 78 | I[i] = (i < numberKeep) ? Ihold[i] : numberDone + i; 79 | keep[i] = state[i].reserve(I[i]); 80 | }, loop_granularity); 81 | } else { 82 | parlay::parallel_for (0, size, [&] (size_t i) { 83 | I[i] = (i < numberKeep) ? Ihold[i] : numberDone + i; 84 | keep[i] = step.reserve(I[i]); 85 | }, loop_granularity); 86 | } 87 | 88 | if (hasState) { 89 | parlay::parallel_for (0, size, [&] (size_t i) { 90 | if (keep[i]) keep[i] = !state[i].commit(I[i]);}, loop_granularity); 91 | } else { 92 | parlay::parallel_for (0, size, [&] (size_t i) { 93 | if (keep[i]) keep[i] = !step.commit(I[i]);}, loop_granularity); 94 | } 95 | 96 | // keep iterations that failed for next round 97 | Ihold = parlay::pack(I.head(size), keep.head(size)); 98 | numberKeep = Ihold.size(); 99 | numberDone += size - numberKeep; 100 | 101 | /* std::cout << size << " : " << numberKeep << " : " */ 102 | /* << numberDone << " : " << currentRoundSize << std::endl; */ 103 | 104 | // adjust round size based on number of failed attempts 105 | if (float(numberKeep)/float(size) > .2) 106 | currentRoundSize = std::max(currentRoundSize/2, 107 | std::max(maxRoundSize/64 + 1, numberKeep)); 108 | else if (float(numberKeep)/float(size) < .1) 109 | currentRoundSize = std::min(currentRoundSize * 2, maxRoundSize); 110 | } 111 | return totalProcessed; 112 | } 113 | 114 | } // End namespace pargeo 115 | -------------------------------------------------------------------------------- /include/pargeo/unionFind.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 | #pragma once 24 | 25 | #include "parlay/primitives.h" 26 | #include "parlay/parallel.h" 27 | #include "pargeo/atomics.h" 28 | #include "pargeo/edge.h" 29 | 30 | namespace pargeo { 31 | 32 | // The following supports both "union" that is only safe sequentially 33 | // and "link" that is safe in parallel. Find is always safe in parallel. 34 | // See: "Internally deterministic parallel algorithms can be fast" 35 | // Blelloch, Fineman, Gibbons, and Shun 36 | // for a discussion of link/find. 37 | template 38 | struct unionFind { 39 | parlay::sequence parents; 40 | 41 | bool is_root(vertexId u) { 42 | return parents[u] == (vertexId)-1;} 43 | 44 | // initialize n elements all as roots 45 | unionFind(size_t n) { 46 | parents = parlay::sequence(n, -1);} 47 | 48 | vertexId find(vertexId i) { 49 | if (is_root(i)) return i; 50 | vertexId p = parents[i]; 51 | if (is_root(p)) return p; 52 | 53 | // find root, shortcutting along the way 54 | do { 55 | vertexId gp = parents[p]; 56 | parents[i] = gp; 57 | i = p; 58 | p = gp; 59 | } while (!is_root(p)); 60 | return p; 61 | } 62 | 63 | // If using "union" then "parents" are used both as 64 | // parent pointer and for rank (a bit of a hack). 65 | // When a vertex is a root (negative) then the magnitude 66 | // of the negative number is its rank. 67 | // Otherwise it is the vertexId of its parent. 68 | // cannot be called union since reserved in C 69 | void union_roots(vertexId u, vertexId v) { 70 | if (parents[v] < parents[u]) std::swap(u,v); 71 | // now u has higher rank (higher negative number) 72 | parents[u] += parents[v]; // update rank of root 73 | parents[v] = u; // update parent of other tree 74 | } 75 | 76 | // Version of union that is safe for parallelism 77 | // when no cycles are created (e.g. only link from larger 78 | // to smaller vertexId). 79 | // Does not use ranks. 80 | void link(vertexId u, vertexId v) { 81 | parents[u] = v;} 82 | 83 | // returns true if successful 84 | bool tryLink(vertexId u, vertexId v) { 85 | return (parents[u] == -1 && 86 | pargeo::atomic_compare_and_swap(&parents[u], -1, v)); 87 | } 88 | }; 89 | 90 | // The following supports both "union" that is only safe sequentially 91 | // and "link" that is safe in parallel. Find is always safe in parallel. 92 | // See: "Internally deterministic parallel algorithms can be fast" 93 | // Blelloch, Fineman, Gibbons, and Shun 94 | // for a discussion of link/find. 95 | template 96 | struct edgeUnionFind { 97 | parlay::sequence parents; 98 | parlay::sequence edges; 99 | 100 | bool is_root(vertexId u) { 101 | return parents[u] == (vertexId)-1;} 102 | 103 | // initialize n elements all as roots 104 | edgeUnionFind(size_t n) { 105 | parents = parlay::sequence(n, -1); 106 | edges = parlay::sequence(n, wghEdge()); 107 | } 108 | 109 | vertexId find(vertexId i) { 110 | if (is_root(i)) return i; 111 | vertexId p = parents[i]; 112 | if (is_root(p)) return p; 113 | 114 | // find root, shortcutting along the way 115 | do { 116 | vertexId gp = parents[p]; 117 | parents[i] = gp; 118 | i = p; 119 | p = gp; 120 | } while (!is_root(p)); 121 | return p; 122 | } 123 | 124 | // Version of union that is safe for parallelism 125 | // when no cycles are created (e.g. only link from larger 126 | // to smaller vertexId). 127 | // Does not use ranks. 128 | void link(vertexId u, vertexId v, 129 | vertexId uReal, vertexId vReal, double weight) { 130 | edges[u] = wghEdge(uReal, vReal, weight); 131 | parents[u] = v;} 132 | 133 | size_t numEdge() { 134 | return parlay::count_if(make_slice(edges), [&](wghEdge e) { 135 | return !e.isEmpty(); 136 | }); 137 | } 138 | 139 | parlay::sequence getEdge() { 140 | return parlay::filter(edges, [&](wghEdge e){return !e.isEmpty();}); 141 | } 142 | }; 143 | 144 | } // End namespace 145 | -------------------------------------------------------------------------------- /include/pargeo/parBuf.h: -------------------------------------------------------------------------------- 1 | // This code is part of the project "Fast Parallel Algorithms for Euclidean 2 | // Minimum Spanning Tree and Hierarchical Spatial Clustering" 3 | // Copyright (c) 2021 Yiqiu Wang, Shangdi Yu, Yan Gu, Julian Shun 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a 6 | // copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights (to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included 14 | // in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | #pragma once 25 | 26 | namespace pargeo { 27 | 28 | template struct parBuf { 29 | using intT = size_t; 30 | 31 | // current buffer 32 | T *m_arr; 33 | intT m_currentSize; 34 | intT m_count; 35 | 36 | // array of buffers 37 | T **m_parent; 38 | intT *m_parentSizes; 39 | intT m_parentUsed; 40 | intT m_parentTotal; 41 | const intT m_defaultParent = 10; 42 | 43 | parBuf(intT t_initSize) { 44 | m_arr = (T *) malloc(sizeof(T) *t_initSize); 45 | m_parent = (T **) malloc(sizeof(T*) *m_defaultParent); 46 | m_parentSizes = (intT *) malloc(sizeof(intT) *(m_defaultParent+1)); 47 | 48 | m_currentSize = t_initSize; 49 | m_count = 0; 50 | 51 | m_parent[0] = m_arr; 52 | m_parentSizes[0] = t_initSize; 53 | 54 | m_parentUsed = 1; 55 | m_parentTotal = m_defaultParent; 56 | } 57 | 58 | intT size() { 59 | intT tmp = 0; 60 | for(intT i = 0; i < m_parentUsed-1; ++ i) { 61 | //cout << "size: parent " << i << ": " << m_parentSizes[i] << endl; 62 | tmp += m_parentSizes[i]; 63 | } 64 | //cout << "size: last parent " << ": " << m_count << endl; 65 | tmp += m_count; 66 | return tmp; 67 | } 68 | 69 | intT incrementParent() { 70 | T **parent1 = (T **) malloc(sizeof(T) * m_parentTotal * 2); 71 | intT *parentSizes1 = (intT *) malloc(sizeof(intT) * (m_parentTotal * 2+1)); 72 | 73 | for(intT i = 0; i < m_parentUsed; ++ i) { 74 | parent1[i] = m_parent[i]; 75 | parentSizes1[i] = m_parentSizes[i]; 76 | } 77 | 78 | free(m_parent); 79 | free(m_parentSizes); 80 | 81 | m_parent = parent1; 82 | m_parentSizes = parentSizes1; 83 | m_parentTotal *= 2; 84 | return m_parentUsed ++; 85 | } 86 | 87 | inline void finalize() { 88 | m_parentSizes[m_parentUsed-1] = m_count; 89 | } 90 | 91 | T* increment() { 92 | if (m_count + 1 > m_currentSize) { 93 | // book keeping for current arr 94 | finalize(); 95 | 96 | // allocate new arr 97 | T *arr1 = (T *) malloc(sizeof(T) * m_currentSize * 2); 98 | 99 | if (m_parentUsed < m_parentTotal) { 100 | m_parent[m_parentUsed++] = arr1; 101 | } else { 102 | m_parent[incrementParent()] = arr1; 103 | } 104 | 105 | m_arr = arr1; 106 | m_count = 0; 107 | m_currentSize *= 2; 108 | } 109 | return m_arr + (m_count++); 110 | } 111 | 112 | ~parBuf() { 113 | for (intT i = 0; i < m_parentUsed; ++ i) { 114 | free(m_parent[i]); 115 | } 116 | 117 | free(m_parent); 118 | } 119 | }; 120 | 121 | template 122 | T prefixSumSerial(T* data, size_t s, size_t e) { 123 | T res = 0; 124 | for (size_t i = s; i < e; ++i) { 125 | res += data[i]; 126 | data[i] = res - data[i]; 127 | } 128 | return res; 129 | } 130 | 131 | template 132 | parlay::sequence parBufCollect(parBuf **t_threadVecs, size_t P) { 133 | using namespace parlay; 134 | using intT = size_t; 135 | 136 | parlay::sequence vecSizes(P); 137 | for(int p = 0; p < P; ++ p) { 138 | t_threadVecs[p]->finalize(); 139 | vecSizes[p] = t_threadVecs[p]->size(); 140 | } 141 | 142 | intT total = scan_inplace(make_slice(vecSizes)); 143 | 144 | T* all= (T*) malloc(sizeof(T) * total); 145 | 146 | parallel_for(0, P, 147 | [&](intT p) { 148 | auto buf = t_threadVecs[p]; 149 | intT threadTotal = prefixSumSerial(buf->m_parentSizes, 0, buf->m_parentUsed); 150 | buf->m_parentSizes[buf->m_parentUsed] = threadTotal; // there's 1 extra space at the end of array 151 | 152 | parallel_for (0, buf->m_parentUsed, [&](intT parent) { 153 | for (intT elem = 0; elem < buf->m_parentSizes[parent+1]-buf->m_parentSizes[parent]; ++ elem) { 154 | all[vecSizes[p] + buf->m_parentSizes[parent] + elem] = buf->m_parent[parent][elem]; 155 | }}, 1); 156 | }, 1); 157 | 158 | return sequence(all, all + total); 159 | } 160 | 161 | } // End namespace 162 | -------------------------------------------------------------------------------- /include/pargeo/kdTreeRange.h: -------------------------------------------------------------------------------- 1 | // This code is part of the project "Fast Parallel Algorithms for Euclidean 2 | // Minimum Spanning Tree and Hierarchical Spatial Clustering" 3 | // Copyright (c) 2021 Yiqiu Wang, Shangdi Yu, Yan Gu, Julian Shun 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a 6 | // copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights (to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included 14 | // in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | #pragma once 25 | 26 | #include "parlay/parallel.h" 27 | #include "parlay/sequence.h" 28 | #include "kdTree.h" 29 | #include "point.h" 30 | 31 | namespace pargeo { 32 | using namespace parlay; 33 | 34 | template 35 | void rangeHelper(nodeT* tree, objT& q, point qMin, point qMax, 36 | double radius, sequence& out, objT* A) { 37 | int relation = tree->boxCompare(qMin, qMax, tree->getMin(), tree->getMax()); 38 | 39 | if(relation == tree->boxExclude) { 40 | return; 41 | } else if (relation == tree->boxInclude) { 42 | for (size_t i = 0; i < tree->size(); ++i) { 43 | objT* p = tree->getItem(i); 44 | out.push_back(p - A); 45 | } 46 | } else { // intersect 47 | if (tree->isLeaf()) { 48 | for (size_t i = 0; i < tree->size(); ++ i) { 49 | objT* p = tree->getItem(i); 50 | double dist = q.dist(*p); 51 | if (dist <= radius) 52 | out.push_back(p - A); 53 | } 54 | } else { 55 | rangeHelper(tree->L(), q, qMin, qMax, radius, out, A); 56 | rangeHelper(tree->R(), q, qMin, qMax, radius, out, A); 57 | } 58 | } 59 | } 60 | 61 | template 62 | sequence kdTreeRange(sequence& A, kdNode* tree, 63 | objT query, double radius) { 64 | auto out = parlay::sequence(); 65 | point qMin, qMax; 66 | for (size_t i=0; i, objT>(tree, query, qMin, qMax, 72 | radius, out, A.data()); 73 | return out; 74 | } 75 | 76 | template 77 | sequence bruteforceRange(sequence& elems, objT query, double radius) { 78 | auto out = parlay::sequence(); 79 | auto flag = parlay::sequence(elems.size(), elems.size()); 80 | parallel_for(0, elems.size(), [&](size_t i) { 81 | if (elems[i].dist(query) <= radius) 82 | flag[i] = i; 83 | }); 84 | return parlay::filter(make_slice(flag), [&](size_t i) { 85 | return i < elems.size(); 86 | }); 87 | } 88 | 89 | template 90 | void orthRangeHelper(nodeT* tree, point qMin, point qMax, 91 | sequence& out, objT* A) { 92 | int relation = tree->boxCompare(qMin, qMax, tree->getMin(), tree->getMax()); 93 | 94 | if(relation == tree->boxExclude) { 95 | return; 96 | } else if (relation == tree->boxInclude) { 97 | for (size_t i = 0; i < tree->size(); ++i) { 98 | objT* p = tree->getItem(i); 99 | out.push_back(p - A); 100 | } 101 | } else { // intersect 102 | if (tree->isLeaf()) { 103 | for (size_t i = 0; i < tree->size(); ++ i) { 104 | objT *p = tree->getItem(i); 105 | objT _p = *p; 106 | bool in = true; 107 | for (int d = 0; d < dim; ++ d) { 108 | if (_p[d] > qMax[d] || _p[d] < qMin[d]) 109 | in = false; 110 | } 111 | if (in) 112 | out.push_back(p - A); 113 | } 114 | } else { 115 | orthRangeHelper(tree->L(), qMin, qMax, out, A); 116 | orthRangeHelper(tree->R(), qMin, qMax, out, A); 117 | } 118 | } 119 | } 120 | 121 | template 122 | sequence kdTreeOrthRange(sequence& A, kdNode* tree, 123 | objT query, double halfLen) { 124 | auto out = parlay::sequence(); 125 | point qMin, qMax; 126 | for (size_t i=0; i, objT>(tree, qMin, qMax, 132 | out, A.data()); 133 | return out; 134 | } 135 | 136 | template 137 | sequence bruteforceOrthRange(sequence& A, 138 | objT query, double halfLen) { 139 | auto out = parlay::sequence(); 140 | point qMin, qMax; 141 | for (size_t i=0; i qMax[d] || A[i][d] < qMin[d]) 151 | in = false; 152 | } 153 | if (in) out.push_back(i); 154 | }); 155 | return out; 156 | } 157 | 158 | } // End namespace 159 | -------------------------------------------------------------------------------- /include/pargeo/zorderSort.h: -------------------------------------------------------------------------------- 1 | // This code is part of the "Pargeo" project 2 | // Copyright (c) 2020 Yiqiu Wang and the Pargeo 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 | #pragma once 24 | 25 | #include "parlay/parallel.h" 26 | #include "parlay/sequence.h" 27 | #include "point.h" 28 | #include 29 | 30 | namespace pargeo { 31 | 32 | using namespace parlay; 33 | using namespace std; 34 | 35 | template 36 | parlay::sequence zorderSort2d(parlay::sequence& P) { 37 | 38 | using floatT = typename pt::floatT; 39 | 40 | static constexpr size_t maxRange = 65535; 41 | static constexpr size_t maxBit = 16; // 2**maxBit = maxRange 42 | 43 | auto shift = [&](size_t x) { 44 | if (x > maxRange) { 45 | cout << x << endl; 46 | throw std::runtime_error("zorder range error"); 47 | } 48 | 49 | x = (x | (x << 16)) & 0x001f0000ff0000ff; 50 | x = (x | (x << 8)) & 0x100f00f00f00f00f; 51 | x = (x | (x << 4)) & 0x10c30c30c30c30c3; 52 | x = (x | (x << 2)) & 0x1249249249249249; 53 | return x; 54 | }; 55 | 56 | auto zorder = [&](pt p, point<2> pMin, floatT boxSize) { 57 | size_t x = floor( (p[0] - pMin[0]) / boxSize ); 58 | size_t y = floor( (p[1] - pMin[1]) / boxSize ); 59 | 60 | x = shift(x); 61 | y = shift(y); 62 | return x | (y << 1); 63 | }; 64 | 65 | std::atomic extrema[4]; 66 | 67 | for (int i=0; i<2; ++i) { 68 | extrema[i*2] = P[0][i]; 69 | extrema[i*2+1] = P[0][i]; 70 | } 71 | 72 | parallel_for(0, P.size(), [&](size_t i){ 73 | write_max(&extrema[0], P[i][0], std::less()); 74 | write_min(&extrema[1], P[i][0], std::less()); 75 | write_max(&extrema[2], P[i][1], std::less()); 76 | write_min(&extrema[3], P[i][1], std::less()); 77 | }); 78 | 79 | floatT maxSpan = max((extrema[2]-extrema[3]),(extrema[0]-extrema[1])); 80 | 81 | floatT boxSize = 1.01 * maxSpan / maxRange; 82 | 83 | point<2> pMin; 84 | pMin[0] = extrema[1]; 85 | pMin[1] = extrema[3]; 86 | 87 | using ip = tuple; 88 | sequence pairs = tabulate(P.size(), [&](size_t i){ 89 | return tuple(zorder(P[i], pMin, boxSize), P[i]); 90 | }); 91 | 92 | parlay::sort_inplace(make_slice(pairs), [&](ip i, ip j){ 93 | return get<0>(i) < get<0>(j); 94 | }); 95 | 96 | return tabulate(pairs.size(), [&](size_t i){ 97 | return get<1>(pairs[i]); 98 | }); 99 | } 100 | 101 | template 102 | parlay::sequence zorderSort3d(parlay::sequence& P) { 103 | 104 | using floatT = typename pt::floatT; 105 | 106 | static constexpr size_t maxRange = 65535; 107 | static constexpr size_t maxBit = 16; // 2**maxBit = maxRange 108 | 109 | auto shift = [&](size_t x) { 110 | if (x > maxRange) { 111 | cout << x << endl; 112 | throw std::runtime_error("zorder range error"); 113 | } 114 | 115 | x = (x | (x << 16)) & 0x001f0000ff0000ff; 116 | x = (x | (x << 8)) & 0x100f00f00f00f00f; 117 | x = (x | (x << 4)) & 0x10c30c30c30c30c3; 118 | x = (x | (x << 2)) & 0x1249249249249249; 119 | return x; 120 | }; 121 | 122 | auto zorder = [&](pt p, point<3> pMin, floatT boxSize) { 123 | size_t x = floor( (p[0] - pMin[0]) / boxSize ); 124 | size_t y = floor( (p[1] - pMin[1]) / boxSize ); 125 | size_t z = floor( (p[2] - pMin[2]) / boxSize ); 126 | 127 | x = shift(x); 128 | y = shift(y); 129 | z = shift(z); 130 | return x | (y << 1) | (z << 2); 131 | }; 132 | 133 | std::atomic extrema[6]; 134 | 135 | for (int i=0; i<3; ++i) { 136 | extrema[i*2] = P[0][i]; 137 | extrema[i*2+1] = P[0][i]; 138 | } 139 | 140 | parallel_for(0, P.size(), [&](size_t i){ 141 | write_max(&extrema[0], P[i][0], std::less()); 142 | write_min(&extrema[1], P[i][0], std::less()); 143 | write_max(&extrema[2], P[i][1], std::less()); 144 | write_min(&extrema[3], P[i][1], std::less()); 145 | write_max(&extrema[4], P[i][2], std::less()); 146 | write_min(&extrema[5], P[i][2], std::less()); 147 | }); 148 | 149 | floatT maxSpan = max(extrema[4]-extrema[5], 150 | max((extrema[2]-extrema[3]),(extrema[0]-extrema[1]))); 151 | 152 | floatT boxSize = 1.01 * maxSpan / maxRange; 153 | 154 | point<3> pMin; 155 | pMin[0] = extrema[1]; 156 | pMin[1] = extrema[3]; 157 | pMin[2] = extrema[5]; 158 | 159 | using ip = tuple; 160 | sequence pairs = tabulate(P.size(), [&](size_t i){ 161 | return tuple(zorder(P[i], pMin, boxSize), P[i]); 162 | }); 163 | 164 | parlay::sort_inplace(make_slice(pairs), [&](ip i, ip j){ 165 | return get<0>(i) < get<0>(j); 166 | }); 167 | 168 | return tabulate(pairs.size(), [&](size_t i){ 169 | return get<1>(pairs[i]); 170 | }); 171 | } 172 | 173 | template 174 | void zorderSortInPlace2d(parlay::sequence& P) { 175 | P = zorderSort2d(P); 176 | } 177 | 178 | template 179 | void zorderSortInPlace3d(parlay::sequence& P) { 180 | P = zorderSort3d(P); 181 | } 182 | 183 | 184 | } // End namespace 185 | -------------------------------------------------------------------------------- /include/pargeo/graphIO.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "parlay/primitives.h" 9 | #include "parlay/parallel.h" 10 | #include "parlay/io.h" 11 | 12 | namespace pargeo { 13 | namespace graphIO { 14 | using namespace std; 15 | using parlay::sequence; 16 | using parlay::tabulate; 17 | using parlay::make_slice; 18 | 19 | inline int xToStringLen(unsigned long a) { return 21;} 20 | inline void xToString(char* s, unsigned long a) { sprintf(s,"%lu",a);} 21 | 22 | inline int xToStringLen(double a) { return 18;} 23 | inline void xToString(char* s, double a) { sprintf(s,"%.11le", a);} 24 | 25 | string emptyHeaderIO = ""; 26 | 27 | /////////////////////////////////////////////////////////////////// 28 | // Write sequence of weighted edges (u,v) to SNAP edge list 29 | /////////////////////////////////////////////////////////////////// 30 | 31 | template 32 | parlay::sequence wghEdgeSeqToString(Seq const &edges) { 33 | parlay::sequence A(edges.size()*2); 34 | parlay::sequence W(edges.size()); 35 | parlay::parallel_for(0, edges.size(), [&](size_t i){ 36 | A[i*2] = edges[i].u; 37 | A[i*2+1] = edges[i].v; 38 | W[i] = edges[i].weight; 39 | }); 40 | 41 | size_t n = A.size() + W.size(); 42 | auto L = parlay::tabulate(n, [&] (size_t i) -> long { 43 | size_t e = i / 3; 44 | size_t ii = i % 3; 45 | if (ii < 2) { 46 | size_t x = A[e*2 + ii]; 47 | return xToStringLen(x)+1; 48 | } else { 49 | double x = W[e]; 50 | return xToStringLen(x)+1; 51 | } 52 | }); 53 | size_t m; 54 | std::tie(L,m) = parlay::scan(std::move(L)); 55 | 56 | parlay::sequence B(m+1, (char) 0); 57 | char* Bs = B.begin(); 58 | 59 | parlay::parallel_for(0, edges.size()-1, [&] (size_t i) { 60 | xToString(Bs + L[i*3], A[i*2]); 61 | Bs[L[i*3+1] - 1] = ' '; 62 | xToString(Bs + L[i*3+1], A[i*2+1]); 63 | Bs[L[i*3+2] - 1] = ' '; 64 | xToString(Bs + L[i*3+2], W[i]); 65 | Bs[L[i*3+3] - 1] = '\n'; 66 | }); 67 | size_t i = edges.size() - 1; 68 | xToString(Bs + L[i*3], A[i*2]); 69 | Bs[L[i*3+1] - 1] = ' '; 70 | xToString(Bs + L[i*3+1], A[i*2+1]); 71 | Bs[L[i*3+2] - 1] = ' '; 72 | xToString(Bs + L[i*3+2], W[i]); 73 | Bs[m] = Bs[m-1] = '\n'; 74 | 75 | parlay::sequence C = parlay::filter(B, [&] (char c) {return c != 0;}); 76 | C[C.size()-1] = 0; 77 | return C; 78 | } 79 | 80 | template 81 | void writeWghEdgeSeqToStream(ofstream& os, parlay::sequence const &A) { 82 | size_t bsize = 10000000; 83 | size_t offset = 0; 84 | size_t n = A.size(); 85 | while (offset < n) { 86 | // Generates a string for a sequence of size at most bsize 87 | // and then wrties it to the output stream 88 | parlay::sequence S = wghEdgeSeqToString(A.cut(offset, min(offset + bsize, n))); 89 | os.write(S.begin(), S.size()-1); 90 | offset += bsize; 91 | } 92 | } 93 | 94 | template 95 | int writeWghEdgeSeqToFile(string header, 96 | parlay::sequence const &A, 97 | char const *fileName) { 98 | auto a = A[0]; 99 | 100 | ofstream file (fileName, ios::out | ios::binary); 101 | if (!file.is_open()) { 102 | std::cout << "Unable to open file: " << fileName << std::endl; 103 | return 1; 104 | } 105 | 106 | //file << header << endl; // not writing a header 107 | 108 | writeWghEdgeSeqToStream(file, A); 109 | file.close(); 110 | return 0; 111 | } 112 | 113 | template 114 | int writeWghEdgeSeqToFile(parlay::sequence const &A, char const *fileName) { 115 | return writeWghEdgeSeqToFile(emptyHeaderIO, A, fileName); 116 | } 117 | 118 | /////////////////////////////////////////////////////////////////// 119 | // Write sequence of unweighted edges (u,v) to SNAP edge list 120 | /////////////////////////////////////////////////////////////////// 121 | 122 | template 123 | parlay::sequence edgeSeqToString(Seq const &edges) { 124 | parlay::sequence A(edges.size()*2); 125 | parlay::parallel_for(0, edges.size(), [&](size_t i){ 126 | A[i*2] = edges[i].u; 127 | A[i*2+1] = edges[i].v; 128 | }); 129 | 130 | size_t n = A.size(); 131 | auto L = parlay::tabulate(n, [&] (size_t i) -> long { 132 | size_t x = A[i]; 133 | return xToStringLen(x)+1;}); 134 | size_t m; 135 | std::tie(L,m) = parlay::scan(std::move(L)); 136 | 137 | parlay::sequence B(m+1, (char) 0); 138 | char* Bs = B.begin(); 139 | 140 | parlay::parallel_for(0, n-1, [&] (long i) { 141 | xToString(Bs + L[i], A[i]); 142 | if (i % 2 == 1) 143 | Bs[L[i+1] - 1] = '\n'; 144 | else 145 | Bs[L[i+1] - 1] = ' '; 146 | }); 147 | xToString(Bs + L[n-1], A[n-1]); 148 | Bs[m] = Bs[m-1] = '\n'; 149 | 150 | parlay::sequence C = parlay::filter(B, [&] (char c) {return c != 0;}); 151 | C[C.size()-1] = 0; 152 | return C; 153 | } 154 | 155 | template 156 | void writeEdgeSeqToStream(ofstream& os, parlay::sequence const &A) { 157 | size_t bsize = 10000000; 158 | size_t offset = 0; 159 | size_t n = A.size(); 160 | while (offset < n) { 161 | // Generates a string for a sequence of size at most bsize 162 | // and then wrties it to the output stream 163 | parlay::sequence S = edgeSeqToString(A.cut(offset, min(offset + bsize, n))); 164 | os.write(S.begin(), S.size()-1); 165 | offset += bsize; 166 | } 167 | } 168 | 169 | template 170 | int writeEdgeSeqToFile(string header, 171 | parlay::sequence const &A, 172 | char const *fileName) { 173 | auto a = A[0]; 174 | 175 | ofstream file (fileName, ios::out | ios::binary); 176 | if (!file.is_open()) { 177 | std::cout << "Unable to open file: " << fileName << std::endl; 178 | return 1; 179 | } 180 | 181 | //file << header << endl; // not writing a header 182 | 183 | writeEdgeSeqToStream(file, A); 184 | file.close(); 185 | return 0; 186 | } 187 | 188 | template 189 | int writeEdgeSeqToFile(parlay::sequence const &A, char const *fileName) { 190 | return writeEdgeSeqToFile(emptyHeaderIO, A, fileName); 191 | } 192 | 193 | } // End namespace graphIO 194 | } // End namespace pargeo 195 | -------------------------------------------------------------------------------- /include/pargeo/kruskal.h: -------------------------------------------------------------------------------- 1 | // This code is part of the Problem Based Benchmark Suite (PBBS) 2 | // Copyright (c) 2011-2019 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 | /* 24 | Added a similar version of Kruskal's algorithm to process batches of edges 25 | for the pargeo project 26 | */ 27 | 28 | #pragma once 29 | 30 | #include 31 | #include 32 | #include "parlay/sequence.h" 33 | #include "parlay/parallel.h" 34 | #include "speculativeFor.h" 35 | #include "unionFind.h" 36 | 37 | // ************************************************************** 38 | // PARALLEL VERSION OF KRUSKAL'S ALGORITHM 39 | // ************************************************************** 40 | 41 | namespace pargeo { 42 | 43 | using namespace std; 44 | 45 | namespace kruskalInternal { 46 | using vertexId = long; 47 | using edgeId = long; 48 | using edgeWeight = double; 49 | 50 | // need to tag each edge with an index so we can keep track of 51 | // which edges are added to the MST 52 | struct indexedEdge { 53 | vertexId u; vertexId v; edgeId id; edgeWeight w; 54 | indexedEdge(vertexId u, vertexId v, edgeId id, edgeWeight w) 55 | : u(u), v(v), id(id), w(w){} 56 | indexedEdge() {}; 57 | }; 58 | 59 | using reservation = reservation; 60 | 61 | struct UnionFindStep { 62 | parlay::sequence &E; 63 | parlay::sequence &R; 64 | unionFind &UF; 65 | parlay::sequence &inST; 66 | UnionFindStep(parlay::sequence &E, 67 | unionFind &UF, 68 | parlay::sequence &R, 69 | parlay::sequence &inST) : 70 | E(E), R(R), UF(UF), inST(inST) {} 71 | 72 | bool reserve(edgeId i) { 73 | vertexId u = E[i].u = UF.find(E[i].u); 74 | vertexId v = E[i].v = UF.find(E[i].v); 75 | if (u != v) { 76 | R[v].reserve(i); 77 | R[u].reserve(i); 78 | return true; 79 | } else return false; 80 | } 81 | 82 | bool commit(edgeId i) { 83 | vertexId u = E[i].u; 84 | vertexId v = E[i].v; 85 | if (R[v].check(i)) { 86 | R[u].checkReset(i); 87 | UF.link(v, u); 88 | inST[E[i].id] = true; 89 | return true;} 90 | else if (R[u].check(i)) { 91 | UF.link(u, v); 92 | inST[E[i].id] = true; 93 | return true; } 94 | else return false; 95 | } 96 | }; 97 | 98 | // Union find step without MST mapping 99 | struct EdgeUnionFindStep { 100 | parlay::sequence &E; 101 | parlay::sequence EReal; 102 | parlay::sequence &R; 103 | edgeUnionFind &UF; 104 | EdgeUnionFindStep(parlay::sequence &E, 105 | edgeUnionFind &UF, 106 | parlay::sequence &R) : 107 | E(E), R(R), UF(UF) { 108 | EReal = parlay::sequence(E); 109 | } 110 | 111 | bool reserve(edgeId i) { 112 | vertexId u = E[i].u = UF.find(E[i].u); 113 | vertexId v = E[i].v = UF.find(E[i].v); 114 | if (u != v) { 115 | R[v].reserve(i); 116 | R[u].reserve(i); 117 | return true; 118 | } else return false; 119 | } 120 | 121 | bool commit(edgeId i) { 122 | vertexId u = E[i].u; 123 | vertexId v = E[i].v; 124 | vertexId uReal = EReal[i].u; 125 | vertexId vReal = EReal[i].v; 126 | if (R[v].check(i)) { 127 | R[u].checkReset(i); 128 | UF.link(v, u, vReal, uReal, EReal[i].w); 129 | return true;} 130 | else if (R[u].check(i)) { 131 | UF.link(u, v, uReal, vReal, EReal[i].w); 132 | return true; } 133 | else return false; 134 | } 135 | }; 136 | 137 | } // End namespace kruskalInternal 138 | 139 | template 140 | parlay::sequence kruskal(parlay::sequence &E, size_t n) { 141 | using namespace kruskalInternal; 142 | 143 | //timer t("mst", true); 144 | /* size_t m = E.m; */ 145 | size_t m = E.size(); 146 | /* size_t n = E.n; */ 147 | size_t k = min(5 * n / 4, m); 148 | 149 | // equal edge weights will prioritize the earliest one 150 | auto edgeLess = [&] (indexedEdge a, indexedEdge b) { 151 | return (a.w < b.w) || ((a.w == b.w) && (a.id < b.id));}; 152 | 153 | // tag each edge with an index 154 | auto IW = parlay::delayed_seq(m, [&] (size_t i) { 155 | return indexedEdge(E[i].u, E[i].v, i, E[i].weight);}); 156 | 157 | auto IW1 = parlay::sort(IW, edgeLess); 158 | //t.next("sort edges"); 159 | 160 | parlay::sequence mstFlags(m, false); 161 | unionFind UF(n); 162 | parlay::sequence R(n); 163 | UnionFindStep UFStep1(IW1, UF, R, mstFlags); 164 | speculative_for(UFStep1, 0, IW1.size(), 20, false); 165 | //t.next("union find loop"); 166 | 167 | parlay::sequence mst = parlay::pack_index(mstFlags); 168 | //t.next("pack out results"); 169 | 170 | return mst; 171 | } 172 | 173 | template 174 | void batchKruskal(parlay::sequence &E, 175 | size_t n, 176 | edgeUnionFind& UF) { 177 | 178 | using namespace kruskalInternal; 179 | 180 | size_t m = E.size(); 181 | size_t k = min(5 * n / 4, m); 182 | 183 | // equal edge weights will prioritize the earliest one 184 | auto edgeLess = [&] (indexedEdge a, indexedEdge b) { 185 | return (a.w < b.w) || ((a.w == b.w) && (a.id < b.id));}; 186 | 187 | // tag each edge with an index 188 | auto IW = parlay::delayed_seq(m, [&] (size_t i) { 189 | return indexedEdge(E[i].u, E[i].v, i, E[i].weight);}); 190 | 191 | auto IW1 = parlay::sort(IW, edgeLess); 192 | 193 | parlay::sequence R(n); 194 | EdgeUnionFindStep UFStep1(IW1, UF, R); 195 | speculative_for(UFStep1, 0, IW1.size(), 20, false); 196 | } 197 | } // End namespace pargeo 198 | -------------------------------------------------------------------------------- /src/delaunayTriangulation/incremental/neighbors.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 | #define report_stats false 24 | #include 25 | #include "parlay/parallel.h" 26 | #include "parlay/primitives.h" 27 | #include "delaunayTriangulation/geometry.h" 28 | #include "oct_tree.h" 29 | 30 | namespace pbbsbench { 31 | 32 | // A k-nearest neighbor structure 33 | // requires vertexT to have pointT and vectT typedefs 34 | template 35 | struct k_nearest_neighbors { 36 | using point = typename vtx::point_t; 37 | using fvect = typename point::vector; 38 | using o_tree = oct_tree; 39 | using node = typename o_tree::node; 40 | using tree_ptr = typename o_tree::tree_ptr; 41 | 42 | tree_ptr tree; 43 | 44 | // generates the search structure 45 | k_nearest_neighbors(parlay::sequence &V) { 46 | tree = o_tree::build(V); 47 | } 48 | 49 | // returns the vertices in the search structure, in an 50 | // order that has spacial locality 51 | parlay::sequence vertices() { 52 | return tree->flatten(); 53 | } 54 | 55 | struct kNN { 56 | vtx *vertex; // the vertex for which we are trying to find a NN 57 | vtx *neighbors[max_k]; // the current k nearest neighbors (nearest last) 58 | double distances[max_k]; // distance to current k nearest neighbors 59 | int k; 60 | int dimensions; 61 | size_t leaf_cnt; 62 | size_t internal_cnt; 63 | kNN() {} 64 | 65 | // returns the ith smallest element (0 is smallest) up to k-1 66 | vtx* operator[] (const int i) { return neighbors[k-i-1]; } 67 | 68 | kNN(vtx *p, int kk) { 69 | if (kk > max_k) { 70 | std::cout << "k too large in kNN" << std::endl; 71 | abort();} 72 | k = kk; 73 | vertex = p; 74 | dimensions = p->pt.dimension(); 75 | leaf_cnt = internal_cnt = 0; 76 | // initialize nearest neighbors to point to Null with 77 | // distance "infinity". 78 | for (int i=0; i::max(); 81 | } 82 | } 83 | 84 | // if p is closer than neighbors[0] then swap it in 85 | void update_nearest(vtx *other) { 86 | auto dist = (vertex->pt - other->pt).Length(); 87 | if (dist < distances[0]) { 88 | neighbors[0] = other; 89 | distances[0] = dist; 90 | for (int i = 1; 91 | i < k && distances[i-1] < distances[i]; 92 | i++) { 93 | swap(distances[i-1], distances[i]); 94 | swap(neighbors[i-1], neighbors[i]); } 95 | } 96 | } 97 | 98 | bool within_epsilon_box(node* T, double epsilon) { 99 | auto box = T->Box(); 100 | bool result = true; 101 | for (int i = 0; i < dimensions; i++) { 102 | result = (result && 103 | (box.first[i] - epsilon < vertex->pt[i]) && 104 | (box.second[i] + epsilon > vertex->pt[i])); 105 | } 106 | return result; 107 | } 108 | 109 | double distance(node* T) { 110 | return (T->center() - vertex->pt).Length(); 111 | } 112 | 113 | // looks for nearest neighbors for this->vertex in Tree node T 114 | void k_nearest_rec(node* T) { 115 | if (report_stats) internal_cnt++; 116 | if (within_epsilon_box(T, distances[0])) { 117 | if (T->is_leaf()) { 118 | if (report_stats) leaf_cnt++; 119 | auto &Vtx = T->Vertices(); 120 | for (int i = 0; i < T->size(); i++) 121 | if (Vtx[i] != vertex) update_nearest(Vtx[i]); 122 | } else if (distance(T->Left()) < distance(T->Right())) { 123 | k_nearest_rec(T->Left()); 124 | k_nearest_rec(T->Right()); 125 | } else { 126 | k_nearest_rec(T->Right()); 127 | k_nearest_rec(T->Left()); 128 | } 129 | } 130 | } 131 | 132 | // finds a point that is vaguely near 133 | void near_rec(node* T) { 134 | if (T->is_leaf()) { 135 | auto &Vtx = T->Vertices(); 136 | for (int i = 0; i < T->size(); i++) 137 | if (Vtx[i] != vertex) update_nearest(Vtx[i]); 138 | } else if (distance(T->Left()) < distance(T->Right())) { 139 | near_rec(T->Left()); 140 | } else { 141 | near_rec(T->Right()); 142 | } 143 | } 144 | 145 | }; 146 | 147 | void k_nearest(vtx *p, int k) { 148 | kNN nn(p,k); 149 | nn.k_nearest_rec(tree.get()); 150 | if (report_stats) p->counter = nn.internal_cnt; 151 | for (int i=0; i < k; i++) 152 | p->ngh[i] = nn[i]; 153 | } 154 | 155 | vtx* nearest(vtx *p) { 156 | kNN nn(p,1); 157 | nn.k_nearest_rec(tree.get()); 158 | if (report_stats) p->counter = nn.internal_cnt; 159 | return nn[0]; 160 | } 161 | 162 | vtx* near(vtx *p) { 163 | kNN nn(p,1); 164 | nn.near_rec(tree.get()); 165 | return nn[0]; 166 | } 167 | 168 | }; 169 | 170 | // find the k nearest neighbors for all points in tree 171 | // places pointers to them in the .ngh field of each vertex 172 | template 173 | void ANN(parlay::sequence &v, int k) { 174 | pargeo::timer t("ANN",report_stats); 175 | 176 | { 177 | using knn_tree = k_nearest_neighbors; 178 | knn_tree T(v); 179 | t.next("build tree"); 180 | 181 | if (report_stats) 182 | std::cout << "depth = " << T.tree->depth() << std::endl; 183 | 184 | // this reorders the vertices for locality 185 | parlay::sequence vr = T.vertices(); 186 | t.next("flatten tree"); 187 | 188 | // find nearest k neighbors for each point 189 | parlay::parallel_for (0, v.size(), [&] (size_t i) { 190 | T.k_nearest(vr[i], k);}, 1); 191 | 192 | t.next("try all"); 193 | if (report_stats) { 194 | auto s = parlay::delayed_seq(v.size(), [&] (size_t i) {return v[i]->counter;}); 195 | size_t i = parlay::max_element(s) - s.begin(); 196 | size_t sum = parlay::reduce(s); 197 | std::cout << "max internal = " << s[i] 198 | << ", average internal = " << sum/((double) v.size()) << std::endl; 199 | t.next("stats"); 200 | } 201 | } 202 | t.next("delete tree"); 203 | } 204 | 205 | } // End namespace pbbsbench 206 | -------------------------------------------------------------------------------- /include/pargeo/kdTreeKnn.h: -------------------------------------------------------------------------------- 1 | // This code is part of the project "Fast Parallel Algorithms for Euclidean 2 | // Minimum Spanning Tree and Hierarchical Spatial Clustering" 3 | // Copyright (c) 2021 Yiqiu Wang, Shangdi Yu, Yan Gu, Julian Shun 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a 6 | // copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights (to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included 14 | // in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | #pragma once 25 | 26 | #include // numeric_limits 27 | #include // nth_element 28 | #include "parlay/parallel.h" 29 | #include "parlay/sequence.h" 30 | #include "kdTree.h" 31 | #include "point.h" 32 | 33 | namespace pargeo { 34 | 35 | namespace knnBuf { 36 | 37 | typedef int intT; 38 | typedef double floatT; 39 | 40 | template 41 | struct elem { 42 | floatT cost;// Non-negative 43 | T entry; 44 | elem(floatT t_cost, T t_entry) : cost(t_cost), entry(t_entry) {} 45 | elem() : cost(std::numeric_limits::max()) {} 46 | bool operator<(const elem& b) const { 47 | if (cost < b.cost) return true; 48 | return false;} 49 | }; 50 | 51 | template 52 | struct buffer { 53 | typedef parlay::slice*, elem*> sliceT; 54 | intT k; 55 | intT ptr; 56 | sliceT buf; 57 | 58 | buffer(intT t_k, sliceT t_buf): k(t_k), ptr(0), buf(t_buf) {} 59 | 60 | inline void reset() {ptr = 0;} 61 | 62 | bool hasK() {return ptr >= k;} 63 | 64 | elem keepK() { 65 | if (ptr < k) throw std::runtime_error("Error, kbuffer not enough k."); 66 | ptr = k; 67 | std::nth_element(buf.begin(), buf.begin()+k-1, buf.end()); 68 | return buf[k-1]; 69 | } 70 | 71 | void sort() { // todo check 72 | if (ptr < k) throw std::runtime_error("Error, sorting kbuffer without enough k."); 73 | parlay::sort(buf.cut(0, k)); 74 | } 75 | 76 | void insert(elem t_elem) { 77 | buf[ptr++] = t_elem; 78 | if (ptr >= buf.size()) keepK(); 79 | } 80 | 81 | elem operator[](intT i) { 82 | if (i < ptr) return buf[i]; 83 | else return elem(); 84 | } 85 | }; 86 | } 87 | 88 | using namespace knnBuf; 89 | using namespace std; 90 | 91 | template 92 | void knnRangeHelper(nodeT* tree, objT& q, point qMin, point qMax, 93 | double radius, buffer& out) { 94 | int relation = tree->boxCompare(qMin, qMax, tree->getMin(), tree->getMax()); 95 | 96 | if(relation == tree->boxExclude) { 97 | return; 98 | } else if (relation == tree->boxInclude) { 99 | for (size_t i = 0; i < tree->size(); ++i) { 100 | objT* p = tree->getItem(i); 101 | out.insert(elem(q.dist(*p), p)); 102 | } 103 | } else { // intersect 104 | if (tree->isLeaf()) { 105 | for (size_t i = 0; i < tree->size(); ++ i) { 106 | objT* p = tree->getItem(i); 107 | double dist = q.dist(*p); 108 | if (dist <= radius) {out.insert(elem(dist, p));} 109 | } 110 | } else { 111 | knnRangeHelper(tree->L(), q, qMin, qMax, radius, out); 112 | knnRangeHelper(tree->R(), q, qMin, qMax, radius, out); 113 | } 114 | } 115 | } 116 | 117 | template 118 | void knnRange(nodeT* tree, objT& q, double radius, buffer& out) { 119 | point qMin, qMax; 120 | for (size_t i=0; i(tree, q, qMin, qMax, radius, out); 126 | } 127 | 128 | // modify to take in the tree instead 129 | template 130 | void knnHelper(nodeT* tree, objT& q, buffer& out) { 131 | // find the leaf first 132 | int relation = tree->boxCompare(tree->getMin(), tree->getMax(), 133 | point(q.coords()), 134 | point(q.coords())); 135 | if (relation == tree->boxExclude) { 136 | return; 137 | } else { 138 | if (tree->isLeaf()) { 139 | // basecase 140 | for (size_t i = 0; i < tree->size(); ++ i) { 141 | objT* p = tree->getItem(i); 142 | out.insert(elem(q.dist(*p), p));} 143 | } else { 144 | knnHelper(tree->L(), q, out); 145 | knnHelper(tree->R(), q, out); 146 | } 147 | } 148 | 149 | if (!out.hasK()) { 150 | if (tree->siblin() == NULL) { 151 | throw std::runtime_error("Error, knnHelper reached root node without enough neighbors."); 152 | } 153 | for (size_t i=0; isiblin()->size(); ++i) { 154 | objT* p = tree->siblin()->getItem(i); 155 | out.insert(elem(q.dist(*p), p));} 156 | } else { // Buffer filled to a least k 157 | if (tree->siblin() != NULL) { 158 | elem tmp = out.keepK(); 159 | knnRange(tree->siblin(), q, tmp.cost, out);} 160 | } 161 | } 162 | 163 | template 164 | parlay::sequence kdTreeKnn(parlay::sequence &queries, 165 | size_t k, 166 | kdNode* tree = nullptr, 167 | bool sorted = false) { 168 | using nodeT = kdNode; 169 | bool freeTree = false; 170 | if (!tree) { 171 | freeTree = true; 172 | tree = buildKdt(queries, true); 173 | } 174 | auto out = parlay::sequence>(2*k*queries.size()); 175 | auto idx = parlay::sequence(k*queries.size()); 176 | parlay::parallel_for(0, queries.size(), [&](size_t i) { 177 | buffer buf = buffer(k, out.cut(i*2*k, (i+1)*2*k)); 178 | knnHelper(tree, queries[i], buf); 179 | buf.keepK(); 180 | if (sorted) buf.sort(); 181 | for(size_t j=0; j 192 | parlay::sequence bruteforceKnn(parlay::sequence &queries, size_t k) { 193 | auto out = parlay::sequence>(2*k*queries.size()); 194 | auto idx = parlay::sequence(k*queries.size()); 195 | parlay::parallel_for(0, queries.size(), [&](size_t i) { 196 | objT q = queries[i]; 197 | buffer buf = buffer(k, out.cut(i*2*k, (i+1)*2*k)); 198 | for(size_t j=0; j 27 | #include "delaunayTriangulation/geometry.h" 28 | 29 | using namespace std; 30 | 31 | // ************************************************************* 32 | // TOPOLOGY 33 | // ************************************************************* 34 | 35 | template 36 | struct vertex; 37 | 38 | // an unoriented triangle with its three neighbors and 3 vertices 39 | // vtx[1] 40 | // o 41 | // | \ -> ngh[1] 42 | // ngh[2] <- | o vtx[0] 43 | // | / -> ngh[0] 44 | // o 45 | // vtx[2] 46 | template 47 | struct triangle { 48 | using tri_t = triangle; 49 | using vtx_t = vertex; 50 | tri_t *ngh [3]; 51 | vtx_t *vtx [3]; 52 | size_t id; 53 | bool initialized; 54 | char bad; // used to mark badly shaped triangles 55 | void setT(tri_t *t1, tri_t *t2, tri_t* t3) { 56 | ngh[0] = t1; ngh[1] = t2; ngh[2] = t3; } 57 | void setV(vtx_t *v1, vtx_t *v2, vtx_t *v3) { 58 | vtx[0] = v1; vtx[1] = v2; vtx[2] = v3; } 59 | int locate(tri_t *t) { 60 | for (int i=0; i < 3; i++) { 61 | //cout << t << ", " << ngh[i] << endl; 62 | if (ngh[i] == t) return i; 63 | } 64 | cout<<"did not locate back pointer in triangulation\n"; 65 | abort(); // did not find 66 | } 67 | void update(tri_t *t, tri_t *tn) { 68 | for (int i=0; i < 3; i++) 69 | if (ngh[i] == t) {ngh[i] = tn; return;} 70 | cout<<"did not update\n"; 71 | abort(); // did not find 72 | } 73 | }; 74 | 75 | // a vertex pointing to an arbitrary triangle to which it belongs (if any) 76 | template 77 | struct vertex { 78 | using point_t = point; 79 | using tri = triangle; 80 | point pt; 81 | tri *t; 82 | tri *badT; 83 | int id; 84 | int reserve; 85 | size_t counter; 86 | void print() { 87 | cout << id << " (" << pt.x << "," << pt.y << ") " << endl; 88 | } 89 | vertex(point p, size_t i) : pt(p), id(i), reserve(-1) 90 | , badT(NULL) 91 | {} 92 | vertex() {} 93 | }; 94 | 95 | inline int mod3(int i) {return (i>2) ? i-3 : i;} 96 | 97 | // a simplex is just an oriented triangle. An integer (o) 98 | // is used to indicate which of 3 orientations it is in (0,1,2) 99 | // If boundary is set then it represents the edge through t.ngh[o], 100 | // which is a NULL pointer. 101 | template 102 | struct simplex { 103 | using vtx_t = vertex; 104 | using tri_t = triangle; 105 | tri_t *t; 106 | int o; 107 | bool boundary; 108 | simplex(tri_t *tt, int oo) : t(tt), o(oo), boundary(0) {} 109 | simplex(tri_t *tt, int oo, bool _b) : t(tt), o(oo), boundary(_b) {} 110 | simplex(vtx_t *v1, vtx_t *v2, vtx_t *v3, tri_t *tt) { 111 | t = tt; 112 | t->ngh[0] = t->ngh[1] = t->ngh[2] = NULL; 113 | t->vtx[0] = v1; v1->t = t; 114 | t->vtx[1] = v2; v2->t = t; 115 | t->vtx[2] = v3; v3->t = t; 116 | o = 0; 117 | boundary = 0; 118 | } 119 | simplex() : t(nullptr), o(0), boundary(false) {} 120 | 121 | void print() { 122 | if (t == NULL) cout << "NULL simp" << endl; 123 | else { 124 | cout << "vtxs="; 125 | for (int i=0; i < 3; i++) 126 | if (t->vtx[mod3(i+o)] != NULL) 127 | cout << t->vtx[mod3(i+o)]->id << " (" << 128 | t->vtx[mod3(i+o)]->pt.x << "," << 129 | t->vtx[mod3(i+o)]->pt.y << ") "; 130 | else cout << "NULL "; 131 | cout << endl; 132 | } 133 | } 134 | 135 | simplex across() { 136 | tri_t *to = t->ngh[o]; 137 | if (to != NULL) return simplex(to,to->locate(t)); 138 | else return simplex(t,o,1); 139 | } 140 | 141 | // depending on initial triangle this could be counterclockwise 142 | simplex rotClockwise() { return simplex(t,mod3(o+1));} 143 | 144 | bool valid() {return (!boundary);} 145 | bool isTriangle() {return (!boundary);} 146 | bool isBoundary() {return boundary;} 147 | 148 | vtx_t *firstVertex() {return t->vtx[o];} 149 | 150 | bool inCirc(vtx_t *v) { 151 | if (boundary || t == NULL) return 0; 152 | return inCircle(t->vtx[0]->pt, t->vtx[1]->pt, 153 | t->vtx[2]->pt, v->pt); 154 | } 155 | 156 | // the angle facing the across edge 157 | double farAngle() { 158 | return angle(t->vtx[mod3(o+1)]->pt, 159 | t->vtx[o]->pt, 160 | t->vtx[mod3(o+2)]->pt); 161 | } 162 | 163 | bool outside(vtx_t *v) { 164 | if (boundary || t == NULL) return 0; 165 | return counterClockwise(t->vtx[mod3(o+2)]->pt, v->pt, t->vtx[o]->pt); 166 | } 167 | 168 | // flips two triangles and adjusts neighboring triangles 169 | void flip() { 170 | simplex s = across(); 171 | int o1 = mod3(o+1); 172 | int os1 = mod3(s.o+1); 173 | 174 | tri_t *t1 = t->ngh[o1]; 175 | tri_t *t2 = s.t->ngh[os1]; 176 | vtx_t *v1 = t->vtx[o1]; 177 | vtx_t *v2 = s.t->vtx[os1]; 178 | 179 | t->vtx[o]->t = s.t; 180 | t->vtx[o] = v2; 181 | t->ngh[o] = t2; 182 | if (t2 != NULL) t2->update(s.t,t); 183 | t->ngh[o1] = s.t; 184 | 185 | s.t->vtx[s.o]->t = t; 186 | s.t->vtx[s.o] = v1; 187 | s.t->ngh[s.o] = t1; 188 | if (t1 != NULL) t1->update(t,s.t); 189 | s.t->ngh[os1] = t; 190 | } 191 | 192 | // splits the triangle into three triangles with new vertex v in the middle 193 | // updates all neighboring simplices 194 | // ta0 and ta0 are pointers to the memory to use for the two new triangles 195 | void split(vtx_t* v, tri_t* ta0, tri_t* ta1) { 196 | v->t = t; 197 | tri_t *t1 = t->ngh[0]; tri_t *t2 = t->ngh[1]; tri_t *t3 = t->ngh[2]; 198 | vtx_t *v1 = t->vtx[0]; vtx_t *v2 = t->vtx[1]; vtx_t *v3 = t->vtx[2]; 199 | t->ngh[1] = ta0; t->ngh[2] = ta1; 200 | t->vtx[1] = v; 201 | ta0->setT(t2,ta1,t); ta0->setV(v2,v,v1); 202 | ta1->setT(t3,t,ta0); ta1->setV(v3,v,v2); 203 | if (t2 != NULL) t2->update(t,ta0); 204 | if (t3 != NULL) t3->update(t,ta1); 205 | v2->t = ta0; 206 | } 207 | 208 | // splits one of the boundaries of a triangle to form two triangles 209 | // the orientation dictates which edge to split (i.e., t.ngh[o]) 210 | // ta is a pointer to memory to use for the new triangle 211 | void splitBoundary(vtx_t* v, tri_t* ta) { 212 | int o1 = mod3(o+1); 213 | int o2 = mod3(o+2); 214 | if (t->ngh[o] != NULL) { 215 | cout << "simplex::splitBoundary: not boundary" << endl; abort();} 216 | v->t = t; 217 | tri_t *t2 = t->ngh[o2]; 218 | vtx_t *v1 = t->vtx[o1]; vtx_t *v2 = t->vtx[o2]; 219 | t->ngh[o2] = ta; t->vtx[o2] = v; 220 | ta->setT(t2,NULL,t); ta->setV(v2,v,v1); 221 | if (t2 != NULL) t2->update(t,ta); 222 | v2->t = t; 223 | } 224 | 225 | // given a vtx v, extends a boundary edge (t.ngh[o]) with an extra 226 | // triangle on that edge with apex v. 227 | // ta is used as the memory for the triangle 228 | simplex extend(vtx_t* v, tri_t* ta) { 229 | if (t->ngh[o] != NULL) { 230 | cout << "simplex::extend: not boundary" << endl; abort();} 231 | t->ngh[o] = ta; 232 | ta->setV(t->vtx[o], t->vtx[mod3(o+2)], v); 233 | ta->setT(NULL,t,NULL); 234 | v->t = ta; 235 | return simplex(ta,0); 236 | } 237 | 238 | }; 239 | 240 | // this might or might not be needed 241 | // void topologyFromTriangles(triangles Tri, vtx** vr, tri** tr); 242 | 243 | #endif // _TOPOLOGY_INCLUDED 244 | -------------------------------------------------------------------------------- /include/pargeo/wspd.h: -------------------------------------------------------------------------------- 1 | // This code is part of the project "Fast Parallel Algorithms for Euclidean 2 | // Minimum Spanning Tree and Hierarchical Spatial Clustering" 3 | // Copyright (c) 2021 Yiqiu Wang, Shangdi Yu, Yan Gu, Julian Shun 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a 6 | // copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights (to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included 14 | // in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | #pragma once 25 | 26 | #include "parlay/sequence.h" 27 | #include "pargeo/point.h" 28 | #include "pargeo/kdTree.h" 29 | #include "pargeo/parBuf.h" 30 | 31 | namespace pargeo { 32 | 33 | using namespace parlay; 34 | 35 | // ------------ Well-separated pair struct -------------- 36 | 37 | template 38 | struct wsp { 39 | nodeT* u; 40 | nodeT* v; 41 | wsp(nodeT* uu, nodeT* vv): u(uu), v(vv) {} 42 | }; 43 | 44 | // ------------ Functions to call -------------- 45 | 46 | template 47 | sequence>>> wspdSerial(kdNode>* tree, double s = 2); 48 | 49 | template 50 | sequence>>> wspdParallel(kdNode>* tree, double s = 2); 51 | 52 | // ------------ Serial implementation -------------- 53 | 54 | template 55 | bool geomWellSeparated(nodeT *u, nodeT *v, floatT s=2) { 56 | floatT circleDiam_u = 0; 57 | floatT circleDiam_v = 0; 58 | floatT circleDistance = 0; 59 | for (int d = 0; d < u->dim; ++ d) { 60 | floatT uTmpDiff = u->getMax(d) - u->getMin(d); 61 | floatT vTmpDiff = v->getMax(d) - v->getMin(d); 62 | floatT uTmpAvg = (u->getMax(d) + u->getMin(d))/2; 63 | floatT vTmpAvg = (v->getMax(d) + v->getMin(d))/2; 64 | circleDistance += (uTmpAvg - vTmpAvg) * (uTmpAvg - vTmpAvg); 65 | circleDiam_u += uTmpDiff * uTmpDiff; 66 | circleDiam_v += vTmpDiff * vTmpDiff; 67 | } 68 | circleDiam_u = sqrt(circleDiam_u); 69 | circleDiam_v = sqrt(circleDiam_v); 70 | floatT myRadius = std::max(circleDiam_u, circleDiam_v)/2; 71 | circleDistance = sqrt(circleDistance) - circleDiam_u/2 - circleDiam_v/2; 72 | return circleDistance >= (s * myRadius); 73 | } 74 | 75 | template 76 | struct wspdNormalSerial { 77 | using floatT = double; 78 | using pType = wsp; 79 | sequence *out; 80 | 81 | wspdNormalSerial(sequence *outt) : out(outt) {} 82 | 83 | inline void run(nodeT *u, nodeT *v) {out->emplace_back(u, v);} 84 | inline bool moveon(nodeT *u, nodeT *v) {return true;} 85 | inline bool start(nodeT *u) { return true; } 86 | inline bool wellSeparated(nodeT *u, nodeT *v, floatT s) { 87 | return geomWellSeparated(u, v, s); 88 | } 89 | }; 90 | 91 | template 92 | inline void findPairSerial(nodeT *u, nodeT *v, opT* f, double s) { 93 | if (!f->moveon(u, v)) return; 94 | 95 | if (f->wellSeparated(u, v, s)) { 96 | f->run(u, v); 97 | } else { 98 | if (u->isLeaf() && v->isLeaf()) { 99 | throw std::runtime_error("error, leaves not well separated"); 100 | } else if (u->isLeaf()) { 101 | findPairSerial(v->L(), u, f, s); 102 | findPairSerial(v->R(), u, f, s); 103 | } else if (v->isLeaf()) { 104 | findPairSerial(u->L(), v, f, s); 105 | findPairSerial(u->R(), v, f, s); 106 | } else { 107 | if (u->lMax() > v->lMax()) { 108 | findPairSerial(u->L(), v, f, s); 109 | findPairSerial(u->R(), v, f, s); 110 | } else { 111 | findPairSerial(v->L(), u, f, s); 112 | findPairSerial(v->R(), u, f, s);} 113 | }} 114 | } 115 | 116 | template 117 | inline void computeWspdSerial(nodeT *nd, opT *f, double s=2) { 118 | if (!nd->isLeaf() && f->start(nd)) { 119 | computeWspdSerial(nd->L(), f, s); 120 | computeWspdSerial(nd->R(), f, s); 121 | findPairSerial(nd->L(), nd->R(), f, s);} 122 | } 123 | 124 | template 125 | sequence>>> wspdSerial(kdNode>* tree, double s) { 126 | using pointT = point; 127 | using nodeT = kdNode; 128 | using pairT = wsp; 129 | 130 | sequence out; 131 | 132 | auto wg = wspdNormalSerial(&out); 133 | 134 | computeWspdSerial>(tree, &wg, s); 135 | //cout << "#-wspd = " << out.size() << endl; 136 | return out; 137 | } 138 | 139 | // ------------ Parallel implementation -------------- 140 | 141 | template 142 | struct wspdNormalParallel { 143 | using floatT = double; 144 | using pType = wsp; 145 | using bufT = parBuf; 146 | 147 | bufT **out; 148 | 149 | wspdNormalParallel(size_t n) { 150 | size_t procs = num_workers(); 151 | out = (bufT**) malloc(sizeof(bufT*)*procs); 152 | parallel_for(0, procs, [&](size_t p) { 153 | out[p] = new bufT(n/procs); 154 | }); 155 | } 156 | 157 | ~wspdNormalParallel() { 158 | size_t procs = num_workers(); 159 | parallel_for(0, procs, [&](size_t p) { 160 | delete out[p];}); 161 | free(out); 162 | } 163 | 164 | inline void run(nodeT *u, nodeT *v) { 165 | auto tmp = out[worker_id()]->increment(); 166 | tmp->u = u; 167 | tmp->v = v; 168 | } 169 | 170 | sequence collectPairs() { 171 | int procs = num_workers(); 172 | return parBufCollect(out, procs); 173 | } 174 | 175 | inline bool moveon(nodeT *u, nodeT *v) {return true;} 176 | inline bool start(nodeT *u) { return true; } 177 | inline bool wellSeparated(nodeT *u, nodeT *v, floatT s) { 178 | return geomWellSeparated(u, v, s); 179 | } 180 | }; 181 | 182 | template 183 | inline void findPairParallel(nodeT *u, nodeT *v, opT* f, double s) { 184 | if (!f->moveon(u, v)) return; 185 | if (u->size() + v->size() < 2000) return findPairSerial(u,v,f,s); 186 | 187 | if (f->wellSeparated(u, v, s)) { 188 | f->run(u, v);//need to be thread safe 189 | } else { 190 | if (u->isLeaf() && v->isLeaf()) { 191 | throw std::runtime_error("error, leaves not well separated"); 192 | } else if (u->isLeaf()) { 193 | par_do([&](){findPairParallel(v->L(), u, f, s);}, 194 | [&](){findPairParallel(v->R(), u, f, s);}); 195 | } else if (v->isLeaf()) { 196 | par_do([&](){findPairParallel(u->L(), v, f, s);}, 197 | [&](){findPairParallel(u->R(), v, f, s);}); 198 | } else { 199 | if (u->lMax() > v->lMax()) { 200 | par_do([&](){findPairParallel(u->L(), v, f, s);}, 201 | [&](){findPairParallel(u->R(), v, f, s);}); 202 | } else { 203 | par_do([&](){findPairParallel(v->L(), u, f, s);}, 204 | [&](){findPairParallel(v->R(), u, f, s);}); 205 | } 206 | }} 207 | } 208 | 209 | template 210 | inline void computeWspdParallel(nodeT *nd, opT *f, double s=2) { 211 | if (nd->size() < 2000) { 212 | computeWspdSerial(nd, f, s); 213 | } else if (!nd->isLeaf() && f->start(nd)) { 214 | par_do([&](){computeWspdParallel(nd->L(), f, s);}, 215 | [&](){computeWspdParallel(nd->R(), f, s);}); 216 | findPairParallel(nd->L(), nd->R(), f, s); 217 | } 218 | } 219 | 220 | template 221 | sequence>>> wspdParallel(kdNode>* tree, double s) { 222 | using pointT = point; 223 | using nodeT = kdNode; 224 | using pairT = wsp; 225 | 226 | auto wg = wspdNormalParallel(tree->size()); 227 | 228 | computeWspdParallel>(tree, &wg, s); 229 | auto out = wg.collectPairs(); 230 | //cout << "#-wspd-collected = " << out.size() << endl; 231 | 232 | return out; 233 | } 234 | 235 | } // End namespace 236 | -------------------------------------------------------------------------------- /src/euclideanMst/memoGfk/wspdFilter.h: -------------------------------------------------------------------------------- 1 | // This code is part of the project "Fast Parallel Algorithms for Euclidean 2 | // Minimum Spanning Tree and Hierarchical Spatial Clustering" 3 | // Copyright (c) 2021 Yiqiu Wang, Shangdi Yu, Yan Gu, Julian Shun 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a 6 | // copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights (to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included 14 | // in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | #pragma once 25 | 26 | #include 27 | #include 28 | #include 29 | #include "pargeo/wspd.h" 30 | #include "pargeo/bccp.h" 31 | #include "pargeo/parBuf.h" 32 | #include "pargeo/getTime.h" 33 | #include "parlay/utilities.h" 34 | 35 | namespace pargeo { 36 | namespace emstInternal { 37 | 38 | template 39 | struct rhoUpdateSerial { 40 | using floatT = double; 41 | 42 | floatT rho; 43 | floatT beta; 44 | UF *uf; 45 | 46 | rhoUpdateSerial(floatT betaa, UF *uff) : 47 | beta(betaa), uf(uff) { 48 | rho = std::numeric_limits::max();} 49 | 50 | void run(nodeT *u, nodeT *v) { 51 | floatT myDist = nodeDistance(u, v); 52 | if (myDist < rho) rho = myDist; 53 | } 54 | 55 | bool moveon(nodeT *u, nodeT *v) { 56 | if (u->hasId() && u->getId() == v->getId()) return false; // filtering todo 57 | if (u->size() + v->size() <= beta) return false; // not in E_u, not considered for rho 58 | if (nodeDistance(u, v) >= rho) return false; // no subsequent finds can update rho 59 | return true; 60 | } 61 | 62 | bool start(nodeT *u) { 63 | if (u->size() > beta) { 64 | return true; 65 | } else { 66 | return false;// if node size < beta, so would children 67 | } 68 | } 69 | 70 | floatT getRho() { return rho;} 71 | 72 | bool wellSeparated(nodeT* u, nodeT* v, floatT s) {return geomWellSeparated(u, v, s);} 73 | }; 74 | 75 | template 76 | struct wspGetSerial { 77 | using floatT = double; 78 | using bcpT = std::tuple; 81 | 82 | floatT rhoLo; 83 | floatT rhoHi; 84 | floatT beta; 85 | sequence out; 86 | nodeT *tree; 87 | UF *uf; 88 | 89 | wspGetSerial(floatT betaa, floatT rhoLoo, floatT rhoHii, nodeT *treee, UF *uff) : 90 | beta(betaa), rhoLo(rhoLoo), rhoHi(rhoHii), tree(treee), uf(uff) { 91 | out = sequence(); 92 | } 93 | 94 | void run(nodeT *u, nodeT *v) { 95 | auto bcp = bccp(u, v); 96 | if (u->size() + v->size() <= beta && 97 | std::get<2>(bcp) >= rhoLo && 98 | std::get<2>(bcp) < rhoHi) { 99 | out.push_back(bcp); 100 | } 101 | } 102 | 103 | bool moveon(nodeT *u, nodeT *v) { 104 | if (u->hasId() && u->getId() == v->getId()) {return false;} // todo need to add id to tree nodes 105 | if (nodeDistance(u, v) >= rhoHi) return false; // too separated to be considered 106 | if (nodeFarDistance(u, v) < rhoLo) return false; // too close to be considered 107 | return true; 108 | } 109 | 110 | bool start(nodeT *u) { 111 | if (u->diag() >= rhoLo) { 112 | return true; 113 | } else { 114 | return false; 115 | } 116 | } 117 | 118 | bool wellSeparated(nodeT* u, nodeT* v, floatT s) {return geomWellSeparated(u, v, s);} 119 | sequence collect() { return out; }; 120 | }; 121 | 122 | template 123 | sequence> 126 | filterWspdSerial(double t_beta, 127 | double t_rho_lo, 128 | double& t_rho_hi, 129 | nodeT *t_kdTree, 130 | UF *t_mst) { 131 | using floatT = double; 132 | using objT = typename nodeT::objT; 133 | using bcpT = std::tuple; 134 | 135 | auto myRho = rhoUpdateSerial(t_beta, t_mst); 136 | 137 | pargeo::computeWspdSerial>(t_kdTree, &myRho); 138 | 139 | auto mySplitter = wspGetSerial(t_beta, t_rho_lo, myRho.getRho(), t_kdTree, t_mst); 140 | 141 | pargeo::computeWspdSerial>(t_kdTree, &mySplitter); 142 | 143 | t_rho_hi = myRho.getRho(); 144 | return mySplitter.collect(); 145 | } 146 | 147 | template 148 | struct rhoUpdateParallel { 149 | using floatT = double; 150 | 151 | std::atomic rho; 152 | floatT beta; 153 | UF *uf; 154 | 155 | rhoUpdateParallel(floatT betaa, UF *uff) : 156 | beta(betaa), uf(uff) { 157 | rho = std::numeric_limits::max();} 158 | 159 | void run(nodeT *u, nodeT *v) { 160 | floatT myDist = nodeDistance(u, v); 161 | parlay::write_min(&rho, myDist, std::less()); //check 162 | } 163 | 164 | bool moveon(nodeT *u, nodeT *v) { 165 | if (u->hasId() && u->getId() == v->getId()) return false; // filtering 166 | if (u->size()+v->size() <= beta) return false; // not in E_u, not considered for rho 167 | if (nodeDistance(u, v) >= rho) return false; // no subsequent finds can update rho 168 | return true; 169 | } 170 | 171 | bool start(nodeT *u) { 172 | if (u->size() > beta) { 173 | return true; 174 | } else { 175 | return false;// if node size < beta, so would children 176 | } 177 | } 178 | 179 | floatT getRho() { return rho;} 180 | 181 | bool wellSeparated(nodeT* u, nodeT* v, floatT s) {return geomWellSeparated(u, v, s);} 182 | }; 183 | 184 | template 185 | struct wspGetParallel { 186 | using floatT = double; 187 | using bcpT = std::tuple; 190 | using bufT = parBuf; 191 | 192 | floatT rhoLo; 193 | floatT rhoHi; 194 | floatT beta; 195 | nodeT *tree; 196 | UF *uf; 197 | bufT **out; 198 | 199 | wspGetParallel(floatT betaa, floatT rhoLoo, floatT rhoHii, nodeT *treee, UF *uff) : 200 | beta(betaa), rhoLo(rhoLoo), rhoHi(rhoHii), tree(treee), uf(uff) { 201 | size_t procs = num_workers(); 202 | out = (bufT**) malloc(sizeof(bufT*)*procs); 203 | parallel_for(0, procs, [&](size_t p) { 204 | out[p] = new bufT(tree->size()/procs); 205 | }); 206 | } 207 | 208 | ~wspGetParallel() { 209 | size_t procs = num_workers(); 210 | parallel_for(0, procs, [&](size_t p) { 211 | delete out[p];}); 212 | free(out); 213 | } 214 | 215 | sequence collect() { 216 | int procs = num_workers(); 217 | return parBufCollect(out, procs); 218 | } 219 | 220 | void run(nodeT *u, nodeT *v) { 221 | auto bcp = bccp(u, v); 222 | if (u->size() + v->size() <= beta && 223 | std::get<2>(bcp) >= rhoLo && 224 | std::get<2>(bcp) < rhoHi) { 225 | auto tmp = out[worker_id()]->increment(); 226 | get<0>(*tmp) = get<0>(bcp); 227 | get<1>(*tmp) = get<1>(bcp); 228 | get<2>(*tmp) = get<2>(bcp); 229 | } 230 | } 231 | 232 | bool moveon(nodeT *u, nodeT *v) { 233 | if (u->hasId() && u->getId() == v->getId()) {return false;} 234 | if (nodeDistance(u, v) >= rhoHi) return false; // too separated to be considered 235 | if (nodeFarDistance(u, v) < rhoLo) return false; // too close to be considered, bug!! 236 | return true; 237 | } 238 | 239 | bool start(nodeT *u) { 240 | if (u->diag() >= rhoLo) { 241 | return true; 242 | } else { 243 | return false; 244 | } 245 | } 246 | bool wellSeparated(nodeT* u, nodeT* v, floatT s) {return geomWellSeparated(u, v, s);} 247 | }; 248 | 249 | template 250 | sequence> 253 | filterWspdParallel(double t_beta, 254 | double t_rho_lo, 255 | double& t_rho_hi, 256 | nodeT *t_kdTree, 257 | UF *t_mst) { 258 | using floatT = double; 259 | using objT = typename nodeT::objT; 260 | using bcpT = std::tuple; 261 | 262 | auto myRho = rhoUpdateParallel(t_beta, t_mst); 263 | 264 | pargeo::computeWspdParallel>(t_kdTree, &myRho); 265 | 266 | auto mySplitter = wspGetParallel(t_beta, t_rho_lo, myRho.getRho(), t_kdTree, t_mst); 267 | 268 | pargeo::computeWspdParallel>(t_kdTree, &mySplitter); 269 | 270 | t_rho_hi = myRho.getRho(); 271 | return mySplitter.collect(); 272 | } 273 | 274 | } // End namespace emstInternal 275 | } // End namespace pargeo 276 | -------------------------------------------------------------------------------- /include/pargeo/IO.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 | #pragma once 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "pargeo/point.h" 30 | #include "parlay/primitives.h" 31 | #include "parlay/parallel.h" 32 | #include "parlay/io.h" 33 | 34 | namespace pargeo { 35 | namespace IO { 36 | 37 | auto is_newline = [] (char c) { 38 | switch (c) { 39 | case '\r': 40 | case '\n': return true; 41 | default : return false; 42 | } 43 | }; 44 | 45 | auto is_delim = [] (char c) { 46 | switch (c) { 47 | case '\t': 48 | case ';': 49 | case ',': 50 | case ' ' : return true; 51 | default : return false; 52 | } 53 | }; 54 | 55 | auto is_space = [] (char c) { 56 | return is_newline(c) || is_delim(c) || c==0; 57 | }; 58 | 59 | auto is_number = [] (char c) { 60 | switch (c) { 61 | case '0': 62 | case '1': 63 | case '2': 64 | case '3': 65 | case '4': 66 | case '5': 67 | case '6': 68 | case '7': 69 | case '8': 70 | case '9': 71 | case '.': 72 | case '+': 73 | case '-': 74 | case 'e' : return true; 75 | default : return false; 76 | } 77 | }; 78 | 79 | // parallel code for converting a string to word pointers 80 | // side effects string by setting to null after each word 81 | template 82 | parlay::sequence stringToWords(Seq &Str) { 83 | size_t n = Str.size(); 84 | 85 | parlay::parallel_for(0, n, [&] (long i) { 86 | if (is_space(Str[i])) Str[i] = 0;}); 87 | 88 | // mark start of words 89 | auto FL = parlay::tabulate(n, [&] (long i) -> bool { 90 | return (i==0) ? Str[0] : Str[i] && !Str[i-1];}); 91 | 92 | // offset for each start of word 93 | auto Offsets = parlay::pack_index(FL); 94 | 95 | // pointer to each start of word 96 | auto SA = parlay::tabulate(Offsets.size(), [&] (long j) -> char* { 97 | return Str.begin() + Offsets[j];}); 98 | 99 | return SA; 100 | } 101 | 102 | inline int xToStringLen(parlay::sequence const &a) { return a.size();} 103 | inline void xToString(char* s, parlay::sequence const &a) { 104 | for (int i=0; i < a.size(); i++) s[i] = a[i];} 105 | 106 | inline int xToStringLen(long a) { return 21;} 107 | inline void xToString(char* s, long a) { sprintf(s,"%ld",a);} 108 | 109 | inline int xToStringLen(unsigned long a) { return 21;} 110 | inline void xToString(char* s, unsigned long a) { sprintf(s,"%lu",a);} 111 | 112 | inline uint xToStringLen(uint a) { return 12;} 113 | inline void xToString(char* s, uint a) { sprintf(s,"%u",a);} 114 | 115 | inline int xToStringLen(int a) { return 12;} 116 | inline void xToString(char* s, int a) { sprintf(s,"%d",a);} 117 | 118 | inline int xToStringLen(double a) { return 18;} 119 | inline void xToString(char* s, double a) { sprintf(s,"%.11le", a);} 120 | 121 | inline int xToStringLen(char* a) { return strlen(a)+1;} 122 | inline void xToString(char* s, char* a) { sprintf(s,"%s",a);} 123 | 124 | template 125 | inline int xToStringLen(std::pair a) { 126 | return xToStringLen(a.first) + xToStringLen(a.second) + 1; 127 | } 128 | 129 | template 130 | inline void xToString(char* s, std::pair a) { 131 | int l = xToStringLen(a.first); 132 | xToString(s, a.first); 133 | s[l] = ' '; 134 | xToString(s+l+1, a.second); 135 | } 136 | 137 | template 138 | inline int xToStringLen(point a) { 139 | int s = 0; 140 | for (int i=0; i 145 | inline void xToString(char* s, point a, bool comma=false) { 146 | char* ss = s; 147 | for (int i=0; i 159 | parlay::sequence seqToString(Seq const &A) { 160 | size_t n = A.size(); 161 | auto L = parlay::tabulate(n, [&] (size_t i) -> long { 162 | typename Seq::value_type x = A[i]; 163 | return xToStringLen(x)+1;}); 164 | size_t m; 165 | std::tie(L,m) = parlay::scan(std::move(L)); 166 | 167 | parlay::sequence B(m+1, (char) 0); 168 | char* Bs = B.begin(); 169 | 170 | parlay::parallel_for(0, n-1, [&] (long i) { 171 | xToString(Bs + L[i], A[i]); 172 | Bs[L[i+1] - 1] = '\n'; 173 | }); 174 | xToString(Bs + L[n-1], A[n-1]); 175 | Bs[m] = Bs[m-1] = '\n'; 176 | 177 | parlay::sequence C = parlay::filter(B, [&] (char c) {return c != 0;}); 178 | C[C.size()-1] = 0; 179 | return C; 180 | } 181 | 182 | template 183 | void writeSeqToStream(std::ofstream& os, parlay::sequence const &A) { 184 | size_t bsize = 10000000; 185 | size_t offset = 0; 186 | size_t n = A.size(); 187 | while (offset < n) { 188 | // Generates a string for a sequence of size at most bsize 189 | // and then wrties it to the output stream 190 | parlay::sequence S = seqToString(A.cut(offset, std::min(offset + bsize, n))); 191 | os.write(S.begin(), S.size()-1); 192 | offset += bsize; 193 | } 194 | } 195 | 196 | template 197 | int writeSeqToFile(std::string header, 198 | parlay::sequence const &A, 199 | char const *fileName) { 200 | auto a = A[0]; 201 | //xToStringLena(a); 202 | std::ofstream file (fileName, std::ios::out | std::ios::binary); 203 | if (!file.is_open()) { 204 | std::cout << "filename: " << fileName << "\n"; 205 | throw std::runtime_error("Unable to open file"); 206 | } 207 | if (header.size() > 0) file << header << std::endl; 208 | writeSeqToStream(file, A); 209 | file.close(); 210 | return 0; 211 | } 212 | 213 | template 214 | int write2SeqToFile(std::string header, 215 | parlay::sequence const &A, 216 | parlay::sequence const &B, 217 | char const *fileName) { 218 | std::ofstream file (fileName, std::ios::out | std::ios::binary); 219 | if (!file.is_open()) { 220 | std::cout << "filename: " << fileName << "\n"; 221 | throw std::runtime_error("Unable to open file"); 222 | } 223 | file << header << std::endl; 224 | writeSeqToStream(file, A); 225 | writeSeqToStream(file, B); 226 | file.close(); 227 | return 0; 228 | } 229 | 230 | parlay::sequence readStringFromFile(char const *fileName) { 231 | std::ifstream file (fileName, std::ios::in | std::ios::binary | std::ios::ate); 232 | if (!file.is_open()) { 233 | std::cout << "filename: " << fileName << "\n"; 234 | throw std::runtime_error("Unable to open file"); 235 | } 236 | long end = file.tellg(); 237 | file.seekg (0, std::ios::beg); 238 | long n = end - file.tellg(); 239 | parlay::sequence bytes(n, (char) 0); 240 | file.read (bytes.begin(), n); 241 | file.close(); 242 | return bytes; 243 | } 244 | 245 | std::string intHeaderIO = "sequenceInt"; 246 | 247 | template 248 | int writeIntSeqToFile(parlay::sequence const &A, char const *fileName) { 249 | return writeSeqToFile(intHeaderIO, A, fileName); 250 | } 251 | 252 | parlay::sequence> get_tokens(char const *fileName) { 253 | // parlay::internal::timer t("get_tokens"); 254 | // auto S = parlay::chars_from_file(fileName); 255 | auto S = parlay::file_map(fileName); 256 | // t.next("file map"); 257 | auto r = parlay::tokens(S, is_space); 258 | // t.next("tokens"); 259 | return r; 260 | } 261 | 262 | template 263 | parlay::sequence readIntSeqFromFile(char const *fileName) { 264 | auto W = get_tokens(fileName); 265 | std::string header(W[0].begin(),W[0].end()); 266 | if (header != intHeaderIO) { 267 | throw std::runtime_error("readIntSeqFromFile: bad input"); 268 | } 269 | long n = W.size()-1; 270 | auto A = parlay::tabulate(n, [&] (long i) -> T { 271 | return parlay::chars_to_long(W[i+1]);}); 272 | return A; 273 | } 274 | } // End namespace IO 275 | } // End namespace pargeo 276 | -------------------------------------------------------------------------------- /include/closestPair/divideConquer.h: -------------------------------------------------------------------------------- 1 | // This code is part of the project "A Parallel Batch-Dynamic Data Structure 2 | // for the Closest Pair Problem" 3 | // Copyright (c) 2020 Yiqiu Wang, Shangdi Yu, Yan Gu, Julian Shun 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a 6 | // copy of this software and associated documentation files (the 7 | // "Software"), to deal in the Software without restriction, including 8 | // without limitation the rights (to use, copy, modify, merge, publish, 9 | // distribute, sublicense, and/or sell copies of the Software, and to 10 | // permit persons to whom the Software is furnished to do so, subject to 11 | // the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included 14 | // in all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | #pragma once 25 | 26 | #include 27 | #include 28 | #include "pargeo/point.h" 29 | #include "parlay/parallel.h" 30 | #include "parlay/internal/sample_sort.h" 31 | #include "parlay/primitives.h" 32 | #include "parlay/sequence.h" 33 | 34 | namespace pargeo { 35 | 36 | using namespace std; 37 | using namespace parlay; 38 | 39 | static constexpr double floatMax = std::numeric_limits::max(); 40 | 41 | template 42 | struct pointPair { 43 | using pt = point; 44 | using pp = pointPair; 45 | using floatT = typename pt::floatT; 46 | 47 | floatT dist; 48 | 49 | pt u,v; 50 | 51 | pointPair(): dist(floatMax) {u=pt();v=pt();} 52 | 53 | pointPair(pt uu, pt vv, floatT distt): u(uu), v(vv), dist(distt) {} 54 | 55 | pointPair(pt uu, pt vv): u(uu), v(vv) { 56 | dist = u.dist(v);} 57 | 58 | bool isEmpty() {return dist==floatMax;} 59 | 60 | void closer(pt uu, pt vv) { 61 | floatT distt = uu.dist(vv); 62 | if (distt 94 | pointPair bruteForceParallel(slice*,point*> P) { 95 | auto A = sequence>(P.size()*P.size()); 96 | parallel_for(0, P.size(), [&](size_t i) { 97 | for(size_t j = 0; j < P.size(); ++ j) { 98 | if (i == j) { 99 | A[i+j*P.size()] = pointPair(P[i], P[j], floatMax); 100 | } else { 101 | A[i+j*P.size()] = pointPair(P[i], P[j]); 102 | } 103 | } 104 | }); 105 | auto R = *min_element(make_slice(A), [&](pointPair i, pointPair j) { return i.dist < j.dist; }); 106 | return R; 107 | } 108 | 109 | template 110 | pointPair bruteForceSerial(slice*,point*> P) { 111 | pointPair A = pointPair(P[0], P[1], floatMax); 112 | parallel_for(0, P.size(), [&](size_t i) { 113 | for(size_t j = i+1; j < P.size(); ++ j) { 114 | A.closer(P[i], P[j]); 115 | } 116 | }); 117 | return A; 118 | } 119 | 120 | // ************************************************************* 121 | // Helper functions 122 | // ************************************************************* 123 | 124 | // todo change double to appropriate type 125 | template 126 | struct findSlab { 127 | double xm, delta; 128 | int k; //dimension along which the slab is computed 129 | findSlab(double xmm, double deltaa, int kk): xm(xmm), delta(deltaa), k(kk) { 130 | if (k >= dim) { 131 | throw std::runtime_error("findSlab exceeds point dimension, abort"); } 132 | } 133 | bool operator() (const point i) { 134 | if (i.x[k] <= xm+delta && i.x[k] >= xm-delta) return true; 135 | else return false; 136 | } 137 | }; 138 | 139 | inline size_t intPow(size_t base, size_t exp) { 140 | size_t r = 1; 141 | for (size_t i=0; i 146 | inline size_t numToCheck() {return intPow(2, dim*2-1)-1;} 147 | 148 | // ************************************************************* 149 | // Divide conquer 150 | // ************************************************************* 151 | 152 | /** 153 | * Sequentially, computes the closest pair of P using a divide and conquer algorithm. 154 | * @param P a point sequence. 155 | * @param k dimensionality of the call, k = [0,dim) 156 | * @return the closest pair 157 | */ 158 | template 159 | pointPair divideConquerSerial(slice*,point*> P, int k) { 160 | using pt = point; 161 | using pp = pointPair; 162 | if (P.size() < 100) return bruteForceSerial(P); 163 | 164 | internal::seq_sort_inplace(P, [&](pt i, pt j){return i[k] < j[k];}, false); 165 | size_t xm = size_t(P.size()/2); 166 | double xmm = P[xm].x[k]; 167 | 168 | auto L = P.cut(0, xm); 169 | auto R = P.cut(xm, P.size()); 170 | pp Lr = divideConquerSerial(L, k); 171 | pp Rr = divideConquerSerial(R, k); 172 | pp better = Lr.dist < Rr.dist ? Lr : Rr; 173 | if (better.dist == 0) return better; 174 | 175 | //create slab in dimension k, of size np and stored in aux-memory Pp 176 | auto Pp = filter(P, findSlab(xmm, better.dist, k));// serial todo 177 | 178 | auto slabR = pp(); 179 | 180 | if (k >= 2) { 181 | slabR = divideConquerSerial(make_slice(Pp), k-1); 182 | } else if (k == 1) { 183 | internal::seq_sort_inplace(make_slice(Pp), [&](pt i, pt j){return i[0] < j[0];}, false); 184 | const size_t check = numToCheck(); 185 | for(size_t i = 0; i < Pp.size(); ++ i) { //for each point in the slab 186 | for (size_t j = 0; j < check; ++ j) { //check constant number of points after 187 | if (i+j+1 204 | pointPair divideConquerParallel(slice*,point*> P, int k) { 205 | using pt = point; 206 | using pp = pointPair; 207 | if (P.size() < 10000) return divideConquerSerial(P, k); 208 | 209 | sort_inplace(P, [&](pt i, pt j){return i[k] < j[k];}); 210 | size_t xm = size_t(P.size()/2); 211 | double xmm = P[xm].x[k]; 212 | 213 | auto L = P.cut(0, xm); 214 | auto R = P.cut(xm, P.size()); 215 | pp Lr, Rr; 216 | par_do([&](){Lr = divideConquerParallel(L, k);}, 217 | [&](){Rr = divideConquerParallel(R, k);}); 218 | pp better = Lr.dist < Rr.dist ? Lr : Rr; 219 | if (better.dist == 0) return better; 220 | 221 | //create slab in dimension k, of size np and stored in aux-memory Pp 222 | auto Pp = filter(P, findSlab(xmm, better.dist, k)); 223 | size_t np = Pp.size(); 224 | 225 | auto slabR = pp(P[0], P[1], floatMax); 226 | if (k >= 2) { 227 | //recursive case, solve the slab in one dimension lower 228 | slabR = divideConquerParallel(make_slice(Pp), k-1); 229 | } else if (k == 1) { 230 | //base case 231 | const size_t check = numToCheck(); 232 | if (np > 100) { 233 | //go parallel 234 | sort_inplace(make_slice(Pp), [&](pt i, pt j){return i[0] < j[0];}); 235 | auto A = sequence(np*check); 236 | parallel_for(0, np, [&](size_t i) { //for each point in the slab in parallel 237 | for (size_t j = 0; j < check; ++ j) { //check constant number of points after 238 | if (i+j+1(); 250 | for(size_t i = 0; i < Pp.size(); ++ i) { //for each point in the slab 251 | for (size_t j = 0; j < check; ++ j) { //check constant number of points after 252 | if (i+j+1 272 | pointPair closestPairDC(sequence>& P, bool serial = false) { 273 | using pt = point; 274 | auto Pp = sequence(P); 275 | pointPair r; 276 | 277 | if (serial) r = divideConquerSerial(make_slice(Pp), dim-1); 278 | else r = divideConquerParallel(make_slice(Pp), dim-1); 279 | 280 | //cout << "dist = " << r.dist << endl; 281 | 282 | //cout << "brute = " << bruteForceSerial(make_slice(P)).dist << endl; 283 | //cout << "brute = " << bruteForceParallel(make_slice(P)).dist << endl; 284 | return r; 285 | } 286 | 287 | } // End namespace pargeo 288 | -------------------------------------------------------------------------------- /src/delaunayTriangulation/incremental/oct_tree.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 | #include 24 | #include "parlay/parallel.h" 25 | #include "parlay/primitives.h" 26 | #include "delaunayTriangulation/geometry.h" 27 | #include "pargeo/getTime.h" 28 | 29 | namespace pbbsbench { 30 | 31 | // vtx must support v->pt 32 | // and v->pt must support pt.dimension(), pt[i], 33 | // (pt1 - pt2).Length(), pt1 + (pt2 - pt3) 34 | // pt1.minCoords(pt2), pt1.maxCoords(pt2), 35 | template 36 | struct oct_tree { 37 | 38 | using point = typename vtx::point_t; 39 | using uint = unsigned int; 40 | using box = std::pair; 41 | using indexed_point = std::pair; 42 | using slice_t = decltype(make_slice(parlay::sequence())); 43 | using slice_v = decltype(make_slice(parlay::sequence())); 44 | 45 | struct node { 46 | 47 | public: 48 | using leaf_seq = parlay::sequence; 49 | point center() {return centerv;} 50 | box Box() {return b;} 51 | size_t size() {return n;} 52 | bool is_leaf() {return L == nullptr;} 53 | node* Left() {return L;} 54 | node* Right() {return R;} 55 | node* Parent() {return parent;} 56 | leaf_seq& Vertices() {return P;}; 57 | 58 | // construct a leaf node with a sequence of points directly in it 59 | node(slice_t Pts) { 60 | n = Pts.size(); 61 | parent = nullptr; 62 | 63 | // strips off the integer tag, no longer needed 64 | P = leaf_seq(n); 65 | for (int i = 0; i < n; i++) 66 | P[i] = Pts[i].second; 67 | L = R = nullptr; 68 | b = get_box(P); 69 | set_center(); 70 | } 71 | 72 | // construct an internal binary node 73 | node(node* L, node* R) : L(L), R(R) { 74 | parent = nullptr; 75 | b = box(L->b.first.minCoords(R->b.first), 76 | L->b.second.maxCoords(R->b.second)); 77 | n = L->size() + R->size(); 78 | set_center(); 79 | } 80 | 81 | static node* new_leaf(slice_t Pts) { 82 | node* r = alloc_node(); 83 | new (r) node(Pts); 84 | return r; 85 | } 86 | 87 | static node* new_node(node* L, node* R) { 88 | node* nd = alloc_node(); 89 | new (nd) node(L, R); 90 | // both children point to this node as their parent 91 | L->parent = R->parent = nd; 92 | return nd; 93 | } 94 | 95 | ~node() { 96 | // need to collect in parallel 97 | parlay::par_do_if(n > 1000, 98 | [&] () { delete_tree(L);}, 99 | [&] () { delete_tree(R);}); 100 | } 101 | 102 | parlay::sequence flatten() { 103 | parlay::sequence r(n); 104 | flatten_rec(this, parlay::make_slice(r)); 105 | return r; 106 | } 107 | 108 | // map a function f(p,node_ptr) over the points, passing 109 | // in the point, and a pointer to the leaf node it is in. 110 | // f should return void 111 | template 112 | void map(F f) { 113 | if (is_leaf()) 114 | for (int i=0; i < size(); i++) f(P[i],this); 115 | else { 116 | parlay::par_do_if(n > 1000, 117 | [&] () {L->map(f);}, 118 | [&] () {R->map(f);}); 119 | } 120 | } 121 | 122 | size_t depth() { 123 | if (is_leaf()) return 0; 124 | else { 125 | size_t l, r; 126 | parlay::par_do_if(n > 1000, 127 | [&] () {l = L->depth();}, 128 | [&] () {r = R->depth();}); 129 | return 1 + std::max(l,r); 130 | } 131 | } 132 | 133 | // recursively frees the tree 134 | static void delete_tree(node* T) { 135 | if (T != nullptr) { 136 | T->~node(); 137 | node::free_node(T); 138 | } 139 | } 140 | 141 | // disable copy and move constructors/assignment since 142 | // they are dangerous with with free. 143 | node(const node&) = delete; 144 | node(node&&) = delete; 145 | node& operator=(node const&) = delete; 146 | node& operator=(node&&) = delete; 147 | 148 | private: 149 | 150 | size_t n; 151 | node *parent; 152 | node *L; 153 | node *R; 154 | box b; 155 | point centerv; 156 | leaf_seq P; 157 | 158 | void set_center() { 159 | centerv = b.first + (b.second-b.first)/2; 160 | } 161 | 162 | // uses the parlay memory manager, could be replaced will alloc/free 163 | static parlay::type_allocator node_allocator; 164 | static node* alloc_node() { 165 | return node_allocator.alloc();} 166 | static void free_node(node* T) { 167 | node_allocator.free(T);} 168 | 169 | static void flatten_rec(node *T, slice_v R) { 170 | if (T->is_leaf()) 171 | for (int i=0; i < T->size(); i++) 172 | R[i] = T->P[i]; 173 | else { 174 | size_t n_left = T->L->size(); 175 | size_t n = T->size(); 176 | parlay::par_do_if(n > 1000, 177 | [&] () {flatten_rec(T->L, R.cut(0, n_left));}, 178 | [&] () {flatten_rec(T->R, R.cut(n_left, n));}); 179 | } 180 | } 181 | }; 182 | 183 | // A unique pointer to a tree node to ensure the tree is 184 | // destructed when the pointer is, and that no copies are made. 185 | struct delete_tree {void operator() (node *T) const {node::delete_tree(T);}}; 186 | using tree_ptr = std::unique_ptr; 187 | 188 | // build a tre given a sequence of pointers to points 189 | template 190 | static tree_ptr build(Seq &P) { 191 | pargeo::timer t("oct_tree",false); 192 | int dims = (P[0]->pt).dimension(); 193 | auto pts = tag_points(P); 194 | t.next("tag"); 195 | node* r = build_recursive(make_slice(pts), dims*(key_bits/dims)); 196 | t.next("build"); 197 | return tree_ptr(r); 198 | } 199 | 200 | private: 201 | constexpr static int key_bits = 64; 202 | 203 | // takes a point, rounds each coordinate to an integer, and interleaves 204 | // the bits into "key_bits" total bits. 205 | // min_point is the minimmum x,y,z coordinate for all points 206 | // delta is the largest range of any of the three dimensions 207 | static size_t interleave_bits(point p, point min_point, double delta) { 208 | int dim = p.dimension(); 209 | int bits = key_bits/dim; 210 | uint maxval = (((size_t) 1) << bits) - 1; 211 | uint ip[dim]; 212 | for (int i = 0; i < dim; i++) 213 | ip[i] = floor(maxval * (p[i] - min_point[i])/delta); 214 | 215 | size_t r = 0; 216 | int loc = 0; 217 | for (int i =0; i < bits; i++) 218 | for (int d = 0; d < dim; d++) 219 | r = r | (((ip[d] >> i) & (size_t) 1) << (loc++)); 220 | return r; 221 | } 222 | 223 | // generates a box consisting of a lower left corner, 224 | // and an upper right corner. 225 | template 226 | static box get_box(Seq &V) { // parlay::sequence &V) { 227 | size_t n = V.size(); 228 | auto minmax = [&] (box x, box y) { 229 | return box(x.first.minCoords(y.first), 230 | x.second.maxCoords(y.second));}; 231 | 232 | // uses a delayed sequence to avoid making a copy 233 | auto pts = parlay::delayed_seq(n, [&] (size_t i) { 234 | return box(V[i]->pt, V[i]->pt);}); 235 | box identity = pts[0]; 236 | return parlay::reduce(pts, parlay::make_monoid(minmax,identity)); 237 | } 238 | 239 | // tags each point (actually a pointer to it), with an interger 240 | // consisting of the interleaved bits for the x,y,z coordinates. 241 | // Also sorts based the integer. 242 | static parlay::sequence tag_points(parlay::sequence &V) { 243 | pargeo::timer t("tag",false); 244 | size_t n = V.size(); 245 | int dims = (V[0]->pt).dimension(); 246 | 247 | // find box around points, and size along largest axis 248 | box b = get_box(V); 249 | double Delta = 0; 250 | for (int i = 0; i < dims; i++) 251 | Delta = std::max(Delta, b.second[i] - b.first[i]); 252 | t.next("get box"); 253 | 254 | auto points = parlay::delayed_seq(n, [&] (size_t i) -> indexed_point { 255 | return std::pair(interleave_bits(V[i]->pt, b.first, Delta), V[i]); 256 | }); 257 | 258 | auto less = [] (indexed_point a, indexed_point b) { 259 | return a.first < b.first;}; 260 | 261 | auto x = parlay::sort(points, less); 262 | t.next("tabulate and sort"); 263 | return x; 264 | } 265 | 266 | // each point is a pair consisting of an interleave integer along with 267 | // the pointer to the point. The bit specifies which bit of the integer 268 | // we are working on (starts at top, and goes down). 269 | static node* build_recursive(slice_t Pts, int bit) { 270 | size_t n = Pts.size(); 271 | if (n == 0) abort(); 272 | int cutoff = 20; 273 | 274 | // if run out of bit, or small then generate a leaf 275 | if (bit == 0 || n < cutoff) { 276 | return node::new_leaf(Pts); 277 | } else { 278 | 279 | // the following defines a less based on the bit 280 | size_t val = ((size_t) 1) << (bit - 1); 281 | size_t mask = (bit == 64) ? ~((size_t) 0) : ~(~((size_t) 0) << bit); 282 | auto less = [&] (indexed_point x) { 283 | return (x.first & mask) < val; 284 | }; 285 | // and then we binary search for the cut point 286 | size_t pos = parlay::internal::binary_search(Pts, less); 287 | 288 | // if all points are on one side, then move onto the next bit 289 | if (pos == 0 || pos == n) 290 | return build_recursive(Pts, bit - 1); 291 | 292 | // otherwise recurse on the two parts, also moving to next bit 293 | else { 294 | node *L, *R; 295 | parlay::par_do_if(n > 1000, 296 | [&] () {L = build_recursive(Pts.cut(0, pos), bit - 1);}, 297 | [&] () {R = build_recursive(Pts.cut(pos, n), bit - 1);}); 298 | return node::new_node(L,R); 299 | } 300 | } 301 | } 302 | 303 | }; 304 | 305 | } // End namespace pbbsbench 306 | --------------------------------------------------------------------------------