From 58423fccf41e12f3b25303a3d70291335dda0de4 Mon Sep 17 00:00:00 2001 From: Aleksandr Date: Thu, 19 Oct 2023 08:59:02 +0300 Subject: [PATCH] [BENCH] Bron-Kerbosch clique detection (#157) * - added DFS cycle detection for directed and undirected graph - added tests * - corrected code according to comments * Apply suggestions from code review * - First example of potential use of the library * - small fixes * Update transport-example.md Corrected trailling slashes * clang format fix * reflect breaking changes on main * clang format fix * - corrected according to git comments - relative railway example - corrected png files * small fixes * small fixes * fix issues after merge * fix syntax highlighting code blocks * clang format * - Added MST algorithm - Added test cases and docs * clang style correction * Apply suggestions from code review * Added topological sort algorithm DFS-based * small fixes * clang format fix * added test * more tests * Minor fixes Added one test * Clang format * Update topological-sort.md Corrected doc file * DOCS for DFS based cycle detection * small fixes * Update dfs-based.md * Added Floyd-WArshall algorithm - algorithm - test - documentation * clang tidy * removed redundant negative cicle check and according tests, added two new tests * Bron-Kerbosch algorithm * Added to readme * corrected according to comments * Update README.md * folder name correction, added json to docs * small correction in name and headers * benchmark test Bron-Kerbosch * Changed according to comments --------- Co-authored-by: Bob Luppes --- perf/graaflib/bron_kerbosch_benchmark.cpp | 173 ++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 perf/graaflib/bron_kerbosch_benchmark.cpp diff --git a/perf/graaflib/bron_kerbosch_benchmark.cpp b/perf/graaflib/bron_kerbosch_benchmark.cpp new file mode 100644 index 00000000..c27c8fbc --- /dev/null +++ b/perf/graaflib/bron_kerbosch_benchmark.cpp @@ -0,0 +1,173 @@ +#include +#include +#include + +#include +#include + +namespace { +// Generating random number of vertices (1- 30) for a clique +std::random_device dev; +std::mt19937 rng(dev()); +std::uniform_int_distribution random_clique_size(1, + 30); +template +[[nodiscard]] std::vector create_vertices( + graaf::undirected_graph& graph, size_t n) { + std::vector vertices{}; + vertices.reserve(n); + + for (size_t i{0}; i < n; ++i) { + vertices.push_back(graph.add_vertex(i)); + } + + return vertices; +} + +template +void add_clique(graaf::undirected_graph& graph, + const std::vector& vertices, + size_t start_vertex, size_t clique_size) { + // We are in range of vector of vertices + size_t end_vertex = std::min(start_vertex + clique_size, vertices.size()); + + // Constructing a clique + for (size_t i{start_vertex}; i < end_vertex; ++i) { + for (size_t j{i + 1}; j < end_vertex; ++j) { + graph.add_edge(vertices[i], vertices[j], 1); + } + } +} +} // namespace + +static void bron_kerbosh_cliques(benchmark::State& state) { + const auto number_of_edges{static_cast(state.range(0))}; + + graaf::undirected_graph graph{}; + + // We create enough vertices to construct the requested number of edges + const auto number_of_vertices{number_of_edges + 1}; + const auto vertices{create_vertices(graph, number_of_vertices)}; + const auto clique_size = static_cast(state.range(1)); + + for (size_t i{0}; i < number_of_edges; i += clique_size) { + add_clique(graph, vertices, i, clique_size); + } + + for (auto _ : state) { + auto result = graaf::algorithm::bron_kerbosch(graph); + benchmark::DoNotOptimize(result); + } + + state.SetComplexityN(state.range(1)); +} + +static void bron_kerbosh_connected_cliques(benchmark::State& state) { + const auto number_of_edges{static_cast(state.range(0))}; + + graaf::undirected_graph graph{}; + + // We create enough vertices to construct the requested number of edges + const auto number_of_vertices{number_of_edges + 1}; + const auto vertices{create_vertices(graph, number_of_vertices)}; + const auto clique_size = static_cast(state.range(1)); + + // Connecting all cliques + for (size_t i{0}; i + clique_size < number_of_edges; i += clique_size) { + graph.add_edge(vertices[i], vertices[i + clique_size], 1); + } + + for (size_t i{0}; i < number_of_edges; i += clique_size) { + add_clique(graph, vertices, i, clique_size); + } + + for (auto _ : state) { + auto result = graaf::algorithm::bron_kerbosch(graph); + benchmark::DoNotOptimize(result); + } + + state.SetComplexityN(state.range(1)); +} + +static void bron_kerbosh_random_cliques(benchmark::State& state) { + const auto number_of_edges{static_cast(state.range(0))}; + + graaf::undirected_graph graph{}; + + // We create enough vertices to construct the requested number of edges + const auto number_of_vertices{number_of_edges + 1}; + const auto vertices{create_vertices(graph, number_of_vertices)}; + size_t clique_size = random_clique_size(rng); + + for (size_t i{0}; i + clique_size < number_of_edges; i += clique_size) { + add_clique(graph, vertices, i, clique_size++); + clique_size = random_clique_size(rng); + } + + for (auto _ : state) { + auto result = graaf::algorithm::bron_kerbosch(graph); + benchmark::DoNotOptimize(result); + } + + state.SetComplexityN(state.range(0)); +} + +static void bron_kerbosh_connected_random_cliques(benchmark::State& state) { + const auto number_of_edges{static_cast(state.range(0))}; + + graaf::undirected_graph graph{}; + + // We create enough vertices to construct the requested number of edges + const auto number_of_vertices{number_of_edges + 1}; + const auto vertices{create_vertices(graph, number_of_vertices)}; + size_t clique_size = random_clique_size(rng); + + // Connecting all cliques + for (size_t i{0}; i < number_of_edges; ++i) { + graph.add_edge(vertices[i], vertices[i + 1], 1); + } + + for (size_t i{0}; i + clique_size < number_of_edges; i += clique_size) { + add_clique(graph, vertices, i, clique_size++); + clique_size = random_clique_size(rng); + } + + for (auto _ : state) { + auto result = graaf::algorithm::bron_kerbosch(graph); + benchmark::DoNotOptimize(result); + } + + state.SetComplexityN(state.range(0)); +} + +BENCHMARK(bron_kerbosh_cliques) + ->Ranges({{100, 10000}, {2, 32}}) /* clique size 2 ^ n */ + ->Unit(benchmark::kMillisecond) + ->Complexity(); +BENCHMARK(bron_kerbosh_connected_cliques) + ->Ranges({{100, 10000}, {2, 32}}) /* clique size 2 ^ n */ + ->Unit(benchmark::kMillisecond) + ->Complexity(); +BENCHMARK(bron_kerbosh_cliques) + ->Ranges({{100, 1000}, + {1000, 1000}}) /* Dense graph, number of vertices are + min(state.range(1), vertex.size()) */ + ->Unit(benchmark::kMillisecond) + ->Complexity(); +BENCHMARK(bron_kerbosh_cliques) + ->Ranges({{100, 10000}, {10, 60}}) /* large cliques */ + ->Unit(benchmark::kMillisecond) + ->Complexity(); +BENCHMARK(bron_kerbosh_connected_cliques) + ->Ranges({{100, 10000}, {10, 60}}) /* large cliques */ + ->Unit(benchmark::kMillisecond) + ->Complexity(); +BENCHMARK(bron_kerbosh_random_cliques) /* random clique size between 1-30 */ + ->Range(100, 10000) + ->Unit(benchmark::kMillisecond) + ->Complexity(); +BENCHMARK( + bron_kerbosh_connected_random_cliques) /* random clique size between 1-30 */ + ->Range(100, 10000) + ->Unit(benchmark::kMillisecond) + ->Complexity(); \ No newline at end of file