blob: 7d63b337f68eeeed406efc27bceb5cb37ec9d7b1 [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>
Austin Schuh70cc9552019-01-21 19:46:48 -080039#include <utility>
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080040#include <vector>
41
Austin Schuh70cc9552019-01-21 19:46:48 -080042#include "ceres/graph.h"
43#include "ceres/wall_time.h"
44#include "glog/logging.h"
45
46namespace ceres {
47namespace internal {
48
49// Compare two vertices of a graph by their degrees, if the degrees
50// are equal then order them by their ids.
51template <typename Vertex>
52class VertexTotalOrdering {
53 public:
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080054 explicit VertexTotalOrdering(const Graph<Vertex>& graph) : graph_(graph) {}
Austin Schuh70cc9552019-01-21 19:46:48 -080055
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:
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080070 explicit VertexDegreeLessThan(const Graph<Vertex>& graph) : graph_(graph) {}
Austin Schuh70cc9552019-01-21 19:46:48 -080071
72 bool operator()(const Vertex& lhs, const Vertex& rhs) const {
73 return graph_.Neighbors(lhs).size() < graph_.Neighbors(rhs).size();
74 }
75
76 private:
77 const Graph<Vertex>& graph_;
78};
79
80// Order the vertices of a graph using its (approximately) largest
81// independent set, where an independent set of a graph is a set of
82// vertices that have no edges connecting them. The maximum
83// independent set problem is NP-Hard, but there are effective
84// approximation algorithms available. The implementation here uses a
85// breadth first search that explores the vertices in order of
86// increasing degree. The same idea is used by Saad & Li in "MIQR: A
87// multilevel incomplete QR preconditioner for large sparse
88// least-squares problems", SIMAX, 2007.
89//
90// Given a undirected graph G(V,E), the algorithm is a greedy BFS
91// search where the vertices are explored in increasing order of their
92// degree. The output vector ordering contains elements of S in
93// increasing order of their degree, followed by elements of V - S in
94// increasing order of degree. The return value of the function is the
95// cardinality of S.
96template <typename Vertex>
97int IndependentSetOrdering(const Graph<Vertex>& graph,
98 std::vector<Vertex>* ordering) {
99 const std::unordered_set<Vertex>& vertices = graph.vertices();
100 const int num_vertices = vertices.size();
101
102 CHECK(ordering != nullptr);
103 ordering->clear();
104 ordering->reserve(num_vertices);
105
106 // Colors for labeling the graph during the BFS.
107 const char kWhite = 0;
108 const char kGrey = 1;
109 const char kBlack = 2;
110
111 // Mark all vertices white.
112 std::unordered_map<Vertex, char> vertex_color;
113 std::vector<Vertex> vertex_queue;
114 for (const Vertex& vertex : vertices) {
115 vertex_color[vertex] = kWhite;
116 vertex_queue.push_back(vertex);
117 }
118
119 std::sort(vertex_queue.begin(),
120 vertex_queue.end(),
121 VertexTotalOrdering<Vertex>(graph));
122
123 // Iterate over vertex_queue. Pick the first white vertex, add it
124 // to the independent set. Mark it black and its neighbors grey.
125 for (const Vertex& vertex : vertex_queue) {
126 if (vertex_color[vertex] != kWhite) {
127 continue;
128 }
129
130 ordering->push_back(vertex);
131 vertex_color[vertex] = kBlack;
132 const std::unordered_set<Vertex>& neighbors = graph.Neighbors(vertex);
133 for (const Vertex& neighbor : neighbors) {
134 vertex_color[neighbor] = kGrey;
135 }
136 }
137
138 int independent_set_size = ordering->size();
139
140 // Iterate over the vertices and add all the grey vertices to the
141 // ordering. At this stage there should only be black or grey
142 // vertices in the graph.
143 for (const Vertex& vertex : vertex_queue) {
144 DCHECK(vertex_color[vertex] != kWhite);
145 if (vertex_color[vertex] != kBlack) {
146 ordering->push_back(vertex);
147 }
148 }
149
150 CHECK_EQ(ordering->size(), num_vertices);
151 return independent_set_size;
152}
153
154// Same as above with one important difference. The ordering parameter
155// is an input/output parameter which carries an initial ordering of
156// the vertices of the graph. The greedy independent set algorithm
157// starts by sorting the vertices in increasing order of their
158// degree. The input ordering is used to stabilize this sort, i.e., if
159// two vertices have the same degree then they are ordered in the same
160// order in which they occur in "ordering".
161//
162// This is useful in eliminating non-determinism from the Schur
163// ordering algorithm over all.
164template <typename Vertex>
165int StableIndependentSetOrdering(const Graph<Vertex>& graph,
166 std::vector<Vertex>* ordering) {
167 CHECK(ordering != nullptr);
168 const std::unordered_set<Vertex>& vertices = graph.vertices();
169 const int num_vertices = vertices.size();
170 CHECK_EQ(vertices.size(), ordering->size());
171
172 // Colors for labeling the graph during the BFS.
173 const char kWhite = 0;
174 const char kGrey = 1;
175 const char kBlack = 2;
176
177 std::vector<Vertex> vertex_queue(*ordering);
178
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800179 std::stable_sort(vertex_queue.begin(),
180 vertex_queue.end(),
181 VertexDegreeLessThan<Vertex>(graph));
Austin Schuh70cc9552019-01-21 19:46:48 -0800182
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>
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800260WeightedGraph<Vertex>* Degree2MaximumSpanningForest(
261 const WeightedGraph<Vertex>& graph) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800262 // 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.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800297 for (int i = 0; i < weighted_edges.size(); ++i) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800298 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_