// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at // the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights // reserved. See file COPYRIGHT for details. // // This file is part of the MFEM library. For more information and source code // availability see http://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the GNU Lesser General Public License (as published by the Free // Software Foundation) version 2.1 dated February 1999. // Implementation of data types dense matrix, inverse dense matrix #include "vector.hpp" #include "matrix.hpp" #include "densemat.hpp" #include "../general/table.hpp" #include "../general/globals.hpp" #include #include #include #include #include #if defined(_MSC_VER) && (_MSC_VER < 1800) #include #define copysign _copysign #endif #ifdef MFEM_USE_LAPACK extern "C" void dgemm_(char *, char *, int *, int *, int *, double *, double *, int *, double *, int *, double *, double *, int *); extern "C" void dgetrf_(int *, int *, double *, int *, int *, int *); extern "C" void dgetrs_(char *, int *, int *, double *, int *, int *, double *, int *, int *); extern "C" void dgetri_(int *N, double *A, int *LDA, int *IPIV, double *WORK, int *LWORK, int *INFO); extern "C" void dsyevr_(char *JOBZ, char *RANGE, char *UPLO, int *N, double *A, int *LDA, double *VL, double *VU, int *IL, int *IU, double *ABSTOL, int *M, double *W, double *Z, int *LDZ, int *ISUPPZ, double *WORK, int *LWORK, int *IWORK, int *LIWORK, int *INFO); extern "C" void dsyev_(char *JOBZ, char *UPLO, int *N, double *A, int *LDA, double *W, double *WORK, int *LWORK, int *INFO); extern "C" void dsygv_ (int *ITYPE, char *JOBZ, char *UPLO, int * N, double *A, int *LDA, double *B, int *LDB, double *W, double *WORK, int *LWORK, int *INFO); extern "C" void dgesvd_(char *JOBU, char *JOBVT, int *M, int *N, double *A, int *LDA, double *S, double *U, int *LDU, double *VT, int *LDVT, double *WORK, int *LWORK, int *INFO); #endif namespace mfem { using namespace std; DenseMatrix::DenseMatrix() : Matrix(0) { data = NULL; capacity = 0; } DenseMatrix::DenseMatrix(const DenseMatrix &m) : Matrix(m.height, m.width) { const int hw = height * width; if (hw > 0) { MFEM_ASSERT(m.data, "invalid source matrix"); data = new double[hw]; capacity = hw; std::memcpy(data, m.data, sizeof(double)*hw); } else { data = NULL; capacity = 0; } } DenseMatrix::DenseMatrix(int s) : Matrix(s) { MFEM_ASSERT(s >= 0, "invalid DenseMatrix size: " << s); capacity = s*s; if (capacity > 0) { data = new double[capacity](); // init with zeroes } else { data = NULL; } } DenseMatrix::DenseMatrix(int m, int n) : Matrix(m, n) { MFEM_ASSERT(m >= 0 && n >= 0, "invalid DenseMatrix size: " << m << " x " << n); capacity = m*n; if (capacity > 0) { data = new double[capacity](); // init with zeroes } else { data = NULL; } } DenseMatrix::DenseMatrix(const DenseMatrix &mat, char ch) : Matrix(mat.width, mat.height) { capacity = height*width; if (capacity > 0) { data = new double[capacity]; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { (*this)(i,j) = mat(j,i); } } } else { data = NULL; } } void DenseMatrix::SetSize(int h, int w) { MFEM_ASSERT(h >= 0 && w >= 0, "invalid DenseMatrix size: " << h << " x " << w); if (Height() == h && Width() == w) { return; } height = h; width = w; const int hw = h*w; if (hw > std::abs(capacity)) { if (capacity > 0) { delete [] data; } capacity = hw; data = new double[hw](); // init with zeroes } } double &DenseMatrix::Elem(int i, int j) { return (*this)(i,j); } const double &DenseMatrix::Elem(int i, int j) const { return (*this)(i,j); } void DenseMatrix::Mult(const double *x, double *y) const { if (width == 0) { for (int row = 0; row < height; row++) { y[row] = 0.0; } return; } double *d_col = data; double x_col = x[0]; for (int row = 0; row < height; row++) { y[row] = x_col*d_col[row]; } d_col += height; for (int col = 1; col < width; col++) { x_col = x[col]; for (int row = 0; row < height; row++) { y[row] += x_col*d_col[row]; } d_col += height; } } void DenseMatrix::Mult(const Vector &x, Vector &y) const { MFEM_ASSERT(height == y.Size() && width == x.Size(), "incompatible dimensions"); Mult((const double *)x, (double *)y); } double DenseMatrix::operator *(const DenseMatrix &m) const { MFEM_ASSERT(Height() == m.Height() && Width() == m.Width(), "incompatible dimensions"); const int hw = height * width; double a = 0.0; for (int i = 0; i < hw; i++) { a += data[i] * m.data[i]; } return a; } void DenseMatrix::MultTranspose(const double *x, double *y) const { double *d_col = data; for (int col = 0; col < width; col++) { double y_col = 0.0; for (int row = 0; row < height; row++) { y_col += x[row]*d_col[row]; } y[col] = y_col; d_col += height; } } void DenseMatrix::MultTranspose(const Vector &x, Vector &y) const { MFEM_ASSERT(height == x.Size() && width == y.Size(), "incompatible dimensions"); MultTranspose((const double *)x, (double *)y); } void DenseMatrix::AddMult(const Vector &x, Vector &y) const { MFEM_ASSERT(height == y.Size() && width == x.Size(), "incompatible dimensions"); const double *xp = x, *d_col = data; double *yp = y; for (int col = 0; col < width; col++) { double x_col = xp[col]; for (int row = 0; row < height; row++) { yp[row] += x_col*d_col[row]; } d_col += height; } } void DenseMatrix::AddMultTranspose(const Vector &x, Vector &y) const { MFEM_ASSERT(height == x.Size() && width == y.Size(), "incompatible dimensions"); const double *d_col = data; for (int col = 0; col < width; col++) { double y_col = 0.0; for (int row = 0; row < height; row++) { y_col += x[row]*d_col[row]; } y[col] += y_col; d_col += height; } } void DenseMatrix::AddMult_a(double a, const Vector &x, Vector &y) const { MFEM_ASSERT(height == y.Size() && width == x.Size(), "incompatible dimensions"); const double *xp = x, *d_col = data; double *yp = y; for (int col = 0; col < width; col++) { const double x_col = a*xp[col]; for (int row = 0; row < height; row++) { yp[row] += x_col*d_col[row]; } d_col += height; } } void DenseMatrix::AddMultTranspose_a(double a, const Vector &x, Vector &y) const { MFEM_ASSERT(height == x.Size() && width == y.Size(), "incompatible dimensions"); const double *d_col = data; for (int col = 0; col < width; col++) { double y_col = 0.0; for (int row = 0; row < height; row++) { y_col += x[row]*d_col[row]; } y[col] += a * y_col; d_col += height; } } double DenseMatrix::InnerProduct(const double *x, const double *y) const { double prod = 0.0; for (int i = 0; i < height; i++) { double Axi = 0.0; for (int j = 0; j < width; j++) { Axi += (*this)(i,j) * x[j]; } prod += y[i] * Axi; } return prod; } // LeftScaling this = diag(s) * this void DenseMatrix::LeftScaling(const Vector & s) { double * it_data = data; for (int j = 0; j < width; ++j) { for (int i = 0; i < height; ++i) { *(it_data++) *= s(i); } } } // InvLeftScaling this = diag(1./s) * this void DenseMatrix::InvLeftScaling(const Vector & s) { double * it_data = data; for (int j = 0; j < width; ++j) { for (int i = 0; i < height; ++i) { *(it_data++) /= s(i); } } } // RightScaling: this = this * diag(s); void DenseMatrix::RightScaling(const Vector & s) { double sj; double * it_data = data; for (int j = 0; j < width; ++j) { sj = s(j); for (int i = 0; i < height; ++i) { *(it_data++) *= sj; } } } // InvRightScaling: this = this * diag(1./s); void DenseMatrix::InvRightScaling(const Vector & s) { double * it_data = data; for (int j = 0; j < width; ++j) { const double sj = 1./s(j); for (int i = 0; i < height; ++i) { *(it_data++) *= sj; } } } // SymmetricScaling this = diag(sqrt(s)) * this * diag(sqrt(s)) void DenseMatrix::SymmetricScaling(const Vector & s) { if (height != width || s.Size() != height) { mfem_error("DenseMatrix::SymmetricScaling"); } double * ss = new double[width]; double * it_s = s.GetData(); double * it_ss = ss; for ( double * end_s = it_s + width; it_s != end_s; ++it_s) { *(it_ss++) = sqrt(*it_s); } double * it_data = data; for (int j = 0; j < width; ++j) { for (int i = 0; i < height; ++i) { *(it_data++) *= ss[i]*ss[j]; } } delete[] ss; } // InvSymmetricScaling this = diag(sqrt(1./s)) * this * diag(sqrt(1./s)) void DenseMatrix::InvSymmetricScaling(const Vector & s) { if (height != width || s.Size() != width) { mfem_error("DenseMatrix::SymmetricScaling"); } double * ss = new double[width]; double * it_s = s.GetData(); double * it_ss = ss; for (double * end_s = it_s + width; it_s != end_s; ++it_s) { *(it_ss++) = 1./sqrt(*it_s); } double * it_data = data; for (int j = 0; j < width; ++j) { for (int i = 0; i < height; ++i) { *(it_data++) *= ss[i]*ss[j]; } } delete[] ss; } double DenseMatrix::Trace() const { #ifdef MFEM_DEBUG if (Width() != Height()) { mfem_error("DenseMatrix::Trace() : not a square matrix!"); } #endif double t = 0.0; for (int i = 0; i < width; i++) { t += (*this)(i, i); } return t; } MatrixInverse *DenseMatrix::Inverse() const { return new DenseMatrixInverse(*this); } double DenseMatrix::Det() const { MFEM_ASSERT(Height() == Width() && Height() > 0, "The matrix must be square and " << "sized larger than zero to compute the determinant." << " Height() = " << Height() << ", Width() = " << Width()); switch (Height()) { case 1: return data[0]; case 2: return data[0] * data[3] - data[1] * data[2]; case 3: { const double *d = data; return d[0] * (d[4] * d[8] - d[5] * d[7]) + d[3] * (d[2] * d[7] - d[1] * d[8]) + d[6] * (d[1] * d[5] - d[2] * d[4]); } case 4: { const double *d = data; return d[ 0] * (d[ 5] * (d[10] * d[15] - d[11] * d[14]) - d[ 9] * (d[ 6] * d[15] - d[ 7] * d[14]) + d[13] * (d[ 6] * d[11] - d[ 7] * d[10]) ) - d[ 4] * (d[ 1] * (d[10] * d[15] - d[11] * d[14]) - d[ 9] * (d[ 2] * d[15] - d[ 3] * d[14]) + d[13] * (d[ 2] * d[11] - d[ 3] * d[10]) ) + d[ 8] * (d[ 1] * (d[ 6] * d[15] - d[ 7] * d[14]) - d[ 5] * (d[ 2] * d[15] - d[ 3] * d[14]) + d[13] * (d[ 2] * d[ 7] - d[ 3] * d[ 6]) ) - d[12] * (d[ 1] * (d[ 6] * d[11] - d[ 7] * d[10]) - d[ 5] * (d[ 2] * d[11] - d[ 3] * d[10]) + d[ 9] * (d[ 2] * d[ 7] - d[ 3] * d[ 6]) ); } default: { // In the general case we compute the determinant from the LU // decomposition. DenseMatrixInverse lu_factors(*this); return lu_factors.Det(); } } // not reachable } double DenseMatrix::Weight() const { if (Height() == Width()) { // return fabs(Det()); return Det(); } else if ((Height() == 2) && (Width() == 1)) { return sqrt(data[0] * data[0] + data[1] * data[1]); } else if ((Height() == 3) && (Width() == 1)) { return sqrt(data[0] * data[0] + data[1] * data[1] + data[2] * data[2]); } else if ((Height() == 3) && (Width() == 2)) { const double *d = data; double E = d[0] * d[0] + d[1] * d[1] + d[2] * d[2]; double G = d[3] * d[3] + d[4] * d[4] + d[5] * d[5]; double F = d[0] * d[3] + d[1] * d[4] + d[2] * d[5]; return sqrt(E * G - F * F); } mfem_error("DenseMatrix::Weight()"); return 0.0; } void DenseMatrix::Set(double alpha, const double *A) { const int s = Width()*Height(); for (int i = 0; i < s; i++) { data[i] = alpha*A[i]; } } void DenseMatrix::Add(const double c, const DenseMatrix &A) { for (int j = 0; j < Width(); j++) { for (int i = 0; i < Height(); i++) { (*this)(i,j) += c * A(i,j); } } } DenseMatrix &DenseMatrix::operator=(double c) { const int s = Height()*Width(); for (int i = 0; i < s; i++) { data[i] = c; } return *this; } DenseMatrix &DenseMatrix::operator=(const double *d) { const int s = Height()*Width(); for (int i = 0; i < s; i++) { data[i] = d[i]; } return *this; } DenseMatrix &DenseMatrix::operator=(const DenseMatrix &m) { SetSize(m.height, m.width); const int hw = height * width; for (int i = 0; i < hw; i++) { data[i] = m.data[i]; } return *this; } DenseMatrix &DenseMatrix::operator+=(const double *m) { const int hw = Height()*Width(); for (int i = 0; i < hw; i++) { data[i] += m[i]; } return *this; } DenseMatrix &DenseMatrix::operator+=(const DenseMatrix &m) { MFEM_ASSERT(Height() == m.Height() && Width() == m.Width(), "incompatible matrix sizes."); return *this += m.GetData(); } DenseMatrix &DenseMatrix::operator-=(const DenseMatrix &m) { for (int j = 0; j < width; j++) { for (int i = 0; i < height; i++) { (*this)(i, j) -= m(i, j); } } return *this; } DenseMatrix &DenseMatrix::operator*=(double c) { int s = Height()*Width(); for (int i = 0; i < s; i++) { data[i] *= c; } return *this; } void DenseMatrix::Neg() { const int hw = Height() * Width(); for (int i = 0; i < hw; i++) { data[i] = -data[i]; } } void DenseMatrix::Invert() { #ifdef MFEM_DEBUG if (Height() <= 0 || Height() != Width()) { mfem_error("DenseMatrix::Invert()"); } #endif #ifdef MFEM_USE_LAPACK int *ipiv = new int[width]; int lwork = -1; double qwork, *work; int info; dgetrf_(&width, &width, data, &width, ipiv, &info); if (info) { mfem_error("DenseMatrix::Invert() : Error in DGETRF"); } dgetri_(&width, data, &width, ipiv, &qwork, &lwork, &info); lwork = (int) qwork; work = new double[lwork]; dgetri_(&width, data, &width, ipiv, work, &lwork, &info); if (info) { mfem_error("DenseMatrix::Invert() : Error in DGETRI"); } delete [] work; delete [] ipiv; #else int c, i, j, n = Width(); double a, b; Array piv(n); for (c = 0; c < n; c++) { a = fabs((*this)(c, c)); i = c; for (j = c + 1; j < n; j++) { b = fabs((*this)(j, c)); if (a < b) { a = b; i = j; } } if (a == 0.0) { mfem_error("DenseMatrix::Invert() : singular matrix"); } piv[c] = i; for (j = 0; j < n; j++) { Swap((*this)(c, j), (*this)(i, j)); } a = (*this)(c, c) = 1.0 / (*this)(c, c); for (j = 0; j < c; j++) { (*this)(c, j) *= a; } for (j++; j < n; j++) { (*this)(c, j) *= a; } for (i = 0; i < c; i++) { (*this)(i, c) = a * (b = -(*this)(i, c)); for (j = 0; j < c; j++) { (*this)(i, j) += b * (*this)(c, j); } for (j++; j < n; j++) { (*this)(i, j) += b * (*this)(c, j); } } for (i++; i < n; i++) { (*this)(i, c) = a * (b = -(*this)(i, c)); for (j = 0; j < c; j++) { (*this)(i, j) += b * (*this)(c, j); } for (j++; j < n; j++) { (*this)(i, j) += b * (*this)(c, j); } } } for (c = n - 1; c >= 0; c--) { j = piv[c]; for (i = 0; i < n; i++) { Swap((*this)(i, c), (*this)(i, j)); } } #endif } void DenseMatrix::SquareRootInverse() { // Square root inverse using Denman--Beavers #ifdef MFEM_DEBUG if (Height() <= 0 || Height() != Width()) { mfem_error("DenseMatrix::SquareRootInverse() matrix not square."); } #endif DenseMatrix tmp1(Height()); DenseMatrix tmp2(Height()); DenseMatrix tmp3(Height()); tmp1 = (*this); (*this) = 0.0; for (int v = 0; v < Height() ; v++) { (*this)(v,v) = 1.0; } for (int j = 0; j < 10; j++) { for (int i = 0; i < 10; i++) { tmp2 = tmp1; tmp3 = (*this); tmp2.Invert(); tmp3.Invert(); tmp1 += tmp3; (*this) += tmp2; tmp1 *= 0.5; (*this) *= 0.5; } mfem::Mult((*this), tmp1, tmp2); for (int v = 0; v < Height() ; v++) { tmp2(v,v) -= 1.0; } if (tmp2.FNorm() < 1e-10) { break; } } if (tmp2.FNorm() > 1e-10) { mfem_error("DenseMatrix::SquareRootInverse not converged"); } } void DenseMatrix::Norm2(double *v) const { for (int j = 0; j < Width(); j++) { v[j] = 0.0; for (int i = 0; i < Height(); i++) { v[j] += (*this)(i,j)*(*this)(i,j); } v[j] = sqrt(v[j]); } } double DenseMatrix::MaxMaxNorm() const { int hw = Height()*Width(); const double *d = data; double norm = 0.0, abs_entry; for (int i = 0; i < hw; i++) { abs_entry = fabs(d[i]); if (norm < abs_entry) { norm = abs_entry; } } return norm; } void DenseMatrix::FNorm(double &scale_factor, double &scaled_fnorm2) const { int i, hw = Height() * Width(); double max_norm = 0.0, entry, fnorm2; for (i = 0; i < hw; i++) { entry = fabs(data[i]); if (entry > max_norm) { max_norm = entry; } } if (max_norm == 0.0) { scale_factor = scaled_fnorm2 = 0.0; return; } fnorm2 = 0.0; for (i = 0; i < hw; i++) { entry = data[i] / max_norm; fnorm2 += entry * entry; } scale_factor = max_norm; scaled_fnorm2 = fnorm2; } void dsyevr_Eigensystem(DenseMatrix &a, Vector &ev, DenseMatrix *evect) { #ifdef MFEM_USE_LAPACK ev.SetSize(a.Width()); char JOBZ = 'N'; char RANGE = 'A'; char UPLO = 'U'; int N = a.Width(); double *A = new double[N*N]; int LDA = N; double VL = 0.0; double VU = 1.0; int IL = 0; int IU = 1; double ABSTOL = 0.0; int M; double *W = ev.GetData(); double *Z = NULL; int LDZ = 1; int *ISUPPZ = new int[2*N]; int LWORK = -1; // query optimal (double) workspace size double QWORK; double *WORK = NULL; int LIWORK = -1; // query optimal (int) workspace size int QIWORK; int *IWORK = NULL; int INFO; if (evect) // Compute eigenvectors too { evect->SetSize(N); JOBZ = 'V'; Z = evect->Data(); LDZ = N; } int hw = a.Height() * a.Width(); double *data = a.Data(); for (int i = 0; i < hw; i++) { A[i] = data[i]; } dsyevr_( &JOBZ, &RANGE, &UPLO, &N, A, &LDA, &VL, &VU, &IL, &IU, &ABSTOL, &M, W, Z, &LDZ, ISUPPZ, &QWORK, &LWORK, &QIWORK, &LIWORK, &INFO ); LWORK = (int) QWORK; LIWORK = QIWORK; WORK = new double[LWORK]; IWORK = new int[LIWORK]; dsyevr_( &JOBZ, &RANGE, &UPLO, &N, A, &LDA, &VL, &VU, &IL, &IU, &ABSTOL, &M, W, Z, &LDZ, ISUPPZ, WORK, &LWORK, IWORK, &LIWORK, &INFO ); if (INFO != 0) { mfem::err << "dsyevr_Eigensystem(...): DSYEVR error code: " << INFO << endl; mfem_error(); } #ifdef MFEM_DEBUG if (M < N) { mfem::err << "dsyevr_Eigensystem(...):\n" << " DSYEVR did not find all eigenvalues " << M << "/" << N << endl; mfem_error(); } if (CheckFinite(W, N) > 0) { mfem_error("dsyevr_Eigensystem(...): inf/nan values in W"); } if (CheckFinite(Z, N*N) > 0) { mfem_error("dsyevr_Eigensystem(...): inf/nan values in Z"); } VU = 0.0; for (IL = 0; IL < N; IL++) for (IU = 0; IU <= IL; IU++) { VL = 0.0; for (M = 0; M < N; M++) { VL += Z[M+IL*N] * Z[M+IU*N]; } if (IU < IL) { VL = fabs(VL); } else { VL = fabs(VL-1.0); } if (VL > VU) { VU = VL; } if (VU > 0.5) { mfem::err << "dsyevr_Eigensystem(...):" << " Z^t Z - I deviation = " << VU << "\n W[max] = " << W[N-1] << ", W[min] = " << W[0] << ", N = " << N << endl; mfem_error(); } } if (VU > 1e-9) { mfem::err << "dsyevr_Eigensystem(...):" << " Z^t Z - I deviation = " << VU << "\n W[max] = " << W[N-1] << ", W[min] = " << W[0] << ", N = " << N << endl; } if (VU > 1e-5) { mfem_error("dsyevr_Eigensystem(...): ERROR: ..."); } VU = 0.0; for (IL = 0; IL < N; IL++) for (IU = 0; IU < N; IU++) { VL = 0.0; for (M = 0; M < N; M++) { VL += Z[IL+M*N] * W[M] * Z[IU+M*N]; } VL = fabs(VL-data[IL+N*IU]); if (VL > VU) { VU = VL; } } if (VU > 1e-9) { mfem::err << "dsyevr_Eigensystem(...):" << " max matrix deviation = " << VU << "\n W[max] = " << W[N-1] << ", W[min] = " << W[0] << ", N = " << N << endl; } if (VU > 1e-5) { mfem_error("dsyevr_Eigensystem(...): ERROR: ..."); } #endif delete [] IWORK; delete [] WORK; delete [] ISUPPZ; delete [] A; #endif } void dsyev_Eigensystem(DenseMatrix &a, Vector &ev, DenseMatrix *evect) { #ifdef MFEM_USE_LAPACK int N = a.Width(); char JOBZ = 'N'; char UPLO = 'U'; int LDA = N; int LWORK = -1; /* query optimal workspace size */ int INFO; ev.SetSize(N); double *A = NULL; double *W = ev.GetData(); double *WORK = NULL; double QWORK; if (evect) { JOBZ = 'V'; evect->SetSize(N); A = evect->Data(); } else { A = new double[N*N]; } int hw = a.Height() * a.Width(); double *data = a.Data(); for (int i = 0; i < hw; i++) { A[i] = data[i]; } dsyev_(&JOBZ, &UPLO, &N, A, &LDA, W, &QWORK, &LWORK, &INFO); LWORK = (int) QWORK; WORK = new double[LWORK]; dsyev_(&JOBZ, &UPLO, &N, A, &LDA, W, WORK, &LWORK, &INFO); if (INFO != 0) { mfem::err << "dsyev_Eigensystem: DSYEV error code: " << INFO << endl; mfem_error(); } delete [] WORK; if (evect == NULL) { delete [] A; } #endif } void DenseMatrix::Eigensystem(Vector &ev, DenseMatrix *evect) { #ifdef MFEM_USE_LAPACK // dsyevr_Eigensystem(*this, ev, evect); dsyev_Eigensystem(*this, ev, evect); #else mfem_error("DenseMatrix::Eigensystem"); #endif } void dsygv_Eigensystem(DenseMatrix &a, DenseMatrix &b, Vector &ev, DenseMatrix *evect) { #ifdef MFEM_USE_LAPACK int N = a.Width(); int ITYPE = 1; char JOBZ = 'N'; char UPLO = 'U'; int LDA = N; int LDB = N; int LWORK = -1; /* query optimal workspace size */ int INFO; ev.SetSize(N); double *A = NULL; double *B = new double[N*N]; double *W = ev.GetData(); double *WORK = NULL; double QWORK; if (evect) { JOBZ = 'V'; evect->SetSize(N); A = evect->Data(); } else { A = new double[N*N]; } int hw = a.Height() * a.Width(); double *a_data = a.Data(); double *b_data = b.Data(); for (int i = 0; i < hw; i++) { A[i] = a_data[i]; B[i] = b_data[i]; } dsygv_(&ITYPE, &JOBZ, &UPLO, &N, A, &LDA, B, &LDB, W, &QWORK, &LWORK, &INFO); LWORK = (int) QWORK; WORK = new double[LWORK]; dsygv_(&ITYPE, &JOBZ, &UPLO, &N, A, &LDA, B, &LDB, W, WORK, &LWORK, &INFO); if (INFO != 0) { mfem::err << "dsygv_Eigensystem: DSYGV error code: " << INFO << endl; mfem_error(); } delete [] WORK; delete [] B; if (evect == NULL) { delete [] A; } #endif } void DenseMatrix::Eigensystem(DenseMatrix &b, Vector &ev, DenseMatrix *evect) { #ifdef MFEM_USE_LAPACK dsygv_Eigensystem(*this, b, ev, evect); #else mfem_error("DenseMatrix::Eigensystem for generalized eigenvalues"); #endif } void DenseMatrix::SingularValues(Vector &sv) const { #ifdef MFEM_USE_LAPACK DenseMatrix copy_of_this = *this; char jobu = 'N'; char jobvt = 'N'; int m = Height(); int n = Width(); double *a = copy_of_this.data; sv.SetSize(min(m, n)); double *s = sv; double *u = NULL; double *vt = NULL; double *work = NULL; int lwork = -1; int info; double qwork; dgesvd_(&jobu, &jobvt, &m, &n, a, &m, s, u, &m, vt, &n, &qwork, &lwork, &info); lwork = (int) qwork; work = new double[lwork]; dgesvd_(&jobu, &jobvt, &m, &n, a, &m, s, u, &m, vt, &n, work, &lwork, &info); delete [] work; if (info) { mfem::err << "DenseMatrix::SingularValues : info = " << info << endl; mfem_error(); } #else // compiling without lapack mfem_error("DenseMatrix::SingularValues"); #endif } int DenseMatrix::Rank(double tol) const { int rank=0; Vector sv(min(Height(), Width())); SingularValues(sv); for (int i=0; i < sv.Size(); ++i) if (sv(i) >= tol) { ++rank; } return rank; } static const double sqrt_1_eps = sqrt(1./numeric_limits::epsilon()); inline void Eigenvalues2S(const double &d12, double &d1, double &d2) { if (d12 != 0.) { // "The Symmetric Eigenvalue Problem", B. N. Parlett, pp.189-190 double t, zeta = (d2 - d1)/(2*d12); // inf/inf from overflows? if (fabs(zeta) < sqrt_1_eps) { t = d12*copysign(1./(fabs(zeta) + sqrt(1. + zeta*zeta)), zeta); } else { t = d12*copysign(0.5/fabs(zeta), zeta); } d1 -= t; d2 += t; } } inline void Eigensystem2S(const double &d12, double &d1, double &d2, double &c, double &s) { if (d12 == 0.) { c = 1.; s = 0.; } else { // "The Symmetric Eigenvalue Problem", B. N. Parlett, pp.189-190 double t, zeta = (d2 - d1)/(2*d12); if (fabs(zeta) < sqrt_1_eps) { t = copysign(1./(fabs(zeta) + sqrt(1. + zeta*zeta)), zeta); } else { t = copysign(0.5/fabs(zeta), zeta); } // c = 1./sqrt(1. + t*t); c = sqrt(1./(1. + t*t)); s = c*t; t *= d12; d1 -= t; d2 += t; } } inline void vec_normalize3_aux( const double &x1, const double &x2, const double &x3, double &n1, double &n2, double &n3) { double m, t, r; m = fabs(x1); r = x2/m; t = 1. + r*r; r = x3/m; t = sqrt(1./(t + r*r)); n1 = copysign(t, x1); t /= m; n2 = x2*t; n3 = x3*t; } inline void vec_normalize3(const double &x1, const double &x2, const double &x3, double &n1, double &n2, double &n3) { // should work ok when xk is the same as nk for some or all k if (fabs(x1) >= fabs(x2)) { if (fabs(x1) >= fabs(x3)) { if (x1 != 0.) { vec_normalize3_aux(x1, x2, x3, n1, n2, n3); } else { n1 = n2 = n3 = 0.; } return; } } else if (fabs(x2) >= fabs(x3)) { vec_normalize3_aux(x2, x1, x3, n2, n1, n3); return; } vec_normalize3_aux(x3, x1, x2, n3, n1, n2); } inline bool KernelVector2G( const int &mode, double &d1, double &d12, double &d21, double &d2) { // Find a vector (z1,z2) in the "near"-kernel of the matrix // | d1 d12 | // | d21 d2 | // using QR factorization. // The vector (z1,z2) is returned in (d1,d2). Return 'true' if the matrix // is zero without setting (d1,d2). // Note: in the current implementation |z1| + |z2| = 1. // l1-norms of the columns double n1 = fabs(d1) + fabs(d21); double n2 = fabs(d2) + fabs(d12); bool swap_columns = (n2 > n1); double mu; if (!swap_columns) { if (n1 == 0.) { return true; } if (mode == 0) // eliminate the larger entry in the column { if (fabs(d1) > fabs(d21)) { Swap(d1, d21); Swap(d12, d2); } } else // eliminate the smaller entry in the column { if (fabs(d1) < fabs(d21)) { Swap(d1, d21); Swap(d12, d2); } } } else { // n2 > n1, swap columns 1 and 2 if (mode == 0) // eliminate the larger entry in the column { if (fabs(d12) > fabs(d2)) { Swap(d1, d2); Swap(d12, d21); } else { Swap(d1, d12); Swap(d21, d2); } } else // eliminate the smaller entry in the column { if (fabs(d12) < fabs(d2)) { Swap(d1, d2); Swap(d12, d21); } else { Swap(d1, d12); Swap(d21, d2); } } } n1 = hypot(d1, d21); if (d21 != 0.) { // v = (n1, n2)^t, |v| = 1 // Q = I - 2 v v^t, Q (d1, d21)^t = (mu, 0)^t mu = copysign(n1, d1); n1 = -d21*(d21/(d1 + mu)); // = d1 - mu d1 = mu; // normalize (n1,d21) to avoid overflow/underflow // normalize (n1,d21) by the max-norm to avoid the sqrt call if (fabs(n1) <= fabs(d21)) { // (n1,n2) <-- (n1/d21,1) n1 = n1/d21; mu = (2./(1. + n1*n1))*(n1*d12 + d2); d2 = d2 - mu; d12 = d12 - mu*n1; } else { // (n1,n2) <-- (1,d21/n1) n2 = d21/n1; mu = (2./(1. + n2*n2))*(d12 + n2*d2); d2 = d2 - mu*n2; d12 = d12 - mu; } } // Solve: // | d1 d12 | | z1 | = | 0 | // | 0 d2 | | z2 | | 0 | // choose (z1,z2) to minimize |d1*z1 + d12*z2| + |d2*z2| // under the condition |z1| + |z2| = 1, z2 >= 0 (for uniqueness) // set t = z1, z2 = 1 - |t|, -1 <= t <= 1 // objective function is: // |d1*t + d12*(1 - |t|)| + |d2|*(1 - |t|) -- piecewise linear with // possible minima are -1,0,1,t1 where t1: d1*t1 + d12*(1 - |t1|) = 0 // values: @t=+/-1 -> |d1|, @t=0 -> |n1| + |d2|, @t=t1 -> |d2|*(1 - |t1|) // evaluate z2 @t=t1 mu = -d12/d1; // note: |mu| <= 1, if using l2-norm for column pivoting // |mu| <= sqrt(2), if using l1-norm n2 = 1./(1. + fabs(mu)); // check if |d1|<=|d2|*z2 if (fabs(d1) <= n2*fabs(d2)) { d2 = 0.; d1 = 1.; } else { d2 = n2; // d1 = (n2 < 0.5) ? copysign(1. - n2, mu) : mu*n2; d1 = mu*n2; } if (swap_columns) { Swap(d1, d2); } return false; } inline int KernelVector3G_aux( const int &mode, double &d1, double &d2, double &d3, double &c12, double &c13, double &c23, double &c21, double &c31, double &c32) { int kdim; double mu, n1, n2, n3, s1, s2, s3; s1 = hypot(c21, c31); n1 = hypot(d1, s1); if (s1 != 0.) { // v = (s1, s2, s3)^t, |v| = 1 // Q = I - 2 v v^t, Q (d1, c12, c13)^t = (mu, 0, 0)^t mu = copysign(n1, d1); n1 = -s1*(s1/(d1 + mu)); // = d1 - mu d1 = mu; // normalize (n1,c21,c31) to avoid overflow/underflow // normalize (n1,c21,c31) by the max-norm to avoid the sqrt call if (fabs(n1) >= fabs(c21)) { if (fabs(n1) >= fabs(c31)) { // n1 is max, (s1,s2,s3) <-- (1,c21/n1,c31/n1) s2 = c21/n1; s3 = c31/n1; mu = 2./(1. + s2*s2 + s3*s3); n2 = mu*(c12 + s2*d2 + s3*c32); n3 = mu*(c13 + s2*c23 + s3*d3); c12 = c12 - n2; d2 = d2 - s2*n2; c32 = c32 - s3*n2; c13 = c13 - n3; c23 = c23 - s2*n3; d3 = d3 - s3*n3; goto done_column_1; } } else if (fabs(c21) >= fabs(c31)) { // c21 is max, (s1,s2,s3) <-- (n1/c21,1,c31/c21) s1 = n1/c21; s3 = c31/c21; mu = 2./(1. + s1*s1 + s3*s3); n2 = mu*(s1*c12 + d2 + s3*c32); n3 = mu*(s1*c13 + c23 + s3*d3); c12 = c12 - s1*n2; d2 = d2 - n2; c32 = c32 - s3*n2; c13 = c13 - s1*n3; c23 = c23 - n3; d3 = d3 - s3*n3; goto done_column_1; } // c31 is max, (s1,s2,s3) <-- (n1/c31,c21/c31,1) s1 = n1/c31; s2 = c21/c31; mu = 2./(1. + s1*s1 + s2*s2); n2 = mu*(s1*c12 + s2*d2 + c32); n3 = mu*(s1*c13 + s2*c23 + d3); c12 = c12 - s1*n2; d2 = d2 - s2*n2; c32 = c32 - n2; c13 = c13 - s1*n3; c23 = c23 - s2*n3; d3 = d3 - n3; } done_column_1: // Solve: // | d2 c23 | | z2 | = | 0 | // | c32 d3 | | z3 | | 0 | if (KernelVector2G(mode, d2, c23, c32, d3)) { // Have two solutions: // two vectors in the kernel are P (-c12/d1, 1, 0)^t and // P (-c13/d1, 0, 1)^t where P is the permutation matrix swapping // entries 1 and col. // A vector orthogonal to both these vectors is P (1, c12/d1, c13/d1)^t d2 = c12/d1; d3 = c13/d1; d1 = 1.; kdim = 2; } else { // solve for z1: // note: |z1| <= a since |z2| + |z3| = 1, and // max{|c12|,|c13|} <= max{norm(col. 2),norm(col. 3)} // <= norm(col. 1) <= a |d1| // a = 1, if using l2-norm for column pivoting // a = sqrt(3), if using l1-norm d1 = -(c12*d2 + c13*d3)/d1; kdim = 1; } vec_normalize3(d1, d2, d3, d1, d2, d3); return kdim; } inline int KernelVector3S( const int &mode, const double &d12, const double &d13, const double &d23, double &d1, double &d2, double &d3) { // Find a unit vector (z1,z2,z3) in the "near"-kernel of the matrix // | d1 d12 d13 | // | d12 d2 d23 | // | d13 d23 d3 | // using QR factorization. // The vector (z1,z2,z3) is returned in (d1,d2,d3). // Returns the dimension of the kernel, kdim, but never zero. // - if kdim == 3, then (d1,d2,d3) is not defined, // - if kdim == 2, then (d1,d2,d3) is a vector orthogonal to the kernel, // - otherwise kdim == 1 and (d1,d2,d3) is a vector in the "near"-kernel. double c12 = d12, c13 = d13, c23 = d23; double c21, c31, c32; int col, row; // l1-norms of the columns: c32 = fabs(d1) + fabs(c12) + fabs(c13); c31 = fabs(d2) + fabs(c12) + fabs(c23); c21 = fabs(d3) + fabs(c13) + fabs(c23); // column pivoting: choose the column with the largest norm if (c32 >= c21) { col = (c32 >= c31) ? 1 : 2; } else { col = (c31 >= c21) ? 2 : 3; } switch (col) { case 1: if (c32 == 0.) // zero matrix { return 3; } break; case 2: if (c31 == 0.) // zero matrix { return 3; } Swap(c13, c23); Swap(d1, d2); break; case 3: if (c21 == 0.) // zero matrix { return 3; } Swap(c12, c23); Swap(d1, d3); } // row pivoting depending on 'mode' if (mode == 0) { if (fabs(d1) <= fabs(c13)) { row = (fabs(d1) <= fabs(c12)) ? 1 : 2; } else { row = (fabs(c12) <= fabs(c13)) ? 2 : 3; } } else { if (fabs(d1) >= fabs(c13)) { row = (fabs(d1) >= fabs(c12)) ? 1 : 2; } else { row = (fabs(c12) >= fabs(c13)) ? 2 : 3; } } switch (row) { case 1: c21 = c12; c31 = c13; c32 = c23; break; case 2: c21 = d1; c31 = c13; c32 = c23; d1 = c12; c12 = d2; d2 = d1; c13 = c23; c23 = c31; break; case 3: c21 = c12; c31 = d1; c32 = c12; d1 = c13; c12 = c23; c13 = d3; d3 = d1; } row = KernelVector3G_aux(mode, d1, d2, d3, c12, c13, c23, c21, c31, c32); // row is kdim switch (col) { case 2: Swap(d1, d2); break; case 3: Swap(d1, d3); } return row; } inline int Reduce3S( const int &mode, double &d1, double &d2, double &d3, double &d12, double &d13, double &d23, double &z1, double &z2, double &z3, double &v1, double &v2, double &v3, double &g) { // Given the matrix // | d1 d12 d13 | // A = | d12 d2 d23 | // | d13 d23 d3 | // and a unit eigenvector z=(z1,z2,z3), transform the matrix A into the // matrix B = Q P A P Q that has the form // | b1 0 0 | // B = Q P A P Q = | 0 b2 b23 | // | 0 b23 b3 | // where P is the permutation matrix switching entries 1 and k, and // Q is the reflection matrix Q = I - g v v^t, defined by: set y = P z and // v = c(y - e_1); if y = e_1, then v = 0 and Q = I. // Note: Q y = e_1, Q e_1 = y ==> Q P A P Q e_1 = ... = lambda e_1. // The entries (b1,b2,b3,b23) are returned in (d1,d2,d3,d23), and the // return value of the function is k. The variable g = 2/(v1^2+v2^2+v3^3). int k; double s, w1, w2, w3; if (mode == 0) { // choose k such that z^t e_k = zk has the smallest absolute value, i.e. // the angle between z and e_k is closest to pi/2 if (fabs(z1) <= fabs(z3)) { k = (fabs(z1) <= fabs(z2)) ? 1 : 2; } else { k = (fabs(z2) <= fabs(z3)) ? 2 : 3; } } else { // choose k such that zk is the largest by absolute value if (fabs(z1) >= fabs(z3)) { k = (fabs(z1) >= fabs(z2)) ? 1 : 2; } else { k = (fabs(z2) >= fabs(z3)) ? 2 : 3; } } switch (k) { case 2: Swap(d13, d23); Swap(d1, d2); Swap(z1, z2); break; case 3: Swap(d12, d23); Swap(d1, d3); Swap(z1, z3); } s = hypot(z2, z3); if (s == 0.) { // s can not be zero, if zk is the smallest (mode == 0) v1 = v2 = v3 = 0.; g = 1.; } else { g = copysign(1., z1); v1 = -s*(s/(z1 + g)); // = z1 - g // normalize (v1,z2,z3) by its max-norm, avoiding the sqrt call g = fabs(v1); if (fabs(z2) > g) { g = fabs(z2); } if (fabs(z3) > g) { g = fabs(z3); } v1 = v1/g; v2 = z2/g; v3 = z3/g; g = 2./(v1*v1 + v2*v2 + v3*v3); // Compute Q A Q = A - v w^t - w v^t, where // w = u - (g/2)(v^t u) v, and u = g A v // set w = g A v w1 = g*( d1*v1 + d12*v2 + d13*v3); w2 = g*(d12*v1 + d2*v2 + d23*v3); w3 = g*(d13*v1 + d23*v2 + d3*v3); // w := w - (g/2)(v^t w) v s = (g/2)*(v1*w1 + v2*w2 + v3*w3); w1 -= s*v1; w2 -= s*v2; w3 -= s*v3; // dij -= vi*wj + wi*vj d1 -= 2*v1*w1; d2 -= 2*v2*w2; d23 -= v2*w3 + v3*w2; d3 -= 2*v3*w3; // compute the offdiagonal entries on the first row/column of B which // should be zero (for debugging): #if 0 s = d12 - v1*w2 - v2*w1; // b12 = 0 s = d13 - v1*w3 - v3*w1; // b13 = 0 #endif } switch (k) { case 2: Swap(z1, z2); break; case 3: Swap(z1, z3); } return k; } inline void GetScalingFactor(const double &d_max, double &mult) { int d_exp; if (d_max > 0.) { mult = frexp(d_max, &d_exp); if (d_exp == numeric_limits::max_exponent) { mult *= numeric_limits::radix; } mult = d_max/mult; } else { mult = 1.; } // mult = 2^d_exp is such that d_max/mult is in [0.5,1) // or in other words d_max is in the interval [0.5,1)*mult } double DenseMatrix::CalcSingularvalue(const int i) const { MFEM_ASSERT(Height() == Width() && Height() > 0 && Height() < 4, "The matrix must be square and sized 1, 2, or 3 to compute the" " singular values." << " Height() = " << Height() << ", Width() = " << Width()); const int n = Height(); const double *d = data; if (n == 1) { return d[0]; } else if (n == 2) { double d0, d1, d2, d3; d0 = d[0]; d1 = d[1]; d2 = d[2]; d3 = d[3]; double mult; { double d_max = fabs(d0); if (d_max < fabs(d1)) { d_max = fabs(d1); } if (d_max < fabs(d2)) { d_max = fabs(d2); } if (d_max < fabs(d3)) { d_max = fabs(d3); } GetScalingFactor(d_max, mult); } d0 /= mult; d1 /= mult; d2 /= mult; d3 /= mult; // double b11 = d[0]*d[0] + d[1]*d[1]; // double b12 = d[0]*d[2] + d[1]*d[3]; // double b22 = d[2]*d[2] + d[3]*d[3]; // t = 0.5*(a+b).(a-b) = 0.5*(|a|^2-|b|^2) // with a,b - the columns of (*this) // double t = 0.5*(b11 - b22); double t = 0.5*((d0+d2)*(d0-d2)+(d1-d3)*(d1+d3)); // double s = sqrt(0.5*(b11 + b22) + sqrt(t*t + b12*b12)); double s = d0*d2 + d1*d3; s = sqrt(0.5*(d0*d0 + d1*d1 + d2*d2 + d3*d3) + sqrt(t*t + s*s)); if (s == 0.0) { return 0.0; } t = fabs(d0*d3 - d1*d2) / s; if (t > s) { if (i == 0) { return t*mult; } return s*mult; } if (i == 0) { return s*mult; } return t*mult; } else { double d0, d1, d2, d3, d4, d5, d6, d7, d8; d0 = d[0]; d3 = d[3]; d6 = d[6]; d1 = d[1]; d4 = d[4]; d7 = d[7]; d2 = d[2]; d5 = d[5]; d8 = d[8]; double mult; { double d_max = fabs(d0); if (d_max < fabs(d1)) { d_max = fabs(d1); } if (d_max < fabs(d2)) { d_max = fabs(d2); } if (d_max < fabs(d3)) { d_max = fabs(d3); } if (d_max < fabs(d4)) { d_max = fabs(d4); } if (d_max < fabs(d5)) { d_max = fabs(d5); } if (d_max < fabs(d6)) { d_max = fabs(d6); } if (d_max < fabs(d7)) { d_max = fabs(d7); } if (d_max < fabs(d8)) { d_max = fabs(d8); } GetScalingFactor(d_max, mult); } d0 /= mult; d1 /= mult; d2 /= mult; d3 /= mult; d4 /= mult; d5 /= mult; d6 /= mult; d7 /= mult; d8 /= mult; double b11 = d0*d0 + d1*d1 + d2*d2; double b12 = d0*d3 + d1*d4 + d2*d5; double b13 = d0*d6 + d1*d7 + d2*d8; double b22 = d3*d3 + d4*d4 + d5*d5; double b23 = d3*d6 + d4*d7 + d5*d8; double b33 = d6*d6 + d7*d7 + d8*d8; // double a, b, c; // a = -(b11 + b22 + b33); // b = b11*(b22 + b33) + b22*b33 - b12*b12 - b13*b13 - b23*b23; // c = b11*(b23*b23 - b22*b33) + b12*(b12*b33 - 2*b13*b23) + b13*b13*b22; // double Q = (a * a - 3 * b) / 9; // double Q = (b12*b12 + b13*b13 + b23*b23 + // ((b11 - b22)*(b11 - b22) + // (b11 - b33)*(b11 - b33) + // (b22 - b33)*(b22 - b33))/6)/3; // Q = (3*(b12^2 + b13^2 + b23^2) + // ((b11 - b22)^2 + (b11 - b33)^2 + (b22 - b33)^2)/2)/9 // or // Q = (1/6)*|B-tr(B)/3|_F^2 // Q >= 0 and // Q = 0 <==> B = scalar * I // double R = (2 * a * a * a - 9 * a * b + 27 * c) / 54; double aa = (b11 + b22 + b33)/3; // aa = tr(B)/3 double c1, c2, c3; // c1 = b11 - aa; // ((b11 - b22) + (b11 - b33))/3 // c2 = b22 - aa; // ((b22 - b11) + (b22 - b33))/3 // c3 = b33 - aa; // ((b33 - b11) + (b33 - b22))/3 { double b11_b22 = ((d0-d3)*(d0+d3)+(d1-d4)*(d1+d4)+(d2-d5)*(d2+d5)); double b22_b33 = ((d3-d6)*(d3+d6)+(d4-d7)*(d4+d7)+(d5-d8)*(d5+d8)); double b33_b11 = ((d6-d0)*(d6+d0)+(d7-d1)*(d7+d1)+(d8-d2)*(d8+d2)); c1 = (b11_b22 - b33_b11)/3; c2 = (b22_b33 - b11_b22)/3; c3 = (b33_b11 - b22_b33)/3; } double Q, R; Q = (2*(b12*b12 + b13*b13 + b23*b23) + c1*c1 + c2*c2 + c3*c3)/6; R = (c1*(b23*b23 - c2*c3)+ b12*(b12*c3 - 2*b13*b23) +b13*b13*c2)/2; // R = (-1/2)*det(B-(tr(B)/3)*I) // Note: 54*(det(S))^2 <= |S|_F^6, when S^t=S and tr(S)=0, S is 3x3 // Therefore: R^2 <= Q^3 if (Q <= 0.) { ; } // else if (fabs(R) >= sqrtQ3) // { // double det = (d[0] * (d[4] * d[8] - d[5] * d[7]) + // d[3] * (d[2] * d[7] - d[1] * d[8]) + // d[6] * (d[1] * d[5] - d[2] * d[4])); // // if (R > 0.) // { // if (i == 2) // // aa -= 2*sqrtQ; // return fabs(det)/(aa + sqrtQ); // else // aa += sqrtQ; // } // else // { // if (i != 0) // aa -= sqrtQ; // // aa = fabs(det)/sqrt(aa + 2*sqrtQ); // else // aa += 2*sqrtQ; // } // } else { double sqrtQ = sqrt(Q); double sqrtQ3 = Q*sqrtQ; // double sqrtQ3 = sqrtQ*sqrtQ*sqrtQ; // double sqrtQ3 = pow(Q, 1.5); double r; if (fabs(R) >= sqrtQ3) { if (R < 0.) { // R = -1.; r = 2*sqrtQ; } else { // R = 1.; r = -2*sqrtQ; } } else { R = R/sqrtQ3; // if (fabs(R) <= 0.95) if (fabs(R) <= 0.9) { if (i == 2) { aa -= 2*sqrtQ*cos(acos(R)/3); // min } else if (i == 0) { aa -= 2*sqrtQ*cos((acos(R) + 2.0*M_PI)/3); // max } else { aa -= 2*sqrtQ*cos((acos(R) - 2.0*M_PI)/3); // mid } goto have_aa; } if (R < 0.) { r = -2*sqrtQ*cos((acos(R) + 2.0*M_PI)/3); // max if (i == 0) { aa += r; goto have_aa; } } else { r = -2*sqrtQ*cos(acos(R)/3); // min if (i == 2) { aa += r; goto have_aa; } } } // (tr(B)/3 + r) is the root which is separated from the other // two roots which are close to each other when |R| is close to 1 c1 -= r; c2 -= r; c3 -= r; // aa += r; // Type of Householder reflections: z --> mu ek, where k is the index // of the entry in z with: // mode == 0: smallest absolute value --> angle closest to pi/2 // (eliminate large entries) // mode == 1: largest absolute value --> angle farthest from pi/2 // (eliminate small entries) const int mode = 1; // Find a unit vector z = (z1,z2,z3) in the "near"-kernel of // | c1 b12 b13 | // | b12 c2 b23 | = B - aa*I // | b13 b23 c3 | // This vector is also an eigenvector for B corresponding to aa // The vector z overwrites (c1,c2,c3). switch (KernelVector3S(mode, b12, b13, b23, c1, c2, c3)) { case 3: aa += r; goto have_aa; case 2: // ok, continue with the returned vector orthogonal to the kernel case 1: // ok, continue with the returned vector in the "near"-kernel ; } // Using the eigenvector c = (c1,c2,c3) to transform B into // | b11 0 0 | // B <-- Q P B P Q = | 0 b22 b23 | // | 0 b23 b33 | double v1, v2, v3, g; Reduce3S(mode, b11, b22, b33, b12, b13, b23, c1, c2, c3, v1, v2, v3, g); // Q = I - g v v^t // P - permutation matrix switching rows and columns 1 and k // find the eigenvalues of // | b22 b23 | // | b23 b33 | Eigenvalues2S(b23, b22, b33); if (i == 2) { aa = std::min(std::min(b11, b22), b33); } else if (i == 1) { if (b11 <= b22) { aa = (b22 <= b33) ? b22 : std::max(b11, b33); } else { aa = (b11 <= b33) ? b11 : std::max(b33, b22); } } else { aa = std::max(std::max(b11, b22), b33); } } have_aa: return sqrt(fabs(aa))*mult; // take abs before we sort? } } void DenseMatrix::CalcEigenvalues(double *lambda, double *vec) const { #ifdef MFEM_DEBUG if (Height() != Width() || Height() < 2 || Height() > 3) { mfem_error("DenseMatrix::CalcEigenvalues"); } #endif const int n = Height(); const double *d = data; if (n == 2) { double d0 = d[0]; double d2 = d[2]; // use the upper triangular entry double d3 = d[3]; double c, s; Eigensystem2S(d2, d0, d3, c, s); if (d0 <= d3) { lambda[0] = d0; lambda[1] = d3; vec[0] = c; vec[1] = -s; vec[2] = s; vec[3] = c; } else { lambda[0] = d3; lambda[1] = d0; vec[0] = s; vec[1] = c; vec[2] = c; vec[3] = -s; } } else { double d11 = d[0]; double d12 = d[3]; // use the upper triangular entries double d22 = d[4]; double d13 = d[6]; double d23 = d[7]; double d33 = d[8]; double mult; { double d_max = fabs(d11); if (d_max < fabs(d22)) { d_max = fabs(d22); } if (d_max < fabs(d33)) { d_max = fabs(d33); } if (d_max < fabs(d12)) { d_max = fabs(d12); } if (d_max < fabs(d13)) { d_max = fabs(d13); } if (d_max < fabs(d23)) { d_max = fabs(d23); } GetScalingFactor(d_max, mult); } d11 /= mult; d22 /= mult; d33 /= mult; d12 /= mult; d13 /= mult; d23 /= mult; double aa = (d11 + d22 + d33)/3; // aa = tr(A)/3 double c1 = d11 - aa; double c2 = d22 - aa; double c3 = d33 - aa; double Q, R; Q = (2*(d12*d12 + d13*d13 + d23*d23) + c1*c1 + c2*c2 + c3*c3)/6; R = (c1*(d23*d23 - c2*c3)+ d12*(d12*c3 - 2*d13*d23) + d13*d13*c2)/2; if (Q <= 0.) { lambda[0] = lambda[1] = lambda[2] = aa; vec[0] = 1.; vec[3] = 0.; vec[6] = 0.; vec[1] = 0.; vec[4] = 1.; vec[7] = 0.; vec[2] = 0.; vec[5] = 0.; vec[8] = 1.; } else { double sqrtQ = sqrt(Q); double sqrtQ3 = Q*sqrtQ; // double sqrtQ3 = sqrtQ*sqrtQ*sqrtQ; // double sqrtQ3 = pow(Q, 1.5); double r; if (fabs(R) >= sqrtQ3) { if (R < 0.) { // R = -1.; r = 2*sqrtQ; } else { // R = 1.; r = -2*sqrtQ; } } else { R = R/sqrtQ3; if (R < 0.) { r = -2*sqrtQ*cos((acos(R) + 2.0*M_PI)/3); // max } else { r = -2*sqrtQ*cos(acos(R)/3); // min } } aa += r; c1 = d11 - aa; c2 = d22 - aa; c3 = d33 - aa; // Type of Householder reflections: z --> mu ek, where k is the index // of the entry in z with: // mode == 0: smallest absolute value --> angle closest to pi/2 // mode == 1: largest absolute value --> angle farthest from pi/2 // Observations: // mode == 0 produces better eigenvectors, less accurate eigenvalues? // mode == 1 produces better eigenvalues, less accurate eigenvectors? const int mode = 0; // Find a unit vector z = (z1,z2,z3) in the "near"-kernel of // | c1 d12 d13 | // | d12 c2 d23 | = A - aa*I // | d13 d23 c3 | // This vector is also an eigenvector for A corresponding to aa. // The vector z overwrites (c1,c2,c3). switch (KernelVector3S(mode, d12, d13, d23, c1, c2, c3)) { case 3: // 'aa' is a triple eigenvalue lambda[0] = lambda[1] = lambda[2] = aa; vec[0] = 1.; vec[3] = 0.; vec[6] = 0.; vec[1] = 0.; vec[4] = 1.; vec[7] = 0.; vec[2] = 0.; vec[5] = 0.; vec[8] = 1.; goto done_3d; case 2: // ok, continue with the returned vector orthogonal to the kernel case 1: // ok, continue with the returned vector in the "near"-kernel ; } // Using the eigenvector c=(c1,c2,c3) transform A into // | d11 0 0 | // A <-- Q P A P Q = | 0 d22 d23 | // | 0 d23 d33 | double v1, v2, v3, g; int k = Reduce3S(mode, d11, d22, d33, d12, d13, d23, c1, c2, c3, v1, v2, v3, g); // Q = I - 2 v v^t // P - permutation matrix switching entries 1 and k // find the eigenvalues and eigenvectors for // | d22 d23 | // | d23 d33 | double c, s; Eigensystem2S(d23, d22, d33, c, s); // d22 <-> P Q (0, c, -s), d33 <-> P Q (0, s, c) double *vec_1, *vec_2, *vec_3; if (d11 <= d22) { if (d22 <= d33) { lambda[0] = d11; vec_1 = vec; lambda[1] = d22; vec_2 = vec + 3; lambda[2] = d33; vec_3 = vec + 6; } else if (d11 <= d33) { lambda[0] = d11; vec_1 = vec; lambda[1] = d33; vec_3 = vec + 3; lambda[2] = d22; vec_2 = vec + 6; } else { lambda[0] = d33; vec_3 = vec; lambda[1] = d11; vec_1 = vec + 3; lambda[2] = d22; vec_2 = vec + 6; } } else { if (d11 <= d33) { lambda[0] = d22; vec_2 = vec; lambda[1] = d11; vec_1 = vec + 3; lambda[2] = d33; vec_3 = vec + 6; } else if (d22 <= d33) { lambda[0] = d22; vec_2 = vec; lambda[1] = d33; vec_3 = vec + 3; lambda[2] = d11; vec_1 = vec + 6; } else { lambda[0] = d33; vec_3 = vec; lambda[1] = d22; vec_2 = vec + 3; lambda[2] = d11; vec_1 = vec + 6; } } vec_1[0] = c1; vec_1[1] = c2; vec_1[2] = c3; d22 = g*(v2*c - v3*s); d33 = g*(v2*s + v3*c); vec_2[0] = - v1*d22; vec_3[0] = - v1*d33; vec_2[1] = c - v2*d22; vec_3[1] = s - v2*d33; vec_2[2] = -s - v3*d22; vec_3[2] = c - v3*d33; switch (k) { case 2: Swap(vec_2[0], vec_2[1]); Swap(vec_3[0], vec_3[1]); break; case 3: Swap(vec_2[0], vec_2[2]); Swap(vec_3[0], vec_3[2]); } } done_3d: lambda[0] *= mult; lambda[1] *= mult; lambda[2] *= mult; } } void DenseMatrix::GetRow(int r, Vector &row) const { int m = Height(); int n = Width(); row.SetSize(n); const double* rp = data + r; double* vp = row.GetData(); for (int i = 0; i < n; i++) { vp[i] = *rp; rp += m; } } void DenseMatrix::GetColumn(int c, Vector &col) const { int m = Height(); col.SetSize(m); double *cp = data + c * m; double *vp = col.GetData(); for (int i = 0; i < m; i++) { vp[i] = cp[i]; } } void DenseMatrix::GetDiag(Vector &d) const { if (height != width) { mfem_error("DenseMatrix::GetDiag\n"); } d.SetSize(height); for (int i = 0; i < height; ++i) { d(i) = (*this)(i,i); } } void DenseMatrix::Getl1Diag(Vector &l) const { if (height != width) { mfem_error("DenseMatrix::Getl1Diag\n"); } l.SetSize(height); l = 0.0; for (int j = 0; j < width; ++j) for (int i = 0; i < height; ++i) { l(i) += fabs((*this)(i,j)); } } void DenseMatrix::GetRowSums(Vector &l) const { l.SetSize(height); for (int i = 0; i < height; i++) { double d = 0.0; for (int j = 0; j < width; j++) { d += operator()(i, j); } l(i) = d; } } void DenseMatrix::Diag(double c, int n) { SetSize(n); const int N = n*n; for (int i = 0; i < N; i++) { data[i] = 0.0; } for (int i = 0; i < n; i++) { data[i*(n+1)] = c; } } void DenseMatrix::Diag(double *diag, int n) { SetSize(n); int i, N = n*n; for (i = 0; i < N; i++) { data[i] = 0.0; } for (i = 0; i < n; i++) { data[i*(n+1)] = diag[i]; } } void DenseMatrix::Transpose() { int i, j; double t; if (Width() == Height()) { for (i = 0; i < Height(); i++) for (j = i+1; j < Width(); j++) { t = (*this)(i,j); (*this)(i,j) = (*this)(j,i); (*this)(j,i) = t; } } else { DenseMatrix T(*this,'t'); (*this) = T; } } void DenseMatrix::Transpose(const DenseMatrix &A) { SetSize(A.Width(),A.Height()); for (int i = 0; i < Height(); i++) for (int j = 0; j < Width(); j++) { (*this)(i,j) = A(j,i); } } void DenseMatrix::Symmetrize() { #ifdef MFEM_DEBUG if (Width() != Height()) { mfem_error("DenseMatrix::Symmetrize() : not a square matrix!"); } #endif for (int i = 0; i < Height(); i++) for (int j = 0; j < i; j++) { double a = 0.5 * ((*this)(i,j) + (*this)(j,i)); (*this)(j,i) = (*this)(i,j) = a; } } void DenseMatrix::Lump() { for (int i = 0; i < Height(); i++) { double L = 0.0; for (int j = 0; j < Width(); j++) { L += (*this)(i, j); (*this)(i, j) = 0.0; } (*this)(i, i) = L; } } void DenseMatrix::GradToCurl(DenseMatrix &curl) { int n = Height(); #ifdef MFEM_DEBUG if ((Width() != 2 || curl.Width() != 1 || 2*n != curl.Height()) && (Width() != 3 || curl.Width() != 3 || 3*n != curl.Height())) { mfem_error("DenseMatrix::GradToCurl(...)"); } #endif if (Width() == 2) { for (int i = 0; i < n; i++) { // (x,y) is grad of Ui double x = (*this)(i,0); double y = (*this)(i,1); int j = i+n; // curl of (Ui,0) curl(i,0) = -y; // curl of (0,Ui) curl(j,0) = x; } } else { for (int i = 0; i < n; i++) { // (x,y,z) is grad of Ui double x = (*this)(i,0); double y = (*this)(i,1); double z = (*this)(i,2); int j = i+n; int k = j+n; // curl of (Ui,0,0) curl(i,0) = 0.; curl(i,1) = z; curl(i,2) = -y; // curl of (0,Ui,0) curl(j,0) = -z; curl(j,1) = 0.; curl(j,2) = x; // curl of (0,0,Ui) curl(k,0) = y; curl(k,1) = -x; curl(k,2) = 0.; } } } void DenseMatrix::GradToDiv(Vector &div) { MFEM_ASSERT(Width()*Height() == div.Size(), "incompatible Vector 'div'!"); // div(dof*j+i) <-- (*this)(i,j) const int n = height * width; double *ddata = div.GetData(); for (int i = 0; i < n; i++) { ddata[i] = data[i]; } } void DenseMatrix::CopyRows(const DenseMatrix &A, int row1, int row2) { SetSize(row2 - row1 + 1, A.Width()); for (int j = 0; j < Width(); j++) { for (int i = row1; i <= row2; i++) { (*this)(i-row1,j) = A(i,j); } } } void DenseMatrix::CopyCols(const DenseMatrix &A, int col1, int col2) { SetSize(A.Height(), col2 - col1 + 1); for (int j = col1; j <= col2; j++) { for (int i = 0; i < Height(); i++) { (*this)(i,j-col1) = A(i,j); } } } void DenseMatrix::CopyMN(const DenseMatrix &A, int m, int n, int Aro, int Aco) { SetSize(m,n); for (int j = 0; j < n; j++) { for (int i = 0; i < m; i++) { (*this)(i,j) = A(Aro+i,Aco+j); } } } void DenseMatrix::CopyMN(const DenseMatrix &A, int row_offset, int col_offset) { double *v = A.data; for (int j = 0; j < A.Width(); j++) { for (int i = 0; i < A.Height(); i++) { (*this)(row_offset+i,col_offset+j) = *(v++); } } } void DenseMatrix::CopyMNt(const DenseMatrix &A, int row_offset, int col_offset) { double *v = A.data; for (int i = 0; i < A.Width(); i++) { for (int j = 0; j < A.Height(); j++) { (*this)(row_offset+i,col_offset+j) = *(v++); } } } void DenseMatrix::CopyMN(const DenseMatrix &A, int m, int n, int Aro, int Aco, int row_offset, int col_offset) { MFEM_VERIFY(row_offset+m <= this->Height() && col_offset+n <= this->Width(), "this DenseMatrix is too small to accomodate the submatrix. " << "row_offset = " << row_offset << ", m = " << m << ", this->Height() = " << this->Height() << ", col_offset = " << col_offset << ", n = " << n << ", this->Width() = " << this->Width() ); MFEM_VERIFY(Aro+m <= A.Height() && Aco+n <= A.Width(), "The A DenseMatrix is too small to accomodate the submatrix. " << "Aro = " << Aro << ", m = " << m << ", A.Height() = " << A.Height() << ", Aco = " << Aco << ", n = " << n << ", A.Width() = " << A.Width() ); for (int j = 0; j < n; j++) { for (int i = 0; i < m; i++) { (*this)(row_offset+i,col_offset+j) = A(Aro+i,Aco+j); } } } void DenseMatrix::CopyMNDiag(double c, int n, int row_offset, int col_offset) { for (int i = 0; i < n; i++) { for (int j = i+1; j < n; j++) { (*this)(row_offset+i,col_offset+j) = (*this)(row_offset+j,col_offset+i) = 0.0; } } for (int i = 0; i < n; i++) { (*this)(row_offset+i,col_offset+i) = c; } } void DenseMatrix::CopyMNDiag(double *diag, int n, int row_offset, int col_offset) { for (int i = 0; i < n; i++) { for (int j = i+1; j < n; j++) { (*this)(row_offset+i,col_offset+j) = (*this)(row_offset+j,col_offset+i) = 0.0; } } for (int i = 0; i < n; i++) { (*this)(row_offset+i,col_offset+i) = diag[i]; } } void DenseMatrix::CopyExceptMN(const DenseMatrix &A, int m, int n) { SetSize(A.Width()-1,A.Height()-1); int i, j, i_off = 0, j_off = 0; for (j = 0; j < A.Width(); j++) { if ( j == n ) { j_off = 1; continue; } for (i = 0; i < A.Height(); i++) { if ( i == m ) { i_off = 1; continue; } (*this)(i-i_off,j-j_off) = A(i,j); } i_off = 0; } } void DenseMatrix::AddMatrix(DenseMatrix &A, int ro, int co) { int h, ah, aw; double *p, *ap; h = Height(); ah = A.Height(); aw = A.Width(); #ifdef MFEM_DEBUG if (co+aw > Width() || ro+ah > h) { mfem_error("DenseMatrix::AddMatrix(...) 1"); } #endif p = data + ro + co * h; ap = A.data; for (int c = 0; c < aw; c++) { for (int r = 0; r < ah; r++) { p[r] += ap[r]; } p += h; ap += ah; } } void DenseMatrix::AddMatrix(double a, const DenseMatrix &A, int ro, int co) { int h, ah, aw; double *p, *ap; h = Height(); ah = A.Height(); aw = A.Width(); #ifdef MFEM_DEBUG if (co+aw > Width() || ro+ah > h) { mfem_error("DenseMatrix::AddMatrix(...) 2"); } #endif p = data + ro + co * h; ap = A.data; for (int c = 0; c < aw; c++) { for (int r = 0; r < ah; r++) { p[r] += a * ap[r]; } p += h; ap += ah; } } void DenseMatrix::AddToVector(int offset, Vector &v) const { const int n = height * width; double *vdata = v.GetData() + offset; for (int i = 0; i < n; i++) { vdata[i] += data[i]; } } void DenseMatrix::GetFromVector(int offset, const Vector &v) { const int n = height * width; const double *vdata = v.GetData() + offset; for (int i = 0; i < n; i++) { data[i] = vdata[i]; } } void DenseMatrix::AdjustDofDirection(Array &dofs) { const int n = Height(); #ifdef MFEM_DEBUG if (dofs.Size() != n || Width() != n) { mfem_error("DenseMatrix::AdjustDofDirection(...)"); } #endif int *dof = dofs; for (int i = 0; i < n-1; i++) { const int s = (dof[i] < 0) ? (-1) : (1); for (int j = i+1; j < n; j++) { const int t = (dof[j] < 0) ? (-s) : (s); if (t < 0) { (*this)(i,j) = -(*this)(i,j); (*this)(j,i) = -(*this)(j,i); } } } } void DenseMatrix::SetRow(int row, double value) { for (int j = 0; j < Width(); j++) { (*this)(row, j) = value; } } void DenseMatrix::SetCol(int col, double value) { for (int i = 0; i < Height(); i++) { (*this)(i, col) = value; } } void DenseMatrix::SetRow(int r, const Vector &row) { for (int j = 0; j < Width(); j++) { (*this)(r, j) = row[j]; } } void DenseMatrix::SetCol(int c, const Vector &col) { for (int i = 0; i < Height(); i++) { (*this)(i, c) = col[i]; } } void DenseMatrix::Threshold(double eps) { for (int col = 0; col < Width(); col++) { for (int row = 0; row < Height(); row++) { if (std::abs(operator()(row,col)) <= eps) { operator()(row,col) = 0.0; } } } } void DenseMatrix::Print(std::ostream &out, int width_) const { // save current output flags ios::fmtflags old_flags = out.flags(); // output flags = scientific + show sign out << setiosflags(ios::scientific | ios::showpos); for (int i = 0; i < height; i++) { out << "[row " << i << "]\n"; for (int j = 0; j < width; j++) { out << (*this)(i,j); if (j+1 == width || (j+1) % width_ == 0) { out << '\n'; } else { out << ' '; } } } // reset output flags to original values out.flags(old_flags); } void DenseMatrix::PrintMatlab(std::ostream &out) const { // save current output flags ios::fmtflags old_flags = out.flags(); // output flags = scientific + show sign out << setiosflags(ios::scientific | ios::showpos); for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { out << (*this)(i,j); out << ' '; } out << "\n"; } // reset output flags to original values out.flags(old_flags); } void DenseMatrix::PrintT(std::ostream &out, int width_) const { // save current output flags ios::fmtflags old_flags = out.flags(); // output flags = scientific + show sign out << setiosflags(ios::scientific | ios::showpos); for (int j = 0; j < width; j++) { out << "[col " << j << "]\n"; for (int i = 0; i < height; i++) { out << (*this)(i,j); if (i+1 == height || (i+1) % width_ == 0) { out << '\n'; } else { out << ' '; } } } // reset output flags to original values out.flags(old_flags); } void DenseMatrix::TestInversion() { DenseMatrix copy(*this), C(width); Invert(); mfem::Mult(*this, copy, C); for (int i = 0; i < width; i++) { C(i,i) -= 1.0; } mfem::out << "size = " << width << ", i_max = " << C.MaxMaxNorm() << ", cond_F = " << FNorm()*copy.FNorm() << endl; } DenseMatrix::~DenseMatrix() { if (capacity > 0) { delete [] data; } } void Add(const DenseMatrix &A, const DenseMatrix &B, double alpha, DenseMatrix &C) { for (int j = 0; j < C.Width(); j++) { for (int i = 0; i < C.Height(); i++) { C(i,j) = A(i,j) + alpha * B(i,j); } } } void Add(double alpha, const double *A, double beta, const double *B, DenseMatrix &C) { const int m = C.Height()*C.Width(); double *C_data = C.GetData(); for (int i = 0; i < m; i++) { C_data[i] = alpha*A[i] + beta*B[i]; } } void Add(double alpha, const DenseMatrix &A, double beta, const DenseMatrix &B, DenseMatrix &C) { MFEM_ASSERT(A.Height() == C.Height(), ""); MFEM_ASSERT(B.Height() == C.Height(), ""); MFEM_ASSERT(A.Width() == C.Width(), ""); MFEM_ASSERT(B.Width() == C.Width(), ""); Add(alpha, A.GetData(), beta, B.GetData(), C); } void Mult(const DenseMatrix &b, const DenseMatrix &c, DenseMatrix &a) { MFEM_ASSERT(a.Height() == b.Height() && a.Width() == c.Width() && b.Width() == c.Height(), "incompatible dimensions"); #ifdef MFEM_USE_LAPACK static char transa = 'N', transb = 'N'; static double alpha = 1.0, beta = 0.0; int m = b.Height(), n = c.Width(), k = b.Width(); dgemm_(&transa, &transb, &m, &n, &k, &alpha, b.Data(), &m, c.Data(), &k, &beta, a.Data(), &m); #else const int ah = a.Height(); const int aw = a.Width(); const int bw = b.Width(); double *ad = a.Data(); const double *bd = b.Data(); const double *cd = c.Data(); for (int i = 0; i < ah*aw; i++) { ad[i] = 0.0; } for (int j = 0; j < aw; j++) { for (int k = 0; k < bw; k++) { for (int i = 0; i < ah; i++) { ad[i+j*ah] += bd[i+k*ah] * cd[k+j*bw]; } } } #endif } void AddMult(const DenseMatrix &b, const DenseMatrix &c, DenseMatrix &a) { MFEM_ASSERT(a.Height() == b.Height() && a.Width() == c.Width() && b.Width() == c.Height(), "incompatible dimensions"); #ifdef MFEM_USE_LAPACK static char transa = 'N', transb = 'N'; static double alpha = 1.0, beta = 1.0; int m = b.Height(), n = c.Width(), k = b.Width(); dgemm_(&transa, &transb, &m, &n, &k, &alpha, b.Data(), &m, c.Data(), &k, &beta, a.Data(), &m); #else const int ah = a.Height(); const int aw = a.Width(); const int bw = b.Width(); double *ad = a.Data(); const double *bd = b.Data(); const double *cd = c.Data(); for (int j = 0; j < aw; j++) { for (int k = 0; k < bw; k++) { for (int i = 0; i < ah; i++) { ad[i+j*ah] += bd[i+k*ah] * cd[k+j*bw]; } } } #endif } void CalcAdjugate(const DenseMatrix &a, DenseMatrix &adja) { #ifdef MFEM_DEBUG if (a.Width() > a.Height() || a.Width() < 1 || a.Height() > 3) { mfem_error("CalcAdjugate(...)"); } if (a.Width() != adja.Height() || a.Height() != adja.Width()) { mfem_error("CalcAdjugate(...)"); } #endif if (a.Width() < a.Height()) { const double *d = a.Data(); double *ad = adja.Data(); if (a.Width() == 1) { // N x 1, N = 2,3 ad[0] = d[0]; ad[1] = d[1]; if (a.Height() == 3) { ad[2] = d[2]; } } else { // 3 x 2 double e, g, f; e = d[0]*d[0] + d[1]*d[1] + d[2]*d[2]; g = d[3]*d[3] + d[4]*d[4] + d[5]*d[5]; f = d[0]*d[3] + d[1]*d[4] + d[2]*d[5]; ad[0] = d[0]*g - d[3]*f; ad[1] = d[3]*e - d[0]*f; ad[2] = d[1]*g - d[4]*f; ad[3] = d[4]*e - d[1]*f; ad[4] = d[2]*g - d[5]*f; ad[5] = d[5]*e - d[2]*f; } return; } if (a.Width() == 1) { adja(0,0) = 1.0; } else if (a.Width() == 2) { adja(0,0) = a(1,1); adja(0,1) = -a(0,1); adja(1,0) = -a(1,0); adja(1,1) = a(0,0); } else { adja(0,0) = a(1,1)*a(2,2)-a(1,2)*a(2,1); adja(0,1) = a(0,2)*a(2,1)-a(0,1)*a(2,2); adja(0,2) = a(0,1)*a(1,2)-a(0,2)*a(1,1); adja(1,0) = a(1,2)*a(2,0)-a(1,0)*a(2,2); adja(1,1) = a(0,0)*a(2,2)-a(0,2)*a(2,0); adja(1,2) = a(0,2)*a(1,0)-a(0,0)*a(1,2); adja(2,0) = a(1,0)*a(2,1)-a(1,1)*a(2,0); adja(2,1) = a(0,1)*a(2,0)-a(0,0)*a(2,1); adja(2,2) = a(0,0)*a(1,1)-a(0,1)*a(1,0); } } void CalcAdjugateTranspose(const DenseMatrix &a, DenseMatrix &adjat) { #ifdef MFEM_DEBUG if (a.Height() != a.Width() || adjat.Height() != adjat.Width() || a.Width() != adjat.Width() || a.Width() < 1 || a.Width() > 3) { mfem_error("CalcAdjugateTranspose(...)"); } #endif if (a.Width() == 1) { adjat(0,0) = 1.0; } else if (a.Width() == 2) { adjat(0,0) = a(1,1); adjat(1,0) = -a(0,1); adjat(0,1) = -a(1,0); adjat(1,1) = a(0,0); } else { adjat(0,0) = a(1,1)*a(2,2)-a(1,2)*a(2,1); adjat(1,0) = a(0,2)*a(2,1)-a(0,1)*a(2,2); adjat(2,0) = a(0,1)*a(1,2)-a(0,2)*a(1,1); adjat(0,1) = a(1,2)*a(2,0)-a(1,0)*a(2,2); adjat(1,1) = a(0,0)*a(2,2)-a(0,2)*a(2,0); adjat(2,1) = a(0,2)*a(1,0)-a(0,0)*a(1,2); adjat(0,2) = a(1,0)*a(2,1)-a(1,1)*a(2,0); adjat(1,2) = a(0,1)*a(2,0)-a(0,0)*a(2,1); adjat(2,2) = a(0,0)*a(1,1)-a(0,1)*a(1,0); } } void CalcInverse(const DenseMatrix &a, DenseMatrix &inva) { MFEM_ASSERT(a.Width() <= a.Height() && a.Width() >= 1 && a.Height() <= 3, ""); MFEM_ASSERT(inva.Height() == a.Width(), "incorrect dimensions"); MFEM_ASSERT(inva.Width() == a.Height(), "incorrect dimensions"); double t; if (a.Width() < a.Height()) { const double *d = a.Data(); double *id = inva.Data(); if (a.Height() == 2) { t = 1.0 / (d[0]*d[0] + d[1]*d[1]); id[0] = d[0] * t; id[1] = d[1] * t; } else { if (a.Width() == 1) { t = 1.0 / (d[0]*d[0] + d[1]*d[1] + d[2]*d[2]); id[0] = d[0] * t; id[1] = d[1] * t; id[2] = d[2] * t; } else { double e, g, f; e = d[0]*d[0] + d[1]*d[1] + d[2]*d[2]; g = d[3]*d[3] + d[4]*d[4] + d[5]*d[5]; f = d[0]*d[3] + d[1]*d[4] + d[2]*d[5]; t = 1.0 / (e*g - f*f); e *= t; g *= t; f *= t; id[0] = d[0]*g - d[3]*f; id[1] = d[3]*e - d[0]*f; id[2] = d[1]*g - d[4]*f; id[3] = d[4]*e - d[1]*f; id[4] = d[2]*g - d[5]*f; id[5] = d[5]*e - d[2]*f; } } return; } #ifdef MFEM_DEBUG t = a.Det(); MFEM_ASSERT(std::abs(t) > 1.0e-14 * pow(a.FNorm()/a.Width(), a.Width()), "singular matrix!"); t = 1.0 / t; #else t = 1.0 / a.Det(); #endif switch (a.Height()) { case 1: inva(0,0) = t; break; case 2: inva(0,0) = a(1,1) * t ; inva(0,1) = -a(0,1) * t ; inva(1,0) = -a(1,0) * t ; inva(1,1) = a(0,0) * t ; break; case 3: inva(0,0) = (a(1,1)*a(2,2)-a(1,2)*a(2,1))*t; inva(0,1) = (a(0,2)*a(2,1)-a(0,1)*a(2,2))*t; inva(0,2) = (a(0,1)*a(1,2)-a(0,2)*a(1,1))*t; inva(1,0) = (a(1,2)*a(2,0)-a(1,0)*a(2,2))*t; inva(1,1) = (a(0,0)*a(2,2)-a(0,2)*a(2,0))*t; inva(1,2) = (a(0,2)*a(1,0)-a(0,0)*a(1,2))*t; inva(2,0) = (a(1,0)*a(2,1)-a(1,1)*a(2,0))*t; inva(2,1) = (a(0,1)*a(2,0)-a(0,0)*a(2,1))*t; inva(2,2) = (a(0,0)*a(1,1)-a(0,1)*a(1,0))*t; break; } } void CalcInverseTranspose(const DenseMatrix &a, DenseMatrix &inva) { #ifdef MFEM_DEBUG if ( (a.Width() != a.Height()) || ( (a.Height()!= 1) && (a.Height()!= 2) && (a.Height()!= 3) ) ) { mfem_error("CalcInverseTranspose(...)"); } #endif double t = 1. / a.Det() ; switch (a.Height()) { case 1: inva(0,0) = 1.0 / a(0,0); break; case 2: inva(0,0) = a(1,1) * t ; inva(1,0) = -a(0,1) * t ; inva(0,1) = -a(1,0) * t ; inva(1,1) = a(0,0) * t ; break; case 3: inva(0,0) = (a(1,1)*a(2,2)-a(1,2)*a(2,1))*t; inva(1,0) = (a(0,2)*a(2,1)-a(0,1)*a(2,2))*t; inva(2,0) = (a(0,1)*a(1,2)-a(0,2)*a(1,1))*t; inva(0,1) = (a(1,2)*a(2,0)-a(1,0)*a(2,2))*t; inva(1,1) = (a(0,0)*a(2,2)-a(0,2)*a(2,0))*t; inva(2,1) = (a(0,2)*a(1,0)-a(0,0)*a(1,2))*t; inva(0,2) = (a(1,0)*a(2,1)-a(1,1)*a(2,0))*t; inva(1,2) = (a(0,1)*a(2,0)-a(0,0)*a(2,1))*t; inva(2,2) = (a(0,0)*a(1,1)-a(0,1)*a(1,0))*t; break; } } void CalcOrtho(const DenseMatrix &J, Vector &n) { MFEM_ASSERT( ((J.Height() == 2 && J.Width() == 1) || (J.Height() == 3 && J.Width() == 2)) && (J.Height() == n.Size()), "Matrix must be 3x2 or 2x1, " << "and the Vector must be sized with the rows. " << " J.Height() = " << J.Height() << ", J.Width() = " << J.Width() << ", n.Size() = " << n.Size() ); const double *d = J.Data(); if (J.Height() == 2) { n(0) = d[1]; n(1) = -d[0]; } else { n(0) = d[1]*d[5] - d[2]*d[4]; n(1) = d[2]*d[3] - d[0]*d[5]; n(2) = d[0]*d[4] - d[1]*d[3]; } } void MultAAt(const DenseMatrix &a, DenseMatrix &aat) { const int height = a.Height(); const int width = a.Width(); for (int i = 0; i < height; i++) { for (int j = 0; j <= i; j++) { double temp = 0.; for (int k = 0; k < width; k++) { temp += a(i,k) * a(j,k); } aat(j,i) = aat(i,j) = temp; } } } void AddMultADAt(const DenseMatrix &A, const Vector &D, DenseMatrix &ADAt) { for (int i = 0; i < A.Height(); i++) { for (int j = 0; j < i; j++) { double t = 0.; for (int k = 0; k < A.Width(); k++) { t += D(k) * A(i, k) * A(j, k); } ADAt(i, j) += t; ADAt(j, i) += t; } } // process diagonal for (int i = 0; i < A.Height(); i++) { double t = 0.; for (int k = 0; k < A.Width(); k++) { t += D(k) * A(i, k) * A(i, k); } ADAt(i, i) += t; } } void MultADAt(const DenseMatrix &A, const Vector &D, DenseMatrix &ADAt) { for (int i = 0; i < A.Height(); i++) { for (int j = 0; j <= i; j++) { double t = 0.; for (int k = 0; k < A.Width(); k++) { t += D(k) * A(i, k) * A(j, k); } ADAt(j, i) = ADAt(i, j) = t; } } } void MultABt(const DenseMatrix &A, const DenseMatrix &B, DenseMatrix &ABt) { #ifdef MFEM_DEBUG if (A.Height() != ABt.Height() || B.Height() != ABt.Width() || A.Width() != B.Width()) { mfem_error("MultABt(...)"); } #endif #ifdef MFEM_USE_LAPACK static char transa = 'N', transb = 'T'; static double alpha = 1.0, beta = 0.0; int m = A.Height(), n = B.Height(), k = A.Width(); dgemm_(&transa, &transb, &m, &n, &k, &alpha, A.Data(), &m, B.Data(), &n, &beta, ABt.Data(), &m); #elif 1 const int ah = A.Height(); const int bh = B.Height(); const int aw = A.Width(); const double *ad = A.Data(); const double *bd = B.Data(); double *cd = ABt.Data(); for (int i = 0, s = ah*bh; i < s; i++) { cd[i] = 0.0; } for (int k = 0; k < aw; k++) { double *cp = cd; for (int j = 0; j < bh; j++) { const double bjk = bd[j]; for (int i = 0; i < ah; i++) { cp[i] += ad[i] * bjk; } cp += ah; } ad += ah; bd += bh; } #elif 1 const int ah = A.Height(); const int bh = B.Height(); const int aw = A.Width(); const double *ad = A.Data(); const double *bd = B.Data(); double *cd = ABt.Data(); for (int j = 0; j < bh; j++) for (int i = 0; i < ah; i++) { double d = 0.0; const double *ap = ad + i; const double *bp = bd + j; for (int k = 0; k < aw; k++) { d += (*ap) * (*bp); ap += ah; bp += bh; } *(cd++) = d; } #else int i, j, k; double d; for (i = 0; i < A.Height(); i++) for (j = 0; j < B.Height(); j++) { d = 0.0; for (k = 0; k < A.Width(); k++) { d += A(i, k) * B(j, k); } ABt(i, j) = d; } #endif } void MultADBt(const DenseMatrix &A, const Vector &D, const DenseMatrix &B, DenseMatrix &ADBt) { #ifdef MFEM_DEBUG if (A.Height() != ADBt.Height() || B.Height() != ADBt.Width() || A.Width() != B.Width() || A.Width() != D.Size()) { mfem_error("MultADBt(...)"); } #endif const int ah = A.Height(); const int bh = B.Height(); const int aw = A.Width(); const double *ad = A.Data(); const double *bd = B.Data(); const double *dd = D.GetData(); double *cd = ADBt.Data(); for (int i = 0, s = ah*bh; i < s; i++) { cd[i] = 0.0; } for (int k = 0; k < aw; k++) { double *cp = cd; for (int j = 0; j < bh; j++) { const double dk_bjk = dd[k] * bd[j]; for (int i = 0; i < ah; i++) { cp[i] += ad[i] * dk_bjk; } cp += ah; } ad += ah; bd += bh; } } void AddMultABt(const DenseMatrix &A, const DenseMatrix &B, DenseMatrix &ABt) { #ifdef MFEM_DEBUG if (A.Height() != ABt.Height() || B.Height() != ABt.Width() || A.Width() != B.Width()) { mfem_error("AddMultABt(...)"); } #endif #ifdef MFEM_USE_LAPACK static char transa = 'N', transb = 'T'; static double alpha = 1.0, beta = 1.0; int m = A.Height(), n = B.Height(), k = A.Width(); dgemm_(&transa, &transb, &m, &n, &k, &alpha, A.Data(), &m, B.Data(), &n, &beta, ABt.Data(), &m); #elif 1 const int ah = A.Height(); const int bh = B.Height(); const int aw = A.Width(); const double *ad = A.Data(); const double *bd = B.Data(); double *cd = ABt.Data(); for (int k = 0; k < aw; k++) { double *cp = cd; for (int j = 0; j < bh; j++) { const double bjk = bd[j]; for (int i = 0; i < ah; i++) { cp[i] += ad[i] * bjk; } cp += ah; } ad += ah; bd += bh; } #else int i, j, k; double d; for (i = 0; i < A.Height(); i++) for (j = 0; j < B.Height(); j++) { d = 0.0; for (k = 0; k < A.Width(); k++) { d += A(i, k) * B(j, k); } ABt(i, j) += d; } #endif } void AddMultADBt(const DenseMatrix &A, const Vector &D, const DenseMatrix &B, DenseMatrix &ADBt) { #ifdef MFEM_DEBUG if (A.Height() != ADBt.Height() || B.Height() != ADBt.Width() || A.Width() != B.Width() || A.Width() != D.Size()) { mfem_error("AddMultADBt(...)"); } #endif const int ah = A.Height(); const int bh = B.Height(); const int aw = A.Width(); const double *ad = A.Data(); const double *bd = B.Data(); const double *dd = D.GetData(); double *cd = ADBt.Data(); for (int k = 0; k < aw; k++) { double *cp = cd; for (int j = 0; j < bh; j++) { const double dk_bjk = dd[k] * bd[j]; for (int i = 0; i < ah; i++) { cp[i] += ad[i] * dk_bjk; } cp += ah; } ad += ah; bd += bh; } } void AddMult_a_ABt(double a, const DenseMatrix &A, const DenseMatrix &B, DenseMatrix &ABt) { #ifdef MFEM_DEBUG if (A.Height() != ABt.Height() || B.Height() != ABt.Width() || A.Width() != B.Width()) { mfem_error("AddMult_a_ABt(...)"); } #endif #ifdef MFEM_USE_LAPACK static char transa = 'N', transb = 'T'; double alpha = a; static double beta = 1.0; int m = A.Height(), n = B.Height(), k = A.Width(); dgemm_(&transa, &transb, &m, &n, &k, &alpha, A.Data(), &m, B.Data(), &n, &beta, ABt.Data(), &m); #elif 1 const int ah = A.Height(); const int bh = B.Height(); const int aw = A.Width(); const double *ad = A.Data(); const double *bd = B.Data(); double *cd = ABt.Data(); for (int k = 0; k < aw; k++) { double *cp = cd; for (int j = 0; j < bh; j++) { const double bjk = a * bd[j]; for (int i = 0; i < ah; i++) { cp[i] += ad[i] * bjk; } cp += ah; } ad += ah; bd += bh; } #else int i, j, k; double d; for (i = 0; i < A.Height(); i++) for (j = 0; j < B.Height(); j++) { d = 0.0; for (k = 0; k < A.Width(); k++) { d += A(i, k) * B(j, k); } ABt(i, j) += a * d; } #endif } void MultAtB(const DenseMatrix &A, const DenseMatrix &B, DenseMatrix &AtB) { #ifdef MFEM_DEBUG if (A.Width() != AtB.Height() || B.Width() != AtB.Width() || A.Height() != B.Height()) { mfem_error("MultAtB(...)"); } #endif #ifdef MFEM_USE_LAPACK static char transa = 'T', transb = 'N'; static double alpha = 1.0, beta = 0.0; int m = A.Width(), n = B.Width(), k = A.Height(); dgemm_(&transa, &transb, &m, &n, &k, &alpha, A.Data(), &k, B.Data(), &k, &beta, AtB.Data(), &m); #elif 1 const int ah = A.Height(); const int aw = A.Width(); const int bw = B.Width(); const double *ad = A.Data(); const double *bd = B.Data(); double *cd = AtB.Data(); for (int j = 0; j < bw; j++) { const double *ap = ad; for (int i = 0; i < aw; i++) { double d = 0.0; for (int k = 0; k < ah; k++) { d += ap[k] * bd[k]; } *(cd++) = d; ap += ah; } bd += ah; } #else int i, j, k; double d; for (i = 0; i < A.Width(); i++) for (j = 0; j < B.Width(); j++) { d = 0.0; for (k = 0; k < A.Height(); k++) { d += A(k, i) * B(k, j); } AtB(i, j) = d; } #endif } void AddMult_a_AAt(double a, const DenseMatrix &A, DenseMatrix &AAt) { double d; for (int i = 0; i < A.Height(); i++) { for (int j = 0; j < i; j++) { d = 0.; for (int k = 0; k < A.Width(); k++) { d += A(i,k) * A(j,k); } AAt(i, j) += (d *= a); AAt(j, i) += d; } d = 0.; for (int k = 0; k < A.Width(); k++) { d += A(i,k) * A(i,k); } AAt(i, i) += a * d; } } void Mult_a_AAt(double a, const DenseMatrix &A, DenseMatrix &AAt) { for (int i = 0; i < A.Height(); i++) { for (int j = 0; j <= i; j++) { double d = 0.; for (int k = 0; k < A.Width(); k++) { d += A(i,k) * A(j,k); } AAt(i, j) = AAt(j, i) = a * d; } } } void MultVVt(const Vector &v, DenseMatrix &vvt) { for (int i = 0; i < v.Size(); i++) { for (int j = 0; j <= i; j++) { vvt(i,j) = vvt(j,i) = v(i) * v(j); } } } void MultVWt(const Vector &v, const Vector &w, DenseMatrix &VWt) { #ifdef MFEM_DEBUG if (v.Size() != VWt.Height() || w.Size() != VWt.Width()) { mfem_error("MultVWt(...)"); } #endif for (int i = 0; i < v.Size(); i++) { const double vi = v(i); for (int j = 0; j < w.Size(); j++) { VWt(i, j) = vi * w(j); } } } void AddMultVWt(const Vector &v, const Vector &w, DenseMatrix &VWt) { const int m = v.Size(), n = w.Size(); #ifdef MFEM_DEBUG if (VWt.Height() != m || VWt.Width() != n) { mfem_error("AddMultVWt(...)"); } #endif for (int i = 0; i < m; i++) { const double vi = v(i); for (int j = 0; j < n; j++) { VWt(i, j) += vi * w(j); } } } void AddMultVVt(const Vector &v, DenseMatrix &VVt) { const int n = v.Size(); #ifdef MFEM_DEBUG if (VVt.Height() != n || VVt.Width() != n) { mfem_error("AddMultVVt(...)"); } #endif for (int i = 0; i < n; i++) { const double vi = v(i); for (int j = 0; j < i; j++) { const double vivj = vi * v(j); VVt(i, j) += vivj; VVt(j, i) += vivj; } VVt(i, i) += vi * vi; } } void AddMult_a_VWt(const double a, const Vector &v, const Vector &w, DenseMatrix &VWt) { const int m = v.Size(), n = w.Size(); #ifdef MFEM_DEBUG if (VWt.Height() != m || VWt.Width() != n) { mfem_error("AddMult_a_VWt(...)"); } #endif for (int j = 0; j < n; j++) { const double awj = a * w(j); for (int i = 0; i < m; i++) { VWt(i, j) += v(i) * awj; } } } void AddMult_a_VVt(const double a, const Vector &v, DenseMatrix &VVt) { MFEM_ASSERT(VVt.Height() == v.Size() && VVt.Width() == v.Size(), "incompatible dimensions!"); const int n = v.Size(); for (int i = 0; i < n; i++) { double avi = a * v(i); for (int j = 0; j < i; j++) { const double avivj = avi * v(j); VVt(i, j) += avivj; VVt(j, i) += avivj; } VVt(i, i) += avi * v(i); } } void LUFactors::Factor(int m) { #ifdef MFEM_USE_LAPACK int info = 0; if (m) { dgetrf_(&m, &m, data, &m, ipiv, &info); } MFEM_VERIFY(!info, "LAPACK: error in DGETRF"); #else // compiling without LAPACK double *data = this->data; for (int i = 0; i < m; i++) { // pivoting { int piv = i; double a = std::abs(data[piv+i*m]); for (int j = i+1; j < m; j++) { const double b = std::abs(data[j+i*m]); if (b > a) { a = b; piv = j; } } ipiv[i] = piv; if (piv != i) { // swap rows i and piv in both L and U parts for (int j = 0; j < m; j++) { Swap(data[i+j*m], data[piv+j*m]); } } } MFEM_ASSERT(data[i+i*m] != 0.0, "division by zero"); const double a_ii_inv = 1.0/data[i+i*m]; for (int j = i+1; j < m; j++) { data[j+i*m] *= a_ii_inv; } for (int k = i+1; k < m; k++) { const double a_ik = data[i+k*m]; for (int j = i+1; j < m; j++) { data[j+k*m] -= a_ik * data[j+i*m]; } } } #endif } double LUFactors::Det(int m) const { double det = 1.0; for (int i=0; idata; const int *ipiv = this->ipiv; double *x = X; for (int k = 0; k < n; k++) { // X <- U X for (int i = 0; i < m; i++) { double x_i = x[i] * data[i+i*m]; for (int j = i+1; j < m; j++) { x_i += x[j] * data[i+j*m]; } x[i] = x_i; } // X <- L X for (int i = m-1; i >= 0; i--) { double x_i = x[i]; for (int j = 0; j < i; j++) { x_i += x[j] * data[i+j*m]; } x[i] = x_i; } // X <- P^{-1} X for (int i = m-1; i >= 0; i--) { Swap(x[i], x[ipiv[i]-ipiv_base]); } x += m; } } void LUFactors::LSolve(int m, int n, double *X) const { const double *data = this->data; const int *ipiv = this->ipiv; double *x = X; for (int k = 0; k < n; k++) { // X <- P X for (int i = 0; i < m; i++) { Swap(x[i], x[ipiv[i]-ipiv_base]); } // X <- L^{-1} X for (int j = 0; j < m; j++) { const double x_j = x[j]; for (int i = j+1; i < m; i++) { x[i] -= data[i+j*m] * x_j; } } x += m; } } void LUFactors::USolve(int m, int n, double *X) const { const double *data = this->data; double *x = X; // X <- U^{-1} X for (int k = 0; k < n; k++) { for (int j = m-1; j >= 0; j--) { const double x_j = ( x[j] /= data[j+j*m] ); for (int i = 0; i < j; i++) { x[i] -= data[i+j*m] * x_j; } } x += m; } } void LUFactors::Solve(int m, int n, double *X) const { #ifdef MFEM_USE_LAPACK char trans = 'N'; int info = 0; if (m > 0 && n > 0) { dgetrs_(&trans, &m, &n, data, &m, ipiv, X, &m, &info); } MFEM_VERIFY(!info, "LAPACK: error in DGETRS"); #else // compiling without LAPACK LSolve(m, n, X); USolve(m, n, X); #endif } void LUFactors::GetInverseMatrix(int m, double *X) const { // A^{-1} = U^{-1} L^{-1} P const double *data = this->data; const int *ipiv = this->ipiv; // X <- U^{-1} (set only the upper triangular part of X) double *x = X; for (int k = 0; k < m; k++) { const double minus_x_k = -( x[k] = 1.0/data[k+k*m] ); for (int i = 0; i < k; i++) { x[i] = data[i+k*m] * minus_x_k; } for (int j = k-1; j >= 0; j--) { const double x_j = ( x[j] /= data[j+j*m] ); for (int i = 0; i < j; i++) { x[i] -= data[i+j*m] * x_j; } } x += m; } // X <- X L^{-1} (use input only from the upper triangular part of X) { int k = m-1; for (int j = 0; j < k; j++) { const double minus_L_kj = -data[k+j*m]; for (int i = 0; i <= j; i++) { X[i+j*m] += X[i+k*m] * minus_L_kj; } for (int i = j+1; i < m; i++) { X[i+j*m] = X[i+k*m] * minus_L_kj; } } } for (int k = m-2; k >= 0; k--) { for (int j = 0; j < k; j++) { const double L_kj = data[k+j*m]; for (int i = 0; i < m; i++) { X[i+j*m] -= X[i+k*m] * L_kj; } } } // X <- X P for (int k = m-1; k >= 0; k--) { const int piv_k = ipiv[k]-ipiv_base; if (k != piv_k) { for (int i = 0; i < m; i++) { Swap(X[i+k*m], X[i+piv_k*m]); } } } } void LUFactors::SubMult(int m, int n, int r, const double *A21, const double *X1, double *X2) { // X2 <- X2 - A21 X1 for (int k = 0; k < r; k++) { for (int j = 0; j < m; j++) { const double x1_jk = X1[j+k*m]; for (int i = 0; i < n; i++) { X2[i+k*n] -= A21[i+j*n] * x1_jk; } } } } void LUFactors::BlockFactor( int m, int n, double *A12, double *A21, double *A22) const { const double *data = this->data; // A12 <- L^{-1} P A12 LSolve(m, n, A12); // A21 <- A21 U^{-1} for (int j = 0; j < m; j++) { const double u_jj_inv = 1.0/data[j+j*m]; for (int i = 0; i < n; i++) { A21[i+j*n] *= u_jj_inv; } for (int k = j+1; k < m; k++) { const double u_jk = data[j+k*m]; for (int i = 0; i < n; i++) { A21[i+k*n] -= A21[i+j*n] * u_jk; } } } // A22 <- A22 - A21 A12 SubMult(m, n, n, A21, A12, A22); } void LUFactors::BlockForwSolve(int m, int n, int r, const double *L21, double *B1, double *B2) const { // B1 <- L^{-1} P B1 LSolve(m, r, B1); // B2 <- B2 - L21 B1 SubMult(m, n, r, L21, B1, B2); } void LUFactors::BlockBackSolve(int m, int n, int r, const double *U12, const double *X2, double *Y1) const { // Y1 <- Y1 - U12 X2 SubMult(n, m, r, U12, X2, Y1); // Y1 <- U^{-1} Y1 USolve(m, r, Y1); } DenseMatrixInverse::DenseMatrixInverse(const DenseMatrix &mat) : MatrixInverse(mat) { MFEM_ASSERT(height == width, "not a square matrix"); a = &mat; lu.data = new double[width*width]; lu.ipiv = new int[width]; Factor(); } DenseMatrixInverse::DenseMatrixInverse(const DenseMatrix *mat) : MatrixInverse(*mat) { MFEM_ASSERT(height == width, "not a square matrix"); a = mat; lu.data = new double[width*width]; lu.ipiv = new int[width]; } void DenseMatrixInverse::Factor() { MFEM_ASSERT(a, "DenseMatrix is not given"); const double *adata = a->data; const int s = width*width; for (int i = 0; i < s; i++) { lu.data[i] = adata[i]; } lu.Factor(width); } void DenseMatrixInverse::GetInverseMatrix(DenseMatrix &Ainv) const { Ainv.SetSize(width); lu.GetInverseMatrix(width, Ainv.Data()); } void DenseMatrixInverse::Factor(const DenseMatrix &mat) { MFEM_VERIFY(mat.height == mat.width, "DenseMatrix is not square!"); if (width != mat.width) { height = width = mat.width; delete [] lu.data; lu.data = new double[width*width]; delete [] lu.ipiv; lu.ipiv = new int[width]; } a = &mat; Factor(); } void DenseMatrixInverse::SetOperator(const Operator &op) { const DenseMatrix *p = dynamic_cast(&op); MFEM_VERIFY(p != NULL, "Operator is not a DenseMatrix!"); Factor(*p); } void DenseMatrixInverse::Mult(const Vector &x, Vector &y) const { y = x; lu.Solve(width, 1, y.GetData()); } void DenseMatrixInverse::Mult(const DenseMatrix &B, DenseMatrix &X) const { X = B; lu.Solve(width, X.Width(), X.Data()); } void DenseMatrixInverse::TestInversion() { DenseMatrix C(width); Mult(*a, C); for (int i = 0; i < width; i++) { C(i,i) -= 1.0; } mfem::out << "size = " << width << ", i_max = " << C.MaxMaxNorm() << endl; } DenseMatrixInverse::~DenseMatrixInverse() { delete [] lu.data; delete [] lu.ipiv; } DenseMatrixEigensystem::DenseMatrixEigensystem(DenseMatrix &m) : mat(m) { n = mat.Width(); EVal.SetSize(n); EVect.SetSize(n); ev.SetDataAndSize(NULL, n); #ifdef MFEM_USE_LAPACK jobz = 'V'; uplo = 'U'; lwork = -1; double qwork; dsyev_(&jobz, &uplo, &n, EVect.Data(), &n, EVal.GetData(), &qwork, &lwork, &info); lwork = (int) qwork; work = new double[lwork]; #endif } DenseMatrixEigensystem::DenseMatrixEigensystem( const DenseMatrixEigensystem &other) : mat(other.mat), EVal(other.EVal), EVect(other.EVect), ev(NULL, other.n), n(other.n) { #ifdef MFEM_USE_LAPACK jobz = other.jobz; uplo = other.uplo; lwork = other.lwork; work = new double[lwork]; #endif } void DenseMatrixEigensystem::Eval() { #ifdef MFEM_DEBUG if (mat.Width() != n) { mfem_error("DenseMatrixEigensystem::Eval()"); } #endif #ifdef MFEM_USE_LAPACK EVect = mat; dsyev_(&jobz, &uplo, &n, EVect.Data(), &n, EVal.GetData(), work, &lwork, &info); if (info != 0) { mfem::err << "DenseMatrixEigensystem::Eval(): DSYEV error code: " << info << endl; mfem_error(); } #else mfem_error("DenseMatrixEigensystem::Eval(): Compiled without LAPACK"); #endif } DenseMatrixEigensystem::~DenseMatrixEigensystem() { #ifdef MFEM_USE_LAPACK delete [] work; #endif } DenseMatrixSVD::DenseMatrixSVD(DenseMatrix &M) { m = M.Height(); n = M.Width(); Init(); } DenseMatrixSVD::DenseMatrixSVD(int h, int w) { m = h; n = w; Init(); } void DenseMatrixSVD::Init() { #ifdef MFEM_USE_LAPACK sv.SetSize(min(m, n)); jobu = 'N'; jobvt = 'N'; double qwork; lwork = -1; dgesvd_(&jobu, &jobvt, &m, &n, NULL, &m, sv.GetData(), NULL, &m, NULL, &n, &qwork, &lwork, &info); lwork = (int) qwork; work = new double[lwork]; #else mfem_error("DenseMatrixSVD::Init(): Compiled without LAPACK"); #endif } void DenseMatrixSVD::Eval(DenseMatrix &M) { #ifdef MFEM_DEBUG if (M.Height() != m || M.Width() != n) { mfem_error("DenseMatrixSVD::Eval()"); } #endif #ifdef MFEM_USE_LAPACK dgesvd_(&jobu, &jobvt, &m, &n, M.Data(), &m, sv.GetData(), NULL, &m, NULL, &n, work, &lwork, &info); if (info) { mfem::err << "DenseMatrixSVD::Eval() : info = " << info << endl; mfem_error(); } #else mfem_error("DenseMatrixSVD::Eval(): Compiled without LAPACK"); #endif } DenseMatrixSVD::~DenseMatrixSVD() { #ifdef MFEM_USE_LAPACK delete [] work; #endif } void DenseTensor::AddMult(const Table &elem_dof, const Vector &x, Vector &y) const { int n = SizeI(), ne = SizeK(); const int *I = elem_dof.GetI(), *J = elem_dof.GetJ(), *dofs; const double *d_col = tdata; double *yp = y, x_col; const double *xp = x; // the '4' here can be tuned for given platform and compiler if (n <= 4) { for (int i = 0; i < ne; i++) { dofs = J + I[i]; for (int col = 0; col < n; col++) { x_col = xp[dofs[col]]; for (int row = 0; row < n; row++) { yp[dofs[row]] += x_col*d_col[row]; } d_col += n; } } } else { Vector ye(n); for (int i = 0; i < ne; i++) { dofs = J + I[i]; x_col = xp[dofs[0]]; for (int row = 0; row < n; row++) { ye(row) = x_col*d_col[row]; } d_col += n; for (int col = 1; col < n; col++) { x_col = xp[dofs[col]]; for (int row = 0; row < n; row++) { ye(row) += x_col*d_col[row]; } d_col += n; } for (int row = 0; row < n; row++) { yp[dofs[row]] += ye(row); } } } } DenseTensor &DenseTensor::operator=(double c) { int s = SizeI() * SizeJ() * SizeK(); for (int i=0; i