<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://www.arbitrary-but-fixed.net/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.arbitrary-but-fixed.net/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-02-26T15:35:09+00:00</updated><id>https://www.arbitrary-but-fixed.net/feed.xml</id><title type="html">Arbitrary but fixed</title><subtitle>In this blog I share fixes to problems that I encounter in my work as an AI engineer or in my private projects. Topics are arbitrary, but somewhat centered around computer science and systems biology.
</subtitle><author><name>Christopher Schölzel</name></author><entry xml:lang="en"><title type="html">Implementing Python decorators with parameters and type hints</title><link href="https://www.arbitrary-but-fixed.net/python/type%20system/2025/12/18/python-decorators-with-parameters-and-types.html" rel="alternate" type="text/html" title="Implementing Python decorators with parameters and type hints" /><published>2025-12-18T21:09:00+00:00</published><updated>2025-12-18T21:09:00+00:00</updated><id>https://www.arbitrary-but-fixed.net/python/type%20system/2025/12/18/python-decorators-with-parameters-and-types</id><content type="html" xml:base="https://www.arbitrary-but-fixed.net/python/type%20system/2025/12/18/python-decorators-with-parameters-and-types.html"><![CDATA[<p>Python decorators are a mighty tool to create frameworks and hide the complexity of using them.
From built-ins like <code class="language-plaintext highlighter-rouge">@dataclass</code> and <code class="language-plaintext highlighter-rouge">@classmethod</code> to pytest’s <code class="language-plaintext highlighter-rouge">@fixture</code> or pydantic’s <code class="language-plaintext highlighter-rouge">@field_validator</code>, we use them regularly, but we rarely have to think about how they work exactly.</p>

<p>Recently, I wanted to implement a decorator that re-tries any function with an (optional) exponential backoff.
Obviously, the settings for the backoff should be configurable, but users shouldn’t <em>have</em> to do that if they are fine with the defaults.</p>

<p>Essentially, I wanted both of the following applications to work:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">@</span><span class="n">retrying</span>
<span class="k">def</span> <span class="nf">may_fail_occasionally</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
     <span class="p">...</span>

<span class="o">@</span><span class="n">retrying</span><span class="p">(</span><span class="n">max_retries</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">initial_sleep</span><span class="o">=</span><span class="mf">0.1</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">may_also_fail_occasionally</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
     <span class="p">...</span>
</code></pre></div></div>

<p>This looks innocent enough, right?
I’m sure you’ve seen decorators with and without parameters before.
Let’s start with the first case without parameters.
An implementation may look like the following:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">retrying</span><span class="p">(</span><span class="n">f</span><span class="p">):</span>
    <span class="n">max_retries</span> <span class="o">=</span> <span class="mi">5</span>
    <span class="n">initial_sleep</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="n">multiplier</span> <span class="o">=</span> <span class="mi">2</span>
    <span class="n">max_sleep</span> <span class="o">=</span> <span class="mi">15</span> <span class="o">*</span> <span class="mi">60</span>
    <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
        <span class="n">exceptions</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="n">sleep_time</span> <span class="o">=</span> <span class="n">initial_sleep</span>
        <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">max_retries</span><span class="p">):</span>
             <span class="k">try</span><span class="p">:</span>
                 <span class="k">return</span> <span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">),</span> <span class="n">exceptions</span><span class="p">)</span>
                 <span class="n">sleep_time</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">max_sleep</span><span class="p">,</span> <span class="n">sleep_time</span> <span class="o">*</span> <span class="n">multiplier</span><span class="p">)</span>
                 <span class="n">sleep</span><span class="p">(</span><span class="n">sleep_time</span><span class="p">)</span>
             <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
                 <span class="n">exceptions</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">wrapper</span>
</code></pre></div></div>

<h2 id="type-hints">Type hints</h2>

<p>So far so well, but let’s look at the types a bit more in detail.
As you can see, I’ve decided to modify the return type of the wrapped function, adding the exceptions that we silently ignored as a second return value.
It would be nice if we could indicate that in the function signature with proper type hints.
For that, we have to think a bit more about what a decorator <em>is</em>.
Essentially, we have a higher-oder function that takes a function <code class="language-plaintext highlighter-rouge">f</code> as argument and returns a modified version of that function:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">P</span> <span class="o">=</span> <span class="n">ParamList</span><span class="p">(</span><span class="s">"P"</span><span class="p">)</span>
<span class="n">R</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s">"R"</span><span class="p">)</span>

<span class="k">def</span> <span class="nf">retrying</span><span class="p">(</span><span class="n">f</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">[</span><span class="n">R</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">Exception</span><span class="p">]]]:</span>
    <span class="p">...</span>
</code></pre></div></div>

<p>We use the handy <code class="language-plaintext highlighter-rouge">TypeVar</code> and <code class="language-plaintext highlighter-rouge">ParamList</code> classes to indicate that we keep the parameters and return type of the wrapped function intact, just adding something to the latter.</p>

<h2 id="adding-parameters-to-the-mix">Adding parameters to the mix</h2>

<p>Now that we have covered the case without paramers, we just need to move the local variables like <code class="language-plaintext highlighter-rouge">initial_sleep</code> into the function signature, and we’re good, right?</p>

<p>Unfortunately, that’s wrong.
Consider our use case again:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">@</span><span class="n">retrying</span>
<span class="k">def</span> <span class="nf">may_fail_occasionally</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
     <span class="p">...</span>

<span class="o">@</span><span class="n">retrying</span><span class="p">(</span><span class="n">max_retries</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">initial_sleep</span><span class="o">=</span><span class="mf">0.1</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">may_also_fail_occasionally</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
     <span class="p">...</span>
</code></pre></div></div>

<p>In the decorator application <code class="language-plaintext highlighter-rouge">@retrying</code>, the <code class="language-plaintext highlighter-rouge">retrying</code> refers to the <em>function</em> itself.
We now want to replace that by something like <code class="language-plaintext highlighter-rouge">@retrying(max_retries=10)</code>, which is a function <em>call</em>.
So if we want to have parameters in our decorator, we need to build a function A that takes the parameters as an input and returns a function B that takes a function C as an argument and returns a modified version of C. 😵‍💫
With that, our return type changes to the following:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">Callable</span><span class="p">[</span>
    <span class="p">[</span><span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">]],</span>
    <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">[</span><span class="n">R</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">Exception</span><span class="p">]]]</span>
<span class="p">]</span>
</code></pre></div></div>

<p>Now we’re in Haskell-level type signature land. :laughing:
Armed with that information, let’s re-implement <code class="language-plaintext highlighter-rouge">retrying</code> with parameters while keeping the types straight (buckle in!).</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Function A
# Returns the actual decorator.
</span><span class="k">def</span> <span class="nf">retrying</span><span class="p">(</span><span class="n">max_retries</span><span class="p">:</span> <span class="nb">int</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span> <span class="n">initial_sleep</span><span class="p">:</span> <span class="nb">int</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">multiplier</span><span class="p">:</span> <span class="nb">int</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">max_sleep</span><span class="p">:</span> <span class="nb">int</span><span class="o">=</span><span class="mi">15</span> <span class="o">*</span> <span class="mi">60</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Callable</span><span class="p">[</span>
    <span class="p">[</span><span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">]],</span>
    <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">[</span><span class="n">R</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">Exception</span><span class="p">]]]</span>
<span class="p">]:</span>
    <span class="c1"># Function B
</span>    <span class="c1"># The decorator, very similar to our initial version or retrying.
</span>    <span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">f</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">[</span><span class="n">R</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">Exception</span><span class="p">]]]:</span>
        <span class="c1"># Function C
</span>        <span class="c1"># The wrapper that replaces the function the decorator is applied to.
</span>        <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">tuple</span><span class="p">[</span><span class="n">R</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">Exception</span><span class="p">]]:</span>
            <span class="n">exceptions</span> <span class="o">=</span> <span class="p">[]</span>
            <span class="n">sleep_time</span> <span class="o">=</span> <span class="n">initial_sleep</span>
            <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">max_retries</span><span class="p">):</span>
                <span class="k">try</span><span class="p">:</span>
                    <span class="k">return</span> <span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">),</span> <span class="n">exceptions</span><span class="p">)</span>
                    <span class="n">sleep_time</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">max_sleep</span><span class="p">,</span> <span class="n">sleep_time</span> <span class="o">*</span> <span class="n">multiplier</span><span class="p">)</span>
                    <span class="n">sleep</span><span class="p">(</span><span class="n">sleep_time</span><span class="p">)</span>
                <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
                    <span class="n">exceptions</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">wrapper</span>
    <span class="k">return</span> <span class="n">decorator</span>
</code></pre></div></div>

<p>Yeah, what the fuck, right?
We only wanted to have a decorator with parameters and what we had to implement is essentially a decorator factory which returns a decorator that is itself a factory for creating wrapper functions.
It looks crazy, but that’s what has to go on under the hood to make this work.</p>

<p>At this point, you might be tempted to just keep the factory monster we have right now, use <code class="language-plaintext highlighter-rouge">@retrying()</code> instead of <code class="language-plaintext highlighter-rouge">@retrying</code> to apply the decorator and call it a day.
But we don’t do things halfway on this blog, so please fasten your seatbelts and brace yourself for a rough landing.
We’re going to marry the two versions of the decorator into one. ✈️</p>

<h2 id="supporting-both-use-without-and-with-parameters">Supporting both use without and with parameters</h2>

<p>We have two type signatures for the <code class="language-plaintext highlighter-rouge">retrying</code> function now, which we need to support both in one function.
Fortunately Python has the <code class="language-plaintext highlighter-rouge">@overload</code> decorator for that since version 3.5:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">@</span><span class="n">overload</span>
<span class="k">def</span> <span class="nf">retrying</span><span class="p">(</span><span class="n">f</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">[</span><span class="n">R</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">Exception</span><span class="p">]]]:</span>
    <span class="p">...</span>

<span class="o">@</span><span class="n">overload</span>
<span class="k">def</span> <span class="nf">retrying</span><span class="p">(</span><span class="n">max_retries</span><span class="p">:</span> <span class="nb">int</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span> <span class="n">initial_sleep</span><span class="p">:</span> <span class="nb">int</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span> <span class="n">multiplier</span><span class="p">:</span> <span class="nb">int</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span> <span class="n">max_sleep</span><span class="p">:</span> <span class="nb">int</span><span class="o">=</span><span class="mi">15</span> <span class="o">*</span> <span class="mi">60</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Callable</span><span class="p">[</span>
    <span class="p">[</span><span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">]],</span>
    <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">[</span><span class="n">R</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">Exception</span><span class="p">]]]</span>
<span class="p">]:</span>
    <span class="p">...</span>
</code></pre></div></div>

<p>Now we have some type hints that don’t <em>immediately</em> make the eyes of our users bleed, but how do we get those types together for the actual implementation?
Is there a way?
Fortunately, yes.
Since <code class="language-plaintext highlighter-rouge">f</code> is provided as a positional argument, and we want our optional parameters to be supplied as keyword arguments, we can even have a super clean definition using the <code class="language-plaintext highlighter-rouge">*</code> in the argument, which enforces that all parameters after it must be supplied as keyword arguments.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">retrying</span><span class="p">(</span>
    <span class="n">f</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">]</span> <span class="o">|</span> <span class="bp">None</span> <span class="o">=</span> <span class="bp">None</span><span class="p">,</span>
    <span class="o">*</span><span class="p">,</span>  <span class="c1"># everything after this _must_ be given as keyword argument
</span>    <span class="n">max_retries</span><span class="p">:</span> <span class="nb">int</span><span class="o">=</span><span class="mi">5</span><span class="p">,</span>
    <span class="n">initial_sleep</span><span class="p">:</span> <span class="nb">int</span><span class="o">=</span><span class="mi">1</span><span class="p">,</span>
    <span class="n">multiplier</span><span class="p">:</span> <span class="nb">int</span><span class="o">=</span><span class="mi">2</span><span class="p">,</span>
    <span class="n">max_sleep</span><span class="p">:</span> <span class="nb">int</span><span class="o">=</span><span class="mi">15</span> <span class="o">*</span> <span class="mi">60</span>
<span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Callable</span><span class="p">[</span>
    <span class="p">[</span><span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">]],</span>
    <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">[</span><span class="n">R</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">Exception</span><span class="p">]]]</span>
<span class="p">]</span> <span class="o">|</span> <span class="n">Callable</span><span class="p">[</span>
    <span class="n">P</span><span class="p">,</span>
    <span class="nb">tuple</span><span class="p">[</span><span class="n">R</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">Exception</span><span class="p">]]</span>
<span class="p">]:</span>
    <span class="k">def</span> <span class="nf">decorator</span><span class="p">(</span><span class="n">f</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">])</span> <span class="o">-&gt;</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="nb">tuple</span><span class="p">[</span><span class="n">R</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">Exception</span><span class="p">]]]:</span>
        <span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">tuple</span><span class="p">[</span><span class="n">R</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="nb">Exception</span><span class="p">]]:</span>
            <span class="n">exceptions</span> <span class="o">=</span> <span class="p">[]</span>
            <span class="n">sleep_time</span> <span class="o">=</span> <span class="n">initial_sleep</span>
            <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">max_retries</span><span class="p">):</span>
                <span class="k">try</span><span class="p">:</span>
                    <span class="k">return</span> <span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">),</span> <span class="n">exceptions</span><span class="p">)</span>
                    <span class="n">sleep_time</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">max_sleep</span><span class="p">,</span> <span class="n">sleep_time</span> <span class="o">*</span> <span class="n">multiplier</span><span class="p">)</span>
                    <span class="n">sleep</span><span class="p">(</span><span class="n">sleep_time</span><span class="p">)</span>
                <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
                    <span class="n">exceptions</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">e</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">f</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
        <span class="c1"># no function argument given =&gt; we are called with parameters
</span>        <span class="k">return</span> <span class="n">decorator</span>
    <span class="c1"># function argument is there =&gt; we are called without parameters
</span>    <span class="c1"># =&gt; we need to return the wrapper directly instead of the decorator
</span>    <span class="k">return</span> <span class="n">decorator</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
</code></pre></div></div>

<p>There we are. Safe and sound in decorator land. Please clap for the captain. ✈️ :laughing:</p>

<p>If we call <code class="language-plaintext highlighter-rouge">retrying(f)</code>, as python will do internally when we use the decorator <code class="language-plaintext highlighter-rouge">@retrying</code> without parameters, we essentially get the same solution as we had in the first version of the implementation:
We define a decorator function, but immediately apply that function, essentially stripping away the extra layer again.
If we call <code class="language-plaintext highlighter-rouge">retrying(initial_sleep=10)</code> instead, which is what happens when we use the decorator <code class="language-plaintext highlighter-rouge">@retrying(initial_sleep=10)</code>, we use the extra wrapping layer to provide the decorator function as a closure that already encapsulates the value we gave for the extra argument <code class="language-plaintext highlighter-rouge">intial_sleep</code>.</p>

<h2 id="final-remarks">Final remarks</h2>

