├── PCLDebug.props ├── PCLRelease.props ├── README.md ├── Teaser.cpp ├── Teaser.props ├── example_data ├── bunny │ ├── bun000.pcd │ └── bun090.pcd └── kitti │ ├── raw_000000.pcd │ └── raw_001100.pcd ├── linux_lib ├── getopt.c ├── getopt.h ├── sys │ └── time.h └── unistd.h ├── pmc ├── LICENSE.md ├── Makefile ├── README.md ├── libpmc.h ├── libpmc_test.cpp ├── pmc.h ├── pmc.jl ├── pmc.m ├── pmc.py ├── pmc_clique_utils.cpp ├── pmc_cores.cpp ├── pmc_driver.cpp ├── pmc_graph.cpp ├── pmc_graph.h ├── pmc_headers.h ├── pmc_heu.cpp ├── pmc_heu.h ├── pmc_input.h ├── pmc_lib.cpp ├── pmc_maxclique.cpp ├── pmc_maxclique.h ├── pmc_neigh_coloring.h ├── pmc_neigh_cores.h ├── pmc_utils.cpp ├── pmc_utils.h ├── pmc_vertex.h ├── pmcx_maxclique.cpp ├── pmcx_maxclique.h ├── pmcx_maxclique_basic.cpp └── pmcx_maxclique_basic.h ├── reg ├── RegBase.h ├── TRO_Utilities.h └── stdafx.h ├── teaser ├── CMakeLists.txt ├── include │ └── teaser │ │ ├── CTeaser.h │ │ ├── no_use │ │ └── ply_io.h │ │ ├── teaser_fpfh.h │ │ ├── teaser_geometry.h │ │ ├── teaser_graph.h │ │ ├── teaser_macros.h │ │ ├── teaser_matcher.h │ │ ├── teaser_registration.h │ │ ├── teaser_utility.h │ │ └── teaser_utils.h └── src │ ├── no_use │ ├── ply_io.cc │ └── teaser_fpfh.cc │ ├── teaser_graph.cc │ ├── teaser_matcher.cc │ └── teaser_registration.cc └── visResult.m /PCLDebug.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | D:\Program Files\PCL 1.8.1\include\pcl-1.8;D:\Program Files\PCL 1.8.1\3rdParty\Boost\include\boost-1_64;D:\Program Files\PCL 1.8.1\3rdParty\Qhull\include;D:\Program Files\PCL 1.8.1\3rdParty\FLANN\include;D:\Program Files\PCL 1.8.1\3rdParty\Eigen\eigen3;D:\Program Files\PCL 1.8.1\3rdParty\VTK\include\vtk-8.0;D:\Program Files\OpenNI2\Include;$(IncludePath) 7 | D:\Program Files\PCL 1.8.1\3rdParty\VTK\lib;D:\Program Files\PCL 1.8.1\3rdParty\FLANN\lib;D:\Program Files\PCL 1.8.1\3rdParty\Qhull\lib;D:\Program Files\PCL 1.8.1\3rdParty\Boost\lib;D:\Program Files\OpenNI2\Lib;D:\Program Files\PCL 1.8.1\lib;$(LibraryPath) 8 | 9 | 10 | 11 | pcl_common_debug.lib;pcl_features_debug.lib;pcl_filters_debug.lib;pcl_io_debug.lib;pcl_io_ply_debug.lib;pcl_kdtree_debug.lib;pcl_keypoints_debug.lib;pcl_ml_debug.lib;pcl_octree_debug.lib;pcl_outofcore_debug.lib;pcl_people_debug.lib;pcl_recognition_debug.lib;pcl_registration_debug.lib;pcl_sample_consensus_debug.lib;pcl_search_debug.lib;pcl_segmentation_debug.lib;pcl_stereo_debug.lib;pcl_surface_debug.lib;pcl_tracking_debug.lib;pcl_visualization_debug.lib;flann_cpp_s-gd.lib;flann_s-gd.lib;flann-gd.lib;libboost_atomic-vc140-mt-gd-1_64.lib;libboost_chrono-vc140-mt-gd-1_64.lib;libboost_container-vc140-mt-gd-1_64.lib;libboost_context-vc140-mt-gd-1_64.lib;libboost_coroutine-vc140-mt-gd-1_64.lib;libboost_date_time-vc140-mt-gd-1_64.lib;libboost_exception-vc140-mt-gd-1_64.lib;libboost_filesystem-vc140-mt-gd-1_64.lib;libboost_graph-vc140-mt-gd-1_64.lib;libboost_iostreams-vc140-mt-gd-1_64.lib;libboost_locale-vc140-mt-gd-1_64.lib;libboost_log-vc140-mt-gd-1_64.lib;libboost_log_setup-vc140-mt-gd-1_64.lib;libboost_math_c99-vc140-mt-gd-1_64.lib;libboost_math_c99f-vc140-mt-gd-1_64.lib;libboost_math_c99l-vc140-mt-gd-1_64.lib;libboost_math_tr1-vc140-mt-gd-1_64.lib;libboost_math_tr1f-vc140-mt-gd-1_64.lib;libboost_math_tr1l-vc140-mt-gd-1_64.lib;libboost_mpi-vc140-mt-gd-1_64.lib;libboost_prg_exec_monitor-vc140-mt-gd-1_64.lib;libboost_program_options-vc140-mt-gd-1_64.lib;libboost_random-vc140-mt-gd-1_64.lib;libboost_regex-vc140-mt-gd-1_64.lib;libboost_serialization-vc140-mt-gd-1_64.lib;libboost_signals-vc140-mt-gd-1_64.lib;libboost_system-vc140-mt-gd-1_64.lib;libboost_test_exec_monitor-vc140-mt-gd-1_64.lib;libboost_thread-vc140-mt-gd-1_64.lib;libboost_timer-vc140-mt-gd-1_64.lib;libboost_unit_test_framework-vc140-mt-gd-1_64.lib;libboost_wave-vc140-mt-gd-1_64.lib;libboost_wserialization-vc140-mt-gd-1_64.lib;qhull_d.lib;qhull_p_d.lib;qhull_r_d.lib;qhullcpp_d.lib;qhullstatic_d.lib;qhullstatic_r_d.lib;vtkalglib-8.0-gd.lib;vtkChartsCore-8.0-gd.lib;vtkCommonColor-8.0-gd.lib;vtkCommonComputationalGeometry-8.0-gd.lib;vtkCommonCore-8.0-gd.lib;vtkCommonDataModel-8.0-gd.lib;vtkCommonExecutionModel-8.0-gd.lib;vtkCommonMath-8.0-gd.lib;vtkCommonMisc-8.0-gd.lib;vtkCommonSystem-8.0-gd.lib;vtkCommonTransforms-8.0-gd.lib;vtkDICOMParser-8.0-gd.lib;vtkDomainsChemistry-8.0-gd.lib;vtkexoIIc-8.0-gd.lib;vtkexpat-8.0-gd.lib;vtkFiltersAMR-8.0-gd.lib;vtkFiltersCore-8.0-gd.lib;vtkFiltersExtraction-8.0-gd.lib;vtkFiltersFlowPaths-8.0-gd.lib;vtkFiltersGeneral-8.0-gd.lib;vtkFiltersGeneric-8.0-gd.lib;vtkFiltersGeometry-8.0-gd.lib;vtkFiltersHybrid-8.0-gd.lib;vtkFiltersHyperTree-8.0-gd.lib;vtkFiltersImaging-8.0-gd.lib;vtkFiltersModeling-8.0-gd.lib;vtkFiltersParallel-8.0-gd.lib;vtkFiltersParallelImaging-8.0-gd.lib;vtkFiltersProgrammable-8.0-gd.lib;vtkFiltersSelection-8.0-gd.lib;vtkFiltersSMP-8.0-gd.lib;vtkFiltersSources-8.0-gd.lib;vtkFiltersStatistics-8.0-gd.lib;vtkFiltersTexture-8.0-gd.lib;vtkFiltersVerdict-8.0-gd.lib;vtkfreetype-8.0-gd.lib;vtkGeovisCore-8.0-gd.lib;vtkhdf5-8.0-gd.lib;vtkhdf5_hl-8.0-gd.lib;vtkImagingColor-8.0-gd.lib;vtkImagingCore-8.0-gd.lib;vtkImagingFourier-8.0-gd.lib;vtkImagingGeneral-8.0-gd.lib;vtkImagingHybrid-8.0-gd.lib;vtkImagingMath-8.0-gd.lib;vtkImagingMorphological-8.0-gd.lib;vtkImagingSources-8.0-gd.lib;vtkImagingStatistics-8.0-gd.lib;vtkImagingStencil-8.0-gd.lib;vtkInfovisCore-8.0-gd.lib;vtkInfovisLayout-8.0-gd.lib;vtkInteractionImage-8.0-gd.lib;vtkInteractionStyle-8.0-gd.lib;vtkInteractionWidgets-8.0-gd.lib;vtkIOAMR-8.0-gd.lib;vtkIOCore-8.0-gd.lib;vtkIOEnSight-8.0-gd.lib;vtkIOExodus-8.0-gd.lib;vtkIOExport-8.0-gd.lib;vtkIOGeometry-8.0-gd.lib;vtkIOImage-8.0-gd.lib;vtkIOImport-8.0-gd.lib;vtkIOInfovis-8.0-gd.lib;vtkIOLegacy-8.0-gd.lib;vtkIOLSDyna-8.0-gd.lib;vtkIOMINC-8.0-gd.lib;vtkIOMovie-8.0-gd.lib;vtkIONetCDF-8.0-gd.lib;vtkIOParallel-8.0-gd.lib;vtkIOPLY-8.0-gd.lib;vtkIOSQL-8.0-gd.lib;vtkIOVideo-8.0-gd.lib;vtkIOXML-8.0-gd.lib;vtkIOXMLParser-8.0-gd.lib;vtkjpeg-8.0-gd.lib;vtkjsoncpp-8.0-gd.lib;vtklibxml2-8.0-gd.lib;vtkmetaio-8.0-gd.lib;vtkNetCDF-8.0-gd.lib;vtknetcdf_c++-gd.lib;vtkoggtheora-8.0-gd.lib;vtkParallelCore-8.0-gd.lib;vtkpng-8.0-gd.lib;vtkproj4-8.0-gd.lib;vtkRenderingAnnotation-8.0-gd.lib;vtkRenderingContext2D-8.0-gd.lib;vtkRenderingCore-8.0-gd.lib;vtkRenderingFreeType-8.0-gd.lib;vtkRenderingImage-8.0-gd.lib;vtkRenderingLabel-8.0-gd.lib;vtkRenderingLOD-8.0-gd.lib;vtkRenderingVolume-8.0-gd.lib;vtksqlite-8.0-gd.lib;vtksys-8.0-gd.lib;vtktiff-8.0-gd.lib;vtkverdict-8.0-gd.lib;vtkViewsContext2D-8.0-gd.lib;vtkViewsCore-8.0-gd.lib;vtkViewsInfovis-8.0-gd.lib;vtkzlib-8.0-gd.lib;OpenNI2.lib;%(AdditionalDependencies) 12 | 13 | 14 | false 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /PCLRelease.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | D:\Program Files\PCL 1.8.1\include\pcl-1.8;D:\Program Files\PCL 1.8.1\3rdParty\Boost\include\boost-1_64;D:\Program Files\PCL 1.8.1\3rdParty\Qhull\include;D:\Program Files\PCL 1.8.1\3rdParty\FLANN\include;D:\Program Files\PCL 1.8.1\3rdParty\Eigen\eigen3;D:\Program Files\PCL 1.8.1\3rdParty\VTK\include\vtk-8.0;D:\Program Files\OpenNI2\Include;$(IncludePath) 7 | D:\Program Files\PCL 1.8.1\3rdParty\VTK\lib;D:\Program Files\PCL 1.8.1\3rdParty\FLANN\lib;D:\Program Files\PCL 1.8.1\3rdParty\Qhull\lib;D:\Program Files\PCL 1.8.1\3rdParty\Boost\lib;D:\Program Files\OpenNI2\Lib;D:\Program Files\PCL 1.8.1\lib;$(LibraryPath) 8 | 9 | 10 | 11 | pcl_common_release.lib;pcl_features_release.lib;pcl_filters_release.lib;pcl_io_release.lib;pcl_io_ply_release.lib;pcl_kdtree_release.lib;pcl_keypoints_release.lib;pcl_ml_release.lib;pcl_octree_release.lib;pcl_outofcore_release.lib;pcl_people_release.lib;pcl_recognition_release.lib;pcl_registration_release.lib;pcl_sample_consensus_release.lib;pcl_search_release.lib;pcl_segmentation_release.lib;pcl_stereo_release.lib;pcl_surface_release.lib;pcl_tracking_release.lib;pcl_visualization_release.lib;flann_cpp_s.lib;flann_s.lib;flann.lib;libboost_atomic-vc140-mt-1_64.lib;libboost_chrono-vc140-mt-1_64.lib;libboost_container-vc140-mt-1_64.lib;libboost_context-vc140-mt-1_64.lib;libboost_coroutine-vc140-mt-1_64.lib;libboost_date_time-vc140-mt-1_64.lib;libboost_exception-vc140-mt-1_64.lib;libboost_filesystem-vc140-mt-1_64.lib;libboost_graph-vc140-mt-1_64.lib;libboost_iostreams-vc140-mt-1_64.lib;libboost_locale-vc140-mt-1_64.lib;libboost_log-vc140-mt-1_64.lib;libboost_log_setup-vc140-mt-1_64.lib;libboost_math_c99-vc140-mt-1_64.lib;libboost_math_c99f-vc140-mt-1_64.lib;libboost_math_c99l-vc140-mt-1_64.lib;libboost_math_tr1-vc140-mt-1_64.lib;libboost_math_tr1f-vc140-mt-1_64.lib;libboost_math_tr1l-vc140-mt-1_64.lib;libboost_mpi-vc140-mt-1_64.lib;libboost_prg_exec_monitor-vc140-mt-1_64.lib;libboost_program_options-vc140-mt-1_64.lib;libboost_random-vc140-mt-1_64.lib;libboost_regex-vc140-mt-1_64.lib;libboost_serialization-vc140-mt-1_64.lib;libboost_signals-vc140-mt-1_64.lib;libboost_system-vc140-mt-1_64.lib;libboost_test_exec_monitor-vc140-mt-1_64.lib;libboost_thread-vc140-mt-1_64.lib;libboost_timer-vc140-mt-1_64.lib;libboost_unit_test_framework-vc140-mt-1_64.lib;libboost_wave-vc140-mt-1_64.lib;libboost_wserialization-vc140-mt-1_64.lib;qhullstatic.lib;qhull.lib;qhull_p.lib;qhull_r.lib;qhullcpp.lib;qhullstatic_r.lib;vtkalglib-8.0.lib;vtkChartsCore-8.0.lib;vtkCommonColor-8.0.lib;vtkCommonComputationalGeometry-8.0.lib;vtkCommonCore-8.0.lib;vtkCommonDataModel-8.0.lib;vtkCommonExecutionModel-8.0.lib;vtkCommonMath-8.0.lib;vtkCommonMisc-8.0.lib;vtkCommonSystem-8.0.lib;vtkCommonTransforms-8.0.lib;vtkDICOMParser-8.0.lib;vtkDomainsChemistry-8.0.lib;vtkexoIIc-8.0.lib;vtkexpat-8.0.lib;vtkFiltersAMR-8.0.lib;vtkFiltersCore-8.0.lib;vtkFiltersExtraction-8.0.lib;vtkFiltersFlowPaths-8.0.lib;vtkFiltersGeneral-8.0.lib;vtkFiltersGeneric-8.0.lib;vtkFiltersGeometry-8.0.lib;vtkFiltersHybrid-8.0.lib;vtkFiltersHyperTree-8.0.lib;vtkFiltersImaging-8.0.lib;vtkFiltersModeling-8.0.lib;vtkFiltersParallel-8.0.lib;vtkFiltersParallelImaging-8.0.lib;vtkFiltersProgrammable-8.0.lib;vtkFiltersSelection-8.0.lib;vtkFiltersSMP-8.0.lib;vtkFiltersSources-8.0.lib;vtkFiltersStatistics-8.0.lib;vtkFiltersTexture-8.0.lib;vtkFiltersVerdict-8.0.lib;vtkfreetype-8.0.lib;vtkGeovisCore-8.0.lib;vtkhdf5-8.0.lib;vtkhdf5_hl-8.0.lib;vtkImagingColor-8.0.lib;vtkImagingCore-8.0.lib;vtkImagingFourier-8.0.lib;vtkImagingGeneral-8.0.lib;vtkImagingHybrid-8.0.lib;vtkImagingMath-8.0.lib;vtkImagingMorphological-8.0.lib;vtkImagingSources-8.0.lib;vtkImagingStatistics-8.0.lib;vtkImagingStencil-8.0.lib;vtkInfovisCore-8.0.lib;vtkInfovisLayout-8.0.lib;vtkInteractionImage-8.0.lib;vtkInteractionStyle-8.0.lib;vtkInteractionWidgets-8.0.lib;vtkIOAMR-8.0.lib;vtkIOCore-8.0.lib;vtkIOEnSight-8.0.lib;vtkIOExodus-8.0.lib;vtkIOExport-8.0.lib;vtkIOGeometry-8.0.lib;vtkIOImage-8.0.lib;vtkIOImport-8.0.lib;vtkIOInfovis-8.0.lib;vtkIOLegacy-8.0.lib;vtkIOLSDyna-8.0.lib;vtkIOMINC-8.0.lib;vtkIOMovie-8.0.lib;vtkIONetCDF-8.0.lib;vtkIOParallel-8.0.lib;vtkIOPLY-8.0.lib;vtkIOSQL-8.0.lib;vtkIOVideo-8.0.lib;vtkIOXML-8.0.lib;vtkIOXMLParser-8.0.lib;vtkjpeg-8.0.lib;vtkjsoncpp-8.0.lib;vtklibxml2-8.0.lib;vtkmetaio-8.0.lib;vtkNetCDF-8.0.lib;vtknetcdf_c++.lib;vtkoggtheora-8.0.lib;vtkParallelCore-8.0.lib;vtkpng-8.0.lib;vtkproj4-8.0.lib;vtkRenderingAnnotation-8.0.lib;vtkRenderingContext2D-8.0.lib;vtkRenderingCore-8.0.lib;vtkRenderingFreeType-8.0.lib;vtkRenderingImage-8.0.lib;vtkRenderingLabel-8.0.lib;vtkRenderingLOD-8.0.lib;vtkRenderingVolume-8.0.lib;vtksqlite-8.0.lib;vtksys-8.0.lib;vtktiff-8.0.lib;vtkverdict-8.0.lib;vtkViewsContext2D-8.0.lib;vtkViewsCore-8.0.lib;vtkViewsInfovis-8.0.lib;vtkzlib-8.0.lib;OpenNI2.lib;%(AdditionalDependencies) 12 | 13 | 14 | false 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WinTeaser 2 | Teaser++[1] is a well-performed point cloud registration algorithm which can be used in computer vision and robotics. However, the original Teaser++ operates in Linux (https://github.com/MIT-SPARK/TEASER-plusplus). This reporsitory configures Teaser++ in Windows with the help of Visual Studio 2015. The linux_lib contains , , and which not exist in Windows OS. If you want to explore more linux libaries, please check https://github.com/robinrowe/libunistd. 3 | 4 | The main procedures are as follows: 5 | 6 | 1. Install PCL-all-in-one-installer_1.8.1. 7 | 8 | 2. Configure PCLRelease/PCLDebug.props and Teaser.props in VS 2015. 9 | 10 | 3. Compile the whole project. The main() lies in Teaser.cpp. 11 | 12 | 4. run visResult.m using MATLAB to see the quality of registration. 13 | 14 | Enjoy it! 15 | 16 | PS: One of the major difference between WinTeaser and Teaser++ lies in the way of computing the FPFH descriptors. Teaser++ computes one FPFH descriptor for each point in point cloud. which is quite computationally inefficiency. Therefore in WinTeaser, the raw cloud is firstly subsampled into cloud_key_points, which is sparse than original cloud. Subsequently, FPFH descriptor is computed for each point in cloud_key_point. Notice that when computing FPFH, the input surface of FPFH_Estimator is still cloud instead of sub-sampled cloud_key_point, which ensures the accuracy of FPFH descriptor has no difference with Teaser++. The modified FPFH computation is in teaser/include/teaser_fpfh.h. 17 | 18 | PPS: There are some other issues worthy to be mentioned. Firstly, the pmc library seems to be wrong when the number of point pairs is too large. This is because pmc uses omp library. If you meet this issue, just use ordinary for loop instead of omp for loop. Secondly, the running time of teaser can be hours since the maximum clique searching in pmc library. To save the time, you can change the configuration of Teaser. There is a maximum pmc search time 19 | in the constructor of Teaser. Both of the above issues may be occurred when the number of input point pairs is large (I think it is around 10000). Therefore, you can enlarge the feature_radius to reduce the point pair number. 20 | 21 | Disclaimer: I strongly recommend to use the original TEASER++ in Linux since it is well maintained and has detailed document. WinTeaser is mainly used for users which are unfamilar with Linux OS and want to quickly test TEASER++ on their own dataset. 22 | 23 | [1] Yang, H., Shi, J., & Carlone, L. (2020). TEASER: Fast and Certifiable Point Cloud Registration. IEEE Transactions on Robotics. 24 | -------------------------------------------------------------------------------- /Teaser.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DrGabor/WinTeaser/2b216e86b7286de678722a2d1ca75b2fbd194deb/Teaser.cpp -------------------------------------------------------------------------------- /Teaser.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | D:\Projects\TEASER-plusplus\teaser\include;D:\Projects\TEASER-plusplus\teaser\src;D:\Projects\TEASER-plusplus;D:\Projects\TEASER-plusplus\wingetopt\src;$(IncludePath) 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example_data/bunny/bun000.pcd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DrGabor/WinTeaser/2b216e86b7286de678722a2d1ca75b2fbd194deb/example_data/bunny/bun000.pcd -------------------------------------------------------------------------------- /example_data/bunny/bun090.pcd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DrGabor/WinTeaser/2b216e86b7286de678722a2d1ca75b2fbd194deb/example_data/bunny/bun090.pcd -------------------------------------------------------------------------------- /example_data/kitti/raw_000000.pcd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DrGabor/WinTeaser/2b216e86b7286de678722a2d1ca75b2fbd194deb/example_data/kitti/raw_000000.pcd -------------------------------------------------------------------------------- /example_data/kitti/raw_001100.pcd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DrGabor/WinTeaser/2b216e86b7286de678722a2d1ca75b2fbd194deb/example_data/kitti/raw_001100.pcd -------------------------------------------------------------------------------- /linux_lib/getopt.h: -------------------------------------------------------------------------------- 1 | #ifndef __GETOPT_H__ 2 | /** 3 | * DISCLAIMER 4 | * This file has no copyright assigned and is placed in the Public Domain. 5 | * This file is a part of the w64 mingw-runtime package. 6 | * 7 | * The w64 mingw-runtime package and its code is distributed in the hope that it 8 | * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR 9 | * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to 10 | * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 11 | */ 12 | 13 | #define __GETOPT_H__ 14 | 15 | /* All the headers include this file. */ 16 | #include 17 | 18 | #if defined( WINGETOPT_SHARED_LIB ) 19 | # if defined( BUILDING_WINGETOPT_DLL ) 20 | # define WINGETOPT_API __declspec(dllexport) 21 | # else 22 | # define WINGETOPT_API __declspec(dllimport) 23 | # endif 24 | #else 25 | # define WINGETOPT_API 26 | #endif 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | WINGETOPT_API extern int optind; /* index of first non-option in argv */ 33 | WINGETOPT_API extern int optopt; /* single option character, as parsed */ 34 | WINGETOPT_API extern int opterr; /* flag to enable built-in diagnostics... */ 35 | /* (user may set to zero, to suppress) */ 36 | 37 | WINGETOPT_API extern char *optarg; /* pointer to argument of current option */ 38 | 39 | extern int getopt(int nargc, char * const *nargv, const char *options); 40 | 41 | #ifdef _BSD_SOURCE 42 | /* 43 | * BSD adds the non-standard `optreset' feature, for reinitialisation 44 | * of `getopt' parsing. We support this feature, for applications which 45 | * proclaim their BSD heritage, before including this header; however, 46 | * to maintain portability, developers are advised to avoid it. 47 | */ 48 | # define optreset __mingw_optreset 49 | extern int optreset; 50 | #endif 51 | #ifdef __cplusplus 52 | } 53 | #endif 54 | /* 55 | * POSIX requires the `getopt' API to be specified in `unistd.h'; 56 | * thus, `unistd.h' includes this header. However, we do not want 57 | * to expose the `getopt_long' or `getopt_long_only' APIs, when 58 | * included in this manner. Thus, close the standard __GETOPT_H__ 59 | * declarations block, and open an additional __GETOPT_LONG_H__ 60 | * specific block, only when *not* __UNISTD_H_SOURCED__, in which 61 | * to declare the extended API. 62 | */ 63 | #endif /* !defined(__GETOPT_H__) */ 64 | 65 | #if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) 66 | #define __GETOPT_LONG_H__ 67 | 68 | #ifdef __cplusplus 69 | extern "C" { 70 | #endif 71 | 72 | struct option /* specification for a long form option... */ 73 | { 74 | const char *name; /* option name, without leading hyphens */ 75 | int has_arg; /* does it take an argument? */ 76 | int *flag; /* where to save its status, or NULL */ 77 | int val; /* its associated status value */ 78 | }; 79 | 80 | enum /* permitted values for its `has_arg' field... */ 81 | { 82 | no_argument = 0, /* option never takes an argument */ 83 | required_argument, /* option always requires an argument */ 84 | optional_argument /* option may take an argument */ 85 | }; 86 | 87 | extern int getopt_long(int nargc, char * const *nargv, const char *options, 88 | const struct option *long_options, int *idx); 89 | extern int getopt_long_only(int nargc, char * const *nargv, const char *options, 90 | const struct option *long_options, int *idx); 91 | /* 92 | * Previous MinGW implementation had... 93 | */ 94 | #ifndef HAVE_DECL_GETOPT 95 | /* 96 | * ...for the long form API only; keep this for compatibility. 97 | */ 98 | # define HAVE_DECL_GETOPT 1 99 | #endif 100 | 101 | #ifdef __cplusplus 102 | } 103 | #endif 104 | 105 | #endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */ 106 | -------------------------------------------------------------------------------- /linux_lib/sys/time.h: -------------------------------------------------------------------------------- 1 | #include -------------------------------------------------------------------------------- /linux_lib/unistd.h: -------------------------------------------------------------------------------- 1 | #ifndef _UNISTD_H 2 | #define _UNISTD_H 3 | #include 4 | #include 5 | 6 | #endif -------------------------------------------------------------------------------- /pmc/LICENSE.md: -------------------------------------------------------------------------------- 1 | License 2 | ------- 3 | **Parallel Maximum Clique (PMC) Library**, 4 | Copyright (C) 2012-2013: Ryan A. Rossi, All rights reserved. 5 | 6 | >This program is free software: you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation, either version 3 of the License, or 9 | (at your option) any later version. 10 | 11 | >This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | >You should have received a copy of the GNU General Public License 17 | along with this program. If not, see . 18 | 19 | If used, please cite the following manuscript: 20 | 21 | Ryan A. Rossi, David F. Gleich, Assefaw H. Gebremedhin, Md. Mostofa 22 | Patwary, A Fast Parallel Maximum Clique Algorithm for Large Sparse Graphs 23 | and Temporal Strong Components, arXiv 2013 24 | -------------------------------------------------------------------------------- /pmc/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for PMC 3 | # 4 | # Ryan A. Rossi 5 | # Copyright, 2012-2016 6 | # 7 | 8 | .KEEP_STATE: 9 | 10 | all: pmc 11 | 12 | #OPTFLAGS = -g -D _GLIBCXX_DEBUG 13 | OPTFLAGS = -O3 14 | CFLAGS = $(OPTFLAGS) -fPIC 15 | #CFLAGS += -D_GLIBCXX_PARALLEL 16 | #CFLAGS += -floop-parallelize-all -ftree-loop-distribution 17 | 18 | 19 | CXX = g++ 20 | H_FILES = pmc.h 21 | 22 | 23 | .cpp.o: 24 | $(CXX) $(CFLAGS) -c $< 25 | 26 | IO_SRC = pmc_utils.cpp \ 27 | pmc_graph.cpp \ 28 | pmc_clique_utils.cpp 29 | 30 | PMC_SRC = pmc_heu.cpp \ 31 | pmc_maxclique.cpp \ 32 | pmcx_maxclique.cpp \ 33 | pmcx_maxclique_basic.cpp 34 | 35 | BOUND_LIB_SRC = pmc_cores.cpp 36 | 37 | PMC_MAIN = pmc_driver.cpp 38 | 39 | OBJ_PMC = $(PMC_MAIN:%.cpp=%.o) $(IO_SRC) $(PMC_SRC) $(BOUND_LIB_SRC) 40 | $(OBJ_PMC): $(H_FILES) Makefile 41 | pmc: $(OBJ_PMC) $(H_FILES) 42 | $(CXX) $(CFLAGS) -o pmc $(OBJ_PMC) -fopenmp 43 | 44 | libpmc.so: $(IO_SRC) $(PMC_SRC) $(BOUND_LIB_SRC) $(H_FILES) pmc_lib.cpp 45 | $(CXX) -static-libstdc++ $(CFLAGS) -shared -o libpmc.so \ 46 | $(IO_SRC) $(PMC_SRC) $(BOUND_LIB_SRC) pmc_lib.cpp -fopenmp 47 | 48 | libpmc_test: libpmc.so libpmc_test.cpp 49 | $(CXX) libpmc_test.cpp ./libpmc.so -o libpmc_test 50 | ./libpmc_test 51 | 52 | clean: 53 | rm -rf *.o pmc libpmc.so 54 | -------------------------------------------------------------------------------- /pmc/README.md: -------------------------------------------------------------------------------- 1 | Parallel Maximum Clique (PMC) Library 2 | ===================================== 3 | 4 | In short, a parameterized high performance library for computing maximum cliques in large sparse graphs. 5 | 6 | Finding maximum cliques, k-cliques, and temporal strong components are in general NP-hard. 7 | Yet, these can be computed fast in most social and information networks. 8 | The PMC library is designed to be fast for solving these problems. 9 | Algorithms in the PMC library are easily adaptable for use with a variety of orderings, heuristic strategies, and bounds. 10 | 11 | * **Maximum clique:** Given a simple undirected graph G and a number k, output the clique of largest size. 12 | * **K-clique:** In k-clique, the problem is to find a clique of size k if one exists. 13 | * **Largest temporal-scc:** Given a temporal graph G, a temporal strong component is a set of vertices where all temporal paths exist between the vertices in that set. The Largest TSCC problem is to find the largest among all the temporal strong components. 14 | 15 | 16 | 17 | Features 18 | -------- 19 | 0. General framework for parallel maximum clique algorithms 20 | 1. Optimized to be fast for large sparse graphs 21 | + Algorithms tested on networks of 1.8 billion edges 22 | 2. Set of fast heuristics shown to give accurate approximations 23 | 3. Algorithms for computing Temporal Strongly Connected Components (TSCC) of large dynamic networks 24 | 4. Parameterized for computing k-cliques as fast as possible 25 | 5. Includes a variety of tight linear time bounds for the maximum clique problem 26 | 6. Ordering of vertices for each algorithm can be selected at runtime 27 | 7. Dynamically reduces the graph representation periodically as vertices are pruned or searched 28 | + Lowers memory-requirements for massive graphs, increases speed, and has caching benefits 29 | 30 | 31 | Synopsis 32 | --------- 33 | 34 | ### Setup 35 | First, you'll need to compile the parallel maximum clique library. 36 | 37 | $ cd path/to/pmc/ 38 | $ make 39 | 40 | Afterwards, the following should work: 41 | 42 | # compute maximum clique using the full algorithm `-a 0` 43 | ./pmc -f data/socfb-Texas84.mtx -a 0 44 | 45 | 46 | *PMC* has been tested on Ubuntu linux (10.10 tested) and Mac OSX (Lion tested) with gcc-mp-4.7 and gcc-mp-4.5.4 47 | 48 | Please let me know if you run into any issues. 49 | 50 | 51 | 52 | ### Input file format 53 | + Matrix Market Coordinate Format (symmetric) 54 | For details see: 55 | 56 | %%MatrixMarket matrix coordinate pattern symmetric 57 | 4 4 6 58 | 2 1 59 | 3 1 60 | 3 2 61 | 4 1 62 | 4 2 63 | 4 3 64 | 65 | 66 | + Edge list (symmetric and unweighted): 67 | Codes for transforming the graph into the correct format are provided in the experiments directory. 68 | 69 | 70 | Overview 71 | --------- 72 | 73 | The parallel maximum clique algorithms use tight bounds that are fast to compute. 74 | A few of those are listed below. 75 | 76 | * K-cores 77 | * Degree 78 | * Neighborhood cores 79 | * Greedy coloring 80 | 81 | All bounds are dynamically updated. 82 | 83 | Examples of the three main maximum clique algorithms are given below. 84 | Each essentially builds on the other. 85 | 86 | # uses the four basic k-core pruning steps 87 | ./pmc -f ../pmc/data/output/socfb-Stanford3.mtx -a 2 88 | 89 | # k-core pruning and greedy coloring 90 | ./pmc -f ../pmc/data/output/socfb-Stanford3.mtx -a 1 91 | 92 | # neighborhood core pruning (and ordering for greedy coloring) 93 | ./pmc -f ../pmc/data/output/socfb-Stanford3.mtx -a 0 94 | 95 | 96 | 97 | 98 | 99 | ### Dynamic graph reduction 100 | 101 | The reduction wait parameter `-r` below is set to be 1 second (default = 4 seconds). 102 | 103 | ./pmc -f data/sanr200-0-9.mtx -a 0 -t 2 -r 1 104 | 105 | In some cases, it may make sense to turn off the explicit graph reduction. 106 | This is done by setting the reduction wait time '-r' to be very large. 107 | 108 | # Set the reduction wait parameter 109 | ./pmc -f data/socfb-Stanford3.mtx -a 0 -t 2 -r 999 110 | 111 | 112 | 113 | 114 | 115 | 116 | ### Orderings 117 | 118 | The PMC algorithms are easily adapted to use various ordering strategies. 119 | To prescribe a vertex ordering, use the -o option with one of the following: 120 | + `deg` 121 | + `kcore` 122 | + `dual_deg`     orders vertices by the sum of degrees from neighbors 123 | + `dual_kcore`  orders vertices by the sum of core numbers from neighbors 124 | + `kcore_deg`    vertices are ordered by the weight k(v)d(v) 125 | + `rand`             randomized ordering of vertices 126 | 127 | 128 | 129 | ##### Direction of ordering 130 | 131 | Vertices are searched by default in increasing order, to search vertices in decreasing order, use the `d` option: 132 | 133 | ./pmc -f data/p-hat700-2.mtx -a 0 -d 134 | 135 | 136 | 137 | 138 | ### Heuristic 139 | The fast heuristic may also be customized to use various greedy selection strategies. 140 | This is done by using `-h` with one of the following: 141 | 142 | + `deg` 143 | + `kcore` 144 | + `kcore_deg`    select vertex that maximizes k(v)d(v) 145 | + `rand`             randomly select vertices 146 | 147 | 148 | #### Terminate after applying the heuristic 149 | Approximate the maximum clique using _ONLY_ the heuristic by not setting the exact algorithm via the `-a [num]` option. 150 | For example: 151 | 152 | ./pmc -f data/sanr200-0-9.mtx -h deg 153 | 154 | #### Turning the heuristic off 155 | 156 | # heuristic is turned off by setting `-h 0`. 157 | ./pmc -f data/tscc_enron-only.mtx -h 0 -a 0 158 | 159 | 160 | 161 | ### K-clique 162 | 163 | The parallel maximum clique algorithms have also been parameterized to find cliques of size k. 164 | This routine is useful for many tasks in network analysis such as mining graphs and community detection. 165 | 166 | # Computes a clique of size 50 from the Stanford facebook network 167 | ./pmc -f data/socfb-Stanford3.mtx -a 0 -k 50 168 | 169 | 170 | using `-o rand` to find potentially different cliques of a certain size 171 | 172 | # Computes a clique of size 36 from sanr200-0-9 173 | ./pmc -f data/sanr200-0-9.mtx -a 0 -k 36 -o rand 174 | 175 | 176 | 177 | Terms and conditions 178 | -------------------- 179 | Please feel free to use these codes. We only ask that you cite: 180 | 181 | Ryan A. Rossi, David F. Gleich, Assefaw H. Gebremedhin, Md. Mostofa Patwary, 182 | A Fast Parallel Maximum Clique Algorithm for Large Sparse Graphs and Temporal 183 | Strong Components, arXiv preprint 1302.6256, 2013. 184 | 185 | _These codes are research prototypes and may not work for you. No promises. But do email if you run into problems._ 186 | 187 | 188 | Copyright 2011-2013, Ryan A. Rossi. All rights reserved. 189 | -------------------------------------------------------------------------------- /pmc/libpmc.h: -------------------------------------------------------------------------------- 1 | int max_clique(long long nedges, int *ei, int *ej, int index_offset, 2 | int outsize, int *clique); 3 | -------------------------------------------------------------------------------- /pmc/libpmc_test.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | extern "C" { 7 | 8 | // a list of edges, where index_offset is the starting index 9 | int max_clique(long long nedges, int *ei, int *ej, int index_offset, 10 | int outsize, int *clique); 11 | 12 | }; 13 | 14 | void test1() { 15 | // test a triangle 16 | int ei[] = {0, 0, 1}; 17 | int ej[] = {1, 2, 2}; 18 | int output[3] = {0, 0, 0}; 19 | 20 | int C = max_clique(3, ej, ei, 0, 3, output); 21 | if (C != 3) { 22 | fprintf(stderr, "Test failed"); 23 | exit(-1); 24 | } 25 | } 26 | 27 | int main(int argc, char **argv) { 28 | test1(); 29 | }; 30 | -------------------------------------------------------------------------------- /pmc/pmc.h: -------------------------------------------------------------------------------- 1 | /** 2 | ============================================================================ 3 | Name : Parallel Maximum Clique (PMC) Library 4 | Author : Ryan A. Rossi (rrossi@purdue.edu) 5 | Description : A general high-performance parallel framework for computing 6 | maximum cliques. The library is designed to be fast for large 7 | sparse graphs. 8 | 9 | Copyright (C) 2012-2013, Ryan A. Rossi, All rights reserved. 10 | 11 | Please cite the following paper if used: 12 | Ryan A. Rossi, David F. Gleich, Assefaw H. Gebremedhin, Md. Mostofa 13 | Patwary, A Fast Parallel Maximum Clique Algorithm for Large Sparse Graphs 14 | and Temporal Strong Components, arXiv preprint 1302.6256, 2013. 15 | 16 | See http://ryanrossi.com/pmc for more information. 17 | ============================================================================ 18 | */ 19 | 20 | #ifndef __PMC_H__ 21 | #define __PMC_H__ 22 | 23 | #include "pmc_headers.h" 24 | #include "pmc_input.h" 25 | #include "pmc_utils.h" 26 | 27 | #include "pmc_heu.h" 28 | #include "pmc_maxclique.h" 29 | #include "pmcx_maxclique.h" 30 | #include "pmcx_maxclique_basic.h" 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /pmc/pmc.jl: -------------------------------------------------------------------------------- 1 | module PMC 2 | 3 | #= 4 | int max_clique(long long nedges, int *ei, int *ej, int index_offset, 5 | int outsize, int *clique); 6 | =# 7 | 8 | const libpmc = joinpath(dirname(@Base.__FILE__),"libpmc") 9 | 10 | function pmc(A::SparseMatrixCSC) 11 | maxd = Int(maximum(sum(spones(A),1))) 12 | ei,ej = findnz(tril(A,1)) 13 | 14 | ei = map(Int32,ei) 15 | ej = map(Int32,ej) 16 | 17 | offset = Cint(1) 18 | 19 | outsize = maxd 20 | output = zeros(Int32,maxd) 21 | 22 | clique_size = ccall( 23 | (:max_clique, libpmc), Cint, 24 | (Clonglong, Ptr{Cint}, Ptr{Cint}, Cint, Cint, Ptr{Cint}), 25 | length(ei), ei, ej, offset, outsize, output) 26 | 27 | return map(Int64, output[1:clique_size]) 28 | end 29 | 30 | end 31 | 32 | #@show PMC.pmc(sprandn(10000,10000,10/10000)) 33 | 34 | -------------------------------------------------------------------------------- /pmc/pmc.m: -------------------------------------------------------------------------------- 1 | function max_clique = pmc(A) 2 | maxd = int32(full(max(sum(spones(A),1)))); 3 | [ei,ej] = find(tril(A,1)); 4 | ei = int32(ei); 5 | ej = int32(ej); 6 | loadlibrary('libpmc'); 7 | offset = 1; 8 | outsize = maxd; 9 | output = int32(zeros(maxd,1)); 10 | eiPtr = libpointer('int32Ptr',ei); 11 | ejPtr = libpointer('int32Ptr',ej); 12 | outputPtr = libpointer('int32Ptr',output); 13 | clique_size = calllib('libpmc','max_clique',size(ei,1),eiPtr,ejPtr,offset,... 14 | outsize,outputPtr); 15 | output = get(outputPtr,'Value'); 16 | max_clique = output(1:clique_size); 17 | unloadlibrary libpmc; -------------------------------------------------------------------------------- /pmc/pmc.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from numpy.ctypeslib import ndpointer 3 | import ctypes 4 | 5 | def pmc(ei,ej,nnodes,nnedges): #ei, ej is edge list whose index starts from 0 6 | degrees = np.zeros(nnodes,dtype = np.int32) 7 | new_ei = [] 8 | new_ej = [] 9 | for i in range(nnedges): 10 | degrees[ei[i]] += 1 11 | if ej[i] <= ei[i] + 1: 12 | new_ei.append(ei[i]) 13 | new_ej.append(ej[i]) 14 | maxd = max(degrees) 15 | offset = 0 16 | new_ei = np.array(new_ei,dtype = np.int32) 17 | new_ej = np.array(new_ej,dtype = np.int32) 18 | outsize = maxd 19 | output = np.zeros(maxd,dtype = np.int32) 20 | lib = ctypes.cdll.LoadLibrary("libpmc.dylib") 21 | fun = lib.max_clique 22 | #call C function 23 | fun.restype = np.int32 24 | fun.argtypes = [ctypes.c_int32,ndpointer(ctypes.c_int32, flags="C_CONTIGUOUS"), 25 | ndpointer(ctypes.c_int32, flags="C_CONTIGUOUS"),ctypes.c_int32, 26 | ctypes.c_int32,ndpointer(ctypes.c_int32, flags="C_CONTIGUOUS")] 27 | clique_size = fun(len(new_ei),new_ei,new_ej,offset,outsize,output) 28 | max_clique = np.empty(clique_size,dtype = np.int32) 29 | max_clique[:]=[output[i] for i in range(clique_size)] 30 | 31 | return max_clique 32 | -------------------------------------------------------------------------------- /pmc/pmc_clique_utils.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | ============================================================================ 3 | Name : Parallel Maximum Clique (PMC) Library 4 | Author : Ryan A. Rossi (rrossi@purdue.edu) 5 | Description : A general high-performance parallel framework for computing 6 | maximum cliques. The library is designed to be fast for large 7 | sparse graphs. 8 | 9 | Copyright (C) 2012-2013, Ryan A. Rossi, All rights reserved. 10 | 11 | Please cite the following paper if used: 12 | Ryan A. Rossi, David F. Gleich, Assefaw H. Gebremedhin, Md. Mostofa 13 | Patwary, A Fast Parallel Maximum Clique Algorithm for Large Sparse Graphs 14 | and Temporal Strong Components, arXiv preprint 1302.6256, 2013. 15 | 16 | See http://ryanrossi.com/pmc for more information. 17 | ============================================================================ 18 | */ 19 | 20 | #include "pmc_graph.h" 21 | #include 22 | 23 | using namespace std; 24 | using namespace pmc; 25 | 26 | int pmc_graph::initial_pruning(pmc_graph& G, int* &pruned, int lb) { 27 | int lb_idx = 0; 28 | for (int i = G.num_vertices()-1; i >= 0; i--) { 29 | if (kcore[kcore_order[i]] == lb) lb_idx = i; 30 | if (kcore[kcore_order[i]] <= lb) pruned[kcore_order[i]] = 1; 31 | } 32 | 33 | double sec = get_time(); 34 | cout << "[pmc: initial k-core pruning] before pruning: |V| = " << G.num_vertices(); 35 | cout << ", |E| = " << G.num_edges() <= 0; i--) { 51 | if (kcore[kcore_order[i]] == lb) lb_idx = i; 52 | if (kcore[kcore_order[i]] <= lb) { 53 | pruned[kcore_order[i]] = 1; 54 | for (long long j = vertices[kcore_order[i]]; j < vertices[kcore_order[i] + 1]; j++) { 55 | adj[kcore_order[i]][edges[j]] = false; 56 | adj[edges[j]][kcore_order[i]] = false; 57 | } 58 | } 59 | } 60 | 61 | double sec = get_time(); 62 | cout << "[pmc: initial k-core pruning] before pruning: |V| = " << G.num_vertices() << ", |E| = " << G.num_edges() < &V, pmc_graph &G, 75 | int &lb_idx, int &lb, string vertex_ordering, bool decr_order) { 76 | 77 | srand (time(NULL)); 78 | int u = 0, val = 0; 79 | for (int k = lb_idx; k < G.num_vertices(); k++) { 80 | if (degree[kcore_order[k]] >= lb - 1) { 81 | u = kcore_order[k]; 82 | 83 | if (vertex_ordering == "deg") 84 | val = vertices[u + 1] - vertices[u]; 85 | else if (vertex_ordering == "kcore") 86 | val = kcore[u]; 87 | else if (vertex_ordering == "kcore_deg") 88 | val = degree[u] * kcore[u]; 89 | else if (vertex_ordering == "rand") 90 | val = rand() % vertices.size(); 91 | // neighbor degrees 92 | else if (vertex_ordering == "dual_deg") { 93 | val = 0; 94 | for (long long j = vertices[u]; j < vertices[u + 1]; j++) { 95 | val = val + G.vertex_degree(edges[j]); 96 | } 97 | } 98 | // neighbor degrees 99 | else if (vertex_ordering == "dual_kcore") { 100 | val = 0; 101 | for (long long j = vertices[u]; j < vertices[u + 1]; j++) { 102 | val = val + kcore[edges[j]]; 103 | } 104 | } 105 | else val = vertices[u + 1] - vertices[u]; 106 | V.push_back(Vertex(u,val)); 107 | } 108 | } 109 | if (decr_order) 110 | std::sort(V.begin(), V.end(), decr_bound); 111 | else 112 | std::sort(V.begin(), V.end(), incr_bound); 113 | } 114 | 115 | 116 | /** 117 | * Reduce the graph by removing the pruned vertices 118 | * + Systematically speeds algorithm up by reducing the neighbors as more vertices are searched 119 | * 120 | * The algorithm below is for parallel maximum clique finders and has the following features: 121 | * + Thread-safe, since local copy of vertices/edges are passed in.. 122 | * + Pruned is a shared variable, but it is safe, since only reads/writes can occur, no deletion 123 | */ 124 | void pmc_graph::reduce_graph( 125 | vector& vs, 126 | vector& es, 127 | int* &pruned, 128 | pmc_graph& G, 129 | int id, 130 | int& mc) { 131 | 132 | int num_vs = vs.size(); 133 | 134 | vector V(num_vs,0); 135 | vector E; 136 | E.reserve(es.size()); 137 | 138 | int start = 0; 139 | for (int i = 0; i < num_vs - 1; i++) { 140 | start = E.size(); 141 | if (!pruned[i]) { //skip these V_local... 142 | for (long long j = vs[i]; j < vs[i + 1]; j++ ) { 143 | if (!pruned[es[j]]) 144 | E.push_back(es[j]); 145 | } 146 | } 147 | V[i] = start; 148 | V[i + 1] = E.size(); 149 | } 150 | vs = V; 151 | es = E; 152 | 153 | // compute k-cores and share bounds: ensure operation completed by single process 154 | #pragma omp single nowait 155 | { 156 | cout << ">>> [pmc: thread " << omp_get_thread_num() + 1 << "]" < &C_max, double &sec) { 165 | cout << "*** [pmc: thread " << omp_get_thread_num() + 1; 166 | cout << "] current max clique = " << C_max.size(); 167 | cout << ", time = " << get_time() - sec << " sec" < &C_max, double sec, double time_limit, bool &time_expired_msg) { 176 | if ((get_time() - sec) > time_limit) { 177 | if (time_expired_msg) { 178 | cout << "\n### Time limit expired, terminating search. ###" <& V, 26 | vector& E, 27 | int* &pruned) { 28 | 29 | long long n, d, i, j, start, num, md; 30 | long long v, u, w, du, pu, pw, md_end; 31 | n = vertices.size(); 32 | 33 | vector pos_tmp(n); 34 | vector core_tmp(n); 35 | vector order_tmp(n); 36 | 37 | md = 0; 38 | for(v=1; v md) md = core_tmp[v]; 41 | } 42 | 43 | md_end = md+1; 44 | vector < int > bin(md_end,0); 45 | 46 | for (v=1; v < n; v++) bin[core_tmp[v]]++; 47 | 48 | start = 1; 49 | for (d=0; d < md_end; d++) { 50 | num = bin[d]; 51 | bin[d] = start; 52 | start = start + num; 53 | } 54 | 55 | for (v=1; v 1; d--) bin[d] = bin[d-1]; 62 | bin[0] = 1; 63 | 64 | for (i = 1; i < n; i++) { 65 | v=order_tmp[i]; 66 | for (j = V[v-1]; j < V[v]; j++) { 67 | u = E[j] + 1; 68 | if (core_tmp[u] > core_tmp[v]) { 69 | du = core_tmp[u]; pu = pos_tmp[u]; 70 | pw = bin[du]; w = order_tmp[pw]; 71 | if (u != w) { 72 | pos_tmp[u] = pw; order_tmp[pu] = w; 73 | pos_tmp[w] = pu; order_tmp[pw] = u; 74 | } 75 | bin[du]++; core_tmp[u]--; 76 | } 77 | } 78 | } 79 | 80 | for (v=0; v pos(n); 100 | if (kcore_order.size() > 0) { 101 | vector tmp(n,0); 102 | kcore = tmp; 103 | kcore_order = tmp; 104 | } 105 | else { 106 | kcore_order.resize(n); 107 | kcore.resize(n); 108 | } 109 | 110 | md = 0; 111 | for (v=1; v md) md = kcore[v]; 114 | } 115 | 116 | md_end = md+1; 117 | vector < int > bin(md_end,0); 118 | 119 | for (v=1; v < n; v++) bin[kcore[v]]++; 120 | 121 | start = 1; 122 | for (d=0; d < md_end; d++) { 123 | num = bin[d]; 124 | bin[d] = start; 125 | start = start + num; 126 | } 127 | 128 | // bucket sort 129 | for (v=1; v 1; d--) bin[d] = bin[d-1]; 136 | bin[0] = 1; 137 | 138 | // kcores 139 | for (i=1; i kcore[v]) { 144 | du = kcore[u]; pu = pos[u]; 145 | pw = bin[du]; w = kcore_order[pw]; 146 | if (u != w) { 147 | pos[u] = pw; kcore_order[pu] = w; 148 | pos[w] = pu; kcore_order[pw] = u; 149 | } 150 | bin[du]++; kcore[u]--; 151 | } 152 | } 153 | } 154 | 155 | for (v = 0; v < n-1; v++) { 156 | kcore[v] = kcore[v+1] + 1; // K + 1 157 | kcore_order[v] = kcore_order[v+1]-1; 158 | } 159 | max_core = kcore[kcore_order[num_vertices()-1]] - 1; 160 | 161 | bin.clear(); 162 | pos.clear(); 163 | } 164 | -------------------------------------------------------------------------------- /pmc/pmc_driver.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | ============================================================================ 3 | Name : Parallel Maximum Clique (PMC) Library 4 | Author : Ryan A. Rossi (rrossi@purdue.edu) 5 | Description : A general high-performance parallel framework for computing 6 | maximum cliques. The library is designed to be fast for large 7 | sparse graphs. 8 | 9 | Copyright (C) 2012-2013, Ryan A. Rossi, All rights reserved. 10 | 11 | Please cite the following paper if used: 12 | Ryan A. Rossi, David F. Gleich, Assefaw H. Gebremedhin, Md. Mostofa 13 | Patwary, A Fast Parallel Maximum Clique Algorithm for Large Sparse Graphs 14 | and Temporal Strong Components, arXiv preprint 1302.6256, 2013. 15 | 16 | See http://ryanrossi.com/pmc for more information. 17 | ============================================================================ 18 | */ 19 | 20 | #include "pmc.h" 21 | 22 | using namespace std; 23 | using namespace pmc; 24 | 25 | int main(int argc, char *argv[]) { 26 | 27 | //! parse command args 28 | input in(argc, argv); 29 | if (in.help) { 30 | usage(argv[0]); 31 | return 0; 32 | } 33 | 34 | //! read graph 35 | pmc_graph G(in.graph_stats,in.graph); 36 | if (in.graph_stats) { G.bound_stats(in.algorithm, in.lb, G); } 37 | 38 | //! ensure wait time is greater than the time to recompute the graph data structures 39 | if (G.num_edges() > 1000000000 && in.remove_time < 120) in.remove_time = 120; 40 | else if (G.num_edges() > 250000000 && in.remove_time < 10) in.remove_time = 10; 41 | cout << "explicit reduce is set to " << in.remove_time << " seconds" < C; 54 | if (in.lb == 0 && in.heu_strat != "0") { // skip if given as input 55 | pmc_heu maxclique(G,in); 56 | in.lb = maxclique.search(G, C); 57 | cout << "Heuristic found clique of size " << in.lb; 58 | cout << " in " << get_time() - seconds << " seconds" <= 0) { 68 | switch(in.algorithm) { 69 | case 0: { 70 | //! k-core pruning, neigh-core pruning/ordering, dynamic coloring bounds/sort 71 | if (G.num_vertices() < in.adj_limit) { 72 | G.create_adj(); 73 | pmcx_maxclique finder(G,in); 74 | finder.search_dense(G,C); 75 | break; 76 | } 77 | else { 78 | pmcx_maxclique finder(G,in); 79 | finder.search(G,C); 80 | break; 81 | } 82 | } 83 | case 1: { 84 | //! k-core pruning, dynamic coloring bounds/sort 85 | if (G.num_vertices() < in.adj_limit) { 86 | G.create_adj(); 87 | pmcx_maxclique_basic finder(G,in); 88 | finder.search_dense(G,C); 89 | break; 90 | } 91 | else { 92 | pmcx_maxclique_basic finder(G,in); 93 | finder.search(G,C); 94 | break; 95 | } 96 | } 97 | case 2: { 98 | //! simple k-core pruning (four new pruning steps) 99 | pmc_maxclique finder(G,in); 100 | finder.search(G,C); 101 | break; 102 | } 103 | default: 104 | cout << "algorithm " << in.algorithm << " not found." < 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "math.h" 30 | #include "pmc_headers.h" 31 | #include "pmc_utils.h" 32 | #include "pmc_vertex.h" 33 | 34 | 35 | namespace pmc { 36 | class pmc_graph { 37 | private: 38 | // helper functions 39 | void read_mtx(const string& filename); 40 | void read_edges(const string& filename); 41 | void read_metis(const string& filename); 42 | 43 | public: 44 | vector edges; 45 | vector vertices; 46 | vector degree; 47 | int min_degree; 48 | int max_degree; 49 | double avg_degree; 50 | bool is_gstats; 51 | string fn; 52 | bool** adj; 53 | 54 | // constructor 55 | pmc_graph(const string& filename); 56 | pmc_graph(bool graph_stats, const string& filename); 57 | pmc_graph(const string& filename, bool make_adj); 58 | pmc_graph(vector vs, vector es) { 59 | edges = es; 60 | vertices = vs; 61 | vertex_degrees(); 62 | } 63 | pmc_graph(long long nedges, int *ei, int *ej, int offset); 64 | 65 | // destructor 66 | ~pmc_graph(); 67 | 68 | void read_graph(const string& filename); 69 | void create_adj(); 70 | void reduce_graph(int* &pruned); 71 | void reduce_graph( 72 | vector& vs, 73 | vector& es, 74 | int* &pruned, 75 | int id, 76 | int& mc); 77 | 78 | int num_vertices() { return vertices.size() - 1; } 79 | int num_edges() { return edges.size()/2; } 80 | vector * get_vertices(){ return &vertices; } 81 | vector* get_edges(){ return &edges; } 82 | vector* get_degree(){ return °ree; } 83 | vector get_edges_array() { return edges; } 84 | vector get_vertices_array() { return vertices; }; 85 | vector e_v, e_u, eid; 86 | 87 | int vertex_degree(int v) { return vertices[v] - vertices[v+1]; } 88 | long long first_neigh(int v) { return vertices[v]; } 89 | long long last_neigh(int v) { return vertices[v+1]; } 90 | 91 | void sum_vertex_degrees(); 92 | void vertex_degrees(); 93 | void update_degrees(); 94 | void update_degrees(bool flag); 95 | void update_degrees(int* &pruned, int& mc); 96 | double density() { return (double)num_edges() / (num_vertices() * (num_vertices() - 1.0) / 2.0); } 97 | int get_max_degree() { return max_degree; } 98 | int get_min_degree() { return min_degree; } 99 | double get_avg_degree() { return avg_degree; } 100 | 101 | void initialize(); 102 | string get_file_extension(const string& filename); 103 | void basic_stats(double sec); 104 | void bound_stats(int alg, int lb, pmc_graph& G); 105 | 106 | // vertex sorter 107 | void compute_ordering(vector& bound, vector& order); 108 | void compute_ordering(string degree, vector& order); 109 | // edge sorters 110 | void degree_bucket_sort(); 111 | void degree_bucket_sort(bool desc); 112 | 113 | int max_core; 114 | vector kcore; 115 | vector kcore_order; 116 | vector* get_kcores() { return &kcore; } 117 | vector* get_kcore_ordering() { return &kcore_order; } 118 | int get_max_core() { return max_core; } 119 | void update_kcores(int* &pruned); 120 | 121 | void compute_cores(); 122 | void induced_cores_ordering( 123 | vector& V, 124 | vector& E, 125 | int* &pruned); 126 | 127 | // clique utils 128 | int initial_pruning(pmc_graph& G, int* &pruned, int lb); 129 | int initial_pruning(pmc_graph& G, int* &pruned, int lb, bool** &adj); 130 | void order_vertices(vector &V, pmc_graph &G, 131 | int &lb_idx, int &lb, string vertex_ordering, bool decr_order); 132 | 133 | void print_info(vector &C_max, double &sec); 134 | void print_break(); 135 | bool time_left(vector &C_max, double sec, 136 | double time_limit, bool &time_expired_msg); 137 | void graph_stats(pmc_graph& G, int& mc, int id, double &sec); 138 | 139 | void reduce_graph( 140 | vector& vs, 141 | vector& es, 142 | int* &pruned, 143 | pmc_graph& G, 144 | int id, 145 | int& mc); 146 | 147 | bool clique_test(pmc_graph& G, vector C); 148 | }; 149 | 150 | } 151 | #endif 152 | -------------------------------------------------------------------------------- /pmc/pmc_headers.h: -------------------------------------------------------------------------------- 1 | /** 2 | ============================================================================ 3 | Name : Parallel Maximum Clique (PMC) Library 4 | Author : Ryan A. Rossi (rrossi@purdue.edu) 5 | Description : A general high-performance parallel framework for computing 6 | maximum cliques. The library is designed to be fast for large 7 | sparse graphs. 8 | 9 | Copyright (C) 2012-2013, Ryan A. Rossi, All rights reserved. 10 | 11 | Please cite the following paper if used: 12 | Ryan A. Rossi, David F. Gleich, Assefaw H. Gebremedhin, Md. Mostofa 13 | Patwary, A Fast Parallel Maximum Clique Algorithm for Large Sparse Graphs 14 | and Temporal Strong Components, arXiv preprint 1302.6256, 2013. 15 | 16 | See http://ryanrossi.com/pmc for more information. 17 | ============================================================================ 18 | */ 19 | 20 | #ifndef PMC_HEADERS_H_ 21 | #define PMC_HEADERS_H_ 22 | 23 | #define NOMINMAX // disable std::max and std::min 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include //// gabor added. 37 | 38 | using namespace std; 39 | 40 | #ifndef LINE_LENGTH 41 | #define LINE_LENGTH 256 42 | #endif 43 | #define NANOSECOND 1000000000 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /pmc/pmc_heu.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | ============================================================================ 3 | Name : Parallel Maximum Clique (PMC) Library 4 | Author : Ryan A. Rossi (rrossi@purdue.edu) 5 | Description : A general high-performance parallel framework for computing 6 | maximum cliques. The library is designed to be fast for large 7 | sparse graphs. 8 | 9 | Copyright (C) 2012-2013, Ryan A. Rossi, All rights reserved. 10 | 11 | Please cite the following paper if used: 12 | Ryan A. Rossi, David F. Gleich, Assefaw H. Gebremedhin, Md. Mostofa 13 | Patwary, A Fast Parallel Maximum Clique Algorithm for Large Sparse Graphs 14 | and Temporal Strong Components, arXiv preprint 1302.6256, 2013. 15 | 16 | See http://ryanrossi.com/pmc for more information. 17 | ============================================================================ 18 | */ 19 | 20 | #include "pmc_heu.h" 21 | 22 | using namespace pmc; 23 | using namespace std; 24 | 25 | 26 | void pmc_heu::branch(vector& P, int sz, 27 | int& mc, vector& C, vector& ind) { 28 | 29 | if (P.size() > 0) { 30 | 31 | int u = P.back().get_id(); 32 | P.pop_back(); 33 | 34 | for (long long j = (*V)[u]; j < (*V)[u + 1]; j++) ind[(*E)[j]] = 1; 35 | 36 | vector R; 37 | R.reserve(P.size()); 38 | for (int i = 0; i < P.size(); i++) 39 | if (ind[P[i].get_id()]) 40 | if ((*K)[P[i].get_id()] > mc) 41 | R.push_back(P[i]); 42 | 43 | for (long long j = (*V)[u]; j < (*V)[u + 1]; j++) ind[(*E)[j]] = 0; 44 | 45 | int mc_prev = mc; 46 | branch(R, sz + 1, mc, C, ind); 47 | 48 | if (mc > mc_prev) C.push_back(u); 49 | 50 | R.clear(); P.clear(); 51 | } 52 | else if (sz > mc) 53 | mc = sz; 54 | return; 55 | } 56 | 57 | int pmc_heu::search_bounds(pmc_graph& G, 58 | vector& C_max) { 59 | 60 | V = G.get_vertices(); 61 | E = G.get_edges(); 62 | degree = G.get_degree(); 63 | vector C, X; 64 | C.reserve(ub); 65 | C_max.reserve(ub); 66 | vector P, T; 67 | P.reserve(G.get_max_degree()+1); 68 | T.reserve(G.get_max_degree()+1); 69 | vector ind(G.num_vertices(),0); 70 | 71 | bool found_ub = false; 72 | int mc = 0, mc_prev, mc_cur, i, v, k, lb_idx = 0; 73 | 74 | #pragma omp parallel for schedule(dynamic) \ 75 | shared(G, X, mc, C_max, lb_idx) private(i, v, P, mc_prev, mc_cur, C, k) firstprivate(ind) 76 | for (i = G.num_vertices()-1; i >= 0; --i) { 77 | if (found_ub) continue; 78 | 79 | v = (*order)[i]; 80 | mc_prev = mc_cur = mc; 81 | 82 | if ((*K)[v] > mc) { 83 | for (long long j = (*V)[v]; j < (*V)[v + 1]; j++) 84 | if ((*K)[(*E)[j]] > mc) 85 | P.push_back( Vertex((*E)[j], compute_heuristic((*E)[j])) ); 86 | 87 | 88 | if (P.size() > mc_cur) { 89 | std::sort(P.begin(), P.end(), incr_heur); 90 | branch(P, 1 , mc_cur, C, ind); 91 | 92 | if (mc_cur > mc_prev) { 93 | if (mc < mc_cur) { 94 | #pragma omp critical 95 | if (mc < mc_cur) { 96 | mc = mc_cur; 97 | C.push_back(v); 98 | C_max = C; 99 | if (mc >= ub) found_ub = true; 100 | print_info(C_max); 101 | } 102 | } 103 | } 104 | } 105 | C = X; P = T; 106 | } 107 | } 108 | cout << "[pmc heuristic]\t mc = " << mc <& C_max, int lb) { 124 | 125 | vector C, X; 126 | C.reserve(ub); 127 | C_max.reserve(ub); 128 | vector P, T; 129 | P.reserve(G.get_max_degree()+1); 130 | T.reserve(G.get_max_degree()+1); 131 | vector ind(G.num_vertices(),0); 132 | 133 | int mc = lb, mc_prev, mc_cur, i; 134 | int lb_idx = 0, v = 0; 135 | for (i = G.num_vertices()-1; i >= 0; i--) { 136 | v = (*order)[i]; 137 | if ((*K)[v] == lb) lb_idx = i; 138 | } 139 | 140 | #pragma omp parallel for schedule(dynamic) \ 141 | shared(G, X, mc, C_max) private(i, v, P, mc_prev, mc_cur, C) firstprivate(ind) 142 | for (i = lb_idx; i <= G.num_vertices()-1; i++) { 143 | 144 | v = (*order)[i]; 145 | mc_prev = mc_cur = mc; 146 | 147 | if ((*K)[v] > mc_cur) { 148 | for (long long j = (*V)[v]; j < (*V)[v + 1]; j++) 149 | if ((*K)[(*E)[j]] > mc_cur) 150 | P.push_back( Vertex((*E)[j], compute_heuristic((*E)[j])) ); 151 | 152 | if (P.size() > mc_cur) { 153 | std::sort(P.begin(), P.end(), incr_heur); 154 | branch(P, 1 , mc_cur, C, ind); 155 | 156 | if (mc_cur > mc_prev) { 157 | if (mc < mc_cur) { 158 | #pragma omp critical 159 | if (mc < mc_cur) { 160 | mc = mc_cur; 161 | C.push_back(v); 162 | C_max = C; 163 | print_info(C_max); 164 | } 165 | } 166 | } 167 | } 168 | } 169 | C = X; P = T; 170 | } 171 | C.clear(); 172 | cout << "[search_cores]\t mc = " << mc <& C_max) { 178 | return search_bounds(G, C_max); 179 | } 180 | 181 | 182 | inline void pmc_heu::print_info(vector C_max) { 183 | cout << "*** [pmc heuristic: thread " << omp_get_thread_num() + 1; 184 | cout << "] current max clique = " << C_max.size(); 185 | cout << ", time = " << get_time() - sec << " sec" < 29 | 30 | namespace pmc { 31 | 32 | class pmc_heu { 33 | public: 34 | vector* E; 35 | vector* V; 36 | vector* K; 37 | vector* order; 38 | vector* degree; 39 | double sec; 40 | int ub; 41 | string strat; 42 | 43 | pmc_heu(pmc_graph& G, input& params) { 44 | K = G.get_kcores(); 45 | order = G.get_kcore_ordering(); 46 | ub = params.ub; 47 | strat = params.heu_strat; 48 | initialize(); 49 | } 50 | 51 | pmc_heu(pmc_graph& G, int tmp_ub) { 52 | K = G.get_kcores(); 53 | order = G.get_kcore_ordering(); 54 | ub = tmp_ub; 55 | strat = "kcore"; 56 | initialize(); 57 | } 58 | 59 | inline void initialize() { 60 | sec = get_time(); 61 | srand (time(NULL)); 62 | }; 63 | 64 | int strategy(vector& P); 65 | void set_strategy(string s) { strat = s; } 66 | int compute_heuristic(int v); 67 | 68 | static bool desc_heur(Vertex v, Vertex u) { 69 | return (v.get_bound() > u.get_bound()); 70 | } 71 | 72 | static bool incr_heur(Vertex v, Vertex u) { 73 | return (v.get_bound() < u.get_bound()); 74 | } 75 | 76 | int search(pmc_graph& graph, vector& C_max); 77 | int search_cores(pmc_graph& graph, vector& C_max, int lb); 78 | int search_bounds(pmc_graph& graph, vector& C_max); 79 | 80 | inline void branch(vector& P, int sz, 81 | int& mc, vector& C, vector& ind); 82 | 83 | inline void print_info(vector C_max); 84 | }; 85 | }; 86 | #endif 87 | -------------------------------------------------------------------------------- /pmc/pmc_input.h: -------------------------------------------------------------------------------- 1 | /** 2 | ============================================================================ 3 | Name : Parallel Maximum Clique (PMC) Library 4 | Author : Ryan A. Rossi (rrossi@purdue.edu) 5 | Description : A general high-performance parallel framework for computing 6 | maximum cliques. The library is designed to be fast for large 7 | sparse graphs. 8 | 9 | Copyright (C) 2012-2013, Ryan A. Rossi, All rights reserved. 10 | 11 | Please cite the following paper if used: 12 | Ryan A. Rossi, David F. Gleich, Assefaw H. Gebremedhin, Md. Mostofa 13 | Patwary, A Fast Parallel Maximum Clique Algorithm for Large Sparse Graphs 14 | and Temporal Strong Components, arXiv preprint 1302.6256, 2013. 15 | 16 | See http://ryanrossi.com/pmc for more information. 17 | ============================================================================ 18 | */ 19 | 20 | #ifndef PMC_INPUT_H_ 21 | #define PMC_INPUT_H_ 22 | 23 | #include "pmc_headers.h" 24 | #include "pmc_utils.h" 25 | 26 | using namespace std; 27 | 28 | class input { 29 | public: 30 | // instance variables 31 | int algorithm; 32 | int threads; 33 | int experiment; 34 | int lb; 35 | int ub; 36 | int param_ub; 37 | int adj_limit; 38 | double time_limit; 39 | double remove_time; 40 | bool graph_stats; 41 | bool verbose; 42 | bool help; 43 | bool MCE; 44 | bool decreasing_order; 45 | string heu_strat; 46 | string format; 47 | string graph; 48 | string output; 49 | string edge_sorter; 50 | string vertex_search_order; 51 | 52 | input() { 53 | // default values 54 | algorithm = 0; 55 | threads = omp_get_max_threads(); 56 | experiment = 0; 57 | lb = 0; 58 | ub = 0; 59 | param_ub = 0; 60 | adj_limit = 20000; 61 | time_limit = 60 * 60; // max time to search 62 | remove_time = 4.0; // time to wait before reducing graph 63 | verbose = false; 64 | graph_stats = false; 65 | help = false; 66 | MCE = false; 67 | decreasing_order = false; 68 | heu_strat = "kcore"; 69 | vertex_search_order = "deg"; 70 | format = "mtx"; 71 | graph = "data/sample.mtx"; 72 | output = ""; 73 | string edge_sorter = ""; 74 | 75 | // both off, use default alg 76 | if (heu_strat == "0" && algorithm == -1) 77 | algorithm = 0; 78 | 79 | if (threads <= 0) threads = 1; 80 | } 81 | 82 | input(int argc, char *argv[]) { 83 | // default values 84 | algorithm = 0; 85 | threads = omp_get_max_threads(); 86 | experiment = 0; 87 | lb = 0; 88 | ub = 0; 89 | param_ub = 0; 90 | adj_limit = 20000; 91 | time_limit = 60 * 60; // max time to search 92 | remove_time = 4.0; // time to wait before reducing graph 93 | verbose = false; 94 | graph_stats = false; 95 | help = false; 96 | MCE = false; 97 | decreasing_order = false; 98 | heu_strat = "kcore"; 99 | vertex_search_order = "deg"; 100 | format = "mtx"; 101 | graph = "data/sample.mtx"; 102 | output = ""; 103 | string edge_sorter = ""; 104 | 105 | int opt; 106 | while ((opt=getopt(argc,argv,"i:t:f:u:l:o:e:a:r:w:h:k:dgsv")) != EOF) { 107 | switch (opt) { 108 | case 'a': 109 | algorithm = atoi(optarg); 110 | if (algorithm > 9) MCE = true; 111 | break; 112 | case 't': 113 | threads = atoi(optarg); 114 | break; 115 | case 'f': 116 | graph = optarg; 117 | break; 118 | case 's': 119 | graph_stats = true; 120 | break; 121 | case 'u': 122 | param_ub = atoi(optarg); // find k-clique fast 123 | lb = 2; // skip heuristic 124 | break; 125 | case 'k': 126 | param_ub = atoi(optarg); 127 | lb = param_ub-1; 128 | break; 129 | case 'l': 130 | lb = atoi(optarg); 131 | break; 132 | case 'h': 133 | heu_strat = optarg; 134 | break; 135 | case 'v': 136 | verbose = 1; 137 | break; 138 | case 'w': 139 | time_limit = atof(optarg) * 60; // convert minutes to seconds 140 | break; 141 | case 'r': 142 | remove_time = atof(optarg); 143 | break; 144 | case 'e': 145 | edge_sorter = optarg; 146 | break; 147 | case 'o': 148 | vertex_search_order = optarg; 149 | break; 150 | case 'd': 151 | // direction of which vertices are ordered 152 | decreasing_order = true; 153 | break; 154 | case '?': 155 | usage(argv[0]); 156 | break; 157 | default: 158 | usage(argv[0]); 159 | break; 160 | } 161 | } 162 | 163 | // both off, use default alg 164 | if (heu_strat == "0" && algorithm == -1) 165 | algorithm = 0; 166 | 167 | if (threads <= 0) threads = 1; 168 | 169 | if (!fexists(graph.c_str())) { 170 | usage(argv[0]); 171 | exit(-1); 172 | } 173 | 174 | FILE* fin = fopen(graph.c_str(), "r+t"); 175 | if (fin == NULL) { 176 | usage(argv[0]); 177 | exit(-1); 178 | } 179 | fclose(fin); 180 | 181 | cout << "\n\nFile Name ------------------------ " << graph.c_str() << endl; 182 | if (!fexists(graph.c_str()) ) { 183 | cout << "File not found!" << endl; 184 | return; 185 | } 186 | cout << "workers: " << threads < 1000000000 && in.remove_time < 120) in.remove_time = 120; 36 | else if (G.num_edges() > 250000000 && in.remove_time < 10) in.remove_time = 10; 37 | cout << "explicit reduce is set to " << in.remove_time << " seconds" < C; 50 | if (in.lb == 0 && in.heu_strat != "0") { // skip if given as input 51 | pmc_heu maxclique(G,in); 52 | in.lb = maxclique.search(G, C); 53 | cout << "Heuristic found clique of size " << in.lb; 54 | cout << " in " << get_time() - dSec << "s" <= 0) { 64 | switch(in.algorithm) { 65 | case 0: { 66 | //! k-core pruning, neigh-core pruning/ordering, dynamic coloring bounds/sort 67 | if (G.num_vertices() < in.adj_limit) { 68 | G.create_adj(); 69 | pmcx_maxclique finder(G,in); 70 | finder.search_dense(G,C); 71 | break; 72 | } 73 | else { 74 | pmcx_maxclique finder(G,in); 75 | finder.search(G,C); 76 | break; 77 | } 78 | } 79 | case 1: { 80 | //! k-core pruning, dynamic coloring bounds/sort 81 | if (G.num_vertices() < in.adj_limit) { 82 | G.create_adj(); 83 | pmcx_maxclique_basic finder(G,in); 84 | finder.search_dense(G,C); 85 | break; 86 | } 87 | else { 88 | pmcx_maxclique_basic finder(G,in); 89 | finder.search(G,C); 90 | break; 91 | } 92 | } 93 | case 2: { 94 | //! simple k-core pruning (four new pruning steps) 95 | pmc_maxclique finder(G,in); 96 | finder.search(G,C); 97 | break; 98 | } 99 | default: 100 | cout << "algorithm " << in.algorithm << " not found." <& sol) { 26 | 27 | vertices = G.get_vertices(); 28 | edges = G.get_edges(); 29 | degree = G.get_degree(); 30 | int* pruned = new int[G.num_vertices()]; 31 | memset(pruned, 0, G.num_vertices() * sizeof(int)); 32 | int mc = lb, i = 0, u = 0; 33 | 34 | // initial pruning 35 | int lb_idx = G.initial_pruning(G, pruned, lb); 36 | 37 | // set to worst case bound of cores/coloring 38 | vector P, T; 39 | P.reserve(G.get_max_degree()+1); 40 | T.reserve(G.get_max_degree()+1); 41 | 42 | vector C, C_max; 43 | C.reserve(G.get_max_degree()+1); 44 | C_max.reserve(G.get_max_degree()+1); 45 | 46 | // order verts for our search routine 47 | vector V; V.reserve(G.num_vertices()); 48 | G.order_vertices(V,G,lb_idx,lb,vertex_ordering,decr_order); 49 | 50 | vector ind(G.num_vertices(),0); 51 | 52 | #pragma omp parallel for schedule(dynamic) \ 53 | shared(pruned, G, T, V, mc, C_max) firstprivate(ind) private(u, P, C) 54 | for (i = 0; i < (V.size()) - (mc-1); ++i) { 55 | if (G.time_left(C_max,sec,time_limit,time_expired_msg)) { 56 | 57 | u = V[i].get_id(); 58 | if ((*bound)[u] > mc) { 59 | P.push_back(V[i]); 60 | for (long long j = (*vertices)[u]; j < (*vertices)[u + 1]; ++j) 61 | if (!pruned[(*edges)[j]]) 62 | if ((*bound)[(*edges)[j]] > mc) 63 | P.push_back(Vertex((*edges)[j], (*degree)[(*edges)[j]])); 64 | 65 | if (P.size() > mc) { 66 | branch(P, ind, C, C_max, pruned, mc); 67 | } 68 | P = T; 69 | } 70 | pruned[u] = 1; 71 | } 72 | } 73 | 74 | if (pruned) delete[] pruned; 75 | 76 | sol.resize(mc); 77 | for (int i = 0; i < C_max.size(); i++) sol[i] = C_max[i]; 78 | G.print_break(); 79 | return sol.size(); 80 | } 81 | 82 | 83 | 84 | 85 | void pmc_maxclique::branch( 86 | vector &P, 87 | vector& ind, 88 | vector& C, 89 | vector& C_max, 90 | int* &pruned, 91 | int& mc) { 92 | 93 | // stop early if ub is reached 94 | if (not_reached_ub) { 95 | while (P.size() > 0) { 96 | // terminating condition 97 | if (C.size() + P.size() > mc) { 98 | int v = P.back().get_id(); C.push_back(v); 99 | 100 | vector R; R.reserve(P.size()); 101 | for (long long j = (*vertices)[v]; j < (*vertices)[v + 1]; j++) ind[(*edges)[j]] = 1; 102 | 103 | // intersection of N(v) and P - {v} 104 | for (int k = 0; k < P.size() - 1; k++) 105 | if (ind[P[k].get_id()]) 106 | if (!pruned[P[k].get_id()]) 107 | if ((*bound)[P[k].get_id()] > mc) 108 | R.push_back(P[k]); 109 | 110 | for (long long j = (*vertices)[v]; j < (*vertices)[v + 1]; j++) ind[(*edges)[j]] = 0; 111 | 112 | if (R.size() > 0) { 113 | branch(R, ind, C, C_max, pruned, mc); 114 | } 115 | else if (C.size() > mc) { 116 | // obtain lock 117 | #pragma omp critical (update_mc) 118 | if (C.size() > mc) { 119 | // ensure updated max is flushed 120 | mc = C.size(); 121 | C_max = C; 122 | print_mc_info(C,sec); 123 | if (mc >= param_ub) { 124 | not_reached_ub = false; 125 | cout << "[pmc: upper bound reached] omega = " << mc <& sol) { 153 | 154 | vertices = G.get_vertices(); 155 | edges = G.get_edges(); 156 | degree = G.get_degree(); 157 | bool** adj = G.adj; 158 | 159 | int* pruned = new int[G.num_vertices()]; 160 | memset(pruned, 0, G.num_vertices() * sizeof(int)); 161 | int mc = lb, i = 0, u = 0; 162 | 163 | // initial pruning 164 | int lb_idx = G.initial_pruning(G, pruned, lb, adj); 165 | 166 | // set to worst case bound of cores 167 | vector P, T; 168 | P.reserve(G.get_max_degree()+1); 169 | T.reserve(G.get_max_degree()+1); 170 | 171 | vector C, C_max; 172 | C.reserve(G.get_max_degree()+1); 173 | C_max.reserve(G.get_max_degree()+1); 174 | 175 | // order verts for our search routine 176 | vector V; V.reserve(G.num_vertices()); 177 | G.order_vertices(V,G,lb_idx,lb,vertex_ordering,decr_order); 178 | 179 | vector ind(G.num_vertices(),0); 180 | 181 | #pragma omp parallel for schedule(dynamic) \ 182 | shared(pruned, G, adj, T, V, mc, C_max) firstprivate(ind) private(u, P, C) 183 | for (i = 0; i < (V.size()) - (mc-1); ++i) { 184 | if (G.time_left(C_max,sec,time_limit,time_expired_msg)) { 185 | 186 | u = V[i].get_id(); 187 | if ((*bound)[u] > mc) { 188 | P.push_back(V[i]); 189 | for (long long j = (*vertices)[u]; j < (*vertices)[u + 1]; ++j) 190 | if (!pruned[(*edges)[j]]) 191 | if ((*bound)[(*edges)[j]] > mc) 192 | P.push_back(Vertex((*edges)[j], (*degree)[(*edges)[j]])); 193 | 194 | if (P.size() > mc) { 195 | branch_dense(P, ind, C, C_max, pruned, mc, adj); 196 | } 197 | P = T; 198 | } 199 | pruned[u] = 1; 200 | for (long long j = (*vertices)[u]; j < (*vertices)[u + 1]; j++) { 201 | adj[u][(*edges)[j]] = false; 202 | adj[(*edges)[j]][u] = false; 203 | } 204 | } 205 | } 206 | if (pruned) delete[] pruned; 207 | 208 | sol.resize(mc); 209 | for (int i = 0; i < C_max.size(); i++) sol[i] = C_max[i]; 210 | G.print_break(); 211 | return sol.size(); 212 | } 213 | 214 | 215 | 216 | void pmc_maxclique::branch_dense( 217 | vector &P, 218 | vector& ind, 219 | vector& C, 220 | vector& C_max, 221 | int* &pruned, 222 | int& mc, 223 | bool** &adj) { 224 | 225 | // stop early if ub is reached 226 | if (not_reached_ub) { 227 | while (P.size() > 0) { 228 | // terminating condition 229 | if (C.size() + P.size() > mc) { 230 | int v = P.back().get_id(); C.push_back(v); 231 | vector R; R.reserve(P.size()); 232 | 233 | for (int k = 0; k < P.size() - 1; k++) 234 | // indicates neighbor AND pruned 235 | if (adj[v][P[k].get_id()]) 236 | if ((*bound)[P[k].get_id()] > mc) 237 | R.push_back(P[k]); 238 | 239 | if (R.size() > 0) { 240 | branch_dense(R, ind, C, C_max, pruned, mc, adj); 241 | } 242 | else if (C.size() > mc) { 243 | // obtain lock 244 | #pragma omp critical (update_mc) 245 | if (C.size() > mc) { 246 | // ensure updated max is flushed 247 | mc = C.size(); 248 | C_max = C; 249 | print_mc_info(C,sec); 250 | if (mc >= param_ub) { 251 | not_reached_ub = false; 252 | cout << "[pmc: upper bound reached] omega = " << mc < 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "pmc_headers.h" 29 | #include "pmc_utils.h" 30 | #include "pmc_graph.h" 31 | #include "pmc_input.h" 32 | #include "pmc_vertex.h" 33 | 34 | using namespace std; 35 | 36 | namespace pmc { 37 | 38 | class pmc_maxclique { 39 | public: 40 | vector* edges; 41 | vector* vertices; 42 | vector* bound; 43 | vector* order; 44 | vector* degree; 45 | int param_ub; 46 | int ub; 47 | int lb; 48 | double time_limit; 49 | double sec; 50 | double wait_time; 51 | bool not_reached_ub; 52 | bool time_expired_msg; 53 | bool decr_order; 54 | 55 | string vertex_ordering; 56 | int edge_ordering; 57 | int style_bounds; 58 | int style_dynamic_bounds; 59 | 60 | int num_threads; 61 | 62 | void initialize() { 63 | vertex_ordering = "kcore"; 64 | edge_ordering = 0; 65 | style_bounds = 0; 66 | style_dynamic_bounds = 0; 67 | not_reached_ub = true; 68 | time_expired_msg = true; 69 | decr_order = false; 70 | } 71 | 72 | void setup_bounds(input& params) { 73 | lb = params.lb; 74 | ub = params.ub; 75 | param_ub = params.param_ub; 76 | if (param_ub == 0) 77 | param_ub = ub; 78 | time_limit = params.time_limit; 79 | wait_time = params.remove_time; 80 | sec = get_time(); 81 | 82 | num_threads = params.threads; 83 | } 84 | 85 | 86 | pmc_maxclique(pmc_graph& G, input& params) { 87 | bound = G.get_kcores(); 88 | order = G.get_kcore_ordering(); 89 | setup_bounds(params); 90 | initialize(); 91 | vertex_ordering = params.vertex_search_order; 92 | decr_order = params.decreasing_order; 93 | } 94 | 95 | ~pmc_maxclique() {}; 96 | 97 | int search(pmc_graph& G, vector& sol); 98 | 99 | void branch( 100 | vector &P, 101 | vector& ind, 102 | vector& C, 103 | vector& C_max, 104 | int* &pruned, 105 | int& mc); 106 | 107 | 108 | int search_dense(pmc_graph& G, vector& sol); 109 | 110 | void branch_dense( 111 | vector &P, 112 | vector& ind, 113 | vector& C, 114 | vector& C_max, 115 | int* &pruned, 116 | int& mc, 117 | bool** &adj); 118 | 119 | }; 120 | }; 121 | 122 | #endif 123 | -------------------------------------------------------------------------------- /pmc/pmc_neigh_coloring.h: -------------------------------------------------------------------------------- 1 | /** 2 | ============================================================================ 3 | Name : Parallel Maximum Clique (PMC) Library 4 | Author : Ryan A. Rossi (rrossi@purdue.edu) 5 | Description : A general high-performance parallel framework for computing 6 | maximum cliques. The library is designed to be fast for large 7 | sparse graphs. 8 | 9 | Copyright (C) 2012-2013, Ryan A. Rossi, All rights reserved. 10 | 11 | Please cite the following paper if used: 12 | Ryan A. Rossi, David F. Gleich, Assefaw H. Gebremedhin, Md. Mostofa 13 | Patwary, A Fast Parallel Maximum Clique Algorithm for Large Sparse Graphs 14 | and Temporal Strong Components, arXiv preprint 1302.6256, 2013. 15 | 16 | See http://ryanrossi.com/pmc for more information. 17 | ============================================================================ 18 | */ 19 | 20 | #ifndef PMC_NEIGH_COLORING_H_ 21 | #define PMC_NEIGH_COLORING_H_ 22 | 23 | #include "pmc_vertex.h" 24 | 25 | using namespace std; 26 | 27 | namespace pmc { 28 | 29 | // sequential dynamic greedy coloring and sort 30 | static void neigh_coloring_bound( 31 | vector& vs, 32 | vector& es, 33 | vector &P, 34 | vector& ind, 35 | vector& C, 36 | vector& C_max, 37 | vector< vector >& colors, 38 | int* pruned, 39 | int& mc) { 40 | 41 | int j = 0, u = 0, k = 1, k_prev = 0; 42 | int max_k = 1; 43 | int min_k = mc - C.size() + 1; 44 | colors[1].clear(); colors[2].clear(); 45 | 46 | for (int w=0; w < P.size(); w++) { 47 | u = P[w].get_id(); 48 | k = 1, k_prev = 0; 49 | 50 | for (long long h = vs[u]; h < vs[u + 1]; h++) ind[es[h]] = 1; 51 | 52 | while (k > k_prev) { 53 | k_prev = k; 54 | for (int i = 0; i < colors[k].size(); i++) { 55 | if (ind[colors[k][i]]) { 56 | k++; 57 | break; 58 | } 59 | } 60 | } 61 | 62 | for (long long h = vs[u]; h < vs[u + 1]; h++) ind[es[h]] = 0; 63 | 64 | if (k > max_k) { 65 | max_k = k; 66 | colors[max_k+1].clear(); 67 | } 68 | 69 | colors[k].push_back(u); 70 | if (k < min_k) { 71 | P[j].set_id(u); 72 | j++; 73 | } 74 | } 75 | 76 | if (j > 0) P[j-1].set_bound(0); 77 | if (min_k <= 0) min_k = 1; 78 | 79 | for (k = min_k; k <= max_k; k++) 80 | for (int w = 0; w < colors[k].size(); w++) { 81 | P[j].set_id(colors[k][w]); 82 | P[j].set_bound(k); 83 | j++; 84 | } 85 | } 86 | 87 | // sequential dynamic greedy coloring and sort 88 | static void neigh_coloring_dense( 89 | vector& vs, 90 | vector& es, 91 | vector &P, 92 | vector& ind, 93 | vector& C, 94 | vector& C_max, 95 | vector< vector >& colors, 96 | int& mc, 97 | bool** &adj) { 98 | 99 | int j = 0, u = 0, k = 1, k_prev = 0; 100 | int max_k = 1; 101 | int min_k = mc - C.size() + 1; 102 | 103 | colors[1].clear(); colors[2].clear(); 104 | 105 | for (int w=0; w < P.size(); w++) { 106 | u = P[w].get_id(); 107 | k = 1, k_prev = 0; 108 | 109 | while (k > k_prev) { 110 | k_prev = k; 111 | for (int i = 0; i < colors[k].size(); i++) { //use directly, sort makes it fast! 112 | if (adj[u][colors[k][i]]) { 113 | k++; 114 | break; 115 | } 116 | } 117 | } 118 | 119 | if (k > max_k) { 120 | max_k = k; 121 | colors[max_k+1].clear(); 122 | } 123 | 124 | colors[k].push_back(u); 125 | if (k < min_k) { 126 | P[j].set_id(u); 127 | j++; 128 | } 129 | } 130 | 131 | if (j > 0) P[j-1].set_bound(0); 132 | if (min_k <= 0) min_k = 1; 133 | 134 | for (k = min_k; k <= max_k; k++) 135 | for (int w = 0; w < colors[k].size(); w++) { 136 | P[j].set_id(colors[k][w]); 137 | P[j].set_bound(k); 138 | j++; 139 | } 140 | } 141 | } 142 | #endif 143 | -------------------------------------------------------------------------------- /pmc/pmc_neigh_cores.h: -------------------------------------------------------------------------------- 1 | /** 2 | ============================================================================ 3 | Name : Parallel Maximum Clique (PMC) Library 4 | Author : Ryan A. Rossi (rrossi@purdue.edu) 5 | Description : A general high-performance parallel framework for computing 6 | maximum cliques. The library is designed to be fast for large 7 | sparse graphs. 8 | 9 | Copyright (C) 2012-2013, Ryan A. Rossi, All rights reserved. 10 | 11 | Please cite the following paper if used: 12 | Ryan A. Rossi, David F. Gleich, Assefaw H. Gebremedhin, Md. Mostofa 13 | Patwary, A Fast Parallel Maximum Clique Algorithm for Large Sparse Graphs 14 | and Temporal Strong Components, arXiv preprint 1302.6256, 2013. 15 | 16 | See http://ryanrossi.com/pmc for more information. 17 | ============================================================================ 18 | */ 19 | 20 | #ifndef PMC_NEIGH_CORES_H_ 21 | #define PMC_NEIGH_CORES_H_ 22 | 23 | #include "pmc_vertex.h" 24 | 25 | using namespace std; 26 | 27 | namespace pmc { 28 | 29 | static void neigh_cores_bound( 30 | vector& vs, 31 | vector& es, 32 | vector &P, 33 | vector& ind, 34 | int& mc) { 35 | 36 | int n = P.size() + 1; 37 | 38 | // lookup table 39 | vector newids_to_actual(n, 0); 40 | vector vert_order(n,0); 41 | vector deg(n,0); 42 | vector pos(n,0); 43 | 44 | // lookup table for neighbors 45 | for (int v = 1; v < n; v++) ind[P[v-1].get_id()] = 1; 46 | 47 | // compute degrees of induced neighborhood 48 | int md = 0, x, u; 49 | for (int v = 1; v < n; v++) { // for each v in P 50 | u = P[v-1].get_id(); 51 | x = 0; 52 | for (long long j=vs[u]; j md) md = deg[v]; 57 | } 58 | 59 | int md_end = md+1; 60 | vector bin(md_end,0); 61 | for (int v = 1; v < n; v++) bin[deg[v]]++; 62 | 63 | int start = 1, num = 0; 64 | for (int d=0; d < md_end; d++) { //for each deg, set bin to be the pos of the first vertex of that degree 65 | num = bin[d]; 66 | bin[d] = start; 67 | start = start + num; 68 | } 69 | 70 | 71 | for (int v=1; v 1; d--) bin[d] = bin[d-1]; 82 | bin[0] = 1; 83 | 84 | 85 | int v_newid, v_actual, u_newid, du, pu, pw, w; 86 | long long j = 0; 87 | for (int i = 1; i < n; i++) { // neighborhood K-cores 88 | v_newid = vert_order[i]; //relabeled id 89 | v_actual = newids_to_actual[v_newid]; // real id 90 | for (j = vs[v_actual]; j 0) { // find common induced neighbors of k 92 | 93 | u_newid = ind[es[j]]; 94 | if (deg[u_newid] > deg[v_newid]) { 95 | du = deg[u_newid]; 96 | pu = pos[u_newid]; 97 | pw = bin[du]; 98 | w = vert_order[pw]; 99 | if (u_newid != w) { 100 | pos[u_newid] = pw; 101 | vert_order[pu] = w; 102 | pos[w] = pu; 103 | vert_order[pw] = u_newid; 104 | } 105 | bin[du]++; deg[u_newid]--; 106 | } 107 | } 108 | } 109 | } 110 | 111 | // reset index 112 | for (int v=1; v 0; --i) { 118 | u = vert_order[i]; 119 | if (deg[u]+1 > mc) { 120 | P[id].set_bound(deg[u]); 121 | P[id].set_id(newids_to_actual[u]); 122 | id++; 123 | } 124 | else prune_vert++; 125 | } 126 | 127 | // remove pruned verts from P 128 | for (int i = 0; i < prune_vert; i++) 129 | P.pop_back(); 130 | } 131 | 132 | 133 | static void neigh_cores_tight( 134 | vector& vs, 135 | vector& es, 136 | vector &P, 137 | vector& ind, 138 | int& mc) { 139 | 140 | int n = P.size() + 1; 141 | 142 | // lookup table 143 | vector newids_to_actual(n, 0); 144 | vector vert_order(n,0); 145 | vector deg(n,0); 146 | vector pos(n,0); 147 | 148 | 149 | // lookup table for neighbors 150 | for (int v = 1; v < n; v++) ind[P[v-1].get_id()] = 1; 151 | 152 | // compute degrees of induced neighborhood 153 | int md = 0, x, u; 154 | for (int v = n-1; v >= 1; v--) { 155 | u = P[v-1].get_id(); 156 | x = 0; 157 | for (long long j=vs[u]; j= mc) { 161 | deg[v] = x; 162 | if (deg[v] > md) md = deg[v]; 163 | } 164 | else { 165 | deg[v] = 0; 166 | ind[P[v-1].get_id()] = 0; 167 | } 168 | } 169 | 170 | int md_end = md+1; 171 | vector bin(md_end,0); 172 | for (int v = 1; v < n; v++) bin[deg[v]]++; 173 | 174 | int start = 1, num = 0; 175 | for (int d=0; d < md_end; d++) { //for each deg, set bin to be the pos of the first vertex of that degree 176 | num = bin[d]; 177 | bin[d] = start; 178 | start = start + num; 179 | } 180 | 181 | 182 | for (int v=1; v 0) { 184 | pos[v] = bin[deg[v]]; 185 | 186 | //view this step as relabeling the vertices 187 | vert_order[pos[v]] = v; 188 | ind[P[v-1].get_id()] = v; // set bit for actual vertex id 189 | newids_to_actual[v] = P[v-1].get_id(); 190 | bin[deg[v]]++; 191 | } 192 | else 193 | ind[P[v-1].get_id()] = 0; 194 | } 195 | 196 | for (int d=md; d > 1; d--) bin[d] = bin[d-1]; 197 | bin[0] = 1; 198 | 199 | 200 | int v_newid, v_actual, u_newid, du, pu, pw, w; 201 | long long j = 0; 202 | for (int i = 1; i < n; i++) { // neighborhood K-cores 203 | v_newid = vert_order[i]; 204 | if (deg[v_newid] > 0) { 205 | //relabeled id 206 | v_actual = newids_to_actual[v_newid]; // real id 207 | for (j = vs[v_actual]; j 0) { // find common induced neighbors of k 209 | 210 | u_newid = ind[es[j]]; 211 | if (deg[u_newid] > deg[v_newid]) { 212 | du = deg[u_newid]; 213 | pu = pos[u_newid]; 214 | pw = bin[du]; 215 | w = vert_order[pw]; 216 | if (u_newid != w) { 217 | pos[u_newid] = pw; 218 | vert_order[pu] = w; 219 | pos[w] = pu; 220 | vert_order[pw] = u_newid; 221 | } 222 | bin[du]++; deg[u_newid]--; 223 | } 224 | } 225 | } 226 | } 227 | } 228 | 229 | // reset index 230 | for (int v=1; v 0; --i) { 236 | u = vert_order[i]; 237 | if (deg[u]+1 > mc) { 238 | P[id].set_bound(deg[u]); 239 | P[id].set_id(newids_to_actual[u]); 240 | id++; 241 | } 242 | else prune_vert++; 243 | } 244 | 245 | // remove pruned verts from P 246 | for (int i = 0; i < prune_vert; i++) 247 | P.pop_back(); 248 | } 249 | } 250 | 251 | 252 | #endif 253 | -------------------------------------------------------------------------------- /pmc/pmc_utils.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | ============================================================================ 3 | Name : Parallel Maximum Clique (PMC) Library 4 | Author : Ryan A. Rossi (rrossi@purdue.edu) 5 | Description : A general high-performance parallel framework for computing 6 | maximum cliques. The library is designed to be fast for large 7 | sparse graphs. 8 | 9 | Copyright (C) 2012-2013, Ryan A. Rossi, All rights reserved. 10 | 11 | Please cite the following paper if used: 12 | Ryan A. Rossi, David F. Gleich, Assefaw H. Gebremedhin, Md. Mostofa 13 | Patwary, A Fast Parallel Maximum Clique Algorithm for Large Sparse Graphs 14 | and Temporal Strong Components, arXiv preprint 1302.6256, 2013. 15 | 16 | See http://ryanrossi.com/pmc for more information. 17 | ============================================================================ 18 | */ 19 | 20 | #include "pmc_utils.h" 21 | 22 | using namespace std; 23 | 24 | bool fexists(const char *filename) { 25 | int nFlag = access(filename, 0); 26 | if (0 == nFlag) { 27 | return true; 28 | } 29 | return false; 30 | } 31 | 32 | void usage(char *argv0) { 33 | const char *params = 34 | "Usage: %s -a alg -f graphfile -t threads -o ordering -h heu_strat -u upper_bound -l lower_bound -r reduce_wait_time -w time_limit \n" 35 | "\t-a algorithm : Algorithm for solving MAX-CLIQUE: 0 = full, 1 = no neighborhood cores, 2 = only basic k-core pruning steps \n" 36 | "\t-f graph file : Input GRAPH file for computing the Maximum Clique (matrix market format or simple edge list). \n" 37 | "\t-o vertex search ordering : Order in which vertices are searched (default = deg, [kcore, dual_deg, dual_kcore, kcore_deg, rand]) \n" 38 | "\t-d decreasing order : Search vertices in DECREASING order. Note if '-d' is not set, then vertices are searched in increasing order by default. \n" 39 | "\t-e neigh/edge ordering : Ordering of neighbors/edges (default = deg, [kcore, dual_deg, dual_kcore, kcore_deg, rand]) \n" 40 | "\t-h heuristic strategy : Strategy for HEURISTIC method (default = kcore, [deg, dual_deg, dual_kcore, rand, 0 = skip heuristic]) \n" 41 | "\t-u upper_bound : UPPER-BOUND on clique size (default = K-cores).\n" 42 | "\t-l lower_bound : LOWER-BOUND on clique size (default = Estimate using the Fast Heuristic). \n" 43 | "\t-t threads : Number of THREADS for the algorithm to use (default = 1). \n" 44 | "\t-r reduce_wait : Number of SECONDS to wait before inducing the graph based on the unpruned vertices (default = 4 seconds). \n" 45 | "\t-w time_limit : Execution TIME LIMIT spent searching for max clique (default = 7 days) \n" 46 | "\t-k clique size : Solve K-CLIQUE problem: find clique of size k if it exists. Parameterized to be fast. \n" 47 | "\t-s stats : Compute BOUNDS and other fast graph stats \n" 48 | "\t-v verbose : Output additional details to the screen. \n" 49 | "\t-? options : Print out this help menu. \n"; 50 | fprintf(stderr, params, argv0); 51 | exit(-1); 52 | } 53 | 54 | //// return in second. 55 | double get_time() { 56 | // timeval t; 57 | // gettimeofday(&t, NULL); 58 | //return t.tv_sec*1.0 + t.tv_usec/1000000.0; 59 | SYSTEMTIME t; 60 | GetLocalTime(&t); 61 | return (t.wHour * 60 + t.wMinute) * 60.0 + t.wSecond + t.wMilliseconds / 1000.0; 62 | 63 | } 64 | 65 | string memory_usage() { 66 | ostringstream mem; 67 | ifstream proc("/proc/self/status"); 68 | string s; 69 | while(getline(proc, s), !proc.fail()) { 70 | if(s.substr(0, 6) == "VmSize") { 71 | mem << s; 72 | return mem.str(); 73 | } 74 | } 75 | return mem.str(); 76 | } 77 | 78 | void indent(int level, string str) { 79 | for (int i = 0; i < level; i++) 80 | cout << " "; 81 | cout << "(" << level << ") "; 82 | } 83 | 84 | void print_max_clique(vector& C) { 85 | cout << "Maximum clique: "; 86 | for(int i = 0; i < C.size(); i++) 87 | cout << C[i] + 1 << " "; 88 | cout << endl; 89 | } 90 | 91 | void print_n_maxcliques(set< vector > C, int n) { 92 | set< vector >::iterator it; 93 | int mc = 0; 94 | for( it = C.begin(); it != C.end(); it++) { 95 | if (mc < n) { 96 | cout << "Maximum clique: "; 97 | const vector& clq = (*it); 98 | for (int j = 0; j < clq.size(); j++) 99 | cout << clq[j] << " "; 100 | cout < &files) { 116 | DIR *dp; 117 | struct dirent *dirp; 118 | if((dp = opendir(dir.c_str())) == NULL) { 119 | cout << "Error(" << errno << ") opening " << dir << endl; 120 | return errno; 121 | } 122 | 123 | while ((dirp = readdir(dp)) != NULL) { 124 | if (dirp->d_name != ".") 125 | files.push_back(string(dirp->d_name)); 126 | } 127 | closedir(dp); 128 | return 0; 129 | } 130 | 131 | 132 | -------------------------------------------------------------------------------- /pmc/pmc_utils.h: -------------------------------------------------------------------------------- 1 | /** 2 | ============================================================================ 3 | Name : Parallel Maximum Clique (PMC) Library 4 | Author : Ryan A. Rossi (rrossi@purdue.edu) 5 | Description : A general high-performance parallel framework for computing 6 | maximum cliques. The library is designed to be fast for large 7 | sparse graphs. 8 | 9 | Copyright (C) 2012-2013, Ryan A. Rossi, All rights reserved. 10 | 11 | Please cite the following paper if used: 12 | Ryan A. Rossi, David F. Gleich, Assefaw H. Gebremedhin, Md. Mostofa 13 | Patwary, A Fast Parallel Maximum Clique Algorithm for Large Sparse Graphs 14 | and Temporal Strong Components, arXiv preprint 1302.6256, 2013. 15 | 16 | See http://ryanrossi.com/pmc for more information. 17 | ============================================================================ 18 | */ 19 | 20 | #ifndef PMC_UTILS_H_ 21 | #define PMC_UTILS_H_ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "assert.h" 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "pmc_headers.h" 34 | 35 | 36 | using namespace std; 37 | 38 | bool fexists(const char *filename); 39 | void usage(char *argv0); 40 | 41 | double get_time(); 42 | string memory_usage(); 43 | 44 | void validate(bool condition, const string& msg); 45 | 46 | void indent(int level); 47 | void indent(int level, string str); 48 | void print_max_clique(vector& max_clique_data); 49 | void print_n_maxcliques(set< vector > C, int n); 50 | 51 | int getdir (string dir, vector &files); 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /pmc/pmc_vertex.h: -------------------------------------------------------------------------------- 1 | /** 2 | ============================================================================ 3 | Name : Parallel Maximum Clique (PMC) Library 4 | Author : Ryan A. Rossi (rrossi@purdue.edu) 5 | Description : A general high-performance parallel framework for computing 6 | maximum cliques. The library is designed to be fast for large 7 | sparse graphs. 8 | 9 | Copyright (C) 2012-2013, Ryan A. Rossi, All rights reserved. 10 | 11 | Please cite the following paper if used: 12 | Ryan A. Rossi, David F. Gleich, Assefaw H. Gebremedhin, Md. Mostofa 13 | Patwary, A Fast Parallel Maximum Clique Algorithm for Large Sparse Graphs 14 | and Temporal Strong Components, arXiv preprint 1302.6256, 2013. 15 | 16 | See http://ryanrossi.com/pmc for more information. 17 | ============================================================================ 18 | */ 19 | 20 | #ifndef PMC_VERTEX_H_ 21 | #define PMC_VERTEX_H_ 22 | 23 | using namespace std; 24 | 25 | namespace pmc { 26 | class Vertex { 27 | private: 28 | int id, b; 29 | public: 30 | Vertex(int vertex_id, int bound): id(vertex_id), b(bound) {}; 31 | 32 | void set_id(int vid) { id = vid; } 33 | int get_id() { return id; } 34 | 35 | void set_bound(int value) { b = value; } 36 | int get_bound() { return b; } 37 | }; 38 | 39 | static bool decr_bound(Vertex v, Vertex u) { 40 | return (v.get_bound() > u.get_bound()); 41 | } 42 | static bool incr_bound(Vertex v, Vertex u) { 43 | return (v.get_bound() < u.get_bound()); 44 | }; 45 | 46 | inline static void print_mc_info(vector &C_max, double &sec) { 47 | cout << "*** [pmc: thread " << omp_get_thread_num() + 1; 48 | cout << "] current max clique = " << C_max.size(); 49 | cout << ", time = " << get_time() - sec << " sec" < 24 | #include 25 | #include 26 | #include 27 | #include "pmc_headers.h" 28 | #include "pmc_utils.h" 29 | #include "pmc_graph.h" 30 | #include "pmc_input.h" 31 | #include "pmc_vertex.h" 32 | #include "pmc_neigh_cores.h" 33 | #include "pmc_neigh_coloring.h" 34 | #include 35 | 36 | using namespace std; 37 | 38 | namespace pmc { 39 | 40 | class pmcx_maxclique { 41 | public: 42 | vector* edges; 43 | vector* vertices; 44 | vector* bound; 45 | vector* order; 46 | int param_ub; 47 | int ub; 48 | int lb; 49 | double time_limit; 50 | double sec; 51 | double wait_time; 52 | bool not_reached_ub; 53 | bool time_expired_msg; 54 | bool decr_order; 55 | 56 | string vertex_ordering; 57 | int edge_ordering; 58 | int style_bounds; 59 | int style_dynamic_bounds; 60 | 61 | int num_threads; 62 | 63 | void initialize() { 64 | vertex_ordering = "deg"; 65 | edge_ordering = 0; 66 | style_bounds = 0; 67 | style_dynamic_bounds = 0; 68 | not_reached_ub = true; 69 | time_expired_msg = true; 70 | decr_order = false; 71 | } 72 | 73 | void setup_bounds(input& params) { 74 | lb = params.lb; 75 | ub = params.ub; 76 | param_ub = params.param_ub; 77 | if (param_ub == 0) 78 | param_ub = ub; 79 | time_limit = params.time_limit; 80 | wait_time = params.remove_time; 81 | sec = get_time(); 82 | 83 | num_threads = params.threads; 84 | } 85 | 86 | pmcx_maxclique(pmc_graph& G, input& params) { 87 | bound = G.get_kcores(); 88 | order = G.get_kcore_ordering(); 89 | setup_bounds(params); 90 | initialize(); 91 | vertex_ordering = params.vertex_search_order; 92 | decr_order = params.decreasing_order; 93 | } 94 | 95 | ~pmcx_maxclique() {}; 96 | 97 | int search(pmc_graph& G, vector& sol); 98 | inline void branch( 99 | vector& vs, 100 | vector& es, 101 | vector &P, 102 | vector& ind, 103 | vector& C, 104 | vector& C_max, 105 | vector< vector >& colors, 106 | int* &pruned, 107 | int& mc); 108 | 109 | int search_dense(pmc_graph& G, vector& sol); 110 | inline void branch_dense( 111 | vector& vs, 112 | vector& es, 113 | vector &P, 114 | vector& ind, 115 | vector& C, 116 | vector& C_max, 117 | vector< vector >& colors, 118 | int* &pruned, 119 | int& mc, 120 | bool** &adj); 121 | 122 | }; 123 | }; 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /pmc/pmcx_maxclique_basic.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | ============================================================================ 3 | Name : Parallel Maximum Clique (PMC) Library 4 | Author : Ryan A. Rossi (rrossi@purdue.edu) 5 | Description : A general high-performance parallel framework for computing 6 | maximum cliques. The library is designed to be fast for large 7 | sparse graphs. 8 | 9 | Copyright (C) 2012-2013, Ryan A. Rossi, All rights reserved. 10 | 11 | Please cite the following paper if used: 12 | Ryan A. Rossi, David F. Gleich, Assefaw H. Gebremedhin, Md. Mostofa 13 | Patwary, A Fast Parallel Maximum Clique Algorithm for Large Sparse Graphs 14 | and Temporal Strong Components, arXiv preprint 1302.6256, 2013. 15 | 16 | See http://ryanrossi.com/pmc for more information. 17 | ============================================================================ 18 | */ 19 | 20 | #include "pmcx_maxclique_basic.h" 21 | 22 | using namespace std; 23 | using namespace pmc; 24 | 25 | int pmcx_maxclique_basic::search(pmc_graph& G, vector& sol) { 26 | 27 | vertices = G.get_vertices(); 28 | edges = G.get_edges(); 29 | degree = G.get_degree(); 30 | int* pruned = new int[G.num_vertices()]; 31 | memset(pruned, 0, G.num_vertices() * sizeof(int)); 32 | int mc = lb, i = 0, u = 0; 33 | 34 | // initial pruning 35 | int lb_idx = G.initial_pruning(G, pruned, lb); 36 | 37 | // set to worst case bound of cores/coloring 38 | vector P, T; 39 | P.reserve(G.get_max_degree()+1); 40 | T.reserve(G.get_max_degree()+1); 41 | 42 | vector C, C_max; 43 | C.reserve(G.get_max_degree()+1); 44 | C_max.reserve(G.get_max_degree()+1); 45 | 46 | // init the neigh coloring array 47 | vector< vector > colors(G.get_max_degree()+1); 48 | for (int i = 0; i < G.get_max_degree()+1; i++) colors[i].reserve(G.get_max_degree()+1); 49 | 50 | // order verts for our search routine 51 | vector V; 52 | V.reserve(G.num_vertices()); 53 | G.order_vertices(V,G,lb_idx,lb,vertex_ordering,decr_order); 54 | cout << "|V| = " << V.size() < ind(G.num_vertices(),0); 57 | vector es = G.get_edges_array(); 58 | vector vs = G.get_vertices_array(); 59 | 60 | vector induce_time(num_threads,get_time()); 61 | for (int t = 0; t < num_threads; ++t) induce_time[t] = induce_time[t] + t/4; 62 | 63 | #pragma omp parallel for schedule(dynamic) shared(pruned, G, T, V, mc, C_max, induce_time) \ 64 | firstprivate(colors,ind,vs,es) private(u, P, C) 65 | for (i = 0; i < (V.size()) - (mc-1); ++i) { 66 | if (G.time_left(C_max,sec,time_limit,time_expired_msg)) { 67 | 68 | u = V[i].get_id(); 69 | if ((*bound)[u] > mc) { 70 | P.push_back(V[i]); 71 | for (long long j = vs[u]; j < vs[u + 1]; ++j) 72 | if (!pruned[es[j]]) 73 | if ((*bound)[es[j]] > mc) 74 | P.push_back(Vertex(es[j], (*degree)[es[j]])); 75 | 76 | if (P.size() > mc) { 77 | neigh_coloring_bound(vs,es,P,ind,C,C_max,colors,pruned,mc); 78 | if (P.back().get_bound() > mc) { 79 | branch(vs,es,P, ind, C, C_max, colors, pruned, mc); 80 | } 81 | } 82 | P = T; 83 | } 84 | pruned[u] = 1; 85 | 86 | // dynamically reduce graph in a thread-safe manner 87 | if ((get_time() - induce_time[omp_get_thread_num()]) > wait_time) { 88 | G.reduce_graph( vs, es, pruned, G, i+lb_idx, mc); 89 | G.graph_stats(G, mc, i+lb_idx, sec); 90 | induce_time[omp_get_thread_num()] = get_time(); 91 | } 92 | } 93 | } 94 | 95 | if (pruned) delete[] pruned; 96 | 97 | sol.resize(mc); 98 | for (int i = 0; i < C_max.size(); i++) sol[i] = C_max[i]; 99 | G.print_break(); 100 | return sol.size(); 101 | } 102 | 103 | 104 | 105 | 106 | void pmcx_maxclique_basic::branch( 107 | vector& vs, 108 | vector& es, 109 | vector &P, 110 | vector& ind, 111 | vector& C, 112 | vector& C_max, 113 | vector< vector >& colors, 114 | int* &pruned, 115 | int& mc) { 116 | 117 | // stop early if ub is reached 118 | if (not_reached_ub) { 119 | while (P.size() > 0) { 120 | // terminating condition 121 | if (C.size() + P.back().get_bound() > mc) { 122 | int v = P.back().get_id(); C.push_back(v); 123 | 124 | vector R; R.reserve(P.size()); 125 | for (long long j = vs[v]; j < vs[v + 1]; j++) ind[es[j]] = 1; 126 | 127 | // intersection of N(v) and P - {v} 128 | for (int k = 0; k < P.size() - 1; k++) 129 | if (ind[P[k].get_id()]) 130 | if (!pruned[P[k].get_id()]) 131 | if ((*bound)[P[k].get_id()] > mc) 132 | R.push_back(P[k]); 133 | 134 | for (long long j = vs[v]; j < vs[v + 1]; j++) ind[es[j]] = 0; 135 | 136 | 137 | if (R.size() > 0) { 138 | // color graph induced by R and sort for O(1) bound check 139 | neigh_coloring_bound(vs, es, R, ind, C, C_max, colors, pruned, mc); 140 | // search reordered R 141 | branch(vs, es, R, ind, C, C_max, colors, pruned, mc); 142 | } 143 | else if (C.size() > mc) { 144 | // obtain lock 145 | #pragma omp critical (update_mc) 146 | if (C.size() > mc) { 147 | // ensure updated max is flushed 148 | mc = C.size(); 149 | C_max = C; 150 | print_mc_info(C,sec); 151 | if (mc >= param_ub) { 152 | not_reached_ub = false; 153 | cout << "[pmc: upper bound reached] omega = " << mc <& sol) { 181 | 182 | vertices = G.get_vertices(); 183 | edges = G.get_edges(); 184 | degree = G.get_degree(); 185 | bool** adj = G.adj; 186 | 187 | int* pruned = new int[G.num_vertices()]; 188 | memset(pruned, 0, G.num_vertices() * sizeof(int)); 189 | int mc = lb, i = 0, u = 0; 190 | 191 | // initial pruning 192 | int lb_idx = G.initial_pruning(G, pruned, lb, adj); 193 | 194 | // set to worst case bound of cores/coloring 195 | vector P, T; 196 | P.reserve(G.get_max_degree()+1); 197 | T.reserve(G.get_max_degree()+1); 198 | 199 | vector C, C_max; 200 | C.reserve(G.get_max_degree()+1); 201 | C_max.reserve(G.get_max_degree()+1); 202 | 203 | // init the neigh coloring array 204 | vector< vector > colors(G.get_max_degree()+1); 205 | for (int i = 0; i < G.get_max_degree()+1; i++) colors[i].reserve(G.get_max_degree()+1); 206 | 207 | // order verts for our search routine 208 | vector V; 209 | V.reserve(G.num_vertices()); 210 | G.order_vertices(V,G,lb_idx,lb,vertex_ordering,decr_order); 211 | cout << "|V| = " << V.size() < ind(G.num_vertices(),0); 214 | vector es = G.get_edges_array(); 215 | vector vs = G.get_vertices_array(); 216 | 217 | vector induce_time(num_threads,get_time()); 218 | for (int t = 0; t < num_threads; ++t) induce_time[t] = induce_time[t] + t/4; 219 | 220 | 221 | #pragma omp parallel for schedule(dynamic) shared(pruned, G, adj, T, V, mc, C_max, induce_time) \ 222 | firstprivate(colors,ind,vs,es) private(u, P, C) 223 | for (i = 0; i < (V.size()) - (mc-1); ++i) { 224 | if (G.time_left(C_max,sec,time_limit,time_expired_msg)) { 225 | 226 | u = V[i].get_id(); 227 | if ((*bound)[u] > mc) { 228 | P.push_back(V[i]); 229 | for (long long j = vs[u]; j < vs[u + 1]; ++j) 230 | if (!pruned[es[j]]) 231 | if ((*bound)[es[j]] > mc) 232 | P.push_back(Vertex(es[j], (*degree)[es[j]])); 233 | 234 | if (P.size() > mc) { 235 | neigh_coloring_dense(vs,es,P,ind,C,C_max,colors,mc, adj); 236 | if (P.back().get_bound() > mc) { 237 | branch_dense(vs,es,P, ind, C, C_max, colors, pruned, mc, adj); 238 | } 239 | } 240 | P = T; 241 | } 242 | pruned[u] = 1; 243 | for (long long j = vs[u]; j < vs[u + 1]; j++) { 244 | adj[u][es[j]] = false; 245 | adj[es[j]][u] = false; 246 | } 247 | 248 | // dynamically reduce graph in a thread-safe manner 249 | if ((get_time() - induce_time[omp_get_thread_num()]) > wait_time) { 250 | G.reduce_graph( vs, es, pruned, G, i+lb_idx, mc); 251 | G.graph_stats(G, mc, i+lb_idx, sec); 252 | induce_time[omp_get_thread_num()] = get_time(); 253 | } 254 | } 255 | } 256 | 257 | if (pruned) delete[] pruned; 258 | 259 | sol.resize(mc); 260 | for (int i = 0; i < C_max.size(); i++) sol[i] = C_max[i]; 261 | G.print_break(); 262 | return sol.size(); 263 | } 264 | 265 | 266 | 267 | 268 | void pmcx_maxclique_basic::branch_dense( 269 | vector& vs, 270 | vector& es, 271 | vector &P, 272 | vector& ind, 273 | vector& C, 274 | vector& C_max, 275 | vector< vector >& colors, 276 | int* &pruned, 277 | int& mc, 278 | bool** &adj) { 279 | 280 | // stop early if ub is reached 281 | if (not_reached_ub) { 282 | while (P.size() > 0) { 283 | // terminating condition 284 | if (C.size() + P.back().get_bound() > mc) { 285 | int v = P.back().get_id(); C.push_back(v); 286 | vector R; R.reserve(P.size()); 287 | 288 | for (int k = 0; k < P.size() - 1; k++) 289 | // indicates neighbor AND pruned, since threads dynamically update it 290 | if (adj[v][P[k].get_id()]) 291 | if ((*bound)[P[k].get_id()] > mc) 292 | R.push_back(P[k]); 293 | 294 | if (R.size() > 0) { 295 | // color graph induced by R and sort for O(1) 296 | neigh_coloring_dense(vs, es, R, ind, C, C_max, colors, mc, adj); 297 | branch_dense(vs, es, R, ind, C, C_max, colors, pruned, mc, adj); 298 | } 299 | else if (C.size() > mc) { 300 | // obtain lock 301 | #pragma omp critical (update_mc) 302 | if (C.size() > mc) { 303 | // ensure updated max is flushed 304 | mc = C.size(); 305 | C_max = C; 306 | print_mc_info(C,sec); 307 | if (mc >= param_ub) { 308 | not_reached_ub = false; 309 | cout << "[pmc: upper bound reached] omega = " << mc < 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "pmc_headers.h" 29 | #include "pmc_utils.h" 30 | #include "pmc_graph.h" 31 | #include "pmc_input.h" 32 | #include "pmc_vertex.h" 33 | #include "pmc_neigh_cores.h" 34 | #include "pmc_neigh_coloring.h" 35 | 36 | using namespace std; 37 | 38 | namespace pmc { 39 | 40 | class pmcx_maxclique_basic { 41 | public: 42 | vector* edges; 43 | vector* vertices; 44 | vector* bound; 45 | vector* order; 46 | vector* degree; 47 | int param_ub; 48 | int ub; 49 | int lb; 50 | double time_limit; 51 | double sec; 52 | double wait_time; 53 | bool not_reached_ub; 54 | bool time_expired_msg; 55 | bool decr_order; 56 | 57 | string vertex_ordering; 58 | int edge_ordering; 59 | int style_bounds; 60 | int style_dynamic_bounds; 61 | 62 | int num_threads; 63 | 64 | void initialize() { 65 | vertex_ordering = "deg"; 66 | edge_ordering = 0; 67 | style_bounds = 0; 68 | style_dynamic_bounds = 0; 69 | not_reached_ub = true; 70 | time_expired_msg = true; 71 | decr_order = false; 72 | } 73 | 74 | void setup_bounds(input& params) { 75 | lb = params.lb; 76 | ub = params.ub; 77 | param_ub = params.param_ub; 78 | if (param_ub == 0) 79 | param_ub = ub; 80 | time_limit = params.time_limit; 81 | wait_time = params.remove_time; 82 | sec = get_time(); 83 | 84 | num_threads = params.threads; 85 | } 86 | 87 | 88 | pmcx_maxclique_basic(pmc_graph& G, input& params) { 89 | bound = G.get_kcores(); 90 | order = G.get_kcore_ordering(); 91 | setup_bounds(params); 92 | initialize(); 93 | vertex_ordering = params.vertex_search_order; 94 | decr_order = params.decreasing_order; 95 | } 96 | 97 | ~pmcx_maxclique_basic() {}; 98 | 99 | int search(pmc_graph& G, vector& sol); 100 | 101 | void branch( 102 | vector& vs, 103 | vector& es, 104 | vector &P, 105 | vector& ind, 106 | vector& C, 107 | vector& C_max, 108 | vector< vector >& colors, 109 | int* &pruned, 110 | int& mc); 111 | 112 | 113 | int search_dense(pmc_graph& G, vector& sol); 114 | 115 | void branch_dense( 116 | vector& vs, 117 | vector& es, 118 | vector &P, 119 | vector& ind, 120 | vector& C, 121 | vector& C_max, 122 | vector< vector >& colors, 123 | int* &pruned, 124 | int& mc, 125 | bool** &adj); 126 | 127 | }; 128 | }; 129 | 130 | #endif 131 | -------------------------------------------------------------------------------- /reg/RegBase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef H_REG_BASE_H 3 | #define H_REG_BASE_H 4 | #include "stdafx.h" 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | //// Use key word of virtual in order to be polymoriphsm. 11 | class CRegBase { 12 | public: 13 | EIGEN_MAKE_ALIGNED_OPERATOR_NEW 14 | //// point set. 15 | pcl::PointCloud::Ptr m_cloud_source; 16 | pcl::PointCloud::Ptr m_cloud_target; 17 | pcl::KdTreeFLANN::Ptr m_kd_tree; 18 | 19 | Eigen::Matrix3Xd m_Mov0; 20 | Eigen::Matrix3Xd m_Ref0; 21 | Eigen::Matrix3Xd m_RefNorm0; 22 | Eigen::Matrix3Xd m_tmpAft; 23 | Eigen::Matrix3Xd m_tmpRef; 24 | Eigen::Matrix3Xd m_tmpNor; 25 | std::vector> m_vInd; 26 | vector> m_vOmega; 27 | 28 | //// transformations. 29 | Eigen::Matrix4d m_Tf0; 30 | Eigen::Matrix4d m_rstTf; 31 | void init_cloud() { 32 | m_cloud_target.reset(new pcl::PointCloud()); 33 | m_cloud_source.reset(new pcl::PointCloud()); 34 | m_kd_tree.reset(new pcl::KdTreeFLANN()); 35 | } 36 | //// 37 | CRegBase() { 38 | init_cloud(); 39 | } 40 | //// 41 | virtual ~CRegBase() { 42 | 43 | } 44 | //// tmpAft must be specified. 45 | void compute_NN(double max_dist_sq = 1e8) { 46 | Eigen::Matrix3Xd Aft = m_tmpAft; 47 | vector vIndMov(Aft.cols(), -1); 48 | vector vIndRef(Aft.cols(), -1); 49 | #pragma omp parallel for 50 | for (int i = 0; i < Aft.cols(); ++i) { 51 | pcl::PointNormal querryPt; 52 | querryPt.x = Aft(0, i); 53 | querryPt.y = Aft(1, i); 54 | querryPt.z = Aft(2, i); 55 | std::vector vIdx; 56 | std::vector vDist2; 57 | m_kd_tree->nearestKSearch(querryPt, 1, vIdx, vDist2); 58 | if (vDist2[0] < max_dist_sq) { 59 | vIndMov[i] = i; 60 | vIndRef[i] = vIdx[0]; 61 | } 62 | } 63 | m_vInd.clear(); 64 | for (int i = 0; i < vIndMov.size(); i++) { 65 | if (vIndMov[i] != -1) { 66 | m_vInd.push_back( std::make_pair(vIndMov[i], vIndRef[i]) ); 67 | } 68 | } 69 | //// store the reference in m_tmpRef. 70 | m_tmpRef.resize(3, m_vInd.size()); 71 | m_tmpAft.resize(3, m_vInd.size()); 72 | //// the normal info should also be retrived. 73 | bool bUseNorm = m_RefNorm0.size() > 0; 74 | if (bUseNorm) { 75 | m_tmpNor.resize(3, m_vInd.size()); 76 | } 77 | for (int i = 0; i < m_vInd.size(); i++) { 78 | int nMov = m_vInd[i].first; 79 | int nRef = m_vInd[i].second; 80 | m_tmpAft.col(i) = Aft.col(nMov); 81 | m_tmpRef.col(i) = m_Ref0.col(nRef); 82 | if (bUseNorm) { 83 | m_tmpNor.col(i) = m_RefNorm0.col(nRef); 84 | } 85 | } 86 | } 87 | //// compute the point-to-plane distance. 88 | double compute_dist(const pcl::PointNormal& pt_source, const pcl::PointNormal& pt_target) { 89 | Eigen::Vector3d pt0, pt1; 90 | pt0 << pt_source.x, pt_source.y, pt_source.z; 91 | pt1 << pt_target.x, pt_target.y, pt_target.z; 92 | Eigen::Vector3d nor; 93 | nor << pt_target.normal_x, pt_target.normal_y, pt_target.normal_z; 94 | double dist = abs( nor.dot(pt0 - pt1) ); 95 | return pow(dist, 2); 96 | } 97 | //// tmpAft must be specified. 98 | void compute_NN_reciprocal(const pcl::KdTreeFLANN::Ptr& kd_tree_source, 99 | const Eigen::Matrix4d& rstTf, double max_dist_sq) { 100 | Eigen::Matrix4d invTf = rstTf.inverse(); 101 | Eigen::Matrix3Xd Aft = m_tmpAft; 102 | vector vIndMov(Aft.cols(), -1); 103 | vector vIndRef(Aft.cols(), -1); 104 | #pragma omp parallel for 105 | for (int i = 0; i < Aft.cols(); ++i) { 106 | pcl::PointNormal querryPt; 107 | querryPt.x = Aft(0, i); 108 | querryPt.y = Aft(1, i); 109 | querryPt.z = Aft(2, i); 110 | std::vector vIdx; 111 | std::vector vDist2; 112 | m_kd_tree->nearestKSearch(querryPt, 1, vIdx, vDist2); 113 | int nId_target = vIdx[0]; 114 | double dist_target = vDist2[0]; 115 | if (dist_target < max_dist_sq) { 116 | pcl::PointNormal targetPt = m_cloud_target->points[nId_target]; 117 | Eigen::Vector4d pt; 118 | pt << targetPt.x, targetPt.y, targetPt.z, 1; 119 | pt.noalias() = invTf * pt; 120 | targetPt.x = pt(0); 121 | targetPt.y = pt(1); 122 | targetPt.z = pt(2); 123 | kd_tree_source->nearestKSearch(targetPt, 1, vIdx, vDist2); 124 | int nId_source = vIdx[0]; 125 | double dist_source = vDist2[0]; 126 | double dist_diff = abs(dist_source - dist_target); 127 | if (dist_source < max_dist_sq && dist_diff <= 0.5) { // nId_source == i 128 | 129 | vIndMov[i] = i; 130 | vIndRef[i] = nId_target; 131 | } 132 | } 133 | } 134 | m_vInd.clear(); 135 | for (int i = 0; i < vIndMov.size(); i++) { 136 | if (vIndMov[i] != -1) { 137 | m_vInd.push_back(std::make_pair(vIndMov[i], vIndRef[i])); 138 | } 139 | } 140 | //// store the reference in m_tmpRef. 141 | m_tmpRef.resize(3, m_vInd.size()); 142 | m_tmpAft.resize(3, m_vInd.size()); 143 | bool bUseNorm = m_RefNorm0.size() > 0; 144 | if (bUseNorm) { 145 | m_tmpNor.resize(3, m_vInd.size()); 146 | } 147 | for (int i = 0; i < m_vInd.size(); i++) { 148 | int nMov = m_vInd[i].first; 149 | int nRef = m_vInd[i].second; 150 | m_tmpAft.col(i) = Aft.col(nMov); 151 | m_tmpRef.col(i) = m_Ref0.col(nRef); 152 | if (bUseNorm) { 153 | m_tmpNor.col(i) = m_RefNorm0.col(nRef); 154 | } 155 | } 156 | } 157 | 158 | virtual void compute_statistics_target() { 159 | 160 | } 161 | 162 | virtual void compute_statistics_source() { 163 | 164 | } 165 | 166 | virtual void setParams(string& sMethod) { 167 | 168 | } 169 | 170 | virtual int getTCIterations() { 171 | return 0; 172 | } 173 | 174 | void setInputTarget(const pcl::PointCloud::Ptr& cloud_target, 175 | const boost::shared_ptr> kd_tree_ptr = nullptr ) { 176 | pcl::copyPointCloud(*cloud_target, *m_cloud_target); 177 | if (kd_tree_ptr == nullptr) { 178 | m_kd_tree.reset(new pcl::KdTreeFLANN()); 179 | m_kd_tree->setInputCloud(m_cloud_target); 180 | } else { 181 | m_kd_tree = kd_tree_ptr; 182 | } 183 | compute_statistics_target(); 184 | m_Ref0.resize(3, m_cloud_target->points.size()); 185 | m_RefNorm0.resize(3, m_cloud_target->points.size()); 186 | for (int i = 0; i < m_cloud_target->points.size(); i++) { 187 | pcl::PointNormal pt = m_cloud_target->points[i]; 188 | m_Ref0.col(i) << pt.x, pt.y, pt.z; 189 | m_RefNorm0.col(i) << pt.normal_x, pt.normal_y, pt.normal_z; 190 | } 191 | } 192 | 193 | void setInputSource(const pcl::PointCloud::Ptr& cloud_source) { 194 | pcl::copyPointCloud(*cloud_source, *m_cloud_source); 195 | m_Mov0.resize(3, m_cloud_source->points.size()); 196 | for (int i = 0; i < m_cloud_source->points.size(); i++) { 197 | pcl::PointNormal pt = m_cloud_source->points[i]; 198 | m_Mov0.col(i) << pt.x, pt.y, pt.z; 199 | } 200 | compute_statistics_source(); 201 | } 202 | 203 | virtual void setInputSurface(const pcl::PointCloud::Ptr& cloud_source) { 204 | 205 | } 206 | 207 | Eigen::Matrix4f getFinalTransformation() const { 208 | return m_rstTf.matrix().cast(); 209 | } 210 | 211 | //// need to be implemented by derived class. 212 | virtual void align(pcl::PointCloud& cloud_aligned, 213 | Eigen::Matrix4f Tf0 = Eigen::Matrix4f::Identity()) { 214 | printf_s("hi, its align() in CICPBase!\n"); 215 | exit(-1); 216 | } 217 | //// by default, the outlier and inlier is discerned by distance. 218 | virtual void outlier_awareness(const pcl::PointCloud::Ptr& cloud_source, 219 | pcl::PointCloud::Ptr& cloud_ins, 220 | pcl::PointCloud::Ptr& cloud_ous) { 221 | cloud_ins.reset(new pcl::PointCloud); 222 | cloud_ous.reset(new pcl::PointCloud); 223 | pcl::PointCloud::Ptr cloud_aft(new pcl::PointCloud()); 224 | pcl::transformPointCloud(*cloud_source, *cloud_aft, m_rstTf); 225 | // std::cout << "No outlier_awareness is available!\n" <points.size(); i++) { 227 | pcl::PointNormal querryPt = cloud_aft->points[i]; 228 | vector vDist2; 229 | m_kd_tree->nearestKSearch(querryPt, 1, vector(), vDist2); 230 | pcl::PointXYZ pt; 231 | pt.x = querryPt.x; 232 | pt.y = querryPt.y; 233 | pt.z = querryPt.z; 234 | if ( vDist2[0] >= pow(0.2, 2) ) { 235 | cloud_ous->points.push_back(pt); 236 | } else { 237 | cloud_ins->points.push_back(pt); 238 | } 239 | } 240 | cloud_ins->is_dense = true; 241 | cloud_ins->width = 1; 242 | cloud_ins->height = cloud_ins->points.size(); 243 | cloud_ous->is_dense = true; 244 | cloud_ous->width = 1; 245 | cloud_ous->height = cloud_ous->points.size(); 246 | } 247 | 248 | std::vector computeRatioFun(const pcl::PointCloud::Ptr& source_cloud, 249 | const Eigen::Matrix4f& rstTf, 250 | double max_dist ) { 251 | std::vector vRatio(2, 0.0); 252 | if (source_cloud->points.empty()) { 253 | return vRatio; 254 | } 255 | pcl::PointCloud::Ptr cloud_aligned(new pcl::PointCloud); 256 | pcl::transformPointCloud(*source_cloud, *cloud_aligned, rstTf); 257 | double max_dist2 = pow(max_dist, 2); 258 | std::vector vPT(cloud_aligned->points.size(), 0), vPL(cloud_aligned->points.size(), 0); 259 | #pragma omp parallel for 260 | for (int i = 0; i < cloud_aligned->points.size(); i++) { 261 | pcl::PointNormal aftPt = cloud_aligned->points[i]; 262 | std::vector vIdx; 263 | m_kd_tree->nearestKSearch(aftPt, 1, vIdx, std::vector()); 264 | int nIdx = vIdx[0]; 265 | pcl::PointNormal refPt = m_cloud_target->points[nIdx]; 266 | float dx = aftPt.x - refPt.x; 267 | float dy = aftPt.y - refPt.y; 268 | float dz = aftPt.z - refPt.z; 269 | float nx = refPt.normal_x, ny = refPt.normal_y, nz = refPt.normal_z; 270 | float fDist_pl = abs(dx*nx + dy*ny + dz*nz); 271 | if (fDist_pl <= max_dist) { 272 | vPL[i] = 1; 273 | } 274 | float fDist_p = dx*dx + dy*dy + dz*dz; 275 | if (fDist_p <= max_dist2) { 276 | vPT[i] = 1; 277 | } 278 | } 279 | int nCnt_PL = std::accumulate(vPL.begin(), vPL.end(), 0); 280 | int nCnt_PT = std::accumulate(vPT.begin(), vPT.end(), 0); 281 | float ratio0 = float(nCnt_PL) / cloud_aligned->points.size(); 282 | float ratio1 = float(nCnt_PT) / cloud_aligned->points.size(); 283 | vRatio[0] = ratio0; 284 | vRatio[1] = ratio1; 285 | return vRatio; 286 | } 287 | 288 | }; 289 | 290 | #endif // !H_ICP_BASE_H 291 | -------------------------------------------------------------------------------- /reg/TRO_Utilities.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef H_TRO_UTILITIES_H 3 | #define H_TRO_UTILITIES_H 4 | #include "stdafx.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | // 15 | inline void stringSplit(std::string s, 16 | std::string delimiter, std::vector& vToken) { 17 | vToken.clear(); 18 | size_t pos = 0; 19 | std::string token; 20 | while ((pos = s.find(delimiter)) != std::string::npos) { 21 | token = s.substr(0, pos); 22 | // std::cout << token << std::endl; 23 | s.erase(0, pos + delimiter.length()); 24 | vToken.push_back(token); 25 | } 26 | if (false == s.empty()) { 27 | vToken.push_back(s); 28 | } 29 | } 30 | 31 | template 32 | inline void randSampleFun(const boost::shared_ptr>& cloud_in, 33 | boost::shared_ptr>& cloud_out, 34 | int nCnt) { 35 | cloud_out.reset(new pcl::PointCloud()); 36 | if ( cloud_in->points.empty() ) 37 | { 38 | return; 39 | } 40 | pcl::copyPointCloud(*cloud_in, *cloud_out); 41 | float fRatio = nCnt / float(cloud_in->points.size()); 42 | if (fRatio >= 1.0) { 43 | return; 44 | } 45 | //// the following is samplling without replacement. 46 | std::random_device rd; 47 | std::mt19937 g(rd()); 48 | std::shuffle(cloud_out->points.begin(), cloud_out->points.end(), g); 49 | cloud_out->points.erase(cloud_out->points.begin() + nCnt, cloud_out->points.end()); 50 | cloud_out->width = 1; 51 | cloud_out->height = cloud_out->points.size(); 52 | cloud_out->is_dense = true; 53 | } 54 | 55 | template 56 | inline void randSampleInPlaceFun(boost::shared_ptr>& cloud_in, 57 | int nCnt) { 58 | boost::shared_ptr> cloud_out(new pcl::PointCloud()); 59 | randSampleFun(cloud_in, cloud_out, nCnt); 60 | cloud_in.swap(cloud_out); 61 | } 62 | 63 | //// the dGridSize is the length of box filter. 64 | template 65 | inline void voxelSampleFun( const boost::shared_ptr>& cloud_in, 66 | boost::shared_ptr>& cloud_out, 67 | double dGridSize) { 68 | if (dGridSize < 0) { 69 | pcl::copyPointCloud(*cloud_in, *cloud_out); 70 | return; 71 | } 72 | pcl::ApproximateVoxelGrid approximate_voxel_filter; 73 | dGridSize = dGridSize * 2.0; 74 | approximate_voxel_filter.setLeafSize(dGridSize, dGridSize, dGridSize); 75 | approximate_voxel_filter.setInputCloud(cloud_in); 76 | approximate_voxel_filter.filter(*cloud_out); 77 | cloud_out->sensor_origin_ = cloud_in->sensor_origin_; 78 | cloud_out->sensor_orientation_ = cloud_in->sensor_orientation_; 79 | } 80 | 81 | template 82 | inline void voxelSampleInPlaceFun(boost::shared_ptr>& cloud_in, 83 | double dGridSize) { 84 | boost::shared_ptr> cloud_out(new pcl::PointCloud()); 85 | voxelSampleFun(cloud_in, cloud_out, dGridSize); 86 | cloud_in.swap(cloud_out); 87 | } 88 | 89 | inline void catCloud(const pcl::PointCloud::Ptr& cloud_xyz, 90 | const pcl::PointCloud::Ptr& cloud_nor, 91 | pcl::PointCloud::Ptr& cloud_normals) 92 | { 93 | if (cloud_xyz->points.size() != cloud_nor->points.size()) 94 | { 95 | cout << "Error in catCloud(), the number of xyz and nor is not the same!\n"; 96 | exit(-1); 97 | } 98 | cloud_normals.reset(new pcl::PointCloud); 99 | pcl::copyPointCloud(*cloud_xyz, *cloud_normals); 100 | pcl::copyPointCloud(*cloud_nor, *cloud_normals); 101 | } 102 | 103 | inline void decoupleCloud(const pcl::PointCloud::Ptr& cloud_normals, 104 | pcl::PointCloud::Ptr& cloud_xyz, 105 | pcl::PointCloud::Ptr& cloud_nor) 106 | { 107 | cloud_xyz.reset(new pcl::PointCloud); 108 | cloud_nor.reset(new pcl::PointCloud); 109 | pcl::copyPointCloud(*cloud_normals, *cloud_xyz); 110 | pcl::copyPointCloud(*cloud_normals, *cloud_nor); 111 | } 112 | 113 | inline void decoupleCloud(const pcl::PointCloud::Ptr& cloud_normals, 114 | pcl::PointCloud::Ptr& cloud_xyz) 115 | { 116 | cloud_xyz.reset(new pcl::PointCloud); 117 | pcl::PointCloud::Ptr cloud_nor(new pcl::PointCloud()); 118 | pcl::copyPointCloud(*cloud_normals, *cloud_xyz); 119 | pcl::copyPointCloud(*cloud_normals, *cloud_nor); 120 | } 121 | 122 | template 123 | inline void read_cloud(std::string& sFileName, 124 | boost::shared_ptr< pcl::PointCloud> & cloud_normal) { 125 | cloud_normal.reset(new pcl::PointCloud() ); 126 | if (0 != access(sFileName.c_str(), 0) ) { 127 | printf_s("%s does not exist!\n", sFileName.c_str()); 128 | exit(-1); 129 | } 130 | std::string sSuffix = sFileName.substr(sFileName.length() - 3, 3); 131 | if (0 == sSuffix.compare(std::string("ply"))) { 132 | pcl::PLYReader plyReader; 133 | plyReader.read(sFileName.c_str(), *cloud_normal); 134 | } 135 | if (0 == sSuffix.compare(std::string("pcd"))) { 136 | pcl::PCDReader pcdReader; 137 | pcdReader.read(sFileName.c_str(), *cloud_normal); 138 | } 139 | } 140 | 141 | //// estimate normal vectors of a point cloud. 142 | class CNormalEstimator { 143 | public: 144 | //// 145 | CNormalEstimator() { 146 | m_sNeighbor = "knn"; 147 | m_dNormRad = 0.5; 148 | clear(); 149 | } 150 | //// 151 | ~CNormalEstimator() { 152 | } 153 | 154 | void setRadius(double dNormRad) { 155 | m_sNeighbor = "radius"; 156 | m_dNormRad = dNormRad; 157 | } 158 | //// 159 | template 160 | void load(const boost::shared_ptr>& cloud_in) { 161 | clear(); 162 | //// load input point cloud. 163 | for (auto i = 0; i < cloud_in->points.size(); i++) { 164 | T pt_src = cloud_in->points[i]; 165 | pcl::PointXYZ pt_dst; 166 | pt_dst.x = pt_src.x; 167 | pt_dst.y = pt_src.y; 168 | pt_dst.z = pt_src.z; 169 | m_cloud_xyz->points.push_back(pt_dst); 170 | } 171 | m_cloud_xyz->is_dense = true; 172 | m_cloud_xyz->width = 1; 173 | m_cloud_xyz->height = m_cloud_xyz->points.size(); 174 | //// set kd_tree. 175 | m_kd_tree->setInputCloud(m_cloud_xyz); 176 | //// compute normals. 177 | compute_normal(); 178 | } 179 | void get_cloud(pcl::PointCloud::Ptr& cloud_out) const { 180 | pcl::copyPointCloud(*m_cloud_out, *cloud_out); 181 | } 182 | //// this will change cloud_out. 183 | void loadInPlace(pcl::PointCloud::Ptr& cloud_out) { 184 | load(cloud_out); 185 | get_cloud(cloud_out); 186 | } 187 | static bool is_valid_normal(const pcl::PointCloud::Ptr& cloud_in) { 188 | //// randomly check the feasibility. 189 | for (auto i = 0; i < cloud_in->points.size(); i += 100) { 190 | pcl::PointNormal pt = cloud_in->points[i]; 191 | double dist = pow(pt.normal_x, 2) + pow(pt.normal_y, 2) + pow(pt.normal_z, 2); 192 | if (dist <= 0.99) { 193 | return false; 194 | } 195 | } 196 | return true; 197 | } 198 | //// make sure that this class is user-security. 199 | private: 200 | pcl::PointCloud::Ptr m_cloud_xyz; 201 | pcl::PointCloud::Ptr m_cloud_nor; 202 | pcl::PointCloud::Ptr m_cloud_out; 203 | pcl::search::KdTree::Ptr m_kd_tree; 204 | std::string m_sNeighbor; 205 | double m_dNormRad; 206 | 207 | //// 208 | void clear() { 209 | m_cloud_xyz.reset(new pcl::PointCloud()); 210 | m_cloud_nor.reset(new pcl::PointCloud()); 211 | m_cloud_out.reset(new pcl::PointCloud()); 212 | m_kd_tree.reset(new pcl::search::KdTree()); 213 | } 214 | //// 215 | void delete_nan_element() { 216 | pcl::Normal norm_pt; 217 | boost::shared_ptr > valid_indices(new std::vector); 218 | for (auto i = 0; i < m_cloud_nor->points.size(); i++) { 219 | norm_pt = m_cloud_nor->points[i]; 220 | if (!isnan(norm_pt.normal_x)) { 221 | valid_indices->push_back(i); 222 | } 223 | } 224 | if (m_cloud_nor->points.size() != valid_indices->size()) { 225 | printf_s("Delete nan normals: %02d, %05d/%05d\n", 226 | m_cloud_nor->points.size() - valid_indices->size(), 227 | valid_indices->size(), m_cloud_nor->points.size()); 228 | pcl::ExtractIndices::Ptr Extractor_Point(new pcl::ExtractIndices); 229 | Extractor_Point->setInputCloud(m_cloud_xyz); 230 | Extractor_Point->setIndices(valid_indices); 231 | pcl::PointCloud::Ptr cloud_tmp(new pcl::PointCloud()); 232 | Extractor_Point->filter(*cloud_tmp); 233 | m_cloud_xyz.swap(cloud_tmp); 234 | //// 235 | pcl::ExtractIndices::Ptr extractor_normal(new pcl::ExtractIndices); 236 | extractor_normal->setInputCloud(m_cloud_nor); 237 | extractor_normal->setIndices(valid_indices); 238 | pcl::PointCloud::Ptr normals_tmp(new pcl::PointCloud); 239 | extractor_normal->filter(*normals_tmp); 240 | m_cloud_nor.swap(normals_tmp); 241 | } 242 | } 243 | //// 244 | void compute_normal() { 245 | //// normal estimator. 246 | pcl::NormalEstimationOMP ne_filter; 247 | ne_filter.setInputCloud(m_cloud_xyz); 248 | ne_filter.setSearchMethod(m_kd_tree); 249 | ne_filter.setNumberOfThreads(8); 250 | if (0 == m_sNeighbor.compare("knn")) { 251 | ne_filter.setKSearch(20); 252 | } 253 | if (0 == m_sNeighbor.compare("radius")) { 254 | ne_filter.setRadiusSearch(m_dNormRad); 255 | } 256 | ne_filter.compute(*m_cloud_nor); 257 | //// delete invalid element. 258 | delete_nan_element(); 259 | //// output the cloud. 260 | catCloud(m_cloud_xyz, m_cloud_nor, m_cloud_out); 261 | } 262 | }; 263 | 264 | #endif 265 | -------------------------------------------------------------------------------- /reg/stdafx.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DrGabor/WinTeaser/2b216e86b7286de678722a2d1ca75b2fbd194deb/reg/stdafx.h -------------------------------------------------------------------------------- /teaser/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(teaser_source) 2 | include(GNUInstallDirs) 3 | 4 | # teaser_io library 5 | add_library(teaser_io SHARED src/ply_io.cc) 6 | target_link_libraries(teaser_io PRIVATE tinyply) 7 | target_include_directories(teaser_io PUBLIC 8 | $ 9 | $ 10 | $) 11 | 12 | install(TARGETS teaser_io 13 | EXPORT teaserpp-export 14 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 15 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 16 | ) 17 | list(APPEND TEASERPP_EXPORTED_TARGETS teaser_io tinyply) 18 | add_library(teaserpp::teaser_io ALIAS teaser_io) 19 | 20 | # teaser_registration library 21 | add_library(teaser_registration SHARED 22 | src/registration.cc 23 | src/graph.cc 24 | ) 25 | target_link_libraries(teaser_registration 26 | PUBLIC Eigen3::Eigen 27 | PRIVATE pmc 28 | ) 29 | target_include_directories(teaser_registration PUBLIC 30 | $ 31 | $ 32 | $) 33 | 34 | find_package(OpenMP REQUIRED) 35 | target_link_libraries(teaser_registration PUBLIC OpenMP::OpenMP_CXX) 36 | 37 | install(TARGETS teaser_registration 38 | EXPORT teaserpp-export 39 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 40 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 41 | ) 42 | list(APPEND TEASERPP_EXPORTED_TARGETS teaser_registration pmc) 43 | add_library(teaserpp::teaser_registration ALIAS teaser_registration) 44 | 45 | # teaser_features library 46 | if (BUILD_TEASER_FPFH) 47 | add_library(teaser_features SHARED 48 | src/fpfh.cc 49 | src/matcher.cc 50 | ) 51 | target_link_libraries(teaser_features 52 | PRIVATE ${PCL_LIBRARIES} 53 | PRIVATE Eigen3::Eigen 54 | ) 55 | if (BUILD_WITH_MKL AND MKL_FOUND) 56 | target_link_libraries(teaser_features 57 | PRIVATE ${MKL_LIBRARIES} 58 | ) 59 | endif () 60 | target_include_directories(teaser_features PUBLIC 61 | $ 62 | $ 63 | $) 64 | 65 | install(TARGETS teaser_features 66 | EXPORT teaserpp-export 67 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 68 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 69 | ) 70 | 71 | list(APPEND TEASERPP_EXPORTED_TARGETS teaser_features) 72 | add_library(teaserpp::teaser_features ALIAS teaser_features) 73 | endif () 74 | 75 | # march=native flag 76 | if (BUILD_WITH_MARCH_NATIVE) 77 | message(STATUS "-march=native flag enabled.") 78 | target_compile_options(teaser_registration PUBLIC -march=native) 79 | endif () 80 | 81 | # set exported targets in parent scope 82 | set(TEASERPP_EXPORTED_TARGETS "${TEASERPP_EXPORTED_TARGETS}" PARENT_SCOPE) 83 | 84 | # installation 85 | install(EXPORT teaserpp-export 86 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/teaserpp 87 | NAMESPACE teaserpp:: 88 | FILE teaserppTargets.cmake 89 | ) 90 | 91 | install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 92 | -------------------------------------------------------------------------------- /teaser/include/teaser/CTeaser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef H_TEASER_H 4 | #define H_TEASER_H 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | class CTEASER : public CRegBase 11 | { 12 | public: 13 | EIGEN_MAKE_ALIGNED_OPERATOR_NEW 14 | 15 | double m_dSampleRadius; 16 | double m_dFeatureRadius; 17 | virtual void compute_statistics_target() { 18 | 19 | } 20 | 21 | virtual void compute_statistics_source() { 22 | 23 | } 24 | 25 | virtual void setParams(string& sMethod) { 26 | m_dSampleRadius = 1.0; 27 | m_dFeatureRadius = 1.0; 28 | } 29 | 30 | virtual int getTCIterations() { 31 | return 0; 32 | } 33 | void setRadius(double dSampleRadius, double FeatureRadius) { 34 | m_dSampleRadius = dSampleRadius; 35 | m_dFeatureRadius = FeatureRadius; 36 | } 37 | void setInputTarget(const pcl::PointCloud::Ptr& cloud_target, 38 | const boost::shared_ptr> kd_tree_ptr = nullptr) { 39 | pcl::copyPointCloud(*cloud_target, *m_cloud_target); 40 | //// m_kd_tree is used to calculate ratio score. 41 | if (kd_tree_ptr == nullptr) { 42 | m_kd_tree.reset(new pcl::KdTreeFLANN()); 43 | m_kd_tree->setInputCloud(m_cloud_target); 44 | } else { 45 | m_kd_tree = kd_tree_ptr; 46 | } 47 | } 48 | 49 | void setInputSource(const pcl::PointCloud::Ptr& cloud_source) { 50 | pcl::copyPointCloud(*cloud_source, *m_cloud_source); 51 | compute_statistics_source(); 52 | } 53 | 54 | virtual void setInputSurface(const pcl::PointCloud::Ptr& cloud_source) { 55 | 56 | } 57 | 58 | Eigen::Matrix4f getFinalTransformation() const { 59 | return m_rstTf.matrix().cast(); 60 | } 61 | 62 | //// need to be implemented by derived class. 63 | virtual void align(pcl::PointCloud& cloud_aligned, 64 | Eigen::Matrix4f Tf0 = Eigen::Matrix4f::Identity()) { 65 | clock_t tBeg, tEnd; 66 | tBeg = clock(); 67 | gabor::CFPFH fpfhEstimator; 68 | fpfhEstimator.setRadius(m_dSampleRadius, m_dFeatureRadius); 69 | teaser::PointCloud src_cloud, tgt_cloud; 70 | teaser::FPFHCloudPtr src_descriptors(new teaser::FPFHCloud()); 71 | fpfhEstimator.compute(m_cloud_source, src_cloud, src_descriptors); 72 | teaser::FPFHCloudPtr tgt_descriptors(new teaser::FPFHCloud()); 73 | fpfhEstimator.compute(m_cloud_target, tgt_cloud, tgt_descriptors); 74 | tEnd = clock(); 75 | int nTime_fpfh = tEnd - tBeg; 76 | //// compute correspondence. 77 | tBeg = clock(); 78 | teaser::Matcher matcher; 79 | bool use_absolute_scale = true, 80 | use_crosscheck = true, 81 | use_tuple_test = true; // false; 82 | float tuple_scale = 0.85; 83 | 84 | auto correspondences = matcher.calculateCorrespondences( 85 | src_cloud, tgt_cloud, 86 | *src_descriptors, *tgt_descriptors, 87 | use_absolute_scale, use_crosscheck, use_tuple_test, tuple_scale); 88 | tEnd = clock(); 89 | int nTime_corres = tEnd - tBeg; 90 | // Run TEASER++ registration 91 | // Prepare solver parameters 92 | tBeg = clock(); 93 | teaser::RobustRegistrationSolver::Params params; 94 | //// noise_bound is very important. 95 | //// Using Gaussian noise for 3D point cloud, noise_bound = sqrt(3) * sigma. 96 | params.noise_bound = m_dSampleRadius / 2; 97 | params.cbar2 = 1.0; // 98 | params.estimate_scaling = false; 99 | params.rotation_max_iterations = 100; // 100 100 | params.rotation_gnc_factor = 1.4; 101 | params.rotation_estimation_algorithm = 102 | teaser::RobustRegistrationSolver::ROTATION_ESTIMATION_ALGORITHM::GNC_TLS; 103 | params.rotation_cost_threshold = 0.005; 104 | 105 | // Solve with TEASER++ 106 | teaser::RobustRegistrationSolver solver(params); 107 | solver.solve(src_cloud, tgt_cloud, correspondences); 108 | tEnd = clock(); 109 | int nTime_reg = tEnd - tBeg; 110 | 111 | auto solution = solver.getSolution(); 112 | 113 | //// save registration result. 114 | m_rstTf = Eigen::Matrix4d::Identity(); 115 | m_rstTf.block(0, 0, 3, 3) = solution.rotation.cast(); 116 | m_rstTf.block(0, 3, 3, 1) = solution.translation.cast(); 117 | pcl::transformPointCloud(*m_cloud_source, cloud_aligned, m_rstTf); 118 | printf_s("time_fpfh = %04dms, time_corres = %04dms, time_reg = %04dms, corresNum = %04d\n", 119 | nTime_fpfh, nTime_corres, nTime_reg, correspondences.size() ); 120 | bool bTest = true; 121 | } 122 | 123 | private: 124 | 125 | 126 | }; 127 | 128 | #endif // !H_TEASER_H 129 | 130 | -------------------------------------------------------------------------------- /teaser/include/teaser/no_use/ply_io.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020, Massachusetts Institute of Technology, 3 | * Cambridge, MA 02139 4 | * All Rights Reserved 5 | * Authors: Jingnan Shi, et al. (see THANKS for the full author list) 6 | * See LICENSE for the license information 7 | */ 8 | 9 | #pragma once 10 | 11 | #include "teaser/geometry.h" 12 | 13 | namespace teaser { 14 | 15 | /** 16 | * @brief A class for reading PLY files. 17 | * 18 | */ 19 | class PLYReader { 20 | public: 21 | /** 22 | * @brief Default constructor 23 | */ 24 | PLYReader() {} 25 | 26 | /** 27 | * @brief A wrapper function for reading ply files into PointCloud 28 | */ 29 | int read(const std::string& file_name, PointCloud& cloud); 30 | }; 31 | 32 | /** 33 | * @brief A class for writing PLY files. 34 | */ 35 | class PLYWriter { 36 | public: 37 | /** 38 | * @brief Default constructor 39 | */ 40 | PLYWriter() {} 41 | 42 | /** 43 | * @brief Write point cloud to ply files 44 | * @param file_name 45 | * @param cloud 46 | * @param binary_mode Set to true to write in binary mode 47 | * @return A status code 48 | */ 49 | int write(const std::string& file_name, const PointCloud& cloud, bool binary_mode = false); 50 | }; 51 | 52 | } // namespace teaser 53 | -------------------------------------------------------------------------------- /teaser/include/teaser/teaser_fpfh.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020, Massachusetts Institute of Technology, 3 | * Cambridge, MA 02139 4 | * All Rights Reserved 5 | * Authors: Jingnan Shi, et al. (see THANKS for the full author list) 6 | * See LICENSE for the license information 7 | */ 8 | 9 | #pragma once 10 | #ifndef H_TEASER_FPFH_H 11 | #define H_TEASER_FPFH_H 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | using namespace std; 18 | 19 | namespace gabor { 20 | using FEATURE_TYPE = pcl::FPFHSignature33; 21 | 22 | class CFPFH { 23 | public: 24 | pcl::search::KdTree::Ptr m_searchMethod; 25 | // pcl::PointCloud::Ptr m_cloud; 26 | // pcl::PointCloud::Ptr m_cloud_normal; 27 | pcl::PointCloud::Ptr m_cloud_raw; 28 | pcl::PointCloud::Ptr m_cloud_feature; 29 | boost::shared_ptr > m_vkeypoint_indices; 30 | pcl::PointCloud::Ptr m_cloud_keypoints; 31 | double m_dSampleRadius; 32 | double m_dFeatureRadius; 33 | 34 | CFPFH() { 35 | m_dFeatureRadius = 3.0; 36 | m_dSampleRadius = m_dFeatureRadius / 2; 37 | m_cloud_raw.reset(new pcl::PointCloud()); 38 | } 39 | ~CFPFH() {} 40 | 41 | void setRadius(double dSampleRadius, double dFeatureRadius) { 42 | m_dSampleRadius = dSampleRadius; 43 | m_dFeatureRadius = dFeatureRadius; 44 | } 45 | //// 46 | void setInputCloud(pcl::PointCloud::Ptr input_cloud) { 47 | pcl::copyPointCloud(*input_cloud, *m_cloud_raw); 48 | pcl::PointCloud::Ptr cloud_xyz(new pcl::PointCloud()); 49 | decoupleCloud(m_cloud_raw, cloud_xyz); 50 | pcl::search::KdTree::Ptr search_method( 51 | new pcl::search::KdTree); 52 | search_method->setInputCloud(cloud_xyz); 53 | m_searchMethod.swap(search_method); 54 | } 55 | //// 56 | void computeKeypoints() { 57 | m_vkeypoint_indices.reset(new std::vector()); 58 | m_cloud_keypoints.reset(new pcl::PointCloud()); 59 | //// key_points calculation. 60 | pcl::PointCloud::Ptr cloud_xyz(new pcl::PointCloud()); 61 | decoupleCloud(m_cloud_raw, cloud_xyz); 62 | voxelSampleFun(cloud_xyz, m_cloud_keypoints, m_dSampleRadius); 63 | char tmpChar[2000] = " "; 64 | sprintf(tmpChar, "%s", "uniform sampling"); 65 | 66 | //// key_points index calculation. 67 | vector pointIdxNKNSearch; 68 | vector pointNKNSquaredDistance; 69 | pcl::PointXYZ querryPt, searchPoint; 70 | int nLen = m_cloud_keypoints->points.size(); 71 | for (int i = 0; i < nLen; i++) { 72 | pcl::PointXYZ pt = m_cloud_keypoints->points[i]; 73 | m_searchMethod->nearestKSearch(pt, 1, pointIdxNKNSearch, 74 | pointNKNSquaredDistance); 75 | m_vkeypoint_indices->push_back(pointIdxNKNSearch[0]); 76 | } 77 | } 78 | void computeFPFH() { 79 | pcl::PointCloud::Ptr features( 80 | new pcl::PointCloud); 81 | features.reset(new pcl::PointCloud()); 82 | //// calculate descriptor. 83 | pcl::FPFHEstimationOMP::Ptr 85 | fpfh_estimation(new pcl::FPFHEstimationOMP); 87 | // fpfh_estimation->setSearchSurface(cloud); 88 | pcl::PointCloud::Ptr cloud(new pcl::PointCloud()); 89 | pcl::PointCloud::Ptr normals(new pcl::PointCloud()); 90 | decoupleCloud(m_cloud_raw, cloud, normals); 91 | fpfh_estimation->setInputCloud(cloud); 92 | fpfh_estimation->setInputNormals(normals); 93 | fpfh_estimation->setSearchMethod(m_searchMethod); 94 | fpfh_estimation->setRadiusSearch(m_dFeatureRadius); 95 | fpfh_estimation->setNumberOfThreads(8); 96 | 97 | fpfh_estimation->setIndices(m_vkeypoint_indices); 98 | fpfh_estimation->compute(*features); 99 | //// 100 | m_cloud_feature.swap(features); 101 | } 102 | 103 | void detectZeroFeatures( 104 | const pcl::PointCloud::Ptr& cloud_features, 105 | pcl::IndicesPtr& indices) { 106 | indices->clear(); 107 | FEATURE_TYPE tmpFeature; 108 | double dDist2 = 0.0; 109 | for (size_t id = 0; id < cloud_features->points.size(); id++) { 110 | tmpFeature = cloud_features->points[id]; 111 | dDist2 = 0.0; 112 | int nLen = tmpFeature.descriptorSize(); 113 | for (int i = 0; i < nLen; i++) { 114 | // dDist2 += pow(tmpFeature.descriptor[i], 2); 115 | dDist2 += pow(tmpFeature.histogram[i], 2); 116 | } 117 | if (dDist2 >= 1e-4) { 118 | indices->push_back(id); 119 | } 120 | } 121 | } 122 | void featureFilter( 123 | const pcl::PointCloud::Ptr& cloud_descriptors, 124 | const pcl::IndicesPtr& indices, 125 | pcl::PointCloud::Ptr& cloud_descriptors_new) { 126 | FEATURE_TYPE descriptor; 127 | for (size_t i = 0; i < indices->size(); i++) { 128 | int nId = (*indices).at(i); 129 | descriptor = cloud_descriptors->points[nId]; 130 | cloud_descriptors_new->push_back(descriptor); 131 | } 132 | cloud_descriptors_new->width = cloud_descriptors_new->points.size(); 133 | cloud_descriptors_new->height = 1; 134 | } 135 | 136 | void deleteInvalidFeature() { 137 | pcl::IndicesPtr indices(new vector()); 138 | detectZeroFeatures(m_cloud_feature, indices); 139 | if (m_cloud_feature->size() != indices->size()) { 140 | if (indices->size() == 0) { 141 | printf("Error: no valid descriptors!\n"); 142 | exit(-1); 143 | } 144 | pcl::ExtractIndices::Ptr extractor_pointCloud( 145 | new pcl::ExtractIndices); 146 | extractor_pointCloud->setInputCloud(m_cloud_keypoints); 147 | extractor_pointCloud->setIndices(indices); 148 | pcl::PointCloud::Ptr cloud_tmp( 149 | new pcl::PointCloud); 150 | extractor_pointCloud->filter(*cloud_tmp); 151 | m_cloud_keypoints.swap(cloud_tmp); 152 | 153 | pcl::PointCloud::Ptr cloud_feature_tmp( 154 | new pcl::PointCloud); 155 | featureFilter(m_cloud_feature, indices, cloud_feature_tmp); 156 | m_cloud_feature.swap(cloud_feature_tmp); 157 | } 158 | PCL_INFO("compute descriptors done, keypoints = %05d/%05d\n", 159 | m_cloud_feature->points.size(), m_cloud_raw->points.size()); 160 | } 161 | 162 | template 163 | inline void cvt2Teaser_cloud(const boost::shared_ptr>& cloud_pcl, 164 | teaser::PointCloud& cloud_teaser) { 165 | cloud_teaser.clear(); 166 | for (int i = 0; i < cloud_pcl->points.size(); i++) { 167 | T pt = cloud_pcl->points[i]; 168 | cloud_teaser.push_back({ pt.x, pt.y, pt.z }); 169 | } 170 | } 171 | 172 | void compute( 173 | const pcl::PointCloud::Ptr& input_cloud, 174 | teaser::PointCloud& teaser_cloud, 175 | teaser::FPFHCloudPtr& fpfh_cloud) { 176 | setInputCloud(input_cloud); 177 | computeKeypoints(); 178 | computeFPFH(); 179 | deleteInvalidFeature(); 180 | //// convert to teaser format. 181 | teaser_cloud.clear(); 182 | cvt2Teaser_cloud(m_cloud_keypoints, teaser_cloud); 183 | fpfh_cloud.reset(new teaser::FPFHCloud()); 184 | pcl::copyPointCloud(*m_cloud_feature, *fpfh_cloud); 185 | } 186 | }; 187 | } 188 | 189 | #endif 190 | -------------------------------------------------------------------------------- /teaser/include/teaser/teaser_geometry.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020, Massachusetts Institute of Technology, 3 | * Cambridge, MA 02139 4 | * All Rights Reserved 5 | * Authors: Jingnan Shi, et al. (see THANKS for the full author list) 6 | * See LICENSE for the license information 7 | */ 8 | 9 | #pragma once 10 | #ifndef H_TEASER_GEOMETRY_H 11 | #define H_TEASER_GEOMETRY_H 12 | 13 | #include 14 | 15 | namespace teaser { 16 | 17 | struct PointXYZ { 18 | float x; 19 | float y; 20 | float z; 21 | 22 | friend inline bool operator==(const PointXYZ& lhs, const PointXYZ& rhs) { 23 | return (lhs.x == rhs.x) && (lhs.y == rhs.y) && (lhs.z == rhs.z); 24 | } 25 | friend inline bool operator!=(const PointXYZ& lhs, const PointXYZ& rhs) { return !(lhs == rhs); } 26 | }; 27 | 28 | class PointCloud { 29 | public: 30 | /** 31 | * @brief Default constructor for PointCloud 32 | */ 33 | PointCloud() = default; 34 | 35 | // c++ container named requirements 36 | using value_type = PointXYZ; 37 | using reference = PointXYZ&; 38 | using const_reference = const PointXYZ&; 39 | using difference_type = std::vector::difference_type; 40 | using size_type = std::vector::size_type; 41 | 42 | // iterators 43 | using iterator = std::vector::iterator; 44 | using const_iterator = std::vector::const_iterator; 45 | inline iterator begin() { return points_.begin(); } 46 | inline iterator end() { return points_.end(); } 47 | inline const_iterator begin() const { return points_.begin(); } 48 | inline const_iterator end() const { return points_.end(); } 49 | 50 | // capacity 51 | inline size_t size() const { return points_.size(); } 52 | inline void reserve(size_t n) { points_.reserve(n); } 53 | inline bool empty() { return points_.empty(); } 54 | 55 | // element access 56 | inline PointXYZ& operator[](size_t i) { return points_[i]; } 57 | inline const PointXYZ& operator[](size_t i) const { return points_[i]; } 58 | inline PointXYZ& at(size_t n) { return points_.at(n); } 59 | inline const PointXYZ& at(size_t n) const { return points_.at(n); } 60 | inline PointXYZ& front() { return points_.front(); } 61 | inline const PointXYZ& front() const { return points_.front(); } 62 | inline PointXYZ& back() { return points_.back(); } 63 | inline const PointXYZ& back() const { return points_.back(); } 64 | 65 | inline void push_back(const PointXYZ& pt) { points_.push_back(pt); } 66 | inline void push_back(PointXYZ& pt) { points_.push_back(pt); } 67 | 68 | inline void clear() { points_.clear(); } 69 | 70 | private: 71 | std::vector points_; 72 | }; 73 | 74 | } // namespace teaser 75 | #endif 76 | -------------------------------------------------------------------------------- /teaser/include/teaser/teaser_graph.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020, Massachusetts Institute of Technology, 3 | * Cambridge, MA 02139 4 | * All Rights Reserved 5 | * Authors: Jingnan Shi, et al. (see THANKS for the full author list) 6 | * See LICENSE for the license information 7 | */ 8 | 9 | #pragma once 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | namespace teaser { 22 | 23 | /** 24 | * A simple undirected graph class 25 | * 26 | * This graph assumes that vertices are numbered. In addition, the vertices numbers have to be 27 | * consecutive starting from 0. 28 | * 29 | * For example, if the graph have 3 vertices, they have to be named 0, 1, and 2. 30 | */ 31 | class Graph { 32 | public: 33 | Graph() : num_edges_(0){}; 34 | 35 | /** 36 | * Constructor that takes in an adjacency list. Notice that for an edge connecting two arbitrary 37 | * vertices v1 & v2, we assume that v2 exists in v1's list, and v1 also exists in v2's list. This 38 | * condition is not enforced. If violated, removeEdge() function might exhibit undefined 39 | * behaviors. 40 | * @param [in] adj_list an map representing an adjacency list 41 | */ 42 | explicit Graph(const std::map>& adj_list) { 43 | adj_list_.resize(adj_list.size()); 44 | num_edges_ = 0; 45 | for (const auto& e_list : adj_list) { 46 | const auto& v = e_list.first; 47 | adj_list_[e_list.first] = e_list.second; 48 | num_edges_ += e_list.second.size(); 49 | } 50 | num_edges_ /= 2; 51 | }; 52 | 53 | /** 54 | * Add a vertex with no edges. 55 | * @param [in] id the id of vertex to be added 56 | */ 57 | void addVertex(const int& id) { 58 | if (id < adj_list_.size()) { 59 | TEASER_DEBUG_ERROR_MSG("Vertex already exists."); 60 | } else { 61 | adj_list_.resize(id + 1); 62 | } 63 | } 64 | 65 | /** 66 | * Populate the graph with the provided number of vertices without any edges. 67 | * @param num_vertices 68 | */ 69 | void populateVertices(const int& num_vertices) { adj_list_.resize(num_vertices); } 70 | 71 | /** 72 | * Return true if said edge exists 73 | * @param [in] vertex_1 74 | * @param [in] vertex_2 75 | */ 76 | bool hasEdge(const int& vertex_1, const int& vertex_2) { 77 | if (vertex_1 >= adj_list_.size() || vertex_2 >= adj_list_.size()) { 78 | return false; 79 | } 80 | auto& connected_vs = adj_list_[vertex_1]; 81 | bool exists = 82 | std::find(connected_vs.begin(), connected_vs.end(), vertex_2) != connected_vs.end(); 83 | return exists; 84 | } 85 | 86 | /** 87 | * Return true if the vertex exists. 88 | * @param vertex 89 | * @return 90 | */ 91 | bool hasVertex(const int& vertex) { return vertex < adj_list_.size(); } 92 | 93 | /** 94 | * Add an edge between two vertices 95 | * @param [in] vertex_1 one vertex of the edge 96 | * @param [in] vertex_2 another vertex of the edge 97 | */ 98 | void addEdge(const int& vertex_1, const int& vertex_2) { 99 | if (hasEdge(vertex_1, vertex_2)) { 100 | TEASER_DEBUG_ERROR_MSG("Edge exists."); 101 | return; 102 | } 103 | adj_list_[vertex_1].push_back(vertex_2); 104 | adj_list_[vertex_2].push_back(vertex_1); 105 | num_edges_++; 106 | } 107 | 108 | /** 109 | * Remove the edge between two vertices. 110 | * @param [in] vertex_1 one vertex of the edge 111 | * @param [in] vertex_2 another vertex of the edge 112 | */ 113 | void removeEdge(const int& vertex_1, const int& vertex_2) { 114 | if (vertex_1 >= adj_list_.size() || vertex_2 >= adj_list_.size()) { 115 | TEASER_DEBUG_ERROR_MSG("Trying to remove non-existent edge."); 116 | return; 117 | } 118 | 119 | adj_list_[vertex_1].erase( 120 | std::remove(adj_list_[vertex_1].begin(), adj_list_[vertex_1].end(), vertex_2), 121 | adj_list_[vertex_1].end()); 122 | adj_list_[vertex_2].erase( 123 | std::remove(adj_list_[vertex_2].begin(), adj_list_[vertex_2].end(), vertex_1), 124 | adj_list_[vertex_2].end()); 125 | 126 | num_edges_--; 127 | } 128 | 129 | /** 130 | * Get the number of vertices 131 | * @return total number of vertices 132 | */ 133 | [[nodiscard]] int numVertices() const { return adj_list_.size(); } 134 | 135 | /** 136 | * Get the number of edges 137 | * @return total number of edges 138 | */ 139 | [[nodiscard]] int numEdges() const { return num_edges_; } 140 | 141 | /** 142 | * Get edges originated from a specific vertex 143 | * @param [in] id 144 | * @return an unordered set of edges 145 | */ 146 | [[nodiscard]] const std::vector& getEdges(int id) const { return adj_list_[id]; } 147 | 148 | /** 149 | * Get all vertices 150 | * @return a vector of all vertices 151 | */ 152 | [[nodiscard]] std::vector getVertices() const { 153 | std::vector v; 154 | for (int i = 0; i < adj_list_.size(); ++i) { 155 | v.push_back(i); 156 | } 157 | return v; 158 | } 159 | 160 | [[nodiscard]] Eigen::MatrixXi getAdjMatrix() const { 161 | const int num_v = numVertices(); 162 | Eigen::MatrixXi adj_matrix(num_v, num_v); 163 | for (size_t i = 0; i < num_v; ++i) { 164 | const auto& c_edges = getEdges(i); 165 | for (size_t j = 0; j < num_v; ++j) { 166 | if (std::find(c_edges.begin(), c_edges.end(), j) != c_edges.end()) { 167 | adj_matrix(i, j) = 1; 168 | } else { 169 | adj_matrix(i, j) = 0; 170 | } 171 | } 172 | } 173 | return adj_matrix; 174 | } 175 | 176 | [[nodiscard]] std::vector> getAdjList() const { return adj_list_; } 177 | 178 | /** 179 | * Preallocate spaces for vertices 180 | * @param num_vertices 181 | */ 182 | void reserve(const int& num_vertices) { adj_list_.reserve(num_vertices); } 183 | 184 | /** 185 | * Reserve space for complete graph. A complete undirected graph should have N*(N-1)/2 edges 186 | * @param num_vertices 187 | */ 188 | void reserveForCompleteGraph(const int& num_vertices) { 189 | adj_list_.reserve(num_vertices); 190 | for (int i = 0; i < num_vertices - 1; ++i) { 191 | std::vector c_edges; 192 | c_edges.reserve(num_vertices - 1); 193 | adj_list_.push_back(c_edges); 194 | } 195 | adj_list_.emplace_back(std::initializer_list{}); 196 | } 197 | 198 | private: 199 | std::vector> adj_list_; 200 | size_t num_edges_; 201 | }; 202 | 203 | /** 204 | * A facade to the Parallel Maximum Clique (PMC) library. 205 | * 206 | * For details about PMC, please refer to: 207 | * https://github.com/ryanrossi/pmc 208 | * and 209 | * Ryan A. Rossi, David F. Gleich, Assefaw H. Gebremedhin, Md. Mostofa Patwary, A Fast Parallel 210 | * Maximum Clique Algorithm for Large Sparse Graphs and Temporal Strong Components, arXiv preprint 211 | * 1302.6256, 2013. 212 | */ 213 | class MaxCliqueSolver { 214 | public: 215 | 216 | /** 217 | * Enum representing the solver algorithm to use 218 | */ 219 | enum class CLIQUE_SOLVER_MODE { 220 | PMC_EXACT = 0, 221 | PMC_HEU = 1, 222 | KCORE_HEU = 2, 223 | }; 224 | 225 | /** 226 | * Parameter struct for MaxCliqueSolver 227 | */ 228 | struct Params { 229 | 230 | /** 231 | * Algorithm used for finding max clique. 232 | */ 233 | CLIQUE_SOLVER_MODE solver_mode = CLIQUE_SOLVER_MODE::PMC_EXACT; 234 | 235 | /** 236 | * \deprecated Use solver_mode instead 237 | * Set this to false to enable heuristic-only max clique finding. 238 | */ 239 | bool solve_exactly = true; 240 | 241 | /** 242 | * The threshold ratio for determining whether to skip max clique and go straightly to 243 | * GNC rotation estimation. Set this to 1 to always use exact max clique selection, 0 to always 244 | * skip exact max clique selection. 245 | */ 246 | double kcore_heuristic_threshold = 1; 247 | 248 | /** 249 | * Time limit on running the solver. 250 | */ 251 | double time_limit = 3600; 252 | }; 253 | 254 | MaxCliqueSolver() = default; 255 | 256 | MaxCliqueSolver(Params params) : params_(params){}; 257 | 258 | /** 259 | * Find the maximum clique within the graph provided. By maximum clique, it means the clique of 260 | * the largest size in an undirected graph. 261 | * @param graph 262 | * @return a vector of indices of cliques 263 | */ 264 | std::vector findMaxClique(Graph graph); 265 | 266 | private: 267 | Graph graph_; 268 | Params params_; 269 | }; 270 | 271 | } // namespace teaser 272 | -------------------------------------------------------------------------------- /teaser/include/teaser/teaser_macros.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020, Massachusetts Institute of Technology, 3 | * Cambridge, MA 02139 4 | * All Rights Reserved 5 | * Authors: Jingnan Shi, et al. (see THANKS for the full author list) 6 | * See LICENSE for the license information 7 | */ 8 | 9 | #pragma once 10 | #ifndef H_TEASER_MACROS_H 11 | #define H_TEASER_MACROS_H 12 | #include 13 | 14 | #define TEASER_DEBUG_ERROR_VAR(x) \ 15 | do { \ 16 | std::cerr << #x << " = " << x << std::endl; \ 17 | } while (0) 18 | 19 | #if defined(NDEBUG) && !defined(TEASER_DIAG_PRINT) 20 | #define TEASER_DEBUG_ERROR_MSG(x) \ 21 | do { \ 22 | } while (0) 23 | #define TEASER_DEBUG_INFO_MSG(x) \ 24 | do { \ 25 | } while (0) 26 | #else 27 | #define TEASER_DEBUG_ERROR_MSG(x) \ 28 | do { \ 29 | std::cerr << x << std::endl; \ 30 | } while (0) 31 | #define TEASER_DEBUG_INFO_MSG(x) \ 32 | do { \ 33 | std::cout << x << std::endl; \ 34 | } while (0) 35 | #endif 36 | 37 | #endif -------------------------------------------------------------------------------- /teaser/include/teaser/teaser_matcher.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020, Massachusetts Institute of Technology, 3 | * Cambridge, MA 02139 4 | * All Rights Reserved 5 | * Authors: Jingnan Shi, et al. (see THANKS for the full author list) 6 | * See LICENSE for the license information 7 | */ 8 | 9 | #pragma once 10 | #ifndef H_TEASER_MATCHER_H 11 | #define H_TEASER_MATCHER_H 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | namespace teaser { 24 | 25 | class Matcher { 26 | public: 27 | typedef std::vector Feature; 28 | typedef flann::Index> KDTree; 29 | 30 | // New methods 31 | // Public methods: 32 | // 1. calculateCorrespondences 33 | // input: source point cloud, target point cloud 34 | // output: correspondences 35 | Matcher() = default; 36 | 37 | /** 38 | * Calculate correspondences based on given features and point clouds. 39 | * @param source_points 40 | * @param target_points 41 | * @param use_absolute_scale 42 | * @param use_crosscheck 43 | * @param use_tuple_test 44 | * @return 45 | */ 46 | std::vector> 47 | calculateCorrespondences(const teaser::PointCloud& source_points, const teaser::PointCloud& target_points, 48 | const teaser::FPFHCloud& source_features, const teaser::FPFHCloud& target_features, 49 | bool use_absolute_scale = true, bool use_crosscheck = true, 50 | bool use_tuple_test = true, float tuple_scale = 0); 51 | 52 | private: 53 | template void buildKDTree(const std::vector& data, KDTree* tree); 54 | 55 | template 56 | void searchKDTree(KDTree* tree, const T& input, std::vector& indices, 57 | std::vector& dists, int nn); 58 | 59 | void advancedMatching(bool use_crosscheck, bool use_tuple_test, float tuple_scale); 60 | 61 | void normalizePoints(bool use_absolute_scale); 62 | 63 | std::vector> corres_; 64 | std::vector pointcloud_; 65 | std::vector features_; 66 | std::vector > means_; // for normalization 67 | float global_scale_; 68 | }; 69 | 70 | } // namespace teaser 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /teaser/include/teaser/teaser_utility.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef H_TEASER_UTILITY_H 3 | #define H_TEASER_UTILITY_H 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | // #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | using namespace std; 25 | 26 | //// Macro constants for generating noise and outliers 27 | //const double NOISE_BOUND = 0.005; // 0.05 28 | //const int N_OUTLIERS = 1700; 29 | //const double OUTLIER_TRANSLATION_LB = -5.0; // 0.5; 30 | //const double OUTLIER_TRANSLATION_UB = +5.0; // 0.5; 31 | 32 | //inline double getAngularError(Eigen::Matrix3d R_exp, Eigen::Matrix3d R_est) { 33 | // return std::abs(std::acos(fmin(fmax(((R_exp.transpose() * R_est).trace() - 1) / 2, -1.0), 1.0))); 34 | //} 35 | // 36 | //inline double rand_fun(const double& a, const double& b) { 37 | // if ( a > b ) { 38 | // printf_s("error in rand_fun()! lb should less than ub!\n"); 39 | // exit(-1); 40 | // } 41 | // //// dVal lies in [0, 1] 42 | // double dVal = rand() / (1.0 + RAND_MAX); 43 | // double dOut = ( b - a ) * dVal + a; 44 | // return dOut; 45 | //} 46 | // 47 | //inline void addNoiseAndOutliers(Eigen::Matrix& tgt) { 48 | // // Add uniform noise 49 | // Eigen::Matrix noise = 50 | // Eigen::Matrix::Random(3, tgt.cols()) * NOISE_BOUND; 51 | // NOISE_BOUND / 2; 52 | // tgt = tgt + noise; 53 | // 54 | // // Add outliers 55 | // std::random_device rd; 56 | // std::mt19937 gen(rd()); 57 | // std::uniform_int_distribution<> dis2(0, tgt.cols() - 1); // pos of outliers 58 | // // std::uniform_int_distribution<> dis3(OUTLIER_TRANSLATION_LB, OUTLIER_TRANSLATION_UB); // random translation 59 | // std::vector expected_outlier_mask(tgt.cols(), false); 60 | // //for (int i = 0; i < 100; i++) { 61 | // // double dVal = rand_fun(OUTLIER_TRANSLATION_LB, OUTLIER_TRANSLATION_UB); 62 | // // printf_s("rand_num = %f\n", dVal); 63 | // //} 64 | // for (int i = 0; i < N_OUTLIERS; ++i) { 65 | // int c_outlier_idx = dis2(gen); 66 | // assert(c_outlier_idx < expected_outlier_mask.size()); 67 | // expected_outlier_mask[c_outlier_idx] = true; 68 | // tgt(0, c_outlier_idx) += rand_fun(OUTLIER_TRANSLATION_LB, OUTLIER_TRANSLATION_UB); 69 | // tgt(1, c_outlier_idx) += rand_fun(OUTLIER_TRANSLATION_LB, OUTLIER_TRANSLATION_UB); 70 | // tgt(2, c_outlier_idx) += rand_fun(OUTLIER_TRANSLATION_LB, OUTLIER_TRANSLATION_UB); 71 | // // tgt.col(c_outlier_idx).array() += dis3(gen); // random translation 72 | // } 73 | //} 74 | 75 | //template 76 | //inline void cvt2Teaser_cloud( 77 | // const boost::shared_ptr>& cloud_pcl, 78 | // teaser::PointCloud& cloud_teaser ) { 79 | // cloud_teaser.clear(); 80 | // for (int i = 0; i < cloud_pcl->points.size(); i++) { 81 | // T pt = cloud_pcl->points[i]; 82 | // cloud_teaser.push_back( {pt.x, pt.y, pt.z} ); 83 | // } 84 | //} 85 | 86 | template 87 | inline void cvt2pcl_cloud(const teaser::PointCloud& cloud_in, 88 | boost::shared_ptr>& cloud_out) { 89 | cloud_out.reset(new pcl::PointCloud()); 90 | for (int i = 0; i < cloud_in.size(); i++) { 91 | T pt; 92 | memset(&pt, 0, sizeof(pt)); 93 | pt.x = cloud_in.at(i).x; 94 | pt.y = cloud_in.at(i).y; 95 | pt.z = cloud_in.at(i).z; 96 | cloud_out->points.push_back(pt); 97 | } 98 | cloud_out->is_dense = true; 99 | cloud_out->width = 1; 100 | cloud_out->height = cloud_out->points.size(); 101 | } 102 | 103 | //inline void test_bunny() { 104 | // //// Load the .ply file 105 | // //teaser::PLYReader reader; 106 | // //teaser::PointCloud src_cloud; 107 | // //string sFile = "example_data\\bun_zipper_res3.ply"; 108 | // //if (-1 == access(sFile.c_str(), 0)) { 109 | // // printf_s("%s does not exist!\n", sFile.c_str()); 110 | // //} 111 | // //auto status = reader.read(sFile.c_str(), src_cloud); 112 | // //int N = src_cloud.size(); 113 | // 114 | // //// Convert the point cloud to Eigen 115 | // //Eigen::Matrix src(3, N); 116 | // //for (size_t i = 0; i < N; ++i) { 117 | // // src.col(i) << src_cloud[i].x, src_cloud[i].y, src_cloud[i].z; 118 | // //} 119 | // 120 | // //// Homogeneous coordinates 121 | // //Eigen::Matrix src_h; 122 | // //src_h.resize(4, src.cols()); 123 | // //src_h.topRows(3) = src; 124 | // //src_h.bottomRows(1) = Eigen::Matrix::Ones(N); 125 | // 126 | // //// Apply an arbitrary SE(3) transformation 127 | // //Eigen::Matrix4d T; 128 | // //// clang-format off 129 | // //T << 9.96926560e-01, 6.68735757e-02, -4.06664421e-02, -1.15576939e-01, 130 | // // -6.61289946e-02, 9.97617877e-01, 1.94008687e-02, -3.87705398e-02, 131 | // // 4.18675510e-02, -1.66517807e-02, 9.98977765e-01, 1.14874890e-01, 132 | // // 0, 0, 0, 1; 133 | // //// clang-format on 134 | // 135 | // //// Apply transformation 136 | // //Eigen::Matrix tgt_h = T * src_h; 137 | // //Eigen::Matrix tgt = tgt_h.topRows(3); 138 | // 139 | // //// Add some noise & outliers 140 | // //addNoiseAndOutliers(tgt); 141 | // 142 | // //// Convert to teaser point cloud 143 | // //teaser::PointCloud tgt_cloud; 144 | // //for (size_t i = 0; i < tgt.cols(); ++i) { 145 | // // tgt_cloud.push_back({ static_cast(tgt(0, i)), static_cast(tgt(1, i)), 146 | // // static_cast(tgt(2, i)) }); 147 | // //} 148 | // ////// save the point cloud. 149 | // //pcl::PointCloud::Ptr cloud_tmp(new pcl::PointCloud()); 150 | // //cvt2pcl_cloud(src_cloud, cloud_tmp); 151 | // //pcl::PCDWriter pcdWriter; 152 | // //string sSaveFile = "cloud_src.pcd"; 153 | // //pcdWriter.writeBinary(sSaveFile, *cloud_tmp); 154 | // //cvt2pcl_cloud(tgt_cloud, cloud_tmp); 155 | // //sSaveFile = "cloud_tgt.pcd"; 156 | // //pcdWriter.writeBinary(sSaveFile, *cloud_tmp); 157 | // //// Compute FPFH 158 | // //std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); 159 | // //teaser::FPFHEstimation fpfh; 160 | // //auto obj_descriptors = fpfh.computeFPFHFeatures(src_cloud, 0.02, 0.04); 161 | // //auto scene_descriptors = fpfh.computeFPFHFeatures(tgt_cloud, 0.02, 0.04); 162 | // //std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); 163 | // 164 | // //int nTime_FPFH = std::chrono::duration_cast(end - begin).count() / 1000.0; 165 | // //begin = std::chrono::steady_clock::now(); 166 | // //teaser::Matcher matcher; 167 | // 168 | // //bool use_absolute_scale = true, 169 | // // use_crosscheck = false, 170 | // // use_tuple_test = true; 171 | // //float tuple_scale = 0.5; 172 | // 173 | // //auto correspondences = matcher.calculateCorrespondences(src_cloud, tgt_cloud, 174 | // // *obj_descriptors, *scene_descriptors, 175 | // // use_absolute_scale, use_crosscheck, use_tuple_test, tuple_scale); 176 | // //end = std::chrono::steady_clock::now(); 177 | // //int nTime_corres = std::chrono::duration_cast(end - begin).count() / 1000.0; 178 | // 179 | // //// Run TEASER++ registration 180 | // //// Prepare solver parameters 181 | // //teaser::RobustRegistrationSolver::Params params; 182 | // //params.noise_bound = NOISE_BOUND; 183 | // //params.cbar2 = 1; 184 | // //params.estimate_scaling = false; 185 | // //params.rotation_max_iterations = 100; 186 | // //params.rotation_gnc_factor = 1.4; 187 | // //params.rotation_estimation_algorithm = 188 | // // teaser::RobustRegistrationSolver::ROTATION_ESTIMATION_ALGORITHM::GNC_TLS; 189 | // //params.rotation_cost_threshold = 0.005; 190 | // 191 | // //// Solve with TEASER++ 192 | // //teaser::RobustRegistrationSolver solver(params); 193 | // //begin = std::chrono::steady_clock::now(); 194 | // //solver.solve(src_cloud, tgt_cloud, correspondences); 195 | // //end = std::chrono::steady_clock::now(); 196 | // 197 | // //auto solution = solver.getSolution(); 198 | // 199 | // //// Compare results 200 | // //std::cout << "=====================================" << std::endl; 201 | // //std::cout << " TEASER++ Results " << std::endl; 202 | // //std::cout << "=====================================" << std::endl; 203 | // //std::cout << "Expected rotation: " << std::endl; 204 | // //std::cout << T.topLeftCorner(3, 3) << std::endl; 205 | // //std::cout << "Estimated rotation: " << std::endl; 206 | // //std::cout << solution.rotation << std::endl; 207 | // //std::cout << "Error (deg): " << getAngularError(T.topLeftCorner(3, 3), solution.rotation) 208 | // // << std::endl; 209 | // //std::cout << std::endl; 210 | // //std::cout << "Expected translation: " << std::endl; 211 | // //std::cout << T.topRightCorner(3, 1) << std::endl; 212 | // //std::cout << "Estimated translation: " << std::endl; 213 | // //std::cout << solution.translation << std::endl; 214 | // //std::cout << "Error (m): " << (T.topRightCorner(3, 1) - solution.translation).norm() << std::endl; 215 | // //std::cout << std::endl; 216 | // //std::cout << "Number of correspondences: " << N << std::endl; 217 | // //std::cout << "Number of outliers: " << N_OUTLIERS << std::endl; 218 | // //int nTime_reg = std::chrono::duration_cast(end - begin).count() / 1000.0; 219 | // //printf_s("time_fpfh = %04dms, time_matcher = %04dms, time_reg = %04dms\n", 220 | // // nTime_FPFH, nTime_corres, nTime_reg); 221 | //} 222 | 223 | 224 | #endif // !H_UTILITY_H 225 | 226 | -------------------------------------------------------------------------------- /teaser/include/teaser/teaser_utils.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020, Massachusetts Institute of Technology, 3 | * Cambridge, MA 02139 4 | * All Rights Reserved 5 | * Authors: Jingnan Shi, et al. (see THANKS for the full author list) 6 | * See LICENSE for the license information 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifndef H_TEASER_UTILS_H 12 | #define H_TEASER_UTILS_H 13 | 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #include 21 | 22 | namespace teaser { 23 | using FPFHCloud = pcl::PointCloud; 24 | using FPFHCloudPtr = pcl::PointCloud::Ptr; 25 | 26 | namespace utils { 27 | 28 | /** 29 | * A templated random sample function (w/o replacement). Based on MATLAB implementation of 30 | * randsample() 31 | * @tparam T A number type 32 | * @tparam URBG A UniformRandomBitGenerator type 33 | * @param input A input vector containing the whole population 34 | * @param num_samples Number of samples we want 35 | * @param g 36 | * @return 37 | */ 38 | template 39 | std::vector randomSample(std::vector input, size_t num_samples, URBG&& g) { 40 | 41 | std::vector output; 42 | output.reserve(num_samples); 43 | if (4 * num_samples > input.size()) { 44 | // if the sample is a sizeable fraction of the whole population, 45 | // just randomly shuffle the entire population and return the 46 | // first num_samples 47 | std::shuffle(input.begin(), input.end(), g); 48 | for (size_t i = 0; i < num_samples; ++i) { 49 | output.push_back(input[i]); 50 | } 51 | } else { 52 | // if the sample is small, repeatedly sample with replacement until num_samples 53 | // unique values 54 | std::unordered_set sample_indices; 55 | std::uniform_int_distribution<> dis(0, input.size()); 56 | while (sample_indices.size() < num_samples) { 57 | sample_indices.insert(dis(std::forward(g))); 58 | } 59 | for (auto&& i : sample_indices) { 60 | output.push_back(input[i]); 61 | } 62 | } 63 | return output; 64 | } 65 | 66 | /** 67 | * Remove one row from matrix. 68 | * Credit to: https://stackoverflow.com/questions/13290395 69 | * @param matrix an Eigen::Matrix. 70 | * @param rowToRemove index of row to remove. If >= matrix.rows(), no operation will be taken 71 | */ 72 | template 73 | void removeRow(Eigen::Matrix& matrix, unsigned int rowToRemove) { 74 | if (rowToRemove >= matrix.rows()) { 75 | return; 76 | } 77 | unsigned int numRows = matrix.rows() - 1; 78 | unsigned int numCols = matrix.cols(); 79 | 80 | if (rowToRemove < numRows) { 81 | matrix.block(rowToRemove, 0, numRows - rowToRemove, numCols) = 82 | matrix.bottomRows(numRows - rowToRemove); 83 | } 84 | 85 | matrix.conservativeResize(numRows, numCols); 86 | } 87 | 88 | /** 89 | * Remove one column from matrix. 90 | * Credit to: https://stackoverflow.com/questions/13290395 91 | * @param matrix 92 | * @param colToRemove index of col to remove. If >= matrix.cols(), no operation will be taken 93 | */ 94 | template 95 | void removeColumn(Eigen::Matrix& matrix, unsigned int colToRemove) { 96 | if (colToRemove >= matrix.cols()) { 97 | return; 98 | } 99 | unsigned int numRows = matrix.rows(); 100 | unsigned int numCols = matrix.cols() - 1; 101 | 102 | if (colToRemove < numCols) { 103 | matrix.block(0, colToRemove, numRows, numCols - colToRemove) = 104 | matrix.rightCols(numCols - colToRemove); 105 | } 106 | 107 | matrix.conservativeResize(numRows, numCols); 108 | } 109 | 110 | /** 111 | * Helper function to calculate the diameter of a row vector of points 112 | * @param X 113 | * @return the diameter of the set of points given 114 | */ 115 | template float calculateDiameter(const Eigen::Matrix& X) { 116 | Eigen::Matrix cog = X.rowwise().sum() / X.cols(); 117 | Eigen::Matrix P = X.colwise() - cog; 118 | Eigen::Matrix temp = P.array().square().colwise().sum(); 119 | return 2 * std::sqrt(temp.maxCoeff()); 120 | } 121 | 122 | /** 123 | * Helper function to use svd to estimate rotation. 124 | * Method described here: http://igl.ethz.ch/projects/ARAP/svd_rot.pdf 125 | * @param X 126 | * @param Y 127 | * @return a rotation matrix R 128 | */ 129 | inline Eigen::Matrix3d svdRot(const Eigen::Matrix& X, 130 | const Eigen::Matrix& Y, 131 | const Eigen::Matrix& W) { 132 | // Assemble the correlation matrix H = X * Y' 133 | Eigen::Matrix3d H = X * W.asDiagonal() * Y.transpose(); 134 | 135 | Eigen::JacobiSVD svd(H, Eigen::ComputeFullU | Eigen::ComputeFullV); 136 | Eigen::Matrix3d U = svd.matrixU(); 137 | Eigen::Matrix3d V = svd.matrixV(); 138 | 139 | if (U.determinant() * V.determinant() < 0) { 140 | V.col(2) *= -1; 141 | } 142 | 143 | return V * U.transpose(); 144 | } 145 | 146 | /** 147 | * Use an boolean Eigen matrix to mask a vector 148 | * @param mask a 1-by-N boolean Eigen matrix 149 | * @param elements vector to be masked 150 | * @return 151 | */ 152 | template 153 | inline std::vector maskVector(Eigen::Matrix mask, 154 | std::vector elements) { 155 | std::vector result; 156 | for (size_t i = 0; i < mask.cols(); ++i) { 157 | if (mask(i)) { 158 | result.push_back(elements[i]); 159 | } 160 | } 161 | return result; 162 | } 163 | 164 | } // namespace utils 165 | 166 | } // namespace teaser 167 | 168 | #endif 169 | -------------------------------------------------------------------------------- /teaser/src/no_use/ply_io.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020, Massachusetts Institute of Technology, 3 | * Cambridge, MA 02139 4 | * All Rights Reserved 5 | * Authors: Jingnan Shi, et al. (see THANKS for the full author list) 6 | * See LICENSE for the license information 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "teaser/ply_io.h" 16 | #include "tinyply.h" 17 | 18 | // Internal datatypes for storing ply vertices 19 | struct float3 { 20 | float x, y, z; 21 | }; 22 | struct double3 { 23 | double x, y, z; 24 | }; 25 | 26 | int teaser::PLYReader::read(const std::string& file_name, teaser::PointCloud& cloud) { 27 | std::unique_ptr file_stream; 28 | std::vector byte_buffer; 29 | 30 | try { 31 | file_stream.reset(new std::ifstream(file_name, std::ios::binary)); 32 | 33 | if (!file_stream || file_stream->fail()) { 34 | std::cerr << "Failed to open " << file_name << std::endl; 35 | return -1; 36 | } 37 | 38 | tinyply::PlyFile file; 39 | file.parse_header(*file_stream); 40 | 41 | std::shared_ptr vertices; 42 | 43 | try { 44 | vertices = file.request_properties_from_element("vertex", {"x", "y", "z"}); 45 | } catch (const std::exception& e) { 46 | std::cerr << "tinyply exception: " << e.what() << std::endl; 47 | return -1; 48 | } 49 | 50 | file.read(*file_stream); 51 | 52 | if (vertices) { 53 | std::cout << "\tRead " << vertices->count << " total vertices " << std::endl; 54 | if (vertices->t == tinyply::Type::FLOAT32) { 55 | std::vector verts_floats(vertices->count); 56 | const size_t numVerticesBytes = vertices->buffer.size_bytes(); 57 | std::memcpy(verts_floats.data(), vertices->buffer.get(), numVerticesBytes); 58 | for (auto& i : verts_floats) { 59 | cloud.push_back({i.x, i.y, i.z}); 60 | } 61 | } 62 | if (vertices->t == tinyply::Type::FLOAT64) { 63 | std::vector verts_doubles(vertices->count); 64 | const size_t numVerticesBytes = vertices->buffer.size_bytes(); 65 | std::memcpy(verts_doubles.data(), vertices->buffer.get(), numVerticesBytes); 66 | for (auto& i : verts_doubles) { 67 | cloud.push_back( 68 | {static_cast(i.x), static_cast(i.y), static_cast(i.z)}); 69 | } 70 | } 71 | } 72 | 73 | } catch (const std::exception& e) { 74 | std::cerr << "Caught tinyply exception: " << e.what() << std::endl; 75 | return -1; 76 | } 77 | 78 | return 0; 79 | } 80 | 81 | int teaser::PLYWriter::write(const std::string& file_name, const teaser::PointCloud& cloud, 82 | bool binary_mode) { 83 | // Open file buffer according to binary mode 84 | std::filebuf fb; 85 | if (binary_mode) { 86 | fb.open(file_name, std::ios::out | std::ios::binary); 87 | } else { 88 | fb.open(file_name, std::ios::out); 89 | } 90 | 91 | // Open output stream 92 | std::ostream outstream(&fb); 93 | if (outstream.fail()) { 94 | std::cerr << "Failed to open " << file_name << std::endl; 95 | return -1; 96 | } 97 | 98 | // Use tinyply to write to ply file 99 | tinyply::PlyFile ply_file; 100 | std::vector temp_vertices; 101 | for (auto& i : cloud) { 102 | temp_vertices.push_back({i.x, i.y, i.z}); 103 | } 104 | ply_file.add_properties_to_element( 105 | "vertex", {"x", "y", "z"}, tinyply::Type::FLOAT32, temp_vertices.size(), 106 | reinterpret_cast(temp_vertices.data()), tinyply::Type::INVALID, 0); 107 | ply_file.write(outstream, binary_mode); 108 | 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /teaser/src/no_use/teaser_fpfh.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020, Massachusetts Institute of Technology, 3 | * Cambridge, MA 02139 4 | * All Rights Reserved 5 | * Authors: Jingnan Shi, et al. (see THANKS for the full author list) 6 | * See LICENSE for the license information 7 | */ 8 | 9 | #include 10 | 11 | //teaser::FPFHCloudPtr teaser::FPFHEstimation::computeFPFHFeatures ( 12 | // const pcl::PointCloud ::Ptr& input_cloud, 13 | // double fpfh_search_radius) { 14 | // // Intermediate variables 15 | // pcl::PointCloud::Ptr normals(new pcl::PointCloud); 16 | // teaser::FPFHCloudPtr descriptors(new pcl::PointCloud()); 17 | // pcl::PointCloud::Ptr pcl_input_cloud(new pcl::PointCloud); 18 | // 19 | // 20 | // // decouple point cloud and normals 21 | // decoupleCloud(input_cloud, pcl_input_cloud, normals); 22 | // pcl::search::KdTree::Ptr kdtree(new pcl::search::KdTree); 23 | // kdtree->setInputCloud(pcl_input_cloud); 24 | // 25 | // // Estimate FPFH 26 | // // setInputCloud(pcl_input_cloud); 27 | // fpfh_estimation_->setInputCloud(pcl_input_cloud); 28 | // fpfh_estimation_->setInputNormals(normals); 29 | // // setInputNormals(normals); 30 | // // setSearchMethod(kdtree); 31 | // // fpfh_estimation_->setSearchMethod(kdtree); 32 | // fpfh_estimation_->setRadiusSearch(fpfh_search_radius); 33 | // // setRadiusSearch(fpfh_search_radius); 34 | // // compute(*descriptors); 35 | // fpfh_estimation_->compute(*descriptors); 36 | // 37 | // return descriptors; 38 | //} 39 | 40 | 41 | //void teaser::FPFHEstimation::setInputCloud(pcl::PointCloud::Ptr input_cloud) { 42 | // fpfh_estimation_->setInputCloud(input_cloud); 43 | //} 44 | // 45 | //void teaser::FPFHEstimation::setInputNormals(pcl::PointCloud::Ptr input_normals) { 46 | // fpfh_estimation_->setInputNormals(input_normals); 47 | //} 48 | // 49 | //void teaser::FPFHEstimation::setSearchMethod( 50 | // pcl::search::KdTree::Ptr search_method) { 51 | // fpfh_estimation_->setSearchMethod(search_method); 52 | //} 53 | // 54 | //void teaser::FPFHEstimation::compute(pcl::PointCloud& output_cloud) { 55 | // fpfh_estimation_->compute(output_cloud); 56 | //} 57 | //void teaser::FPFHEstimation::setRadiusSearch(double r) { fpfh_estimation_->setRadiusSearch(r); } 58 | -------------------------------------------------------------------------------- /teaser/src/teaser_graph.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020, Massachusetts Institute of Technology, 3 | * Cambridge, MA 02139 4 | * All Rights Reserved 5 | * Authors: Jingnan Shi, et al. (see THANKS for the full author list) 6 | * See LICENSE for the license information 7 | */ 8 | 9 | #include 10 | 11 | vector teaser::MaxCliqueSolver::findMaxClique(teaser::Graph graph) { 12 | 13 | // Handle deprecated field 14 | if (!params_.solve_exactly) { 15 | params_.solver_mode = CLIQUE_SOLVER_MODE::PMC_HEU; 16 | } 17 | 18 | // Create a PMC graph from the TEASER graph 19 | vector edges; 20 | vector vertices; 21 | vertices.push_back(edges.size()); 22 | 23 | const auto all_vertices = graph.getVertices(); 24 | for (const auto& i : all_vertices) { 25 | const auto& c_edges = graph.getEdges(i); 26 | edges.insert(edges.end(), c_edges.begin(), c_edges.end()); 27 | vertices.push_back(edges.size()); 28 | } 29 | 30 | // Use PMC to calculate 31 | pmc::pmc_graph G(vertices, edges); 32 | 33 | // Prepare PMC input 34 | // TODO: Incorporate this to the constructor. 35 | input in; 36 | in.algorithm = 0; 37 | in.threads = 12; 38 | in.experiment = 0; 39 | in.lb = 0; 40 | in.ub = 0; 41 | in.param_ub = 0; 42 | in.adj_limit = 20000; 43 | in.time_limit = params_.time_limit; 44 | in.remove_time = 4; 45 | in.graph_stats = false; 46 | in.verbose = false; 47 | in.help = false; 48 | in.MCE = false; 49 | in.decreasing_order = false; 50 | in.heu_strat = "kcore"; 51 | in.vertex_search_order = "deg"; 52 | 53 | // vector to represent max clique 54 | vector C; 55 | 56 | // upper-bound of max clique 57 | G.compute_cores(); 58 | auto max_core = G.get_max_core(); 59 | 60 | TEASER_DEBUG_INFO_MSG("Max core number: " << max_core); 61 | TEASER_DEBUG_INFO_MSG("Num vertices: " << vertices.size()); 62 | 63 | // check for k-core heuristic threshold 64 | // check whether threshold equals 1 to short circuit the comparison 65 | if (params_.solver_mode == CLIQUE_SOLVER_MODE::KCORE_HEU && 66 | params_.kcore_heuristic_threshold != 1 && 67 | max_core > static_cast(params_.kcore_heuristic_threshold * 68 | static_cast(all_vertices.size()))) { 69 | TEASER_DEBUG_INFO_MSG("Using K-core heuristic finder."); 70 | // remove all nodes with core number less than max core number 71 | // k_cores is a vector saving the core number of each vertex 72 | auto k_cores = G.get_kcores(); 73 | for (int i = 1; i < k_cores->size(); ++i) { 74 | // Note: k_core has size equals to num vertices + 1 75 | if ((*k_cores)[i] >= max_core) { 76 | C.push_back(i-1); 77 | } 78 | } 79 | return C; 80 | } 81 | 82 | if (in.ub == 0) { 83 | in.ub = max_core + 1; 84 | } 85 | 86 | // lower-bound of max clique 87 | if (in.lb == 0 && in.heu_strat != "0") { // skip if given as input 88 | pmc::pmc_heu maxclique(G, in); 89 | in.lb = maxclique.search(G, C); 90 | } 91 | 92 | assert(in.lb != 0); 93 | if (in.lb == 0) { 94 | // This means that max clique has a size of one 95 | TEASER_DEBUG_ERROR_MSG("Max clique lower bound equals to zero. Abort."); 96 | return C; 97 | } 98 | 99 | if (in.lb == in.ub) { 100 | return C; 101 | } 102 | 103 | // Optional exact max clique finding 104 | if (params_.solver_mode == CLIQUE_SOLVER_MODE::PMC_EXACT) { 105 | // The following methods are used: 106 | // 1. k-core pruning 107 | // 2. neigh-core pruning/ordering 108 | // 3. dynamic coloring bounds/sort 109 | // see the original PMC paper and implementation for details: 110 | // R. A. Rossi, D. F. Gleich, and A. H. Gebremedhin, “Parallel Maximum Clique Algorithms with 111 | // Applications to Network Analysis,” SIAM J. Sci. Comput., vol. 37, no. 5, pp. C589–C616, Jan. 112 | // 2015. 113 | if (G.num_vertices() < in.adj_limit) { 114 | G.create_adj(); 115 | pmc::pmcx_maxclique finder(G, in); 116 | finder.search_dense(G, C); 117 | } else { 118 | pmc::pmcx_maxclique finder(G, in); 119 | finder.search(G, C); 120 | } 121 | } 122 | 123 | return C; 124 | } 125 | -------------------------------------------------------------------------------- /teaser/src/teaser_matcher.cc: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2020, Massachusetts Institute of Technology, 3 | * Cambridge, MA 02139 4 | * All Rights Reserved 5 | * Authors: Jingnan Shi, et al. (see THANKS for the full author list) 6 | * See LICENSE for the license information 7 | */ 8 | 9 | #include 10 | 11 | namespace teaser { 12 | 13 | std::vector> Matcher::calculateCorrespondences( 14 | const teaser::PointCloud& source_points, const teaser::PointCloud& target_points, 15 | const teaser::FPFHCloud& source_features, const teaser::FPFHCloud& target_features, 16 | bool use_absolute_scale, 17 | bool use_crosscheck, bool use_tuple_test, float tuple_scale) { 18 | //// clear variable before computing. 19 | pointcloud_.clear(); 20 | features_.clear(); 21 | 22 | pointcloud_.push_back(source_points); 23 | pointcloud_.push_back(target_points); 24 | 25 | // It compute the global_scale_ required to set correctly the search radius 26 | normalizePoints(use_absolute_scale); 27 | Feature cloud_features; 28 | for (auto& f : source_features) { 29 | Eigen::VectorXf fpfh(33); 30 | for (int i = 0; i < 33; i++) 31 | fpfh(i) = f.histogram[i]; 32 | cloud_features.push_back(fpfh); 33 | } 34 | features_.push_back(cloud_features); 35 | 36 | cloud_features.clear(); 37 | for (auto& f : target_features) { 38 | Eigen::VectorXf fpfh(33); 39 | for (int i = 0; i < 33; i++) 40 | fpfh(i) = f.histogram[i]; 41 | cloud_features.push_back(fpfh); 42 | } 43 | features_.push_back(cloud_features); 44 | 45 | advancedMatching(use_crosscheck, use_tuple_test, tuple_scale); 46 | 47 | return corres_; 48 | } 49 | 50 | void Matcher::normalizePoints(bool use_absolute_scale) { 51 | int num = 2; 52 | float scale = 0; 53 | 54 | means_.clear(); 55 | 56 | for (int i = 0; i < num; ++i) { 57 | float max_scale = 0; 58 | 59 | // compute mean 60 | Eigen::Vector3f mean; 61 | mean.setZero(); 62 | 63 | int npti = pointcloud_[i].size(); 64 | for (int ii = 0; ii < npti; ++ii) { 65 | Eigen::Vector3f p(pointcloud_[i][ii].x, pointcloud_[i][ii].y, pointcloud_[i][ii].z); 66 | mean = mean + p; 67 | } 68 | mean = mean / npti; 69 | means_.push_back(mean); 70 | 71 | for (int ii = 0; ii < npti; ++ii) { 72 | pointcloud_[i][ii].x -= mean(0); 73 | pointcloud_[i][ii].y -= mean(1); 74 | pointcloud_[i][ii].z -= mean(2); 75 | } 76 | 77 | // compute scale 78 | for (int ii = 0; ii < npti; ++ii) { 79 | Eigen::Vector3f p(pointcloud_[i][ii].x, pointcloud_[i][ii].y, pointcloud_[i][ii].z); 80 | float temp = p.norm(); // because we extract mean in the previous stage. 81 | if (temp > max_scale) { 82 | max_scale = temp; 83 | } 84 | } 85 | 86 | if (max_scale > scale) { 87 | scale = max_scale; 88 | } 89 | } 90 | 91 | // mean of the scale variation 92 | if (use_absolute_scale) { 93 | global_scale_ = 1.0f; 94 | } 95 | else { 96 | global_scale_ = scale; // second choice: we keep the maximum scale. 97 | } 98 | 99 | if (global_scale_ != 1.0f) { 100 | for (int i = 0; i < num; ++i) { 101 | int npti = pointcloud_[i].size(); 102 | for (int ii = 0; ii < npti; ++ii) { 103 | pointcloud_[i][ii].x /= global_scale_; 104 | pointcloud_[i][ii].y /= global_scale_; 105 | pointcloud_[i][ii].z /= global_scale_; 106 | } 107 | } 108 | } 109 | } 110 | void Matcher::advancedMatching(bool use_crosscheck, bool use_tuple_test, float tuple_scale) { 111 | 112 | int fi = 0; // source idx 113 | int fj = 1; // destination idx 114 | 115 | bool swapped = false; 116 | 117 | if (pointcloud_[fj].size() > pointcloud_[fi].size()) { 118 | int temp = fi; 119 | fi = fj; 120 | fj = temp; 121 | swapped = true; 122 | } 123 | 124 | int nPti = pointcloud_[fi].size(); 125 | int nPtj = pointcloud_[fj].size(); 126 | 127 | /////////////////////////// 128 | /// Build FLANNTREE 129 | /////////////////////////// 130 | KDTree feature_tree_i(flann::KDTreeSingleIndexParams(15)); 131 | buildKDTree(features_[fi], &feature_tree_i); 132 | 133 | KDTree feature_tree_j(flann::KDTreeSingleIndexParams(15)); 134 | buildKDTree(features_[fj], &feature_tree_j); 135 | 136 | std::vector corres_K, corres_K2; 137 | std::vector dis; 138 | std::vector ind; 139 | 140 | std::vector> corres; 141 | std::vector> corres_cross; 142 | std::vector> corres_ij; 143 | std::vector> corres_ji; 144 | 145 | /////////////////////////// 146 | /// INITIAL MATCHING 147 | /////////////////////////// 148 | std::vector i_to_j(nPti, -1); 149 | for (int j = 0; j < nPtj; j++) { 150 | searchKDTree(&feature_tree_i, features_[fj][j], corres_K, dis, 1); 151 | int i = corres_K[0]; 152 | if (i_to_j[i] == -1) { 153 | searchKDTree(&feature_tree_j, features_[fi][i], corres_K, dis, 1); 154 | int ij = corres_K[0]; 155 | i_to_j[i] = ij; 156 | } 157 | corres_ji.push_back(std::pair(i, j)); 158 | } 159 | 160 | for (int i = 0; i < nPti; i++) { 161 | if (i_to_j[i] != -1) 162 | corres_ij.push_back(std::pair(i, i_to_j[i])); 163 | } 164 | 165 | int ncorres_ij = corres_ij.size(); 166 | int ncorres_ji = corres_ji.size(); 167 | 168 | // corres = corres_ij + corres_ji; 169 | for (int i = 0; i < ncorres_ij; ++i) 170 | corres.push_back(std::pair(corres_ij[i].first, corres_ij[i].second)); 171 | for (int j = 0; j < ncorres_ji; ++j) 172 | corres.push_back(std::pair(corres_ji[j].first, corres_ji[j].second)); 173 | 174 | /////////////////////////// 175 | /// CROSS CHECK 176 | /// input : corres_ij, corres_ji 177 | /// output : corres 178 | /////////////////////////// 179 | if (use_crosscheck) { 180 | std::cout << "CROSS CHECK" << std::endl; 181 | // build data structure for cross check 182 | corres.clear(); 183 | corres_cross.clear(); 184 | std::vector> Mi(nPti); 185 | std::vector> Mj(nPtj); 186 | 187 | int ci, cj; 188 | for (int i = 0; i < ncorres_ij; ++i) { 189 | ci = corres_ij[i].first; 190 | cj = corres_ij[i].second; 191 | Mi[ci].push_back(cj); 192 | } 193 | for (int j = 0; j < ncorres_ji; ++j) { 194 | ci = corres_ji[j].first; 195 | cj = corres_ji[j].second; 196 | Mj[cj].push_back(ci); 197 | } 198 | 199 | // cross check 200 | for (int i = 0; i < nPti; ++i) { 201 | for (int ii = 0; ii < Mi[i].size(); ++ii) { 202 | int j = Mi[i][ii]; 203 | for (int jj = 0; jj < Mj[j].size(); ++jj) { 204 | if (Mj[j][jj] == i) { 205 | corres.push_back(std::pair(i, j)); 206 | corres_cross.push_back(std::pair(i, j)); 207 | } 208 | } 209 | } 210 | } 211 | } 212 | else { 213 | std::cout << "Skipping Cross Check." << std::endl; 214 | } 215 | 216 | /////////////////////////// 217 | /// TUPLE CONSTRAINT 218 | /// input : corres 219 | /// output : corres 220 | /////////////////////////// 221 | if (use_tuple_test && tuple_scale != 0) { 222 | std::cout << "TUPLE CONSTRAINT" << std::endl; 223 | srand(time(NULL)); 224 | int rand0, rand1, rand2; 225 | int idi0, idi1, idi2; 226 | int idj0, idj1, idj2; 227 | float scale = tuple_scale; 228 | int ncorr = corres.size(); 229 | int number_of_trial = ncorr * 100; 230 | std::vector> corres_tuple; 231 | 232 | for (int i = 0; i < number_of_trial; i++) { 233 | rand0 = rand() % ncorr; 234 | rand1 = rand() % ncorr; 235 | rand2 = rand() % ncorr; 236 | 237 | idi0 = corres[rand0].first; 238 | idj0 = corres[rand0].second; 239 | idi1 = corres[rand1].first; 240 | idj1 = corres[rand1].second; 241 | idi2 = corres[rand2].first; 242 | idj2 = corres[rand2].second; 243 | 244 | // collect 3 points from i-th fragment 245 | Eigen::Vector3f pti0 = { pointcloud_[fi][idi0].x, pointcloud_[fi][idi0].y, 246 | pointcloud_[fi][idi0].z }; 247 | Eigen::Vector3f pti1 = { pointcloud_[fi][idi1].x, pointcloud_[fi][idi1].y, 248 | pointcloud_[fi][idi1].z }; 249 | Eigen::Vector3f pti2 = { pointcloud_[fi][idi2].x, pointcloud_[fi][idi2].y, 250 | pointcloud_[fi][idi2].z }; 251 | 252 | float li0 = (pti0 - pti1).norm(); 253 | float li1 = (pti1 - pti2).norm(); 254 | float li2 = (pti2 - pti0).norm(); 255 | 256 | // collect 3 points from j-th fragment 257 | Eigen::Vector3f ptj0 = { pointcloud_[fj][idj0].x, pointcloud_[fj][idj0].y, 258 | pointcloud_[fj][idj0].z }; 259 | Eigen::Vector3f ptj1 = { pointcloud_[fj][idj1].x, pointcloud_[fj][idj1].y, 260 | pointcloud_[fj][idj1].z }; 261 | Eigen::Vector3f ptj2 = { pointcloud_[fj][idj2].x, pointcloud_[fj][idj2].y, 262 | pointcloud_[fj][idj2].z }; 263 | 264 | float lj0 = (ptj0 - ptj1).norm(); 265 | float lj1 = (ptj1 - ptj2).norm(); 266 | float lj2 = (ptj2 - ptj0).norm(); 267 | 268 | if ((li0 * scale < lj0) && (lj0 < li0 / scale) && (li1 * scale < lj1) && 269 | (lj1 < li1 / scale) && (li2 * scale < lj2) && (lj2 < li2 / scale)) { 270 | corres_tuple.push_back(std::pair(idi0, idj0)); 271 | corres_tuple.push_back(std::pair(idi1, idj1)); 272 | corres_tuple.push_back(std::pair(idi2, idj2)); 273 | } 274 | } 275 | corres.clear(); 276 | 277 | for (size_t i = 0; i < corres_tuple.size(); ++i) 278 | corres.push_back(std::pair(corres_tuple[i].first, corres_tuple[i].second)); 279 | } 280 | else { 281 | std::cout << "Skipping Tuple Constraint." << std::endl; 282 | } 283 | 284 | if (swapped) { 285 | std::vector> temp; 286 | for (size_t i = 0; i < corres.size(); i++) 287 | temp.push_back(std::pair(corres[i].second, corres[i].first)); 288 | corres.clear(); 289 | corres = temp; 290 | } 291 | corres_ = corres; 292 | 293 | /////////////////////////// 294 | /// ERASE DUPLICATES 295 | /// input : corres_ 296 | /// output : corres_ 297 | /////////////////////////// 298 | std::sort(corres_.begin(), corres_.end()); 299 | corres_.erase(std::unique(corres_.begin(), corres_.end()), corres_.end()); 300 | } 301 | 302 | template void Matcher::buildKDTree(const std::vector& data, Matcher::KDTree* tree) { 303 | int rows, dim; 304 | rows = (int)data.size(); 305 | dim = (int)data[0].size(); 306 | std::vector dataset(rows * dim); 307 | flann::Matrix dataset_mat(&dataset[0], rows, dim); 308 | for (int i = 0; i < rows; i++) 309 | for (int j = 0; j < dim; j++) 310 | dataset[i * dim + j] = data[i][j]; 311 | KDTree temp_tree(dataset_mat, flann::KDTreeSingleIndexParams(15)); 312 | temp_tree.buildIndex(); 313 | *tree = temp_tree; 314 | } 315 | 316 | template 317 | void Matcher::searchKDTree(Matcher::KDTree* tree, const T& input, std::vector& indices, 318 | std::vector& dists, int nn) { 319 | int rows_t = 1; 320 | int dim = input.size(); 321 | 322 | std::vector query; 323 | query.resize(rows_t * dim); 324 | for (int i = 0; i < dim; i++) 325 | query[i] = input(i); 326 | flann::Matrix query_mat(&query[0], rows_t, dim); 327 | 328 | indices.resize(rows_t * nn); 329 | dists.resize(rows_t * nn); 330 | flann::Matrix indices_mat(&indices[0], rows_t, nn); 331 | flann::Matrix dists_mat(&dists[0], rows_t, nn); 332 | 333 | tree->knnSearch(query_mat, indices_mat, dists_mat, nn, flann::SearchParams(128)); 334 | } 335 | 336 | } // namespace teaser 337 | -------------------------------------------------------------------------------- /visResult.m: -------------------------------------------------------------------------------- 1 | clc; close all; clear all; 2 | DataFolder = 'D:\Projects\Teaser_SPARK\Teaser'; 3 | cloud_src = pcread( fullfile(DataFolder, 'cloud_src.pcd') ); 4 | cloud_aft = pcread( fullfile(DataFolder, 'cloud_aft.pcd') ); 5 | cloud_tgt = pcread( fullfile(DataFolder, 'cloud_tgt.pcd') ); 6 | figure; set(gcf, 'Position', [50 50 800 600], 'color', 'w'); hold on; grid on; box on; axis equal; 7 | pcshow(cloud_tgt.Location, 'g', 'markersize', 50); 8 | pcshow(cloud_aft.Location, 'b', 'markersize', 50); 9 | % pcshow(cloud_src.Location, 'r', 'markersize', 50); --------------------------------------------------------------------------------