#ifndef MATRIXMATH_H #define MATRIXMATH_H #include "CoolPropTools.h" #include "Exceptions.h" #include #include #include // inner_product #include #include "float.h" #include /// A wrapper around std::vector /** This wrapper makes the standard vector multi-dimensional. * A useful thing even though we might not need it that * much. However, it makes the code look better and the * polynomial class really is a mess... * Source: http://stackoverflow.com/questions/13105514/n-dimensional-vector */ template struct VectorNd { typedef std::vector< typename VectorNd::type > type; }; template struct VectorNd<0,T> { typedef T type; }; namespace CoolProp{ /// Convert vectors and matrices /** Conversion functions for the different kinds of object-like * parameters. This might be obsolete at a later stage, but now * it is still needed. */ /// @param coefficients matrix containing the ordered coefficients template void convert(const Eigen::MatrixBase &coefficients, std::vector > &result){ // Eigen uses columns as major axis, this might be faster than the row iteration. // However, the 2D vector stores things differently, no idea what is faster... size_t R = coefficients.rows(), C = coefficients.cols(); //std::vector > result(R, std::vector(C, 0)); result.resize(R, std::vector(C, 0)); // extends vector if necessary for (size_t i = 0; i < R; ++i) { result[i].resize(C, 0); for (size_t j = 0; j < C; ++j) { result[i][j] = coefficients(i,j); } } } /// @param coefficients matrix containing the ordered coefficients template void convert(const std::vector > &coefficients, Eigen::MatrixBase &result){ size_t nRows = num_rows(coefficients), nCols = num_cols(coefficients); size_t R = result.rows(), C = result.cols(); if (nRows!=R) throw ValueError(format("You have to provide matrices with the same number of rows: %d is not %d. ",nRows,R)); if (nCols!=C) throw ValueError(format("You have to provide matrices with the same number of columns: %d is not %d. ",nCols,C)); for (size_t i = 0; i < nCols; ++i) { for (size_t j = 0; j < nRows; ++j) { result(j,i) = coefficients[j][i]; } } } /// @param coefficients matrix containing the ordered coefficients template void convert(const Eigen::MatrixBase &coefficients, std::vector &result){ size_t R = result.rows(); result.resize(R, 0); // extends vector if necessary for (size_t i = 0; i < R; ++i) { result[i] = coefficients(i,0); } } /// @param coefficients matrix containing the ordered coefficients template void convert(const std::vector &coefficients, Eigen::MatrixBase &result){ size_t nRows = num_rows(coefficients); size_t R = result.rows(); if (nRows!=R) throw ValueError(format("You have to provide matrices with the same number of rows: %d is not %d. ",nRows,R)); for (size_t i = 0; i < nRows; ++i) { result(i,0) = coefficients[i]; } } ///// @param coefficients matrix containing the ordered coefficients //template Eigen::Matrix convert(const std::vector > &coefficients){ // size_t nRows = num_rows(coefficients), nCols = num_cols(coefficients); // Eigen::MatrixBase result(nRows,nCols); // for (size_t i = 0; i < nCols; ++i) { // for (size_t j = 0; j < nRows; ++j) { // result(j,i) = coefficients[j][i]; // } // } // return result; //} // ///// @param coefficients matrix containing the ordered coefficients //template void convert(const std::vector > &coefficients, Eigen::Matrix &result){ // size_t nRows = num_rows(coefficients), nCols = num_cols(coefficients); // //Eigen::MatrixBase result(nRows,nCols); // for (size_t i = 0; i < nCols; ++i) { // for (size_t j = 0; j < nRows; ++j) { // result(j,i) = coefficients[j][i]; // } // } // //return result; //} // //template void convert(const std::vector > &coefficients, Eigen::MatrixBase &result){ // size_t nRows = num_rows(coefficients), nCols = num_cols(coefficients); // //Eigen::MatrixBase result; // //if ((R!=nRows) || (C!=nCols)) // result.resize(nRows,nCols); // for (size_t i = 0; i < nCols; ++i) { // for (size_t j = 0; j < nRows; ++j) { // result(j,i) = coefficients[j][i]; // } // } // //return result; //} //template //inline void func1(MatrixBase &out_mat ){ // // Do something then return a matrix // out_mat = ... //} //template //Eigen::Matrix //Multiply(const Eigen::MatrixBase& p1, // const Eigen::MatrixBase& p2) //{ // return (p1 * p2); //} // // //template //Eigen::Matrix //Multiply(const Eigen::MatrixBase& p1, // const Eigen::MatrixBase& p2) //{ // return (p1 * p2); //} /// Templates for printing numbers, vectors and matrices static const char* stdFmt = "%7.3f"; ///Templates for turning vectors (1D-matrices) into strings template std::string vec_to_string(const std::vector &a, const char *fmt) { if (a.size()<1) return std::string(""); std::stringstream out; out << "[ " << format(fmt,a[0]); for (size_t j = 1; j < a.size(); j++) { out << ", " << format(fmt, a[j]); } out << " ]"; return out.str(); }; template std::string vec_to_string(const std::vector &a) { return vec_to_string(a, stdFmt); }; /// Templates for turning numbers (0D-matrices) into strings template std::string vec_to_string(const T &a, const char *fmt) { std::vector vec; vec.push_back(a); return vec_to_string(vec, fmt); }; template std::string vec_to_string(const T &a) { return vec_to_string(a, stdFmt); }; ///Templates for turning 2D-matrices into strings template std::string vec_to_string(const std::vector > &A, const char *fmt) { if (A.size()<1) return std::string(""); std::stringstream out; out << "[ " << vec_to_string(A[0], fmt); for (size_t j = 1; j < A.size(); j++) { out << ", " << std::endl << " " << vec_to_string(A[j], fmt); } out << " ]"; return out.str(); }; template std::string vec_to_string(const std::vector > &A) { return vec_to_string(A, stdFmt); }; /// Template class for turning numbers (0D-matrices) into strings //template std::string vec_to_string(const T &a){ // return vec_to_string(a, stdFmt); // std::stringstream out; // out << format("[ %7.3f ]",a); // return out.str(); //}; //template std::string vec_to_string(const VectorNd<0, T> &a){ // return vec_to_string(a, stdFmt); //}; //template std::string vec_to_string(const VectorNd<0, T> &a, const char *fmt) { // VectorNd<1, T> vec; // vec.push_back(a); // return vec_to_string(vec, fmt); //}; // /////Template classes for turning vectors (1D-matrices) into strings //template std::string vec_to_string(const VectorNd<1, T> &a) { // return vec_to_string(a, stdFmt); //}; //template std::string vec_to_string(const VectorNd<1, T> &a, const char *fmt) { // if (a.size()<1) { // return std::string(""); // } else { // std::stringstream out; // out << "[ "; // out << format(fmt,a[0]); // for (size_t j = 1; j < a.size(); j++) { // out << ", "; // out << format(fmt,a[j]); // } // out << " ]"; // return out.str(); // } //}; // /////Template classes for turning 2D-matrices into strings //template std::string vec_to_string(const VectorNd<2, T> &A) { // return vec_to_string(A, stdFmt); //} //template std::string vec_to_string(const VectorNd<2, T> &A, const char *fmt) { // if (A.size()<1) return std::string(""); // std::stringstream out; // out << "[ " << format(fmt,A[0]); // for (size_t j = 1; j < A.size(); j++) { // out << ", " << std::endl << " " << vec_to_string(A[j], fmt); // } // out << " ]"; // return out.str(); //} ///// Publish the linear algebra solver //template std::vector linsolve(std::vector > const& A, std::vector const& b); //template std::vector > linsolve(std::vector > const& A, std::vector > const& B); // ///// Some shortcuts and regularly needed operations //template std::size_t num_rows (std::vector > const& in); //template std::size_t num_cols (std::vector > const& in); //template std::size_t max_cols (std::vector > const& in); //template std::vector get_row (std::vector > const& in, size_t row); //template std::vector get_col (std::vector > const& in, size_t col); //template bool is_squared(std::vector > const& in); //template std::vector > make_squared(std::vector > const& in); // ///// Define some basic math operations for vectors //template T multiply( std::vector const& A, std::vector const& B); //template std::vector multiply(std::vector > const& A, std::vector const& B); //template std::vector > multiply(std::vector > const& A, std::vector > const& B); // //template T dot_product(std::vector const& a, std::vector const& b); //template std::vector cross_product(std::vector const& a, std::vector const& b); // //template std::vector > transpose(std::vector > const& in); //template std::vector > invert(std::vector > const& in); // //template std::string vec_to_string( T const& a); //template std::string vec_to_string( std::vector const& a); //template std::string vec_to_string(std::vector > const& A); // //template std::string vec_to_string( std::vector const& a, const char *fmt); //template std::string vec_to_string(std::vector > const& A, const char *fmt); // Forward definitions template std::size_t num_rows (std::vector > const& in); template std::size_t max_cols (std::vector > const& in); template bool is_squared(std::vector > const& in){ std::size_t cols = max_cols(in); if (cols!=num_rows(in)) { return false;} else { for (std::size_t i = 0; i < in.size(); i++) { if (cols!=in[i].size()) {return false; } } } return true; }; template std::size_t max_cols (std::vector > const& in){ std::size_t cols = 0; std::size_t col = 0; for (std::size_t i = 0; i < in.size(); i++) { col = in[i].size(); if (cols std::size_t num_rows ( std::vector const& in){ return in.size(); } template std::size_t num_cols ( std::vector const& in){ return 1; } template std::size_t num_rows (std::vector > const& in){ return in.size(); } template std::size_t num_cols (std::vector > const& in){ if (num_rows(in)>0) { if (is_squared(in)) { return in[0].size(); } else { return max_cols(in); } } else { return 0; } }; /* Owe a debt of gratitude to http://sole.ooz.ie/en - very clear treatment of GJ */ template void swap_rows(std::vector > *A, size_t row1, size_t row2) { for (size_t col = 0; col < (*A)[0].size(); col++){ std::swap((*A)[row1][col],(*A)[row2][col]); } }; template void subtract_row_multiple(std::vector > *A, size_t row, T multiple, size_t pivot_row) { for (size_t col = 0; col < (*A)[0].size(); col++){ (*A)[row][col] -= multiple*(*A)[pivot_row][col]; } }; template void divide_row_by(std::vector > *A, size_t row, T value) { for (size_t col = 0; col < (*A)[0].size(); col++){ (*A)[row][col] /= value; } }; template size_t get_pivot_row(std::vector > *A, size_t col) { int index = col; T max = 0, val; for (size_t row = col; row < (*A).size(); row++) { val = (*A)[row][col]; if (fabs(val) > max) { max = fabs(val); index = row; } } return index; }; template std::vector > linsolve_Gauss_Jordan(std::vector > const& A, std::vector > const& B) { std::vector > AB; std::vector > X; size_t pivot_row; T pivot_element; size_t NrowA = num_rows(A); size_t NrowB = num_rows(B); size_t NcolA = num_cols(A); size_t NcolB = num_cols(B); if (NrowA!=NrowB) throw ValueError(format("You have to provide matrices with the same number of rows: %d is not %d. ",NrowA,NrowB)); AB.resize(NrowA, std::vector(NcolA+NcolB, 0)); X.resize(NrowA, std::vector(NcolB, 0)); // Build the augmented matrix for (size_t row = 0; row < NrowA; row++){ for (size_t col = 0; col < NcolA; col++){ AB[row][col] = A[row][col]; } for (size_t col = NcolA; col < NcolA+NcolB; col++){ AB[row][col] = B[row][col-NcolA]; } } for (size_t col = 0; col < NcolA; col++){ // Find the pivot value pivot_row = get_pivot_row(&AB, col); if (fabs(AB[pivot_row][col]) < 10*DBL_EPSILON){ throw ValueError(format("Zero occurred in row %d, the matrix is singular. ",pivot_row));} if (pivot_row>=col){ // Swap pivot row and current row swap_rows(&AB, col, pivot_row); } // Get the pivot element pivot_element = AB[col][col]; // Divide the pivot row by the pivot element divide_row_by(&AB,col,pivot_element); if (col < NrowA-1) { // All the rest of the rows, subtract the value of the [r][c] combination for (size_t row = col + 1; row < NrowA; row++) { subtract_row_multiple(&AB,row,AB[row][col],col); } } } for (int col = NcolA - 1; col > 0; col--) { for (int row = col - 1; row >=0; row--) { subtract_row_multiple(&AB,row,AB[row][col],col); } } // Set the output value for (size_t row = 0; row < NrowA; row++){ for (size_t col = 0; col < NcolB; col++){ X[row][col] = AB[row][NcolA+col]; } } return X; }; //std::vector > linsolve_Gauss_Jordan_reimpl(std::vector > const& A, std::vector > const& B) { // std::vector > AB; // std::vector > X; // size_t pivot_row; // double pivot_element; // double tmp_element; // // size_t NrowA = num_rows(A); // size_t NrowB = num_rows(B); // size_t NcolA = num_cols(A); // size_t NcolB = num_cols(B); // // if (NrowA!=NrowB) throw ValueError(format("You have to provide matrices with the same number of rows: %d is not %d. ",NrowA,NrowB)); // // AB.resize(NrowA, std::vector(NcolA+NcolB, 0)); // X.resize(NrowA, std::vector(NcolB, 0)); // // // Build the augmented matrix // for (size_t row = 0; row < NrowA; row++){ // for (size_t col = 0; col < NcolA; col++){ // AB[row][col] = A[row][col]; // } // for (size_t col = NcolA; col < NcolA+NcolB; col++){ // AB[row][col] = B[row][col-NcolA]; // } // } // // for (size_t col = 0; col < NcolA; col++){ // // Find the pivot row // pivot_row = 0; // pivot_element = 0.0; // for (size_t row = col; row < NrowA; row++){ // tmp_element = fabs(AB[row][col]); // if (tmp_element>pivot_element) { // pivot_element = tmp_element; // pivot_row = row; // } // } // // Check for errors // if (AB[pivot_row][col]<1./_HUGE) throw ValueError(format("Zero occurred in row %d, the matrix is singular. ",pivot_row)); // // Swap the rows // if (pivot_row>col) { // for (size_t colInt = 0; colInt < NcolA; colInt++){ // std::swap(AB[pivot_row][colInt],AB[pivot_row][colInt]); // } // } // // Process the entries below current element // for (size_t row = col; row < NrowA; row++){ // // Entries to the right of current element (until end of A) // for (size_t colInt = col+1; colInt < NcolA; colInt++){ // // All entries in augmented matrix // for (size_t colFull = col; colFull < NcolA+NcolB; colFull++){ // AB[colInt][colFull] -= AB[col][colFull] * AB[colInt][col] / AB[col][col]; // } // AB[colInt][col] = 0.0; // } // } // } // return AB; //} template std::vector > linsolve(std::vector > const& A, std::vector > const& B){ return linsolve_Gauss_Jordan(A, B); }; template std::vector linsolve(std::vector > const& A, std::vector const& b){ std::vector > B; for (size_t i = 0; i < b.size(); i++){ B.push_back(std::vector(1,b[i])); } B = linsolve(A, B); B[0].resize(B.size(),0.0); for (size_t i = 1; i < B.size(); i++){ B[0][i] = B[i][0]; } return B[0]; }; template std::vector get_row(std::vector< std::vector > const& in, size_t row) { return in[row]; }; template std::vector get_col(std::vector< std::vector > const& in, size_t col) { std::size_t sizeX = in.size(); if (sizeX<1) throw ValueError(format("You have to provide values, a vector length of %d is not valid. ",sizeX)); size_t sizeY = in[0].size(); if (sizeY<1) throw ValueError(format("You have to provide values, a vector length of %d is not valid. ",sizeY)); std::vector out; for (std::size_t i = 0; i < sizeX; i++) { sizeY = in[i].size(); if (sizeY-1 std::vector > make_squared(std::vector > const& in){ std::size_t cols = max_cols(in); std::size_t rows = num_rows(in); std::size_t maxVal = 0; std::vector > out; std::vector tmp; if (cols>rows) {maxVal = cols; } else {maxVal = rows; } out.clear(); for (std::size_t i = 0; i < in.size(); i++) { tmp.clear(); for (std::size_t j = 0; j < in[i].size(); j++) { tmp.push_back(in[i][j]); } while (maxVal>tmp.size()) { tmp.push_back(0.0); } out.push_back(tmp); } // Check rows tmp.clear(); tmp.resize(maxVal,0.0); while (maxVal>out.size()) { out.push_back(tmp); } return out; }; template T multiply( std::vector const& a, std::vector const& b){ return dot_product(a,b); }; template std::vector multiply(std::vector > const& A, std::vector const& b){ std::vector > B; for (size_t i = 0; i < b.size(); i++){ B.push_back(std::vector(1,b[i])); } B = multiply(A, B); B[0].resize(B.size(),0.0); for (size_t i = 1; i < B.size(); i++){ B[0][i] = B[i][0]; } return B[0]; } template std::vector > multiply(std::vector > const& A, std::vector > const& B){ if (num_cols(A) != num_rows(B)){ throw ValueError(format("You have to provide matrices with the same columns and rows: %d is not equal to %d. ",num_cols(A),num_rows(B))); } size_t rows = num_rows(A); size_t cols = num_cols(B); T tmp; std::vector > outVec; std::vector tmpVec; outVec.clear(); for (size_t i = 0; i < rows; i++){ tmpVec.clear(); for (size_t j = 0; j < cols; j++){ tmp = 0.0; for (size_t k = 0; k < num_cols(A); k++){ tmp += A[i][k] * B[k][j]; } tmpVec.push_back(tmp); } outVec.push_back(tmpVec); } return outVec; }; template T dot_product(std::vector const& a, std::vector const& b){ if (a.size()==b.size()){ return std::inner_product(a.begin(), a.end(), b.begin(), 0.0); } throw ValueError(format("You have to provide vectors with the same length: %d is not equal to %d. ",a.size(),b.size())); }; template std::vector cross_product(std::vector const& a, std::vector const& b){ throw NotImplementedError("The cross product function has not been implemented, yet"); }; template std::vector< std::vector > transpose(std::vector > const& in){ size_t sizeX = in.size(); if (sizeX<1) throw ValueError(format("You have to provide values, a vector length of %d is not a valid. ",sizeX)); size_t sizeY = in[0].size(); size_t sizeYOld = sizeY; if (sizeY<1) throw ValueError(format("You have to provide values, a vector length of %d is not a valid. ",sizeY)); std::vector< std::vector > out(sizeY,std::vector(sizeX)); for (size_t i = 0; i < sizeX; ++i){ sizeY = in[i].size(); if (sizeY!=sizeYOld) throw ValueError(format("You have to provide a rectangular matrix: %d is not equal to %d. ",sizeY,sizeYOld)); for (size_t j = 0; j < sizeY; ++j){ out[j][i] = in[i][j]; } } return out; }; template std::vector< std::vector > invert(std::vector > const& in){ if (!is_squared(in)) throw ValueError(format("Only square matrices can be inverted: %d is not equal to %d. ",num_rows(in),num_cols(in))); std::vector > identity; // Build the identity matrix size_t dim = num_rows(in); identity.resize(dim, std::vector(dim, 0)); for (size_t row = 0; row < dim; row++){ identity[row][row] = 1.0; } return linsolve(in,identity); }; }; /* namespace CoolProp */ #endif