mirror of
https://gitlab.com/libeigen/eigen.git
synced 2026-04-10 11:34:33 +08:00
Apply clang-format
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -17,205 +17,194 @@
|
||||
// IWYU pragma: private
|
||||
#include "./InternalHeaderCheck.h"
|
||||
|
||||
namespace Eigen {
|
||||
namespace Eigen {
|
||||
|
||||
namespace internal
|
||||
{
|
||||
template <typename Scalar, typename StorageIndex>
|
||||
inline void GetMarketLine (const char* line, StorageIndex& i, StorageIndex& j, Scalar& value)
|
||||
{
|
||||
std::stringstream sline(line);
|
||||
sline >> i >> j >> value;
|
||||
}
|
||||
namespace internal {
|
||||
template <typename Scalar, typename StorageIndex>
|
||||
inline void GetMarketLine(const char* line, StorageIndex& i, StorageIndex& j, Scalar& value) {
|
||||
std::stringstream sline(line);
|
||||
sline >> i >> j >> value;
|
||||
}
|
||||
|
||||
template<> inline void GetMarketLine (const char* line, int& i, int& j, float& value)
|
||||
{ std::sscanf(line, "%d %d %g", &i, &j, &value); }
|
||||
template <>
|
||||
inline void GetMarketLine(const char* line, int& i, int& j, float& value) {
|
||||
std::sscanf(line, "%d %d %g", &i, &j, &value);
|
||||
}
|
||||
|
||||
template<> inline void GetMarketLine (const char* line, int& i, int& j, double& value)
|
||||
{ std::sscanf(line, "%d %d %lg", &i, &j, &value); }
|
||||
template <>
|
||||
inline void GetMarketLine(const char* line, int& i, int& j, double& value) {
|
||||
std::sscanf(line, "%d %d %lg", &i, &j, &value);
|
||||
}
|
||||
|
||||
template<> inline void GetMarketLine (const char* line, int& i, int& j, std::complex<float>& value)
|
||||
{ std::sscanf(line, "%d %d %g %g", &i, &j, &numext::real_ref(value), &numext::imag_ref(value)); }
|
||||
template <>
|
||||
inline void GetMarketLine(const char* line, int& i, int& j, std::complex<float>& value) {
|
||||
std::sscanf(line, "%d %d %g %g", &i, &j, &numext::real_ref(value), &numext::imag_ref(value));
|
||||
}
|
||||
|
||||
template<> inline void GetMarketLine (const char* line, int& i, int& j, std::complex<double>& value)
|
||||
{ std::sscanf(line, "%d %d %lg %lg", &i, &j, &numext::real_ref(value), &numext::imag_ref(value)); }
|
||||
template <>
|
||||
inline void GetMarketLine(const char* line, int& i, int& j, std::complex<double>& value) {
|
||||
std::sscanf(line, "%d %d %lg %lg", &i, &j, &numext::real_ref(value), &numext::imag_ref(value));
|
||||
}
|
||||
|
||||
template <typename Scalar, typename StorageIndex>
|
||||
inline void GetMarketLine (const char* line, StorageIndex& i, StorageIndex& j, std::complex<Scalar>& value)
|
||||
{
|
||||
std::stringstream sline(line);
|
||||
Scalar valR, valI;
|
||||
sline >> i >> j >> valR >> valI;
|
||||
value = std::complex<Scalar>(valR,valI);
|
||||
}
|
||||
template <typename Scalar, typename StorageIndex>
|
||||
inline void GetMarketLine(const char* line, StorageIndex& i, StorageIndex& j, std::complex<Scalar>& value) {
|
||||
std::stringstream sline(line);
|
||||
Scalar valR, valI;
|
||||
sline >> i >> j >> valR >> valI;
|
||||
value = std::complex<Scalar>(valR, valI);
|
||||
}
|
||||
|
||||
template <typename RealScalar>
|
||||
inline void GetDenseElt (const std::string& line, RealScalar& val)
|
||||
{
|
||||
std::istringstream newline(line);
|
||||
newline >> val;
|
||||
}
|
||||
template <typename RealScalar>
|
||||
inline void GetDenseElt(const std::string& line, RealScalar& val) {
|
||||
std::istringstream newline(line);
|
||||
newline >> val;
|
||||
}
|
||||
|
||||
template <typename RealScalar>
|
||||
inline void GetDenseElt (const std::string& line, std::complex<RealScalar>& val)
|
||||
{
|
||||
RealScalar valR, valI;
|
||||
std::istringstream newline(line);
|
||||
newline >> valR >> valI;
|
||||
val = std::complex<RealScalar>(valR, valI);
|
||||
}
|
||||
|
||||
template<typename Scalar>
|
||||
inline void putMarketHeader(std::string& header,int sym)
|
||||
{
|
||||
header= "%%MatrixMarket matrix coordinate ";
|
||||
if(internal::is_same<Scalar, std::complex<float> >::value || internal::is_same<Scalar, std::complex<double> >::value)
|
||||
{
|
||||
header += " complex";
|
||||
if(sym == Symmetric) header += " symmetric";
|
||||
else if (sym == SelfAdjoint) header += " Hermitian";
|
||||
else header += " general";
|
||||
}
|
||||
template <typename RealScalar>
|
||||
inline void GetDenseElt(const std::string& line, std::complex<RealScalar>& val) {
|
||||
RealScalar valR, valI;
|
||||
std::istringstream newline(line);
|
||||
newline >> valR >> valI;
|
||||
val = std::complex<RealScalar>(valR, valI);
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
inline void putMarketHeader(std::string& header, int sym) {
|
||||
header = "%%MatrixMarket matrix coordinate ";
|
||||
if (internal::is_same<Scalar, std::complex<float> >::value ||
|
||||
internal::is_same<Scalar, std::complex<double> >::value) {
|
||||
header += " complex";
|
||||
if (sym == Symmetric)
|
||||
header += " symmetric";
|
||||
else if (sym == SelfAdjoint)
|
||||
header += " Hermitian";
|
||||
else
|
||||
{
|
||||
header += " real";
|
||||
if(sym == Symmetric) header += " symmetric";
|
||||
else header += " general";
|
||||
}
|
||||
header += " general";
|
||||
} else {
|
||||
header += " real";
|
||||
if (sym == Symmetric)
|
||||
header += " symmetric";
|
||||
else
|
||||
header += " general";
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Scalar, typename StorageIndex>
|
||||
inline void PutMatrixElt(Scalar value, StorageIndex row, StorageIndex col, std::ofstream& out)
|
||||
{
|
||||
out << row << " "<< col << " " << value << "\n";
|
||||
}
|
||||
template<typename Scalar, typename StorageIndex>
|
||||
inline void PutMatrixElt(std::complex<Scalar> value, StorageIndex row, StorageIndex col, std::ofstream& out)
|
||||
{
|
||||
out << row << " " << col << " " << value.real() << " " << value.imag() << "\n";
|
||||
}
|
||||
template <typename Scalar, typename StorageIndex>
|
||||
inline void PutMatrixElt(Scalar value, StorageIndex row, StorageIndex col, std::ofstream& out) {
|
||||
out << row << " " << col << " " << value << "\n";
|
||||
}
|
||||
template <typename Scalar, typename StorageIndex>
|
||||
inline void PutMatrixElt(std::complex<Scalar> value, StorageIndex row, StorageIndex col, std::ofstream& out) {
|
||||
out << row << " " << col << " " << value.real() << " " << value.imag() << "\n";
|
||||
}
|
||||
|
||||
template <typename Scalar>
|
||||
inline void putDenseElt(Scalar value, std::ofstream& out) {
|
||||
out << value << "\n";
|
||||
}
|
||||
template <typename Scalar>
|
||||
inline void putDenseElt(std::complex<Scalar> value, std::ofstream& out) {
|
||||
out << value.real() << " " << value.imag() << "\n";
|
||||
}
|
||||
|
||||
template<typename Scalar>
|
||||
inline void putDenseElt(Scalar value, std::ofstream& out)
|
||||
{
|
||||
out << value << "\n";
|
||||
}
|
||||
template<typename Scalar>
|
||||
inline void putDenseElt(std::complex<Scalar> value, std::ofstream& out)
|
||||
{
|
||||
out << value.real() << " " << value.imag()<< "\n";
|
||||
}
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
/**
|
||||
* \ingroup SparseExtra_Module
|
||||
* @brief Reads the header of a matrixmarket file and determines the properties of a matrix
|
||||
*
|
||||
*
|
||||
* @param filename of the file
|
||||
* @param sym if the matrix is hermitian,symmetric or none of the latter (sym=0)
|
||||
* @param iscomplex if the matrix has complex or real coefficients
|
||||
* @param sym if the matrix is hermitian,symmetric or none of the latter (sym=0)
|
||||
* @param iscomplex if the matrix has complex or real coefficients
|
||||
* @param isdense if the matrix is dense or sparse
|
||||
* @return true if the file was found
|
||||
*/
|
||||
inline bool getMarketHeader(const std::string& filename, int& sym, bool& iscomplex, bool& isdense)
|
||||
{
|
||||
sym = 0;
|
||||
inline bool getMarketHeader(const std::string& filename, int& sym, bool& iscomplex, bool& isdense) {
|
||||
sym = 0;
|
||||
iscomplex = false;
|
||||
isdense = false;
|
||||
std::ifstream in(filename.c_str(),std::ios::in);
|
||||
if(!in)
|
||||
return false;
|
||||
|
||||
std::string line;
|
||||
// The matrix header is always the first line in the file
|
||||
std::getline(in, line); eigen_assert(in.good());
|
||||
|
||||
std::stringstream fmtline(line);
|
||||
std::ifstream in(filename.c_str(), std::ios::in);
|
||||
if (!in) return false;
|
||||
|
||||
std::string line;
|
||||
// The matrix header is always the first line in the file
|
||||
std::getline(in, line);
|
||||
eigen_assert(in.good());
|
||||
|
||||
std::stringstream fmtline(line);
|
||||
std::string substr[5];
|
||||
fmtline>> substr[0] >> substr[1] >> substr[2] >> substr[3] >> substr[4];
|
||||
if(substr[2].compare("array") == 0) isdense = true;
|
||||
if(substr[3].compare("complex") == 0) iscomplex = true;
|
||||
if(substr[4].compare("symmetric") == 0) sym = Symmetric;
|
||||
else if (substr[4].compare("Hermitian") == 0) sym = SelfAdjoint;
|
||||
|
||||
fmtline >> substr[0] >> substr[1] >> substr[2] >> substr[3] >> substr[4];
|
||||
if (substr[2].compare("array") == 0) isdense = true;
|
||||
if (substr[3].compare("complex") == 0) iscomplex = true;
|
||||
if (substr[4].compare("symmetric") == 0)
|
||||
sym = Symmetric;
|
||||
else if (substr[4].compare("Hermitian") == 0)
|
||||
sym = SelfAdjoint;
|
||||
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* \ingroup SparseExtra_Module
|
||||
* @brief Loads a sparse matrix from a matrixmarket format file.
|
||||
*
|
||||
*
|
||||
* @tparam SparseMatrixType to read into, symmetries are not supported
|
||||
* @param mat SparseMatrix to read into, current values are overwritten
|
||||
* @param filename to parse matrix from
|
||||
* @return returns true if file exists. Returns false if the parsing did not succeed.
|
||||
*/
|
||||
template<typename SparseMatrixType>
|
||||
bool loadMarket(SparseMatrixType& mat, const std::string& filename)
|
||||
{
|
||||
template <typename SparseMatrixType>
|
||||
bool loadMarket(SparseMatrixType& mat, const std::string& filename) {
|
||||
typedef typename SparseMatrixType::Scalar Scalar;
|
||||
typedef typename SparseMatrixType::StorageIndex StorageIndex;
|
||||
std::ifstream input(filename.c_str(),std::ios::in);
|
||||
if(!input)
|
||||
return false;
|
||||
std::ifstream input(filename.c_str(), std::ios::in);
|
||||
if (!input) return false;
|
||||
|
||||
char rdbuffer[4096];
|
||||
input.rdbuf()->pubsetbuf(rdbuffer, 4096);
|
||||
|
||||
|
||||
const int maxBuffersize = 2048;
|
||||
char buffer[maxBuffersize];
|
||||
|
||||
|
||||
bool readsizes = false;
|
||||
|
||||
typedef Triplet<Scalar,StorageIndex> T;
|
||||
typedef Triplet<Scalar, StorageIndex> T;
|
||||
std::vector<T> elements;
|
||||
|
||||
|
||||
Index M(-1), N(-1), NNZ(-1);
|
||||
Index count = 0;
|
||||
while(input.getline(buffer, maxBuffersize))
|
||||
{
|
||||
// skip comments
|
||||
//NOTE An appropriate test should be done on the header to get the symmetry
|
||||
if(buffer[0]=='%')
|
||||
continue;
|
||||
while (input.getline(buffer, maxBuffersize)) {
|
||||
// skip comments
|
||||
// NOTE An appropriate test should be done on the header to get the symmetry
|
||||
if (buffer[0] == '%') continue;
|
||||
|
||||
if(!readsizes)
|
||||
{
|
||||
if (!readsizes) {
|
||||
std::stringstream line(buffer);
|
||||
line >> M >> N >> NNZ;
|
||||
if(M > 0 && N > 0)
|
||||
{
|
||||
if (M > 0 && N > 0) {
|
||||
readsizes = true;
|
||||
mat.resize(M,N);
|
||||
mat.resize(M, N);
|
||||
mat.reserve(NNZ);
|
||||
elements.reserve(NNZ);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
StorageIndex i(-1), j(-1);
|
||||
Scalar value;
|
||||
Scalar value;
|
||||
internal::GetMarketLine(buffer, i, j, value);
|
||||
|
||||
i--;
|
||||
j--;
|
||||
if(i>=0 && j>=0 && i<M && j<N)
|
||||
{
|
||||
if (i >= 0 && j >= 0 && i < M && j < N) {
|
||||
++count;
|
||||
elements.push_back(T(i,j,value));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Invalid read: " << i << "," << j << "\n";
|
||||
elements.push_back(T(i, j, value));
|
||||
} else {
|
||||
std::cerr << "Invalid read: " << i << "," << j << "\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mat.setFromTriplets(elements.begin(), elements.end());
|
||||
if(count!=NNZ){
|
||||
if (count != NNZ) {
|
||||
std::cerr << count << "!=" << NNZ << "\n";
|
||||
return false;
|
||||
}
|
||||
@@ -223,68 +212,66 @@ bool loadMarket(SparseMatrixType& mat, const std::string& filename)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \ingroup SparseExtra_Module
|
||||
* @brief Loads a dense Matrix or Vector from a matrixmarket file. If a statically sized matrix has to be parsed and the file contains the wrong dimensions it is undefined behaviour.
|
||||
*
|
||||
* @brief Loads a dense Matrix or Vector from a matrixmarket file. If a statically sized matrix has to be parsed and the
|
||||
* file contains the wrong dimensions it is undefined behaviour.
|
||||
*
|
||||
* @tparam DenseMatrixType to read into
|
||||
* @param mat DenseMatrix to read into, current values are overwritten, symmetries are not supported
|
||||
* @param filename to parse matrix from
|
||||
* @return true if parsing was successful. Returns false if the parsing did not succeed.
|
||||
*/
|
||||
template<typename DenseType>
|
||||
bool loadMarketDense(DenseType& mat, const std::string& filename)
|
||||
{
|
||||
typedef typename DenseType::Scalar Scalar;
|
||||
template <typename DenseType>
|
||||
bool loadMarketDense(DenseType& mat, const std::string& filename) {
|
||||
typedef typename DenseType::Scalar Scalar;
|
||||
std::ifstream in(filename.c_str(), std::ios::in);
|
||||
if(!in)
|
||||
return false;
|
||||
|
||||
std::string line;
|
||||
Index rows(0), cols(0);
|
||||
do
|
||||
{ // Skip comments
|
||||
std::getline(in, line); eigen_assert(in.good());
|
||||
if (!in) return false;
|
||||
|
||||
std::string line;
|
||||
Index rows(0), cols(0);
|
||||
do { // Skip comments
|
||||
std::getline(in, line);
|
||||
eigen_assert(in.good());
|
||||
} while (line[0] == '%');
|
||||
std::istringstream newline(line);
|
||||
newline >> rows >> cols;
|
||||
newline >> rows >> cols;
|
||||
|
||||
bool sizes_not_positive=(rows<1 || cols<1);
|
||||
bool sizes_not_positive = (rows < 1 || cols < 1);
|
||||
bool wrong_input_rows = (DenseType::MaxRowsAtCompileTime != Dynamic && rows > DenseType::MaxRowsAtCompileTime) ||
|
||||
(DenseType::RowsAtCompileTime!=Dynamic && rows!=DenseType::RowsAtCompileTime);
|
||||
(DenseType::RowsAtCompileTime != Dynamic && rows != DenseType::RowsAtCompileTime);
|
||||
bool wrong_input_cols = (DenseType::MaxColsAtCompileTime != Dynamic && cols > DenseType::MaxColsAtCompileTime) ||
|
||||
(DenseType::ColsAtCompileTime!=Dynamic && cols!=DenseType::ColsAtCompileTime);
|
||||
(DenseType::ColsAtCompileTime != Dynamic && cols != DenseType::ColsAtCompileTime);
|
||||
|
||||
if(sizes_not_positive || wrong_input_rows || wrong_input_cols){
|
||||
if(sizes_not_positive){
|
||||
std::cerr<< "non-positive row or column size in file" << filename << "\n";
|
||||
}else{
|
||||
std::cerr<< "Input matrix can not be resized to"<<rows<<" x "<<cols<< "as given in " << filename << "\n";
|
||||
if (sizes_not_positive || wrong_input_rows || wrong_input_cols) {
|
||||
if (sizes_not_positive) {
|
||||
std::cerr << "non-positive row or column size in file" << filename << "\n";
|
||||
} else {
|
||||
std::cerr << "Input matrix can not be resized to" << rows << " x " << cols << "as given in " << filename << "\n";
|
||||
}
|
||||
in.close();
|
||||
return false;
|
||||
}
|
||||
|
||||
mat.resize(rows,cols);
|
||||
mat.resize(rows, cols);
|
||||
Index row = 0;
|
||||
Index col = 0;
|
||||
Index n=0;
|
||||
Scalar value;
|
||||
while ( std::getline(in, line) && (row < rows) && (col < cols)){
|
||||
internal::GetDenseElt(line, value);
|
||||
//matrixmarket format is column major
|
||||
mat(row,col) = value;
|
||||
Index col = 0;
|
||||
Index n = 0;
|
||||
Scalar value;
|
||||
while (std::getline(in, line) && (row < rows) && (col < cols)) {
|
||||
internal::GetDenseElt(line, value);
|
||||
// matrixmarket format is column major
|
||||
mat(row, col) = value;
|
||||
row++;
|
||||
if(row==rows){
|
||||
row=0;
|
||||
if (row == rows) {
|
||||
row = 0;
|
||||
col++;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
in.close();
|
||||
if (n!=mat.size()){
|
||||
std::cerr<< "Unable to read all elements from file " << filename << "\n";
|
||||
if (n != mat.size()) {
|
||||
std::cerr << "Unable to read all elements from file " << filename << "\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -293,94 +280,86 @@ bool loadMarketDense(DenseType& mat, const std::string& filename)
|
||||
* \ingroup SparseExtra_Module
|
||||
* @brief Same functionality as loadMarketDense, deprecated
|
||||
*/
|
||||
template<typename VectorType>
|
||||
bool loadMarketVector(VectorType& vec, const std::string& filename)
|
||||
{
|
||||
return loadMarketDense(vec, filename);
|
||||
template <typename VectorType>
|
||||
bool loadMarketVector(VectorType& vec, const std::string& filename) {
|
||||
return loadMarketDense(vec, filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* \ingroup SparseExtra_Module
|
||||
* @brief writes a sparse Matrix to a marketmarket format file
|
||||
*
|
||||
*
|
||||
* @tparam SparseMatrixType to write to file
|
||||
* @param mat matrix to write to file
|
||||
* @param filename filename to write to
|
||||
* @param filename filename to write to
|
||||
* @param sym at the moment no symmetry operations are supported
|
||||
* @return true if writing succeeded
|
||||
*/
|
||||
template<typename SparseMatrixType>
|
||||
bool saveMarket(const SparseMatrixType& mat, const std::string& filename, int sym = 0)
|
||||
{
|
||||
template <typename SparseMatrixType>
|
||||
bool saveMarket(const SparseMatrixType& mat, const std::string& filename, int sym = 0) {
|
||||
typedef typename SparseMatrixType::Scalar Scalar;
|
||||
typedef typename SparseMatrixType::RealScalar RealScalar;
|
||||
std::ofstream out(filename.c_str(),std::ios::out);
|
||||
if(!out)
|
||||
return false;
|
||||
|
||||
std::ofstream out(filename.c_str(), std::ios::out);
|
||||
if (!out) return false;
|
||||
|
||||
out.flags(std::ios_base::scientific);
|
||||
out.precision(std::numeric_limits<RealScalar>::digits10 + 2);
|
||||
std::string header;
|
||||
internal::putMarketHeader<Scalar>(header, sym);
|
||||
out << header << std::endl;
|
||||
std::string header;
|
||||
internal::putMarketHeader<Scalar>(header, sym);
|
||||
out << header << std::endl;
|
||||
out << mat.rows() << " " << mat.cols() << " " << mat.nonZeros() << "\n";
|
||||
int count = 0;
|
||||
for(int j=0; j<mat.outerSize(); ++j)
|
||||
for(typename SparseMatrixType::InnerIterator it(mat,j); it; ++it)
|
||||
{
|
||||
++ count;
|
||||
internal::PutMatrixElt(it.value(), it.row()+1, it.col()+1, out);
|
||||
for (int j = 0; j < mat.outerSize(); ++j)
|
||||
for (typename SparseMatrixType::InnerIterator it(mat, j); it; ++it) {
|
||||
++count;
|
||||
internal::PutMatrixElt(it.value(), it.row() + 1, it.col() + 1, out);
|
||||
}
|
||||
out.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \ingroup SparseExtra_Module
|
||||
* @brief writes a dense Matrix or vector to a marketmarket format file
|
||||
*
|
||||
*
|
||||
* @tparam DenseMatrixType to write to file
|
||||
* @param mat matrix to write to file
|
||||
* @param filename filename to write to
|
||||
* @param filename filename to write to
|
||||
* @return true if writing succeeded
|
||||
*/
|
||||
|
||||
template<typename DenseType>
|
||||
bool saveMarketDense (const DenseType& mat, const std::string& filename)
|
||||
{
|
||||
typedef typename DenseType::Scalar Scalar;
|
||||
typedef typename DenseType::RealScalar RealScalar;
|
||||
std::ofstream out(filename.c_str(),std::ios::out);
|
||||
if(!out)
|
||||
return false;
|
||||
|
||||
template <typename DenseType>
|
||||
bool saveMarketDense(const DenseType& mat, const std::string& filename) {
|
||||
typedef typename DenseType::Scalar Scalar;
|
||||
typedef typename DenseType::RealScalar RealScalar;
|
||||
std::ofstream out(filename.c_str(), std::ios::out);
|
||||
if (!out) return false;
|
||||
|
||||
out.flags(std::ios_base::scientific);
|
||||
out.precision(std::numeric_limits<RealScalar>::digits10 + 2);
|
||||
if(internal::is_same<Scalar, std::complex<float> >::value || internal::is_same<Scalar, std::complex<double> >::value)
|
||||
out << "%%MatrixMarket matrix array complex general\n";
|
||||
if (internal::is_same<Scalar, std::complex<float> >::value || internal::is_same<Scalar, std::complex<double> >::value)
|
||||
out << "%%MatrixMarket matrix array complex general\n";
|
||||
else
|
||||
out << "%%MatrixMarket matrix array real general\n";
|
||||
out << mat.rows() << " "<< mat.cols() << "\n";
|
||||
for (Index i=0; i < mat.cols(); i++){
|
||||
for (Index j=0; j < mat.rows(); j++){
|
||||
internal::putDenseElt(mat(j,i), out);
|
||||
out << "%%MatrixMarket matrix array real general\n";
|
||||
out << mat.rows() << " " << mat.cols() << "\n";
|
||||
for (Index i = 0; i < mat.cols(); i++) {
|
||||
for (Index j = 0; j < mat.rows(); j++) {
|
||||
internal::putDenseElt(mat(j, i), out);
|
||||
}
|
||||
}
|
||||
out.close();
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* \ingroup SparseExtra_Module
|
||||
* @brief Same functionality as saveMarketDense, deprecated
|
||||
*/
|
||||
template<typename VectorType>
|
||||
bool saveMarketVector (const VectorType& vec, const std::string& filename)
|
||||
{
|
||||
template <typename VectorType>
|
||||
bool saveMarketVector(const VectorType& vec, const std::string& filename) {
|
||||
return saveMarketDense(vec, filename);
|
||||
}
|
||||
|
||||
} // end namespace Eigen
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif // EIGEN_SPARSE_MARKET_IO_H
|
||||
#endif // EIGEN_SPARSE_MARKET_IO_H
|
||||
|
||||
@@ -16,235 +16,205 @@
|
||||
|
||||
namespace Eigen {
|
||||
|
||||
enum {
|
||||
SPD = 0x100,
|
||||
NonSymmetric = 0x0
|
||||
};
|
||||
enum { SPD = 0x100, NonSymmetric = 0x0 };
|
||||
|
||||
/**
|
||||
/**
|
||||
* @brief Iterator to browse matrices from a specified folder
|
||||
*
|
||||
* This is used to load all the matrices from a folder.
|
||||
*
|
||||
* This is used to load all the matrices from a folder.
|
||||
* The matrices should be in Matrix Market format
|
||||
* It is assumed that the matrices are named as matname.mtx
|
||||
* and matname_SPD.mtx if the matrix is Symmetric and positive definite (or Hermitian)
|
||||
* The right hand side vectors are loaded as well, if they exist.
|
||||
* They should be named as matname_b.mtx.
|
||||
* They should be named as matname_b.mtx.
|
||||
* Note that the right hand side for a SPD matrix is named as matname_SPD_b.mtx
|
||||
*
|
||||
*
|
||||
* Sometimes a reference solution is available. In this case, it should be named as matname_x.mtx
|
||||
*
|
||||
*
|
||||
* Sample code
|
||||
* \code
|
||||
*
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* \tparam Scalar The scalar type
|
||||
*
|
||||
* \tparam Scalar The scalar type
|
||||
*/
|
||||
template <typename Scalar>
|
||||
class MatrixMarketIterator
|
||||
{
|
||||
typedef typename NumTraits<Scalar>::Real RealScalar;
|
||||
public:
|
||||
typedef Matrix<Scalar,Dynamic,1> VectorType;
|
||||
typedef SparseMatrix<Scalar,ColMajor> MatrixType;
|
||||
|
||||
public:
|
||||
MatrixMarketIterator(const std::string &folder)
|
||||
: m_sym(0), m_isvalid(false), m_matIsLoaded(false), m_hasRhs(false), m_hasrefX(false), m_folder(folder)
|
||||
{
|
||||
m_folder_id = opendir(folder.c_str());
|
||||
if(m_folder_id)
|
||||
Getnextvalidmatrix();
|
||||
}
|
||||
|
||||
~MatrixMarketIterator()
|
||||
{
|
||||
if (m_folder_id) closedir(m_folder_id);
|
||||
}
|
||||
|
||||
inline MatrixMarketIterator& operator++()
|
||||
{
|
||||
m_matIsLoaded = false;
|
||||
m_hasrefX = false;
|
||||
m_hasRhs = false;
|
||||
Getnextvalidmatrix();
|
||||
return *this;
|
||||
}
|
||||
inline operator bool() const { return m_isvalid;}
|
||||
|
||||
/** Return the sparse matrix corresponding to the current file */
|
||||
inline MatrixType& matrix()
|
||||
{
|
||||
// Read the matrix
|
||||
if (m_matIsLoaded) return m_mat;
|
||||
|
||||
std::string matrix_file = m_folder + "/" + m_matname + ".mtx";
|
||||
if ( !loadMarket(m_mat, matrix_file))
|
||||
{
|
||||
std::cerr << "Warning loadMarket failed when loading \"" << matrix_file << "\"" << std::endl;
|
||||
m_matIsLoaded = false;
|
||||
return m_mat;
|
||||
}
|
||||
m_matIsLoaded = true;
|
||||
class MatrixMarketIterator {
|
||||
typedef typename NumTraits<Scalar>::Real RealScalar;
|
||||
|
||||
if (m_sym != NonSymmetric)
|
||||
{
|
||||
// Check whether we need to restore a full matrix:
|
||||
RealScalar diag_norm = m_mat.diagonal().norm();
|
||||
RealScalar lower_norm = m_mat.template triangularView<Lower>().norm();
|
||||
RealScalar upper_norm = m_mat.template triangularView<Upper>().norm();
|
||||
if(lower_norm>diag_norm && upper_norm==diag_norm)
|
||||
{
|
||||
// only the lower part is stored
|
||||
MatrixType tmp(m_mat);
|
||||
m_mat = tmp.template selfadjointView<Lower>();
|
||||
}
|
||||
else if(upper_norm>diag_norm && lower_norm==diag_norm)
|
||||
{
|
||||
// only the upper part is stored
|
||||
MatrixType tmp(m_mat);
|
||||
m_mat = tmp.template selfadjointView<Upper>();
|
||||
}
|
||||
}
|
||||
return m_mat;
|
||||
public:
|
||||
typedef Matrix<Scalar, Dynamic, 1> VectorType;
|
||||
typedef SparseMatrix<Scalar, ColMajor> MatrixType;
|
||||
|
||||
public:
|
||||
MatrixMarketIterator(const std::string& folder)
|
||||
: m_sym(0), m_isvalid(false), m_matIsLoaded(false), m_hasRhs(false), m_hasrefX(false), m_folder(folder) {
|
||||
m_folder_id = opendir(folder.c_str());
|
||||
if (m_folder_id) Getnextvalidmatrix();
|
||||
}
|
||||
|
||||
~MatrixMarketIterator() {
|
||||
if (m_folder_id) closedir(m_folder_id);
|
||||
}
|
||||
|
||||
inline MatrixMarketIterator& operator++() {
|
||||
m_matIsLoaded = false;
|
||||
m_hasrefX = false;
|
||||
m_hasRhs = false;
|
||||
Getnextvalidmatrix();
|
||||
return *this;
|
||||
}
|
||||
inline operator bool() const { return m_isvalid; }
|
||||
|
||||
/** Return the sparse matrix corresponding to the current file */
|
||||
inline MatrixType& matrix() {
|
||||
// Read the matrix
|
||||
if (m_matIsLoaded) return m_mat;
|
||||
|
||||
std::string matrix_file = m_folder + "/" + m_matname + ".mtx";
|
||||
if (!loadMarket(m_mat, matrix_file)) {
|
||||
std::cerr << "Warning loadMarket failed when loading \"" << matrix_file << "\"" << std::endl;
|
||||
m_matIsLoaded = false;
|
||||
return m_mat;
|
||||
}
|
||||
|
||||
/** Return the right hand side corresponding to the current matrix.
|
||||
* If the rhs file is not provided, a random rhs is generated
|
||||
*/
|
||||
inline VectorType& rhs()
|
||||
{
|
||||
// Get the right hand side
|
||||
if (m_hasRhs) return m_rhs;
|
||||
|
||||
std::string rhs_file;
|
||||
rhs_file = m_folder + "/" + m_matname + "_b.mtx"; // The pattern is matname_b.mtx
|
||||
m_hasRhs = Fileexists(rhs_file);
|
||||
if (m_hasRhs)
|
||||
{
|
||||
m_rhs.resize(m_mat.cols());
|
||||
m_hasRhs = loadMarketVector(m_rhs, rhs_file);
|
||||
}
|
||||
if (!m_hasRhs)
|
||||
{
|
||||
// Generate a random right hand side
|
||||
if (!m_matIsLoaded) this->matrix();
|
||||
m_refX.resize(m_mat.cols());
|
||||
m_refX.setRandom();
|
||||
m_rhs = m_mat * m_refX;
|
||||
m_hasrefX = true;
|
||||
m_hasRhs = true;
|
||||
}
|
||||
return m_rhs;
|
||||
}
|
||||
|
||||
/** Return a reference solution
|
||||
* If it is not provided and if the right hand side is not available
|
||||
* then refX is randomly generated such that A*refX = b
|
||||
* where A and b are the matrix and the rhs.
|
||||
* Note that when a rhs is provided, refX is not available
|
||||
*/
|
||||
inline VectorType& refX()
|
||||
{
|
||||
// Check if a reference solution is provided
|
||||
if (m_hasrefX) return m_refX;
|
||||
|
||||
std::string lhs_file;
|
||||
lhs_file = m_folder + "/" + m_matname + "_x.mtx";
|
||||
m_hasrefX = Fileexists(lhs_file);
|
||||
if (m_hasrefX)
|
||||
{
|
||||
m_refX.resize(m_mat.cols());
|
||||
m_hasrefX = loadMarketVector(m_refX, lhs_file);
|
||||
}
|
||||
else
|
||||
m_refX.resize(0);
|
||||
return m_refX;
|
||||
}
|
||||
|
||||
inline std::string& matname() { return m_matname; }
|
||||
|
||||
inline int sym() { return m_sym; }
|
||||
|
||||
bool hasRhs() {return m_hasRhs; }
|
||||
bool hasrefX() {return m_hasrefX; }
|
||||
bool isFolderValid() { return bool(m_folder_id); }
|
||||
|
||||
protected:
|
||||
|
||||
inline bool Fileexists(std::string file)
|
||||
{
|
||||
std::ifstream file_id(file.c_str());
|
||||
if (!file_id.good() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
file_id.close();
|
||||
return true;
|
||||
m_matIsLoaded = true;
|
||||
|
||||
if (m_sym != NonSymmetric) {
|
||||
// Check whether we need to restore a full matrix:
|
||||
RealScalar diag_norm = m_mat.diagonal().norm();
|
||||
RealScalar lower_norm = m_mat.template triangularView<Lower>().norm();
|
||||
RealScalar upper_norm = m_mat.template triangularView<Upper>().norm();
|
||||
if (lower_norm > diag_norm && upper_norm == diag_norm) {
|
||||
// only the lower part is stored
|
||||
MatrixType tmp(m_mat);
|
||||
m_mat = tmp.template selfadjointView<Lower>();
|
||||
} else if (upper_norm > diag_norm && lower_norm == diag_norm) {
|
||||
// only the upper part is stored
|
||||
MatrixType tmp(m_mat);
|
||||
m_mat = tmp.template selfadjointView<Upper>();
|
||||
}
|
||||
}
|
||||
|
||||
void Getnextvalidmatrix( )
|
||||
{
|
||||
return m_mat;
|
||||
}
|
||||
|
||||
/** Return the right hand side corresponding to the current matrix.
|
||||
* If the rhs file is not provided, a random rhs is generated
|
||||
*/
|
||||
inline VectorType& rhs() {
|
||||
// Get the right hand side
|
||||
if (m_hasRhs) return m_rhs;
|
||||
|
||||
std::string rhs_file;
|
||||
rhs_file = m_folder + "/" + m_matname + "_b.mtx"; // The pattern is matname_b.mtx
|
||||
m_hasRhs = Fileexists(rhs_file);
|
||||
if (m_hasRhs) {
|
||||
m_rhs.resize(m_mat.cols());
|
||||
m_hasRhs = loadMarketVector(m_rhs, rhs_file);
|
||||
}
|
||||
if (!m_hasRhs) {
|
||||
// Generate a random right hand side
|
||||
if (!m_matIsLoaded) this->matrix();
|
||||
m_refX.resize(m_mat.cols());
|
||||
m_refX.setRandom();
|
||||
m_rhs = m_mat * m_refX;
|
||||
m_hasrefX = true;
|
||||
m_hasRhs = true;
|
||||
}
|
||||
return m_rhs;
|
||||
}
|
||||
|
||||
/** Return a reference solution
|
||||
* If it is not provided and if the right hand side is not available
|
||||
* then refX is randomly generated such that A*refX = b
|
||||
* where A and b are the matrix and the rhs.
|
||||
* Note that when a rhs is provided, refX is not available
|
||||
*/
|
||||
inline VectorType& refX() {
|
||||
// Check if a reference solution is provided
|
||||
if (m_hasrefX) return m_refX;
|
||||
|
||||
std::string lhs_file;
|
||||
lhs_file = m_folder + "/" + m_matname + "_x.mtx";
|
||||
m_hasrefX = Fileexists(lhs_file);
|
||||
if (m_hasrefX) {
|
||||
m_refX.resize(m_mat.cols());
|
||||
m_hasrefX = loadMarketVector(m_refX, lhs_file);
|
||||
} else
|
||||
m_refX.resize(0);
|
||||
return m_refX;
|
||||
}
|
||||
|
||||
inline std::string& matname() { return m_matname; }
|
||||
|
||||
inline int sym() { return m_sym; }
|
||||
|
||||
bool hasRhs() { return m_hasRhs; }
|
||||
bool hasrefX() { return m_hasrefX; }
|
||||
bool isFolderValid() { return bool(m_folder_id); }
|
||||
|
||||
protected:
|
||||
inline bool Fileexists(std::string file) {
|
||||
std::ifstream file_id(file.c_str());
|
||||
if (!file_id.good()) {
|
||||
return false;
|
||||
} else {
|
||||
file_id.close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void Getnextvalidmatrix() {
|
||||
m_isvalid = false;
|
||||
// Here, we return with the next valid matrix in the folder
|
||||
while ((m_curs_id = readdir(m_folder_id)) != NULL) {
|
||||
m_isvalid = false;
|
||||
// Here, we return with the next valid matrix in the folder
|
||||
while ( (m_curs_id = readdir(m_folder_id)) != NULL) {
|
||||
m_isvalid = false;
|
||||
std::string curfile;
|
||||
curfile = m_folder + "/" + m_curs_id->d_name;
|
||||
// Discard if it is a folder
|
||||
if (m_curs_id->d_type == DT_DIR) continue; //FIXME This may not be available on non BSD systems
|
||||
// struct stat st_buf;
|
||||
// stat (curfile.c_str(), &st_buf);
|
||||
// if (S_ISDIR(st_buf.st_mode)) continue;
|
||||
|
||||
// Determine from the header if it is a matrix or a right hand side
|
||||
bool isvector,iscomplex=false;
|
||||
if(!getMarketHeader(curfile,m_sym,iscomplex,isvector)) continue;
|
||||
if(isvector) continue;
|
||||
if (!iscomplex)
|
||||
{
|
||||
if(internal::is_same<Scalar, std::complex<float> >::value || internal::is_same<Scalar, std::complex<double> >::value)
|
||||
continue;
|
||||
}
|
||||
if (iscomplex)
|
||||
{
|
||||
if(internal::is_same<Scalar, float>::value || internal::is_same<Scalar, double>::value)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Get the matrix name
|
||||
std::string filename = m_curs_id->d_name;
|
||||
m_matname = filename.substr(0, filename.length()-4);
|
||||
|
||||
// Find if the matrix is SPD
|
||||
size_t found = m_matname.find("SPD");
|
||||
if( (found!=std::string::npos) && (m_sym != NonSymmetric) )
|
||||
m_sym = SPD;
|
||||
|
||||
m_isvalid = true;
|
||||
break;
|
||||
std::string curfile;
|
||||
curfile = m_folder + "/" + m_curs_id->d_name;
|
||||
// Discard if it is a folder
|
||||
if (m_curs_id->d_type == DT_DIR) continue; // FIXME This may not be available on non BSD systems
|
||||
// struct stat st_buf;
|
||||
// stat (curfile.c_str(), &st_buf);
|
||||
// if (S_ISDIR(st_buf.st_mode)) continue;
|
||||
|
||||
// Determine from the header if it is a matrix or a right hand side
|
||||
bool isvector, iscomplex = false;
|
||||
if (!getMarketHeader(curfile, m_sym, iscomplex, isvector)) continue;
|
||||
if (isvector) continue;
|
||||
if (!iscomplex) {
|
||||
if (internal::is_same<Scalar, std::complex<float> >::value ||
|
||||
internal::is_same<Scalar, std::complex<double> >::value)
|
||||
continue;
|
||||
}
|
||||
if (iscomplex) {
|
||||
if (internal::is_same<Scalar, float>::value || internal::is_same<Scalar, double>::value) continue;
|
||||
}
|
||||
|
||||
// Get the matrix name
|
||||
std::string filename = m_curs_id->d_name;
|
||||
m_matname = filename.substr(0, filename.length() - 4);
|
||||
|
||||
// Find if the matrix is SPD
|
||||
size_t found = m_matname.find("SPD");
|
||||
if ((found != std::string::npos) && (m_sym != NonSymmetric)) m_sym = SPD;
|
||||
|
||||
m_isvalid = true;
|
||||
break;
|
||||
}
|
||||
int m_sym; // Symmetry of the matrix
|
||||
MatrixType m_mat; // Current matrix
|
||||
VectorType m_rhs; // Current vector
|
||||
VectorType m_refX; // The reference solution, if exists
|
||||
std::string m_matname; // Matrix Name
|
||||
bool m_isvalid;
|
||||
bool m_matIsLoaded; // Determine if the matrix has already been loaded from the file
|
||||
bool m_hasRhs; // The right hand side exists
|
||||
bool m_hasrefX; // A reference solution is provided
|
||||
std::string m_folder;
|
||||
DIR * m_folder_id;
|
||||
struct dirent *m_curs_id;
|
||||
|
||||
}
|
||||
int m_sym; // Symmetry of the matrix
|
||||
MatrixType m_mat; // Current matrix
|
||||
VectorType m_rhs; // Current vector
|
||||
VectorType m_refX; // The reference solution, if exists
|
||||
std::string m_matname; // Matrix Name
|
||||
bool m_isvalid;
|
||||
bool m_matIsLoaded; // Determine if the matrix has already been loaded from the file
|
||||
bool m_hasRhs; // The right hand side exists
|
||||
bool m_hasrefX; // A reference solution is provided
|
||||
std::string m_folder;
|
||||
DIR* m_folder_id;
|
||||
struct dirent* m_curs_id;
|
||||
};
|
||||
|
||||
} // end namespace Eigen
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#define EIGEN_RANDOMSETTER_H
|
||||
|
||||
#if defined(EIGEN_GOOGLEHASH_SUPPORT)
|
||||
// Ensure the ::google namespace exists, required for checking existence of
|
||||
// Ensure the ::google namespace exists, required for checking existence of
|
||||
// ::google::dense_hash_map and ::google::sparse_hash_map.
|
||||
namespace google {}
|
||||
#endif
|
||||
@@ -22,31 +22,26 @@ namespace google {}
|
||||
namespace Eigen {
|
||||
|
||||
/** Represents a std::map
|
||||
*
|
||||
* \see RandomSetter
|
||||
*/
|
||||
template<typename Scalar> struct StdMapTraits
|
||||
{
|
||||
*
|
||||
* \see RandomSetter
|
||||
*/
|
||||
template <typename Scalar>
|
||||
struct StdMapTraits {
|
||||
typedef int KeyType;
|
||||
typedef std::map<KeyType,Scalar> Type;
|
||||
enum {
|
||||
IsSorted = 1
|
||||
};
|
||||
typedef std::map<KeyType, Scalar> Type;
|
||||
enum { IsSorted = 1 };
|
||||
|
||||
static void setInvalidKey(Type&, const KeyType&) {}
|
||||
};
|
||||
|
||||
|
||||
/** Represents a std::unordered_map
|
||||
* \see RandomSetter
|
||||
*/
|
||||
template<typename Scalar> struct StdUnorderedMapTraits
|
||||
{
|
||||
* \see RandomSetter
|
||||
*/
|
||||
template <typename Scalar>
|
||||
struct StdUnorderedMapTraits {
|
||||
typedef int KeyType;
|
||||
typedef std::unordered_map<KeyType,Scalar> Type;
|
||||
enum {
|
||||
IsSorted = 0
|
||||
};
|
||||
typedef std::unordered_map<KeyType, Scalar> Type;
|
||||
enum { IsSorted = 0 };
|
||||
|
||||
static void setInvalidKey(Type&, const KeyType&) {}
|
||||
};
|
||||
@@ -54,283 +49,255 @@ template<typename Scalar> struct StdUnorderedMapTraits
|
||||
#if defined(EIGEN_GOOGLEHASH_SUPPORT)
|
||||
|
||||
namespace google {
|
||||
|
||||
|
||||
// Namespace work-around, since sometimes dense_hash_map and sparse_hash_map
|
||||
// are in the global namespace, and other times they are under ::google.
|
||||
using namespace ::google;
|
||||
|
||||
template<typename KeyType, typename Scalar>
|
||||
template <typename KeyType, typename Scalar>
|
||||
struct DenseHashMap {
|
||||
typedef dense_hash_map<KeyType, Scalar> type;
|
||||
};
|
||||
|
||||
template<typename KeyType, typename Scalar>
|
||||
template <typename KeyType, typename Scalar>
|
||||
struct SparseHashMap {
|
||||
typedef sparse_hash_map<KeyType, Scalar> type;
|
||||
};
|
||||
|
||||
} // namespace google
|
||||
} // namespace google
|
||||
|
||||
/** Represents a google::dense_hash_map
|
||||
*
|
||||
* \see RandomSetter
|
||||
*/
|
||||
template<typename Scalar> struct GoogleDenseHashMapTraits
|
||||
{
|
||||
*
|
||||
* \see RandomSetter
|
||||
*/
|
||||
template <typename Scalar>
|
||||
struct GoogleDenseHashMapTraits {
|
||||
typedef int KeyType;
|
||||
typedef typename google::DenseHashMap<KeyType,Scalar>::type Type;
|
||||
enum {
|
||||
IsSorted = 0
|
||||
};
|
||||
typedef typename google::DenseHashMap<KeyType, Scalar>::type Type;
|
||||
enum { IsSorted = 0 };
|
||||
|
||||
static void setInvalidKey(Type& map, const KeyType& k)
|
||||
{ map.set_empty_key(k); }
|
||||
static void setInvalidKey(Type& map, const KeyType& k) { map.set_empty_key(k); }
|
||||
};
|
||||
|
||||
/** Represents a google::sparse_hash_map
|
||||
*
|
||||
* \see RandomSetter
|
||||
*/
|
||||
template<typename Scalar> struct GoogleSparseHashMapTraits
|
||||
{
|
||||
*
|
||||
* \see RandomSetter
|
||||
*/
|
||||
template <typename Scalar>
|
||||
struct GoogleSparseHashMapTraits {
|
||||
typedef int KeyType;
|
||||
typedef typename google::SparseHashMap<KeyType,Scalar>::type Type;
|
||||
enum {
|
||||
IsSorted = 0
|
||||
};
|
||||
typedef typename google::SparseHashMap<KeyType, Scalar>::type Type;
|
||||
enum { IsSorted = 0 };
|
||||
|
||||
static void setInvalidKey(Type&, const KeyType&) {}
|
||||
};
|
||||
#endif
|
||||
|
||||
/** \class RandomSetter
|
||||
* \ingroup SparseExtra_Module
|
||||
* \brief The RandomSetter is a wrapper object allowing to set/update a sparse matrix with random access
|
||||
*
|
||||
* \tparam SparseMatrixType the type of the sparse matrix we are updating
|
||||
* \tparam MapTraits a traits class representing the map implementation used for the temporary sparse storage.
|
||||
* Its default value depends on the system.
|
||||
* \tparam OuterPacketBits defines the number of rows (or columns) manage by a single map object
|
||||
* as a power of two exponent.
|
||||
*
|
||||
* This class temporarily represents a sparse matrix object using a generic map implementation allowing for
|
||||
* efficient random access. The conversion from the compressed representation to a hash_map object is performed
|
||||
* in the RandomSetter constructor, while the sparse matrix is updated back at destruction time. This strategy
|
||||
* suggest the use of nested blocks as in this example:
|
||||
*
|
||||
* \code
|
||||
* SparseMatrix<double> m(rows,cols);
|
||||
* {
|
||||
* RandomSetter<SparseMatrix<double> > w(m);
|
||||
* // don't use m but w instead with read/write random access to the coefficients:
|
||||
* for(;;)
|
||||
* w(rand(),rand()) = rand;
|
||||
* }
|
||||
* // when w is deleted, the data are copied back to m
|
||||
* // and m is ready to use.
|
||||
* \endcode
|
||||
*
|
||||
* Since hash_map objects are not fully sorted, representing a full matrix as a single hash_map would
|
||||
* involve a big and costly sort to update the compressed matrix back. To overcome this issue, a RandomSetter
|
||||
* use multiple hash_map, each representing 2^OuterPacketBits columns or rows according to the storage order.
|
||||
* To reach optimal performance, this value should be adjusted according to the average number of nonzeros
|
||||
* per rows/columns.
|
||||
*
|
||||
* The possible values for the template parameter MapTraits are:
|
||||
* - \b StdMapTraits: corresponds to std::map. (does not perform very well)
|
||||
* - \b StdUnorderedMapTraits: corresponds to std::unordered_map
|
||||
* - \b GoogleDenseHashMapTraits: corresponds to google::dense_hash_map (best efficiency, reasonable memory consumption)
|
||||
* - \b GoogleSparseHashMapTraits: corresponds to google::sparse_hash_map (best memory consumption, relatively good performance)
|
||||
*
|
||||
* The default map implementation depends on the availability, and the preferred order is:
|
||||
* GoogleSparseHashMapTraits, StdUnorderedMapTraits, and finally StdMapTraits.
|
||||
*
|
||||
* For performance and memory consumption reasons it is highly recommended to use one of
|
||||
* Google's hash_map implementations. To enable the support for them, you must define
|
||||
* EIGEN_GOOGLEHASH_SUPPORT. This will include both <google/dense_hash_map> and
|
||||
* <google/sparse_hash_map> for you.
|
||||
*
|
||||
* \see https://github.com/sparsehash/sparsehash
|
||||
*/
|
||||
template<typename SparseMatrixType,
|
||||
template <typename T> class MapTraits =
|
||||
* \ingroup SparseExtra_Module
|
||||
* \brief The RandomSetter is a wrapper object allowing to set/update a sparse matrix with random access
|
||||
*
|
||||
* \tparam SparseMatrixType the type of the sparse matrix we are updating
|
||||
* \tparam MapTraits a traits class representing the map implementation used for the temporary sparse storage.
|
||||
* Its default value depends on the system.
|
||||
* \tparam OuterPacketBits defines the number of rows (or columns) manage by a single map object
|
||||
* as a power of two exponent.
|
||||
*
|
||||
* This class temporarily represents a sparse matrix object using a generic map implementation allowing for
|
||||
* efficient random access. The conversion from the compressed representation to a hash_map object is performed
|
||||
* in the RandomSetter constructor, while the sparse matrix is updated back at destruction time. This strategy
|
||||
* suggest the use of nested blocks as in this example:
|
||||
*
|
||||
* \code
|
||||
* SparseMatrix<double> m(rows,cols);
|
||||
* {
|
||||
* RandomSetter<SparseMatrix<double> > w(m);
|
||||
* // don't use m but w instead with read/write random access to the coefficients:
|
||||
* for(;;)
|
||||
* w(rand(),rand()) = rand;
|
||||
* }
|
||||
* // when w is deleted, the data are copied back to m
|
||||
* // and m is ready to use.
|
||||
* \endcode
|
||||
*
|
||||
* Since hash_map objects are not fully sorted, representing a full matrix as a single hash_map would
|
||||
* involve a big and costly sort to update the compressed matrix back. To overcome this issue, a RandomSetter
|
||||
* use multiple hash_map, each representing 2^OuterPacketBits columns or rows according to the storage order.
|
||||
* To reach optimal performance, this value should be adjusted according to the average number of nonzeros
|
||||
* per rows/columns.
|
||||
*
|
||||
* The possible values for the template parameter MapTraits are:
|
||||
* - \b StdMapTraits: corresponds to std::map. (does not perform very well)
|
||||
* - \b StdUnorderedMapTraits: corresponds to std::unordered_map
|
||||
* - \b GoogleDenseHashMapTraits: corresponds to google::dense_hash_map (best efficiency, reasonable memory
|
||||
* consumption)
|
||||
* - \b GoogleSparseHashMapTraits: corresponds to google::sparse_hash_map (best memory consumption, relatively good
|
||||
* performance)
|
||||
*
|
||||
* The default map implementation depends on the availability, and the preferred order is:
|
||||
* GoogleSparseHashMapTraits, StdUnorderedMapTraits, and finally StdMapTraits.
|
||||
*
|
||||
* For performance and memory consumption reasons it is highly recommended to use one of
|
||||
* Google's hash_map implementations. To enable the support for them, you must define
|
||||
* EIGEN_GOOGLEHASH_SUPPORT. This will include both <google/dense_hash_map> and
|
||||
* <google/sparse_hash_map> for you.
|
||||
*
|
||||
* \see https://github.com/sparsehash/sparsehash
|
||||
*/
|
||||
template <typename SparseMatrixType,
|
||||
template <typename T> class MapTraits =
|
||||
#if defined(EIGEN_GOOGLEHASH_SUPPORT)
|
||||
GoogleDenseHashMapTraits
|
||||
GoogleDenseHashMapTraits
|
||||
#else
|
||||
StdUnorderedMapTraits
|
||||
StdUnorderedMapTraits
|
||||
#endif
|
||||
,int OuterPacketBits = 6>
|
||||
class RandomSetter
|
||||
{
|
||||
typedef typename SparseMatrixType::Scalar Scalar;
|
||||
typedef typename SparseMatrixType::StorageIndex StorageIndex;
|
||||
,
|
||||
int OuterPacketBits = 6>
|
||||
class RandomSetter {
|
||||
typedef typename SparseMatrixType::Scalar Scalar;
|
||||
typedef typename SparseMatrixType::StorageIndex StorageIndex;
|
||||
|
||||
struct ScalarWrapper
|
||||
{
|
||||
ScalarWrapper() : value(0) {}
|
||||
Scalar value;
|
||||
};
|
||||
typedef typename MapTraits<ScalarWrapper>::KeyType KeyType;
|
||||
typedef typename MapTraits<ScalarWrapper>::Type HashMapType;
|
||||
static constexpr int OuterPacketMask = (1 << OuterPacketBits) - 1;
|
||||
enum {
|
||||
SwapStorage = 1 - MapTraits<ScalarWrapper>::IsSorted,
|
||||
TargetRowMajor = (SparseMatrixType::Flags & RowMajorBit) ? 1 : 0,
|
||||
SetterRowMajor = SwapStorage ? 1-TargetRowMajor : TargetRowMajor
|
||||
};
|
||||
struct ScalarWrapper {
|
||||
ScalarWrapper() : value(0) {}
|
||||
Scalar value;
|
||||
};
|
||||
typedef typename MapTraits<ScalarWrapper>::KeyType KeyType;
|
||||
typedef typename MapTraits<ScalarWrapper>::Type HashMapType;
|
||||
static constexpr int OuterPacketMask = (1 << OuterPacketBits) - 1;
|
||||
enum {
|
||||
SwapStorage = 1 - MapTraits<ScalarWrapper>::IsSorted,
|
||||
TargetRowMajor = (SparseMatrixType::Flags & RowMajorBit) ? 1 : 0,
|
||||
SetterRowMajor = SwapStorage ? 1 - TargetRowMajor : TargetRowMajor
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
/** Constructs a random setter object from the sparse matrix \a target
|
||||
*
|
||||
* Note that the initial value of \a target are imported. If you want to re-set
|
||||
* a sparse matrix from scratch, then you must set it to zero first using the
|
||||
* setZero() function.
|
||||
*/
|
||||
inline RandomSetter(SparseMatrixType& target)
|
||||
: mp_target(&target)
|
||||
{
|
||||
const Index outerSize = SwapStorage ? target.innerSize() : target.outerSize();
|
||||
const Index innerSize = SwapStorage ? target.outerSize() : target.innerSize();
|
||||
m_outerPackets = outerSize >> OuterPacketBits;
|
||||
if (outerSize&OuterPacketMask)
|
||||
m_outerPackets += 1;
|
||||
m_hashmaps = new HashMapType[m_outerPackets];
|
||||
// compute number of bits needed to store inner indices
|
||||
Index aux = innerSize - 1;
|
||||
m_keyBitsOffset = 0;
|
||||
while (aux)
|
||||
{
|
||||
++m_keyBitsOffset;
|
||||
aux = aux >> 1;
|
||||
}
|
||||
KeyType ik = (1<<(OuterPacketBits+m_keyBitsOffset));
|
||||
for (Index k=0; k<m_outerPackets; ++k)
|
||||
MapTraits<ScalarWrapper>::setInvalidKey(m_hashmaps[k],ik);
|
||||
|
||||
// insert current coeffs
|
||||
for (Index j=0; j<mp_target->outerSize(); ++j)
|
||||
for (typename SparseMatrixType::InnerIterator it(*mp_target,j); it; ++it)
|
||||
(*this)(TargetRowMajor?j:it.index(), TargetRowMajor?it.index():j) = it.value();
|
||||
public:
|
||||
/** Constructs a random setter object from the sparse matrix \a target
|
||||
*
|
||||
* Note that the initial value of \a target are imported. If you want to re-set
|
||||
* a sparse matrix from scratch, then you must set it to zero first using the
|
||||
* setZero() function.
|
||||
*/
|
||||
inline RandomSetter(SparseMatrixType& target) : mp_target(&target) {
|
||||
const Index outerSize = SwapStorage ? target.innerSize() : target.outerSize();
|
||||
const Index innerSize = SwapStorage ? target.outerSize() : target.innerSize();
|
||||
m_outerPackets = outerSize >> OuterPacketBits;
|
||||
if (outerSize & OuterPacketMask) m_outerPackets += 1;
|
||||
m_hashmaps = new HashMapType[m_outerPackets];
|
||||
// compute number of bits needed to store inner indices
|
||||
Index aux = innerSize - 1;
|
||||
m_keyBitsOffset = 0;
|
||||
while (aux) {
|
||||
++m_keyBitsOffset;
|
||||
aux = aux >> 1;
|
||||
}
|
||||
KeyType ik = (1 << (OuterPacketBits + m_keyBitsOffset));
|
||||
for (Index k = 0; k < m_outerPackets; ++k) MapTraits<ScalarWrapper>::setInvalidKey(m_hashmaps[k], ik);
|
||||
|
||||
/** Destructor updating back the sparse matrix target */
|
||||
~RandomSetter()
|
||||
// insert current coeffs
|
||||
for (Index j = 0; j < mp_target->outerSize(); ++j)
|
||||
for (typename SparseMatrixType::InnerIterator it(*mp_target, j); it; ++it)
|
||||
(*this)(TargetRowMajor ? j : it.index(), TargetRowMajor ? it.index() : j) = it.value();
|
||||
}
|
||||
|
||||
/** Destructor updating back the sparse matrix target */
|
||||
~RandomSetter() {
|
||||
KeyType keyBitsMask = (1 << m_keyBitsOffset) - 1;
|
||||
if (!SwapStorage) // also means the map is sorted
|
||||
{
|
||||
KeyType keyBitsMask = (1<<m_keyBitsOffset)-1;
|
||||
if (!SwapStorage) // also means the map is sorted
|
||||
{
|
||||
mp_target->setZero();
|
||||
mp_target->makeCompressed();
|
||||
mp_target->reserve(nonZeros());
|
||||
Index prevOuter = -1;
|
||||
for (Index k=0; k<m_outerPackets; ++k)
|
||||
{
|
||||
const Index outerOffset = (1<<OuterPacketBits) * k;
|
||||
typename HashMapType::iterator end = m_hashmaps[k].end();
|
||||
for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it!=end; ++it)
|
||||
{
|
||||
const Index outer = (it->first >> m_keyBitsOffset) + outerOffset;
|
||||
const Index inner = it->first & keyBitsMask;
|
||||
if (prevOuter!=outer)
|
||||
{
|
||||
for (Index j=prevOuter+1;j<=outer;++j)
|
||||
mp_target->startVec(j);
|
||||
prevOuter = outer;
|
||||
}
|
||||
mp_target->insertBackByOuterInner(outer, inner) = it->second.value;
|
||||
}
|
||||
}
|
||||
mp_target->finalize();
|
||||
}
|
||||
else
|
||||
{
|
||||
VectorXi positions(mp_target->outerSize());
|
||||
positions.setZero();
|
||||
// pass 1
|
||||
for (Index k=0; k<m_outerPackets; ++k)
|
||||
{
|
||||
typename HashMapType::iterator end = m_hashmaps[k].end();
|
||||
for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it!=end; ++it)
|
||||
{
|
||||
const Index outer = it->first & keyBitsMask;
|
||||
++positions[outer];
|
||||
}
|
||||
}
|
||||
// prefix sum
|
||||
StorageIndex count = 0;
|
||||
for (Index j=0; j<mp_target->outerSize(); ++j)
|
||||
{
|
||||
StorageIndex tmp = positions[j];
|
||||
mp_target->outerIndexPtr()[j] = count;
|
||||
positions[j] = count;
|
||||
count += tmp;
|
||||
}
|
||||
mp_target->makeCompressed();
|
||||
mp_target->outerIndexPtr()[mp_target->outerSize()] = count;
|
||||
mp_target->resizeNonZeros(count);
|
||||
// pass 2
|
||||
for (Index k=0; k<m_outerPackets; ++k)
|
||||
{
|
||||
const Index outerOffset = (1<<OuterPacketBits) * k;
|
||||
typename HashMapType::iterator end = m_hashmaps[k].end();
|
||||
for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it!=end; ++it)
|
||||
{
|
||||
const Index inner = (it->first >> m_keyBitsOffset) + outerOffset;
|
||||
const Index outer = it->first & keyBitsMask;
|
||||
// sorted insertion
|
||||
// Note that we have to deal with at most 2^OuterPacketBits unsorted coefficients,
|
||||
// moreover those 2^OuterPacketBits coeffs are likely to be sparse, an so only a
|
||||
// small fraction of them have to be sorted, whence the following simple procedure:
|
||||
Index posStart = mp_target->outerIndexPtr()[outer];
|
||||
Index i = (positions[outer]++) - 1;
|
||||
while ( (i >= posStart) && (mp_target->innerIndexPtr()[i] > inner) )
|
||||
{
|
||||
mp_target->valuePtr()[i+1] = mp_target->valuePtr()[i];
|
||||
mp_target->innerIndexPtr()[i+1] = mp_target->innerIndexPtr()[i];
|
||||
--i;
|
||||
}
|
||||
mp_target->innerIndexPtr()[i+1] = internal::convert_index<StorageIndex>(inner);
|
||||
mp_target->valuePtr()[i+1] = it->second.value;
|
||||
mp_target->setZero();
|
||||
mp_target->makeCompressed();
|
||||
mp_target->reserve(nonZeros());
|
||||
Index prevOuter = -1;
|
||||
for (Index k = 0; k < m_outerPackets; ++k) {
|
||||
const Index outerOffset = (1 << OuterPacketBits) * k;
|
||||
typename HashMapType::iterator end = m_hashmaps[k].end();
|
||||
for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it != end; ++it) {
|
||||
const Index outer = (it->first >> m_keyBitsOffset) + outerOffset;
|
||||
const Index inner = it->first & keyBitsMask;
|
||||
if (prevOuter != outer) {
|
||||
for (Index j = prevOuter + 1; j <= outer; ++j) mp_target->startVec(j);
|
||||
prevOuter = outer;
|
||||
}
|
||||
mp_target->insertBackByOuterInner(outer, inner) = it->second.value;
|
||||
}
|
||||
}
|
||||
mp_target->finalize();
|
||||
} else {
|
||||
VectorXi positions(mp_target->outerSize());
|
||||
positions.setZero();
|
||||
// pass 1
|
||||
for (Index k = 0; k < m_outerPackets; ++k) {
|
||||
typename HashMapType::iterator end = m_hashmaps[k].end();
|
||||
for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it != end; ++it) {
|
||||
const Index outer = it->first & keyBitsMask;
|
||||
++positions[outer];
|
||||
}
|
||||
}
|
||||
// prefix sum
|
||||
StorageIndex count = 0;
|
||||
for (Index j = 0; j < mp_target->outerSize(); ++j) {
|
||||
StorageIndex tmp = positions[j];
|
||||
mp_target->outerIndexPtr()[j] = count;
|
||||
positions[j] = count;
|
||||
count += tmp;
|
||||
}
|
||||
mp_target->makeCompressed();
|
||||
mp_target->outerIndexPtr()[mp_target->outerSize()] = count;
|
||||
mp_target->resizeNonZeros(count);
|
||||
// pass 2
|
||||
for (Index k = 0; k < m_outerPackets; ++k) {
|
||||
const Index outerOffset = (1 << OuterPacketBits) * k;
|
||||
typename HashMapType::iterator end = m_hashmaps[k].end();
|
||||
for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it != end; ++it) {
|
||||
const Index inner = (it->first >> m_keyBitsOffset) + outerOffset;
|
||||
const Index outer = it->first & keyBitsMask;
|
||||
// sorted insertion
|
||||
// Note that we have to deal with at most 2^OuterPacketBits unsorted coefficients,
|
||||
// moreover those 2^OuterPacketBits coeffs are likely to be sparse, an so only a
|
||||
// small fraction of them have to be sorted, whence the following simple procedure:
|
||||
Index posStart = mp_target->outerIndexPtr()[outer];
|
||||
Index i = (positions[outer]++) - 1;
|
||||
while ((i >= posStart) && (mp_target->innerIndexPtr()[i] > inner)) {
|
||||
mp_target->valuePtr()[i + 1] = mp_target->valuePtr()[i];
|
||||
mp_target->innerIndexPtr()[i + 1] = mp_target->innerIndexPtr()[i];
|
||||
--i;
|
||||
}
|
||||
mp_target->innerIndexPtr()[i + 1] = internal::convert_index<StorageIndex>(inner);
|
||||
mp_target->valuePtr()[i + 1] = it->second.value;
|
||||
}
|
||||
}
|
||||
delete[] m_hashmaps;
|
||||
}
|
||||
delete[] m_hashmaps;
|
||||
}
|
||||
|
||||
/** \returns a reference to the coefficient at given coordinates \a row, \a col */
|
||||
Scalar& operator() (Index row, Index col)
|
||||
{
|
||||
const Index outer = SetterRowMajor ? row : col;
|
||||
const Index inner = SetterRowMajor ? col : row;
|
||||
const Index outerMajor = outer >> OuterPacketBits; // index of the packet/map
|
||||
const Index outerMinor = outer & OuterPacketMask; // index of the inner vector in the packet
|
||||
const KeyType key = internal::convert_index<KeyType>((outerMinor<<m_keyBitsOffset) | inner);
|
||||
return m_hashmaps[outerMajor][key].value;
|
||||
}
|
||||
/** \returns a reference to the coefficient at given coordinates \a row, \a col */
|
||||
Scalar& operator()(Index row, Index col) {
|
||||
const Index outer = SetterRowMajor ? row : col;
|
||||
const Index inner = SetterRowMajor ? col : row;
|
||||
const Index outerMajor = outer >> OuterPacketBits; // index of the packet/map
|
||||
const Index outerMinor = outer & OuterPacketMask; // index of the inner vector in the packet
|
||||
const KeyType key = internal::convert_index<KeyType>((outerMinor << m_keyBitsOffset) | inner);
|
||||
return m_hashmaps[outerMajor][key].value;
|
||||
}
|
||||
|
||||
/** \returns the number of non zero coefficients
|
||||
*
|
||||
* \note According to the underlying map/hash_map implementation,
|
||||
* this function might be quite expensive.
|
||||
*/
|
||||
Index nonZeros() const
|
||||
{
|
||||
Index nz = 0;
|
||||
for (Index k=0; k<m_outerPackets; ++k)
|
||||
nz += static_cast<Index>(m_hashmaps[k].size());
|
||||
return nz;
|
||||
}
|
||||
/** \returns the number of non zero coefficients
|
||||
*
|
||||
* \note According to the underlying map/hash_map implementation,
|
||||
* this function might be quite expensive.
|
||||
*/
|
||||
Index nonZeros() const {
|
||||
Index nz = 0;
|
||||
for (Index k = 0; k < m_outerPackets; ++k) nz += static_cast<Index>(m_hashmaps[k].size());
|
||||
return nz;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
HashMapType* m_hashmaps;
|
||||
SparseMatrixType* mp_target;
|
||||
Index m_outerPackets;
|
||||
unsigned char m_keyBitsOffset;
|
||||
protected:
|
||||
HashMapType* m_hashmaps;
|
||||
SparseMatrixType* mp_target;
|
||||
Index m_outerPackets;
|
||||
unsigned char m_keyBitsOffset;
|
||||
};
|
||||
|
||||
} // end namespace Eigen
|
||||
} // end namespace Eigen
|
||||
|
||||
#endif // EIGEN_RANDOMSETTER_H
|
||||
#endif // EIGEN_RANDOMSETTER_H
|
||||
|
||||
Reference in New Issue
Block a user