libMesh
Public Member Functions | Private Member Functions | List of all members
DenseMatrixTest Class Reference
Inheritance diagram for DenseMatrixTest:
[legend]

Public Member Functions

void setUp ()
 
void tearDown ()
 
 LIBMESH_CPPUNIT_TEST_SUITE (DenseMatrixTest)
 
 CPPUNIT_TEST (testOuterProduct)
 
 CPPUNIT_TEST (testSVD)
 
 CPPUNIT_TEST (testEVDreal)
 
 CPPUNIT_TEST (testEVDcomplex)
 
 CPPUNIT_TEST (testComplexSVD)
 
 CPPUNIT_TEST (testSubMatrix)
 
 CPPUNIT_TEST_SUITE_END ()
 

Private Member Functions

void testOuterProduct ()
 
void testSVD ()
 
void testEVD_helper (DenseMatrix< Real > &A, std::vector< Real > true_lambda_real, std::vector< Real > true_lambda_imag, Real tol)
 
void testEVDreal ()
 
void testEVDcomplex ()
 
void testComplexSVD ()
 
void testSubMatrix ()
 

Detailed Description

Definition at line 14 of file dense_matrix_test.C.

Member Function Documentation

◆ CPPUNIT_TEST() [1/6]

DenseMatrixTest::CPPUNIT_TEST ( testOuterProduct  )

◆ CPPUNIT_TEST() [2/6]

DenseMatrixTest::CPPUNIT_TEST ( testSVD  )

◆ CPPUNIT_TEST() [3/6]

DenseMatrixTest::CPPUNIT_TEST ( testEVDreal  )

◆ CPPUNIT_TEST() [4/6]

DenseMatrixTest::CPPUNIT_TEST ( testEVDcomplex  )

◆ CPPUNIT_TEST() [5/6]

DenseMatrixTest::CPPUNIT_TEST ( testComplexSVD  )

◆ CPPUNIT_TEST() [6/6]

DenseMatrixTest::CPPUNIT_TEST ( testSubMatrix  )

◆ CPPUNIT_TEST_SUITE_END()

DenseMatrixTest::CPPUNIT_TEST_SUITE_END ( )

◆ LIBMESH_CPPUNIT_TEST_SUITE()

DenseMatrixTest::LIBMESH_CPPUNIT_TEST_SUITE ( DenseMatrixTest  )

◆ setUp()

void DenseMatrixTest::setUp ( )
inline

Definition at line 17 of file dense_matrix_test.C.

17 {}

◆ tearDown()

void DenseMatrixTest::tearDown ( )
inline

Definition at line 19 of file dense_matrix_test.C.

19 {}

◆ testComplexSVD()

void DenseMatrixTest::testComplexSVD ( )
inlineprivate

Definition at line 340 of file dense_matrix_test.C.

References libMesh::DenseVector< T >::size(), and libMesh::DenseMatrix< T >::svd().

341  {
342 #ifdef LIBMESH_USE_COMPLEX_NUMBERS
343  LOG_UNIT_TEST;
344 
345  DenseMatrix<Complex> A(3,3);
346 
347  A(0,0) = Complex(2.18904,4.44523e-18); A(0,1) = Complex(-3.20491,-0.136699); A(0,2) = Complex(0.716316,-0.964802);
348  A(1,0) = Complex(-3.20491,0.136699); A(1,1) = Complex(4.70076,-3.25261e-18); A(1,2) = Complex(-0.98849,1.45727);
349  A(2,0) = Complex(0.716316,0.964802); A(2,1) = Complex(-0.98849,-1.45727); A(2,2) = Complex(0.659629,-4.01155e-18);
350 
351  DenseVector<Real> sigma;
352  A.svd(sigma);
353 
354  DenseVector<Real> true_sigma(3);
355  true_sigma(0) = 7.54942516052;
356  true_sigma(1) = 3.17479511368e-06;
357  true_sigma(2) = 6.64680908281e-07;
358 
359  for (unsigned i=0; i<sigma.size(); ++i)
360  LIBMESH_ASSERT_FP_EQUAL(sigma(i), true_sigma(i), 1.e-10);
361 #endif
362  }
std::complex< Real > Complex
virtual unsigned int size() const override final
Definition: dense_vector.h:104
Defines a dense vector for use in Finite Element-type computations.
Definition: dof_map.h:74
Defines a dense matrix for use in Finite Element-type computations.
Definition: dof_map.h:75

◆ testEVD_helper()

void DenseMatrixTest::testEVD_helper ( DenseMatrix< Real > &  A,
std::vector< Real >  true_lambda_real,
std::vector< Real >  true_lambda_imag,
Real  tol 
)
inlineprivate

