Files
mfem/tests/unit/linalg/test_matrix_block.cpp
T

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;
}