#ifndef MATRIXMATH_H #define MATRIXMATH_H #include "CoolPropTools.h" #include "Exceptions.h" #include #include #include // inner_product #include #include "float.h" #include "Eigen/Core" /// 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::type> type; }; template struct VectorNd<0, T> { typedef T type; }; namespace CoolProp { /// Some shortcuts and regularly needed operations template std::size_t num_rows(std::vector const& in) { return in.size(); } template std::size_t num_rows(std::vector> const& in) { return in.size(); } 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 < col) { cols = col; } } return cols; }; 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 num_cols(std::vector const& in) { return 1; } 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; } }; /// 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 * @param axis axis along which to extract */ template std::vector eigen_to_vec1D(const Eigen::Matrix& coefficients, int axis = 0) { std::vector result; size_t r = coefficients.rows(), c = coefficients.cols(); if (axis == 0) { if (c != 1) throw ValueError(format("Your matrix has the wrong dimensions: %d,%d", r, c)); result.resize(r); for (size_t i = 0; i < r; ++i) { result[i] = coefficients(i, 0); } } else if (axis == 1) { if (r != 1) throw ValueError(format("Your matrix has the wrong dimensions: %d,%d", r, c)); result.resize(c); for (size_t i = 0; i < c; ++i) { result[i] = coefficients(0, i); } } else { throw ValueError(format("You have to provide axis information: %d is not valid. ", axis)); } return result; } /// @param coefficients matrix containing the ordered coefficients template std::vector> eigen_to_vec(const Eigen::Matrix& coefficients) { // 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... std::vector> result; size_t r = coefficients.rows(), c = coefficients.cols(); 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); } } return result; } /// @param coefficients matrix containing the ordered coefficients template Eigen::Matrix vec_to_eigen(const std::vector>& coefficients) { size_t nRows = num_rows(coefficients), nCols = num_cols(coefficients); Eigen::Matrix 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 * @param axis axis along which to extract data */ template Eigen::Matrix vec_to_eigen(const std::vector& coefficients, int axis = 0) { size_t nRows = num_rows(coefficients); Eigen::Matrix result; if (axis == 0) result.resize(nRows, 1); else if (axis == 1) result.resize(1, nRows); else throw ValueError(format("You have to provide axis information: %d is not valid. ", axis)); for (size_t i = 0; i < nRows; ++i) { if (axis == 0) result(i, 0) = coefficients[i]; if (axis == 1) result(0, i) = coefficients[i]; } return result; } /// @param coefficient template Eigen::Matrix vec_to_eigen(const T& coefficient) { Eigen::Matrix result = Eigen::Matrix(1, 1); result(0, 0) = coefficient; return result; } /// Convert 1D matrix to vector /** Returns either a row- or a column-based * vector. By default, Eigen prefers column * major ordering, just like Fortran. */ template Eigen::Matrix makeColVector(const Eigen::Matrix& matrix) { std::size_t r = matrix.rows(); std::size_t c = matrix.cols(); Eigen::Matrix vector; if (r == 1 && c >= 1) { // Check passed, matrix can be transformed vector = matrix.transpose().block(0, 0, c, r); } else if (r >= 1 && c == 1) { // Check passed, matrix can be transformed vector = matrix.block(0, 0, r, c); } else { // Check failed, throw error throw ValueError(format("Your matrix (%d,%d) cannot be converted into a vector (x,1).", r, c)); } return vector; } template Eigen::Matrix makeVector(const Eigen::Matrix& matrix) { return makeColVector(matrix); } template Eigen::Matrix makeRowVector(const Eigen::Matrix& matrix) { std::size_t r = matrix.rows(); std::size_t c = matrix.cols(); Eigen::Matrix vector; if (r == 1 && c >= 1) { // Check passed, matrix can be transformed vector = matrix.block(0, 0, r, c); } else if (r >= 1 && c == 1) { // Check passed, matrix can be transformed vector = matrix.transpose().block(0, 0, c, r); } else { // Check failed, throw error throw ValueError(format("Your matrix (%d,%d) cannot be converted into a vector (1,x).", r, c)); } return vector; } /// Remove rows and columns from matrices /** A set of convenience functions inspired by http://stackoverflow.com/questions/13290395/how-to-remove-a-certain-row-or-column-while-using-eigen-library-c * but altered to respect templates. */ template void removeRow(Eigen::Matrix& matrix, std::size_t rowToRemove) { //template void removeRow(Eigen::MatrixXd& matrix, std::size_t rowToRemove){ //void removeRow(Eigen::MatrixXd& matrix, unsigned int rowToRemove){ //template void removeRow(Eigen::MatrixBase &matrix, std::size_t rowToRemove){ std::size_t numRows = matrix.rows() - 1; std::size_t numCols = matrix.cols(); if (rowToRemove < numRows) { matrix.block(rowToRemove, 0, numRows - rowToRemove, numCols) = matrix.block(rowToRemove + 1, 0, numRows - rowToRemove, numCols); } else { if (rowToRemove > numRows) { throw ValueError(format("Your matrix does not have enough rows, %d is not greater or equal to %d.", numRows, rowToRemove)); } // Do nothing, resize removes the last row } matrix.conservativeResize(numRows, numCols); } template void removeColumn(Eigen::Matrix& matrix, std::size_t colToRemove) { //template void removeColumn(Eigen::MatrixXd& matrix, std::size_t colToRemove){ //void removeColumn(Eigen::MatrixXd& matrix, unsigned int colToRemove){ //template void removeColumn(Eigen::MatrixBase &matrix, std::size_t colToRemove){ std::size_t numRows = matrix.rows(); std::size_t numCols = matrix.cols() - 1; if (colToRemove < numCols) { matrix.block(0, colToRemove, numRows, numCols - colToRemove) = matrix.block(0, colToRemove + 1, numRows, numCols - colToRemove); } else { if (colToRemove > numCols) { throw ValueError(format("Your matrix does not have enough columns, %d is not greater or equal to %d.", numCols, colToRemove)); } // Do nothing, resize removes the last column } matrix.conservativeResize(numRows, numCols); } ///// @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 = "%8.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(std::vector(a.begin(), a.end()), stdFmt); }; ///Templates for turning vectors (1D-matrices) into strings inline std::string stringvec_to_string(const std::vector& a) { if (a.size() < 1) return std::string(""); std::stringstream out; out << "[ " << format("%s", a[0].c_str()); for (size_t j = 1; j < a.size(); j++) { out << ", " << format("%s", a[j].c_str()); } out << " ]"; return out.str(); }; /// 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((double)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); }; ///Templates for turning Eigen matrices into strings template std::string mat_to_string(const Eigen::Matrix& A, const char* fmt) { //std::string mat_to_string(const Eigen::MatrixXd &A, const char *fmt) { std::size_t r = A.rows(); std::size_t c = A.cols(); if ((r < 1) || (c < 1)) return std::string(""); std::stringstream out; out << "[ "; if (r == 1) { out << format(fmt, A(0, 0)); for (size_t j = 1; j < c; j++) { out << ", " << format(fmt, A(0, j)); } } else { out << mat_to_string(Eigen::Matrix(A.row(0)), fmt); for (size_t i = 1; i < r; i++) { out << ", " << std::endl << " " << mat_to_string(Eigen::Matrix(A.row(i)), fmt); } } out << " ]"; return out.str(); }; template std::string mat_to_string(const Eigen::Matrix& A) { //std::string vec_to_string(const Eigen::MatrixXd &A) { return mat_to_string(A, stdFmt); }; ///// Templates for printing numbers, vectors and matrices //static const char* stdFmt = "%8.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); //}; // /////Templates for turning Eigen matrices into strings //template std::string mat_to_string(const Eigen::Matrix &A, const char *fmt) { ////std::string mat_to_string(const Eigen::MatrixXd &A, const char *fmt) { // std::size_t r = A.rows(); // std::size_t c = A.cols(); // if ((r<1)||(c<1)) return std::string(""); // std::stringstream out; // out << "[ "; // if (r==1) { // out << format(fmt, A(0,0)); // for (size_t j = 1; j < c; j++) { // out << ", " << format(fmt, A(0,j)); // } // } else { // out << mat_to_string(Eigen::Matrix(A.row(0)), fmt); // for (size_t i = 1; i < r; i++) { // out << ", " << std::endl << " " << mat_to_string(Eigen::Matrix(A.row(i)), fmt); // } // } // out << " ]"; // return out.str(); //}; //template std::string mat_to_string(const Eigen::Matrix &A) { ////std::string vec_to_string(const Eigen::MatrixXd &A) { // return mat_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); /* 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) { std::size_t index = col; T max = 0, val; for (size_t row = col; row < (*A).size(); row++) { val = (*A)[row][col]; if (std::abs(val) > max) { max = std::abs(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 (std::abs(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 (std::size_t col = NcolA - 1; col > 0; col--) { for (int row = static_cast(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 = std::abs(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> const& in, size_t row) { return in[row]; }; template std::vector get_col(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 < col) throw ValueError(format("Your matrix does not have enough entries in row %d, last index %d is less than %d. ", i, sizeY - 1, col)); out.push_back(in[i][col]); } return out; }; template 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> 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> 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> 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); }; inline void removeRow(Eigen::MatrixXd& matrix, unsigned int rowToRemove) { unsigned int numRows = static_cast(matrix.rows()) - 1; unsigned int numCols = static_cast(matrix.cols()); if (rowToRemove <= numRows) matrix.block(rowToRemove, 0, numRows - rowToRemove, numCols) = matrix.block(rowToRemove + 1, 0, numRows - rowToRemove, numCols); else { throw ValueError(format("Trying to remove row index [%d] greater than max index [%d] ", rowToRemove, numRows)); } matrix.conservativeResize(numRows, numCols); }; inline void removeColumn(Eigen::MatrixXd& matrix, unsigned int colToRemove) { unsigned int numRows = static_cast(matrix.rows()); unsigned int numCols = static_cast(matrix.cols()) - 1; if (colToRemove <= numCols) matrix.block(0, colToRemove, numRows, numCols - colToRemove) = matrix.block(0, colToRemove + 1, numRows, numCols - colToRemove); else { throw ValueError(format("Trying to remove column index [%d] greater than max index [%d] ", colToRemove, numCols)); } matrix.conservativeResize(numRows, numCols); }; template inline Eigen::MatrixXd minor_matrix(const Eigen::MatrixBase& A, std::size_t i, std::size_t j) { Eigen::MatrixXd Am = A; removeRow(Am, static_cast(i)); removeColumn(Am, static_cast(j)); return Am; }; template static Eigen::MatrixXd adjugate(const Eigen::MatrixBase& A) { std::size_t N = A.rows(); if (N == 1) { Eigen::MatrixXd Aadj(1, 1); Aadj << 1; return Aadj; } Eigen::MatrixXd Aadj(N, N); for (std::size_t i = 0; i < N; ++i) { for (std::size_t j = 0; j < N; ++j) { int negative_1_to_the_i_plus_j = ((i + j) % 2 == 0) ? 1 : -1; Aadj(i, j) = negative_1_to_the_i_plus_j * minor_matrix(A, j, i).determinant(); } } return Aadj; } template static Eigen::MatrixXd adjugate_derivative(const Eigen::MatrixBase& A, const Eigen::MatrixBase& dAdt) { std::size_t N = A.rows(); Eigen::MatrixXd Aadj(N, N); for (std::size_t i = 0; i < N; ++i) { for (std::size_t j = 0; j < N; ++j) { int negative_1_to_the_i_plus_j = ((i + j) % 2 == 0) ? 1 : -1; Eigen::MatrixXd mm = minor_matrix(A, j, i); Aadj(i, j) = negative_1_to_the_i_plus_j * (adjugate(minor_matrix(A, j, i)) * minor_matrix(dAdt, j, i)).trace(); } } return Aadj; } }; /* namespace CoolProp */ #endif