Definition at line 100 of file dense_matrix_test.C.

References libMesh::DenseMatrix< T >::evd_left_and_right(), libMesh::DenseVector< T >::get_values(), libMesh::DenseMatrixBase< T >::m(), libMesh::DenseVector< T >::size(), and libMesh::DenseVector< T >::zero().

104  {
105  // Note: see bottom of this file, we only do this test if PETSc is
106  // available, but this function currently only exists if we're
107  // using real numbers.
108 #ifdef LIBMESH_USE_REAL_NUMBERS
109  // Let's compute the eigenvalues on a copy of A, so that we can
110  // use the original to check the computation.
111  DenseMatrix<Real> A_copy = A;
112 
113  DenseVector<Real> lambda_real, lambda_imag;
114  DenseMatrix<Real> VR; // right eigenvectors
115  DenseMatrix<Real> VL; // left eigenvectors
116  A_copy.evd_left_and_right(lambda_real, lambda_imag, VL, VR);
117 
118  // The matrix is square and of size N x N.
119  const unsigned N = A.m();
120 
121  // Verify left eigen-values.
122  // Test that the right eigenvalues are self-consistent by computing
123  // u_j**H * A = lambda_j * u_j**H
124  // Note that we have to handle real and complex eigenvalues
125  // differently, since complex eigenvectors share their storage.
126  for (unsigned eigenval=0; eigenval<N; ++eigenval)
127  {
128  // Only check real eigenvalues
129  if (std::abs(lambda_imag(eigenval)) < tol*tol)
130  {
131  // remove print libMesh::out << "Checking eigenvalue: " << eigenval << std::endl;
132  DenseVector<Real> lhs(N), rhs(N);
133  for (unsigned i=0; i<N; ++i)
134  {
135  rhs(i) = lambda_real(eigenval) * VL(i, eigenval);
136  for (unsigned j=0; j<N; ++j)
137  lhs(i) += A(j, i) * VL(j, eigenval); // Note: A(j,i)
138  }
139 
140  // Subtract and assert that the norm of the difference is
141  // below some tolerance.
142  lhs -= rhs;
143  LIBMESH_ASSERT_FP_EQUAL(/*expected=*/0., /*actual=*/lhs.l2_norm(), std::sqrt(tol)*tol);
144  }
145  else
146  {
147  // This is a complex eigenvalue, so:
148  // a.) It occurs in a complex-conjugate pair
149  // b.) the real part of the eigenvector is stored is VL(:,eigenval)
150  // c.) the imag part of the eigenvector is stored in VL(:,eigenval+1)
151  //
152  // Equating the real and imaginary parts of Ax=lambda*x leads to two sets
153  // of relations that must hold:
154  // 1.) A^T x_r = lambda_r*x_r + lambda_i*x_i
155  // 2.) A^T x_i = -lambda_i*x_r + lambda_r*x_i
156  // which we can verify.
157 
158  // 1.)
159  DenseVector<Real> lhs(N), rhs(N);
160  for (unsigned i=0; i<N; ++i)
161  {
162  rhs(i) = lambda_real(eigenval) * VL(i, eigenval) + lambda_imag(eigenval) * VL(i, eigenval+1);
163  for (unsigned j=0; j<N; ++j)
164  lhs(i) += A(j, i) * VL(j, eigenval); // Note: A(j,i)
165  }
166 
167  lhs -= rhs;
168  LIBMESH_ASSERT_FP_EQUAL(/*expected=*/0., /*actual=*/lhs.l2_norm(), std::sqrt(tol)*tol);
169 
170  // libMesh::out << "lhs=" << std::endl;
171  // lhs.print_scientific(libMesh::out, /*precision=*/15);
172  //
173  // libMesh::out << "rhs=" << std::endl;
174  // rhs.print_scientific(libMesh::out, /*precision=*/15);
175 
176  // 2.)
177  lhs.zero();
178  rhs.zero();
179  for (unsigned i=0; i<N; ++i)
180  {
181  rhs(i) = -lambda_imag(eigenval) * VL(i, eigenval) + lambda_real(eigenval) * VL(i, eigenval+1);
182  for (unsigned j=0; j<N; ++j)
183  lhs(i) += A(j, i) * VL(j, eigenval+1); // Note: A(j,i)
184  }
185 
186  lhs -= rhs;
187  LIBMESH_ASSERT_FP_EQUAL(/*expected=*/0., /*actual=*/lhs.l2_norm(), std::sqrt(tol)*tol);
188 
189  // libMesh::out << "lhs=" << std::endl;
190  // lhs.print_scientific(libMesh::out, /*precision=*/15);
191  //
192  // libMesh::out << "rhs=" << std::endl;
193  // rhs.print_scientific(libMesh::out, /*precision=*/15);
194 
195  // We'll skip the second member of the complex conjugate
196  // pair. If the first one worked, the second one should
197  // as well...
198  eigenval += 1;
199  }
200  }
201 
202  // Verify right eigen-values.
203  // Test that the right eigenvalues are self-consistent by computing
204  // A * v_j - lambda_j * v_j
205  // Note that we have to handle real and complex eigenvalues
206  // differently, since complex eigenvectors share their storage.
207  for (unsigned eigenval=0; eigenval<N; ++eigenval)
208  {
209  // Only check real eigenvalues
210  if (std::abs(lambda_imag(eigenval)) < tol*tol)
211  {
212  // remove print libMesh::out << "Checking eigenvalue: " << eigenval << std::endl;
213  DenseVector<Real> lhs(N), rhs(N);
214  for (unsigned i=0; i<N; ++i)
215  {
216  rhs(i) = lambda_real(eigenval) * VR(i, eigenval);
217  for (unsigned j=0; j<N; ++j)
218  lhs(i) += A(i, j) * VR(j, eigenval);
219  }
220 
221  lhs -= rhs;
222  LIBMESH_ASSERT_FP_EQUAL(/*expected=*/0., /*actual=*/lhs.l2_norm(), std::sqrt(tol)*tol);
223  }
224  else
225  {
226  // This is a complex eigenvalue, so:
227  // a.) It occurs in a complex-conjugate pair
228  // b.) the real part of the eigenvector is stored is VR(:,eigenval)
229  // c.) the imag part of the eigenvector is stored in VR(:,eigenval+1)
230  //
231  // Equating the real and imaginary parts of Ax=lambda*x leads to two sets
232  // of relations that must hold:
233  // 1.) Ax_r = lambda_r*x_r - lambda_i*x_i
234  // 2.) Ax_i = lambda_i*x_r + lambda_r*x_i
235  // which we can verify.
236 
237  // 1.)
238  DenseVector<Real> lhs(N), rhs(N);
239  for (unsigned i=0; i<N; ++i)
240  {
241  rhs(i) = lambda_real(eigenval) * VR(i, eigenval) - lambda_imag(eigenval) * VR(i, eigenval+1);
242  for (unsigned j=0; j<N; ++j)
243  lhs(i) += A(i, j) * VR(j, eigenval);
244  }
245 
246  lhs -= rhs;
247  LIBMESH_ASSERT_FP_EQUAL(/*expected=*/0., /*actual=*/lhs.l2_norm(), std::sqrt(tol)*tol);
248 
249  // 2.)
250  lhs.zero();
251  rhs.zero();
252  for (unsigned i=0; i<N; ++i)
253  {
254  rhs(i) = lambda_imag(eigenval) * VR(i, eigenval) + lambda_real(eigenval) * VR(i, eigenval+1);
255  for (unsigned j=0; j<N; ++j)
256  lhs(i) += A(i, j) * VR(j, eigenval+1);
257  }
258 
259  lhs -= rhs;
260  LIBMESH_ASSERT_FP_EQUAL(/*expected=*/0., /*actual=*/lhs.l2_norm(), std::sqrt(tol)*tol);
261 
262  // We'll skip the second member of the complex conjugate
263  // pair. If the first one worked, the second one should
264  // as well...
265  eigenval += 1;
266  }
267  }
268 
269  // Sort the results from Lapack *individually*.
270  std::sort(lambda_real.get_values().begin(), lambda_real.get_values().end());
271  std::sort(lambda_imag.get_values().begin(), lambda_imag.get_values().end());
272 
273  // Sort the true eigenvalues *individually*.
274  std::sort(true_lambda_real.begin(), true_lambda_real.end());
275  std::sort(true_lambda_imag.begin(), true_lambda_imag.end());
276 
277  // Compare the individually-sorted values.
278  for (unsigned i=0; i<lambda_real.size(); ++i)
279  {
280  // Note: I initially verified the results with TOLERANCE**2,
281  // but that turned out to be just a bit too tight for some of
282  // the test problems. I'm not sure what controls the accuracy
283  // of the eigenvalue computation in LAPACK, there is no way to
284  // set a tolerance in the LAPACKgeev_ interface.
285  LIBMESH_ASSERT_FP_EQUAL(/*expected=*/true_lambda_real[i], /*actual=*/lambda_real(i), std::sqrt(tol)*tol);
286  LIBMESH_ASSERT_FP_EQUAL(/*expected=*/true_lambda_imag[i], /*actual=*/lambda_imag(i), std::sqrt(tol)*tol);
287  }
288 #endif
289  }
std::vector< T > & get_values()
Definition: dense_vector.h:278
void evd_left_and_right(DenseVector< T > &lambda_real, DenseVector< T > &lambda_imag, DenseMatrix< T > &VL, DenseMatrix< T > &VR)
Compute the eigenvalues (both real and imaginary parts) as well as the left and right eigenvectors of...
unsigned int m() const
virtual unsigned int size() const override final
Definition: dense_vector.h:104
Defines a dense vector for use in Finite Element-type computations.
Definition: dof_map.h:74

