and transfers. The Memory class is now used by some MFEM classes (like Array and Vector) which can be used on the Device. Such classes now provide methods to access the underlying Memory object, e.g. GetMemory. Updated ex1/ex1p and ex6/ex6p to not need to enable/disable the Device at specific points -- the Device is now enabled just at the start. Also, the same examples can now run on Device (e.g. -d cuda) without the partial assembly option (-pa) -- full assembly will be still done on CPU but the sparse matrix action and vector operations will be done using the Device. Reverted changes in class DenseMatrix related to using the Device. At this point, DenseMatrix operations are only used for small matrices and using the Device in this case is not a good option.
636 lines
17 KiB
C++
636 lines
17 KiB
C++
// Copyright (c) 2010, Lawrence Livermore National Security, LLC. Produced at
|
|
// the Lawrence Livermore National Laboratory. LLNL-CODE-443211. All Rights
|
|
// reserved. See file COPYRIGHT for details.
|
|
//
|
|
// This file is part of the MFEM library. For more information and source code
|
|
// availability see http://mfem.org.
|
|
//
|
|
// MFEM is free software; you can redistribute it and/or modify it under the
|
|
// terms of the GNU Lesser General Public License (as published by the Free
|
|
// Software Foundation) version 2.1 dated February 1999.
|
|
|
|
#include "../general/array.hpp"
|
|
#include "../general/globals.hpp"
|
|
#include "matrix.hpp"
|
|
#include "sparsemat.hpp"
|
|
#include "blockvector.hpp"
|
|
#include "blockmatrix.hpp"
|
|
|
|
namespace mfem
|
|
{
|
|
|
|
BlockMatrix::BlockMatrix(const Array<int> & offsets):
|
|
AbstractSparseMatrix(offsets.Last()),
|
|
owns_blocks(false),
|
|
nRowBlocks(offsets.Size()-1),
|
|
nColBlocks(offsets.Size()-1),
|
|
row_offsets(const_cast< Array<int>& >(offsets).GetData(), offsets.Size()),
|
|
col_offsets(const_cast< Array<int>& >(offsets).GetData(), offsets.Size()),
|
|
Aij(nRowBlocks, nColBlocks)
|
|
{
|
|
Aij = (SparseMatrix *)NULL;
|
|
}
|
|
|
|
BlockMatrix::BlockMatrix(const Array<int> & row_offsets_,
|
|
const Array<int> & col_offsets_):
|
|
AbstractSparseMatrix(row_offsets_.Last(), col_offsets_.Last()),
|
|
owns_blocks(false),
|
|
nRowBlocks(row_offsets_.Size()-1),
|
|
nColBlocks(col_offsets_.Size()-1),
|
|
row_offsets(const_cast< Array<int>& >(row_offsets_).GetData(),
|
|
row_offsets_.Size()),
|
|
col_offsets(const_cast< Array<int>& >(col_offsets_).GetData(),
|
|
col_offsets_.Size()),
|
|
Aij(nRowBlocks, nColBlocks)
|
|
{
|
|
Aij = (SparseMatrix *)NULL;
|
|
}
|
|
|
|
|
|
BlockMatrix::~BlockMatrix()
|
|
{
|
|
if (owns_blocks)
|
|
{
|
|
for (SparseMatrix ** it = Aij.GetRow(0);
|
|
it != Aij.GetRow(0)+(Aij.NumRows()*Aij.NumCols()); ++it)
|
|
{
|
|
delete *it;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BlockMatrix::SetBlock(int i, int j, SparseMatrix * mat)
|
|
{
|
|
#ifdef MFEM_DEBUG
|
|
if (nRowBlocks <= i || nColBlocks <= j)
|
|
{
|
|
mfem_error("BlockMatrix::SetBlock #0");
|
|
}
|
|
|
|
if (mat->Height() != row_offsets[i+1] - row_offsets[i])
|
|
{
|
|
mfem_error("BlockMatrix::SetBlock #1");
|
|
}
|
|
|
|
if (mat->Width() != col_offsets[j+1] - col_offsets[j])
|
|
{
|
|
mfem_error("BlockMatrix::SetBlock #2");
|
|
}
|
|
#endif
|
|
Aij(i,j) = mat;
|
|
}
|
|
|
|
SparseMatrix & BlockMatrix::GetBlock(int i, int j)
|
|
{
|
|
#ifdef MFEM_DEBUG
|
|
if (nRowBlocks <= i || nColBlocks <= j)
|
|
{
|
|
mfem_error("BlockMatrix::Block #0");
|
|
}
|
|
|
|
if (IsZeroBlock(i,j))
|
|
{
|
|
mfem_error("BlockMatrix::Block #1");
|
|
}
|
|
#endif
|
|
return *Aij(i,j);
|
|
}
|
|
|
|
const SparseMatrix & BlockMatrix::GetBlock(int i, int j) const
|
|
{
|
|
#ifdef MFEM_DEBUG
|
|
if (nRowBlocks <= i || nColBlocks <= j)
|
|
{
|
|
mfem_error("BlockMatrix::Block const #0");
|
|
}
|
|
|
|
if (IsZeroBlock(i,j))
|
|
{
|
|
mfem_error("BlockMatrix::Block const #1");
|
|
}
|
|
#endif
|
|
|
|
return *Aij(i,j);
|
|
}
|
|
|
|
|
|
int BlockMatrix::NumNonZeroElems() const
|
|
{
|
|
int nnz_elem = 0;
|
|
for (int jcol = 0; jcol != nColBlocks; ++jcol)
|
|
{
|
|
for (int irow = 0; irow != nRowBlocks; ++irow)
|
|
{
|
|
if (Aij(irow,jcol))
|
|
{
|
|
nnz_elem+= Aij(irow,jcol)->NumNonZeroElems();
|
|
}
|
|
}
|
|
}
|
|
return nnz_elem;
|
|
}
|
|
|
|
|
|
double& BlockMatrix::Elem (int i, int j)
|
|
{
|
|
int iloc, iblock;
|
|
int jloc, jblock;
|
|
|
|
findGlobalRow(i, iblock, iloc);
|
|
findGlobalCol(j, jblock, jloc);
|
|
|
|
if (IsZeroBlock(i, j))
|
|
{
|
|
mfem_error("BlockMatrix::Elem");
|
|
}
|
|
|
|
return Aij(iblock, jblock)->Elem(iloc, jloc);
|
|
}
|
|
|
|
const double& BlockMatrix::Elem (int i, int j) const
|
|
{
|
|
int iloc, iblock;
|
|
int jloc, jblock;
|
|
|
|
findGlobalRow(i, iblock, iloc);
|
|
findGlobalCol(j, jblock, jloc);
|
|
|
|
if (IsZeroBlock(i, j))
|
|
{
|
|
mfem_error("BlockMatrix::Elem");
|
|
}
|
|
|
|
return Aij(iblock, jblock)->Elem(iloc, jloc);
|
|
}
|
|
|
|
int BlockMatrix::RowSize(const int i) const
|
|
{
|
|
int rowsize = 0;
|
|
|
|
int iblock, iloc;
|
|
findGlobalRow(i, iblock, iloc);
|
|
|
|
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
|
{
|
|
if (Aij(iblock,jblock) != NULL)
|
|
{
|
|
rowsize += Aij(iblock,jblock)->RowSize(iloc);
|
|
}
|
|
}
|
|
|
|
return rowsize;
|
|
}
|
|
|
|
int BlockMatrix::GetRow(const int row, Array<int> &cols, Vector &srow) const
|
|
{
|
|
int iblock, iloc, rowsize;
|
|
findGlobalRow(row, iblock, iloc);
|
|
rowsize = RowSize(row);
|
|
cols.SetSize(rowsize);
|
|
srow.SetSize(rowsize);
|
|
|
|
Array<int> bcols;
|
|
Vector bsrow;
|
|
|
|
int * it_cols = cols.GetData();
|
|
double *it_srow = srow.GetData();
|
|
|
|
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
|
{
|
|
if (Aij(iblock,jblock) != NULL)
|
|
{
|
|
Aij(iblock,jblock)->GetRow(iloc, bcols, bsrow);
|
|
for (int i = 0; i < bcols.Size(); ++i)
|
|
{
|
|
*(it_cols++) = bcols[i] + col_offsets[jblock];
|
|
*(it_srow++) = bsrow(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void BlockMatrix::EliminateRowCol(int rc, DiagonalPolicy dpolicy)
|
|
{
|
|
// Find the block to which the dof belongs and its local number
|
|
int idx, iiblock;
|
|
for (iiblock = 0; iiblock < nRowBlocks; ++iiblock)
|
|
{
|
|
idx = rc - row_offsets[iiblock];
|
|
if (idx < 0 ) { break; }
|
|
}
|
|
iiblock--;
|
|
idx = rc - row_offsets[iiblock];
|
|
|
|
// Asserts
|
|
MFEM_ASSERT(nRowBlocks == nColBlocks,
|
|
"BlockMatrix::EliminateRowCol: nRowBlocks != nColBlocks");
|
|
|
|
MFEM_ASSERT(row_offsets[iiblock] == col_offsets[iiblock],
|
|
"BlockMatrix::EliminateRowCol: row_offests["
|
|
<< iiblock << "] != col_offsets["<<iiblock<<"]");
|
|
|
|
MFEM_ASSERT(Aij(iiblock, iiblock),
|
|
"BlockMatrix::EliminateRowCol: Null diagonal block");
|
|
|
|
// Apply the constraint idx to the iiblock
|
|
for (int jjblock = 0; jjblock < nRowBlocks; ++jjblock)
|
|
{
|
|
if (iiblock == jjblock) { continue; }
|
|
if (Aij(iiblock,jjblock)) { Aij(iiblock,jjblock)->EliminateRow(idx); }
|
|
}
|
|
for (int jjblock = 0; jjblock < nRowBlocks; ++jjblock)
|
|
{
|
|
if (iiblock == jjblock) { continue; }
|
|
if (Aij(jjblock,iiblock)) { Aij(jjblock,iiblock)->EliminateCol(idx); }
|
|
}
|
|
Aij(iiblock, iiblock)->EliminateRowCol(idx,dpolicy);
|
|
}
|
|
|
|
void BlockMatrix::EliminateRowCol(Array<int> & ess_bc_dofs, Vector & sol,
|
|
Vector & rhs)
|
|
{
|
|
if (nRowBlocks != nColBlocks)
|
|
{
|
|
mfem_error("BlockMatrix::EliminateRowCol: nRowBlocks != nColBlocks");
|
|
}
|
|
|
|
for (int iiblock = 0; iiblock < nRowBlocks; ++iiblock)
|
|
{
|
|
if (row_offsets[iiblock] != col_offsets[iiblock])
|
|
{
|
|
mfem::out << "BlockMatrix::EliminateRowCol: row_offests["
|
|
<< iiblock << "] != col_offsets["<<iiblock<<"]\n";
|
|
mfem_error();
|
|
}
|
|
}
|
|
|
|
// We also have to do the same for each Aij
|
|
Array<int> block_dofs;
|
|
Vector block_sol, block_rhs;
|
|
|
|
for (int iiblock = 0; iiblock < nRowBlocks; ++iiblock)
|
|
{
|
|
int dsize = row_offsets[iiblock+1] - row_offsets[iiblock];
|
|
block_dofs.MakeRef(ess_bc_dofs.GetData()+row_offsets[iiblock], dsize);
|
|
block_sol.SetDataAndSize(sol.GetData()+row_offsets[iiblock], dsize);
|
|
block_rhs.SetDataAndSize(rhs.GetData()+row_offsets[iiblock], dsize);
|
|
|
|
if (Aij(iiblock, iiblock))
|
|
{
|
|
for (int i = 0; i < block_dofs.Size(); ++i)
|
|
{
|
|
if (block_dofs[i])
|
|
{
|
|
Aij(iiblock, iiblock)->EliminateRowCol(i,block_sol(i), block_rhs);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < block_dofs.Size(); ++i)
|
|
{
|
|
if (block_dofs[i])
|
|
{
|
|
mfem_error("BlockMatrix::EliminateRowCol: Null diagonal block \n");
|
|
}
|
|
}
|
|
}
|
|
|
|
for (int jjblock = 0; jjblock < nRowBlocks; ++jjblock)
|
|
{
|
|
if (jjblock != iiblock && Aij(iiblock, jjblock))
|
|
{
|
|
for (int i = 0; i < block_dofs.Size(); ++i)
|
|
{
|
|
if (block_dofs[i])
|
|
{
|
|
Aij(iiblock, jjblock)->EliminateRow(i);
|
|
}
|
|
}
|
|
}
|
|
if (jjblock != iiblock && Aij(jjblock, iiblock))
|
|
{
|
|
block_rhs.SetDataAndSize(rhs.GetData()+row_offsets[jjblock],
|
|
row_offsets[jjblock+1] - row_offsets[jjblock]);
|
|
Aij(jjblock, iiblock)->EliminateCols(block_dofs, &block_sol, &block_rhs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void BlockMatrix::EliminateZeroRows(const double threshold)
|
|
{
|
|
MFEM_VERIFY(nRowBlocks == nColBlocks, "not a square matrix");
|
|
|
|
for (int iblock = 0; iblock < nRowBlocks; ++iblock)
|
|
{
|
|
if (Aij(iblock,iblock))
|
|
{
|
|
double norm;
|
|
for (int i = 0; i < Aij(iblock, iblock)->NumRows(); ++i)
|
|
{
|
|
norm = 0.;
|
|
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
|
if (Aij(iblock,jblock))
|
|
{
|
|
norm += Aij(iblock,jblock)->GetRowNorml1(i);
|
|
}
|
|
|
|
if (norm <= threshold)
|
|
{
|
|
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
|
{
|
|
if (Aij(iblock,jblock))
|
|
{
|
|
Aij(iblock,jblock)->EliminateRow(
|
|
i, (iblock==jblock) ? DIAG_ONE : DIAG_ZERO);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
double norm;
|
|
for (int i = 0; i < row_offsets[iblock+1] - row_offsets[iblock]; ++i)
|
|
{
|
|
norm = 0.;
|
|
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
|
{
|
|
if (Aij(iblock,jblock))
|
|
{
|
|
norm += Aij(iblock,jblock)->GetRowNorml1(i);
|
|
}
|
|
}
|
|
|
|
MFEM_VERIFY(!(norm <= threshold), "diagonal block is NULL:"
|
|
" iblock = " << iblock << ", i = " << i << ", norm = "
|
|
<< norm);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void BlockMatrix::Finalize(int skip_zeros, bool fix_empty_rows)
|
|
{
|
|
for (int iblock = 0; iblock < nRowBlocks; ++iblock)
|
|
{
|
|
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
|
{
|
|
if (!Aij(iblock,jblock)) { continue; }
|
|
if (!Aij(iblock,jblock)->Finalized())
|
|
{
|
|
Aij(iblock,jblock)->Finalize(skip_zeros, fix_empty_rows);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void BlockMatrix::Mult(const Vector & x, Vector & y) const
|
|
{
|
|
if (x.GetData() == y.GetData())
|
|
{
|
|
mfem_error("Error: x and y can't point to the same datas \n");
|
|
}
|
|
|
|
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 << ")");
|
|
y = 0.;
|
|
AddMult(x, y, 1.0);
|
|
}
|
|
|
|
void BlockMatrix::AddMult(const Vector & x, Vector & y, const double val) const
|
|
{
|
|
if (x.GetData() == y.GetData())
|
|
{
|
|
mfem_error("Error: x and y can't point to the same datas \n");
|
|
}
|
|
|
|
Vector xblockview, yblockview;
|
|
|
|
for (int iblock = 0; iblock != nRowBlocks; ++iblock)
|
|
{
|
|
yblockview.SetDataAndSize(y.GetData() + row_offsets[iblock],
|
|
row_offsets[iblock+1] - row_offsets[iblock]);
|
|
|
|
for (int jblock = 0; jblock != nColBlocks; ++jblock)
|
|
{
|
|
if (Aij(iblock, jblock) != NULL)
|
|
{
|
|
xblockview.SetDataAndSize(
|
|
x.GetData() + col_offsets[jblock],
|
|
col_offsets[jblock+1] - col_offsets[jblock]);
|
|
|
|
Aij(iblock, jblock)->AddMult(xblockview, yblockview, val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void BlockMatrix::MultTranspose(const Vector & x, Vector & y) const
|
|
{
|
|
if (x.GetData() == y.GetData())
|
|
{
|
|
mfem_error("Error: x and y can't point to the same datas \n");
|
|
}
|
|
|
|
y = 0.;
|
|
AddMultTranspose(x, y, 1.0);
|
|
}
|
|
|
|
void BlockMatrix::AddMultTranspose(const Vector & x, Vector & y,
|
|
const double val) const
|
|
{
|
|
if (x.GetData() == y.GetData())
|
|
{
|
|
mfem_error("Error: x and y can't point to the same datas \n");
|
|
}
|
|
|
|
Vector xblockview, yblockview;
|
|
|
|
for (int iblock = 0; iblock != nColBlocks; ++iblock)
|
|
{
|
|
yblockview.SetDataAndSize(y.GetData() + col_offsets[iblock],
|
|
col_offsets[iblock+1] - col_offsets[iblock]);
|
|
|
|
for (int jblock = 0; jblock != nRowBlocks; ++jblock)
|
|
{
|
|
if (Aij(jblock, iblock) != NULL)
|
|
{
|
|
xblockview.SetDataAndSize(
|
|
x.GetData() + row_offsets[jblock],
|
|
row_offsets[jblock+1] - row_offsets[jblock]);
|
|
|
|
Aij(jblock, iblock)->AddMultTranspose(xblockview, yblockview, val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SparseMatrix * BlockMatrix::CreateMonolithic() const
|
|
{
|
|
int nnz = NumNonZeroElems();
|
|
|
|
int * i_amono = new int[row_offsets[nRowBlocks]+2];
|
|
int * j_amono = new int[nnz];
|
|
double * data = new double[nnz];
|
|
|
|
for (int i = 0; i < row_offsets[nRowBlocks]+2; i++)
|
|
{
|
|
i_amono[i] = 0;
|
|
}
|
|
|
|
int * i_amono_construction = i_amono+1;
|
|
|
|
for (int iblock = 0; iblock != nRowBlocks; ++iblock)
|
|
{
|
|
for (int irow(row_offsets[iblock]); irow < row_offsets[iblock+1]; ++irow)
|
|
{
|
|
int local_row = irow - row_offsets[iblock];
|
|
int ind = i_amono_construction[irow];
|
|
for (int jblock = 0; jblock < nColBlocks; ++jblock)
|
|
{
|
|
if (Aij(iblock,jblock) != NULL)
|
|
ind += Aij(iblock, jblock)->GetI()[local_row+1]
|
|
- Aij(iblock, jblock)->GetI()[local_row];
|
|
}
|
|
i_amono_construction[irow+1] = ind;
|
|
}
|
|
}
|
|
|
|
// Fill in the jarray and copy the data
|
|
for (int iblock = 0; iblock != nRowBlocks; ++iblock)
|
|
{
|
|
for (int jblock = 0; jblock != nColBlocks; ++jblock)
|
|
{
|
|
if (Aij(iblock,jblock) != NULL)
|
|
{
|
|
int nrow = row_offsets[iblock+1]-row_offsets[iblock];
|
|
int * i_aij = Aij(iblock, jblock)->GetI();
|
|
int * j_aij = Aij(iblock, jblock)->GetJ();
|
|
double * data_aij = Aij(iblock, jblock)->GetData();
|
|
int *i_it = i_amono_construction+row_offsets[iblock];
|
|
|
|
int loc_start_index = 0;
|
|
int loc_end_index = 0;
|
|
int glob_start_index = 0;
|
|
|
|
int shift(col_offsets[jblock]);
|
|
for (int * i_it_aij(i_aij+1); i_it_aij != i_aij+nrow+1; ++i_it_aij)
|
|
{
|
|
glob_start_index = *i_it;
|
|
|
|
#ifdef MFEM_DEBUG
|
|
if (glob_start_index > nnz)
|
|
{
|
|
mfem::out<<"glob_start_index = " << glob_start_index << "\n";
|
|
mfem::out<<"Block:" << iblock << " " << jblock << "\n";
|
|
mfem::out<<std::endl;
|
|
}
|
|
#endif
|
|
loc_end_index = *(i_it_aij);
|
|
for (int cnt = 0; cnt < loc_end_index-loc_start_index; cnt++)
|
|
{
|
|
data[glob_start_index+cnt] = data_aij[loc_start_index+cnt];
|
|
j_amono[glob_start_index+cnt] = j_aij[loc_start_index+cnt] + shift;
|
|
}
|
|
|
|
*i_it += loc_end_index-loc_start_index;
|
|
++i_it;
|
|
loc_start_index = loc_end_index;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return new SparseMatrix(i_amono, j_amono, data, row_offsets[nRowBlocks],
|
|
col_offsets[nColBlocks]);
|
|
}
|
|
|
|
void BlockMatrix::PrintMatlab(std::ostream & os) const
|
|
{
|
|
|
|
Vector row_data;
|
|
Array<int> row_ind;
|
|
int nnz_elem = NumNonZeroElems();
|
|
os<<"% size " << row_offsets.Last() << " " << col_offsets.Last() << "\n";
|
|
os<<"% Non Zeros " << nnz_elem << "\n";
|
|
int i, j;
|
|
std::ios::fmtflags old_fmt = os.flags();
|
|
os.setf(std::ios::scientific);
|
|
std::streamsize old_prec = os.precision(14);
|
|
for (i = 0; i < row_offsets.Last(); i++)
|
|
{
|
|
GetRow(i, row_ind, row_data);
|
|
for (j = 0; j < row_ind.Size(); j++)
|
|
{
|
|
os << i+1 << " " << row_ind[j]+1 << " " << row_data[j] << std::endl;
|
|
}
|
|
}
|
|
|
|
os.precision(old_prec);
|
|
os.flags(old_fmt);
|
|
}
|
|
|
|
BlockMatrix * Transpose(const BlockMatrix & A)
|
|
{
|
|
BlockMatrix * At = new BlockMatrix(A.ColOffsets(), A.RowOffsets());
|
|
At->owns_blocks = 1;
|
|
|
|
for (int irowAt = 0; irowAt < At->NumRowBlocks(); ++irowAt)
|
|
{
|
|
for (int jcolAt = 0; jcolAt < At->NumColBlocks(); ++jcolAt)
|
|
{
|
|
if (!A.IsZeroBlock(jcolAt, irowAt))
|
|
{
|
|
At->SetBlock(irowAt, jcolAt, Transpose(A.GetBlock(jcolAt, irowAt)));
|
|
}
|
|
}
|
|
}
|
|
return At;
|
|
}
|
|
|
|
BlockMatrix * Mult(const BlockMatrix & A, const BlockMatrix & B)
|
|
{
|
|
BlockMatrix * C= new BlockMatrix(A.RowOffsets(), B.ColOffsets());
|
|
C->owns_blocks = 1;
|
|
Array<SparseMatrix *> CijPieces(A.NumColBlocks());
|
|
|
|
for (int irowC = 0; irowC < A.NumRowBlocks(); ++irowC)
|
|
{
|
|
for (int jcolC = 0; jcolC < B.NumColBlocks(); ++jcolC)
|
|
{
|
|
CijPieces.SetSize(0, static_cast<SparseMatrix *>(NULL));
|
|
for (int k = 0; k < A.NumColBlocks(); ++k)
|
|
{
|
|
if (!A.IsZeroBlock(irowC, k) && !B.IsZeroBlock(k, jcolC))
|
|
{
|
|
CijPieces.Append(Mult(A.GetBlock(irowC, k), B.GetBlock(k, jcolC)));
|
|
}
|
|
}
|
|
|
|
if (CijPieces.Size() > 1)
|
|
{
|
|
C->SetBlock(irowC, jcolC, Add(CijPieces));
|
|
for (SparseMatrix ** it = CijPieces.GetData();
|
|
it != CijPieces.GetData()+CijPieces.Size(); ++it)
|
|
{
|
|
delete *it;
|
|
}
|
|
}
|
|
else if (CijPieces.Size() == 1)
|
|
{
|
|
C->SetBlock(irowC, jcolC, CijPieces[0]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return C;
|
|
}
|
|
|
|
}
|