Jekyll2020-05-27T12:12:29+00:00/feed.xmlBen’s GitHub homepageThe GitHub homepage for Ben Reiniger -- mathematics PhD (in graph theory and related subjects) turned data scientist.Ben ReinigerData Science at ODSC West 20192019-12-20T00:00:00+00:002019-12-20T00:00:00+00:00/blog/2019/12/20/odsc-westBen ReinigerInteger Programs and Nauty - Poset Dimension2018-07-07T00:00:00+00:002018-07-07T00:00:00+00:00/blog/2018/07/07/integer-programs-and-nauty-poset-dimension<p>(Warning: the math involved in this problem is more advanced and niche than I’m discussing elsewhere. I still want this post to focus on the programmatical aspect rather than the math, so I try to black-box most of the math here.)</p>
<blockquote>
<p>How many subsets of \([n]\) can we include in a poset with dimension 2?</p>
</blockquote>
<p>It’s “obvious” that the answer is \(1, 2, 4\) for \(n=0,1,2\), and it’s not hard to see that the answer is 7 when \(n=3\). The dimension of a poset is not a particularly easy parameter to get one’s mind around, so past this some computer experiments seemed prudent.</p>
<p>I tried the most naive thing first. SageMath has some builtin utilities for poset dimension, so it’s easy to write a script to run through all the families of \(k\) subsets of \([n]\) and compute the dimension, stopping if/when a dimension-2 family is produced.</p>
<p>That’s awful:</p>
<ol>
<li>The dimension is an invariant of the partial order on the sets we choose; in particular, renaming the elements of \([n]\) doesn’t change things, so we’re checking almost a factor of \(n!\) too many families.</li>
<li>As we increase \(k\) (remember, we’re looking for the largest \(k\)), we’re re-checking smaller posets (because dimension is monotone under containment).</li>
</ol>
<p>Enter the graph package <code class="language-plaintext highlighter-rouge">nauty</code>. We can encode our family of sets by a bipartite graph: one partite set represents the \(n\) elements of the underlying set, and the other partite set has \(k\) vertices representing our sets, a set vertex being adjacent to each element it contains. Under this correspondence, no two vertices in the second partite set can have the same neighborhood. By only caring about isomorphism classes (respecting which partite set is which, but not which individual vertices are which), we strip out the \(n!\) symmetry mentioned earlier. And <code class="language-plaintext highlighter-rouge">nauty</code> is set up to do exactly that:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>genbg <span class="nt">-szl</span> n k filename.txt
</code></pre></div></div>
<p>produces an output file containing an encoding of all the desired bipartite graphs. (The option -s specifies the encoding, the -z enforces different neighborhoods in the second partite set, and the -l produces a canonical labeling.) Now we pull this back into SageMath, reinterpret the bigraphs as posets, and ask whether these have dimension 2.</p>
<p>That fixes problem 1 above, but not problem 2.</p>
<p>We can move even more work to nauty. The program builds up the desired graphs from scratch, stripping out automorphisms as it goes. It allows us to define a pruning function: when a graph meets the criteria in the pruning function, neither that graph nor any of its descendents get output. Since dimension is monotone, and poset containment corresponds to subgraphs in our bigraph model, pruning when we find a dimension-3 poset works to produce only the posets of dimension at most 2.</p>
<h3 id="integer-programs">Integer programs?</h3>
<p>In SageMath, there is a class for posets, and it includes a function to return the dimension of the poset. So in the early tests, I likely just tested for <code class="language-plaintext highlighter-rouge">P.dimension()<=2</code>. (Computing the dimension can be phrased as a hypergraph coloring question, which in turn can be phrased as an integer program, and that is how it is implemented in SageMath.)</p>
<p>However, computing poset dimension is NP-hard, while testing specifically for dimension at most 2 is in P. In fact, dimension at most 2 is equivalent to the comparability graph’s complement also being a comparability graph, and <em>that</em> test has poly-time algorithms. Testing a graph for being a comparability graph has been implemented in SageMath, but only with two slow algorithms: an integer program, and a greedy partitioning algorithm. (The documentation implies that someone intend(s/ed) to code something more efficient.)</p>
<p>When I moved to pruning inside nauty, I needed to be able to code a test for dimension at most 2 in C. Since I was already familiar with Gurobi, I just implemented the integer program. If it had seemed important, I would have tried to implement the greedy partitioning algorithm, or maybe even found a more efficient algorithm and implemented it in both C for this purpose and python for inclusion in the SageMath library. But…</p>
<h3 id="conclusion">Conclusion</h3>
<p>Testing small cases is only really helpful in the pure mathematics context for guessing the right answer. Here’s what we know from the experiments described above:</p>
<table>
<thead>
<tr>
<th>\(n\)</th>
<th>0</th>
<th>1</th>
<th>2</th>
<th>3</th>
<th>4</th>
<th>5</th>
</tr>
</thead>
<tbody>
<tr>
<td>max size of poset with dimension at most 2</td>
<td>1</td>
<td>2</td>
<td>4</td>
<td>7</td>
<td>12</td>
<td>20</td>
</tr>
</tbody>
</table>
<p>And yes, those are recognizable: they are one less than the Fibonacci numbers! And it turns out there’s a good reason for that: there is a nice Fibonacci way to construct these posets of dimension 2, which I found by examining some of the solutions the program produced.</p>
<p><strong>But</strong> that fails eventually. The Fibonacci numbers grow exponentially, but with base the golden ratio (about 1.618). Taking all the subsets of size \(n/2\) produces a poset of dimension 2, and when \(n\) is large enough, there are many more of these than the Fibonacci number.</p>
<p>So, I think having even a much longer collection of values computed is unlikely to help much in finding the correct answer. We know now that the correct answer is \(\Theta(\binom{n}{n/2})\), and pinning down the constant is probably not easy just from example data.</p>Ben Reiniger(Warning: the math involved in this problem is more advanced and niche than I’m discussing elsewhere. I still want this post to focus on the programmatical aspect rather than the math, so I try to black-box most of the math here.)Everybody’s First Linear Program - Cheap Eating2018-07-05T00:00:00+00:002018-07-05T00:00:00+00:00/blog/2018/07/05/everybodys-first-lp<p>This should be the only post I make relating to my own coursework. It’s easy and a common problem, but instructive and fun.</p>
<blockquote>
<p>You have a list of food items you’re willing to eat together with their nutritional information. You have some nutritional requirements for yourself, and want to minimize the cost of your daily food intake subject to those requirements.</p>
</blockquote>
<p>This can be formulated as a <em>linear program</em>: the daily amount of each food item you will eat is a variable, so that the cost and the total of each nutritional item are all linear functions of these variables.</p>
<table>
<thead>
<tr>
<th>food item</th>
<th>fat</th>
<th>Cals</th>
<th>fiber</th>
<th>iron</th>
<th>sodium</th>
<th>protein</th>
<th>carbs</th>
<th>COST</th>
</tr>
</thead>
<tbody>
<tr>
<td>NutriGrain (1 bar)</td>
<td>5</td>
<td>120</td>
<td>10</td>
<td>10</td>
<td>5</td>
<td>2</td>
<td>8</td>
<td>25</td>
</tr>
<tr>
<td>raisin Granola (1 bar)</td>
<td>3</td>
<td>90</td>
<td>4</td>
<td>2</td>
<td>3</td>
<td>1</td>
<td>6</td>
<td>25</td>
</tr>
<tr>
<td>bread (1 slice)</td>
<td>1</td>
<td>70</td>
<td>4</td>
<td>4</td>
<td>4</td>
<td>3</td>
<td>4</td>
<td>10</td>
</tr>
<tr>
<td>fruit/nut cereal (3/4 c)</td>
<td>6</td>
<td>210</td>
<td>20</td>
<td>50</td>
<td>6</td>
<td>4</td>
<td>14</td>
<td>42</td>
</tr>
<tr>
<td>peanut butter (2T)</td>
<td>18</td>
<td>190</td>
<td>12</td>
<td>4</td>
<td>10</td>
<td>7</td>
<td>4</td>
<td>20</td>
</tr>
<tr>
<td>lentils (1/4 c dry)</td>
<td>0</td>
<td>70</td>
<td>36</td>
<td>15</td>
<td>0</td>
<td>8</td>
<td>6</td>
<td>9.5</td>
</tr>
<tr>
<td>split peas (1/4 c dry)</td>
<td>0</td>
<td>110</td>
<td>44</td>
<td>15</td>
<td>1</td>
<td>11</td>
<td>9</td>
<td>13.5</td>
</tr>
<tr>
<td>Nutella (2T)</td>
<td>11</td>
<td>200</td>
<td>6</td>
<td>4</td>
<td>1</td>
<td>3</td>
<td>7</td>
<td>34</td>
</tr>
<tr>
<td>pumpkin puree (1/2 c)</td>
<td>0</td>
<td>50</td>
<td>12</td>
<td>4</td>
<td>1</td>
<td>2</td>
<td>4</td>
<td>30</td>
</tr>
<tr>
<td>egg (1)</td>
<td>7</td>
<td>70</td>
<td>0</td>
<td>4</td>
<td>2</td>
<td>6</td>
<td>0</td>
<td>13</td>
</tr>
<tr>
<td>brown rice (1/4 c dry)</td>
<td>2</td>
<td>150</td>
<td>4</td>
<td>2</td>
<td>0</td>
<td>3</td>
<td>11</td>
<td>17.5</td>
</tr>
<tr>
<td>Goldfish crackers (30g)</td>
<td>8</td>
<td>140</td>
<td>7</td>
<td>6</td>
<td>10</td>
<td>4</td>
<td>6</td>
<td>20</td>
</tr>
<tr>
<td>applesauce (111g)</td>
<td>0</td>
<td>50</td>
<td>4</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>4</td>
<td>36</td>
</tr>
<tr>
<td>boxed mac’n’cheese (1/3 box)</td>
<td>5</td>
<td>260</td>
<td>4</td>
<td>10</td>
<td>24</td>
<td>10</td>
<td>16</td>
<td>33</td>
</tr>
<tr>
<td>butter (1T)</td>
<td>17</td>
<td>100</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>7.5</td>
</tr>
<tr>
<td>heavy cream (1T)</td>
<td>9</td>
<td>60</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>9</td>
</tr>
<tr>
<td>cheese (28g)</td>
<td>8</td>
<td>70</td>
<td>0</td>
<td>0</td>
<td>8</td>
<td>6</td>
<td>0</td>
<td>25</td>
</tr>
<tr>
<td>Snickers (34g)</td>
<td>12</td>
<td>160</td>
<td>4</td>
<td>0</td>
<td>4</td>
<td>3</td>
<td>7</td>
<td>27.5</td>
</tr>
<tr>
<td>salt (1/4 t)</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td>25</td>
<td>0</td>
<td>0</td>
<td>0.2</td>
</tr>
<tr>
<td>Ramen (1/2 package)</td>
<td>11</td>
<td>190</td>
<td>4</td>
<td>8</td>
<td>32</td>
<td>5</td>
<td>9</td>
<td>12.5</td>
</tr>
</tbody>
</table>
<p>All nutritional entries except for Calories and protein are measured in Percent Daily Values, as recommended on nutritional information panels; Calories are measured in Calories, and protein is measured in grams. Cost is measured in cents per serving. The recommended amount of protein for this purpose comes from <a href="http://www.webmd.com/food-recipes/protein">WebMD</a>. The constraints were chosen as follows (the instructor of the course requested the higher sodium requirement).</p>
<script type="math/tex; mode=display">% <![CDATA[
\begin{align*}
100 &\leq \text{fat} \leq 200 \\
2000 &\leq \text{Calories} \leq 2500 \\
100 &\leq \text{fiber} \\
100 &\leq \text{iron} \\
150 &\leq \text{sodium} \leq 300 \\
56 &\leq \text{protein} \\
100 &\leq \text{carbs} \leq 300
\end{align*} %]]></script>
<p>I ran the LP through SageMath, a program built on top of Python.
I ran it before including all 20 food items. Including those up to the mac’n’cheese gave a solution that involved just cereal, peanut butter, and mac’n’cheese. Thinking perhaps that these were chosen to get enough fat, I added butter, cream, cheese, and Snickers. After these additions the solution was unchanged. Then I thought perhaps the sodium was the culprit; I would have checked for slackness, but that didn’t seem easy in Sage, so I just added table salt to the menu. This gave rise to a solution involving lentils, eggs, mac’n’cheese, and salt. (Interesting that peanut butter has dropped out!) Finally I decided that the final menu item should be the old college staple, Ramen.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sage</span><span class="p">:</span> <span class="n">p</span> <span class="o">=</span> <span class="n">MixedIntegerLinearProgram</span><span class="p">(</span><span class="n">maximization</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
<span class="n">sage</span><span class="p">:</span> <span class="n">w</span> <span class="o">=</span> <span class="n">p</span><span class="p">.</span><span class="n">new_variable</span><span class="p">()</span>
<span class="n">sage</span><span class="p">:</span> <span class="n">n</span> <span class="o">=</span> <span class="p">[[</span> <span class="mi">5</span><span class="p">,</span><span class="mi">120</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">8</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">90</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">70</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span> <span class="mi">6</span><span class="p">,</span><span class="mi">210</span><span class="p">,</span><span class="mi">20</span><span class="p">,</span><span class="mi">50</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span><span class="mi">14</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span><span class="mi">18</span><span class="p">,</span><span class="mi">190</span><span class="p">,</span><span class="mi">12</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">4</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">70</span><span class="p">,</span><span class="mi">36</span><span class="p">,</span><span class="mi">15</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span> <span class="mi">0</span><span class="p">,</span><span class="mi">110</span><span class="p">,</span><span class="mi">44</span><span class="p">,</span><span class="mi">15</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span><span class="mi">11</span><span class="p">,</span> <span class="mi">9</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span><span class="mi">11</span><span class="p">,</span><span class="mi">200</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">7</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span><span class="mi">12</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">4</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">70</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span> <span class="mi">2</span><span class="p">,</span><span class="mi">150</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span><span class="mi">11</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span> <span class="mi">8</span><span class="p">,</span><span class="mi">140</span><span class="p">,</span> <span class="mi">7</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">50</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span> <span class="mi">5</span><span class="p">,</span><span class="mi">260</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">24</span><span class="p">,</span><span class="mi">10</span><span class="p">,</span><span class="mi">16</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span><span class="mi">17</span><span class="p">,</span><span class="mi">100</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">60</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">70</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span> <span class="mi">6</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span><span class="mi">12</span><span class="p">,</span><span class="mi">160</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">3</span><span class="p">,</span> <span class="mi">7</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span><span class="mi">25</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span>
<span class="p">...</span> <span class="p">[</span><span class="mi">11</span><span class="p">,</span><span class="mi">190</span><span class="p">,</span> <span class="mi">4</span><span class="p">,</span> <span class="mi">8</span><span class="p">,</span><span class="mi">32</span><span class="p">,</span> <span class="mi">5</span><span class="p">,</span> <span class="mi">9</span><span class="p">]]</span>
<span class="p">...</span>
<span class="n">sage</span><span class="p">:</span> <span class="n">c</span> <span class="o">=</span> <span class="p">[</span><span class="mi">25</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mi">10</span><span class="p">,</span> <span class="mi">42</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mf">9.5</span><span class="p">,</span> <span class="mf">13.5</span><span class="p">,</span> <span class="mi">34</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="mi">13</span><span class="p">,</span> <span class="mf">17.5</span><span class="p">,</span> <span class="mi">20</span><span class="p">,</span> <span class="mi">36</span><span class="p">,</span> <span class="mi">33</span><span class="p">,</span> <span class="mf">7.5</span><span class="p">,</span> <span class="mi">9</span><span class="p">,</span> <span class="mi">25</span><span class="p">,</span> <span class="mf">27.5</span><span class="p">,</span> <span class="mf">0.2</span><span class="p">,</span> <span class="mf">12.5</span><span class="p">]</span>
<span class="n">sage</span><span class="p">:</span> <span class="n">k</span><span class="o">=</span><span class="mi">19</span> <span class="c1">#=number of food items-1
</span><span class="n">sage</span><span class="p">:</span> <span class="c1"># total fat, as percent daily value
</span><span class="n">sage</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">add_constraint</span><span class="p">(</span> <span class="nb">sum</span><span class="p">([</span><span class="n">n</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="n">w</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">[</span><span class="mf">0.</span><span class="p">.</span><span class="n">k</span><span class="p">]])</span> <span class="o">>=</span> <span class="mi">100</span> <span class="p">)</span>
<span class="n">sage</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">add_constraint</span><span class="p">(</span> <span class="nb">sum</span><span class="p">([</span><span class="n">n</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="n">w</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">[</span><span class="mf">0.</span><span class="p">.</span><span class="n">k</span><span class="p">]])</span> <span class="o"><=</span> <span class="mi">200</span> <span class="p">)</span>
<span class="n">sage</span><span class="p">:</span> <span class="c1"># Calories
</span><span class="n">sage</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">add_constraint</span><span class="p">(</span> <span class="nb">sum</span><span class="p">([</span><span class="n">n</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span><span class="o">*</span><span class="n">w</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">[</span><span class="mf">0.</span><span class="p">.</span><span class="n">k</span><span class="p">]])</span> <span class="o">>=</span> <span class="mi">2000</span> <span class="p">)</span>
<span class="n">sage</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">add_constraint</span><span class="p">(</span> <span class="nb">sum</span><span class="p">([</span><span class="n">n</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span><span class="o">*</span><span class="n">w</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">[</span><span class="mf">0.</span><span class="p">.</span><span class="n">k</span><span class="p">]])</span> <span class="o"><=</span> <span class="mi">2500</span> <span class="p">)</span>
<span class="n">sage</span><span class="p">:</span> <span class="c1"># fiber, as PDV
</span><span class="n">sage</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">add_constraint</span><span class="p">(</span> <span class="nb">sum</span><span class="p">([</span><span class="n">n</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">2</span><span class="p">]</span><span class="o">*</span><span class="n">w</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">[</span><span class="mf">0.</span><span class="p">.</span><span class="n">k</span><span class="p">]])</span> <span class="o">>=</span> <span class="mi">100</span> <span class="p">)</span>
<span class="n">sage</span><span class="p">:</span> <span class="c1"># iron, PDV
</span><span class="n">sage</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">add_constraint</span><span class="p">(</span> <span class="nb">sum</span><span class="p">([</span><span class="n">n</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">3</span><span class="p">]</span><span class="o">*</span><span class="n">w</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">[</span><span class="mf">0.</span><span class="p">.</span><span class="n">k</span><span class="p">]])</span> <span class="o">>=</span> <span class="mi">100</span> <span class="p">)</span>
<span class="n">sage</span><span class="p">:</span> <span class="c1"># salt, PDV, desired 150% per instructions
</span><span class="n">sage</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">add_constraint</span><span class="p">(</span> <span class="nb">sum</span><span class="p">([</span><span class="n">n</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">4</span><span class="p">]</span><span class="o">*</span><span class="n">w</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">[</span><span class="mf">0.</span><span class="p">.</span><span class="n">k</span><span class="p">]])</span> <span class="o">>=</span> <span class="mi">150</span> <span class="p">)</span>
<span class="n">sage</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">add_constraint</span><span class="p">(</span> <span class="nb">sum</span><span class="p">([</span><span class="n">n</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">4</span><span class="p">]</span><span class="o">*</span><span class="n">w</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">[</span><span class="mf">0.</span><span class="p">.</span><span class="n">k</span><span class="p">]])</span> <span class="o"><=</span> <span class="mi">300</span> <span class="p">)</span>
<span class="n">sage</span><span class="p">:</span> <span class="c1"># protein, grams
</span><span class="n">sage</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">add_constraint</span><span class="p">(</span> <span class="nb">sum</span><span class="p">([</span><span class="n">n</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">5</span><span class="p">]</span><span class="o">*</span><span class="n">w</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">[</span><span class="mf">0.</span><span class="p">.</span><span class="n">k</span><span class="p">]])</span> <span class="o">>=</span> <span class="mi">56</span> <span class="p">)</span>
<span class="n">sage</span><span class="p">:</span> <span class="c1"># carbs, PDV
</span><span class="n">sage</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">add_constraint</span><span class="p">(</span> <span class="nb">sum</span><span class="p">([</span><span class="n">n</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">6</span><span class="p">]</span><span class="o">*</span><span class="n">w</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">[</span><span class="mf">0.</span><span class="p">.</span><span class="n">k</span><span class="p">]])</span> <span class="o">>=</span> <span class="mi">100</span> <span class="p">)</span>
<span class="n">sage</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">add_constraint</span><span class="p">(</span> <span class="nb">sum</span><span class="p">([</span><span class="n">n</span><span class="p">[</span><span class="n">i</span><span class="p">][</span><span class="mi">6</span><span class="p">]</span><span class="o">*</span><span class="n">w</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">[</span><span class="mf">0.</span><span class="p">.</span><span class="n">k</span><span class="p">]])</span> <span class="o"><=</span> <span class="mi">300</span> <span class="p">)</span>
<span class="n">sage</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">set_objective</span><span class="p">(</span> <span class="nb">sum</span><span class="p">([</span><span class="n">c</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">*</span><span class="n">w</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">[</span><span class="mf">0.</span><span class="p">.</span><span class="n">k</span><span class="p">]])</span> <span class="p">)</span>
<span class="n">sage</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">show</span><span class="p">()</span>
<span class="n">Minimization</span><span class="p">:</span>
<span class="mf">25.0</span> <span class="n">x_0</span> <span class="o">+</span><span class="mf">25.0</span> <span class="n">x_1</span> <span class="o">+</span><span class="mf">10.0</span> <span class="n">x_2</span> <span class="o">+</span><span class="mf">42.0</span> <span class="n">x_3</span> <span class="o">+</span><span class="mf">20.0</span> <span class="n">x_4</span> <span class="o">+</span><span class="mf">9.5</span> <span class="n">x_5</span> <span class="o">+</span><span class="mf">13.5</span> <span class="n">x_6</span> <span class="o">+</span><span class="mf">34.0</span> <span class="n">x_7</span> <span class="o">+</span><span class="mf">30.0</span> <span class="n">x_8</span> <span class="o">+</span><span class="mf">13.0</span> <span class="n">x_9</span> <span class="o">+</span><span class="mf">17.5</span> <span class="n">x_10</span> <span class="o">+</span><span class="mf">20.0</span> <span class="n">x_11</span> <span class="o">+</span><span class="mf">36.0</span> <span class="n">x_12</span> <span class="o">+</span><span class="mf">33.0</span> <span class="n">x_13</span> <span class="o">+</span><span class="mf">7.5</span> <span class="n">x_14</span> <span class="o">+</span><span class="mf">9.0</span> <span class="n">x_15</span> <span class="o">+</span><span class="mf">25.0</span> <span class="n">x_16</span> <span class="o">+</span><span class="mf">27.5</span> <span class="n">x_17</span> <span class="o">+</span><span class="mf">0.2</span> <span class="n">x_18</span> <span class="o">+</span><span class="mf">12.5</span> <span class="n">x_19</span>
<span class="n">Constraints</span><span class="p">:</span>
<span class="o">-</span><span class="mf">5.0</span> <span class="n">x_0</span> <span class="o">-</span><span class="mf">3.0</span> <span class="n">x_1</span> <span class="o">-</span><span class="n">x_2</span> <span class="o">-</span><span class="mf">6.0</span> <span class="n">x_3</span> <span class="o">-</span><span class="mf">18.0</span> <span class="n">x_4</span> <span class="o">-</span><span class="mf">11.0</span> <span class="n">x_7</span> <span class="o">-</span><span class="mf">7.0</span> <span class="n">x_9</span> <span class="o">-</span><span class="mf">2.0</span> <span class="n">x_10</span> <span class="o">-</span><span class="mf">8.0</span> <span class="n">x_11</span> <span class="o">-</span><span class="mf">5.0</span> <span class="n">x_13</span> <span class="o">-</span><span class="mf">17.0</span> <span class="n">x_14</span> <span class="o">-</span><span class="mf">9.0</span> <span class="n">x_15</span> <span class="o">-</span><span class="mf">8.0</span> <span class="n">x_16</span> <span class="o">-</span><span class="mf">12.0</span> <span class="n">x_17</span> <span class="o">-</span><span class="mf">11.0</span> <span class="n">x_19</span> <span class="o"><=</span> <span class="o">-</span><span class="mf">100.0</span>
<span class="mf">5.0</span> <span class="n">x_0</span> <span class="o">+</span><span class="mf">3.0</span> <span class="n">x_1</span> <span class="o">+</span><span class="n">x_2</span> <span class="o">+</span><span class="mf">6.0</span> <span class="n">x_3</span> <span class="o">+</span><span class="mf">18.0</span> <span class="n">x_4</span> <span class="o">+</span><span class="mf">11.0</span> <span class="n">x_7</span> <span class="o">+</span><span class="mf">7.0</span> <span class="n">x_9</span> <span class="o">+</span><span class="mf">2.0</span> <span class="n">x_10</span> <span class="o">+</span><span class="mf">8.0</span> <span class="n">x_11</span> <span class="o">+</span><span class="mf">5.0</span> <span class="n">x_13</span> <span class="o">+</span><span class="mf">17.0</span> <span class="n">x_14</span> <span class="o">+</span><span class="mf">9.0</span> <span class="n">x_15</span> <span class="o">+</span><span class="mf">8.0</span> <span class="n">x_16</span> <span class="o">+</span><span class="mf">12.0</span> <span class="n">x_17</span> <span class="o">+</span><span class="mf">11.0</span> <span class="n">x_19</span> <span class="o"><=</span> <span class="mf">200.0</span>
<span class="o">-</span><span class="mf">120.0</span> <span class="n">x_0</span> <span class="o">-</span><span class="mf">90.0</span> <span class="n">x_1</span> <span class="o">-</span><span class="mf">70.0</span> <span class="n">x_2</span> <span class="o">-</span><span class="mf">210.0</span> <span class="n">x_3</span> <span class="o">-</span><span class="mf">190.0</span> <span class="n">x_4</span> <span class="o">-</span><span class="mf">70.0</span> <span class="n">x_5</span> <span class="o">-</span><span class="mf">110.0</span> <span class="n">x_6</span> <span class="o">-</span><span class="mf">200.0</span> <span class="n">x_7</span> <span class="o">-</span><span class="mf">50.0</span> <span class="n">x_8</span> <span class="o">-</span><span class="mf">70.0</span> <span class="n">x_9</span> <span class="o">-</span><span class="mf">150.0</span> <span class="n">x_10</span> <span class="o">-</span><span class="mf">140.0</span> <span class="n">x_11</span> <span class="o">-</span><span class="mf">50.0</span> <span class="n">x_12</span> <span class="o">-</span><span class="mf">260.0</span> <span class="n">x_13</span> <span class="o">-</span><span class="mf">100.0</span> <span class="n">x_14</span> <span class="o">-</span><span class="mf">60.0</span> <span class="n">x_15</span> <span class="o">-</span><span class="mf">70.0</span> <span class="n">x_16</span> <span class="o">-</span><span class="mf">160.0</span> <span class="n">x_17</span> <span class="o">-</span><span class="mf">190.0</span> <span class="n">x_19</span> <span class="o"><=</span> <span class="o">-</span><span class="mf">2000.0</span>
<span class="mf">120.0</span> <span class="n">x_0</span> <span class="o">+</span><span class="mf">90.0</span> <span class="n">x_1</span> <span class="o">+</span><span class="mf">70.0</span> <span class="n">x_2</span> <span class="o">+</span><span class="mf">210.0</span> <span class="n">x_3</span> <span class="o">+</span><span class="mf">190.0</span> <span class="n">x_4</span> <span class="o">+</span><span class="mf">70.0</span> <span class="n">x_5</span> <span class="o">+</span><span class="mf">110.0</span> <span class="n">x_6</span> <span class="o">+</span><span class="mf">200.0</span> <span class="n">x_7</span> <span class="o">+</span><span class="mf">50.0</span> <span class="n">x_8</span> <span class="o">+</span><span class="mf">70.0</span> <span class="n">x_9</span> <span class="o">+</span><span class="mf">150.0</span> <span class="n">x_10</span> <span class="o">+</span><span class="mf">140.0</span> <span class="n">x_11</span> <span class="o">+</span><span class="mf">50.0</span> <span class="n">x_12</span> <span class="o">+</span><span class="mf">260.0</span> <span class="n">x_13</span> <span class="o">+</span><span class="mf">100.0</span> <span class="n">x_14</span> <span class="o">+</span><span class="mf">60.0</span> <span class="n">x_15</span> <span class="o">+</span><span class="mf">70.0</span> <span class="n">x_16</span> <span class="o">+</span><span class="mf">160.0</span> <span class="n">x_17</span> <span class="o">+</span><span class="mf">190.0</span> <span class="n">x_19</span> <span class="o"><=</span> <span class="mf">2500.0</span>
<span class="o">-</span><span class="mf">10.0</span> <span class="n">x_0</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_1</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_2</span> <span class="o">-</span><span class="mf">20.0</span> <span class="n">x_3</span> <span class="o">-</span><span class="mf">12.0</span> <span class="n">x_4</span> <span class="o">-</span><span class="mf">36.0</span> <span class="n">x_5</span> <span class="o">-</span><span class="mf">44.0</span> <span class="n">x_6</span> <span class="o">-</span><span class="mf">6.0</span> <span class="n">x_7</span> <span class="o">-</span><span class="mf">12.0</span> <span class="n">x_8</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_10</span> <span class="o">-</span><span class="mf">7.0</span> <span class="n">x_11</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_12</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_13</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_17</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_19</span> <span class="o"><=</span> <span class="o">-</span><span class="mf">100.0</span>
<span class="o">-</span><span class="mf">10.0</span> <span class="n">x_0</span> <span class="o">-</span><span class="mf">2.0</span> <span class="n">x_1</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_2</span> <span class="o">-</span><span class="mf">50.0</span> <span class="n">x_3</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_4</span> <span class="o">-</span><span class="mf">15.0</span> <span class="n">x_5</span> <span class="o">-</span><span class="mf">15.0</span> <span class="n">x_6</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_7</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_8</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_9</span> <span class="o">-</span><span class="mf">2.0</span> <span class="n">x_10</span> <span class="o">-</span><span class="mf">6.0</span> <span class="n">x_11</span> <span class="o">-</span><span class="mf">10.0</span> <span class="n">x_13</span> <span class="o">-</span><span class="mf">8.0</span> <span class="n">x_19</span> <span class="o"><=</span> <span class="o">-</span><span class="mf">100.0</span>
<span class="o">-</span><span class="mf">5.0</span> <span class="n">x_0</span> <span class="o">-</span><span class="mf">3.0</span> <span class="n">x_1</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_2</span> <span class="o">-</span><span class="mf">6.0</span> <span class="n">x_3</span> <span class="o">-</span><span class="mf">10.0</span> <span class="n">x_4</span> <span class="o">-</span><span class="n">x_6</span> <span class="o">-</span><span class="n">x_7</span> <span class="o">-</span><span class="n">x_8</span> <span class="o">-</span><span class="mf">2.0</span> <span class="n">x_9</span> <span class="o">-</span><span class="mf">10.0</span> <span class="n">x_11</span> <span class="o">-</span><span class="mf">24.0</span> <span class="n">x_13</span> <span class="o">-</span><span class="mf">8.0</span> <span class="n">x_16</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_17</span> <span class="o">-</span><span class="mf">25.0</span> <span class="n">x_18</span> <span class="o">-</span><span class="mf">32.0</span> <span class="n">x_19</span> <span class="o"><=</span> <span class="o">-</span><span class="mf">150.0</span>
<span class="mf">5.0</span> <span class="n">x_0</span> <span class="o">+</span><span class="mf">3.0</span> <span class="n">x_1</span> <span class="o">+</span><span class="mf">4.0</span> <span class="n">x_2</span> <span class="o">+</span><span class="mf">6.0</span> <span class="n">x_3</span> <span class="o">+</span><span class="mf">10.0</span> <span class="n">x_4</span> <span class="o">+</span><span class="n">x_6</span> <span class="o">+</span><span class="n">x_7</span> <span class="o">+</span><span class="n">x_8</span> <span class="o">+</span><span class="mf">2.0</span> <span class="n">x_9</span> <span class="o">+</span><span class="mf">10.0</span> <span class="n">x_11</span> <span class="o">+</span><span class="mf">24.0</span> <span class="n">x_13</span> <span class="o">+</span><span class="mf">8.0</span> <span class="n">x_16</span> <span class="o">+</span><span class="mf">4.0</span> <span class="n">x_17</span> <span class="o">+</span><span class="mf">25.0</span> <span class="n">x_18</span> <span class="o">+</span><span class="mf">32.0</span> <span class="n">x_19</span> <span class="o"><=</span> <span class="mf">300.0</span>
<span class="o">-</span><span class="mf">2.0</span> <span class="n">x_0</span> <span class="o">-</span><span class="n">x_1</span> <span class="o">-</span><span class="mf">2.0</span> <span class="n">x_2</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_3</span> <span class="o">-</span><span class="mf">7.0</span> <span class="n">x_4</span> <span class="o">-</span><span class="mf">8.0</span> <span class="n">x_5</span> <span class="o">-</span><span class="mf">11.0</span> <span class="n">x_6</span> <span class="o">-</span><span class="mf">3.0</span> <span class="n">x_7</span> <span class="o">-</span><span class="mf">2.0</span> <span class="n">x_8</span> <span class="o">-</span><span class="mf">6.0</span> <span class="n">x_9</span> <span class="o">-</span><span class="mf">3.0</span> <span class="n">x_10</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_11</span> <span class="o">-</span><span class="mf">10.0</span> <span class="n">x_13</span> <span class="o">-</span><span class="mf">6.0</span> <span class="n">x_16</span> <span class="o">-</span><span class="mf">3.0</span> <span class="n">x_17</span> <span class="o">-</span><span class="mf">5.0</span> <span class="n">x_19</span> <span class="o"><=</span> <span class="o">-</span><span class="mf">56.0</span>
<span class="o">-</span><span class="mf">8.0</span> <span class="n">x_0</span> <span class="o">-</span><span class="mf">6.0</span> <span class="n">x_1</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_2</span> <span class="o">-</span><span class="mf">14.0</span> <span class="n">x_3</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_4</span> <span class="o">-</span><span class="mf">6.0</span> <span class="n">x_5</span> <span class="o">-</span><span class="mf">9.0</span> <span class="n">x_6</span> <span class="o">-</span><span class="mf">7.0</span> <span class="n">x_7</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_8</span> <span class="o">-</span><span class="mf">11.0</span> <span class="n">x_10</span> <span class="o">-</span><span class="mf">6.0</span> <span class="n">x_11</span> <span class="o">-</span><span class="mf">4.0</span> <span class="n">x_12</span> <span class="o">-</span><span class="mf">16.0</span> <span class="n">x_13</span> <span class="o">-</span><span class="mf">7.0</span> <span class="n">x_17</span> <span class="o">-</span><span class="mf">9.0</span> <span class="n">x_19</span> <span class="o"><=</span> <span class="o">-</span><span class="mf">100.0</span>
<span class="mf">8.0</span> <span class="n">x_0</span> <span class="o">+</span><span class="mf">6.0</span> <span class="n">x_1</span> <span class="o">+</span><span class="mf">4.0</span> <span class="n">x_2</span> <span class="o">+</span><span class="mf">14.0</span> <span class="n">x_3</span> <span class="o">+</span><span class="mf">4.0</span> <span class="n">x_4</span> <span class="o">+</span><span class="mf">6.0</span> <span class="n">x_5</span> <span class="o">+</span><span class="mf">9.0</span> <span class="n">x_6</span> <span class="o">+</span><span class="mf">7.0</span> <span class="n">x_7</span> <span class="o">+</span><span class="mf">4.0</span> <span class="n">x_8</span> <span class="o">+</span><span class="mf">11.0</span> <span class="n">x_10</span> <span class="o">+</span><span class="mf">6.0</span> <span class="n">x_11</span> <span class="o">+</span><span class="mf">4.0</span> <span class="n">x_12</span> <span class="o">+</span><span class="mf">16.0</span> <span class="n">x_13</span> <span class="o">+</span><span class="mf">7.0</span> <span class="n">x_17</span> <span class="o">+</span><span class="mf">9.0</span> <span class="n">x_19</span> <span class="o"><=</span> <span class="mf">300.0</span>
<span class="n">Variables</span><span class="p">:</span>
<span class="n">x_0</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_1</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_2</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_3</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_4</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_5</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_6</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_7</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_8</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_9</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_10</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_11</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_12</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_13</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_14</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_15</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_16</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_17</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_18</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">x_19</span> <span class="ow">is</span> <span class="n">a</span> <span class="n">continuous</span> <span class="n">variable</span> <span class="p">(</span><span class="nb">min</span><span class="o">=</span><span class="mf">0.0</span><span class="p">,</span> <span class="nb">max</span><span class="o">=+</span><span class="n">oo</span><span class="p">)</span>
<span class="n">sage</span><span class="p">:</span> <span class="n">p</span><span class="p">.</span><span class="n">solve</span><span class="p">()</span>
<span class="mf">143.06214933103149</span>
<span class="n">sage</span><span class="p">:</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">p</span><span class="p">.</span><span class="n">get_values</span><span class="p">(</span><span class="n">w</span><span class="p">).</span><span class="n">iteritems</span><span class="p">():</span>
<span class="p">...</span> <span class="k">print</span> <span class="s">'w_%s = %s'</span> <span class="o">%</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>
<span class="n">w_0</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">w_1</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">w_2</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">w_3</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">w_4</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">w_5</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">w_6</span> <span class="o">=</span> <span class="mf">1.68321104877</span>
<span class="n">w_7</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">w_8</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">w_9</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">w_10</span> <span class="o">=</span> <span class="mf">0.0863185153215</span>
<span class="n">w_11</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">w_12</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">w_13</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">w_14</span> <span class="o">=</span> <span class="mf">0.306430729391</span>
<span class="n">w_15</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">w_16</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">w_17</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">w_18</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">w_19</span> <span class="o">=</span> <span class="mf">9.32239965473</span>
</code></pre></div></div>
<p>The result? I can eat for a measly $1.43 per day, by eating a bit under half a cup (measured dry) of lentils, a very small bit of egg, about 1/9 of a box of mac’n’cheese, and between 4 and 5 packages of Ramen noodles.</p>
<p>Yum.</p>
<p>Finally, linear programs come with their <em>dual</em> program, which in this situation gives values to each nutrient. Here’s that code.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sage</span><span class="p">:</span> <span class="c1">#Now, on to the DUAL PROBLEM:
</span><span class="n">sage</span><span class="p">:</span> <span class="n">d</span> <span class="o">=</span> <span class="n">MixedIntegerLinearProgram</span><span class="p">()</span>
<span class="n">sage</span><span class="p">:</span> <span class="n">x</span> <span class="o">=</span> <span class="n">d</span><span class="p">.</span><span class="n">new_variable</span><span class="p">()</span>
<span class="n">sage</span><span class="p">:</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="p">[</span><span class="mf">0.</span><span class="p">.</span><span class="mi">19</span><span class="p">]:</span>
<span class="p">...</span> <span class="n">d</span><span class="p">.</span><span class="n">add_constraint</span><span class="p">(</span> <span class="nb">sum</span><span class="p">(</span> <span class="p">[</span> <span class="n">n</span><span class="p">[</span><span class="n">j</span><span class="p">][</span><span class="n">i</span><span class="p">]</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">[</span><span class="mf">0.</span><span class="p">.</span><span class="mi">6</span><span class="p">]</span> <span class="p">]</span> <span class="p">)</span> <span class="o"><=</span> <span class="n">n</span><span class="p">[</span><span class="n">j</span><span class="p">][</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="mi">7</span><span class="p">]</span> <span class="o">+</span> <span class="n">n</span><span class="p">[</span><span class="n">j</span><span class="p">][</span><span class="mi">1</span><span class="p">]</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span> <span class="o">+</span> <span class="n">n</span><span class="p">[</span><span class="n">j</span><span class="p">][</span><span class="mi">4</span><span class="p">]</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="mi">11</span><span class="p">]</span> <span class="o">+</span> <span class="n">n</span><span class="p">[</span><span class="n">j</span><span class="p">][</span><span class="mi">6</span><span class="p">]</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="mi">13</span><span class="p">]</span> <span class="o">+</span> <span class="n">c</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="p">)</span>
<span class="p">...</span>
<span class="n">sage</span><span class="p">:</span> <span class="n">d</span><span class="p">.</span><span class="n">set_objective</span><span class="p">(</span> <span class="mi">100</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">-</span> <span class="mi">200</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="mi">7</span><span class="p">]</span> <span class="o">+</span> <span class="mi">2000</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="mi">2500</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span> <span class="o">+</span> <span class="mi">100</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span>
<span class="p">...</span> <span class="o">+</span> <span class="mi">100</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">+</span> <span class="mi">150</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">-</span> <span class="mi">300</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="mi">11</span><span class="p">]</span> <span class="o">+</span> <span class="mi">56</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">+</span> <span class="mi">100</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="o">-</span> <span class="mi">300</span><span class="o">*</span><span class="n">x</span><span class="p">[</span><span class="mi">13</span><span class="p">]</span> <span class="p">)</span>
<span class="p">...</span>
<span class="n">sage</span><span class="p">:</span> <span class="n">d</span><span class="p">.</span><span class="n">solve</span><span class="p">()</span>
<span class="mf">143.06214933103152</span>
<span class="n">sage</span><span class="p">:</span> <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">d</span><span class="p">.</span><span class="n">get_values</span><span class="p">(</span><span class="n">x</span><span class="p">).</span><span class="n">iteritems</span><span class="p">():</span>
<span class="p">...</span> <span class="k">print</span> <span class="s">'x_%s = %s'</span> <span class="o">%</span> <span class="p">(</span><span class="n">i</span><span class="p">,</span> <span class="n">v</span><span class="p">)</span>
<span class="n">x_0</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">x_1</span> <span class="o">=</span> <span class="mf">0.075</span>
<span class="n">x_2</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">x_3</span> <span class="o">=</span> <span class="mf">0.0266508416055</span>
<span class="n">x_4</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">x_5</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">x_6</span> <span class="o">=</span> <span class="mf">0.563336210617</span>
<span class="n">x_7</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">x_8</span> <span class="o">=</span> <span class="mf">0.0</span>
<span class="n">x_11</span> <span class="o">=</span> <span class="mf">0.219788519637</span>
<span class="n">x_13</span> <span class="o">=</span> <span class="mf">0.0</span>
</code></pre></div></div>
<p>Again you see the optimal $1.43. Here only Calories, iron, and carbohydrates should have positive cost, while sodium should have negative cost. (The negative is allowed here since I have put an upper bound on the daily intake of sodium.)</p>
<p>Finally, I decided to see what happened after I removed the upper bounds (since these were not really requested in the problem). In this version of the problem, I can save three cents, and should eat 2 teaspoons of peanut butter, 1/4 cup lentils, and nearly 10 packages of Ramen.</p>Ben ReinigerThis should be the only post I make relating to my own coursework. It’s easy and a common problem, but instructive and fun.Combinatorial Games2018-07-01T00:00:00+00:002018-07-01T00:00:00+00:00/blog/2018/07/01/combinatorial-games<p>To kick things off, I thought I’d post some of my earliest research-oriented scripts.</p>
<p>In the summers of my degree, we had a large research group in combinatorics, with tons of problems presented and then worked on in small groups. A number of them were <strong>combinatorial games</strong>: games of perfect information in which 2 players take alternating turns.</p>
<p>Several games arose as variations of one introduced to us as “Chaos”, based on a discrete chip-firing/sandpile process. (We were, or at least I was, astonished to learn the extent to which sandpile models appear in different branches of mathematics.) A graph is given as the game board, and players alternate turns adding a chip to a vertex. Between players’ turns, the chip-firing process occurs: if a vertex has at least as many chips as its degree, then it sends one chip to each of its neighbors, and this repeats. It is possible, and indeed necessary when enough chips have been played, that the chip-firing goes on forever, in which case our game ends.</p>
<p>I want to focus on two of the variants we studied, and we’ll just stick to complete graphs for the game boards. The two variants I’ll discuss here are impartial, meaning that the two players have the same play options throughout; for complete graphs, this means we can forget the graphs and just consider the chip counts at each vertex. (For non-complete boards, we did make similar scripts, using the graph utility <code class="language-plaintext highlighter-rouge">nauty</code>.) Hence our game state is just a non-increasing list of integers, and the chip-firing (which we were calling “toppling”) can be achieved by the following code common to both variants.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">topple</span><span class="p">(</span><span class="n">G</span><span class="p">):</span>
<span class="n">ret</span> <span class="o">=</span> <span class="s">""</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">G</span><span class="p">)):</span>
<span class="k">if</span> <span class="n">G</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">>=</span><span class="nb">len</span><span class="p">(</span><span class="n">G</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">:</span>
<span class="n">G</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">-=</span> <span class="nb">len</span><span class="p">(</span><span class="n">G</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span>
<span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">G</span><span class="p">)):</span>
<span class="k">if</span> <span class="n">j</span> <span class="o">!=</span> <span class="n">i</span><span class="p">:</span>
<span class="n">G</span><span class="p">[</span><span class="n">j</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">G</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">>=</span><span class="nb">len</span><span class="p">(</span><span class="n">G</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">:</span>
<span class="n">ret</span> <span class="o">=</span> <span class="s">"End Game"</span>
<span class="n">G</span><span class="p">.</span><span class="n">sort</span><span class="p">(</span><span class="n">reverse</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span> <span class="c1">#could make more efficient with targeted sort...(?)
</span> <span class="k">return</span> <span class="n">ret</span>
</code></pre></div></div>
<h2 id="last-play-wins">Last play wins</h2>
<p>The first variant is won by the player who sets off the infinite chip-firing sequence. Using the following script, we found that the first player wins on any complete graph with up to ten vertices.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">sys</span>
<span class="k">def</span> <span class="nf">winner</span><span class="p">(</span><span class="n">G</span><span class="p">,</span> <span class="n">player</span><span class="p">,</span> <span class="n">E</span><span class="p">):</span>
<span class="k">if</span> <span class="n">G</span> <span class="ow">in</span> <span class="n">E</span><span class="p">:</span>
<span class="k">return</span> <span class="n">E</span><span class="p">[</span><span class="n">G</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1">#check value of subgames:
</span> <span class="n">subgames</span> <span class="o">=</span> <span class="n">populate_subgames</span><span class="p">(</span><span class="n">G</span><span class="p">)</span>
<span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">subgames</span><span class="p">)</span><span class="o">==</span><span class="mi">0</span><span class="p">:</span>
<span class="n">E</span><span class="p">[</span><span class="n">G</span><span class="p">]</span> <span class="o">=</span> <span class="n">player</span>
<span class="k">return</span> <span class="n">E</span><span class="p">[</span><span class="n">G</span><span class="p">]</span>
<span class="n">E</span><span class="p">[</span><span class="n">G</span><span class="p">]</span><span class="o">=</span><span class="mi">1</span><span class="o">-</span><span class="n">player</span> <span class="c1">#set initial winner as NOT this player
</span> <span class="k">for</span> <span class="n">option</span> <span class="ow">in</span> <span class="n">subgames</span><span class="p">:</span>
<span class="k">if</span> <span class="n">winner</span><span class="p">(</span><span class="n">option</span><span class="p">,</span> <span class="mi">1</span><span class="o">-</span><span class="n">player</span><span class="p">,</span> <span class="n">E</span><span class="p">)</span><span class="o">==</span><span class="n">player</span><span class="p">:</span>
<span class="n">E</span><span class="p">[</span><span class="n">G</span><span class="p">]</span> <span class="o">=</span> <span class="n">player</span>
<span class="k">break</span>
<span class="k">return</span> <span class="n">E</span><span class="p">[</span><span class="n">G</span><span class="p">]</span>
<span class="k">def</span> <span class="nf">populate_subgames</span><span class="p">(</span><span class="n">G</span><span class="p">):</span> <span class="c1">#returns empty if there is a winning move
</span> <span class="n">subgames</span><span class="o">=</span><span class="nb">set</span><span class="p">()</span>
<span class="n">H</span><span class="o">=</span><span class="nb">list</span><span class="p">(</span><span class="n">G</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">G</span><span class="p">)):</span>
<span class="k">if</span> <span class="n">i</span><span class="o">==</span><span class="mi">0</span> <span class="ow">or</span> <span class="n">H</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o"><</span><span class="n">H</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]:</span>
<span class="c1">#create gameboard with new token there
</span> <span class="n">newH</span> <span class="o">=</span> <span class="n">H</span><span class="p">[:]</span>
<span class="n">newH</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">if</span> <span class="n">topple</span><span class="p">(</span><span class="n">newH</span><span class="p">)</span><span class="o">==</span><span class="s">"End Game"</span><span class="p">:</span>
<span class="n">subgames</span><span class="o">=</span><span class="nb">set</span><span class="p">()</span>
<span class="k">break</span>
<span class="k">else</span><span class="p">:</span>
<span class="n">newG</span><span class="o">=</span><span class="nb">tuple</span><span class="p">(</span><span class="n">newH</span><span class="p">)</span>
<span class="n">subgames</span><span class="p">.</span><span class="n">add</span><span class="p">(</span> <span class="n">newG</span> <span class="p">)</span>
<span class="k">return</span> <span class="n">subgames</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">G</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">([</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="n">n</span><span class="p">)</span>
<span class="n">E</span><span class="o">=</span><span class="p">{}</span>
<span class="k">print</span> <span class="n">winner</span><span class="p">(</span><span class="n">G</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">E</span><span class="p">)</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
<span class="n">main</span><span class="p">()</span>
</code></pre></div></div>
<p>There’s no real reason to expect a change after ten vertices. To try to prove that, it seemed helpful to consider the full game tree, to see what moves the first player should make. For that we need some small tweaks throughout to save the subgames from each position, and an addition to the main function to describe the game tree in graphviz code and automatically run that to produce a graphic:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">subprocess</span> <span class="c1">#to run graphviz code to produce graphical game tree
</span>
<span class="k">def</span> <span class="nf">main</span><span class="p">():</span>
<span class="n">n</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">sys</span><span class="p">.</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
<span class="n">G</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">([</span><span class="mi">0</span><span class="p">]</span><span class="o">*</span><span class="n">n</span><span class="p">)</span>
<span class="n">E</span><span class="o">=</span><span class="p">{}</span> <span class="c1">#holds values
</span> <span class="n">S</span><span class="o">=</span><span class="p">{}</span> <span class="c1">#holds subgames
</span> <span class="n">whowins</span> <span class="o">=</span> <span class="n">winner</span><span class="p">(</span><span class="n">G</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="n">E</span><span class="p">,</span><span class="n">S</span><span class="p">)</span>
<span class="n">gvcode</span> <span class="o">=</span> <span class="s">"digraph G{"</span>
<span class="k">for</span> <span class="n">graph</span> <span class="ow">in</span> <span class="n">E</span><span class="p">:</span>
<span class="n">gvcode</span> <span class="o">+=</span> <span class="s">'</span><span class="se">\"</span><span class="s">'</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">graph</span><span class="p">)</span><span class="o">+</span><span class="s">"</span><span class="se">\"</span><span class="s"> "</span>
<span class="k">if</span> <span class="n">E</span><span class="p">[</span><span class="n">graph</span><span class="p">]</span><span class="o">==</span><span class="mi">1</span><span class="p">:</span>
<span class="n">gvcode</span> <span class="o">+=</span> <span class="s">"[shape=box] "</span>
<span class="k">for</span> <span class="n">child</span> <span class="ow">in</span> <span class="n">S</span><span class="p">[</span><span class="n">graph</span><span class="p">]:</span>
<span class="k">if</span> <span class="n">E</span><span class="p">[</span><span class="n">graph</span><span class="p">]</span><span class="o">==</span><span class="mi">0</span><span class="p">:</span>
<span class="n">gvcode</span> <span class="o">+=</span> <span class="s">'</span><span class="se">\"</span><span class="s">'</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">graph</span><span class="p">)</span><span class="o">+</span><span class="s">"</span><span class="se">\"</span><span class="s">-></span><span class="se">\"</span><span class="s">"</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">child</span><span class="p">)</span><span class="o">+</span><span class="s">"</span><span class="se">\"</span><span class="s"> "</span>
<span class="k">if</span> <span class="n">E</span><span class="p">[</span><span class="n">graph</span><span class="p">]</span><span class="o">==</span><span class="mi">0</span> <span class="ow">and</span> <span class="n">E</span><span class="p">[</span><span class="n">child</span><span class="p">]</span><span class="o">==</span><span class="mi">1</span><span class="p">:</span>
<span class="n">gvcode</span> <span class="o">+=</span> <span class="s">"[color=red] "</span>
<span class="n">gvcode</span> <span class="o">+=</span> <span class="s">"}"</span>
<span class="n">filename</span><span class="o">=</span><span class="s">"K"</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">n</span><span class="p">).</span><span class="n">zfill</span><span class="p">(</span><span class="mi">2</span><span class="p">)</span>
<span class="n">f</span><span class="o">=</span><span class="nb">open</span><span class="p">(</span><span class="n">filename</span><span class="o">+</span><span class="s">".gvz"</span><span class="p">,</span><span class="s">'w'</span><span class="p">)</span>
<span class="n">f</span><span class="p">.</span><span class="n">write</span><span class="p">(</span><span class="n">gvcode</span><span class="p">)</span>
<span class="n">f</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
<span class="n">subprocess</span><span class="p">.</span><span class="n">call</span><span class="p">([</span><span class="s">"dot"</span><span class="p">,</span><span class="s">"-Tpng"</span><span class="p">,</span><span class="n">filename</span><span class="o">+</span><span class="s">".gvz"</span><span class="p">,</span><span class="s">"-o"</span><span class="o">+</span><span class="n">filename</span><span class="o">+</span><span class="s">".png"</span><span class="p">])</span>
</code></pre></div></div>
<p>And here are the first few game trees. (Most of the tree anyway. Since we “knew” that player 1 should win, we cut off the subtrees rooted at a second-player-win node. Those nodes are boxed, and the arrows going into them made red to draw attention to the first player’s losing moves.)</p>
<p><img src="/assets/K03.png" alt="K3 A/B game tree" /></p>
<p><img src="/assets/K04.png" alt="K4 A/B game tree" /></p>
<p><img src="/assets/K05.png" alt="K5 A/B game tree" /></p>
<p><a href="/assets/K06.png" target="_blank">K6 A/B game tree</a></p>
<p>We did actually manage to prove that the first player wins when the number of vertices is odd.</p>
<h2 id="max-min-variant">Max-min variant</h2>
<p>In this variant, the two players have opposing goals: one tries to maximize the number of chips played (which is also the length of the game) while the other tries to minimize it. Thus optimal play gives a parameter of the graph, the <em>game chip number</em>.</p>
<p>We cannot cut off branches when the maximizing player could end the game, so <code class="language-plaintext highlighter-rouge">populate_subgames</code> gets changed a bit (in particular, the ending state is recorded as an empty tuple). The most relevant change is in the winner function, which now returns a score instead:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">bBetter</span><span class="p">(</span><span class="n">option1</span><span class="p">,</span> <span class="n">option2</span><span class="p">,</span> <span class="n">player</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">True</span> <span class="k">if</span> <span class="p">((</span><span class="n">option1</span><span class="o">></span><span class="n">option2</span> <span class="ow">and</span> <span class="n">player</span><span class="o">==</span><span class="mi">0</span><span class="p">)</span> <span class="ow">or</span>
<span class="p">(</span><span class="n">option1</span><span class="o"><</span><span class="n">option2</span> <span class="ow">and</span> <span class="n">player</span><span class="o">==</span><span class="mi">1</span><span class="p">))</span> <span class="k">else</span> <span class="bp">False</span>
<span class="k">def</span> <span class="nf">score</span><span class="p">(</span><span class="n">G</span><span class="p">,</span> <span class="n">player</span><span class="p">,</span> <span class="n">E</span><span class="p">,</span> <span class="n">S</span><span class="p">):</span>
<span class="k">if</span> <span class="n">G</span> <span class="ow">in</span> <span class="n">E</span><span class="p">:</span>
<span class="k">return</span> <span class="n">E</span><span class="p">[</span><span class="n">G</span><span class="p">]</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1">#check value of subgames:
</span> <span class="n">S</span><span class="p">[</span><span class="n">G</span><span class="p">]</span> <span class="o">=</span> <span class="n">populate_subgames</span><span class="p">(</span><span class="n">G</span><span class="p">)</span>
<span class="k">if</span> <span class="p">(</span> <span class="p">()</span> <span class="ow">in</span> <span class="n">S</span><span class="p">[</span><span class="n">G</span><span class="p">]</span> <span class="p">)</span> <span class="ow">and</span> <span class="p">(</span><span class="n">player</span><span class="o">==</span><span class="mi">1</span> <span class="ow">or</span> <span class="n">S</span><span class="p">[</span><span class="n">G</span><span class="p">]</span><span class="o">==</span><span class="nb">set</span><span class="p">([()])</span> <span class="p">):</span>
<span class="c1">#if Min can end the game or the game must end
</span> <span class="n">E</span><span class="p">[</span><span class="n">G</span><span class="p">]</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">G</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span>
<span class="n">S</span><span class="p">[</span><span class="n">G</span><span class="p">]</span> <span class="o">=</span> <span class="nb">set</span><span class="p">([()])</span> <span class="c1">#if Min's turn, kill any child nodes
</span> <span class="k">return</span> <span class="n">E</span><span class="p">[</span><span class="n">G</span><span class="p">]</span>
<span class="n">bestoption</span><span class="o">=</span><span class="p">(</span><span class="mi">0</span> <span class="k">if</span> <span class="n">player</span><span class="o">==</span><span class="mi">0</span> <span class="k">else</span> <span class="nb">len</span><span class="p">(</span><span class="n">G</span><span class="p">)</span><span class="o">**</span><span class="mi">2</span> <span class="p">)</span> <span class="c1">#set initial best option as bad for player
</span> <span class="k">for</span> <span class="n">option</span> <span class="ow">in</span> <span class="n">S</span><span class="p">[</span><span class="n">G</span><span class="p">]:</span>
<span class="k">if</span> <span class="n">option</span><span class="o">!=</span><span class="p">():</span>
<span class="n">newoption</span><span class="o">=</span><span class="n">score</span><span class="p">(</span><span class="n">option</span><span class="p">[:],</span> <span class="mi">1</span><span class="o">-</span><span class="n">player</span><span class="p">,</span> <span class="n">E</span><span class="p">,</span> <span class="n">S</span><span class="p">)</span>
<span class="k">if</span> <span class="n">bBetter</span><span class="p">(</span> <span class="n">newoption</span><span class="p">,</span> <span class="n">bestoption</span><span class="p">,</span> <span class="n">player</span> <span class="p">):</span>
<span class="n">bestoption</span><span class="o">=</span><span class="n">newoption</span>
<span class="n">E</span><span class="p">[</span><span class="n">G</span><span class="p">]</span> <span class="o">=</span> <span class="n">bestoption</span>
<span class="k">return</span> <span class="n">E</span><span class="p">[</span><span class="n">G</span><span class="p">]</span>
</code></pre></div></div>
<p>Here are a couple of the game trees; the number in front of a position is the score when play starts from that node.</p>
<p><img src="/assets/gcn-K04.png" alt="K4 game chip number tree" /></p>
<p><img src="/assets/gcn-K05.png" alt="K5 game chip number tree" /></p>
<p>I imagine we had more data than this at some point, but here’s what I have about the game chip number of the complete graph on n vertices:</p>
<table>
<thead>
<tr>
<th style="text-align: right">n</th>
<th style="text-align: center">2</th>
<th style="text-align: center">3</th>
<th style="text-align: center">4</th>
<th style="text-align: center">5</th>
<th style="text-align: center">6</th>
<th style="text-align: center">7</th>
<th style="text-align: center">8</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: right">game chip number</td>
<td style="text-align: center">1</td>
<td style="text-align: center">4</td>
<td style="text-align: center">8</td>
<td style="text-align: center">12</td>
<td style="text-align: center">18</td>
<td style="text-align: center">26</td>
<td style="text-align: center">34</td>
</tr>
</tbody>
</table>
<p>Oh, and there are “easy” upper and lower bounds that we could include:</p>
<table>
<thead>
<tr>
<th style="text-align: right">n</th>
<th style="text-align: center">2</th>
<th style="text-align: center">3</th>
<th style="text-align: center">4</th>
<th style="text-align: center">5</th>
<th style="text-align: center">6</th>
<th style="text-align: center">7</th>
<th style="text-align: center">8</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: right">lower bound</td>
<td style="text-align: center">1</td>
<td style="text-align: center">3</td>
<td style="text-align: center">6</td>
<td style="text-align: center">10</td>
<td style="text-align: center">15</td>
<td style="text-align: center">21</td>
<td style="text-align: center">28</td>
</tr>
<tr>
<td style="text-align: right">game chip number</td>
<td style="text-align: center">1</td>
<td style="text-align: center">4</td>
<td style="text-align: center">8</td>
<td style="text-align: center">12</td>
<td style="text-align: center">18</td>
<td style="text-align: center">26</td>
<td style="text-align: center">34</td>
</tr>
<tr>
<td style="text-align: right">upper bound</td>
<td style="text-align: center">1</td>
<td style="text-align: center">4</td>
<td style="text-align: center">9</td>
<td style="text-align: center">16</td>
<td style="text-align: center">25</td>
<td style="text-align: center">36</td>
<td style="text-align: center">49</td>
</tr>
</tbody>
</table>
<p>The game chip number hovers (for these small n) about 1/3 of the way from the lower bound to the upper bound, but I don’t see any pattern in the particular numbers; do you?</p>
<h3 id="references">References</h3>
<p><a href="https://faculty.math.illinois.edu/~west/regs/gamechip.html">REGS page</a></p>Ben ReinigerTo kick things off, I thought I’d post some of my earliest research-oriented scripts.