├── .gitignore ├── CMakeLists.txt ├── LICENSE.md ├── Makefile ├── README.md ├── cmake └── pmcConfig.cmake ├── include └── pmc │ ├── pmc.h │ ├── pmc_bool_vector.h │ ├── pmc_debug_utils.h │ ├── pmc_graph.h │ ├── pmc_headers.h │ ├── pmc_heu.h │ ├── pmc_input.h │ ├── pmc_maxclique.h │ ├── pmc_neigh_coloring.h │ ├── pmc_neigh_cores.h │ ├── pmc_utils.h │ ├── pmc_vertex.h │ ├── pmcx_maxclique.h │ └── pmcx_maxclique_basic.h ├── libpmc.h ├── libpmc_test.cpp ├── pmc.jl ├── pmc.m ├── pmc.py ├── pmc_clique_utils.cpp ├── pmc_cores.cpp ├── pmc_driver.cpp ├── pmc_graph.cpp ├── pmc_heu.cpp ├── pmc_lib.cpp ├── pmc_maxclique.cpp ├── pmc_utils.cpp ├── pmcx_maxclique.cpp └── pmcx_maxclique_basic.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.sql 26 | *.sqlite 27 | *.mat 28 | *.edges 29 | *.smat 30 | *.labels 31 | *.graphml 32 | *.gephi 33 | *.png 34 | *.eps 35 | *.jpg 36 | 37 | # OS generated files # 38 | ###################### 39 | .DS_Store 40 | .DS_Store? 41 | ._* 42 | .Spotlight-V100 43 | .Trashes 44 | Icon? 45 | ehthumbs.db 46 | Thumbs.db 47 | .settings/ 48 | .cproject 49 | .project 50 | .pydevproject 51 | data/ 52 | exp/ 53 | 54 | 55 | #Latex # 56 | ############## 57 | *.acn 58 | *.acr 59 | *.alg 60 | *.aux 61 | *.bbl 62 | *.blg 63 | *.dvi 64 | *.fdb_latexmk 65 | *.glg 66 | *.glo 67 | *.gls 68 | *.idx 69 | *.ilg 70 | *.ind 71 | *.ist 72 | *.lof 73 | *.log 74 | *.lot 75 | *.maf 76 | *.mtc 77 | *.mtc0 78 | *.nav 79 | *.nlo 80 | *.out 81 | *.pdfsync 82 | *.ps 83 | *.snm 84 | *.synctex.gz 85 | *.toc 86 | *.vrb 87 | *.xdy 88 | 89 | # build folders 90 | /cmake-build-* 91 | /build* 92 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(pmc VERSION 1.0.0) 3 | 4 | set(PMC_SOURCE_FILES 5 | ${CMAKE_CURRENT_SOURCE_DIR}/pmc_heu.cpp 6 | ${CMAKE_CURRENT_SOURCE_DIR}/pmc_maxclique.cpp 7 | ${CMAKE_CURRENT_SOURCE_DIR}/pmcx_maxclique.cpp 8 | ${CMAKE_CURRENT_SOURCE_DIR}/pmcx_maxclique_basic.cpp 9 | ${CMAKE_CURRENT_SOURCE_DIR}/pmc_cores.cpp 10 | ${CMAKE_CURRENT_SOURCE_DIR}/pmc_utils.cpp 11 | ${CMAKE_CURRENT_SOURCE_DIR}/pmc_graph.cpp 12 | ${CMAKE_CURRENT_SOURCE_DIR}/pmc_clique_utils.cpp 13 | ) 14 | 15 | option(PMC_BUILD_SHARED "Build pmc as a shared library (.so)" ON) 16 | if (PMC_BUILD_SHARED) 17 | add_library(pmc SHARED ${PMC_SOURCE_FILES}) 18 | else() 19 | add_library(pmc STATIC ${PMC_SOURCE_FILES}) 20 | set_property(TARGET pmc PROPERTY POSITION_INDEPENDENT_CODE 1) 21 | endif() 22 | 23 | target_compile_options(pmc PRIVATE 24 | -Wall 25 | -Werror 26 | -Wextra 27 | -Wno-format 28 | -Wno-sign-compare 29 | -Wno-unused-function 30 | ) 31 | 32 | target_include_directories(pmc PUBLIC 33 | $ 34 | $ 35 | $ 36 | ) 37 | 38 | add_executable(pmc_main pmc_driver.cpp) 39 | target_link_libraries(pmc_main pmc) 40 | 41 | find_package(OpenMP REQUIRED) 42 | target_link_libraries(pmc OpenMP::OpenMP_CXX) 43 | target_link_libraries(pmc_main OpenMP::OpenMP_CXX) 44 | 45 | # Installation 46 | include(GNUInstallDirs) 47 | 48 | install(TARGETS pmc 49 | EXPORT pmc-export 50 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 51 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 52 | ) 53 | 54 | export( 55 | EXPORT pmc-export 56 | FILE ${CMAKE_CURRENT_BINARY_DIR}/pmcTargets.cmake 57 | NAMESPACE pmc:: 58 | ) 59 | 60 | install(EXPORT pmc-export 61 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/pmc 62 | NAMESPACE pmc:: 63 | FILE pmcTargets.cmake 64 | ) 65 | 66 | install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 67 | 68 | install(FILES cmake/pmcConfig.cmake 69 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/pmc 70 | ) 71 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /cmake/pmcConfig.cmake: -------------------------------------------------------------------------------- 1 | get_filename_component(PMC_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) 2 | 3 | include(CMakeFindDependencyMacro) 4 | 5 | find_dependency(OpenMP REQUIRED) 6 | 7 | include("${PMC_CMAKE_DIR}/pmcTargets.cmake") 8 | -------------------------------------------------------------------------------- /include/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 | -------------------------------------------------------------------------------- /include/pmc/pmc_bool_vector.h: -------------------------------------------------------------------------------- 1 | #ifndef PMC_BOOL_VECTOR_H_ 2 | #define PMC_BOOL_VECTOR_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace pmc { 8 | 9 | /// A bare minimum implementation of a boolean vector. 10 | /// 11 | /// This class is recommended in place of std::vector or std::vector for thread-safety. 12 | /// std::vector could cause a race condition if it is implemented as a dynamic bitset. 13 | /// std::vector is not memory efficient and misleading as an element can hold other than 0 or 1. 14 | class bool_wrapper { 15 | public: 16 | /// NOTE: It should not be marked `explicit` to allow implicit conversion from `bool` to `bool_wrapper`. 17 | bool_wrapper(bool value = false) : byte_(to_int(value)) {} 18 | 19 | operator bool() const noexcept { return byte_ != 0; } 20 | 21 | bool_wrapper& operator=(bool value) noexcept { 22 | byte_ = to_int(value); 23 | return *this; 24 | } 25 | 26 | private: 27 | static constexpr std::uint8_t to_int(bool value) noexcept { 28 | return value ? 1 : 0; 29 | } 30 | 31 | std::uint8_t byte_; 32 | }; 33 | 34 | using bool_vector = std::vector; 35 | 36 | } // namespace pmc 37 | 38 | #endif -------------------------------------------------------------------------------- /include/pmc/pmc_debug_utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by jnshi on 10/2/22. 3 | // 4 | 5 | #ifndef PMC_DEBUG_UTILS_H_ 6 | #define PMC_DEBUG_UTILS_H_ 7 | 8 | template 9 | constexpr void discard(Args&&... /*args*/) {} 10 | 11 | #ifdef PMC_ENABLE_DEBUG 12 | #define DEBUG_PRINTF(...) printf(__VA_ARGS__) 13 | #else 14 | #define DEBUG_PRINTF(...) discard(__VA_ARGS__) 15 | #endif 16 | 17 | #endif //PMC_DEBUG_UTILS_H_ 18 | -------------------------------------------------------------------------------- /include/pmc/pmc_graph.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_GRAPH_H_ 21 | #define PMC_GRAPH_H_ 22 | 23 | #include "pmc/pmc_bool_vector.h" 24 | #include "pmc_vertex.h" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | namespace pmc { 31 | class pmc_graph { 32 | private: 33 | // helper functions 34 | void read_mtx(const std::string& filename); 35 | void read_edges(const std::string& filename); 36 | void read_metis(const std::string& /*filename*/) {} 37 | 38 | public: 39 | std::vector edges; 40 | std::vector vertices; 41 | std::vector degree; 42 | int min_degree; 43 | int max_degree; 44 | double avg_degree; 45 | bool is_gstats; 46 | std::string fn; 47 | std::vector adj; 48 | 49 | // constructor 50 | pmc_graph(const std::string& filename); 51 | pmc_graph(bool graph_stats, const std::string& filename); 52 | pmc_graph(const std::string& filename, bool make_adj); 53 | pmc_graph(std::vector vs, std::vector es) { 54 | edges = std::move(es); 55 | vertices = std::move(vs); 56 | vertex_degrees(); 57 | } 58 | pmc_graph(long long nedges, const int *ei, const int *ej, int offset); 59 | pmc_graph(const std::map>& v_map); 60 | 61 | // destructor 62 | ~pmc_graph(); 63 | 64 | void read_graph(const std::string& filename); 65 | void create_adj(); 66 | void reduce_graph(const bool_vector& pruned); 67 | void reduce_graph( 68 | std::vector& vs, 69 | std::vector& es, 70 | const bool_vector& pruned); 71 | 72 | int num_vertices() const noexcept { return vertices.size() - 1; } 73 | int num_edges() const noexcept { return edges.size()/2; } 74 | const std::vector & get_vertices() const noexcept { return vertices; } 75 | const std::vector& get_edges() const noexcept { return edges; } 76 | std::vector* get_degree(){ return °ree; } 77 | std::vector get_edges_array() { return edges; } 78 | std::vector get_vertices_array() { return vertices; }; 79 | std::vector e_v, e_u, eid; 80 | 81 | int vertex_degree(int v) const noexcept { return vertices[v] - vertices[v+1]; } 82 | long long first_neigh(int v) const noexcept { return vertices[v]; } 83 | long long last_neigh(int v) const noexcept { return vertices[v+1]; } 84 | 85 | void sum_vertex_degrees(); 86 | void vertex_degrees(); 87 | void update_degrees(); 88 | void update_degrees(bool flag); 89 | void update_degrees(bool_vector& pruned, int& mc); 90 | double density() const noexcept { return (double)num_edges() / (num_vertices() * (num_vertices() - 1.0) / 2.0); } 91 | int get_max_degree() const noexcept { return max_degree; } 92 | int get_min_degree() const noexcept { return min_degree; } 93 | double get_avg_degree() const noexcept { return avg_degree; } 94 | 95 | void initialize(); 96 | std::string get_file_extension(const std::string& filename); 97 | void basic_stats(double sec); 98 | void bound_stats(int alg); 99 | 100 | // vertex sorter 101 | void compute_ordering(std::vector& bound, std::vector& order); 102 | void compute_ordering(std::string degree, std::vector& order); 103 | // edge sorters 104 | void degree_bucket_sort(); 105 | void degree_bucket_sort(bool desc); 106 | 107 | int max_core; 108 | std::vector kcore; 109 | std::vector kcore_order; 110 | std::vector* get_kcores() { return &kcore; } 111 | std::vector* get_kcore_ordering() { return &kcore_order; } 112 | int get_max_core() const noexcept { return max_core; } 113 | void update_kcores(const bool_vector& pruned); 114 | 115 | void compute_cores(); 116 | void induced_cores_ordering( 117 | std::vector& V, 118 | std::vector& E); 119 | 120 | // clique utils 121 | int initial_pruning(pmc_graph& G, bool_vector& pruned, int lb); 122 | int initial_pruning(pmc_graph& G, bool_vector& pruned, int lb, std::vector& adj); 123 | void order_vertices(std::vector &V, pmc_graph &G, 124 | int &lb_idx, int &lb, std::string vertex_ordering, bool decr_order); 125 | 126 | void print_info(std::vector &C_max, double &sec); 127 | void print_break(); 128 | bool time_left(std::vector &C_max, double sec, 129 | double time_limit, bool &time_expired_msg); 130 | void graph_stats(pmc_graph& G, int& mc, int id, double &sec); 131 | 132 | void reduce_graph( 133 | std::vector& vs, 134 | std::vector& es, 135 | const bool_vector& pruned, 136 | pmc_graph& G); 137 | 138 | bool clique_test(pmc_graph& G, std::vector C); 139 | }; 140 | 141 | } 142 | #endif 143 | -------------------------------------------------------------------------------- /include/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 | #ifndef LINE_LENGTH 24 | #define LINE_LENGTH 256 25 | #endif 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /include/pmc/pmc_heu.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_HEU_H_ 21 | #define PMC_HEU_H_ 22 | 23 | #include "pmc/pmc_bool_vector.h" 24 | #include "pmc_graph.h" 25 | #include "pmc_input.h" 26 | #include "pmc_utils.h" 27 | #include "pmc_vertex.h" 28 | 29 | #include 30 | #include 31 | 32 | namespace pmc { 33 | 34 | class pmc_heu { 35 | public: 36 | std::vector const* E; 37 | std::vector const* V; 38 | std::vector* K; 39 | std::vector* order; 40 | std::vector* degree; 41 | double sec; 42 | int ub; 43 | std::string strat; 44 | 45 | int num_threads; 46 | 47 | pmc_heu(pmc_graph& G, input& params) { 48 | K = G.get_kcores(); 49 | order = G.get_kcore_ordering(); 50 | ub = params.ub; 51 | strat = params.heu_strat; 52 | num_threads = params.threads; 53 | initialize(); 54 | } 55 | 56 | pmc_heu(pmc_graph& G, int tmp_ub) { 57 | K = G.get_kcores(); 58 | order = G.get_kcore_ordering(); 59 | ub = tmp_ub; 60 | strat = "kcore"; 61 | initialize(); 62 | } 63 | 64 | inline void initialize() { 65 | sec = get_time(); 66 | srand (time(NULL)); 67 | }; 68 | 69 | int strategy(std::vector& P); 70 | void set_strategy(std::string s) { strat = s; } 71 | int compute_heuristic(int v); 72 | 73 | static bool desc_heur(Vertex v, Vertex u) { 74 | return (v.get_bound() > u.get_bound()); 75 | } 76 | 77 | static bool incr_heur(Vertex v, Vertex u) { 78 | return (v.get_bound() < u.get_bound()); 79 | } 80 | 81 | int search(const pmc_graph& graph, std::vector& C_max); 82 | int search_cores(const pmc_graph& graph, std::vector& C_max, int lb); 83 | int search_bounds(const pmc_graph& graph, std::vector& C_max); 84 | 85 | inline void branch(std::vector& P, int sz, 86 | int& mc, std::vector& C, bool_vector& ind); 87 | 88 | void print_info(const std::vector& C_max) const; 89 | }; 90 | }; 91 | #endif 92 | -------------------------------------------------------------------------------- /include/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_utils.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | namespace pmc { 31 | class input { 32 | public: 33 | // instance variables 34 | int algorithm; 35 | int threads; 36 | int experiment; 37 | int lb; 38 | int ub; 39 | int param_ub; 40 | int adj_limit; 41 | double time_limit; 42 | double remove_time; 43 | bool graph_stats; 44 | bool verbose; 45 | bool help; 46 | bool MCE; 47 | bool decreasing_order; 48 | std::string heu_strat; 49 | std::string format; 50 | std::string graph; 51 | std::string output; 52 | std::string edge_sorter; 53 | std::string vertex_search_order; 54 | 55 | input() { 56 | // default values 57 | algorithm = 0; 58 | threads = omp_get_max_threads(); 59 | experiment = 0; 60 | lb = 0; 61 | ub = 0; 62 | param_ub = 0; 63 | adj_limit = 20000; 64 | time_limit = 60 * 60; // max time to search 65 | remove_time = 4.0; // time to wait before reducing graph 66 | verbose = false; 67 | graph_stats = false; 68 | help = false; 69 | MCE = false; 70 | decreasing_order = false; 71 | heu_strat = "kcore"; 72 | vertex_search_order = "deg"; 73 | format = "mtx"; 74 | graph = "data/sample.mtx"; 75 | output = ""; 76 | std::string edge_sorter = ""; 77 | 78 | // both off, use default alg 79 | if (heu_strat == "0" && algorithm == -1) 80 | algorithm = 0; 81 | 82 | if (threads <= 0) threads = 1; 83 | } 84 | 85 | input(int argc, char *argv[]) { 86 | // default values 87 | algorithm = 0; 88 | threads = omp_get_max_threads(); 89 | experiment = 0; 90 | lb = 0; 91 | ub = 0; 92 | param_ub = 0; 93 | adj_limit = 20000; 94 | time_limit = 60 * 60; // max time to search 95 | remove_time = 4.0; // time to wait before reducing graph 96 | verbose = false; 97 | graph_stats = false; 98 | help = false; 99 | MCE = false; 100 | decreasing_order = false; 101 | heu_strat = "kcore"; 102 | vertex_search_order = "deg"; 103 | format = "mtx"; 104 | graph = "data/sample.mtx"; 105 | output = ""; 106 | std::string edge_sorter = ""; 107 | 108 | int opt; 109 | while ((opt=getopt(argc,argv,"i:t:f:u:l:o:e:a:r:w:h:k:dgsv")) != EOF) { 110 | switch (opt) { 111 | case 'a': 112 | algorithm = atoi(optarg); 113 | if (algorithm > 9) MCE = true; 114 | break; 115 | case 't': 116 | threads = atoi(optarg); 117 | break; 118 | case 'f': 119 | graph = optarg; 120 | break; 121 | case 's': 122 | graph_stats = true; 123 | break; 124 | case 'u': 125 | param_ub = atoi(optarg); // find k-clique fast 126 | lb = 2; // skip heuristic 127 | break; 128 | case 'k': 129 | param_ub = atoi(optarg); 130 | lb = param_ub-1; 131 | break; 132 | case 'l': 133 | lb = atoi(optarg); 134 | break; 135 | case 'h': 136 | heu_strat = optarg; 137 | break; 138 | case 'v': 139 | verbose = 1; 140 | break; 141 | case 'w': 142 | time_limit = atof(optarg) * 60; // convert minutes to seconds 143 | break; 144 | case 'r': 145 | remove_time = atof(optarg); 146 | break; 147 | case 'e': 148 | edge_sorter = optarg; 149 | break; 150 | case 'o': 151 | vertex_search_order = optarg; 152 | break; 153 | case 'd': 154 | // direction of which vertices are ordered 155 | decreasing_order = true; 156 | break; 157 | case '?': 158 | usage(argv[0]); 159 | break; 160 | default: 161 | usage(argv[0]); 162 | break; 163 | } 164 | } 165 | 166 | // both off, use default alg 167 | if (heu_strat == "0" && algorithm == -1) 168 | algorithm = 0; 169 | 170 | if (threads <= 0) threads = 1; 171 | 172 | if (!fexists(graph.c_str())) { 173 | usage(argv[0]); 174 | exit(-1); 175 | } 176 | 177 | FILE* fin = fopen(graph.c_str(), "r+t"); 178 | if (fin == NULL) { 179 | usage(argv[0]); 180 | exit(-1); 181 | } 182 | fclose(fin); 183 | 184 | std::cout << "\n\nFile Name ------------------------ " << graph.c_str() << std::endl; 185 | if (!fexists(graph.c_str()) ) { 186 | std::cout << "File not found!" << std::endl; 187 | return; 188 | } 189 | std::cout << "workers: " << threads < 29 | #include 30 | 31 | namespace pmc { 32 | 33 | class pmc_maxclique { 34 | public: 35 | std::vector const* edges; 36 | std::vector const* vertices; 37 | std::vector* bound; 38 | std::vector* order; 39 | std::vector* degree; 40 | int param_ub; 41 | int ub; 42 | int lb; 43 | double time_limit; 44 | double sec; 45 | double wait_time; 46 | bool not_reached_ub; 47 | bool time_expired_msg; 48 | bool decr_order; 49 | 50 | std::string vertex_ordering; 51 | int edge_ordering; 52 | int style_bounds; 53 | int style_dynamic_bounds; 54 | 55 | int num_threads; 56 | 57 | void initialize() { 58 | vertex_ordering = "kcore"; 59 | edge_ordering = 0; 60 | style_bounds = 0; 61 | style_dynamic_bounds = 0; 62 | not_reached_ub = true; 63 | time_expired_msg = true; 64 | decr_order = false; 65 | } 66 | 67 | void setup_bounds(input& params) { 68 | lb = params.lb; 69 | ub = params.ub; 70 | param_ub = params.param_ub; 71 | if (param_ub == 0) 72 | param_ub = ub; 73 | time_limit = params.time_limit; 74 | wait_time = params.remove_time; 75 | sec = get_time(); 76 | 77 | num_threads = params.threads; 78 | } 79 | 80 | 81 | pmc_maxclique(pmc_graph& G, input& params) { 82 | bound = G.get_kcores(); 83 | order = G.get_kcore_ordering(); 84 | setup_bounds(params); 85 | initialize(); 86 | vertex_ordering = params.vertex_search_order; 87 | decr_order = params.decreasing_order; 88 | } 89 | 90 | ~pmc_maxclique() {}; 91 | 92 | int search(pmc_graph& G, std::vector& sol); 93 | 94 | void branch( 95 | std::vector &P, 96 | std::vector& ind, 97 | std::vector& C, 98 | std::vector& C_max, 99 | const bool_vector& pruned, 100 | int& mc); 101 | 102 | 103 | int search_dense(pmc_graph& G, std::vector& sol); 104 | 105 | void branch_dense( 106 | std::vector &P, 107 | std::vector& ind, 108 | std::vector& C, 109 | std::vector& C_max, 110 | bool_vector& pruned, 111 | int& mc, 112 | std::vector& adj); 113 | 114 | }; 115 | }; 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /include/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/pmc_bool_vector.h" 24 | #include "pmc_vertex.h" 25 | 26 | #include 27 | 28 | namespace pmc { 29 | 30 | // sequential dynamic greedy coloring and sort 31 | static void neigh_coloring_bound( 32 | std::vector& vs, 33 | std::vector& es, 34 | std::vector &P, 35 | std::vector& ind, 36 | std::vector& C, 37 | std::vector< std::vector >& colors, 38 | int& mc) { 39 | 40 | int j = 0, u = 0, k = 1, k_prev = 0; 41 | int max_k = 1; 42 | int min_k = mc - C.size() + 1; 43 | colors[1].clear(); colors[2].clear(); 44 | 45 | for (int w=0; w < P.size(); w++) { 46 | u = P[w].get_id(); 47 | k = 1, k_prev = 0; 48 | 49 | for (long long h = vs[u]; h < vs[u + 1]; h++) ind[es[h]] = 1; 50 | 51 | while (k > k_prev) { 52 | k_prev = k; 53 | for (int i = 0; i < colors[k].size(); i++) { 54 | if (ind[colors[k][i]]) { 55 | k++; 56 | break; 57 | } 58 | } 59 | } 60 | 61 | for (long long h = vs[u]; h < vs[u + 1]; h++) ind[es[h]] = 0; 62 | 63 | if (k > max_k) { 64 | max_k = k; 65 | colors[max_k+1].clear(); 66 | } 67 | 68 | colors[k].push_back(u); 69 | if (k < min_k) { 70 | P[j].set_id(u); 71 | j++; 72 | } 73 | } 74 | 75 | if (j > 0) P[j-1].set_bound(0); 76 | if (min_k <= 0) min_k = 1; 77 | 78 | for (k = min_k; k <= max_k; k++) 79 | for (int w = 0; w < colors[k].size(); w++) { 80 | P[j].set_id(colors[k][w]); 81 | P[j].set_bound(k); 82 | j++; 83 | } 84 | } 85 | 86 | // sequential dynamic greedy coloring and sort 87 | static void neigh_coloring_dense( 88 | std::vector &P, 89 | std::vector& C, 90 | std::vector< std::vector >& colors, 91 | int& mc, 92 | const std::vector& adj) { 93 | 94 | int j = 0, u = 0, k = 1, k_prev = 0; 95 | int max_k = 1; 96 | int min_k = mc - C.size() + 1; 97 | 98 | colors[1].clear(); colors[2].clear(); 99 | 100 | for (int w=0; w < P.size(); w++) { 101 | u = P[w].get_id(); 102 | k = 1, k_prev = 0; 103 | 104 | while (k > k_prev) { 105 | k_prev = k; 106 | for (int i = 0; i < colors[k].size(); i++) { //use directly, sort makes it fast! 107 | if (adj[u][colors[k][i]]) { 108 | k++; 109 | break; 110 | } 111 | } 112 | } 113 | 114 | if (k > max_k) { 115 | max_k = k; 116 | colors[max_k+1].clear(); 117 | } 118 | 119 | colors[k].push_back(u); 120 | if (k < min_k) { 121 | P[j].set_id(u); 122 | j++; 123 | } 124 | } 125 | 126 | if (j > 0) P[j-1].set_bound(0); 127 | if (min_k <= 0) min_k = 1; 128 | 129 | for (k = min_k; k <= max_k; k++) 130 | for (int w = 0; w < colors[k].size(); w++) { 131 | P[j].set_id(colors[k][w]); 132 | P[j].set_bound(k); 133 | j++; 134 | } 135 | } 136 | } 137 | #endif 138 | -------------------------------------------------------------------------------- /include/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 | #include 26 | 27 | namespace pmc { 28 | 29 | static void neigh_cores_bound( 30 | std::vector& vs, 31 | std::vector& es, 32 | std::vector &P, 33 | std::vector& ind, 34 | int& mc) { 35 | 36 | int n = P.size() + 1; 37 | 38 | // lookup table 39 | std::vector newids_to_actual(n, 0); 40 | std::vector vert_order(n,0); 41 | std::vector deg(n,0); 42 | std::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 | std::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 | std::vector& vs, 135 | std::vector& es, 136 | std::vector &P, 137 | std::vector& ind, 138 | int& mc) { 139 | 140 | int n = P.size() + 1; 141 | 142 | // lookup table 143 | std::vector newids_to_actual(n, 0); 144 | std::vector vert_order(n,0); 145 | std::vector deg(n,0); 146 | std::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 | std::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 | -------------------------------------------------------------------------------- /include/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 | 27 | bool fexists(const char *filename); 28 | void usage(char *argv0); 29 | 30 | double get_time(); 31 | std::string memory_usage(); 32 | 33 | void validate(bool condition, const std::string &msg); 34 | 35 | void indent(int level); 36 | void indent(int level, std::string str); 37 | void print_max_clique(std::vector &max_clique_data); 38 | void print_n_maxcliques(std::set> C, int n); 39 | 40 | int getdir(std::string dir, std::vector &files); 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /include/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 | #include "pmc/pmc_utils.h" 24 | #include "pmc_debug_utils.h" 25 | 26 | #include 27 | #include 28 | 29 | namespace pmc { 30 | class Vertex { 31 | private: 32 | int id, b; 33 | public: 34 | Vertex(int vertex_id, int bound): id(vertex_id), b(bound) {} 35 | 36 | void set_id(int vid) noexcept { id = vid; } 37 | int get_id() const noexcept { return id; } 38 | 39 | void set_bound(int value) noexcept { b = value; } 40 | int get_bound() const noexcept { return b; } 41 | }; 42 | 43 | static bool decr_bound(Vertex v, Vertex u) { 44 | return (v.get_bound() > u.get_bound()); 45 | } 46 | static bool incr_bound(Vertex v, Vertex u) { 47 | return (v.get_bound() < u.get_bound()); 48 | }; 49 | 50 | inline static void print_mc_info(const std::vector& C_max, double sec) { 51 | DEBUG_PRINTF("*** [pmc: thread %i", omp_get_thread_num() + 1); 52 | DEBUG_PRINTF("] current max clique = %i", C_max.size()); 53 | DEBUG_PRINTF(", time = %i sec\n", get_time() - sec); 54 | }; 55 | }; 56 | #endif 57 | -------------------------------------------------------------------------------- /include/pmc/pmcx_maxclique.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 PMCX_MAXCLIQUE_H_ 21 | #define PMCX_MAXCLIQUE_H_ 22 | 23 | #include "pmc_graph.h" 24 | #include "pmc_input.h" 25 | #include "pmc_utils.h" 26 | #include "pmc_vertex.h" 27 | 28 | #include 29 | #include 30 | 31 | namespace pmc { 32 | 33 | class pmcx_maxclique { 34 | public: 35 | std::vector* bound; 36 | std::vector* order; 37 | int param_ub; 38 | int ub; 39 | int lb; 40 | double time_limit; 41 | double sec; 42 | double wait_time; 43 | bool not_reached_ub; 44 | bool time_expired_msg; 45 | bool decr_order; 46 | 47 | std::string vertex_ordering; 48 | int edge_ordering; 49 | int style_bounds; 50 | int style_dynamic_bounds; 51 | 52 | int num_threads; 53 | 54 | void initialize() { 55 | vertex_ordering = "deg"; 56 | edge_ordering = 0; 57 | style_bounds = 0; 58 | style_dynamic_bounds = 0; 59 | not_reached_ub = true; 60 | time_expired_msg = true; 61 | decr_order = false; 62 | } 63 | 64 | void setup_bounds(input& params) { 65 | lb = params.lb; 66 | ub = params.ub; 67 | param_ub = params.param_ub; 68 | if (param_ub == 0) 69 | param_ub = ub; 70 | time_limit = params.time_limit; 71 | wait_time = params.remove_time; 72 | sec = get_time(); 73 | 74 | num_threads = params.threads; 75 | } 76 | 77 | pmcx_maxclique(pmc_graph& G, input& params) { 78 | bound = G.get_kcores(); 79 | order = G.get_kcore_ordering(); 80 | setup_bounds(params); 81 | initialize(); 82 | vertex_ordering = params.vertex_search_order; 83 | decr_order = params.decreasing_order; 84 | } 85 | 86 | ~pmcx_maxclique() {}; 87 | 88 | int search(pmc_graph& G, std::vector& sol); 89 | inline void branch( 90 | std::vector& vs, 91 | std::vector& es, 92 | std::vector &P, 93 | std::vector& ind, 94 | std::vector& C, 95 | std::vector& C_max, 96 | std::vector< std::vector >& colors, 97 | const bool_vector& pruned, 98 | int& mc); 99 | 100 | int search_dense(pmc_graph& G, std::vector& sol); 101 | inline void branch_dense( 102 | std::vector& vs, 103 | std::vector& es, 104 | std::vector &P, 105 | std::vector& ind, 106 | std::vector& C, 107 | std::vector& C_max, 108 | std::vector< std::vector >& colors, 109 | const bool_vector& pruned, 110 | int& mc, 111 | const std::vector& adj); 112 | 113 | }; 114 | }; 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /include/pmc/pmcx_maxclique_basic.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 PMCX_MAXCLIQUE_BASIC_H_ 21 | #define PMCX_MAXCLIQUE_BASIC_H_ 22 | 23 | #include "pmc_graph.h" 24 | #include "pmc_input.h" 25 | #include "pmc_utils.h" 26 | #include "pmc_vertex.h" 27 | 28 | #include 29 | #include 30 | 31 | namespace pmc { 32 | 33 | class pmcx_maxclique_basic { 34 | public: 35 | std::vector* bound; 36 | std::vector* order; 37 | std::vector* degree; 38 | int param_ub; 39 | int ub; 40 | int lb; 41 | double time_limit; 42 | double sec; 43 | double wait_time; 44 | bool not_reached_ub; 45 | bool time_expired_msg; 46 | bool decr_order; 47 | 48 | std::string vertex_ordering; 49 | int edge_ordering; 50 | int style_bounds; 51 | int style_dynamic_bounds; 52 | 53 | int num_threads; 54 | 55 | void initialize() { 56 | vertex_ordering = "deg"; 57 | edge_ordering = 0; 58 | style_bounds = 0; 59 | style_dynamic_bounds = 0; 60 | not_reached_ub = true; 61 | time_expired_msg = true; 62 | decr_order = false; 63 | } 64 | 65 | void setup_bounds(input& params) { 66 | lb = params.lb; 67 | ub = params.ub; 68 | param_ub = params.param_ub; 69 | if (param_ub == 0) 70 | param_ub = ub; 71 | time_limit = params.time_limit; 72 | wait_time = params.remove_time; 73 | sec = get_time(); 74 | 75 | num_threads = params.threads; 76 | } 77 | 78 | 79 | pmcx_maxclique_basic(pmc_graph& G, input& params) { 80 | bound = G.get_kcores(); 81 | order = G.get_kcore_ordering(); 82 | setup_bounds(params); 83 | initialize(); 84 | vertex_ordering = params.vertex_search_order; 85 | decr_order = params.decreasing_order; 86 | } 87 | 88 | ~pmcx_maxclique_basic() {}; 89 | 90 | int search(pmc_graph& G, std::vector& sol); 91 | 92 | void branch( 93 | std::vector& vs, 94 | std::vector& es, 95 | std::vector &P, 96 | std::vector& ind, 97 | std::vector& C, 98 | std::vector& C_max, 99 | std::vector< std::vector >& colors, 100 | const bool_vector& pruned, 101 | int& mc); 102 | 103 | 104 | int search_dense(pmc_graph& G, std::vector& sol); 105 | 106 | void branch_dense( 107 | std::vector& vs, 108 | std::vector& es, 109 | std::vector &P, 110 | std::vector& ind, 111 | std::vector& C, 112 | std::vector& C_max, 113 | std::vector< std::vector >& colors, 114 | bool_vector& pruned, 115 | int& mc, 116 | std::vector& adj); 117 | 118 | }; 119 | }; 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /libpmc.h: -------------------------------------------------------------------------------- 1 | int max_clique(long long nedges, int *ei, int *ej, int index_offset, 2 | int outsize, int *clique); 3 | -------------------------------------------------------------------------------- /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.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.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.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_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/pmc_debug_utils.h" 21 | #include "pmc/pmc_graph.h" 22 | #include "pmc/pmc_utils.h" 23 | 24 | #include 25 | #include 26 | 27 | using namespace std; 28 | using namespace pmc; 29 | 30 | int pmc_graph::initial_pruning(pmc_graph& G, bool_vector& pruned, int lb) { 31 | int lb_idx = 0; 32 | for (int i = G.num_vertices()-1; i >= 0; i--) { 33 | if (kcore[kcore_order[i]] == lb) lb_idx = i; 34 | if (kcore[kcore_order[i]] <= lb) pruned[kcore_order[i]] = 1; 35 | } 36 | 37 | double sec = get_time(); 38 | DEBUG_PRINTF("[pmc: initial k-core pruning] before pruning: |V| = %i, |E| = %i\n", G.num_vertices(), G.num_edges()); 39 | G.reduce_graph(pruned); 40 | DEBUG_PRINTF("[pmc: initial k-core pruning] after pruning: |V| = %i, |E| = %i\n", G.num_vertices() - lb_idx, G.num_edges()); 41 | DEBUG_PRINTF("[pmc] initial pruning took %i sec\n", get_time()-sec); 42 | 43 | G.update_degrees(); 44 | G.degree_bucket_sort(true); // largest to smallest degree 45 | 46 | return lb_idx; 47 | } 48 | 49 | 50 | int pmc_graph::initial_pruning(pmc_graph& G, bool_vector& pruned, int lb, std::vector& adj) { 51 | int lb_idx = 0; 52 | for (int i = G.num_vertices()-1; i >= 0; i--) { 53 | if (kcore[kcore_order[i]] == lb) lb_idx = i; 54 | if (kcore[kcore_order[i]] <= lb) { 55 | pruned[kcore_order[i]] = 1; 56 | for (long long j = vertices[kcore_order[i]]; j < vertices[kcore_order[i] + 1]; j++) { 57 | adj[kcore_order[i]][edges[j]] = false; 58 | adj[edges[j]][kcore_order[i]] = false; 59 | } 60 | } 61 | } 62 | 63 | double sec = get_time(); 64 | DEBUG_PRINTF("[pmc: initial k-core pruning] before pruning: |V| = %i, |E| = %i\n", G.num_vertices(), G.num_edges()); 65 | G.reduce_graph(pruned); 66 | DEBUG_PRINTF("[pmc: initial k-core pruning] after pruning: |V| = %i, |E| = %i\n", G.num_vertices() - lb_idx, G.num_edges()); 67 | DEBUG_PRINTF("[pmc] initial pruning took %i sec\n", get_time()-sec); 68 | 69 | G.update_degrees(); 70 | G.degree_bucket_sort(true); 71 | 72 | return lb_idx; 73 | } 74 | 75 | 76 | void pmc_graph::order_vertices(vector &V, pmc_graph &G, 77 | int &lb_idx, int &lb, string vertex_ordering, bool decr_order) { 78 | 79 | srand (time(NULL)); 80 | int u = 0, val = 0; 81 | for (int k = lb_idx; k < G.num_vertices(); k++) { 82 | if (degree[kcore_order[k]] >= lb - 1) { 83 | u = kcore_order[k]; 84 | 85 | if (vertex_ordering == "deg") 86 | val = vertices[u + 1] - vertices[u]; 87 | else if (vertex_ordering == "kcore") 88 | val = kcore[u]; 89 | else if (vertex_ordering == "kcore_deg") 90 | val = degree[u] * kcore[u]; 91 | else if (vertex_ordering == "rand") 92 | val = rand() % vertices.size(); 93 | // neighbor degrees 94 | else if (vertex_ordering == "dual_deg") { 95 | val = 0; 96 | for (long long j = vertices[u]; j < vertices[u + 1]; j++) { 97 | val = val + G.vertex_degree(edges[j]); 98 | } 99 | } 100 | // neighbor degrees 101 | else if (vertex_ordering == "dual_kcore") { 102 | val = 0; 103 | for (long long j = vertices[u]; j < vertices[u + 1]; j++) { 104 | val = val + kcore[edges[j]]; 105 | } 106 | } 107 | else val = vertices[u + 1] - vertices[u]; 108 | V.push_back(Vertex(u,val)); 109 | } 110 | } 111 | if (decr_order) 112 | std::sort(V.begin(), V.end(), decr_bound); 113 | else 114 | std::sort(V.begin(), V.end(), incr_bound); 115 | } 116 | 117 | 118 | /** 119 | * Reduce the graph by removing the pruned vertices 120 | * + Systematically speeds algorithm up by reducing the neighbors as more vertices are searched 121 | * 122 | * The algorithm below is for parallel maximum clique finders and has the following features: 123 | * + Thread-safe, since local copy of vertices/edges are passed in.. 124 | * + Pruned is a shared variable, but it is safe, since only reads/writes can occur, no deletion 125 | */ 126 | void pmc_graph::reduce_graph( 127 | vector& vs, 128 | vector& es, 129 | const bool_vector& pruned, 130 | pmc_graph& G) { 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 | DEBUG_PRINTF("\n### Time limit expired, terminating search. ###\n"); 179 | DEBUG_PRINTF("Size: %i\n", C_max.size()); 180 | print_max_clique(C_max); 181 | time_expired_msg = false; 182 | } 183 | return false; 184 | } 185 | return true; 186 | } 187 | 188 | void pmc_graph::graph_stats(pmc_graph& G, int& mc, int id, double &sec) { 189 | cout << "[pmc: bounds updated - thread " << omp_get_thread_num() + 1 << "] "; 190 | cout << "time = " << get_time() - sec << " sec, "; 191 | cout << "|V| = " << (G.num_vertices() - id); 192 | cout << " (" << id << " / " << G.num_vertices(); 193 | cout << "), |E| = " << G.num_edges(); 194 | cout << ", w = " << mc; 195 | cout << ", p = " << G.density(); 196 | cout << ", d_min = " << G.get_min_degree(); 197 | cout << ", d_avg = " << G.get_avg_degree(); 198 | cout << ", d_max = " << G.get_max_degree(); 199 | cout << ", k_max = " << G.get_max_core(); 200 | cout <& V, 27 | vector& E) { 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_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/pmc.h" 21 | #include "pmc/pmc_input.h" 22 | 23 | using namespace std; 24 | using namespace pmc; 25 | 26 | int main(int argc, char *argv[]) { 27 | 28 | //! parse command args 29 | input in(argc, argv); 30 | if (in.help) { 31 | usage(argv[0]); 32 | return 0; 33 | } 34 | 35 | //! read graph 36 | pmc_graph G(in.graph_stats,in.graph); 37 | if (in.graph_stats) { G.bound_stats(in.algorithm); } 38 | 39 | //! ensure wait time is greater than the time to recompute the graph data structures 40 | if (G.num_edges() > 1000000000 && in.remove_time < 120) in.remove_time = 120; 41 | else if (G.num_edges() > 250000000 && in.remove_time < 10) in.remove_time = 10; 42 | cout << "explicit reduce is set to " << in.remove_time << " seconds" < C; 55 | if (in.lb == 0 && in.heu_strat != "0") { // skip if given as input 56 | pmc_heu maxclique(G,in); 57 | in.lb = maxclique.search(G, C); 58 | cout << "Heuristic found clique of size " << in.lb; 59 | cout << " in " << get_time() - seconds << " seconds" <= 0) { 69 | switch(in.algorithm) { 70 | case 0: { 71 | //! k-core pruning, neigh-core pruning/ordering, dynamic coloring bounds/sort 72 | if (G.num_vertices() < in.adj_limit) { 73 | G.create_adj(); 74 | pmcx_maxclique finder(G,in); 75 | finder.search_dense(G,C); 76 | break; 77 | } 78 | else { 79 | pmcx_maxclique finder(G,in); 80 | finder.search(G,C); 81 | break; 82 | } 83 | } 84 | case 1: { 85 | //! k-core pruning, dynamic coloring bounds/sort 86 | if (G.num_vertices() < in.adj_limit) { 87 | G.create_adj(); 88 | pmcx_maxclique_basic finder(G,in); 89 | finder.search_dense(G,C); 90 | break; 91 | } 92 | else { 93 | pmcx_maxclique_basic finder(G,in); 94 | finder.search(G,C); 95 | break; 96 | } 97 | } 98 | case 2: { 99 | //! simple k-core pruning (four new pruning steps) 100 | pmc_maxclique finder(G,in); 101 | finder.search(G,C); 102 | break; 103 | } 104 | default: 105 | cout << "algorithm " << in.algorithm << " not found." < 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | using namespace pmc; 32 | using namespace std; 33 | 34 | 35 | void pmc_graph::initialize() { 36 | max_degree = 0; 37 | min_degree = 0; 38 | avg_degree = 0; 39 | max_core = 0; 40 | is_gstats = false; 41 | } 42 | 43 | pmc_graph::~pmc_graph() { 44 | } 45 | 46 | pmc_graph::pmc_graph(const string& filename) { 47 | initialize(); 48 | fn = filename; 49 | read_graph(filename); 50 | } 51 | 52 | pmc_graph::pmc_graph(bool graph_stats, const string& filename) { 53 | initialize(); 54 | fn = filename; 55 | is_gstats = graph_stats; 56 | read_graph(filename); 57 | } 58 | 59 | pmc_graph::pmc_graph(const string& filename, bool make_adj) { 60 | initialize(); 61 | fn = filename; 62 | read_graph(filename); 63 | if (make_adj) create_adj(); 64 | } 65 | 66 | void pmc_graph::read_graph(const string& filename) { 67 | fn = filename; 68 | double sec = get_time(); 69 | string ext = get_file_extension(filename); 70 | 71 | if (ext == "edges" || ext == "eg2" || ext == "txt") 72 | read_edges(filename); 73 | else if (ext == "mtx") 74 | read_mtx(filename); 75 | else if (ext == "gr") 76 | read_metis(filename); 77 | else { 78 | cout << "Unsupported graph format." < > vert_list; 98 | int v = 0, u = 0, self_edges = 0; 99 | 100 | ifstream in_check (filename.c_str()); 101 | if (!in_check) { cout << filename << "File not found!" <> v >> u; 111 | if (v == 0 || u == 0) { 112 | fix_start_idx = false; 113 | break; 114 | } 115 | } 116 | } 117 | ifstream in (filename.c_str()); 118 | if (!in) { cout << filename << "File not found!" <> v >> u; 127 | 128 | if (fix_start_idx) { 129 | v--; 130 | u--; 131 | } 132 | if (v == u) self_edges++; 133 | else { 134 | vert_list[v].push_back(u); 135 | vert_list[u].push_back(v); 136 | } 137 | } 138 | } 139 | vertices.push_back(edges.size()); 140 | for (int i=0; i < vert_list.size(); i++) { 141 | edges.insert(edges.end(),vert_list[i].begin(),vert_list[i].end()); 142 | vertices.push_back(edges.size()); 143 | } 144 | vert_list.clear(); 145 | vertex_degrees(); 146 | cout << "self-loops: " << self_edges < > vert_list; 152 | for (long long i = 0; i < nedges; i++) { 153 | int v = ei[i] - offset; 154 | int u = ej[i] - offset; 155 | if ( v > u ) { 156 | vert_list[v].push_back(u); 157 | vert_list[u].push_back(v); 158 | } 159 | } 160 | vertices.push_back(edges.size()); 161 | for (int i=0; i < vert_list.size(); i++) { 162 | edges.insert(edges.end(),vert_list[i].begin(),vert_list[i].end()); 163 | vertices.push_back(edges.size()); 164 | } 165 | vert_list.clear(); 166 | vertex_degrees(); 167 | } 168 | 169 | pmc_graph::pmc_graph(const map>& v_map) { 170 | vertices.push_back(edges.size()); 171 | for (const auto& v_pair : v_map) { 172 | edges.insert(edges.end(), v_pair.second.begin(), v_pair.second.end()); 173 | vertices.push_back(edges.size()); 174 | } 175 | vertex_degrees(); 176 | } 177 | 178 | void pmc_graph::read_mtx(const string& filename) { 179 | float connStrength = std::numeric_limits::lowest(); 180 | istringstream in2; 181 | string line=""; 182 | map > v_map; 183 | map > valueList; 184 | int col=0, row=0, ridx=0, cidx=0; 185 | int entry_counter = 0, num_of_entries = 0; 186 | double value; 187 | 188 | ifstream in (filename.c_str()); 189 | if(!in) { 190 | cout<0&&line[0]=='%') getline(in,line); 216 | in2.str(line); 217 | in2 >> row >> col >> num_of_entries; 218 | 219 | if(row!=col) { 220 | cout<<"* ERROR: This is not a square matrix."<> ridx >> cidx >> value; 232 | ridx--; 233 | cidx--; 234 | 235 | if (ridx < 0 || ridx >= row) cout << "sym-mtx error: " << ridx << " row " << row << endl; 236 | if (cidx < 0 || cidx >= col) cout << "sym-mtx error: " << cidx << " col " << col << endl; 237 | if (ridx == cidx) continue; 238 | 239 | if (ridx > cidx) { 240 | if (b_getValue) { 241 | if(value > connStrength) { 242 | v_map[ridx].push_back(cidx); 243 | v_map[cidx].push_back(ridx); 244 | if (is_gstats) { 245 | e_v.push_back(ridx); 246 | e_u.push_back(cidx); 247 | } 248 | } 249 | } else { 250 | v_map[ridx].push_back(cidx); 251 | v_map[cidx].push_back(ridx); 252 | if (is_gstats) { 253 | e_v.push_back(ridx); 254 | e_u.push_back(cidx); 255 | } 256 | } 257 | 258 | if (b_getValue && value > connStrength) { 259 | valueList[ridx].push_back(value); 260 | valueList[cidx].push_back(value); 261 | } 262 | } else { 263 | cout << "* WARNING: Found a nonzero in the upper triangular. "; 264 | break; 265 | } 266 | } 267 | } 268 | vertices.push_back(edges.size()); 269 | for (int i=0;i < row; i++) { 270 | edges.insert(edges.end(),v_map[i].begin(),v_map[i].end()); 271 | vertices.push_back(edges.size()); 272 | } 273 | v_map.clear(); 274 | valueList.clear(); 275 | vertex_degrees(); 276 | } 277 | 278 | void pmc_graph::create_adj() { 279 | double sec = get_time(); 280 | 281 | int size = num_vertices(); 282 | adj.resize(size); 283 | for (int i = 0; i < size; i++) { 284 | adj[i].resize(size); 285 | } 286 | 287 | for (int i = 0; i < num_vertices(); i++) { 288 | for (long long j = vertices[i]; j < vertices[i + 1]; j++ ) 289 | adj[i][edges[j]] = true; 290 | } 291 | DEBUG_PRINTF("Created adjacency matrix in %i seconds\n", get_time() - sec); 292 | } 293 | 294 | 295 | void pmc_graph::sum_vertex_degrees() { 296 | int n = vertices.size() - 1; 297 | 298 | uint64_t sum = 0; 299 | for (long long v = 0; v < n; v++) { 300 | degree[v] = vertices[v+1] - vertices[v]; 301 | sum += (degree[v] * degree[v]-1) / 2; 302 | } 303 | cout << "sum of degrees: " << sum <(vertices.size()) - 1; 308 | degree.resize(n); 309 | 310 | // initialize min and max to degree of first vertex 311 | min_degree = static_cast(vertices[1]) - static_cast(vertices[0]); 312 | max_degree = static_cast(vertices[1]) - static_cast(vertices[0]); 313 | for (int v=0; v(vertices[v+1]) - static_cast(vertices[v]); 315 | if (max_degree < degree[v]) { 316 | max_degree = degree[v]; 317 | } 318 | if (degree[v] < min_degree) { 319 | min_degree = degree[v]; 320 | } 321 | } 322 | avg_degree = static_cast(edges.size())/ static_cast(n); 323 | return; 324 | } 325 | 326 | // fast update 327 | void pmc_graph::update_degrees() { 328 | for (long long v = 0; v < num_vertices(); v++) 329 | degree[v] = vertices[v+1] - vertices[v]; 330 | } 331 | 332 | void pmc_graph::update_degrees(bool /*flag*/) { 333 | 334 | int p = 0; 335 | max_degree = vertices[1] - vertices[0]; 336 | for (long long v = 0; v < num_vertices(); v++) { 337 | degree[v] = vertices[v+1] - vertices[v]; 338 | if (degree[v] > 0) { 339 | if (max_degree < degree[v]) max_degree = degree[v]; 340 | p++; 341 | } 342 | } 343 | avg_degree = (double)edges.size() / p; 344 | return; 345 | } 346 | 347 | 348 | void pmc_graph::update_degrees(bool_vector& pruned, int& mc) { 349 | max_degree = -1; 350 | min_degree = std::numeric_limits::max(); 351 | int p = 0; 352 | for (long long v=0; v < num_vertices(); v++) { 353 | degree[v] = vertices[v+1] - vertices[v]; 354 | if (degree[v] < mc) { 355 | if (!pruned[v]) pruned[v] = true; 356 | p++; 357 | } 358 | else { 359 | if (max_degree < degree[v]) max_degree = degree[v]; 360 | if (degree[v] < min_degree) min_degree = degree[v]; 361 | } 362 | } 363 | avg_degree = (double)edges.size() / p; 364 | cout << ", pruned: " << p << endl; 365 | } 366 | 367 | 368 | void pmc_graph::update_kcores(const bool_vector& pruned) { 369 | 370 | long long n, d, i, j, start, num, md; 371 | long long v, u, w, du, pu, pw, md_end; 372 | n = vertices.size(); 373 | kcore.resize(n); 374 | fill(kcore.begin(), kcore.end(), 0); 375 | vector pos_tmp(n); 376 | vector order_tmp(n); 377 | 378 | md = 0; 379 | for(v=1; v md) md = kcore[v]; 383 | } 384 | } 385 | 386 | md_end = md+1; 387 | vector < int > bin(md_end,0); 388 | 389 | for (v=1; v < n; v++) bin[kcore[v]]++; 390 | 391 | start = 1; 392 | for (d=0; d < md_end; d++) { 393 | num = bin[d]; 394 | bin[d] = start; 395 | start = start + num; 396 | } 397 | 398 | for (v=1; v 1; d--) bin[d] = bin[d-1]; 405 | bin[0] = 1; 406 | 407 | for (i = 1; i < n; i++) { 408 | v=order_tmp[i]; 409 | if (!pruned[v-1]) { 410 | for (j = vertices[v-1]; j < vertices[v]; j++) { 411 | if (!pruned[edges[j]]) { 412 | u = edges[j] + 1; 413 | if (kcore[u] > kcore[v]) { 414 | du = kcore[u]; pu = pos_tmp[u]; 415 | pw = bin[du]; w = order_tmp[pw]; 416 | if (u != w) { 417 | pos_tmp[u] = pw; order_tmp[pu] = w; 418 | pos_tmp[w] = pu; order_tmp[pw] = u; 419 | } 420 | bin[du]++; kcore[u]--; 421 | } 422 | } 423 | } 424 | } 425 | } 426 | 427 | max_core = 0; 428 | for (v=0; v max_core) max_core = kcore[v]; 433 | } 434 | else kcore[v] = 0; 435 | } 436 | DEBUG_PRINTF("[pmc: updated cores] K: %i\n", max_core); 437 | 438 | bin.clear(); 439 | pos_tmp.clear(); 440 | order_tmp.clear(); 441 | } 442 | 443 | 444 | 445 | string pmc_graph::get_file_extension(const string& filename) { 446 | string::size_type result; 447 | string fileExtension = ""; 448 | result = filename.rfind('.', filename.size() - 1); 449 | if(result != string::npos) 450 | fileExtension = filename.substr(result+1); 451 | return fileExtension; 452 | } 453 | 454 | 455 | 456 | void pmc_graph::reduce_graph(const bool_vector& pruned) { 457 | vector V(vertices.size(),0); 458 | vector E; 459 | E.reserve(edges.size()); 460 | 461 | int start = 0; 462 | for (int i = 0; i < num_vertices(); i++) { 463 | start = E.size(); 464 | if (!pruned[i]) { 465 | for (long long j = vertices[i]; j < vertices[i + 1]; j++ ) { 466 | if (!pruned[edges[j]]) 467 | E.push_back(edges[j]); 468 | } 469 | } 470 | V[i] = start; 471 | V[i + 1] = E.size(); 472 | } 473 | vertices = V; 474 | edges = E; 475 | } 476 | 477 | 478 | void pmc_graph::reduce_graph( 479 | vector& vs, 480 | vector& es, 481 | const bool_vector& pruned) { 482 | 483 | int num_vs = vs.size(); 484 | 485 | vector V(num_vs,0); 486 | vector E; 487 | E.reserve(es.size()); 488 | 489 | int start = 0; 490 | for (int i = 0; i < num_vs - 1; i++) { 491 | start = E.size(); 492 | if (!pruned[i]) { //skip these V_local... 493 | for (long long j = vs[i]; j < vs[i + 1]; j++ ) { 494 | if (!pruned[es[j]]) 495 | E.push_back(es[j]); 496 | } 497 | } 498 | V[i] = start; 499 | V[i + 1] = E.size(); 500 | } 501 | vs = V; 502 | es = E; 503 | } 504 | 505 | 506 | void pmc_graph::bound_stats(int alg) { 507 | cout << "graph: " << fn <& bound, vector& order) { 521 | long long n, d, start, num, md; 522 | long long v, md_end; 523 | 524 | n = bound.size(); 525 | order.reserve(n); 526 | vector < long long > pos(n); 527 | 528 | md = 0; 529 | for(v=1; v md) md = bound[v]; 531 | 532 | md_end = md+1; 533 | vector < long long > bin(md_end,0); 534 | 535 | for (v=1; v < n; v++) bin[bound[v]]++; 536 | 537 | start = 1; 538 | for (d=0; d < md_end; d++) { 539 | num = bin[d]; 540 | bin[d] = start; 541 | start = start + num; 542 | } 543 | 544 | for (v=1; v 1; d--) bin[d] = bin[d-1]; 551 | bin[0] = 1; 552 | 553 | for (v=0; v tmp_edges; 571 | tmp_edges.reserve(edges.size()); 572 | 573 | for (v = 0; v < num_vertices(); v++) { 574 | 575 | n = vertices[v+1] - vertices[v] + 1; 576 | vector vert(n); 577 | vector pos(n); 578 | vector deg(n); 579 | 580 | md = 0; 581 | for(u=1; u md) 584 | md = deg[u]; 585 | } 586 | 587 | md_end = md+1; 588 | vector < int > bin(md_end,0); 589 | 590 | for (u=1; u < n; u++) bin[deg[u]]++; 591 | 592 | start = 1; 593 | for (d=0; d < md_end; d++) { 594 | num = bin[d]; 595 | bin[d] = start; 596 | start = start + num; 597 | } 598 | 599 | for (u=1; u C) { 623 | int u = 0; 624 | vector ind(G.num_vertices(),0); 625 | for (size_t i = 0; i < C.size(); i++) ind[C[i]] = 1; 626 | 627 | 628 | // ensure each vertex in C has |C|-1 edges between each other 629 | for (size_t i = 0; i < C.size(); i++) { 630 | u = C[i]; 631 | int sz = 0; 632 | for (long long j = G.vertices[u]; j < G.vertices[u+1]; j++) 633 | if (ind[G.edges[j]]) sz++; 634 | 635 | // check if connected to |C|-1 vertices 636 | if (sz != C.size()-1) 637 | return false; 638 | } 639 | return true; 640 | } 641 | -------------------------------------------------------------------------------- /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/pmc_bool_vector.h" 21 | #include "pmc/pmc_debug_utils.h" 22 | #include "pmc/pmc_heu.h" 23 | 24 | #include 25 | 26 | using namespace pmc; 27 | 28 | 29 | void pmc_heu::branch(std::vector& P, int sz, 30 | int& mc, std::vector& C, bool_vector& ind) { 31 | 32 | if (!P.empty()) { 33 | 34 | const int u = P.back().get_id(); 35 | P.pop_back(); 36 | 37 | for (long long j = (*V)[u]; j < (*V)[u + 1]; j++) ind[(*E)[j]] = true; 38 | 39 | std::vector R; 40 | R.reserve(P.size()); 41 | 42 | std::copy_if(P.begin(), P.end(), std::back_inserter(R), 43 | [this, mc, &ind](const Vertex &v) -> bool { 44 | return ind[v.get_id()] && (*K)[v.get_id()] > mc; 45 | }); 46 | 47 | for (long long j = (*V)[u]; j < (*V)[u + 1]; j++) ind[(*E)[j]] = false; 48 | 49 | const int mc_prev = mc; 50 | branch(R, sz + 1, mc, C, ind); 51 | 52 | if (mc > mc_prev) C.push_back(u); 53 | 54 | P.clear(); 55 | } 56 | else if (sz > mc) 57 | mc = sz; 58 | return; 59 | } 60 | 61 | int pmc_heu::search_bounds(const pmc_graph& G, std::vector& C_max) { 62 | V = &G.get_vertices(); 63 | E = &G.get_edges(); 64 | 65 | std::vector C; 66 | std::vector P; 67 | 68 | C_max.reserve(ub); 69 | C.reserve(ub); 70 | P.reserve(G.get_max_degree()+1); 71 | 72 | bool_vector ind(G.num_vertices(), false); 73 | 74 | bool found_ub = false; 75 | int mc = 0; 76 | 77 | #pragma omp parallel for schedule(dynamic) \ 78 | shared(G, mc, C_max, found_ub) \ 79 | private(P, C) firstprivate(ind) \ 80 | num_threads(num_threads) 81 | for (int i = G.num_vertices()-1; i >= 0; --i) { 82 | bool found_ub_local = false; 83 | #pragma omp atomic read acquire 84 | found_ub_local = found_ub; 85 | 86 | if (found_ub_local) { 87 | continue; 88 | } 89 | 90 | const int v = (*order)[i]; 91 | 92 | int mc_prev, mc_cur; 93 | { 94 | #pragma omp critical 95 | mc_prev = mc_cur = mc; 96 | } 97 | 98 | if ((*K)[v] > mc_cur) { 99 | for (long long j = (*V)[v]; j < (*V)[v + 1]; j++) 100 | if ((*K)[(*E)[j]] > mc_cur) 101 | P.emplace_back((*E)[j], compute_heuristic((*E)[j])); 102 | 103 | if (P.size() > mc_cur) { 104 | std::sort(P.begin(), P.end(), incr_heur); 105 | branch(P, 1 , mc_cur, C, ind); 106 | 107 | if (mc_cur >= ub) { 108 | #pragma omp atomic write release 109 | found_ub = true; 110 | } 111 | 112 | if (mc_cur > mc_prev) { 113 | C.push_back(v); 114 | 115 | #pragma omp critical 116 | if (mc_cur > mc) { 117 | mc = mc_cur; 118 | 119 | std::swap(C_max, C); 120 | print_info(C_max); 121 | } 122 | } 123 | } 124 | C.clear(); 125 | } 126 | } 127 | DEBUG_PRINTF("[pmc heuristic]\t mc = %i\n", mc); 128 | return mc; 129 | } 130 | 131 | 132 | int pmc_heu::compute_heuristic(int v) { 133 | if (strat == "kcore_deg") return (*K)[v] * (*degree)[v]; 134 | else if (strat == "deg") return (*degree)[v]; 135 | else if (strat == "kcore") return (*K)[v]; 136 | else if (strat == "rand") return rand() % (*V).size(); 137 | else if (strat == "var") return (*K)[v] * ((int)(*degree)[v]/(*K)[v]); 138 | return v; 139 | } 140 | 141 | 142 | int pmc_heu::search_cores(const pmc_graph& G, std::vector& C_max, int lb) { 143 | std::vector C; 144 | std::vector P; 145 | 146 | C_max.reserve(ub); 147 | C.reserve(ub); 148 | P.reserve(G.get_max_degree()+1); 149 | 150 | bool_vector ind(G.num_vertices(), false); 151 | 152 | int mc = lb; 153 | 154 | int lb_idx = 0; 155 | for (int i = G.num_vertices()-1; i >= 0; i--) { 156 | const int v = (*order)[i]; 157 | if ((*K)[v] == lb) lb_idx = i; 158 | } 159 | 160 | #pragma omp parallel for schedule(dynamic) \ 161 | shared(G, mc, C_max) \ 162 | private(P, C) firstprivate(ind) \ 163 | num_threads(num_threads) 164 | for (int i = lb_idx; i < G.num_vertices(); i++) { 165 | const int v = (*order)[i]; 166 | 167 | int mc_prev, mc_cur; 168 | { 169 | #pragma omp critical 170 | mc_prev = mc_cur = mc; 171 | } 172 | 173 | if ((*K)[v] > mc_cur) { 174 | for (long long j = (*V)[v]; j < (*V)[v + 1]; j++) 175 | if ((*K)[(*E)[j]] > mc_cur) 176 | P.emplace_back((*E)[j], compute_heuristic((*E)[j])); 177 | 178 | if (P.size() > mc_cur) { 179 | std::sort(P.begin(), P.end(), incr_heur); 180 | branch(P, 1 , mc_cur, C, ind); 181 | 182 | if (mc_cur > mc_prev) { 183 | C.push_back(v); 184 | 185 | #pragma omp critical 186 | if (mc_cur > mc) { 187 | mc = mc_cur; 188 | 189 | std::swap(C_max, C); 190 | print_info(C_max); 191 | } 192 | } 193 | } 194 | C.clear(); 195 | } 196 | } 197 | DEBUG_PRINTF("[search_cores]\t mc = %i\n", mc); 198 | return mc; 199 | } 200 | 201 | 202 | int pmc_heu::search(const pmc_graph& G, std::vector& C_max) { 203 | return search_bounds(G, C_max); 204 | } 205 | 206 | 207 | void pmc_heu::print_info(const std::vector& C_max) const { 208 | DEBUG_PRINTF("*** [pmc heuristic: thread %i", omp_get_thread_num() + 1); 209 | DEBUG_PRINTF("] current max clique = %i", C_max.size()); 210 | DEBUG_PRINTF(", time = %i sec\n", get_time() - sec); 211 | } 212 | 213 | -------------------------------------------------------------------------------- /pmc_lib.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/pmc.h" 21 | 22 | using namespace std; 23 | using namespace pmc; 24 | 25 | extern "C" { 26 | 27 | // a list of edges, where index_offset is the starting index 28 | int max_clique(long long nedges, int *ei, int *ej, int index_offset, 29 | int outsize, int *clique) { 30 | input in; 31 | 32 | pmc_graph G(nedges, ei, ej, index_offset); 33 | 34 | //! ensure wait time is greater than the time to recompute the graph data structures 35 | if (G.num_edges() > 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; 49 | if (in.lb == 0 && in.heu_strat != "0") { // skip if given as input 50 | pmc_heu maxclique(G,in); 51 | in.lb = maxclique.search(G, C); 52 | cout << "Heuristic found clique of size " << in.lb; 53 | cout << " in " << get_time() - seconds << " seconds" <= 0) { 63 | switch(in.algorithm) { 64 | case 0: { 65 | //! k-core pruning, neigh-core pruning/ordering, dynamic coloring bounds/sort 66 | if (G.num_vertices() < in.adj_limit) { 67 | G.create_adj(); 68 | pmcx_maxclique finder(G,in); 69 | finder.search_dense(G,C); 70 | break; 71 | } 72 | else { 73 | pmcx_maxclique finder(G,in); 74 | finder.search(G,C); 75 | break; 76 | } 77 | } 78 | case 1: { 79 | //! k-core pruning, dynamic coloring bounds/sort 80 | if (G.num_vertices() < in.adj_limit) { 81 | G.create_adj(); 82 | pmcx_maxclique_basic finder(G,in); 83 | finder.search_dense(G,C); 84 | break; 85 | } 86 | else { 87 | pmcx_maxclique_basic finder(G,in); 88 | finder.search(G,C); 89 | break; 90 | } 91 | } 92 | case 2: { 93 | //! simple k-core pruning (four new pruning steps) 94 | pmc_maxclique finder(G,in); 95 | finder.search(G,C); 96 | break; 97 | } 98 | default: 99 | cout << "algorithm " << in.algorithm << " not found." < 24 | 25 | using namespace std; 26 | using namespace pmc; 27 | 28 | int pmc_maxclique::search(pmc_graph& G, vector& sol) { 29 | 30 | vertices = &G.get_vertices(); 31 | edges = &G.get_edges(); 32 | degree = G.get_degree(); 33 | bool_vector pruned(G.num_vertices()); 34 | int mc = lb, i = 0, u = 0; 35 | 36 | // initial pruning 37 | int lb_idx = G.initial_pruning(G, pruned, lb); 38 | 39 | // set to worst case bound of cores/coloring 40 | vector P, T; 41 | P.reserve(G.get_max_degree()+1); 42 | T.reserve(G.get_max_degree()+1); 43 | 44 | vector C, C_max; 45 | C.reserve(G.get_max_degree()+1); 46 | C_max.reserve(G.get_max_degree()+1); 47 | 48 | // order verts for our search routine 49 | vector V; V.reserve(G.num_vertices()); 50 | G.order_vertices(V,G,lb_idx,lb,vertex_ordering,decr_order); 51 | 52 | vector ind(G.num_vertices(),0); 53 | 54 | #pragma omp parallel for schedule(dynamic) \ 55 | shared(pruned, G, T, V, mc, C_max) firstprivate(ind) private(u, P, C) num_threads(num_threads) 56 | for (i = 0; i < (V.size()) - (mc-1); ++i) { 57 | if (G.time_left(C_max,sec,time_limit,time_expired_msg)) { 58 | 59 | u = V[i].get_id(); 60 | if ((*bound)[u] > mc) { 61 | P.push_back(V[i]); 62 | for (long long j = (*vertices)[u]; j < (*vertices)[u + 1]; ++j) 63 | if (!pruned[(*edges)[j]]) 64 | if ((*bound)[(*edges)[j]] > mc) 65 | P.push_back(Vertex((*edges)[j], (*degree)[(*edges)[j]])); 66 | 67 | if (P.size() > mc) { 68 | branch(P, ind, C, C_max, pruned, mc); 69 | } 70 | P = T; 71 | } 72 | pruned[u] = true; 73 | } 74 | } 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 | const bool_vector& 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 | DEBUG_PRINTF("[pmc: upper bound reached] omega = %i\n", mc); 126 | } 127 | } 128 | 129 | } 130 | // backtrack and search another branch 131 | R.clear(); 132 | C.pop_back(); 133 | } 134 | else return; 135 | P.pop_back(); 136 | } 137 | } 138 | } 139 | 140 | 141 | 142 | 143 | 144 | 145 | /** 146 | * Dense graphs: we use ADJ matrix + CSC Representation 147 | * ADJ: O(1) edge lookups 148 | * CSC: O(1) time to compute degree 149 | * 150 | */ 151 | 152 | int pmc_maxclique::search_dense(pmc_graph& G, vector& sol) { 153 | 154 | vertices = &G.get_vertices(); 155 | edges = &G.get_edges(); 156 | degree = G.get_degree(); 157 | auto adj = G.adj; 158 | 159 | bool_vector pruned(G.num_vertices()); 160 | int mc = lb, i = 0, u = 0; 161 | 162 | // initial pruning 163 | int lb_idx = G.initial_pruning(G, pruned, lb, adj); 164 | 165 | // set to worst case bound of cores 166 | vector P, T; 167 | P.reserve(G.get_max_degree()+1); 168 | T.reserve(G.get_max_degree()+1); 169 | 170 | vector C, C_max; 171 | C.reserve(G.get_max_degree()+1); 172 | C_max.reserve(G.get_max_degree()+1); 173 | 174 | // order verts for our search routine 175 | vector V; V.reserve(G.num_vertices()); 176 | G.order_vertices(V,G,lb_idx,lb,vertex_ordering,decr_order); 177 | 178 | vector ind(G.num_vertices(),0); 179 | 180 | #pragma omp parallel for schedule(dynamic) \ 181 | shared(pruned, G, adj, T, V, mc, C_max) firstprivate(ind) private(u, P, C) num_threads(num_threads) 182 | for (i = 0; i < (V.size()) - (mc-1); ++i) { 183 | if (G.time_left(C_max,sec,time_limit,time_expired_msg)) { 184 | 185 | u = V[i].get_id(); 186 | if ((*bound)[u] > mc) { 187 | P.push_back(V[i]); 188 | for (long long j = (*vertices)[u]; j < (*vertices)[u + 1]; ++j) 189 | if (!pruned[(*edges)[j]]) 190 | if ((*bound)[(*edges)[j]] > mc) 191 | P.push_back(Vertex((*edges)[j], (*degree)[(*edges)[j]])); 192 | 193 | if (P.size() > mc) { 194 | branch_dense(P, ind, C, C_max, pruned, mc, adj); 195 | } 196 | P = T; 197 | } 198 | pruned[u] = true; 199 | for (long long j = (*vertices)[u]; j < (*vertices)[u + 1]; j++) { 200 | adj[u][(*edges)[j]] = false; 201 | adj[(*edges)[j]][u] = false; 202 | } 203 | } 204 | } 205 | 206 | sol.resize(mc); 207 | for (int i = 0; i < C_max.size(); i++) sol[i] = C_max[i]; 208 | G.print_break(); 209 | return sol.size(); 210 | } 211 | 212 | 213 | 214 | void pmc_maxclique::branch_dense( 215 | vector &P, 216 | vector& ind, 217 | vector& C, 218 | vector& C_max, 219 | bool_vector& pruned, 220 | int& mc, 221 | std::vector& adj) { 222 | 223 | // stop early if ub is reached 224 | if (not_reached_ub) { 225 | while (P.size() > 0) { 226 | // terminating condition 227 | if (C.size() + P.size() > mc) { 228 | int v = P.back().get_id(); C.push_back(v); 229 | vector R; R.reserve(P.size()); 230 | 231 | for (int k = 0; k < P.size() - 1; k++) 232 | // indicates neighbor AND pruned 233 | if (adj[v][P[k].get_id()]) 234 | if ((*bound)[P[k].get_id()] > mc) 235 | R.push_back(P[k]); 236 | 237 | if (R.size() > 0) { 238 | branch_dense(R, ind, C, C_max, pruned, mc, adj); 239 | } 240 | else if (C.size() > mc) { 241 | // obtain lock 242 | #pragma omp critical (update_mc) 243 | if (C.size() > mc) { 244 | // ensure updated max is flushed 245 | mc = C.size(); 246 | C_max = C; 247 | print_mc_info(C,sec); 248 | if (mc >= param_ub) { 249 | not_reached_ub = false; 250 | DEBUG_PRINTF("[pmc: upper bound reached] omega = %i\n", mc); 251 | } 252 | } 253 | 254 | } 255 | // backtrack and search another branch 256 | R.clear(); 257 | C.pop_back(); 258 | } 259 | else return; 260 | P.pop_back(); 261 | } 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /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/pmc_utils.h" 21 | #include "pmc/pmc_debug_utils.h" 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | using namespace std; 32 | 33 | bool fexists(const char *filename) { 34 | ifstream ifile(filename); 35 | return !ifile.fail(); 36 | } 37 | 38 | void usage(char *argv0) { 39 | const char *params = 40 | "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" 41 | "\t-a algorithm : Algorithm for solving MAX-CLIQUE: 0 = full, 1 = no neighborhood cores, 2 = only basic k-core pruning steps \n" 42 | "\t-f graph file : Input GRAPH file for computing the Maximum Clique (matrix market format or simple edge list). \n" 43 | "\t-o vertex search ordering : Order in which vertices are searched (default = deg, [kcore, dual_deg, dual_kcore, kcore_deg, rand]) \n" 44 | "\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" 45 | "\t-e neigh/edge ordering : Ordering of neighbors/edges (default = deg, [kcore, dual_deg, dual_kcore, kcore_deg, rand]) \n" 46 | "\t-h heuristic strategy : Strategy for HEURISTIC method (default = kcore, [deg, dual_deg, dual_kcore, rand, 0 = skip heuristic]) \n" 47 | "\t-u upper_bound : UPPER-BOUND on clique size (default = K-cores).\n" 48 | "\t-l lower_bound : LOWER-BOUND on clique size (default = Estimate using the Fast Heuristic). \n" 49 | "\t-t threads : Number of THREADS for the algorithm to use (default = 1). \n" 50 | "\t-r reduce_wait : Number of SECONDS to wait before inducing the graph based on the unpruned vertices (default = 4 seconds). \n" 51 | "\t-w time_limit : Execution TIME LIMIT spent searching for max clique (default = 7 days) \n" 52 | "\t-k clique size : Solve K-CLIQUE problem: find clique of size k if it exists. Parameterized to be fast. \n" 53 | "\t-s stats : Compute BOUNDS and other fast graph stats \n" 54 | "\t-v verbose : Output additional details to the screen. \n" 55 | "\t-? options : Print out this help menu. \n"; 56 | fprintf(stderr, params, argv0); 57 | exit(-1); 58 | } 59 | 60 | 61 | double get_time() { 62 | timeval t; 63 | gettimeofday(&t, NULL); 64 | return t.tv_sec*1.0 + t.tv_usec/1000000.0; 65 | } 66 | 67 | string memory_usage() { 68 | ostringstream mem; 69 | ifstream proc("/proc/self/status"); 70 | string s; 71 | while(getline(proc, s), !proc.fail()) { 72 | if(s.substr(0, 6) == "VmSize") { 73 | mem << s; 74 | return mem.str(); 75 | } 76 | } 77 | return mem.str(); 78 | } 79 | 80 | void indent(int level) { 81 | for (int i = 0; i < level; i++) 82 | cout << " "; 83 | cout << "(" << level << ") "; 84 | } 85 | 86 | void print_max_clique(vector& C) { 87 | #ifdef PMC_ENABLE_DEBUG 88 | cout << "Maximum clique: "; 89 | for(int i = 0; i < C.size(); i++) 90 | cout << C[i] + 1 << " "; 91 | cout << endl; 92 | #else 93 | discard(C); 94 | #endif 95 | } 96 | 97 | void print_n_maxcliques(set< vector > C, int n) { 98 | #ifdef PMC_ENABLE_DEBUG 99 | set< vector >::iterator it; 100 | int mc = 0; 101 | for( it = C.begin(); it != C.end(); it++) { 102 | if (mc < n) { 103 | cout << "Maximum clique: "; 104 | const vector& clq = (*it); 105 | for (int j = 0; j < clq.size(); j++) 106 | cout << clq[j] << " "; 107 | cout < &files) { 126 | DIR *dp; 127 | struct dirent *dirp; 128 | if((dp = opendir(dir.c_str())) == NULL) { 129 | cout << "Error(" << errno << ") opening " << dir << endl; 130 | return errno; 131 | } 132 | 133 | while ((dirp = readdir(dp)) != NULL) { 134 | if (strcmp(dirp->d_name, ".") != 0) 135 | files.push_back(string(dirp->d_name)); 136 | } 137 | closedir(dp); 138 | return 0; 139 | } 140 | 141 | 142 | -------------------------------------------------------------------------------- /pmcx_maxclique.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/pmcx_maxclique.h" 21 | #include "pmc/pmc_neigh_coloring.h" 22 | #include "pmc/pmc_neigh_cores.h" 23 | 24 | #include 25 | 26 | using namespace std; 27 | using namespace pmc; 28 | 29 | int pmcx_maxclique::search(pmc_graph& G, vector& sol) { 30 | 31 | bool_vector pruned(G.num_vertices()); 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 | // in theory, this should be G.get_max_core()+1, but 48 | // the starting color is 1, not 0, so that's +2 49 | // and then there is an extra off-by-one error that makes +3 50 | vector< vector > colors(G.get_max_core()+3); 51 | for (int i = 0; i < G.get_max_core()+1; i++) colors[i].reserve(G.get_max_core()+1); 52 | 53 | // order verts for our search routine 54 | vector V; 55 | V.reserve(G.num_vertices()); 56 | G.order_vertices(V,G,lb_idx,lb,vertex_ordering,decr_order); 57 | DEBUG_PRINTF("|V| = %i\n", V.size()); 58 | 59 | vector ind(G.num_vertices(),0); 60 | vector es = G.get_edges_array(); 61 | vector vs = G.get_vertices_array(); 62 | 63 | vector induce_time(num_threads,get_time()); 64 | for (int t = 0; t < num_threads; ++t) induce_time[t] = induce_time[t] + t/4; 65 | 66 | #pragma omp parallel for schedule(dynamic) shared(pruned, G, T, V, mc, C_max, induce_time) \ 67 | firstprivate(colors,ind,vs,es) private(u, P, C) num_threads(num_threads) 68 | for (i = 0; i < (V.size()) - (mc-1); ++i) { 69 | if (not_reached_ub) { 70 | if (G.time_left(C_max,sec,time_limit,time_expired_msg)) { 71 | 72 | u = V[i].get_id(); 73 | if ((*bound)[u] > mc) { 74 | P.push_back(V[i]); 75 | for (long long j = vs[u]; j < vs[u + 1]; ++j) 76 | if (!pruned[es[j]]) 77 | if ((*bound)[es[j]] > mc) 78 | P.push_back(Vertex(es[j], (vs[es[j]+1] - vs[es[j]]) )); /// local 79 | 80 | 81 | if (P.size() > mc) { 82 | neigh_cores_bound(vs,es,P,ind,mc); 83 | if (P.size() > mc && P[0].get_bound() >= mc) { 84 | neigh_coloring_bound(vs,es,P,ind,C,colors,mc); 85 | if (P.back().get_bound() > mc) { 86 | branch(vs,es,P, ind, C, C_max, colors, pruned, mc); 87 | } 88 | } 89 | } 90 | P = T; 91 | } 92 | pruned[u] = true; 93 | 94 | // dynamically reduce graph in a thread-safe manner 95 | if ((get_time() - induce_time[omp_get_thread_num()]) > wait_time) { 96 | G.reduce_graph( vs, es, pruned, G); 97 | G.graph_stats(G, mc, i+lb_idx, sec); 98 | induce_time[omp_get_thread_num()] = get_time(); 99 | } 100 | } 101 | } 102 | } 103 | 104 | sol.resize(mc); 105 | for (int i = 0; i < C_max.size(); i++) sol[i] = C_max[i]; 106 | G.print_break(); 107 | return sol.size(); 108 | } 109 | 110 | void pmcx_maxclique::branch( 111 | vector& vs, 112 | vector& es, 113 | vector &P, 114 | vector& ind, 115 | vector& C, 116 | vector& C_max, 117 | vector< vector >& colors, 118 | const bool_vector& pruned, 119 | int& mc) { 120 | 121 | // stop early if ub is reached 122 | if (not_reached_ub) { 123 | while (P.size() > 0) { 124 | // terminating condition 125 | if (C.size() + P.back().get_bound() > mc) { 126 | int v = P.back().get_id(); C.push_back(v); 127 | 128 | vector R; R.reserve(P.size()); 129 | for (long long j = vs[v]; j < vs[v + 1]; j++) ind[es[j]] = 1; 130 | 131 | // intersection of N(v) and P - {v} 132 | for (int k = 0; k < P.size() - 1; k++) 133 | if (ind[P[k].get_id()]) 134 | if (!pruned[P[k].get_id()]) 135 | if ((*bound)[P[k].get_id()] > mc) 136 | R.push_back(P[k]); 137 | 138 | for (long long j = vs[v]; j < vs[v + 1]; j++) ind[es[j]] = 0; 139 | 140 | 141 | if (R.size() > 0) { 142 | // color graph induced by R and sort for O(1) 143 | neigh_coloring_bound(vs, es, R, ind, C, colors, mc); 144 | branch(vs, es, R, ind, C, C_max, colors, pruned, mc); 145 | } 146 | else if (C.size() > mc) { 147 | // obtain lock 148 | #pragma omp critical (update_mc) 149 | if (C.size() > mc) { 150 | // ensure updated max is flushed 151 | mc = C.size(); 152 | C_max = C; 153 | print_mc_info(C,sec); 154 | if (mc >= param_ub) { 155 | not_reached_ub = false; 156 | DEBUG_PRINTF("[pmc: upper bound reached] omega = %i\n", mc); 157 | } 158 | } 159 | } 160 | // backtrack and search another branch 161 | R.clear(); 162 | C.pop_back(); 163 | } 164 | else return; 165 | P.pop_back(); 166 | } 167 | } 168 | } 169 | 170 | /** 171 | * Dense graphs: we use ADJ matrix + CSC Representation 172 | * ADJ: O(1) edge lookups 173 | * CSC: O(1) time to compute degree 174 | * 175 | */ 176 | int pmcx_maxclique::search_dense(pmc_graph& G, vector& sol) { 177 | 178 | auto adj = G.adj; 179 | 180 | bool_vector pruned(G.num_vertices()); 181 | int mc = lb, i = 0, u = 0; 182 | 183 | // initial pruning 184 | int lb_idx = G.initial_pruning(G, pruned, lb, adj); 185 | 186 | // set to worst case bound of cores/coloring 187 | vector P, T; 188 | P.reserve(G.get_max_degree()+1); 189 | T.reserve(G.get_max_degree()+1); 190 | 191 | vector C, C_max; 192 | C.reserve(G.get_max_degree()+1); 193 | C_max.reserve(G.get_max_degree()+1); 194 | 195 | // init the neigh coloring array 196 | // in theory, this should be G.get_max_core()+1, but 197 | // the starting color is 1, not 0, so that's +2 198 | // and then there is an extra off-by-one error that makes +3 199 | vector< vector > colors(G.get_max_core()+3); 200 | for (int i = 0; i < G.get_max_core()+1; i++) colors[i].reserve(G.get_max_core()+1); 201 | 202 | // order verts for our search routine 203 | vector V; 204 | V.reserve(G.num_vertices()); 205 | G.order_vertices(V,G,lb_idx,lb,vertex_ordering,decr_order); 206 | DEBUG_PRINTF("|V| = %i\n", V.size()); 207 | 208 | vector ind(G.num_vertices(),0); 209 | vector es = G.get_edges_array(); 210 | vector vs = G.get_vertices_array(); 211 | 212 | vector induce_time(num_threads,get_time()); 213 | for (int t = 0; t < num_threads; ++t) induce_time[t] = induce_time[t] + t/4; 214 | 215 | 216 | #pragma omp parallel for schedule(dynamic) shared(pruned, G, adj, T, V, mc, C_max, induce_time) \ 217 | firstprivate(colors,ind,vs,es) private(u, P, C) num_threads(num_threads) 218 | for (i = 0; i < (V.size()) - (mc-1); ++i) { 219 | DEBUG_PRINTF("DEBUG current mc: %i\n", mc); 220 | if (not_reached_ub) { 221 | if (G.time_left(C_max,sec,time_limit,time_expired_msg)) { 222 | 223 | u = V[i].get_id(); 224 | if ((*bound)[u] > mc) { 225 | P.push_back(V[i]); 226 | for (long long j = vs[u]; j < vs[u + 1]; ++j) 227 | if (!pruned[es[j]]) 228 | if ((*bound)[es[j]] > mc) 229 | P.push_back(Vertex(es[j], (vs[es[j]+1] - vs[es[j]]) )); /// local 230 | 231 | if (P.size() > mc) { 232 | // neighborhood core ordering and pruning 233 | neigh_cores_bound(vs,es,P,ind,mc); 234 | if (P.size() > mc && P[0].get_bound() >= mc) { 235 | neigh_coloring_dense(P,C,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 | } 241 | P = T; 242 | } 243 | pruned[u] = true; 244 | for (long long j = vs[u]; j < vs[u + 1]; j++) { 245 | adj[u][es[j]] = false; 246 | adj[es[j]][u] = false; 247 | } 248 | 249 | // dynamically reduce graph in a thread-safe manner 250 | if ((get_time() - induce_time[omp_get_thread_num()]) > wait_time) { 251 | G.reduce_graph( vs, es, pruned, G); 252 | G.graph_stats(G, mc, i+lb_idx, sec); 253 | induce_time[omp_get_thread_num()] = get_time(); 254 | } 255 | } 256 | } 257 | } 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 | void pmcx_maxclique::branch_dense( 267 | vector& vs, 268 | vector& es, 269 | vector &P, 270 | vector& ind, 271 | vector& C, 272 | vector& C_max, 273 | vector< vector >& colors, 274 | const bool_vector& pruned, 275 | int& mc, 276 | const std::vector& adj) { 277 | 278 | // stop early if ub is reached 279 | if (not_reached_ub) { 280 | while (P.size() > 0) { 281 | // terminating condition 282 | if (C.size() + P.back().get_bound() > mc) { 283 | int v = P.back().get_id(); C.push_back(v); 284 | vector R; R.reserve(P.size()); 285 | 286 | for (int k = 0; k < P.size() - 1; k++) 287 | // indicates neighbor AND pruned, since threads dynamically update it 288 | if (adj[v][P[k].get_id()]) 289 | if ((*bound)[P[k].get_id()] > mc) 290 | R.push_back(P[k]); 291 | 292 | 293 | if (R.size() > 0) { 294 | // color graph induced by R and sort for O(1) 295 | neigh_coloring_dense(R, C, colors, mc, adj); 296 | branch_dense(vs, es, R, ind, C, C_max, colors, pruned, mc, adj); 297 | } 298 | else if (C.size() > mc) { 299 | // obtain lock 300 | #pragma omp critical (update_mc) 301 | if (C.size() > mc) { 302 | // ensure updated max is flushed 303 | mc = C.size(); 304 | C_max = C; 305 | print_mc_info(C,sec); 306 | if (mc >= param_ub) { 307 | not_reached_ub = false; 308 | DEBUG_PRINTF("[pmc: upper bound reached] omega = %i\n", mc); 309 | } 310 | } 311 | } 312 | // backtrack and search another branch 313 | R.clear(); 314 | C.pop_back(); 315 | } 316 | else return; 317 | P.pop_back(); 318 | } 319 | } 320 | } 321 | 322 | 323 | 324 | -------------------------------------------------------------------------------- /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 "pmc/pmcx_maxclique_basic.h" 21 | #include "pmc/pmc_neigh_coloring.h" 22 | 23 | #include 24 | 25 | using namespace std; 26 | using namespace pmc; 27 | 28 | int pmcx_maxclique_basic::search(pmc_graph& G, vector& sol) { 29 | 30 | degree = G.get_degree(); 31 | bool_vector pruned(G.num_vertices()); 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 | DEBUG_PRINTF("|V| = %i\n", V.size()); 55 | 56 | vector 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) num_threads(num_threads) 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,colors,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] = true; 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); 89 | G.graph_stats(G, mc, i+lb_idx, sec); 90 | induce_time[omp_get_thread_num()] = get_time(); 91 | } 92 | } 93 | } 94 | 95 | sol.resize(mc); 96 | for (int i = 0; i < C_max.size(); i++) sol[i] = C_max[i]; 97 | G.print_break(); 98 | return sol.size(); 99 | } 100 | 101 | 102 | 103 | 104 | void pmcx_maxclique_basic::branch( 105 | vector& vs, 106 | vector& es, 107 | vector &P, 108 | vector& ind, 109 | vector& C, 110 | vector& C_max, 111 | vector< vector >& colors, 112 | const bool_vector& pruned, 113 | int& mc) { 114 | 115 | // stop early if ub is reached 116 | if (not_reached_ub) { 117 | while (P.size() > 0) { 118 | // terminating condition 119 | if (C.size() + P.back().get_bound() > mc) { 120 | int v = P.back().get_id(); C.push_back(v); 121 | 122 | vector R; R.reserve(P.size()); 123 | for (long long j = vs[v]; j < vs[v + 1]; j++) ind[es[j]] = 1; 124 | 125 | // intersection of N(v) and P - {v} 126 | for (int k = 0; k < P.size() - 1; k++) 127 | if (ind[P[k].get_id()]) 128 | if (!pruned[P[k].get_id()]) 129 | if ((*bound)[P[k].get_id()] > mc) 130 | R.push_back(P[k]); 131 | 132 | for (long long j = vs[v]; j < vs[v + 1]; j++) ind[es[j]] = 0; 133 | 134 | 135 | if (R.size() > 0) { 136 | // color graph induced by R and sort for O(1) bound check 137 | neigh_coloring_bound(vs, es, R, ind, C, colors, mc); 138 | // search reordered R 139 | branch(vs, es, R, ind, C, C_max, colors, pruned, mc); 140 | } 141 | else if (C.size() > mc) { 142 | // obtain lock 143 | #pragma omp critical (update_mc) 144 | if (C.size() > mc) { 145 | // ensure updated max is flushed 146 | mc = C.size(); 147 | C_max = C; 148 | print_mc_info(C,sec); 149 | if (mc >= param_ub) { 150 | not_reached_ub = false; 151 | DEBUG_PRINTF("[pmc: upper bound reached] omega = %i\n", mc); 152 | } 153 | } 154 | 155 | } 156 | // backtrack and search another branch 157 | R.clear(); 158 | C.pop_back(); 159 | } 160 | else return; 161 | P.pop_back(); 162 | } 163 | } 164 | } 165 | 166 | 167 | 168 | 169 | 170 | 171 | /** 172 | * Dense graphs: we use ADJ matrix + CSC Representation 173 | * ADJ: O(1) edge lookups 174 | * CSC: O(1) time to compute degree 175 | * 176 | */ 177 | 178 | int pmcx_maxclique_basic::search_dense(pmc_graph& G, vector& sol) { 179 | 180 | degree = G.get_degree(); 181 | auto adj = G.adj; 182 | 183 | bool_vector pruned(G.num_vertices()); 184 | 185 | int mc = lb, i = 0, u = 0; 186 | 187 | // initial pruning 188 | int lb_idx = G.initial_pruning(G, pruned, lb, adj); 189 | 190 | // set to worst case bound of cores/coloring 191 | vector P, T; 192 | P.reserve(G.get_max_degree()+1); 193 | T.reserve(G.get_max_degree()+1); 194 | 195 | vector C, C_max; 196 | C.reserve(G.get_max_degree()+1); 197 | C_max.reserve(G.get_max_degree()+1); 198 | 199 | // init the neigh coloring array 200 | vector< vector > colors(G.get_max_degree()+1); 201 | for (int i = 0; i < G.get_max_degree()+1; i++) colors[i].reserve(G.get_max_degree()+1); 202 | 203 | // order verts for our search routine 204 | vector V; 205 | V.reserve(G.num_vertices()); 206 | G.order_vertices(V,G,lb_idx,lb,vertex_ordering,decr_order); 207 | DEBUG_PRINTF("|V| = %u\n", V.size()); 208 | 209 | vector ind(G.num_vertices(),0); 210 | vector es = G.get_edges_array(); 211 | vector vs = G.get_vertices_array(); 212 | 213 | vector induce_time(num_threads,get_time()); 214 | for (int t = 0; t < num_threads; ++t) induce_time[t] = induce_time[t] + t/4; 215 | 216 | 217 | #pragma omp parallel for schedule(dynamic) shared(pruned, G, adj, T, V, mc, C_max, induce_time) \ 218 | firstprivate(colors,ind,vs,es) private(u, P, C) num_threads(num_threads) 219 | for (i = 0; i < (V.size()) - (mc-1); ++i) { 220 | if (G.time_left(C_max,sec,time_limit,time_expired_msg)) { 221 | 222 | u = V[i].get_id(); 223 | if ((*bound)[u] > mc) { 224 | P.push_back(V[i]); 225 | for (long long j = vs[u]; j < vs[u + 1]; ++j) 226 | if (!pruned[es[j]]) 227 | if ((*bound)[es[j]] > mc) 228 | P.push_back(Vertex(es[j], (*degree)[es[j]])); 229 | 230 | if (P.size() > mc) { 231 | neigh_coloring_dense(P,C,colors,mc, adj); 232 | if (P.back().get_bound() > mc) { 233 | branch_dense(vs,es,P, ind, C, C_max, colors, pruned, mc, adj); 234 | } 235 | } 236 | P = T; 237 | } 238 | pruned[u] = true; 239 | for (long long j = vs[u]; j < vs[u + 1]; j++) { 240 | adj[u][es[j]] = false; 241 | adj[es[j]][u] = false; 242 | } 243 | 244 | // dynamically reduce graph in a thread-safe manner 245 | if ((get_time() - induce_time[omp_get_thread_num()]) > wait_time) { 246 | G.reduce_graph( vs, es, pruned, G); 247 | G.graph_stats(G, mc, i+lb_idx, sec); 248 | induce_time[omp_get_thread_num()] = get_time(); 249 | } 250 | } 251 | } 252 | 253 | sol.resize(mc); 254 | for (int i = 0; i < C_max.size(); i++) sol[i] = C_max[i]; 255 | G.print_break(); 256 | return sol.size(); 257 | } 258 | 259 | 260 | 261 | 262 | void pmcx_maxclique_basic::branch_dense( 263 | vector& vs, 264 | vector& es, 265 | vector &P, 266 | vector& ind, 267 | vector& C, 268 | vector& C_max, 269 | vector< vector >& colors, 270 | bool_vector& pruned, 271 | int& mc, 272 | std::vector& adj) { 273 | 274 | // stop early if ub is reached 275 | if (not_reached_ub) { 276 | while (P.size() > 0) { 277 | // terminating condition 278 | if (C.size() + P.back().get_bound() > mc) { 279 | int v = P.back().get_id(); C.push_back(v); 280 | vector R; R.reserve(P.size()); 281 | 282 | for (int k = 0; k < P.size() - 1; k++) 283 | // indicates neighbor AND pruned, since threads dynamically update it 284 | if (adj[v][P[k].get_id()]) 285 | if ((*bound)[P[k].get_id()] > mc) 286 | R.push_back(P[k]); 287 | 288 | if (R.size() > 0) { 289 | // color graph induced by R and sort for O(1) 290 | neigh_coloring_dense(R, C, colors, mc, adj); 291 | branch_dense(vs, es, R, ind, C, C_max, colors, pruned, mc, adj); 292 | } 293 | else if (C.size() > mc) { 294 | // obtain lock 295 | #pragma omp critical (update_mc) 296 | if (C.size() > mc) { 297 | // ensure updated max is flushed 298 | mc = C.size(); 299 | C_max = C; 300 | print_mc_info(C,sec); 301 | if (mc >= param_ub) { 302 | not_reached_ub = false; 303 | DEBUG_PRINTF("[pmc: upper bound reached] omega = %i\n", mc); 304 | } 305 | } 306 | 307 | } 308 | // backtrack and search another branch 309 | R.clear(); 310 | C.pop_back(); 311 | } 312 | else return; 313 | P.pop_back(); 314 | } 315 | } 316 | } 317 | --------------------------------------------------------------------------------