RainerKuemmerle / g2o

g2o: A General Framework for Graph Optimization
3.04k stars 1.1k forks source link

Linking Issue on OSx using CMake #152

Open mehoggan opened 7 years ago

mehoggan commented 7 years ago

Building my project using cmake in the following manner:

/Applications/CLion.app/Contents/bin/cmake/bin/cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_VERBOSE_MAKEFILE=On -DCMAKE_BUILD_TYPE=Release -G "CodeBlocks - Unix Makefiles" /Users/mhoggan/Development/bluenote/image_pose_adjustment/image_pose_adjustment/native/graph

I get the following build error when my library goes to link against g2o.

cd /Users/mhoggan/Development/bluenote/image_pose_adjustment/image_pose_adjustment/native/graph/cmake-build-debug/src && /Applications/CLion.app/Contents/bin/cmake/bin/cmake -E cmake_link_script CMakeFiles/graph_optimization.dir/link.txt --verbose=1
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++    -std=c++11 -Wall -Werror -O3 -DNDEBUG -Wl,-search_paths_first -Wl,-headerpad_max_install_names  CMakeFiles/graph_optimization.dir/main.cpp.o CMakeFiles/graph_optimization.dir/BundleRigidGraphOptimizer.cpp.o CMakeFiles/graph_optimization.dir/FlexibleGraphOptimizer.cpp.o CMakeFiles/graph_optimization.dir/LoopClosure.cpp.o CMakeFiles/graph_optimization.dir/PoseJsonReader.cpp.o  -o graph_optimization  -L/Users/mhoggan/Development/bluenote/image_pose_adjustment/image_pose_adjustment/native/graph/cmake-build-debug/g2o-src/lib  /usr/local/lib/libboost_filesystem-mt.a /usr/local/lib/libboost_system-mt.a -lg2o_cli -lg2o_core -lg2o_interface -lg2o_parser -lg2o_solver_csparse -lg2o_solver_dense -lg2o_solver_pcg -lg2o_stuff -lg2o_types_icp -lg2o_types_sba -lg2o_types_sim3 -lg2o_types_slam2d -lg2o_types_slam3d -lg2o_csparse_extension -lcsparse -ljsoncpp -Wl,-rpath,/Users/mhoggan/Development/bluenote/image_pose_adjustment/image_pose_adjustment/native/graph/cmake-build-debug/g2o-src/lib 
Undefined symbols for architecture x86_64:
  "g2o::csparse_extension::cs_cholsolsymb(cs_sparse const*, double*, cs_symbolic const*, double*, int*)", referenced from:
      g2o::LinearSolverCSparse<Eigen::Matrix<double, -1, -1, 0, -1, -1> >::solve(g2o::SparseBlockMatrix<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, double*, double*) in LoopClosure.cpp.o
  "g2o::csparse_extension::writeCs2Octave(char const*, cs_sparse const*, bool)", referenced from:
      g2o::LinearSolverCSparse<Eigen::Matrix<double, -1, -1, 0, -1, -1> >::solve(g2o::SparseBlockMatrix<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, double*, double*) in LoopClosure.cpp.o
  "g2o::csparse_extension::cs_chol_workspace(cs_sparse const*, cs_symbolic const*, int*, double*)", referenced from:
      g2o::LinearSolverCSparse<Eigen::Matrix<double, -1, -1, 0, -1, -1> >::solveBlocks(double**&, g2o::SparseBlockMatrix<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&) in LoopClosure.cpp.o
      g2o::LinearSolverCSparse<Eigen::Matrix<double, -1, -1, 0, -1, -1> >::solvePattern(g2o::SparseBlockMatrix<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, std::__1::vector<std::__1::pair<int, int>, std::__1::allocator<std::__1::pair<int, int> > > const&, g2o::SparseBlockMatrix<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&) in LoopClosure.cpp.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

However, the nm utility shows the symbols are present:

mhoggan-C02S81PRG8WM:graph mhoggan$ nm /Users/mhoggan/Development/bluenote/image_pose_adjustment/image_pose_adjustment/native/graph/cmake-build-debug/g2o-src/lib/libg2o_csparse_extension.dylib | grep cs_cholsolsymb
0000000000002120 T __ZN3g2o17csparse_extension14cs_cholsolsymbEPK12cs_di_sparsePdPK14cs_di_symbolicS4_Pi
mhoggan-C02S81PRG8WM:graph mhoggan$ nm /Users/mhoggan/Development/bluenote/image_pose_adjustment/image_pose_adjustment/native/graph/cmake-build-debug/g2o-src/lib/libg2o_csparse_extension.dylib | grep writeCs2Octave
00000000000027d0 T __ZN3g2o17csparse_extension14writeCs2OctaveEPKcPK12cs_di_sparseb
mhoggan-C02S81PRG8WM:graph mhoggan$ nm /Users/mhoggan/Development/bluenote/image_pose_adjustment/image_pose_adjustment/native/graph/cmake-build-debug/g2o-src/lib/libg2o_csparse_extension.dylib | grep cs_chol_workspace
0000000000002210 T __ZN3g2o17csparse_extension17cs_chol_workspaceEPK12cs_di_sparsePK14cs_di_symbolicPiPd
sergiud commented 7 years ago

