From 8286073c7339b3d8b296a570ba38221d57e4a468 Mon Sep 17 00:00:00 2001 From: Jens Wehner Date: Thu, 2 Sep 2021 17:23:33 +0000 Subject: [PATCH] Matrixmarket extension --- unsupported/Eigen/SparseExtra | 6 +- unsupported/Eigen/src/SparseExtra/MarketIO.h | 160 ++++++++++++++---- .../Eigen/src/SparseExtra/RandomSetter.h | 2 +- unsupported/test/sparse_extra.cpp | 49 +++++- 4 files changed, 176 insertions(+), 41 deletions(-) diff --git a/unsupported/Eigen/SparseExtra b/unsupported/Eigen/SparseExtra index 1b61ad38f..2fa8f9f6a 100644 --- a/unsupported/Eigen/SparseExtra +++ b/unsupported/Eigen/SparseExtra @@ -31,10 +31,12 @@ /** * \defgroup SparseExtra_Module SparseExtra module * - * This module contains some experimental features extending the sparse module. + * This module contains some experimental features extending the sparse module: + * - A RandomSetter which is a wrapper object allowing to set/update a sparse matrix with random access. + * - MatrixMarket format(https://math.nist.gov/MatrixMarket/formats.html) readers and writers for sparse and dense matrices. * * \code - * #include + * #include * \endcode */ diff --git a/unsupported/Eigen/src/SparseExtra/MarketIO.h b/unsupported/Eigen/src/SparseExtra/MarketIO.h index dd786d561..e05f638d6 100644 --- a/unsupported/Eigen/src/SparseExtra/MarketIO.h +++ b/unsupported/Eigen/src/SparseExtra/MarketIO.h @@ -47,14 +47,14 @@ namespace internal } template - inline void GetVectorElt (const std::string& line, RealScalar& val) + inline void GetDenseElt (const std::string& line, RealScalar& val) { std::istringstream newline(line); newline >> val; } template - inline void GetVectorElt (const std::string& line, std::complex& val) + inline void GetDenseElt (const std::string& line, std::complex& val) { RealScalar valR, valI; std::istringstream newline(line); @@ -94,23 +94,34 @@ namespace internal template - inline void putVectorElt(Scalar value, std::ofstream& out) + inline void putDenseElt(Scalar value, std::ofstream& out) { out << value << "\n"; } template - inline void putVectorElt(std::complex value, std::ofstream& out) + inline void putDenseElt(std::complex value, std::ofstream& out) { out << value.real() << " " << value.imag()<< "\n"; } } // end namespace internal -inline bool getMarketHeader(const std::string& filename, int& sym, bool& iscomplex, bool& isvector) + +/** + * \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 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; iscomplex = false; - isvector = false; + isdense = false; std::ifstream in(filename.c_str(),std::ios::in); if(!in) return false; @@ -122,14 +133,22 @@ inline bool getMarketHeader(const std::string& filename, int& sym, bool& iscompl 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) isvector = true; + 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 suceed. + */ template bool loadMarket(SparseMatrixType& mat, const std::string& filename) { @@ -184,50 +203,108 @@ bool loadMarket(SparseMatrixType& mat, const std::string& filename) elements.push_back(T(i,j,value)); } else - std::cerr << "Invalid read: " << i << "," << j << "\n"; + { + 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; + } input.close(); return true; } -template -bool loadMarketVector(VectorType& vec, const std::string& filename) + +/** + * \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. + * + * @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 suceed. + */ +template +bool loadMarketDense(DenseType& mat, const std::string& filename) { - typedef typename VectorType::Scalar Scalar; + typedef typename DenseType::Scalar Scalar; std::ifstream in(filename.c_str(), std::ios::in); if(!in) return false; std::string line; - int n(0), col(0); + Index rows(0), cols(0); do { // Skip comments std::getline(in, line); eigen_assert(in.good()); } while (line[0] == '%'); std::istringstream newline(line); - newline >> n >> col; - eigen_assert(n>0 && col>0); - vec.resize(n); - int i = 0; + newline >> rows >> cols; + + bool sizes_not_positive=(rows<1 || cols<1); + bool wrong_input_rows = (DenseType::MaxRowsAtCompileTime != Dynamic && rows > DenseType::MaxRowsAtCompileTime) || + (DenseType::RowsAtCompileTime!=Dynamic && rows!=DenseType::RowsAtCompileTime); + bool wrong_input_cols = (DenseType::MaxColsAtCompileTime != Dynamic && cols > DenseType::MaxColsAtCompileTime) || + (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"< +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 sym at the moment no symmetry operations are supported + * @return true if writing suceeded + */ template bool saveMarket(const SparseMatrixType& mat, const std::string& filename, int sym = 0) { @@ -254,11 +331,22 @@ bool saveMarket(const SparseMatrixType& mat, const std::string& filename, int sy return true; } -template -bool saveMarketVector (const VectorType& vec, const std::string& filename) + +/** + * \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 + * @return true if writing suceeded + */ + +template +bool saveMarketDense (const DenseType& mat, const std::string& filename) { - typedef typename VectorType::Scalar Scalar; - typedef typename VectorType::RealScalar RealScalar; + typedef typename DenseType::Scalar Scalar; + typedef typename DenseType::RealScalar RealScalar; std::ofstream out(filename.c_str(),std::ios::out); if(!out) return false; @@ -269,14 +357,26 @@ bool saveMarketVector (const VectorType& vec, const std::string& filename) out << "%%MatrixMarket matrix array complex general\n"; else out << "%%MatrixMarket matrix array real general\n"; - out << vec.size() << " "<< 1 << "\n"; - for (int i=0; i < vec.size(); i++){ - internal::putVectorElt(vec(i), out); + 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; } +/** + * \ingroup SparseExtra_Module + * @brief Same functionality as saveMarketDense, deprecated + */ +template +bool saveMarketVector (const VectorType& vec, const std::string& filename) +{ + return saveMarketDense(vec, filename); +} + } // end namespace Eigen #endif // EIGEN_SPARSE_MARKET_IO_H diff --git a/unsupported/Eigen/src/SparseExtra/RandomSetter.h b/unsupported/Eigen/src/SparseExtra/RandomSetter.h index d4393a7a6..f6ab0953e 100644 --- a/unsupported/Eigen/src/SparseExtra/RandomSetter.h +++ b/unsupported/Eigen/src/SparseExtra/RandomSetter.h @@ -101,7 +101,7 @@ template struct GoogleSparseHashMapTraits #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 diff --git a/unsupported/test/sparse_extra.cpp b/unsupported/test/sparse_extra.cpp index 3f1ad866e..70a9abe08 100644 --- a/unsupported/test/sparse_extra.cpp +++ b/unsupported/test/sparse_extra.cpp @@ -173,6 +173,30 @@ void check_marketio_vector() VERIFY_IS_EQUAL(v1,v2); } +template +void check_marketio_dense() +{ + Index rows=DenseMatrixType::MaxRowsAtCompileTime; + if (DenseMatrixType::MaxRowsAtCompileTime==Dynamic){ + rows=internal::random(1,100); + }else if(DenseMatrixType::RowsAtCompileTime==Dynamic){ + rows=internal::random(1,DenseMatrixType::MaxRowsAtCompileTime); + } + + Index cols =DenseMatrixType::MaxColsAtCompileTime; + if (DenseMatrixType::MaxColsAtCompileTime==Dynamic){ + cols=internal::random(1,100); + }else if(DenseMatrixType::ColsAtCompileTime==Dynamic){ + cols=internal::random(1,DenseMatrixType::MaxColsAtCompileTime); + } + + DenseMatrixType m1, m2; + m1= DenseMatrixType::Random(rows,cols); + saveMarketDense(m1, "dense_extra.mtx"); + loadMarketDense(m2, "dense_extra.mtx"); + VERIFY_IS_EQUAL(m1,m2); +} + EIGEN_DECLARE_TEST(sparse_extra) { for(int i = 0; i < g_repeat; i++) { @@ -181,15 +205,24 @@ EIGEN_DECLARE_TEST(sparse_extra) CALL_SUBTEST_2( sparse_extra(SparseMatrix >(s, s)) ); CALL_SUBTEST_1( sparse_extra(SparseMatrix(s, s)) ); - CALL_SUBTEST_4( (check_marketio >()) ); - CALL_SUBTEST_4( (check_marketio >()) ); - CALL_SUBTEST_4( (check_marketio,ColMajor,int> >()) ); - CALL_SUBTEST_4( (check_marketio,ColMajor,int> >()) ); - CALL_SUBTEST_4( (check_marketio >()) ); - CALL_SUBTEST_4( (check_marketio >()) ); - CALL_SUBTEST_4( (check_marketio,ColMajor,long int> >()) ); - CALL_SUBTEST_4( (check_marketio,ColMajor,long int> >()) ); + CALL_SUBTEST_3( (check_marketio >()) ); + CALL_SUBTEST_3( (check_marketio >()) ); + CALL_SUBTEST_3( (check_marketio,ColMajor,int> >()) ); + CALL_SUBTEST_3( (check_marketio,ColMajor,int> >()) ); + CALL_SUBTEST_3( (check_marketio >()) ); + CALL_SUBTEST_3( (check_marketio >()) ); + CALL_SUBTEST_3( (check_marketio,ColMajor,long int> >()) ); + CALL_SUBTEST_3( (check_marketio,ColMajor,long int> >()) ); + CALL_SUBTEST_4( (check_marketio_dense >()) ); + CALL_SUBTEST_4( (check_marketio_dense >()) ); + CALL_SUBTEST_4( (check_marketio_dense >()) ); + CALL_SUBTEST_4( (check_marketio_dense,Dynamic,Dynamic> >()) ); + CALL_SUBTEST_4( (check_marketio_dense,Dynamic,Dynamic> >()) ); + CALL_SUBTEST_4( (check_marketio_dense >()) ); + CALL_SUBTEST_4( (check_marketio_dense >()) ); + CALL_SUBTEST_4( (check_marketio_dense >()) ); + CALL_SUBTEST_4( (check_marketio_dense >()) ); CALL_SUBTEST_5( (check_marketio_vector >()) ); CALL_SUBTEST_5( (check_marketio_vector >()) );