Skip to content

Commit cdf15ff

Browse files
committed
Method to condition adjacency matrix.
1 parent cdc35c8 commit cdf15ff

File tree

2 files changed

+79
-13
lines changed

2 files changed

+79
-13
lines changed

polygon_coverage_solvers/include/polygon_coverage_solvers/graph_base.h

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,6 @@ class GraphBase {
108108
// transforming cost into milli int.
109109
std::vector<std::vector<int>> getAdjacencyMatrix() const;
110110

111-
// Preserving three decimal digits.
112-
inline int doubleToMilliInt(double in) const {
113-
return static_cast<int>(std::round(in * kToMilli));
114-
}
115-
inline double milliIntToDouble(int in) const {
116-
return static_cast<double>(in) * kFromMilli;
117-
}
118-
119111
protected:
120112
// Called from addNode. Creates all edges to the node at the back of the
121113
// graph.
@@ -138,6 +130,27 @@ class GraphBase {
138130
size_t goal_idx_;
139131
bool is_created_;
140132
};
133+
134+
// Recursive function to search for largest x between lower and upper bound.
135+
inline double searchLargestFeasibleScale(const double low, const double high,
136+
const double condition,
137+
const double eps = 0.1) {
138+
// Early abort.
139+
if (high < low) return low;
140+
// Terminate recursion.
141+
if ((high - low) < eps) return low;
142+
143+
const double mid = (high + low) / 2.0;
144+
145+
if (mid < condition) {
146+
// mid satisfies condition. Find even higher scale.
147+
return searchLargestFeasibleScale(mid, high, condition, eps);
148+
} else {
149+
// mid does not satisfy condition. Find smaller scale that does.
150+
return searchLargestFeasibleScale(low, mid, condition, eps);
151+
}
152+
}
153+
141154
} // namespace polygon_coverage_planning
142155

143156
#include "polygon_coverage_solvers/impl/graph_base_impl.h"

polygon_coverage_solvers/include/polygon_coverage_solvers/impl/graph_base_impl.h

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@
2020
#ifndef POLYGON_COVERAGE_SOLVERS_GRAPH_BASE_IMPL_H_
2121
#define POLYGON_COVERAGE_SOLVERS_GRAPH_BASE_IMPL_H_
2222

23-
#include <algorithm>
24-
#include <set>
25-
2623
#include <ros/assert.h>
2724
#include <ros/console.h>
2825

26+
#include <algorithm>
27+
#include <set>
28+
2929
namespace polygon_coverage_planning {
3030

3131
template <class NodeProperty, class EdgeProperty>
@@ -324,15 +324,68 @@ Solution GraphBase<NodeProperty, EdgeProperty>::reconstructSolution(
324324
template <class NodeProperty, class EdgeProperty>
325325
std::vector<std::vector<int>>
326326
GraphBase<NodeProperty, EdgeProperty>::getAdjacencyMatrix() const {
327+
// First scale the matrix such that when converting the cost to integers two
328+
// costs can always be differentiated (except for if they are the same). Find
329+
// the minimum absolute difference between any two values to normalize the
330+
// adjacency matrix.
331+
// https://afteracademy.com/blog/minimum-absolute-difference-in-an-array
332+
std::vector<double> sorted_cost;
333+
sorted_cost.reserve(graph_.size() * graph_.size());
334+
for (size_t i = 0; i < graph_.size(); ++i) {
335+
for (size_t j = 0; j < graph_[i].size(); ++j) {
336+
const EdgeId edge(i, j);
337+
double cost;
338+
if (edgeExists(edge) && getEdgeCost(edge, &cost)) {
339+
sorted_cost.push_back(cost);
340+
}
341+
}
342+
}
343+
sort(sorted_cost.begin(), sorted_cost.end());
344+
auto min_diff = std::numeric_limits<double>::max();
345+
for (size_t i = 0; i < sorted_cost.size() - 2; i++) {
346+
auto diff = std::fabs(sorted_cost[i + 1] - sorted_cost[i]);
347+
if (diff > std::numeric_limits<double>::epsilon() && diff < min_diff)
348+
min_diff = diff;
349+
}
350+
351+
// Only scale with min_diff if this does not blow up the cost.
352+
// TODO(rikba): Find a tighter upper bound.
353+
double total_cost_upper_bound = sorted_cost.back() * graph_.size();
354+
double max_scale = 1.1 / min_diff;
355+
double min_scale =
356+
std::numeric_limits<int>::max() / (total_cost_upper_bound + 1.0);
357+
double scale = 1.0;
358+
if (max_scale * total_cost_upper_bound < std::numeric_limits<int>::max()) {
359+
ROS_DEBUG("Optimal scale applied.");
360+
scale = max_scale;
361+
} else if (min_scale * total_cost_upper_bound + 1 >
362+
std::numeric_limits<int>::max()) {
363+
// Recursive search for largest scale between min_scale and max_scale.
364+
ROS_WARN(
365+
"The adjacency matrix is ill conditioned. Adjacency matrix "
366+
"coefficients are reduced to guarantee upper cost bound. Consider "
367+
"removing small details in the polygon to have even decomposition "
368+
"size.");
369+
ROS_WARN("Recursively searching for best scale.");
370+
scale = searchLargestFeasibleScale(
371+
min_scale, max_scale,
372+
std::numeric_limits<int>::max() / total_cost_upper_bound + 1);
373+
374+
ROS_WARN_COND(min_diff < 1.0, "Loss of optimality.");
375+
}
376+
377+
ROS_DEBUG("The minimum cost difference is: %.9f", min_diff);
378+
ROS_INFO("The adjacency matrix scale is: %.9f", scale);
379+
380+
// Create scale adjacency matrix.
327381
std::vector<std::vector<int>> m(graph_.size(),
328382
std::vector<int>(graph_.size()));
329-
330383
for (size_t i = 0; i < m.size(); ++i) {
331384
for (size_t j = 0; j < m[i].size(); ++j) {
332385
const EdgeId edge(i, j);
333386
double cost;
334387
if (edgeExists(edge) && getEdgeCost(edge, &cost)) {
335-
m[i][j] = doubleToMilliInt(cost);
388+
m[i][j] = static_cast<int>(cost * scale);
336389
} else {
337390
m[i][j] = std::numeric_limits<int>::max();
338391
}

0 commit comments

Comments
 (0)