Skip to content
Snippets Groups Projects
ShortestPathTest.cpp 12.9 KiB
Newer Older
  • Learn to ignore specific revisions
  • // ShortestPathTest.cpp
    //
    //     Created on: 21. 01. 2018
    //         Author: Jan Uhlik
    //    Modified by:
    //
    // Copyright (c) 2017 Czech Technical University in Prague | Faculty of Information Technology. All rights reserved.
    // Git repository: https://gitlab.fit.cvut.cz/algorithms-library-toolkit/automata-library
    
    
    #include <catch2/catch.hpp>
    
    
    #include <graph/GraphClasses.hpp>
    #include <node/NodeClasses.hpp>
    #include <edge/EdgeClasses.hpp>
    
    #include <heuristic/SquareGridHeuristics.hpp>
    
    #include <traverse/BFS.hpp>
    #include <traverse/IDDFS.hpp>
    #include <shortest_path/BellmanFord.hpp>
    #include <shortest_path/SPFA.hpp>
    #include "shortest_path/Dijkstra.hpp"
    #include <shortest_path/AStar.hpp>
    #include <shortest_path/IDAStar.hpp>
    #include <shortest_path/MM.hpp>
    #include <shortest_path/JPS.hpp>
    #include <generate/RandomGraphFactory.hpp>
    #include <generate/RandomGridFactory.hpp>
    
    using namespace graph;
    const double EPS = 10E-7; // Magic constant for double compare
    
    
    TEST_CASE ( "Shortest Path", "[unit][graph][shortest_path]" ) {
    	SECTION ( "BFS" ) {
    		graph::DirectedGraph<int, edge::WeightedEdge<int>> graph;
    
    		graph.addNode(1);
    		graph.addNode(2);
    		graph.addNode(3);
    		graph.addNode(4);
    		graph.addNode(5);
    
    		graph.addEdge(edge::WeightedEdge<int>(1, 2, 5));
    		graph.addEdge(edge::WeightedEdge<int>(2, 3, 3));
    		graph.addEdge(edge::WeightedEdge<int>(3, 4, 3));
    		graph.addEdge(edge::WeightedEdge<int>(4, 5, 5));
    		graph.addEdge(edge::WeightedEdge<int>(2, 4, 4));
    
    		auto res1 = traverse::BFS::findPath(graph, 1, 5);
    
    		auto res2 = traverse::BFS::findPathBidirectional(graph, 1, 5);
    
    		ext::vector<int> res = {1, 2, 4, 5};
    		CHECK(res1 == res);
    		CHECK(res1.size() == res.size());
    		CHECK(res2 == res);
    		CHECK(res2.size() == res.size());
    	}
    
    	// ---------------------------------------------------------------------------------------------------------------------
    
    	SECTION ( "IDDFS" ) {
    		graph::DirectedGraph<int, ext::pair<int, int>> graph;
    
    		graph.addNode(1);
    		graph.addNode(2);
    		graph.addNode(3);
    		graph.addNode(4);
    		graph.addNode(5);
    		graph.addNode(5);
    		graph.addNode(6);
    		graph.addNode(7);
    		graph.addNode(8);
    
    		graph.addEdge(1, 2);
    		graph.addEdge(1, 3);
    		graph.addEdge(1, 4);
    		graph.addEdge(2, 5);
    		graph.addEdge(2, 6);
    		graph.addEdge(3, 7);
    		graph.addEdge(6, 8);
    
    		auto bfs = traverse::BFS::findPath(graph, 1, 8);
    		auto iddfs = traverse::IDDFS::findPath(graph, 1, 8);
    		auto iddfsBi = traverse::IDDFS::findPathBidirectional(graph, 1, 8);
    
    		CHECK(bfs == iddfs);
    		CHECK(iddfsBi == iddfs);
    	}
    
    	// ---------------------------------------------------------------------------------------------------------------------
    
    	SECTION ( "Dijkstra" ) {
    		graph::UndirectedGraph<int, edge::WeightedEdge<int>> graph;
    
    		graph.addNode(1);
    		graph.addNode(2);
    		graph.addNode(3);
    		graph.addNode(4);
    		graph.addNode(5);
    
    		graph.addEdge(edge::WeightedEdge<int>(1, 2, 5));
    		graph.addEdge(edge::WeightedEdge<int>(2, 3, 3));
    		graph.addEdge(edge::WeightedEdge<int>(3, 4, 3));
    		graph.addEdge(edge::WeightedEdge<int>(4, 5, 5));
    		graph.addEdge(edge::WeightedEdge<int>(2, 4, 4));
    
    		auto res1 = graph::shortest_path::Dijkstra::findPath(graph, 1, 5);
    
    		auto res2 = graph::shortest_path::Dijkstra::findPathBidirectional(graph, 1, 5);
    
    		ext::vector<int> res = {1, 2, 4, 5};
    		CHECK(res1.first == res);
    		CHECK(res1.second == 14);
    		CHECK(res2.first == res);
    		CHECK(res2.second == 14);
    	}
    
    	// ---------------------------------------------------------------------------------------------------------------------
    
    	SECTION ( "BFS grid" ) {
    		grid::SquareGrid4<> graph(10, 10);
    		using node_type = decltype(graph)::node_type;
    
    		graph.addObstacle(3, 2);
    		graph.addObstacle(3, 3);
    		graph.addObstacle(3, 4);
    		graph.addObstacle(3, 5);
    		graph.addObstacle(4, 5);
    		graph.addObstacle(5, 5);
    		graph.addObstacle(6, 5);
    
    		node_type start = ext::make_pair(8l, 2l);
    		node_type goal = ext::make_pair(1l, 9l);
    
    		auto res1 = traverse::BFS::findPath(graph, start, goal);
    		auto res2 = traverse::BFS::findPathBidirectional(graph, start, goal);
    
    		CHECK(res1.size() == 15);
    		CHECK(res1.size() == res2.size());
    	}
    
    	// ---------------------------------------------------------------------------------------------------------------------
    
    	SECTION ( "Dijkstra Grid" ) {
    		grid::WeightedSquareGrid8<> graph(10, 10);
    		using node_type = decltype(graph)::node_type;
    
    		graph.addObstacle(3, 2);
    		graph.addObstacle(3, 3);
    		graph.addObstacle(3, 4);
    		graph.addObstacle(3, 5);
    		graph.addObstacle(4, 5);
    		graph.addObstacle(5, 5);
    		graph.addObstacle(6, 5);
    
    		node_type start = ext::make_pair(8l, 2l);
    		node_type goal = ext::make_pair(1l, 9l);
    
    		auto res1 = graph::shortest_path::Dijkstra::findPath(graph, start, goal);
    		auto res2 = graph::shortest_path::Dijkstra::findPathBidirectional(graph, start, goal);
    
    		CHECK(fabs(res1.second - (M_SQRT2 * 5 + 4)) < EPS);
    		CHECK(res1.second == res2.second);
    	}
    
    	// ---------------------------------------------------------------------------------------------------------------------
    
    	SECTION ( "A* Grid" ) {
    		grid::WeightedSquareGrid8<> graph(11, 11);
    		using node_type = decltype(graph)::node_type;
    
    		graph.addObstacle(2, 5);
    		graph.addObstacle(3, 5);
    		graph.addObstacle(4, 5);
    		graph.addObstacle(5, 5);
    		graph.addObstacle(6, 5);
    		graph.addObstacle(7, 5);
    		graph.addObstacle(5, 3);
    		graph.addObstacle(5, 4);
    		graph.addObstacle(5, 6);
    		graph.addObstacle(5, 7);
    		graph.addObstacle(5, 8);
    
    		node_type start = ext::make_pair(9l, 1l);
    		node_type goal = ext::make_pair(1l, 9l);
    
    		auto f_heuristic_forward = [&](const node_type &n) -> double {
    			return heuristic::DiagonalDistance::diagonalDistance(goal, n);
    		};
    
    		auto f_heuristic_backward = [&](const node_type &n) -> double {
    			return heuristic::DiagonalDistance::diagonalDistance(start, n);
    		};
    
    		auto res1 = graph::shortest_path::AStar::findPath(graph,
    				start,
    				goal,
    				f_heuristic_forward);
    
    		auto res2 = graph::shortest_path::AStar::findPathBidirectional(graph,
    				start,
    				goal,
    				f_heuristic_forward,
    				f_heuristic_backward);
    
    		auto res3 = graph::shortest_path::MM::findPathBidirectional(graph,
    				start,
    				goal,
    				f_heuristic_forward,
    				f_heuristic_backward);
    
    		CHECK(fabs(res1.second - (M_SQRT2 * 4 + 8)) < EPS);
    		CHECK(res1.second == res2.second);
    		CHECK(res2.second == res3.second);
    	}
    
    	// ---------------------------------------------------------------------------------------------------------------------
    
    	SECTION ( "All" ) {
    		graph::UndirectedGraph<int, edge::WeightedEdge<int, int>> graph;
    		int start = 5;
    		int goal = 17;
    
    		auto f_heuristic = [&](const int &) -> int {
    			return 0;
    		};
    
    		graph.addNode(1);
    		graph.addNode(2);
    		graph.addNode(3);
    		graph.addNode(4);
    		graph.addNode(5);
    		graph.addNode(6);
    		graph.addNode(7);
    		graph.addNode(8);
    		graph.addNode(9);
    		graph.addNode(10);
    		graph.addNode(11);
    		graph.addNode(12);
    		graph.addNode(13);
    		graph.addNode(14);
    		graph.addNode(15);
    		graph.addNode(16);
    		graph.addNode(17);
    		graph.addNode(18);
    		graph.addNode(19);
    		graph.addNode(20);
    
    		graph.addEdge(1, 5, 10);
    		graph.addEdge(2, 3, 20);
    		graph.addEdge(2, 4, 4);
    		graph.addEdge(2, 6, 19);
    		graph.addEdge(2, 5, 9);
    		graph.addEdge(4, 7, 21);
    		graph.addEdge(5, 6, 24);
    		graph.addEdge(6, 7, 1);
    		graph.addEdge(6, 8, 3);
    		graph.addEdge(6, 9, 15);
    		graph.addEdge(7, 10, 18);
    		graph.addEdge(8, 12, 22);
    		graph.addEdge(9, 11, 14);
    		graph.addEdge(10, 18, 50); //
    		graph.addEdge(10, 11, 8);
    		graph.addEdge(11, 13, 2);
    		graph.addEdge(12, 13, 5);
    		graph.addEdge(13, 14, 13);
    		graph.addEdge(13, 19, 7);
    		graph.addEdge(14, 15, 16);
    		graph.addEdge(15, 16, 6);
    		graph.addEdge(15, 17, 25);
    		graph.addEdge(16, 18, 17);
    		graph.addEdge(17, 18, 12);
    		graph.addEdge(17, 20, 11);
    		graph.addEdge(19, 20, 23);
    
    		auto dijkstra = graph::shortest_path::Dijkstra::findPath(graph, start, goal);
    		auto dijkstraBi = graph::shortest_path::Dijkstra::findPathBidirectional(graph, start, goal);
    		auto bellmanFord = graph::shortest_path::BellmanFord::findPath(graph, start, goal);
    		auto spfa = graph::shortest_path::SPFA::findPath(graph, start, goal);
    		auto astar = graph::shortest_path::AStar::findPath(graph, start, goal, f_heuristic);
    		auto idastar = graph::shortest_path::IDAStar::findPath(graph, start, goal, f_heuristic);
    		auto astarBi = graph::shortest_path::AStar::findPathBidirectional(graph, start, goal, f_heuristic, f_heuristic);
    		auto mm = graph::shortest_path::MM::findPathBidirectional(graph, start, goal, f_heuristic, f_heuristic);
    
    		CHECK(dijkstra.second == 94);
    
    		CHECK(dijkstra == dijkstraBi);
    		CHECK(bellmanFord == dijkstraBi);
    		CHECK(bellmanFord == spfa);
    		CHECK(astar == spfa);
    		CHECK(astar == idastar);
    		CHECK(astarBi == idastar);
    		CHECK(astarBi == mm);
    
    		auto BFS = traverse::BFS::findPath(graph, start, goal);
    		auto BFSBi = traverse::BFS::findPathBidirectional(graph, start, goal);
    
    		CHECK(BFS == BFSBi);
    	}
    
    	// ---------------------------------------------------------------------------------------------------------------------
    
    	SECTION ( "All Grid" ) {
    		grid::WeightedSquareGrid8<int, edge::WeightedEdge<ext::pair<int, int>, double>> graph(11, 11);
    		using node_type = ext::pair<int, int>;
    
    		auto start = ext::make_pair(8, 2);
    		auto goal = ext::make_pair(1, 9);
    
    		graph.addObstacle(2, 5);
    		graph.addObstacle(3, 5);
    		graph.addObstacle(4, 5);
    		graph.addObstacle(5, 5);
    		graph.addObstacle(6, 5);
    		graph.addObstacle(7, 5);
    		graph.addObstacle(5, 3);
    		graph.addObstacle(5, 4);
    		graph.addObstacle(5, 6);
    		graph.addObstacle(5, 7);
    		graph.addObstacle(5, 8);
    
    		auto f_heuristic_forward = [&](const node_type &n) -> double {
    			return heuristic::DiagonalDistance::diagonalDistance(goal, n);
    		};
    
    		auto f_heuristic_backward = [&](const node_type &n) -> double {
    			return heuristic::DiagonalDistance::diagonalDistance(start, n);
    		};
    
    		auto dijkstra = graph::shortest_path::Dijkstra::findPath(graph, start, goal);
    		auto dijkstraBi = graph::shortest_path::Dijkstra::findPathBidirectional(graph, start, goal);
    		auto bellmanFord = graph::shortest_path::BellmanFord::findPath(graph, start, goal);
    		auto spfa = graph::shortest_path::SPFA::findPath(graph, start, goal);
    		auto astar = graph::shortest_path::AStar::findPath(graph, start, goal, f_heuristic_forward);
    		auto idastar = graph::shortest_path::IDAStar::findPath(graph, start, goal, f_heuristic_forward);
    		auto astarBi = graph::shortest_path::AStar::findPathBidirectional(graph, start, goal, f_heuristic_forward, f_heuristic_backward);
    		auto mm = graph::shortest_path::MM::findPathBidirectional(graph, start, goal, f_heuristic_forward, f_heuristic_backward);
    		auto jps = graph::shortest_path::JPS::findPath(graph, start, goal, f_heuristic_forward);
    
    		CHECK(fabs(dijkstra.second - (M_SQRT2 * 3 + 8)) < EPS);
    		CHECK(fabs(dijkstra.second - dijkstraBi.second) < EPS);
    		CHECK(fabs(bellmanFord.second - dijkstraBi.second) < EPS);
    		CHECK(fabs(bellmanFord.second - spfa.second) < EPS);
    		CHECK(fabs(astar.second - spfa.second) < EPS);
    		CHECK(fabs(astar.second - idastar.second) < EPS);
    		CHECK(fabs(astarBi.second - idastar.second) < EPS);
    		CHECK(fabs(astarBi.second - mm.second) < EPS);
    		CHECK(fabs(jps.second - mm.second) < EPS);
    	}
    
    	// ---------------------------------------------------------------------------------------------------------------------
    
    	SECTION ( "All Grid Random" ) {
    		auto graph_tuple = generate::RandomGridFactory::
    			randomGrid<grid::WeightedSquareGrid8<long, edge::WeightedEdge<ext::pair<long, long>>>>(50, 50, 100);
    		auto graph = std::get<0>(graph_tuple);
    		auto start = std::get<1>(graph_tuple);
    		auto goal = std::get<2>(graph_tuple);
    
    		using weight_type = decltype(graph)::edge_type::weight_type;
    		using node_type = decltype(graph)::node_type;
    
    		auto f_heuristic_forward = [&](const node_type &n) -> weight_type {
    			return heuristic::DiagonalDistance::diagonalDistance(goal, n);
    		};
    
    		auto f_heuristic_backward = [&](const node_type &n) -> weight_type {
    			return heuristic::DiagonalDistance::diagonalDistance(start, n);
    		};
    
    		auto dijkstra = graph::shortest_path::Dijkstra::findPath(graph, start, goal);
    		auto dijkstraBi = graph::shortest_path::Dijkstra::findPathBidirectional(graph, start, goal);
    		auto bellmanFord = graph::shortest_path::BellmanFord::findPath(graph, start, goal);
    		auto spfa = graph::shortest_path::SPFA::findPath(graph, start, goal);
    		auto astar = graph::shortest_path::AStar::findPath(graph, start, goal, f_heuristic_forward);
    		auto astarBi = graph::shortest_path::AStar::findPathBidirectional(graph, start, goal, f_heuristic_forward, f_heuristic_backward);
    		auto mm = graph::shortest_path::MM::findPathBidirectional(graph, start, goal, f_heuristic_forward, f_heuristic_backward);
    		auto jps = graph::shortest_path::JPS::findPath(graph, start, goal, f_heuristic_forward);
    
    		CHECK(fabs(dijkstra.second - dijkstraBi.second) < EPS);
    		CHECK(fabs(bellmanFord.second - dijkstraBi.second) < EPS);
    		CHECK(fabs(bellmanFord.second - spfa.second) < EPS);
    		CHECK(fabs(astar.second - spfa.second) < EPS);
    		CHECK(fabs(astarBi.second - astar.second) < EPS);
    		CHECK(fabs(astarBi.second - mm.second) < EPS);
    		CHECK(fabs(jps.second - mm.second) < EPS);
    	}