<p>I have mixed feelings about types like <code class="language-plaintext highlighter-rouge">Callable[[Callable[P, R]], Callable[P, tuple[R, list[Exception]]]] | Callable[P,tuple[R, list[Exception]]</code>.
Let’s be honest: Nobody will understand this type when they come across it in the code or in an error message of the type checker.
Even in functional programming languages, this is the point where I would say “Just let me put <code class="language-plaintext highlighter-rouge">auto</code> as the type and figure it out yourself!”.
However, spending some time to think about the actual type that is returned by the decorator was what ultimately helped me to understand why I had to implement it that way and that, yes, there just is no easier way.
So I think I’m still in favor of typing everything you can, but I do understand if you don’t want to touch types like that with a 10-foot pole. 🙈</p>]]></content><author><name>Christopher Schölzel</name></author><category term="python" /><category term="type system" /><summary type="html"><![CDATA[Have you wondered how Python libraries can provide decorators which can both be used with and without parameters? I haven't. Until I tried to implement one myself.]]></summary></entry><entry xml:lang="en"><title type="html">COBOL quick start guide</title><link href="https://www.arbitrary-but-fixed.net/2025/01/12/cobol-quick-start.html" rel="alternate" type="text/html" title="COBOL quick start guide" /><published>2025-01-12T18:24:00+00:00</published><updated>2025-01-12T18:24:00+00:00</updated><id>https://www.arbitrary-but-fixed.net/2025/01/12/cobol-quick-start</id><content type="html" xml:base="https://www.arbitrary-but-fixed.net/2025/01/12/cobol-quick-start.html"><![CDATA[<link rel="stylesheet" href="https://unpkg.com/@highlightjs/cdn-assets@11.9.0/styles/dark.min.css" />

<script src="https://unpkg.com/@highlightjs/cdn-assets@11.9.0/highlight.min.js"></script>

<script type="text/javascript" src="https://unpkg.com/highlightjs-cobol/dist/cobol.min.js"></script>

<script>
// Only apply Highlight.js to COBOL code
hljs.configure({languages:["cobol"], cssSelector:"code.language-cobol"})
hljs.highlightAll();
</script>

<script type="text/javascript" src="/assets/js/punchcard.js"></script>

<h2 id="why-cobol">Why COBOL?</h2>

<p>COBOL is like a dragon. Every programmer has heard of it, and that it is formidable.
We’ve heard stories about people fighting the beast, but very few of us have actually seen a live COBOL program out in the wild.
If that isn’t reason enough to be intrigued, maybe its historical sgnificance brought you here, wondering about how programming was like in the days of <a href="https://de.wikipedia.org/wiki/Grace_Hopper">Grace Hopper</a> - inventor of the first compiler, rear admiral, and DPMA man of the year 1969, who democratized coding, was a passionate teacher and genuinely kept her staff happy as a manager.
Whatever your angle is, I assume you have a reason for being here, so I won’t jabber any longer about why COBOL is still interesting.</p>

<h2 id="why-this-guide">Why this guide?</h2>

<p>A lot of people have written amazing and detailed COBOL guides and here I am, having solved one exercise of <a href="https://adventofcode.com/">Advent of Code</a> 2024 in COBOL.
Why should you listen to me introducing a language I barely know?
Well, the COBOL guides I found value precision and proper contextualization over speed.
This is the opposite: If you want to spend no less than 30 minutes but be able to write a rudimentary COBOL program after that, I’m here for you.</p>

<h2 id="hello-world">Hello world</h2>

<p>Enough of the introduction. I promised speed, so here is “Hello World” in COBOL:</p>

<pre><code class="language-cobol">000001 IDENTIFICATION DIVISION.                                         HELLO
000002 PROGRAM-ID. HelloWorld.                                          HELLO
000003 PROCEDURE DIVISION.                                              HELLO
000004     DISPLAY "Hello World!".                                      HELLO
</code></pre>

<p>If you want to run this, you can install <a href="https://gnucobol.sourceforge.io/">GnuCOBOL</a> and run the following:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cobc <span class="nt">-xjO</span> hello.cob
</code></pre></div></div>

<h2 id="cobol-file-format">COBOL file format</h2>

<p>The above hello world program is written in <em>fixed format</em>, which was used for COBOL programs prior to 2002.
There is also a <em>free format</em> now, which removes the restrictions about which <em>columns</em> source code has to start (and end) in.
However, for the purpose of this guide, we go old-school because that’s more fun.</p>

<p>In <em>fixed format</em>, the actual <em>code</em> resides in columns 8-11 (Area A) and 12-72 (Area B).
This seems crazy from today’s coding standards, but was actually pretty reasonable back in the day.
You see, each of these lines of code would be written on a punchcard—yes, just one line of code per punchcard.
Why punchcards? Because they were way cheaper than any form of electronic storage back in the day.
So imagine you’ve finally written your 300 LOC program.
You’re on the way to the computer room to test it, but on the way you bump into a colleague, also holding a stack of 300 punchcards.
The cards go flying all over the place and now what?
Don’t fret! COBOL has you covered (if you followed best practices, that is).
You can just stack them in any order, put them into a sorting machine, and instruct it to first sort by column 73-80 (Program Name Area) and then by column 1-6 (Sequence Number Area).
Voilà, now both of your programs are in the right order again, and can be separated from each other!
Today, we don’t need this anymore, and we can just leave these areas empty, but I’m sure this feature saved countless work hours.</p>

<p>The only things <em>we</em> need to remember now are these:</p>

<ul>
  <li>Column 7 can be used to designate a line as a comment (<code class="language-plaintext highlighter-rouge">*</code>) or code that’s only used for debugging (<code class="language-plaintext highlighter-rouge">d</code>). There are a few other symbols that appear here, but they are not relevant for this guide.</li>
  <li>Actual code starts in Area A (column 8), or sometimes Area B (column 12).</li>
  <li>Code must end on column 72. Everyting beyond that will be ignored.</li>
</ul>

<p>Side note: If you’re weird like me and this makes you want to dive deeper into how punchcards worked, feel free to have some fun with this interactive editor for IBM 5081 punch cards:</p>

<div id="punchcard-editor" style="margin-bottom:8pt;">
<div id="punchcards"></div>
<div style="font-size:8pt;margin-bottom:8pt;">
Image: Douglas W. Jones, <a href="https://homepage.divms.uiowa.edu/~jones/cards/collection/i-onefield.shtml#IBM5081">Punched Card Collection</a>, University of Iowa, Department of Computer Science.<br />
Character arrangement: <a href="https://archive.org/details/bitsavers_ibmpunched33326ReferenceManualModel29CardPunchJun7_8896656/page/n39/mode/2up">Reference Manual: IBM 29 Card Punch, 7. ed, IBM 1970.</a>
</div>

Code <input type="text" value="000004     DISPLAY 'Hello World!'.                                      HELLO" id="pc_text" oninput="updatePunchcard();" style="width:300px;" />
Card punch <select id="pc_key_punch" oninput="updatePunchcard();">
    <option value="IBM_029_EL">IBM 029 arrangement EL</option>
    <option value="IBM_029_H">IBM 029 arrangement H</option>
</select>
<input type="checkbox" checked="1" id="pc_print" oninput="updatePunchcard();" /> Print text
<!--<input type="button" value="Download" onclick="downloadPunchcard();"/>-->
</div>
<script>
updatePunchcard();
</script>

<h2 id="cobol-syntax">COBOL syntax</h2>

<p>You might have already noticed that the hello world program above only uses characters that we would also use when writing text in English.
This is very much by design.
Grace Hopper herself heavily pushed for using English over symbols because she wanted to democratize programming and make it more accessible.</p>

<p>It’s important to keep that in mind, because unlike most modern programming languages, COBOL doesn’t aim to have a minimal number of syntactic elements and keywords (<code class="language-plaintext highlighter-rouge">if</code>, <code class="language-plaintext highlighter-rouge">for</code>, …).
Instead, it provides a plethora of keywords, which sometimes consist of more than one word and can have alternatives, to make code sound as natural as possible to a non-technical person that just writes their first program.</p>

<p>Time for another example: Let’s calculate how a user-defined amount of money grows with 1% interest over 10 years:</p>

<pre><code class="language-cobol">       IDENTIFICATION DIVISION.
       PROGRAM-ID. Interest.
       DATA DIVISION.
       WORKING-STORAGE SECTION.
       01 balance PICTURE s9(7)V99.
       PROCEDURE DIVISION.
       calculate-interest.
           DISPLAY "Please enter starting balance:"
           ACCEPT balance.
           PERFORM 10 TIMES
               MULTIPLY balance BY 1.01 GIVING balance
               DISPLAY "New balance: " balance
           END-PERFORM.
</code></pre>

<h3 id="hierarchy">Hierarchy</h3>

<p>You probably already noticed in the hello world example that COBOL code has a document-like hierarchy.
There are <code class="language-plaintext highlighter-rouge">DIVISION</code>s, and <code class="language-plaintext highlighter-rouge">SECTION</code>s that seem to structure the code.
In fact, there are the following hierarchical levels:</p>

<ul>
  <li>Divisions</li>
  <li>Sections</li>
  <li>Paragraphs</li>
  <li>Sentences</li>
  <li>Statements</li>
</ul>

<p>Divisions, sections, and paragraphs have pre-defined names and structures, with the exception of sections and paragraphs in the <code class="language-plaintext highlighter-rouge">PROCEDURE DIVISION</code>, which can be named freely by the programmer.
Sentences are groups of statements (which again have pre-defined structures) that are terminated with a dot.
One effect of this hierarchy is that the definition of variables (which are called <em>data items</em> in COBOL) in the <code class="language-plaintext highlighter-rouge">WORKING-STORAGE SECTION</code> of the <code class="language-plaintext highlighter-rouge">DATA DIVISON</code> is separated from the code in the <code class="language-plaintext highlighter-rouge">PROCEDURE DIVISION</code>.</p>

<h3 id="data-items">Data items</h3>

<p>The <code class="language-plaintext highlighter-rouge">WORKING-STORAGE SECTION</code> looks bizarre from the standpoint of modern programming languages:</p>

<pre><code class="language-cobol">      * level             signed
      * |   name          |digit
      * |   |             || 7 times
      * |   |             || | decimal point
      * |   |             || | |
       01 balance PICTURE s9(7)V99.
</code></pre>

<p>The number in the beginning is called the level.
A level of 01 designates an elementary data item.
A higher number would be used for an aggregate data item that belongs to a group item of the next lower level above it.
This allows to define and reference nested data types of arbitrary complexity.
We’ll go into a bit more detail about that later.</p>

<p>The name of the data item is pretty self-explanatory, but the <code class="language-plaintext highlighter-rouge">PICTURE</code> seems weird again.
COBOL actually never forces you to think in binary or any low-level data types for that matter.
Instead, you define your data types by their “picture”, i.e. by the format how you would write them in a text file.
The syntax for this picture part almost looks like a form of proto-regex:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">s</code> denotes a sign (either the character <code class="language-plaintext highlighter-rouge">+</code> or <code class="language-plaintext highlighter-rouge">-</code>).</li>
  <li><code class="language-plaintext highlighter-rouge">9</code> denotes a single decimal digit.</li>
  <li><code class="language-plaintext highlighter-rouge">(7)</code> repeats the character in front of it 7 times.</li>
  <li><code class="language-plaintext highlighter-rouge">V</code> denotes where the decimal point is placed.</li>
</ul>

<p>With that, the picture <code class="language-plaintext highlighter-rouge">s9(7)V99</code> stands for a signed 7-figure number with two decimal places - a pretty reasonable data item for the account balance of most people.</p>

<h3 id="procedures">Procedures</h3>

<p>Our example program has a single procedure defined by the named paragraph <code class="language-plaintext highlighter-rouge">calculate-interest</code>.
Naming the paragraph is only necessary if we aim to call it as a procedure later in the code.
By default, the first paragraph (named or unnamed) of the <code class="language-plaintext highlighter-rouge">PROCEDURE DIVISION</code> will be called as the main procedure.</p>

<p>Inside a procedure, you can create sentences out of an arbitrary number of statements.
The statements that we use here are:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">DISPLAY some-value some-other-value ...</code> to display an arbitrary number of values as a concatenated string on the terminal.</li>
  <li><code class="language-plaintext highlighter-rouge">ACCEPT data-item</code> to read user input and store it in a data item. Note how the <code class="language-plaintext highlighter-rouge">PICTURE</code> definition of that item gives you input validation for free.</li>
  <li><code class="language-plaintext highlighter-rouge">PERFORM 10 TIMES [...] END PERFORM</code> to repeat the statements in <code class="language-plaintext highlighter-rouge">[...]</code> in a loop.</li>
  <li><code class="language-plaintext highlighter-rouge">MULTIPLY x BY y GIVING z</code> to multiply the value of <code class="language-plaintext highlighter-rouge">x</code> by <code class="language-plaintext highlighter-rouge">y</code> and store the result in <code class="language-plaintext highlighter-rouge">z</code>.</li>
</ul>

<p>As already mentioned, COBOL uses many more keywords and statements than most modern programming languages, so learning to program in COBOL consists largely of searching for the right keywords and associated statements.
There are also the classical named functions that we are more used to, but those will be covered in the next section.</p>

<h2 id="solving-a-non-trivial-problem-in-cobol">Solving a non-trivial problem in COBOL</h2>

<p>We could end our quick-start instructions here, but I think the step from a toy example to one that solves an actual non-trivial task still brings a lot of insights with a good cost-benefit ratio.
The following COBOL program solves the first part of <a href="https://adventofcode.com/2024/day/2">day 2 from Advent of Code 2024</a>.</p>

<p>For this exercise, we have to read a text file with a list of numbers on each line, separated by spaces and count how many of those are “safe”.
A list is safe if:</p>

<ul>
  <li>The numbers are sorted in ascending or descending order.</li>
  <li>And the absolute difference between adjacent numbers is between 1 and 3 (inclusive).</li>
</ul>

<pre><code class="language-cobol">       IDENTIFICATION DIVISION.
       PROGRAM-ID. AoC-2024-Day2.
       ENVIRONMENT DIVISION.
       INPUT-OUTPUT SECTION.
       FILE-CONTROL.
           SELECT input-file
           ASSIGN TO "input"
           ORGANIZATION IS LINE SEQUENTIAL.
       DATA DIVISION.
       FILE SECTION.
       FD input-file.
       01 input-file-line PICTURE x(1024).
       WORKING-STORAGE SECTION.
       01 FILLER PICTURE a.
           88 at-eof VALUE 'Y' FALSE 'N'.
       01 line-cursor PICTURE 999.
       01 previous-line-cursor PICTURE 999.
       01 number-pair.
           02 previous PICTURE 999.
           02 current PICTURE 999.
       01 line-length PICTURE 999.
       01 FILLER PICTURE a.
           88 all-increasing VALUE 'Y' FALSE 'N'.
       01 FILLER PICTURE a.
           88 all-decreasing VALUE 'Y' FALSE 'N'.
       01 safe-count PICTURE 999.
       01 difference-safe PICTURE a.
           88 difference-is-safe VALUE 'Y' FALSE 'N'.
       01 difference PICTURE 999.
       PROCEDURE DIVISION.
       parse-file.
           OPEN INPUT input-file.
           PERFORM UNTIL at-eof
               READ input-file INTO input-file-line
               AT END
                   SET at-eof TO TRUE
               NOT AT END
                   PERFORM process-line
               END-READ
           END-PERFORM.
           DISPLAY "Total count of safe sequences: " safe-count.
           CLOSE input-file.
           STOP RUN.
       process-line.
           MOVE FUNCTION LENGTH(FUNCTION TRIM(input-file-line))
           TO line-length.
           MOVE 1 TO line-cursor.
           SET all-increasing TO TRUE.
           SET all-decreasing TO TRUE.
           SET difference-is-safe TO TRUE.
           PERFORM UNTIL line-cursor GREATER line-length
               MOVE line-cursor TO previous-line-cursor
               PERFORM read-next-number
               PERFORM check-if-increasing
               PERFORM check-if-decreasing
               PERFORM check-difference
           END-PERFORM.
           IF (all-increasing OR all-decreasing) AND difference-is-safe
               ADD 1 to safe-count.
       read-next-number.
           MOVE current TO previous.
           UNSTRING input-file-line
           DELIMITED BY " " INTO current
           WITH POINTER line-cursor.
       check-if-increasing.
           IF previous-line-cursor IS GREATER THAN 1
           AND previous IS GREATER THAN current
               SET all-increasing TO FALSE.
       check-if-decreasing.
           IF previous-line-cursor IS GREATER THAN 1
           AND previous IS LESS THAN current
               SET all-decreasing TO FALSE.
       check-difference.
           COMPUTE difference EQUAL FUNCTION ABS(previous - current).
           IF previous-line-cursor IS GREATER THAN 1
           AND (
               difference IS LESS THAN 1
               OR difference IS GREATER THAN 3
           )
               SET difference-is-safe TO FALSE.
</code></pre>

<h3 id="reading-an-input-file">Reading an input file</h3>

<p>The first challenge that we haven’t tackled yet is file access.
The definition of what file is accessed is handleed in the <code class="language-plaintext highlighter-rouge">INPUT-OUTPUT SECTION</code> of the <code class="language-plaintext highlighter-rouge">ENVIRONMENT DIVISION</code>:</p>

<pre><code class="language-cobol">       FILE-CONTROL.
           SELECT input-file
           ASSIGN TO "input"
           ORGANIZATION IS LINE SEQUENTIAL.
</code></pre>

<p>All of this is pretty straightforward:
We give our file the name <code class="language-plaintext highlighter-rouge">input-file</code>, provide the file name on the local file system (<code class="language-plaintext highlighter-rouge">"input"</code>), and tell COBOL that it should read the file line by line.
If you’re wondering “how else would I read a file?”, consider that COBOL mostly operates on fixed-length records.
This has the benefit of allowing random access to files, skipping to the nth record in the file in O(1) instead of having to read O(n) variable-width lines.
In that regard, our file structure is the worst case scenario from an efficiency perspective.</p>

<p>There is one more part of the definition in the <code class="language-plaintext highlighter-rouge">FILE SECTION</code> of the <code class="language-plaintext highlighter-rouge">DATA DIVISION</code> that is a little less straightforward:</p>

<pre><code class="language-cobol">       FD input-file.
       01 input-file-line PICTURE x(1024).       
</code></pre>

<p>Here, we first define what type of tile <code class="language-plaintext highlighter-rouge">input-file</code> is.
The type <code class="language-plaintext highlighter-rouge">FD</code> designates a normal file used for reading and writing data.
Another alternative would be <code class="language-plaintext highlighter-rouge">SD</code>, which designates a file that is used as a working memory area for COBOL’s sorting and merging functionality.
The next line, defines a data item that represents one record from the file.
As our file has <code class="language-plaintext highlighter-rouge">ORGANIZATION IS LINE SEQUENTIAL</code>, we effectively define what data type should be used for reading a line.
As we want to be agnostic about line size and content, we just pick 1024 alphanumerical characters.
The downside of this is obviously that if a line is less than 1024 characters long, we will still fill 1024 bytes of memory padded with spaces.</p>

<p>The code for actually reading a line from the file has again an interesting property:</p>

<pre><code class="language-cobol">       READ input-file INTO input-file-line
       AT END
           SET at-eof TO TRUE
       NOT AT END
           PERFORM process-line
       END-READ
</code></pre>

<p>As you can see, the <code class="language-plaintext highlighter-rouge">READ</code> statement has an option to execute other statements depending on whether there was something to read or we already reached the end of the file.
Here, we use this to set the condition <code class="language-plaintext highlighter-rouge">at-eof</code>, which controls the outer loop of the program.
But more on conditions in the next section.</p>

<h3 id="defining-a-condition-name">Defining a condition name</h3>

<p>Let’s look a bit closer at the definition of <code class="language-plaintext highlighter-rouge">at-eof</code>:</p>

<pre><code class="language-cobol">       01 FILLER PICTURE a.
           88 at-eof VALUE 'Y' FALSE 'N'.
</code></pre>

<p>The first thing that’s new is the <code class="language-plaintext highlighter-rouge">FILLER</code> in place of the name of the top-level data item.
This is just a way of saying that we will never access that data item itself, so it doesn’t need a name.
We still need to give it a picture, which is just one alphabetic character.</p>

<p>Now it gets interesting: The second line defines a subitem with level 88.
Normally, you can only use levels 01 through 49, but there are a few special levels in COBOL.
Level 88 is used for defining so-called “condition names”, which can be used as booleans.
If set to <code class="language-plaintext highlighter-rouge">TRUE</code>, a condition name will assume the value given after <code class="language-plaintext highlighter-rouge">VALUE</code> (or the first possible value in case a range of values is specified there).
If set to <code class="language-plaintext highlighter-rouge">FALSE</code>, it assumes the value given after <code class="language-plaintext highlighter-rouge">FALSE</code>.
So essentially, we have an alphabetic data item that can either be <code class="language-plaintext highlighter-rouge">Y</code> or <code class="language-plaintext highlighter-rouge">N</code>, but we can use <code class="language-plaintext highlighter-rouge">at-eof</code> directly in place of a condition in an <code class="language-plaintext highlighter-rouge">IF</code> or <code class="language-plaintext highlighter-rouge">UNTIL</code>, and we can assign it to <code class="language-plaintext highlighter-rouge">TRUE</code> or <code class="language-plaintext highlighter-rouge">FALSE</code> with the <code class="language-plaintext highlighter-rouge">SET</code> statement.</p>

<h3 id="defining-a-group-item">Defining a group item</h3>

<p>We’ve seen the special level 88 for condition names, but we haven’t seen a true group item yet:</p>

<pre><code class="language-cobol">       01 number-pair.
           02 previous PICTURE 999.
           02 current PICTURE 999.
</code></pre>

<p>Here, <code class="language-plaintext highlighter-rouge">number-pair</code> is a group item and <code class="language-plaintext highlighter-rouge">previous</code> and <code class="language-plaintext highlighter-rouge">current</code> are its subitems.
The level <code class="language-plaintext highlighter-rouge">02</code> for the subitems is arbitrary.
The only rule is that the level has to be larger than the parent item and both subitems have to have the same level.
With this, we have a <code class="language-plaintext highlighter-rouge">number-pair</code> consiting of two numbers.
If we wanted to swap pairs around as a whole, we could access <code class="language-plaintext highlighter-rouge">number-pair</code> directly, but for the purpose of this task we actually don’t need that.
I just created the group item for learning purposes.
You can access the subitems just by their name, but in case you would have subitems with the same name in differently named group items, you could also reference them explicitly as <code class="language-plaintext highlighter-rouge">previous OF number-pair</code>, for example.</p>

<h3 id="calling-a-procedure">Calling a procedure</h3>

<p>In the code for reading a line from our file, you might have already noticed the <code class="language-plaintext highlighter-rouge">PERFORM process-line</code> statement.
This is how you call a procedure in COBOL.
As long as it’s in the same file, there is no need to pass any arguments, as all data is shared between procedures.
This makes it harder to track which procedure is responsible for changing which data items, but it allows you to divide your code into procedures at virtually zero cost:
Just add one line in Area A that introduces a new paragraph and thus gives the procedure a name.</p>

<p>In this program, we make heavy use of this feature to make the code more readable.
For example, consider the loop over the numbers in a line:</p>

<pre><code class="language-cobol">           PERFORM UNTIL line-cursor GREATER line-length
               MOVE line-cursor TO previous-line-cursor
               PERFORM read-next-number
               PERFORM check-if-increasing
               PERFORM check-if-decreasing
               PERFORM check-difference
           END-PERFORM.
</code></pre>

<p>If all the procedures were written out here instead of calling them, the code would become quite convoluted.
There is the downside of losing track where the data item <code class="language-plaintext highlighter-rouge">line-cursor</code> in the loop condition is changed, but to some degree such problems can be alleviated by choosing speaking names for the procedures.</p>

<p>As you might have noticed, procedures are not the only way of breaking down code into smaller elements.
COBOL has functions, which are called similar to how we are used to in modern languages.
For example, <code class="language-plaintext highlighter-rouge">FUNCTION TRIM(input-file-line)</code> calls the function <code class="language-plaintext highlighter-rouge">TRIM</code> and passes <code class="language-plaintext highlighter-rouge">input-file-line</code> as an argument.
You can, of course, define your own functions in COBOL, but this is beyond the scope of this quick start guide.</p>

<h3 id="overview-of-program-logic">Overview of program logic</h3>

<p>With all the syntactic peculiarities out of the way, let’s briefly discuss what the program actually does:</p>

<ul>
  <li>The main loop <code class="language-plaintext highlighter-rouge">PERFORM UNTIL at-eof</code> just ensures that <code class="language-plaintext highlighter-rouge">process-line</code> is called for each line in the input file.</li>
  <li><code class="language-plaintext highlighter-rouge">process-line</code> reads the numbers found in the line one by one into the <code class="language-plaintext highlighter-rouge">number-pair</code> data item to check for each pair whether one of the safety conditions is violated.</li>
  <li><code class="language-plaintext highlighter-rouge">read-next-number</code> advances the <code class="language-plaintext highlighter-rouge">line-cursor</code> by reading the next number in the line and storing it in <code class="language-plaintext highlighter-rouge">current</code> while saving the previous content of <code class="language-plaintext highlighter-rouge">current</code> in <code class="language-plaintext highlighter-rouge">previous</code> to do the comparions later on.
    <ul>
      <li>I admit that it took me a while to find out that <code class="language-plaintext highlighter-rouge">UNSTRING</code> is the statement I needed here.</li>
    </ul>
  </li>
  <li><code class="language-plaintext highlighter-rouge">check-if-increasing</code> and <code class="language-plaintext highlighter-rouge">check-if-decreasing</code> update the data items <code class="language-plaintext highlighter-rouge">all-increasing</code> and <code class="language-plaintext highlighter-rouge">all-decreasing</code> if they find a pair of numbers that violates the condition that the numbers in the line are sorted in ascending or descending order respectively.</li>
  <li><code class="language-plaintext highlighter-rouge">check-difference</code> calculates the absolute difference between the numbers in the pair and sets <code class="language-plaintext highlighter-rouge">difference-is-safe</code> to false if the difference is too small or too large.</li>
  <li>Based on the values of <code class="language-plaintext highlighter-rouge">all-increasing</code>, <code class="language-plaintext highlighter-rouge">all-decreasing</code>, and <code class="language-plaintext highlighter-rouge">difference-is-safe</code>, the counter <code class="language-plaintext highlighter-rouge">safe-count</code> is incremented if the line violated none of the safety conditions.</li>
  <li>At the very end, we use <code class="language-plaintext highlighter-rouge">DISPLAY</code> to print the final value of <code class="language-plaintext highlighter-rouge">safe-count</code> and thus solve the Advent of Code exercise.</li>
</ul>

<h2 id="learnings-and-outlook">Learnings and outlook</h2>

<p>So, what have we learned?
By this point, you are no more of a COBOL expert than I am.
You understood a very basic program that can be solved with a handful of lines in Python or Ruby, and we haven’t even touched on any advanced concepts such as copybooks, tables, or sorting.
However, I hope that now that the very basic questions are out of the way, you are able to dig into the <a href="https://gnucobol.sourceforge.io/HTML/gnucobpg.html">GnuCOBOL Programmer’s Guide</a> or your COBOL manual of choice to find the statements you need for your program.
And I hope that this will be much quicker for you than if you had to start reading that fine but long document from the beginning.</p>

<p>Alternatively (or maybe additionally), I hope you just had a bit of fun with a quirky old language and maybe learned to appreciate where it got its quirks from.
Maybe COBOL doesn’t look so much like a dragon now but more like a bear.
Sure, it can still kill you, and you might not want to get <em>too</em> close to it, but you can see how it has its place in nature.
If you just let it sleep and don’t disturb it too much, it’ll probably be okay.</p>]]></content><author><name>Christopher Schölzel</name></author><category term="COBOL" /><category term="teaching" /><summary type="html"><![CDATA[Ever wondered what all the fuss is about with COBOL? Why it is regarded as such an obscure language that's difficult to learn and responsible for legacy code that virtually nobody can maintain today? Or maybe you are like me and read a book about Grace Hopper and now want to take a peek at how programming looked like during her time? Either way, this is the post for you.]]></summary></entry><entry><title type="html">AI for laypersons: Measuring classification performance</title><link href="https://www.arbitrary-but-fixed.net/ai%20for%20laypersons/2023/02/09/ai-explained-measuring-performance.html" rel="alternate" type="text/html" title="AI for laypersons: Measuring classification performance" /><published>2023-02-09T19:40:00+00:00</published><updated>2023-02-09T19:40:00+00:00</updated><id>https://www.arbitrary-but-fixed.net/ai%20for%20laypersons/2023/02/09/ai-explained-measuring-performance</id><content type="html" xml:base="https://www.arbitrary-but-fixed.net/ai%20for%20laypersons/2023/02/09/ai-explained-measuring-performance.html"><![CDATA[<h2 id="accuracy">Accuracy</h2>

<p>Think about a very simple AI that classifies emails into benign “ham” or unwanted “spam”, very much like the one we built in a <a href="/artificial%20intelligence/machine%20learning/2021/05/30/ai-explained-with-k-nearest-neighbors.html">previous article</a>:
How do we measure how good our AI performs?</p>

<p>Until now, we just looked at a few examples manually and determined whether we felt that the outcome was correct.
This works if our question is just “Can the AI distinguish between a typical ‘Nigerian prince’ spam mail and a genuine mail to a friend from Nigeria?”.
But what if the question becomes “Can the AI detect all kinds of typical spam mails that are in my inbox?” or if we also want to check for the kind of spam our friends and colleagues are getting?
The more general we want our AI to be, the more examples we will need to check.
Wouldn’t it be great if we could just automate this?
After all, all we need for that a list of emails for which we already know whether they are “spam” or “not spam”.
This is the same kind of labeled data that we needed for building our AI in the first place.
To distinguish both datasets, we will call the data we use for testing the <em>test set</em>.</p>

<p>Having this data lets us rephrase the question to “What percentage of these examples does the AI get right?”.
This measure is called <em>accuracy</em>, and it can be calculated simply like this:</p>

<ol>
  <li>Classify all example emails in the test set with the spam detection AI.</li>
  <li>Count how often the AI outputs the right choice of “spam” or “not spam”.</li>
  <li>Divide that number by the total number of examples in the test set.</li>
</ol>

<p>The possible outcomes are between 0 (the AI does not get any of the examples right → 0% accuracy) and 1 (the AI gets every example right → 100% accuracy).
In general, an AI with 80% accuracy is better than an AI with 70% accuracy, for example.</p>

<h2 id="a-cautionary-note-about-data-separation">A cautionary note about data separation</h2>

<p>In the previous example, I just assumed that we do not use the <em>same</em> data for building the AI and for testing it.
Let’s have a brief look at why we do this and what will go wrong if we fail to separate between <em>training</em> and <em>test</em> data:</p>

<p>Remember the instructions for our spam detection AI:</p>

<blockquote>
  <ol>
    <li>For all labeled emails in the database, calculate the number of matching words between that email and the query.</li>
    <li>Find the database entry with the maximum number of matching words.</li>
    <li>Output the label attached to this database entry.</li>
  </ol>
</blockquote>

<p>What happens if we try to classify an email that was already in the database?
Well, the maximum possible number of matching words between one text and another is of course <em>all</em> the words in the text.
So if we ask the AI for the best label for an entry that it has already stored in its database, it will always find the exact copy of that entry and output the label attached to that copy.
In other words: If we use our <em>training</em> data to calculate accuracy, we will end up with 100% accuracy.
Always.
By definition.</p>

<p>The same is true for many other algorithms that you can use to build an AI.
This is why AI researchers and developers always stash away a part of their data as <em>test</em> data that the AI is never allowed to see until the time comes to evaluate its performance.</p>

<h2 id="different-kinds-of-errors">Different kinds of errors</h2>

<p>Let’s say we have two separate spam classification AIs with 70% accuracy.
Can there still be differences between them?</p>

<p>Well, let’s have a look at the different <em>kinds</em> of errors our AI can make by going through all possibilities:</p>

<ul>
  <li><em>True positive</em>: If it classifies a mail as spam that actually was a spam mail, that’s good. No error here.</li>
  <li><em>False positive</em>: If it classifies a mail as spam that actually was not spam, that’s one error to make. The AI was too eager in finding spam.</li>
  <li><em>False negative</em>: If it classifies a mail as not spam that actually was spam, that is also an error, but in the opposite direction. It was too lazy and did not catch all the spam mails.</li>
  <li><em>True negative</em>: If it classifies a mail as not spam that actually was not spam, that’s fine again.</li>
</ul>

<p>So, we end up with two kinds of errors: “eagerness errors” and “laziness errors”.
As you may have noticed, one of the two is a little more dangerous:
Being too lazy just means we still have to delete a few spam mails ourselves.
Being too <em>eager</em> might mean that a mail from our Nigerian friend or maybe from the company in Nigeria where we applied for a job lands in the spam folder, and we might never notice it.</p>

<p>This leads to two new questions about the AI’s performance:</p>

<ol>
  <li>What percentage of the examples that end up in the spam folder actually are spam.</li>
  <li>What percentage of the spam mails that I receive in my inbox will be sent to the spam folder.</li>
</ol>

<p>The first measure is called <em>precision</em> and the second is called <em>recall</em>.
As you might already have guessed, they can be calculated as follows:</p>

<p><strong>Precision</strong></p>

<ol>
  <li>Count the number of emails that get a “spam” label from the AI and actually are spam.</li>
  <li>Count the number of emails that get a “spam” label from the AI, regardless of whether they were spam or not.</li>
  <li>Divide the number from step 1 by the number of step 2.</li>
</ol>

<p><strong>Recall</strong></p>

<ol>
  <li>Count the number of emails that get a “spam” label from the AI and actually are spam.</li>
  <li>Count the number of emails that are spam, regardless of whether the AI classifies them as such.</li>
  <li>Divide the number from step 1 by the number of step 2.</li>
</ol>

<p>To put it in simple terms, higher precision means less eagerness errors and higher recall means less laziness errors.</p>

<p>Precision and recall are, however, not the only measures that help to distinguish between those two kinds of errors.
Imagine a medical setting where you test for a disease like COVID-19:
On the one hand, you want to know how good the test is at detecting sick people as sick.
But on the other hand, you also want to know how good it is at detecting <em>healthy</em> people as healthy.</p>

<p>For the first part, you can just use recall because that is exactly what recall measures: The percentage of all sick people that will test positive.
In this setting, this is called <em>sensitivity</em>, however, because it measures how <em>sensitive</em> the test is to finding the disease.</p>

<p>For the second part, we use a different measure that we call <em>specificity</em>.</p>

<p><strong>Specificity</strong></p>

<ol>
  <li>Count the number of healthy people who are tested negative.</li>
  <li>Count the number of healthy people in the whole test group, regardless of whether they were tested negative.</li>
  <li>Divide the number from step 1 by the number of step 2.</li>
</ol>

<p>Again, you can think of a test that is more <em>specific</em> of having less eagerness errors and a test that is more <em>sensitive</em> of having less laziness errors.
It is just a slightly different definition and terminology that helps to make the right decisions in a medical setting.</p>

<p>It is important to note here that both for precision and recall and for sensitivity and specificity, only knowing <em>one</em> of these two measures will tell you nothing about the actual quality of the AI or COVID-19 test.
This is because you can easily cheat them by just classifying every sample as spam/sick (100% recall, 100% sensitivity) or classifying every as no spam/healthy (100% specificity).
Precision is a little harder to trick, since we would divide by zero if we classify every sample as spam.
However, that just means we need to find that one spam mail that we are really sure about, and we still can get 100% precision.</p>

<h2 id="dealing-with-more-than-two-categories">Dealing with more than two categories</h2>

<p>Until now, we only looked at the spam example, where there was only a yes/no decision to make by our AI.
How about the <a href="/ai%20for%20laypersons/2021/09/23/ai-explained-image-recognition.html">image classification task</a> where we tried to recognize handwritten digits from 0 to 9?
Here, we have ten possible classification outcomes and ten possible <em>true</em> labels.</p>

<p>We can still calculate the overall accuracy, which tells us how close we are to our goal in a single number.
We can also calculate precision and recall for each digit, which will tell us which digit we recognize too often or too seldom.
However, there is a new question that becomes interesting: “Which digit is confused with which?”.
If our AI sometimes confuses a 1 with a 7 or an 8 with a 0, that might be acceptable, but if it starts confusing a 4 and a 1 really often, something weird is going on.</p>

<p>To diagnose these issues, we can just count:</p>

<ol>
  <li>How often does the AI classify a 0 as a 0?</li>
  <li>How often does the AI classify a 0 as a 1?</li>
  <li>How often does the AI classify a 0 as a 2?</li>
  <li>…</li>
  <li>How often does the AI classify a 1 as a 0?</li>
  <li>How often does the AI classify a 1 as a 1?</li>
  <li>…</li>
</ol>

<p>And so on. This gives us 100 numbers for all the ten times ten possible outcomes.
To visualize this, you can build what is called a <em>confusion matrix</em> that puts the label predicted by the AI (<code class="language-plaintext highlighter-rouge">p:</code>) on the columns and the true class label (<code class="language-plaintext highlighter-rouge">t:</code>) on the rows of a table.
The result looks like this.</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right"> </th>
      <th style="text-align: right">p:0</th>
      <th style="text-align: right">p:1</th>
      <th style="text-align: right">p:2</th>
      <th style="text-align: right">p:3</th>
      <th style="text-align: right">p:4</th>
      <th style="text-align: right">p:5</th>
      <th style="text-align: right">p:6</th>
      <th style="text-align: right">p:7</th>
      <th style="text-align: right">p:8</th>
      <th style="text-align: right">p:9</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right">t:0</td>
      <td style="text-align: right">967</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">2</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">5</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">2</td>
      <td style="text-align: right">1</td>
    </tr>
    <tr>
      <td style="text-align: right">t:1</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">1126</td>
      <td style="text-align: right">3</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">3</td>
      <td style="text-align: right">0</td>
    </tr>
    <tr>
      <td style="text-align: right">t:2</td>
      <td style="text-align: right">3</td>
      <td style="text-align: right">2</td>
      <td style="text-align: right">1001</td>
      <td style="text-align: right">8</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">3</td>
      <td style="text-align: right">6</td>
      <td style="text-align: right">8</td>
      <td style="text-align: right">0</td>
    </tr>
    <tr>
      <td style="text-align: right">t:3</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">1002</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">5</td>
      <td style="text-align: right">0</td>
    </tr>
    <tr>
      <td style="text-align: right">t:4</td>
      <td style="text-align: right">3</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">2</td>
      <td style="text-align: right">2</td>
      <td style="text-align: right">955</td>
      <td style="text-align: right">2</td>
      <td style="text-align: right">6</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">3</td>
      <td style="text-align: right">7</td>
    </tr>
    <tr>
      <td style="text-align: right">t:5</td>
      <td style="text-align: right">3</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">37</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">833</td>
      <td style="text-align: right">9</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">6</td>
      <td style="text-align: right">2</td>
    </tr>
    <tr>
      <td style="text-align: right">t:6</td>
      <td style="text-align: right">4</td>
      <td style="text-align: right">3</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">3</td>
      <td style="text-align: right">941</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">4</td>
      <td style="text-align: right">0</td>
    </tr>
    <tr>
      <td style="text-align: right">t:7</td>
      <td style="text-align: right">2</td>
      <td style="text-align: right">9</td>
      <td style="text-align: right">8</td>
      <td style="text-align: right">5</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">988</td>
      <td style="text-align: right">8</td>
      <td style="text-align: right">8</td>
    </tr>
    <tr>
      <td style="text-align: right">t:8</td>
      <td style="text-align: right">3</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">3</td>
      <td style="text-align: right">10</td>
      <td style="text-align: right">3</td>
      <td style="text-align: right">2</td>
      <td style="text-align: right">2</td>
      <td style="text-align: right">3</td>
      <td style="text-align: right">946</td>
      <td style="text-align: right">1</td>
    </tr>
    <tr>
      <td style="text-align: right">t:9</td>
      <td style="text-align: right">3</td>
      <td style="text-align: right">8</td>
      <td style="text-align: right">0</td>
      <td style="text-align: right">10</td>
      <td style="text-align: right">8</td>
      <td style="text-align: right">8</td>
      <td style="text-align: right">1</td>
      <td style="text-align: right">4</td>
      <td style="text-align: right">5</td>
      <td style="text-align: right">962</td>
    </tr>
  </tbody>
</table>

<p>To look for mistakes, we search for the largest numbers outside the diagonal, since the diagonal shows us the samples that were classified <em>correctly</em>.
For this particular classifier, we can see that the most common mistake is to predict a 3 (<code class="language-plaintext highlighter-rouge">p:3</code>) for images that actually showed a 5 (<code class="language-plaintext highlighter-rouge">t:5</code>).
If we roughly calculate the sum over the rows, we can also see that the dataset used for the test contained fewer examples for the digit 5 than for the digit 3.
If the same was true for the training data, this might already indicate why the AI makes exactly this kind of mistake.
It could be that it just hasn’t seen enough examples of the digit 3.
When it is in doubt, it errs on the side of the class label that is more likely to occur in the data.</p>

<p>As you can see, confusion matrices may be confusing (heh) to look at at first, but they can tell you a lot about the performance of an AI that is supposed to classify data into multiple options.</p>

<h2 id="final-remarks">Final remarks</h2>

<p>Let’s sum up what we have learned:</p>

<ul>
  <li>There are automatic measures that can tell you how good an AI is.</li>
  <li>Some of these measures (<em>accuracy</em>) are just one number, others are number pairs (<em>recall/precision</em>, <em>sensitivity/specificity</em>).
  Never trust anyone, who just boasts a high score in <em>one</em> of the numbers belonging to a pair!</li>
  <li>When you want to look at what kind of errors an AI used for classification makes in detail, you can build a <em>confusion matrix</em>.</li>
  <li>It’s important not to test an AI on the data it has already seen when it was trained, since that makes it easy for the AI to cheat.</li>
</ul>

<p>Even if you won’t remember any more details from this post than those bullet points, you are already in a powerful position to judge AI systems.
You know what numbers to look out for, be suspicious if they are not or only partly reported, and can compare different AIs with each other based on those numbers.</p>]]></content><author><name>Christopher Schölzel</name></author><category term="AI for laypersons" /><category term="artificial intelligence" /><category term="machine learning" /><summary type="html"><![CDATA[In this post you will learn how to measure the performance of an AI that classifies text or images into categories.]]></summary></entry><entry><title type="html">Why 31? — Explaining the use of prime numbers in Java hash functions</title><link href="https://www.arbitrary-but-fixed.net/2022/04/28/why-prime-numbers-for-hashing.html" rel="alternate" type="text/html" title="Why 31? — Explaining the use of prime numbers in Java hash functions" /><published>2022-04-28T19:02:00+00:00</published><updated>2022-04-28T19:02:00+00:00</updated><id>https://www.arbitrary-but-fixed.net/2022/04/28/why-prime-numbers-for-hashing</id><content type="html" xml:base="https://www.arbitrary-but-fixed.net/2022/04/28/why-prime-numbers-for-hashing.html"><![CDATA[<h2 id="prerequisites-what-is-a-hash-table">Prerequisites: What is a hash table?</h2>

