C++ for Scientists - Technische Universität Dresden
C++ for Scientists - Technische Universität Dresden
C++ for Scientists - Technische Universität Dresden
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
56 CHAPTER 2. <strong>C++</strong> BASICS<br />
A function computing the inverse must return the same value and also pass the test agains<br />
identity:<br />
Matrix A inverse(inverse(A));<br />
cout ≪ ”inverse(A) is \n” ≪ A inverse ≪ ”A ∗ AI is\n” ≪ Matrix(A inverse ∗ A);<br />
assert(one norm(Matrix(A inverse ∗ A − I)) < 0.1);<br />
After establishing tests <strong>for</strong> all components of our calculation we start with their implementations.<br />
The first function we program is the inversion of an upper triangular matrix. This function<br />
takes a dense matrix as argument and returns another matrix:<br />
dense2D inline inverse upper(dense2D const& A) {<br />
}<br />
Since we do not need another copy of the input matrix we pass it as reference. The argument<br />
shall not be changed so we can pass it as const. The constancy has several advantages:<br />
• We improve the reliability of our program. Arguments passed as const are guaranteed<br />
not to change, if we accidentally modify them the compiler will tell us and abort the<br />
compilation. There is a way to remove the constancy but this should only be used as<br />
last resort, e.g. <strong>for</strong> interfacing obsolete libraries written by others. Everything you write<br />
yourself can be realized without eliminating the constancy of arguments.<br />
• Compilers can optimize better when the objects are guaranteed not to alter.<br />
• In case of references, the function can be called with expressions. Non-const references<br />
require to store the expression into a variable and pass the variable to the function.<br />
Another comment, people might tell you that it is too expensive to return containers as results<br />
and it is more efficient to use references. This is true — in principle. For the moment we accept<br />
this extra cost and pay more attention to clarity and convenience. Later in this book we will<br />
introduce techniques how to minimize the cost of returning containers from functions.<br />
So much <strong>for</strong> the function signature, let us now turn our attention to the function body. The<br />
first thing we do is verifying that our argument is valid. Obviously the matrix must be square:<br />
const unsigned n= num rows(A);<br />
assert(num cols(A) == n); // Matrix must be square<br />
The number of rows is needed several times in this function and is there<strong>for</strong>e stored in a variable,<br />
well constant. Another prerequisite is that the matrix has no zero entries in the diagonal. We<br />
leave this test to the triangular solver.<br />
Speaking of which, we can get our inverse triangular matrix with a triangular solver of a linear<br />
system, which we find in MTL4, more precisely the k-th vector of U −1 is the solution of<br />
Ux = ek<br />
where ek is the k-th unit vector. First we define a temporary variable <strong>for</strong> the result.<br />
dense2D Inv(n, n);<br />
Then we iterate over the columns of Inv: