<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Python on adventures in optimization</title>
    <link>https://ryanjoneil.dev/tags/python/</link>
    <description>Recent content in Python on adventures in optimization</description>
    <generator>Hugo -- 0.151.1</generator>
    <language>en</language>
    <lastBuildDate>Mon, 11 Nov 2024 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://ryanjoneil.dev/tags/python/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>👔 Hierarchical Optimization with HiGHS</title>
      <link>https://ryanjoneil.dev/posts/2024-11-11-hierarchical-optimization-with-highs/</link>
      <pubDate>Mon, 11 Nov 2024 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2024-11-11-hierarchical-optimization-with-highs/</guid>
      <description>Managing trade-offs between different objectives with HiGHS.</description>
      <content:encoded><![CDATA[<p>In the <a href="../2024-11-08-hierarchical-optimization-with-gurobi/">last post</a>, we used Gurobi&rsquo;s hierarchical optimization features to compute the Pareto front for primary and secondary objectives in an assignment problem. This relied on Gurobi&rsquo;s <code>setObjectiveN</code> method and its internal code for managing hierarchical problems.</p>
<p>Some practitioners may need to do this without access to a commercial license. This post adapts the previous example to use HiGHS and its native Python interface, <a href="https://pypi.org/project/highspy/"><code>highspy</code></a>. It&rsquo;s also useful to see what the procedure is in order to understand it better. This isn&rsquo;t exactly what I&rsquo;d call <em>hard</em>, but it is <em>easy to mess up</em>.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<h2 id="code">Code</h2>
<p>The mathematical models are available in the last post, so I won&rsquo;t restate them here. We start in roughly the same manner as before<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>: create a binary variable for each worker-patient pair, add assignment problem constraints, and state the primary objective.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">from</span> <span style="color:#ff7b72">itertools</span> <span style="color:#ff7b72">import</span> product
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">highspy</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>n <span style="color:#ff7b72;font-weight:bold">=</span> len(data[<span style="color:#a5d6ff">&#34;cost&#34;</span>])
</span></span><span style="display:flex;"><span>workers <span style="color:#ff7b72;font-weight:bold">=</span> range(n)
</span></span><span style="display:flex;"><span>patients <span style="color:#ff7b72;font-weight:bold">=</span> range(n)
</span></span><span style="display:flex;"><span>workers_patients <span style="color:#ff7b72;font-weight:bold">=</span> list(product(workers, patients))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>h <span style="color:#ff7b72;font-weight:bold">=</span> highspy<span style="color:#ff7b72;font-weight:bold">.</span>Highs()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># x[w,p] = 1 if worker w is assigned to patient p.</span>
</span></span><span style="display:flex;"><span>x <span style="color:#ff7b72;font-weight:bold">=</span> {(w, p): h<span style="color:#ff7b72;font-weight:bold">.</span>addBinary(obj<span style="color:#ff7b72;font-weight:bold">=</span>data[<span style="color:#a5d6ff">&#34;cost&#34;</span>][w][p]) <span style="color:#ff7b72">for</span> w, p <span style="color:#ff7b72;font-weight:bold">in</span> workers_patients}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Each worker is assigned to one patient.</span>
</span></span><span style="display:flex;"><span>h<span style="color:#ff7b72;font-weight:bold">.</span>addConstrs(sum(x[w, p] <span style="color:#ff7b72">for</span> p <span style="color:#ff7b72;font-weight:bold">in</span> patients) <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">1</span> <span style="color:#ff7b72">for</span> w <span style="color:#ff7b72;font-weight:bold">in</span> workers)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Each patient is assigned one worker.</span>
</span></span><span style="display:flex;"><span>h<span style="color:#ff7b72;font-weight:bold">.</span>addConstrs(sum(x[w, p] <span style="color:#ff7b72">for</span> w <span style="color:#ff7b72;font-weight:bold">in</span> workers) <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">1</span> <span style="color:#ff7b72">for</span> p <span style="color:#ff7b72;font-weight:bold">in</span> patients)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Primary objective: minimize cost.</span>
</span></span><span style="display:flex;"><span>h<span style="color:#ff7b72;font-weight:bold">.</span>setMinimize()
</span></span><span style="display:flex;"><span>h<span style="color:#ff7b72;font-weight:bold">.</span>solve()
</span></span><span style="display:flex;"><span>cost <span style="color:#ff7b72;font-weight:bold">=</span> h<span style="color:#ff7b72;font-weight:bold">.</span>getObjectiveValue()
</span></span></code></pre></div><p>Note that if the costs and affinities were lists instead of matrices, we could have used <code>h.addBinaries</code> instead of <code>h.addBinary</code>.</p>
<p>From here we&rsquo;ll be solving the model twice for every value of alpha. These expressions for total cost and affinity will make a code a little cleaner.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>cost_expr <span style="color:#ff7b72;font-weight:bold">=</span> sum(data[<span style="color:#a5d6ff">&#34;cost&#34;</span>][w][p] <span style="color:#ff7b72;font-weight:bold">*</span> x[w, p] <span style="color:#ff7b72">for</span> w, p <span style="color:#ff7b72;font-weight:bold">in</span> workers_patients)
</span></span><span style="display:flex;"><span>affinity_expr <span style="color:#ff7b72;font-weight:bold">=</span> sum(data[<span style="color:#a5d6ff">&#34;affinity&#34;</span>][w][p] <span style="color:#ff7b72;font-weight:bold">*</span> x[w, p] <span style="color:#ff7b72">for</span> w, p <span style="color:#ff7b72;font-weight:bold">in</span> workers_patients)
</span></span></code></pre></div><p>Now comes the hierarchical optimization logic. For every value of alpha, we find the best affinity possible while keeping cost within alpha of its best possible value.</p>
<ul>
<li>Update the objective function to maximize affinity (see the calls to <code>h.changeColCost</code> and <code>h.setMaximize</code>).</li>
<li>Constrain the cost to be within alpha of the original optimal cost (see <code>cost_cons</code>).</li>
<li>Re-optimize and save the maximal affinity.</li>
</ul>
<p>Now we constrain the affinity and re-optimize cost.<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<ul>
<li>Update the objective function to minimize cost again.</li>
<li>Constrain the affinity.</li>
</ul>
<p>Once that&rsquo;s done, we remove the additional constraints and repeat for a new value of alpha.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">for</span> alpha <span style="color:#ff7b72;font-weight:bold">in</span> alphas:
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Secondary objective: maximize affinity.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> (w, p), x_wp <span style="color:#ff7b72;font-weight:bold">in</span> x<span style="color:#ff7b72;font-weight:bold">.</span>items():
</span></span><span style="display:flex;"><span>        h<span style="color:#ff7b72;font-weight:bold">.</span>changeColCost(x_wp<span style="color:#ff7b72;font-weight:bold">.</span>index, data[<span style="color:#a5d6ff">&#34;affinity&#34;</span>][w][p])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Constrain cost to be within alpha of maximum.</span>
</span></span><span style="display:flex;"><span>    cost_cons <span style="color:#ff7b72;font-weight:bold">=</span> h<span style="color:#ff7b72;font-weight:bold">.</span>addConstr(cost_expr <span style="color:#ff7b72;font-weight:bold">&lt;=</span> (<span style="color:#a5d6ff">1</span> <span style="color:#ff7b72;font-weight:bold">+</span> alpha) <span style="color:#ff7b72;font-weight:bold">*</span> cost)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    h<span style="color:#ff7b72;font-weight:bold">.</span>setMaximize()
</span></span><span style="display:flex;"><span>    h<span style="color:#ff7b72;font-weight:bold">.</span>solve()
</span></span><span style="display:flex;"><span>    affinity <span style="color:#ff7b72;font-weight:bold">=</span> h<span style="color:#ff7b72;font-weight:bold">.</span>getObjectiveValue()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Re-optimize with original cost objective, constraining affinity.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> (w, p), x_wp <span style="color:#ff7b72;font-weight:bold">in</span> x<span style="color:#ff7b72;font-weight:bold">.</span>items():
</span></span><span style="display:flex;"><span>        h<span style="color:#ff7b72;font-weight:bold">.</span>changeColCost(x_wp<span style="color:#ff7b72;font-weight:bold">.</span>index, data[<span style="color:#a5d6ff">&#34;cost&#34;</span>][w][p])
</span></span><span style="display:flex;"><span>    affinity_cons <span style="color:#ff7b72;font-weight:bold">=</span> h<span style="color:#ff7b72;font-weight:bold">.</span>addConstr(affinity_expr <span style="color:#ff7b72;font-weight:bold">&gt;=</span> affinity)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    h<span style="color:#ff7b72;font-weight:bold">.</span>setMinimize()
</span></span><span style="display:flex;"><span>    h<span style="color:#ff7b72;font-weight:bold">.</span>solve()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">yield</span> alpha, h<span style="color:#ff7b72;font-weight:bold">.</span>getObjectiveValue(), affinity
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Remove cost and affinity constraints for</span>
</span></span><span style="display:flex;"><span>    h<span style="color:#ff7b72;font-weight:bold">.</span>removeConstr(cost_cons)
</span></span><span style="display:flex;"><span>    h<span style="color:#ff7b72;font-weight:bold">.</span>removeConstr(affinity_cons)
</span></span></code></pre></div><p>Encouragingly, running this using the <code>model.py</code> linked below gives the same values as the Gurobi model, albeit not as quickly. Floating point values are rounded for readability.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-txt" data-lang="txt"><span style="display:flex;"><span>| alpha | cost     | affinity |
</span></span><span style="display:flex;"><span>| ----- | -------- | -------- |
</span></span><span style="display:flex;"><span>| 0.0   | 11212.0  | 53816.0  |
</span></span><span style="display:flex;"><span>| 0.05  | 11761.0  | 74001.0  |
</span></span><span style="display:flex;"><span>| 0.1   | 12332.0  | 79981.0  |
</span></span><span style="display:flex;"><span>| 0.15  | 12886.0  | 83103.0  |
</span></span><span style="display:flex;"><span>| 0.2   | 13454.0  | 85394.0  |
</span></span><span style="display:flex;"><span>| 0.25  | 13996.0  | 87136.0  |
</span></span><span style="display:flex;"><span>| 0.3   | 14557.0  | 88546.0  |
</span></span><span style="display:flex;"><span>| 0.35  | 15125.0  | 89751.0  |
</span></span><span style="display:flex;"><span>| 0.4   | 15670.0  | 90664.0  |
</span></span><span style="display:flex;"><span>| 0.45  | 16255.0  | 91345.0  |
</span></span><span style="display:flex;"><span>| 0.5   | 16816.0  | 91997.0  |
</span></span><span style="display:flex;"><span>| 0.55  | 17370.0  | 92537.0  |
</span></span><span style="display:flex;"><span>| 0.6   | 17924.0  | 93012.0  |
</span></span><span style="display:flex;"><span>| 0.65  | 18495.0  | 93491.0  |
</span></span><span style="display:flex;"><span>| 0.7   | 19055.0  | 93829.0  |
</span></span><span style="display:flex;"><span>| 0.75  | 19591.0  | 94228.0  |
</span></span><span style="display:flex;"><span>| 0.8   | 20167.0  | 94530.0  |
</span></span><span style="display:flex;"><span>| 0.85  | 20737.0  | 94833.0  |
</span></span><span style="display:flex;"><span>| 0.9   | 21295.0  | 95114.0  |
</span></span><span style="display:flex;"><span>| 0.95  | 21812.0  | 95361.0  |
</span></span><span style="display:flex;"><span>| 1.0   | 22402.0  | 95613.0  |
</span></span></code></pre></div><h2 id="resources">Resources</h2>
<ul>
<li><a href="/files/2024-11-11-hierarchical-optimization-with-highs/model.py"><code>model.py</code></a> hierarchical objectives HiGHS model</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>It gets even easier to mess up with more than two objectives.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Isn&rsquo;t it nice that MIP modeling is similar across different APIs?&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Exercise for the reader: why do we need to re-optimize cost?&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>👔 Hierarchical Optimization with Gurobi</title>
      <link>https://ryanjoneil.dev/posts/2024-11-08-hierarchical-optimization-with-gurobi/</link>
      <pubDate>Fri, 08 Nov 2024 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2024-11-08-hierarchical-optimization-with-gurobi/</guid>
      <description>Managing trade-offs between different objectives with Gurobi.</description>
      <content:encoded><![CDATA[<p>One of the first technology choices to make when setting up an optimization stack is which modeling interface to use. Even if we restrict our choices to Python interfaces for MIP modeling, there are lots of options to consider.</p>
<p>If you use a specific solver, you can opt for its native Python interface. Examples include libraries like <a href="https://pypi.org/project/gurobipy/"><code>gurobipy</code></a>, <a href="https://docs.mosek.com/latest/pythonfusion/index.html">Fusion</a>, <a href="https://pypi.org/project/highspy/"><code>highspy</code></a>, or <a href="https://github.com/scipopt/PySCIPOpt"><code>PySCIPOpt</code></a>. This approach provides access to important solver-specific features such as lazy constraints, heuristics, and various solver settings. However, it can also lock you into a solver before ready for that.</p>
<p>You can also choose a modeling API that targets multiple solvers. In the Python ecosystem. These are libraries like <a href="https://amplpy.ampl.com/en/latest/"><code>amplpy</code></a>, <a href="http://www.pyomo.org/">Pyomo</a>, <a href="https://github.com/metab0t/PyOptInterface">PyOptInterface</a>, and <a href="https://linopy.readthedocs.io/en/latest/"><code>linopy</code></a>. These interfaces target multiple solver backends (both open source and commercial) and provide a subset of the functionality of each. Since they make it easy to switch between solvers, this is usually where I start.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<h2 id="hierarchical-assignment">Hierarchical assignment</h2>
<p>However, there are plenty of times when solver-specific APIs are useful, or even critical. One example is hierarchical optimization. This is a simple technique for managing trade-offs between multiple objectives in a problem. Let&rsquo;s look at an example.</p>
<p>Imagine we are assigning in-home health care workers ($w \in W$) to patients ($p \in P$). For simplicity, let&rsquo;s say we have $n$ workers and $n$ patients, and we are assigning them one-to-one. Each worker has a given cost ($c_{wp}$) of assignment to each patient, which may reflect something like the travel time to get to them. We want to assign each worker to exactly one patient while minimizing the overall cost.</p>
<h3 id="model">Model</h3>
<p>So far, what we have is a simple linear sum assignment problem.</p>
<p>$$
\begin{align*}
&amp; \text{min}  &amp;&amp; z = \sum_{wp} c_{wp} x_{wp} \\
&amp; \text{s.t.} &amp;&amp; \sum_w x_{wp} = 1 &amp;&amp; \forall \quad p \in P \\
&amp;             &amp;&amp; \sum_p x_{wp} = 1 &amp;&amp; \forall \quad w \in W \\
&amp;             &amp;&amp; x \in \{0,1\}^{|W \times P|}
\end{align*}
$$</p>
<p>Solving this model gives us the minimum cost assignment. That&rsquo;s all well and good, but now say we have a secondary objective of maximizing <em>affinity</em> of workers to patients ($a_{wp}$). That is, we want to <em>prefer assignments that increase overall affinity while still minimizing cost</em>. This is actually a common goal in health care scheduling: if possible, send the same worker to a given patient that you usually send.</p>
<p>Hierarchical optimization gives us a simple way to solve this problem. First, we optimize the model as stated above. This gives us an optimal objective value $z^*$. Then we re-solve the same optimization model, while constraining the cost to be $z^*$ and using the secondary objective function. This says to the optimizer, &ldquo;improve the affinity as much as you can, but keep the cost optimal.&rdquo;</p>
<p>$$
\begin{align*}
&amp; \text{max}  &amp;&amp; w = \sum_{wp} a_{wp} x_{wp} \\
&amp; \text{s.t.} &amp;&amp; \sum_{wp} c_{wp} x_{wp} \le z^* \\
&amp;             &amp;&amp; \sum_w x_{wp} = 1 &amp;&amp; \forall \quad p \in P \\
&amp;             &amp;&amp; \sum_p x_{wp} = 1 &amp;&amp; \forall \quad w \in W \\
&amp;             &amp;&amp; x \in \{0,1\}^{|W \times P|}
\end{align*}
$$</p>
<p>From here, the natural question becomes: what if we trade off some cost for affinity? If we&rsquo;re willing to increase cost by some percentage, how much more affinity do we get? We can do this by setting a constant $\alpha \ge 0$ and solving the model a number of times.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
<p>$$
\begin{align*}
&amp; \text{max}   &amp;&amp; w = \sum_{wp} a_{wp} x_{wp} \\
&amp; \text{s.t.} &amp;&amp; \sum_{wp} c_{wp} x_{wp} \le (1 + \alpha) z^* \\
&amp;             &amp;&amp; \sum_w x_{wp} = 1 &amp;&amp; \forall \quad p \in P \\
&amp;             &amp;&amp; \sum_p x_{wp} = 1 &amp;&amp; \forall \quad w \in W \\
&amp;             &amp;&amp; x \in \{0,1\}^{|W \times P|}
\end{align*}
$$</p>
<p>For example, if $\alpha = 0.05$, then we&rsquo;re willing to accept a 5% increase in overall cost to improve affinity. Setting different values of $\alpha$ lets us explore the space of that trade-off and its impact on cost and affinity.</p>
<p>Once we solve this and get the optimal affinity ($w^*$), we should re-optimize for the primary objective again while constraining the secondary one.</p>
<p>$$
\begin{align*}
&amp; \text{min}  &amp;&amp; \sum_{wp} c_{wp} x_{wp} \\
&amp; \text{s.t.} &amp;&amp; \sum_{wp} a_{wp} x_{wp} \ge w^* \\
&amp;             &amp;&amp; \sum_w x_{wp} = 1 &amp;&amp; \forall \quad p \in P \\
&amp;             &amp;&amp; \sum_p x_{wp} = 1 &amp;&amp; \forall \quad w \in W \\
&amp;             &amp;&amp; x \in \{0,1\}^{|W \times P|}
\end{align*}
$$</p>
<h3 id="code">Code</h3>
<p>So the math looks reasonable. How do we implement it? If we have a Gurobi license, we can use <a href="https://docs.gurobi.com/projects/optimizer/en/current/features/multiobjective.html#multiple-objectives">its built-in facilities for multiobjective optimization</a>. This means that, instead solving a model multiple times and adding constraints to keep cost within $\alpha$ of its optimal value, we can create a single model that does all of this for us.</p>
<p>Assume we have input data which looks like this.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-json" data-lang="json"><span style="display:flex;"><span>{
</span></span><span style="display:flex;"><span>    <span style="color:#7ee787">&#34;cost&#34;</span>: [
</span></span><span style="display:flex;"><span>        [<span style="color:#a5d6ff">10</span>, <span style="color:#a5d6ff">20</span>, <span style="color:#f85149">...</span>],
</span></span><span style="display:flex;"><span>        [<span style="color:#a5d6ff">30</span>, <span style="color:#a5d6ff">40</span>, <span style="color:#f85149">...</span>],
</span></span><span style="display:flex;"><span>        <span style="color:#f85149">...</span>
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex;"><span>    <span style="color:#7ee787">&#34;affinity&#34;</span>: [
</span></span><span style="display:flex;"><span>        [<span style="color:#a5d6ff">25</span>, <span style="color:#a5d6ff">15</span>, <span style="color:#f85149">...</span>],
</span></span><span style="display:flex;"><span>        [<span style="color:#a5d6ff">35</span>, <span style="color:#a5d6ff">25</span>, <span style="color:#f85149">...</span>],
</span></span><span style="display:flex;"><span>        <span style="color:#f85149">...</span>
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>}
</span></span></code></pre></div><p>We start with a simple assignment problem formulation.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">gurobipy</span> <span style="color:#ff7b72">as</span> <span style="color:#ff7b72">gp</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>n <span style="color:#ff7b72;font-weight:bold">=</span> len(data[<span style="color:#a5d6ff">&#34;cost&#34;</span>])
</span></span><span style="display:flex;"><span>workers <span style="color:#ff7b72;font-weight:bold">=</span> range(n)
</span></span><span style="display:flex;"><span>patients <span style="color:#ff7b72;font-weight:bold">=</span> range(n)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>m <span style="color:#ff7b72;font-weight:bold">=</span> gp<span style="color:#ff7b72;font-weight:bold">.</span>Model()
</span></span><span style="display:flex;"><span>m<span style="color:#ff7b72;font-weight:bold">.</span>ModelSense <span style="color:#ff7b72;font-weight:bold">=</span> gp<span style="color:#ff7b72;font-weight:bold">.</span>GRB<span style="color:#ff7b72;font-weight:bold">.</span>MINIMIZE
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># x[w,p] = 1 if worker w is assigned to patient p.</span>
</span></span><span style="display:flex;"><span>x <span style="color:#ff7b72;font-weight:bold">=</span> m<span style="color:#ff7b72;font-weight:bold">.</span>addVars(n, n, vtype<span style="color:#ff7b72;font-weight:bold">=</span>gp<span style="color:#ff7b72;font-weight:bold">.</span>GRB<span style="color:#ff7b72;font-weight:bold">.</span>BINARY)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> i <span style="color:#ff7b72;font-weight:bold">in</span> range(n):
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Each worker is assigned to one patient.</span>
</span></span><span style="display:flex;"><span>    m<span style="color:#ff7b72;font-weight:bold">.</span>addConstr(gp<span style="color:#ff7b72;font-weight:bold">.</span>quicksum(x[i, p] <span style="color:#ff7b72">for</span> p <span style="color:#ff7b72;font-weight:bold">in</span> patients) <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">1</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Each patient is assigned one worker.</span>
</span></span><span style="display:flex;"><span>    m<span style="color:#ff7b72;font-weight:bold">.</span>addConstr(gp<span style="color:#ff7b72;font-weight:bold">.</span>quicksum(x[w, i] <span style="color:#ff7b72">for</span> w <span style="color:#ff7b72;font-weight:bold">in</span> workers) <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">1</span>)
</span></span></code></pre></div><p>We add primary and secondary objectives, and call <code>optimize</code>. The objectives are solved in descending order of the <code>priority</code> flag for <code>Model.setObjectiveN</code>. <code>reltol</code> allows us to degrade the primary objective by some amount (e.g. 5%) to improve the secondary objective.</p>
<p>One catch is that the model only has one objective sense. Since we are <em>minimizing</em> the primary objective, we give the secondary objective a <code>weight</code> of <code>-1</code> in order to <em>maximize</em> it.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">from</span> <span style="color:#ff7b72">itertools</span> <span style="color:#ff7b72">import</span> product
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Primary objective: minimize cost.</span>
</span></span><span style="display:flex;"><span>z <span style="color:#ff7b72;font-weight:bold">=</span> (data[<span style="color:#a5d6ff">&#34;cost&#34;</span>][w][p] <span style="color:#ff7b72;font-weight:bold">*</span> x[w, p] <span style="color:#ff7b72">for</span> w, p <span style="color:#ff7b72;font-weight:bold">in</span> product(workers, patients))
</span></span><span style="display:flex;"><span>m<span style="color:#ff7b72;font-weight:bold">.</span>setObjectiveN(expr<span style="color:#ff7b72;font-weight:bold">=</span>gp<span style="color:#ff7b72;font-weight:bold">.</span>quicksum(z), index<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">0</span>, name<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#34;cost&#34;</span>, priority<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">1</span>, reltol<span style="color:#ff7b72;font-weight:bold">=</span>alpha)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Secondary objective: maximize affinity. Since the model sense is minimize,</span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># we negate the secondary objective in order to maximize it.</span>
</span></span><span style="display:flex;"><span>w <span style="color:#ff7b72;font-weight:bold">=</span> (data[<span style="color:#a5d6ff">&#34;affinity&#34;</span>][w][p] <span style="color:#ff7b72;font-weight:bold">*</span> x[w, p] <span style="color:#ff7b72">for</span> w, p <span style="color:#ff7b72;font-weight:bold">in</span> product(workers, patients))
</span></span><span style="display:flex;"><span>m<span style="color:#ff7b72;font-weight:bold">.</span>setObjectiveN(
</span></span><span style="display:flex;"><span>    expr<span style="color:#ff7b72;font-weight:bold">=</span>gp<span style="color:#ff7b72;font-weight:bold">.</span>quicksum(w), index<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">1</span>, name<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#34;affinity&#34;</span>, priority<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">0</span>, weight<span style="color:#ff7b72;font-weight:bold">=-</span><span style="color:#a5d6ff">1</span>
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>m<span style="color:#ff7b72;font-weight:bold">.</span>optimize()
</span></span></code></pre></div><p>Then we use this magic syntax to pull out the optimal cost and affinity.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>m<span style="color:#ff7b72;font-weight:bold">.</span>params<span style="color:#ff7b72;font-weight:bold">.</span>ObjNumber <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">0</span>
</span></span><span style="display:flex;"><span>cost <span style="color:#ff7b72;font-weight:bold">=</span> m<span style="color:#ff7b72;font-weight:bold">.</span>ObjNVal
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>m<span style="color:#ff7b72;font-weight:bold">.</span>params<span style="color:#ff7b72;font-weight:bold">.</span>ObjNumber <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">1</span>
</span></span><span style="display:flex;"><span>affinity <span style="color:#ff7b72;font-weight:bold">=</span> m<span style="color:#ff7b72;font-weight:bold">.</span>ObjNVal
</span></span></code></pre></div><h3 id="results">Results</h3>
<p>If we solve this in a loop with alpha values from <code>0</code> to <code>1</code> in increments of <code>0.05</code>, we can plot the trade-off between cost and affinity. Going from $\alpha = 0$ to $\alpha = 0.05$ or $\alpha = 0.1$ gives a pretty sizable improvement in affinity. After that, the return starts to gradually level off. This allows us to make a more informed choice about these two objectives.</p>
<p><img alt="Pareto front - cost vs affinity" loading="lazy" src="/files/2024-11-08-hierarchical-optimization-with-gurobi/plot.png#center"></p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="/files/2024-11-08-hierarchical-optimization-with-gurobi/generate.py"><code>generate.py</code></a> generates input data</li>
<li><a href="/files/2024-11-08-hierarchical-optimization-with-gurobi/input-100x100.json"><code>input-100x100.json</code></a> contains input data</li>
<li><a href="/files/2024-11-08-hierarchical-optimization-with-gurobi/model.py"><code>model.py</code></a> hierarchical objectives Gurobi model</li>
</ul>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>While commercial libraries like AMPL have always focussed on modeling performance, some of the open source options targeting multiple solvers come with significant performance penalties during formulation and model handoff to the solver. Newer options like <code>linopy</code> (<a href="https://linopy.readthedocs.io/en/latest/benchmark.html">benchmarks</a>) and PyOptInterface (<a href="https://metab0t.github.io/PyOptInterface/benchmark.html">benchmarks</a>) don&rsquo;t have that issue.&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>This gives us a <a href="https://en.wikipedia.org/wiki/Pareto_front">Pareto front</a>, which explores the trade-offs between different objectives.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>👾 Detecting Polygon Intersections</title>
      <link>https://ryanjoneil.dev/posts/2015-09-27-detecting-polygon-intersections/</link>
      <pubDate>Sun, 27 Sep 2015 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2015-09-27-detecting-polygon-intersections/</guid>
      <description>Detecting when two polygons touch or overlap.</description>
      <content:encoded><![CDATA[<p><em>Note: This post has been updated to work with HiGHS.</em></p>
<p>A fun geometry problem to think about is: given two polygons, do they intersect? That is, do they touch on the border or overlap? Does one reside entirely within the other? While this question has obvious applications in computer graphics <em>(see: arcade games of the 1980s)</em>, it&rsquo;s also important in areas such as <a href="https://www.euro-online.org/websites/esicup/">cutting and packing problems</a>.</p>
<p>There are a number of way to answer this. In computer graphics, the problem is often approached using a <a href="https://en.wikipedia.org/wiki/Clipping_(computer_graphics)">clipping algorithm</a>. This post examines a couple of simpler techniques using linear inequalities and properties of convexity. To simplify the presentation, we assume we&rsquo;re only interested in convex polygons in two dimensions. We also assume that rotation is not an issue. That is, if one of the polygons is rotated, we can simply re-test to see if they overlap.</p>
<p><img alt="Don&rsquo;t get clobbered by an asteroid!" loading="lazy" src="/files/2015-09-27-polygon-intersections-part-1-detecting-overlap/asteroids.jpg#center"></p>
<h2 id="problem">Problem</h2>
<p>Let&rsquo;s say we have two objects: a right triangle and a square. We can place them anywhere inside a larger rectangle. The triangle has vertices:</p>
<p>$$\{\left(x_t, y_t\right), \left(x_t, y_t + a\right), \left(x_t + a, y_t\right)\}$$</p>
<p>The square has vertices:</p>
<p>$$\{\left(x_s, y_s\right), \left(x_s, y_s + a\right), \left(x_s + a, y_s + a\right), \left(x_s + a, y_s\right)\}$$</p>
<p>We will be given $\left(x_t, y_t\right)$, $\left(x_s, y_s\right)$, and $a$, but we do not know them a priori. We would like to know, for any set of values these can take, whether or not the triangle and square they define intersect.</p>
<p>$\left(x_t, y_t\right)$ and $\left(x_s, y_s\right)$ are the offsets of the triangle and square with respect to the bottom left corner of the rectangle. If they are far enough apart in any direction, the two objects do not intersect. The figure below shows such a case, with small gray circles representing $\left(x_t, y_t\right)$ and $\left(x_s, y_s\right)$.</p>
<p><img alt="Polygons that do not intersect" loading="lazy" src="/files/2015-09-27-polygon-intersections-part-1-detecting-overlap/polygons-that-do-not-intersect.svg#center"></p>
<p>However, if they are too close in some manner, the objects will either touch or overlap, as shown below.</p>
<p><img alt="Polygons that intersect" loading="lazy" src="/files/2015-09-27-polygon-intersections-part-1-detecting-overlap/polygons-that-intersect.svg#center"></p>
<p>he two polygons can intersect in a few different ways. They may touch on their borders, in which case they will share a single point or line segment. They may overlap such that their intersecting region has nonzero relative interior but each polygon contains points outside the other. Or one of them might live entirely within the other, so that the former is a subset of the latter. Our goal is to determine if any of these cases are true given any $\left(x_t, y_t\right)$, $\left(x_s, y_s\right)$, and $a$.</p>
<h3 id="method-1-define-the-intersecting-polygon-with-linear-inequalities">Method 1. Define the intersecting polygon with linear inequalities</h3>
<p>The first method we use to detect intersection is based on the fact that our polygons themselves are the intersections of finite numbers of linear inequalities. Instead of defining them based on their vertices, we can equivalently represent them as the set of $\left(x, y\right)$ that satisfy a known inequality for each edge.</p>
<p>Let $S_t$ be the set of points in our triangle. It can be defined as follows. $x$ must be greater than or equal to $x_t$. $y$ must be greater than or equal to $y_t$. And $x + y$ must be left of or lower than the triangle&rsquo;s hypotenuse. There are three sides on the triangle, so we have three inequalities.</p>
<p>$$
\begin{array}{rcl}
S_t = \{\,\left(x, y\right) &amp; | &amp; x \ge x_t,\\
&amp;   &amp; y \ge y_t,\\
&amp;   &amp; x + y \le x_t + y_t + a \,\}
\end{array}
$$</p>
<p>Similarly, let $S_s$ be the set of points in our square. This set is defined using four inequalities, which are shown in a slightly compacted form.</p>
<p>$$
\begin{array}{rcl}
S_s = \{\,\left(x, y\right) &amp; | &amp; x_s \le x \le x_s + a,\\
&amp;   &amp; y_s \le y \le y_s + a \,\}
\end{array}
$$</p>
<p>Finally, let $S_i = S_t \cap S_s$ be the set of points that satisfy all seven inequalities.</p>
<p>$$
\begin{array}{rcl}
S_i = \{\,\left(x, y\right) &amp; | &amp; x \ge x_t,\\
&amp;   &amp; y \ge y_t,\\
&amp;   &amp; x + y \le x_t + y_t + a,\\
&amp;   &amp; x_s \le x \le x_s + a,\\
&amp;   &amp; y_s \le y \le y_s + a \,\}
\end{array}
$$</p>
<p>If $S_i \ne \emptyset$, then there must exist some point that satisfies the inequalities of both the triangle and the square. This point resides in both of them, therefore they intersect. If $S_i = \emptyset$, then there is no such point and they do not intersect.</p>
<h3 id="method-2-use-convex-combinations-of-the-polygon-vertices">Method 2. Use convex combinations of the polygon vertices</h3>
<p>Both of our polygons are convex. That is, they contain every <a href="https://en.wikipedia.org/wiki/Convex_combination">convex combination</a> of their vertices. So every point in the triangle, regardless of where it is located, can be represented as a linear combination of ${\left(x_t, y_t\right), \left(x_t + a, y_t\right), \left(x_t, y_t + a\right)}$ where $\lambda_1, \lambda_2, \lambda_3 \ge 0$ and $\lambda_1 + \lambda_2 + \lambda_3 = 1$.</p>
<p>We can define the set $S_t$ equivalently using this concept.</p>
<p>$$
S_t = \{\, \lambda_1 \left(\begin{array}{c} x_t \\ y_t \end{array}\right) +
\lambda_2 \left(\begin{array}{c} x_t + a \\ y_t \end{array}\right) +
\lambda_3 \left(\begin{array}{c} x_t \\ y_t + a \end{array}\right) \, | \\
\lambda_1 + \lambda_2 + \lambda_3 = 1, \\
\lambda_i \ge 0, , i = {1, \ldots, 3 } \, \}
$$</p>
<p>Similarly, the square is defined a the convex combination of its vertices.</p>
<p>$$
S_s = \{\, \lambda_4 \left(\begin{array}{c} x_s \\ y_s \end{array}\right) +
\lambda_5 \left(\begin{array}{c} x_s + a \\ y_s \end{array}\right) +
\lambda_6 \left(\begin{array}{c} x_s \\ y_s + a \end{array}\right) +
\lambda_7 \left(\begin{array}{c} x_s + a \\ y_s + a \end{array}\right) \, | \\
\lambda_4 + \lambda_5 + \lambda_6 + \lambda_7 = 1, \\
\lambda_i \ge 0, , i = {4, \ldots, 7 } \, \}
$$</p>
<p>If there exists a point inside both the triangle and the square, then it must satisfy both convex combinations. Thus we can define our intersecting set $S_i$ as follows. <em>(This is a little loose with the notation, but I think it makes the point a bit better.)</em></p>
<p>$$
\begin{array}{rl}
S_i = \{\, &amp; \\
&amp; \lambda_1 \left(\begin{array}{c} x_t \\ y_t \end{array}\right) +
\lambda_2 \left(\begin{array}{c} x_t + a \\ y_t \end{array}\right) +
\lambda_3 \left(\begin{array}{c} x_t \\ y_t + a \end{array}\right) =\\
&amp; \lambda_4 \left(\begin{array}{c} x_s \\ y_s \end{array}\right) +
\lambda_5 \left(\begin{array}{c} x_s + a \\ y_s \end{array}\right) +
\lambda_6 \left(\begin{array}{c} x_s \\ y_s + a \end{array}\right) +
\lambda_7 \left(\begin{array}{c} x_s + a \\ y_s + a \end{array}\right),\\
&amp; \lambda_1 + \lambda_2 + \lambda_3 = 1,\\
&amp; \lambda_4 + \lambda_5 + \lambda_6 + \lambda_7 = 1,\\
&amp; \lambda_i \ge 0, \, i = {1, \ldots, 7}\\
\,\} &amp;
\end{array}
$$</p>
<p>Just as before, if $S_i \ne \emptyset$, our polygons intersect.</p>
<h2 id="code">Code</h2>
<p>Both models are pretty easy to implement using an <a href="https://en.wikipedia.org/wiki/Linear_programming">LP Solver</a>. But they look very different. That&rsquo;s because in the first method we&rsquo;re thinking about the problem in terms of inequalities and in the second we&rsquo;re thinking about it in terms of vertices. The code below generates a thousand random instances of the problem and tests that each method produces the same result.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">highspy</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">random</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">method1</span>(xy_t, xy_s, a):
</span></span><span style="display:flex;"><span>    x_t, y_t <span style="color:#ff7b72;font-weight:bold">=</span> xy_t
</span></span><span style="display:flex;"><span>    x_s, y_s <span style="color:#ff7b72;font-weight:bold">=</span> xy_s
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    h <span style="color:#ff7b72;font-weight:bold">=</span> highspy<span style="color:#ff7b72;font-weight:bold">.</span>Highs()
</span></span><span style="display:flex;"><span>    h<span style="color:#ff7b72;font-weight:bold">.</span>silent()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    x <span style="color:#ff7b72;font-weight:bold">=</span> h<span style="color:#ff7b72;font-weight:bold">.</span>addVariable()
</span></span><span style="display:flex;"><span>    y <span style="color:#ff7b72;font-weight:bold">=</span> h<span style="color:#ff7b72;font-weight:bold">.</span>addVariable()
</span></span><span style="display:flex;"><span>    h<span style="color:#ff7b72;font-weight:bold">.</span>addConstrs(
</span></span><span style="display:flex;"><span>        x_t <span style="color:#ff7b72;font-weight:bold">&lt;=</span> x <span style="color:#ff7b72;font-weight:bold">&lt;=</span> x_t <span style="color:#ff7b72;font-weight:bold">+</span> a,
</span></span><span style="display:flex;"><span>        x_s <span style="color:#ff7b72;font-weight:bold">&lt;=</span> x <span style="color:#ff7b72;font-weight:bold">&lt;=</span> x_s <span style="color:#ff7b72;font-weight:bold">+</span> a,
</span></span><span style="display:flex;"><span>        y_t <span style="color:#ff7b72;font-weight:bold">&lt;=</span> y <span style="color:#ff7b72;font-weight:bold">&lt;=</span> y_t <span style="color:#ff7b72;font-weight:bold">+</span> a,
</span></span><span style="display:flex;"><span>        y_s <span style="color:#ff7b72;font-weight:bold">&lt;=</span> y <span style="color:#ff7b72;font-weight:bold">&lt;=</span> y_s <span style="color:#ff7b72;font-weight:bold">+</span> a,
</span></span><span style="display:flex;"><span>        x <span style="color:#ff7b72;font-weight:bold">+</span> y <span style="color:#ff7b72;font-weight:bold">&lt;=</span> x_t <span style="color:#ff7b72;font-weight:bold">+</span> y_t <span style="color:#ff7b72;font-weight:bold">+</span> a,
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> h
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">method2</span>(xy_t, xy_s, a):
</span></span><span style="display:flex;"><span>    x_t, y_t <span style="color:#ff7b72;font-weight:bold">=</span> xy_t
</span></span><span style="display:flex;"><span>    x_s, y_s <span style="color:#ff7b72;font-weight:bold">=</span> xy_s
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    h <span style="color:#ff7b72;font-weight:bold">=</span> highspy<span style="color:#ff7b72;font-weight:bold">.</span>Highs()
</span></span><span style="display:flex;"><span>    h<span style="color:#ff7b72;font-weight:bold">.</span>silent()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    lm <span style="color:#ff7b72;font-weight:bold">=</span> [h<span style="color:#ff7b72;font-weight:bold">.</span>addVariable(lb<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">0</span>, ub<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">1</span>) <span style="color:#ff7b72">for</span> _ <span style="color:#ff7b72;font-weight:bold">in</span> range(<span style="color:#a5d6ff">7</span>)]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    conv_xt <span style="color:#ff7b72;font-weight:bold">=</span> lm[<span style="color:#a5d6ff">0</span>] <span style="color:#ff7b72;font-weight:bold">*</span> x_t <span style="color:#ff7b72;font-weight:bold">+</span> lm[<span style="color:#a5d6ff">1</span>] <span style="color:#ff7b72;font-weight:bold">*</span> (x_t <span style="color:#ff7b72;font-weight:bold">+</span> a) <span style="color:#ff7b72;font-weight:bold">+</span> lm[<span style="color:#a5d6ff">2</span>] <span style="color:#ff7b72;font-weight:bold">*</span> x_t
</span></span><span style="display:flex;"><span>    conv_xs <span style="color:#ff7b72;font-weight:bold">=</span> lm[<span style="color:#a5d6ff">3</span>] <span style="color:#ff7b72;font-weight:bold">*</span> x_s <span style="color:#ff7b72;font-weight:bold">+</span> lm[<span style="color:#a5d6ff">4</span>] <span style="color:#ff7b72;font-weight:bold">*</span> (x_s <span style="color:#ff7b72;font-weight:bold">+</span> a) <span style="color:#ff7b72;font-weight:bold">+</span> lm[<span style="color:#a5d6ff">5</span>] <span style="color:#ff7b72;font-weight:bold">*</span> x_s <span style="color:#ff7b72;font-weight:bold">+</span> lm[<span style="color:#a5d6ff">6</span>] <span style="color:#ff7b72;font-weight:bold">*</span> (x_s <span style="color:#ff7b72;font-weight:bold">+</span> a)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    conv_yt <span style="color:#ff7b72;font-weight:bold">=</span> lm[<span style="color:#a5d6ff">0</span>] <span style="color:#ff7b72;font-weight:bold">*</span> y_t <span style="color:#ff7b72;font-weight:bold">+</span> lm[<span style="color:#a5d6ff">1</span>] <span style="color:#ff7b72;font-weight:bold">*</span> y_t <span style="color:#ff7b72;font-weight:bold">+</span> lm[<span style="color:#a5d6ff">2</span>] <span style="color:#ff7b72;font-weight:bold">*</span> (y_t <span style="color:#ff7b72;font-weight:bold">+</span> a)
</span></span><span style="display:flex;"><span>    conv_ys <span style="color:#ff7b72;font-weight:bold">=</span> lm[<span style="color:#a5d6ff">3</span>] <span style="color:#ff7b72;font-weight:bold">*</span> y_s <span style="color:#ff7b72;font-weight:bold">+</span> lm[<span style="color:#a5d6ff">4</span>] <span style="color:#ff7b72;font-weight:bold">*</span> y_s <span style="color:#ff7b72;font-weight:bold">+</span> lm[<span style="color:#a5d6ff">5</span>] <span style="color:#ff7b72;font-weight:bold">*</span> (y_s <span style="color:#ff7b72;font-weight:bold">+</span> a) <span style="color:#ff7b72;font-weight:bold">+</span> lm[<span style="color:#a5d6ff">6</span>] <span style="color:#ff7b72;font-weight:bold">*</span> (y_s <span style="color:#ff7b72;font-weight:bold">+</span> a)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    h<span style="color:#ff7b72;font-weight:bold">.</span>addConstrs(
</span></span><span style="display:flex;"><span>        conv_xt <span style="color:#ff7b72;font-weight:bold">==</span> conv_xs,
</span></span><span style="display:flex;"><span>        conv_yt <span style="color:#ff7b72;font-weight:bold">==</span> conv_ys,
</span></span><span style="display:flex;"><span>        sum(lm[:<span style="color:#a5d6ff">3</span>]) <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">1</span>,
</span></span><span style="display:flex;"><span>        sum(lm[<span style="color:#a5d6ff">3</span>:]) <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">1</span>,
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> h
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">if</span> <span style="color:#79c0ff">__name__</span> <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#34;__main__&#34;</span>:
</span></span><span style="display:flex;"><span>    problems1 <span style="color:#ff7b72;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    problems2 <span style="color:#ff7b72;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> _ <span style="color:#ff7b72;font-weight:bold">in</span> range(<span style="color:#a5d6ff">1000</span>):
</span></span><span style="display:flex;"><span>        a <span style="color:#ff7b72;font-weight:bold">=</span> random<span style="color:#ff7b72;font-weight:bold">.</span>random() <span style="color:#ff7b72;font-weight:bold">*</span> <span style="color:#a5d6ff">2.5</span> <span style="color:#ff7b72;font-weight:bold">+</span> <span style="color:#a5d6ff">1</span>
</span></span><span style="display:flex;"><span>        x_t <span style="color:#ff7b72;font-weight:bold">=</span> random<span style="color:#ff7b72;font-weight:bold">.</span>random() <span style="color:#ff7b72;font-weight:bold">*</span> <span style="color:#a5d6ff">10</span>
</span></span><span style="display:flex;"><span>        y_t <span style="color:#ff7b72;font-weight:bold">=</span> random<span style="color:#ff7b72;font-weight:bold">.</span>random() <span style="color:#ff7b72;font-weight:bold">*</span> <span style="color:#a5d6ff">10</span>
</span></span><span style="display:flex;"><span>        x_s <span style="color:#ff7b72;font-weight:bold">=</span> random<span style="color:#ff7b72;font-weight:bold">.</span>random() <span style="color:#ff7b72;font-weight:bold">*</span> <span style="color:#a5d6ff">10</span>
</span></span><span style="display:flex;"><span>        y_s <span style="color:#ff7b72;font-weight:bold">=</span> random<span style="color:#ff7b72;font-weight:bold">.</span>random() <span style="color:#ff7b72;font-weight:bold">*</span> <span style="color:#a5d6ff">10</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        problems1<span style="color:#ff7b72;font-weight:bold">.</span>append(method1([x_t, y_t], [x_s, y_s], a))
</span></span><span style="display:flex;"><span>        problems2<span style="color:#ff7b72;font-weight:bold">.</span>append(method2([x_t, y_t], [x_s, y_s], a))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    overlap1 <span style="color:#ff7b72;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> h <span style="color:#ff7b72;font-weight:bold">in</span> problems1:
</span></span><span style="display:flex;"><span>        h<span style="color:#ff7b72;font-weight:bold">.</span>solve()
</span></span><span style="display:flex;"><span>        overlap1<span style="color:#ff7b72;font-weight:bold">.</span>append(h<span style="color:#ff7b72;font-weight:bold">.</span>getModelStatus())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    overlap2 <span style="color:#ff7b72;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> h <span style="color:#ff7b72;font-weight:bold">in</span> problems2:
</span></span><span style="display:flex;"><span>        h<span style="color:#ff7b72;font-weight:bold">.</span>solve()
</span></span><span style="display:flex;"><span>        overlap2<span style="color:#ff7b72;font-weight:bold">.</span>append(h<span style="color:#ff7b72;font-weight:bold">.</span>getModelStatus())
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">assert</span> overlap1 <span style="color:#ff7b72;font-weight:bold">==</span> overlap2
</span></span></code></pre></div><p>These aren&rsquo;t necessarily the best ways to solve this particular problem, but they are quick and flexible. And they leverage existing solver technology. One downside is that they aren&rsquo;t easy to adapt to certain decision making contexts. That is, we can use them to <em>determine whether objects overlap</em>, but not to <em>force objects not to overlap</em>. In the next post, we&rsquo;ll go over another tool from computational geometry that allows us to embed decisions about the relative locations of objects in our models.</p>
<h2 id="exercises">Exercises</h2>
<ul>
<li>We assumed convex polygons in this presentation. How might one extend the model to work on non-convex polygons? What problems does this introduce?</li>
<li>The two methods shown above are equivalent. How can this be proven?</li>
<li>This post only answers the question of whether two convex polygons intersect. Devise models for determining if they only touch, or if one is a subset of the other.</li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>🏖️ Langrangian Relaxation with Gurobi</title>
      <link>https://ryanjoneil.dev/posts/2012-09-22-lagrangian-relaxation-with-gurobi/</link>
      <pubDate>Sat, 22 Sep 2012 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2012-09-22-lagrangian-relaxation-with-gurobi/</guid>
      <description>Solving integer programs with Lagrangian relaxation and Gurobi.</description>
      <content:encoded><![CDATA[<p><em>Note: This post was updated to work with Python 3 and the 2nd edition of &ldquo;Integer Programming&rdquo; by Laurence Wolsey.</em></p>
<p>We&rsquo;ve been studying Lagrangian Relaxation (LR) in the Advanced Topics in Combinatorial Optimization course I&rsquo;m taking this term, and I had some difficulty finding a simple example covering its application. In case anyone else finds it useful, I&rsquo;m posting a Python version for solving the <a href="https://en.wikipedia.org/wiki/Generalized_assignment_problem">Generalized Assignment Problem</a> (GAP). This won&rsquo;t discuss the theory of LR at all, just give example code using Gurobi.</p>
<h2 id="generalized-assignment">Generalized assignment</h2>
<p>The GAP as defined by <a href="https://onlinelibrary.wiley.com/doi/book/10.1002/9781119606475">Wolsey</a> consists of a maximization problem subject to a set of set packing constraints followed by a set of knapsack constraints.</p>
<p>$$
\begin{align*}
&amp; \text{max}  &amp;&amp; \sum_i \sum_j c_{ij} x_{ij} \\
&amp; \text{s.t.} &amp;&amp; \sum_j x_{ij} \leq 1             &amp;&amp; \forall i \\
&amp;             &amp;&amp; \sum_i a_{ij} x_{ij} \leq b_{ij} &amp;&amp; \forall j \\
&amp;             &amp;&amp; x_{ij} \in {0, 1}
\end{align*}
$$</p>
<h3 id="naive-model">Naive model</h3>
<p>A naive version of this model using Gurobi might look like the following.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">#!/usr/bin/env python</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># This is the GAP per Wolsey, pg 208.</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">from</span> <span style="color:#ff7b72">gurobipy</span> <span style="color:#ff7b72">import</span> Model, GRB, quicksum <span style="color:#ff7b72">as</span> qsum
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>m <span style="color:#ff7b72;font-weight:bold">=</span> Model(<span style="color:#a5d6ff">&#34;GAP per Wolsey&#34;</span>)
</span></span><span style="display:flex;"><span>m<span style="color:#ff7b72;font-weight:bold">.</span>modelSense <span style="color:#ff7b72;font-weight:bold">=</span> GRB<span style="color:#ff7b72;font-weight:bold">.</span>MAXIMIZE
</span></span><span style="display:flex;"><span>m<span style="color:#ff7b72;font-weight:bold">.</span>setParam(<span style="color:#a5d6ff">&#34;OutputFlag&#34;</span>, <span style="color:#79c0ff">False</span>)  <span style="color:#8b949e;font-style:italic"># turns off solver chatter</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>b <span style="color:#ff7b72;font-weight:bold">=</span> [<span style="color:#a5d6ff">15</span>, <span style="color:#a5d6ff">15</span>, <span style="color:#a5d6ff">15</span>]
</span></span><span style="display:flex;"><span>c <span style="color:#ff7b72;font-weight:bold">=</span> [
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">6</span>, <span style="color:#a5d6ff">10</span>, <span style="color:#a5d6ff">1</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">12</span>, <span style="color:#a5d6ff">12</span>, <span style="color:#a5d6ff">5</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">15</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">3</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">10</span>, <span style="color:#a5d6ff">3</span>, <span style="color:#a5d6ff">9</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">8</span>, <span style="color:#a5d6ff">9</span>, <span style="color:#a5d6ff">5</span>],
</span></span><span style="display:flex;"><span>]
</span></span><span style="display:flex;"><span>a <span style="color:#ff7b72;font-weight:bold">=</span> [
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">5</span>, <span style="color:#a5d6ff">7</span>, <span style="color:#a5d6ff">2</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">14</span>, <span style="color:#a5d6ff">8</span>, <span style="color:#a5d6ff">7</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">10</span>, <span style="color:#a5d6ff">6</span>, <span style="color:#a5d6ff">12</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">8</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">15</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">6</span>, <span style="color:#a5d6ff">12</span>, <span style="color:#a5d6ff">5</span>],
</span></span><span style="display:flex;"><span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># x[i][j] = 1 if i is assigned to j</span>
</span></span><span style="display:flex;"><span>x <span style="color:#ff7b72;font-weight:bold">=</span> [[m<span style="color:#ff7b72;font-weight:bold">.</span>addVar(vtype<span style="color:#ff7b72;font-weight:bold">=</span>GRB<span style="color:#ff7b72;font-weight:bold">.</span>BINARY) <span style="color:#ff7b72">for</span> _ <span style="color:#ff7b72;font-weight:bold">in</span> row] <span style="color:#ff7b72">for</span> row <span style="color:#ff7b72;font-weight:bold">in</span> c]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># sum j: x_ij &lt;= 1 for all i</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> x_i <span style="color:#ff7b72;font-weight:bold">in</span> x:
</span></span><span style="display:flex;"><span>    m<span style="color:#ff7b72;font-weight:bold">.</span>addConstr(sum(x_i) <span style="color:#ff7b72;font-weight:bold">&lt;=</span> <span style="color:#a5d6ff">1</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># sum i: a_ij * x_ij &lt;= b[j] for all j</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> j, b_j <span style="color:#ff7b72;font-weight:bold">in</span> enumerate(b):
</span></span><span style="display:flex;"><span>    m<span style="color:#ff7b72;font-weight:bold">.</span>addConstr(qsum(a[i][j] <span style="color:#ff7b72;font-weight:bold">*</span> x_i[j] <span style="color:#ff7b72">for</span> i, x_i <span style="color:#ff7b72;font-weight:bold">in</span> enumerate(x)) <span style="color:#ff7b72;font-weight:bold">&lt;=</span> b_j)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># max sum i,j: c_ij * x_ij</span>
</span></span><span style="display:flex;"><span>m<span style="color:#ff7b72;font-weight:bold">.</span>setObjective(
</span></span><span style="display:flex;"><span>    qsum(qsum(c_ij <span style="color:#ff7b72;font-weight:bold">*</span> x_ij <span style="color:#ff7b72">for</span> c_ij, x_ij <span style="color:#ff7b72;font-weight:bold">in</span> zip(c_i, x_i)) <span style="color:#ff7b72">for</span> c_i, x_i <span style="color:#ff7b72;font-weight:bold">in</span> zip(c, x))
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>m<span style="color:#ff7b72;font-weight:bold">.</span>optimize()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Pull solution out of m.</span>
</span></span><span style="display:flex;"><span>print(<span style="color:#79c0ff">f</span><span style="color:#a5d6ff">&#34;z = </span><span style="color:#a5d6ff">{</span>m<span style="color:#ff7b72;font-weight:bold">.</span>objVal<span style="color:#a5d6ff">}</span><span style="color:#a5d6ff">&#34;</span>)
</span></span><span style="display:flex;"><span>print(<span style="color:#a5d6ff">&#34;x = [&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> x_i <span style="color:#ff7b72;font-weight:bold">in</span> x:
</span></span><span style="display:flex;"><span>    print(<span style="color:#79c0ff">f</span><span style="color:#a5d6ff">&#34;  </span><span style="color:#a5d6ff">{</span>[<span style="color:#a5d6ff">1</span> <span style="color:#ff7b72">if</span> x_ij<span style="color:#ff7b72;font-weight:bold">.</span>x <span style="color:#ff7b72;font-weight:bold">&gt;=</span> <span style="color:#a5d6ff">0.5</span> <span style="color:#ff7b72">else</span> <span style="color:#a5d6ff">0</span> <span style="color:#ff7b72">for</span> x_ij <span style="color:#ff7b72;font-weight:bold">in</span> x_i]<span style="color:#a5d6ff">}</span><span style="color:#a5d6ff">&#34;</span>)
</span></span><span style="display:flex;"><span>print(<span style="color:#a5d6ff">&#34;]&#34;</span>)
</span></span></code></pre></div><p>The solver quickly finds the following optimal solution of this toy problem.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>z = 46.0
</span></span><span style="display:flex;"><span>x = [
</span></span><span style="display:flex;"><span>  [0, 1, 0]
</span></span><span style="display:flex;"><span>  [0, 1, 0]
</span></span><span style="display:flex;"><span>  [1, 0, 0]
</span></span><span style="display:flex;"><span>  [0, 0, 1]
</span></span><span style="display:flex;"><span>  [0, 0, 0]
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></div><h3 id="lagrangian-model">Lagrangian model</h3>
<p>There are two sets of constraints we can dualize. It can be beneficial to apply Lagrangian Relaxation against problems composed of knapsack constraints, so we will dualize the set packing ones.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># sum j: x_ij &lt;= 1 for all i</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> x_i <span style="color:#ff7b72;font-weight:bold">in</span> x:
</span></span><span style="display:flex;"><span>    model<span style="color:#ff7b72;font-weight:bold">.</span>addConstr(sum(x_i) <span style="color:#ff7b72;font-weight:bold">&lt;=</span> <span style="color:#a5d6ff">1</span>)
</span></span></code></pre></div><p>We replace these with a new set of variables, <code>penalties</code>, which take the values of the slacks on the set packing constraints. We then modify the objective function, adding Lagrangian multipliers times these penalties.</p>
<p>Instead of optimizing once, we do so iteratively. An important consideration is we may get nothing more than a dual bound from this process. Any integer solution is not guaranteed to be primal feasible unless it satisfies complementary slackness conditions &ndash; for each dualized constraint either its multiplier or penalty must be zero.</p>
<p>We then set the initial multiplier values to 2 and use sub-gradient optimization with a step size of <code>1 / (iteration #)</code> to adjust them.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">#!/usr/bin/env python</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># This is the GAP per Wolsey, pg 208, using Lagrangian Relaxation.</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">from</span> <span style="color:#ff7b72">gurobipy</span> <span style="color:#ff7b72">import</span> Model, GRB, quicksum <span style="color:#ff7b72">as</span> qsum
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>m <span style="color:#ff7b72;font-weight:bold">=</span> Model(<span style="color:#a5d6ff">&#34;GAP per Wolsey with Lagrangian Relaxation&#34;</span>)
</span></span><span style="display:flex;"><span>m<span style="color:#ff7b72;font-weight:bold">.</span>modelSense <span style="color:#ff7b72;font-weight:bold">=</span> GRB<span style="color:#ff7b72;font-weight:bold">.</span>MAXIMIZE
</span></span><span style="display:flex;"><span>m<span style="color:#ff7b72;font-weight:bold">.</span>setParam(<span style="color:#a5d6ff">&#34;OutputFlag&#34;</span>, <span style="color:#79c0ff">False</span>)  <span style="color:#8b949e;font-style:italic"># turns off solver chatter</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>b <span style="color:#ff7b72;font-weight:bold">=</span> [<span style="color:#a5d6ff">15</span>, <span style="color:#a5d6ff">15</span>, <span style="color:#a5d6ff">15</span>]
</span></span><span style="display:flex;"><span>c <span style="color:#ff7b72;font-weight:bold">=</span> [
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">6</span>, <span style="color:#a5d6ff">10</span>, <span style="color:#a5d6ff">1</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">12</span>, <span style="color:#a5d6ff">12</span>, <span style="color:#a5d6ff">5</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">15</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">3</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">10</span>, <span style="color:#a5d6ff">3</span>, <span style="color:#a5d6ff">9</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">8</span>, <span style="color:#a5d6ff">9</span>, <span style="color:#a5d6ff">5</span>],
</span></span><span style="display:flex;"><span>]
</span></span><span style="display:flex;"><span>a <span style="color:#ff7b72;font-weight:bold">=</span> [
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">5</span>, <span style="color:#a5d6ff">7</span>, <span style="color:#a5d6ff">2</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">14</span>, <span style="color:#a5d6ff">8</span>, <span style="color:#a5d6ff">7</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">10</span>, <span style="color:#a5d6ff">6</span>, <span style="color:#a5d6ff">12</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">8</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">15</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">6</span>, <span style="color:#a5d6ff">12</span>, <span style="color:#a5d6ff">5</span>],
</span></span><span style="display:flex;"><span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># x[i][j] = 1 if i is assigned to j</span>
</span></span><span style="display:flex;"><span>x <span style="color:#ff7b72;font-weight:bold">=</span> [[m<span style="color:#ff7b72;font-weight:bold">.</span>addVar(vtype<span style="color:#ff7b72;font-weight:bold">=</span>GRB<span style="color:#ff7b72;font-weight:bold">.</span>BINARY) <span style="color:#ff7b72">for</span> _ <span style="color:#ff7b72;font-weight:bold">in</span> row] <span style="color:#ff7b72">for</span> row <span style="color:#ff7b72;font-weight:bold">in</span> c]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># As stated, the GAP has these following constraints. We dualize these into</span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># penalties instead, using variables so we can easily extract their values.</span>
</span></span><span style="display:flex;"><span>penalties <span style="color:#ff7b72;font-weight:bold">=</span> [m<span style="color:#ff7b72;font-weight:bold">.</span>addVar() <span style="color:#ff7b72">for</span> _ <span style="color:#ff7b72;font-weight:bold">in</span> x]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Dualized constraints: sum j: x_ij &lt;= 1 for all i</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> p, x_i <span style="color:#ff7b72;font-weight:bold">in</span> zip(penalties, x):
</span></span><span style="display:flex;"><span>    m<span style="color:#ff7b72;font-weight:bold">.</span>addConstr(p <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">1</span> <span style="color:#ff7b72;font-weight:bold">-</span> sum(x_i))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># sum i: a_ij * x_ij &lt;= b[j] for all j</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> j, b_j <span style="color:#ff7b72;font-weight:bold">in</span> enumerate(b):
</span></span><span style="display:flex;"><span>    m<span style="color:#ff7b72;font-weight:bold">.</span>addConstr(qsum(a[i][j] <span style="color:#ff7b72;font-weight:bold">*</span> x_i[j] <span style="color:#ff7b72">for</span> i, x_i <span style="color:#ff7b72;font-weight:bold">in</span> enumerate(x)) <span style="color:#ff7b72;font-weight:bold">&lt;=</span> b_j)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># u[i] = Lagrangian Multiplier for the set packing constraint i</span>
</span></span><span style="display:flex;"><span>u <span style="color:#ff7b72;font-weight:bold">=</span> [<span style="color:#a5d6ff">2.0</span>] <span style="color:#ff7b72;font-weight:bold">*</span> len(x)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Re-optimize until either we have run a certain number of iterations</span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># or complementary slackness conditions apply.</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> k <span style="color:#ff7b72;font-weight:bold">in</span> range(<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">101</span>):
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># max sum i,j: c_ij * x_ij</span>
</span></span><span style="display:flex;"><span>    m<span style="color:#ff7b72;font-weight:bold">.</span>setObjective(
</span></span><span style="display:flex;"><span>        qsum(
</span></span><span style="display:flex;"><span>            <span style="color:#8b949e;font-style:italic"># Original objective function</span>
</span></span><span style="display:flex;"><span>            sum(c_ij <span style="color:#ff7b72;font-weight:bold">*</span> x_ij <span style="color:#ff7b72">for</span> c_ij, x_ij <span style="color:#ff7b72;font-weight:bold">in</span> zip(c_i, x_i))
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">for</span> c_i, x_i <span style="color:#ff7b72;font-weight:bold">in</span> zip(c, x)
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72;font-weight:bold">+</span> qsum(
</span></span><span style="display:flex;"><span>            <span style="color:#8b949e;font-style:italic"># Penalties for dualized constraints</span>
</span></span><span style="display:flex;"><span>            u_j <span style="color:#ff7b72;font-weight:bold">*</span> p_j
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">for</span> u_j, p_j <span style="color:#ff7b72;font-weight:bold">in</span> zip(u, penalties)
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    m<span style="color:#ff7b72;font-weight:bold">.</span>optimize()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    print(
</span></span><span style="display:flex;"><span>        <span style="color:#79c0ff">f</span><span style="color:#a5d6ff">&#34;iteration </span><span style="color:#a5d6ff">{</span>k<span style="color:#a5d6ff">}</span><span style="color:#a5d6ff">: z = </span><span style="color:#a5d6ff">{</span>m<span style="color:#ff7b72;font-weight:bold">.</span>objVal<span style="color:#a5d6ff">}</span><span style="color:#a5d6ff">, u = </span><span style="color:#a5d6ff">{</span>u<span style="color:#a5d6ff">}</span><span style="color:#a5d6ff">, penalties = </span><span style="color:#a5d6ff">{</span>[p<span style="color:#ff7b72;font-weight:bold">.</span>x <span style="color:#ff7b72">for</span> p <span style="color:#ff7b72;font-weight:bold">in</span> penalties]<span style="color:#a5d6ff">}</span><span style="color:#a5d6ff">&#34;</span>
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Test for complementary slackness</span>
</span></span><span style="display:flex;"><span>    stop <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#79c0ff">True</span>
</span></span><span style="display:flex;"><span>    eps <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">10e-6</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> u_i, p_i <span style="color:#ff7b72;font-weight:bold">in</span> zip(u, penalties):
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">if</span> abs(u_i) <span style="color:#ff7b72;font-weight:bold">&gt;</span> eps <span style="color:#ff7b72;font-weight:bold">and</span> abs(p_i<span style="color:#ff7b72;font-weight:bold">.</span>x) <span style="color:#ff7b72;font-weight:bold">&gt;</span> eps:
</span></span><span style="display:flex;"><span>            stop <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#79c0ff">False</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">break</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">if</span> stop:
</span></span><span style="display:flex;"><span>        print(<span style="color:#a5d6ff">&#34;primal feasible &amp; optimal&#34;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">break</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">else</span>:
</span></span><span style="display:flex;"><span>        s <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">1.0</span> <span style="color:#ff7b72;font-weight:bold">/</span> k
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">for</span> i <span style="color:#ff7b72;font-weight:bold">in</span> range(len(x)):
</span></span><span style="display:flex;"><span>            u[i] <span style="color:#ff7b72;font-weight:bold">=</span> max(u[i] <span style="color:#ff7b72;font-weight:bold">-</span> s <span style="color:#ff7b72;font-weight:bold">*</span> (penalties[i]<span style="color:#ff7b72;font-weight:bold">.</span>x), <span style="color:#a5d6ff">0.0</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Pull solution out of m.</span>
</span></span><span style="display:flex;"><span>print(<span style="color:#79c0ff">f</span><span style="color:#a5d6ff">&#34;z = </span><span style="color:#a5d6ff">{</span>m<span style="color:#ff7b72;font-weight:bold">.</span>objVal<span style="color:#a5d6ff">}</span><span style="color:#a5d6ff">&#34;</span>)
</span></span><span style="display:flex;"><span>print(<span style="color:#a5d6ff">&#34;x = [&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> x_i <span style="color:#ff7b72;font-weight:bold">in</span> x:
</span></span><span style="display:flex;"><span>    print(<span style="color:#79c0ff">f</span><span style="color:#a5d6ff">&#34;  </span><span style="color:#a5d6ff">{</span>[<span style="color:#a5d6ff">1</span> <span style="color:#ff7b72">if</span> x_ij<span style="color:#ff7b72;font-weight:bold">.</span>x <span style="color:#ff7b72;font-weight:bold">&gt;=</span> <span style="color:#a5d6ff">0.5</span> <span style="color:#ff7b72">else</span> <span style="color:#a5d6ff">0</span> <span style="color:#ff7b72">for</span> x_ij <span style="color:#ff7b72;font-weight:bold">in</span> x_i]<span style="color:#a5d6ff">}</span><span style="color:#a5d6ff">&#34;</span>)
</span></span><span style="display:flex;"><span>print(<span style="color:#a5d6ff">&#34;]&#34;</span>)
</span></span></code></pre></div><p>Again, the example converges very quickly to an optimal solution.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-txt" data-lang="txt"><span style="display:flex;"><span>iteration 1: z = 48.0, u = [2.0, 2.0, 2.0, 2.0, 2.000], penalties = [0.0, 0.0, 0.0, 0.0, 1.0]
</span></span><span style="display:flex;"><span>iteration 2: z = 47.0, u = [2.0, 2.0, 2.0, 2.0, 1.000], penalties = [0.0, 0.0, 0.0, 0.0, 1.0]
</span></span><span style="display:flex;"><span>iteration 3: z = 46.5, u = [2.0, 2.0, 2.0, 2.0, 0.500], penalties = [0.0, 0.0, 0.0, 0.0, 1.0]
</span></span><span style="display:flex;"><span>iteration 4: z = 46.2, u = [2.0, 2.0, 2.0, 2.0, 0.167], penalties = [0.0, 0.0, 0.0, 0.0, 1.0]
</span></span><span style="display:flex;"><span>iteration 5: z = 46.0, u = [2.0, 2.0, 2.0, 2.0, 0.000], penalties = [0.0, 0.0, 0.0, 0.0, 1.0]
</span></span><span style="display:flex;"><span>primal feasible &amp; optimal
</span></span><span style="display:flex;"><span>z = 46.0
</span></span><span style="display:flex;"><span>x = [
</span></span><span style="display:flex;"><span>  [0, 1, 0]
</span></span><span style="display:flex;"><span>  [0, 1, 0]
</span></span><span style="display:flex;"><span>  [1, 0, 0]
</span></span><span style="display:flex;"><span>  [0, 0, 1]
</span></span><span style="display:flex;"><span>  [0, 0, 0]
</span></span><span style="display:flex;"><span>]
</span></span></code></pre></div><p>Exercise for the reader: change the script to dualize the knapsack constraints instead of the set packing constraints. What is the result of this change in terms of convergence?</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="/files/2012-09-22-lagrangian-relaxation-with-gurobi/gap.py"><code>gap.py</code></a></li>
<li><a href="/files/2012-09-22-lagrangian-relaxation-with-gurobi/gap-lagrangian.py"><code>gap-lagrangian.py</code></a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>🔲 Normal Magic Squares</title>
      <link>https://ryanjoneil.dev/posts/2012-01-13-normal-magic-squares/</link>
      <pubDate>Fri, 13 Jan 2012 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2012-01-13-normal-magic-squares/</guid>
      <description>An integer programming formulation of the normal magic squares problem.</description>
      <content:encoded><![CDATA[<p><em>Note: This post was updated to work with Python 3 and <a href="https://github.com/scipopt/PySCIPOpt">PySCIPOpt</a>. The original version used Python 2 and <a href="https://pythonhosted.org/python-zibopt/">python-zibopt</a>. It has also been edited for clarity.</em></p>
<p>As a followup to the <a href="../2012-01-12-magic-squares-and-big-ms/">last post</a>, I created <a href="/files/2012-01-13-normal-magic-squares/normal-magic-square.py">another SCIP example</a> for finding Normal Magic Squares. This is similar to <a href="https://github.com/CPMpy/cpmpy/blob/master/examples/quickstart_sudoku.ipynb">solving a Sudoku problem</a>, except that here the number of binary variables depends on the square size. In the case of Sudoku, each cell has 9 binary variables &ndash; one for each potential value it might take. For a normal magic square, there are $n^2$ possible values for each cell, $n^2$ cells, and one variable representing the row, column, and diagonal sums. This makes a total of $n^4$ binary variables and one continuous variables in the model.</p>
<p>However, there are no big-Ms.</p>
<p>I think the neat part of this code is in this section:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Construct an expression for each cell that is the sum of</span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># its binary variables with their associated coefficients.</span>
</span></span><span style="display:flex;"><span>sums <span style="color:#ff7b72;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> row <span style="color:#ff7b72;font-weight:bold">in</span> matrix:
</span></span><span style="display:flex;"><span>    sums_row <span style="color:#ff7b72;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> cell <span style="color:#ff7b72;font-weight:bold">in</span> row:
</span></span><span style="display:flex;"><span>        sums_row<span style="color:#ff7b72;font-weight:bold">.</span>append(sum((i <span style="color:#ff7b72;font-weight:bold">+</span> <span style="color:#a5d6ff">1</span>) <span style="color:#ff7b72;font-weight:bold">*</span> x <span style="color:#ff7b72">for</span> i, x <span style="color:#ff7b72;font-weight:bold">in</span> enumerate(cell)))
</span></span><span style="display:flex;"><span>    sums<span style="color:#ff7b72;font-weight:bold">.</span>append(sums_row)
</span></span></code></pre></div><p>It creates sums of the $n^2$ variables for each cell with their appropriate coefficients ($1$ to $n^2$) and stores those expressions to make the subsequent constraint creation simpler.</p>
<p>Another interesting exercise for the reader: Change <a href="/files/2012-01-13-normal-magic-squares/normal-magic-square.py">the code</a> to minimize the sum of each column. How does that impact the solution time?</p>
]]></content:encoded>
    </item>
    <item>
      <title>🔲 Magic Squares and Big-Ms</title>
      <link>https://ryanjoneil.dev/posts/2012-01-12-magic-squares-and-big-ms/</link>
      <pubDate>Thu, 12 Jan 2012 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2012-01-12-magic-squares-and-big-ms/</guid>
      <description>An integer programming formulation of the magic squares problem.</description>
      <content:encoded><![CDATA[<p><em>Note: This post was updated to work with Python 3 and <a href="https://github.com/scipopt/PySCIPOpt">PySCIPOpt</a>. The original version used Python 2 and <a href="https://pythonhosted.org/python-zibopt/">python-zibopt</a>. It has also been edited for clarity.</em></p>
<p>Back in October of 2011, I started toying with a model for finding <a href="https://en.wikipedia.org/wiki/Magic_square">magic squares</a> using SCIP. This is a fun modeling exercise and a challenging problem. First one constructs a square matrix of integer-valued variables.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">from</span> <span style="color:#ff7b72">pyscipopt</span> <span style="color:#ff7b72">import</span> Model
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># [...snip...]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>m <span style="color:#ff7b72;font-weight:bold">=</span> Model()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>matrix <span style="color:#ff7b72;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> i <span style="color:#ff7b72;font-weight:bold">in</span> range(size):
</span></span><span style="display:flex;"><span>    row <span style="color:#ff7b72;font-weight:bold">=</span> [m<span style="color:#ff7b72;font-weight:bold">.</span>addVar(vtype<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#34;I&#34;</span>, lb<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">1</span>) <span style="color:#ff7b72">for</span> _ <span style="color:#ff7b72;font-weight:bold">in</span> range(size)]
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> x <span style="color:#ff7b72;font-weight:bold">in</span> row:
</span></span><span style="display:flex;"><span>        m<span style="color:#ff7b72;font-weight:bold">.</span>addCons(x <span style="color:#ff7b72;font-weight:bold">&lt;=</span> M)
</span></span><span style="display:flex;"><span>    matrix<span style="color:#ff7b72;font-weight:bold">.</span>append(row)
</span></span></code></pre></div><p>Then one adds the following constraints:</p>
<ul>
<li>All variables ≥ 1.</li>
<li>All rows, columns, and the diagonal sum to the same value.</li>
<li>All variables take different values.</li>
</ul>
<p>The first two constraints are trivial to implement, and relatively easy for the solver. What I do is add a single extra variable then set it equal to the sums of each row, column, and the diagonal.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>sum_val <span style="color:#ff7b72;font-weight:bold">=</span> m<span style="color:#ff7b72;font-weight:bold">.</span>addVar(vtype<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#34;M&#34;</span>)
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> i <span style="color:#ff7b72;font-weight:bold">in</span> range(size):
</span></span><span style="display:flex;"><span>    m<span style="color:#ff7b72;font-weight:bold">.</span>addCons(sum(matrix[i]) <span style="color:#ff7b72;font-weight:bold">==</span> sum_val)
</span></span><span style="display:flex;"><span>    m<span style="color:#ff7b72;font-weight:bold">.</span>addCons(sum(matrix[j][i] <span style="color:#ff7b72">for</span> j <span style="color:#ff7b72;font-weight:bold">in</span> range(size)) <span style="color:#ff7b72;font-weight:bold">==</span> sum_val)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>m<span style="color:#ff7b72;font-weight:bold">.</span>addCons(sum(matrix[i][i] <span style="color:#ff7b72">for</span> i <span style="color:#ff7b72;font-weight:bold">in</span> range(size)) <span style="color:#ff7b72;font-weight:bold">==</span> sum_val)
</span></span></code></pre></div><p>It&rsquo;s the third that messes things up. You can think of this as saying, for every possible pair of integer-valued variables $x$ and $y$:</p>
<p>$$ x \ge y + 1 \quad \text{or} \quad x \le y - 1 $$</p>
<p>Why is this hard? Because we can&rsquo;t add both constraints to the model. That would make it infeasible. Instead, we add write them in such a way that exactly one will be active for any any given solution. This requires, for each pair of variables, an additional binary variable $z$ and a (possibly big) constant $M$. Thus we reformulate the above as:</p>
<p>$$
x \ge (y + 1) - M z \
x \le (y - 1) + M (1-z) \
z \in {0,1}
$$</p>
<p>In code this looks like:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">from</span> <span style="color:#ff7b72">itertools</span> <span style="color:#ff7b72">import</span> chain
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>all_vars <span style="color:#ff7b72;font-weight:bold">=</span> list(chain(<span style="color:#ff7b72;font-weight:bold">*</span>matrix))
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> i, x <span style="color:#ff7b72;font-weight:bold">in</span> enumerate(all_vars):
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> y <span style="color:#ff7b72;font-weight:bold">in</span> all_vars[i<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">1</span>:]:
</span></span><span style="display:flex;"><span>        z <span style="color:#ff7b72;font-weight:bold">=</span> m<span style="color:#ff7b72;font-weight:bold">.</span>addVar(vtype<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#34;B&#34;</span>)
</span></span><span style="display:flex;"><span>        m<span style="color:#ff7b72;font-weight:bold">.</span>addCons(x <span style="color:#ff7b72;font-weight:bold">&gt;=</span> y <span style="color:#ff7b72;font-weight:bold">+</span> <span style="color:#a5d6ff">1</span> <span style="color:#ff7b72;font-weight:bold">-</span> M<span style="color:#ff7b72;font-weight:bold">*</span>z)
</span></span><span style="display:flex;"><span>        m<span style="color:#ff7b72;font-weight:bold">.</span>addCons(x <span style="color:#ff7b72;font-weight:bold">&lt;=</span> y <span style="color:#ff7b72;font-weight:bold">-</span> <span style="color:#a5d6ff">1</span> <span style="color:#ff7b72;font-weight:bold">+</span> M<span style="color:#ff7b72;font-weight:bold">*</span>(<span style="color:#a5d6ff">1</span><span style="color:#ff7b72;font-weight:bold">-</span>z))
</span></span></code></pre></div><p>However, <a href="https://orinanobworld.blogspot.com/2011/07/perils-of-big-m.html">here be dragons</a>. We may not know how big (or small) to make $M$. Generally we want it as small as possible to make the LP relaxation of our integer programming model tighter. Different values of $M$ have unpredictable effects on solution time.</p>
<p>Which brings us to an interesting idea:</p>
<p>SCIP now supports bilinear constraints. This means that I can make $M$ a variable in the above model.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">sys</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">try</span>:
</span></span><span style="display:flex;"><span>    M <span style="color:#ff7b72;font-weight:bold">=</span> int(sys<span style="color:#ff7b72;font-weight:bold">.</span>argv[<span style="color:#a5d6ff">2</span>])
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">except</span> <span style="color:#f0883e;font-weight:bold">IndexError</span>:
</span></span><span style="display:flex;"><span>    M <span style="color:#ff7b72;font-weight:bold">=</span> m<span style="color:#ff7b72;font-weight:bold">.</span>addVar(vtype<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#34;M&#34;</span>, lb<span style="color:#ff7b72;font-weight:bold">=</span>size <span style="color:#ff7b72;font-weight:bold">*</span> size)
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">else</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">assert</span> M <span style="color:#ff7b72;font-weight:bold">&gt;=</span> size <span style="color:#ff7b72;font-weight:bold">*</span> size
</span></span></code></pre></div><p>The magic square model linked to in this post provides both options. The first command line argument it requires is the matrix size. The second one, $M$, is optional. If not given, it leaves $M$ up to the solver.</p>
<p>An interesting exercise for the reader: Change <a href="/files/2012-01-12-magic-squares-and-big-ms/magic-square.py">the code</a> to search for a <em>minimal</em> magic square, which minimizes either the value of $M$ or the sums of the columns, rows, and diagonal.</p>
]]></content:encoded>
    </item>
    <item>
      <title>⏳️ Know Your Time Complexities - Part 2</title>
      <link>https://ryanjoneil.dev/posts/2011-11-25-know-your-time-complexities-part-2/</link>
      <pubDate>Fri, 25 Nov 2011 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2011-11-25-know-your-time-complexities-part-2/</guid>
      <description>More on the importance of time complexity in basic programming</description>
      <content:encoded><![CDATA[<p>In response to <a href="../2011-10-25-know-your-time-complexities/">this</a> post, <a href="https://en.wikipedia.org/wiki/Structure_and_Interpretation_of_Computer_Programs">Ben Bitdiddle</a> inquires:</p>
<blockquote>
<p>I understand the concept of using a companion set to remove duplicates from a list while preserving the order of its elements. But what should I do if these elements are composed of smaller pieces? For instance, say I am generating <a href="https://en.wikipedia.org/wiki/Combination">combinations</a> of numbers in which order is unimportant. How do I make a set recognize that <code>[1,2,3]</code> is the same as <code>[3,2,1]</code> in this case?</p>
</blockquote>
<p>There are a couple points that should help here.</p>
<p>While lists are unhashable and therefore cannot be put into sets, tuples are perfectly capable of this. Therefore I cannot do this.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>s <span style="color:#ff7b72;font-weight:bold">=</span> set()
</span></span><span style="display:flex;"><span>s<span style="color:#ff7b72;font-weight:bold">.</span>add([<span style="color:#a5d6ff">1</span>,<span style="color:#a5d6ff">2</span>,<span style="color:#a5d6ff">3</span>])
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-txt" data-lang="txt"><span style="display:flex;"><span>Traceback (most recent call last):
</span></span><span style="display:flex;"><span> File &#34;&lt;stdin&gt;&#34;, line 1, in &lt;module&gt;
</span></span><span style="display:flex;"><span>TypeError: unhashable type: &#39;list&#39;
</span></span></code></pre></div><p>But this works just fine <em>(extra space added for emphasis of tuple parentheses)</em>.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>s<span style="color:#ff7b72;font-weight:bold">.</span>add( (<span style="color:#a5d6ff">1</span>,<span style="color:#a5d6ff">2</span>,<span style="color:#a5d6ff">3</span>) )
</span></span></code></pre></div><p><code>(3,2,1)</code> and <code>(1,2,3)</code> may not hash to the same thing, but tuples are easily sortable. If I sort them before adding them to a set, they look the same.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>tuple(sorted( (<span style="color:#a5d6ff">3</span>,<span style="color:#a5d6ff">2</span>,<span style="color:#a5d6ff">1</span>) ))
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>(<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">3</span>)
</span></span></code></pre></div><p>If I want to be a little fancier, I can user <a href="https://docs.python.org/3/library/itertools.html#itertools.combinations"><code>itertools.combinations</code></a>. The following generates all unique 3-digit combinations of integers from 1 to 4:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">from</span> <span style="color:#ff7b72">itertools</span> <span style="color:#ff7b72">import</span> combinations
</span></span><span style="display:flex;"><span>list(combinations(range(<span style="color:#a5d6ff">1</span>,<span style="color:#a5d6ff">5</span>), <span style="color:#a5d6ff">3</span>))
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>[(<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">3</span>), (<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">4</span>), (<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">3</span>, <span style="color:#a5d6ff">4</span>), (<span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">3</span>, <span style="color:#a5d6ff">4</span>)]
</span></span></code></pre></div><p>Now say I want to only find those that match some condition. I can add a filter to return, say, only those 3-digit combinations of integers from 1 to 6 that multiply to a number divisible by 10:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>list(filter(
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">lambda</span> x: <span style="color:#ff7b72;font-weight:bold">not</span> (x[<span style="color:#a5d6ff">0</span>]<span style="color:#ff7b72;font-weight:bold">*</span>x[<span style="color:#a5d6ff">1</span>]<span style="color:#ff7b72;font-weight:bold">*</span>x[<span style="color:#a5d6ff">2</span>]) <span style="color:#ff7b72;font-weight:bold">%</span> <span style="color:#a5d6ff">10</span>,
</span></span><span style="display:flex;"><span>    combinations(range(<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">7</span>), <span style="color:#a5d6ff">3</span>)
</span></span><span style="display:flex;"><span>))
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>[(<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">5</span>),
</span></span><span style="display:flex;"><span> (<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">5</span>),
</span></span><span style="display:flex;"><span> (<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">5</span>, <span style="color:#a5d6ff">6</span>),
</span></span><span style="display:flex;"><span> (<span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">3</span>, <span style="color:#a5d6ff">5</span>),
</span></span><span style="display:flex;"><span> (<span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">5</span>),
</span></span><span style="display:flex;"><span> (<span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">5</span>, <span style="color:#a5d6ff">6</span>),
</span></span><span style="display:flex;"><span> (<span style="color:#a5d6ff">3</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">5</span>),
</span></span><span style="display:flex;"><span> (<span style="color:#a5d6ff">3</span>, <span style="color:#a5d6ff">5</span>, <span style="color:#a5d6ff">6</span>),
</span></span><span style="display:flex;"><span> (<span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">5</span>, <span style="color:#a5d6ff">6</span>)]
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>⏳️ Know Your Time Complexities</title>
      <link>https://ryanjoneil.dev/posts/2011-10-25-know-your-time-complexities/</link>
      <pubDate>Tue, 25 Oct 2011 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2011-10-25-know-your-time-complexities/</guid>
      <description>The importance of time complexity in basic programming</description>
      <content:encoded><![CDATA[<p>This is based on a lightning talk I gave at the LA PyLadies October Hackathon.</p>
<p>I&rsquo;m actually not going to go into anything much resembling algorithmic complexity here. What I&rsquo;d like to do is present a common performance anti-pattern that I see from novice programmers about once every year or so. If I can prevent one person from committing this error, this post will have achieved its goal. I&rsquo;d also like to show how an intuitive understanding of time required by operations in relation to the size of data they operate on can be helpful.</p>
<p>Say you have a Big List of Things. It doesn&rsquo;t particularly matter what these things are. Often they might be objects or dictionaries of denormalized data. In this example we&rsquo;ll use numbers. Let&rsquo;s generate a list of 1 million integers, each randomly chosen from the first 100 thousand natural numbers:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">random</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>choices <span style="color:#ff7b72;font-weight:bold">=</span> range(<span style="color:#a5d6ff">100000</span>)
</span></span><span style="display:flex;"><span>x <span style="color:#ff7b72;font-weight:bold">=</span> [random<span style="color:#ff7b72;font-weight:bold">.</span>choice(choices) <span style="color:#ff7b72">for</span> i <span style="color:#ff7b72;font-weight:bold">in</span> range(<span style="color:#a5d6ff">1000000</span>)]
</span></span></code></pre></div><p>Now say you want to remove (or aggregate, or structure) duplicate data while keeping them <em>in order of appearance</em>. Intuitively, this seems simple enough. A first solution might involve creating a new empty list, iterating over x, and only appending those items that are not already in the new list.</p>
<h2 id="the-bad-way">The Bad Way</h2>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>order <span style="color:#ff7b72;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> i <span style="color:#ff7b72;font-weight:bold">in</span> x:
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">if</span> i <span style="color:#ff7b72;font-weight:bold">not</span> <span style="color:#ff7b72;font-weight:bold">in</span> order:
</span></span><span style="display:flex;"><span>        order<span style="color:#ff7b72;font-weight:bold">.</span>append(i)
</span></span></code></pre></div><p>Try running this. What&rsquo;s wrong with it?</p>
<p>The issue is the conditional on line 3. In the worst case, it could look at every item in the order list for each item in x. If the list is big, as it is in our example, that wastes a lot of cycles. We can reason that we can improve the performance of our code by replacing this conditional with something faster.</p>
<h2 id="the-good-way">The Good Way</h2>
<p>Given that sets have near constant time for membership tests, one solution is to create a companion data structure, which we&rsquo;ll call seen. Being a set, it doesn&rsquo;t care about the order of the items, but it will allow us to test for membership quickly.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>order <span style="color:#ff7b72;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>seen <span style="color:#ff7b72;font-weight:bold">=</span> set()
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> i <span style="color:#ff7b72;font-weight:bold">in</span> x:
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">if</span> i <span style="color:#ff7b72;font-weight:bold">not</span> <span style="color:#ff7b72;font-weight:bold">in</span> seen:
</span></span><span style="display:flex;"><span>        seen<span style="color:#ff7b72;font-weight:bold">.</span>add(i)
</span></span><span style="display:flex;"><span>        order<span style="color:#ff7b72;font-weight:bold">.</span>append(i)
</span></span></code></pre></div><p>Now try running this. Better?</p>
<p>Not that this is the best way to perform this particular action. If you aren&rsquo;t familiar with it, take a look at the <a href="http://docs.python.org/library/itertools.html#itertools.groupby"><code>groupby</code></a> function from <code>itertools</code>, which is what I will sometimes reach for in a case like this.</p>
]]></content:encoded>
    </item>
    <item>
      <title>🎰 Deterministic vs. Stochastic Simulation</title>
      <link>https://ryanjoneil.dev/posts/2011-06-11-deterministic-vs-stochastic-simulation/</link>
      <pubDate>Sat, 11 Jun 2011 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2011-06-11-deterministic-vs-stochastic-simulation/</guid>
      <description>The importance of randomness in simulations</description>
      <content:encoded><![CDATA[<p>I find I have to build simulations with increasing frequency in my work and life. Usually this indicates I&rsquo;m faced with one of the following situations:</p>
<ul>
<li>The need for a quick estimate regarding the quantitative behavior of some situation.</li>
<li>The desire to verify the result of a computation or assumption.</li>
<li>A situation which is too complex or random to effectively model or understand.</li>
</ul>
<p>Anyone familiar at all with simulation will recognize the last item as the motivating force of the entire field. Simulation models tend to take over when systems become so complex that understanding them is prohibitive in cost and time or entirely infeasible. In a simulation, the modeler can focus on individual interactions between entities while still hoping for useful output in the form of descriptive statistics.</p>
<p>As such, simulations are nearly always stochastic. The output of a simulation, whether it be the mean time to service upon entering a queue or the number of fish alive in a pond, is determined by a number of random inputs. It is estimated by looking at a sample of the entire, often infinite, problem space and therefore must be described in terms of mean and variance.</p>
<p>For me, simulation building usually follows a process roughly like this:</p>
<ul>
<li>Work with a domain expert to understand the process under study.</li>
<li>Convert this process into a deterministic simulation (no randomness).</li>
<li>Verify the output of the deterministic simulation.</li>
<li>Anlyze the inputs of the simulation to determine their probability distributions.</li>
<li>Convert the deterministic simulation to a stochastic simulation.</li>
</ul>
<p>The reason for creating a simulation without randomness first is that it can be difficult or impossible to verify its correctness otherwise. Thus one may focus on the simulation logic first before analyzing and adding sources of randomness.</p>
<p>Where the procedure breaks down is after the third step. Domain experts are often happy to share their knowledge about systems to aid in designing simulations, and typically can understand the resulting abstractions. They are also invaluable in verifying simulation output. However, they are unlikely to understand why it is necessary to add randomness to a system that they already perceive as functional. Further, doing so can be just as difficult and time consuming as the initial model development and therefore requires justification.</p>
<p>This can be a quandary for the model builder. How does one communicate the need to incorporate randomness to decision makers who lack understanding of probability? It is trivially easy to construct simulations that use the same input parameters but yield drastically different outputs. Consider the code below, which simulates two events occurring and counts the number of times event b happens before event a.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">random</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">sim_stochastic</span>(event_a_lambda, event_b_lambda):
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Returns 0 if event A arrives first, 1 if event B arrives first</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Calculate next arrival time for each event randomly.</span>
</span></span><span style="display:flex;"><span>    event_a_arrival <span style="color:#ff7b72;font-weight:bold">=</span> random<span style="color:#ff7b72;font-weight:bold">.</span>expovariate(event_a_lambda)
</span></span><span style="display:flex;"><span>    event_b_arrival <span style="color:#ff7b72;font-weight:bold">=</span> random<span style="color:#ff7b72;font-weight:bold">.</span>expovariate(event_b_lambda)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> <span style="color:#a5d6ff">0.0</span> <span style="color:#ff7b72">if</span> event_a_arrival <span style="color:#ff7b72;font-weight:bold">&lt;=</span> event_b_arrival <span style="color:#ff7b72">else</span> <span style="color:#a5d6ff">1.0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">sim_deterministic</span>(event_a_lambda, event_b_lambda):
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Returns 0 if event A arrives first, 1 if event B arrives first</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Calculate next arrival time for each event deterministically.</span>
</span></span><span style="display:flex;"><span>    event_a_arrival <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">1.0</span> <span style="color:#ff7b72;font-weight:bold">/</span> event_a_lambda
</span></span><span style="display:flex;"><span>    event_b_arrival <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">1.0</span> <span style="color:#ff7b72;font-weight:bold">/</span> event_b_lambda
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> <span style="color:#a5d6ff">0.0</span> <span style="color:#ff7b72">if</span> event_a_arrival <span style="color:#ff7b72;font-weight:bold">&lt;=</span> event_b_arrival <span style="color:#ff7b72">else</span> <span style="color:#a5d6ff">1.0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">if</span> <span style="color:#79c0ff">__name__</span> <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;__main__&#39;</span>:
</span></span><span style="display:flex;"><span>    event_a_lambda <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">0.3</span>
</span></span><span style="display:flex;"><span>    event_b_lambda <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">0.5</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    repetitions <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">10000</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> sim <span style="color:#ff7b72;font-weight:bold">in</span> (sim_stochastic, sim_deterministic):
</span></span><span style="display:flex;"><span>        output <span style="color:#ff7b72;font-weight:bold">=</span> [
</span></span><span style="display:flex;"><span>            sim(event_a_lambda, event_b_lambda)
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">for</span> _ <span style="color:#ff7b72;font-weight:bold">in</span> range(repetitions)
</span></span><span style="display:flex;"><span>        ]
</span></span><span style="display:flex;"><span>        event_b_first <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">100.0</span> <span style="color:#ff7b72;font-weight:bold">*</span> (sum(output) <span style="color:#ff7b72;font-weight:bold">/</span> len(output))
</span></span><span style="display:flex;"><span>        print(<span style="color:#a5d6ff">&#39;event b is first </span><span style="color:#a5d6ff">%0.1f%%</span><span style="color:#a5d6ff"> of the time&#39;</span> <span style="color:#ff7b72;font-weight:bold">%</span> event_b_first)
</span></span></code></pre></div><p>Both simulations use the same input parameter, but the second one is essentially wrong as b will always happen first. In the stochastic version, we use exponential distributions for the inputs and obtain an output that verifies our basic understanding of these distributions.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-txt" data-lang="txt"><span style="display:flex;"><span>event b is first 63.0% of the time
</span></span><span style="display:flex;"><span>event b is first 100.0% of the time
</span></span></code></pre></div><p>How about you? How do you discuss the need to model a random world with decision makers?</p>
]]></content:encoded>
    </item>
    <item>
      <title>🔮 NetworkX and Python Futures</title>
      <link>https://ryanjoneil.dev/posts/2011-05-19-networkx-and-python-futures/</link>
      <pubDate>Thu, 19 May 2011 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2011-05-19-networkx-and-python-futures/</guid>
      <description>Solve graph problems on multiple cores NetworkX and Python futures</description>
      <content:encoded><![CDATA[<p><em>Note: This post was updated to work with <a href="https://networkx.org/">NetworkX</a> and for clarity.</em></p>
<p>It&rsquo;s possible this will turn out like the day when Python 2.5 introduced <a href="https://docs.python.org/release/2.5/whatsnew/pep-342.html">coroutines</a>. At the time I was very excited. I spent several hours trying to convince my coworkers we should immediately abandon all our existing Java infrastructure and port it to finite state machines implemented using Python coroutines. After a day of hand waving over a proof of concept, we put that idea aside and went about our lives.</p>
<p>Soon after, I left for a Python shop, but in the next half decade I still never found a good place to use this interesting feature.</p>
<p>But it doesn&rsquo;t feel like that.</p>
<p>As I come to terms more with switching to Python 3.2, the <a href="https://docs.python.org/py3k/library/concurrent.futures.html">futures</a> module seems similarly exciting. I wish I&rsquo;d had it years ago, and it&rsquo;s almost reason in itself to upgrade from Python 2.7. <em>Who cares if none of your libraries have been ported yet?</em></p>
<p>This library lets you take any function and distribute it over a process pool. To test that out, we&rsquo;ll generate a bunch of random graphs and iterate over all their <a href="https://en.wikipedia.org/wiki/Clique_(graph_theory)">cliques</a>.</p>
<h2 id="code">Code</h2>
<p>First, let&rsquo;s generate some test data using the <a href="https://networkx.org/documentation/stable/reference/generated/networkx.generators.random_graphs.gnm_random_graph.html"><code>dense_gnm_random_graph</code></a> function. Our data includes 1000 random graphs, each with 100 nodes and 100 * 100 edges.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">networkx</span> <span style="color:#ff7b72">as</span> <span style="color:#ff7b72">nx</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>n <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">100</span>
</span></span><span style="display:flex;"><span>graphs <span style="color:#ff7b72;font-weight:bold">=</span> [nx<span style="color:#ff7b72;font-weight:bold">.</span>dense_gnm_random_graph(n, n<span style="color:#ff7b72;font-weight:bold">*</span>n) <span style="color:#ff7b72">for</span> _ <span style="color:#ff7b72;font-weight:bold">in</span> range(<span style="color:#a5d6ff">1000</span>)]
</span></span></code></pre></div><p>Now we write a function iterate over all cliques in a given graph. NetworkX provides a <code>find_cliques</code> function which returns a generator. Iterating over them ensures we will run through the entire process of finding all cliques for a graph.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">iterate_cliques</span>(g):
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> _ <span style="color:#ff7b72;font-weight:bold">in</span> nx<span style="color:#ff7b72;font-weight:bold">.</span>find_cliques(g):
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">pass</span>
</span></span></code></pre></div><p>Now we just define two functions, one for running in serial and one for running in parallel using <code>futures</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">from</span> <span style="color:#ff7b72">concurrent</span> <span style="color:#ff7b72">import</span> futures
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">serial_test</span>(graphs):
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> g <span style="color:#ff7b72;font-weight:bold">in</span> graphs:
</span></span><span style="display:flex;"><span>        iterate_cliques(g)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">parallel_test</span>(graphs, max_workers):
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">with</span> futures<span style="color:#ff7b72;font-weight:bold">.</span>ProcessPoolExecutor(max_workers<span style="color:#ff7b72;font-weight:bold">=</span>max_workers) <span style="color:#ff7b72">as</span> executor:
</span></span><span style="display:flex;"><span>        executor<span style="color:#ff7b72;font-weight:bold">.</span>map(iterate_cliques, graphs)
</span></span></code></pre></div><p>Our <code>__main__</code> simply generates the random graphs, samples from them, times both functions, and write CSV data to standard output.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">from</span> <span style="color:#ff7b72">csv</span> <span style="color:#ff7b72">import</span> writer
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">random</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">sys</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">time</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">if</span> <span style="color:#79c0ff">__name__</span> <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;__main__&#39;</span>:
</span></span><span style="display:flex;"><span>    out <span style="color:#ff7b72;font-weight:bold">=</span> writer(sys<span style="color:#ff7b72;font-weight:bold">.</span>stdout)
</span></span><span style="display:flex;"><span>    out<span style="color:#ff7b72;font-weight:bold">.</span>writerow([<span style="color:#a5d6ff">&#39;num graphs&#39;</span>, <span style="color:#a5d6ff">&#39;serial time&#39;</span>, <span style="color:#a5d6ff">&#39;parallel time&#39;</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    n <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">100</span>
</span></span><span style="display:flex;"><span>    graphs <span style="color:#ff7b72;font-weight:bold">=</span> [nx<span style="color:#ff7b72;font-weight:bold">.</span>dense_gnm_random_graph(n, n<span style="color:#ff7b72;font-weight:bold">*</span>n) <span style="color:#ff7b72">for</span> _ <span style="color:#ff7b72;font-weight:bold">in</span> range(<span style="color:#a5d6ff">1000</span>)]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Run with a number of different randomly generated graphs</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> num_graphs <span style="color:#ff7b72;font-weight:bold">in</span> range(<span style="color:#a5d6ff">50</span>, <span style="color:#a5d6ff">1001</span>, <span style="color:#a5d6ff">50</span>):
</span></span><span style="display:flex;"><span>        sample <span style="color:#ff7b72;font-weight:bold">=</span> random<span style="color:#ff7b72;font-weight:bold">.</span>choices(graphs, k <span style="color:#ff7b72;font-weight:bold">=</span> num_graphs)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        start <span style="color:#ff7b72;font-weight:bold">=</span> time<span style="color:#ff7b72;font-weight:bold">.</span>time()
</span></span><span style="display:flex;"><span>        serial_test(sample)
</span></span><span style="display:flex;"><span>        serial_time <span style="color:#ff7b72;font-weight:bold">=</span> time<span style="color:#ff7b72;font-weight:bold">.</span>time() <span style="color:#ff7b72;font-weight:bold">-</span> start
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        start <span style="color:#ff7b72;font-weight:bold">=</span> time<span style="color:#ff7b72;font-weight:bold">.</span>time()
</span></span><span style="display:flex;"><span>        parallel_test(sample, <span style="color:#a5d6ff">16</span>)
</span></span><span style="display:flex;"><span>        parallel_time <span style="color:#ff7b72;font-weight:bold">=</span> time<span style="color:#ff7b72;font-weight:bold">.</span>time() <span style="color:#ff7b72;font-weight:bold">-</span> start
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        out<span style="color:#ff7b72;font-weight:bold">.</span>writerow([num_graphs, serial_time, parallel_time])
</span></span></code></pre></div><p>The output of this script shows that we get a fairly linear speedup to this code with little effort.</p>
<p><img alt="Speedup" loading="lazy" src="/files/2011-05-19-networkx-and-python-futures/speedup.png#center"></p>
<p>I ran this on a machine with 8 cores and hyperthreading. Eyeballing the chart, it looks like the speedup is roughly 5x. My system monitor shows spikes on CPU usage across cores whenever the parallel test runs.</p>
<p><img alt="CPU usage" loading="lazy" src="/files/2011-05-19-networkx-and-python-futures/cpu.png#center"></p>
<h2 id="resources">Resources</h2>
<ul>
<li>Output <a href="/files/2011-05-19-networkx-and-python-futures/data.csv">data</a></li>
<li>Full <a href="/files/2011-05-19-networkx-and-python-futures/iterate-cliques.py">source listing</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>🐪 Reformed JAPHs: Transpiler</title>
      <link>https://ryanjoneil.dev/posts/2011-04-18-reformed-japhs-transpiler/</link>
      <pubDate>Wed, 20 Apr 2011 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2011-04-18-reformed-japhs-transpiler/</guid>
      <description>Scheme to Python transpiler</description>
      <content:encoded><![CDATA[<p><em>Note: This post was edited for clarity.</em></p>
<p>For the final JAPH in this series, I implemented a simple transpiler that converts a small subset of <a href="https://www.scheme.org/">Scheme</a> programs to equivalent Python programs. It starts with a Scheme program that prints <code>'just another scheme hacker'</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-scheme" data-lang="scheme"><span style="display:flex;"><span>(<span style="color:#ff7b72">define </span>(<span style="color:#d2a8ff;font-weight:bold">output</span> <span style="color:#79c0ff">x</span>)
</span></span><span style="display:flex;"><span>    (<span style="color:#ff7b72">if </span>(null? <span style="color:#79c0ff">x</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#a5d6ff">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>        (<span style="color:#ff7b72">begin </span>(display (car <span style="color:#79c0ff">x</span>))
</span></span><span style="display:flex;"><span>                (<span style="color:#ff7b72">if </span>(null? (cdr <span style="color:#79c0ff">x</span>))
</span></span><span style="display:flex;"><span>                    (display <span style="color:#a5d6ff">&#34;\n&#34;</span>)
</span></span><span style="display:flex;"><span>                    (<span style="color:#ff7b72">begin </span>(display <span style="color:#a5d6ff">&#34; &#34;</span>)
</span></span><span style="display:flex;"><span>                            (<span style="color:#d2a8ff;font-weight:bold">output</span> (cdr <span style="color:#79c0ff">x</span>)))))))
</span></span><span style="display:flex;"><span>(<span style="color:#d2a8ff;font-weight:bold">output</span> (list <span style="color:#a5d6ff">&#34;just&#34;</span> <span style="color:#a5d6ff">&#34;another&#34;</span> <span style="color:#a5d6ff">&#34;scheme&#34;</span> <span style="color:#a5d6ff">&#34;hacker&#34;</span>))
</span></span></code></pre></div><p>The program then tokenizes that Scheme source, parses the token stream, and converts that into Python 3.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">output</span>(x):
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">if</span> <span style="color:#ff7b72;font-weight:bold">not</span> x:
</span></span><span style="display:flex;"><span>        <span style="color:#a5d6ff">&#34;&#34;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">else</span>:
</span></span><span style="display:flex;"><span>        print(x[<span style="color:#a5d6ff">0</span>], end<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;&#39;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">if</span> <span style="color:#ff7b72;font-weight:bold">not</span> x[<span style="color:#a5d6ff">1</span>:]:
</span></span><span style="display:flex;"><span>            print(<span style="color:#a5d6ff">&#34;</span><span style="color:#79c0ff">\n</span><span style="color:#a5d6ff">&#34;</span>, end<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;&#39;</span>)
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">else</span>:
</span></span><span style="display:flex;"><span>            print(<span style="color:#a5d6ff">&#34; &#34;</span>, end<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;&#39;</span>)
</span></span><span style="display:flex;"><span>            output(x[<span style="color:#a5d6ff">1</span>:])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>output([<span style="color:#a5d6ff">&#34;just&#34;</span>, <span style="color:#a5d6ff">&#34;another&#34;</span>, <span style="color:#a5d6ff">&#34;python&#34;</span>, <span style="color:#a5d6ff">&#34;hacker&#34;</span>])
</span></span></code></pre></div><p>Finally it executes the resulting Python string using <code>exec</code>. Obfuscation is left as an exercise for the reader.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">re</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">tokenize</span>(input):
</span></span><span style="display:flex;"><span>    <span style="color:#a5d6ff">&#39;&#39;&#39;Tokenizes an input stream into a list of recognizable tokens&#39;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>    token_res <span style="color:#ff7b72;font-weight:bold">=</span> (
</span></span><span style="display:flex;"><span>        <span style="color:#79c0ff">r</span><span style="color:#a5d6ff">&#39;\(&#39;</span>,      <span style="color:#8b949e;font-style:italic"># open paren -&gt; starts expression</span>
</span></span><span style="display:flex;"><span>        <span style="color:#79c0ff">r</span><span style="color:#a5d6ff">&#39;\)&#39;</span>,      <span style="color:#8b949e;font-style:italic"># close paren -&gt; ends expression</span>
</span></span><span style="display:flex;"><span>        <span style="color:#79c0ff">r</span><span style="color:#a5d6ff">&#39;&#34;[^&#34;]*&#34;&#39;</span>, <span style="color:#8b949e;font-style:italic"># quoted string (don&#39;t support \&#34; yet)</span>
</span></span><span style="display:flex;"><span>        <span style="color:#79c0ff">r</span><span style="color:#a5d6ff">&#39;[\w?]+&#39;</span>   <span style="color:#8b949e;font-style:italic"># atom</span>
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> re<span style="color:#ff7b72;font-weight:bold">.</span>findall(<span style="color:#79c0ff">r</span><span style="color:#a5d6ff">&#39;(&#39;</span> <span style="color:#ff7b72;font-weight:bold">+</span> <span style="color:#a5d6ff">&#39;|&#39;</span><span style="color:#ff7b72;font-weight:bold">.</span>join(token_res) <span style="color:#ff7b72;font-weight:bold">+</span> <span style="color:#a5d6ff">&#39;)&#39;</span>, input)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">parse</span>(stream):
</span></span><span style="display:flex;"><span>    <span style="color:#a5d6ff">&#39;&#39;&#39;Parses a token stream into a syntax tree&#39;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">if</span> <span style="color:#ff7b72;font-weight:bold">not</span> stream:
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">return</span> []
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#8b949e;font-style:italic"># Build a list of arguments (possibly expressions) at this level</span>
</span></span><span style="display:flex;"><span>        args <span style="color:#ff7b72;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">while</span> <span style="color:#79c0ff">True</span>:
</span></span><span style="display:flex;"><span>            <span style="color:#8b949e;font-style:italic"># Get the next token</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">try</span>:
</span></span><span style="display:flex;"><span>                x <span style="color:#ff7b72;font-weight:bold">=</span> stream<span style="color:#ff7b72;font-weight:bold">.</span>pop(<span style="color:#a5d6ff">0</span>)
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">except</span> <span style="color:#f0883e;font-weight:bold">IndexError</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#ff7b72">return</span> args
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#8b949e;font-style:italic"># ( and ) control the level of the tree we&#39;re at</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">if</span> x <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;(&#39;</span>:
</span></span><span style="display:flex;"><span>                args<span style="color:#ff7b72;font-weight:bold">.</span>append(parse(stream))
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">elif</span> x <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;)&#39;</span>:
</span></span><span style="display:flex;"><span>                <span style="color:#ff7b72">return</span> args
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">else</span>:
</span></span><span style="display:flex;"><span>                args<span style="color:#ff7b72;font-weight:bold">.</span>append(x)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">compile</span>(tree):
</span></span><span style="display:flex;"><span>    <span style="color:#a5d6ff">&#39;&#39;&#39;Compiles an Scheme Abstract Syntax Tree into near-Python&#39;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">compile_expr</span>(indent, expr):
</span></span><span style="display:flex;"><span>        indent <span style="color:#ff7b72;font-weight:bold">+=</span> <span style="color:#a5d6ff">1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        lines <span style="color:#ff7b72;font-weight:bold">=</span> [] <span style="color:#8b949e;font-style:italic"># these will have [(indent, statement), ...] structure</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">while</span> expr:
</span></span><span style="display:flex;"><span>            <span style="color:#8b949e;font-style:italic"># Two options: expr is a string like &#34;&#39;&#34; or it is a list</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">if</span> isinstance(expr, str):
</span></span><span style="display:flex;"><span>                <span style="color:#ff7b72">return</span> [(
</span></span><span style="display:flex;"><span>                    indent,
</span></span><span style="display:flex;"><span>                    expr<span style="color:#ff7b72;font-weight:bold">.</span>replace(<span style="color:#a5d6ff">&#39;scheme&#39;</span>, <span style="color:#a5d6ff">&#39;python&#39;</span>)<span style="color:#ff7b72;font-weight:bold">.</span>replace(<span style="color:#a5d6ff">&#39;</span><span style="color:#79c0ff">\n</span><span style="color:#a5d6ff">&#39;</span>, <span style="color:#a5d6ff">&#39;</span><span style="color:#79c0ff">\\</span><span style="color:#a5d6ff">n&#39;</span>)
</span></span><span style="display:flex;"><span>                )]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">else</span>:
</span></span><span style="display:flex;"><span>                start <span style="color:#ff7b72;font-weight:bold">=</span> expr<span style="color:#ff7b72;font-weight:bold">.</span>pop(<span style="color:#a5d6ff">0</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#ff7b72">if</span> start <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;define&#39;</span>:
</span></span><span style="display:flex;"><span>                    signature <span style="color:#ff7b72;font-weight:bold">=</span> expr<span style="color:#ff7b72;font-weight:bold">.</span>pop(<span style="color:#a5d6ff">0</span>)
</span></span><span style="display:flex;"><span>                    lines<span style="color:#ff7b72;font-weight:bold">.</span>append((indent,
</span></span><span style="display:flex;"><span>                        <span style="color:#a5d6ff">&#39;def </span><span style="color:#a5d6ff">%s</span><span style="color:#a5d6ff">(</span><span style="color:#a5d6ff">%s</span><span style="color:#a5d6ff">):&#39;</span> <span style="color:#ff7b72;font-weight:bold">%</span> (
</span></span><span style="display:flex;"><span>                            signature[<span style="color:#a5d6ff">0</span>],
</span></span><span style="display:flex;"><span>                            <span style="color:#a5d6ff">&#39;, &#39;</span><span style="color:#ff7b72;font-weight:bold">.</span>join(signature[<span style="color:#a5d6ff">1</span>:])
</span></span><span style="display:flex;"><span>                        )
</span></span><span style="display:flex;"><span>                    ))
</span></span><span style="display:flex;"><span>                    <span style="color:#ff7b72">while</span> expr:
</span></span><span style="display:flex;"><span>                        lines<span style="color:#ff7b72;font-weight:bold">.</span>extend(compile_expr(indent, expr<span style="color:#ff7b72;font-weight:bold">.</span>pop(<span style="color:#a5d6ff">0</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#ff7b72">elif</span> start <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;if&#39;</span>:
</span></span><span style="display:flex;"><span>                    <span style="color:#8b949e;font-style:italic"># We don&#39;t support multi-clause conditionals yet</span>
</span></span><span style="display:flex;"><span>                    clause <span style="color:#ff7b72;font-weight:bold">=</span> compile_expr(indent, expr<span style="color:#ff7b72;font-weight:bold">.</span>pop(<span style="color:#a5d6ff">0</span>))[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">1</span>]
</span></span><span style="display:flex;"><span>                    lines<span style="color:#ff7b72;font-weight:bold">.</span>append((indent, <span style="color:#a5d6ff">&#39;if </span><span style="color:#a5d6ff">%s</span><span style="color:#a5d6ff">:&#39;</span> <span style="color:#ff7b72;font-weight:bold">%</span> clause))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                    if_true_lines <span style="color:#ff7b72;font-weight:bold">=</span> compile_expr(indent, expr<span style="color:#ff7b72;font-weight:bold">.</span>pop(<span style="color:#a5d6ff">0</span>))
</span></span><span style="display:flex;"><span>                    if_false_lines <span style="color:#ff7b72;font-weight:bold">=</span> compile_expr(indent, expr<span style="color:#ff7b72;font-weight:bold">.</span>pop(<span style="color:#a5d6ff">0</span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                    lines<span style="color:#ff7b72;font-weight:bold">.</span>extend(if_true_lines)
</span></span><span style="display:flex;"><span>                    lines<span style="color:#ff7b72;font-weight:bold">.</span>append((indent, <span style="color:#a5d6ff">&#39;else:&#39;</span>))
</span></span><span style="display:flex;"><span>                    lines<span style="color:#ff7b72;font-weight:bold">.</span>extend(if_false_lines)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#ff7b72">elif</span> start <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;null?&#39;</span>:
</span></span><span style="display:flex;"><span>                    <span style="color:#8b949e;font-style:italic"># Only supports conditionals of the form (null? foo)</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#ff7b72">if</span> isinstance(expr[<span style="color:#a5d6ff">0</span>], str):
</span></span><span style="display:flex;"><span>                        condition <span style="color:#ff7b72;font-weight:bold">=</span> expr<span style="color:#ff7b72;font-weight:bold">.</span>pop(<span style="color:#a5d6ff">0</span>)
</span></span><span style="display:flex;"><span>                    <span style="color:#ff7b72">else</span>:
</span></span><span style="display:flex;"><span>                        condition <span style="color:#ff7b72;font-weight:bold">=</span> compile_expr(indent, expr<span style="color:#ff7b72;font-weight:bold">.</span>pop(<span style="color:#a5d6ff">0</span>))[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">1</span>]
</span></span><span style="display:flex;"><span>                    <span style="color:#ff7b72">return</span> [(indent, <span style="color:#a5d6ff">&#39;not </span><span style="color:#a5d6ff">%s</span><span style="color:#a5d6ff">&#39;</span> <span style="color:#ff7b72;font-weight:bold">%</span> condition)]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#ff7b72">elif</span> start <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;begin&#39;</span>:
</span></span><span style="display:flex;"><span>                    <span style="color:#8b949e;font-style:italic"># This is just a series of statements, so don&#39;t indent</span>
</span></span><span style="display:flex;"><span>                    <span style="color:#ff7b72">while</span> expr:
</span></span><span style="display:flex;"><span>                        lines<span style="color:#ff7b72;font-weight:bold">.</span>extend(compile_expr(indent<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">1</span>, expr<span style="color:#ff7b72;font-weight:bold">.</span>pop(<span style="color:#a5d6ff">0</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#ff7b72">elif</span> start <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;display&#39;</span>:
</span></span><span style="display:flex;"><span>                    arguments <span style="color:#ff7b72;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>                    <span style="color:#ff7b72">while</span> expr:
</span></span><span style="display:flex;"><span>                        arguments<span style="color:#ff7b72;font-weight:bold">.</span>append(
</span></span><span style="display:flex;"><span>                            compile_expr(indent, expr<span style="color:#ff7b72;font-weight:bold">.</span>pop(<span style="color:#a5d6ff">0</span>))[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">1</span>]
</span></span><span style="display:flex;"><span>                        )
</span></span><span style="display:flex;"><span>                    lines<span style="color:#ff7b72;font-weight:bold">.</span>append((
</span></span><span style="display:flex;"><span>                        indent,
</span></span><span style="display:flex;"><span>                        <span style="color:#a5d6ff">&#34;print(</span><span style="color:#a5d6ff">%s</span><span style="color:#a5d6ff">, end=&#39;&#39;)&#34;</span> <span style="color:#ff7b72;font-weight:bold">%</span> (<span style="color:#a5d6ff">&#39;, &#39;</span><span style="color:#ff7b72;font-weight:bold">.</span>join(arguments))
</span></span><span style="display:flex;"><span>                    ))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#ff7b72">elif</span> start <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;car&#39;</span>:
</span></span><span style="display:flex;"><span>                    lines<span style="color:#ff7b72;font-weight:bold">.</span>append((indent, <span style="color:#a5d6ff">&#39;</span><span style="color:#a5d6ff">%s</span><span style="color:#a5d6ff">[0]&#39;</span> <span style="color:#ff7b72;font-weight:bold">%</span> expr<span style="color:#ff7b72;font-weight:bold">.</span>pop(<span style="color:#a5d6ff">0</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#ff7b72">elif</span> start <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;cdr&#39;</span>:
</span></span><span style="display:flex;"><span>                    lines<span style="color:#ff7b72;font-weight:bold">.</span>append((indent, <span style="color:#a5d6ff">&#39;</span><span style="color:#a5d6ff">%s</span><span style="color:#a5d6ff">[1:]&#39;</span> <span style="color:#ff7b72;font-weight:bold">%</span> expr<span style="color:#ff7b72;font-weight:bold">.</span>pop(<span style="color:#a5d6ff">0</span>)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#ff7b72">elif</span> start <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;list&#39;</span>:
</span></span><span style="display:flex;"><span>                    arguments <span style="color:#ff7b72;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>                    <span style="color:#ff7b72">while</span> expr:
</span></span><span style="display:flex;"><span>                        arguments<span style="color:#ff7b72;font-weight:bold">.</span>append(
</span></span><span style="display:flex;"><span>                            compile_expr(indent, expr<span style="color:#ff7b72;font-weight:bold">.</span>pop(<span style="color:#a5d6ff">0</span>))[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">1</span>]
</span></span><span style="display:flex;"><span>                        )
</span></span><span style="display:flex;"><span>                    lines<span style="color:#ff7b72;font-weight:bold">.</span>append((indent, <span style="color:#a5d6ff">&#39;[</span><span style="color:#a5d6ff">%s</span><span style="color:#a5d6ff">]&#39;</span> <span style="color:#ff7b72;font-weight:bold">%</span> <span style="color:#a5d6ff">&#39;, &#39;</span><span style="color:#ff7b72;font-weight:bold">.</span>join(arguments)))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                <span style="color:#ff7b72">else</span>:
</span></span><span style="display:flex;"><span>                    <span style="color:#8b949e;font-style:italic"># Assume this is a function call</span>
</span></span><span style="display:flex;"><span>                    arguments <span style="color:#ff7b72;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>                    <span style="color:#ff7b72">while</span> expr:
</span></span><span style="display:flex;"><span>                        arguments<span style="color:#ff7b72;font-weight:bold">.</span>append(
</span></span><span style="display:flex;"><span>                            compile_expr(indent, expr<span style="color:#ff7b72;font-weight:bold">.</span>pop(<span style="color:#a5d6ff">0</span>))[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">1</span>]
</span></span><span style="display:flex;"><span>                        )
</span></span><span style="display:flex;"><span>                    lines<span style="color:#ff7b72;font-weight:bold">.</span>append((
</span></span><span style="display:flex;"><span>                        indent,
</span></span><span style="display:flex;"><span>                        <span style="color:#a5d6ff">&#34;</span><span style="color:#a5d6ff">%s</span><span style="color:#a5d6ff">(</span><span style="color:#a5d6ff">%s</span><span style="color:#a5d6ff">)&#34;</span> <span style="color:#ff7b72;font-weight:bold">%</span> (start, <span style="color:#a5d6ff">&#39;, &#39;</span><span style="color:#ff7b72;font-weight:bold">.</span>join(arguments))
</span></span><span style="display:flex;"><span>                    ))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">return</span> lines
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> [compile_expr(<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">1</span>, expr) <span style="color:#ff7b72">for</span> expr <span style="color:#ff7b72;font-weight:bold">in</span> tree]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">if</span> <span style="color:#79c0ff">__name__</span> <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;__main__&#39;</span>:
</span></span><span style="display:flex;"><span>    scheme <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">&#39;&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#a5d6ff">        (define (output x)
</span></span></span><span style="display:flex;"><span><span style="color:#a5d6ff">            (if (null? x)
</span></span></span><span style="display:flex;"><span><span style="color:#a5d6ff">                &#34;&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#a5d6ff">                (begin (display (car x))
</span></span></span><span style="display:flex;"><span><span style="color:#a5d6ff">                       (if (null? (cdr x))
</span></span></span><span style="display:flex;"><span><span style="color:#a5d6ff">                           (display &#34;</span><span style="color:#79c0ff">\n</span><span style="color:#a5d6ff">&#34;)
</span></span></span><span style="display:flex;"><span><span style="color:#a5d6ff">                           (begin (display &#34; &#34;)
</span></span></span><span style="display:flex;"><span><span style="color:#a5d6ff">                                  (output (cdr x)))))))
</span></span></span><span style="display:flex;"><span><span style="color:#a5d6ff">        (output (list &#34;just&#34; &#34;another&#34; &#34;scheme&#34; &#34;hacker&#34;))
</span></span></span><span style="display:flex;"><span><span style="color:#a5d6ff">    &#39;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>    python <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> expr <span style="color:#ff7b72;font-weight:bold">in</span> compile(parse(tokenize(scheme))):
</span></span><span style="display:flex;"><span>        python <span style="color:#ff7b72;font-weight:bold">+=</span> <span style="color:#a5d6ff">&#39;</span><span style="color:#79c0ff">\n</span><span style="color:#a5d6ff">&#39;</span><span style="color:#ff7b72;font-weight:bold">.</span>join([(<span style="color:#a5d6ff">&#39; &#39;</span> <span style="color:#ff7b72;font-weight:bold">*</span> <span style="color:#a5d6ff">4</span> <span style="color:#ff7b72;font-weight:bold">*</span> x[<span style="color:#a5d6ff">0</span>]) <span style="color:#ff7b72;font-weight:bold">+</span> x[<span style="color:#a5d6ff">1</span>] <span style="color:#ff7b72">for</span> x <span style="color:#ff7b72;font-weight:bold">in</span> expr]) <span style="color:#ff7b72;font-weight:bold">+</span> <span style="color:#a5d6ff">&#39;</span><span style="color:#79c0ff">\n\n</span><span style="color:#a5d6ff">&#39;</span>
</span></span><span style="display:flex;"><span>    exec(python)
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>🐪 Reformed JAPHs: Turing Machine</title>
      <link>https://ryanjoneil.dev/posts/2011-04-18-reformed-japhs-turing-machine/</link>
      <pubDate>Mon, 18 Apr 2011 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2011-04-18-reformed-japhs-turing-machine/</guid>
      <description>Python obfuscation with a Turing machine</description>
      <content:encoded><![CDATA[<p><em>Note: This post was edited for clarity.</em></p>
<p>This JAPH uses a <a href="https://en.wikipedia.org/wiki/Turing_machine">Turing machine</a>. The machine accepts any string that ends in <code>'\n'</code> and allows side effects. This lets us print the value of the tape as it encounters each character. While the idea of using lambda functions as side effects in a Turing machine is a little bizarre on many levels, we work with what we have. And Python is multi-paradigmatic, so what the heck.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">re</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">turing</span>(tape, transitions):
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># The tape input comes in as a string.  We approximate an infinite</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># length tape via a hash, so we need to convert this to {index: value}</span>
</span></span><span style="display:flex;"><span>    tape_hash <span style="color:#ff7b72;font-weight:bold">=</span> {i: x <span style="color:#ff7b72">for</span> i, x <span style="color:#ff7b72;font-weight:bold">in</span> enumerate(tape)}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Start at 0 using our transition matrix</span>
</span></span><span style="display:flex;"><span>    index <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">0</span>
</span></span><span style="display:flex;"><span>    state <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">while</span> <span style="color:#79c0ff">True</span>:
</span></span><span style="display:flex;"><span>        value <span style="color:#ff7b72;font-weight:bold">=</span> tape_hash<span style="color:#ff7b72;font-weight:bold">.</span>get(index, <span style="color:#a5d6ff">&#39;&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#8b949e;font-style:italic"># This is a modified Turing machine: it uses regexen</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8b949e;font-style:italic"># and has side effects.  Oh well, I needed IO.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">for</span> rule <span style="color:#ff7b72;font-weight:bold">in</span> transitions[state]:
</span></span><span style="display:flex;"><span>            regex, next, direction, new_value, side_effect <span style="color:#ff7b72;font-weight:bold">=</span> rule
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">if</span> re<span style="color:#ff7b72;font-weight:bold">.</span><span style="color:#ff7b72">match</span>(regex, value):
</span></span><span style="display:flex;"><span>                <span style="color:#8b949e;font-style:italic"># Terminal states</span>
</span></span><span style="display:flex;"><span>                <span style="color:#ff7b72">if</span> new_value <span style="color:#ff7b72;font-weight:bold">in</span> (<span style="color:#a5d6ff">&#39;YES&#39;</span>, <span style="color:#a5d6ff">&#39;NO&#39;</span>):
</span></span><span style="display:flex;"><span>                    <span style="color:#ff7b72">return</span> new_value
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>                tape_hash[index] <span style="color:#ff7b72;font-weight:bold">=</span> new_value
</span></span><span style="display:flex;"><span>                side_effect(value)
</span></span><span style="display:flex;"><span>                index <span style="color:#ff7b72;font-weight:bold">+=</span> direction
</span></span><span style="display:flex;"><span>                state <span style="color:#ff7b72;font-weight:bold">=</span> next
</span></span><span style="display:flex;"><span>                <span style="color:#ff7b72">break</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">assert</span> <span style="color:#a5d6ff">&#39;YES&#39;</span> <span style="color:#ff7b72;font-weight:bold">==</span> turing(<span style="color:#a5d6ff">&#39;just another python hacker</span><span style="color:#79c0ff">\n</span><span style="color:#a5d6ff">&#39;</span>, [
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># This Turing machine recognizes the language of strings that end in \n.</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Regex rule, next state, left/right = -1/+1, new value, side effect.</span>
</span></span><span style="display:flex;"><span>    [ <span style="color:#8b949e;font-style:italic"># State 0:</span>
</span></span><span style="display:flex;"><span>        [<span style="color:#79c0ff">r</span><span style="color:#a5d6ff">&#39;^[a-z ]$&#39;</span>, <span style="color:#a5d6ff">0</span>, <span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">&#39;&#39;</span>, <span style="color:#ff7b72">lambda</span> x: print(x, end<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;&#39;</span>)],
</span></span><span style="display:flex;"><span>        [<span style="color:#79c0ff">r</span><span style="color:#a5d6ff">&#39;^\n$&#39;</span>, <span style="color:#a5d6ff">1</span>, <span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">&#39;&#39;</span>, <span style="color:#ff7b72">lambda</span> x: print(x, end<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;&#39;</span>)],
</span></span><span style="display:flex;"><span>        [<span style="color:#79c0ff">r</span><span style="color:#a5d6ff">&#39;^.*$&#39;</span>, <span style="color:#a5d6ff">0</span>, <span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">&#39;NO&#39;</span>, <span style="color:#79c0ff">None</span>],
</span></span><span style="display:flex;"><span>    ],
</span></span><span style="display:flex;"><span>    [ <span style="color:#8b949e;font-style:italic"># State 1:</span>
</span></span><span style="display:flex;"><span>        [<span style="color:#79c0ff">r</span><span style="color:#a5d6ff">&#39;^$&#39;</span>, <span style="color:#a5d6ff">1</span>, <span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">&#39;YES&#39;</span>, <span style="color:#79c0ff">None</span>]
</span></span><span style="display:flex;"><span>    ]
</span></span><span style="display:flex;"><span>])
</span></span></code></pre></div><p>Obfuscation again consists of converting the above code into lambda functions using Y combinators. This is a nice programming exercise, so I&rsquo;ve left it out of this post in case anyone wants to try. The Turing machine has to return <code>'YES'</code> to indicate that it accepts the string, thus the assertion. Our final obfuscated JAPH is a single expression.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">assert</span><span style="color:#a5d6ff">&#39;&#39;&#39;YES&#39;&#39;&#39;</span><span style="color:#ff7b72;font-weight:bold">==</span>(<span style="color:#ff7b72">lambda</span> g:(<span style="color:#ff7b72">lambda</span> f:g(<span style="color:#ff7b72">lambda</span> arg:f(f)(arg)))(<span style="color:#ff7b72">lambda</span> f:g(
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">lambda</span> arg: f(f)(arg))))(<span style="color:#ff7b72">lambda</span> f: <span style="color:#ff7b72">lambda</span> q:[(<span style="color:#ff7b72">lambda</span> g:(<span style="color:#ff7b72">lambda</span> f:g(<span style="color:#ff7b72">lambda</span>
</span></span><span style="display:flex;"><span>arg:f(f)(arg)))(<span style="color:#ff7b72">lambda</span> f: g(<span style="color:#ff7b72">lambda</span> arg:f(f)(arg))))(<span style="color:#ff7b72">lambda</span> f: <span style="color:#ff7b72">lambda</span> x:(x
</span></span><span style="display:flex;"><span>[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">0</span>]<span style="color:#ff7b72">if</span> x[<span style="color:#a5d6ff">0</span>] <span style="color:#ff7b72;font-weight:bold">and</span> __import__(<span style="color:#a5d6ff">&#39;re&#39;</span>)<span style="color:#ff7b72;font-weight:bold">.</span><span style="color:#ff7b72">match</span>(x[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">0</span>],x[<span style="color:#a5d6ff">1</span>])<span style="color:#ff7b72">else</span> f([x[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">1</span>:]
</span></span><span style="display:flex;"><span>,x[<span style="color:#a5d6ff">1</span>]]))) ([q[<span style="color:#a5d6ff">3</span>][q[<span style="color:#a5d6ff">1</span>]],q[<span style="color:#a5d6ff">2</span>]<span style="color:#ff7b72;font-weight:bold">.</span>get(q[<span style="color:#a5d6ff">0</span>],<span style="color:#a5d6ff">&#39;&#39;</span>)])[<span style="color:#a5d6ff">4</span>](q[<span style="color:#a5d6ff">2</span>]<span style="color:#ff7b72;font-weight:bold">.</span>get(q[<span style="color:#a5d6ff">0</span>],<span style="color:#a5d6ff">&#39;&#39;</span>)), (<span style="color:#ff7b72">lambda</span>
</span></span><span style="display:flex;"><span>g:(<span style="color:#ff7b72">lambda</span> f:g(<span style="color:#ff7b72">lambda</span> arg:f(f)(arg))) (<span style="color:#ff7b72">lambda</span> f:g(<span style="color:#ff7b72">lambda</span> arg:f(f)(arg))))(
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">lambda</span> f:<span style="color:#ff7b72">lambda</span> x:(x[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">0</span>]<span style="color:#ff7b72">if</span> x[<span style="color:#a5d6ff">0</span>] <span style="color:#ff7b72;font-weight:bold">and</span> __import__(<span style="color:#a5d6ff">&#39;re&#39;</span>)<span style="color:#ff7b72;font-weight:bold">.</span><span style="color:#ff7b72">match</span>(x[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">0</span>],x
</span></span><span style="display:flex;"><span>[<span style="color:#a5d6ff">1</span>])<span style="color:#ff7b72">else</span> f([x[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">1</span>:],x[<span style="color:#a5d6ff">1</span>]])))([q[<span style="color:#a5d6ff">3</span>][q[<span style="color:#a5d6ff">1</span>]],q[<span style="color:#a5d6ff">2</span>]<span style="color:#ff7b72;font-weight:bold">.</span>get(q[<span style="color:#a5d6ff">0</span>],<span style="color:#a5d6ff">&#39;&#39;</span>)])[<span style="color:#a5d6ff">3</span>]<span style="color:#ff7b72">if</span>(<span style="color:#ff7b72">lambda</span>
</span></span><span style="display:flex;"><span>g:(<span style="color:#ff7b72">lambda</span> f:g(<span style="color:#ff7b72">lambda</span> arg:f(f)(arg))) (<span style="color:#ff7b72">lambda</span> f:g(<span style="color:#ff7b72">lambda</span> arg:f(f)(arg))))(
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">lambda</span> f:<span style="color:#ff7b72">lambda</span> x:(x[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">0</span>]<span style="color:#ff7b72">if</span> x[<span style="color:#a5d6ff">0</span>]<span style="color:#ff7b72;font-weight:bold">and</span> __import__(<span style="color:#a5d6ff">&#39;re&#39;</span>)<span style="color:#ff7b72;font-weight:bold">.</span><span style="color:#ff7b72">match</span>(x[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">0</span>],x[
</span></span><span style="display:flex;"><span><span style="color:#a5d6ff">1</span>]) <span style="color:#ff7b72">else</span> f([x[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">1</span>:],x[<span style="color:#a5d6ff">1</span>]])))([q[<span style="color:#a5d6ff">3</span>][q[<span style="color:#a5d6ff">1</span>]],q[<span style="color:#a5d6ff">2</span>]<span style="color:#ff7b72;font-weight:bold">.</span>get(q[<span style="color:#a5d6ff">0</span>],<span style="color:#a5d6ff">&#39;&#39;</span>)])[<span style="color:#a5d6ff">3</span>]<span style="color:#ff7b72;font-weight:bold">in</span>(<span style="color:#a5d6ff">&#39;YES&#39;</span>,
</span></span><span style="display:flex;"><span><span style="color:#a5d6ff">&#39;NO&#39;</span>)<span style="color:#ff7b72">else</span> f([q[<span style="color:#a5d6ff">0</span>]<span style="color:#ff7b72;font-weight:bold">+</span>(<span style="color:#ff7b72">lambda</span> g:(<span style="color:#ff7b72">lambda</span> f:g(<span style="color:#ff7b72">lambda</span> arg:f(f)(arg)))(<span style="color:#ff7b72">lambda</span> f:g
</span></span><span style="display:flex;"><span>(<span style="color:#ff7b72">lambda</span> arg:f(f)(arg))))(<span style="color:#ff7b72">lambda</span> f:<span style="color:#ff7b72">lambda</span> x:(x[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">0</span>]<span style="color:#ff7b72">if</span> x[<span style="color:#a5d6ff">0</span>]<span style="color:#ff7b72;font-weight:bold">and</span> __import__(
</span></span><span style="display:flex;"><span><span style="color:#a5d6ff">&#39;re&#39;</span>)<span style="color:#ff7b72;font-weight:bold">.</span><span style="color:#ff7b72">match</span>(x[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">0</span>],x[<span style="color:#a5d6ff">1</span>])<span style="color:#ff7b72">else</span> f([x[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">1</span>:], x[<span style="color:#a5d6ff">1</span>]])))([q[<span style="color:#a5d6ff">3</span>][q[<span style="color:#a5d6ff">1</span>]], q[<span style="color:#a5d6ff">2</span>]<span style="color:#ff7b72;font-weight:bold">.</span>
</span></span><span style="display:flex;"><span>get(q[<span style="color:#a5d6ff">0</span>],<span style="color:#a5d6ff">&#39;&#39;</span>)])[<span style="color:#a5d6ff">2</span>],(<span style="color:#ff7b72">lambda</span> g:(<span style="color:#ff7b72">lambda</span> f:g(<span style="color:#ff7b72">lambda</span> arg: f(f)(arg)))(<span style="color:#ff7b72">lambda</span> f:
</span></span><span style="display:flex;"><span>g(<span style="color:#ff7b72">lambda</span> arg:f(f)(arg))))(<span style="color:#ff7b72">lambda</span> f:<span style="color:#ff7b72">lambda</span> x:(x[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">0</span>]<span style="color:#ff7b72">if</span> x[<span style="color:#a5d6ff">0</span>]<span style="color:#ff7b72;font-weight:bold">and</span> __import__
</span></span><span style="display:flex;"><span>(<span style="color:#a5d6ff">&#39;re&#39;</span>)<span style="color:#ff7b72;font-weight:bold">.</span><span style="color:#ff7b72">match</span>(x[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">0</span>],x[<span style="color:#a5d6ff">1</span>])<span style="color:#ff7b72">else</span> f([x[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">1</span>:], x[<span style="color:#a5d6ff">1</span>]])))([q[<span style="color:#a5d6ff">3</span>][q[<span style="color:#a5d6ff">1</span>]],q[<span style="color:#a5d6ff">2</span>]<span style="color:#ff7b72;font-weight:bold">.</span>
</span></span><span style="display:flex;"><span>get(q[<span style="color:#a5d6ff">0</span>],<span style="color:#a5d6ff">&#39;&#39;</span>)])[<span style="color:#a5d6ff">1</span>],q[<span style="color:#a5d6ff">2</span>],q[<span style="color:#a5d6ff">3</span>]])][<span style="color:#a5d6ff">1</span>])([<span style="color:#a5d6ff">0</span>,<span style="color:#a5d6ff">0</span>,{i:x <span style="color:#ff7b72">for</span> i,x <span style="color:#ff7b72;font-weight:bold">in</span> enumerate(<span style="color:#a5d6ff">&#39;just &#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#a5d6ff">&#39;another python hacker</span><span style="color:#79c0ff">\n</span><span style="color:#a5d6ff">&#39;</span>)}, [[[<span style="color:#79c0ff">r</span><span style="color:#a5d6ff">&#39;^[a-z ]$&#39;</span>,<span style="color:#a5d6ff">0</span>,<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">1</span>,<span style="color:#a5d6ff">&#39;&#39;</span>,<span style="color:#ff7b72">lambda</span> x:print(x,end<span style="color:#ff7b72;font-weight:bold">=</span>
</span></span><span style="display:flex;"><span><span style="color:#a5d6ff">&#39;&#39;</span>)], [<span style="color:#79c0ff">r</span><span style="color:#a5d6ff">&#39;^\n$&#39;</span>,<span style="color:#a5d6ff">1</span>,<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">1</span>,<span style="color:#a5d6ff">&#39;&#39;</span>,<span style="color:#ff7b72">lambda</span> x:print(x, end<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;&#39;</span>)],[<span style="color:#79c0ff">r</span><span style="color:#a5d6ff">&#39;^.*$&#39;</span>,<span style="color:#a5d6ff">0</span>,<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">1</span>,<span style="color:#a5d6ff">&#39;&#39;&#39;NO&#39;&#39;&#39;</span>,
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">lambda</span> x:<span style="color:#79c0ff">None</span>]], [[<span style="color:#79c0ff">r</span><span style="color:#a5d6ff">&#39;&#39;&#39;^$&#39;&#39;&#39;</span>,<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">1</span>,<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">1</span>,<span style="color:#a5d6ff">&#39;&#39;&#39;YES&#39;&#39;&#39;</span>, <span style="color:#ff7b72">lambda</span> x: <span style="color:#79c0ff">None</span> <span style="color:#ff7b72;font-weight:bold">or</span> <span style="color:#79c0ff">None</span>]]]])
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>🐪 Reformed JAPHs: Huffman Coding</title>
      <link>https://ryanjoneil.dev/posts/2011-04-14-reformed-japhs-huffman-coding/</link>
      <pubDate>Thu, 14 Apr 2011 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2011-04-14-reformed-japhs-huffman-coding/</guid>
      <description>Python obfuscation and Huffman coding</description>
      <content:encoded><![CDATA[<p><em>Note: This post was edited for clarity.</em></p>
<p>At this point, tricking <code>python</code> into printing strings via indirect means got a little boring. So I switched to obfuscating fundamental computer science algorithms. Here&rsquo;s a JAPH that takes in a <a href="https://en.wikipedia.org/wiki/Huffman_coding">Huffman coded</a> version of <code>'just another python hacker'</code>, decodes, and prints it.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Build coding tree</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">build_tree</span>(scheme):
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">if</span> scheme<span style="color:#ff7b72;font-weight:bold">.</span>startswith(<span style="color:#a5d6ff">&#39;*&#39;</span>):
</span></span><span style="display:flex;"><span>        left, scheme <span style="color:#ff7b72;font-weight:bold">=</span> build_tree(scheme[<span style="color:#a5d6ff">1</span>:])
</span></span><span style="display:flex;"><span>        right, scheme <span style="color:#ff7b72;font-weight:bold">=</span> build_tree(scheme)
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">return</span> (left, right), scheme
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">else</span>:
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">return</span> scheme[<span style="color:#a5d6ff">0</span>], scheme[<span style="color:#a5d6ff">1</span>:]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">decode</span>(tree, encoded):
</span></span><span style="display:flex;"><span>    ret <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>    node <span style="color:#ff7b72;font-weight:bold">=</span> tree
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> direction <span style="color:#ff7b72;font-weight:bold">in</span> encoded:
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">if</span> direction <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;0&#39;</span>:
</span></span><span style="display:flex;"><span>            node <span style="color:#ff7b72;font-weight:bold">=</span> node[<span style="color:#a5d6ff">0</span>]
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">else</span>:
</span></span><span style="display:flex;"><span>            node <span style="color:#ff7b72;font-weight:bold">=</span> node[<span style="color:#a5d6ff">1</span>]
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">if</span> isinstance(node, str):
</span></span><span style="display:flex;"><span>            ret <span style="color:#ff7b72;font-weight:bold">+=</span> node
</span></span><span style="display:flex;"><span>            node <span style="color:#ff7b72;font-weight:bold">=</span> tree
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> ret
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>tree <span style="color:#ff7b72;font-weight:bold">=</span> build_tree(<span style="color:#a5d6ff">&#39;*****ju*sp*er***yct* h**ka*no&#39;</span>)[<span style="color:#a5d6ff">0</span>]
</span></span><span style="display:flex;"><span>print(
</span></span><span style="display:flex;"><span>    decode(tree, bin(<span style="color:#a5d6ff">10627344201836243859174935587</span>)<span style="color:#ff7b72;font-weight:bold">.</span>lstrip(<span style="color:#a5d6ff">&#39;0b&#39;</span>)<span style="color:#ff7b72;font-weight:bold">.</span>zfill(<span style="color:#a5d6ff">103</span>))
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>The decoding tree is like a LISP-style sequence of pairs. <code>'*'</code> represents a branch in the tree while other characters are leaf nodes. This looks like the following.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>(
</span></span><span style="display:flex;"><span>    (
</span></span><span style="display:flex;"><span>        (
</span></span><span style="display:flex;"><span>            (
</span></span><span style="display:flex;"><span>                (<span style="color:#a5d6ff">&#39;j&#39;</span>, <span style="color:#a5d6ff">&#39;u&#39;</span>), 
</span></span><span style="display:flex;"><span>                (<span style="color:#a5d6ff">&#39;s&#39;</span>, <span style="color:#a5d6ff">&#39;p&#39;</span>)
</span></span><span style="display:flex;"><span>            ), 
</span></span><span style="display:flex;"><span>            (<span style="color:#a5d6ff">&#39;e&#39;</span>, <span style="color:#a5d6ff">&#39;r&#39;</span>)
</span></span><span style="display:flex;"><span>        ), 
</span></span><span style="display:flex;"><span>        (
</span></span><span style="display:flex;"><span>            (
</span></span><span style="display:flex;"><span>                (<span style="color:#a5d6ff">&#39;y&#39;</span>, <span style="color:#a5d6ff">&#39;c&#39;</span>), 
</span></span><span style="display:flex;"><span>                <span style="color:#a5d6ff">&#39;t&#39;</span>
</span></span><span style="display:flex;"><span>            ), 
</span></span><span style="display:flex;"><span>            (<span style="color:#a5d6ff">&#39; &#39;</span>, <span style="color:#a5d6ff">&#39;h&#39;</span>)
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>    ), 
</span></span><span style="display:flex;"><span>    (
</span></span><span style="display:flex;"><span>        (<span style="color:#a5d6ff">&#39;k&#39;</span>, <span style="color:#a5d6ff">&#39;a&#39;</span>), 
</span></span><span style="display:flex;"><span>        (<span style="color:#a5d6ff">&#39;n&#39;</span>, <span style="color:#a5d6ff">&#39;o&#39;</span>)
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>The actual Huffman coded version of our favorite string gets about 50% smaller represented in base-2.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-txt" data-lang="txt"><span style="display:flex;"><span>0000000001000100101011010111011101010111001000110110000110100001010111111110011001111010100110000100011
</span></span></code></pre></div><p>There&rsquo;s a catch here, which is that this is hard to obfuscate unless we turn it into a single expression. This means that we have to convert <code>build_tree</code> and <code>decode</code> into lambda functions. Unfortunately, they are recursive and lambda functions recurse naturally. Fortunately, we can use <a href="https://code.activestate.com/recipes/576366-y-combinator/">Y combinators</a> to get around the problem. These are worth some study since they will pop up again in future JAPHs.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>Y <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72">lambda</span> g: (
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">lambda</span> f: g(<span style="color:#ff7b72">lambda</span> arg: f(f)(arg))) (<span style="color:#ff7b72">lambda</span> f: g(<span style="color:#ff7b72">lambda</span> arg: f(f)(arg))
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>build_tree <span style="color:#ff7b72;font-weight:bold">=</span> Y(
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">lambda</span> f: <span style="color:#ff7b72">lambda</span> scheme: (
</span></span><span style="display:flex;"><span>        (f(scheme[<span style="color:#a5d6ff">1</span>:])[<span style="color:#a5d6ff">0</span>], f(f(scheme[<span style="color:#a5d6ff">1</span>:])[<span style="color:#a5d6ff">1</span>])[<span style="color:#a5d6ff">0</span>]),
</span></span><span style="display:flex;"><span>        f(f(scheme[<span style="color:#a5d6ff">1</span>:])[<span style="color:#a5d6ff">1</span>])[<span style="color:#a5d6ff">1</span> ]
</span></span><span style="display:flex;"><span>    ) <span style="color:#ff7b72">if</span> scheme<span style="color:#ff7b72;font-weight:bold">.</span>startswith(<span style="color:#a5d6ff">&#39;*&#39;</span>) <span style="color:#ff7b72">else</span> (scheme[<span style="color:#a5d6ff">0</span>], scheme[<span style="color:#a5d6ff">1</span>:])
</span></span><span style="display:flex;"><span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>decode <span style="color:#ff7b72;font-weight:bold">=</span> Y(<span style="color:#ff7b72">lambda</span> f: <span style="color:#ff7b72">lambda</span> x: x[<span style="color:#a5d6ff">3</span>]<span style="color:#ff7b72;font-weight:bold">+</span>x[<span style="color:#a5d6ff">1</span>] <span style="color:#ff7b72">if</span> <span style="color:#ff7b72;font-weight:bold">not</span> x[<span style="color:#a5d6ff">2</span>] <span style="color:#ff7b72">else</span> (
</span></span><span style="display:flex;"><span>    f([x[<span style="color:#a5d6ff">0</span>], x[<span style="color:#a5d6ff">0</span>], x[<span style="color:#a5d6ff">2</span>], x[<span style="color:#a5d6ff">3</span>]<span style="color:#ff7b72;font-weight:bold">+</span>x[<span style="color:#a5d6ff">1</span>]]) <span style="color:#ff7b72">if</span> isinstance(x[<span style="color:#a5d6ff">1</span>], str) <span style="color:#ff7b72">else</span> (
</span></span><span style="display:flex;"><span>        f([x[<span style="color:#a5d6ff">0</span>], x[<span style="color:#a5d6ff">1</span>][<span style="color:#a5d6ff">0</span>], x[<span style="color:#a5d6ff">2</span>][<span style="color:#a5d6ff">1</span>:], x[<span style="color:#a5d6ff">3</span>]]) <span style="color:#ff7b72">if</span> x[<span style="color:#a5d6ff">2</span>][<span style="color:#a5d6ff">0</span>] <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;0&#39;</span> <span style="color:#ff7b72">else</span> (
</span></span><span style="display:flex;"><span>            f([x[<span style="color:#a5d6ff">0</span>], x[<span style="color:#a5d6ff">1</span>][<span style="color:#a5d6ff">1</span>], x[<span style="color:#a5d6ff">2</span>][<span style="color:#a5d6ff">1</span>:], x[<span style="color:#a5d6ff">3</span>]])
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>tree <span style="color:#ff7b72;font-weight:bold">=</span> build_tree(<span style="color:#a5d6ff">&#39;*****ju*sp*er***yct* h**ka*no&#39;</span>)[<span style="color:#a5d6ff">0</span>]
</span></span><span style="display:flex;"><span>print(
</span></span><span style="display:flex;"><span>    decode([
</span></span><span style="display:flex;"><span>        tree,
</span></span><span style="display:flex;"><span>        tree,
</span></span><span style="display:flex;"><span>        bin(<span style="color:#a5d6ff">10627344201836243859174935587</span>)<span style="color:#ff7b72;font-weight:bold">.</span>lstrip(<span style="color:#a5d6ff">&#39;0b&#39;</span>)<span style="color:#ff7b72;font-weight:bold">.</span>zfill(<span style="color:#a5d6ff">103</span>), <span style="color:#a5d6ff">&#39;&#39;</span>
</span></span><span style="display:flex;"><span>    ])
</span></span><span style="display:flex;"><span>)
</span></span></code></pre></div><p>The final version is a condensed (and expanded, oddly) version of the above.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>print((<span style="color:#ff7b72">lambda</span> t,e,s:(<span style="color:#ff7b72">lambda</span> g:(<span style="color:#ff7b72">lambda</span> f:g(<span style="color:#ff7b72">lambda</span> arg:f(f)(arg)))(<span style="color:#ff7b72">lambda</span> f:
</span></span><span style="display:flex;"><span>g(<span style="color:#ff7b72">lambda</span> arg: f(f)(arg))))(<span style="color:#ff7b72">lambda</span> f:<span style="color:#ff7b72">lambda</span> x: x[<span style="color:#a5d6ff">3</span>]<span style="color:#ff7b72;font-weight:bold">+</span>x[<span style="color:#a5d6ff">1</span>]<span style="color:#ff7b72">if</span> <span style="color:#ff7b72;font-weight:bold">not</span> x[<span style="color:#a5d6ff">2</span>]<span style="color:#ff7b72">else</span> f([
</span></span><span style="display:flex;"><span>x[<span style="color:#a5d6ff">0</span>],x[<span style="color:#a5d6ff">0</span>],x[<span style="color:#a5d6ff">2</span>],x[<span style="color:#a5d6ff">3</span>]<span style="color:#ff7b72;font-weight:bold">+</span>x[<span style="color:#a5d6ff">1</span>]])<span style="color:#ff7b72">if</span> isinstance(x[<span style="color:#a5d6ff">1</span>],str)<span style="color:#ff7b72">else</span> f([x[<span style="color:#a5d6ff">0</span>],x[<span style="color:#a5d6ff">1</span>][<span style="color:#a5d6ff">0</span>],x[<span style="color:#a5d6ff">2</span>]
</span></span><span style="display:flex;"><span>[<span style="color:#a5d6ff">1</span>:],x[<span style="color:#a5d6ff">3</span>]])<span style="color:#ff7b72">if</span> x[<span style="color:#a5d6ff">2</span>][<span style="color:#a5d6ff">0</span>]<span style="color:#ff7b72;font-weight:bold">==</span><span style="color:#a5d6ff">&#39;0&#39;</span><span style="color:#ff7b72">else</span> f([x[<span style="color:#a5d6ff">0</span>],x[<span style="color:#a5d6ff">1</span>][<span style="color:#a5d6ff">1</span>],x[<span style="color:#a5d6ff">2</span>][<span style="color:#a5d6ff">1</span>:],x[<span style="color:#a5d6ff">3</span>]]))([t,t,e,s])
</span></span><span style="display:flex;"><span>)((<span style="color:#ff7b72">lambda</span> g:(<span style="color:#ff7b72">lambda</span> f:g(<span style="color:#ff7b72">lambda</span> arg:f(f)(arg)))(<span style="color:#ff7b72">lambda</span> f:g(<span style="color:#ff7b72">lambda</span> arg:f(f)(
</span></span><span style="display:flex;"><span>arg))))(<span style="color:#ff7b72">lambda</span> f:<span style="color:#ff7b72">lambda</span> p:((f(p[<span style="color:#a5d6ff">1</span>:])[<span style="color:#a5d6ff">0</span>],f(f(p[<span style="color:#a5d6ff">1</span>:])[<span style="color:#a5d6ff">1</span>])[<span style="color:#a5d6ff">0</span>]),f(f(p[<span style="color:#a5d6ff">1</span>:])[<span style="color:#a5d6ff">1</span>])[
</span></span><span style="display:flex;"><span><span style="color:#a5d6ff">1</span>])<span style="color:#ff7b72">if</span> p<span style="color:#ff7b72;font-weight:bold">.</span>startswith(<span style="color:#a5d6ff">&#39;*&#39;</span>)<span style="color:#ff7b72">else</span>(p[<span style="color:#a5d6ff">0</span>],p[<span style="color:#a5d6ff">1</span>:]))(<span style="color:#a5d6ff">&#39;*****ju*sp*er***yct* h**ka*no&#39;</span>)[
</span></span><span style="display:flex;"><span><span style="color:#a5d6ff">0</span>],bin(<span style="color:#a5d6ff">10627344201836243859179756385</span><span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">4820798</span>)<span style="color:#ff7b72;font-weight:bold">.</span>lstrip(<span style="color:#a5d6ff">&#39;0b&#39;</span>)<span style="color:#ff7b72;font-weight:bold">.</span>zfill(<span style="color:#a5d6ff">103</span>),<span style="color:#a5d6ff">&#39;&#39;</span>))
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>🐪 Reformed JAPHs: Rolling Effect</title>
      <link>https://ryanjoneil.dev/posts/2011-04-11-reformed-japhs-rolling-effect/</link>
      <pubDate>Mon, 11 Apr 2011 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2011-04-11-reformed-japhs-rolling-effect/</guid>
      <description>Python obfuscation with a cute visual effect</description>
      <content:encoded><![CDATA[<p><em>Note: This post was updated to work with Python 3.12. It may not work with different versions.</em></p>
<p>Here&rsquo;s a JAPH composed solely for effect. For each letter in <code>'just another python hacker'</code> it loops over each the characters <code>' abcdefghijklmnopqrstuvwxyz'</code>, printing each. Between characters it pauses for 0.05 seconds, backing up and moving on to the next if it hasn&rsquo;t reached the desired one yet. This achieves a sort of rolling effect by which the final string appears on our screen over time.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">string</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">sys</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">time</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>letters <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">&#39; &#39;</span> <span style="color:#ff7b72;font-weight:bold">+</span> string<span style="color:#ff7b72;font-weight:bold">.</span>ascii_lowercase
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">for</span> l <span style="color:#ff7b72;font-weight:bold">in</span> <span style="color:#a5d6ff">&#39;just another python hacker&#39;</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> x <span style="color:#ff7b72;font-weight:bold">in</span> letters:
</span></span><span style="display:flex;"><span>        print(x, end<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;&#39;</span>)
</span></span><span style="display:flex;"><span>        sys<span style="color:#ff7b72;font-weight:bold">.</span>stdout<span style="color:#ff7b72;font-weight:bold">.</span>flush()
</span></span><span style="display:flex;"><span>        time<span style="color:#ff7b72;font-weight:bold">.</span>sleep(<span style="color:#a5d6ff">0.05</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">if</span> x <span style="color:#ff7b72;font-weight:bold">==</span> l:
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">break</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">else</span>:
</span></span><span style="display:flex;"><span>            print(<span style="color:#a5d6ff">&#39;</span><span style="color:#79c0ff">\b</span><span style="color:#a5d6ff">&#39;</span>, end<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;&#39;</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print()
</span></span></code></pre></div><p>We locate and print each letter in the string with a list comprehension.  At the end we have an extra line of code (the eval statement) that gives us our newline.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>[[(<span style="color:#ff7b72">lambda</span> x,l:str(print(x,end<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;&#39;</span>))<span style="color:#ff7b72;font-weight:bold">+</span>str(__import__(print<span style="color:#ff7b72;font-weight:bold">.</span>
</span></span><span style="display:flex;"><span><span style="color:#79c0ff">__doc__</span>[print<span style="color:#ff7b72;font-weight:bold">.</span><span style="color:#79c0ff">__doc__</span><span style="color:#ff7b72;font-weight:bold">.</span>index(<span style="color:#a5d6ff">&#39;stdout&#39;</span>) <span style="color:#ff7b72;font-weight:bold">-</span> <span style="color:#a5d6ff">4</span>:print<span style="color:#ff7b72;font-weight:bold">.</span><span style="color:#79c0ff">__doc__</span><span style="color:#ff7b72;font-weight:bold">.</span>
</span></span><span style="display:flex;"><span>index(<span style="color:#a5d6ff">&#39;stdout&#39;</span>)<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">1</span>])<span style="color:#ff7b72;font-weight:bold">.</span>stdout<span style="color:#ff7b72;font-weight:bold">.</span>flush()) <span style="color:#ff7b72;font-weight:bold">+</span> str(__import__(<span style="color:#a5d6ff">&#39;&#39;</span><span style="color:#ff7b72;font-weight:bold">.</span>
</span></span><span style="display:flex;"><span>join(reversed(<span style="color:#a5d6ff">&#39;emit&#39;</span>)))<span style="color:#ff7b72;font-weight:bold">.</span>sleep(<span style="color:#a5d6ff">0o5</span><span style="color:#ff7b72;font-weight:bold">*</span><span style="color:#a5d6ff">1.01</span><span style="color:#ff7b72;font-weight:bold">/</span><span style="color:#a5d6ff">0x64</span>))<span style="color:#ff7b72;font-weight:bold">+</span>str(print(
</span></span><span style="display:flex;"><span><span style="color:#a5d6ff">&#39;</span><span style="color:#79c0ff">\b</span><span style="color:#a5d6ff">&#39;</span>,end<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#a5d6ff">&#39;</span><span style="color:#79c0ff">\x09</span><span style="color:#a5d6ff">&#39;</span><span style="color:#ff7b72;font-weight:bold">.</span>strip())<span style="color:#ff7b72">if</span> x<span style="color:#ff7b72;font-weight:bold">!=</span>l <span style="color:#ff7b72">else</span><span style="color:#a5d6ff">&#39;*&amp;#&#39;</span>))(x1,l1)<span style="color:#ff7b72">for</span> x1
</span></span><span style="display:flex;"><span><span style="color:#ff7b72;font-weight:bold">in</span>(<span style="color:#a5d6ff">&#39;</span><span style="color:#79c0ff">\x20</span><span style="color:#a5d6ff">&#39;</span><span style="color:#ff7b72;font-weight:bold">+</span>getattr(__import__(type(<span style="color:#a5d6ff">&#39;phear&#39;</span>)<span style="color:#ff7b72;font-weight:bold">.</span><span style="color:#79c0ff">__name__</span><span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">&#39;in&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#a5d6ff">&#39;g&#39;</span>),dir(__import__(type(<span style="color:#a5d6ff">&#39;snarf&#39;</span>)<span style="color:#ff7b72;font-weight:bold">.</span><span style="color:#79c0ff">__name__</span><span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">&#39;ing&#39;</span>))[<span style="color:#a5d6ff">15</span>]))
</span></span><span style="display:flex;"><span>[:(<span style="color:#a5d6ff">&#39;</span><span style="color:#79c0ff">\x20</span><span style="color:#a5d6ff">&#39;</span><span style="color:#ff7b72;font-weight:bold">+</span>getattr(__import__(type(<span style="color:#a5d6ff">&#39;smear&#39;</span>)<span style="color:#ff7b72;font-weight:bold">.</span><span style="color:#79c0ff">__name__</span><span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">&#39;in&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#a5d6ff">&#39;g&#39;</span>),dir(__import__(type(<span style="color:#a5d6ff">&#39;slurp&#39;</span>)<span style="color:#ff7b72;font-weight:bold">.</span><span style="color:#79c0ff">__name__</span><span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">&#39;ing&#39;</span>))[<span style="color:#a5d6ff">15</span>]))
</span></span><span style="display:flex;"><span><span style="color:#ff7b72;font-weight:bold">.</span>index(l1)<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">1</span>]]<span style="color:#ff7b72">for</span> l1 <span style="color:#ff7b72;font-weight:bold">in</span><span style="color:#a5d6ff">&#39;&#39;&#39;just another python hacker&#39;&#39;&#39;</span>]
</span></span><span style="display:flex;"><span>eval(<span style="color:#a5d6ff">&#39;&#39;&#39;</span><span style="color:#79c0ff">\x20\x09</span><span style="color:#a5d6ff">eval(&#34;</span><span style="color:#79c0ff">\x20\x09</span><span style="color:#a5d6ff">eval(&#39;</span><span style="color:#79c0ff">\x20</span><span style="color:#a5d6ff"> print()&#39;)&#34;)&#39;&#39;&#39;</span>)
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>🐪 Reformed JAPHs: ROT13</title>
      <link>https://ryanjoneil.dev/posts/2011-04-06-reformed-japhs-rot13/</link>
      <pubDate>Wed, 06 Apr 2011 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2011-04-06-reformed-japhs-rot13/</guid>
      <description>Python obfuscation using ROT13 encoding</description>
      <content:encoded><![CDATA[<p><em>Note: This post was updated to work with Python 3.12. It may not work with different versions.</em></p>
<p>No series of JAPHs would be complete without <a href="https://en.wikipedia.org/wiki/ROT13">ROT13</a>. This is the example through which aspiring Perl programmers learn to use <code>tr</code> and its synonym <code>y</code>. In Perl the basic ROT13 JAPH starts as:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-perl" data-lang="perl"><span style="display:flex;"><span><span style="color:#79c0ff">$foo</span> <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">&#39;whfg nabgure crey unpxre&#39;</span>;
</span></span><span style="display:flex;"><span><span style="color:#79c0ff">$foo</span> <span style="color:#ff7b72;font-weight:bold">=~</span> y<span style="color:#79c0ff">/a-z/</span>n<span style="color:#ff7b72;font-weight:bold">-</span>za<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#79c0ff">m</span><span style="color:#f85149">/;</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">print</span> <span style="color:#79c0ff">$foo</span>;
</span></span></code></pre></div><p>Python has nothing quite so elegant in its default namespace. However, this does give us the opportunity to explore a little used aspect of strings: the translate method. If we construct a dictionary of ordinals we can accomplish the same thing with a touch more effort.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>table <span style="color:#ff7b72;font-weight:bold">=</span> {
</span></span><span style="display:flex;"><span>    ord(x): ord(y) <span style="color:#ff7b72">for</span> x, y <span style="color:#ff7b72;font-weight:bold">in</span> zip(
</span></span><span style="display:flex;"><span>        string<span style="color:#ff7b72;font-weight:bold">.</span>ascii_lowercase,
</span></span><span style="display:flex;"><span>        string<span style="color:#ff7b72;font-weight:bold">.</span>ascii_lowercase[<span style="color:#a5d6ff">13</span>:] <span style="color:#ff7b72;font-weight:bold">+</span> string<span style="color:#ff7b72;font-weight:bold">.</span>ascii_lowercase
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(<span style="color:#a5d6ff">&#39;whfg nabgure clguba unpxre&#39;</span><span style="color:#ff7b72;font-weight:bold">.</span>translate(table))
</span></span></code></pre></div><p>We obfuscate the construction of this translation dictionary and, for added measure, use <code>getattr</code> to find the <code>print</code> function off of <code>__builtins__</code>.  This will likely only work in Python 3.2, since the order of attributes on <code>__builtins__</code> matters.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>getattr(vars()[list(filter(<span style="color:#ff7b72">lambda</span> _:<span style="color:#a5d6ff">&#39;</span><span style="color:#79c0ff">\x5f\x62</span><span style="color:#a5d6ff">&#39;</span><span style="color:#ff7b72;font-weight:bold">in</span> _,dir
</span></span><span style="display:flex;"><span>()))[<span style="color:#a5d6ff">0</span>]], dir(vars()[list(filter(<span style="color:#ff7b72">lambda</span> _:<span style="color:#a5d6ff">&#39;</span><span style="color:#79c0ff">\x5f\x62</span><span style="color:#a5d6ff">&#39;</span><span style="color:#ff7b72;font-weight:bold">in</span>
</span></span><span style="display:flex;"><span>_, dir()))[<span style="color:#a5d6ff">0</span>]])[list(filter(<span style="color:#ff7b72">lambda</span> _:_ [<span style="color:#a5d6ff">1</span>]<span style="color:#ff7b72;font-weight:bold">.</span>startswith(
</span></span><span style="display:flex;"><span><span style="color:#a5d6ff">&#39;</span><span style="color:#79c0ff">\x70\x72</span><span style="color:#a5d6ff">&#39;</span>),enumerate(dir(vars()[list(filter(<span style="color:#ff7b72">lambda</span> _:
</span></span><span style="display:flex;"><span><span style="color:#a5d6ff">&#39;</span><span style="color:#79c0ff">\x5f\x62</span><span style="color:#a5d6ff">&#39;</span><span style="color:#ff7b72;font-weight:bold">in</span> _,dir()))[<span style="color:#a5d6ff">0</span>]]))))[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">0</span>]])(getattr(<span style="color:#a5d6ff">&#39;whfg &#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">&#39;&#39;&#39;nabgure clguba unpxre&#39;&#39;&#39;</span>, dir(<span style="color:#a5d6ff">&#39;0o52&#39;</span>)[<span style="color:#a5d6ff">0o116</span>])({ _:
</span></span><span style="display:flex;"><span>(_<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">0o124</span>) <span style="color:#ff7b72;font-weight:bold">%</span><span style="color:#a5d6ff">0o32</span> <span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">0o141</span> <span style="color:#ff7b72">for</span> _ <span style="color:#ff7b72;font-weight:bold">in</span> range(<span style="color:#a5d6ff">0o141</span>, <span style="color:#a5d6ff">0o173</span>)}))
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>🐪 Reformed JAPHs: Ridiculous Anagram</title>
      <link>https://ryanjoneil.dev/posts/2011-04-03-reformed-japhs-ridiculous-anagram/</link>
      <pubDate>Sun, 03 Apr 2011 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2011-04-03-reformed-japhs-ridiculous-anagram/</guid>
      <description>Python obfuscation using anagrams</description>
      <content:encoded><![CDATA[<p>Here&rsquo;s the second in my reformed JAPH series. It takes an anagram of <code>'just another python hacker'</code> and converts it prior to printing. It sorts the anagram by the indices of another string, in order of their associated characters. This is sort of like a pre-digested <a href="https://en.wikipedia.org/wiki/Schwartzian_transform">Schwartzian transform</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>x <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">&#39;upjohn tehran hectors katy&#39;</span>
</span></span><span style="display:flex;"><span>y <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">&#39;1D0HG6JFO9P5ICKAM87B24NL3E&#39;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(<span style="color:#a5d6ff">&#39;&#39;</span><span style="color:#ff7b72;font-weight:bold">.</span>join(x[i] <span style="color:#ff7b72">for</span> i <span style="color:#ff7b72;font-weight:bold">in</span> sorted(range(len(x)), key<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#ff7b72">lambda</span> p: y[p])))
</span></span></code></pre></div><p>Obfuscation consists mostly of using silly machinations to construct the string we use to sort the anagram.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>print(<span style="color:#a5d6ff">&#39;&#39;</span><span style="color:#ff7b72;font-weight:bold">.</span>join(<span style="color:#a5d6ff">&#39;&#39;&#39;upjohn tehran hectors katy&#39;&#39;&#39;</span>[_]<span style="color:#ff7b72">for</span> _ <span style="color:#ff7b72;font-weight:bold">in</span> sorted(range
</span></span><span style="display:flex;"><span>(<span style="color:#a5d6ff">26</span>),key<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#ff7b72">lambda</span> p:(hex(<span style="color:#a5d6ff">29</span>)[<span style="color:#a5d6ff">2</span>:]<span style="color:#ff7b72;font-weight:bold">.</span>upper()<span style="color:#ff7b72;font-weight:bold">+</span>str(<span style="color:#a5d6ff">3</span><span style="color:#ff7b72;font-weight:bold">*</span><span style="color:#a5d6ff">3</span><span style="color:#ff7b72;font-weight:bold">*</span><span style="color:#a5d6ff">3</span><span style="color:#ff7b72;font-weight:bold">*</span><span style="color:#a5d6ff">3</span><span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">3</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">4</span>)<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">&#39;HG&#39;</span><span style="color:#ff7b72;font-weight:bold">+</span>str(sum(
</span></span><span style="display:flex;"><span>range(<span style="color:#a5d6ff">4</span>)))<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">&#39;JFO&#39;</span><span style="color:#ff7b72;font-weight:bold">+</span>str((<span style="color:#a5d6ff">1</span><span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">2</span>)<span style="color:#ff7b72;font-weight:bold">**</span>(<span style="color:#a5d6ff">1</span><span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">1</span>))<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">&#39;P&#39;</span><span style="color:#ff7b72;font-weight:bold">+</span>str(<span style="color:#a5d6ff">35</span><span style="color:#ff7b72;font-weight:bold">/</span><span style="color:#a5d6ff">7</span>)[:<span style="color:#a5d6ff">1</span>]<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">&#39;i.c.k.&#39;</span><span style="color:#ff7b72;font-weight:bold">.</span>replace(
</span></span><span style="display:flex;"><span><span style="color:#a5d6ff">&#39;.&#39;</span>,<span style="color:#a5d6ff">&#39;&#39;</span>)<span style="color:#ff7b72;font-weight:bold">.</span>upper()<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">&#39;AM&#39;</span><span style="color:#ff7b72;font-weight:bold">+</span>str(<span style="color:#a5d6ff">3</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span><span style="color:#ff7b72;font-weight:bold">*</span>sum(range(<span style="color:#a5d6ff">5</span>))<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">3</span>)<span style="color:#ff7b72;font-weight:bold">+</span>hex(<span style="color:#a5d6ff">0o5444</span>)[<span style="color:#a5d6ff">2</span>:]<span style="color:#ff7b72;font-weight:bold">.</span>replace
</span></span><span style="display:flex;"><span>(<span style="color:#ff7b72;font-weight:bold">*</span><span style="color:#a5d6ff">&#39;</span><span style="color:#79c0ff">\x62</span><span style="color:#a5d6ff">|</span><span style="color:#79c0ff">\x42</span><span style="color:#a5d6ff">&#39;</span><span style="color:#ff7b72;font-weight:bold">.</span>split(<span style="color:#a5d6ff">&#39;|&#39;</span>))<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">&#39;NL&#39;</span><span style="color:#ff7b72;font-weight:bold">+</span>hex(<span style="color:#a5d6ff">0o076</span>)<span style="color:#ff7b72;font-weight:bold">.</span>split(<span style="color:#a5d6ff">&#39;x&#39;</span>)[<span style="color:#a5d6ff">1</span>]<span style="color:#ff7b72;font-weight:bold">.</span>upper())[p])))
</span></span></code></pre></div>]]></content:encoded>
    </item>
    <item>
      <title>🐪 Reformed JAPHs: Alphabetic Indexing</title>
      <link>https://ryanjoneil.dev/posts/2011-04-01-reformed-japhs-alphabetic-indexing/</link>
      <pubDate>Fri, 01 Apr 2011 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2011-04-01-reformed-japhs-alphabetic-indexing/</guid>
      <description>Python obfuscation</description>
      <content:encoded><![CDATA[<p><em>Note: This post was edited for clarity.</em></p>
<p>Many years ago, I was a Perl programmer. Then one day I became disillusioned at the progress of Perl 6 and decided to <a href="https://www.python.org/dev/peps/pep-0020/">import this</a>.</p>
<p>This seems to be a fairly common story for Perl to Python converts. While I haven&rsquo;t looked back much, there are a number of things I really miss about <code>perl</code> <em>(lower case intentional)</em>. I miss having value types in a dynamic language, magical and ill-advised use of <a href="https://www.foo.be/docs/tpj/issues/vol3_1/tpj0301-0003.html">cryptocontext</a>, and sometimes even <a href="https://web.archive.org/web/20040712204117/https://perldesignpatterns.com/?PseudoHash">pseudohashes</a> because they were inexcusably weird. A language that supports so many ideas out of the box enables an extended learning curve that lasts for <a href="https://web.archive.org/web/20020607034341/https://silver.sucs.org/~manic/humour/languages/perlhacker.htm">many years</a>. &ldquo;Perl itself is the game.&rdquo;</p>
<p>Most of all I think I miss writing Perl <a href="https://www.perlmonks.org/?node=Perl%20Poetry">poetry</a> and <a href="https://en.wikipedia.org/wiki/Just_another_Perl_hacker">JAPHs</a>. Sadly, I didn&rsquo;t keep any of those I wrote, and I&rsquo;m not competent enough with the language anymore to write interesting ones. At the time I was intentionally distancing myself from a model that was largely implicit and based on archaic systems internals and moving to one that was (supposedly) explicit and simple.</p>
<p>After switching to Python as my primary language, I used the following email signature in a nod to this change in orientation <em>(intended for Python 2)</em>:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>print <span style="color:#a5d6ff">&#39;just another python hacker&#39;</span>
</span></span></code></pre></div><p>Recently I&rsquo;ve been experimenting with writing JAPHs in Python. I think of these as &ldquo;reformed JAPHs.&rdquo; They accomplish the same purpose as programming exercises but in a more restricted context. In some ways they are more challenging. Creativity can be difficult in a narrowly defined landscape.</p>
<p>I have written a small series of reformed JAPHs which increase monotonically in complexity. Here is the first one, written in plain understandable Python 3.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">string</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>letters <span style="color:#ff7b72;font-weight:bold">=</span> string<span style="color:#ff7b72;font-weight:bold">.</span>ascii_lowercase <span style="color:#ff7b72;font-weight:bold">+</span> <span style="color:#a5d6ff">&#39; &#39;</span>
</span></span><span style="display:flex;"><span>indices <span style="color:#ff7b72;font-weight:bold">=</span> [
</span></span><span style="display:flex;"><span>     <span style="color:#a5d6ff">9</span>, <span style="color:#a5d6ff">20</span>, <span style="color:#a5d6ff">18</span>, <span style="color:#a5d6ff">19</span>, <span style="color:#a5d6ff">26</span>,  <span style="color:#a5d6ff">0</span>, <span style="color:#a5d6ff">13</span>, <span style="color:#a5d6ff">14</span>, <span style="color:#a5d6ff">19</span>, <span style="color:#a5d6ff">7</span>,  <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">17</span>, <span style="color:#a5d6ff">26</span>,
</span></span><span style="display:flex;"><span>    <span style="color:#a5d6ff">15</span>, <span style="color:#a5d6ff">24</span>, <span style="color:#a5d6ff">19</span>,  <span style="color:#a5d6ff">7</span>, <span style="color:#a5d6ff">14</span>, <span style="color:#a5d6ff">13</span>, <span style="color:#a5d6ff">26</span>,  <span style="color:#a5d6ff">7</span>,  <span style="color:#a5d6ff">0</span>, <span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">10</span>,  <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">17</span>
</span></span><span style="display:flex;"><span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(<span style="color:#a5d6ff">&#39;&#39;</span><span style="color:#ff7b72;font-weight:bold">.</span>join(letters[i] <span style="color:#ff7b72">for</span> i <span style="color:#ff7b72;font-weight:bold">in</span> indices))
</span></span></code></pre></div><p>This is fairly simple. Instead of explicitly embedding the string <code>'just another python hacker'</code> in the program, we assemble it using the index of its letters in the string <code>'abcdefghijklmnopqrstuvwxyz '</code>. We then obfuscate through a series of minor measures:</p>
<ul>
<li>Instead of calling the print function, we <code>import sys</code> and make a call to <code>sys.stdout.write</code>.</li>
<li>We assemble <code>string.lowercase + ' '</code> by joining together the character versions of its respective ordinal values (97 to 123 and 32).</li>
<li>We join together the integer indices using <code>'l'</code> and split that into a list.</li>
<li>We apply <code>'''</code> liberally and rely on the fact that <code>python</code> concatenates adjacent strings.</li>
</ul>
<p>Here&rsquo;s the obfuscated version:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span>eval(<span style="color:#a5d6ff">&#34;__import__(&#39;&#39;&#39;</span><span style="color:#79c0ff">\x73</span><span style="color:#a5d6ff">&#39;&#39;&#39;&#39;&#39;&#39;</span><span style="color:#79c0ff">\x79</span><span style="color:#a5d6ff">&#39;&#39;&#39;&#39;&#39;&#39;</span><span style="color:#79c0ff">\x73</span><span style="color:#a5d6ff">&#39;&#39;&#39;).sTdOuT&#34;</span><span style="color:#ff7b72;font-weight:bold">.</span>lower()
</span></span><span style="display:flex;"><span>)<span style="color:#ff7b72;font-weight:bold">.</span>write(<span style="color:#a5d6ff">&#39;&#39;</span><span style="color:#ff7b72;font-weight:bold">.</span>join(map(<span style="color:#ff7b72">lambda</span> _:(list(map(chr,range(<span style="color:#a5d6ff">97</span>,<span style="color:#a5d6ff">123</span>)))<span style="color:#ff7b72;font-weight:bold">+</span>[chr(
</span></span><span style="display:flex;"><span><span style="color:#a5d6ff">32</span>)])[int(_)],(<span style="color:#a5d6ff">&#39;&#39;&#39;9l20l18l19&#39;&#39;&#39;&#39;&#39;&#39;l26l0l13l14l19l7l4l17l26l15&#39;&#39;&#39;</span>
</span></span><span style="display:flex;"><span><span style="color:#a5d6ff">&#39;&#39;&#39;l24l19l7l14l1&#39;&#39;&#39;&#39;&#39;&#39;3l26l7l0l2l10l4l17&#39;&#39;&#39;</span>)<span style="color:#ff7b72;font-weight:bold">.</span>split(<span style="color:#a5d6ff">&#39;l&#39;</span>)))<span style="color:#ff7b72;font-weight:bold">+</span><span style="color:#a5d6ff">&#39;</span><span style="color:#79c0ff">\n</span><span style="color:#a5d6ff">&#39;</span>,)
</span></span></code></pre></div><p>We could certainly do more, but that&rsquo;s where I left this one. Stay tuned for the next JAPH.</p>
]]></content:encoded>
    </item>
    <item>
      <title>🧐 Data Fitting 2 - Very, Very Simple Linear Regression in Python</title>
      <link>https://ryanjoneil.dev/posts/2011-02-15-data-fitting-2-very-very-simple-linear-regression-in-python/</link>
      <pubDate>Tue, 15 Feb 2011 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2011-02-15-data-fitting-2-very-very-simple-linear-regression-in-python/</guid>
      <description>Predict how much people like cats and dogs based on their ice cream preferences. Also, Python and numpy.</description>
      <content:encoded><![CDATA[<p>This post is based on a memo I sent to some former colleagues at the Post. I&rsquo;ve edited it for use here since it fits well as the second in a series on simple data fitting techniques. If you&rsquo;re among the many enlightened individuals already using regression analysis, then this post is probably not for you. If you aren&rsquo;t, then hopefully this provides everything you need to develop rudimentary predictive models that yield surprising levels of accuracy.</p>
<h2 id="data">Data</h2>
<p>For purposes of a simple working example, we have collected six records of input data over three dimensions with the goal of predicting two outputs. The input data are:</p>
<p>$$
\begin{align*}
x_1 &amp;= \text{How much a respondent likes vanilla [0-10]}\\
x_2 &amp;= \text{How much a respondent likes strawberry [0-10]}\\
x_3 &amp;= \text{How much a respondent likes chocolate [0-10]}
\end{align*}
$$</p>
<p>Output data consist of:</p>
<p>$$
\begin{align*}
b_1 &amp;= \text{How much a respondent likes dogs [0-10]}\\
b_2 &amp;= \text{How much a respondent likes cats [0-10]}
\end{align*}
$$</p>
<p>Below are anonymous data collected from a random sample of people.</p>
<table>
  <thead>
      <tr>
          <th>respondent</th>
          <th style="text-align: right">vanilla ❤️</th>
          <th style="text-align: right">strawberry ❤️</th>
          <th style="text-align: right">chocolate ❤️</th>
          <th style="text-align: right">dog ❤️</th>
          <th style="text-align: right">cat ❤️</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Alyssa P Hacker</td>
          <td style="text-align: right">9</td>
          <td style="text-align: right">4</td>
          <td style="text-align: right">9</td>
          <td style="text-align: right">9</td>
          <td style="text-align: right">8</td>
      </tr>
      <tr>
          <td>Ben Bitdiddle</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">6</td>
          <td style="text-align: right">4</td>
          <td style="text-align: right">10</td>
          <td style="text-align: right">4</td>
      </tr>
      <tr>
          <td>Cy D. Fect</td>
          <td style="text-align: right">9</td>
          <td style="text-align: right">4</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">2</td>
          <td style="text-align: right">6</td>
      </tr>
      <tr>
          <td>Eva Lu Ator</td>
          <td style="text-align: right">3</td>
          <td style="text-align: right">7</td>
          <td style="text-align: right">9</td>
          <td style="text-align: right">4</td>
          <td style="text-align: right">6</td>
      </tr>
      <tr>
          <td>Lem E. Tweakit</td>
          <td style="text-align: right">6</td>
          <td style="text-align: right">8</td>
          <td style="text-align: right">5</td>
          <td style="text-align: right">2</td>
          <td style="text-align: right">5</td>
      </tr>
      <tr>
          <td>Louis Reasoner</td>
          <td style="text-align: right">4</td>
          <td style="text-align: right">5</td>
          <td style="text-align: right">3</td>
          <td style="text-align: right">10</td>
          <td style="text-align: right">3</td>
      </tr>
  </tbody>
</table>
<p>Our input is in three dimensions. Each output requires its own model, so we&rsquo;ll have one for dogs and one for cats. We&rsquo;re looking for functions, <code>dog(x)</code> and <code>cat(x)</code>, that can predict $b_1$ and $b_2$ based on given values of $x_1$, $x_2$, and $x_3$.</p>
<h2 id="model-1">Model 1</h2>
<p>For both models we want to find parameters that minimize their squared residuals (read: errors). There&rsquo;s a number of names for this. Optimization folks like to think of it as unconstrained quadratic optimization, but it&rsquo;s more common to call it least squares or linear regression. It&rsquo;s not necessary to entirely understand why for our purposes, but the function that minimizes these errors is:</p>
<p>$$\beta = ({A^t}A)^{-1}{A^t}b$$</p>
<p>This is implemented for you in the <code>numpy.linalg</code> Python package, which we&rsquo;ll use for examples. Much more information than you probably want can be found <a href="http://en.wikipedia.org/wiki/Least_squares">here</a>.</p>
<p>Below is a first stab at a Python version. It runs least squares against our input and output data exactly as they are. You can see the matrix $A$ and outputs $b_1$ and $b_2$ (dog and cat love, respectively) are represented just as they are in the table.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Version 1: No offset, no squared inputs</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">numpy</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>A <span style="color:#ff7b72;font-weight:bold">=</span> numpy<span style="color:#ff7b72;font-weight:bold">.</span>vstack([
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">9</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">9</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">8</span>, <span style="color:#a5d6ff">6</span>, <span style="color:#a5d6ff">4</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">9</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">8</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">3</span>, <span style="color:#a5d6ff">7</span>, <span style="color:#a5d6ff">9</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">6</span>, <span style="color:#a5d6ff">8</span>, <span style="color:#a5d6ff">5</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">5</span>, <span style="color:#a5d6ff">3</span>]
</span></span><span style="display:flex;"><span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>b1 <span style="color:#ff7b72;font-weight:bold">=</span> numpy<span style="color:#ff7b72;font-weight:bold">.</span>array([<span style="color:#a5d6ff">9</span>, <span style="color:#a5d6ff">10</span>, <span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">10</span>])
</span></span><span style="display:flex;"><span>b2 <span style="color:#ff7b72;font-weight:bold">=</span> numpy<span style="color:#ff7b72;font-weight:bold">.</span>array([<span style="color:#a5d6ff">9</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">6</span>, <span style="color:#a5d6ff">6</span>, <span style="color:#a5d6ff">5</span>, <span style="color:#a5d6ff">3</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(<span style="color:#a5d6ff">&#39;dog ❤️:&#39;</span>, numpy<span style="color:#ff7b72;font-weight:bold">.</span>linalg<span style="color:#ff7b72;font-weight:bold">.</span>lstsq(A, b1, rcond<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#79c0ff">None</span>)[<span style="color:#a5d6ff">0</span>])
</span></span><span style="display:flex;"><span>print(<span style="color:#a5d6ff">&#39;cat ❤️:&#39;</span>, numpy<span style="color:#ff7b72;font-weight:bold">.</span>linalg<span style="color:#ff7b72;font-weight:bold">.</span>lstsq(A, b2, rcond<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#79c0ff">None</span>)[<span style="color:#a5d6ff">0</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Output:</span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># dog ❤️: [0.72548294      0.53045642     -0.29952361]</span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># cat ❤️: [2.36110929e-01  2.61934385e-05  6.26892476e-01]</span>
</span></span></code></pre></div><p>The resulting model is:</p>
<pre tabindex="0"><code>dog(x) = 0.72548294 * x1 + 0.53045642 * x2 - 0.29952361 * x3
cat(x) = 2.36110929e-01 * x1 + 2.61934385e-05 * x2 + 6.26892476e-01 * x3
</code></pre><p>The coefficients before our variables correspond to beta in the formula above. Errors between observed and predicted data, shown below, are calculated and summed. For these six records, <code>dog(x)</code> has a total error of 20.76 and <code>cat(x)</code> has 3.74. Not great.</p>
<table>
  <thead>
      <tr>
          <th>respondent</th>
          <th style="text-align: right">predicted b1</th>
          <th style="text-align: right">b1 error</th>
          <th style="text-align: right">predicted b2</th>
          <th style="text-align: right">b2 error</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Alyssa P Hacker</td>
          <td style="text-align: right">5.96</td>
          <td style="text-align: right">3.04</td>
          <td style="text-align: right">7.77</td>
          <td style="text-align: right">1.23</td>
      </tr>
      <tr>
          <td>Ben Bitdiddle</td>
          <td style="text-align: right">7.79</td>
          <td style="text-align: right">2.21</td>
          <td style="text-align: right">4.40</td>
          <td style="text-align: right">0.40</td>
      </tr>
      <tr>
          <td>Cy D. Fect</td>
          <td style="text-align: right">6.25</td>
          <td style="text-align: right">4.25</td>
          <td style="text-align: right">7.14</td>
          <td style="text-align: right">1.14</td>
      </tr>
      <tr>
          <td>Eva Lu Ator</td>
          <td style="text-align: right">3.19</td>
          <td style="text-align: right">0.81</td>
          <td style="text-align: right">6.35</td>
          <td style="text-align: right">0.35</td>
      </tr>
      <tr>
          <td>Lem E. Tweakit</td>
          <td style="text-align: right">7.10</td>
          <td style="text-align: right">5.10</td>
          <td style="text-align: right">4.55</td>
          <td style="text-align: right">0.45</td>
      </tr>
      <tr>
          <td>Louis Reasoner</td>
          <td style="text-align: right">4.66</td>
          <td style="text-align: right">5.34</td>
          <td style="text-align: right">2.83</td>
          <td style="text-align: right">0.17</td>
      </tr>
      <tr>
          <td>Total error:</td>
          <td style="text-align: right"></td>
          <td style="text-align: right">20.76</td>
          <td style="text-align: right"></td>
          <td style="text-align: right">3.74</td>
      </tr>
  </tbody>
</table>
<h2 id="model-2">Model 2</h2>
<p>One problem with this model is that <code>dog(x)</code> and <code>cat(x)</code> are forced to pass through the origin. <em>(Why is that?)</em> We can improve it somewhat if we add an offset. This amounts to prepending 1 to every row in $A$ and adding a constant to the resulting functions. You can see the very slight difference between the code for this model and that of the previous:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Version 2: Offset, no squared inputs</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">numpy</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>A <span style="color:#ff7b72;font-weight:bold">=</span> numpy<span style="color:#ff7b72;font-weight:bold">.</span>vstack([
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">9</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">9</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">8</span>, <span style="color:#a5d6ff">6</span>, <span style="color:#a5d6ff">4</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">9</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">8</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">3</span>, <span style="color:#a5d6ff">7</span>, <span style="color:#a5d6ff">9</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">6</span>, <span style="color:#a5d6ff">8</span>, <span style="color:#a5d6ff">5</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">5</span>, <span style="color:#a5d6ff">3</span>]
</span></span><span style="display:flex;"><span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(<span style="color:#a5d6ff">&#39;dog ❤️:&#39;</span>, numpy<span style="color:#ff7b72;font-weight:bold">.</span>linalg<span style="color:#ff7b72;font-weight:bold">.</span>lstsq(A, b1, rcond<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#79c0ff">None</span>)[<span style="color:#a5d6ff">0</span>])
</span></span><span style="display:flex;"><span>print(<span style="color:#a5d6ff">&#39;cat ❤️:&#39;</span>, numpy<span style="color:#ff7b72;font-weight:bold">.</span>linalg<span style="color:#ff7b72;font-weight:bold">.</span>lstsq(A, b2, rcond<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#79c0ff">None</span>)[<span style="color:#a5d6ff">0</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Output:</span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># dog ❤️: [20.92975427  -0.27831197  -1.43135684  -0.76469017]</span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># cat ❤️: [-0.31744124   0.25133547   0.02978098   0.63394765]</span>
</span></span></code></pre></div><p>This yields the seconds version of our models:</p>
<pre tabindex="0"><code>dog(x) = 20.92975427 - 0.27831197 * x1 - 1.43135684 * x2 - 0.76469017 * x3
cat(x) = -0.31744124 + 0.25133547 * x1 + 0.02978098 * x2 + 0.63394765 * x3
</code></pre><p>These models provide errors of 13.87 and 3.79.  A little better on the dog side, but still not quite usable.</p>
<table>
  <thead>
      <tr>
          <th>respondent</th>
          <th style="text-align: right">predicted b1</th>
          <th style="text-align: right">b1 error</th>
          <th style="text-align: right">predicted b2</th>
          <th style="text-align: right">b2 error</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Alyssa P Hacker</td>
          <td style="text-align: right">5.82</td>
          <td style="text-align: right">3.18</td>
          <td style="text-align: right">7.77</td>
          <td style="text-align: right">1.23</td>
      </tr>
      <tr>
          <td>Ben Bitdiddle</td>
          <td style="text-align: right">7.06</td>
          <td style="text-align: right">2.94</td>
          <td style="text-align: right">4.41</td>
          <td style="text-align: right">0.41</td>
      </tr>
      <tr>
          <td>Cy D. Fect</td>
          <td style="text-align: right">6.58</td>
          <td style="text-align: right">4.58</td>
          <td style="text-align: right">7.14</td>
          <td style="text-align: right">1.14</td>
      </tr>
      <tr>
          <td>Eva Lu Ator</td>
          <td style="text-align: right">3.19</td>
          <td style="text-align: right">0.81</td>
          <td style="text-align: right">6.35</td>
          <td style="text-align: right">0.35</td>
      </tr>
      <tr>
          <td>Lem E. Tweakit</td>
          <td style="text-align: right">3.99</td>
          <td style="text-align: right">1.99</td>
          <td style="text-align: right">4.60</td>
          <td style="text-align: right">0.40</td>
      </tr>
      <tr>
          <td>Louis Reasoner</td>
          <td style="text-align: right">10.37</td>
          <td style="text-align: right">0.37</td>
          <td style="text-align: right">2.74</td>
          <td style="text-align: right">0.26</td>
      </tr>
      <tr>
          <td>Total error:</td>
          <td style="text-align: right"></td>
          <td style="text-align: right">13.87</td>
          <td style="text-align: right"></td>
          <td style="text-align: right">3.79</td>
      </tr>
  </tbody>
</table>
<h2 id="model-3">Model 3</h2>
<p>The problem is that <code>dog(x)</code> and <code>cat(x)</code> are linear functions. Most observed data don&rsquo;t conform to straight lines. Take a moment and draw the line $f(x) = x$ and the curve $f(x) = x^2$. The former makes a poor approximation of the latter.</p>
<p>Most of the time, people just use squares of the input data to add curvature to their models. We do this in our next version of the code by just adding squares of the input row values to our $A$ matrix. Everything else is the same. (In reality, you can add any function of the input data you feel best models the data, if you understand it well enough.)</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Version 3: Offset with squared inputs</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">numpy</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>A <span style="color:#ff7b72;font-weight:bold">=</span> numpy<span style="color:#ff7b72;font-weight:bold">.</span>vstack([
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">9</span>, <span style="color:#a5d6ff">9</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">4</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">9</span>, <span style="color:#a5d6ff">9</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">8</span>, <span style="color:#a5d6ff">8</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">6</span>, <span style="color:#a5d6ff">6</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">4</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">9</span>, <span style="color:#a5d6ff">9</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">4</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">8</span>, <span style="color:#a5d6ff">8</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">3</span>, <span style="color:#a5d6ff">3</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">7</span>, <span style="color:#a5d6ff">7</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">9</span>, <span style="color:#a5d6ff">9</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">6</span>, <span style="color:#a5d6ff">6</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">8</span>, <span style="color:#a5d6ff">8</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">5</span>, <span style="color:#a5d6ff">5</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>],
</span></span><span style="display:flex;"><span>    [<span style="color:#a5d6ff">1</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">4</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">5</span>, <span style="color:#a5d6ff">5</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">3</span>, <span style="color:#a5d6ff">3</span><span style="color:#ff7b72;font-weight:bold">**</span><span style="color:#a5d6ff">2</span>]
</span></span><span style="display:flex;"><span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>b1 <span style="color:#ff7b72;font-weight:bold">=</span> numpy<span style="color:#ff7b72;font-weight:bold">.</span>array([<span style="color:#a5d6ff">9</span>, <span style="color:#a5d6ff">10</span>, <span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">2</span>, <span style="color:#a5d6ff">10</span>])
</span></span><span style="display:flex;"><span>b2 <span style="color:#ff7b72;font-weight:bold">=</span> numpy<span style="color:#ff7b72;font-weight:bold">.</span>array([<span style="color:#a5d6ff">9</span>, <span style="color:#a5d6ff">4</span>, <span style="color:#a5d6ff">6</span>, <span style="color:#a5d6ff">6</span>, <span style="color:#a5d6ff">5</span>, <span style="color:#a5d6ff">3</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>print(<span style="color:#a5d6ff">&#39;dog ❤️:&#39;</span>, numpy<span style="color:#ff7b72;font-weight:bold">.</span>linalg<span style="color:#ff7b72;font-weight:bold">.</span>lstsq(A, b1, rcond<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#79c0ff">None</span>)[<span style="color:#a5d6ff">0</span>])
</span></span><span style="display:flex;"><span>print(<span style="color:#a5d6ff">&#39;cat ❤️:&#39;</span>, numpy<span style="color:#ff7b72;font-weight:bold">.</span>linalg<span style="color:#ff7b72;font-weight:bold">.</span>lstsq(A, b2, rcond<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#79c0ff">None</span>)[<span style="color:#a5d6ff">0</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># dog ❤️: [1.29368307  7.03633306  -0.44795498  9.98093332</span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">#  -0.75689575  -19.00757486  1.52985734]</span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># cat ❤️: [0.47945896  5.30866067  -0.39644128 -1.28704188</span>
</span></span><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic">#   0.12634295   -4.32392606  0.43081918]</span>
</span></span></code></pre></div><p>This gives us our final version of the model:</p>
<pre tabindex="0"><code>dog(x) = 1.29368307 + 7.03633306 * x1 - 0.44795498 * x1**2 + 9.98093332 * x2 - 0.75689575 * x2**2 - 19.00757486 * x3 + 1.52985734 * x3**2
cat(x) = 0.47945896 + 5.30866067 * x1 - 0.39644128 * x1**2 - 1.28704188 * x2 + 0.12634295 * x2**2 - 4.32392606 * x3 + 0.43081918 * x3**2
</code></pre><p>Adding curvature to our model eliminates all perceived error, at least within 1e-16. This may seem unbelievable, but when you consider that we only have six input records, it isn&rsquo;t really.</p>
<table>
  <thead>
      <tr>
          <th>respondent</th>
          <th style="text-align: right">predicted b1</th>
          <th style="text-align: right">b1 error</th>
          <th style="text-align: right">predicted b2</th>
          <th style="text-align: right">b2 error</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Alyssa P Hacker</td>
          <td style="text-align: right">9</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">9</td>
          <td style="text-align: right">0</td>
      </tr>
      <tr>
          <td>Ben Bitdiddle</td>
          <td style="text-align: right">10</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">4</td>
          <td style="text-align: right">0</td>
      </tr>
      <tr>
          <td>Cy D. Fect</td>
          <td style="text-align: right">2</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">6</td>
          <td style="text-align: right">0</td>
      </tr>
      <tr>
          <td>Eva Lu Ator</td>
          <td style="text-align: right">4</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">6</td>
          <td style="text-align: right">0</td>
      </tr>
      <tr>
          <td>Lem E. Tweakit</td>
          <td style="text-align: right">2</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">5</td>
          <td style="text-align: right">0</td>
      </tr>
      <tr>
          <td>Louis Reasoner</td>
          <td style="text-align: right">10</td>
          <td style="text-align: right">0</td>
          <td style="text-align: right">3</td>
          <td style="text-align: right">0</td>
      </tr>
      <tr>
          <td>Total error:</td>
          <td style="text-align: right"></td>
          <td style="text-align: right">0</td>
          <td style="text-align: right"></td>
          <td style="text-align: right">0</td>
      </tr>
  </tbody>
</table>
<p>It should be fairly obvious how one can take this and extrapolate to much larger models. I hope this is useful and that least squares becomes an important part of your lives.</p>
]]></content:encoded>
    </item>
    <item>
      <title>🗳 Off the Cuff Voter Fraud Detection</title>
      <link>https://ryanjoneil.dev/posts/2010-11-30-off-the-cuff-voter-fraud-detection/</link>
      <pubDate>Tue, 30 Nov 2010 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2010-11-30-off-the-cuff-voter-fraud-detection/</guid>
      <description>Using the exponential distribution to interpret votes in a web survey</description>
      <content:encoded><![CDATA[<p>Consider this scenario: You run a contest that accepts votes from the general Internet population. In order to encourage user engagement, you record any and all votes into a database over several days, storing nothing more than the competitor voted for, when each vote is cast, and a cookie set on the voter&rsquo;s computer along with their apparent IP addresses. If a voter already has a recorded cookie set they are denied subsequent votes. This way you can avoid requiring site registration, a huge turnoff for your users. Simple enough.</p>
<p>Unfortunately, some of the competitors are wily and attached to the idea of winning. They go so far as programming or hiring bots to cast thousands of votes for them. Your manager wants to know which votes are real and which ones are fake Right Now. Given very limited time, and ignoring actions that you <em>could</em> have taken to avoid the problem, how can you tell apart sets of good votes from those that shouldn&rsquo;t be counted?</p>
<p>One quick-and-dirty option involves comparing histograms of <a href="http://www.ehow.com/how_5417319_calculate-interarrival-time.html">interarrival times</a> for sets of votes. Say you&rsquo;re concerned that all the votes during a particular period of time or from a given IP address might be fraudulent. Put all the vote times you&rsquo;re concerned about into a list, sort them, and compute their differences:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># times is a list of datetime instances from vote records</span>
</span></span><span style="display:flex;"><span>times<span style="color:#ff7b72;font-weight:bold">.</span>sort(reversed<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#79c0ff">True</span>)
</span></span><span style="display:flex;"><span>interarrivals <span style="color:#ff7b72;font-weight:bold">=</span> [y<span style="color:#ff7b72;font-weight:bold">-</span>x <span style="color:#ff7b72">for</span> x, y <span style="color:#ff7b72;font-weight:bold">in</span> zip(times, times[<span style="color:#a5d6ff">1</span>:]]
</span></span></code></pre></div><p>Now use matplotlib to <a href="https://matplotlib.org/2.0.2/users/pyplot_tutorial.html#working-with-text">display a histogram</a> of these. Votes that occur naturally are likely to resemble an <a href="http://en.wikipedia.org/wiki/Exponential_distribution">exponential distribution</a> in their interarrival times. For instance, here are interarrival times for all votes received in a contest:</p>
<p><img alt="Interarrival times for all submissions" loading="lazy" src="/files/2010-11-30-off-the-cuff-voter-fraud-detection/all-votes.png#center"></p>
<p>This subset of votes is clearly fraudulent, due to the near determinism of their interarrival times. This is most likely caused by the voting bot not taking random sleep intervals during voting. It casts a vote, receives a response, clears its cookies, and repeats:</p>
<p><img alt="Interarrival times for clearly fraudulent votes" loading="lazy" src="/files/2010-11-30-off-the-cuff-voter-fraud-detection/fraud-plot.png#center"></p>
<p>These votes, on the other hand, are most likely legitimate. They exhibit a nice Erlang shape and appear to have natural interarrival times that one would expect:</p>
<p><img alt="Proper-looking interarrival times" loading="lazy" src="/files/2010-11-30-off-the-cuff-voter-fraud-detection/not-fraud.png#center"></p>
<p>Of course this method is woefully inadequate for rigorous detection of voting fraud. Ideally one would find a method to compute the probability that a set of votes is generated by a bot. This is enough to inform quick, ad hoc decisions though.</p>
]]></content:encoded>
    </item>
    <item>
      <title>🧐 Data Fitting 1 - Linear Data Fitting</title>
      <link>https://ryanjoneil.dev/posts/2010-11-23-data-fitting-1-linear-data-fitting/</link>
      <pubDate>Tue, 23 Nov 2010 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2010-11-23-data-fitting-1-linear-data-fitting/</guid>
      <description>An introduction to data fitting and classification using linear optimization in Python</description>
      <content:encoded><![CDATA[<p><em>Note: This post was updated to work with Python 3 and <a href="https://github.com/scipopt/PySCIPOpt">PySCIPOpt</a>. The original version used Python 2 and <a href="https://pythonhosted.org/python-zibopt/">python-zibopt</a>.</em></p>
<p>Data fitting is one of those tasks that everyone should have at least some exposure to. Certainly developers and analysts will benefit from a working knowledge of its fundamentals and their implementations. However, in my own reading I&rsquo;ve found it difficult to locate good examples that are simple enough to pick up quickly and come with accompanying source code.</p>
<p>This article commences an ongoing series introducing basic data fitting techniques. With any luck they won&rsquo;t be overly complex, while still being useful enough to get the point across with a real example and real data. We&rsquo;ll start with a binary classification problem: presented with a series of records, each containing a set number of input values describing it, determine whether or not each record exhibits some property.</p>
<h2 id="model">Model</h2>
<p>We&rsquo;ll use the <code>cancer1.dt</code> data from the <code>proben1</code> set of test cases, which you can download <a href="/files/2010-11-23-data-fitting-1-linear-data-fitting/cancer1.dt">here</a>. Each record starts with 9 data points containing physical characteristics of a tumor. The second to last data point contains 1 if a tumor is benign and 0 if it is malignant. We seek to find a linear function we can run on an arbitrary record that will return a value greater than zero if that record&rsquo;s tumor is predicted to be benign and less than zero if it is predicted to be malignant. We will train our linear model on the first 350 records, and test it for accuracy on the remaining rows.</p>
<p>This is similar to the data fitting problem found in <a href="https://www.thriftbooks.com/w/linear-programming-series-of-books-in-the-mathematical-sciences_vasek-chvatal/249798/#edition=2416723&amp;idiq=15706498">Chvatal</a>. Our inputs consist of a matrix of observed data, $A$, and a vector of classifications, $b$. In order to classify a record, we require another vector $x$ such that the dot product of $x$ and that record will be either greater or less than zero depending on its predicted classification.</p>
<p>A couple points to note before we start:</p>
<ul>
<li>
<p>Most observed data are noisy. This means it may be impossible to locate a hyperplane that cleanly separates given records of one type from another. In this case, we must resort to finding a function that minimizes our predictive error. For the purposes of this example, we&rsquo;ll minimize the sum of the absolute values of the observed and predicted values. That is, we seek $x$ such that we find $min \sum_i{|a_i^T x-b_i|}$.</p>
</li>
<li>
<p>The <a href="https://www.purplemath.com/modules/strtlneq.htm">slope-intercept</a> form of a line, $f(x)=m^T x+b$, contains an offset. It should be obvious that this is necessary in our model so that our function isn&rsquo;t required to pass through the origin. Thus, we&rsquo;ll be adding an extra variable with the coefficient of 1 to represent our offset value.</p>
</li>
<li>
<p>In order to model this, we use two linear constraints for each absolute value. We minimize the sum of these. Our Linear Programming model thus looks like:</p>
</li>
</ul>
<p>$$
\begin{align*}
\min\quad       &amp; z = x_0 + \sum_i{v_i}\\
\text{s.t.}\quad&amp; v_i \geq x_0 + a_i^\intercal x - 1    &amp;\quad\forall&amp;\quad\text{benign tumors}\\
&amp; v_i \geq 1 - x_0 - a_i^\intercal x    &amp;\quad\forall&amp;\quad\text{benign tumors}\\
&amp; v_i \geq x_0 + a_i^\intercal x - (-1) &amp;\quad\forall&amp;\quad\text{malignant tumors}\\
&amp; v_i \geq -1 - x_0 - a_i^\intercal x   &amp;\quad\forall&amp;\quad\text{malignant tumors}
\end{align*}
$$</p>
<h2 id="code">Code</h2>
<p>In order to do this in Python, we use <a href="https://www.scipopt.org/">SCIP</a> and <a href="https://soplex.zib.de/">SoPlex</a>. We start by setting constants for benign and malignant outputs and providing a function to read in the training and testing data sets.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#8b949e;font-style:italic"># Preferred output values for tumor categories</span>
</span></span><span style="display:flex;"><span>BENIGN <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">1</span>
</span></span><span style="display:flex;"><span>MALIGNANT <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">read_proben1_cancer_data</span>(filename, train_size):
</span></span><span style="display:flex;"><span>    <span style="color:#a5d6ff">&#39;&#39;&#39;Loads a proben1 cancer file into train &amp; test sets&#39;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Number of input data points per record</span>
</span></span><span style="display:flex;"><span>    DATA_POINTS <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">9</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    train_data <span style="color:#ff7b72;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>    test_data <span style="color:#ff7b72;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">with</span> open(filename) <span style="color:#ff7b72">as</span> infile:
</span></span><span style="display:flex;"><span>        <span style="color:#8b949e;font-style:italic"># Read in the first train_size lines to a training data list, and the</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8b949e;font-style:italic"># others to testing data. This allows us to test how general our model</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8b949e;font-style:italic"># is on something other than the input data.</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">for</span> line <span style="color:#ff7b72;font-weight:bold">in</span> infile<span style="color:#ff7b72;font-weight:bold">.</span>readlines()[<span style="color:#a5d6ff">7</span>:]: <span style="color:#8b949e;font-style:italic"># skip header</span>
</span></span><span style="display:flex;"><span>            line <span style="color:#ff7b72;font-weight:bold">=</span> line<span style="color:#ff7b72;font-weight:bold">.</span>split()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#8b949e;font-style:italic"># Records = offset (x0) + remaining data points</span>
</span></span><span style="display:flex;"><span>            input <span style="color:#ff7b72;font-weight:bold">=</span> [float(x) <span style="color:#ff7b72">for</span> x <span style="color:#ff7b72;font-weight:bold">in</span> line[:DATA_POINTS]]
</span></span><span style="display:flex;"><span>            output <span style="color:#ff7b72;font-weight:bold">=</span> BENIGN <span style="color:#ff7b72">if</span> line[<span style="color:#ff7b72;font-weight:bold">-</span><span style="color:#a5d6ff">2</span>] <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;1&#39;</span> <span style="color:#ff7b72">else</span> MALIGNANT
</span></span><span style="display:flex;"><span>            record <span style="color:#ff7b72;font-weight:bold">=</span> {<span style="color:#a5d6ff">&#39;input&#39;</span>: input, <span style="color:#a5d6ff">&#39;output&#39;</span>: output}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>            <span style="color:#8b949e;font-style:italic"># Determine what data set to put this in</span>
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">if</span> len(train_data) <span style="color:#ff7b72;font-weight:bold">&gt;=</span> train_size:
</span></span><span style="display:flex;"><span>                test_data<span style="color:#ff7b72;font-weight:bold">.</span>append(record)
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">else</span>:
</span></span><span style="display:flex;"><span>                train_data<span style="color:#ff7b72;font-weight:bold">.</span>append(record)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> train_data, test_data
</span></span></code></pre></div><p>The next function implements the LP model described above using SoPlex and SCIP. It minimizes the sum of residuals for each training record. This amounts to summing the absolute value of the difference between predicted and observed output data. The following function takes in input and observed output data and returns a list of coefficients. Our resulting model consists of taking the <a href="https://en.wikipedia.org/wiki/Dot_product">dot product</a> of an input record and these coefficients. If the result is greater than or equal to zero, that record is predicted to be a benign tumor, otherwise it is predicted to be malignant.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">from</span> <span style="color:#ff7b72">pyscipopt</span> <span style="color:#ff7b72">import</span> Model
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">train_linear_model</span>(train_data):
</span></span><span style="display:flex;"><span>    <span style="color:#a5d6ff">&#39;&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#a5d6ff">    Accepts a set of input training data with known output
</span></span></span><span style="display:flex;"><span><span style="color:#a5d6ff">    values.  Returns a list of coefficients to apply to
</span></span></span><span style="display:flex;"><span><span style="color:#a5d6ff">    arbitrary records for purposes of binary categorization.
</span></span></span><span style="display:flex;"><span><span style="color:#a5d6ff">    &#39;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Make sure we have at least one training record.</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">assert</span> len(train_data) <span style="color:#ff7b72;font-weight:bold">&gt;</span> <span style="color:#a5d6ff">0</span>
</span></span><span style="display:flex;"><span>    num_variables <span style="color:#ff7b72;font-weight:bold">=</span> len(train_data[<span style="color:#a5d6ff">0</span>][<span style="color:#a5d6ff">&#39;input&#39;</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Variables are coefficients in front of the data points. It is important</span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># that these be unrestricted in sign so they can take negative values.</span>
</span></span><span style="display:flex;"><span>    m <span style="color:#ff7b72;font-weight:bold">=</span> Model()
</span></span><span style="display:flex;"><span>    x <span style="color:#ff7b72;font-weight:bold">=</span> [m<span style="color:#ff7b72;font-weight:bold">.</span>addVar(<span style="color:#79c0ff">f</span><span style="color:#a5d6ff">&#39;x</span><span style="color:#a5d6ff">{</span>i<span style="color:#a5d6ff">}</span><span style="color:#a5d6ff">&#39;</span>, lb<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#79c0ff">None</span>) <span style="color:#ff7b72">for</span> i <span style="color:#ff7b72;font-weight:bold">in</span> range(num_variables)]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Residual for each data row</span>
</span></span><span style="display:flex;"><span>    residuals <span style="color:#ff7b72;font-weight:bold">=</span> [m<span style="color:#ff7b72;font-weight:bold">.</span>addVar(lb<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#79c0ff">None</span>, ub<span style="color:#ff7b72;font-weight:bold">=</span><span style="color:#79c0ff">None</span>) <span style="color:#ff7b72">for</span> _ <span style="color:#ff7b72;font-weight:bold">in</span> train_data]
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> r, d <span style="color:#ff7b72;font-weight:bold">in</span> zip(residuals, train_data):
</span></span><span style="display:flex;"><span>        <span style="color:#8b949e;font-style:italic"># r will be the absolute value of the difference between observed and</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8b949e;font-style:italic"># predicted values. We can model absolute values such as r &gt;= |foo| as:</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8b949e;font-style:italic">#</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8b949e;font-style:italic">#   r &gt;=  foo</span>
</span></span><span style="display:flex;"><span>        <span style="color:#8b949e;font-style:italic">#   r &gt;= -foo</span>
</span></span><span style="display:flex;"><span>        m<span style="color:#ff7b72;font-weight:bold">.</span>addCons(sum(x <span style="color:#ff7b72;font-weight:bold">*</span> y <span style="color:#ff7b72">for</span> x, y <span style="color:#ff7b72;font-weight:bold">in</span> zip(x, d[<span style="color:#a5d6ff">&#39;input&#39;</span>])) <span style="color:#ff7b72;font-weight:bold">+</span> r <span style="color:#ff7b72;font-weight:bold">&gt;=</span> d[<span style="color:#a5d6ff">&#39;output&#39;</span>])
</span></span><span style="display:flex;"><span>        m<span style="color:#ff7b72;font-weight:bold">.</span>addCons(sum(x <span style="color:#ff7b72;font-weight:bold">*</span> y <span style="color:#ff7b72">for</span> x, y <span style="color:#ff7b72;font-weight:bold">in</span> zip(x, d[<span style="color:#a5d6ff">&#39;input&#39;</span>])) <span style="color:#ff7b72;font-weight:bold">-</span> r <span style="color:#ff7b72;font-weight:bold">&lt;=</span> d[<span style="color:#a5d6ff">&#39;output&#39;</span>])
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Find and return coefficients that min sum of residuals.</span>
</span></span><span style="display:flex;"><span>    m<span style="color:#ff7b72;font-weight:bold">.</span>setObjective(sum(residuals))
</span></span><span style="display:flex;"><span>    m<span style="color:#ff7b72;font-weight:bold">.</span>setMinimize()
</span></span><span style="display:flex;"><span>    m<span style="color:#ff7b72;font-weight:bold">.</span>optimize()
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    solution <span style="color:#ff7b72;font-weight:bold">=</span> m<span style="color:#ff7b72;font-weight:bold">.</span>getBestSol()
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> [solution[xi] <span style="color:#ff7b72">for</span> xi <span style="color:#ff7b72;font-weight:bold">in</span> x]
</span></span></code></pre></div><p>We also provide a convenience function for counting the number of correct predictions by our resulting model against either the test or training data sets.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">count_correct</span>(data_set, coefficients):
</span></span><span style="display:flex;"><span>    <span style="color:#a5d6ff">&#39;&#39;&#39;Returns the number of correct predictions.&#39;&#39;&#39;</span>
</span></span><span style="display:flex;"><span>    correct <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> d <span style="color:#ff7b72;font-weight:bold">in</span> data_set:
</span></span><span style="display:flex;"><span>        result <span style="color:#ff7b72;font-weight:bold">=</span> sum(x<span style="color:#ff7b72;font-weight:bold">*</span>y <span style="color:#ff7b72">for</span> x, y <span style="color:#ff7b72;font-weight:bold">in</span> zip(coefficients, d[<span style="color:#a5d6ff">&#39;input&#39;</span>]))
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>        <span style="color:#8b949e;font-style:italic"># Do we predict the same as the output?</span>
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">if</span> (result <span style="color:#ff7b72;font-weight:bold">&gt;=</span> <span style="color:#a5d6ff">0</span>) <span style="color:#ff7b72;font-weight:bold">==</span> (d[<span style="color:#a5d6ff">&#39;output&#39;</span>] <span style="color:#ff7b72;font-weight:bold">&gt;=</span> <span style="color:#a5d6ff">0</span>):
</span></span><span style="display:flex;"><span>            correct <span style="color:#ff7b72;font-weight:bold">+=</span> <span style="color:#a5d6ff">1</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> correct
</span></span></code></pre></div><p>Finally we write a main method to read in the data, build our linear model, and test its efficacy.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">from</span> <span style="color:#ff7b72">pprint</span> <span style="color:#ff7b72">import</span> pprint
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">if</span> <span style="color:#79c0ff">__name__</span> <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;__main__&#39;</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Specs for this input file</span>
</span></span><span style="display:flex;"><span>    INPUT_FILE_NAME <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">&#39;cancer1.dt&#39;</span>
</span></span><span style="display:flex;"><span>    TRAIN_SIZE <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">350</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    train_data, test_data <span style="color:#ff7b72;font-weight:bold">=</span> read_proben1_cancer_data(
</span></span><span style="display:flex;"><span>        INPUT_FILE_NAME,
</span></span><span style="display:flex;"><span>        TRAIN_SIZE
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Add the offset variable to each of our data records</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">for</span> data_set <span style="color:#ff7b72;font-weight:bold">in</span> [train_data, test_data]:
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">for</span> row <span style="color:#ff7b72;font-weight:bold">in</span> data_set:
</span></span><span style="display:flex;"><span>            row[<span style="color:#a5d6ff">&#39;input&#39;</span>] <span style="color:#ff7b72;font-weight:bold">=</span> [<span style="color:#a5d6ff">1</span>] <span style="color:#ff7b72;font-weight:bold">+</span> row[<span style="color:#a5d6ff">&#39;input&#39;</span>]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    coefficients <span style="color:#ff7b72;font-weight:bold">=</span> train_linear_model(train_data)
</span></span><span style="display:flex;"><span>    print(<span style="color:#a5d6ff">&#39;coefficients:&#39;</span>)
</span></span><span style="display:flex;"><span>    pprint(coefficients)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Print % of correct predictions for each data set</span>
</span></span><span style="display:flex;"><span>    correct <span style="color:#ff7b72;font-weight:bold">=</span> count_correct(train_data, coefficients)
</span></span><span style="display:flex;"><span>    print(
</span></span><span style="display:flex;"><span>        <span style="color:#a5d6ff">&#39;</span><span style="color:#a5d6ff">%s</span><span style="color:#a5d6ff"> / </span><span style="color:#a5d6ff">%s</span><span style="color:#a5d6ff"> = </span><span style="color:#a5d6ff">%.02f%%</span><span style="color:#a5d6ff"> correct on training set&#39;</span> <span style="color:#ff7b72;font-weight:bold">%</span> (
</span></span><span style="display:flex;"><span>            correct, len(train_data),
</span></span><span style="display:flex;"><span>            <span style="color:#a5d6ff">100</span> <span style="color:#ff7b72;font-weight:bold">*</span> float(correct) <span style="color:#ff7b72;font-weight:bold">/</span> len(train_data)
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>    )
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    correct <span style="color:#ff7b72;font-weight:bold">=</span> count_correct(test_data, coefficients)
</span></span><span style="display:flex;"><span>    print(
</span></span><span style="display:flex;"><span>        <span style="color:#a5d6ff">&#39;</span><span style="color:#a5d6ff">%s</span><span style="color:#a5d6ff"> / </span><span style="color:#a5d6ff">%s</span><span style="color:#a5d6ff"> = </span><span style="color:#a5d6ff">%.02f%%</span><span style="color:#a5d6ff"> correct on testing set&#39;</span> <span style="color:#ff7b72;font-weight:bold">%</span> (
</span></span><span style="display:flex;"><span>            correct, len(test_data),
</span></span><span style="display:flex;"><span>            <span style="color:#a5d6ff">100</span> <span style="color:#ff7b72;font-weight:bold">*</span> float(correct) <span style="color:#ff7b72;font-weight:bold">/</span> len(test_data)
</span></span><span style="display:flex;"><span>        )
</span></span><span style="display:flex;"><span>    )
</span></span></code></pre></div><h2 id="results">Results</h2>
<p>The result of running this model against the <code>cancer1.dt</code> data set is:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-txt" data-lang="txt"><span style="display:flex;"><span>coefficients:
</span></span><span style="display:flex;"><span>[1.4072882449702786,
</span></span><span style="display:flex;"><span> -0.14014055927954652,
</span></span><span style="display:flex;"><span> -0.6239513714263405,
</span></span><span style="display:flex;"><span> -0.26727681774258882,
</span></span><span style="display:flex;"><span> 0.067107753841131157,
</span></span><span style="display:flex;"><span> -0.28300216102808429,
</span></span><span style="display:flex;"><span> -1.0355594670918404,
</span></span><span style="display:flex;"><span> -0.22774451038152174,
</span></span><span style="display:flex;"><span> -0.69871243677663608,
</span></span><span style="display:flex;"><span> -0.072575089848659444]
</span></span><span style="display:flex;"><span>328 / 350 = 93.71% correct on training set
</span></span><span style="display:flex;"><span>336 / 349 = 96.28% correct on testing set
</span></span></code></pre></div><p>The accuracy is pretty good here against the both the training and testing sets, so this particular model generalizes well.  This is about the simplest model we can implement for data fitting, and we&rsquo;ll get to more complicated ones later, but it&rsquo;s nice to see we can do so well so quickly.  The coefficients correspond to using a function of this form, rounding off to three decimal places:</p>
<p>$$
\begin{align*}
f(x) =\ &amp;1.407 - 0.140 x_1 - 0.624 x_2 - 0.267 x_3 + 0.067 x_4 - \\
&amp;0.283 x_5 - 1.037 x_6 - 0.228 x_7 - 0.699 x_8 - 0.073 x_9
\end{align*}
$$</p>
<h2 id="resources">Resources</h2>
<ul>
<li><a href="/files/2010-11-23-data-fitting-1-linear-data-fitting/cancer1.dt"><code>cancer1.dt</code></a> data file from <code>proben1</code></li>
<li>Full <a href="/files/2010-11-23-data-fitting-1-linear-data-fitting/fit-linear.py">source listing</a></li>
</ul>
]]></content:encoded>
    </item>
    <item>
      <title>🐍 Monte Carlo Simulation in Python</title>
      <link>https://ryanjoneil.dev/posts/2009-10-08-monte-carlo-simulation-in-python/</link>
      <pubDate>Thu, 08 Oct 2009 00:00:00 +0000</pubDate>
      <guid>https://ryanjoneil.dev/posts/2009-10-08-monte-carlo-simulation-in-python/</guid>
      <description>A quick introduction to writing and interpreting Monte Carlo simulations in Python</description>
      <content:encoded><![CDATA[<p><em>Note: This post was updated to work with Python 3.</em></p>
<p>One of the most useful tools one learns in an Operations Research curriculum is <a href="https://en.wikipedia.org/wiki/Monte_Carlo_method">Monte Carlo Simulation</a>. Its utility lies in its simplicity: one can learn vital information about nearly any process, be it deterministic or stochastic, without wading through the grunt work of finding an analytical solution. It can be used for off-the-cuff estimates or as a proper scientific tool. All one needs to know is how to simulate a given process and its appropriate probability distributions and parameters if that process is stochastic.</p>
<p>Here&rsquo;s how it works:</p>
<ul>
<li>Construct a simulation that, given input values, returns a value of interest. This could be a pure quantity, like time spent waiting for a bus, or a boolean indicating whether or not a particular event occurs.</li>
<li>Run the simulation a, usually large, number of times, each time with randomly generated input variables. Record its output values.</li>
<li>Compute sample mean and variance of the output values.</li>
</ul>
<p>In the case of time spent waiting for a bus, the sample mean and variance are estimators of mean and variance for one&rsquo;s wait time. In the boolean case, these represent probability that the given event will occur.</p>
<p>One can think of Monte Carlo Simulation as throwing darts. Say you want to find the area under a curve without integrating. All you must do is draw the curve on a wall and throw darts at it randomly. After you&rsquo;ve thrown enough darts, the area under the curve can be approximated using the percentage of darts that end up under the curve times the total area.</p>
<p>This technique is often performed using a spreadsheet, but that can be a bit clunky and may make more complex simulations difficult. I&rsquo;d like to spend a minute showing how it can be done in Python. Consider the following scenario:</p>
<p>Passengers for a train arrive according to a Poisson process with a mean of 100 per hour. The next train arrives exponentially with a rate of 5 per hour. How many passers will be aboard the train?</p>
<p>We can simulate this using the fact that a Poisson process can be represented as a string of events occurring with exponential inter-arrival times. We use the <code>sim()</code> function below to generate the number of passengers for random instances of the problem. We then compute sample mean and variance for these values.</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#ff7b72">import</span> <span style="color:#ff7b72">random</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>PASSENGERS <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">100.0</span>
</span></span><span style="display:flex;"><span>TRAINS     <span style="color:#ff7b72;font-weight:bold">=</span>   <span style="color:#a5d6ff">5.0</span>
</span></span><span style="display:flex;"><span>ITERATIONS <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">10000</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">def</span> <span style="color:#d2a8ff;font-weight:bold">sim</span>():
</span></span><span style="display:flex;"><span>    passengers <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">0.0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Determine when the train arrives</span>
</span></span><span style="display:flex;"><span>    train <span style="color:#ff7b72;font-weight:bold">=</span> random<span style="color:#ff7b72;font-weight:bold">.</span>expovariate(TRAINS)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#8b949e;font-style:italic"># Count the number of passenger arrivals before the train</span>
</span></span><span style="display:flex;"><span>    now <span style="color:#ff7b72;font-weight:bold">=</span> <span style="color:#a5d6ff">0.0</span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">while</span> <span style="color:#79c0ff">True</span>:
</span></span><span style="display:flex;"><span>        now <span style="color:#ff7b72;font-weight:bold">+=</span> random<span style="color:#ff7b72;font-weight:bold">.</span>expovariate(PASSENGERS)
</span></span><span style="display:flex;"><span>        <span style="color:#ff7b72">if</span> now <span style="color:#ff7b72;font-weight:bold">&gt;=</span> train:
</span></span><span style="display:flex;"><span>            <span style="color:#ff7b72">break</span>
</span></span><span style="display:flex;"><span>        passengers <span style="color:#ff7b72;font-weight:bold">+=</span> <span style="color:#a5d6ff">1.0</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    <span style="color:#ff7b72">return</span> passengers
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">if</span> <span style="color:#79c0ff">__name__</span> <span style="color:#ff7b72;font-weight:bold">==</span> <span style="color:#a5d6ff">&#39;__main__&#39;</span>:
</span></span><span style="display:flex;"><span>    output <span style="color:#ff7b72;font-weight:bold">=</span> [sim() <span style="color:#ff7b72">for</span> _ <span style="color:#ff7b72;font-weight:bold">in</span> range(ITERATIONS)]
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    total <span style="color:#ff7b72;font-weight:bold">=</span> sum(output)
</span></span><span style="display:flex;"><span>    mean <span style="color:#ff7b72;font-weight:bold">=</span> total <span style="color:#ff7b72;font-weight:bold">/</span> len(output)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    sum_sqrs <span style="color:#ff7b72;font-weight:bold">=</span> sum(x<span style="color:#ff7b72;font-weight:bold">*</span>x <span style="color:#ff7b72">for</span> x <span style="color:#ff7b72;font-weight:bold">in</span> output)
</span></span><span style="display:flex;"><span>    variance <span style="color:#ff7b72;font-weight:bold">=</span> (sum_sqrs <span style="color:#ff7b72;font-weight:bold">-</span> total <span style="color:#ff7b72;font-weight:bold">*</span> mean) <span style="color:#ff7b72;font-weight:bold">/</span> (len(output) <span style="color:#ff7b72;font-weight:bold">-</span> <span style="color:#a5d6ff">1</span>)
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>    print(<span style="color:#a5d6ff">&#39;E[X] = </span><span style="color:#a5d6ff">%.02f</span><span style="color:#a5d6ff">&#39;</span> <span style="color:#ff7b72;font-weight:bold">%</span> mean)
</span></span><span style="display:flex;"><span>    print(<span style="color:#a5d6ff">&#39;Var(X) = </span><span style="color:#a5d6ff">%.02f</span><span style="color:#a5d6ff">&#39;</span> <span style="color:#ff7b72;font-weight:bold">%</span> variance)
</span></span></code></pre></div>]]></content:encoded>
    </item>
  </channel>
</rss>