<p>A hash table allows storing and retrieving values, which are identified by associated keys.
They are the data structure behind the types that we know as map, dict, or hash.
The main idea of a hash table is to calculate an integer value for each key that can be used to determine the index where the associated value should be stored in an array.
This magic integer is called a hash and the array structure in which the values are stored is sometimes called a table—hence the name hash table.
Since the number of elements we want to store is usually smaller than the maximum integer value, the actual array index is determined by using the modulo operation to obtain <code class="language-plaintext highlighter-rouge">index = hash(key) % size</code>.</p>

<p>The main difficulty in finding a good <code class="language-plaintext highlighter-rouge">hash()</code> function is that the range of available hashes is limited by the number of possible (positive) integer values while the theoretical number of possible key objects is often infinite or at least much larger.
Take the example of using strings as keys: A string <code class="language-plaintext highlighter-rouge">s</code> can have aribitrary length, but there are only 2<sup>32</sup> possible different results of <code class="language-plaintext highlighter-rouge">hash(s)</code>.
This implies that <em>some</em> Strings will need to have the exact same hash value and will therefore be put into the same index in the hash table, even though they are completely different.
Even if two different strings have a different hash value, they can still end up with the same index through the modulo operation.
For example, lets say the string <code class="language-plaintext highlighter-rouge">"foo"</code> has a hash of 5 and the string <code class="language-plaintext highlighter-rouge">"bar"</code> has a hash of 9.
If both strings are put into a hash table of size 4, they both are assigned the index 1 since <code class="language-plaintext highlighter-rouge">5 % 4 = 1</code> and <code class="language-plaintext highlighter-rouge">9 % 4 = 1</code>.</p>

<p>There are different possibilities to solve these collisions, the most common of which is called <em>separate chaining</em>.
It works by actually storing a linked list in each of the entries of the array and chaining the values that are assigned to the same index in this list.
Each time you retrieve a value from the hash table you then have to do a sequential search in this “bucket” of values.
Regardless of which mechanism is used to avoid collisions, a high number of collisions in a hash table will mean more searching and therefore a slower access to the individual values.
A good hash function is therefore one that minimizes collisions by spreading out hash values as uniformly as possible across the integer value range, avoiding any clusters of similar keys that end up with the same hash value.</p>

<h2 id="the-question">The question</h2>

<p>To use a data type as a key in a hash table, we need a hash function for that particular data type.
Finding a good hash function is hard, and usually the best bet is to resort to using predefined functions in the standard library.
However, it is never a good idea to blindly trust an algorithm without having a general understanding how it works and why we should use it in place of other alternatives.
At the very least, this knowledge will help to anticipate and debug issues that may occur in our application.</p>

<p>In Java, two methods are therefore of particular interest:</p>

