Files
mfem/linalg/sparsemat.cpp
T
2020-03-09 15:36:41 -07:00

3677 lines
82 KiB
C++

// Copyright (c) 2010-2020, 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 sparse matrix
#include "linalg.hpp"
#include "../general/forall.hpp"
#include "../general/table.hpp"
#include "../general/sort_pairs.hpp"
#include <iostream>
#include <iomanip>
#include <cmath>
#include <algorithm>
#include <limits>
#include <cstring>
namespace mfem
{
using namespace std;
SparseMatrix::SparseMatrix(int nrows, int ncols)
: AbstractSparseMatrix(nrows, (ncols >= 0) ? ncols : nrows),
Rows(new RowNode *[nrows]),
current_row(-1),
ColPtrJ(NULL),
ColPtrNode(NULL),
At(NULL),
isSorted(false)
{
// We probably do not need to set the ownership flags here.
I.Reset(); I.SetHostPtrOwner(true);
J.Reset(); J.SetHostPtrOwner(true);
A.Reset(); A.SetHostPtrOwner(true);
for (int i = 0; i < nrows; i++)
{
Rows[i] = NULL;
}
#ifdef MFEM_USE_MEMALLOC
NodesMem = new RowNodeAlloc;
#endif
}
SparseMatrix::SparseMatrix(int *i, int *j, double *data, int m, int n)
: AbstractSparseMatrix(m, n),
Rows(NULL),
ColPtrJ(NULL),
ColPtrNode(NULL),
At(NULL),
isSorted(false)
{
I.Wrap(i, height+1, true);
J.Wrap(j, I[height], true);
A.Wrap(data, I[height], true);
#ifdef MFEM_USE_MEMALLOC
NodesMem = NULL;
#endif
}
SparseMatrix::SparseMatrix(int *i, int *j, double *data, int m, int n,
bool ownij, bool owna, bool issorted)
: AbstractSparseMatrix(m, n),
Rows(NULL),
ColPtrJ(NULL),
ColPtrNode(NULL),
At(NULL),
isSorted(issorted)
{
I.Wrap(i, height+1, ownij);
J.Wrap(j, I[height], ownij);
#ifdef MFEM_USE_MEMALLOC
NodesMem = NULL;
#endif
if (data)
{
A.Wrap(data, I[height], owna);
}
else
{
const int nnz = I[height];
A.New(nnz);
for (int i=0; i<nnz; ++i)
{
A[i] = 0.0;
}
}
}
SparseMatrix::SparseMatrix(int nrows, int ncols, int rowsize)
: AbstractSparseMatrix(nrows, ncols)
, Rows(NULL)
, ColPtrJ(NULL)
, ColPtrNode(NULL)
, At(NULL)
, isSorted(false)
{
#ifdef MFEM_USE_MEMALLOC
NodesMem = NULL;
#endif
I.New(nrows + 1);
J.New(nrows * rowsize);
A.New(nrows * rowsize);
for (int i = 0; i <= nrows; i++)
{
I[i] = i * rowsize;
}
}
SparseMatrix::SparseMatrix(const SparseMatrix &mat, bool copy_graph)
: AbstractSparseMatrix(mat.Height(), mat.Width())
{
if (mat.Finalized())
{
const int nnz = mat.I[height];
if (copy_graph)
{
I.New(height+1, mat.I.GetMemoryType());
J.New(nnz, mat.J.GetMemoryType());
I.CopyFrom(mat.I, height+1);
J.CopyFrom(mat.J, nnz);
}
else
{
I = mat.I;
J = mat.J;
I.ClearOwnerFlags();
J.ClearOwnerFlags();
}
A.New(nnz, mat.A.GetMemoryType());
A.CopyFrom(mat.A, nnz);
Rows = NULL;
#ifdef MFEM_USE_MEMALLOC
NodesMem = NULL;
#endif
}
else
{
#ifdef MFEM_USE_MEMALLOC
NodesMem = new RowNodeAlloc;
#endif
Rows = new RowNode *[height];
for (int i = 0; i < height; i++)
{
RowNode **node_pp = &Rows[i];
for (RowNode *node_p = mat.Rows[i]; node_p; node_p = node_p->Prev)
{
#ifdef MFEM_USE_MEMALLOC
RowNode *new_node_p = NodesMem->Alloc();
#else
RowNode *new_node_p = new RowNode;
#endif
new_node_p->Value = node_p->Value;
new_node_p->Column = node_p->Column;
*node_pp = new_node_p;
node_pp = &new_node_p->Prev;
}
*node_pp = NULL;
}
// We probably do not need to set the ownership flags here.
I.Reset(); I.SetHostPtrOwner(true);
J.Reset(); J.SetHostPtrOwner(true);
A.Reset(); A.SetHostPtrOwner(true);
}
current_row = -1;
ColPtrJ = NULL;
ColPtrNode = NULL;
At = NULL;
isSorted = mat.isSorted;
}
SparseMatrix::SparseMatrix(const Vector &v)
: AbstractSparseMatrix(v.Size(), v.Size())
, Rows(NULL)
, ColPtrJ(NULL)
, ColPtrNode(NULL)
, At(NULL)
, isSorted(true)
{
#ifdef MFEM_USE_MEMALLOC
NodesMem = NULL;
#endif
I.New(height + 1);
J.New(height);
A.New(height);
for (int i = 0; i <= height; i++)
{
I[i] = i;
}
for (int r=0; r<height; r++)
{
J[r] = r;
A[r] = v[r];
}
}
SparseMatrix& SparseMatrix::operator=(const SparseMatrix &rhs)
{
Clear();
SparseMatrix copy(rhs);
Swap(copy);
return *this;
}
void SparseMatrix::MakeRef(const SparseMatrix &master)
{
MFEM_ASSERT(master.Finalized(), "'master' must be finalized");
Clear();
height = master.Height();
width = master.Width();
I = master.I; I.ClearOwnerFlags();
J = master.J; J.ClearOwnerFlags();
A = master.A; A.ClearOwnerFlags();
isSorted = master.isSorted;
}
void SparseMatrix::SetEmpty()
{
height = width = 0;
I.Reset();
J.Reset();
A.Reset();
Rows = NULL;
current_row = -1;
ColPtrJ = NULL;
ColPtrNode = NULL;
At = NULL;
#ifdef MFEM_USE_MEMALLOC
NodesMem = NULL;
#endif
isSorted = false;
}
int SparseMatrix::RowSize(const int i) const
{
int gi = i;
if (gi < 0)
{
gi = -1-gi;
}
if (I)
{
return I[gi+1]-I[gi];
}
int s = 0;
RowNode *row = Rows[gi];
for ( ; row != NULL; row = row->Prev)
if (row->Value != 0.0)
{
s++;
}
return s;
}
int SparseMatrix::MaxRowSize() const
{
int out=0;
int rowSize=0;
if (I)
{
for (int i=0; i < height; ++i)
{
rowSize = I[i+1]-I[i];
out = (out > rowSize) ? out : rowSize;
}
}
else
{
for (int i=0; i < height; ++i)
{
rowSize = RowSize(i);
out = (out > rowSize) ? out : rowSize;
}
}
return out;
}
int *SparseMatrix::GetRowColumns(const int row)
{
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
return J + I[row];
}
const int *SparseMatrix::GetRowColumns(const int row) const
{
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
return J + I[row];
}
double *SparseMatrix::GetRowEntries(const int row)
{
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
return A + I[row];
}
const double *SparseMatrix::GetRowEntries(const int row) const
{
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
return A + I[row];
}
void SparseMatrix::SetWidth(int newWidth)
{
if (newWidth == width)
{
// Nothing to be done here
return;
}
else if (newWidth == -1)
{
// Compute the actual width
width = ActualWidth();
// No need to reset the ColPtr, since the new ColPtr will be shorter.
}
else if (newWidth > width)
{
// We need to reset ColPtr, since now we may have additional columns.
if (Rows != NULL)
{
delete [] ColPtrNode;
ColPtrNode = static_cast<RowNode **>(NULL);
}
else
{
delete [] ColPtrJ;
ColPtrJ = static_cast<int *>(NULL);
}
width = newWidth;
}
else
{
// Check that the new width is bigger or equal to the actual width.
MFEM_ASSERT(newWidth >= ActualWidth(),
"The new width needs to be bigger or equal to the actual width");
width = newWidth;
}
}
void SparseMatrix::SortColumnIndices()
{
MFEM_VERIFY(Finalized(), "Matrix is not Finalized!");
if (isSorted)
{
return;
}
Array<Pair<int,double> > row;
for (int j = 0, i = 0; i < height; i++)
{
int end = I[i+1];
row.SetSize(end - j);
for (int k = 0; k < row.Size(); k++)
{
row[k].one = J[j+k];
row[k].two = A[j+k];
}
row.Sort();
for (int k = 0; k < row.Size(); k++, j++)
{
J[j] = row[k].one;
A[j] = row[k].two;
}
}
isSorted = true;
}
void SparseMatrix::MoveDiagonalFirst()
{
MFEM_VERIFY(Finalized(), "Matrix is not Finalized!");
for (int row = 0, end = 0; row < height; row++)
{
int start = end, j;
end = I[row+1];
for (j = start; true; j++)
{
MFEM_VERIFY(j < end, "diagonal entry not found in row = " << row);
if (J[j] == row) { break; }
}
const double diag = A[j];
for ( ; j > start; j--)
{
J[j] = J[j-1];
A[j] = A[j-1];
}
J[start] = row;
A[start] = diag;
}
}
double &SparseMatrix::Elem(int i, int j)
{
return operator()(i,j);
}
const double &SparseMatrix::Elem(int i, int j) const
{
return operator()(i,j);
}
double &SparseMatrix::operator()(int i, int j)
{
MFEM_ASSERT(i < height && i >= 0 && j < width && j >= 0,
"Trying to access element outside of the matrix. "
<< "height = " << height << ", "
<< "width = " << width << ", "
<< "i = " << i << ", "
<< "j = " << j);
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
for (int k = I[i], end = I[i+1]; k < end; k++)
{
if (J[k] == j)
{
return A[k];
}
}
MFEM_ABORT("Did not find i = " << i << ", j = " << j << " in matrix.");
return A[0];
}
const double &SparseMatrix::operator()(int i, int j) const
{
static const double zero = 0.0;
MFEM_ASSERT(i < height && i >= 0 && j < width && j >= 0,
"Trying to access element outside of the matrix. "
<< "height = " << height << ", "
<< "width = " << width << ", "
<< "i = " << i << ", "
<< "j = " << j);
if (Finalized())
{
for (int k = I[i], end = I[i+1]; k < end; k++)
{
if (J[k] == j)
{
return A[k];
}
}
}
else
{
for (RowNode *node_p = Rows[i]; node_p != NULL; node_p = node_p->Prev)
{
if (node_p->Column == j)
{
return node_p->Value;
}
}
}
return zero;
}
void SparseMatrix::GetDiag(Vector & d) const
{
MFEM_VERIFY(height == width, "Matrix must be square, not height = "
<< height << ", width = " << width);
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
d.SetSize(height);
int j, end;
for (int i = 0; i < height; i++)
{
end = I[i+1];
for (j = I[i]; j < end; j++)
{
if (J[j] == i)
{
d[i] = A[j];
break;
}
}
if (j == end)
{
d[i] = 0.;
}
}
}
/// Produces a DenseMatrix from a SparseMatrix
DenseMatrix *SparseMatrix::ToDenseMatrix() const
{
int num_rows = this->Height();
int num_cols = this->Width();
DenseMatrix * B = new DenseMatrix(num_rows, num_cols);
this->ToDenseMatrix(*B);
return B;
}
/// Produces a DenseMatrix from a SparseMatrix
void SparseMatrix::ToDenseMatrix(DenseMatrix & B) const
{
B.SetSize(height, width);
B = 0.0;
for (int r=0; r<height; r++)
{
const int * col = this->GetRowColumns(r);
const double * val = this->GetRowEntries(r);
for (int cj=0; cj<this->RowSize(r); cj++)
{
B(r, col[cj]) = val[cj];
}
}
}
void SparseMatrix::Mult(const Vector &x, Vector &y) const
{
if (Finalized()) { y.UseDevice(true); }
y = 0.0;
AddMult(x, y);
}
void SparseMatrix::AddMult(const Vector &x, Vector &y, const double a) const
{
MFEM_ASSERT(width == x.Size(), "Input vector size (" << x.Size()
<< ") must match matrix width (" << width << ")");
MFEM_ASSERT(height == y.Size(), "Output vector size (" << y.Size()
<< ") must match matrix height (" << height << ")");
if (!Finalized())
{
const double *xp = x.HostRead();
double *yp = y.HostReadWrite();
// The matrix is not finalized, but multiplication is still possible
for (int i = 0; i < height; i++)
{
RowNode *row = Rows[i];
double b = 0.0;
for ( ; row != NULL; row = row->Prev)
{
b += row->Value * xp[row->Column];
}
*yp += a * b;
yp++;
}
return;
}
#ifndef MFEM_USE_LEGACY_OPENMP
const int height = this->height;
const int nnz = J.Capacity();
auto d_I = Read(I, height+1);
auto d_J = Read(J, nnz);
auto d_A = Read(A, nnz);
auto d_x = x.Read();
auto d_y = y.ReadWrite();
MFEM_FORALL(i, height,
{
double d = 0.0;
const int end = d_I[i+1];
for (int j = d_I[i]; j < end; j++)
{
d += d_A[j] * d_x[d_J[j]];
}
d_y[i] += a * d;
});
#else
const double *Ap = A, *xp = x.GetData();
double *yp = y.GetData();
const int *Jp = J, *Ip = I;
#pragma omp parallel for
for (int i = 0; i < height; i++)
{
double d = 0.0;
const int end = Ip[i+1];
for (int j = Ip[i]; j < end; j++)
{
d += Ap[j] * xp[Jp[j]];
}
yp[i] += a * d;
}
#endif
}
void SparseMatrix::MultTranspose(const Vector &x, Vector &y) const
{
if (Finalized()) { y.UseDevice(true); }
y = 0.0;
AddMultTranspose(x, y);
}
void SparseMatrix::AddMultTranspose(const Vector &x, Vector &y,
const double a) const
{
MFEM_ASSERT(height == x.Size(), "Input vector size (" << x.Size()
<< ") must match matrix height (" << height << ")");
MFEM_ASSERT(width == y.Size(), "Output vector size (" << y.Size()
<< ") must match matrix width (" << width << ")");
if (!Finalized())
{
double *yp = y.GetData();
// The matrix is not finalized, but multiplication is still possible
for (int i = 0; i < height; i++)
{
RowNode *row = Rows[i];
double b = a * x(i);
for ( ; row != NULL; row = row->Prev)
{
yp[row->Column] += row->Value * b;
}
}
return;
}
if (At)
{
At->AddMult(x, y, a);
}
else
{
MFEM_VERIFY(Device::IsDisabled(), "transpose action on device is not "
"enabled; see BuildTranspose() for details.");
for (int i = 0; i < height; i++)
{
const double xi = a * x[i];
const int end = I[i+1];
for (int j = I[i]; j < end; j++)
{
const int Jj = J[j];
y[Jj] += A[j] * xi;
}
}
}
}
void SparseMatrix::BuildTranspose() const
{
if (At == NULL)
{
At = Transpose(*this);
}
}
void SparseMatrix::ResetTranspose() const
{
delete At;
At = NULL;
}
void SparseMatrix::PartMult(
const Array<int> &rows, const Vector &x, Vector &y) const
{
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
const int n = rows.Size();
const int nnz = J.Capacity();
auto d_rows = rows.Read();
auto d_I = Read(I, height+1);
auto d_J = Read(J, nnz);
auto d_A = Read(A, nnz);
auto d_x = x.Read();
auto d_y = y.Write();
MFEM_FORALL(i, n,
{
const int r = d_rows[i];
const int end = d_I[r + 1];
double a = 0.0;
for (int j = d_I[r]; j < end; j++)
{
a += d_A[j] * d_x[d_J[j]];
}
d_y[r] = a;
});
}
void SparseMatrix::PartAddMult(
const Array<int> &rows, const Vector &x, Vector &y, const double a) const
{
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
for (int i = 0; i < rows.Size(); i++)
{
int r = rows[i];
int end = I[r + 1];
double val = 0.0;
for (int j = I[r]; j < end; j++)
{
val += A[j] * x(J[j]);
}
y(r) += a * val;
}
}
void SparseMatrix::BooleanMult(const Array<int> &x, Array<int> &y) const
{
MFEM_ASSERT(Finalized(), "Matrix must be finalized.");
MFEM_ASSERT(x.Size() == Width(), "Input vector size (" << x.Size()
<< ") must match matrix width (" << Width() << ")");
y.SetSize(Height(), Device::GetDeviceMemoryType());
const int height = Height();
const int nnz = J.Capacity();
auto d_I = Read(I, height+1);
auto d_J = Read(J, nnz);
auto d_x = Read(x.GetMemory(), x.Size());
auto d_y = Write(y.GetMemory(), y.Size());
MFEM_FORALL(i, height,
{
bool d_yi = false;
const int end = d_I[i+1];
for (int j = d_I[i]; j < end; j++)
{
if (d_x[d_J[j]])
{
d_yi = true;
break;
}
}
d_y[i] = d_yi;
});
}
void SparseMatrix::BooleanMultTranspose(const Array<int> &x,
Array<int> &y) const
{
MFEM_ASSERT(Finalized(), "Matrix must be finalized.");
MFEM_ASSERT(x.Size() == Height(), "Input vector size (" << x.Size()
<< ") must match matrix height (" << Height() << ")");
y.SetSize(Width());
y = 0;
for (int i = 0; i < Height(); i++)
{
if (x[i])
{
int end = I[i+1];
for (int j = I[i]; j < end; j++)
{
y[J[j]] = x[i];
}
}
}
}
double SparseMatrix::InnerProduct(const Vector &x, const Vector &y) const
{
MFEM_ASSERT(x.Size() == Width(), "x.Size() = " << x.Size()
<< " must be equal to Width() = " << Width());
MFEM_ASSERT(y.Size() == Height(), "y.Size() = " << y.Size()
<< " must be equal to Height() = " << Height());
x.HostRead();
y.HostRead();
if (Finalized())
{
const int nnz = J.Capacity();
HostRead(I, height+1);
HostRead(J, nnz);
HostRead(A, nnz);
}
double prod = 0.0;
for (int i = 0; i < height; i++)
{
double a = 0.0;
if (A)
{
for (int j = I[i], end = I[i+1]; j < end; j++)
{
a += A[j] * x(J[j]);
}
}
else
{
for (RowNode *np = Rows[i]; np != NULL; np = np->Prev)
{
a += np->Value * x(np->Column);
}
}
prod += a * y(i);
}
return prod;
}
void SparseMatrix::GetRowSums(Vector &x) const
{
for (int i = 0; i < height; i++)
{
double a = 0.0;
if (A)
{
for (int j = I[i], end = I[i+1]; j < end; j++)
{
a += A[j];
}
}
else
{
for (RowNode *np = Rows[i]; np != NULL; np = np->Prev)
{
a += np->Value;
}
}
x(i) = a;
}
}
double SparseMatrix::GetRowNorml1(int irow) const
{
MFEM_VERIFY(irow < height,
"row " << irow << " not in matrix with height " << height);
double a = 0.0;
if (A)
{
for (int j = I[irow], end = I[irow+1]; j < end; j++)
{
a += fabs(A[j]);
}
}
else
{
for (RowNode *np = Rows[irow]; np != NULL; np = np->Prev)
{
a += fabs(np->Value);
}
}
return a;
}
void SparseMatrix::Threshold(double tol, bool fix_empty_rows)
{
MFEM_ASSERT(Finalized(), "Matrix must be finalized.");
double atol;
atol = std::abs(tol);
fix_empty_rows = height == width ? fix_empty_rows : false;
double *newA;
int *newI, *newJ;
int i, j, nz;
newI = Memory<int>(height+1);
newI[0] = 0;
for (i = 0, nz = 0; i < height; i++)
{
bool found = false;
for (j = I[i]; j < I[i+1]; j++)
if (std::abs(A[j]) > atol)
{
found = true;
nz++;
}
if (fix_empty_rows && !found) { nz++; }
newI[i+1] = nz;
}
newJ = Memory<int>(nz);
newA = Memory<double>(nz);
// Assume we're sorted until we find out otherwise
isSorted = true;
for (i = 0, nz = 0; i < height; i++)
{
bool found = false;
int lastCol = -1;
for (j = I[i]; j < I[i+1]; j++)
if (std::abs(A[j]) > atol)
{
found = true;
newJ[nz] = J[j];
newA[nz] = A[j];
if ( lastCol > newJ[nz] )
{
isSorted = false;
}
lastCol = newJ[nz];
nz++;
}
if (fix_empty_rows && !found)
{
newJ[nz] = i;
newA[nz] = 0.0;
nz++;
}
}
Destroy();
I.Wrap(newI, height+1, true);
J.Wrap(newJ, I[height], true);
A.Wrap(newA, I[height], true);
}
void SparseMatrix::Finalize(int skip_zeros, bool fix_empty_rows)
{
int i, j, nr, nz;
RowNode *aux;
if (Finalized())
{
return;
}
delete [] ColPtrNode;
ColPtrNode = NULL;
I.New(height+1);
I[0] = 0;
for (i = 1; i <= height; i++)
{
nr = 0;
for (aux = Rows[i-1]; aux != NULL; aux = aux->Prev)
{
if (!skip_zeros || aux->Value != 0.0) { nr++; }
}
if (fix_empty_rows && !nr) { nr = 1; }
I[i] = I[i-1] + nr;
}
nz = I[height];
J.New(nz);
A.New(nz);
// Assume we're sorted until we find out otherwise
isSorted = true;
for (j = i = 0; i < height; i++)
{
int lastCol = -1;
nr = 0;
for (aux = Rows[i]; aux != NULL; aux = aux->Prev)
{
if (!skip_zeros || aux->Value != 0.0)
{
J[j] = aux->Column;
A[j] = aux->Value;
if ( lastCol > J[j] )
{
isSorted = false;
}
lastCol = J[j];
j++;
nr++;
}
}
if (fix_empty_rows && !nr)
{
J[j] = i;
A[j] = 1.0;
j++;
}
}
#ifdef MFEM_USE_MEMALLOC
delete NodesMem;
NodesMem = NULL;
#else
for (i = 0; i < height; i++)
{
RowNode *node_p = Rows[i];
while (node_p != NULL)
{
aux = node_p;
node_p = node_p->Prev;
delete aux;
}
}
#endif
delete [] Rows;
Rows = NULL;
}
void SparseMatrix::GetBlocks(Array2D<SparseMatrix *> &blocks) const
{
int br = blocks.NumRows(), bc = blocks.NumCols();
int nr = (height + br - 1)/br, nc = (width + bc - 1)/bc;
for (int j = 0; j < bc; j++)
{
for (int i = 0; i < br; i++)
{
int *bI = Memory<int>(nr + 1);
for (int k = 0; k <= nr; k++)
{
bI[k] = 0;
}
blocks(i,j) = new SparseMatrix(bI, NULL, NULL, nr, nc);
}
}
for (int gr = 0; gr < height; gr++)
{
int bi = gr/nr, i = gr%nr + 1;
if (Finalized())
{
for (int j = I[gr]; j < I[gr+1]; j++)
{
if (A[j] != 0.0)
{
blocks(bi, J[j]/nc)->I[i]++;
}
}
}
else
{
for (RowNode *n_p = Rows[gr]; n_p != NULL; n_p = n_p->Prev)
{
if (n_p->Value != 0.0)
{
blocks(bi, n_p->Column/nc)->I[i]++;
}
}
}
}
for (int j = 0; j < bc; j++)
{
for (int i = 0; i < br; i++)
{
SparseMatrix &b = *blocks(i,j);
int nnz = 0, rs;
for (int k = 1; k <= nr; k++)
{
rs = b.I[k], b.I[k] = nnz, nnz += rs;
}
b.J.New(nnz);
b.A.New(nnz);
}
}
for (int gr = 0; gr < height; gr++)
{
int bi = gr/nr, i = gr%nr + 1;
if (Finalized())
{
for (int j = I[gr]; j < I[gr+1]; j++)
{
if (A[j] != 0.0)
{
SparseMatrix &b = *blocks(bi, J[j]/nc);
b.J[b.I[i]] = J[j] % nc;
b.A[b.I[i]] = A[j];
b.I[i]++;
}
}
}
else
{
for (RowNode *n_p = Rows[gr]; n_p != NULL; n_p = n_p->Prev)
{
if (n_p->Value != 0.0)
{
SparseMatrix &b = *blocks(bi, n_p->Column/nc);
b.J[b.I[i]] = n_p->Column % nc;
b.A[b.I[i]] = n_p->Value;
b.I[i]++;
}
}
}
}
}
double SparseMatrix::IsSymmetric() const
{
if (height != width)
{
return infinity();
}
double symm = 0.0;
if (Empty())
{
// return 0.0;
}
else if (Finalized())
{
for (int i = 1; i < height; i++)
{
for (int j = I[i]; j < I[i+1]; j++)
{
if (J[j] < i)
{
symm = std::max(symm, std::abs(A[j]-(*this)(J[j],i)));
}
}
}
}
else
{
for (int i = 0; i < height; i++)
{
for (RowNode *node_p = Rows[i]; node_p != NULL; node_p = node_p->Prev)
{
int col = node_p->Column;
if (col < i)
{
symm = std::max(symm, std::abs(node_p->Value-(*this)(col,i)));
}
}
}
}
return symm;
}
void SparseMatrix::Symmetrize()
{
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
int i, j;
for (i = 1; i < height; i++)
{
for (j = I[i]; j < I[i+1]; j++)
{
if (J[j] < i)
{
A[j] += (*this)(J[j],i);
A[j] *= 0.5;
(*this)(J[j],i) = A[j];
}
}
}
}
int SparseMatrix::NumNonZeroElems() const
{
if (A != NULL) // matrix is finalized
{
return I[height];
}
else
{
int nnz = 0;
for (int i = 0; i < height; i++)
{
for (RowNode *node_p = Rows[i]; node_p != NULL; node_p = node_p->Prev)
{
nnz++;
}
}
return nnz;
}
}
double SparseMatrix::MaxNorm() const
{
double m = 0.0;
if (A)
{
int nnz = I[height];
for (int j = 0; j < nnz; j++)
{
m = std::max(m, std::abs(A[j]));
}
}
else
{
for (int i = 0; i < height; i++)
{
for (RowNode *n_p = Rows[i]; n_p != NULL; n_p = n_p->Prev)
{
m = std::max(m, std::abs(n_p->Value));
}
}
}
return m;
}
int SparseMatrix::CountSmallElems(double tol) const
{
int counter = 0;
if (A)
{
const int nz = I[height];
const double *Ap = A;
for (int i = 0; i < nz; i++)
{
counter += (std::abs(Ap[i]) <= tol);
}
}
else
{
for (int i = 0; i < height; i++)
{
for (RowNode *aux = Rows[i]; aux != NULL; aux = aux->Prev)
{
counter += (std::abs(aux->Value) <= tol);
}
}
}
return counter;
}
int SparseMatrix::CheckFinite() const
{
if (Empty())
{
return 0;
}
else if (Finalized())
{
return mfem::CheckFinite(A, I[height]);
}
else
{
int counter = 0;
for (int i = 0; i < height; i++)
{
for (RowNode *aux = Rows[i]; aux != NULL; aux = aux->Prev)
{
counter += !IsFinite(aux->Value);
}
}
return counter;
}
}
MatrixInverse *SparseMatrix::Inverse() const
{
return NULL;
}
void SparseMatrix::EliminateRow(int row, const double sol, Vector &rhs)
{
RowNode *aux;
MFEM_ASSERT(row < height && row >= 0,
"Row " << row << " not in matrix of height " << height);
MFEM_VERIFY(!Finalized(), "Matrix must NOT be finalized.");
for (aux = Rows[row]; aux != NULL; aux = aux->Prev)
{
rhs(aux->Column) -= sol * aux->Value;
aux->Value = 0.0;
}
}
void SparseMatrix::EliminateRow(int row, DiagonalPolicy dpolicy)
{
RowNode *aux;
MFEM_ASSERT(row < height && row >= 0,
"Row " << row << " not in matrix of height " << height);
MFEM_ASSERT(dpolicy != DIAG_KEEP, "Diagonal policy must not be DIAG_KEEP");
MFEM_ASSERT(dpolicy != DIAG_ONE || height == width,
"if dpolicy == DIAG_ONE, matrix must be square, not height = "
<< height << ", width = " << width);
if (Rows == NULL)
{
for (int i=I[row]; i < I[row+1]; ++i)
{
A[i]=0.0;
}
}
else
{
for (aux = Rows[row]; aux != NULL; aux = aux->Prev)
{
aux->Value = 0.0;
}
}
if (dpolicy == DIAG_ONE)
{
SearchRow(row, row) = 1.;
}
}
void SparseMatrix::EliminateCol(int col, DiagonalPolicy dpolicy)
{
MFEM_ASSERT(col < width && col >= 0,
"Col " << col << " not in matrix of width " << width);
MFEM_ASSERT(dpolicy != DIAG_KEEP, "Diagonal policy must not be DIAG_KEEP");
MFEM_ASSERT(dpolicy != DIAG_ONE || height == width,
"if dpolicy == DIAG_ONE, matrix must be square, not height = "
<< height << ", width = " << width);
if (Rows == NULL)
{
const int nnz = I[height];
for (int jpos = 0; jpos != nnz; ++jpos)
{
if (J[jpos] == col)
{
A[jpos] = 0.0;
}
}
}
else
{
for (int i = 0; i < height; i++)
{
for (RowNode *aux = Rows[i]; aux != NULL; aux = aux->Prev)
{
if (aux->Column == col)
{
aux->Value = 0.0;
}
}
}
}
if (dpolicy == DIAG_ONE)
{
SearchRow(col, col) = 1.0;
}
}
void SparseMatrix::EliminateCols(const Array<int> &cols, const Vector *x,
Vector *b)
{
if (Rows == NULL)
{
for (int i = 0; i < height; i++)
{
for (int jpos = I[i]; jpos != I[i+1]; ++jpos)
{
if (cols[ J[jpos]])
{
if (x && b)
{
(*b)(i) -= A[jpos] * (*x)( J[jpos] );
}
A[jpos] = 0.0;
}
}
}
}
else
{
for (int i = 0; i < height; i++)
{
for (RowNode *aux = Rows[i]; aux != NULL; aux = aux->Prev)
{
if (cols[aux -> Column])
{
if (x && b)
{
(*b)(i) -= aux -> Value * (*x)(aux -> Column);
}
aux->Value = 0.0;
}
}
}
}
}
void SparseMatrix::EliminateCols(const Array<int> &col_marker, SparseMatrix &Ae)
{
if (Rows)
{
RowNode *nd;
for (int row = 0; row < height; row++)
{
for (nd = Rows[row]; nd != NULL; nd = nd->Prev)
{
if (col_marker[nd->Column])
{
Ae.Add(row, nd->Column, nd->Value);
nd->Value = 0.0;
}
}
}
}
else
{
for (int row = 0; row < height; row++)
{
for (int j = I[row]; j < I[row+1]; j++)
{
if (col_marker[J[j]])
{
Ae.Add(row, J[j], A[j]);
A[j] = 0.0;
}
}
}
}
}
void SparseMatrix::EliminateRowCol(int rc, const double sol, Vector &rhs,
DiagonalPolicy dpolicy)
{
MFEM_ASSERT(rc < height && rc >= 0,
"Row " << rc << " not in matrix of height " << height);
if (Rows == NULL)
{
for (int j = I[rc]; j < I[rc+1]; j++)
{
const int col = J[j];
if (col == rc)
{
switch (dpolicy)
{
case DIAG_KEEP:
rhs(rc) = A[j] * sol;
break;
case DIAG_ONE:
A[j] = 1.0;
rhs(rc) = sol;
break;
case DIAG_ZERO:
A[j] = 0.;
rhs(rc) = 0.;
break;
default:
mfem_error("SparseMatrix::EliminateRowCol () #2");
break;
}
}
else
{
A[j] = 0.0;
for (int k = I[col]; 1; k++)
{
if (k == I[col+1])
{
mfem_error("SparseMatrix::EliminateRowCol () #3");
}
else if (J[k] == rc)
{
rhs(col) -= sol * A[k];
A[k] = 0.0;
break;
}
}
}
}
}
else
{
for (RowNode *aux = Rows[rc]; aux != NULL; aux = aux->Prev)
{
const int col = aux->Column;
if (col == rc)
{
switch (dpolicy)
{
case DIAG_KEEP:
rhs(rc) = aux->Value * sol;
break;
case DIAG_ONE:
aux->Value = 1.0;
rhs(rc) = sol;
break;
case DIAG_ZERO:
aux->Value = 0.;
rhs(rc) = 0.;
break;
default:
mfem_error("SparseMatrix::EliminateRowCol () #4");
break;
}
}
else
{
aux->Value = 0.0;
for (RowNode *node = Rows[col]; 1; node = node->Prev)
{
if (node == NULL)
{
mfem_error("SparseMatrix::EliminateRowCol () #5");
}
else if (node->Column == rc)
{
rhs(col) -= sol * node->Value;
node->Value = 0.0;
break;
}
}
}
}
}
}
void SparseMatrix::EliminateRowColMultipleRHS(int rc, const Vector &sol,
DenseMatrix &rhs,
DiagonalPolicy dpolicy)
{
MFEM_ASSERT(rc < height && rc >= 0,
"Row " << rc << " not in matrix of height " << height);
MFEM_ASSERT(sol.Size() == rhs.Width(), "solution size (" << sol.Size()
<< ") must match rhs width (" << rhs.Width() << ")");
const int num_rhs = rhs.Width();
if (Rows == NULL)
{
for (int j = I[rc]; j < I[rc+1]; j++)
{
const int col = J[j];
if (col == rc)
{
switch (dpolicy)
{
case DIAG_KEEP:
for (int r = 0; r < num_rhs; r++)
{
rhs(rc,r) = A[j] * sol(r);
}
break;
case DIAG_ONE:
A[j] = 1.0;
for (int r = 0; r < num_rhs; r++)
{
rhs(rc,r) = sol(r);
}
break;
case DIAG_ZERO:
A[j] = 0.;
for (int r = 0; r < num_rhs; r++)
{
rhs(rc,r) = 0.;
}
break;
default:
mfem_error("SparseMatrix::EliminateRowColMultipleRHS() #3");
break;
}
}
else
{
A[j] = 0.0;
for (int k = I[col]; 1; k++)
{
if (k == I[col+1])
{
mfem_error("SparseMatrix::EliminateRowColMultipleRHS() #4");
}
else if (J[k] == rc)
{
for (int r = 0; r < num_rhs; r++)
{
rhs(col,r) -= sol(r) * A[k];
}
A[k] = 0.0;
break;
}
}
}
}
}
else
{
for (RowNode *aux = Rows[rc]; aux != NULL; aux = aux->Prev)
{
const int col = aux->Column;
if (col == rc)
{
switch (dpolicy)
{
case DIAG_KEEP:
for (int r = 0; r < num_rhs; r++)
{
rhs(rc,r) = aux->Value * sol(r);
}
break;
case DIAG_ONE:
aux->Value = 1.0;
for (int r = 0; r < num_rhs; r++)
{
rhs(rc,r) = sol(r);
}
break;
case DIAG_ZERO:
aux->Value = 0.;
for (int r = 0; r < num_rhs; r++)
{
rhs(rc,r) = 0.;
}
break;
default:
mfem_error("SparseMatrix::EliminateRowColMultipleRHS() #5");
break;
}
}
else
{
aux->Value = 0.0;
for (RowNode *node = Rows[col]; 1; node = node->Prev)
{
if (node == NULL)
{
mfem_error("SparseMatrix::EliminateRowColMultipleRHS() #6");
}
else if (node->Column == rc)
{
for (int r = 0; r < num_rhs; r++)
{
rhs(col,r) -= sol(r) * node->Value;
}
node->Value = 0.0;
break;
}
}
}
}
}
}
void SparseMatrix::EliminateRowCol(int rc, DiagonalPolicy dpolicy)
{
MFEM_ASSERT(rc < height && rc >= 0,
"Row " << rc << " not in matrix of height " << height);
if (Rows == NULL)
{
for (int j = I[rc]; j < I[rc+1]; j++)
{
const int col = J[j];
if (col == rc)
{
if (dpolicy == DIAG_ONE)
{
A[j] = 1.0;
}
else if (dpolicy == DIAG_ZERO)
{
A[j] = 0.0;
}
}
else
{
A[j] = 0.0;
for (int k = I[col]; 1; k++)
{
if (k == I[col+1])
{
mfem_error("SparseMatrix::EliminateRowCol() #2");
}
else if (J[k] == rc)
{
A[k] = 0.0;
break;
}
}
}
}
}
else
{
RowNode *aux, *node;
for (aux = Rows[rc]; aux != NULL; aux = aux->Prev)
{
const int col = aux->Column;
if (col == rc)
{
if (dpolicy == DIAG_ONE)
{
aux->Value = 1.0;
}
else if (dpolicy == DIAG_ZERO)
{
aux->Value = 0.;
}
}
else
{
aux->Value = 0.0;
for (node = Rows[col]; 1; node = node->Prev)
{
if (node == NULL)
{
mfem_error("SparseMatrix::EliminateRowCol() #3");
}
else if (node->Column == rc)
{
node->Value = 0.0;
break;
}
}
}
}
}
}
// This is almost identical to EliminateRowCol(int, int), except for
// the A[j] = value; and aux->Value = value; lines.
void SparseMatrix::EliminateRowColDiag(int rc, double value)
{
MFEM_ASSERT(rc < height && rc >= 0,
"Row " << rc << " not in matrix of height " << height);
if (Rows == NULL)
{
for (int j = I[rc]; j < I[rc+1]; j++)
{
const int col = J[j];
if (col == rc)
{
A[j] = value;
}
else
{
A[j] = 0.0;
for (int k = I[col]; 1; k++)
{
if (k == I[col+1])
{
mfem_error("SparseMatrix::EliminateRowCol() #2");
}
else if (J[k] == rc)
{
A[k] = 0.0;
break;
}
}
}
}
}
else
{
RowNode *aux, *node;
for (aux = Rows[rc]; aux != NULL; aux = aux->Prev)
{
const int col = aux->Column;
if (col == rc)
{
aux->Value = value;
}
else
{
aux->Value = 0.0;
for (node = Rows[col]; 1; node = node->Prev)
{
if (node == NULL)
{
mfem_error("SparseMatrix::EliminateRowCol() #3");
}
else if (node->Column == rc)
{
node->Value = 0.0;
break;
}
}
}
}
}
}
void SparseMatrix::EliminateRowCol(int rc, SparseMatrix &Ae,
DiagonalPolicy dpolicy)
{
if (Rows)
{
RowNode *nd, *nd2;
for (nd = Rows[rc]; nd != NULL; nd = nd->Prev)
{
const int col = nd->Column;
if (col == rc)
{
switch (dpolicy)
{
case DIAG_ONE:
Ae.Add(rc, rc, nd->Value - 1.0);
nd->Value = 1.0;
break;
case DIAG_ZERO:
Ae.Add(rc, rc, nd->Value);
nd->Value = 0.;
break;
case DIAG_KEEP:
break;
default:
mfem_error("SparseMatrix::EliminateRowCol #1");
break;
}
}
else
{
Ae.Add(rc, col, nd->Value);
nd->Value = 0.0;
for (nd2 = Rows[col]; 1; nd2 = nd2->Prev)
{
if (nd2 == NULL)
{
mfem_error("SparseMatrix::EliminateRowCol #2");
}
else if (nd2->Column == rc)
{
Ae.Add(col, rc, nd2->Value);
nd2->Value = 0.0;
break;
}
}
}
}
}
else
{
for (int j = I[rc]; j < I[rc+1]; j++)
{
const int col = J[j];
if (col == rc)
{
switch (dpolicy)
{
case DIAG_ONE:
Ae.Add(rc, rc, A[j] - 1.0);
A[j] = 1.0;
break;
case DIAG_ZERO:
Ae.Add(rc, rc, A[j]);
A[j] = 0.;
break;
case DIAG_KEEP:
break;
default:
mfem_error("SparseMatrix::EliminateRowCol #3");
break;
}
}
else
{
Ae.Add(rc, col, A[j]);
A[j] = 0.0;
for (int k = I[col]; true; k++)
{
if (k == I[col+1])
{
mfem_error("SparseMatrix::EliminateRowCol #4");
}
else if (J[k] == rc)
{
Ae.Add(col, rc, A[k]);
A[k] = 0.0;
break;
}
}
}
}
}
}
void SparseMatrix::SetDiagIdentity()
{
for (int i = 0; i < height; i++)
{
if (I[i+1] == I[i]+1 && fabs(A[I[i]]) < 1e-16)
{
A[I[i]] = 1.0;
}
}
}
void SparseMatrix::EliminateZeroRows(const double threshold)
{
for (int i = 0; i < height; i++)
{
double zero = 0.0;
for (int j = I[i]; j < I[i+1]; j++)
{
zero += fabs(A[j]);
}
if (zero <= threshold)
{
for (int j = I[i]; j < I[i+1]; j++)
{
A[j] = (J[j] == i) ? 1.0 : 0.0;
}
}
}
}
void SparseMatrix::Gauss_Seidel_forw(const Vector &x, Vector &y) const
{
if (!Finalized())
{
double *yp = y.GetData();
const double *xp = x.GetData();
RowNode *diag_p, *n_p, **R = Rows;
const int s = height;
for (int i = 0; i < s; i++)
{
double sum = 0.0;
diag_p = NULL;
for (n_p = R[i]; n_p != NULL; n_p = n_p->Prev)
{
const int c = n_p->Column;
if (c == i)
{
diag_p = n_p;
}
else
{
sum += n_p->Value * yp[c];
}
}
if (diag_p != NULL && diag_p->Value != 0.0)
{
yp[i] = (xp[i] - sum) / diag_p->Value;
}
else if (xp[i] == sum)
{
yp[i] = sum;
}
else
{
mfem_error("SparseMatrix::Gauss_Seidel_forw()");
}
}
}
else
{
const int s = height;
const int nnz = J.Capacity();
const int *Ip = HostRead(I, s+1);
const int *Jp = HostRead(J, nnz);
const double *Ap = HostRead(A, nnz);
double *yp = y.HostReadWrite();
const double *xp = x.HostRead();
for (int i = 0, j = Ip[0]; i < s; i++)
{
const int end = Ip[i+1];
double sum = 0.0;
int d = -1;
for ( ; j < end; j++)
{
const int c = Jp[j];
if (c == i)
{
d = j;
}
else
{
sum += Ap[j] * yp[c];
}
}
if (d >= 0 && Ap[d] != 0.0)
{
yp[i] = (xp[i] - sum) / Ap[d];
}
else if (xp[i] == sum)
{
yp[i] = sum;
}
else
{
mfem_error("SparseMatrix::Gauss_Seidel_forw(...) #2");
}
}
}
}
void SparseMatrix::Gauss_Seidel_back(const Vector &x, Vector &y) const
{
if (!Finalized())
{
double *yp = y.GetData();
const double *xp = x.GetData();
RowNode *diag_p, *n_p, **R = Rows;
for (int i = height-1; i >= 0; i--)
{
double sum = 0.;
diag_p = NULL;
for (n_p = R[i]; n_p != NULL; n_p = n_p->Prev)
{
const int c = n_p->Column;
if (c == i)
{
diag_p = n_p;
}
else
{
sum += n_p->Value * yp[c];
}
}
if (diag_p != NULL && diag_p->Value != 0.0)
{
yp[i] = (xp[i] - sum) / diag_p->Value;
}
else if (xp[i] == sum)
{
yp[i] = sum;
}
else
{
mfem_error("SparseMatrix::Gauss_Seidel_back()");
}
}
}
else
{
const int s = height;
const int nnz = J.Capacity();
const int *Ip = HostRead(I, s+1);
const int *Jp = HostRead(J, nnz);
const double *Ap = HostRead(A, nnz);
double *yp = y.HostReadWrite();
const double *xp = x.HostRead();
for (int i = s-1, j = Ip[s]-1; i >= 0; i--)
{
const int beg = Ip[i];
double sum = 0.;
int d = -1;
for ( ; j >= beg; j--)
{
const int c = Jp[j];
if (c == i)
{
d = j;
}
else
{
sum += Ap[j] * yp[c];
}
}
if (d >= 0 && Ap[d] != 0.0)
{
yp[i] = (xp[i] - sum) / Ap[d];
}
else if (xp[i] == sum)
{
yp[i] = sum;
}
else
{
mfem_error("SparseMatrix::Gauss_Seidel_back(...) #2");
}
}
}
}
double SparseMatrix::GetJacobiScaling() const
{
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
double sc = 1.0;
for (int i = 0; i < height; i++)
{
int d = -1;
double norm = 0.0;
for (int j = I[i]; j < I[i+1]; j++)
{
if (J[j] == i)
{
d = j;
}
norm += fabs(A[j]);
}
if (d >= 0 && A[d] != 0.0)
{
double a = 1.8 * fabs(A[d]) / norm;
if (a < sc)
{
sc = a;
}
}
else
{
mfem_error("SparseMatrix::GetJacobiScaling() #2");
}
}
return sc;
}
void SparseMatrix::Jacobi(const Vector &b, const Vector &x0, Vector &x1,
double sc) const
{
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
for (int i = 0; i < height; i++)
{
int d = -1;
double sum = b(i);
for (int j = I[i]; j < I[i+1]; j++)
{
if (J[j] == i)
{
d = j;
}
else
{
sum -= A[j] * x0(J[j]);
}
}
if (d >= 0 && A[d] != 0.0)
{
x1(i) = sc * (sum / A[d]) + (1.0 - sc) * x0(i);
}
else
{
mfem_error("SparseMatrix::Jacobi(...) #2");
}
}
}
void SparseMatrix::DiagScale(const Vector &b, Vector &x, double sc) const
{
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
bool scale = (sc != 1.0);
for (int i = 0, j = 0; i < height; i++)
{
int end = I[i+1];
for ( ; true; j++)
{
MFEM_VERIFY(j != end, "Couldn't find diagonal in row. i = " << i
<< ", j = " << j
<< ", I[i+1] = " << end );
if (J[j] == i)
{
MFEM_VERIFY(std::abs(A[j]) > 0.0, "Diagonal " << j << " must be nonzero");
if (scale)
{
x(i) = sc * b(i) / A[j];
}
else
{
x(i) = b(i) / A[j];
}
break;
}
}
j = end;
}
return;
}
void SparseMatrix::Jacobi2(const Vector &b, const Vector &x0, Vector &x1,
double sc) const
{
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
for (int i = 0; i < height; i++)
{
double resi = b(i), norm = 0.0;
for (int j = I[i]; j < I[i+1]; j++)
{
resi -= A[j] * x0(J[j]);
norm += fabs(A[j]);
}
if (norm > 0.0)
{
x1(i) = x0(i) + sc * resi / norm;
}
else
{
MFEM_ABORT("L1 norm of row " << i << " is zero.");
}
}
}
void SparseMatrix::Jacobi3(const Vector &b, const Vector &x0, Vector &x1,
double sc) const
{
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
for (int i = 0; i < height; i++)
{
double resi = b(i), sum = 0.0;
for (int j = I[i]; j < I[i+1]; j++)
{
resi -= A[j] * x0(J[j]);
sum += A[j];
}
if (sum > 0.0)
{
x1(i) = x0(i) + sc * resi / sum;
}
else
{
MFEM_ABORT("sum of row " << i << " is zero.");
}
}
}
void SparseMatrix::AddSubMatrix(const Array<int> &rows, const Array<int> &cols,
const DenseMatrix &subm, int skip_zeros)
{
int i, j, gi, gj, s, t;
double a;
for (i = 0; i < rows.Size(); i++)
{
if ((gi=rows[i]) < 0) { gi = -1-gi, s = -1; }
else { s = 1; }
MFEM_ASSERT(gi < height,
"Trying to insert a row " << gi << " outside the matrix height "
<< height);
SetColPtr(gi);
for (j = 0; j < cols.Size(); j++)
{
if ((gj=cols[j]) < 0) { gj = -1-gj, t = -s; }
else { t = s; }
MFEM_ASSERT(gj < width,
"Trying to insert a column " << gj << " outside the matrix width "
<< width);
a = subm(i, j);
if (skip_zeros && a == 0.0)
{
// if the element is zero do not assemble it unless this breaks
// the symmetric structure
if (&rows != &cols || subm(j, i) == 0.0)
{
continue;
}
}
if (t < 0) { a = -a; }
_Add_(gj, a);
}
ClearColPtr();
}
}
void SparseMatrix::Set(const int i, const int j, const double A)
{
double a = A;
int gi, gj, s, t;
if ((gi=i) < 0) { gi = -1-gi, s = -1; }
else { s = 1; }
MFEM_ASSERT(gi < height,
"Trying to set a row " << gi << " outside the matrix height "
<< height);
if ((gj=j) < 0) { gj = -1-gj, t = -s; }
else { t = s; }
MFEM_ASSERT(gj < width,
"Trying to set a column " << gj << " outside the matrix width "
<< width);
if (t < 0) { a = -a; }
_Set_(gi, gj, a);
}
void SparseMatrix::Add(const int i, const int j, const double A)
{
int gi, gj, s, t;
double a = A;
if ((gi=i) < 0) { gi = -1-gi, s = -1; }
else { s = 1; }
MFEM_ASSERT(gi < height,
"Trying to insert a row " << gi << " outside the matrix height "
<< height);
if ((gj=j) < 0) { gj = -1-gj, t = -s; }
else { t = s; }
MFEM_ASSERT(gj < width,
"Trying to insert a column " << gj << " outside the matrix width "
<< width);
if (t < 0) { a = -a; }
_Add_(gi, gj, a);
}
void SparseMatrix::SetSubMatrix(const Array<int> &rows, const Array<int> &cols,
const DenseMatrix &subm, int skip_zeros)
{
int i, j, gi, gj, s, t;
double a;
for (i = 0; i < rows.Size(); i++)
{
if ((gi=rows[i]) < 0) { gi = -1-gi, s = -1; }
else { s = 1; }
MFEM_ASSERT(gi < height,
"Trying to set a row " << gi << " outside the matrix height "
<< height);
SetColPtr(gi);
for (j = 0; j < cols.Size(); j++)
{
a = subm(i, j);
if (skip_zeros && a == 0.0)
{
continue;
}
if ((gj=cols[j]) < 0) { gj = -1-gj, t = -s; }
else { t = s; }
MFEM_ASSERT(gj < width,
"Trying to set a column " << gj << " outside the matrix width "
<< width);
if (t < 0) { a = -a; }
_Set_(gj, a);
}
ClearColPtr();
}
}
void SparseMatrix::SetSubMatrixTranspose(const Array<int> &rows,
const Array<int> &cols,
const DenseMatrix &subm,
int skip_zeros)
{
int i, j, gi, gj, s, t;
double a;
for (i = 0; i < rows.Size(); i++)
{
if ((gi=rows[i]) < 0) { gi = -1-gi, s = -1; }
else { s = 1; }
MFEM_ASSERT(gi < height,
"Trying to set a row " << gi << " outside the matrix height "
<< height);
SetColPtr(gi);
for (j = 0; j < cols.Size(); j++)
{
a = subm(j, i);
if (skip_zeros && a == 0.0)
{
continue;
}
if ((gj=cols[j]) < 0) { gj = -1-gj, t = -s; }
else { t = s; }
MFEM_ASSERT(gj < width,
"Trying to set a column " << gj << " outside the matrix width "
<< width);
if (t < 0) { a = -a; }
_Set_(gj, a);
}
ClearColPtr();
}
}
void SparseMatrix::GetSubMatrix(const Array<int> &rows, const Array<int> &cols,
DenseMatrix &subm) const
{
int i, j, gi, gj, s, t;
double a;
for (i = 0; i < rows.Size(); i++)
{
if ((gi=rows[i]) < 0) { gi = -1-gi, s = -1; }
else { s = 1; }
MFEM_ASSERT(gi < height,
"Trying to read a row " << gi << " outside the matrix height "
<< height);
SetColPtr(gi);
for (j = 0; j < cols.Size(); j++)
{
if ((gj=cols[j]) < 0) { gj = -1-gj, t = -s; }
else { t = s; }
MFEM_ASSERT(gj < width,
"Trying to read a column " << gj << " outside the matrix width "
<< width);
a = _Get_(gj);
subm(i, j) = (t < 0) ? (-a) : (a);
}
ClearColPtr();
}
}
bool SparseMatrix::RowIsEmpty(const int row) const
{
int gi;
if ((gi=row) < 0)
{
gi = -1-gi;
}
MFEM_ASSERT(gi < height,
"Trying to query a row " << gi << " outside the matrix height "
<< height);
if (Rows)
{
return (Rows[gi] == NULL);
}
else
{
return (I[gi] == I[gi+1]);
}
}
int SparseMatrix::GetRow(const int row, Array<int> &cols, Vector &srow) const
{
RowNode *n;
int j, gi;
if ((gi=row) < 0) { gi = -1-gi; }
MFEM_ASSERT(gi < height,
"Trying to read a row " << gi << " outside the matrix height "
<< height);
if (Rows)
{
for (n = Rows[gi], j = 0; n; n = n->Prev)
{
j++;
}
cols.SetSize(j);
srow.SetSize(j);
for (n = Rows[gi], j = 0; n; n = n->Prev, j++)
{
cols[j] = n->Column;
srow(j) = n->Value;
}
if (row < 0)
{
srow.Neg();
}
return 0;
}
else
{
j = I[gi];
cols.MakeRef(const_cast<int*>((const int*)J) + j, I[gi+1]-j);
srow.NewDataAndSize(
const_cast<double*>((const double*)A) + j, cols.Size());
MFEM_ASSERT(row >= 0, "Row not valid: " << row << ", height: " << height);
return 1;
}
}
void SparseMatrix::SetRow(const int row, const Array<int> &cols,
const Vector &srow)
{
int gi, gj, s, t;
double a;
if ((gi=row) < 0) { gi = -1-gi, s = -1; }
else { s = 1; }
MFEM_ASSERT(gi < height,
"Trying to set a row " << gi << " outside the matrix height "
<< height);
if (!Finalized())
{
SetColPtr(gi);
for (int j = 0; j < cols.Size(); j++)
{
if ((gj=cols[j]) < 0) { gj = -1-gj, t = -s; }
else { t = s; }
MFEM_ASSERT(gj < width,
"Trying to set a column " << gj << " outside the matrix"
" width " << width);
a = srow(j);
if (t < 0) { a = -a; }
_Set_(gj, a);
}
ClearColPtr();
}
else
{
MFEM_ASSERT(cols.Size() == RowSize(gi), "");
MFEM_ASSERT(cols.Size() == srow.Size(), "");
for (int i = I[gi], j = 0; j < cols.Size(); j++, i++)
{
if ((gj=cols[j]) < 0) { gj = -1-gj, t = -s; }
else { t = s; }
MFEM_ASSERT(gj < width,
"Trying to set a column " << gj << " outside the matrix"
" width " << width);
J[i] = gj;
A[i] = srow[j] * t;
}
}
}
void SparseMatrix::AddRow(const int row, const Array<int> &cols,
const Vector &srow)
{
int j, gi, gj, s, t;
double a;
MFEM_VERIFY(!Finalized(), "Matrix must NOT be finalized.");
if ((gi=row) < 0) { gi = -1-gi, s = -1; }
else { s = 1; }
MFEM_ASSERT(gi < height,
"Trying to insert a row " << gi << " outside the matrix height "
<< height);
SetColPtr(gi);
for (j = 0; j < cols.Size(); j++)
{
if ((gj=cols[j]) < 0) { gj = -1-gj, t = -s; }
else { t = s; }
MFEM_ASSERT(gj < width,
"Trying to insert a column " << gj << " outside the matrix width "
<< width);
a = srow(j);
if (a == 0.0)
{
continue;
}
if (t < 0) { a = -a; }
_Add_(gj, a);
}
ClearColPtr();
}
void SparseMatrix::ScaleRow(const int row, const double scale)
{
int i;
if ((i=row) < 0)
{
i = -1-i;
}
if (Rows != NULL)
{
RowNode *aux;
for (aux = Rows[i]; aux != NULL; aux = aux -> Prev)
{
aux -> Value *= scale;
}
}
else
{
int j, end = I[i+1];
for (j = I[i]; j < end; j++)
{
A[j] *= scale;
}
}
}
void SparseMatrix::ScaleRows(const Vector & sl)
{
double scale;
if (Rows != NULL)
{
RowNode *aux;
for (int i=0; i < height; ++i)
{
scale = sl(i);
for (aux = Rows[i]; aux != NULL; aux = aux -> Prev)
{
aux -> Value *= scale;
}
}
}
else
{
int j, end;
for (int i=0; i < height; ++i)
{
end = I[i+1];
scale = sl(i);
for (j = I[i]; j < end; j++)
{
A[j] *= scale;
}
}
}
}
void SparseMatrix::ScaleColumns(const Vector & sr)
{
if (Rows != NULL)
{
RowNode *aux;
for (int i=0; i < height; ++i)
{
for (aux = Rows[i]; aux != NULL; aux = aux -> Prev)
{
aux -> Value *= sr(aux->Column);
}
}
}
else
{
int j, end;
for (int i=0; i < height; ++i)
{
end = I[i+1];
for (j = I[i]; j < end; j++)
{
A[j] *= sr(J[j]);
}
}
}
}
SparseMatrix &SparseMatrix::operator+=(const SparseMatrix &B)
{
MFEM_ASSERT(height == B.height && width == B.width,
"Mismatch of this matrix size and rhs. This height = "
<< height << ", width = " << width << ", B.height = "
<< B.height << ", B.width = " << width);
for (int i = 0; i < height; i++)
{
SetColPtr(i);
if (B.Rows)
{
for (RowNode *aux = B.Rows[i]; aux != NULL; aux = aux->Prev)
{
_Add_(aux->Column, aux->Value);
}
}
else
{
for (int j = B.I[i]; j < B.I[i+1]; j++)
{
_Add_(B.J[j], B.A[j]);
}
}
ClearColPtr();
}
return (*this);
}
void SparseMatrix::Add(const double a, const SparseMatrix &B)
{
for (int i = 0; i < height; i++)
{
B.SetColPtr(i);
if (Rows)
{
for (RowNode *np = Rows[i]; np != NULL; np = np->Prev)
{
np->Value += a * B._Get_(np->Column);
}
}
else
{
for (int j = I[i]; j < I[i+1]; j++)
{
A[j] += a * B._Get_(J[j]);
}
}
B.ClearColPtr();
}
}
SparseMatrix &SparseMatrix::operator=(double a)
{
if (Rows == NULL)
{
const int nnz = J.Capacity();
double *h_A = HostWrite(A, nnz);
for (int i = 0; i < nnz; i++)
{
h_A[i] = a;
}
}
else
{
for (int i = 0; i < height; i++)
{
for (RowNode *node_p = Rows[i]; node_p != NULL;
node_p = node_p -> Prev)
{
node_p -> Value = a;
}
}
}
return (*this);
}
SparseMatrix &SparseMatrix::operator*=(double a)
{
if (Rows == NULL)
{
for (int i = 0, nnz = I[height]; i < nnz; i++)
{
A[i] *= a;
}
}
else
{
for (int i = 0; i < height; i++)
{
for (RowNode *node_p = Rows[i]; node_p != NULL;
node_p = node_p -> Prev)
{
node_p -> Value *= a;
}
}
}
return (*this);
}
void SparseMatrix::Print(std::ostream & out, int _width) const
{
int i, j;
if (A == NULL)
{
RowNode *nd;
for (i = 0; i < height; i++)
{
out << "[row " << i << "]\n";
for (nd = Rows[i], j = 0; nd != NULL; nd = nd->Prev, j++)
{
out << " (" << nd->Column << "," << nd->Value << ")";
if ( !((j+1) % _width) )
{
out << '\n';
}
}
if (j % _width)
{
out << '\n';
}
}
return;
}
for (i = 0; i < height; i++)
{
out << "[row " << i << "]\n";
for (j = I[i]; j < I[i+1]; j++)
{
out << " (" << J[j] << "," << A[j] << ")";
if ( !((j+1-I[i]) % _width) )
{
out << '\n';
}
}
if ((j-I[i]) % _width)
{
out << '\n';
}
}
}
void SparseMatrix::PrintMatlab(std::ostream & out) const
{
out << "% size " << height << " " << width << "\n";
out << "% Non Zeros " << NumNonZeroElems() << "\n";
int i, j;
ios::fmtflags old_fmt = out.flags();
out.setf(ios::scientific);
std::streamsize old_prec = out.precision(14);
for (i = 0; i < height; i++)
{
for (j = I[i]; j < I[i+1]; j++)
{
out << i+1 << " " << J[j]+1 << " " << A[j] << '\n';
}
}
out.precision(old_prec);
out.flags(old_fmt);
}
void SparseMatrix::PrintMM(std::ostream & out) const
{
int i, j;
ios::fmtflags old_fmt = out.flags();
out.setf(ios::scientific);
std::streamsize old_prec = out.precision(14);
out << "%%MatrixMarket matrix coordinate real general" << '\n'
<< "% Generated by MFEM" << '\n';
out << height << " " << width << " " << NumNonZeroElems() << '\n';
for (i = 0; i < height; i++)
{
for (j = I[i]; j < I[i+1]; j++)
{
out << i+1 << " " << J[j]+1 << " " << A[j] << '\n';
}
}
out.precision(old_prec);
out.flags(old_fmt);
}
void SparseMatrix::PrintCSR(std::ostream & out) const
{
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
int i;
out << height << '\n'; // number of rows
for (i = 0; i <= height; i++)
{
out << I[i]+1 << '\n';
}
for (i = 0; i < I[height]; i++)
{
out << J[i]+1 << '\n';
}
for (i = 0; i < I[height]; i++)
{
out << A[i] << '\n';
}
}
void SparseMatrix::PrintCSR2(std::ostream & out) const
{
MFEM_VERIFY(Finalized(), "Matrix must be finalized.");
int i;
out << height << '\n'; // number of rows
out << width << '\n'; // number of columns
for (i = 0; i <= height; i++)
{
out << I[i] << '\n';
}
for (i = 0; i < I[height]; i++)
{
out << J[i] << '\n';
}
for (i = 0; i < I[height]; i++)
{
out << A[i] << '\n';
}
}
void SparseMatrix::PrintInfo(std::ostream &out) const
{
const double MiB = 1024.*1024;
int nnz = NumNonZeroElems();
double pz = 100./nnz;
int nz = CountSmallElems(0.0);
double max_norm = MaxNorm();
double symm = IsSymmetric();
int nnf = CheckFinite();
int ns12 = CountSmallElems(1e-12*max_norm);
int ns15 = CountSmallElems(1e-15*max_norm);
int ns18 = CountSmallElems(1e-18*max_norm);
out <<
"SparseMatrix statistics:\n"
" Format : " <<
(Empty() ? "(empty)" : (Finalized() ? "CSR" : "LIL")) << "\n"
" Dimensions : " << height << " x " << width << "\n"
" Number of entries (total) : " << nnz << "\n"
" Number of entries (per row) : " << 1.*nnz/Height() << "\n"
" Number of stored zeros : " << nz*pz << "% (" << nz << ")\n"
" Number of Inf/Nan entries : " << nnf*pz << "% ("<< nnf << ")\n"
" Norm, max |a_ij| : " << max_norm << "\n"
" Symmetry, max |a_ij-a_ji| : " << symm << "\n"
" Number of small entries:\n"
" |a_ij| <= 1e-12*Norm : " << ns12*pz << "% (" << ns12 << ")\n"
" |a_ij| <= 1e-15*Norm : " << ns15*pz << "% (" << ns15 << ")\n"
" |a_ij| <= 1e-18*Norm : " << ns18*pz << "% (" << ns18 << ")\n";
if (Finalized())
{
out << " Memory used by CSR : " <<
(sizeof(int)*(height+1+nnz)+sizeof(double)*nnz)/MiB << " MiB\n";
}
if (Rows != NULL)
{
size_t used_mem = sizeof(RowNode*)*height;
#ifdef MFEM_USE_MEMALLOC
used_mem += NodesMem->MemoryUsage();
#else
for (int i = 0; i < height; i++)
{
for (RowNode *aux = Rows[i]; aux != NULL; aux = aux->Prev)
{
used_mem += sizeof(RowNode);
}
}
#endif
out << " Memory used by LIL : " << used_mem/MiB << " MiB\n";
}
}
void SparseMatrix::Destroy()
{
I.Delete();
J.Delete();
A.Delete();
if (Rows != NULL)
{
#if !defined(MFEM_USE_MEMALLOC)
for (int i = 0; i < height; i++)
{
RowNode *aux, *node_p = Rows[i];
while (node_p != NULL)
{
aux = node_p;
node_p = node_p->Prev;
delete aux;
}
}
#endif
delete [] Rows;
}
delete [] ColPtrJ;
delete [] ColPtrNode;
#ifdef MFEM_USE_MEMALLOC
delete NodesMem;
#endif
delete At;
}
int SparseMatrix::ActualWidth() const
{
int awidth = 0;
if (A)
{
const int *start_j = J;
const int *end_j = J + I[height];
for (const int *jptr = start_j; jptr != end_j; ++jptr)
{
awidth = std::max(awidth, *jptr + 1);
}
}
else
{
RowNode *aux;
for (int i = 0; i < height; i++)
{
for (aux = Rows[i]; aux != NULL; aux = aux->Prev)
{
awidth = std::max(awidth, aux->Column + 1);
}
}
}
return awidth;
}
void SparseMatrixFunction (SparseMatrix & S, double (*f)(double))
{
int n = S.NumNonZeroElems();
double * s = S.GetData();
for (int i = 0; i < n; i++)
{
s[i] = f(s[i]);
}
}
SparseMatrix *Transpose (const SparseMatrix &A)
{
MFEM_VERIFY(
A.Finalized(),
"Finalize must be called before Transpose. Use TransposeRowMatrix instead");
int i, j, end;
const int *A_i, *A_j;
int m, n, nnz, *At_i, *At_j;
const double *A_data;
double *At_data;
m = A.Height(); // number of rows of A
n = A.Width(); // number of columns of A
nnz = A.NumNonZeroElems();
A_i = A.GetI();
A_j = A.GetJ();
A_data = A.GetData();
At_i = Memory<int>(n+1);
At_j = Memory<int>(nnz);
At_data = Memory<double>(nnz);
for (i = 0; i <= n; i++)
{
At_i[i] = 0;
}
for (i = 0; i < nnz; i++)
{
At_i[A_j[i]+1]++;
}
for (i = 1; i < n; i++)
{
At_i[i+1] += At_i[i];
}
for (i = j = 0; i < m; i++)
{
end = A_i[i+1];
for ( ; j < end; j++)
{
At_j[At_i[A_j[j]]] = i;
At_data[At_i[A_j[j]]] = A_data[j];
At_i[A_j[j]]++;
}
}
for (i = n; i > 0; i--)
{
At_i[i] = At_i[i-1];
}
At_i[0] = 0;
return new SparseMatrix(At_i, At_j, At_data, n, m);
}
SparseMatrix *TransposeAbstractSparseMatrix (const AbstractSparseMatrix &A,
int useActualWidth)
{
int i, j;
int m, n, nnz, *At_i, *At_j;
double *At_data;
Array<int> Acols;
Vector Avals;
m = A.Height(); // number of rows of A
if (useActualWidth)
{
n = 0;
int tmp;
for (i = 0; i < m; i++)
{
A.GetRow(i, Acols, Avals);
if (Acols.Size())
{
tmp = Acols.Max();
if (tmp > n)
{
n = tmp;
}
}
}
++n;
}
else
{
n = A.Width(); // number of columns of A
}
nnz = A.NumNonZeroElems();
At_i = Memory<int>(n+1);
At_j = Memory<int>(nnz);
At_data = Memory<double>(nnz);
for (i = 0; i <= n; i++)
{
At_i[i] = 0;
}
for (i = 0; i < m; i++)
{
A.GetRow(i, Acols, Avals);
for (j = 0; j<Acols.Size(); ++j)
{
At_i[Acols[j]+1]++;
}
}
for (i = 1; i < n; i++)
{
At_i[i+1] += At_i[i];
}
for (i = 0; i < m; i++)
{
A.GetRow(i, Acols, Avals);
for (j = 0; j<Acols.Size(); ++j)
{
At_j[At_i[Acols[j]]] = i;
At_data[At_i[Acols[j]]] = Avals[j];
At_i[Acols[j]]++;
}
}
for (i = n; i > 0; i--)
{
At_i[i] = At_i[i-1];
}
At_i[0] = 0;
return new SparseMatrix(At_i, At_j, At_data, n, m);
}
SparseMatrix *Mult (const SparseMatrix &A, const SparseMatrix &B,
SparseMatrix *OAB)
{
int nrowsA, ncolsA, nrowsB, ncolsB;
const int *A_i, *A_j, *B_i, *B_j;
int *C_i, *C_j, *B_marker;
const double *A_data, *B_data;
double *C_data;
int ia, ib, ic, ja, jb, num_nonzeros;
int row_start, counter;
double a_entry, b_entry;
SparseMatrix *C;
nrowsA = A.Height();
ncolsA = A.Width();
nrowsB = B.Height();
ncolsB = B.Width();
MFEM_VERIFY(ncolsA == nrowsB,
"number of columns of A (" << ncolsA
<< ") must equal number of rows of B (" << nrowsB << ")");
A_i = A.GetI();
A_j = A.GetJ();
A_data = A.GetData();
B_i = B.GetI();
B_j = B.GetJ();
B_data = B.GetData();
B_marker = new int[ncolsB];
for (ib = 0; ib < ncolsB; ib++)
{
B_marker[ib] = -1;
}
if (OAB == NULL)
{
C_i = Memory<int>(nrowsA+1);
C_i[0] = num_nonzeros = 0;
for (ic = 0; ic < nrowsA; ic++)
{
for (ia = A_i[ic]; ia < A_i[ic+1]; ia++)
{
ja = A_j[ia];
for (ib = B_i[ja]; ib < B_i[ja+1]; ib++)
{
jb = B_j[ib];
if (B_marker[jb] != ic)
{
B_marker[jb] = ic;
num_nonzeros++;
}
}
}
C_i[ic+1] = num_nonzeros;
}
C_j = Memory<int>(num_nonzeros);
C_data = Memory<double>(num_nonzeros);
C = new SparseMatrix(C_i, C_j, C_data, nrowsA, ncolsB);
for (ib = 0; ib < ncolsB; ib++)
{
B_marker[ib] = -1;
}
}
else
{
C = OAB;
MFEM_VERIFY(nrowsA == C -> Height() && ncolsB == C -> Width(),
"Input matrix sizes do not match output sizes"
<< " nrowsA = " << nrowsA
<< ", C->Height() = " << C->Height()
<< " ncolsB = " << ncolsB
<< ", C->Width() = " << C->Width());
// C_i = C -> GetI(); // not used
C_j = C -> GetJ();
C_data = C -> GetData();
}
counter = 0;
for (ic = 0; ic < nrowsA; ic++)
{
// row_start = C_i[ic];
row_start = counter;
for (ia = A_i[ic]; ia < A_i[ic+1]; ia++)
{
ja = A_j[ia];
a_entry = A_data[ia];
for (ib = B_i[ja]; ib < B_i[ja+1]; ib++)
{
jb = B_j[ib];
b_entry = B_data[ib];
if (B_marker[jb] < row_start)
{
B_marker[jb] = counter;
if (OAB == NULL)
{
C_j[counter] = jb;
}
C_data[counter] = a_entry*b_entry;
counter++;
}
else
{
C_data[B_marker[jb]] += a_entry*b_entry;
}
}
}
}
MFEM_VERIFY(
OAB == NULL || counter == OAB->NumNonZeroElems(),
"With pre-allocated output matrix, number of non-zeros ("
<< OAB->NumNonZeroElems()
<< ") did not match number of entries changed from matrix-matrix multiply, "
<< counter);
delete [] B_marker;
return C;
}
SparseMatrix * TransposeMult(const SparseMatrix &A, const SparseMatrix &B)
{
SparseMatrix *At = Transpose(A);
SparseMatrix *AtB = Mult(*At, B);
delete At;
return AtB;
}
SparseMatrix *MultAbstractSparseMatrix (const AbstractSparseMatrix &A,
const AbstractSparseMatrix &B)
{
int nrowsA, ncolsA, nrowsB, ncolsB;
int *C_i, *C_j, *B_marker;
double *C_data;
int ia, ib, ic, ja, jb, num_nonzeros;
int row_start, counter;
double a_entry, b_entry;
SparseMatrix *C;
nrowsA = A.Height();
ncolsA = A.Width();
nrowsB = B.Height();
ncolsB = B.Width();
MFEM_VERIFY(ncolsA == nrowsB,
"number of columns of A (" << ncolsA
<< ") must equal number of rows of B (" << nrowsB << ")");
B_marker = new int[ncolsB];
for (ib = 0; ib < ncolsB; ib++)
{
B_marker[ib] = -1;
}
C_i = Memory<int>(nrowsA+1);
C_i[0] = num_nonzeros = 0;
Array<int> colsA, colsB;
Vector dataA, dataB;
for (ic = 0; ic < nrowsA; ic++)
{
A.GetRow(ic, colsA, dataA);
for (ia = 0; ia < colsA.Size(); ia++)
{
ja = colsA[ia];
B.GetRow(ja, colsB, dataB);
for (ib = 0; ib < colsB.Size(); ib++)
{
jb = colsB[ib];
if (B_marker[jb] != ic)
{
B_marker[jb] = ic;
num_nonzeros++;
}
}
}
C_i[ic+1] = num_nonzeros;
}
C_j = Memory<int>(num_nonzeros);
C_data = Memory<double>(num_nonzeros);
C = new SparseMatrix(C_i, C_j, C_data, nrowsA, ncolsB);
for (ib = 0; ib < ncolsB; ib++)
{
B_marker[ib] = -1;
}
counter = 0;
for (ic = 0; ic < nrowsA; ic++)
{
row_start = counter;
A.GetRow(ic, colsA, dataA);
for (ia = 0; ia < colsA.Size(); ia++)
{
ja = colsA[ia];
a_entry = dataA[ia];
B.GetRow(ja, colsB, dataB);
for (ib = 0; ib < colsB.Size(); ib++)
{
jb = colsB[ib];
b_entry = dataB[ib];
if (B_marker[jb] < row_start)
{
B_marker[jb] = counter;
C_j[counter] = jb;
C_data[counter] = a_entry*b_entry;
counter++;
}
else
{
C_data[B_marker[jb]] += a_entry*b_entry;
}
}
}
}
delete [] B_marker;
return C;
}
DenseMatrix *Mult (const SparseMatrix &A, DenseMatrix &B)
{
DenseMatrix *C = new DenseMatrix(A.Height(), B.Width());
Vector columnB, columnC;
for (int j = 0; j < B.Width(); ++j)
{
B.GetColumnReference(j, columnB);
C->GetColumnReference(j, columnC);
A.Mult(columnB, columnC);
}
return C;
}
DenseMatrix *RAP (const SparseMatrix &A, DenseMatrix &P)
{
DenseMatrix R (P, 't'); // R = P^T
DenseMatrix *AP = Mult (A, P);
DenseMatrix *_RAP = new DenseMatrix(R.Height(), AP->Width());
Mult (R, *AP, *_RAP);
delete AP;
return _RAP;
}
DenseMatrix *RAP(DenseMatrix &A, const SparseMatrix &P)
{
SparseMatrix *R = Transpose(P);
DenseMatrix *RA = Mult(*R, A);
DenseMatrix AtP(*RA, 't');
delete RA;
DenseMatrix *RAtP = Mult(*R, AtP);
delete R;
DenseMatrix * _RAP = new DenseMatrix(*RAtP, 't');
delete RAtP;
return _RAP;
}
SparseMatrix *RAP (const SparseMatrix &A, const SparseMatrix &R,
SparseMatrix *ORAP)
{
SparseMatrix *P = Transpose (R);
SparseMatrix *AP = Mult (A, *P);
delete P;
SparseMatrix *_RAP = Mult (R, *AP, ORAP);
delete AP;
return _RAP;
}
SparseMatrix *RAP(const SparseMatrix &Rt, const SparseMatrix &A,
const SparseMatrix &P)
{
SparseMatrix * R = Transpose(Rt);
SparseMatrix * RA = Mult(*R,A);
delete R;
SparseMatrix * out = Mult(*RA, P);
delete RA;
return out;
}
SparseMatrix *Mult_AtDA (const SparseMatrix &A, const Vector &D,
SparseMatrix *OAtDA)
{
int i, At_nnz, *At_j;
double *At_data;
SparseMatrix *At = Transpose (A);
At_nnz = At -> NumNonZeroElems();
At_j = At -> GetJ();
At_data = At -> GetData();
for (i = 0; i < At_nnz; i++)
{
At_data[i] *= D(At_j[i]);
}
SparseMatrix *AtDA = Mult (*At, A, OAtDA);
delete At;
return AtDA;
}
SparseMatrix * Add(double a, const SparseMatrix & A, double b,
const SparseMatrix & B)
{
int nrows = A.Height();
int ncols = A.Width();
int * C_i = Memory<int>(nrows+1);
int * C_j;
double * C_data;
const int *A_i = A.GetI();
const int *A_j = A.GetJ();
const double *A_data = A.GetData();
const int *B_i = B.GetI();
const int *B_j = B.GetJ();
const double *B_data = B.GetData();
int * marker = new int[ncols];
std::fill(marker, marker+ncols, -1);
int num_nonzeros = 0, jcol;
C_i[0] = 0;
for (int ic = 0; ic < nrows; ic++)
{
for (int ia = A_i[ic]; ia < A_i[ic+1]; ia++)
{
jcol = A_j[ia];
marker[jcol] = ic;
num_nonzeros++;
}
for (int ib = B_i[ic]; ib < B_i[ic+1]; ib++)
{
jcol = B_j[ib];
if (marker[jcol] != ic)
{
marker[jcol] = ic;
num_nonzeros++;
}
}
C_i[ic+1] = num_nonzeros;
}
C_j = Memory<int>(num_nonzeros);
C_data = Memory<double>(num_nonzeros);
for (int ia = 0; ia < ncols; ia++)
{
marker[ia] = -1;
}
int pos = 0;
for (int ic = 0; ic < nrows; ic++)
{
for (int ia = A_i[ic]; ia < A_i[ic+1]; ia++)
{
jcol = A_j[ia];
C_j[pos] = jcol;
C_data[pos] = a*A_data[ia];
marker[jcol] = pos;
pos++;
}
for (int ib = B_i[ic]; ib < B_i[ic+1]; ib++)
{
jcol = B_j[ib];
if (marker[jcol] < C_i[ic])
{
C_j[pos] = jcol;
C_data[pos] = b*B_data[ib];
marker[jcol] = pos;
pos++;
}
else
{
C_data[marker[jcol]] += b*B_data[ib];
}
}
}
delete[] marker;
return new SparseMatrix(C_i, C_j, C_data, nrows, ncols);
}
SparseMatrix * Add(const SparseMatrix & A, const SparseMatrix & B)
{
return Add(1.,A,1.,B);
}
SparseMatrix * Add(Array<SparseMatrix *> & Ai)
{
MFEM_ASSERT(Ai.Size() > 0, "invalid size Ai.Size() = " << Ai.Size());
SparseMatrix * accumulate = Ai[0];
SparseMatrix * result = accumulate;
for (int i=1; i < Ai.Size(); ++i)
{
result = Add(*accumulate, *Ai[i]);
if (i != 1)
{
delete accumulate;
}
accumulate = result;
}
return result;
}
/// B += alpha * A
void Add(const SparseMatrix &A,
double alpha, DenseMatrix &B)
{
for (int r = 0; r < B.Height(); r++)
{
const int * colA = A.GetRowColumns(r);
const double * valA = A.GetRowEntries(r);
for (int i=0; i<A.RowSize(r); i++)
{
B(r, colA[i]) += alpha * valA[i];
}
}
}
/// Produces a block matrix with blocks A_{ij}*B
DenseMatrix *OuterProduct(const DenseMatrix &A, const DenseMatrix &B)
{
int mA = A.Height(), nA = A.Width();
int mB = B.Height(), nB = B.Width();
DenseMatrix *C = new DenseMatrix(mA * mB, nA * nB);
*C = 0.0;
for (int i=0; i<mA; i++)
{
for (int j=0; j<nA; j++)
{
C->AddMatrix(A(i,j), B, i * mB, j * nB);
}
}
return C;
}
/// Produces a block matrix with blocks A_{ij}*B
SparseMatrix *OuterProduct(const DenseMatrix &A, const SparseMatrix &B)
{
int mA = A.Height(), nA = A.Width();
int mB = B.Height(), nB = B.Width();
SparseMatrix *C = new SparseMatrix(mA * mB, nA * nB);
for (int i=0; i<mA; i++)
{
for (int j=0; j<nA; j++)
{
for (int r=0; r<mB; r++)
{
const int * colB = B.GetRowColumns(r);
const double * valB = B.GetRowEntries(r);
for (int cj=0; cj<B.RowSize(r); cj++)
{
C->Set(i * mB + r, j * nB + colB[cj], A(i,j) * valB[cj]);
}
}
}
}
C->Finalize();
return C;
}
/// Produces a block matrix with blocks A_{ij}*B
SparseMatrix *OuterProduct(const SparseMatrix &A, const DenseMatrix &B)
{
int mA = A.Height(), nA = A.Width();
int mB = B.Height(), nB = B.Width();
SparseMatrix *C = new SparseMatrix(mA * mB, nA * nB);
for (int r=0; r<mA; r++)
{
const int * colA = A.GetRowColumns(r);
const double * valA = A.GetRowEntries(r);
for (int aj=0; aj<A.RowSize(r); aj++)
{
for (int i=0; i<mB; i++)
{
for (int j=0; j<nB; j++)
{
C->Set(r * mB + i, colA[aj] * nB + j, valA[aj] * B(i, j));
}
}
}
}
C->Finalize();
return C;
}
/// Produces a block matrix with blocks A_{ij}*B
SparseMatrix *OuterProduct(const SparseMatrix &A, const SparseMatrix &B)
{
int mA = A.Height(), nA = A.Width();
int mB = B.Height(), nB = B.Width();
SparseMatrix *C = new SparseMatrix(mA * mB, nA * nB);
for (int ar=0; ar<mA; ar++)
{
const int * colA = A.GetRowColumns(ar);
const double * valA = A.GetRowEntries(ar);
for (int aj=0; aj<A.RowSize(ar); aj++)
{
for (int br=0; br<mB; br++)
{
const int * colB = B.GetRowColumns(br);
const double * valB = B.GetRowEntries(br);
for (int bj=0; bj<B.RowSize(br); bj++)
{
C->Set(ar * mB + br, colA[aj] * nB + colB[bj],
valA[aj] * valB[bj]);
}
}
}
}
C->Finalize();
return C;
}
void SparseMatrix::Swap(SparseMatrix &other)
{
mfem::Swap(width, other.width);
mfem::Swap(height, other.height);
mfem::Swap(I, other.I);
mfem::Swap(J, other.J);
mfem::Swap(A, other.A);
mfem::Swap(Rows, other.Rows);
mfem::Swap(current_row, other.current_row);
mfem::Swap(ColPtrJ, other.ColPtrJ);
mfem::Swap(ColPtrNode, other.ColPtrNode);
mfem::Swap(At, other.At);
#ifdef MFEM_USE_MEMALLOC
mfem::Swap(NodesMem, other.NodesMem);
#endif
mfem::Swap(isSorted, other.isSorted);
}
}