Files
mlpack/fastlib/sparse/sparse_matrix.h
T

557 lines
20 KiB
C++

/*
* =====================================================================================
*
* Filename: sparse_matrix.h
*
* Description:
*
* Version: 1.0
* Created: 12/01/2007 04:12:00 PM EST
* Revision: none
* Compiler: gcc
*
* Author: Nikolaos Vasiloglou (NV), nvasil@ieee.org
* Company: Georgia Tech Fastlab-ESP Lab
*
* =====================================================================================
*/
#ifndef SPARSE_MATRIX_H_
#define SPARSE_MATRIX_H_
#ifndef HAVE_CONFIG_H
#define HAVE_CONFIG_H
#endif
#ifndef USE_TRILINOS
#define USE_TRILINOS
#endif
#include <stdio.h>
#include <errno.h>
#include <string>
#include <map>
#include <vector>
#include <sstream>
#include <algorithm>
#include "fastlib/fastlib.h"
#include "la/matrix.h"
// you need this because trillinos redifines it. It's ok
// if you don't have it, but you will get an annoying warning
#ifdef F77_FUNC
#undef F77_FUNC
#endif
#include "trilinos/include/Epetra_CrsMatrix.h"
#include "trilinos/include/Epetra_SerialComm.h"
#include "trilinos/include/Epetra_Map.h"
#include "trilinos/include/Epetra_Vector.h"
#include "trilinos/include/Epetra_MultiVector.h"
#include "trilinos/include/AnasaziBasicEigenproblem.hpp"
#include "trilinos/include/AnasaziEpetraAdapter.hpp"
#include "trilinos/include/AnasaziBlockKrylovSchurSolMgr.hpp"
#include "trilinos/include/AztecOO.h"
#include "trilinos/include/Ifpack_CrsIct.h"
/* class SparseMatrix created by Nick
* This is a sparse matrix wrapper for trilinos Epetra_CrsMatrix
* It is much simpler than Epetra_CrsMatrix. At this time
* it supports eigenvalues (Krylov method) and linear system solution
* I have added matrix addition/subtraction multiplication
* I am also trying to add the submatrices
* Note: There is a restriction on these matrices, the number of rows is
* always greater or equal to the number of columns. The number of rows
* is also called dimension. We pose this restriction because trilinos supports
* square matrices only. In sparse matrices though this is not the problem since
* an mxn matrix where m>n can is equivalent to an mxm matrix where all the
* elements with n<j<m are zero
*/
class Sparsem;
class SparseMatrix {
public:
friend class Sparsem;
// Some typedefs for oft-used data types
typedef Epetra_MultiVector MV;
typedef Epetra_Operator OP;
typedef Anasazi::MultiVecTraits<double, Epetra_MultiVector> MVT;
SparseMatrix() ;
// Constructor
// num_of_rows: number of rows
// num_of_cols: number of columns
// nnz_per_row: an estimate of the non zero elements per row
// This doesn't need to be accurate. If you need
// more it will automatically resize. Try to be as accurate
// as you can because resizing costs. It is better if your
// estimete if greater than the true non zero elements. So
// it is better to overestimate than underestimate
SparseMatrix(const index_t num_of_rows,
const index_t num_of_cols,
const index_t nnz_per_row);
// Copy constructor
SparseMatrix(const SparseMatrix &other);
SparseMatrix(std::string textfile) {
Init(textfile);
}
~SparseMatrix() {
Destruct();
}
void Destruct();
// Use this initializer like the Constructor
void Init(const index_t num_of_rows,
const index_t num_of_columns,
const index_t nnz_per_row);
// This Initializer is like the previous one with the main difference that
// for every row we give a seperate estimate for the non-zero elements.
void Init(index_t num_of_rows, index_t num_of_columns, index_t *nnz_per_row);
// This Initializer fills the sparse matrix with data.
// row_indices: row indices for non-zero elements
// col_indices: column indices for non-zero elements
// values : values of non-zeros elements
// If the dimension (number of rows)and the expected (nnz elements per row)
// are set to a negative value, the function will automatically detect it
void Init(const std::vector<index_t> &row_indices,
const std::vector<index_t> &col_indices,
const Vector &values,
index_t nnz_per_row,
index_t dimension);
// The same as above but we use STL vector for values
void Init(const std::vector<index_t> &row_indices,
const std::vector<index_t> &col_indices,
const std::vector<double> &values,
index_t nnz_per_row,
index_t dimension);
// Initialize from a text file in the following format
// row column value \n
void Init(std::string textfile);
// Initialize the diagonal
void InitDiagonal(const Vector &vec);
// Initialize the diagonal with a constant
void InitDiagonal(const double value);
// It is recomended that you load the matrix row-wise, Before
// you do that call StartLoadingRows()
void StartLoadingRows();
// All these functions load Rows, with the data in different format
void LoadRow(index_t row, std::vector<index_t> &columns, Vector &values);
void LoadRow(index_t row, index_t *columns, Vector &values);
void LoadRow(index_t row, index_t num, index_t *columns, double *values);
void LoadRow(index_t row, std::vector<index_t> &columns, std::vector<double> &values);
// When you are done call this it does some optimization in the storage, no
// further asignment
void EndLoading();
// It makes the matrix symmetric. It scans the rows of the matrix and for every (i,j)
// element (j,i) equal to (j,i)
void MakeSymmetric();
// if you know that the matrix is symmetric set the flag
void set_symmetric(bool val) {
issymmetric_ = val;
}
// Not implemented yet
void SetDiagonal(const Vector &vector);
// Copy function, used also by copy constructor
void Copy(const SparseMatrix &other);
// Not implemented yet
void Alias(const Matrix& other);
// Not Implemented yet
void SwapValues(SparseMatrix* other);
// Access values, It will fail if EndLoading() has been called
double get(index_t r, index_t c) const;
// Set Values
void set(index_t r, index_t c, double v);
// For debug purposes you can call it to print the matrix
std::string Print() {
std::ostringstream s1;
matrix_->Print(s1);
return s1.str();
}
// Get the number of rows
index_t get_num_of_rows() {
return num_of_rows_;
}
// Get the number of columns
index_t get_num_of_columns() {
return num_of_columns_;
}
// Dimension should be equal to the number of rows
index_t get_dimension() {
return dimension_;
}
// The number of non zero elements
index_t get_nnz() {
return matrix_->NumGlobalNonzeros();
}
// Computes the eignvalues with the Krylov Method
void Eig(index_t num_of_eigvalues, // number of eigenvalues to compute
std::string eigtype, // Choose which eigenvalues to compute
// Choices are:
// LM - target the largest magnitude
// SM - target the smallest magnitude
// LR - target the largest real
// SR - target the smallest real
// LI - target the largest imaginary
// SI - target the smallest imaginary
Matrix *eigvectors, // The eigenvectors computed must not be initialized
std::vector<double> *real_eigvalues, // real part of the eigenvalues
// must be initialized, but should not
// allocate space. The eigenvalues
// returned might actually be less
// than the ones requested
// for example when the matrix has
// rank n< eigenvalues requested
std::vector<double> *imag_eigvalues // imaginary part of the eigenvalues
// must be initialized. If the
// problem is symmetric there is
// no need to initialize.
// The same as real_eigvalues
// hold for the space allocated
// in the non-symmetric case
);
// Linear System solution, Call Endloading First.
void LinSolve(Vector &b, // must be initialized (space allocated)
Vector *x, // must be initialized (space allocated)
double tolerance,
index_t iterations
) {
if (matrix_->Filled()==false && matrix_->StorageOptimized()==false){
FATAL("You should call EndLoading() first\n");
}
Epetra_Vector tempb(View, *map_, b.ptr());
Epetra_Vector tempx(View, *map_, x->ptr());
// create linear problem
Epetra_LinearProblem problem(matrix_.get(), &tempx, &tempb);
// create the AztecOO instance
AztecOO solver(problem);
solver.SetAztecOption( AZ_precond, AZ_Jacobi);
solver.Iterate(iterations, tolerance);
NONFATAL("Solver performed %i iterations, true residual %lg",
solver.NumIters(), solver.TrueResidual());
}
// Use this for the general case
void LinSolve(Vector &b, Vector *x) {
LinSolve(b, x, 1E-9, 1000);
}
void IncompleteCholesky(double level_fill,
double drop_tol,
SparseMatrix *U,
SparseMatrix *D);
// scales the matrix with a scalar;
void Scale(double scalar) {
matrix_->Scale(scalar);
}
// The matrix will be scaled such that A(i,j) = x(j)*A(i,j)
// where i denotes the global row number of A and j denotes the column number
void ColumnScale(const Vector &vec) {
Epetra_Vector temp(View, *map_, (double*)vec.ptr());
matrix_->RightScale(temp);
}
// The matrix will be scaled such that A(i,j) = x(i)*A(i,j)
// where i denotes the row number of A and j denotes the column number of A.
void RowScale(const Vector &vec) {
Epetra_Vector temp(View, *map_, (double *)vec.ptr());
matrix_->LeftScale(temp);
}
// computes the L1 norm
double L1Norm() {
return matrix_->NormOne();
}
// L infinity norm
double LInfNorm() {
return matrix_->NormInf();
}
// Computes the inverse of the sum of absolute values of the rows
// of the matrix
void InvRowsSums(Vector *result) {
Epetra_Vector temp(View, *map_, result->ptr());
matrix_->InvRowSums(temp);
}
// Computes the inv of max of absolute values of the rows of the matrix,
void InvRowMaxs(Vector *result) {
Epetra_Vector temp(View, *map_, result->ptr());
matrix_->InvRowMaxs(temp);
}
// Computes the inverse of the sum of absolute values of the columns of the
// matrix
void InvColSums(Vector *result) {
Epetra_Vector temp(View, *map_, result->ptr());
matrix_->InvColSums(temp);
}
// Computes the inv of max of absolute values of the columns of the matrix,
void InvColMaxs(Vector *result) {
Epetra_Vector temp(View, *map_, result->ptr());
matrix_->InvColMaxs(temp);
}
private:
index_t dimension_;
index_t num_of_rows_;
index_t num_of_columns_;
Epetra_SerialComm comm_;
bool issymmetric_;
Epetra_Map *map_;
Teuchos::RCP<Epetra_CrsMatrix> matrix_;
index_t *my_global_elements_;
void Init(const Epetra_CrsMatrix &other);
void Load(const std::vector<index_t> &rows,
const std::vector<index_t> &columns,
const Vector &values);
void AllRowsLoad(Vector &rows, Vector &columns);
};
class Sparsem {
public:
static inline void Add(const SparseMatrix &a,
const SparseMatrix &b,
SparseMatrix *result) {
DEBUG_ASSERT(a.num_of_rows_==b.num_of_rows_);
DEBUG_ASSERT(a.num_of_columns_==b.num_of_columns_);
DEBUG_ASSERT(a.num_of_rows_==result->num_of_rows_);
DEBUG_ASSERT(a.num_of_columns_==result->num_of_columns_);
result->StartLoadingRows();
for(index_t r=0; r<a.num_of_rows_; r++) {
index_t num1, num2;
double *values1, *values2;
index_t *indices1, *indices2;
a.matrix_->ExtractGlobalRowView(a.my_global_elements_[r], num1, values1, indices1);
b.matrix_->ExtractGlobalRowView(b.my_global_elements_[r], num2, values2, indices2);
std::vector<double> values3;
std::vector<index_t> indices3;
index_t i=0;
index_t j=0;
while (likely(i<num1 && j<num2)) {
while (indices1[i] < indices2[j]) {
values3.push_back(values1[i]);
indices3.push_back(indices1[i]);
i++;
if unlikely((i>=num1)) {
break;
}
}
if ( likely(i<num1) && indices1[i] == indices2[j]) {
values3.push_back(values1[i] + values2[j]);
indices3.push_back(indices1[i]);
} else {
values3.push_back(values2[j]);
indices3.push_back(indices2[j]);
}
j++;
}
if (i<num1) {
values3.insert(values3.end(), values1+i, values1+num1);
indices3.insert(indices3.end(), indices1+i, indices1+num1);
}
if (j<num2) {
values3.insert(values3.end(), values2+j, values2+num2);
indices3.insert(indices3.end(), indices2+j, indices2+num2);
}
result->LoadRow(r, indices3, values3);
}
}
static inline void Subtract(const SparseMatrix &a,
const SparseMatrix &b,
SparseMatrix *result) {
DEBUG_ASSERT(a.num_of_rows_==b.num_of_rows_);
DEBUG_ASSERT(a.num_of_columns_==b.num_of_columns_);
DEBUG_ASSERT(a.num_of_rows_==result->num_of_rows_);
DEBUG_ASSERT(a.num_of_columns_==result->num_of_columns_);
// If you try assigning the results to an already initialized matrix
// you might get unexpected results. The following assertions
// prevent you partially from that
DEBUG_ASSERT(&a != result);
DEBUG_ASSERT(&b != result);
result->StartLoadingRows();
for(index_t r=0; r<a.num_of_rows_; r++) {
index_t num1, num2;
double *values1, *values2;
index_t *indices1, *indices2;
a.matrix_->ExtractGlobalRowView(a.my_global_elements_[r], num1, values1, indices1);
b.matrix_->ExtractGlobalRowView(b.my_global_elements_[r], num2, values2, indices2);
std::vector<double> values3;
std::vector<index_t> indices3;
index_t i=0;
index_t j=0;
while (likely(i<num1 && j<num2)) {
while (indices1[i] < indices2[j]) {
values3.push_back(values1[i]);
indices3.push_back(indices1[i]);
i++;
if unlikely((i>=num1)) {
break;
}
}
if (likely(i<num1) && indices1[i] == indices2[j]) {
double diff=values1[i] - values2[j];
if (diff!=0) {
values3.push_back(diff);
indices3.push_back(indices1[i]);
}
} else {
values3.push_back(-values2[j]);
indices3.push_back(indices2[j]);
}
j++;
}
if (i<num1) {
values3.insert(values3.end(), values1+i, values1+num1);
indices3.insert(indices3.end(), indices1+i, indices1+num1);
}
if (j<num2) {
for(index_t k=j; k<num2; k++) {
values3.push_back(-values2[k]);
indices3.push_back(indices2[k]);
}
}
result->LoadRow(r, indices3, values3);
}
}
/* Multiplication of two matrices A*B in matlab notation
* If B is symmetric then it is much faster, because we can
* multiply rows. Otherwise we have to compute the transpose
* As an advise multiplication of two sparse matrices might
* lead to a dense one, so please be carefull
*/
static inline void Multiply(const SparseMatrix &a,
const SparseMatrix &b,
SparseMatrix *result) {
DEBUG_ASSERT(a.num_of_columns_ == b.num_of_rows_);
DEBUG_ASSERT(a.num_of_rows_ == result->num_of_rows_);
DEBUG_ASSERT(b.num_of_columns_ == result->num_of_columns_);
// If you try assigning the results to an already initialized matrix
// you might get unexpected results. The following assertions
// prevent you partially from that
DEBUG_ASSERT(&a != result);
DEBUG_ASSERT(&b != result);
if (b.issymmetric_ == true) {
for(index_t r1=0; r1<a.num_of_rows_; r1++) {
std::vector<index_t> indices3;
std::vector<double> values3;
index_t num1;
double *values1;
index_t *indices1;
a.matrix_->ExtractGlobalRowView(a.my_global_elements_[r1],
num1, values1, indices1);
for(index_t r2=0; r2<b.num_of_rows_; r2++) {
index_t num2;
double *values2;
index_t *indices2;
b.matrix_->ExtractGlobalRowView(b.my_global_elements_[r2],
num2, values2, indices2);
index_t i=0;
index_t j=0;
double dot_product=0;
while (likely(i<num1 && j<num2)) {
while (indices1[i] < indices2[j]) {
i++;
if unlikely((i>=num1)) {
break;
}
}
if (likely(i<num1) && indices1[i] == indices2[j]) {
dot_product += values1[i] * values2[j];
}
j++;
}
if (dot_product!=0) {
indices3.push_back(r2);
values3.push_back(dot_product);
}
}
result->LoadRow(r1, indices3, values3);
indices3.clear();
values3.clear();
}
} else {
for(index_t r1=0; r1<a.num_of_rows_; r1++) {
index_t num1;
double *values1;
index_t *indices1;
a.matrix_->ExtractGlobalRowView(a.my_global_elements_[r1],
num1, values1, indices1);
double dot_product=0;
std::vector<index_t> indices3;
std::vector<double> values3;
for(index_t r2=0; r2<b.num_of_columns_; r2++) {
for(index_t k=0; k< num1; k++) {
dot_product += values1[k]*b.get(indices1[k], r2);
}
if (dot_product!=0){
indices3.push_back(r2);
values3.push_back(dot_product);
}
dot_product=0;
}
if (!indices3.empty()) {
result->LoadRow(r1, indices3, values3);
}
indices3.clear();
values3.clear();
}
}
}
/* The transpose flag should be set to true if
* we want to use the transpose of mat, otherwise
* set it to false.
* */
static inline void Multiply(const SparseMatrix &mat,
const Vector &vec,
Vector *result,
bool transpose_flag) {
Epetra_Vector temp_in(View, *(mat.map_), (double *)vec.ptr());
Epetra_Vector temp_out(View, *(mat.map_), (double *)result->ptr());
mat.matrix_->Multiply(transpose_flag, temp_in, temp_out);
}
/* Multiply the matrix with a scalar
*/
static inline void Multiply(const SparseMatrix &mat,
const double scalar,
SparseMatrix *result) {
result->Copy(mat);
result->Scale(scalar);
}
/* element wise multiplication of the matrices
* A.*B in matlab notation
*/
static inline void DotMultiply(const SparseMatrix &a,
const SparseMatrix &b,
SparseMatrix *result) {
DEBUG_ASSERT(a.num_of_columns_ == b.num_of_rows_);
DEBUG_ASSERT(a.num_of_rows_ == result->num_of_rows_);
DEBUG_ASSERT(b.num_of_columns_ == result->num_of_columns_);
// If you try assigning the results to an already initialized matrix
// you might get unexpected results. The following assertions
// prevent you partially from that
DEBUG_ASSERT(&a != result);
DEBUG_ASSERT(&b != result);
for(index_t r=0; r<a.num_of_rows_; r++) {
std::vector<index_t> indices3;
std::vector<double> values3;
indices3.clear();
values3.clear();
index_t num1, num2;
double *values1, *values2;
index_t *indices1, *indices2;
a.matrix_->ExtractGlobalRowView(a.my_global_elements_[r], num1, values1, indices1);
b.matrix_->ExtractGlobalRowView(b.my_global_elements_[r], num2, values2, indices2);
index_t i=0;
index_t j=0;
while (likely(i<num1 && j<num2)) {
while (indices1[i] < indices2[j]) {
i++;
if unlikely((i>=num1)) {
break;
}
}
if ( likely(i<num1) && indices1[i] == indices2[j]) {
values3.push_back(values1[i] * values2[j]);
indices3.push_back(indices1[i]);
}
j++;
}
result->LoadRow(r, indices3, values3);
}
}
};
#include "u/nvasil/sparse_matrix/sparse_matrix_impl.h"
#endif