User Guide
crout.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2023 Blue Brain Project, EPFL.
3  * See the top-level LICENSE file for details.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include "crout/crout.hpp"
9 
10 #include <catch2/catch_test_macros.hpp>
11 
12 #include <chrono>
13 #include <iostream>
14 #include <random>
15 
16 #include "Eigen/Dense"
17 #include "Eigen/LU"
18 
19 using namespace nmodl;
20 using namespace Eigen;
21 using namespace std;
22 
23 
24 /// https://stackoverflow.com/questions/15051367/how-to-compare-vectors-approximately-in-eigen
25 template <typename DerivedA, typename DerivedB>
26 bool allclose(const Eigen::DenseBase<DerivedA>& a,
27  const Eigen::DenseBase<DerivedB>& b,
28  const typename DerivedA::RealScalar& rtol =
29  Eigen::NumTraits<typename DerivedA::RealScalar>::dummy_precision(),
30  const typename DerivedA::RealScalar& atol =
31  Eigen::NumTraits<typename DerivedA::RealScalar>::epsilon()) {
32  return ((a.derived() - b.derived()).array().abs() <= (atol + rtol * b.derived().array().abs()))
33  .all();
34 }
35 
36 
37 template <typename T>
38 bool test_Crout_correctness(T rtol = 1e-8, T atol = 1e-8) {
39  std::random_device rd; // seeding
40  auto seed = rd();
41  std::mt19937 mt(seed);
42  std::uniform_real_distribution<T> nums(-1e3, 1e3);
43 
44  std::chrono::duration<double> eigen_timing(std::chrono::duration<double>::zero());
45  std::chrono::duration<double> crout_timing(std::chrono::duration<double>::zero());
46 
47  auto t1 = std::chrono::high_resolution_clock::now();
48  auto t2 = std::chrono::high_resolution_clock::now();
49 
50  for (int mat_size = 5; mat_size < 15; mat_size++) {
51  Matrix<T, Dynamic, Dynamic, Eigen::ColMajor> A_ColMajor(mat_size,
52  mat_size); // default in Eigen!
53  Matrix<T, Dynamic, Dynamic, Eigen::RowMajor> A_RowMajor(mat_size, mat_size);
54  Matrix<T, Dynamic, 1> b(mat_size);
55 
56  for (int repetitions = 0; repetitions < static_cast<int>(1e3); ++repetitions) {
57  do {
58  // initialization
59  for (int r = 0; r < mat_size; r++) {
60  for (int c = 0; c < mat_size; c++) {
61  A_ColMajor(r, c) = nums(mt);
62  A_RowMajor(r, c) = A_ColMajor(r, c);
63  b(r) = nums(mt);
64  }
65  }
66  } while (!A_ColMajor.fullPivLu().isInvertible()); // Checking Invertibility
67 
68  t1 = std::chrono::high_resolution_clock::now();
69  // Eigen (ColMajor)
70  Matrix<T, Dynamic, 1> eigen_x_ColMajor(mat_size);
71  eigen_x_ColMajor = A_ColMajor.partialPivLu().solve(b);
72 
73  // Eigen (RowMajor)
74  Matrix<T, Dynamic, 1> eigen_x_RowMajor(mat_size);
75  eigen_x_RowMajor = A_RowMajor.partialPivLu().solve(b);
76  t2 = std::chrono::high_resolution_clock::now();
77  eigen_timing += (t2 - t1);
78 
79  if (!allclose(eigen_x_ColMajor, eigen_x_RowMajor, rtol, atol)) {
80  cerr << "eigen_x_ColMajor vs eigen_x_RowMajor (issue) / seed = " << seed << endl;
81  return false;
82  }
83 
84  t1 = std::chrono::high_resolution_clock::now();
85  // Crout with A_ColMajor
86  Matrix<T, Dynamic, 1> crout_x_ColMajor(mat_size);
87  if (!A_ColMajor.IsRowMajor)
88  A_ColMajor.transposeInPlace();
89  Matrix<int, Dynamic, 1> pivot(mat_size);
90  Matrix<T, Dynamic, 1> rowmax(mat_size);
91  crout::Crout<T>(mat_size, A_ColMajor.data(), pivot.data(), rowmax.data());
92  crout::solveCrout<T>(
93  mat_size, A_ColMajor.data(), b.data(), crout_x_ColMajor.data(), pivot.data());
94 
95  // Crout with A_RowMajor
96  Matrix<T, Dynamic, 1> crout_x_RowMajor(mat_size);
97  crout::Crout<T>(mat_size, A_RowMajor.data(), pivot.data(), rowmax.data());
98  crout::solveCrout<T>(
99  mat_size, A_RowMajor.data(), b.data(), crout_x_RowMajor.data(), pivot.data());
100  t2 = std::chrono::high_resolution_clock::now();
101  crout_timing += (t2 - t1);
102 
103  if (!allclose(eigen_x_ColMajor, crout_x_ColMajor, rtol, atol)) {
104  cerr << "eigen_x_ColMajor vs crout_x_ColMajor (issue) / seed = " << seed << endl;
105  return false;
106  }
107 
108  if (!allclose(eigen_x_RowMajor, crout_x_RowMajor, rtol, atol)) {
109  cerr << "eigen_x_RowMajor vs crout_x_RowMajor (issue) / seed = " << seed << endl;
110  return false;
111  }
112  }
113  }
114 
115  std::cout << "eigen_timing [ms] : " << eigen_timing.count() * 1e3 << std::endl;
116  std::cout << "crout_timing [ms] : " << crout_timing.count() * 1e3 << std::endl;
117 
118  return true;
119 }
120 
121 
122 SCENARIO("Compare Crout solver with Eigen") {
123  GIVEN("crout (double)") {
124  constexpr double rtol = 1e-8;
125  constexpr double atol = 1e-8;
126 
127  auto test = test_Crout_correctness<double>(rtol, atol);
128 
129  THEN("run tests & compare") {
130  REQUIRE(test);
131  }
132  }
133 }
nmodl
encapsulates code generation backend implementations
Definition: ast_common.hpp:26
test_Crout_correctness
bool test_Crout_correctness(T rtol=1e-8, T atol=1e-8)
Definition: crout.cpp:38
allclose
bool allclose(const Eigen::DenseBase< DerivedA > &a, const Eigen::DenseBase< DerivedB > &b, const typename DerivedA::RealScalar &rtol=Eigen::NumTraits< typename DerivedA::RealScalar >::dummy_precision(), const typename DerivedA::RealScalar &atol=Eigen::NumTraits< typename DerivedA::RealScalar >::epsilon())
https://stackoverflow.com/questions/15051367/how-to-compare-vectors-approximately-in-eigen
Definition: crout.cpp:26
SCENARIO
SCENARIO("Compare Crout solver with Eigen")
Definition: crout.cpp:122
crout.hpp
Implementation of Crout matrix decomposition (LU decomposition) followed by Forward/Backward substitu...