Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
triangularsolvetest.cc 3.26 KiB
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <random>

#include <dune/common/bitsetvector.hh>
#include <dune/common/fmatrix.hh>
#include <dune/common/fvector.hh>
#include <dune/istl/bcrsmatrix.hh>
#include <dune/istl/bvector.hh>
#include <dune/istl/matrixindexset.hh>

#include "../transpose.hh"
#include "../triangularsolve.hh"

#include "common.hh"

double const tol = 1e-10;
double const reg = 1e-6;

template <int n, bool lower>
bool test() {
  using ft = double;
  using LocalMatrix = Dune::FieldMatrix<ft, 1, 1>;
  using Matrix = Dune::BCRSMatrix<LocalMatrix>;
  using LocalVector = Dune::FieldVector<ft, 1>;
  using Vector = Dune::BlockVector<LocalVector>;

  bool passed = true;

  std::random_device randomDevice;
  std::mt19937 generator(randomDevice());

  Matrix M;
  Dune::MatrixIndexSet indices(n, n);
  for (size_t i = 0; i < n; ++i) {
    size_t initialJ = lower ? 0 : i;
    size_t finalJ = lower ? i + 1 : n;
    for (size_t j = initialJ; j < finalJ; ++j) {
      std::uniform_int_distribution<int> integerDistribution(1,
                                                             finalJ - initialJ);
      auto aroundTen = [&]() {
        auto x = integerDistribution(generator);
        return x <= 10;
      };
      // Add a diagonal entry and approximately ten non-zero entries
      if (i == j or aroundTen())
        indices.add(i, j);
    }
  }
  indices.exportIdx(M);

  std::uniform_real_distribution<ft> unconstrainedDistribution(-1, 1);
  for (auto it = M.begin(); it != M.end(); ++it) {
    size_t const i = it.index();
    for (auto cIt = it->begin(); cIt != it->end(); ++cIt) {
      size_t const j = cIt.index();
      *cIt = unconstrainedDistribution(generator);
      // Make sure the diagonal is bounded away from zero
      if (i == j)
        *cIt += (*cIt > 0 ? 1 : -1) + reg;
    }
  }

  Vector sol(n);
  for (auto &x : sol)
    x = unconstrainedDistribution(generator);
  Vector b(n);
  M.mv(sol, b);

  Dune::BitSetVector<1> *ignore = nullptr;
  {
    auto x = lower ? Dune::MatrixVector::lowerTriangularSolve(M, b, ignore)
                   : Dune::MatrixVector::upperTriangularSolve(M, b, ignore);
    Vector y(n);
    M.mv(x, y);
    auto diff = diffDune(b, y);
    std::cout << "|x - A^{-1}b| = " << diff << std::endl;
    passed &= diff < tol;
  }
  {
    auto x = lower
                 ? Dune::MatrixVector::upperTriangularSolve(M, b, ignore, true)
                 : Dune::MatrixVector::lowerTriangularSolve(M, b, ignore, true);
    Vector y(n);
    M.mtv(x, y);
    auto diff = diffDune(b, y);
    std::cout << "|x - A^{-T}b| = " << diff << " (manual transpose)"
              << std::endl;
    passed &= diff < tol;
  }
  {
    Matrix Mt;
    Dune::MatrixVector::transpose(M, Mt);
    auto x = lower ? Dune::MatrixVector::upperTriangularSolve(Mt, b, ignore)
                   : Dune::MatrixVector::lowerTriangularSolve(Mt, b, ignore);
    Vector y(n);
    M.mtv(x, y);
    auto diff = diffDune(b, y);
    std::cout << "|x - A^{-T}b| = " << diff << std::endl;
    passed &= diff < tol;
  }
  return passed;
}

int main() {
  bool passed = true;
  std::cout << "Testing random upper triangular matrix" << std::endl;
  passed &= test<5000, false>();
  std::cout << "Testing lower triangular matrix" << std::endl;
  passed &= test<5000, true>();
  return passed ? 0 : 1;
}