<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>David Anekstein's blog</title>
    <subtitle><![CDATA[David Anekstein's blog posts]]></subtitle>
    <link href="https://anekstein.com/atom.xml" rel="self" />
    <link href="https://anekstein.com" />
    <id>https://anekstein.com/atom.xml</id>
    <author>
        <name>David Anekstein</name>
        
        <email>david.anekstein.public@gmail.com</email>
        
    </author>
    <updated>2026-02-01T00:00:00Z</updated>
    <entry>
    <title>Solving Soma
</title>
    <link href="https://anekstein.com/posts/2026-02-01-blocker.html" />
    <id>https://anekstein.com/posts/2026-02-01-blocker.html</id>
    <published>2026-02-01T00:00:00Z</published>
    <updated>2026-02-01T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>Years ago in New York City’s Bryant park I came across a pop up store that sold puzzles, one of which was this wooden cube. I didn’t know what it was back then, but simplicity of the puzzle and the feel of the wooden blocks was attractive to me and so I bought it.</p>
<p><link rel="stylesheet" href="/posts/blocker/css/style.css"></p>
<figure class="interactive">
<div id="viz-intro-cube" class="canvas-container">
<canvas id="intro-canvas">
</canvas>
<p><img class="fallback-image" src="/posts/blocker/images/fallback-intro.png" alt="A 3x3x3 wooden Soma cube puzzle, assembled from 7 interlocking pieces">
<button id="intro-reassemble" type="button" aria-label="Reassemble puzzle" title="Reassemble puzzle" hidden><span aria-hidden="true">↻</span></button></p>
</div>
<div class="controls">
<p><input type="range" id="intro-explode" class="mouse-only" min="0" max="100" value="0">
<span class="touch-only">Hold and drag a piece, spread to explode</span></p>
</div>
</figure>
<p>For the last decade or so it’s been on my desk. Occasionally I take it apart and challenge myself to assemble it. It can take anywhere from 30 seconds to 30 minutes depending on how lucky I am with the initial placement.</p>
<p>Looking for further esoteric and probably-useless topics to dive deep into and blog about, I decided to try my hand at writing a solver for this puzzle.</p>
<h2 id="high-level-approach">High-Level Approach</h2>
<p>The first naive thing I thought of was a sort of backtracking algorithm, placing piece by piece until a cube was formed. The algorithm would move through each non-negative <code>(x,y,z)</code> coordinate and try to place a piece. If a piece couldn’t be placed (because it was out of bounds or colliding with another piece), it would be rotated until it could either be placed or until the next piece could be tried. That pattern would repeat for each unoccupied cell of the cube’s space until the last piece could be placed. Being able to place the last piece without collision means that a valid solution has been found.</p>
<h2 id="data-representation">Data Representation</h2>
<h3 id="piece-coordinates">Piece Coordinates</h3>
<p>Each piece is represented as an array of (x,y,z) coordinates, normalized to the origin of the grid, i.e. each starting from (0,0,0), and with their shape extending positively along each axis. I did this for simplicity because I had the puzzle in front of me while trying to encode each shape.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">type</span> Coord <span class="op">=</span> (<span class="dt">i32</span><span class="op">,</span> <span class="dt">i32</span><span class="op">,</span> <span class="dt">i32</span>)<span class="op">;</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">const</span> PIECES<span class="op">:</span> <span class="op">&amp;</span>[<span class="op">&amp;</span>[Coord]] <span class="op">=</span> <span class="op">&amp;</span>[</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">&amp;</span>[(<span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">2</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span>)]<span class="op">,</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    <span class="op">&amp;</span>[(<span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">2</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span>)]<span class="op">,</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">&amp;</span>[(<span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">1</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">2</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span>)]<span class="op">,</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">&amp;</span>[(<span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span>)]<span class="op">,</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">&amp;</span>[(<span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">1</span>)]<span class="op">,</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>    <span class="op">&amp;</span>[(<span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">1</span>)]<span class="op">,</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>    <span class="op">&amp;</span>[(<span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">1</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">,</span> (<span class="dv">0</span><span class="op">,</span> <span class="dv">1</span><span class="op">,</span> <span class="dv">1</span>)]<span class="op">,</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>]<span class="op">;</span></span></code></pre></div>
<figure class="interactive">
<div id="viz-pieces" class="canvas-container">
<canvas id="pieces-canvas">
</canvas>
<p><img class="fallback-image" src="/posts/blocker/images/fallback-pieces.png" alt="The 7 Soma cube pieces: an L-shape, T-shape, S-shape, small L, and three 3D corner pieces in red, green, blue, yellow, magenta, cyan, and orange"></p>
</div>
</figure>
<h3 id="piece-orientations">Piece Orientations</h3>
<p>In order to test each piece’s placement in a particular cell, we need to generate all possible rotations, of which there are 24.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">const</span> ROTATIONS<span class="op">:</span> [<span class="kw">fn</span>(Coord) <span class="op">-&gt;</span> Coord<span class="op">;</span> <span class="dv">24</span>] <span class="op">=</span> [</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>    <span class="co">// +z face up, rotate around z axis</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span>(x<span class="op">,</span> y<span class="op">,</span> z)<span class="op">|</span> (x<span class="op">,</span> y<span class="op">,</span> z)<span class="op">,</span>      <span class="co">// 0 degrees</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span>(x<span class="op">,</span> y<span class="op">,</span> z)<span class="op">|</span> (<span class="op">-</span>y<span class="op">,</span> x<span class="op">,</span> z)<span class="op">,</span>     <span class="co">// 90 degrees</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span>(x<span class="op">,</span> y<span class="op">,</span> z)<span class="op">|</span> (<span class="op">-</span>x<span class="op">,</span> <span class="op">-</span>y<span class="op">,</span> z)<span class="op">,</span>    <span class="co">// 180 degrees</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span>(x<span class="op">,</span> y<span class="op">,</span> z)<span class="op">|</span> (y<span class="op">,</span> <span class="op">-</span>x<span class="op">,</span> z)<span class="op">,</span>     <span class="co">// 270 degrees</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>    <span class="co">// +y face up, rotate around y axis</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span>(x<span class="op">,</span> y<span class="op">,</span> z)<span class="op">|</span> (x<span class="op">,</span> <span class="op">-</span>z<span class="op">,</span> y)<span class="op">,</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>    <span class="op">|</span>(x<span class="op">,</span> y<span class="op">,</span> z)<span class="op">|</span> (z<span class="op">,</span> x<span class="op">,</span> y)<span class="op">,</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ... all of the other rotations</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>]<span class="op">;</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">fn</span> all_orientations(piece<span class="op">:</span> <span class="op">&amp;</span>[Coord]) <span class="op">-&gt;</span> <span class="dt">Vec</span><span class="op">&lt;</span><span class="dt">Vec</span><span class="op">&lt;</span>Coord<span class="op">&gt;&gt;</span> <span class="op">{</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> <span class="kw">mut</span> orientations<span class="op">:</span> <span class="dt">Vec</span><span class="op">&lt;</span><span class="dt">Vec</span><span class="op">&lt;</span>Coord<span class="op">&gt;&gt;</span> <span class="op">=</span> ROTATIONS</span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a>        <span class="op">.</span>iter()</span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a>        <span class="op">.</span>map(<span class="op">|</span>rotate<span class="op">|</span> <span class="op">{</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a>            <span class="kw">let</span> rotated_coords<span class="op">:</span> <span class="dt">Vec</span><span class="op">&lt;</span>Coord<span class="op">&gt;</span> <span class="op">=</span> piece</span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a>                <span class="op">.</span>iter()</span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a>                <span class="op">.</span>map(<span class="op">|&amp;</span>coord<span class="op">|</span> rotate(coord))</span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a>                <span class="op">.</span>collect()<span class="op">;</span></span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a>            normalize_to_origin(rotated_coords)</span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span>)</span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a>        <span class="op">.</span>collect()<span class="op">;</span></span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-25"><a href="#cb2-25" aria-hidden="true" tabindex="-1"></a>    orientations<span class="op">.</span>sort()<span class="op">;</span></span>
<span id="cb2-26"><a href="#cb2-26" aria-hidden="true" tabindex="-1"></a>    orientations<span class="op">.</span>dedup()<span class="op">;</span></span>
<span id="cb2-27"><a href="#cb2-27" aria-hidden="true" tabindex="-1"></a>    orientations</span>
<span id="cb2-28"><a href="#cb2-28" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<figure class="interactive">
<div id="viz-rotations" class="canvas-container">
<canvas id="rotations-canvas">
</canvas>
<p><img class="fallback-image" src="/posts/blocker/images/fallback-rotations.png" alt="A single L-shaped piece shown in one of its 24 possible orientations"></p>
</div>
<div class="controls">
<div class="solution-nav">
<span id="rotation-label">Orientation 1 / 24</span>
<div class="nav-buttons">
<button id="prev-rotation" class="nav-btn" aria-label="Previous orientation">
←
</button>
<button id="next-rotation" class="nav-btn" aria-label="Next orientation">
→
</button>
</div>
</div>
</div>
</figure>
<p>Normalizing a piece to the origin sets us up for sorting and deduplication:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> normalize_to_origin(<span class="kw">mut</span> coords<span class="op">:</span> <span class="dt">Vec</span><span class="op">&lt;</span>Coord<span class="op">&gt;</span>) <span class="op">-&gt;</span> <span class="dt">Vec</span><span class="op">&lt;</span>Coord<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> min_x <span class="op">=</span> coords<span class="op">.</span>iter()<span class="op">.</span>map(<span class="op">|</span>(x<span class="op">,</span> _<span class="op">,</span> _)<span class="op">|</span> <span class="op">*</span>x)<span class="op">.</span>min()<span class="op">.</span>unwrap()<span class="op">;</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> min_y <span class="op">=</span> coords<span class="op">.</span>iter()<span class="op">.</span>map(<span class="op">|</span>(_<span class="op">,</span> y<span class="op">,</span> _)<span class="op">|</span> <span class="op">*</span>y)<span class="op">.</span>min()<span class="op">.</span>unwrap()<span class="op">;</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> min_z <span class="op">=</span> coords<span class="op">.</span>iter()<span class="op">.</span>map(<span class="op">|</span>(_<span class="op">,</span> _<span class="op">,</span> z)<span class="op">|</span> <span class="op">*</span>z)<span class="op">.</span>min()<span class="op">.</span>unwrap()<span class="op">;</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> (x<span class="op">,</span> y<span class="op">,</span> z) <span class="kw">in</span> <span class="op">&amp;</span><span class="kw">mut</span> coords <span class="op">{</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>        <span class="op">*</span>x <span class="op">-=</span> min_x<span class="op">;</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>        <span class="op">*</span>y <span class="op">-=</span> min_y<span class="op">;</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>        <span class="op">*</span>z <span class="op">-=</span> min_z<span class="op">;</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>    coords</span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h3 id="placement-encoding">Placement Encoding</h3>
<p>In order to easily and efficiently check for collisions and occupancy of cells, we represent coordinates as indices ranging from 0-26.</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">const</span> <span class="kw">fn</span> coord_to_idx(x<span class="op">:</span> <span class="dt">i32</span><span class="op">,</span> y<span class="op">:</span> <span class="dt">i32</span><span class="op">,</span> z<span class="op">:</span> <span class="dt">i32</span>) <span class="op">-&gt;</span> <span class="dt">usize</span> <span class="op">{</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>    (x <span class="op">*</span> <span class="dv">9</span> <span class="op">+</span> y <span class="op">*</span> <span class="dv">3</span> <span class="op">+</span> z) <span class="kw">as</span> <span class="dt">usize</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">const</span> <span class="kw">fn</span> idx_to_coord(cell_index<span class="op">:</span> <span class="dt">usize</span>) <span class="op">-&gt;</span> Coord <span class="op">{</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>    (</span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>        (cell_index <span class="op">/</span> <span class="dv">9</span>) <span class="kw">as</span> <span class="dt">i32</span><span class="op">,</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>        ((cell_index <span class="op">/</span> <span class="dv">3</span>) <span class="op">%</span> <span class="dv">3</span>) <span class="kw">as</span> <span class="dt">i32</span><span class="op">,</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>        (cell_index <span class="op">%</span> <span class="dv">3</span>) <span class="kw">as</span> <span class="dt">i32</span><span class="op">,</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>    )</span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>This lets us use a bitmask in order to tell if a cell is occupied. For example, a small L piece with an occupied position of <code>[(0, 0, 0), (1, 0, 0), (0, 1, 0)]</code> would have a bitmask of <code>0b1000001001</code>. If we try to place another piece that would occupy the <code>(0,0,0)</code> cell, <code>coordinates1 &amp; coordinates2</code> would return a bitmask that is non-zero and indicate a collision.</p>
<p>Now we can represent a placement as a struct with the bitmask encoding of position, a coordinate representation of the piece to construct final solutions, and a count of the number of cubes to bound the iteration on the cube positions:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">/// a piece orientation: cube positions after rotation and normalization</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> Orientation <span class="op">=</span> <span class="dt">Vec</span><span class="op">&lt;</span>Coord<span class="op">&gt;;</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="co">/// all valid placements for one piece at a specific target cell</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> CellPlacements <span class="op">=</span> <span class="dt">Vec</span><span class="op">&lt;</span>Placement<span class="op">&gt;;</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="co">/// lookup table indexed by `[piece_index][cell_index]`</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> PlacementTable <span class="op">=</span> <span class="dt">Vec</span><span class="op">&lt;</span><span class="dt">Vec</span><span class="op">&lt;</span>CellPlacements<span class="op">&gt;&gt;;</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Placement <span class="op">{</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>    <span class="co">/// bitmask where bit `i` is set if cell `i` is occupied by this placement</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>    occupied_mask<span class="op">:</span> <span class="dt">u32</span><span class="op">,</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>    <span class="co">/// absolute coordinates of each cube in the grid</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>    cube_positions<span class="op">:</span> [Coord<span class="op">;</span> MAX_CUBES_PER_PIECE]<span class="op">,</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>    <span class="co">/// number of cubes in this piece (3 or 4)</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a>    cube_count<span class="op">:</span> <span class="dt">u8</span><span class="op">,</span></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h3 id="precomputing-valid-placements">Precomputing Valid Placements</h3>
<p>With this, we can now check and construct partial solutions. <code>try_create_placement</code> takes a piece in a particular <code>orientation</code> (normalized) and tries to place its <code>anchor</code> point at a <code>target</code> location. An anchor is simply one of the cubes a particular piece is comprised of. Placing a piece in a target cell relative to some anchor means that the anchor cube is meant to be placed in that target cell. To see if a piece can be placed at the target relative to its anchor, it calculates each cube’s absolute position, makes sure no component is out of bounds and, if in bounds, returns that placement containing its collision bitmask.</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> try_create_placement(</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a>    orientation<span class="op">:</span> <span class="op">&amp;</span>Orientation<span class="op">,</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>    target<span class="op">:</span> Coord<span class="op">,</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>    anchor<span class="op">:</span> Coord<span class="op">,</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a>) <span class="op">-&gt;</span> <span class="dt">Option</span><span class="op">&lt;</span>Placement<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> <span class="kw">mut</span> occupied_mask <span class="op">=</span> <span class="dv">0u32</span><span class="op">;</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> <span class="kw">mut</span> cube_positions <span class="op">=</span> [(<span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">;</span> MAX_CUBES_PER_PIECE]<span class="op">;</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> offset <span class="op">=</span> (</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>        target<span class="op">.</span><span class="dv">0</span> <span class="op">-</span> anchor<span class="op">.</span><span class="dv">0</span><span class="op">,</span></span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>        target<span class="op">.</span><span class="dv">1</span> <span class="op">-</span> anchor<span class="op">.</span><span class="dv">1</span><span class="op">,</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>        target<span class="op">.</span><span class="dv">2</span> <span class="op">-</span> anchor<span class="op">.</span><span class="dv">2</span><span class="op">,</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>    )<span class="op">;</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a>    <span class="cf">for</span> (cube_index<span class="op">,</span> <span class="op">&amp;</span>(piece_x<span class="op">,</span> piece_y<span class="op">,</span> piece_z)) <span class="kw">in</span></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a>        orientation<span class="op">.</span>iter()<span class="op">.</span>enumerate()</span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a>    <span class="op">{</span></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a>        <span class="kw">let</span> absolute_x <span class="op">=</span> piece_x <span class="op">+</span> offset<span class="op">.</span><span class="dv">0</span><span class="op">;</span></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a>        <span class="kw">let</span> absolute_y <span class="op">=</span> piece_y <span class="op">+</span> offset<span class="op">.</span><span class="dv">1</span><span class="op">;</span></span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a>        <span class="kw">let</span> absolute_z <span class="op">=</span> piece_z <span class="op">+</span> offset<span class="op">.</span><span class="dv">2</span><span class="op">;</span></span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a>        <span class="co">// check if this cube is within the 3x3x3 grid bounds</span></span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a>        <span class="cf">if</span> <span class="op">!</span>(<span class="dv">0</span><span class="op">..</span><span class="dv">3</span>)<span class="op">.</span>contains(<span class="op">&amp;</span>absolute_x)</span>
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a>            <span class="op">||</span> <span class="op">!</span>(<span class="dv">0</span><span class="op">..</span><span class="dv">3</span>)<span class="op">.</span>contains(<span class="op">&amp;</span>absolute_y)</span>
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a>            <span class="op">||</span> <span class="op">!</span>(<span class="dv">0</span><span class="op">..</span><span class="dv">3</span>)<span class="op">.</span>contains(<span class="op">&amp;</span>absolute_z)</span>
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a>        <span class="op">{</span></span>
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a>            <span class="cf">return</span> <span class="cn">None</span><span class="op">;</span></span>
<span id="cb6-27"><a href="#cb6-27" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb6-28"><a href="#cb6-28" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-29"><a href="#cb6-29" aria-hidden="true" tabindex="-1"></a>        <span class="kw">let</span> idx <span class="op">=</span> coord_to_idx(absolute_x<span class="op">,</span> absolute_y<span class="op">,</span> absolute_z)<span class="op">;</span></span>
<span id="cb6-30"><a href="#cb6-30" aria-hidden="true" tabindex="-1"></a>        occupied_mask <span class="op">|=</span> <span class="dv">1</span> <span class="op">&lt;&lt;</span> idx<span class="op">;</span></span>
<span id="cb6-31"><a href="#cb6-31" aria-hidden="true" tabindex="-1"></a>        cube_positions[cube_index] <span class="op">=</span> (absolute_x<span class="op">,</span> absolute_y<span class="op">,</span> absolute_z)<span class="op">;</span></span>
<span id="cb6-32"><a href="#cb6-32" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb6-33"><a href="#cb6-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-34"><a href="#cb6-34" aria-hidden="true" tabindex="-1"></a>    <span class="cn">Some</span>(Placement <span class="op">{</span></span>
<span id="cb6-35"><a href="#cb6-35" aria-hidden="true" tabindex="-1"></a>        occupied_mask<span class="op">,</span></span>
<span id="cb6-36"><a href="#cb6-36" aria-hidden="true" tabindex="-1"></a>        cube_positions<span class="op">,</span></span>
<span id="cb6-37"><a href="#cb6-37" aria-hidden="true" tabindex="-1"></a>        cube_count<span class="op">:</span> orientation<span class="op">.</span>len() <span class="kw">as</span> <span class="dt">u8</span><span class="op">,</span></span>
<span id="cb6-38"><a href="#cb6-38" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>)</span>
<span id="cb6-39"><a href="#cb6-39" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p><code>try_create_placement</code> is called for all pieces in all orientations to generate a table of placements in <code>build_placement_table</code>. The purpose of <code>build_placement_table</code> is simply to generate an account of all valid ways you can place each piece within the 3x3x3 grid by itself without going out of bounds, i.e. without considering any collisions. To do so, it iterates over all orientations and placements for each possible piece, anchor point, and target cell.</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> build_placement_table(pieces<span class="op">:</span> <span class="op">&amp;</span>[<span class="op">&amp;</span>[Coord]]) <span class="op">-&gt;</span> PlacementTable <span class="op">{</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> piece_orientations<span class="op">:</span> <span class="dt">Vec</span><span class="op">&lt;</span><span class="dt">Vec</span><span class="op">&lt;</span>Orientation<span class="op">&gt;&gt;</span> <span class="op">=</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>        pieces<span class="op">.</span>iter()<span class="op">.</span>map(<span class="op">|</span>piece<span class="op">|</span> all_orientations(piece))<span class="op">.</span>collect()<span class="op">;</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>    piece_orientations</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>        <span class="op">.</span>iter()</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>        <span class="op">.</span>map(<span class="op">|</span>orientations<span class="op">|</span> <span class="op">{</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a>            (<span class="dv">0</span><span class="op">..</span>GRID_SIZE)</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a>                <span class="op">.</span>map(<span class="op">|</span>target_cell<span class="op">|</span> <span class="op">{</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a>                    <span class="kw">let</span> target_position <span class="op">=</span> idx_to_coord(target_cell)<span class="op">;</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a>                    <span class="kw">let</span> <span class="kw">mut</span> placements <span class="op">=</span> <span class="dt">Vec</span><span class="pp">::</span>new()<span class="op">;</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a>                    <span class="cf">for</span> orientation <span class="kw">in</span> orientations <span class="op">{</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a>                        <span class="cf">for</span> <span class="op">&amp;</span>anchor <span class="kw">in</span> orientation <span class="op">{</span></span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a>                            <span class="cf">if</span> <span class="kw">let</span> <span class="cn">Some</span>(placement) <span class="op">=</span></span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a>                                try_create_placement(orientation<span class="op">,</span> target_position<span class="op">,</span> anchor)</span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a>                            <span class="op">{</span></span>
<span id="cb7-18"><a href="#cb7-18" aria-hidden="true" tabindex="-1"></a>                                placements<span class="op">.</span>push(placement)<span class="op">;</span></span>
<span id="cb7-19"><a href="#cb7-19" aria-hidden="true" tabindex="-1"></a>                            <span class="op">}</span></span>
<span id="cb7-20"><a href="#cb7-20" aria-hidden="true" tabindex="-1"></a>                        <span class="op">}</span></span>
<span id="cb7-21"><a href="#cb7-21" aria-hidden="true" tabindex="-1"></a>                    <span class="op">}</span></span>
<span id="cb7-22"><a href="#cb7-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-23"><a href="#cb7-23" aria-hidden="true" tabindex="-1"></a>                    placements</span>
<span id="cb7-24"><a href="#cb7-24" aria-hidden="true" tabindex="-1"></a>                <span class="op">}</span>)</span>
<span id="cb7-25"><a href="#cb7-25" aria-hidden="true" tabindex="-1"></a>                <span class="op">.</span>collect()</span>
<span id="cb7-26"><a href="#cb7-26" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span>)</span>
<span id="cb7-27"><a href="#cb7-27" aria-hidden="true" tabindex="-1"></a>        <span class="op">.</span>collect()</span>
<span id="cb7-28"><a href="#cb7-28" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h2 id="the-search">The Search</h2>
<p>Now that we have a table that can iterate over all valid placements, we can conduct the search. As mentioned earlier, this is going to take the form of a backtracking / depth-first-search algortihm which will attempt to advance the search for a valid solution and, if we are unable to advance, rewind the stack to the last decision point (piece/orientation) and try another path.</p>
<figure class="interactive">
<div id="viz-backtrack" class="canvas-container canvas-container--small">
<canvas id="backtrack-canvas">
</canvas>
<p><img class="fallback-image" src="/posts/blocker/images/fallback-backtrack.png" alt="A partially assembled Soma cube showing a search in progress"></p>
</div>
<div class="controls">
<p><input type="range" id="backtrack-slider" min="0" max="100" value="0"></p>
</div>
</figure>
<h3 id="search-setup">Search Setup</h3>
<p>First is the setup:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> placement_table <span class="op">=</span> build_placement_table(pieces)<span class="op">;</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> num_pieces <span class="op">=</span> pieces<span class="op">.</span>len()<span class="op">;</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> <span class="kw">mut</span> solutions <span class="op">=</span> <span class="dt">Vec</span><span class="pp">::</span>new()<span class="op">;</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> <span class="kw">mut</span> seen_states<span class="op">:</span> FxHashSet<span class="op">&lt;</span>GridKey<span class="op">&gt;</span> <span class="op">=</span> <span class="pp">FxHashSet::</span><span class="kw">default</span>()<span class="op">;</span></span></code></pre></div>
<p><code>seen_states</code> lets us avoid traversing down symmetric search branches (solutions can be symmetric by rotation and reflection), which would overcount and slow down the search.</p>
<p>To keep track of our search, we’ll maintain a stack with a snapshot of the partial solution:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> PartialSolution <span class="op">{</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>    <span class="co">// pieces placed so far in this search path</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>    placed_pieces<span class="op">:</span> [PlacedPiece<span class="op">;</span> MAX_PIECES]<span class="op">,</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>    <span class="co">// number of pieces placed</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>    placed_count<span class="op">:</span> <span class="dt">usize</span><span class="op">,</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>    <span class="co">// bitmask of available pieces</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a>    remaining_pieces<span class="op">:</span> <span class="dt">u8</span><span class="op">,</span></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a>    <span class="co">// bitmask of filled cells</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a>    occupied_cells<span class="op">:</span> <span class="dt">u32</span><span class="op">,</span></span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a>    <span class="co">// which piece we&#39;re trying</span></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a>    current_piece_idx<span class="op">:</span> <span class="dt">usize</span><span class="op">,</span></span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a>    <span class="co">// which orientation we&#39;re on</span></span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a>    current_orientation_index<span class="op">:</span> <span class="dt">usize</span><span class="op">,</span></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The search begins by pushing an initial partial solution onto the stack:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> initial_remaining <span class="op">=</span> (<span class="dv">1u8</span> <span class="op">&lt;&lt;</span> num_pieces) <span class="op">-</span> <span class="dv">1</span><span class="op">;</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> empty_piece<span class="op">:</span> PlacedPiece <span class="op">=</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>    (<span class="dv">0</span><span class="op">,</span> [(<span class="dv">0</span><span class="op">,</span> <span class="dv">0</span><span class="op">,</span> <span class="dv">0</span>)<span class="op">;</span> MAX_CUBES_PER_PIECE]<span class="op">,</span> <span class="dv">0</span>)<span class="op">;</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> <span class="kw">mut</span> search_stack <span class="op">=</span> <span class="pp">vec!</span>[PartialSolution <span class="op">{</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>    placed_pieces<span class="op">:</span> [empty_piece<span class="op">;</span> MAX_PIECES]<span class="op">,</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a>    placed_count<span class="op">:</span> <span class="dv">0</span><span class="op">,</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>    remaining_pieces<span class="op">:</span> initial_remaining<span class="op">,</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a>    occupied_cells<span class="op">:</span> <span class="dv">0</span><span class="op">,</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a>    current_piece_idx<span class="op">:</span> <span class="dv">0</span><span class="op">,</span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a>    current_orientation_index<span class="op">:</span> <span class="dv">0</span><span class="op">,</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>]<span class="op">;</span></span></code></pre></div>
<h3 id="finding-empty-cells">Finding Empty Cells</h3>
<p>The search starts by popping the next partial solution from the stack and tries to extend it by placing a piece in the first available location. If there are no more empty cells, that means we found a valid solution:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="cf">while</span> <span class="kw">let</span> <span class="cn">Some</span>(<span class="kw">mut</span> partial) <span class="op">=</span> search_stack<span class="op">.</span>pop() <span class="op">{</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> target_cell <span class="op">=</span> <span class="cf">match</span> find_first_empty_cell(partial<span class="op">.</span>occupied_cells) <span class="op">{</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>        <span class="cn">Some</span>(cell) <span class="op">=&gt;</span> cell<span class="op">,</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>        <span class="cn">None</span> <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>            <span class="kw">let</span> solution <span class="op">=</span> partial<span class="op">.</span>placed_pieces</span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>                [<span class="op">..</span>partial<span class="op">.</span>placed_count]<span class="op">.</span>to_vec()<span class="op">;</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a>            solutions<span class="op">.</span>push(solution)<span class="op">;</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a>            <span class="cf">continue</span><span class="op">;</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a>    <span class="op">};</span></span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ...</span></span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h3 id="trying-pieces">Trying Pieces</h3>
<p>Next is to try placing each remaining piece at that target cell, using the
placement table:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="ot">&#39;pieces</span><span class="op">:</span> <span class="cf">loop</span> <span class="op">{</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>    <span class="co">// find the next available piece, starting from current_piece_idx</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> <span class="cn">Some</span>(piece_index) <span class="op">=</span> (partial<span class="op">.</span>current_piece_idx<span class="op">..</span>num_pieces)</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>        <span class="op">.</span>find(<span class="op">|&amp;</span>i<span class="op">|</span> (partial<span class="op">.</span>remaining_pieces <span class="op">&amp;</span> (<span class="dv">1</span> <span class="op">&lt;&lt;</span> i)) <span class="op">!=</span> <span class="dv">0</span>)</span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a>    <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">break</span> <span class="ot">&#39;pieces</span><span class="op">;</span></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">};</span></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>    partial<span class="op">.</span>current_piece_idx <span class="op">=</span> piece_index<span class="op">;</span></span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> valid_placements <span class="op">=</span> <span class="op">&amp;</span>placement_table[piece_index][target_cell]<span class="op">;</span></span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ...</span></span>
<span id="cb12-13"><a href="#cb12-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h3 id="collision-detection">Collision Detection</h3>
<p>For each piece, the valid placement table gives us all of the orientations that piece can be placed in the target cell. For each of those orientations, we run a collision check using the bitmask to quickly know if we can continue adding pieces or must backtrack:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="co">// try each orientation of this piece at the target cell</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a><span class="cf">while</span> partial<span class="op">.</span>current_orientation_index <span class="op">&lt;</span> valid_placements<span class="op">.</span>len() <span class="op">{</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> placement <span class="op">=</span> <span class="op">&amp;</span>valid_placements[partial<span class="op">.</span>current_orientation_index]<span class="op">;</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>    partial<span class="op">.</span>current_orientation_index <span class="op">+=</span> <span class="dv">1</span><span class="op">;</span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a>    <span class="co">// fast collision check using bitmask AND</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> (partial<span class="op">.</span>occupied_cells <span class="op">&amp;</span> placement<span class="op">.</span>occupied_mask) <span class="op">!=</span> <span class="dv">0</span> <span class="op">{</span></span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a>        <span class="cf">continue</span><span class="op">;</span></span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span></code></pre></div>
<h3 id="advancing-the-search">Advancing the Search</h3>
<p>If there is no collision, we can push the updated state of the search onto the stack:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> new_occupied <span class="op">=</span> partial<span class="op">.</span>occupied_cells <span class="op">|</span> placement<span class="op">.</span>occupied_mask<span class="op">;</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> new_piece<span class="op">:</span> PlacedPiece <span class="op">=</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>    (piece_index<span class="op">,</span> placement<span class="op">.</span>cube_positions<span class="op">,</span> placement<span class="op">.</span>cube_count)<span class="op">;</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> <span class="kw">mut</span> new_placed <span class="op">=</span> partial<span class="op">.</span>placed_pieces<span class="op">;</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a>new_placed[partial<span class="op">.</span>placed_count] <span class="op">=</span> new_piece<span class="op">;</span></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> new_count <span class="op">=</span> partial<span class="op">.</span>placed_count <span class="op">+</span> <span class="dv">1</span><span class="op">;</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a><span class="co">// skip if we&#39;ve seen this state under any rotation or reflection</span></span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> canonical <span class="op">=</span> canonical_key(<span class="op">&amp;</span>new_placed[<span class="op">..</span>new_count])<span class="op">;</span></span>
<span id="cb14-11"><a href="#cb14-11" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> seen_states<span class="op">.</span>contains(<span class="op">&amp;</span>canonical) <span class="op">{</span></span>
<span id="cb14-12"><a href="#cb14-12" aria-hidden="true" tabindex="-1"></a>    <span class="cf">continue</span><span class="op">;</span></span>
<span id="cb14-13"><a href="#cb14-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb14-14"><a href="#cb14-14" aria-hidden="true" tabindex="-1"></a>seen_states<span class="op">.</span>insert(canonical)<span class="op">;</span></span>
<span id="cb14-15"><a href="#cb14-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-16"><a href="#cb14-16" aria-hidden="true" tabindex="-1"></a><span class="co">// remove the placed piece from remaining (flip bit off)</span></span>
<span id="cb14-17"><a href="#cb14-17" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> new_remaining <span class="op">=</span> partial<span class="op">.</span>remaining_pieces <span class="op">&amp;</span> <span class="op">!</span>(<span class="dv">1</span> <span class="op">&lt;&lt;</span> piece_index)<span class="op">;</span></span>
<span id="cb14-18"><a href="#cb14-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-19"><a href="#cb14-19" aria-hidden="true" tabindex="-1"></a><span class="co">// save current partial solution for backtracking, then push new state</span></span>
<span id="cb14-20"><a href="#cb14-20" aria-hidden="true" tabindex="-1"></a>search_stack<span class="op">.</span>push(partial)<span class="op">;</span></span>
<span id="cb14-21"><a href="#cb14-21" aria-hidden="true" tabindex="-1"></a>search_stack<span class="op">.</span>push(PartialSolution <span class="op">{</span></span>
<span id="cb14-22"><a href="#cb14-22" aria-hidden="true" tabindex="-1"></a>    placed_pieces<span class="op">:</span> new_placed<span class="op">,</span></span>
<span id="cb14-23"><a href="#cb14-23" aria-hidden="true" tabindex="-1"></a>    placed_count<span class="op">:</span> new_count<span class="op">,</span></span>
<span id="cb14-24"><a href="#cb14-24" aria-hidden="true" tabindex="-1"></a>    remaining_pieces<span class="op">:</span> new_remaining<span class="op">,</span></span>
<span id="cb14-25"><a href="#cb14-25" aria-hidden="true" tabindex="-1"></a>    occupied_cells<span class="op">:</span> new_occupied<span class="op">,</span></span>
<span id="cb14-26"><a href="#cb14-26" aria-hidden="true" tabindex="-1"></a>    current_piece_idx<span class="op">:</span> <span class="dv">0</span><span class="op">,</span></span>
<span id="cb14-27"><a href="#cb14-27" aria-hidden="true" tabindex="-1"></a>    current_orientation_index<span class="op">:</span> <span class="dv">0</span><span class="op">,</span></span>
<span id="cb14-28"><a href="#cb14-28" aria-hidden="true" tabindex="-1"></a><span class="op">}</span>)<span class="op">;</span></span>
<span id="cb14-29"><a href="#cb14-29" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb14-30"><a href="#cb14-30" aria-hidden="true" tabindex="-1"></a><span class="co">// placed a piece successfully, explore this branch</span></span>
<span id="cb14-31"><a href="#cb14-31" aria-hidden="true" tabindex="-1"></a><span class="cf">break</span> <span class="ot">&#39;pieces</span><span class="op">;</span></span></code></pre></div>
<h3 id="eliminating-duplicate-solutions">Eliminating Duplicate Solutions</h3>
<p>You may have noticed the check for partial solutions already explored. There are many possible rotational equivalents (24x) and chiral equivalents (2x) that we can encounter in the search, so <code>canonical_key</code> finds a stable representative of all rotational and chiral equivalent solutions in order to break early and avoid duplicating work:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">fn</span> canonical_key(solution<span class="op">:</span> <span class="op">&amp;</span>[PlacedPiece]) <span class="op">-&gt;</span> GridKey <span class="op">{</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> grid_key <span class="op">=</span> grid_to_key(<span class="op">&amp;</span>solution_to_grid(solution))<span class="op">;</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a>    find_smallest_rotation_with_reflection(<span class="op">&amp;</span>grid_key)</span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>All this does is create a grid representation of the solution and then construct the lexicographically-smallest representation of that grid, which gives us a stable key by which to compare partial solutions.</p>
<figure class="interactive">
<div id="viz-canonical" class="canvas-container">
<canvas id="canonical-canvas">
</canvas>
<p><img class="fallback-image" src="/posts/blocker/images/fallback-canonical.png" alt="Two Soma cube solutions side by side, showing a solution and its mirror image which are considered equivalent"></p>
</div>
<div class="controls">
<p><label>Rotation <input type="range" id="canonical-slider" min="0" max="23" value="0"> <span id="canonical-label">1 / 24</span></label></p>
</div>
<figcaption>
A solution and its mirror image, considered symmetric.
</figcaption>
</figure>
<p>With this, we now have the final result, which is 240 asymmetric solutions. On my macbook air, the search for all solutions takes ~80ms. You can view all of the code from this blog post <a href="https://github.com/oinoom/blocker">here</a>.</p>
<figure class="interactive">
<div id="viz-solution" class="canvas-container">
<canvas id="solution-canvas">
</canvas>
<p><img class="fallback-image" src="/posts/blocker/images/fallback-solution.png" alt="A complete Soma cube solution with pieces slightly exploded to show how they fit together"></p>
</div>
<div class="controls">
<input type="range" id="solution-explode" class="mouse-only" min="0" max="100" value="0">
<div class="solution-nav">
<span id="solution-label">Solution 1 of 240</span>
<div class="nav-buttons">
<button id="prev-solution" class="nav-btn" aria-label="Previous solution">
←
</button>
<button id="next-solution" class="nav-btn" aria-label="Next solution">
→
</button>
</div>
</div>
</div>
</figure>
<h2 id="afterword">Afterword</h2>
<p>Wondering what other fun things I could do and visualize and animate. I wondered about generalizing the solver with the Bedlam cube as the next candidate. The <a href="https://en.wikipedia.org/wiki/Bedlam_cube">Bedlam</a> cube is another polycube (twelve pentacubes and one tetracube), with 4x4x4 dimensions and 19,186 solutions, making for a much more complicated puzzle. To support this involved combing through all of the portions of the code that hardcoded anything related to Soma and parameterizing it by things like max piece size, number of pieces, cube dimensions, etc. With that, the solver is now generic for cubic dissection puzzles; you can find the updates to the solver in the repo <a href="https://github.com/oinoom/blocker/commit/3ff5684e2698bc5fd3fe367fbb244f636be967fc">here</a>.</p>
<figure class="interactive">
<div id="viz-bedlam-pieces" class="canvas-container">
<canvas id="bedlam-pieces-canvas">
</canvas>
<p><img class="fallback-image" src="/posts/blocker/images/fallback-bedlam.png" alt="A 4x4x4 Bedlam cube assembled from 13 colorful polycube pieces"></p>
</div>
</figure>
<h2 id="further-reading">Further Reading</h2>
<p>When writing this blog post, I actually had no idea the puzzle on my desk was called a Soma cube. After finding out and doing some digging I found this wonderful <a href="https://www.aswinvanwoudenberg.com/posts/soma-cube/">blog post</a> by Aswin van Woudenberg explaining a similar approach in Python. I’m only slightly sad I didn’t think of this first. His blog is full of programmatic puzzle solutions and I encourage you to check it out! And after implementing the generic solver, I found even more resources on this topic, which really does seem beaten to death by this point. There is another <a href="https://www.mattbusche.org/blog/article/polycube/">incredibly detailed post</a> by Matt Busche, where he shares several approaches and optimization techniques to solving these puzzles, including the ones I took: hole-filling backtracking coupled with bitfields and rotational symmetry pruning. On top of that, this polycube solving problem (another term I hadn’t known) can be modeled as an exact cover problem (<a href="http://arxiv.org/abs/cs/0011047">thanks Donald Knuth!</a>).</p>
<script src="/posts/blocker/js/base.js"></script>
<script src="/posts/blocker/js/solutions-data.js"></script>
<script src="/posts/blocker/js/blocker.js"></script>
<script src="/posts/blocker/js/bedlam-solutions-data.js"></script>
<script src="/posts/blocker/js/bedlam.js"></script>]]></summary>
</entry>
<entry>
    <title>Matrix Profiles 
</title>
    <link href="https://anekstein.com/posts/2025-03-26.html" />
    <id>https://anekstein.com/posts/2025-03-26.html</id>
    <published>2025-03-26T00:00:00Z</published>
    <updated>2025-03-26T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>Lately I’ve been thinking about time series analysis to aid in <a href="https://apps.apple.com/us/app/reflect-track-anything/id6463800032">Reflect’s</a> insights features. Towards this end, I’ve had a Hacker News <a href="https://news.ycombinator.com/item?id=42609595">thread</a> about anomaly detection bookmarked in <a href="https://apps.apple.com/us/app/later-set-intentions/id6742691976">Later</a>. I finally got to looking at it and there was a comment that mentioned the article left out <a href="https://www.cs.ucr.edu/~eamonn/MatrixProfile.html">matrix profiles</a>, which I had never heard of, so I decided to look into them.</p>
<h2 id="matrix-profiles">Matrix Profiles</h2>
<p>Here is an example time series taken from a popular matrix profile library:</p>
<figure>
<img src="/images/mp-series.jpg" alt="Example time series">
</figure>
<p>A matrix profile is a vector that describes similarity between windowed subsets of the time series. Each index corresponds to the start of a section and its value’s magnitude corresponds to the uniqueness of that section. The higher the value, the more unique it is. The lower the value, the more common the pattern is.</p>
<p>Here is the matrix profile for the time series shown above, for a 30 day window:</p>
<figure>
<img src="/images/mp-profile.jpg" alt="Matrix profile">
</figure>
<p>You can see that there are spikes in the matrix profile directly preceding deviations. That’s because each anomaly begins at these points. Anomalies aren’t always so clear, but I thought this time series made for a good illustration. The low points of the matrix profile are called motifs – they are the portions of the time series that are common.</p>
<p>There is an auxiliary vector called the matrix profile index; each element contains a pointer to the index of section that is most similar to the current section. The relationship between a section and its most similar neighbor is not symmetric, meaning that if section A has the highest similarity to section B, it does not imply that section B has the highest similarity to section A. It’s like with Euclidean distance; imagine a scalene triangle with points A, B, and C. A’s closest neighbor is B, but B’s closest neighbor is C.</p>
<p>It turns out that having the matrix profile gets you most of the way towards answering many of the questions one may have when presented with a time series. Anomaly detection is perhaps the most plain to see. The most anomalous sections are the ones with the highest values in the matrix profile, as I mentioned before.</p>
<p>But another application, with a really cool algorithm behind it, is time series segmentation.</p>
<h2 id="time-series-segmentation-with-matrix-profiles">Time Series Segmentation with Matrix Profiles</h2>
<p>The idea behind time series segmentation is to divide the time series into regions that are self-similar. For example, if you have a group of birds chirping to one another, you may want to identify which portions of the time series correspond to each bird’s song.</p>
<p>Given a matrix profile, we can draw lines above the matrix profile index to visualize the nearest neighbor relationships. Each line creates an arc.</p>
<figure>
<img src="/images/mp-arc.png" alt="Matrix profile arcs">
<figcaption style="color: #555; font-style: italic; text-align: center; margin-top: 8px; font-size: 0.9em;">
These arcs are above a different time series, not the matrix profile, but the arcs themselves are the relevant thing to focus on.
</figcaption>
</figure>
<p>Now, a third vector is constructed by sliding a window and counting the number of arcs that appear above each point. This creates another series, called an arc curve, that describes inflection points where a new segment begins, denoted by local minima on the curve. The algorithm that computes these curves is called <a href="http://www.cs.ucr.edu/%7Eeamonn/Segmentation_ICDM.pdf">FLUSS</a>, and it takes as input the desired number of segments (also called regimes) as well as the matrix profile for the series.</p>
<figure>
<img src="/images/mp-arc-curve.png" alt="Arc curve" style="width: 100%; height: auto;">
<figcaption style="color: #555; font-style: italic; text-align: center; margin-top: 8px; font-size: 0.9em;">
Arc curve showing inflection points that identify segment boundaries
</figcaption>
</figure>
<p>Another cool thing about this algorithm is that it remains robust to common time series artifcats and transformations such as downsampling, smoothing, and linear interpolation.</p>
<p>Looking at our original time series, we can see what the segments look like with a window of 30 days and 4 desired segments:</p>
<figure>
<img src="/images/mp-segment.jpg" alt="Segmented time series">
<figcaption style="color: #555; font-style: italic; text-align: center; margin-top: 8px; font-size: 0.9em;">
Segmented time series with 4 distinct regimes identified using a 30-day window
</figcaption>
</figure>
<h3 id="using-the-matrix-profile">Using the Matrix Profile</h3>
<p>I’ve implemented these algorithms in <strong>Reflect</strong> and will soon be using it to help its users analyze their own personal time series data! As I mentioned before, matrix profiles are a very helpful primitive that sets the stage for further analysis. I’m happy to have casually found this and will be diving into the huge series of papers written about this fascinating idea.</p>
<h4 id="sources">Sources</h4>
<ul>
<li><a href="https://www.cs.ucr.edu/~eamonn/MatrixProfile.html">Matrix Profile Homepage</a></li>
<li><a href="http://www.cs.ucr.edu/%7Eeamonn/Segmentation_ICDM.pdf">Domain Agnostic Online Semantic Segmentation at Superhuman Performance Levels</a></li>
<li><a href="https://matrixprofile.docs.matrixprofile.org/index.html">Matrix Profile Foundation</a></li>
<li><a href="https://stumpy.readthedocs.io">Stumpy</a></li>
</ul>]]></summary>
</entry>
<entry>
    <title>Exploring Dataflow Analysis in the Rust Compiler 
</title>
    <link href="https://anekstein.com/posts/2023-06-12.html" />
    <id>https://anekstein.com/posts/2023-06-12.html</id>
    <published>2023-06-12T00:00:00Z</published>
    <updated>2023-06-12T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>Recently I’ve been working in static analysis land and as a part of that have been familiarizing myself with data flow analysis. I look at a fair amount of MIR and so decided to delve into the <code>rustc_mir_dataflow</code> crate to see how these things are handled in the rust compiler. There is a helpful introduction to this topic in the <a href="https://rustc-dev-guide.rust-lang.org/mir/dataflow.html"><code>rustc dev guide</code></a>, and this post fleshes things out a bit.</p>
<h2 id="dataflow-analysis">Dataflow Analysis</h2>
<p>Briefly, dataflow analysis helps one understand the way data flows throughout a program. There are intraprocedural and interprocedural approaches. Some types of applications dataflow analysis include:</p>
<ol type="1">
<li>constant propagation / folding: for constant variables, replacing uses of those variables, and expressions containing those variables, with constants</li>
<li>live variable analysis: determining when a variable may be used in the future – useful for identifying dead code</li>
<li>available expression analysis: determining which sets of expressions need not be recomputed</li>
<li>reaching analysis: determining which definitions may reach a given point</li>
</ol>
<p>Dataflow analysis is commonly performed on a control flow graph (CFG). Rust’s MIR representation is basically a control flow graph structure, and so <code>rustc_mir_dataflow</code> employs common analysis algorithms to determine some of your program’s properties.</p>
<p>Dataflow analysis on CFG’s is usually performed using fixed point iteration:</p>
<ol type="1">
<li>initialize some start state associated with the entry point of the CFG</li>
<li>for each node in the CFG, compute an input state using a <code>meet</code> (or <code>join</code>) function to combine incoming states from predecessor nodes and computing an output state using a <code>transfer</code> function, applied to the input state</li>
<li>once all states of all nodes are unchanged (a fixed point has been reached), terminate</li>
</ol>
<p>Once iteration has terminated, now you have a mapping from each node to its input and output states (also called facts) describing the analysis results at each point in the program.</p>
<p>Some examples of states:</p>
<ol type="1">
<li>which variables are live?</li>
<li>which expressions are available and precomputed?</li>
<li>what definitions have established, constant values?</li>
</ol>
<p>Some dataflow analysis problems lend themselves to efficient transfer function representations, and a technique called gen-kill analysis may be utilized in these cases.</p>
<p>Whereas before input and output were defined with:</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>input <span class="op">=</span> meet([facts[predecessor] <span class="cf">for</span> predecessor <span class="kw">in</span> node<span class="op">.</span>preds])</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a>output <span class="op">=</span> transfer(input)</span></code></pre></div>
<p>Gen-kill problems define input and output as:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a>input <span class="op">=</span> <span class="kw">union</span>(facts[predecessor] <span class="cf">for</span> predecessor <span class="kw">in</span> node<span class="op">.</span>preds)</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>output <span class="op">=</span> <span class="kw">union</span>((input <span class="op">-</span> kill[current_node])<span class="op">,</span> gen[current_node])</span></code></pre></div>
<p>where <code>kill</code> and <code>gen</code> map to the portions of state that are removed and added at a particular point in the flow. For example, in live variable analysis <code>gen[statement]</code> would describe the set of variables that are used at <code>statement</code> and <code>kill[statement]</code> would describe the set of variables defined at <code>statement</code>.</p>
<p>Let’s take a look at what is perhaps the simplest instance of a gen-kill analysis in the rust compiler – determining <a href="https://github.com/rust-lang/rust/blob/df77afbcaf3365a32066a8ca4a00ae6fc9a69647/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs#L83"><code>MaybeStorageDead</code></a>. <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/enum.StatementKind.html#variant.StorageLive"><code>StorageDead</code></a> describes when a variable can have its memory freed. Knowing when a variable is dead is important for the compiler to know so that it may avoid using memory that is unallocated.</p>
<p>First, <code>MaybeStorageDead</code> keeps track of the variables that are always live; this includes the return value of a function and all of its arguments:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="at">#[</span>derive<span class="at">(</span><span class="bu">Clone</span><span class="at">)]</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">struct</span> MaybeStorageDead <span class="op">{</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>	always_live_locals<span class="op">:</span> BitSet<span class="op">&lt;</span>Local<span class="op">&gt;,</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span> MaybeStorageDead <span class="op">{</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>	<span class="kw">pub</span> <span class="kw">fn</span> new(always_live_locals<span class="op">:</span> BitSet<span class="op">&lt;</span>Local<span class="op">&gt;</span>) <span class="op">-&gt;</span> <span class="dt">Self</span> <span class="op">{</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>		MaybeStorageDead <span class="op">{</span> always_live_locals <span class="op">}</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>	<span class="op">}</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>A <code>BitSet</code> is used as it is a space-efficient and performant representation of the state (or domain) of the analysis problem. Bit sets and bit vectors are common ways to represent the domain in gen-kill analysis problems.</p>
<p>Next, <code>MaybeStorageDead</code> implements the <a href="https://github.com/rust-lang/rust/blob/df77afbcaf3365a32066a8ca4a00ae6fc9a69647/compiler/rustc_mir_dataflow/src/framework/mod.rs#L103"><code>AnalysisDomain</code></a> trait, which defines what the starting state value is for the analysis problem. In this case, it’s an empty bit set (all zeros) where each bit represents one of the locals in the MIR body. The trait also requires one to define mutations to the state upon entry to the starting block. This flips the bits representing every non-argument and non-return value to 1 – they’re presumed-dead by default:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span><span class="op">&lt;</span><span class="ot">&#39;tcx</span><span class="op">&gt;</span> <span class="kw">crate</span><span class="pp">::</span>AnalysisDomain<span class="op">&lt;</span><span class="ot">&#39;tcx</span><span class="op">&gt;</span> <span class="cf">for</span> MaybeStorageDead <span class="op">{</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>	<span class="kw">type</span> Domain <span class="op">=</span> BitSet<span class="op">&lt;</span>Local<span class="op">&gt;;</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>	<span class="kw">const</span> NAME<span class="op">:</span> <span class="op">&amp;</span><span class="ot">&#39;static</span> <span class="dt">str</span> <span class="op">=</span> <span class="st">&quot;maybe_storage_dead&quot;</span><span class="op">;</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>	<span class="kw">fn</span> bottom_value(<span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> body<span class="op">:</span> <span class="op">&amp;</span><span class="pp">mir::</span>Body<span class="op">&lt;</span><span class="ot">&#39;tcx</span><span class="op">&gt;</span>) <span class="op">-&gt;</span> <span class="dt">Self</span><span class="pp">::</span>Domain <span class="op">{</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>		<span class="co">// bottom = live</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>		<span class="pp">BitSet::</span>new_empty(body<span class="op">.</span>local_decls<span class="op">.</span>len())</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>	<span class="op">}</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>	<span class="kw">fn</span> initialize_start_block(</span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>        <span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span> body<span class="op">:</span> <span class="op">&amp;</span><span class="pp">mir::</span>Body<span class="op">&lt;</span><span class="ot">&#39;tcx</span><span class="op">&gt;,</span> on_entry<span class="op">:</span> <span class="op">&amp;</span><span class="kw">mut</span> <span class="dt">Self</span><span class="pp">::</span>Domain) <span class="op">{</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>		<span class="pp">assert_eq!</span>(</span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a>            body<span class="op">.</span>local_decls<span class="op">.</span>len()<span class="op">,</span> </span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a>            <span class="kw">self</span><span class="op">.</span>always_live_locals<span class="op">.</span>domain_size()</span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a>        )<span class="op">;</span></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a>		<span class="co">// Do not iterate on return place and args,</span></span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a>                <span class="co">// as they are trivially always live.</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a>		<span class="cf">for</span> local <span class="kw">in</span> body<span class="op">.</span>vars_and_temps_iter() <span class="op">{</span></span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a>			<span class="cf">if</span> <span class="op">!</span><span class="kw">self</span><span class="op">.</span>always_live_locals<span class="op">.</span>contains(local) <span class="op">{</span></span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a>				on_entry<span class="op">.</span>insert(local)<span class="op">;</span></span>
<span id="cb4-22"><a href="#cb4-22" aria-hidden="true" tabindex="-1"></a>			<span class="op">}</span></span>
<span id="cb4-23"><a href="#cb4-23" aria-hidden="true" tabindex="-1"></a>		<span class="op">}</span></span>
<span id="cb4-24"><a href="#cb4-24" aria-hidden="true" tabindex="-1"></a>	<span class="op">}</span></span>
<span id="cb4-25"><a href="#cb4-25" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Next, it’s time to implement the necessary functions for the <a href="https://github.com/rust-lang/rust/blob/df77afbcaf3365a32066a8ca4a00ae6fc9a69647/compiler/rustc_mir_dataflow/src/framework/mod.rs#L295"><code>GenKillAnalysis</code></a> trait. This involves defining the effects of particular statements, terminators, and calls. Some of these methods have default implementations.</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span><span class="op">&lt;</span><span class="ot">&#39;tcx</span><span class="op">&gt;</span> <span class="kw">crate</span><span class="pp">::</span>GenKillAnalysis<span class="op">&lt;</span><span class="ot">&#39;tcx</span><span class="op">&gt;</span> <span class="cf">for</span> MaybeStorageDead <span class="op">{</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">type</span> Idx <span class="op">=</span> Local<span class="op">;</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    <span class="kw">fn</span> statement_effect(</span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>        <span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>        trans<span class="op">:</span> <span class="op">&amp;</span><span class="kw">mut</span> <span class="kw">impl</span> GenKill<span class="op">&lt;</span><span class="dt">Self</span><span class="pp">::</span>Idx<span class="op">&gt;,</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>        stmt<span class="op">:</span> <span class="op">&amp;</span><span class="pp">mir::</span>Statement<span class="op">&lt;</span><span class="ot">&#39;tcx</span><span class="op">&gt;,</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>        _<span class="op">:</span> Location<span class="op">,</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>    ) <span class="op">{</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>        <span class="cf">match</span> stmt<span class="op">.</span>kind <span class="op">{</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>            <span class="pp">StatementKind::</span>StorageLive(l) <span class="op">=&gt;</span> trans<span class="op">.</span>kill(l)<span class="op">,</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>            <span class="pp">StatementKind::</span>StorageDead(l) <span class="op">=&gt;</span> trans<span class="op">.</span>gen(l)<span class="op">,</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>            _ <span class="op">=&gt;</span> ()<span class="op">,</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a>    <span class="kw">fn</span> terminator_effect(</span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a>        <span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a>        _trans<span class="op">:</span> <span class="op">&amp;</span><span class="kw">mut</span> <span class="kw">impl</span> GenKill<span class="op">&lt;</span><span class="dt">Self</span><span class="pp">::</span>Idx<span class="op">&gt;,</span></span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a>        _<span class="op">:</span> <span class="op">&amp;</span><span class="pp">mir::</span>Terminator<span class="op">&lt;</span><span class="ot">&#39;tcx</span><span class="op">&gt;,</span></span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a>        _<span class="op">:</span> Location<span class="op">,</span></span>
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true" tabindex="-1"></a>    ) <span class="op">{</span></span>
<span id="cb5-23"><a href="#cb5-23" aria-hidden="true" tabindex="-1"></a>        <span class="co">// Terminators have no effect</span></span>
<span id="cb5-24"><a href="#cb5-24" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb5-25"><a href="#cb5-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-26"><a href="#cb5-26" aria-hidden="true" tabindex="-1"></a>    <span class="kw">fn</span> call_return_effect(</span>
<span id="cb5-27"><a href="#cb5-27" aria-hidden="true" tabindex="-1"></a>        <span class="op">&amp;</span><span class="kw">self</span><span class="op">,</span></span>
<span id="cb5-28"><a href="#cb5-28" aria-hidden="true" tabindex="-1"></a>        _trans<span class="op">:</span> <span class="op">&amp;</span><span class="kw">mut</span> <span class="kw">impl</span> GenKill<span class="op">&lt;</span><span class="dt">Self</span><span class="pp">::</span>Idx<span class="op">&gt;,</span></span>
<span id="cb5-29"><a href="#cb5-29" aria-hidden="true" tabindex="-1"></a>        _block<span class="op">:</span> BasicBlock<span class="op">,</span></span>
<span id="cb5-30"><a href="#cb5-30" aria-hidden="true" tabindex="-1"></a>        _return_places<span class="op">:</span> CallReturnPlaces<span class="op">&lt;</span><span class="ot">&#39;_</span><span class="op">,</span> <span class="ot">&#39;tcx</span><span class="op">&gt;,</span></span>
<span id="cb5-31"><a href="#cb5-31" aria-hidden="true" tabindex="-1"></a>    ) <span class="op">{</span></span>
<span id="cb5-32"><a href="#cb5-32" aria-hidden="true" tabindex="-1"></a>        <span class="co">// Nothing to do when a call returns successfully</span></span>
<span id="cb5-33"><a href="#cb5-33" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb5-34"><a href="#cb5-34" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Here, the only interesting part is in <code>statement_effect</code>. When a variable becomes live, it’s added to the kill set. When it becomes dead, it’s added to the gen set.</p>
<p>And that’s all there is to it. Now we can take an arbitrary program and run analysis on it, and inspect the state at each program point. Some of these comments originate from the compiler code:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="co">// The set of locals in a MIR body that do not have </span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="co">// `StorageLive`/`StorageDead` annotations.</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="co">// These locals have fixed storage for the duration of the body.</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a><span class="co">// This is a bit set with all locals that don&#39;t have an explicit `StorageLive`</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a><span class="co">// or `StorageLocal` set removed from the bit set (i.e. always live, or 0)</span></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> always_live_locals <span class="op">=</span> always_storage_live_locals(<span class="op">&amp;</span>mir)<span class="op">;</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> <span class="kw">mut</span> maybe_dead <span class="op">=</span> <span class="pp">MaybeStorageDead::</span>new(always_live_locals)</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>    <span class="op">.</span>into_engine(tcx<span class="op">,</span> <span class="op">&amp;</span>mir)</span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>    <span class="op">.</span>iterate_to_fixpoint()</span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>    <span class="op">.</span>into_results_cursor(<span class="op">&amp;</span>mir)<span class="op">;</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> (bb<span class="op">,</span> _block) <span class="kw">in</span> mir<span class="op">.</span>basic_blocks<span class="op">.</span>iter_enumerated() <span class="op">{</span></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a>    maybe_dead<span class="op">.</span>seek_after_primary_effect(mir<span class="op">.</span>terminator_loc(bb))<span class="op">;</span></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> state <span class="op">=</span> maybe_dead<span class="op">.</span>get()<span class="op">;</span></span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a>    <span class="pp">println!</span>(<span class="st">&quot;maybe dead: {:?}&quot;</span><span class="op">,</span> state)<span class="op">;</span></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Let’s test this on a contrived program:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> test() <span class="op">{</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> <span class="kw">mut</span> x <span class="op">=</span> <span class="dv">2</span><span class="op">;</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> y <span class="op">=</span> <span class="op">&amp;</span><span class="kw">mut</span> x<span class="op">;</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>    <span class="cf">if</span> <span class="op">*</span>y <span class="op">&lt;</span> <span class="dv">10</span> <span class="op">{</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>        <span class="op">*</span>y <span class="op">=</span> <span class="op">*</span>y <span class="op">+</span> <span class="dv">1</span><span class="op">;</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a>    <span class="op">*</span>y <span class="op">=</span> <span class="dv">3</span><span class="op">;</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Let’s look at the MIR for this program block by block, along with the results of our dataflow analysis:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> test() <span class="op">-&gt;</span> () <span class="op">{</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a>	<span class="co">// Live (_0 is always the MIR return value, and is always live)</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> <span class="kw">mut</span> _0<span class="op">:</span> ()<span class="op">;</span> </span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>	<span class="co">// Presumed dead to start (via `initialize_start_block`)</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> <span class="kw">mut</span> _1<span class="op">:</span> <span class="dt">i32</span><span class="op">;</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> _3<span class="op">:</span> ()<span class="op">;</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> <span class="kw">mut</span> _4<span class="op">:</span> <span class="dt">bool</span><span class="op">;</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> <span class="kw">mut</span> _5<span class="op">:</span> <span class="dt">i32</span><span class="op">;</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> <span class="kw">mut</span> _6<span class="op">:</span> <span class="dt">i32</span><span class="op">;</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a>    <span class="co">// Will have no associated `StorageDead` or `StorageLive` annotation,</span></span>
<span id="cb8-13"><a href="#cb8-13" aria-hidden="true" tabindex="-1"></a>    <span class="co">// so always presumed live</span></span>
<span id="cb8-14"><a href="#cb8-14" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> <span class="kw">mut</span> _7<span class="op">:</span> (<span class="dt">i32</span><span class="op">,</span> <span class="dt">bool</span>)<span class="op">;</span></span>
<span id="cb8-15"><a href="#cb8-15" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb8-16"><a href="#cb8-16" aria-hidden="true" tabindex="-1"></a>    scope <span class="dv">1</span> <span class="op">{</span></span>
<span id="cb8-17"><a href="#cb8-17" aria-hidden="true" tabindex="-1"></a>        debug x <span class="op">=&gt;</span> _1<span class="op">;</span></span>
<span id="cb8-18"><a href="#cb8-18" aria-hidden="true" tabindex="-1"></a>	    <span class="co">// Presumed dead to start (via `initialize_start_block`)</span></span>
<span id="cb8-19"><a href="#cb8-19" aria-hidden="true" tabindex="-1"></a>        <span class="kw">let</span> _2<span class="op">:</span> <span class="op">&amp;</span><span class="kw">mut</span> <span class="dt">i32</span><span class="op">;</span></span>
<span id="cb8-20"><a href="#cb8-20" aria-hidden="true" tabindex="-1"></a>        scope <span class="dv">2</span> <span class="op">{</span></span>
<span id="cb8-21"><a href="#cb8-21" aria-hidden="true" tabindex="-1"></a>            debug y <span class="op">=&gt;</span> _2<span class="op">;</span></span>
<span id="cb8-22"><a href="#cb8-22" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb8-23"><a href="#cb8-23" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb8-24"><a href="#cb8-24" aria-hidden="true" tabindex="-1"></a>    <span class="co">// ...</span></span></code></pre></div>
<p>This is the set of local declarations for the program. A combination of always-live locals and presumed-dead locals yield the state <code>[_1, _2, _3, _4, _5, _6]</code> when entering <code>bb0</code>.</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>bb0<span class="op">:</span> <span class="op">{</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>	StorageLive(_1)<span class="op">;</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>	_1 <span class="op">=</span> <span class="kw">const</span> <span class="dv">2_i32</span><span class="op">;</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>	FakeRead(ForLet(<span class="cn">None</span>)<span class="op">,</span> _1)<span class="op">;</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>	StorageLive(_2)<span class="op">;</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a>	_2 <span class="op">=</span> <span class="op">&amp;</span><span class="kw">mut</span> _1<span class="op">;</span></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a>	FakeRead(ForLet(<span class="cn">None</span>)<span class="op">,</span> _2)<span class="op">;</span></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a>	StorageLive(_3)<span class="op">;</span></span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a>	StorageLive(_4)<span class="op">;</span></span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a>	StorageLive(_5)<span class="op">;</span></span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a>	_5 <span class="op">=</span> (<span class="op">*</span>_2)<span class="op">;</span></span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a>	_4 <span class="op">=</span> Lt(<span class="kw">move</span> _5<span class="op">,</span> <span class="kw">const</span> <span class="dv">10_i32</span>)<span class="op">;</span></span>
<span id="cb9-13"><a href="#cb9-13" aria-hidden="true" tabindex="-1"></a>	StorageDead(_5)<span class="op">;</span></span>
<span id="cb9-14"><a href="#cb9-14" aria-hidden="true" tabindex="-1"></a>	switchInt(<span class="kw">move</span> _4) <span class="op">-&gt;</span> [<span class="dv">0</span><span class="op">:</span> bb2<span class="op">,</span> otherwise<span class="op">:</span> bb1]<span class="op">;</span></span>
<span id="cb9-15"><a href="#cb9-15" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb9-16"><a href="#cb9-16" aria-hidden="true" tabindex="-1"></a><span class="co">// bb0 (output): [_5, _6]</span></span></code></pre></div>
<p>Here we can see that all previously-presumed dead locals except <code>_5</code> and <code>_6</code> have their own <code>StorageLive</code> statement not paired by a <code>StorageDead</code> statement, and so upon leaving this block only those locals are presumed dead (after having been added to the kill set).</p>
<p>In one branch of the <code>bb0</code> control flow (<code>bb1</code>), <code>_6</code> becomes live, and so is part of the kill set and removed from the state:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a>bb1<span class="op">:</span> <span class="op">{</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>	StorageLive(_6)<span class="op">;</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>	_6 <span class="op">=</span> (<span class="op">*</span>_2)<span class="op">;</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>	_7 <span class="op">=</span> CheckedAdd(_6<span class="op">,</span> <span class="kw">const</span> <span class="dv">1_i32</span>)<span class="op">;</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>	assert(</span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>            <span class="op">!</span><span class="kw">move</span> (_7<span class="op">.</span><span class="dv">1</span><span class="op">:</span> <span class="dt">bool</span>)<span class="op">,</span> </span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a>            <span class="st">&quot;attempt to compute `{} + {}`, which would overflow&quot;</span><span class="op">,</span> </span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>            <span class="kw">move</span> _6<span class="op">,</span> <span class="kw">const</span> <span class="dv">1_i32</span>) <span class="op">-&gt;</span> [success<span class="op">:</span> bb3<span class="op">,</span> unwind<span class="op">:</span> bb6]<span class="op">;</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a><span class="co">// bb1 (output): [_5]</span></span></code></pre></div>
<p>but in the other branch (<code>bb2</code>), it does not become live, and so remains in the set of locals presumed dead:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a>bb2<span class="op">:</span> <span class="op">{</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a> goto <span class="op">-&gt;</span> bb4<span class="op">;</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a><span class="co">// bb2 (output): [_5, _6]</span></span></code></pre></div>
<p>Coming from <code>bb1</code> where <code>_6</code> became live, it now becomes dead in <code>bb3</code> and so is added to the <code>gen</code> set and the state leaving that block:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a>bb3<span class="op">:</span> <span class="op">{</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>	(<span class="op">*</span>_2) <span class="op">=</span> <span class="kw">move</span> (_7<span class="op">.</span><span class="dv">0</span><span class="op">:</span> <span class="dt">i32</span>)<span class="op">;</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>	StorageDead(_6)<span class="op">;</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>	_3 <span class="op">=</span> <span class="kw">const</span> ()<span class="op">;</span></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a>	goto <span class="op">-&gt;</span> bb5<span class="op">;</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a><span class="co">// bb3 (output): [_5, _6]</span></span></code></pre></div>
<p>Coming from <code>bb2</code>, nothing changes in <code>bb4</code> because no variables are declared live or dead:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a>bb4<span class="op">:</span> <span class="op">{</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>	_3 <span class="op">=</span> <span class="kw">const</span> ()<span class="op">;</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>	goto <span class="op">-&gt;</span> bb5<span class="op">;</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a><span class="co">// bb4 (output): [_5, _6]</span></span></code></pre></div>
<p>Now, at the end of the function, all of the remaining variables <code>_1</code> through <code>_4</code> are declared dead and so are added to the gen set and outgoing state:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a>bb5<span class="op">:</span> <span class="op">{</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>	StorageDead(_4)<span class="op">;</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>	StorageDead(_3)<span class="op">;</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>	(<span class="op">*</span>_2) <span class="op">=</span> <span class="kw">const</span> <span class="dv">3_i32</span><span class="op">;</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a>	_0 <span class="op">=</span> <span class="kw">const</span> ()<span class="op">;</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a>	StorageDead(_2)<span class="op">;</span></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a>	StorageDead(_1)<span class="op">;</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a>	<span class="cf">return</span><span class="op">;</span></span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a><span class="co">// bb5 (output): [_1, _2, _3, _4, _5, _6]</span></span></code></pre></div>
<p>Finally, if the checked addition in <code>bb1</code> fails (where the output state is <code>[_5]</code>), <code>bb6</code> is entered where no variables are declared live are dead, and so the state remains unchanged:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a>bb6 (cleanup)<span class="op">:</span> <span class="op">{</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a>	resume<span class="op">;</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a><span class="co">// bb6 (output): [_5]</span></span></code></pre></div>
<p>I’ve skipped over a bunch of the terminology. Those are covered in depth in compiler courses that touch upon dataflow analysis; you can search for terms like bottom, lattice, semilattice, joins, transfer functions, and more. The <a href="https://en.wikipedia.org/wiki/Data-flow_analysis">Wikipedia</a> page on the topic is a decent place to dig into more.</p>
<p>As a way of wrapping up, I wanted to give a list of where else the Rust compiler utilizes this form of dataflow analysis.</p>
<ol type="1">
<li><a href="https://github.com/rust-lang/rust/blob/df77afbcaf3365a32066a8ca4a00ae6fc9a69647/compiler/rustc_borrowck/src/dataflow.rs#LL342C20-L342C20">Tracking the flow of borrows</a></li>
<li><a href="https://github.com/rust-lang/rust/blob/df77afbcaf3365a32066a8ca4a00ae6fc9a69647/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs#L36">Tracking whether a reference or pointer could point to a given local</a></li>
<li><a href="https://github.com/rust-lang/rust/blob/df77afbcaf3365a32066a8ca4a00ae6fc9a69647/compiler/rustc_mir_dataflow/src/impls/liveness.rs#L43">Liveness analysis</a></li>
<li><a href="https://github.com/rust-lang/rust/blob/df77afbcaf3365a32066a8ca4a00ae6fc9a69647/compiler/rustc_mir_dataflow/src/impls/mod.rs#L305">Possibly-initialized variables</a></li>
<li><a href="https://github.com/rust-lang/rust/blob/df77afbcaf3365a32066a8ca4a00ae6fc9a69647/compiler/rustc_mir_dataflow/src/impls/mod.rs#L441">Possibly-uninitialized variables</a></li>
<li><a href="https://github.com/rust-lang/rust/blob/df77afbcaf3365a32066a8ca4a00ae6fc9a69647/compiler/rustc_mir_dataflow/src/impls/mod.rs#L561">Definitely-initialized variables</a></li>
<li><a href="https://github.com/rust-lang/rust/blob/df77afbcaf3365a32066a8ca4a00ae6fc9a69647/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs#L189">Possibly-requiring storage</a></li>
</ol>
<p>In addition to this (incomplete) list of gen-kill problems, the rust compiler also has some analyses modeled using the more generic albeit less-performant <a href="https://github.com/rust-lang/rust/blob/df77afbcaf3365a32066a8ca4a00ae6fc9a69647/compiler/rustc_mir_dataflow/src/framework/mod.rs#L146"><code>Analysis</code></a> trait. For example, as of writing this post, the latest version of constant propagation, <a href="https://github.com/rust-lang/rust/blob/df77afbcaf3365a32066a8ca4a00ae6fc9a69647/compiler/rustc_mir_transform/src/dataflow_const_prop.rs#L77"><code>ConstAnalysis</code></a>, is built on top of <a href="https://github.com/rust-lang/rust/blob/df77afbcaf3365a32066a8ca4a00ae6fc9a69647/compiler/rustc_mir_dataflow/src/value_analysis.rs#L341"><code>ValueAnalysis</code></a> (something surprising to me is that this isn’t also set up as a gen-kill analysis, particularly the portion formulating <a href="https://github.com/rust-lang/rust/blob/df77afbcaf3365a32066a8ca4a00ae6fc9a69647/compiler/rustc_mir_dataflow/src/value_analysis.rs#L441">reachability</a>, which is a go-to use-case for gen-kill analysis. Please let me know why, if you know).</p>
<p><em>The code for the post is available <a href="https://github.com/oinoom/rustc-dataflow-example">here</a>.</em></p>]]></summary>
</entry>
<entry>
    <title>Generic Recursion Applied to Algebraic Graphs
</title>
    <link href="https://anekstein.com/posts/2022-07-31.html" />
    <id>https://anekstein.com/posts/2022-07-31.html</id>
    <published>2022-07-31T00:00:00Z</published>
    <updated>2022-07-31T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>It would be such a shame if I couldn’t combine Rust, recursion schemes, and graphs into one blog post (with the added bonus of leaving out C++). So here we go!</p>
<p>Just recently, <a href="https://recursion.wtf/posts/rust_schemes/">two</a> <a href="https://recursion.wtf/posts/rust_schemes_2/">articles</a> have surfaced describing generic recursion in Rust. I recommend reading them. There will likely be further posts in the series, but I didn’t want to wait to at least try out the basics
of the <a href="https://docs.rs/recursion/0.1.0/recursion/index.html"><code>recursion</code></a> crate. Thinking of what recursive data structures could be tried, I decided to start by prototyping a Rust adaptation of <a href="https://github.com/snowleopard/alga"><code>alga</code></a>, a Haskell library for algebraic graphs.</p>
<h2 id="representation">Representation</h2>
<p><em>small aside: I’m focusing on <code>alga</code>’s
<a href="https://hackage.haskell.org/package/algebraic-graphs-0.7/docs/Algebra-Graph.html#t:Graph"><code>Graph</code></a>
type specifically, not the
<a href="https://hackage.haskell.org/package/algebraic-graphs-0.7/docs/Algebra-Graph-Class.html#t:Graph"><code>Graph</code> <code>typeclass</code></a>
it defines, of which many more-efficient representations are made an instance, because it was the simplest representation
I could use to play with the <code>recursion</code> crate</em></p>
<p>Below we have an adapted form of the datatype introduced in the paper <a href="https://github.com/snowleopard/alga-paper"><em>Algebraic Graphs with Class</em></a>.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="at">#[</span>derive<span class="at">(</span><span class="bu">Debug</span><span class="op">,</span> <span class="bu">Clone</span><span class="at">)]</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">enum</span> RGraph<span class="op">&lt;</span>Val<span class="op">,</span> A<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>    Empty<span class="op">,</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>    Vertex(Val)<span class="op">,</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>    Overlay(A<span class="op">,</span> A)<span class="op">,</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>    Connect(A<span class="op">,</span> A)<span class="op">,</span></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>A <code>Graph</code> can be one of:</p>
<ol type="1">
<li><p><code>Empty</code>: no vertices, no edges</p></li>
<li><p><code>Vertex</code>: a single vertex of type <code>Val</code></p></li>
<li><p><code>Overlay</code>: a graph built by taking the union of both sets of vertices, and both sets of edges <span class="math inline">(<em>V</em><sub>1</sub> ∪ <em>V</em><sub>2</sub>, <em>E</em><sub>1</sub> ∪ <em>E</em><sub>2</sub>)</span></p></li>
<li><p><code>Connect</code>: a graph constructed by connecting the edges of the two graphs. This is done by unioning in the same way as <code>Overlay</code>, but additionally unioning the resulting edge set with the cross-product of both vertex sets <span class="math inline">(<em>V</em><sub>1</sub> ∪ <em>V</em><sub>2</sub>, <em>E</em><sub>1</sub> ∪ <em>E</em><sub>2</sub> ∪ <em>V</em><sub>1</sub> × <em>V</em><sub>2</sub>)</span></p></li>
</ol>
<p>For further detail, I encourage you to check out the paper and library.</p>
<h2 id="functors-via-the-recursion-crate">Functors via the <code>recursion</code> crate</h2>
<p>With the recursive definition out of the way, let’s make use of the <code>recursion</code> crate’s <code>RecursionTree</code> to make <code>RGraph</code> a functor, wrapping the type with a struct <code>Graph</code>:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">recursion::map_layer::</span>MapLayer<span class="op">;</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">recursion::recursive_tree::arena_eval::</span>ArenaIndex<span class="op">;</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">use</span> <span class="pp">recursion::recursive_tree::</span>RecursiveTree<span class="op">;</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">type</span> RecursiveGraph<span class="op">&lt;</span>V<span class="op">&gt;</span> <span class="op">=</span> RecursiveTree<span class="op">&lt;</span>RGraph<span class="op">&lt;</span>V<span class="op">,</span> ArenaIndex<span class="op">&gt;,</span> ArenaIndex<span class="op">&gt;;</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span><span class="op">&lt;</span>A<span class="op">,</span> B<span class="op">,</span> V<span class="op">&gt;</span> MapLayer<span class="op">&lt;</span>B<span class="op">&gt;</span> <span class="cf">for</span> RGraph<span class="op">&lt;</span>V<span class="op">,</span> A<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">type</span> To <span class="op">=</span> RGraph<span class="op">&lt;</span>V<span class="op">,</span> B<span class="op">&gt;;</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>    <span class="kw">type</span> Unwrapped <span class="op">=</span> A<span class="op">;</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a>    <span class="kw">fn</span> map_layer<span class="op">&lt;</span>F<span class="op">:</span> <span class="bu">FnMut</span>(<span class="dt">Self</span><span class="pp">::</span>Unwrapped) <span class="op">-&gt;</span> B<span class="op">&gt;</span>(<span class="kw">self</span><span class="op">,</span> <span class="kw">mut</span> f<span class="op">:</span> F) <span class="op">-&gt;</span> <span class="dt">Self</span><span class="pp">::</span>To <span class="op">{</span></span>
<span id="cb2-12"><a href="#cb2-12" aria-hidden="true" tabindex="-1"></a>        <span class="kw">use</span> <span class="pp">RGraph::</span><span class="op">*;</span></span>
<span id="cb2-13"><a href="#cb2-13" aria-hidden="true" tabindex="-1"></a>        <span class="cf">match</span> <span class="kw">self</span> <span class="op">{</span></span>
<span id="cb2-14"><a href="#cb2-14" aria-hidden="true" tabindex="-1"></a>            Empty <span class="op">=&gt;</span> Empty<span class="op">,</span></span>
<span id="cb2-15"><a href="#cb2-15" aria-hidden="true" tabindex="-1"></a>            Vertex(v) <span class="op">=&gt;</span> Vertex(v)<span class="op">,</span></span>
<span id="cb2-16"><a href="#cb2-16" aria-hidden="true" tabindex="-1"></a>            Overlay(a<span class="op">,</span> b) <span class="op">=&gt;</span> Overlay(f(a)<span class="op">,</span> f(b))<span class="op">,</span></span>
<span id="cb2-17"><a href="#cb2-17" aria-hidden="true" tabindex="-1"></a>            Connect(a<span class="op">,</span> b) <span class="op">=&gt;</span> Connect(f(a)<span class="op">,</span> f(b))<span class="op">,</span></span>
<span id="cb2-18"><a href="#cb2-18" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb2-19"><a href="#cb2-19" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb2-20"><a href="#cb2-20" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb2-21"><a href="#cb2-21" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-22"><a href="#cb2-22" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Graph<span class="op">&lt;</span>V<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb2-23"><a href="#cb2-23" aria-hidden="true" tabindex="-1"></a>    inner<span class="op">:</span> RecursiveGraph<span class="op">&lt;</span>V<span class="op">&gt;</span></span>
<span id="cb2-24"><a href="#cb2-24" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<h2 id="constructors">Constructors</h2>
<p>With that, we can now use catamorphisms and anamorphisms to destruct and construct this datatype for our purposes, starting with
a method to construct a graph from a list of vertices:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span><span class="op">&lt;</span>V<span class="op">:</span> <span class="bu">Hash</span> <span class="op">+</span> <span class="bu">Eq</span> <span class="op">+</span> <span class="bu">Clone</span> <span class="op">+</span> <span class="bu">Debug</span><span class="op">&gt;</span> Graph<span class="op">&lt;</span>V<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    <span class="co">/// Constructs a [`Graph`] from a vector of vertices</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">pub</span> <span class="kw">fn</span> vertices(vs<span class="op">:</span> <span class="dt">Vec</span><span class="op">&lt;</span>V<span class="op">&gt;</span>) <span class="op">-&gt;</span> <span class="dt">Self</span> <span class="op">{</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>        Graph <span class="op">{</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>            inner<span class="op">:</span> <span class="pp">RecursiveGraph::</span>expand_layers(vs<span class="op">,</span> <span class="op">|</span><span class="kw">mut</span> remaining<span class="op">|</span> <span class="op">{</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>                <span class="kw">use</span> <span class="pp">RGraph::</span><span class="op">*;</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>                <span class="cf">match</span> remaining<span class="op">.</span>len() <span class="op">{</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>                    <span class="dv">0</span> <span class="op">=&gt;</span> Empty<span class="op">,</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>                    <span class="dv">1</span> <span class="op">=&gt;</span> Vertex(remaining<span class="op">.</span>pop()<span class="op">.</span>unwrap())<span class="op">,</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>                    _ <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>                        <span class="kw">let</span> ending_half <span class="op">=</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>                            remaining<span class="op">.</span>split_off(remaining<span class="op">.</span>len() <span class="op">/</span> <span class="dv">2</span>)<span class="op">;</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>                        Overlay(remaining<span class="op">,</span> ending_half)</span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a>                    <span class="op">}</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a>                <span class="op">}</span></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span>)<span class="op">,</span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a>    <span class="op">...</span></span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>This function isn’t terribly interesting on its own, it doesn’t add any edges to the graph and is not
suited for adding vertices to an already-existing graph, but it is one of the primitive builders provided
by <code>alga</code>. We can see that this anamorphism splits the list of vertices in half and overlays them.</p>
<p>We can do something very similar in order to construct a clique (which <code>alga</code> also defines), simply
by using <code>Connect</code> instead of <code>Overlay</code>:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>    <span class="co">/// Constructs fully connected graph</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">pub</span> <span class="kw">fn</span> clique(vs<span class="op">:</span> <span class="dt">Vec</span><span class="op">&lt;</span>V<span class="op">&gt;</span>) <span class="op">-&gt;</span> Graph<span class="op">&lt;</span>V<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>        Graph <span class="op">{</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>            inner<span class="op">:</span> <span class="pp">RecursiveGraph::</span>expand_layers(vs<span class="op">,</span> <span class="op">|</span><span class="kw">mut</span> remaining<span class="op">|</span> <span class="op">{</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>                <span class="kw">use</span> <span class="pp">RGraph::</span><span class="op">*;</span></span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>                <span class="cf">match</span> remaining<span class="op">.</span>len() <span class="op">{</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>                    <span class="dv">0</span> <span class="op">=&gt;</span> Empty<span class="op">,</span></span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>                    <span class="dv">1</span> <span class="op">=&gt;</span> Vertex(remaining<span class="op">.</span>pop()<span class="op">.</span>unwrap())<span class="op">,</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>                    _ <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>                        <span class="kw">let</span> ending_half <span class="op">=</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>                            remaining<span class="op">.</span>split_off(remaining<span class="op">.</span>len() <span class="op">/</span> <span class="dv">2</span>)<span class="op">;</span></span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>                        Connect(remaining<span class="op">,</span> ending_half)</span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>                    <span class="op">}</span></span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a>                <span class="op">}</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span>)<span class="op">,</span></span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span></code></pre></div>
<h2 id="destructors">Destructors</h2>
<p>To ensure that <code>vertices</code> does the thing it’s supposed to, we can add a method to count the number of vertices in the graph,
also known as the graph’s order:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a>    <span class="co">/// Gets the number of vertices in the graph</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">pub</span> <span class="kw">fn</span> order(<span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">usize</span> <span class="op">{</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>        <span class="kw">use</span> <span class="pp">RGraph::</span><span class="op">*;</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>        <span class="kw">self</span><span class="op">.</span>inner<span class="op">.</span>collapse_layers(<span class="op">|</span>layer<span class="op">:</span> RGraph<span class="op">&lt;</span>V<span class="op">,</span> HashSet<span class="op">&lt;</span>V<span class="op">&gt;&gt;|</span> <span class="cf">match</span> layer <span class="op">{</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>            Empty <span class="op">=&gt;</span> <span class="pp">HashSet::</span>new()<span class="op">,</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>            Vertex(v) <span class="op">=&gt;</span> <span class="pp">HashSet::</span>from_iter([v])<span class="op">,</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>            Overlay(a<span class="op">,</span> b) <span class="op">=&gt;</span> set_union(a<span class="op">,</span> b)<span class="op">,</span></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>            Connect(a<span class="op">,</span> b) <span class="op">=&gt;</span> set_union(a<span class="op">,</span> b)</span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span>)<span class="op">.</span>len()</span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span></code></pre></div>
<p>Here we fold the graph layer by layer, unioning the sets of vertices together and getting
the cardinality of the set at the end.</p>
<p>As you may have guessed, there isn’t really anything in the representation above preventing one from
adding vertices and edges when they are already present. In fact, <code>alga</code> has a function to
<a href="https://hackage.haskell.org/package/algebraic-graphs-0.7/docs/Algebra-Graph.html#v:simplify"><code>simplify</code></a>
a representation by pruning redundancies. I’m unsure how often this
is needed in practice, but efficiency is not the focus of this blog post in any case.</p>
<p>Speaking of efficiency… <code>alga</code> conducts some of its queries on <a href="https://hackage.haskell.org/package/algebraic-graphs-0.7/docs/src/Algebra.Graph.html#edgeCount">temporary
structures</a>
that are more performant, such as adjacency maps. Replicated below:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> AdjacencyMap<span class="op">&lt;</span>V<span class="op">&gt;</span> <span class="op">=</span> HashMap<span class="op">&lt;</span>V<span class="op">,</span> HashSet<span class="op">&lt;</span>V<span class="op">&gt;&gt;;</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span><span class="op">&lt;</span>V<span class="op">:</span> <span class="bu">Hash</span> <span class="op">+</span> <span class="bu">Eq</span> <span class="op">+</span> <span class="bu">Clone</span> <span class="op">+</span> <span class="bu">Debug</span><span class="op">&gt;</span> Graph<span class="op">&lt;</span>V<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">...</span></span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a>    <span class="co">/// Folds a [`Graph`] to construct an adjacency map</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a>    <span class="kw">pub</span> <span class="kw">fn</span> to_adjacency_map(<span class="kw">self</span>) <span class="op">-&gt;</span> AdjacencyMap<span class="op">&lt;</span>V<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>        <span class="kw">use</span> <span class="pp">RGraph::</span><span class="op">*;</span></span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>        <span class="kw">self</span><span class="op">.</span>inner</span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>            <span class="op">.</span>collapse_layers(<span class="op">|</span>layer<span class="op">:</span> RGraph<span class="op">&lt;</span>V<span class="op">,</span> AdjacencyMap<span class="op">&lt;</span>V<span class="op">&gt;&gt;|</span> <span class="cf">match</span> layer <span class="op">{</span></span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a>                Empty <span class="op">=&gt;</span> <span class="pp">HashMap::</span>new()<span class="op">,</span></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a>                Vertex(v) <span class="op">=&gt;</span> <span class="op">{</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a>                    <span class="kw">let</span> <span class="kw">mut</span> map <span class="op">=</span> <span class="pp">HashMap::</span>new()<span class="op">;</span></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a>                    map<span class="op">.</span>insert(v<span class="op">,</span> <span class="pp">HashSet::</span>new())<span class="op">;</span></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a>                    map</span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a>                <span class="op">}</span></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a>                Overlay(a<span class="op">,</span> b) <span class="op">=&gt;</span> union_with(<span class="op">&amp;</span>a<span class="op">,</span> <span class="op">&amp;</span>b<span class="op">,</span> set_union)<span class="op">,</span></span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a>                Connect(a<span class="op">,</span> b) <span class="op">=&gt;</span> unions_with(</span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a>                    <span class="pp">vec!</span>[</span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a>                        <span class="op">&amp;</span>a<span class="op">,</span></span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a>                        <span class="op">&amp;</span>b<span class="op">,</span></span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a>                        <span class="op">&amp;</span>from_iter_with(<span class="pp">HashSet::</span><span class="op">&lt;</span>V<span class="op">&gt;</span><span class="pp">::</span>from_iter(a<span class="op">.</span>keys()<span class="op">.</span>cloned())<span class="op">,</span> </span>
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a>                            <span class="op">|</span>_<span class="op">|</span> <span class="op">{</span></span>
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a>                                <span class="co">// every `a` node gets `bs` as neighbors</span></span>
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a>                                <span class="pp">HashSet::</span>from_iter(b<span class="op">.</span>keys()<span class="op">.</span>cloned())</span>
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a>                            <span class="op">}</span>)<span class="op">,</span></span>
<span id="cb6-27"><a href="#cb6-27" aria-hidden="true" tabindex="-1"></a>                    ]<span class="op">,</span></span>
<span id="cb6-28"><a href="#cb6-28" aria-hidden="true" tabindex="-1"></a>                    set_union<span class="op">,</span></span>
<span id="cb6-29"><a href="#cb6-29" aria-hidden="true" tabindex="-1"></a>                )<span class="op">,</span></span>
<span id="cb6-30"><a href="#cb6-30" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span>)</span>
<span id="cb6-31"><a href="#cb6-31" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb6-32"><a href="#cb6-32" aria-hidden="true" tabindex="-1"></a>    <span class="op">...</span></span>
<span id="cb6-33"><a href="#cb6-33" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>In the case of an <code>Overlay</code> we combine adjacency lists by unioning the neighbors of identical nodes.
In the case of a <code>Connect</code>, the same combination is also unioned with a neighbor set representing a
fully connected subgraph of all nodes in <code>a</code> and <code>b</code>.</p>
<p>With this representation available, we can easily query the number of edges in the graph:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a>    <span class="co">/// Calculates the number of edges in the graph</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">pub</span> <span class="kw">fn</span> size(<span class="kw">self</span>) <span class="op">-&gt;</span> <span class="dt">usize</span> <span class="op">{</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>        <span class="kw">self</span><span class="op">.</span>to_adjacency_map()<span class="op">.</span>values()<span class="op">.</span>map(<span class="pp">HashSet::</span>len)<span class="op">.</span>sum()</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span></code></pre></div>
<h2 id="testing-the-implementation">Testing the implementation</h2>
<p>Finally, to test what we’ve got:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="at">#[</span>cfg<span class="at">(</span>test<span class="at">)]</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="kw">mod</span> tests <span class="op">{</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">use</span> <span class="kw">crate</span><span class="pp">::</span><span class="op">*;</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a>    <span class="at">#[</span>test<span class="at">]</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">fn</span> test_graph() <span class="op">{</span></span>
<span id="cb8-6"><a href="#cb8-6" aria-hidden="true" tabindex="-1"></a>        <span class="kw">let</span> vs <span class="op">=</span> <span class="pp">vec!</span>[<span class="dv">1</span><span class="op">,</span> <span class="dv">2</span><span class="op">,</span> <span class="dv">3</span><span class="op">,</span> <span class="dv">4</span><span class="op">,</span> <span class="dv">5</span><span class="op">,</span> <span class="dv">6</span><span class="op">,</span> <span class="dv">7</span><span class="op">,</span> <span class="dv">8</span><span class="op">,</span> <span class="dv">9</span>]<span class="op">;</span></span>
<span id="cb8-7"><a href="#cb8-7" aria-hidden="true" tabindex="-1"></a>        <span class="co">// fully connected graphs have (n)(n-1)/2 edges</span></span>
<span id="cb8-8"><a href="#cb8-8" aria-hidden="true" tabindex="-1"></a>        <span class="pp">assert_eq!</span>(<span class="pp">Graph::</span>clique(vs<span class="op">.</span>clone())<span class="op">.</span>size()<span class="op">,</span> (<span class="dv">9</span> <span class="op">*</span> <span class="dv">8</span> <span class="op">/</span> <span class="dv">2</span>))<span class="op">;</span></span>
<span id="cb8-9"><a href="#cb8-9" aria-hidden="true" tabindex="-1"></a>        <span class="pp">assert_eq!</span>(<span class="pp">Graph::</span>vertices(vs<span class="op">.</span>clone())<span class="op">.</span>size()<span class="op">,</span> <span class="dv">0</span>)<span class="op">;</span></span>
<span id="cb8-10"><a href="#cb8-10" aria-hidden="true" tabindex="-1"></a>        <span class="pp">assert_eq!</span>(<span class="pp">Graph::</span>vertices(vs<span class="op">.</span>clone())<span class="op">.</span>order()<span class="op">,</span> <span class="dv">9</span>)<span class="op">;</span></span>
<span id="cb8-11"><a href="#cb8-11" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb8-12"><a href="#cb8-12" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>That’s a decent start. Credit to Inanna Malick for making the <code>recursion</code> crate: it was fun
to play with!</p>
<h2 id="update-working-with-references-courtesy-of-inanna-malick">Update: working with references (courtesy of Inanna Malick)</h2>
<p>It would be nice if we could run multiple passes over the graph, and not have to consume it
just to do something like count its nodes. To enable this, a few tweaks have to be made. First, we introduce
a recursive definition of <code>RGraph</code> over borrowed data:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">pub</span> <span class="kw">enum</span> RGraphRef<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> Val<span class="op">,</span> A<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>    Empty<span class="op">,</span></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>    Vertex(<span class="op">&amp;</span><span class="ot">&#39;a</span> Val)<span class="op">,</span></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>    Overlay(A<span class="op">,</span> A)<span class="op">,</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>    Connect(A<span class="op">,</span> A)<span class="op">,</span></span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>This definition allows us to create an implementation of <code>MapLayer</code> that takes a recursive
type that owns data (<code>RGraph</code>), and map it to one that refers to that data (<code>RGraphRef</code>):</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">impl</span><span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> A<span class="op">:</span> <span class="bu">Copy</span> <span class="op">+</span> <span class="ot">&#39;a</span><span class="op">,</span> B<span class="op">:</span> <span class="ot">&#39;a</span><span class="op">,</span> V<span class="op">:</span> <span class="ot">&#39;a</span><span class="op">&gt;</span> MapLayer<span class="op">&lt;</span>B<span class="op">&gt;</span> <span class="cf">for</span> <span class="op">&amp;</span><span class="ot">&#39;a</span> RGraph<span class="op">&lt;</span>V<span class="op">,</span> A<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">type</span> To <span class="op">=</span> RGraphRef<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> V<span class="op">,</span> B<span class="op">&gt;;</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>    <span class="kw">type</span> Unwrapped <span class="op">=</span> A<span class="op">;</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>    <span class="kw">fn</span> map_layer<span class="op">&lt;</span>F<span class="op">:</span> <span class="bu">FnMut</span>(<span class="dt">Self</span><span class="pp">::</span>Unwrapped) <span class="op">-&gt;</span> B<span class="op">&gt;</span>(<span class="kw">self</span><span class="op">,</span> <span class="kw">mut</span> f<span class="op">:</span> F) <span class="op">-&gt;</span> <span class="dt">Self</span><span class="pp">::</span>To <span class="op">{</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>        <span class="cf">match</span> <span class="kw">self</span> <span class="op">{</span></span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a>            <span class="pp">RGraph::</span>Empty <span class="op">=&gt;</span> <span class="pp">RGraphRef::</span>Empty<span class="op">,</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>            <span class="pp">RGraph::</span>Vertex(v) <span class="op">=&gt;</span> <span class="pp">RGraphRef::</span>Vertex(v)<span class="op">,</span></span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a>            <span class="pp">RGraph::</span>Overlay(a<span class="op">,</span> b) <span class="op">=&gt;</span> <span class="pp">RGraphRef::</span>Overlay(f(<span class="op">*</span>a)<span class="op">,</span> f(<span class="op">*</span>b))<span class="op">,</span></span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a>            <span class="pp">RGraph::</span>Connect(a<span class="op">,</span> b) <span class="op">=&gt;</span> <span class="pp">RGraphRef::</span>Connect(f(<span class="op">*</span>a)<span class="op">,</span> f(<span class="op">*</span>b))<span class="op">,</span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span></span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Now, when applying our algebra to tear down this recursive structure, it can be in
terms of the referenced data. For example, let’s see how our definition of <code>order</code>
would change:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode diff"><code class="sourceCode diff"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="st">-    pub fn order(self) -&gt; usize {</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="va">+    pub fn order(&amp;self) -&gt; usize {</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>         let unique =</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a><span class="st">-            self.inner.collapse_layers(|layer: RGraph&lt;V, HashSet&lt;V&gt;&gt;| {</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a><span class="va">+            self.inner.as_ref().collapse_layers(|layer: RGraphRef&lt;V, HashSet&lt;&amp;V&gt;&gt;| {</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a><span class="st">-                use RGraph::*;</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a><span class="va">+                use RGraphRef::*;</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a>                 match layer {</span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a>                     Empty =&gt; HashSet::new(),</span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a>                     Vertex(v) =&gt; HashSet::from_iter(vec![v]),</span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a>                     Overlay(a, b) =&gt; set_union(a, b),</span>
<span id="cb11-12"><a href="#cb11-12" aria-hidden="true" tabindex="-1"></a>                     Connect(a, b) =&gt; set_union(a, b),</span>
<span id="cb11-13"><a href="#cb11-13" aria-hidden="true" tabindex="-1"></a>                 }</span>
<span id="cb11-14"><a href="#cb11-14" aria-hidden="true" tabindex="-1"></a>             });</span>
<span id="cb11-15"><a href="#cb11-15" aria-hidden="true" tabindex="-1"></a>         unique.len()</span>
<span id="cb11-16"><a href="#cb11-16" aria-hidden="true" tabindex="-1"></a>     }</span></code></pre></div>
<p>And there you have it!</p>
<h2 id="appendix">Appendix</h2>
<p>Below are the definitions of referenced helper functions:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode rust"><code class="sourceCode rust"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> set_union<span class="op">&lt;</span>V<span class="op">:</span> <span class="bu">Eq</span> <span class="op">+</span> <span class="bu">Hash</span> <span class="op">+</span> <span class="bu">Clone</span><span class="op">&gt;</span>(u<span class="op">:</span> HashSet<span class="op">&lt;</span>V<span class="op">&gt;,</span> v<span class="op">:</span> HashSet<span class="op">&lt;</span>V<span class="op">&gt;</span>) <span class="op">-&gt;</span> HashSet<span class="op">&lt;</span>V<span class="op">&gt;</span> <span class="op">{</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>    u<span class="op">.</span><span class="kw">union</span>(<span class="op">&amp;</span>v)<span class="op">.</span>cloned()<span class="op">.</span>collect()</span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a><span class="co">/// Constructs a HashMap, determines a key&#39;s values by applying</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a><span class="co">/// a given function to each respective key</span></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> from_iter_with<span class="op">&lt;</span>K<span class="op">:</span> <span class="bu">Eq</span> <span class="op">+</span> <span class="bu">Hash</span><span class="op">,</span> V<span class="op">,</span> F<span class="op">&gt;</span>(</span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>    it<span class="op">:</span> <span class="kw">impl</span> <span class="bu">IntoIterator</span><span class="op">&lt;</span>Item <span class="op">=</span> K<span class="op">&gt;,</span></span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a>    f<span class="op">:</span> F<span class="op">,</span></span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a>) <span class="op">-&gt;</span> HashMap<span class="op">&lt;</span>K<span class="op">,</span> V<span class="op">&gt;</span></span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a><span class="kw">where</span></span>
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a>    F<span class="op">:</span> <span class="bu">Fn</span>(<span class="op">&amp;</span>K) <span class="op">-&gt;</span> V<span class="op">,</span></span>
<span id="cb12-13"><a href="#cb12-13" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb12-14"><a href="#cb12-14" aria-hidden="true" tabindex="-1"></a>    it<span class="op">.</span>into_iter()<span class="op">.</span>fold(<span class="pp">HashMap::</span>new()<span class="op">,</span> <span class="op">|</span><span class="kw">mut</span> acc<span class="op">,</span> k<span class="op">|</span> <span class="op">{</span></span>
<span id="cb12-15"><a href="#cb12-15" aria-hidden="true" tabindex="-1"></a>        <span class="kw">let</span> v <span class="op">=</span> f(<span class="op">&amp;</span>k)<span class="op">;</span></span>
<span id="cb12-16"><a href="#cb12-16" aria-hidden="true" tabindex="-1"></a>        acc<span class="op">.</span>insert(k<span class="op">,</span> v)<span class="op">;</span></span>
<span id="cb12-17"><a href="#cb12-17" aria-hidden="true" tabindex="-1"></a>        acc</span>
<span id="cb12-18"><a href="#cb12-18" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>)</span>
<span id="cb12-19"><a href="#cb12-19" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb12-20"><a href="#cb12-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-21"><a href="#cb12-21" aria-hidden="true" tabindex="-1"></a><span class="co">/// Unions two HashMaps by applying a given function to</span></span>
<span id="cb12-22"><a href="#cb12-22" aria-hidden="true" tabindex="-1"></a><span class="co">/// the values of common keys</span></span>
<span id="cb12-23"><a href="#cb12-23" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> union_with<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> K<span class="op">:</span> <span class="ot">&#39;a</span> <span class="op">+</span> <span class="bu">Eq</span> <span class="op">+</span> <span class="bu">Hash</span> <span class="op">+</span> <span class="bu">Clone</span><span class="op">,</span> V<span class="op">:</span> <span class="ot">&#39;a</span> <span class="op">+</span> <span class="bu">Clone</span><span class="op">,</span> F<span class="op">&gt;</span>(</span>
<span id="cb12-24"><a href="#cb12-24" aria-hidden="true" tabindex="-1"></a>    a<span class="op">:</span> <span class="op">&amp;</span><span class="ot">&#39;a</span> HashMap<span class="op">&lt;</span>K<span class="op">,</span> V<span class="op">&gt;,</span></span>
<span id="cb12-25"><a href="#cb12-25" aria-hidden="true" tabindex="-1"></a>    b<span class="op">:</span> <span class="op">&amp;</span><span class="ot">&#39;a</span> HashMap<span class="op">&lt;</span>K<span class="op">,</span> V<span class="op">&gt;,</span></span>
<span id="cb12-26"><a href="#cb12-26" aria-hidden="true" tabindex="-1"></a>    f<span class="op">:</span> F<span class="op">,</span></span>
<span id="cb12-27"><a href="#cb12-27" aria-hidden="true" tabindex="-1"></a>) <span class="op">-&gt;</span> HashMap<span class="op">&lt;</span>K<span class="op">,</span> V<span class="op">&gt;</span></span>
<span id="cb12-28"><a href="#cb12-28" aria-hidden="true" tabindex="-1"></a><span class="kw">where</span></span>
<span id="cb12-29"><a href="#cb12-29" aria-hidden="true" tabindex="-1"></a>    F<span class="op">:</span> <span class="bu">Fn</span>(V<span class="op">,</span> V) <span class="op">-&gt;</span> V<span class="op">,</span></span>
<span id="cb12-30"><a href="#cb12-30" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb12-31"><a href="#cb12-31" aria-hidden="true" tabindex="-1"></a>    unions_with([a<span class="op">,</span> b]<span class="op">,</span> f)</span>
<span id="cb12-32"><a href="#cb12-32" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span>
<span id="cb12-33"><a href="#cb12-33" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-34"><a href="#cb12-34" aria-hidden="true" tabindex="-1"></a><span class="co">/// Unions multiple HashMaps by applying a given function to</span></span>
<span id="cb12-35"><a href="#cb12-35" aria-hidden="true" tabindex="-1"></a><span class="co">/// the values of common keys</span></span>
<span id="cb12-36"><a href="#cb12-36" aria-hidden="true" tabindex="-1"></a><span class="kw">fn</span> unions_with<span class="op">&lt;</span><span class="ot">&#39;a</span><span class="op">,</span> K<span class="op">:</span> <span class="ot">&#39;a</span> <span class="op">+</span> <span class="bu">Eq</span> <span class="op">+</span> <span class="bu">Hash</span> <span class="op">+</span> <span class="bu">Clone</span><span class="op">,</span> V<span class="op">:</span> <span class="ot">&#39;a</span> <span class="op">+</span> <span class="bu">Clone</span><span class="op">,</span> F<span class="op">&gt;</span>(</span>
<span id="cb12-37"><a href="#cb12-37" aria-hidden="true" tabindex="-1"></a>    maps<span class="op">:</span> <span class="kw">impl</span> <span class="bu">IntoIterator</span><span class="op">&lt;</span>Item <span class="op">=</span> <span class="op">&amp;</span><span class="ot">&#39;a</span> HashMap<span class="op">&lt;</span>K<span class="op">,</span> V<span class="op">&gt;&gt;,</span></span>
<span id="cb12-38"><a href="#cb12-38" aria-hidden="true" tabindex="-1"></a>    f<span class="op">:</span> F<span class="op">,</span></span>
<span id="cb12-39"><a href="#cb12-39" aria-hidden="true" tabindex="-1"></a>) <span class="op">-&gt;</span> HashMap<span class="op">&lt;</span>K<span class="op">,</span> V<span class="op">&gt;</span></span>
<span id="cb12-40"><a href="#cb12-40" aria-hidden="true" tabindex="-1"></a><span class="kw">where</span></span>
<span id="cb12-41"><a href="#cb12-41" aria-hidden="true" tabindex="-1"></a>    F<span class="op">:</span> <span class="bu">Fn</span>(V<span class="op">,</span> V) <span class="op">-&gt;</span> V<span class="op">,</span></span>
<span id="cb12-42"><a href="#cb12-42" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb12-43"><a href="#cb12-43" aria-hidden="true" tabindex="-1"></a>    maps<span class="op">.</span>into_iter()<span class="op">.</span>fold(<span class="pp">HashMap::</span>new()<span class="op">,</span> <span class="op">|</span><span class="kw">mut</span> acc<span class="op">,</span> map<span class="op">|</span> <span class="op">{</span></span>
<span id="cb12-44"><a href="#cb12-44" aria-hidden="true" tabindex="-1"></a>        <span class="cf">for</span> (k<span class="op">,</span> v) <span class="kw">in</span> map<span class="op">.</span>iter() <span class="op">{</span></span>
<span id="cb12-45"><a href="#cb12-45" aria-hidden="true" tabindex="-1"></a>            <span class="cf">if</span> <span class="kw">let</span> <span class="cn">Some</span>(u) <span class="op">=</span> acc<span class="op">.</span>remove(<span class="op">&amp;</span>k) <span class="op">{</span></span>
<span id="cb12-46"><a href="#cb12-46" aria-hidden="true" tabindex="-1"></a>                acc<span class="op">.</span>insert(k<span class="op">.</span>clone()<span class="op">,</span> f(u<span class="op">,</span> v<span class="op">.</span>clone()))<span class="op">;</span></span>
<span id="cb12-47"><a href="#cb12-47" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span> <span class="cf">else</span> <span class="op">{</span></span>
<span id="cb12-48"><a href="#cb12-48" aria-hidden="true" tabindex="-1"></a>                acc<span class="op">.</span>insert(k<span class="op">.</span>clone()<span class="op">,</span> v<span class="op">.</span>clone())<span class="op">;</span></span>
<span id="cb12-49"><a href="#cb12-49" aria-hidden="true" tabindex="-1"></a>            <span class="op">}</span></span>
<span id="cb12-50"><a href="#cb12-50" aria-hidden="true" tabindex="-1"></a>        <span class="op">}</span></span>
<span id="cb12-51"><a href="#cb12-51" aria-hidden="true" tabindex="-1"></a>        acc</span>
<span id="cb12-52"><a href="#cb12-52" aria-hidden="true" tabindex="-1"></a>    <span class="op">}</span>)</span>
<span id="cb12-53"><a href="#cb12-53" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>]]></summary>
</entry>
<entry>
    <title>Discovering I've Shot Myself in the Foot with std::async
</title>
    <link href="https://anekstein.com/posts/2022-03-04.html" />
    <id>https://anekstein.com/posts/2022-03-04.html</id>
    <published>2022-03-04T00:00:00Z</published>
    <updated>2022-03-04T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p>I was recently looking into implementing some
speculative execution functionality using <code>std::async</code>; based on some seed data,
the goal was to asynchronously run a compute-intensive calculation
where the result may or may not be needed at a later time. When new seed
data became available, it was an indication that any
in-progress computation should be terminated early and that
its result be considered defunct.</p>
<p>Below I’m going to outline a contrived example that
demonstrates the sort of trouble I ended up running into.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">enum</span> <span class="kw">class</span> CalculationProgress <span class="op">{</span> Unaborted<span class="op">,</span> Aborted <span class="op">};</span></span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a><span class="bu">std::</span>optional<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>calculate<span class="op">(</span><span class="bu">std::</span>string name<span class="op">,</span></span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>          <span class="bu">std::</span>shared_ptr<span class="op">&lt;</span><span class="bu">std::</span>atomic<span class="op">&lt;</span>CalculationProgress<span class="op">&gt;&gt;</span> abort_status<span class="op">)</span> <span class="op">{</span></span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>  <span class="at">const</span> <span class="kw">auto</span> arbitrary_computation_time <span class="op">=</span> <span class="bu">std::</span>chrono::seconds<span class="op">(</span><span class="dv">10</span><span class="op">);</span></span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>  <span class="at">const</span> <span class="kw">auto</span> arbitrary_loop_time <span class="op">=</span> <span class="bu">std::</span>chrono::seconds<span class="op">(</span><span class="dv">5</span><span class="op">);</span></span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>  <span class="at">const</span> <span class="kw">auto</span> arbitrary_max_loops <span class="op">=</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>      arbitrary_computation_time <span class="op">/</span> arbitrary_loop_time<span class="op">;</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> count <span class="op">=</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a>  <span class="dt">bool</span> was_aborted <span class="op">=</span> <span class="kw">false</span><span class="op">;</span></span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a>  <span class="cf">do</span> <span class="op">{</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a>    <span class="bu">std::</span>this_thread::sleep_for<span class="op">(</span>arbitrary_loop_time<span class="op">);</span></span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a>    count<span class="op">++;</span></span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a>    was_aborted <span class="op">=</span> <span class="op">*</span>abort_status <span class="op">==</span> CalculationProgress<span class="op">::</span>Aborted<span class="op">;</span></span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span> <span class="cf">while</span> <span class="op">(</span>count <span class="op">&lt;</span> arbitrary_max_loops <span class="op">&amp;&amp;</span> <span class="op">!</span>was_aborted<span class="op">);</span></span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-20"><a href="#cb1-20" aria-hidden="true" tabindex="-1"></a>  <span class="at">const</span> <span class="kw">auto</span> status <span class="op">=</span> was_aborted <span class="op">?</span> <span class="st">&quot;ABORTED&quot;</span> <span class="op">:</span> <span class="st">&quot;COMPLETE&quot;</span><span class="op">;</span></span>
<span id="cb1-21"><a href="#cb1-21" aria-hidden="true" tabindex="-1"></a>  <span class="bu">std::</span>cout <span class="op">&lt;&lt;</span> name <span class="op">&lt;&lt;</span> <span class="st">&quot;: &quot;</span> <span class="op">&lt;&lt;</span> status <span class="op">&lt;&lt;</span> <span class="st">&quot;, count: &quot;</span> <span class="op">&lt;&lt;</span> count <span class="op">&lt;&lt;</span> <span class="bu">std::</span>endl<span class="op">;</span></span>
<span id="cb1-22"><a href="#cb1-22" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> was_aborted <span class="op">?</span> <span class="bu">std::</span>nullopt <span class="op">:</span> <span class="bu">std::</span>optional<span class="op">(</span>count<span class="op">);</span></span>
<span id="cb1-23"><a href="#cb1-23" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>The function <code>calculate</code> above is the contrived, compute-intensive calculation. It periodically checks the condition while performing its calculation (in this case every five seconds). Depending on its value,
it either finishes its long-running calculation or gets aborted – whichever occurs first. From <code>arbitrary_loop_time</code>
in the do-while loop, it’s clear that <code>calculate</code> will have a best-case execution time of five seconds.</p>
<p>The entity making async calls to <code>calculate</code> is a class called <code>Calculator</code>, and it
tracks the results of <code>calculate</code> in one of its member attributes.</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Calculator <span class="op">{</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a>  <span class="bu">std::</span>future<span class="op">&lt;</span><span class="bu">std::</span>optional<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;&gt;</span> future_value<span class="op">{};</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> launch_async_calc<span class="op">(</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a>      <span class="bu">std::</span>string name<span class="op">,</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a>      <span class="bu">std::</span>shared_ptr<span class="op">&lt;</span><span class="bu">std::</span>atomic<span class="op">&lt;</span>CalculationProgress<span class="op">&gt;&gt;</span> abort_status<span class="op">)</span> <span class="op">{</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a>    future_value <span class="op">=</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a>        <span class="bu">std::</span>async<span class="op">(</span><span class="bu">std::</span>launch::async<span class="op">,</span> <span class="op">[=]()</span> <span class="op">{</span> <span class="cf">return</span> calculate<span class="op">(</span>name<span class="op">,</span> abort_status<span class="op">);</span> <span class="op">});</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
<p>With these in place, async computations can be launched and aborted:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>  Calculator calculator<span class="op">{};</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> cv1 <span class="op">=</span> <span class="bu">std::</span>make_shared<span class="op">&lt;</span><span class="bu">std::</span>atomic<span class="op">&lt;</span>CalculationProgress<span class="op">&gt;&gt;(</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>      CalculationProgress<span class="op">::</span>Unaborted<span class="op">);</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>  calculator<span class="op">.</span>launch_async_calc<span class="op">(</span><span class="st">&quot;foo&quot;</span><span class="op">,</span> cv1<span class="op">);</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a>  <span class="kw">auto</span> cv2 <span class="op">=</span> <span class="bu">std::</span>make_shared<span class="op">&lt;</span><span class="bu">std::</span>atomic<span class="op">&lt;</span>CalculationProgress<span class="op">&gt;&gt;(</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a>      CalculationProgress<span class="op">::</span>Unaborted<span class="op">);</span></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>  calculator<span class="op">.</span>launch_async_calc<span class="op">(</span><span class="st">&quot;bar&quot;</span><span class="op">,</span> cv2<span class="op">);</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a>  <span class="op">*</span>cv1 <span class="op">=</span> CalculationProgress<span class="op">::</span>Aborted<span class="op">;</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a>  <span class="op">*</span>cv2 <span class="op">=</span> CalculationProgress<span class="op">::</span>Aborted<span class="op">;</span></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a>  <span class="cf">return</span> <span class="dv">0</span><span class="op">;</span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>When running this program, we get the following output:</p>
<pre><code>$ time ./calculate_foo
foo: COMPLETE, count: 2
bar: ABORTED, count: 2
./calculate_foo  0.00s user 0.00s system 0% cpu 10.002 total</code></pre>
<p>Something is off – wasn’t <code>foo</code> supposed to be aborted? And the program runs for about
ten seconds. I’d expect both <code>launch_async_calc</code> calls to run in parallel on
my machine; <code>foo</code> would recognize its toggled abort after one loop
of five seconds, <code>bar</code> would would do the same, and the program time
should then be five seconds or so. What happened?</p>
<p>Using some good, old-fashioned print debugging, let’s check to see if
these processes are launched concurrently by adding the following
to the top of <code>calculate</code>:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="bu">std::</span>optional<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>calculate<span class="op">(</span><span class="bu">std::</span>string name<span class="op">,</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>          <span class="bu">std::</span>shared_ptr<span class="op">&lt;</span><span class="bu">std::</span>atomic<span class="op">&lt;</span>CalculationProgress<span class="op">&gt;&gt;</span> abort_status<span class="op">)</span> <span class="op">{</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>  <span class="bu">std::</span>cout <span class="op">&lt;&lt;</span> name <span class="op">&lt;&lt;</span> <span class="st">&quot;: LAUNCHED&quot;</span> <span class="op">&lt;&lt;</span> <span class="bu">std::</span>endl<span class="op">;</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>Now the program outputs:</p>
<pre><code>$ time ./calculate_foo
foo: LAUNCHED
bar: LAUNCHED
foo: COMPLETE, count: 2
bar: ABORTED, count: 2
./calculate_foo  0.00s user 0.00s system 0% cpu 10.002 total</code></pre>
<p>So it does look like they’re getting launched concurrently. From the
ten-second run time and loop count, it would appear that <code>foo</code> wasn’t appropriately
aborted as we would suspect. Adding one more log line to <code>main</code>:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="dt">int</span> main<span class="op">()</span> <span class="op">{</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a>  <span class="bu">std::</span>cout <span class="op">&lt;&lt;</span> <span class="st">&quot;ABORTING&quot;</span> <span class="op">&lt;&lt;</span> <span class="bu">std::</span>endl<span class="op">;</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>  <span class="op">*</span>cv1 <span class="op">=</span> CalculationProgress<span class="op">::</span>Aborted<span class="op">;</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>  <span class="op">...</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p>we see:</p>
<pre><code>$ time ./calculate_foo
foo: LAUNCHED
bar: LAUNCHED
foo: COMPLETE, count: 2
ABORTING
bar: ABORTED, count: 2
./calculate_foo  0.00s user 0.00s system 0% cpu 10.002 total</code></pre>
<p>Okay, so <code>foo</code> has completely finished its calculation before it’s
been properly aborted, despite the fact each calculation is launched
concurrently. It would appear something is blocking on the calculation
for <code>foo</code>.</p>
<p>After a few more careful placements, the culprit is narrowed
down to the only line in <code>calculator.launch_async_calc("bar", cv2)</code>:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a>future_value <span class="op">=</span></span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a>    <span class="bu">std::</span>async<span class="op">(</span><span class="bu">std::</span>launch::async<span class="op">,</span> <span class="op">[=]()</span> <span class="op">{</span> <span class="cf">return</span> calculate<span class="op">(</span>name<span class="op">,</span> abort_status<span class="op">);</span> <span class="op">});</span></span></code></pre></div>
<p>After measuring the time it takes to execute this line, the program
shows that it takes a whole nine seconds to reassign
the <code>std::future</code> returned by the <code>std::async</code> call.</p>
<pre><code>$ time ./calculate_foo
foo: assigned in 0 seconds
foo: LAUNCHED
bar: LAUNCHED
bar: ABORTED, count: 0
foo: COMPLETE, count: 2
bar: assigned in 9 seconds
ABORTING
./calculate_foo  0.00s user 0.00s system 0% cpu 10.002 total</code></pre>
<p>When dealing with this problem originally, outside of this contrived example, I started
completely puzzled. After narrowing down to two possibilities, the <code>std::future</code> move assignment
operator or its destructor, I decided to search online and found a page that resulted in palm-to-face contact…</p>
<p><a href="https://stackoverflow.com/q/23455104">Why is the destructor of a future returned from <code>std::async</code> blocking?</a></p>
<p>After the initial surprise wore off, and after reading through a portion of the treasure-trove of
information linked by the top answer, I decided to see if the
<a href="https://en.cppreference.com/w/cpp/thread/async">official documentation</a> for <code>std::future</code>
had anything to say about this. More or less, about halfway down the page, it does:</p>
<blockquote>
<p><em>If the std::future obtained from std::async is not moved from or bound to a reference, the destructor of the std::future will block at the end of the full expression until the asynchronous operation completes, essentially making code such as the following synchronous:</em></p>
</blockquote>
<div class="sourceCode" id="cb11"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="bu">std::</span>async<span class="op">(</span><span class="bu">std::</span>launch::async<span class="op">,</span> <span class="op">[]{</span> f<span class="op">();</span> <span class="op">});</span> <span class="co">// temporary&#39;s dtor waits for f()</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="bu">std::</span>async<span class="op">(</span><span class="bu">std::</span>launch::async<span class="op">,</span> <span class="op">[]{</span> g<span class="op">();</span> <span class="op">});</span> <span class="co">// does not start until f() completes</span></span></code></pre></div>
<blockquote>
<p><em>(note that the destructors of std::futures obtained by means other than a call to std::async never block)</em></p>
</blockquote>
<p>I’m not completely satisfied with lack of emphasis on the reference page, but it at least alludes to one possible way of making the above program work as intended. All it takes
is a few lines. We maintain a vector of past calculations and move the future that’s about to get reassigned
into the vector before doing so:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode cpp"><code class="sourceCode cpp"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">struct</span> Calculator <span class="op">{</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a>  <span class="bu">std::</span>future<span class="op">&lt;</span><span class="bu">std::</span>optional<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;&gt;</span> future_value<span class="op">{};</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>  <span class="bu">std::</span>vector<span class="op">&lt;</span><span class="bu">std::</span>future<span class="op">&lt;</span><span class="bu">std::</span>optional<span class="op">&lt;</span><span class="dt">int</span><span class="op">&gt;&gt;&gt;</span> old_futures<span class="op">{};</span> <span class="co">// new</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a>  <span class="dt">void</span> launch_async_calc<span class="op">(</span><span class="bu">std::</span>string name<span class="op">,</span></span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a>                         <span class="bu">std::</span>shared_ptr<span class="op">&lt;</span><span class="bu">std::</span>atomic<span class="op">&lt;</span>CalculationProgress<span class="op">&gt;&gt;</span> abort_status<span class="op">)</span> <span class="op">{</span></span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>    old_futures<span class="op">.</span>push_back<span class="op">(</span><span class="bu">std::</span>move<span class="op">(</span>future_value<span class="op">));</span> <span class="co">// new</span></span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a>    future_value <span class="op">=</span></span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a>        <span class="bu">std::</span>async<span class="op">(</span><span class="bu">std::</span>launch::async<span class="op">,</span> <span class="op">[=]()</span> <span class="op">{</span> <span class="cf">return</span> calculate<span class="op">(</span>name<span class="op">,</span> abort_status<span class="op">);</span> <span class="op">});</span></span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a>  <span class="op">}</span></span>
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a><span class="op">};</span></span></code></pre></div>
<p>This solution has its problems. For example, now there arguably
should be something that prunes <code>old_futures</code>, once they complete,
in order to avoid a vector that perpetually grows. The program now outputs:</p>
<pre><code>$ time ./calculate_foo
foo: assigned in 0 seconds
foo: LAUNCHED
bar: assigned in 0 seconds
bar: LAUNCHED
ABORTING
foo: ABORTED, count: 1
bar: ABORTED, count: 1
./calculate_foo  0.00s user 0.00s system 0% cpu 5.002 total</code></pre>
<p>The total run time of the program is still limited to the minumum run time of calculating
<code>foo</code>, because each element in <code>old_futures</code> still has a blocking destructor, but at least
subsequent calls to <code>launch_async_calc</code> will not be blocking on prior calls and
the program now behaves as we would expect.</p>
<p>I’m on the fence about considering this a true foot-gun or not. But
something that does make it seem like one, at least to me, is the
fact that <code>std::futures</code> returned by <code>std::promise</code>, for example,
do not exhibit this blocking behavior. Either way, having been made aware of
this, I’ll be keeping it in mind.</p>]]></summary>
</entry>
<entry>
    <title>Tensor Chain Contraction with Refolds
</title>
    <link href="https://anekstein.com/posts/2020-08-09.html" />
    <id>https://anekstein.com/posts/2020-08-09.html</id>
    <published>2020-08-09T00:00:00Z</published>
    <updated>2020-08-09T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p><em>You can find the source code for this post <a href="https://github.com/oinoom/tensorchain">here</a>.</em></p>
<p>In a <a href="./posts/2019-11-15.html">previous post</a> we utilized recursion schemes in prototyping a genetic algorithms library. I wanted to look more into their use cases and was happy to discover that they could even be leveraged for dynamic programming.</p>
<h2 id="matrix-chain-multiplication">Matrix Chain Multiplication</h2>
<p>One of my favorite applications of dynamic programming is matrix chain multiplication; given a bunch of matrices with shared indices, the goal is to find the smallest number of arithmetic operations possible in calculating their product. Usually, this is accompanied by deriving the optimal parenthesization.</p>
<p>There are <em>lots</em> of examples online that outline the conventional dynamic programming approach, so I’m not going to rehash it here. I do, however, recommend familiarizing yourself with the dynamic programming solution before moving on. Here, we’ll focus on leveraging recursion schemes to get the job done.</p>
<p>To find the optimal parenthesization, <a href="http://www.cs.ox.ac.uk/people/nicolas.wu/publications/Histomorphisms.pdf">Hinze and Wu</a> leverage a recursion scheme called a dynamorphism, which can be modeled in part with a recursion scheme touched upon in the <a href="./posts/2019-11-15.html">last post</a> called a hylomorphism, described as a function that unfolds (builds) some intermediate structure (using a <code>CoAlgebra</code>) and folds (reduces) that intermediate structure into some accumulated value (using an <code>Algebra</code>):</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Algebra</span> f a <span class="ot">=</span> f a <span class="ot">-&gt;</span> a</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">CoAlgebra</span> f a <span class="ot">=</span> a <span class="ot">-&gt;</span> f a</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="ot">hylo ::</span> <span class="dt">Functor</span> f <span class="ot">=&gt;</span> <span class="dt">CoAlgebra</span> f a <span class="ot">-&gt;</span> <span class="dt">Algebra</span> f b <span class="ot">-&gt;</span> a <span class="ot">-&gt;</span> b</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>hylo f g <span class="ot">=</span> h <span class="kw">where</span> h <span class="ot">=</span> g <span class="op">.</span> <span class="fu">fmap</span> h <span class="op">.</span> f</span></code></pre></div>
<p>Due to this unfolding and folding mechanism, hylomorphisms are sometimes referred to as a kind of refold.</p>
<p>Dynamorphisms, another type of refold, are very similar to a hylomorphism; a dynamorphism performs the same behavior as a hylomorphism but maintains a record of its folds by storing the result of each one into a structure called a <code>Cofree Comonad</code>:</p>
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">Cofree</span> f a <span class="ot">=</span> a <span class="op">:&lt;</span> (f (<span class="dt">Cofree</span> f a))</span></code></pre></div>
<p>If this looks confusing, don’t fret. It’s extremely similar to a recursive definition of a list type, and we’ll be treating it as such:</p>
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- normal list type</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">List</span> valueType <span class="ot">=</span> </span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Cons</span> valueType (<span class="dt">List</span> valueType) <span class="op">|</span> <span class="dt">Nil</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="co">-- closer to Cofree</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">ListS</span> containerType valueType <span class="ot">=</span> </span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>    <span class="dt">ConsS</span> valueType (<span class="dt">ListS</span> containerType valueType)</span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="co">-- even closer to Cofree</span></span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">ListCF</span> containerType valueType <span class="ot">=</span> </span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>    <span class="dt">ConsCF</span> valueType (containerType (<span class="dt">ListCF</span> containerType valueType))</span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a><span class="co">-- basically Cofree</span></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">ListCF</span> containerType valueType <span class="ot">=</span> </span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a>    valueType <span class="op">:&lt;</span> (containerType (<span class="dt">ListCF</span> containerType valueType))</span></code></pre></div>
<p>Let’s compare the signature of a dynamorphism to a hylomorphism without the type synonyms:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="ot">hylo ::</span> <span class="dt">Functor</span> f <span class="ot">=&gt;</span> (a <span class="ot">-&gt;</span> f a) <span class="ot">-&gt;</span> (f b <span class="ot">-&gt;</span> b)            <span class="ot">-&gt;</span> a <span class="ot">-&gt;</span> b</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="ot">dyna ::</span> <span class="dt">Functor</span> f <span class="ot">=&gt;</span> (a <span class="ot">-&gt;</span> f a) <span class="ot">-&gt;</span> (f (<span class="dt">Cofree</span> f b) <span class="ot">-&gt;</span> b) <span class="ot">-&gt;</span> a <span class="ot">-&gt;</span> b</span></code></pre></div>
<p>We can see that they are pretty similar, and we can see in the definition of <code>dyna</code> that this really is a hylomorphism that keeps track of the values calculated:</p>
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- extracts the first value from Cofree</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a><span class="ot">extract ::</span> <span class="dt">Cofree</span> f a <span class="ot">-&gt;</span> a</span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>extract (a <span class="op">:&lt;</span> _) <span class="ot">=</span> a</span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="ot">dyna ::</span> <span class="dt">Functor</span> f <span class="ot">=&gt;</span> (a <span class="ot">-&gt;</span> f a) <span class="ot">-&gt;</span> (f (<span class="dt">Cofree</span> f b) <span class="ot">-&gt;</span> b) <span class="ot">-&gt;</span> a <span class="ot">-&gt;</span> b</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>dyna h g <span class="ot">=</span> extract <span class="op">.</span> hylo h (\fcfb <span class="ot">-&gt;</span> (g fcfb) <span class="op">:&lt;</span> fcfb)</span></code></pre></div>
<p>The dynamorphism builds an intermediate structure with <code>h a</code> and it stores the result of applying <code>g</code> to that value in a <code>Cofree</code>. Using our list analogy, it’s applying <code>g</code> to the incoming functor and prepending the result to an existing list of past-calculated values. This is reminiscent of the <a href="https://hackage.haskell.org/package/base-4.14.0.0/docs/Data-List.html#g:8"><code>iterate</code></a> function, which repeatedly applies a function <code>f</code> to some value <code>x</code> and appends each application’s result to a list.</p>
<p>By keeping a history of the dynamorphism’s applications of the folding function it’s supplied, one can reach back into the <code>Cofree</code> structure and utilize pre-calculated values a la dynamic programming, which is exactly what Hinze and Wu do for matrix chain multiplication:</p>
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- grab the nth element of a Cofree</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="ot">get ::</span> <span class="dt">Cofree</span> (<span class="dt">ListF</span> v) a <span class="ot">-&gt;</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> a</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>get (x <span class="op">:&lt;</span> xs) <span class="dv">0</span> <span class="ot">=</span> x</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>get (x <span class="op">:&lt;</span> (<span class="dt">Cons</span> _ xs)) n <span class="ot">=</span> xs <span class="ot">`get`</span> (n<span class="op">-</span><span class="dv">1</span>)</span>
<span id="cb6-5"><a href="#cb6-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-6"><a href="#cb6-6" aria-hidden="true" tabindex="-1"></a><span class="co">-- take the first n elements of a Cofree</span></span>
<span id="cb6-7"><a href="#cb6-7" aria-hidden="true" tabindex="-1"></a><span class="ot">collect ::</span> <span class="dt">Cofree</span> (<span class="dt">ListF</span> v) a <span class="ot">-&gt;</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> [a]</span>
<span id="cb6-8"><a href="#cb6-8" aria-hidden="true" tabindex="-1"></a>collect _ <span class="dv">0</span> <span class="ot">=</span> []</span>
<span id="cb6-9"><a href="#cb6-9" aria-hidden="true" tabindex="-1"></a>collect (x <span class="op">:&lt;</span> (<span class="dt">Some</span> _)) n <span class="ot">=</span> [x]</span>
<span id="cb6-10"><a href="#cb6-10" aria-hidden="true" tabindex="-1"></a>collect (x <span class="op">:&lt;</span> (<span class="dt">Cons</span> _ cf)) n <span class="ot">=</span> x <span class="op">:</span> cf <span class="ot">`collect`</span> (n<span class="op">-</span><span class="dv">1</span>)</span>
<span id="cb6-11"><a href="#cb6-11" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-12"><a href="#cb6-12" aria-hidden="true" tabindex="-1"></a><span class="ot">chainM ::</span> [<span class="dt">Int</span>] <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb6-13"><a href="#cb6-13" aria-hidden="true" tabindex="-1"></a>chainM dims <span class="ot">=</span> dyna triangle findParen <span class="fu">range</span> <span class="kw">where</span></span>
<span id="cb6-14"><a href="#cb6-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-15"><a href="#cb6-15" aria-hidden="true" tabindex="-1"></a>    <span class="fu">range</span> <span class="ot">=</span> (<span class="dv">1</span>, <span class="fu">length</span> dims <span class="op">-</span> <span class="dv">1</span>)</span>
<span id="cb6-16"><a href="#cb6-16" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-17"><a href="#cb6-17" aria-hidden="true" tabindex="-1"></a><span class="ot">    triangle ::</span> (<span class="dt">Int</span>,<span class="dt">Int</span>) <span class="ot">-&gt;</span> <span class="dt">ListF</span> (<span class="dt">Int</span>,<span class="dt">Int</span>) (<span class="dt">Int</span>,<span class="dt">Int</span>)</span>
<span id="cb6-18"><a href="#cb6-18" aria-hidden="true" tabindex="-1"></a>    triangle (<span class="dv">1</span>,<span class="dv">1</span>) <span class="ot">=</span> <span class="dt">Some</span> (<span class="dv">1</span>,<span class="dv">1</span>)</span>
<span id="cb6-19"><a href="#cb6-19" aria-hidden="true" tabindex="-1"></a>    triangle (i,j)</span>
<span id="cb6-20"><a href="#cb6-20" aria-hidden="true" tabindex="-1"></a>        <span class="op">|</span> i <span class="op">==</span> j <span class="ot">=</span> <span class="dt">Cons</span> (i,j) (<span class="dv">1</span>,j<span class="op">-</span><span class="dv">1</span>)</span>
<span id="cb6-21"><a href="#cb6-21" aria-hidden="true" tabindex="-1"></a>        <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">=</span> <span class="dt">Cons</span> (i,j) (i<span class="op">+</span><span class="dv">1</span>,j)</span>
<span id="cb6-22"><a href="#cb6-22" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb6-23"><a href="#cb6-23" aria-hidden="true" tabindex="-1"></a><span class="ot">    findParen ::</span> <span class="dt">ListF</span> (<span class="dt">Int</span>,<span class="dt">Int</span>) (<span class="dt">Cofree</span> (<span class="dt">ListF</span> (<span class="dt">Int</span>,<span class="dt">Int</span>)) <span class="dt">Int</span>) <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb6-24"><a href="#cb6-24" aria-hidden="true" tabindex="-1"></a>    findParen (<span class="dt">Some</span> j) <span class="ot">=</span> <span class="dv">0</span></span>
<span id="cb6-25"><a href="#cb6-25" aria-hidden="true" tabindex="-1"></a>    findParen (<span class="dt">Cons</span> (i,j) table)</span>
<span id="cb6-26"><a href="#cb6-26" aria-hidden="true" tabindex="-1"></a>        <span class="op">|</span> i <span class="op">==</span> j <span class="ot">=</span> <span class="dv">0</span></span>
<span id="cb6-27"><a href="#cb6-27" aria-hidden="true" tabindex="-1"></a>        <span class="op">|</span> i <span class="op">&lt;</span> j <span class="ot">=</span> <span class="fu">minimum</span> (<span class="fu">zipWith</span> (<span class="op">+</span>) as bs) <span class="kw">where</span></span>
<span id="cb6-28"><a href="#cb6-28" aria-hidden="true" tabindex="-1"></a>            as <span class="ot">=</span> [(dims <span class="op">!!</span> (i<span class="op">-</span><span class="dv">1</span>)) <span class="op">*</span> (dims <span class="op">!!</span> k) <span class="op">*</span> (dims <span class="op">!!</span> j) </span>
<span id="cb6-29"><a href="#cb6-29" aria-hidden="true" tabindex="-1"></a>                   <span class="op">+</span> (table <span class="ot">`get`</span> offset k) <span class="op">|</span> k <span class="ot">&lt;-</span> [i<span class="op">..</span>j<span class="op">-</span><span class="dv">1</span>]]</span>
<span id="cb6-30"><a href="#cb6-30" aria-hidden="true" tabindex="-1"></a>            bs <span class="ot">=</span> table <span class="ot">`collect`</span> (j<span class="op">-</span>i)</span>
<span id="cb6-31"><a href="#cb6-31" aria-hidden="true" tabindex="-1"></a>            offset k <span class="ot">=</span> ((j<span class="op">*</span>(j<span class="op">+</span><span class="dv">1</span>) <span class="op">-</span> k<span class="op">*</span>(k<span class="op">+</span><span class="dv">1</span>)) <span class="ot">`div`</span> <span class="dv">2</span>) <span class="op">-</span> <span class="dv">1</span></span></code></pre></div>
<p>With dependencies between subproblems modeled as a directed acyclic graph, the algorithm unfolds a range into a list of cell coordinates in reverse topological order using the <code>triangle</code> function. It then folds that list in topological order with <code>findParen</code>, maintaining a table of past-calculated values along the way. With every folding step, <code>findParen</code> reaches back into the table to find the pre-calculated values in cells <code>(i,k)</code> and <code>(k+1,j)</code> for every <code>k</code> in the range <code>[i,j-1]</code> and finds the best <code>k</code> to split the intermediate matrix <span class="math inline"><em>M</em><sub><em>i</em><em>j</em></sub></span>. As you can tell from the way these values are calculated, Hinze and Wu carefully consider and identify where these previously-calculated values may be found in the <code>Cofree</code>.</p>
<p>However, there are two pieces causing this function to lose the <span class="math inline">𝒪(<em>N</em><sup>3</sup>)</span> complexity found in the conventional dynamic programming approach; our first problem is that Haskell lists are not built like typical arrays, but like linked lists, so <code>dims !! x</code> has complexity <span class="math inline">𝒪(<em>N</em>)</span> rather than the desired <span class="math inline">𝒪(1)</span> – but this at least can be remedied with something like <code>Data.Vector</code>. Our second problem is similar; <code>Cofree</code> is like a linked list, and so reaching into the <code>Cofree</code> structure with <code>get</code> is a linear operation; the pre-calculated value at <code>offset k</code> is not guaranteed to be within a constant distance of the head of <code>Cofree</code>. If we were calculating something like the <span class="math inline"><em>n</em><sup><em>t</em><em>h</em></sup></span> Fibonacci number, we would only need to look back two elements and would be in the clear, but here it is not the case; we’ve caused the overall complexity to reach <span class="math inline">𝒪(<em>N</em><sup>4</sup>)</span>.</p>
<p>I’m unaware of a way that something like <code>Cofree</code> can allow us to peek into its structure in constant time, particularly when the element we’d like grab is arbitrarily nested. So, instead of relying upon a dynamorphism, we can return to a hylomorphism – and, instead of a list or a vector of pre-calculated scores with nontrivial offset calculations, we’ll maintain a “two-dimensional” memoization table. For simplicity’s sake, we’ll leverage a <code>HashMap</code>, but one could easily use <code>Data.Vector</code>. So, <code>findParen</code> will then take the following form:</p>
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Map</span> <span class="ot">=</span> <span class="dt">HashMap.Map</span></span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="ot">chainM ::</span> [<span class="dt">Int</span>] <span class="ot">-&gt;</span> <span class="dt">Int</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a>chainM dims <span class="ot">=</span> best <span class="kw">where</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>    best <span class="ot">=</span> (hylo triangle findParen <span class="fu">range</span>) <span class="op">!</span> <span class="fu">range</span></span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a><span class="co">-- ...</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a><span class="ot">    findParen ::</span> <span class="dt">Algebra</span> (<span class="dt">ListF</span> (<span class="dt">Int</span>,<span class="dt">Int</span>)) (<span class="dt">Map</span> (<span class="dt">Int</span>,<span class="dt">Int</span>) <span class="dt">Int</span>)</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a>    findParen (<span class="dt">Some</span> (j,_)) <span class="ot">=</span> Map.insert (j,j) <span class="dv">0</span> Map.empty</span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a>    findParen (<span class="dt">Cons</span> (i,j) table)</span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a>        <span class="op">|</span> i <span class="op">==</span> j <span class="ot">=</span> Map.insert (i,j) <span class="dv">0</span> table</span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a>        <span class="op">|</span> i <span class="op">&lt;</span> j <span class="ot">=</span> Map.insert (i,j) (<span class="fu">minimum</span> parenthesizations) table <span class="kw">where</span></span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a>            cost x y <span class="ot">=</span> table <span class="op">!</span> (x,y)</span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a>            space (x,y,z) <span class="ot">=</span> (dims <span class="op">!!</span> x) <span class="op">*</span> (dims <span class="op">!!</span> y) <span class="op">*</span> (dims <span class="op">!!</span> z)</span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a>            parenthesizations <span class="ot">=</span> </span>
<span id="cb7-17"><a href="#cb7-17" aria-hidden="true" tabindex="-1"></a>                [space (i<span class="op">-</span><span class="dv">1</span>,k,j) <span class="op">+</span> cost i k <span class="op">+</span> cost (k<span class="op">+</span><span class="dv">1</span>) j <span class="op">|</span> k <span class="ot">&lt;-</span> [i<span class="op">..</span>j<span class="op">-</span><span class="dv">1</span>]]</span></code></pre></div>
<p>With this change and an imagined switch to <code>Data.Vector</code> for the sequence of <code>dims</code>, we can bring the time complexity back to <span class="math inline">𝒪(<em>N</em><sup>3</sup>)<sup>*</sup></span>.</p>
<p>* <em>Sort of, the <code>unordered-containers</code> package mentions “[m]any operations have a average-case complexity of <span class="math inline">𝒪(<em>l</em><em>o</em><em>g</em><em>N</em>)</span>. The implementation uses a large base (i.e. 16) so in practice these operations are constant time.” If we really wanted constant time, we could use a two dimensional</em> <code>Vector</code> <em>but we’ll stick with</em> <code>HashMap</code> <em>for simplicity.</em></p>
<p>For a chain of matrices such as:</p>
<p><img src="../images/10.png" /></p>
<p>we can see the minimum number of operations possible is <code>102</code>:</p>
<div class="sourceCode" id="cb8"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">let</span> matrices <span class="ot">=</span> [<span class="dv">4</span>,<span class="dv">3</span>,<span class="dv">5</span>,<span class="dv">2</span>,<span class="dv">4</span>,<span class="dv">3</span>]</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a><span class="fu">putStrLn</span> <span class="op">.</span> <span class="fu">show</span> <span class="op">$</span> chainM matrices <span class="co">-- 102</span></span></code></pre></div>
<h2 id="tensor-chain-contraction">Tensor Chain Contraction</h2>
<p>We can view this chain of matrices as a graph with weighted edges that we’d like to contract together; each matrix multiplication costs the product of all edge weights incident to the matrices being multiplied, up to a constant factor:</p>
<p><img src="../images/1.png" /></p>
<p>If the relationship between the equation and diagram is escaping you, I encourage you to check out the <a href="https://www.math3ma.com/blog/matrices-as-tensor-network-diagrams">wonderful tutorial</a> by Tai-Danae Bradley, which indicates that matrix multiplication is a special case of tensor contraction. Let’s take a look at a different graph:</p>
<p><img src="../images/3.png" /></p>
<p>Instead of matrices with two dimensions, we have multidimensional tensors with some indices that are free. The problem is now to find the optimal parenthesization of a chain of tensors to be contracted.</p>
<p>Additionally, the indices of the tensors involved in the chain contraction need not be free; they can be bound to other tensors outside of the chain of interest, or even to other tensors in the chain.</p>
<p><img src="../images/2.png" /></p>
<p>We can now put our sights on finding the optimal contraction order of a path within some arbitrary tensor network.</p>
<p>It turns out that we can apply the same dynamic programming approach to tensor chain contraction – and credit goes to my colleague Jonathan Jakes-Schauer for the insight. The key is to keep track of all indices that are incident to the chain of interest, because they’ll each contribute to the cost of the contractions in which their tensors participate.</p>
<p>For matrix chain products we saw that <code>findParen</code> identified an index <code>k</code> that optimally decomposed a given intermediate matrix into two. This calculation recursively depended upon the optimal way to decompose those two matrices, down to the original matrices in the chain (which cost nothing to construct). The question then is how to calculate the cost of contracting two tensors; two contracted tensors may share more than just one index, and so we must account for more than just the dimensions of those indices, as we did before. To illustrate, consider the example below:</p>
<p><img src="../images/4.png" /></p>
<p>Once tensor A has been contracted into B, and C into D, the resulting intermediate tensors AB and CD share two indices. Because index <code>w</code> was originally incident to neither tensor B nor D, it’s important that we don’t overcount the number of operations associated with contracting AB and CD; if this contraction appeared in the dynamic programming routine with <span class="math inline"><em>i</em> = 1</span>, <span class="math inline"><em>k</em> = 2</span>, and <span class="math inline"><em>j</em> = 4</span>, and we calculated the cost as before without regard to the identity of the indices involved, we would cost this contraction at <span class="math inline"><em>w</em><em>z</em><em>w</em></span> rather than the true cost, which is <span class="math inline"><em>w</em><em>z</em></span>. Towards this end, we’ll maintain the identity of all indices participant to and resulting from each contraction, using the following types:</p>
<div class="sourceCode" id="cb9"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Tensor</span> <span class="ot">=</span> <span class="dt">Map</span> <span class="dt">Int</span> <span class="dt">Int</span> </span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">ContractionTree</span> a <span class="ot">=</span> </span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Tensor</span> a </span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>  <span class="op">|</span> <span class="dt">Intermediate</span> (<span class="dt">ContractionTree</span> a) (<span class="dt">ContractionTree</span> a)</span>
<span id="cb9-6"><a href="#cb9-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-7"><a href="#cb9-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-8"><a href="#cb9-8" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">TensorData</span> <span class="ot">=</span> <span class="dt">TensorData</span> {</span>
<span id="cb9-9"><a href="#cb9-9" aria-hidden="true" tabindex="-1"></a><span class="ot">    totalCost ::</span> <span class="dt">Int</span>,</span>
<span id="cb9-10"><a href="#cb9-10" aria-hidden="true" tabindex="-1"></a><span class="ot">    recipe ::</span> <span class="dt">ContractionTree</span> <span class="dt">Int</span>,</span>
<span id="cb9-11"><a href="#cb9-11" aria-hidden="true" tabindex="-1"></a><span class="ot">    indices ::</span> <span class="dt">Tensor</span></span>
<span id="cb9-12"><a href="#cb9-12" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>where <code>totalCost</code> denotes the cost of the contraction plus the total cost of having created each of the two tensors being contracted, <code>recipe</code> is a helper datatype for knowing the order in which those two tensors were created and contracted together, and <code>indices</code> is the resulting tensor itself, represented by the bundle of incident indices – a mapping from index identifiers to their dimensions.</p>
<p>Suppose we would like to contract two tensors and want to know the resulting <code>totalCost</code> and <code>indices</code> using a function with the following type:</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="ot">contract ::</span> (<span class="dt">TensorData</span>, <span class="dt">TensorData</span>) <span class="ot">-&gt;</span> <span class="dt">TensorData</span></span></code></pre></div>
<p>Suppose the indices of those two tensors are a given and we’d like to know what indices are left over from the contraction. We can see from an example like the one below:</p>
<p><img src="../images/5.png" /></p>
<p>that the indices left over from the contraction are equal to the symmetric difference of all indices incident to the tensors being contracted:</p>
<div class="sourceCode" id="cb11"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="ot">contract ::</span> (<span class="dt">TensorData</span>, <span class="dt">TensorData</span>) <span class="ot">-&gt;</span> <span class="dt">TensorData</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a>contract (left,right) <span class="ot">=</span> <span class="dt">TensorData</span> {</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>    totalCost <span class="ot">=</span> <span class="op">...</span>,</span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a>    recipe <span class="ot">=</span> <span class="op">...</span>,</span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a>    indices <span class="ot">=</span> symDiff (indices left) (indices right),</span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a>} <span class="kw">where</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a>    symDiff l r <span class="ot">=</span> (l \\ r) <span class="op">&lt;&gt;</span> (r \\ l)</span></code></pre></div>
<p>where <code>x \\ y</code> is the set difference, or <code>x - y</code>, and <code>x &lt;&gt; y</code> is the set union.</p>
<p>We’ve mentioned already that the cost of contracting two tensors is the product of all indices incident to the two tensors. More formally, for tensors <span class="math inline"><em>A</em></span> and <span class="math inline"><em>B</em></span>, the cost of contraction is</p>
<p><img src="../images/11.png" /></p>
<p>where <span class="math inline"><em>I</em>(<em>X</em>)</span> denotes the set of indices belonging to some tensor <span class="math inline"><em>X</em></span>. Given this, and set of indices we’ve maintained from earlier contractions, we can then calculate the cost of splitting an intermediate tensor as was done in the case of matrices. We identify two “child” tensors when splitting some intermediate tensor as discussed above; it’s determined by the start <span class="math inline"><em>i</em></span> of the subchain in question, the end <span class="math inline"><em>j</em></span> of the subchain in question, and the marker <span class="math inline"><em>k</em></span>, where the chain shall be split; the intermediate tensor <span class="math inline"><em>T</em><sub><em>i</em></sub><em>T</em><sub><em>i</em> + 1</sub>..<em>T</em><sub><em>k</em></sub> = <em>T</em><sub><em>i</em><em>k</em></sub></span> marks the first tensor, and <span class="math inline"><em>T</em><sub><em>k</em> + 1</sub><em>T</em><sub><em>k</em> + 2</sub>..<em>T</em><sub><em>j</em></sub> = <em>T</em><sub>(<em>k</em> + 1)<em>j</em></sub></span> marks the second. When contracted, they result in tensor <span class="math inline"><em>T</em><sub><em>i</em><em>j</em></sub></span>.</p>
<p><img src="../images/12.png" /></p>
<p>With this, we have all the information necessary to represent the resulting tensor:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a>tspace <span class="ot">=</span> Map.foldl (<span class="op">*</span>) <span class="dv">1</span></span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a><span class="ot">contract ::</span> (<span class="dt">TensorData</span>, <span class="dt">TensorData</span>) <span class="ot">-&gt;</span> <span class="dt">TensorData</span></span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>contract (left,right) <span class="ot">=</span> <span class="dt">TensorData</span> {</span>
<span id="cb12-5"><a href="#cb12-5" aria-hidden="true" tabindex="-1"></a>    totalCost <span class="ot">=</span> tspace (indices left <span class="op">&lt;&gt;</span> indices right) </span>
<span id="cb12-6"><a href="#cb12-6" aria-hidden="true" tabindex="-1"></a>                <span class="op">+</span> (totalCost left) </span>
<span id="cb12-7"><a href="#cb12-7" aria-hidden="true" tabindex="-1"></a>                <span class="op">+</span> (totalCost right),</span>
<span id="cb12-8"><a href="#cb12-8" aria-hidden="true" tabindex="-1"></a>    recipe <span class="ot">=</span> <span class="dt">Intermediate</span> (recipe left) (recipe right)</span>
<span id="cb12-9"><a href="#cb12-9" aria-hidden="true" tabindex="-1"></a>    indices <span class="ot">=</span> symDiff (indices left) (indices right),</span>
<span id="cb12-10"><a href="#cb12-10" aria-hidden="true" tabindex="-1"></a>    </span>
<span id="cb12-11"><a href="#cb12-11" aria-hidden="true" tabindex="-1"></a>} <span class="kw">where</span></span>
<span id="cb12-12"><a href="#cb12-12" aria-hidden="true" tabindex="-1"></a>    symDiff l r <span class="ot">=</span> (l \\ r) <span class="op">&lt;&gt;</span> (r \\ l) <span class="co">-- symmetric difference</span></span></code></pre></div>
<p>However, calculating the symmetric difference as well as the union of the indices incident to participant tensors is <span class="math inline">𝒪(<em>R</em>)</span>, where <span class="math inline"><em>R</em></span> is maximum order among tensors contracted. If we do this for every <span class="math inline"><em>T</em><sub><em>i</em><em>k</em></sub></span> and <span class="math inline"><em>T</em><sub>(<em>k</em> + 1)<em>j</em></sub></span>, we’ve increased the complexity of our algorithm to <span class="math inline">𝒪(<em>R</em><em>N</em><sup>3</sup>)</span>.</p>
<p>Can we do better? Can we calculate cost and symmetric difference in a way that’s independent of <code>k</code> to store in our memoization table for when it’s time to consider <span class="math inline"><em>T</em><sub><em>i</em><em>j</em></sub></span>?</p>
<p>Let’s consider the symmetric difference, which is <a href="https://proofwiki.org/wiki/Symmetric_Difference_is_Associative">associative</a>. If we have an intermediate tensor <span class="math inline"><em>T</em><sub><em>i</em><em>j</em></sub></span>, no matter which value for <code>k</code> we evaluate, the set of indices belonging to <span class="math inline"><em>T</em><sub><em>i</em><em>j</em></sub></span> will always be the same. So, we don’t need to calculate this for every value of <code>k</code> after all, we only need to reach into the memoization table to find one <code>k</code> value that’s already been considered. Where can we find that <code>k</code>? If we look back at the <code>triangle</code> function, we receive a hint:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="ot">    triangle ::</span> (<span class="dt">Int</span>,<span class="dt">Int</span>) <span class="ot">-&gt;</span> <span class="dt">ListF</span> (<span class="dt">Int</span>,<span class="dt">Int</span>) (<span class="dt">Int</span>,<span class="dt">Int</span>)</span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>    triangle (<span class="dv">1</span>,<span class="dv">1</span>) <span class="ot">=</span> <span class="dt">Some</span> (<span class="dv">1</span>,<span class="dv">1</span>)</span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>    triangle (i,j)</span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a>        <span class="op">|</span> i <span class="op">==</span> j <span class="ot">=</span> <span class="dt">Cons</span> (i,j) (<span class="dv">1</span>,j<span class="op">-</span><span class="dv">1</span>)</span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a>        <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">=</span> <span class="dt">Cons</span> (i,j) (i<span class="op">+</span><span class="dv">1</span>,j)</span></code></pre></div>
<p>for a given pair <code>(i,j)</code>, we append that pair to a list containing <code>(i+1,j)</code> at the head. So, by the time we reach <code>(i,j)</code> in our fold within <code>findParen</code>, <code>(i+1,j)</code> is already in the memoization table. This means that a value <code>k = i</code> has already been accounted for and we can access the information for <span class="math inline"><em>T</em><sub><em>i</em><em>i</em></sub></span> and <span class="math inline"><em>T</em><sub>(<em>i</em> + 1)<em>j</em></sub></span>, and calculate the symmetric difference, i.e. the indices leftover from the contraction:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a>indLeft <span class="ot">=</span> indices <span class="op">$</span> table <span class="op">!</span> (i,i)</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a>indNext <span class="ot">=</span> indices <span class="op">$</span> table <span class="op">!</span> (i<span class="op">+</span><span class="dv">1</span>,j)</span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>symdiff <span class="ot">=</span> (indLeft \\ indNext) <span class="op">&lt;&gt;</span> (indNext \\ indLeft) <span class="co">-- O(R)</span></span></code></pre></div>
<p>That’s one half of our problem solved. Unfortunately, the same intuition cannot be applied to finding each contraction cost, which is not associative. This is obvious when you consider for a moment that, if it were, this problem of finding the optimal parenthesization wouldn’t exist!</p>
<p>This doesn’t completely kill our chances of remaining independent of <code>k</code>, however, it just means we can’t do so and continue thinking about cost in the same way. So, let’s think about it another way.</p>
<h3 id="contraction-trees">Contraction Trees</h3>
<p>Described in <a href="https://arxiv.org/abs/1908.11034">Carving-width and contraction trees for tensor networks</a>, a contraction tree is a data structure describing a particular set of tensor contraction orders. For example, take a look at the following parenthesization of our matrix chain and notice its mapping to a contraction tree:</p>
<p><img src="../images/6.png" /></p>
<p>If this chain was comprised of higher-order tensors, the concept would remain the same.</p>
<p>We can weight the arcs of this tree, where the weight corresponds to the symmetric difference of the indices incident to the tensors getting contracted. We start with the arcs incident to the leaf nodes, corresponding to the original tensors and their indices:</p>
<p><img src="../images/7.png" /></p>
<p>Similarly, we can weight each internal node, representing each intermediate tensor, with their cost of creation – the product of the set union of its protruding arcs:</p>
<p><img src="../images/8.png" /></p>
<p>If you look closely at each internal node, you’ll notice one interesting property:</p>
<p><img src="../images/9.png" /></p>
<p>For each node with arcs <span class="math inline"><em>a</em></span>, <span class="math inline"><em>a</em><sup>′</sup></span>, and <span class="math inline"><em>a</em><sup>″</sup></span>, the weight of the node itself, i.e. the cost of its creation, is:</p>
<p><img src="../images/13.png" /></p>
<p>We can reconsider the labels of our arcs in a way that captures the nature of the problem at hand:</p>
<p><img src="../images/14.png" /></p>
<p>With this we can see that the weights of arcs correspond to the set of indices child tensors <span class="math inline"><em>T</em><sub><em>i</em><em>k</em></sub></span> and <span class="math inline"><em>T</em><sub>(<em>k</em> + 1)<em>j</em></sub></span> and their result, <span class="math inline"><em>T</em><sub><em>i</em><em>j</em></sub></span>, each contain. By maintaining an account of those arc weights, we can calculate the cost of the contraction in a way that relies on the associative symmetric difference. In other words, we can now calculate the cost in a way that’s independent of <code>k</code>. First, we’ll add a new attribute called <code>cspace</code> that captures this arc weight:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">TensorData</span> <span class="ot">=</span> <span class="dt">TensorData</span> {</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="ot">    totalCost ::</span> <span class="dt">Int</span>,</span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a><span class="ot">    recipe ::</span> <span class="dt">ContractionTree</span> <span class="dt">Int</span>,</span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a><span class="ot">    cspace ::</span> <span class="dt">Int</span>,</span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a><span class="ot">    indices ::</span> <span class="dt">Tensor</span></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>which we can utilize in calculating the cost:</p>
<div class="sourceCode" id="cb16"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a>    indLeft <span class="ot">=</span> indices <span class="op">$</span> table <span class="op">!</span> (i,i)</span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a>    indNext <span class="ot">=</span> indices <span class="op">$</span> table <span class="op">!</span> (i<span class="op">+</span><span class="dv">1</span>,j)</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>    symdiff <span class="ot">=</span> (indLeft \\ indNext) <span class="op">&lt;&gt;</span> (indNext \\ indLeft) <span class="co">-- O(R)</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>    cspaceij <span class="ot">=</span> tspace symdiff <span class="co">-- O(R)</span></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- get contraction data of combining two intermediate tensors: O(1)</span></span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a><span class="ot">    contract ::</span> (<span class="dt">TensorData</span>, <span class="dt">TensorData</span>) <span class="ot">-&gt;</span> <span class="dt">TensorData</span></span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a>    contract (left,right) <span class="ot">=</span> <span class="dt">TensorData</span> {</span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a>        totalCost <span class="ot">=</span> totalCost left <span class="op">+</span> totalCost right <span class="op">+</span> sqrtCspaces,</span>
<span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a>        recipe <span class="ot">=</span> <span class="dt">Intermediate</span> (recipe left) (recipe right),</span>
<span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a>        cspace <span class="ot">=</span> cspaceij,</span>
<span id="cb16-12"><a href="#cb16-12" aria-hidden="true" tabindex="-1"></a>        indices <span class="ot">=</span> symdiff</span>
<span id="cb16-13"><a href="#cb16-13" aria-hidden="true" tabindex="-1"></a>    } <span class="kw">where</span></span>
<span id="cb16-14"><a href="#cb16-14" aria-hidden="true" tabindex="-1"></a>        cspaces <span class="ot">=</span> cspace left <span class="op">*</span> cspace right <span class="op">*</span> cspaceij</span>
<span id="cb16-15"><a href="#cb16-15" aria-hidden="true" tabindex="-1"></a>        sqrtCspaces <span class="ot">=</span> <span class="fu">round</span> <span class="op">.</span> <span class="fu">sqrt</span> <span class="op">.</span> <span class="fu">fromIntegral</span> <span class="op">$</span> cspaces</span></code></pre></div>
<h2 id="wrapping-up">Wrapping Up</h2>
<p>Putting everything together, we now have everything we need for an algorithm that can calculate the tensor chain product for any path in a tensor network in <span class="math inline">𝒪(<em>R</em><em>N</em><sup>2</sup> + <em>N</em><sup>3</sup>)</span> time. After addressing the portions of our memoization table that correspond to our original tensors, we have our final result:</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="ot">chainT ::</span> <span class="dt">Vector</span> <span class="dt">Tensor</span> <span class="ot">-&gt;</span> <span class="dt">TensorData</span></span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a>chainT tensors <span class="ot">=</span> best <span class="kw">where</span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a>    best <span class="ot">=</span> (hylo triangle findParen <span class="fu">range</span>) <span class="op">!</span> <span class="fu">range</span></span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a>    <span class="fu">range</span> <span class="ot">=</span> (<span class="dv">1</span>, <span class="fu">length</span> tensors)</span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a>    emptyData i <span class="ot">=</span> <span class="dt">TensorData</span> {</span>
<span id="cb17-8"><a href="#cb17-8" aria-hidden="true" tabindex="-1"></a>        totalCost <span class="ot">=</span> <span class="dv">0</span>,</span>
<span id="cb17-9"><a href="#cb17-9" aria-hidden="true" tabindex="-1"></a>        recipe <span class="ot">=</span> <span class="dt">Tensor</span> i,</span>
<span id="cb17-10"><a href="#cb17-10" aria-hidden="true" tabindex="-1"></a>        cspace <span class="ot">=</span> tspace t,</span>
<span id="cb17-11"><a href="#cb17-11" aria-hidden="true" tabindex="-1"></a>        indices <span class="ot">=</span> t</span>
<span id="cb17-12"><a href="#cb17-12" aria-hidden="true" tabindex="-1"></a>    } <span class="kw">where</span> t <span class="ot">=</span> (<span class="op">V.!</span>) tensors (i<span class="op">-</span><span class="dv">1</span>)</span>
<span id="cb17-13"><a href="#cb17-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-14"><a href="#cb17-14" aria-hidden="true" tabindex="-1"></a><span class="ot">    triangle ::</span> (<span class="dt">Int</span>,<span class="dt">Int</span>) <span class="ot">-&gt;</span> <span class="dt">ListF</span> (<span class="dt">Int</span>,<span class="dt">Int</span>) (<span class="dt">Int</span>,<span class="dt">Int</span>)</span>
<span id="cb17-15"><a href="#cb17-15" aria-hidden="true" tabindex="-1"></a>    triangle (<span class="dv">1</span>,<span class="dv">1</span>) <span class="ot">=</span> <span class="dt">Some</span> (<span class="dv">1</span>,<span class="dv">1</span>)</span>
<span id="cb17-16"><a href="#cb17-16" aria-hidden="true" tabindex="-1"></a>    triangle (i,j)</span>
<span id="cb17-17"><a href="#cb17-17" aria-hidden="true" tabindex="-1"></a>        <span class="op">|</span> i <span class="op">==</span> j <span class="ot">=</span> <span class="dt">Cons</span> (i,j) (<span class="dv">1</span>,j<span class="op">-</span><span class="dv">1</span>)</span>
<span id="cb17-18"><a href="#cb17-18" aria-hidden="true" tabindex="-1"></a>        <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">=</span> <span class="dt">Cons</span> (i,j) (i<span class="op">+</span><span class="dv">1</span>,j)</span>
<span id="cb17-19"><a href="#cb17-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-20"><a href="#cb17-20" aria-hidden="true" tabindex="-1"></a><span class="ot">    findParen ::</span> <span class="dt">Algebra</span> (<span class="dt">ListF</span> (<span class="dt">Int</span>,<span class="dt">Int</span>)) (<span class="dt">Map</span> (<span class="dt">Int</span>,<span class="dt">Int</span>) <span class="dt">TensorData</span>)</span>
<span id="cb17-21"><a href="#cb17-21" aria-hidden="true" tabindex="-1"></a>    findParen (<span class="dt">Some</span> (t,_)) <span class="ot">=</span> Map.insert (t,t) (emptyData t) Map.empty <span class="co">-- O(R)</span></span>
<span id="cb17-22"><a href="#cb17-22" aria-hidden="true" tabindex="-1"></a>    findParen (<span class="dt">Cons</span> (i,j) table) <span class="co">-- O(R + N) per (i,j)</span></span>
<span id="cb17-23"><a href="#cb17-23" aria-hidden="true" tabindex="-1"></a>        <span class="op">|</span> i <span class="op">==</span> j <span class="ot">=</span> Map.insert (i,j) (emptyData i) table <span class="co">-- O(R)</span></span>
<span id="cb17-24"><a href="#cb17-24" aria-hidden="true" tabindex="-1"></a>        <span class="op">|</span> i <span class="op">&lt;</span> j <span class="ot">=</span> Map.insert (i,j) best table <span class="kw">where</span></span>
<span id="cb17-25"><a href="#cb17-25" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-26"><a href="#cb17-26" aria-hidden="true" tabindex="-1"></a>            <span class="co">-- O(R)</span></span>
<span id="cb17-27"><a href="#cb17-27" aria-hidden="true" tabindex="-1"></a>            indLeft <span class="ot">=</span> indices <span class="op">$</span> table <span class="op">!</span> (i,i)</span>
<span id="cb17-28"><a href="#cb17-28" aria-hidden="true" tabindex="-1"></a>            indNext <span class="ot">=</span> indices <span class="op">$</span> table <span class="op">!</span> (i<span class="op">+</span><span class="dv">1</span>,j)</span>
<span id="cb17-29"><a href="#cb17-29" aria-hidden="true" tabindex="-1"></a>            symdiff <span class="ot">=</span> (indLeft \\ indNext) <span class="op">&lt;&gt;</span> (indNext \\ indLeft)</span>
<span id="cb17-30"><a href="#cb17-30" aria-hidden="true" tabindex="-1"></a>            cspaceij <span class="ot">=</span> tspace symdiff</span>
<span id="cb17-31"><a href="#cb17-31" aria-hidden="true" tabindex="-1"></a>            </span>
<span id="cb17-32"><a href="#cb17-32" aria-hidden="true" tabindex="-1"></a>            <span class="co">-- O(N)</span></span>
<span id="cb17-33"><a href="#cb17-33" aria-hidden="true" tabindex="-1"></a>            splits <span class="ot">=</span> [((i,k),(k<span class="op">+</span><span class="dv">1</span>,j)) <span class="op">|</span> k <span class="ot">&lt;-</span> [i<span class="op">..</span>j<span class="op">-</span><span class="dv">1</span>]]</span>
<span id="cb17-34"><a href="#cb17-34" aria-hidden="true" tabindex="-1"></a>            getData (l,r) <span class="ot">=</span> (table <span class="op">!</span> l, table <span class="op">!</span> r)</span>
<span id="cb17-35"><a href="#cb17-35" aria-hidden="true" tabindex="-1"></a>            parenthesizations <span class="ot">=</span> <span class="fu">map</span> (contract <span class="op">.</span> getData) splits</span>
<span id="cb17-36"><a href="#cb17-36" aria-hidden="true" tabindex="-1"></a>            best <span class="ot">=</span> argmin totalCost parenthesizations</span>
<span id="cb17-37"><a href="#cb17-37" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb17-38"><a href="#cb17-38" aria-hidden="true" tabindex="-1"></a>            <span class="co">-- O(1)</span></span>
<span id="cb17-39"><a href="#cb17-39" aria-hidden="true" tabindex="-1"></a>            <span class="co">-- get contraction data of combining two intermediate tensors</span></span>
<span id="cb17-40"><a href="#cb17-40" aria-hidden="true" tabindex="-1"></a><span class="ot">            contract ::</span> (<span class="dt">TensorData</span>, <span class="dt">TensorData</span>) <span class="ot">-&gt;</span> <span class="dt">TensorData</span></span>
<span id="cb17-41"><a href="#cb17-41" aria-hidden="true" tabindex="-1"></a>            contract (left,right) <span class="ot">=</span> <span class="dt">TensorData</span> {</span>
<span id="cb17-42"><a href="#cb17-42" aria-hidden="true" tabindex="-1"></a>                totalCost <span class="ot">=</span> totalCost left <span class="op">+</span> totalCost right <span class="op">+</span> sqrtCspaces,</span>
<span id="cb17-43"><a href="#cb17-43" aria-hidden="true" tabindex="-1"></a>                recipe <span class="ot">=</span> <span class="dt">Intermediate</span> (recipe left) (recipe right),</span>
<span id="cb17-44"><a href="#cb17-44" aria-hidden="true" tabindex="-1"></a>                cspace <span class="ot">=</span> cspaceij,</span>
<span id="cb17-45"><a href="#cb17-45" aria-hidden="true" tabindex="-1"></a>                indices <span class="ot">=</span> symdiff</span>
<span id="cb17-46"><a href="#cb17-46" aria-hidden="true" tabindex="-1"></a>            } <span class="kw">where</span></span>
<span id="cb17-47"><a href="#cb17-47" aria-hidden="true" tabindex="-1"></a>                cspaces <span class="ot">=</span> cspace left <span class="op">*</span> cspace right <span class="op">*</span> cspaceij</span>
<span id="cb17-48"><a href="#cb17-48" aria-hidden="true" tabindex="-1"></a>                sqrtCspaces <span class="ot">=</span> <span class="fu">round</span> <span class="op">.</span> <span class="fu">sqrt</span> <span class="op">.</span> <span class="fu">fromIntegral</span> <span class="op">$</span> cspaces</span></code></pre></div>
<p>As a sanity check, we can evaluate the same matrix chain we started with:</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> tensors <span class="ot">=</span> V.fromList <span class="op">$</span> <span class="fu">map</span> (Map.fromList) [</span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a>                [(<span class="dv">1</span>,<span class="dv">4</span>),(<span class="dv">2</span>,<span class="dv">3</span>)],</span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a>                [(<span class="dv">2</span>,<span class="dv">3</span>),(<span class="dv">3</span>,<span class="dv">5</span>)],</span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a>                [(<span class="dv">3</span>,<span class="dv">5</span>),(<span class="dv">4</span>,<span class="dv">2</span>)],</span>
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a>                [(<span class="dv">4</span>,<span class="dv">2</span>),(<span class="dv">5</span>,<span class="dv">4</span>)],</span>
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a>                [(<span class="dv">5</span>,<span class="dv">4</span>),(<span class="dv">6</span>,<span class="dv">3</span>)],</span>
<span id="cb18-8"><a href="#cb18-8" aria-hidden="true" tabindex="-1"></a>                [(<span class="dv">6</span>,<span class="dv">3</span>),(<span class="dv">7</span>,<span class="dv">2</span>)]</span>
<span id="cb18-9"><a href="#cb18-9" aria-hidden="true" tabindex="-1"></a>            ]</span>
<span id="cb18-10"><a href="#cb18-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-11"><a href="#cb18-11" aria-hidden="true" tabindex="-1"></a>        matrices <span class="ot">=</span> [<span class="dv">4</span>,<span class="dv">3</span>,<span class="dv">5</span>,<span class="dv">2</span>,<span class="dv">4</span>,<span class="dv">3</span>,<span class="dv">2</span>]</span>
<span id="cb18-12"><a href="#cb18-12" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb18-13"><a href="#cb18-13" aria-hidden="true" tabindex="-1"></a>    <span class="fu">putStrLn</span> <span class="op">.</span> <span class="fu">show</span> <span class="op">.</span> chainM <span class="op">$</span> matrices <span class="co">-- 102</span></span>
<span id="cb18-14"><a href="#cb18-14" aria-hidden="true" tabindex="-1"></a>    <span class="fu">putStrLn</span> <span class="op">.</span> <span class="fu">show</span> <span class="op">.</span> totalCost <span class="op">.</span> chainT <span class="op">$</span> tensors <span class="co">-- 102</span></span></code></pre></div>]]></summary>
</entry>
<entry>
    <title>Prototyping a Small Genetic Algorithms Library in
Haskell
</title>
    <link href="https://anekstein.com/posts/2019-11-15.html" />
    <id>https://anekstein.com/posts/2019-11-15.html</id>
    <published>2019-11-15T00:00:00Z</published>
    <updated>2019-11-15T00:00:00Z</updated>
    <summary type="html"><![CDATA[<p><em>This post assumes a basic understanding of genetic algorithms and the
terminology associated with them, as well as a cursory understanding of
recursion schemes; resources for both may be found scattered within the
post. All source code can be found
<a href="https://github.com/oinoom/gabble">here</a>.</em></p>
<p>First blog post – yay! This post documents some of my experience
getting practice with recursion schemes and some monadic computations in
the context of prototyping a <a href="https://en.wikipedia.org/wiki/Genetic_algorithm">genetic
algorithms</a> library.
For a full-fledged, flexible, genetic algorithms library written in
Haskell, I refer the reader to <a href="https://github.com/astanin/moo/">moo</a>.</p>
<h2 id="getting-a-birds-eye-view">Getting a birds-eye view</h2>
<p>Genetic algorithms are a type of heuristic in which candidate solutions
to a problem are stochastically and incrementally evolved over time with
the aim of producing performant ones; candidates, or individuals, are
evolved with the help of genetic operators for selecting, manufacturing,
and altering those individuals.</p>
<h2 id="contextualizing-the-computations">Contextualizing the computations</h2>
<p>Let’s start by defining some of the context in which our genetic
algorithm should run. It would be nice to reference a configuration
containing all the definitions and parameters we could need (like
<a href="https://en.wikipedia.org/wiki/Mutation_(genetic_algorithm)">mutation</a>
and
<a href="https://en.wikipedia.org/wiki/Selection_(genetic_algorithm)">selection</a>
methods), utilize and update a random number generator for generating
and mutating individuals, and log intermediate data. The
<a href="https://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-RWS-Lazy.html"><code>RWS</code></a>
monad presents itself as a candidate for meeting these criteria, so
let’s wrap it in a newtype:</p>
<div id="cb1" class="sourceCode">
<div class="sourceCode" id="cb1"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">newtype</span> <span class="dt">GAContext</span> indv a <span class="ot">=</span> <span class="dt">GAContext</span> {</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="ot">    ctx ::</span> <span class="dt">RWS</span> (<span class="dt">GAConfig</span> indv) [<span class="dt">T.Text</span>] <span class="dt">PureMT</span> a</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a>} <span class="kw">deriving</span> (</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Functor</span>, </span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Applicative</span>, </span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Monad</span>, </span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a>        <span class="dt">MonadReader</span> (<span class="dt">GAConfig</span> indv), </span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>        <span class="dt">MonadWriter</span> [<span class="dt">T.Text</span>],</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a>        <span class="dt">MonadState</span> <span class="dt">PureMT</span></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>    )</span></code></pre></div>
</div>
<p>With this definition (which requires <code>GeneralizedNewtypeDeriving</code>), we
can reference and update the
<a href="https://hackage.haskell.org/package/mersenne-random-pure64-0.2.2.0/docs/System-Random-Mersenne-Pure64.html"><code>PureMT</code></a>
random number generator with <a href="https://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-State-Class.html#t:MonadState"><code>get</code> and
<code>put</code></a>,
refer to our configuration with
<a href="https://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Reader-Class.html#t:MonadReader"><code>ask</code></a>,
and log intermediate data with
<a href="https://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-Writer-Class.html"><code>tell</code></a>.</p>
<h2 id="gathering-intermediate-data">Gathering intermediate data</h2>
<p>One of my favorite genetic algorithm libraries,
<a href="https://github.com/DEAP/deap">deap</a>, allows you to keep track of a
<a href="https://deap.readthedocs.io/en/master/api/tools.html#deap.tools.HallOfFame">hall of
fame</a>
– a collection of the most-fit individuals. We can represent this
collection as a continually-updated min-heap, where the worst-performing
individuals at a particular point in time can be popped from the heap
and discarded:</p>
<div id="cb2" class="sourceCode">
<div class="sourceCode" id="cb2"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">Data.Heap</span> <span class="kw">as</span> <span class="dt">Heap</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">HOF</span> a <span class="ot">=</span> <span class="dt">Heap.MinHeap</span> a</span></code></pre></div>
</div>
<p>It would be helpful to have a means of tracking the best individuals
over time, along with any other data that could be gathered with every
new generation. For this, a snapshot data type:</p>
<div id="cb3" class="sourceCode">
<div class="sourceCode" id="cb3"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">GASnapshot</span> a <span class="ot">=</span> <span class="dt">Snapshot</span> {</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- the collection of individuals from the last generation</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="ot">    lastGeneration ::</span> <span class="dt">Vector</span> a</span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- the collection of top performers, the Hall of Fame (HOF)</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> hof ::</span> <span class="dt">HOF</span> a </span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- the current generation id</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> generationNumber ::</span> <span class="dt">Int</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a>} <span class="kw">deriving</span> (<span class="dt">Show</span>)</span></code></pre></div>
</div>
<h2 id="configuring-the-genetic-algorithm">Configuring the genetic algorithm</h2>
<p>Next we can define the data type containing all of our configuration
parameters that we will then be able to reference in <code>GAContext</code>
computations:</p>
<div id="cb4" class="sourceCode">
<div class="sourceCode" id="cb4"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">GAConfig</span> i <span class="ot">=</span> <span class="dt">Config</span> {</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- the probability an individual is mutated</span></span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a><span class="ot">    mutationRateInd ::</span> <span class="dt">Double</span> </span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- the probability a gene of an individual is mutated</span></span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> mutationRateGene ::</span> <span class="dt">Double</span> </span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- the percentage of the population that gets replaced through recombination</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> crossoverRate ::</span> <span class="dt">Double</span> </span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- the population size</span></span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> popSize ::</span> <span class="dt">Int</span> </span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- the mutation method</span></span>
<span id="cb4-11"><a href="#cb4-11" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> mutate ::</span> i <span class="ot">-&gt;</span> <span class="dt">GAContext</span> i i </span>
<span id="cb4-12"><a href="#cb4-12" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- the crossover method</span></span>
<span id="cb4-13"><a href="#cb4-13" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> crossover ::</span> i <span class="ot">-&gt;</span> i <span class="ot">-&gt;</span> <span class="dt">GAContext</span> i i </span>
<span id="cb4-14"><a href="#cb4-14" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- the method to create a new individual</span></span>
<span id="cb4-15"><a href="#cb4-15" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> randomIndividual ::</span> <span class="dt">GAContext</span> i i  </span>
<span id="cb4-16"><a href="#cb4-16" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- the selection method</span></span>
<span id="cb4-17"><a href="#cb4-17" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> selectionMethod ::</span> <span class="dt">Vector</span> i <span class="ot">-&gt;</span> <span class="dt">GAContext</span> i (<span class="dt">Vector</span> i) </span>
<span id="cb4-18"><a href="#cb4-18" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- the fitness function (higher fitness is preferred)</span></span>
<span id="cb4-19"><a href="#cb4-19" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> fitness ::</span> i <span class="ot">-&gt;</span> <span class="dt">Double</span> </span>
<span id="cb4-20"><a href="#cb4-20" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- the number of generations</span></span>
<span id="cb4-21"><a href="#cb4-21" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> numGenerations ::</span> <span class="dt">Int</span> </span>
<span id="cb4-22"><a href="#cb4-22" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- the `hofSize` best individuals across all generations</span></span>
<span id="cb4-23"><a href="#cb4-23" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> hofSize ::</span> <span class="dt">Int</span> </span>
<span id="cb4-24"><a href="#cb4-24" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- function for information sourced from most recent snapshot</span></span>
<span id="cb4-25"><a href="#cb4-25" aria-hidden="true" tabindex="-1"></a>  ,<span class="ot"> logFunc ::</span> <span class="dt">GASnapshot</span> i <span class="ot">-&gt;</span> <span class="dt">GAContext</span> i () </span>
<span id="cb4-26"><a href="#cb4-26" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
</div>
<p>This configuration serves as the basic interface to the library. Once an
instance of this data type is created, the genetic algorithm can do the
bulk of its work.</p>
<h2 id="utilizing-the-genetic-operators">Utilizing the genetic operators</h2>
<p>The genetic algorithm will evolve our set of candidate solutions over
time for a fixed number of steps, or generations.</p>
<h3 id="grabbing-snapshots">Grabbing snapshots</h3>
<p>Every generation of the genetic algorithm is determined by a <code>step</code>
function:</p>
<div id="cb5" class="sourceCode">
<div class="sourceCode" id="cb5"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="ot">step ::</span> <span class="dt">Ord</span> a <span class="ot">=&gt;</span> <span class="dt">GASnapshot</span> a <span class="ot">-&gt;</span> <span class="dt">GAContext</span> a (<span class="dt">GASnapshot</span> a)</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a>step (<span class="dt">Snapshot</span> lastGen hof genNumber) <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Config</span> {hofSize, logFunc, popSize, selectionMethod} <span class="ot">&lt;-</span> ask </span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- select parents and create the next generation from them</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a>    selectedParents <span class="ot">&lt;-</span> selectionMethod lastGen</span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- use the set of parents to create and mutate a new generation</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>    children <span class="ot">&lt;-</span> crossAndMutate selectedParents popSize</span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- update the HOF</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a>    updatedHOF <span class="ot">&lt;-</span> updateHOF hof children hofSize</span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- construct the new snapshot</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> nextSnapshot <span class="ot">=</span> <span class="dt">Snapshot</span>{</span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a>        lastGeneration <span class="ot">=</span> children,</span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a>        hof <span class="ot">=</span> updatedHOF,</span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a>        generationNumber <span class="ot">=</span> genNumber <span class="op">+</span> <span class="dv">1</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a>    }</span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- log intermediate results</span></span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a>    logFunc nextSnapshot</span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- return the mutated generation</span></span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a>    <span class="fu">return</span> nextSnapshot</span></code></pre></div>
</div>
<p>The <code>step</code> function takes the current snapshot, along with the
user-defined configuration to select a portion of the population to pass
genetic material,
<a href="https://en.wikipedia.org/wiki/Crossover_(genetic_algorithm)#uniform_crossover">crossover</a>
individuals from that subset to generate children, and mutate a portion
of those children. With every pass, the Hall of Fame is updated with
better-fit individuals, if they are found, and the subsequent snapshot
is returned.</p>
<h3 id="crossover-and-mutation">Crossover and Mutation</h3>
<p>After parents are selected with the user-defined <code>selectionMethod</code>, the
<code>Vector</code> of parents act as a seed from which children are produced. The
generation of these children via crossover and their mutation are done
in the same pass with a hylomorphism:</p>
<div id="cb6" class="sourceCode">
<div class="sourceCode" id="cb6"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- repeatedly selects two new parents from `parents` from</span></span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a><span class="co">-- which `n` total children are produced</span></span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a><span class="ot">crossAndMutate ::</span> (<span class="dt">Vector</span> a) <span class="ot">-&gt;</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">GAContext</span> a (<span class="dt">Vector</span> a)</span>
<span id="cb6-4"><a href="#cb6-4" aria-hidden="true" tabindex="-1"></a>crossAndMutate parents n <span class="ot">=</span> hyloM toVector (newChild parents) n</span></code></pre></div>
</div>
<p>At this point, I refer the reader to the existing (and superior)
resources on recursion schemes, if they are unfamiliar with the concept;
I found <a href="https://github.com/passy/awesome-recursion-schemes">Awesome Recursion
Schemes</a> to be
helpful, particularly <a href="https://blog.sumtypeofway.com/recursion-schemes-part-2/">Patrick Thompson’s
series</a> and
<a href="https://jtobin.io/practical-recursion-schemes">Jared Tobin’s blog
posts</a>.</p>
<p>Briefly, and skipping over useful generalizations provided by the
<a href="https://hackage.haskell.org/package/recursion-schemes"><code>recursion-schemes</code></a>
library: catamorphisms tear down structures, anamorphisms construct
structures, and hylomorphisms are the composition of an anamorphism and
a catamorphism, i.e. the construction and tearing-down of an
intermediate structure. Catamorphisms utilize a function to tear down
their structures while anamorphisms utilize a function to build up their
structures. Both functions can be found within
<a href="https://hackage.haskell.org/package/category-extras-0.53.0/docs/Control-Functor-Algebra.html"><code>Control.Functor.Algebra</code></a>
and are representations of the morphisms that each comprise a third of
an
<a href="https://www.schoolofhaskell.com/user/bartosz/understanding-algebras">F-Algebra</a>
and <a href="https://en.wikipedia.org/wiki/F-coalgebra">F-CoAlgebra</a>
respectively:</p>
<div id="cb7" class="sourceCode">
<div class="sourceCode" id="cb7"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">Algebra</span> f a <span class="ot">=</span> f a <span class="ot">-&gt;</span> a</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">CoAlgebra</span> f a <span class="ot">=</span> a <span class="ot">-&gt;</span> f a</span></code></pre></div>
</div>
<p>Normal hylomorphisms have the type:</p>
<div id="cb8" class="sourceCode">
<div class="sourceCode" id="cb8"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="ot">hylo ::</span> <span class="dt">Functor</span> f <span class="ot">=&gt;</span> (<span class="dt">Algebra</span> f b) <span class="ot">-&gt;</span> (<span class="dt">CoAlgebra</span> f a) <span class="ot">-&gt;</span> a <span class="ot">-&gt;</span> b </span></code></pre></div>
</div>
<p>For our case, the monadic context of <code>GAContext</code> needs to be preserved.
The haskell package
<a href="https://hackage.haskell.org/package/data-fix-0.2.0/docs/Data-Fix.html"><code>data-fix</code></a>
offers the <code>hyloM</code> function, which instead relies on the monadic
<code>AlgebraM</code> and <code>CoAlgebraM</code> types:</p>
<div id="cb9" class="sourceCode">
<div class="sourceCode" id="cb9"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">AlgebraM</span> m f a <span class="ot">=</span> f a <span class="ot">-&gt;</span> m a</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="dt">CoAlgebraM</span> m f a <span class="ot">=</span> a <span class="ot">-&gt;</span> m (f a)</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb9-4"><a href="#cb9-4" aria-hidden="true" tabindex="-1"></a><span class="ot">hyloM ::</span> (<span class="dt">Functor</span> f, <span class="dt">Monad</span> m) <span class="ot">=&gt;</span> (<span class="dt">AlgebraM</span> m f b) <span class="ot">-&gt;</span></span>
<span id="cb9-5"><a href="#cb9-5" aria-hidden="true" tabindex="-1"></a>                                 (<span class="dt">CoAlgebraM</span> m f a) <span class="ot">-&gt;</span> a <span class="ot">-&gt;</span> m b </span></code></pre></div>
</div>
<p>With the monadic hylomorphism in <code>crossAndMutate</code> above, a fixed list of
mutated children is unfolded from a seed using <code>newChild</code> and folded
into a vector of the same type using <code>toVector</code>. This yields the next
generation of candidate solutions for the genetic algorithm.</p>
<p>Let’s take a look at the <code>newChild</code> function:</p>
<div id="cb10" class="sourceCode">
<div class="sourceCode" id="cb10"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- selects two parents to breed, a child is born, joy to the world</span></span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a><span class="ot">newChild ::</span> (<span class="dt">Vector</span> a) <span class="ot">-&gt;</span> <span class="dt">CoAlgebraM</span> (<span class="dt">GAContext</span> a) (<span class="dt">ListF</span> a) <span class="dt">Int</span></span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>newChild parents <span class="dv">0</span> <span class="ot">=</span> <span class="fu">return</span> <span class="dt">Nil</span></span>
<span id="cb10-4"><a href="#cb10-4" aria-hidden="true" tabindex="-1"></a>newChild parents m <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb10-5"><a href="#cb10-5" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- get mutation and crossover methods</span></span>
<span id="cb10-6"><a href="#cb10-6" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Config</span> {crossover, mutate} <span class="ot">&lt;-</span> ask</span>
<span id="cb10-7"><a href="#cb10-7" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- get two random indices</span></span>
<span id="cb10-8"><a href="#cb10-8" aria-hidden="true" tabindex="-1"></a>    i <span class="ot">&lt;-</span> randomI</span>
<span id="cb10-9"><a href="#cb10-9" aria-hidden="true" tabindex="-1"></a>    j <span class="ot">&lt;-</span> randomI</span>
<span id="cb10-10"><a href="#cb10-10" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- from the two indices, grab two parents</span></span>
<span id="cb10-11"><a href="#cb10-11" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> p1 <span class="ot">=</span> parents <span class="op">!</span> (i <span class="ot">`mod`</span> (<span class="fu">length</span> parents))</span>
<span id="cb10-12"><a href="#cb10-12" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> p2 <span class="ot">=</span> parents <span class="op">!</span> (j <span class="ot">`mod`</span> (<span class="fu">length</span> parents))</span>
<span id="cb10-13"><a href="#cb10-13" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- make a child</span></span>
<span id="cb10-14"><a href="#cb10-14" aria-hidden="true" tabindex="-1"></a>    child <span class="ot">&lt;-</span> crossover p1 p2</span>
<span id="cb10-15"><a href="#cb10-15" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- mutate the child</span></span>
<span id="cb10-16"><a href="#cb10-16" aria-hidden="true" tabindex="-1"></a>    mutatedChild <span class="ot">&lt;-</span> mutate child</span>
<span id="cb10-17"><a href="#cb10-17" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- add the child to the collection</span></span>
<span id="cb10-18"><a href="#cb10-18" aria-hidden="true" tabindex="-1"></a>    <span class="fu">return</span> <span class="op">$</span> <span class="dt">Cons</span> mutatedChild (m<span class="op">-</span><span class="dv">1</span>)</span></code></pre></div>
</div>
<p><code>newChild</code> generates a new individual with the user-defined <code>crossover</code>
function from two parents chosen at random from the group individuals
selected to pass on their genetic material. We then apply the
user-defined <code>mutate</code> function to the child and append that mutated
individual to the in-progress collection of children. This is the
anamorphic half of the hylomorphism.</p>
<p>The catamorphic half of the transformation is accomplished with
<code>toVector</code> below:</p>
<div id="cb11" class="sourceCode">
<div class="sourceCode" id="cb11"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- converts Fix (ListF a) into Vector a</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="ot">toVector ::</span> <span class="dt">AlgebraM</span> (<span class="dt">GAContext</span> a) (<span class="dt">ListF</span> a) (<span class="dt">Vector</span> a)</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>toVector <span class="ot">=</span> <span class="fu">return</span> <span class="op">.</span> embed</span></code></pre></div>
</div>
<p>and we can see that it is rather straightfoward, once we make a
<code>Corecursive</code> instance of <code>Vector</code> to leverage the <code>embed</code> function:</p>
<div id="cb12" class="sourceCode">
<div class="sourceCode" id="cb12"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">type</span> <span class="kw">instance</span> <span class="dt">Base</span> (<span class="dt">Vector</span> a) <span class="ot">=</span> <span class="dt">ListF</span> a</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Corecursive</span> (<span class="dt">Vector</span> a) <span class="kw">where</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>  embed (<span class="dt">Cons</span> x xs) <span class="ot">=</span> x <span class="ot">`V.cons`</span> xs</span>
<span id="cb12-4"><a href="#cb12-4" aria-hidden="true" tabindex="-1"></a>  embed <span class="dt">Nil</span> <span class="ot">=</span> V.empty</span></code></pre></div>
</div>
<p>In addition to the above instance, we will find later on, with our use
of
<a href="https://hackage.haskell.org/package/recursion-schemes-5.1.3/docs/src/Data.Functor.Foldable.html#cata"><code>cata</code></a>
that defining a <code>Recursive</code> instance of <code>Vector</code> is also necessary:</p>
<div id="cb13" class="sourceCode">
<div class="sourceCode" id="cb13"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Recursive</span> (<span class="dt">Vector</span> a) <span class="kw">where</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a>  project xs <span class="op">|</span> V.null xs <span class="ot">=</span> <span class="dt">Nil</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>             <span class="op">|</span> <span class="fu">otherwise</span> <span class="ot">=</span> <span class="dt">Cons</span> (V.head xs) (V.tail xs)</span></code></pre></div>
</div>
<h3 id="updating-the-hall-of-fame">Updating the Hall of Fame</h3>
<p>Once the collection of mutated children has been returned by
<code>crossAndMutate</code>, we will want to update the Hall of Fame with any
individuals that perform better than the extant individuals therein.
Let’s create a function that will take a vector of individuals and
insert them all into the heap representing the Hall of Fame:</p>
<div id="cb14" class="sourceCode">
<div class="sourceCode" id="cb14"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- inserts elements from a list into a heap</span></span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a><span class="ot">insertHeap ::</span> <span class="dt">Ord</span> a <span class="ot">=&gt;</span> <span class="dt">HOF</span> a <span class="ot">-&gt;</span> (<span class="dt">Vector</span> a) <span class="ot">-&gt;</span> <span class="dt">HOF</span> a</span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a>insertHeap hof <span class="ot">=</span> cata insert <span class="kw">where</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a>    insert <span class="dt">Nil</span> <span class="ot">=</span> hof</span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a>    insert (<span class="dt">Cons</span> a heap) <span class="ot">=</span> Heap.insert a heap</span></code></pre></div>
</div>
<p>Simple enough. Our catamorphism breaks down our <code>Vector</code> into a <code>HOF</code>;
all it needs is the existing one into which we can insert the elements.</p>
<p>With this, we can update the current <code>HOF</code> by dumping the latest
population into it and popping off minimally-fit individuals until the
HOF is back at its original size.</p>
<div id="cb15" class="sourceCode">
<div class="sourceCode" id="cb15"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- updates the HOF by removing the worst-fit individuals from the min-heap</span></span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a><span class="ot">updateHOF ::</span> <span class="dt">Ord</span> a <span class="ot">=&gt;</span> <span class="dt">HOF</span> a <span class="ot">-&gt;</span> <span class="dt">Vector</span> a <span class="ot">-&gt;</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">GAContext</span> a (<span class="dt">HOF</span> a)</span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a>updateHOF hof pop hofSize <span class="ot">=</span> <span class="fu">return</span> <span class="op">.</span> Heap.drop n <span class="op">$</span> oversizedHOF <span class="kw">where</span></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- insert all of the current population</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a>    oversizedHOF <span class="ot">=</span> insertHeap hof pop</span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- drop all but hofSize individuals</span></span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a>    n <span class="ot">=</span> V.length pop <span class="op">-</span> <span class="kw">if</span> Heap.isEmpty hof <span class="kw">then</span> hofSize <span class="kw">else</span> <span class="dv">0</span></span></code></pre></div>
</div>
<h2 id="invoking-the-genetic-algorithm">Invoking the Genetic Algorithm</h2>
<p>Now that we have outlined the flow of the genetic algorithm, we need to
provide an initial population. For this, we leverage the user-defined
<code>randomIndividual</code> function, provided within the ever-present
<code>GAConfig</code>:</p>
<div id="cb16" class="sourceCode">
<div class="sourceCode" id="cb16"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- creates a vector of random individuals</span></span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a><span class="ot">makePopulation ::</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> <span class="dt">GAContext</span> a (<span class="dt">Vector</span> a)</span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>makePopulation s <span class="ot">=</span> hyloM toVector addRandomInd s <span class="kw">where</span></span>
<span id="cb16-4"><a href="#cb16-4" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- creates a random individual and adds it to the collection</span></span>
<span id="cb16-5"><a href="#cb16-5" aria-hidden="true" tabindex="-1"></a><span class="ot">    addRandomInd ::</span> <span class="dt">CoAlgebraM</span> (<span class="dt">GAContext</span> a) (<span class="dt">ListF</span> a) <span class="dt">Int</span></span>
<span id="cb16-6"><a href="#cb16-6" aria-hidden="true" tabindex="-1"></a>    addRandomInd <span class="dv">0</span> <span class="ot">=</span> <span class="fu">return</span> <span class="dt">Nil</span></span>
<span id="cb16-7"><a href="#cb16-7" aria-hidden="true" tabindex="-1"></a>    addRandomInd n <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb16-8"><a href="#cb16-8" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- get a new, random individual</span></span>
<span id="cb16-9"><a href="#cb16-9" aria-hidden="true" tabindex="-1"></a>        ind <span class="ot">&lt;-</span> randomIndividual <span class="op">=&lt;&lt;</span> ask</span>
<span id="cb16-10"><a href="#cb16-10" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- add it to the collection</span></span>
<span id="cb16-11"><a href="#cb16-11" aria-hidden="true" tabindex="-1"></a>        <span class="fu">return</span> <span class="op">$</span> <span class="dt">Cons</span> ind (n<span class="op">-</span><span class="dv">1</span>)</span></code></pre></div>
</div>
<p>We now have all pieces necessary for running the genetic algorithm for
one complete generation. After some initial setup, we can run for the
user-specified number of generations:</p>
<div id="cb17" class="sourceCode">
<div class="sourceCode" id="cb17"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="ot">runGA ::</span> <span class="dt">Ord</span> a <span class="ot">=&gt;</span> <span class="dt">GAContext</span> a (<span class="dt">GASnapshot</span> a)</span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a>runGA <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Config</span> {numGenerations, popSize, hofSize} <span class="ot">&lt;-</span> ask</span>
<span id="cb17-4"><a href="#cb17-4" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- initialize the population</span></span>
<span id="cb17-5"><a href="#cb17-5" aria-hidden="true" tabindex="-1"></a>    initialPop <span class="ot">&lt;-</span> makePopulation popSize</span>
<span id="cb17-6"><a href="#cb17-6" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- set up the initial Hall of Fame</span></span>
<span id="cb17-7"><a href="#cb17-7" aria-hidden="true" tabindex="-1"></a>    initialHOF <span class="ot">&lt;-</span> updateHOF (Heap.empty<span class="ot"> ::</span> <span class="dt">HOF</span> a) initialPop hofSize</span>
<span id="cb17-8"><a href="#cb17-8" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- set up the initial snapshot</span></span>
<span id="cb17-9"><a href="#cb17-9" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> snapshot <span class="ot">=</span> <span class="dt">Snapshot</span> {</span>
<span id="cb17-10"><a href="#cb17-10" aria-hidden="true" tabindex="-1"></a>                lastGeneration <span class="ot">=</span> initialPop,</span>
<span id="cb17-11"><a href="#cb17-11" aria-hidden="true" tabindex="-1"></a>                hof <span class="ot">=</span> initialHOF,</span>
<span id="cb17-12"><a href="#cb17-12" aria-hidden="true" tabindex="-1"></a>                generationNumber <span class="ot">=</span> <span class="dv">0</span></span>
<span id="cb17-13"><a href="#cb17-13" aria-hidden="true" tabindex="-1"></a>              }</span>
<span id="cb17-14"><a href="#cb17-14" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- run the genetic algorithm</span></span>
<span id="cb17-15"><a href="#cb17-15" aria-hidden="true" tabindex="-1"></a>    runN numGenerations step snapshot</span></code></pre></div>
</div>
<p>Using our configuration parameters we create an initial snapshot and
pass that to a function that runs the step function for a set number of
iterations equal to the number of generations. Let’s take a look at the
definition of <code>runN</code>:</p>
<div id="cb18" class="sourceCode">
<div class="sourceCode" id="cb18"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- a function reminiscent of iterateM that completes</span></span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a><span class="co">-- after `n` evaluations, returning the `n`th result</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a><span class="ot">runN ::</span> <span class="dt">Monad</span> m <span class="ot">=&gt;</span> <span class="dt">Int</span> <span class="ot">-&gt;</span> (a <span class="ot">-&gt;</span> m a) <span class="ot">-&gt;</span> a <span class="ot">-&gt;</span> m a</span>
<span id="cb18-4"><a href="#cb18-4" aria-hidden="true" tabindex="-1"></a>runN <span class="dv">0</span> _ a <span class="ot">=</span> <span class="fu">return</span> a</span>
<span id="cb18-5"><a href="#cb18-5" aria-hidden="true" tabindex="-1"></a>runN n f a <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb18-6"><a href="#cb18-6" aria-hidden="true" tabindex="-1"></a>    a&#39; <span class="ot">&lt;-</span> f a</span>
<span id="cb18-7"><a href="#cb18-7" aria-hidden="true" tabindex="-1"></a>    runN (n<span class="op">-</span><span class="dv">1</span>) f a&#39;</span></code></pre></div>
</div>
<p>it takes a function (in our case <code>step</code>) and applies that function <code>n</code>
times, returning the final result.</p>
<p>Finally, we can run the <code>GAContext</code>, a newtype wrapper for the <code>RWS</code>
monad, with <code>runRWS</code> and <code>evalRWS</code>:</p>
<div id="cb19" class="sourceCode">
<div class="sourceCode" id="cb19"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- from a new rng, run the genetic algorithm</span></span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a><span class="ot">evalGA ::</span> <span class="dt">Ord</span> i <span class="ot">=&gt;</span> <span class="dt">GAConfig</span> i <span class="ot">-&gt;</span> <span class="dt">IO</span> (<span class="dt">GASnapshot</span> i, [<span class="dt">T.Text</span>])</span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a>evalGA cfg <span class="ot">=</span> newPureMT <span class="op">&gt;&gt;=</span> (<span class="fu">return</span> <span class="op">.</span> evalGASeed cfg)</span>
<span id="cb19-4"><a href="#cb19-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-5"><a href="#cb19-5" aria-hidden="true" tabindex="-1"></a><span class="co">-- from a user-supplied rng, run the genetic algorithm</span></span>
<span id="cb19-6"><a href="#cb19-6" aria-hidden="true" tabindex="-1"></a><span class="ot">evalGASeed ::</span> <span class="dt">Ord</span> i <span class="ot">=&gt;</span> <span class="dt">GAConfig</span> i <span class="ot">-&gt;</span> <span class="dt">PureMT</span> <span class="ot">-&gt;</span> (<span class="dt">GASnapshot</span> i, [<span class="dt">T.Text</span>])</span>
<span id="cb19-7"><a href="#cb19-7" aria-hidden="true" tabindex="-1"></a>evalGASeed cfg rng <span class="ot">=</span> evalRWS (ctx runGA) cfg rng</span>
<span id="cb19-8"><a href="#cb19-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb19-9"><a href="#cb19-9" aria-hidden="true" tabindex="-1"></a><span class="co">-- from a user-supplied rng, run the genetic algorithm and return the updated seed</span></span>
<span id="cb19-10"><a href="#cb19-10" aria-hidden="true" tabindex="-1"></a><span class="ot">runGASeed ::</span> <span class="dt">Ord</span> i <span class="ot">=&gt;</span> <span class="dt">GAConfig</span> i <span class="ot">-&gt;</span> <span class="dt">PureMT</span> <span class="ot">-&gt;</span> (<span class="dt">GASnapshot</span> i, <span class="dt">PureMT</span>, [<span class="dt">T.Text</span>])</span>
<span id="cb19-11"><a href="#cb19-11" aria-hidden="true" tabindex="-1"></a>runGASeed cfg rng <span class="ot">=</span> runRWS (ctx runGA) cfg rng</span></code></pre></div>
</div>
<p>With this, all the user needs to do is define their genetic operators
and fitness functions for their own individual representation, and they
should be able to call one of these three functions to run the genetic
algorithm.</p>
<h2 id="using-the-library">Using the library</h2>
<p>Let’s see an example of it in action with a very simple problem:
maximizing the number of 1’s in a 500-bit binary string. Source can be
found in
<a href="https://github.com/oinoom/gabble/blob/dev-0.1.0.0/src/BinaryInd.hs"><code>BinaryInd.hs</code></a>.</p>
<h3 id="representation">Representation</h3>
<p>We’ll represent the binary string as a list of <code>Bool</code>:</p>
<div id="cb20" class="sourceCode">
<div class="sourceCode" id="cb20"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="kw">data</span> <span class="dt">BinaryInd</span> <span class="ot">=</span> <span class="dt">BI</span> [<span class="dt">Bool</span>] <span class="kw">deriving</span> (<span class="dt">Show</span>)</span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Eq</span> <span class="dt">BinaryInd</span> <span class="kw">where</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a>    (<span class="dt">BI</span> b1) <span class="op">==</span> (<span class="dt">BI</span> b2) <span class="ot">=</span> b1 <span class="op">==</span> b2</span></code></pre></div>
</div>
<h3 id="fitness-function">Fitness function</h3>
<p>We can start simply by defining the fitness function for this individual
representation, which is just the number of <code>True</code> booleans in the list:</p>
<div id="cb21" class="sourceCode">
<div class="sourceCode" id="cb21"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- count the number of `True` bools in the chromosome</span></span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a><span class="ot">score ::</span> <span class="dt">BinaryInd</span> <span class="ot">-&gt;</span> <span class="dt">Double</span></span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a>score (<span class="dt">BI</span> bs) <span class="ot">=</span> <span class="fu">fromIntegral</span> <span class="op">.</span> <span class="fu">length</span> <span class="op">.</span> <span class="fu">filter</span> <span class="fu">id</span> <span class="op">$</span> bs</span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a><span class="kw">instance</span> <span class="dt">Ord</span> <span class="dt">BinaryInd</span> <span class="kw">where</span></span>
<span id="cb21-6"><a href="#cb21-6" aria-hidden="true" tabindex="-1"></a>    b1 <span class="ot">`compare`</span> b2 <span class="ot">=</span> (score b1) <span class="ot">`compare`</span> (score b2)</span></code></pre></div>
</div>
<h3 id="random-individuals">Random individuals</h3>
<p>Next we can define a function to create a new and random bit string of
length 500:</p>
<div id="cb22" class="sourceCode">
<div class="sourceCode" id="cb22"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- create an individual, represented by a list, by</span></span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a><span class="co">-- initializing its elements randomly</span></span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a><span class="ot">new ::</span> <span class="dt">GAContext</span> <span class="dt">BinaryInd</span> <span class="dt">BinaryInd</span></span>
<span id="cb22-4"><a href="#cb22-4" aria-hidden="true" tabindex="-1"></a>new <span class="ot">=</span> <span class="fu">fmap</span> <span class="dt">BI</span> <span class="op">$</span> replicateM <span class="dv">500</span> randomBool</span></code></pre></div>
</div>
<h3 id="mutation">Mutation</h3>
<p>We’ll also need to provide a way to mutate our individual:</p>
<div id="cb23" class="sourceCode">
<div class="sourceCode" id="cb23"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- mutate a binary string representation</span></span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a><span class="ot">mutate ::</span> <span class="dt">BinaryInd</span> <span class="ot">-&gt;</span> <span class="dt">GAContext</span> <span class="dt">BinaryInd</span> <span class="dt">BinaryInd</span></span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a>mutate ind<span class="op">@</span>(<span class="dt">BI</span> bs) <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- grab individual and gene mutation rates</span></span>
<span id="cb23-5"><a href="#cb23-5" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Config</span>{mutationRateGene, mutationRateInd} <span class="ot">&lt;-</span> ask</span>
<span id="cb23-6"><a href="#cb23-6" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- get a random double</span></span>
<span id="cb23-7"><a href="#cb23-7" aria-hidden="true" tabindex="-1"></a>        indp <span class="ot">&lt;-</span> randomD</span>
<span id="cb23-8"><a href="#cb23-8" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- if the value is less than mutation rate for an individual</span></span>
<span id="cb23-9"><a href="#cb23-9" aria-hidden="true" tabindex="-1"></a>        <span class="kw">if</span> indp <span class="op">&lt;</span> mutationRateInd <span class="kw">then</span></span>
<span id="cb23-10"><a href="#cb23-10" aria-hidden="true" tabindex="-1"></a>            <span class="co">-- mutate each bit with `mutationRateGene` probability</span></span>
<span id="cb23-11"><a href="#cb23-11" aria-hidden="true" tabindex="-1"></a>            <span class="fu">fmap</span> <span class="dt">BI</span> <span class="op">$</span> <span class="fu">mapM</span> (mutateBool mutationRateGene) bs</span>
<span id="cb23-12"><a href="#cb23-12" aria-hidden="true" tabindex="-1"></a>        <span class="kw">else</span></span>
<span id="cb23-13"><a href="#cb23-13" aria-hidden="true" tabindex="-1"></a>            <span class="co">-- return the unaltered individual</span></span>
<span id="cb23-14"><a href="#cb23-14" aria-hidden="true" tabindex="-1"></a>            <span class="fu">return</span> ind</span>
<span id="cb23-15"><a href="#cb23-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb23-16"><a href="#cb23-16" aria-hidden="true" tabindex="-1"></a><span class="co">-- mutate a boolean by flipping it</span></span>
<span id="cb23-17"><a href="#cb23-17" aria-hidden="true" tabindex="-1"></a><span class="ot">mutateBool ::</span> <span class="dt">Double</span> <span class="ot">-&gt;</span> <span class="dt">Bool</span> <span class="ot">-&gt;</span> <span class="dt">GAContext</span> a <span class="dt">Bool</span></span>
<span id="cb23-18"><a href="#cb23-18" aria-hidden="true" tabindex="-1"></a>mutateBool p x <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb23-19"><a href="#cb23-19" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- get a random double</span></span>
<span id="cb23-20"><a href="#cb23-20" aria-hidden="true" tabindex="-1"></a>    indp <span class="ot">&lt;-</span> randomD</span>
<span id="cb23-21"><a href="#cb23-21" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- determine whether or not to flip the bit</span></span>
<span id="cb23-22"><a href="#cb23-22" aria-hidden="true" tabindex="-1"></a>    <span class="fu">return</span> <span class="op">$</span> <span class="kw">if</span> indp <span class="op">&lt;</span> p <span class="kw">then</span> <span class="fu">not</span> x <span class="kw">else</span> x</span></code></pre></div>
</div>
<p>In <code>mutate</code>, we get a random double with a helper function <code>randomD</code> and
decide whether the given individual is to be mutated at all. If it is to
be mutated, iterate over the given individual and determine whether the
genes themselves (the bits) should be mutated with some given
probability with <code>mutateBool</code>.</p>
<h3 id="crossover">Crossover</h3>
<p>To cross two parents, we’ll generate a bitmask that will inform us
whether a given gene should be taken from the first parent or the second
parent:</p>
<div id="cb24" class="sourceCode">
<div class="sourceCode" id="cb24"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb24-1"><a href="#cb24-1" aria-hidden="true" tabindex="-1"></a><span class="co">-- recombine two individuals from the population</span></span>
<span id="cb24-2"><a href="#cb24-2" aria-hidden="true" tabindex="-1"></a><span class="ot">crossover ::</span> <span class="dt">BinaryInd</span> <span class="ot">-&gt;</span> <span class="dt">BinaryInd</span> <span class="ot">-&gt;</span> <span class="dt">GAContext</span> <span class="dt">BinaryInd</span> <span class="dt">BinaryInd</span></span>
<span id="cb24-3"><a href="#cb24-3" aria-hidden="true" tabindex="-1"></a>crossover (<span class="dt">BI</span> i1) (<span class="dt">BI</span> i2) <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb24-4"><a href="#cb24-4" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- get the crossover rate</span></span>
<span id="cb24-5"><a href="#cb24-5" aria-hidden="true" tabindex="-1"></a>        <span class="dt">Config</span>{crossoverRate} <span class="ot">&lt;-</span> ask</span>
<span id="cb24-6"><a href="#cb24-6" aria-hidden="true" tabindex="-1"></a>        <span class="co">-- get a random double</span></span>
<span id="cb24-7"><a href="#cb24-7" aria-hidden="true" tabindex="-1"></a>        indp <span class="ot">&lt;-</span> randomD</span>
<span id="cb24-8"><a href="#cb24-8" aria-hidden="true" tabindex="-1"></a>        <span class="kw">if</span> indp <span class="op">&lt;</span> crossoverRate <span class="kw">then</span> <span class="kw">do</span> <span class="co">-- perform crossover</span></span>
<span id="cb24-9"><a href="#cb24-9" aria-hidden="true" tabindex="-1"></a>            <span class="co">-- get booleans specifying which gene to take</span></span>
<span id="cb24-10"><a href="#cb24-10" aria-hidden="true" tabindex="-1"></a>            code <span class="ot">&lt;-</span> replicateM (<span class="fu">length</span> i1) randomBool</span>
<span id="cb24-11"><a href="#cb24-11" aria-hidden="true" tabindex="-1"></a>            <span class="co">-- choose genetic material from first or second parent</span></span>
<span id="cb24-12"><a href="#cb24-12" aria-hidden="true" tabindex="-1"></a>            <span class="kw">let</span> eitherOr <span class="ot">=</span> (\takeThis this that <span class="ot">-&gt;</span> <span class="kw">if</span> takeThis <span class="kw">then</span> this <span class="kw">else</span> that)</span>
<span id="cb24-13"><a href="#cb24-13" aria-hidden="true" tabindex="-1"></a>            <span class="co">-- perform uniform crossover</span></span>
<span id="cb24-14"><a href="#cb24-14" aria-hidden="true" tabindex="-1"></a>            <span class="fu">return</span> <span class="op">.</span> <span class="dt">BI</span> <span class="op">$</span> <span class="fu">zipWith3</span> eitherOr code i1 i2</span>
<span id="cb24-15"><a href="#cb24-15" aria-hidden="true" tabindex="-1"></a>        <span class="kw">else</span> <span class="kw">do</span></span>
<span id="cb24-16"><a href="#cb24-16" aria-hidden="true" tabindex="-1"></a>            <span class="co">-- choose the genetic material from one of the parents</span></span>
<span id="cb24-17"><a href="#cb24-17" aria-hidden="true" tabindex="-1"></a>            chooseFirstParent <span class="ot">&lt;-</span> randomBool</span>
<span id="cb24-18"><a href="#cb24-18" aria-hidden="true" tabindex="-1"></a>            <span class="fu">return</span> <span class="op">.</span> <span class="dt">BI</span> <span class="op">$</span> <span class="kw">if</span> chooseFirstParent <span class="kw">then</span> i1 <span class="kw">else</span> i2</span></code></pre></div>
</div>
<p>This type of crossover is called <a href="https://en.wikipedia.org/wiki/Crossover_(genetic_algorithm)#Uniform_crossover">uniform
crossover</a>.</p>
<h3 id="selection">Selection</h3>
<p>Our selection scheme is simple: take the best 20% of the population:</p>
<div id="cb25" class="sourceCode">
<div class="sourceCode" id="cb25"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb25-1"><a href="#cb25-1" aria-hidden="true" tabindex="-1"></a><span class="ot">select ::</span> <span class="dt">Ord</span> a <span class="ot">=&gt;</span> <span class="dt">Vector</span> a <span class="ot">-&gt;</span> <span class="dt">GAContext</span> a (<span class="dt">Vector</span> a)</span>
<span id="cb25-2"><a href="#cb25-2" aria-hidden="true" tabindex="-1"></a>select pop <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb25-3"><a href="#cb25-3" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- get the population size</span></span>
<span id="cb25-4"><a href="#cb25-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Config</span>{popSize} <span class="ot">&lt;-</span> ask</span>
<span id="cb25-5"><a href="#cb25-5" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- get the number of individuals to breed</span></span>
<span id="cb25-6"><a href="#cb25-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> numToSelect <span class="ot">=</span> <span class="fu">round</span> <span class="op">$</span> <span class="fl">0.2</span> <span class="op">*</span> (<span class="fu">fromIntegral</span> popSize)</span>
<span id="cb25-7"><a href="#cb25-7" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- get the top 20% of the best-performing individuals</span></span>
<span id="cb25-8"><a href="#cb25-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> selectedParents <span class="ot">=</span> V.take numToSelect <span class="op">.</span> V.reverse <span class="op">$</span> V.modify <span class="fu">sort</span> pop</span>
<span id="cb25-9"><a href="#cb25-9" aria-hidden="true" tabindex="-1"></a>    <span class="fu">return</span> selectedParents</span></code></pre></div>
</div>
<h3 id="optimizing-our-bit-string">Optimizing our bit string</h3>
<p>Almost there! It’s time to run the genetic algorithm in our main
function by instantiating a <code>GAConfig</code> with the functions we’ve defined:</p>
<div id="cb26" class="sourceCode">
<div class="sourceCode" id="cb26"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a><span class="kw">import</span> <span class="kw">qualified</span> <span class="dt">BinaryInd</span> <span class="kw">as</span> <span class="dt">BI</span></span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a><span class="ot">main ::</span> <span class="dt">IO</span> ()</span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a>main <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> cfg <span class="ot">=</span> <span class="dt">Config</span> {</span>
<span id="cb26-7"><a href="#cb26-7" aria-hidden="true" tabindex="-1"></a>        mutationRateInd <span class="ot">=</span> <span class="fl">0.8</span></span>
<span id="cb26-8"><a href="#cb26-8" aria-hidden="true" tabindex="-1"></a>      , mutationRateGene <span class="ot">=</span> <span class="fl">0.02</span></span>
<span id="cb26-9"><a href="#cb26-9" aria-hidden="true" tabindex="-1"></a>      , crossoverRate <span class="ot">=</span> <span class="fl">0.7</span></span>
<span id="cb26-10"><a href="#cb26-10" aria-hidden="true" tabindex="-1"></a>      , popSize <span class="ot">=</span> <span class="dv">100</span></span>
<span id="cb26-11"><a href="#cb26-11" aria-hidden="true" tabindex="-1"></a>      , mutate <span class="ot">=</span> BI.mutate</span>
<span id="cb26-12"><a href="#cb26-12" aria-hidden="true" tabindex="-1"></a>      , crossover <span class="ot">=</span> BI.crossover</span>
<span id="cb26-13"><a href="#cb26-13" aria-hidden="true" tabindex="-1"></a>      , randomIndividual <span class="ot">=</span> BI.new</span>
<span id="cb26-14"><a href="#cb26-14" aria-hidden="true" tabindex="-1"></a>      , selectionMethod <span class="ot">=</span> BI.select</span>
<span id="cb26-15"><a href="#cb26-15" aria-hidden="true" tabindex="-1"></a>      , fitness <span class="ot">=</span> BI.score</span>
<span id="cb26-16"><a href="#cb26-16" aria-hidden="true" tabindex="-1"></a>      , numGenerations <span class="ot">=</span> <span class="dv">200</span></span>
<span id="cb26-17"><a href="#cb26-17" aria-hidden="true" tabindex="-1"></a>      , hofSize <span class="ot">=</span> <span class="dv">1</span></span>
<span id="cb26-18"><a href="#cb26-18" aria-hidden="true" tabindex="-1"></a>      , logFunc <span class="ot">=</span> logHOF</span>
<span id="cb26-19"><a href="#cb26-19" aria-hidden="true" tabindex="-1"></a>    }</span>
<span id="cb26-20"><a href="#cb26-20" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-21"><a href="#cb26-21" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- run the genetic algorithm</span></span>
<span id="cb26-22"><a href="#cb26-22" aria-hidden="true" tabindex="-1"></a>    (finalSnapshot, progress) <span class="ot">&lt;-</span> evalGA cfg</span>
<span id="cb26-23"><a href="#cb26-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb26-24"><a href="#cb26-24" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- output the best fitnesses as they&#39;re found</span></span>
<span id="cb26-25"><a href="#cb26-25" aria-hidden="true" tabindex="-1"></a>    <span class="fu">mapM_</span> (<span class="fu">putStrLn</span> <span class="op">.</span> T.unpack) progress</span></code></pre></div>
</div>
<p>We call the <code>evalGA</code> function on our configuration to yield the final
snapshot containing the hof. We can log the progress of the genetic
algorithm by printing the logging messages written with <code>tell</code> and
<code>logFunc</code>.</p>
<p>The <code>logHOF</code> function puts the scores of the <code>HOF</code> into CSV format for
easy graphing:</p>
<div id="cb27" class="sourceCode">
<div class="sourceCode" id="cb27"><pre class="sourceCode haskell"><code class="sourceCode haskell"><span id="cb27-1"><a href="#cb27-1" aria-hidden="true" tabindex="-1"></a><span class="ot">logHOF ::</span> <span class="dt">Ord</span> a <span class="ot">=&gt;</span> <span class="dt">GASnapshot</span> a <span class="ot">-&gt;</span> <span class="dt">GAContext</span> a ()</span>
<span id="cb27-2"><a href="#cb27-2" aria-hidden="true" tabindex="-1"></a>logHOF <span class="dt">Snapshot</span>{hof, generationNumber} <span class="ot">=</span> <span class="kw">do</span></span>
<span id="cb27-3"><a href="#cb27-3" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- get the fitness function</span></span>
<span id="cb27-4"><a href="#cb27-4" aria-hidden="true" tabindex="-1"></a>    <span class="dt">Config</span> {fitness} <span class="ot">&lt;-</span> ask</span>
<span id="cb27-5"><a href="#cb27-5" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- get string representations of the best individuals</span></span>
<span id="cb27-6"><a href="#cb27-6" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> best <span class="ot">=</span> <span class="fu">map</span> (T.pack <span class="op">.</span> <span class="fu">show</span> <span class="op">.</span> fitness) <span class="op">$</span> Heap.toList hof</span>
<span id="cb27-7"><a href="#cb27-7" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- craft the comma-separated line</span></span>
<span id="cb27-8"><a href="#cb27-8" aria-hidden="true" tabindex="-1"></a>    <span class="kw">let</span> msg <span class="ot">=</span> [T.concat <span class="op">$</span> intersperse (T.pack <span class="st">&quot;,&quot;</span>) best]</span>
<span id="cb27-9"><a href="#cb27-9" aria-hidden="true" tabindex="-1"></a>    <span class="co">-- log the line</span></span>
<span id="cb27-10"><a href="#cb27-10" aria-hidden="true" tabindex="-1"></a>    tell msg</span></code></pre></div>
</div>
<p>And here we can see how the GA improves fitness across generations:</p>
<p><img src="../images/progress.png" /></p>
<p>We can see that the GA is does pretty well for our little problem,
making it most of the way towards an optimal solution within the first
100 generations. Not bad!</p>
<h2 id="wrapping-up">Wrapping up</h2>
<p>We’ve prototyped a library that can allow us to see if our given (and
contrived) problem could stand to benefit from a genetic algorithm. I
realize I’ve glossed over some details here, such as the <code>randomD</code> and
<code>randomBool</code> definitions; if you want code that compiles, you’ll need to
consult the <a href="https://github.com/oinoom/gabble">source</a>.</p>
<p>I also briefly mentioned the resources for recursion schemes, but if
you’d like more examples (namely with <code>cata</code>, <code>cataM</code>, and <code>anaM</code>) I’ve
created a recursion-scheme-based analogue to <code>BinaryInd</code> in
<a href="https://github.com/oinoom/gabble/blob/dev-0.1.0.0/src/BinaryIndRec.hs"><code>BinaryIndRec.hs</code></a>.</p>]]></summary>
</entry>

</feed>
