Skip to content

Commit

Permalink
Update visualization and cmd args
Browse files Browse the repository at this point in the history
  • Loading branch information
GoncaloPascoal committed Jun 23, 2022
1 parent e8123be commit 2e8619e
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 22 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ add_executable(${PROJECT_NAME}
src/analysis/real_data.cpp
src/cvrp/cvrp.cpp
src/cvrp/stage_1.cpp
src/cvrp/stage_2.cpp
src/cvrp/visualization.cpp
src/data_structures/quadtree.cpp
src/data_structures/kd_tree.cpp
Expand Down
94 changes: 94 additions & 0 deletions src/cvrp/stage_2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@

#include <functional>

#include "stage_2.hpp"
#include "../algorithms/ant_colony.hpp"
#include "../algorithms/greedy.hpp"
#include "../algorithms/simulated_annealing.hpp"
#include "../algorithms/tabu_search.hpp"

using namespace std;

template<typename T>
void readOption(T& option, const char* prompt, function<T(const string&)> conversionFunc) {
cout << prompt << flush;
string str;
getline(cin, str);

if (!str.empty()) {
option = conversionFunc(str);
}
}

inline u64 convertUnsignedInt(const string& str) {
return stoull(str);
}

inline double convertDouble(const string& str) {
return stod(str);
}

bool convertBool(const string& str) {
string lower(str);
transform(str.begin(), str.end(), lower.begin(), ::tolower);
return lower == "yes";
}

CvrpSolution applyAntColonyOptimization(const CvrpInstance& instance, bool config) {
AntColonyConfig acoConfig;

readOption<double>(acoConfig.alpha, "Alpha: ", convertDouble);
readOption<double>(acoConfig.beta, "Beta: ", convertDouble);
readOption<u64>(acoConfig.numAnts, "Num ants: ", convertUnsignedInt);
readOption<u32>(acoConfig.eliteAnts, "Elite ants: ", convertUnsignedInt);
readOption<u64>(acoConfig.maxIterations, "Max. iterations: ", convertUnsignedInt);
readOption<bool>(acoConfig.useSwapHeuristic, "Use swap heuristic (yes / no): ", convertBool);

return antColonyOptimization(instance, acoConfig);
}

CvrpSolution applyClarkeWrightSavings(const CvrpInstance& instance, bool config) {
return clarkeWrightSavings(instance);
}

CvrpSolution applyGranularTabuSearch(const CvrpInstance& instance, bool config) {
size_t maxIterations = 1000;
double beta = 1.5;

readOption<u64>(maxIterations, "Max. iterations: ", convertUnsignedInt);
readOption<double>(beta, "Beta: ", convertDouble);

return granularTabuSearch(instance, maxIterations, beta);
}

CvrpSolution applyGreedyAlgorithm(const CvrpInstance& instance, bool config) {
return greedyAlgorithm(instance);
}

CvrpSolution applySimulatedAnnealing(const CvrpInstance& instance, bool config) {
InitialSolution initialSolution;

readOption<InitialSolution>(
initialSolution,
"Initial solution (0 - trivial, 1 - greedy, 2 - Clarke-Wright): ",
[](const string& str) { return (InitialSolution) stoi(str); }
);

return simulatedAnnealing(instance, initialSolution);
}

CvrpSolution applyCvrpAlgorithm(string algorithm, const CvrpInstance& instance, bool config) {
static const unordered_map<const char*, function<CvrpSolution(const CvrpInstance&, bool)>> algorithms = {
{"aco", applyAntColonyOptimization},
{"cws", applyClarkeWrightSavings},
{"gts", applyGranularTabuSearch},
{"greedy", applyGreedyAlgorithm},
{"sa", applySimulatedAnnealing}
};

if (algorithms.count(algorithm.c_str())) {
return algorithms.at(algorithm.c_str())(instance, config);
}

return { {} , 0 };
}
10 changes: 10 additions & 0 deletions src/cvrp/stage_2.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

#ifndef CVRP_STAGE_2_H
#define CVRP_STAGE_2_H

#include <string>
#include "cvrp.hpp"

CvrpSolution applyCvrpAlgorithm(std::string algorithm, const CvrpInstance& instance, bool config);

#endif // CVRP_STAGE_2_H
23 changes: 19 additions & 4 deletions src/cvrp/visualization.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ sf::Vector2f vecFromCoordinates(const Coordinates& coords, float scale = 1.0) {
return sf::Vector2f(coords.getLongitude() * scale, coords.getLatitude() * scale);
}

GraphVisualizationResult generateGraphVisualization(const OsmXmlData& data, float scale) {
GraphVisualizationResult result;
GraphVisualizationResult* generateGraphVisualization(const OsmXmlData& data, float scale) {
GraphVisualizationResult* resultPtr = new GraphVisualizationResult();
GraphVisualizationResult& result = *resultPtr;

// TODO: make this a parameter?
result.gv->setScale(5);
Expand Down Expand Up @@ -49,7 +50,7 @@ GraphVisualizationResult generateGraphVisualization(const OsmXmlData& data, floa
);
setGraphCenter(*(result.gv), center, scale);

return result;
return resultPtr;
}

void setGraphCenter(GraphViewer& gv, const Coordinates& coords, float scale) {
Expand Down Expand Up @@ -110,14 +111,20 @@ void highlightPath(GraphVisualizationResult& result, const list<u64>& path, cons
static const u32 MAX_RGB = 510;

void showSolution(GraphVisualizationResult& result, const MapMatchingResult& mmResult, const Graph<OsmNode>& graph, const CvrpSolution& solution) {
auto matchedNode = [mmResult](u64 idx) {
auto matchedNode = [&mmResult](u64 idx) {
return idx == 0 ? mmResult.originNode : mmResult.deliveryNodes[idx - 1];
};

auto randomEngine = default_random_engine(
chrono::system_clock::now().time_since_epoch().count()
);

auto& origin = result.gv->getNode(mmResult.originNode);
origin.setColor(sf::Color::Green);
origin.setSize(35);
origin.setLabel("O");
origin.enable();

for (const auto& route : solution.routes) {
array<u32, 3> rgb = {0, 0, 0};
rgb[0] = min(MAX_RGB, (u32) rand() % 256);
Expand All @@ -131,6 +138,14 @@ void showSolution(GraphVisualizationResult& result, const MapMatchingResult& mmR
u64 from = matchedNode(route[i]), to = matchedNode(route[i + 1]);
list<u64> path = aStarSearch(graph, from, to).first;
highlightPath(result, path, color);

if (route[i + 1] != 0) {
auto& node = result.gv->getNode(to);
node.setColor(color);
node.setSize(35);
node.setLabel(to_string(route[i + 1]));
node.enable();
}
}
}
}
2 changes: 1 addition & 1 deletion src/cvrp/visualization.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ struct GraphVisualizationResult {
}
};

GraphVisualizationResult generateGraphVisualization(const OsmXmlData& data, float scale = 200000.0);
GraphVisualizationResult* generateGraphVisualization(const OsmXmlData& data, float scale = 200000.0);
void setGraphCenter(GraphViewer& gv, const Coordinates& coords, float scale = 200000.0);
void showMapMatchingResults(GraphViewer& gv, const CvrpInstance& instance,
const MapMatchingResult& result, float scale = 200000.0);
Expand Down
76 changes: 59 additions & 17 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@
#include "analysis/real_data.hpp"
#include "cvrp/cvrp.hpp"
#include "cvrp/stage_1.hpp"
#include "cvrp/stage_2.hpp"
#include "cvrp/visualization.hpp"
#include "utils.hpp"

using namespace std;

static const unordered_set<const char*> algorithms = {
"greedy", "cws", "sa", "gts", "aco"
};

int main(int argc, char** argv) {
cxxopts::Options opts("cvrp", "Solver for large CVRP instances from the LoggiBUD dataset");

Expand All @@ -25,11 +30,14 @@ int main(int argc, char** argv) {
("dm", "[OPT] Path to distance matrix", cxxopts::value<string>())
("vmm", "[OPT] Visualize map matching")
("vsp", "[OPT] Visualize shortest paths (for depot point)")
("vs", "[OPT] Visualize the CVRP solution obtained by the solver")
("t,threads", "[OPT] Number of threads to use in shortest path calculation", cxxopts::value<u32>()->default_value("1"))
("h,help", "[OPT] Print usage")
("l,logs", "[OPT] Enable additional execution logs")
("quadtree", "[OPT] Use quadtrees instead of k-d trees for map matching")
("bin-heap", "[OPT] Use binary heaps instead of Fibonacci heaps for Dijkstra's algorithm")
("a,algorithm", "[OPT] Algorithm used to solve the CVRP. Possibilities are: 'greedy', 'cws', 'sa', 'gts' and 'aco'. Defaults to 'cws'", cxxopts::value<string>())
("c,config", "[OPT] Use custom configuration for chosen CVRP algorithm")
;

auto result = opts.parse(argc, argv);
Expand All @@ -39,11 +47,24 @@ int main(int argc, char** argv) {
exit(0);
}

string cvrpAlgorithm = "cws";
if (result.count("algorithm")) {
cvrpAlgorithm = result["algorithm"].as<string>();
if (!algorithms.count(cvrpAlgorithm.c_str())) {
cerr << "Error: `algorithm` must be a valid CVRP algorithm (given: '"
<< cvrpAlgorithm << "')." << endl;
exit(1);
}
}

bool logs = result["logs"].as<bool>();
u32 threads = result["threads"].as<u32>();

bool mmVis = result["vmm"].as<bool>(), spVis = result["vsp"].as<bool>();

bool mmVis = result["vmm"].as<bool>(), spVis = result["vsp"].as<bool>(),
solVis = result["vs"].as<bool>();

bool config = result["config"].as<bool>();

MapMatchingDataStructure mmDataStructure = result["quadtree"].as<bool>() ? QUADTREE : KD_TREE;
ShortestPathDataStructure spDataStructure = result["bin-heap"].as<bool>() ? BINARY_HEAP : FIBONACCI_HEAP;

Expand All @@ -67,21 +88,27 @@ int main(int argc, char** argv) {
ifstream ifs(cvrpPath);
CvrpInstance instance(ifs);

cout << "Generating graph visualization..." << endl;
GraphVisualizationResult gvr = generateGraphVisualization(data);
GraphViewer& gv = gvr.gvRef();
GraphVisualizationResult* gvr;
GraphViewer* gv = nullptr;

if (spVis || mmVis || solVis) {
cout << "Generating graph visualization..." << endl;
gvr = generateGraphVisualization(data);
gv = gvr->gv;
}

cout << "Matching coordinates to OSM network nodes..." << endl;
MapMatchingResult mmResult = matchLocations(data, instance, mmDataStructure, logs);
showMapMatchingResults(gv, instance, mmResult);
setGraphCenter(gv, instance.getOrigin());
gv.setZipEdges(true);
gv.setEnabledEdgesText(false);

if (mmVis) {
gv.createWindow(1280, 720);
gv.join();
gv.closeWindow();
showMapMatchingResults(*gv, instance, mmResult);
setGraphCenter(*gv, instance.getOrigin());
gv->setZipEdges(true);
gv->setEnabledEdgesText(false);

gv->createWindow(1280, 720);
gv->join();
gv->closeWindow();
}

string dmPath = "";
Expand All @@ -107,19 +134,34 @@ int main(int argc, char** argv) {
mmResult.originNode, mmResult.deliveryNodes, spDataStructure);

for (const auto& res : spResult) {
highlightPath(gvr, res.path);
highlightPath(*gvr, res.path);
}
gv.setZipEdges(true);
gv->setZipEdges(true);

gv.createWindow(1280, 720);
gv.join();
gv.closeWindow();
gv->createWindow(1280, 720);
gv->join();
gv->closeWindow();
}

if (!dmPath.empty()) {
instance.writeDistanceMatrixToFile(dmPath.c_str());
}
}

CvrpSolution solution = applyCvrpAlgorithm(cvrpAlgorithm, instance, config);

cout << "Final solution has length " << solution.length / 1000.0 << " and uses "
<< solution.routes.size() << " vehicles." << endl;

if (solVis) {
showSolution(*gvr, mmResult, data.graph, solution);
gv->setZipEdges(true);
gv->createWindow(1280, 720);
gv->join();
gv->closeWindow();
}

delete gvr;
}
else {
cerr << "Error: `cvrp` and `osm` options are required." << endl;
Expand Down

0 comments on commit 2e8619e

Please sign in to comment.