◆ testEVDcomplex()

void DenseMatrixTest::testEVDcomplex ( )
inlineprivate

Definition at line 312 of file dense_matrix_test.C.

References libMesh::Real, and libMesh::TOLERANCE.

313  {
314  LOG_UNIT_TEST;
315 
316  // This test is also from a Matlab example, and has complex eigenvalues.
317  // http://www.mathworks.com/help/matlab/math/eigenvalues.html?s_tid=gn_loc_drop
318  DenseMatrix<Real> A(3, 3);
319  A(0,0) = 0; A(0,1) = -6; A(0,2) = -1;
320  A(1,0) = 6; A(1,1) = 2; A(1,2) = -16;
321  A(2,0) = -5; A(2,1) = 20; A(2,2) = -10;
322 
323  std::vector<Real> true_lambda_real(3);
324  true_lambda_real[0] = -3.070950351248293;
325  true_lambda_real[1] = -2.464524824375853;
326  true_lambda_real[2] = -2.464524824375853;
327  std::vector<Real> true_lambda_imag(3);
328  true_lambda_imag[0] = 0.;
329  true_lambda_imag[1] = 17.60083096447099;
330  true_lambda_imag[2] = -17.60083096447099;
331 
332  // We're good up to double precision (due to the truncated
333  // literals above) or Real precision, whichever is worse
334  const Real tol = std::max(Real(1e-6), TOLERANCE);
335 
336  // call helper function to compute and verify results
337  testEVD_helper(A, true_lambda_real, true_lambda_imag, tol);
338  }
static constexpr Real TOLERANCE
void testEVD_helper(DenseMatrix< Real > &A, std::vector< Real > true_lambda_real, std::vector< Real > true_lambda_imag, Real tol)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real

