Simple Markov chain
A exercise in a book called Introduction to linear algebra by Strang (2016) first defines a matrix of coefficients ($\mathbf{A}$), a vector of starting values ($\mathbf{u}_1$) and then asks for computing successive values $\mathbf{Au}_1 = \mathbf{u}_2$, $\mathbf{Au}_2 = \mathbf{u}_3$, $\mathbf{Au}_3 = \mathbf{u}_4$, and to explore if any interesting properties appear. Also, the exercise asks for a program that does the computation in some programming language, so let’s see how to do this in C++.
Firstly, the defined matrix of coefficients and first input vector in the exercise are defined as
$$ \phantom{,} \ \mathbf{A} = \begin{bmatrix} 0.8 & 0.3 \\ 0.2 & 0.7 \end{bmatrix} % \ , \quad % \mathbf{u}_1 = \begin{bmatrix} 1 \\ 0 \end{bmatrix} \ , $$respectively.
Now to write the program, one needs to decide how to represent matrices in C++. One to do this is to use the well known Eigen library, that has a simple syntax for doing the computing in discussion. There’s of course many ways for making use of the Eigen library, but since it’s available in the Arch Linux repository, I just ended up installing it from there.
The syntax for defining a matrix in Eigen differs from that used in
MATLAB/Octave, for example, but is still straightforward. To
initialize $\mathbf{A}$ and $\mathbf{u}_1$, first a Eigen::MatrixXd
,
which is defined in the <Eigen/Dense>
header of Eigen, can be
instantiated by writing
Eigen::MatrixXd A(2,2);
and
Eigen::MatrixXd u(2,1); // Initial of u which is the same as u_1
Then the coefficients of $\mathbf{A}$ and $\mathbf{u}_1$ can be set by typing
A << 0.8, 0.3,
0.2, 0.7;
and
u << 0.0, 1.0;
After this, solving the iterations $\mathbf{Au}_1 = \mathbf{u}_2$,
$\mathbf{Au}_2 = \mathbf{u}_3$, and so forth, is just a simple matter of
using a for
-loop as follows,
for (int i = 0; i < 100; i++) {
u = A * u; // The "A * u" call is equal to "Au_{i-1}"
std::cout << u(0) << ',' << u(1) << '\n';
}
say, where iterations are continued up to the total of $100$ times, for
example. The call to std::cout
at line 2
is in place so that the output
can be saved to a CSV-file.
Using the gcc along with pkg-config
for compiling the program
with the Eigen library can be made by writing the command
$ g++ $(pkg-config --cflags eigen3) markovchain.cpp -o markovchain
to the terminal. Then running the executable as a command
$ ./markovchain | head -n 10
in order to get the first ten values, the output is
0.3,0.7 # u_2
0.45,0.55 # u_3
0.525,0.475 # u_4
0.5625,0.4375 # u_5
0.58125,0.41875 # u_6
0.590625,0.409375 # u_7
0.595312,0.404687 # u_8
0.597656,0.402344 # u_9
0.598828,0.401172 # u_10
0.599414,0.400586 # u_11
The original output does not have the comments.
By observing the first decimals it is possible to see that there’s a rapid convergence to values $0.5\ldots$ and $0.4\ldots$ in the output.
Piping the full output of the ./markovchain
to a file and inspecting the
file, it can be seen that with the above decimal precision the output of
the program converges to (0.6,04)
at $\mathbf{u}_{22}$ even though at the
first iteration, i.e. at $\mathbf{u}_2 = [ \ u_{1_2} \ \ u_{2_2} \ ]^\top$,
$u_{1_2} < u_{2_2} \ $.
This can be seen by plotting the values from $\mathbf{u}_2$ to $\mathbf{u}_{25}$ by letting $x$-axis represent the number of iteration and $y$-axis the value of both components at a given iteration:
Source code listings
Program used for computing the Markov chain
#include <iostream>
#include <Eigen/Dense>
int main(int argc, char ** argv) {
Eigen::MatrixXd A(2,2), u(2,1);
A << 0.8, 0.3,
0.2, 0.7;
u << 0.0, 1.0;
std::cout << "x,y" << '\n';
for (int i = 0; i < 100; i++) {
u = A * u;
std::cout << u(0) << ',' << u(1) << '\n';
}
return 0;
}
References
Strang, G. (2016). Introduction to linear algebra (5th ed.). Wellesley-Cambridge Press.