I guess you compiled only a 32-bit version of g2o, but trying to link a 64-bit target to it. This is from your log:

ld: symbol(s) not found for architecture x86_64
mehoggan commented 7 years ago

From the following:

mhoggan-C02S81PRG8WM:cmake-build-debug mhoggan$ file ./g2o-src/lib/libg2o_csparse_extension.dylib
./g2o-src/lib/libg2o_csparse_extension.dylib: Mach-O 64-bit dynamically linked shared library x86_64

I would assume that last comment to be incorrect.

sergiud commented 7 years ago

Is the C++ standard being used by the compiler (i.e., the -std=c++xx compile flag) consistent across the targets?

mehoggan commented 7 years ago

Outside of g2o there is only one other target, a target added using add_executable and it has -std=c++11 set in CXX_FLAGS. As seen in the following compiler line:

cd /Users/mhoggan/Development/bluenote/image_pose_adjustment/image_pose_adjustment/native/graph/cmake-build-debug/src && /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++    -I/usr/local/include -I/usr/local/include/eigen3 -I/Users/mhoggan/Development/bluenote/image_pose_adjustment/image_pose_adjustment/native/graph/cmake-build-debug/g2o-build -I/Users/mhoggan/Development/bluenote/image_pose_adjustment/image_pose_adjustment/native/graph/cmake-build-debug/g2o-src -I/Users/mhoggan/Development/bluenote/image_pose_adjustment/image_pose_adjustment/native/graph/cmake-build-debug/g2o-src/EXTERNAL/csparse -I/Users/mhoggan/Development/bluenote/image_pose_adjustment/image_pose_adjustment/native/graph/include  -std=c++11 -Wall -Werror -O3 -DNDEBUG   -o CMakeFiles/graph_optimization.dir/PoseJsonReader.cpp.o -c /Users/mhoggan/Development/bluenote/image_pose_adjustment/image_pose_adjustment/native/graph/src/PoseJsonReader.cpp

This is the line right before the executable goes to link against g2o.

Here is the CMakeLists.txt file used.

cmake_minimum_required(VERSION 3.2.2)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Werror")
set(EXE_NAME graph_optimization)
set(SOURCE_FILES )

add_executable(${EXE_NAME} "main.cpp"
    "BundleRigidGraphOptimizer.cpp"
    "FlexibleGraphOptimizer.cpp"
    "LoopClosure.cpp"
    "PoseJsonReader.cpp")

set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)

find_package(Boost REQUIRED COMPONENTS filesystem system)
find_package(Eigen3 REQUIRED)
find_package(BLAS REQUIRED)
find_package(LAPACK REQUIRED)

include_directories(${Boost_INCLUDE_DIRS})
include_directories(${EIGEN3_INCLUDE_DIR})
include_directories(${G2O_INCLUDE_DIR})
include_directories("${CMAKE_SOURCE_DIR}/include")
include_directories("/usr/local/include")

link_directories("/usr/local/lib")
target_link_libraries(${EXE_NAME} ${Boost_LIBRARIES}
                                  g2o_cli
                                  g2o_core
                                  g2o_interface
                                  g2o_parser
                                  g2o_solver_csparse
                                  g2o_solver_dense
                                  g2o_solver_pcg
                                  g2o_stuff
                                  g2o_types_icp
                                  g2o_types_sba
                                  g2o_types_sim3
                                  g2o_types_slam2d
                                  g2o_types_slam3d
                                  g2o_csparse_extension
                                  sparse
                                  jsoncpp)
mehoggan commented 7 years ago

This only happens on a El Capitan OSx. But is a result of including linear_solver_csparse.h from a executable linking against g2o. This does not happen on debian jessie using g++ (Debian 4.9.2-10) 4.9.2.

rancheng commented 7 years ago

Hi, I just figured out this problem, by adding this line in myCMakeLists.txt file:

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libstdc++ -lstdc++ -std=c++11")

Please note: This is a Fu*king typical problem on Clang, and you can search the funning history with google: The libstdC++ vs libC++ issue!

hope you guys work well, good luck.

rancheng commented 7 years ago

Another issue you guys may face is:

fatal error: 'type_traits' file not found

and this is caused by using libstdc++, and libstdc++ does not support c++11 traits, in another word: this is a dead end on Mac..

One more thing: Just don't waste your time on this problem, don't try your hard time frustrating on this platform, install a VM and run a linux, then find other problems to hack on.

mehoggan commented 7 years ago

Unfortunately, moving off mac for developers is not an option. All developers in our office use macs for 95% of their local development. We deploy to production using docker containers created from local debian VM's.

Also I am now starting to see this error on debian Jessie. I will post my compilation errors in a bit.

mehoggan commented 7 years ago

