Files
mfem/linalg/sparsemat.hpp
T
2020-03-09 20:01:11 -07:00

803 lines
29 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.
#ifndef MFEM_SPARSEMAT_HPP
#define MFEM_SPARSEMAT_HPP
// Data types for sparse matrix
#include "../general/mem_alloc.hpp"
#include "../general/mem_manager.hpp"
#include "../general/device.hpp"
#include "../general/table.hpp"
#include "../general/globals.hpp"
#include "densemat.hpp"
namespace mfem
{
class
#if defined(__alignas_is_defined)
alignas(double)
#endif
RowNode
{
public:
double Value;
RowNode *Prev;
int Column;
};
/// Data type sparse matrix
class SparseMatrix : public AbstractSparseMatrix
{
protected:
/// @name Arrays used by the CSR storage format.
/** */
///@{
/// @brief %Array with size (#height+1) containing the row offsets.
/** The data for row r, 0 <= r < height, is at offsets j, I[r] <= j < I[r+1].
The offsets, j, are indices in the #J and #A arrays. The first entry in
this array is always zero, I[0] = 0, and the last entry, I[height], gives
the total number of entries stored (at a minimum, all nonzeros must be
represented) in the sparse matrix. */
Memory<int> I;
/** @brief %Array with size #I[#height], containing the column indices for
all matrix entries, as indexed by the #I array. */
Memory<int> J;
/** @brief %Array with size #I[#height], containing the actual entries of the
sparse matrix, as indexed by the #I array. */
Memory<double> A;
///@}
/** @brief %Array of linked lists, one for every row. This array represents
the linked list (LIL) storage format. */
RowNode **Rows;
mutable int current_row;
mutable int* ColPtrJ;
mutable RowNode ** ColPtrNode;
/// Transpose of A. Owned. Used to perform MultTranspose() on devices.
mutable SparseMatrix *At;
#ifdef MFEM_USE_MEMALLOC
typedef MemAlloc <RowNode, 1024> RowNodeAlloc;
RowNodeAlloc * NodesMem;
#endif
/// Are the columns sorted already.
bool isSorted;
void Destroy(); // Delete all owned data
void SetEmpty(); // Init all entries with empty values
public:
/// Create an empty SparseMatrix.
SparseMatrix() { SetEmpty(); }
/** @brief Create a sparse matrix with flexible sparsity structure using a
row-wise linked list (LIL) format. */
/** New entries are added as needed by methods like AddSubMatrix(),
SetSubMatrix(), etc. Calling Finalize() will convert the SparseMatrix to
the more compact compressed sparse row (CSR) format. */
explicit SparseMatrix(int nrows, int ncols = -1);
/** @brief Create a sparse matrix in CSR format. Ownership of @a i, @a j, and
@a data is transferred to the SparseMatrix. */
SparseMatrix(int *i, int *j, double *data, int m, int n);
/** @brief Create a sparse matrix in CSR format. Ownership of @a i, @a j, and
@a data is optionally transferred to the SparseMatrix. */
/** If the parameter @a data is NULL, then the internal #A array is allocated
by this constructor (initializing it with zeros and taking ownership,
regardless of the parameter @a owna). */
SparseMatrix(int *i, int *j, double *data, int m, int n, bool ownij,
bool owna, bool issorted);
/** @brief Create a sparse matrix in CSR format where each row has space
allocated for exactly @a rowsize entries. */
/** SetRow() can then be called or the #I, #J, #A arrays can be used
directly. */
SparseMatrix(int nrows, int ncols, int rowsize);
/// Copy constructor (deep copy).
/** If @a mat is finalized and @a copy_graph is false, the #I and #J arrays
will use a shallow copy (copy the pointers only) without transferring
ownership. */
SparseMatrix(const SparseMatrix &mat, bool copy_graph = true);
/// Create a SparseMatrix with diagonal @a v, i.e. A = Diag(v)
SparseMatrix(const Vector & v);
/// Assignment operator: deep copy
SparseMatrix& operator=(const SparseMatrix &rhs);
/** @brief Clear the contents of the SparseMatrix and make it a reference to
@a master */
/** After this call, the matrix will point to the same data as @a master but
it will not own its data. The @a master must be finalized. */
void MakeRef(const SparseMatrix &master);
/// For backward compatibility, define Size() to be synonym of Height().
int Size() const { return Height(); }
/// Clear the contents of the SparseMatrix.
void Clear() { Destroy(); SetEmpty(); }
/// Check if the SparseMatrix is empty.
bool Empty() const { return (A == NULL) && (Rows == NULL); }
/// Return the array #I.
inline int *GetI() { return I; }
/// Return the array #I, const version.
inline const int *GetI() const { return I; }
/// Return the array #J.
inline int *GetJ() { return J; }
/// Return the array #J, const version.
inline const int *GetJ() const { return J; }
/// Return the element data, i.e. the array #A.
inline double *GetData() { return A; }
/// Return the element data, i.e. the array #A, const version.
inline const double *GetData() const { return A; }
// Memory access methods for the #I array.
Memory<int> &GetMemoryI() { return I; }
const Memory<int> &GetMemoryI() const { return I; }
const int *ReadI(bool on_dev = true) const
{ return mfem::Read(I, Height()+1, on_dev); }
int *WriteI(bool on_dev = true)
{ return mfem::Write(I, Height()+1, on_dev); }
int *ReadWriteI(bool on_dev = true)
{ return mfem::ReadWrite(I, Height()+1, on_dev); }
const int *HostReadI() const
{ return mfem::Read(I, Height()+1, false); }
int *HostWriteI()
{ return mfem::Write(I, Height()+1, false); }
int *HostReadWriteI()
{ return mfem::ReadWrite(I, Height()+1, false); }
// Memory access methods for the #J array.
Memory<int> &GetMemoryJ() { return J; }
const Memory<int> &GetMemoryJ() const { return J; }
const int *ReadJ(bool on_dev = true) const
{ return mfem::Read(J, J.Capacity(), on_dev); }
int *WriteJ(bool on_dev = true)
{ return mfem::Write(J, J.Capacity(), on_dev); }
int *ReadWriteJ(bool on_dev = true)
{ return mfem::ReadWrite(J, J.Capacity(), on_dev); }
const int *HostReadJ() const
{ return mfem::Read(J, J.Capacity(), false); }
int *HostWriteJ()
{ return mfem::Write(J, J.Capacity(), false); }
int *HostReadWriteJ()
{ return mfem::ReadWrite(J, J.Capacity(), false); }
// Memory access methods for the #A array.
Memory<double> &GetMemoryData() { return A; }
const Memory<double> &GetMemoryData() const { return A; }
const double *ReadData(bool on_dev = true) const
{ return mfem::Read(A, A.Capacity(), on_dev); }
double *WriteData(bool on_dev = true)
{ return mfem::Write(A, A.Capacity(), on_dev); }
double *ReadWriteData(bool on_dev = true)
{ return mfem::ReadWrite(A, A.Capacity(), on_dev); }
const double *HostReadData() const
{ return mfem::Read(A, A.Capacity(), false); }
double *HostWriteData()
{ return mfem::Write(A, A.Capacity(), false); }
double *HostReadWriteData()
{ return mfem::ReadWrite(A, A.Capacity(), false); }
/// Returns the number of elements in row @a i.
int RowSize(const int i) const;
/// Returns the maximum number of elements among all rows.
int MaxRowSize() const;
/// Return a pointer to the column indices in a row.
int *GetRowColumns(const int row);
/// Return a pointer to the column indices in a row, const version.
const int *GetRowColumns(const int row) const;
/// Return a pointer to the entries in a row.
double *GetRowEntries(const int row);
/// Return a pointer to the entries in a row, const version.
const double *GetRowEntries(const int row) const;
/// Change the width of a SparseMatrix.
/*!
* If width_ = -1 (DEFAULT), this routine will set the new width
* to the actual Width of the matrix awidth = max(J) + 1.
* Values 0 <= width_ < awidth are not allowed (error check in Debug Mode only)
*
* This method can be called for matrices finalized or not.
*/
void SetWidth(int width_ = -1);
/// Returns the actual Width of the matrix.
/*! This method can be called for matrices finalized or not. */
int ActualWidth() const;
/// Sort the column indices corresponding to each row.
void SortColumnIndices();
/** @brief Move the diagonal entry to the first position in each row,
preserving the order of the rest of the columns. */
void MoveDiagonalFirst();
/// Returns reference to a_{ij}.
virtual double &Elem(int i, int j);
/// Returns constant reference to a_{ij}.
virtual const double &Elem(int i, int j) const;
/// Returns reference to A[i][j].
double &operator()(int i, int j);
/// Returns reference to A[i][j].
const double &operator()(int i, int j) const;
/// Returns the Diagonal of A
void GetDiag(Vector & d) const;
/// Produces a DenseMatrix from a SparseMatrix
DenseMatrix *ToDenseMatrix() const;
/// Produces a DenseMatrix from a SparseMatrix
void ToDenseMatrix(DenseMatrix & B) const;
virtual MemoryClass GetMemoryClass() const
{
return Finalized() ?
Device::GetDeviceMemoryClass() : Device::GetHostMemoryClass();
}
/// Matrix vector multiplication.
virtual void Mult(const Vector &x, Vector &y) const;
/// y += A * x (default) or y += a * A * x
void AddMult(const Vector &x, Vector &y, const double a = 1.0) const;
/// Multiply a vector with the transposed matrix. y = At * x
void MultTranspose(const Vector &x, Vector &y) const;
/// y += At * x (default) or y += a * At * x
void AddMultTranspose(const Vector &x, Vector &y,
const double a = 1.0) const;
/** @brief Build and store internally the transpose of this matrix which will
be used in the methods AddMultTranspose() and MultTranspose(). */
/** If this method has been called, the internal transpose matrix will be
used to perform the action of the transpose matrix in AddMultTranspose(),
and MultTranspose().
Warning: any changes in this matrix will invalidate the internal
transpose. To rebuild the transpose, call ResetTranspose() followed by a
call to this method. If the internal transpose is already built, this
method has no effect.
When any non-default backend is enabled, i.e. Device::IsEnabled() is
true, the methods AddMultTranspose(), and MultTranspose(), require the
internal transpose to be built. If that is not the case (i.e. the
internal transpose is not built), these methods will raise an error with
an appropriate message pointing to this method. When using the default
backend, calling this method is optional.
This method can only be used when the sparse matrix is finalized. */
void BuildTranspose() const;
/** Reset (destroy) the internal transpose matrix. See BuildTranspose() for
more details. */
void ResetTranspose() const;
void PartMult(const Array<int> &rows, const Vector &x, Vector &y) const;
void PartAddMult(const Array<int> &rows, const Vector &x, Vector &y,
const double a=1.0) const;
/// y = A * x, treating all entries as booleans (zero=false, nonzero=true).
/** The actual values stored in the data array, #A, are not used - this means
and that all entries in the sparsity pattern are considered to be true by
this method. */
void BooleanMult(const Array<int> &x, Array<int> &y) const;
/// y = At * x, treating all entries as booleans (zero=false, nonzero=true).
/** The actual values stored in the data array, #A, are not used - this means
and that all entries in the sparsity pattern are considered to be true by
this method. */
void BooleanMultTranspose(const Array<int> &x, Array<int> &y) const;
/// Compute y^t A x
double InnerProduct(const Vector &x, const Vector &y) const;
/// For all i compute \f$ x_i = \sum_j A_{ij} \f$
void GetRowSums(Vector &x) const;
/// For i = irow compute \f$ x_i = \sum_j | A_{i, j} | \f$
double GetRowNorml1(int irow) const;
/// This virtual method is not supported: it always returns NULL.
virtual MatrixInverse *Inverse() const;
/// Eliminates a column from the transpose matrix.
void EliminateRow(int row, const double sol, Vector &rhs);
/// Eliminates a row from the matrix.
/*!
* - If @a dpolicy = #DIAG_ZERO, all the entries in the row will be set to 0.
* - If @a dpolicy = #DIAG_ONE (matrix must be square), the diagonal entry
* will be set equal to 1 and all other entries in the row to 0.
* - The policy #DIAG_KEEP is not supported.
*/
void EliminateRow(int row, DiagonalPolicy dpolicy = DIAG_ZERO);
/// Eliminates the column @a col from the matrix.
/** - If @a dpolicy = #DIAG_ZERO, all entries in the column will be set to 0.
- If @a dpolicy = #DIAG_ONE (matrix must be square), the diagonal entry
will be set equal to 1 and all other entries in the column to 0.
- The policy #DIAG_KEEP is not supported. */
void EliminateCol(int col, DiagonalPolicy dpolicy = DIAG_ZERO);
/// Eliminate all columns i for which @a cols[i] != 0.
/** Elimination of a column means that all entries in the column are set to
zero. In addition, if the pointers @a x and @a b are not NULL, the
eliminated matrix entries are multiplied by the corresponding solution
value in @a *x and subtracted from the r.h.s. vector, @a *b. */
void EliminateCols(const Array<int> &cols, const Vector *x = NULL,
Vector *b = NULL);
/** @brief Similar to EliminateCols + save the eliminated entries into
@a Ae so that (*this) + Ae is equal to the original matrix. */
void EliminateCols(const Array<int> &col_marker, SparseMatrix &Ae);
/// Eliminate row @a rc and column @a rc and modify the @a rhs using @a sol.
/** Eliminates the column @a rc to the @a rhs, deletes the row @a rc and
replaces the element (rc,rc) with 1.0; assumes that element (i,rc)
is assembled if and only if the element (rc,i) is assembled.
By default, elements (rc,rc) are set to 1.0, although this behavior
can be adjusted by changing the @a dpolicy parameter. */
void EliminateRowCol(int rc, const double sol, Vector &rhs,
DiagonalPolicy dpolicy = DIAG_ONE);
/** @brief Similar to
EliminateRowCol(int, const double, Vector &, DiagonalPolicy), but
multiple values for eliminated unknowns are accepted, and accordingly
multiple right-hand-sides are used. */
void EliminateRowColMultipleRHS(int rc, const Vector &sol,
DenseMatrix &rhs,
DiagonalPolicy dpolicy = DIAG_ONE);
/// Perform elimination and set the diagonal entry to the given value
void EliminateRowColDiag(int rc, double value);
/// Eliminate row @a rc and column @a rc.
void EliminateRowCol(int rc, DiagonalPolicy dpolicy = DIAG_ONE);
/** @brief Similar to EliminateRowCol(int, DiagonalPolicy) + save the
eliminated entries into @a Ae so that (*this) + Ae is equal to the
original matrix */
void EliminateRowCol(int rc, SparseMatrix &Ae,
DiagonalPolicy dpolicy = DIAG_ONE);
/// If a row contains only one diag entry of zero, set it to 1.
void SetDiagIdentity();
/// If a row contains only zeros, set its diagonal to 1.
virtual void EliminateZeroRows(const double threshold = 1e-12);
/// Gauss-Seidel forward and backward iterations over a vector x.
void Gauss_Seidel_forw(const Vector &x, Vector &y) const;
void Gauss_Seidel_back(const Vector &x, Vector &y) const;
/// Determine appropriate scaling for Jacobi iteration
double GetJacobiScaling() const;
/** One scaled Jacobi iteration for the system A x = b.
x1 = x0 + sc D^{-1} (b - A x0) where D is the diag of A. */
void Jacobi(const Vector &b, const Vector &x0, Vector &x1, double sc) const;
void DiagScale(const Vector &b, Vector &x, double sc = 1.0) const;
/** x1 = x0 + sc D^{-1} (b - A x0) where \f$ D_{ii} = \sum_j |A_{ij}| \f$. */
void Jacobi2(const Vector &b, const Vector &x0, Vector &x1,
double sc = 1.0) const;
/** x1 = x0 + sc D^{-1} (b - A x0) where \f$ D_{ii} = \sum_j A_{ij} \f$. */
void Jacobi3(const Vector &b, const Vector &x0, Vector &x1,
double sc = 1.0) const;
/** @brief Finalize the matrix initialization, switching the storage format
from LIL to CSR. */
/** This method should be called once, after the matrix has been initialized.
Internally, this method converts the matrix from row-wise linked list
(LIL) format into CSR (compressed sparse row) format. */
virtual void Finalize(int skip_zeros = 1) { Finalize(skip_zeros, false); }
/// A slightly more general version of the Finalize(int) method.
void Finalize(int skip_zeros, bool fix_empty_rows);
/// Returns whether or not CSR format has been finalized.
bool Finalized() const { return !A.Empty(); }
/// Returns whether or not the columns are sorted.
bool ColumnsAreSorted() const { return isSorted; }
/** @brief Remove entries smaller in absolute value than a given tolerance
@a tol. If @a fix_empty_rows is true, a zero value is inserted in the
diagonal entry (for square matrices only) */
void Threshold(double tol, bool fix_empty_rows = false);
/** Split the matrix into M x N blocks of sparse matrices in CSR format.
The 'blocks' array is M x N (i.e. M and N are determined by its
dimensions) and its entries are overwritten by the new blocks. */
void GetBlocks(Array2D<SparseMatrix *> &blocks) const;
void GetSubMatrix(const Array<int> &rows, const Array<int> &cols,
DenseMatrix &subm) const;
/** @brief Initialize the SparseMatrix for fast access to the entries of the
given @a row which becomes the "current row". */
/** Fast access to the entries of the "current row" can be performed using
the methods: SearchRow(const int), _Add_(const int, const double),
_Set_(const int, const double), and _Get_(const int). */
inline void SetColPtr(const int row) const;
/** @brief Reset the "current row" set by calling SetColPtr(). This method
must be called between any two calls to SetColPtr(). */
inline void ClearColPtr() const;
/// Perform a fast search for an entry in the "current row". See SetColPtr().
/** If the matrix is not finalized and the entry is not found in the
SparseMatrix, it will be added to the sparsity pattern initialized with
zero. If the matrix is finalized and the entry is not found, an error
will be generated. */
inline double &SearchRow(const int col);
/// Add a value to an entry in the "current row". See SetColPtr().
inline void _Add_(const int col, const double a)
{ SearchRow(col) += a; }
/// Set an entry in the "current row". See SetColPtr().
inline void _Set_(const int col, const double a)
{ SearchRow(col) = a; }
/// Read the value of an entry in the "current row". See SetColPtr().
inline double _Get_(const int col) const;
inline double &SearchRow(const int row, const int col);
inline void _Add_(const int row, const int col, const double a)
{ SearchRow(row, col) += a; }
inline void _Set_(const int row, const int col, const double a)
{ SearchRow(row, col) = a; }
void Set(const int i, const int j, const double a);
void Add(const int i, const int j, const double a);
void SetSubMatrix(const Array<int> &rows, const Array<int> &cols,
const DenseMatrix &subm, int skip_zeros = 1);
void SetSubMatrixTranspose(const Array<int> &rows, const Array<int> &cols,
const DenseMatrix &subm, int skip_zeros = 1);
void AddSubMatrix(const Array<int> &rows, const Array<int> &cols,
const DenseMatrix &subm, int skip_zeros = 1);
bool RowIsEmpty(const int row) const;
/// Extract all column indices and values from a given row.
/** If the matrix is finalized (i.e. in CSR format), @a cols and @a srow will
simply be references to the specific portion of the #J and #A arrays.
As required by the AbstractSparseMatrix interface this method returns:
- 0, if @a cols and @a srow are copies of the values in the matrix, i.e.
when the matrix is open.
- 1, if @a cols and @a srow are views of the values in the matrix, i.e.
when the matrix is finalized.
@warning This method breaks the const-ness when the matrix is finalized
because it gives write access to the #J and #A arrays. */
virtual int GetRow(const int row, Array<int> &cols, Vector &srow) const;
void SetRow(const int row, const Array<int> &cols, const Vector &srow);
void AddRow(const int row, const Array<int> &cols, const Vector &srow);
void ScaleRow(const int row, const double scale);
/// this = diag(sl) * this;
void ScaleRows(const Vector & sl);
/// this = this * diag(sr);
void ScaleColumns(const Vector & sr);
/** @brief Add the sparse matrix 'B' to '*this'. This operation will cause an
error if '*this' is finalized and 'B' has larger sparsity pattern. */
SparseMatrix &operator+=(const SparseMatrix &B);
/** @brief Add the sparse matrix 'B' scaled by the scalar 'a' into '*this'.
Only entries in the sparsity pattern of '*this' are added. */
void Add(const double a, const SparseMatrix &B);
SparseMatrix &operator=(double a);
SparseMatrix &operator*=(double a);
/// Prints matrix to stream out.
void Print(std::ostream &out = mfem::out, int width_ = 4) const;
/// Prints matrix in matlab format.
void PrintMatlab(std::ostream &out = mfem::out) const;
/// Prints matrix in Matrix Market sparse format.
void PrintMM(std::ostream &out = mfem::out) const;
/// Prints matrix to stream out in hypre_CSRMatrix format.
void PrintCSR(std::ostream &out) const;
/// Prints a sparse matrix to stream out in CSR format.
void PrintCSR2(std::ostream &out) const;
/// Print various sparse matrix statistics.
void PrintInfo(std::ostream &out) const;
/// Returns max_{i,j} |(i,j)-(j,i)| for a finalized matrix
double IsSymmetric() const;
/// (*this) = 1/2 ((*this) + (*this)^t)
void Symmetrize();
/// Returns the number of the nonzero elements in the matrix
virtual int NumNonZeroElems() const;
double MaxNorm() const;
/// Count the number of entries with |a_ij| <= tol.
int CountSmallElems(double tol) const;
/// Count the number of entries that are NOT finite, i.e. Inf or Nan.
int CheckFinite() const;
/// Set the graph ownership flag (I and J arrays).
void SetGraphOwner(bool ownij)
{ I.SetHostPtrOwner(ownij); J.SetHostPtrOwner(ownij); }
/// Set the data ownership flag (A array).
void SetDataOwner(bool owna) { A.SetHostPtrOwner(owna); }
/// Get the graph ownership flag (I and J arrays).
bool OwnsGraph() const { return I.OwnsHostPtr() && J.OwnsHostPtr(); }
/// Get the data ownership flag (A array).
bool OwnsData() const { return A.OwnsHostPtr(); }
/// Lose the ownership of the graph (I, J) and data (A) arrays.
void LoseData() { SetGraphOwner(false); SetDataOwner(false); }
void Swap(SparseMatrix &other);
/// Destroys sparse matrix.
virtual ~SparseMatrix() { Destroy(); }
Type GetType() const { return MFEM_SPARSEMAT; }
};
/// Applies f() to each element of the matrix (after it is finalized).
void SparseMatrixFunction(SparseMatrix &S, double (*f)(double));
/// Transpose of a sparse matrix. A must be finalized.
SparseMatrix *Transpose(const SparseMatrix &A);
/// Transpose of a sparse matrix. A does not need to be a CSR matrix.
SparseMatrix *TransposeAbstractSparseMatrix (const AbstractSparseMatrix &A,
int useActualWidth);
/// Matrix product A.B.
/** If @a OAB is not NULL, we assume it has the structure of A.B and store the
result in @a OAB. If @a OAB is NULL, we create a new SparseMatrix to store
the result and return a pointer to it.
All matrices must be finalized. */
SparseMatrix *Mult(const SparseMatrix &A, const SparseMatrix &B,
SparseMatrix *OAB = NULL);
/// C = A^T B
SparseMatrix *TransposeMult(const SparseMatrix &A, const SparseMatrix &B);
/// Matrix product of sparse matrices. A and B do not need to be CSR matrices
SparseMatrix *MultAbstractSparseMatrix (const AbstractSparseMatrix &A,
const AbstractSparseMatrix &B);
/// Matrix product A.B
DenseMatrix *Mult(const SparseMatrix &A, DenseMatrix &B);
/// RAP matrix product (with R=P^T)
DenseMatrix *RAP(const SparseMatrix &A, DenseMatrix &P);
/// RAP matrix product (with R=P^T)
DenseMatrix *RAP(DenseMatrix &A, const SparseMatrix &P);
/** RAP matrix product (with P=R^T). ORAP is like OAB above.
All matrices must be finalized. */
SparseMatrix *RAP(const SparseMatrix &A, const SparseMatrix &R,
SparseMatrix *ORAP = NULL);
/// General RAP with given R^T, A and P
SparseMatrix *RAP(const SparseMatrix &Rt, const SparseMatrix &A,
const SparseMatrix &P);
/// Matrix multiplication A^t D A. All matrices must be finalized.
SparseMatrix *Mult_AtDA(const SparseMatrix &A, const Vector &D,
SparseMatrix *OAtDA = NULL);
/// Matrix addition result = A + B.
SparseMatrix * Add(const SparseMatrix & A, const SparseMatrix & B);
/// Matrix addition result = a*A + b*B
SparseMatrix * Add(double a, const SparseMatrix & A, double b,
const SparseMatrix & B);
/// Matrix addition result = sum_i A_i
SparseMatrix * Add(Array<SparseMatrix *> & Ai);
/// B += alpha * A
void Add(const SparseMatrix &A, double alpha, DenseMatrix &B);
/// Produces a block matrix with blocks A_{ij}*B
DenseMatrix *OuterProduct(const DenseMatrix &A, const DenseMatrix &B);
/// Produces a block matrix with blocks A_{ij}*B
SparseMatrix *OuterProduct(const DenseMatrix &A, const SparseMatrix &B);
/// Produces a block matrix with blocks A_{ij}*B
SparseMatrix *OuterProduct(const SparseMatrix &A, const DenseMatrix &B);
/// Produces a block matrix with blocks A_{ij}*B
SparseMatrix *OuterProduct(const SparseMatrix &A, const SparseMatrix &B);
// Inline methods
inline void SparseMatrix::SetColPtr(const int row) const
{
if (Rows)
{
if (ColPtrNode == NULL)
{
ColPtrNode = new RowNode *[width];
for (int i = 0; i < width; i++)
{
ColPtrNode[i] = NULL;
}
}
for (RowNode *node_p = Rows[row]; node_p != NULL; node_p = node_p->Prev)
{
ColPtrNode[node_p->Column] = node_p;
}
}
else
{
if (ColPtrJ == NULL)
{
ColPtrJ = new int[width];
for (int i = 0; i < width; i++)
{
ColPtrJ[i] = -1;
}
}
for (int j = I[row], end = I[row+1]; j < end; j++)
{
ColPtrJ[J[j]] = j;
}
}
current_row = row;
}
inline void SparseMatrix::ClearColPtr() const
{
if (Rows)
{
for (RowNode *node_p = Rows[current_row]; node_p != NULL;
node_p = node_p->Prev)
{
ColPtrNode[node_p->Column] = NULL;
}
}
else
{
for (int j = I[current_row], end = I[current_row+1]; j < end; j++)
{
ColPtrJ[J[j]] = -1;
}
}
}
inline double &SparseMatrix::SearchRow(const int col)
{
if (Rows)
{
RowNode *node_p = ColPtrNode[col];
if (node_p == NULL)
{
#ifdef MFEM_USE_MEMALLOC
node_p = NodesMem->Alloc();
#else
node_p = new RowNode;
#endif
node_p->Prev = Rows[current_row];
node_p->Column = col;
node_p->Value = 0.0;
Rows[current_row] = ColPtrNode[col] = node_p;
}
return node_p->Value;
}
else
{
const int j = ColPtrJ[col];
MFEM_VERIFY(j != -1, "Entry for column " << col << " is not allocated.");
return A[j];
}
}
inline double SparseMatrix::_Get_(const int col) const
{
if (Rows)
{
RowNode *node_p = ColPtrNode[col];
return (node_p == NULL) ? 0.0 : node_p->Value;
}
else
{
const int j = ColPtrJ[col];
return (j == -1) ? 0.0 : A[j];
}
}
inline double &SparseMatrix::SearchRow(const int row, const int col)
{
if (Rows)
{
RowNode *node_p;
for (node_p = Rows[row]; 1; node_p = node_p->Prev)
{
if (node_p == NULL)
{
#ifdef MFEM_USE_MEMALLOC
node_p = NodesMem->Alloc();
#else
node_p = new RowNode;
#endif
node_p->Prev = Rows[row];
node_p->Column = col;
node_p->Value = 0.0;
Rows[row] = node_p;
break;
}
else if (node_p->Column == col)
{
break;
}
}
return node_p->Value;
}
else
{
int *Ip = I+row, *Jp = J;
for (int k = Ip[0], end = Ip[1]; k < end; k++)
{
if (Jp[k] == col)
{
return A[k];
}
}
MFEM_ABORT("Could not find entry for row = " << row << ", col = " << col);
}
return A[0];
}
/// Specialization of the template function Swap<> for class SparseMatrix
template<> inline void Swap<SparseMatrix>(SparseMatrix &a, SparseMatrix &b)
{
a.Swap(b);
}
} // namespace mfem
#endif