179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// Ceres Solver - A fast non-linear least squares minimizer
279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// Copyright 2010, 2011, 2012 Google Inc. All rights reserved.
379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// http://code.google.com/p/ceres-solver/
479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez//
579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// Redistribution and use in source and binary forms, with or without
679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// modification, are permitted provided that the following conditions are met:
779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez//
879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// * Redistributions of source code must retain the above copyright notice,
979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez//   this list of conditions and the following disclaimer.
1079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// * Redistributions in binary form must reproduce the above copyright notice,
1179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez//   this list of conditions and the following disclaimer in the documentation
1279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez//   and/or other materials provided with the distribution.
1379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// * Neither the name of Google Inc. nor the names of its contributors may be
1479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez//   used to endorse or promote products derived from this software without
1579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez//   specific prior written permission.
1679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez//
1779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// POSSIBILITY OF SUCH DAMAGE.
2879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez//
2979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// Author: sameeragarwal@google.com (Sameer Agarwal)
3079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
3179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez#include "ceres/partitioned_matrix_view.h"
3279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
3379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez#include <algorithm>
3479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez#include <cstring>
3579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez#include <vector>
3679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez#include "ceres/block_sparse_matrix.h"
3779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez#include "ceres/block_structure.h"
3879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez#include "ceres/internal/eigen.h"
3979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez#include "ceres/small_blas.h"
4079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez#include "glog/logging.h"
4179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
4279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandeznamespace ceres {
4379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandeznamespace internal {
4479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
4579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandeztemplate <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
4679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezPartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>::
4779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezPartitionedMatrixView(
4879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const BlockSparseMatrix& matrix,
4979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    int num_col_blocks_e)
5079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    : matrix_(matrix),
5179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      num_col_blocks_e_(num_col_blocks_e) {
5279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  const CompressedRowBlockStructure* bs = matrix_.block_structure();
5379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  CHECK_NOTNULL(bs);
5479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
5579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  num_col_blocks_f_ = bs->cols.size() - num_col_blocks_e_;
5679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
5779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // Compute the number of row blocks in E. The number of row blocks
5879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // in E maybe less than the number of row blocks in the input matrix
5979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // as some of the row blocks at the bottom may not have any
6079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // e_blocks. For a definition of what an e_block is, please see
6179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // explicit_schur_complement_solver.h
6279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  num_row_blocks_e_ = 0;
6379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  for (int r = 0; r < bs->rows.size(); ++r) {
6479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const vector<Cell>& cells = bs->rows[r].cells;
6579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    if (cells[0].block_id < num_col_blocks_e_) {
6679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      ++num_row_blocks_e_;
6779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    }
6879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  }
6979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
7079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // Compute the number of columns in E and F.
7179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  num_cols_e_ = 0;
7279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  num_cols_f_ = 0;
7379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
7479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  for (int c = 0; c < bs->cols.size(); ++c) {
7579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const Block& block = bs->cols[c];
7679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    if (c < num_col_blocks_e_) {
7779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      num_cols_e_ += block.size;
7879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    } else {
7979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      num_cols_f_ += block.size;
8079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    }
8179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  }
8279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
8379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  CHECK_EQ(num_cols_e_ + num_cols_f_, matrix_.num_cols());
8479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez}
8579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
8679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandeztemplate <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
8779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezPartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>::
8879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez~PartitionedMatrixView() {
8979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez}
9079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
9179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// The next four methods don't seem to be particularly cache
9279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// friendly. This is an artifact of how the BlockStructure of the
9379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// input matrix is constructed. These methods will benefit from
9479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// multithreading as well as improved data layout.
9579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
9679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandeztemplate <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
9779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandezvoid
9879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezPartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>::
9979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezRightMultiplyE(const double* x, double* y) const {
10079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  const CompressedRowBlockStructure* bs = matrix_.block_structure();
10179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
10279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // Iterate over the first num_row_blocks_e_ row blocks, and multiply
10379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // by the first cell in each row block.
10479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  const double* values = matrix_.values();
10579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  for (int r = 0; r < num_row_blocks_e_; ++r) {
10679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const Cell& cell = bs->rows[r].cells[0];
10779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int row_block_pos = bs->rows[r].block.position;
10879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int row_block_size = bs->rows[r].block.size;
10979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int col_block_id = cell.block_id;
11079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int col_block_pos = bs->cols[col_block_id].position;
11179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int col_block_size = bs->cols[col_block_id].size;
11279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    MatrixVectorMultiply<kRowBlockSize, kEBlockSize, 1>(
11379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez        values + cell.position, row_block_size, col_block_size,
11479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez        x + col_block_pos,
11579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez        y + row_block_pos);
11679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  }
11779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez}
11879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
11979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandeztemplate <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
12079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandezvoid
12179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezPartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>::
12279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezRightMultiplyF(const double* x, double* y) const {
12379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  const CompressedRowBlockStructure* bs = matrix_.block_structure();
12479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
12579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // Iterate over row blocks, and if the row block is in E, then
12679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // multiply by all the cells except the first one which is of type
12779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // E. If the row block is not in E (i.e its in the bottom
12879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // num_row_blocks - num_row_blocks_e row blocks), then all the cells
12979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // are of type F and multiply by them all.
13079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  const double* values = matrix_.values();
13179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  for (int r = 0; r < num_row_blocks_e_; ++r) {
13279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int row_block_pos = bs->rows[r].block.position;
13379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int row_block_size = bs->rows[r].block.size;
13479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const vector<Cell>& cells = bs->rows[r].cells;
13579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    for (int c = 1; c < cells.size(); ++c) {
13679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int col_block_id = cells[c].block_id;
13779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int col_block_pos = bs->cols[col_block_id].position;
13879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int col_block_size = bs->cols[col_block_id].size;
13979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      MatrixVectorMultiply<kRowBlockSize, kFBlockSize, 1>(
14079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez          values + cells[c].position, row_block_size, col_block_size,
14179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez          x + col_block_pos - num_cols_e_,
14279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez          y + row_block_pos);
14379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    }
14479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  }
14579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
14679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  for (int r = num_row_blocks_e_; r < bs->rows.size(); ++r) {
14779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int row_block_pos = bs->rows[r].block.position;
14879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int row_block_size = bs->rows[r].block.size;
14979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const vector<Cell>& cells = bs->rows[r].cells;
15079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    for (int c = 0; c < cells.size(); ++c) {
15179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int col_block_id = cells[c].block_id;
15279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int col_block_pos = bs->cols[col_block_id].position;
15379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int col_block_size = bs->cols[col_block_id].size;
15479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      MatrixVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>(
15579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez          values + cells[c].position, row_block_size, col_block_size,
15679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez          x + col_block_pos - num_cols_e_,
15779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez          y + row_block_pos);
15879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    }
15979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  }
16079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez}
16179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
16279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandeztemplate <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
16379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandezvoid
16479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezPartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>::
16579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezLeftMultiplyE(const double* x, double* y) const {
16679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  const CompressedRowBlockStructure* bs = matrix_.block_structure();
16779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
16879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // Iterate over the first num_row_blocks_e_ row blocks, and multiply
16979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // by the first cell in each row block.
17079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  const double* values = matrix_.values();
17179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  for (int r = 0; r < num_row_blocks_e_; ++r) {
17279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const Cell& cell = bs->rows[r].cells[0];
17379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int row_block_pos = bs->rows[r].block.position;
17479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int row_block_size = bs->rows[r].block.size;
17579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int col_block_id = cell.block_id;
17679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int col_block_pos = bs->cols[col_block_id].position;
17779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int col_block_size = bs->cols[col_block_id].size;
17879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    MatrixTransposeVectorMultiply<kRowBlockSize, kEBlockSize, 1>(
17979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez        values + cell.position, row_block_size, col_block_size,
18079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez        x + row_block_pos,
18179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez        y + col_block_pos);
18279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  }
18379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez}
18479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
18579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandeztemplate <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
18679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandezvoid
18779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezPartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>::
18879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezLeftMultiplyF(const double* x, double* y) const {
18979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  const CompressedRowBlockStructure* bs = matrix_.block_structure();
19079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
19179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // Iterate over row blocks, and if the row block is in E, then
19279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // multiply by all the cells except the first one which is of type
19379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // E. If the row block is not in E (i.e its in the bottom
19479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // num_row_blocks - num_row_blocks_e row blocks), then all the cells
19579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // are of type F and multiply by them all.
19679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  const double* values = matrix_.values();
19779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  for (int r = 0; r < num_row_blocks_e_; ++r) {
19879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int row_block_pos = bs->rows[r].block.position;
19979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int row_block_size = bs->rows[r].block.size;
20079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const vector<Cell>& cells = bs->rows[r].cells;
20179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    for (int c = 1; c < cells.size(); ++c) {
20279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int col_block_id = cells[c].block_id;
20379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int col_block_pos = bs->cols[col_block_id].position;
20479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int col_block_size = bs->cols[col_block_id].size;
20579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      MatrixTransposeVectorMultiply<kRowBlockSize, kFBlockSize, 1>(
20679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez        values + cells[c].position, row_block_size, col_block_size,
20779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez        x + row_block_pos,
20879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez        y + col_block_pos - num_cols_e_);
20979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    }
21079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  }
21179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
21279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  for (int r = num_row_blocks_e_; r < bs->rows.size(); ++r) {
21379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int row_block_pos = bs->rows[r].block.position;
21479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int row_block_size = bs->rows[r].block.size;
21579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const vector<Cell>& cells = bs->rows[r].cells;
21679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    for (int c = 0; c < cells.size(); ++c) {
21779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int col_block_id = cells[c].block_id;
21879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int col_block_pos = bs->cols[col_block_id].position;
21979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int col_block_size = bs->cols[col_block_id].size;
22079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      MatrixTransposeVectorMultiply<Eigen::Dynamic, Eigen::Dynamic, 1>(
22179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez        values + cells[c].position, row_block_size, col_block_size,
22279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez        x + row_block_pos,
22379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez        y + col_block_pos - num_cols_e_);
22479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    }
22579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  }
22679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez}
22779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
22879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// Given a range of columns blocks of a matrix m, compute the block
22979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// structure of the block diagonal of the matrix m(:,
23079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// start_col_block:end_col_block)'m(:, start_col_block:end_col_block)
23179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// and return a BlockSparseMatrix with the this block structure. The
23279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// caller owns the result.
23379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandeztemplate <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
23479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezBlockSparseMatrix*
23579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezPartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>::
23679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezCreateBlockDiagonalMatrixLayout(int start_col_block, int end_col_block) const {
23779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  const CompressedRowBlockStructure* bs = matrix_.block_structure();
23879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  CompressedRowBlockStructure* block_diagonal_structure =
23979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      new CompressedRowBlockStructure;
24079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
24179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  int block_position = 0;
24279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  int diagonal_cell_position = 0;
24379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
24479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // Iterate over the column blocks, creating a new diagonal block for
24579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // each column block.
24679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  for (int c = start_col_block; c < end_col_block; ++c) {
24779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const Block& block = bs->cols[c];
24879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    block_diagonal_structure->cols.push_back(Block());
24979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    Block& diagonal_block = block_diagonal_structure->cols.back();
25079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    diagonal_block.size = block.size;
25179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    diagonal_block.position = block_position;
25279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
25379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    block_diagonal_structure->rows.push_back(CompressedRow());
25479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    CompressedRow& row = block_diagonal_structure->rows.back();
25579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    row.block = diagonal_block;
25679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
25779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    row.cells.push_back(Cell());
25879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    Cell& cell = row.cells.back();
25979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    cell.block_id = c - start_col_block;
26079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    cell.position = diagonal_cell_position;
26179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
26279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    block_position += block.size;
26379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    diagonal_cell_position += block.size * block.size;
26479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  }
26579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
26679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // Build a BlockSparseMatrix with the just computed block
26779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  // structure.
26879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  return new BlockSparseMatrix(block_diagonal_structure);
26979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez}
27079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
27179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandeztemplate <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
27279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezBlockSparseMatrix*
27379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezPartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>::
27479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezCreateBlockDiagonalEtE() const {
27579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  BlockSparseMatrix* block_diagonal =
27679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      CreateBlockDiagonalMatrixLayout(0, num_col_blocks_e_);
27779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  UpdateBlockDiagonalEtE(block_diagonal);
27879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  return block_diagonal;
27979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez}
28079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
28179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandeztemplate <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
28279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezBlockSparseMatrix*
28379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezPartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>::
28479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezCreateBlockDiagonalFtF() const {
28579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  BlockSparseMatrix* block_diagonal =
28679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      CreateBlockDiagonalMatrixLayout(
28779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez          num_col_blocks_e_, num_col_blocks_e_ + num_col_blocks_f_);
28879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  UpdateBlockDiagonalFtF(block_diagonal);
28979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  return block_diagonal;
29079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez}
29179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
29279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// Similar to the code in RightMultiplyE, except instead of the matrix
29379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// vector multiply its an outer product.
29479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez//
29579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez//    block_diagonal = block_diagonal(E'E)
29679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez//
29779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandeztemplate <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
29879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandezvoid
29979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezPartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>::
30079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezUpdateBlockDiagonalEtE(
30179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    BlockSparseMatrix* block_diagonal) const {
30279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  const CompressedRowBlockStructure* bs = matrix_.block_structure();
30379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  const CompressedRowBlockStructure* block_diagonal_structure =
30479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      block_diagonal->block_structure();
30579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
30679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  block_diagonal->SetZero();
30779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  const double* values = matrix_.values();
30879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  for (int r = 0; r < num_row_blocks_e_ ; ++r) {
30979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const Cell& cell = bs->rows[r].cells[0];
31079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int row_block_size = bs->rows[r].block.size;
31179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int block_id = cell.block_id;
31279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int col_block_size = bs->cols[block_id].size;
31379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int cell_position =
31479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez        block_diagonal_structure->rows[block_id].cells[0].position;
31579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
31679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    MatrixTransposeMatrixMultiply
31779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez        <kRowBlockSize, kEBlockSize, kRowBlockSize, kEBlockSize, 1>(
31879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez            values + cell.position, row_block_size, col_block_size,
31979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez            values + cell.position, row_block_size, col_block_size,
32079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez            block_diagonal->mutable_values() + cell_position,
32179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez            0, 0, col_block_size, col_block_size);
32279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  }
32379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez}
32479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
32579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// Similar to the code in RightMultiplyF, except instead of the matrix
32679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez// vector multiply its an outer product.
32779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez//
32879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez//   block_diagonal = block_diagonal(F'F)
32979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez//
33079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandeztemplate <int kRowBlockSize, int kEBlockSize, int kFBlockSize>
33179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandezvoid
33279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezPartitionedMatrixView<kRowBlockSize, kEBlockSize, kFBlockSize>::
33379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos HernandezUpdateBlockDiagonalFtF(BlockSparseMatrix* block_diagonal) const {
33479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  const CompressedRowBlockStructure* bs = matrix_.block_structure();
33579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  const CompressedRowBlockStructure* block_diagonal_structure =
33679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      block_diagonal->block_structure();
33779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
33879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  block_diagonal->SetZero();
33979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  const double* values = matrix_.values();
34079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  for (int r = 0; r < num_row_blocks_e_; ++r) {
34179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int row_block_size = bs->rows[r].block.size;
34279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const vector<Cell>& cells = bs->rows[r].cells;
34379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    for (int c = 1; c < cells.size(); ++c) {
34479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int col_block_id = cells[c].block_id;
34579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int col_block_size = bs->cols[col_block_id].size;
34679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int diagonal_block_id = col_block_id - num_col_blocks_e_;
34779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int cell_position =
34879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez          block_diagonal_structure->rows[diagonal_block_id].cells[0].position;
34979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
35079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      MatrixTransposeMatrixMultiply
35179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez          <kRowBlockSize, kFBlockSize, kRowBlockSize, kFBlockSize, 1>(
35279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez              values + cells[c].position, row_block_size, col_block_size,
35379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez              values + cells[c].position, row_block_size, col_block_size,
35479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez              block_diagonal->mutable_values() + cell_position,
35579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez              0, 0, col_block_size, col_block_size);
35679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    }
35779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  }
35879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
35979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  for (int r = num_row_blocks_e_; r < bs->rows.size(); ++r) {
36079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const int row_block_size = bs->rows[r].block.size;
36179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    const vector<Cell>& cells = bs->rows[r].cells;
36279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    for (int c = 0; c < cells.size(); ++c) {
36379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int col_block_id = cells[c].block_id;
36479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int col_block_size = bs->cols[col_block_id].size;
36579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int diagonal_block_id = col_block_id - num_col_blocks_e_;
36679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      const int cell_position =
36779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez          block_diagonal_structure->rows[diagonal_block_id].cells[0].position;
36879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
36979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez      MatrixTransposeMatrixMultiply
37079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez          <Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic, Eigen::Dynamic, 1>(
37179397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez              values + cells[c].position, row_block_size, col_block_size,
37279397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez              values + cells[c].position, row_block_size, col_block_size,
37379397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez              block_diagonal->mutable_values() + cell_position,
37479397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez              0, 0, col_block_size, col_block_size);
37579397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez    }
37679397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez  }
37779397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez}
37879397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez
37979397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez}  // namespace internal
38079397c21138f54fcff6ec067b44b847f1f7e0e98Carlos Hernandez}  // namespace ceres
381