diff --git a/cpp/cmake/boost.cmake b/cpp/cmake/boost.cmake new file mode 100644 index 0000000..213b32f --- /dev/null +++ b/cpp/cmake/boost.cmake @@ -0,0 +1,10 @@ +set(BOOST_INCLUDE_LIBRARIES heap) +set(BOOST_ENABLE_CMAKE ON) +include(FetchContent) +FetchContent_Declare( + Boost + URL https://github.com/boostorg/boost/releases/download/boost-1.87.0/boost-1.87.0-cmake.tar.gz + USES_TERMINAL_DOWNLOAD TRUE + DOWNLOAD_NO_EXTRACT FALSE +) +FetchContent_MakeAvailable(Boost) diff --git a/cpp/include/algo_analysis/algorithms/dijkstra_with_fib.hpp b/cpp/include/algo_analysis/algorithms/dijkstra_with_fib.hpp new file mode 100644 index 0000000..57485ea --- /dev/null +++ b/cpp/include/algo_analysis/algorithms/dijkstra_with_fib.hpp @@ -0,0 +1,91 @@ +#ifndef DIJKSTRA_WITH_FIB_HPP +#define DIJKSTRA_WITH_FIB_HPP + + +#include +#include +#include +#include +#include +#include + +// Dijkstra's algorithm +template +struct DijkstraWithFibAlgorithm : public Algorithm { + using Edge = Edge; + + // Graph representation + struct Graph : public AdjacencyListGraph { + using BaseClass = AdjacencyListGraph; + + size_t nodesNumber; + + Graph() : BaseClass() {} + + Graph(const std::vector &edges): BaseClass(edges) { + nodesNumber = BaseClass::neighbors.size(); + } + }; + + struct Node { + size_t vertex; + WeightType distance; + + bool operator>(const Node& other) const { + return distance > other.distance; + } + }; + + + std::vector distances; // Distances from the start node + Graph graph; + + DijkstraWithFibAlgorithm() { + + } + + WeightType inf; + + // Fit the algorithm to the graph + void fit(const EdgesListGraph &graph_) override { + graph = Graph(graph_.edges); + inf = 1; + for (const Edge &edge : graph_.edges) { + inf += edge.weight; + } + } + + // Compute distances from the start node + std::vector computeDistances(size_t startIndex) override { + int n = graph.nodesNumber; + distances = std::vector(n, inf); + distances[startIndex] = 0; + + boost::heap::fibonacci_heap>> q; + q.push({ startIndex, 0 }); + + while (!q.empty()) { + auto [ u, dist ] = q.top(); + q.pop(); + + if (dist > distances[u]) continue; + + for (const Edge& edge : graph.neighbors[u]) { + size_t v = edge.toIndex; + WeightType newDist = distances[u] + edge.weight; + + if (newDist < distances[v]) { + distances[v] = newDist; + q.push({ v, newDist }); + } + } + } + + return distances; + } + +}; + + + +#endif diff --git a/cpp/include/algo_analysis/algorithms/floid.hpp b/cpp/include/algo_analysis/algorithms/floid.hpp new file mode 100644 index 0000000..b4ed699 --- /dev/null +++ b/cpp/include/algo_analysis/algorithms/floid.hpp @@ -0,0 +1,92 @@ +#ifndef FLOID_HPP +#define FLOID_HPP + +#include +#include +#include +#include + +// Floid algorithm +template +struct FloidAlgorithm : public Algorithm { + using Edge = Edge; + + // Graph representation + struct Graph : public AdjacencyListGraph { + using BaseClass = AdjacencyListGraph; + + size_t nodesNumber; + + Graph() : BaseClass() {} + + Graph(const std::vector &edges): BaseClass(edges) { + nodesNumber = BaseClass::neighbors.size(); + } + }; + + + std::vector> distances; // Distances from each node to all the rest + WeightType inf; + Graph graph; + + FloidAlgorithm() { + + } + + // Fit the algorithm to the graph + void fit(const EdgesListGraph &graph_) override { + graph = Graph(graph_.edges); + inf = 1; + for (const Edge &edge : graph_.edges) { + inf += edge.weight; + } + + distances.resize(graph.nodesNumber, std::vector(graph.nodesNumber, inf)); + for (int v = 0; v < distances.size(); ++v) { + distances[v][v] = 0; + } + for (const Edge &edge : graph_.edges) { + distances[edge.fromIndex][edge.toIndex] = edge.weight; + } + + calcDistances(); + } + + // Compute distances from the start node + std::vector computeDistances(size_t startIndex) override { + std::vector result = distances[startIndex]; + // mark infinities as unreachable + for (auto& w : result) { + if (w >= inf) w = -1; + } + return result; + } + +private: + void calcDistances() { + int n = distances.size(); + for (int i = 0; i < n; ++i) { + for (int u = 0; u < n; ++u) { + for (int v = 0; v < n; ++v) { + distances[u][v] = std::min( + distances[u][v], + distances[u][i] + distances[i][v] + ); + } + } + } + } + + /// @brief Just for debugging purposes + void printDistances() const { + int n = distances.size(); + for (int u = 0; u < n; ++u) { + for (int v = 0; v < n; ++v) { + std::cout << distances[u][v] << ' '; + } + std::cout << std::endl; + } + } +}; + +#endif \ No newline at end of file diff --git a/cpp/micro_benchmarks/CMakeLists.txt b/cpp/micro_benchmarks/CMakeLists.txt index 8fc71f0..7168fcb 100644 --- a/cpp/micro_benchmarks/CMakeLists.txt +++ b/cpp/micro_benchmarks/CMakeLists.txt @@ -27,6 +27,8 @@ FetchContent_Declare( GIT_TAG v1.8.3) FetchContent_MakeAvailable(googlebenchmark) +include(../cmake/boost.cmake) + add_custom_target(micro_benchmarks) include_directories(SYSTEM ${ALGO_ANALYSIS_INCLUDE_DIR}) @@ -36,7 +38,7 @@ set(TOP_LEVEL_DIR "${PROJECT_SOURCE_DIR}/..") function(add_benchmark test_name test_src) add_executable(${test_name} ${PROJECT_SOURCES} ${test_src}) - target_link_libraries(${test_name} PUBLIC benchmark::benchmark) + target_link_libraries(${test_name} PUBLIC benchmark::benchmark Boost::heap) target_compile_features(${test_name} PRIVATE cxx_std_17) target_compile_definitions(${test_name} PUBLIC TOP_LEVEL_DIR="${TOP_LEVEL_DIR}") @@ -47,4 +49,6 @@ endfunction() add_benchmark(DFSBenchmark dfs.cpp) add_benchmark(BFSBenchmark bfs.cpp) add_benchmark(DijkstraBenchmark dijkstra.cpp) +add_benchmark(DijkstraWithFibBenchmark dijkstra_with_fib.cpp) add_benchmark(FordBellmanBenchmark ford_bellman.cpp) +add_benchmark(FloidBenchmark floid.cpp) diff --git a/cpp/micro_benchmarks/benchmarks_results/MacOS/dijkstra_with_fib.json b/cpp/micro_benchmarks/benchmarks_results/MacOS/dijkstra_with_fib.json new file mode 100644 index 0000000..040c1cd --- /dev/null +++ b/cpp/micro_benchmarks/benchmarks_results/MacOS/dijkstra_with_fib.json @@ -0,0 +1,86 @@ +{ + "context": { + "date": "2025-03-03T01:41:22+00:00", + "host_name": "d5962e068320", + "executable": "./micro_benchmarks/DijkstraWithFibBenchmark", + "num_cpus": 16, + "mhz_per_cpu": 48, + "cpu_scaling_enabled": false, + "caches": [ + ], + "load_avg": [0.640137,0.686523,0.703613], + "library_build_type": "debug" + }, + "benchmarks": [ + { + "name": "benchMark/1", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "benchMark/1", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 1393, + "real_time": 1.2728448801150415e+06, + "cpu_time": 1.2728250739411341e+06, + "time_unit": "ns" + }, + { + "name": "benchMark/2", + "family_index": 0, + "per_family_instance_index": 1, + "run_name": "benchMark/2", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 541, + "real_time": 1.3001248465817112e+06, + "cpu_time": 1.3000489796672829e+06, + "time_unit": "ns" + }, + { + "name": "benchMark/3", + "family_index": 0, + "per_family_instance_index": 2, + "run_name": "benchMark/3", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 540, + "real_time": 1.3020578722245300e+06, + "cpu_time": 1.3019908907407410e+06, + "time_unit": "ns" + }, + { + "name": "benchMark/4", + "family_index": 0, + "per_family_instance_index": 3, + "run_name": "benchMark/4", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 535, + "real_time": 1.2960419009364033e+06, + "cpu_time": 1.2959945439252341e+06, + "time_unit": "ns" + }, + { + "name": "benchMark/5", + "family_index": 0, + "per_family_instance_index": 4, + "run_name": "benchMark/5", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 527, + "real_time": 1.3031827172666665e+06, + "cpu_time": 1.3030856223908912e+06, + "time_unit": "ns" + } + ] +} diff --git a/cpp/micro_benchmarks/benchmarks_results/MacOS/floid.json b/cpp/micro_benchmarks/benchmarks_results/MacOS/floid.json new file mode 100644 index 0000000..9ad5667 --- /dev/null +++ b/cpp/micro_benchmarks/benchmarks_results/MacOS/floid.json @@ -0,0 +1,86 @@ +{ + "context": { + "date": "2025-03-03T00:05:48+00:00", + "host_name": "d5962e068320", + "executable": "../../build/micro_benchmarks/FloidBenchmark", + "num_cpus": 16, + "mhz_per_cpu": 48, + "cpu_scaling_enabled": false, + "caches": [ + ], + "load_avg": [0.712402,0.681152,0.664062], + "library_build_type": "debug" + }, + "benchmarks": [ + { + "name": "benchMark/1", + "family_index": 0, + "per_family_instance_index": 0, + "run_name": "benchMark/1", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 1000, + "real_time": 1.2278971260002437e+06, + "cpu_time": 1.2278868280000000e+06, + "time_unit": "ns" + }, + { + "name": "benchMark/2", + "family_index": 0, + "per_family_instance_index": 1, + "run_name": "benchMark/2", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 558, + "real_time": 1.2478864247299575e+06, + "cpu_time": 1.2478506523297487e+06, + "time_unit": "ns" + }, + { + "name": "benchMark/3", + "family_index": 0, + "per_family_instance_index": 2, + "run_name": "benchMark/3", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 547, + "real_time": 1.2409552120659300e+06, + "cpu_time": 1.2409081297989034e+06, + "time_unit": "ns" + }, + { + "name": "benchMark/4", + "family_index": 0, + "per_family_instance_index": 3, + "run_name": "benchMark/4", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 562, + "real_time": 1.2446258896799588e+06, + "cpu_time": 1.2445583434163705e+06, + "time_unit": "ns" + }, + { + "name": "benchMark/5", + "family_index": 0, + "per_family_instance_index": 4, + "run_name": "benchMark/5", + "run_type": "iteration", + "repetitions": 1, + "repetition_index": 0, + "threads": 1, + "iterations": 560, + "real_time": 1.2480564732137672e+06, + "cpu_time": 1.2480045339285720e+06, + "time_unit": "ns" + } + ] +} diff --git a/cpp/micro_benchmarks/dijkstra_with_fib.cpp b/cpp/micro_benchmarks/dijkstra_with_fib.cpp new file mode 100644 index 0000000..0fd5887 --- /dev/null +++ b/cpp/micro_benchmarks/dijkstra_with_fib.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022-2025, HSE CORPORATION. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +void pointQuery(int maxCapacity) { + Task task( + "../tests/.data/task_01.txt" + ); + + DijkstraWithFibAlgorithm dijkstra; + + task.run(dijkstra); +} + +void benchMark(benchmark::State &state) { + int maxCapacity = state.range(0); + for (auto _ : state) { + pointQuery(maxCapacity); + } +} + +int main(int argc, char **argv) { + BENCHMARK(benchMark)->DenseRange(1, 5); + + ::benchmark::Initialize(&argc, argv); + if (::benchmark::ReportUnrecognizedArguments(argc, argv)) { + return 1; + } + ::benchmark::RunSpecifiedBenchmarks(); + + return 0; +} diff --git a/cpp/micro_benchmarks/floid.cpp b/cpp/micro_benchmarks/floid.cpp new file mode 100644 index 0000000..693daa4 --- /dev/null +++ b/cpp/micro_benchmarks/floid.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022-2025, HSE CORPORATION. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include + +void pointQuery(int maxCapacity) { + Task task( + "../tests/.data/task_01.txt" + ); + + FloidAlgorithm floid; + + task.run(floid); +} + +void benchMark(benchmark::State &state) { + int maxCapacity = state.range(0); + for (auto _ : state) { + pointQuery(maxCapacity); + } +} + +int main(int argc, char **argv) { + BENCHMARK(benchMark)->DenseRange(1, 5); + + ::benchmark::Initialize(&argc, argv); + if (::benchmark::ReportUnrecognizedArguments(argc, argv)) { + return 1; + } + ::benchmark::RunSpecifiedBenchmarks(); + + return 0; +} diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index a1e68e1..c69555b 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -24,10 +24,12 @@ FetchContent_Declare( set_ifndef(gtest_force_shared_crt ON CACHE BOOL "" FORCE) FetchContent_MakeAvailable(googletest) +include(../cmake/boost.cmake) + include_directories(SYSTEM ${ALGO_ANALYSIS_INCLUDE_DIR}) file(GLOB TEST_SOURCES "algorithms/*.cpp" "graphs/*.cpp" "task/*.cpp") add_executable(${PROJECT_NAME}_tests ${PROJECT_SOURCES} ${TEST_SOURCES}) -target_link_libraries(${PROJECT_NAME}_tests gtest gtest_main) \ No newline at end of file +target_link_libraries(${PROJECT_NAME}_tests gtest gtest_main Boost::heap) \ No newline at end of file diff --git a/cpp/tests/algorithms/dijkstra_with_fib_test.cpp b/cpp/tests/algorithms/dijkstra_with_fib_test.cpp new file mode 100644 index 0000000..3ebeb9b --- /dev/null +++ b/cpp/tests/algorithms/dijkstra_with_fib_test.cpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2022-2025, HSE CORPORATION. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + + +TEST(DijkstraWithFib, Task01) { + + Task task( + "../tests/.data/task_01.txt" + ); + DijkstraWithFibAlgorithm dijkstra; + auto results = task.run(dijkstra); + std::vector> expectedResult = { + {0, 10, 10, 20}, + }; + + EXPECT_EQ(results, expectedResult); +} + + +TEST(DijkstraWithFib, TaskTree) { + + Task task( + "../tests/.data/task_tree.txt" + ); + DijkstraWithFibAlgorithm algo; + auto results = task.run(algo); + std::vector> expectedResult = { + { 0, 5, 3, 13, 7, 10, 7 } + }; + + EXPECT_EQ(results, expectedResult); +} + diff --git a/cpp/tests/algorithms/floid_test.cpp b/cpp/tests/algorithms/floid_test.cpp new file mode 100644 index 0000000..d564c85 --- /dev/null +++ b/cpp/tests/algorithms/floid_test.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2022-2025, HSE CORPORATION. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + #include + #include + #include + #include + #include + + +TEST(Floid, Task01) { + Task task( + "../tests/.data/task_01.txt" + ); + FloidAlgorithm floid; + auto results = task.run(floid); + std::vector> expectedResult = { + {0, 10, 10, 20}, + }; + + EXPECT_EQ(results, expectedResult); +} + +TEST(Floid, TaskTreeOnlyOnes) { + Task task( + "../tests/.data/task_tree_only_ones.txt" + ); + FloidAlgorithm floid; + auto results = task.run(floid); + std::vector> expectedResult = { + { 0, 1, 1, 2, 2, 2, 2 } + }; + + EXPECT_EQ(results, expectedResult); +} + +TEST(Floid, TaskTree) { + Task task( + "../tests/.data/task_tree.txt" + ); + FloidAlgorithm floid; + auto results = task.run(floid); + std::vector> expectedResult = { + { 0, 5, 3, 13, 7, 10, 7 } + }; + + EXPECT_EQ(results, expectedResult); +} + +TEST(Floid, TaskFullGraph) { + Task task( + "../tests/.data/task_full_graph_only_ones.txt" + ); + FloidAlgorithm floid; + auto results = task.run(floid); + std::vector> expectedResult = { + { 0, 1, 1, 1 }, { -1, 0, 1, 1 } + }; + + EXPECT_EQ(results, expectedResult); +} + +TEST(Floid, TaskSeveralComponents) { + Task task( + "../tests/.data/task_several_components.txt" + ); + FloidAlgorithm floid; + auto results = task.run(floid); + std::vector> expectedResult = { + { 0, 2, 7, 4, 7, 10, 16, -1, -1, -1 }, { -1, -1, -1, -1, 0, 3, 9, -1, -1, -1 } + }; + + EXPECT_EQ(results, expectedResult); +} + +TEST(Floid, TaskCycleWithExtraEdges) { + Task task( + "../tests/.data/task_cycle_with_extra_edges.txt" + ); + FloidAlgorithm floid; + auto results = task.run(floid); + std::vector> expectedResult = { + { { 0, 6, 7, 11, 7, 9 }, { 14, 0, 1, 5, 8, 9 } } + }; + + EXPECT_EQ(results, expectedResult); +} \ No newline at end of file