◆ testEVDreal()

void DenseMatrixTest::testEVDreal ( )
inlineprivate

Definition at line 291 of file dense_matrix_test.C.

References libMesh::TOLERANCE.

292  {
293  LOG_UNIT_TEST;
294 
295  // This is an example from Matlab's gallery(3) which is a
296  // non-symmetric 3x3 matrix with eigen values lambda = 1, 2, 3.
297  DenseMatrix<Real> A(3, 3);
298  A(0,0) = -149; A(0,1) = -50; A(0,2) = -154;
299  A(1,0) = 537; A(1,1) = 180; A(1,2) = 546;
300  A(2,0) = -27; A(2,1) = -9; A(2,2) = -25;
301 
302  std::vector<Real> true_lambda_real(3);
303  true_lambda_real[0] = 1.;
304  true_lambda_real[1] = 2.;
305  true_lambda_real[2] = 3.;
306  std::vector<Real> true_lambda_imag(3); // all zero
307 
308  // call helper function to compute and verify results.
309  testEVD_helper(A, true_lambda_real, true_lambda_imag, TOLERANCE);
310  }
static constexpr Real TOLERANCE
void testEVD_helper(DenseMatrix< Real > &A, std::vector< Real > true_lambda_real, std::vector< Real > true_lambda_imag, Real tol)

◆ testOuterProduct()

void DenseMatrixTest::testOuterProduct ( )
inlineprivate

Definition at line 35 of file dense_matrix_test.C.

References libMesh::DenseMatrix< T >::outer_product(), libMesh::DenseVector< T >::size(), and libMesh::TOLERANCE.

