Files
mfem/linalg/densemat.cpp
T

4421 lines
100 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.
// Implementation of data types dense matrix, inverse dense matrix
#include "kernels.hpp"
#include "vector.hpp"
#include "matrix.hpp"
#include "densemat.hpp"
#include "lapack.hpp"
#include "batched/batched.hpp"
#include "../general/forall.hpp"
#include "../general/table.hpp"
#include "../general/globals.hpp"
#include <iostream>
#include <iomanip>
#include <limits>
#include <algorithm>
#include <cstdlib>
#if defined(_MSC_VER) && (_MSC_VER < 1800)
#include <float.h>
#define copysign _copysign
#endif
namespace mfem
{
using namespace std;
DenseMatrix::DenseMatrix() : Matrix(0) { }
DenseMatrix::DenseMatrix(int s) : Matrix(s)
{
MFEM_ASSERT(s >= 0, "invalid DenseMatrix size: " << s);
if (s > 0)
{
data.SetSize(s*s);
*this = 0.0; // init with zeroes
}
}
DenseMatrix::DenseMatrix(int m, int n) : Matrix(m, n)
{
MFEM_ASSERT(m >= 0 && n >= 0,
"invalid DenseMatrix size: " << m << " x " << n);
const int capacity = m*n;
if (capacity > 0)
{
data.SetSize(capacity);
*this = 0.0; // init with zeroes
}
}
DenseMatrix::DenseMatrix(const DenseMatrix &mat, char ch)
: Matrix(mat.width, mat.height)
{
MFEM_CONTRACT_VAR(ch);
const int capacity = height*width;
if (capacity > 0)
{
data.SetSize(capacity);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
(*this)(i,j) = mat(j,i);
}
}
}
}
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;
data.SetSize(h*w, 0.0);
}
real_t &DenseMatrix::Elem(int i, int j)
{
return (*this)(i,j);
}
const real_t &DenseMatrix::Elem(int i, int j) const
{
return (*this)(i,j);
}
void DenseMatrix::Mult(const real_t *x, real_t *y) const
{
kernels::Mult(height, width, HostRead(), x, y);
}
void DenseMatrix::Mult(const real_t *x, Vector &y) const
{
MFEM_ASSERT(height == y.Size(), "incompatible dimensions");
Mult(x, y.HostWrite());
}
void DenseMatrix::Mult(const Vector &x, real_t *y) const
{
MFEM_ASSERT(width == x.Size(), "incompatible dimensions");
Mult(x.HostRead(), y);
}
void DenseMatrix::Mult(const Vector &x, Vector &y) const
{
MFEM_ASSERT(height == y.Size() && width == x.Size(),
"incompatible dimensions");
Mult(x.HostRead(), y.HostWrite());
}
void DenseMatrix::AbsMult(const Vector &x, Vector &y) const
{
MFEM_ASSERT(height == y.Size() && width == x.Size(),
"incompatible dimensions");
kernels::AbsMult(height, width, HostRead(), x.HostRead(), y.HostWrite());
}
real_t DenseMatrix::operator *(const DenseMatrix &m) const
{
MFEM_ASSERT(Height() == m.Height() && Width() == m.Width(),
"incompatible dimensions");
const int hw = height * width;
real_t a = 0.0;
for (int i = 0; i < hw; i++)
{
a += data[i] * m.data[i];
}
return a;
}
void DenseMatrix::MultTranspose(const real_t *x, real_t *y) const
{
kernels::MultTranspose(height, width, HostRead(), x, y);
}
void DenseMatrix::MultTranspose(const real_t *x, Vector &y) const
{
MFEM_ASSERT(width == y.Size(), "incompatible dimensions");
MultTranspose(x, y.HostWrite());
}
void DenseMatrix::MultTranspose(const Vector &x, real_t *y) const
{
MFEM_ASSERT(height == x.Size(), "incompatible dimensions");
MultTranspose(x.HostRead(), y);
}
void DenseMatrix::MultTranspose(const Vector &x, Vector &y) const
{
MFEM_ASSERT(height == x.Size() && width == y.Size(),
"incompatible dimensions");
MultTranspose(x.HostRead(), y.HostWrite());
}
void DenseMatrix::AbsMultTranspose(const Vector &x, Vector &y) const
{
MFEM_ASSERT(height == x.Size() && width == y.Size(),
"incompatible dimensions");
kernels::AbsMultTranspose(height, width, HostRead(),
x.HostRead(), y.HostWrite());
}
void DenseMatrix::AddMult(const Vector &x, Vector &y, const real_t a) const
{
if (a != 1.0)
{
AddMult_a(a, x, y);
return;
}
MFEM_ASSERT(height == y.Size() && width == x.Size(),
"incompatible dimensions");
const real_t *xp = x.GetData(), *d_col = data;
real_t *yp = y.GetData();
for (int col = 0; col < width; col++)
{
real_t 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 real_t a) const
{
if (a != 1.0)
{
AddMultTranspose_a(a, x, y);
return;
}
MFEM_ASSERT(height == x.Size() && width == y.Size(),
"incompatible dimensions");
const real_t *d_col = data;
for (int col = 0; col < width; col++)
{
real_t 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(real_t a, const Vector &x, Vector &y) const
{
MFEM_ASSERT(height == y.Size() && width == x.Size(),
"incompatible dimensions");
HostRead();
x.HostRead();
y.HostReadWrite();
const real_t *xp = x.GetData(), *d_col = data;
real_t *yp = y.GetData();
for (int col = 0; col < width; col++)
{
const real_t 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(real_t a, const Vector &x,
Vector &y) const
{
MFEM_ASSERT(height == x.Size() && width == y.Size(),
"incompatible dimensions");
const real_t *d_col = data;
for (int col = 0; col < width; col++)
{
real_t 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;
}
}
real_t DenseMatrix::InnerProduct(const real_t *x, const real_t *y) const
{
real_t prod = 0.0;
for (int i = 0; i < height; i++)
{
real_t 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)
{
real_t * 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)
{
real_t * 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)
{
real_t sj;
real_t * 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)
{
real_t * it_data = data;
for (int j = 0; j < width; ++j)
{
const real_t 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: dimension mismatch");
}
real_t * ss = new real_t[width];
real_t * it_s = s.GetData();
real_t * it_ss = ss;
for ( real_t * end_s = it_s + width; it_s != end_s; ++it_s)
{
*(it_ss++) = sqrt(*it_s);
}
real_t * 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::InvSymmetricScaling: dimension mismatch");
}
real_t * ss = new real_t[width];
real_t * it_s = s.GetData();
real_t * it_ss = ss;
for (real_t * end_s = it_s + width; it_s != end_s; ++it_s)
{
*(it_ss++) = 1./sqrt(*it_s);
}
real_t * 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;
}
real_t DenseMatrix::Trace() const
{
#ifdef MFEM_DEBUG
if (Width() != Height())
{
mfem_error("DenseMatrix::Trace() : not a square matrix!");
}
#endif
real_t t = 0.0;
for (int i = 0; i < width; i++)
{
t += (*this)(i, i);
}
return t;
}
MatrixInverse *DenseMatrix::Inverse() const
{
return new DenseMatrixInverse(*this);
}
void DenseMatrix::Exponential()
{
MFEM_ASSERT(Height() == Width() && Height() <= 2,
"The matrix must be square and "
<< "of size less than or equal to 2."
<< " Height() = " << Height()
<< ", Width() = " << Width());
switch (Height())
{
case 1:
{
data[0] = std::exp(data[0]);
break;
}
case 2:
{
/// Formulas from Corollary 2.4 of doi:10.1109/9.233156
/// Note typo in the paper, in the prefactor in the equation under (i).
const real_t a = data[0];
const real_t b = data[1];
const real_t c = data[2];
const real_t d = data[3];
const real_t e = (a - d)*(a - d) + 4*b*c;
const real_t f = std::exp((a + d)/2.0);
const real_t g = std::sqrt(std::abs(e)) / 2.0;
if (e == 0)
{
data[0] = 1.0 + (a - d)/2.0;
data[3] = 1.0 - (a - d)/2.0;
}
else if (e > 0)
{
data[0] = std::cosh(g) + (a - d)/2 * std::sinh(g) / g;
data[1] = b * std::sinh(g) / g;
data[2] = c * std::sinh(g) / g;
data[3] = std::cosh(g) - (a - d)/2 * std::sinh(g) / g;
}
else
{
data[0] = std::cos(g) + (a - d)/2 * std::sin(g) / g;
data[1] = b * std::sin(g) / g;
data[2] = c * std::sin(g) / g;
data[3] = std::cos(g) - (a - d)/2 * std::sin(g) / g;
}
for (int i = 0; i < 4; i++)
{
data[i] *= f;
}
break;
}
case 3:
{
MFEM_ABORT("3x3 matrices are not currently supported");
}
default:
{
MFEM_ABORT("Only 1x1 and 2x2 matrices are currently supported");
}
}
}
real_t 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 real_t *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 real_t *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
}
real_t 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 real_t *d = data;
real_t E = d[0] * d[0] + d[1] * d[1] + d[2] * d[2];
real_t G = d[3] * d[3] + d[4] * d[4] + d[5] * d[5];
real_t F = d[0] * d[3] + d[1] * d[4] + d[2] * d[5];
return sqrt(E * G - F * F);
}
mfem_error("DenseMatrix::Weight(): mismatched or unsupported dimensions");
return 0.0;
}
void DenseMatrix::Set(real_t alpha, const real_t *A)
{
const int s = Width()*Height();
for (int i = 0; i < s; i++)
{
data[i] = alpha*A[i];
}
}
void DenseMatrix::Add(const real_t 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);
}
}
}
void DenseMatrix::Add(const real_t c, const real_t *A)
{
const int s = Width()*Height();
for (int i = 0; i < s; i++)
{
data[i] += c*A[i];
}
}
DenseMatrix &DenseMatrix::operator=(real_t c)
{
const int s = Height()*Width();
for (int i = 0; i < s; i++)
{
data[i] = c;
}
return *this;
}
DenseMatrix &DenseMatrix::operator=(const real_t *d)
{
const int s = Height()*Width();
for (int i = 0; i < s; i++)
{
data[i] = d[i];
}
return *this;
}
DenseMatrix &DenseMatrix::operator+=(const real_t *m)
{
kernels::Add(Height(), Width(), m, (real_t*)data);
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*=(real_t 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(): dimension mismatch");
}
#endif
#ifdef MFEM_USE_LAPACK
int *ipiv = new int[width];
int lwork = -1;
real_t qwork, *work;
int info;
MFEM_LAPACK_PREFIX(getrf_)(&width, &width, data, &width, ipiv, &info);
if (info)
{
mfem_error("DenseMatrix::Invert() : Error in DGETRF");
}
MFEM_LAPACK_PREFIX(getri_)(&width, data, &width, ipiv, &qwork, &lwork, &info);
lwork = (int) qwork;
work = new real_t[lwork];
MFEM_LAPACK_PREFIX(getri_)(&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();
real_t a, b;
Array<int> 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++)
{
mfem::Swap<real_t>((*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++)
{
mfem::Swap<real_t>((*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(real_t *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]);
}
}
real_t DenseMatrix::MaxMaxNorm() const
{
int hw = Height()*Width();
const real_t *d = data;
real_t 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(real_t &scale_factor, real_t &scaled_fnorm2) const
{
int i, hw = Height() * Width();
real_t 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();
real_t *A = new real_t[N*N];
int LDA = N;
real_t VL = 0.0;
real_t VU = 1.0;
int IL = 0;
int IU = 1;
real_t ABSTOL = 0.0;
int M;
real_t *W = ev.GetData();
real_t *Z = NULL;
int LDZ = 1;
int *ISUPPZ = new int[2*N];
int LWORK = -1; // query optimal (double) workspace size
real_t QWORK;
real_t *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();
real_t *data = a.Data();
for (int i = 0; i < hw; i++)
{
A[i] = data[i];
}
MFEM_LAPACK_PREFIX(syevr_)(&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 real_t[LWORK];
IWORK = new int[LIWORK];
MFEM_LAPACK_PREFIX(syevr_)(&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;
#else
MFEM_CONTRACT_VAR(a);
MFEM_CONTRACT_VAR(ev);
MFEM_CONTRACT_VAR(evect);
#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);
real_t *A = NULL;
real_t *W = ev.GetData();
real_t *WORK = NULL;
real_t QWORK;
if (evect)
{
JOBZ = 'V';
evect->SetSize(N);
A = evect->Data();
}
else
{
A = new real_t[N*N];
}
int hw = a.Height() * a.Width();
real_t *data = a.Data();
for (int i = 0; i < hw; i++)
{
A[i] = data[i];
}
MFEM_LAPACK_PREFIX(syev_)(&JOBZ, &UPLO, &N, A, &LDA, W, &QWORK, &LWORK, &INFO);
LWORK = (int) QWORK;
WORK = new real_t[LWORK];
MFEM_LAPACK_PREFIX(syev_)(&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; }
#else
MFEM_CONTRACT_VAR(a);
MFEM_CONTRACT_VAR(ev);
MFEM_CONTRACT_VAR(evect);
#endif
}
void DenseMatrix::Eigensystem(Vector &ev, DenseMatrix *evect)
{
#ifdef MFEM_USE_LAPACK
// dsyevr_Eigensystem(*this, ev, evect);
dsyev_Eigensystem(*this, ev, evect);
#else
MFEM_CONTRACT_VAR(ev);
MFEM_CONTRACT_VAR(evect);
mfem_error("DenseMatrix::Eigensystem: Compiled without LAPACK");
#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);
real_t *A = NULL;
real_t *B = new real_t[N*N];
real_t *W = ev.GetData();
real_t *WORK = NULL;
real_t QWORK;
if (evect)
{
JOBZ = 'V';
evect->SetSize(N);
A = evect->Data();
}
else
{
A = new real_t[N*N];
}
int hw = a.Height() * a.Width();
real_t *a_data = a.Data();
real_t *b_data = b.Data();
for (int i = 0; i < hw; i++)
{
A[i] = a_data[i];
B[i] = b_data[i];
}
MFEM_LAPACK_PREFIX(sygv_)(&ITYPE, &JOBZ, &UPLO, &N, A, &LDA, B, &LDB, W,
&QWORK, &LWORK, &INFO);
LWORK = (int) QWORK;
WORK = new real_t[LWORK];
MFEM_LAPACK_PREFIX(sygv_)(&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; }
#else
MFEM_CONTRACT_VAR(a);
MFEM_CONTRACT_VAR(b);
MFEM_CONTRACT_VAR(ev);
MFEM_CONTRACT_VAR(evect);
#endif
}
void DenseMatrix::Eigensystem(DenseMatrix &b, Vector &ev,
DenseMatrix *evect)
{
#ifdef MFEM_USE_LAPACK
dsygv_Eigensystem(*this, b, ev, evect);
#else
MFEM_CONTRACT_VAR(b);
MFEM_CONTRACT_VAR(ev);
MFEM_CONTRACT_VAR(evect);
mfem_error("DenseMatrix::Eigensystem(generalized): Compiled without LAPACK");
#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();
real_t *a = copy_of_this.data;
sv.SetSize(min(m, n));
real_t *s = sv.GetData();
real_t *u = NULL;
real_t *vt = NULL;
real_t *work = NULL;
int lwork = -1;
int info;
real_t qwork;
MFEM_LAPACK_PREFIX(gesvd_)(&jobu, &jobvt, &m, &n, a, &m, s, u, &m, vt, &n,
&qwork, &lwork, &info);
lwork = (int) qwork;
work = new real_t[lwork];
MFEM_LAPACK_PREFIX(gesvd_)(&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
MFEM_CONTRACT_VAR(sv);
// compiling without lapack
mfem_error("DenseMatrix::SingularValues: Compiled without LAPACK");
#endif
}
int DenseMatrix::Rank(real_t 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;
}
real_t 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 real_t *d = data;
if (n == 1)
{
return d[0];
}
else if (n == 2)
{
return kernels::CalcSingularvalue<2>(d,i);
}
else
{
return kernels::CalcSingularvalue<3>(d,i);
}
}
void DenseMatrix::CalcEigenvalues(real_t *lambda, real_t *vec) const
{
#ifdef MFEM_DEBUG
if (Height() != Width() || Height() < 2 || Height() > 3)
{
mfem_error("DenseMatrix::CalcEigenvalues");
}
#endif
const int n = Height();
const real_t *d = data;
if (n == 2)
{
kernels::CalcEigenvalues<2>(d, lambda, vec);
}
else
{
kernels::CalcEigenvalues<3>(d, lambda, vec);
}
}
void DenseMatrix::GetRow(int r, Vector &row) const
{
int m = Height();
int n = Width();
row.SetSize(n);
const real_t* rp = data + r;
real_t* 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);
real_t *cp = Data() + c * m;
real_t *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::GetRowl1(Vector &l) const
{
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::GetRowl2(Vector &l) const
{
l.SetSize(height);
l = 0.0;
for (int j = 0; j < width; ++j)
for (int i = 0; i < height; ++i)
{
l[i] += operator()(i,j)*operator()(i,j);
}
for (int i = 0; i < height; ++i)
{
l[i] = sqrt(l[i]);
}
}
void DenseMatrix::GetRowSums(Vector &l) const
{
l.SetSize(height);
for (int i = 0; i < height; i++)
{
real_t d = 0.0;
for (int j = 0; j < width; j++)
{
d += operator()(i, j);
}
l(i) = d;
}
}
void DenseMatrix::Diag(real_t 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(real_t *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;
real_t 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
kernels::Symmetrize(Height(), Data());
}
void DenseMatrix::Lump()
{
for (int i = 0; i < Height(); i++)
{
real_t 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(...): dimension mismatch");
}
#endif
if (Width() == 2)
{
for (int i = 0; i < n; i++)
{
// (x,y) is grad of Ui
real_t x = (*this)(i,0);
real_t 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
real_t x = (*this)(i,0);
real_t y = (*this)(i,1);
real_t 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::GradToVectorCurl2D(DenseMatrix &curl)
{
MFEM_VERIFY(Width() == 2,
"DenseMatrix::GradToVectorCurl2D(...): dimension must be 2")
int n = Height();
// rotate gradient
for (int i = 0; i < n; i++)
{
curl(i,0) = (*this)(i,1);
curl(i,1) = -(*this)(i,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;
real_t *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)
{
real_t *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)
{
real_t *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 accommodate 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 accommodate 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(real_t 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(real_t *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;
real_t *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 : dimension mismatch");
}
#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(real_t a, const DenseMatrix &A, int ro, int co)
{
int h, ah, aw;
real_t *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 : dimension mismatch");
}
#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::GetSubMatrix(const Array<int> & idx, DenseMatrix & A) const
{
int k = idx.Size();
int idx_max = idx.Max();
MFEM_VERIFY(idx.Min() >=0 && idx_max < this->height && idx_max < this->width,
"DenseMatrix::GetSubMatrix: Index out of bounds");
A.SetSize(k);
real_t * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = idx[i];
for (int j = 0; j<k; j++)
{
jj = idx[j];
adata[i+j*k] = this->data[ii+jj*height];
}
}
}
void DenseMatrix::GetSubMatrix(const Array<int> & idx_i,
const Array<int> & idx_j, DenseMatrix & A) const
{
int k = idx_i.Size();
int l = idx_j.Size();
MFEM_VERIFY(idx_i.Min() >=0 && idx_i.Max() < this->height,
"DenseMatrix::GetSubMatrix: Row index out of bounds");
MFEM_VERIFY(idx_j.Min() >=0 && idx_j.Max() < this->width,
"DenseMatrix::GetSubMatrix: Col index out of bounds");
A.SetSize(k,l);
real_t * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = idx_i[i];
for (int j = 0; j<l; j++)
{
jj = idx_j[j];
adata[i+j*k] = this->data[ii+jj*height];
}
}
}
void DenseMatrix::GetSubMatrix(int ibeg, int iend, DenseMatrix & A)
{
MFEM_VERIFY(iend >= ibeg, "DenseMatrix::GetSubMatrix: Inconsistent range");
MFEM_VERIFY(ibeg >=0,
"DenseMatrix::GetSubMatrix: Negative index");
MFEM_VERIFY(iend <= this->height && iend <= this->width,
"DenseMatrix::GetSubMatrix: Index bigger than upper bound");
int k = iend - ibeg;
A.SetSize(k);
real_t * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = ibeg + i;
for (int j = 0; j<k; j++)
{
jj = ibeg + j;
adata[i+j*k] = this->data[ii+jj*height];
}
}
}
void DenseMatrix::GetSubMatrix(int ibeg, int iend, int jbeg, int jend,
DenseMatrix & A)
{
MFEM_VERIFY(iend >= ibeg,
"DenseMatrix::GetSubMatrix: Inconsistent row range");
MFEM_VERIFY(jend >= jbeg,
"DenseMatrix::GetSubMatrix: Inconsistent col range");
MFEM_VERIFY(ibeg >=0,
"DenseMatrix::GetSubMatrix: Negative row index");
MFEM_VERIFY(jbeg >=0,
"DenseMatrix::GetSubMatrix: Negative row index");
MFEM_VERIFY(iend <= this->height,
"DenseMatrix::GetSubMatrix: Index bigger than row upper bound");
MFEM_VERIFY(jend <= this->width,
"DenseMatrix::GetSubMatrix: Index bigger than col upper bound");
int k = iend - ibeg;
int l = jend - jbeg;
A.SetSize(k,l);
real_t * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = ibeg + i;
for (int j = 0; j<l; j++)
{
jj = jbeg + j;
adata[i+j*k] = this->data[ii+jj*height];
}
}
}
void DenseMatrix::SetSubMatrix(const Array<int> & idx, const DenseMatrix & A)
{
int k = idx.Size();
MFEM_VERIFY(A.Height() == k && A.Width() == k,
"DenseMatrix::SetSubMatrix:Inconsistent matrix dimensions");
int idx_max = idx.Max();
MFEM_VERIFY(idx.Min() >=0,
"DenseMatrix::SetSubMatrix: Negative index");
MFEM_VERIFY(idx_max < this->height,
"DenseMatrix::SetSubMatrix: Index bigger than row upper bound");
MFEM_VERIFY(idx_max < this->width,
"DenseMatrix::SetSubMatrix: Index bigger than col upper bound");
real_t * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = idx[i];
for (int j = 0; j<k; j++)
{
jj = idx[j];
this->data[ii+jj*height] = adata[i+j*k];
}
}
}
void DenseMatrix::SetSubMatrix(const Array<int> & idx_i,
const Array<int> & idx_j, const DenseMatrix & A)
{
int k = idx_i.Size();
int l = idx_j.Size();
MFEM_VERIFY(k == A.Height() && l == A.Width(),
"DenseMatrix::SetSubMatrix:Inconsistent matrix dimensions");
MFEM_VERIFY(idx_i.Min() >=0,
"DenseMatrix::SetSubMatrix: Negative row index");
MFEM_VERIFY(idx_j.Min() >=0,
"DenseMatrix::SetSubMatrix: Negative col index");
MFEM_VERIFY(idx_i.Max() < this->height,
"DenseMatrix::SetSubMatrix: Index bigger than row upper bound");
MFEM_VERIFY(idx_j.Max() < this->width,
"DenseMatrix::SetSubMatrix: Index bigger than col upper bound");
real_t * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = idx_i[i];
for (int j = 0; j<l; j++)
{
jj = idx_j[j];
this->data[ii+jj*height] = adata[i+j*k];
}
}
}
void DenseMatrix::SetSubMatrix(int ibeg, const DenseMatrix & A)
{
int k = A.Height();
MFEM_VERIFY(A.Width() == k, "DenseMatrix::SetSubmatrix: A is not square");
MFEM_VERIFY(ibeg >=0,
"DenseMatrix::SetSubmatrix: Negative index");
MFEM_VERIFY(ibeg + k <= this->height,
"DenseMatrix::SetSubmatrix: index bigger than row upper bound");
MFEM_VERIFY(ibeg + k <= this->width,
"DenseMatrix::SetSubmatrix: index bigger than col upper bound");
real_t * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = ibeg + i;
for (int j = 0; j<k; j++)
{
jj = ibeg + j;
this->data[ii+jj*height] = adata[i+j*k];
}
}
}
void DenseMatrix::SetSubMatrix(int ibeg, int jbeg, const DenseMatrix & A)
{
int k = A.Height();
int l = A.Width();
MFEM_VERIFY(ibeg>=0,
"DenseMatrix::SetSubmatrix: Negative row index");
MFEM_VERIFY(jbeg>=0,
"DenseMatrix::SetSubmatrix: Negative col index");
MFEM_VERIFY(ibeg + k <= this->height,
"DenseMatrix::SetSubmatrix: Index bigger than row upper bound");
MFEM_VERIFY(jbeg + l <= this->width,
"DenseMatrix::SetSubmatrix: Index bigger than col upper bound");
real_t * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = ibeg + i;
for (int j = 0; j<l; j++)
{
jj = jbeg + j;
this->data[ii+jj*height] = adata[i+j*k];
}
}
}
void DenseMatrix::AddSubMatrix(const Array<int> & idx, const DenseMatrix & A)
{
int k = idx.Size();
MFEM_VERIFY(A.Height() == k && A.Width() == k,
"DenseMatrix::AddSubMatrix:Inconsistent matrix dimensions");
int idx_max = idx.Max();
MFEM_VERIFY(idx.Min() >=0, "DenseMatrix::AddSubMatrix: Negative index");
MFEM_VERIFY(idx_max < this->height,
"DenseMatrix::AddSubMatrix: Index bigger than row upper bound");
MFEM_VERIFY(idx_max < this->width,
"DenseMatrix::AddSubMatrix: Index bigger than col upper bound");
real_t * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = idx[i];
for (int j = 0; j<k; j++)
{
jj = idx[j];
this->data[ii+jj*height] += adata[i+j*k];
}
}
}
void DenseMatrix::AddSubMatrix(const Array<int> & idx_i,
const Array<int> & idx_j, const DenseMatrix & A)
{
int k = idx_i.Size();
int l = idx_j.Size();
MFEM_VERIFY(k == A.Height() && l == A.Width(),
"DenseMatrix::AddSubMatrix:Inconsistent matrix dimensions");
MFEM_VERIFY(idx_i.Min() >=0,
"DenseMatrix::AddSubMatrix: Negative row index");
MFEM_VERIFY(idx_j.Min() >=0,
"DenseMatrix::AddSubMatrix: Negative col index");
MFEM_VERIFY(idx_i.Max() < this->height,
"DenseMatrix::AddSubMatrix: Index bigger than row upper bound");
MFEM_VERIFY(idx_j.Max() < this->width,
"DenseMatrix::AddSubMatrix: Index bigger than col upper bound");
real_t * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = idx_i[i];
for (int j = 0; j<l; j++)
{
jj = idx_j[j];
this->data[ii+jj*height] += adata[i+j*k];
}
}
}
void DenseMatrix::AddSubMatrix(int ibeg, const DenseMatrix & A)
{
int k = A.Height();
MFEM_VERIFY(A.Width() == k, "DenseMatrix::AddSubmatrix: A is not square");
MFEM_VERIFY(ibeg>=0,
"DenseMatrix::AddSubmatrix: Negative index");
MFEM_VERIFY(ibeg + k <= this->Height(),
"DenseMatrix::AddSubmatrix: Index bigger than row upper bound");
MFEM_VERIFY(ibeg + k <= this->Width(),
"DenseMatrix::AddSubmatrix: Index bigger than col upper bound");
real_t * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = ibeg + i;
for (int j = 0; j<k; j++)
{
jj = ibeg + j;
this->data[ii+jj*height] += adata[i+j*k];
}
}
}
void DenseMatrix::AddSubMatrix(int ibeg, int jbeg, const DenseMatrix & A)
{
int k = A.Height();
int l = A.Width();
MFEM_VERIFY(ibeg>=0,
"DenseMatrix::AddSubmatrix: Negative row index");
MFEM_VERIFY(jbeg>=0,
"DenseMatrix::AddSubmatrix: Negative col index");
MFEM_VERIFY(ibeg + k <= this->height,
"DenseMatrix::AddSubmatrix: Index bigger than row upper bound");
MFEM_VERIFY(jbeg + l <= this->width,
"DenseMatrix::AddSubmatrix: Index bigger than col upper bound");
real_t * adata = A.Data();
int ii, jj;
for (int i = 0; i<k; i++)
{
ii = ibeg + i;
for (int j = 0; j<l; j++)
{
jj = jbeg + j;
this->data[ii+jj*height] += adata[i+j*k];
}
}
}
void DenseMatrix::AddToVector(int offset, Vector &v) const
{
const int n = height * width;
real_t *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 real_t *vdata = v.GetData() + offset;
for (int i = 0; i < n; i++)
{
data[i] = vdata[i];
}
}
void DenseMatrix::AdjustDofDirection(Array<int> &dofs)
{
const int n = Height();
#ifdef MFEM_DEBUG
if (dofs.Size() != n || Width() != n)
{
mfem_error("DenseMatrix::AdjustDofDirection(...): dimension mismatch");
}
#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, real_t value)
{
for (int j = 0; j < Width(); j++)
{
(*this)(row, j) = value;
}
}
void DenseMatrix::SetCol(int col, real_t value)
{
for (int i = 0; i < Height(); i++)
{
(*this)(i, col) = value;
}
}
void DenseMatrix::SetRow(int r, const real_t* row)
{
MFEM_ASSERT(row != nullptr, "supplied row pointer is null");
for (int j = 0; j < Width(); j++)
{
(*this)(r, j) = row[j];
}
}
void DenseMatrix::SetRow(int r, const Vector &row)
{
MFEM_ASSERT(Width() == row.Size(), "");
SetRow(r, row.GetData());
}
void DenseMatrix::SetCol(int c, const real_t* col)
{
MFEM_ASSERT(col != nullptr, "supplied column pointer is null");
for (int i = 0; i < Height(); i++)
{
(*this)(i, c) = col[i];
}
}
void DenseMatrix::SetCol(int c, const Vector &col)
{
MFEM_ASSERT(Height() == col.Size(), "");
SetCol(c, col.GetData());
}
void DenseMatrix::Threshold(real_t 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 &os, int width_) const
{
// save current output flags
ios::fmtflags old_flags = os.flags();
// output flags = scientific + show sign
os << setiosflags(ios::scientific | ios::showpos);
for (int i = 0; i < height; i++)
{
os << "[row " << i << "]\n";
for (int j = 0; j < width; j++)
{
os << (*this)(i,j);
if (j+1 == width || (j+1) % width_ == 0)
{
os << '\n';
}
else
{
os << ' ';
}
}
}
// reset output flags to original values
os.flags(old_flags);
}
void DenseMatrix::PrintMatlab(std::ostream &os) const
{
// save current output flags
ios::fmtflags old_flags = os.flags();
// output flags = scientific + show sign
os << setiosflags(ios::scientific | ios::showpos);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
os << (*this)(i,j);
os << ' ';
}
os << "\n";
}
// reset output flags to original values
os.flags(old_flags);
}
void DenseMatrix::PrintMathematica(std::ostream &os) const
{
ios::fmtflags old_fmt = os.flags();
os.setf(ios::scientific);
std::streamsize old_prec = os.precision(14);
os << "(* Read file into Mathematica using: "
<< "myMat = Get[\"this_file_name\"] *)\n";
os << "{\n";
for (int i = 0; i < height; i++)
{
os << "{\n";
for (int j = 0; j < width; j++)
{
os << "Internal`StringToMReal[\"" << (*this)(i,j) << "\"]";
if (j < width - 1) { os << ','; }
os << '\n';
}
os << '}';
if (i < height - 1) { os << ','; }
os << '\n';
}
os << "}\n";
os.precision(old_prec);
os.flags(old_fmt);
}
void DenseMatrix::PrintT(std::ostream &os, int width_) const
{
// save current output flags
ios::fmtflags old_flags = os.flags();
// output flags = scientific + show sign
os << setiosflags(ios::scientific | ios::showpos);
for (int j = 0; j < width; j++)
{
os << "[col " << j << "]\n";
for (int i = 0; i < height; i++)
{
os << (*this)(i,j);
if (i+1 == height || (i+1) % width_ == 0)
{
os << '\n';
}
else
{
os << ' ';
}
}
}
// reset output flags to original values
os.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;
}
void DenseMatrix::Swap(DenseMatrix &other)
{
mfem::Swap(*this, other);
}
void Add(const DenseMatrix &A, const DenseMatrix &B,
real_t alpha, DenseMatrix &C)
{
kernels::Add(C.Height(), C.Width(), alpha, A.Data(), B.Data(), C.Data());
}
void Add(real_t alpha, const real_t *A,
real_t beta, const real_t *B, DenseMatrix &C)
{
kernels::Add(C.Height(), C.Width(), alpha, A, beta, B, C.Data());
}
void Add(real_t alpha, const DenseMatrix &A,
real_t 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);
}
bool LinearSolve(DenseMatrix& A, real_t* X, real_t TOL)
{
MFEM_VERIFY(A.IsSquare(), "A must be a square matrix!");
MFEM_ASSERT(A.NumCols() > 0, "supplied matrix, A, is empty!");
MFEM_ASSERT(X != nullptr, "supplied vector, X, is null!");
int N = A.NumCols();
switch (N)
{
case 1:
{
real_t det = A(0,0);
if (std::abs(det) <= TOL) { return false; } // singular
X[0] /= det;
break;
}
case 2:
{
real_t det = A.Det();
if (std::abs(det) <= TOL) { return false; } // singular
real_t invdet = 1. / det;
real_t b0 = X[0];
real_t b1 = X[1];
X[0] = ( A(1,1)*b0 - A(0,1)*b1) * invdet;
X[1] = (-A(1,0)*b0 + A(0,0)*b1) * invdet;
break;
}
default:
{
// default to LU factorization for the general case
Array<int> ipiv(N);
LUFactors lu(A.Data(), ipiv);
if (!lu.Factor(N,TOL)) { return false; } // singular
lu.Solve(N, 1, X);
}
} // END switch
return true;
}
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 real_t alpha = 1.0, beta = 0.0;
int m = b.Height(), n = c.Width(), k = b.Width();
MFEM_LAPACK_PREFIX(gemm_)(&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();
real_t *ad = a.Data();
const real_t *bd = b.Data();
const real_t *cd = c.Data();
kernels::Mult(ah,aw,bw,bd,cd,ad);
#endif
}
void AddMult_a(real_t alpha, 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 real_t beta = 1.0;
int m = b.Height(), n = c.Width(), k = b.Width();
MFEM_LAPACK_PREFIX(gemm_)(&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();
real_t *ad = a.Data();
const real_t *bd = b.Data();
const real_t *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] += alpha * 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 real_t alpha = 1.0, beta = 1.0;
int m = b.Height(), n = c.Width(), k = b.Width();
MFEM_LAPACK_PREFIX(gemm_)(&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();
real_t *ad = a.Data();
const real_t *bd = b.Data();
const real_t *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(...): unsupported dimensions");
}
if (a.Width() != adja.Height() || a.Height() != adja.Width())
{
mfem_error("CalcAdjugate(...): dimension mismatch");
}
#endif
if (a.Width() < a.Height())
{
const real_t *d = a.Data();
real_t *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
real_t 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(...): dimension mismatch");
}
#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");
if (a.Width() < a.Height())
{
const real_t *d = a.Data();
real_t *id = inva.Data();
if (a.Height() == 2)
{
kernels::CalcLeftInverse<2,1>(d, id);
}
else
{
if (a.Width() == 1)
{
kernels::CalcLeftInverse<3,1>(d, id);
}
else
{
kernels::CalcLeftInverse<3,2>(d, id);
}
}
return;
}
#ifdef MFEM_DEBUG
const real_t t = a.Det();
MFEM_ASSERT(std::abs(t) > 1.0e-14 * pow(a.FNorm()/a.Width(), a.Width()),
"singular matrix!");
#endif
switch (a.Height())
{
case 1:
inva(0,0) = 1.0 / a.Det();
break;
case 2:
kernels::CalcInverse<2>(a.Data(), inva.Data());
break;
case 3:
kernels::CalcInverse<3>(a.Data(), inva.Data());
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(...): dimension mismatch");
}
#endif
real_t 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 real_t *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++)
{
real_t 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++)
{
real_t 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++)
{
real_t 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++)
{
real_t 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(...): dimension mismatch");
}
#endif
#ifdef MFEM_USE_LAPACK
static char transa = 'N', transb = 'T';
static real_t alpha = 1.0, beta = 0.0;
int m = A.Height(), n = B.Height(), k = A.Width();
MFEM_LAPACK_PREFIX(gemm_)(&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 real_t *ad = A.Data();
const real_t *bd = B.Data();
real_t *cd = ABt.Data();
kernels::MultABt(ah, aw, bh, ad, bd, cd);
#elif 1
const int ah = A.Height();
const int bh = B.Height();
const int aw = A.Width();
const real_t *ad = A.Data();
const real_t *bd = B.Data();
real_t *cd = ABt.Data();
for (int j = 0; j < bh; j++)
for (int i = 0; i < ah; i++)
{
real_t d = 0.0;
const real_t *ap = ad + i;
const real_t *bp = bd + j;
for (int k = 0; k < aw; k++)
{
d += (*ap) * (*bp);
ap += ah;
bp += bh;
}
*(cd++) = d;
}
#else
int i, j, k;
real_t 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(...): dimension mismatch");
}
#endif
const int ah = A.Height();
const int bh = B.Height();
const int aw = A.Width();
const real_t *ad = A.Data();
const real_t *bd = B.Data();
const real_t *dd = D.GetData();
real_t *cd = ADBt.Data();
for (int i = 0, s = ah*bh; i < s; i++)
{
cd[i] = 0.0;
}
for (int k = 0; k < aw; k++)
{
real_t *cp = cd;
for (int j = 0; j < bh; j++)
{
const real_t 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(...): dimension mismatch");
}
#endif
#ifdef MFEM_USE_LAPACK
static char transa = 'N', transb = 'T';
static real_t alpha = 1.0, beta = 1.0;
int m = A.Height(), n = B.Height(), k = A.Width();
MFEM_LAPACK_PREFIX(gemm_)(&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 real_t *ad = A.Data();
const real_t *bd = B.Data();
real_t *cd = ABt.Data();
for (int k = 0; k < aw; k++)
{
real_t *cp = cd;
for (int j = 0; j < bh; j++)
{
const real_t 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;
real_t 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(...): dimension mismatch");
}
#endif
const int ah = A.Height();
const int bh = B.Height();
const int aw = A.Width();
const real_t *ad = A.Data();
const real_t *bd = B.Data();
const real_t *dd = D.GetData();
real_t *cd = ADBt.Data();
for (int k = 0; k < aw; k++)
{
real_t *cp = cd;
for (int j = 0; j < bh; j++)
{
const real_t 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(real_t 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(...): dimension mismatch");
}
#endif
#ifdef MFEM_USE_LAPACK
static char transa = 'N', transb = 'T';
real_t alpha = a;
static real_t beta = 1.0;
int m = A.Height(), n = B.Height(), k = A.Width();
MFEM_LAPACK_PREFIX(gemm_)(&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 real_t *ad = A.Data();
const real_t *bd = B.Data();
real_t *cd = ABt.Data();
for (int k = 0; k < aw; k++)
{
real_t *cp = cd;
for (int j = 0; j < bh; j++)
{
const real_t 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;
real_t 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(...): dimension mismatch");
}
#endif
#ifdef MFEM_USE_LAPACK
static char transa = 'T', transb = 'N';
static real_t alpha = 1.0, beta = 0.0;
int m = A.Width(), n = B.Width(), k = A.Height();
MFEM_LAPACK_PREFIX(gemm_)(&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 real_t *ad = A.Data();
const real_t *bd = B.Data();
real_t *cd = AtB.Data();
for (int j = 0; j < bw; j++)
{
const real_t *ap = ad;
for (int i = 0; i < aw; i++)
{
real_t 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;
real_t 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 AddMultAtB(const DenseMatrix &A, const DenseMatrix &B,
DenseMatrix &AtB)
{
MFEM_ASSERT(AtB.Height() == A.Width() && AtB.Width() == B.Width() &&
A.Height() == B.Height(), "incompatible dimensions");
#ifdef MFEM_USE_LAPACK
static char transa = 'T', transb = 'N';
static real_t alpha = 1.0, beta = 1.0;
int m = A.Width(), n = B.Width(), k = A.Height();
MFEM_LAPACK_PREFIX(gemm_)(&transa, &transb, &m, &n, &k, &alpha, A.Data(), &k,
B.Data(), &k, &beta, AtB.Data(), &m);
#else
const int ah = A.Height();
const int aw = A.Width();
const int bw = B.Width();
const real_t *ad = A.Data();
const real_t *bd = B.Data();
real_t *cd = AtB.Data();
for (int j = 0; j < bw; j++)
{
const real_t *ap = ad;
for (int i = 0; i < aw; i++)
{
real_t d = 0.0;
for (int k = 0; k < ah; k++)
{
d += ap[k] * bd[k];
}
*(cd++) += d;
ap += ah;
}
bd += ah;
}
#endif
}
void AddMult_a_AtB(real_t a, const DenseMatrix &A, const DenseMatrix &B,
DenseMatrix &AtB)
{
MFEM_ASSERT(AtB.Height() == A.Width() && AtB.Width() == B.Width() &&
A.Height() == B.Height(), "incompatible dimensions");
#ifdef MFEM_USE_LAPACK
static char transa = 'T', transb = 'N';
real_t alpha = a;
static real_t beta = 1.0;
int m = A.Width(), n = B.Width(), k = A.Height();
MFEM_LAPACK_PREFIX(gemm_)(&transa, &transb, &m, &n, &k, &alpha, A.Data(), &k,
B.Data(), &k, &beta, AtB.Data(), &m);
#else
const int ah = A.Height();
const int aw = A.Width();
const int bw = B.Width();
const real_t *ad = A.Data();
const real_t *bd = B.Data();
real_t *cd = AtB.Data();
for (int j = 0; j < bw; j++)
{
const real_t *ap = ad;
for (int i = 0; i < aw; i++)
{
real_t d = 0.0;
for (int k = 0; k < ah; k++)
{
d += ap[k] * bd[k];
}
*(cd++) += a * d;
ap += ah;
}
bd += ah;
}
#endif
}
void AddMult_a_AAt(real_t a, const DenseMatrix &A, DenseMatrix &AAt)
{
real_t 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(real_t a, const DenseMatrix &A, DenseMatrix &AAt)
{
for (int i = 0; i < A.Height(); i++)
{
for (int j = 0; j <= i; j++)
{
real_t 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(...): dimension mismatch");
}
#endif
for (int i = 0; i < v.Size(); i++)
{
const real_t 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(...): dimension mismatch");
}
#endif
for (int i = 0; i < m; i++)
{
const real_t 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(...): dimension mismatch");
}
#endif
for (int i = 0; i < n; i++)
{
const real_t vi = v(i);
for (int j = 0; j < i; j++)
{
const real_t vivj = vi * v(j);
VVt(i, j) += vivj;
VVt(j, i) += vivj;
}
VVt(i, i) += vi * vi;
}
}
void AddMult_a_VWt(const real_t 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(...): dimension mismatch");
}
#endif
for (int j = 0; j < n; j++)
{
const real_t awj = a * w(j);
for (int i = 0; i < m; i++)
{
VWt(i, j) += v(i) * awj;
}
}
}
void AddMult_a_VVt(const real_t 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++)
{
real_t avi = a * v(i);
for (int j = 0; j < i; j++)
{
const real_t avivj = avi * v(j);
VVt(i, j) += avivj;
VVt(j, i) += avivj;
}
VVt(i, i) += avi * v(i);
}
}
void RAP(const DenseMatrix &A, const DenseMatrix &P, DenseMatrix & RAP)
{
DenseMatrix RA(P.Width(),A.Width());
MultAtB(P,A,RA);
RAP.SetSize(RA.Height(), P.Width());
Mult(RA,P, RAP);
}
void RAP(const DenseMatrix &Rt, const DenseMatrix &A,
const DenseMatrix &P, DenseMatrix & RAP)
{
DenseMatrix RA(Rt.Width(),A.Width());
MultAtB(Rt,A,RA);
RAP.SetSize(RA.Height(), P.Width());
Mult(RA,P, RAP);
}
bool LUFactors::Factor(int m, real_t TOL)
{
#ifdef MFEM_USE_LAPACK
int info = 0;
if (m) { MFEM_LAPACK_PREFIX(getrf_)(&m, &m, data, &m, ipiv, &info); }
return info == 0;
#else
// compiling without LAPACK
real_t *data_ptr = this->data;
for (int i = 0; i < m; i++)
{
// pivoting
{
int piv = i;
real_t a = std::abs(data_ptr[piv+i*m]);
for (int j = i+1; j < m; j++)
{
const real_t b = std::abs(data_ptr[j+i*m]);
if (b > a)
{
a = b;
piv = j;
}
}
ipiv[i] = piv + 1;
if (piv != i)
{
// swap rows i and piv in both L and U parts
for (int j = 0; j < m; j++)
{
mfem::Swap<real_t>(data_ptr[i+j*m], data_ptr[piv+j*m]);
}
}
}
if (abs(data_ptr[i + i*m]) <= TOL)
{
return false; // failed
}
const real_t a_ii_inv = 1.0 / data_ptr[i+i*m];
for (int j = i+1; j < m; j++)
{
data_ptr[j+i*m] *= a_ii_inv;
}
for (int k = i+1; k < m; k++)
{
const real_t a_ik = data_ptr[i+k*m];
for (int j = i+1; j < m; j++)
{
data_ptr[j+k*m] -= a_ik * data_ptr[j+i*m];
}
}
}
#endif
return true; // success
}
real_t LUFactors::Det(int m) const
{
real_t det = 1.0;
for (int i=0; i<m; i++)
{
if (ipiv[i] != i - ipiv_base)
{
det *= -data[m * i + i];
}
else
{
det *= data[m * i + i];
}
}
return det;
}
void LUFactors::Mult(int m, int n, real_t *X) const
{
real_t *x = X;
for (int k = 0; k < n; k++)
{
// X <- U X
for (int i = 0; i < m; i++)
{
real_t 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--)
{
real_t 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--)
{
mfem::Swap<real_t>(x[i], x[ipiv[i]-ipiv_base]);
}
x += m;
}
}
void LUFactors::LSolve(int m, int n, real_t *X) const
{
real_t *x = X;
for (int k = 0; k < n; k++)
{
kernels::LSolve(data, m, ipiv, x);
x += m;
}
}
void LUFactors::USolve(int m, int n, real_t *X) const
{
real_t *x = X;
for (int k = 0; k < n; k++)
{
kernels::USolve(data, m, x);
x += m;
}
}
void LUFactors::Solve(int m, int n, real_t *X) const
{
#ifdef MFEM_USE_LAPACK
char trans = 'N';
int info = 0;
if (m > 0 && n > 0)
{
MFEM_LAPACK_PREFIX(getrs_)(&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::RightSolve(int m, int n, real_t *X) const
{
real_t *x;
#ifdef MFEM_USE_LAPACK
char n_ch = 'N', side = 'R', u_ch = 'U', l_ch = 'L';
real_t alpha = 1.0;
if (m > 0 && n > 0)
{
MFEM_LAPACK_PREFIX(trsm_)(&side,&u_ch,&n_ch,&n_ch,&n,&m,&alpha,data,&m,X,&n);
MFEM_LAPACK_PREFIX(trsm_)(&side,&l_ch,&n_ch,&u_ch,&n,&m,&alpha,data,&m,X,&n);
}
#else
// compiling without LAPACK
// X <- X U^{-1}
x = X;
for (int k = 0; k < n; k++)
{
for (int j = 0; j < m; j++)
{
const real_t x_j = ( x[j*n] /= data[j+j*m]);
for (int i = j+1; i < m; i++)
{
x[i*n] -= data[j + i*m] * x_j;
}
}
++x;
}
// X <- X L^{-1}
x = X;
for (int k = 0; k < n; k++)
{
for (int j = m-1; j >= 0; j--)
{
const real_t x_j = x[j*n];
for (int i = 0; i < j; i++)
{
x[i*n] -= data[j + i*m] * x_j;
}
}
++x;
}
#endif
// X <- X P
x = X;
for (int k = 0; k < n; k++)
{
for (int i = m-1; i >= 0; --i)
{
mfem::Swap<real_t>(x[i*n], x[(ipiv[i]-ipiv_base)*n]);
}
++x;
}
}
void LUFactors::GetInverseMatrix(int m, real_t *X) const
{
// A^{-1} = U^{-1} L^{-1} P
// X <- U^{-1} (set only the upper triangular part of X)
real_t *x = X;
for (int k = 0; k < m; k++)
{
const real_t 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 real_t 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 real_t 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 real_t 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<real_t>(X[i+k*m], X[i+piv_k*m]);
}
}
}
}
void LUFactors::SubMult(int m, int n, int r, const real_t *A21,
const real_t *X1, real_t *X2)
{
kernels::SubMult(m, n, r, A21, X1, X2);
}
void LUFactors::BlockFactor(
int m, int n, real_t *A12, real_t *A21, real_t *A22) const
{
kernels::BlockFactor(data, m, ipiv, n, A12, A21, A22);
}
void LUFactors::BlockForwSolve(int m, int n, int r, const real_t *L21,
real_t *B1, real_t *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 real_t *U12,
const real_t *X2, real_t *Y1) const
{
// Y1 <- Y1 - U12 X2
SubMult(n, m, r, U12, X2, Y1);
// Y1 <- U^{-1} Y1
USolve(m, r, Y1);
}
bool CholeskyFactors::Factor(int m, real_t TOL)
{
#ifdef MFEM_USE_LAPACK
int info = 0;
char uplo = 'L';
MFEM_VERIFY(data, "Matrix data not set");
if (m) { MFEM_LAPACK_PREFIX(potrf_)(&uplo, &m, data, &m, &info); }
return info == 0;
#else
// CholeskyCrout algorithm
for (int j = 0; j<m; j++)
{
real_t a = 0.;
for (int k = 0; k<j; k++)
{
a+=data[j+k*m]*data[j+k*m];
}
MFEM_VERIFY(data[j+j*m] - a > 0.,
"CholeskyFactors::Factor: The matrix is not SPD");
data[j+j*m] = std::sqrt(data[j+j*m] - a);
if (data[j + j*m] <= TOL)
{
return false; // failed
}
for (int i = j+1; i<m; i++)
{
a = 0.;
for (int k = 0; k<j; k++)
{
a+= data[i+k*m]*data[j+k*m];
}
data[i+j*m] = 1./data[j+m*j]*(data[i+j*m] - a);
}
}
return true; // success
#endif
}
real_t CholeskyFactors::Det(int m) const
{
real_t det = 1.0;
for (int i=0; i<m; i++)
{
det *= data[i + i*m];
}
return det;
}
void CholeskyFactors::LMult(int m, int n, real_t * X) const
{
// X <- L X
real_t *x = X;
for (int k = 0; k < n; k++)
{
for (int j = m-1; j >= 0; j--)
{
real_t x_j = x[j] * data[j+j*m];
for (int i = 0; i < j; i++)
{
x_j += x[i] * data[j+i*m];
}
x[j] = x_j;
}
x += m;
}
}
void CholeskyFactors::UMult(int m, int n, real_t * X) const
{
real_t *x = X;
for (int k = 0; k < n; k++)
{
for (int i = 0; i < m; i++)
{
real_t x_i = x[i] * data[i+i*m];
for (int j = i+1; j < m; j++)
{
x_i += x[j] * data[j+i*m];
}
x[i] = x_i;
}
x += m;
}
}
void CholeskyFactors::LSolve(int m, int n, real_t * X) const
{
#ifdef MFEM_USE_LAPACK
char uplo = 'L';
char trans = 'N';
char diag = 'N';
int info = 0;
MFEM_LAPACK_PREFIX(trtrs_)(&uplo, &trans, &diag, &m, &n, data, &m, X, &m,
&info);
MFEM_VERIFY(!info, "CholeskyFactors:LSolve:: info");
#else
real_t *x = X;
for (int k = 0; k < n; k++)
{
// X <- L^{-1} X
for (int j = 0; j < m; j++)
{
const real_t x_j = (x[j] /= data[j+j*m]);
for (int i = j+1; i < m; i++)
{
x[i] -= data[i+j*m] * x_j;
}
}
x += m;
}
#endif
}
void CholeskyFactors::USolve(int m, int n, real_t * X) const
{
#ifdef MFEM_USE_LAPACK
char uplo = 'L';
char trans = 'T';
char diag = 'N';
int info = 0;
MFEM_LAPACK_PREFIX(trtrs_)(&uplo, &trans, &diag, &m, &n, data, &m, X, &m,
&info);
MFEM_VERIFY(!info, "CholeskyFactors:USolve:: info");
#else
// X <- L^{-t} X
real_t *x = X;
for (int k = 0; k < n; k++)
{
for (int j = m-1; j >= 0; j--)
{
const real_t x_j = ( x[j] /= data[j+j*m] );
for (int i = 0; i < j; i++)
{
x[i] -= data[j+i*m] * x_j;
}
}
x += m;
}
#endif
}
void CholeskyFactors::Solve(int m, int n, real_t * X) const
{
#ifdef MFEM_USE_LAPACK
char uplo = 'L';
int info = 0;
MFEM_LAPACK_PREFIX(potrs_)(&uplo, &m, &n, data, &m, X, &m, &info);
MFEM_VERIFY(!info, "CholeskyFactors:Solve:: info");
#else
LSolve(m, n, X);
USolve(m, n, X);
#endif
}
void CholeskyFactors::RightSolve(int m, int n, real_t * X) const
{
#ifdef MFEM_USE_LAPACK
char side = 'R';
char uplo = 'L';
char transt = 'T';
char trans = 'N';
char diag = 'N';
real_t alpha = 1.0;
if (m > 0 && n > 0)
{
MFEM_LAPACK_PREFIX(trsm_)(&side,&uplo,&transt,&diag,&n,&m,&alpha,data,&m,X,&n);
MFEM_LAPACK_PREFIX(trsm_)(&side,&uplo,&trans,&diag,&n,&m,&alpha,data,&m,X,&n);
}
#else
// X <- X L^{-t}
real_t *x = X;
for (int k = 0; k < n; k++)
{
for (int j = 0; j < m; j++)
{
const real_t x_j = ( x[j*n] /= data[j+j*m]);
for (int i = j+1; i < m; i++)
{
x[i*n] -= data[i + j*m] * x_j;
}
}
++x;
}
// X <- X L^{-1}
x = X;
for (int k = 0; k < n; k++)
{
for (int j = m-1; j >= 0; j--)
{
const real_t x_j = (x[j*n] /= data[j+j*m]);
for (int i = 0; i < j; i++)
{
x[i*n] -= data[j + i*m] * x_j;
}
}
++x;
}
#endif
}
void CholeskyFactors::GetInverseMatrix(int m, real_t * X) const
{
// A^{-1} = L^{-t} L^{-1}
#ifdef MFEM_USE_LAPACK
// copy the lower triangular part of L to X
for (int i = 0; i<m; i++)
{
for (int j = i; j<m; j++)
{
X[j+i*m] = data[j+i*m];
}
}
char uplo = 'L';
int info = 0;
MFEM_LAPACK_PREFIX(potri_)(&uplo, &m, X, &m, &info);
MFEM_VERIFY(!info, "CholeskyFactors:GetInverseMatrix:: info");
// fill in the upper triangular part
for (int i = 0; i<m; i++)
{
for (int j = i+1; j<m; j++)
{
X[i+j*m] = X[j+i*m];
}
}
#else
// L^-t * L^-1 (in place)
for (int k = 0; k<m; k++)
{
X[k+k*m] = 1./data[k+k*m];
for (int i = k+1; i < m; i++)
{
real_t s=0.;
for (int j=k; j<i; j++)
{
s -= data[i+j*m] * X[j+k*m]/data[i+i*m];
}
X[i+k*m] = s;
}
}
for (int i = 0; i < m; i++)
{
for (int j = i; j < m; j++)
{
real_t s = 0.;
for (int k=j; k<m; k++)
{
s += X[k+i*m] * X[k+j*m];
}
X[i+j*m] = X[j+i*m] = s;
}
}
#endif
}
void DenseMatrixInverse::Init(int m)
{
if (spd)
{
factors = new CholeskyFactors();
}
else
{
factors = new LUFactors();
}
if (m>0)
{
factors->data = new real_t[m*m];
if (!spd)
{
dynamic_cast<LUFactors *>(factors)->ipiv = new int[m];
}
own_data = true;
}
}
DenseMatrixInverse::DenseMatrixInverse(const DenseMatrix &mat, bool spd_)
: MatrixInverse(mat), spd(spd_)
{
MFEM_ASSERT(height == width, "not a square matrix");
a = &mat;
Init(width);
Factor();
}
DenseMatrixInverse::DenseMatrixInverse(const DenseMatrix *mat, bool spd_)
: MatrixInverse(*mat), spd(spd_)
{
MFEM_ASSERT(height == width, "not a square matrix");
a = mat;
Init(width);
}
void DenseMatrixInverse::Factor()
{
MFEM_ASSERT(a, "DenseMatrix is not given");
const real_t *adata = a->data;
const int s = width*width;
for (int i = 0; i < s; i++)
{
factors->data[i] = adata[i];
}
factors->Factor(width);
}
void DenseMatrixInverse::GetInverseMatrix(DenseMatrix &Ainv) const
{
Ainv.SetSize(width);
factors->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;
if (own_data) { delete [] factors->data; }
factors->data = new real_t[width*width];
if (!spd)
{
LUFactors * lu = dynamic_cast<LUFactors *>(factors);
if (own_data) { delete [] lu->ipiv; }
lu->ipiv = new int[width];
}
own_data = true;
}
a = &mat;
Factor();
}
void DenseMatrixInverse::SetOperator(const Operator &op)
{
const DenseMatrix *p = dynamic_cast<const DenseMatrix*>(&op);
MFEM_VERIFY(p != NULL, "Operator is not a DenseMatrix!");
Factor(*p);
}
void DenseMatrixInverse::Mult(const real_t *x, real_t *y) const
{
for (int row = 0; row < height; row++)
{
y[row] = x[row];
}
factors->Solve(width, 1, y);
}
void DenseMatrixInverse::Mult(const Vector &x, Vector &y) const
{
y = x;
factors->Solve(width, 1, y.GetData());
}
void DenseMatrixInverse::Mult(const DenseMatrix &B, DenseMatrix &X) const
{
X = B;
factors->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()
{
if (own_data)
{
delete [] factors->data;
if (!spd)
{
delete [] dynamic_cast<LUFactors *>(factors)->ipiv;
}
}
delete factors;
}
#ifdef MFEM_USE_LAPACK
DenseMatrixEigensystem::DenseMatrixEigensystem(DenseMatrix &m)
: mat(m)
{
n = mat.Width();
EVal.SetSize(n);
EVect.SetSize(n);
ev.SetDataAndSize(NULL, n);
jobz = 'V';
uplo = 'U';
lwork = -1;
real_t qwork;
MFEM_LAPACK_PREFIX(syev_)(&jobz, &uplo, &n, EVect.Data(), &n, EVal.GetData(),
&qwork, &lwork, &info);
lwork = (int) qwork;
work = new real_t[lwork];
}
DenseMatrixEigensystem::DenseMatrixEigensystem(
const DenseMatrixEigensystem &other)
: mat(other.mat), EVal(other.EVal), EVect(other.EVect), ev(NULL, other.n),
n(other.n)
{
jobz = other.jobz;
uplo = other.uplo;
lwork = other.lwork;
work = new real_t[lwork];
}
void DenseMatrixEigensystem::Eval()
{
#ifdef MFEM_DEBUG
if (mat.Width() != n)
{
mfem_error("DenseMatrixEigensystem::Eval(): dimension mismatch");
}
#endif
EVect = mat;
MFEM_LAPACK_PREFIX(syev_)(&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();
}
}
DenseMatrixEigensystem::~DenseMatrixEigensystem()
{
delete [] work;
}
DenseMatrixGeneralizedEigensystem::DenseMatrixGeneralizedEigensystem(
DenseMatrix &a, DenseMatrix &b,
bool left_eigen_vectors,
bool right_eigen_vectors)
: A(a), B(b)
{
MFEM_VERIFY(A.Height() == A.Width(), "A has to be a square matrix");
MFEM_VERIFY(B.Height() == B.Width(), "B has to be a square matrix");
n = A.Width();
MFEM_VERIFY(B.Height() == n, "A and B dimension mismatch");
jobvl = 'N';
jobvr = 'N';
A_copy.SetSize(n);
B_copy.SetSize(n);
if (left_eigen_vectors)
{
jobvl = 'V';
Vl.SetSize(n);
}
if (right_eigen_vectors)
{
jobvr = 'V';
Vr.SetSize(n);
}
lwork = -1;
real_t qwork;
alphar = new real_t[n];
alphai = new real_t[n];
beta = new real_t[n];
int nl = max(1,Vl.Height());
int nr = max(1,Vr.Height());
MFEM_LAPACK_PREFIX(ggev_)(&jobvl,&jobvr,&n,A_copy.Data(),&n,B_copy.Data(),&n,
alphar, alphai, beta, Vl.Data(), &nl, Vr.Data(),
&nr, &qwork, &lwork, &info);
lwork = (int) qwork;
work = new real_t[lwork];
}
void DenseMatrixGeneralizedEigensystem::Eval()
{
int nl = max(1,Vl.Height());
int nr = max(1,Vr.Height());
A_copy = A;
B_copy = B;
MFEM_LAPACK_PREFIX(ggev_)(&jobvl,&jobvr,&n,A_copy.Data(),&n,B_copy.Data(),&n,
alphar, alphai, beta, Vl.Data(), &nl, Vr.Data(),
&nr, work, &lwork, &info);
if (info != 0)
{
mfem::err << "DenseMatrixGeneralizedEigensystem::Eval(): DGGEV error code: "
<< info << endl;
mfem_error();
}
evalues_r.SetSize(n);
evalues_i.SetSize(n);
for (int i = 0; i<n; i++)
{
if (beta[i] != 0.)
{
evalues_r(i) = alphar[i]/beta[i];
evalues_i(i) = alphai[i]/beta[i];
}
else
{
evalues_r(i) = infinity();
evalues_i(i) = infinity();
}
}
}
DenseMatrixGeneralizedEigensystem::~DenseMatrixGeneralizedEigensystem()
{
delete [] alphar;
delete [] alphai;
delete [] beta;
delete [] work;
}
DenseMatrixSVD::DenseMatrixSVD(DenseMatrix &M,
bool left_singular_vectors,
bool right_singular_vectors)
{
m = M.Height();
n = M.Width();
jobu = (left_singular_vectors)? 'S' : 'N';
jobvt = (right_singular_vectors)? 'S' : 'N';
Init();
}
DenseMatrixSVD::DenseMatrixSVD(int h, int w,
bool left_singular_vectors,
bool right_singular_vectors)
{
m = h;
n = w;
jobu = (left_singular_vectors)? 'S' : 'N';
jobvt = (right_singular_vectors)? 'S' : 'N';
Init();
}
DenseMatrixSVD::DenseMatrixSVD(DenseMatrix &M,
char left_singular_vectors,
char right_singular_vectors)
{
m = M.Height();
n = M.Width();
jobu = left_singular_vectors;
jobvt = right_singular_vectors;
Init();
}
DenseMatrixSVD::DenseMatrixSVD(int h, int w,
char left_singular_vectors,
char right_singular_vectors)
{
m = h;
n = w;
jobu = left_singular_vectors;
jobvt = right_singular_vectors;
Init();
}
void DenseMatrixSVD::Init()
{
sv.SetSize(min(m, n));
real_t qwork;
lwork = -1;
MFEM_LAPACK_PREFIX(gesvd_)(&jobu, &jobvt, &m, &n, NULL, &m, sv.GetData(),
NULL, &m, NULL, &n, &qwork, &lwork, &info);
lwork = (int) qwork;
work = new real_t[lwork];
}
void DenseMatrixSVD::Eval(DenseMatrix &M)
{
#ifdef MFEM_DEBUG
if (M.Height() != m || M.Width() != n)
{
mfem_error("DenseMatrixSVD::Eval()");
}
#endif
real_t * datau = nullptr;
real_t * datavt = nullptr;
if (jobu == 'A')
{
U.SetSize(m,m);
datau = U.Data();
}
else if (jobu == 'S')
{
U.SetSize(m,min(m,n));
datau = U.Data();
}
if (jobvt == 'A')
{
Vt.SetSize(n,n);
datavt = Vt.Data();
}
else if (jobvt == 'S')
{
Vt.SetSize(min(m,n),n);
datavt = Vt.Data();
}
Mc = M;
MFEM_LAPACK_PREFIX(gesvd_)(&jobu, &jobvt, &m, &n, Mc.Data(), &m, sv.GetData(),
datau, &m, datavt, &n, work, &lwork, &info);
if (info)
{
mfem::err << "DenseMatrixSVD::Eval() : info = " << info << endl;
mfem_error();
}
}
DenseMatrixSVD::~DenseMatrixSVD()
{
delete [] work;
}
#endif // if MFEM_USE_LAPACK
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 real_t *d_col = tdata.HostRead();
real_t *yp = y.HostReadWrite();
real_t x_col;
const real_t *xp = x.HostRead();
// 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=(real_t c)
{
int s = SizeI() * SizeJ() * SizeK();
for (int i=0; i<s; i++)
{
tdata[i] = c;
}
return *this;
}
void BatchLUFactor(DenseTensor &Mlu, Array<int> &P, const real_t TOL)
{
BatchedLinAlg::LUFactor(Mlu, P);
}
void BatchLUSolve(const DenseTensor &Mlu, const Array<int> &P, Vector &X)
{
BatchedLinAlg::LUSolve(Mlu, P, X);
}
#ifdef MFEM_USE_LAPACK
void BandedSolve(int KL, int KU, DenseMatrix &AB, DenseMatrix &B,
Array<int> &ipiv)
{
int LDAB = (2*KL) + KU + 1;
int N = AB.NumCols();
int NRHS = B.NumCols();
int info;
ipiv.SetSize(N);
MFEM_LAPACK_PREFIX(gbsv_)(&N, &KL, &KU, &NRHS, AB.GetData(), &LDAB,
ipiv.GetData(), B.GetData(), &N, &info);
MFEM_ASSERT(info == 0, "BandedSolve failed in LAPACK");
}
void BandedFactorizedSolve(int KL, int KU, DenseMatrix &AB, DenseMatrix &B,
bool transpose, Array<int> &ipiv)
{
int LDAB = (2*KL) + KU + 1;
int N = AB.NumCols();
int NRHS = B.NumCols();
char trans = transpose ? 'T' : 'N';
int info;
MFEM_LAPACK_PREFIX(gbtrs_)(&trans, &N, &KL, &KU, &NRHS, AB.GetData(), &LDAB,
ipiv.GetData(), B.GetData(), &N, &info);
MFEM_ASSERT(info == 0, "BandedFactorizedSolve failed in LAPACK");
}
#endif
} // namespace mfem