diff --git a/.cache/graph_example_1.txt b/.cache/graph_example_1.txt deleted file mode 100644 index 065f825..0000000 --- a/.cache/graph_example_1.txt +++ /dev/null @@ -1,11 +0,0 @@ -4 1 -1 -2 -3 -4 -0 1 10 -1 2 20 -2 3 30 -3 0 40 -0 2 50 -1 3 60 diff --git a/.cache/graph_example_2.txt b/.cache/graph_example_2.txt deleted file mode 100644 index dc7dbf8..0000000 --- a/.cache/graph_example_2.txt +++ /dev/null @@ -1,10 +0,0 @@ -4 1 -1 -2 -3 -4 -0 1 10 -1 2 20 -2 3 30 -3 0 40 -1 3 3 diff --git a/.cache/graph_example_3.txt b/.cache/graph_example_3.txt deleted file mode 100644 index 5bc7673..0000000 --- a/.cache/graph_example_3.txt +++ /dev/null @@ -1,11 +0,0 @@ -6 0 -0 1 10 -0 2 14 -0 3 13 -1 2 20 -1 3 12 -2 3 11 -2 5 9 -2 5 15 -3 4 17 -4 5 20 diff --git a/.cache/graph_example_4.txt b/.cache/graph_example_4.txt deleted file mode 100644 index 0964286..0000000 --- a/.cache/graph_example_4.txt +++ /dev/null @@ -1,5 +0,0 @@ -5 0 -0 1 10 -0 2 20 -1 3 30 -3 4 40 \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e4e18c..86ebfef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,23 +1,68 @@ name: Build project - on: [push] - jobs: - ci: + ci-linux: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Installing cmake... - run: sudo apt install cmake - - name: Building... + + - name: Install dependencies on Ubuntu + run: sudo apt update && sudo apt install cmake -y + + - name: Build the project on Ubuntu run: | - cd src + cd cpp mkdir .build && cd .build cmake .. make -j - - name: Testing... + + - name: Run tests on Ubuntu run: | - cd src/.build - ./HSE_SD_tests + cd cpp/.build + export ASAN_OPTIONS="alloc_dealloc_mismatch=0" + ./tests/algo_analysis_tests + + ci-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + + - name: Windows moment )) + run: choco install cmake --installargs 'ADD_CMAKE_TO_PATH=System' --yes + + # - name: Windows moment )) + # run: | + # cd cpp + # mkdir .build && cd .build + # cmake -DCMAKE_BUILD_TYPE=Debug .. + # cmake --build . --config Debug -- /m + # - name: Debug Windows build output + # run: dir cpp\.build + + # - name: List files in Debug folder + # run: dir cpp\.build\Debug + # - name: Run tests on Windows + # run: | + # cd src\.build + # .\Debug\HSE_SD_tests.exe + + ci-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + - name: Install dependencies on macOS + run: brew install cmake + + - name: Build the project on macOS + run: | + cd cpp + mkdir .build && cd .build + cmake .. + make -j + + - name: Run tests on macOS + run: | + cd cpp/.build + ./tests/algo_analysis_tests \ No newline at end of file diff --git a/.gitignore b/.gitignore index 0c63205..d352e3c 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ src/cmake-build-debug/* */.build/* build/* .build/* +cpp/micro_benchmarks/build \ No newline at end of file diff --git a/README.md b/README.md index eb5d23b..87c7a9b 100644 --- a/README.md +++ b/README.md @@ -1,212 +1,56 @@ -# Документация по библиотеке графовых алгоритмов +# algo_analysis +![Build Status](https://github.com/HSE-Software-Development/algorithms_analysis/actions/workflows/ci.yml/badge.svg) +![License](https://img.shields.io/badge/License-MIT-blue.svg) -1. [Класс GeneralGraph](#класс-generalgraph) -2. [Формат графа](#формат-графа) -3. [Алгоритмы](#алгоритмы) - - [FordBellman](#fordbellman) - - [DFS](#dfs) - - [BSF](#bfs) - - [Dijkstra](#dijkstra) +Данная библиотека предназначена для реализации различных алгоритмов поиска кратчайшего пути с учетом стоимости, таких как алгоритмы Форда-Беллмана, Дейкстры и другие. Экспериментальная среда будет позволять тестировать и сравнивать производительность этих алгоритмов на различных данных. -## Архитектура - -### Пространство имен algorithms -В пространстве имен algorithms находятся шаблонные методы для выполнения графовых алгоритмов. Каждый алгоритм принимает на вход объект класса GeneralGraph, и опциональные переменные. - -### Класс GeneralGraph -Класс GeneralGraph является шаблонным и имеет две переменные типа: -- TVertex — тип данных для вершин -- TEdge — тип данных для ребер +## Алгоритмы -#### Конструкторы -Класс GeneralGraph предлагает два конструктора для инициализации графа: -1. GeneralGraph(const std::string& filePath) - Конструктор, принимающий путь до файла, в котором хранится граф в необходимом формате. +**Задача SSSP** (single-source shortest path problem): Найти расстояния от выделенной вершины s до всех вершин -2. GeneralGraph(std::istream& inputStream) - Конструктор, принимающий поток ввода для задания графа в необходимом формате. +**Задача APSP** (all pairs shortest path problem): Найти расстояния между всеми парами вершин, матрицу расстояний (SSSP ⊂ APSP) -## Примеры использования +1. DFS (SSSP) +2. BFS + queue (SSSP) +3. BFS + deque (SSSP) +4. Dijkstra (SSSP) +5. Dijkstra + fib-heap (SSSP) +6. A\* (SSSP) +7. Ford-Bellman (SSSP) +8. Floid (APSP) -### Пример создания графа из файла -``` -#include "GeneralGraph.h" -#include "algorithms.h" - -int main() { - GeneralGraph graph("path/to/graph.txt"); - algorithms::someGraphAlgorithm(graph, ...); - return 0; -} -``` +Подробнее в [документации по алгоритмам](docs/algorithms.md) +## Build -## Формат графа -Для корректного создания графа, необходимо следующее оформление данных: -- В первом ряду указывается два числа: n - количество вершин в графе и число 0 или 1(При отсутствии веса в вершинах или наличии соответственно) -- В случае, если был выставлен флаг 1: - - В каждом из следующих n строчек указывается вес внутри вершины с соответствующим номером -- В каждом следующем ряду указывается новое ребро: - - Формат записи: - vertex1 vertex2 weight \ - где: - - vertex1 и vertex2 — идентификаторы вершин, соединенных ребром. - - weight — вес ребра (должен быть типом данных TEdge). +### For Unix(Linux/MacOs) -### Пример формата графа +1. **Build the project with tests and micro benchmarking:** +```sh + git clone git@github.com:HSE-Software-Development/algorithms_analysis.git + cd algorithms_analysis + sh scripts/build_algo_analysis.sh ``` -4 1 -1 -2 -3 -4 -0 1 10 -1 2 20 -2 3 30 -3 0 40 -0 2 50 -1 3 60 +Если вы не хотите запускать тесты автоматически после сборки библиотеки то: +```sh + sh scripts/build_algo_analysis.sh 0 ``` -В данном примере указан полный граф на 4-ех вершинах с указанными весами на ребрах и в вершинах. - -## Алгоритмы - -### FordBellman -#### Описание -Функция реализует алгоритм Форда - Беллмана для поиска кратчайших путей от одной начальной вершины до всех остальных в графе, в том числе с учетом отрицательных весов. - -#### Шаблон -```c++ -template -void FordBellman(size_t startIndex, GeneralGraph *graph, std::vector &distances); +2. **Run the tests:** +```sh + cd cpp/build + ./tests/algo_analysis_tests ``` -#### Параметры -- startIndex — начальный индекс для алгоритма (указатель на начальную вершину для вычисления кратчайших путей). -- graph — указатель на объект GeneralGraph, который представляет граф. -- distances — ссылка на вектор, который будет заполнен наименьшими расстояниями от начальной вершины до всех остальных. - -#### Пример использования -```c++ -#include "GeneralGraph.h" -#include "algorithms.h" - -int main() { - GeneralGraph graph("path/to/graph_with_negatives.txt"); - std::vector distances(graph.vertexCount(), std::numeric_limits::infinity()); - - size_t startIndex = 0; // Начальная вершина - algorithms::FordBellman(startIndex, &graph, distances); - - // Теперь вектор distances содержит кратчайшие расстояния, даже с учетом отрицательных весов - return 0; -} -``` - -### DFS -#### Функция DFS -#### Описание -Функция выполняет поиск в глубину (DFS) в графе и может использоваться для вычисления кратчайших расстояний в графе без отрицательных весов. - -#### Шаблон -```c++ -template -void DFS(size_t startIndex, GeneralGraph *graph, std::vector &distances); -``` +### For Windows (MSVC) -#### Параметры -- startIndex — начальный индекс для поиска (указатель на начальную вершину). -- graph — указатель на объект GeneralGraph, содержащий граф, по которому будет выполнен поиск. -- distances — ссылка на вектор, который будет заполнен расстояниями от начальной вершины до всех остальных. - -#### Пример использования -```c++ -#include "GeneralGraph.h" -#include "algorithms.h" - -int main() { - GeneralGraph graph("path/to/graph.txt"); - std::vector distances(graph.vertexCount(), std::numeric_limits::infinity()); - - size_t startIndex = 0; // Начальная вершина - algorithms::DFS(startIndex, &graph, distances); - - // Теперь вектор distances содержит расстояния - return 0; -} -``` -### BFS -#### Описание -Функция реализует алгоритм поиска в ширину (BFS) для нахождения кратчайших путей от одной стартовой вершины до всех остальных. Алгоритм работает для графов без отрицательных весов и подходит для неориентированных и ориентированных графов. - -#### Шаблон -```c++ -template -void BFS(size_t startIndex, GeneralGraph *graph, std::vector &distances); -``` +До слез -#### Параметры -- startIndex — начальный индекс для поиска, указывающий на стартовую вершину. -- graph — указатель на объект GeneralGraph, представляющий сам граф. -- distances — ссылка на вектор, который будет заполнен кратчайшими расстояниями от начальной вершины до всех остальных. - -#### Пример использования -```c++ -#include "GeneralGraph.h" -#include "algorithms.h" - -int main() { - GeneralGraph graph("path/to/graph.txt"); - std::vector distances(graph.vertexCount(), std::numeric_limits::infinity()); - - size_t startIndex = 0; // Начальная вершина - algorithms::BFS(startIndex, &graph, distances); - - // distances теперь содержит кратчайшие расстояния от startIndex до всех других вершин - return 0; -} -``` +## Installation and contributing +[Примеры использования тут](examples/README.md) -### Dijkstra -#### Описание -Функция реализует алгоритм Дейкстры для решения задачи нахождения кратчайшего пути от одной стартовой вершины до всех остальных вершин в графе с неотрицательными весами. +Также у вас есть возможность дополнять нашу библиотеку алгоритмов путем их добавления в пространство имен algorithms или же просто как отдельные методы (важно: чтобы этими методами могли оперировать [классы задач](docs/architecture.md) они должны обладать конкретными параметрами в своем определении) -#### Шаблон -```c++ -template -void Dijkstra(size_t startIndex, GeneralGraph *graph, std::vector &distances); -``` +## License -#### Параметры -- startIndex — начальный индекс для поиска кратчайших путей, указывающий на стартовую вершину в графе. -- graph — указатель на объект GeneralGraph, представляющий граф, по которому будет выполняться поиск кратчайших путей. -- distances — ссылка на вектор, который будет заполнен расстояниями от начальной вершины до всех остальных. Вектор должен быть инициализирован (например, значением "бесконечность" для всех вершин, кроме стартовой). - -#### Пример использования -```c++ -#include "GeneralGraph.h" -#include "algorithms.h" -#include -#include - -int main() { - GeneralGraph graph("path/to/graph.txt"); - std::vector distances(graph.vertexCount(), std::numeric_limits::infinity()); - - size_t startIndex = 0; // Начальная вершина - distances[startIndex] = 0; // Расстояние до самой себя - 0 - algorithms::Dijkstra(startIndex, &graph, distances); - - // Вывод расстояний от стартовой вершины до всех остальных - for (size_t i = 0; i < distances.size(); ++i) { - if (distances[i] == std::numeric_limits::infinity()) { - std::cout << "Расстояние до вершины " << i << ": бесконечность" << std::endl; - } else { - std::cout << "Расстояние до вершины " << i << ": " << distances[i] << std::endl; - } - } - - return 0; -} -``` \ No newline at end of file +This project is licensed under the [MIT license](LICENSE) \ No newline at end of file diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt new file mode 100644 index 0000000..4b5c870 --- /dev/null +++ b/cpp/CMakeLists.txt @@ -0,0 +1,76 @@ +# 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. + + +cmake_minimum_required(VERSION 3.18 FATAL_ERROR) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_CXX_COMPILER_LAUNCHER ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_odr_violation=0 ${CMAKE_CXX_COMPILER_LAUNCHER}) + +include(CheckLanguage) +include(cmake/set_ifndef.cmake) + +set_ifndef(PROJECT_NAME algo_analysis) +project(${PROJECT_NAME} LANGUAGES CXX) + +option(BUILD_TESTS "Build Google tests" ON) +option(BUILD_EXAMPLES "Build examples" ON) +option(BUILD_MICRO_BENCHMARKS "Build C++ micro benchmarks" ON) +option(WARNING_IS_ERROR "Treat all warnings as errors" ON) + +set_ifndef(ALGO_ANALYSIS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") +set_ifndef(ALGO_ANALYSIS_INCLUDE_DIR ${ALGO_ANALYSIS_DIR}/cpp/include) + +if(BUILD_TESTS) + message(STATUS "Building Google tests") +else() + message(STATUS "Not building Google tests") +endif() + +if(BUILD_MICRO_BENCHMARKS) + message(STATUS "Building C++ micro benchmarks") +else() + message(STATUS "Not building C++ micro benchmarks") +endif() + +if(WARNING_IS_ERROR) + message(STATUS "All warnings will be treat as errors") +endif() + +file(GLOB PROJECT_SOURCES ${PROJECT_NAME}/memory/*.cpp) + +if(WARNING_IS_ERROR) + set(CMAKE_COMPILE_WARNING_AS_ERROR ON) +endif() + +# For detecting memory leaks +add_compile_options(-fsanitize=address) +add_compile_options(-fsanitize=undefined) +add_link_options(-fsanitize=address) +add_link_options(-fsanitize=undefined) + +if(BUILD_TESTS) + enable_testing() + add_subdirectory(tests) +endif() + +if(BUILD_MICRO_BENCHMARKS) + add_subdirectory(micro_benchmarks) +endif() + +option(MEASURE_BUILD_TIME "Measure the build time of each module" ON) +if(MEASURE_BUILD_TIME) + set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CMAKE_COMMAND} -E time") + set_property(GLOBAL PROPERTY RULE_LAUNCH_CUSTOM "${CMAKE_COMMAND} -E time") + set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK "${CMAKE_COMMAND} -E time") +endif() \ No newline at end of file diff --git a/cpp/algo_analysis/main.cpp b/cpp/algo_analysis/main.cpp new file mode 100644 index 0000000..cbbedff --- /dev/null +++ b/cpp/algo_analysis/main.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +#include +#include + + +int main() { + EdgesListGraph graph({ + Edge{0, 1, 10}, + Edge{0, 2, 10}, + Edge{1, 3, 10}, + }); + + + Task task({0}, graph); + + DFSAlgorithm algo; + + Task::Statistics stats = task.estimate(algo); + std::cout << "MU: " << stats.initializationMemoryUsage << "\n"; + std::cout << "IT: " << stats.initializationTime << "\n"; + + std::cout << "Hello world!" << "\n"; + +} \ No newline at end of file diff --git a/cpp/algo_analysis/memory.cpp b/cpp/algo_analysis/memory.cpp new file mode 100644 index 0000000..104b3ea --- /dev/null +++ b/cpp/algo_analysis/memory.cpp @@ -0,0 +1,26 @@ +/* + * 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 + +// Redefinition of the allocating memory method +void *operator new(size_t size) { + MemoryBenchmarking::getInstance().totalMemoryAllocated += size; + return ::std::malloc(size); +} + +// Redefinition of the deleting memory method +void operator delete(void *ptr) noexcept { ::std::free(ptr); } diff --git a/cpp/cmake/set_ifndef.cmake b/cpp/cmake/set_ifndef.cmake new file mode 100644 index 0000000..2d8ad8d --- /dev/null +++ b/cpp/cmake/set_ifndef.cmake @@ -0,0 +1,21 @@ +# 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. + +function(set_ifndef variable value) + if(NOT DEFINED ${variable}) + set(${variable} + ${value} + PARENT_SCOPE) + endif() +endfunction() diff --git a/cpp/include/algo_analysis/algorithms/algorithm.hpp b/cpp/include/algo_analysis/algorithms/algorithm.hpp new file mode 100644 index 0000000..6d5bfa3 --- /dev/null +++ b/cpp/include/algo_analysis/algorithms/algorithm.hpp @@ -0,0 +1,15 @@ +#ifndef ALGORITHM_HPP +#define ALGORITHM_HPP + +#include +#include + + +template +struct Algorithm { + virtual void fit(const EdgesListGraph &graph) = 0; + virtual std::vector computeDistances(size_t startIndex) = 0; +}; + +#endif + diff --git a/cpp/include/algo_analysis/algorithms/bfs.hpp b/cpp/include/algo_analysis/algorithms/bfs.hpp new file mode 100644 index 0000000..f7e6d7f --- /dev/null +++ b/cpp/include/algo_analysis/algorithms/bfs.hpp @@ -0,0 +1,74 @@ +#ifndef BFS_HPP +#define BFS_HPP + + +#include +#include +#include +#include + +template +struct BFSAlgorithm : public Algorithm { + using Edge = Edge; + + struct Graph : public AdjacencyListGraph { + using BaseClass = AdjacencyListGraph; + + struct Node { + bool isVisited; + }; + + size_t nodesNumber; + std::vector nodes; + + Graph() : BaseClass() { + + } + + Graph(const std::vector &edges): BaseClass(edges) { + nodesNumber = BaseClass::neighbors.size(); + nodes.resize(nodesNumber, {false}); + } + }; + + + std::vector distances; + Graph graph; + + BFSAlgorithm() { + + } + + void fit(const EdgesListGraph &graph_) override { + graph = Graph(graph_.edges); + } + + std::vector computeDistances(size_t startIndex) override { + distances = std::vector(graph.neighbors.size(), -1); + for (auto &node : graph.nodes) { + node.isVisited = false; + } + std::queue hor; + hor.push(startIndex); + distances[startIndex] = 0; + while (!hor.empty()) { + size_t v = hor.front(); + hor.pop(); + + for (const Edge &edge : graph.neighbors[v]) { + size_t u = edge.toIndex; + if (!graph.nodes[u].isVisited) { + graph.nodes[u].isVisited = true; + distances[u] = distances[v] + edge.weight; + hor.push(u); + } + } + } + return distances; + } + +}; + + + +#endif diff --git a/cpp/include/algo_analysis/algorithms/bfs_with_zeroes.hpp b/cpp/include/algo_analysis/algorithms/bfs_with_zeroes.hpp new file mode 100644 index 0000000..174c78c --- /dev/null +++ b/cpp/include/algo_analysis/algorithms/bfs_with_zeroes.hpp @@ -0,0 +1,75 @@ +#ifndef BFS_WITH_ZEROES_HPP +#define BFS_WITH_ZEROES_HPP + + +#include +#include +#include +#include + +template +struct BFSWithZerosAlgorithm : public Algorithm { + using Edge = Edge; + + struct Graph : public AdjacencyListGraph { + using BaseClass = AdjacencyListGraph; + + struct Node { + bool isVisited; + }; + + size_t nodesNumber; + std::vector nodes; + + Graph() : BaseClass() { + + } + + Graph(const std::vector &edges): BaseClass(edges) { + nodesNumber = BaseClass::neighbors.size(); + nodes.resize(nodesNumber, {false}); + } + }; + + + std::vector distances; + Graph graph; + + BFSWithZerosAlgorithm() { + } + + void fit(const EdgesListGraph &graph_) override { + graph = Graph(graph_.edges); + distances = std::vector(graph.neighbors.size()); + } + + std::vector computeDistances(size_t startIndex) override { + std::deque hor; + hor.push_back(startIndex); + distances[startIndex] = 0; + while (!hor.empty()) { + size_t v = hor.front(); + hor.pop_front(); + + for (const Edge &edge : graph.neighbors[v]) { + size_t u = edge.toIndex; + if (!graph.nodes[u].isVisited) { + graph.nodes[u].isVisited = true; + distances[u] = distances[v] + edge.weight; + if (edge.weight == 0) { + hor.push_front(u); + } else { + hor.push_back(u); + } + } + } + } + return distances; + } + +}; + + + +#endif + diff --git a/cpp/include/algo_analysis/algorithms/dfs.hpp b/cpp/include/algo_analysis/algorithms/dfs.hpp new file mode 100644 index 0000000..8fb79cc --- /dev/null +++ b/cpp/include/algo_analysis/algorithms/dfs.hpp @@ -0,0 +1,71 @@ +#ifndef DFS_HPP +#define DFS_HPP + + +#include +#include +#include + +template +struct DFSAlgorithm : public Algorithm { + + struct Graph : public AdjacencyListGraph { + using BaseClass = AdjacencyListGraph; + + struct Node { + bool isVisited; + }; + + size_t nodesNumber; + std::vector nodes; + + Graph() : BaseClass() { + + } + + Graph(const std::vector> &edges): BaseClass(edges) { + nodesNumber = BaseClass::neighbors.size(); + nodes = std::vector(nodesNumber, {false}); + } + }; + + + std::vector distances; + Graph graph; + + DFSAlgorithm() { + + } + + void fit(const EdgesListGraph &graph_) override { + graph = Graph(graph_.edges); + } + + + void dfs(size_t v) { + for (const Edge &edge : graph.neighbors[v]) { + size_t u = edge.toIndex; + if (!graph.nodes[u].isVisited) { + graph.nodes[u].isVisited = true; + distances[u] = distances[v] + edge.weight; + dfs(u); + } + } + } + + std::vector computeDistances(size_t startIndex) override { + distances = std::vector(graph.neighbors.size(), -1); + for (auto &node : graph.nodes) { + node.isVisited = false; + } + graph.nodes[startIndex].isVisited = true; + distances[startIndex] = 0; + dfs(startIndex); + return distances; + } + +}; + + + +#endif \ No newline at end of file diff --git a/cpp/include/algo_analysis/algorithms/dijkstra.hpp b/cpp/include/algo_analysis/algorithms/dijkstra.hpp new file mode 100644 index 0000000..37f1852 --- /dev/null +++ b/cpp/include/algo_analysis/algorithms/dijkstra.hpp @@ -0,0 +1,77 @@ +#ifndef DIJKSTRA_HPP +#define DIJKSTRA_HPP + + +#include +#include +#include +#include + +template +struct DijkstraAlgorithm : public Algorithm { + using Edge = Edge; + + struct Graph : public AdjacencyListGraph { + using BaseClass = AdjacencyListGraph; + + struct Node { + bool isVisited; + }; + + size_t nodesNumber; + std::vector nodes; + + Graph() : BaseClass() { + + } + + Graph(const std::vector &edges): BaseClass(edges) { + nodesNumber = BaseClass::neighbors.size(); + nodes.resize(nodesNumber, {false}); + } + }; + + + std::vector distances; + Graph graph; + + DijkstraAlgorithm() { + + } + + WeightType inf; + + void fit(const EdgesListGraph &graph_) override { + graph = Graph(graph_.edges); + inf = 1; + for (const Edge &edge : graph_.edges) { + inf += edge.weight; + } + } + + std::vector computeDistances(size_t startIndex) override { + distances = std::vector(graph.neighbors.size(), inf); + distances[startIndex] = 0; + for (int i = 0; i < graph.neighbors.size(); i++) { + int v = -1; + for (int u = 0; u < graph.neighbors.size(); u++) + if (!graph.nodes[u].isVisited && (v == -1 || distances[u] < distances[v])) { + v = u; + } + + graph.nodes[v].isVisited = true; + for (const Edge& edge : graph.neighbors[v]) { + distances[edge.toIndex] = std::min( + distances[edge.toIndex], + distances[v] + edge.weight + ); + } + } + return distances; + } + +}; + + + +#endif diff --git a/cpp/include/algo_analysis/algorithms/ford_bellman.hpp b/cpp/include/algo_analysis/algorithms/ford_bellman.hpp new file mode 100644 index 0000000..792b6a8 --- /dev/null +++ b/cpp/include/algo_analysis/algorithms/ford_bellman.hpp @@ -0,0 +1,85 @@ +#ifndef FORD_BELLMAN_HPP +#define FORD_BELLMAN_HPP + + +#include +#include +#include +#include + +template +struct FordBellmanAlgorithm : public Algorithm { + using Edge = Edge; + + struct Graph : public AdjacencyListGraph { + using BaseClass = AdjacencyListGraph; + + struct Node { + bool isVisited; + }; + + size_t nodesNumber; + std::vector nodes; + + Graph() : BaseClass() { + + } + + Graph(const std::vector &edges): BaseClass(edges) { + nodesNumber = BaseClass::neighbors.size(); + nodes.resize(nodesNumber, {false}); + } + }; + + + std::vector distances; + std::vector inQueue; + Graph graph; + WeightType inf; + + FordBellmanAlgorithm() { + + } + + void fit(const EdgesListGraph &graph_) override { + graph = Graph(graph_.edges); + inf = 1; + for (const Edge &edge : graph_.edges) { + inf += edge.weight; + } + } + + std::vector computeDistances(size_t startIndex) override { + distances = std::vector(graph.neighbors.size(), inf); + inQueue = std::vector(graph.neighbors.size(), false); + distances[startIndex] = 0; + std::queue hor; + hor.push(startIndex); + + while (!hor.empty()) { + size_t v = hor.front(); + hor.pop(); + inQueue[v] = 0; + + for (const Edge &edge : graph.neighbors[v]) { + if (distances[v] + edge.weight < distances[edge.toIndex]) { + distances[edge.toIndex] = distances[v] + edge.weight; + if (!inQueue[edge.toIndex]) { + inQueue[edge.toIndex] = true; + hor.push(edge.toIndex); + } + } + } + } + + for (int i = 0; i < distances.size(); i++) { + distances[i] = distances[i] == inf ? -1 : distances[i]; + } + return distances; + } + +}; + + + +#endif diff --git a/cpp/include/algo_analysis/graphs/adjacency_list_graph.hpp b/cpp/include/algo_analysis/graphs/adjacency_list_graph.hpp new file mode 100644 index 0000000..5e01a36 --- /dev/null +++ b/cpp/include/algo_analysis/graphs/adjacency_list_graph.hpp @@ -0,0 +1,34 @@ +#ifndef ADJACENCY_LIST_GRAPH_HPP +#define ADJACENCY_LIST_GRAPH_HPP + +#include "edge.hpp" +#include + + +template +struct AdjacencyListGraph { + using Edge = Edge; + + std::vector> neighbors; + + AdjacencyListGraph() { + + } + + AdjacencyListGraph(const std::vector &edges) { + size_t maxIndex = 0; + for (const Edge &edge : edges) { + maxIndex = std::max(maxIndex, edge.fromIndex); + maxIndex = std::max(maxIndex, edge.toIndex); + } + + neighbors.resize(maxIndex + 1, std::vector()); + + for (const Edge &edge : edges) { + neighbors[edge.fromIndex].push_back(edge); + } + } +}; + + +#endif \ No newline at end of file diff --git a/cpp/include/algo_analysis/graphs/edge.hpp b/cpp/include/algo_analysis/graphs/edge.hpp new file mode 100644 index 0000000..5ca0154 --- /dev/null +++ b/cpp/include/algo_analysis/graphs/edge.hpp @@ -0,0 +1,16 @@ +#ifndef EDGE_HPP +#define EDGE_HPP + + +#include + + +template +struct Edge { + size_t fromIndex; + size_t toIndex; + WeightType weight; +}; + + +#endif \ No newline at end of file diff --git a/cpp/include/algo_analysis/graphs/edges_list_graph.hpp b/cpp/include/algo_analysis/graphs/edges_list_graph.hpp new file mode 100644 index 0000000..e8fe2bb --- /dev/null +++ b/cpp/include/algo_analysis/graphs/edges_list_graph.hpp @@ -0,0 +1,38 @@ +#ifndef EDGES_LIST_GRAPH_HPP +#define EDGES_LIST_GRAPH_HPP + +#include "edge.hpp" +#include + + +template +struct EdgesListGraph { + std::vector> edges; + + EdgesListGraph() {} + + EdgesListGraph(const std::vector> &edges) : edges(edges) { + } + + + void serialize(std::ostream &stream) { + stream << edges.size() << '\n'; + for (const Edge &edge : edges) { + stream << edge.fromIndex << " --> " << edge.toIndex << " w: " << edge.weight << '\n'; + } + } + + void deserialize(std::istream &stream) { + size_t numOfEdges; + stream >> numOfEdges; + edges = std::vector>(numOfEdges); + for (int i = 0; i < numOfEdges; i++) { + std::string separator; + stream >> edges[i].fromIndex >> separator >> edges[i].toIndex >> separator >> edges[i].weight; + } + + } +}; + + +#endif diff --git a/cpp/include/algo_analysis/memory/memory.hpp b/cpp/include/algo_analysis/memory/memory.hpp new file mode 100644 index 0000000..c1362a4 --- /dev/null +++ b/cpp/include/algo_analysis/memory/memory.hpp @@ -0,0 +1,63 @@ +/* + * 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. + */ + +#ifndef MEMORY_HPP +#define MEMORY_HPP + +#include +#include + +// Memory benchmarking global class +class MemoryBenchmarking { +private: + size_t totalMemoryAllocated; // Total bytes of allocated memory + + /// @brief Class default constructor + explicit MemoryBenchmarking() = default; + + MemoryBenchmarking(MemoryBenchmarking const &) = delete; + MemoryBenchmarking &operator=(MemoryBenchmarking const &) = delete; + + /// @brief Class default destructor + ~MemoryBenchmarking() = default; + +public: + /// @brief Getter of instance of memory benchmarking class + /// @return static link to the instance of this class + static MemoryBenchmarking &getInstance() { + static MemoryBenchmarking memoryBenchmarkingInstance; + + return memoryBenchmarkingInstance; + } + + /// @brief Getter of the total number of allocated bytes value + /// @return total number of allocated bytes value + size_t getTotalMemoryAllocated() const noexcept { + return totalMemoryAllocated; + } + + // Friends methods of this class + friend void *operator new(size_t size); + friend void operator delete(void *ptr) noexcept; +}; + +// Redefinition of the allocating memory method +void *operator new(size_t size); + +// Redefinition of the deleting memory method +void operator delete(void *ptr) noexcept; + +#endif // MEMORY_HPP \ No newline at end of file diff --git a/cpp/include/algo_analysis/task.hpp b/cpp/include/algo_analysis/task.hpp new file mode 100644 index 0000000..6b003c0 --- /dev/null +++ b/cpp/include/algo_analysis/task.hpp @@ -0,0 +1,143 @@ +/* + * 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. + */ + +#ifndef TASK_HPP +#define TASK_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +template +struct Task{ + + uint64_t getTimeInMs() noexcept { + using namespace std::chrono; + return duration_cast(system_clock::now().time_since_epoch()) + .count(); + } + + EdgesListGraph graph; + std::vector startIndexes; + + + struct Statistics { + size_t initializationMemoryUsage; + std::vector computationMemoryUsage; + uint64_t initializationTime; + std::vector computationTime; + }; + + + Task(const std::string &filePath) { + fromFile(filePath); + } + + Task(const std::vector &startIndexes_, const EdgesListGraph &graph_) : graph(graph_), startIndexes(startIndexes_) { + } + + + void serialize(std::ostream &stream) { + graph.serialize(stream); + + stream << startIndexes.size() << '\n'; + for (int i = 0; i < startIndexes.size(); i++) { + stream << startIndexes[i] << '\n'; + } + } + + void deserialize(std::istream &stream) { + graph.deserialize(stream); + + size_t numberOfStartIndexes; + stream >> numberOfStartIndexes; + startIndexes = std::vector(numberOfStartIndexes); + + for (int i = 0; i < numberOfStartIndexes; i++) { + stream >> startIndexes[i]; + } + } + + void toFile(std::string &filePath) { + std::ofstream file(filePath); + serialize(file); + } + + void fromFile(const std::string &filePath) { + std::ifstream file(filePath); + if (!file.is_open()) { + std::cerr << "Ошибка: Не удалось открыть файл " << filePath << std::endl; + return; + } + deserialize(file); + } + + std::vector> run(Algorithm &algorithm) { + std::vector> result; + algorithm.fit(graph); + for (size_t startIndex : startIndexes) { + result.push_back(algorithm.computeDistances(startIndex)); + } + return result; + } + + Statistics estimate(Algorithm &algorithm) { + Statistics stats; + + double initializationStartMemory = MemoryBenchmarking::getInstance().getTotalMemoryAllocated(); + double initializationStartTime = static_cast(getTimeInMs()); + algorithm.fit(graph); + stats.initializationTime = static_cast(getTimeInMs()) - initializationStartTime; + stats.initializationMemoryUsage = static_cast( + MemoryBenchmarking::getInstance().getTotalMemoryAllocated() - initializationStartMemory + ) / 1000; + + for (size_t startIndex : startIndexes) { + double computationStartMemory = MemoryBenchmarking::getInstance().getTotalMemoryAllocated(); + double computationStartTime = static_cast(getTimeInMs()); + + auto res = algorithm.computeDistances(startIndex); + + stats.computationTime.push_back( + static_cast(getTimeInMs()) - computationStartTime + ); + stats.computationMemoryUsage.push_back( + static_cast( + MemoryBenchmarking::getInstance().getTotalMemoryAllocated() - computationStartMemory + ) / 1000 + ); + + // for (WeightType dist : res) { + // std::cout << "\t" << dist; + // } + } + return stats; + } + +}; + + + + +#endif // GRAPH_HPP diff --git a/cpp/micro_benchmarks/.data/task_01.txt b/cpp/micro_benchmarks/.data/task_01.txt new file mode 100644 index 0000000..e328ffc --- /dev/null +++ b/cpp/micro_benchmarks/.data/task_01.txt @@ -0,0 +1,6 @@ +3 +0 --> 1 w: 10 +0 --> 2 w: 10 +1 --> 3 w: 10 +1 +0 diff --git a/cpp/micro_benchmarks/CMakeLists.txt b/cpp/micro_benchmarks/CMakeLists.txt new file mode 100644 index 0000000..8fc71f0 --- /dev/null +++ b/cpp/micro_benchmarks/CMakeLists.txt @@ -0,0 +1,50 @@ +# 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. + +# Google Benchmark Preparation - Same as google test ../tests/CMakeLists.txt +# Google Benchmark is provided under Apache-2.0 license +include(FetchContent) +set(BENCHMARK_ENABLE_TESTING + OFF + CACHE INTERNAL "Disable google-benchmark tests") +set(BENCHMARK_ENABLE_INSTALL + OFF + CACHE INTERNAL "Disable google-benchmark install") +FetchContent_Declare( + googlebenchmark + GIT_REPOSITORY https://github.com/google/benchmark.git + GIT_TAG v1.8.3) +FetchContent_MakeAvailable(googlebenchmark) + +add_custom_target(micro_benchmarks) + +include_directories(SYSTEM ${ALGO_ANALYSIS_INCLUDE_DIR}) + +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_compile_features(${test_name} PRIVATE cxx_std_17) + target_compile_definitions(${test_name} PUBLIC TOP_LEVEL_DIR="${TOP_LEVEL_DIR}") + + add_dependencies(micro_benchmarks ${test_name}) +endfunction() + +add_benchmark(DFSBenchmark dfs.cpp) +add_benchmark(BFSBenchmark bfs.cpp) +add_benchmark(DijkstraBenchmark dijkstra.cpp) +add_benchmark(FordBellmanBenchmark ford_bellman.cpp) diff --git a/cpp/micro_benchmarks/benchmarks_results/MacOS/bfs.json b/cpp/micro_benchmarks/benchmarks_results/MacOS/bfs.json new file mode 100644 index 0000000..bc5a536 --- /dev/null +++ b/cpp/micro_benchmarks/benchmarks_results/MacOS/bfs.json @@ -0,0 +1,104 @@ +{ + "context": { + "date": "2025-02-26T17:56:12+03:00", + "host_name": "MacBook-Air-2.local", + "executable": "./BFSBenchmark", + "num_cpus": 8, + "mhz_per_cpu": 24, + "cpu_scaling_enabled": false, + "caches": [ + { + "type": "Data", + "level": 1, + "size": 65536, + "num_sharing": 0 + }, + { + "type": "Instruction", + "level": 1, + "size": 131072, + "num_sharing": 0 + }, + { + "type": "Unified", + "level": 2, + "size": 4194304, + "num_sharing": 1 + } + ], + "load_avg": [1.83154,1.93066,1.96143], + "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": 12436, + "real_time": 8.0200349141062674e+04, + "cpu_time": 5.6555644901897707e+04, + "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": 13200, + "real_time": 7.8331227872915799e+04, + "cpu_time": 5.4633636363636339e+04, + "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": 13169, + "real_time": 7.6109800141649001e+04, + "cpu_time": 5.2569747133419391e+04, + "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": 13068, + "real_time": 7.8417113021456273e+04, + "cpu_time": 5.4253367003367064e+04, + "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": 13363, + "real_time": 7.7376381351859149e+04, + "cpu_time": 5.4403651874579104e+04, + "time_unit": "ns" + } + ] +} diff --git a/cpp/micro_benchmarks/benchmarks_results/MacOS/dfs.json b/cpp/micro_benchmarks/benchmarks_results/MacOS/dfs.json new file mode 100644 index 0000000..6031ad6 --- /dev/null +++ b/cpp/micro_benchmarks/benchmarks_results/MacOS/dfs.json @@ -0,0 +1,104 @@ +{ + "context": { + "date": "2025-02-26T17:55:58+03:00", + "host_name": "MacBook-Air-2.local", + "executable": "./DFSBenchmark", + "num_cpus": 8, + "mhz_per_cpu": 24, + "cpu_scaling_enabled": false, + "caches": [ + { + "type": "Data", + "level": 1, + "size": 65536, + "num_sharing": 0 + }, + { + "type": "Instruction", + "level": 1, + "size": 131072, + "num_sharing": 0 + }, + { + "type": "Unified", + "level": 2, + "size": 4194304, + "num_sharing": 1 + } + ], + "load_avg": [1.80127,1.92871,1.96143], + "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": 12599, + "real_time": 8.2898044130654831e+04, + "cpu_time": 5.6991586633859850e+04, + "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": 13196, + "real_time": 7.8754272131886188e+04, + "cpu_time": 5.4931494392240085e+04, + "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": 13009, + "real_time": 7.7658604964521131e+04, + "cpu_time": 5.4300868629410434e+04, + "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": 13036, + "real_time": 7.7826934339005384e+04, + "cpu_time": 5.4946379257440996e+04, + "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": 13126, + "real_time": 7.7622266874823035e+04, + "cpu_time": 5.4188023769617561e+04, + "time_unit": "ns" + } + ] +} diff --git a/cpp/micro_benchmarks/benchmarks_results/MacOS/dijkstra.json b/cpp/micro_benchmarks/benchmarks_results/MacOS/dijkstra.json new file mode 100644 index 0000000..8127e0c --- /dev/null +++ b/cpp/micro_benchmarks/benchmarks_results/MacOS/dijkstra.json @@ -0,0 +1,104 @@ +{ + "context": { + "date": "2025-02-26T17:57:03+03:00", + "host_name": "MacBook-Air-2.local", + "executable": "./DijkstraBenchmark", + "num_cpus": 8, + "mhz_per_cpu": 24, + "cpu_scaling_enabled": false, + "caches": [ + { + "type": "Data", + "level": 1, + "size": 65536, + "num_sharing": 0 + }, + { + "type": "Instruction", + "level": 1, + "size": 131072, + "num_sharing": 0 + }, + { + "type": "Unified", + "level": 2, + "size": 4194304, + "num_sharing": 1 + } + ], + "load_avg": [2.53662,2.14404,2.03906], + "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": 12973, + "real_time": 8.0700576818180372e+04, + "cpu_time": 5.6072304016033289e+04, + "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": 12803, + "real_time": 7.8344889867014674e+04, + "cpu_time": 5.5012887604467694e+04, + "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": 13220, + "real_time": 7.7395716711312372e+04, + "cpu_time": 5.3308547655068112e+04, + "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": 10000, + "real_time": 7.7917258394882083e+04, + "cpu_time": 5.4159699999999990e+04, + "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": 12557, + "real_time": 7.6436648886474883e+04, + "cpu_time": 5.3909054710520068e+04, + "time_unit": "ns" + } + ] +} diff --git a/cpp/micro_benchmarks/benchmarks_results/MacOS/ford_bellman.json b/cpp/micro_benchmarks/benchmarks_results/MacOS/ford_bellman.json new file mode 100644 index 0000000..ab4e3d0 --- /dev/null +++ b/cpp/micro_benchmarks/benchmarks_results/MacOS/ford_bellman.json @@ -0,0 +1,104 @@ +{ + "context": { + "date": "2025-02-26T17:56:35+03:00", + "host_name": "MacBook-Air-2.local", + "executable": "./FordBellmanBenchmark", + "num_cpus": 8, + "mhz_per_cpu": 24, + "cpu_scaling_enabled": false, + "caches": [ + { + "type": "Data", + "level": 1, + "size": 65536, + "num_sharing": 0 + }, + { + "type": "Instruction", + "level": 1, + "size": 131072, + "num_sharing": 0 + }, + { + "type": "Unified", + "level": 2, + "size": 4194304, + "num_sharing": 1 + } + ], + "load_avg": [2.63525,2.12402,2.02979], + "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": 12306, + "real_time": 7.8749634328834232e+04, + "cpu_time": 5.4876726799934979e+04, + "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": 12835, + "real_time": 7.9258008719282967e+04, + "cpu_time": 5.5301441371250468e+04, + "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": 13057, + "real_time": 7.7892563989040878e+04, + "cpu_time": 5.4809221107451951e+04, + "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": 12884, + "real_time": 7.8217437056675946e+04, + "cpu_time": 5.4708242781744848e+04, + "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": 13099, + "real_time": 7.7482823122365851e+04, + "cpu_time": 5.4304527063134592e+04, + "time_unit": "ns" + } + ] +} diff --git a/cpp/micro_benchmarks/bfs.cpp b/cpp/micro_benchmarks/bfs.cpp new file mode 100644 index 0000000..99fb119 --- /dev/null +++ b/cpp/micro_benchmarks/bfs.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 +#include + + +void pointQuery(int maxCapacity) { + Task task( + "../micro_benchmarks/.data/task_01.txt" + ); + + BFSAlgorithm bfs; + + task.run(bfs); +} + +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/dfs.cpp b/cpp/micro_benchmarks/dfs.cpp new file mode 100644 index 0000000..369399e --- /dev/null +++ b/cpp/micro_benchmarks/dfs.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 +#include + + +void pointQuery(int maxCapacity) { + Task task( + "../tests/.data/task_01.txt" + ); + + DFSAlgorithm dfs; + + task.run(dfs); +} + +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/dijkstra.cpp b/cpp/micro_benchmarks/dijkstra.cpp new file mode 100644 index 0000000..a99f867 --- /dev/null +++ b/cpp/micro_benchmarks/dijkstra.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" + ); + + DijkstraAlgorithm 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/ford_bellman.cpp b/cpp/micro_benchmarks/ford_bellman.cpp new file mode 100644 index 0000000..630e3f2 --- /dev/null +++ b/cpp/micro_benchmarks/ford_bellman.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" + ); + + FordBellmanAlgorithm fordBellman; + + task.run(fordBellman); +} + +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/.data/task_01.txt b/cpp/tests/.data/task_01.txt new file mode 100644 index 0000000..32c6a35 --- /dev/null +++ b/cpp/tests/.data/task_01.txt @@ -0,0 +1,6 @@ +3 +0 --> 1 w: 10 +0 --> 2 w: 10 +1 --> 3 w: 10 +1 +0 \ No newline at end of file diff --git a/cpp/tests/.data/task_cycle_with_extra_edges.txt b/cpp/tests/.data/task_cycle_with_extra_edges.txt new file mode 100644 index 0000000..286d58f --- /dev/null +++ b/cpp/tests/.data/task_cycle_with_extra_edges.txt @@ -0,0 +1,12 @@ +8 +0 --> 1 w: 6 +1 --> 2 w: 1 +2 --> 3 w: 4 +3 --> 4 w: 3 +4 --> 5 w: 2 +5 --> 0 w: 5 +0 --> 4 w: 7 +2 --> 5 w: 8 +2 +0 +1 \ No newline at end of file diff --git a/cpp/tests/.data/task_full_graph.txt b/cpp/tests/.data/task_full_graph.txt new file mode 100644 index 0000000..6db032b --- /dev/null +++ b/cpp/tests/.data/task_full_graph.txt @@ -0,0 +1,10 @@ +6 +0 --> 1 w: 7 +0 --> 2 w: 1 +1 --> 2 w: 3 +1 --> 3 w: 9 +2 --> 3 w: 4 +0 --> 3 w: 2 +2 +0 +1 diff --git a/cpp/tests/.data/task_full_graph_ones_and_zeroes.txt b/cpp/tests/.data/task_full_graph_ones_and_zeroes.txt new file mode 100644 index 0000000..79dc65e --- /dev/null +++ b/cpp/tests/.data/task_full_graph_ones_and_zeroes.txt @@ -0,0 +1,11 @@ +6 +0 --> 1 w: 1 +0 --> 2 w: 1 +1 --> 2 w: 1 +1 --> 3 w: 1 +2 --> 3 w: 1 +0 --> 3 w: 1 +2 +0 +1 + diff --git a/cpp/tests/.data/task_full_graph_only_ones.txt b/cpp/tests/.data/task_full_graph_only_ones.txt new file mode 100644 index 0000000..79dc65e --- /dev/null +++ b/cpp/tests/.data/task_full_graph_only_ones.txt @@ -0,0 +1,11 @@ +6 +0 --> 1 w: 1 +0 --> 2 w: 1 +1 --> 2 w: 1 +1 --> 3 w: 1 +2 --> 3 w: 1 +0 --> 3 w: 1 +2 +0 +1 + diff --git a/cpp/tests/.data/task_several_components.txt b/cpp/tests/.data/task_several_components.txt new file mode 100644 index 0000000..9c9b854 --- /dev/null +++ b/cpp/tests/.data/task_several_components.txt @@ -0,0 +1,14 @@ +10 +0 --> 1 w: 2 +1 --> 2 w: 5 +1 --> 3 w: 2 +4 --> 5 w: 3 +5 --> 6 w: 6 +6 --> 4 w: 1 +7 --> 8 w: 4 +8 --> 9 w: 9 +9 --> 7 w: 3 +0 --> 4 w: 7 +2 +0 +4 \ No newline at end of file diff --git a/cpp/tests/.data/task_tree.txt b/cpp/tests/.data/task_tree.txt new file mode 100644 index 0000000..d96bb19 --- /dev/null +++ b/cpp/tests/.data/task_tree.txt @@ -0,0 +1,9 @@ +6 +0 --> 1 w: 5 +0 --> 2 w: 3 +1 --> 3 w: 8 +1 --> 4 w: 2 +2 --> 5 w: 7 +2 --> 6 w: 4 +1 +0 \ No newline at end of file diff --git a/cpp/tests/.data/task_tree_only_ones.txt b/cpp/tests/.data/task_tree_only_ones.txt new file mode 100644 index 0000000..7661ff7 --- /dev/null +++ b/cpp/tests/.data/task_tree_only_ones.txt @@ -0,0 +1,9 @@ +6 +0 --> 1 w: 1 +0 --> 2 w: 1 +1 --> 3 w: 1 +1 --> 4 w: 1 +2 --> 5 w: 1 +2 --> 6 w: 1 +1 +0 diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt new file mode 100644 index 0000000..a1e68e1 --- /dev/null +++ b/cpp/tests/CMakeLists.txt @@ -0,0 +1,33 @@ +# 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. + +# GoogleTest Preparation - Code block copied from +# https://google.github.io/googletest/quickstart-cmake.html +include(FetchContent) +FetchContent_Declare( + googletest + GIT_REPOSITORY + https://github.com/google/googletest.git + GIT_TAG v1.15.2) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set_ifndef(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +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 diff --git a/cpp/tests/algorithms/bfs_test.cpp b/cpp/tests/algorithms/bfs_test.cpp new file mode 100644 index 0000000..8ebfe7a --- /dev/null +++ b/cpp/tests/algorithms/bfs_test.cpp @@ -0,0 +1,64 @@ +/* + * 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(BFS, Task01) { + + Task task( + "../tests/.data/task_01.txt" + ); + BFSAlgorithm bfs; + auto results = task.run(bfs); + std::vector> expectedResult = { + {0, 10, 10, 20}, + }; + + EXPECT_EQ(results, expectedResult); +} + +TEST(BFS, TaskTree) { + + Task task( + "../tests/.data/task_tree_only_ones.txt" + ); + BFSAlgorithm dfs; + auto results = task.run(dfs); + std::vector> expectedResult = { + { 0, 1, 1, 2, 2, 2, 2 } + }; + + EXPECT_EQ(results, expectedResult); +} + +TEST(BFS, TaskFullGraph) { + + Task task( + "../tests/.data/task_full_graph_only_ones.txt" + ); + BFSAlgorithm algo; + auto results = task.run(algo); + std::vector> expectedResult = { + { 0, 1, 1, 1 }, { -1, 0, 1, 1 } + }; + + EXPECT_EQ(results, expectedResult); +} \ No newline at end of file diff --git a/cpp/tests/algorithms/bfs_with_zeroes_test.cpp b/cpp/tests/algorithms/bfs_with_zeroes_test.cpp new file mode 100644 index 0000000..441da4f --- /dev/null +++ b/cpp/tests/algorithms/bfs_with_zeroes_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 + + +// Demonstrate some basic assertions. +TEST(BFSWithZeroes, Task01) { + + Task task( + "../tests/.data/task_01.txt" + ); + BFSWithZerosAlgorithm bfs; + auto results = task.run(bfs); + std::vector> expectedResult = { + {0, 10, 10, 20}, + }; + + EXPECT_EQ(results, expectedResult); +} + + +TEST(BFSWithZeroes, TaskFullGraph) { + + Task task( + "../tests/.data/task_full_graph_ones_and_zeroes.txt" + ); + BFSWithZerosAlgorithm algo; + auto results = task.run(algo); + std::vector> expectedResult = { + { { 0, 1, 1, 1 }, { 0, 0, 1, 1 } } + }; + + EXPECT_EQ(results, expectedResult); +} \ No newline at end of file diff --git a/cpp/tests/algorithms/dfs_test.cpp b/cpp/tests/algorithms/dfs_test.cpp new file mode 100644 index 0000000..d6ddb47 --- /dev/null +++ b/cpp/tests/algorithms/dfs_test.cpp @@ -0,0 +1,94 @@ +/* + * 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(DFS, Task01) { + + Task task( + "../tests/.data/task_01.txt" + ); + DFSAlgorithm dfs; + auto results = task.run(dfs); + std::vector> expectedResult = { + {0, 10, 10, 20}, + }; + + EXPECT_EQ(results, expectedResult); +} + +TEST(DFS, TaskTree) { + + Task task( + "../tests/.data/task_tree.txt" + ); + DFSAlgorithm dfs; + auto results = task.run(dfs); + std::vector> expectedResult = { + { 0, 5, 3, 13, 7, 10, 7 } + }; + + EXPECT_EQ(results, expectedResult); +} + + +TEST(DFS, TaskFullGraph) { + + Task task( + "../tests/.data/task_full_graph.txt" + ); + DFSAlgorithm dfs; + auto results = task.run(dfs); + std::vector> expectedResult = { + { 0, 7, 10, 14 }, { -1, 0, 3, 7 } + }; + + EXPECT_EQ(results, expectedResult); +} + + +TEST(DFS, TaskSeveralComponents) { + + Task task( + "../tests/.data/task_several_components.txt" + ); + DFSAlgorithm dfs; + auto results = task.run(dfs); + 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(DFS, TaskCycleWithExtraEdges) { + + Task task( + "../tests/.data/task_cycle_with_extra_edges.txt" + ); + DFSAlgorithm dfs; + auto results = task.run(dfs); + std::vector> expectedResult = { + { 0, 6, 7, 11, 14, 16 }, { 15, 0, 1, 5, 8, 10 } + }; + + EXPECT_EQ(results, expectedResult); +} \ No newline at end of file diff --git a/cpp/tests/algorithms/dijkstra_test.cpp b/cpp/tests/algorithms/dijkstra_test.cpp new file mode 100644 index 0000000..ecc4659 --- /dev/null +++ b/cpp/tests/algorithms/dijkstra_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(Dijkstra, Task01) { + + Task task( + "../tests/.data/task_01.txt" + ); + DijkstraAlgorithm dijkstra; + auto results = task.run(dijkstra); + std::vector> expectedResult = { + {0, 10, 10, 20}, + }; + + EXPECT_EQ(results, expectedResult); +} + + +TEST(Dijkstra, TaskTree) { + + Task task( + "../tests/.data/task_tree.txt" + ); + DijkstraAlgorithm 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/ford_bellman_test.cpp b/cpp/tests/algorithms/ford_bellman_test.cpp new file mode 100644 index 0000000..f84d494 --- /dev/null +++ b/cpp/tests/algorithms/ford_bellman_test.cpp @@ -0,0 +1,96 @@ +/* + * 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 + + +// Demonstrate some basic assertions. +TEST(FordBellman, Task01) { + + Task task( + "../tests/.data/task_01.txt" + ); + FordBellmanAlgorithm fordBellman; + auto results = task.run(fordBellman); + std::vector> expectedResult = { + {0, 10, 10, 20}, + }; + + EXPECT_EQ(results, expectedResult); +} + + +TEST(FordBellman, TaskTree) { + + Task task( + "../tests/.data/task_tree.txt" + ); + FordBellmanAlgorithm algo; + auto results = task.run(algo); + std::vector> expectedResult = { + { 0, 5, 3, 13, 7, 10, 7 } + }; + + EXPECT_EQ(results, expectedResult); +} + + +TEST(FordBellman, TaskFullGraph) { + + Task task( + "../tests/.data/task_full_graph.txt" + ); + FordBellmanAlgorithm algo; + auto results = task.run(algo); + std::vector> expectedResult = { + { 0, 7, 1, 2 }, { -1, 0, 3, 7 } + }; + + EXPECT_EQ(results, expectedResult); +} + + +TEST(FordBellman, TaskSeveralComponents) { + + Task task( + "../tests/.data/task_several_components.txt" + ); + FordBellmanAlgorithm algo; + auto results = task.run(algo); + 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(FordBellman, TaskCycleWithExtraEdges) { + + Task task( + "../tests/.data/task_cycle_with_extra_edges.txt" + ); + FordBellmanAlgorithm algo; + auto results = task.run(algo); + 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 diff --git a/cpp/tests/graphs/edges_list_graph.cpp b/cpp/tests/graphs/edges_list_graph.cpp new file mode 100644 index 0000000..b2bd2e9 --- /dev/null +++ b/cpp/tests/graphs/edges_list_graph.cpp @@ -0,0 +1,63 @@ +/* + * 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 + + +TEST(EdgesListGraph1, Serialization) { + EdgesListGraph graph({ + Edge{0, 1, 10}, + Edge{0, 2, 10}, + Edge{1, 3, 10}, + }); + + std::stringstream ss; + graph.serialize(ss); + + EXPECT_EQ(ss.str(), std::string("3\n0 --> 1 w: 10\n" +"0 --> 2 w: 10\n" +"1 --> 3 w: 10\n")); + +} + +TEST(EdgesListGraph1, Deserialization) { + + EdgesListGraph graph; + + std::stringstream ss; + ss << std::string("3\n0 --> 1 w: 10\n" +"0 --> 2 w: 10\n" +"1 --> 3 w: 10\n"); + + graph.deserialize(ss); + + std::vector> expectedGraph = { + {0, 1, 10}, + {0, 2, 10}, + {1, 3, 10} + }; + + for (size_t i = 0; i < graph.edges.size(); i++) { + EXPECT_EQ(graph.edges[i].fromIndex, expectedGraph[i][0]); + EXPECT_EQ(graph.edges[i].toIndex, expectedGraph[i][1]); + EXPECT_EQ(graph.edges[i].weight, expectedGraph[i][2]); + } +} \ No newline at end of file diff --git a/cpp/tests/task/task_test.cpp b/cpp/tests/task/task_test.cpp new file mode 100644 index 0000000..b8b6afa --- /dev/null +++ b/cpp/tests/task/task_test.cpp @@ -0,0 +1,72 @@ +/* + * 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 + + +TEST(Task, Serialization) { + EdgesListGraph graph({ + Edge{0, 1, 10}, + Edge{0, 2, 10}, + Edge{1, 3, 10}, + }); + + Task task({0, 1, 2}, graph); + + std::stringstream ss; + + task.serialize(ss); + + EXPECT_EQ(ss.str(), std::string("3\n0 --> 1 w: 10\n" +"0 --> 2 w: 10\n" +"1 --> 3 w: 10\n" +"3\n0\n1\n2\n")); + +} + +TEST(Task, Deserialization) { + + Task task({}, EdgesListGraph{}); + + std::stringstream ss; + ss << std::string("3\n0 --> 1 w: 10\n" +"0 --> 2 w: 10\n" +"1 --> 3 w: 10\n" +"3\n0\n1\n2\n"); + + task.deserialize(ss); + + std::vector expected = {0, 1, 2}; + EXPECT_EQ(task.startIndexes, expected); + + + std::vector> expectedGraph = { + {0, 1, 10}, + {0, 2, 10}, + {1, 3, 10} + }; + for (size_t i = 0; i < task.graph.edges.size(); i++) { + EXPECT_EQ(task.graph.edges[i].fromIndex, expectedGraph[i][0]); + EXPECT_EQ(task.graph.edges[i].toIndex, expectedGraph[i][1]); + EXPECT_EQ(task.graph.edges[i].weight, expectedGraph[i][2]); + } + +} \ No newline at end of file diff --git a/docs/architecture.md b/docs/architecture.md index 7230647..56b7aea 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -7,59 +7,46 @@ ## Структура библиотеки ### 1. Основные компоненты +- Task - представляет собой граф в виде списка ребер, а также множество вершин, от которых предстоит найти расстояние до других вершин. +- Algorithm - базовый класс для алгоритмов. Потомки должны реализовывать две функции fit (подстраивание алгоритма для работы с графом) и computeDistances (поиск расстояний от выделенной вершины) +- EdgesListGraph и AdjacencyListGraph - представляют собой графы, заданные с помощью списка ребер и списка связности соответственно. -- GeneralGraph: Класс для представления графа. - - GeneralNode: Класс для представления вершин. - - GeneralEdge: Класс для представления ребер. -- algorithms: namespace с функциями различных алгоритмов - - FordBellman: Реализация алгоритма Форда-Беллмана. ### 2. Классы, их поля и формат заголовков алгоритмов -#### 2.1 GeneralGraph +#### 2.1 Algorithm +```c++ +template +struct Algorithm { + virtual void fit(const EdgesListGraph &graph) = 0; + virtual std::vector computeDistances(size_t startIndex) = 0; +}; ``` -template -class GeneralGraph { -private: - std::vector >> - edges; // All edges of the graph - std::vector >> - nodes; // All nodes of the graph - -``` -##### 2.1.1 GeneralNode -``` -template -class GeneralNode { -private: - std::vector *, size_t>> - neighbors; // Node neighbors vector - T value; // Value of the node - size_t index; // Index of the node - size_t capacity; // +##### 2.2 AdjacencyListGraph +```c++ +template +struct AdjacencyListGraph { + std::vector> neighbors; + + AdjacencyListGraph(const std::vector &edges); +}; ``` -##### 2.1.2 GeneralEdge -``` -template -class GeneralEdge { -private: - size_t start, end; // Edge's ends - T weight; // Edge's weight -``` -#### 2.2 -``` -namespace algorithms { -/// @brief Ford Bellman algorithm for SSSP -/// @tparam T type for graph's weights and distances -/// @param startIndex start index for the SSSP -/// @param g graph itself -/// @param distances vector of distances from start point to all others -template -void FordBellman(size_t startIndex, GeneralGraph *g, - std::vector &distances); -} +##### 2.3 EdgesListGraph +```c++ +template +struct EdgesListGraph { + std::vector> edges; + + EdgesListGraph(const std::vector> &edges) : edges(edges); + + + void serialize(std::ostream &stream); + + void deserialize(std::istream &stream); +}; + ``` @@ -71,44 +58,36 @@ void FordBellman(size_t startIndex, GeneralGraph *g, - Анализ сложности в зависимости от структуры графа. ### 2. Структура экспериментальной среды -- Task: Виртуальный класс задачи - - SSSP: Наследник Task. Окружение тестирования задачи SSSP на разных алгоритмах - - APSP: Наследник Task. Окружение тестирования задачи APSP +- Task - представляет собой граф в виде списка ребер, а также множество вершин, от которых предстоит найти расстояние до других вершин. Позволяет запускать вычисления алгоритма с помощью команды run, а также запускать вычисления алгоритма со сбором статистики (используемой памяти и времени работы). #### 2.1 class Task -``` -class Task { - public: - /// @brief Run task method - virtual void run(int logLevel) = 0; - - /// @brief Class destructor - virtual ~Task() = default; -}; // End of class 'Task' -``` +```c++ +struct Task{ -##### 2.1.1 class SSSP -``` -template class SSSP : public Task { -public: - SSSP(const std::vector *> &graphs_, const std::vector &startNodeIndexes_, - const std::function *, std::vector &)> &algorithm_); - - void run(int logLevel = 1) override; -} -``` + struct Statistics { + size_t initializationMemoryUsage; + std::vector computationMemoryUsage; + uint64_t initializationTime; + std::vector computationTime; + }; -##### 2.1.2 class APSP -``` -template class APSP : public Task { -public: - APSP(const std::vector *> &graphs_, - std::function *, std::vector> &)> algorithm_) { - - void run(int logLevel = 1) override; -} + Task(const std::string &filePath); + + Task(const std::vector &startIndexes_, const EdgesListGraph &graph_); + + + void toFile(std::string &filePath); + + void fromFile(const std::string &filePath); + + std::vector> run(Algorithm &algorithm); + + Statistics estimate(Algorithm &algorithm); + +}; ``` + #### 2.2 Параметры эксперимента - Тип графа (смешанный, ориентированный, взвешенный). diff --git a/docs/requirements.md b/docs/requirements.md index cb79a6a..0c72ac1 100644 --- a/docs/requirements.md +++ b/docs/requirements.md @@ -1,21 +1,16 @@ ## **Требования** 1. Требования к алгоритмам: - - Библиотека должна предоставлять возможность находить кратчайшие пути от заданной вершины до всех остальных вершин в графе всеми алгоритмами перечисленным в [файле](algorithms.md), кроме Froid'a. - - Библиотека должна корректно обеспечивать возможность расчета кратчайших путей между всеми парами вершин графа алгоритмом Floid. - - Поддержка как ориентированных, так и неориентированных графов. + - Библиотека должна предоставлять возможность находить кратчайшие пути от заданной вершины до всех остальных вершин в графе всеми алгоритмами перечисленным в [файле](algorithms.md). - Алгоритмы должны удовлетворять асимптотику, в соответствии с таблицей из [документа с алгоритмами](algorithms.md) - Алгоритмы должны поддерживать работу с разными весами, в соответствии с таблицей из [документа с алгоритмами](algorithms.md) 2. Требования к структурам - - Поддержка хранения как ориентированных, так и неориентированных графов. - - Поддержка хранения как взвешенный, так и не взвешенных графов - - Поддержка хранения графов со значениями в вершинах, так и без них - Библиотека должна хранить граф в виде списка смежностей и списка ребер - Библиотека должна принимать граф в указанном в документации формате 3. Требование к окружению - - Окружение должно поддерживать сравнение алгоритмов, выполняющих одинаковые задачи, повремени на разных видах графов и с разными весами. + - Окружение должно поддерживать сравнение алгоритмов, выполняющих одинаковые задачи, по времени на разных видах графов и с разными весами. ## **Приемка** - Критерий 1: Структуры: @@ -23,7 +18,6 @@ - Критерий 2: Алгоритмы: - Алгоритмы должны давать 100% точность на графах до 5000 вершин. - Критерий 3: Обработка ошибок: - - Библиотека должна корректно обрабатывать случаи, когда граф содержит циклы с отрицательными весами и выдавать соответствующее сообщение об ошибке. - Библиотека должны корректно отрабатывать случаи неправильного формата файлов и выдавать соответствующие сообщение diff --git a/examples/.data/task_01.txt b/examples/.data/task_01.txt new file mode 100644 index 0000000..32c6a35 --- /dev/null +++ b/examples/.data/task_01.txt @@ -0,0 +1,6 @@ +3 +0 --> 1 w: 10 +0 --> 2 w: 10 +1 --> 3 w: 10 +1 +0 \ No newline at end of file diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..7692bfd --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,32 @@ +# 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. + +cmake_minimum_required(VERSION 3.18 FATAL_ERROR) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +set(CMAKE_CXX_COMPILER_LAUNCHER ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_odr_violation=0 ${CMAKE_CXX_COMPILER_LAUNCHER}) + +set(ALGO_ANALYSIS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/..") +include(${ALGO_ANALYSIS_DIR}/cpp/cmake/set_ifndef.cmake) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED TRUE) +set(CMAKE_VERBOSE_MAKEFILE 1) + +set(PROJECT_NAME algo_analysis_examples) +project(${PROJECT_NAME}) + +set_ifndef(ALGO_ANALYSIS_INCLUDE_DIR ${ALGO_ANALYSIS_DIR}/cpp/include) +include_directories(SYSTEM ${ALGO_ANALYSIS_INCLUDE_DIR}) + +add_executable(ford_bellman_example "${ALGO_ANALYSIS_DIR}/cpp/algo_analysis/memory/memory.cpp" "dfs.cpp") diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..99d5a3d --- /dev/null +++ b/examples/README.md @@ -0,0 +1,23 @@ +## Примеры использования + +Каждая отдельная директория является самодостаточным примером имплементирования и использования нашей библиотеки algo_analysis \ +Назнание директории с примером является достаточным чтобы понять что он делает )) + +### Build + +1. **Build examles:** +```sh + git clone git@github.com:HSE-Software-Development/algorithms_analysis.git + cd algorithms_analysis + sh scripts/build_examples.sh +``` + +2. **Run the examples:** +```sh + pwd | rev | cut -d '/' -f 1 | rev # must be only "algorithms_analysis" in output + cd examples/build + ./*_example +``` + +### **Warning:** +Так как примеры внутри себя работают со сторонними файлами (файлы с данными графов) то запуск исполняемого файла не из указанного места (как раз проверка путем pwd) приведет к тому что никакой граф не загрузится и они все будут пустыми diff --git a/examples/dfs.cpp b/examples/dfs.cpp new file mode 100644 index 0000000..19a6556 --- /dev/null +++ b/examples/dfs.cpp @@ -0,0 +1,32 @@ +/* + * 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 + +int main() { + Task task( + "../tests/.data/task_01.txt" + ); + + DFSAlgorithm algo; + Task::Statistics stats = task.estimate(algo); + + std::cout << "MU: " << stats.initializationMemoryUsage << "\n"; + std::cout << "IT: " << stats.initializationTime << "\n"; + return 0; +} \ No newline at end of file diff --git a/src/.clang-format b/legacy/src/.clang-format similarity index 100% rename from src/.clang-format rename to legacy/src/.clang-format diff --git a/src/CMakeLists.txt b/legacy/src/CMakeLists.txt similarity index 89% rename from src/CMakeLists.txt rename to legacy/src/CMakeLists.txt index a0ee2e1..3bd063a 100644 --- a/src/CMakeLists.txt +++ b/legacy/src/CMakeLists.txt @@ -3,6 +3,7 @@ set(PROJECT_NAME HSE_SD) project(${PROJECT_NAME}) set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_COMPILER_LAUNCHER ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_odr_violation=0 ${CMAKE_CXX_COMPILER_LAUNCHER}) # include_directories(include) @@ -20,8 +21,7 @@ else () message(FATAL_ERROR "Not supported OS (only MacOS/Linux/Windows)") endif () - -# for detecting memory leaks +# For detecting memory leaks add_compile_options(-fsanitize=address) add_compile_options(-fsanitize=undefined) add_link_options(-fsanitize=address) diff --git a/src/algorithms/algorithms.hpp b/legacy/src/algorithms/algorithms.hpp similarity index 79% rename from src/algorithms/algorithms.hpp rename to legacy/src/algorithms/algorithms.hpp index ee6dba1..a7c553a 100644 --- a/src/algorithms/algorithms.hpp +++ b/legacy/src/algorithms/algorithms.hpp @@ -2,6 +2,7 @@ #define ALGORITHMS_HPP #include +#include #include #include "../graph/graph.hpp" @@ -100,18 +101,50 @@ template void BFS(size_t startIndex, GeneralGraph while (!horizon.empty()) { size_t nodeIndex = horizon.front(); horizon.pop(); - std::cout << nodeIndex << std::endl; const GeneralNode *node = graph->getNode(nodeIndex); for (const std::pair *, size_t> &node_and_index : node->getNeighbours()) { - std::cout << node_and_index.first->getIndex() << std::endl; if (distances[node_and_index.first->getIndex()] == inf) { distances[node_and_index.first->getIndex()] = distances[nodeIndex] + graph->getEdge(node_and_index.second)->getWeight(); horizon.push(node_and_index.first->getIndex()); } } - std::cout << "====" << std::endl; + } +} + +/// @brief BFS algorithm for SSSP with zero edges +/// @tparam T type for graph's weights and distances +/// @param startIndex start index for the SSSP +/// @param graph graph itself +/// @param distances vector of distances from start point to all others +template +void BFS_WithZeroEdges(size_t startIndex, GeneralGraph *graph, std::vector &distances) { + const auto size = graph->getSize(); + + distances.resize(size); + std::fill(distances.begin(), distances.end(), inf); + std::deque horizon; + + distances[startIndex] = 0; + horizon.push_back(startIndex); + while (!horizon.empty()) { + size_t nodeIndex = horizon.front(); + horizon.pop_front(); + const GeneralNode *node = graph->getNode(nodeIndex); + + for (const std::pair *, size_t> &node_and_index : node->getNeighbours()) { + if (distances[node_and_index.first->getIndex()] == inf) { + T weight = graph->getEdge(node_and_index.second)->getWeight(); + distances[node_and_index.first->getIndex()] = distances[nodeIndex] + weight; + + if (weight == 0) { + horizon.push_front(node_and_index.first->getIndex()); + } else { + horizon.push_back(node_and_index.first->getIndex()); + } + } + } } } diff --git a/src/graph/graph.hpp b/legacy/src/graph/graph.hpp similarity index 89% rename from src/graph/graph.hpp rename to legacy/src/graph/graph.hpp index 5b99f67..eab9c48 100644 --- a/src/graph/graph.hpp +++ b/legacy/src/graph/graph.hpp @@ -46,24 +46,26 @@ template class GeneralNode { /// @brief Getter of the all node's neighbors /// @return const link to the node's neighbors vector - const std::vector *, size_t>> &getNeighbours() const { return neighbors; } + const std::vector *, size_t>> &getNeighbours() const noexcept { return neighbors; } /// @brief Getter of the node's value /// @return Value of the node - T getValue() const { return value; } + T getValue() const noexcept { return value; } /// @brief Getter of the node's index /// @return Index of the node - T getIndex() const { return index; } + T getIndex() const noexcept { return index; } /// @brief Setter of the node's value /// @param value_ new value of the node - void setValue(const T &value_) { value = value_; } + void setValue(const T &value_) noexcept { value = value_; } /// @brief Add neighbour to the node /// @param neighbour new node's neighbour /// @param edgeIndex index of the edge between the node and its neighbour - void addNeighbour(GeneralNode *neighbour, size_t edgeIndex) { neighbors.emplace_back(neighbour, edgeIndex); } + void addNeighbour(GeneralNode *neighbour, size_t edgeIndex) noexcept { + neighbors.emplace_back(neighbour, edgeIndex); + } /// @brief Class destructor ~GeneralNode() = default; @@ -90,19 +92,19 @@ template class GeneralEdge { /// @brief Getter of the edge's weight /// @return Weight of the edge - T getWeight() const { return weight; } + T getWeight() const noexcept { return weight; } /// @brief Getter of the edge's start node index /// @return Start node index of the edge - size_t getStart() const { return start; } + size_t getStart() const noexcept { return start; } /// @brief Getter of the edge's end node index /// @return End node index of the edge - size_t getEnd() const { return end; } + size_t getEnd() const noexcept { return end; } /// @brief Setter of the edge's weight /// @param weight_ new weight of the edge - void setWeight(T weight_) { weight = weight_; } + void setWeight(T weight_) noexcept { weight = weight_; } /// @brief Class destructor ~GeneralEdge() = default; @@ -163,7 +165,7 @@ template class GeneralGraph { /// @brief Getter of graph's edge by its index /// @param index index of the grap's edge /// @return Non-owning pointer to the edge in the graph - const GeneralEdge *getEdge(size_t index) const { + const GeneralEdge *getEdge(size_t index) const noexcept { if (index >= edges.size()) { return nullptr; } @@ -173,7 +175,7 @@ template class GeneralGraph { /// @brief Getter of graph's node by its index /// @param index index of the grap's node /// @return Non-owning pointer to the node in the graph - const GeneralNode *getNode(size_t index) const { + const GeneralNode *getNode(size_t index) const noexcept { if (index >= nodes.size()) { return nullptr; } @@ -182,7 +184,7 @@ template class GeneralGraph { /// @brief Get graph's size /// @return size of the graph - size_t getSize() const { return nodes.size(); } + size_t getSize() const noexcept { return nodes.size(); } /// @brief Class destrutor ~GeneralGraph() = default; diff --git a/src/main.cpp b/legacy/src/main.cpp similarity index 56% rename from src/main.cpp rename to legacy/src/main.cpp index f927ab8..ae5fa03 100644 --- a/src/main.cpp +++ b/legacy/src/main.cpp @@ -2,14 +2,13 @@ #include "tasks/tasks.hpp" -template struct FunctionType { +template struct Algorithm { typedef std::function *, std::vector &)> Type; }; int main() { - std::unique_ptr graph_1 = - std::make_unique("../../.cache/graph_example_1.txt"); - FunctionType::Type f = algorithms::FordBellman; + std::unique_ptr graph_1 = std::make_unique("../../.cache/graph_10.txt"); + Algorithm::Type f = algorithms::FordBellman; std::vector graphs; diff --git a/src/scripts/build.sh b/legacy/src/scripts/build.sh similarity index 100% rename from src/scripts/build.sh rename to legacy/src/scripts/build.sh diff --git a/src/scripts/format_source_code.sh b/legacy/src/scripts/format_source_code.sh similarity index 100% rename from src/scripts/format_source_code.sh rename to legacy/src/scripts/format_source_code.sh diff --git a/legacy/src/tasks/memory.cpp b/legacy/src/tasks/memory.cpp new file mode 100644 index 0000000..9758970 --- /dev/null +++ b/legacy/src/tasks/memory.cpp @@ -0,0 +1,10 @@ +#include "memory.hpp" + +// Redefinition of the allocating memory method +void *operator new(size_t size) { + MemoryBenchmarking::getInstance().totalMemoryAllocated += size; + return ::std::malloc(size); +} + +// Redefinition of the deleting memory method +void operator delete(void *ptr) noexcept { ::std::free(ptr); } diff --git a/legacy/src/tasks/memory.hpp b/legacy/src/tasks/memory.hpp new file mode 100644 index 0000000..d48badc --- /dev/null +++ b/legacy/src/tasks/memory.hpp @@ -0,0 +1,45 @@ +#ifndef MEMORY_HPP +#define MEMORY_HPP + +#include +#include + +// Memory benchmarking global class +class MemoryBenchmarking { + private: + size_t totalMemoryAllocated; // Total bytes of allocated memory + + /// @brief Class default constructor + explicit MemoryBenchmarking() = default; + + MemoryBenchmarking(MemoryBenchmarking const &) = delete; + MemoryBenchmarking &operator=(MemoryBenchmarking const &) = delete; + + /// @brief Class default destructor + ~MemoryBenchmarking() = default; + + public: + /// @brief Getter of instance of memory benchmarking class + /// @return static link to the instance of this class + static MemoryBenchmarking &getInstance() { + static MemoryBenchmarking memoryBenchmarkingInstance; + + return memoryBenchmarkingInstance; + } + + /// @brief Getter of the total number of allocated bytes value + /// @return total number of allocated bytes value + size_t getTotalMemoryAllocated() const noexcept { return totalMemoryAllocated; } + + // Friends methods of this class + friend void *operator new(size_t size); + friend void operator delete(void *ptr) noexcept; +}; + +// Redefinition of the allocating memory method +void *operator new(size_t size); + +// Redefinition of the deleting memory method +void operator delete(void *ptr) noexcept; + +#endif // MEMORY_HPP \ No newline at end of file diff --git a/src/tasks/tasks.hpp b/legacy/src/tasks/tasks.hpp similarity index 64% rename from src/tasks/tasks.hpp rename to legacy/src/tasks/tasks.hpp index b460c32..e1a9e6b 100644 --- a/src/tasks/tasks.hpp +++ b/legacy/src/tasks/tasks.hpp @@ -4,6 +4,7 @@ #include #include "../algorithms/algorithms.hpp" +#include "memory.hpp" // Task class class Task { @@ -13,7 +14,7 @@ class Task { /// @brief Get current time in ms /// @return current time in ms - uint64_t getTimeInMs() { + uint64_t getTimeInMs() noexcept { using namespace std::chrono; return duration_cast(system_clock::now().time_since_epoch()).count(); } @@ -30,7 +31,9 @@ template class SSSP : public Task { std::vector *> graphs; // Non-owning pointer to the graphs for the task std::function *, std::vector &)> algorithm; // Algorithm for the task std::vector> distances; // Distances from the start node to all others - std::vector benchmarks; // Algorithm executions time in seconds + std::vector timeBenchmarks; // Algorithm executions time in seconds + double totalMemoryUsage; // Total memory usage by whole task in KB + std::vector memoryBenchmarks; // Algorithm memory usage in KB public: /// @brief Class constructor @@ -42,13 +45,18 @@ template class SSSP : public Task { bool is_ok = !graphs_.empty() && !startNodeIndexes_.empty() && graphs_.size() == startNodeIndexes_.size(); if (is_ok) { + totalMemoryUsage = MemoryBenchmarking::getInstance().getTotalMemoryAllocated(); const auto size = graphs_.size(); startNodeIndexes = startNodeIndexes_; distances.resize(size); - benchmarks.resize(size); + memoryBenchmarks.resize(size); + timeBenchmarks.resize(size); graphs = graphs_; algorithm = algorithm_; + totalMemoryUsage = + static_cast(MemoryBenchmarking::getInstance().getTotalMemoryAllocated() - totalMemoryUsage) / + 1000; } else { std::cout << "The number of graphs is 0 or is not equal to the number of start nodes, aborting.." << std::endl; @@ -57,15 +65,27 @@ template class SSSP : public Task { /// @brief Getter of SSSP start node's indexes /// @return All start node's indexes vector - const std::vector &getStartNodeIndexes() const { return startNodeIndexes; } + const std::vector &getStartNodeIndexes() const noexcept { return startNodeIndexes; } /// @brief Getter of SSSP distances /// @return All distances vector - const std::vector> &getDistances() const { return distances; } + const std::vector> &getDistances() const noexcept { return distances; } + + /// @brief Getter of SSSP memory usage + /// @return All task's memory usage + const double &getTaskMemoryBenchmark() const noexcept { return totalMemoryUsage; } + + /// @brief Getter of SSSP algorithm's memory usage + /// @return All algorithm's memory benchmarks + const std::vector &getAlgorithmMemoryBenchmarks() const noexcept { return memoryBenchmarks; } + + /// @brief Getter of SSSP time benchmarks + /// @return All time benchmarks + const std::vector &getTimeBenchmarks() const noexcept { return timeBenchmarks; } /// @brief Getter of SSSP graphs /// @return All graphs vector - const std::vector *> &getGraphs() const { return graphs; } + const std::vector *> &getGraphs() const noexcept { return graphs; } /// @brief Executor of the task /// @param logLevel logging level @@ -75,15 +95,21 @@ template class SSSP : public Task { for (size_t i = 0; i < size; i++) { std::cout << "SSSP for graph #" << i + 1 << ":\n"; - benchmarks[i] = static_cast(getTimeInMs()); + memoryBenchmarks[i] = MemoryBenchmarking::getInstance().getTotalMemoryAllocated(); + timeBenchmarks[i] = static_cast(getTimeInMs()); algorithm(startNodeIndexes[i], graphs[i], distances[i]); - benchmarks[i] = (static_cast(getTimeInMs()) - benchmarks[i]); + memoryBenchmarks[i] = + static_cast(MemoryBenchmarking::getInstance().getTotalMemoryAllocated() - memoryBenchmarks[i]) / + 1000; + totalMemoryUsage += memoryBenchmarks[i]; + timeBenchmarks[i] = (static_cast(getTimeInMs()) - timeBenchmarks[i]); for (size_t j = 0; j < graphs[i]->getSize(); j++) { std::cout << "\t" << j + 1 << ") Distanse from node #" << startNodeIndexes[i] << " to node #" << j - << " = " << distances[i][j] << "; " - << "time = " << benchmarks[i] << " sec\n"; + << " = " << distances[i][j] << ";\n"; } - std::cout << std::endl; + std::cout << "\t\tMemory usage by task: " << totalMemoryUsage << " KB;\n"; + std::cout << "\t\tMemory usage by alorithm: " << memoryBenchmarks[i] << " KB;\n"; + std::cout << "\t\tTime: " << timeBenchmarks[i] << " ms." << std::endl; } } diff --git a/src/tests/bfs_test.cpp b/legacy/src/tests/bfs_test.cpp similarity index 100% rename from src/tests/bfs_test.cpp rename to legacy/src/tests/bfs_test.cpp diff --git a/legacy/src/tests/bfs_with_zero_edges_test.cpp b/legacy/src/tests/bfs_with_zero_edges_test.cpp new file mode 100644 index 0000000..b991dea --- /dev/null +++ b/legacy/src/tests/bfs_with_zero_edges_test.cpp @@ -0,0 +1,26 @@ +#include "tests.hpp" + +// Demonstrate some basic assertions. +TEST(BFS_WithZeroEdges, Graph1) { + std::unique_ptr graph_1 = std::make_unique("../../.cache/graph_example_4.txt"); + + std::cout << graph_1.get()->getSize() << std::endl; + + FunctionType::Type f = algorithms::BFS_WithZeroEdges; + + std::vector graphs; + + graphs.push_back(graph_1.get()); + SSSP task(graphs, {0}, f); + + task.run(); + + std::vector> right_distances = {{0, 10, 20, 40, 80}}; + + std::cout << task.getDistances().size() << std::endl; + for (int i = 0; i < task.getDistances().size(); i++) { + for (int j = 0; j < task.getDistances()[i].size(); j++) { + EXPECT_EQ(task.getDistances()[i][j], right_distances[i][j]); + } + } +} diff --git a/src/tests/dfs_test.cpp b/legacy/src/tests/dfs_test.cpp similarity index 100% rename from src/tests/dfs_test.cpp rename to legacy/src/tests/dfs_test.cpp diff --git a/src/tests/dijkstra_test.cpp b/legacy/src/tests/dijkstra_test.cpp similarity index 100% rename from src/tests/dijkstra_test.cpp rename to legacy/src/tests/dijkstra_test.cpp diff --git a/src/tests/ford_bellman_test.cpp b/legacy/src/tests/ford_bellman_test.cpp similarity index 100% rename from src/tests/ford_bellman_test.cpp rename to legacy/src/tests/ford_bellman_test.cpp diff --git a/src/tests/graph_test.cpp b/legacy/src/tests/graph_test.cpp similarity index 72% rename from src/tests/graph_test.cpp rename to legacy/src/tests/graph_test.cpp index 7aae15e..9928736 100644 --- a/src/tests/graph_test.cpp +++ b/legacy/src/tests/graph_test.cpp @@ -5,9 +5,9 @@ TEST(GeneralNodeTest, Constructor) { int value = 42; size_t capacity = 8; GeneralNode node(index, value, capacity); - - EXPECT_EQ(node.getIndex(), index); - EXPECT_EQ(node.getValue(), value); + + EXPECT_EQ(node.getIndex(), index); + EXPECT_EQ(node.getValue(), value); int newValue = 30; node.setValue(newValue); EXPECT_EQ(node.getValue(), newValue); @@ -21,9 +21,9 @@ TEST(GeneralNodeTest, AddNeighbour) { node1.addNeighbour(&node2, edgeIndex); const auto &neighbours = node1.getNeighbours(); - EXPECT_EQ(neighbours.size(), 1); - EXPECT_EQ(neighbours[0].first->getIndex(), 1); - EXPECT_EQ(neighbours[0].second, edgeIndex); + EXPECT_EQ(neighbours.size(), 1); + EXPECT_EQ(neighbours[0].first->getIndex(), 1); + EXPECT_EQ(neighbours[0].second, edgeIndex); } TEST(GeneralEdge, Constructor) { @@ -32,9 +32,9 @@ TEST(GeneralEdge, Constructor) { int weight = 7; GeneralEdge edge(start, end, weight); - EXPECT_EQ(edge.getStart(), start); - EXPECT_EQ(edge.getEnd(), end); - EXPECT_EQ(edge.getWeight(), weight); + EXPECT_EQ(edge.getStart(), start); + EXPECT_EQ(edge.getEnd(), end); + EXPECT_EQ(edge.getWeight(), weight); int newWeight = 10; edge.setWeight(newWeight); @@ -45,12 +45,12 @@ TEST(GeneralGraphTest, ConstructorFromFile) { const std::string filePath = "../../.cache/graph_example_4.txt"; std::unique_ptr> graph = std::make_unique>(filePath); - EXPECT_EQ(graph->getSize(), 5); + EXPECT_EQ(graph->getSize(), 5); for (size_t i = 0; i < 5; ++i) { const GeneralNode *node = graph->getNode(i); - ASSERT_NE(node, nullptr); - EXPECT_EQ(node->getIndex(), i); + ASSERT_NE(node, nullptr); + EXPECT_EQ(node->getIndex(), i); } // Ребро 0 -> 1 с весом 10 @@ -81,7 +81,6 @@ TEST(GeneralGraphTest, ConstructorFromFile) { EXPECT_EQ(edge3->getEnd(), 4); EXPECT_EQ(edge3->getWeight(), 40); - EXPECT_EQ(graph->getEdge(4), nullptr); } @@ -92,29 +91,27 @@ TEST(GeneralGraphTest, NeighborsCheck) { const GeneralNode *node0 = graph->getNode(0); ASSERT_NE(node0, nullptr); const auto &neighbors0 = node0->getNeighbours(); - EXPECT_EQ(neighbors0.size(), 2); - EXPECT_EQ(neighbors0[0].first->getIndex(), 1); - EXPECT_EQ(neighbors0[0].second, 0); - EXPECT_EQ(neighbors0[1].first->getIndex(), 2); - EXPECT_EQ(neighbors0[1].second, 1); + EXPECT_EQ(neighbors0.size(), 2); + EXPECT_EQ(neighbors0[0].first->getIndex(), 1); + EXPECT_EQ(neighbors0[0].second, 0); + EXPECT_EQ(neighbors0[1].first->getIndex(), 2); + EXPECT_EQ(neighbors0[1].second, 1); const GeneralNode *node1 = graph->getNode(1); ASSERT_NE(node1, nullptr); const auto &neighbors1 = node1->getNeighbours(); - EXPECT_EQ(neighbors1.size(), 2); + EXPECT_EQ(neighbors1.size(), 2); EXPECT_EQ(neighbors1[0].first->getIndex(), 0); - EXPECT_EQ(neighbors1[0].second, 0); - EXPECT_EQ(neighbors1[1].first->getIndex(), 3); + EXPECT_EQ(neighbors1[0].second, 0); + EXPECT_EQ(neighbors1[1].first->getIndex(), 3); EXPECT_EQ(neighbors1[1].second, 2); const GeneralNode *node3 = graph->getNode(3); ASSERT_NE(node3, nullptr); const auto &neighbors3 = node3->getNeighbours(); - EXPECT_EQ(neighbors3.size(), 2); + EXPECT_EQ(neighbors3.size(), 2); EXPECT_EQ(neighbors3[0].first->getIndex(), 1); - EXPECT_EQ(neighbors3[0].second, 2); - EXPECT_EQ(neighbors3[1].first->getIndex(), 4); - EXPECT_EQ(neighbors3[1].second, 3); + EXPECT_EQ(neighbors3[0].second, 2); + EXPECT_EQ(neighbors3[1].first->getIndex(), 4); + EXPECT_EQ(neighbors3[1].second, 3); } - - diff --git a/src/tests/tests.hpp b/legacy/src/tests/tests.hpp similarity index 100% rename from src/tests/tests.hpp rename to legacy/src/tests/tests.hpp diff --git a/pozor_maxima_26.02.25.md b/pozor_maxima_26.02.25.md new file mode 100644 index 0000000..eb5d23b --- /dev/null +++ b/pozor_maxima_26.02.25.md @@ -0,0 +1,212 @@ +# Документация по библиотеке графовых алгоритмов + +1. [Класс GeneralGraph](#класс-generalgraph) +2. [Формат графа](#формат-графа) +3. [Алгоритмы](#алгоритмы) + - [FordBellman](#fordbellman) + - [DFS](#dfs) + - [BSF](#bfs) + - [Dijkstra](#dijkstra) + +## Архитектура + +### Пространство имен algorithms +В пространстве имен algorithms находятся шаблонные методы для выполнения графовых алгоритмов. Каждый алгоритм принимает на вход объект класса GeneralGraph, и опциональные переменные. + +### Класс GeneralGraph +Класс GeneralGraph является шаблонным и имеет две переменные типа: +- TVertex — тип данных для вершин +- TEdge — тип данных для ребер + +#### Конструкторы +Класс GeneralGraph предлагает два конструктора для инициализации графа: +1. GeneralGraph(const std::string& filePath) + Конструктор, принимающий путь до файла, в котором хранится граф в необходимом формате. + +2. GeneralGraph(std::istream& inputStream) + Конструктор, принимающий поток ввода для задания графа в необходимом формате. + +## Примеры использования + +### Пример создания графа из файла +``` +#include "GeneralGraph.h" +#include "algorithms.h" + +int main() { + GeneralGraph graph("path/to/graph.txt"); + algorithms::someGraphAlgorithm(graph, ...); + return 0; +} +``` + + +## Формат графа +Для корректного создания графа, необходимо следующее оформление данных: +- В первом ряду указывается два числа: n - количество вершин в графе и число 0 или 1(При отсутствии веса в вершинах или наличии соответственно) +- В случае, если был выставлен флаг 1: + - В каждом из следующих n строчек указывается вес внутри вершины с соответствующим номером +- В каждом следующем ряду указывается новое ребро: + - Формат записи: + vertex1 vertex2 weight \ + где: + - vertex1 и vertex2 — идентификаторы вершин, соединенных ребром. + - weight — вес ребра (должен быть типом данных TEdge). + +### Пример формата графа +``` +4 1 +1 +2 +3 +4 +0 1 10 +1 2 20 +2 3 30 +3 0 40 +0 2 50 +1 3 60 +``` + +В данном примере указан полный граф на 4-ех вершинах с указанными весами на ребрах и в вершинах. + +## Алгоритмы + +### FordBellman +#### Описание +Функция реализует алгоритм Форда - Беллмана для поиска кратчайших путей от одной начальной вершины до всех остальных в графе, в том числе с учетом отрицательных весов. + +#### Шаблон +```c++ +template +void FordBellman(size_t startIndex, GeneralGraph *graph, std::vector &distances); +``` +#### Параметры +- startIndex — начальный индекс для алгоритма (указатель на начальную вершину для вычисления кратчайших путей). +- graph — указатель на объект GeneralGraph, который представляет граф. +- distances — ссылка на вектор, который будет заполнен наименьшими расстояниями от начальной вершины до всех остальных. + +#### Пример использования +```c++ +#include "GeneralGraph.h" +#include "algorithms.h" + +int main() { + GeneralGraph graph("path/to/graph_with_negatives.txt"); + std::vector distances(graph.vertexCount(), std::numeric_limits::infinity()); + + size_t startIndex = 0; // Начальная вершина + algorithms::FordBellman(startIndex, &graph, distances); + + // Теперь вектор distances содержит кратчайшие расстояния, даже с учетом отрицательных весов + return 0; +} +``` + +### DFS +#### Функция DFS + +#### Описание +Функция выполняет поиск в глубину (DFS) в графе и может использоваться для вычисления кратчайших расстояний в графе без отрицательных весов. + +#### Шаблон +```c++ +template +void DFS(size_t startIndex, GeneralGraph *graph, std::vector &distances); +``` + +#### Параметры +- startIndex — начальный индекс для поиска (указатель на начальную вершину). +- graph — указатель на объект GeneralGraph, содержащий граф, по которому будет выполнен поиск. +- distances — ссылка на вектор, который будет заполнен расстояниями от начальной вершины до всех остальных. + +#### Пример использования +```c++ +#include "GeneralGraph.h" +#include "algorithms.h" + +int main() { + GeneralGraph graph("path/to/graph.txt"); + std::vector distances(graph.vertexCount(), std::numeric_limits::infinity()); + + size_t startIndex = 0; // Начальная вершина + algorithms::DFS(startIndex, &graph, distances); + + // Теперь вектор distances содержит расстояния + return 0; +} +``` +### BFS +#### Описание +Функция реализует алгоритм поиска в ширину (BFS) для нахождения кратчайших путей от одной стартовой вершины до всех остальных. Алгоритм работает для графов без отрицательных весов и подходит для неориентированных и ориентированных графов. + +#### Шаблон +```c++ +template +void BFS(size_t startIndex, GeneralGraph *graph, std::vector &distances); +``` + +#### Параметры +- startIndex — начальный индекс для поиска, указывающий на стартовую вершину. +- graph — указатель на объект GeneralGraph, представляющий сам граф. +- distances — ссылка на вектор, который будет заполнен кратчайшими расстояниями от начальной вершины до всех остальных. + +#### Пример использования +```c++ +#include "GeneralGraph.h" +#include "algorithms.h" + +int main() { + GeneralGraph graph("path/to/graph.txt"); + std::vector distances(graph.vertexCount(), std::numeric_limits::infinity()); + + size_t startIndex = 0; // Начальная вершина + algorithms::BFS(startIndex, &graph, distances); + + // distances теперь содержит кратчайшие расстояния от startIndex до всех других вершин + return 0; +} +``` + +### Dijkstra +#### Описание +Функция реализует алгоритм Дейкстры для решения задачи нахождения кратчайшего пути от одной стартовой вершины до всех остальных вершин в графе с неотрицательными весами. + +#### Шаблон +```c++ +template +void Dijkstra(size_t startIndex, GeneralGraph *graph, std::vector &distances); +``` + +#### Параметры +- startIndex — начальный индекс для поиска кратчайших путей, указывающий на стартовую вершину в графе. +- graph — указатель на объект GeneralGraph, представляющий граф, по которому будет выполняться поиск кратчайших путей. +- distances — ссылка на вектор, который будет заполнен расстояниями от начальной вершины до всех остальных. Вектор должен быть инициализирован (например, значением "бесконечность" для всех вершин, кроме стартовой). + +#### Пример использования +```c++ +#include "GeneralGraph.h" +#include "algorithms.h" +#include +#include + +int main() { + GeneralGraph graph("path/to/graph.txt"); + std::vector distances(graph.vertexCount(), std::numeric_limits::infinity()); + + size_t startIndex = 0; // Начальная вершина + distances[startIndex] = 0; // Расстояние до самой себя - 0 + algorithms::Dijkstra(startIndex, &graph, distances); + + // Вывод расстояний от стартовой вершины до всех остальных + for (size_t i = 0; i < distances.size(); ++i) { + if (distances[i] == std::numeric_limits::infinity()) { + std::cout << "Расстояние до вершины " << i << ": бесконечность" << std::endl; + } else { + std::cout << "Расстояние до вершины " << i << ": " << distances[i] << std::endl; + } + } + + return 0; +} +``` \ No newline at end of file diff --git a/scripts/build_algo_analysis.sh b/scripts/build_algo_analysis.sh new file mode 100644 index 0000000..4e4f817 --- /dev/null +++ b/scripts/build_algo_analysis.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +pwd="$(pwd | rev | cut -d '/' -f 1 | rev)" +echo ${pwd} +if [ "${pwd}" = "algorithms_analysis" ]; then + cd cpp + mkdir build 2> /dev/null + cd build + cmake .. + make -j + + if [ -z "$1" ]; then + ./tests/algo_analysis_tests + else + if [ $1 -gt 0 ]; then + ./tests/algo_analysis_tests + fi + fi +else + echo "Please change directory to .../algorithms_analysis" +fi + +# Usage: sh scripts/build_algo_analysis.sh \ No newline at end of file diff --git a/scripts/build_examples.sh b/scripts/build_examples.sh new file mode 100644 index 0000000..729f304 --- /dev/null +++ b/scripts/build_examples.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +pwd="$(pwd | rev | cut -d '/' -f 1 | rev)" +if [ "${pwd}" = "algorithms_analysis" ]; then + cd examples + mkdir build 2> /dev/null + cd build + cmake .. + make -j 2> /dev/null +else + echo "Please cd to algorithms_analysis directory" +fi + +# Usage: sh scripts/build_algo_analysis.sh \ No newline at end of file diff --git a/scripts/format_source_code.sh b/scripts/format_source_code.sh new file mode 100644 index 0000000..f5dea03 --- /dev/null +++ b/scripts/format_source_code.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +files="$(find . -regex ".*/*\.[c|h]pp" 2> /dev/null)" + +if [ -z "$1" ] +then + echo "" +else + if [ $1 -gt 0 ]; then + echo "Found files to format: ${files}" + fi +fi + +echo ${files} | xargs clang-format -style=file -i 2>/dev/null + +# Usage: sh scripts/format_source_code.sh