blob: b06293131592fc861283e24d0fba23f4fcc1f7a6 [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2015 Google Inc. All rights reserved.
3// http://ceres-solver.org/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are met:
7//
8// * Redistributions of source code must retain the above copyright notice,
9// this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright notice,
11// this list of conditions and the following disclaimer in the documentation
12// and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors may be
14// used to endorse or promote products derived from this software without
15// specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27// POSSIBILITY OF SUCH DAMAGE.
28//
29// Author: sameeragarwal@google.com (Sameer Agarwal)
30//
31// Various algorithms that operate on undirected graphs.
32
33#ifndef CERES_INTERNAL_GRAPH_ALGORITHMS_H_
34#define CERES_INTERNAL_GRAPH_ALGORITHMS_H_
35
36#include <algorithm>
37#include <unordered_map>
38#include <unordered_set>
39#include <vector>
40#include <utility>
41#include "ceres/graph.h"
42#include "ceres/wall_time.h"
43#include "glog/logging.h"
44
45namespace ceres {
46namespace internal {
47
48// Compare two vertices of a graph by their degrees, if the degrees
49// are equal then order them by their ids.
50template <typename Vertex>
51class VertexTotalOrdering {
52 public:
53 explicit VertexTotalOrdering(const Graph<Vertex>& graph)
54 : graph_(graph) {}
55
56 bool operator()(const Vertex& lhs, const Vertex& rhs) const {
57 if (graph_.Neighbors(lhs).size() == graph_.Neighbors(rhs).size()) {
58 return lhs < rhs;
59 }
60 return graph_.Neighbors(lhs).size() < graph_.Neighbors(rhs).size();
61 }
62
63 private:
64 const Graph<Vertex>& graph_;
65};
66
67template <typename Vertex>
68class VertexDegreeLessThan {
69 public:
70 explicit VertexDegreeLessThan(const Graph<Vertex>& graph)
71 : graph_(graph) {}
72
73 bool operator()(const Vertex& lhs, const Vertex& rhs) const {
74 return graph_.Neighbors(lhs).size() < graph_.Neighbors(rhs).size();
75 }
76
77 private:
78 const Graph<Vertex>& graph_;
79};
80
81// Order the vertices of a graph using its (approximately) largest
82// independent set, where an independent set of a graph is a set of
83// vertices that have no edges connecting them. The maximum
84// independent set problem is NP-Hard, but there are effective
85// approximation algorithms available. The implementation here uses a
86// breadth first search that explores the vertices in order of
87// increasing degree. The same idea is used by Saad & Li in "MIQR: A
88// multilevel incomplete QR preconditioner for large sparse
89// least-squares problems", SIMAX, 2007.
90//
91// Given a undirected graph G(V,E), the algorithm is a greedy BFS
92// search where the vertices are explored in increasing order of their
93// degree. The output vector ordering contains elements of S in
94// increasing order of their degree, followed by elements of V - S in
95// increasing order of degree. The return value of the function is the
96// cardinality of S.
97template <typename Vertex>
98int IndependentSetOrdering(const Graph<Vertex>& graph,
99 std::vector<Vertex>* ordering) {
100 const std::unordered_set<Vertex>& vertices = graph.vertices();
101 const int num_vertices = vertices.size();
102
103 CHECK(ordering != nullptr);
104 ordering->clear();
105 ordering->reserve(num_vertices);
106
107 // Colors for labeling the graph during the BFS.
108 const char kWhite = 0;
109 const char kGrey = 1;
110 const char kBlack = 2;
111
112 // Mark all vertices white.
113 std::unordered_map<Vertex, char> vertex_color;
114 std::vector<Vertex> vertex_queue;
115 for (const Vertex& vertex : vertices) {
116 vertex_color[vertex] = kWhite;
117 vertex_queue.push_back(vertex);
118 }
119
120 std::sort(vertex_queue.begin(),
121 vertex_queue.end(),
122 VertexTotalOrdering<Vertex>(graph));
123
124 // Iterate over vertex_queue. Pick the first white vertex, add it
125 // to the independent set. Mark it black and its neighbors grey.
126 for (const Vertex& vertex : vertex_queue) {
127 if (vertex_color[vertex] != kWhite) {
128 continue;
129 }
130
131 ordering->push_back(vertex);
132 vertex_color[vertex] = kBlack;
133 const std::unordered_set<Vertex>& neighbors = graph.Neighbors(vertex);
134 for (const Vertex& neighbor : neighbors) {
135 vertex_color[neighbor] = kGrey;
136 }
137 }
138
139 int independent_set_size = ordering->size();
140
141 // Iterate over the vertices and add all the grey vertices to the
142 // ordering. At this stage there should only be black or grey
143 // vertices in the graph.
144 for (const Vertex& vertex : vertex_queue) {
145 DCHECK(vertex_color[vertex] != kWhite);
146 if (vertex_color[vertex] != kBlack) {
147 ordering->push_back(vertex);
148 }
149 }
150
151 CHECK_EQ(ordering->size(), num_vertices);
152 return independent_set_size;
153}
154
155// Same as above with one important difference. The ordering parameter
156// is an input/output parameter which carries an initial ordering of
157// the vertices of the graph. The greedy independent set algorithm
158// starts by sorting the vertices in increasing order of their
159// degree. The input ordering is used to stabilize this sort, i.e., if
160// two vertices have the same degree then they are ordered in the same
161// order in which they occur in "ordering".
162//
163// This is useful in eliminating non-determinism from the Schur
164// ordering algorithm over all.
165template <typename Vertex>
166int StableIndependentSetOrdering(const Graph<Vertex>& graph,
167 std::vector<Vertex>* ordering) {
168 CHECK(ordering != nullptr);
169 const std::unordered_set<Vertex>& vertices = graph.vertices();
170 const int num_vertices = vertices.size();
171 CHECK_EQ(vertices.size(), ordering->size());
172
173 // Colors for labeling the graph during the BFS.
174 const char kWhite = 0;
175 const char kGrey = 1;
176 const char kBlack = 2;
177
178 std::vector<Vertex> vertex_queue(*ordering);
179
180 std::stable_sort(vertex_queue.begin(), vertex_queue.end(),
181 VertexDegreeLessThan<Vertex>(graph));
182
183 // Mark all vertices white.
184 std::unordered_map<Vertex, char> vertex_color;
185 for (const Vertex& vertex : vertices) {
186 vertex_color[vertex] = kWhite;
187 }
188
189 ordering->clear();
190 ordering->reserve(num_vertices);
191 // Iterate over vertex_queue. Pick the first white vertex, add it
192 // to the independent set. Mark it black and its neighbors grey.
193 for (int i = 0; i < vertex_queue.size(); ++i) {
194 const Vertex& vertex = vertex_queue[i];
195 if (vertex_color[vertex] != kWhite) {
196 continue;
197 }
198
199 ordering->push_back(vertex);
200 vertex_color[vertex] = kBlack;
201 const std::unordered_set<Vertex>& neighbors = graph.Neighbors(vertex);
202 for (const Vertex& neighbor : neighbors) {
203 vertex_color[neighbor] = kGrey;
204 }
205 }
206
207 int independent_set_size = ordering->size();
208
209 // Iterate over the vertices and add all the grey vertices to the
210 // ordering. At this stage there should only be black or grey
211 // vertices in the graph.
212 for (const Vertex& vertex : vertex_queue) {
213 DCHECK(vertex_color[vertex] != kWhite);
214 if (vertex_color[vertex] != kBlack) {
215 ordering->push_back(vertex);
216 }
217 }
218
219 CHECK_EQ(ordering->size(), num_vertices);
220 return independent_set_size;
221}
222
223// Find the connected component for a vertex implemented using the
224// find and update operation for disjoint-set. Recursively traverse
225// the disjoint set structure till you reach a vertex whose connected
226// component has the same id as the vertex itself. Along the way
227// update the connected components of all the vertices. This updating
228// is what gives this data structure its efficiency.
229template <typename Vertex>
230Vertex FindConnectedComponent(const Vertex& vertex,
231 std::unordered_map<Vertex, Vertex>* union_find) {
232 auto it = union_find->find(vertex);
233 DCHECK(it != union_find->end());
234 if (it->second != vertex) {
235 it->second = FindConnectedComponent(it->second, union_find);
236 }
237
238 return it->second;
239}
240
241// Compute a degree two constrained Maximum Spanning Tree/forest of
242// the input graph. Caller owns the result.
243//
244// Finding degree 2 spanning tree of a graph is not always
245// possible. For example a star graph, i.e. a graph with n-nodes
246// where one node is connected to the other n-1 nodes does not have
247// a any spanning trees of degree less than n-1.Even if such a tree
248// exists, finding such a tree is NP-Hard.
249
250// We get around both of these problems by using a greedy, degree
251// constrained variant of Kruskal's algorithm. We start with a graph
252// G_T with the same vertex set V as the input graph G(V,E) but an
253// empty edge set. We then iterate over the edges of G in decreasing
254// order of weight, adding them to G_T if doing so does not create a
255// cycle in G_T} and the degree of all the vertices in G_T remains
256// bounded by two. This O(|E|) algorithm results in a degree-2
257// spanning forest, or a collection of linear paths that span the
258// graph G.
259template <typename Vertex>
260WeightedGraph<Vertex>*
261Degree2MaximumSpanningForest(const WeightedGraph<Vertex>& graph) {
262 // Array of edges sorted in decreasing order of their weights.
263 std::vector<std::pair<double, std::pair<Vertex, Vertex>>> weighted_edges;
264 WeightedGraph<Vertex>* forest = new WeightedGraph<Vertex>();
265
266 // Disjoint-set to keep track of the connected components in the
267 // maximum spanning tree.
268 std::unordered_map<Vertex, Vertex> disjoint_set;
269
270 // Sort of the edges in the graph in decreasing order of their
271 // weight. Also add the vertices of the graph to the Maximum
272 // Spanning Tree graph and set each vertex to be its own connected
273 // component in the disjoint_set structure.
274 const std::unordered_set<Vertex>& vertices = graph.vertices();
275 for (const Vertex& vertex1 : vertices) {
276 forest->AddVertex(vertex1, graph.VertexWeight(vertex1));
277 disjoint_set[vertex1] = vertex1;
278
279 const std::unordered_set<Vertex>& neighbors = graph.Neighbors(vertex1);
280 for (const Vertex& vertex2 : neighbors) {
281 if (vertex1 >= vertex2) {
282 continue;
283 }
284 const double weight = graph.EdgeWeight(vertex1, vertex2);
285 weighted_edges.push_back(
286 std::make_pair(weight, std::make_pair(vertex1, vertex2)));
287 }
288 }
289
290 // The elements of this vector, are pairs<edge_weight,
291 // edge>. Sorting it using the reverse iterators gives us the edges
292 // in decreasing order of edges.
293 std::sort(weighted_edges.rbegin(), weighted_edges.rend());
294
295 // Greedily add edges to the spanning tree/forest as long as they do
296 // not violate the degree/cycle constraint.
297 for (int i =0; i < weighted_edges.size(); ++i) {
298 const std::pair<Vertex, Vertex>& edge = weighted_edges[i].second;
299 const Vertex vertex1 = edge.first;
300 const Vertex vertex2 = edge.second;
301
302 // Check if either of the vertices are of degree 2 already, in
303 // which case adding this edge will violate the degree 2
304 // constraint.
305 if ((forest->Neighbors(vertex1).size() == 2) ||
306 (forest->Neighbors(vertex2).size() == 2)) {
307 continue;
308 }
309
310 // Find the id of the connected component to which the two
311 // vertices belong to. If the id is the same, it means that the
312 // two of them are already connected to each other via some other
313 // vertex, and adding this edge will create a cycle.
314 Vertex root1 = FindConnectedComponent(vertex1, &disjoint_set);
315 Vertex root2 = FindConnectedComponent(vertex2, &disjoint_set);
316
317 if (root1 == root2) {
318 continue;
319 }
320
321 // This edge can be added, add an edge in either direction with
322 // the same weight as the original graph.
323 const double edge_weight = graph.EdgeWeight(vertex1, vertex2);
324 forest->AddEdge(vertex1, vertex2, edge_weight);
325 forest->AddEdge(vertex2, vertex1, edge_weight);
326
327 // Connected the two connected components by updating the
328 // disjoint_set structure. Always connect the connected component
329 // with the greater index with the connected component with the
330 // smaller index. This should ensure shallower trees, for quicker
331 // lookup.
332 if (root2 < root1) {
333 std::swap(root1, root2);
334 }
335
336 disjoint_set[root2] = root1;
337 }
338 return forest;
339}
340
341} // namespace internal
342} // namespace ceres
343
344#endif // CERES_INTERNAL_GRAPH_ALGORITHMS_H_