Jekyll2021-03-23T13:52:04+00:00https://hugogranstrom.com/feed.xmlHugo Granström - Math, Physics and Nim/Python BlogMy Nim/Python programming, math and physics blogHugo GranströmNumerical Integration in Nim - Tutorial2020-09-30T00:00:00+00:002020-09-30T00:00:00+00:00https://hugogranstrom.com/nim-integration<p>This is the first blog post in a <a href="/category/Nim/">series</a> about numerical computations in <a href="https://nim-lang.org">Nim</a>. Today we will go over numerical integration using Nim, specifically using my library <a href="https://github.com/hugogranstrom/numericalnim">NumericalNim</a>. I will assume you have some experience using Nim, otherwise check out the tutorials <a href="https://nim-lang.org/learn.html">here</a>. Let’s begin!</p> <p>The first thing we want to do is to install NumericalNim using Nimble by running this command in the terminal:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">nimble</span> <span class="n">install</span> <span class="n">numericalnim</span> </code></pre></div></div> <p>To use it we import it at the top of the <code class="language-plaintext highlighter-rouge">.nim</code> file:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="n">numericalnim</span> </code></pre></div></div> <p>We will go through three scenarios depending on what exactly you want to integrate.</p> <ol> <li>You have a mathematical expression for the function.</li> <li>You only know the values of the function at specific discrete points (for example if you have done some kind of measurement).</li> <li>We want to calculate a cumulative integral from a to b, ie we also want the integral between a -&gt; a + dt and a -&gt; a + 2*dt and so on.</li> </ol> <h1 id="1-integrating-mathematical-functions">1. Integrating mathematical functions</h1> <p>In this scenario we know the function <code class="language-plaintext highlighter-rouge">f(x)</code> on the entire interval of integration. Now we need to define a Nim proc that allows us to calculate it at any point. In this example I will use the function $$f(x) = \sin^2(2 \pi x)$$. It has the primitive function $$F(x) = \frac{x}{2} - \frac{\sin(4 \pi x)}{8 \pi}$$ which we will use to check how accurate we can get our numeric solutions. From this we can define two procs:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="n">math</span> <span class="k">import</span> <span class="n">numericalnim</span> <span class="k">proc </span><span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="kt">float</span><span class="p">,</span> <span class="n">ctx</span><span class="p">:</span> <span class="n">NumContext</span><span class="o">[</span><span class="kt">float</span><span class="o">]</span><span class="p">):</span> <span class="kt">float</span> <span class="o">=</span> <span class="n">sin</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">PI</span> <span class="o">*</span> <span class="n">x</span><span class="p">)</span> <span class="p">^</span> <span class="mi">2</span> <span class="k">proc </span><span class="nf">primitive</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="kt">float</span><span class="p">):</span> <span class="kt">float</span> <span class="o">=</span> <span class="n">x</span> <span class="o">/</span> <span class="mi">2</span> <span class="o">-</span> <span class="n">sin</span><span class="p">(</span><span class="mi">4</span><span class="o">*</span> <span class="n">PI</span> <span class="o">*</span> <span class="n">x</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="mi">8</span> <span class="o">*</span> <span class="n">PI</span><span class="p">)</span> </code></pre></div></div> <p>NumericalNim requires that the proc you pass in is on the form <code class="language-plaintext highlighter-rouge">proc(x: float, ctx: NumContext[T]): T</code> where <code class="language-plaintext highlighter-rouge">NumContext</code> is a type which can be used to save parameters or data between function calls. Now we are ready to start integrating <code class="language-plaintext highlighter-rouge">f(x)</code>!</p> <p><img src="https://hugogranstrom.com/images/nim-integration/a_sineAndPrimitive.svg" alt="Sine^2 and Primitive" /></p> <p>Above is a plot of <code class="language-plaintext highlighter-rouge">f(x)</code> and its primitive function <code class="language-plaintext highlighter-rouge">F(x)</code>. The plots in this article was generated using <a href="https://github.com/Vindaar/ggplotnim">ggplotnim</a>.</p> <p>The integral we are going to compute is this:</p> $I_1 = \int_0^{10} f(x) \, \mathrm{d}x$ <p>Now we have all we need to calculate the integral! Are you ready? I can’t hear you, so I assume you are if you proceed ;)</p> <p>NumericalNim offers quite a few different methods you can use for your integration. They differ in accuracy and performance as well as adaptability. That means some methods use a fixed step size where it evaluates <code class="language-plaintext highlighter-rouge">f(x)</code>, while some can on its own change the step size so it takes smaller steps in regions where the function changes rapidly and larger steps where it doesn’t change much. Adaptive integrators are the kind of integrators I recommend if you know the function on the entire interval, because you can set an error tolerance which the method then will do its best to satisfy.</p> <p>NumericalNim offers two such methods: <code class="language-plaintext highlighter-rouge">adaptiveSimpson</code> and <code class="language-plaintext highlighter-rouge">adaptiveGauss</code> out of which <code class="language-plaintext highlighter-rouge">adaptiveGauss</code> is the recommended one because it is faster, more robust and supports integrating from $$-\infty$$ to $$\infty$$ by means of a change of variable. If you want some geeky details it uses a Gauss-Kronrod method of order 21 with a global error control scheme. Compare that to <code class="language-plaintext highlighter-rouge">adaptiveSimpson</code> which is of order 4 (or 5 depending on how you see it) and uses local error control. I have found that global error control is superior in basically all cases because it does the minimal work to reduce the error by subdividing the interval with the greatest error first. Enough geekyness, let’s get coding!</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">I1</span> <span class="o">=</span> <span class="n">adaptiveGauss</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">10</span><span class="p">)</span> <span class="k">let</span> <span class="n">correct1</span> <span class="o">=</span> <span class="n">primitive</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span> <span class="o">-</span> <span class="n">primitive</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="n">echo</span> <span class="s">"Correct: "</span><span class="p">,</span> <span class="n">correct1</span> <span class="n">echo</span> <span class="s">"Numeric: "</span><span class="p">,</span> <span class="n">I1</span> <span class="n">echo</span> <span class="s">"Error: "</span><span class="p">,</span> <span class="n">abs</span><span class="p">(</span><span class="n">correct1</span> <span class="o">-</span> <span class="n">I1</span><span class="p">)</span> </code></pre></div></div> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Correct</span><span class="p">:</span> <span class="mf">5.0</span> <span class="n">Numeric</span><span class="p">:</span> <span class="mf">5.000000000000002</span> <span class="n">Error</span><span class="p">:</span> <span class="mf">1.776356839400251e-015</span> </code></pre></div></div> <p>That is pretty accurate! That’s cool and all but why bother doing it numerically when we could have done it analytically instead? That’s a good point, but the fact is that there are loads of integrals for which we can’t find a primitive function. A simple example is $$e^{x^2}$$ which is widely used. For example in the normal distribution where we want to calculate integrals to get a probability. That is what we will do next! The function we will integrate is:</p> $g(x) = \frac{1}{\sigma \sqrt{2\pi}} e ^ {-\frac{1}{2} \left(\frac{x - \mu}{\sigma}\right)^2}$ <p>Here $$\sigma$$ is the standard deviation and $$\mu$$ is the mean value where the curve is centered about. If we want to know the probability that a random value from this distribution is between two number $$a$$ and $$b$$ we integrate from $$a$$ to $$b$$:</p> $P(a &lt; X &lt; b) = \int_a^b g(x) \, \mathrm{d}x$ <p>Let’s implement this function in Nim! But how are we supposed to pass in $$\mu$$ and $$\sigma$$ if we only can have one variable, <code class="language-plaintext highlighter-rouge">x</code>? The answer is the <code class="language-plaintext highlighter-rouge">NumContext</code>! We can create a <code class="language-plaintext highlighter-rouge">NumContext</code> and store the values in there and then pass it in:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="n">ctx</span> <span class="o">=</span> <span class="n">newNumContext</span><span class="o">[</span><span class="kt">float</span><span class="o">]</span><span class="p">()</span> <span class="c"># These values correspond to a normal distribution centered around x = 3</span> <span class="c"># with standard deviation 1.</span> <span class="n">ctx</span><span class="o">[</span><span class="s">"sigma"</span><span class="o">]</span> <span class="o">=</span> <span class="mf">1.0</span> <span class="n">ctx</span><span class="o">[</span><span class="s">"mu"</span><span class="o">]</span> <span class="o">=</span> <span class="mf">3.0</span> </code></pre></div></div> <p>As you can see, the context behaves like a table where we use a string as the key. Note though that the type of the <code class="language-plaintext highlighter-rouge">NumContext</code> must be the same as the return-value of your function. So if your <code class="language-plaintext highlighter-rouge">f(x)</code> has return-type <code class="language-plaintext highlighter-rouge">CoolType</code>, then you must create a <code class="language-plaintext highlighter-rouge">NumContext[CoolType]</code> and NOT one with <code class="language-plaintext highlighter-rouge">float</code>. A NumContext does however always have a <code class="language-plaintext highlighter-rouge">float</code> storage which can be accessed using the procs <code class="language-plaintext highlighter-rouge">setF</code> and <code class="language-plaintext highlighter-rouge">getF</code>.</p> <p>If we now plot <code class="language-plaintext highlighter-rouge">g(x)</code> we get this: <img src="https://hugogranstrom.com/images/nim-integration/b_normalDist.svg" alt="Normal Distribution" /></p> <p>Now we can define <code class="language-plaintext highlighter-rouge">g(x)</code>:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">proc </span><span class="nf">g</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="kt">float</span><span class="p">,</span> <span class="n">ctx</span><span class="p">:</span> <span class="n">NumContext</span><span class="o">[</span><span class="kt">float</span><span class="o">]</span><span class="p">):</span> <span class="kt">float</span> <span class="o">=</span> <span class="k">let</span> <span class="n">sigma</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">[</span><span class="s">"sigma"</span><span class="o">]</span> <span class="k">let</span> <span class="n">mu</span> <span class="o">=</span> <span class="n">ctx</span><span class="o">[</span><span class="s">"mu"</span><span class="o">]</span> <span class="n">result</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">/</span> <span class="p">(</span><span class="n">sigma</span> <span class="o">*</span> <span class="n">sqrt</span><span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">PI</span><span class="p">))</span> <span class="o">*</span> <span class="n">exp</span><span class="p">(</span><span class="o">-</span><span class="mf">0.5</span> <span class="o">*</span> <span class="p">((</span><span class="n">x</span> <span class="o">-</span> <span class="n">mu</span><span class="p">)</span> <span class="o">/</span> <span class="n">sigma</span><span class="p">)</span> <span class="p">^</span> <span class="mi">2</span><span class="p">)</span> </code></pre></div></div> <p>The only integral we can analytically compute is the infinite ones: $$-\infty$$ to $$\mu$$ and to $$\infty$$. The integral over all numbers is 1, which is logical as the probability to find x somewhere is 1. So we can first test this to see if we get the correct answer:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">I2</span> <span class="o">=</span> <span class="n">adaptiveGauss</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="o">-</span><span class="n">Inf</span><span class="p">,</span> <span class="n">Inf</span><span class="p">,</span> <span class="n">ctx</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">)</span> <span class="n">echo</span> <span class="s">"I2 = "</span><span class="p">,</span> <span class="n">I2</span> </code></pre></div></div> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">I2</span> <span class="o">=</span> <span class="mf">1.000000000000013</span> </code></pre></div></div> <p>That is pretty close to 1! And we can get even closer if we lower then error tolerance from the default <code class="language-plaintext highlighter-rouge">1e-8</code> to <code class="language-plaintext highlighter-rouge">1e-10</code>:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">echo</span> <span class="n">adaptiveGauss</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="o">-</span><span class="n">Inf</span><span class="p">,</span> <span class="n">Inf</span><span class="p">,</span> <span class="n">ctx</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">tol</span> <span class="o">=</span> <span class="mf">1e-10</span><span class="p">)</span> <span class="c"># 1.0</span> </code></pre></div></div> <p>So we can be pretty certain that we have implemented <code class="language-plaintext highlighter-rouge">g(x)</code> correctly. Let’s now do something algebra can’t: integrate our function from 0 to 3.5:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">I3</span> <span class="o">=</span> <span class="n">adaptiveGauss</span><span class="p">(</span><span class="n">g</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mf">3.5</span><span class="p">,</span> <span class="n">ctx</span> <span class="o">=</span> <span class="n">ctx</span><span class="p">,</span> <span class="n">tol</span> <span class="o">=</span> <span class="mf">1e-10</span><span class="p">)</span> <span class="n">echo</span> <span class="n">I3</span> <span class="c"># 0.6901125632423831</span> </code></pre></div></div> <p>In other words, the probability that it is between 0 and 3.5 is approximately 69%.</p> <h1 id="2-integrating-discrete-functions">2. Integrating discrete functions</h1> <p>In this scenario we only know the values of our function <code class="language-plaintext highlighter-rouge">f(x)</code> at discrete points. Perhaps because we have done some measurements at those points. We will then have a set of points <code class="language-plaintext highlighter-rouge">X</code> which contains the variable we want to integrate over and <code class="language-plaintext highlighter-rouge">Y</code> which contains the function value at all those points. In this first example <code class="language-plaintext highlighter-rouge">X</code> will be points in time and <code class="language-plaintext highlighter-rouge">Y</code> will be the velocity of a bike at those points in time:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="n">numericalnim</span><span class="p">,</span> <span class="n">stats</span> <span class="k">var</span> <span class="n">X</span> <span class="o">=</span> <span class="o">@[</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">10.0</span><span class="p">,</span> <span class="mf">20.0</span><span class="p">,</span> <span class="mf">30.0</span><span class="p">,</span> <span class="mf">40.0</span><span class="p">,</span> <span class="mf">50.0</span><span class="p">,</span> <span class="mf">60.0</span><span class="o">]</span> <span class="c"># seconds</span> <span class="k">var</span> <span class="n">Y</span> <span class="o">=</span> <span class="o">@[</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">4.0</span><span class="p">,</span> <span class="mf">6.0</span><span class="p">,</span> <span class="mf">6.5</span><span class="p">,</span> <span class="mf">6.4</span><span class="p">,</span> <span class="mf">6.2</span><span class="p">,</span> <span class="mf">0.0</span><span class="o">]</span> <span class="c"># meters/second</span> </code></pre></div></div> <p>Just by looking at the data it seems like someone started from standing still and then biked for 1 minute before stopping. How far has this person traveled in this time? Just by looking at this data we can never know for certain but we can get an approximation. The first and simplest way is to take the mean of the velocity and use the formula $$s = vt$$ to get a first approximation:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">s1</span> <span class="o">=</span> <span class="n">mean</span><span class="p">(</span><span class="n">Y</span><span class="p">)</span> <span class="o">*</span> <span class="mi">60</span> <span class="n">echo</span> <span class="s">"Mean method: "</span><span class="p">,</span> <span class="n">s1</span><span class="p">,</span> <span class="s">" meters"</span> <span class="c"># Mean method: 249.4285714285714 meters</span> </code></pre></div></div> <p>So we now know that it’s in the ballpark of 250 meters but we can of course do better than just assuming a constant velocity. Enter <code class="language-plaintext highlighter-rouge">trapz</code>! It approximates our points with straight lines between them an integrates them:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">s2</span> <span class="o">=</span> <span class="n">trapz</span><span class="p">(</span><span class="n">Y</span><span class="p">,</span> <span class="n">X</span><span class="p">)</span> <span class="n">echo</span> <span class="s">"Trapz: "</span><span class="p">,</span> <span class="n">s2</span><span class="p">,</span> <span class="s">" meters"</span> <span class="c"># Trapz: 291.0 meters</span> </code></pre></div></div> <p>That’s quite a difference so we see the mean method has its limitations. We can do even better though by using <code class="language-plaintext highlighter-rouge">simpson</code> which approximates the function as a second degree polynomial instead:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">s3</span> <span class="o">=</span> <span class="n">simpson</span><span class="p">(</span><span class="n">Y</span><span class="p">,</span> <span class="n">X</span><span class="p">)</span> <span class="n">echo</span> <span class="s">"Simpson: "</span><span class="p">,</span> <span class="n">s3</span><span class="p">,</span> <span class="s">" meters"</span> <span class="c"># Simpson: 305.3333333333334 meters</span> </code></pre></div></div> <p>Once again we are getting a slightly different answer which should be more accurate as it approximates the velocity as smoother than <code class="language-plaintext highlighter-rouge">trapz</code> does. There is one more method that can handle discrete integrands, <code class="language-plaintext highlighter-rouge">romberg</code>, but it does ONLY work if we have <code class="language-plaintext highlighter-rouge">N = 2^k + 1</code> equally spaced points (3, 5, 9, 17, 33, 65, 129 etc). In our case we have 7 equally spaced points which does NOT WORK.</p> <p>There is a trick we can do though to try and get an even better approximation. That is to interpolate our data using cubic splines, which will give us a piecewise 3rd degree polynomial so we should get even better accuracy. There are two types of splines in NumericalNim: natural cubic splines which only supports floats and Hermite cubic splines which works for any type. The advantage of the natural cubic splines though is that it gives a continuous second derivative while the hermite only gives continuous first derivative (if we doesn’t supply it with the derivatives in all points as well. But we don’t have those here). I will use the natural cubic splines to get the smoothest possible approximation:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">spline</span> <span class="o">=</span> <span class="n">newCubicSpline</span><span class="p">(</span><span class="n">X</span><span class="p">,</span> <span class="n">Y</span><span class="p">)</span> <span class="c"># If you want to try out the hermite spline instead:</span> <span class="c"># let spline2 = newHermiteSpline(X, Y) </span> </code></pre></div></div> <p>In this plot where we show the discrete velocity values and the two different spline types we can see that there isn’t just a single way of interpolating the data but multiple. And hence we get slightly different results depending on which one we choose: <img src="https://hugogranstrom.com/images/nim-integration/c_velocity.svg" alt="Velocity and splines" /></p> <p>We can evaluate it at a certain point by using the <code class="language-plaintext highlighter-rouge">eval</code> proc (and <code class="language-plaintext highlighter-rouge">derivEval</code> to evaluate the derivative):</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">echo</span> <span class="n">spline</span><span class="p">.</span><span class="n">eval</span><span class="p">(</span><span class="mf">5.0</span><span class="p">)</span> <span class="c"># 2.167596153846154</span> </code></pre></div></div> <p>We could also want an ordinary proc instead of calling <code class="language-plaintext highlighter-rouge">eval</code> on the spline every time. <code class="language-plaintext highlighter-rouge">toProc</code> wraps the <code class="language-plaintext highlighter-rouge">spline.eval(x)</code> in a proc for us:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">splineProc</span> <span class="o">=</span> <span class="n">spline</span><span class="p">.</span><span class="n">toProc</span> <span class="c"># We don't need spline any more now:</span> <span class="n">echo</span> <span class="n">splineProc</span><span class="p">(</span><span class="mf">5.0</span><span class="p">)</span> <span class="c"># 2.167596153846154</span> </code></pre></div></div> <p>We can also just pass the spline directly to the integration methods and it will automatically be converted into the right kind of proc:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">s4</span> <span class="o">=</span> <span class="n">adaptiveGauss</span><span class="p">(</span><span class="n">spline</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">60.0</span><span class="p">)</span> <span class="n">echo</span> <span class="s">"Gauss: "</span><span class="p">,</span> <span class="n">s4</span><span class="p">,</span> <span class="s">" meters"</span> <span class="c"># Gauss: 301.2115384615747 meters</span> </code></pre></div></div> <p>Here we used the <code class="language-plaintext highlighter-rouge">adaptiveGauss</code> method as we with the spline “know” all the function values between 0 and 60. I think we can with a reasonable certainty say that the biker has traveled about 300 meters during this minute. As you can see there are many ways to approach this problem and we can’t really know for certain exactly how long this biker has traveled, not only because of the lack of points but also because the time and velocity probably (most definitely!) also has a measurement uncertainty.</p> <p>All we can do is to reduce this uncertainty by taking more measurements with better accuracy and as we do so, all methods should converge towards the “correct” solution. But we will never reach it with 100% certainty, only within a reasonable tolerance. In this case that tolerance may be plus minus 10 meters, which might not matter for the average person but in a competition where it’s milliseconds that differ between the contestants it’s nowhere near enough. All is relative, know your situation and its requirements!</p> <h1 id="3-cumulative-integration">3. Cumulative integration</h1> <p>There are scenarios where you could want to not only calculate a single integral but many subintegrals. For example if we have the velocity as a function of time. With one integral we can calculate how long we have traveled in a certain amount of time. But what if we want the velocity as a function of time? Then we have to calculate many integrals, one for every point in time we want.</p> <p>NumericalNim has specialized methods to deal with these kinds of integrals which are called cumulative integrals. The available methods are <code class="language-plaintext highlighter-rouge">cumtrapz</code> and <code class="language-plaintext highlighter-rouge">cumsimpson</code>. They work for both discrete and continuous functions, but only the discrete case will be handled here. The continuous one works the same way only that you give it a function instead of Y-values and you give it the X-values you want it to calculate the integral at. Let’s get started with the discrete case!</p> <p>We are given the velocities V at the time points t (same as in section 2 above):</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="n">numericalnim</span> <span class="k">var</span> <span class="n">t</span> <span class="o">=</span> <span class="o">@[</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">10.0</span><span class="p">,</span> <span class="mf">20.0</span><span class="p">,</span> <span class="mf">30.0</span><span class="p">,</span> <span class="mf">40.0</span><span class="p">,</span> <span class="mf">50.0</span><span class="p">,</span> <span class="mf">60.0</span><span class="o">]</span> <span class="c"># seconds</span> <span class="k">var</span> <span class="n">V</span> <span class="o">=</span> <span class="o">@[</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">4.0</span><span class="p">,</span> <span class="mf">6.0</span><span class="p">,</span> <span class="mf">6.5</span><span class="p">,</span> <span class="mf">6.4</span><span class="p">,</span> <span class="mf">6.2</span><span class="p">,</span> <span class="mf">0.0</span><span class="o">]</span> <span class="c"># meters/second</span> </code></pre></div></div> <p>Now we want to know not just the total distance, but the distance the biker has traveled at every point in t. For that we can start by using <code class="language-plaintext highlighter-rouge">cumtrapz</code>:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">dist1</span> <span class="o">=</span> <span class="n">cumtrapz</span><span class="p">(</span><span class="n">V</span><span class="p">,</span> <span class="n">t</span><span class="p">)</span> <span class="n">echo</span> <span class="s">"Trapz: "</span><span class="p">,</span> <span class="n">dist1</span> <span class="c"># Trapz: @[0.0, 20.0, 70.0, 132.5, 197.0, 260.0, 291.0]</span> </code></pre></div></div> <p>We can see that after 30 seconds we have traveled 132.5 meters. Now let’s compare that to <code class="language-plaintext highlighter-rouge">cumsimpson</code>:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">dist2</span> <span class="o">=</span> <span class="n">cumsimpson</span><span class="p">(</span><span class="n">V</span><span class="p">,</span> <span class="n">t</span><span class="p">)</span> <span class="n">echo</span> <span class="s">"Simpson: "</span><span class="p">,</span> <span class="n">dist2</span> <span class="c"># Simpson: @[0.0, 21.66666666666667, 73.33333333333334, 136.3333333333333, 201.3333333333333, 269.3333333333334, 305.3333333333334]</span> </code></pre></div></div> <p>Here we instead have traveled 136.3 meters after 30 seconds which isn’t much of a difference, but probably closer to the real value. Below is a two plots which shows the discrete velocity and the distances at each time point for trapz and simpson:</p> <p><img src="https://hugogranstrom.com/images/nim-integration/d_velocity.svg" alt="Velocity" /></p> <p><img src="https://hugogranstrom.com/images/nim-integration/d_distance.svg" alt="Distance Trapz Simpson" /></p> <p>That’s neat and all but can we use this for something else? Yes! Say that we just measure the acceleration of the biker instead, then we can get the velocity by this method. And if we repeat it again, we can get the distance as well! So just by measuring the acceleration of something we can get an approximation for the distance it has traveled. A simple example is a free falling object with constant acceleration 9.82 m/s^2 (here where I live). We do of course ignore air resistance or else it would be more of a differential equation instead. We get these measurements from the accelerometer:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="n">t2</span> <span class="o">=</span> <span class="o">@[</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">2.0</span><span class="p">,</span> <span class="mf">3.0</span><span class="p">,</span> <span class="mf">4.0</span><span class="p">,</span> <span class="mf">5.0</span><span class="o">]</span> <span class="k">var</span> <span class="n">a</span> <span class="o">=</span> <span class="o">@[</span><span class="mf">9.82</span><span class="p">,</span> <span class="mf">9.82</span><span class="p">,</span> <span class="mf">9.82</span><span class="p">,</span> <span class="mf">9.82</span><span class="p">,</span> <span class="mf">9.82</span><span class="p">,</span> <span class="mf">0.0</span><span class="o">]</span> </code></pre></div></div> <p>As we can see by looking at the last acceleration we seem to have hit the ground. So what we will try to calculate from how high up the object was dropped from. The first step is to get the velocity as a function of time:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">var</span> <span class="n">v</span> <span class="o">=</span> <span class="n">cumsimpson</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">t2</span><span class="p">)</span> <span class="n">echo</span> <span class="s">"Velocity: "</span><span class="p">,</span> <span class="n">v</span> <span class="c"># Velocity: @[0.0, 9.82, 19.64, 29.46, 39.28, 45.00833333333333]</span> </code></pre></div></div> <p>Now we see that we started at velocity 0, what if we had given it another starting velocity instead, like 1 m/s upwards? That’s where the constant after the integral come in. You know that one you always forget about, that’s the one! You would then have to add it to all elements in <code class="language-plaintext highlighter-rouge">v</code>:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">import</span> <span class="n">sequtils</span> <span class="k">let</span> <span class="n">v0</span> <span class="o">=</span> <span class="o">-</span><span class="mf">1.0</span> <span class="c"># minus sign because its in the opposite direction</span> <span class="k">let</span> <span class="n">v_throw</span> <span class="o">=</span> <span class="n">v</span><span class="p">.</span><span class="n">mapIt</span><span class="p">(</span><span class="n">it</span> <span class="o">+</span> <span class="n">v0</span><span class="p">)</span> <span class="n">echo</span> <span class="s">"Adjusted Velocity: "</span><span class="p">,</span> <span class="n">v_throw</span> </code></pre></div></div> <p>But this isn’t what we are looking for as we want to drop it from a certain height so we will keep on using <code class="language-plaintext highlighter-rouge">v</code> instead. Now that we have <code class="language-plaintext highlighter-rouge">v</code> we can get the distance it has traveled and in this case we only care about the total distance so we don’t use a cumulative method:</p> <div class="language-nim highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">h</span> <span class="o">=</span> <span class="n">simpson</span><span class="p">(</span><span class="n">v</span><span class="p">,</span> <span class="n">t2</span><span class="p">)</span> <span class="n">echo</span> <span class="s">"Height: "</span><span class="p">,</span> <span class="n">h</span><span class="p">,</span> <span class="s">" meters"</span> <span class="c"># Height: 121.0451388888889 meters</span> </code></pre></div></div> <p>Roughly 121 meters, that’s a pretty high building! Or a really low plane! Below is two plots showing the velocity and distance the object has fallen as a function of time:</p> <p><img src="https://hugogranstrom.com/images/nim-integration/e_velocity.svg" alt="Fall Velocity" /></p> <p><img src="https://hugogranstrom.com/images/nim-integration/e_height.svg" alt="Fall Height" /></p> <p>The problem we have here is that we don’t exactly know when the object hit the ground, just that it happened sometime between 4 and 5 seconds. We can solve this analytically and check if 121 meters lies within the possible heights. The formula is $$h = \frac{gt^2}{2}$$. By putting in $$t = 4$$ we get 78.56 meters and with $$t = 5$$ we get 122.75 meters. This shows that because of our poor resolution in our measurements, just one per second. We have a interval spanning over 40 meters so we can’t expect the integration method to do better than that. In this case it seems like the approximation it had to do put the impact closer to 5 than 4 seconds, which might or might not be correct.</p> <p>Numerical integration is effective to calculate integrals when we can’t do it symbolically. But there is always a certain amount of error because of the approximations that has to be done. But those start to become noticeable first when your data get reliable. Mathematical functions are the most exact things we have so those are well suited for numerical integration and you don’t have to think about it much. But when you want to integrate something that isn’t perfect you must always consider the errors, the better your data is, the better your integration will be. Don’t just blindly trust what the integrator spits out without giving a little bit of thought to what conditions it is working under.</p> <h1 id="conclusion">Conclusion</h1> <p>I hope you enjoy this tutorial :D If you have any questions, don’t hesitate to ask them. The best place to ask questions is either in the issue tracker on NumericalNim’s <a href="https://github.com/hugogranstrom/numericalnim">github</a> or on the Gitter channel <a href="https://gitter.im/NumericalNim/community">here</a>.</p> <p>Have an awesome day and integrate to your heart’s content! :D</p>Hugo GranströmThis is the first blog post in a series about numerical computations in Nim. Today we will go over numerical integration using Nim, specifically using my library NumericalNim. I will assume you have some experience using Nim, otherwise check out the tutorials here. Let’s begin!Math Codified - Derivatives2018-12-08T00:00:00+00:002018-12-08T00:00:00+00:00https://hugogranstrom.com/math-codified-derivatives<p>This equation is probably somewhat familiar to most people who have studied a bit of calculus: $$f'(x) = \lim_{h \to 0}\frac{f(x+h)-f(x)}{h}$$. Today we are breaking it down and converting it to code to really see how this works, when the math notation has been stripped off.</p> <p>To start with, how do we calculate the gradient (slope) of a function? We take the difference in y divided by the difference in x: $$\frac{\Delta y}{\Delta x} = \frac{y_2-y_1}{x_2-x_1} = \frac{f(x_2)-f(x_1)}{x_2-x_1}$$</p> <p>If our function is a straight line this will work no matter which $$x_2$$ and $$x_1$$ we choose (as long as $$x_2 &gt; x_1$$, otherwise we get the wrong sign). If our function instead for example is a quadratic equation, we will not get the the gradient in a point, but the average gradient in a segment of the function. The smaller we make the distance between $$x_2$$ and $$x_1$$ the better of an approximation it will be for the function’s gradient at a certain point. This is where the $$\lim_{h \to 0}$$ comes in. $$h$$ is the difference between $$x_2$$ and $$x_1$$ and when it approaches $$0$$ the result becomes the gradient at a single point $$x$$. The reason we only have a $$h$$ in the denominator is because the x’s cancel out: $$(x+h)-x = h$$.</p> <p>Now to the part you probably came here to read. Translating this math expression to a piece of code-cake:</p> <p>Let’s first create a python function for our math-function. I choose the equation $$f(x)=x^2$$</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> <span class="k">return</span> <span class="n">x</span><span class="o">**</span><span class="mi">2</span> </code></pre></div></div> <p>Next let’s define h, the very small difference between $$x_2$$ and $$x_1$$. We can’t set it to 0 because we can’t divide by 0. So we choose a very small number instead, namely $$h = 10^{-6}$$:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">h</span> <span class="o">=</span> <span class="mi">10</span><span class="o">**-</span><span class="mi">6</span> </code></pre></div></div> <p>Now we have the $$\lim_{h \to 0}$$ and f(x) part sorted. Now it’s time to define our python representation of $$f(x)'$$:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">derivative</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> <span class="n">x2</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">h</span> <span class="n">y2</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x2</span><span class="p">)</span> <span class="n">x1</span> <span class="o">=</span> <span class="n">x</span> <span class="n">y1</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x1</span><span class="p">)</span> <span class="n">gradient</span> <span class="o">=</span> <span class="p">(</span><span class="n">y2</span> <span class="o">-</span> <span class="n">y1</span><span class="p">)</span><span class="o">/</span><span class="p">(</span><span class="n">h</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="n">gradient</span><span class="p">)</span> <span class="k">return</span> <span class="n">gradient</span> </code></pre></div></div> <p>If we test to call the derivative function with a x-value we can check if it work. The derivative function of $$x^2$$ is $$2x$$:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">derivative</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span> <span class="c1"># returns 4.0000010006480125 </span></code></pre></div></div> <p>The actual value is 4 but this is a pretty good approximation I would say (it’s first at the 6’th decimal it gets it wrong). To get even more accurate results, choose a smaller $$h$$.</p> <h2 id="how-to-choose-a-good-h">How to choose a good $$h$$?</h2> <p>You may think that if you choose a really really small $$h$$ like $$10^{-100}$$ that you would get a super good approximation of the derivative. That would have been really nice but sadly normal computers can’t handle extremely small numbers at the same time as it handles large one. For example has Python no problems handling $$10^{-100}$$ on it’s own put if you add it to $$1$$ it is too small comparatively:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">print</span><span class="p">(</span><span class="mf">1e-100</span><span class="p">)</span> <span class="c1"># same as 1 * 10 ** -100 # prints: 1e-100 </span><span class="k">print</span><span class="p">(</span><span class="mi">1</span> <span class="o">+</span> <span class="mf">1e-100</span><span class="p">)</span> <span class="c1"># prints: 1.0 </span></code></pre></div></div> <p>The reason for this is that computers save numbers with limited precision. Just like we for example can’t write $$1/3 = 0.3333333333...$$ the entire decimal expansion on paper your computer can’t save all numbers exactly. The smallest number you can have between to two numbers is called machine epsilon and is denoted $$\epsilon_M$$. On a 64-bit computer a float (double precision to be more exact) has a $$\epsilon_M \approx 2.2 * 10^{-16}$$. If you want to get the machine epsilon on your machine using Python you can get it with numpy:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span> <span class="n">eps</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">finfo</span><span class="p">(</span><span class="nb">float</span><span class="p">).</span><span class="n">eps</span> </code></pre></div></div> <p>Now you may wonder why I have brought this up, what does it have to do with choosing a good $$h$$? The answer to that question is that we don’t want our h to be too close to $$\epsilon_M$$ because then our computer won’t be able to see a difference between $$f(x+h)$$ and $$f(x)$$ (remember what the definition of $$\epsilon_M$$ was?) and then $$f(x+h)-f(x) = 0$$ which we don’t want. So how are we supposed to choose $$h$$ then, what is small enough to get a good approximation while not being to close to $$\epsilon_M$$.</p> <p>$$h = \sqrt{\epsilon_M} * x$$ happens to be a good choice. Here $$x$$ is the x-value we want to evaluate the derivative of f(x) at. This works when $$x \neq 0$$ because then $$h = 0$$ which is problematic. We will have to add a check for that in our function. In Python we can modify our derivate function:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span> <span class="k">def</span> <span class="nf">derivative</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> <span class="k">if</span> <span class="n">x</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span> <span class="n">eps</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">finfo</span><span class="p">(</span><span class="nb">float</span><span class="p">).</span><span class="n">eps</span> <span class="n">h</span> <span class="o">=</span> <span class="n">eps</span><span class="o">**</span><span class="mf">0.5</span> <span class="o">*</span> <span class="n">x</span> <span class="k">else</span><span class="p">:</span> <span class="n">h</span> <span class="o">=</span> <span class="mf">1e-6</span> <span class="n">x2</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">h</span> <span class="n">y2</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x2</span><span class="p">)</span> <span class="n">x1</span> <span class="o">=</span> <span class="n">x</span> <span class="n">y1</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x1</span><span class="p">)</span> <span class="n">gradient</span> <span class="o">=</span> <span class="p">(</span><span class="n">y2</span> <span class="o">-</span> <span class="n">y1</span><span class="p">)</span><span class="o">/</span><span class="p">(</span><span class="n">h</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="n">gradient</span><span class="p">)</span> <span class="k">return</span> <span class="n">gradient</span> </code></pre></div></div> <p>Now our function chooses an appropriate $$h$$ by itself. Cool, right!?</p> <h2 id="are-there-more-accurate-methods">Are there more accurate methods?</h2> <p>This is a question you may ask yourself. Are there any other, better ways to calculate the derivative of a function? The answer is: Yes there are! Instead of calculating the value of the function in $$x$$ and $$x+h$$ we now calculate it in $$x-h$$ and $$x+h$$. This gives a $$h = (x+h) - (x-h) = 2h$$. The formula becomes: $$f'(x) = \frac{f(x+h) - f(x-h)}{2h}$$. It doesn’t look that different from our old formula but it is actually more accurate. If we write it in Python using everything we have learned so far:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span> <span class="k">def</span> <span class="nf">cool_function</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="n">sin</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">exp</span><span class="p">(</span><span class="n">x</span><span class="p">))</span> <span class="c1"># f(x) = sin(e^x) </span> <span class="k">def</span> <span class="nf">derivative</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">x</span><span class="p">):</span> <span class="k">if</span> <span class="n">x</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">:</span> <span class="n">eps</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">finfo</span><span class="p">(</span><span class="nb">float</span><span class="p">).</span><span class="n">eps</span> <span class="n">h</span> <span class="o">=</span> <span class="n">eps</span><span class="o">**</span><span class="mf">0.5</span> <span class="o">*</span> <span class="n">x</span> <span class="k">else</span><span class="p">:</span> <span class="n">h</span> <span class="o">=</span> <span class="mf">1e-6</span> <span class="n">x1</span> <span class="o">=</span> <span class="n">x</span> <span class="o">-</span> <span class="n">h</span> <span class="n">x2</span> <span class="o">=</span> <span class="n">x</span> <span class="o">+</span> <span class="n">h</span> <span class="n">y1</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x1</span><span class="p">)</span> <span class="n">y2</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x2</span><span class="p">)</span> <span class="n">gradient</span> <span class="o">=</span> <span class="p">(</span><span class="n">y2</span> <span class="o">-</span> <span class="n">y1</span><span class="p">)</span><span class="o">/</span><span class="p">(</span><span class="mi">2</span><span class="o">*</span><span class="n">h</span><span class="p">)</span> <span class="k">return</span> <span class="n">gradient</span> </code></pre></div></div> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">derivative</span><span class="p">(</span><span class="n">cool_function</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span> <span class="c1"># calculate the derivative of cool_function in x=3 # returns: 6.600002091377974 </span></code></pre></div></div> <p>The correct value is roughly 6.60000209300594833 so it’s a really good approximation. Our first method give the approximation 6.599993718167146 which isn’t far off either but it’s still 3 orders of magnitude less accurate than our second method.</p> <p>It still begs the question: are there even more accurate methods? The answer is: Yes! But I won’t cover them in this article. I will leave you with one of them though, and it’s up to you to implement it in Python:</p> $f'(x) = \frac{-f(x + 2h) + 8f(x+h) - 8f(x-h) + f(x-2h)}{12h}$ <p>(Note: that this method appears to be more accurate then the previous one when $$h$$ is bigger then $$10^{-6}$$. When it becomes smaller the inaccuracy becomes larger because we are adding more rounding errors together then we are in the others)</p> <p>That was it. I hope you enjoyed this and learned something new. My hope is that if you have experience in programming but you didn’t fully understand derivatives in school, that this article helped you grasp it better.</p>Hugo GranströmThis equation is probably somewhat familiar to most people who have studied a bit of calculus: $$f'(x) = \lim_{h \to 0}\frac{f(x+h)-f(x)}{h}$$. Today we are breaking it down and converting it to code to really see how this works, when the math notation has been stripped off.Showing that 0.999… = 1 using geometric sums2018-07-31T00:00:00+00:002018-07-31T00:00:00+00:00https://hugogranstrom.com/0.999-equals-1<p>If you have browsed a forum where math is discussed, it is likely you have seen people claiming both that $$0.999... = 1$$ and $$0.999... \neq 1$$. Today I will show you that the first one is correct using geometric sums. You don’t need to take my word for it, you will be able to prove it yourself.</p> <h2 id="geometric-sums">Geometric Sums</h2> <p>“What is a geometric sum?”, you may ask. Keep calm, I will get you covered. A geometric sum is a sum where the first term is a number, let’s call it $$a_1$$ and the second term is a constant, $$k$$, times the first term, so $$a_1*k$$. The third term is the second term times the same constant so it becomes $$a_1*k*k = a_1*k^2$$. And then it continues like that for as long as we want to calculate the sum. If we want to calculate the sum of the first 5 terms the last one would be $$a_1*k^4$$ because the first term don’t have a $$k$$ in it. Generally the last term thus becomes $$a_1*k^{n-1}$$ if we want to take the sum of the $$n$$ first terms. A geometric sum is of the form:</p> $S_n = a_1 + a_1*k + a_1*k^2 + a_1*k^3 + a_1*k^4 + ... + a_1*k^{n-2} + a_1*k^{n-1}$ <p>Here $$S_n$$ stands for “the sum of the $$n$$ first terms”. The terms are quite similar so it isn’t to farfetched to think that there is a formula to calculate the the sum given only $$a_1$$, $$k$$ and $$n$$. There is in fact a formula but I won’t just give it to you, we will derive it ourself to have a stable foundation for our argument.</p> <h2 id="deriving-the-formula-for-a-geometric-sum">Deriving the formula for a Geometric Sum</h2> <p>We start out with the equation for a general geometric sum:</p> $S_n = a_1 + a_1*k + a_1*k^2 + a_1*k^3 + a_1*k^4 + ... + a_1*k^{n-2} + a_1*k^{n-1}$ <p>Then we multiply both sides by $$k$$:</p> $k*S_n = a_1*k + a_1*k^2 + a_1*k^3 + a_1*k^4 + a_1*k^5 + ... + a_1*k^{n-1} + a_1*k^n$ <p>The reason we did this is because now we can see that the right side of the equation is nearly the same as it was before except $$a_1$$ is missing and we have a new term, $$a_1*k^n$$. We can then utilize that fact to rewrite the equation like this:</p> $k*S_n = S_n + a_1*k^n - a_1$ <p>This is much neater, right? Now all we have to do is to solve for $$S_n$$. We start by subtracting $$S_n$$ from both sides:</p> $k*S_n - S_n = a_1*k^n - a_1$ <p>Then we factor both sides by them self:</p> $S_n(k - 1) = a_1(k^n - 1)$ <p>Dividing by $$(k-1)$$ will give us our formula:</p> $S_n = a_1 \frac{k^n - 1}{k - 1}$ <p>There we have it, a simple formula for calculating a geometric sum.</p> <h2 id="lets-tackle-the-question">Let’s tackle the question</h2> <p>I said we would show that $$0.999... = 1$$ so let’s cut to the action. First what does $$0.999...$$ really mean? In this context it means that the pattern shown will continue forever. In other words that there is an infinite amount of 9’s after the decimal sign. Just a quick reflection here: Is there any real number between 1 and 0.999…? Independent of your answer, what implications would that have? Think about it, and experiment. That’s how new mathematics are invented, people playing around with the rules of math. “What happens if I ignore that?” and “How would this work if it was like this” are questions reasonable to wonder about. Complex numbers (they are as real as the real numbers, not imaginary in any way) were found this way, when someone thought “What if negative numbers have a square root” and just rolled along.</p> <p>Must have been an uneven cut because we seems to have sidetracked a bit from the action. 0.999… can if we separate every digit into a 9 times a power of a 10th be written as:</p> $0 + 9 \frac{1}{10} + 9 \frac{1}{10^2} + 9 \frac{1}{10^3} ...$ <p>This looks looks like a geometric sum, who could have guessed? $$a_1$$ in this case is $$9\frac{1}{10}$$, $$k$$ is $$\frac{1}{10}$$ and $$n$$ is $$\infty$$. We have a slight problem here: $$n$$ is $$\infty$$. Let’s see how our formula handles that.</p> $S_n = a_1 \frac{k^n - 1}{k - 1}$ <p>If $$k$$ is larger than or equal to 1, then the sum diverges and it doesn’t have a defined sum. If on the other hand $$k &lt; 1$$, then the sum converges. We can in fact simplify the formula when <strong>both</strong> $$n = \infty$$ <strong>and</strong> $$k &lt; 1$$ because $$k^n$$ then becomes 0 at the limit:</p> $lim_{n\to\infty} a_1\frac{k^n-1}{k-1} = a_1\frac{0-1}{k-1} = \frac{-a_1}{k-1} = \frac{-a_1}{-(1-k)} = \frac{a_1}{1-k}$ <p>There we have a really nice and simple formula. It is quite remarkable that we can calculate something that has an infinite component using such a simple and beautiful formula. If we now insert our values into it we get:</p> $\frac{a_1}{1-k} = \frac{\frac{9}{10}}{1-\frac{1}{10}} = \frac{\frac{9}{10}}{\frac{9}{10}} = 1$ <p>There we have it! We have showed that $$0.999... = 1$$ using a geometric sum. If someone ever tells you that it isn’t true, prove it to them yourself. You have all the tools you need for that now.</p> <p>Can you come up with any other “debate-able” sequence of decimals that can be proven to be something using geometric sums? If you do, please let me know in the comments. Have a nice day and remember that the human fantasy is limitless (but not undefined).</p>Hugo GranströmIf you have browsed a forum where math is discussed, it is likely you have seen people claiming both that $$0.999... = 1$$ and $$0.999... \neq 1$$. Today I will show you that the first one is correct using geometric sums. You don’t need to take my word for it, you will be able to prove it yourself.Making sense of the Monty Hall Problem2018-07-30T00:00:00+00:002018-07-30T00:00:00+00:00https://hugogranstrom.com/making-sense-monty-hall<p>The Monty Hall problem is a quiet famous problem where most people’s intuition are wrong. There are many variations of it but I will use one with playing card. It goes like this:</p> <p><em>You and a friend sit at a table opposite of each other. On the table there are three faced down playing cards, one king and two knights. Your friend knows which card is which but you have no idea. The ultimate goal in this game is to figure out which card is the king. First you get to choose one of the cards, and you tell your friend which. He/She then flips one of the other card two cards and it is a knight. You are now offered to stay with the card you chose at first or to switch to the second faced down card. Which of these choices has the greatest probability of you picking the king?</em></p> <p>Most people unfamiliar with this problem will probably say: “It doesn’t matter. They are equally likely”. We will here in a few different ways show that it does in fact matter if you switch or stay.</p> <p>First let’s try a more obvious example. Instead of three card, we have ten, nine knights and one king. You choose a card like before but instead of flipping just one knight, your friend flips 8 knights. Now there remains just two faced down cards, your first choice and one other. The key here is that you always have just two cards in the end to choose from. Would you switch or would you stay? Here your intuition probably tells you to switch because there is a greater probability that the card you chose was a knight than a king. In fact there is just 1/10 probability that you chose the king as your first card and thus a 9/10 probability that any of the other cards are the king. Therefore you have a 9/10 probability to pick the king if you switch card because you know that 8 of the other cards are not the king. Are you on board? If not, think through this example one more time and remember the key: we are always left with just two cards in the end to choose from.</p> <p>If we lower the amount of cards to nine, would it still be better to switch most of the time? Yes, you would have a 8/9 probability of picking the king if you switched. The same holds for 8, 7, 6, 5, 4 and 3 cards as well. Let’s look a little closer on the case of 4 cards. When you first choose a card you have 1/4 probability of picking the king. When you are left with just two card to choose from, the probability that you chose the king is still just 1/4 and therefore there must be a 3/4 probability that the other card is the king. We are nearly there now! Time for 3 cards: You have 1/3 probability of choosing the king and therefore a 2/3 probability that you did not choose the king. When one of the other cards are flipped and shown to be a knight, the probability still remains. You are more likely to have missed the king than to have picked it at first and therefore switching is the better option. In the case of 3 cards, you have a 2/3 probability to pick the king if you switch.</p> <p>Now a more mathy way to attack the problem (keep calm, we won’t hurt it). Is there any probability that remains the same throughout the whole problem, both mathematically and intuition-wise? One such probability I think is the probability that we did NOT choose the king as our first card, <strong>P(not king)</strong>. This probability is 2/3 and it will not change during the game. The game goes as before but we pause when the knight has been flipped. The probability is still the same that we did NOT choose the king at first and therefore it is still just 1/3 that the card we chose is a king but it is 2/3 that any of the other card are the king. Now we also know that one of those cards are not the king and therefore the other card must have the probability 2/3 of being king. Switching cards therefore gives you a 2/3 chance to pick the king.</p> <p>If this article didn’t make you understand the Monty Hall Problem then I recommend <a href="https://www.youtube.com/watch?v=4Lb-6rxZxx0">this</a> and <a href="https://www.youtube.com/watch?v=7u6kFlWZOWg">this</a> Youtube videos by a channel called Numberphile which explains this with pictures as well. They are by the way a really interesting channel for people interested in math.</p> <p>Have a nice day!</p>Hugo GranströmThe Monty Hall problem is a quiet famous problem where most people’s intuition are wrong. There are many variations of it but I will use one with playing card. It goes like this:Gravity Simulator2018-07-21T00:00:00+00:002018-07-21T00:00:00+00:00https://hugogranstrom.com/gravity-simulator<p>This is a gravity simulator I have written in python using the library VPython/Glowscript. This project was inspired by an exercise my math teacher gave us where we were supposed to simulate the moon’s orbit around the earth in excel using differential equations.</p> <p>Luckily there exists better tools for this, like python in conjunction with VPython where you get both vectors and graphics. So no need for splitting the vectors in an X-part and an Y-part, that is really convenient actually. My first intention was to just write the earth-moon simulation but then I thought: “Why not do the entire solar system instead”. And so I did, the Sun, the eight planets and Pluto (you will forever be a planet to me) all dancing together in a cosmic dance. Futhermore, I chose to use the Euler method for integration because it is easy to implement and it is the one most people are familiar with. This simulation won’t be near accurate enough to simulate the actual solar system anyway (for example relativity is not accounted for and the timestep is too big) so the inaccuracy doesn’t bother me. I just want to see some planets dance… and throw in a black hole later. I have also chosen to use other units then the SI-units: distance is measured in AU (average distance between earth and the sun), time is measured in days and mass in solar masses. This is because then the computer doesn’t have to handle such large numbers and hence it consumes less memory. A consequence of this is that G, the gravitational constant changes it’s values from the familiar 6.67e-11 to 2.959e-04. The size of the planets are scaled up by a factor of 1000 to make them visible. Otherwise are all the distances correct. The initial positions and velocities are real data from June 2018 from <a href="https://ssd.jpl.nasa.gov/horizons.cgi">here</a>. Watch it and have fun!</p> <p>The source code can be found on Glowscript <a href="http://www.glowscript.org/#/user/hajenzoo/folder/Public/program/gravity-simulator">here</a> (click “View this program” to see the source code)</p> <p>The simulation with a black hole can be found <a href="http://www.glowscript.org/#/user/hajenzoo/folder/Public/program/gravity-simulator-black-hole">here</a> (click “Run this program” in the upper left corner to start it)</p> <p>A tutorial on how to code this yourself will be coming in the future. I the meanwhile if you’re interested, I have written comments in the code that may be helpful.</p> <p>Remember, you can always count on your best friend, the calculator!</p> <p>A heads up: running this simulation can drain quite a lot of power so if you’re on a laptop, I recommend that you have your charger plugged in while running this.</p> <p>Controls:</p> <ul> <li>Zoom: scroll</li> <li>Pan: Hold SHIFT and drag with left mouse button</li> <li>Rotate: Hold and drag right mouse button</li> </ul> <p>Thanks to @marsater for ideas and feedback for this project.</p> <div id="glowscript" class="glowscript"> <script type="text/javascript" src="https://s3.amazonaws.com/glowscript/lib/jquery/2.1/jquery.min.js"></script> <script type="text/javascript" src="https://s3.amazonaws.com/glowscript/lib/jquery/2.1/jquery-ui.custom.min.js"></script> <script type="text/javascript" src="https://s3.amazonaws.com/glowscript/package/glow.2.7.min.js"></script> <script type="text/javascript" src="https://s3.amazonaws.com/glowscript/package/RSrun.2.7.min.js"></script> <script type="text/javascript"><!--//--><![CDATA[//><!-- ;(function() { var __rt=srequire('streamline/lib/callbacks/runtime').runtime(__filename, false),__func=__rt.__func,__cb=__rt.__cb; var RS_modules = {}; RS_modules.pythonize = {}; (function() { function strings() { var string_funcs, exclude, name; string_funcs = set("capitalize strip lstrip rstrip islower isupper isspace lower upper swapcase center count endswith startswith find rfind index rindex format join ljust rjust partition rpartition replace split rsplit splitlines zfill".split(" ")); if (!arguments.length) { exclude = (function() { var s = RS_set(); s.jsset.add("split"); s.jsset.add("replace"); return s; })(); } else if (arguments) { exclude = Array.prototype.slice.call(arguments); } else { exclude = null; } ; if (exclude) { string_funcs = string_funcs.difference(set(exclude)); } ; var RS_Iter0 = RS_Iterable(string_funcs); for (var RS_Index0 = 0; RS_Index0["<"](RS_Iter0.length); RS_Index0++) { name = RS_Iter0[RS_Index0]; (RS_expr_temp = String.prototype)[((((typeof name === "number") && name["<"](0))) ? RS_expr_temp.length["+"](name) : name)] = (RS_expr_temp = RS_str.prototype)[((((typeof name === "number") && name["<"](0))) ? RS_expr_temp.length["+"](name) : name)]; }; }; RS_modules.pythonize.strings = strings; })(); function main(_) { var version, box, sphere, cylinder, pyramid, cone, helix, ellipsoid, ring, arrow, compound, display, vector, print, scene, RS_ls, G, dt, time, scale_factor, AU, M, bodies, sun, earth, mercury, venus, mars, jupiter, saturn, uranus, neptune, pluto, time_label, body, __name__, strings, RS_Iter4, RS_Index4, RS_Iter5, RS_Index5, __this = this; function Body() { if ((this.RS_object_id === undefined)) { Object.defineProperty(this, "RS_object_id", { value: ++RS_object_counter }); }; Body.prototype.__init__.apply(this, arguments); }; var __frame = { name: "main", line: 32 }; return __func(_, this, arguments, main, 0, __frame, function __$main() { version = RS_list_decorate(["2.7","glowscript",]); Array.prototype["+"] = function(r) { return this.concat(r); }; Array.prototype["*"] = function(r) { return __array_times_number(this, r); }; __name__ = "__main__"; window.__GSlang = "vpython"; box = vp_box; sphere = vp_sphere; cylinder = vp_cylinder; pyramid = vp_pyramid; cone = vp_cone; helix = vp_helix; ellipsoid = vp_ellipsoid; ring = vp_ring; arrow = vp_arrow; compound = vp_compound; display = canvas; vector = vec; print = GSprint; scene = canvas(); strings = RS_modules.pythonize.strings; strings(); "8"; G = 0.00029592; "9"; dt = 0.01; "10"; time = 0; "11"; scale_factor = 1000; "12"; AU = 150000000000; "13"; M = 2e+30; "16"; bodies = RS_list_decorate([]); "18"; Body.prototype.__init__ = function __init__() { var self = this; var mass = ((((arguments === undefined) || (((((0 === arguments.length["-"](1)) && (arguments[arguments.length["-"](1)] !== null)) && (typeof arguments[arguments.length["-"](1)] === "object")) && (arguments[arguments.length["-"](1)][RS_kwargs_symbol] === true))))) ? __init__.__defaults__.mass : arguments); var radius = ((((arguments === undefined) || (((((1 === arguments.length["-"](1)) && (arguments[arguments.length["-"](1)] !== null)) && (typeof arguments[arguments.length["-"](1)] === "object")) && (arguments[arguments.length["-"](1)][RS_kwargs_symbol] === true))))) ? __init__.__defaults__.radius : arguments); var velocity = ((((arguments === undefined) || (((((2 === arguments.length["-"](1)) && (arguments[arguments.length["-"](1)] !== null)) && (typeof arguments[arguments.length["-"](1)] === "object")) && (arguments[arguments.length["-"](1)][RS_kwargs_symbol] === true))))) ? __init__.__defaults__.velocity : arguments); var position = ((((arguments === undefined) || (((((3 === arguments.length["-"](1)) && (arguments[arguments.length["-"](1)] !== null)) && (typeof arguments[arguments.length["-"](1)] === "object")) && (arguments[arguments.length["-"](1)][RS_kwargs_symbol] === true))))) ? __init__.__defaults__.position : arguments); var color = ((((arguments === undefined) || (((((4 === arguments.length["-"](1)) && (arguments[arguments.length["-"](1)] !== null)) && (typeof arguments[arguments.length["-"](1)] === "object")) && (arguments[arguments.length["-"](1)][RS_kwargs_symbol] === true))))) ? __init__.__defaults__.color : arguments); var trail = ((((arguments === undefined) || (((((5 === arguments.length["-"](1)) && (arguments[arguments.length["-"](1)] !== null)) && (typeof arguments[arguments.length["-"](1)] === "object")) && (arguments[arguments.length["-"](1)][RS_kwargs_symbol] === true))))) ? __init__.__defaults__.trail : arguments); var name = ((((arguments === undefined) || (((((6 === arguments.length["-"](1)) && (arguments[arguments.length["-"](1)] !== null)) && (typeof arguments[arguments.length["-"](1)] === "object")) && (arguments[arguments.length["-"](1)][RS_kwargs_symbol] === true))))) ? __init__.__defaults__.name : arguments); var RS_kwargs_obj = arguments[arguments.length["-"](1)]; if ((((RS_kwargs_obj === null) || (typeof RS_kwargs_obj !== "object")) || (RS_kwargs_obj[RS_kwargs_symbol] !== true))) { RS_kwargs_obj = { }; }; if (Object.prototype.hasOwnProperty.call(RS_kwargs_obj, "mass")) { mass = RS_kwargs_obj.mass; } ; if (Object.prototype.hasOwnProperty.call(RS_kwargs_obj, "radius")) { radius = RS_kwargs_obj.radius; } ; if (Object.prototype.hasOwnProperty.call(RS_kwargs_obj, "velocity")) { velocity = RS_kwargs_obj.velocity; } ; if (Object.prototype.hasOwnProperty.call(RS_kwargs_obj, "position")) { position = RS_kwargs_obj.position; } ; if (Object.prototype.hasOwnProperty.call(RS_kwargs_obj, "color")) { color = RS_kwargs_obj.color; } ; if (Object.prototype.hasOwnProperty.call(RS_kwargs_obj, "trail")) { trail = RS_kwargs_obj.trail; } ; if (Object.prototype.hasOwnProperty.call(RS_kwargs_obj, "name")) { name = RS_kwargs_obj.name; } ; var RS_ls; "20"; self.mass = mass; "21"; self.velocity = velocity; "22"; self.position = position; "23"; self.color = color; "24"; self.radius = radius; "25"; self.forces = RS_list_decorate([]); "26"; self.acc = vector(0, 0, 0); "27"; self.sum_force = vector(0, 0, 0); "28"; self.name = name; "29"; self.label = RS_interpolate_kwargs.call(this, label, [RS_desugar_kwargs({ pos: self.position, text: self.name, height: 10 }),]); "30"; self.sphere = RS_interpolate_kwargs.call(this, sphere, [RS_desugar_kwargs({ pos: self.position, color: self.color, radius: self.radius["*"](scale_factor), make_trail: trail, retain: 300 }),]); "31"; bodies.append(self); }; if (!Body.prototype.__init__.__defaults__) { Object.defineProperties(Body.prototype.__init__, { __defaults__: { value: { mass: 1, radius: 1, velocity: vector(0, 0, 0), position: vector(0, 0, 0), color: color.white, trail: true, name: "Body" } }, __handles_kwarg_interpolation__: { value: true }, __argnames__: { value: ["mass","radius","velocity","position","color","trail","name",] } }); } ; Body.__argnames__ = Body.prototype.__init__.__argnames__; Body.__handles_kwarg_interpolation__ = Body.prototype.__init__.__handles_kwarg_interpolation__; Body.prototype.update = function update() { var self = this; var RS_ls, force; "33"; self.forces = RS_list_decorate([]); "34"; self.sum_force = vector(0, 0, 0); "35"; self.gravitational_force(); "39"; var RS_Iter1 = RS_Iterable(self.forces); for (var RS_Index1 = 0; RS_Index1["<"](RS_Iter1.length); RS_Index1++) { force = RS_Iter1[RS_Index1]; "40"; self.sum_force = self.sum_force["+="](force); }; "43"; self.acc = self.sum_force["/"](self.mass); "44"; self.velocity = self.velocity["+="](dt["*"](self.acc)); }; Body.prototype.move = function move() { var self = this; var RS_ls; "47"; self.position = self.position["+="](dt["*"](self.velocity)); "50"; self.sphere.pos = self.position; "51"; self.label.pos = self.position; }; Body.prototype.gravitational_force = function gravitational_force() { var self = this; var RS_ls, r, force, dir, body; "55"; var RS_Iter2 = RS_Iterable(bodies); for (var RS_Index2 = 0; RS_Index2["<"](RS_Iter2.length); RS_Index2++) { body = RS_Iter2[RS_Index2]; "57"; r = mag(self.position["-"](body.position)); "59"; if (r["<"](self.radius["+"](body.radius))) { "60"; continue; } ; "62"; force = G["*"](self.mass)["*"](body.mass)["/"](GS_power(r, 2)); "64"; dir = norm(body.position["-"](self.position)); "66"; force = force["*"](dir); "68"; self.forces.append(force); }; }; Body.prototype.updateVerlet = function updateVerlet() { var self = this; var RS_ls, force; "72"; self.forces = RS_list_decorate([]); "73"; self.sum_force = vector(0, 0, 0); "74"; self.gravitational_force(); "78"; var RS_Iter3 = RS_Iterable(self.forces); for (var RS_Index3 = 0; RS_Index3["<"](RS_Iter3.length); RS_Index3++) { force = RS_Iter3[RS_Index3]; "79"; self.sum_force = self.sum_force["+="](force); }; "82"; self.position = self.position["+="](self.velocity["*"](dt)["+"](self.acc["/"](2)["*"](GS_power(dt, 2)))); "83"; self.velocity = self.velocity["+="](dt["/"](2)["*"](self.acc["+"](self.sum_force["/"](self.mass)))); "84"; self.acc = self.sum_force["/"](self.mass); "86"; self.sphere.pos = self.position; "87"; self.label.pos = self.position; }; Body.prototype.__repr__ = function __repr__() { return "<"["+"](__name__)["+"](".")["+"](this.constructor.name)["+"](" #")["+"](this.RS_object_id)["+"](">"); }; Body.prototype.__str__ = function __str__() { return this.__repr_; }; Object.defineProperty(Body.prototype, "__bases__", { value: [] }); Body.prototype.RS_ls = "19"; Body.prototype.RS_ls = "32"; Body.prototype.RS_ls = "46"; Body.prototype.RS_ls = "53"; Body.prototype.RS_ls = "71"; "92"; sun = RS_interpolate_kwargs_constructor.call(Object.create(Body.prototype), false, Body, [RS_desugar_kwargs({ mass: 1, radius: 700000000["*"](5)["/"](scale_factor)["/"](AU), color: color.yellow, trail: false, name: "Sun" }),]); "100"; earth = RS_interpolate_kwargs_constructor.call(Object.create(Body.prototype), false, Body, [RS_desugar_kwargs({ mass: 6e+24["/"](M), radius: 6371000["/"](AU), position: vector(0.5111702950987252["-u"](), 0.8734341386147972["-u"](), 0.00003902531498407046), velocity: vector(0.01457401965494037, 0.008749957786090569["-u"](), 3.393201214360642e-7["-u"]()), color: color.green, name: "Earth" }),]); "111"; mercury = RS_interpolate_kwargs_constructor.call(Object.create(Body.prototype), false, Body, [RS_desugar_kwargs({ mass: earth.mass["*"](0.055), radius: 6000000["/"](AU), position: vector(0.360006238731298, 0.08310671431721671["-u"](), 0.03981766501010686["-u"]()), velocity: vector(0.0008732371820239134, 0.0286750815794258, 0.002263026727476856), color: color.red, name: "Mercury" }),]); "123"; venus = RS_interpolate_kwargs_constructor.call(Object.create(Body.prototype), false, Body, [RS_desugar_kwargs({ mass: earth.mass["*"](0.815), radius: 6000000["/"](AU), position: vector(0.5460148756311848["-u"](), 0.4654289630909307, 0.03789319798488837), velocity: vector(0.01319751648139675["-u"](), 0.01549708277964608["-u"](), 0.0005490020542624818), color: color.white, name: "Venus" }),]); "135"; mars = RS_interpolate_kwargs_constructor.call(Object.create(Body.prototype), false, Body, [RS_desugar_kwargs({ mass: earth.mass["*"](0.107), radius: 6000000["/"](AU), velocity: vector(0.01444719742599419, 0.0002365918534978303["-u"](), 0.000359488561244826["-u"]()), position: vector(0.1508529480814324["-u"](), 1.460121856503524["-u"](), 0.02689190873994556["-u"]()), color: color.red, name: "Mars" }),]); "145"; jupiter = RS_interpolate_kwargs_constructor.call(Object.create(Body.prototype), false, Body, [RS_desugar_kwargs({ mass: earth.mass["*"](318), radius: 70000000["/"](AU), velocity: vector(0.005611682808441865, 0.004596785105938998["-u"](), 0.0001064356940327842["-u"]()), position: vector(3.545075313382027["-u"](), 4.081361865858232["-u"](), 0.09627457319753692), color: color.blue, name: "Jupiter" }),]); "155"; saturn = RS_interpolate_kwargs_constructor.call(Object.create(Body.prototype), false, Body, [RS_desugar_kwargs({ mass: earth.mass["*"](95), radius: 60000000["/"](AU), velocity: vector(0.005262021976694793, 0.0004141890616120753, 0.0002169327374705523["-u"]()), position: vector(0.7842529344684837, 10.03393486265119["-u"](), 0.1431896871358062), color: color.white, name: "Saturn" }),]); "165"; uranus = RS_interpolate_kwargs_constructor.call(Object.create(Body.prototype), false, Body, [RS_desugar_kwargs({ mass: earth.mass["*"](14), radius: 25000000["/"](AU), velocity: vector(0.0019052013493924["-u"](), 0.003265505721711341, 0.000036690734434005), position: vector(17.46114323983198, 9.517430938519276, 0.1907513002050031["-u"]()), color: color.blue, name: "Uranus" }),]); "175"; neptune = RS_interpolate_kwargs_constructor.call(Object.create(Body.prototype), false, Body, [RS_desugar_kwargs({ mass: earth.mass["*"](17), radius: 24000000["/"](AU), velocity: vector(0.0008427417626787077, 0.003035037625808767, 0.00008199842541642128["-u"]()), position: vector(28.80079206580985, 8.17390036348871["-u"](), 0.495478418972816["-u"]()), color: color.yellow, name: "Neptune" }),]); "185"; pluto = RS_interpolate_kwargs_constructor.call(Object.create(Body.prototype), false, Body, [RS_desugar_kwargs({ mass: earth.mass["*"](0.0022), radius: 1000000["/"](AU), position: vector(11.20198708794019, 31.64123744663468["-u"](), 0.1446313453325374), velocity: vector(0.003029567845289497, 0.0003743167934314588, 0.000926069693706297["-u"]()), color: color.yellow, name: "Pluto" }),]); "213"; time_label = RS_interpolate_kwargs.call(__this, label, [RS_desugar_kwargs({ pos: vector(75, 350, 0), pixel_pos: true, text: "Time: "["+"](str(time["/"](365)))["+"](" years") }),]); "216"; return (function ___(__break) { var __more; var __loop = __cb(_, __frame, 0, 0, function __$main() { __more = false; var __1 = true; if (__1) { "217"; return rate(5000, __cb(_, __frame, 228, 8, function __$main() { "219"; RS_Iter4 = RS_Iterable(bodies); for (RS_Index4 = 0; RS_Index4["<"](RS_Iter4.length); RS_Index4++) { body = RS_Iter4[RS_Index4]; "220"; body.update(); }; "222"; RS_Iter5 = RS_Iterable(bodies); for (RS_Index5 = 0; RS_Index5["<"](RS_Iter5.length); RS_Index5++) { body = RS_Iter5[RS_Index5]; "223"; body.move(); }; "224"; time_label.text = "Time: {:.2f} years".format(time["/"](365)); "225"; time = time["+="](dt); while (__more) { __loop(); }; __more = true; }, true)); } else { __break(); } ; }); do { __loop(); } while (__more); __more = true; })(_); });}; if (!main.__argnames__) { Object.defineProperties(main, { __argnames__: { value: ["_",] } });}; ;$(function(){ window.__context = { glowscript_container: \$("#glowscript").removeAttr("id") }; main(__func) })})() //--><!]]></script> </div>Hugo GranströmThis is a gravity simulator I have written in python using the library VPython/Glowscript. This project was inspired by an exercise my math teacher gave us where we were supposed to simulate the moon’s orbit around the earth in excel using differential equations.Math Codified - Limits2018-07-12T00:00:00+00:002018-07-12T00:00:00+00:00https://hugogranstrom.com/math-codified-limits<h3 id="prerequisites">Prerequisites</h3> <ul> <li>I assume you have a basic understanding of python and math.</li> <li>I will be using python 3.6 and above (sorry no legacy code here).</li> <li>I recommend writing the code yourself (in a jupyter notebook perhaps) and to play around with it.</li> <li>Being prepared for bad jokes</li> </ul> <h3 id="basics">Basics</h3> <p>Limits are a concept in math used in various situations, for example when calculating derivatives. I have a article about how to understand derivatives using python <a href="https://hugogranstrom.com/math-codified-derivatives/">here</a>.</p> <p>Limits are used when a function $$f(x)$$ isn’t defined for a certain input $$x$$ or if we want to see if the function has a limit at $$+\infty$$ or $$-\infty$$ ie if the function converges at infinity (functions can also converge to other functions but that is a matter for another day). That a function converges means that there is a number that the function approaches at $$\infty$$, an invisible line it never crosses. Often it can be seen as a “ceiling” or a “floor” like in this picture:</p> <p><img src="https://hugogranstrom.com/images/2018-07/floor1.png" alt="Floor function" /></p> <p>A limit is written as this:</p> $\lim_{x \to a} f(x)$ <p>$$\lim$$ stands for <em>limes</em> in latin but I always think of it as standing for the english word <em>limit</em>, so if you hear someone talk about <em>limes</em> they are probably talking about limits. $$f(x)$$ is the function we want to get the limit of and $$a$$ is the limit we want $$x$$ to approach. We write $$x \to a$$ to say “as $$x$$ approaches $$a$$”. The whole expression if spoken would be <em>The limit of $$f(x)$$ as $$x$$ approaches $$a$$</em>.</p> <p>Can we with that in mind evaluate some limits of the function in the picture above, $$f(x) = \frac{1}{x} + 2$$? <strong>Let’s try!</strong></p> <p>First let’s explore what happens when $$x$$ becomes really big, namely when $$x$$ approaches infinity. We would write the expression like this:</p> $\lim_{x \to \infty} f(x) = \lim_{x \to \infty} \frac{1}{x} + 2$ <p>If we study the graph we can see that the function never seems to go lower than the red-dashed line at $$y=2$$, in other words the limit as x approaches $$\infty$$ is 2 or as a math expression:</p> $\lim_{x \to \infty} \frac{1}{x} + 2 = 2$ <p>When you just look at the equation that makes alot of sense as well, $$\frac{1}{x}$$ becomes really small when $$x$$ is large so it basically becomes $$0 + 2$$ at $$\infty$$.</p> <p>A quick note about when to use limits and when not to. If you have a well behaved function (defined everywhere basically) then the limit is just the function evaluated at that point:</p> $\lim_{ x \to a } f(x) = f(x)$ <p>It is just when a function isn’t defined for certain $$x$$ (or if you want to see how it behaves at $$\infty$$) that limits are useful. We will show this in an code example below.</p> <h3 id="exercise">Exercise</h3> <p>That wasn’t too hard, right? Here comes an exercise for you to test yourself (the answer is at the bottom of this page):</p> <p>Write a mathematical expression of the limit of $$\lim_{x \to \infty} \frac{1}{x} + 2$$ as $$x$$ approaches $$0$$ and write a reasonable answer to that limit.</p> <h1 id="code-time">Code Time!</h1> <p>Now comes the juicy part you came here for! Put on your coding gear, here we GO! (don’t tell me you haven’t got any coding gear, let’s see… I know, take that old hoodie and… a pair of glasses! That looks rather code-ish, right?)</p> <p>The way we are going to do limits in python is when $$a$$ is a number we will add a small number $$h$$ to it and evaluate an approximation of the value like this:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># our function. is the same as above </span><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> <span class="k">return</span> <span class="mi">1</span><span class="o">/</span><span class="n">x</span> <span class="o">+</span> <span class="mi">2</span> <span class="c1"># the small number we will add to x. </span><span class="n">h</span> <span class="o">=</span> <span class="mf">1e-3</span> <span class="c1"># The limit as x goes to 0 </span><span class="n">x</span> <span class="o">=</span> <span class="mi">0</span> <span class="o">+</span> <span class="n">h</span> <span class="n">limit</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="n">limit</span><span class="p">)</span> <span class="c1"># 1002.0 </span></code></pre></div></div> <p>Note: 1e-3 is scientfic notation for $$1 \times 10^{-3}$$ and likewise is 7e3 the same as $$7 \times 10^{3}$$.</p> <p>If we test with a smaller $$h$$, for example 1e-6, we get 1000002. This is a strong indication that it tends to infinity and if we choose even smaller values for $$h$$ the limits get’s higher and higher. We can modify the code to write the answer for multiple values of $$h$$ and that could help us see a trend:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> <span class="k">return</span> <span class="mi">1</span><span class="o">/</span><span class="n">x</span> <span class="o">+</span> <span class="mi">2</span> <span class="n">x</span> <span class="o">=</span> <span class="mi">0</span> <span class="c1"># a list of the h's we want to test </span><span class="n">h_list</span> <span class="o">=</span> <span class="p">[</span><span class="mf">1e-3</span><span class="p">,</span> <span class="mf">1e-6</span><span class="p">,</span> <span class="mf">1e-9</span><span class="p">,</span> <span class="mf">1e-12</span><span class="p">]</span> <span class="c1"># loop over the h's and evaluate the function at each </span><span class="k">for</span> <span class="n">h</span> <span class="ow">in</span> <span class="n">h_list</span><span class="p">:</span> <span class="n">limit</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">h</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"h: </span><span class="si">{</span><span class="n">h</span><span class="si">}</span><span class="s">, limit: </span><span class="si">{</span><span class="n">limit</span><span class="si">}</span><span class="s">"</span><span class="p">)</span> <span class="c1">#h: 0.001, limit: 1002.0 #h: 1e-06, limit: 1000002.0 #h: 1e-09, limit: 1000000001.9999999 #h: 1e-12, limit: 1000000000002.0 </span></code></pre></div></div> <p>This is the structure we will be using now when we want to see how a function behaves as it approaches infinity. We will have a list of increasingly big numbers (instead of small):</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">):</span> <span class="k">return</span> <span class="mi">1</span><span class="o">/</span><span class="n">x</span> <span class="o">+</span> <span class="mi">2</span> <span class="c1"># list of big numbers </span><span class="n">x_list</span> <span class="o">=</span> <span class="p">[</span><span class="mf">1e3</span><span class="p">,</span> <span class="mf">1e6</span><span class="p">,</span> <span class="mf">1e9</span><span class="p">,</span> <span class="mf">1e12</span><span class="p">]</span> <span class="c1"># loop over the x's and evaluate the function at each </span><span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">x_list</span><span class="p">:</span> <span class="n">limit</span> <span class="o">=</span> <span class="n">f</span><span class="p">(</span><span class="n">x</span><span class="p">)</span> <span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">"x: </span><span class="si">{</span><span class="n">x</span><span class="si">}</span><span class="s">, limit: </span><span class="si">{</span><span class="n">limit</span><span class="si">}</span><span class="s">"</span><span class="p">)</span> <span class="c1">#x: 1000.0, limit: 2.001 #x: 1000000.0, limit: 2.000001 #x: 1000000000.0, limit: 2.000000001 #x: 1000000000000.0, limit: 2.000000000001 </span></code></pre></div></div> <p>As we can see it seems to converge to 2 at infinity. If you continue to increase $$x$$ you will soon surpass the precision of floating point numbers (that is shown) and it will just display “2.0”. Python is really convinient in the way that you can have really big numbers without worrying about what whether it should be an <em>unsigned</em>, <em>long</em> etc <em>int</em>. It just works! Test to print some really big number, preferably using the notation I have used above if you don’t want to hold the 0-button until the heat-death of the universe. It will handle quite big numbers, numbers to0 big for us to even imagine. 1e100, $$10^{100}$$ also called a <em>googol</em> would need to divided by 2 one hundred times just to get down to $$10^{70}$$. Think about how big $$2^{100}$$ is, even so it is just $$\frac{1}{10^{68}}$$ % of a googol. A googol was probably a piece of cake for your computer, then I have a challenge for it: beware! The <em>googolplex</em> ! (the space after the word is to keep the size of the number down, in <em>fact</em> it would be so ridiculously huge that I don’t know how to write it in any other way then that). A <em>googolplex</em> is 10 to the power of a googol, $$10^{10^{100}}$$. Seeing as my computer can’t handle numbers larger than 1e300 I have a hard time seeing a computer being able to handle a googolplex without cheating. For anyone interested, a <em>googol</em> is about a <em>googol</em>-th ($$\frac{1}{10^{98}}$$%) of a percent of a <em>googolplex</em>.</p> <p>If you are reading this at night I apologize for the last paragraph, it might have been a bit to math heavy for this article about codifying math. But I just think that it is remarkable that we can express really big numbers even though we can’t really comprehend them.</p> <p>That was it for this time, I hope you learnt at least something new and that I haven’t scared you away from math. I would really like to discuss math and such with you so if you are in the mood you can find me on <a href="https://twitter.com/GranstromHugo">twitter</a>.</p> <p><strong>Have a nice day!</strong></p> <h3 id="answer-to-exercise">Answer to Exercise</h3> $\lim_{ x \to 0 } \frac{1}{x} + 2 = \infty$ <p>An important note here is differentiate between $$\lim_{ x \to 0 } \frac{1}{x} + 2 = \infty$$ and $$\frac{1}{0} + 2 = \infty$$. The first one is true but the second one is not, this is correct then: $$\frac{1}{0} + 2$$ <em>is undefined</em>.</p> <h3 id="questions--contact">Questions &amp; Contact</h3> <p>If you got any questions comment them below, send me an email or contact me on twitter. The links are in the footer below. You can also contact me there casually just to discuss what’s at the <em>limit</em> of your imagination right now.</p>Hugo GranströmLimits are a concept in math used in various situations, for example when calculating derivatives. Limits are used when a function f(x) isn't defined for a certain input x or if we want to see if the function has a limit at infinity.Hello World!2018-05-06T00:00:00+00:002018-05-06T00:00:00+00:00https://hugogranstrom.com/hello-world<p>Hello World!</p> <p>This is my first blog post here. I’m a science student in Sweden who is interested in mathematics and programming. My programming language of choice is Python, mainly because I think Java (my first language) and the others look too cluttered with their overuse of curly brackets {}.</p> <p>Here is the line of code most of us had as our first computer program:</p> <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">print</span><span class="p">(</span><span class="s">"Hello World!"</span><span class="p">)</span> </code></pre></div></div> <p>Ahh… What nostalgia! My memories come back from when I was 13 and coded Java on my 4 inch android phone with “Hacker’s Keyboard”. Though that time it was a more complicated expression I had to write…..</p>Hugo GranströmHello World!