Commit f719b4a3 authored by Henrik Zimmer's avatar Henrik Zimmer

SparseQR now works with Eigen::SparseMatrix and can compute min2norm solutions...

SparseQR now works with Eigen::SparseMatrix and can compute min2norm solutions to underdetermined problem.

git-svn-id: http://www.openflipper.org/svnrepo/CoMISo/trunk@159 1355f012-dd97-4b2f-ae87-10fa9f823a57
parent 77bb4dde
......@@ -336,6 +336,10 @@ endif()
if( EXISTS "${CMAKE_SOURCE_DIR}/Examples/small_miqp/CMakeLists.txt" )
add_subdirectory (Examples/small_miqp)
endif()
if( EXISTS "${CMAKE_SOURCE_DIR}/Examples/small_sparseqr/CMakeLists.txt" )
add_subdirectory (Examples/small_sparseqr)
endif()
include (ACGCommon)
include (CoMISoExample)
# source code directories
set (directories
.
)
# collect all header and source files
acg_append_files (headers "*.hh" ${directories})
acg_append_files (sources "*.cc" ${directories})
# remove template cc files from source file list
acg_drop_templates (sources)
if (WIN32)
acg_add_executable (small_sparseqr WIN32 ${sources} ${headers} )
elseif (APPLE)
# generate bundle on mac
acg_add_executable (small_sparseqr MACOSX_BUNDLE ${sources} ${headers} )
else ()
acg_add_executable (small_sparseqr ${sources} ${headers} )
endif ()
# enable rpath linking
set_target_properties(small_sparseqr PROPERTIES INSTALL_RPATH_USE_LINK_PATH 1)
target_link_libraries (small_sparseqr
CoMISo
${COMISO_LINK_LIBRARIES}
)
if (APPLE)
# create bundle in "Build" directory and set icon
# no install needed here, because the whole bundle will be installed in the
# toplevel CMakeLists.txt
set_target_properties (
small_sparseqr PROPERTIES
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/Build"
MACOSX_BUNDLE_INFO_STRING "CoMISo small_sparseqr"
)
endif ()
/*===========================================================================*\
* *
* CoMISo *
* Copyright (C) 2008-2009 by Computer Graphics Group, RWTH Aachen *
* www.rwth-graphics.de *
* *
*---------------------------------------------------------------------------*
* This file is part of CoMISo. *
* *
* CoMISo is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* CoMISo is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with CoMISo. If not, see <http://www.gnu.org/licenses/>. *
* *
\*===========================================================================*/
#include <CoMISo/Config/config.hh>
#include <CoMISo/Utils/StopWatch.hh>
#include <vector>
#include <cstdlib>
#include <Eigen/Sparse>
#include <CoMISo/Solver/SparseQRSolver.hh>
#include <CoMISo/Solver/Eigen_Tools.hh>
//------------------------------------------------------------------------------------------------------
// Example main
int main(void)
{
std::cout << "---------- 0) Using Sparse QR for solving underdetermined equations and computing Null spaces " << std::endl;
typedef Eigen::SparseMatrix< double > SpMatrix;
typedef Eigen::MatrixXd DenMatrix;
typedef Eigen::Triplet< double > Triplet;
int dimr(4+1);
int dimc(4+2);
std::cout << "---------- 1) Creating matrix " << std::endl;
std::vector< Triplet > triplets;
for( int i = 0; i < dimc*dimr/2; ++i)
{
int x( rand()%(dimr-1));
int y( rand()%dimc);
double val( rand()%10);
//std::cerr << " setting (" << x << ", " << y << ") to " << val << std::endl;
triplets.push_back( Triplet( x, y, val));
}
SpMatrix A(dimr,dimc);
A.setFromTriplets(triplets.begin(), triplets.end());
std::cerr << DenMatrix(A) << std::endl;
int m = dimr;
int n = dimc;
if( m < n )
{
std::swap( m,n);
std::cerr << " ... m < n -> form transposed ..." << std::endl;
A = SpMatrix(A.transpose());
// test make also row -rank-deficinet
A.middleCols(n-1,1) = A.middleCols(0,1);
A.middleCols(0,1) = A.middleCols(n-2,1);
std::cerr << DenMatrix(A) << std::endl;
}
std::cerr << " ... m = " << m << "; n = " << n << std::endl;
std::cerr << std::endl;
std::cout << "---------- 2) Sparse QR " << std::endl;
COMISO::SparseQRSolver spqr;
SpMatrix Q,R;
std::vector< size_t > P;
int rank = spqr.factorize_system_eigen( A, Q, R, P);
int nullity(dimc-rank);
// setup permutation matrix
SpMatrix Pm( n, n);
if( P.size() != 0)
{
for( size_t i = 0; i < P.size(); ++i)
{
Pm.coeffRef( i, P[i]) = 1;
}
}
std::cout << "---------- 3) Result " << std::endl;
std::cerr << " Q " << std::endl << DenMatrix(Q) << std::endl;
std::cerr << " R " << std::endl << DenMatrix(R) << std::endl;
std::cerr << " P " << std::endl << P << std::endl;
std::cerr << " P matrix " << std::endl << DenMatrix(Pm) << std::endl;
std::cerr << " Rank " << rank << std::endl;
std::cerr << " Nullity " << nullity << std::endl;
// extract nullspace
SpMatrix NullSpace( Q.middleCols( std::max( 0, m-nullity), nullity));
std::cerr << " Nullspace " << std::endl << DenMatrix(NullSpace) << std::endl;
// non nullspace part of R
//// assuming superflous column in R is the last (if A is also row deficient)
//SpMatrix Rtmp(R.middleCols(0,std::min(n,n-(n-rank))).transpose());
//SpMatrix R1( R.transpose().middleCols(0, m-nullity));
SpMatrix Rtmp(R.transpose());
SpMatrix R1t( Rtmp.middleCols(0,m-nullity));
SpMatrix R1( R1t.transpose());
std::cerr << " Non-Nullspace R " << std::endl << DenMatrix(R1) << std::endl;
std::cout << "---------- 4) Verification " << std::endl;
SpMatrix reconstructedA(Q*R*Pm.transpose());
std::cerr << " Q orthogonal? \t " << ((fabs((Q.transpose()*Q).squaredNorm()-m) < 1e-8)?"yes":"no") << std::endl;
std::cerr << " A = QR? \t " << (((reconstructedA-A).squaredNorm() < 1e-8)? "yes":"no") << std::endl;
std::cerr << std::endl << std::endl;
std::cout << "---------- 5) Solving Ax=b (with x without nullspace component)" << std::endl;
// NOTE: A was transposed above to be m>n
SpMatrix b(n,1);
SpMatrix x(m,1);
for( int i = 0; i < n; ++i)
b.coeffRef(i,0) = rand()%10;
std::cerr << " ... System Ax = b .. \n";
std::cerr << " A " << std::endl << DenMatrix(A.transpose()) << " x " << std::endl << DenMatrix(x) << " b " << std::endl << DenMatrix(b) << std::endl;
std::cout << "---------- 5.1) test: solve using sparse QR solving .." << std::endl;
SpMatrix At(A.transpose());
spqr.solve_system_eigen( At, b, x);
std::cerr << " ... solution x .. " << std::endl;
std::cerr << DenMatrix(x) << std::endl;
std::cerr << " ... test: is a solution ? " << (((A.transpose()*x-b).squaredNorm()<1e-8)?"yes":"no") << std::endl;
std::cerr << " ... test: has nullspace component ? " << ((x.transpose()*NullSpace).squaredNorm()<1e-8?"yes":"no") << std::endl;
std::cerr << " ... Nullspace projections : " << (x.transpose()*NullSpace) << std::endl;
std::cout << "---------- 5.2) test: solve without nullspace .." << std::endl;
SpMatrix Atnull(At);
SpMatrix bnull(b);
SpMatrix xnull(m,1);
spqr.solve_system_eigen_min2norm( Atnull, bnull, xnull);
std::cerr << " ... solution x .. " << std::endl;
std::cerr << DenMatrix(xnull) << std::endl;
std::cerr << " ... test: is a solution ? " << (((A.transpose()*xnull-bnull).squaredNorm()<1e-8)?"yes":"no") << std::endl;
std::cerr << " ... test: has nullspace component ? " << ((xnull.transpose()*NullSpace).squaredNorm()<1e-8?"yes":"no") << std::endl;
std::cerr << " ... Nullspace projections : " << (xnull.transpose()*NullSpace) << std::endl;
return 0;
}
This diff is collapsed.
......@@ -34,6 +34,8 @@
#include <algorithm>
#include <limits>
#include <cmath>
#include <Eigen/Eigen>
#include <Eigen/Sparse>
//#ifdef COMISO_Eigen3_AVAILABLE
//#include <Eigen/Eigen>
......@@ -87,6 +89,23 @@ void inspect_matrix( const MatrixT& _A);
template<class MatrixT>
bool is_symmetric( const MatrixT& _A);
template< class Eigen_MatrixT, class IntT >
void permute( const Eigen_MatrixT& _QR, const std::vector< IntT>& _Pvec, Eigen_MatrixT& _A);
#ifndef COMISO_NCHOLMOD
/// Eigen to Cholmod_sparse interface
template<class MatrixT>
void cholmod_to_eigen( const cholmod_sparse& _AC, MatrixT& _A);
template<class MatrixT>
void eigen_to_cholmod( const MatrixT& _A,
cholmod_sparse* &_AC,
cholmod_common* _common,
int _sparsity_type = 0,
bool _long_int = false);
#endif
//=============================================================================
......
......@@ -83,14 +83,21 @@ public:
bool calc_system_gmm( const GMM_MatrixT& _mat);
template< class GMM_MatrixT>
bool update_system_gmm( const GMM_MatrixT& _mat);
template< class Eigen_MatrixT>
bool calc_system_eigen( const Eigen_MatrixT& _mat);
template< class Eigen_MatrixT>
bool update_system_eigen( const Eigen_MatrixT& _mat);
bool update_system( const std::vector<Int>& _colptr,
const std::vector<Int>& _rowind,
const std::vector<double>& _values );
template< class GMM_MatrixT>
bool update_system_gmm( const GMM_MatrixT& _mat);
bool solve ( double * _x0, double * _b);
......@@ -107,6 +114,19 @@ public:
template< class GMM_MatrixT, class GMM_MatrixT2, class GMM_MatrixT3, class IntT>
int factorize_system_gmm( const GMM_MatrixT& _A, GMM_MatrixT2& _Q, GMM_MatrixT3& _R, std::vector<IntT>& _P);
// factorize _A*P = _Q*_R and return rank
template< class Eigen_MatrixT, class IntT >
int factorize_system_eigen( const Eigen_MatrixT& _A, Eigen_MatrixT& _Q, Eigen_MatrixT& _R, std::vector<IntT>& _P);
// Solve Ax=b, not the most efficient as it uses SparseMatrices also for _b and _x
template< class Eigen_MatrixT>
void solve_system_eigen( const Eigen_MatrixT& _A, const Eigen_MatrixT& _b, Eigen_MatrixT& _x);
// Solve Ax=b, min( ||x||_2) not the most efficient as it uses SparseMatrices also for _b and _x
template< class Eigen_MatrixT>
void solve_system_eigen_min2norm( const Eigen_MatrixT& _A, const Eigen_MatrixT& _b, Eigen_MatrixT& _x);
private:
cholmod_common * mp_cholmodCommon;
......
......@@ -27,6 +27,7 @@
#define COMISO_SPARSE_QR_SOLVER_TEMPLATES_C
#include "SparseQRSolver.hh"
#include <CoMISo/Solver/Eigen_Tools.hh>
namespace COMISO {
......@@ -50,7 +51,7 @@ bool SparseQRSolver::calc_system_gmm( const GMM_MatrixT& _mat)
if(show_timings_)
{
std::cerr << "Cholmod Timing GMM convert: " << sw_.stop()/1000.0 << "s\n";
std::cerr << "SparseQRSolver Timing GMM convert: " << sw_.stop()/1000.0 << "s\n";
}
return calc_system( colptr_, rowind_, values_);
......@@ -78,7 +79,43 @@ bool SparseQRSolver::update_system_gmm( const GMM_MatrixT& _mat)
//-----------------------------------------------------------------------------
template< class Eigen_MatrixT>
bool SparseQRSolver::calc_system_eigen( const Eigen_MatrixT& _mat)
{
if(show_timings_) sw_.start();
COMISO_EIGEN::get_ccs_symmetric_data( _mat,
'c',
values_,
rowind_,
colptr_ );
if(show_timings_)
{
std::cerr << "SparseQRSolver Timing EIGEN convert: " << sw_.stop()/1000.0 << "s\n";
}
return calc_system( colptr_, rowind_, values_);
}
//-----------------------------------------------------------------------------
template< class Eigen_MatrixT>
bool SparseQRSolver::update_system_eigen( const Eigen_MatrixT& _mat)
{
COMISO_EIGEN::get_ccs_symmetric_data( _mat,
'c',
values_,
rowind_,
colptr_ );
return update_system( colptr_, rowind_, values_);
}
//-----------------------------------------------------------------------------
template< class GMM_MatrixT, class GMM_MatrixT2, class GMM_MatrixT3, class IntT>
......@@ -146,4 +183,186 @@ factorize_system_gmm( const GMM_MatrixT& _A, GMM_MatrixT2& _Q, GMM_MatrixT3& _R,
return rank;
}
//-----------------------------------------------------------------------------
template< class Eigen_MatrixT, class IntT >
int
SparseQRSolver::
factorize_system_eigen( const Eigen_MatrixT& _A, Eigen_MatrixT& _Q, Eigen_MatrixT& _R, std::vector<IntT>& _P)
{
std::cerr << "factorize_system_eigen" << std::endl;
// get dimensions
int m = _A.innerSize();
int n = _A.outerSize();
std::cerr << " m " << m << " n " << n << std::endl;
// 1. _A -> cholmod_sparse A
cholmod_sparse* AC(0);
COMISO_EIGEN::eigen_to_cholmod(_A, AC, mp_cholmodCommon, 0, true);
//COMISO_GMM::gmm_to_cholmod(_A, AC, mp_cholmodCommon, 0, true);
std::cerr << "eigen_to_cholmod finished" << std::endl;
cholmod_print_sparse(AC, "AC", mp_cholmodCommon);
// 2. factorize A -> Q,R,P
UF_long econ = m;
cholmod_sparse *Q, *R;
// UF_long *P = new UF_long[n];
UF_long *P;
double rank = SuiteSparseQR<double>(ordering_, tolerance_, econ, AC, &Q, &R, &P, mp_cholmodCommon);
std::cerr << "factorization finished" << std::endl;
std::cerr << "rank: " << rank << std::endl;
cholmod_print_sparse(Q, "Q", mp_cholmodCommon);
// 3. convert Q,R,P -> _Q, _R, _P
COMISO_EIGEN::cholmod_to_eigen(*Q, _Q);
COMISO_EIGEN::cholmod_to_eigen(*R, _R);
std::cerr << "cholmod_to_eigen finished" << std::endl;
_P.clear(); _P.resize(n);
for( int i=0; i<n; ++i)
_P[i] = P[i];
std::cerr << "copy vector finished" << std::endl;
cholmod_l_free_sparse(&Q, mp_cholmodCommon);
cholmod_l_free_sparse(&R, mp_cholmodCommon);
cholmod_l_free_sparse(&AC, mp_cholmodCommon);
std::cerr << "free1 finished" << std::endl;
// TODO: alloc or free P ???
cholmod_free(n, sizeof(UF_long), P, mp_cholmodCommon);
std::cerr << "free2 finished" << std::endl;
//// [Q,R,E] = qr(A), returning Q as a sparse matrix
//template <typename Entry> UF_long SuiteSparseQR // returns rank(A) estimate
//(
// int ordering, // all, except 3:given treated as 0:fixed
// double tol,
// UF_long econ,
// cholmod_sparse *A, // m-by-n sparse matrix
// // outputs
// cholmod_sparse **Q, // m-by-e sparse matrix where e=max(econ,rank(A))
// cholmod_sparse **R, // e-by-n sparse matrix
// UF_long **E, // permutation of 0:n-1, NULL if identity
// cholmod_common *cc // workspace and parameters
//) ;
std::cerr << " ############## QR Factorization Info #############\n";
std::cerr << " m: " << m << ", n: " << n << ", rank: " << rank << std::endl;
return rank;
}
//-----------------------------------------------------------------------------
template< class Eigen_MatrixT >
void
SparseQRSolver::
solve_system_eigen( const Eigen_MatrixT& _A, const Eigen_MatrixT& _b, Eigen_MatrixT& _x)
{
std::cerr << __FUNCTION__ << std::endl;
// 1. _A -> cholmod_sparse A, x, b
cholmod_sparse* AC(0);
COMISO_EIGEN::eigen_to_cholmod(_A, AC, mp_cholmodCommon, 0, true);
cholmod_print_sparse(AC, "AC", mp_cholmodCommon);
cholmod_sparse* bC(0);
COMISO_EIGEN::eigen_to_cholmod(_b, bC, mp_cholmodCommon, 0, true);
cholmod_print_sparse(AC, "bC", mp_cholmodCommon);
cholmod_sparse* xC(0);
// allocation of X done internally
//COMISO_EIGEN::eigen_to_cholmod_sparse(_x, xC, mp_cholmodCommon, 0, true);
//cholmod_print_sparse(AC, "xC", mp_cholmodCommon);
// 2. solve Ax=b
xC = SuiteSparseQR<double>(ordering_, tolerance_, AC, bC, mp_cholmodCommon);
std::cerr << "solve finished" << std::endl;
cholmod_print_sparse(xC, "xC", mp_cholmodCommon);
// 3. convert solution xC to eigen
COMISO_EIGEN::cholmod_to_eigen(*xC, _x);
std::cerr << "cholmod_to_eigen finished" << std::endl;
cholmod_l_free_sparse(&AC, mp_cholmodCommon);
cholmod_l_free_sparse(&bC, mp_cholmodCommon);
cholmod_l_free_sparse(&xC, mp_cholmodCommon);
std::cerr << "free1 finished" << std::endl;
//// X = A\sparse(B)
//template <typename Entry> cholmod_sparse *SuiteSparseQR
//(
// int ordering, // all, except 3:given treated as 0:fixed
// double tol,
// cholmod_sparse *A, // m-by-n sparse matrix
// cholmod_sparse *B, // m-by-nrhs
// cholmod_common *cc // workspace and parameters
//) ;
}
//-----------------------------------------------------------------------------
template< class Eigen_MatrixT >
void
SparseQRSolver::
solve_system_eigen_min2norm( const Eigen_MatrixT& _A, const Eigen_MatrixT& _b, Eigen_MatrixT& _x)
{
std::cerr << __FUNCTION__ << std::endl;
// 1. _A -> cholmod_sparse A, x, b
cholmod_sparse* AC(0);
COMISO_EIGEN::eigen_to_cholmod(_A, AC, mp_cholmodCommon, 0, true);
//cholmod_print_sparse(AC, "AC", mp_cholmodCommon);
cholmod_sparse* bC(0);
COMISO_EIGEN::eigen_to_cholmod(_b, bC, mp_cholmodCommon, 0, true);
//cholmod_print_sparse(AC, "bC", mp_cholmodCommon);
cholmod_sparse* xC(0);
// allocation of X done internally
//COMISO_EIGEN::eigen_to_cholmod_sparse(_x, xC, mp_cholmodCommon, 0, true);
//cholmod_print_sparse(AC, "xC", mp_cholmodCommon);
// 2. solve Ax=b
xC = SuiteSparseQR_min2norm<double>(ordering_, tolerance_, AC, bC, mp_cholmodCommon);
//std::cerr << "solve finished" << std::endl;
//cholmod_print_sparse(xC, "xC", mp_cholmodCommon);
// 3. convert solution xC to eigen
COMISO_EIGEN::cholmod_to_eigen(*xC, _x);
//std::cerr << "cholmod_to_eigen finished" << std::endl;
cholmod_l_free_sparse(&AC, mp_cholmodCommon);
cholmod_l_free_sparse(&bC, mp_cholmodCommon);
cholmod_l_free_sparse(&xC, mp_cholmodCommon);
//std::cerr << "free1 finished" << std::endl;
//template <typename Entry> cholmod_sparse *SuiteSparseQR_min2norm
//(
// int ordering, // all, except 3:given treated as 0:fixed
// double tol,
// cholmod_sparse *A,
// cholmod_sparse *B,
// cholmod_common *cc
//) ;
}
}
......@@ -64,6 +64,10 @@ if(SUITESPARSE_FOUND)
list( APPEND COMISO_LINK_DIRECTORIES ${SUITESPARSE_LIBRARY_DIRS} )
endif()
FIND_PACKAGE( Eigen3)
if(Eigen3_FOUND)
list( APPEND COMISO_INCLUDE_DIRECTORIES ${Eigen3_INCLUDE_DIR})
endif()
#MESSAGE( ${COMISO_LINK_LIBRARIES})
......@@ -78,4 +82,4 @@ include_directories (
link_directories (
${COMISO_LINK_DIRECTORIES}
)
\ No newline at end of file
)
......@@ -64,8 +64,10 @@ IF(GUROBI_INCLUDE_DIR AND NOT GUROBI_LIBRARY)
SET(GUROBI_LIBRARY "gurobi45;gurobi_c++;pthread" CACHE STRING "GUROBI Libraries")
ELSEIF(EXISTS "${GUROBI_LIBRARY_DIR}/libgurobi46.so")
SET(GUROBI_LIBRARY "gurobi46;gurobi_c++;pthread" CACHE STRING "GUROBI Libraries")
ELSEIF(EXISTS "${GUROBI_LIBRARY_DIR}/libgurobi50.so")
SET(GUROBI_LIBRARY "gurobi50;gurobi_c++;pthread" CACHE STRING "GUROBI Libraries")
ELSE()
message(FATAL_ERROR "Couldn't find a gurobi lib in ${GUROBI_LIBRARY_DIR}. Maybe it's a version I don't know about, yet.")
ENDIF()
ENDIF()
ENDIF(UNIX)
\ No newline at end of file
ENDIF(UNIX)
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment