andres::graph
lifting.hxx
1 #pragma once
2 #ifndef ANDRES_GRAPH_LIFTING_HXX
3 #define ANDRES_GRAPH_LIFTING_HXX
4 
5 #include <cassert>
6 #include <cstddef>
7 #include <cmath>
8 #include <stdexcept>
9 #include <iterator> // std::iterator_traits
10 #include <algorithm> // std::fill
11 #include <vector>
12 
13 #include "grid-graph.hxx"
14 #include "bfs.hxx"
15 
16 
17 namespace andres {
18 namespace graph {
19 
20 enum class LiftingMetric { PathLength, L2 };
21 
23 template<class INPUT_GRAPH, class OUTPUT_GRAPH>
24 inline void
26  const INPUT_GRAPH& inputGraph,
27  OUTPUT_GRAPH& outputGraph,
28  const std::size_t distanceUpperBound,
29  const std::size_t distanceLowerBound = 0
30 ) {
31  typedef std::size_t size_type;
32 
33  if(outputGraph.numberOfVertices() != 0)
34  throw std::runtime_error("output graph is not empty.");
35 
36  outputGraph.insertVertices(inputGraph.numberOfVertices());
37 
38  BreadthFirstSearchData<size_type> breadthFirstSearchData(inputGraph.numberOfVertices());
39  std::vector<std::size_t> visited;
40  std::vector<std::size_t> vertices;
41 
42  for (size_type v = 0; v < inputGraph.numberOfVertices(); ++v)
43  {
45  inputGraph,
46  v,
47  [&](size_type w, size_type depth, bool& proceed, bool& add)
48  {
49  proceed = true;
50  add = false;
51 
52  if (depth <= distanceUpperBound)
53  {
54  if (depth + 1 <= distanceUpperBound)
55  {
56  add = true;
57  visited.push_back(w);
58  }
59 
60  if (depth > distanceLowerBound)
61  vertices.push_back(w);
62  }
63  },
64  breadthFirstSearchData
65  );
66 
67  std::sort(vertices.begin(), vertices.end());
68 
69  for (auto w : vertices)
70  outputGraph.insertEdge(v, w);
71 
72  vertices.clear();
73 
74  breadthFirstSearchData.depth(v) = BreadthFirstSearchData<size_type>::NOT_VISITED;
75  for (auto w : visited)
76  breadthFirstSearchData.depth(w) = BreadthFirstSearchData<size_type>::NOT_VISITED;
77 
78  visited.clear();
79  }
80 }
81 
83 template<class INPUT_GRAPH_VISITOR, class OUTPUT_GRAPH>
84 inline void
86  const GridGraph<2, INPUT_GRAPH_VISITOR>& inputGraph,
87  OUTPUT_GRAPH& outputGraph,
88  const std::size_t distanceUpperBound,
89  const std::size_t distanceLowerBound = 0,
90  LiftingMetric metric = LiftingMetric::PathLength
91 ) {
92  typedef GridGraph<2, INPUT_GRAPH_VISITOR> INPUT_GRAPH;
93 
94  typedef std::size_t size_type;
95  typedef typename INPUT_GRAPH::VertexCoordinate VertexCoordinate;
96 
97  const size_type distanceUpperBoundSquared = distanceUpperBound * distanceUpperBound;
98  const size_type distanceLowerBoundSquared = distanceLowerBound * distanceLowerBound;
99 
100  if(outputGraph.numberOfVertices() != 0)
101  throw std::runtime_error("output graph is not empty.");
102 
103  outputGraph.insertVertices(inputGraph.numberOfVertices());
104 
105  VertexCoordinate cv;
106  for (size_type v = 0; v < inputGraph.numberOfVertices(); ++v)
107  {
108  inputGraph.vertex(v, cv);
109 
110  // fill above portion of the window
111  if (cv[1] > 0)
112  {
113  const std::size_t row0 = cv[1] < distanceUpperBound ? 0 : cv[1] - distanceUpperBound;
114  std::size_t offsetY = 1;
115 
116  // We use yPlus = y+1 to avoid the 0-1 case. In this block y = yPlus-1.
117  for(std::size_t yPlus = cv[1]; yPlus > row0; --yPlus, ++offsetY)
118  {
119  const std::size_t offsetX = (metric == LiftingMetric::PathLength) ?
120  distanceUpperBound - offsetY:
121  ::floor(::sqrt(distanceUpperBoundSquared - offsetY * offsetY));
122 
123  const std::size_t col0 = cv[0] < offsetX ? 0 : cv[0] - offsetX;
124  std::size_t colN = cv[0] + offsetX;
125  if(colN > inputGraph.shape(0) - 1)
126  colN = inputGraph.shape(0) - 1;
127 
128  for (std::size_t x = col0; x <= colN; ++x)
129  {
130  if (metric == LiftingMetric::PathLength)
131  {
132  const std::size_t distance = ::abs(x - cv[0]) + ::abs(yPlus - 1 - cv[1]);
133 
134  if (distance > distanceLowerBound)
135  {
136  const size_type w = inputGraph.vertex({{x, yPlus - 1}});
137  outputGraph.insertEdge(v, w);
138  }
139  }
140  else
141  {
142  const std::size_t sqaredDistance = (x - cv[0]) * (x - cv[0]) + (yPlus - 1 - cv[1]) * (yPlus - 1 - cv[1]);
143 
144  if (sqaredDistance > distanceLowerBoundSquared)
145  {
146  const size_type w = inputGraph.vertex({{x, yPlus - 1}});
147  outputGraph.insertEdge(v, w);
148  }
149  }
150  }
151  }
152  }
153 
154  // Add middle horizontal line, except for the central point
155  {
156  const std::size_t offsetX = distanceUpperBound;
157  const std::size_t y = cv[1];
158  const std::size_t col0 = cv[0] < offsetX ? 0 : cv[0] - offsetX;
159  std::size_t colN = cv[0] + offsetX;
160 
161  if (colN > inputGraph.shape(0) - 1)
162  colN = inputGraph.shape(0) - 1;
163 
164  if (cv[0] > distanceLowerBound)
165  for (std::size_t x = col0; x <= cv[0] - distanceLowerBound - 1; ++x)
166  {
167  const size_type& w = inputGraph.vertex({{x, y}});
168  outputGraph.insertEdge(v, w);
169  }
170 
171  for (std::size_t x = cv[0] + distanceLowerBound + 1; x <= colN; ++x)
172  {
173  const size_type& w = inputGraph.vertex({{x, y}});
174  outputGraph.insertEdge(v, w);
175  }
176  }
177 
178  // fill below window
179  if (cv[1] < inputGraph.shape(1) - 1)
180  {
181  const std::size_t row0 = cv[1] + 1;
182  std::size_t rowN = cv[1] + distanceUpperBound;
183 
184  if (cv[1] + distanceUpperBound > inputGraph.shape(1) - 1)
185  rowN = inputGraph.shape(1) - 1;
186 
187  std::size_t offsetY = 1;
188  for (std::size_t y = row0; y <= rowN; ++y, ++offsetY)
189  {
190  const std::size_t offsetX = (metric == LiftingMetric::PathLength) ?
191  distanceUpperBound - offsetY :
192  ::floor(::sqrt(distanceUpperBoundSquared - offsetY * offsetY));
193 
194  const std::size_t col0 = cv[0] < offsetX ? 0 : cv[0] - offsetX;
195  std::size_t colN = cv[0] + offsetX;
196 
197  if (colN > inputGraph.shape(0) - 1)
198  colN = inputGraph.shape(0) - 1;
199 
200  for (std::size_t x = col0; x <= colN; ++x)
201  {
202  if (metric == LiftingMetric::PathLength)
203  {
204  const std::size_t distance = ::abs(x - cv[0]) + ::abs(y - cv[1]);
205 
206  if (distance > distanceLowerBound)
207  {
208  const size_type w = inputGraph.vertex({{x, y}});
209  outputGraph.insertEdge(v, w);
210  }
211  }
212  else
213  {
214  const std::size_t sqaredDistance = (x - cv[0]) * (x - cv[0]) + (y - cv[1]) * (y - cv[1]);
215 
216  if (sqaredDistance > distanceLowerBoundSquared)
217  {
218  const size_type w = inputGraph.vertex({{x, y}});
219  outputGraph.insertEdge(v, w);
220  }
221  }
222  }
223  }
224  }
225  }
226 }
227 
228 } // namespace graph
229 } // namespace andres
230 
231 #endif // #ifndef ANDRES_GRAPH_LIFTING_HXX