/***************************************************************** This software is part of Jean-Sébastien Caux's Abacus toolsuite. Copyright © Jean-Sébastien Caux. *****************************************************************/ export module matrix; import std; import conveniences; // version using flattened vector //////////////////// // ↓ Class Matrix // //////////////////// export template class Matrix { private: std::size_t dim_; std::vector element_; public: Matrix (std::size_t dim); inline std::size_t size() const { return dim_; }; inline T operator() (IndexU i, IndexU j) const { return element_[i*dim_ + j]; }; inline T& operator() (IndexU i, IndexU j) { return element_[i*dim_ + j]; }; void setZero(); Matrix& operator*= (const T& a); Matrix& operator/= (const T& a); void ludcmp (std::vector& indx, T& d); void lubksb (std::vector& indx, std::vector& b); std::complex lndet_LU_destroy (); }; template Matrix::Matrix (std::size_t dim) : dim_(dim) , element_(std::vector(dim_*dim_)) {} template void Matrix::setZero () { std::fill(element_.begin(), element_.end(), T(0)); } template Matrix& Matrix::operator*= (const T& a) { std::transform(element_.begin(), element_.end(), element_.begin(), [a](T el) { return el * a; }); return *this; } template Matrix& Matrix::operator/= (const T& a) { T oneovera = T(1)/a; std::transform(element_.begin(), element_.end(), element_.begin(), [oneovera](T el) { return el * oneovera; }); return *this; } template void Matrix::ludcmp (std::vector& indx, T& d) { IndexU i, j, k; IndexU imax { 0 }; IndexU idim_, jdim_, imaxdim_; T big, dum, sum, temp; IndexU n { size() }; std::vector vv(n); d = T(1); for (i = 0; i < n; i++) { big = T(0); idim_ = i*dim_; for (j = 0; j < n; j++) { if ((std::fabs(temp = element_[idim_ + j])) > std::fabs(big)) big = temp; } if (big == T(0)) throw; vv[i] = T(1)/big; } for (j = 0; j < n; j++) { for (i = 0; i < j; i++) { idim_ = i*dim_; sum = element_[idim_ + j]; for (k = 0; k < i; k++) sum -= element_[idim_ + k] * element_[k*dim_ + j]; element_[idim_ + j] = sum; } big = T(0); for (i = j; i < n; i++) { idim_ = i*dim_; sum = element_[idim_ + j]; for (k = 0; k < j; k++) sum -= element_[idim_ + k] * element_[k*dim_ + j]; element_[idim_ + j] = sum; if ((std::fabs(dum = vv[i]*sum)) >= std::fabs(big)) { big = dum; imax = i; } } jdim_ = j*dim_; if (j != imax) { imaxdim_ = imax*dim_; for (k = 0; k < n; k++) { dum = element_[imaxdim_ + k]; element_[imaxdim_ + k] = element_[jdim_ + k]; element_[jdim_ + k] = dum; } d = -d; vv[imax] = vv[j]; } indx[j] = imax; if (j !=n-1) { dum = T(1)/(element_[jdim_ + j]); for (i = j + 1; i < n; i++) element_[i*dim_ + j] *= dum; } } } template void Matrix::lubksb (std::vector& indx, std::vector& b) { // int i, ip, j; // int ii { 0 }; // int idim_; // T sum; // int n { int(size()) }; // for (i = 0; i < n; i++) { // ip = indx[i]; // sum = b[ip]; // b[ip] = b[i]; // idim_ = i*dim_; // if (ii != 0) // for (j = ii-1; j < i; j++) sum -= element_[idim_ + j] * b[j]; // else if (sum != T(0)) // ii = i + 1; // b[i] = sum; // } // for (i = n - 1; i >= 0; i--) { // sum = b[i]; // idim_ = i*dim_; // for (j = i + 1; j < n; j++) sum -= element_[idim_ + j] * b[j]; // b[i] = sum/element_[idim_ + i]; // } std::size_t i, ip, j; std::size_t ii { 0 }; std::size_t idim_; T sum; std::size_t n { size() }; for (i = 0; i < n; i++) { ip = indx[i]; sum = b[ip]; b[ip] = b[i]; idim_ = i*dim_; if (ii != 0) for (j = ii-1; j < i; j++) sum -= element_[idim_ + j] * b[j]; else if (sum != T(0)) ii = i + 1; b[i] = sum; } for (i = n; i--;) { sum = b[i]; idim_ = i*dim_; for (j = i + 1; j < n; j++) sum -= element_[idim_ + j] * b[j]; b[i] = sum/element_[idim_ + i]; } } template std::complex Matrix::lndet_LU_destroy () { std::vector indx(size()); T d; std::complex lndet { 0 }; (*this).ludcmp(indx, d); lndet = log(std::complex(d)); for (IndexU j { 0 }; j < size(); j++) { lndet += log(std::complex(element_[j*dim_ + j])); } return lndet; } export template std::ostream& operator<< (std::ostream& s, const Matrix& M) { for (IndexU i { 0 }; i < M.size(); ++i) { for (IndexU j { 0 }; j < M.size(); ++j) s << M(i,j) << "\t"; s << std::endl; } return s; } //////////////////// // ↑ Class Matrix // //////////////////// // Additional utilities inline Real SIGN (const Real& a, const Real& b) { return b >= Real(0) ? (a >= Real(0) ? a : -a) : (a >= Real(0) ? -a : a); } Real pythag(const Real a, const Real b) { Real absa, absb; absa = std::abs(a); absb = std::abs(b); if (absa > absb) return absa * std::sqrt(Real(1) + (absb/absa)*(absb/absa)); else return absb * std::sqrt(Real(1) + (absa/absb)*(absa/absb)); } // Singular value decomposition // Numerical Recipes section 2.6 // Restricted to square matrix due to use of Matrix class export void svdcmp(Matrix& a, std::vector& w, Matrix& v) { bool flag; int i, its, j, jj, k, l, nm; Real anorm, c, f, g, h, s, scale, x, y, z; int m { int(a.size()) }; int n { int(a.size()) }; std::vector rv1(n); g = Real(0); scale = Real(0); anorm = Real(0); for (i = 0; i < n; i++) { l = i+2; rv1[i] = scale*g; g = Real(0); s = Real(0); scale = Real(0); if (i < m) { for (k = i; k < m; k++) scale += std::abs(a(k,i)); if (scale != Real(0)) { for (k = i; k < m; k++) { a(k,i) /= scale; s += a(k,i) * a(k,i); } f = a(i,i); g = -SIGN(std::sqrt(s), f); h = f*g-s; a(i,i) = f-g; for (j = l-1; j < n; j++) { for (s=Real(0), k=i; k < m; k++) s += a(k,i)*a(k,j); f=s/h; for (k=i; k < m; k++) a(k,j) += f*a(k,i); } for (k=i; k < m; k++) a(k,i) *= scale; } } w[i] = scale*g; g = Real(0); s = Real(0); scale = Real(0); if (i+1 <= m && i != n) { for (k = l-1; k < n; k++) scale += std::abs(a(i,k)); if (scale != Real(0)) { for (k=l-1; k < n; k++) { a(i,k) /= scale; s += a(i,k)*a(i,k); } f = a(i, l-1); g = -SIGN(std::sqrt(s), f); h = f*g - s; a(i, l-1) = f-g; for (k=l-1; k < n; k++) rv1[k] = a(i,k)/h; for (j = l-1; j < m; j++) { for (s=Real(0), k=l-1; k < n; k++) s += a(j,k)*a(i,k); for (k=l-1; k < n; k++) a(j,k) += s*rv1[k]; } for (k=l-1; k < n; k++) a(i,k) *= scale; } } anorm = std::max(anorm, std::abs(w[i]) + std::abs(rv1[i])); } for (i = n-1; i >= 0; i--) { // accumulation of right-hand transforms if (i < n-1) { if (g != Real(0)) { for (j = l; j < n; j++) v(j,i) = (a(i,j)/a(i,l))/g; for (j=l; j < n; j++) { for (s=Real(0), k=l; k < n; k++) s += a(i,k)*v(k,j); for (k=l; k < n; k++) v(k,j) += s*v(k,i); } } for (j=l; j < n; j++) { v(i,j) = Real(0); v(j,i) = Real(0); } } v(i,i) = Real(1); g = rv1[i]; l = i; } for (i = std::min(m,n) -1; i >= 0; i--) { // accumulation of left-hand transforms l = i+1; g = w[i]; for (j=l; j < n; j++) a(i,j) = Real(0); if (g != Real(0)) { g = Real(1)/g; for (j=l; j < n; j++) { for (s=Real(0), k=l; k < m; k++) s += a(k,i) * a(k,j); f = (s/a(i,i))*g; for (k=i; k < m; k++) a(k,j) += f*a(k,i); } for (j=i; j < m; j++) a(j,i) *= g; } else for (j=i; j < m; j++) a(j,i) = Real(0); ++a(i,i); } for (k=n-1; k >= 0; k--) { for (its=0; its < 30; its++) { flag = true; for (l=k; l >= 0; l--) { nm = l-1; if (std::abs(rv1[l]) + anorm == anorm) { flag = false; break; } if (std::abs(w[nm]) + anorm == anorm) break; } if (flag) { c = Real(0); s = Real(1); for (i=l-1; i < k+1; i++) { f = s*rv1[i]; rv1[i] = c*rv1[i]; if (std::abs(f) + anorm == anorm) break; g = w[i]; h = pythag(f,g); w[i] = h; h = Real(1)/h; c = g*h; s = -f*h; for (j=0; j < m; j++) { y = a(j, nm); z = a(j,i); a(j, nm) = y*c + z*s; a(j,i) = z*c - y*s; } } } z = w[k]; if (l == k) { if (z < Real(0)) { w[k] = -z; for (j=0; j < n; j++) v(j,k) = -v(j,k); } break; } if (its == 29) throw AbacusException("No convergence in 30 svdmp iterations"); x = w[l]; nm = k-1; y = w[nm]; g = rv1[nm]; h = rv1[k]; f = ((y-z) * (y+z) + (g-h) * (g+h))/(2*h*y); g = pythag(f, Real(1)); f = ((x-z)*(x+z) + h*((y/(f+SIGN(g,f)))-h))/x; c = Real(1); s = Real(1); // Next QR transformation for (j=l; j <= nm; j++) { i = j+1; g = rv1[i]; y = w[i]; h = s*g; g = c*g; z = pythag(f,h); rv1[j] = z; c = f/z; s = h/z; f = x*c + g*s; g = g*c - x*s; h = y*s; y *= c; for (jj = 0; jj < n; jj++) { x = v(jj, j); z = v(jj, i); v(jj, j) = x*c + z*s; v(jj, i) = z*c - x*s; } z = pythag(f,h); w[j] = z; if (z) { z = Real(1)/z; c = f*z; s = h*z; } f = c*g + s*y; x = c*y - s*g; for (jj=0; jj < m; jj++) { y = a(jj, j); z = a(jj, i); a(jj, j) = y*c + z*s; a(jj, i) = z*c - y*s; } } rv1[l] = Real(0); rv1[k] = f; w[k] = x; } } } // Householder reduction of a real symmetric matrix // NR section 11.2 export void tred2 (Matrix& a, std::vector& d, std::vector& e) { int l, k, j, i; Real scale, hh, h, g, f; int n { int(a.size()) }; for (i = n-1; i > 0; i--) { l = i - 1; h = scale = Real(0); if (l > 0) { for (k = 0; k < l + 1; k++) scale += std::abs(a(i,k)); if (scale == Real(0)) e[i] = a(i,l); else { for (k = 0; k < l + 1; k++) { a(i,k) /= scale; h += a(i,k) * a(i,k); } f = a(i,l); g = (f >= Real(0) ? -std::sqrt(h) : std::sqrt(h)); e[i] = scale * g; h -= f * g; a(i,l) = f - g; f = Real(0); for (j = 0; j < l + 1; j++) { a(j,i) = a(i,j)/h; g = Real(0); for (k = 0; k < j + 1; k++) g += a(j,k) * a(i,k); for (k = j + 1; k < l + 1; k++) g += a(k,j) * a(i,k); e[j] = g/h; f += e[j] * a(i,j); } hh = f/(h +h); for (j = 0; j < l + 1; j++) { f = a(i,j); e[j] = g = e[j] - hh * f; for (k = 0; k < j + 1; k++) a(j,k) -= (f * e[k] + g * a(i,k)); } } } else e[i] = a(i,l); d[i] = h; } d[0] = Real(0); e[0] = Real(0); for (i = 0; i < n; i++) { l = i; if (d[i] != Real(0)) { for (j = 0; j < l; j++) { g = Real(0); for (k = 0; k < l; k++) g += a(i,k) * a(k,j); for (k = 0; k < l; k++) a(k,j) -= g * a(k,i); } } d[i] = a(i,i); a(i,i) = Real(1); for (j = 0; j < l; j++) a(j,i) = a(i,j) = Real(0); } } // tred2 // QL algorithm with implicit shifts // NR section 11.3 export void tqli (std::vector& d, std::vector& e, Matrix& z) { int m, l, iter, i, k; Real s, r, p, g, f, dd, c, b; int n { int(d.size()) }; for (i = 1; i < n; i++) e[i-1] = e[i]; e[n-1] = Real(0); for (l = 0; l < n; l++) { iter = 0; do { for (m = l; m < n-1; m++) { dd = std::abs(d[m]) + std::abs(d[m+1]); if (std::abs(e[m]) + dd == dd) break; } if (m != l) { if (iter++ == 30) { std::cout << "Too many iterations in tqli" << std::endl; std::exit(1); } g = (d[l + 1] - d[l])/(2 * e[l]); r = pythag(g, Real(1)); g = d[m] - d[l] + e[l]/(g + SIGN(r, g)); s = c = Real(1); p = Real(0); for (i = m - 1; i >= l; i--) { f = s * e[i]; b = c * e[i]; e[i + 1] = (r = pythag(f,g)); if (r == Real(0)) { d[i + 1] -= p; e[m] = Real(0); break; } s = f/r; c = g/r; g = d[i + 1] - p; r = (d[i] - g) * s + 2 * c * b; d[i + 1] = g + (p = s * r); g = c * r - b; for (k = 0; k < n; k++) { f = z(k, i + 1); z(k, i + 1) = s * z(k, i) + c * f; z(k, i) = c * z(k, i) - s * f; } } if (r == Real(0) && i >= l) continue; d[l] -= p; e[l] = g; e[m] = Real(0); } } while (m != l); } } // tqli // Failed version using T** // // //////////////////// // // ↓ Class Matrix // // //////////////////// // export template // class Matrix { // private: // std::size_t dim_; // T** element_; // public: // Matrix (std::size_t dim); // // ~Matrix () = default; // ~Matrix (); // inline std::size_t size() { return dim_; }; // // inline T* operator[] (const IndexU i); // // inline const T* operator[] (const IndexU i) const; // inline T operator() (IndexU i, IndexU j) const { return element_[i][j]; }; // inline T& operator() (IndexU i, IndexU j) { return element_[i][j]; }; // void setZero(); // // Matrix& operator= (const Matrix& rhs); // Matrix& operator*= (const T& a); // Matrix& operator/= (const T& a); // void ludcmp (std::vector& indx, T& d); // void lubksb (std::vector& indx, std::vector& b); // std::complex lndet_LU_destroy (); // }; // template // Matrix::Matrix (std::size_t dim) // : dim_(dim) // , element_(new T*[dim]) // { // // element_[0] = new T[dim_*dim_]; // // for (IndexU i { 0 }; i + 1 < dim_; i++) element_[i+1] = element_[i] + dim_; // for (IndexU i { 0 }; i < dim_; ++i) element_[i] = new T[dim]; // } // template // Matrix::~Matrix() // { // // if (element_ != 0) { // // delete[] (element_[0]); // // delete[] (element_); // // } // for (IndexU i { 0 }; i < dim_; ++i) delete[] element_[i]; // delete[] element_; // } // // template // // inline T* Matrix::operator[] (const IndexU i) // // { // // return element_[i]; // // } // // template // // inline const T* Matrix::operator[] (const IndexU i) const // // { // // return element_[i]; // // } // template // void Matrix::setZero () // { // for (IndexU i { 0 }; i < dim_; ++i) // for (IndexU j { 0 }; j < dim_; ++j) element_[i][j] = T(0); // } // // template // // Matrix& Matrix::operator= (const Matrix& rhs) // // { // // if (this != &rhs) { // // if (dim_ != rhs.dim_) { // // throw; // // } // // for (int i = 0; i < dim_; ++i) // // for (int j = 0; j < dim_; ++j) element_[i][j] = rhs.element_[i][j]; // // } // // return *this; // // } // template // Matrix& Matrix::operator*= (const T& a) // { // for (IndexU i { 0 }; i < dim_; ++i) // for (IndexU j { 0 }; j < dim_; ++j) element_[i][j] *= a; // return *this; // } // template // Matrix& Matrix::operator/= (const T& a) // { // T oneovera = T(1)/a; // for (IndexU i { 0 }; i < dim_; ++i) // for (IndexU j { 0 }; j < dim_; ++j) element_[i][j] *= oneovera; // return *this; // } // template // void Matrix::ludcmp (std::vector& indx, T& d) // { // IndexU i, j, k; // IndexU imax { 0 }; // T big, dum, sum, temp; // IndexU n { size() }; // std::vector vv(n); // d = T(1); // for (i = 0; i < n; i++) { // big = T(0); // for (j = 0; j < n; j++) { // if ((std::fabs(temp = element_[i][j])) > std::fabs(big)) big = temp; // } // if (big == T(0)) throw; // vv[i] = T(1)/big; // } // for (j = 0; j < n; j++) { // for (i = 0; i < j; i++) { // sum = element_[i][j]; // for (k = 0; k < i; k++) sum -= element_[i][k] * element_[k][j]; // element_[i][j] = sum; // } // big = T(0); // for (i = j; i < n; i++) { // sum = element_[i][j]; // for (k = 0; k < j; k++) sum -= element_[i][k] * element_[k][j]; // element_[i][j] = sum; // if ((std::fabs(dum = vv[i]*sum)) >= std::fabs(big)) { // big = dum; // imax = i; // } // } // if (j != imax) { // for (k = 0; k < n; k++) { // dum = element_[imax][k]; // element_[imax][k] = element_[j][k]; // element_[j][k] = dum; // } // d = -d; // vv[imax] = vv[j]; // } // indx[j] = imax; // if (j !=n-1) { // dum = T(1)/(element_[j][j]); // for (i = j + 1; i < n; i++) element_[i][j] *= dum; // } // } // } // template // void Matrix::lubksb (std::vector& indx, std::vector& b) // { // int i, ip, j; // int ii { 0 }; // T sum; // int n { int(size()) }; // for (i = 0; i < n; i++) { // ip = indx[i]; // sum = b[ip]; // b[ip] = b[i]; // if (ii != 0) // for (j = ii-1; j < i; j++) sum -= element_[i][j] * b[j]; // else if (sum != T(0)) // ii = i + 1; // b[i] = sum; // } // for (i = n - 1; i >= 0; i--) { // sum = b[i]; // for (j = i + 1; j < n; j++) sum -= element_[i][j] * b[j]; // b[i] = sum/element_[i][i]; // } // } // template // std::complex Matrix::lndet_LU_destroy () // { // std::vector indx(size()); // T d; // std::complex lndet { 0 }; // (*this).ludcmp(indx, d); // lndet = log(std::complex(d)); // for (IndexU j { 0 }; j < size(); j++) { // lndet += log(std::complex(element_[j][j])); // } // return lndet; // } // template // std::ostream& operator<< (std::ostream& s, const Matrix& M) // { // for (IndexU i { 0 }; i < M.dim(); ++i) { // for (IndexU j { 0 }; j < M.dim(); ++j) s << M[i][j] << "\t"; // s << std::endl; // } // return s; // } // //////////////////// // // ↑ Class Matrix // // //////////////////// // // Version using vector of vector (not working) // //////////////////// // // ↓ Class Matrix // // //////////////////// // export template // class Matrix { // private: // std::size_t dim_; // std::vector> element_; // public: // Matrix (std::size_t dim); // ~Matrix () = default; // inline std::size_t size() { return dim_; }; // inline std::vector> rows () const { return element_; }; // inline std::vector& operator[] (const std::size_t i); // inline T operator() (IndexU i, IndexU j) const { return element_[i][j]; }; // inline T& operator() (IndexU i, IndexU j) { return element_[i][j]; }; // void setZero(); // Matrix& operator*= (const T& a); // Matrix& operator/= (const T& a); // void ludcmp (std::vector& indx, T& d); // void lubksb (std::vector& indx, std::vector& b); // std::complex lndet_LU_destroy (); // }; // template // inline std::vector& Matrix::operator[] (const std::size_t i) // { // return element_[i]; // } // template // Matrix::Matrix (std::size_t dim) // : dim_(dim) // , element_(std::vector>(dim_, std::vector(dim_))) // { // } // template // Matrix& Matrix::operator*= (const T& a) // { // for (auto &row : element_) // for (auto &col : row) col *= a; // return *this; // } // template // Matrix& Matrix::operator/= (const T& a) // { // for (auto &row : element_) // for (auto &col : row) col /= a; // return *this; // } // template // void Matrix::setZero () // { // for (auto &row : element_) // for (auto &col : row) col = T(0); // } // template // void Matrix::ludcmp (std::vector& indx, T& d) // { // IndexU i, j, k; // IndexU imax { 0 }; // T big, dum, sum, temp; // IndexU n { size() }; // std::vector vv(n); // d = T(1); // for (i = 0; i < n; i++) { // big = T(0); // for (j = 0; j < n; j++) { // if ((std::fabs(temp = element_[i][j])) > std::fabs(big)) big = temp; // } // if (big == T(0)) throw; // vv[i] = T(1)/big; // } // for (j = 0; j < n; j++) { // for (i = 0; i < j; i++) { // sum = element_[i][j]; // for (k = 0; k < i; k++) sum -= element_[i][k] * element_[k][j]; // element_[i][j] = sum; // } // big = T(0); // for (i = j; i < n; i++) { // sum = element_[i][j]; // for (k = 0; k < j; k++) sum -= element_[i][k] * element_[k][j]; // element_[i][j] = sum; // if ((std::fabs(dum = vv[i]*sum)) >= std::fabs(big)) { // big = dum; // imax = i; // } // } // if (j != imax) { // for (k = 0; k < n; k++) { // dum = element_[imax][k]; // element_[imax][k] = element_[j][k]; // element_[j][k] = dum; // } // d = -d; // vv[imax] = vv[j]; // } // indx[j] = imax; // if (j !=n-1) { // dum = T(1)/(element_[j][j]); // for (i = j + 1; i < n; i++) element_[i][j] *= dum; // } // } // } // template // void Matrix::lubksb (std::vector& indx, std::vector& b) // { // int i, ip, j; // int ii { 0 }; // T sum; // int n { int(size()) }; // for (i = 0; i < n; i++) { // ip = indx[i]; // sum = b[ip]; // b[ip] = b[i]; // if (ii != 0) // for (j = ii-1; j < i; j++) sum -= element_[i][j] * b[j]; // else if (sum != T(0)) // ii = i + 1; // b[i] = sum; // } // for (i = n - 1; i >= 0; i--) { // sum = b[i]; // for (j = i + 1; j < n; j++) sum -= element_[i][j] * b[j]; // b[i] = sum/element_[i][i]; // } // } // template // std::complex Matrix::lndet_LU_destroy () // { // std::vector indx(size()); // T d; // std::complex lndet { 0 }; // (*this).ludcmp(indx, d); // lndet = log(std::complex(d)); // for (IndexU j { 0 }; j < size(); j++) { // lndet += log(std::complex(element_[j][j])); // } // return lndet; // } // template // std::ostream& operator<< (std::ostream& s, const Matrix& M) // { // for (auto row : M.rows()) { // for (auto col : row) s << col << "\t"; // s << std::endl; // } // return s; // } // //////////////////// // // ↑ Class Matrix // // ////////////////////