Files
mlpack/fastlib/branches/fastlib-old/sparse/sparse_matrix.h
T
Ryan Curtin f6864dd435 Move fastlib-old (originally 'fastlib') to fastlib/branches/fastlib-old where it
will sit until the end of time and nobody will touch it because it's old
2010-01-31 22:31:55 +00:00

494 lines
16 KiB
C++

/**
* @sparse_matrix.h
* Wrappers on the trilinos sparse solver
* It also has functionality for adding, subtracting and multiplying
* sparse matrices
*/
#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 Sparsem;
/** 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 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);
~SparseMatrix();
/** 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
* NOTE !!!!!!!
* the text file must be sorted according to the rows
* meaning that the rows should be in increasing order
* you can do that easily in unix with the sort -n command
* if it is not sorted it will still work but it will load
* much slower
*/
void Init(std::string textfile);
/**
* Copy function, used also by copy constructor
*/
void Copy(const SparseMatrix &other);
/**
* Not implemented yet
*/
void Alias(const Matrix& other);
void Destruct();
/**
* 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
* WARNING!!! there should not be duplicate indices
* If you load the same row twice or the row has duplicate columns
* then you will get unexpected results
*/
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
* !!!WARNING !!!!
* if there are empty rows it is going to eliminate them,
* so it might change the dimensions of the matrix
*/
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;
}
/**
* Sets the diagonal with the values of the vector
*/
void SetDiagonal(const Vector &vector);
/**
* Sets the diagonal withe a scalar
*/
void SetDiagonal(const double scalar);
/**
* Not Implemented yet
*/
void SwapValues(SparseMatrix* other);
/**
* Returns a copy of a row. It allocates memory for *columns
* and values. Make sure that you do delete []*columns and
* delete []*values after you use them
*/
void get_row_copy(index_t r, index_t *num,
index_t **columns,
double **values) const ;
/** 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);
/**
* scales the matrix with a scalar
*/
void Scale(double scalar) {
matrix_->Scale(scalar);
}
/**
* negate the matrix get -A
*/
void Negate();
/** 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
* You must have called EndLoading()
*/
void ColumnScale(const Vector &vec) {
if (unlikely(!matrix_->Filled())) {
FATAL("You should call EndLoading first...\n");
}
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.
* You must have called EndLoading()
*/
void RowScale(const Vector &vec) {
if (unlikely(!matrix_->Filled())) {
FATAL("You should call EndLoading first...\n");
}
Epetra_Vector temp(View, *map_, (double *)vec.ptr());
matrix_->LeftScale(temp);
}
/**
* computes the L1 norm
* You must have called EndLoading()
*/
double L1Norm() {
if (unlikely(!matrix_->Filled())) {
FATAL("You should call EndLoading first...\n");
}
return matrix_->NormOne();
}
/**
* L infinity norm
* You must have called EndLoading()
*/
double LInfNorm() {
if (unlikely(!matrix_->Filled())) {
FATAL("You should call EndLoading first...\n");
}
return matrix_->NormInf();
}
/**
* Computes the inverse of the sum of absolute values of the rows
* of the matrix
* You must have called EndLoading()
*/
void InvRowSums(Vector *result) {
if (unlikely(!matrix_->Filled())) {
FATAL("You have to call EndLoading first...\n");
}
result->Init(dimension_);
Epetra_Vector temp(View, *map_, result->ptr());
matrix_->InvRowSums(temp);
}
/**
* Computes the the sum of absolute values of the rows
* of the matrix
* You must have called EndLoading()
*/
void RowSums(Vector *result) {
if (unlikely(!matrix_->Filled())) {
FATAL("You have to call EndLoading first...\n");
}
result->Init(dimension_);
Epetra_Vector temp(View, *map_, result->ptr());
matrix_->InvRowSums(temp);
for(index_t i=0; i<dimension_; i++) {
(*result)[i]= 1/(*result)[i];
}
}
/**
* Computes the inv of max of absolute values of the rows of the matrixa
* You must have called EndLoading()
*/
void InvRowMaxs(Vector *result) {
if (unlikely(!matrix_->Filled())) {
FATAL("You have to call EndLoading first...\n");
}
result->Init(dimension_);
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
* You must have called EndLoading()
*/
void InvColSums(Vector *result) {
if (unlikely(!matrix_->Filled())) {
FATAL("You have to call EndLoading first...\n");
}
result->Init(dimension_);
Epetra_Vector temp(View, *map_, result->ptr());
matrix_->InvColSums(temp);
}
/**
* Computes the inv of max of absolute values of the columns of the matrix
* You must have called EndLoading()
*/
void InvColMaxs(Vector *result) {
if (unlikely(!matrix_->Filled())) {
FATAL("You have to call EndLoading first...\n");
}
result->Init(num_of_columns_);
Epetra_Vector temp(View, *map_, result->ptr());
matrix_->InvColMaxs(temp);
}
/**
* Get the number of rows
*/
index_t num_of_rows() {
return num_of_rows_;
}
/**
* Get the number of columns
*/
index_t num_of_columns() {
return num_of_columns_;
}
/**
* Dimension should be equal to the number of rows
*/
index_t dimension() {
return dimension_;
}
/**
* The number of non zero elements
*/
index_t nnz() const {
return matrix_->NumGlobalNonzeros();
}
/** Apply a function on every non-zero element, very usefull for kernels
* If you have entered a zero element then it will also be applied on it
* as well
*/
template<typename FUNC>
void ApplyFunction(FUNC &function);
/**
* For debug purposes you can call it to print the matrix
*/
std::string Print() {
std::ostringstream s1;
matrix_->Print(s1);
return s1.str();
}
void ToFile(std::string file);
/**
* Computes the eignvalues with the Krylov Method
* 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
* Vector *real_eigvalues: real part of the eigenvalues
* must not be initialized.
* The eigenvalues
* returned might actually be less
* than the ones requested
* for example when the matrix has
* rank n< eigenvalues requested
* Vector *imag_eigvalues: imaginary part of the eigenvalues
* must not be initialized.
* The same as real_eigvalues
* hold for the space allocated
* in the non-symmetric case
*/
void Eig(index_t num_of_eigvalues,
std::string eigtype,
Matrix *eigvectors,
Vector *real_eigvalues,
Vector *imag_eigvalues);
/**
* Solves the pancil problem:
* A*x=lambda *B*x
* where pencil_part is the B matrix
* You have to call EndLoading() for B first
*/
void Eig(SparseMatrix &pencil_part,
index_t num_of_eigvalues,
std::string eigtype,
Matrix *eigvectors,
Vector *real_eigvalues,
Vector *imag_eigvalues);
/**
* 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);
/** Use this for the general case
*/
void LinSolve(Vector &b, Vector *x) {
LinSolve(b, x, 1E-9, 1000);
}
void IncompleteCholesky(index_t level_fill,
double drop_tol,
SparseMatrix *u,
Vector *d,
double *condest);
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);
};
/**
* Sparsem is more like an interface providing basic lagebraic operations
* addition, subtraction multiplicatiion, for sparse matrices. It should
* have been a namespace, but I prefered to make it a class with static
* member functions so that I can declare it as a friend to the SparseMatrix
* class
*
* WARNING !!!! THE RESULT SHOULD NOT BE INITIALIZED !!!
*/
class Sparsem {
public:
static inline void Add(const SparseMatrix &a,
const SparseMatrix &b,
SparseMatrix *result);
static inline void Subtract(const SparseMatrix &a,
const SparseMatrix &b,
SparseMatrix *result);
/** 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);
/*
* Computes the result = A * A^T
*/
static inline void MultiplyT(SparseMatrix &a,
SparseMatrix *result);
/** 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);
/**
* Multiply the matrix with a scalar
*/
static inline void Multiply(const SparseMatrix &mat,
const double scalar,
SparseMatrix *result);
/**
* element wise multiplication of the matrices
* A.*B in matlab notation
*/
static inline void DotMultiply(const SparseMatrix &a,
const SparseMatrix &b,
SparseMatrix *result);
};
#include "sparse/sparse_matrix_impl.h"
#endif