From b15f8f68e4c4287a6d677c7efe5a1760410b7a39 Mon Sep 17 00:00:00 2001 From: Chau Nguyen Date: Sun, 5 Jul 2015 00:15:55 +0200 Subject: [PATCH] refactor and improve the round trip computation of multiple SCCs Problem: - old solution was slow - depending on the result of TarjanSCC, new distance tables and new phantom node vectors were created to run tsp on it Solution: - dont create new distance tables and phantom node vectors - pass an additional vector with the information which locations are in the same component and ignore all others fix bug for scc split computation --- CMakeLists.txt | 2 +- algorithms/tarjan_scc.hpp | 5 + data_structures/matrix_graph_wrapper.hpp | 66 +++++++ plugins/round_trip.hpp | 163 +++++++--------- plugins/round_trip_BF.hpp | 129 +++++++------ plugins/round_trip_FI.hpp | 107 ++++++----- plugins/round_trip_NN.hpp | 110 ++++++----- routing_algorithms/tsp_brute_force.hpp | 43 ++++- routing_algorithms/tsp_farthest_insertion.hpp | 174 +++++++++++++----- routing_algorithms/tsp_nearest_neighbour.hpp | 122 ++++++++++++ 10 files changed, 634 insertions(+), 287 deletions(-) create mode 100644 data_structures/matrix_graph_wrapper.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 89c2c58f8..990ce884c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -91,7 +91,7 @@ set( add_library(COORDINATE OBJECT ${CoordinateGlob}) add_library(GITDESCRIPTION OBJECT util/git_sha.cpp) -add_library(OSRM ${OSRMSources} $ $ $ $ $ $ $ $ $ $) +add_library(OSRM ${OSRMSources} $ $ $ $ $ $ $ $ $ $ $) add_library(FINGERPRINT OBJECT util/fingerprint.cpp) add_dependencies(FINGERPRINT FingerPrintConfigure) diff --git a/algorithms/tarjan_scc.hpp b/algorithms/tarjan_scc.hpp index 0d8743543..05bbb0422 100644 --- a/algorithms/tarjan_scc.hpp +++ b/algorithms/tarjan_scc.hpp @@ -201,6 +201,11 @@ template class TarjanSCC return component_size_vector[component_id]; } + unsigned get_component_size_by_id(const unsigned component_id) const + { + return component_size_vector[component_id]; + } + unsigned get_component_id(const NodeID node) const { return components_index[node]; } }; diff --git a/data_structures/matrix_graph_wrapper.hpp b/data_structures/matrix_graph_wrapper.hpp new file mode 100644 index 000000000..3bf9259ec --- /dev/null +++ b/data_structures/matrix_graph_wrapper.hpp @@ -0,0 +1,66 @@ +/* + +Copyright (c) 2015, Project OSRM contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef MATRIX_GRAPH_WRAPPER_H +#define MATRIX_GRAPH_WRAPPER_H + +#include + +//This Wrapper provides all methods that are needed for TarjanSCC, when the graph is given in a +//matrix representation (e.g. as output from a distance table call) + +template class MatrixGraphWrapper { +public: + + MatrixGraphWrapper(std::vector table, const unsigned number_of_nodes) : table_(table), number_of_nodes_(number_of_nodes) {}; + + unsigned GetNumberOfNodes() { + return number_of_nodes_; + } + + std::vector GetAdjacentEdgeRange(const unsigned node) const { + std::vector edges; + const auto maxint = std::numeric_limits::max(); + for (auto i = 0; i < number_of_nodes_; ++i) { + if (*(table_.begin() + node * number_of_nodes_ + i) != maxint) { + edges.push_back(i); + } + } + return edges; + } + + unsigned GetTarget(const unsigned edge) { + return edge; + } + +private: + std::vector table_; + const unsigned number_of_nodes_; +}; + + +#endif // MATRIX_GRAPH_WRAPPER_H diff --git a/plugins/round_trip.hpp b/plugins/round_trip.hpp index 525c6ba98..bdb42a6ef 100644 --- a/plugins/round_trip.hpp +++ b/plugins/round_trip.hpp @@ -31,11 +31,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "plugin_base.hpp" #include "../algorithms/object_encoder.hpp" +#include "../algorithms/tiny_components.hpp" #include "../routing_algorithms/tsp_nearest_neighbour.hpp" #include "../routing_algorithms/tsp_farthest_insertion.hpp" #include "../routing_algorithms/tsp_brute_force.hpp" #include "../data_structures/query_edge.hpp" #include "../data_structures/search_engine.hpp" +#include "../data_structures/matrix_graph_wrapper.hpp" #include "../descriptors/descriptor_base.hpp" #include "../descriptors/json_descriptor.hpp" #include "../util/json_renderer.hpp" @@ -95,97 +97,41 @@ template class RoundTripPlugin final : public BasePlugin } } - void SplitUnaccessibleLocations(PhantomNodeArray & phantom_node_vector, std::vector & result_table) { + void SplitUnaccessibleLocations(PhantomNodeArray & phantom_node_vector, + std::vector & result_table, + std::vector> & components) { + + // Run TarjanSCC auto number_of_locations = phantom_node_vector.size(); - const auto maxint = std::numeric_limits::max(); + auto wrapper = std::make_shared>(result_table, number_of_locations); + auto empty_restriction = RestrictionMap(std::vector()); + auto empty_vector = std::vector(); + auto scc = TarjanSCC>(wrapper, empty_restriction, empty_vector); + scc.run(); - //////////////////////////////////// DELETE UNACCESSIBLE LOCATIONS ///////////////////////////////////////// - if (*std::max_element(result_table.begin(), result_table.end()) == maxint) { - const int half = number_of_locations / 2; - std::vector to_delete; + for (int j = 0; j < scc.get_number_of_components(); ++j){ + components.push_back(std::vector()); + } - for (int i = number_of_locations - 1; i >= 0; --i) { - // if the location is unaccessible by most of the other locations, remember the location - if (std::count(result_table.begin() + i * number_of_locations, result_table.begin() + (i+1) * number_of_locations, maxint) > half) { - to_delete.push_back(i); - } - } - //delete all unaccessible locations - for (int k = 0; k < to_delete.size(); ++k) { - // delete its row - result_table.erase(result_table.begin() + to_delete[k] * number_of_locations, result_table.begin() + (to_delete[k]+1) * number_of_locations); - --number_of_locations; - // delete its column - for (int j = 0; j < number_of_locations; ++j) { - result_table.erase(result_table.begin() + j * number_of_locations + to_delete[k]); - } - // delete its PhantomNode - phantom_node_vector.erase(phantom_node_vector.begin() + to_delete[k]); - } + for (int i = 0; i < number_of_locations; ++i) { + components[scc.get_component_id(i)].push_back(i); } } - void SetJSONOutput (const RouteParameters &route_parameters, - int tsp_time, - InternalRouteResult & min_route, - std::vector & min_loc_permutation, - osrm::json::Object & json_result){ + void SetLocPermutationOutput(const std::vector & loc_permutation, osrm::json::Object & json_result){ osrm::json::Array json_loc_permutation; - json_loc_permutation.values.insert(json_loc_permutation.values.end(), min_loc_permutation.begin(), min_loc_permutation.end()); + json_loc_permutation.values.insert(json_loc_permutation.values.end(), loc_permutation.begin(), loc_permutation.end()); json_result.values["loc_permutation"] = json_loc_permutation; - json_result.values["distance"] = min_route.shortest_path_length; - json_result.values["runtime"] = tsp_time; + } + void SetDistanceOutput(const int distance, osrm::json::Object & json_result) { + json_result.values["distance"] = distance; + } + void SetRuntimeOutput(const float runtime, osrm::json::Object & json_result) { + json_result.values["runtime"] = runtime; + } - - // if (route_parameters.tsp_algo.compare("NN")) - // //######################### NEAREST NEIGHBOUR ###############################// - // TIMER_START(tsp); - // osrm::tsp::NearestNeighbour(route_parameters, phantom_node_vector, *result_table, min_route, min_loc_permutation); - // search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); - // TIMER_STOP(tsp); - // SimpleLogger().Write() << "Distance " << min_route.shortest_path_length; - // SimpleLogger().Write() << "Time " << TIMER_MSEC(tsp) + TIMER_MSEC(tsp_pre); - - // osrm::json::Array json_loc_permutation; - // json_loc_permutation.values.insert(json_loc_permutation.values.end(), min_loc_permutation.begin(), min_loc_permutation.end()); - // json_result.values["loc_permutation"] = json_loc_permutation; - // json_result.values["distance"] = min_route.shortest_path_length; - // json_result.values["runtime"] = TIMER_MSEC(tsp); - // else if (route_parameters.tsp_algo.compare("BF") - // //########################### BRUTE FORCE ####################################// - // if (route_parameters.coordinates.size() < 12) { - // TIMER_START(tsp); - // osrm::tsp::BruteForce(route_parameters, phantom_node_vector, *result_table, min_route, min_loc_permutation); - // search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); - // TIMER_STOP(tsp); - // SimpleLogger().Write() << "Distance " << min_route.shortest_path_length; - // SimpleLogger().Write() << "Time " << TIMER_MSEC(tsp); - - // osrm::json::Array json_loc_permutation; - // json_loc_permutation.values.insert(json_loc_permutation.values.end(), min_loc_permutation.begin(), min_loc_permutation.end()); - // json_result.values["loc_permutation"] = json_loc_permutation; - // json_result.values["distance"] = min_route.shortest_path_length; - // json_result.values["runtime"] = TIMER_MSEC(tsp); - // } else { - // json_result.values["distance"] = -1; - // json_result.values["runtime"] = -1; - // } - // else - // //######################## FARTHEST INSERTION ###############################// - // TIMER_START(tsp); - // osrm::tsp::FarthestInsertion(route_parameters, phantom_node_vector, *result_table, min_route, min_loc_permutation); - // search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); - // TIMER_STOP(tsp); - // SimpleLogger().Write() << "Distance " << min_route.shortest_path_length; - // SimpleLogger().Write() << "Time " << TIMER_MSEC(tsp); - - // osrm::json::Array json_loc_permutation; - // json_loc_permutation.values.insert(json_loc_permutation.values.end(), min_loc_permutation.begin(), min_loc_permutation.end()); - // json_result.values["loc_permutation"] = json_loc_permutation; - // json_result.values["distance"] = min_route.shortest_path_length; - // json_result.values["runtime"] = TIMER_MSEC(tsp); - + void SetGeometry(const RouteParameters &route_parameters, const InternalRouteResult & min_route, osrm::json::Object & json_result) { // return geometry result to json std::unique_ptr> descriptor; descriptor = osrm::make_unique>(facade); @@ -212,20 +158,53 @@ template class RoundTripPlugin final : public BasePlugin return 400; } - SplitUnaccessibleLocations(phantom_node_vector, *result_table); - auto number_of_locations = phantom_node_vector.size(); - InternalRouteResult min_route; - std::vector min_loc_permutation(number_of_locations, -1); - //######################## FARTHEST INSERTION ###############################// - TIMER_START(tsp); - osrm::tsp::FarthestInsertionTSP(phantom_node_vector, *result_table, min_route, min_loc_permutation); - search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); - TIMER_STOP(tsp); + const auto maxint = std::numeric_limits::max(); + if (*std::max_element(result_table->begin(), result_table->end()) == maxint) { + std::unique_ptr> descriptor; + descriptor = osrm::make_unique>(facade); + descriptor->SetConfig(route_parameters); - BOOST_ASSERT(min_route.segment_end_coordinates.size() == route_parameters.coordinates.size()); + std::vector> components; + TIMER_START(tsp); + SplitUnaccessibleLocations(phantom_node_vector, *result_table, components); + auto number_of_locations = phantom_node_vector.size(); + std::vector min_loc_permutation(number_of_locations, -1); + auto min_dist = 0; + for(auto k = 0; k < components.size(); ++k) { + if (components[k].size() > 1) { + InternalRouteResult min_route; + osrm::tsp::FarthestInsertionTSP(components[k], phantom_node_vector, *result_table, min_route, min_loc_permutation); + search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); + min_dist += min_route.shortest_path_length; + descriptor->Run(min_route, json_result); + } + } + TIMER_STOP(tsp); - SetJSONOutput(route_parameters, TIMER_MSEC(tsp), min_route, min_loc_permutation, json_result); + SetRuntimeOutput(TIMER_MSEC(tsp), json_result); + SetDistanceOutput(min_dist, json_result); + SetLocPermutationOutput(min_loc_permutation, json_result); + } else { + auto number_of_locations = phantom_node_vector.size(); + InternalRouteResult min_route; + std::vector min_loc_permutation(number_of_locations, -1); + //######################## FARTHEST INSERTION ###############################// + TIMER_START(tsp); + osrm::tsp::FarthestInsertionTSP(phantom_node_vector, *result_table, min_route, min_loc_permutation); + search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); + TIMER_STOP(tsp); + // //######################### NEAREST NEIGHBOUR ###############################// + // TIMER_START(tsp); + // osrm::tsp::NearestNeighbourTSP(phantom_node_vector, *result_table, min_route, min_loc_permutation); + // search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); + // TIMER_STOP(tsp); + BOOST_ASSERT(min_route.segment_end_coordinates.size() == route_parameters.coordinates.size()); + SetLocPermutationOutput(min_loc_permutation, json_result); + SetDistanceOutput(min_route.shortest_path_length, json_result); + SetRuntimeOutput(TIMER_MSEC(tsp), json_result); + SetGeometry(route_parameters, min_route, json_result); + } diff --git a/plugins/round_trip_BF.hpp b/plugins/round_trip_BF.hpp index 7a667604a..10510aa03 100644 --- a/plugins/round_trip_BF.hpp +++ b/plugins/round_trip_BF.hpp @@ -96,47 +96,41 @@ template class RoundTripPluginBF final : public BasePlugin } } - void SplitUnaccessibleLocations(PhantomNodeArray & phantom_node_vector, std::vector & result_table) { + void SplitUnaccessibleLocations(PhantomNodeArray & phantom_node_vector, + std::vector & result_table, + std::vector> & components) { + // Run TarjanSCC auto number_of_locations = phantom_node_vector.size(); - const auto maxint = std::numeric_limits::max(); + auto wrapper = std::make_shared>(result_table, number_of_locations); + auto empty_restriction = RestrictionMap(std::vector()); + auto empty_vector = std::vector(); + auto scc = TarjanSCC>(wrapper, empty_restriction, empty_vector); + scc.run(); - //////////////////////////////////// DELETE UNACCESSIBLE LOCATIONS ///////////////////////////////////////// - if (*std::max_element(result_table.begin(), result_table.end()) == maxint) { - const int half = number_of_locations / 2; - std::vector to_delete; + for (int j = 0; j < scc.get_number_of_components(); ++j){ + components.push_back(std::vector()); + } - for (int i = number_of_locations - 1; i >= 0; --i) { - // if the location is unaccessible by most of the other locations, remember the location - if (std::count(result_table.begin() + i * number_of_locations, result_table.begin() + (i+1) * number_of_locations, maxint) > half) { - to_delete.push_back(i); - } - } - //delete all unaccessible locations - for (int k = 0; k < to_delete.size(); ++k) { - // delete its row - result_table.erase(result_table.begin() + to_delete[k] * number_of_locations, result_table.begin() + (to_delete[k]+1) * number_of_locations); - --number_of_locations; - // delete its column - for (int j = 0; j < number_of_locations; ++j) { - result_table.erase(result_table.begin() + j * number_of_locations + to_delete[k]); - } - // delete its PhantomNode - phantom_node_vector.erase(phantom_node_vector.begin() + to_delete[k]); - } + for (int i = 0; i < number_of_locations; ++i) { + components[scc.get_component_id(i)].push_back(i); } } - void SetJSONOutput (const RouteParameters &route_parameters, - int tsp_time, - InternalRouteResult & min_route, - std::vector & min_loc_permutation, - osrm::json::Object & json_result){ + void SetLocPermutationOutput(const std::vector & loc_permutation, osrm::json::Object & json_result){ osrm::json::Array json_loc_permutation; - json_loc_permutation.values.insert(json_loc_permutation.values.end(), min_loc_permutation.begin(), min_loc_permutation.end()); + json_loc_permutation.values.insert(json_loc_permutation.values.end(), loc_permutation.begin(), loc_permutation.end()); json_result.values["loc_permutation"] = json_loc_permutation; - json_result.values["distance"] = min_route.shortest_path_length; - json_result.values["runtime"] = tsp_time; + } + void SetDistanceOutput(const int distance, osrm::json::Object & json_result) { + json_result.values["distance"] = distance; + } + + void SetRuntimeOutput(const float runtime, osrm::json::Object & json_result) { + json_result.values["runtime"] = runtime; + } + + void SetGeometry(const RouteParameters &route_parameters, const InternalRouteResult & min_route, osrm::json::Object & json_result) { // return geometry result to json std::unique_ptr> descriptor; descriptor = osrm::make_unique>(facade); @@ -145,6 +139,7 @@ template class RoundTripPluginBF final : public BasePlugin descriptor->Run(min_route, json_result); } + int HandleRequest(const RouteParameters &route_parameters, osrm::json::Object &json_result) override final { @@ -163,39 +158,57 @@ template class RoundTripPluginBF final : public BasePlugin return 400; } - SplitUnaccessibleLocations(phantom_node_vector, *result_table); + if (route_parameters.coordinates.size() < 14) { + const auto maxint = std::numeric_limits::max(); + if (*std::max_element(result_table->begin(), result_table->end()) == maxint) { + std::unique_ptr> descriptor; + descriptor = osrm::make_unique>(facade); + descriptor->SetConfig(route_parameters); - InternalRouteResult min_route; - std::vector min_loc_permutation(phantom_node_vector.size(), -1); - //########################### BRUTE FORCE ####################################// - if (route_parameters.coordinates.size() < 11) { + std::vector> components; TIMER_START(tsp); - osrm::tsp::BruteForceTSP(phantom_node_vector, *result_table, min_route, min_loc_permutation); - search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); + SplitUnaccessibleLocations(phantom_node_vector, *result_table, components); + + std::vector min_loc_permutation(phantom_node_vector.size(), -1); + auto min_dist = 0; + for(auto k = 0; k < components.size(); ++k) { + if (components[k].size() > 1) { + InternalRouteResult min_route; + //run nearest neighbour + osrm::tsp::BruteForceTSP(components[k], phantom_node_vector, *result_table, min_route, min_loc_permutation); + //compute route + search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); + //return geometry + min_dist += min_route.shortest_path_length; + descriptor->Run(min_route, json_result); + } + } TIMER_STOP(tsp); - BOOST_ASSERT(min_route.segment_end_coordinates.size() == route_parameters.coordinates.size()); + SetRuntimeOutput(TIMER_MSEC(tsp), json_result); + SetDistanceOutput(min_dist, json_result); + SetLocPermutationOutput(min_loc_permutation, json_result); + } else { + auto number_of_locations = phantom_node_vector.size(); + InternalRouteResult min_route; + std::vector min_loc_permutation(number_of_locations, -1); + //########################### BRUTE FORCE ####################################// + TIMER_START(tsp); + osrm::tsp::BruteForceTSP(phantom_node_vector, *result_table, min_route, min_loc_permutation); + search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); + TIMER_STOP(tsp); - osrm::json::Array json_loc_permutation; - json_loc_permutation.values.insert(json_loc_permutation.values.end(), min_loc_permutation.begin(), min_loc_permutation.end()); - json_result.values["loc_permutation"] = json_loc_permutation; - json_result.values["distance"] = min_route.shortest_path_length; - SimpleLogger().Write() << "BF GEOM DISTANCE " << min_route.shortest_path_length; - json_result.values["runtime"] = TIMER_MSEC(tsp); + BOOST_ASSERT(min_route.segment_end_coordinates.size() == route_parameters.coordinates.size()); + + SetLocPermutationOutput(min_loc_permutation, json_result); + SetDistanceOutput(min_route.shortest_path_length, json_result); + SetRuntimeOutput(TIMER_MSEC(tsp), json_result); + SetGeometry(route_parameters, min_route, json_result); + } } else { - json_result.values["distance"] = -1; - json_result.values["runtime"] = -1; + SetRuntimeOutput(-1, json_result); + SetDistanceOutput(-1, json_result); } - // return geometry result to json - - std::unique_ptr> descriptor; - descriptor = osrm::make_unique>(facade); - - descriptor->SetConfig(route_parameters); - descriptor->Run(min_route, json_result); - - - return 200; } diff --git a/plugins/round_trip_FI.hpp b/plugins/round_trip_FI.hpp index b06220055..c52075bd3 100644 --- a/plugins/round_trip_FI.hpp +++ b/plugins/round_trip_FI.hpp @@ -95,47 +95,41 @@ template class RoundTripPluginFI final : public BasePlugin } } - void SplitUnaccessibleLocations(PhantomNodeArray & phantom_node_vector, std::vector & result_table) { + void SplitUnaccessibleLocations(PhantomNodeArray & phantom_node_vector, + std::vector & result_table, + std::vector> & components) { + // Run TarjanSCC auto number_of_locations = phantom_node_vector.size(); - const auto maxint = std::numeric_limits::max(); + auto wrapper = std::make_shared>(result_table, number_of_locations); + auto empty_restriction = RestrictionMap(std::vector()); + auto empty_vector = std::vector(); + auto scc = TarjanSCC>(wrapper, empty_restriction, empty_vector); + scc.run(); - //////////////////////////////////// DELETE UNACCESSIBLE LOCATIONS ///////////////////////////////////////// - if (*std::max_element(result_table.begin(), result_table.end()) == maxint) { - const int half = number_of_locations / 2; - std::vector to_delete; + for (int j = 0; j < scc.get_number_of_components(); ++j){ + components.push_back(std::vector()); + } - for (int i = number_of_locations - 1; i >= 0; --i) { - // if the location is unaccessible by most of the other locations, remember the location - if (std::count(result_table.begin() + i * number_of_locations, result_table.begin() + (i+1) * number_of_locations, maxint) > half) { - to_delete.push_back(i); - } - } - //delete all unaccessible locations - for (int k = 0; k < to_delete.size(); ++k) { - // delete its row - result_table.erase(result_table.begin() + to_delete[k] * number_of_locations, result_table.begin() + (to_delete[k]+1) * number_of_locations); - --number_of_locations; - // delete its column - for (int j = 0; j < number_of_locations; ++j) { - result_table.erase(result_table.begin() + j * number_of_locations + to_delete[k]); - } - // delete its PhantomNode - phantom_node_vector.erase(phantom_node_vector.begin() + to_delete[k]); - } + for (int i = 0; i < number_of_locations; ++i) { + components[scc.get_component_id(i)].push_back(i); } } - void SetJSONOutput (const RouteParameters &route_parameters, - int tsp_time, - InternalRouteResult & min_route, - std::vector & min_loc_permutation, - osrm::json::Object & json_result){ + void SetLocPermutationOutput(const std::vector & loc_permutation, osrm::json::Object & json_result){ osrm::json::Array json_loc_permutation; - json_loc_permutation.values.insert(json_loc_permutation.values.end(), min_loc_permutation.begin(), min_loc_permutation.end()); + json_loc_permutation.values.insert(json_loc_permutation.values.end(), loc_permutation.begin(), loc_permutation.end()); json_result.values["loc_permutation"] = json_loc_permutation; - json_result.values["distance"] = min_route.shortest_path_length; - json_result.values["runtime"] = tsp_time; + } + void SetDistanceOutput(const int distance, osrm::json::Object & json_result) { + json_result.values["distance"] = distance; + } + + void SetRuntimeOutput(const float runtime, osrm::json::Object & json_result) { + json_result.values["runtime"] = runtime; + } + + void SetGeometry(const RouteParameters &route_parameters, const InternalRouteResult & min_route, osrm::json::Object & json_result) { // return geometry result to json std::unique_ptr> descriptor; descriptor = osrm::make_unique>(facade); @@ -162,19 +156,48 @@ template class RoundTripPluginFI final : public BasePlugin return 400; } - SplitUnaccessibleLocations(phantom_node_vector, *result_table); + const auto maxint = std::numeric_limits::max(); + if (*std::max_element(result_table->begin(), result_table->end()) == maxint) { + std::unique_ptr> descriptor; + descriptor = osrm::make_unique>(facade); + descriptor->SetConfig(route_parameters); - InternalRouteResult min_route; - std::vector min_loc_permutation(phantom_node_vector.size(), -1); - //######################## FARTHEST INSERTION ###############################// - TIMER_START(tsp); - osrm::tsp::FarthestInsertionTSP(phantom_node_vector, *result_table, min_route, min_loc_permutation); - search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); - TIMER_STOP(tsp); + std::vector> components; + TIMER_START(tsp); + SplitUnaccessibleLocations(phantom_node_vector, *result_table, components); + auto number_of_locations = phantom_node_vector.size(); + std::vector min_loc_permutation(number_of_locations, -1); + auto min_dist = 0; + for(auto k = 0; k < components.size(); ++k) { + if (components[k].size() > 1) { + InternalRouteResult min_route; + osrm::tsp::FarthestInsertionTSP(components[k], phantom_node_vector, *result_table, min_route, min_loc_permutation); + search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); + min_dist += min_route.shortest_path_length; + descriptor->Run(min_route, json_result); + } + } + TIMER_STOP(tsp); - BOOST_ASSERT(min_route.segment_end_coordinates.size() == route_parameters.coordinates.size()); + SetRuntimeOutput(TIMER_MSEC(tsp), json_result); + SetDistanceOutput(min_dist, json_result); + SetLocPermutationOutput(min_loc_permutation, json_result); + } else { + auto number_of_locations = phantom_node_vector.size(); + InternalRouteResult min_route; + std::vector min_loc_permutation(number_of_locations, -1); + //######################## FARTHEST INSERTION ###############################// + TIMER_START(tsp); + osrm::tsp::FarthestInsertionTSP(phantom_node_vector, *result_table, min_route, min_loc_permutation); + search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); + TIMER_STOP(tsp); - SetJSONOutput(route_parameters, TIMER_MSEC(tsp), min_route, min_loc_permutation, json_result); + BOOST_ASSERT(min_route.segment_end_coordinates.size() == route_parameters.coordinates.size()); + SetLocPermutationOutput(min_loc_permutation, json_result); + SetDistanceOutput(min_route.shortest_path_length, json_result); + SetRuntimeOutput(TIMER_MSEC(tsp), json_result); + SetGeometry(route_parameters, min_route, json_result); + } return 200; } diff --git a/plugins/round_trip_NN.hpp b/plugins/round_trip_NN.hpp index 620d0fabc..cc88d899b 100644 --- a/plugins/round_trip_NN.hpp +++ b/plugins/round_trip_NN.hpp @@ -96,47 +96,41 @@ template class RoundTripPluginNN final : public BasePlugin } } - void SplitUnaccessibleLocations(PhantomNodeArray & phantom_node_vector, std::vector & result_table) { + void SplitUnaccessibleLocations(PhantomNodeArray & phantom_node_vector, + std::vector & result_table, + std::vector> & components) { + // Run TarjanSCC auto number_of_locations = phantom_node_vector.size(); - const auto maxint = std::numeric_limits::max(); + auto wrapper = std::make_shared>(result_table, number_of_locations); + auto empty_restriction = RestrictionMap(std::vector()); + auto empty_vector = std::vector(); + auto scc = TarjanSCC>(wrapper, empty_restriction, empty_vector); + scc.run(); - //////////////////////////////////// DELETE UNACCESSIBLE LOCATIONS ///////////////////////////////////////// - if (*std::max_element(result_table.begin(), result_table.end()) == maxint) { - const int half = number_of_locations / 2; - std::vector to_delete; + for (int j = 0; j < scc.get_number_of_components(); ++j){ + components.push_back(std::vector()); + } - for (int i = number_of_locations - 1; i >= 0; --i) { - // if the location is unaccessible by most of the other locations, remember the location - if (std::count(result_table.begin() + i * number_of_locations, result_table.begin() + (i+1) * number_of_locations, maxint) > half) { - to_delete.push_back(i); - } - } - //delete all unaccessible locations - for (int k = 0; k < to_delete.size(); ++k) { - // delete its row - result_table.erase(result_table.begin() + to_delete[k] * number_of_locations, result_table.begin() + (to_delete[k]+1) * number_of_locations); - --number_of_locations; - // delete its column - for (int j = 0; j < number_of_locations; ++j) { - result_table.erase(result_table.begin() + j * number_of_locations + to_delete[k]); - } - // delete its PhantomNode - phantom_node_vector.erase(phantom_node_vector.begin() + to_delete[k]); - } + for (int i = 0; i < number_of_locations; ++i) { + components[scc.get_component_id(i)].push_back(i); } } - void SetJSONOutput (const RouteParameters &route_parameters, - int tsp_time, - InternalRouteResult & min_route, - std::vector & min_loc_permutation, - osrm::json::Object & json_result){ + void SetLocPermutationOutput(const std::vector & loc_permutation, osrm::json::Object & json_result){ osrm::json::Array json_loc_permutation; - json_loc_permutation.values.insert(json_loc_permutation.values.end(), min_loc_permutation.begin(), min_loc_permutation.end()); + json_loc_permutation.values.insert(json_loc_permutation.values.end(), loc_permutation.begin(), loc_permutation.end()); json_result.values["loc_permutation"] = json_loc_permutation; - json_result.values["distance"] = min_route.shortest_path_length; - json_result.values["runtime"] = tsp_time; + } + void SetDistanceOutput(const int distance, osrm::json::Object & json_result) { + json_result.values["distance"] = distance; + } + + void SetRuntimeOutput(const float runtime, osrm::json::Object & json_result) { + json_result.values["runtime"] = runtime; + } + + void SetGeometry(const RouteParameters &route_parameters, const InternalRouteResult & min_route, osrm::json::Object & json_result) { // return geometry result to json std::unique_ptr> descriptor; descriptor = osrm::make_unique>(facade); @@ -163,19 +157,51 @@ template class RoundTripPluginNN final : public BasePlugin return 400; } - SplitUnaccessibleLocations(phantom_node_vector, *result_table); - InternalRouteResult min_route; - std::vector min_loc_permutation(phantom_node_vector.size(), -1); - //######################### NEAREST NEIGHBOUR ###############################// - TIMER_START(tsp); - osrm::tsp::NearestNeighbourTSP(phantom_node_vector, *result_table, min_route, min_loc_permutation); - search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); - TIMER_STOP(tsp); + const auto maxint = std::numeric_limits::max(); + if (*std::max_element(result_table->begin(), result_table->end()) == maxint) { + std::unique_ptr> descriptor; + descriptor = osrm::make_unique>(facade); + descriptor->SetConfig(route_parameters); - BOOST_ASSERT(min_route.segment_end_coordinates.size() == route_parameters.coordinates.size()); + std::vector> components; + TIMER_START(tsp); + SplitUnaccessibleLocations(phantom_node_vector, *result_table, components); - SetJSONOutput(route_parameters, TIMER_MSEC(tsp), min_route, min_loc_permutation, json_result); + std::vector min_loc_permutation(phantom_node_vector.size(), -1); + auto min_dist = 0; + for(auto k = 0; k < components.size(); ++k) { + if (components[k].size() > 1) { + InternalRouteResult min_route; + //run nearest neighbour + osrm::tsp::NearestNeighbourTSP(components[k], phantom_node_vector, *result_table, min_route, min_loc_permutation); + //compute route + search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); + //return geometry + min_dist += min_route.shortest_path_length; + descriptor->Run(min_route, json_result); + } + } + TIMER_STOP(tsp); + + SetRuntimeOutput(TIMER_MSEC(tsp), json_result); + SetDistanceOutput(min_dist, json_result); + SetLocPermutationOutput(min_loc_permutation, json_result); + } else { + auto number_of_locations = phantom_node_vector.size(); + InternalRouteResult min_route; + std::vector min_loc_permutation(number_of_locations, -1); + //######################### NEAREST NEIGHBOUR ###############################// + TIMER_START(tsp); + osrm::tsp::NearestNeighbourTSP(phantom_node_vector, *result_table, min_route, min_loc_permutation); + search_engine_ptr->shortest_path(min_route.segment_end_coordinates, route_parameters.uturns, min_route); + TIMER_STOP(tsp); + BOOST_ASSERT(min_route.segment_end_coordinates.size() == route_parameters.coordinates.size()); + SetLocPermutationOutput(min_loc_permutation, json_result); + SetDistanceOutput(min_route.shortest_path_length, json_result); + SetRuntimeOutput(TIMER_MSEC(tsp), json_result); + SetGeometry(route_parameters, min_route, json_result); + } return 200; } diff --git a/routing_algorithms/tsp_brute_force.hpp b/routing_algorithms/tsp_brute_force.hpp index a414659c4..9e0c266f2 100644 --- a/routing_algorithms/tsp_brute_force.hpp +++ b/routing_algorithms/tsp_brute_force.hpp @@ -51,19 +51,19 @@ namespace osrm namespace tsp { - -int ReturnDistance(const std::vector & dist_table, const std::vector location_order, const int min_route_dist, const int number_of_locations) { +template +int ReturnDistance(const std::vector & dist_table, const std::vector & location_order, const int min_route_dist, const int number_of_locations, const int component_size) { int i = 0; int route_dist = 0; // compute length and stop if length is longer than route already found - while (i < number_of_locations - 1 && route_dist < min_route_dist) { + while (i < component_size - 1 && route_dist < min_route_dist) { //get distance from location i to location i+1 route_dist += *(dist_table.begin() + (location_order[i] * number_of_locations) + location_order[i+1]); ++i; } //get distance from last location to first location - route_dist += *(dist_table.begin() + (location_order[number_of_locations-1] * number_of_locations) + location_order[0]); + route_dist += *(dist_table.begin() + (location_order[component_size-1] * number_of_locations) + location_order[0]); if (route_dist < min_route_dist) { return route_dist; @@ -73,6 +73,39 @@ int ReturnDistance(const std::vector & dist_table, const std::vector } } +void BruteForceTSP(std::vector & location, + const PhantomNodeArray & phantom_node_vector, + const std::vector & dist_table, + InternalRouteResult & min_route, + std::vector & min_loc_permutation) { + + const auto number_of_location = phantom_node_vector.size(); + const int component_size = location.size(); + int min_route_dist = std::numeric_limits::max(); + + std::vector min_location; + + // check length of all possible permutation of the location ids + do { + // int new_distance = ReturnDistance(dist_table, location, min_route_dist, number_of_location, component_size); + int new_distance = 4; + if (new_distance != -1) { + min_route_dist = new_distance; + min_location = location; + } + } while(std::next_permutation(location.begin(), location.end())); + + PhantomNodes viapoint; + for (int i = 0; i < component_size - 1; ++i) { + viapoint = PhantomNodes{phantom_node_vector[min_location[i]][0], phantom_node_vector[min_location[i + 1]][0]}; + min_route.segment_end_coordinates.emplace_back(viapoint); + min_loc_permutation[min_location[i]] = i; + } + min_loc_permutation[min_location[component_size - 1]] = component_size - 1; + viapoint = PhantomNodes{phantom_node_vector[min_location[component_size - 1]][0], phantom_node_vector[min_location[0]][0]}; + min_route.segment_end_coordinates.emplace_back(viapoint); +} + void BruteForceTSP(const PhantomNodeArray & phantom_node_vector, const std::vector & dist_table, InternalRouteResult & min_route, @@ -87,7 +120,7 @@ void BruteForceTSP(const PhantomNodeArray & phantom_node_vector, // check length of all possible permutation of the location ids do { - int new_distance = ReturnDistance(dist_table, location_ids, min_route_dist, number_of_locations); + int new_distance = ReturnDistance(dist_table, location_ids, min_route_dist, number_of_locations, number_of_locations); if (new_distance != -1) { min_route_dist = new_distance; //TODO: this gets copied right? fix this diff --git a/routing_algorithms/tsp_farthest_insertion.hpp b/routing_algorithms/tsp_farthest_insertion.hpp index 05ef5e612..cb3353d9e 100644 --- a/routing_algorithms/tsp_farthest_insertion.hpp +++ b/routing_algorithms/tsp_farthest_insertion.hpp @@ -31,6 +31,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../data_structures/search_engine.hpp" #include "../util/string_util.hpp" +#include "../tools/tsp_logs.hpp" #include @@ -41,12 +42,136 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include namespace osrm { namespace tsp { +void GetLongestRoundTrip(const int current_loc, + std::list & current_trip, + const std::vector & dist_table, + const int number_of_locations, + int & longest_min_tour, + std::list::iterator & following_loc){ + // for all nodes in the current trip find the best insertion resulting in the shortest path + for (auto from_node = current_trip.begin(); from_node != std::prev(current_trip.end()); ++from_node) { + auto to_node = std::next(from_node); + + auto dist_from = *(dist_table.begin() + (*from_node * number_of_locations) + current_loc); + auto dist_to = *(dist_table.begin() + (current_loc * number_of_locations) + *to_node); + auto trip_dist = dist_from + dist_to - *(dist_table.begin() + (*from_node * number_of_locations) + *to_node); + + // from all possible insertions to the current trip, choose the longest of all minimal insertions + if (trip_dist < longest_min_tour) { + longest_min_tour = trip_dist; + following_loc = to_node; + } + } + { // check insertion between last and first location too + auto from_node = std::prev(current_trip.end()); + auto to_node = current_trip.begin(); + + auto dist_from = *(dist_table.begin() + (*from_node * number_of_locations) + current_loc); + auto dist_to = *(dist_table.begin() + (current_loc * number_of_locations) + *to_node); + auto trip_dist = dist_from + dist_to - *(dist_table.begin() + (*from_node * number_of_locations) + *to_node); + if (trip_dist < longest_min_tour) { + longest_min_tour = trip_dist; + following_loc = to_node; + } + } +} + +void ComputeRouteAndPermutation(const PhantomNodeArray & phantom_node_vector, + std::list & current_trip, + InternalRouteResult & min_route, + std::vector & min_loc_permutation) { + // given he final trip, compute total distance and return the route and location permutation + PhantomNodes viapoint; + int perm = 0; + for (auto it = current_trip.begin(); it != std::prev(current_trip.end()); ++it) { + auto from_node = *it; + auto to_node = *std::next(it); + viapoint = PhantomNodes{phantom_node_vector[from_node][0], phantom_node_vector[to_node][0]}; + min_route.segment_end_coordinates.emplace_back(viapoint); + min_loc_permutation[from_node] = perm; + ++perm; + } + // check dist between last and first location too + viapoint = PhantomNodes{phantom_node_vector[*std::prev(current_trip.end())][0], phantom_node_vector[current_trip.front()][0]}; + min_route.segment_end_coordinates.emplace_back(viapoint); + min_loc_permutation[*std::prev(current_trip.end())] = perm; +} + +void FarthestInsertionTSP(const std::vector & locations, + const PhantomNodeArray & phantom_node_vector, + const std::vector & dist_table, + InternalRouteResult & min_route, + std::vector & min_loc_permutation) { + ////////////////////////////////////////////////////////////////////////////////////////////////// + // START FARTHEST INSERTION HERE + // 1. start at a random round trip of 2 locations + // 2. find the location that is the farthest away from the visited locations and whose insertion will make the round trip the longest + // 3. add the found location to the current round trip such that round trip is the shortest + // 4. repeat 2-3 until all locations are visited + // 5. DONE! + ////////////////////////////////////////////////////////////////////////////////////////////////// + const int number_of_locations = phantom_node_vector.size(); + const int size_of_component = locations.size(); + // list of the trip that will be found incrementally + std::list current_trip; + // tracks which nodes have been already visited + std::vector visited(number_of_locations, false); + + auto max_dist = 0; + auto index = -1; + for (auto x : locations) { + for (auto y : locations) { + if (*(dist_table.begin() + x * number_of_locations + y) > max_dist) { + max_dist = *(dist_table.begin() + x * number_of_locations + y); + index = x * number_of_locations + y; + } + } + } + const int max_from = index / number_of_locations; + const int max_to = index % number_of_locations; + + visited[max_from] = true; + visited[max_to] = true; + current_trip.push_back(max_from); + current_trip.push_back(max_to); + + // add all other nodes missing (two nodes are already in the initial start trip) + for (int j = 2; j < size_of_component; ++j) { + auto shortest_max_tour = -1; + int next_node = -1; + std::list::iterator min_max_insert; + + // find unvisited loc i that is the farthest away from all other visited locs + for (auto i : locations) { + if (!visited[i]) { + // longest_min_tour is the distance of the longest of all insertions with the minimal distance + auto longest_min_tour = std::numeric_limits::max(); + // following_loc is the location that comes after the location that is to be inserted + std::list::iterator following_loc; + GetLongestRoundTrip(i, current_trip, dist_table, number_of_locations, longest_min_tour, following_loc); + + // add the location to the current trip such that it results in the shortest total tour + if (longest_min_tour > shortest_max_tour) { + shortest_max_tour = longest_min_tour; + next_node = i; + min_max_insert = following_loc; + } + } + } + // mark as visited and insert node + visited[next_node] = true; + current_trip.insert(min_max_insert, next_node); + } + ComputeRouteAndPermutation(phantom_node_vector, current_trip, min_route, min_loc_permutation); +} + void FarthestInsertionTSP(const PhantomNodeArray & phantom_node_vector, const std::vector & dist_table, InternalRouteResult & min_route, @@ -66,14 +191,11 @@ void FarthestInsertionTSP(const PhantomNodeArray & phantom_node_vector, // tracks which nodes have been already visited std::vector visited(number_of_locations, false); - // PrintDistTable(dist_table, number_of_locations); - // find the pair of location with the biggest distance and make the pair the initial start trip const auto index = std::distance(dist_table.begin(), std::max_element(dist_table.begin(), dist_table.end())); const int max_from = index / number_of_locations; const int max_to = index % number_of_locations; - visited[max_from] = true; visited[max_to] = true; current_trip.push_back(max_from); @@ -93,32 +215,7 @@ void FarthestInsertionTSP(const PhantomNodeArray & phantom_node_vector, // following_loc is the location that comes after the location that is to be inserted std::list::iterator following_loc; - // for all nodes in the current trip find the best insertion resulting in the shortest path - for (auto from_node = current_trip.begin(); from_node != std::prev(current_trip.end()); ++from_node) { - auto to_node = std::next(from_node); - - auto dist_from = *(dist_table.begin() + (*from_node * number_of_locations) + i); - auto dist_to = *(dist_table.begin() + (i * number_of_locations) + *to_node); - auto trip_dist = dist_from + dist_to - *(dist_table.begin() + (*from_node * number_of_locations) + *to_node); - - // from all possible insertions to the current trip, choose the longest of all minimal insertions - if (trip_dist < longest_min_tour) { - longest_min_tour = trip_dist; - following_loc = to_node; - } - } - { // check insertion between last and first location too - auto from_node = std::prev(current_trip.end()); - auto to_node = current_trip.begin(); - - auto dist_from = *(dist_table.begin() + (*from_node * number_of_locations) + i); - auto dist_to = *(dist_table.begin() + (i * number_of_locations) + *to_node); - auto trip_dist = dist_from + dist_to - *(dist_table.begin() + (*from_node * number_of_locations) + *to_node); - if (trip_dist < longest_min_tour) { - longest_min_tour = trip_dist; - following_loc = to_node; - } - } + GetLongestRoundTrip(i, current_trip, dist_table, number_of_locations, longest_min_tour, following_loc); // add the location to the current trip such that it results in the shortest total tour if (longest_min_tour > shortest_max_tour) { @@ -133,24 +230,7 @@ void FarthestInsertionTSP(const PhantomNodeArray & phantom_node_vector, current_trip.insert(min_max_insert, next_node); } - // given he final trip, compute total distance and return the route and location permutation - PhantomNodes viapoint; - int perm = 0; - for (auto it = current_trip.begin(); it != std::prev(current_trip.end()); ++it) { - auto from_node = *it; - auto to_node = *std::next(it); - - viapoint = PhantomNodes{phantom_node_vector[from_node][0], phantom_node_vector[to_node][0]}; - min_route.segment_end_coordinates.emplace_back(viapoint); - - min_loc_permutation[from_node] = perm; - ++perm; - } - { // check dist between last and first location too - viapoint = PhantomNodes{phantom_node_vector[*std::prev(current_trip.end())][0], phantom_node_vector[current_trip.front()][0]}; - min_route.segment_end_coordinates.emplace_back(viapoint); - min_loc_permutation[*std::prev(current_trip.end())] = perm; - } + ComputeRouteAndPermutation(phantom_node_vector, current_trip, min_route, min_loc_permutation); } diff --git a/routing_algorithms/tsp_nearest_neighbour.hpp b/routing_algorithms/tsp_nearest_neighbour.hpp index bf9492293..6a5d05994 100644 --- a/routing_algorithms/tsp_nearest_neighbour.hpp +++ b/routing_algorithms/tsp_nearest_neighbour.hpp @@ -48,6 +48,128 @@ namespace osrm namespace tsp { +void NearestNeighbourTSP(const std::vector & locations, + const PhantomNodeArray & phantom_node_vector, + const std::vector & dist_table, + InternalRouteResult & min_route, + std::vector & min_loc_permutation) { + ////////////////////////////////////////////////////////////////////////////////////////////////// + // START GREEDY NEAREST NEIGHBOUR HERE + // 1. grab a random location and mark as starting point + // 2. find the nearest unvisited neighbour, set it as the current location and mark as visited + // 3. repeat 2 until there is no unvisited location + // 4. return route back to starting point + // 5. compute route + // 6. repeat 1-5 with different starting points and choose iteration with shortest trip + // 7. DONE! + ////////////////////////////////////////////////////////////////////////////////////////////////// + + const auto number_of_locations = phantom_node_vector.size(); + const int size_of_component = locations.size(); + min_route.shortest_path_length = std::numeric_limits::max(); + + // ALWAYS START AT ANOTHER STARTING POINT + for(auto start_node : locations) + { + int curr_node = start_node; + InternalRouteResult raw_route; + //TODO: Should we always use the same vector or does it not matter at all because of loop scope? + std::vector loc_permutation(number_of_locations, -1); + loc_permutation[start_node] = 0; + // visited[i] indicates whether node i was already visited by the salesman + std::vector visited(number_of_locations, false); + visited[start_node] = true; + + PhantomNodes viapoint; + // 3. REPEAT FOR EVERY UNVISITED NODE + int trip_dist = 0; + for(int via_point = 1; via_point < size_of_component; ++via_point) + { + int min_dist = std::numeric_limits::max(); + int min_id = -1; + + // 2. FIND NEAREST NEIGHBOUR + for (auto next : locations) { + if(!visited[next] && + *(dist_table.begin() + curr_node * number_of_locations + next) < min_dist) { + min_dist = *(dist_table.begin() + curr_node * number_of_locations + next); + min_id = next; + } + } + loc_permutation[min_id] = via_point; + visited[min_id] = true; + viapoint = PhantomNodes{phantom_node_vector[curr_node][0], phantom_node_vector[min_id][0]}; + raw_route.segment_end_coordinates.emplace_back(viapoint); + trip_dist += min_dist; + curr_node = min_id; + } + + // 4. ROUTE BACK TO STARTING POINT + viapoint = PhantomNodes{raw_route.segment_end_coordinates.back().target_phantom, phantom_node_vector[start_node][0]}; + raw_route.segment_end_coordinates.emplace_back(viapoint); + + // check round trip with this starting point is shorter than the shortest round trip found till now + if (trip_dist < min_route.shortest_path_length) { + min_route = raw_route; + min_route.shortest_path_length = trip_dist; + //TODO: this gets copied right? fix this + min_loc_permutation = loc_permutation; + } + } + + // // ALWAYS START AT ANOTHER STARTING POINT + // for(auto start_node : locations) { + // SimpleLogger().Write() << "STARTING AT " << start_node; + // int curr_node = start_node; + // InternalRouteResult raw_route; + // //TODO: Should we always use the same vector or does it not matter at all because of loop scope? + // std::vector loc_permutation(number_of_locations, -1); + + // // visited[i] indicates whether node i was already visited by the salesman + // std::vector visited(number_of_locations, false); + // visited[start_node] = true; + // loc_permutation[start_node] = 0; + + // PhantomNodes viapoint; + // // 3. REPEAT FOR EVERY UNVISITED NODE + // int trip_dist = 0; + // for(int via_point = 1; via_point < size_of_component; ++via_point) + // { + // int min_dist = std::numeric_limits::max(); + // int min_id = -1; + + // // 2. FIND NEAREST NEIGHBOUR + // for (auto next : locations) { + // if(!visited[next] && + // *(dist_table.begin() + curr_node * number_of_locations + next) < min_dist) { + // min_dist = *(dist_table.begin() + curr_node * number_of_locations + next); + // min_id = next; + // } + // } + + // loc_permutation[min_id] = via_point; + // visited[min_id] = true; + // SimpleLogger().Write() << "MOVING TO " << min_id; + // viapoint = PhantomNodes{phantom_node_vector[curr_node][0], phantom_node_vector[min_id][0]}; + // raw_route.segment_end_coordinates.emplace_back(viapoint); + // trip_dist += min_dist; + // curr_node = min_id; + // } + + // // 4. ROUTE BACK TO STARTING POINT + // viapoint = PhantomNodes{raw_route.segment_end_coordinates.back().target_phantom, phantom_node_vector[start_node][0]}; + // raw_route.segment_end_coordinates.emplace_back(viapoint); + + // // check round trip with this starting point is shorter than the shortest round trip found till now + // if (trip_dist < min_route.shortest_path_length) { + // min_route = raw_route; + // min_route.shortest_path_length = trip_dist; + // //TODO: this gets copied right? fix this + // min_loc_permutation = loc_permutation; + // } + // } +} + void NearestNeighbourTSP(const PhantomNodeArray & phantom_node_vector, const std::vector & dist_table, InternalRouteResult & min_route,