From 54691cfcc29fcd2d1870fc8311ceb09392095a97 Mon Sep 17 00:00:00 2001 From: p-mary <mary@labri.fr> Date: Fri, 30 Nov 2018 14:33:59 +0100 Subject: [PATCH] add tlp::selectShortestPaths(Graph*, node, node, ...) in GraphTools + cleanup and performance improvements for path finder interactor --- .../tulip-core/include/tulip}/Dikjstra.h | 47 ++-- library/tulip-core/include/tulip/GraphTools.h | 20 ++ library/tulip-core/src/CMakeLists.txt | 1 + library/tulip-core/src/Dikjstra.cpp | 218 ++++++++++++++++ library/tulip-core/src/GraphTools.cpp | 52 ++++ plugins/interactor/PathFinder/CMakeLists.txt | 1 - plugins/interactor/PathFinder/PathFinder.cpp | 8 +- plugins/interactor/PathFinder/PathFinder.h | 2 +- .../PathFinderConfigurationWidget.cpp | 9 +- .../PathFinderConfigurationWidget.h | 4 +- .../PathFinder/PathFinding/DFS/DFS.cpp | 31 +-- .../PathFinder/PathFinding/DFS/DFS.h | 8 +- .../PathFinding/Dikjstra/Dikjstra.cpp | 232 ------------------ .../PathFinder/PathFinding/PathAlgorithm.cpp | 104 ++++---- .../PathFinder/PathFinding/PathAlgorithm.h | 7 +- .../designer/PathFinderConfiguration.ui | 13 +- tests/gui/interactor_path_finder.tlp | 22 +- 17 files changed, 421 insertions(+), 358 deletions(-) rename {plugins/interactor/PathFinder/PathFinding/Dikjstra => library/tulip-core/include/tulip}/Dikjstra.h (64%) create mode 100644 library/tulip-core/src/Dikjstra.cpp delete mode 100644 plugins/interactor/PathFinder/PathFinding/Dikjstra/Dikjstra.cpp diff --git a/plugins/interactor/PathFinder/PathFinding/Dikjstra/Dikjstra.h b/library/tulip-core/include/tulip/Dikjstra.h similarity index 64% rename from plugins/interactor/PathFinder/PathFinding/Dikjstra/Dikjstra.h rename to library/tulip-core/include/tulip/Dikjstra.h index adcdb247bc..7d31983343 100644 --- a/plugins/interactor/PathFinder/PathFinding/Dikjstra/Dikjstra.h +++ b/library/tulip-core/include/tulip/Dikjstra.h @@ -17,11 +17,13 @@ * */ -#ifndef DIKJSTRA_H -#define DIKJSTRA_H +///@cond DOXYGEN_HIDDEN +#ifndef DIKJSTRA_TOOL_H +#define DIKJSTRA_TOOL_H #include <vector> #include <set> #include <climits> +#include <functional> #include <tulip/Graph.h> #include <tulip/Vector.h> #include <tulip/LayoutProperty.h> @@ -29,28 +31,27 @@ #include <tulip/StaticProperty.h> #include <tulip/MutableContainer.h> -#include "../PathAlgorithm.h" - namespace tlp { -class Dikjstra : public PathAlgorithm { +class Dikjstra { public: //============================================================ - void initDikjstra(const tlp::Graph *const graph, const tlp::Graph *const forbiddenNodes, - tlp::node src, EdgeOrientation directed, - const tlp::EdgeStaticProperty<double> &weights, double maxDist, - const std::set<tlp::node> &focus); + Dikjstra(const Graph *const graph, + node src, + const EdgeStaticProperty<double> &weights, + NodeStaticProperty<double> &nodeDistance, + std::function<Iterator<edge>* (node)>& getFunc); //======================================================== - bool searchPaths(tlp::node n, tlp::BooleanProperty *result); + bool searchPaths(node n, BooleanProperty *result); //========================================================= - bool searchPath(tlp::node n, tlp::BooleanProperty *result, std::vector<tlp::node> &vNodes); + bool searchPath(node n, BooleanProperty *result); //============================================================= private: - void internalSearchPaths(tlp::node n, tlp::BooleanProperty *result); + void internalSearchPaths(node n, BooleanProperty *result); //========================================================= struct DikjstraElement { - DikjstraElement(const double dist = DBL_MAX, const tlp::node previous = tlp::node(), - const tlp::node n = tlp::node()) + DikjstraElement(const double dist = DBL_MAX, const node previous = node(), + const node n = node()) : dist(dist), previous(previous), n(n) {} bool operator==(const DikjstraElement &b) const { return n == b.n; @@ -59,9 +60,9 @@ class Dikjstra : public PathAlgorithm { return n != b.n; } double dist; - tlp::node previous; - tlp::node n; - std::vector<tlp::edge> usedEdge; + node previous; + node n; + std::vector<edge> usedEdge; }; struct LessDikjstraElement { @@ -74,13 +75,11 @@ class Dikjstra : public PathAlgorithm { } }; - tlp::Graph const *graph; - tlp::Graph const *forbiddenNodes; - tlp::node src; - tlp::MutableContainer<bool> usedEdges; - -public: - tlp::MutableContainer<double> nodeDistance; + Graph const *graph; + node src; + MutableContainer<bool> usedEdges; + NodeStaticProperty<double>& nodeDistance; }; } // namespace tlp #endif // DIKJSTRA_H +///@endcond diff --git a/library/tulip-core/include/tulip/GraphTools.h b/library/tulip-core/include/tulip/GraphTools.h index a1cfcb0f7f..f44dffc619 100644 --- a/library/tulip-core/include/tulip/GraphTools.h +++ b/library/tulip-core/include/tulip/GraphTools.h @@ -174,6 +174,26 @@ TLP_SCOPE void buildEdgesUniformQuantification(const Graph *graph, const Numeric */ TLP_SCOPE unsigned makeSelectionGraph(const Graph *graph, BooleanProperty *selection, bool *test = nullptr); + +enum ShortestPathType { OnePath = 0, OneDirectedPath = 1 , OneReversedPath = 2, + AllPaths = 3, AllDirectedPaths = 4, AllReversedPaths = 5 }; + +/** + * @brief set selection to the shortets paths + * @param graph The graph to compute on. + * @param src The source node of the paths + * @param tgt The target node of the paths + * @param pathType The type of path to consider + * @param weights A Double property to get the edges weight if weighted paths have to be considered. Can be set to null to select unweighted paths. + * @param selection The Boolean property to consider as selection. + * @return false if no path exist between the src and tgt nodes; true if not. + */ +TLP_SCOPE bool selectShortestPaths(const Graph *const graph, + node src, node tgt, + ShortestPathType pathType, + const DoubleProperty *const weights, + BooleanProperty* selection); + } // namespace tlp #endif ///@endcond diff --git a/library/tulip-core/src/CMakeLists.txt b/library/tulip-core/src/CMakeLists.txt index 36f8d31d10..3b59265dab 100644 --- a/library/tulip-core/src/CMakeLists.txt +++ b/library/tulip-core/src/CMakeLists.txt @@ -12,6 +12,7 @@ ConnectedTest.cpp ConvexHull.cpp DataSet.cpp Delaunay.cpp +Dikjstra.cpp DoubleProperty.cpp DrawingTools.cpp FaceIterator.cpp diff --git a/library/tulip-core/src/Dikjstra.cpp b/library/tulip-core/src/Dikjstra.cpp new file mode 100644 index 0000000000..d7ef08538e --- /dev/null +++ b/library/tulip-core/src/Dikjstra.cpp @@ -0,0 +1,218 @@ +/** + * + * This file is part of Tulip (http://tulip.labri.fr) + * + * Authors: David Auber and the Tulip development Team + * from LaBRI, University of Bordeaux + * + * Tulip is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Tulip is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + */ + +#include <tulip/Dikjstra.h> +#include <tulip/LayoutProperty.h> +#include <tulip/BooleanProperty.h> +#include <tulip/GraphTools.h> + +using namespace tlp; +using namespace std; + +//============================================================ +Dikjstra::Dikjstra(const Graph *const graph, node src, + const EdgeStaticProperty<double> &weights, + NodeStaticProperty<double> &nd, + std::function<Iterator<edge>* (node)>& getEdges): + nodeDistance(nd) { + assert(src.isValid()); + this->graph = graph; + usedEdges.setAll(false); + this->src = src; + set<DikjstraElement *, LessDikjstraElement> dikjstraTable; + NodeStaticProperty<DikjstraElement *> mapDik(graph); + mapDik.setAll(nullptr); + + unsigned int i = 0; + for (auto n : graph->nodes()) { + DikjstraElement *tmp; + if (n != src) + // init all nodes to +inf + tmp = new DikjstraElement(DBL_MAX / 2. + 10., node(), n); + else + // init starting node to 0 + tmp = new DikjstraElement(0, n, n); + + dikjstraTable.insert(tmp); + + mapDik[i++] = tmp; + } + + while (!dikjstraTable.empty()) { + // select the first element in the list the one with min value + set<DikjstraElement *, LessDikjstraElement>::iterator it = dikjstraTable.begin(); + DikjstraElement &u = *(*it); + dikjstraTable.erase(it); + + edge e; + + Iterator<edge> *iter = getEdges(u.n); + + while (iter->hasNext()) { + e = iter->next(); + node v = graph->opposite(e, u.n); + auto dEle = mapDik[v]; + double eWeight = weights.getEdgeValue(e); + assert(eWeight > 0); + + if (fabs((u.dist + eWeight) - dEle->dist) < 1E-9) // path of the same length + dEle->usedEdge.push_back(e); + else if ((u.dist + eWeight) < dEle->dist) { + // we find a node closer with that path + dEle->usedEdge.clear(); + //********************************************** + dikjstraTable.erase(dEle); + + dEle->dist = u.dist + eWeight; + dEle->previous = u.n; + dEle->usedEdge.push_back(e); + dikjstraTable.insert(dEle); + } + } delete iter; + } + + usedEdges.setAll(false); + i = 0; + for (auto n : graph->nodes()) { + DikjstraElement *dEle = mapDik[i++]; + nodeDistance[n] = dEle->dist; + + for (auto e : dEle->usedEdge) { + usedEdges.set(e.id, true); + } + + delete dEle; + } +} +//============================================================================= +bool Dikjstra::searchPath(node n, BooleanProperty *result) { + bool ok = true; + + while (ok) { + result->setNodeValue(n, true); + ok = false; + Iterator<edge>* it = graph->getInOutEdges(n); + while (it->hasNext()) { + edge e = it->next(); + if (!usedEdges.get(e.id)) + continue; // edge does not belong to the shortest path + + if (result->getEdgeValue(e)) + continue; // edge already treated + + node tgt = graph->opposite(e, n); + + if (nodeDistance[tgt] >= nodeDistance[n]) + continue; + + n = tgt; + result->setEdgeValue(e, true); + ok = true; + break; + } delete it; + } + + if (n != src) { +#ifndef NDEBUG + cout << "Path does not exist !" << endl; +#endif /* NDEBUG */ + return false; + } + + return true; +} +//======================================================================= +void Dikjstra::internalSearchPaths(node n, BooleanProperty *result) { + result->setNodeValue(n, true); + for (auto e : graph->getInOutEdges(n)) { + if (!usedEdges.get(e.id)) + continue; + + if (result->getEdgeValue(e)) + continue; + + node tgt = graph->opposite(e, n); + + if (nodeDistance[tgt] >= nodeDistance[n]) + continue; + + result->setEdgeValue(e, true); + if (!result->getNodeValue(tgt)) + internalSearchPaths(tgt, result); + } +} +//======================================== +bool Dikjstra::searchPaths(node n, BooleanProperty *result) { + internalSearchPaths(n, result); + return result->getNodeValue(src); +} + +//======================================== +#define SMALLEST_WEIGHT 1.E-6 + +bool selectShortestPaths(const Graph *const graph, + node src, node tgt, + ShortestPathType pathType, + const DoubleProperty *const weights, + BooleanProperty *result) { + std::function<Iterator<edge>* (node)> getOutEdges = + [&](node un) { return graph->getOutEdges(un); }; + std::function<Iterator<edge>* (node)> getInOutEdges = + [&](node un) { return graph->getInOutEdges(un); }; + std::function<Iterator<edge>* (node)> getInEdges = + [&](node un) { return graph->getInEdges(un); }; + + std::function<Iterator<edge>* (node)> getEdges; + switch (pathType) { + case ShortestPathType::OnePath: + case ShortestPathType::AllPaths: + getEdges = getInOutEdges; + break; + case ShortestPathType::OneDirectedPath: + case ShortestPathType::AllDirectedPaths: + getEdges = getOutEdges; + break; + case ShortestPathType::OneReversedPath: + case ShortestPathType::AllReversedPaths: + getEdges = getInEdges; + break; + } + + EdgeStaticProperty<double> eWeights(graph); + if (!weights) { + eWeights.setAll(SMALLEST_WEIGHT); + } else { + auto fn = [&](edge e, unsigned int i) { + double val(weights->getEdgeValue(e)); + + eWeights[i] = val ? val : SMALLEST_WEIGHT; + }; + TLP_PARALLEL_MAP_EDGES_AND_INDICES(graph, fn); + } + + NodeStaticProperty<double> nodeDistance(graph); + Dikjstra dikjstra(graph, src, eWeights, nodeDistance, getEdges); + + result->setAllNodeValue(false); + result->setAllEdgeValue(false); + + if (uint(pathType) < ShortestPathType::AllPaths) + return dikjstra.searchPath(tgt, result); + return dikjstra.searchPaths(tgt, result); +} diff --git a/library/tulip-core/src/GraphTools.cpp b/library/tulip-core/src/GraphTools.cpp index a98d1aaa77..ce2e18fca5 100644 --- a/library/tulip-core/src/GraphTools.cpp +++ b/library/tulip-core/src/GraphTools.cpp @@ -31,6 +31,7 @@ #include <tulip/Ordering.h> #include <tulip/PlanarConMap.h> #include <tulip/GraphParallelTools.h> +#include <tulip/Dikjstra.h> #include <queue> #include <stack> @@ -713,4 +714,55 @@ unsigned makeSelectionGraph(const Graph *graph, BooleanProperty *selection, bool return added; } + +#define SMALLEST_WEIGHT 1.E-6 + +bool selectShortestPaths(const Graph *const graph, + node src, node tgt, + ShortestPathType pathType, + const DoubleProperty *const weights, + BooleanProperty *result) { + std::function<Iterator<edge>* (node)> getOutEdges = + [&](node un) { return graph->getOutEdges(un); }; + std::function<Iterator<edge>* (node)> getInOutEdges = + [&](node un) { return graph->getInOutEdges(un); }; + std::function<Iterator<edge>* (node)> getInEdges = + [&](node un) { return graph->getInEdges(un); }; + + std::function<Iterator<edge>* (node)> getEdges; + switch (pathType) { + case ShortestPathType::OnePath: + case ShortestPathType::AllPaths: + getEdges = getInOutEdges; + break; + case ShortestPathType::OneDirectedPath: + case ShortestPathType::AllDirectedPaths: + getEdges = getOutEdges; + break; + case ShortestPathType::OneReversedPath: + case ShortestPathType::AllReversedPaths: + getEdges = getInEdges; + break; + } + + EdgeStaticProperty<double> eWeights(graph); + if (!weights) { + eWeights.setAll(SMALLEST_WEIGHT); + } else { + auto fn = [&](edge e, unsigned int i) { + double val(weights->getEdgeValue(e)); + + eWeights[i] = val ? val : SMALLEST_WEIGHT; + }; + TLP_PARALLEL_MAP_EDGES_AND_INDICES(graph, fn); + } + + NodeStaticProperty<double> nodeDistance(graph); + Dikjstra dikjstra(graph, src, eWeights, nodeDistance, getEdges); + + if (uint(pathType) < ShortestPathType::AllPaths) + return dikjstra.searchPath(tgt, result); + return dikjstra.searchPaths(tgt, result); +} + } // namespace tlp diff --git a/plugins/interactor/PathFinder/CMakeLists.txt b/plugins/interactor/PathFinder/CMakeLists.txt index ea9025564f..9fc421593d 100644 --- a/plugins/interactor/PathFinder/CMakeLists.txt +++ b/plugins/interactor/PathFinder/CMakeLists.txt @@ -8,7 +8,6 @@ SET(LIB_SRCS PathFinderTools.cpp PathFinding/PathAlgorithm.cpp PathFinding/DFS/DFS.cpp - PathFinding/Dikjstra/Dikjstra.cpp highlighters/EnclosingCircleHighlighter.cpp highlighters/PathHighlighter.cpp highlighters/ZoomAndPanHighlighter.cpp diff --git a/plugins/interactor/PathFinder/PathFinder.cpp b/plugins/interactor/PathFinder/PathFinder.cpp index 369354b7e1..45fefce6f7 100644 --- a/plugins/interactor/PathFinder/PathFinder.cpp +++ b/plugins/interactor/PathFinder/PathFinder.cpp @@ -50,8 +50,8 @@ PathFinder::PathFinder(const tlp::PluginContext *) tolerance(DEFAULT_TOLERANCE), _configurationWidget(nullptr), highlightersListWidget(nullptr), configureHighlighterBtn(nullptr) { - edgeOrientationLabels[PathAlgorithm::Oriented] = "Consider edges as oriented"; - edgeOrientationLabels[PathAlgorithm::NonOriented] = "Consider edges as non-oriented"; + edgeOrientationLabels[PathAlgorithm::Directed] = "Consider edges as directed"; + edgeOrientationLabels[PathAlgorithm::Undirected] = "Consider edges as undirected"; edgeOrientationLabels[PathAlgorithm::Reversed] = "Consider edges as reversed"; pathsTypesLabels[PathAlgorithm::AllPaths] = "Select all paths"; pathsTypesLabels[PathAlgorithm::AllShortest] = "Select all shortest paths"; @@ -174,9 +174,7 @@ void PathFinder::setPathsType(const QString &pathType) { } bool disabled(pathsTypes != PathAlgorithm::AllPaths); - _configurationWidget->toleranceCheckDisabled(disabled); - _configurationWidget->toleranceSpinDisabled(disabled); - _configurationWidget->toleranceLabelDisabled(disabled); + _configurationWidget->toleranceDisabled(disabled); } double PathFinder::getTolerance() { diff --git a/plugins/interactor/PathFinder/PathFinder.h b/plugins/interactor/PathFinder/PathFinder.h index 67aacb7879..9b7af3ee7e 100644 --- a/plugins/interactor/PathFinder/PathFinder.h +++ b/plugins/interactor/PathFinder/PathFinder.h @@ -26,7 +26,7 @@ #include "PathFinding/PathAlgorithm.h" #define NO_METRIC "None" -#define DEFAULT_ORIENTATION PathAlgorithm::NonOriented +#define DEFAULT_ORIENTATION PathAlgorithm::Undirected #define DEFAULT_PATHS_TYPE PathAlgorithm::OneShortest #define DEFAULT_TOLERANCE 100 #define DEFAULT_TOLERANCE_ACTIVATION false diff --git a/plugins/interactor/PathFinder/PathFinderConfigurationWidget.cpp b/plugins/interactor/PathFinder/PathFinderConfigurationWidget.cpp index 0a8259b64e..cc72e8385e 100644 --- a/plugins/interactor/PathFinder/PathFinderConfigurationWidget.cpp +++ b/plugins/interactor/PathFinder/PathFinderConfigurationWidget.cpp @@ -85,14 +85,9 @@ void PathFinderConfigurationWidget::addbottomWidget(QWidget *w) { _ui->bottomArea->addWidget(w, 0, Qt::AlignLeft); } -void PathFinderConfigurationWidget::toleranceCheckDisabled(const bool disabled) { +void PathFinderConfigurationWidget::toleranceDisabled(const bool disabled) { _ui->toleranceCheck->setDisabled(disabled); -} - -void PathFinderConfigurationWidget::toleranceSpinDisabled(const bool disabled) { _ui->toleranceSpin->setDisabled(disabled); -} - -void PathFinderConfigurationWidget::toleranceLabelDisabled(const bool disabled) { _ui->toleranceLabel->setDisabled(disabled); + _ui->tolerancePercentLabel->setDisabled(disabled); } diff --git a/plugins/interactor/PathFinder/PathFinderConfigurationWidget.h b/plugins/interactor/PathFinder/PathFinderConfigurationWidget.h index 60c0f07b0c..30fc157e64 100644 --- a/plugins/interactor/PathFinder/PathFinderConfigurationWidget.h +++ b/plugins/interactor/PathFinder/PathFinderConfigurationWidget.h @@ -51,9 +51,7 @@ class PathFinderConfigurationWidget : public QWidget { void setToleranceSpinValue(const int val); void highlightersLabelDisabled(const bool disable); void addbottomWidget(QWidget *w); - void toleranceCheckDisabled(const bool disabled); - void toleranceSpinDisabled(const bool disabled); - void toleranceLabelDisabled(const bool disabled); + void toleranceDisabled(const bool disabled); signals: void setWeightMetric(const QString &); diff --git a/plugins/interactor/PathFinder/PathFinding/DFS/DFS.cpp b/plugins/interactor/PathFinder/PathFinding/DFS/DFS.cpp index c0acd13641..77cab2e3a7 100644 --- a/plugins/interactor/PathFinder/PathFinding/DFS/DFS.cpp +++ b/plugins/interactor/PathFinder/PathFinding/DFS/DFS.cpp @@ -26,25 +26,28 @@ using namespace tlp; using namespace std; -DFS::DFS(Graph *graph, BooleanProperty *result, DoubleProperty *dists, node tgt, +DFS::DFS(Graph *graph, BooleanProperty *result, node tgt, const EdgeStaticProperty<double> &eWeights, EdgeOrientation edgesOrientation, double maxDist) - : graph(graph), result(result), dists(dists), tgt(tgt), weights(eWeights), currentDist(0), + : graph(graph), result(result), tgt(tgt), weights(eWeights), currentDist(0), edgesOrientation(edgesOrientation), maxDist(maxDist) { #ifndef NDEBUG assert(graph->getRoot() == result->getGraph()->getRoot()); #endif /* NDEBUG */ - dists->setAllNodeValue(DBL_MAX); - visitable = new BooleanProperty(graph); - visitable->setAllNodeValue(true); - visitable->setAllEdgeValue(true); } -DFS::~DFS() { - delete visitable; +bool DFS::searchPaths(node src) { + DoubleProperty dists(result->getGraph()); + dists.setAllNodeValue(DBL_MAX); + + BooleanProperty visitable(graph); + visitable.setAllNodeValue(true); + + return computeSearchPaths(src, &visitable, &dists); } -bool DFS::searchPaths(node src) { +bool DFS::computeSearchPaths(node src, BooleanProperty *visitable, + DoubleProperty *dists) { if (!visitable->getNodeValue(src)) return false; @@ -78,17 +81,17 @@ bool DFS::searchPaths(node src) { return true; } - bool result = false; + bool res = false; visitable->setNodeValue(src, false); Iterator<edge> *edgeIt = nullptr; switch (edgesOrientation) { - case NonOriented: + case Undirected: edgeIt = graph->getInOutEdges(src); break; - case Oriented: + case Directed: edgeIt = graph->getOutEdges(src); break; @@ -100,11 +103,11 @@ bool DFS::searchPaths(node src) { for (auto e : edgeIt) { currentDist += weights.getEdgeValue(e); path.push_back(e); - result |= searchPaths(graph->opposite(e, src)); + res |= computeSearchPaths(graph->opposite(e, src), visitable, dists); path.pop_back(); currentDist -= weights.getEdgeValue(e); } visitable->setNodeValue(src, true); - return result; + return res; } diff --git a/plugins/interactor/PathFinder/PathFinding/DFS/DFS.h b/plugins/interactor/PathFinder/PathFinding/DFS/DFS.h index 2801de37f2..605bf411f1 100644 --- a/plugins/interactor/PathFinder/PathFinding/DFS/DFS.h +++ b/plugins/interactor/PathFinder/PathFinding/DFS/DFS.h @@ -50,12 +50,10 @@ class DFS : public PathAlgorithm { * @param maxDist The maximal distance the algorithm can go from the source node before dropping * the search (DBL_MAX by default) */ - DFS(tlp::Graph *graph, tlp::BooleanProperty *result, tlp::DoubleProperty *dists, tlp::node tgt, + DFS(tlp::Graph *graph, tlp::BooleanProperty *result, tlp::node tgt, const tlp::EdgeStaticProperty<double> &weights, EdgeOrientation edgesOrientation, double maxDist = DBL_MAX); - ~DFS(); - /** * Compute the path between the source node and the target node. Caution ! This method is * recursive and could cause a stack overflow on big graphs. @@ -67,14 +65,14 @@ class DFS : public PathAlgorithm { private: tlp::Graph *graph; tlp::BooleanProperty *result; - tlp::DoubleProperty *dists; - tlp::BooleanProperty *visitable; tlp::node tgt; const tlp::EdgeStaticProperty<double> &weights; std::vector<tlp::edge> path; double currentDist; EdgeOrientation edgesOrientation; double maxDist; + + bool computeSearchPaths(tlp::node src, tlp::BooleanProperty *visitable, tlp::DoubleProperty *dists); }; } // namespace tlp #endif /* DFS_H_ */ diff --git a/plugins/interactor/PathFinder/PathFinding/Dikjstra/Dikjstra.cpp b/plugins/interactor/PathFinder/PathFinding/Dikjstra/Dikjstra.cpp deleted file mode 100644 index 14974ad578..0000000000 --- a/plugins/interactor/PathFinder/PathFinding/Dikjstra/Dikjstra.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/** - * - * This file is part of Tulip (http://tulip.labri.fr) - * - * Authors: David Auber and the Tulip development Team - * from LaBRI, University of Bordeaux - * - * Tulip is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation, either version 3 - * of the License, or (at your option) any later version. - * - * Tulip is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU General Public License for more details. - * - */ - -#include "Dikjstra.h" -#include <tulip/LayoutProperty.h> -#include <tulip/BooleanProperty.h> - -using namespace tlp; -using namespace std; - -//============================================================ -void Dikjstra::initDikjstra(const tlp::Graph *const graph, const tlp::Graph *const forbiddenNodes, - tlp::node src, EdgeOrientation directed, - const tlp::EdgeStaticProperty<double> &weights, double, - const set<node> &focus) { - assert(src.isValid()); - this->graph = graph; - this->forbiddenNodes = forbiddenNodes; - usedEdges.setAll(false); - this->src = src; - set<DikjstraElement *, LessDikjstraElement> dikjstraTable; - set<DikjstraElement *, LessDikjstraElement> focusTable; - MutableContainer<DikjstraElement *> mapDik; - mapDik.setAll(nullptr); - - for (auto n : graph->nodes()) { - if (n != src) { // init all nodes to +inf - DikjstraElement *tmp = new DikjstraElement(DBL_MAX / 2. + 10., node(), n); - dikjstraTable.insert(tmp); - - if (focus.find(n) != focus.end()) - focusTable.insert(tmp); - - mapDik.set(n.id, tmp); - } else { // init starting node to 0 - DikjstraElement *tmp = new DikjstraElement(0, n, n); - dikjstraTable.insert(tmp); - mapDik.set(n.id, tmp); - } - } - - nodeDistance.setAll(DBL_MAX); - nodeDistance.set(src.id, 0); - - while (!dikjstraTable.empty()) { - // select the first element in the list the one with min value - set<DikjstraElement *, LessDikjstraElement>::iterator it = dikjstraTable.begin(); - DikjstraElement &u = *(*it); - dikjstraTable.erase(it); - - if (!focusTable.empty()) { - set<DikjstraElement *, LessDikjstraElement>::reverse_iterator it = focusTable.rbegin(); - // set<DikjstraElement *>::iterator it2 = focusTable.begin(); - double maxDist = (*it)->dist; - - if (u.dist > maxDist) - break; - - /* - if (fabs(u.dist - maxDist) > 1E-9) { - if (u.dist > maxDist) { - assert(maxDist < (DBL_MAX/2.)); - break; - } - } - */ - // if ((u.dist > maxDist) break; - } - - if (forbiddenNodes != nullptr) - if (forbiddenNodes->isElement(u.n) && u.n != this->src) - continue; - - edge e; - - Iterator<edge> *iter = nullptr; - - switch (directed) { - case Oriented: - iter = graph->getOutEdges(u.n); - break; - - case NonOriented: - iter = graph->getInOutEdges(u.n); - break; - - case Reversed: - iter = graph->getInEdges(u.n); - break; - } - - for (auto e : iter) { - node v = graph->opposite(e, u.n); - DikjstraElement &dEle = *mapDik.get(v.id); - double eWeight = weights.getEdgeValue(e); - assert(eWeight > 0); - - /* - if (DBL_MAX - u.dist < eWeight) - cerr << __PRETTY_FUNCTION__ << "at line : " << __LINE__ << " : Double overflow" << - endl; - if (DBL_MAX - fabs((u.dist + eWeight)) < dEle.dist) - cerr << __PRETTY_FUNCTION__ << "at line : " << __LINE__ << " : Double overflow" << - endl; - */ - if (fabs((u.dist + eWeight) - dEle.dist) < 1E-9) // path of the same length - dEle.usedEdge.push_back(e); - else - - // we find a node closer with that path - if ((u.dist + eWeight) < dEle.dist) { - dEle.usedEdge.clear(); - //********************************************** - dikjstraTable.erase(&dEle); - - if (focus.find(dEle.n) != focus.end()) { - focusTable.erase(&dEle); - } - - dEle.dist = u.dist + eWeight; - /* - if (DBL_MAX - u.dist < eWeight) - cerr << __PRETTY_FUNCTION__ << "at line : " << __LINE__ << " : Double overflow" << endl; - */ - dEle.previous = u.n; - dEle.usedEdge.push_back(e); - dikjstraTable.insert(&dEle); - - if (focus.find(dEle.n) != focus.end()) { - focusTable.insert(&dEle); - } - } - } - } - - usedEdges.setAll(false); - for (auto n : graph->nodes()) { - DikjstraElement *dEle = mapDik.get(n.id); - nodeDistance.set(n.id, dEle->dist); - - for (unsigned int i = 0; i < dEle->usedEdge.size(); ++i) { - usedEdges.set(dEle->usedEdge[i].id, true); - } - - delete dEle; - } -} -//======================================================================= -void Dikjstra::internalSearchPaths(node n, BooleanProperty *result) { - if (result->getNodeValue(n)) - return; - - result->setNodeValue(n, true); - for (auto e : graph->getInOutEdges(n)) { - if (!usedEdges.get(e.id)) - continue; - - if (result->getEdgeValue(e)) - continue; - - node tgt = graph->opposite(e, n); - - if (nodeDistance.get(tgt.id) >= nodeDistance.get(n.id)) - continue; - - result->setEdgeValue(e, true); - searchPaths(tgt, result); - } -} -//============================================================================= -bool Dikjstra::searchPath(node n, BooleanProperty *result, vector<node> &vNodes) { - result->setAllNodeValue(false); - result->setAllEdgeValue(false); - bool ok = true; - - while (ok) { - result->setNodeValue(n, true); - vNodes.push_back(n); - ok = false; - edge validEdge; - for (auto e : graph->getInOutEdges(n)) { - if (!usedEdges.get(e.id)) - continue; // edge does not belong to the shortest path - - if (result->getEdgeValue(e)) - continue; // edge already treated - - node tgt = graph->opposite(e, n); - - if (nodeDistance.get(tgt.id) >= nodeDistance.get(n.id)) - continue; - - validEdge = e; - } - - if (validEdge.isValid()) { - ok = true; - n = graph->opposite(validEdge, n); - result->setEdgeValue(validEdge, true); - } - } - - if (n != src) { -#ifndef NDEBUG - cout << "Path does not exist !" << endl; -#endif /* NDEBUG */ - return false; - } - - return true; -} -//======================================== -bool Dikjstra::searchPaths(tlp::node n, tlp::BooleanProperty *result) { - internalSearchPaths(n, result); - return result->getNodeValue(src); -} diff --git a/plugins/interactor/PathFinder/PathFinding/PathAlgorithm.cpp b/plugins/interactor/PathFinder/PathFinding/PathAlgorithm.cpp index ec14b6ecbb..7faf37264c 100644 --- a/plugins/interactor/PathFinder/PathFinding/PathAlgorithm.cpp +++ b/plugins/interactor/PathFinder/PathFinding/PathAlgorithm.cpp @@ -24,8 +24,8 @@ #include <tulip/MutableContainer.h> #include <tulip/Graph.h> #include <tulip/GraphParallelTools.h> +#include <tulip/GraphTools.h> -#include "Dikjstra/Dikjstra.h" #include "DFS/DFS.h" #define SMALLEST_WEIGHT 1.E-6 @@ -59,60 +59,66 @@ bool PathAlgorithm::computePath(Graph *graph, PathType pathType, EdgeOrientation assert(src != tgt); #endif /* NDEBUG */ - // We always compute Dikjstra as it is used in the all path computation too - EdgeStaticProperty<double> eWeights(graph); - - if (!weights) { - eWeights.setAll(SMALLEST_WEIGHT); + bool retVal = false; + tlp::ShortestPathType spt; + + if (pathType == AllShortest) { + switch (edgesOrientation) { + case Directed: + spt = ShortestPathType::AllDirectedPaths; + break; + case Undirected: + spt = ShortestPathType::AllPaths; + break; + case Reversed: + default: + spt = ShortestPathType::AllReversedPaths; + } } else { - auto fn = [&](edge e, unsigned int i) { - double val(weights->getEdgeValue(e)); - - eWeights[i] = val ? val : SMALLEST_WEIGHT; - }; - TLP_PARALLEL_MAP_EDGES_AND_INDICES(graph, fn); + switch (edgesOrientation) { + case Directed: + spt = ShortestPathType::OneDirectedPath; + break; + case Undirected: + spt = ShortestPathType::OnePath; + break; + case Reversed: + default: + spt = ShortestPathType::OneReversedPath; + } } + retVal = selectShortestPaths(graph, src, tgt, spt, weights, result); + if (pathType == AllPaths && retVal) { + EdgeStaticProperty<double> eWeights(graph); + + if (!weights) { + eWeights.setAll(SMALLEST_WEIGHT); + } else { + auto fn = [&](edge e, unsigned int i) { + double val(weights->getEdgeValue(e)); + + eWeights[i] = val ? val : SMALLEST_WEIGHT; + }; + TLP_PARALLEL_MAP_EDGES_AND_INDICES(graph, fn); + } - set<node> focus; - vector<node> vNodes; - Dikjstra dikjstra; - dikjstra.initDikjstra(graph, nullptr, src, edgesOrientation, eWeights, 0, focus); - - bool retVal = false; + double pathLength; - switch (pathType) { - case AllShortest: - retVal = dikjstra.searchPaths(tgt, result); - break; - - case OneShortest: - retVal = dikjstra.searchPath(tgt, result, vNodes); - break; - - case AllPaths: - retVal = dikjstra.searchPath(tgt, result, vNodes); - - if (retVal) { - double pathLength; - - if (tolerance == DBL_MAX) - pathLength = DBL_MAX; - else { - pathLength = computePathLength(result, eWeights); - pathLength *= tolerance; - } - - if (tolerance > 1) { // We only compute the other paths if the tolerance is greater than 1. - // Meaning that the user doesn't want only the shortest path. - result->setAllNodeValue(false); - result->setAllEdgeValue(false); - DoubleProperty dists(result->getGraph()); - DFS d(graph, result, &dists, tgt, eWeights, edgesOrientation, pathLength); - retVal = d.searchPaths(src); - } + if (tolerance == DBL_MAX) + pathLength = DBL_MAX; + else { + pathLength = computePathLength(result, eWeights); + pathLength *= tolerance; } - break; + if (tolerance > 1) { + // We only compute the other paths if the tolerance is greater than 1. + // Meaning that the user doesn't want only the shortest path. + result->setAllNodeValue(false); + result->setAllEdgeValue(false); + DFS d(graph, result, tgt, eWeights, edgesOrientation, pathLength); + retVal = d.searchPaths(src); + } } return retVal; } diff --git a/plugins/interactor/PathFinder/PathFinding/PathAlgorithm.h b/plugins/interactor/PathFinder/PathFinding/PathAlgorithm.h index e87f2226fa..4593e6915a 100644 --- a/plugins/interactor/PathFinder/PathFinding/PathAlgorithm.h +++ b/plugins/interactor/PathFinder/PathFinding/PathAlgorithm.h @@ -36,10 +36,10 @@ class Graph; class PathAlgorithm { public: /** - * By default, Tulip works on oriented edges. This behavior can be overloaded by forcing the edges - * to be Oriented, non oriented or reversed. + * By default, Tulip works on directed edges. This behavior can be overloaded by forcing the edges + * to be directed, undirected or reversed. */ - enum EdgeOrientation { Oriented, NonOriented, Reversed }; + enum EdgeOrientation { Directed, Undirected, Reversed }; /** * A path algorithm can look for only one (shortest) path, all the shortest paths or all the @@ -61,7 +61,6 @@ class PathAlgorithm { * * @see PathType * @see EdgeOrientation - * @see Dikjstra * @see DFS */ static bool computePath(tlp::Graph *graph, PathType pathType, EdgeOrientation edgesOrientation, diff --git a/plugins/interactor/PathFinder/designer/PathFinderConfiguration.ui b/plugins/interactor/PathFinder/designer/PathFinderConfiguration.ui index 95c401a76a..1209a504c4 100644 --- a/plugins/interactor/PathFinder/designer/PathFinderConfiguration.ui +++ b/plugins/interactor/PathFinder/designer/PathFinderConfiguration.ui @@ -168,6 +168,9 @@ p, li { white-space: pre-wrap; } </property> <item> <widget class="QCheckBox" name="toleranceCheck"> + <property name="enabled"> + <bool>false</bool> + </property> <property name="text"> <string>Max path length:</string> </property> @@ -175,6 +178,9 @@ p, li { white-space: pre-wrap; } </item> <item> <widget class="QSpinBox" name="toleranceSpin"> + <property name="enabled"> + <bool>false</bool> + </property> <property name="minimum"> <number>100</number> </property> @@ -187,9 +193,12 @@ p, li { white-space: pre-wrap; } </widget> </item> <item> - <widget class="QLabel" name="label_6"> + <widget class="QLabel" name="tolerancePercentLabel"> + <property name="enabled"> + <bool>false</bool> + </property> <property name="text"> - <string>%</string> + <string>% of the shortest path length</string> </property> </widget> </item> diff --git a/tests/gui/interactor_path_finder.tlp b/tests/gui/interactor_path_finder.tlp index 3357722a49..36f24513c8 100644 --- a/tests/gui/interactor_path_finder.tlp +++ b/tests/gui/interactor_path_finder.tlp @@ -1,5 +1,5 @@ (tlp "2.3" -(date "05-18-2018") +(date "11-30-2018") (comments "This file was generated by Tulip.") (nb_nodes 200) ;(nodes <node_id> <node_id> ...) @@ -895,22 +895,22 @@ ) (property 0 color "viewColor" (default "(255,0,0,255)" "(0,0,0,255)") +(node 4 "(85,170,255,255)") (node 11 "(85,170,255,255)") +(node 16 "(85,170,255,255)") +(node 24 "(85,170,255,255)") +(node 26 "(85,170,255,255)") +(node 29 "(85,170,255,255)") +(node 33 "(85,170,255,255)") (node 45 "(85,170,255,255)") -(node 56 "(85,170,255,255)") -(node 77 "(85,170,255,255)") +(node 51 "(85,170,255,255)") (node 80 "(85,170,255,255)") -(node 116 "(85,170,255,255)") -(node 119 "(85,170,255,255)") -(node 125 "(85,170,255,255)") +(node 89 "(85,170,255,255)") +(node 114 "(85,170,255,255)") (node 131 "(85,170,255,255)") (node 133 "(85,170,255,255)") -(node 146 "(85,170,255,255)") +(node 136 "(85,170,255,255)") (node 161 "(85,170,255,255)") -(node 182 "(85,170,255,255)") -(node 193 "(85,170,255,255)") -(node 195 "(85,170,255,255)") -(node 198 "(85,170,255,255)") ) (property 0 string "viewFont" (default "TulipBitmapDir/font.ttf" "TulipBitmapDir/font.ttf")