www.michael-schaefer.org/en/2015-04-01T00:00:00+02:00The difficulty of mass screenings2015-04-01T00:00:00+02:00Michael Schaefertag:,2015-04-01:en/2015/04/the-difficulty-of-mass-screenings<p>For many diseases there are so-called screening programs where more or less big parts of the population are called to undergo certain tests with the goal to early discover these diseases. The probably best known example is the mammography screening. Especially there public critizism is frequently voiced because the number of misdiagnoses is high. In this article I want to use relatively simple mathematics to deduce an index that allows to judge the quality of such tests. As specific examples I present the already mentioned mammography as well as HIV tests.</p>
<h2>Conditional probabilities</h2>
<p>The basis of the modelling will be <a href="http://en.wikipedia.org/wiki/Conditional_probability">conditional probabilities</a>. Therefore we consider two events <code>$A$</code> and <code>$B$</code>. The conditional probability <code>$P(B \mid A)$</code> (read: Probability of <code>$B$</code> given <code>$A$</code>) denotes the probability of the occurrence of event <code>$B$</code> under the condition that event <code>$A$</code> already happend. As an example we throw two dice. Event <code>$A$</code> should be "the first die shows 3". If now <code>$B$</code> is the event "the sum on both dice is exactly 3", then obviously <code>$P(B \mid A) = 0$</code>, because the first one already shows 3 and the second will add at least one. If on the other hand <code>$B$</code> reads "the sum on both dice is bigger than 6", then <code>$P(B \mid A) = \frac{3}{6} = \frac{1}{2}$</code>, because this will be true in case the second die shows 4, 5 or 6.</p>
<p>For the calculus of conditional probabilities, <a href="http://en.wikipedia.org/wiki/Bayes%27_theorem">Bayes' theorem</a> is essential. Its formulation is
<code>$$ P(B \mid A) = \frac{P(A \mid B) \cdot P(B)}{P(A)}. $$</code>
Thus the formula describes how to calculate a conditional probability from the inverted one. In addition, let <code>$\bar{B}$</code> denote the complementary event of <code>$B$</code>, then the <a href="http://en.wikipedia.org/wiki/Law_of_total_probability">Law of total probability</a> reads:
<code>$$ P(A) = P(A \mid B) \cdot P(B) + P(A \mid \bar{B}) \cdot P(\bar{B}). $$</code>
Inserting this into the first formula we end up with
<code>$$ \begin{equation} P(B \mid A) = \frac{P(A \mid B) \cdot P(B)}{P(A \mid B) \cdot P(B) + P(A \mid \bar{B}) \cdot P(\bar{B})} \label{eq:1} \end{equation} $$</code></p>
<h2>Stochastic model</h2>
<p>For a general modelling we consider a population whose members have a certain condition or they don't. Furthermore there should be a test that for every person gives either a positive or a negative result. Let <code>$H$</code> (healty) be the event of a person being healthy and <code>$P$</code> (positive) that the test turns out positive. The corresponding complementary probabilities (the person being sick resp. the test turning out negative) shall be <code>$S$</code> (sick) and <code>$N$</code> (negativ). Further important quantities are:</p>
<ul>
<li>prevalence <code>$p$</code>, that is the amount of the population being sick</li>
<li>sensitivity <code>$\alpha$</code>, that is the amout of sick people who get a positive test result (Example: <code>$\alpha = 0.95$</code> means that 95 out of 100 sick people will actualy get a positive result)</li>
<li>specificity <code>$\beta$</code>, that is the amount of healthy people who get a negative test result (Example: <code>$\beta = 0.99$</code> means that 99 out of 100 healthy people will acually get a negative result)</li>
</ul>
<p>Translated to the world of probabilities we obviously have <code>$p = P(S)$</code>, <code>$\alpha = P(P \mid S)$</code> and <code>$\beta = P(N \mid H)$</code>, and the complementary probabilities are given as
<code>$$ \begin{align*} P(H) &= 1 - P(S) = 1-p, \\ P(P \mid H) &= 1 - P(N \mid H) = 1-\beta, \\ P(N \mid S) &= 1-P(P \mid S) = 1-\alpha. \end{align*} $$</code>
If we define
<code>$$ \gamma := \frac{P(H \mid P)}{P(S \mid P)} $$</code>
then <code>$\gamma$</code> denotes how much more likely it is to be healthy despite a positive test result. A good test should therefore have a small value of <code>$\gamma$</code>. Together with formula <code>$\eqref{eq:1}$</code> we calculate
<code>$$
\begin{align*}
P(H \mid P) &= \frac{P(P \mid H) \cdot P(H)}{P(P \mid H) \cdot P(H) + P(P \mid S) \cdot P(S)} \\ &= \frac{(1-\beta)(1-p)}{(1-\beta)(1-p) + \alpha p}, \\
P(S \mid P) &= \frac{P(P \mid S) \cdot P(S)}{P(P \mid S) \cdot P(S) + P(P \mid H) \cdot P(H)} \\ &= \frac{\alpha p}{\alpha p + (1-\beta)(1-p)}
\end{align*}
$$</code>
Putting this into the definition of <code>$\gamma$</code> one can cancel out a lot of things, resulting in the following formula which does only depend on the test quantities <code>$p$</code>, <code>$\alpha$</code> and <code>$\beta$</code>:
<code>$$ \begin{equation} \gamma = \frac{1-\beta}{\alpha} \cdot \frac{1-p}{p}. \label{eq:2} \end{equation} $$</code></p>
<h2>Example: Mammography</h2>
<p>It is not very easy to get reliable and recent values for sensitivity and prevalence for mammography. The <a href="http://de.wikipedia.org/wiki/Mammographie#Kritik_am_Mammographie-Screening">german Wikipedia article</a> refers to <code>$\alpha=0.83$</code> and <code>$\beta=0.97$</code>. These values will be used since they roughly agree with some studies I found, like <a href="http://www.ncbi.nlm.nih.gov/pubmed/11002452">this one</a>. For the prevalence there are some statements from the <a href="http://edoc.rki.de/documents/rki_fv/re2vZ2t28Ir8Y/PDF/23GSS31yB0GKUhU.pdf">Robert Koch Institute</a> (p. 77ff), that suggest a value of <code>$p = 0.009$</code> for Germany. If we plug all these values into formula <code>$\eqref{eq:2}$</code> we get
<code>$$ \gamma_{\text{mammo}} = \frac{0.03 \cdot 0.991}{0.83 \cdot 0.009} \approx 3.98 $$</code>
This means that when a woman undergoes a mammography and gets a positive result it is nevertheless nearly four times more likely that she does not have breast cancer than that the diagnosis is correct. The imbalance becomes even more obvious if we translate this into absolute numbers (I will, however, not carry out the neccessary calculations). From the data of the Robert Koch Institute we can deduce that at the time of the study there were roughly 42.9 million woman in Germany. If each one of them would undergo a mammography, this would end up in about 1.6 million positive test. But from these, not less than 1.2 million are false positives and merely 400,000 acually have breast cancer. These numbers alone should be reason enough to challenge the value of such screenings.</p>
<h2>Example 2: HIV test</h2>
<p>Another very relevant example are HIV tests. Although the standard tests partly have an extremely high accuracy of up to <code>$\alpha = \beta = 0.999$</code>, the validity of a single test is rather limited.The reason for this is a very low prevalence: The <a href="http://www.rki.de/DE/Content/InfAZ/H/HIVAIDS/Epidemiologie/Daten_und_Berichte/EckdatenDeutschland.pdf?__blob=publicationFile">Robert Koch Institute</a> estimated the number of HIV infected people in Germany to about 80,000 at the end of 2013. At that time this meant a prevalence of <code>$p \approx 0.0011$</code>. From this we deduce
<code>$$ \gamma_{\text{HIV}} = \frac{0.001 \cdot 0.9989}{0.999 \cdot 0.0011} \approx 0.91 $$</code>
This in turn means that the likelihood to be healthy in case of a positive test result is only slightly less than the one of being actually sick. That is the reason why a person with a positive HIV test will directly undergo some additional tests to get a proper result.</p>Pattern formation in simple systems2015-02-08T00:00:00+01:00Michael Schaefertag:,2015-02-08:en/2015/02/pattern-formation-in-simple-systems<p>Last summer, together with a colleague I held an introductory course in the programming language <a href="http://www.python.org">Python</a> at the University of Münster where the participents were especially taught in the basics of numerical programming with this language. At that time I stumbled over a paper by <a href="http://www.sciencemag.org/content/261/5118/189">J. E. Pearson (1993)</a> in which a mathematical model for pattern formation in biological systems was presented that despite its relative simplicity is able to produce a variety of complex dynamics. Since the implementation is already possible with the means presented within the course I presented it as a motivating example in the end. Months had passed until this week I read an article at Spiegel online (a german newsportal) where a paper <a href="http://www.nature.com/nmat/journal/vaop/ncurrent/full/nmat4202.html">(Stoop et al., 2014)</a> about a model for pattern formation on elastic surfaces was described. Because the images presented therein had strong similarities with my own simulations I decided to make an article about it.</p>
<h2>Modelling and numerical ansatz</h2>
<p>This section is devoted to a short demonstration of the modelling and the basics of the numerical treatment and is thought for the mathematically experienced readers of this blog. Anyone else who is just interested in the fancy images and videos may jump forward to the next second.</p>
<p>The model presented in Pearson's paper "Complex Patterns in a Simple System" is based on the chemical reaction of two different species whose contentrations will be denoted by <code>$U(t)$</code> and <code>$V(t)$</code>. The fundamental reaction equations are the following:
<code>$$
\begin{align*}
U + 2V &\to 3V \\
V &\to P
\end{align*}
$$</code>
Vividly speaking this means that one part of substance <code>$U$</code> reacts with two parts of <code>$V$</code> to form three part of <code>$V$</code>, while <code>$V$</code> itself decomposes into a further product <code>$P$</code>. This is why we call <code>$U$</code> the activator, because it allows the reaction to happen in the first place. It is also clear that <code>$U$</code> has to be refilled continuously to keep the reaction going.</p>
<p>From this reaction equations we can deduce a mathematical model for the concentrations <code>$U(t)$</code> and <code>$V(t)$</code>. This leads to the following system of partial differential equations:
<code>$$
\begin{align*}
\partial_t U(t) &= D_U \Delta U(t) - U(t) V^2(t) + F(1 - U(t)) \\
\partial_t V(t) &= D_V \Delta V(t) + U(t) V^2(t) - (F+k) V(t)
\end{align*}
$$</code>
together with periodic boundary conditions. The parameters are the diffusion constants <code>$D_U, D_V$</code>, a feed rate <code>$F$</code> and a reaction rate <code>$k$</code> of the second equation relative to the first one. As we can see this is a coupled system of reaction-diffusion equations. This system is numerically very stable so that a simple explicit Euler scheme for the time discretization together with finite difference in space is good enough to do the simulations. The explicit time stepping scheme has the advantage that no cumbersome decoupling mechanism is needed. For the step sizes taking both <code>$\Delta t$</code> and <code>$\Delta h$</code> as 1 is sufficient. As computational domain we use <code>$[0,N]^2$</code> for an integer <code>$N$</code> which is typically taken as 256 or 512. For the simulations, both diffusion constants were fixed while both the feed rate an the reaction rate could be changed. For the images and videos the concentration profile <code>$V(t)$</code> was used.</p>
<h2>Simulation results</h2>
<p>Since the simulation speed of the original Python implementation was not satisfactory for me I decided to re-implement it in C++ using the <a href="http://www.qt-project.org">Qt framework</a>. The source code of both programs can be found on my Github account under <a href="https://github.com/michaelschaefer/grayscott">github.com/michaelschaefer/grayscott</a>. The results presented here were created using the C++ version, a screenshot of which is shown here:
<img alt="Screenshot of the GUI of the program "grayscott"" src="/files/images/grayscott/grayscottgui_en.png" />
The program allows to vary the relevant model parameters as well as the resolution of the resulting images. Additionally, sequences of images can automatically be saved to disk so that they can be converted into videos by a suitable tool. Although the system dynamics reacts very sensitively to changes in the parameters we can essentially distinguish three categories each of which is presented below, together with some simulation results. By clicking the images you get to the associated video file.</p>
<ul>
<li>bacteria-like patterns (even with cell division!) consisting of individual, stationary or barely moving points <em>(F=0.035, k=0.065, N=512. Video size: 2,4MB)</em> <a href="/files/videos/grayscott/bacteria.mp4"><img alt="bacteria-like pattern" src="/files/images/grayscott/bacteria.png" /></a></li>
<li>fingerprint-like patterns made of many convoluted lines <em>(F=0.035, k=0.06, N=512. Video size: 2.5MB)</em> <a href="/files/videos/grayscott/fingerprint.mp4"><img alt="fingerprint-like pattern" src="/files/images/grayscott/fingerprint.png" /></a></li>
<li>chaotic patterns of permanently appearing and disappearing points <em>(F=0.02, k=0.055, N=512. Video size: 8.9MB)</em> <a href="/files/videos/grayscott/unstable.mp4"><img alt="chaotic pattern" src="/files/images/grayscott/unstable.png" /></a></li>
</ul>
<p>As an additional gimmick I have create a kind of map of this model by the following procedure: First, simulations were run for a variety of different values for the parameters F and k. After some time a (hopefully) representative image for the particular dynamics was taken. In the end, all these images were stitched together to a large image, the map. All in all, 81 different values for F and 41 for k were used so that the final map consists of 3,321 individual images. Calculations took several hours on a multi-core system. Though using JPEG compression, the image is still about 3.6MB in size, so that I decided not to integrate it directly but just placing a <a href="/files/images/grayscott/parametermap.jpg">link</a> to it. The values of F are constant in each row while k stays the same along the columns.</p>The problem with the queens2014-11-26T00:00:00+01:00Michael Schaefertag:,2014-11-26:en/2014/11/the-problem-with-the-queens<p>For the last two weeks the chess world championships were held. I did not take a very great interest in it but in that context I stumbled over an old riddle, the so called <a href="http://en.wikipedia.org/wiki/Eight_queens_puzzle">eight queens puzzle</a>. With this article I want to introduce that problem together with a solution stategy. The original idea behind the eight queens puzzle is to take a usual chess board of 8 x 8 fields and place eight queens on it in such a way that none of them can capture another one (as a reminder: a queen can move horizontally, vertically and diagonally). The question for the total number of such solutions was posed first in 1848 and answered in 1850: There are exactly 92.</p>
<h2>Generalization and solution ansatz</h2>
<p>Of course this problem can be and was generalized in order to ask for the number of possibilities to place <code>$n$</code> queens on a chess board of size <code>$n$</code> x <code>$n$</code> without conflicts. For small boards this can be done systematically by hand, but with <code>$n$</code> growing, this technique quickly goes to the limits of its capacity. It is therefore a natural idea to write a computer program to do that task. As a first step one obviously needs a proper solution strategy.</p>
<p>The most basic idea would be to let the computer systematically test all distributions of the queens on the chess board and decide if they are valid. But it comes clear that this if by far to inefficient, because most of the distributions already become invalid after a few of the <code>$n$</code> queens. Apart from this even for the moderately sized original problem there are no less than <code>$\frac{64!}{56!} \approx 1.8 \cdot 10^{14}$</code>, that is about 180 trillion, combinations to test.</p>
<p>A much more efficient and at the same time not much more complicated algorithm is the so called <a href="http://en.wikipedia.org/wiki/Backtracking">backtracking</a>. Loosely speaking, the idea of this technique is to place queen after queen to the board until a conflict arises, meaning that one of the queens can capture another. Once this happens we step back to the last state that was free of conlicts. Starting from there we then construct another solution. To get a better feeling of it, we want to perform the method exemplary on a board of size 4.</p>
<p><object data="/files/images/chess-backtracking.svg"><img src="/files/images/chess-backtracking.png" /></object></p>
<p><em>Figure (a)</em>: We start by placing a queen in the upper left field. The fields guarded by this queen are marked in gray, they are unavailable for the further placing. <em>Figure (b)</em>: The first valid field for the second queen in the third in the second row. If the figure is placed there, all but one fields are guarded. So this cannot lead to a valid solution, since we have to put two more queens to the board. <em>Figure (c)</em>: We have to start again with the last conflict free state from figure (a). The field marked red is the one that previously led to a problem. <em>Figure (d)</em>: We have to place the second queen on the last field of the second row. If we again mark the guarded fields in gray we see that this cannot result in a valid solution, too: The remaining two queens can only be placed in such a way that they can capture each other. <em>Figure (e)</em>: We have seen that placing the first queen in the upper left field will not give a valid solution. We thus move on and instead place it on the second field of the first row. <em>Figures (f) - (h)</em>: Marking guarded fields in gray and placing a queen on the first field that remains free now leads to a valid solution of the problem. If we continue this way until the first queen reaches the last field of the first row, we will ultimately find all the solutions.</p>
<h2>Programming</h2>
<p>For small programs of this kind I usually like to use <a href="http://www.python.org">Python</a>. However, for performance reasons I decided to use C++ this time. If you want to have a look at the code you can do this: <a href="/files/code/nqueens.cc">nqueens.cc</a>. I do not want to present the entire program but just to explain the most important function, namely the backtracking algorithm itself.</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31</pre></div></td><td class="code"><div class="highlight"><pre><span class="kt">int</span> <span class="nf">countSolutions</span><span class="p">(</span><span class="kt">int</span><span class="o">*</span> <span class="n">queens</span><span class="p">,</span> <span class="kt">int</span> <span class="n">N</span><span class="p">,</span> <span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="kt">int</span> <span class="n">count</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="n">N</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
<span class="c1">// print progress</span>
<span class="n">cout</span> <span class="o"><<</span> <span class="p">(</span><span class="mi">100</span> <span class="o">*</span> <span class="n">i</span> <span class="o">/</span> <span class="n">N</span><span class="p">)</span> <span class="o"><<</span> <span class="s">"% done</span><span class="se">\r</span><span class="s">"</span> <span class="o"><<</span> <span class="n">flush</span><span class="p">;</span>
<span class="k">else</span> <span class="p">{</span>
<span class="c1">// check for direct collision with upper row</span>
<span class="kt">int</span> <span class="n">dx</span> <span class="o">=</span> <span class="n">queens</span><span class="p">[</span><span class="n">n</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="n">i</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">dx</span> <span class="o">></span> <span class="o">-</span><span class="mi">2</span> <span class="o">&&</span> <span class="n">dx</span> <span class="o"><</span> <span class="mi">2</span><span class="p">)</span>
<span class="k">continue</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">queens</span><span class="p">[</span><span class="n">n</span><span class="p">]</span> <span class="o">=</span> <span class="n">i</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">isCollisionFree</span><span class="p">(</span><span class="n">queens</span><span class="p">,</span> <span class="n">N</span><span class="p">,</span> <span class="n">n</span><span class="p">))</span> <span class="k">continue</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">n</span><span class="o">+</span><span class="mi">1</span> <span class="o">==</span> <span class="n">N</span><span class="p">)</span> <span class="p">{</span>
<span class="n">count</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="kt">int</span><span class="o">*</span> <span class="n">tmp</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">int</span><span class="p">[</span><span class="n">N</span><span class="p">];</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o"><</span> <span class="n">N</span><span class="p">;</span> <span class="o">++</span><span class="n">j</span><span class="p">)</span> <span class="n">tmp</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">queens</span><span class="p">[</span><span class="n">j</span><span class="p">];</span>
<span class="n">count</span> <span class="o">+=</span> <span class="n">countSolutions</span><span class="p">(</span><span class="n">queens</span><span class="p">,</span> <span class="n">N</span><span class="p">,</span> <span class="n">n</span><span class="o">+</span><span class="mi">1</span><span class="p">);</span>
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">j</span> <span class="o"><</span> <span class="n">N</span><span class="p">;</span> <span class="o">++</span><span class="n">j</span><span class="p">)</span> <span class="n">queens</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">=</span> <span class="n">tmp</span><span class="p">[</span><span class="n">j</span><span class="p">];</span>
<span class="k">delete</span><span class="p">[]</span> <span class="n">tmp</span><span class="p">;</span>
<span class="n">tmp</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="n">n</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="n">cout</span> <span class="o"><<</span> <span class="s">"100% done"</span><span class="p">;</span>
<span class="k">return</span> <span class="n">count</span><span class="p">;</span>
<span class="p">}</span>
</pre></div>
</td></tr></table>
<p><em>Line 1</em>: Let's first explain the function's parameters: queens is an array storing the positions of the queens in their respective row (it is easy to see that for a valid solution each row has to contain exactly one queen), where not yet places queens are represented by -1. The value N denotes the number of dimensions of the chess board and n is the number of the queen to be placed next (starting with 0!). <em>Line 8-11</em>: There is already at least one queens on the board. Thus we can check if the field i in row n which is to be filled next is already guarded by a queen in row n-1. That particular case will then not lead to a valid solution and we can directly proceed with the next field. <em>Line 14</em>: The n-th queen is placed in the n-th row to the i-th field. <em>Line 16</em>: If the queen being of that position causes a collision we can reject the current configuration. <em>Line 18</em>: A valid solution was found since the N-th queen could be placed on the board without conflicts. <em>Line 20-25</em>: The current configuration is free of conflicts but not yet complete. Thus we save the current state in the array tmp and let the solution function call itself recursively, where the third parameter n+1 denotes that the (n+1)-th queen is to be placed on the board next. <em>Line 30</em>: Once the for-loop ran over all fields in the current row, the number of solutions found on the current level n can be returned.</p>
<p>Because the computational time rapidly increases with the size of the board I implemented the program to support parallel computing, meaning that it is able to distribute the calculations over more than one processor. This way we can get the desired result more quickly. To make this work we have to have installed a library implementing the <a href="http://de.wikipedia.org/wiki/Message_Passing_Interface">MPI</a> interface like <a href="http://www.open-mpi.org/">Open MPI</a>. If you use Linux, the following commands (executed on the command line) allow you to compile and execute the program:</p>
<table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2</pre></div></td><td class="code"><div class="highlight"><pre>mpic++ -O3 -o nqueens nqueens.cc
mpirun -n p ./nqueens n
</pre></div>
</td></tr></table>
<p>Here, p is to be replaced by the number of processors to use for parallelization and n by the desired size of the board.</p>
<h2>Results</h2>
<p>The number of solutions are known for boards up to size 26 x 26 and can be checked in the Wikipedia article I already linked. Instead, I want to show that parallelization indeed pays off for this problem. Therefore I ran the program on a multi-processor computer for board sizes 14 to 17 on up to 17 processors. To get a feeling of the complexity please note that on a single core the calculation on the board of size 17 took about 64 minutes on a recent computer.</p>
<p><object data="/files/images/chess-speedup.svg"><img src="/files/images/chess-speedup.png" /></object></p>
<p>The diagram shows the speed-up factor <code>$s_n$</code> that is calculated as follows: If we denote by <code>$t_n$</code> the runtime of the program using <code>$n$</code> processors, then <code>$s_n$</code>is defined as <code>$s_n := \frac{t_1}{t_n}$</code>. In a perfect world we would have <code>$s_n = n$</code> meaning that for example the runtime halves itself when the program runs on two instead of one processor. However, the world is not perfect and thus these ratios are not obtained in practice. But in our case we see that the speed-up for 17 processors is at least 14, which is not bad. Furthermore we see that the parallelization works better with increasing complexity of the problem since the curves grow faster for bigger <code>$n$</code>. This effect is not by coincidental but comes from the computational overhead that has to be done in order to synchronize the computations over the involved processors. If the computational complexity grows this overhead becomes more and more negligible with respect to the overall computational time.</p>Bolognese vegetarian style2014-06-16T00:00:00+02:00Michael Schaefertag:,2014-06-16:en/2014/06/bolognese-vegetarian-style<p>Renouncing meat entirely or to least partly comes into fashion more and more lately. That this doesn't mean we have to quit eating classical recipes containing meat I want to show today using the example of the <a href="http://en.wikipedia.org/wiki/Bolognese_sauce">Ragu alle Bolognese</a>. I own a vegetarian version of this for quite some time, but last weekend I asked myself why not to take the original one and vary it directly. Based on this the following recipe was developed.</p>
<h2>Replacing the meat</h2>
<p>A "true" Bolognese is of course made with minced meat. So the question comes up for an appropriate replacement. Especially in this case soya granule is a good candidate. It can be purchased from different brands over the internet, in health stores or well-assorted super markets. It is best to use relatively fine granule because it doesn't crumble very much while cooking.</p>
<h2>First step: Preparing the soya granule</h2>
<p>Soya granule by nature lacks flavor. Thus it is important to spice it up already while soaking. We use the following ingredients:</p>
<ul>
<li>500 ml water</li>
<li>1 tbs. tomato puree</li>
<li>1 tbs. vegetable broth (instant)</li>
<li>2-3 tbs. soya sauce</li>
<li>150 g soya granule</li>
</ul>
<p>First, boil the water in a pot. Solve the tomato puree in it and add broth and soya sauce. Once the fluid is cooking, add the soya granule, put the pot aside and let the granule soak for 10 minutes. Afterwards, drain the remaining fluid and put it aside (do not throw it away!). The soya granule should dry now so that it can be browned better later.</p>
<h2>Second step: The Soffritto</h2>
<p>The Soffritto (italian, meaning relish) is the basis of many pasta sauces like the Bolognese. It is basically just stewed vegetables that spice up the sauce. Ingredients:</p>
<ul>
<li>60 g butter</li>
<li>1 tbs. olive oil</li>
<li>1 onion (not too small)</li>
<li>2 carrots</li>
<li>1 piece of celeriac</li>
</ul>
<p>Onion and celeriac should be as big that the three vegetables are roughly in a ratio of 1:1:1. Clean the vegetables and dice them. Heat butter and oil in a pot, add the vegetables and stew them at low fire until they are smooth. If you like you can add some parsley and garlic.</p>
<h2>Third step: The sauce itself</h2>
<p>For the last step we prepare the sauce itself. Therefore, the soya granule is browned, combined with different additional ingredients as well as the soffritto and finally boiled down to the desired consistency. We need:</p>
<ul>
<li>1 tbs. tomato puree</li>
<li>a dash of red wine</li>
<li>100 ml milk</li>
<li>1 can of tomatoes (unchopped, 425 ml)</li>
<li>1-2 cloves of garlic</li>
<li>salt, pepper and further spices according to taste (like basil or oregano)</li>
</ul>
<p>First, put the soya granule in a pot and brown in at high fire. Add the tomato puree and wait until it begins to stick to the bottom. Deglaze it with red wine and the fluid from the first step. Add the milk (the purpose according to the original recipe is to make the minced meat more smooth; I keep it for the taste since the same effect doesn't seem to occure for soya granule). If it boils add the tomatoes, chop them with a spatula and squeeze the garlic into the pot. Now let the sauce boil at low fire in the open pot (with the lid closed it cannot boil down) until the desired consistency is reached. This usually takes at least 45 minutes. At the end, flavour it with salt, pepper and eventually further spices.</p>
<h2>Variants</h2>
<ul>
<li>alcohol free: Instead of red wine you can use water, broth or - as long as you have enough of it - just the remaining fluid for deglazing.</li>
<li>vegan: Replace butter by 4-5 tbs. olive oil and leave the milk out. If the amount of fluid seems to be too small without the milk, just add some water or broth.</li>
</ul>
<p>Bon appetit!</p>Forecasting the death of Facebook2014-05-29T00:00:00+02:00Michael Schaefertag:,2014-05-29:en/2014/05/forecasting-the-death-of-facebook<p>Recently there was a little mathematical paper written by <a href="http://arxiv.org/pdf/1401.4208v1.pdf">Cannarella and Spechler (2014)</a> with the title "Epidemiological modelling of online social network dynamics". The basic message of their work is that Facebook will undergo a rapid decline and almost nobody will be interested in it anymore within the next few years. For this unusual statement I started to read the paper myself and implemented the software necessary to reconstruct their results. In the next paragraphs I will try to explain to you the paper with as less advances calculus as possible.</p>
<h2>The model</h2>
<p>The basic idea behind the mathematical model in the paper is what we call an <em>epidemiology model</em> or <em>SIR model</em> where the letters stand for <em>S</em>usceptibles, <em>I</em>nfected and <em>R</em>ecovered. In the world of diseases, <code>$S$</code> represents the number of people who are well but still can get a certain disease, whereas <code>$I$</code> and <code>$R$</code> are the number of already ill resp. cured inhabitants.</p>
<p>There are two crucial assumptions behind this model: the disease is not fatal (meaning that no one dies from it) and people are immunized so that once they recover they will not become ill for a second time. The dynamics of the system is thus the following: A fixed fraction <code>$c > 0$</code> of healthy but still not immunized people from <code>$S$</code> become ill, meaning they move to group <code>$I$</code>. On the other hand, with a fixed fraction <code>$w > 0$</code> people recover and become immune, so they move to group <code>$R$</code>. </p>
<p>Let me put this into mathematical terms. The previous ideas give raise to a system of ordinary differential equations. A differential equation is an equation that relates the derivative of a function (that is its rate of change, i.e. how strong it increases of decreases) with the function itself and other functions. For the SIR model we have three functions, namely <code>$S(t)$</code>, <code>$I(t)$</code> and <code>$R(t)$</code>, where <code>$t$</code> denotes the time. The model then consist in the following three equations:</p>
<p><code>$$
\begin{align}
S'(t) &= -c I(t) S(t) \\
I'(t) &= c I(t) S(t) - w(t) I(t) \label{eq:I} \\
R'(t) &= w I(t) \label{eq:R}
\end{align}
$$</code></p>
<p>Additionally we have to prescribe so called initial values, that is we specify which values the functions should have at time <code>$t = 0$</code>.</p>
<h2>So what about social networks?</h2>
<p>Okay, we have now introduced (and you have hopefully understood) the SIR model. In the paper this model is related to the growth of social networks with the following analogies: <code>$S$</code> is the number of people who haven't joint a certain network yet but would in general be willing to do so, whereas <code>$I$</code> represents the registered users of it and <code>$R$</code> the ones that either turned their backs on the network or (in contrast to the <code>$S$</code> people) where not willing to create an account in the first place. With these information we could start to simulate the life cycle of social networks. However, in the paper they use a slight modification of the original model, namely the <em>irSIR</em> model (where the <em>ir</em> stands for <em>i</em>nfectious <em>r</em>ecovery). The modification affects the equations <code>$\eqref{eq:I}$</code> and <code>$\eqref{eq:R}$</code> that are replaced by the following ones.</p>
<p><code>$$
\begin{align}
I'(t) &= c I(t) S(t) - wI(t) R(t) \tag{2a} \\
R'(t) &= w I(t) R(t) \tag{3a}
\end{align}
$$</code></p>
<p>The idea behind this modification is to model the effect that people who leave a network might make others do the same (if all your friends leave Facebook it becomes more likely that you will do it, too).</p>
<h2>Data acquisition and simulation</h2>
<p>So now we have a model at our hands. But with which data do we want to feed it? The easiest way is to retrieve data from <a href="http://trends.google.com/trends/">Google Trends</a> for some social network. After a few postprocessing steps you can use these data and try to fit the parameters in our model such that <code>$I(t)$</code> behaves like them. But what are the parameters? If you have a look at the equations you see that there five of them: First of all of course the rates <code>$c$</code> and <code>$w$</code>, but also some initial values <code>$S(0)$</code>, <code>$I(0)$</code>, <code>$R(0)$</code>, where the dynamics starts. If you want to have a closer look on how to simulate such a system and and find the optimal parameters, you can download the postprocessed data for Facebook and Myspace together with the necessary computer programs from my <a href="https://github.com/michaelschaefer/social-network-modelling">Github</a>.</p>
<h2>Results</h2>
<p>First of all the authors considered MySpace for a simple reason: Since almost nobody is using MySpace anymore, its life cycle can be considered somewhat complete which makes it a perfect test case for the irSIR model. Below you see a graph with the results. The blue curve are the postprocessed data from Google Trends. They are normalized such that 100 stands for the overall maximum activity. The red curve is the simulation result of <code>$I(t)$</code> with the parameters chosen such that the simulation catches the data as good as possible. You see that the dynamics is caught quite well. The green line marks the points where interest in the network drops below 20% of its maximum, which the authors interpret kind of like the death of the network. In this case, MySpace is to be considered dead since last 2010.</p>
<p><img alt="Simulation result for MySpace" src="/files/images/myspace.svg" /></p>
<p>Now for Facebook. This case is more interesting since the network is still alive (the blue curve is still far above the 20% line). Again I carried out a parameter fitting and the resulting best-fit curve is printed in red. You see that the simulation predicts a rapid decent of interest in Facebook in the near future, with the result that interest in the network will drop below 20% of its maximum and thus cease to exist in late 2015.</p>
<p><img alt="Simulation result for Facebook" src="/files/images/facebook.png" /></p>
<h2>Conclusion</h2>
<p>So how to deal with such results? At the first glance this looks pretty convincing to many people, because there is math involved. But in fact, I would not give too much about it. First, you should question the use of Google Trends for some reasons. Not everyone uses Google as search engine so the data could not be representative. But more important is the fact that in my eyes the amount of search queries containing "Facebook" doesn't relate to the communication happening inside of the network. As long as no one searches for Facebook but still uses it, the network is alive although the model tells a different story. The second - and from a mathematical point of view more severe - objection is the model itself. Fitting curves to data is a nice thing. But using these curves as a measure to predict the future is a delicate task. There is a saying from the famous mathematician John von Neumann who once said: "With four parameters I can fit an elephant, and with five I can make him wiggle his trunk". What he wants to say is that with the right amount of parameters you can explain basically any data with a suitable mathematical model. This leads to the conclusion that our irSIR model might explain the data purely by accident and not because it truly catches the intrinsic behaviour of social networks (indeed, I find that highly questionable). Actually, there may be other mathematical models explaining our Google data as well as the presented one, but with very different predictions for the future.</p>The Lost Vikings2014-05-17T00:00:00+02:00Michael Schaefertag:,2014-05-17:en/2014/05/the-lost-vikings<p>For my very first blog entry I have come up with something somewhat special. Recently, I felt myself put back to my youth like from nowhere. The reason for that was that Blizzard had decided to re-publish my favorite computer game from 20 years ago for free: The Lost Vikings! The English version of this game can be <a href="https://us.battle.net/account/download/?show=classic">downloaded</a> from Blizzards platform battle.net.</p>
<p><img alt="Startup screen of the game "The Lost Vikings"" src="/files/images/thelostvikings-logo.png" /></p>
<h2>A short introduction</h2>
<p>The story behind the game is told quickly: The three vikings Erik, Baleog and Olaf are being kidnapped from their village by the evil space alien Tomator. That guy wants to have the three for his exotic zoos. Due to a short circuit, the three vikings are not materialized at the planned location but directly at Tomator's ship. From there they start their attempt to escape back home.</p>
<p>That much for the story. In the game you control each of the vikings separately. Everyone has different skills: While Erik can jump and run fast, Baleog owns a sword and a bow and Olaf can use his shield to block enemies and glide safely through the air. In each level one has to use the different abilities to solve diverse riddles. In the end all three vikings have to reach the exit alive.</p>
<h2>Installation</h2>
<p>For republication, Blizzard has equipped the game with a Dosbox and reconfigured it such that can be run directly even on modern computers. Once downloaded and extracted, under Windows you can start it immediately by executing the file <code>The Lost Vikings.exe</code> in the main folder. If you use Linux there are two possibilities: If <code>wine</code> is installed you can start the same file via the command <code>wine The\ Lost\ Vikings.exe</code>. If instead you have Dosbox installed already on your system, just enter <code>dosbox</code> in the main folder. This will start the game automatically (note that both ways are meant to be done in a terminal).</p>
<h2>(Finally) played through!</h2>
<p>Of course I started immediately to play the game through again. Well, "again" might be the wrong word because I seem to remember that back then I was never able to beat the final level. But this time I made it. As a little "proof", further down you find a screenshot directly from the end of the last level (you can recognize it as an contemporary screenshot because of one of the glitches directly above the vikings that within my recollection were not there in the original game).</p>
<p><img alt="Screenshot from the end of the last level" src="/files/images/thelostvikings-end.png" /></p>
<p>For everyone who has started again playing the game, too but gets caught at a certain point and wants to continue with the next level, I have collected all the passwords. They are not picked together from the internet but - as already mentioned - collected by my own hands :-)</p>
<table>
<tr>
<th>Level</th>
<th>Password</th>
<th>Level</th>
<th>Password</th>
<th>Level</th>
<th>Password</th>
</tr>
<tr>
<td>1</td><td>STRT</td>
<td>14</td><td>C1R0</td>
<td>27</td><td>WKYY</td>
</tr>
<tr>
<td>2</td><td>GR8T</td>
<td>15</td><td>SPKS</td>
<td>28</td><td>CMB0</td>
</tr>
<tr>
<td>3</td><td>TLPT</td>
<td>16</td><td>JMNN</td>
<td>29</td><td>8BLL</td>
</tr>
<tr>
<td>4</td><td>GRND</td>
<td>17</td><td>TTRS</td>
<td>30</td><td>TRDR</td>
</tr>
<tr>
<td>5</td><td>LLM0</td>
<td>18</td><td>JLLY</td>
<td>31</td><td>FNTM</td>
</tr>
<tr>
<td>6</td><td>FL0T</td>
<td>19</td><td>PLNG</td>
<td>32</td><td>WRLR</td>
</tr>
<tr>
<td>7</td><td>TRSS</td>
<td>20</td><td>BTRY</td>
<td>33</td><td>TRPD</td>
</tr>
<tr>
<td>8</td><td>PRHS</td>
<td>21</td><td>JNKR</td>
<td>34</td><td>TFFF</td>
</tr>
<tr>
<td>9</td><td>CVRN</td>
<td>22</td><td>CBLT</td>
<td>35</td><td>FRGT</td>
</tr>
<tr>
<td>10</td><td>BBLS</td>
<td>23</td><td>H0PP</td>
<td>36</td><td>4RN4</td>
</tr>
<tr>
<td>11</td><td>VLCN</td>
<td>24</td><td>SMRT</td>
<td>37</td><td>MSTR</td>
</tr>
<tr>
<td>12</td><td>QCKS</td>
<td>25</td><td>V8TR</td>
</tr>
<tr>
<td>13</td><td>PHR0</td>
<td>26</td><td>NFL8</td>
</tr>
</table>