36  {
37  LOG_UNIT_TEST;
38 
39  DenseVector<Real> a = {1.0, 2.0};
40  DenseVector<Real> b = {3.0, 4.0, 5.0};
41 
42  DenseMatrix<Real> a_times_b;
43  a_times_b.outer_product(a, b);
44 
45  DenseMatrix<Real> a_times_b_correct(2, 3,
46  {3., 4., 5.,
47  6., 8., 10.});
48 
49  for (unsigned int i = 0; i < a.size(); ++i)
50  for (unsigned int j = 0; j < b.size(); ++j)
51  LIBMESH_ASSERT_NUMBERS_EQUAL
52  (a_times_b(i,j), a_times_b_correct(i,j), TOLERANCE*TOLERANCE);
53  }
static constexpr Real TOLERANCE
void outer_product(const DenseVector< T > &a, const DenseVector< T > &b)
Computes the outer (dyadic) product of two vectors and stores in (*this).
virtual unsigned int size() const override final
Definition: dense_vector.h:104
Defines a dense vector for use in Finite Element-type computations.
Definition: dof_map.h:74

◆ testSubMatrix()

void DenseMatrixTest::testSubMatrix ( )
inlineprivate

Definition at line 364 of file dense_matrix_test.C.

References libMesh::DenseMatrix< T >::sub_matrix().

365  {
366  LOG_UNIT_TEST;
367 
368  DenseMatrix<Number> A(4, 3);
369  A(0,0) = 1.0; A(0,1) = 2.0; A(0,2) = 3.0;
370  A(1,0) = 4.0; A(1,1) = 5.0; A(1,2) = 6.0;
371  A(2,0) = 7.0; A(2,1) = 8.0; A(2,2) = 9.0;
372  A(3,0) =10.0; A(3,1) =11.0; A(3,2) =12.0;
373 
374  DenseMatrix<Number> B(2, 2);
375  B(0,0) = 7.0; B(0,1) = 8.0;
376  B(1,0) =10.0; B(1,1) =11.0;
377 
378  DenseMatrix<Number> C = A.sub_matrix(2, 2, 0, 2);
379  CPPUNIT_ASSERT(B == C);
380  }
Definition: assembly.h:38
DenseMatrix sub_matrix(unsigned int row_id, unsigned int row_size, unsigned int col_id, unsigned int col_size) const
Get submatrix with the smallest row and column indices and the submatrix size.
Definition: dense_matrix.h:922

◆ testSVD()

void DenseMatrixTest::testSVD ( )
inlineprivate

Definition at line 55 of file dense_matrix_test.C.

References libMesh::DenseMatrixBase< T >::m(), libMesh::DenseMatrixBase< T >::n(), libMesh::Real, libMesh::DenseVector< T >::size(), libMesh::DenseMatrix< T >::svd(), and libMesh::TOLERANCE.

56  {
57  LOG_UNIT_TEST;
58 
59  DenseMatrix<Number> U, VT;
60  DenseVector<Real> sigma;
61  DenseMatrix<Number> A(3, 2, {
62  1.0, 2.0,
63  3.0, 4.0,
64  5.0, 6.0});
65 
66  A.svd(sigma, U, VT);
67 
68  // Solution for this case is (verified with numpy)
69  DenseMatrix<Number> true_U(3, 2, {
70  -2.298476964000715e-01, 8.834610176985250e-01,
71  -5.247448187602936e-01, 2.407824921325463e-01,
72  -8.196419411205157e-01, -4.018960334334318e-01});
73 
74  DenseMatrix<Number> true_VT(2, 2, {
75  -6.196294838293402e-01, -7.848944532670524e-01,
76  -7.848944532670524e-01, 6.196294838293400e-01});
77 
78  DenseVector<Real> true_sigma = {9.525518091565109e+00, 5.143005806586446e-01};
79 
80  // Tolerance is bounded by the double literals above and by using Real
81  const Real tol = std::max(Real(1e-12), TOLERANCE*TOLERANCE);
82 
83  for (unsigned i=0; i<U.m(); ++i)
84  for (unsigned j=0; j<U.n(); ++j)
85  LIBMESH_ASSERT_NUMBERS_EQUAL( U(i,j), true_U(i,j), tol);
86 
87  for (unsigned i=0; i<VT.m(); ++i)
88  for (unsigned j=0; j<VT.n(); ++j)
89  LIBMESH_ASSERT_NUMBERS_EQUAL( VT(i,j), true_VT(i,j), tol);
90 
91  for (unsigned i=0; i<sigma.size(); ++i)
92  LIBMESH_ASSERT_FP_EQUAL(sigma(i), true_sigma(i), tol);
93  }
static constexpr Real TOLERANCE
unsigned int m() const
void svd(DenseVector< Real > &sigma)
Compute the singular value decomposition of the matrix.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual unsigned int size() const override final
Definition: dense_vector.h:104
Defines a dense vector for use in Finite Element-type computations.
Definition: dof_map.h:74
unsigned int n() const

The documentation for this class was generated from the following file: