WifiCIDR Blog (Posts about math)https://blog.wificidr.net/enMon, 05 Oct 2020 18:50:23 GMTNikola (getnikola.com)http://blogs.law.harvard.edu/tech/rssMonoids for Pythonistashttps://blog.wificidr.net/posts/monoids-for-pythonistas/Daniel Justice<div><div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt">
</div><div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="Monoids-for-Pythonistas">Monoids for Pythonistas<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#Monoids-for-Pythonistas">¶</a></h2><p><a href="https://bartoszmilewski.com/">Bartosz Milewsky</a> has a great series of articles on category theory as it applies to programmers.
I'm not going to lie, the material is pretty dense which is par for the course for a lot of mathematics subjects (maybe it's worth mentioning that Bartosz has a PhD in Theoretical Physics 😬).
However, many of the core concepts are reasonably simple abstractions, and programmers know all about abstractions!
The language can be as big a hurdle as reading mathematics symbols, but these are the tools mathematicians use to convey ideas in an elegant and concise way.</p>
<p>I promise that you know what monoids are - you use them every day as a programmer!
I am going to flip the script and try to work my way to the definition instead of the other way around.
Hopefully, with a bit of patience, we can uncover the power of these ideas and see how they apply to our day-to-day jobs!</p>
<p><strong>Disclaimer</strong> This is not meant to be a mathematically rigorous discussion. Many small details are overlooked in favor of a high-level understanding. However, please make me aware of any egregious offenses!</p>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt">
</div><div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h3 id="Sets">Sets<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#Sets">¶</a></h3><p>As a programmer, you <em>should</em> know what a set is - an unordered collection of unique items of the same type (and if not, maybe a blog post for another day!).</p>
<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="n">my_pets</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"Lamby"</span><span class="p">,</span> <span class="s2">"Dozer"</span><span class="p">,</span> <span class="s2">"Onyx"</span><span class="p">,</span> <span class="s2">"BlackHen"</span><span class="p">,</span> <span class="s2">"RedHen"</span><span class="p">,</span> <span class="s2">"NoisyRooster"</span><span class="p">}</span>
<span class="o">>>></span> <span class="n">my_pets</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s2">"BuffHen"</span><span class="p">)</span>
<span class="o">>>></span> <span class="s2">"Frank"</span> <span class="ow">in</span> <span class="n">my_pets</span>
<span class="kc">False</span>
<span class="o">>>></span> <span class="n">my_pets</span><span class="o">.</span><span class="n">add</span><span class="p">(</span><span class="s2">"BuffHen"</span><span class="p">)</span> <span class="c1"># `add` is idempotent and won't create a duplicate</span>
<span class="o">>>></span> <span class="n">my_pets</span> <span class="c1"># Notice that the order isn't consistent</span>
<span class="p">{</span><span class="s1">'NoisyRooster'</span><span class="p">,</span> <span class="s1">'RedHen'</span><span class="p">,</span> <span class="s1">'BuffHen'</span><span class="p">,</span> <span class="s1">'BlackHen'</span><span class="p">,</span> <span class="s1">'Lamby'</span><span class="p">,</span> <span class="s1">'Onyx'</span><span class="p">,</span> <span class="s1">'Dozer'</span><span class="p">}</span>
<span class="o">>>></span>
<span class="o">>>></span> <span class="n">pets_son_feeds</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"Shayna"</span><span class="p">,</span> <span class="s2">"Lamby"</span><span class="p">}</span>
<span class="o">>>></span> <span class="n">pets_son_feeds</span><span class="o">.</span><span class="n">intersection</span><span class="p">(</span><span class="n">my_pets</span><span class="p">)</span>
<span class="p">{</span><span class="s1">'Lamby'</span><span class="p">}</span>
<span class="o">>>></span>
</pre></div>
<p>The integers form a set, $\mathbb{I} = \{-1,0,1,2,3,4...\}$.
Rational numbers form a set, $\mathbb{Q} = \{-99.42,0,1.1,1.2,1.3...\}$.
Character strings even form a set, $\{\text{"a", "b", "c", "foo", "cow", "Bob", ...}\}$.
For the purposes of this article, a "set" is a mathematical set, not constrained by physical memory limits of computers.</p>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt">
</div><div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h3 id="Symbols">Symbols<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#Symbols">¶</a></h3><p>Symbols are the syntax of mathematics.</p>
<h4 id="$\forall$">$\forall$<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#%24%5Cforall%24">¶</a></h4><p>Means "for all". Given a set, "<em>for every element</em> in the set." In the definition of a monoid, we will use this to assert a property of the items in the set. It is somewhat analogous to a <code>for</code> loop over all the items.</p>
<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="n">odds</span> <span class="o">=</span> <span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">9</span><span class="p">}</span>
<span class="o">>>></span> <span class="k">for</span> <span class="n">odd</span> <span class="ow">in</span> <span class="n">odds</span><span class="p">:</span> <span class="c1"># ∀ in the set...</span>
<span class="o">...</span> <span class="k">assert</span> <span class="n">odd</span> <span class="o">%</span> <span class="mi">2</span> <span class="o">==</span> <span class="mi">1</span> <span class="c1"># Assert that an odd divided by 2 has a remainder of 1</span>
<span class="o">...</span>
<span class="o">>>></span> <span class="c1"># Success!</span>
</pre></div>
<h4 id="$\in$">$\in$<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#%24%5Cin%24">¶</a></h4><p>means "in" or "an element of". 1 is <em>an element of</em> the set of integers.</p>
<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="n">some_primes</span> <span class="o">=</span> <span class="p">{</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">11</span><span class="p">}</span>
<span class="o">>>></span> <span class="mi">7</span> <span class="ow">in</span> <span class="n">some_primes</span> <span class="c1"># 7 ∈ some_primes == True</span>
<span class="kc">True</span>
<span class="o">>>></span>
</pre></div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt">
</div><div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h3 id="More-terminology">More terminology<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#More-terminology">¶</a></h3><h4 id="Binary-operation">Binary operation<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#Binary-operation">¶</a></h4><p>An operation that works on two (hence binary) values; addition, multiplication, division, etc. Most of the Python numeric <a href="https://docs.python.org/3/reference/datamodel.html#emulating-numeric-types">dunder methods</a> are binary operations.</p>
<h4 id="Associative">Associative<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#Associative">¶</a></h4><p>If an operation is associative, the order of <em>composition</em> doesn't matter.
This property is <strong>very</strong> important as we'll see a bit later.</p>
<ul>
<li>Addition <strong>is</strong> associative. 1 + (2 + 3) is the same as (1 + 2) + 3.</li>
<li>String concatenation <strong>is</strong> associative. "A" + ("B" + "C") == ("A" + "B") + "C"<ul>
<li>Unlike numeric addition where 1 + 2 == 2 + 1, string concatenation is <strong>not</strong> <em>commutative</em>! Monoids only depend on the associative property. (See <a href="https://blogs.ncl.ac.uk/andreymokhov/united-monoids/">Andrey Mokhov's article</a> for a fascinating deep-dive into this subject)</li>
</ul>
</li>
<li>Subtraction is <strong>not</strong> associative as order of composition matters. 1 - (2 - 3) is <strong>not equal</strong> to (1 - 2) - 3.</li>
</ul>
<h4 id="Closed">Closed<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#Closed">¶</a></h4><p>For a given operation, the input and output types must match for the operation to be closed.</p>
<ul>
<li>The minimum of two integers maps back into the set of integers.</li>
</ul>
<div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">min_</span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
<span class="k">return</span> <span class="nb">min</span><span class="p">(</span><span class="n">a</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
</pre></div>
<ul>
<li>Multiplication of two integers stays within the set of integers.</li>
</ul>
<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="nb">type</span><span class="p">(</span><span class="mi">42</span> <span class="o">*</span> <span class="mi">7</span><span class="p">)</span>
<span class="o"><</span><span class="k">class</span> <span class="err">'</span><span class="nc">int</span><span class="s1">'></span>
<span class="o">>>></span> <span class="n">a</span> <span class="o">=</span> <span class="mi">42</span>
<span class="o">>>></span> <span class="n">b</span> <span class="o">=</span> <span class="mi">7</span>
<span class="o">>>></span> <span class="nb">type</span><span class="p">(</span><span class="n">a</span><span class="p">)</span>
<span class="o"><</span><span class="k">class</span> <span class="err">'</span><span class="nc">int</span><span class="s1">'></span>
<span class="o">>>></span> <span class="nb">type</span><span class="p">(</span><span class="n">b</span><span class="p">)</span>
<span class="o"><</span><span class="k">class</span> <span class="err">'</span><span class="nc">int</span><span class="s1">'></span>
<span class="o">>>></span> <span class="nb">type</span><span class="p">(</span><span class="n">a</span> <span class="o">*</span> <span class="n">b</span><span class="p">)</span>
<span class="o"><</span><span class="k">class</span> <span class="err">'</span><span class="nc">int</span><span class="s1">'></span>
<span class="o">>>></span>
</pre></div>
<ul>
<li>Floating point division of two integers returns a float, which is outside the set of integers. The <em>input</em> and <em>output</em> types don't match. This is <strong>not</strong> a closed operation.</li>
</ul>
<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="nb">type</span><span class="p">(</span><span class="mi">42</span> <span class="o">/</span> <span class="mi">33</span><span class="p">)</span>
<span class="o"><</span><span class="k">class</span> <span class="err">'</span><span class="nc">float</span><span class="s1">'></span>
<span class="o">>>></span>
</pre></div>
<ul>
<li>The <code>chr</code> function in Python takes an integer and returns a <code>str</code>. This operation is <strong>not</strong> closed for the same reason; the output is of a different type than the input.</li>
</ul>
<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="nb">type</span><span class="p">(</span><span class="nb">chr</span><span class="p">(</span><span class="mi">51</span><span class="p">))</span>
<span class="o"><</span><span class="k">class</span> <span class="err">'</span><span class="nc">str</span><span class="s1">'></span>
<span class="o">>>></span>
</pre></div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt">
</div><div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h3 id="Axioms">Axioms<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#Axioms">¶</a></h3><p>We're almost there! We need to mention identity. The importance of identity is a bit of a rabbit hole, so I'll leave it as an exercise for the reader to dig deeper (abstract algebra rings and rngs).</p>
<p>An identity element is an element that does not change the output when paired with any other input.</p>
<h4 id="Identity">Identity<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#Identity">¶</a></h4><ul>
<li>Numeric addition: 0 is the identity (works with reals, integers, naturals, etc).</li>
</ul>
<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="mi">42</span> <span class="o">+</span> <span class="mi">0</span>
<span class="mi">42</span>
<span class="o">>>></span>
</pre></div>
<ul>
<li>Numeric multiplication: 1 is the identity (gotcha! The identity is dependent on the <em>operation</em> and the <em>type</em>).</li>
</ul>
<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="mi">42</span> <span class="o">*</span> <span class="mi">1</span>
<span class="mi">42</span>
<span class="o">>>></span>
</pre></div>
<ul>
<li>List concatenation: the empty list is the identity</li>
</ul>
<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span> <span class="o">+</span> <span class="p">[]</span>
<span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">3</span><span class="p">]</span>
<span class="o">>>></span>
</pre></div>
<p>The reason we need this is a little subtle.
We are using a binary operation to make a computation, but what do we do when we only have one value to work with?
Many of the use cases of monoids are recursive in nature, so what do you do when you reduce a list to one element?
This is fairly representative of the problem (or imagine if <code>functools.reduce</code> didn't take a default argument).</p>
<div class="highlight"><pre><span></span><span class="o">>>></span> <span class="nb">list</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span><span class="p">,</span> <span class="p">[</span><span class="mi">42</span><span class="p">]))</span>
<span class="n">Traceback</span> <span class="p">(</span><span class="n">most</span> <span class="n">recent</span> <span class="n">call</span> <span class="n">last</span><span class="p">):</span>
<span class="n">File</span> <span class="s2">"<stdin>"</span><span class="p">,</span> <span class="n">line</span> <span class="mi">1</span><span class="p">,</span> <span class="ow">in</span> <span class="o"><</span><span class="n">module</span><span class="o">></span>
<span class="ne">TypeError</span><span class="p">:</span> <span class="o"><</span><span class="k">lambda</span><span class="o">></span><span class="p">()</span> <span class="n">missing</span> <span class="mi">1</span> <span class="n">required</span> <span class="n">positional</span> <span class="n">argument</span><span class="p">:</span> <span class="s1">'y'</span>
<span class="o">>>></span>
</pre></div>
<p>We can use the identity!
This allows the developer to safely perform a binary operation on a single element without changing the output.</p>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt">
</div><div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h3 id="Definition">Definition<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#Definition">¶</a></h3><p>Hopefully, we now have the tools to unpack this definition...</p>
<p>According to Eric Weisstein on Wolfram, a monoid is "a set that is closed under an associative binary operation and has an identity element $I \in S$ such that $ \forall a \in S, Ia=aI=a$".</p>
<p>I don't like that definition.
What the heck does $Ia=aI=a$ mean?
It <em>looks</em> like multiplication, but that is one of many possible binary operations.
Here's my rephrasing:<br>
A monoid consists of a set, $S$, that is closed under an associative binary operation, $f$, and has an identity element $I \in S$ such that $ \forall a \in S, f(I, a)=f(a, I)=a$.</p>
<p>A monoid has three components.</p>
<ul>
<li>The set $S$, often called the "carrier set". Programmers know this as a type (int, float, double, etc).</li>
<li>$I$ is the identity element, which we identified in the previous section</li>
<li>A binary operation. Addition, multiplication, list concatenation, etc.</li>
</ul>
<p>I said in the intro that you already know what a monoid is.
Here are some examples:</p>
<ul>
<li>Integers, addition, 0</li>
<li>Reals, multiplication, 1</li>
<li>Lists, concatenation, [ ]</li>
</ul>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt">
</div><div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h3 id="Why-it-matters">Why it matters<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#Why-it-matters">¶</a></h3><p>At this point, you might be thinking about whipping me with a large stick for describing simple arithmetic in such obtuse and abstract terms.
It matters, I promise!
These definitions allow us to precisely identify objects with certain properties that support certain operations.
Enough math, why does this matter to programmers?!</p>
<h4 id="Divide">Divide<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#Divide">¶</a></h4><p>Remember how I said that associative property would be important later on?
Consider the following items... they're all equivalent.</p>
<pre><code>1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10
(1 + 2) + (3 + 4 + 5 + 6 + 7 + 8 + 9 + 10)
(1 + (2 + (3 + (4 + (5 + (6 + (7 + (8 + (9 + 10)))))))))
(((((((((1 + 2) + 3) + 4) + 5) + 6) + 7) + 8) + 9) + 10)
</code></pre>
<p>Here's another example.</p>
<pre><code>"a" + "b" + "c" + "d" + "e" + "f" + "g" + "h" + "i" + "j"
"ab" + "cd" + "ef" + "gh" + "ij"
"abcd" + "efgh" + "ij"
"abcdefgh" + "ij"
"abcdefghij"
</code></pre>
<p>I can write those down on a set of index cards and pass the pairs to two people at a time (think two CPU cores).
As long as I track the order of the results, I can recursively continue this process in any order of composition until the task is done.
This process succeeds even if the workers have different computational capacities.
The answer is the same as if one poor individual toiled away at the tasks independently.</p>
<h4 id="More-cores,-more-computers">More cores, more computers<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#More-cores,-more-computers">¶</a></h4><p>If the list is really long or the binary operation is non-trivial, we can <strong>safely</strong> and easily divide this tasks amongst different cores - or even different computers.
Notice that we are talking about <em>CPU</em>-bound tasks, not <em>IO</em>-bound tasks (multiple cores do little to alleviate the latter).
Python doesn't have the ability to do this natively, but other languages do.</p>
<h5 id="But-be-careful">But be careful<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#But-be-careful">¶</a></h5><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">multiprocessing</span> <span class="kn">import</span> <span class="n">Pool</span><span class="p">,</span> <span class="n">cpu_count</span>
<span class="kn">import</span> <span class="nn">timeit</span>
<span class="k">def</span> <span class="nf">read_data</span><span class="p">(</span><span class="n">fname</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
<span class="sd">"""Read Wiki tables"""</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">fname</span><span class="p">)</span> <span class="k">as</span> <span class="n">fp</span><span class="p">:</span>
<span class="n">items</span> <span class="o">=</span> <span class="n">fp</span><span class="o">.</span><span class="n">readlines</span><span class="p">()</span>
<span class="k">return</span> <span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">item</span><span class="p">:</span> <span class="nb">int</span><span class="p">(</span><span class="n">item</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">" "</span><span class="p">)[</span><span class="mi">3</span><span class="p">]),</span> <span class="n">items</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">do_it</span><span class="p">(</span><span class="n">fname</span><span class="p">):</span>
<span class="sd">"""Read data and perform calculation"""</span>
<span class="n">data</span> <span class="o">=</span> <span class="n">read_data</span><span class="p">(</span><span class="n">fname</span><span class="p">)</span>
<span class="k">return</span> <span class="nb">sum</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">multi_proc_demo</span><span class="p">()</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
<span class="n">N_PROC</span> <span class="o">=</span> <span class="n">cpu_count</span><span class="p">()</span>
<span class="c1"># Start the processes</span>
<span class="k">with</span> <span class="n">Pool</span><span class="p">(</span><span class="n">processes</span><span class="o">=</span><span class="n">N_PROC</span><span class="p">)</span> <span class="k">as</span> <span class="n">p</span><span class="p">:</span>
<span class="n">results</span> <span class="o">=</span> <span class="p">[]</span>
<span class="n">fnames</span> <span class="o">=</span> <span class="p">[</span>
<span class="s2">"data/pagecounts-20090601-000000"</span><span class="p">,</span>
<span class="s2">"data/pagecounts-20090601-010000"</span><span class="p">,</span>
<span class="s2">"data/pagecounts-20090601-020000"</span>
<span class="p">]</span>
<span class="k">for</span> <span class="n">fname</span> <span class="ow">in</span> <span class="n">fnames</span><span class="p">:</span>
<span class="n">results</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">p</span><span class="o">.</span><span class="n">apply_async</span><span class="p">(</span><span class="n">do_it</span><span class="p">,</span> <span class="n">kwds</span><span class="o">=</span><span class="p">{</span><span class="s1">'fname'</span><span class="p">:</span> <span class="n">fname</span><span class="p">}))</span>
<span class="nb">sum</span><span class="p">([</span><span class="n">result</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">timeout</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span> <span class="k">for</span> <span class="n">result</span> <span class="ow">in</span> <span class="n">results</span><span class="p">])</span>
<span class="k">def</span> <span class="nf">single_proc_demo</span><span class="p">()</span> <span class="o">-></span> <span class="kc">None</span><span class="p">:</span>
<span class="c1"># cat data/pagecounts* >> all.txt</span>
<span class="nb">sum</span><span class="p">(</span><span class="n">read_data</span><span class="p">(</span><span class="s2">"data/all.txt"</span><span class="p">))</span>
<span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Multi-proc: "</span><span class="p">,</span> <span class="n">timeit</span><span class="o">.</span><span class="n">Timer</span><span class="p">(</span><span class="s2">"multi_proc_demo()"</span><span class="p">,</span> <span class="nb">globals</span><span class="o">=</span><span class="nb">globals</span><span class="p">())</span><span class="o">.</span><span class="n">repeat</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">10</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"Single-proc: "</span><span class="p">,</span> <span class="n">timeit</span><span class="o">.</span><span class="n">Timer</span><span class="p">(</span><span class="s2">"single_proc_demo()"</span><span class="p">,</span> <span class="nb">globals</span><span class="o">=</span><span class="nb">globals</span><span class="p">())</span><span class="o">.</span><span class="n">repeat</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mi">10</span><span class="p">))</span>
</pre></div>
<div class="highlight"><pre><span></span>python wiki.py
Multi-proc: <span class="o">[</span><span class="m">28</span>.17182195998612, <span class="m">28</span>.688616012994316, <span class="m">28</span>.539513622003142, <span class="m">28</span>.077732990990626, <span class="m">28</span>.294970447997912<span class="o">]</span>
Single-proc: <span class="o">[</span><span class="m">75</span>.17859069499536, <span class="m">73</span>.89702447700256, <span class="m">74</span>.0759316909971, <span class="m">74</span>.918352623994, <span class="m">74</span>.7120841670112<span class="o">]</span>
</pre></div>
<p>The example above uses a publicly-available dataset from Wikipedia (linked in the References).
I spent way too much time fighting <code>multiprocessing</code>, but I learned a lot along the way.
Always RTFM!
There are several caveats to the library, and it is essential to read the <a href="https://docs.python.org/3/library/multiprocessing.html#programming-guidelines">Programming Guidelines</a>.
One of the issues is how Python copies memory to child processes.
This serialization overhead is huge, so it is much better for your child processes to retrieve their own data.
When in doubt, measure (the <code>timeit</code> library is great for this)!
What does this have to do with monoids?</p>
<p>Since addition is a moniod, we can safely split the work up and aggregate the results.
It is also commutative which means that we don't even have to track the order of the results.
Not all operations are commutative, though!
If you are multiplying a bunch of matrices, you can still batch the computation, but you <em>must</em> track the order of the results.</p>
<h5 id="The-right-tool-for-the-right-job">The right tool for the right job<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#The-right-tool-for-the-right-job">¶</a></h5><p>Know your use case.
I am an avid Pythonista, but sometimes alternatives might be the better choice.
Gloang doesn't read <code>all.txt</code> any faster, but it performs the summation in milliseconds instead of seconds!
Julia as native support for <a href="https://docs.julialang.org/en/v1/manual/parallel-computing/">multi-core processing</a>, and there are other languages that do as well.</p>
<h4 id="And-conquer">And conquer<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#And-conquer">¶</a></h4><p>This divide-and-conquer concept is critical to well-known large-scale algorithms like <a href="https://hadoop.apache.org/docs/r1.2.1/mapred_tutorial.html">MapReduce</a>.</p>
<p>It doesn't mean we can't do it ourselves, though!
Libraries like <code>multiprocessing</code> allow Python developers to delegate tasks among different cores or processors.
Keep in mind that just like delegating tasks to friends, there is a computational overhead to splitting up tasks.
It is important to understand that libraries like <a href="https://numpy.org/"><code>Numpy</code></a> don't <a href="https://scipy.github.io/old-wiki/pages/ParallelProgramming">get you around the single-core limit</a> even though it is implemented in C.
Do your research!
Know your data and the problem domain before you start concatenating small strings across processes! 😜</p>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt">
</div><div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h4 id="BLS-data">BLS data<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#BLS-data">¶</a></h4><p>Let's look at some data freely available from the Bureau of Labor Statistics.
I downloaded the flat files from the link in the references and created some <code>dataclasses</code> to support our operations.
This is a quick-and-dirty module.
I'm not doing any type checking or validation in an effort to minimize boilerplate and get to the essence of the post.</p>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">In [1]:</div>
<div class="inner_cell">
<div class="input_area">
<div class=" highlight hl-ipython3"><pre><span></span><span class="kn">from</span> <span class="nn">functools</span> <span class="kn">import</span> <span class="n">partial</span><span class="p">,</span> <span class="n">reduce</span>
<span class="kn">from</span> <span class="nn">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span><span class="p">,</span> <span class="n">asdict</span>
<span class="nd">@dataclass</span><span class="p">(</span><span class="n">frozen</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">BLSRecord</span><span class="p">:</span>
<span class="sd">"""BLS table entry"""</span>
<span class="n">series_id</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">year</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">period</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">value</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">footnote_codes</span><span class="p">:</span> <span class="nb">str</span>
<span class="nd">@dataclass</span><span class="p">(</span><span class="n">frozen</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="k">class</span> <span class="nc">BLSSeries</span><span class="p">:</span>
<span class="sd">"""BLS series entry"""</span>
<span class="n">series_id</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">area_code</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">item_code</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">series_title</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">footnote_codes</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">begin_year</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">begin_period</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">end_year</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">end_period</span><span class="p">:</span> <span class="nb">str</span>
<span class="k">def</span> <span class="nf">read_with_header</span><span class="p">(</span><span class="n">fname</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">rec_type</span><span class="p">):</span>
<span class="sd">"""Read BLS tables with header row"""</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">fname</span><span class="p">)</span> <span class="k">as</span> <span class="n">fp</span><span class="p">:</span>
<span class="n">items</span> <span class="o">=</span> <span class="n">fp</span><span class="o">.</span><span class="n">readlines</span><span class="p">()</span>
<span class="c1"># Skip the header row, split, strip, and instantiate the dataclass</span>
<span class="k">return</span> <span class="nb">map</span><span class="p">(</span><span class="k">lambda</span> <span class="n">item</span><span class="p">:</span> <span class="n">rec_type</span><span class="p">(</span><span class="o">*</span><span class="nb">map</span><span class="p">(</span><span class="nb">str</span><span class="o">.</span><span class="n">strip</span><span class="p">,</span> <span class="n">item</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">"</span><span class="se">\t</span><span class="s2">"</span><span class="p">))),</span> <span class="n">items</span><span class="p">[</span><span class="mi">1</span><span class="p">::])</span>
</pre></div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt">
</div><div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h4 id="Composition">Composition<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#Composition">¶</a></h4><p>When dealing with business logic, we are often faced with brittle code when requirements change and that massive <code>if</code>/<code>else</code> block just keeps growing.
Even worse is when you realize that similar logic is needed in multiple parts of the code and it's unclear how to "glue" the pieces together.</p>
<p>Here is a much less trivial example.
One of the key axioms is that we are dealing with <strong>binary operations</strong> that map some type to the same type.
Does this type have to be a number or a string?
Not at all!
Let's reconsider <code>sum</code> on integers; it takes two integers and returns an integer.
Replace integer with "numeric type", and we can still play ball.
Can we go further?
Here is the formula for standard deviation:</p>
$$ \sigma = \sqrt{\frac{\sum_{i=1}^N (x_i - \bar{x})^2}{N-1}} $$<p>Look at the summation $\sum_{i=1}^N (x_i - \bar{x})^2$ and break it down a little.
Couldn't we also say $\sum_{i=1}^N func()$?
In other words, instead of passing some numeric type to <code>add</code>, I want to pass a <em>function</em> that <strong>returns</strong> a numeric type to <code>add</code>.
This is a perfectly valid construction!
The cool thing is that we can <em>compose</em> functions that return numeric types with the <code>add</code> binary operation.</p>
<p>We can use this idea with boolean operations to compose data queries instead of using an endless string of <code>if X and Y and Z</code> where <code>X</code>, <code>Y</code>, and <code>Z</code> tend to get lengthy in my experience.</p>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">In [24]:</div>
<div class="inner_cell">
<div class="input_area">
<div class=" highlight hl-ipython3"><pre><span></span><span class="k">def</span> <span class="nf">is_coffee_lookup</span><span class="p">(</span><span class="n">rec</span><span class="p">:</span> <span class="n">BLSRecord</span><span class="p">,</span> <span class="n">lookup</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
<span class="k">return</span> <span class="n">series_data</span><span class="p">[</span><span class="n">rec</span><span class="o">.</span><span class="n">series_id</span><span class="p">]</span><span class="o">.</span><span class="n">item_code</span> <span class="o">==</span> <span class="s2">"717311"</span>
<span class="k">def</span> <span class="nf">is_january</span><span class="p">(</span><span class="n">rec</span><span class="p">:</span> <span class="n">BLSRecord</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
<span class="k">return</span> <span class="n">rec</span><span class="o">.</span><span class="n">period</span> <span class="o">==</span> <span class="s2">"M01"</span>
<span class="k">def</span> <span class="nf">is_not_1995</span><span class="p">(</span><span class="n">rec</span><span class="p">:</span> <span class="n">BLSRecord</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
<span class="k">return</span> <span class="n">rec</span><span class="o">.</span><span class="n">year</span> <span class="o">!=</span> <span class="s2">"1995"</span>
<span class="k">def</span> <span class="nf">intersect_identity</span><span class="p">(</span><span class="n">rec</span><span class="p">:</span> <span class="n">BLSRecord</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
<span class="c1"># The "natural element" for intersections is True</span>
<span class="k">return</span> <span class="kc">True</span>
<span class="k">def</span> <span class="nf">intersect</span><span class="p">(</span><span class="n">fst_fn</span><span class="p">,</span> <span class="n">snd_fn</span><span class="p">):</span>
<span class="k">return</span> <span class="k">lambda</span> <span class="n">rec</span><span class="p">:</span> <span class="n">fst_fn</span><span class="p">(</span><span class="n">rec</span><span class="p">)</span> <span class="ow">and</span> <span class="n">snd_fn</span><span class="p">(</span><span class="n">rec</span><span class="p">)</span>
<span class="c1"># Create a lookup table for series entries</span>
<span class="n">series_data</span> <span class="o">=</span> <span class="p">{</span><span class="n">row</span><span class="o">.</span><span class="n">series_id</span><span class="p">:</span> <span class="n">row</span> <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">read_with_header</span><span class="p">(</span><span class="s2">"data/ap.series"</span><span class="p">,</span> <span class="n">BLSSeries</span><span class="p">)}</span>
<span class="c1"># Inject our state</span>
<span class="n">is_coffee</span> <span class="o">=</span> <span class="n">partial</span><span class="p">(</span><span class="n">is_coffee_lookup</span><span class="p">,</span> <span class="n">lookup</span><span class="o">=</span><span class="n">series_data</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"All records: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">price_data</span><span class="p">)</span><span class="si">:</span><span class="s2">,</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="c1"># Filter for entries that are coffee</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Coffee: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">filter</span><span class="p">(</span><span class="n">is_coffee</span><span class="p">,</span> <span class="n">price_data</span><span class="p">)))</span><span class="si">:</span><span class="s2">,</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="c1"># Filter for entries that are in January</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"January: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">filter</span><span class="p">(</span><span class="n">is_january</span><span class="p">,</span> <span class="n">price_data</span><span class="p">)))</span><span class="si">:</span><span class="s2">,</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="c1"># Compose and profit!</span>
<span class="n">coffee_and_january</span> <span class="o">=</span> <span class="n">reduce</span><span class="p">(</span><span class="n">intersect</span><span class="p">,</span> <span class="p">[</span><span class="n">is_coffee</span><span class="p">,</span> <span class="n">is_january</span><span class="p">],</span> <span class="n">intersect_identity</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Coffee and January: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">filter</span><span class="p">(</span><span class="n">coffee_and_january</span><span class="p">,</span> <span class="n">price_data</span><span class="p">)))</span><span class="si">:</span><span class="s2">,</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="c1"># We can compose any number of functions that are valid Moniods!</span>
<span class="n">coffee_and_january_not_1995</span> <span class="o">=</span> <span class="n">reduce</span><span class="p">(</span><span class="n">intersect</span><span class="p">,</span> <span class="p">[</span><span class="n">is_not_1995</span><span class="p">,</span> <span class="n">is_coffee</span><span class="p">,</span> <span class="n">is_january</span><span class="p">],</span> <span class="n">intersect_identity</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Coffee and January and not 1995: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="nb">list</span><span class="p">(</span><span class="nb">filter</span><span class="p">(</span><span class="n">coffee_and_january_not_1995</span><span class="p">,</span> <span class="n">price_data</span><span class="p">)))</span><span class="si">:</span><span class="s2">,</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
</pre></div>
</div>
</div>
</div>
<div class="output_wrapper">
<div class="output">
<div class="output_area">
<div class="prompt"></div>
<div class="output_subarea output_stream output_stdout output_text">
<pre>All records: 177,894
Coffee: 786
January: 15,042
Coffee and January: 66
Coffee and January and not 1995: 62
</pre>
</div>
</div>
</div>
</div>
</div>
<div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt">
</div><div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h4 id="Conclusion">Conclusion<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#Conclusion">¶</a></h4><p>I hope I didn't chase too many rabbits; there are plenty to chase on this topic!
I also hope that this has sparked some ideas and will help you identify common patterns in software that will make your coding a bit more efficient and clean.</p>
<h3 id="References">References<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#References">¶</a></h3><ul>
<li>Weisstein, Eric W. "Monoid." From MathWorld--A Wolfram Web Resource. <a href="https://mathworld.wolfram.com/Monoid.html">https://mathworld.wolfram.com/Monoid.html</a> </li>
<li><a href="https://en.wikipedia.org/wiki/Monoid">https://en.wikipedia.org/wiki/Monoid</a></li>
<li><a href="https://wiki.haskell.org/Monoid">https://wiki.haskell.org/Monoid</a></li>
<li><a href="https://github.com/dry-python/functional-jargon-python">https://github.com/dry-python/functional-jargon-python</a></li>
<li><a href="https://planetmath.org/examplesofnoncommutativeoperations">https://planetmath.org/examplesofnoncommutativeoperations</a></li>
<li><a href="https://blogs.ncl.ac.uk/andreymokhov/united-monoids/">https://blogs.ncl.ac.uk/andreymokhov/united-monoids/</a></li>
</ul>
<h3 id="Data">Data<a class="anchor-link" href="https://blog.wificidr.net/posts/monoids-for-pythonistas/#Data">¶</a></h3><ul>
<li><a href="https://download.bls.gov/pub/time.series/ap">https://download.bls.gov/pub/time.series/ap</a></li>
<li><a href="https://archive.org/details/wikipedia_visitor_stats_200906">https://archive.org/details/wikipedia_visitor_stats_200906</a></li>
</ul>
</div>
</div>
</div></div>category-theorymathpythonhttps://blog.wificidr.net/posts/monoids-for-pythonistas/Mon, 05 Oct 2020 12:30:10 GMTCircles and Parabolashttps://blog.wificidr.net/posts/parabola/circles-and-parabolas/Daniel Justice<div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt">
</div><div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h2 id="A-few-notes-on-circles-and-tangents">A few notes on circles and tangents<a class="anchor-link" href="https://blog.wificidr.net/posts/parabola/circles-and-parabolas/#A-few-notes-on-circles-and-tangents">¶</a></h2><p>I have been <em>very</em> slowly making my way through Paul Lockhart's "Measurement". It is a fantastic book, and I highly recommend it for anyone who enjoys math as a hobby or pastime. One of the questions posed is to find the radius of the smaller, nested circle given the radius of the two larger circles (all tangent to each other and the line below them).</p>
<p><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/5/54/Sangaku_Circles.JPG/514px-Sangaku_Circles.JPG" alt="">
<small>Wikipedia</small></p>
<p>I struggled with this problem for much longer than I care to admit, and I struggled with whether or not to even write this post. I'm not terribly clever with writing, or math for that matter, but I hope others who are slogging their way through work or school will find a bit of encouragment by the end.</p>
<h3 id="Lessons-learned">Lessons learned<a class="anchor-link" href="https://blog.wificidr.net/posts/parabola/circles-and-parabolas/#Lessons-learned">¶</a></h3><p>The internet is at most of our fingertips, and it is much too easy to search for the answers to all of our questions. I think much of my own personal struggle in mathematics has been due to the fact that I am lazy. It's true. I'd much rather watch <a href="https://www.youtube.com/channel/UC1_uAIS3r8Vu6JjXWvastJg">Mathologer</a> perform magic in front of my eyes than actually <strong>do</strong> math. However, just like exercising, finding my way to the end usually provides the motivation to continue the next time around. I hope through continued practice and discovery that I will build up the skills and tools to make this a little less painful as I attempt other problems. Think of watching Danny MacAskill doing amazing tricks on his bicycle. What we don't see are the endless hours of <strong>practice</strong>, and <strong>repeated failure</strong> that led to amazing moments. This is true of every profession, but I seem to need to remind myself of it daily!</p>
<h3 id="Two-circles-tangent-to-a-line">Two circles tangent to a line<a class="anchor-link" href="https://blog.wificidr.net/posts/parabola/circles-and-parabolas/#Two-circles-tangent-to-a-line">¶</a></h3><p>The way I attacked this problem may be the most naive thing you have ever seen, but I made some personal discoveries along the way that I haven't seen in the textbooks at my disposal. Let's start with two circles tangent to each other and a horizontal line. Grab a piece of scrap paper and draw a few examples. Fix the radius of one circle, and vary the radius of the other. I have a stencil with various sizes of circles on it. I found it much easier to use for this problem than simply relying on a compass. The traditional approach to this problem is to use triangles and a few applications of the Pythagorean theorum. Don't forget my disclaimer about a naive approach!</p>
<p>Do you notice a pattern drawn by the centers of the tangential circles? They follow a quadratic curve (parabola) away from the fixed circle. After a little poking around in an algebra book, I quickly realized that given a fixed circle, the centers of the tangential circles all lie on a parabola where the fixed circle sits at the focus! Check out the Desmos link below for a demo.</p>
<p><a href="https://www.desmos.com/calculator/kgnhqs8b5r">Two circles tangent to one another</a><br>
Move the slider around to see what happens.</p>
<p>If we think about the <a href="https://www.purplemath.com/modules/parabola.htm">definition of a parabola</a>, this pattern makes sense. The vertex sits midway between the focus and the directrix. The directrix is a fixed line, and a "shortest line" between a point on the parabola and the directrix always hits the directrix at a 90-degree angle. The focus is a different case as it is a fixed point, not a line. This means a "shortest line" from the focus to a point on the parabola is a matter of connecting the dots at whatever angle they make.</p>
<p>What if we identify all the "fixed" parts of this setup? The distance between the focus and the vertex is the same as the distance between the vertex and the directrix. If we draw a line through the vertex parallel to the directrix, we've identified all the points this fixed distance from the directrix. What about the focus? Let's do the same thing. All the points at this fixed distance around the focus forms a circle lying in the "cup" of the parabola... and tangent to it. You should be noticing the same pattern here that we saw earlier. The reason is pretty neat (at least to my pea brain). By identifying a set of fixed points, we are left with the difference between the circle around the focus and the line through the vertex - and tangent to both!</p>
<p><img src="https://blog.wificidr.net/images/parabola-cir.png" alt=""></p>
<p>Let's use the previous image as an example. I have identified all of our "fixed points" (the black circle around the focus and the purple line along the x-axis) that are the focal distance from either the focus or the directrix. If we consider the point (4, 4), it's "shortest line" to the directrix is straight down and interesecting the directrix at a perpedicular angle. It is also a distance of 4 from the purple line through the vertex (and a distance of 5 to the directrix). What about the "shortest line" between the focus (0, 1) and (4, 4)? Well, the horizontal distance is 4, and the vertical distance is 3. This makes a 3-4-5 triangle, so it is 5 units away from the focus. That means it is also only 4 units from the circle around the focus. This means we can sweep a line of length 4 around the point (4, 4) and get a circle that intersects both the circle around the focus <strong>and</strong> the line through the vertex. Cool, right?!</p>
<p>To solve this problem I noticed that what is true for one circle is also true for the other. The nested, inner cicle lies at the intersection of the parabolas formed by the two bigger circles. The algebra gets a little messy since you have to locate one of the parabolas in terms of the other, but the math does work out. I'm going to present an interactive example instead of a mess of LaTex since the geometry is really the point of this post.</p>
<p><a href="https://www.desmos.com/calculator/77xvzr94v1">The solution</a><br>
<img src="https://blog.wificidr.net/images/parabola-cir-demo.png" alt=""><br>
Move the slider to see the solution adjust to changing conditions.</p>
<h3 id="Why-does-it-matter?">Why does it matter?<a class="anchor-link" href="https://blog.wificidr.net/posts/parabola/circles-and-parabolas/#Why-does-it-matter?">¶</a></h3><p>It really doesn't at the end of the day, but I learned something from it even if it was a really long-winded way around to a much simpler problem. It got me to poking around with parabolas quite a bit even though that wasn't even the point of the question... or was it? That is the beauty of mathematics to me. The breadth of material is vast to say the least, but interconnected in interesting and beautiful ways!</p>
</div>
</div>
</div>mathhttps://blog.wificidr.net/posts/parabola/circles-and-parabolas/Wed, 30 Jan 2019 01:04:39 GMTProject Euler #6https://blog.wificidr.net/posts/project-euler-6/project-euler-6/Daniel Justice<div><div class="cell border-box-sizing text_cell rendered"><div class="prompt input_prompt">
</div><div class="inner_cell">
<div class="text_cell_render border-box-sizing rendered_html">
<h3 id="Project-Euler-problem-6">Project Euler problem 6<a class="anchor-link" href="https://blog.wificidr.net/posts/project-euler-6/project-euler-6/#Project-Euler-problem-6">¶</a></h3><p>I stumbled across Project Euler (at <a href="https://www.hackerrank.com/contests/projecteuler/challenges/euler006">HackerRank</a> or at <a href="https://projecteuler.net/">Project Euler</a>) a while back, and it has been a low item on my to-do list for some time. It can be quite frustrating at first, but I find it rewarding each time I unlock a little piece of the mathematical puzzle. Of course you can cheat, but that completely defeats the purpose and harms no one but yourself. I hope I can demonstrate why with this example.</p>
<ul>
<li>On a side note, HackerRank typically penalizes you for brute force methods. They force you to really think about the problems.</li>
</ul>
<p><a href="https://projecteuler.net/problem=6">Problem 6</a> asks, "Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum." In other words, what is
$$ \big|\big(\sum_1^n n\big)^2 - \big(\sum_1^n n^2\big)\big| $$</p>
<p>I was already familiar with <a href="http://mathandmultimedia.com/2010/09/15/sum-first-n-positive-integers/">Gauss's method</a> of summing the first series of natural numbers. What I did not know how to do was to sum their squares. The brute force method will yield a result, but HackerRank will penalize you with a timeout if you go that route. I chose to dig deeper...</p>
<h4 id="A-row-of-boxes">A row of boxes<a class="anchor-link" href="https://blog.wificidr.net/posts/project-euler-6/project-euler-6/#A-row-of-boxes">¶</a></h4><p>Nothing beats a little chair time with pencil and paper. I suspected a strong link between geometry and the answer to this problem, so I started drawing squares on my graph paper.</p>
<p><img src="https://blog.wificidr.net/images/boxes.png" alt=""><!-- .element height="50%" width="50%" --></p>
<p>If you are familiar with <a href="https://www.mathsisfun.com/algebra/triangular-numbers.html">triangle numbers</a>, you might notice the pattern of the right edge of each square. It is the series 1, 3, 6, 10, 15... It is really fascinating to me how these things relate to one another. The formula to find the nth triangle number is the same formula to find the sum of a series of natural numbers!<br>
$$ \sum_1^n n=\frac{n(n+1)}{2} $$</p>
<p>Therefore, to find the area of the enclosing rectangle, we simply multiply the sum from above by our 'n'. Now, we need to account for all those little strips of triangle numbers that are left over on top of the squares. I have to admit, I spent quite a bit of time on this one and simply got stuck. I knew I needed to find another sum, but this time the series is the sum of the triangle numbers themselves. I needed to find a formula for the series 1, 4, 10, 20, 35...</p>
<h4 id="Tetrahedral-Numbers">Tetrahedral Numbers<a class="anchor-link" href="https://blog.wificidr.net/posts/project-euler-6/project-euler-6/#Tetrahedral-Numbers">¶</a></h4><p>In an attempt to not ruin the learning process, I did an internet search for the mystery series. I immediately came across this article on <a href="https://en.wikipedia.org/wiki/Tetrahedral_number">tetrahedral numbers</a>. This was my extra quantity! To find the sum of a series of squares, subtract the (n - 1)th tetrahedral number from the area of the rectangle that overlays the boxes.</p>
<p>The tetrahedral numbers are easily visualized if you think of a triangular tower of spheres as in this image:<br>
<img src="https://upload.wikimedia.org/wikipedia/commons/8/87/Pyramid_of_35_spheres_animation_original.gif" alt=""></p>
<p><small>By Blotwell [GFDL (http://www.gnu.org/copyleft/fdl.html), CC-BY-SA-3.0 (http://creativecommons.org/licenses/by-sa/3.0/) or GPL (http://www.gnu.org/licenses/gpl.html)], from Wikimedia Commons</small></p>
<p>If you start taking apart the tower, the spheres perfectly fill in the gaps in the stack of rectangles above. If you get really bored, you can validate this like I did with a bunch of blocks and marbles. These items are readily available if you have kids. One last bit worth noting is that we only need (n - 1) tetrahedral numbers because we don't need to fill in the last row.</p>
<p>Finally, the sum of a series of squares can be found by:
$$ \sum_1^n n^2=n * \frac{n(n+1)}{2} - \frac{(n - 1)(n)(n+1)}{6} $$</p>
$$ =\frac{n^3+n^2}{2} - \frac{(n^2-n)(n+1)}{6} $$<p></p>
$$ =\frac{n^3+n^2}{2} - \frac{n^3+n^2-n^2-n}{6} $$<p></p>
$$ =\frac{3(n^3+n^2)}{6} - \frac{n^3-n}{6} $$<p></p>
$$ =\frac{3n^3+3n^2-n^3+n}{6} $$<p></p>
$$ \sum_1^n n^2=\frac{2n^3+3n^2+n}{6} $$<p></p>
<h4 id="Why-does-this-matter?">Why does this matter?<a class="anchor-link" href="https://blog.wificidr.net/posts/project-euler-6/project-euler-6/#Why-does-this-matter?">¶</a></h4><p>Not to sound overly cliché, but it is easy, often too easy, to find answers to all of our questions at our fingertips. For example, if you want to cut right to the chase, you could have a look at <a href="http://www.mathblog.dk/project-euler-problem-6/">this answer</a>. Be sure to take a look at the <a href="https://trans4mind.com/personal_development/mathematics/series/sumNaturalSquares.htm">proof</a> as well. Both posts are respectable, but I think they hide the intuition in the formulas.</p>
<p>In my search for an answer I learned something new, and I also saw the problem in a new light not illustrated at either of the previous links. Pardon my drawing skills, but imagine the previous image as a stack of cubes, all laid on top of one another pushed to one side. Here's a top-down view of what I'm describing:</p>
<p><img src="https://blog.wificidr.net/images/stacked_boxes.png" alt=""><!-- .element height="50%" width="50%" --></p>
<p>If you imagine the sum of squares as rows of stacked boxes, you can clearly see the relationship between the sum and the tetrahedral numbers from earlier (the missing space to be subtracted)!</p>
<p>I hope I have encouraged you to dig a little deeper the next time you run across a tricky math problem and to look for a deeper understanding of the fundamental forces that shape it.</p>
</div>
</div>
</div>
<div class="cell border-box-sizing code_cell rendered">
<div class="input">
<div class="prompt input_prompt">In [ ]:</div>
<div class="inner_cell">
<div class="input_area">
<div class=" highlight hl-ipython3"><pre><span></span>
</pre></div>
</div>
</div>
</div>
</div></div>mathpythonhttps://blog.wificidr.net/posts/project-euler-6/project-euler-6/Tue, 23 Jan 2018 02:35:26 GMT