<ul>
  <li>
    <p><code class="language-plaintext highlighter-rouge">String.hashCode()</code> implements the hashing function for the data type <code class="language-plaintext highlighter-rouge">String</code>, which is both often used as key for hash tables and is very flexible since it can have an arbitrary length.
  Ignoring particularities like internal caching of hash codes and the Java 9 feature to compact strings to store them in latin-1 encoding if possible, we arrive at <code class="language-plaintext highlighter-rouge">StringUTF16.hashCode(byte[])</code>:</p>

    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">public</span> <span class="kd">static</span> <span class="kt">int</span> <span class="nf">hashCode</span><span class="o">(</span><span class="kt">byte</span><span class="o">[]</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
      <span class="kt">int</span> <span class="n">h</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
      <span class="kt">int</span> <span class="n">length</span> <span class="o">=</span> <span class="n">value</span><span class="o">.</span><span class="na">length</span> <span class="o">&gt;&gt;</span> <span class="mi">1</span><span class="o">;</span> <span class="c1">// two bytes per character</span>
      <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
          <span class="n">h</span> <span class="o">=</span> <span class="mi">31</span> <span class="o">*</span> <span class="n">h</span> <span class="o">+</span> <span class="n">getChar</span><span class="o">(</span><span class="n">value</span><span class="o">,</span> <span class="n">i</span><span class="o">);</span>
      <span class="o">}</span>
      <span class="k">return</span> <span class="n">h</span><span class="o">;</span>
  <span class="o">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p><code class="language-plaintext highlighter-rouge">Objects.hash(Object ...)</code> is a helper method for Java developers who want to overwrite <code class="language-plaintext highlighter-rouge">Object.hashCode()</code> for their custom data types.
  Instead of creating a hashing function from scratch, developers can simply pass all components of the object that are relevant for equality as argument to the helper method.
  It then delegates its work to <code class="language-plaintext highlighter-rouge">Arrays.hashCode(Object[])</code>:</p>

    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">public</span> <span class="kd">static</span> <span class="kt">int</span> <span class="nf">hashCode</span><span class="o">(</span><span class="nc">Object</span> <span class="n">a</span><span class="o">[])</span> <span class="o">{</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">a</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
          <span class="k">return</span> <span class="mi">0</span><span class="o">;</span>

      <span class="kt">int</span> <span class="n">result</span> <span class="o">=</span> <span class="mi">1</span><span class="o">;</span>

      <span class="k">for</span> <span class="o">(</span><span class="nc">Object</span> <span class="n">element</span> <span class="o">:</span> <span class="n">a</span><span class="o">)</span>
          <span class="n">result</span> <span class="o">=</span> <span class="mi">31</span> <span class="o">*</span> <span class="n">result</span> <span class="o">+</span> <span class="o">(</span><span class="n">element</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">?</span> <span class="mi">0</span> <span class="o">:</span> <span class="n">element</span><span class="o">.</span><span class="na">hashCode</span><span class="o">());</span>

      <span class="k">return</span> <span class="n">result</span><span class="o">;</span>
  <span class="o">}</span>
</code></pre></div>    </div>
  </li>
</ul>

<p>As you can see, both methods essentially use the same idea of multiplying the parts of the objects successively with the prime number 31.
So our questions for today are:
Why the successive multiplication?
Is it important that 31 is a prime number?
And why the magic number 31 in particular?</p>

<!--
Possible tests:
- words (german / english)
- dates (as three integers)
- colors (as ARGB, but with A = 0 and with shortened hex code)
- points (two ints within 1024 x 786)

Bad links:

Why primes
- https://theknowledgeburrow.com/why-are-prime-numbers-better-for-hashing/
- https://stackoverflow.com/questions/1145217/why-should-hash-functions-use-a-prime-number-modulus

Why 31?
- https://stackoverflow.com/questions/1835976/what-is-a-sensible-prime-for-hashcode-calculation
- https://www.baeldung.com/java-hashcode
- https://yeahexp.com/why-does-string-hashcode-in-java-use-31-as-multiplier/

Good links:

Why prime numbers in general
- https://stackoverflow.com/questions/1145217/why-should-hash-functions-use-a-prime-number-modulus
- https://stackoverflow.com/questions/3613102/why-use-a-prime-number-in-hashcode
- https://cs.stackexchange.com/questions/11029/why-is-it-best-to-use-a-prime-number-as-a-mod-in-a-hashing-function
- https://medium.com/swlh/why-should-the-length-of-your-hash-table-be-a-prime-number-760ec65a75d1
- https://archive.org/details/B-001-001-250/page/523/mode/2up?q=prime

Why 31?
- https://stackoverflow.com/questions/299304/why-does-javas-hashcode-in-string-use-31-as-a-multiplier/299748
- https://arxiv.org/pdf/2008.08654.pdf
- https://stackoverflow.com/a/35304979
- https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4045622

Tests
- https://blog.birost.com/a?ID=01800-b514fa4b-3924-499a-81de-7430e470fea7
- https://mp.weixin.qq.com/s?__biz=MzI3ODcxMzQzMw==&mid=2247490895&idx=3&sn=e732a4dfc36e68a4685a737b10eef88f&chksm=eb539879dc24116f350d3c31adba9281efe11e93252d1532a50f406d01030eb23349bd389a44&scene=21#wechat_redirect
-->

<h2 id="non-answers">Non-answers</h2>

<p>I noticed that searching for this question online yields a lot of answers which are either false, incomplete, or assume too much mathematical background knowledge. Examples include…</p>

<ul>
  <li>… suggesting that <a href="https://programming.guide/prime-numbers-in-hash-tables.html">the multiplication is important since it scales up values</a>, which is moot since scaling a number that is distributed over a small range of possible values does not increase that number of possible values;</li>
  <li>… using phrases like <a href="https://stackoverflow.com/q/1145217">“because of the nature of maths”</a> in the explanation;</li>
  <li>… saying that actually <a href="https://stackoverflow.com/questions/1835976/what-is-a-sensible-prime-for-hashcode-calculation">larger primes are better, because they are less likely to produce collisions with small numbers</a>, which ignores the fact that the modulo operation can also introduce collisions even if the hash codes of all keys in the hash table are unique;</li>
  <li>… suggesting that we use a <a href="https://theknowledgeburrow.com/why-are-prime-numbers-better-for-hashing/">prime number as the <em>bucket size</em> of the hash table</a>, which is cumbersome since hash tables would need a list of prime numbers to choose their bucket size from;</li>
  <li>… capitulating before the question, stating that “The value 31 was chosen because it is an odd prime.” (all primes other than two are odd) and “The advantage of using a prime is less clear, but it is traditional.” as is <a href="https://www.google.de/books/edition/Effective_Java/ka2VUBqHiWkC?hl=en&amp;gbpv=1&amp;dq=prime%20traditional&amp;pg=PA48&amp;printsec=frontcover&amp;bsq=prime%20traditional">true for <em>Effective Java 2nd Edition</em></a>, which sadly is one of the <a href="https://stackoverflow.com/a/3613764">most cited</a> <a href="https://stackoverflow.com/a/299748">references</a> <a href="https://www.baeldung.com/java-hashcode">on the topic</a>;</li>
  <li>… explainig the choice of 31 with the fact that <a href="https://www.baeldung.com/java-hashcode">multiplication with 31 can be expressed as a bit shift and a subtraction</a>, which has some truth in it but still does not explain why 31 and not another Mersenne prime like 127 or 8191, which have the same property;</li>
  <li>… just stating that it is good to <a href="https://medium.com/swlh/why-should-the-length-of-your-hash-table-be-a-prime-number-760ec65a75d1">keep the number of prime factors low to avoid collisions</a>;</li>
  <li>… stating that <a href="https://www.geeksforgeeks.org/string-hashing-using-polynomial-rolling-hash-function/">31 is chosen because it is close to the number of letters in the latin alphabet</a> or <a href="https://yeahexp.com/why-does-string-hashcode-in-java-use-31-as-multiplier/">because it is close to the 32 bits of the int data type</a>, which both is utter nonsense;</li>
  <li>or claiming that the <a href="https://computinglife.wordpress.com/2008/11/20/why-do-hash-functions-use-prime-numbers/">product of a prime with any other number has the best chance of being unique</a>, whatever that means.</li>
</ul>

<p>All these non-answers serve to show that this is an area where computer scientists tend to be out of their comfort zone, because it involves number theory, i.e. hardcore math.
I do not think that anyone in the above examples is to blame for not knowing a better answer.
After all, they are programmers and computer scientists and not mathematicians, but I do question the lack of curiosity that leads to such shallow answers being given and accepted.
You cannot learn anything if you do not acknowledge the gaps in your knowledge first.
Put this way, the answer from <em>Effective Java</em> might be the most honest one.</p>

<p>There <em>are</em> also good answers that explain parts of the problem really well:</p>

<ul>
  <li>The StackOverflow user advait shows an example how a <a href="https://stackoverflow.com/a/3613423">prime modulus yields a more uniform distribution than a non-prime modulus</a>.</li>
  <li><a href="https://stackoverflow.com/a/1147232">Steve Jessop</a> and <a href="https://stackoverflow.com/a/3613382">ILMTitan</a> clarify that the mathematical property that we want does not require either the multiplication factor or the number of buckets to be prime, but just that both are coprime, meaning that they have orthogonal prime factorizations, i.e. no common divisor other than 1.</li>
  <li><a href="https://stackoverflow.com/a/300111">JohnZaj</a> gives a reference to an experiment testing which multiplication factors result in the least collisions for a list of 50,000 English words, in which 31 is one of the winners.</li>
  <li><a href="https://stackoverflow.com/a/35304979">David Ongaro</a> and <a href="https://stackoverflow.com/a/44508855">Flow</a> both demonstrate that they have mastered necromany to level 100 by conjuring up an <a href="https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4045622">old JDK bug report</a> in which Joshua Bloch explains his reasoning for choosing the prime 31 in the implementation of <code class="language-plaintext highlighter-rouge">String.hashCode()</code>, which turns out to be part empirical testing and part literature research.</li>
  <li><a href="https://mp.weixin.qq.com/s?__biz=MzI3ODcxMzQzMw==&amp;mid=2247490895&amp;idx=3&amp;sn=e732a4dfc36e68a4685a737b10eef88f&amp;chksm=eb539879dc24116f350d3c31adba9281efe11e93252d1532a50f406d01030eb23349bd389a44&amp;scene=21#wechat_redirect">coolblog</a> published a Chinese blog article in which they perform an experiment using 230,000 english words, also finding that 31 as a multiplier gives good results.</li>
</ul>

<p>However, I did not find any single source that pieces these bits of information together to a fully comprehensive answer.</p>

<h2 id="ask-a-mathematician">Ask a mathematician</h2>

<p>As a teacher at the THM, I wanted to do better for my students and therefore did the only thing that came to my mind after the internet and books failed me: ask a mathematician.
The following explanation is therefore fully owed to Prof. Dr. Bettina Just, who sacrificed her coffee break to change my view of prime numbers in hashing forever by drawing a few numbers on a whiteboard from the top of her head.
Any errors and obscurities in this explanation of course remain entirely my own.</p>

<p>Let’s start by what Prof. Just wrote on the whiteboard on that fateful day:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: right">x</th>
      <th style="text-align: right">x * 5</th>
      <th style="text-align: right">x * 7</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: right">1</td>
      <td style="text-align: right">5</td>
      <td style="text-align: right">7</td>
    </tr>
    <tr>
      <td style="text-align: right">2</td>
      <td style="text-align: right">10</td>
      <td style="text-align: right">14</td>
    </tr>
    <tr>
      <td style="text-align: right">3</td>
      <td style="text-align: right">15</td>
      <td style="text-align: right">21</td>
    </tr>
    <tr>
      <td style="text-align: right">4</td>
      <td style="text-align: right">20</td>
      <td style="text-align: right">28</td>
    </tr>
    <tr>
      <td style="text-align: right">5</td>
      <td style="text-align: right">25</td>
      <td style="text-align: right">35</td>
    </tr>
    <tr>
      <td style="text-align: right">6</td>
      <td style="text-align: right">30</td>
      <td style="text-align: right">42</td>
    </tr>
    <tr>
      <td style="text-align: right">7</td>
      <td style="text-align: right">35</td>
      <td style="text-align: right">49</td>
    </tr>
    <tr>
      <td style="text-align: right">8</td>
      <td style="text-align: right">40</td>
      <td style="text-align: right">56</td>
    </tr>
    <tr>
      <td style="text-align: right">9</td>
      <td style="text-align: right">45</td>
      <td style="text-align: right">63</td>
    </tr>
  </tbody>
</table>

<p>The thing that you should note is what the multiplication with 5 and 7 does to <em>the last digit</em> of x, i.e. the result of x % 10.
For the factor 5, which is <em>not</em> coprime to the modulus 10, the last digit can only be either 0 or 5 meaning that we lost some diversity.
For 7, which <em>is</em> coprime to 10, we get all the values we had before from 1 to 9—just in a different order.</p>

<p>It turns out that this is a general mathematical property that is true whenever the multiplicative factor and the modulus are coprime.
I will not pretend to understand the deeper mathematical reason behind this or attempt to provide a proof here.
Instead I will just leave you with <a href="https://math.stackexchange.com/questions/3619509/coprime-group-element-multiplication">this StackExchange question</a>, and the reference to <a href="https://en.wikipedia.org/wiki/Euler%27s_theorem">Euler’s theorem</a>, which I believe implies this unnamed theorem, and which is interestingly also central for the <a href="https://en.wikipedia.org/wiki/RSA_(cryptosystem)">RSA algorithm</a> used for encrypted communication.
Putting aside the mathematical origins, let’s try to state our key theorem in a more understandable way:</p>

<blockquote>
  <p>Theorem: If a and n are coprime, then multiplying all possible (nonzero) values of x % n = 1, 2, 3, …., n-1 with a and again applying the modulus n to the result yields a permutation of these values.</p>
</blockquote>

<p>Or in even less mathematical terms:</p>

<blockquote>
  <p>Multiplying x with a number a that shares no divisors with n “shuffles” the possible outcomes of x % n.</p>
</blockquote>

<p>Going back to our hash function, this means that multiplying a key value with a number coprime to the number of buckets will never make the distribution of values into buckets less uniform.</p>

<p>Ok, this means that we do no harm with the multiplication, but what <em>good</em> does it actually do?
To explore this question, we must first take a look at the algorithm in which this multiplication takes place.</p>

<h2 id="benefits-of-polynomial-rolling-hashes">Benefits of polynomial rolling hashes</h2>