uber@ford-ct-5:~/graph-optimization/debian$ cat /etc/*release* PRETTY_NAME="Debian GNU/Linux 8 (jessie)" NAME="Debian GNU/Linux" VERSION_ID="8" VERSION="8 (jessie)" ID=debian HOME_URL="http://www.debian.org/" SUPPORT_URL="http://www.debian.org/support" BUG_REPORT_URL="https://bugs.debian.org/" uber@ford-ct-5:~/graph-optimization/debian$ http://www.target.com/p/promark-photo-tripod/-/A-50626812^C uber@ford-ct-5:~/graph-optimization/debian$ /usr/bin/c++ --version c++ (Debian 4.9.2-10) 4.9.2 Copyright (C) 2014 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Here are the linker errors on Linux:

../lib/libgraph_optimization.so: undefined reference tog2o::csparse_extension::cs_chol_workspace(cs_sparse const, cs_symbolic const, int, double)' ../lib/libgraph_optimization.so: undefined reference to g2o::csparse_extension::writeCs2Octave(char const*, cs_sparse const*, bool)' ../lib/libgraph_optimization.so: undefined reference tog2o::csparse_extension::cs_cholsolsymb(cs_sparse const, double, cs_symbolic const, double, int*)'`

I am going to work on a patch since this is now blocking us from using your library.

mehoggan commented 7 years ago

For what its worth. When using/linking against the library in question from g2o and using the LinearSolverCSparse templated type. Explicit Template Instantiation (ETI) from a cpp file for linear_solver_csparse.h solves the problem. Below is the patch to apply to g2o if you are having the same problem. Because it uses ETI it is not a solution for everyone unless we new the set of all MatrixType(s) that would be used with the LinearSolverCSparse. This does appear to be a problem with certain tool chains. The patch below works on g++-4.9 in debian but was not tested in MacOSX. I would assume that it would work there since forcing the type to be compiled into the library insure the symbols are placed in the shared library.

From cc703029f3ca366906f05748171235303ace4336 Mon Sep 17 00:00:00 2001
From: Matthew Hoggan <mhoggan@uber.com>
Date: Fri, 28 Apr 2017 21:22:18 +0000
Subject: [PATCH] Explicit template instantiation.

---
 g2o/solvers/csparse/CMakeLists.txt            |   3 +-
 g2o/solvers/csparse/linear_solver_csparse.cpp | 335 ++++++++++++++++++++++++++
 g2o/solvers/csparse/linear_solver_csparse.h   | 288 ++--------------------
 3 files changed, 357 insertions(+), 269 deletions(-)
 create mode 100644 g2o/solvers/csparse/linear_solver_csparse.cpp

diff --git a/g2o/solvers/csparse/CMakeLists.txt b/g2o/solvers/csparse/CMakeLists.txt
index 20728d1..9861be5 100644
--- a/g2o/solvers/csparse/CMakeLists.txt
+++ b/g2o/solvers/csparse/CMakeLists.txt
@@ -15,14 +15,13 @@ ENDIF()

 ADD_LIBRARY(solver_csparse ${G2O_LIB_TYPE}
   solver_csparse.cpp
-  linear_solver_csparse.h
+  linear_solver_csparse.cpp
   g2o_csparse_api.h
 )
 SET_TARGET_PROPERTIES(solver_csparse PROPERTIES OUTPUT_NAME ${LIB_PREFIX}solver_csparse)

 TARGET_LINK_LIBRARIES(solver_csparse csparse_extension core)

-
 INSTALL(TARGETS solver_csparse csparse_extension
   RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
   LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/lib
diff --git a/g2o/solvers/csparse/linear_solver_csparse.cpp b/g2o/solvers/csparse/linear_solver_csparse.cpp
new file mode 100644
index 0000000..7266a27
--- /dev/null
+++ b/g2o/solvers/csparse/linear_solver_csparse.cpp
@@ -0,0 +1,335 @@
+// g2o - General Graph Optimization
+// Copyright (C) 2011 R. Kuemmerle, G. Grisetti, W. Burgard
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright notice,
+//   this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+//   notice, this list of conditions and the following disclaimer in the
+//   documentation and/or other materials provided with the distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "g2o/solvers/csparse/linear_solver_csparse.h"
+
+#include "g2o/core/block_solver.h"
+
+#include <iostream>
+
+namespace g2o {
+
+CSparseExt::CSparseExt()
+{
+  nzmax = 0;
+  m = 0;
+  n = 0;
+  p = 0;
+  i = 0;
+  x = 0;
+  nz = 0;
+  columnsAllocated = 0;
+}
+
+CSparseExt::~CSparseExt()
+{
+  delete[] p;
+  delete[] i;
+  delete[] x;
+}
+
+template <typename MatrixType>
+LinearSolverCSparse<MatrixType>::LinearSolverCSparse() :
+  LinearSolverCCS<MatrixType>()
+{
+  _symbolicDecomposition = 0;
+  _csWorkspaceSize = -1;
+  _csWorkspace = 0;
+  _csIntWorkspace = 0;
+  _ccsA = new CSparseExt;
+  _blockOrdering = true;
+  _writeDebug = true;
+}
+
+template <typename MatrixType>
+LinearSolverCSparse<MatrixType>::~LinearSolverCSparse()
+{
+  if (_symbolicDecomposition) {
+    cs_sfree(_symbolicDecomposition);
+    _symbolicDecomposition = 0;
+  }
+  delete[] _csWorkspace; _csWorkspace = 0;
+  delete[] _csIntWorkspace; _csIntWorkspace = 0;
+  delete _ccsA;
+}
+
+template <typename MatrixType>
+bool LinearSolverCSparse<MatrixType>::init()
+{
+  if (_symbolicDecomposition) {
+    cs_sfree(_symbolicDecomposition);
+    _symbolicDecomposition = 0;
+  }
+  return true;
+}
+
+template <typename MatrixType>
+bool LinearSolverCSparse<MatrixType>::solve(const SparseBlockMatrix<MatrixType>& A, double* x, double* b)
+{
+  fillCSparse(A, _symbolicDecomposition != 0);
+  // perform symbolic cholesky once
+  if (_symbolicDecomposition == 0) {
+    computeSymbolicDecomposition(A);
+  }
+  // re-allocate the temporary workspace for cholesky
+  if (_csWorkspaceSize < _ccsA->n) {
+    _csWorkspaceSize = 2 * _ccsA->n;
+    delete[] _csWorkspace;
+    _csWorkspace = new double[_csWorkspaceSize];
+    delete[] _csIntWorkspace;
+    _csIntWorkspace = new int[2*_csWorkspaceSize];
+  }
+
+  double t=get_monotonic_time();
+  // _x = _b for calling csparse
+  if (x != b)
+    memcpy(x, b, _ccsA->n * sizeof(double));
+  int ok = csparse_extension::cs_cholsolsymb(_ccsA, x, _symbolicDecomposition, _csWorkspace, _csIntWorkspace);
+  if (! ok) {
+    if (_writeDebug) {
+      std::cerr << "Cholesky failure, writing debug.txt (Hessian loadable by Octave)" << std::endl;
+      csparse_extension::writeCs2Octave("debug.txt", _ccsA, true);
+    }
+    return false;
+  }
+
+  G2OBatchStatistics* globalStats = G2OBatchStatistics::globalStats();
+  if (globalStats){
+    globalStats->timeNumericDecomposition = get_monotonic_time() - t;
+    globalStats->choleskyNNZ = static_cast<size_t>(_symbolicDecomposition->lnz);
+  }
+
+  return ok != 0;
+}
+
+template <typename MatrixType>
+bool LinearSolverCSparse<MatrixType>::solveBlocks(double**& blocks, const SparseBlockMatrix<MatrixType>& A) {
+  fillCSparse(A, _symbolicDecomposition != 0);
+  // perform symbolic cholesky once
+  if (_symbolicDecomposition == 0) {
+    computeSymbolicDecomposition(A);
+    assert(_symbolicDecomposition && "Symbolic cholesky failed");
+  }
+  // re-allocate the temporary workspace for cholesky
+  if (_csWorkspaceSize < _ccsA->n) {
+    _csWorkspaceSize = 2 * _ccsA->n;
+    delete[] _csWorkspace;
+    _csWorkspace = new double[_csWorkspaceSize];
+    delete[] _csIntWorkspace;
+    _csIntWorkspace = new int[2*_csWorkspaceSize];
+  }
+
+  if (! blocks){
+    blocks=new double*[A.rows()];
+    double **block=blocks;
+    for (size_t i=0; i < A.rowBlockIndices().size(); ++i){
+      int dim = A.rowsOfBlock(i) * A.colsOfBlock(i);
+      *block = new double [dim];
+      block++;
+    }
+  }
+
+  int ok = 1;
+  csn* numericCholesky = csparse_extension::cs_chol_workspace(_ccsA, _symbolicDecomposition, _csIntWorkspace, _csWorkspace);
+  if (numericCholesky) {
+    MarginalCovarianceCholesky mcc;
+    mcc.setCholeskyFactor(_ccsA->n, numericCholesky->L->p, numericCholesky->L->i, numericCholesky->L->x, _symbolicDecomposition->pinv);
+    mcc.computeCovariance(blocks, A.rowBlockIndices());
+    cs_nfree(numericCholesky);
+  } else {
+    ok = 0;
+    std::cerr << "inverse fail (numeric decomposition)" << std::endl;
+  }
+
+  G2OBatchStatistics* globalStats = G2OBatchStatistics::globalStats();
+  if (globalStats){
+    globalStats->choleskyNNZ = static_cast<size_t>(_symbolicDecomposition->lnz);
+  }
+
+  return ok != 0;
+}
+
+template <typename MatrixType>
+bool LinearSolverCSparse<MatrixType>::solvePattern(SparseBlockMatrix<MatrixXD>& spinv, const std::vector<std::pair<int, int> >& blockIndices,
+  const SparseBlockMatrix<MatrixType>& A) {
+  fillCSparse(A, _symbolicDecomposition != 0);
+  // perform symbolic cholesky once
+  if (_symbolicDecomposition == 0) {
+    computeSymbolicDecomposition(A);
+    assert(_symbolicDecomposition && "Symbolic cholesky failed");
+  }
+  // re-allocate the temporary workspace for cholesky
+  if (_csWorkspaceSize < _ccsA->n) {
+    _csWorkspaceSize = 2 * _ccsA->n;
+    delete[] _csWorkspace;
+    _csWorkspace = new double[_csWorkspaceSize];
+    delete[] _csIntWorkspace;
+    _csIntWorkspace = new int[2*_csWorkspaceSize];
+  }
+
+
+  int ok = 1;
+  csn* numericCholesky = csparse_extension::cs_chol_workspace(_ccsA, _symbolicDecomposition, _csIntWorkspace, _csWorkspace);
+  if (numericCholesky) {
+    MarginalCovarianceCholesky mcc;
+    mcc.setCholeskyFactor(_ccsA->n, numericCholesky->L->p, numericCholesky->L->i, numericCholesky->L->x, _symbolicDecomposition->pinv);
+    mcc.computeCovariance(spinv, A.rowBlockIndices(), blockIndices);
+    cs_nfree(numericCholesky);
+  } else {
+    ok = 0;
+    std::cerr << "inverse fail (numeric decomposition)" << std::endl;
+  }
+
+  G2OBatchStatistics* globalStats = G2OBatchStatistics::globalStats();
+  if (globalStats){
+    globalStats->choleskyNNZ = static_cast<size_t>(_symbolicDecomposition->lnz);
+  }
+
+  return ok != 0;
+}
+
+//! do the AMD ordering on the blocks or on the scalar matrix
+template <typename MatrixType>
+bool LinearSolverCSparse<MatrixType>::blockOrdering() const { return _blockOrdering;}
+
+template <typename MatrixType>
+void LinearSolverCSparse<MatrixType>::setBlockOrdering(bool blockOrdering) { _blockOrdering = blockOrdering;}
+
+//! write a debug dump of the system matrix if it is not SPD in solve
+template <typename MatrixType>
+bool LinearSolverCSparse<MatrixType>::writeDebug() const { return _writeDebug;}
+
+template <typename MatrixType>
+void LinearSolverCSparse<MatrixType>::setWriteDebug(bool b) { _writeDebug = b;}
+
+template <typename MatrixType>
+void LinearSolverCSparse<MatrixType>::computeSymbolicDecomposition(const SparseBlockMatrix<MatrixType>& A)
+{
+  double t=get_monotonic_time();
+  if (! _blockOrdering) {
+    _symbolicDecomposition = cs_schol (1, _ccsA) ;
+  } else {
+    A.fillBlockStructure(_matrixStructure);
+
+    // prepare block structure for the CSparse call
+    cs auxBlock;
+    auxBlock.nzmax = _matrixStructure.nzMax();
+    auxBlock.m = auxBlock.n = _matrixStructure.n;
+    auxBlock.p = _matrixStructure.Ap;
+    auxBlock.i = _matrixStructure.Aii;
+    auxBlock.x = NULL; // no values
+    auxBlock.nz = -1; // CCS format
+
+    // AMD ordering on the block structure
+    const int& n = _ccsA->n;
+    int* P = cs_amd(1, &auxBlock);
+
+    // blow up the permutation to the scalar matrix
+    if (_scalarPermutation.size() == 0)
+      _scalarPermutation.resize(n);
+    if (_scalarPermutation.size() < n)
+      _scalarPermutation.resize(2*n);
+    size_t scalarIdx = 0;
+    for (int i = 0; i < _matrixStructure.n; ++i) {
+      const int& p = P[i];
+      int base  = A.colBaseOfBlock(p);
+      int nCols = A.colsOfBlock(p);
+      for (int j = 0; j < nCols; ++j)
+        _scalarPermutation(scalarIdx++) = base++;
+    }
+    assert((int)scalarIdx == n);
+    cs_free(P);
+
+    // apply the scalar permutation to finish symbolic decomposition
+    _symbolicDecomposition = (css*) cs_calloc(1, sizeof(css));       /* allocate result S */
+    _symbolicDecomposition->pinv = cs_pinv(_scalarPermutation.data(), n);
+    cs* C = cs_symperm(_ccsA, _symbolicDecomposition->pinv, 0);
+    _symbolicDecomposition->parent = cs_etree(C, 0);
+    int* post = cs_post(_symbolicDecomposition->parent, n);
+    int* c = cs_counts(C, _symbolicDecomposition->parent, post, 0);
+    cs_free(post);
+    cs_spfree(C);
+    _symbolicDecomposition->cp = (int*) cs_malloc(n+1, sizeof(int));
+    _symbolicDecomposition->unz = _symbolicDecomposition->lnz = cs_cumsum(_symbolicDecomposition->cp, c, n);
+    cs_free(c);
+    if (_symbolicDecomposition->lnz < 0) {
+      cs_sfree(_symbolicDecomposition);
+      _symbolicDecomposition = 0;
+    }
+
+  }
+  G2OBatchStatistics* globalStats = G2OBatchStatistics::globalStats();
+  if (globalStats){
+    globalStats->timeSymbolicDecomposition = get_monotonic_time() - t;
+  }
+
+  /* std::cerr << "# Number of nonzeros in L: " << (int)_symbolicDecomposition->lnz << " by " */
+  /*   << (_blockOrdering ? "block" : "scalar") << " AMD ordering " << std::endl; */
+}
+
+template <typename MatrixType>
+void LinearSolverCSparse<MatrixType>::fillCSparse(const SparseBlockMatrix<MatrixType>& A, bool onlyValues)
+{
+  if (! onlyValues)
+    this->initMatrixStructure(A);
+  int m = A.rows();
+  int n = A.cols();
+  assert(m > 0 && n > 0 && "Hessian has 0 rows/cols");
+
+  if (_ccsA->columnsAllocated < n) {
+    _ccsA->columnsAllocated = _ccsA->columnsAllocated == 0 ? n : 2 * n; // pre-allocate more space if re-allocating
+    delete[] _ccsA->p;
+    _ccsA->p = new int[_ccsA->columnsAllocated+1];
+  }
+
+  if (! onlyValues) {
+    int nzmax = A.nonZeros();
+    if (_ccsA->nzmax < nzmax) {
+      _ccsA->nzmax = _ccsA->nzmax == 0 ? nzmax : 2 * nzmax; // pre-allocate more space if re-allocating
+      delete[] _ccsA->x;
+      delete[] _ccsA->i;
+      _ccsA->i = new int[_ccsA->nzmax];
+      _ccsA->x = new double[_ccsA->nzmax];
+    }
+  }
+  _ccsA->m = m;
+  _ccsA->n = n;
+
+  if (onlyValues) {
+    this->_ccsMatrix->fillCCS(_ccsA->x, true);
+  } else {
+    int nz = this->_ccsMatrix->fillCCS(_ccsA->p, _ccsA->i, _ccsA->x, true); (void) nz;
+    assert(nz <= _ccsA->nzmax);
+  }
+  _ccsA->nz=-1; // tag as CCS formatted matrix
+}
+
+} // end namespace
+
+template class g2o::LinearSolverCSparse<g2o::BlockSolver<g2o::BlockSolverTraits<-1, -1>>::PoseMatrixType>;
+template class g2o::LinearSolverCSparse<Eigen::Matrix<double, 3, 3, 0, 3, 3>>;
+template class g2o::LinearSolverCSparse<Eigen::Matrix<double, 6, 6, 0, 6, 6>>;
+template class g2o::LinearSolverCSparse<Eigen::Matrix<double, 7, 7, 0, 7, 7>>;
diff --git a/g2o/solvers/csparse/linear_solver_csparse.h b/g2o/solvers/csparse/linear_solver_csparse.h
index 92b3498..869f5e8 100644
--- a/g2o/solvers/csparse/linear_solver_csparse.h
+++ b/g2o/solvers/csparse/linear_solver_csparse.h
@@ -44,193 +44,43 @@ namespace g2o {
  */
 struct G2O_SOLVER_CSPARSE_API CSparseExt : public cs
 {
-  CSparseExt()
-  {
-    nzmax = 0;
-    m = 0;
-    n = 0;
-    p = 0;
-    i = 0;
-    x = 0;
-    nz = 0;
-    columnsAllocated = 0;
-  }
-  ~CSparseExt()
-  {
-    delete[] p;
-    delete[] i;
-    delete[] x;
-  }
+  CSparseExt();
+
+  ~CSparseExt();
+
   int columnsAllocated;
 };

 /**
- * \brief linear solver which uses CSparse
+ * \brief linear solver which uses CSparse. Supported MatrixType(s)
+ * are listed in the .cpp for this templated type as explicit
+ * template instantiation.
  */
 template <typename MatrixType>
 class LinearSolverCSparse : public LinearSolverCCS<MatrixType>
 {
   public:
-    LinearSolverCSparse() :
-      LinearSolverCCS<MatrixType>()
-    {
-      _symbolicDecomposition = 0;
-      _csWorkspaceSize = -1;
-      _csWorkspace = 0;
-      _csIntWorkspace = 0;
-      _ccsA = new CSparseExt;
-      _blockOrdering = true;
-      _writeDebug = true;
-    }
-
-    virtual ~LinearSolverCSparse()
-    {
-      if (_symbolicDecomposition) {
-        cs_sfree(_symbolicDecomposition);
-        _symbolicDecomposition = 0;
-      }
-      delete[] _csWorkspace; _csWorkspace = 0;
-      delete[] _csIntWorkspace; _csIntWorkspace = 0;
-      delete _ccsA;
-    }
-
-    virtual bool init()
-    {
-      if (_symbolicDecomposition) {
-        cs_sfree(_symbolicDecomposition);
-        _symbolicDecomposition = 0;
-      }
-      return true;
-    }
-
-    bool solve(const SparseBlockMatrix<MatrixType>& A, double* x, double* b)
-    {
-      fillCSparse(A, _symbolicDecomposition != 0);
-      // perform symbolic cholesky once
-      if (_symbolicDecomposition == 0) {
-        computeSymbolicDecomposition(A);
-      }
-      // re-allocate the temporary workspace for cholesky
-      if (_csWorkspaceSize < _ccsA->n) {
-        _csWorkspaceSize = 2 * _ccsA->n;
-        delete[] _csWorkspace;
-        _csWorkspace = new double[_csWorkspaceSize];
-        delete[] _csIntWorkspace;
-        _csIntWorkspace = new int[2*_csWorkspaceSize];
-      }
-
-      double t=get_monotonic_time();
-      // _x = _b for calling csparse
-      if (x != b)
-        memcpy(x, b, _ccsA->n * sizeof(double));
-      int ok = csparse_extension::cs_cholsolsymb(_ccsA, x, _symbolicDecomposition, _csWorkspace, _csIntWorkspace);
-      if (! ok) {
-        if (_writeDebug) {
-          std::cerr << "Cholesky failure, writing debug.txt (Hessian loadable by Octave)" << std::endl;
-          csparse_extension::writeCs2Octave("debug.txt", _ccsA, true);
-        }
-        return false;
-      }
-
-      G2OBatchStatistics* globalStats = G2OBatchStatistics::globalStats();
-      if (globalStats){
-        globalStats->timeNumericDecomposition = get_monotonic_time() - t;
-        globalStats->choleskyNNZ = static_cast<size_t>(_symbolicDecomposition->lnz);
-      }
-
-      return ok != 0;
-    }
-
-    bool solveBlocks(double**& blocks, const SparseBlockMatrix<MatrixType>& A) {
-      fillCSparse(A, _symbolicDecomposition != 0);
-      // perform symbolic cholesky once
-      if (_symbolicDecomposition == 0) {
-        computeSymbolicDecomposition(A);
-        assert(_symbolicDecomposition && "Symbolic cholesky failed");
-      }
-      // re-allocate the temporary workspace for cholesky
-      if (_csWorkspaceSize < _ccsA->n) {
-        _csWorkspaceSize = 2 * _ccsA->n;
-        delete[] _csWorkspace;
-        _csWorkspace = new double[_csWorkspaceSize];
-        delete[] _csIntWorkspace;
-        _csIntWorkspace = new int[2*_csWorkspaceSize];
-      }
-
-      if (! blocks){
-        blocks=new double*[A.rows()];
-        double **block=blocks;
-        for (size_t i=0; i < A.rowBlockIndices().size(); ++i){
-          int dim = A.rowsOfBlock(i) * A.colsOfBlock(i);
-          *block = new double [dim];
-          block++;
-        }
-      }
-
-      int ok = 1;
-      csn* numericCholesky = csparse_extension::cs_chol_workspace(_ccsA, _symbolicDecomposition, _csIntWorkspace, _csWorkspace);
-      if (numericCholesky) {
-        MarginalCovarianceCholesky mcc;
-        mcc.setCholeskyFactor(_ccsA->n, numericCholesky->L->p, numericCholesky->L->i, numericCholesky->L->x, _symbolicDecomposition->pinv);
-        mcc.computeCovariance(blocks, A.rowBlockIndices());
-        cs_nfree(numericCholesky);
-      } else {
-        ok = 0;
-        std::cerr << "inverse fail (numeric decomposition)" << std::endl;
-      }
+    LinearSolverCSparse();

-      G2OBatchStatistics* globalStats = G2OBatchStatistics::globalStats();
-      if (globalStats){
-        globalStats->choleskyNNZ = static_cast<size_t>(_symbolicDecomposition->lnz);
-      }
+    virtual ~LinearSolverCSparse();

-      return ok != 0;
-    }
+    virtual bool init();

-    virtual bool solvePattern(SparseBlockMatrix<MatrixXD>& spinv, const std::vector<std::pair<int, int> >& blockIndices, const SparseBlockMatrix<MatrixType>& A) {
-      fillCSparse(A, _symbolicDecomposition != 0);
-      // perform symbolic cholesky once
-      if (_symbolicDecomposition == 0) {
-        computeSymbolicDecomposition(A);
-        assert(_symbolicDecomposition && "Symbolic cholesky failed");
-      }
-      // re-allocate the temporary workspace for cholesky
-      if (_csWorkspaceSize < _ccsA->n) {
-        _csWorkspaceSize = 2 * _ccsA->n;
-        delete[] _csWorkspace;
-        _csWorkspace = new double[_csWorkspaceSize];
-        delete[] _csIntWorkspace;
-        _csIntWorkspace = new int[2*_csWorkspaceSize];
-      }
+    bool solve(const SparseBlockMatrix<MatrixType>& A, double* x, double* b);

+    bool solveBlocks(double**& blocks, const SparseBlockMatrix<MatrixType>& A);

-      int ok = 1;
-      csn* numericCholesky = csparse_extension::cs_chol_workspace(_ccsA, _symbolicDecomposition, _csIntWorkspace, _csWorkspace);
-      if (numericCholesky) {
-        MarginalCovarianceCholesky mcc;
-        mcc.setCholeskyFactor(_ccsA->n, numericCholesky->L->p, numericCholesky->L->i, numericCholesky->L->x, _symbolicDecomposition->pinv);
-        mcc.computeCovariance(spinv, A.rowBlockIndices(), blockIndices);
-        cs_nfree(numericCholesky);
-      } else {
-        ok = 0;
-        std::cerr << "inverse fail (numeric decomposition)" << std::endl;
-      }
-
-      G2OBatchStatistics* globalStats = G2OBatchStatistics::globalStats();
-      if (globalStats){
-        globalStats->choleskyNNZ = static_cast<size_t>(_symbolicDecomposition->lnz);
-      }
-
-      return ok != 0;
-    }
+    virtual bool solvePattern(SparseBlockMatrix<MatrixXD>& spinv,
+      const std::vector<std::pair<int, int> >& blockIndices,
+      const SparseBlockMatrix<MatrixType>& A);

     //! do the AMD ordering on the blocks or on the scalar matrix
-    bool blockOrdering() const { return _blockOrdering;}
-    void setBlockOrdering(bool blockOrdering) { _blockOrdering = blockOrdering;}
+    bool blockOrdering() const;
+    void setBlockOrdering(bool blockOrdering);

     //! write a debug dump of the system matrix if it is not SPD in solve
-    virtual bool writeDebug() const { return _writeDebug;}
-    virtual void setWriteDebug(bool b) { _writeDebug = b;}
+    virtual bool writeDebug() const;
+    virtual void setWriteDebug(bool b);

   protected:
     css* _symbolicDecomposition;
@@ -243,105 +93,9 @@ class LinearSolverCSparse : public LinearSolverCCS<MatrixType>
     VectorXI _scalarPermutation;
     bool _writeDebug;

-    void computeSymbolicDecomposition(const SparseBlockMatrix<MatrixType>& A)
-    {
-      double t=get_monotonic_time();
-      if (! _blockOrdering) {
-        _symbolicDecomposition = cs_schol (1, _ccsA) ;
-      } else {
-        A.fillBlockStructure(_matrixStructure);
-
-        // prepare block structure for the CSparse call
-        cs auxBlock;
-        auxBlock.nzmax = _matrixStructure.nzMax();
-        auxBlock.m = auxBlock.n = _matrixStructure.n;
-        auxBlock.p = _matrixStructure.Ap;
-        auxBlock.i = _matrixStructure.Aii;
-        auxBlock.x = NULL; // no values
-        auxBlock.nz = -1; // CCS format
-
-        // AMD ordering on the block structure
-        const int& n = _ccsA->n;
-        int* P = cs_amd(1, &auxBlock);
-
-        // blow up the permutation to the scalar matrix
-        if (_scalarPermutation.size() == 0)
-          _scalarPermutation.resize(n);
-        if (_scalarPermutation.size() < n)
-          _scalarPermutation.resize(2*n);
-        size_t scalarIdx = 0;
-        for (int i = 0; i < _matrixStructure.n; ++i) {
-          const int& p = P[i];
-          int base  = A.colBaseOfBlock(p);
-          int nCols = A.colsOfBlock(p);
-          for (int j = 0; j < nCols; ++j)
-            _scalarPermutation(scalarIdx++) = base++;
-        }
-        assert((int)scalarIdx == n);
-        cs_free(P);
-
-        // apply the scalar permutation to finish symbolic decomposition
-        _symbolicDecomposition = (css*) cs_calloc(1, sizeof(css));       /* allocate result S */
-        _symbolicDecomposition->pinv = cs_pinv(_scalarPermutation.data(), n);
-        cs* C = cs_symperm(_ccsA, _symbolicDecomposition->pinv, 0);
-        _symbolicDecomposition->parent = cs_etree(C, 0);
-        int* post = cs_post(_symbolicDecomposition->parent, n);
-        int* c = cs_counts(C, _symbolicDecomposition->parent, post, 0);
-        cs_free(post);
-        cs_spfree(C);
-        _symbolicDecomposition->cp = (int*) cs_malloc(n+1, sizeof(int));
-        _symbolicDecomposition->unz = _symbolicDecomposition->lnz = cs_cumsum(_symbolicDecomposition->cp, c, n);
-        cs_free(c);
-        if (_symbolicDecomposition->lnz < 0) {
-          cs_sfree(_symbolicDecomposition);
-          _symbolicDecomposition = 0;
-        }
-
-      }
-      G2OBatchStatistics* globalStats = G2OBatchStatistics::globalStats();
-      if (globalStats){
-        globalStats->timeSymbolicDecomposition = get_monotonic_time() - t;
-      }
-
-      /* std::cerr << "# Number of nonzeros in L: " << (int)_symbolicDecomposition->lnz << " by " */
-      /*   << (_blockOrdering ? "block" : "scalar") << " AMD ordering " << std::endl; */
-    }
-
-    void fillCSparse(const SparseBlockMatrix<MatrixType>& A, bool onlyValues)
-    {
-      if (! onlyValues)
-        this->initMatrixStructure(A);
-      int m = A.rows();
-      int n = A.cols();
-      assert(m > 0 && n > 0 && "Hessian has 0 rows/cols");
-
-      if (_ccsA->columnsAllocated < n) {
-        _ccsA->columnsAllocated = _ccsA->columnsAllocated == 0 ? n : 2 * n; // pre-allocate more space if re-allocating
-        delete[] _ccsA->p;
-        _ccsA->p = new int[_ccsA->columnsAllocated+1];
-      }
-
-      if (! onlyValues) {
-        int nzmax = A.nonZeros();
-        if (_ccsA->nzmax < nzmax) {
-          _ccsA->nzmax = _ccsA->nzmax == 0 ? nzmax : 2 * nzmax; // pre-allocate more space if re-allocating
-          delete[] _ccsA->x;
-          delete[] _ccsA->i;
-          _ccsA->i = new int[_ccsA->nzmax];
-          _ccsA->x = new double[_ccsA->nzmax];
-        }
-      }
-      _ccsA->m = m;
-      _ccsA->n = n;
+    void computeSymbolicDecomposition(const SparseBlockMatrix<MatrixType>& A);

-      if (onlyValues) {
-        this->_ccsMatrix->fillCCS(_ccsA->x, true);
-      } else {
-        int nz = this->_ccsMatrix->fillCCS(_ccsA->p, _ccsA->i, _ccsA->x, true); (void) nz;
-        assert(nz <= _ccsA->nzmax);
-      }
-      _ccsA->nz=-1; // tag as CCS formatted matrix
-    }
+    void fillCSparse(const SparseBlockMatrix<MatrixType>& A, bool onlyValues);
 };

 } // end namespace
-- 
2.1.4