blob: a3bebed2c1d0587fbcacae05a747249452c017fc [file] [log] [blame]
Keir Mierle8ebb0732012-04-30 23:09:08 -07001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2010, 2011, 2012 Google Inc. All rights reserved.
3// http://code.google.com/p/ceres-solver/
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
Sameer Agarwal8140f0f2013-03-12 09:45:08 -070031#ifndef CERES_NO_SUITESPARSE
32
Keir Mierle8ebb0732012-04-30 23:09:08 -070033#include "ceres/visibility_based_preconditioner.h"
34
35#include <algorithm>
36#include <functional>
37#include <iterator>
Keir Mierle8ebb0732012-04-30 23:09:08 -070038#include <set>
39#include <utility>
40#include <vector>
Keir Mierle8ebb0732012-04-30 23:09:08 -070041#include "Eigen/Dense"
42#include "ceres/block_random_access_sparse_matrix.h"
43#include "ceres/block_sparse_matrix.h"
44#include "ceres/canonical_views_clustering.h"
45#include "ceres/collections_port.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070046#include "ceres/graph.h"
47#include "ceres/graph_algorithms.h"
Sameer Agarwal0beab862012-08-13 15:12:01 -070048#include "ceres/internal/scoped_ptr.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070049#include "ceres/linear_solver.h"
50#include "ceres/schur_eliminator.h"
Sameer Agarwalf06b9fa2013-10-27 21:38:13 -070051#include "ceres/single_linkage_clustering.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070052#include "ceres/visibility.h"
Sameer Agarwal0beab862012-08-13 15:12:01 -070053#include "glog/logging.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070054
55namespace ceres {
56namespace internal {
57
58// TODO(sameeragarwal): Currently these are magic weights for the
59// preconditioner construction. Move these higher up into the Options
60// struct and provide some guidelines for choosing them.
61//
62// This will require some more work on the clustering algorithm and
63// possibly some more refactoring of the code.
Sameer Agarwalf06b9fa2013-10-27 21:38:13 -070064static const double kCanonicalViewsSizePenaltyWeight = 3.0;
65static const double kCanonicalViewsSimilarityPenaltyWeight = 0.0;
66static const double kSingleLinkageMinSimilarity = 0.9;
Keir Mierle8ebb0732012-04-30 23:09:08 -070067
Keir Mierle8ebb0732012-04-30 23:09:08 -070068VisibilityBasedPreconditioner::VisibilityBasedPreconditioner(
69 const CompressedRowBlockStructure& bs,
Sameer Agarwal290b9752013-02-17 16:50:37 -080070 const Preconditioner::Options& options)
Keir Mierle8ebb0732012-04-30 23:09:08 -070071 : options_(options),
72 num_blocks_(0),
73 num_clusters_(0),
74 factor_(NULL) {
Sameer Agarwal0c52f1e2012-09-17 11:30:14 -070075 CHECK_GT(options_.elimination_groups.size(), 1);
76 CHECK_GT(options_.elimination_groups[0], 0);
Sameer Agarwal290b9752013-02-17 16:50:37 -080077 CHECK(options_.type == CLUSTER_JACOBI ||
78 options_.type == CLUSTER_TRIDIAGONAL)
79 << "Unknown preconditioner type: " << options_.type;
Sameer Agarwal0c52f1e2012-09-17 11:30:14 -070080 num_blocks_ = bs.cols.size() - options_.elimination_groups[0];
Keir Mierle8ebb0732012-04-30 23:09:08 -070081 CHECK_GT(num_blocks_, 0)
82 << "Jacobian should have atleast 1 f_block for "
83 << "visibility based preconditioning.";
84
85 // Vector of camera block sizes
86 block_size_.resize(num_blocks_);
87 for (int i = 0; i < num_blocks_; ++i) {
Sameer Agarwal0c52f1e2012-09-17 11:30:14 -070088 block_size_[i] = bs.cols[i + options_.elimination_groups[0]].size;
Keir Mierle8ebb0732012-04-30 23:09:08 -070089 }
90
91 const time_t start_time = time(NULL);
Sameer Agarwal290b9752013-02-17 16:50:37 -080092 switch (options_.type) {
Keir Mierle8ebb0732012-04-30 23:09:08 -070093 case CLUSTER_JACOBI:
94 ComputeClusterJacobiSparsity(bs);
95 break;
96 case CLUSTER_TRIDIAGONAL:
97 ComputeClusterTridiagonalSparsity(bs);
98 break;
99 default:
100 LOG(FATAL) << "Unknown preconditioner type";
101 }
102 const time_t structure_time = time(NULL);
103 InitStorage(bs);
104 const time_t storage_time = time(NULL);
105 InitEliminator(bs);
106 const time_t eliminator_time = time(NULL);
107
108 // Allocate temporary storage for a vector used during
109 // RightMultiply.
110 tmp_rhs_ = CHECK_NOTNULL(ss_.CreateDenseVector(NULL,
111 m_->num_rows(),
112 m_->num_rows()));
113 const time_t init_time = time(NULL);
114 VLOG(2) << "init time: "
115 << init_time - start_time
116 << " structure time: " << structure_time - start_time
117 << " storage time:" << storage_time - structure_time
118 << " eliminator time: " << eliminator_time - storage_time;
119}
120
121VisibilityBasedPreconditioner::~VisibilityBasedPreconditioner() {
122 if (factor_ != NULL) {
123 ss_.Free(factor_);
124 factor_ = NULL;
125 }
126 if (tmp_rhs_ != NULL) {
127 ss_.Free(tmp_rhs_);
128 tmp_rhs_ = NULL;
129 }
130}
131
Keir Mierle8ebb0732012-04-30 23:09:08 -0700132// Determine the sparsity structure of the CLUSTER_JACOBI
133// preconditioner. It clusters cameras using their scene
134// visibility. The clusters form the diagonal blocks of the
135// preconditioner matrix.
136void VisibilityBasedPreconditioner::ComputeClusterJacobiSparsity(
137 const CompressedRowBlockStructure& bs) {
138 vector<set<int> > visibility;
Sameer Agarwal0c52f1e2012-09-17 11:30:14 -0700139 ComputeVisibility(bs, options_.elimination_groups[0], &visibility);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700140 CHECK_EQ(num_blocks_, visibility.size());
141 ClusterCameras(visibility);
142 cluster_pairs_.clear();
143 for (int i = 0; i < num_clusters_; ++i) {
144 cluster_pairs_.insert(make_pair(i, i));
145 }
146}
147
148// Determine the sparsity structure of the CLUSTER_TRIDIAGONAL
149// preconditioner. It clusters cameras using using the scene
150// visibility and then finds the strongly interacting pairs of
151// clusters by constructing another graph with the clusters as
152// vertices and approximating it with a degree-2 maximum spanning
153// forest. The set of edges in this forest are the cluster pairs.
154void VisibilityBasedPreconditioner::ComputeClusterTridiagonalSparsity(
155 const CompressedRowBlockStructure& bs) {
156 vector<set<int> > visibility;
Sameer Agarwal0c52f1e2012-09-17 11:30:14 -0700157 ComputeVisibility(bs, options_.elimination_groups[0], &visibility);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700158 CHECK_EQ(num_blocks_, visibility.size());
159 ClusterCameras(visibility);
160
161 // Construct a weighted graph on the set of clusters, where the
162 // edges are the number of 3D points/e_blocks visible in both the
163 // clusters at the ends of the edge. Return an approximate degree-2
164 // maximum spanning forest of this graph.
165 vector<set<int> > cluster_visibility;
166 ComputeClusterVisibility(visibility, &cluster_visibility);
167 scoped_ptr<Graph<int> > cluster_graph(
168 CHECK_NOTNULL(CreateClusterGraph(cluster_visibility)));
169 scoped_ptr<Graph<int> > forest(
170 CHECK_NOTNULL(Degree2MaximumSpanningForest(*cluster_graph)));
171 ForestToClusterPairs(*forest, &cluster_pairs_);
172}
173
174// Allocate storage for the preconditioner matrix.
175void VisibilityBasedPreconditioner::InitStorage(
176 const CompressedRowBlockStructure& bs) {
177 ComputeBlockPairsInPreconditioner(bs);
178 m_.reset(new BlockRandomAccessSparseMatrix(block_size_, block_pairs_));
179}
180
181// Call the canonical views algorithm and cluster the cameras based on
182// their visibility sets. The visibility set of a camera is the set of
183// e_blocks/3D points in the scene that are seen by it.
184//
185// The cluster_membership_ vector is updated to indicate cluster
186// memberships for each camera block.
187void VisibilityBasedPreconditioner::ClusterCameras(
188 const vector<set<int> >& visibility) {
189 scoped_ptr<Graph<int> > schur_complement_graph(
190 CHECK_NOTNULL(CreateSchurComplementGraph(visibility)));
191
Keir Mierle8ebb0732012-04-30 23:09:08 -0700192 HashMap<int, int> membership;
Sameer Agarwalf06b9fa2013-10-27 21:38:13 -0700193
194 if (options_.visibility_clustering_type == CANONICAL_VIEWS) {
195 vector<int> centers;
196 CanonicalViewsClusteringOptions clustering_options;
197 clustering_options.size_penalty_weight =
198 kCanonicalViewsSizePenaltyWeight;
199 clustering_options.similarity_penalty_weight =
200 kCanonicalViewsSimilarityPenaltyWeight;
201 ComputeCanonicalViewsClustering(clustering_options,
202 *schur_complement_graph,
203 &centers,
204 &membership);
205 num_clusters_ = centers.size();
206 } else if (options_.visibility_clustering_type == SINGLE_LINKAGE) {
207 SingleLinkageClusteringOptions clustering_options;
208 clustering_options.min_similarity =
209 kSingleLinkageMinSimilarity;
210 num_clusters_ = ComputeSingleLinkageClustering(clustering_options,
211 *schur_complement_graph,
212 &membership);
Sameer Agarwal9ba0b352013-11-05 13:04:56 -0800213 } else {
214 LOG(FATAL) << "Unknown visibility clustering algorithm.";
Sameer Agarwalf06b9fa2013-10-27 21:38:13 -0700215 }
216
Keir Mierle8ebb0732012-04-30 23:09:08 -0700217 CHECK_GT(num_clusters_, 0);
218 VLOG(2) << "num_clusters: " << num_clusters_;
219 FlattenMembershipMap(membership, &cluster_membership_);
220}
221
222// Compute the block sparsity structure of the Schur complement
223// matrix. For each pair of cameras contributing a non-zero cell to
224// the schur complement, determine if that cell is present in the
225// preconditioner or not.
226//
227// A pair of cameras contribute a cell to the preconditioner if they
228// are part of the same cluster or if the the two clusters that they
229// belong have an edge connecting them in the degree-2 maximum
230// spanning forest.
231//
232// For example, a camera pair (i,j) where i belonges to cluster1 and
233// j belongs to cluster2 (assume that cluster1 < cluster2).
234//
235// The cell corresponding to (i,j) is present in the preconditioner
236// if cluster1 == cluster2 or the pair (cluster1, cluster2) were
237// connected by an edge in the degree-2 maximum spanning forest.
238//
239// Since we have already expanded the forest into a set of camera
240// pairs/edges, including self edges, the check can be reduced to
241// checking membership of (cluster1, cluster2) in cluster_pairs_.
242void VisibilityBasedPreconditioner::ComputeBlockPairsInPreconditioner(
243 const CompressedRowBlockStructure& bs) {
244 block_pairs_.clear();
245 for (int i = 0; i < num_blocks_; ++i) {
246 block_pairs_.insert(make_pair(i, i));
247 }
248
249 int r = 0;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700250 const int num_row_blocks = bs.rows.size();
Sameer Agarwal0c52f1e2012-09-17 11:30:14 -0700251 const int num_eliminate_blocks = options_.elimination_groups[0];
Keir Mierle8ebb0732012-04-30 23:09:08 -0700252
253 // Iterate over each row of the matrix. The block structure of the
254 // matrix is assumed to be sorted in order of the e_blocks/point
255 // blocks. Thus all row blocks containing an e_block/point occur
256 // contiguously. Further, if present, an e_block is always the first
257 // parameter block in each row block. These structural assumptions
258 // are common to all Schur complement based solvers in Ceres.
259 //
260 // For each e_block/point block we identify the set of cameras
261 // seeing it. The cross product of this set with itself is the set
262 // of non-zero cells contibuted by this e_block.
263 //
264 // The time complexity of this is O(nm^2) where, n is the number of
265 // 3d points and m is the maximum number of cameras seeing any
266 // point, which for most scenes is a fairly small number.
267 while (r < num_row_blocks) {
268 int e_block_id = bs.rows[r].cells.front().block_id;
269 if (e_block_id >= num_eliminate_blocks) {
270 // Skip the rows whose first block is an f_block.
271 break;
272 }
273
274 set<int> f_blocks;
275 for (; r < num_row_blocks; ++r) {
276 const CompressedRow& row = bs.rows[r];
277 if (row.cells.front().block_id != e_block_id) {
278 break;
279 }
280
281 // Iterate over the blocks in the row, ignoring the first block
282 // since it is the one to be eliminated and adding the rest to
283 // the list of f_blocks associated with this e_block.
284 for (int c = 1; c < row.cells.size(); ++c) {
285 const Cell& cell = row.cells[c];
286 const int f_block_id = cell.block_id - num_eliminate_blocks;
287 CHECK_GE(f_block_id, 0);
288 f_blocks.insert(f_block_id);
289 }
290 }
291
292 for (set<int>::const_iterator block1 = f_blocks.begin();
293 block1 != f_blocks.end();
294 ++block1) {
295 set<int>::const_iterator block2 = block1;
296 ++block2;
297 for (; block2 != f_blocks.end(); ++block2) {
298 if (IsBlockPairInPreconditioner(*block1, *block2)) {
299 block_pairs_.insert(make_pair(*block1, *block2));
Keir Mierle8ebb0732012-04-30 23:09:08 -0700300 }
301 }
302 }
303 }
304
305 // The remaining rows which do not contain any e_blocks.
306 for (; r < num_row_blocks; ++r) {
307 const CompressedRow& row = bs.rows[r];
308 CHECK_GE(row.cells.front().block_id, num_eliminate_blocks);
309 for (int i = 0; i < row.cells.size(); ++i) {
310 const int block1 = row.cells[i].block_id - num_eliminate_blocks;
311 for (int j = 0; j < row.cells.size(); ++j) {
312 const int block2 = row.cells[j].block_id - num_eliminate_blocks;
313 if (block1 <= block2) {
314 if (IsBlockPairInPreconditioner(block1, block2)) {
315 block_pairs_.insert(make_pair(block1, block2));
Keir Mierle8ebb0732012-04-30 23:09:08 -0700316 }
317 }
318 }
319 }
320 }
321
Sameer Agarwaleb22b8b2012-06-11 11:50:43 -0700322 VLOG(1) << "Block pair stats: " << block_pairs_.size();
Keir Mierle8ebb0732012-04-30 23:09:08 -0700323}
324
325// Initialize the SchurEliminator.
326void VisibilityBasedPreconditioner::InitEliminator(
327 const CompressedRowBlockStructure& bs) {
328 LinearSolver::Options eliminator_options;
Sameer Agarwal0c52f1e2012-09-17 11:30:14 -0700329 eliminator_options.elimination_groups = options_.elimination_groups;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700330 eliminator_options.num_threads = options_.num_threads;
Sameer Agarwal5a161a22013-10-29 22:08:15 -0700331 eliminator_options.e_block_size = options_.e_block_size;
332 eliminator_options.f_block_size = options_.f_block_size;
333 eliminator_options.row_block_size = options_.row_block_size;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700334 eliminator_.reset(SchurEliminatorBase::Create(eliminator_options));
Sameer Agarwal5a161a22013-10-29 22:08:15 -0700335 eliminator_->Init(eliminator_options.elimination_groups[0], &bs);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700336}
337
Sameer Agarwala9d8ef82012-05-14 02:28:05 -0700338// Update the values of the preconditioner matrix and factorize it.
Sameer Agarwal2f1454f2013-06-13 23:34:46 -0700339bool VisibilityBasedPreconditioner::UpdateImpl(const BlockSparseMatrix& A,
340 const double* D) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700341 const time_t start_time = time(NULL);
342 const int num_rows = m_->num_rows();
343 CHECK_GT(num_rows, 0);
344
345 // We need a dummy rhs vector and a dummy b vector since the Schur
346 // eliminator combines the computation of the reduced camera matrix
347 // with the computation of the right hand side of that linear
348 // system.
349 //
350 // TODO(sameeragarwal): Perhaps its worth refactoring the
351 // SchurEliminator::Eliminate function to allow NULL for the rhs. As
352 // of now it does not seem to be worth the effort.
353 Vector rhs = Vector::Zero(m_->num_rows());
354 Vector b = Vector::Zero(A.num_rows());
355
356 // Compute a subset of the entries of the Schur complement.
357 eliminator_->Eliminate(&A, b.data(), D, m_.get(), rhs.data());
358
Sameer Agarwal290b9752013-02-17 16:50:37 -0800359 // Try factorizing the matrix. For CLUSTER_JACOBI, this should
360 // always succeed modulo some numerical/conditioning problems. For
361 // CLUSTER_TRIDIAGONAL, in general the preconditioner matrix as
362 // constructed is not positive definite. However, we will go ahead
363 // and try factorizing it. If it works, great, otherwise we scale
364 // all the cells in the preconditioner corresponding to the edges in
365 // the degree-2 forest and that guarantees positive
Keir Mierle8ebb0732012-04-30 23:09:08 -0700366 // definiteness. The proof of this fact can be found in Lemma 1 in
367 // "Visibility Based Preconditioning for Bundle Adjustment".
368 //
369 // Doing the factorization like this saves us matrix mass when
370 // scaling is not needed, which is quite often in our experience.
Sameer Agarwal79bde352013-11-21 21:33:51 -0800371 LinearSolverTerminationType status = Factorize();
372
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800373 if (status == LINEAR_SOLVER_FATAL_ERROR) {
Sameer Agarwal79bde352013-11-21 21:33:51 -0800374 return false;
375 }
Keir Mierle8ebb0732012-04-30 23:09:08 -0700376
377 // The scaling only affects the tri-diagonal case, since
378 // ScaleOffDiagonalBlocks only pays attenion to the cells that
Sameer Agarwal290b9752013-02-17 16:50:37 -0800379 // belong to the edges of the degree-2 forest. In the CLUSTER_JACOBI
380 // case, the preconditioner is guaranteed to be positive
381 // semidefinite.
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800382 if (status == LINEAR_SOLVER_FAILURE && options_.type == CLUSTER_TRIDIAGONAL) {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700383 VLOG(1) << "Unscaled factorization failed. Retrying with off-diagonal "
384 << "scaling";
385 ScaleOffDiagonalCells();
386 status = Factorize();
387 }
388
389 VLOG(2) << "Compute time: " << time(NULL) - start_time;
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800390 return (status == LINEAR_SOLVER_SUCCESS);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700391}
392
393// Consider the preconditioner matrix as meta-block matrix, whose
394// blocks correspond to the clusters. Then cluster pairs corresponding
395// to edges in the degree-2 forest are off diagonal entries of this
396// matrix. Scaling these off-diagonal entries by 1/2 forces this
397// matrix to be positive definite.
398void VisibilityBasedPreconditioner::ScaleOffDiagonalCells() {
399 for (set< pair<int, int> >::const_iterator it = block_pairs_.begin();
400 it != block_pairs_.end();
401 ++it) {
402 const int block1 = it->first;
403 const int block2 = it->second;
404 if (!IsBlockPairOffDiagonal(block1, block2)) {
405 continue;
406 }
407
408 int r, c, row_stride, col_stride;
409 CellInfo* cell_info = m_->GetCell(block1, block2,
410 &r, &c,
411 &row_stride, &col_stride);
412 CHECK(cell_info != NULL)
413 << "Cell missing for block pair (" << block1 << "," << block2 << ")"
414 << " cluster pair (" << cluster_membership_[block1]
415 << " " << cluster_membership_[block2] << ")";
416
417 // Ah the magic of tri-diagonal matrices and diagonal
418 // dominance. See Lemma 1 in "Visibility Based Preconditioning
419 // For Bundle Adjustment".
420 MatrixRef m(cell_info->values, row_stride, col_stride);
421 m.block(r, c, block_size_[block1], block_size_[block2]) *= 0.5;
422 }
423}
424
425// Compute the sparse Cholesky factorization of the preconditioner
426// matrix.
Sameer Agarwal79bde352013-11-21 21:33:51 -0800427LinearSolverTerminationType VisibilityBasedPreconditioner::Factorize() {
Keir Mierle8ebb0732012-04-30 23:09:08 -0700428 // Extract the TripletSparseMatrix that is used for actually storing
429 // S and convert it into a cholmod_sparse object.
430 cholmod_sparse* lhs = ss_.CreateSparseMatrix(
431 down_cast<BlockRandomAccessSparseMatrix*>(
432 m_.get())->mutable_matrix());
433
434 // The matrix is symmetric, and the upper triangular part of the
435 // matrix contains the values.
436 lhs->stype = 1;
437
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800438 // TODO(sameeragarwal): Refactor to pipe this up and out.
439 string status;
440
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700441 // Symbolic factorization is computed if we don't already have one handy.
Keir Mierle8ebb0732012-04-30 23:09:08 -0700442 if (factor_ == NULL) {
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800443 factor_ = ss_.BlockAnalyzeCholesky(lhs, block_size_, block_size_, &status);
Sameer Agarwal7a3c43b2012-06-05 23:10:59 -0700444 }
445
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800446 const LinearSolverTerminationType termination_type =
Sameer Agarwal33e01b92013-11-27 10:24:03 -0800447 (factor_ != NULL)
448 ? ss_.Cholesky(lhs, factor_, &status)
449 : LINEAR_SOLVER_FATAL_ERROR;
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800450
Keir Mierle8ebb0732012-04-30 23:09:08 -0700451 ss_.Free(lhs);
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800452 return termination_type;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700453}
454
455void VisibilityBasedPreconditioner::RightMultiply(const double* x,
456 double* y) const {
457 CHECK_NOTNULL(x);
458 CHECK_NOTNULL(y);
459 SuiteSparse* ss = const_cast<SuiteSparse*>(&ss_);
460
461 const int num_rows = m_->num_rows();
462 memcpy(CHECK_NOTNULL(tmp_rhs_)->x, x, m_->num_rows() * sizeof(*x));
Sameer Agarwalb16e1182013-11-25 05:47:43 -0800463 // TODO(sameeragarwal): Better error handling.
464 string status;
Sameer Agarwal89a592f2013-11-26 11:35:49 -0800465 cholmod_dense* solution =
466 CHECK_NOTNULL(ss->Solve(factor_, tmp_rhs_, &status));
Keir Mierle8ebb0732012-04-30 23:09:08 -0700467 memcpy(y, solution->x, sizeof(*y) * num_rows);
468 ss->Free(solution);
469}
470
471int VisibilityBasedPreconditioner::num_rows() const {
472 return m_->num_rows();
473}
474
475// Classify camera/f_block pairs as in and out of the preconditioner,
476// based on whether the cluster pair that they belong to is in the
477// preconditioner or not.
478bool VisibilityBasedPreconditioner::IsBlockPairInPreconditioner(
479 const int block1,
480 const int block2) const {
481 int cluster1 = cluster_membership_[block1];
482 int cluster2 = cluster_membership_[block2];
483 if (cluster1 > cluster2) {
484 std::swap(cluster1, cluster2);
485 }
486 return (cluster_pairs_.count(make_pair(cluster1, cluster2)) > 0);
487}
488
489bool VisibilityBasedPreconditioner::IsBlockPairOffDiagonal(
490 const int block1,
491 const int block2) const {
492 return (cluster_membership_[block1] != cluster_membership_[block2]);
493}
494
495// Convert a graph into a list of edges that includes self edges for
496// each vertex.
497void VisibilityBasedPreconditioner::ForestToClusterPairs(
498 const Graph<int>& forest,
499 HashSet<pair<int, int> >* cluster_pairs) const {
500 CHECK_NOTNULL(cluster_pairs)->clear();
501 const HashSet<int>& vertices = forest.vertices();
502 CHECK_EQ(vertices.size(), num_clusters_);
503
504 // Add all the cluster pairs corresponding to the edges in the
505 // forest.
506 for (HashSet<int>::const_iterator it1 = vertices.begin();
507 it1 != vertices.end();
508 ++it1) {
509 const int cluster1 = *it1;
510 cluster_pairs->insert(make_pair(cluster1, cluster1));
511 const HashSet<int>& neighbors = forest.Neighbors(cluster1);
512 for (HashSet<int>::const_iterator it2 = neighbors.begin();
513 it2 != neighbors.end();
514 ++it2) {
515 const int cluster2 = *it2;
516 if (cluster1 < cluster2) {
517 cluster_pairs->insert(make_pair(cluster1, cluster2));
518 }
519 }
520 }
521}
522
523// The visibilty set of a cluster is the union of the visibilty sets
524// of all its cameras. In other words, the set of points visible to
525// any camera in the cluster.
526void VisibilityBasedPreconditioner::ComputeClusterVisibility(
527 const vector<set<int> >& visibility,
528 vector<set<int> >* cluster_visibility) const {
529 CHECK_NOTNULL(cluster_visibility)->resize(0);
530 cluster_visibility->resize(num_clusters_);
531 for (int i = 0; i < num_blocks_; ++i) {
532 const int cluster_id = cluster_membership_[i];
533 (*cluster_visibility)[cluster_id].insert(visibility[i].begin(),
534 visibility[i].end());
535 }
536}
537
538// Construct a graph whose vertices are the clusters, and the edge
539// weights are the number of 3D points visible to cameras in both the
540// vertices.
541Graph<int>* VisibilityBasedPreconditioner::CreateClusterGraph(
542 const vector<set<int> >& cluster_visibility) const {
543 Graph<int>* cluster_graph = new Graph<int>;
544
545 for (int i = 0; i < num_clusters_; ++i) {
546 cluster_graph->AddVertex(i);
547 }
548
549 for (int i = 0; i < num_clusters_; ++i) {
550 const set<int>& cluster_i = cluster_visibility[i];
551 for (int j = i+1; j < num_clusters_; ++j) {
552 vector<int> intersection;
553 const set<int>& cluster_j = cluster_visibility[j];
554 set_intersection(cluster_i.begin(), cluster_i.end(),
555 cluster_j.begin(), cluster_j.end(),
556 back_inserter(intersection));
557
558 if (intersection.size() > 0) {
559 // Clusters interact strongly when they share a large number
560 // of 3D points. The degree-2 maximum spanning forest
561 // alorithm, iterates on the edges in decreasing order of
562 // their weight, which is the number of points shared by the
563 // two cameras that it connects.
564 cluster_graph->AddEdge(i, j, intersection.size());
565 }
566 }
567 }
568 return cluster_graph;
569}
570
571// Canonical views clustering returns a HashMap from vertices to
572// cluster ids. Convert this into a flat array for quick lookup. It is
573// possible that some of the vertices may not be associated with any
574// cluster. In that case, randomly assign them to one of the clusters.
Sameer Agarwalf06b9fa2013-10-27 21:38:13 -0700575//
576// The cluster ids can be non-contiguous integers. So as we flatten
577// the membership_map, we also map the cluster ids to a contiguous set
578// of integers so that the cluster ids are in [0, num_clusters_).
Keir Mierle8ebb0732012-04-30 23:09:08 -0700579void VisibilityBasedPreconditioner::FlattenMembershipMap(
580 const HashMap<int, int>& membership_map,
581 vector<int>* membership_vector) const {
582 CHECK_NOTNULL(membership_vector)->resize(0);
583 membership_vector->resize(num_blocks_, -1);
Sameer Agarwalf06b9fa2013-10-27 21:38:13 -0700584
585 HashMap<int, int> cluster_id_to_index;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700586 // Iterate over the cluster membership map and update the
587 // cluster_membership_ vector assigning arbitrary cluster ids to
588 // the few cameras that have not been clustered.
589 for (HashMap<int, int>::const_iterator it = membership_map.begin();
590 it != membership_map.end();
591 ++it) {
592 const int camera_id = it->first;
593 int cluster_id = it->second;
594
595 // If the view was not clustered, randomly assign it to one of the
596 // clusters. This preserves the mathematical correctness of the
597 // preconditioner. If there are too many views which are not
598 // clustered, it may lead to some quality degradation though.
599 //
600 // TODO(sameeragarwal): Check if a large number of views have not
601 // been clustered and deal with it?
602 if (cluster_id == -1) {
603 cluster_id = camera_id % num_clusters_;
604 }
605
Sameer Agarwalf06b9fa2013-10-27 21:38:13 -0700606 const int index = FindWithDefault(cluster_id_to_index,
607 cluster_id,
608 cluster_id_to_index.size());
609
610 if (index == cluster_id_to_index.size()) {
611 cluster_id_to_index[cluster_id] = index;
Sameer Agarwalf06b9fa2013-10-27 21:38:13 -0700612 }
613
Sameer Agarwal9ba0b352013-11-05 13:04:56 -0800614 CHECK_LT(index, num_clusters_);
Sameer Agarwalf06b9fa2013-10-27 21:38:13 -0700615 membership_vector->at(camera_id) = index;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700616 }
617}
618
Keir Mierle8ebb0732012-04-30 23:09:08 -0700619} // namespace internal
620} // namespace ceres
Sameer Agarwal8140f0f2013-03-12 09:45:08 -0700621
622#endif // CERES_NO_SUITESPARSE