<p>The algorithm that we see both in <code class="language-plaintext highlighter-rouge">String.hashCode()</code> and <code class="language-plaintext highlighter-rouge">Objects.hash(Object...)</code> is called a <a href="https://en.wikipedia.org/wiki/Rolling_hash#Polynomial_rolling_hash">polynomial rolling hash</a>.
It is used to calculate hashes of composite objects in a way that each component has an equally important influence on the hash value.
To understand how it does this and why this is imporant, we first look at the following prototype:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">hash</span><span class="o">(</span><span class="kt">int</span><span class="o">[]</span> <span class="n">data</span><span class="o">,</span> <span class="kt">int</span> <span class="n">p</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">hash</span> <span class="o">=</span> <span class="mi">1</span>
    <span class="k">for</span><span class="o">(</span><span class="kt">int</span> <span class="nl">x:</span> <span class="n">data</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">hash</span> <span class="o">=</span> <span class="n">hash</span> <span class="o">*</span> <span class="n">p</span> <span class="o">+</span> <span class="n">x</span><span class="o">;</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>At first glance it is odd that the whole <code class="language-plaintext highlighter-rouge">hash</code> is successively multiplied with <code class="language-plaintext highlighter-rouge">p</code>, but this becomes easier to see if we write down the result for <code class="language-plaintext highlighter-rouge">data.length = 3</code>:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">hash</span><span class="o">(</span><span class="n">data</span><span class="o">)</span> <span class="o">=</span> <span class="o">((</span><span class="mi">1</span> <span class="o">*</span> <span class="n">p</span> <span class="o">+</span> <span class="n">data</span><span class="o">[</span><span class="mi">0</span><span class="o">])</span> <span class="o">*</span> <span class="n">p</span> <span class="o">+</span> <span class="n">data</span><span class="o">[</span><span class="mi">1</span><span class="o">])</span> <span class="o">*</span> <span class="n">p</span> <span class="o">+</span> <span class="n">data</span><span class="o">[</span><span class="mi">2</span><span class="o">]</span>
           <span class="o">=</span> <span class="n">data</span><span class="o">[</span><span class="mi">2</span><span class="o">]</span> <span class="o">+</span> <span class="n">p</span> <span class="o">*</span> <span class="n">data</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="o">+</span> <span class="n">p</span> <span class="o">*</span> <span class="n">p</span> <span class="n">data</span><span class="o">[</span><span class="mi">0</span><span class="o">]</span> <span class="o">+</span> <span class="n">p</span> <span class="o">*</span> <span class="n">p</span> <span class="o">*</span> <span class="n">p</span>
</code></pre></div></div>

<p>So what <code class="language-plaintext highlighter-rouge">hash(int[], int)</code> is doing is actually just building a polynomial in which the elements of <code class="language-plaintext highlighter-rouge">data</code> are the coefficients and <code class="language-plaintext highlighter-rouge">p</code> is the base.
We could write this in a more straightforward way, but exponentiation is more expensive than multiplication and since hash tables are often used for performance reasons and we have to build a hash each time we retrieve a value from the table, we want to save a little time here.</p>

<p>So why are polynomials a good idea?
To understand this, let us first look at the most naive solution we could think of.
Essentially, we want to reduce an array of numbers to a single number.
So why not just take the sum of the elements?</p>

<p>The main issue here is that addition is a commutative operation. a + b is the same as b + a, which means that the order of the elements does not matter for the end result.
If we think about hashing strings, all anagrams will collide with each other, since, for example, the string “one” will have the same hash value as the string “neo”.
Additionally, if the range of possible values for the individual elements is smaller than the range of integers, a sum does not grow quickly enough to yield a good distribution of hash values across the integer range.
This is again the case for strings.
For ASCII strings of length 4, we have 128 possible characters with a maximum sum of 4 · 127 = 508, but we have 128<sup>4</sup> = 268,435,456 possible four-character strings, which means an expected collision rate of at least 1 - 508 / 268,435,456 = 99.9998%.</p>

<p>This is where the polynomial rolling hash comes in.
By using polynomials, we both avoid the commutativity of addition, because each summand is multiplied with a different value before summation, and even when the data only consists of very small values, the exponentiation quickly spreads these values over the integer range.</p>

<p>Now for the choice of <code class="language-plaintext highlighter-rouge">p</code>, remember our theorem:</p>

<blockquote>
  <p>Multiplying x with a number a that shares no divisors with n “shuffles” the possible outcomes of x % n.</p>
</blockquote>

<p>Here, <code class="language-plaintext highlighter-rouge">p</code> is the factor a and the amount of buckets in the hash table is n.
To choose a good value for <code class="language-plaintext highlighter-rouge">p</code>, we have to know how the scaling of the hash table is implemented.
If we don’t, the safest bet is a prime number, since it will not share any divisors other than itself with any other number.</p>

<p>Since we are in Java, we can look up the implementation of <code class="language-plaintext highlighter-rouge">HashMap&lt;K,V&gt;</code>, which always uses powers of two as the table length.
The reason for this is that instead of using the modulo operator we can just calculate <code class="language-plaintext highlighter-rouge">(n - 1) &amp; h</code> when <code class="language-plaintext highlighter-rouge">h</code> is the hash value.
Since n = 2<sup>e</sup>, the binary representation of <code class="language-plaintext highlighter-rouge">n - 1</code> are zeros followed by a series of <code class="language-plaintext highlighter-rouge">e</code> ones and the bitwise and operator gives us just the last <code class="language-plaintext highlighter-rouge">e</code> bits of the hash.</p>

<p>This also means that the only restriction we have to follow for <code class="language-plaintext highlighter-rouge">p</code> is that it cannot be even.
All odd numbers will be coprime to 2<sup>e</sup> for all e.</p>

<h2 id="remaining-sources-of-collisions">Remaining sources of collisions</h2>

<p>By now we have ensured that neither the multiplicative factor nor the table size may introduce any particular pattern of collisions.
Assuming a uniform distribution of the content of the <code class="language-plaintext highlighter-rouge">data</code> array across the possible number of options (be it 128 ASCII chars, 2<sup>16</sup> UTF-16 chars, or 2<sup>32</sup> integers), this would give us a perfect hash function with minimal collisions.</p>

<p>The only remaining point of concern is our data itself.
Remember that multiplication with a number coprime to the number of buckets does not make the spread across buckets <em>worse</em>, but it also cannot do anything if the spread was already bad <em>before</em> the multiplication.
For example, if we would start with <code class="language-plaintext highlighter-rouge">hash = 0</code> instead of <code class="language-plaintext highlighter-rouge">hash = 1</code>, we would be able to easily produce collisions if all elements of <code class="language-plaintext highlighter-rouge">data</code> were divisible by two for all or at least many of the keys that we want to store.
By starting with <code class="language-plaintext highlighter-rouge">hash = 1</code> we add the summand p<sup>data.length</sup> to the calculation, which cannot be divisible by two.</p>

<p>At this point we have ruled out the most easily <em>predictable</em> patterns of our data that may cause collisions, but in real data there might be all kinds of <em>unpredictable</em> patterns.
Unless we run tests with a particular <code class="language-plaintext highlighter-rouge">p</code> and a particular data set that is representative of real world applications, we cannot be sure that the hash function really works well for our use case.</p>

<p>Just to give you an example of what kind of patterns we might be talking about, the letter ‘e’ is far more likely to occur in an english text than the letter ‘k’.
For texts in other languages the probability distribution might be similar or entirely different.
And what about other kinds of data such as file system paths, points in a 2D coordinate system, or dates?
All of these are not uniformely distributed by any means, so some choices for <code class="language-plaintext highlighter-rouge">p</code> might work well and others quite poorly.</p>

<h2 id="history-of-31">History of 31</h2>

<p>This brings us to the magic number 31.
As already hinted at, you can read up the whole history of how this number was proposed by Joschua Bloch in an <a href="https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4045622">old JDK bug report</a>.
In short, there are two main arguments: One is the previous use of 31 by other programmers, and the other is a test performed by Bloch himself.</p>

<p>The references for the choice of the number 31 go back to Kerninghan and Ritchies “The C programming language”, but when Bloch called them and asked them about its origin, neither of the authors could remember it.</p>

<p>Since Bloch was aware that 31 was by far not the only candidate, he performed tests evaluating collision probabilities with the following kind of data:</p>

<blockquote>
  <ul>
    <li>All of the words and phrases with entries in Merriam-Webster’s 2nd Int’l Unabridged Dictionary (311,141 strings, avg length 10 chars).</li>
    <li>All of the strings in /bin/<em>, /usr/bin/</em>, /usr/lib/<em>, /usr/ucb/</em> and /usr/openwin/bin/*  (66,304 strings, avg length 21 characters).</li>
    <li>A list of URLs gathered by a web-crawler that ran for several hours last night (28,372 strings, avg length 49 characters).</li>
  </ul>
</blockquote>

<p>The composite number 33 performed a little better than the prime 31, but since Bloch was unsure about the mathematical foundations, he was more comfortable choosing the candidate that was indeed a prime number.</p>

<p>Let’s put aside for a minute that this testing procedure leads to a preferential treatment of English speaking countries over others and Linux users over Windows users, and just acknowledge that choosing any single number that would work well for all application scenarios across all existing and future Java applications is an extremely hard problem.
It may very well be that choosing a number that “works well enough” for some popular applications might be the best that Bloch could do in this situation.</p>

<p>However, while this is true for <code class="language-plaintext highlighter-rouge">String.hashCode()</code> it <em>does</em> seem that the 31 was copied over to <code class="language-plaintext highlighter-rouge">Object.hash()</code> without much thought or testing.
After all, we should assume different patterns to occur in arbitrary composite objects than in strings, which are heavily shaped by natural and formal languages.</p>

<p>Performing a bit of necromancy myself, I could indeed find other old JDK bug reports, which report bad performance of <code class="language-plaintext highlighter-rouge">Arrays.hashCode(long[])</code> for <a href="https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8134141">small arrays</a> and for <a href="https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6530203">arrays only containing the values 0 and -1</a>.
This was acknowledged by the Java developers, but no change was implemented since the internal algorithm is already fully specified in the documentation of the Java API and thus some applications might rely on this specific implementation.</p>

<h2 id="bonus-evaluating-different-primes">Bonus: Evaluating different primes</h2>

<p>Other people have already tried to come up with “better” primes than 31.
In general I find the quest for the best prime number for a general hash function moot, because—as explained earlier—different application scenarios will have different patterns leading to a different performance of individual prime numbers.</p>

<p>However, there are a few questions left that still tickled me:</p>

<ul>
  <li>Do larger prime numbers really perform better than smaller ones?</li>
  <li>Do Mersenne primes perform better in speed and/or collision avoidance than other primes?</li>
  <li>Will the prime 31 perform as well for strings in other languages as it does for English?</li>
</ul>

<p>So in a small myth busting experiment, I put together a few very small test cases which I think are fairly realistic keys for a hash table:</p>

<ul>
  <li><a href="https://en.wiktionary.org/wiki/Wiktionary:Frequency_lists/PG/2006/04/1-10000">The 1000 most common English words</a></li>
  <li><a href="https://en.wiktionary.org/wiki/Wiktionary:Frequency_lists/A_Frequency_Dictionary_of_German">The 1000 most common German words</a></li>
  <li><a href="https://en.wiktionary.org/wiki/Appendix:Frequency_dictionary_of_the_modern_Russian_language_(the_Russian_National_Corpus)/1">The 1000 most common Russian words</a></li>
  <li>100000 random pixels in a 1024x768 pixel image consisting of two integers for the x- and y-coordinate</li>
  <li>the same pixels, but converted to strings of the form <code class="language-plaintext highlighter-rouge">"(x, y)"</code></li>
  <li>10000 random dates between 1983 and 2022 consisting of three integers for year, month, and day</li>
  <li>the same dates, but converted to strings in ISO 8601 format (<code class="language-plaintext highlighter-rouge">"YYYY-MM-DD"</code>).</li>
</ul>

<p>For the pixel example I also assessed the performance of a custom hash function that just adds the coordinates after shifting the y coordinate left by 10 bits, effectively just enumerating the pixels line by line.</p>

<p>To make the comparison as realistic as possible, I calculated the number of buckets as the lowest power of two such that the stored values fill only 75% of the available space at maximum.
In Java this is assured by the “load factor” in the HashMap implementation.
I also did not use the polynomial rolling hash directly, but applied a second hash function that XORs the hash with itself shifted by 16 bits to the right as in the protected method <code class="language-plaintext highlighter-rouge">HashMap.hash(Object)</code>.
Unlike Bloch, I did not calculate the average number of items per bucket in the hash table but the collision percentage, because this allows comparison across different tests with different table sizes.</p>

<p>I also performed a very crude performance test by running the whole simulation once to ensure that the JVM is warmed up and then taking time measures with <code class="language-plaintext highlighter-rouge">System.nanoTime()</code>, since I did not want to set up a <a href="https://github.com/openjdk/jmh">JMH</a> project for such a small test.</p>

<p>Let’s have a first look at the prime values below 50 to see how the prime 31 performs in comparison to its direct neighbors:</p>

<div class="bokeh-container"><script src="/assets/img/prime31_1_50.js" id="prime31_1_50"></script></div>

<p>The dotted vertical lines show prime numbers and the solid vertical lines indicate the Mersenne primes 3, 7, and 31.
The colored lines represent the different test scenarios and the faded area in the background is the result of the crude performance test.</p>

<p>We can see a few things here that were expected and also a few that were surprising.
For one, the prime 31 seems to be neither a particularly bad nor a particularly good candidate for our test cases.
If we had to determine a “winner” in this range, it would probably be the prime 29 due to its exceptional performance for the date test.
However, if you care more about Russian than about English or German, you might also be interested in the prime 41.</p>

<p>Both the raw versions of the date and points tests show symptoms of having a too narrow value range when the multiplicative factor is small.
Most surprisingly, converting dates and points to strings yields much better hashing performance in both cases.
Even the custom idea of just enumerating pixels performs well but still worse than the string variant.
Memo to myself: Stop scolding students for implementing <code class="language-plaintext highlighter-rouge">hashCode()</code> as <code class="language-plaintext highlighter-rouge">return this.toString().hashCode()</code>!</p>

<p>Another result that is surprising at first glance is that we actually don’t see more collisions for even factors as we would have predicted.
This can be explained by the second hash function applied by <code class="language-plaintext highlighter-rouge">HashMap</code>, which unfortunately makes our results less predictable.
I have run the test without it and the result is indeed much more jagged with worse performance for even factors.</p>

<p>Finally, there is no noticeable performance gain in terms of computation speed when using Mersenne primes.
This can be explained by the fact that the Java compiler does not automatically perform the required optimization step to transform the multiplication into a shift and subtract operation.
Looking a little closer into this, there is a <a href="https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6506618">JDK bug report about changing the code to improve the performance of <code class="language-plaintext highlighter-rouge">String.hashCode()</code></a>, but it turns out that the performance gain on modern machines is a whooping 0.01%.
Myth busted!</p>

<p>The only myth that remains is that larger primes would perform significantly better.
For this, let’s redo this calculation around the other Mersenne primes within the integer range: 127, 8191, 131071, 524287, and 2147483647.</p>

<div class="bokeh-container"><script src="/assets/img/prime31_102_152.js" id="prime31_102_152"></script></div>

<div class="bokeh-container"><script src="/assets/img/prime31_8166_8216.js" id="prime31_8166_8216"></script></div>

<div class="bokeh-container"><script src="/assets/img/prime31_131046_131096.js" id="prime31_131046_131096"></script></div>

<div class="bokeh-container"><script src="/assets/img/prime31_524262_524312.js" id="prime31_524262_524312"></script></div>

<div class="bokeh-container"><script src="/assets/img/prime31_2147483597_2147483647.js" id="prime31_2147483597_2147483647"></script></div>

<p>And for good measure one that is quite large but not near any power of two, which I chose by calling <code class="language-plaintext highlighter-rouge">new Random(31).nextInt(1 &lt;&lt; 15, 1 &lt;&lt; 25)</code>. (The 31 is only in there for funsies, because I needed some number to fix the seed of the random number generator.)</p>

<div class="bokeh-container"><script src="/assets/img/prime31_28739431_28739481.js" id="prime31_28739431_28739481"></script></div>

<p>Apparently, Mersenne primes are not only <em>not better</em>, but actually worse than other primes.
It seems like there is some kind of “gravity well” around powers of two, which drags the adjacent Mersenne primes into the performance abyss along with them.
I suspect that this has something to do with the fact that all values in this area will have a quite large number of zeros in their binary representation.
The effect we see is, however, extremely dependent on the test case.
For the raw date and point tests it is much more pronounced than for words, and for the string version of points and dates it does not occur at all.</p>

<p>In this regard, Mersenne primes might have been a poor choice to look at the effect of the size of the multiplicative factor on the collision performance, but the last test of a pseudorandomly generated number shows that there are fluctuations everywhere and that the only effect of size that we can reliably determine is that there are some use cases where extremely small and extremely large multiplicative factors perform equally badly.
Therefore: “Large numbers are better” myth (mostly) busted!</p>

<p>That being said, I have no good explanation for many of the observed phenomena in this test.
Maybe I chose weird test cases, maybe I have some bugs in my simulation?
If you want, you can <a href="https://github.com/CSchoel/arbitrary-but-fixed/tree/main/assets/code/prime31">have a look at the source code</a>.
I would be grateful to hear if you found any errors, but currently I think the most likely explanation is the one I gave earlier:
Finding a multiplicative factor that performs well for all test cases is extremely hard and predicting which patterns will occur in the keys of hash tables is next to impossible.
After all, I bet you could think of at least a dozen other test cases that would be interesting to investigate right now.</p>

<p>What do we take away from this?
I would suggest four things:</p>

<ul>
  <li>It’s always more complicated than you think at first glance.</li>
  <li>In Java, it seems to be OK if not outright beneficial to implement hash functions based on String representations of objects.</li>
  <li>Sometimes, rolling a die is the best way to avoid running into patterns that degenerate algorithmic performance.</li>
  <li>If you really want to know if your hash function performs well for your application scenario, test it with data that is representative of this scenario.</li>
</ul>]]></content><author><name>Christopher Schölzel</name></author><category term="polynomial rolling hash" /><category term="prime number" /><category term="hash function" /><category term="hash table" /><summary type="html"><![CDATA[Ever looked at default implementations of hash functions and wondered: Why prime numbers? Why 31 specifically? And why do we multiply multiple times with the same prime number? If so, this post is for you.]]></summary></entry><entry><title type="html">AI for laypersons: Stock price prediction with nearest neighbors</title><link href="https://www.arbitrary-but-fixed.net/ai%20for%20laypersons/2022/03/31/ai-explained-financial-prediction.html" rel="alternate" type="text/html" title="AI for laypersons: Stock price prediction with nearest neighbors" /><published>2022-03-31T19:48:00+00:00</published><updated>2022-03-31T19:48:00+00:00</updated><id>https://www.arbitrary-but-fixed.net/ai%20for%20laypersons/2022/03/31/ai-explained-financial-prediction</id><content type="html" xml:base="https://www.arbitrary-but-fixed.net/ai%20for%20laypersons/2022/03/31/ai-explained-financial-prediction.html"><![CDATA[<!--
Stock market prediction (prediction)
  - prediction problem
  - "distance" of two lists of integers (~> euclidean distance)
  - heterogeneous data (take date into account for summer/winter changes)
-->

<h2 id="recap">Recap</h2>

<p>In this series we have previously classified emails in “spam” and “ham” and recognized hand-drawn digits from images.
In both cases, we used the nearest neighbor algorithm, which simply finds the “most similar” example from a large database and copies the label (“ham”/”spam” or the digits from 0 to 9) attached to that element as classification output.</p>

<p>One interesting observation was that the only difference between the spam detection AI and the image recognition AI was the data that we used.
For emails, we counted the number of matching words to measure similarity and for images we counted the number of matching pixels.
In this post, I want to give another example of the broad applicability of this approach by looking at stock market prediction.</p>

<h2 id="a-new-task-stock-market-prediction">A new task: Stock market prediction</h2>

<p>Today I will teach you how to get rich quick—just kidding, <em>please do not use the techniques presented here for actual stock trading</em>.
That being said, it <em>is</em> interesting to think about the problem how one could predict the price of a stock on the following day or in the following week given the price history.
After all, very real money is being made with artificial intelligence in this area.</p>

<p>Our data might look as follows:</p>

<table>
  <thead>
    <tr>
      <th>Date</th>
      <th>Value [$]</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>2022-03-01</td>
      <td>50</td>
    </tr>
    <tr>
      <td>2022-03-07</td>
      <td>54</td>
    </tr>
    <tr>
      <td>2022-03-14</td>
      <td>60</td>
    </tr>
    <tr>
      <td>2022-03-21</td>
      <td>59</td>
    </tr>
    <tr>
      <td>2022-03-28</td>
      <td>57</td>
    </tr>
    <tr>
      <td>2022-04-05</td>
      <td>58</td>
    </tr>
  </tbody>
</table>

<p>In this case we see the price of a single share of a company’s stock<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> change from week to week.
I have chosen weeks instead of days here because then we do not have to consider that markets are closed on the weekend.
In principle, however, we could also formulate this problem for daily or monthly prices.</p>

<p>To earn money with stock trading, you have to buy shares of a stock when they have a low value and then sell them when their value has risen.
The core question we have to ask is therefore: “What will the price of the stock be next week?”
This prediction of a future trend is the inherent risk in stock trading and the attack point that we want to tackle with our AI.</p>

<h2 id="data-series-as-database-entries">Data series as database entries</h2>

<p>The first difference that you might notice between this task and the previous ones is that our dataset is no longer composed of individual items that are independent of each other.
Instead, it is now a series of interconnected data points.
This poses the question what kind of “neighbors” we might consider in our nearest neighbor approach.</p>

<p>In principle, we want to predict an unknown, future share value based on a history of known, past share values.
The easiest way to transform the data series given above into a list of individual database entries that we can use for the nearest neighbor approach is therefore to only consider, for example, the history of the last three weeks.
We can then split the data series into subseries of four successive weeks, where the values at the first three weeks are the database entry and the value at the fourth week is the “label” that we want to predict:</p>

<table>
  <thead>
    <tr>
      <th>Database entry</th>
      <th>Label</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>50, 54, 60</td>
      <td>59</td>
    </tr>
    <tr>
      <td>54, 60, 59</td>
      <td>57</td>
    </tr>
    <tr>
      <td>60, 59, 57</td>
      <td>58</td>
    </tr>
  </tbody>
</table>

<p>In this table I have used overlapping individual histories (week 1–3, week 2–4, and week 3-5) to obtain as many database entries as possible.</p>

<h2 id="neighbors-of-data-series">Neighbors of data series</h2>

<p>Now that we know how our database entries look, the question remains how to determine the similarity between two of these entries in order to find our beloved nearest neighbors.
In our previous examples, we always just counted the number of matching items (words, pixels) between the two database entries.
Since our database entries now consist of arbitrary numbers, that does not work anymore.
Instead, we have to calculate how close these numbers are to each other.
The easiest way to do so is to take the absolute difference between them, that is, subtract the one from the other and if the result is negative, just flip the sign to obtain a positive number.
For example, the individual differences between the numbers in the first two database entries would look like this:</p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th>Entry 1</th>
      <th>Entry 2</th>
      <th>Abs. diff.</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>last week</td>
      <td>50</td>
      <td>54</td>
      <td>|50 - 54| = |-4| = 4</td>
    </tr>
    <tr>
      <td>2nd last week</td>
      <td>54</td>
      <td>60</td>
      <td>|54 - 60| = |-6| = 6</td>
    </tr>
    <tr>
      <td>3rd last week</td>
      <td>60</td>
      <td>59</td>
      <td>|60 - 59| = |1| = 1</td>
    </tr>
  </tbody>
</table>

<p>The smaller these differences are, the higher the similarity between the two entries.
However, each difference still consists of three numbers.
Is a difference of (4, 6, 1) larger or smaller than a difference of (3, 5, 2)?
To answer this problem we need to condense the difference down to a single number, for example by simply taking the sum of the individual differences.
For our example, we can then say that 4 + 6 + 1 = 11 is indeed larger than 3 + 5 + 2 = 10.</p>

<p>In a real application, one would use more complex formulas such as the <a href="https://en.wikipedia.org/wiki/Euclidean_distance#Higher_dimensions">Euclidean distance</a>, but for our purposes this simple sum of differences is perfectly fine.</p>

<h2 id="predicting-numbers-instead-of-classes">Predicting numbers instead of classes</h2>

<p>When discussing our database format, I have put the term “label” in quotes, since we actually do not want to predict distinct labels (such as “ham”, “spam”, “digit 1”, or “digit 2”) but an arbitrary number.
After a week the share value could drop to zero, or it could double or quadruple.
While some answers are of course more probable than others, the theoretical number of possibilities is endless.</p>

<p>How can we deal with this infinite number of possible labels?
The answer is surprisingly simple: We do the same as before.
Once we have found the nearest neighbor to a given series of share values, we simply copy the share value at the fourth week as our prediction.</p>

<p>This has the disadvantage that we will never produce any numbers that are not already in the database, but this can be compensated by having a really large database with a good coverage of different cases, which we would need anyway to make good predictions.
Alternatively, we could also store trends (by how much did the value rise/fall between weeks) instead of absolute values, which would also allow us to predict an infinitely rising or falling trend, but for now we just want a simple solution that works.</p>

<h2 id="putting-together-the-algorithm">Putting together the algorithm</h2>

<p>With this, we have all the information that we need to build an AI using the nearest neighbor approach to predict a share value based on a three-week history:</p>

<h3 id="algorithm-simple-share-value-predictor">Algorithm: Simple share value predictor</h3>

<p>Inputs:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Database</code>: list of three-week share value histories with separate fourth-week value</li>
  <li><code class="language-plaintext highlighter-rouge">Query</code>: a three-week share value history</li>
</ul>

<p>Output:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Prediction</code>: the most likely share value in the next week</li>
</ul>

<p>Steps:</p>

<ol>
  <li>For all histories in the database, calculate the sum of absolute differences between that history and the query.</li>
  <li>Find the database entry with the smallest sum of differences.</li>
  <li>Output the fourth-week value attached to this database entry.</li>
</ol>

<p>Again, we have successfully converted the human task “Find the share price that this stock will have next week.” into a set of instructions that can be automatically performed by a computer.
While you will definitely not get rich quick with this particular algorithm, real trading algorithms do follow the same principle to get as much information out of existing historical data in order to predict share values in the future—and they are as susceptible to unforeseen patterns that do not appear in their database.</p>

<p>If you imagine a much more sophisticated algorithm than this one running on years and years of data from thousands of stocks, maybe also analyzing the behavior of other traders and then making sub-second decisions, it is easy to see why high-frequency trading is so effective at making the rich even richer and why people who do not have access to these tools tend to lose out in comparison.</p>

<p>To paint a less bleak picture, we have unlocked a whole new class of problems that we can now tackle with our AI approach, namely all problems that require us to predict a numeric value that depends on a list of other numeric values.
This general task is called <a href="https://en.wikipedia.org/wiki/Regression_analysis">regression analysis</a>, and it can also, for example, be used to predict crop yield in agriculture or the effect of medication on blood pressure based on patient-specific data.</p>

<h2 id="beyond-one-week">Beyond one week</h2>

<p>You might have already asked yourself what we would do if we wanted to predict the share price for a duration of more than one week into the future.
For this, we basically have two possible approaches:</p>

<ol>
  <li>We can predict more than one week by storing more values in the “label” part of our database.
  Instead of just the fourth-week value we could also store the value for the fifth, sixt week, and so on.
  This approach increases the problem that we only can predict trends that we have already seen as we now have even more possible outputs and need even more data in the database.</li>
  <li>We can simply run our algorithm multiple times.
  If we predicted the value 59 for the history [50, 54, 60], we can then see what our AI yields for the new query [54, 60, 59].
  This does not require to change the algorithm or to acquire more data, but it also means that our errors will accumulate, and our predictions will get increasingly unreliable the longer the desired output.</li>
</ol>

<h2 id="next-steps">Next steps</h2>

<p>We have now seen that the nearest neighbor algorithm is very flexible and in principle behaves similar to state-of-the-art machine learning approaches across a large variety of tasks.
To dive deeper into the core principles of artificial intelligence, we just need to take one additional step moving from one to an arbitrary number k of neighbors to obtain the k-nearest neighbor algorithm.
This will be established in the next post in this series.</p>

<hr />

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>If you are as unfamiliar with stock market terms as me before I wrote this post: A company’s stock encompasses an arbitrary number of shares (some companies could have a total of 1000 shares, others a total of 50000), which represent a share (hence the name) of the company itself. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Christopher Schölzel</name></author><category term="AI for laypersons" /><category term="artificial intelligence" /><category term="machine learning" /><category term="image recognition" /><summary type="html"><![CDATA[In the first post of the 'AI for laypersons' series, I introduced a very simple AI based on the nearest neighbor algorithm. In this post, I want to show that the same idea can also be applied to prediction tasks such as the prediction of stock prices.]]></summary></entry><entry><title type="html">How to run headless unit tests for GUIs on GitHub actions</title><link href="https://www.arbitrary-but-fixed.net/2022/01/21/headless-gui-github-actions.html" rel="alternate" type="text/html" title="How to run headless unit tests for GUIs on GitHub actions" /><published>2022-01-21T18:08:00+00:00</published><updated>2022-01-21T18:08:00+00:00</updated><id>https://www.arbitrary-but-fixed.net/2022/01/21/headless-gui-github-actions</id><content type="html" xml:base="https://www.arbitrary-but-fixed.net/2022/01/21/headless-gui-github-actions.html"><![CDATA[<h2 id="the-problem-headless-gui-testing">The problem: Headless GUI testing</h2>

<p>Building unit tests for GUIs is not entirely straightforward.
Usually, you want to separate your application logic from your GUI as much as possible precisely because this makes the code easier to test and debug.
However, what if the graphical display <em>is</em> the main part of the application?
This is true for <a href="https://github.com/CSchoel/fcanvas/">FCanvas</a>, a project that I started in my first years of teaching at the THM in 2013 and now recently uploaded to GitHub.
FCanvas is a library that allows programming novices to draw on a canvas and create simple animations, so the core functionality is closely tied to what can be seen on the canvas.</p>

<p>While brushing the dust off the project I also wanted to add a few unit tests to ensure that none of my refactoring attempts would introduce any bugs or visual glitches.
And since I love a good CI/CD pipeline, I also wanted to add respective GitHub actions workflows.
I already had a hunch that those two things in combination might be a problem, and sure enough my unit test, which ran fine on my local machine, threw the following exception in GitHub actions:</p>

<pre><code class="language-verbatim">java.lang.ExceptionInInitializerError
    at de.thm.mni.oop.fcanvas.FCanvasTest.testRectangle(FCanvasTest.java:20)

    Caused by:
    java.awt.HeadlessException:
    No X11 DISPLAY variable was set,
    but this program performed an operation which requires it.
        at java.desktop/java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:166)
        at java.desktop/java.awt.Window.&lt;init&gt;(Window.java:553)
        at java.desktop/java.awt.Frame.&lt;init&gt;(Frame.java:428)
        at java.desktop/java.awt.Frame.&lt;init&gt;(Frame.java:393)
        at java.desktop/javax.swing.JFrame.&lt;init&gt;(JFrame.java:180)
        at de.thm.mni.oop.fcanvas.FCanvasGUI.&lt;init&gt;(FCanvasGUI.java:18)
        at de.thm.mni.oop.fcanvas.FCanvas.&lt;clinit&gt;(FCanvas.java:85)
        ... 1 more
</code></pre>

<p>The culprit was a call to <code class="language-plaintext highlighter-rouge">new FCanvasGUI();</code>, because <code class="language-plaintext highlighter-rouge">FCanvasGUI</code> inherits from <code class="language-plaintext highlighter-rouge">JFrame</code>, <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.desktop/javax/swing/JFrame.html#%3Cinit%3E()">whose constructor</a> can throw the <code class="language-plaintext highlighter-rouge">java.awt.HeadlessException</code>, which we see here.
The term “headless” means that no display device is attached to the machine that runs the Java code, which in consequence means that there is no way to actually <em>display</em> the <code class="language-plaintext highlighter-rouge">JFrame</code> we have just created.</p>

<h2 id="solution-1-avoid-code-that-can-throw-javaawtheadlessexception">Solution 1: Avoid code that can throw java.awt.HeadlessException</h2>

<p>Searching for the term “headless” in the <a href="https://www.oracle.com/technical-resources/articles/javase/headless.html">Oracle documentation</a> reveals that this affects all graphical components except for <code class="language-plaintext highlighter-rouge">Canvas</code>, <code class="language-plaintext highlighter-rouge">Panel</code>, and <code class="language-plaintext highlighter-rouge">Image</code>.
My simple test for drawing a rectangle on a <code class="language-plaintext highlighter-rouge">JPanel</code> could therefore also be performed in headless mode if I skipped the creation of the <code class="language-plaintext highlighter-rouge">JFrame</code> in which the panel is displayed.
However, I also plan to add fancier tests down the road, which will involve simulating user input through <a href="https://docs.oracle.com/en/java/javase/17/docs/api/java.desktop/java/awt/Robot.html"><code class="language-plaintext highlighter-rouge">java.awt.Robot</code></a>, so this was not an option for me.</p>

<h2 id="solution-2-create-a-dummy-display">Solution 2: Create a dummy display</h2>

<p>Instead, I searched for a way to fix the issue on the side of the operating system.
Surely the Linux community has found some way to run X11-applications on a headless server, right?
Right!
There is the <a href="https://linux.die.net/man/1/xvfb">X virtual framebuffer (Xvfb)</a>, which holds an image buffer in memory that behaves like an X server display but does not require an actual display device.</p>

<p>Xvfb comes with a simple tool <code class="language-plaintext highlighter-rouge">xvfb-run</code>, which runs a single command with such an Xvfb server and closes the server right after the command exits.
The solution for my GitHub workflow was therefore simply to install the <code class="language-plaintext highlighter-rouge">xvfb</code> package using <code class="language-plaintext highlighter-rouge">apt install xvfb</code> and then exchanging <code class="language-plaintext highlighter-rouge">./gradlew build</code> with <code class="language-plaintext highlighter-rouge">xvfb-run ./gradlew build</code>.
Works like a charm. 🎉</p>

<h2 id="notes">Notes</h2>

<p>While searching for these solutions I came across a few misleading tips, which I want to discuss here in order to save you the trouble should you run into the same issues.</p>

<h3 id="running-a-gradle-task-through-xvfb-run-vs-running-xvfb-run-from-within-gradle">Running a gradle task through <code class="language-plaintext highlighter-rouge">xvfb-run</code> vs running <code class="language-plaintext highlighter-rouge">xvfb-run</code> from within gradle</h3>

<p>Searching for the terms “xvfb” and “gradle” leads to <a href="https://askubuntu.com/questions/748321/how-to-run-gradle-run-through-xvfb">an Ask Ubuntu answer</a> that states the following:</p>

<blockquote>
  <p>You likely don’t want to run a gradle task through Xvfb, but rather execute something within an X Windows Virtual Frame Buffer FROM a gradle task.</p>
</blockquote>

<p>The context is using <code class="language-plaintext highlighter-rouge">xvfb-run</code> for a <code class="language-plaintext highlighter-rouge">gradle run</code> configuration, not for <code class="language-plaintext highlighter-rouge">gradle test</code> or <code class="language-plaintext highlighter-rouge">gradle build</code>.
In that context the suggestion is somewhat sensible: Gradle <em>itself</em> does not need the display, only the sub-process that it starts does.
Nonetheless, I thought that there might be some deeper meaning to why simply using <code class="language-plaintext highlighter-rouge">xvfb-run</code> directly with Gradle would be a bad idea (the Ask Ubuntu post unfortunately does not explain this).
Maybe the display is not properly passed through to sub-processes?
Maybe <code class="language-plaintext highlighter-rouge">xvfb-run</code> only works with programs that actually ask for a display or there is some other incompatibility with the <code class="language-plaintext highlighter-rouge">gradle</code> executable?</p>

<p>Nope!
None of this is true.
The only downside of using <code class="language-plaintext highlighter-rouge">xvfb-run</code> for the whole Gradle task is that the framebuffer will exist for a slightly longer duration than it is needed.
In contrast to <code class="language-plaintext highlighter-rouge">gradle run</code>, there also simply is no option in Gradle to alter the call used to start the unit tests beyond setting system properties and other JVM args.
So in the sense of simplicity over premature optimization you can quote me on this: “You likely <em>do</em> want to run a Gradle task through Xvfb.” 😉</p>

<h3 id="dont-blindly-use-examples-from-man-pages">Don’t blindly use examples from man pages</h3>

<p>Due to the aforementioned Ask Ubuntu answer, I first tried to avoid <code class="language-plaintext highlighter-rouge">xvfb-run</code> and instead set up xvfb manually.
The first example in the man page for xvfb (or at least the version that is <a href="https://linux.die.net/man/1/xvfb">online on die.net</a>) is the following:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Xvfb :1 <span class="nt">-screen</span> 0 1600x1200x32
</code></pre></div></div>

<p>The problem with that is the <code class="language-plaintext highlighter-rouge">x32</code>, which sets the color depth to 32 bit.
While there are pixel formats with 32 bit, they technically only use 24 bits for the color information and the last 8 bits for transparency.
Therefore, <code class="language-plaintext highlighter-rouge">Xvfb</code> crashes with the following error message:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Fatal server error:
Couldn't add screen 0
</code></pre></div></div>

<p>If, for some reason, you want to set up <code class="language-plaintext highlighter-rouge">Xvfb</code> manually without <code class="language-plaintext highlighter-rouge">xvfb-run</code>, you can do the following:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">DISPLAY</span><span class="o">=</span>:1
Xvfb :1 <span class="nt">-screen</span> 0 1600x1200x24 &amp;
<span class="c"># your call using the Xvfb display goes here</span>
killall Xvfb
</code></pre></div></div>

<p>Which would look like this in a GitHub actions workflow:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Setup xvfb for screen </span><span class="m">0</span>
  <span class="na">run</span><span class="pi">:</span> <span class="s">Xvfb :1 -screen 0 1600x1200x24 &amp;</span>
<span class="pi">-</span> <span class="na">run</span><span class="pi">:</span> <span class="c1"># your program call goes here</span>
  <span class="na">env</span><span class="pi">:</span>
    <span class="na">DISPLAY</span><span class="pi">:</span> <span class="s">:1</span>
<span class="pi">-</span> <span class="na">name</span><span class="pi">:</span> <span class="s">Tear down xvfb</span>
  <span class="na">run</span><span class="pi">:</span> <span class="s">killall Xvfb</span>
</code></pre></div></div>

<p>By the way, there is a <a href="https://bugs.freedesktop.org/show_bug.cgi?id=17453">bug report</a> for the misleading line in the man page, which was opened in 2008 and lead to a change in the man page that was implemented in 2018.
Such is the way of low-priority issues, I guess.</p>]]></content><author><name>Christopher Schölzel</name></author><category term="headless" /><category term="swing" /><category term="awt" /><category term="gradle" /><category term="junit" /><category term="continuous integration" /><category term="gui" /><summary type="html"><![CDATA[Continuous integration servers are typically headless, meaning that they do not have a display attached. If you try to run any program that creates a GUI, you will get errors such as Java's java.awt.HeadlessException. This post tells you how to avoid that for Gradle tests with JUnit involving Java Swing applications in a GitHub actions workflow.]]></summary></entry><entry><title type="html">Allowing copy and paste shortcuts in disabled (read-only) text widget in Tcl/Tk</title><link href="https://www.arbitrary-but-fixed.net/2021/11/21/tk-disabled-text-copy-paste.html" rel="alternate" type="text/html" title="Allowing copy and paste shortcuts in disabled (read-only) text widget in Tcl/Tk" /><published>2021-11-21T17:05:00+00:00</published><updated>2021-11-21T17:05:00+00:00</updated><id>https://www.arbitrary-but-fixed.net/2021/11/21/tk-disabled-text-copy-paste</id><content type="html" xml:base="https://www.arbitrary-but-fixed.net/2021/11/21/tk-disabled-text-copy-paste.html"><![CDATA[<h2 id="the-problem">The Problem</h2>

<p>I wanted to write a simple Tk application that would assist my students in running unit tests for their exercises in an introductory Python course.
I choose Tk, because it can be accessed from the Python standard library with the <code class="language-plaintext highlighter-rouge">tkinter</code> module, which would allow my students to run the app without installing additional Python packages.
Inside the app, I needed a <code class="language-plaintext highlighter-rouge">Text</code> widget to display the standard output and standard error streams of the unit test process.
The students should be able to copy text from this widget into a search engine to learn about the meaning of Python error messages, but they should not be able to alter the content of the widget accidentally by typing something into the widget.
Like most GUI toolkits, Tk allows to “disable” widgets so that they do not react to user input.</p>

<p>However, from there on it got a little more complicated.
First, the <code class="language-plaintext highlighter-rouge">insert()</code> method used for setting the content of the text widget programmatically also becomes disabled when the widget state is set to <code class="language-plaintext highlighter-rouge">"disabled"</code>.
This can be fixed by wrapping calls to <code class="language-plaintext highlighter-rouge">insert()</code> with an activation and deactivation call as follows:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">text_widget</span><span class="p">.</span><span class="n">config</span><span class="p">(</span><span class="n">state</span><span class="o">=</span><span class="s">"normal"</span><span class="p">)</span>
<span class="n">text_widget</span><span class="p">.</span><span class="n">delete</span><span class="p">(</span><span class="s">"1.0"</span><span class="p">,</span> <span class="s">"end"</span><span class="p">)</span>
<span class="n">text_widget</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="s">"1.0"</span><span class="p">,</span> <span class="n">res</span><span class="p">)</span>
<span class="n">text_widget</span><span class="p">.</span><span class="n">config</span><span class="p">(</span><span class="n">state</span><span class="o">=</span><span class="s">"disabled"</span><span class="p">)</span>
</code></pre></div></div>

<p>Once this was fixed, however, my tutors noticed that copying text with the usual shortcut CTRL-C did not work in Ubuntu.
The curious thing: It <em>did</em> work on my Manjaro machine.
What could be the reason of this platform dependent behavior?</p>

<h2 id="things-i-learned-along-the-way">Things I learned along the way</h2>

<p>Before I tell you about the solution, I want to share a few discoveries with you that I made along the way:
My first instinct was check whether removing the lines containing <code class="language-plaintext highlighter-rouge">.config(state="disabled")</code> would remove the problem.
According to my tutor it did not, so I searched for the problem online learning that…</p>

<ul>
  <li>… there might be an <a href="https://stackoverflow.com/q/40946919">issue related to language settings</a>,</li>
  <li>… there is an <a href="https://stackoverflow.com/q/68964581">issue involving <code class="language-plaintext highlighter-rouge">ScrolledText</code></a>,</li>
  <li>… there are a lot of <a href="https://www.delftstack.com/howto/python-tkinter/how-to-make-tkinter-text-widget-read-only/">solutions for building a read-only <code class="language-plaintext highlighter-rouge">Text</code> widget</a>, but all of them somehow feel wrong.</li>
</ul>

<p>It seemed like disabling all keybindings manually by binding them to a function that returns <code class="language-plaintext highlighter-rouge">'break'</code> might be an easy solution, but this seemed a little too hacky for me, so I went on to do some research on the interna of Tk.</p>

<p>The first interesting thing that I found was a mention of a file called <code class="language-plaintext highlighter-rouge">tk.tcl</code>, which apparently is responsible for setting up the key bindings for copy-paste commands.
I did find the file at <code class="language-plaintext highlighter-rouge">/usr/lib/tk8.6/tk.tcl</code> in Manjaro and <code class="language-plaintext highlighter-rouge">/usr/share/tcltk/tk8.6/tk.tcl</code> in Ubuntu and by looking at the contents I could confirm that…</p>

<ul>
  <li>… the only variable that controls how the “virtual event” <code class="language-plaintext highlighter-rouge">&lt;&lt;Copy&gt;&gt;</code> is set up, is <code class="language-plaintext highlighter-rouge">tk windowingsystem</code>, which can be <code class="language-plaintext highlighter-rouge">x11</code>, <code class="language-plaintext highlighter-rouge">win32</code>, or <code class="language-plaintext highlighter-rouge">aqua</code>,</li>
  <li>… if you put <code class="language-plaintext highlighter-rouge">puts "&lt;insert frustrated slur here&gt;"</code> somewhere into the file, you are indeed insulted by every Tk app you start afterwards, including <code class="language-plaintext highlighter-rouge">git gui</code>,</li>
</ul>

<p>To find an easier way to check the value of <code class="language-plaintext highlighter-rouge">tk windowingsystem</code> than meddling with the <code class="language-plaintext highlighter-rouge">tk.tcl</code> file, I then searched for a way to execute Tcl/Tk commands.
It turns out that there are two shells <code class="language-plaintext highlighter-rouge">tclsh</code> and <code class="language-plaintext highlighter-rouge">wish</code>, the former being a REPL for plain Tcl and the latter already including the Tk library.
In <code class="language-plaintext highlighter-rouge">wish</code>, you can then simply type <code class="language-plaintext highlighter-rouge">tk windowingsystem</code> and as suspected it returned <code class="language-plaintext highlighter-rouge">x11</code> for both Linux systems, ruling out the <code class="language-plaintext highlighter-rouge">tk.tcl</code> file as possible culprit.</p>

<p>Since the next possible offender was <code class="language-plaintext highlighter-rouge">ScrolledText</code>, I looked at the <a href="https://github.com/python/cpython/blob/3.10/Lib/tkinter/scrolledtext.py">source code for the <code class="language-plaintext highlighter-rouge">tkinter.scrolledtext</code> module</a> and found out that the relevant class is only a little more than 20 lines of code, which in passing alleviated my concerns how hard it would be to write a version of <code class="language-plaintext highlighter-rouge">ScrolledText</code> that used themed <code class="language-plaintext highlighter-rouge">ttk</code> widgets.</p>

<h2 id="the-solution">The solution</h2>

<p>After a lot of back and forth changing search terms, I finally arrived at a <a href="https://stackoverflow.com/a/10817982">StackOverflow post</a> that mentioned the following with regard to using the disabled state for a read-only widget.</p>

<blockquote>
  <p>On some platforms, you also need to add a binding on &lt;1&gt; to give the focus to the widget, otherwise the highlighting for copy doesn’t appear:</p>

  <div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">text_widget</span><span class="p">.</span><span class="n">bind</span><span class="p">(</span><span class="s">"&lt;1&gt;"</span><span class="p">,</span> <span class="k">lambda</span> <span class="n">event</span><span class="p">:</span> <span class="n">text_widget</span><span class="p">.</span><span class="n">focus_set</span><span class="p">())</span>
</code></pre></div>  </div>
</blockquote>

<p>Having set up a VM with a vanilla Ubuntu installation in the meantime, this led me to re-investigate the issue regarding the disabled state again myself.</p>

<p>Sure enough, the copy-paste problem <em>did</em> vanish when I disabled the two relevant lines that set the disabled state.
So there is another lesson in passing here: If you can only communicate via text, make absolutely sure that what you <em>think</em> is being tested is actually what is being tested.
Back to the problem, I re-enabled the code lines that set the disabled state and used the suggestion from Bryan Oakley from StackOverflow instead and it did also work.</p>

<p>So the problem was solved, but I was still not satisfied, because I wanted to know the <em>cause</em> of this difference.
Who is responsible here? Is it Tk, is it Linux? Are there really such fundamental differences between Ubuntu and Manjaro?
My journey took me to the release notes of Tcl/Tk and I found the following bullet point in the <a href="(https://sourceforge.net/projects/tcl/files/Tcl/8.6.11/tcltk-release-notes-8.6.11.txt/view)">notes for version 8.6.11</a>:</p>

<blockquote>
  <ul>
    <li>Allow for select/copy from disabled text widget on all platforms</li>
  </ul>
</blockquote>

<p>So it <em>was</em> just a bug in Tk after all and the suggested fix to set the focus when the mouse button is clicked is the correct solution. Whew!
Sure enough, I checked the package versions and my Manjaro used version <code class="language-plaintext highlighter-rouge">8.6.11.1-1</code> of the <code class="language-plaintext highlighter-rouge">tk</code> package while Ubuntu used version <code class="language-plaintext highlighter-rouge">8.6.9+1</code>. 
As a last bit of information, if you want to find out your Tk version from <em>within</em> a Tk app, you can query <code class="language-plaintext highlighter-rouge">root.tk.call("info", "patchlevel")</code> where <code class="language-plaintext highlighter-rouge">root</code> is your root-level <code class="language-plaintext highlighter-rouge">Tk</code> object.</p>

<p>I hope you enjoyed this little murder mystery as much as I did, even if it was just the copy-paste function that was murdered and at the end of the day it turned out that it only did not find the way home, since it was lacking focus. 😆</p>]]></content><author><name>Christopher Schölzel</name></author><category term="tcl/tk" /><category term="Python" /><category term="GUI" /><summary type="html"><![CDATA[The shortcuts CTRL-C and CTRL-V should work in every Tk text widget whether it is disabled or not. However, a disabled "read-only" text widget currently allows these copy-paste shortcuts on some but not all platforms.]]></summary></entry><entry><title type="html">AI for laypersons: Image recognition with nearest neighbors</title><link href="https://www.arbitrary-but-fixed.net/ai%20for%20laypersons/2021/09/23/ai-explained-image-recognition.html" rel="alternate" type="text/html" title="AI for laypersons: Image recognition with nearest neighbors" /><published>2021-09-23T17:46:00+00:00</published><updated>2021-09-23T17:46:00+00:00</updated><id>https://www.arbitrary-but-fixed.net/ai%20for%20laypersons/2021/09/23/ai-explained-image-recognition</id><content type="html" xml:base="https://www.arbitrary-but-fixed.net/ai%20for%20laypersons/2021/09/23/ai-explained-image-recognition.html"><![CDATA[<h2 id="recap">Recap</h2>

<p>In the <a href="/artificial%20intelligence/machine%20learning/2021/05/30/ai-explained-with-k-nearest-neighbors.html">last post</a> we constructed our very first AI algorithm that was able to mimic the human decision whether a newly received email is unwanted “spam” or benign “ham”.
We also established that the technical term “algorithm” actually only means a step-by-step description of how to translate input values into output values.
For our spam detection algorithm we needed to find the database entry that has the maximum number of matching words between itself and the email that needs to be decided upon.
Then we could just look up whether that database email was considered “spam” or “ham” and copy the same label for our output.</p>

<p>This algorithm was effective, but it only detected spam, so how could we possibly use it as a proxy for understanding all the different and complex AI systems out there?
Well, in fact the <em>nearest neighbor</em> algorithm that we used as the basis for our spam detection AI, can be applied to a wide range of problems.
In this post, I want to show you how it can be used for a problem that has nothing at all to do with spam detection, and that is image recognition.</p>

<h2 id="a-new-task-image-recognition">A new task: Image recognition</h2>

<p>Imagine you work in mail distribution center and have to find a way to sort letters by the handwritten zip codes on letter envelopes.
This can and has for a long time been done by manual labor, but I imagine this is not anybody’s dream job.
Instead, it would be nice if we could just pass the mail through a scanner, and then use a computer program to automatically extract the zip code from the scanner image.
There are a whole lot of messy details in this process: The program needs to align the image, find the area on the envelope where the address is written, find the zip code within the address and separate the code into individual digits that it then needs to recognize.
To keep things simple, we only focus on the last step: Recognizing a handwritten digit from a black and white scanner image.</p>

<p>Some examples may look like this (which are converted samples from an actual AI dataset called <a href="http://yann.lecun.com/exdb/mnist/index.html">MNIST</a> by Corinna Cortes, Christopher JC Burges, and Yann LeCun):</p>

<p><img src="/assets/img/MNIST/MNIST_bw_0_3.png" alt="MNIST_bw_0_3" /> 
<img src="/assets/img/MNIST/MNIST_bw_0_10.png" alt="MNIST_bw_0_10" /> 
<img src="/assets/img/MNIST/MNIST_bw_0_25.png" alt="MNIST_bw_0_25" /> 
<img src="/assets/img/MNIST/MNIST_bw_0_28.png" alt="MNIST_bw_0_28" /> 
<img src="/assets/img/MNIST/MNIST_bw_0_55.png" alt="MNIST_bw_0_55" /> 
<img src="/assets/img/MNIST/MNIST_bw_0_69.png" alt="MNIST_bw_0_69" /> 
<img src="/assets/img/MNIST/MNIST_bw_0_71.png" alt="MNIST_bw_0_71" /> 
<img src="/assets/img/MNIST/MNIST_bw_0_101.png" alt="MNIST_bw_0_101" /> 
<img src="/assets/img/MNIST/MNIST_bw_0_126.png" alt="MNIST_bw_0_126" />
<img src="/assets/img/MNIST/MNIST_bw_0_136.png" alt="MNIST_bw_0_136" /></p>

<p><img src="/assets/img/MNIST/MNIST_bw_1_74.png" alt="MNIST_bw_1_74" />
<img src="/assets/img/MNIST/MNIST_bw_1_900.png" alt="MNIST_bw_1_900" />
<img src="/assets/img/MNIST/MNIST_bw_1_3124.png" alt="MNIST_bw_1_3124" />
<img src="/assets/img/MNIST/MNIST_bw_1_3906.png" alt="MNIST_bw_1_3906" />
<img src="/assets/img/MNIST/MNIST_bw_1_5254.png" alt="MNIST_bw_1_5254" />
<img src="/assets/img/MNIST/MNIST_bw_1_6901.png" alt="MNIST_bw_1_6901" />
<img src="/assets/img/MNIST/MNIST_bw_1_8488.png" alt="MNIST_bw_1_8488" />
<img src="/assets/img/MNIST/MNIST_bw_1_8682.png" alt="MNIST_bw_1_8682" />
<img src="/assets/img/MNIST/MNIST_bw_1_9540.png" alt="MNIST_bw_1_9540" />
<img src="/assets/img/MNIST/MNIST_bw_1_9931.png" alt="MNIST_bw_1_9931" /></p>

<p><img src="/assets/img/MNIST/MNIST_bw_2_646.png" alt="MNIST_bw_2_646" />
<img src="/assets/img/MNIST/MNIST_bw_2_1224.png" alt="MNIST_bw_2_1224" />
<img src="/assets/img/MNIST/MNIST_bw_2_1722.png" alt="MNIST_bw_2_1722" />
<img src="/assets/img/MNIST/MNIST_bw_2_3511.png" alt="MNIST_bw_2_3511" />
<img src="/assets/img/MNIST/MNIST_bw_2_6418.png" alt="MNIST_bw_2_6418" />
<img src="/assets/img/MNIST/MNIST_bw_2_6785.png" alt="MNIST_bw_2_6785" />
<img src="/assets/img/MNIST/MNIST_bw_2_7986.png" alt="MNIST_bw_2_7986" />
<img src="/assets/img/MNIST/MNIST_bw_2_8102.png" alt="MNIST_bw_2_8102" />
<img src="/assets/img/MNIST/MNIST_bw_2_8198.png" alt="MNIST_bw_2_8198" />
<img src="/assets/img/MNIST/MNIST_bw_2_9477.png" alt="MNIST_bw_2_9477" /></p>

<p><img src="/assets/img/MNIST/MNIST_bw_3_699.png" alt="MNIST_bw_3_699" />
<img src="/assets/img/MNIST/MNIST_bw_3_1607.png" alt="MNIST_bw_3_1607" />
<img src="/assets/img/MNIST/MNIST_bw_3_2441.png" alt="MNIST_bw_3_2441" />
<img src="/assets/img/MNIST/MNIST_bw_3_2770.png" alt="MNIST_bw_3_2770" />
<img src="/assets/img/MNIST/MNIST_bw_3_4443.png" alt="MNIST_bw_3_4443" />
<img src="/assets/img/MNIST/MNIST_bw_3_4509.png" alt="MNIST_bw_3_4509" />
<img src="/assets/img/MNIST/MNIST_bw_3_4613.png" alt="MNIST_bw_3_4613" />
<img src="/assets/img/MNIST/MNIST_bw_3_4990.png" alt="MNIST_bw_3_4990" />
<img src="/assets/img/MNIST/MNIST_bw_3_7849.png" alt="MNIST_bw_3_7849" />
<img src="/assets/img/MNIST/MNIST_bw_3_9882.png" alt="MNIST_bw_3_9882" /></p>

<p><img src="/assets/img/MNIST/MNIST_bw_4_65.png" alt="MNIST_bw_4_65" />
<img src="/assets/img/MNIST/MNIST_bw_4_774.png" alt="MNIST_bw_4_774" />
<img src="/assets/img/MNIST/MNIST_bw_4_1542.png" alt="MNIST_bw_4_1542" />
<img src="/assets/img/MNIST/MNIST_bw_4_1701.png" alt="MNIST_bw_4_1701" />
<img src="/assets/img/MNIST/MNIST_bw_4_4324.png" alt="MNIST_bw_4_4324" />
<img src="/assets/img/MNIST/MNIST_bw_4_4483.png" alt="MNIST_bw_4_4483" />
<img src="/assets/img/MNIST/MNIST_bw_4_5631.png" alt="MNIST_bw_4_5631" />
<img src="/assets/img/MNIST/MNIST_bw_4_5720.png" alt="MNIST_bw_4_5720" />
<img src="/assets/img/MNIST/MNIST_bw_4_5956.png" alt="MNIST_bw_4_5956" />
<img src="/assets/img/MNIST/MNIST_bw_4_9350.png" alt="MNIST_bw_4_9350" /></p>

<p><img src="/assets/img/MNIST/MNIST_bw_5_797.png" alt="MNIST_bw_5_797" />
<img src="/assets/img/MNIST/MNIST_bw_5_1940.png" alt="MNIST_bw_5_1940" />
<img src="/assets/img/MNIST/MNIST_bw_5_4131.png" alt="MNIST_bw_5_4131" />
<img src="/assets/img/MNIST/MNIST_bw_5_4583.png" alt="MNIST_bw_5_4583" />
<img src="/assets/img/MNIST/MNIST_bw_5_7241.png" alt="MNIST_bw_5_7241" />
<img src="/assets/img/MNIST/MNIST_bw_5_7451.png" alt="MNIST_bw_5_7451" />
<img src="/assets/img/MNIST/MNIST_bw_5_7888.png" alt="MNIST_bw_5_7888" />
<img src="/assets/img/MNIST/MNIST_bw_5_9013.png" alt="MNIST_bw_5_9013" />
<img src="/assets/img/MNIST/MNIST_bw_5_9814.png" alt="MNIST_bw_5_9814" />
<img src="/assets/img/MNIST/MNIST_bw_5_9877.png" alt="MNIST_bw_5_9877" /></p>

<p><img src="/assets/img/MNIST/MNIST_bw_6_2471.png" alt="MNIST_bw_6_2471" />
<img src="/assets/img/MNIST/MNIST_bw_6_2654.png" alt="MNIST_bw_6_2654" />
<img src="/assets/img/MNIST/MNIST_bw_6_3121.png" alt="MNIST_bw_6_3121" />
<img src="/assets/img/MNIST/MNIST_bw_6_5303.png" alt="MNIST_bw_6_5303" />
<img src="/assets/img/MNIST/MNIST_bw_6_5916.png" alt="MNIST_bw_6_5916" />
<img src="/assets/img/MNIST/MNIST_bw_6_5958.png" alt="MNIST_bw_6_5958" />
<img src="/assets/img/MNIST/MNIST_bw_6_5963.png" alt="MNIST_bw_6_5963" />
<img src="/assets/img/MNIST/MNIST_bw_6_6002.png" alt="MNIST_bw_6_6002" />
<img src="/assets/img/MNIST/MNIST_bw_6_6020.png" alt="MNIST_bw_6_6020" />
<img src="/assets/img/MNIST/MNIST_bw_6_6038.png" alt="MNIST_bw_6_6038" /></p>

<p><img src="/assets/img/MNIST/MNIST_bw_7_141.png" alt="MNIST_bw_7_141" />
<img src="/assets/img/MNIST/MNIST_bw_7_262.png" alt="MNIST_bw_7_262" />
<img src="/assets/img/MNIST/MNIST_bw_7_370.png" alt="MNIST_bw_7_370" />
<img src="/assets/img/MNIST/MNIST_bw_7_1260.png" alt="MNIST_bw_7_1260" />
<img src="/assets/img/MNIST/MNIST_bw_7_3594.png" alt="MNIST_bw_7_3594" />
<img src="/assets/img/MNIST/MNIST_bw_7_3969.png" alt="MNIST_bw_7_3969" />
<img src="/assets/img/MNIST/MNIST_bw_7_4530.png" alt="MNIST_bw_7_4530" />
<img src="/assets/img/MNIST/MNIST_bw_7_4730.png" alt="MNIST_bw_7_4730" />
<img src="/assets/img/MNIST/MNIST_bw_7_5999.png" alt="MNIST_bw_7_5999" />
<img src="/assets/img/MNIST/MNIST_bw_7_9302.png" alt="MNIST_bw_7_9302" /></p>

<p><img src="/assets/img/MNIST/MNIST_bw_8_947.png" alt="MNIST_bw_8_947" />
<img src="/assets/img/MNIST/MNIST_bw_8_1687.png" alt="MNIST_bw_8_1687" />
<img src="/assets/img/MNIST/MNIST_bw_8_2038.png" alt="MNIST_bw_8_2038" />
<img src="/assets/img/MNIST/MNIST_bw_8_3987.png" alt="MNIST_bw_8_3987" />
<img src="/assets/img/MNIST/MNIST_bw_8_4389.png" alt="MNIST_bw_8_4389" />
<img src="/assets/img/MNIST/MNIST_bw_8_5343.png" alt="MNIST_bw_8_5343" />
<img src="/assets/img/MNIST/MNIST_bw_8_7735.png" alt="MNIST_bw_8_7735" />
<img src="/assets/img/MNIST/MNIST_bw_8_7921.png" alt="MNIST_bw_8_7921" />
<img src="/assets/img/MNIST/MNIST_bw_8_8065.png" alt="MNIST_bw_8_8065" />
<img src="/assets/img/MNIST/MNIST_bw_8_8408.png" alt="MNIST_bw_8_8408" /></p>

<p><img src="/assets/img/MNIST/MNIST_bw_9_1045.png" alt="MNIST_bw_9_1045" />
<img src="/assets/img/MNIST/MNIST_bw_9_1554.png" alt="MNIST_bw_9_1554" />
<img src="/assets/img/MNIST/MNIST_bw_9_2089.png" alt="MNIST_bw_9_2089" />
<img src="/assets/img/MNIST/MNIST_bw_9_2387.png" alt="MNIST_bw_9_2387" />
<img src="/assets/img/MNIST/MNIST_bw_9_2916.png" alt="MNIST_bw_9_2916" />
<img src="/assets/img/MNIST/MNIST_bw_9_3369.png" alt="MNIST_bw_9_3369" />
<img src="/assets/img/MNIST/MNIST_bw_9_4325.png" alt="MNIST_bw_9_4325" />
<img src="/assets/img/MNIST/MNIST_bw_9_6000.png" alt="MNIST_bw_9_6000" />
<img src="/assets/img/MNIST/MNIST_bw_9_6895.png" alt="MNIST_bw_9_6895" />
<img src="/assets/img/MNIST/MNIST_bw_9_7952.png" alt="MNIST_bw_9_7952" /></p>

<p>The “intelligent” decision we are looking for in this situation is to take one of these images and recognize which number is shown on it.
For most of the above examples, this would be simple for a human, but there are also some instances that are a little more tricky:</p>

<ul>
  <li><img src="/assets/img/MNIST/MNIST_bw_7_1260.png" alt="MNIST_bw_7_1260" /> could be both a 1 or a 7.</li>
  <li><img src="/assets/img/MNIST/MNIST_bw_3_4990.png" alt="MNIST_bw_3_4990" /> is somewhere between a 2 and a 3.</li>
  <li><img src="/assets/img/MNIST/MNIST_bw_8_947.png" alt="MNIST_bw_8_947" /> might be confused for a 9 instead of an 8.</li>
</ul>

<h2 id="how-computers-see-images">How computers see images</h2>

<p>In order to automate this digit recognition task, we need to know how images are represented in a computer.
You probably already know this, but to start from the beginning: Digital images are made up of small squares with uniform color and size, which are called <em>pixels</em> (short for “picture element”).
To build up a whole image, these pixels are arranged in a regular grid of rows and columns.
The images above all have 28 rows and 28 columns of pixels, which each can be either fully black or fully white.
Let’s scale up one of these images to ten times its size to actually see the pixels.</p>

<p><img src="/assets/img/MNIST/MNIST_bw_1_3906_280x280.png" alt="Enlarged version of MNIST_bw_1_3906" /></p>

<p>Now we are still talking about images in terms of colors and geometric terms instead of text or numbers that a computer can read and manipulate.
One simple way of storing the above image in a machine-readable format is to create a text file and write a zero for each white pixel and a one for each black pixel, arranged in rows and columns and separated by spaces:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 1 1 1 1 1 1 1 1 1 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
</code></pre></div></div>

<p>If you squint your eyes a little, you can even still see the picture, but now it is just a bunch of zeros and ones.
This image format is called a <a href="https://en.wikipedia.org/wiki/Netpbm#PBM_example">Portable BitMap (PBM)</a>, and it can actually be read by open-source image editing tools like <a href="https://www.gimp.org/">GIMP</a>.
If you want to, you can try it out by downloading <a href="/assets/img/MNIST/MNIST_bw_1_3906.pbm">the above image in PBM format</a>.</p>

<p>The image formats that you are used to, like JPEG, PNG, or GIF, are much more complicated, but this is just because they are designed to save storage space.
Whenever images are displayed on the screen or opened in an image editor, it is in some bitmap-like format.</p>

<h2 id="comparing-images">Comparing images</h2>

<p>Now that we know how represent images in a format that can be understood by a computer program, we can start thinking about how to adjust our nearest neighbor algorithm to cope with 28x28 pixel images of digits.
First, let’s start by revisiting our spam detection algorithm:</p>

<h3 id="algorithm-simple-spam-detector">Algorithm: Simple spam detector</h3>

<p>Inputs:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Database</code>: list of labeled emails</li>
  <li><code class="language-plaintext highlighter-rouge">Query</code>: unlabeled email that should be classified</li>
</ul>

<p>Output:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Label</code>: the most fitting label for the query (either “spam” or “ham”)</li>
</ul>

<p>Steps:</p>

<ol>
  <li>For all labeled emails in the database, calculate the number of matching words between that email and the query.</li>
  <li>Find the database entry with the maximum number of matching words.</li>
  <li>Output the label attached to this database entry.</li>
</ol>

<p>There are a few things that we have to change to make this work with images.
First, we have different inputs and outputs now.
The <code class="language-plaintext highlighter-rouge">Database</code> is now a list of labeled images, where the label is the digit shown in the image.
The <code class="language-plaintext highlighter-rouge">Query</code> is an unlabeled image that has to be recognized.
And finally, the output is again a <code class="language-plaintext highlighter-rouge">Label</code>, but instead of only two options we now have to choose one of ten different labels that correspond to the ten digits from zero to nine.</p>

<p>Moving to the steps of the algorithm, the “number of matching words” is, of course, meaningless for images.
However, we can match something else to find the “nearest neighbor” of an image: the pixels.
We can count the number of matching <em>pixels</em> by moving through both pictures at the same time, starting at the top left, and moving in “reading” order from left to right and from top to bottom.
Every time the pixels at corresponding positions of the two images match, we increase the count of matching pixels by one.
When we reach the bottom right of the image, we will have the total number of matching pixels between both images.</p>

<p>To see that this works, let’s look at a small example with tiny 3x4 pixel images:</p>

<pre><code class="language-verbatim">[Database entry 1:]

Label: 1

0 1 0
0 1 0
0 1 0
0 1 0


[Database entry 2:]

Label: 7

1 1 1
0 0 1
0 0 1
0 0 1


[Query image:]

1 1 1
0 0 1
0 1 0
1 0 0
</code></pre>

<p>We have two images in the database of the digits one and seven, both consisting of straight lines.
The query image that we want to know about is another seven, but this time with a slanted lower line.
Our AI now compares the query image to both database images to find the following:</p>

<pre><code class="language-verbatim">Matches for entry 1:
0 1 0     1 1 1      x ✓ x
0 1 0  =  0 0 1  -&gt;  ✓ x x
0 1 0     0 1 0      ✓ ✓ ✓
0 1 0     1 0 0      x x ✓

  Total number of matching pixels: 6


Matches for entry 2:
1 1 1     1 1 1      ✓ ✓ ✓
0 0 1  =  0 0 1  -&gt;  ✓ ✓ ✓
0 0 1     0 1 0      ✓ x x
0 0 1     1 0 0      x ✓ x

  Total number of matching pixels: 8
</code></pre>

<p>Since database entry 2 has more matching pixels than database entry 1, our AI chooses to copy the label of entry 2 and correctly decides that the query image is a seven and not a one.</p>

<p>Now that we know that our idea works, the only thing that is left to do is to turn it into a new formal algorithm description:</p>

<h3 id="algorithm-simple-digit-recognizer">Algorithm: Simple digit recognizer</h3>

<p>Inputs:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Database</code>: list of labeled images</li>
  <li><code class="language-plaintext highlighter-rouge">Query</code>: unlabeled image that should be recognized</li>
</ul>

<p>Output:</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">Label</code>: the most fitting label for the query (0-9)</li>
</ul>

<p>Steps:</p>

<ol>
  <li>For all labeled images in the database, calculate the number of matching pixels between that image and the query.</li>
  <li>Find the database entry with the maximum number of matching pixels.</li>
  <li>Output the label attached to this database entry.</li>
</ol>

<p>As you can see, we only hat to change a few words.
The resulting algorithm is still a nearest neighbor algorithm, it only needed to be adapted to find “neighbors” of <em>images</em> instead of <em>emails</em>.</p>

<p>Again, we have turned the human task “recognize the handwritten number in this image” into a set of instructions for a machine that is now able to mimic this human decision-making process and thus can be considered an AI.
This also means that all implications that we have drawn for the spam detection algorithm are also valid for image recognition tasks.
In particular, the “intelligence” of the algorithm still only comes from having good samples in the database that represent the set of possible inputs well.
While the recognition of the slanted seven worked out fine in our simplified example, a database featuring larger images should definitely include both straight and slanted sevens, and possibly also sevens with an additional horizontal bar in the center.
If it does not have enough of these examples, some sevens could be falsely recognized as ones or fours.
In our mail distribution center setting, this could result in a delay in package delivery, as the package would initially end up in the wrong district, but real-world examples of image recognition systems based on too simple datasets can have far worse outcomes:
If you have dark skin, you have a <a href="https://www.wired.com/story/best-algorithms-struggle-recognize-black-faces-equally/">tenfold chance to be misidentified</a> by state-of-the-art facial recognition software.
This can lead to you not being able to pass through biometric passport validation at the airport or even a false identification as a criminal.
A major source for this problem is likely the fact that the databases used to train these algorithms can have as much as 80% light-skinned persons.
Of course these systems use more complicated algorithms than our nearest neighbor approach, but as in cooking, it is all about the ingredients.
If you give a bunch of moldy vegetables to a Michelin star chef and to a home-cook, the chef might be able to produce a more tolerable looking dish than the home-cook, but you probably would not want to eat either one.
This problem is endearingly called <a href="https://en.wikipedia.org/wiki/Garbage_in,_garbage_out">garbage in, garbage out</a>.
We will go into detail on this issue in one of the next posts of the series, but first we will take a little more time to explore the variety of possible application areas of the nearest neighbor algorithm.</p>

<h2 id="edits-and-acknowledgements">Edits and Acknowledgements</h2>

<p>Since it was first published, this article has undergone the following changes due to reader feedback:</p>

<ul>
  <li>Added more detailed explanation of “garbage in, garbage out” (thanks to Annina)</li>
</ul>]]></content><author><name>Christopher Schölzel</name></author><category term="AI for laypersons" /><category term="artificial intelligence" /><category term="machine learning" /><category term="image recognition" /><summary type="html"><![CDATA[In the first post of the 'AI for laypersons' series, I introduced a very simple AI based on the nearest neighbor algorithm. In this post, I want to show that the same idea can also be applied to image data - for example the MNIST database for recognizing handwritten digits.]]></summary></entry><entry><title type="html">Explaining artificial intelligence for laypersons using the k-nearest neighbors algorithm</title><link href="https://www.arbitrary-but-fixed.net/artificial%20intelligence/machine%20learning/2021/05/30/ai-explained-with-k-nearest-neighbors.html" rel="alternate" type="text/html" title="Explaining artificial intelligence for laypersons using the k-nearest neighbors algorithm" /><published>2021-05-30T18:38:00+00:00</published><updated>2021-05-30T18:38:00+00:00</updated><id>https://www.arbitrary-but-fixed.net/artificial%20intelligence/machine%20learning/2021/05/30/ai-explained-with-k-nearest-neighbors</id><content type="html" xml:base="https://www.arbitrary-but-fixed.net/artificial%20intelligence/machine%20learning/2021/05/30/ai-explained-with-k-nearest-neighbors.html"><![CDATA[<h2 id="why-write-a-post-about-artificial-intelligence">Why write a post about artificial intelligence?</h2>

<p>There are already countless answers to the question “What is artificial intelligence?”—most of them written by people that are far more experienced than I am.
So why write the umpteenth blog post about it?
The answer is that I tried to find a good introductory text about the topic for my students online and was disappointed with the search results.
For my taste, they were either too shallow to produce real understanding, leaving the reader with a large list of half-explained terms, or too technical to be approachable, focusing only on the tools and applications that the writer is most enthusiastic about.</p>

<p>This post is the first in a series with which I hope to bridge that gap by using (and to some extent abusing) the example of a very simple artificial intelligence (AI).
At the end of this first post, I want you to truly and fully understand how this AI works whether you are a computer scientist, a hairdresser, a physician or a baker.
With the whole series, I want to enable you to use the simple method behind it as a proxy for understanding other AI systems, approaches, and general questions and issues.</p>

<h2 id="what-is-ai">What is AI?</h2>

<p>I could start this section by introducing a lot of fancy words that describe different kinds of artificial intelligence (AI) to carefully contrast the term with other related terms such as “machine learning” and with more philosophical approaches.
Instead, I will just assume that for the layperson AI means “the stuff that companies like Google do that detects faces, plays chess, drives cars, recommends movies on Netflix, and powers Alexa and Siri”.</p>

<p>All those highly successful and widely used AI systems are programs that make predictions or guesses about some real-world problem based on a set of existing examples.
These predictions and guesses should be “intelligent” in the sense that they emulate the choices a skilled human being would make.</p>

<h2 id="a-typical-ai-problem">A typical AI problem</h2>

<p>To make this a little more concrete, think about your mail inbox and that nice Nigerian prince that wants to share his inherited fortune with you.
Most humans know that mails from nice Nigerian princes are usually just scams to get your money (sorry to all honest Nigerian princes out there 🙈), but how do we teach this knowledge to a machine?
After all we do not want to delete all those spam emails by ourselves!
There are a lot of ways to do this.
For example, we could set up a rule to delete all mails that contain the words “Nigerian” and “prince”.
But what if you want to talk to your Nigerian friend about Prince Harry and the latest gossip about the British royals?
This example shows that such simple keyword-based rules are not “intelligent” at all, but rather stupid.
I am sure most of you will easily recall a moment where your spam filter accidentally flagged an email that was obviously not spam—or the other way around.
As it turns out, acting “intelligent” is quite hard for a machine.</p>

<p>The basic idea of AI is that this elusive “intelligence” can be harnessed from existing data.
Each time you flag an email as spam, leave it in your inbox, or rescue that email address validation link from your spam folder, you generate examples of the human decisions that an AI spam filter should mimic.
These examples contain two components: An email, and a <em>label</em>, which is the result of your decision if that mail was either “spam” or “not spam” (also called “ham”).
It is important to have this label, because this is where information about “intelligence” is stored.</p>

<h2 id="comparing-emails">Comparing emails</h2>

<p>Once we have a set of labeled data, we can rephrase our initial task from “classify whether this email is spam or not” to “decide the label of this email like humans did in these previous examples”.
This is, of course, still not understandable for a machine.
For some spam mails, a lookup in our data set might be enough, because we already received the exact same email, but usually there is a little variance in spam in order to avoid detection by such simple means.
Therefore, we cannot avoid dealing with some form of uncertainty and as we all learned in school, uncertainty can best be tackled by statistics.
For our task this means that we do not look for exact matches in the database, but rather for <em>similar</em> emails, and we have to find some kind of statistic calculations that expresses the similarity of two emails as a number.
Expressing similarity between texts is in itself a highly complex task that requires a lot of intelligence, but there is a simple approach that we can take as first approximation:
Just count the number of words that both emails have in common.</p>

<p>Consider this example:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[Database entry 1:]

Label: spam
Subject: Need trustworthy business partner

I am Mohammed Abacha, prince of Nigeria. I am the son of the late Nigerian
Head of State who died on the 8th of June 1998. I have secretly deposited
the sum of $30,000,000.00 with a security firm abroad. I shall be grateful
if you could receive this fund into your Bank account for safekeeping.


[Database entry 2:]

Label: ham
Subject: The tabloids are at it again

This is a great one: Woman&amp;home titles "Meghan Markle and Prince Harry
weren't in 'great shape' mentally - reveals Tom Bradby who interviewed them
on Africa tour". And in that SAME article talking about mental health, they
end with "In other royal news, the Duchess of Cambridge stuns in red as she
steps out in London to promote photography book launch." Can you believe
it?! In other news ;), how are things in Nigeria?


[New email:]

Label: ???
Subject: The Guardian Nigeria

I just learned that Nigeria has a newspaper called "The Guardian Nigeria".
You don't find Prince Harry on the front page here, but you sure do find
him: https://guardian.ng/tag/prince-harry/ :D
</code></pre></div></div>

<p>In this example, our simple AI would find the following sets of matching words (assuming we split by punctuation and whitespace and transform letters to lowercase).</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Matches entry 1: ["the", "nigeria", "i", "a", "you", "prince", "on"]
Matches entry 2: ["the", "nigeria", "that", "a", "you", "prince", "harry", "on"]
</code></pre></div></div>

<p>With this, we have seven matches for database entry one and eight matches for database entry two.
Since the second entry has more matching words and was labeled as “ham” in the database, our spam detection AI will correctly copy this label for our friend’s email about the Guardian Nigeria.
As it turns out, <code class="language-plaintext highlighter-rouge">"harry"</code> was our savior after all.</p>

<h2 id="my-first-ai-algorithm">My first AI algorithm</h2>

<p>With this example, we have defined our first AI algorithm.
In fact, let’s look a little closer at the word “algorithm”.
It is used a lot in conjunction with AI or with any complex automated system—often to the point that it sounds a little arcane and ominous.
However, at the end of the day an <em>algorithm</em> is nothing more than a formal set of instructions that have to be carried out to calculate a result based on some input data.
Our algorithm in this article could be described as follows:</p>

<h3 id="algorithm-simple-spam-detector">Algorithm: Simple spam detector</h3>

<p>Inputs:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">Database</code>: list of labeled emails</li>
  <li><code class="language-plaintext highlighter-rouge">Query</code>: unlabeled email that should be classified</li>
</ul>

<p>Result:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">Label</code>: the most fitting label for the query (either “spam” or “ham”)</li>
</ul>

<p>Steps:</p>
<ol>
  <li>For all labeled emails in the database, calculate the number of matching words between that email and the query.</li>
  <li>Find the database entry with the maximum number of matching words.</li>
  <li>Output the label attached to this database entry.</li>
</ol>

<p>Usually, algorithmic definitions tend to be a bit more detailed and technical, but the phrases that we used here like “for all entries in a database” or “find the maximum” are universal and simple enough that they can be directly translated into code that can be understood by a computer.
The only remaining “difficult” part is the term “number of matching words”.
We will translate this into a more detailed algorithmic form in one of the next posts, but for now let’s just accept that it too can be made understandable to a computer.</p>

<p>We therefore have turned the instruction for the human task “decide whether this email is spam or not” into a set of instructions for a machine that can now mimic human decisions—we have created our first actual artificial intelligence.
There is, of course, a lot of room for improvement, and we will discuss some approaches in the next posts.
However, if we generalize our approach by replacing the term “maximum number of matching words” with “maximum similarity” or “minimum distance”, we obtain the so-called <em>nearest neighbor</em> algorithm.
This algorithm is a standard tool in any AI researcher’s inventory and if you do not only look at the single closest match but at the <em>k</em> closest matches (<em>k</em> just being any number like 5 or 100) you get the <a href="https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm"><em>k-nearest neighbors</em></a> algorithm, which often already is surprisingly accurate even for complex tasks.</p>

<p>This also highlights a curious fact about this algorithm:
Notice that the instructions do not contain any information describing the nature of what a spam email actually looks like.
We could use the very same algorithm to distinguish between “work” and “private” mail, by simply using a different database with a different set of labels.
The algorithm itself is very generic and all the “intelligence” comes from the data.
This is true for almost any artificial intelligence currently out there on the consumer market.
Sef-driving cars drive based on sensory data collected from humans driving these vehicles in test scenarios;
Alexa’s and Siri’s speech recognition is based on human speech samples that were manually transcribed by other humans;
and maybe the fact that computers can beat top-level players at chess isn’t that mind-boggling anymore when you consider that those computers were able to use data from thousands of the world’s best chess players—probably even including the very players they were able to beat.</p>

<p>If you could follow my explanation to this point, let me congratulate you and formally bestow you the title “apprentice AI researcher”.
If not, I would be very grateful if you could email me and tell me which parts you did not understand—ideally with a suggestion of how the text could be improved.</p>

<h2 id="edits-and-acknowledgements">Edits and Acknowledgements</h2>

<p>Since it was first published, this article has undergone the following changes due to reader feedback:</p>

<ul>
  <li>Properly introduced the acronym AI (thanks to Franzi)</li>
  <li>Removed mentions of the word “algorithm” before it was explained (thanks to Fabi)</li>
  <li>Re-wrote the algorithmic definition, removing unnecessary low-level detail and adding context (thanks to Franzi)</li>
</ul>]]></content><author><name>Christopher Schölzel</name></author><category term="artificial intelligence" /><category term="machine learning" /><summary type="html"><![CDATA[Today the term artificial intelligence (AI) is ubiquitous. It is easy to marvel at the achievements of the latest Google project or to quiver in fear before the scenarios invoked by singularity doomsday priests. Probably only a very small proportion of the population actually has a realistic impression of what AI is and what it can and cannot do. With this post I want to make my contribution to change that using an AI that is so simple that everyone can understand it.]]></summary></entry><entry><title type="html">How to get a git commit hash from a tree hash</title><link href="https://www.arbitrary-but-fixed.net/git/julia/2021/03/18/git-tree-sha1-to-commit-sha1.html" rel="alternate" type="text/html" title="How to get a git commit hash from a tree hash" /><published>2021-03-18T22:18:00+00:00</published><updated>2021-03-18T22:18:00+00:00</updated><id>https://www.arbitrary-but-fixed.net/git/julia/2021/03/18/git-tree-sha1-to-commit-sha1</id><content type="html" xml:base="https://www.arbitrary-but-fixed.net/git/julia/2021/03/18/git-tree-sha1-to-commit-sha1.html"><![CDATA[<h2 id="git-commit-hashes">Git commit hashes</h2>

<p>You probably know that git uses SHA-1 hashes to identify a commit along with its full history of commits.
This is a neat trick to ensure that you can absolutely be sure that two versions of a repository contain the exact same files with the exact same history.
You can see these hashes in your git log or on GitHub and they are commonly used to identify versions of a repository that have no corresponding tag.
They look like this <code class="language-plaintext highlighter-rouge">e78e40aa4a2c03bf469ef842d37ec5eeaf49f37b</code>, but usually a prefix like <code class="language-plaintext highlighter-rouge">e78e40a</code> is enough to uniquely identify them since there are already over 200 million of these prefixes and SHA-1 ensures a quite even distribution of hashes across these possibilities.</p>

<h2 id="git-hashes-in-julias-package-manager">Git hashes in Julia’s package manager</h2>

<p>However, recently I stumbled upon another kind of hash that git uses and that can <em>also</em> be used to identify the current version of a repository.
In many of my Modelica projects, I need the latest version of my Julia package <a href="https://github.com/THM-MoTE/ModelicaScriptingTools.jl">MoST.jl</a>, which leads to entries like the follwing in the <code class="language-plaintext highlighter-rouge">Manifest.toml</code> that Julia uses to identify the versions of all packages installed for the current project.</p>

<div class="language-toml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">[[ModelicaScriptingTools]]</span>
<span class="py">deps</span> <span class="p">=</span> <span class="p">[</span><span class="s">"CSV"</span><span class="p">,</span> <span class="s">"DataFrames"</span><span class="p">,</span> <span class="s">"Documenter"</span><span class="p">,</span> <span class="s">"Markdown"</span><span class="p">,</span> <span class="s">"OMJulia"</span><span class="p">,</span> <span class="s">"PyCall"</span><span class="p">,</span> <span class="s">"Test"</span><span class="p">,</span> <span class="s">"ZMQ"</span><span class="p">]</span>
<span class="py">git-tree-sha1</span> <span class="p">=</span> <span class="s">"950e41b2d5a6aacd24d541cb95a172b0bc2a0230"</span>
<span class="py">repo-rev</span> <span class="p">=</span> <span class="s">"main"</span>
<span class="py">repo-url</span> <span class="p">=</span> <span class="s">"https://github.com/THM-MoTE/ModelicaScriptingTools.jl.git"</span>
<span class="py">uuid</span> <span class="p">=</span> <span class="s">"9bd7ba1c-b518-491f-8f72-7efe190322aa"</span>
<span class="py">version</span> <span class="p">=</span> <span class="s">"1.1.0"</span>
</code></pre></div></div>

<p>The interesting part here is the <code class="language-plaintext highlighter-rouge">git-tree-sha1</code> entry, which has clearly something to do with Git and with SHA-1, but what does the <code class="language-plaintext highlighter-rouge">tree</code> mean?
This question became important for me, because I wanted to identify the <em>actual</em> version of my package that I used to produce the results for one of my papers.
I knew I had this information stored in the <code class="language-plaintext highlighter-rouge">Manifest.toml</code>, so I did not bother to write it down at the time.
This meant I was left with the task to obtain the SHA-1 hash identifying the commit version from this ominous “tree” hash.</p>

<h2 id="git-tree-hashes">Git tree hashes</h2>

<p>It turns out that Git uses hashes for a lot of things and it also always uses SHA-1.
The “tree” hashes are used to capture the contents of a directory tree including file contents and usage rights.
Each commit actually has <em>three</em> SHA-1 hashes associated with it:</p>

<ul>
  <li>The <em>commit hash</em> identifies the current commit with all of its history.</li>
  <li>The <em>parent hash</em> is a pointer to the parent of the current commit.</li>
  <li>The <em>tree hash</em>, which is used by Julia, captures the state of the whole directory tree of all the files in the repository.</li>
</ul>

<h2 id="getting-the-commit-hash-associated-with-a-tree-hash">Getting the commit hash associated with a tree hash</h2>

<p>You can see these, by using <code class="language-plaintext highlighter-rouge">git log --pretty=raw</code>, which gives an output like the following:</p>

<pre><code class="language-git">commit a8a3dd68a37e21b1c2c835b7797e004940b2d9e6
tree 950e41b2d5a6aacd24d541cb95a172b0bc2a0230
parent 17577427038d695c4bce8696f36d2df726eb1c4d
author Christopher Schölzel &lt;christopher.schoelzel@gmx.net&gt; 1615040546 +0100
committer Christopher Schölzel &lt;christopher.schoelzel@gmx.net&gt; 1615040546 +0100

    adds documenter key for deploying docs

...
</code></pre>

<p>So one easy way to get the commit hash associated to a particular tree hash is to use</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>git log <span class="nt">--pretty</span><span class="o">=</span>raw | <span class="nb">grep</span> <span class="nt">-B</span> 1 &lt;tree-hash&gt;
</code></pre></div></div>

<p>Note that the association of tree hashes to commit hashes is not unique.
If, for example, a commit reverts the changes of the previous commit, the new tree hash will be the same as the tree hash before the first commit.</p>

<h2 id="why-use-tree-hashes-to-identify-a-commit">Why use tree hashes to identify a commit?</h2>

<p>Now the only question that remains is why the heck does Julia’s package manager use tree hashes instead of commit hashes, when only the latter can be used to uniquely identify the package version on GitHub, for example?</p>

<p>The answer is as simple as it is unpleasant: Because people like to rewrite history far more than they should.
The main advantage of tree hashes is that they stay the same after <code class="language-plaintext highlighter-rouge">git rebase</code> has been used to “clean up” the history (which in my opinion is more accurately described as <em>messing up</em> the history 99% of the time).
So if a Julia package developer rebases their repository, the <code class="language-plaintext highlighter-rouge">Manifest.toml</code> file of all people using this package stays valid as long as there is some commit in the new repository history that provides the exact same file content.
This is, of course, perfectly safe for the sake of identifying a dependency to a precise version of a package to install, but it’s one of these features where you are kinda doing it wrong if you need it.</p>]]></content><author><name>Christopher Schölzel</name></author><category term="git" /><category term="julia" /><summary type="html"><![CDATA[Julia uses git tree hashes to designate the version of a package in a git repository. What are those and how can we find the commit hash to identify the version on GitHub?]]></summary></entry></feed>