370 lines
8.8 KiB
C++
370 lines
8.8 KiB
C++
// Copyright (c) 2010-2025, Lawrence Livermore National Security, LLC. Produced
|
|
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
|
|
// LICENSE and NOTICE for details. LLNL-CODE-806117.
|
|
//
|
|
// This file is part of the MFEM library. For more information and source code
|
|
// availability visit https://mfem.org.
|
|
//
|
|
// MFEM is free software; you can redistribute it and/or modify it under the
|
|
// terms of the BSD-3 license. We welcome feedback and contributions, see file
|
|
// CONTRIBUTING.md for details.
|
|
|
|
#include <fstream>
|
|
#include "mfem.hpp"
|
|
|
|
using namespace std;
|
|
using namespace mfem;
|
|
|
|
#include "unit_tests.hpp"
|
|
|
|
void fillRandomMatrix(SparseMatrix & M)
|
|
{
|
|
int nrows = M.Size();
|
|
int ncols = M.Width();
|
|
int max_nnz_row = ncols/50;
|
|
|
|
for (int i(0); i < nrows; ++i)
|
|
{
|
|
int nnz_row = rand()%max_nnz_row+1;
|
|
for (int j = 0; j < nnz_row; ++j)
|
|
{
|
|
M.Set(i,rand()%ncols, static_cast<double>( rand() )/static_cast<double>
|
|
(RAND_MAX) -.5 );
|
|
}
|
|
}
|
|
M.Finalize();
|
|
}
|
|
|
|
TEST_CASE("BlockMatrix", "[BlockMatrix]")
|
|
{
|
|
|
|
int size0 = 1000;
|
|
int size1 = 350;
|
|
|
|
Array<int> offsets(3);
|
|
offsets[0] = 0;
|
|
offsets[1] = size0;
|
|
offsets[2] = size0+size1;
|
|
|
|
SparseMatrix A00(size0, size0), A10(size1,size0), A01(size0,size1);
|
|
fillRandomMatrix(A00);
|
|
fillRandomMatrix(A10);
|
|
fillRandomMatrix(A01);
|
|
|
|
BlockMatrix * A = NULL;
|
|
{
|
|
A = new BlockMatrix( offsets );
|
|
A->SetBlock(0,0, &A00);
|
|
A->SetBlock(0,1, &A01);
|
|
A->SetBlock(1,0, &A10);
|
|
}
|
|
|
|
SparseMatrix * Amono = NULL;
|
|
{
|
|
Amono = A->CreateMonolithic();
|
|
}
|
|
|
|
|
|
int size(A->NumRows());
|
|
double tol = 1e-10;
|
|
int ntry(5);
|
|
|
|
SECTION("Check method BlockMatrix::RowSize")
|
|
{
|
|
int nfails(0);
|
|
for (int i(0); i < size; ++i)
|
|
{
|
|
if ( A->RowSize(i) != Amono->RowSize(i) )
|
|
{
|
|
UNSCOPED_INFO("BlockMatrix::RowSize failure: " << i << '\t'
|
|
<< A->RowSize( i) << '\t'
|
|
<< Amono->RowSize(i));
|
|
++nfails;
|
|
}
|
|
}
|
|
REQUIRE(nfails == 0);
|
|
}
|
|
|
|
SECTION("Check method BlockMatrix::GetRow")
|
|
{
|
|
double maxerror(-1.), currentError;
|
|
Vector glob(size), globgood(size);
|
|
Vector srow, srowgood;
|
|
Array<int> cols, colsgood;
|
|
|
|
for (int i(0); i < size; ++i)
|
|
{
|
|
A->GetRow(i, cols, srow);
|
|
glob = 0.0;
|
|
glob.SetSubVector(cols, srow);
|
|
Amono->GetRow(i, colsgood, srowgood);
|
|
globgood = 0.0;
|
|
globgood.SetSubVector(colsgood, srowgood);
|
|
glob.Add(-1., globgood);
|
|
currentError = glob.Normlinf();
|
|
|
|
if (currentError > maxerror)
|
|
{
|
|
maxerror = currentError;
|
|
}
|
|
}
|
|
REQUIRE(maxerror < tol);
|
|
}
|
|
|
|
|
|
Vector x(size), y(size), ymono(size);
|
|
|
|
SECTION("Check BlockMatrix::Mult")
|
|
{
|
|
double maxerror(-1.), currentError;
|
|
for (int i(0); i < ntry; ++i)
|
|
{
|
|
x.Randomize();
|
|
A->Mult(x,y);
|
|
Amono->Mult(x,ymono);
|
|
y.Add(-1., ymono);
|
|
currentError = y.Normlinf();
|
|
|
|
if (currentError > maxerror)
|
|
{
|
|
maxerror = currentError;
|
|
}
|
|
|
|
}
|
|
REQUIRE(maxerror < tol);
|
|
}
|
|
|
|
SECTION("Check BlockMatrix::AddMult #1")
|
|
{
|
|
double maxerror(-1.), currentError;
|
|
for (int i(0); i < ntry; ++i)
|
|
{
|
|
x.Randomize();
|
|
y.Randomize();
|
|
ymono = y;
|
|
A->AddMult(x,y);
|
|
Amono->AddMult(x,ymono);
|
|
y.Add(-1., ymono);
|
|
currentError = y.Normlinf();
|
|
|
|
if (currentError > maxerror)
|
|
{
|
|
maxerror = currentError;
|
|
}
|
|
|
|
}
|
|
REQUIRE(maxerror < tol);
|
|
}
|
|
|
|
SECTION("Check BlockMatrix::AddMult #2")
|
|
{
|
|
double maxerror(-1.), currentError;
|
|
for (int i(0); i < ntry; ++i)
|
|
{
|
|
x.Randomize();
|
|
y.Randomize();
|
|
ymono = y;
|
|
double a = 10 * static_cast<double>( rand() ) / static_cast<double>
|
|
( RAND_MAX );
|
|
a -= 5;
|
|
A->AddMult(x,y,a);
|
|
Amono->AddMult(x,ymono,a);
|
|
y.Add(-1., ymono);
|
|
currentError = y.Normlinf();
|
|
|
|
if (currentError > maxerror)
|
|
{
|
|
maxerror = currentError;
|
|
}
|
|
|
|
}
|
|
REQUIRE(maxerror < tol);
|
|
}
|
|
|
|
SECTION("Check BlockMatrix::MultTranspose")
|
|
{
|
|
double maxerror(-1.), currentError;
|
|
for (int i(0); i < ntry; ++i)
|
|
{
|
|
x.Randomize();
|
|
A->MultTranspose(x,y);
|
|
Amono->MultTranspose(x,ymono);
|
|
y.Add(-1., ymono);
|
|
currentError = y.Normlinf();
|
|
|
|
if (currentError > maxerror)
|
|
{
|
|
maxerror = currentError;
|
|
}
|
|
|
|
}
|
|
REQUIRE(maxerror < tol);
|
|
}
|
|
|
|
SECTION("Check BlockMatrix::AddMultTranspose #1")
|
|
{
|
|
double maxerror(-1.), currentError;
|
|
for (int i(0); i < ntry; ++i)
|
|
{
|
|
x.Randomize();
|
|
y.Randomize();
|
|
ymono = y;
|
|
A->AddMultTranspose(x,y);
|
|
Amono->AddMultTranspose(x,ymono);
|
|
y.Add(-1., ymono);
|
|
currentError = y.Normlinf();
|
|
|
|
if (currentError > maxerror)
|
|
{
|
|
maxerror = currentError;
|
|
}
|
|
|
|
}
|
|
REQUIRE(maxerror < tol);
|
|
}
|
|
|
|
SECTION("Check BlockMatrix::AddMultTranspose #2")
|
|
{
|
|
double maxerror(-1.), currentError;
|
|
for (int i(0); i < ntry; ++i)
|
|
{
|
|
x.Randomize();
|
|
y.Randomize();
|
|
ymono = y;
|
|
double a = 10 * static_cast<double>( rand() ) / static_cast<double>
|
|
( RAND_MAX );
|
|
a -= 5;
|
|
A->AddMultTranspose(x,y,a);
|
|
Amono->AddMultTranspose(x,ymono,a);
|
|
y.Add(-1., ymono);
|
|
currentError = y.Normlinf();
|
|
|
|
if (currentError > maxerror)
|
|
{
|
|
maxerror = currentError;
|
|
}
|
|
|
|
}
|
|
REQUIRE(maxerror < tol);
|
|
}
|
|
|
|
SECTION("Check Transpose(const BlockMatrix &)")
|
|
{
|
|
BlockMatrix * At = Transpose(*A);
|
|
REQUIRE(At->Height() == A->Width() );
|
|
REQUIRE(At->Width() == A->Height() );
|
|
|
|
x.Randomize();
|
|
y.Randomize();
|
|
Vector Ax(A->Height()), Aty(At->Height());
|
|
A->Mult(x,Ax);
|
|
double yAx = y*Ax;
|
|
|
|
At->Mult(y, Aty);
|
|
double xAty = x* Aty;
|
|
|
|
delete At;
|
|
|
|
REQUIRE(fabs(yAx - xAty) < tol );
|
|
}
|
|
|
|
SECTION("Check Mult(const BlockMatrix &, const BlockMatrix &)")
|
|
{
|
|
BlockMatrix * B = new BlockMatrix(offsets);
|
|
SparseMatrix B00(size0, size0), B10(size1,size0), B01(size0,size1), B11(size1,
|
|
size1);
|
|
fillRandomMatrix(B00);
|
|
fillRandomMatrix(B10);
|
|
fillRandomMatrix(B01);
|
|
fillRandomMatrix(B11);
|
|
B->SetBlock(0,0, &B00);
|
|
B->SetBlock(0,1, &B01);
|
|
B->SetBlock(1,0, &B10);
|
|
B->SetBlock(1,1, &B11);
|
|
|
|
BlockMatrix * C = Mult(*A,*B);
|
|
x.Randomize();
|
|
Vector Bx(A->Height()), ABx(A->Height()), Cx(C->Height());
|
|
B->Mult(x, Bx);
|
|
A->Mult(Bx,ABx);
|
|
C->Mult(x, Cx);
|
|
|
|
subtract(Cx, ABx, Cx);
|
|
|
|
double error = Cx.Normlinf();
|
|
|
|
delete B;
|
|
delete C;
|
|
|
|
REQUIRE(error < tol );
|
|
}
|
|
|
|
Vector y1(A->Height());
|
|
Vector y2(Amono->Height());
|
|
SECTION("Check PartMult")
|
|
{
|
|
Array<int> rows{{10,39,509,289,1112,1321,927}};
|
|
x.Randomize();
|
|
y1.Randomize();
|
|
y2 = y1;
|
|
A->PartMult(rows,x,y1);
|
|
Amono->PartMult(rows,x,y2);
|
|
y1-=y2;
|
|
REQUIRE(y1.Norml2() == MFEM_Approx(0.0));
|
|
}
|
|
|
|
SECTION("Check PartAddMult")
|
|
{
|
|
Array<int> rows{{8,92,591,203,1094,1211,927}};
|
|
x.Randomize();
|
|
y1.Randomize();
|
|
y2 = y1;
|
|
A->PartAddMult(rows,x,y1);
|
|
Amono->PartAddMult(rows,x,y2);
|
|
y1-=y2;
|
|
REQUIRE(y1.Norml2() == MFEM_Approx(0.0));
|
|
}
|
|
|
|
SECTION("Check EliminateRowCols")
|
|
{
|
|
Array<int> rows{{18,72,1342,951,423,877,1234}};
|
|
BlockMatrix Ae(offsets); Ae.owns_blocks = 1;
|
|
|
|
// Make sure the matrix is symmetric
|
|
BlockMatrix * At = Transpose(*A);
|
|
BlockMatrix * AtA = Mult(*At,*A);
|
|
delete At;
|
|
|
|
for (int i = 0; i<Ae.NumRowBlocks(); i++)
|
|
{
|
|
int h = offsets[i+1] - offsets[i];
|
|
for (int j = 0; j<Ae.NumColBlocks(); j++)
|
|
{
|
|
int w = offsets[j+1] - offsets[j];
|
|
Ae.SetBlock(i,j,new SparseMatrix(h, w));
|
|
}
|
|
}
|
|
AtA->EliminateRowCols(rows,&Ae,mfem::Operator::DIAG_ONE);
|
|
|
|
SparseMatrix *At_mono = Transpose(*Amono);
|
|
SparseMatrix *AtA_mono = Mult(*At_mono, *Amono);
|
|
delete At_mono;
|
|
SparseMatrix AtAmono_e(offsets.Last());
|
|
|
|
for (int i = 0; i<rows.Size(); i++)
|
|
{
|
|
AtA_mono->EliminateRowCol(rows[i],AtAmono_e,mfem::Operator::DIAG_ONE);
|
|
}
|
|
|
|
SparseMatrix * diff = AtA->CreateMonolithic();
|
|
diff->Add(-1.0, *AtA_mono);
|
|
REQUIRE(diff->MaxNorm() == MFEM_Approx(0.0));
|
|
delete AtA_mono;
|
|
delete AtA;
|
|
delete diff;
|
|
}
|
|
|
|
delete A;
|
|
delete Amono;
|
|
}
|