MyraMath
sLDLTSolver3


Source: tests/dense/RLDLTSolver.cpp

1 // ========================================================================= //
2 // This file is part of MyraMath, copyright (c) 2014-2019 by Ryan A Chilton //
3 // and distributed by MyraCore, LLC. See LICENSE.txt for license terms. //
4 // ========================================================================= //
5 
11 // Containers.
13 #include <myramath/dense/Matrix.h>
16 
17 // Algorithms.
19 #include <myramath/dense/gemm.h>
20 #include <myramath/dense/syrk.h>
23 #include <myramath/dense/inverse.h>
24 
25 // Solver class under test.
27 
28 // Serialization testing.
30 
31 // Reporting.
33 #include <tests/myratest.h>
34 
35 using namespace myra;
36 using namespace myra_stlprint;
37 
38 namespace {
39 
40 template<class Precision> void test(int I, int J, Precision tolerance)
41  {
42  myra::out() << typestring<Precision>() << std::endl;
43  // Construct random symmetric matrix, A = G+G'
44  auto G = Matrix<Precision>::random(I,I);
45  auto A = G + transpose(G);
46  // Restrict A to lower triangle, generate LDL' solver for it.
48  typedef RLDLTSolver<Precision> Solver;
49  Solver solver(L);
50  myra::out() << "inertia = " << solver.inertia() << std::endl;
51  // Solve A*X=B
52  {
53  auto X = Matrix<Precision>::random(I,J);
54  auto B = A*X;
55  solver.solve(B,'L','N');
56  Precision error = frobenius(B-X)/frobenius(X);
57  myra::out() << " |inv(A)*B-X| = " << error << std::endl;
58  REQUIRE(error < tolerance);
59  }
60  // Solve X*A=B
61  {
62  auto X = Matrix<Precision>::random(J,I);
63  auto B = X*A;
64  solver.solve(B,'R','N');
65  Precision error = frobenius(B-X)/frobenius(X);
66  myra::out() << " |B*inv(A)-X| = " << error << std::endl;
67  REQUIRE(error < tolerance);
68  }
69  // Generate symmetric schur complement.
70  {
71  // RLDLTSolver is well suited for computing symmetric schur complements,
72  // S = Bt*inv(A)*B. Try it out and compare to a reference calculation.
73  // (In the case of sparse A/B, this approach is dramatically more efficient)
74  // Reference answer.
75  auto B = Matrix<Precision>::random(I,J);
76  LowerMatrix<Precision> S1(transpose(B)*inverse(A)*B);
77  // Structure exploiting answer, approximately half the work and inplace on B.
78  Precision one(1);
79  int n_plus = solver.inertia().first;
80  int n_minus = solver.inertia().second;
82  solver.solveL(B,'L','N');
83  syrk_inplace(S2, B.top(n_plus), 'T', one, one);
84  syrk_inplace(S2, B.bottom(n_minus), 'T',-one, one);
85  // Compare/report.
86  Precision error = frobenius(S1-S2) / frobenius(S1);
87  myra::out() << " |B'*inv(A)*B - (L\\B)'*I*(L\\B)| = " << error << std::endl;
88  REQUIRE(error < tolerance);
89  }
90  // Generate unsymmetric schur complement.
91  {
92  // RLTDTSolver can also do unsymmetric schur complements, S = Ct*inv(A)*B.
93  // Not especially efficient, but still tricky so it's demonstrated here.
94  // (In the case of sparse A/B/C, this approach is dramatically more efficient)
95  // Reference answer.
96  auto B = Matrix<Precision>::random(I,J);
97  auto C = Matrix<Precision>::random(I,J+3);
98  Matrix<Precision> S1 = (transpose(C)*inverse(A)*B);
99  // Structure exploiting answer.
100  Precision one(1);
101  int n_plus = solver.inertia().first;
102  int n_minus = solver.inertia().second;
103  Matrix<Precision> S2(J+3,J);
104  solver.solveL(B,'L','N');
105  solver.solveL(C,'L','N');
106  gemm_inplace(S2, C.top(n_plus) , 'T', B.top(n_plus) , 'N', one, one);
107  gemm_inplace(S2, C.bottom(n_minus), 'T', B.bottom(n_minus), 'N', -one, one);
108  // Compare/report.
109  Precision error = frobenius(S1-S2) / frobenius(S1);
110  myra::out() << " |C'*inv(A)*B - (L\\C)'*I*(L\\B)| = " << error << std::endl;
111  REQUIRE(error < tolerance);
112  }
113  // Save solver to disk, then solve A*X=B again.
114  {
115  VectorStream inout;
116  solver.write(inout);
117  Solver saved(inout);
118  auto X = Matrix<Precision>::random(I,J);
119  auto B = A*X;
120  saved.solve(B,'L','N');
121  Precision error = frobenius(B-X)/frobenius(X);
122  myra::out() << " |inv(A)*B-X| (saved) = " << error << std::endl;
123  REQUIRE(error < tolerance);
124  }
125  };
126 
127 template<class Precision> void test3(Precision tolerance)
128  {
129  myra::out() << typestring<Precision>() << std::endl;
130  // Construct A = [0 1 0; 1 0 0; 0 0 1], a problematic example.
131  auto A = Matrix<Precision>::zeros(3,3);
132  Precision a10 = random<Precision>();
133  A(1,0) = a10;
134  A(0,1) = a10;
135  A(2,2) = 1;
136  // Restrict A to lower triangle, generate LDL' solver for it.
138  typedef RLDLTSolver<Precision> Solver;
139  Solver solver(L);
140  // Solve A*X=B
141  auto X = Matrix<Precision>::random(3,2);
142  auto B = A*X;
143  solver.solve(B,'L','N');
144  Precision error = frobenius(B-X)/frobenius(X);
145  myra::out() << " |inv(A)*B-X| = " << error << std::endl;
146  REQUIRE(error < tolerance);
147  }
148 
149 } // namespace
150 
151 ADD_TEST("sLDLTSolver","[dense][solver]")
152  { test<float>(43,14,1.0e-3f); }
153 
154 ADD_TEST("dLDLTSolver","[dense][solver]")
155  { test<double>(82,45,1.0e-10); }
156 
157 ADD_TEST("sLDLTSolver3","[dense][solver]")
158  { test3<float>(1.0e-3f); }
159 
160 ADD_TEST("dLDLTSolver3","[dense][solver]")
161  { test3<double>(1.0e-10); }
static Matrix< Number > zeros(int I, int J)
Generates a zeros Matrix of specified size.
Definition: Matrix.cpp:357
Routines for computing Frobenius norms of various algebraic containers.
static Matrix< Number > random(int I, int J)
Generates a random Matrix of specified size.
Definition: Matrix.cpp:353
CMatrixRange< Number > bottom(int i) const
Returns a MatrixRange over the i bottommost rows, this(I-i:I,:)
Definition: Matrix.cpp:225
Wraps a std::vector<char>, presents it as both an InputStream and OutputStream. Useful for hygienic u...
Definition: VectorStream.h:22
Range construct for a lower triangular matrix stored in rectangular packed format.
Definition: syntax.dox:1
Definition: stlprint.h:32
Specialized container for a lower triangular matrix, O(N^2/2) storage. Used by symmetry exploiting ma...
Returns a transposed copy of a Matrix. The inplace version only works on a square operand...
Routines for printing the contents of various std::container&#39;s to a std::ostream using operator <<...
Various utility functions/classes related to scalar Number types.
A stream that serialize/deserializes to std::vector<char> buffer.
General purpose dense matrix container, O(i*j) storage.
Overwrites a LowerMatrix, DiagonalMatrix, or square Matrix with its own inverse. Or, returns it as a copy.
Simplistic random number functions.
Factors A = L*D*L&#39;, where D is a "sign matrix" of the form [I 0; 0 -I]. Presents solve methods...
Definition: RLDLTSolver.h:30
A solver suitable for a real symmetric Matrix (indefinite is fine). Encapsulates sytrf() ...
Routines for symmetric rank-k updates, a specialized form of Matrix*Matrix multiplication.
Variety of routines all for dense Matrix*Matrix multiplies. Delegates to the BLAS.
CMatrixRange< Number > top(int i) const
Returns a MatrixRange over the i topmost rows, this(0:i,:)
Definition: Matrix.cpp:207


Results: [PASS]

float
|inv(A)*B-X| = 5.5718e-08


Go back to Summary of /test programs.