From ab25bdd38ba67b663484b21fab6b57a194e3c477 Mon Sep 17 00:00:00 2001 From: Ritika Date: Sat, 4 Jan 2025 20:09:59 -0800 Subject: [PATCH 1/2] =?UTF-8?q?Implement=20Bellman=20Ford=E2=80=99s=20algo?= =?UTF-8?q?rithm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implemented Bellman Ford's algorithm and function to find shortest path. Bellman Ford uses property passed in and supports weights of type int, float, and double. Implemented tests for Bellman Ford. --- CMakeLists.txt | 3 + data/graph.cpp | 222 ++++++++++++++++++++++++++ data/graph.h | 28 ++++ tests/bellman_ford_test.cpp | 300 ++++++++++++++++++++++++++++++++++++ 4 files changed, 553 insertions(+) create mode 100644 tests/bellman_ford_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a7e219..633df2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,3 +8,6 @@ target_include_directories (tinygraph PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) add_executable(tinygraph_test test.cpp) target_link_libraries (tinygraph_test LINK_PUBLIC tinygraph) + +add_executable(bellman_ford_test tests/bellman_ford_test.cpp) +target_link_libraries (bellman_ford_test LINK_PUBLIC tinygraph) diff --git a/data/graph.cpp b/data/graph.cpp index dd38e9f..97c3d24 100644 --- a/data/graph.cpp +++ b/data/graph.cpp @@ -10,6 +10,7 @@ #include #include #include "graph.h" +#include namespace tinygraph { Graph::Graph() = default; @@ -66,5 +67,226 @@ namespace tinygraph { this->str(); return std::vector>(); } + + void Graph::reset_paths() + { + distances.clear(); + parent.clear(); + } + + bool Graph::vertex_exists(const std::string& vertex) + { + for(auto& [vertex_name, vertex_ptr] : vertices) + { + if (vertex_name == vertex) + { + return true; + } + } + return false; + } + + + bool Graph::initialize_distances() { + number infinity; + for (auto& [vertex_name, vertex_ptr] : vertices) + { + for (auto& edge : vertex_ptr->connections) + { + if (edge->properties && edge->properties->find(path_property) != edge->properties->end()) + { + std::any property = edge->properties->at(path_property); + + if (property.type() == typeid(int)) + { + infinity = std::numeric_limits::max(); + } + else if (property.type() == typeid(float)) + { + infinity = std::numeric_limits::max(); + } + else if (property.type() == typeid(double)) + { + infinity = std::numeric_limits::max(); + } + else return false; + + for (auto& [vertex_name, vertex_ptr] : vertices) + { + distances[vertex_name] = infinity; + parent[vertex_name] = ""; + } + distances[source_name] = 0; + + return true; + + } + else if (edge->properties && edge->properties->find(path_property) == edge->properties->end()) + { + return false; + } + } + } + return false; + } + + Graph::number Graph::find_value(std::any& property) + { + if (property.type() == typeid(int)) + { + return std::any_cast(property); + } + else if (property.type() == typeid(float)) + { + return std::any_cast(property); + } + else if (property.type() == typeid(double)) + { + return std::any_cast(property); + } + + else return std::numeric_limits::min(); + } + + bool Graph::bellman_ford(const std::string& the_source_name, const std::string& sorting_property) + { + if (sorting_property.empty() || the_source_name.empty()) return false; + path_property = sorting_property; + source_name = the_source_name; + if (!vertex_exists(the_source_name)) + { + reset_paths(); + return false; + } + + if (!initialize_distances()) + { + reset_paths(); + return false; + } + + for (int i=0; iconnections) + { + if (edge->properties && edge->properties->find(sorting_property) != edge->properties->end()) + { + std::any property = edge->properties->at(path_property); + + number weight = find_value(property); + + if (typeid(weight) == typeid(int) && std::get(weight) == std::numeric_limits::min()) + { + reset_paths(); + return false; + } + + auto& ref_vertex_name = vertex_name; + + std::visit( + [this, &ref_vertex_name, &edge](auto& distance_from, auto& distance_to, auto& weight) { + + using type_used = std::common_type_t; + if (distance_from != std::numeric_limits::max() && distance_to > distance_from + weight) + { + this->distances[edge->to->name] = distance_from + weight; + this->parent[edge->to->name] = ref_vertex_name; + } + + }, + distances[vertex_name], + distances[edge->to->name], + weight + ); + } + + + else if (edge->properties && edge->properties->find(sorting_property) == edge->properties->end()) + { + reset_paths(); + return false; + } + } + } + } + + for (auto& [vertex_name, vertex_ptr] : vertices) + { + for (auto& edge : vertex_ptr->connections) + { + if (edge->properties && edge->properties->find(sorting_property) != edge->properties->end()) + { + std::any property = edge->properties->at(path_property); + + number weight = find_value(property); + + if (typeid(weight) == typeid(int) && std::get(weight) == std::numeric_limits::min()) + { + reset_paths(); + return false; + } + + bool returning = true; + + std::visit( + [this, &returning](auto& distance_from, auto& distance_to, auto& weight) { + using type_used = std::common_type_t; + if (distance_from != std::numeric_limits::max() && distance_to > distance_from + weight) + { + this->negative_cycle = negative; + returning = false; + } + + }, + distances[vertex_name], + distances[edge->to->name], + weight + ); + + if (!returning) + { + reset_paths(); + return false; + } + } + } + } + negative_cycle = non_negative; + return true; + } + + + std::vector Graph::find_shortest_path(const std::string& destination) + { + std::vector shortest_path; + + if (!vertex_exists(destination)) + { + return shortest_path; + } + + std::string current = destination; + + + while (current != source_name) + { + if (current.empty()) + { + shortest_path.clear(); + return shortest_path; + } + shortest_path.push_back(current); + current = parent[current]; + } + + shortest_path.push_back(current); + + std::reverse(shortest_path.begin(), shortest_path.end()); + + return shortest_path; + } + } diff --git a/data/graph.h b/data/graph.h index 334bab6..ffb8f43 100644 --- a/data/graph.h +++ b/data/graph.h @@ -7,6 +7,7 @@ #include "types.h" #include +#include namespace tinygraph { class Graph { @@ -27,6 +28,33 @@ namespace tinygraph { std::vector> connected_components(); std::string str(); + + using number = std::variant; // more types can be added here + + std::map distances; + + std::map parent; + + std::string path_property; + + bool bellman_ford(const std::string& the_source_name, const std::string& sorting_property); + + bool vertex_exists(const std::string& vertex); + + void reset_paths(); + + std::string source_name; + + std::vector find_shortest_path(const std::string& destination); + + enum neg_cycle_state { unknown, negative, non_negative }; + + neg_cycle_state negative_cycle = unknown; + + bool initialize_distances(); + + number find_value(std::any& property); + }; } diff --git a/tests/bellman_ford_test.cpp b/tests/bellman_ford_test.cpp new file mode 100644 index 0000000..b3f2d21 --- /dev/null +++ b/tests/bellman_ford_test.cpp @@ -0,0 +1,300 @@ +#include "../tinygraph.h" +#include +#include +#include + +static constexpr char DISTANCE[] = "distance"; +static constexpr char NAME[] = "name"; + +void print_bellman_ford(std::unique_ptr &g) { + for (auto& [city, distance] : g->distances) { + auto& city_ref = city; + std::visit( + [&city_ref](auto distance) { + using distance_type = decltype(distance); + std::cout << "\t" << city_ref << " : " << std::flush; + if (distance == std::numeric_limits::max()) { + std::cout << "infinity" << std::endl; + } else + std::cout << distance << std::endl; + }, + distance); + } +} + +void handle_path(std::unique_ptr &g, + std::string destination) { + std::cout << "path: " << std::flush; + std::vector path = g->find_shortest_path(destination); + + for (std::string &parent : path) { + std::cout << parent << std::flush; + } + std::cout << std::endl; +} + +void handle_bellman_ford(std::unique_ptr &g, + std::string vertex_name, std::string function_name, + bool print_path = false, + std::string destination = "") { + bool value = g->bellman_ford(vertex_name, DISTANCE); + std::cout << function_name << std::endl; + if (value) + print_bellman_ford(g); + else + std::cout << "\tdidn't work" << std::endl; + + if (print_path) + handle_path(g, destination); +} + +void play_around_example() { + auto port = tinygraph::typestore_add("port"); + + auto g = std::make_unique(); + + std::shared_ptr v; + + v = g->add("A", port); + v->properties[NAME] = "dkfjdkfj"; + + v = g->add("B", port); + v->properties[NAME] = "lkja"; + + v = g->add("C", port); + v->properties[NAME] = "addjm"; + + std::shared_ptr> linkprops; + + bool unlinked = false; + + linkprops = g->link("A", "C", unlinked); + linkprops->insert({DISTANCE, 4}); + + linkprops = g->link("C", "B", unlinked); + linkprops->insert({DISTANCE, -3.2}); + + linkprops = g->link("A", "B", unlinked); + linkprops->insert({DISTANCE, 2}); + + handle_bellman_ford(g, "A", "more complex example", true, "C"); +} + +void more_complex_example() { + auto port = tinygraph::typestore_add("port"); + + auto g = std::make_unique(); + + std::shared_ptr v; + + v = g->add("A", port); + v->properties[NAME] = "dkfjdkfj"; + + v = g->add("B", port); + v->properties[NAME] = "lkja"; + + v = g->add("C", port); + v->properties[NAME] = "addjm"; + + v = g->add("D", port); + v->properties[NAME] = "kjse"; + + v = g->add("E", port); + v->properties[NAME] = "aegf"; + + std::shared_ptr> linkprops; + + bool unlinked = false; + + linkprops = g->link("A", "B", unlinked); + linkprops->insert({DISTANCE, -1}); + + linkprops = g->link("B", "C", unlinked); + linkprops->insert({DISTANCE, 3}); + + linkprops = g->link("B", "D", unlinked); + linkprops->insert({DISTANCE, 2}); + + linkprops = g->link("D", "B", unlinked); + linkprops->insert({DISTANCE, 1}); + + linkprops = g->link("B", "E", unlinked); + linkprops->insert({DISTANCE, 2}); + + linkprops = g->link("E", "D", unlinked); + linkprops->insert({DISTANCE, -3}); + + linkprops = g->link("D", "C", unlinked); + linkprops->insert({DISTANCE, 5}); + + linkprops = g->link("A", "C", unlinked); + linkprops->insert({DISTANCE, 4}); + + handle_bellman_ford(g, "A", "more complex example"); +} + +void negative_cycle_example() { + auto port = tinygraph::typestore_add("port"); + + auto g = std::make_unique(); + + std::shared_ptr v; + + v = g->add("0", port); + v->properties[NAME] = "dkfjdkfj"; + + v = g->add("1", port); + v->properties[NAME] = "lkja"; + + v = g->add("2", port); + v->properties[NAME] = "addjm"; + + v = g->add("3", port); + v->properties[NAME] = "kjse"; + + std::shared_ptr> linkprops; + + bool unlinked = false; + + linkprops = g->link("1", "0", unlinked); + linkprops->insert({DISTANCE, 4}); + + linkprops = g->link("1", "2", unlinked); + linkprops->insert({DISTANCE, -6}); + + linkprops = g->link("3", "1", unlinked); + linkprops->insert({DISTANCE, -2}); + + linkprops = g->link("2", "3", unlinked); + linkprops->insert({DISTANCE, 5}); + + handle_bellman_ford(g, "A", "negative cycles"); +} + +void double_example() { + auto port = tinygraph::typestore_add("port"); + + auto g = std::make_unique(); + + std::shared_ptr v; + + v = g->add("0", port); + v->properties[NAME] = "dkfjdkfj"; + + v = g->add("1", port); + v->properties[NAME] = "lkja"; + + v = g->add("2", port); + v->properties[NAME] = "addjm"; + + v = g->add("3", port); + v->properties[NAME] = "kjse"; + + v = g->add("4", port); + v->properties[NAME] = "aegf"; + + v = g->add("5", port); + v->properties[NAME] = "llk"; + + v = g->add("6", port); + v->properties[NAME] = "luh"; + + v = g->add("7", port); + v->properties[NAME] = "knu"; + + std::shared_ptr> linkprops; + + bool unlinked = false; + + linkprops = g->link("5", "1", unlinked); + linkprops->insert({DISTANCE, 0.32}); + + linkprops = g->link("0", "2", unlinked); + linkprops->insert({DISTANCE, 0.26}); + + linkprops = g->link("7", "3", unlinked); + linkprops->insert({DISTANCE, 0.39}); + + linkprops = g->link("0", "4", unlinked); + linkprops->insert({DISTANCE, 0.38}); + + linkprops = g->link("4", "5", unlinked); + linkprops->insert({DISTANCE, 0.35}); + + linkprops = g->link("3", "6", unlinked); + linkprops->insert({DISTANCE, 0.52}); + + linkprops = g->link("2", "7", unlinked); + linkprops->insert({DISTANCE, 0.34}); + + handle_bellman_ford(g, "0", "doubles", true, "6"); +} + +void float_example() { + auto port = tinygraph::typestore_add("port"); + + auto g = std::make_unique(); + + std::shared_ptr v; + + v = g->add("0", port); + v->properties[NAME] = "dkfjdkfj"; + + v = g->add("1", port); + v->properties[NAME] = "lkja"; + + v = g->add("2", port); + v->properties[NAME] = "addjm"; + + v = g->add("3", port); + v->properties[NAME] = "kjse"; + + v = g->add("4", port); + v->properties[NAME] = "aegf"; + + v = g->add("5", port); + v->properties[NAME] = "llk"; + + v = g->add("6", port); + v->properties[NAME] = "luh"; + + v = g->add("7", port); + v->properties[NAME] = "knu"; + + std::shared_ptr> linkprops; + + bool unlinked = false; + + linkprops = g->link("5", "1", unlinked); + linkprops->insert({DISTANCE, 0.32f}); + + linkprops = g->link("0", "2", unlinked); + linkprops->insert({DISTANCE, 0.26f}); + + linkprops = g->link("7", "3", unlinked); + linkprops->insert({DISTANCE, 0.39f}); + + linkprops = g->link("0", "4", unlinked); + linkprops->insert({DISTANCE, 0.38f}); + + linkprops = g->link("4", "5", unlinked); + linkprops->insert({DISTANCE, 0.35f}); + + linkprops = g->link("3", "6", unlinked); + linkprops->insert({DISTANCE, 0.52f}); + + linkprops = g->link("2", "7", unlinked); + linkprops->insert({DISTANCE, 0.34f}); + + handle_bellman_ford(g, "0", "floats"); +} + +int main() { + tinygraph::typestore_init(); + more_complex_example(); + float_example(); + negative_cycle_example(); + double_example(); + play_around_example(); + return 0; +} From 32fbaf266241b4721dc76717f356320575b44372 Mon Sep 17 00:00:00 2001 From: Ritika Date: Tue, 21 Jan 2025 21:27:26 -0800 Subject: [PATCH 2/2] DFS Added test DFS and DFS setup functions. --- data/graph.cpp | 65 +++++++++++++++++++++++++++++++++++++++++++++++++- data/graph.h | 4 ++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/data/graph.cpp b/data/graph.cpp index 97c3d24..ebf92a1 100644 --- a/data/graph.cpp +++ b/data/graph.cpp @@ -288,5 +288,68 @@ namespace tinygraph { return shortest_path; } -} + void Graph::dfs(const std::string& current, const std::string& destination, std::map>& adj, std::map& visited, std::vector& path, bool& found) + { + visited[current] = true; + + path.push_back(current); + + if (current == destination) + { + found = true; + return; + } + + for (std::string testing : adj[current]) + { + if (!visited[testing] && !found) + { + dfs(testing, destination, adj, visited, path, found); + } + } + if (!found) path.pop_back(); + } + + bool Graph::dfsSetup(const std::string& source, const std::string& destination) + { + if (vertices.find(source) == vertices.end() || vertices.find(destination) == vertices.end()) return false; + + std::map> adj; + std::map visited; + + std::vector path; + + for (auto& [vertex_name, vertex_ptr] : vertices) + { + visited[vertex_name] = false; + } + + + for (auto& [vertex_name, vertex_ptr] : vertices) + { + for (auto& edge : vertex_ptr->connections) + { + if (undirected) + { + adj[vertex_name].push_back(edge->to->name); + adj[edge->to->name].push_back(vertex_name); + } + else + { + adj[vertex_name].push_back(edge->to->name); + } + } + } + + bool found = false; + + dfs(source, destination, adj, visited, path, found); + + if (!found) path.clear(); + + return found; + + + } +} diff --git a/data/graph.h b/data/graph.h index ffb8f43..a7d1e88 100644 --- a/data/graph.h +++ b/data/graph.h @@ -55,6 +55,10 @@ namespace tinygraph { number find_value(std::any& property); + void dfs(const std::string& current, const std::string& destination, std::map>& adj, std::map& visited, std::vector& path); + + bool dfsSetup(const std::string& source, const std::string& destination); + }; }