summaryrefslogtreecommitdiff
path: root/geom_bottleneck/include/bottleneck_detail.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'geom_bottleneck/include/bottleneck_detail.hpp')
-rw-r--r--geom_bottleneck/include/bottleneck_detail.hpp905
1 files changed, 496 insertions, 409 deletions
diff --git a/geom_bottleneck/include/bottleneck_detail.hpp b/geom_bottleneck/include/bottleneck_detail.hpp
index 24cb725..ad6c882 100644
--- a/geom_bottleneck/include/bottleneck_detail.hpp
+++ b/geom_bottleneck/include/bottleneck_detail.hpp
@@ -44,464 +44,551 @@ derivative works thereof, in binary and source code form.
#include "bottleneck_detail.h"
namespace hera {
-namespace bt {
-
-template<class Real>
-void binarySearch(const Real epsilon,
- std::pair<Real, Real>& result,
- BoundMatchOracle<Real>& oracle,
- const Real infinityCost,
- bool isResultInitializedCorrectly,
- const Real distProbeInit)
-{
- // aliases for result components
- Real& distMin = result.first;
- Real& distMax = result.second;
-
- distMin = std::max(distMin, infinityCost);
- distMax = std::max(distMax, infinityCost);
-
- Real distProbe;
-
- if (not isResultInitializedCorrectly) {
- distProbe = distProbeInit;
- if (oracle.isMatchLess(distProbe)) {
- // distProbe is an upper bound,
- // find lower bound with binary search
- do {
- distMax = distProbe;
- distProbe /= 2.0;
- } while (oracle.isMatchLess(distProbe));
- distMin = distProbe;
- } else {
- // distProbe is a lower bound,
- // find upper bound with exponential search
- do {
- distMin = distProbe;
- distProbe *= 2.0;
- } while (!oracle.isMatchLess(distProbe));
- distMax = distProbe;
- }
- }
- // bounds are correct , perform binary search
- distProbe = ( distMin + distMax ) / 2.0;
- while (( distMax - distMin ) / distMin >= epsilon ) {
-
- if (distMax < infinityCost) {
- distMin = infinityCost;
- distMax = infinityCost;
- break;
- }
+ namespace bt {
+
+ template<class Real>
+ void binarySearch(const Real epsilon,
+ std::pair<Real, Real>& result,
+ BoundMatchOracle <Real>& oracle,
+ const Real infinityCost,
+ bool isResultInitializedCorrectly,
+ const Real distProbeInit)
+ {
+ // aliases for result components
+ Real& distMin = result.first;
+ Real& distMax = result.second;
+
+ distMin = std::max(distMin, infinityCost);
+ distMax = std::max(distMax, infinityCost);
+
+ Real distProbe;
+
+ if (not isResultInitializedCorrectly) {
+ distProbe = distProbeInit;
+ if (oracle.isMatchLess(distProbe)) {
+ // distProbe is an upper bound,
+ // find lower bound with binary search
+ do {
+ distMax = distProbe;
+ distProbe /= 2.0;
+ } while (oracle.isMatchLess(distProbe));
+ distMin = distProbe;
+ } else {
+ // distProbe is a lower bound,
+ // find upper bound with exponential search
+ do {
+ distMin = distProbe;
+ distProbe *= 2.0;
+ } while (!oracle.isMatchLess(distProbe));
+ distMax = distProbe;
+ }
+ }
+ // bounds are correct , perform binary search
+ distProbe = (distMin + distMax) / 2.0;
+ while ((distMax - distMin) / distMin >= epsilon) {
+
+ if (distMax < infinityCost) {
+ distMin = infinityCost;
+ distMax = infinityCost;
+ break;
+ }
+
+ if (oracle.isMatchLess(distProbe)) {
+ distMax = distProbe;
+ } else {
+ distMin = distProbe;
+ }
- if (oracle.isMatchLess(distProbe)) {
- distMax = distProbe;
- } else {
- distMin = distProbe;
+ distProbe = (distMin + distMax) / 2.0;
+ }
+
+ distMin = std::max(distMin, infinityCost);
+ distMax = std::max(distMax, infinityCost);
}
- distProbe = ( distMin + distMax ) / 2.0;
- }
-
- distMin = std::max(distMin, infinityCost);
- distMax = std::max(distMax, infinityCost);
-}
-
-template<class Real>
-inline Real getOneDimensionalCost(std::vector<Real> &set_A, std::vector<Real> &set_B)
-{
- if (set_A.size() != set_B.size()) {
- return std::numeric_limits<Real>::infinity();
- }
-
- if (set_A.empty()) {
- return Real(0.0);
- }
-
- std::sort(set_A.begin(), set_A.end());
- std::sort(set_B.begin(), set_B.end());
-
- Real result = 0.0;
- for(size_t i = 0; i < set_A.size(); ++i) {
- result = std::max(result, (std::fabs(set_A[i] - set_B[i])));
- }
-
- return result;
-}
-
-template<class Real>
-inline Real getInfinityCost(const DiagramPointSet <Real> &A, const DiagramPointSet <Real> &B)
-{
- std::vector<Real> x_plus_A, x_minus_A, y_plus_A, y_minus_A;
- std::vector<Real> x_plus_B, x_minus_B, y_plus_B, y_minus_B;
-
- for(auto iter_A = A.cbegin(); iter_A != A.cend(); ++iter_A) {
- Real x = iter_A->getRealX();
- Real y = iter_A->getRealY();
- if ( x == std::numeric_limits<Real>::infinity()) {
- y_plus_A.push_back(y);
- } else if (x == -std::numeric_limits<Real>::infinity()) {
- y_minus_A.push_back(y);
- } else if (y == std::numeric_limits<Real>::infinity()) {
- x_plus_A.push_back(x);
- } else if (y == -std::numeric_limits<Real>::infinity()) {
- x_minus_A.push_back(x);
+ template<class Real>
+ inline Real getOneDimensionalCost(std::vector<Real>& set_A, std::vector<Real>& set_B)
+ {
+ if (set_A.size() != set_B.size()) {
+ return std::numeric_limits<Real>::infinity();
+ }
+
+ if (set_A.empty()) {
+ return Real(0.0);
+ }
+
+ std::sort(set_A.begin(), set_A.end());
+ std::sort(set_B.begin(), set_B.end());
+
+ Real result = 0.0;
+ for (size_t i = 0; i < set_A.size(); ++i) {
+ result = std::max(result, (std::fabs(set_A[i] - set_B[i])));
+ }
+
+ return result;
}
- }
-
- for(auto iter_B = B.cbegin(); iter_B != B.cend(); ++iter_B) {
- Real x = iter_B->getRealX();
- Real y = iter_B->getRealY();
- if (x == std::numeric_limits<Real>::infinity()) {
- y_plus_B.push_back(y);
- } else if (x == -std::numeric_limits<Real>::infinity()) {
- y_minus_B.push_back(y);
- } else if (y == std::numeric_limits<Real>::infinity()) {
- x_plus_B.push_back(x);
- } else if (y == -std::numeric_limits<Real>::infinity()) {
- x_minus_B.push_back(x);
+
+
+ template<class Real>
+ using CostEdgePair = std::pair<Real, typename hera::bt::MatchingEdge<Real>>;
+
+ template<class Real>
+ using CoordPointPair = std::pair<Real, typename hera::bt::DiagramPoint<Real>>;
+
+ template<class Real>
+ using CoordPointVector = std::vector<typename hera::bt::CoordPointPair<Real>>;
+
+ template<class Real>
+ struct CoordPointPairComparator
+ {
+ bool operator()(const CoordPointPair<Real>& a, const CoordPointPair<Real>& b) const
+ {
+ return a.first < b.first or (a.first == b.first and a.second.id < b.second.id);
+ };
+ };
+
+ template<class Real>
+ inline typename hera::bt::CostEdgePair<Real> getOneDimensionalCost(typename hera::bt::CoordPointVector<Real>& set_A, typename hera::bt::CoordPointVector<Real>& set_B)
+ {
+ using MatchingEdgeR = hera::bt::MatchingEdge<Real>;
+ if (set_A.size() != set_B.size()) {
+ return std::make_pair(std::numeric_limits<Real>::infinity(), MatchingEdgeR());
+ }
+
+ if (set_A.empty()) {
+ return std::make_pair(Real(0.0), MatchingEdgeR());
+ }
+
+ std::sort(set_A.begin(), set_A.end(), CoordPointPairComparator<Real>());
+ std::sort(set_B.begin(), set_B.end(), CoordPointPairComparator<Real>());
+
+ CostEdgePair<Real> result(-1.0, MatchingEdgeR());
+
+ for (size_t i = 0; i < set_A.size(); ++i) {
+ Real curr_cost = std::fabs(set_A[i].first - set_B[i].first);
+ if (curr_cost > result.first) {
+ result.first = curr_cost;
+ result.second = MatchingEdgeR(set_A[i].second, set_B[i].second);
+ }
+ }
+ return result;
}
- }
- Real infinity_cost = getOneDimensionalCost(x_plus_A, x_plus_B);
- infinity_cost = std::max(infinity_cost, getOneDimensionalCost(x_minus_A, x_minus_B));
- infinity_cost = std::max(infinity_cost, getOneDimensionalCost(y_plus_A, y_plus_B));
- infinity_cost = std::max(infinity_cost, getOneDimensionalCost(y_minus_A, y_minus_B));
- return infinity_cost;
-}
+ template<class Real>
+ inline Real getInfinityCost(const DiagramPointSet <Real>& A, const DiagramPointSet <Real>& B,
+ bool compute_longest_edge = false)
+ {
+ std::vector<Real> x_plus_A, x_minus_A, y_plus_A, y_minus_A;
+ std::vector<Real> x_plus_B, x_minus_B, y_plus_B, y_minus_B;
+
+ for (auto iter_A = A.cbegin(); iter_A != A.cend(); ++iter_A) {
+ Real x = iter_A->getRealX();
+ Real y = iter_A->getRealY();
+ if (x == std::numeric_limits<Real>::infinity()) {
+ y_plus_A.push_back(y);
+ } else if (x == -std::numeric_limits<Real>::infinity()) {
+ y_minus_A.push_back(y);
+ } else if (y == std::numeric_limits<Real>::infinity()) {
+ x_plus_A.push_back(x);
+ } else if (y == -std::numeric_limits<Real>::infinity()) {
+ x_minus_A.push_back(x);
+ }
+ }
+
+ for (auto iter_B = B.cbegin(); iter_B != B.cend(); ++iter_B) {
+ Real x = iter_B->getRealX();
+ Real y = iter_B->getRealY();
+ if (x == std::numeric_limits<Real>::infinity()) {
+ y_plus_B.push_back(y);
+ } else if (x == -std::numeric_limits<Real>::infinity()) {
+ y_minus_B.push_back(y);
+ } else if (y == std::numeric_limits<Real>::infinity()) {
+ x_plus_B.push_back(x);
+ } else if (y == -std::numeric_limits<Real>::infinity()) {
+ x_minus_B.push_back(x);
+ }
+ }
+
+ Real infinity_cost = getOneDimensionalCost(x_plus_A, x_plus_B);
+ infinity_cost = std::max(infinity_cost, getOneDimensionalCost(x_minus_A, x_minus_B));
+ infinity_cost = std::max(infinity_cost, getOneDimensionalCost(y_plus_A, y_plus_B));
+ infinity_cost = std::max(infinity_cost, getOneDimensionalCost(y_minus_A, y_minus_B));
+
+ return infinity_cost;
+ }
// return the interval (distMin, distMax) such that:
// a) actual bottleneck distance between A and B is contained in the interval
// b) if the interval is not (0,0), then (distMax - distMin) / distMin < epsilon
-template<class Real>
-inline std::pair<Real, Real> bottleneckDistApproxInterval(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B, const Real epsilon)
-{
- // empty diagrams are not considered as error
- if (A.empty() and B.empty())
- return std::make_pair(0.0, 0.0);
-
- Real infinity_cost = getInfinityCost(A, B);
- if (infinity_cost == std::numeric_limits<Real>::infinity())
- return std::make_pair(infinity_cost, infinity_cost);
-
- // link diagrams A and B by adding projections
- addProjections(A, B);
-
- // TODO: think about that!
- // we need one threshold for checking if the distance is 0,
- // another one for the oracle!
- constexpr Real epsThreshold { 1.0e-10 };
- std::pair<Real, Real> result { 0.0, 0.0 };
- bool useRangeSearch { true };
- // construct an oracle
- BoundMatchOracle<Real> oracle(A, B, epsThreshold, useRangeSearch);
- // check for distance = 0
- if (oracle.isMatchLess(2*epsThreshold)) {
- return result;
- }
- // get a 3-approximation of maximal distance between A and B
- // as a starting value for probe distance
- Real distProbe { getFurthestDistance3Approx<Real, DiagramPointSet<Real>>(A, B) };
- binarySearch(epsilon, result, oracle, infinity_cost, false, distProbe);
- return result;
-}
-
-template<class Real>
-void sampleDiagramForHeur(const DiagramPointSet<Real>& dgmIn, DiagramPointSet<Real>& dgmOut)
-{
- struct pair_hash {
- std::size_t operator()(const std::pair<Real, Real> p) const
+ template<class Real>
+ inline std::pair<Real, Real>
+ bottleneckDistApproxInterval(DiagramPointSet <Real>& A, DiagramPointSet <Real>& B, const Real epsilon,
+ MatchingEdge <Real>& edge, bool compute_longest_edge)
{
- return std::hash<Real>()(p.first) ^ std::hash<Real>()(p.second);
- }
- };
- std::unordered_map<std::pair<Real, Real>, int, pair_hash> m;
- for(auto ptIter = dgmIn.cbegin(); ptIter != dgmIn.cend(); ++ptIter) {
- if (ptIter->isNormal() and not ptIter->isInfinity()) {
- m[std::make_pair(ptIter->getRealX(), ptIter->getRealY())]++;
- }
- }
- if (m.size() < 2) {
- dgmOut = dgmIn;
- return;
- }
- std::vector<int> v;
- for(const auto& ptQtyPair : m) {
- v.push_back(ptQtyPair.second);
- }
- std::sort(v.begin(), v.end());
- int maxLeap = v[1] - v[0];
- int cutVal = v[0];
- for(int i = 1; i < static_cast<int>(v.size())- 1; ++i) {
- int currLeap = v[i+1] - v[i];
- if (currLeap > maxLeap) {
- maxLeap = currLeap;
- cutVal = v[i];
+ // empty diagrams are not considered as error
+ if (A.empty() and B.empty()) {
+ return std::make_pair(0.0, 0.0);
+ }
+
+ if (compute_longest_edge) {
+ auto inf_cost_edge = getInfinityCost(A, B, true);
+ } else {
+ }
+
+ Real infinity_cost = getInfinityCost(A, B);
+ if (infinity_cost == std::numeric_limits<Real>::infinity()) {
+ return std::make_pair(infinity_cost, infinity_cost);
+ }
+
+ // link diagrams A and B by adding projections
+ addProjections(A, B);
+
+ // TODO: think about that!
+ // we need one threshold for checking if the distance is 0,
+ // another one for the oracle!
+ constexpr Real epsThreshold { 1.0e-10 };
+ std::pair<Real, Real> result { 0.0, 0.0 };
+ bool useRangeSearch { true };
+ // construct an oracle
+ BoundMatchOracle<Real> oracle(A, B, epsThreshold, useRangeSearch);
+ // check for distance = 0
+ if (oracle.isMatchLess(2 * epsThreshold)) {
+ result.first = std::max(result.first, infinity_cost);
+ result.second = std::max(result.first, infinity_cost);
+ return result;
+ }
+ // get a 3-approximation of maximal distance between A and B
+ // as a starting value for probe distance
+ Real distProbe { getFurthestDistance3Approx<Real, DiagramPointSet<Real>>(A, B) };
+ binarySearch(epsilon, result, oracle, infinity_cost, false, distProbe);
+ if (compute_longest_edge) {
+ edge = oracle.get_longest_edge();
+ }
+ return result;
}
- }
- std::vector<std::pair<Real, Real>> vv;
- // keep points whose multiplicites are at most cutVal
- // quick-and-dirty: fill in vv with copies of each point
- // to construct DiagramPointSet from it later
- for(const auto& ptQty : m) {
- if (ptQty.second < cutVal) {
- for(int i = 0; i < ptQty.second; ++i) {
- vv.push_back(std::make_pair(ptQty.first.first, ptQty.first.second));
+
+ template<class Real>
+ void sampleDiagramForHeur(const DiagramPointSet <Real>& dgmIn, DiagramPointSet <Real>& dgmOut)
+ {
+ struct pair_hash
+ {
+ std::size_t operator()(const std::pair<Real, Real> p) const
+ {
+ return std::hash<Real>()(p.first) ^ std::hash<Real>()(p.second);
+ }
+ };
+ std::unordered_map<std::pair<Real, Real>, int, pair_hash> m;
+ for (auto ptIter = dgmIn.cbegin(); ptIter != dgmIn.cend(); ++ptIter) {
+ if (ptIter->isNormal() and not ptIter->isInfinity()) {
+ m[std::make_pair(ptIter->getRealX(), ptIter->getRealY())]++;
+ }
+ }
+ if (m.size() < 2) {
+ dgmOut = dgmIn;
+ return;
+ }
+ std::vector<int> v;
+ for (const auto& ptQtyPair : m) {
+ v.push_back(ptQtyPair.second);
+ }
+ std::sort(v.begin(), v.end());
+ int maxLeap = v[1] - v[0];
+ int cutVal = v[0];
+ for (int i = 1; i < static_cast<int>(v.size()) - 1; ++i) {
+ int currLeap = v[i + 1] - v[i];
+ if (currLeap > maxLeap) {
+ maxLeap = currLeap;
+ cutVal = v[i];
+ }
+ }
+ std::vector<std::pair<Real, Real>> vv;
+ // keep points whose multiplicites are at most cutVal
+ // quick-and-dirty: fill in vv with copies of each point
+ // to construct DiagramPointSet from it later
+ for (const auto& ptQty : m) {
+ if (ptQty.second < cutVal) {
+ for (int i = 0; i < ptQty.second; ++i) {
+ vv.push_back(std::make_pair(ptQty.first.first, ptQty.first.second));
+ }
+ }
}
+ dgmOut.clear();
+ dgmOut = DiagramPointSet<Real>(vv.begin(), vv.end());
}
- }
- dgmOut.clear();
- dgmOut = DiagramPointSet<Real>(vv.begin(), vv.end());
-}
// return the interval (distMin, distMax) such that:
// a) actual bottleneck distance between A and B is contained in the interval
// b) if the interval is not (0,0), then (distMax - distMin) / distMin < epsilon
-template<class Real>
-std::pair<Real, Real> bottleneckDistApproxIntervalWithInitial(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B,
- const Real epsilon,
- const std::pair<Real, Real> initialGuess,
- const Real infinity_cost)
-{
- // empty diagrams are not considered as error
- if (A.empty() and B.empty())
- return std::make_pair(0.0, 0.0);
-
- // link diagrams A and B by adding projections
- addProjections(A, B);
-
- constexpr Real epsThreshold { 1.0e-10 };
- std::pair<Real, Real> result { 0.0, 0.0 };
- bool useRangeSearch { true };
- // construct an oracle
- BoundMatchOracle<Real> oracle(A, B, epsThreshold, useRangeSearch);
-
- Real& distMin {result.first};
- Real& distMax {result.second};
-
- // initialize search interval from initialGuess
- distMin = initialGuess.first;
- distMax = initialGuess.second;
-
- assert(distMin <= distMax);
-
- // make sure that distMin is a lower bound
- while(oracle.isMatchLess(distMin)) {
- // distMin is in fact an upper bound, so assign it to distMax
- distMax = distMin;
- // and decrease distMin by 5 %
- distMin = 0.95 * distMin;
- }
-
- // make sure that distMax is an upper bound
- while(not oracle.isMatchLess(distMax)) {
- // distMax is in fact a lower bound, so assign it to distMin
- distMin = distMax;
- // and increase distMax by 5 %
- distMax = 1.05 * distMax;
- }
-
- // bounds are found, perform binary search
- Real distProbe = ( distMin + distMax ) / 2.0;
- binarySearch(epsilon, result, oracle, infinity_cost, true, distProbe);
- return result;
-}
+ template<class Real>
+ std::pair<Real, Real>
+ bottleneckDistApproxIntervalWithInitial(DiagramPointSet <Real>& A, DiagramPointSet <Real>& B,
+ const Real epsilon,
+ const std::pair<Real, Real> initialGuess,
+ const Real infinity_cost,
+ MatchingEdge <Real>& longest_edge,
+ bool compute_longest_edge = false)
+ {
+ // empty diagrams are not considered as error
+ if (A.empty() and B.empty()) {
+ return std::make_pair(0.0, 0.0);
+ }
+
+ // link diagrams A and B by adding projections
+ addProjections(A, B);
+
+ constexpr Real epsThreshold { 1.0e-10 };
+ std::pair<Real, Real> result { 0.0, 0.0 };
+ bool useRangeSearch { true };
+ // construct an oracle
+ BoundMatchOracle<Real> oracle(A, B, epsThreshold, useRangeSearch);
+
+ Real& distMin { result.first };
+ Real& distMax { result.second };
+
+ // initialize search interval from initialGuess
+ distMin = initialGuess.first;
+ distMax = initialGuess.second;
+
+ assert(distMin <= distMax);
+
+ // make sure that distMin is a lower bound
+ while (oracle.isMatchLess(distMin)) {
+ // distMin is in fact an upper bound, so assign it to distMax
+ distMax = distMin;
+ // and decrease distMin by 5 %
+ distMin = 0.95 * distMin;
+ }
+
+ // make sure that distMax is an upper bound
+ while (not oracle.isMatchLess(distMax)) {
+ // distMax is in fact a lower bound, so assign it to distMin
+ distMin = distMax;
+ // and increase distMax by 5 %
+ distMax = 1.05 * distMax;
+ }
+
+ // bounds are found, perform binary search
+ Real distProbe = (distMin + distMax) / 2.0;
+ binarySearch(epsilon, result, oracle, infinity_cost, true, distProbe);
+ if (compute_longest_edge) {
+ longest_edge = oracle.get_longest_edge();
+ }
+ return result;
+ }
// return the interval (distMin, distMax) such that:
// a) actual bottleneck distance between A and B is contained in the interval
// b) if the interval is not (0,0), then (distMax - distMin) / distMin < epsilon
// use heuristic: initial estimate on sampled diagrams
-template<class Real>
-std::pair<Real, Real> bottleneckDistApproxIntervalHeur(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B, const Real epsilon)
-{
- // empty diagrams are not considered as error
- if (A.empty() and B.empty())
- return std::make_pair(0.0, 0.0);
-
- Real infinity_cost = getInfinityCost(A, B);
- if (infinity_cost == std::numeric_limits<Real>::infinity())
- return std::make_pair(infinity_cost, infinity_cost);
+ template<class Real>
+ std::pair<Real, Real>
+ bottleneckDistApproxIntervalHeur(DiagramPointSet <Real>& A, DiagramPointSet <Real>& B, const Real epsilon,
+ MatchingEdge <Real>& longest_edge)
+ {
+ // empty diagrams are not considered as error
+ if (A.empty() and B.empty()) {
+ return std::make_pair(0.0, 0.0);
+ }
- DiagramPointSet<Real> sampledA, sampledB;
- sampleDiagramForHeur(A, sampledA);
- sampleDiagramForHeur(B, sampledB);
+ Real infinity_cost = getInfinityCost(A, B);
+ if (infinity_cost == std::numeric_limits<Real>::infinity()) {
+ return std::make_pair(infinity_cost, infinity_cost);
+ }
- std::pair<Real, Real> initGuess = bottleneckDistApproxInterval(sampledA, sampledB, epsilon);
+ DiagramPointSet<Real> sampledA, sampledB;
+ sampleDiagramForHeur(A, sampledA);
+ sampleDiagramForHeur(B, sampledB);
- initGuess.first = std::max(initGuess.first, infinity_cost);
- initGuess.second = std::max(initGuess.second, infinity_cost);
+ std::pair<Real, Real> initGuess = bottleneckDistApproxInterval(sampledA, sampledB, epsilon);
- return bottleneckDistApproxIntervalWithInitial<Real>(A, B, epsilon, initGuess, infinity_cost);
-}
+ initGuess.first = std::max(initGuess.first, infinity_cost);
+ initGuess.second = std::max(initGuess.second, infinity_cost);
+ return bottleneckDistApproxIntervalWithInitial<Real>(A, B, epsilon, initGuess, infinity_cost, longest_edge);
+ }
// get approximate distance,
// see bottleneckDistApproxInterval
-template<class Real>
-Real bottleneckDistApprox(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B, const Real epsilon)
-{
- auto interval = bottleneckDistApproxInterval<Real>(A, B, epsilon);
- return interval.second;
-}
-
-
-template<class Real>
-Real bottleneckDistExactFromSortedPwDist(DiagramPointSet<Real>&A, DiagramPointSet<Real>& B, std::vector<Real>& pairwiseDist, const int decPrecision)
-{
- // trivial case: we have only one candidate
- if (pairwiseDist.size() == 1)
- return pairwiseDist[0];
-
- bool useRangeSearch = true;
- Real distEpsilon = std::numeric_limits<Real>::max();
- Real diffThreshold = 0.1;
- for(int k = 0; k < decPrecision; ++k) {
- diffThreshold /= 10.0;
- }
- for(size_t k = 0; k < pairwiseDist.size() - 2; ++k) {
- auto diff = pairwiseDist[k+1]- pairwiseDist[k];
- if ( diff > diffThreshold and diff < distEpsilon ) {
- distEpsilon = diff;
- }
- }
- distEpsilon /= 3.0;
-
- BoundMatchOracle<Real> oracle(A, B, distEpsilon, useRangeSearch);
- // binary search
- size_t iterNum {0};
- size_t idxMin {0}, idxMax {pairwiseDist.size() - 1};
- size_t idxMid;
- while(idxMax > idxMin) {
- idxMid = static_cast<size_t>(floor(idxMin + idxMax) / 2.0);
- iterNum++;
- // not A[imid] < dist <=> A[imid] >= dist <=> A[imid[ >= dist + eps
- if (oracle.isMatchLess(pairwiseDist[idxMid] + distEpsilon / 2.0)) {
- idxMax = idxMid;
- } else {
- idxMin = idxMid + 1;
- }
- }
- idxMid = static_cast<size_t>(floor(idxMin + idxMax) / 2.0);
- return pairwiseDist[idxMid];
-}
-
-
-template<class Real>
-Real bottleneckDistExact(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B)
-{
- return bottleneckDistExact(A, B, 14);
-}
-
-template<class Real>
-Real bottleneckDistExact(DiagramPointSet<Real>& A, DiagramPointSet<Real>& B, const int decPrecision)
-{
- using DgmPoint = DiagramPoint<Real>;
-
- constexpr Real epsilon = 0.001;
- auto interval = bottleneckDistApproxInterval(A, B, epsilon);
- if (interval.first == interval.second)
- return interval.first;
- const Real delta = 0.50001 * (interval.second - interval.first);
- const Real approxDist = 0.5 * ( interval.first + interval.second);
- const Real minDist = interval.first;
- const Real maxDist = interval.second;
- if ( delta == 0 ) {
- return interval.first;
- }
- // copy points from A to a vector
- // todo: get rid of this?
- std::vector<DgmPoint> pointsA;
- pointsA.reserve(A.size());
- for(const auto& ptA : A) {
- pointsA.push_back(ptA);
- }
-
- // in this vector we store the distances between the points
- // that are candidates to realize
- std::vector<Real> pairwiseDist;
- {
- // vector to store centers of vertical stripes
- // two for each point in A and the id of the corresponding point
- std::vector<std::pair<Real, DgmPoint>> xCentersVec;
- xCentersVec.reserve(2 * pointsA.size());
- for(auto ptA : pointsA) {
- xCentersVec.push_back(std::make_pair(ptA.getRealX() - approxDist, ptA));
- xCentersVec.push_back(std::make_pair(ptA.getRealX() + approxDist, ptA));
+ template<class Real>
+ Real bottleneckDistApprox(DiagramPointSet <Real>& A, DiagramPointSet <Real>& B, const Real epsilon,
+ MatchingEdge <Real>& longest_edge, bool compute_longest_edge)
+ {
+ auto interval = bottleneckDistApproxInterval<Real>(A, B, epsilon, longest_edge);
+ return interval.second;
}
- // lambda to compare pairs <coordinate, id> w.r.t coordinate
- auto compLambda = [](std::pair<Real, DgmPoint> a, std::pair<Real, DgmPoint> b)
- { return a.first < b.first; };
-
- std::sort(xCentersVec.begin(), xCentersVec.end(), compLambda);
- // todo: sort points in B, reduce search range in lower and upper bounds
- for(auto ptB : B) {
- // iterator to the first stripe such that ptB lies to the left
- // from its right boundary (x_B <= x_j + \delta iff x_j >= x_B - \delta
- auto itStart = std::lower_bound(xCentersVec.begin(),
- xCentersVec.end(),
- std::make_pair(ptB.getRealX() - delta, ptB),
- compLambda);
-
- for(auto iterA = itStart; iterA < xCentersVec.end(); ++iterA) {
- if ( ptB.getRealX() < iterA->first - delta) {
- // from that moment x_B >= x_j - delta
- // is violated: x_B no longer lies to right from the left
- // boundary of current stripe
- break;
+
+
+ template<class Real>
+ Real bottleneckDistExactFromSortedPwDist(DiagramPointSet <Real>& A, DiagramPointSet <Real>& B,
+ const std::vector<Real>& pairwiseDist,
+ const int decPrecision, MatchingEdge <Real>& longest_edge,
+ bool compute_longest_edge = false)
+ {
+ // trivial case: we have only one candidate
+ if (pairwiseDist.size() == 1) {
+ return pairwiseDist[0];
+ }
+
+ bool useRangeSearch = true;
+ Real distEpsilon = std::numeric_limits<Real>::max();
+ Real diffThreshold = 0.1;
+ for (int k = 0; k < decPrecision; ++k) {
+ diffThreshold /= 10.0;
+ }
+ for (size_t k = 0; k < pairwiseDist.size() - 2; ++k) {
+ auto diff = pairwiseDist[k + 1] - pairwiseDist[k];
+ if (diff > diffThreshold and diff < distEpsilon) {
+ distEpsilon = diff;
}
- // we're here => ptB lies in vertical stripe,
- // check if distance fits into the interval we've found
- Real pwDist = distLInf(iterA->second, ptB);
- if (pwDist >= minDist and pwDist <= maxDist) {
- pairwiseDist.push_back(pwDist);
+ }
+ distEpsilon /= 3.0;
+
+ BoundMatchOracle<Real> oracle(A, B, distEpsilon, useRangeSearch);
+ // binary search
+ size_t iterNum { 0 };
+ size_t idxMin { 0 }, idxMax { pairwiseDist.size() - 1 };
+ size_t idxMid;
+ while (idxMax > idxMin) {
+ idxMid = static_cast<size_t>(floor(idxMin + idxMax) / 2.0);
+ iterNum++;
+ // not A[imid] < dist <=> A[imid] >= dist <=> A[imid[ >= dist + eps
+ if (oracle.isMatchLess(pairwiseDist[idxMid] + distEpsilon / 2.0)) {
+ idxMax = idxMid;
+ } else {
+ idxMin = idxMid + 1;
}
}
+ idxMid = static_cast<size_t>(floor(idxMin + idxMax) / 2.0);
+ if (compute_longest_edge) {
+ longest_edge = oracle.get_longest_edge();
+ }
+ return pairwiseDist[idxMid];
}
- }
-
- {
- // for y
- // vector to store centers of vertical stripes
- // two for each point in A and the id of the corresponding point
- std::vector<std::pair<Real, DgmPoint>> yCentersVec;
- yCentersVec.reserve(2 * pointsA.size());
- for(auto ptA : pointsA) {
- yCentersVec.push_back(std::make_pair(ptA.getRealY() - approxDist, ptA));
- yCentersVec.push_back(std::make_pair(ptA.getRealY() + approxDist, ptA));
+
+
+ template<class Real>
+ Real bottleneckDistExact(DiagramPointSet <Real>& A, DiagramPointSet <Real>& B, MatchingEdge <Real>& longest_edge,
+ bool compute_longest_edge)
+ {
+ return bottleneckDistExact(A, B, 14, longest_edge, compute_longest_edge);
}
- // lambda to compare pairs <coordinate, id> w.r.t coordinate
- auto compLambda = [](std::pair<Real, DgmPoint> a, std::pair<Real, DgmPoint> b)
- { return a.first < b.first; };
- std::sort(yCentersVec.begin(), yCentersVec.end(), compLambda);
+ template<class Real>
+ Real bottleneckDistExact(DiagramPointSet <Real>& A, DiagramPointSet <Real>& B, const int decPrecision,
+ MatchingEdge <Real>& longest_edge, bool compute_longest_edge)
+ {
+ using DgmPoint = DiagramPoint<Real>;
- // todo: sort points in B, reduce search range in lower and upper bounds
- for(auto ptB : B) {
- auto itStart = std::lower_bound(yCentersVec.begin(),
- yCentersVec.end(),
- std::make_pair(ptB.getRealY() - delta, ptB),
- compLambda);
+ constexpr Real epsilon = 0.001;
+ auto interval = bottleneckDistApproxInterval(A, B, epsilon, longest_edge);
+ if (interval.first == interval.second) {
+ return interval.first;
+ }
+ const Real delta = 0.50001 * (interval.second - interval.first);
+ const Real approxDist = 0.5 * (interval.first + interval.second);
+ const Real minDist = interval.first;
+ const Real maxDist = interval.second;
+ if (delta == 0) {
+ return interval.first;
+ }
+ // copy points from A to a vector
+ // todo: get rid of this?
+ std::vector<DgmPoint> pointsA;
+ pointsA.reserve(A.size());
+ for (const auto& ptA : A) {
+ pointsA.push_back(ptA);
+ }
+ // in this vector we store the distances between the points
+ // that are candidates to realize
+ std::vector<Real> pairwiseDist;
+ {
+ // vector to store centers of vertical stripes
+ // two for each point in A and the id of the corresponding point
+ std::vector<std::pair<Real, DgmPoint>> xCentersVec;
+ xCentersVec.reserve(2 * pointsA.size());
+ for (auto ptA : pointsA) {
+ xCentersVec.push_back(std::make_pair(ptA.getRealX() - approxDist, ptA));
+ xCentersVec.push_back(std::make_pair(ptA.getRealX() + approxDist, ptA));
+ }
+ // lambda to compare pairs <coordinate, id> w.r.t coordinate
+ auto compLambda = [](std::pair<Real, DgmPoint> a, std::pair<Real, DgmPoint> b) {
+ return a.first < b.first;
+ };
+
+ std::sort(xCentersVec.begin(), xCentersVec.end(), compLambda);
+ // todo: sort points in B, reduce search range in lower and upper bounds
+ for (auto ptB : B) {
+ // iterator to the first stripe such that ptB lies to the left
+ // from its right boundary (x_B <= x_j + \delta iff x_j >= x_B - \delta
+ auto itStart = std::lower_bound(xCentersVec.begin(),
+ xCentersVec.end(),
+ std::make_pair(ptB.getRealX() - delta, ptB),
+ compLambda);
+
+ for (auto iterA = itStart; iterA < xCentersVec.end(); ++iterA) {
+ if (ptB.getRealX() < iterA->first - delta) {
+ // from that moment x_B >= x_j - delta
+ // is violated: x_B no longer lies to right from the left
+ // boundary of current stripe
+ break;
+ }
+ // we're here => ptB lies in vertical stripe,
+ // check if distance fits into the interval we've found
+ Real pwDist = distLInf(iterA->second, ptB);
+ if (pwDist >= minDist and pwDist <= maxDist) {
+ pairwiseDist.push_back(pwDist);
+ }
+ }
+ }
+ }
- for(auto iterA = itStart; iterA < yCentersVec.end(); ++iterA) {
- if ( ptB.getRealY() < iterA->first - delta) {
- break;
+ {
+ // for y
+ // vector to store centers of vertical stripes
+ // two for each point in A and the id of the corresponding point
+ std::vector<std::pair<Real, DgmPoint>> yCentersVec;
+ yCentersVec.reserve(2 * pointsA.size());
+ for (auto ptA : pointsA) {
+ yCentersVec.push_back(std::make_pair(ptA.getRealY() - approxDist, ptA));
+ yCentersVec.push_back(std::make_pair(ptA.getRealY() + approxDist, ptA));
}
- Real pwDist = distLInf(iterA->second, ptB);
- if (pwDist >= minDist and pwDist <= maxDist) {
- pairwiseDist.push_back(pwDist);
+ // lambda to compare pairs <coordinate, id> w.r.t coordinate
+ auto compLambda = [](std::pair<Real, DgmPoint> a, std::pair<Real, DgmPoint> b) {
+ return a.first < b.first;
+ };
+
+ std::sort(yCentersVec.begin(), yCentersVec.end(), compLambda);
+
+ // todo: sort points in B, reduce search range in lower and upper bounds
+ for (auto ptB : B) {
+ auto itStart = std::lower_bound(yCentersVec.begin(),
+ yCentersVec.end(),
+ std::make_pair(ptB.getRealY() - delta, ptB),
+ compLambda);
+
+
+ for (auto iterA = itStart; iterA < yCentersVec.end(); ++iterA) {
+ if (ptB.getRealY() < iterA->first - delta) {
+ break;
+ }
+ Real pwDist = distLInf(iterA->second, ptB);
+ if (pwDist >= minDist and pwDist <= maxDist) {
+ pairwiseDist.push_back(pwDist);
+ }
+ }
}
}
- }
- }
- std::sort(pairwiseDist.begin(), pairwiseDist.end());
+ std::sort(pairwiseDist.begin(), pairwiseDist.end());
- return bottleneckDistExactFromSortedPwDist(A, B, pairwiseDist, decPrecision);
-}
+ return bottleneckDistExactFromSortedPwDist(A, B, pairwiseDist, decPrecision, longest_edge,
+ compute_longest_edge);
+ }
-} // end namespace bt
+ } // end namespace bt
} // end namespace hera
#endif // HERA_BOTTLENECK_HPP